diff --git a/.gitignore b/.gitignore index 2920e458a2..82cc18930f 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,41 @@ +nbproject +ccan/opt/.dirstamp +compat/jansson-2.6/jansson.pc +compat/jansson-2.6/libtool +compat/jansson-2.6/ltmain.sh +compat/jansson-2.6/m4/libtool.m4 +compat/jansson-2.6/m4/ltoptions.m4 +compat/jansson-2.6/m4/ltsugar.m4 +compat/jansson-2.6/m4/ltversion.m4 +compat/jansson-2.6/m4/lt~obsolete.m4 +compat/jansson-2.6/src/.libs/ +compat/jansson-2.6/src/dump.lo +compat/jansson-2.6/src/error.lo +compat/jansson-2.6/src/hashtable.lo +compat/jansson-2.6/src/jansson_config.h +compat/jansson-2.6/src/libjansson.la +compat/jansson-2.6/src/load.lo +compat/jansson-2.6/src/memory.lo +compat/jansson-2.6/src/pack_unpack.lo +compat/jansson-2.6/src/strbuffer.lo +compat/jansson-2.6/src/strconv.lo +compat/jansson-2.6/src/utf.lo +compat/jansson-2.6/src/value.lo +compat/libusb-1.0/libtool +compat/libusb-1.0/libusb-1.0.pc +compat/libusb-1.0/libusb/.libs/ +compat/libusb-1.0/libusb/os/.dirstamp +compat/libusb-1.0/ltmain.sh +libtool +ltmain.sh +m4/libtool.m4 +m4/ltoptions.m4 +m4/ltsugar.m4 +m4/ltversion.m4 +m4/lt~obsolete.m4 + + + cgminer cgminer.exe minerd @@ -32,6 +70,7 @@ ext_deps config.h.in config.h + ccan/libccan.a lib/arg-nonnull.h lib/c++defs.h diff --git a/01-cgminer.rules b/01-cgminer.rules index f3a488a113..93c25a8e3b 100644 --- a/01-cgminer.rules +++ b/01-cgminer.rules @@ -1,20 +1,52 @@ # Butterfly Labs FPGA and ASIC devices -ATTRS{idVendor}=="0403", ATTRS{idProduct}=="6014", SUBSYSTEMS=="usb", ACTION=="add", MODE="0666", GROUP="plugdev" +ATTRS{idVendor}=="0403", ATTRS{idProduct}=="6014", SUBSYSTEM=="usb", ACTION=="add", MODE="0666", GROUP="plugdev" # ModMinerQuad -ATTRS{idVendor}=="1fc9", ATTRS{idProduct}=="0003", SUBSYSTEMS=="usb", ACTION=="add", MODE="0666", GROUP="plugdev" +ATTRS{idVendor}=="1fc9", ATTRS{idProduct}=="0003", SUBSYSTEM=="usb", ACTION=="add", MODE="0666", GROUP="plugdev" # Lancelot and Avalon -ATTRS{idVendor}=="0403", ATTRS{idProduct}=="6001", SUBSYSTEMS=="usb", ACTION=="add", MODE="0666", GROUP="plugdev" +ATTRS{idVendor}=="0403", ATTRS{idProduct}=="6001", SUBSYSTEM=="usb", ACTION=="add", MODE="0666", GROUP="plugdev" # Icarus -ATTRS{idVendor}=="067b", ATTRS{idProduct}=="2303", SUBSYSTEMS=="usb", ACTION=="add", MODE="0666", GROUP="plugdev" +ATTRS{idVendor}=="067b", ATTRS{idProduct}=="2303", SUBSYSTEM=="usb", ACTION=="add", MODE="0666", GROUP="plugdev" +ATTRS{idVendor}=="1fc9", ATTRS{idProduct}=="0083", SUBSYSTEM=="usb", ACTION=="add", MODE="0666", GROUP="plugdev" -# AsicminerUSB -ATTRS{idVendor}=="10c4", ATTRS{idProduct}=="ea60", SUBSYSTEMS=="usb", ACTION=="add", MODE="0666", GROUP="plugdev" +# AsicminerUSB and Antminer U1 +ATTRS{idVendor}=="10c4", ATTRS{idProduct}=="ea60", SUBSYSTEM=="usb", ACTION=="add", MODE="0666", GROUP="plugdev" # Cairnsmore1 -ATTRS{idVendor}=="067b", ATTRS{idProduct}=="0230", SUBSYSTEMS=="usb", ACTION=="add", MODE="0666", GROUP="plugdev" +ATTRS{idVendor}=="067b", ATTRS{idProduct}=="0230", SUBSYSTEM=="usb", ACTION=="add", MODE="0666", GROUP="plugdev" + +# Cairnsmore1-2 +ATTRS{idVendor}=="0403", ATTRS{idProduct}=="8350", SUBSYSTEM=="usb", ACTION=="add", MODE="0666", GROUP="plugdev" # Ztex -ATTRS{idVendor}=="221a", ATTRS{idProduct}=="0100", SUBSYSTEMS=="usb", ACTION=="add", MODE="0666", GROUP="plugdev" +ATTRS{idVendor}=="221a", ATTRS{idProduct}=="0100", SUBSYSTEM=="usb", ACTION=="add", MODE="0666", GROUP="plugdev" + +# BF1 +ATTRS{idVendor}=="03eb", ATTRS{idProduct}=="204b", SUBSYSTEM=="usb", ACTION=="add", MODE="0666", GROUP="plugdev" + +# Klondike +ATTRS{idVendor}=="04d8", ATTRS{idProduct}=="f60a", SUBSYSTEM=="usb", ACTION=="add", MODE="0666", GROUP="plugdev" + +# HashFast +ATTRS{idVendor}=="297c", ATTRS{idProduct}=="0001", SUBSYSTEM=="usb", ACTION=="add", MODE="0666", GROUP="plugdev" +ATTRS{idVendor}=="297c", ATTRS{idProduct}=="8001", SUBSYSTEM=="usb", ACTION=="add", MODE="0666", GROUP="plugdev" + +# BXF +ATTRS{idVendor}=="198c", ATTRS{idProduct}=="b1f1", SUBSYSTEM=="usb", ACTION=="add", MODE="0666", GROUP="plugdev" + +# NF1 +ATTRS{idVendor}=="04d8", ATTRS{idProduct}=="00de", SUBSYSTEM=="usb", ACTION=="add", MODE="0666", GROUP="plugdev" + +# ANT_S1 +ATTRS{idVendor}=="4254", ATTRS{idProduct}=="4153", SUBSYSTEM=="usb", ACTION=="add", MODE="0666", GROUP="plugdev" + +# Cointerra +ATTRS{idVendor}=="1cbe", ATTRS{idProduct}=="0003", SUBSYSTEM=="usb", ACTION=="add", MODE="0666", GROUP="plugdev" + +# Drillbit Thumb +ATTRS{idVendor}=="03eb", ATTRS{idProduct}=="2404", SUBSYSTEM=="usb", ACTION=="add", MODE="0666", GROUP="plugdev" + +# Avalon4 +ATTRS{idVendor}=="29f1", ATTRS{idProduct}=="33f2", SUBSYSTEM=="usb", ACTION=="add", MODE="0666", GROUP="plugdev" diff --git a/A1-board-selector-CCD.c b/A1-board-selector-CCD.c new file mode 100644 index 0000000000..d0e173f24d --- /dev/null +++ b/A1-board-selector-CCD.c @@ -0,0 +1,117 @@ +/* + * board selector support for TCA9535 used in Bitmine's CoinCraft Desk + * + * Copyright 2014 Zefir Kurtisi + * + * 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 +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include "miner.h" + +#include "A1-board-selector.h" +#include "i2c-context.h" + +static struct board_selector ccd_selector; + +struct i2c_ctx *U1_tca9535; +uint8_t chain_mask = 0xff; +uint8_t active_chain = 255; +pthread_mutex_t lock; + + +#define UNUSED_BITS 0xe0 + +static void ccd_unlock(void) +{ + mutex_unlock(&lock); +} + +static void ccd_exit(void) +{ + if (U1_tca9535 != NULL) + U1_tca9535->exit(U1_tca9535); +} +uint8_t retval = 0; + +extern struct board_selector *ccd_board_selector_init(void) +{ + mutex_init(&lock); + U1_tca9535 = i2c_slave_open(I2C_BUS, 0x27); + if (U1_tca9535 == NULL) + return NULL; + bool retval = U1_tca9535->write(U1_tca9535, 0x06, 0xe0) && + U1_tca9535->write(U1_tca9535, 0x07, 0xe0) && + U1_tca9535->write(U1_tca9535, 0x02, 0x1f) && + U1_tca9535->write(U1_tca9535, 0x03, 0x00); + if (retval) + return &ccd_selector; + ccd_exit(); + return NULL; +} + +static bool ccd_select(uint8_t chain) +{ + if (chain >= CCD_MAX_CHAINS) + return false; + + mutex_lock(&lock); + if (active_chain == chain) + return true; + + active_chain = chain; + chain_mask = 1 << active_chain; + return U1_tca9535->write(U1_tca9535, 0x02, ~chain_mask); +} + +static bool __ccd_board_selector_reset(uint8_t mask) +{ + if (!U1_tca9535->write(U1_tca9535, 0x03, mask)) + return false; + cgsleep_ms(RESET_LOW_TIME_MS); + if (!U1_tca9535->write(U1_tca9535, 0x03, 0x00)) + return false; + cgsleep_ms(RESET_HI_TIME_MS); + return true; +} +// we assume we are already holding the mutex +static bool ccd_reset(void) +{ + return __ccd_board_selector_reset(chain_mask); +} + +static bool ccd_reset_all(void) +{ + mutex_lock(&lock); + bool retval = __ccd_board_selector_reset(0xff & ~UNUSED_BITS); + mutex_unlock(&lock); + return retval; +} + + +static struct board_selector ccd_selector = { + .select = ccd_select, + .release = ccd_unlock, + .exit = ccd_exit, + .reset = ccd_reset, + .reset_all = ccd_reset_all, + /* don't have a temp sensor dedicated to chain */ + .get_temp = dummy_get_temp, +}; + diff --git a/A1-board-selector-CCR.c b/A1-board-selector-CCR.c new file mode 100644 index 0000000000..1752d3c40c --- /dev/null +++ b/A1-board-selector-CCR.c @@ -0,0 +1,186 @@ +/* + * board selector support for TCA9535 used in Bitmine's CoinCraft Desk + * + * Copyright 2014 Zefir Kurtisi + * + * 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 "miner.h" + +#include "A1-board-selector.h" +#include "i2c-context.h" + + +static struct board_selector ccr_selector; + +static struct i2c_ctx *U1_tca9548; +static struct i2c_ctx *U3_tca9535; +static struct i2c_ctx *U4_tca9535; +static uint8_t active_chain; +static pthread_mutex_t lock; + +struct chain_mapping { + uint8_t chain_id; + uint8_t U1; + uint8_t U3p0; + uint8_t U3p1; +}; + +static const struct chain_mapping chain_mapping[CCR_MAX_CHAINS] = { + { 0, 0x01, 0x01, 0x00, }, + { 1, 0x01, 0x00, 0x80, }, + { 2, 0x02, 0x02, 0x00, }, + { 3, 0x02, 0x00, 0x40, }, + { 4, 0x04, 0x04, 0x00, }, + { 5, 0x04, 0x00, 0x20, }, + { 6, 0x08, 0x08, 0x00, }, + { 7, 0x08, 0x00, 0x10, }, + { 8, 0x10, 0x10, 0x00, }, + { 9, 0x10, 0x00, 0x08, }, + { 10, 0x20, 0x20, 0x00, }, + { 11, 0x20, 0x00, 0x04, }, + { 12, 0x40, 0x40, 0x00, }, + { 13, 0x40, 0x00, 0x02, }, + { 14, 0x80, 0x80, 0x00, }, + { 15, 0x80, 0x00, 0x01, }, +}; + +static void ccr_unlock(void) +{ + mutex_unlock(&lock); +} + +static void ccr_exit(void) +{ + if (U1_tca9548 != NULL) + U1_tca9548->exit(U1_tca9548); + if (U3_tca9535 != NULL) + U3_tca9535->exit(U3_tca9535); + if (U4_tca9535 != NULL) + U4_tca9535->exit(U4_tca9535); +} + + +extern struct board_selector *ccr_board_selector_init(void) +{ + mutex_init(&lock); + applog(LOG_INFO, "ccr_board_selector_init()"); + + /* detect all i2c slaves */ + U1_tca9548 = i2c_slave_open(I2C_BUS, 0x70); + U3_tca9535 = i2c_slave_open(I2C_BUS, 0x23); + U4_tca9535 = i2c_slave_open(I2C_BUS, 0x22); + if (U1_tca9548 == NULL || U3_tca9535 == NULL || U4_tca9535 == NULL) + goto fail; + + /* init I2C multiplexer */ + bool res = U1_tca9548->write(U1_tca9548, 0x00, 0x00) && + /* init reset selector */ + U3_tca9535->write(U3_tca9535, 0x06, 0x00) && + U3_tca9535->write(U3_tca9535, 0x07, 0x00) && + U3_tca9535->write(U3_tca9535, 0x02, 0x00) && + U3_tca9535->write(U3_tca9535, 0x03, 0x00) && + /* init chain selector */ + U4_tca9535->write(U4_tca9535, 0x06, 0x00) && + U4_tca9535->write(U4_tca9535, 0x07, 0x00) && + U4_tca9535->write(U4_tca9535, 0x02, 0x00) && + U4_tca9535->write(U4_tca9535, 0x03, 0x00); + + if (!res) + goto fail; + + return &ccr_selector; + +fail: + ccr_exit(); + return NULL; +} + +static bool ccr_select(uint8_t chain) +{ + if (chain >= CCR_MAX_CHAINS) + return false; + + mutex_lock(&lock); + if (active_chain == chain) + return true; + + active_chain = chain; + const struct chain_mapping *cm = &chain_mapping[chain]; + + if (!U1_tca9548->write(U1_tca9548, cm->U1, cm->U1)) + return false; + + if (!U4_tca9535->write(U4_tca9535, 0x02, cm->U3p0) || + !U4_tca9535->write(U4_tca9535, 0x03, cm->U3p1)) + return false; + + /* sanity check: ensure i2c command has been written before we leave */ + uint8_t tmp; + if (!U4_tca9535->read(U4_tca9535, 0x02, &tmp) || tmp != cm->U3p0) { + applog(LOG_ERR, "ccr_select: wrote 0x%02x, read 0x%02x", + cm->U3p0, tmp); + } + applog(LOG_DEBUG, "selected chain %d", chain); + return true; +} + +static bool __ccr_board_selector_reset(uint8_t p0, uint8_t p1) +{ + if (!U3_tca9535->write(U3_tca9535, 0x02, p0) || + !U3_tca9535->write(U3_tca9535, 0x03, p1)) + return false; + cgsleep_ms(RESET_LOW_TIME_MS); + if (!U3_tca9535->write(U3_tca9535, 0x02, 0x00) || + !U3_tca9535->write(U3_tca9535, 0x03, 0x00)) + return false; + cgsleep_ms(RESET_HI_TIME_MS); + return true; +} +// we assume we are already holding the mutex +static bool ccr_reset(void) +{ + const struct chain_mapping *cm = &chain_mapping[active_chain]; + applog(LOG_DEBUG, "resetting chain %d", cm->chain_id); + bool retval = __ccr_board_selector_reset(cm->U3p0, cm->U3p1); + return retval; +} + +static bool ccr_reset_all(void) +{ + mutex_lock(&lock); + bool retval = __ccr_board_selector_reset(0xff, 0xff); + mutex_unlock(&lock); + return retval; +} + +static uint8_t ccr_get_temp(uint8_t sensor_id) +{ + if ((active_chain & 1) != 0 || sensor_id != 0) + return 0; + + struct i2c_ctx *U7 = i2c_slave_open(I2C_BUS, 0x4c); + if (U7 == NULL) + return 0; + + uint8_t retval = 0; + if (!U7->read(U7, 0, &retval)) + retval = 0; + U7->exit(U7); + return retval; +} + +static struct board_selector ccr_selector = { + .select = ccr_select, + .release = ccr_unlock, + .exit = ccr_exit, + .reset = ccr_reset, + .reset_all = ccr_reset_all, + .get_temp = ccr_get_temp, +}; + diff --git a/A1-board-selector.h b/A1-board-selector.h new file mode 100644 index 0000000000..bcadf7d7ed --- /dev/null +++ b/A1-board-selector.h @@ -0,0 +1,50 @@ +#ifndef A1_BOARD_SELECTOR_H +#define A1_BOARD_SELECTOR_H + +#include +#include + +#define RESET_LOW_TIME_MS 200 +#define RESET_HI_TIME_MS 100 + +struct board_selector { + /* destructor */ + void (*exit)(void); + /* select board and chip chain for given chain index*/ + bool (*select)(uint8_t chain); + /* release access to selected chain */ + void (*release)(void); + /* reset currently selected chain */ + bool (*reset)(void); + /* reset all chains on board */ + bool (*reset_all)(void); + /* get temperature for selected chain at given sensor */ + uint8_t (*get_temp)(uint8_t sensor); + /* prepare board (voltage) for given sys_clock */ + bool (*prepare_clock)(int clock_khz); +}; + +static bool dummy_select(uint8_t b) { (void)b; return true; } +static void dummy_void(void) { }; +static bool dummy_bool(void) { return true; } +//static uint8_t dummy_u8(void) { return 0; } +static uint8_t dummy_get_temp(uint8_t s) { (void)s; return 0; } +static bool dummy_prepare_clock(int c) { (void)c; return true; } + +static const struct board_selector dummy_board_selector = { + .exit = dummy_void, + .select = dummy_select, + .release = dummy_void, + .reset = dummy_bool, + .reset_all = dummy_bool, + .get_temp = dummy_get_temp, + .prepare_clock = dummy_prepare_clock, +}; + +/* CoinCraft Desk and Rig board selector constructors */ +#define CCD_MAX_CHAINS 5 +#define CCR_MAX_CHAINS 16 +extern struct board_selector *ccd_board_selector_init(void); +extern struct board_selector *ccr_board_selector_init(void); + +#endif /* A1_BOARD_SELECTOR_H */ diff --git a/A1-common.h b/A1-common.h new file mode 100644 index 0000000000..b048a998b7 --- /dev/null +++ b/A1-common.h @@ -0,0 +1,91 @@ +#ifndef A1_COMMON_H +#define A1_COMMON_H + +#include +#include +#include + +/********** work queue */ +struct work_ent { + struct work *work; + struct list_head head; +}; + +struct work_queue { + int num_elems; + struct list_head head; +}; + +/********** chip and chain context structures */ +/* the WRITE_JOB command is the largest (2 bytes command, 56 bytes payload) */ +#define WRITE_JOB_LENGTH 58 +#define MAX_CHAIN_LENGTH 64 +/* + * For commands to traverse the chain, we need to issue dummy writes to + * keep SPI clock running. To reach the last chip in the chain, we need to + * write the command, followed by chain-length words to pass it through the + * chain and another chain-length words to get the ACK back to host + */ +#define MAX_CMD_LENGTH (WRITE_JOB_LENGTH + MAX_CHAIN_LENGTH * 2 * 2) + +struct A1_chip { + int num_cores; + int last_queued_id; + struct work *work[4]; + /* stats */ + int hw_errors; + int stales; + int nonces_found; + int nonce_ranges_done; + + /* systime in ms when chip was disabled */ + int cooldown_begin; + /* number of consecutive failures to access the chip */ + int fail_count; + /* mark chip disabled, do not try to re-enable it */ + bool disabled; +}; + +struct A1_chain { + int chain_id; + struct cgpu_info *cgpu; + struct mcp4x *trimpot; + int num_chips; + int num_cores; + int num_active_chips; + int chain_skew; + uint8_t spi_tx[MAX_CMD_LENGTH]; + uint8_t spi_rx[MAX_CMD_LENGTH]; + struct spi_ctx *spi_ctx; + struct A1_chip *chips; + pthread_mutex_t lock; + + struct work_queue active_wq; + + /* mark chain disabled, do not try to re-enable it */ + bool disabled; + uint8_t temp; + int last_temp_time; +}; + +#define MAX_CHAINS_PER_BOARD 2 +struct A1_board { + int board_id; + int num_chains; + struct A1_chain *chain[MAX_CHAINS_PER_BOARD]; +}; + +/********** config paramters */ +struct A1_config_options { + int ref_clk_khz; + int sys_clk_khz; + int spi_clk_khz; + /* limit chip chain to this number of chips (testing only) */ + int override_chip_num; + int wiper; +}; + +/* global configuration instance */ +extern struct A1_config_options A1_config_options; + +#endif /* A1_COMMON_H */ diff --git a/A1-desk-board-selector.c b/A1-desk-board-selector.c new file mode 100644 index 0000000000..fdc1a6d5c1 --- /dev/null +++ b/A1-desk-board-selector.c @@ -0,0 +1,158 @@ +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include "miner.h" + +struct pcf8575_ctx { + uint8_t addr; + uint8_t p0; + uint8_t p1; + int file; + uint8_t active_board; + pthread_mutex_t lock; +}; + +static struct pcf8575_ctx board_ctx = { 0x27, 0xff, 0xff, -1, .active_board = 255,}; + + +#define UNUSED_BITS 0xe0 +#define SLEEP_MS_AFTER_CS 0 +static bool pcf8575_write(void) +{ + union i2c_smbus_data data; + data.byte = board_ctx.p1 | UNUSED_BITS; + + struct i2c_smbus_ioctl_data args; + __s32 err; + + args.read_write = I2C_SMBUS_WRITE; + args.command = board_ctx.p0 | UNUSED_BITS; + args.size = I2C_SMBUS_BYTE_DATA; + args.data = &data; + + err = ioctl(board_ctx.file, I2C_SMBUS, &args); + if (err == -1) { + fprintf(stderr, + "Error: Failed to write: %s\n", + strerror(errno)); + err = -errno; + } else { + applog(LOG_DEBUG, "written: 0x%02x, 0x%02x", board_ctx.p0, board_ctx.p1); +// usleep(25000); + cgsleep_ms(SLEEP_MS_AFTER_CS); + } + return err == 0; +} + +void lock_board_selector(void) +{ +// applog(LOG_WARNING, "lock_board_selector()"); + mutex_lock(&board_ctx.lock); +} + +void unlock_board_selector(void) +{ +// applog(LOG_WARNING, "unlock_board_selector()"); + mutex_unlock(&board_ctx.lock); +} + +bool a1_board_selector_init(void) +{ + mutex_init(&board_ctx.lock); + applog(LOG_WARNING, "a1_board_selector_init()"); + + board_ctx.file = open("/dev/i2c-1", O_RDWR); + if (board_ctx.file < 0) { + fprintf(stderr, + "Error: Could not open i2c-1: %s\n", + board_ctx.addr, strerror(errno)); + return false; + } + + if (ioctl(board_ctx.file, I2C_SLAVE, board_ctx.addr) < 0) { + fprintf(stderr, + "Error: Could not set address to 0x%02x: %s\n", + board_ctx.addr, strerror(errno)); + return false; + } + return pcf8575_write(); +} + +void a1_board_selector_exit(void) +{ + close(board_ctx.file); + board_ctx.file = -1; +} + +bool a1_board_selector_select_board(uint8_t board) +{ + if (board > 7) + return false; + +// applog(LOG_WARNING, "board_selector_select_board(%d)", board); + lock_board_selector(); + if (board_ctx.active_board == board) + return true; + + board_ctx.active_board = board; + board_ctx.p0 = 1 << board_ctx.active_board; + board_ctx.p1 = 0xff; + bool retval = pcf8575_write(); + return retval; +} + +static bool __board_selector_reset(void) +{ + board_ctx.p1 = ~board_ctx.p0; + if (!pcf8575_write()) + return false; + usleep(1000000); + board_ctx.p1 = 0xff; + if (!pcf8575_write()) + return false; + usleep(1000000); + return true; +} +// we assume we are already holding the mutex +bool a1_board_selector_reset_board(void) +{ +// lock_board_selector(); + bool retval = __board_selector_reset(); +// unlock_board_selector(); + return retval; +} + +bool a1_board_selector_reset_all_boards(void) +{ + lock_board_selector(); + board_ctx.p1 = 0; + bool retval = __board_selector_reset(); + unlock_board_selector(); + return retval; +} + +#if 0 +int main(void) +{ + if (init_pcf8575(&board_ctx)) { + if (!pcf8575_write(&g_ctx)) { + fprintf(stderr, + "Error: Failed to write: %s\n", + strerror(errno)); + } + a1_board_selector_exit(&g_ctx); + } + return 0; +} +#endif +///////////////////////////////////////////////////////////////////////////// diff --git a/A1-trimpot-mcp4x.c b/A1-trimpot-mcp4x.c new file mode 100644 index 0000000000..a9244fd9c0 --- /dev/null +++ b/A1-trimpot-mcp4x.c @@ -0,0 +1,116 @@ +/* + * support for MCP46x digital trimpot used in Bitmine's products + * + * Copyright 2014 Zefir Kurtisi + * + * 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 +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include "miner.h" + +#include "A1-trimpot-mcp4x.h" + + +static bool mcp4x_check_status(int file) +{ + union i2c_smbus_data data; + struct i2c_smbus_ioctl_data args; + + args.read_write = I2C_SMBUS_READ; + args.command = ((5 & 0x0f) << 4) | 0x0c; + args.size = I2C_SMBUS_WORD_DATA; + args.data = &data; + + return ioctl(file, I2C_SMBUS, &args) >= 0; +} + +static uint16_t mcp4x_get_wiper(struct mcp4x *me, uint8_t id) +{ + assert(id < 2); + union i2c_smbus_data data; + struct i2c_smbus_ioctl_data args; + + args.read_write = I2C_SMBUS_READ; + args.command = ((id & 0x0f) << 4) | 0x0c; + args.size = I2C_SMBUS_WORD_DATA; + args.data = &data; + + if (ioctl(me->file, I2C_SMBUS, &args) < 0) { + applog(LOG_ERR, "Failed to read id %d: %s\n", id, + strerror(errno)); + return 0xffff; + } + return htobe16(data.word & 0xffff); +} + +static bool mcp4x_set_wiper(struct mcp4x *me, uint8_t id, uint16_t w) +{ + assert(id < 2); + union i2c_smbus_data data; + data.word = w; + + struct i2c_smbus_ioctl_data args; + + args.read_write = I2C_SMBUS_WRITE; + args.command = (id & 0x0f) << 4; + args.size = I2C_SMBUS_WORD_DATA; + args.data = &data; + + if (ioctl(me->file, I2C_SMBUS, &args) < 0) { + applog(LOG_ERR, "Failed to read id %d: %s\n", id, + strerror(errno)); + return false; + } + return me->get_wiper(me, id) == w; +} + +void mcp4x_exit(struct mcp4x *me) +{ + close(me->file); + free(me); +} + +struct mcp4x *mcp4x_init(uint8_t addr) +{ + struct mcp4x *me; + int file = open("/dev/i2c-1", O_RDWR); + if (file < 0) { + applog(LOG_INFO, "Failed to open i2c-1: %s\n", strerror(errno)); + return NULL; + } + + if (ioctl(file, I2C_SLAVE, addr) < 0) + return NULL; + + if (!mcp4x_check_status(file)) + return NULL; + + me = malloc(sizeof(*me)); + assert(me != NULL); + + me->addr = addr; + me->file = file; + me->exit = mcp4x_exit; + me->get_wiper = mcp4x_get_wiper; + me->set_wiper = mcp4x_set_wiper; + return me; +} + diff --git a/A1-trimpot-mcp4x.h b/A1-trimpot-mcp4x.h new file mode 100644 index 0000000000..b7ff6008ac --- /dev/null +++ b/A1-trimpot-mcp4x.h @@ -0,0 +1,19 @@ +#ifndef TRIMPOT_MPC4X_H +#define TRIMPOT_MPC4X_H + +#include +#include + + +struct mcp4x { + uint16_t (*get_wiper)(struct mcp4x *me, uint8_t id); + bool (*set_wiper)(struct mcp4x *me, uint8_t id, uint16_t w); + void (*exit)(struct mcp4x *me); + uint8_t addr; + int file; +}; + +/* constructor */ +extern struct mcp4x *mcp4x_init(uint8_t addr); + +#endif /* TRIMPOT_MPC4X_H */ diff --git a/ADL_SDK/.gitignore b/ADL_SDK/.gitignore deleted file mode 100644 index 05c579eed4..0000000000 --- a/ADL_SDK/.gitignore +++ /dev/null @@ -1,3 +0,0 @@ -adl_defines.h -adl_sdk.h -adl_structures.h diff --git a/ADL_SDK/readme.txt b/ADL_SDK/readme.txt deleted file mode 100644 index afc3708ec1..0000000000 --- a/ADL_SDK/readme.txt +++ /dev/null @@ -1,3 +0,0 @@ -Please insert AMD ADL files adl_defines.h adl_sdk.h adl_structures.h here. -They can be found here: -http://developer.amd.com/tools/graphics-development/display-library-adl-sdk/ diff --git a/API-README b/API-README index 2829b95d10..78564b0c08 100644 --- a/API-README +++ b/API-README @@ -65,11 +65,11 @@ response, otherwise it replies with text formatted as described further below. The JSON request format required is '{"command":"CMD","parameter":"PARAM"}' (though of course parameter is not required for all requests) where "CMD" is from the "Request" column below and "PARAM" would be e.g. -the ASC/GPU number if required. +the ASC/PGA number if required. -An example request in both formats to set GPU 0 fan to 80%: - gpufan|0,80 - {"command":"gpufan","parameter":"0,80"} +An example request in both formats to disable Hotplug: + hotplug|0 + {"command":"hotplug","parameter":"0"} The format of each reply (unless stated otherwise) is a STATUS section followed by an optional detail section @@ -100,7 +100,7 @@ The STATUS section is: Standard long time of request in seconds Code=N - Each unique reply has a unigue Code (See api.c - #define MSG_NNNNNN) + Each unique reply has a unique Code (See api.c - #define MSG_NNNNNN) Msg=string Message matching the Code value N @@ -109,6 +109,36 @@ The STATUS section is: This defaults to the cgminer version but is the value of --api-description if it was specified at runtime. +With API V3.1 you can also request multiple report replies in a single command +request +e.g. to request both summary and devs, the command would be summary+devs + +This is only available for report commands that don't need parameters, +and is not available for commands that change anything +Any parameters supplied will be ignored + +The extra formatting of the result is to have a section for each command +e.g. CMD=summary|STATUS=....|CMD=devs|STATUS=... +With JSON, each result is within a section of the command name +e.g. {"summary":{"STATUS":[{"STATUS":"S"...}],"SUMMARY":[...],"id":1}, + "devs":{"STATUS":[{"STATUS:"S"...}],"DEVS":[...],"id":1},"id":1} + +As before, if you supply bad JSON you'll just get a single 'E' STATUS section +in the old format, since it doesn't switch to using the new format until it +correctly processes the JSON and can match a '+' in the command + +If you request a command multiple times, e.g. devs+devs +you'll just get it once +If this results in only one command, it will still use the new layout +with just the one command + +If you request a command that can't be used due to requiring parameters, +a command that isn't a report, or an invalid command, you'll get an 'E' STATUS +for that one but it will still attempt to process all other commands supplied + +Blank/missing commands are ignored e.g. +devs++ +will just show 'devs' using the new layout + For API version 1.10 and later: The list of requests - a (*) means it requires privileged access - and replies: @@ -119,15 +149,12 @@ The list of requests - a (*) means it requires privileged access - and replies: API=API| version config CONFIG Some miner configuration information: - GPU Count=N, <- the number of GPUs ASC Count=N, <- the number of ASCs PGA Count=N, <- the number of PGAs Pool Count=N, <- the number of Pools - ADL=X, <- Y or N if ADL is compiled in the code - ADL in use=X, <- Y or N if any GPU has ADL Strategy=Name, <- the current pool strategy Log Interval=N, <- log interval (--log N) - Device Code=GPU ICA , <- spaced list of compiled + Device Code=ICA , <- spaced list of compiled device drivers OS=Linux/Apple/..., <- operating System Failover-Only=true/false, <- failover-only setting @@ -141,8 +168,8 @@ The list of requests - a (*) means it requires privileged access - and replies: pools POOLS The status of each pool e.g. Pool=0,URL=http://pool.com:6311,Status=Alive,...| - devs DEVS Each available GPU, PGA and ASC with their details - e.g. GPU=0,Accepted=NN,MHS av=NNN,...,Intensity=D| + devs DEVS Each available PGA and ASC with their details + e.g. ASC=0,Accepted=NN,MHS av=NNN,...,Intensity=D| Last Share Time=NNN, <- standand long time in sec (or 0 if none) of last accepted share Last Share Pool=N, <- pool number (or -1 if none) @@ -151,8 +178,15 @@ The list of requests - a (*) means it requires privileged access - and replies: Will not report PGAs if PGA mining is disabled Will not report ASCs if ASC mining is disabled - gpu|N GPU The details of a single GPU number N in the same - format and details as for DEVS + edevs[|old] DEVS The same as devs, except it ignores blacklisted + devices and zombie devices + If you specify the optional 'old' parameter, then + the output will include zombie devices that became + zombies less than 'old' seconds ago + A value of zero for 'old', which is the default, + means ignore all zombies + It will return an empty list of devices if all + devices are blacklisted or zombies pga|N PGA The details of a single PGA number N in the same format and details as for DEVS @@ -160,8 +194,6 @@ The list of requests - a (*) means it requires privileged access - and replies: Use 'pgacount' or 'config' first to see if there are any - gpucount GPUS Count=N| <- the number of GPUs - pgacount PGAS Count=N| <- the number of PGAs Always returns 0 if PGA mining is disabled @@ -179,7 +211,7 @@ The list of requests - a (*) means it requires privileged access - and replies: addpool|URL,USR,PASS (*) none There is no reply section just the STATUS section stating the results of attempting to add pool N - The Msg includes the pool URL + The Msg includes the pool number and URL Use '\\' to get a '\' and '\,' to include a comma inside URL, USR or PASS @@ -188,6 +220,10 @@ The list of requests - a (*) means it requires privileged access - and replies: stating the results of changing pool priorities See usage below + poolquota|N,Q (*) + none There is no reply section just the STATUS section + stating the results of changing pool quota to Q + disablepool|N (*) none There is no reply section just the STATUS section stating the results of disabling pool N @@ -199,42 +235,6 @@ The list of requests - a (*) means it requires privileged access - and replies: The Msg includes the pool URL N.B. all details for the pool will be lost - gpuenable|N (*) - none There is no reply section just the STATUS section - stating the results of the enable request - - gpudisable|N (*) - none There is no reply section just the STATUS section - stating the results of the disable request - - gpurestart|N (*) - none There is no reply section just the STATUS section - stating the results of the restart request - - gpuintensity|N,I (*) - none There is no reply section just the STATUS section - stating the results of setting GPU N intensity - to I - - gpumem|N,V (*) - none There is no reply section just the STATUS section - stating the results of setting GPU N memoryclock - to V MHz - - gpuengine|N,V (*) - none There is no reply section just the STATUS section - stating the results of setting GPU N clock - to V MHz - - gpufan|N,V (*) - none There is no reply section just the STATUS section - stating the results of setting GPU N fan speed - to V% - - gpuvddc|N,V (*) - none There is no reply section just the STATUS section - stating the results of setting GPU N vddc to V - save|filename (*) none There is no reply section just the STATUS section stating success or failure saving the cgminer @@ -242,14 +242,14 @@ The list of requests - a (*) means it requires privileged access - and replies: The filename is optional and will use the cgminer default if not specified - quit (*) none There is no status section but just a single "BYE" - reply before cgminer quits + quit (*) none Status is a single "BYE" reply before cgminer + quits notify NOTIFY The last status and history count of each devices problem This lists all devices including those not supported by the 'devs' command e.g. - NOTIFY=0,Name=GPU,ID=0,Last Well=1332432290,...| + NOTIFY=0,Name=ASC,ID=0,Last Well=1332432290,...| privileged (*) none There is no reply section just the STATUS section @@ -273,13 +273,13 @@ The list of requests - a (*) means it requires privileged access - and replies: none There is no reply section just the STATUS section stating the results of the identify request This is only available if PGA mining is enabled - and currently only BFL singles support this - command + and currently only BFL singles and Cairnsmore1's + with the appropriate firmware support this command On a BFL single it will flash the led on the front of the device for appoximately 4s - All other non BFL PGA devices will return a + All other non BFL,ICA PGA devices will return a warning status message stating that they dont - support it + support it. Non-CMR ICAs will ignore the command. This adds a 4s delay to the BFL share being processed so you may get a message stating that procssing took longer than 7000ms if the request @@ -291,10 +291,10 @@ The list of requests - a (*) means it requires privileged access - and replies: devdetails DEVDETAILS Each device with a list of their static details This lists all devices including those not supported by the 'devs' command - e.g. DEVDETAILS=0,Name=GPU,ID=0,Driver=opencl,...| + e.g. DEVDETAILS=0,Name=ASC,ID=0,Driver=yuu,...| - restart (*) none There is no status section but just a single - "RESTART" reply before cgminer restarts + restart (*) none Status is a single "RESTART" reply before cgminer + restarts stats STATS Each device or pool that has 1 or more getworks with a list of stats regarding getwork times @@ -303,7 +303,17 @@ The list of requests - a (*) means it requires privileged access - and replies: Device drivers are also able to add stats to the end of the details returned - check|cmd COMMAND Exists=Y/N, <- 'cmd' exists in this version + estats[|old] STATS The same as stats, except it ignores blacklisted + devices, zombie devices and pools + If you specify the optional 'old' parameter, then + the output will include zombie devices that became + zombies less than 'old' seconds ago + A value of zero for 'old', which is the default, + means ignore all zombies + It will return an empty list of devices if all + devices are blacklisted or zombies + + check|cmd CHECK Exists=Y/N, <- 'cmd' exists in this version Access=Y/N| <- you have access to use 'cmd' failover-only|true/false (*) @@ -360,6 +370,7 @@ The list of requests - a (*) means it requires privileged access - and replies: The current options are: MMQ opt=clock val=160 to 230 (a multiple of 2) + CMR opt=clock val=100 to 220 zero|Which,true/false (*) none There is no reply section just the STATUS section @@ -429,10 +440,30 @@ The list of requests - a (*) means it requires privileged access - and replies: help message about the options available The current options are: - AVA+BTB opt=freq val=256 to 450 - chip frequency - BTB opt=millivolts val=1000 to 1310 - corevoltage - -When you enable, disable or restart a GPU, PGA or ASC, you will also get + AVA+BTB opt=freq val=256 to 1024 - chip frequency + BTB opt=millivolts val=1000 to 1400 - corevoltage + MBA opt=reset val=0 to chipcount - reset a chip + BMA opt=volt val=0-9 opt=clock val=0-15 + MBA opt=freq val=0-chip:100-1400 - set chip freq + MBA opt=ledcount val=0-100 - chip count for led + MBA opt=ledlimit val=0-200 - led off below GHs + MBA opt=spidelay val=0-9999 - SPI per I/O delay + MBA opt=spireset i|s0-9999 - SPI regular reset + MBA opt=spisleep val=0-9999 - SPI reset sleep ms + + lcd LCD An all-in-one short status summary of the miner + e.g. Elapsed,GHS av,GHS 5m,GHS 5s,Temp, + Last Share Difficulty,Last Share Time, + Best Share,Last Valid Work,Found Blocks, + Pool,User| + + lockstats (*) none There is no reply section just the STATUS section + stating the results of the request + A warning reply means lock stats are not compiled + into cgminer + The API writes all the lock stats to stderr + +When you enable, disable or restart a PGA or ASC, you will also get Thread messages in the cgminer status window The 'poolpriority' command can be used to reset the priority order of multiple @@ -465,6 +496,10 @@ api-example.php - a php script to access the API You must modify the line "$socket = getsock('127.0.0.1', 4028);" at the beginning of "function request($cmd)" to change where it looks for cgminer +api-example.rb - a Ruby script to access the API. + usage: ruby api-example.rb command[:parameter] [HOST [PORT]] +This script prints the parsed cgminer API response + API.java/API.class a java program to access the API (with source code) usAge is: java API command address port @@ -485,6 +520,83 @@ miner.php - an example web page to access the API Feature Changelog for external applications using the API: +--------- + +API V3.5 (cgminer v4.7.0) + +- Made quit and restart return valid JSON as a STATUS mirroring the reqest. +- Made addpool return what pool number the added pool is. + +--------- + +API V3.4 (cgminer v4.3.?) + +Added API commands: + 'lcd' - An all-in-one short status summary of the miner + +--------- + +API V3.3 (cgminer v4.2.0) + +Added API commands: + 'edevs' - Only enabled devices, for 'devs' + 'estats' - Only enabled devices, for 'stats' + +--------- + +API V3.2 (cgminer v4.1.0) + +Fix for: +HEX32 data type in the API version v3.1 JSON - since cgminer v3.12.1 - +returns an incorrect formatted json data element for the API stats command +for HashFast hardware + +--------- + +API V3.1 (cgminer v3.12.1) + +Multiple report request command with '+' e.g. summary+devs + +--------- + +API V3.0 (cgminer v3.11.0) + +Allow unlimited size replies + +--------- + +API V2.0 (cgminer v3.8.0) + +Removed all GPU related commands and information from the replies + +--------- + +API V1.32 (cgminer v3.6.5) + +Modified API commands: + 'devs' 'gpu' 'pga' and 'asc' - add 'Device Elapsed' + +--------- + +API V1.31 (cgminer v3.6.3) + +Added API command: + 'lockstats' - display cgminer dev lock stats if compiled in + +Modified API command: + 'summary' - add 'MHS %ds' (where %d is the log interval) + +--------- + +API V1.30 (cgminer v3.4.3) + +Added API command: + 'poolquota' - Set pool quota for load-balance strategy. + +Modified API command: + 'pools' - add 'Quota' + +--------- API V1.29 (cgminer v3.4.1) @@ -578,7 +690,7 @@ Added API commands: Modified API commands: 'summary' - add 'Best Share' -Modifed output: +Modified output: each MMQ shows up as 4 devices, each with it's own stats ---------- @@ -1025,8 +1137,8 @@ It will be superceded by myminer.php --------- -The example.php above also shows how to define more that one rig to -be shown my miner.php +The example myminer.php above also shows how to define more that one rig +to be shown my miner.php Each rig string is 2 or 3 values seperated by colons ':' They are simply an IP address or host name, followed by the @@ -1177,20 +1289,49 @@ However, if $readonly is true, it will not display them --------- +Default: + $rigport = 4028; + +Default port to use if any $rigs entries don't specify the port number + +--------- + Default: $rigs = array('127.0.0.1:4028'); Set $rigs to an array of your cgminer rigs that are running - format: 'IP:Port' or 'Host:Port' or 'Host:Port:Name' + format: 'IP' or 'Host' or 'IP:Port' or 'Host:Port' or 'Host:Port:Name' If you only have one rig, it will just show the detail of that rig If you have more than one rig it will show a summary of all the rigs with buttons to show the details of each rig - the button contents will be 'Name' rather than rig number, if you specify 'Name' +If Port is missing or blank, it will try $rigport e.g. $rigs = array('127.0.0.1:4028','myrig.com:4028:Sugoi'); --------- +Default: + $rignames = false; + +Set $rignames to false to not affect the display. +Set $rignames to one of 'ip' or 'ipx' to alter the name displayed +if the rig doesn't have a 'name' in $rigs +Currently: + 'ip' means use the 4th byte of the rig IP address as an integer + 'ipx' means use the 4th byte of the rig IP address as 2 hex bytes + +--------- + +Default: + $rigbuttons = true; + +Set $rigbuttons to false to display a link rather than a button on +the left of any summary table with rig buttons, in order to reduce +the height of the table cells + +--------- + Default: $mcast = false; @@ -1247,13 +1388,35 @@ N.B. the accuracy of the timing used to wait for the replies is --------- +Default: + $mcastretries = 0; + +Set $mcastretries to the number of times to retry the multicast + +If $mcastexpect is 0, this is simply the number of extra times +that it will send the multicast request +N.B. cgminer doesn't listen for multicast requests for 1000ms after +each one it hears + +If $mcastexpect is > 0, it will stop looking for replies once it +has found at least $mcastexpect rigs, but it only checks this rig +limit each time it reaches the $mcasttimeout limit, thus it can find +more than $mcastexpect rigs if more exist +It will send the multicast message up to $mcastretries extra times or +until it has found at least $mcastexpect rigs +However, when using $mcastretries, it is possible for it to sometimes +ignore some rigs on the network if $mcastexpect is less than the +number of rigs on the network and some rigs are too slow to reply + +--------- + Default: $allowgen = false; -Set $allowgen to true to allow customsummarypages to use 'gen' -false means ignore any 'gen' options -This is disabled by default due to the possible security risk -of using it, see the end of this document for an explanation +Set $allowgen to true to allow customsummarypages to use 'gen' and 'bgen' +false means ignore any 'gen' or 'bgen' options +This is disabled by default due to the possible security risk of using it +See the end of this document for an explanation --------- @@ -1340,20 +1503,25 @@ $autorefresh = default value, 0 means dont auto-refresh --------- Default: - $placebuttons = 'top'; - -Where to place the Refresh, Summary, Custom Pages, Quit, etc. buttons + $miner_font_family = 'verdana,arial,sans'; + $miner_font_size = '13pt'; -Valid values are: 'top' 'bot' 'both' - anything else means don't show them - case sensitive +Change these to set the font and font size used on the web page --------- Default: - $miner_font_family = 'verdana,arial,sans'; - $miner_font_size = '13pt'; + $add_css_names = array(); -Change these to set the font and font size used on the web page +List of CSS names to add to the CSS style object + e.g. array('td.cool' => false); +true/false to not include the default $miner_font +The CSS name/value pairs must be defined in $colouroverride below + +This allows you to create multiple complete CSS styles, optionally +using a different font to the default used/specified for all other +styles, and then when using the class name in a custom formatting +function (fmt) in a customsummarypage, it can use this style --------- @@ -1374,6 +1542,20 @@ you could do the following: 'td.h background' => 'blue' ); +You can also add your own CSS styles to be used by a customsummarypage +custom format function, if you specify the class name in $add_css_names +and put the class styles in $colouroverride + +--------- + +Default: + $placebuttons = 'top'; + +Where to place the Refresh, Summary, Custom Pages, Quit, etc. buttons + +Valid values are: 'top' 'bot' 'both' + anything else means don't show them - case sensitive + --------- Default: @@ -1392,16 +1574,22 @@ The section defines what data you want in the summary table and the Fields define what data you want shown from that section Standard sections are: - SUMMARY, POOL, PGA, GPU, NOTIFY, CONFIG, DEVDETAILS, DEVS, STATS, COIN + SUMMARY, POOL, PGA, GPU, NOTIFY, CONFIG, DEVDETAILS, DEVS, EDEVS, STATS, + ESTATS, COIN Fields are the names as shown on the headers on the normal pages +There is a special field name '#' that will total to the number of rows +displayed in the custom summary page +In the actual row output it is a row counter per rig + Fields can be 'name=new name' to display 'name' with a different heading 'new name' There are also now joined sections: - SUMMARY+POOL, SUMMARY+DEVS, SUMMARY+CONFIG, DEVS+NOTIFY, DEVS+DEVDETAILS - SUMMARY+COIN + SUMMARY+POOL, SUMMARY+DEVS, SUMMARY+EDEVS, DEVS+STATS, EDEVS+ESTATS, + POOL+STATS plus many more +See the miner.php function joinsections() for the full list These sections are an SQL join of the two sections and the fields in them are named section.field where section. is the section the field comes from @@ -1518,10 +1706,11 @@ The example given: With cgminer 2.10.2 and later, miner.php includes an extension to the custom pages that allows you to apply SQL style commands to the data: where, group, and having -cgminer 3.4.2 also includes another option 'gen' +cgminer 3.4.2 and later also includes another option 'gen' +cgminer 4.2.0 and later also includes another option 'fmt' +cgminer 4.2.1 and later also includes another option 'bgen' -As an example, miner.php includes a more complex custom page called 'Pools' -this includes the extension: +An example of an 'ext' section in a more complex custom summary page: $poolsext = array( 'POOL+STATS' => array( @@ -1535,8 +1724,30 @@ $poolsext = array( 'STATS.Times Recv' => 'sum', 'STATS.Bytes Recv' => 'sum'), 'gen' => array('AvShr', 'POOL.Difficulty Accepted/max(POOL.Accepted,1)), - 'having' => array(array('STATS.Bytes Recv', '>', 0))) -); + 'having' => array(array('STATS.Bytes Recv', '>', 0)), + 'fmt' => 'myfmtfunc')); + +function myfmtfunc($section, $name, $value, $when, $alldata, + $warnclass, $errorclass, $hiclass, $loclass, $totclass); +{ + $ret = ''; + $class = ''; + switch ($section.'.'.$name) + { + case 'GEN.AvShr': + $ret = number_format((float)$value, 2); + if ($value == 0) + $class = $errorclass; + break; + // Nonsence example :) since total would show the sum of the averages + case 'total.AvShr': + $ret = $value; + if ($value == 0) + $class = $warnclass; + break; + } + return array($ret, $class); +} This allows you to group records together from one or more rigs In the example, you'll get each Pool (with the same URL+Stratum+GBT settings) @@ -1577,7 +1788,7 @@ You can only see fields listed in 'group' and 'calc' A 'calc' is formatted as: 'Field' => 'function' The current list of operations available for 'calc' are: -'sum', 'avg', 'min', 'max', 'lo', 'hi', 'coount', 'any' +'sum', 'avg', 'min', 'max', 'lo', 'hi', 'count', 'any' The first 4 are as expected - the numerical sum, average, minimum or maximum 'lo' is the first string of the list, sorted ignoring case 'hi' is the last string of the list, sorted ignoring case @@ -1587,20 +1798,55 @@ The first 4 are as expected - the numerical sum, average, minimum or maximum 'any' is effectively random: the field value in the 1st row of the grouped data An unrecognised 'function' uses 'any' -A 'gen' allows you to generate new fields from any php valid function of any -of the other fields + +A 'fmt' allows you to specify a function to be called by miner.php to format +data to be displayed in the output html +If the function doesn't exist in miner.php or myminer.php, then it will be +ignored +If the function returns a $ret value (see the example 'myfmtfunc' above) then +that will be displayed, however if $ret is empty, then the normal formatting +code will process the data to be displayed +Thus, if there is no formatting code in miner.php for the field value, then it +will be displayed as it was received from the API +i.e. this allows you to either supply some php code to format field values +that are not formatted by miner.php, or you can also override the formatting +done by miner.php itself for your chosen list of field data +You can return an ' ' if you wish to force it to display as blank +Use the example 'myfmtfunc' above as a template to write your own +Note that your provided function will be called for all data being displayed, +so you should use the 'case' layout as in the example to select the data fields +you wish to format, but return '' for fields you don't wish to change the way +they are formatted +The 2nd return field is the name of a CSS class in $colourtable or created in +your own $add_css_names and $colouroverride +The value you return can stay in effect even if you return an empty $ret, if +the default formatting function for the field doesn't set the $class variable +The fields passed to your function by miner.php: + $warnclass, $errorclass, $hiclass, $loclass, $totclass +contain the default class names used for formatting + + +A 'gen' or 'bgen' allows you to generate new fields from any php valid function +of any of the other fields e.g. 'gen' => array('AvShr', 'POOL.Difficulty Accepted/max(POOL.Accepted,1)), will generate a new field called GEN.AvShr that is the function shown, which in this case is the average difficulty of each share submitted -THERE IS A SECURITY RISK WITH HOW GEN WORKS +The difference between 'bgen' and 'gen' is that 'bgen' is done before doing +the 'group' and 'calc', however 'gen' is done after doing 'group' and 'calc' +This means that 'group' and 'calc' can also use 'bgen' fields +As before, 'gen' fields act on the results of the 'group' and 'calc' +If there is no 'group' or 'calc' then they both will produce the same results +Note that 'gen' fields are called 'GEN.field' and 'bgen' fields, 'BGEN.field' + +THERE IS A SECURITY RISK WITH HOW GEN/BGEN WORKS It simply replaces all the variables with their values and then requests PHP -the execute the formula - thus if a field value returned from a cgminer API +to execute the formula - thus if a field value returned from a cgminer API request contained PHP code, it could be executed by your web server Of course cgminer doesn't do this, but if you do not control the cgminer that returns the data in the API calls, someone could modify cgminer to return a -PHP string in a field you use in 'gen' -Thus use 'gen' at your own risk +PHP string in a field you use in 'gen' or 'bgen' +Thus use 'gen' and 'bgen' at your own risk If someone feels the urge to write a mathematical interpreter in PHP to get around this risk, feel free to write one and submit it to the API author for consideration diff --git a/API.class b/API.class index d506b93dc8..0fdf7342d8 100644 Binary files a/API.class and b/API.class differ diff --git a/API.java b/API.java index 74c57a2140..8ba099100e 100644 --- a/API.java +++ b/API.java @@ -88,7 +88,7 @@ public void process(String cmd, InetAddress ip, int port) throws Exception { socket = new Socket(ip, port); PrintStream ps = new PrintStream(socket.getOutputStream()); - ps.print(cmd.toLowerCase().toCharArray()); + ps.print(cmd.toCharArray()); ps.flush(); InputStreamReader isr = new InputStreamReader(socket.getInputStream()); diff --git a/ASIC-README b/ASIC-README index e48503231d..f2900327d9 100644 --- a/ASIC-README +++ b/ASIC-README @@ -1,11 +1,34 @@ SUPPORTED DEVICES -Currently supported devices include the Avalon (including BitBurner), the -Butterfly Labs SC range of devices and the ASICMINER block erupters. No COM -ports on windows or TTY devices will be used by cgminer as it communicates -directly with them via USB so it is normal for them to not exist or be -disconnected when cgminer is running. - +Currently supported devices include: +- Antminer U1/U2/U2+/U3 USB +- Antminer S1 +- ASICMINER block erupters +- ASICMINER Tube/Prisma +- Avalon (including BitBurner and Klondike) +- Avalon2/3 +- Avalon4 +- BFx2 USB +- Butterfly Labs SC 65/28nm range +- BF1 (bitfury) USB (red and blue) +- BlackArrow Bitfury +- BlackArrow Minion +- Bi*fury USB +- Cointerra +- Hashfast Babyjet and Sierra +- Hashratio +- Hexfury USB +- KnCminer Mercury, Saturn and Jupiter +- Nanofury USB +- Other bitfury USB devices +- Onestring miner USB +- Rockminer R-Box/RK-Box/T1/New R-Box +- Spondoolies SP10, SP30 + + +No COM ports on windows or TTY devices will be used by cgminer as it +communicates directly with them via USB so it is normal for them to not exist or +be disconnected when cgminer is running. The BFL devices should come up as one of the following: @@ -13,6 +36,7 @@ BAJ: BFL ASIC JalapeƱo BAL: BFL ASIC Little Single BAS: BFL ASIC Single BAM: BFL ASIC Minirig +BMA: BFL Monarch BFL devices need the --enable-bflsc option when compiling cgminer yourself. @@ -20,14 +44,121 @@ Avalon will come up as AVA. Avalon devices need the --enable-avalon option when compiling cgminer. +Avalon2/3 will come up as AV2. + +Avalon2/3 devices need the --enable-avalon2 option when compiling cgminer. + +Avalon4 will come up as AV4. + +Avalon4 devies need the --enable-avalon4 option when compiling cgminer. + +Klondike will come up as KLN. + +Klondike devices need the --enable-klondike option when compiling cgminer. + ASICMINER block erupters will come up as AMU. ASICMINER devices need the --enable-icarus option when compiling cgminer. Also note that the AMU is managed by the Icarus driver which is detailed -in the FPGA-README +in the FPGA-README. Configuring them uses the same mechanism as outlined +below for getting started with USB ASICs. + +ASICMINER BlockErupter Tube/Prisma will come up as BET. + +ASICMINER Tube/Prisma devices need the --enable-blockerupter option when +compiling cgminer. + +BlackArrow Bitfury devices + +BlackArrow Bitfury devices need the --enable-bab option when compiling cgminer. + +The current BlackArrow Bitfury devices are similar to the Bitfury GPIO mining +boards, with both V1 and V2 controllers, and come up as BaB. + + +BlackArrow Minion devices + +BlackArrow Minion devices need the --enable-minion option when compiling +cgminer. + +BlackArrow Minion devices are SPI/GPIO mining devices and come up as MBA + + +BITFURY devices + +Bitfury devices need the --enable-bitfury option when compiling cgminer. + +Currently the BPMC/BGMC BF1 devices AKA redfury/bluefury are supported and +come up as BF1, along with the Bi*fury USB devices which come up as BXF. +Nanofury devices come up as NF1. BFx2 devices come up as BXM. + +Bitfury USB devices are also set up as per the USB ASICs below. + + +COINTERRA devices + +Cointerra devices need the --enable-cointerra option when compiling cgminer. + +Cointerra devices come up as CTA devices and currently take only hidden command +line arguments for power settings. + +Cointerra USB devices are set up as per the USB ASIC instructions below. + + +HASHFAST devices + +Hashfast devices need the --enable-hashfast option when compiling cgminer. + +All current HFA devices are supported and are recognised with the name HFA +in the --usb commands. After initialisation, cgminer will determine what type +they are and give them the following names: + +HFB: Hashfast Babyjet +HFS: Hashfast Sierra +HFA: Hashfast non standard (eg. a Babyjet with an added board, Habanero) + + +HASHRATIO devices + +Hashratio devices need the --enable-hashratio option when compiling cgminer. + + +ANTMINER U1/U2+/U3 devices + +Antminer devices need the --enable-icarus option when compiling cgminer. + +Currently the U1/2/3 USB sticks are supported and come up as the following +devices: +ANU: Antminer U1/U2/U2+ +AU3: Antminer U3 -GETTING STARTED WITH BUTTERFLY LABS ASICS +They are also set up as per the USB ASICs below. + +ANTMINER S1 devices + +Antminer S1 devices need the --enable-ants1 option when compiling cgminer. + +They are custom OpenWRT linux devices + +They are recognised with the name ANT + + +BITMINE A1 devices + +Bitmine A1 devices need the --enable-bitmine_A1 compile option set. + + +Rockminer R*Box + +Rockminer R*Box devices need the --enable-icarus compile option set. + +They appear with the following names: +LIN: R-Box +LIR: New R-Box + +--- +GETTING STARTED WITH USB ASICS Unlike other software, cgminer uses direct USB communication instead of the ancient serial USB communication to be much faster, more reliable and use a @@ -38,9 +169,13 @@ devices requires different drivers. WINDOWS: On windows, the direct USB support requires the installation of a WinUSB -driver (NOT the ftdi_sio driver), and attach it to the Butterfly labs device. -The easiest way to do this is to use the zadig utility which will install the -drivers for you and then once you plug in your device you can choose the +driver (NOT the ftdi_sio driver), and attach it to the chosen USB device. +When configuring your device, plug it in and wait for windows to attempt to +install a driver on its own. It may think it has succeeded or failed but wait +for it to finish regardless. This is NOT the driver you want installed. At this +point you need to associate your device with the WinUSB driver. The easiest +way to do this is to use the zadig utility which you must right click on and +run as administrator. Then once you plug in your device you can choose the "list all devices" from the "option" menu and you should be able to see the device as something like: "BitFORCE SHA256 SC". Choose the install or replace driver option and select WinUSB. You can either google for zadig or download @@ -77,19 +212,144 @@ After this you can either manually restart udev and re-login, or more easily just reboot. +OSX: + +On OSX, like Linux, no drivers need to be installed. However some devices +like the bitfury USB sticks automatically load a driver thinking they're a +modem and the driver needs to be unloaded for cgminer to work: + + sudo kextunload -b com.apple.driver.AppleUSBCDC + sudo kextunload -b com.apple.driver.AppleUSBCDCACMData + +There may be a limit to the number of USB devices that you are allowed to start. +The following set of commands, followed by a reboot will increase that: + + sudo su + touch /etc/sysctl.conf + echo kern.sysv.semume=100 >> /etc/sysctl.conf + chown root:wheel /etc/sysctl.conf + chmod 0644 /etc/sysctl.conf + +Some devices need superuser access to mine on them so cgminer may need to +be started with sudo +i.e.: + sudo cgminer + + +--- + ASIC SPECIFIC COMMANDS +--anu-freq Set AntminerU1/2 frequency in MHz, range 125-500 (default: 250.0) +--au3-freq Set AntminerU3 frequency in MHz, range 100-250 (default: 225.0) +--au3-volt Set AntminerU3 voltage in mv, range 725-850, 0 to not set (default: 750) --avalon-auto Adjust avalon overclock frequency dynamically for best hashrate --avalon-cutoff Set avalon overheat cut off temperature (default: 60) --avalon-fan Set fanspeed percentage for avalon, single value or range (default: 20-100) --avalon-freq Set frequency range for avalon-auto, single value or range ---avalon-options Set avalon options baud:miners:asic:timeout:freq +--avalon-options Set avalon options baud:miners:asic:timeout:freq:tech --avalon-temp Set avalon target temperature (default: 50) +--avalon2-freq Set frequency range for Avalon2, single value or range +--avalon2-voltage Set Avalon2 core voltage, in millivolts +--avalon2-fan Set Avalon2 target fan speed +--avalon2-cutoff Set Avalon2 overheat cut off temperature (default: 88) +--avalon2-fixed-speed Set Avalon2 fan to fixed speed +--avalon4-automatic-voltage Automatic adjust voltage base on module DH +--avalon4-voltage Set Avalon4 core voltage, in millivolts, step: 125 +--avalon4-freq Set frequency for Avalon4, 1 to 3 values, example: 445:385:370 +--avalon4-fan Set Avalon4 target fan speed range +--avalon4-temp Set Avalon4 target temperature (default: 42) +--avalon4-cutoff Set Avalon4 overheat cut off temperature (default: 65) +--avalon4-polling-delay Set Avalon4 polling delay value (ms) (default: 20) +--avalon4-ntime-offset Set Avalon4 MM ntime rolling max offset (default: 4) +--avalon4-aucspeed Set Avalon4 AUC IIC bus speed (default: 400000) +--avalon4-aucxdelay Set Avalon4 AUC IIC xfer read delay, 4800 ~= 1ms (default: 9600) +--bab-options Set BaB options max:def:min:up:down:hz:delay:trf +--bet-clk Set clockspeed of ASICMINER Tube/Prisma to (arg+1)*10MHz (default: 23) --bflsc-overheat Set overheat temperature where BFLSC devices throttle, 0 to disable (default: 90) ---bitburner-voltage Set BitBurner core voltage, in millivolts - - -AVALON DEVICES +--bitburner-fury-options Override avalon-options for BitBurner Fury boards baud:miners:asic:timeout:freq +--bitburner-fury-voltage Set BitBurner Fury core voltage, in millivolts +--bitburner-voltage Set BitBurner (Avalon) core voltage, in millivolts +--bitmain-auto Adjust bitmain overclock frequency dynamically for best hashrate +--bitmain-cutoff Set bitmain overheat cut off temperature +--bitmain-fan Set fanspeed percentage for bitmain, single value or range (default: 20-100) +--bitmain-freq Set frequency range for bitmain-auto, single value or range +--bitmain-hwerror Set bitmain device detect hardware error +--bitmain-options Set bitmain options baud:miners:asic:timeout:freq +--bitmain-temp Set bitmain target temperature +--bxf-bits Set max BXF/HXF bits for overclocking (default: 54) +--bxf-temp-target Set target temperature for BXF/HXF devices (default: 82) +--bxm-bits Set BXM bits for overclocking (default: 54) +--hfa-hash-clock Set hashfast clock speed (default: 550) +--hfa-fail-drop Set how many MHz to drop clockspeed each failure on an overlocked hashfast device (default: 10) +--hfa-fan Set fanspeed percentage for hashfast, single value or range (default: 10-85) +--hfa-name Set a unique name for a single hashfast device specified with --usb or the first device found +--hfa-noshed Disable hashfast dynamic core disabling feature +--hfa-options Set hashfast options name:clock or name:clock@voltage (comma separated) +--hfa-temp-overheat Set the hashfast overheat throttling temperature (default: 95) +--hfa-temp-target Set the hashfast target temperature (0 to disable) (default: 88) +--hro-freq Set the hashratio clock frequency (default: 280) +--klondike-options Set klondike options clock:temptarget +--minion-chipreport Seconds to report chip 5min hashrate, range 0-100 (default: 0=disabled) +--minion-freq Set minion chip frequencies in MHz, single value or comma list, range 100-1400 (default: 1200) +--minion-freqchange Millisecond total time to do frequency changes (default: 1000) +--minion-freqpercent Percentage to use when starting up a chip (default: 70%) +--minion-idlecount Report when IdleCount is >0 or changes +--minion-ledcount Turn off led when more than this many chips below the ledlimit (default: 0) +--minion-ledlimit Turn off led when chips GHs are below this (default: 90) +--minion-idlecount Report when IdleCount is >0 or changes +--minion-noautofreq Disable automatic frequency adjustment +--minion-overheat Enable directly halting any chip when the status exceeds 100C +--minion-spidelay Add a delay in microseconds after each SPI I/O +--minion-spireset SPI regular reset: iNNN for I/O count or sNNN for seconds - 0 means none +--minion-spisleep Sleep time in milliseconds when doing an SPI reset +--minion-temp Set minion chip temperature threshold, single value or comma list, range 120-160 (default: 135C) +--nfu-bits Set nanofury bits for overclocking, range 32-63 (default: 50) +--rock-freq Set RockMiner frequency in MHz, range 125-500 (default: 270) + + +ANTMINER S1 DEVICES + +--bitmain-auto Adjust bitmain overclock frequency dynamically for best hashrate +--bitmain-cutoff Set bitmain overheat cut off temperature +--bitmain-fan Set fanspeed percentage for bitmain, single value or range (default: 20-100) +--bitmain-freq Set frequency range for bitmain-auto, single value or range +--bitmain-hwerror Set bitmain device detect hardware error +--bitmain-options Set bitmain options baud:miners:asic:timeout:freq +--bitmain-temp Set bitmain target temperature + +The Antminer S1 device comes with its own operating system and a preinstalled +version of cgminer as part of the flash firmware. No configuration should be +necessary. + + +ANTMINER U1/2/3 DEVICES + +--anu-freq Set AntminerU1 frequency in MHz, range 150-500 (default: 200) +--au3-freq Set AntminerU3 frequency in MHz, range 100-250 (default: 225.0) +--au3-volt Set AntminerU3 voltage in mv, range 725-850, 0 to not set (default: 750) + +By default, Antminer U1 devices run at a clockspeed of 200. This command allows +you to specify a chosen frequency to attempt to run all ANU devices at. Cgminer +will try to find the nearest frequency the device supports and will report if +the frequency is not exactly as requested. Note that cgminer reports hashrate +ONLY FROM VALID HASHES so if you increase the frequency but your hashrate does +not increase or it decreases and hardware errors start showing up, you have +overclocked it too much. In the worst case scenario it will fail to start at too +high a speed. Most will run happily up to 250. + +ASICMINER BlockErupter Tube/Prisma DEVICES + +--bet-clk Set clockspeed of ASICMINER Tube/Prisma to (arg+1)*10MHz (default: 23) + +Default clockspeed for Tube/Prisma is 240MHz. This command allows to set clockspeed +of on board BE200 chips in range from 200MHz to 320MHz. For Tube devices, you can +try overclocking to 270MHz or even higher, but NOT recommended for Prisma devices. +If you notice hash rate drops or board fails to start, restart cgminer with lower +clockspeed. + + +AVALON AND BITBURNER DEVICES Currently all known Avalon devices come with their own operating system and a preinstalled version of cgminer as part of the flash firmware, based on the @@ -101,15 +361,20 @@ command, and adjust its fan control-temperature relationship with avalon-temp. By default the avalon will also cut off when its temperature reaches 60 degrees. +All current BitBurner devices (BitBurner X, BitBurner XX and BitBurner Fury) +emulate Avalon devices, whether or not they use Avalon chips. + Avalon commands: --avalon-auto Adjust avalon overclock frequency dynamically for best hashrate --avalon-cutoff Set avalon overheat cut off temperature (default: 60) --avalon-fan Set fanspeed percentage for avalon, single value or range (default: 20-100) --avalon-freq Set frequency range for avalon-auto, single value or range ---avalon-options Set avalon options baud:miners:asic:timeout:freq +--avalon-options Set avalon options baud:miners:asic:timeout:freq:tech --avalon-temp Set avalon target temperature (default: 50) ---bitburner-voltage Set BitBurner core voltage, in millivolts +--bitburner-fury-options Override avalon-options for BitBurner Fury boards baud:miners:asic:timeout:freq +--bitburner-fury-voltage Set BitBurner Fury core voltage, in millivolts +--bitburner-voltage Set BitBurner (Avalon) core voltage, in millivolts Avalon auto will enable dynamic overclocking gradually increasing and @@ -148,9 +413,9 @@ This will cut off the avalon should it get up to 65 degrees and will then re-enable it when it gets to the target temperature as specified by avalon-temp. eg: ---avalon-options 115200:24:10:45:282 +--avalon-options 115200:24:10:D:1500:55 -The values are baud : miners : asic count : timeout : frequency. +The values are baud : miners : asic count : timeout : frequency : technology. Baud: The device is pretty much hard coded to emulate 115200 baud so you shouldn't @@ -160,8 +425,15 @@ Miners: Most Avalons are 3 module devices, which come to 24 miners. 4 module devices would use 32 here. +For BitBurner X and BitBurner XX devices you should use twice the number of +boards in the stack. e.g. for a two-board stack you would use 4. For +BitBurner Fury devices you should use the total number of BitFury chips in the +stack (i.e. 16 times the number of boards). e.g. for a two-board stack you +would use 32. + Asic count: -Virtually all have 10, so don't change this. +Virtually all have 10, so don't change this. BitBurner devices use 10 here +even if the boards have some other number of ASICs. Timeout: This is how long the device will work on a work item before accepting new work @@ -170,27 +442,78 @@ It is possible to set this a little lower if you are trying to tune for short block mining (eg p2pool) but much lower and the device will start creating duplicate shares. A value of 'd' means cgminer will calculate it for you based on the frequency +and is highly recommended. -Sample settings for valid different frequencies (last 2 values): -34:375 * -36:350 * -39:325 * -43:300 -45:282 (default) -47:270 -50:256 - -Frequency: -This is the clock speed of the devices. Only specific values work, 256, 270, -282 (default), 300, 325, 350 and 375. +Sample settings for valid different frequencies (last 3 values) for 110nm AVAs: +34:375:110 * +36:350:110 * +43:300:110 +45:282:110 (default) +50:256:110 Note that setting a value with an asterisk next to it will be using your avalon outside its spec and you do so at your own risk. +For 55nm AVAs, the usual values are 8:1500 + +Frequency: +This is the clock speed of the devices. For Avalon 110nm devices, values from +256 upwards are valid with the default being 282 and the maximum practical +being approximately 350. For 55nm devices values from 1000-2000 are valid with +1500 being the default. + +Technology: +What sized technology ASICs are in use in the avalon, choices are 55 or 110, +corresponding to the nm technology chips in use. + +The default frequency for BitBurner X and BitBurner XX boards is 282. The +default frequency for BitBurner Fury boards is 256. Overclocking is +possible - please consult the product documentation and/or manufacturer for +information on safe values. Values outside this range are used at your own +risk. Underclocking is also possible, at least with the X and XX boards. + eg: ---bitburner-voltage Set BitBurner core voltage, in millivolts +--bitburner-fury-options Override avalon-options for BitBurner Fury boards baud:miners:asic:timeout:freq + +This option takes the same format as --avalon-options. When specified, it +will be used for BitBurner Fury boards in preference to the values specified +in --avalon-options. (If not specified, BitBurner Fury boards will be +controlled by the values used in --avalon options.) See --avalon-options for +a detailed description of the fields. + +This option is particularly useful when using a mixture of different BitBurner +devices as BitBurner Fury devices generally require significantly different +clock frequencies from Avalon-based devices. This option is only available +for boards with recent firmware that are recognized by cgminer as BBF. + +eg: +--bitburner-fury-voltage Set BitBurner Fury core voltage, in millivolts + +Sets the core voltage for the BitBurner Fury boards. The default value is +900. Overvolting is possible - please consult the product documentation +and/or manufaturer about the safe range of values. Values outside this range +are used at your own risk. + +This option is only available for boards with recent firmware that are +recognized by cgminer as BBF. For boards recognized as BTB, see +--bitburner-voltage + +eg: +--bitburner-voltage Set BitBurner (Avalon) core voltage, in millivolts + +Sets the core voltage for the Avalon-based BitBurner X and BitBurner XX +boards. The default value is 1200. Overvolting and undervolting is +possible - please consult the product documentation and/or the manufacturer +for information about the safe range. Values outside this range are used at +your own risk. + +Older BitBurner Fury firmware emulates a BitBurner XX board and is identified +by cgminer as BTB. On these devices, --bitburner-voltage is used to control +the voltage of the BitBurner Fury board. The actual core voltage will be +300mV less than the requested voltage, so to run a BitBurner Fury board at +950mV use --bitburner-voltage 1250. The default value of 1200 therefore +corresponds to the default core voltage of 900mV. -Self evident. If you use the full curses based interface with Avalons you will get this information: @@ -202,6 +525,29 @@ ambient temp / highest device temp lowest detected ASIC cooling fan RPM. Use the API for more detailed information than this. +Avalon2 Devices + +--avalon2-freq Set frequency range for Avalon2, single value or range +--avalon2-voltage Set Avalon2 core voltage, in millivolts +--avalon2-fan Set Avalon2 target fan speed +--avalon2-cutoff Set Avalon2 overheat cut off temperature (default: 88) +--avalon2-fixed-speed Set Avalon2 fan to fixed speed + + +Avalon4 Devices + +--avalon4-automatic-voltage Automatic adjust voltage base on module DH +--avalon4-voltage Set Avalon4 core voltage, in millivolts, step: 125 +--avalon4-freq Set frequency for Avalon4, 1 to 3 values, example: 445:385:370 +--avalon4-fan Set Avalon4 target fan speed range +--avalon4-temp Set Avalon4 target temperature (default: 42) +--avalon4-cutoff Set Avalon4 overheat cut off temperature (default: 65) +--avalon4-polling-delay Set Avalon4 polling delay value (ms) (default: 20) +--avalon4-ntime-offset Set Avalon4 MM ntime rolling max offset (default: 4) +--avalon4-aucspeed Set Avalon4 AUC IIC bus speed (default: 400000) +--avalon4-aucxdelay Set Avalon4 AUC IIC xfer read delay, 4800 ~= 1ms (default: 9600) + + BFLSC Devices --bflsc-overheat Set overheat temperature where BFLSC devices throttle, 0 to disable (default: 90) @@ -210,6 +556,203 @@ This will allow you to change or disable the default temperature where cgminer throttles BFLSC devices by allowing them to temporarily go idle. +BITFURY Devices + +--bxf-bits Set max BXF/HXF bits for overclocking (default: 54) + +In combination with the dynamic clocking on Bi*fury devices, this sets the +highest bit target that cgminer will aim for. + + +--bxf-temp-target Set target temperature for BXF/HXF devices (default: 82) + +Cgminer uses dynamic clocking on Bi*fury devices to try and maintain the +temperature just below an optimal target. This option allows you to change the +target temperature. When actively cooled below this, the devices will run at +maximum speed. + +--bxm-bits Set BXM bits for overclocking (default: 54) + +Choose the overclocking bits for BFx2 devices. + + +--nfu-bits Set nanofury bits for overclocking range 32-63 (default: 50) + +Cgminer by default sets the clockspeed on nanofury devices to the highest that +is still within USB2 spec. This value allows you to alter the clockspeed, with +~54 being the optimal but requiring a higher power or USB3 port. + + +Cointerra Devices + +--cta-load (0 - 255) +--ps-load (0 - 100) + +These are undocumented. + + +Drillbit Systems Devices + +--drillbit-options Set drillbit options :clock[:clock_divider][:voltage] + +* int/ext defines the clock source - default int. Not all boards support ext. +* clock_divider must be 1 or 2 with a default of 1. Bitfury only, + ignored on Avalon. +* clock is in MHz, on Drillbit range 80-250 with a default of 200, + recommended maximum 230. On Avalon range 500-1000 with a + recommended maximum of 800. +* voltage is ASIC core voltage in millivolts, available values vary per board but + default is 850 and the recommended maximum is 950 (Bitfury) and 1000 (Avalon.) + +--drillbit-auto :[::] + +If supported by firmware and device, this feature allows cgminer to +automatically tweak each ASIC's clock rate up and down in to achieve +optimal performance. + +* every - only required param, check each ASIC after each block of + this many work units. Recommended value 100. +* gooderr - the "Good" threshold is when less hardware errors than + this per "every" work units, the clock rate will be increased. + Default value 1. +* baderr - the "Bad" threshold is when more hardware errors than + this per "every" work units, the clock rate will be decreased. + Default value 3. +* maxerr - the "Max" threshold is when more hardware errors than + this per "every" work units (including pre-empting before + "every" work units is up), the clock rate will be decreased and + will not be increased again past this point. Default value 10. + + +BlackArrow Bitfury devices + +--bab-options Set BaB options Max:Def:Min:Up:Down:Hz:Delay:Trf + +Any option left blank or starting with 'd' will use the default setting +If there are not enough options, then the remaining will be left at their +default value + +Max:Def:Min are the chip speed limits to allow, ranging from 52 to 57 + +Up:Down are the HW error % used to tune the chip speed +Up means if the HW error % is less than up, over a 5 minute period, +then increase the chip speed +Down means if the HW error % is greater than down, over 5 minutes, +then decrease the chip speed + +Hz is the SPI clock speed to use + +Delay is the us delay used between bytes for the SPI I/O - default 0 + +Trf is the us delay used between sends for the SPI I/O - default 0 + + +Hashfast devices + +--hfa-hash-clock Set hashfast clock speed (default: 550) + +This will change the initialisation clock speed on all attached hfa devices. +Note that if instability is detected by cgminer and the device has to undergo +a reset, cgminer will lower the clockspeed on resetting it each time till the +value returns to the default of 550. + +--hfa-fail-drop Set how many MHz to drop clockspeed each failure on an overlocked hashfast device (default: 10) + +If you overclock your hashfast device with --hfa-hash-clock and cgminer detects +it failing to return hashes, it will restart it at a lower clock speed if +possible. Changing this value will allow you to choose how much it will lower +the clock speed or to disable this function entirely. + +--hfa-fan Set fanspeed percentage for hashfast, single value or range (default: 10-85) + +This changes the range of fanspeeds used on hashfast devices with firmware that +supports it. Note that the fanspeed will dynamically change to try and maintain +a target temperature with --hfa-temp-target but if the target temperature is +disabled, the fanspeed will remain static. +eg: +--hfa-fan 25-100 + +--hfa-temp-overheat Set the hashfast overheat throttling temperature (default: 95) + +Cgminer will temporarily stop sending hashfast devices work once this +temperature is reached. Note that with the water cooling in these devices, +temperature recovery is likely to be very quick and the device will start +hashing again after only a very brief period. + +--hfa-temp-target Set the hashfast target temperature (0 to disable) (default: 88) + +On hashfast devices with firmware that supports dynamic fanspeed and die speeds, +cgminer will try to maintain temperature according to this target by adjusting +fanspeed and then if need be, throttle speeds on a die-by-die basis. Disabling +this feature will leave a constant fanspeed and die speed but will not disable +the temp-overheat feature. + +--hfa-name Set a unique name for a single hashfast device specified with --usb or the first device found + +This command allows you to specify the unique name stored in nvram on a single +hashfast device. This name can be queried from the API stats command and comes +up as "op name". Discrete names are used by cgminer to try to maintain settings +across restarts, unplugs/hotplugs and so on. If this command is used by itself, +the name will be given to the first hashfast device it encounters and then +cgminer will proceed to go back to regular mining. If you have multiple devices, +it is best to discretely choose the device you wish to use with the --usb +command. For example +'lsusb' on linux shows the following devices (297c:0001 is a hfa device): + Bus 001 Device 079: ID 297c:0001 + Bus 004 Device 042: ID 297c:0001 +If you wished to name the second device Slug you would add the commands: + --hfa-name Slug --usb 4:42 + +--hfa-noshed Disable hashfast dynamic core disabling feature + +Newer firmwares on hashfast devices dynamically disable cores that generate +invalid data. This command will disable this feature where possible. + +--hfa-options Set hashfast options name:clock or clock@voltage (comma separated) + +This command allows you to set options for each discrete hashfast device by +its name (if the firmware has naming support, i.e. version 0.3+). Currently +this takes as option the clock speed alone or clock speed and voltage, +although future options may be added. +e.g.: +--hfa-options "rabbit:650,turtle:550@800" + +Would set a device named rabbit to clock speed 650 MHz using default voltage +and the one named turtle to 550 MHz using a voltage of 800 mv. Starting the +device at a speed where it is most stable will give more reliable hashrates +long term and prevent it interacting with other devices, rather than depending +on the clockdown feature in cgminer. + +Note: Setting voltage cause a board reset and hotplug event on cgminer startup. + +Other undocumented hashfast command line options are for development purposes +only at this stage and serve no useful purpose to end users. + + +Hashratio Devices + +--hro-freq Set the hashratio clock frequency (default: 280) + + +Bitmine A1 Devices + +--bitmine-a1-options ::: +ref_clk: reference clock in kHz (default: 16000) +sys_clk: target system clock in kHz to be set in PLL (default: 250000) +spi_clk: SPI clock in kHz (default: 800) +max_chip: [debug/testing] limit chip chain + +Set 0 for fields you want to keep untouched to default, e.g. +--bitmine-a1-options 0:0:400 +to only set SPI clock to 400kHz + + +Rockminer R-Box Devices + +--rock-freq Set RockMiner frequency in MHz, range 125-500 (default: 270) + +Note that only a limited range is likely to be accepted (usually 200-290) + --- This code is provided entirely free of charge by the programmer in his spare diff --git a/AUTHORS b/AUTHORS index 58244ffd21..3ec617429d 100644 --- a/AUTHORS +++ b/AUTHORS @@ -1,5 +1,5 @@ Current maintainers and active developers: -Main code+ASIC+GPU+SCRYPT+maintainer: Con Kolivas 15qSxP1SQcUX3o4nhkfdbgyoWEFMomJ4rZ +Main code+USB+ASIC+maintainer: Con Kolivas 15qSxP1SQcUX3o4nhkfdbgyoWEFMomJ4rZ API+USB+FPGA+ASIC: Andrew Smith 1Jjk2LmktEQKnv8r2cZ9MvLiZwZ9gxabKm Legacy: diff --git a/GPU-README b/GPU-README deleted file mode 100644 index 1541b88b08..0000000000 --- a/GPU-README +++ /dev/null @@ -1,514 +0,0 @@ -EXECUTIVE SUMMARY ON GPU USAGE (SEE ALSO SCRYPT-README FOR SCRYPT MINING): - -Single pool, regular desktop: - -cgminer -o http://pool:port -u username -p password - -By default if you have configured your system properly, cgminer will mine on -ALL GPUs, but in "dynamic" mode which is designed to keep your system usable -and sacrifice some mining performance. - -Single pool, dedicated miner: - -cgminer -o http://pool:port -u username -p password -I 9 - -Single pool, first card regular desktop, 3 other dedicated cards: - -cgminer -o http://pool:port -u username -p password -I d,9,9,9 - -Multiple pool, dedicated miner: - -cgminer -o http://pool1:port -u pool1username -p pool1password -o http://pool2:port -u pool2usernmae -p pool2password -I 9 - -Add overclocking settings, GPU and fan control for all cards: - -cgminer -o http://pool:port -u username -p password -I 9 --auto-fan --auto-gpu --gpu-engine 750-950 --gpu-memclock 300 - -Add overclocking settings, GPU and fan control with different engine settings for 4 cards: - -cgminer -o http://pool:port -u username -p password -I 9 --auto-fan --auto-gpu --gpu-engine 750-950,945,700-930,960 --gpu-memclock 300 - -READ WARNINGS AND DOCUMENTATION BELOW ABOUT OVERCLOCKING - -To configure multiple displays on linux you need to configure your Xorg cleanly -to use them all: - -sudo aticonfig --adapter=all -f --initial - -On Linux you virtually always need to export your display settings before -starting to get all the cards recognised and/or temperature+clocking working: - -export DISPLAY=:0 - ---- -BUILDING FOR GPU SUPPORT: - - To build with GPU mining support: - Install AMD APP sdk, ideal version (see FAQ!) - no official place to - install it so just keep track of where it is if you're not installing - the include files and library files into the system directory. - (Do NOT install the ati amd sdk if you are on nvidia.) - To build with GPU monitoring & clocking support: - Extract the AMD ADL SDK, latest version - there is also no official - place for these files. Copy all the *.h files in the "include" - directory into cgminer's ADL_SDK directory. - -The easiest way to install the ATI AMD SPP sdk on linux is to actually put it -into a system location. Then building will be simpler. Download the correct -version for either 32 bit or 64 bit from here: - http://developer.amd.com/tools/heterogeneous-computing/amd-accelerated-parallel-processing-app-sdk/downloads/ - -The best version for Radeon 5xxx and 6xxx is v2.5, while 7xxx cards need -v2.6 or later, 2.7 seems the best. - -For versions 2.4 or earlier you will need to manually install them: -This will give you a file with a name like: - AMD-APP-SDK-v2.4-lnx64.tgz (64-bit) -or - AMD-APP-SDK-v2.4-lnx32.tgz (32-bit) - -Then: - -sudo su -cd /opt -tar xf /path/to/AMD-APP-SDK-v2.4-lnx##.tgz -cd / -tar xf /opt/AMD-APP-SDK-v2.4-lnx##/icd-registration.tgz -ln -s /opt/AMD-APP-SDK-v2.4-lnx##/include/CL /usr/include -ln -s /opt/AMD-APP-SDK-v2.4-lnx##/lib/x86_64/* /usr/lib/ -ldconfig - -Where ## is 32 or 64, depending on the bitness of the SDK you downloaded. -If you are on 32 bit, x86_64 in the 2nd last line should be x86 - -Basic *nix build instructions: - CFLAGS="-O2 -Wall -march=native" ./configure - or if you haven't installed the AMD files in system locations: - CFLAGS="-O2 -Wall -march=native -I" LDFLAGS="-L ./configure - make - - If it finds the opencl files it will inform you with - "OpenCL: FOUND. GPU mining support enabled." - - ---- -INTENSITY INFORMATION: - -Intensity correlates with the size of work being submitted at any one time to -a GPU. The higher the number the larger the size of work. Generally speaking -finding an optimal value rather than the highest value is the correct approach -as hash rate rises up to a point with higher intensities but above that, the -device may be very slow to return responses, or produce errors. - -NOTE: Running BTC intensities above 9 with current hardware is likely to only -diminish return performance even if the hash rate might appear better. A good -starting baseline intensity to try on dedicated miners is 9. 11 is the upper -limit for intensity while BTC mining, if the GPU_USE_SYNC_OBJECTS variable -is set (see FAQ). The upper limit for sha256 mining is 14 and 20 for scrypt. - - ---- -OVERCLOCKING WARNING AND INFORMATION - -AS WITH ALL OVERCLOCKING TOOLS YOU ARE ENTIRELY RESPONSIBLE FOR ANY HARM YOU -MAY CAUSE TO YOUR HARDWARE. OVERCLOCKING CAN INVALIDATE WARRANTIES, DAMAGE -HARDWARE AND EVEN CAUSE FIRES. THE AUTHOR ASSUMES NO RESPONSIBILITY FOR ANY -DAMAGE YOU MAY CAUSE OR UNPLANNED CHILDREN THAT MAY OCCUR AS A RESULT. - -The GPU monitoring, clocking and fanspeed control incorporated into cgminer -comes through use of the ATI Display Library. As such, it only supports ATI -GPUs. Even if ADL support is successfully built into cgminer, unless the card -and driver supports it, no GPU monitoring/settings will be available. - -Cgminer supports initial setting of GPU engine clock speed, memory clock -speed, voltage, fanspeed, and the undocumented powertune feature of 69x0+ GPUs. -The setting passed to cgminer is used by all GPUs unless separate values are -specified. All settings can all be changed within the menu on the fly on a -per-GPU basis. - -For example: ---gpu-engine 950 --gpu-memclock 825 - -will try to set all GPU engine clocks to 950 and all memory clocks to 825, -while: ---gpu-engine 950,945,930,960 --gpu-memclock 300 - -will try to set the engine clock of card 0 to 950, 1 to 945, 2 to 930, 3 to -960 and all memory clocks to 300. - -AUTO MODES: -There are two "auto" modes in cgminer, --auto-fan and --auto-gpu. These can -be used independently of each other and are complementary. Both auto modes -are designed to safely change settings while trying to maintain a target -temperature. By default this is set to 75 degrees C but can be changed with: - ---temp-target -e.g. ---temp-target 80 -Sets all cards' target temperature to 80 degrees. - ---temp-target 75,85 -Sets card 0 target temperature to 75, and card 1 to 85 degrees. - -AUTO FAN: -e.g. ---auto-fan (implies 85% upper limit) ---gpu-fan 25-85,65 --auto-fan - -Fan control in auto fan works off the theory that the minimum possible fan -required to maintain an optimal temperature will use less power, make less -noise, and prolong the life of the fan. In auto-fan mode, the fan speed is -limited to 85% if the temperature is below "overheat" intentionally, as -higher fanspeeds on GPUs do not produce signficantly more cooling, yet -significanly shorten the lifespan of the fans. If temperature reaches the -overheat value, fanspeed will still be increased to 100%. The overheat value -is set to 85 degrees by default and can be changed with: - ---temp-overheat -e.g. ---temp-overheat 75,85 -Sets card 0 overheat threshold to 75 degrees and card 1 to 85. - -AUTO GPU: -e.g. ---auto-gpu --gpu-engine 750-950 ---auto-gpu --gpu-engine 750-950,945,700-930,960 - -GPU control in auto gpu tries to maintain as high a clock speed as possible -while not reaching overheat temperatures. As a lower clock speed limit, -the auto-gpu mode checks the GPU card's "normal" clock speed and will not go -below this unless you have manually set a lower speed in the range. Also, -unless a higher clock speed was specified at startup, it will not raise the -clockspeed. If the temperature climbs, fanspeed is adjusted and optimised -before GPU engine clockspeed is adjusted. If fan speed control is not available -or already optimal, then GPU clock speed is only decreased if it goes over -the target temperature by the hysteresis amount, which is set to 3 by default -and can be changed with: ---temp-hysteresis -If the temperature drops below the target temperature, and engine clock speed -is not at the highest level set at startup, cgminer will raise the clock speed. -If at any time you manually set an even higher clock speed successfully in -cgminer, it will record this value and use it as its new upper limit (and the -same for low clock speeds and lower limits). If the temperature goes over the -cutoff limit (95 degrees by default), cgminer will completely disable the GPU -from mining and it will not be re-enabled unless manually done so. The cutoff -temperature can be changed with: - ---temp-cutoff -e.g. ---temp-cutoff 95,105 -Sets card 0 cutoff temperature to 95 and card 1 to 105. - ---gpu-memdiff -125 -This setting will modify the memory speed whenever the GPU clock speed is -modified by --auto-gpu. In this example, it will set the memory speed to -be 125 Mhz lower than the GPU speed. This is useful for some cards like the -6970 which normally don't allow a bigger clock speed difference. The 6970 is -known to only allow -125, while the 7970 only allows -150. - - -CHANGING SETTINGS: -When setting values, it is important to realise that even though the driver -may report the value was changed successfully, and the new card power profile -information contains the values you set it to, that the card itself may -refuse to use those settings. As the performance profile changes dynamically, -querying the "current" value on the card can be wrong as well. So when changing -values in cgminer, after a pause of 1 second, it will report to you the current -values where you should check that your change has taken. An example is that -6970 reference cards will accept low memory values but refuse to actually run -those lower memory values unless they're within 125 of the engine clock speed. -In that scenario, they usually set their real speed back to their default. - -Cgminer reports the so-called "safe" range of whatever it is you are modifying -when you ask to modify it on the fly. However, you can change settings to values -outside this range. Despite this, the card can easily refuse to accept your -changes, or worse, to accept your changes and then silently ignore them. So -there is absolutely to know how far to/from where/to it can set things safely or -otherwise, and there is nothing stopping you from at least trying to set them -outside this range. Being very conscious of these possible failures is why -cgminer will report back the current values for you to examine how exactly the -card has responded. Even within the reported range of accepted values by the -card, it is very easy to crash just about any card, so it cannot use those -values to determine what range to set. You have to provide something meaningful -manually for cgminer to work with through experimentation. - -STARTUP / SHUTDOWN: -When cgminer starts up, it tries to read off the current profile information -for clock and fan speeds and stores these values. When quitting cgminer, it -will then try to restore the original values. Changing settings outside of -cgminer while it's running may be reset to the startup cgminer values when -cgminer shuts down because of this. - ---- - -GPU DEVICE ISSUES and use of --gpu-map - -GPUs mine with OpenCL software via the GPU device driver. This means you need -to have both an OpenCL SDK installed, and the GPU device driver RUNNING (i.e. -Xorg up and running configured for all devices that will mine on linux etc.) -Meanwhile, the hardware monitoring that cgminer offers for AMD devices relies -on the ATI Display Library (ADL) software to work. OpenCL DOES NOT TALK TO THE -ADL. There is no 100% reliable way to know that OpenCL devices are identical -to the ADL devices, as neither give off the same information. cgminer does its -best to correlate these devices based on the order that OpenCL and ADL numbers -them. It is possible that this will fail for the following reasons: - -1. The device order is listed differently by OpenCL and ADL (rare), even if the -number of devices is the same. -2. There are more OpenCL devices than ADL. OpenCL stupidly sees one GPU as two -devices if you have two monitors connected to the one GPU. -3. There are more ADL devices than OpenCL. ADL devices include any ATI GPUs, -including ones that can't mine, like some older R4xxx cards. - -To cope with this, the ADVANCED option for --gpu-map is provided with cgminer. -DO NOT USE THIS UNLESS YOU KNOW WHAT YOU ARE DOING. The default will work the -vast majority of the time unless you know you have a problem already. - -To get useful information, start cgminer with just the -n option. You will get -output that looks like this: - -[2012-04-25 13:17:34] CL Platform 0 vendor: Advanced Micro Devices, Inc. -[2012-04-25 13:17:34] CL Platform 0 name: AMD Accelerated Parallel Processing -[2012-04-25 13:17:34] CL Platform 0 version: OpenCL 1.1 AMD-APP (844.4) -[2012-04-25 13:17:34] Platform 0 devices: 3 -[2012-04-25 13:17:34] 0 Tahiti -[2012-04-25 13:17:34] 1 Tahiti -[2012-04-25 13:17:34] 2 Cayman -[2012-04-25 13:17:34] GPU 0 AMD Radeon HD 7900 Series hardware monitoring enabled -[2012-04-25 13:17:34] GPU 1 AMD Radeon HD 7900 Series hardware monitoring enabled -[2012-04-25 13:17:34] GPU 2 AMD Radeon HD 6900 Series hardware monitoring enabled -[2012-04-25 13:17:34] 3 GPU devices max detected - -Note the number of devices here match, and the order is the same. If devices 1 -and 2 were different between Tahiti and Cayman, you could run cgminer with: ---gpu-map 2:1,1:2 -And it would swap the monitoring it received from ADL device 1 and put it to -opencl device 2 and vice versa. - -If you have 2 monitors connected to the first device it would look like this: - -[2012-04-25 13:17:34] Platform 0 devices: 4 -[2012-04-25 13:17:34] 0 Tahiti -[2012-04-25 13:17:34] 1 Tahiti -[2012-04-25 13:17:34] 2 Tahiti -[2012-04-25 13:17:34] 3 Cayman -[2012-04-25 13:17:34] GPU 0 AMD Radeon HD 7900 Series hardware monitoring enabled -[2012-04-25 13:17:34] GPU 1 AMD Radeon HD 7900 Series hardware monitoring enabled -[2012-04-25 13:17:34] GPU 2 AMD Radeon HD 6900 Series hardware monitoring enabled - -To work around this, you would use: --d 0 -d 2 -d 3 --gpu-map 2:1,3:2 - -If you have an older card as well as the rest it would look like this: - -[2012-04-25 13:17:34] Platform 0 devices: 3 -[2012-04-25 13:17:34] 0 Tahiti -[2012-04-25 13:17:34] 1 Tahiti -[2012-04-25 13:17:34] 2 Cayman -[2012-04-25 13:17:34] GPU 0 AMD Radeon HD 4500 Series hardware monitoring enabled -[2012-04-25 13:17:34] GPU 1 AMD Radeon HD 7900 Series hardware monitoring enabled -[2012-04-25 13:17:34] GPU 2 AMD Radeon HD 7900 Series hardware monitoring enabled -[2012-04-25 13:17:34] GPU 3 AMD Radeon HD 6900 Series hardware monitoring enabled - -To work around this you would use: ---gpu-map 0:1,1:2,2:3 - - ---- -GPU FAQ: - -Q: Can I change the intensity settings individually for each GPU? -A: Yes, pass a list separated by commas such as -I d,4,9,9 - -Q: The CPU usage is high. -A: The ATI drivers after 11.6 have a bug that makes them consume 100% of one -CPU core unnecessarily so downgrade to 11.6. Binding cgminer to one CPU core on -windows can minimise it to 100% (instead of more than one core). Driver version -11.11 on linux and 11.12 on windows appear to have fixed this issue. Note that -later drivers may have an apparent return of high CPU usage. Try -'export GPU_USE_SYNC_OBJECTS=1' on Linux before starting cgminer. You can also -set this variable in windows via a batch file or on the command line before -starting cgminer with 'setx GPU_USE_SYNC_OBJECTS 1' - -Q: My GPU hangs and I have to reboot it to get it going again? -A: The more aggressively the mining software uses your GPU, the less overclock -you will be able to run. You are more likely to hit your limits with cgminer -and you will find you may need to overclock your GPU less aggressively. The -software cannot be responsible and make your GPU hang directly. If you simply -cannot get it to ever stop hanging, try decreasing the intensity, and if even -that fails, try changing to the poclbm kernel with -k poclbm, though you will -sacrifice performance. cgminer is designed to try and safely restart GPUs as -much as possible, but NOT if that restart might actually crash the rest of the -GPUs mining, or even the machine. It tries to restart them with a separate -thread and if that separate thread dies, it gives up trying to restart any more -GPUs. - -Q: Can you change the autofan/autogpu to change speeds in a different manner? -A: The defaults are sane and safe. I'm not interested in changing them -further. The starting fan speed is set to 50% in auto-fan mode as a safety -precaution. - -Q: I upgraded cgminer version and my hashrate suddenly dropped! -A: No, you upgraded your SDK version unwittingly between upgrades of cgminer -and that caused your hashrate to drop. See the next question. - -Q: I upgraded my ATI driver/SDK/cgminer and my hashrate suddenly dropped! -A: The hashrate performance in cgminer is tied to the version of the ATI SDK -that is installed only for the very first time cgminer is run. This generates -binaries that are used by the GPU every time after that. Any upgrades to the -SDK after that time will have no effect on the binaries. However, if you -install a fresh version of cgminer, and have since upgraded your SDK, new -binaries will be built. It is known that the 2.6 ATI SDK has a huge hashrate -penalty on generating new binaries. It is recommended to not use this SDK at -this time unless you are using an ATI 7xxx card that needs it. - -Q: Which AMD SDK is the best for cgminer? -A: At the moment, versions 2.4 and 2.5 work the best for R5xxx and R6xxx GPUS. -SDK 2.6 or 2.7 works best for R7xxx. SDK 2.8 is known to have many problems. -If you are need to use the 2.6+ SDK or R7xxx or later, the phatk kernel will -perform poorly, while the diablo or my custom modified poclbm kernel are -optimised for it. - -Q: Which AMD driver is the best? -A: Unfortunately AMD has a history of having quite a few releases with issues -when it comes to mining, either in terms of breaking mining, increasing CPU -usage or very low hashrates. Only experimentation can tell you for sure, but -some good releases were 11.6, 11.12, 12.4 and 12.8. Note that older cards may -not work with the newer drivers. - -Q: I have multiple SDKs installed, can I choose which one it uses? -A: Run cgminer with the -n option and it will list all the platforms currently -installed. Then you can tell cgminer which platform to use with --gpu-platform. - -Q: cgminer reports no devices or only one device on startup on Linux although -I have multiple devices and drivers+SDK installed properly? -A: Try "export DISPLAY=:0" before running cgminer. - -Q: cgminer crashes immediately on startup. -A: One of the common reasons for this is that you have mixed files on your -machine for the driver or SDK. Windows has a nasty history of not cleanly -uninstalling files so you may have to use third party tools like driversweeper -to remove old versions. The other common reason for this is windows -antivirus software is disabling one of the DLLs from working. If cgminer -starts with the -T option but never starts without it, this is a sure fire -sign you have this problem and will have to disable your antivirus or make -exceptions. - -Q: Cgminer cannot see any of my GPUs even though I have configured them all -to be enabled and installed OpenCL (+/- Xorg is running and the DISPLAY -variable is exported on linux)? -A: Check the output of 'cgminer -n', it will list what OpenCL devices your -installed SDK recognises. If it lists none, you have a problem with your -version or installation of the SDK. - -Q: Cgminer is mining on the wrong GPU, I want it on the AMD but it's mining -on my on board GPU? -A: Make sure the AMD OpenCL SDK is installed, check the output of 'cgminer -n' -and use the appropriate parameter with --gpu-platform. - -Q: I'm getting much lower hashrates than I should be for my GPU? -A: Look at your driver/SDK combination and disable power saving options for -your GPU. Specifically look to disable ULPS. Make sure not to set intensity -above 11 for BTC mining. - -Q: Can I mine with AMD while running Nvidia or Intel GPUs at the same time? -A: If you can install both drivers successfully (easier on windows) then -yes, using the --gpu-platform option. - -Q: Can I mine with Nvidia or Intel GPUs? -A: Yes but their hashrate is very poor and likely you'll be using much more -energy than you'll be earning in coins. - -Q: Can I mine on both Nvidia and AMD GPUs at the same time? -A: No, you must run one instance of cgminer with the --gpu-platform option for -each. - -Q: Can I mine on Linux without running Xorg? -A: With Nvidia you can, but with AMD you cannot. - -Q: I can't get anywhere near enough hashrate for scrypt compared to other -people? -A: You may not have enough system RAM as this is also required. - -Q: My scrypt hashrate is high but the pool reports only a tiny proportion of -my hashrate? -A: You are generating garbage hashes due to your choice of settings. Your -Work Utility (WU) value will confirm you are not generating garbage. You -should be getting about .9WU per kHash. If not, then try decreasing your -intensity, do not increase the number of gpu-threads, and consider adding -system RAM to match your GPU ram. You may also be using a bad combination -of driver and/or SDK. If you are getting a lot more HW errors with the -current version of cgminer but were not on an older version, chances are that -the older version simply wasn't reporting them so going back to and older -version is not a real solution. - -Q: Scrypt fails to initialise the kernel every time? -A: Your parameters are too high. Don't add GPU threads, don't set intensity -too high, decrease thread concurrency. See the SCRYPT-README for a lot more -help. - -Q: Cgminer stops mining (or my GPUs go DEAD) and I can't close it? -A: Once the driver has crashed, there is no way for cgminer to close cleanly. -You will have to kill it, and depending on how corrupted your driver state -has gotten, you may even need to reboot. Windows is known to reset drivers -when they fail and cgminer will be stuck trying to use the old driver instance. -GPUs going SICK or DEAD is a sign of overclocking too much, overheating, -driver or hardware instability. - -Q: I can't get any monitoring of temperatures or fanspeed with cgminer when -I start it remotely? -A: With linux, make sure to export the DISPLAY variable. On windows, you -cannot access these monitoring values via RDP. This should work with tightVNC -or teamviewer though. - -Q: I change my GPU engine/memory/voltage and cgminer reports back no change? -A: Cgminer asks the GPU using the ATI Display Library to change settings, but -the driver and hardware are free to do what it wants with that query, including -ignoring it. Some GPUs are locked with one or more of those properties as well. -The most common of these is that many GPUs only allow a fixed difference -between the engine clock speed and the memory clock speed (such as the memory -being no lower than the engine - 150). Other 3rd party tools have unofficial -data on these devices on windows and can get the memory clock speed down -further but cgminer does not have access to these means. - -Q: I have multiple GPUs and although many devices show up, it appears to be -working only on one GPU splitting it up. -A: Your driver setup is failing to properly use the accessory GPUs. Your -driver may be configured wrong or you have a driver version that needs a dummy -plug on all the GPUs that aren't connected to a monitor. - -Q: Should I use crossfire/SLI? -A: It does not benefit mining at all and depending on the GPU may actually -worsen performance. - -Q: I have some random GPU performance related problem not addressed above. -A: Seriously, it's the driver and/or SDK. Uninstall them and start again, -noting there is no clean way to uninstall them so you have to use extra tools -or do it manually. - -Q: Do I need to recompile after updating my driver/SDK? -A: No. The software is unchanged regardless of which driver/SDK/ADL_SDK version -you are running. However if you change SDKs you should delete any generated -.bin files for them to be recreated with the new SDK. - -Q: I do not want cgminer to modify my engine/clock/fanspeed? -A: Cgminer only modifies values if you tell it to via some parameters. -Otherwise it will just monitor the values. - -Q: Cgminer does not disable my GPU even though it hit the overheat temperature? -A: It only disables GPUs if you enable the --auto-gpu option. If you don't give -it parameters for engine clock it will not adjust engine clocks with this -option. - -Q: Can I use the open source radeon driver for AMD GPUs or the nouveau driver -for NVIDIA GPUs? -A: None of them currently support OpenCL, so no you cannot. - ---- - -This code is provided entirely free of charge by the programmer in his spare -time so donations would be greatly appreciated. Please consider donating to the -address below. - -Con Kolivas -15qSxP1SQcUX3o4nhkfdbgyoWEFMomJ4rZ diff --git a/Makefile.am b/Makefile.am index 706ebed77b..191fa0c982 100644 --- a/Makefile.am +++ b/Makefile.am @@ -1,42 +1,38 @@ ACLOCAL_AMFLAGS = -I m4 -JANSSON_INCLUDES= -I$(top_srcdir)/compat/jansson +JANSSON_CPPFLAGS= -I$(top_builddir)/compat/jansson-2.6/src -I$(top_srcdir)/compat/jansson-2.6/src if WANT_USBUTILS -USBUTILS_INCLUDES = -I$(top_srcdir)/compat/libusb-1.0/libusb +USBUTILS_CPPFLAGS = -I$(top_builddir)/compat/libusb-1.0/libusb -I$(top_srcdir)/compat/libusb-1.0/libusb else -USBUTILS_INCLUDES = +USBUTILS_CPPFLAGS = endif -EXTRA_DIST = example.conf m4/gnulib-cache.m4 linux-usb-cgminer \ - ADL_SDK/readme.txt api-example.php miner.php \ +EXTRA_DIST = example.conf linux-usb-cgminer \ + api-example.php miner.php \ API.class API.java api-example.c windows-build.txt \ - bitstreams/* API-README FPGA-README SCRYPT-README \ + bitstreams/README API-README FPGA-README \ bitforce-firmware-flash.c hexdump.c ASIC-README \ - 01-cgminer.rules GPU-README + 01-cgminer.rules SUBDIRS = lib compat ccan -INCLUDES = $(PTHREAD_FLAGS) -fno-strict-aliasing $(JANSSON_INCLUDES) $(USBUTILS_INCLUDES) +cgminer_CPPFLAGS = $(PTHREAD_FLAGS) -fno-strict-aliasing $(JANSSON_CPPFLAGS) $(USBUTILS_CPPFLAGS) bin_PROGRAMS = cgminer -bin_SCRIPTS = $(top_srcdir)/*.cl - cgminer_LDFLAGS = $(PTHREAD_FLAGS) cgminer_LDADD = $(DLOPEN_FLAGS) @LIBCURL_LIBS@ @JANSSON_LIBS@ @PTHREAD_LIBS@ \ - @OPENCL_LIBS@ @NCURSES_LIBS@ @PDCURSES_LIBS@ @WS2_LIBS@ \ + @NCURSES_LIBS@ @PDCURSES_LIBS@ @WS2_LIBS@ \ @LIBUSB_LIBS@ @MM_LIBS@ @RT_LIBS@ \ @MATH_LIBS@ lib/libgnu.a ccan/libccan.a -if HAVE_WINDOWS -cgminer_CPPFLAGS = -I$(top_builddir)/lib -I$(top_srcdir)/lib @OPENCL_FLAGS@ -else -cgminer_CPPFLAGS = -I$(top_builddir)/lib -I$(top_srcdir)/lib @OPENCL_FLAGS@ @LIBCURL_CFLAGS@ -endif +cgminer_CPPFLAGS += -I$(top_builddir)/lib -I$(top_srcdir)/lib -cgminer_CPPFLAGS += $(ADL_CPPFLAGS) +if !HAVE_WINDOWS +cgminer_CPPFLAGS += @LIBCURL_CFLAGS@ +endif # common sources cgminer_SOURCES := cgminer.c @@ -47,18 +43,9 @@ cgminer_SOURCES += elist.h miner.h compat.h bench_block.h \ cgminer_SOURCES += logging.c -# GPU sources, TODO: make them selectable -# the GPU portion extracted from original main.c -cgminer_SOURCES += driver-opencl.h driver-opencl.c - -# the original GPU related sources, unchanged -cgminer_SOURCES += ocl.c ocl.h findnonce.c findnonce.h -cgminer_SOURCES += adl.c adl.h adl_functions.h -cgminer_SOURCES += *.cl +cgminer_SOURCES += klist.h klist.c -if HAS_SCRYPT -cgminer_SOURCES += scrypt.c scrypt.h -endif +cgminer_SOURCES += noncedup.c if NEED_FPGAUTILS cgminer_SOURCES += fpgautils.c fpgautils.h @@ -68,6 +55,24 @@ if WANT_USBUTILS cgminer_SOURCES += usbutils.c usbutils.h endif +if WANT_LIBBITFURY +cgminer_SOURCES += libbitfury.c libbitfury.h mcp2210.c mcp2210.h +endif + +if WANT_CRC16 +cgminer_SOURCES += crc16.c crc.h +endif + +# Device drivers +if HAS_AVALON +cgminer_SOURCES += driver-avalon.c driver-avalon.h +endif + +if HAS_KNC +cgminer_SOURCES += driver-knc.c knc-asic.c knc-asic.h knc-transport.h knc-transport-spi.c +cgminer_LDADD += -lz +endif + if HAS_BFLSC cgminer_SOURCES += driver-bflsc.c driver-bflsc.h endif @@ -76,22 +81,85 @@ if HAS_BITFORCE cgminer_SOURCES += driver-bitforce.c endif +if HAS_HASHFAST +cgminer_SOURCES += driver-hashfast.c driver-hashfast.h hf_protocol.h hf_protocol_be.h +endif + +if HAS_HASHRATIO +cgminer_SOURCES += driver-hashratio.c driver-hashratio.h +endif + +if HAS_BITFURY +cgminer_SOURCES += driver-bitfury.c driver-bitfury.h +endif + +if HAS_BITMINE_A1 +cgminer_SOURCES += driver-SPI-bitmine-A1.c +cgminer_SOURCES += spi-context.c spi-context.h +cgminer_SOURCES += A1-common.h +cgminer_SOURCES += A1-board-selector.h +cgminer_SOURCES += A1-board-selector-CCD.c A1-board-selector-CCR.c +cgminer_SOURCES += A1-trimpot-mcp4x.h A1-trimpot-mcp4x.c +cgminer_SOURCES += i2c-context.c i2c-context.h +endif + +if HAS_DRILLBIT +cgminer_SOURCES += driver-drillbit.c driver-drillbit.h +endif + if HAS_ICARUS cgminer_SOURCES += driver-icarus.c endif -if HAS_AVALON -cgminer_SOURCES += driver-avalon.c driver-avalon.h +if HAS_KLONDIKE +cgminer_SOURCES += driver-klondike.c +endif + +if HAS_COINTERRA +cgminer_SOURCES += driver-cointerra.c driver-cointerra.h +endif + +if HAS_SP10 +cgminer_SOURCES += driver-spondoolies-sp10.c driver-spondoolies-sp10.h \ + driver-spondoolies-sp10-p.c driver-spondoolies-sp10-p.h +endif + + +if HAS_SP30 +cgminer_SOURCES += driver-spondoolies-sp30.c driver-spondoolies-sp30.h \ + driver-spondoolies-sp30-p.c driver-spondoolies-sp30-p.h +endif + +if HAS_BAB +cgminer_SOURCES += driver-bab.c +endif + +if HAS_AVALON2 +cgminer_SOURCES += driver-avalon2.c driver-avalon2.h +endif + +if HAS_AVALON4 +cgminer_SOURCES += driver-avalon4.c driver-avalon4.h +endif + +if HAS_MINION +cgminer_SOURCES += driver-minion.c +endif + +if HAS_BMSC +cgminer_SOURCES += driver-bmsc.c +endif + +if HAS_BITMAIN +cgminer_SOURCES += driver-bitmain.c driver-bitmain.h endif if HAS_MODMINER cgminer_SOURCES += driver-modminer.c bitstreamsdir = $(bindir)/bitstreams -dist_bitstreams_DATA = $(top_srcdir)/bitstreams/* +dist_bitstreams_DATA = $(top_srcdir)/bitstreams/README endif -if HAS_ZTEX -cgminer_SOURCES += driver-ztex.c libztex.c libztex.h -bitstreamsdir = $(bindir)/bitstreams -dist_bitstreams_DATA = $(top_srcdir)/bitstreams/* +if HAS_BLOCKERUPTER +cgminer_SOURCES += driver-blockerupter.c driver-blockerupter.h endif diff --git a/NEWS b/NEWS index 2f6d0c3e97..86e9f53163 100644 --- a/NEWS +++ b/NEWS @@ -1,3 +1,2371 @@ +Version 4.9.0 - 16th December 2014 + +- Minor fix +- Fix MM41 voltage setting +- Fix the default settings of new module +- Count non matching stratum as a hw error on ava4 +- Fix ava4 build incompatibilites and missing write config parameters +- Use strcasecmp for device matching in usbutils in case of subtle manufacturer +changes +- Add manufacturer and product definitions for ava4 +- Cosmetic ava4 change +- Cosmetic ava4 message fixes +- Add sanity check for NULL data being passed to usb_perform_transfer +- All write errors should be treated as fatal for ava4 devices +- Change initial fan start speed, mins and max for avalon4 to ensure fan starts +spinning but can go lower RPM +- Disable zero length packets on ava4 before trying to init +- Add a cgpu device option to disable zero length packets and enable it for +avalon4 +- Display ava4 stats consistent with other devices +- Add ava4 to udev rules file +- Fix build warnings on ava4 +- Fix ava4 build +- Add Avalon4 support +- Filter duplicate stratum shares from being submitted upstream +- Do rudimentary detection of duplicate shares per device + + +Version 4.8.0 - 25th November 2014 + +- Allow forcing of building driver combinations with --enable-forcecombo +- Put spaces between name and id in avalon2 and icarus +- Relax detection of a failing ava2 to more than 1 minute and perform the test +after polling for results +- Cap maximum diff on ava2 in order to still get shares +- Put space between device name and id to prevent device names with numbers in +them confusing the display +- USB write errors are always fatal so they should be treated as such on ava2 +- Issue a usb reset for ava2 that is not returning valid shares and then drop it +if it persists for over a minute +- Process share results without a result value +- Damp out hashrate displayed for antminer USBs +- Add voltage and speed where relevant to antminer USBs +- Don't estimate time on any antminer usb during a timeout +- Return icarus nonce ok only when the nonce size matches the device or more +- Don't discard old workids until we cycle back to them on antusb and look for +more nonces in the buffer +- Adjust ant usb timing for queued work +- Use a cyclical list for the ant queued work +- Mask and limit workid for antusb and dont clear buffer +- Check the nonce on the worked item, not the submitted work +- Skip over unfinished work that we can't free in ant usb +- Use a workid and array if possible for the small ant usb work queue +- Create an array for antworks for antminer usb devices +- On U3 calculate hashrate purely on shares, not timeouts +- Add switches for AU3 +- Adjust icarus wait timeout according to device +- Differentiate U3 from U1/2 as a separate driver with different parameters and +adjust timing accordingly +- Skip ANUs detected in rock detect +- Try U3 after trying other icarus options +- Add rudimentary ANU voltage setting support for U3 +- Fix ignoring unprefixed v6 address in api allow list +- Fix minor typos in Spondoolies SP10 and SP30 drivers +- Implement a basic rock_flush function to discard the base work we are rolling +work from. +- Task_no for rockminer from the nonce bin should simply be masked +- Change rbox default correction times to 5 in a revised frequency order +- Change default frequency on T1 to 330 +- Reinstate last received check and resend in rockminer, being more lenient at 2 +seconds to allow for dither errors at 1 +- Roll work for the rbox when possible + + +Version 4.7.1 - 4th November 2014 + +- Selectively yield on dropping a lock only on single CPU platforms +- Make it impossible to configure in more than one device that is meant to be +standalone. Add more information to configure help, along with comments for new +drivers. +- Add warning against system libusb in configure help +- stratum_rthread sleep only 3s when all the pool have disconnected +- Filter responses that don't have a result +- Implement support for pool ping and json integers of zero in getversion and +ping +- Fix segfault when writing config with hashratio built in +- Save pools in priority order at time of writing config +- Set the correct flag for close on exec for sockets +- Suspend stratum on removing a pool +- Set CLOEXEC on sockets on linux +- Drivers that take a diff should specify a max diff or it is assumed they don't +support one so set max_diff to 1 if unset +- Send hfa generic frame only if voltage was specified on the command line for +that device +- Set hashfast voltage settings only when really needed +- Hashfast voltage support +- Increase max diff on sp30 to 1024 +- Reset ipv6 flag to false in every api-allow loop +- undeclared identifier 'IPV6_ADD_MEMBERSHIP' fix for apple +- two back temps spondoolies2 +- two back temps spondoolies +- correct suggest_difficulty json rpc call +- Add more usb3 hub identifiers for windows +- Set driver max diff to large value if unset +- Wake gws on get queued +- Implement blacklisting of attempting to match known products from ones without +identifiers +- Fix hfa driver building without libcurl +- Enable building libusb without udev +- Fix off by one calculation error in sp30 leading zeroes +- Send correct diff work to sp30 for hashmeter to be correct +- Do the sleep in spondoolies_queue_full_sp30 after dropping the lock +- Minor tidy in sp30 driver +- Fix sp30 warnings + +Version 4.7.0 - 14th October 2014 + +- Implement generic inet_pton for windows +- Fix warnings +- Fix bulk of remaining style in blockerupter.c +- Tidy style in blockerupter.h +- Tidy bulk of style in blockerupter.c +- Fix missing minimum diff setting for blockerupter +- Fix unused variable warnings +- remove unnecessary sleep; fix potenital div by 0 errs; use min_diff in driver +definition +- Fix coding style +- Make the sp30 hashrate meter based on valid share generation +- Change default max queue back to 1 in line with speed of most current asic +controllers +- Change diff limits to values suitable for sp30 +- Add pool number to response from addpool to the API +- Make the restart and quit API commands valid json responses +- Fix number of nos +- Add option to set clock ('--bet-clk X' actual clock is (X+1)*10 ) +- compatible with X24 board +- Fix error when using v6 without mask in api-allow +- Support ipv6 multicast +- Set min_diff to 1 +- Allow arbitrary clamping of lower device diffs for slow controllers by driver +- Don't set default fan to max on hashratio +- The 2nd read never gets anything on ava2 so remove it entirely and just return +an error if we are out of sync +- Implement support for mining.suggest_difficulty +- Fix client ip address output +- Free addrinfo garbage +- Remove the brackets when using v6 pool address +- Add ipv6 support for api listen +- Avalon Nano: Add support Avalon Nano usb miner +- fix bug in setdiff +- limit minimum diff to 64 +- Add BlockErupter Driver +- Avalon2: display currect max temperature on statline +- Remove unused variable + + +Version 4.6.1 - 20th September 2014 + +- Throttle bflsc28 devices when they hit the overheat limit +- Add whitelisting of firmware used in final bflsc28 products +- API.java - remove lowercase of all data sent +- Avalon2: Add 3 bytes nonce2 support +- Avalon2: MM needs n2size length <= 4 +- Use fan min as fan speed when run with --avalon2-fixed-speed +- Clear the pool submit fail bool after adding shares to the stratum hashtable +to minimise window the share is not in the table +- api-example unlimited socket works +- Add custom strcasestr and use custom gnu type functions in bflsc +- Fix windows build of bflsc driver +- Fix possible deref in bflsc28 + + +Version 4.6.0 - 7th September 2014 + +- We should not be checking for pool_unworkable in cnx_needed as it is keeping +stratum connections open on unused pools +- Properly handle lack of input when adding pool via menu +- Allow workers without passwords +- minion - increase max chip number +- Avalon2: add more comments on Avalon2 options +- Avalon2: add polling delay option, long coinbase support, LED status on API, +change overheat from 88 to 98 +- minion - add a ' before non-zero core error counts +- minion - hidden optional per core nonce stats +- bflsc28 - clock is hex +- bflsc28 - allow setting clock and volt from the API ascset command +- bflsc28 - add chip count to stats +- bflsc28 stats +- Simplehacks to better serve bflsc28 +- Only use one hashtable for bflsc28 work queued +- Copy back the buffer after we've stripped the inprocess field on bflsc +- Parse results for BMA based on uid and remove work from the queue when found +- Strip out the inprocess details from bflsc results if it exists +- Create a hashtable of work by uid as it's accepted by BMA +- Add some rudimentary values for BMA sleep times +- Fix various errors in queueing work for bflsc28 and limit job queueing to 10 +to fit within a usb frame +- Create preliminary work queueing for bflsc28 using jobs of up to 20 at a time +by rolling work where possible +- Convert all bflsc transfers to the full 512 bytes +- Don't mistake bflsc28 for fpga +- Do initial detection of bflsc28 devices + + +Version 4.5.0 - 29th July 2014 + +- Fix windows build for hashratio and ava2 +- Demote bad checksum message in cointerra driver but allow message to still be +parsed since it won't allow existing firmwares to work otherwise +- Reorder and document the configure options +- Merge https://github.com/KnCMiner/cgminer into knc +- Change default voltage on ava2 to 0.666V because Satan +- Enable combined building of avalon2 and hashratio +- Fix stratum embedded fpgas to not duplicate work with other devices +- Implement smarter PID type fan control for ava2 allowing more generous +temperatures and far lower fan speeds for optimal power and noise usage. Adjust +default frequency to 450 as per recommendation. +- Fix various warnings in ava2 +- Go back to polling design since async will not work for ava2 and fix various +read design errors +- Fix error in 2nd read functions for av2 and hro +- Correct init and read sequence for ava2, and convert from a polling mechanism +to a separate read thread +- Initial commit of ava2 conversion to direct USB +- Display frequency and voltage with ava2 on the statline +- Store fan percentage and display temp and fan percent for ava2 +- Set avalon2 frequency and voltage to appropriate defaults if none are +specified on the command line +- Demote some ava2 messages that don't need to be errors and remove unused works +array +- Fix broken fan logic for ava2 +- Fix hexdump on 64bit +- rockminer frequency is between 200 and 400 MHz +- fix jansson include path in cgminer-api compile instructions +- Remove requirement for ifndefs for avalon2 from the generic driver work +function +- Fix hashratio device name +- Make submit_nonce2_nonce return whether the share was valid or not +- Reinstate missing necessary init sequence for hashratio +- Handle disconnected hashratio devices +- Add hashratio frequency command line +- Fix stratum updates not being passed to hashratio devices and clean up +- Move to updated avalon2 type driver model for hashratio device +- Initial import and conversion of hashratio driver to direct USB +- Increase the internal buffer for API response, as "stats" command response +can grow greater than 8K +- Detach test pool thread only if we have a blocking startup + + +Version 4.4.2 - 17th July 2014 + +- Remove the use of the pthread_tryjoin_np which is currently unimplemented on +many platforms +- Fix processarg parameters loaded from a config file not being saveable +- We only use the jansson in our source tree so no need for special case +handling of older versions +- Upgrade jansson to 2.6 +- Only clear sockbuf if it's been allocated +- Fix missing osm-led-mode support in write config +- Deal with nanosecond overflow in both directions on both addition and +subtration of timespecs +- Rename sp10 driver internally from spondoolies to sp10 +- minion - add a 2nd (optional - disabled) reset test +- production stats added, reset queue added +- minion - correct led ghs2 choice +- minion - correct ghs2 display +- minion - reset the led counter when doing a chip RSTN full reset +- minion - don't reset the led counter for an SPI reset +- minion - led per chip and use all time average +- minion - report spi error counts and settings in stats +- minion - undeclared fix +- minion - chip power cycle option +- minion - record 0xff error history and reduce screen output +- minion - reset on result or fifo 0xff +- minion - clarify the 0 value of spireset +- minion - make SPI reset more configurable +- minion - make the SPI reset ms sleep a parameter and API settable +- sp10 sensors +- sp30 +- minion - led+more api setting +- Avoid blocking all pool testing if one pool fails to ever init +- There is no point storing the hints addrinfo in struct pool +- minion - 'reset' SPI when getting errors +- initialise more pool values in benchmark +- minion - auto adjust freq +- merge upstream frequency changes +- icarus - timing history in its own function +- rbox - add lotsa stats, tidy up a bit more +- Fix an off-by-one. +- icarus - detect stat should be LOG_DEBUG +- icarus - tidy up rbox code, remove statics, and add rocketbox +- minion - do an early reset to clear the chip status +- minion - use descriptive names for the list types +- Avalon2: automatic adjust fan speed, using crc16 on job_id compare, turn on +the led by API, detect twice when start, remember the last stratum message +increase the hashrate, add cutoff option +- fix AntS1 breakages from AntS2 changes +- minion - disable dup nonce check +- minion - add an ioseq number for each ioctl to simplify work ordering +- minion - redo old work expiry based on txrx order +- minion - more work stats, minimise queued work, free flushed queued work +- minion - allow resetting a chip via the API +- minion - correct 'WQue Count' in stats +- minion - delay after reset, reset history also, add dups to api stats +- noncedup - give access to the internal stats +- minion - increase reset to 75% +- minion - dup checking, disable reread by default and extra ioctl debugging +- minion - always check the chip queue before queuing new work + + +Version 4.4.1 - 21st June 2014 + +- Move icarus driver to being seen as an asic +- Clear usb reads on each pass through icarus detect to hopefully prevent false +positives for detecting rboxes +- Clean up pool failure and failover code for stratum + + +Version 4.4.0 - 16th June 2014 + +- Tidy unused rockminer variables +- Tidy rockminer defines +- Make rockminer driver compatible with other icarus drivers being present +- Import basic rbox driver +- minion - add optional (on) GPIO chip selection +- Clear the pool idle flag in the pool test thread +- CoreFmatch in cointerra should be a uint16 +- Display error message if we receive one on share rejects +- Allow zero length strings to be passed to valid_hex +- delete unused roundl definition + + +Version 4.3.5 - 10th June 2014 + +- Cointerra driver updates. +- Sleep before retrying in the test pool thread after a pool has died +- Use valid_ascii testing for job_id since it need not be hex only +- Only show slow/down message till pool is flagged idle +- Do some random sanity checking for stratum message parsing +- Keep looking for when a pool comes to life at startup and touch the logwin so +the message is not invisible +- Fix no libcurl build +- Added Drillbit Thumb to udev rules. +- Avoid dereference on getting API stats on partially initialised HFA instances +- A1: add support for updated product variants, small fixes +- Add one more usbutils fix +- Convert uses of usbutils memcpy to cg_memcpy +- Add a sanity checking memcpy function which checks for overflows +- minion - count force use reread +- minion - add a disabled ioctl() test +- minion - add more checking of SPI results for corruption +- minion - optional (disabled) ioctl() debug +- Increase S1 overheat to 75 degrees C +- Add ruby api-example to API-README +- minion - allow core selection at runtime +- API - lcd all-in-one brief summary + + +Version 4.3.4 - 25th May 2014 + +- Add support for 2 nonces per block in spond driver +- Increase timeout on reset in cta driver to 5 seconds +- Increase max diff on spondoolies driver slightly to be well below spi comms +limitations +- Use the active contents lock and safe list iteration within the linux usbfs +code +- Add Ruby Api Example +- Automatic detect the small miners +- Update default modules from 3 to 4 +- Fix the temp max. we should use currect max temp +- add avalon2-cutoff options +- Enable the cutofftemp to Avalon2. ignore longer coinbase and longer merkles +stratum +- Fix the diff value used on MM firmware +- Mark pool as idle if stratum restart is failed +- Add hacky workaround for double list removal race in libusb +- Make the work given in benchmark mode deterministic on a per-device basis +- Rework the benchmarking code to use a deterministic set of work items with a +known number of diff share nonces at regular spaced intervals +- minion - restrict nonce read result size to ioctl() limit +- minion - must check temp when overheated +- minion - idle chips that hit >100C until back to 80C +- minion - report the chip/reg when aborting due to an invalid ioctl() size +- minion - all freq in Mhz but only convert when used +- minion - remove unused ioctl debug +- minion - command queue is now larger +- minion - check rolled in stale work cleanup +- Work stats should be based on device_diff not work_difficulty since non-shares +haven't been filtered out yet +- Prevent a segfault when writing a config file containing 'rotate' option +- minion - comment out HW debug message +- minion - roll work to reduce CPU +- minion - report init_freq in stats +- api - howoldsec is only used for USB +- minion - allow setting the frequency +- minion - disable iostats by default since it slows down mining +- minion - define frequency value table +- minion - report temp/cores/freq and handle temp formatting +- minion - item is undefined +- Rationalise diffs stored in the work struct and document them to avoid further +confusion +- Add basic API stats for nfu drivers to see how many submits each chip returns +- Add output direction for the EN0 pin on nfu driver +- Support power management optimisations in newer nf* firmware +- Support variable numbers of chips with NFU and BXM drivers +- Identify number of chips in nanofury devices and change name accordingly +- Rename nf1 driver to nfu in anticipation of support for more chips +- Make hashfast reset counter rise on old instances when inheriting the value on +new ones + + +Version 4.3.3 - 3rd May 2014 + +- Fix typo +- Work should be freed when aged, fixing a massive memory leak for bxf devices +- miner.php fix single rig summary/config field formatting +- miner.php fix single rig total formatting + + +Version 4.3.2 - 2nd May 2014 + +- Fix accounting bug with nrolltime drivers + + +Version 4.3.1 - 2nd May 2014 + +- upgrade some int to int64_t to avoid overflows in reporting +- Make reconnection messages more explanatory +- Stratum client.reconnect require matching URL +- Fix memory leak in submit_noffset_nonce +- Clean up any work that may not have been used in the work scheduler +- Avoid unnecessary deref now that it's done within discard_work +- Clean work pointers after one way usage functions +- Avoid unnecessary total_work_inc in generating local work +- Cosmetic fixes +- Fix idle bug, when redirected client can't auth +- Rename spond temp rate to asics total rate +- Build fixes +- Set the unique id only for usb devices with serial strings longer than 4 chars +long +- Use usb serial strings as unique id if devices have them +- Discretely identify the onestring miners as OSM +- Add bxf debugging option and osm led modes +- A1: modularize board selector / add initial CCR support +- A1: cleanup tca9535 logging +- A1: fix and extend PLL parameters +- A1: clean up compile warnings +- A1: use real level in hexdump +- Add identification for onestring miner variants +- Avalon2: Parser the power good signal +- driver-avalon2: this functions used on detect, which don't have thr setup yet + + +Version 4.3.0 - 18th April 2014 + +- Put sleep in spond hash instead of queue full function +- Remove unused function for when compiled without curses +- Fix typo +- Add temperature rate, front, rear and device temperature to spond API output +- Limit bxf sleep in bxf_scan to 100ms minimum for strings of many chips +- -Werror=format-security error on driver-bitmain.c +- Fix parameters passed with getblockhash +- Check the block hash with the proper command when looking for orphan chains +- syslog requires a facility ... in more than one place +- Shuffle windows headers included +- Adjust the bxf sleep time according to the number of chips detected +- Fix off by one error in bxf chip count when adjusting device size +- Recalloc correct pointer +- Make instructions associated with winusb error even more explicit +- Add midsing headers to cgminer source in Makefile +- Trivial style changes to mg proto parser +- Trivial style and warning clean ups on spondoolies driver +- Merge spondoolies driver patch +- Call any BXF device with 3-6 chips reported HXF +- Avoid derefrence when calling statline before on hfa device during init +sequence +- Calloc the info structures even on failed hfa reset to prevent later possible +dereference +- Load all hfa devices based on identification alone and defer init sequence +till mining thread init sequence to allow all devices to be recognised rapidly +but each device initialisation not delay others +- Do not do thread shutdown unless thread init succeeded +- Remove unnecessary check for thread_prepare function +- Recognise variations on BXF based on chip value returned in responses +- Provide helper function for recallocing memory +- syslog requires a facility + + +Version 4.2.3 - 3rd April 2014 + +- Decay the per device hashrates when only the watchdog is calling the hashmeter +- Fix parsing of config files failing on custom parsing +- Allow an arbitrary number of chips in the BXF driver, showing results from +each chip in the API and identify the hexfury, naming it HXF +- Disable toggling display by default and offer a --widescreen option to have +all the information on an extra wide display. +- Use OPT_WITH_CBARG for all custom parsing functions to allow their values to +be written generically when writing the config file from the menu. +- Provide a ccan variant OPT_WITH_CBARG that assigns the arguments passed as a +string and then performs the callback function on the string. +- Define strings to store special option parsing parameters leaving no +OPT_WITH_ARG missing args +- Correct the writing of special case options to the config file +- Provide support for writing anu freq from menu write option +- Update to diver-avalon2.c +- Generalise a lot more of the command line options simplifying the write config +function and making it write far more values unaided +- Use the general opt_set_charp functions for setting api parameters +- Json escape any strings written to the config file +- Store standard charp options when writing config files +- Add support for all the integer range options when writing the config file +from the menu +- Remove the --device and --remove-disabled options which don't work in a +meaningful way any more +- Make the bxf bits configurable on the command line +- Provide a --btc-sig option to optionally add a custom signature to the solo +mining coinbsae +- Compact gbt solo extra data and store the length, allowing it to be variable, +leaving room for a signature +- miner.php - Kano summary Pool Acc/Rej should be only work submitted +- miner.php add best share and gen formatting for pool summary +- miner.php - remove BGEN/GEN eval() errors from the web log +- miner.php allow optional fields when gen is disabled +- miner.php dont format missing gen fields +- miner.php make Summary a custompage +- miner.php allow uers and system lists of customsummarypages and add more +examples +- Fix getwork share submission +- Cosmetic fix to udev rules +- Put WU on the hashrate status to compact lines further +- miner.php show api/rig errors at the top of a customsummarypage + + +Version 4.2.2 - 29th March 2014 + +- Minor correctness fix for unnecessary free +- Clean up various curl build issues +- allow url based config files +- Frequency only needs 3 digits for cointerra statline +- Use the serial number as unique_id for cta display +- Make it possible to enable/disable the status window from switching via the +display menu +- We should not update the tv hashmeter time unless we're updating the hashrates +- Add cointerra devices to udev rules. +- Use hashfast unique id instead of number since the unique id is displayed +- Remove displayed space +- Left align the displayed unique id +- Use the hashfast opname as its unique identifier +- Display BF1 serial number as its unique identifier +- Display a unique identifier instead of a number if the device has one +- Use an alternating status display to return to a compact width of 80 +characters, allowing more information to be displayed. +- No need for looking for khash hashrates in summary any more +- Fix two potential minor mem leaks +- Fix memory leaks in setup and generate work for gbt solo. +- Fix off by one malloc size error +- Fix memory leak in update_gbt_solo +- Put sanity check on decay_time to prevent updates with no time +- Add 3 rolling average hashrates to API output for summary and devs. +- Use the extra status screen real estate better, displaying rolling 1/5/15min +average hashrates as well. +- Revamp the ageing crufty hashmeter code to have proper exponential decaying +values and store rolling 1/5/15min hashrates. +- Increment total_work under control lock. +- Trivial variable reuse +- Add support for other usb3 hubs on windows + + +Version 4.2.1 - 24th March 2014 + +- Fix various ava2 build issues generically +- Minimise the amount of heap memory allocations/frees when submitting gbt +shares. +- Make varint in gbt submission a stack object. +- Fix big endian problems with gbt submissions. +- Fix 32bit overflow on relative diff shown. +- ants1 - stop results read hard looping +- ants1 - slow down mining if overheat occurs +- miner.php allow gen before (bgen) and after (gen) grouping +- Change default solo mining to failing when no btc address is specified. +- Use upgrade cglock variants in get_gbt_curl +- Provide a cg_uilock to unlock the intermediate variant of cglocks. +- Use the one curl instance for all gbt solo operations, protecting its use with +a bool set under gbt lock. +- Only start block detection with gbt solo if setup succeeded +- One less block detection message +- Toss out the curl handle after each solo poll +- Don't reuse any curl handles for solo mining and break out of the lp thread if +the pool is removed. +- Make sure to only start the lognpoll thread once on gbt solo. +- Don't keep RPC connections open for solo mining since bitcoind doesn't like +having many persistent connections. +- GBT solo pools should be considered localgen pools. +- miner.php - speed up formatting and allow calc on gen fields +- Always show the address we're solo mining to to avoid confusion for when no +address is set. + + +Version 4.2.0 - 18th March 2014 + +- Fix missing htobe16 on windows and meaningless >u32 string warning. +- Software ntime roll for all hashfast devices. +- Silence harmless warning. +- Drop a failed restart icarus device to allow it to be rehotplugged if +possible. +- Work with more than one transaction. +- Kill gbt solo pools that don't respond to the gbt request 5 times +sequentially. +- Fix ser_number for no remaining val byte. +- Create a work item and stage it when updating the gbt solo template to allow +new block detection and restart code to work. +- Test block hash as well as block height when solo mining to ensure we haven't +been mining on an orphan branch. +- Fix transaction processing for gbt solo. +- Encode height using integer varint format. +- Make new block detection message not show in gbt solo from test_work_current +- Add block detection via getblockcount polling in gbt solo and update gbt +template every 60 seconds. +- Iterate over transactions twice to malloc only once when copying all the +transaction data. +- Update solo coinbase regularly and submit as gbt work +- Only show merkle hashes for solo mining in debug mode. +- Set correct flag for solo work. +- Generate gbt solo work emulating stratum work construction. +- Set the diff as a double sdiff from gbt solo data. +- Move swork.diff out of the stratum work section to be shared as sdiff. +- Generate a header bin from gbt solo as per the cached stratum one. +- Store strings similar to stratum's when decoding gbt solo +- Avoid allocing and freeing stratum strings that should be fixed length. +- Run parser through detect_stratum after stratum+tcp:// is force added +- Remove unnecessary header length calculation for stratum header binary and +only binary convert the correct length of the header. +- Share more fields between stratum and gbt +- Share coinbase_len variable b/w stratum and gbt and setup more gbt solo +parameters. +- Generate a valid coinbase and set nonce2offset for gbt solo +- Move scriptsig header bin conversion to setup gbt solo +- Create our own custom scriptsig base. +- Add helper functions for creating script signature templates and beging +building template. +- Do gbt solo decoding under gbt lock. +- Add more gbt variable decoding from gbt solo information. +- Store all the transaction data in binary form when using GBT +- When setting up solo mining, check validity of bitcoin address against +bitcoind +- Make pooled GBT mining use merkle bin optimisations slated for solo mining. +- Abstract out the merkle bin calculation for gbt solo +- Implement efficient merkle tree base from solo GBT information. +- miner.php custom formatting and row counter '#' +- Drillbit: Fix for underestimating hash rate from Bitfury devices +- Send per-core hashrates at regular ~5min intervals back to cta devices. +- Calculate the cta per core hashrate at 5 minute intervals. +- Check the bits of the correct core in cta bit count. +- Display the bit count along with the bitmap for each cta core in the API stats +output. +- Store and display the per core hashrate on cta relative to each work restart. +- Decrease the time we wait for unsetting a core on the cta bitmap to correspond +with the lower max diff of 32. +- Set max diff on cointerra devices to 32 which is still only 11 shares per +second but allows for earlier confirmation of per core hashrates. +- Keep track of when the last restart and work updates were triggered and +provide helper functions for knowing the time since then. +- hashfast make api stats field names unique +- Fix gcc longjmp warning in api.c +- Add a per-core hashrate to the cta API stats. +- miner.php support edevs and estats +- API - put edevstatus where it was supposed to be +- Icarus - allow timing mode to work with ANU and not slow it down +- drillbit - remove warnings +- drillbit - minor code tidy up +- Drillbit: Change language around 'void' to warning about limiter disabled +- Drillbit: Fix accidental over-counting of HW errors +- Drillbit: --drillbit-auto parameter for tweakable custom tuning of ASIC speeds +- Drillbit: Output warning if board reports void warranty +- Drillbit: Add Avalon & drillbit-autotune notes to ASIC-README +- Drillbit: Limit work sent out to 8 units in a single pass, was DoSing a full +double scroll +- Drillbit: Move drillbit_empty_buffer calls to only when errors occur, were +limiting performance on Windows +- Fix Windows bug with libusb_reset_device returning SUCCESS for disconnected +device +- Drillbit: Fix some warnings +- Drillbit: Add --drillbit-autotune option for device to dynamically alter clock +speed +- Drillbit: Fix typo in previous commit +- Drillbit: Remove default config in cgminer, rely on defaults in firmware +- Drillbit: Combine split USB transfer for sending new work, reduce overhead +- Drillbit: Add support for protocol V4, with device-agnostic board +configuration data +- Drillbit driver: Add support for Avalon-based Drillbit miners +- API - add edevs and estats - to only show enabled devices +- Check device data exists on a hfa instance before trying to reinit it. +- Print off what quadrant regulator failed if known in hfa driver. +- Reset all the stats on autovoltage complete in cta driver. +- Use correct diff instead of diffbits in cta driver. +- Whitelist all firmwares <= 0.5 on hfa for software rolling of ntime. +- Avoid a memory leak by reusing the ntime field when rolling stratum work. +- Clear the pipe bitmap on cta only when no share has occurred for 2 hours +instead of 1. +- Cta share_hashes should be added, and we can base it on device wdiff instead +of pool work difficulty for more accurate hashrates. +- Since the device runtime is now reset, the Raw hashrate entry in the cta API +output is no longer meaningful. +- Look for autovoltage returning to zero on cta driver and reset stats at that +point since the hashrate is unreliable till then. +- ants1 - cgminerise applog calls +- Default to stratum+tcp:// on any urls that don't have a prefix instead of +http. +- Trivial cta style changes. +- ants1 - fix/enable temperature checking and remove unneeded temp_old +- ants1 - move local cgpu variables to info structure +- ants1 use a klist to store work and copied work +- Simplify dramatically the cross-process cgminer locking through use of flock +instead of sysv semaphores. + + +Version 4.1.0 - 8th March 2014 + +- Correct fix for dev start time being adjusted for stat zeroing. +- Make per device stats work for average after a stat zeroing. +- Add an hfa-options command line that allows the clockspeed to be chosen per +device by name comma separated, with a function that can be expanded with more +options in the future. +- Off by one drv_rolllimit check against jobs +- Free the work that may be lost, leaking memory, in a failed hfa_send_frame +- Roll the ntime for work within the hfa driver for firmware we know doesn't do +it internally as an optimisation. +- Export the roll_work function to be usable by driver code and make it +compatible with rolling stratum work. +- Make opt_queue be respected as a maximum value for staged items. +- Disable mistakenly enabled lock tracking. +- api version update for HEX32 +- api.c - HEX32 type needs quotes +- Disable the MAX_CLOCK_DIFF check for newer hashfast firmwares since it's not +required. +- Store the hardware and firmware revision in the info struct for easy use in +the hfa driver. +- Only decrease the hfa clock rate if the device has been running for less than +an hour before dying. +- Change lack of op name response message in hfa driver +- Check for lost devices at every write/read in hfa_detect_common +- Make bxm bits configurable. +- Move avalon2 options to ~alphabetic position in help. +- Do a shutdown routine on bxm close. +- Provide support for 2 chips in libbitfury sendhashdata and enable the 2nd chip +on BXM devices. +- Remove unnecessary opayload and newbuf members of bitfury info struct. +- Add an spi add fasync command. +- Cope with older hfa firmware not even responding to op_name. +- Forcibly kill everything silently with an exit code of 1 should we fail to +cleanly shut down and use a completion timeout for the __kill_work in +app_restart. +- Make __kill_work itself also be a completion timeout. +- Generalise more of libbitfury for more reuse in both nf1 and bxm drivers. +- Remove redundant init components of bxm driver. +- Set default osc6 bits on bxm to 50 +- Enable the transaction translator emulator for bxm devices and use a dummy spi +tx the size of a normal payload. +- Store usb11 and tt flags as booleans in cgusbdev allowing them to be +discretely enabled as well as detected by the device data. +- Add bxm scan function and check spi txrx returns only as much as sent. +- Add init sequence to bxm detect one. +- Add a bxm specific txrx function for spi transfers. +- Add bxm close to bitfury shutdown switch. +- Add reset/purge/cshigh/low sequence to bxm init +- Add bitmode init to bxm open sequence. +- Add initial bxm opening sequence for detect one. +- Add identifiers for bxm bitfury devices. +- Clean up parse_method +- More gracefully break out of parse_notify on a corrupted hex string error, +checking the return value of all hex2bin conversions and being consistent with +using stack memory. Fix an unlocking error in cases of failure. +- AntS1 - add detection information to usbutils +- Enable Bitmain Ant S1 code and make it conform to cgminer requirements +- Make the cointerra displayed hashrate based on valid share generation. +- Convert and update values shown in the cointerra api output. +- Export the api_add_int16 function. +- Use a custom mystrstr function in cointerra driver. +- Add api_add_int16 to API functions. +- Add support for Bitmain Multi Chain and Single Chain and optimize the +efficiency +- Add support for bitmain devices +- Perfect function of BitMain Multi Chain +- Add support for Bitmain Multi Chain and Single Chain and optimize the +efficiency +- Add support for bitmain devices + + +Version 4.0.1 - 28th February 2014 + +- Refresh the log window on pool failure message at startup. +- Rework the pool fail to connect at startup to not get stuck indefinitely +repeatedly probing pools with new threads and to exit immediately when any key +is pressed. +- Use an early_quit function for shutting down when we have not successfully +initialised that does not try to clean up. +- Add more information to a hfa bad sequence tail event. +- Increase the work queue at the top end if we've hit the bottom as well. +- Set the work generation thread high priority, not the miner threads. +- Bringing each hfa device online takes a lot of work generation so only ever do +one at a time. +- Increase the opt_queue if we can hit the maximum amount asked for but are +still bottoming out. +- Keep the old hfa device data intact with a clean thread shutdown to allow it +to be re-hotplugged with the old information. +- Cope with the API calling hfa on partially initialised devices having no info. +- Show only as many digits as are required to display the number of devices. +- Cold plug only one hashfast device to get started, and then hotplug many to +minimise startup delays and possible communication delays causing failed first +starts. +- Send a shutdown and do a usb_nodev if hfa_reset fails. +- Null a device driver should thread prepare fail. +- Add a function for making all driver functions noops. +- Don't try to reinit a device that's disabled. +- Disable a device that fails to prepare. +- Check for lack of thread in watchdog thread for a failed startup. +- Make all device_data dereferences in the hfa driver safe by not accessing it +in statline before when it's non-existent. +- Add an option to disable dynamic core shedding on hashfast devices. +- Do not remove the info struct on a failure to hfa prepare. +- Detect an hfa device purely on the basis of getting a valid header response to +an OP_NAME query, leaving init to hfa_prepare which will allow multiple devices +to start without holding each other up at startup. +- Store the presence and validity of opname in the hfa info. +- api - buffer size off by 1 for joined commands +- minion - clean up statline +- Only break out of usb_detect_one when a new device is found. +- Use usb_detect_one in the hfa driver. +- Provide a usb_detect_one wrapper which only plugs one device at a time, +breaking out otherwise. +- Issue a usb_nodev on a bad work sequence tail in hfa +- Read in hfa stream until we get a HF_PREAMBLE +- Add shed count to hfa API stats output. +- Display the base clockrate for hfa devices with a different name to per die +clockrates to be able to easily distinguish them. +- Use op_name if possible first with hfa devices to detect old instances and be +able to choose the starting clockspeed before sending an init sequence, +reverting to setting op name and serial number as fallbacks. +- Make hfa resets properly inherit across a shutdown. +- Don't break out of hfa_old_device early if there's no serial number. +- Fix harmless warning. +- Allow the drop in MHz per hfa failure to be specified on the command line. +- Icarus - ignore HW errors in hash rate ... and fix detection of them +- Enable the hfa shed supported feature by default. +- Add to udev rules hfa devices for firmware writing. +- Remove ENV from hashfast udev rules. +- Add a --hfa-name command that allows one to specify the unique opname for a +hashfast device. +- Ava2 decode the voltage, get the temp_max +- Set the clock rate with a work restart instead of an init when changing to old +clocks for hfa +- Set opname on hfa devices without a serial number to a hex value based on time +to not overflow the field. +- Add op name to hfa API stats output if it exists. +- Set the actual op_name in hfa devices if cgminer is choosing it itself due to +it being invalid. +- Re-init an hfa device to its old data before setting up info structures as +their sizes may change. +- Remove the usb device whenever we do a running shutdown on hfa and do a +shutdown as the imitated reinit to allow it to hotplug again. +- Reset opt hfa dfu boot after it's used. +- Comment out windows only transfer on hfa startup. +- Clean up structures unused in case of all failures in hfa detect common +- Clear all structures should we fail to hfa reset on adjusting clock on a +hotplug. +- Set master and copy cgpu hash clock rate for hfa when dropping it on a +restart. +- Set the master hfa clock speed to lower when shutting down a copy. +- Do a clear readbuf on any hfa reset in case the device has not yet cleanly +shut down. +- Increase hfa fanspeed slightly more when it's rising in the optimal range than +falling. +- Always decrease hfa clock speed on a running shutdown and don't try sending an +init frame since it will be dropped regardless. +- Match hfa devices to old ones based on OP_NAME values before serial numbers if +possible. +- Read off the OP_NAME if it exists and is supported on hfa devices, setting it +to the device serial number or a timestamp if it is invalid. +- Updated hf protocol +- Check for an amount along with no error in hfa clear readbuf +- Hfa clear readbuf can return a nonsense amount when there's a return error so +ignore the amount. +- Running resets always cause a shutdown on hfa meaning the device will +disappear with modern firmware so always kill off the threads to allow +re-hotplugging. +- Reset the hfa hash clock rate to the old one if we find an old instance, only +setting the device id in hfa_prepare +- Keep the device_id on the original zombie thread for HFA in case of further +resets. +- Break out of hfa inherit if there is no device data. +- Inherit the hfa zombie instance after the device id has been allocated. +- The list_for_each_cgpu macro will dereference when there are no mining threads +yet. +- Make hfa hotplug inherit some parameters from a previous instance if the +serial number exists and is matching, avoiding dropping the clock on all +devices. +- Per device last getwork won't work if the device stops asking for work. +- Use the share_work_tdiff function in the driver watchdogs. +- Provide a helper function for determining time between valid share and getwork +per device. +- Store last_getwork time on a per-device basis. +- Limit the decrease of hfa clock rate on reset to the default clockrate. +- Base the hfa failure time on the current expected hashrate instead of a static +15 seconds. +- We shouldn't be trying to read from the hfa_send_shutdown function itself. +- Reset the icarus failing flag only when a valid nonce is found. +- Transferred value is corrupt on a NODEV error in usbutils. +- Set each miner thread last valid work just before starting its hash loop in +case there are delays at startup. +- Only memcopy *transferred data in usbutils if we have received only success or +a non-fatal error. +- Increase to 25 nonce ranges on icarus fail detect. +- Set icarus device fail time to be dependent on device speed to avoid falsely +detecting failure on slower AMU devices. +- Updated hf protocol header. +- Updated BE hf protocol header. +- Take into account shed cores on hfa devices when determining how many jobs to +send. +- Fix compilation error with two avalon types. +- Fix missing A1 files from distribution. + + +Version 4.0.0 - 21st February 2014 + +- Check for error from setfloatval +- Halfdelay cannot be larger than 255. +- Allow any arbitrary frequency to be specified for ANU devices and try to find +the nearest frequency when initialising it, reporting if the frequency is not +exactly as requested. +- Only show system libusb warning when appropriate during configure. +- Merge branch 'avalon2' of https://github.com/xiangfu/cgminer into +xiangfu-avalon2 +- Hfa cooling remains satisfactory down to a minimum fanspeed of 5% +- Give a nodev error if we have already set nodev in hfa clear readbuf to avoid +further usb comms attempts. +- Fix missing include +- Move bitmine options to alphabetic positioning. +- bab - missed a few 'DEAD's in last commit +- bab - use 'bad' instead of 'dead' as per the screen B: +- bab - roll work if possible to reduce CPU +- Update the per die hash clock data on a running reset on hfa devices. +- Set the per die clock on hfa to the known starting base clock instead of our +requested clock rate. +- Hfa device failure can be detected within 15 seconds so we should try +restarting it sooner to avoid tripping the device's own watchdog. +- Check return result of hfa clear readbuf to minimise error messages on device +failure. +- Put MHz into cta statline description. +- Send a work restart with every shutdown message to hfa devices to clear any +work that might be stale on the next restart. +- Store the hfa hash_clock rate and display it in the statline. +- Store the maximum board temperature for hfa devices and take that into +consideration when calculating the highest temperature as well as the dies. +- A1: CoinCraft-Desk driver variant +- Initial import of Bitmine.ch A1 SPI driver +- klondike ensure stats type matches +- avalon, bab, drillbit, klondike use more screen space rather than truncating +info +- Add hashfast fanspeed% to statline display. +- Move driver statline padding to cgminer.c, expanding width of maximum +displayable statistics and window width to add more info. +- Prune old stratum shares that we've seen no response for over 2 minutes to +avoid memory leaks for pools that don't respond about some shares. +- Add warning if system libusb is being added. +- Only run ./configure with autogen.sh if extra parameters are passed to it. +- Updated cointerra features. +- Add le16toh defines for platforms that may be missing it. +- Remove modminer bitstreams from distribution and replace with a README saying +what file needs to be added if modminer build is still desired. +- Use the simplelog function from usb_list() +- Add a simplelog function that does not log date and time. +- Use a unique usb_list function displaying only pertinent information when +listing usb devices from the menu. +- Abstract out the _in_use function to take different linked lists. +- Break out of linked list loop in remove_in_use in case we've gone over the +whole list. +- Check for hfa invalid hash clockrate after other error messages. +- Detect non-responsive icarus devices and attempt a usb reset before killing +them after 2 minutes of no hashes. +- Detect non-responsive bitfury devices and try a usb reset on them before +killing their instances off after 2 minutes of no activity. +- Allow hotplug interval to be changed from the USB menu. +- Prevent recursive loop in __is_in_use linked list walking. +- Add the ability to whitelist previously blacklisted usb devices from the menu. +- Use a bool in struct cgpu to know when a usb device has been blacklisted, +avoiding blacklisting it more than once. +- bab - ensure disabled chips are counted in the screen dead chip counter +- bab - only disable the chip once ... +- bab - short work list skip disabled chips +- api.c avoid incorrect gcc warning +- cgminer -h crash fix +- Add blacklisting as an option to the USB menu. +- Add a mechanism to blacklist a usb device from its cgpu. +- Add an option to the USB menu to list all known devices. +- Add an option to send a USB reset via the USB menu. +- Add a usb_reset by cgpu function to usbutils. +- Add warning for attempting to unplug a usb device that is already removed. +- Add USB Unplug option to USB management device management menu. +- Add enable and disable USB device functions to the menu. +- Add a [U]SB menu item, initially with just statistics per device, adding +device number to the device status window display. +- Reuse the cgpu temp entry for avalon and bitfury devices, changing avalon to a +damped value. +- Store the cointerra maximum temperature in the cgpu struct as an exponentially +changing value based on the maximum temperature. +- Reuse the cgpu->temp entry for max temperature in hfa driver. +- bab - disable chips that return only bad results +- Add driver for cointerra devices. +- Add Avalon2 (2U size machine) support +- miner.php - define a default rigport (that can be changed) and don't require a +port number in the rigs array +- miner.php allow links for rig buttons in tables and allow using the 4th IP +octet if no rig name - default disabled for both +- format fix and bad variable usage fix for --benchfile +- Allow running cgminer in benchmark mode with a work file --benchfile +- ANU frequency is in MHz, not hex. +- Remove bitfury devices from the usb list on shutdown in case they have stopped +responding but have not had a fatal usb error. + + +Version 3.12.3 - 8th February 2014 + +- Put the hashfast temperature into the cgpu structure so that it shows up in +the devs API call. +- We shouldn't block on no work situations directly from the getwork scheduler +itself. +- Revert "Make the pthread cond wait in the getwork scheduler a timed wait in +case we miss a wakeup." + + +Version 3.12.2 - 8th February 2014 + +- Adjust antminer U1 timing according to command line frequency set, fixing the +need for icarus timing on the command line. +- Read pipe errors that don't clear are worth attempting to reset the usb. +- Revert "Do away with usb resets entirely since we retry on both pipe and io +errors now and they're of dubious value." +- Make the pthread cond wait in the getwork scheduler a timed wait in case we +miss a wakeup. + + +Version 3.12.1 - 7th February 2014 + +- Document new features for antminer U1 and hfa devices. +- Add support for ANU overclocking. +- Increase hfa fanspeed by more if we're rising in temp above the target than if +the temp is staying the same. +- Add debug output when get_work() is blocked for an extended period and add +grace time to the device's last valid work to prevent false positives for device +failure. +- Issue a shutdown prior to a reset command for hfa devices and lock access to +reads awaiting the response if the device is already running. +- Do not register as successful a hfa init sequence that reports the clockrate +as zero. +- Show device info in noffset nonce share above target message. +- Widen lines in top menu to fit extra large share values. +- Only show one decimal place if pool diff is not an integer. +- Show serial number as a hex value in hfa verbose startup. +- Slowly remove work even if it's not being used to keep the getwork counter +incrementing even if work is not used and as a test that pools are still +working. +- Increase the maximum diff between hfa dies to 100Mhz. +- Show which hfa die is bringing down all the others when decreasing all the +clock speeds. +- Increase the decrease when temp has increased more and we want to decrease it +on hfa. +- Give device info with share above target message. +- Allow throttling of hfa dies more frequently and increasing of speeds less +frequently. +- Wait after sending a hfa shutdown to allow the device to properly shut down +before possibly sending it more commands. +- Minimise the die clock differences in hfa to no more than 50Mhz. +- Check for when errno is set on windows as well as the windows variant for +errors. +- Revert "Update to libusb-1.0.18" +- Disable fan/die clock control in hfa if the firmware does not support it, with +notification. +- Add ability to enter ANU frequency as a multiple of 25 from 150-500. +- Decrease hfa clock by 10 if a reset is attempted due to the device remaining +idle. +- ifdef out icarus options unused without icarus built in. +- Reorder command line options alphabetically. +- Add no matching work to hfa API output. +- Change various logging message levels in the hfa driver. +- Only adjust clocks if there is no restart in hfa to avoid 2 restarts back to +back. +- Ensure we iterate over all dies adjusting temperate for hfa by starting +iterating after the last die modified. +- Clamp initial hfa fanspeed to min/max if passed as parameters. +- Allow hfa fanspeed to be set via command line. +- Further relax the target temperatures on hfa driver, targetting 88 degrees. +- Try one more time to get the hfa header on init since it can take 2 seconds +for all 3 boards on a sierra. +- Update authors for removal of gpu/scrypt. +- Wait for 5 temperature updates in hfa before adjusting fanspeed. +- Have some leeway before starting to throttle hfa dies. +- Use increments of 10 when increasing hfa clock since it may not have 5 MHz +granularity internally. +- Only perform a hfa fan speed update if we have new temps to work with. +- Correctly measure the hfa max temp and smooth out the changes in its value. +- Choose better defaults for min/max/default fan settings for hfa driver. +- bab - reduce def speed, fix speed staying in ranges and report bank/chips in +ioctl() errors +- bab - add info about number of boards/chips to each Dead Chain +- These may not be longs (eg: OSX)... fo a safe cast to ensure. +- bab - add dead boards and dead chains to stats +- Add fanspeed to hfa api output and set initial fanspeed to 10% +- Add hfa fanspeed control to try and maintain a target temperature. +- API-README correct new text format documentation +- API allow multiple commands/replies in one request +- Add op commands necessary to control hfa fanspeeds. +- Add OP_FAN to hf protocol header. +- Always show the stratum share lag time in debug mode. +- Add stratum share response lag time to verbose output if it's greater than 1 +second. +- Add stratum share submission lag time to verbose information if it's over 1 +second. +- Check for more interrupted conditions in util.c and handle them gracefully. +- Send a ping to hfa devices if nothing is sent for over 5 seconds. +- Add OP_PING to hfa commands +- Display the hfa serial number as a hexadecimal value. +- Add the ability to display a hexadecimal 32 bit unsigned integer to the API. +- Limit all hfa restarts for temperature control to no closer than 15 seconds +apart. +- Allow the hfa temp target to be disabled by setting it to zero. +- Handle interruptions to various select calls in util.c +- Add sanity check for silly overflows in hfa die temperature readings. +- Add per-die throttling control for hfa driver based on each die's temperature, +issuing a suitable reset to maintain the temperature below a configurable target +temperature. +- Update hf protocol +- Do not memcpy in usbutils unless data was transferred. +- Send a full allotment of jobs to the hfa device after a restart instead of +reading the status. +- Export the flush_queue function for use by drivers. +- Remove wrong goto +- Remove the unqueued work reference when we discard work from get queued as +well. +- Wake the global work scheduler when we remove a work item from the unqueued +work pointer. +- Discard work that is stale in the get_queued() function, returning NULL +instead. +- Add a call to a driver specific zero stats function when zero stats is called +to allow each driver to reset its own stats as well if desired. + + +Version 3.12.0 - 29th January 2014 + +- Add support for AntminerU1 devices with the icarus driver. +- Add antminer U1 to comment in udev rules. +- Do away with usb resets entirely since we retry on both pipe and io errors now +and they're of dubious value. +- Retry on usb IO errors instead of faking success. +- Check that we've cleared the pipe error after a clear request, not the err +value which is unchanged. +- Update to libusb-1.0.18 +- Change hfa overheat limit to 90 degrees. +- Relax timeout in hf get header to 500ms to match the usb timeout. +- Minion - check/clear interrupts for all chips +- Set info work to null after it is freed in nf1 after a restart to prevent +double free later. +- The second_run bool in libbitfury should be per device. Microoptimise its and +job_switched usage, removing the unused results array for NF1 devices. +- Fix displayed diff when solo mining at >2^32 diff. +- bab - stop stale work accumulating +- bab - set the default SPI speed back to 96000 + + +Version 3.11.0 - 25th January 2014 + +- Add hashfast documentation to ASIC README +- Support the variable HFA naming throughout the driver notices. +- Set the global hfa hash clock rate to equal the lowest if we are lowering it +for a device reset since it may be re-hotplugged after failing reset. +- Decrease the hfa clock rate if it is overclocked and we have had to try +resetting it. +- Put a sanity check on the measured temperature in the hfa driver for obviously +wrong values. +- Avoid calling applog from within hfa statline before to avoid a deadlock. +- Add throttling control to hfa driver, configurable at command line, nominally +set to 85 degrees. +- Reset hfa device if no valid hashes are seen for 1 minute from the last work. +- Store when the last getwork was retrieved and display it in the API summary. +- bab - also report dead chip count screen +- Count share based hashes in the hfa driver with the device diff to get more +frequent updates. +- Only count 2/3 of the accumulated hashes on each pass through the hfa scan +work loop to smooth out displayed hashrate. +- bab add total history HW% to API stats +- Test valid nonces in the hashfast driver allowing us to check against the +target when trying to submit them. +- No point casting a double to a uint64 +- Convert the hfa hashmeter to one based on successful share return and display +the raw and calculated hash totals in the API. +- bab - remove libbitfury dependency since it requires USB +- Add description to hfa hash clock command. +- Add hfa board temperatures to API output. +- Wait for up to 0.5 seconds in the hashfast scanwork loop if no jobs are +required. +- Label HFA devices as B or S when their configuration matches babyjet or +sierra. +- Fix libbitfury being compiled in always by mistake. +- bab - spelling +- Add bab-options +- bab - tune the chip speed based on error rates +- bab record/report spie and miso errors +- Win32 falsely comes up as big endian pulling in the wrong hf protocol header. +- Remove unused components in hashfast driver. +- Check in all usb communication places for hashfast driver that the device +still exists. +- Do not send a usb reset on a usb read pipe error. +- Don't replace usb pipe errors with the pipe reset return code. +- Updated hf protocol header. +- The search for extra nonce is not worth performing in the hashfast driver. +- Add core address to hfa parse nonce debugging. +- Retry sending a frame once if it has failed in hfa_send_frame +- Add extra hfa usb init errors. +- Quiet now unused variable warning in hfa detect. +- Remove unused variable. +- Add board temperature to hfa debug +- Make submit_tested_work return a bool about whether it meets the work target +or not. +- Provide a helper function for determining dev runtime and use it in the +hashmeters used. +- Look for hfa usb init header for 2 seconds, then resend the init twice more +before failing. +- Really only set up the hfa crc table once. +- Generically increase the queue if we are mining on a pool without local work +generation each time we run out of work. +- Change new block detection message since longpoll is rarely relevant today. +- Change the default clockspeed bits on nanofury devices to 50 and add a command +line option to allow it to be changed. +- Use unused line at the top of the log window which often gets stuck +unchanging. +- Clear pool work on a stratum reconnect message. +- bab record/report spie and miso errors +- bab - cleanup old work for dead chips also +- bab add avg fail tests to API stats +- bab report bank/board/chip for dead and v.slow chips +- bab process all nonce replies per chip together +- bab reduce work delays +- bab record the number of E0s discarded +- bab - modified result parsing +- bab restore removed unused flag +- configure - correct minion name +- bab only scan valid nonce offsets +- bab record continuous (and max) bad nonces +- bab display Banks/Boards/Chips in the device window +- Modify thread naming to make them easier to identify +- bab reduce the work send delay +- bab remove results polling +- bab report SPI wait in seconds +- bab report missing chips at start and API +- bab ensure there's enough space for the nonce reply +- bab correct stats 'Send Max' +- bab allow long enough wait on ioctl() per board +- bab more I/O stats +- api.c 2014 +- api allow any size stats data +- bab add processed links which excludes expired links skipped +- bab report chips per bank, hw% and ghs per chip +- bab lock access to new_nonces to ensure correct reporting +- bab report V2 banks/boards during initialisation +- bab expire chip work +- bab use only k_lists and make work handling more refined +- klist - allow adding to tail +- bab remove old unused #define +- bab correct for master git +- correct klist reallocs +- klist lists for bab +- api.c correct DEVICECODE and ordering +- Maxchips should be 384 (16 chips/board 24 boards/controller) +- bab more detailed stats and delay less when waiting for a buffer +- api add data type AVG float 3 decimal +- bab - add V2 detect with bug fix in detect +- api.c set the actual version number to 3.0 +- API V3.0 unlimited socket reply size +- README update --usb +- Check for loss of device in usb read before any other code on the usbdev +- Change stratum strings under stratum_lock in reconnect and free old strings. +- Add mcp2210 compilation to want_libbitfury configs. +- Fix HF driver typo. + + +Version 3.10.0 - 9th January 2014 + +- Set the mcp2210 transfer setting only when it changes. +- Buffer sizes in nanofury device data are unnecessarily large. +- Only perform spi reset on init, not with each transaction. +- Remove spi_detect_bitfury at nanofury startup and fix incorrect refresh time. +- Use a simple serialised work model for nanofury +- Use bitfury_checkresults to avoid hashing results twice in nanofury. +- Export bitfury_checkresults in libbitfury +- Pass extra parameters for later use in libbitfury_sendHashData +- Avoid double handling bswap of the nonce value in nanofury +- Avoid unnecessary rehashing in nanofury nonce checking. +- Remove the unused portions of atrvec in the nanofury driver +- Age work in nf1_scan to avoid risk of losing a work item and leaking memory. +- bitfury_work_to_payload is double handling the data unnecessarily +- Default bitrate on nanofury should be 200kHz +- localvec should be only 80 bytes not 80 words +- Wrong init value for nanofury +- Remove unused rehash values from nanofury driver. +- Only update info work in nanofury driver when it's empty. +- Fill the appropriate type of usb transfer when we know if it's an interrupt +transfer instead of a bulk one. +- Use the internal knowledge of the usb epinfo to determine whether we should be +doing an interrupt instead of a bulk transfer, and do not send a ZLP if so, and +limit read transfer to expected size automatically. +- Avoid bin2hex memleak when we start getting nanofury nonces +- Set atrvec only once and use a local array for each device's work. +- Cancel any spi transfers on nf1 close +- Add bitfury detection loop to nanofury startup +- Move spi init code to libbitfury +- Remove inappropriate extra config reg in nanofury setup. +- Status 0x30 should never happen with spi transfers. +- Fix spi transfer data size transmission mistakes. +- Minor correctness change in spi_add_data +- spi_txrx should always send and receive the same size message +- Random libbitfury changes. +- Set value of gpio pins to low on closing nanofury. +- Fix more init sequence for nanofury. +- Add basic initialisation for nf1 devices +- Add basic nf1_scan function. +- Basic import of libbitfury functions from nanofury branch +- Import functions from nanofury fork for libbitfury +- Meter out spi sends to only 2 bytes at a time, offsetting according to how +much data returns. +- Use the usb read limit function for mcp2210 reads. +- Provide a way for usb reads to just read the size asked for with a limit bool. +- Get pin value after an nf1 spi reset. +- Make sure what we send in the buffer doesn't change during spi reset for +nanofury +- Remove all standalone gpio setting change functions in mcp2210 and just use +the one global setting function. +- Set gpio values in the one function with all values for nanofury. +- Provide a helper function for setting all mcp2210 gpio settings. +- Add a helper function for getting all mcp2210 gpio settings. +- Set all pin designations and directions in one call for nanofury and don't +bother storing their values in the info struct. +- Provide helper functions for setting all pins and dirs on mcp2210 +- Set all nanofury pin designations in one call +- Provide a helper function for setting all pin designations on mcp2210 +- Store the spi settings in a struct for nanofury devices. +- Check the received status in mcp2210 spi transfers and repeat a zero byte send +if it's in progress. +- Set the bytes per spi transfer prior to each mcp2210 transfer. +- Separate out the send and receive functions for mcp2210 and check response +value in return. +- Check that mcp2210 spi settings have taken and check the value of the pin +during nanofury setup. +- Don't set GPIO pin designations after initial setting in nanofury since the +direction and values will be changed. +- Provide an mcp 2210 set gpio input helper function that sets a pin to gpio and +input. +- Move the set gpio output function to a generic mcp2210 version from nanofury +which also sets the pin to gpio. +- Implement a nanofury txrx with a larger buffer and cycling over data too large +to send. +- Implement magic spi reset sequence for nanofury. +- Add more spi magic to the nanofury init sequence. +- Add lots of magic spi initialisation to nanofury. +- Export reused components of bitfury management into a libbitfury and use for +bab and bitfury drivers. +- More init sequence for nanofury and implement a close function that sets all +pins to input. +- Reword offset header handling in hfa_get_header +- Sanity check in hfa_get_header +- Add more checks in hashfast driver for lost devices. +- Change spimode and send more data in nanofury setup. +- Add basic setup comms to nanofury. +- Implement an mcp2210 spi transfer function. +- Set the initial spi settings for nanofury driver. +- Provide a helper function for gettings mcp2210 spi settings. +- Implement an mcp2210 set spi transfer settings function. +- Cancel any SPI transfers in progress in nanofury after initial setup. +- Implement an mcp2210 spi cancel function. +- Return only binary values for mcp2210 GPIO values. +- Set GPIO LED and power to high in nanofury driver. +- Implement initial part of nanofury init sequence for GPIO pin settings and add +output debugging of set values. +- Add helper functions for getting and setting mcp2210 gpio pin designations. +- Don't return an error in usb read if we've managed to get the whole read +length we've asked for. +- Use correct endpoint order for nanofury devices and read with a short timeout +on return loop from send_recv. +- Add mcp2210 helper functions for getting and setting one GPIO pin val and +direction. +- Create a generic gpio pin struct and add helpers for mcp get pin val and dirs. +- Check the receive msg of a send/receive cycle on mcp2210 matches the send +message. +- Add a set of usb commands to the usbutils defines for mcp2210 comms, and use +the same command name for send and receive. +- Create a generic mcp2210 send_rcv function. +- Include mcp header for bitfury and fix extra params in macro. +- Add basic SPI comms defines for mcp2210 and build rules for bitfury. +- Minion set some core defaults similar to final requirements +- minion compile warnings +- move driver-minion.c to main directory +- Minion with ioctl() stats, settings to attempt to emulate 21TH/s +- minion driver with results interrupt working +- tested working driver-minion.c without interrupts +- Working driver-minion.c v0.1 +- driver-minion.c compilable untested +- minion driver - incomplete +- Add minion driver into cgminer +- Add basic device detection and updated udev rules for nanofury devices. +- Remove GPU from share logging example. +- Don't keep resetting BXF clockspeed to default. +- If no pools are active on startup wait 60s before trying to reconnect since we +likely have the wrong credentials rather than all the pools being out. +- Discard bad crc packets for hashfast driver instead of trying to process them. +- Update documentation for modified avalon options syntax and document relevant +55nm details. +- Modify the auto tuning sequence to work with the 50MHz changes required to +work with 55nm Avalon. +- 55nm avalon requires the delays between writes reinstated for stability. +- Use an equation instead of a lookup table to set the frequency for 55nm avalon +allowing arbitrary values to be used. +- Make the result return rate low detection on avalon less trigger happy. +- Always send the bxf device a clockspeed after parsing the temperature in case +the device has changed the clockspeed itself without notification. +- Fix BXF being inappropriately dependent on drillbit. + + +Version 3.9.0 - 23rd December 2013 + +- drillbit asic - enable in api.c +- Fix trivial warnings in knc driver. +- Reinstate work utility based hashmeter for knc. +- drillbit format %z not valid on windows +- drillbit more formatting changes +- usbutils remove old code added back +- Memset the spi tx buffer under lock in knc driver. +- drillbit fix temp display to fit in standard space +- Drillbit formatting +- drillbit - use one drvlog and display dname before add_cgpu +- Keep orginal naming for the bitfury driver +- knc: Bugfix - good shares wrongly reported as HW errors. Root cause of the +problem: several work items were assigned the same work_id in the active works +queue of the knc driver. Thus when good nonce report arrived from the FPGA, +wrong work item was picked up from the queue, and submit_nonce evaluated that +as an error. Fix: Limit the work_id counter update rate. Update it only to the +number of works actually consumed by the FPGA, not to the number of works +send. +- Store per-chip submit information for bxf device and show them in the API. +- Check for removed bxf devices before trying to update work or send messages. +- api.c no decref if not json +- Minimise risk of nonce2 overflow with small nonce2 lengths by always encoding +the work little endian, and increasing the maximum size of nonce2 to 8 bytes. +- Change default hashfast timeout to 500ms. +- Ensure we can look up the work item in the hashfast driver or print out an +error if we don't. +- Drillbit source formatting - reindent and retabify +- Add ASIC count, temperature status to drillbit API output (closes #1) +- Many warning fixes +- knc: Do not include variable "last minute" data into the "last hour" per-core +stats +- knc: Make per-core statistics available through API +- Implement command line control of the bxf target temperature. +- Add a simple PID-like controller to bi*fury devices to dynamically alter the +clock setting to maintain a nominal target temperature set to 82 degrees. +- Add data to BXF API output. +- Add support for newer protocol bi*fury commands job, clock and hwerror, +setting clock to default 54 value, turning parsing into a compact macro. +- Look for the thermal overload flag in the gwq status message in the hashfast +driver and send it a shutdown followed by an attempted reset. +- Log message fixups +- Fix for "Timing out unresponsive ASIC" for pools which send early reconnect +requests, and then take a short time to send work (ie BTCGuild) +- Shorten initial config line, win32/pdcurses doesn't like long lines during +early logging +- Pull back the very long timeouts set in fe478953cf50 +- Fix bug where work restart during results scan could lead to bad device state +- Align device status lines same regardless of number of temp status or >10 +ASICs +- Tag log lines from brand new devices as DRB-1 until they are initialised +- Tag log lines as 'DRB0' rather than 'DRB 0', same as other places in cgminer +- Print a summary of the device settings at level NOTICE during initialisation +- Allow chosing device settings based on 'short' product names shown in status +line +- Allow per-device settings to use "DRBnn" as an identifier instead +- Issue an ASIC restart during a work_restart, removes spurious timeout messages +from ASICs and probably some rejected shares +- Check all results against all work instead of just taking the first match +(avoids some rejected submissions to the pool, ASIC can produce multiple +candidate results.) +- Fix memory leak caused by unnecesarily copied work +- Fix bug with find_settings not returning default value +- Set timeouts on write, set very long timeouts +- Merge drillbit driver + + +Version 3.8.5 - 10th December 2013 + +- Increase the BFLSC overtemp to 75 for fanspeed to maximum. +- Set bflsc cutoff temperature to 85 degrees and throttle 3 degrees below the +cutoff temperature. +- Only set LIBUSB_TRANSFER_ADD_ZERO_PACKET for libusb versions we know include +support for. +- Provide a helper function that can reset cgsems to zero. +- Add to cgminer_CPPFLAGS instead of redefining them. +- Attempt a libusb reset device on usb devices that have stopped responding. +- Replace deprecated use of INCLUDES with _CPPFLAGS. +- Remove more unused GPU code. +- Attempt USB device resets on usb read/write errors that will normally cause +the device to drop out. +- Quieten down jansson component of build. +- Cache the bool value for usb1.1 in _usb_write +- Initialise usb locks within usbutils.c instead of exporting them. +- Imitate a transaction translator for all usb1.1 device writes to compensate +for variable quality hubs and operating system support. +- Rationalise variables passed to usb_bulk_transfer. +- Unlink files opened as semaphores on releasing them. +- Remove user configuration flag from pll bypass enabling in hashfast driver. +- Provide an hfa-dfu-boot option for resetting hashfast devices for +reprogramming. +- Fixed one byte stack overflow in mcast recvfrom. +- Having changed C_MAX means we don't calloc enough for usb stats, off by one. +- Don't free the info struct on hashfast shutdown since it's still accessed +after a device is removed. + + +Version 3.8.4 - 1st December 2013 + +- Deprecate the usb usecps function and just split up transfers equal to the +maxpacketsize on usb1.1 devices. +- Retry sending after successfully clearing a pipe error. +- Drop logging of timeout overrun message to verbose level. +- Use a much longer callback timeout for USB writes on windows only as a last +resort since cancellations work so poorly. +- Use vcc2 in bflsc voltage displayed. +- Increment per core errors on false nonces in bflsc and add per core statistics +to api stats, removing debugging. +- Store a per-core nonce and hw error count for bflsc. +- Fix json parsing in api.c +- Add debugging to hfa driver for how many jobs are being sent. +- Shut down the hfa read thread if the device disappears. +- Add debug output saying what frame command is being sent in hfa driver. +- Revert "Disable USB stats which were not meant to be enabled by default and +add extra memory for a memory error when stats are enabled." +- Reset work restart flag in hfa driver since we may check for it again in +restart_wait. +- Add more op usb init errors for hfa driver. +- Perform basic displaying of hfa notices received. +- Add hfa op usb notice macros. +- Update hf protocol header. +- Use sync usb transfers in lowmem mode. +- Go back to allowing timeout errors on USB writes to be passed back to the +driver without removing the device in case the driver wishes to manage them. +- Initialise more values for the hfa data structures. +- A USB control error must be < 0 +- Simplify USB NODEV error checking to success only for writes and control +transfers, and success and timeout for reads. +- libusb error IO should be fatal as well if it gets through usb read and write. +- Allow IO errors in usb reads/writes to be ignored up to retry max times. +- Use correct padding for bxf temperature display. +- Initialise devices before attempting to connect to pools to allow their thread +prepare function to be called before having to connect to pools. +- Add hidden hfa options to set hash clock, group ntime roll and pll bypass, +fixing frame sent on reset to include extra data. +- Relax the timeouts for the slower usb devices on linux. +- Add big endian hf protocol header to Makefile +- Check for correct big endian macro in hf_protocol +- Use an absolute timeout in hfa_get_header to cope with buffered usb reads +returning instantly confusing the 200ms counter. +- Update hfa_detect_one to use the new detect function API. + + +Version 3.8.3 - 23rd November 2013 + +- Set the bitfury device start times from when we first get valid work. +- Fix stack corruption of zeroing too much in bf1 driver. +- Make usb_detect return the cgpu associated with it to check if it succeeds to +decide on whether to increment the device count or not. +- Set tv work start time for bxf driver. +- Age the bxf work items over 90 seconds, not the bf1 work items. +- Zero the read buffer in _usb_read to avoid stale data and only use stack +memory instead of using the bulkbuf since it is only used in _usb_read. +- Leave room for temperatures above 100 degrees and pad consistently for bxf +statline. +- Drop json stratum auth failed message log level to verbose. +- Change the processed value not the bufsiz in response to an end of message +marker. +- Don't lose data beyond the end of message in a usb read. +- Silence irrelevant warning. +- Only check strlen on end if end exists. +- Simplify the end of message detection in _usb_read and allow it to return +without doing another read if the message is already in the buffer. +- Increase work ageing time to 90 seconds for bxf driver to account for firmware +changes. +- Use the age_queued_work function in the bitfury driver. +- Provide a function to discard queued work based on age. +- The json_val in api.c is a borrowed reference, not a new one so don't decref +it. +- Decrement json references in api.c to not leak memory. +- line 2913 added urlencode +- With reliable writes to the avalon there is no need for the sleep delays +between writes. +- There is no need to limit usb write transfers to maxpacketsize and it's +harmful for large transfers on slow devices such as wrt routers. +- Disable USB stats which were not meant to be enabled by default and add extra +memory for a memory error when stats are enabled. +- Set limit and count to integers to not overflow during failed hotplug attempts +and then not trying again. +- Update api example compilation instructions. + + +Version 3.8.2 - 16th November 2013 + +- Add more verbose documentation to the readme files for windows users. +- Add more information on libusb failure to init telling users to check README +file. +- Add information on unloading cdc drivers on osx to README +- Prevent a deadlock with use of restart_threads by spawning a thread to send +the driver flush work messages. +- Set priority of various threads if possible. +- Add bxf data to api output. +- Do not hold the mining thread lock in restart_threads when calling the driver +flush work commands. +- Send extra work regularly to the bxf device and parse the needwork command by +sending the amount of work it requests. +- Allow messages to have arbitrary offsets in the bxf parser in case we have +lingering buffered data. +- Send the maxroll command to the bxf driver and store the value to see if we +need to update it. +- Add sending of flush command to bxf on flush_work +- Add flush and version commands to bxf start up, flush buffer and try to parse +version response string. +- Abstract out bxf recv message. +- Add extra bxf commands to usbutils +- Abstract out bxf send message to allow us to easily add extra commands. +- Don't run device restart code if the device is not enabled. +- Expand size of bitfury statline +- Various driver fixes for bitfury devices, including a flag from when first +valid work appears. +- Look up work results in bxf driver from correct variable. +- Correct incorrect error code in bxf driver for usb writes and add debugging. +- Add bxf details to usbutils. +- Implement a statline showing temperature for bxf +- Add api data for bxf device, sharing the hashrate function with bf1. +- Count no matching work as a hw error on bxf +- Add BXF to udev rules. +- Work id should be hexadecimal in bxf messages. +- Add unrecognised string debugging to bxf driver. +- Implement the main scanloop for bxf, trying to prevent it from ntime rolling +work if the work protocol does not allow it. +- Parse bxf work submits fully, submitting the results. +- Provide a function for setting the work ntime. +- Implement a skeleton parse bxf submit function. +- Use the bxf read thread to set the device target and send its first work item. +- Implement a bxf send work function and set update and restart functions to +sending new work since that's the equivalent for that device. +- Add temperature parsing to bxf driver +- Create and destroy a basic bxf read thread. +- Remove the buffer from bitfury info since it is only used on one pass in the +bf1 device. +- Add a rudimentary bxf detect one function. +- Rename all bf1 specific functions in the bitfury driver, using a switch to +choose correct function. +- Rename bitfury_getinfo to bf1_getinfo since it's unique to bf1 devices. +- Separate out the bf1 reset from bitfury reset. +- Store the bitfury identity in the info struct. +- BaB - updated tested OS comment +- Uniquely identify the BF1 and BXF bitfury devices. +- Remove the default libusb WinUsb pipe policies that don't suit us. +- Only set the winusb pipe policy if it doesn't match our requirements instead +of every transfer. +- klondike - dont try to flush if not initialised +- api.c trylock() add missing locklock +- Use our new zero length packet support directly in windows. +- Enable support for zero length packet on windows and auto clear pipe stalls. +- util.c: Decreasing reference count on allocated JSON obects to prevent memory +leak +- api.c: Release apisock on error in api() +- api.c: Release io_data->ptr when releasing io_data in io_free() +- We can't connect to a GBT pool at all with fix protocol enabled. +- Initialise the stgd lock mutex earlier to prevent dereferences when pool +testing occurs before it. +- Klondike support I2C USB layout also - as KLI +- Return error codes in avalon_read() if they're not timeouts. +- Break out of the avalon idle loop if we get a send error. +- Set avalon ftdi latency to just less than the time it would take to fill the +ftdi buffer at 115200 baud +- Update example.conf +- Only limit packetsize on usb out writes. +- We must chop up every 64 bytes returned on an ftdi chip, not just the first 2 +bytes so revert to parsing the data internally in the avalon instead of using +usbutils' simple ftdi parser. +- Only retry 3 times in hfa_reset. +- Only add_cgpu in hashfast driver once we have a real driver set up. +- Clean up properly if hfa_detect_common fails in the hashfast driver. +- --shares should be scaled to diff1 not absolute number of shares + + +Version 3.8.1 - 11th November 2013 + +- Revert "Send a zero length packet at the end of every usb transfer on windows +in case libusb internally has batched them into one maxpacket sized." + + +Version 3.8.0 - 10th November 2013 + +- api update version to 2.0 and remove GPU form API-README +-Remove now unused scrypt files. +- api.c remove all GPU/gpu references and correct code as required +- Rudimentary removal of GPU OpenCL and Scrypt features from api.c +- Reorder configure alphabetically for devices to compile and fail if no support +is selected to be compiled in. +- BaB update/format some comments +- BlackArrowBitfury early GPIO V1 driver +- Fine tune the reading of results in bitfury driver to not lose any across work +restarts or corrupt due to store results not parsed during restart. +- Send a zero length packet at the end of every usb transfer on windows in case +libusb internally has batched them into one maxpacket sized. +- Framework for ntime rolling, keep looking for OP_USB_INIT replies when other +packets received +- Configure source for a new BaB driver +- sha2 allow external access to some macros and the K array +- Fixed a math issue when reporting fan speed on the status line. +- Use the main hashlist to store work done in the bitfury driver and remove work +from the list by time, thereby fixing the duplicates at startup. Count hardware +errors for when no match occurs. +- Add a get and queue helper work function. +- Remove GPU mining code. +- Use libusb's own zero length packet support unless we have to emulate it on +windows since only libusb knows for sure if it's needed. +- Unlock the avalon qlock while sending tasks to not hold the lock for an +extended period. +- Sleep in avalon send task on return to the function to allow other code to +work during the sleep period. +- Send zero length packets when terminating a usb write aligned to +maxpacketsize. +- Do the driver flush in avalon code lockless since it can lead to deadlocks. +- Reset the work_restart bool after the scanwork loop in case the driver flushes +work synchronously. +- Only check for the stratum clean message if we have had a valid message. +- Get rid of the stage thread since all work can be asynchronously added now via +hash_push anyway. +- Remove the now incorrect faq entry regarding scrypt difficulty. +- Check for fatal read errors and break out of the read loop in avalon. +- Send errors are basically fatal in avalon driver so break out of the send +tasks loop. +- Make the avalon driver return -1 for hash count when usb fails, allowing the +main loop code to send it the shutdown flag. +- Break out of the hash work loops when a failure is detected instead of +dropping into mt disable. +- Use usbutils' own ftdi parser for avalon and the ftdir's own latency for +managing timeouts since we can wait on reads with completely asynchronous +reads+writes. +- Use usbutils' own cps function for slowing rate of usb writes on avalon. +- Fix build for no libcurl +- Check length before submitting sync transfers + + +Version 3.7.2 - 5th November 2013 + +- Clean up completely on avalon shutdown. +- Use cgsem timed waits in avalon driver to not miss any queued wake ups to +account for async messages coming during a flush work. +- Statline before is too long on icarus that doesn't have monitoring. +- Different windows+usb combinations respond with varying levels of reliability +wrt timeouts so use a nominal extra 40ms before cancelling transfers that fail +to time out on their own. +- Do all hotplug_process under the write mining_thr_lock +- Fix for opt_worktime on big endian machines. +- Correct set_blockdiff for big endian machines. +- Make sure cgpu exists in the restart threads loop in cases of hotplug etc. +- Treat usb write timeout errors as unrecoverable. +- Transfer errors are filtered out in usbutils now so no need to look for them +in NODEV checks. +- Remove now unused entries from struct cg_usb_device +- Do not double up with checking for end of timeout measurements in usb +read/write. +- Do get_work in fill_queue without holding other locks. +- Initialise usb after all the locks and conditionals are initialised. +- Use only a trylock in flush queue to prevent deadlocks. +- Add a wr_trylock wrapper for pthread rw lock write trylock. +- Scale diff for scrypt when testing for block solves. +- Fix for non curses build. + + +Version 3.7.0 - 4th November 2013 + +- Use WRITEIOERR macro check for all usb writes. +- Always use a usb read buffer instead of having to explicitly enable it. +- Force unlocking of the console lock on restart to avoid corrupting the console +state when we finally quit. +- Never wait indefinitely for a pthread conditional in the hash_pop loop in case +the work scheduler misses the last wakeup. +- Make hash_pop signal the work scheduler each time it waits on the conditional +that it should look for more work. +- Discriminate between libusb transfer errors and regular libusb errors and make +sure to capture them all. +- Always read a full sized transfer for bulk reads. +- Deprecate preferred packet size functions in usbutils since they're unhelpful. +- Copy known transferred amount back to buffer for usb reads instead of +requested length. +- Treat timeout errors on usb writes as IO errors. +- Ignore iManufacturer from bitfury devices to support bluefury as well as +redfury. +- Add more debugging info for when usb details don't match. +- Look for timeout overruns in usb read/write. +- Use an int for usb_read/write to identify overruns. +- Use the callback timeout as a safety mechanism only on windows. +- Instead of using complicated sleeps to emulate characters per second on usb +writes, submit only as many characters as can be transferred per usb poll of +1ms, and use timeouts in bulk transfers, cancelling transfers only as a +failsafe. +- Remove discarded work from quota used. +- Display works completed in summary and API data. +- Store how many work items are worked on per pool. +- Make each pool store its on reference for what the most current block is and +fine tune management of block change in shared pool failover strategies using +the information. +- Rationalise use of current_hash to a single hex string the length of the +previous block and display only the first non zero hex chars of the block in the +status window. +- Update uthash to latest. +- show_hash doesn't know the size of the string so hard code the max size. +- Remove as many initial zeroes as exist on share display, abstracting out a +hash show function to use across different submission mechanisms. +- Add missing endian swap functions for 64bits. +- Sanity check for absurd target setting and divide by zero. +- Abstract out conversion of a 256 bit endian number to a double, correcting +errors and use it for determining any magnitude share diff. +- Avoid the extra generation of a byte flipped hash2 in struct work and directly +use the LE work hash. +- Add a sanity check to avoid divide by zero crashes in set_target +- Calculate diff from target accurately for all 256 bits. +- Set a true 256bit binary target based on any diff value in set_target() +- Provide a copy_work_noffset function for copying a work struct but changing +its ntime. +- Make calls to flush queue and flush work asynchronous wrt to the main work +loops. +- Share is also above target for submit noffset nonce. +- Use round for displaying current pool diff. +- Use round for stratum share diff display instead of floor. +- Use round instead of floor for displayed pool difficulty. +- Allow arbitrary diffs to be tested against nonces via a test_nonce_diff +function. +- Abstract out the rebuilding of hash2 in work. +- Share is above, not below target, when it doesn't meet it. +- Add the ability to add uint8 and uint16 entities to api data. +- Use a non blocking connect with a 1 second select timeout when initiating +stratum to allow us to iterate over all IPs returned by getaddrinfo in round +robin DNS pools. +- Minor style changes to output. +- Revert two different hash_sequence(_head)'s to one variable, use +HF_SEQUENCE_DISTANCE in both places +- Remove duplicate HF_SEQUENCE_DISTANCE() macro, and duplicate hash_sequence +from info structure +- Change SEQUENCE_DISTANCE() macro to HF_SEQUENCE_DISTANCE() +- Structure changes for OP_NONCE, add big endian header +- klondike - initialise stat_lock +- klondike - better to unlock locks than to lock them twice :) +- Add copyright notice to knc driver. +- Trivial style changes to knc driver. +- Improve performance of work generation by optimizing hex2bin and bin2hex +- klondike - change options to clock and temptarget only +- klondike - fix another uninit dev warning +- klondike - downgrade 'late update' but add an idle detect - and correct error +levels +- klondike - fix isc uninit warning +- Use a mutex to protect data in the knc structure, to prevent loading more work +during a flush, and unlock and return to main between calls to get_queued_work. +- Use the existing device_data for knc state data. +- Only count successful nonces as hashrate in the knc driver. +- Fix trivial warnings in knc driver. +- Add KNC to api +- klondike - drop the device for hotplug if it's unresponsive +- usbutils - usb_nodev() allow a driver to drop a device +- klondike - single 'shutdown' and ensure it happens +- klondike remove SCNu8 - unsupported on windows +- Correctly calculate sleep_estimate in usbutils that may have been preventing +usecps from working. +- Use a sanity check on timeout on windows. +- Better HW error count; disable permanently those cores which fail often +- KnC driver: knc-spi-fpga ASIC driver +- Fixup jansson & libusb include paths when using separate build directory +- 'llround' is more suitable here than 'roundl' +- Silence warning if MAX/MIN is already defined +- Remove prebuild ccan/opt dependencies +- Reinstate block solve testing. +- Dramatically simplify the calculation of blockdiff. +- Simplify the set_target function, allowing it to work properly for fractional +diffs. +- Merge hashfast driver +- Merge KnC driver + + +Version 3.6.6 - 26th October 2013 + +- Remove inappropriate extra locking in _usb_transfer_read + + +Version 3.6.5 - 26th October 2013 + +- klondike - fix uninitialised dev bug +- Adjust the binary ntime data in submit_noffset_nonce even when there is no hex +ntime string for eg. gbt. +- Put an entry into the work struct telling drivers how much they can roll the +ntime themselves. +- Only set libusb cancellable status if the transfer succeeds. +- Remove the applog on miner threads dying to prevent deadlocks on exit. +- Do one extra guaranteed libusb event handling before testing if there are any +pending async usb transfers. +- Use a linked list for all usb transfers instead of just cancellable ones. +- Provide a mechanism for informing drivers of updated work templates for +stratum and gbt mining. +- Add cancellable transfers correctly to the ct_list +- Check for presence of thr in icarus get nonce for startup nonce testing to +work. +- Use cancellable usb transfers in the icarus driver to avoid having to loop and +poll when waiting for a response and to speed up work restart response time. +- Add a usb_read_ii_timeout_cancellable wrapper +- Add usb transfer cancellation on shutdown and documentation regarding where +cancellable transfers are suitable. +- Use cancellable transfers on bitfury device. +- Cancel cancellable usb transfers on work restart messages. +- Don't bother having a separate cancellable transfer struct for usb transfers, +simply include the list in the usb_transfer struct. +- Add wrappers for usb_read_cancellable and usb_read_timeout_cancellable +- Specifically set the cancellable state for it to not be uninitialised in the +usb transfer struct. +- Alter the usb cancellable list only under cgusb_fd_lock write lock. +- Pass the cancellable option to _usb_read options to decide on whether to add +usb transfers to the list of cancellable transfers. +- Create a linked list of potentially cancellable usb transfers. +- Don't attempt to disable curses or print a summary during an app restart to +prevent deadlocks. +- Keep the libusb event handle polling thread active until there are no async +usb transfers in progress. +- Keep a global counter of how many async usb transfers are in place. +- Perform libusb_submit_transfer under the write variant of cgusb_fd_lock +- klondike - error condition handling +- Avoid entering static libusb directory if --with-system-libusb is enabled. +- Minor opencl build corrections. +- Enable dynamic linking against system libusb --with-system-libusb +- Modify Makefile to only include opencl related code when configured in. +- Convert opencl to need to be explicitly enabled during build with +--enable-opencl +- Implement a cglock_destroy function. +- Implement a rwlock_destroy function. +- Implement a mutex_destroy function. +- Add usb command name to critical libusb error reporting. +- Use windows' own higher resolution time and handlers allowing us to have +higher precision absolute timeouts. +- Fix lldiv error in windows cgminer_t calculation. +- miner.php correct sort gen field names largest to smallest +- api ... the code related to device elapsed +- api add device elapsed since hotplug devices Elapsed is less than cgminer +Elapsed +- Drop usb buffering message to debug logging level. +- Do the ntime binary modification to the work struct when submitting an ntime +offset nonce within submit_noffset_nonce +- Code cleanup and improved documentation +- Improvements to support for BitBurner boards +- Convert libusb transfer errors to regular libusb error messages to allow for +accurate message reporting. + + +Version 3.6.4 - 18th October 2013 + +- Fixing the memory leak for remaining semaphores means we can go back to using +async transfers on other OSes with our own timeout management again. +- Use the forcelog function on shutdown to cope with indeterminate console lock +states due to killing of threads. +- Add a forcelog variant of applog which invalidates any console lock to force +output. +- Send pthread_cancel to failed completion_timeout that has timed out. +- Simplify queued hashtable by storing unqueued work separately in a single +pointer. +- bflsc use getinfo chip parallelization if it is present +- bflsc - fix brackets so [Chips] isn't always null +- Remove unused variables. +- Use cgcompletion timeouts for the unreliable shutdown functions on kill_work. +- Fix cgcompletion return code and free on successful completion. +- Provide a cg_completion_timeout helper function for unreliable functions that +takes arbitrary functions and parameters and reliably returns. +- Perform sync transfers on shutdown to allow final transfers to complete. +- Destroy cgsems used after transfers to not leave open files on osx. +- klondike rewrite work control +- allow __work_complete() access +- miner.h allow devices to tv_stamp work + + +Version 3.6.3 - 17th October 2013 + +- API add 'MHS %ds' to 'summary' +- Optional lock tracking and stats via the API +- Speed up polling repeat again in usb poll thread and handle async after the +message to disable polling is complete. +- Revert to using timeouts on !linux since libusb leaks memory without them. +- Revert to libusb instead of libusbx + + +Version 3.6.2 - 17th October 2013 + +- Remove unused components of jansson +- Remove unused parts of libusb +- Work around older libtoolize that fails without top ltmain.sh not being +present during autogen +- Fix open coded use of autoreconf in autogen +- Update jansson to only build parts we require and suited to our build +environment. +- Initial import of jansson-2.5 +- Prevent further USB transfers from occurring once the shutdown signal has been +sent to prevent transfers getting stuck and libusb failing to shut down. +- Make the USB polling thread poll every second to potentially aid longer +timeout transfers. +- Set device_diff on work in get_work to not be missed with drivers that use +get_work directly. +- Convert icarus driver to hash_driver_work model. +- bflsc - also allow ' 0' in DEVICES IN CHAIN +- bflsc - allow a 0 in DEVICES IN CHAIN +- Add needed EXTRA_DIST for libusbx. +- Update libusbx configure.ac changes. +- Revert libusb Makefile changes from going to libusbx. +- Fix trivial libusbx warnings. +- Convert libusb-1.0.16-rc10 to libusbx-1.0.17 + + +Version 3.6.1 - 14th October 2013 + +- Emulate the libusb_control_transfer sync setup in our async variant. +- usbutils - make all libusb_error_name messages the same + + +Version 3.6.0 - 14th October 2013 + +- increasing max miners for avalon driver +- using separate identifier for bitburner fury boards +- changes to bitburner driver for bitburner fury boards +- hexstr is too small in test_work_current +- Windows uses errno for WSAETIMEDOUT +- Convert the usb callback function to using cgsem_t timed waits to avoid race +conditions with conditionals/mutexes. +- Give correct return code in cgsem_mswait +- Check for correct timeout error in cgsem_mswait +- Fix util.h exports for cgsem_mswait +- Implement a generic cgsem_mswait similar to sem_timedwait +- Use the one LIBUSB_ERROR_TIMEOUT for cancelled transactions since this error +is explicitly tested for in various drivers. +- Do not use locking on usb callback function pthread signalling to prevent +deadlock with libusb's own event lock. +- Use a write lock when performing any USB control transfers to prevent +concurrent transfers. +- Free a libusb transfer after we have finished using it to avoid a dereference +in usb_control_transfer +- Do not perform bfi int patching for opencl1.2 or later. +- Although async transfers are meant to use heap memory, we never return before +the transfer function has completed so stack memory will suffice for control +transfers, fixing a memory leak in the process. +- klondike - correct/reverse min/max stats +- api incorrect message name +- klondike - use a link list queue rather than a circular buffer - and add +timing stats +- Use a timeout with usb handle events set to a nominal 200ms and wait for the +polling thread to shut down before deinitialising libusb. +- Use stack memory for hex used in stratum share submissions. +- Use stack memory in test_work_current, avoiding a malloc/free cycle each time. +- Provide a lower level __bin2hex function that does not allocate memory itself. +- Convert the bitfury driver to use the hash_driver_work version of hash_work. +- Add a hash_driver_work function to allow for drivers that wish to do their own +work queueing and management. +- Convert all usb control transfers to asynchronous communication with our own +timeout management as well. +- Klondike - increase circular read buffer size +- Klondike - extra zero value and range checking in temp conversion +- klondike - display MHz also +- Make pthread conditional timeouts handle all bulk usb transfer timeouts +performing libusb_cancel_transfer, disabling timeouts within libusb itself. +- Avoid calling get_statline_before on exit to avoid trying to use it on drivers +in an indeterminate state. +- Avoid calling get_statline on exit. +- Add a small amount to the usb timeout before cancelling to allow for a regular +usb polling interval to pass. +- Do not attempt to clear a usb halt before sending the cancel message since all +transfers should normally be cancelled before attempting to clear a halt +condition, and only change the return message to a timeout if it's consistent +with a cancellation. +- Retry up to USB_RETRY_MAX times to clear a halt condition before failing. +- Show the error number as well as the description in erroring bulk transfers. +- Drop logging level for failed to connect to stratum to verbose mode only since +we hit it regularly. +- We are always dependent on libusb handling events so use the blocking +libusb_handle_events in the polling thread and use a bool to know if we should +continue polling. +- Use fractional hashrate return values in bitfury_scanhash to minimise the +number of times we return 0 based on hashrate so far to further damp out +displayed hashrate. +- Check for presence of driver name in DRIVER_COUNT_FOUND to prevent strcmp on a +null pointer when a driver is not built in. +- CMR allow sending flash and clock commands +- Kill off threads that have failed using hash_sole_work instead of just +disabling them. +- Make the bf1 getinfo size a macro +- Failing to add_cgpu in bitfury should be a terminal failure. +- Check return values when attempting to open a BF1 device and set the msg size +as a macro. +- Display errors on failed usb read and write and consider sequential IO errors +a permanent failure. +- Use libusb's own error name function instead of hand coding the error names. +- Limit ms_tdiff to 1 hour as a sanity check. +- Enable the usb buffer in avalon driver. +- Check for async transfer variants of error messages. +- Remove unused variables. +- Try switching pools if for some reason we end up with only idle pools and have +ended up current_pool set to an idle one. +- Check a pool is stable for >5 mins before switching back to it. +- Minimise the time between dropping the read devlock and grabbing the write +devlock to avoid tons of logging spam in the interim. +- Check for libusb transfer stall error to be consistent with async IO errors +returned for a halt condition. +- Check for continuous IO errors on USB and consider the device inactive if more +than retry max. +- Make the devlock a cglock in usbutils and only grab the write lock for +fundamental changes allowing us to send and receive transfers concurrently +without lock contention. +- Prevent overflows in us_tdiff and ms_tdiff. +- Change second initialise message on bitfury verbose mode. +- Submitting an ntime offset nonce needs to be done on a copy of the work +instead of the original so abstract out shared components as much as possible, +minimising strdups in copy_work and make submit_work_async work take copied +work, cleaning up code in the process. +- Provide a way for drivers to submit work that it has internally rolled the +ntime value by returning the amount it has ntime rolled to be added. +- Typo in configure.ac +- Remove unmaintained broken ztex driver. +- Icarus - use a data structure for I/O rather than magic numbers +- delete old tracked ccan/opt/*.o files +- klondike correct cvtKlnToC() temperature calculation +- klondike - correct 1st reply debug based on define +- klondike - debug dump structured replies +- klondike - avoid division by zero if maxcount is unexpectedly zero +- klondike store and report errorcount and noise +- klondike - fix chipstats api stats buffer overrun with 16 chips +- klondike add new nonecount only once +- klondike - report mh/s based on nonces found + put old estimate into API stats +- klondike use a memcpy +- klondike fix bracket tabs indenting +- api.c missing Klondike from ASIC list +- Klondike update code to current git +- Add 2nd CMR to 01-cgminer.rules +- Add Klondike to 01-cgminer.rules +- Klondike to main directory +- Klondike consistent code spacing +- Klondike update driver code to current git +- update firmware for 16 chips, add dist files +- beta final 0.3.0 release +- updated firmware, IOC method +- prevent nonces when not state W +- added driver config option support +- fixes for 300 MHz, fix K1 parts list +- update driver, docs +- update firmware & utils +- updated cgminer driver for 3.3.1 +- update firmware and driver, create new cgminer fork +- update klondike driver +- add cgminer driver file as-is +- Add API output displaying USB cancellations. +- Store statistics on how often we have to cancel async bulk transfers and add a +debug message whenever we do. +- Treat any unexpected timeouts waiting for async transfers as though there may +be a usb halt condition and attempt to clear the halt before cancelling the +tranfer. +- Remove zero packet flag on usb as it's unsupported outside linux and +unnecessary. +- Fake the libusb transfer timed out message if we force cancel it with our own +async functions. +- Use asynchronous transfers for all bulk transfers, allowing us to use our own +timers and cancelling transfers that take too long. +- Add libusb error warning message when significant error occurs. +- Icarus CMR2 detect FPGA setup +- Disable bitfury device thread on it disappearing. + + +Version 3.5.0 - 29th September 2013 + +- Add magic init sequence required on BF1 devices to get them mining on windows. +- usbinfo.devlock is only ever write locked so convert it to a mutex +- Icarus remove unneeded opt_debug tests due to applog being a macro +- Icarus - CMR shouldn't wait the full timeout due to handle sharing +- We should only yield once in cg_wunlock +- Provide a function to downgrade a cglock from a write lock to an intermediate +variant. +- Deuglify use of _PARSE_COMMANDS macro expansions. +- Deuglify use of usb parse commands macro in usbutils. +- Use the driver add commands macros in api.c to avoid individually listing +them. +- Separate out asic fpga and opencl drivers in the driver parse commands macro +for use individually as needed. +- Use macro expansion in usb_find_devices to avoid explicitly listing them all. +- Use macro expansion to iterate over all the drivers without explicitly writing +them out in usbutils.c +- Iterate over the bitfury offsets in order of decreasing likelihood. +- Reattach the kernel driver on linux on usb_uninit. +- Attach the kernel driver on failure to usb init on linux. +- libusb kernel driver operations are only available on linux. +- There is no need to get the external prototypes for drivers in cgminer.c any +more. +- Remove unnecessary gpu_threads initialisation. +- Put avalon last in the sequence of adding drivers to prevent it trying to +claim similar chip devices on startup. +- Use macro expansion to iterate over all device drivers without needing to +explicitly code in support in all places. Pass a hotplug bool to the detect() +function to prevent opencl trying to hogplug GPUs. +- Forward declare all device drivers in miner.h avoiding the need to export them +everywhere else. +- Add a noop function for driver detect when it's missing. +- Reuse the DRIVER_ macros to avoid having yet another definition for DRV_ +- Use macro expansion to generate extern device_drv prototypes. +- Create a macro list of drivers to enable easier addition of further drivers. +- There is no point setting the BF1 preferred packet size to the maximum since +it will do so automatically. +- icarus ensure all cmr interfaces are initialised properly +- usbutils - fix USBDEBUG warnings +- Remove unnecessary steps in communicating with BF1 and just use USB interface +1. +- usbutils - usb_bulk_transfer fix the buf/data fix +- usb_bulk_transfer - use the allocated buffer +- Set preferred packet sizes per interface on BF1. +- usbutils allow PrefPacketSize per endpoint +- Remove magic control sequences on open/close on BF1 and just flush the read +buffers. +- Check return codes in getinfo and reset and fail as needed in BF1. +- Check return code for bitfury_open and release resources properly on failed +initialisation. +- Abstract out flushing of interrupt reads in BF1 devices. +- Perform interrupt read after close message on BF1 as per serial close. +- Perform interrupt read flush as per serial open on BF1 devices. +- Add information for 2nd USB interface on BF1 devices and choose interface 1 +for bulk transfers. +- usbutils - bulk transfer copy test fix +- usbutils - add USBDEBUG for usb_bulk_transfer +- Add more read_ii variants to usbutils. +- Name remainder of BFU usb commands used. +- Use submit_tested_work in bitfury driver to avoid unnecessarily re-testing the +work for validity. +- Abstract out work submission once it's been tested, to be used by drivers that +do their own internal validity testing. +- Store the hash2 array in struct work for further reuse. +- usbutils - which_intinfo not requried +- Use the test_nonce function within submit_nonce and store the uint32 +corresponding to hash2 37 for further use. +- usbutils - interfaces must all be on one handle - ep implies the interface +- avalon stats use exact type +- Only set share diff if we've confirmed it's a share first. +- Update ASIC-README for bitfury devices. +- Use an array of offsets when checking nonces in bitfury_checkresults +- Limit the duration we wait for reads in BF1 based on time already elapsed to +account for other delays such as work restart messages or out of work. +- Minimise size of serial string we copy in BF1 stats to avoid overflow. +- Implement basic API stats for BF1 and increase array of results to check for +the rare straggling result. +- Space debug output for bf1 to separate from numerals. +- Abstract out the bitfury open close and reset functions and use them on +reinit. +- Rename BF1 devices BF1 +- Check for work restart, breaking out early after usb reads in BF1. +- Do not lose the first sets of results from BF1. +- There is no point checking for results from the next round of work on BF1. +- Last result returned by BF1 is an end of results marker so ignore it. +- restart_wait should return 0 if thr_restart is true. +- Remove unused code by bitfury driver since current driver uses serialised +scanhash. +- Meter out return of estimated hashes in BF1 to smooth out visible hashrate. +- Optimise inner scanhash loop for bf1. +- Add yet another backup work for triple buffering of work in bf1 to account for +extra late results returned and don't check nonce offsets which appear to never +return. +- Name the work request and result usb commands for BF1 +- Define a mandatory upper limit to waiting for reset and data on BF1 based on +full nonce duration. +- Decrease usb buffering to verbose logging. +- Add in first draft for a serialised work model sending/receiving data for BF1 +devices. +- Add complete close sequence to bf1 as it happens on serial. +- Provide a bitfury identify function for bf1. +- Reliably extract BF1 information at startup and reset the device. +- Add commands for getting BF1 bitfury info +- Add magic BF1 bitfury open and close control sequences. +- Add BF1 detection code to bitfury driver. +- Create basic placeholders for bitfury driver code. +- Add bf1 device information to usbutils to enable device detection. +- Add basic defines for building for bitfury devices. +- Add redfury device to udev rules. +- avalon: display the FPGA controller version on API +- pool_active uninitialised_var rolltime +- Use macro expansion to only need to define usb enums and commands in one +place. +- usbutils saving incorrect overflow buffer +- ignore libusb.la and *.lo on linux +- icarus support CMR with no extensions +- usbtils - interfaces dont work yet in libusb windows so disable for that only +- Provide a --disable-libcurl config option to build support for stratum mining +only. +- Fix the api-example.c compile under Linux +- usbutils - only release the device once - for the first intinfo +- usbutils set_interface is no longer valid +- ubsutils interfaces much each have their own handle +- usbutils kernel_detach should use the interface number +- usbutils - allow the driver to change which_intinfo +- Reset quotas on load balance for all pools at the same time to avoid running +out during selection and unintentionally dropping to fallback. +- Break out of select pool from a common point for appropriate debug messages +and to avoid further tests. +- usbutils correct/reverse CMR product numbers +- usbutils specifically track handles and interfaces +- change drivers to use usb_interface() - required for multi interface change +- usbutils - allow a device to use multiple interfaces (and better var names) +- Cast -1 to (char) to cope with different default char types on ARM. + + +Version 3.4.3 - 13th September 2013 + +- Put corefoundation and iokit separate in ldflags for darwin. +- Add rules for libusb Makefile.am building on osx +- Add flags for building libusb statically on osx. +- Find the greatest common denominator in quotas and use the smallest number of +consecutive work items per pool in quota load balance mode to smooth hashrate +across pools with large quotas. Give excess quota to priority pool 0 instead of +pool 0. +- Avoid dynamically adding stack memory for nonce2 in the stratum send thread +and check the pool's nonce2_len will not cause an overflow. +- Add subdir-objects to automake options. +- Use inet_addr instead of inet_network to fix windows build. +- Remove unused pbase variable. +- Add support for socks4/4a proxies with stratum, and drop back to socks4 +support via the global --socks-proxy command to not break previous +configurations. +- Fix warning on mingw build. +- Only show long-poll message in pool summary if it's not using stratum. +- Increase the time for the waiting for work message to be given to be greater +than that required for a pool swap in the scheduler which is set to 5s. +- Change message in status when using a balanced pool strategy to notify if +there's a stratum pool as well. +- Use the --failover-only flag to have special meaning in combination with +load-balance mode to distribute any unused quota back to pool 0 to maintain +ratios amongst other pools. +- Display quota and allow it to be modified via the pool menu. +- Add API commands and modify output to support pool quota displaying and +changing. +- Change message in status when using a balanced pool strategy to notify if +there's a stratum pool as well. +- Add quota support to configuration files. +- Rotate pools on all failures to set a pool in select_pool. +- Use quotas for load-balance pool strategy. +- Provide a mechanism for setting a pool quota to be used by load-balance. +- Use the --socks-proxy option with stratum, changing it to defaulting to socks5 +and give appropriate message should it fail to connect. +- Cope with trailing slashes in stratum urls. +- Add more debugging messages when negotiating with proxies for stratum. +- Test specifically for socks5h in socks support for stratum. +- Add support for socks5 proxy with stratum +- Provide support for negotiating a stratum connection via http proxies. +- Connect to the proxy URL and port if specified for stratum sockets instead of +the pool directly. +- Extract any proxy url and port to be used by sockaddr if possible using +extract_sockaddr. +- Make extract_sockaddr set variables passed to it rather than pool struct +members. +- miner.php sort the mcast rigs so they are always in the same relative order +- miner.php allow sending the muticast message multiple times +- miner.php mcast ignore duplicate replies + + Version 3.4.2 - 3rd September 2013 - take_queued_work_bymidstate should use a write lock. diff --git a/README b/README index 06bd3c825c..91a21706e6 100644 --- a/README +++ b/README @@ -1,6 +1,4 @@ -This is a multi-threaded multi-pool GPU, FPGA and ASIC miner with ATI GPU -monitoring, (over)clocking and fanspeed support for bitcoin and derivative -coins. Do not use on multiple block chains at the same time! +This is a multi-threaded multi-pool FPGA and ASIC miner for bitcoin. This code is provided entirely free of charge by the programmer in his spare time so donations would be greatly appreciated. Please consider donating to the @@ -9,6 +7,11 @@ address below. Con Kolivas 15qSxP1SQcUX3o4nhkfdbgyoWEFMomJ4rZ +NOTE: This code is licensed under the GPLv3. This means that the source to any +modifications you make to this code MUST be provided by law if you distribute +modified binaries. See COPYING for details. + + DOWNLOADS: http://ck.kolivas.org/apps/cgminer @@ -25,24 +28,12 @@ IRC Channel: irc://irc.freenode.net/cgminer -License: GPLv3. See COPYING for details. - -SEE ALSO API-README, ASIC-README, FGPA-README, GPU-README AND SCRYPT-README FOR -MORE INFORMATION ON EACH. +SEE ALSO API-README, ASIC-README and FGPA-README FOR MORE INFORMATION ON EACH. --- EXECUTIVE SUMMARY ON USAGE: -After saving configuration from the menu, you do not need to give cgminer any -arguments and it will load your configuration. - -Any configuration file may also contain a single - "include" : "filename" -to recursively include another configuration file. -Writing the configuration will save all settings from all files in the output. - - Single pool: cgminer -o http://pool:port -u username -p password @@ -51,11 +42,11 @@ Multiple pools: cgminer -o http://pool1:port -u pool1username -p pool1password -o http://pool2:port -u pool2usernmae -p pool2password -Single pool with a standard http proxy, regular desktop: +Single pool with a standard http proxy: cgminer -o "http:proxy:port|http://pool:port" -u username -p password -Single pool with a socks5 proxy, regular desktop: +Single pool with a socks5 proxy: cgminer -o "socks5:proxy:port|http://pool:port" -u username -p password @@ -63,6 +54,10 @@ Single pool with stratum protocol support: cgminer -o stratum+tcp://pool:port -u username -p password +Solo mining to local bitcoind: + +cgminer -o http://localhost:8332 -u username -p password --btc-address 15qSxP1SQcUX3o4nhkfdbgyoWEFMomJ4rZ + The list of proxy types are: http: standard http 1.1 proxy http0: http 1.0 proxy @@ -77,134 +72,250 @@ not be available. All are available since CURL version 7.19.4 If you specify the --socks-proxy option to cgminer, it will only be applied to all pools that don't specify their own proxy setting like above + +After saving configuration from the menu, you do not need to give cgminer any +arguments and it will load your configuration. + +Any configuration file may also contain a single + "include" : "filename" +to recursively include another configuration file. +Writing the configuration will save all settings from all files in the output. + + --- BUILDING CGMINER FOR YOURSELF DEPENDENCIES: Mandatory: - curl dev library http://curl.haxx.se/libcurl/ - (libcurl4-openssl-dev) - pkg-config http://www.freedesktop.org/wiki/Software/pkg-config libtool http://www.gnu.org/software/libtool/ Optional: + curl dev library http://curl.haxx.se/libcurl/ + (libcurl4-openssl-dev - Must tell configure --disable-libcurl otherwise + it will attempt to compile it in) + curses dev library (libncurses5-dev or libpdcurses on WIN32 for text user interface) - AMD APP SDK http://developer.amd.com/sdks/AMDAPPSDK - (This sdk is mandatory for GPU mining) - - AMD ADL SDK http://developer.amd.com/sdks/ADLSDK - (This sdk is mandatory for ATI GPU monitoring & clocking) - libudev dev library (libudev-dev) - (This is only required for ASIC+FPGA support and is linux only) + (This is only required for USB device support and is linux only) If building from git: autoconf automake +If building on Red Hat: + sudo yum install autoconf automake autoreconf libtool openssl-compat-bitcoin-devel.x86_64 \ + curl libcurl libcurl-devel openssh + +If building on Ubuntu: + sudo apt-get install build-essential autoconf automake libtool pkg-config \ + libcurl3-dev libudev-dev CGMiner specific configuration options: - --disable-opencl Override detection and disable building with opencl - --disable-adl Override detection and disable building with adl - --enable-bflsc Compile support for BFL ASICs (default disabled) - --enable-bitforce Compile support for BitForce FPGAs(default disabled) - --enable-icarus Compile support for Icarus bitstream FPGAs(default disabled) - --enable-modminer Compile support for ModMiner FPGAs(default disabled) - --enable-ztex Compile support for Ztex Board(default disabled) - --enable-avalon Compile support for Avalon (default disabled) - --enable-scrypt Compile support for scrypt litecoin mining (default disabled) - --without-curses Compile support for curses TUI (default enabled) + --enable-bmsc Compile support for BitMain Single Chain(default disabled) + --enable-bitmain Compile support for BitMain Multi Chain(default disabled) + --enable-avalon Compile support for Avalon (default disabled) + --enable-avalon2 Compile support for Avalon2 (default disabled) + --enable-avalon4 Compile support for Avalon4 (default disabled) + --enable-bab Compile support for BlackArrow Bitfury (default + disabled) + --enable-bflsc Compile support for BFL ASICs (default disabled) + --enable-bitforce Compile support for BitForce FPGAs (default + disabled) + --enable-bitfury Compile support for BitFury ASICs (default disabled) + --enable-bitmine_A1 Compile support for Bitmine.ch A1 ASICs (default + disabled) + --enable-blockerupter Compile support for ASICMINER BlockErupter Tube/Prisma + (default disabled) + --enable-cointerra Compile support for Cointerra ASICs (default disabled) + --enable-drillbit Compile support for Drillbit BitFury ASICs (default + disabled) + --enable-hashfast Compile support for Hashfast (default disabled) + --enable-icarus Compile support for Icarus (default disabled) + --enable-klondike Compile support for Klondike (default disabled) + --enable-knc Compile support for KnC miners (default disabled) + --enable-minion Compile support for Minion BlackArrow ASIC (default + disabled) + --enable-modminer Compile support for ModMiner FPGAs(default disabled) + --enable-sp10 Compile support for Spondoolies SP10 (default + disabled) + --enable-sp30 Compile support for Spondoolies SP30 (default + disabled) + --disable-libcurl Disable building with libcurl for getwork and GBT + support + --without-curses Compile support for curses TUI (default enabled) + --with-system-libusb Compile against dynamic system libusb (default use + included static libusb) Basic *nix build instructions: To actually build: ./autogen.sh # only needed if building from git repo CFLAGS="-O2 -Wall -march=native" ./configure + make No installation is necessary. You may run cgminer from the build directory directly, but you may do make install if you wish to install cgminer to a system location or location you specified. -Native WIN32 build instructions: see windows-build.txt +Building for windows: + +It is actually easiest to build a windows binary using cross compilation tools +provided by "mxe" available at http://mxe.cc/ (use the 32 bit one!) +Once you have followed the instructions for building mxe: + export PATH=(path/to/mxe)/usr/bin/:$PATH + CFLAGS="-O2 -Wall -W -march=i686" ./configure --host=i686-pc-mingw32 + make + +Native WIN32 build instructions: see windows-build.txt but these instructions +are now hopelessly out of date. --- Usage instructions: Run "cgminer --help" to see options: -Usage: . [-atDdGCgIKklmpPQqrRsTouvwOchnV] +Usage: cgminer [-DdElmpPQqUsTouOchnV] + Options for both config file and command line: ---api-allow Allow API access (if enabled) only to the given list of [W:]IP[/Prefix] address[/subnets] - This overrides --api-network and you must specify 127.0.0.1 if it is required - W: in front of the IP address gives that address privileged access to all api commands ---api-description Description placed in the API status header (default: cgminer version) ---api-groups API one letter groups G:cmd:cmd[,P:cmd:*...] - See API-README for usage ---api-listen Listen for API requests (default: disabled) - By default any command that does not just display data returns access denied - See --api-allow to overcome this ---api-network Allow API (if enabled) to listen on/for any address (default: only 127.0.0.1) ---api-mcast Enable API Multicast listener, (default: disabled) - The listener will only run if the API is also enabled ---api-mcast-addr API Multicast listen address, (default: 224.0.0.75) ---api-mcast-code Code expected in the API Multicast message, don't use '-' (default: "FTW") ---api-mcast-port API Multicast listen port, (default: 4028) ---api-port Port number of miner API (default: 4028) ---auto-fan Automatically adjust all GPU fan speeds to maintain a target temperature ---auto-gpu Automatically adjust all GPU engine clock speeds to maintain a target temperature +--anu-freq Set AntminerU1/2 frequency in MHz, range 125-500 (default: 250.0) +--api-allow Allow API access only to the given list of [G:]IP[/Prefix] addresses[/subnets] +--api-description Description placed in the API status header, default: cgminer version +--api-groups API one letter groups G:cmd:cmd[,P:cmd:*...] defining the cmds a groups can use +--api-listen Enable API, default: disabled +--api-mcast Enable API Multicast listener, default: disabled +--api-mcast-addr API Multicast listen address +--api-mcast-code Code expected in the API Multicast message, don't use '-' +--api-mcast-des Description appended to the API Multicast reply, default: '' +--api-mcast-port API Multicast listen port (default: 4028) +--api-network Allow API (if enabled) to listen on/for any address, default: only 127.0.0.1 +--api-port Port number of miner API (default: 4028) +--au3-freq Set AntminerU3 frequency in MHz, range 100-250 (default: 225.0) +--au3-volt Set AntminerU3 voltage in mv, range 725-850, 0 to not set (default: 750) +--avalon-auto Adjust avalon overclock frequency dynamically for best hashrate +--avalon-cutoff Set avalon overheat cut off temperature (default: 60) +--avalon-fan Set fanspeed percentage for avalon, single value or range (default: 20-100) +--avalon-freq Set frequency range for avalon-auto, single value or range +--avalon-options Set avalon options baud:miners:asic:timeout:freq:tech +--avalon-temp Set avalon target temperature (default: 50) +--avalon2-freq Set frequency range for Avalon2, single value or range +--avalon2-voltage Set Avalon2 core voltage, in millivolts +--avalon2-fan Set Avalon2 target fan speed +--avalon2-cutoff Set Avalon2 overheat cut off temperature (default: 88) +--avalon2-fixed-speed Set Avalon2 fan to fixed speed +--avalon4-automatic-voltage Automatic adjust voltage base on module DH +--avalon4-voltage Set Avalon4 core voltage, in millivolts, step: 125 +--avalon4-freq Set frequency for Avalon4, 1 to 3 values, example: 445:385:370 +--avalon4-fan Set Avalon4 target fan speed range +--avalon4-temp Set Avalon4 target temperature (default: 42) +--avalon4-cutoff Set Avalon4 overheat cut off temperature (default: 65) +--avalon4-polling-delay Set Avalon4 polling delay value (ms) (default: 20) +--avalon4-ntime-offset Set Avalon4 MM ntime rolling max offset (default: 4) +--avalon4-aucspeed Set Avalon4 AUC IIC bus speed (default: 400000) +--avalon4-aucxdelay Set Avalon4 AUC IIC xfer read delay, 4800 ~= 1ms (default: 9600) +--bab-options Set BaB options max:def:min:up:down:hz:delay:trf --balance Change multipool strategy from failover to even share balance +--benchfile Run cgminer in benchmark mode using a work file - produces no shares +--benchfile-display Display each benchfile nonce found --benchmark Run cgminer in benchmark mode - produces no shares +--bet-clk Set clockspeed of ASICMINER Tube/Prisma to (arg+1)*10MHz (default: 23) +--bfl-range Use nonce range on bitforce devices if supported +--bflsc-overheat Set overheat temperature where BFLSC devices throttle, 0 to disable (default: 85) +--bitburner-fury-voltage Set BitBurner Fury core voltage, in millivolts +--bitburner-fury-options Override avalon-options for BitBurner Fury boards baud:miners:asic:timeout:freq +--bitburner-voltage Set BitBurner (Avalon) core voltage, in millivolts +--bitmain-auto Adjust bitmain overclock frequency dynamically for best hashrate +--bitmain-cutoff Set bitmain overheat cut off temperature +--bitmain-fan Set fanspeed percentage for bitmain, single value or range (default: 20-100) +--bitmain-freq Set frequency range for bitmain-auto, single value or range +--bitmain-hwerror Set bitmain device detect hardware error +--bitmain-options Set bitmain options baud:miners:asic:timeout:freq +--bitmain-temp Set bitmain target temperature +--bxf-bits Set max BXF/HXF bits for overclocking (default: 54) +--bxf-temp-target Set target temperature for BXF/HXF devices (default: 82) +--bxm-bits Set BXM bits for overclocking (default: 54) +--btc-address Set bitcoin target address when solo mining to bitcoind +--btc-sig Set signature to add to coinbase when solo mining (optional) --compact Use compact display without per device statistics --debug|-D Enable debug output ---device|-d Select device to use, one value, range and/or comma separated (e.g. 0-2,4) default: all --disable-rejecting Automatically disable pools that continually reject shares +--drillbit-options Set drillbit options :clock[:clock_divider][:voltage] --expiry|-E Upper bound on how many seconds after getting work we consider a share from it stale (default: 120) --failover-only Don't leak work to backup pools when primary pool is lagging --fix-protocol Do not redirect to a different getwork protocol (eg. stratum) ---hotplug Set hotplug check time to seconds (0=never default: 5) - only with libusb ---kernel-path|-K Specify a path to where bitstream and kernel files are (default: "/usr/local/bin") ---load-balance Change multipool strategy from failover to efficiency based balance +--hfa-hash-clock Set hashfast clock speed (default: 550) +--hfa-fail-drop Set how many MHz to drop clockspeed each failure on an overlocked hashfast device (default: 10) +--hfa-fan Set fanspeed percentage for hashfast, single value or range (default: 10-85) +--hfa-name Set a unique name for a single hashfast device specified with --usb or the first device found +--hfa-noshed Disable hashfast dynamic core disabling feature +--hfa-options Set hashfast options name:clock (comma separated) +--hfa-temp-overheat Set the hashfast overheat throttling temperature (default: 95) +--hfa-temp-target Set the hashfast target temperature (0 to disable) (default: 88) +--hro-freq Set the hashratio clock frequency (default: 280) +--hotplug Seconds between hotplug checks (0 means never check) +--klondike-options Set klondike options clock:temptarget +--load-balance Change multipool strategy from failover to quota based balance --log|-l Interval in seconds between log output (default: 5) --lowmem Minimise caching of shares for low memory applications +--minion-chipreport Seconds to report chip 5min hashrate, range 0-100 (default: 0=disabled) +--minion-freq Set minion chip frequencies in MHz, single value or comma list, range 100-1400 (default: 1200) +--minion-freqchange Millisecond total time to do frequency changes (default: 1000) +--minion-freqpercent Percentage to use when starting up a chip (default: 70%) +--minion-idlecount Report when IdleCount is >0 or changes +--minion-ledcount Turn off led when more than this many chips below the ledlimit (default: 0) +--minion-ledlimit Turn off led when chips GHs are below this (default: 90) +--minion-noautofreq Disable automatic frequency adjustment +--minion-overheat Enable directly halting any chip when the status exceeds 100C +--minion-spidelay Add a delay in microseconds after each SPI I/O +--minion-spireset SPI regular reset: iNNN for I/O count or sNNN for seconds - 0 means none +--minion-spisleep Sleep time in milliseconds when doing an SPI reset +--minion-temp Set minion chip temperature threshold, single value or comma list, range 120-160 (default: 135C) --monitor|-m Use custom pipe cmd for output messages +--nfu-bits Set nanofury bits for overclocking, range 32-63 (default: 50) --net-delay Impose small delays in networking to not overload slow routers --no-submit-stale Don't submit shares if they are detected as stale +--osm-led-mode Set LED mode for OneStringMiner devices (default: 4) --pass|-p Password for bitcoin JSON-RPC server --per-device-stats Force verbose mode and output per-device statistics --protocol-dump|-P Verbose dump of protocol-level activities ---queue|-Q Minimum number of work items to have queued (0 - 10) (default: 1) +--queue|-Q Minimum number of work items to have queued (0+) (default: 1) --quiet|-q Disable logging output, display status and errors +--quota|-U quota;URL combination for server with load-balance strategy quotas --real-quiet Disable all output ---remove-disabled Remove disabled devices entirely, as if they didn't exist +--rock-freq Set RockMiner frequency in MHz, range 200-400 (default: 270) --rotate Change multipool strategy from failover to regularly rotate at N minutes (default: 0) --round-robin Change multipool strategy from failover to round robin on failure ---scan-time|-s Upper bound on time spent scanning current work, in seconds (default: 60) +--scan-time|-s Upper bound on time spent scanning current work, in seconds (default: -1) --sched-start Set a time of day in HH:MM to start mining (a once off without a stop time) --sched-stop Set a time of day in HH:MM to stop mining (will quit without a start time) ---scrypt Use the scrypt algorithm for mining (litecoin only) --sharelog Append share log to file --shares Quit after mining N shares (default: unlimited) ---socks-proxy Set socks4 proxy (host:port) for all pools without a proxy specified +--socks-proxy Set socks4 proxy (host:port) +--suggest-diff Suggest miner difficulty for pool to user (default: none) --syslog Use system log for output messages (default: standard error) --temp-cutoff Temperature where a device will be automatically disabled, one value or comma separated list (default: 95) --text-only|-T Disable ncurses formatted screen output --url|-o URL for bitcoin JSON-RPC server +--usb USB device selection --user|-u Username for bitcoin JSON-RPC server ---verbose Log verbose output to stderr as well as status output --userpass|-O Username:Password pair for bitcoin JSON-RPC server +--verbose Log verbose output to stderr as well as status output +--widescreen Use extra wide display without toggling +--worktime Display extra work time debug information Options for command line only: --config|-c Load a JSON-format configuration file See example.conf for an example configuration. +--default-config Specify the filename of the default config file +Loaded at start and used when saving without a name. --help|-h Print this message +--ndevs|-n Display all USB devices and exit --version|-V Display version and exit -USB device (ASIC and FPGA) options: +Silent USB device (ASIC and FPGA) options: --icarus-options Set specific FPGA board configurations - one set of values for all or comma separated --icarus-timing Set how the Icarus timing is calculated - one setting/value for all or comma separated ---usb USB device selection (See below) --usb-dump (See FPGA-README) See FGPA-README or ASIC-README for more information regarding these. @@ -212,14 +323,48 @@ See FGPA-README or ASIC-README for more information regarding these. ASIC only options: +--anu-freq Set AntminerU1/2 frequency in MHz, range 125-500 (default: 250.0) +--au3-freq Set AntminerU3 frequency in MHz, range 100-250 (default: 225.0) +--au3-volt Set AntminerU3 voltage in mv, range 725-850, 0 to not set (default: 750) --avalon-auto Adjust avalon overclock frequency dynamically for best hashrate +--avalon-cutoff Set avalon overheat cut off temperature (default: 60) --avalon-fan Set fanspeed percentage for avalon, single value or range (default: 20-100) --avalon-freq Set frequency range for avalon-auto, single value or range ---avalon-cutoff Set avalon overheat cut off temperature (default: 60) ---avalon-options Set avalon options baud:miners:asic:timeout:freq +--avalon-options Set avalon options baud:miners:asic:timeout:freq:tech --avalon-temp Set avalon target temperature (default: 50) +--avalon2-freq Set frequency range for Avalon2, single value or range +--avalon2-voltage Set Avalon2 core voltage, in millivolts +--avalon2-fan Set Avalon2 target fan speed +--avalon2-cutoff Set Avalon2 overheat cut off temperature (default: 88) +--avalon2-fixed-speed Set Avalon2 fan to fixed speed +--avalon4-automatic-voltage Automatic adjust voltage base on module DH +--avalon4-voltage Set Avalon4 core voltage, in millivolts, step: 125 +--avalon4-freq Set frequency for Avalon4, 1 to 3 values, example: 445:385:370 +--avalon4-fan Set Avalon4 target fan speed range +--avalon4-temp Set Avalon4 target temperature (default: 42) +--avalon4-cutoff Set Avalon4 overheat cut off temperature (default: 65) +--avalon4-polling-delay Set Avalon4 polling delay value (ms) (default: 20) +--avalon4-ntime-offset Set Avalon4 MM ntime rolling max offset (default: 4) +--avalon4-aucspeed Set Avalon4 AUC IIC bus speed (default: 400000) +--avalon4-aucxdelay Set Avalon4 AUC IIC xfer read delay, 4800 ~= 1ms (default: 9600) +--bab-options Set BaB options max:def:min:up:down:hz:delay:trf --bflsc-overheat Set overheat temperature where BFLSC devices throttle, 0 to disable (default: 90) ---bitburner-voltage Set BitBurner core voltage, in millivolts +--bitburner-fury-options Override avalon-options for BitBurner Fury boards baud:miners:asic:timeout:freq +--bitburner-fury-voltage Set BitBurner Fury core voltage, in millivolts +--bitburner-voltage Set BitBurner (Avalon) core voltage, in millivolts +--bitmine-a1-options ::: +--bxf-temp-target Set target temperature for BXF devices (default: 82) +--bxm-bits Set BXM bits for overclocking (default: 50) +--hfa-hash-clock Set hashfast clock speed (default: 550) +--hfa-fail-drop Set how many MHz to drop clockspeed each failure on an overlocked hashfast device (default: 10) +--hfa-fan Set fanspeed percentage for hashfast, single value or range (default: 10-85) +--hfa-name Set a unique name for a single hashfast device specified with --usb or the first device found +--hfa-noshed Disable hashfast dynamic core disabling feature +--hfa-temp-overheat Set the hashfast overheat throttling temperature (default: 95) +--hfa-temp-target Set the hashfast target temperature (0 to disable) (default: 88) +--hro-freq Set the hashratio clock frequency (default: 280) +--klondike-options Set klondike options clock:temptarget +--rock-freq Set RockMiner frequency in MHz, range 125-500 (default: 270) See ASIC-README for more information regarding these. @@ -231,46 +376,13 @@ FPGA only options: See FGPA-README for more information regarding this. -GPU only options: - ---auto-fan Automatically adjust all GPU fan speeds to maintain a target temperature ---auto-gpu Automatically adjust all GPU engine clock speeds to maintain a target temperature ---disable-gpu|-G Disable GPU mining even if suitable devices exist ---gpu-threads|-g Number of threads per GPU (1 - 10) (default: 2) ---gpu-dyninterval Set the refresh interval in ms for GPUs using dynamic intensity (default: 7) ---gpu-engine GPU engine (over)clock range in Mhz - one value, range and/or comma separated list (e.g. 850-900,900,750-850) ---gpu-fan GPU fan percentage range - one value, range and/or comma separated list (e.g. 25-85,85,65) ---gpu-map Map OpenCL to ADL device order manually, paired CSV (e.g. 1:0,2:1 maps OpenCL 1 to ADL 0, 2 to 1) ---gpu-memclock Set the GPU memory (over)clock in Mhz - one value for all or separate by commas for per card. ---gpu-memdiff Set a fixed difference in clock speed between the GPU and memory in auto-gpu mode ---gpu-powertune Set the GPU powertune percentage - one value for all or separate by commas for per card. ---gpu-reorder Attempt to reorder GPU devices according to PCI Bus ID ---gpu-vddc Set the GPU voltage in Volts - one value for all or separate by commas for per card. ---intensity|-I Intensity of GPU scanning (d or -10 -> 10, default: d to maintain desktop interactivity) ---kernel|-k Override kernel to use (diablo, poclbm, phatk or diakgcn) - one value or comma separated ---ndevs|-n Enumerate number of detected GPUs and exit ---no-restart Do not attempt to restart GPUs that hang ---temp-hysteresis Set how much the temperature can fluctuate outside limits when automanaging speeds (default: 3) ---temp-overheat Overheat temperature when automatically managing fan and GPU speeds (default: 85) ---temp-target Target temperature when automatically managing fan and GPU speeds (default: 75) ---vectors|-v Override detected optimal vector (1, 2 or 4) - one value or comma separated list ---worksize|-w Override detected optimal worksize - one value or comma separated list - -See GPU-README for more information regarding GPU mining. - - -SCRYPT only options: - ---lookup-gap Set GPU lookup gap for scrypt mining, comma separated ---shaders GPU shaders per card for tuning scrypt, comma separated ---thread-concurrency Set GPU thread concurrency for scrypt mining, comma separated - -See SCRYPT-README for more information regarding litecoin mining. - - Cgminer should automatically find all of your Avalon ASIC, BFL ASIC, BitForce -FPGAs, Icarus bitstream FPGAs, ASICMINER usb block erupters, ModMiner FPGAs, -or Ztex FPGAs +FPGAs, Icarus bitstream FPGAs, Klondike ASIC, ASICMINER usb block erupters, +KnC ASICs, BaB ASICs, Hashfast ASICs, ModMiner FPGAs, BPMC/BGMC BF1 USB ASICs, +Bi*fury USB ASICs, Onestring miner USB ASICs, Hexfury USB ASICs, Nanofury USB +ASICs, Antminer U1/U2/U2+ U3 USB ASICs, Cointerra devices, BFx2 USB ASICs, +Rockminer R-Box/RK-Box/T1 USB ASICs, Avalon2/3/4 USB ASICs and Hashratio USB +ASICs. --- @@ -279,16 +391,33 @@ SETTING UP USB DEVICES WINDOWS: On windows, the direct USB support requires the installation of a WinUSB -driver (NOT the ftdi_sio driver), and attach it to your devices. -The easiest way to do this is to use the zadig utility which will install the -drivers for you and then once you plug in your device you can choose the +driver (NOT the ftdi_sio driver), and attach it to the chosen USB device. +When configuring your device, plug it in and wait for windows to attempt to +install a driver on its own. It may think it has succeeded or failed but wait +for it to finish regardless. This is NOT the driver you want installed. At this +point you need to associate your device with the WinUSB driver. The easiest +way to do this is to use the zadig utility which you must right click on and +run as administrator. Then once you plug in your device you can choose the "list all devices" from the "option" menu and you should be able to see the device as something like: "BitFORCE SHA256 SC". Choose the install or replace driver option and select WinUSB. You can either google for zadig or download -it from the cgminer directoy in the DOWNLOADS link above. +it from the cgminer directory in the DOWNLOADS link above. + +When you first switch a device over to WinUSB with zadig and it shows that +correctly on the left of the zadig window, but it still gives permission +errors, you may need to unplug the USB miner and then plug it back in. Some +users may need to reboot at this point. + LINUX: +The short version: + + sudo cp 01-cgminer.rules /etc/udev/rules.d/ + + +The long version: + On linux, the direct USB support requires no drivers at all. However due to permissions issues, you may not be able to mine directly on the devices as a regular user without giving the user access to the device or by mining as @@ -301,7 +430,7 @@ If your distribution does not have the plugdev group you can create it with: sudo groupadd plugdev -In order for the BFL devices to instantly be owned by the plugdev group and +In order for the USB devices to instantly be owned by the plugdev group and accessible by anyone from the plugdev group you can copy the file "01-cgminer.rules" from the cgminer archive into the /etc/udev/rules.d directory with the following command: @@ -311,14 +440,40 @@ directory with the following command: After this you can either manually restart udev and re-login, or more easily just reboot. + +OSX: + +On OSX, like Linux, no drivers need to be installed. However some devices +like the bitfury USB sticks automatically load a driver thinking they're a +modem and the driver needs to be unloaded for cgminer to work: + +sudo kextunload -b com.apple.driver.AppleUSBCDC +sudo kextunload -b com.apple.driver.AppleUSBCDCACMData + +There may be a limit to the number of USB devices that you are allowed to start. +The following set of commands, followed by a reboot will increase that: + + sudo su + touch /etc/sysctl.conf + echo kern.sysv.semume=100 >> /etc/sysctl.conf + chown root:wheel /etc/sysctl.conf + chmod 0644 /etc/sysctl.conf + +Some devices need superuser access to mine on them so cgminer may need to +be started with sudo +i.e.: +sudo cgminer + + +--- + Advanced USB options: -The --usb option can restrict how many Avalon, BFL ASIC, BitForce FPGAs, -ModMiner FPGAs or Icarus bitstream FPGAs it finds: +The --usb option can restrict how many USB devices are found: --usb 1:2,1:3,1:4,1:* or - --usb BAS:1,BFL:1,MMQ:0,ICA:0 + --usb BAS:1,BFL:1,MMQ:0,ICA:0,KLN:0 or --usb :10 @@ -337,19 +492,24 @@ This is useful if you unplug a device then plug it back in the same port, it usually reappears with the same bus_number but a different device_address You can see the list of all USB devices on linux with 'sudo lsusb' -Cgminer will list the recognised USB devices with the '-n' option or the +Cgminer will list the recognised USB devices + +with the '-n' option or the '--usb-dump 0' option The '--usb-dump N' option with a value of N greater than 0 will dump a lot of details about each recognised USB device If you wish to see all USB devices, include the --usb-list-all option The second version - --usb BAS:1,BFL:1,MMQ:0,ICA:0 + --usb BAS:1,BFL:1,MMQ:0,ICA:0,KLN:0 allows you to specify how many devices to choose based on each device -driver cgminer has - there are currently 4 USB drivers: BAS, BFL, MMQ & ICA +driver cgminer has - the current USB drivers are: +AVA, BAS, BFL, BF1, DRB, HFA, ICA, KLN and MMQ. + N.B. you can only specify which device driver to limit, not the type of each device, e.g. with BAS:n you can limit how many BFL ASIC devices will be checked, but you cannot limit the number of each type of BFL ASIC + Also note that the MMQ count is the number of MMQ backplanes you have not the number of MMQ FPGAs @@ -363,17 +523,27 @@ If one of the 10 devices stops working, hotplug - if enabled, as is default --usb :0 will disable all USB I/O other than to initialise libusb -NOTE: The --device option will limit which devices are in use based on their -numbering order of the total devices, so if you hotplug USB devices regularly, -it will not reliably be the same devices. - --- WHILE RUNNING: The following options are available while running with a single keypress: -[P]ool management [G]PU management [S]ettings [D]isplay options [Q]uit + [U]SB management [P]ool management [S]ettings [D]isplay options [Q]uit + + +U gives you: + +[S]ummary of device information +[E]nable device +[D]isable device +[U]nplug to allow hotplug restart +[R]eset device USB +[L]ist all known devices +[B]lacklist current device from current instance of cgminer +[W]hitelist previously blacklisted device +[H]otplug interval (0 to disable) + P gives you: @@ -402,39 +572,23 @@ D gives you: [R]PC debug:off [W]orkTime details:off co[M]pact: off +[T]oggle status switching:enabled +[Z]ero statistics [L]og interval:5 Q quits the application. -G gives you something like: - -GPU 0: [124.2 / 191.3 Mh/s] [A:77 R:33 HW:0 U:1.73/m WU 1.73/m] -Temp: 67.0 C -Fan Speed: 35% (2500 RPM) -Engine Clock: 960 MHz -Memory Clock: 480 Mhz -Vddc: 1.200 V -Activity: 93% -Powertune: 0% -Last initialised: [2011-09-06 12:03:56] -Thread 0: 62.4 Mh/s Enabled ALIVE -Thread 1: 60.2 Mh/s Enabled ALIVE - -[E]nable [D]isable [R]estart GPU [C]hange settings -Or press any other key to continue - - The running log shows output like this: - [2012-10-12 18:02:20] Accepted f0c05469 Diff 1/1 GPU 0 pool 1 - [2012-10-12 18:02:22] Accepted 218ac982 Diff 7/1 GPU 1 pool 1 - [2012-10-12 18:02:23] Accepted d8300795 Diff 1/1 GPU 3 pool 1 - [2012-10-12 18:02:24] Accepted 122c1ff1 Diff 14/1 GPU 1 pool 1 + [2013-11-09 11:04:41] Accepted 01b3bde7 Diff 150/128 AVA 1 pool 0 + [2013-11-09 11:04:49] Accepted 015df995 Diff 187/128 AVA 1 pool 0 + [2013-11-09 11:04:50] Accepted 01163b68 Diff 236/128 AVA 1 pool 0 + [2013-11-09 11:04:53] Accepted 9f745840 Diff 411/128 BAS 1 pool 0 -The 8 byte hex value are the 2nd 8 bytes of the share being submitted to the -pool. The 2 diff values are the actual difficulty target that share reached +The 8 byte hex value are the 1st nonzero bytes of the share being submitted to +the pool. The 2 diff values are the actual difficulty target that share reached followed by the difficulty target the pool is currently asking for. --- @@ -442,46 +596,85 @@ Also many issues and FAQs are covered in the forum thread dedicated to this program, http://forum.bitcoin.org/index.php?topic=28402.0 +DISPLAY: + +The display is roughly split into two portions, the top status window and the +bottom scrolling log window. + + +STATUS WINDOW +The status window is split into overall status and per device status. + +Overall status: + The output line shows the following: -(5s):1713.6 (avg):1707.8 Mh/s | A:729 R:8 HW:0 WU:22.53/m + (5s):2.469T (1m):2.677T (5m):2.040T (15m):1.014T (avg):2.733Th/s + +These are exponentially decaying average hashrates over 5s/1m/5m/15m and an +average since the start. + +Followed by: + A:290391 R:5101 HW:145 WU:37610.4/m Each column is as follows: -5s: A 5 second exponentially decaying average hash rate -avg: An all time average hash rate A: The total difficulty of Accepted shares R: The total difficulty of Rejected shares HW: The number of HardWare errors WU: The Work Utility defined as the number of diff1 shares work / minute (accepted or rejected). - GPU 1: 73.5C 2551RPM | 427.3/443.0Mh/s | A:8 R:0 HW:0 WU:4.39/m +alternating with: + ST: 22 SS: 0 NB: 2 LW: 356090 GF: 0 RF: 0 + +ST is STaged work items (ready to use). +SS is Stale Shares discarded (detected and not submitted so don't count as rejects) +NB is New Blocks detected on the network +LW is Locally generated Work items +GF is Getwork Fail Occasions (server slow to provide work) +RF is Remote Fail occasions (server slow to accept work) + +Followed by: + Connected to pool.com diff 3.45K with stratum as user me + +The diff shown is the current vardiff requested by the pool currently being +mined at. + +Followed by: +Block: ca0d237f... Diff:5.01G Started: [00:14:27] Best share: 1.18M + +This shows a short stretch about the current block, when the new block started, +and the all time best difficulty share you've found since starting cgminer +this time. + +Per device status: + + 6: HFS Random : 645MHz 85C 13% 0.79V | 2.152T / 1.351Th/s Each column is as follows: Temperature (if supported) Fanspeed (if supported) +Voltage (if supported) + A 5 second exponentially decaying average hash rate An all time average hash rate + +alternating with + + 6: HFS Random : 645MHz 86C 13% 0.80V | A:290348 R:1067 HW:88 WU:18901.8/m + The total difficulty of accepted shares The total difficulty of rejected shares The number of hardware erorrs The work utility defined as the number of diff1 shares work / minute -The cgminer status line shows: - ST: 1 SS: 0 NB: 1 LW: 8 GF: 1 RF: 1 -ST is STaged work items (ready to use). -SS is Stale Shares discarded (detected and not submitted so don't count as rejects) -NB is New Blocks detected on the network -LW is Locally generated Work items -GF is Getwork Fail Occasions (server slow to provide work) -RF is Remote Fail occasions (server slow to accept work) +LOG WINDOW -The block display shows: -Block: 0074c5e482e34a506d2a051a... Started: [17:17:22] Best share: 2.71K +All running information is shown here, usually share submission results and +block update notifications, along with device messages and warnings. -This shows a short stretch of the current block, when the new block started, -and the all time best difficulty share you've found since starting cgminer -this time. + [2014-03-29 00:24:09] Accepted 1397768d Diff 3.35K/2727 HFS 0 pool 0 + [2014-03-29 00:24:13] Stratum from pool 0 detected new block --- @@ -507,15 +700,86 @@ This strategy moves at user-defined intervals from one active pool to the next, skipping pools that are idle. LOAD BALANCE: -This strategy sends work to all the pools to maintain optimum load. The most -efficient pools will tend to get a lot more shares. If any pool falls idle, the -rest will tend to take up the slack keeping the miner busy. +This strategy sends work to all the pools on a quota basis. By default, all +pools are allocated equal quotas unless specified with --quota. This +apportioning of work is based on work handed out, not shares returned so is +independent of difficulty targets or rejected shares. While a pool is disabled +or dead, its quota is dropped until it is re-enabled. Quotas are forward +looking, so if the quota is changed on the fly, it only affects future work. +If all pools are set to zero quota or all pools with quota are dead, it will +fall back to a failover mode. See quota below for more information. + +The failover-only flag has special meaning in combination with load-balance +mode and it will distribute quota back to priority pool 0 from any pools that +are unable to provide work for any reason so as to maintain quota ratios +between the rest of the pools. BALANCE: This strategy monitors the amount of difficulty 1 shares solved for each pool and uses it to try to end up doing the same amount of work for all pools. +--- +QUOTAS + +The load-balance multipool strategy works off a quota based scheduler. The +quotas handed out by default are equal, but the user is allowed to specify any +arbitrary ratio of quotas. For example, if all the quota values add up to 100, +each quota value will be a percentage, but if 2 pools are specified and pool0 +is given a quota of 1 and pool1 is given a quota of 9, pool0 will get 10% of +the work and pool1 will get 90%. Quotas can be changed on the fly by the API, +and do not act retrospectively. Setting a quota to zero will effectively +disable that pool unless all other pools are disabled or dead. In that +scenario, load-balance falls back to regular failover priority-based strategy. +While a pool is dead, it loses its quota and no attempt is made to catch up +when it comes back to life. + +To specify quotas on the command line, pools should be specified with a +semicolon separated --quota(or -U) entry instead of --url. Pools specified with +--url are given a nominal quota value of 1 and entries can be mixed. + +For example: +--url poola:porta -u usernamea -p passa --quota "2;poolb:portb" -u usernameb -p passb +Will give poola 1/3 of the work and poolb 2/3 of the work. + +Writing configuration files with quotas is likewise supported. To use the above +quotas in a configuration file they would be specified thus: + +"pools" : [ + { + "url" : "poola:porta", + "user" : "usernamea", + "pass" : "passa" + }, + { + "quota" : "2;poolb:portb", + "user" : "usernameb", + "pass" : "passb" + } +] + + +--- +SOLO MINING + +Solo mining can be done efficiently as a single pool entry or a backup to +any other pooled mining and it is recommended everyone have solo mining set up +as their final backup in case all their other pools are DDoSed/down for the +security of the network. To enable solo mining, one must be running a local +bitcoind/bitcoin-qt or have one they have rpc access to. To do this, edit your +bitcoind configuration file (bitcoin.conf) with the following extra lines, +using your choice of username and password: + +rpcuser=username +rpcpassword=password + +Restart bitcoind, then start cgminer, pointing to the bitcoind and choose a +btc address with the following options, altering to suit their setup: + +cgminer -o http://localhost:8332 -u username -p password --btc-address 15qSxP1SQcUX3o4nhkfdbgyoWEFMomJ4rZ + +Note the http:// is mandatory for solo mining. + --- LOGGING @@ -569,7 +833,7 @@ format: For example (this is wrapped, but it's all on one line for real): 1335313090,reject, ffffffffffffffffffffffffffffffffffffffffffffffffffffffff00000000, - http://localhost:8337,GPU0,0, + http://localhost:8337,ASC0,0, 6f983c918f3299b58febf95ec4d0c7094ed634bc13754553ec34fc3800000000, 00000001a0980aff4ce4a96d53f4b89a2d5f0e765c978640fe24372a000001c5 000000004a4366808f81d44f26df3d69d7dc4b3473385930462d9ab707b50498 @@ -578,6 +842,34 @@ For example (this is wrapped, but it's all on one line for real): --- +BENCHMARK + +The --benchmark option hashes a single fixed work item over and over and does +not submit shares to any pools. + +The --benchfile option hashes the work given in the file supplied. +The format of the work file is: +version,merkleroot,prevhash,diffbits,noncetime +Any empty line or any line starting with '#' or '/' is ignored. +When it reaches the end of the file it continues back at the top. + +The format of the data items matches the byte ordering and format of the +the bitcoind getblock RPC output. + +An example file containing bitcoin block #1 would be: + +# Block 1 +1,0e3e2357e806b6cdb1f70b54c3a3a17b6714ee1f0e68bebb44a74b1efd512098,00000000001 +9d6689c085ae165831e934ff763ae46a2a6c172b3f1b60a8ce26f,1d00ffff,1231469665 + +However, the work data should be one line without the linebreak in the middle + +If you use --benchfile , then --benchfile-display will output a log line, +for each nonce found, showing the nonce value in decimal and hex and the work +used to find it in hex. + +--- + RPC API For RPC API details see the API-README file @@ -586,7 +878,22 @@ For RPC API details see the API-README file FAQ -Q: Can I mine on servers from different networks (eg smartcoin and bitcoin) at +Q: Help, I've started cgminer and everything reads zero!? +A: Welcome to bitcoin mining. Your computer by itself cannot mine bitcoin no +matter how powerful it is. You have to purchase dedicated mining hardware +called ASICs to plug into your computer. See Q regarding ASICs below. + +Q: I have multiple USB stick devices but I can't get them all to work at once? +A: Very few USB hubs deliver the promised power required to run as many devices +as they fit if all of them draw power from USB. + +Q: I've plugged my devices into my USB hub but nothing shows up? +A: RPis and Windows have incomplete or non-standard USB3 support so they may +never work. It may be possible to get a USB3 hub to work by plugging it into +a USB2 hub. When choosing a hub, USB2 hubs are preferable whenever possible +due to better support all round. + +Q: Can I mine on servers from different networks (eg xxxcoin and bitcoin) at the same time? A: No, cgminer keeps a database of the block it's working on to ensure it does not work on stale blocks, and having different blocks from two networks would @@ -603,7 +910,7 @@ config file and the file will be loaded one each startup. Q: The build fails with gcc is unable to build a binary. A: Remove the "-march=native" component of your CFLAGS as your version of gcc -does not support it. +does not support it. Also -O2 is capital o 2, not zero 2. Q: Can you implement feature X? A: I can, but time is limited, and people who donate are more likely to get @@ -613,7 +920,7 @@ Q: Work keeps going to my backup pool even though my primary pool hasn't failed? A: Cgminer checks for conditions where the primary pool is lagging and will pass some work to the backup servers under those conditions. The reason for -doing this is to try its absolute best to keep the GPUs working on something +doing this is to try its absolute best to keep the devices working on something useful and not risk idle periods. You can disable this behaviour with the option --failover-only. @@ -630,22 +937,18 @@ less of another, or can you change the quiet mode or can you add yet another output mode? A: Everyone will always have their own view of what's important to monitor. The defaults are very sane and I have very little interest in changing this -any further. +any further. There is far more detail in the API output than can be reasonably +displayed on the small console window, and using an external interface such +as miner.php is much more useful for setups with many devices. Q: What are the best parameters to pass for X pool/hardware/device. A: Virtually always, the DEFAULT parameters give the best results. Most user -defined settings lead to worse performance. The ONLY thing most users should -need to set is the Intensity for GPUs. - -Q: What happened to CPU mining? -A: Being increasingly irrelevant for most users, and a maintenance issue, it is -no longer under active development and will not be supported. No binary builds -supporting CPU mining will be released. Virtually all remaining users of CPU -mining are as back ends for illegal botnets. The main reason cgminer is being -inappopriately tagged as a virus by antivirus software is due to the trojans -packaging a CPU mining capable version of it. There is no longer ANY CPU mining -code in cgminer. If you are mining bitcoin with CPU today, you are spending -1000x more in electricity costs than you are earning in bitcoin. +defined settings lead to worse performance. + +Q: What happened to CPU and GPU mining? +A: Their efficiency makes them irrelevant in the bitcoin mining world today +and the author has no interest in supporting alternative coins that are better +mined by these devices. Q: GUI version? A: No. The RPC interface makes it possible for someone else to write one @@ -653,49 +956,26 @@ though. Q: I'm having an issue. What debugging information should I provide? A: Start cgminer with your regular commands and add -D -T --verbose and provide -the full startup output and a summary of your hardware, operating system, ATI -driver version and ATI stream version. +the full startup output and a summary of your hardware and operating system. Q: Why don't you provide win64 builds? A: Win32 builds work everywhere and there is precisely zero advantage to a 64 bit build on windows. Q: Is it faster to mine on windows or linux? -A: It makes no difference. It comes down to choice of operating system for -their various features. Linux offers much better long term stability and -remote monitoring and security, while windows offers you overclocking tools -that can achieve much more than cgminer can do on linux. - -Q: Can I mine with cgminer on a MAC? -A: cgminer will compile on OSX, but the performance of GPU mining is -compromised due to the opencl implementation on OSX, there is no temperature -or fanspeed monitoring, and the cooling design of most MACs, despite having -powerful GPUs, will usually not cope with constant usage leading to a high -risk of thermal damage. It is highly recommended not to mine on a MAC unless -it is to a USB device. - -Q: I'm trying to mine litecoin but cgminer shows MH values instead of kH and -submits no shares? -A: Add the --scrypt parameter. - -Q: I switch users on windows and my mining stops working? -A: That's correct, it does. It's a permissions issue that there is no known -fix for due to monitoring of GPU fanspeeds and temperatures. If you disable -the monitoring with --no-adl it should switch okay. +A: It makes no difference in terms of performance. It comes down to choice of +operating system for their various features and your comfort level. However +linux is the primary development platform and is virtually guaranteed to be +more stable. Q: My network gets slower and slower and then dies for a minute? -A; Try the --net-delay option. +A; Try the --net-delay option if you are on a getwork or GBT server. This does +nothing with stratum mining. Q: How do I tune for p2pool? -A: p2pool has very rapid expiration of work and new blocks, it is suggested you -decrease intensity by 1 from your optimal value, and decrease GPU threads to 1 -with -g 1. It is also recommended to use --failover-only since the work is -effectively like a different block chain. If mining with a minirig, it is worth -adding the --bfl-range option. - -Q: Are OpenCL kernels from other mining software useable in cgminer? -A: No, the APIs are slightly different between the different software and they -will not work. +A: It is also recommended to use --failover-only since the work is effectively +like a different block chain, and not enabling --no-submit-stale. If mining with +a BFL (fpga) minirig, it is worth adding the --bfl-range option. Q: I run PHP on windows to access the API with the example miner.php. Why does it fail when php is installed properly but I only get errors about Sockets not @@ -703,27 +983,22 @@ working in the logs? A: http://us.php.net/manual/en/sockets.installation.php Q: What is a PGA? -A: At the moment, cgminer supports 4 FPGAs: BitForce, Icarus, ModMiner, and Ztex. +A: Cgminer supports 3 FPGAs: BitForce, Icarus and ModMiner. They are Field-Programmable Gate Arrays that have been programmed to do Bitcoin mining. Since the acronym needs to be only 3 characters, the "Field-" part has been skipped. Q: What is an ASIC? -A: Cgminer currently supports 2 ASICs: Avalon and BitForce SC devices. They -are Application Specify Integrated Circuit devices and provide the highest -performance per unit power due to being dedicated to only one purpose. - -Q: Can I mine scrypt with FPGAs or ASICs? -A: No. +A: They are Application Specify Integrated Circuit devices and provide the +highest performance per unit power due to being dedicated to only one purpose. +They are the only meaningful way to mine bitcoin today. Q: What is stratum and how do I use it? A: Stratum is a protocol designed for pooled mining in such a way as to minimise the amount of network communications, yet scale to hardware of any speed. With versions of cgminer 2.8.0+, if a pool has stratum support, cgminer will automatically detect it and switch to the support as advertised if it can. -Stratum uses direct TCP connections to the pool and thus it will NOT currently -work through a http proxy but will work via a socks proxy if you need to use -one. If you input the stratum port directly into your configuration, or use the +If you input the stratum port directly into your configuration, or use the special prefix "stratum+tcp://" instead of "http://", cgminer will ONLY try to use stratum protocol mining. The advantages of stratum to the miner are no delays in getting more work for the miner, less rejects across block changes, @@ -740,16 +1015,6 @@ average to find one 8 difficulty share, per 8 single difficulty shares found. However, the number is actually random and converges over time, it is an average, not an exact value, thus you may find more or less than the expected average. -Q: Why do the scrypt diffs not match with the current difficulty target? -A: The current scrypt block difficulty is expressed in terms of how many -multiples of the BTC difficulty it currently is (eg 28) whereas the shares of -"difficulty 1" are actually 65536 times smaller than the BTC ones. The diff -expressed by cgminer is as multiples of difficulty 1 shares. - -Q: Can I make a donation in litecoin? -A: Yes, see SCRYPT-README for the address, but the author prefers bitcoin if -possible. - Q: My keyboard input momentarily pauses or repeats keys every so often on windows while mining? A: The USB implementation on windows can be very flaky on some hardware and @@ -757,11 +1022,6 @@ every time cgminer looks for new hardware to hotplug it it can cause these sorts of problems. You can disable hotplug with: --hotplug 0 -Q: Can I use a proxy? -A: Proxies only work with the getwork and GBT protocols using the --proxy -command. If you wish to use a proxy with stratum, people have supported -success with various 3rd party tools like proxifier. - Q: What should my Work Utility (WU) be? A: Work utility is the product of hashrate * luck and only stabilises over a very long period of time. Assuming all your work is valid work, bitcoin mining @@ -770,6 +1030,23 @@ should produce a work utility of approximately 1 per 71.6MH. This means at do "better WU" than this - it is luck related. However you can make it much worse if your machine produces a lot of hardware errors producing invalid work. +Q: What should I build in for a generic distribution binary? +A: There are a number of drivers that expect to be used on dedicated standalone +hardware. That said, the drivers that are designed to work generically with +USB on any hardware are the following: + +--enable-avalon +--enable-avalon2 +--enable-avalon4 +--enable-bflsc +--enable-bitfury +--enable-blockerupter +--enable-cointerra +--enable-drillbit +--enable-hashfast +--enable-hashratio +--enable-icarus +--enable-klondike --- diff --git a/SCRYPT-README b/SCRYPT-README deleted file mode 100644 index 7addc05d76..0000000000 --- a/SCRYPT-README +++ /dev/null @@ -1,242 +0,0 @@ -While BTC donations are preferred, if you wish to donate to the author, Con -Kolivas, in LTC, please submit your donations to: - -Lc8TWMiKM7gRUrG8VB8pPNP1Yvt1SGZnoH - -Otherwise, please donate in BTC as per the main README. - ---- - -Scrypt mining, AKA litecoin mining, for GPU is completely different to sha256 -used for bitcoin mining. The algorithm was originally developed in a manner -that it was anticipated would make it suitable for mining on CPU but NOT GPU. -Thanks to some innovative work by Artforz and mtrlt, this was proven to be -wrong. However, it has very different requirements to bitcoin mining and is a -lot more complicated to get working well. Note that it is a ram dependent -workload, and requires you to have enough system ram as well as fast enough -GPU ram. If you have less system ram than your GPU has, it may not be possible -to mine at any reasonable rate. - -There are 5 main parameters to tuning scrypt, all of which are optional for -further fine tuning. When you start scrypt mining with the --scrypt option, -cgminer will fail IN RANDOM WAYS. They are all due to parameters being outside -what the GPU can cope with. - -NOTE that if it does not fail at startup, the presence of hardware errors (HW) -are a sure sign that you have set the parameters too high. - - -DRIVERS AND OPENCL SDK - -The choice of driver version for your GPU is critical, as some are known to -break scrypt mining entirely while others give poor hashrates. As for the -OpenCL SDK installed, for AMD it must be version 2.6 or later. - - -Step 1 on linux: -export GPU_MAX_ALLOC_PERCENT=100 -If you do not do this, you may find it impossible to scrypt mine. You may find -a value of 40 is enough and increasing this further has little effect. - -export GPU_USE_SYNC_OBJECTS=1 -may help CPU usage a little as well. - -On windows the same commands can be passed via a batch file if the following -lines are in the .bat before starting cgminer: -setx GPU_MAX_ALLOC_PERCENT 100 -setx GPU_USE_SYNC_OBJECTS 1 - ---intensity XX (-I XX) - -Just like in bitcoin mining, scrypt mining takes an intensity, however the -scale goes from 0 to 20 to mimic the "Aggression" used in mtrlt's reaper. The -reason this is crucial is that too high an intensity can actually be -disastrous with scrypt because it CAN run out of ram. High intensities -start writing over the same ram and it is highly dependent on the GPU, but they -can start actually DECREASING your hashrate, or even worse, start producing -garbage with HW errors skyrocketing. Note that if you do NOT specify an -intensity, cgminer uses dynamic mode which is designed to minimise the harm -to a running desktop and performance WILL be poor. The lower limit to intensity -with scrypt is usually 8 and cgminer will prevent it going too low. -SUMMARY: Setting this for reasonable hashrates is mandatory. - ---shaders XXX - -is a new option where you tell cgminer how many shaders your GPU has. This -helps cgminer try to choose some meaningful baseline parameters. Use this table -below to determine how many shaders your GPU has, and note that there are some -variants of these cards, and nvidia shaders are much much lower and virtually -pointless trying to mine on. If this is not set, cgminer will query the -device for how much memory it supports and will try to set a value based on -that. -SUMMARY: This will get you started but fine tuning for optimal performance is -required. - -GPU Shaders -7750 512 -7770 640 -7850 1024 -7870 1280 -7950 1792 -7970 2048 - -6850 960 -6870 1120 -6950 1408 -6970 1536 -6990 (6970x2) - -6570 480 -6670 480 -6790 800 - -6450 160 - -5670 400 -5750 720 -5770 800 -5830 1120 -5850 1440 -5870 1600 -5970 (5870x2) - -These are only used as a rough guide for cgminer, and it is rare that this is -all you will need to set. - - -Optional parameters to tune: --g, --thread-concurrency, --lookup-gap - ---thread-concurrency: -This tunes the optimal size of work that scrypt can do. It is internally tuned -by cgminer to be the highest reasonable multiple of shaders that it can -allocate on your GPU. Ideally it should be a multiple of your shader count. -vliw5 architecture (R5XXX) would be best at 5x shaders, while VLIW4 (R6xxx and -R7xxx) are best at 4x. Setting thread concurrency overrides anything you put -into --shaders and is ultimately a BETTER way to tune performance. -SUMMARY: Spend lots of time finding the highest value that your device likes -and increases hashrate. - --g: -Once you have found the optimal shaders and intensity, you can start increasing -the -g value till cgminer fails to start. This is really only of value if you -want to run low intensities as you will be unable to run more than 1. -SUMMARY: Don't touch this. - ---lookup-gap -This tunes a compromise between ram usage and performance. Performance peaks -at a gap of 2, but increasing the gap can save you some GPU ram, but almost -always at the cost of significant loss of hashrate. Setting lookup gap -overrides the default of 2, but cgminer will use the --shaders value to choose -a thread-concurrency if you haven't chosen one. -SUMMARY: Don't touch this. - - -Related parameters: ---worksize XX (-w XX) -Has a minor effect, should be a multiple of 64 up to 256 maximum. -SUMMARY: Worth playing with once everything else has been tried but will -probably do nothing. - ---vectors XX (-v XX) -Vectors are NOT used by the scrypt mining kernel. -SUMMARY: Does nothing. - - -Overclocking for scrypt mining: -First of all, do not underclock your memory initially. Scrypt mining requires -memory speed and on most, but not all, GPUs, lowering memory speed lowers -mining performance. - -Second, absolute engine clock speeds do NOT correlate with hashrate. The ratio -of engine clock speed to memory matters, so if you set your memory to the -default value, and then start overclocking as you are running it, you should -find a sweet spot where the hashrate peaks and then it might actually drop if -you increase the engine clock speed further. - -Third, the combination of motherboard, CPU and system ram ALSO makes a -difference, so values that work for a GPU on one system may not work for the -same GPU on a different system. A decent amount of system ram is actually -required for scrypt mining, and 4GB is suggested. - -Finally, the power consumption while mining at high engine clocks, very high -memory clocks can be far in excess of what you might imagine. -For example, a 7970 running with the following settings: ---thread-concurrency 22392 --gpu-engine 1135 --gpu-memclock 1890 -was using 305W! - ---- -TUNING AN AMD RADEON 7970 -Example tuning a 7970 for Scrypt mining: - -On linux run this command: -export GPU_MAX_ALLOC_PERCENT=100 -or on windows this: -setx GPU_MAX_ALLOC_PERCENT 100 -in the same console/bash/dos prompt/bat file/whatever you want to call it, -before running cgminer. - -First, find the highest thread concurrency that you can start it at. They should -all start at 8192 but some will go up to 3 times that. Don't go too high on the -intensity while testing and don't change gpu threads. If you cannot go above -8192, don't fret as you can still get a high hashrate. - -Delete any .bin files so you're starting from scratch and see what bins get -generated. - -First try without any thread concurrency or even shaders, as cgminer will try to -find an optimal value -cgminer -I 13 - -If that starts mining, see what bin was generated, it is likely the largest -meaningful TC you can set. -Starting it on mine I get: -scrypt130302Tahitiglg2tc22392w64l8.bin - -See tc22392 that's telling you what thread concurrency it was. It should start -without TC parameters, but you never know. So if it doesn't, start with ---thread-concurrency 8192 and add 2048 to it at a time till you find the highest -value it will start successfully at. - -Then start overclocking the eyeballs off your memory, as 7970s are exquisitely -sensitive to memory speed and amazingly overclockable but please make sure it -keeps adequately cooled with --auto-fan! Do it while it's running from the GPU -menu. Go up by 25 at a time every 30 seconds or so until your GPU crashes. Then -reboot and start it 25 lower as a rough start. Mine runs stable at 1900 memory -without overvolting. Overvolting is the only thing that can actually damage your -GPU so I wouldn't recommend it at all. - -Then once you find the maximum memory clock speed, you need to find the sweet -spot engine clock speed that matches it. It's a fine line where one more MHz -will make the hashrate drop by 20%. It's somewhere in the .57 - 0.6 ratio range. -Start your engine clock speed at half your memory clock speed and then increase -it by 5 at a time. The hashrate should climb a little each rise in engine speed -and then suddenly drop above a certain value. Decrease it by 1 then until you -find it climbs dramatically. If your engine clock speed cannot get that high -without crashing the GPU, you will have to use a lower memclock. - -Then, and only then, bother trying to increase intensity further. - -My final settings were: ---gpu-engine 1141 --gpu-memclock 1875 -I 20 -for a hashrate of 745kH. - -Note I did not bother setting a thread concurrency. Once you have the magic -endpoint, look at what tc was chosen by the bin file generated and then hard -code that in next time (eg --thread-concurrency 22392) as slight changes in -thread concurrency will happen every time if you don't specify one, and the tc -to clock ratios are critical! - -Good luck, and if this doesn't work for you, well same old magic discussion -applies, I cannot debug every hardware combo out there. - -Your numbers will be your numbers depending on your hardware combination and OS, -so don't expect to get exactly the same results! - ---- -While BTC donations are preferred, if you wish to donate to the author, Con -Kolivas, in LTC, please submit your donations to: - -Lc8TWMiKM7gRUrG8VB8pPNP1Yvt1SGZnoH - -Otherwise, please donate in BTC as per the main README. diff --git a/adl.c b/adl.c deleted file mode 100644 index 8d14c27ddd..0000000000 --- a/adl.c +++ /dev/null @@ -1,1442 +0,0 @@ -/* - * Copyright 2011-2012 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 "config.h" - -#if defined(HAVE_ADL) && (defined(__linux) || defined (WIN32)) - -#include -#include -#include - -#ifdef HAVE_CURSES -#include -#endif - -#include "miner.h" -#include "ADL_SDK/adl_sdk.h" -#include "compat.h" - -#if defined (__linux) -#include -#include -#include -#else /* WIN32 */ -#include -#include -#endif -#include "adl_functions.h" - -#ifndef HAVE_CURSES -#define wlogprint(...) applog(LOG_WARNING, __VA_ARGS__) -#endif - -bool adl_active; -bool opt_reorder = false; - -int opt_hysteresis = 3; -const int opt_targettemp = 75; -const int opt_overheattemp = 85; -static pthread_mutex_t adl_lock; - -struct gpu_adapters { - int iAdapterIndex; - int iBusNumber; - int virtual_gpu; - int id; -}; - -// Memory allocation function -static void * __stdcall ADL_Main_Memory_Alloc(int iSize) -{ - void *lpBuffer = malloc(iSize); - - return lpBuffer; -} - -// Optional Memory de-allocation function -static void __stdcall ADL_Main_Memory_Free (void **lpBuffer) -{ - if (*lpBuffer) { - free (*lpBuffer); - *lpBuffer = NULL; - } -} - -#if defined (LINUX) -// equivalent functions in linux -static void *GetProcAddress(void *pLibrary, const char *name) -{ - return dlsym( pLibrary, name); -} -#endif - -static ADL_MAIN_CONTROL_CREATE ADL_Main_Control_Create; -static ADL_MAIN_CONTROL_DESTROY ADL_Main_Control_Destroy; -static ADL_ADAPTER_NUMBEROFADAPTERS_GET ADL_Adapter_NumberOfAdapters_Get; -static ADL_ADAPTER_ADAPTERINFO_GET ADL_Adapter_AdapterInfo_Get; -static ADL_ADAPTER_ID_GET ADL_Adapter_ID_Get; -static ADL_OVERDRIVE5_TEMPERATURE_GET ADL_Overdrive5_Temperature_Get; -static ADL_OVERDRIVE5_CURRENTACTIVITY_GET ADL_Overdrive5_CurrentActivity_Get; -static ADL_OVERDRIVE5_ODPARAMETERS_GET ADL_Overdrive5_ODParameters_Get; -static ADL_OVERDRIVE5_FANSPEEDINFO_GET ADL_Overdrive5_FanSpeedInfo_Get; -static ADL_OVERDRIVE5_FANSPEED_GET ADL_Overdrive5_FanSpeed_Get; -static ADL_OVERDRIVE5_FANSPEED_SET ADL_Overdrive5_FanSpeed_Set; -static ADL_OVERDRIVE5_ODPERFORMANCELEVELS_GET ADL_Overdrive5_ODPerformanceLevels_Get; -static ADL_OVERDRIVE5_ODPERFORMANCELEVELS_SET ADL_Overdrive5_ODPerformanceLevels_Set; -static ADL_MAIN_CONTROL_REFRESH ADL_Main_Control_Refresh; -static ADL_OVERDRIVE5_POWERCONTROL_GET ADL_Overdrive5_PowerControl_Get; -static ADL_OVERDRIVE5_POWERCONTROL_SET ADL_Overdrive5_PowerControl_Set; -static ADL_OVERDRIVE5_FANSPEEDTODEFAULT_SET ADL_Overdrive5_FanSpeedToDefault_Set; - -#if defined (LINUX) - static void *hDLL; // Handle to .so library -#else - HINSTANCE hDLL; // Handle to DLL -#endif -static int iNumberAdapters; -static LPAdapterInfo lpInfo = NULL; - -int set_fanspeed(int gpu, int iFanSpeed); -static float __gpu_temp(struct gpu_adl *ga); - -static inline void lock_adl(void) -{ - mutex_lock(&adl_lock); -} - -static inline void unlock_adl(void) -{ - mutex_unlock(&adl_lock); -} - -/* This looks for the twin GPU that has the fanspeed control of a non fanspeed - * control GPU on dual GPU cards */ -static bool fanspeed_twin(struct gpu_adl *ga, struct gpu_adl *other_ga) -{ - if (!other_ga->has_fanspeed) - return false; - if (abs(ga->iBusNumber - other_ga->iBusNumber) != 1) - return false; - if (strcmp(ga->strAdapterName, other_ga->strAdapterName)) - return false; - return true; -} - -static bool prepare_adl(void) -{ - int result; - -#if defined (LINUX) - hDLL = dlopen( "libatiadlxx.so", RTLD_LAZY|RTLD_GLOBAL); -#else - hDLL = LoadLibrary("atiadlxx.dll"); - if (hDLL == NULL) - // A 32 bit calling application on 64 bit OS will fail to LoadLIbrary. - // Try to load the 32 bit library (atiadlxy.dll) instead - hDLL = LoadLibrary("atiadlxy.dll"); -#endif - if (hDLL == NULL) { - applog(LOG_INFO, "Unable to load ati adl library"); - return false; - } - ADL_Main_Control_Create = (ADL_MAIN_CONTROL_CREATE) GetProcAddress(hDLL,"ADL_Main_Control_Create"); - ADL_Main_Control_Destroy = (ADL_MAIN_CONTROL_DESTROY) GetProcAddress(hDLL,"ADL_Main_Control_Destroy"); - ADL_Adapter_NumberOfAdapters_Get = (ADL_ADAPTER_NUMBEROFADAPTERS_GET) GetProcAddress(hDLL,"ADL_Adapter_NumberOfAdapters_Get"); - ADL_Adapter_AdapterInfo_Get = (ADL_ADAPTER_ADAPTERINFO_GET) GetProcAddress(hDLL,"ADL_Adapter_AdapterInfo_Get"); - ADL_Adapter_ID_Get = (ADL_ADAPTER_ID_GET) GetProcAddress(hDLL,"ADL_Adapter_ID_Get"); - ADL_Overdrive5_Temperature_Get = (ADL_OVERDRIVE5_TEMPERATURE_GET) GetProcAddress(hDLL,"ADL_Overdrive5_Temperature_Get"); - ADL_Overdrive5_CurrentActivity_Get = (ADL_OVERDRIVE5_CURRENTACTIVITY_GET) GetProcAddress(hDLL, "ADL_Overdrive5_CurrentActivity_Get"); - ADL_Overdrive5_ODParameters_Get = (ADL_OVERDRIVE5_ODPARAMETERS_GET) GetProcAddress(hDLL, "ADL_Overdrive5_ODParameters_Get"); - ADL_Overdrive5_FanSpeedInfo_Get = (ADL_OVERDRIVE5_FANSPEEDINFO_GET) GetProcAddress(hDLL, "ADL_Overdrive5_FanSpeedInfo_Get"); - ADL_Overdrive5_FanSpeed_Get = (ADL_OVERDRIVE5_FANSPEED_GET) GetProcAddress(hDLL, "ADL_Overdrive5_FanSpeed_Get"); - ADL_Overdrive5_FanSpeed_Set = (ADL_OVERDRIVE5_FANSPEED_SET) GetProcAddress(hDLL, "ADL_Overdrive5_FanSpeed_Set"); - ADL_Overdrive5_ODPerformanceLevels_Get = (ADL_OVERDRIVE5_ODPERFORMANCELEVELS_GET) GetProcAddress(hDLL, "ADL_Overdrive5_ODPerformanceLevels_Get"); - ADL_Overdrive5_ODPerformanceLevels_Set = (ADL_OVERDRIVE5_ODPERFORMANCELEVELS_SET) GetProcAddress(hDLL, "ADL_Overdrive5_ODPerformanceLevels_Set"); - ADL_Main_Control_Refresh = (ADL_MAIN_CONTROL_REFRESH) GetProcAddress(hDLL, "ADL_Main_Control_Refresh"); - ADL_Overdrive5_PowerControl_Get = (ADL_OVERDRIVE5_POWERCONTROL_GET) GetProcAddress(hDLL, "ADL_Overdrive5_PowerControl_Get"); - ADL_Overdrive5_PowerControl_Set = (ADL_OVERDRIVE5_POWERCONTROL_SET) GetProcAddress(hDLL, "ADL_Overdrive5_PowerControl_Set"); - ADL_Overdrive5_FanSpeedToDefault_Set = (ADL_OVERDRIVE5_FANSPEEDTODEFAULT_SET) GetProcAddress(hDLL, "ADL_Overdrive5_FanSpeedToDefault_Set"); - - if (!ADL_Main_Control_Create || !ADL_Main_Control_Destroy || - !ADL_Adapter_NumberOfAdapters_Get || !ADL_Adapter_AdapterInfo_Get || - !ADL_Adapter_ID_Get || !ADL_Overdrive5_Temperature_Get || - !ADL_Overdrive5_CurrentActivity_Get || - !ADL_Overdrive5_ODParameters_Get || !ADL_Overdrive5_FanSpeedInfo_Get || - !ADL_Overdrive5_FanSpeed_Get || !ADL_Overdrive5_FanSpeed_Set || - !ADL_Overdrive5_ODPerformanceLevels_Get || !ADL_Overdrive5_ODPerformanceLevels_Set || - !ADL_Main_Control_Refresh || !ADL_Overdrive5_PowerControl_Get || - !ADL_Overdrive5_PowerControl_Set || !ADL_Overdrive5_FanSpeedToDefault_Set) { - applog(LOG_WARNING, "ATI ADL's API is missing"); - return false; - } - - // Initialise ADL. The second parameter is 1, which means: - // retrieve adapter information only for adapters that are physically present and enabled in the system - result = ADL_Main_Control_Create (ADL_Main_Memory_Alloc, 1); - if (result != ADL_OK) { - applog(LOG_INFO, "ADL Initialisation Error! Error %d!", result); - return false; - } - - result = ADL_Main_Control_Refresh(); - if (result != ADL_OK) { - applog(LOG_INFO, "ADL Refresh Error! Error %d!", result); - return false; - } - - return true; -} - -void init_adl(int nDevs) -{ - int result, i, j, devices = 0, last_adapter = -1, gpu = 0, dummy = 0; - struct gpu_adapters adapters[MAX_GPUDEVICES], vadapters[MAX_GPUDEVICES]; - bool devs_match = true; - - if (unlikely(pthread_mutex_init(&adl_lock, NULL))) { - applog(LOG_ERR, "Failed to init adl_lock in init_adl"); - return; - } - - if (!prepare_adl()) - return; - - // Obtain the number of adapters for the system - result = ADL_Adapter_NumberOfAdapters_Get (&iNumberAdapters); - if (result != ADL_OK) { - applog(LOG_INFO, "Cannot get the number of adapters! Error %d!", result); - return ; - } - - if (iNumberAdapters > 0) { - lpInfo = malloc ( sizeof (AdapterInfo) * iNumberAdapters ); - memset ( lpInfo,'\0', sizeof (AdapterInfo) * iNumberAdapters ); - - lpInfo->iSize = sizeof(lpInfo); - // Get the AdapterInfo structure for all adapters in the system - result = ADL_Adapter_AdapterInfo_Get (lpInfo, sizeof (AdapterInfo) * iNumberAdapters); - if (result != ADL_OK) { - applog(LOG_INFO, "ADL_Adapter_AdapterInfo_Get Error! Error %d", result); - return ; - } - } else { - applog(LOG_INFO, "No adapters found"); - return; - } - - /* Iterate over iNumberAdapters and find the lpAdapterID of real devices */ - for (i = 0; i < iNumberAdapters; i++) { - int iAdapterIndex; - int lpAdapterID; - - iAdapterIndex = lpInfo[i].iAdapterIndex; - /* Get unique identifier of the adapter, 0 means not AMD */ - result = ADL_Adapter_ID_Get(iAdapterIndex, &lpAdapterID); - if (result != ADL_OK) { - applog(LOG_INFO, "Failed to ADL_Adapter_ID_Get. Error %d", result); - if (result == -10) - applog(LOG_INFO, "This error says the device is not enabled"); - continue; - } - - /* Each adapter may have multiple entries */ - if (lpAdapterID == last_adapter) - continue; - - applog(LOG_DEBUG, "GPU %d " - "iAdapterIndex %d " - "strUDID %s " - "iBusNumber %d " - "iDeviceNumber %d " - "iFunctionNumber %d " - "iVendorID %d " - "strAdapterName %s ", - devices, - iAdapterIndex, - lpInfo[i].strUDID, - lpInfo[i].iBusNumber, - lpInfo[i].iDeviceNumber, - lpInfo[i].iFunctionNumber, - lpInfo[i].iVendorID, - lpInfo[i].strAdapterName); - - adapters[devices].iAdapterIndex = iAdapterIndex; - adapters[devices].iBusNumber = lpInfo[i].iBusNumber; - adapters[devices].id = i; - - /* We found a truly new adapter instead of a logical - * one. Now since there's no way of correlating the - * opencl enumerated devices and the ADL enumerated - * ones, we have to assume they're in the same order.*/ - if (++devices > nDevs && devs_match) { - applog(LOG_ERR, "ADL found more devices than opencl!"); - applog(LOG_ERR, "There is possibly at least one GPU that doesn't support OpenCL"); - applog(LOG_ERR, "Use the gpu map feature to reliably map OpenCL to ADL"); - devs_match = false; - } - last_adapter = lpAdapterID; - - if (!lpAdapterID) { - applog(LOG_INFO, "Adapter returns ID 0 meaning not AMD. Card order might be confused"); - continue; - } - } - - if (devices < nDevs) { - applog(LOG_ERR, "ADL found less devices than opencl!"); - applog(LOG_ERR, "There is possibly more than one display attached to a GPU"); - applog(LOG_ERR, "Use the gpu map feature to reliably map OpenCL to ADL"); - devs_match = false; - } - - for (i = 0; i < devices; i++) { - vadapters[i].virtual_gpu = i; - vadapters[i].id = adapters[i].id; - } - - /* Apply manually provided OpenCL to ADL mapping, if any */ - for (i = 0; i < nDevs; i++) { - if (gpus[i].mapped) { - vadapters[gpus[i].virtual_adl].virtual_gpu = i; - applog(LOG_INFO, "Mapping OpenCL device %d to ADL device %d", i, gpus[i].virtual_adl); - } else - gpus[i].virtual_adl = i; - } - - if (!devs_match) { - applog(LOG_ERR, "WARNING: Number of OpenCL and ADL devices did not match!"); - applog(LOG_ERR, "Hardware monitoring may NOT match up with devices!"); - } else if (opt_reorder) { - /* Windows has some kind of random ordering for bus number IDs and - * ordering the GPUs according to ascending order fixes it. Linux - * has usually sequential but decreasing order instead! */ - for (i = 0; i < devices; i++) { - int j, virtual_gpu; - - virtual_gpu = 0; - for (j = 0; j < devices; j++) { - if (i == j) - continue; -#ifdef WIN32 - if (adapters[j].iBusNumber < adapters[i].iBusNumber) -#else - if (adapters[j].iBusNumber > adapters[i].iBusNumber) -#endif - virtual_gpu++; - } - if (virtual_gpu != i) { - applog(LOG_INFO, "Mapping device %d to GPU %d according to Bus Number order", - i, virtual_gpu); - vadapters[virtual_gpu].virtual_gpu = i; - vadapters[virtual_gpu].id = adapters[i].id; - } - } - } - - if (devices > nDevs) - devices = nDevs; - - for (gpu = 0; gpu < devices; gpu++) { - struct gpu_adl *ga; - int iAdapterIndex; - int lpAdapterID; - ADLODPerformanceLevels *lpOdPerformanceLevels; - int lev, adlGpu; - - adlGpu = gpus[gpu].virtual_adl; - i = vadapters[adlGpu].id; - iAdapterIndex = lpInfo[i].iAdapterIndex; - gpus[gpu].virtual_gpu = vadapters[adlGpu].virtual_gpu; - - /* Get unique identifier of the adapter, 0 means not AMD */ - result = ADL_Adapter_ID_Get(iAdapterIndex, &lpAdapterID); - if (result != ADL_OK) { - applog(LOG_INFO, "Failed to ADL_Adapter_ID_Get. Error %d", result); - continue; - } - - if (gpus[gpu].deven == DEV_DISABLED) { - gpus[gpu].gpu_engine = - gpus[gpu].gpu_memclock = - gpus[gpu].gpu_vddc = - gpus[gpu].gpu_fan = - gpus[gpu].gpu_powertune = 0; - continue; - } - - applog(LOG_INFO, "GPU %d %s hardware monitoring enabled", gpu, lpInfo[i].strAdapterName); - if (gpus[gpu].name) - free(gpus[gpu].name); - gpus[gpu].name = lpInfo[i].strAdapterName; - gpus[gpu].has_adl = true; - /* Flag adl as active if any card is successfully activated */ - adl_active = true; - - /* From here on we know this device is a discrete device and - * should support ADL */ - ga = &gpus[gpu].adl; - ga->gpu = gpu; - ga->iAdapterIndex = iAdapterIndex; - ga->lpAdapterID = lpAdapterID; - strcpy(ga->strAdapterName, lpInfo[i].strAdapterName); - ga->DefPerfLev = NULL; - ga->twin = NULL; - - ga->lpOdParameters.iSize = sizeof(ADLODParameters); - if (ADL_Overdrive5_ODParameters_Get(iAdapterIndex, &ga->lpOdParameters) != ADL_OK) - applog(LOG_INFO, "Failed to ADL_Overdrive5_ODParameters_Get"); - - lev = ga->lpOdParameters.iNumberOfPerformanceLevels - 1; - /* We're only interested in the top performance level */ - lpOdPerformanceLevels = malloc(sizeof(ADLODPerformanceLevels) + (lev * sizeof(ADLODPerformanceLevel))); - lpOdPerformanceLevels->iSize = sizeof(ADLODPerformanceLevels) + sizeof(ADLODPerformanceLevel) * lev; - - /* Get default performance levels first */ - if (ADL_Overdrive5_ODPerformanceLevels_Get(iAdapterIndex, 1, lpOdPerformanceLevels) != ADL_OK) - applog(LOG_INFO, "Failed to ADL_Overdrive5_ODPerformanceLevels_Get"); - /* Set the limits we'd use based on default gpu speeds */ - ga->maxspeed = ga->minspeed = lpOdPerformanceLevels->aLevels[lev].iEngineClock; - - ga->lpTemperature.iSize = sizeof(ADLTemperature); - ga->lpFanSpeedInfo.iSize = sizeof(ADLFanSpeedInfo); - ga->lpFanSpeedValue.iSize = ga->DefFanSpeedValue.iSize = sizeof(ADLFanSpeedValue); - /* Now get the current performance levels for any existing overclock */ - ADL_Overdrive5_ODPerformanceLevels_Get(iAdapterIndex, 0, lpOdPerformanceLevels); - /* Save these values as the defaults in case we wish to reset to defaults */ - ga->DefPerfLev = lpOdPerformanceLevels; - - if (gpus[gpu].gpu_engine) { - int setengine = gpus[gpu].gpu_engine * 100; - - /* Lower profiles can't have a higher setting */ - for (j = 0; j < lev; j++) { - if (lpOdPerformanceLevels->aLevels[j].iEngineClock > setengine) - lpOdPerformanceLevels->aLevels[j].iEngineClock = setengine; - } - lpOdPerformanceLevels->aLevels[lev].iEngineClock = setengine; - applog(LOG_INFO, "Setting GPU %d engine clock to %d", gpu, gpus[gpu].gpu_engine); - ADL_Overdrive5_ODPerformanceLevels_Set(iAdapterIndex, lpOdPerformanceLevels); - ga->maxspeed = setengine; - if (gpus[gpu].min_engine) - ga->minspeed = gpus[gpu].min_engine * 100; - ga->managed = true; - if (gpus[gpu].gpu_memdiff) - set_memoryclock(gpu, gpus[gpu].gpu_engine + gpus[gpu].gpu_memdiff); - } - - if (gpus[gpu].gpu_memclock) { - int setmem = gpus[gpu].gpu_memclock * 100; - - for (j = 0; j < lev; j++) { - if (lpOdPerformanceLevels->aLevels[j].iMemoryClock > setmem) - lpOdPerformanceLevels->aLevels[j].iMemoryClock = setmem; - } - lpOdPerformanceLevels->aLevels[lev].iMemoryClock = setmem; - applog(LOG_INFO, "Setting GPU %d memory clock to %d", gpu, gpus[gpu].gpu_memclock); - ADL_Overdrive5_ODPerformanceLevels_Set(iAdapterIndex, lpOdPerformanceLevels); - ga->managed = true; - } - - if (gpus[gpu].gpu_vddc) { - int setv = gpus[gpu].gpu_vddc * 1000; - - for (j = 0; j < lev; j++) { - if (lpOdPerformanceLevels->aLevels[j].iVddc > setv) - lpOdPerformanceLevels->aLevels[j].iVddc = setv; - } - lpOdPerformanceLevels->aLevels[lev].iVddc = setv; - applog(LOG_INFO, "Setting GPU %d voltage to %.3f", gpu, gpus[gpu].gpu_vddc); - ADL_Overdrive5_ODPerformanceLevels_Set(iAdapterIndex, lpOdPerformanceLevels); - ga->managed = true; - } - - ADL_Overdrive5_ODPerformanceLevels_Get(iAdapterIndex, 0, lpOdPerformanceLevels); - ga->iEngineClock = lpOdPerformanceLevels->aLevels[lev].iEngineClock; - ga->iMemoryClock = lpOdPerformanceLevels->aLevels[lev].iMemoryClock; - ga->iVddc = lpOdPerformanceLevels->aLevels[lev].iVddc; - ga->iBusNumber = lpInfo[i].iBusNumber; - - if (ADL_Overdrive5_FanSpeedInfo_Get(iAdapterIndex, 0, &ga->lpFanSpeedInfo) != ADL_OK) - applog(LOG_INFO, "Failed to ADL_Overdrive5_FanSpeedInfo_Get"); - else - ga->has_fanspeed = true; - - /* Save the fanspeed values as defaults in case we reset later */ - ADL_Overdrive5_FanSpeed_Get(ga->iAdapterIndex, 0, &ga->DefFanSpeedValue); - if (gpus[gpu].gpu_fan) - set_fanspeed(gpu, gpus[gpu].gpu_fan); - else - gpus[gpu].gpu_fan = 85; /* Set a nominal upper limit of 85% */ - - /* Not fatal if powercontrol get fails */ - if (ADL_Overdrive5_PowerControl_Get(ga->iAdapterIndex, &ga->iPercentage, &dummy) != ADL_OK) - applog(LOG_INFO, "Failed to ADL_Overdrive5_PowerControl_get"); - - if (gpus[gpu].gpu_powertune) { - ADL_Overdrive5_PowerControl_Set(ga->iAdapterIndex, gpus[gpu].gpu_powertune); - ADL_Overdrive5_PowerControl_Get(ga->iAdapterIndex, &ga->iPercentage, &dummy); - ga->managed = true; - } - - /* Set some default temperatures for autotune when enabled */ - if (!ga->targettemp) - ga->targettemp = opt_targettemp; - if (!ga->overtemp) - ga->overtemp = opt_overheattemp; - if (!gpus[gpu].cutofftemp) - gpus[gpu].cutofftemp = opt_cutofftemp; - if (opt_autofan) { - /* Set a safe starting default if we're automanaging fan speeds */ - int nominal = 50; - - ga->autofan = true; - /* Clamp fanspeed values to range provided */ - if (nominal > gpus[gpu].gpu_fan) - nominal = gpus[gpu].gpu_fan; - if (nominal < gpus[gpu].min_fan) - nominal = gpus[gpu].min_fan; - set_fanspeed(gpu, nominal); - } - if (opt_autoengine) { - ga->autoengine = true; - ga->managed = true; - } - ga->lasttemp = __gpu_temp(ga); - } - - for (gpu = 0; gpu < devices; gpu++) { - struct gpu_adl *ga = &gpus[gpu].adl; - int j; - - for (j = 0; j < devices; j++) { - struct gpu_adl *other_ga; - - if (j == gpu) - continue; - - other_ga = &gpus[j].adl; - - /* Search for twin GPUs on a single card. They will be - * separated by one bus id and one will have fanspeed - * while the other won't. */ - if (!ga->has_fanspeed) { - if (fanspeed_twin(ga, other_ga)) { - applog(LOG_INFO, "Dual GPUs detected: %d and %d", - ga->gpu, other_ga->gpu); - ga->twin = other_ga; - other_ga->twin = ga; - } - } - } - } -} - -static float __gpu_temp(struct gpu_adl *ga) -{ - if (ADL_Overdrive5_Temperature_Get(ga->iAdapterIndex, 0, &ga->lpTemperature) != ADL_OK) - return -1; - return (float)ga->lpTemperature.iTemperature / 1000; -} - -float gpu_temp(int gpu) -{ - struct gpu_adl *ga; - float ret = -1; - - if (!gpus[gpu].has_adl || !adl_active) - return ret; - - ga = &gpus[gpu].adl; - lock_adl(); - ret = __gpu_temp(ga); - unlock_adl(); - gpus[gpu].temp = ret; - return ret; -} - -static inline int __gpu_engineclock(struct gpu_adl *ga) -{ - return ga->lpActivity.iEngineClock / 100; -} - -int gpu_engineclock(int gpu) -{ - struct gpu_adl *ga; - int ret = -1; - - if (!gpus[gpu].has_adl || !adl_active) - return ret; - - ga = &gpus[gpu].adl; - lock_adl(); - if (ADL_Overdrive5_CurrentActivity_Get(ga->iAdapterIndex, &ga->lpActivity) != ADL_OK) - goto out; - ret = __gpu_engineclock(ga); -out: - unlock_adl(); - return ret; -} - -static inline int __gpu_memclock(struct gpu_adl *ga) -{ - return ga->lpActivity.iMemoryClock / 100; -} - -int gpu_memclock(int gpu) -{ - struct gpu_adl *ga; - int ret = -1; - - if (!gpus[gpu].has_adl || !adl_active) - return ret; - - ga = &gpus[gpu].adl; - lock_adl(); - if (ADL_Overdrive5_CurrentActivity_Get(ga->iAdapterIndex, &ga->lpActivity) != ADL_OK) - goto out; - ret = __gpu_memclock(ga); -out: - unlock_adl(); - return ret; -} - -static inline float __gpu_vddc(struct gpu_adl *ga) -{ - return (float)ga->lpActivity.iVddc / 1000; -} - -float gpu_vddc(int gpu) -{ - struct gpu_adl *ga; - float ret = -1; - - if (!gpus[gpu].has_adl || !adl_active) - return ret; - - ga = &gpus[gpu].adl; - lock_adl(); - if (ADL_Overdrive5_CurrentActivity_Get(ga->iAdapterIndex, &ga->lpActivity) != ADL_OK) - goto out; - ret = __gpu_vddc(ga); -out: - unlock_adl(); - return ret; -} - -static inline int __gpu_activity(struct gpu_adl *ga) -{ - if (!ga->lpOdParameters.iActivityReportingSupported) - return -1; - return ga->lpActivity.iActivityPercent; -} - -int gpu_activity(int gpu) -{ - struct gpu_adl *ga; - int ret = -1; - - if (!gpus[gpu].has_adl || !adl_active) - return ret; - - ga = &gpus[gpu].adl; - lock_adl(); - ret = ADL_Overdrive5_CurrentActivity_Get(ga->iAdapterIndex, &ga->lpActivity); - unlock_adl(); - if (ret != ADL_OK) - return ret; - if (!ga->lpOdParameters.iActivityReportingSupported) - return ret; - return ga->lpActivity.iActivityPercent; -} - -static inline int __gpu_fanspeed(struct gpu_adl *ga) -{ - if (!ga->has_fanspeed && ga->twin) - return __gpu_fanspeed(ga->twin); - - if (!(ga->lpFanSpeedInfo.iFlags & ADL_DL_FANCTRL_SUPPORTS_RPM_READ)) - return -1; - ga->lpFanSpeedValue.iSpeedType = ADL_DL_FANCTRL_SPEED_TYPE_RPM; - if (ADL_Overdrive5_FanSpeed_Get(ga->iAdapterIndex, 0, &ga->lpFanSpeedValue) != ADL_OK) - return -1; - return ga->lpFanSpeedValue.iFanSpeed; -} - -int gpu_fanspeed(int gpu) -{ - struct gpu_adl *ga; - int ret = -1; - - if (!gpus[gpu].has_adl || !adl_active) - return ret; - - ga = &gpus[gpu].adl; - lock_adl(); - ret = __gpu_fanspeed(ga); - unlock_adl(); - return ret; -} - -static int __gpu_fanpercent(struct gpu_adl *ga) -{ - if (!ga->has_fanspeed && ga->twin) - return __gpu_fanpercent(ga->twin); - - if (!(ga->lpFanSpeedInfo.iFlags & ADL_DL_FANCTRL_SUPPORTS_PERCENT_READ )) - return -1; - ga->lpFanSpeedValue.iSpeedType = ADL_DL_FANCTRL_SPEED_TYPE_PERCENT; - if (ADL_Overdrive5_FanSpeed_Get(ga->iAdapterIndex, 0, &ga->lpFanSpeedValue) != ADL_OK) - return -1; - return ga->lpFanSpeedValue.iFanSpeed; -} - -int gpu_fanpercent(int gpu) -{ - struct gpu_adl *ga; - int ret = -1; - - if (!gpus[gpu].has_adl || !adl_active) - return ret; - - ga = &gpus[gpu].adl; - lock_adl(); - ret = __gpu_fanpercent(ga); - unlock_adl(); - if (unlikely(ga->has_fanspeed && ret == -1)) { -#if 0 - /* Recursive calling applog causes a hang, so disable messages */ - applog(LOG_WARNING, "GPU %d stopped reporting fanspeed due to driver corruption", gpu); - if (opt_restart) { - applog(LOG_WARNING, "Restart enabled, will attempt to restart cgminer"); - applog(LOG_WARNING, "You can disable this with the --no-restart option"); - app_restart(); - } - applog(LOG_WARNING, "Disabling fanspeed monitoring on this device"); - ga->has_fanspeed = false; - if (ga->twin) { - applog(LOG_WARNING, "Disabling fanspeed linking on GPU twins"); - ga->twin->twin = NULL;; - ga->twin = NULL; - } -#endif - if (opt_restart) - app_restart(); - ga->has_fanspeed = false; - if (ga->twin) { - ga->twin->twin = NULL;; - ga->twin = NULL; - } - } - return ret; -} - -static inline int __gpu_powertune(struct gpu_adl *ga) -{ - int dummy = 0; - - if (ADL_Overdrive5_PowerControl_Get(ga->iAdapterIndex, &ga->iPercentage, &dummy) != ADL_OK) - return -1; - return ga->iPercentage; -} - -int gpu_powertune(int gpu) -{ - struct gpu_adl *ga; - int ret = -1; - - if (!gpus[gpu].has_adl || !adl_active) - return ret; - - ga = &gpus[gpu].adl; - lock_adl(); - ret = __gpu_powertune(ga); - unlock_adl(); - return ret; -} - -bool gpu_stats(int gpu, float *temp, int *engineclock, int *memclock, float *vddc, - int *activity, int *fanspeed, int *fanpercent, int *powertune) -{ - struct gpu_adl *ga; - - if (!gpus[gpu].has_adl || !adl_active) - return false; - - ga = &gpus[gpu].adl; - - lock_adl(); - *temp = __gpu_temp(ga); - if (ADL_Overdrive5_CurrentActivity_Get(ga->iAdapterIndex, &ga->lpActivity) != ADL_OK) { - *engineclock = 0; - *memclock = 0; - *vddc = 0; - *activity = 0; - } else { - *engineclock = __gpu_engineclock(ga); - *memclock = __gpu_memclock(ga); - *vddc = __gpu_vddc(ga); - *activity = __gpu_activity(ga); - } - *fanspeed = __gpu_fanspeed(ga); - *fanpercent = __gpu_fanpercent(ga); - *powertune = __gpu_powertune(ga); - unlock_adl(); - - return true; -} - -#ifdef HAVE_CURSES -static void get_enginerange(int gpu, int *imin, int *imax) -{ - struct gpu_adl *ga; - - if (!gpus[gpu].has_adl || !adl_active) { - wlogprint("Get enginerange not supported\n"); - return; - } - ga = &gpus[gpu].adl; - *imin = ga->lpOdParameters.sEngineClock.iMin / 100; - *imax = ga->lpOdParameters.sEngineClock.iMax / 100; -} -#endif - -int set_engineclock(int gpu, int iEngineClock) -{ - ADLODPerformanceLevels *lpOdPerformanceLevels; - struct cgpu_info *cgpu; - int i, lev, ret = 1; - struct gpu_adl *ga; - - if (!gpus[gpu].has_adl || !adl_active) { - wlogprint("Set engineclock not supported\n"); - return ret; - } - - iEngineClock *= 100; - ga = &gpus[gpu].adl; - - /* Keep track of intended engine clock in case the device changes - * profile and drops while idle, not taking the new engine clock */ - ga->lastengine = iEngineClock; - - lev = ga->lpOdParameters.iNumberOfPerformanceLevels - 1; - lpOdPerformanceLevels = alloca(sizeof(ADLODPerformanceLevels) + (lev * sizeof(ADLODPerformanceLevel))); - lpOdPerformanceLevels->iSize = sizeof(ADLODPerformanceLevels) + sizeof(ADLODPerformanceLevel) * lev; - - lock_adl(); - if (ADL_Overdrive5_ODPerformanceLevels_Get(ga->iAdapterIndex, 0, lpOdPerformanceLevels) != ADL_OK) - goto out; - for (i = 0; i < lev; i++) { - if (lpOdPerformanceLevels->aLevels[i].iEngineClock > iEngineClock) - lpOdPerformanceLevels->aLevels[i].iEngineClock = iEngineClock; - } - lpOdPerformanceLevels->aLevels[lev].iEngineClock = iEngineClock; - ADL_Overdrive5_ODPerformanceLevels_Set(ga->iAdapterIndex, lpOdPerformanceLevels); - ADL_Overdrive5_ODPerformanceLevels_Get(ga->iAdapterIndex, 0, lpOdPerformanceLevels); - if (lpOdPerformanceLevels->aLevels[lev].iEngineClock == iEngineClock) - ret = 0; - ga->iEngineClock = lpOdPerformanceLevels->aLevels[lev].iEngineClock; - if (ga->iEngineClock > ga->maxspeed) - ga->maxspeed = ga->iEngineClock; - if (ga->iEngineClock < ga->minspeed) - ga->minspeed = ga->iEngineClock; - ga->iMemoryClock = lpOdPerformanceLevels->aLevels[lev].iMemoryClock; - ga->iVddc = lpOdPerformanceLevels->aLevels[lev].iVddc; - ga->managed = true; -out: - unlock_adl(); - - cgpu = &gpus[gpu]; - if (cgpu->gpu_memdiff) - set_memoryclock(gpu, iEngineClock / 100 + cgpu->gpu_memdiff); - - return ret; -} - -#ifdef HAVE_CURSES -static void get_memoryrange(int gpu, int *imin, int *imax) -{ - struct gpu_adl *ga; - - if (!gpus[gpu].has_adl || !adl_active) { - wlogprint("Get memoryrange not supported\n"); - return; - } - ga = &gpus[gpu].adl; - *imin = ga->lpOdParameters.sMemoryClock.iMin / 100; - *imax = ga->lpOdParameters.sMemoryClock.iMax / 100; -} -#endif - -int set_memoryclock(int gpu, int iMemoryClock) -{ - ADLODPerformanceLevels *lpOdPerformanceLevels; - int i, lev, ret = 1; - struct gpu_adl *ga; - - if (!gpus[gpu].has_adl || !adl_active) { - wlogprint("Set memoryclock not supported\n"); - return ret; - } - - iMemoryClock *= 100; - ga = &gpus[gpu].adl; - - lev = ga->lpOdParameters.iNumberOfPerformanceLevels - 1; - lpOdPerformanceLevels = alloca(sizeof(ADLODPerformanceLevels) + (lev * sizeof(ADLODPerformanceLevel))); - lpOdPerformanceLevels->iSize = sizeof(ADLODPerformanceLevels) + sizeof(ADLODPerformanceLevel) * lev; - - lock_adl(); - if (ADL_Overdrive5_ODPerformanceLevels_Get(ga->iAdapterIndex, 0, lpOdPerformanceLevels) != ADL_OK) - goto out; - lpOdPerformanceLevels->aLevels[lev].iMemoryClock = iMemoryClock; - for (i = 0; i < lev; i++) { - if (lpOdPerformanceLevels->aLevels[i].iMemoryClock > iMemoryClock) - lpOdPerformanceLevels->aLevels[i].iMemoryClock = iMemoryClock; - } - ADL_Overdrive5_ODPerformanceLevels_Set(ga->iAdapterIndex, lpOdPerformanceLevels); - ADL_Overdrive5_ODPerformanceLevels_Get(ga->iAdapterIndex, 0, lpOdPerformanceLevels); - if (lpOdPerformanceLevels->aLevels[lev].iMemoryClock == iMemoryClock) - ret = 0; - ga->iEngineClock = lpOdPerformanceLevels->aLevels[lev].iEngineClock; - ga->iMemoryClock = lpOdPerformanceLevels->aLevels[lev].iMemoryClock; - ga->iVddc = lpOdPerformanceLevels->aLevels[lev].iVddc; - ga->managed = true; -out: - unlock_adl(); - return ret; -} - -#ifdef HAVE_CURSES -static void get_vddcrange(int gpu, float *imin, float *imax) -{ - struct gpu_adl *ga; - - if (!gpus[gpu].has_adl || !adl_active) { - wlogprint("Get vddcrange not supported\n"); - return; - } - ga = &gpus[gpu].adl; - *imin = (float)ga->lpOdParameters.sVddc.iMin / 1000; - *imax = (float)ga->lpOdParameters.sVddc.iMax / 1000; -} - -static float curses_float(const char *query) -{ - float ret; - char *cvar; - - cvar = curses_input(query); - ret = atof(cvar); - free(cvar); - return ret; -} -#endif - -int set_vddc(int gpu, float fVddc) -{ - ADLODPerformanceLevels *lpOdPerformanceLevels; - int i, iVddc, lev, ret = 1; - struct gpu_adl *ga; - - if (!gpus[gpu].has_adl || !adl_active) { - wlogprint("Set vddc not supported\n"); - return ret; - } - - iVddc = 1000 * fVddc; - ga = &gpus[gpu].adl; - - lev = ga->lpOdParameters.iNumberOfPerformanceLevels - 1; - lpOdPerformanceLevels = alloca(sizeof(ADLODPerformanceLevels) + (lev * sizeof(ADLODPerformanceLevel))); - lpOdPerformanceLevels->iSize = sizeof(ADLODPerformanceLevels) + sizeof(ADLODPerformanceLevel) * lev; - - lock_adl(); - if (ADL_Overdrive5_ODPerformanceLevels_Get(ga->iAdapterIndex, 0, lpOdPerformanceLevels) != ADL_OK) - goto out; - for (i = 0; i < lev; i++) { - if (lpOdPerformanceLevels->aLevels[i].iVddc > iVddc) - lpOdPerformanceLevels->aLevels[i].iVddc = iVddc; - } - lpOdPerformanceLevels->aLevels[lev].iVddc = iVddc; - ADL_Overdrive5_ODPerformanceLevels_Set(ga->iAdapterIndex, lpOdPerformanceLevels); - ADL_Overdrive5_ODPerformanceLevels_Get(ga->iAdapterIndex, 0, lpOdPerformanceLevels); - if (lpOdPerformanceLevels->aLevels[lev].iVddc == iVddc) - ret = 0; - ga->iEngineClock = lpOdPerformanceLevels->aLevels[lev].iEngineClock; - ga->iMemoryClock = lpOdPerformanceLevels->aLevels[lev].iMemoryClock; - ga->iVddc = lpOdPerformanceLevels->aLevels[lev].iVddc; - ga->managed = true; -out: - unlock_adl(); - return ret; -} - -static void get_fanrange(int gpu, int *imin, int *imax) -{ - struct gpu_adl *ga; - - if (!gpus[gpu].has_adl || !adl_active) { - wlogprint("Get fanrange not supported\n"); - return; - } - ga = &gpus[gpu].adl; - *imin = ga->lpFanSpeedInfo.iMinPercent; - *imax = ga->lpFanSpeedInfo.iMaxPercent; -} - -int set_fanspeed(int gpu, int iFanSpeed) -{ - struct gpu_adl *ga; - int ret = 1; - - if (!gpus[gpu].has_adl || !adl_active) { - wlogprint("Set fanspeed not supported\n"); - return ret; - } - - ga = &gpus[gpu].adl; - if (!(ga->lpFanSpeedInfo.iFlags & (ADL_DL_FANCTRL_SUPPORTS_RPM_WRITE | ADL_DL_FANCTRL_SUPPORTS_PERCENT_WRITE ))) { - applog(LOG_DEBUG, "GPU %d doesn't support rpm or percent write", gpu); - return ret; - } - - /* Store what fanspeed we're actually aiming for for re-entrant changes - * in case this device does not support fine setting changes */ - ga->targetfan = iFanSpeed; - - lock_adl(); - if (ADL_Overdrive5_FanSpeed_Get(ga->iAdapterIndex, 0, &ga->lpFanSpeedValue) != ADL_OK) { - applog(LOG_DEBUG, "GPU %d call to fanspeed get failed", gpu); - } - if (!(ga->lpFanSpeedInfo.iFlags & ADL_DL_FANCTRL_SUPPORTS_PERCENT_WRITE)) { - /* Must convert speed to an RPM */ - iFanSpeed = ga->lpFanSpeedInfo.iMaxRPM * iFanSpeed / 100; - ga->lpFanSpeedValue.iSpeedType = ADL_DL_FANCTRL_SPEED_TYPE_RPM; - } else - ga->lpFanSpeedValue.iSpeedType = ADL_DL_FANCTRL_SPEED_TYPE_PERCENT; - if (!(ga->lpFanSpeedValue.iFlags & ADL_DL_FANCTRL_FLAG_USER_DEFINED_SPEED)) { - /* If user defined is not already specified, set it first */ - ga->lpFanSpeedValue.iFlags = ADL_DL_FANCTRL_FLAG_USER_DEFINED_SPEED; - ADL_Overdrive5_FanSpeed_Set(ga->iAdapterIndex, 0, &ga->lpFanSpeedValue); - } - ga->lpFanSpeedValue.iFanSpeed = iFanSpeed; - ret = ADL_Overdrive5_FanSpeed_Set(ga->iAdapterIndex, 0, &ga->lpFanSpeedValue); - ga->managed = true; - unlock_adl(); - - return ret; -} - -#ifdef HAVE_CURSES -static int set_powertune(int gpu, int iPercentage) -{ - struct gpu_adl *ga; - int dummy, ret = 1; - - if (!gpus[gpu].has_adl || !adl_active) { - wlogprint("Set powertune not supported\n"); - return ret; - } - - ga = &gpus[gpu].adl; - - lock_adl(); - ADL_Overdrive5_PowerControl_Set(ga->iAdapterIndex, iPercentage); - ADL_Overdrive5_PowerControl_Get(ga->iAdapterIndex, &ga->iPercentage, &dummy); - if (ga->iPercentage == iPercentage) - ret = 0; - ga->managed = true; - unlock_adl(); - return ret; -} -#endif - -/* Returns whether the fanspeed is optimal already or not. The fan_window bool - * tells us whether the current fanspeed is in the target range for fanspeeds. - */ -static bool fan_autotune(int gpu, int temp, int fanpercent, int lasttemp, bool *fan_window) -{ - struct cgpu_info *cgpu = &gpus[gpu]; - int tdiff = round(temp - lasttemp); - struct gpu_adl *ga = &cgpu->adl; - int top = gpus[gpu].gpu_fan; - int bot = gpus[gpu].min_fan; - int newpercent = fanpercent; - int iMin = 0, iMax = 100; - - get_fanrange(gpu, &iMin, &iMax); - if (temp > ga->overtemp && fanpercent < iMax) { - applog(LOG_WARNING, "Overheat detected on GPU %d, increasing fan to 100%%", gpu); - newpercent = iMax; - - dev_error(cgpu, REASON_DEV_OVER_HEAT); - } else if (temp > ga->targettemp && fanpercent < top && tdiff >= 0) { - applog(LOG_DEBUG, "Temperature over target, increasing fanspeed"); - if (temp > ga->targettemp + opt_hysteresis) - newpercent = ga->targetfan + 10; - else - newpercent = ga->targetfan + 5; - if (newpercent > top) - newpercent = top; - } else if (fanpercent > bot && temp < ga->targettemp - opt_hysteresis) { - /* Detect large swings of 5 degrees or more and change fan by - * a proportion more */ - if (tdiff <= 0) { - applog(LOG_DEBUG, "Temperature %d degrees below target, decreasing fanspeed", opt_hysteresis); - newpercent = ga->targetfan - 1 + tdiff / 5; - } else if (tdiff >= 5) { - applog(LOG_DEBUG, "Temperature climbed %d while below target, increasing fanspeed", tdiff); - newpercent = ga->targetfan + tdiff / 5; - } - } else { - - /* We're in the optimal range, make minor adjustments if the - * temp is still drifting */ - if (fanpercent > bot && tdiff < 0 && lasttemp < ga->targettemp) { - applog(LOG_DEBUG, "Temperature dropping while in target range, decreasing fanspeed"); - newpercent = ga->targetfan + tdiff; - } else if (fanpercent < top && tdiff > 0 && temp > ga->targettemp - opt_hysteresis) { - applog(LOG_DEBUG, "Temperature rising while in target range, increasing fanspeed"); - newpercent = ga->targetfan + tdiff; - } - } - - if (newpercent > iMax) - newpercent = iMax; - else if (newpercent < iMin) - newpercent = iMin; - - if (newpercent <= top) - *fan_window = true; - else - *fan_window = false; - - if (newpercent != fanpercent) { - applog(LOG_INFO, "Setting GPU %d fan percentage to %d", gpu, newpercent); - set_fanspeed(gpu, newpercent); - /* If the fanspeed is going down and we're below the top speed, - * consider the fan optimal to prevent minute changes in - * fanspeed delaying GPU engine speed changes */ - if (newpercent < fanpercent && *fan_window) - return true; - return false; - } - return true; -} - -void gpu_autotune(int gpu, enum dev_enable *denable) -{ - int temp, fanpercent, engine, newengine, twintemp = 0; - bool fan_optimal = true, fan_window = true; - struct cgpu_info *cgpu; - struct gpu_adl *ga; - - cgpu = &gpus[gpu]; - ga = &cgpu->adl; - - lock_adl(); - ADL_Overdrive5_CurrentActivity_Get(ga->iAdapterIndex, &ga->lpActivity); - temp = __gpu_temp(ga); - if (ga->twin) - twintemp = __gpu_temp(ga->twin); - fanpercent = __gpu_fanpercent(ga); - unlock_adl(); - - newengine = engine = gpu_engineclock(gpu) * 100; - - if (temp && fanpercent >= 0 && ga->autofan) { - if (!ga->twin) - fan_optimal = fan_autotune(gpu, temp, fanpercent, ga->lasttemp, &fan_window); - else if (ga->autofan && (ga->has_fanspeed || !ga->twin->autofan)) { - /* On linked GPUs, we autotune the fan only once, based - * on the highest temperature from either GPUs */ - int hightemp, fan_gpu; - int lasttemp; - - if (twintemp > temp) { - lasttemp = ga->twin->lasttemp; - hightemp = twintemp; - } else { - lasttemp = ga->lasttemp; - hightemp = temp; - } - if (ga->has_fanspeed) - fan_gpu = gpu; - else - fan_gpu = ga->twin->gpu; - fan_optimal = fan_autotune(fan_gpu, hightemp, fanpercent, lasttemp, &fan_window); - } - } - - if (engine && ga->autoengine) { - if (temp > cgpu->cutofftemp) { - applog(LOG_WARNING, "Hit thermal cutoff limit on GPU %d, disabling!", gpu); - *denable = DEV_RECOVER; - newengine = ga->minspeed; - dev_error(cgpu, REASON_DEV_THERMAL_CUTOFF); - } else if (temp > ga->overtemp && engine > ga->minspeed) { - applog(LOG_WARNING, "Overheat detected, decreasing GPU %d clock speed", gpu); - newengine = ga->minspeed; - - dev_error(cgpu, REASON_DEV_OVER_HEAT); - } else if (temp > ga->targettemp + opt_hysteresis && engine > ga->minspeed && fan_optimal) { - applog(LOG_DEBUG, "Temperature %d degrees over target, decreasing clock speed", opt_hysteresis); - newengine = engine - ga->lpOdParameters.sEngineClock.iStep; - /* Only try to tune engine speed up if this GPU is not disabled */ - } else if (temp < ga->targettemp && engine < ga->maxspeed && fan_window && *denable == DEV_ENABLED) { - int iStep = ga->lpOdParameters.sEngineClock.iStep; - - applog(LOG_DEBUG, "Temperature below target, increasing clock speed"); - if (temp < ga->targettemp - opt_hysteresis) - iStep *= 2; - newengine = engine + iStep; - } else if (temp < ga->targettemp && *denable == DEV_RECOVER && opt_restart) { - applog(LOG_NOTICE, "Device recovered to temperature below target, re-enabling"); - *denable = DEV_ENABLED; - } - - if (newengine > ga->maxspeed) - newengine = ga->maxspeed; - else if (newengine < ga->minspeed) - newengine = ga->minspeed; - - /* Adjust engine clock speed if it's lower, or if it's higher - * but higher than the last intended value as well as the - * current speed, to avoid setting the engine clock speed to - * a speed relateive to a lower profile during idle periods. */ - if (newengine < engine || (newengine > engine && newengine > ga->lastengine)) { - newengine /= 100; - applog(LOG_INFO, "Setting GPU %d engine clock to %d", gpu, newengine); - set_engineclock(gpu, newengine); - } - } - ga->lasttemp = temp; -} - -void set_defaultfan(int gpu) -{ - struct gpu_adl *ga; - if (!gpus[gpu].has_adl || !adl_active) - return; - - ga = &gpus[gpu].adl; - lock_adl(); - ADL_Overdrive5_FanSpeed_Set(ga->iAdapterIndex, 0, &ga->DefFanSpeedValue); - unlock_adl(); -} - -void set_defaultengine(int gpu) -{ - struct gpu_adl *ga; - if (!gpus[gpu].has_adl || !adl_active) - return; - - ga = &gpus[gpu].adl; - lock_adl(); - ADL_Overdrive5_ODPerformanceLevels_Set(ga->iAdapterIndex, ga->DefPerfLev); - unlock_adl(); -} - -#ifdef HAVE_CURSES -void change_autosettings(int gpu) -{ - struct gpu_adl *ga = &gpus[gpu].adl; - char input; - int val; - - wlogprint("Target temperature: %d\n", ga->targettemp); - wlogprint("Overheat temperature: %d\n", ga->overtemp); - wlogprint("Cutoff temperature: %d\n", gpus[gpu].cutofftemp); - wlogprint("Toggle [F]an auto [G]PU auto\nChange [T]arget [O]verheat [C]utoff\n"); - wlogprint("Or press any other key to continue\n"); - input = getch(); - if (!strncasecmp(&input, "f", 1)) { - ga->autofan ^= true; - wlogprint("Fan autotune is now %s\n", ga->autofan ? "enabled" : "disabled"); - if (!ga->autofan) { - wlogprint("Resetting fan to startup settings\n"); - set_defaultfan(gpu); - } - } else if (!strncasecmp(&input, "g", 1)) { - ga->autoengine ^= true; - wlogprint("GPU engine clock autotune is now %s\n", ga->autoengine ? "enabled" : "disabled"); - if (!ga->autoengine) { - wlogprint("Resetting GPU engine clock to startup settings\n"); - set_defaultengine(gpu); - } - } else if (!strncasecmp(&input, "t", 1)) { - val = curses_int("Enter target temperature for this GPU in C (0-200)"); - if (val < 0 || val > 200) - wlogprint("Invalid temperature"); - else - ga->targettemp = val; - } else if (!strncasecmp(&input, "o", 1)) { - wlogprint("Enter overheat temperature for this GPU in C (%d+)", ga->targettemp); - val = curses_int(""); - if (val <= ga->targettemp || val > 200) - wlogprint("Invalid temperature"); - else - ga->overtemp = val; - } else if (!strncasecmp(&input, "c", 1)) { - wlogprint("Enter cutoff temperature for this GPU in C (%d+)", ga->overtemp); - val = curses_int(""); - if (val <= ga->overtemp || val > 200) - wlogprint("Invalid temperature"); - else - gpus[gpu].cutofftemp = val; - } -} - -void change_gpusettings(int gpu) -{ - struct gpu_adl *ga = &gpus[gpu].adl; - float fval, fmin = 0, fmax = 0; - int val, imin = 0, imax = 0; - char input; - int engineclock = 0, memclock = 0, activity = 0, fanspeed = 0, fanpercent = 0, powertune = 0; - float temp = 0, vddc = 0; - -updated: - if (gpu_stats(gpu, &temp, &engineclock, &memclock, &vddc, &activity, &fanspeed, &fanpercent, &powertune)) - wlogprint("Temp: %.1f C\n", temp); - if (fanpercent >= 0 || fanspeed >= 0) { - wlogprint("Fan Speed: "); - if (fanpercent >= 0) - wlogprint("%d%% ", fanpercent); - if (fanspeed >= 0) - wlogprint("(%d RPM)", fanspeed); - wlogprint("\n"); - } - wlogprint("Engine Clock: %d MHz\nMemory Clock: %d Mhz\nVddc: %.3f V\nActivity: %d%%\nPowertune: %d%%\n", - engineclock, memclock, vddc, activity, powertune); - wlogprint("Fan autotune is %s (%d-%d)\n", ga->autofan ? "enabled" : "disabled", - gpus[gpu].min_fan, gpus[gpu].gpu_fan); - wlogprint("GPU engine clock autotune is %s (%d-%d)\n", ga->autoengine ? "enabled" : "disabled", - ga->minspeed / 100, ga->maxspeed / 100); - wlogprint("Change [A]utomatic [E]ngine [F]an [M]emory [V]oltage [P]owertune\n"); - wlogprint("Or press any other key to continue\n"); - input = getch(); - - if (!strncasecmp(&input, "a", 1)) { - change_autosettings(gpu); - } else if (!strncasecmp(&input, "e", 1)) { - get_enginerange(gpu, &imin, &imax); - wlogprint("Enter GPU engine clock speed (%d - %d Mhz)", imin, imax); - val = curses_int(""); - if (val < imin || val > imax) { - wlogprint("Value is outside safe range, are you sure?\n"); - input = getch(); - if (strncasecmp(&input, "y", 1)) - return; - } - if (!set_engineclock(gpu, val)) - wlogprint("Driver reports success but check values below\n"); - else - wlogprint("Failed to modify engine clock speed\n"); - } else if (!strncasecmp(&input, "f", 1)) { - get_fanrange(gpu, &imin, &imax); - wlogprint("Enter fan percentage (%d - %d %%)", imin, imax); - val = curses_int(""); - if (val < imin || val > imax) { - wlogprint("Value is outside safe range, are you sure?\n"); - input = getch(); - if (strncasecmp(&input, "y", 1)) - return; - } - if (!set_fanspeed(gpu, val)) - wlogprint("Driver reports success but check values below\n"); - else - wlogprint("Failed to modify fan speed\n"); - } else if (!strncasecmp(&input, "m", 1)) { - get_memoryrange(gpu, &imin, &imax); - wlogprint("Enter GPU memory clock speed (%d - %d Mhz)", imin, imax); - val = curses_int(""); - if (val < imin || val > imax) { - wlogprint("Value is outside safe range, are you sure?\n"); - input = getch(); - if (strncasecmp(&input, "y", 1)) - return; - } - if (!set_memoryclock(gpu, val)) - wlogprint("Driver reports success but check values below\n"); - else - wlogprint("Failed to modify memory clock speed\n"); - } else if (!strncasecmp(&input, "v", 1)) { - get_vddcrange(gpu, &fmin, &fmax); - wlogprint("Enter GPU voltage (%.3f - %.3f V)", fmin, fmax); - fval = curses_float(""); - if (fval < fmin || fval > fmax) { - wlogprint("Value is outside safe range, are you sure?\n"); - input = getch(); - if (strncasecmp(&input, "y", 1)) - return; - } - if (!set_vddc(gpu, fval)) - wlogprint("Driver reports success but check values below\n"); - else - wlogprint("Failed to modify voltage\n"); - } else if (!strncasecmp(&input, "p", 1)) { - val = curses_int("Enter powertune value (-20 - 20)"); - if (val < -20 || val > 20) { - wlogprint("Value is outside safe range, are you sure?\n"); - input = getch(); - if (strncasecmp(&input, "y", 1)) - return; - } - if (!set_powertune(gpu, val)) - wlogprint("Driver reports success but check values below\n"); - else - wlogprint("Failed to modify powertune value\n"); - } else { - clear_logwin(); - return; - } - cgsleep_ms(1000); - goto updated; -} -#endif - -static void free_adl(void) -{ - ADL_Main_Memory_Free ((void **)&lpInfo); - ADL_Main_Control_Destroy (); -#if defined (LINUX) - dlclose(hDLL); -#else - FreeLibrary(hDLL); -#endif -} - -void clear_adl(int nDevs) -{ - struct gpu_adl *ga; - int i; - - if (!adl_active) - return; - - lock_adl(); - /* Try to reset values to their defaults */ - for (i = 0; i < nDevs; i++) { - ga = &gpus[i].adl; - /* Only reset the values if we've changed them at any time */ - if (!gpus[i].has_adl || !ga->managed) - continue; - ADL_Overdrive5_ODPerformanceLevels_Set(ga->iAdapterIndex, ga->DefPerfLev); - free(ga->DefPerfLev); - ADL_Overdrive5_FanSpeed_Set(ga->iAdapterIndex, 0, &ga->DefFanSpeedValue); - ADL_Overdrive5_FanSpeedToDefault_Set(ga->iAdapterIndex, 0); - } - adl_active = false; - unlock_adl(); - free_adl(); -} -#endif /* HAVE_ADL */ diff --git a/adl.h b/adl.h deleted file mode 100644 index ca67550c1c..0000000000 --- a/adl.h +++ /dev/null @@ -1,28 +0,0 @@ -#ifndef __ADL_H__ -#define __ADL_H__ -#ifdef HAVE_ADL -bool adl_active; -bool opt_reorder; -int opt_hysteresis; -const int opt_targettemp; -const int opt_overheattemp; -void init_adl(int nDevs); -float gpu_temp(int gpu); -int gpu_engineclock(int gpu); -int gpu_memclock(int gpu); -float gpu_vddc(int gpu); -int gpu_activity(int gpu); -int gpu_fanspeed(int gpu); -int gpu_fanpercent(int gpu); -bool gpu_stats(int gpu, float *temp, int *engineclock, int *memclock, float *vddc, - int *activity, int *fanspeed, int *fanpercent, int *powertune); -void change_gpusettings(int gpu); -void gpu_autotune(int gpu, enum dev_enable *denable); -void clear_adl(int nDevs); -#else /* HAVE_ADL */ -#define adl_active (0) -static inline void init_adl(__maybe_unused int nDevs) {} -static inline void change_gpusettings(__maybe_unused int gpu) { } -static inline void clear_adl(__maybe_unused int nDevs) {} -#endif -#endif diff --git a/adl_functions.h b/adl_functions.h deleted file mode 100644 index 6d4a9a1b0f..0000000000 --- a/adl_functions.h +++ /dev/null @@ -1,274 +0,0 @@ -/******************************************************************************* - - * This program reads HW information from your ATI Radeon card and displays them - * You can also change frequencies and voltages. - - * THIS PROGRAM MAY DAMAGE YOUR VIDEO CARD, IF YOU APPLY NONSENSIAL VALUES. - * e.g. INCREASING THE VOLTAGES AND FREQUENCIES IN CONJUNCTION WITH LOWERING THE - * FAN SPEED IS NOT ADVISABLE! - - * Copyright(C) Thorsten Gilling (tgilling@web.de) - - * 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 2 - * of the License, or (at your option) any later version. - - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. - -*******************************************************************************/ - -// ------------------------------------------------------------------------------------------------------------ -// AMD ADL function types from Version 3.0 -// ------------------------------------------------------------------------------------------------------------ - -#if defined (linux) - #include //dyopen, dlsym, dlclose - #include - #include //memeset -#else - #include - #include -#endif - -#include "ADL_SDK/adl_sdk.h" - -// Definitions of the used function pointers. Add more if you use other ADL APIs - -// ------------------------------------------------------------------------------------------------------------ - -// ADL Main -typedef int ( *ADL_MAIN_CONTROL_CREATE ) (ADL_MAIN_MALLOC_CALLBACK callback, int iEnumConnectedAdapters); -typedef int ( *ADL_MAIN_CONTROL_REFRESH ) (); -typedef int ( *ADL_MAIN_CONTROL_DESTROY ) (); -typedef int ( *ADL_GRAPHICS_PLATFORM_GET ) (int *lpPlatForm); - -// ------------------------------------------------------------------------------------------------------------ - -// ADL Adapter/General -typedef int ( *ADL_ADAPTER_ACTIVE_GET ) (int iAdapterIndex, int *lpStatus); -typedef int ( *ADL_ADAPTER_NUMBEROFADAPTERS_GET ) (int *lpNumAdapters); -typedef int ( *ADL_ADAPTER_ADAPTERINFO_GET ) (LPAdapterInfo lpInfo, int iInputSize); -typedef int ( *ADL_ADAPTER_ASICFAMILYTYPE_GET ) (int iAdapterIndex, int *lpAsicTypes, int *lpValids); -typedef int ( *ADL_ADAPTER_SPEED_CAPS ) (int iAdapterIndex, int *lpCaps, int *lpValid); -typedef int ( *ADL_ADAPTER_SPEED_GET ) (int iAdapterIndex, int *lpCurrent, int *lpDefault); -typedef int ( *ADL_ADAPTER_SPEED_SET ) (int iAdapterIndex, int iSpeed); -typedef int ( *ADL_ADAPTER_ACCESSIBILITY_GET ) (int iAdapterIndex, int *lpAccessibility); -typedef int ( *ADL_ADAPTER_VIDEOBIOSINFO_GET ) (int iAdapterIndex, ADLBiosInfo *lpBiosInfo); -typedef int ( *ADL_ADAPTER_ID_GET ) (int iAdapterIndex, int *lpAdapterID); - -// ADL Adapter/CrossDisplay -typedef int ( *ADL_ADAPTER_CROSSDISPLAYADAPTERROLE_CAPS ) (int iAdapterIndex, int *lpCrossDisplaySupport, int *lpAdapterRole, int *lpNumPossDisplayAdapters, int **lppPossDisplayAdapters, int *lpNnumPosRenderingAdapters, int **lppPosRenderingAdapters, int *lpErrorStatus); -typedef int ( *ADL_ADAPTER_CROSSDISPLAYINFO_GET ) (int iAdapterIndex, int *lpAdapterRole, int *lpCrossdisplayMode, int *lpNumDisplayAdapters, int **lppDisplayAdapters, int *lpNumRenderingAdapters, int **lppRenderingAdapters, int *lpErrorCodeStatus); -typedef int ( *ADL_ADAPTER_CROSSDISPLAYINFO_SET ) (int iAdapterIndex, int iDisplayAdapterIndex, int iRenderingAdapterIndex, int crossdisplayMode, int *lpErrorCode); - -// ADL Adapter/CrossFire -typedef int ( *ADL_ADAPTER_CROSSFIRE_CAPS ) (int iAdapterIndex, int *lpPreferred, int *lpNumComb, ADLCrossfireComb **ppCrossfireComb); -typedef int ( *ADL_ADAPTER_CROSSFIRE_GET ) (int iAdapterIndex, ADLCrossfireComb *lpCrossfireComb, ADLCrossfireInfo *lpCrossfireInfo); -typedef int ( *ADL_ADAPTER_CROSSFIRE_SET ) (int iAdapterIndex, ADLCrossfireComb *lpCrossfireComb); - -// ------------------------------------------------------------------------------------------------------------ - -// ADL Display/Misc - -typedef int ( *ADL_DISPLAY_DISPLAYINFO_GET ) (int iAdapterIndex, int *lpNumDisplays, ADLDisplayInfo **lppInfo, int iForceDetect); -typedef int ( *ADL_DISPLAY_NUMBEROFDISPLAYS_GET ) (int iAdapterIndex, int *lpNumDisplays); -typedef int ( *ADL_DISPLAY_PRESERVEDASPECTRATIO_GET ) (int iAdapterIndex, int iDisplayIndex, int *lpSupport, int *lpCurrent, int *lpDefault); -typedef int ( *ADL_DISPLAY_PRESERVEDASPECTRATIO_SET ) (int iAdapterIndex, int iDisplayIndex, int iCurrent); -typedef int ( *ADL_DISPLAY_IMAGEEXPANSION_GET ) (int iAdapterIndex, int iDisplayIndex, int *lpSupport, int *lpCurrent, int *lpDefault); -typedef int ( *ADL_DISPLAY_IMAGEEXPANSION_SET ) (int iAdapterIndex, int iDisplayIndex, int iCurrent); -typedef int ( *ADL_DISPLAY_POSITION_GET ) (int iAdapterIndex, int iDisplayIndex, int *lpX, int *lpY, int *lpXDefault, int *lpYDefault, int *lpMinX, int *lpMinY, int *lpMaxX, int *lpMaxY, int *lpStepX, int *lpStepY); -typedef int ( *ADL_DISPLAY_POSITION_SET ) (int iAdapterIndex, int iDisplayIndex, int iX, int iY); -typedef int ( *ADL_DISPLAY_SIZE_GET ) (int iAdapterIndex, int iDisplayIndex, int *lpWidth, int *lpHeight, int *lpDefaultWidth, int *lpDefaultHeight, int *lpMinWidth, int *lpMinHeight, int *lpMaxWidth, int *lpMaxHeight, int *lpStepWidth, int *lpStepHeight); -typedef int ( *ADL_DISPLAY_SIZE_SET ) (int iAdapterIndex, int iDisplayIndex, int iWidth, int iHeight); -typedef int ( *ADL_DISPLAY_ADJUSTCAPS_GET ) (int iAdapterIndex, int iDisplayIndex, int *lpInfo); -typedef int ( *ADL_DISPLAY_CAPABILITIES_GET ) (int iAdapterIndex, int *lpNumberOfControlers, int *lpNumberOfDisplays); -typedef int ( *ADL_DISPLAY_CONNECTEDDISPLAYS_GET ) (int iAdapterIndex, int *lpConnections); -typedef int ( *ADL_DISPLAY_DEVICECONFIG_GET ) (int iAdapterIndex, int iDisplayIndex, ADLDisplayConfig *lpDisplayConfig); -typedef int ( *ADL_DISPLAY_PROPERTY_GET ) (int iAdapterIndex, int iDisplayIndex, ADLDisplayProperty *lpDisplayProperty); -typedef int ( *ADL_DISPLAY_PROPERTY_SET ) (int iAdapterIndex, int iDisplayIndex, ADLDisplayProperty *lpDisplayProperty); -typedef int ( *ADL_DISPLAY_SWITCHINGCAPABILITY_GET ) (int iAdapterIndex, int *lpResult); -typedef int ( *ADL_DISPLAY_DITHERSTATE_GET ) (int iAdapterIndex, int iDisplayIndex, int *lpDitherState); -typedef int ( *ADL_DISPLAY_DITHERSTATE_SET ) (int iAdapterIndex, int iDisplayIndex, int iDitherState); -typedef int ( *ADL_DISPLAY_SUPPORTEDPIXELFORMAT_GET ) (int iAdapterIndex, int iDisplayIndex, int *lpPixelFormat); -typedef int ( *ADL_DISPLAY_PIXELFORMAT_GET ) (int iAdapterIndex, int iDisplayIndex, int *lpPixelFormat); -typedef int ( *ADL_DISPLAY_PIXELFORMAT_SET ) (int iAdapterIndex, int iDisplayIndex, int iPixelFormat); -typedef int ( *ADL_DISPLAY_ODCLOCKINFO_GET ) (int iAdapterIndex, ADLAdapterODClockInfo *lpOdClockInfo); -typedef int ( *ADL_DISPLAY_ODCLOCKCONFIG_SET ) (int iAdapterIndex, ADLAdapterODClockConfig *lpOdClockConfig); -typedef int ( *ADL_DISPLAY_ADJUSTMENTCOHERENT_GET ) (int iAdapterIndex, int iDisplayIndex, int *lpAdjustmentCoherentCurrent, int *lpAdjustmentCoherentDefault); -typedef int ( *ADL_DISPLAY_ADJUSTMENTCOHERENT_SET ) (int iAdapterIndex, int iDisplayIndex, int iAdjustmentCoherent); -typedef int ( *ADL_DISPLAY_REDUCEDBLANKING_GET ) (int iAdapterIndex, int iDisplayIndex, int *lpReducedBlankingCurrent, int *lpReducedBlankingDefault); -typedef int ( *ADL_DISPLAY_REDUCEDBLANKING_SET ) (int iAdapterIndex, int iDisplayIndex, int iReducedBlanking); -typedef int ( *ADL_DISPLAY_FORMATSOVERRIDE_GET ) (int iAdapterIndex, int iDisplayIndex, int *lpSettingsSupported, int *lpSettingsSupportedEx, int *lpCurSettings); -typedef int ( *ADL_DISPLAY_FORMATSOVERRIDE_SET ) (int iAdapterIndex, int iDisplayIndex, int iOverrideSettings); -typedef int ( *ADL_DISPLAY_MVPUCAPS_GET ) (int iAdapterIndex, ADLMVPUCaps *lpMvpuCaps); -typedef int ( *ADL_DISPLAY_MVPUSTATUS_GET ) (int iAdapterIndex, ADLMVPUStatus *lpMvpuStatus); - -// ADL Display/Eyefinity -typedef int ( *ADL_ADAPTER_ACTIVE_SET ) (int iAdapterIndex, int iStatus, int *lpNewlyActivate); -typedef int ( *ADL_ADAPTER_ACTIVE_SETPREFER ) (int iAdapterIndex, int iStatus, int iNumPreferTarget, ADLDisplayTarget *lpPreferTarget, int *lpNewlyActivate); -typedef int ( *ADL_ADAPTER_PRIMARY_GET ) (int *lpPrimaryAdapterIndex); -typedef int ( *ADL_ADAPTER_PRIMARY_SET ) (int iAdapterIndex); -typedef int ( *ADL_ADAPTER_MODESWITCH ) (int iAdapterIndex); -typedef int ( *ADL_DISPLAY_MODES_GET ) (int iAdapterIndex, int iDisplayIndex, int *lpNumModes, ADLMode **lppModes); -typedef int ( *ADL_DISPLAY_MODES_SET ) (int iAdapterIndex, int iDisplayIndex, int iNumModes, ADLMode *lpModes); -typedef int ( *ADL_DISPLAY_POSSIBLEMODE_GET ) (int iAdapterIndex, int *lpNumModes, ADLMode **lppModes); -typedef int ( *ADL_DISPLAY_FORCIBLEDISPLAY_GET ) (int iAdapterIndex, int iDisplayIndex, int *lpStatus); -typedef int ( *ADL_DISPLAY_FORCIBLEDISPLAY_SET ) (int iAdapterIndex, int iDisplayIndex, int iStatus); -typedef int ( *ADL_ADAPTER_NUMBEROFACTIVATABLESOURCES_GET ) (int iAdapterIndex, int *lpNumSources, ADLActivatableSource **lppSources); -typedef int ( *ADL_ADAPTER_DISPLAY_CAPS ) (int iAdapterIndex, int *lpNumDisplayCaps, ADLAdapterDisplayCap **lppAdapterDisplayCaps); -typedef int ( *ADL_DISPLAY_DISPLAYMAPCONFIG_GET ) (int iAdapterIndex, int *lpNumDisplayMap, ADLDisplayMap **lppDisplayMap, int *lpNumDisplayTarget, ADLDisplayTarget **lppDisplayTarget, int iOptions); -typedef int ( *ADL_DISPLAY_DISPLAYMAPCONFIG_SET ) (int iAdapterIndex, int iNumDisplayMap, ADLDisplayMap *lpDisplayMap, int iNumDisplayTarget, ADLDisplayTarget *lpDisplayTarget); -typedef int ( *ADL_DISPLAY_POSSIBLEMAPPING_GET ) (int iAdapterIndex, int iNumberOfPresetMapping, ADLPossibleMapping *lpPresetMappings, int iEnquiryControllerIndex, int *lpNumberOfEnquiryPossibleMappings, ADLPossibleMapping **lppEnquiryPossibleMappings); -typedef int ( *ADL_DISPLAY_DISPLAYMAPCONFIG_VALIDATE ) (int iAdapterIndex, int iNumPossibleMap, ADLPossibleMap *lpPossibleMaps, int *lpNumPossibleMapResult, ADLPossibleMapResult **lppPossibleMapResult); -typedef int ( *ADL_DISPLAY_DISPLAYMAPCONFIG_POSSIBLEADDANDREMOVE ) (int iAdapterIndex, int iNumDisplayMap, ADLDisplayMap *lpDisplayMap, int iNumDisplayTarget, ADLDisplayTarget *lpDisplayTarget, int *lpNumPossibleAddTarget, ADLDisplayTarget **lppPossibleAddTarget, int *lpNumPossibleRemoveTarget, ADLDisplayTarget **lppPossibleRemoveTarget); -typedef int ( *ADL_DISPLAY_SLSGRID_CAPS ) (int iAdapterIndex, int *lpNumSLSGrid, ADLSLSGrid **lppSLSGrid, int iOption); -typedef int ( *ADL_DISPLAY_SLSMAPINDEXLIST_GET ) (int iAdapterIndex, int *lpNumSLSMapIndexList, int **lppSLSMapIndexList, int iOptions); -typedef int ( *ADL_DISPLAY_SLSMAPINDEX_GET ) (int iAdapterIndex, int iADLNumDisplayTarget, ADLDisplayTarget *lpDisplayTarget, int *lpSLSMapIndex); -typedef int ( *ADL_DISPLAY_SLSMAPCONFIG_GET ) (int iAdapterIndex, int iSLSMapIndex, ADLSLSMap *lpSLSMap, int *lpNumSLSTarget, ADLSLSTarget **lppSLSTarget, int *lpNumNativeMode, ADLSLSMode **lppNativeMode, int *lpNumBezelMode, ADLBezelTransientMode **lppBezelMode, int *lpNumTransientMode, ADLBezelTransientMode **lppTransientMode, int *lpNumSLSOffset, ADLSLSOffset **lppSLSOffset, int iOption); -typedef int ( *ADL_DISPLAY_SLSMAPCONFIG_CREATE ) (int iAdapterIndex, ADLSLSMap SLSMap, int iNumTargetTarget, ADLSLSTarget *lpSLSTarget, int iBezelModePercent, int *lpSLSMapIndex, int iOption); -typedef int ( *ADL_DISPLAY_SLSMAPCONFIG_DELETE ) (int iAdapterIndex, int iSLSMapIndex); -typedef int ( *ADL_DISPLAY_SLSMAPCONFIG_SETSTATE ) (int iAdapterIndex, int iSLSMapIndex, int iState); -typedef int ( *ADL_DISPLAY_SLSMAPCONFIG_REARRANGE ) (int iAdapterIndex, int iSLSMapIndex, int iNumDisplayTarget, ADLSLSTarget *lpSLSTarget, ADLSLSMap slsMap, int iOption); -typedef int ( *ADL_DISPLAY_POSSIBLEMODE_WINXP_GET ) (int iAdapterIndex, int iNumDisplayTargets, ADLDisplayTarget *lpDisplayTargets, int iLargeDesktopSupportedType, int iDevicePanningControl, int *lpNumModes, ADLMode **lppModes); -typedef int ( *ADL_DISPLAY_BEZELOFFSETSTEPPINGSIZE_GET ) (int iAdapterIndex, int *lpNumBezelOffsetSteppingSize, ADLBezelOffsetSteppingSize **lppBezelOffsetSteppingSize); -typedef int ( *ADL_DISPLAY_BEZELOFFSET_SET ) (int iAdapterIndex, int iSLSMapIndex, int iNumBezelOffset, LPADLSLSOffset lpBezelOffset, ADLSLSMap SLSMap, int iOption); -typedef int ( *ADL_DISPLAY_BEZELSUPPORTED_VALIDATE ) (int iAdapterIndex, int iNumPossibleSLSMap, LPADLPossibleSLSMap lpPossibleSLSMaps, int *lpNumPossibleSLSMapResult, LPADLPossibleMapResult *lppPossibleMapResult); - -// ADL Display/Color -typedef int ( *ADL_DISPLAY_COLORCAPS_GET ) (int iAdapterIndex, int iDisplayIndex, int *lpCaps, int *lpValids); -typedef int ( *ADL_DISPLAY_COLOR_SET ) (int iAdapterIndex, int iDisplayIndex, int iColorType, int iCurrent); -typedef int ( *ADL_DISPLAY_COLOR_GET ) (int iAdapterIndex, int iDisplayIndex, int iColorType, int *lpCurrent, int *lpDefault, int *lpMin, int *lpMax, int *lpStep); -typedef int ( *ADL_DISPLAY_COLORTEMPERATURESOURCE_GET ) (int iAdapterIndex, int iDisplayIndex, int *lpTempSource); -typedef int ( *ADL_DISPLAY_COLORTEMPERATURESOURCE_SET ) (int iAdapterIndex, int iDisplayIndex, int iTempSource); - -// ADL Display/Timing -typedef int ( *ADL_DISPLAY_MODETIMINGOVERRIDE_GET ) (int iAdapterIndex, int iDisplayIndex, ADLDisplayMode *lpModeIn, ADLDisplayModeInfo *lpModeInfoOut); -typedef int ( *ADL_DISPLAY_MODETIMINGOVERRIDE_SET ) (int iAdapterIndex, int iDisplayIndex, ADLDisplayModeInfo *lpMode, int iForceUpdate); -typedef int ( *ADL_DISPLAY_MODETIMINGOVERRIDELIST_GET ) (int iAdapterIndex, int iDisplayIndex, int iMaxNumOfOverrides, ADLDisplayModeInfo *lpModeInfoList, int *lpNumOfOverrides); - -// ADL Display/Customize -typedef int ( *ADL_DISPLAY_CUSTOMIZEDMODELISTNUM_GET ) (int iAdapterIndex, int iDisplayIndex, int *lpListNum); -typedef int ( *ADL_DISPLAY_CUSTOMIZEDMODELIST_GET ) (int iAdapterIndex, int iDisplayIndex, ADLCustomMode *lpCustomModeList, int iBuffSize); -typedef int ( *ADL_DISPLAY_CUSTOMIZEDMODE_ADD ) (int iAdapterIndex, int iDisplayIndex, ADLCustomMode customMode); -typedef int ( *ADL_DISPLAY_CUSTOMIZEDMODE_DELETE ) (int iAdapterIndex, int iDisplayIndex, int iIndex); -typedef int ( *ADL_DISPLAY_CUSTOMIZEDMODE_VALIDATE ) (int iAdapterIndex, int iDisplayIndex, ADLCustomMode customMode, int *lpValid); - -// ADL Display/Over-Underscan -typedef int ( *ADL_DISPLAY_UNDERSCAN_SET ) (int iAdapterIndex, int iDisplayIndex, int iCurrent); -typedef int ( *ADL_DISPLAY_UNDERSCAN_GET ) (int iAdapterIndex, int iDisplayIndex, int *lpCurrent, int *lpDefault, int *lpMin, int *lpMax, int *lpStep); -typedef int ( *ADL_DISPLAY_OVERSCAN_SET ) (int iAdapterIndex, int iDisplayIndex, int iCurrent); -typedef int ( *ADL_DISPLAY_OVERSCAN_GET ) (int iAdapterIndex, int iDisplayIndex, int *lpCurrent, int *lpDefualt, int *lpMin, int *lpMax, int *lpStep); - -// ADL Display/Overlay -typedef int ( *ADL_DISPLAY_CONTROLLEROVERLAYADJUSTMENTCAPS_GET ) (int iAdapterIndex, ADLControllerOverlayInput *lpOverlayInput, ADLControllerOverlayInfo *lpCapsInfo); -typedef int ( *ADL_DISPLAY_CONTROLLEROVERLAYADJUSTMENTDATA_GET ) (int iAdapterIndex, ADLControllerOverlayInput *lpOverlay); -typedef int ( *ADL_DISPLAY_CONTROLLEROVERLAYADJUSTMENTDATA_SET ) (int iAdapterIndex, ADLControllerOverlayInput *lpOverlay); - -// ADL Display/PowerXpress -typedef int ( *ADL_DISPLAY_POWERXPRESSVERSION_GET ) (int iAdapterIndex, int *lpVersion); -typedef int ( *ADL_DISPLAY_POWERXPRESSACTIVEGPU_GET ) (int iAdapterIndex, int *lpActiveGPU); -typedef int ( *ADL_DISPLAY_POWERXPRESSACTIVEGPU_SET ) (int iAdapterIndex, int iActiveGPU, int *lpOperationResult); -typedef int ( *ADL_DISPLAY_POWERXPRESS_AUTOSWITCHCONFIG_GET ) (int iAdapterIndex, int *lpAutoSwitchOnACDCEvent, int *lpAutoSwitchOnDCACEvent); -typedef int ( *ADL_DISPLAY_POWERXPRESS_AUTOSWITCHCONFIG_SET ) (int iAdapterIndex, int iAutoSwitchOnACDCEvent, int iAutoSwitchOnDCACEvent); - -// ------------------------------------------------------------------------------------------------------------ - -// ADL DFP -typedef int ( *ADL_DFP_BASEAUDIOSUPPORT_GET ) (int iAdapterIndex, int iDisplayIndex, int *lpSupport); -typedef int ( *ADL_DFP_HDMISUPPORT_GET ) (int iAdapterIndex, int iDisplayIndex, int *lpSupport); -typedef int ( *ADL_DFP_MVPUANALOGSUPPORT_GET ) (int iAdapterIndex, int iDisplayIndex, int *lpSupport); -typedef int ( *ADL_DFP_PIXELFORMAT_CAPS ) (int iAdapterIndex, int iDisplayIndex, int *lpValidBits, int *lpValidCaps); -typedef int ( *ADL_DFP_PIXELFORMAT_GET ) (int iAdapterIndex, int iDisplayIndex, int *lpCurState, int *lpDefault); -typedef int ( *ADL_DFP_PIXELFORMAT_SET ) (int iAdapterIndex, int iDisplayIndex, int iState); -typedef int ( *ADL_DFP_GPUSCALINGENABLE_GET ) (int iAdapterIndex, int iDisplayIndex, int *lpSupport, int *lpCurrent, int *lpDefault); -typedef int ( *ADL_DFP_GPUSCALINGENABLE_SET ) (int iAdapterIndex, int iDisplayIndex, int iCurrent); -typedef int ( *ADL_DFP_ALLOWONLYCETIMINGS_GET ) (int iAdapterIndex, int iDisplayIndex, int *lpSupport, int *lpCurrent, int *lpDefault); -typedef int ( *ADL_DFP_ALLOWONLYCETIMINGS_SET ) (int iAdapterIndex, int iDisplayIndex, int iCurrent); - -// ADl TV -typedef int ( *ADL_DISPLAY_TVCAPS_GET ) (int iAdapterIndex, int iDisplayIndex, int *lpcaps); -typedef int ( *ADL_TV_STANDARD_SET ) (int iAdapterIndex, int iDisplayIndex, int iCurrent); -typedef int ( *ADL_TV_STANDARD_GET ) (int iAdapterIndex, int iDisplayIndex, int *lpCurrent, int *lpDefault, int *lpSupportedStandards); - -// ADL Component Video -typedef int ( *ADL_CV_DONGLESETTINGS_GET ) (int iAdapterIndex, int iDisplayIndex, int *lpDongleSetting, int *lpOverrideSettingsSupported, int *lpCurOverrideSettings); -typedef int ( *ADL_CV_DONGLESETTINGS_SET ) (int iAdapterIndex, int iDisplayIndex, int iOverrideSettings); -typedef int ( *ADL_CV_DONGLESETTINGS_RESET ) (int iAdapterIndex, int iDisplayIndex); - -// ------------------------------------------------------------------------------------------------------------ - -// ADL Overdrive 5 -typedef int ( *ADL_OVERDRIVE5_CURRENTACTIVITY_GET ) (int iAdapterIndex, ADLPMActivity *lpActivity); -typedef int ( *ADL_OVERDRIVE5_THERMALDEVICES_ENUM ) (int iAdapterIndex, int iThermalControllerIndex, ADLThermalControllerInfo *lpThermalControllerInfo); -typedef int ( *ADL_OVERDRIVE5_TEMPERATURE_GET ) (int iAdapterIndex, int iThermalControllerIndex, ADLTemperature *lpTemperature); -typedef int ( *ADL_OVERDRIVE5_FANSPEEDINFO_GET ) (int iAdapterIndex, int iThermalControllerIndex, ADLFanSpeedInfo *lpFanSpeedInfo); -typedef int ( *ADL_OVERDRIVE5_FANSPEED_GET ) (int iAdapterIndex, int iThermalControllerIndex, ADLFanSpeedValue *lpFanSpeedValue); -typedef int ( *ADL_OVERDRIVE5_FANSPEED_SET ) (int iAdapterIndex, int iThermalControllerIndex, ADLFanSpeedValue *lpFanSpeedValue); -typedef int ( *ADL_OVERDRIVE5_FANSPEEDTODEFAULT_SET ) (int iAdapterIndex, int iThermalControllerIndex); -typedef int ( *ADL_OVERDRIVE5_ODPARAMETERS_GET ) (int iAdapterIndex, ADLODParameters *lpOdParameters); -typedef int ( *ADL_OVERDRIVE5_ODPERFORMANCELEVELS_GET ) (int iAdapterIndex, int iDefault, ADLODPerformanceLevels *lpOdPerformanceLevels); -typedef int ( *ADL_OVERDRIVE5_ODPERFORMANCELEVELS_SET ) (int iAdapterIndex, ADLODPerformanceLevels *lpOdPerformanceLevels); - -// ------------------------------------------------------------------------------------------------------------ - -// ADL I2C -typedef int ( *ADL_DISPLAY_WRITEANDREADI2CREV_GET ) (int iAdapterIndex, int *lpMajor, int *lpMinor); -typedef int ( *ADL_DISPLAY_WRITEANDREADI2C ) (int iAdapterIndex, ADLI2C *plI2C); -typedef int ( *ADL_DISPLAY_DDCBLOCKACCESS_GET ) (int iAdapterIndex, int iDisplayIndex, int iOption, int iCommandIndex, int iSendMsgLen, char *lpucSendMsgBuf, int *lpulRecvMsgLen, char *lpucRecvMsgBuf); -typedef int ( *ADL_DISPLAY_DDCINFO_GET ) (int iAdapterIndex, int iDisplayIndex, ADLDDCInfo *lpInfo); -typedef int ( *ADL_DISPLAY_EDIDDATA_GET ) (int iAdapterIndex, int iDisplayIndex, ADLDisplayEDIDData *lpEDIDData); - -// ------------------------------------------------------------------------------------------------------------ - -// ADL Workstation -typedef int ( *ADL_WORKSTATION_CAPS ) (int iAdapterIndex, int *lpValidBits, int *lpCaps); -typedef int ( *ADL_WORKSTATION_STEREO_GET ) (int iAdapterIndex, int *lpDefState, int *lpCurState); -typedef int ( *ADL_WORKSTATION_STEREO_SET ) (int iAdapterIndex, int iCurState); -typedef int ( *ADL_WORKSTATION_ADAPTERNUMOFGLSYNCCONNECTORS_GET ) (int iAdapterIndex, int *lpNumOfGLSyncConnectors); -typedef int ( *ADL_WORKSTATION_DISPLAYGENLOCKCAPABLE_GET ) (int iAdapterIndex, int iDisplayIndex, int *lpCanGenlock); -typedef int ( *ADL_WORKSTATION_GLSYNCMODULEDETECT_GET ) (int iAdapterIndex, int iGlSyncConnector, ADLGLSyncModuleID *lpGlSyncModuleID); -typedef int ( *ADL_WORKSTATION_GLSYNCMODULEINFO_GET ) (int iAdapterIndex, int iGlSyncConnector, int *lpNumGLSyncGPUPorts, int *lpNumGlSyncPorts, int *lpMaxSyncDelay, int *lpMaxSampleRate, ADLGLSyncPortCaps **ppGlSyncPorts); -typedef int ( *ADL_WORKSTATION_GLSYNCGENLOCKCONFIGURATION_GET ) (int iAdapterIndex, int iGlSyncConnector, int iGlValidMask, ADLGLSyncGenlockConfig *lpGlSyncGenlockConfig); -typedef int ( *ADL_WORKSTATION_GLSYNCGENLOCKCONFIGURATION_SET ) (int iAdapterIndex, int iGlSyncConnector, ADLGLSyncGenlockConfig glSyncGenlockConfig); -typedef int ( *ADL_WORKSTATION_GLSYNCPORTSTATE_GET ) (int iAdapterIndex, int iGlSyncConnector, int iGlSyncPortType, int iNumLEDs, ADLGlSyncPortInfo *lpGlSyncPortInfo, int **ppGlSyncLEDs); -typedef int ( *ADL_WORKSTATION_GLSYNCPORTSTATE_SET ) (int iAdapterIndex, int iGlSyncConnector, ADLGlSyncPortControl glSyncPortControl); -typedef int ( *ADL_WORKSTATION_DISPLAYGLSYNCMODE_GET ) (int iAdapterIndex, int iDisplayIndex, ADLGlSyncMode *lpGlSyncMode); -typedef int ( *ADL_WORKSTATION_DISPLAYGLSYNCMODE_SET ) (int iAdapterIndex, int iDisplayIndex, ADLGlSyncMode glSyncMode); -typedef int ( *ADL_WORKSTATION_GLSYNCSUPPORTEDTOPOLOGY_GET ) (int iAdapterIndex, int iNumSyncModes, ADLGlSyncMode2 *glSyncModes, int *iNumSugSyncModes, ADLGlSyncMode2 **glSugSyncModes); -typedef int ( *ADL_WORKSTATION_LOADBALANCING_GET ) (int *lpResultMask, int *lpCurResultValue, int *lpDefResultValue); -typedef int ( *ADL_WORKSTATION_LOADBALANCING_SET ) (int iCurState); -typedef int ( *ADL_WORKSTATION_LOADBALANCING_CAPS ) (int iAdapterIndex, int *lpResultMask, int *lpResultValue); - -// ------------------------------------------------------------------------------------------------------------ - -#ifdef LINUX -// ADL Linux -typedef int ( *ADL_ADAPTER_MEMORYINFO_GET ) (int iAdapterIndex, ADLMemoryInfo *lpMemoryInfo); -typedef int ( *ADL_CONTROLLER_COLOR_SET ) (int iAdapterIndex, int iControllerIndex, ADLGamma adlGamma); -typedef int ( *ADL_CONTROLLER_COLOR_GET ) (int iAdapterIndex, int iControllerIndex, ADLGamma *lpGammaCurrent, ADLGamma *lpGammaDefault, ADLGamma *lpGammaMin, ADLGamma *lpGammaMax); -typedef int ( *ADL_DESKTOPCONFIG_GET ) (int iAdapterIndex, int *lpDesktopConfig); -typedef int ( *ADL_DESKTOPCONFIG_SET ) (int iAdapterIndex, int iDesktopConfig); -typedef int ( *ADL_NUMBEROFDISPLAYENABLE_GET ) (int iAdapterIndex, int *lpNumberOfDisplays); -typedef int ( *ADL_DISPLAYENABLE_SET ) (int iAdapterIndex, int *lpDisplayIndexList, int iDisplayListSize, int bPersistOnly); -typedef int ( *ADL_DISPLAY_IDENTIFYDISPLAY ) (int iAdapterIndex, int iDisplayIndex, int iDisplayControllerIndex, int iShow, int iDisplayNum, int iPosX, int iPosY); -typedef int ( *ADL_DISPLAY_LUTCOLOR_SET ) (int iAdapterIndex, int iDisplayIndex, ADLGamma adlGamma); -typedef int ( *ADL_DISPLAY_LUTCOLOR_GET ) (int iAdapterIndex, int iDisplayIndex, ADLGamma *lpGammaCurrent, ADLGamma *lpGammaDefault, ADLGamma *lpGammaMin, ADLGamma *lpGammaMax); -typedef int ( *ADL_ADAPTER_XSCREENINFO_GET ) (LPXScreenInfo lpXScreenInfo, int iInputSize); -typedef int ( *ADL_DISPLAY_XRANDRDISPLAYNAME_GET ) (int iAdapterIndex, int iDisplayIndex, char *lpXrandrDisplayName, int iBuffSize); -#endif -// ------------------------------------------------------------------------------------------------------------ - - -// experimental undocumented -typedef int ( *ADL_OVERDRIVE5_POWERCONTROL_GET ) (int iAdapterIndex, int* iPercentage, int* whatever); -typedef int ( *ADL_OVERDRIVE5_POWERCONTROL_SET ) (int iAdapterIndex, int iPercentage); -//typedef int ( *ADL_OVERDRIVE5_POWERCONTROL_CAPS ) (int iAdapterIndex, int* lpCaps, int* lpValid); -//typedef int ( *ADL_OVERDRIVE5_POWERCONTROLINFO_GET) (int iAdapterIndex, ...) \ No newline at end of file diff --git a/api-example.c b/api-example.c index 71b5b00260..5046b5209f 100644 --- a/api-example.c +++ b/api-example.c @@ -8,7 +8,7 @@ */ /* Compile: - * gcc api-example.c -I compat/jansson -o cgminer-api + * gcc api-example.c -Icompat/jansson-2.6/src -Icompat/libusb-1.0/libusb -o cgminer-api */ #include "config.h" @@ -32,7 +32,6 @@ #include #include - #define SOCKETTYPE int #define SOCKETFAIL(a) ((a) < 0) #define INVSOCK -1 #define CLOSESOCKET close @@ -140,8 +139,6 @@ #endif #endif -#define RECVSIZE 65500 - static const char SEPARATOR = '|'; static const char COMMA = ','; static const char EQ = '='; @@ -190,18 +187,25 @@ void display(char *buf) } } +#define SOCKSIZ 65535 + int callapi(char *command, char *host, short int port) { - char buf[RECVSIZE+1]; struct hostent *ip; struct sockaddr_in serv; SOCKETTYPE sock; int ret = 0; - int n, p; + int n; + char *buf = NULL; + size_t len, p; SOCKETINIT; ip = gethostbyname(host); + if (!ip) { + printf("Couldn't get hostname: '%s'\n", host); + return 1; + } sock = socket(AF_INET, SOCK_STREAM, 0); if (sock == INVSOCK) { @@ -225,10 +229,24 @@ int callapi(char *command, char *host, short int port) ret = 1; } else { + len = SOCKSIZ; + buf = malloc(len+1); + if (!buf) { + printf("Err: OOM (%d)\n", (int)(len+1)); + return 1; + } p = 0; - buf[0] = '\0'; - while (p < RECVSIZE) { - n = recv(sock, &buf[p], RECVSIZE - p , 0); + while (42) { + if ((len - p) < 1) { + len += SOCKSIZ; + buf = realloc(buf, len+1); + if (!buf) { + printf("Err: OOM (%d)\n", (int)(len+1)); + return 1; + } + } + + n = recv(sock, &buf[p], len - p , 0); if (SOCKETFAIL(n)) { printf("Recv failed: %s\n", SOCKERRMSG); @@ -240,8 +258,8 @@ int callapi(char *command, char *host, short int port) break; p += n; - buf[p] = '\0'; } + buf[p] = '\0'; if (ONLY) printf("%s\n", buf); diff --git a/api-example.rb b/api-example.rb new file mode 100644 index 0000000000..28c4402f91 --- /dev/null +++ b/api-example.rb @@ -0,0 +1,38 @@ +#!/usr/bin/env ruby + +# Copyright 2014 James Hilliard +# +# 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. + +require 'socket' +require 'json' + +api_command = ARGV[0].split(":") + +if ARGV.length == 3 + api_ip = ARGV[1] + api_port = ARGV[2] +elsif ARGV.length == 2 + api_ip = ARGV[1] + api_port = 4028 +else + api_ip = "127.0.0.1" + api_port = 4028 +end + +s = TCPSocket.open(api_ip, api_port) + +if api_command.count == 2 + s.write({ :command => api_command[0], :parameter => api_command[1]}.to_json) +else + s.write({ :command => api_command[0]}.to_json) +end + +response = s.read.strip +response = JSON.parse(response) + +puts response +s.close diff --git a/api.c b/api.c index cb9df4a3b6..a9b7df67e3 100644 --- a/api.c +++ b/api.c @@ -1,15 +1,11 @@ /* - * Copyright 2011-2013 Andrew Smith - * Copyright 2011-2012 Con Kolivas + * Copyright 2011-2014 Andrew Smith + * Copyright 2011-2014 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. - * - * Note: the code always includes GPU support even if there are no GPUs - * this simplifies handling multiple other device code being included - * depending on compile options */ #define _MEMORY_DEBUG_MASTER 1 @@ -22,27 +18,27 @@ #include #include #include +#include #include #include "compat.h" #include "miner.h" #include "util.h" - -#if defined(USE_BFLSC) || defined(USE_AVALON) +#include "klist.h" + +#if defined(USE_BFLSC) || defined(USE_AVALON) || defined(USE_AVALON2) || defined(USE_AVALON4) || \ + defined(USE_HASHFAST) || defined(USE_BITFURY) || defined(USE_BLOCKERUPTER) || defined(USE_KLONDIKE) || \ + defined(USE_KNC) || defined(USE_BAB) || defined(USE_DRILLBIT) || \ + defined(USE_MINION) || defined(USE_COINTERRA) || defined(USE_BITMINE_A1) || \ + defined(USE_BMSC) || defined(USE_BITMAIN) || defined(USE_SP10) || defined(USE_SP30) || \ + defined(USE_ICARUS) || defined(USE_HASHRATIO) #define HAVE_AN_ASIC 1 #endif -#if defined(USE_BITFORCE) || defined(USE_ICARUS) || defined(USE_ZTEX) || defined(USE_MODMINER) +#if defined(USE_BITFORCE) || defined(USE_MODMINER) #define HAVE_AN_FPGA 1 #endif -// Big enough for largest API request -// though a PC with 100s of PGAs may exceed the size ... -// data is truncated at the end of the last record that fits -// but still closed correctly for JSON -// Current code assumes it can socket send this size + JSON_CLOSE + JSON_END -#define SOCKBUFSIZ 65432 - // BUFSIZ varies on Windows and Linux #define TMPBUFSIZ 8192 @@ -125,6 +121,11 @@ char *WSAErrorMsg(void) { } #endif +#if defined(__APPLE__) +#define IPV6_ADD_MEMBERSHIP IPV6_JOIN_GROUP +#define IPV6_DROP_MEMBERSHIP IPV6_LEAVE_GROUP +#endif + static const char *UNAVAILABLE = " - API will not be available"; static const char *MUNAVAILABLE = " - API multicast listener will not be available"; @@ -133,23 +134,19 @@ static const char *COMMA = ","; #define COMSTR "," static const char SEPARATOR = '|'; #define SEPSTR "|" -static const char GPUSEP = ','; +#define CMDJOIN '+' +#define JOIN_CMD "CMD=" +#define BETWEEN_JOIN SEPSTR -static const char *APIVERSION = "1.29"; +static const char *APIVERSION = "3.1"; static const char *DEAD = "Dead"; -#if defined(HAVE_OPENCL) || defined(HAVE_AN_FPGA) || defined(HAVE_AN_ASIC) static const char *SICK = "Sick"; static const char *NOSTART = "NoStart"; static const char *INIT = "Initialising"; -#endif static const char *DISABLED = "Disabled"; static const char *ALIVE = "Alive"; static const char *REJECTING = "Rejecting"; static const char *UNKNOWN = "Unknown"; -#define _DYNAMIC "D" -#ifdef HAVE_OPENCL -static const char *DYNAMIC = _DYNAMIC; -#endif static __maybe_unused const char *NONE = "None"; @@ -160,14 +157,20 @@ static const char *NULLSTR = "(null)"; static const char *TRUESTR = "true"; static const char *FALSESTR = "false"; -#ifdef USE_SCRYPT -static const char *SCRYPTSTR = "scrypt"; -#endif static const char *SHA256STR = "sha256"; static const char *DEVICECODE = "" -#ifdef HAVE_OPENCL - "GPU " +#ifdef USE_BMSC + "BTM " +#endif +#ifdef USE_BITMAIN + "BTM " +#endif +#ifdef USE_AVALON + "AVA " +#endif +#ifdef USE_BAB + "BaB " #endif #ifdef USE_BFLSC "BAS " @@ -175,18 +178,47 @@ static const char *DEVICECODE = "" #ifdef USE_BITFORCE "BFL " #endif +#ifdef USE_BITFURY + "BFU " +#endif +#ifdef USE_BLOCKERUPTER + "BET " +#endif +#ifdef USE_DRILLBIT + "DRB " +#endif +#ifdef USE_HASHFAST + "HFA " +#endif +#ifdef USE_HASHRATIO + "HRO " +#endif +#ifdef USE_BITMINE_A1 + "BA1 " +#endif #ifdef USE_ICARUS "ICA " #endif -#ifdef USE_AVALON - "AVA " +#ifdef USE_KNC + "KnC " #endif -#ifdef USE_ZTEX - "ZTX " +#ifdef USE_MINION + "MBA " #endif #ifdef USE_MODMINER "MMQ " #endif +#ifdef USE_COINTERRA + "CTA " +#endif +#ifdef USE_SP10 + "SPN " +#endif +#ifdef USE_SP30 + "S30 " +#endif + + ""; static const char *OSINFO = @@ -214,7 +246,6 @@ static const char *OSINFO = #define _STATUS "STATUS" #define _VERSION "VERSION" #define _MINECONFIG "CONFIG" -#define _GPU "GPU" #ifdef HAVE_AN_FPGA #define _PGA "PGA" @@ -224,7 +255,6 @@ static const char *OSINFO = #define _ASC "ASC" #endif -#define _GPUS "GPUS" #define _PGAS "PGAS" #define _ASCS "ASCS" #define _NOTIFY "NOTIFY" @@ -237,6 +267,7 @@ static const char *OSINFO = #define _DEBUGSET "DEBUG" #define _SETCONFIG "SETCONFIG" #define _USBSTATS "USBSTATS" +#define _LCD "LCD" static const char ISJSON = '{'; #define JSON0 "{" @@ -247,6 +278,7 @@ static const char ISJSON = '{'; // If anyone cares, id=0 for truncated output #define JSON4_TRUNCATED ",\"id\":0" #define JSON5 "}" +#define JSON6 "\":" #define JSON_START JSON0 #define JSON_DEVS JSON1 _DEVS JSON2 @@ -255,7 +287,7 @@ static const char ISJSON = '{'; #define JSON_STATUS JSON1 _STATUS JSON2 #define JSON_VERSION JSON1 _VERSION JSON2 #define JSON_MINECONFIG JSON1 _MINECONFIG JSON2 -#define JSON_GPU JSON1 _GPU JSON2 +#define JSON_ACTION JSON0 JSON1 _STATUS JSON6 #ifdef HAVE_AN_FPGA #define JSON_PGA JSON1 _PGA JSON2 @@ -265,7 +297,6 @@ static const char ISJSON = '{'; #define JSON_ASC JSON1 _ASC JSON2 #endif -#define JSON_GPUS JSON1 _GPUS JSON2 #define JSON_PGAS JSON1 _PGAS JSON2 #define JSON_ASCS JSON1 _ASCS JSON2 #define JSON_NOTIFY JSON1 _NOTIFY JSON2 @@ -279,30 +310,21 @@ static const char ISJSON = '{'; #define JSON_DEBUGSET JSON1 _DEBUGSET JSON2 #define JSON_SETCONFIG JSON1 _SETCONFIG JSON2 #define JSON_USBSTATS JSON1 _USBSTATS JSON2 +#define JSON_LCD JSON1 _LCD JSON2 #define JSON_END JSON4 JSON5 #define JSON_END_TRUNCATED JSON4_TRUNCATED JSON5 +#define JSON_BETWEEN_JOIN "," static const char *JSON_COMMAND = "command"; static const char *JSON_PARAMETER = "parameter"; -#define MSG_INVGPU 1 -#define MSG_ALRENA 2 -#define MSG_ALRDIS 3 -#define MSG_GPUMRE 4 -#define MSG_GPUREN 5 -#define MSG_GPUNON 6 #define MSG_POOL 7 #define MSG_NOPOOL 8 #define MSG_DEVS 9 #define MSG_NODEVS 10 #define MSG_SUMM 11 -#define MSG_GPUDIS 12 -#define MSG_GPUREI 13 #define MSG_INVCMD 14 #define MSG_MISID 15 -#define MSG_GPUDEV 17 - -#define MSG_NUMGPU 20 #define MSG_VERSION 22 #define MSG_INVJSON 23 @@ -312,18 +334,8 @@ static const char *JSON_PARAMETER = "parameter"; #define MSG_SWITCHP 27 #define MSG_MISVAL 28 #define MSG_NOADL 29 -#define MSG_NOGPUADL 30 #define MSG_INVINT 31 -#define MSG_GPUINT 32 #define MSG_MINECONFIG 33 -#define MSG_GPUMERR 34 -#define MSG_GPUMEM 35 -#define MSG_GPUEERR 36 -#define MSG_GPUENG 37 -#define MSG_GPUVERR 38 -#define MSG_GPUVDDC 39 -#define MSG_GPUFERR 40 -#define MSG_GPUFAN 41 #define MSG_MISFN 42 #define MSG_BADFN 43 #define MSG_SAVED 44 @@ -422,6 +434,12 @@ static const char *JSON_PARAMETER = "parameter"; #define MSG_ASCSETERR 120 #endif +#define MSG_INVNEG 121 +#define MSG_SETQUOTA 122 +#define MSG_LOCKOK 123 +#define MSG_LOCKDIS 124 +#define MSG_LCD 125 + enum code_severity { SEVERITY_ERR, SEVERITY_WARN, @@ -431,11 +449,9 @@ enum code_severity { }; enum code_parameters { - PARAM_GPU, PARAM_PGA, PARAM_ASC, PARAM_PID, - PARAM_GPUMAX, PARAM_PGAMAX, PARAM_ASCMAX, PARAM_PMAX, @@ -460,57 +476,36 @@ struct CODES { const enum code_parameters params; const char *description; } codes[] = { -#ifdef HAVE_OPENCL - { SEVERITY_ERR, MSG_INVGPU, PARAM_GPUMAX, "Invalid GPU id %d - range is 0 - %d" }, - { SEVERITY_INFO, MSG_ALRENA, PARAM_GPU, "GPU %d already enabled" }, - { SEVERITY_INFO, MSG_ALRDIS, PARAM_GPU, "GPU %d already disabled" }, - { SEVERITY_WARN, MSG_GPUMRE, PARAM_GPU, "GPU %d must be restarted first" }, - { SEVERITY_INFO, MSG_GPUREN, PARAM_GPU, "GPU %d sent enable message" }, -#endif - { SEVERITY_ERR, MSG_GPUNON, PARAM_NONE, "No GPUs" }, { SEVERITY_SUCC, MSG_POOL, PARAM_PMAX, "%d Pool(s)" }, { SEVERITY_ERR, MSG_NOPOOL, PARAM_NONE, "No pools" }, { SEVERITY_SUCC, MSG_DEVS, PARAM_DMAX, -#ifdef HAVE_OPENCL - "%d GPU(s)" -#endif -#if defined(HAVE_AN_ASIC) && defined(HAVE_OPENCL) - " - " -#endif #ifdef HAVE_AN_ASIC "%d ASC(s)" #endif -#if defined(HAVE_AN_FPGA) && (defined(HAVE_OPENCL) || defined(HAVE_AN_ASIC)) +#if defined(HAVE_AN_ASIC) && defined(HAVE_AN_FPGA) " - " #endif #ifdef HAVE_AN_FPGA "%d PGA(s)" -#endif -#if (defined(HAVE_OPENCL) || defined(HAVE_AN_ASIC) || defined(HAVE_AN_FPGA)) - " - " #endif }, - { SEVERITY_ERR, MSG_NODEVS, PARAM_NONE, "No GPUs" + { SEVERITY_ERR, MSG_NODEVS, PARAM_NONE, "No " #ifdef HAVE_AN_ASIC - "/ASCs" + "ASCs" +#endif +#if defined(HAVE_AN_ASIC) && defined(HAVE_AN_FPGA) + "/" #endif #ifdef HAVE_AN_FPGA - "/PGAs" + "PGAs" #endif }, { SEVERITY_SUCC, MSG_SUMM, PARAM_NONE, "Summary" }, -#ifdef HAVE_OPENCL - { SEVERITY_INFO, MSG_GPUDIS, PARAM_GPU, "GPU %d set disable flag" }, - { SEVERITY_INFO, MSG_GPUREI, PARAM_GPU, "GPU %d restart attempted" }, -#endif { SEVERITY_ERR, MSG_INVCMD, PARAM_NONE, "Invalid command" }, { SEVERITY_ERR, MSG_MISID, PARAM_NONE, "Missing device id parameter" }, -#ifdef HAVE_OPENCL - { SEVERITY_SUCC, MSG_GPUDEV, PARAM_GPU, "GPU%d" }, -#endif #ifdef HAVE_AN_FPGA { SEVERITY_ERR, MSG_PGANON, PARAM_NONE, "No PGAs" }, { SEVERITY_SUCC, MSG_PGADEV, PARAM_PGA, "PGA%d" }, @@ -521,7 +516,6 @@ struct CODES { { SEVERITY_INFO, MSG_PGADIS, PARAM_PGA, "PGA %d set disable flag" }, { SEVERITY_ERR, MSG_PGAUNW, PARAM_PGA, "PGA %d is not flagged WELL, cannot enable" }, #endif - { SEVERITY_SUCC, MSG_NUMGPU, PARAM_NONE, "GPU count" }, { SEVERITY_SUCC, MSG_NUMPGA, PARAM_NONE, "PGA count" }, { SEVERITY_SUCC, MSG_NUMASC, PARAM_NONE, "ASC count" }, { SEVERITY_SUCC, MSG_VERSION, PARAM_NONE, "CGMiner versions" }, @@ -530,22 +524,7 @@ struct CODES { { SEVERITY_ERR, MSG_MISPID, PARAM_NONE, "Missing pool id parameter" }, { SEVERITY_ERR, MSG_INVPID, PARAM_POOLMAX, "Invalid pool id %d - range is 0 - %d" }, { SEVERITY_SUCC, MSG_SWITCHP, PARAM_POOL, "Switching to pool %d:'%s'" }, - { SEVERITY_ERR, MSG_MISVAL, PARAM_NONE, "Missing comma after GPU number" }, - { SEVERITY_ERR, MSG_NOADL, PARAM_NONE, "ADL is not available" }, - { SEVERITY_ERR, MSG_NOGPUADL,PARAM_GPU, "GPU %d does not have ADL" }, - { SEVERITY_ERR, MSG_INVINT, PARAM_STR, "Invalid intensity (%s) - must be '" _DYNAMIC "' or range " MIN_SHA_INTENSITY_STR " - " MAX_SCRYPT_INTENSITY_STR }, - { SEVERITY_INFO, MSG_GPUINT, PARAM_BOTH, "GPU %d set new intensity to %s" }, { SEVERITY_SUCC, MSG_MINECONFIG,PARAM_NONE, "CGMiner config" }, -#ifdef HAVE_OPENCL - { SEVERITY_ERR, MSG_GPUMERR, PARAM_BOTH, "Setting GPU %d memoryclock to (%s) reported failure" }, - { SEVERITY_SUCC, MSG_GPUMEM, PARAM_BOTH, "Setting GPU %d memoryclock to (%s) reported success" }, - { SEVERITY_ERR, MSG_GPUEERR, PARAM_BOTH, "Setting GPU %d clock to (%s) reported failure" }, - { SEVERITY_SUCC, MSG_GPUENG, PARAM_BOTH, "Setting GPU %d clock to (%s) reported success" }, - { SEVERITY_ERR, MSG_GPUVERR, PARAM_BOTH, "Setting GPU %d vddc to (%s) reported failure" }, - { SEVERITY_SUCC, MSG_GPUVDDC, PARAM_BOTH, "Setting GPU %d vddc to (%s) reported success" }, - { SEVERITY_ERR, MSG_GPUFERR, PARAM_BOTH, "Setting GPU %d fan to (%s) reported failure" }, - { SEVERITY_SUCC, MSG_GPUFAN, PARAM_BOTH, "Setting GPU %d fan to (%s) reported success" }, -#endif { SEVERITY_ERR, MSG_MISFN, PARAM_NONE, "Missing save filename parameter" }, { SEVERITY_ERR, MSG_BADFN, PARAM_STR, "Can't open or create save file '%s'" }, { SEVERITY_SUCC, MSG_SAVED, PARAM_STR, "Configuration saved to file '%s'" }, @@ -561,7 +540,7 @@ struct CODES { { SEVERITY_ERR, MSG_MISPDP, PARAM_NONE, "Missing addpool details" }, { SEVERITY_ERR, MSG_INVPDP, PARAM_STR, "Invalid addpool details '%s'" }, { SEVERITY_ERR, MSG_TOOMANYP,PARAM_NONE, "Reached maximum number of pools (%d)" }, - { SEVERITY_SUCC, MSG_ADDPOOL, PARAM_STR, "Added pool '%s'" }, + { SEVERITY_SUCC, MSG_ADDPOOL, PARAM_POOL, "Added pool %d: '%s'" }, { SEVERITY_ERR, MSG_REMLASTP,PARAM_POOL, "Cannot remove last pool %d:'%s'" }, { SEVERITY_ERR, MSG_ACTPOOL, PARAM_POOL, "Cannot remove active pool %d:'%s'" }, { SEVERITY_SUCC, MSG_REMPOOL, PARAM_BOTH, "Removed pool %d:'%s'" }, @@ -582,6 +561,8 @@ struct CODES { { SEVERITY_SUCC, MSG_SETCONFIG,PARAM_SET, "Set config '%s' to %d" }, { SEVERITY_ERR, MSG_UNKCON, PARAM_STR, "Unknown config '%s'" }, { SEVERITY_ERR, MSG_INVNUM, PARAM_BOTH, "Invalid number (%d) for '%s' range is 0-9999" }, + { SEVERITY_ERR, MSG_INVNEG, PARAM_BOTH, "Invalid negative number (%d) for '%s'" }, + { SEVERITY_SUCC, MSG_SETQUOTA,PARAM_SET, "Set pool '%s' to quota %d'" }, { SEVERITY_ERR, MSG_CONPAR, PARAM_NONE, "Missing config parameters 'name,N'" }, { SEVERITY_ERR, MSG_CONVAL, PARAM_STR, "Missing config value N for '%s,N'" }, { SEVERITY_SUCC, MSG_USBSTA, PARAM_NONE, "USB Statistics" }, @@ -618,11 +599,14 @@ struct CODES { { SEVERITY_SUCC, MSG_ASCIDENT,PARAM_ASC, "Identify command sent to ASC%d" }, { SEVERITY_WARN, MSG_ASCNOID, PARAM_ASC, "ASC%d does not support identify" }, { SEVERITY_ERR, MSG_MISASCOPT, PARAM_NONE, "Missing option after ASC number" }, - { SEVERITY_WARN, MSG_ASCNOSET, PARAM_ASC, "ASC %d does not support pgaset" }, + { SEVERITY_WARN, MSG_ASCNOSET, PARAM_ASC, "ASC %d does not support ascset" }, { SEVERITY_INFO, MSG_ASCHELP, PARAM_BOTH, "ASC %d set help: %s" }, { SEVERITY_SUCC, MSG_ASCSETOK, PARAM_BOTH, "ASC %d set OK" }, { SEVERITY_ERR, MSG_ASCSETERR, PARAM_BOTH, "ASC %d set failed: %s" }, #endif + { SEVERITY_SUCC, MSG_LCD, PARAM_NONE, "LCD" }, + { SEVERITY_SUCC, MSG_LOCKOK, PARAM_NONE, "Lock stats created" }, + { SEVERITY_WARN, MSG_LOCKDIS, PARAM_NONE, "Lock stats not enabled" }, { SEVERITY_FAIL, 0, 0, NULL } }; @@ -639,9 +623,9 @@ static bool do_a_restart; static time_t when = 0; // when the request occurred -struct IP4ACCESS { - in_addr_t ip; - in_addr_t mask; +struct IPACCESS { + struct in6_addr ip; + struct in6_addr mask; char group; }; @@ -659,7 +643,7 @@ struct APIGROUPS { char *commands; } apigroups['Z' - 'A' + 1]; // only A=0 to Z=25 (R: noprivs, W: allprivs) -static struct IP4ACCESS *ipaccess = NULL; +static struct IPACCESS *ipaccess = NULL; static int ips = 0; struct io_data { @@ -667,7 +651,6 @@ struct io_data { char *ptr; char *cur; bool sock; - bool full; bool close; }; @@ -679,14 +662,31 @@ struct io_list { static struct io_list *io_head = NULL; +#define SOCKBUFALLOCSIZ 65536 + #define io_new(init) _io_new(init, false) -#define sock_io_new() _io_new(SOCKBUFSIZ, true) +#define sock_io_new() _io_new(SOCKBUFALLOCSIZ, true) + +#define ALLOC_SBITEMS 2 +#define LIMIT_SBITEMS 0 + +typedef struct sbitem { + char *buf; + size_t siz; + size_t tot; +} SBITEM; + +// Size to grow tot if exceeded +#define SBEXTEND 4096 + +#define DATASB(_item) ((SBITEM *)(_item->data)) + +static K_LIST *strbufs; static void io_reinit(struct io_data *io_data) { io_data->cur = io_data->ptr; *(io_data->ptr) = '\0'; - io_data->full = false; io_data->close = false; } @@ -723,29 +723,16 @@ static bool io_add(struct io_data *io_data, char *buf) { size_t len, dif, tot; - if (io_data->full) - return false; - len = strlen(buf); dif = io_data->cur - io_data->ptr; - tot = len + 1 + dif; + // send will always have enough space to add the JSON + tot = len + 1 + dif + sizeof(JSON_CLOSE) + sizeof(JSON_END); if (tot > io_data->siz) { - size_t new = io_data->siz * 2; + size_t new = io_data->siz + (2 * SOCKBUFALLOCSIZ); if (new < tot) - new = tot * 2; - - if (io_data->sock) { - if (new > SOCKBUFSIZ) { - if (tot > SOCKBUFSIZ) { - io_data->full = true; - return false; - } - - new = SOCKBUFSIZ; - } - } + new = (2 + (size_t)((float)tot / (float)SOCKBUFALLOCSIZ)) * SOCKBUFALLOCSIZ; io_data->ptr = realloc(io_data->ptr, new); io_data->cur = io_data->ptr + dif; @@ -778,6 +765,7 @@ static void io_free() do { io_next = io_list->next; + free(io_list->io_data->ptr); free(io_list->io_data); free(io_list); @@ -818,8 +806,10 @@ static char *escape_string(char *str, bool isjson) return str; buf = malloc(strlen(str) + count + 1); - if (unlikely(!buf)) - quit(1, "Failed to malloc escape buf"); + if (unlikely(!buf)) { + quithere(1, "Failed to malloc escape buf %d", + (int)(strlen(str) + count + 1)); + } ptr = buf; while (*str) @@ -890,8 +880,7 @@ static struct api_data *api_add_data_full(struct api_data *root, char *name, enu root = api_data; root->prev = root; root->next = root; - } - else { + } else { api_data->prev = root->prev; root->prev = api_data; api_data->next = root; @@ -917,6 +906,21 @@ static struct api_data *api_add_data_full(struct api_data *root, char *name, enu api_data->data = (void *)malloc(strlen((char *)data) + 1); strcpy((char*)(api_data->data), (char *)data); break; + case API_UINT8: + /* Most OSs won't really alloc less than 4 */ + api_data->data = malloc(4); + *(uint8_t *)api_data->data = *(uint8_t *)data; + break; + case API_INT16: + /* Most OSs won't really alloc less than 4 */ + api_data->data = malloc(4); + *(int16_t *)api_data->data = *(int16_t *)data; + break; + case API_UINT16: + /* Most OSs won't really alloc less than 4 */ + api_data->data = malloc(4); + *(uint16_t *)api_data->data = *(uint16_t *)data; + break; case API_INT: api_data->data = (void *)malloc(sizeof(int)); *((int *)(api_data->data)) = *((int *)data); @@ -929,10 +933,18 @@ static struct api_data *api_add_data_full(struct api_data *root, char *name, enu api_data->data = (void *)malloc(sizeof(uint32_t)); *((uint32_t *)(api_data->data)) = *((uint32_t *)data); break; + case API_HEX32: + api_data->data = (void *)malloc(sizeof(uint32_t)); + *((uint32_t *)(api_data->data)) = *((uint32_t *)data); + break; case API_UINT64: api_data->data = (void *)malloc(sizeof(uint64_t)); *((uint64_t *)(api_data->data)) = *((uint64_t *)data); break; + case API_INT64: + api_data->data = (void *)malloc(sizeof(int64_t)); + *((int64_t *)(api_data->data)) = *((int64_t *)data); + break; case API_DOUBLE: case API_ELAPSED: case API_MHS: @@ -959,6 +971,7 @@ static struct api_data *api_add_data_full(struct api_data *root, char *name, enu break; case API_VOLTS: case API_TEMP: + case API_AVG: api_data->data = (void *)malloc(sizeof(float)); *((float *)(api_data->data)) = *((float *)data); break; @@ -988,6 +1001,21 @@ struct api_data *api_add_const(struct api_data *root, char *name, const char *da return api_add_data_full(root, name, API_CONST, (void *)data, copy_data); } +struct api_data *api_add_uint8(struct api_data *root, char *name, uint8_t *data, bool copy_data) +{ + return api_add_data_full(root, name, API_UINT8, (void *)data, copy_data); +} + +struct api_data *api_add_int16(struct api_data *root, char *name, uint16_t *data, bool copy_data) +{ + return api_add_data_full(root, name, API_INT16, (void *)data, copy_data); +} + +struct api_data *api_add_uint16(struct api_data *root, char *name, uint16_t *data, bool copy_data) +{ + return api_add_data_full(root, name, API_UINT16, (void *)data, copy_data); +} + struct api_data *api_add_int(struct api_data *root, char *name, int *data, bool copy_data) { return api_add_data_full(root, name, API_INT, (void *)data, copy_data); @@ -1003,11 +1031,21 @@ struct api_data *api_add_uint32(struct api_data *root, char *name, uint32_t *dat return api_add_data_full(root, name, API_UINT32, (void *)data, copy_data); } +struct api_data *api_add_hex32(struct api_data *root, char *name, uint32_t *data, bool copy_data) +{ + return api_add_data_full(root, name, API_HEX32, (void *)data, copy_data); +} + struct api_data *api_add_uint64(struct api_data *root, char *name, uint64_t *data, bool copy_data) { return api_add_data_full(root, name, API_UINT64, (void *)data, copy_data); } +struct api_data *api_add_int64(struct api_data *root, char *name, int64_t *data, bool copy_data) +{ + return api_add_data_full(root, name, API_INT64, (void *)data, copy_data); +} + struct api_data *api_add_double(struct api_data *root, char *name, double *data, bool copy_data) { return api_add_data_full(root, name, API_DOUBLE, (void *)data, copy_data); @@ -1078,108 +1116,186 @@ struct api_data *api_add_percent(struct api_data *root, char *name, double *data return api_add_data_full(root, name, API_PERCENT, (void *)data, copy_data); } -static struct api_data *print_data(struct api_data *root, char *buf, bool isjson, bool precom) +struct api_data *api_add_avg(struct api_data *root, char *name, float *data, bool copy_data) +{ + return api_add_data_full(root, name, API_AVG, (void *)data, copy_data); +} + +static void add_item_buf(K_ITEM *item, const char *str) +{ + size_t old_siz, new_siz, siz, ext; + char *buf; + + buf = DATASB(item)->buf; + siz = (size_t)strlen(str); + + old_siz = DATASB(item)->siz; + new_siz = old_siz + siz + 1; // include '\0' + if (DATASB(item)->tot < new_siz) { + ext = (siz + 1) + SBEXTEND - ((siz + 1) % SBEXTEND); + DATASB(item)->buf = buf = realloc(DATASB(item)->buf, DATASB(item)->tot + ext); + if (!buf) { + quithere(1, "OOM buf siz=%d tot=%d ext=%d", + (int)siz, (int)(DATASB(item)->tot), (int)ext); + } + DATASB(item)->tot += ext; + } + memcpy(buf + old_siz, str, siz + 1); + DATASB(item)->siz += siz; +} + +static struct api_data *print_data(struct io_data *io_data, struct api_data *root, bool isjson, bool precom) { + // N.B. strings don't use this buffer so 64 is enough (for now) + char buf[64]; struct api_data *tmp; - bool first = true; + bool done, first = true; char *original, *escape; - char *quote; + K_ITEM *item; - *buf = '\0'; + K_WLOCK(strbufs); + item = k_unlink_head(strbufs); + K_WUNLOCK(strbufs); - if (precom) { - *(buf++) = *COMMA; - *buf = '\0'; - } + DATASB(item)->siz = 0; - if (isjson) { - strcpy(buf, JSON0); - buf = strchr(buf, '\0'); - quote = JSON1; - } else - quote = (char *)BLANK; + if (precom) + add_item_buf(item, COMMA); + + if (isjson) + add_item_buf(item, JSON0); while (root) { if (!first) - *(buf++) = *COMMA; + add_item_buf(item, COMMA); else first = false; - sprintf(buf, "%s%s%s%s", quote, root->name, quote, isjson ? ":" : "="); + if (isjson) + add_item_buf(item, JSON1); + + add_item_buf(item, root->name); + + if (isjson) + add_item_buf(item, JSON1); + + if (isjson) + add_item_buf(item, ":"); + else + add_item_buf(item, "="); - buf = strchr(buf, '\0'); + first = false; + done = false; switch(root->type) { case API_STRING: case API_CONST: - sprintf(buf, "%s%s%s", quote, (char *)(root->data), quote); + if (isjson) + add_item_buf(item, JSON1); + add_item_buf(item, (char *)(root->data)); + if (isjson) + add_item_buf(item, JSON1); + done = true; break; case API_ESCAPE: original = (char *)(root->data); escape = escape_string((char *)(root->data), isjson); - sprintf(buf, "%s%s%s", quote, escape, quote); + if (isjson) + add_item_buf(item, JSON1); + add_item_buf(item, escape); + if (isjson) + add_item_buf(item, JSON1); if (escape != original) free(escape); + done = true; + break; + case API_UINT8: + snprintf(buf, sizeof(buf), "%u", *(uint8_t *)root->data); + break; + case API_INT16: + snprintf(buf, sizeof(buf), "%d", *(int16_t *)root->data); + break; + case API_UINT16: + snprintf(buf, sizeof(buf), "%u", *(uint16_t *)root->data); break; case API_INT: - sprintf(buf, "%d", *((int *)(root->data))); + snprintf(buf, sizeof(buf), "%d", *((int *)(root->data))); break; case API_UINT: - sprintf(buf, "%u", *((unsigned int *)(root->data))); + snprintf(buf, sizeof(buf), "%u", *((unsigned int *)(root->data))); break; case API_UINT32: - sprintf(buf, "%"PRIu32, *((uint32_t *)(root->data))); + snprintf(buf, sizeof(buf), "%"PRIu32, *((uint32_t *)(root->data))); + break; + case API_HEX32: + if (isjson) + add_item_buf(item, JSON1); + snprintf(buf, sizeof(buf), "0x%08x", *((uint32_t *)(root->data))); + add_item_buf(item, buf); + if (isjson) + add_item_buf(item, JSON1); + done = true; break; case API_UINT64: - sprintf(buf, "%"PRIu64, *((uint64_t *)(root->data))); + snprintf(buf, sizeof(buf), "%"PRIu64, *((uint64_t *)(root->data))); + break; + case API_INT64: + snprintf(buf, sizeof(buf), "%"PRId64, *((int64_t *)(root->data))); break; case API_TIME: - sprintf(buf, "%lu", *((unsigned long *)(root->data))); + snprintf(buf, sizeof(buf), "%lu", *((unsigned long *)(root->data))); break; case API_DOUBLE: - sprintf(buf, "%f", *((double *)(root->data))); + snprintf(buf, sizeof(buf), "%f", *((double *)(root->data))); break; case API_ELAPSED: - sprintf(buf, "%.0f", *((double *)(root->data))); + snprintf(buf, sizeof(buf), "%.0f", *((double *)(root->data))); break; case API_UTILITY: case API_FREQ: case API_MHS: - sprintf(buf, "%.2f", *((double *)(root->data))); + snprintf(buf, sizeof(buf), "%.2f", *((double *)(root->data))); break; case API_VOLTS: - sprintf(buf, "%.3f", *((float *)(root->data))); + case API_AVG: + snprintf(buf, sizeof(buf), "%.3f", *((float *)(root->data))); break; case API_MHTOTAL: - sprintf(buf, "%.4f", *((double *)(root->data))); + snprintf(buf, sizeof(buf), "%.4f", *((double *)(root->data))); break; case API_HS: - sprintf(buf, "%.15f", *((double *)(root->data))); + snprintf(buf, sizeof(buf), "%.15f", *((double *)(root->data))); break; case API_DIFF: - sprintf(buf, "%.8f", *((double *)(root->data))); + snprintf(buf, sizeof(buf), "%.8f", *((double *)(root->data))); break; case API_BOOL: - sprintf(buf, "%s", *((bool *)(root->data)) ? TRUESTR : FALSESTR); + snprintf(buf, sizeof(buf), "%s", *((bool *)(root->data)) ? TRUESTR : FALSESTR); break; case API_TIMEVAL: - sprintf(buf, "%ld.%06ld", - ((struct timeval *)(root->data))->tv_sec, - ((struct timeval *)(root->data))->tv_usec); + snprintf(buf, sizeof(buf), "%ld.%06ld", + (long)((struct timeval *)(root->data))->tv_sec, + (long)((struct timeval *)(root->data))->tv_usec); break; case API_TEMP: - sprintf(buf, "%.2f", *((float *)(root->data))); + snprintf(buf, sizeof(buf), "%.2f", *((float *)(root->data))); break; case API_PERCENT: - sprintf(buf, "%.4f", *((double *)(root->data)) * 100.0); + snprintf(buf, sizeof(buf), "%.4f", *((double *)(root->data)) * 100.0); break; default: applog(LOG_ERR, "API: unknown2 data type %d ignored", root->type); - sprintf(buf, "%s%s%s", quote, UNKNOWN, quote); + if (isjson) + add_item_buf(item, JSON1); + add_item_buf(item, UNKNOWN); + if (isjson) + add_item_buf(item, JSON1); + done = true; break; } - buf = strchr(buf, '\0'); + if (!done) + add_item_buf(item, buf); free(root->name); if (root->data_was_malloc) @@ -1197,27 +1313,32 @@ static struct api_data *print_data(struct api_data *root, char *buf, bool isjson } } - strcpy(buf, isjson ? JSON5 : SEPSTR); + if (isjson) + add_item_buf(item, JSON5); + else + add_item_buf(item, SEPSTR); + + io_add(io_data, DATASB(item)->buf); + + K_WLOCK(strbufs); + k_add_head(strbufs, item); + K_WUNLOCK(strbufs); return root; } +#define DRIVER_COUNT_DRV(X) if (devices[i]->drv->drv_id == DRIVER_##X) \ + count++; + #ifdef HAVE_AN_ASIC -static int numascs() +static int numascs(void) { int count = 0; int i; rd_lock(&devices_lock); for (i = 0; i < total_devices; i++) { -#ifdef USE_AVALON - if (devices[i]->drv->drv_id == DRIVER_AVALON) - count++; -#endif -#ifdef USE_BFLSC - if (devices[i]->drv->drv_id == DRIVER_BFLSC) - count++; -#endif + ASIC_PARSE_COMMANDS(DRIVER_COUNT_DRV) } rd_unlock(&devices_lock); return count; @@ -1230,14 +1351,7 @@ static int ascdevice(int ascid) rd_lock(&devices_lock); for (i = 0; i < total_devices; i++) { -#ifdef USE_AVALON - if (devices[i]->drv->drv_id == DRIVER_AVALON) - count++; -#endif -#ifdef USE_BFLSC - if (devices[i]->drv->drv_id == DRIVER_BFLSC) - count++; -#endif + ASIC_PARSE_COMMANDS(DRIVER_COUNT_DRV) if (count == (ascid + 1)) goto foundit; } @@ -1253,29 +1367,14 @@ static int ascdevice(int ascid) #endif #ifdef HAVE_AN_FPGA -static int numpgas() +static int numpgas(void) { int count = 0; int i; rd_lock(&devices_lock); for (i = 0; i < total_devices; i++) { -#ifdef USE_BITFORCE - if (devices[i]->drv->drv_id == DRIVER_BITFORCE) - count++; -#endif -#ifdef USE_ICARUS - if (devices[i]->drv->drv_id == DRIVER_ICARUS) - count++; -#endif -#ifdef USE_ZTEX - if (devices[i]->drv->drv_id == DRIVER_ZTEX) - count++; -#endif -#ifdef USE_MODMINER - if (devices[i]->drv->drv_id == DRIVER_MODMINER) - count++; -#endif + FPGA_PARSE_COMMANDS(DRIVER_COUNT_DRV) } rd_unlock(&devices_lock); return count; @@ -1288,22 +1387,7 @@ static int pgadevice(int pgaid) rd_lock(&devices_lock); for (i = 0; i < total_devices; i++) { -#ifdef USE_BITFORCE - if (devices[i]->drv->drv_id == DRIVER_BITFORCE) - count++; -#endif -#ifdef USE_ICARUS - if (devices[i]->drv->drv_id == DRIVER_ICARUS) - count++; -#endif -#ifdef USE_ZTEX - if (devices[i]->drv->drv_id == DRIVER_ZTEX) - count++; -#endif -#ifdef USE_MODMINER - if (devices[i]->drv->drv_id == DRIVER_MODMINER) - count++; -#endif + FPGA_PARSE_COMMANDS(DRIVER_COUNT_DRV) if (count == (pgaid + 1)) goto foundit; } @@ -1325,7 +1409,6 @@ static void message(struct io_data *io_data, int messageid, int paramid, char *p { struct api_data *root = NULL; char buf[TMPBUFSIZ]; - char buf2[TMPBUFSIZ]; char severity[2]; #ifdef HAVE_AN_ASIC int asc; @@ -1335,10 +1418,8 @@ static void message(struct io_data *io_data, int messageid, int paramid, char *p #endif int i; - io_reinit(io_data); - if (isjson) - io_put(io_data, JSON_START JSON_STATUS); + io_add(io_data, JSON_START JSON_STATUS); for (i = 0; codes[i].severity != SEVERITY_FAIL; i++) { if (codes[i].code == messageid) { @@ -1360,7 +1441,6 @@ static void message(struct io_data *io_data, int messageid, int paramid, char *p severity[1] = '\0'; switch(codes[i].params) { - case PARAM_GPU: case PARAM_PGA: case PARAM_ASC: case PARAM_PID: @@ -1370,11 +1450,6 @@ static void message(struct io_data *io_data, int messageid, int paramid, char *p case PARAM_POOL: sprintf(buf, codes[i].description, paramid, pools[paramid]->rpc_url); break; -#ifdef HAVE_OPENCL - case PARAM_GPUMAX: - sprintf(buf, codes[i].description, paramid, nDevs - 1); - break; -#endif #ifdef HAVE_AN_FPGA case PARAM_PGAMAX: pga = numpgas(); @@ -1402,9 +1477,6 @@ static void message(struct io_data *io_data, int messageid, int paramid, char *p #endif sprintf(buf, codes[i].description -#ifdef HAVE_OPENCL - , nDevs -#endif #ifdef HAVE_AN_ASIC , asc #endif @@ -1439,8 +1511,7 @@ static void message(struct io_data *io_data, int messageid, int paramid, char *p root = api_add_escape(root, "Msg", buf, false); root = api_add_escape(root, "Description", opt_api_description, false); - root = print_data(root, buf2, isjson, false); - io_add(io_data, buf2); + root = print_data(io_data, root, isjson, false); if (isjson) io_add(io_data, JSON_CLOSE); return; @@ -1455,199 +1526,492 @@ static void message(struct io_data *io_data, int messageid, int paramid, char *p root = api_add_escape(root, "Msg", buf, false); root = api_add_escape(root, "Description", opt_api_description, false); - root = print_data(root, buf2, isjson, false); - io_add(io_data, buf2); + root = print_data(io_data, root, isjson, false); if (isjson) io_add(io_data, JSON_CLOSE); } -static void apiversion(struct io_data *io_data, __maybe_unused SOCKETTYPE c, __maybe_unused char *param, bool isjson, __maybe_unused char group) +#if LOCK_TRACKING + +#define LOCK_FMT_FFL " - called from %s %s():%d" + +#define LOCKMSG(fmt, ...) fprintf(stderr, "APILOCK: " fmt "\n", ##__VA_ARGS__) +#define LOCKMSGMORE(fmt, ...) fprintf(stderr, " " fmt "\n", ##__VA_ARGS__) +#define LOCKMSGFFL(fmt, ...) fprintf(stderr, "APILOCK: " fmt LOCK_FMT_FFL "\n", ##__VA_ARGS__, file, func, linenum) +#define LOCKMSGFLUSH() fflush(stderr) + +typedef struct lockstat { + uint64_t lock_id; + const char *file; + const char *func; + int linenum; + struct timeval tv; +} LOCKSTAT; + +typedef struct lockline { + struct lockline *prev; + struct lockstat *stat; + struct lockline *next; +} LOCKLINE; + +typedef struct lockinfo { + void *lock; + enum cglock_typ typ; + const char *file; + const char *func; + int linenum; + uint64_t gets; + uint64_t gots; + uint64_t tries; + uint64_t dids; + uint64_t didnts; // should be tries - dids + uint64_t unlocks; + LOCKSTAT lastgot; + LOCKLINE *lockgets; + LOCKLINE *locktries; +} LOCKINFO; + +typedef struct locklist { + LOCKINFO *info; + struct locklist *next; +} LOCKLIST; + +static uint64_t lock_id = 1; + +static LOCKLIST *lockhead; + +static void lockmsgnow() { - struct api_data *root = NULL; - char buf[TMPBUFSIZ]; - bool io_open; - - message(io_data, MSG_VERSION, 0, NULL, isjson); - io_open = io_add(io_data, isjson ? COMSTR JSON_VERSION : _VERSION COMSTR); - - root = api_add_string(root, "CGMiner", VERSION, false); - root = api_add_const(root, "API", APIVERSION, false); + struct timeval now; + struct tm *tm; + time_t dt; + + cgtime(&now); + + dt = now.tv_sec; + tm = localtime(&dt); + + LOCKMSG("%d-%02d-%02d %02d:%02d:%02d", + tm->tm_year + 1900, + tm->tm_mon + 1, + tm->tm_mday, + tm->tm_hour, + tm->tm_min, + tm->tm_sec); +} - root = print_data(root, buf, isjson, false); - io_add(io_data, buf); - if (isjson && io_open) - io_close(io_data); +static LOCKLIST *newlock(void *lock, enum cglock_typ typ, const char *file, const char *func, const int linenum) +{ + LOCKLIST *list; + + list = calloc(1, sizeof(*list)); + if (!list) + quithere(1, "OOM list"); + list->info = calloc(1, sizeof(*(list->info))); + if (!list->info) + quithere(1, "OOM info"); + list->next = lockhead; + lockhead = list; + + list->info->lock = lock; + list->info->typ = typ; + list->info->file = file; + list->info->func = func; + list->info->linenum = linenum; + + return list; } -static void minerconfig(struct io_data *io_data, __maybe_unused SOCKETTYPE c, __maybe_unused char *param, bool isjson, __maybe_unused char group) +static LOCKINFO *findlock(void *lock, enum cglock_typ typ, const char *file, const char *func, const int linenum) { - struct api_data *root = NULL; - char buf[TMPBUFSIZ]; - bool io_open; - int gpucount = 0; - int asccount = 0; - int pgacount = 0; - char *adlinuse = (char *)NO; -#ifdef HAVE_ADL - const char *adl = YES; - int i; + LOCKLIST *look; - for (i = 0; i < nDevs; i++) { - if (gpus[i].has_adl) { - adlinuse = (char *)YES; + look = lockhead; + while (look) { + if (look->info->lock == lock) break; - } + look = look->next; } -#else - const char *adl = NO; -#endif -#ifdef HAVE_OPENCL - gpucount = nDevs; -#endif + if (!look) + look = newlock(lock, typ, file, func, linenum); -#ifdef HAVE_AN_ASIC - asccount = numascs(); -#endif + return look->info; +} -#ifdef HAVE_AN_FPGA - pgacount = numpgas(); -#endif +static void addgettry(LOCKINFO *info, uint64_t id, const char *file, const char *func, const int linenum, bool get) +{ + LOCKSTAT *stat; + LOCKLINE *line; + + stat = calloc(1, sizeof(*stat)); + if (!stat) + quithere(1, "OOM stat"); + line = calloc(1, sizeof(*line)); + if (!line) + quithere(1, "OOM line"); + + if (get) + info->gets++; + else + info->tries++; - message(io_data, MSG_MINECONFIG, 0, NULL, isjson); - io_open = io_add(io_data, isjson ? COMSTR JSON_MINECONFIG : _MINECONFIG COMSTR); + stat->lock_id = id; + stat->file = file; + stat->func = func; + stat->linenum = linenum; + cgtime(&stat->tv); - root = api_add_int(root, "GPU Count", &gpucount, false); - root = api_add_int(root, "ASC Count", &asccount, false); - root = api_add_int(root, "PGA Count", &pgacount, false); - root = api_add_int(root, "Pool Count", &total_pools, false); - root = api_add_const(root, "ADL", (char *)adl, false); - root = api_add_string(root, "ADL in use", adlinuse, false); - root = api_add_const(root, "Strategy", strategies[pool_strategy].s, false); - root = api_add_int(root, "Log Interval", &opt_log_interval, false); - root = api_add_const(root, "Device Code", DEVICECODE, false); - root = api_add_const(root, "OS", OSINFO, false); - root = api_add_bool(root, "Failover-Only", &opt_fail_only, false); - root = api_add_int(root, "ScanTime", &opt_scantime, false); - root = api_add_int(root, "Queue", &opt_queue, false); - root = api_add_int(root, "Expiry", &opt_expiry, false); -#ifdef USE_USBUTILS - if (hotplug_time == 0) - root = api_add_const(root, "Hotplug", DISABLED, false); - else - root = api_add_int(root, "Hotplug", &hotplug_time, false); -#else - root = api_add_const(root, "Hotplug", NONE, false); -#endif + line->stat = stat; - root = print_data(root, buf, isjson, false); - io_add(io_data, buf); - if (isjson && io_open) - io_close(io_data); + if (get) { + line->next = info->lockgets; + if (info->lockgets) + info->lockgets->prev = line; + info->lockgets = line; + } else { + line->next = info->locktries; + if (info->locktries) + info->locktries->prev = line; + info->locktries = line; + } } -#if defined(HAVE_OPENCL) || defined(HAVE_AN_FPGA) || defined(HAVE_AN_ASIC) -static const char *status2str(enum alive status) +static void markgotdid(LOCKINFO *info, uint64_t id, const char *file, const char *func, const int linenum, bool got, int ret) { - switch (status) { - case LIFE_WELL: - return ALIVE; - case LIFE_SICK: - return SICK; - case LIFE_DEAD: - return DEAD; - case LIFE_NOSTART: - return NOSTART; - case LIFE_INIT: - return INIT; - default: - return UNKNOWN; + LOCKLINE *line; + + if (got) + info->gots++; + else { + if (ret == 0) + info->dids++; + else + info->didnts++; } -} -#endif -#ifdef HAVE_OPENCL -static void gpustatus(struct io_data *io_data, int gpu, bool isjson, bool precom) -{ - struct api_data *root = NULL; - char intensity[20]; - char buf[TMPBUFSIZ]; - char *enabled; - char *status; - float gt, gv; - int ga, gf, gp, gc, gm, pt; + if (got || ret == 0) { + info->lastgot.lock_id = id; + info->lastgot.file = file; + info->lastgot.func = func; + info->lastgot.linenum = linenum; + cgtime(&info->lastgot.tv); + } - if (gpu >= 0 && gpu < nDevs) { - struct cgpu_info *cgpu = &gpus[gpu]; + if (got) + line = info->lockgets; + else + line = info->locktries; + while (line) { + if (line->stat->lock_id == id) + break; + line = line->next; + } - cgpu->utility = cgpu->accepted / ( total_secs ? total_secs : 1 ) * 60; + if (!line) { + lockmsgnow(); + LOCKMSGFFL("ERROR attempt to mark a lock as '%s' that wasn't '%s' id=%"PRIu64, + got ? "got" : "did/didnt", got ? "get" : "try", id); + } -#ifdef HAVE_ADL - if (!gpu_stats(gpu, >, &gc, &gm, &gv, &ga, &gf, &gp, &pt)) -#endif - gt = gv = gm = gc = ga = gf = gp = pt = 0; + // Unlink it + if (line->prev) + line->prev->next = line->next; + if (line->next) + line->next->prev = line->prev; - if (cgpu->deven != DEV_DISABLED) - enabled = (char *)YES; - else - enabled = (char *)NO; + if (got) { + if (info->lockgets == line) + info->lockgets = line->next; + } else { + if (info->locktries == line) + info->locktries = line->next; + } - status = (char *)status2str(cgpu->status); + free(line->stat); + free(line); +} - if (cgpu->dynamic) - strcpy(intensity, DYNAMIC); - else - sprintf(intensity, "%d", cgpu->intensity); +// Yes this uses locks also ... ;/ +static void locklock() +{ + if (unlikely(pthread_mutex_lock(&lockstat_lock))) + quithere(1, "WTF MUTEX ERROR ON LOCK! errno=%d", errno); +} - root = api_add_int(root, "GPU", &gpu, false); - root = api_add_string(root, "Enabled", enabled, false); - root = api_add_string(root, "Status", status, false); - root = api_add_temp(root, "Temperature", >, false); - root = api_add_int(root, "Fan Speed", &gf, false); - root = api_add_int(root, "Fan Percent", &gp, false); - root = api_add_int(root, "GPU Clock", &gc, false); - root = api_add_int(root, "Memory Clock", &gm, false); - root = api_add_volts(root, "GPU Voltage", &gv, false); - root = api_add_int(root, "GPU Activity", &ga, false); - root = api_add_int(root, "Powertune", &pt, false); - double mhs = cgpu->total_mhashes / total_secs; - root = api_add_mhs(root, "MHS av", &mhs, false); - char mhsname[27]; - sprintf(mhsname, "MHS %ds", opt_log_interval); - root = api_add_mhs(root, mhsname, &(cgpu->rolling), false); - root = api_add_int(root, "Accepted", &(cgpu->accepted), false); - root = api_add_int(root, "Rejected", &(cgpu->rejected), false); - root = api_add_int(root, "Hardware Errors", &(cgpu->hw_errors), false); - root = api_add_utility(root, "Utility", &(cgpu->utility), false); - root = api_add_string(root, "Intensity", intensity, false); - int last_share_pool = cgpu->last_share_pool_time > 0 ? - cgpu->last_share_pool : -1; - root = api_add_int(root, "Last Share Pool", &last_share_pool, false); - root = api_add_time(root, "Last Share Time", &(cgpu->last_share_pool_time), false); - root = api_add_mhtotal(root, "Total MH", &(cgpu->total_mhashes), false); - root = api_add_int(root, "Diff1 Work", &(cgpu->diff1), false); - root = api_add_diff(root, "Difficulty Accepted", &(cgpu->diff_accepted), false); - root = api_add_diff(root, "Difficulty Rejected", &(cgpu->diff_rejected), false); - root = api_add_diff(root, "Last Share Difficulty", &(cgpu->last_share_diff), false); - root = api_add_time(root, "Last Valid Work", &(cgpu->last_device_valid_work), false); - double hwp = (cgpu->hw_errors + cgpu->diff1) ? - (double)(cgpu->hw_errors) / (double)(cgpu->hw_errors + cgpu->diff1) : 0; - root = api_add_percent(root, "Device Hardware%", &hwp, false); - double rejp = cgpu->diff1 ? - (double)(cgpu->diff_rejected) / (double)(cgpu->diff1) : 0; - root = api_add_percent(root, "Device Rejected%", &rejp, false); +static void lockunlock() +{ + if (unlikely(pthread_mutex_unlock(&lockstat_lock))) + quithere(1, "WTF MUTEX ERROR ON UNLOCK! errno=%d", errno); +} + +uint64_t api_getlock(void *lock, const char *file, const char *func, const int linenum) +{ + LOCKINFO *info; + uint64_t id; + + locklock(); + + info = findlock(lock, CGLOCK_UNKNOWN, file, func, linenum); + id = lock_id++; + addgettry(info, id, file, func, linenum, true); + + lockunlock(); + + return id; +} + +void api_gotlock(uint64_t id, void *lock, const char *file, const char *func, const int linenum) +{ + LOCKINFO *info; + + locklock(); + + info = findlock(lock, CGLOCK_UNKNOWN, file, func, linenum); + markgotdid(info, id, file, func, linenum, true, 0); + + lockunlock(); +} + +uint64_t api_trylock(void *lock, const char *file, const char *func, const int linenum) +{ + LOCKINFO *info; + uint64_t id; + + locklock(); + + info = findlock(lock, CGLOCK_UNKNOWN, file, func, linenum); + id = lock_id++; + addgettry(info, id, file, func, linenum, false); + + lockunlock(); + + return id; +} + +void api_didlock(uint64_t id, int ret, void *lock, const char *file, const char *func, const int linenum) +{ + LOCKINFO *info; + + locklock(); + + info = findlock(lock, CGLOCK_UNKNOWN, file, func, linenum); + markgotdid(info, id, file, func, linenum, false, ret); + + lockunlock(); +} + +void api_gunlock(void *lock, const char *file, const char *func, const int linenum) +{ + LOCKINFO *info; + + locklock(); + + info = findlock(lock, CGLOCK_UNKNOWN, file, func, linenum); + info->unlocks++; + + lockunlock(); +} - root = print_data(root, buf, isjson, precom); - io_add(io_data, buf); +void api_initlock(void *lock, enum cglock_typ typ, const char *file, const char *func, const int linenum) +{ + locklock(); + + findlock(lock, typ, file, func, linenum); + + lockunlock(); +} + +void dsp_det(char *msg, LOCKSTAT *stat) +{ + struct tm *tm; + time_t dt; + + dt = stat->tv.tv_sec; + tm = localtime(&dt); + + LOCKMSGMORE("%s id=%"PRIu64" by %s %s():%d at %d-%02d-%02d %02d:%02d:%02d", + msg, + stat->lock_id, + stat->file, + stat->func, + stat->linenum, + tm->tm_year + 1900, + tm->tm_mon + 1, + tm->tm_mday, + tm->tm_hour, + tm->tm_min, + tm->tm_sec); +} + +void dsp_lock(LOCKINFO *info) +{ + LOCKLINE *line; + char *status; + + LOCKMSG("Lock %p created by %s %s():%d", + info->lock, + info->file, + info->func, + info->linenum); + LOCKMSGMORE("gets:%"PRIu64" gots:%"PRIu64" tries:%"PRIu64 + " dids:%"PRIu64" didnts:%"PRIu64" unlocks:%"PRIu64, + info->gets, + info->gots, + info->tries, + info->dids, + info->didnts, + info->unlocks); + + if (info->gots > 0 || info->dids > 0) { + if (info->unlocks < info->gots + info->dids) + status = "Last got/did still HELD"; + else + status = "Last got/did (idle)"; + + dsp_det(status, &(info->lastgot)); + } else + LOCKMSGMORE("... unused ..."); + + if (info->lockgets) { + LOCKMSGMORE("BLOCKED gets (%"PRIu64")", info->gets - info->gots); + line = info->lockgets; + while (line) { + dsp_det("", line->stat); + line = line->next; + } + } else + LOCKMSGMORE("no blocked gets"); + + if (info->locktries) { + LOCKMSGMORE("BLOCKED tries (%"PRIu64")", info->tries - info->dids - info->didnts); + line = info->lockgets; + while (line) { + dsp_det("", line->stat); + line = line->next; + } + } else + LOCKMSGMORE("no blocked tries"); +} + +void show_locks() +{ + LOCKLIST *list; + + locklock(); + + lockmsgnow(); + + list = lockhead; + if (!list) + LOCKMSG("no locks?!?\n"); + else { + while (list) { + dsp_lock(list->info); + list = list->next; + } } + + LOCKMSGFLUSH(); + + lockunlock(); } #endif +static void lockstats(struct io_data *io_data, __maybe_unused SOCKETTYPE c, __maybe_unused char *param, bool isjson, __maybe_unused char group) +{ +#if LOCK_TRACKING + show_locks(); + message(io_data, MSG_LOCKOK, 0, NULL, isjson); +#else + message(io_data, MSG_LOCKDIS, 0, NULL, isjson); +#endif +} + +static void apiversion(struct io_data *io_data, __maybe_unused SOCKETTYPE c, __maybe_unused char *param, bool isjson, __maybe_unused char group) +{ + struct api_data *root = NULL; + bool io_open; + + message(io_data, MSG_VERSION, 0, NULL, isjson); + io_open = io_add(io_data, isjson ? COMSTR JSON_VERSION : _VERSION COMSTR); + + root = api_add_string(root, "CGMiner", VERSION, false); + root = api_add_const(root, "API", APIVERSION, false); + root = api_add_string(root, "Miner", g_miner_version, false); + root = api_add_string(root, "CompileTime", g_miner_compiletime, false); + root = api_add_string(root, "Type", g_miner_type, false); + + root = print_data(io_data, root, isjson, false); + if (isjson && io_open) + io_close(io_data); +} + +static void minerconfig(struct io_data *io_data, __maybe_unused SOCKETTYPE c, __maybe_unused char *param, bool isjson, __maybe_unused char group) +{ + struct api_data *root = NULL; + bool io_open; + int asccount = 0; + int pgacount = 0; + +#ifdef HAVE_AN_ASIC + asccount = numascs(); +#endif + +#ifdef HAVE_AN_FPGA + pgacount = numpgas(); +#endif + + message(io_data, MSG_MINECONFIG, 0, NULL, isjson); + io_open = io_add(io_data, isjson ? COMSTR JSON_MINECONFIG : _MINECONFIG COMSTR); + + root = api_add_int(root, "ASC Count", &asccount, false); + root = api_add_int(root, "PGA Count", &pgacount, false); + root = api_add_int(root, "Pool Count", &total_pools, false); + root = api_add_const(root, "Strategy", strategies[pool_strategy].s, false); + root = api_add_int(root, "Log Interval", &opt_log_interval, false); + root = api_add_const(root, "Device Code", DEVICECODE, false); + root = api_add_const(root, "OS", OSINFO, false); + root = api_add_bool(root, "Failover-Only", &opt_fail_only, false); + root = api_add_int(root, "ScanTime", &opt_scantime, false); + root = api_add_int(root, "Queue", &opt_queue, false); + root = api_add_int(root, "Expiry", &opt_expiry, false); +#ifdef USE_USBUTILS + if (hotplug_time == 0) + root = api_add_const(root, "Hotplug", DISABLED, false); + else + root = api_add_int(root, "Hotplug", &hotplug_time, false); +#else + root = api_add_const(root, "Hotplug", NONE, false); +#endif + + root = print_data(io_data, root, isjson, false); + if (isjson && io_open) + io_close(io_data); +} + +static const char *status2str(enum alive status) +{ + switch (status) { + case LIFE_WELL: + return ALIVE; + case LIFE_SICK: + return SICK; + case LIFE_DEAD: + return DEAD; + case LIFE_NOSTART: + return NOSTART; + case LIFE_INIT: + return INIT; + default: + return UNKNOWN; + } +} + #ifdef HAVE_AN_ASIC static void ascstatus(struct io_data *io_data, int asc, bool isjson, bool precom) { struct api_data *root = NULL; - char buf[TMPBUFSIZ]; char *enabled; char *status; int numasc = numascs(); @@ -1659,18 +2023,9 @@ static void ascstatus(struct io_data *io_data, int asc, bool isjson, bool precom struct cgpu_info *cgpu = get_devices(dev); float temp = cgpu->temp; - struct timeval now; double dev_runtime; - if (cgpu->dev_start_tv.tv_sec == 0) - dev_runtime = total_secs; - else { - cgtime(&now); - dev_runtime = tdiff(&now, &(cgpu->dev_start_tv)); - } - - if (dev_runtime < 1.0) - dev_runtime = 1.0; + dev_runtime = cgpu_runtime(cgpu); cgpu->utility = cgpu->accepted / dev_runtime * 60; @@ -1701,7 +2056,7 @@ static void ascstatus(struct io_data *io_data, int asc, bool isjson, bool precom root = api_add_int(root, "Last Share Pool", &last_share_pool, false); root = api_add_time(root, "Last Share Time", &(cgpu->last_share_pool_time), false); root = api_add_mhtotal(root, "Total MH", &(cgpu->total_mhashes), false); - root = api_add_int(root, "Diff1 Work", &(cgpu->diff1), false); + root = api_add_int64(root, "Diff1 Work", &(cgpu->diff1), false); root = api_add_diff(root, "Difficulty Accepted", &(cgpu->diff_accepted), false); root = api_add_diff(root, "Difficulty Rejected", &(cgpu->diff_rejected), false); root = api_add_diff(root, "Last Share Difficulty", &(cgpu->last_share_diff), false); @@ -1715,9 +2070,9 @@ static void ascstatus(struct io_data *io_data, int asc, bool isjson, bool precom double rejp = cgpu->diff1 ? (double)(cgpu->diff_rejected) / (double)(cgpu->diff1) : 0; root = api_add_percent(root, "Device Rejected%", &rejp, false); + root = api_add_elapsed(root, "Device Elapsed", &(dev_runtime), false); - root = print_data(root, buf, isjson, precom); - io_add(io_data, buf); + root = print_data(io_data, root, isjson, precom); } } #endif @@ -1726,7 +2081,6 @@ static void ascstatus(struct io_data *io_data, int asc, bool isjson, bool precom static void pgastatus(struct io_data *io_data, int pga, bool isjson, bool precom) { struct api_data *root = NULL; - char buf[TMPBUFSIZ]; char *enabled; char *status; int numpga = numpgas(); @@ -1752,12 +2106,8 @@ static void pgastatus(struct io_data *io_data, int pga, bool isjson, bool precom if (dev_runtime < 1.0) dev_runtime = 1.0; -#ifdef USE_ZTEX - if (cgpu->drv->drv_id == DRIVER_ZTEX && cgpu->device_ztex) - frequency = cgpu->device_ztex->freqM1 * (cgpu->device_ztex->freqM + 1); -#endif #ifdef USE_MODMINER - if (cgpu->drv->drv_id == DRIVER_MODMINER) + if (cgpu->drv->drv_id == DRIVER_modminer) frequency = cgpu->clock; #endif @@ -1791,7 +2141,7 @@ static void pgastatus(struct io_data *io_data, int pga, bool isjson, bool precom root = api_add_time(root, "Last Share Time", &(cgpu->last_share_pool_time), false); root = api_add_mhtotal(root, "Total MH", &(cgpu->total_mhashes), false); root = api_add_freq(root, "Frequency", &frequency, false); - root = api_add_int(root, "Diff1 Work", &(cgpu->diff1), false); + root = api_add_int64(root, "Diff1 Work", &(cgpu->diff1), false); root = api_add_diff(root, "Difficulty Accepted", &(cgpu->diff_accepted), false); root = api_add_diff(root, "Difficulty Rejected", &(cgpu->diff_rejected), false); root = api_add_diff(root, "Last Share Difficulty", &(cgpu->last_share_diff), false); @@ -1805,9 +2155,9 @@ static void pgastatus(struct io_data *io_data, int pga, bool isjson, bool precom double rejp = cgpu->diff1 ? (double)(cgpu->diff_rejected) / (double)(cgpu->diff1) : 0; root = api_add_percent(root, "Device Rejected%", &rejp, false); + root = api_add_elapsed(root, "Device Elapsed", &(dev_runtime), false); - root = print_data(root, buf, isjson, precom); - io_add(io_data, buf); + root = print_data(io_data, root, isjson, precom); } } #endif @@ -1816,15 +2166,10 @@ static void devstatus(struct io_data *io_data, __maybe_unused SOCKETTYPE c, __ma { bool io_open = false; int devcount = 0; - int numgpu = 0; int numasc = 0; int numpga = 0; int i; -#ifdef HAVE_OPENCL - numgpu = nDevs; -#endif - #ifdef HAVE_AN_ASIC numasc = numascs(); #endif @@ -1833,7 +2178,7 @@ static void devstatus(struct io_data *io_data, __maybe_unused SOCKETTYPE c, __ma numpga = numpgas(); #endif - if (numgpu == 0 && numpga == 0 && numasc == 0) { + if (numpga == 0 && numasc == 0) { message(io_data, MSG_NODEVS, 0, NULL, isjson); return; } @@ -1843,13 +2188,6 @@ static void devstatus(struct io_data *io_data, __maybe_unused SOCKETTYPE c, __ma if (isjson) io_open = io_add(io_data, COMSTR JSON_DEVS); -#ifdef HAVE_OPENCL - for (i = 0; i < nDevs; i++) { - gpustatus(io_data, i, isjson, isjson && devcount > 0); - - devcount++; - } -#endif #ifdef HAVE_AN_ASIC if (numasc > 0) { for (i = 0; i < numasc; i++) { @@ -1874,39 +2212,98 @@ static void devstatus(struct io_data *io_data, __maybe_unused SOCKETTYPE c, __ma io_close(io_data); } -#ifdef HAVE_OPENCL -static void gpudev(struct io_data *io_data, __maybe_unused SOCKETTYPE c, char *param, bool isjson, __maybe_unused char group) +static void edevstatus(struct io_data *io_data, __maybe_unused SOCKETTYPE c, __maybe_unused char *param, bool isjson, __maybe_unused char group) { bool io_open = false; - int id; + int devcount = 0; + int numasc = 0; + int numpga = 0; + int i; +#ifdef USE_USBUTILS + time_t howoldsec = 0; +#endif - if (nDevs == 0) { - message(io_data, MSG_GPUNON, 0, NULL, isjson); - return; - } +#ifdef HAVE_AN_ASIC + numasc = numascs(); +#endif - if (param == NULL || *param == '\0') { - message(io_data, MSG_MISID, 0, NULL, isjson); - return; - } +#ifdef HAVE_AN_FPGA + numpga = numpgas(); +#endif - id = atoi(param); - if (id < 0 || id >= nDevs) { - message(io_data, MSG_INVGPU, id, NULL, isjson); + if (numpga == 0 && numasc == 0) { + message(io_data, MSG_NODEVS, 0, NULL, isjson); return; } - message(io_data, MSG_GPUDEV, id, NULL, isjson); +#ifdef USE_USBUTILS + if (param && *param) + howoldsec = (time_t)atoi(param); +#endif + message(io_data, MSG_DEVS, 0, NULL, isjson); if (isjson) - io_open = io_add(io_data, COMSTR JSON_GPU); + io_open = io_add(io_data, COMSTR JSON_DEVS); + +#ifdef HAVE_AN_ASIC + if (numasc > 0) { + for (i = 0; i < numasc; i++) { +#ifdef USE_USBUTILS + int dev = ascdevice(i); + if (dev < 0) // Should never happen + continue; + + struct cgpu_info *cgpu = get_devices(dev); + if (!cgpu) + continue; + if (cgpu->blacklisted) + continue; + if (cgpu->usbinfo.nodev) { + if (howoldsec <= 0) + continue; + if ((when - cgpu->usbinfo.last_nodev.tv_sec) >= howoldsec) + continue; + } +#endif + + ascstatus(io_data, i, isjson, isjson && devcount > 0); - gpustatus(io_data, id, isjson, false); + devcount++; + } + } +#endif + +#ifdef HAVE_AN_FPGA + if (numpga > 0) { + for (i = 0; i < numpga; i++) { +#ifdef USE_USBUTILS + int dev = pgadevice(i); + if (dev < 0) // Should never happen + continue; + + struct cgpu_info *cgpu = get_devices(dev); + if (!cgpu) + continue; + if (cgpu->blacklisted) + continue; + if (cgpu->usbinfo.nodev) { + if (howoldsec <= 0) + continue; + if ((when - cgpu->usbinfo.last_nodev.tv_sec) >= howoldsec) + continue; + } +#endif + + pgastatus(io_data, i, isjson, isjson && devcount > 0); + + devcount++; + } + } +#endif if (isjson && io_open) io_close(io_data); } -#endif #ifdef HAVE_AN_FPGA static void pgadev(struct io_data *io_data, __maybe_unused SOCKETTYPE c, char *param, bool isjson, __maybe_unused char group) @@ -2097,10 +2494,15 @@ static void pgaidentify(struct io_data *io_data, __maybe_unused SOCKETTYPE c, ch static void poolstatus(struct io_data *io_data, __maybe_unused SOCKETTYPE c, __maybe_unused char *param, bool isjson, __maybe_unused char group) { struct api_data *root = NULL; - char buf[TMPBUFSIZ]; bool io_open = false; char *status, *lp; int i; + int hour = 0; + int minute = 0; + int second = 0; + + char lasttime[256] = {0}; + long timediff = 0; if (total_pools == 0) { message(io_data, MSG_NOPOOL, 0, NULL, isjson); @@ -2141,21 +2543,38 @@ static void poolstatus(struct io_data *io_data, __maybe_unused SOCKETTYPE c, __m else lp = (char *)NO; + if(pool->last_share_time <= 0) { + strcpy(lasttime, "0"); + } else { + timediff = time(NULL) - pool->last_share_time; + if(timediff < 0) + timediff = 0; + + hour = timediff / 3600; + minute = (timediff % 3600) / 60; + second = (timediff % 3600) % 60; + sprintf(lasttime, "%d:%02d:%02d", hour, minute, second); + } + root = api_add_int(root, "POOL", &i, false); root = api_add_escape(root, "URL", pool->rpc_url, false); root = api_add_string(root, "Status", status, false); root = api_add_int(root, "Priority", &(pool->prio), false); + root = api_add_int(root, "Quota", &pool->quota, false); root = api_add_string(root, "Long Poll", lp, false); root = api_add_uint(root, "Getworks", &(pool->getwork_requested), false); - root = api_add_int(root, "Accepted", &(pool->accepted), false); - root = api_add_int(root, "Rejected", &(pool->rejected), false); + root = api_add_int64(root, "Accepted", &(pool->accepted), false); + root = api_add_int64(root, "Rejected", &(pool->rejected), false); + //root = api_add_int(root, "Works", &pool->works, false); root = api_add_uint(root, "Discarded", &(pool->discarded_work), false); root = api_add_uint(root, "Stale", &(pool->stale_shares), false); root = api_add_uint(root, "Get Failures", &(pool->getfail_occasions), false); root = api_add_uint(root, "Remote Failures", &(pool->remotefail_occasions), false); root = api_add_escape(root, "User", pool->rpc_user, false); - root = api_add_time(root, "Last Share Time", &(pool->last_share_time), false); - root = api_add_int(root, "Diff1 Shares", &(pool->diff1), false); + //root = api_add_time(root, "Last Share Time", &(pool->last_share_time), false); + root = api_add_string(root, "Last Share Time", lasttime, false); + root = api_add_string(root, "Diff", pool->diff, false); + root = api_add_int64(root, "Diff1 Shares", &(pool->diff1), false); if (pool->rpc_proxy) { root = api_add_const(root, "Proxy Type", proxytype(pool->rpc_proxytype), false); root = api_add_escape(root, "Proxy", pool->rpc_proxy, false); @@ -2182,10 +2601,58 @@ static void poolstatus(struct io_data *io_data, __maybe_unused SOCKETTYPE c, __m (double)(pool->diff_stale) / (double)(pool->diff_accepted + pool->diff_rejected + pool->diff_stale) : 0; root = api_add_percent(root, "Pool Stale%", &stalep, false); - root = print_data(root, buf, isjson, isjson && (i > 0)); - io_add(io_data, buf); + root = print_data(io_data, root, isjson, isjson && (i > 0)); + } + + if (isjson && io_open) + io_close(io_data); +} + +static void lcddisplay(struct io_data *io_data, __maybe_unused SOCKETTYPE c, __maybe_unused char *param, bool isjson, __maybe_unused char group) +{ + struct api_data *root = NULL; + bool io_open = false; + char *status, *lp; + double ghs; + + char szindex[32] = {0}; + char szfan[32] = {0}; + char sztemp[32] = {0}; + char szpool[32] = {0}; + char szuser[32] = {0}; + + struct pool *pool = current_pool(); + + message(io_data, MSG_POOL, 0, NULL, isjson); + + if (isjson) + io_open = io_add(io_data, COMSTR JSON_POOLS); + + ghs = total_mhashes_done / 1000 / total_secs; + + strcpy(szindex, "0"); + root = api_add_string(root, "LCD", szindex, false); + + root = api_add_mhs(root, "GHS5s", &(g_displayed_rolling), false); + root = api_add_mhs(root, "GHSavg", &(ghs), false); + + sprintf(szfan, "%d", g_max_fan); + root = api_add_string(root, "fan", szfan, false); + sprintf(sztemp, "%d", g_max_temp); + root = api_add_string(root, "temp", sztemp, false); + + if(pool == NULL) { + strcpy(szpool, "no"); + strcpy(szuser, "no"); + root = api_add_string(root, "pool", szpool, false); + root = api_add_string(root, "user", szuser, false); + } else { + root = api_add_string(root, "pool", pool->rpc_url, false); + root = api_add_string(root, "user", pool->rpc_user, false); } + root = print_data(io_data, root, isjson, isjson); + if (isjson && io_open) io_close(io_data); } @@ -2193,9 +2660,8 @@ static void poolstatus(struct io_data *io_data, __maybe_unused SOCKETTYPE c, __m static void summary(struct io_data *io_data, __maybe_unused SOCKETTYPE c, __maybe_unused char *param, bool isjson, __maybe_unused char group) { struct api_data *root = NULL; - char buf[TMPBUFSIZ]; bool io_open; - double utility, mhs, work_utility; + double utility, ghs, work_utility; message(io_data, MSG_SUMM, 0, NULL, isjson); io_open = io_add(io_data, isjson ? COMSTR JSON_SUMMARY : _SUMMARY COMSTR); @@ -2204,19 +2670,20 @@ static void summary(struct io_data *io_data, __maybe_unused SOCKETTYPE c, __mayb mutex_lock(&hash_lock); utility = total_accepted / ( total_secs ? total_secs : 1 ) * 60; - mhs = total_mhashes_done / total_secs; + ghs = total_mhashes_done / 1000 / total_secs; work_utility = total_diff1 / ( total_secs ? total_secs : 1 ) * 60; root = api_add_elapsed(root, "Elapsed", &(total_secs), true); - root = api_add_mhs(root, "MHS av", &(mhs), false); + root = api_add_mhs(root, "GHS 5s", &(g_displayed_rolling), false); + root = api_add_mhs(root, "GHS av", &(ghs), false); root = api_add_uint(root, "Found Blocks", &(found_blocks), true); - root = api_add_int(root, "Getworks", &(total_getworks), true); - root = api_add_int(root, "Accepted", &(total_accepted), true); - root = api_add_int(root, "Rejected", &(total_rejected), true); + root = api_add_int64(root, "Getworks", &(total_getworks), true); + root = api_add_int64(root, "Accepted", &(total_accepted), true); + root = api_add_int64(root, "Rejected", &(total_rejected), true); root = api_add_int(root, "Hardware Errors", &(hw_errors), true); root = api_add_utility(root, "Utility", &(utility), false); - root = api_add_int(root, "Discarded", &(total_discarded), true); - root = api_add_int(root, "Stale", &(total_stale), true); + root = api_add_int64(root, "Discarded", &(total_discarded), true); + root = api_add_int64(root, "Stale", &(total_stale), true); root = api_add_uint(root, "Get Failures", &(total_go), true); root = api_add_uint(root, "Local Work", &(local_work), true); root = api_add_uint(root, "Remote Failures", &(total_ro), true); @@ -2239,141 +2706,11 @@ static void summary(struct io_data *io_data, __maybe_unused SOCKETTYPE c, __mayb double stalep = (total_diff_accepted + total_diff_rejected + total_diff_stale) ? (double)(total_diff_stale) / (double)(total_diff_accepted + total_diff_rejected + total_diff_stale) : 0; root = api_add_percent(root, "Pool Stale%", &stalep, false); + root = api_add_time(root, "Last getwork", &last_getwork, false); mutex_unlock(&hash_lock); - root = print_data(root, buf, isjson, false); - io_add(io_data, buf); - if (isjson && io_open) - io_close(io_data); -} - -#ifdef HAVE_OPENCL -static void gpuenable(struct io_data *io_data, __maybe_unused SOCKETTYPE c, char *param, bool isjson, __maybe_unused char group) -{ - struct thr_info *thr; - int gpu; - int id; - int i; - - if (gpu_threads == 0) { - message(io_data, MSG_GPUNON, 0, NULL, isjson); - return; - } - - if (param == NULL || *param == '\0') { - message(io_data, MSG_MISID, 0, NULL, isjson); - return; - } - - id = atoi(param); - if (id < 0 || id >= nDevs) { - message(io_data, MSG_INVGPU, id, NULL, isjson); - return; - } - - applog(LOG_DEBUG, "API: request to gpuenable gpuid %d %s%u", - id, gpus[id].drv->name, gpus[id].device_id); - - if (gpus[id].deven != DEV_DISABLED) { - message(io_data, MSG_ALRENA, id, NULL, isjson); - return; - } - - for (i = 0; i < gpu_threads; i++) { - thr = get_thread(i); - gpu = thr->cgpu->device_id; - if (gpu == id) { - if (thr->cgpu->status != LIFE_WELL) { - message(io_data, MSG_GPUMRE, id, NULL, isjson); - return; - } - gpus[id].deven = DEV_ENABLED; - applog(LOG_DEBUG, "API Pushing sem post to thread %d", thr->id); - cgsem_post(&thr->sem); - } - } - - message(io_data, MSG_GPUREN, id, NULL, isjson); -} - -static void gpudisable(struct io_data *io_data, __maybe_unused SOCKETTYPE c, char *param, bool isjson, __maybe_unused char group) -{ - int id; - - if (nDevs == 0) { - message(io_data, MSG_GPUNON, 0, NULL, isjson); - return; - } - - if (param == NULL || *param == '\0') { - message(io_data, MSG_MISID, 0, NULL, isjson); - return; - } - - id = atoi(param); - if (id < 0 || id >= nDevs) { - message(io_data, MSG_INVGPU, id, NULL, isjson); - return; - } - - applog(LOG_DEBUG, "API: request to gpudisable gpuid %d %s%u", - id, gpus[id].drv->name, gpus[id].device_id); - - if (gpus[id].deven == DEV_DISABLED) { - message(io_data, MSG_ALRDIS, id, NULL, isjson); - return; - } - - gpus[id].deven = DEV_DISABLED; - - message(io_data, MSG_GPUDIS, id, NULL, isjson); -} - -static void gpurestart(struct io_data *io_data, __maybe_unused SOCKETTYPE c, char *param, bool isjson, __maybe_unused char group) -{ - int id; - - if (nDevs == 0) { - message(io_data, MSG_GPUNON, 0, NULL, isjson); - return; - } - - if (param == NULL || *param == '\0') { - message(io_data, MSG_MISID, 0, NULL, isjson); - return; - } - - id = atoi(param); - if (id < 0 || id >= nDevs) { - message(io_data, MSG_INVGPU, id, NULL, isjson); - return; - } - - reinit_device(&gpus[id]); - - message(io_data, MSG_GPUREI, id, NULL, isjson); -} -#endif - -static void gpucount(struct io_data *io_data, __maybe_unused SOCKETTYPE c, __maybe_unused char *param, bool isjson, __maybe_unused char group) -{ - struct api_data *root = NULL; - char buf[TMPBUFSIZ]; - bool io_open; - int numgpu = 0; - -#ifdef HAVE_OPENCL - numgpu = nDevs; -#endif - - message(io_data, MSG_NUMGPU, 0, NULL, isjson); - io_open = io_add(io_data, isjson ? COMSTR JSON_GPUS : _GPUS COMSTR); - - root = api_add_int(root, "Count", &numgpu, false); - - root = print_data(root, buf, isjson, false); - io_add(io_data, buf); + root = print_data(io_data, root, isjson, false); if (isjson && io_open) io_close(io_data); } @@ -2381,7 +2718,6 @@ static void gpucount(struct io_data *io_data, __maybe_unused SOCKETTYPE c, __may static void pgacount(struct io_data *io_data, __maybe_unused SOCKETTYPE c, __maybe_unused char *param, bool isjson, __maybe_unused char group) { struct api_data *root = NULL; - char buf[TMPBUFSIZ]; bool io_open; int count = 0; @@ -2394,8 +2730,7 @@ static void pgacount(struct io_data *io_data, __maybe_unused SOCKETTYPE c, __may root = api_add_int(root, "Count", &count, false); - root = print_data(root, buf, isjson, false); - io_add(io_data, buf); + root = print_data(io_data, root, isjson, false); if (isjson && io_open) io_close(io_data); } @@ -2509,7 +2844,7 @@ static void addpool(struct io_data *io_data, __maybe_unused SOCKETTYPE c, char * add_pool_details(pool, true, url, user, pass); ptr = escape_string(url, isjson); - message(io_data, MSG_ADDPOOL, 0, ptr, isjson); + message(io_data, MSG_ADDPOOL, pool->pool_no, ptr, isjson); if (ptr != url) free(ptr); ptr = NULL; @@ -2617,10 +2952,11 @@ static void poolpriority(struct io_data *io_data, __maybe_unused SOCKETTYPE c, c message(io_data, MSG_POOLPRIO, 0, NULL, isjson); } -static void disablepool(struct io_data *io_data, __maybe_unused SOCKETTYPE c, char *param, bool isjson, __maybe_unused char group) +static void poolquota(struct io_data *io_data, __maybe_unused SOCKETTYPE c, char *param, bool isjson, __maybe_unused char group) { struct pool *pool; - int id; + int quota, id; + char *comma; if (total_pools == 0) { message(io_data, MSG_NOPOOL, 0, NULL, isjson); @@ -2632,35 +2968,35 @@ static void disablepool(struct io_data *io_data, __maybe_unused SOCKETTYPE c, ch return; } + comma = strchr(param, ','); + if (!comma) { + message(io_data, MSG_CONVAL, 0, param, isjson); + return; + } + + *(comma++) = '\0'; + id = atoi(param); if (id < 0 || id >= total_pools) { message(io_data, MSG_INVPID, id, NULL, isjson); return; } - pool = pools[id]; - if (pool->enabled == POOL_DISABLED) { - message(io_data, MSG_ALRDISP, id, NULL, isjson); - return; - } - if (enabled_pools <= 1) { - message(io_data, MSG_DISLASTP, id, NULL, isjson); + quota = atoi(comma); + if (quota < 0) { + message(io_data, MSG_INVNEG, quota, pool->rpc_url, isjson); return; } - pool->enabled = POOL_DISABLED; - if (pool == current_pool()) - switch_pools(NULL); - - message(io_data, MSG_DISPOOL, id, NULL, isjson); + pool->quota = quota; + adjust_quota_gcd(); + message(io_data, MSG_SETQUOTA, quota, pool->rpc_url, isjson); } -static void removepool(struct io_data *io_data, __maybe_unused SOCKETTYPE c, char *param, bool isjson, __maybe_unused char group) +static void disablepool(struct io_data *io_data, __maybe_unused SOCKETTYPE c, char *param, bool isjson, __maybe_unused char group) { struct pool *pool; - char *rpc_url; - bool dofree = false; int id; if (total_pools == 0) { @@ -2679,188 +3015,79 @@ static void removepool(struct io_data *io_data, __maybe_unused SOCKETTYPE c, cha return; } - if (total_pools <= 1) { - message(io_data, MSG_REMLASTP, id, NULL, isjson); - return; - } - pool = pools[id]; - if (pool == current_pool()) - switch_pools(NULL); - - if (pool == current_pool()) { - message(io_data, MSG_ACTPOOL, id, NULL, isjson); + if (pool->enabled == POOL_DISABLED) { + message(io_data, MSG_ALRDISP, id, NULL, isjson); return; } - pool->enabled = POOL_DISABLED; - rpc_url = escape_string(pool->rpc_url, isjson); - if (rpc_url != pool->rpc_url) - dofree = true; - - remove_pool(pool); - - message(io_data, MSG_REMPOOL, id, rpc_url, isjson); - - if (dofree) - free(rpc_url); - rpc_url = NULL; -} - -#ifdef HAVE_OPENCL -static bool splitgpuvalue(struct io_data *io_data, char *param, int *gpu, char **value, bool isjson) -{ - int id; - char *gpusep; - - if (nDevs == 0) { - message(io_data, MSG_GPUNON, 0, NULL, isjson); - return false; - } - - if (param == NULL || *param == '\0') { - message(io_data, MSG_MISID, 0, NULL, isjson); - return false; - } - - gpusep = strchr(param, GPUSEP); - if (gpusep == NULL) { - message(io_data, MSG_MISVAL, 0, NULL, isjson); - return false; - } - - *(gpusep++) = '\0'; - - id = atoi(param); - if (id < 0 || id >= nDevs) { - message(io_data, MSG_INVGPU, id, NULL, isjson); - return false; - } - - *gpu = id; - *value = gpusep; - - return true; -} - -static void gpuintensity(struct io_data *io_data, __maybe_unused SOCKETTYPE c, char *param, bool isjson, __maybe_unused char group) -{ - int id; - char *value; - int intensity; - char intensitystr[7]; - - if (!splitgpuvalue(io_data, param, &id, &value, isjson)) + if (enabled_pools <= 1) { + message(io_data, MSG_DISLASTP, id, NULL, isjson); return; - - if (!strncasecmp(value, DYNAMIC, 1)) { - gpus[id].dynamic = true; - strcpy(intensitystr, DYNAMIC); } - else { - intensity = atoi(value); - if (intensity < MIN_INTENSITY || intensity > MAX_INTENSITY) { - message(io_data, MSG_INVINT, 0, value, isjson); - return; - } - - gpus[id].dynamic = false; - gpus[id].intensity = intensity; - sprintf(intensitystr, "%d", intensity); - } - - message(io_data, MSG_GPUINT, id, intensitystr, isjson); -} - -static void gpumem(struct io_data *io_data, __maybe_unused SOCKETTYPE c, __maybe_unused char *param, bool isjson, __maybe_unused char group) -{ -#ifdef HAVE_ADL - int id; - char *value; - int clock; - - if (!splitgpuvalue(io_data, param, &id, &value, isjson)) - return; - clock = atoi(value); + pool->enabled = POOL_DISABLED; + if (pool == current_pool()) + switch_pools(NULL); - if (set_memoryclock(id, clock)) - message(io_data, MSG_GPUMERR, id, value, isjson); - else - message(io_data, MSG_GPUMEM, id, value, isjson); -#else - message(io_data, MSG_NOADL, 0, NULL, isjson); -#endif + message(io_data, MSG_DISPOOL, id, NULL, isjson); } -static void gpuengine(struct io_data *io_data, __maybe_unused SOCKETTYPE c, __maybe_unused char *param, bool isjson, __maybe_unused char group) +static void removepool(struct io_data *io_data, __maybe_unused SOCKETTYPE c, char *param, bool isjson, __maybe_unused char group) { -#ifdef HAVE_ADL + struct pool *pool; + char *rpc_url; + bool dofree = false; int id; - char *value; - int clock; - if (!splitgpuvalue(io_data, param, &id, &value, isjson)) + if (total_pools == 0) { + message(io_data, MSG_NOPOOL, 0, NULL, isjson); return; + } - clock = atoi(value); - - if (set_engineclock(id, clock)) - message(io_data, MSG_GPUEERR, id, value, isjson); - else - message(io_data, MSG_GPUENG, id, value, isjson); -#else - message(io_data, MSG_NOADL, 0, NULL, isjson); -#endif -} + if (param == NULL || *param == '\0') { + message(io_data, MSG_MISPID, 0, NULL, isjson); + return; + } -static void gpufan(struct io_data *io_data, __maybe_unused SOCKETTYPE c, __maybe_unused char *param, bool isjson, __maybe_unused char group) -{ -#ifdef HAVE_ADL - int id; - char *value; - int fan; + id = atoi(param); + if (id < 0 || id >= total_pools) { + message(io_data, MSG_INVPID, id, NULL, isjson); + return; + } - if (!splitgpuvalue(io_data, param, &id, &value, isjson)) + if (total_pools <= 1) { + message(io_data, MSG_REMLASTP, id, NULL, isjson); return; + } - fan = atoi(value); + pool = pools[id]; + if (pool == current_pool()) + switch_pools(NULL); - if (set_fanspeed(id, fan)) - message(io_data, MSG_GPUFERR, id, value, isjson); - else - message(io_data, MSG_GPUFAN, id, value, isjson); -#else - message(io_data, MSG_NOADL, 0, NULL, isjson); -#endif -} + if (pool == current_pool()) { + message(io_data, MSG_ACTPOOL, id, NULL, isjson); + return; + } -static void gpuvddc(struct io_data *io_data, __maybe_unused SOCKETTYPE c, __maybe_unused char *param, bool isjson, __maybe_unused char group) -{ -#ifdef HAVE_ADL - int id; - char *value; - float vddc; + pool->enabled = POOL_DISABLED; + rpc_url = escape_string(pool->rpc_url, isjson); + if (rpc_url != pool->rpc_url) + dofree = true; - if (!splitgpuvalue(io_data, param, &id, &value, isjson)) - return; + remove_pool(pool); - vddc = atof(value); + message(io_data, MSG_REMPOOL, id, rpc_url, isjson); - if (set_vddc(id, vddc)) - message(io_data, MSG_GPUVERR, id, value, isjson); - else - message(io_data, MSG_GPUVDDC, id, value, isjson); -#else - message(io_data, MSG_NOADL, 0, NULL, isjson); -#endif + if (dofree) + free(rpc_url); + rpc_url = NULL; } -#endif void doquit(struct io_data *io_data, __maybe_unused SOCKETTYPE c, __maybe_unused char *param, bool isjson, __maybe_unused char group) { if (isjson) - io_put(io_data, JSON_START JSON_BYE); + io_put(io_data, JSON_ACTION JSON_BYE); else io_put(io_data, _BYE); @@ -2871,7 +3098,7 @@ void doquit(struct io_data *io_data, __maybe_unused SOCKETTYPE c, __maybe_unused void dorestart(struct io_data *io_data, __maybe_unused SOCKETTYPE c, __maybe_unused char *param, bool isjson, __maybe_unused char group) { if (isjson) - io_put(io_data, JSON_START JSON_RESTART); + io_put(io_data, JSON_ACTION JSON_RESTART); else io_put(io_data, _RESTART); @@ -2887,7 +3114,6 @@ void privileged(struct io_data *io_data, __maybe_unused SOCKETTYPE c, __maybe_un void notifystatus(struct io_data *io_data, int device, struct cgpu_info *cgpu, bool isjson, __maybe_unused char group) { struct api_data *root = NULL; - char buf[TMPBUFSIZ]; char *reason; if (cgpu->device_last_not_well == 0) @@ -2945,8 +3171,7 @@ void notifystatus(struct io_data *io_data, int device, struct cgpu_info *cgpu, b root = api_add_int(root, "*Dev Comms Error", &(cgpu->dev_comms_error_count), false); root = api_add_int(root, "*Dev Throttle", &(cgpu->dev_throttle_count), false); - root = print_data(root, buf, isjson, isjson && (device > 0)); - io_add(io_data, buf); + root = print_data(io_data, root, isjson, isjson && (device > 0)); } static void notify(struct io_data *io_data, __maybe_unused SOCKETTYPE c, __maybe_unused char *param, bool isjson, char group) @@ -2977,7 +3202,6 @@ static void notify(struct io_data *io_data, __maybe_unused SOCKETTYPE c, __maybe static void devdetails(struct io_data *io_data, __maybe_unused SOCKETTYPE c, __maybe_unused char *param, bool isjson, __maybe_unused char group) { struct api_data *root = NULL; - char buf[TMPBUFSIZ]; bool io_open = false; struct cgpu_info *cgpu; int i; @@ -3003,8 +3227,7 @@ static void devdetails(struct io_data *io_data, __maybe_unused SOCKETTYPE c, __m root = api_add_const(root, "Model", cgpu->name ? : BLANK, false); root = api_add_const(root, "Device Path", cgpu->device_path ? : BLANK, false); - root = print_data(root, buf, isjson, isjson && (i > 0)); - io_add(io_data, buf); + root = print_data(io_data, root, isjson, isjson && (i > 0)); } if (isjson && io_open) @@ -3045,7 +3268,9 @@ void dosave(struct io_data *io_data, __maybe_unused SOCKETTYPE c, char *param, b static int itemstats(struct io_data *io_data, int i, char *id, struct cgminer_stats *stats, struct cgminer_pool_stats *pool_stats, struct api_data *extra, struct cgpu_info *cgpu, bool isjson) { struct api_data *root = NULL; - char buf[TMPBUFSIZ]; + double ghs; + + ghs = total_mhashes_done / 1000 / total_secs; root = api_add_int(root, "STATS", &i, false); root = api_add_string(root, "ID", id, false); @@ -3054,7 +3279,10 @@ static int itemstats(struct io_data *io_data, int i, char *id, struct cgminer_st root = api_add_timeval(root, "Wait", &(stats->getwork_wait), false); root = api_add_timeval(root, "Max", &(stats->getwork_wait_max), false); root = api_add_timeval(root, "Min", &(stats->getwork_wait_min), false); + root = api_add_mhs(root, "GHS 5s", &(g_displayed_rolling), false); + root = api_add_mhs(root, "GHS av", &(ghs), false); + /* if (pool_stats) { root = api_add_uint32(root, "Pool Calls", &(pool_stats->getwork_calls), false); root = api_add_uint32(root, "Pool Attempts", &(pool_stats->getwork_attempts), false); @@ -3077,7 +3305,7 @@ static int itemstats(struct io_data *io_data, int i, char *id, struct cgminer_st root = api_add_uint64(root, "Bytes Recv", &(pool_stats->bytes_received), false); root = api_add_uint64(root, "Net Bytes Sent", &(pool_stats->net_bytes_sent), false); root = api_add_uint64(root, "Net Bytes Recv", &(pool_stats->net_bytes_received), false); - } + }*/ if (extra) root = api_add_extra(root, extra); @@ -3099,6 +3327,7 @@ static int itemstats(struct io_data *io_data, int i, char *id, struct cgminer_st root = api_add_string(root, "USB Pipe", details, true); + /* snprintf(details, sizeof(details), "r%"PRIu64" %.6f w%"PRIu64" %.6f", cgpu->usbinfo.read_delay_count, @@ -3136,18 +3365,18 @@ static int itemstats(struct io_data *io_data, int i, char *id, struct cgminer_st cgpu->usbinfo.usb_tmo[2].total_tmo); } - root = api_add_string(root, "USB tmo", details, true); + root = api_add_string(root, "USB tmo", details, true);*/ #endif } - root = print_data(root, buf, isjson, isjson && (i > 0)); - io_add(io_data, buf); + root = print_data(io_data, root, isjson, isjson && (i > 0)); return ++i; } static void minerstats(struct io_data *io_data, __maybe_unused SOCKETTYPE c, __maybe_unused char *param, bool isjson, __maybe_unused char group) { + struct api_data *root = NULL; struct cgpu_info *cgpu; bool io_open = false; struct api_data *extra; @@ -3159,6 +3388,12 @@ static void minerstats(struct io_data *io_data, __maybe_unused SOCKETTYPE c, __m if (isjson) io_open = io_add(io_data, COMSTR JSON_MINESTATS); + root = api_add_string(root, "CGMiner", VERSION, false); + root = api_add_string(root, "Miner", g_miner_version, false); + root = api_add_string(root, "CompileTime", g_miner_compiletime, false); + root = api_add_string(root, "Type", g_miner_type, false); + root = print_data(io_data, root, isjson, false); + i = 0; for (j = 0; j < total_devices; j++) { cgpu = get_devices(j); @@ -3173,12 +3408,60 @@ static void minerstats(struct io_data *io_data, __maybe_unused SOCKETTYPE c, __m i = itemstats(io_data, i, id, &(cgpu->cgminer_stats), NULL, extra, cgpu, isjson); } } - + /* for (j = 0; j < total_pools; j++) { struct pool *pool = pools[j]; sprintf(id, "POOL%d", j); i = itemstats(io_data, i, id, &(pool->cgminer_stats), &(pool->cgminer_pool_stats), NULL, NULL, isjson); + }*/ + + if (isjson && io_open) + io_close(io_data); +} + +static void minerestats(struct io_data *io_data, __maybe_unused SOCKETTYPE c, __maybe_unused char *param, bool isjson, __maybe_unused char group) +{ + struct cgpu_info *cgpu; + bool io_open = false; + struct api_data *extra; + char id[20]; + int i, j; +#ifdef USE_USBUTILS + time_t howoldsec = 0; + + if (param && *param) + howoldsec = (time_t)atoi(param); +#endif + + message(io_data, MSG_MINESTATS, 0, NULL, isjson); + if (isjson) + io_open = io_add(io_data, COMSTR JSON_MINESTATS); + + i = 0; + for (j = 0; j < total_devices; j++) { + cgpu = get_devices(j); + if (!cgpu) + continue; +#ifdef USE_USBUTILS + if (cgpu->blacklisted) + continue; + if (cgpu->usbinfo.nodev) { + if (howoldsec <= 0) + continue; + if ((when - cgpu->usbinfo.last_nodev.tv_sec) >= howoldsec) + continue; + } +#endif + if (cgpu->drv) { + if (cgpu->drv->get_api_stats) + extra = cgpu->drv->get_api_stats(cgpu); + else + extra = NULL; + + sprintf(id, "%s%d", cgpu->drv->name, cgpu->device_id); + i = itemstats(io_data, i, id, &(cgpu->cgminer_stats), NULL, extra, cgpu, isjson); + } } if (isjson && io_open) @@ -3209,35 +3492,22 @@ static void failoveronly(struct io_data *io_data, __maybe_unused SOCKETTYPE c, c static void minecoin(struct io_data *io_data, __maybe_unused SOCKETTYPE c, __maybe_unused char *param, bool isjson, __maybe_unused char group) { struct api_data *root = NULL; - char buf[TMPBUFSIZ]; bool io_open; message(io_data, MSG_MINECOIN, 0, NULL, isjson); io_open = io_add(io_data, isjson ? COMSTR JSON_MINECOIN : _MINECOIN COMSTR); -#ifdef USE_SCRYPT - if (opt_scrypt) - root = api_add_const(root, "Hash Method", SCRYPTSTR, false); - else -#endif - root = api_add_const(root, "Hash Method", SHA256STR, false); + root = api_add_const(root, "Hash Method", SHA256STR, false); cg_rlock(&ch_lock); - if (current_fullhash && *current_fullhash) { - root = api_add_timeval(root, "Current Block Time", &block_timeval, true); - root = api_add_string(root, "Current Block Hash", current_fullhash, true); - } else { - struct timeval t = {0,0}; - root = api_add_timeval(root, "Current Block Time", &t, true); - root = api_add_const(root, "Current Block Hash", BLANK, false); - } + root = api_add_timeval(root, "Current Block Time", &block_timeval, true); + root = api_add_string(root, "Current Block Hash", current_hash, true); cg_runlock(&ch_lock); root = api_add_bool(root, "LP", &have_longpoll, false); root = api_add_diff(root, "Network Difficulty", ¤t_diff, true); - root = print_data(root, buf, isjson, false); - io_add(io_data, buf); + root = print_data(io_data, root, isjson, false); if (isjson && io_open) io_close(io_data); } @@ -3245,7 +3515,6 @@ static void minecoin(struct io_data *io_data, __maybe_unused SOCKETTYPE c, __may static void debugstate(struct io_data *io_data, __maybe_unused SOCKETTYPE c, char *param, bool isjson, __maybe_unused char group) { struct api_data *root = NULL; - char buf[TMPBUFSIZ]; bool io_open; if (param == NULL) @@ -3315,8 +3584,7 @@ static void debugstate(struct io_data *io_data, __maybe_unused SOCKETTYPE c, cha root = api_add_bool(root, "PerDevice", &want_per_device_stats, false); root = api_add_bool(root, "WorkTime", &opt_worktime, false); - root = print_data(root, buf, isjson, false); - io_add(io_data, buf); + root = print_data(io_data, root, isjson, false); if (isjson && io_open) io_close(io_data); } @@ -3363,7 +3631,6 @@ static void usbstats(struct io_data *io_data, __maybe_unused SOCKETTYPE c, __may struct api_data *root = NULL; #ifdef USE_USBUTILS - char buf[TMPBUFSIZ]; bool io_open = false; int count = 0; @@ -3381,16 +3648,14 @@ static void usbstats(struct io_data *io_data, __maybe_unused SOCKETTYPE c, __may if (isjson) io_open = io_add(io_data, COMSTR JSON_USBSTATS); - root = print_data(root, buf, isjson, false); - io_add(io_data, buf); + root = print_data(io_data, root, isjson, false); while (42) { root = api_usb_stats(&count); if (!root) break; - root = print_data(root, buf, isjson, isjson); - io_add(io_data, buf); + root = print_data(io_data, root, isjson, isjson); } if (isjson && io_open) @@ -3723,7 +3988,6 @@ static void ascidentify(struct io_data *io_data, __maybe_unused SOCKETTYPE c, ch static void asccount(struct io_data *io_data, __maybe_unused SOCKETTYPE c, __maybe_unused char *param, bool isjson, __maybe_unused char group) { struct api_data *root = NULL; - char buf[TMPBUFSIZ]; bool io_open; int count = 0; @@ -3736,8 +4000,7 @@ static void asccount(struct io_data *io_data, __maybe_unused SOCKETTYPE c, __may root = api_add_int(root, "Count", &count, false); - root = print_data(root, buf, isjson, false); - io_add(io_data, buf); + root = print_data(io_data, root, isjson, false); if (isjson && io_open) io_close(io_data); } @@ -3802,78 +4065,142 @@ static void ascset(struct io_data *io_data, __maybe_unused SOCKETTYPE c, __maybe } #endif +static void lcddata(struct io_data *io_data, __maybe_unused SOCKETTYPE c, __maybe_unused char *param, bool isjson, __maybe_unused char group) +{ + struct api_data *root = NULL; + struct cgpu_info *cgpu; + bool io_open; + double ghs = 0.0, last_share_diff = 0.0; + float temp = 0.0; + time_t last_share_time = 0; + time_t last_device_valid_work = 0; + struct pool *pool = NULL; + char *rpc_url = "none", *rpc_user = ""; + int i; + + message(io_data, MSG_LCD, 0, NULL, isjson); + io_open = io_add(io_data, isjson ? COMSTR JSON_LCD : _LCD COMSTR); + + // stop hashmeter() changing some while copying + mutex_lock(&hash_lock); + + root = api_add_elapsed(root, "Elapsed", &(total_secs), true); + ghs = total_mhashes_done / total_secs / 1000.0; + root = api_add_mhs(root, "GHS av", &ghs, true); + ghs = rolling5 / 1000.0; + root = api_add_mhs(root, "GHS 5m", &ghs, true); + ghs = total_rolling / 1000.0; + root = api_add_mhs(root, "GHS 5s", &ghs, true); + + mutex_unlock(&hash_lock); + + temp = 0; + last_device_valid_work = 0; + for (i = 0; i < total_devices; i++) { + cgpu = get_devices(i); + if (last_device_valid_work == 0 || + last_device_valid_work < cgpu->last_device_valid_work) + last_device_valid_work = cgpu->last_device_valid_work; + if (temp < cgpu->temp) + temp = cgpu->temp; + } + + last_share_time = 0; + last_share_diff = 0; + for (i = 0; i < total_pools; i++) { + pool = pools[i]; + + if (pool->removed) + continue; + + if (last_share_time == 0 || last_share_time < pool->last_share_time) { + last_share_time = pool->last_share_time; + last_share_diff = pool->last_share_diff; + } + } + pool = current_pool(); + if (pool) { + rpc_url = pool->rpc_url; + rpc_user = pool->rpc_user; + } + + root = api_add_temp(root, "Temperature", &temp, false); + root = api_add_diff(root, "Last Share Difficulty", &last_share_diff, false); + root = api_add_time(root, "Last Share Time", &last_share_time, false); + root = api_add_uint64(root, "Best Share", &best_diff, true); + root = api_add_time(root, "Last Valid Work", &last_device_valid_work, false); + root = api_add_uint(root, "Found Blocks", &found_blocks, true); + root = api_add_escape(root, "Current Pool", rpc_url, true); + root = api_add_escape(root, "User", rpc_user, true); + + root = print_data(io_data, root, isjson, false); + if (isjson && io_open) + io_close(io_data); +} + static void checkcommand(struct io_data *io_data, __maybe_unused SOCKETTYPE c, char *param, bool isjson, char group); struct CMDS { char *name; void (*func)(struct io_data *, SOCKETTYPE, char *, bool, char); bool iswritemode; + bool joinable; } cmds[] = { - { "version", apiversion, false }, - { "config", minerconfig, false }, - { "devs", devstatus, false }, - { "pools", poolstatus, false }, - { "summary", summary, false }, -#ifdef HAVE_OPENCL - { "gpuenable", gpuenable, true }, - { "gpudisable", gpudisable, true }, - { "gpurestart", gpurestart, true }, - { "gpu", gpudev, false }, -#endif + { "version", apiversion, false, true }, + { "config", minerconfig, false, true }, + { "devs", devstatus, false, true }, + { "edevs", edevstatus, false, true }, + { "pools", poolstatus, false, true }, + { "summary", summary, false, true }, #ifdef HAVE_AN_FPGA - { "pga", pgadev, false }, - { "pgaenable", pgaenable, true }, - { "pgadisable", pgadisable, true }, - { "pgaidentify", pgaidentify, true }, -#endif - { "gpucount", gpucount, false }, - { "pgacount", pgacount, false }, - { "switchpool", switchpool, true }, - { "addpool", addpool, true }, - { "poolpriority", poolpriority, true }, - { "enablepool", enablepool, true }, - { "disablepool", disablepool, true }, - { "removepool", removepool, true }, -#ifdef HAVE_OPENCL - { "gpuintensity", gpuintensity, true }, - { "gpumem", gpumem, true }, - { "gpuengine", gpuengine, true }, - { "gpufan", gpufan, true }, - { "gpuvddc", gpuvddc, true }, + { "pga", pgadev, false, false }, + { "pgaenable", pgaenable, true, false }, + { "pgadisable", pgadisable, true, false }, + { "pgaidentify", pgaidentify, true, false }, #endif - { "save", dosave, true }, - { "quit", doquit, true }, - { "privileged", privileged, true }, - { "notify", notify, false }, - { "devdetails", devdetails, false }, - { "restart", dorestart, true }, - { "stats", minerstats, false }, - { "check", checkcommand, false }, - { "failover-only", failoveronly, true }, - { "coin", minecoin, false }, - { "debug", debugstate, true }, - { "setconfig", setconfig, true }, - { "usbstats", usbstats, false }, + { "pgacount", pgacount, false, true }, + { "switchpool", switchpool, true, false }, + { "addpool", addpool, true, false }, + { "poolpriority", poolpriority, true, false }, + { "poolquota", poolquota, true, false }, + { "enablepool", enablepool, true, false }, + { "disablepool", disablepool, true, false }, + { "removepool", removepool, true, false }, + { "save", dosave, true, false }, + { "quit", doquit, true, false }, + { "privileged", privileged, true, false }, + { "notify", notify, false, true }, + { "devdetails", devdetails, false, true }, + { "restart", dorestart, true, false }, + { "stats", minerstats, false, true }, + { "estats", minerestats, false, true }, + { "check", checkcommand, false, false }, + { "failover-only", failoveronly, true, false }, + { "coin", minecoin, false, true }, + { "debug", debugstate, true, false }, + { "setconfig", setconfig, true, false }, + { "usbstats", usbstats, false, true }, #ifdef HAVE_AN_FPGA - { "pgaset", pgaset, true }, + { "pgaset", pgaset, true, false }, #endif - { "zero", dozero, true }, - { "hotplug", dohotplug, true }, + { "zero", dozero, true, false }, + { "hotplug", dohotplug, true, false }, #ifdef HAVE_AN_ASIC - { "asc", ascdev, false }, - { "ascenable", ascenable, true }, - { "ascdisable", ascdisable, true }, - { "ascidentify", ascidentify, true }, - { "ascset", ascset, true }, + { "asc", ascdev, false, false }, + { "ascenable", ascenable, true, false }, + { "ascdisable", ascdisable, true, false }, + { "ascidentify", ascidentify, true, false }, + { "ascset", ascset, true, false }, #endif - { "asccount", asccount, false }, - { NULL, NULL, false } + { "asccount", asccount, false, true }, + { "lcd", lcddisplay, false, true }, + { "lockstats", lockstats, true, true }, + { NULL, NULL, false, false } }; static void checkcommand(struct io_data *io_data, __maybe_unused SOCKETTYPE c, char *param, bool isjson, char group) { struct api_data *root = NULL; - char buf[TMPBUFSIZ]; bool io_open; char cmdbuf[100]; bool found, access; @@ -3904,36 +4231,74 @@ static void checkcommand(struct io_data *io_data, __maybe_unused SOCKETTYPE c, c root = api_add_const(root, "Exists", found ? YES : NO, false); root = api_add_const(root, "Access", access ? YES : NO, false); - root = print_data(root, buf, isjson, false); - io_add(io_data, buf); + root = print_data(io_data, root, isjson, false); if (isjson && io_open) io_close(io_data); } +static void head_join(struct io_data *io_data, char *cmdptr, bool isjson, bool *firstjoin) +{ + char *ptr; + + if (*firstjoin) { + if (isjson) + io_add(io_data, JSON0); + *firstjoin = false; + } else { + if (isjson) + io_add(io_data, JSON_BETWEEN_JOIN); + } + + // External supplied string + ptr = escape_string(cmdptr, isjson); + + if (isjson) { + io_add(io_data, JSON1); + io_add(io_data, ptr); + io_add(io_data, JSON2); + } else { + io_add(io_data, JOIN_CMD); + io_add(io_data, ptr); + io_add(io_data, BETWEEN_JOIN); + } + + if (ptr != cmdptr) + free(ptr); +} + +static void tail_join(struct io_data *io_data, bool isjson) +{ + if (io_data->close) { + io_add(io_data, JSON_CLOSE); + io_data->close = false; + } + + if (isjson) { + io_add(io_data, JSON_END); + io_add(io_data, JSON3); + } +} + static void send_result(struct io_data *io_data, SOCKETTYPE c, bool isjson) { - char buf[SOCKBUFSIZ + sizeof(JSON_CLOSE) + sizeof(JSON_END)]; - int count, res, tosend, len, n; + int count, sendc, res, tosend, len, n; + char *buf = io_data->ptr; strcpy(buf, io_data->ptr); if (io_data->close) strcat(buf, JSON_CLOSE); - if (isjson) { - if (io_data->full) - strcat(buf, JSON_END_TRUNCATED); - else - strcat(buf, JSON_END); - } + if (isjson) + strcat(buf, JSON_END); len = strlen(buf); tosend = len+1; applog(LOG_DEBUG, "API: send reply: (%d) '%.10s%s'", tosend, buf, len > 10 ? "..." : BLANK); - count = 0; - while (count++ < 5 && tosend > 0) { + count = sendc = 0; + while (count < 5 && tosend > 0) { // allow 50ms per attempt struct timeval timeout = {0, 50000}; fd_set wd; @@ -3946,28 +4311,34 @@ static void send_result(struct io_data *io_data, SOCKETTYPE c, bool isjson) } n = send(c, buf, tosend, 0); + sendc++; if (SOCKETFAIL(n)) { + count++; if (sock_blocks()) continue; - applog(LOG_WARNING, "API: send (%d) failed: %s", tosend, SOCKERRMSG); + applog(LOG_WARNING, "API: send (%d:%d) failed: %s", len+1, (len+1 - tosend), SOCKERRMSG); return; } else { - if (count <= 1) { + if (sendc <= 1) { if (n == tosend) applog(LOG_DEBUG, "API: sent all of %d first go", tosend); else applog(LOG_DEBUG, "API: sent %d of %d first go", n, tosend); } else { if (n == tosend) - applog(LOG_DEBUG, "API: sent all of remaining %d (count=%d)", tosend, count); + applog(LOG_DEBUG, "API: sent all of remaining %d (sendc=%d)", tosend, sendc); else - applog(LOG_DEBUG, "API: sent %d of remaining %d (count=%d)", n, tosend, count); + applog(LOG_DEBUG, "API: sent %d of remaining %d (sendc=%d)", n, tosend, sendc); } tosend -= n; + buf += n; + + if (n == 0) + count++; } } } @@ -4134,17 +4505,20 @@ static void setup_groups() /* * Interpret [W:]IP[/Prefix][,[R|W:]IP2[/Prefix2][,...]] --api-allow option + * ipv6 address should be enclosed with a pair of square brackets and the prefix left outside * special case of 0/0 allows /0 (means all IP addresses) */ -#define ALLIP4 "0/0" +#define ALLIP "0/0" /* * N.B. IP4 addresses are by Definition 32bit big endian on all platforms */ static void setup_ipaccess() { - char *buf, *ptr, *comma, *slash, *dot; - int ipcount, mask, octet, i; + char *buf, *ptr, *comma, *slash, *end; + int ipcount, mask, i, shift; + bool ipv6 = false; char group; + char tmp[30]; buf = malloc(strlen(opt_api_allow) + 1); if (unlikely(!buf)) @@ -4159,7 +4533,7 @@ static void setup_ipaccess() ipcount++; // possibly more than needed, but never less - ipaccess = calloc(ipcount, sizeof(struct IP4ACCESS)); + ipaccess = calloc(ipcount, sizeof(struct IPACCESS)); if (unlikely(!ipaccess)) quit(1, "Failed to calloc ipaccess"); @@ -4189,41 +4563,61 @@ static void setup_ipaccess() ipaccess[ips].group = group; - if (strcmp(ptr, ALLIP4) == 0) - ipaccess[ips].ip = ipaccess[ips].mask = 0; + if (strcmp(ptr, ALLIP) == 0) { + for (i = 0; i < 16; i++) { + ipaccess[ips].ip.s6_addr[i] = 0; + ipaccess[ips].mask.s6_addr[i] = 0; + } + } else { - slash = strchr(ptr, '/'); - if (!slash) - ipaccess[ips].mask = 0xffffffff; - else { + end = strchr(ptr, '/'); + if (!end) { + for (i = 0; i < 16; i++) + ipaccess[ips].mask.s6_addr[i] = 0xff; + end = ptr + strlen(ptr); + } + slash = end--; + if (*ptr == '[' && *end == ']') { + *(ptr++) = '\0'; + *(end--) = '\0'; + ipv6 = true; + } + else + ipv6 = false; + if (*slash) { *(slash++) = '\0'; mask = atoi(slash); - if (mask < 1 || mask > 32) + if (mask < 1 || (mask += ipv6 ? 0 : 96) > 128 ) goto popipo; // skip invalid/zero - ipaccess[ips].mask = 0; - while (mask-- >= 0) { - octet = 1 << (mask % 8); - ipaccess[ips].mask |= (octet << (24 - (8 * (mask >> 3)))); + for (i = 0; i < 16; i++) + ipaccess[ips].mask.s6_addr[i] = 0; + + i = 0; + shift = 7; + while (mask-- > 0) { + ipaccess[ips].mask.s6_addr[i] |= 1 << shift; + if (shift-- == 0) { + i++; + shift = 7; + } } } - ipaccess[ips].ip = 0; // missing default to '.0' - for (i = 0; ptr && (i < 4); i++) { - dot = strchr(ptr, '.'); - if (dot) - *(dot++) = '\0'; - - octet = atoi(ptr); - if (octet < 0 || octet > 0xff) - goto popipo; // skip invalid - - ipaccess[ips].ip |= (octet << (24 - (i * 8))); - - ptr = dot; + for (i = 0; i < 16; i++) + ipaccess[ips].ip.s6_addr[i] = 0; // missing default to '[::]' + if (ipv6) { + if (INET_PTON(AF_INET6, ptr, &(ipaccess[ips].ip)) != 1) + goto popipo; } - - ipaccess[ips].ip &= ipaccess[ips].mask; + else { + // v4 mapped v6 address, such as "::ffff:255.255.255.255" + sprintf(tmp, "::ffff:%s", ptr); + if (INET_PTON(AF_INET6, tmp, &(ipaccess[ips].ip)) != 1) + goto popipo; + } + for (i = 0; i < 16; i++) + ipaccess[ips].ip.s6_addr[i] &= ipaccess[ips].mask.s6_addr[i]; } ips++; @@ -4262,18 +4656,37 @@ static void *restart_thread(__maybe_unused void *userdata) return NULL; } -static bool check_connect(struct sockaddr_in *cli, char **connectaddr, char *group) +static bool check_connect(struct sockaddr_storage *cli, char **connectaddr, char *group) { bool addrok = false; - int i; + int i, j; + bool match; + char tmp[30]; + struct in6_addr client_ip; + + *connectaddr = (char *)malloc(INET6_ADDRSTRLEN); + getnameinfo((struct sockaddr *)cli, sizeof(*cli), + *connectaddr, INET6_ADDRSTRLEN, NULL, 0, NI_NUMERICHOST); - *connectaddr = inet_ntoa(cli->sin_addr); + // v4 mapped v6 address, such as "::ffff:255.255.255.255" + if (cli->ss_family == AF_INET) { + sprintf(tmp, "::ffff:%s", *connectaddr); + INET_PTON(AF_INET6, tmp, &client_ip); + } + else + INET_PTON(AF_INET6, *connectaddr, &client_ip); *group = NOPRIVGROUP; if (opt_api_allow) { - int client_ip = htonl(cli->sin_addr.s_addr); for (i = 0; i < ips; i++) { - if ((client_ip & ipaccess[i].mask) == ipaccess[i].ip) { + match = true; + for (j = 0; j < 16; j++) + if ((client_ip.s6_addr[j] & ipaccess[i].mask.s6_addr[j]) + != ipaccess[i].ip.s6_addr[j]) { + match = false; + break; + } + if (match) { addrok = true; *group = ipaccess[i].group; break; @@ -4283,7 +4696,8 @@ static bool check_connect(struct sockaddr_in *cli, char **connectaddr, char *gro if (opt_api_network) addrok = true; else - addrok = (strcmp(*connectaddr, localaddr) == 0); + addrok = (strcmp(*connectaddr, localaddr) == 0) + || IN6_IS_ADDR_LOOPBACK(&client_ip); } return addrok; @@ -4291,13 +4705,11 @@ static bool check_connect(struct sockaddr_in *cli, char **connectaddr, char *gro static void mcast() { - struct sockaddr_in listen; - struct ip_mreq grp; - struct sockaddr_in came_from; + struct sockaddr_storage came_from; time_t bindstart; char *binderror; - SOCKETTYPE mcast_sock; - SOCKETTYPE reply_sock; + SOCKETTYPE mcast_sock = INVSOCK; + SOCKETTYPE reply_sock = INVSOCK; socklen_t came_from_siz; char *connectaddr; ssize_t rep; @@ -4307,19 +4719,31 @@ static void mcast() bool addrok; char group; + char port_s[10], came_from_port[10]; + struct addrinfo hints, *res, *host, *client; + char expect[] = "cgminer-"; // first 8 bytes constant char *expect_code; size_t expect_code_len; char buf[1024]; char replybuf[1024]; - memset(&grp, 0, sizeof(grp)); - grp.imr_multiaddr.s_addr = inet_addr(opt_api_mcast_addr); - if (grp.imr_multiaddr.s_addr == INADDR_NONE) - quit(1, "Invalid Multicast Address"); - grp.imr_interface.s_addr = INADDR_ANY; - - mcast_sock = socket(AF_INET, SOCK_DGRAM, 0); + sprintf(port_s, "%d", opt_api_mcast_port); + memset(&hints, 0, sizeof(hints)); + hints.ai_family = AF_UNSPEC; + if (getaddrinfo(opt_api_mcast_addr, port_s, &hints, &res) != 0) + quit(1, "Invalid API Multicast Address"); + host = res; + while (host != NULL) { + mcast_sock = socket(res->ai_family, SOCK_DGRAM, 0); + if (mcast_sock > 0) + break; + host = host->ai_next; + } + if (mcast_sock == INVSOCK) { + freeaddrinfo(res); + quit(1, "API mcast could not open socket"); + } int optval = 1; if (SOCKETFAIL(setsockopt(mcast_sock, SOL_SOCKET, SO_REUSEADDR, (void *)(&optval), sizeof(optval)))) { @@ -4327,16 +4751,11 @@ static void mcast() goto die; } - memset(&listen, 0, sizeof(listen)); - listen.sin_family = AF_INET; - listen.sin_addr.s_addr = INADDR_ANY; - listen.sin_port = htons(opt_api_mcast_port); - // try for more than 1 minute ... in case the old one hasn't completely gone yet bound = 0; bindstart = time(NULL); while (bound == 0) { - if (SOCKETFAIL(bind(mcast_sock, (struct sockaddr *)(&listen), sizeof(listen)))) { + if (SOCKETFAIL(bind(mcast_sock, host->ai_addr, host->ai_addrlen))) { binderror = SOCKERRMSG; if ((time(NULL) - bindstart) > 61) break; @@ -4347,14 +4766,41 @@ static void mcast() } if (bound == 0) { - applog(LOG_ERR, "API mcast bind to port %d failed (%s)%s", opt_api_port, binderror, MUNAVAILABLE); + applog(LOG_ERR, "API mcast bind to port %d failed (%s)%s", opt_api_mcast_port, binderror, MUNAVAILABLE); goto die; } - if (SOCKETFAIL(setsockopt(mcast_sock, IPPROTO_IP, IP_ADD_MEMBERSHIP, (void *)(&grp), sizeof(grp)))) { + switch (host->ai_family) { + case AF_INET: { + struct ip_mreq grp; + memset(&grp, 0, sizeof(grp)); + grp.imr_multiaddr.s_addr = ((struct sockaddr_in *)(host->ai_addr))->sin_addr.s_addr; + grp.imr_interface.s_addr = INADDR_ANY; + + if (SOCKETFAIL(setsockopt(mcast_sock, IPPROTO_IP, IP_ADD_MEMBERSHIP, + (void *)(&grp), sizeof(grp)))) { + applog(LOG_ERR, "API mcast join failed (%s)%s", SOCKERRMSG, MUNAVAILABLE); + goto die; + } + break; + } + case AF_INET6: { + struct ipv6_mreq grp; + memcpy(&grp.ipv6mr_multiaddr, &(((struct sockaddr_in6 *)(host->ai_addr))->sin6_addr), + sizeof(struct in6_addr)); + grp.ipv6mr_interface= 0; + + if (SOCKETFAIL(setsockopt(mcast_sock, IPPROTO_IPV6, IPV6_ADD_MEMBERSHIP, + (void *)(&grp), sizeof(grp)))) { applog(LOG_ERR, "API mcast join failed (%s)%s", SOCKERRMSG, MUNAVAILABLE); goto die; } + break; + } + default: + break; + } + freeaddrinfo(res); expect_code_len = sizeof(expect) + strlen(opt_api_mcast_code); expect_code = malloc(expect_code_len+1); @@ -4368,7 +4814,7 @@ static void mcast() count++; came_from_siz = sizeof(came_from); - if (SOCKETFAIL(rep = recvfrom(mcast_sock, buf, sizeof(buf), + if (SOCKETFAIL(rep = recvfrom(mcast_sock, buf, sizeof(buf) - 1, 0, (struct sockaddr *)(&came_from), &came_from_siz))) { applog(LOG_DEBUG, "API mcast failed count=%d (%s) (%d)", count, SOCKERRMSG, (int)mcast_sock); @@ -4385,10 +4831,11 @@ static void mcast() if (rep > 0 && buf[rep-1] == '\n') buf[--rep] = '\0'; - applog(LOG_DEBUG, "API mcast request rep=%d (%s) from %s:%d", - (int)rep, buf, - inet_ntoa(came_from.sin_addr), - ntohs(came_from.sin_port)); + getnameinfo((struct sockaddr *)(&came_from), came_from_siz, + NULL, 0, came_from_port, sizeof(came_from_port), NI_NUMERICHOST); + + applog(LOG_DEBUG, "API mcast request rep=%d (%s) from [%s]:%s", + (int)rep, buf, connectaddr, came_from_port); if ((size_t)rep > expect_code_len && memcmp(buf, expect_code, expect_code_len) == 0) { reply_port = atoi(&buf[expect_code_len]); @@ -4399,16 +4846,30 @@ static void mcast() applog(LOG_DEBUG, "API mcast request OK port %s=%d", &buf[expect_code_len], reply_port); - came_from.sin_port = htons(reply_port); - reply_sock = socket(AF_INET, SOCK_DGRAM, 0); + if (getaddrinfo(connectaddr, &buf[expect_code_len], &hints, &res) != 0) { + applog(LOG_ERR, "Invalid client address %s", connectaddr); + continue; + } + client = res; + while (client) { + reply_sock = socket(res->ai_family, SOCK_DGRAM, 0); + if (mcast_sock > 0) + break; + client = client->ai_next; + } + if (reply_sock == INVSOCK) { + freeaddrinfo(res); + applog(LOG_ERR, "API mcast could not open socket to client %s", connectaddr); + continue; + } snprintf(replybuf, sizeof(replybuf), "cgm-" API_MCAST_CODE "-%d-%s", opt_api_port, opt_api_mcast_des); rep = sendto(reply_sock, replybuf, strlen(replybuf)+1, - 0, (struct sockaddr *)(&came_from), - sizeof(came_from)); + 0, client->ai_addr, client->ai_addrlen); + freeaddrinfo(res); if (SOCKETFAIL(rep)) { applog(LOG_DEBUG, "API mcast send reply failed (%s) (%d)", SOCKERRMSG, (int)reply_sock); @@ -4435,7 +4896,7 @@ static void *mcast_thread(void *userdata) pthread_detach(pthread_self()); pthread_setcanceltype(PTHREAD_CANCEL_ASYNCHRONOUS, NULL); - RenameThread("api_mcast"); + RenameThread("APIMcast"); mcast(); @@ -4468,20 +4929,21 @@ void api(int api_thr_id) char *binderror; time_t bindstart; short int port = opt_api_port; - struct sockaddr_in serv; - struct sockaddr_in cli; + char port_s[10]; + struct sockaddr_storage cli; socklen_t clisiz; char cmdbuf[100]; - char *cmd; + char *cmd = NULL; char *param; bool addrok; char group; json_error_t json_err; - json_t *json_config; + json_t *json_config = NULL; json_t *json_val; bool isjson; - bool did; + bool did, isjoin = false, firstjoin; int i; + struct addrinfo hints, *res, *host; SOCKETTYPE *apisock; @@ -4490,6 +4952,7 @@ void api(int api_thr_id) if (!opt_api_listen) { applog(LOG_DEBUG, "API not running%s", UNAVAILABLE); + free(apisock); return; } @@ -4507,6 +4970,7 @@ void api(int api_thr_id) if (ips == 0) { applog(LOG_WARNING, "API not running (no valid IPs specified)%s", UNAVAILABLE); + free(apisock); return; } } @@ -4515,25 +4979,28 @@ void api(int api_thr_id) * to ensure curl has already called WSAStartup() in windows */ cgsleep_ms(opt_log_interval*1000); - *apisock = socket(AF_INET, SOCK_STREAM, 0); - if (*apisock == INVSOCK) { - applog(LOG_ERR, "API1 initialisation failed (%s)%s", SOCKERRMSG, UNAVAILABLE); + sprintf(port_s, "%d", port); + memset(&hints, 0, sizeof(hints)); + hints.ai_flags = AI_PASSIVE; + hints.ai_family = AF_UNSPEC; + if (getaddrinfo(opt_api_host, port_s, &hints, &res) != 0) { + applog(LOG_ERR, "API failed to resolve %s", opt_api_host); + free(apisock); return; } - - memset(&serv, 0, sizeof(serv)); - - serv.sin_family = AF_INET; - - if (!opt_api_allow && !opt_api_network) { - serv.sin_addr.s_addr = inet_addr(localaddr); - if (serv.sin_addr.s_addr == (in_addr_t)INVINETADDR) { - applog(LOG_ERR, "API2 initialisation failed (%s)%s", SOCKERRMSG, UNAVAILABLE); + host = res; + while (host) { + *apisock = socket(res->ai_family, SOCK_STREAM, 0); + if (*apisock > 0) + break; + host = host->ai_next; + } + if (*apisock == INVSOCK) { + applog(LOG_ERR, "API initialisation failed (%s)%s", SOCKERRMSG, UNAVAILABLE); + freeaddrinfo(res); + free(apisock); return; } - } - - serv.sin_port = htons(port); #ifndef WIN32 // On linux with SO_REUSEADDR, bind will get the port if the previous @@ -4553,7 +5020,7 @@ void api(int api_thr_id) bound = 0; bindstart = time(NULL); while (bound == 0) { - if (SOCKETFAIL(bind(*apisock, (struct sockaddr *)(&serv), sizeof(serv)))) { + if (SOCKETFAIL(bind(*apisock, host->ai_addr, host->ai_addrlen))) { binderror = SOCKERRMSG; if ((time(NULL) - bindstart) > 61) break; @@ -4564,15 +5031,18 @@ void api(int api_thr_id) } else bound = 1; } + freeaddrinfo(res); if (bound == 0) { applog(LOG_ERR, "API bind to port %d failed (%s)%s", port, binderror, UNAVAILABLE); + free(apisock); return; } if (SOCKETFAIL(listen(*apisock, QUEUE))) { applog(LOG_ERR, "API3 initialisation failed (%s)%s", SOCKERRMSG, UNAVAILABLE); CLOSESOCKET(*apisock); + free(apisock); return; } @@ -4588,6 +5058,8 @@ void api(int api_thr_id) if (opt_api_mcast) mcast_init(); + strbufs = k_new_list("StrBufs", sizeof(SBITEM), ALLOC_SBITEMS, LIMIT_SBITEMS, false); + while (!bye) { clisiz = sizeof(cli); if (SOCKETFAIL(c = accept(*apisock, (struct sockaddr *)(&cli), &clisiz))) { @@ -4595,7 +5067,7 @@ void api(int api_thr_id) goto die; } - addrok = check_connect(&cli, &connectaddr, &group); + addrok = check_connect((struct sockaddr_storage *)&cli, &connectaddr, &group); applog(LOG_DEBUG, "API: connection from %s - %s", connectaddr, addrok ? "Accepted" : "Ignored"); @@ -4634,33 +5106,24 @@ void api(int api_thr_id) param = NULL; -#if JANSSON_MAJOR_VERSION > 2 || (JANSSON_MAJOR_VERSION == 2 && JANSSON_MINOR_VERSION > 0) json_config = json_loadb(buf, n, 0, &json_err); -#elif JANSSON_MAJOR_VERSION > 1 - json_config = json_loads(buf, 0, &json_err); -#else - json_config = json_loads(buf, &json_err); -#endif if (!json_is_object(json_config)) { message(io_data, MSG_INVJSON, 0, NULL, isjson); send_result(io_data, c, isjson); did = true; - } - else { + } else { json_val = json_object_get(json_config, JSON_COMMAND); if (json_val == NULL) { message(io_data, MSG_MISCMD, 0, NULL, isjson); send_result(io_data, c, isjson); did = true; - } - else { + } else { if (!json_is_string(json_val)) { message(io_data, MSG_INVCMD, 0, NULL, isjson); send_result(io_data, c, isjson); did = true; - } - else { + } else { cmd = (char *)json_string_value(json_val); json_val = json_object_get(json_config, JSON_PARAMETER); if (json_is_string(json_val)) @@ -4677,27 +5140,85 @@ void api(int api_thr_id) } } - if (!did) + if (!did) { + char *cmdptr, *cmdsbuf = NULL; + + if (strchr(cmd, CMDJOIN)) { + firstjoin = isjoin = true; + // cmd + leading+tailing '|' + '\0' + cmdsbuf = malloc(strlen(cmd) + 3); + if (!cmdsbuf) + quithere(1, "OOM cmdsbuf"); + strcpy(cmdsbuf, "|"); + param = NULL; + } else + firstjoin = isjoin = false; + + cmdptr = cmd; + do { + did = false; + if (isjoin) { + cmd = strchr(cmdptr, CMDJOIN); + if (cmd) + *(cmd++) = '\0'; + if (!*cmdptr) + goto inochi; + } + for (i = 0; cmds[i].name != NULL; i++) { - if (strcmp(cmd, cmds[i].name) == 0) { - sprintf(cmdbuf, "|%s|", cmd); - if (ISPRIVGROUP(group) || strstr(COMMANDS(group), cmdbuf)) - (cmds[i].func)(io_data, c, param, isjson, group); - else { - message(io_data, MSG_ACCDENY, 0, cmds[i].name, isjson); - applog(LOG_DEBUG, "API: access denied to '%s' for '%s' command", connectaddr, cmds[i].name); + if (strcmp(cmdptr, cmds[i].name) == 0) { + sprintf(cmdbuf, "|%s|", cmdptr); + if (isjoin) { + if (strstr(cmdsbuf, cmdbuf)) { + did = true; + break; + } + strcat(cmdsbuf, cmdptr); + strcat(cmdsbuf, "|"); + head_join(io_data, cmdptr, isjson, &firstjoin); + if (!cmds[i].joinable) { + message(io_data, MSG_ACCDENY, 0, cmds[i].name, isjson); + did = true; + tail_join(io_data, isjson); + break; + } } - - send_result(io_data, c, isjson); - did = true; - break; + if (ISPRIVGROUP(group) || strstr(COMMANDS(group), cmdbuf)) + (cmds[i].func)(io_data, c, param, isjson, group); + else { + message(io_data, MSG_ACCDENY, 0, cmds[i].name, isjson); + applog(LOG_DEBUG, "API: access denied to '%s' for '%s' command", connectaddr, cmds[i].name); } + + did = true; + if (!isjoin) + send_result(io_data, c, isjson); + else + tail_join(io_data, isjson); + break; } + } if (!did) { + if (isjoin) + head_join(io_data, cmdptr, isjson, &firstjoin); message(io_data, MSG_INVCMD, 0, NULL, isjson); + if (isjoin) + tail_join(io_data, isjson); + else send_result(io_data, c, isjson); } +inochi: + if (isjoin) + cmdptr = cmd; + } while (isjoin && cmdptr); + } + + if (isjoin) + send_result(io_data, c, isjson); + + if (isjson && json_is_object(json_config)) + json_decref(json_config); } } CLOSESOCKET(c); @@ -4709,6 +5230,8 @@ void api(int api_thr_id) ; pthread_cleanup_pop(true); + free(apisock); + if (opt_debug) applog(LOG_DEBUG, "API: terminating due to: %s", do_a_quit ? "QUIT" : (do_a_restart ? "RESTART" : (bye ? "BYE" : "UNKNOWN!"))); diff --git a/autogen.sh b/autogen.sh index 889c739445..cae99ba0f3 100755 --- a/autogen.sh +++ b/autogen.sh @@ -1,22 +1,11 @@ #!/bin/sh -bs_dir="$(dirname $(readlink -f $0))" -rm -rf "${bs_dir}"/autom4te.cache -rm -f "${bs_dir}"/aclocal.m4 "${bs_dir}"/ltmain.sh +bs_dir=$(cd "$(dirname "$0")"; pwd) -libusb_dir="${bs_dir}"/compat/libusb-1.0/ -rm -rf "${libusb_dir}"/autom4te.cache -rm -rf "${libusb_dir}"/aclocal.m4 "${libusb_dir}"/ltmain.sh +#Some versions of libtoolize don't like there being no ltmain.sh file already +touch "${bs_dir}"/ltmain.sh +autoreconf -fi "${bs_dir}" -echo 'Running autoreconf -if...' -aclocal --force -I m4 -libtoolize --install --copy --force -autoheader --force -automake --add-missing --copy --force-missing -autoconf --force - -autoreconf -fi "${libusb_dir}" - -if test -z "$NOCONFIGURE" ; then +if test -n "$1" && test -z "$NOCONFIGURE" ; then echo 'Configuring...' "$bs_dir"/configure "$@" fi diff --git a/bench_block.h b/bench_block.h index a1aa35a80b..729a95e4ff 100644 --- a/bench_block.h +++ b/bench_block.h @@ -1,39 +1,170 @@ -#if !defined(__BENCH_BLOCK_H__) - #define __BENCH_BLOCK_H__ 1 - - // Random work pulled from a pool - #define CGMINER_BENCHMARK_BLOCK \ - 0x00, 0x00, 0x00, 0x01, 0x20, 0x00, 0xD8, 0x07, 0x17, 0xC9, 0x13, 0x6F, 0xDC, 0xBE, 0xDE, 0xB7, \ - 0xB2, 0x14, 0xEF, 0xD1, 0x72, 0x7F, 0xA3, 0x72, 0xB2, 0x5D, 0x88, 0xF0, 0x00, 0x00, 0x05, 0xAA, \ - 0x00, 0x00, 0x00, 0x00, 0x92, 0x8B, 0x4C, 0x77, 0xF5, 0xB2, 0xE6, 0x56, 0x96, 0x27, 0xE0, 0x66, \ - 0x3C, 0x5B, 0xDD, 0xDC, 0x88, 0x6A, 0x7D, 0x7C, 0x7B, 0x8C, 0xE4, 0x92, 0x38, 0x92, 0x58, 0x2E, \ - 0x18, 0x4D, 0x95, 0x9E, 0x4E, 0x44, 0xF1, 0x5F, 0x1A, 0x08, 0xE1, 0xE5, 0x00, 0x00, 0x00, 0x00, \ - 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x02, 0x00, 0x00, \ - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ - 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, \ - 0x86, 0x7E, 0x3A, 0xAF, 0x37, 0x83, 0xAF, 0xA0, 0xB5, 0x33, 0x2C, 0x28, 0xED, 0xA9, 0x89, 0x3E, \ - 0x0A, 0xB6, 0x46, 0x81, 0xC2, 0x71, 0x4F, 0x34, 0x5A, 0x74, 0x89, 0x0E, 0x2B, 0x04, 0xB3, 0x16, \ - 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, \ - 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, \ - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xA0, 0xF6, 0x09, 0x02, 0x00, 0x00, 0x00, 0x00, \ - 0x55, 0xF1, 0x44, 0x4E, 0x00, 0x00, 0x00, 0x00, 0x79, 0x63, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, \ - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ - -#endif // !defined(__BENCH_BLOCK_H__) +#ifndef __BENCH_BLOCK_H__ +#define __BENCH_BLOCK_H__ + +/* This contains 32 carefully chosen work items, 16 of which return diff >= 32 + * at nonces spaced ~ 0x10000000 apart and 16 < diff 32. */ + +const char bench_hidiffs[16][324] = { +// 0002108b diff 131 +"000000029c6bf469abe4ad37605c097a860cff3cf5c1ef4377618f74000000000000000082b1514e7b6565941e5824f084292164ec5f97e7ea20c494bd96e524d478977b536dd2261900896c8b100200" +"000000800000000000000000000000000000000000000000000000000000000000000000000000000000000080020000" +"64e4e3becc01064d808269b330f40f4de82dc92e894d635025daa3e2e2c410b4", + +// 1003dacf diff 37 +"00000002e790c23987181950eeb144591c3ac4d06c0705f2801d097600000000000000009ebbce2f5f0d6cc0aca284ecb1059c856ef2f7f42e7edd403d246754ee4c905a536dd2a91900896ccfda0310" +"000000800000000000000000000000000000000000000000000000000000000000000000000000000000000080020000" +"4a78daf1b5eb3397af1c00dbd9b06659cdc04183c8baaf5be1dbf32f79e00459", + +// 200e57b4 diff 3866 +"000000023e91fce7300a792bfbaa0c76e1aa5f9b546c1db582aee4ff0000000000000000f04650a8e748d2e6fde86a8a920b285f3e22398f583700236958323ef9ea8321536dcf431900896cb4570e20" +"000000800000000000000000000000000000000000000000000000000000000000000000000000000000000080020000" +"0a1d654ae2b06f219ccf4601933fab408de1c3b7c8c9c85e03231d4aaf5a26cd", + +// 300f71e2 diff 335 +"000000023e91fce7300a792bfbaa0c76e1aa5f9b546c1db582aee4ff000000000000000074b39134c2930d2f2e7339f9d502c776c44d6ee599f7efebec6c9bbd04787aae536dce561900896ce2710f30" +"000000800000000000000000000000000000000000000000000000000000000000000000000000000000000080020000" +"94e60c1180022f337232ab3d298f838304b6008ab237cf7e1717f1933407e592", + +// 400548ed diff 2670 +"000000023e91fce7300a792bfbaa0c76e1aa5f9b546c1db582aee4ff0000000000000000c5b821fb0b26d63b00cc26e7ac4d6cfd1d3fc109b0db188e7e792e3d18342919536dce501900896ced480540" +"000000800000000000000000000000000000000000000000000000000000000000000000000000000000000080020000" +"a290eac61642949c00d17f7cd5980abedb8647fc5df9955dcfe4d56a50a0c564", + +// 5001f760 diff 60 +"00000002e790c23987181950eeb144591c3ac4d06c0705f2801d097600000000000000006e9d94bf5a0ab7b202d39e1200af96074e4f641f4e55e3e9e3aee72aa00a70e9536dd2ae1900896c60f70150" +"000000800000000000000000000000000000000000000000000000000000000000000000000000000000000080020000" +"1477ca8536702eacbd65a6a162cfe90d62016a14ffe58d52b7dd4c3628a27e5b", + +// 600c9816 diff 35 +"00000002194bb5b4f8ac3392fbd66f3dd3e9dcdb22370e380837fe44000000000000000003bbb250f2dc23717e8192c0b8bec6a175cd059e4089d325006eaee3446254c9536dd39e1900896c16980c60" +"000000800000000000000000000000000000000000000000000000000000000000000000000000000000000080020000" +"68db599d6b7a55fd61d4244a3dfa465055ead6b5c0a37c7a3d4555b58e99065e", + +// 70092d5f diff 114 +"000000023e91fce7300a792bfbaa0c76e1aa5f9b546c1db582aee4ff000000000000000072e17babd4089b204797cebda7dc6e277950eab1b2908991ae1d72335f82d204536dcf441900896c5f2d0970" +"000000800000000000000000000000000000000000000000000000000000000000000000000000000000000080020000" +"c7d601ce3b01e569a49508d541bbcba9b3c8394b1834523ef1e5cb2c60bd34a3", + +// 800eeaa8 diff 159 +"000000029c6bf469abe4ad37605c097a860cff3cf5c1ef4377618f74000000000000000022388b6f022144db134af1bc8e61b385ca37cae038c1d165ae98c496b3b41e8b536dd2101900896ca8ea0e80" +"000000800000000000000000000000000000000000000000000000000000000000000000000000000000000080020000" +"410761e97e67b494fd547cfe9ffbb36893da7aec75c6b51b8d5f38f87b5d63cf", + +// 900f600d diff 144 +"000000029c6bf469abe4ad37605c097a860cff3cf5c1ef4377618f7400000000000000000e1f0cfdf5ad8248fc4520f3bb0b2040226430348cddeff5ca9181beeb78870d536dd2161900896c0d600f90" +"000000800000000000000000000000000000000000000000000000000000000000000000000000000000000080020000" +"ad1a8d354a7e8b13ec47f4c3d907d00945a61e86059f4943e42c1e52398eba5d", + +// a00210bf diff 1055 +"00000002194bb5b4f8ac3392fbd66f3dd3e9dcdb22370e380837fe4400000000000000002232a16d38cc0e13e4b16d917bff4c34727deb3b5c50e424fb8453ff9b2adcb4536dd4231900896cbf1002a0" +"000000800000000000000000000000000000000000000000000000000000000000000000000000000000000080020000" +"adc67d38f31f589b18b9d8e531b994ce5733c021a03d88d38611ee6b4c2710a5", + +// b004309d diff 43 +"00000002194bb5b4f8ac3392fbd66f3dd3e9dcdb22370e380837fe440000000000000000a2860471277b4a93fea2a8b6d8c281fab7bde3b78f2acd1bfdc89d464ed3bb3c536dd35e1900896c9d3004b0" +"000000800000000000000000000000000000000000000000000000000000000000000000000000000000000080020000" +"537686c611aae4397c7c04b2c190708453d00e8c9563525610c31ba46e80dbc2", + +// c00b7537 diff 64 +"00000002194bb5b4f8ac3392fbd66f3dd3e9dcdb22370e380837fe440000000000000000f370230607998fbbd10275c5890885fcd81b68018ba2373abf0f93a06d02ab28536dd33e1900896c37750bc0" +"000000800000000000000000000000000000000000000000000000000000000000000000000000000000000080020000" +"1fdda952da6abd70022a6e5f2b9dc5e1b66011128c3fa249f0b7439f00d5943e", + +// d0005bd5 diff 1539 +"00000002c0a2c91fc41254539a5b2a27be28de2a6187e2af3f129d6300000000000000005e45ffc512d5ca3bc4d2063dd3af1669c296ae126a5a2ef896d1e190cedf67b9536dd46b1900896cd55b00d0" +"000000800000000000000000000000000000000000000000000000000000000000000000000000000000000080020000" +"07094d6cbe76538a88612624fc5e655cc405cb8198dcad516b88dbac5bf8b906", + +// e00a7796 diff 41 +"00000002194bb5b4f8ac3392fbd66f3dd3e9dcdb22370e380837fe44000000000000000027c548815127c125147af91c356c293f0defbd2771f8dc3b1142b367528656db536dd37c1900896c96770ae0" +"000000800000000000000000000000000000000000000000000000000000000000000000000000000000000080020000" +"1bdbb3b1be7216872ea787627b03c389a527451f6dd832d8540874306f9c07c6", + +// f001f029 diff 77 +"00000002194bb5b4f8ac3392fbd66f3dd3e9dcdb22370e380837fe440000000000000000adef758770bb90c5b13769c5b61affb322b24c747573b38ebe2ee81748d0b557536dd4071900896c29f001f0" +"000000800000000000000000000000000000000000000000000000000000000000000000000000000000000080020000" +"1ac8eea63285353944e40eec54d2dd6cd0994b447429bb0ed0598d38f42da0e2" +}; + +const char bench_lodiffs[16][324] = { +// 000ed6b6 diff 2 +"00000002c01f502cb3e9fdb053230ec12a4954c1021a6b35862b5e29000000000000000084d1b83ae44057025e8c5b5756b44f04df5fffe4a7a30e5c12d12a97a7a4c2ea536dce431900896cb6d60e00" +"000000800000000000000000000000000000000000000000000000000000000000000000000000000000000080020000" +"d08f7e14c50dad77dc238b4db2901a0578e657b1954779ab9cd82a73829edf7f", + +// 1000818f diff 5 +"000000023bf53ef343a50f7599601f849c93ecce63530b0b449a44630000000000000000c1a174254a6593ffba987f68fe26e716e3c129a7f33a9c43ae7ecf90c8cd0d2c536dc4e61900896c8f810010" +"000000800000000000000000000000000000000000000000000000000000000000000000000000000000000080020000" +"6700aeedada2b3877900b58a183c42c40949956bb8b4a8d21481f8936b572922", + +// 20006be9 diff 7 +"00000002138cf4b61dff74e3c26b2d80045064e8ab4802521bab2cda000000000000000071eef64a7ef4e47cda16e96673197d36c7235a4aadd23c21a38ce53827d1f8bc536dc4d71900896ce96b0020" +"000000800000000000000000000000000000000000000000000000000000000000000000000000000000000080020000" +"0730cf7a6b8a85eb1cc017b109d23c392464f99aa8c020ea107c525b671adde0", + +// 300029f4 diff 2 +"000000023bf53ef343a50f7599601f849c93ecce63530b0b449a44630000000000000000fe2c6b926468565e524ab7c2f111035dcde7c60955842111930589eccb410f83536dc66b1900896cf4290030" +"000000800000000000000000000000000000000000000000000000000000000000000000000000000000000080020000" +"27dbb374a97f15c59587256662f36904d075d0e61f749618182711288ac617c7", + +// 40001d82 diff 2 +"000000023bf53ef343a50f7599601f849c93ecce63530b0b449a4463000000000000000003073385e05c29f0435a6001c8eca9c8d5602890aeff9d4d103d3383cf80dae5536dc57c1900896c821d0040" +"000000800000000000000000000000000000000000000000000000000000000000000000000000000000000080020000" +"7da3b97e82c0c3125a58dad8a0d1d0369244731f3b096e972484298d15b843d9", + +// 50003ce6 diff 1 +"000000029ca55e5f1bc0328c84f358fddadc13cb232599bc2ca9dbe10000000000000000b5b4d19c20a7fc2b174ff673c006edd2247c4b2336571864df93eb7ec0c8c276536dfe041900896ce63c0050" +"000000800000000000000000000000000000000000000000000000000000000000000000000000000000000080020000" +"1514bd586511e531e2b6277a6d112b171f9e008d56ef4a971e619acf22e75072", + +// 60004314 diff 2 +"000000023bf53ef343a50f7599601f849c93ecce63530b0b449a446300000000000000003e3030629ff4258056dc9efaf922bd173a65f65ee799b0c765097d3deeddef10536dc4d81900896c14430060" +"000000800000000000000000000000000000000000000000000000000000000000000000000000000000000080020000" +"dcb77a9c36d894d2dbc31437e5c2a1564e927937848ea2eb20b38638afc64b96", + +// 700041d4 diff 7 +"000000023bf53ef343a50f7599601f849c93ecce63530b0b449a446300000000000000005513c22bb99e9daa9936b0df5dce64d7737e3706be99e5098d112002492cf81b536dc5691900896cd4410070" +"000000800000000000000000000000000000000000000000000000000000000000000000000000000000000080020000" +"0d896267fda5dda0f85308e77f754c8b94b7b88e3cb315475cd9efd16401e3ce", + +// 80009d99 diff 1 +"00000002e155f07e652e4d671ca4db51bbde14d2b5ae34ee67ecc74400000000000000004af5cffd7e5a7087f1b484b526c7350c86d8389283509ca878502f792115e8dc536dc6ad1900896c999d0080" +"000000800000000000000000000000000000000000000000000000000000000000000000000000000000000080020000" +"339354568f506ac3cd69bb427b1af83a0473b87c16bf3b562a93d0a2ffc53e54", + +// 9000fb14 diff 4 +"000000029ca55e5f1bc0328c84f358fddadc13cb232599bc2ca9dbe100000000000000005925a624e5c84f96d2c34dce3b6a736addb891724b48a36320c7494435f9c915536dfe621900896c14fb0090" +"000000800000000000000000000000000000000000000000000000000000000000000000000000000000000080020000" +"8362009c07cf48249f481be6b79e67247cab1d20050cf11c276085b90732110c", + +// a000eb5e diff 2 +"00000002e155f07e652e4d671ca4db51bbde14d2b5ae34ee67ecc74400000000000000001e69f1d6507f4b7b50980930f7d8089834fbe65f0980b8592d53cdda08e50d24536dc7da1900896c5eeb00a0" +"000000800000000000000000000000000000000000000000000000000000000000000000000000000000000080020000" +"21e4f654d26ab8c9164ff311657a9f9c4cdc0e8a09334925f7c02138819d7e61", + +// b0002ec5 diff 2 +"000000023bf53ef343a50f7599601f849c93ecce63530b0b449a4463000000000000000064923b63f53c72c04ebe6c1c9140b6377132b6e50865814fe562291bd023d348536dc65a1900896cc52e00b0" +"000000800000000000000000000000000000000000000000000000000000000000000000000000000000000080020000" +"55db91a25401a89daf9ff7d7954bab722b894ba480fefaf1f0a95aaf5f600567", + +// c0001f6e diff 2 +"000000023bf53ef343a50f7599601f849c93ecce63530b0b449a44630000000000000000ee9817160e35d4410601c8dc741c1a810c485f3b40a0859be5f58f0bf6ef1694536dc6321900896c6e1f00c0" +"000000800000000000000000000000000000000000000000000000000000000000000000000000000000000080020000" +"ae215785178ff6350064060ebbb219a71716a10e88528fc4bb1cb5c8fdd0cf60", + +// d0005f26 diff 7 +"000000029ca55e5f1bc0328c84f358fddadc13cb232599bc2ca9dbe100000000000000001e514cf738455a54f004ec86edafcfd9fd2022017bb31c245340353911744fb7536dfe1f1900896c265f00d0" +"000000800000000000000000000000000000000000000000000000000000000000000000000000000000000080020000" +"dcafaa86defe850b057ae74f7218a79b0ede086a196f18f0e7c585eb88d1139a", + +// e0008993 diff 2 +"000000023bf53ef343a50f7599601f849c93ecce63530b0b449a446300000000000000005edbd53fcc64850b5334678199d769514818fbcc79861fc77e572bb4753b7fe2536dc5d91900896c938900e0" +"000000800000000000000000000000000000000000000000000000000000000000000000000000000000000080020000" +"5e653df5956ece518a78a5d11297431af94ce8ba91d80cfb2aa8c5b3095fa256", + +// f000709e diff 1 +"000000023bf53ef343a50f7599601f849c93ecce63530b0b449a44630000000000000000596fc4aa5da839ba267c36aa1a5b29d813747b2273dc03aa9e404c4da0238e2b536dc4cc1900896c9e7000f0" +"000000800000000000000000000000000000000000000000000000000000000000000000000000000000000080020000" +"0e23806a533bd956787eef52dd8edee456c60d6cecbb6175458ee53fc8c6c813" +}; +#endif /* __BENCH_BLOCK_H__ */ diff --git a/bitmain-readme.txt b/bitmain-readme.txt new file mode 100644 index 0000000000..a37dc97fe7 --- /dev/null +++ b/bitmain-readme.txt @@ -0,0 +1,58 @@ +###################################################################################### +# # +# BitMain setup and build instructions (on mingw32/Windows): # +# # +###################################################################################### + +************************************************************************************** +* Build cgminer.exe * +************************************************************************************** +Run the MinGW MSYS shell +(Start Icon/keyboard key ==> All Programs ==> MinGW ==> MinGW Shell). +Change the working directory to your CGMiner project folder. +Example: cd cgminer-2.1.2 [Enter Key] if you are unsure then type "ls -la" +Another way is to type "cd cg" and then press the tab key; It will auto fill. +Type the lines below one at a time. Look for problems after each one before going on +to the next. + + adl.sh (optional - see below) + autoreconf -fvi + CFLAGS="-O2 -msse2" ./configure (additional config options, see below) + make + strip cgminer.exe <== only do this if you are not compiling for debugging + +For bitmain mode: + autoreconf -fvi + CFLAGS="-O2 -msse2" ./configure --enable-bmsc + make + +************************************************************************************** +* Some ./configure options * +************************************************************************************** +--enable-cpumining Build with cpu mining support(default disabled) +--disable-opencl Override detection and disable building with opencl +--disable-adl Override detection and disable building with adl +--enable-bitforce Compile support for BitForce FPGAs(default disabled) +--enable-icarus Compile support for Icarus Board(default disabled) +--enable-bitmain Compile support for BitMain Devices(default disabled) +--enable-modminer Compile support for ModMiner FPGAs(default disabled) +--enable-ztex Compile support for Ztex Board(default disabled) +--enable-scrypt Compile support for scrypt litecoin mining (default disabled) +--without-curses Compile support for curses TUI (default enabled) +--without-libudev Autodetect FPGAs using libudev (default enabled) +--enable-forcecombo Allow combinations of drivers not intended to be built together(default disabled) + +************************************************************************************** +* Run cgminer for bitmain mode * +************************************************************************************** +BitMain options: +--bitmain-options baud:miner_count:asic_count:timeout:frequency + +For example: +cgminer --bitmain-options 115200:24:10:30:300 -o http://stratum.btcguild.com:3333 -u xlc1985_1 -p abc123456 -D + +###################################################################################### +# # +# BitMain setup and build instructions (on mingw32/Windows) complete # +# # +###################################################################################### diff --git a/bitstreams/COPYING_ztex b/bitstreams/COPYING_ztex deleted file mode 100644 index 99cd2ed9fb..0000000000 --- a/bitstreams/COPYING_ztex +++ /dev/null @@ -1,24 +0,0 @@ -All the bitstream files included in this directory that follow the name pattern ztex_*.bit are: - ----- - -Copyright (C) 2009-2011 ZTEX GmbH. -http://www.ztex.de - - -This program is free software; you can redistribute it and/or modify -it under the terms of the GNU General Public License version 3 as -published by the Free Software Foundation. - -This program is distributed in the hope that it will be useful, but -WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU -General Public License for more details. - -You should have received a copy of the GNU General Public License -along with this program; if not, see http://www.gnu.org/licenses/. - ----- - -You can find the original sources at the BTCMiner project home page: http://www.ztex.de/btcminer/ - diff --git a/bitstreams/README b/bitstreams/README new file mode 100644 index 0000000000..a00d01a559 --- /dev/null +++ b/bitstreams/README @@ -0,0 +1 @@ +You must put the file fpgaminer_top_fixed7_197MHz.ncd in here for modminer to work. \ No newline at end of file diff --git a/bitstreams/ztex_ufm1_15b1.bit b/bitstreams/ztex_ufm1_15b1.bit deleted file mode 100644 index 4ed6f4c830..0000000000 Binary files a/bitstreams/ztex_ufm1_15b1.bit and /dev/null differ diff --git a/bitstreams/ztex_ufm1_15d1.bit b/bitstreams/ztex_ufm1_15d1.bit deleted file mode 100644 index b87eaf4f5e..0000000000 Binary files a/bitstreams/ztex_ufm1_15d1.bit and /dev/null differ diff --git a/bitstreams/ztex_ufm1_15d3.bit b/bitstreams/ztex_ufm1_15d3.bit deleted file mode 100644 index 78157a53d5..0000000000 Binary files a/bitstreams/ztex_ufm1_15d3.bit and /dev/null differ diff --git a/bitstreams/ztex_ufm1_15d4.bin b/bitstreams/ztex_ufm1_15d4.bin deleted file mode 100644 index 411c172b19..0000000000 Binary files a/bitstreams/ztex_ufm1_15d4.bin and /dev/null differ diff --git a/bitstreams/ztex_ufm1_15d4.bit b/bitstreams/ztex_ufm1_15d4.bit deleted file mode 100644 index 09519764d1..0000000000 Binary files a/bitstreams/ztex_ufm1_15d4.bit and /dev/null differ diff --git a/bitstreams/ztex_ufm1_15y1.bin b/bitstreams/ztex_ufm1_15y1.bin deleted file mode 100644 index f1532f0608..0000000000 Binary files a/bitstreams/ztex_ufm1_15y1.bin and /dev/null differ diff --git a/bitstreams/ztex_ufm1_15y1.bit b/bitstreams/ztex_ufm1_15y1.bit deleted file mode 100644 index c9b38ae059..0000000000 Binary files a/bitstreams/ztex_ufm1_15y1.bit and /dev/null differ diff --git a/ccan/opt/helpers.d b/ccan/opt/helpers.d deleted file mode 100644 index 669baaff5f..0000000000 --- a/ccan/opt/helpers.d +++ /dev/null @@ -1,24 +0,0 @@ -ccan/opt/helpers.o: ccan/opt/helpers.c ccan/opt/opt.h \ - ccan/compiler/compiler.h config.h ccan/typesafe_cb/typesafe_cb.h \ - /usr/lib/i386-linux-gnu/gcc/i686-linux-gnu/4.5.2/include/stdbool.h \ - /usr/include/stdlib.h /usr/include/features.h \ - /usr/include/bits/predefs.h /usr/include/sys/cdefs.h \ - /usr/include/bits/wordsize.h /usr/include/gnu/stubs.h \ - /usr/include/gnu/stubs-32.h \ - /usr/lib/i386-linux-gnu/gcc/i686-linux-gnu/4.5.2/include/stddef.h \ - /usr/include/bits/waitflags.h /usr/include/bits/waitstatus.h \ - /usr/include/endian.h /usr/include/bits/endian.h \ - /usr/include/bits/byteswap.h /usr/include/xlocale.h \ - /usr/include/sys/types.h /usr/include/bits/types.h \ - /usr/include/bits/typesizes.h /usr/include/time.h \ - /usr/include/sys/select.h /usr/include/bits/select.h \ - /usr/include/bits/sigset.h /usr/include/bits/time.h \ - /usr/include/sys/sysmacros.h /usr/include/bits/pthreadtypes.h \ - /usr/include/alloca.h /usr/include/string.h /usr/include/errno.h \ - /usr/include/bits/errno.h /usr/include/linux/errno.h \ - /usr/include/i386-linux-gnu/asm/errno.h /usr/include/asm-generic/errno.h \ - /usr/include/asm-generic/errno-base.h /usr/include/stdio.h \ - /usr/include/libio.h /usr/include/_G_config.h /usr/include/wchar.h \ - /usr/lib/i386-linux-gnu/gcc/i686-linux-gnu/4.5.2/include/stdarg.h \ - /usr/include/bits/stdio_lim.h /usr/include/bits/sys_errlist.h \ - ccan/opt/private.h diff --git a/ccan/opt/helpers.o b/ccan/opt/helpers.o deleted file mode 100644 index 586212640e..0000000000 Binary files a/ccan/opt/helpers.o and /dev/null differ diff --git a/ccan/opt/opt.c b/ccan/opt/opt.c index 9bc34d92e9..48cd38641a 100644 --- a/ccan/opt/opt.c +++ b/ccan/opt/opt.c @@ -116,7 +116,7 @@ static void check_opt(const struct opt_table *entry) const char *p; unsigned len; - if (entry->type != OPT_HASARG && entry->type != OPT_NOARG) + if (entry->type != OPT_HASARG && entry->type != OPT_NOARG && entry->type != OPT_PROCESSARG) errx(1, "Option %s: unknown entry type %u", entry->names, entry->type); @@ -138,7 +138,7 @@ static void check_opt(const struct opt_table *entry) errx(1, "Option %s: invalid short option" " '%.*s'", entry->names, len+1, p-1); opt_num_short++; - if (entry->type == OPT_HASARG) + if (entry->type == OPT_HASARG || entry->type == OPT_PROCESSARG) opt_num_short_arg++; } /* Don't document args unless there are some. */ diff --git a/ccan/opt/opt.d b/ccan/opt/opt.d deleted file mode 100644 index 7320564365..0000000000 --- a/ccan/opt/opt.d +++ /dev/null @@ -1,26 +0,0 @@ -ccan/opt/opt.o: ccan/opt/opt.c ccan/opt/opt.h ccan/compiler/compiler.h \ - config.h ccan/typesafe_cb/typesafe_cb.h \ - /usr/lib/i386-linux-gnu/gcc/i686-linux-gnu/4.5.2/include/stdbool.h \ - /usr/include/stdlib.h /usr/include/features.h \ - /usr/include/bits/predefs.h /usr/include/sys/cdefs.h \ - /usr/include/bits/wordsize.h /usr/include/gnu/stubs.h \ - /usr/include/gnu/stubs-32.h \ - /usr/lib/i386-linux-gnu/gcc/i686-linux-gnu/4.5.2/include/stddef.h \ - /usr/include/bits/waitflags.h /usr/include/bits/waitstatus.h \ - /usr/include/endian.h /usr/include/bits/endian.h \ - /usr/include/bits/byteswap.h /usr/include/xlocale.h \ - /usr/include/sys/types.h /usr/include/bits/types.h \ - /usr/include/bits/typesizes.h /usr/include/time.h \ - /usr/include/sys/select.h /usr/include/bits/select.h \ - /usr/include/bits/sigset.h /usr/include/bits/time.h \ - /usr/include/sys/sysmacros.h /usr/include/bits/pthreadtypes.h \ - /usr/include/alloca.h /usr/include/string.h /usr/include/errno.h \ - /usr/include/bits/errno.h /usr/include/linux/errno.h \ - /usr/include/i386-linux-gnu/asm/errno.h /usr/include/asm-generic/errno.h \ - /usr/include/asm-generic/errno-base.h /usr/include/stdio.h \ - /usr/include/libio.h /usr/include/_G_config.h /usr/include/wchar.h \ - /usr/lib/i386-linux-gnu/gcc/i686-linux-gnu/4.5.2/include/stdarg.h \ - /usr/include/bits/stdio_lim.h /usr/include/bits/sys_errlist.h \ - /usr/include/err.h /usr/include/assert.h \ - /usr/lib/i386-linux-gnu/gcc/i686-linux-gnu/4.5.2/include/stdint.h \ - /usr/include/stdint.h /usr/include/bits/wchar.h ccan/opt/private.h diff --git a/ccan/opt/opt.h b/ccan/opt/opt.h index 6fe2fb0ec0..90ff680893 100644 --- a/ccan/opt/opt.h +++ b/ccan/opt/opt.h @@ -67,6 +67,13 @@ struct opt_table; #define OPT_WITH_ARG(name, cb, show, arg, desc) \ { (name), OPT_CB_ARG((cb), (show), (arg)), { (arg) }, (desc) } +/** + * OPT_WITH_CBARG() - variant of OPT_WITH_ARG which assigns arguments to arg + * and then performs the callback function on the args as well. + */ +#define OPT_WITH_CBARG(name, cb, show, arg, desc) \ + { (name), OPT_CB_WITHARG((cb), (show), (arg)), { (arg) }, (desc) } + /** * OPT_SUBTABLE() - macro for including another table inside a table. * @table: the table to include in this table. @@ -296,8 +303,9 @@ char *opt_usage_and_exit(const char *extra); enum opt_type { OPT_NOARG = 1, /* -f|--foo */ OPT_HASARG = 2, /* -f arg|--foo=arg|--foo arg */ - OPT_SUBTABLE = 4, /* Actually, longopt points to a subtable... */ - OPT_END = 8, /* End of the table. */ + OPT_PROCESSARG = 4, + OPT_SUBTABLE = 8, /* Actually, longopt points to a subtable... */ + OPT_END = 16, /* End of the table. */ }; struct opt_table { @@ -334,6 +342,16 @@ struct opt_table { typesafe_cb_cast(void (*)(char buf[], const void *), \ void (*)(char buf[], const typeof(*(arg))*), (show)) +#define OPT_CB_WITHARG(cb, show, arg) \ + OPT_PROCESSARG, NULL, \ + typesafe_cb_cast3(char *(*)(const char *,void *), \ + char *(*)(const char *, typeof(*(arg))*), \ + char *(*)(const char *, const typeof(*(arg))*), \ + char *(*)(const char *, const void *), \ + (cb)), \ + typesafe_cb_cast(void (*)(char buf[], const void *), \ + void (*)(char buf[], const typeof(*(arg))*), (show)) + /* Non-typesafe register function. */ void _opt_register(const char *names, enum opt_type type, char *(*cb)(void *arg), diff --git a/ccan/opt/opt.o b/ccan/opt/opt.o deleted file mode 100644 index f57d5914b0..0000000000 Binary files a/ccan/opt/opt.o and /dev/null differ diff --git a/ccan/opt/parse.c b/ccan/opt/parse.c index 7b7c02c593..18af157796 100644 --- a/ccan/opt/parse.c +++ b/ccan/opt/parse.c @@ -107,6 +107,8 @@ int parse_one(int *argc, char *argv[], unsigned *offset, if (!optarg) return parse_err(errlog, argv[0], o, len, "requires an argument"); + if (opt_table[i].type == OPT_PROCESSARG) + opt_set_charp(optarg, opt_table[i].u.arg); problem = opt_table[i].cb_arg(optarg, opt_table[i].u.arg); } diff --git a/ccan/opt/parse.d b/ccan/opt/parse.d deleted file mode 100644 index 016ba9e07d..0000000000 --- a/ccan/opt/parse.d +++ /dev/null @@ -1,18 +0,0 @@ -ccan/opt/parse.o: ccan/opt/parse.c ccan/opt/opt.h \ - ccan/compiler/compiler.h config.h ccan/typesafe_cb/typesafe_cb.h \ - /usr/lib/i386-linux-gnu/gcc/i686-linux-gnu/4.5.2/include/stdbool.h \ - /usr/include/stdlib.h /usr/include/features.h \ - /usr/include/bits/predefs.h /usr/include/sys/cdefs.h \ - /usr/include/bits/wordsize.h /usr/include/gnu/stubs.h \ - /usr/include/gnu/stubs-32.h \ - /usr/lib/i386-linux-gnu/gcc/i686-linux-gnu/4.5.2/include/stddef.h \ - /usr/include/bits/waitflags.h /usr/include/bits/waitstatus.h \ - /usr/include/endian.h /usr/include/bits/endian.h \ - /usr/include/bits/byteswap.h /usr/include/xlocale.h \ - /usr/include/sys/types.h /usr/include/bits/types.h \ - /usr/include/bits/typesizes.h /usr/include/time.h \ - /usr/include/sys/select.h /usr/include/bits/select.h \ - /usr/include/bits/sigset.h /usr/include/bits/time.h \ - /usr/include/sys/sysmacros.h /usr/include/bits/pthreadtypes.h \ - /usr/include/alloca.h /usr/include/string.h /usr/include/assert.h \ - ccan/opt/private.h diff --git a/ccan/opt/parse.o b/ccan/opt/parse.o deleted file mode 100644 index ef7ac29dad..0000000000 Binary files a/ccan/opt/parse.o and /dev/null differ diff --git a/ccan/opt/usage.d b/ccan/opt/usage.d deleted file mode 100644 index 0cacbfd171..0000000000 --- a/ccan/opt/usage.d +++ /dev/null @@ -1,22 +0,0 @@ -ccan/opt/usage.o: ccan/opt/usage.c ccan/opt/opt.h \ - ccan/compiler/compiler.h config.h ccan/typesafe_cb/typesafe_cb.h \ - /usr/lib/i386-linux-gnu/gcc/i686-linux-gnu/4.5.2/include/stdbool.h \ - /usr/include/stdlib.h /usr/include/features.h \ - /usr/include/bits/predefs.h /usr/include/sys/cdefs.h \ - /usr/include/bits/wordsize.h /usr/include/gnu/stubs.h \ - /usr/include/gnu/stubs-32.h \ - /usr/lib/i386-linux-gnu/gcc/i686-linux-gnu/4.5.2/include/stddef.h \ - /usr/include/bits/waitflags.h /usr/include/bits/waitstatus.h \ - /usr/include/endian.h /usr/include/bits/endian.h \ - /usr/include/bits/byteswap.h /usr/include/xlocale.h \ - /usr/include/sys/types.h /usr/include/bits/types.h \ - /usr/include/bits/typesizes.h /usr/include/time.h \ - /usr/include/sys/select.h /usr/include/bits/select.h \ - /usr/include/bits/sigset.h /usr/include/bits/time.h \ - /usr/include/sys/sysmacros.h /usr/include/bits/pthreadtypes.h \ - /usr/include/alloca.h /usr/include/string.h /usr/include/stdio.h \ - /usr/include/libio.h /usr/include/_G_config.h /usr/include/wchar.h \ - /usr/lib/i386-linux-gnu/gcc/i686-linux-gnu/4.5.2/include/stdarg.h \ - /usr/include/bits/stdio_lim.h /usr/include/bits/sys_errlist.h \ - /usr/lib/i386-linux-gnu/gcc/i686-linux-gnu/4.5.2/include/stdint.h \ - /usr/include/stdint.h /usr/include/bits/wchar.h ccan/opt/private.h diff --git a/ccan/opt/usage.o b/ccan/opt/usage.o deleted file mode 100644 index 1e72da3558..0000000000 Binary files a/ccan/opt/usage.o and /dev/null differ diff --git a/cgminer.c b/cgminer.c index 3b9e3ba005..d1aca0f51b 100644 --- a/cgminer.c +++ b/cgminer.c @@ -1,5 +1,5 @@ /* - * Copyright 2011-2013 Con Kolivas + * Copyright 2011-2014 Con Kolivas * Copyright 2011-2012 Luke Dashjr * Copyright 2010 Jeff Garzik * @@ -27,6 +27,7 @@ #include #include #include +#include #ifdef USE_USBUTILS #include @@ -42,35 +43,72 @@ #endif #include #include +#ifdef HAVE_LIBCURL #include +#else +char *curly = ":D"; +#endif #include #include #include "compat.h" #include "miner.h" -#include "findnonce.h" -#include "adl.h" -#include "driver-opencl.h" #include "bench_block.h" -#include "scrypt.h" +#ifdef USE_USBUTILS +#include "usbutils.h" +#endif + +#if defined(unix) || defined(__APPLE__) + #include + #include + #include +#endif #ifdef USE_AVALON #include "driver-avalon.h" #endif +#ifdef USE_AVALON2 +#include "driver-avalon2.h" +#endif + +#ifdef USE_AVALON4 +#include "driver-avalon4.h" +#endif + #ifdef USE_BFLSC #include "driver-bflsc.h" #endif -#if defined(unix) || defined(__APPLE__) - #include - #include - #include +#ifdef USE_SP10 +#include "driver-spondoolies-sp10.h" #endif -#if defined(USE_BITFORCE) || defined(USE_ICARUS) || defined(USE_AVALON) || defined(USE_MODMINER) -# define USE_FPGA -#elif defined(USE_ZTEX) +#ifdef USE_SP30 +#include "driver-spondoolies-sp30.h" +#endif + +#ifdef USE_BLOCK_ERUPTER +#include "driver-blockerupter.h" +#endif + +#ifdef USE_BITFURY +#include "driver-bitfury.h" +#endif + +#ifdef USE_COINTERRA +#include "driver-cointerra.h" +#endif + +#ifdef USE_HASHFAST +#include "driver-hashfast.h" +#endif + +#ifdef USE_BITMAIN +#include "driver-bitmain.h" +#endif + +#if defined(USE_BITFORCE) || defined(USE_ICARUS) || defined(USE_AVALON) || defined(USE_AVALON2) || defined(USE_BMSC) || defined (USE_BITMAIN) || defined(USE_MODMINER) # define USE_FPGA #endif @@ -84,7 +122,41 @@ struct strategies strategies[] = { static char packagename[256]; +FILE * g_logwork_file = NULL; +FILE * g_logwork_files[65] = {0}; +FILE * g_logwork_diffs[65] = {0}; +int g_logwork_asicnum = 0; + +bool opt_work_update; bool opt_protocol; +static struct benchfile_layout { + int length; + char *name; +} benchfile_data[] = { + { 1, "Version" }, + { 64, "MerkleRoot" }, + { 64, "PrevHash" }, + { 8, "DifficultyBits" }, + { 10, "NonceTime" } // 10 digits +}; +enum benchwork { + BENCHWORK_VERSION = 0, + BENCHWORK_MERKLEROOT, + BENCHWORK_PREVHASH, + BENCHWORK_DIFFBITS, + BENCHWORK_NONCETIME, + BENCHWORK_COUNT +}; + +#ifdef HAVE_LIBCURL +static char *opt_btc_address; +static char *opt_btc_sig; +#endif +static char *opt_benchfile; +static bool opt_benchfile_display; +static FILE *benchfile_in; +static int benchfile_line; +static int benchfile_work; static bool opt_benchmark; bool have_longpoll; bool want_per_device_stats; @@ -96,35 +168,26 @@ bool opt_compact; const int opt_cutofftemp = 95; int opt_log_interval = 5; int opt_queue = 1; +static int max_queue = 1; int opt_scantime = -1; int opt_expiry = 120; static const bool opt_time = true; unsigned long long global_hashrate; +unsigned long global_quota_gcd = 1; +time_t last_getwork; -#if defined(HAVE_OPENCL) || defined(USE_USBUTILS) +#if defined(USE_USBUTILS) int nDevs; #endif -#ifdef HAVE_OPENCL -int opt_dynamic_interval = 7; -int opt_g_threads = -1; -int gpu_threads; -#ifdef USE_SCRYPT -bool opt_scrypt; -#endif -#endif bool opt_restart = true; -static bool opt_nogpu; +bool opt_nogpu; struct list_head scan_devices; -static bool devices_enabled[MAX_DEVICES]; -static int opt_devs_enabled; static bool opt_display_devs; -static bool opt_removedisabled; int total_devices; int zombie_devs; static int most_devices; struct cgpu_info **devices; -bool have_opencl; int mining_threads; int num_processors; #ifdef HAVE_CURSES @@ -132,18 +195,28 @@ bool use_curses = true; #else bool use_curses; #endif +static bool opt_widescreen; +static bool alt_status; +static bool switch_status; static bool opt_submit_stale = true; static int opt_shares; bool opt_fail_only; static bool opt_fix_protocol; -static bool opt_lowmem; +bool opt_lowmem; bool opt_autofan; bool opt_autoengine; bool opt_noadl; +char *opt_version_path = NULL; +char *opt_logfile_path = NULL; +char *opt_logfile_openflag = NULL; +char *opt_logwork_path = NULL; +char *opt_logwork_asicnum = NULL; +bool opt_logwork_diff = false; char *opt_api_allow = NULL; char *opt_api_groups; char *opt_api_description = PACKAGE_STRING; int opt_api_port = 4028; +char *opt_api_host = API_LISTEN_ADDR; bool opt_api_listen; bool opt_api_mcast; char *opt_api_mcast_addr = API_MCAST_ADDR; @@ -154,17 +227,94 @@ bool opt_api_network; bool opt_delaynet; bool opt_disable_pool; static bool no_work; +#ifdef USE_ICARUS char *opt_icarus_options = NULL; char *opt_icarus_timing = NULL; +float opt_anu_freq = 250; +float opt_au3_freq = 225; +int opt_au3_volt = 750; +float opt_rock_freq = 270; +#endif bool opt_worktime; #ifdef USE_AVALON -char *opt_avalon_options = NULL; +char *opt_avalon_options; +char *opt_bitburner_fury_options; +static char *opt_set_avalon_fan; +static char *opt_set_avalon_freq; +#endif +#ifdef USE_AVALON2 +static char *opt_set_avalon2_freq; +static char *opt_set_avalon2_fan; +static char *opt_set_avalon2_voltage; +#endif +#ifdef USE_AVALON4 +static char *opt_set_avalon4_fan; +static char *opt_set_avalon4_voltage; +static char *opt_set_avalon4_freq; +#endif +#ifdef USE_BLOCKERUPTER +int opt_bet_clk = 0; +#endif +#ifdef USE_HASHRATIO +#include "driver-hashratio.h" +#endif +#ifdef USE_KLONDIKE +char *opt_klondike_options = NULL; +#endif +#ifdef USE_DRILLBIT +char *opt_drillbit_options = NULL; +char *opt_drillbit_auto = NULL; +#endif +char *opt_bab_options = NULL; +#ifdef USE_BITMINE_A1 +char *opt_bitmine_a1_options = NULL; +#endif +#ifdef USE_BMSC +char *opt_bmsc_options = NULL; +char *opt_bmsc_bandops = NULL; +char *opt_bmsc_timing = NULL; +bool opt_bmsc_gray = false; +char *opt_bmsc_freq = NULL; +char *opt_bmsc_rdreg = NULL; +char *opt_bmsc_voltage = NULL; +bool opt_bmsc_bootstart = false; +bool opt_bmsc_rdworktest = false; +#endif +#ifdef USE_BITMAIN +char *opt_bitmain_options = NULL; +char *opt_bitmain_freq = NULL; +char *opt_bitmain_voltage = NULL; +#endif +#ifdef USE_HASHFAST +static char *opt_set_hfa_fan; +#endif +static char *opt_set_null; +#ifdef USE_MINION +int opt_minion_chipreport; +char *opt_minion_cores; +bool opt_minion_extra; +char *opt_minion_freq; +int opt_minion_freqchange = 1000; +int opt_minion_freqpercent = 70; +bool opt_minion_idlecount; +int opt_minion_ledcount; +int opt_minion_ledlimit = 98; +bool opt_minion_noautofreq; +bool opt_minion_overheat; +int opt_minion_spidelay; +char *opt_minion_spireset; +int opt_minion_spisleep = 200; +int opt_minion_spiusec; +char *opt_minion_temp; #endif + #ifdef USE_USBUTILS char *opt_usb_select = NULL; int opt_usbdump = -1; bool opt_usb_list_all; cgsem_t usb_resource_sem; +static pthread_t usb_poll_thread; +static bool usb_polling; #endif char *opt_kernel_path; @@ -178,7 +328,6 @@ bool opt_bfl_noncerange; struct thr_info *control_thr; struct thr_info **mining_thr; static int gwsched_thr_id; -static int stage_thr_id; static int watchpool_thr_id; static int watchdog_thr_id; #ifdef HAVE_CURSES @@ -196,10 +345,8 @@ static int new_devices; static int new_threads; int hotplug_time = 5; -#ifdef USE_USBUTILS -pthread_mutex_t cgusb_lock; -pthread_mutex_t cgusbres_lock; -cglock_t cgusb_fd_lock; +#if LOCK_TRACKING +pthread_mutex_t lockstat_lock; #endif pthread_mutex_t hash_lock; @@ -221,22 +368,36 @@ pthread_cond_t restart_cond; pthread_cond_t gws_cond; +#define CG_LOCAL_MHASHES_MAX_NUM 12 +double g_local_mhashes_dones[CG_LOCAL_MHASHES_MAX_NUM] = {0}; +int g_local_mhashes_index = 0; +double g_displayed_rolling = 0; +char g_miner_version[256] = {0}; +char g_miner_compiletime[256] = {0}; +char g_miner_type[256] = {0}; + +double rolling1, rolling5, rolling15; +double total_rolling; double total_mhashes_done; static struct timeval total_tv_start, total_tv_end; +static struct timeval restart_tv_start, update_tv_start; cglock_t control_lock; pthread_mutex_t stats_lock; int hw_errors; -int total_accepted, total_rejected, total_diff1; -int total_getworks, total_stale, total_discarded; +int g_max_fan, g_max_temp; +int64_t total_accepted, total_rejected, total_diff1; +int64_t total_getworks, total_stale, total_discarded; double total_diff_accepted, total_diff_rejected, total_diff_stale; static int staged_rollable; unsigned int new_blocks; -static unsigned int work_block; +static unsigned int work_block = 0; unsigned int found_blocks; unsigned int local_work; +unsigned int local_work_last = 0; +long local_work_lasttime = 0; unsigned int total_go, total_ro; struct pool **pools; @@ -253,11 +414,10 @@ const #endif bool curses_active; -static char current_block[40]; - /* Protected by ch_lock */ -static char *current_hash; -char *current_fullhash; +char current_hash[68]; +static char prev_block[12]; +static char current_block[32]; static char datestamp[40]; static char blocktime[32]; @@ -268,7 +428,7 @@ static char block_diff[8]; uint64_t best_diff = 0; struct block { - char hash[40]; + char hash[68]; UT_hash_handle hh; int block_no; }; @@ -286,12 +446,13 @@ struct stratum_share { struct work *work; int id; time_t sshare_time; + time_t sshare_sent; }; static struct stratum_share *stratum_shares = NULL; char *opt_socks_proxy = NULL; - +int opt_suggest_diff; static const char def_conf[] = "cgminer.conf"; static char *default_config; static bool config_loaded; @@ -301,6 +462,7 @@ static int include_count; #define JSON_LOAD_ERROR_LEN strlen(JSON_LOAD_ERROR) #define JSON_MAX_DEPTH 10 #define JSON_MAX_DEPTH_ERR "Too many levels of JSON includes (limit 10) or a loop" +#define JSON_WEB_ERROR "WEB config err" #if defined(unix) || defined(__APPLE__) static char *opt_stderr_cmd = NULL; @@ -311,7 +473,7 @@ struct sigaction termhandler, inthandler; struct thread_q *getq; -static int total_work; +static uint32_t total_work; struct work *staged_work = NULL; struct schedtime { @@ -406,19 +568,24 @@ static void applog_and_exit(const char *fmt, ...) va_start(ap, fmt); vsnprintf(exit_buf, sizeof(exit_buf), fmt, ap); va_end(ap); - _applog(LOG_ERR, exit_buf); + _applog(LOG_ERR, exit_buf, true); exit(1); } static pthread_mutex_t sharelog_lock; static FILE *sharelog_file = NULL; +static struct thr_info *__get_thread(int thr_id) +{ + return mining_thr[thr_id]; +} + struct thr_info *get_thread(int thr_id) { struct thr_info *thr; rd_lock(&mining_thr_lock); - thr = mining_thr[thr_id]; + thr = __get_thread(thr_id); rd_unlock(&mining_thr_lock); return thr; @@ -488,6 +655,49 @@ static char *getwork_req = "{\"method\": \"getwork\", \"params\": [], \"id\":0}\ static char *gbt_req = "{\"id\": 0, \"method\": \"getblocktemplate\", \"params\": [{\"capabilities\": [\"coinbasetxn\", \"workid\", \"coinbase/append\"]}]}\n"; +static char *gbt_solo_req = "{\"id\": 0, \"method\": \"getblocktemplate\"}\n"; + +/* Adjust all the pools' quota to the greatest common denominator after a pool + * has been added or the quotas changed. */ +void adjust_quota_gcd(void) +{ + unsigned long gcd, lowest_quota = ~0UL, quota; + struct pool *pool; + int i; + + for (i = 0; i < total_pools; i++) { + pool = pools[i]; + quota = pool->quota; + if (!quota) + continue; + if (quota < lowest_quota) + lowest_quota = quota; + } + + if (likely(lowest_quota < ~0UL)) { + gcd = lowest_quota; + for (i = 0; i < total_pools; i++) { + pool = pools[i]; + quota = pool->quota; + if (!quota) + continue; + while (quota % gcd) + gcd--; + } + } else + gcd = 1; + + for (i = 0; i < total_pools; i++) { + pool = pools[i]; + pool->quota_used *= global_quota_gcd; + pool->quota_used /= gcd; + pool->quota_gcd = pool->quota / gcd; + } + + global_quota_gcd = gcd; + applog(LOG_DEBUG, "Global quota greatest common denominator set to %lu", gcd); +} + /* Return value is ignored if not called from add_pool_details */ struct pool *add_pool(void) { @@ -512,6 +722,8 @@ struct pool *add_pool(void) pool->rpc_req = getwork_req; pool->rpc_proxy = NULL; + pool->quota = 1; + adjust_quota_gcd(); return pool; } @@ -580,26 +792,48 @@ static char *set_int_0_to_10(const char *arg, int *i) return set_int_range(arg, i, 0, 10); } -#ifdef USE_AVALON static char *set_int_0_to_100(const char *arg, int *i) { return set_int_range(arg, i, 0, 100); } -#endif -#ifdef USE_BFLSC +static char *set_int_0_to_255(const char *arg, int *i) +{ + return set_int_range(arg, i, 0, 255); +} + static char *set_int_0_to_200(const char *arg, int *i) { return set_int_range(arg, i, 0, 200); } -#endif + +static char *set_int_32_to_63(const char *arg, int *i) +{ + return set_int_range(arg, i, 32, 63); +} + +static char *set_int_22_to_55(const char *arg, int *i) +{ + return set_int_range(arg, i, 22, 55); +} + +static char *set_int_42_to_65(const char *arg, int *i) +{ + return set_int_range(arg, i, 42, 62); +} static char *set_int_1_to_10(const char *arg, int *i) { return set_int_range(arg, i, 1, 10); } +static char __maybe_unused *set_int_0_to_4(const char *arg, int *i) +{ + return set_int_range(arg, i, 0, 4); +} + #ifdef USE_FPGA_SERIAL +static char *opt_add_serial; static char *add_serial(char *arg) { string_elist_add(arg, &scan_devices); @@ -613,49 +847,6 @@ void get_intrange(char *arg, int *val1, int *val2) *val2 = *val1; } -static char *set_devices(char *arg) -{ - int i, val1 = 0, val2 = 0; - char *nextptr; - - if (*arg) { - if (*arg == '?') { - opt_display_devs = true; - return NULL; - } - } else - return "Invalid device parameters"; - - nextptr = strtok(arg, ","); - if (nextptr == NULL) - return "Invalid parameters for set devices"; - get_intrange(nextptr, &val1, &val2); - if (val1 < 0 || val1 > MAX_DEVICES || val2 < 0 || val2 > MAX_DEVICES || - val1 > val2) { - return "Invalid value passed to set devices"; - } - - for (i = val1; i <= val2; i++) { - devices_enabled[i] = true; - opt_devs_enabled++; - } - - while ((nextptr = strtok(NULL, ",")) != NULL) { - get_intrange(nextptr, &val1, &val2); - if (val1 < 0 || val1 > MAX_DEVICES || val2 < 0 || val2 > MAX_DEVICES || - val1 > val2) { - return "Invalid value passed to set devices"; - } - - for (i = val1; i <= val2; i++) { - devices_enabled[i] = true; - opt_devs_enabled++; - } - } - - return NULL; -} - static char *set_balance(enum pool_strategy *strategy) { *strategy = POOL_BALANCE; @@ -668,10 +859,10 @@ static char *set_loadbalance(enum pool_strategy *strategy) return NULL; } -static char *set_rotate(const char *arg, int *i) +static char *set_rotate(const char *arg, char __maybe_unused *i) { pool_strategy = POOL_ROTATE; - return set_int_range(arg, i, 0, 9999); + return set_int_range(arg, &opt_rotate_period, 0, 9999); } static char *set_rr(enum pool_strategy *strategy) @@ -684,7 +875,8 @@ static char *set_rr(enum pool_strategy *strategy) * stratum+tcp or by detecting a stratum server response */ bool detect_stratum(struct pool *pool, char *url) { - if (!extract_sockaddr(pool, url)) + check_extranonce_option(pool, url); + if (!extract_sockaddr(url, &pool->sockaddr_url, &pool->stratum_port)) return false; if (!strncasecmp(url, "stratum+tcp://", 14)) { @@ -697,32 +889,68 @@ bool detect_stratum(struct pool *pool, char *url) return false; } -static char *set_url(char *arg) +static struct pool *add_url(void) { - struct pool *pool; - total_urls++; if (total_urls > total_pools) add_pool(); - pool = pools[total_urls - 1]; + return pools[total_urls - 1]; +} +static void setup_url(struct pool *pool, char *arg) +{ arg = get_proxy(arg, pool); if (detect_stratum(pool, arg)) - return NULL; + return; opt_set_charp(arg, &pool->rpc_url); if (strncmp(arg, "http://", 7) && strncmp(arg, "https://", 8)) { char *httpinput; - httpinput = malloc(255); + httpinput = malloc(256); if (!httpinput) quit(1, "Failed to malloc httpinput"); - strcpy(httpinput, "http://"); - strncat(httpinput, arg, 248); - pool->rpc_url = httpinput; + strcpy(httpinput, "stratum+tcp://"); + strncat(httpinput, arg, 242); + detect_stratum(pool, httpinput); } +} + +static char *set_url(char *arg) +{ + struct pool *pool = add_url(); + + setup_url(pool, arg); + return NULL; +} + +static char *set_quota(char *arg) +{ + char *semicolon = strchr(arg, ';'), *url; + int len, qlen, quota; + struct pool *pool; + + if (!semicolon) + return "No semicolon separated quota;URL pair found"; + len = strlen(arg); + *semicolon = '\0'; + qlen = strlen(arg); + if (!qlen) + return "No parameter for quota found"; + len -= qlen + 1; + if (len < 1) + return "No parameter for URL found"; + quota = atoi(arg); + if (quota < 0) + return "Invalid negative parameter for quota set"; + url = arg + qlen + 1; + pool = add_url(); + setup_url(pool, url); + pool->quota = quota; + applog(LOG_INFO, "Setting pool %d to quota %d", pool->pool_no, pool->quota); + adjust_quota_gcd(); return NULL; } @@ -778,7 +1006,7 @@ static char *set_userpass(const char *arg) return "Failed to find : delimited user info"; pool->rpc_pass = strtok(NULL, ":"); if (!pool->rpc_pass) - return "Failed to find : delimited pass info"; + pool->rpc_pass = strdup(""); return NULL; } @@ -791,6 +1019,9 @@ static char *enable_debug(bool *flag) return NULL; } +static char *opt_set_sched_start; +static char *opt_set_sched_stop; + static char *set_schedtime(const char *arg, struct schedtime *st) { if (sscanf(arg, "%d:%d", &st->tm.tm_hour, &st->tm.tm_min) != 2) @@ -801,6 +1032,17 @@ static char *set_schedtime(const char *arg, struct schedtime *st) return NULL; } +static char *set_sched_start(const char *arg) +{ + return set_schedtime(arg, &schedstart); +} + +static char *set_sched_stop(const char *arg) +{ + return set_schedtime(arg, &schedstop); +} + +static char *opt_set_sharelog; static char* set_sharelog(char *arg) { char *r = ""; @@ -824,6 +1066,7 @@ static char* set_sharelog(char *arg) } static char *temp_cutoff_str = NULL; +static char __maybe_unused *opt_set_temp_cutoff; char *set_temp_cutoff(char *arg) { @@ -874,80 +1117,130 @@ static void load_temp_cutoffs() } } -static char *set_api_allow(const char *arg) +static char *set_logfile_path(const char *arg) +{ + opt_set_charp(arg, &opt_logfile_path); + + return NULL; +} + +static char *set_logfile_openflag(const char *arg) { - opt_set_charp(arg, &opt_api_allow); + opt_set_charp(arg, &opt_logfile_openflag); return NULL; } -static char *set_api_groups(const char *arg) +static char *set_logwork_path(const char *arg) { - opt_set_charp(arg, &opt_api_groups); + opt_set_charp(arg, &opt_logwork_path); return NULL; } -static char *set_api_description(const char *arg) +static char *set_logwork_asicnum(const char *arg) { - opt_set_charp(arg, &opt_api_description); + opt_set_charp(arg, &opt_logwork_asicnum); return NULL; } -static char *set_api_mcast_addr(const char *arg) +static char *set_float_125_to_500(const char *arg, float *i) { - opt_set_charp(arg, &opt_api_mcast_addr); + char *err = opt_set_floatval(arg, i); + + if (err) + return err; + + if (*i < 125 || *i > 500) + return "Value out of range"; return NULL; } -static char *set_api_mcast_code(const char *arg) +static char *set_float_100_to_250(const char *arg, float *i) +{ + char *err = opt_set_floatval(arg, i); + + if (err) + return err; + + if (*i < 100 || *i > 250) + return "Value out of range"; + + return NULL; +} +static char *set_version_path(const char *arg) { - opt_set_charp(arg, &opt_api_mcast_code); + opt_set_charp(arg, &opt_version_path); return NULL; } -static char *set_api_mcast_des(const char *arg) +#ifdef USE_BMSC +static char *set_bmsc_options(const char *arg) { - opt_set_charp(arg, &opt_api_mcast_des); + opt_set_charp(arg, &opt_bmsc_options); return NULL; } -#ifdef USE_ICARUS -static char *set_icarus_options(const char *arg) +static char *set_bmsc_bandops(const char *arg) { - opt_set_charp(arg, &opt_icarus_options); + opt_set_charp(arg, &opt_bmsc_bandops); return NULL; } -static char *set_icarus_timing(const char *arg) +static char *set_bmsc_timing(const char *arg) { - opt_set_charp(arg, &opt_icarus_timing); + opt_set_charp(arg, &opt_bmsc_timing); return NULL; } -#endif -#ifdef USE_AVALON -static char *set_avalon_options(const char *arg) +static char *set_bmsc_freq(const char *arg) +{ + opt_set_charp(arg, &opt_bmsc_freq); + + return NULL; +} + +static char *set_bmsc_rdreg(const char *arg) +{ + opt_set_charp(arg, &opt_bmsc_rdreg); + + return NULL; +} + +static char *set_bmsc_voltage(const char *arg) { - opt_set_charp(arg, &opt_avalon_options); + opt_set_charp(arg, &opt_bmsc_voltage); return NULL; } #endif -#ifdef USE_USBUTILS -static char *set_usb_select(const char *arg) +#ifdef USE_BITMAIN +static char *set_bitmain_options(const char *arg) +{ + opt_set_charp(arg, &opt_bitmain_options); + + return NULL; +} +static char *set_bitmain_freq(const char *arg) +{ + opt_set_charp(arg, &opt_bitmain_freq); + + return NULL; +} +static char *set_bitmain_voltage(const char *arg) { - opt_set_charp(arg, &opt_usb_select); + opt_set_charp(arg, &opt_bitmain_voltage); return NULL; } + #endif static char *set_null(const char __maybe_unused *arg) @@ -957,14 +1250,38 @@ static char *set_null(const char __maybe_unused *arg) /* These options are available from config file or commandline */ static struct opt_table opt_config_table[] = { +#ifdef USE_ICARUS + OPT_WITH_ARG("--anu-freq", + set_float_125_to_500, &opt_show_floatval, &opt_anu_freq, + "Set AntminerU1/2 frequency in MHz, range 125-500"), +#endif + + OPT_WITH_ARG("--version-file", + set_version_path, NULL, opt_hidden, + "Set miner version file"), + OPT_WITH_ARG("--logfile-openflag", + set_logfile_openflag, NULL, opt_hidden, + "Set log file open flag, default: a+"), + OPT_WITH_ARG("--logwork", + set_logwork_path, NULL, opt_hidden, + "Set log work file path, following: minertext"), + OPT_WITH_ARG("--logwork-asicnum", + set_logwork_asicnum, NULL, opt_hidden, + "Set log work asic num, following: 1, 32, 64"), + OPT_WITHOUT_ARG("--logwork-diff", + opt_set_bool, &opt_logwork_diff, + "Allow log work diff"), + OPT_WITH_ARG("--logfile", + set_logfile_path, NULL, opt_hidden, + "Set log file, default: cgminer.log"), OPT_WITH_ARG("--api-allow", - set_api_allow, NULL, NULL, + opt_set_charp, NULL, &opt_api_allow, "Allow API access only to the given list of [G:]IP[/Prefix] addresses[/subnets]"), OPT_WITH_ARG("--api-description", - set_api_description, NULL, NULL, + opt_set_charp, NULL, &opt_api_description, "Description placed in the API status header, default: cgminer version"), OPT_WITH_ARG("--api-groups", - set_api_groups, NULL, NULL, + opt_set_charp, NULL, &opt_api_groups, "API one letter groups G:cmd:cmd[,P:cmd:*...] defining the cmds a groups can use"), OPT_WITHOUT_ARG("--api-listen", opt_set_bool, &opt_api_listen, @@ -973,13 +1290,13 @@ static struct opt_table opt_config_table[] = { opt_set_bool, &opt_api_mcast, "Enable API Multicast listener, default: disabled"), OPT_WITH_ARG("--api-mcast-addr", - set_api_mcast_addr, NULL, NULL, + opt_set_charp, NULL, &opt_api_mcast_addr, "API Multicast listen address"), OPT_WITH_ARG("--api-mcast-code", - set_api_mcast_code, NULL, NULL, + opt_set_charp, NULL, &opt_api_mcast_code, "Code expected in the API Multicast message, don't use '-'"), OPT_WITH_ARG("--api-mcast-des", - set_api_mcast_des, NULL, NULL, + opt_set_charp, NULL, &opt_api_mcast_des, "Description appended to the API Multicast reply, default: ''"), OPT_WITH_ARG("--api-mcast-port", set_int_1_to_65535, opt_show_intval, &opt_api_mcast_port, @@ -990,17 +1307,103 @@ static struct opt_table opt_config_table[] = { OPT_WITH_ARG("--api-port", set_int_1_to_65535, opt_show_intval, &opt_api_port, "Port number of miner API"), -#ifdef HAVE_ADL - OPT_WITHOUT_ARG("--auto-fan", - opt_set_bool, &opt_autofan, - "Automatically adjust all GPU fan speeds to maintain a target temperature"), - OPT_WITHOUT_ARG("--auto-gpu", - opt_set_bool, &opt_autoengine, - "Automatically adjust all GPU engine clock speeds to maintain a target temperature"), + OPT_WITH_ARG("--api-host", + opt_set_charp, NULL, &opt_api_host, + "Specify API listen address, default: 0.0.0.0"), +#ifdef USE_ICARUS + OPT_WITH_ARG("--au3-freq", + set_float_100_to_250, &opt_show_floatval, &opt_au3_freq, + "Set AntminerU3 frequency in MHz, range 100-250"), + OPT_WITH_ARG("--au3-volt", + set_int_0_to_9999, &opt_show_intval, &opt_au3_volt, + "Set AntminerU3 voltage in mv, range 725-850, 0 to not set"), +#endif +#ifdef USE_AVALON + OPT_WITHOUT_ARG("--avalon-auto", + opt_set_bool, &opt_avalon_auto, + "Adjust avalon overclock frequency dynamically for best hashrate"), + OPT_WITH_ARG("--avalon-cutoff", + set_int_0_to_100, opt_show_intval, &opt_avalon_overheat, + "Set avalon overheat cut off temperature"), + OPT_WITH_CBARG("--avalon-fan", + set_avalon_fan, NULL, &opt_set_avalon_fan, + "Set fanspeed percentage for avalon, single value or range (default: 20-100)"), + OPT_WITH_CBARG("--avalon-freq", + set_avalon_freq, NULL, &opt_set_avalon_freq, + "Set frequency range for avalon-auto, single value or range"), + OPT_WITH_ARG("--avalon-options", + opt_set_charp, NULL, &opt_avalon_options, + "Set avalon options baud:miners:asic:timeout:freq:tech"), + OPT_WITH_ARG("--avalon-temp", + set_int_0_to_100, opt_show_intval, &opt_avalon_temp, + "Set avalon target temperature"), +#endif +#ifdef USE_AVALON2 + OPT_WITH_CBARG("--avalon2-freq", + set_avalon2_freq, NULL, &opt_set_avalon2_freq, + "Set frequency range for Avalon2, single value or range, step: 25"), + OPT_WITH_CBARG("--avalon2-voltage", + set_avalon2_voltage, NULL, &opt_set_avalon2_voltage, + "Set Avalon2 core voltage, in millivolts, step: 125"), + OPT_WITH_CBARG("--avalon2-fan", + set_avalon2_fan, NULL, &opt_set_avalon2_fan, + "Set Avalon2 target fan speed"), + OPT_WITH_ARG("--avalon2-cutoff", + set_int_0_to_100, opt_show_intval, &opt_avalon2_overheat, + "Set Avalon2 overheat cut off temperature"), + OPT_WITHOUT_ARG("--avalon2-fixed-speed", + set_avalon2_fixed_speed, &opt_avalon2_fan_fixed, + "Set Avalon2 fan to fixed speed"), + OPT_WITH_ARG("--avalon2-polling-delay", + set_int_1_to_65535, opt_show_intval, &opt_avalon2_polling_delay, + "Set Avalon2 polling delay value (ms)"), +#endif +#ifdef USE_AVALON4 + OPT_WITHOUT_ARG("--avalon4-automatic-voltage", + opt_set_bool, &opt_avalon4_autov, + "Automatic adjust voltage base on module DH"), + OPT_WITH_CBARG("--avalon4-voltage", + set_avalon4_voltage, NULL, &opt_set_avalon4_voltage, + "Set Avalon4 core voltage, in millivolts, step: 125"), + OPT_WITH_CBARG("--avalon4-freq", + set_avalon4_freq, NULL, &opt_set_avalon4_freq, + "Set frequency for Avalon4, 1 to 3 values, example: 445:385:370"), + OPT_WITH_CBARG("--avalon4-fan", + set_avalon4_fan, NULL, &opt_set_avalon4_fan, + "Set Avalon4 target fan speed range"), + OPT_WITH_ARG("--avalon4-temp", + set_int_22_to_55, opt_show_intval, &opt_avalon4_temp_target, + "Set Avalon4 target temperature"), + OPT_WITH_ARG("--avalon4-cutoff", + set_int_42_to_65, opt_show_intval, &opt_avalon4_overheat, + "Set Avalon4 overheat cut off temperature"), + OPT_WITH_ARG("--avalon4-polling-delay", + set_int_1_to_65535, opt_show_intval, &opt_avalon4_polling_delay, + "Set Avalon4 polling delay value (ms)"), + OPT_WITH_ARG("--avalon4-ntime-offset", + opt_set_intval, opt_show_intval, &opt_avalon4_ntime_offset, + "Set Avalon4 MM ntime rolling max offset"), + OPT_WITH_ARG("--avalon4-aucspeed", + opt_set_intval, opt_show_intval, &opt_avalon4_aucspeed, + "Set Avalon4 AUC IIC bus speed"), + OPT_WITH_ARG("--avalon4-aucxdelay", + opt_set_intval, opt_show_intval, &opt_avalon4_aucxdelay, + "Set Avalon4 AUC IIC xfer read delay, 4800 ~= 1ms"), +#endif +#ifdef USE_BAB + OPT_WITH_ARG("--bab-options", + opt_set_charp, NULL, &opt_bab_options, + "Set bab options max:def:min:up:down:hz:delay:trf"), #endif OPT_WITHOUT_ARG("--balance", set_balance, &pool_strategy, "Change multipool strategy from failover to even share balance"), + OPT_WITH_ARG("--benchfile", + opt_set_charp, NULL, &opt_benchfile, + "Run cgminer in benchmark mode using a work file - produces no shares"), + OPT_WITHOUT_ARG("--benchfile-display", + opt_set_bool, &opt_benchfile_display, + "Display each benchfile nonce found"), OPT_WITHOUT_ARG("--benchmark", opt_set_bool, &opt_benchmark, "Run cgminer in benchmark mode - produces no shares"), @@ -1014,28 +1417,149 @@ static struct opt_table opt_config_table[] = { set_int_0_to_200, opt_show_intval, &opt_bflsc_overheat, "Set overheat temperature where BFLSC devices throttle, 0 to disable"), #endif +#ifdef USE_AVALON + OPT_WITH_ARG("--bitburner-voltage", + opt_set_intval, NULL, &opt_bitburner_core_voltage, + "Set BitBurner (Avalon) core voltage, in millivolts"), + OPT_WITH_ARG("--bitburner-fury-voltage", + opt_set_intval, NULL, &opt_bitburner_fury_core_voltage, + "Set BitBurner Fury core voltage, in millivolts"), + OPT_WITH_ARG("--bitburner-fury-options", + opt_set_charp, NULL, &opt_bitburner_fury_options, + "Override avalon-options for BitBurner Fury boards baud:miners:asic:timeout:freq"), +#endif +#ifdef USE_BMSC + OPT_WITH_ARG("--bmsc-options", + set_bmsc_options, NULL, NULL, + opt_hidden), + OPT_WITH_ARG("--bmsc-bandops", + set_bmsc_bandops, NULL, NULL, + opt_hidden), + OPT_WITH_ARG("--bmsc-timing", + set_bmsc_timing, NULL, NULL, + opt_hidden), + OPT_WITHOUT_ARG("--bmsc-gray", + opt_set_bool, &opt_bmsc_gray, + "Use gray"), + OPT_WITH_ARG("--bmsc-freq", + set_bmsc_freq, NULL, NULL, + opt_hidden), + OPT_WITH_ARG("--bmsc-rdreg", + set_bmsc_rdreg, NULL, NULL, + opt_hidden), + OPT_WITH_ARG("--bmsc-voltage", + set_bmsc_voltage, NULL, NULL, + opt_hidden), + OPT_WITHOUT_ARG("--bmsc-bootstart", + opt_set_bool, &opt_bmsc_bootstart, + "Enable boot start, default: disabled"), + OPT_WITHOUT_ARG("--bmsc-rdworktest", + opt_set_bool, &opt_bmsc_rdworktest, + "Record work test data to file"), +#endif +#ifdef USE_BITMAIN + OPT_WITH_ARG("--bitmain-dev", + set_bitmain_dev, NULL, NULL, + "Set bitmain device (default: usb mode, other windows: COM1 or linux: /dev/bitmain-asic)"), + OPT_WITHOUT_ARG("--bitmain-hwerror", + opt_set_bool, &opt_bitmain_hwerror, + "Set bitmain device detect hardware error"), + OPT_WITHOUT_ARG("--bitmain-checkall", + opt_set_bool, &opt_bitmain_checkall, + "Set bitmain check all"), + OPT_WITHOUT_ARG("--bitmain-checkn2diff", + opt_set_bool, &opt_bitmain_checkn2diff, + "Set bitmain check not 2 pow diff"), + OPT_WITHOUT_ARG("--bitmain-nobeeper", + opt_set_bool, &opt_bitmain_nobeeper, + "Set bitmain beeper no ringing"), + OPT_WITHOUT_ARG("--bitmain-notempoverctrl", + opt_set_bool, &opt_bitmain_notempoverctrl, + "Set bitmain not stop runing when temprerature is over 80 degree Celsius"), + OPT_WITHOUT_ARG("--bitmain-auto", + opt_set_bool, &opt_bitmain_auto, + "Adjust bitmain overclock frequency dynamically for best hashrate"), + OPT_WITHOUT_ARG("--bitmain-homemode", + opt_set_bool, &opt_bitmain_homemode, + "Set bitmain miner to home mode"), + OPT_WITH_ARG("--bitmain-cutoff", + set_int_0_to_100, opt_show_intval, &opt_bitmain_overheat, + "Set bitmain overheat cut off temperature"), + OPT_WITH_ARG("--bitmain-fan", + set_bitmain_fan, NULL, NULL, + "Set fanspeed percentage for bitmain, single value or range (default: 20-100)"), + OPT_WITH_ARG("--bitmain-freq", + set_bitmain_freq, NULL, NULL, + "Set frequency"), + OPT_WITH_ARG("--bitmain-voltage", + set_bitmain_voltage, NULL, NULL, + "Set voltage"), + OPT_WITH_ARG("--bitmain-options", + set_bitmain_options, NULL, NULL, + "Set bitmain options baud:miners:asic:timeout:freq"), + OPT_WITH_ARG("--bitmain-temp", + set_int_0_to_100, opt_show_intval, &opt_bitmain_temp, + "Set bitmain target temperature"), +#endif +#ifdef USE_BITMINE_A1 + OPT_WITH_ARG("--bitmine-a1-options", + opt_set_charp, NULL, &opt_bitmine_a1_options, + "Bitmine A1 options ref_clk_khz:sys_clk_khz:spi_clk_khz:override_chip_num"), +#endif +#ifdef USE_BITFURY + OPT_WITH_ARG("--bxf-bits", + set_int_32_to_63, opt_show_intval, &opt_bxf_bits, + "Set max BXF/HXF bits for overclocking"), + OPT_WITH_ARG("--bxf-debug", + set_int_0_to_4, opt_show_intval, &opt_bxf_debug, + "BXF: Debug all USB I/O, > is to the board(s), < is from the board(s)"), + OPT_WITH_ARG("--bxf-temp-target", + set_int_0_to_200, opt_show_intval, &opt_bxf_temp_target, + "Set target temperature for BXF/HXF devices"), + OPT_WITH_ARG("--bxm-bits", + set_int_0_to_100, opt_show_intval, &opt_bxm_bits, + "Set BXM bits for overclocking"), +#endif +#ifdef USE_BLOCKERUPTER + OPT_WITH_ARG("--bet-clk", + opt_set_intval, opt_show_intval, &opt_bet_clk, + "Set Block Erupter clock"), +#endif +#ifdef HAVE_LIBCURL + OPT_WITH_ARG("--btc-address", + opt_set_charp, NULL, &opt_btc_address, + "Set bitcoin target address when solo mining to bitcoind (mandatory)"), + OPT_WITH_ARG("--btc-sig", + opt_set_charp, NULL, &opt_btc_sig, + "Set signature to add to coinbase when solo mining (optional)"), +#endif #ifdef HAVE_CURSES OPT_WITHOUT_ARG("--compact", opt_set_bool, &opt_compact, "Use compact display without per device statistics"), +#endif +#ifdef USE_COINTERRA + OPT_WITH_ARG("--cta-load", + set_int_0_to_255, opt_show_intval, &opt_cta_load, + "Set load for CTA devices, 0-255 range"), + OPT_WITH_ARG("--ps-load", + set_int_0_to_100, opt_show_intval, &opt_ps_load, + "Set power supply load for CTA devices, 0-100 range"), #endif OPT_WITHOUT_ARG("--debug|-D", enable_debug, &opt_debug, "Enable debug output"), - OPT_WITH_ARG("--device|-d", - set_devices, NULL, NULL, - "Select device to use, one value, range and/or comma separated (e.g. 0-2,4) default: all"), - OPT_WITHOUT_ARG("--disable-gpu|-G", - opt_set_bool, &opt_nogpu, -#ifdef HAVE_OPENCL - "Disable GPU mining even if suitable devices exist" -#else - opt_hidden -#endif - ), OPT_WITHOUT_ARG("--disable-rejecting", opt_set_bool, &opt_disable_pool, "Automatically disable pools that continually reject shares"), +#ifdef USE_DRILLBIT + OPT_WITH_ARG("--drillbit-options", + opt_set_charp, NULL, &opt_drillbit_options, + "Set drillbit options :clock[:clock_divider][:voltage]"), + OPT_WITH_ARG("--drillbit-auto", + opt_set_charp, NULL, &opt_drillbit_auto, + "Enable drillbit automatic tuning :[::]"), +#endif OPT_WITH_ARG("--expiry|-E", set_int_0_to_9999, opt_show_intval, &opt_expiry, "Upper bound on how many seconds after getting work we consider a share from it stale"), @@ -1045,58 +1569,45 @@ static struct opt_table opt_config_table[] = { OPT_WITHOUT_ARG("--fix-protocol", opt_set_bool, &opt_fix_protocol, "Do not redirect to a different getwork protocol (eg. stratum)"), -#ifdef HAVE_OPENCL - OPT_WITH_ARG("--gpu-dyninterval", - set_int_1_to_65535, opt_show_intval, &opt_dynamic_interval, - "Set the refresh interval in ms for GPUs using dynamic intensity"), - OPT_WITH_ARG("--gpu-platform", - set_int_0_to_9999, opt_show_intval, &opt_platform_id, - "Select OpenCL platform ID to use for GPU mining"), - OPT_WITH_ARG("--gpu-threads|-g", - set_int_1_to_10, opt_show_intval, &opt_g_threads, - "Number of threads per GPU (1 - 10)"), -#ifdef HAVE_ADL - OPT_WITH_ARG("--gpu-engine", - set_gpu_engine, NULL, NULL, - "GPU engine (over)clock range in Mhz - one value, range and/or comma separated list (e.g. 850-900,900,750-850)"), - OPT_WITH_ARG("--gpu-fan", - set_gpu_fan, NULL, NULL, - "GPU fan percentage range - one value, range and/or comma separated list (e.g. 0-85,85,65)"), - OPT_WITH_ARG("--gpu-map", - set_gpu_map, NULL, NULL, - "Map OpenCL to ADL device order manually, paired CSV (e.g. 1:0,2:1 maps OpenCL 1 to ADL 0, 2 to 1)"), - OPT_WITH_ARG("--gpu-memclock", - set_gpu_memclock, NULL, NULL, - "Set the GPU memory (over)clock in Mhz - one value for all or separate by commas for per card"), - OPT_WITH_ARG("--gpu-memdiff", - set_gpu_memdiff, NULL, NULL, - "Set a fixed difference in clock speed between the GPU and memory in auto-gpu mode"), - OPT_WITH_ARG("--gpu-powertune", - set_gpu_powertune, NULL, NULL, - "Set the GPU powertune percentage - one value for all or separate by commas for per card"), - OPT_WITHOUT_ARG("--gpu-reorder", - opt_set_bool, &opt_reorder, - "Attempt to reorder GPU devices according to PCI Bus ID"), - OPT_WITH_ARG("--gpu-vddc", - set_gpu_vddc, NULL, NULL, - "Set the GPU voltage in Volts - one value for all or separate by commas for per card"), -#endif -#ifdef USE_SCRYPT - OPT_WITH_ARG("--lookup-gap", - set_lookup_gap, NULL, NULL, - "Set GPU lookup gap for scrypt mining, comma separated"), - OPT_WITH_ARG("--intensity|-I", - set_intensity, NULL, NULL, - "Intensity of GPU scanning (d or " MIN_SHA_INTENSITY_STR - " -> " MAX_SCRYPT_INTENSITY_STR - ",default: d to maintain desktop interactivity)"), -#else - OPT_WITH_ARG("--intensity|-I", - set_intensity, NULL, NULL, - "Intensity of GPU scanning (d or " MIN_SHA_INTENSITY_STR - " -> " MAX_SHA_INTENSITY_STR - ",default: d to maintain desktop interactivity)"), -#endif +#ifdef USE_HASHFAST + OPT_WITHOUT_ARG("--hfa-dfu-boot", + opt_set_bool, &opt_hfa_dfu_boot, + opt_hidden), + OPT_WITH_ARG("--hfa-hash-clock", + set_int_0_to_9999, opt_show_intval, &opt_hfa_hash_clock, + "Set hashfast clock speed"), + OPT_WITH_ARG("--hfa-fail-drop", + set_int_0_to_100, opt_show_intval, &opt_hfa_fail_drop, + "Set how many MHz to drop clockspeed each failure on an overlocked hashfast device"), + OPT_WITH_CBARG("--hfa-fan", + set_hfa_fan, NULL, &opt_set_hfa_fan, + "Set fanspeed percentage for hashfast, single value or range (default: 10-85)"), + OPT_WITH_ARG("--hfa-name", + opt_set_charp, NULL, &opt_hfa_name, + "Set a unique name for a single hashfast device specified with --usb or the first device found"), + OPT_WITHOUT_ARG("--hfa-noshed", + opt_set_bool, &opt_hfa_noshed, + "Disable hashfast dynamic core disabling feature"), + OPT_WITH_ARG("--hfa-ntime-roll", + opt_set_intval, NULL, &opt_hfa_ntime_roll, + opt_hidden), + OPT_WITH_ARG("--hfa-options", + opt_set_charp, NULL, &opt_hfa_options, + "Set hashfast options name:clock (comma separated)"), + OPT_WITHOUT_ARG("--hfa-pll-bypass", + opt_set_bool, &opt_hfa_pll_bypass, + opt_hidden), + OPT_WITH_ARG("--hfa-temp-overheat", + set_int_0_to_200, opt_show_intval, &opt_hfa_overheat, + "Set the hashfast overheat throttling temperature"), + OPT_WITH_ARG("--hfa-temp-target", + set_int_0_to_200, opt_show_intval, &opt_hfa_target, + "Set the hashfast target temperature (0 to disable)"), +#endif +#ifdef USE_HASHRATIO + OPT_WITH_CBARG("--hro-freq", + set_hashratio_freq, NULL, &opt_hashratio_freq, + "Set the hashratio clock frequency"), #endif OPT_WITH_ARG("--hotplug", set_int_0_to_9999, NULL, &hotplug_time, @@ -1106,143 +1617,163 @@ static struct opt_table opt_config_table[] = { opt_hidden #endif ), -#if defined(HAVE_OPENCL) || defined(HAVE_MODMINER) - OPT_WITH_ARG("--kernel-path|-K", - opt_set_charp, opt_show_charp, &opt_kernel_path, - "Specify a path to where bitstream and kernel files are"), -#endif -#ifdef HAVE_OPENCL - OPT_WITH_ARG("--kernel|-k", - set_kernel, NULL, NULL, - "Override sha256 kernel to use (diablo, poclbm, phatk or diakgcn) - one value or comma separated"), -#endif #ifdef USE_ICARUS OPT_WITH_ARG("--icarus-options", - set_icarus_options, NULL, NULL, + opt_set_charp, NULL, &opt_icarus_options, opt_hidden), OPT_WITH_ARG("--icarus-timing", - set_icarus_timing, NULL, NULL, + opt_set_charp, NULL, &opt_icarus_timing, opt_hidden), #endif -#ifdef USE_AVALON - OPT_WITHOUT_ARG("--avalon-auto", - opt_set_bool, &opt_avalon_auto, - "Adjust avalon overclock frequency dynamically for best hashrate"), - OPT_WITH_ARG("--avalon-cutoff", - set_int_0_to_100, opt_show_intval, &opt_avalon_overheat, - "Set avalon overheat cut off temperature"), - OPT_WITH_ARG("--avalon-fan", - set_avalon_fan, NULL, NULL, - "Set fanspeed percentage for avalon, single value or range (default: 20-100)"), - OPT_WITH_ARG("--avalon-freq", - set_avalon_freq, NULL, NULL, - "Set frequency range for avalon-auto, single value or range"), - OPT_WITH_ARG("--avalon-options", - set_avalon_options, NULL, NULL, - "Set avalon options baud:miners:asic:timeout:freq"), - OPT_WITH_ARG("--avalon-temp", - set_int_0_to_100, opt_show_intval, &opt_avalon_temp, - "Set avalon target temperature"), - OPT_WITH_ARG("--bitburner-voltage", - opt_set_intval, NULL, &opt_bitburner_core_voltage, - "Set BitBurner core voltage, in millivolts"), +#if defined(HAVE_MODMINER) + OPT_WITH_ARG("--kernel-path|-K", + opt_set_charp, opt_show_charp, &opt_kernel_path, + "Specify a path to where bitstream files are"), +#endif +#ifdef USE_KLONDIKE + OPT_WITH_ARG("--klondike-options", + opt_set_charp, NULL, &opt_klondike_options, + "Set klondike options clock:temptarget"), #endif OPT_WITHOUT_ARG("--load-balance", set_loadbalance, &pool_strategy, - "Change multipool strategy from failover to efficiency based balance"), + "Change multipool strategy from failover to quota based balance"), OPT_WITH_ARG("--log|-l", set_int_0_to_9999, opt_show_intval, &opt_log_interval, "Interval in seconds between log output"), OPT_WITHOUT_ARG("--lowmem", opt_set_bool, &opt_lowmem, "Minimise caching of shares for low memory applications"), +#ifdef USE_MINION + OPT_WITH_ARG("--minion-chipreport", + set_int_0_to_100, opt_show_intval, &opt_minion_chipreport, + "Seconds to report chip 5min hashrate, range 0-100 (default: 0=disabled)"), + OPT_WITH_ARG("--minion-cores", + opt_set_charp, NULL, &opt_minion_cores, + opt_hidden), + OPT_WITHOUT_ARG("--minion-extra", + opt_set_bool, &opt_minion_extra, + opt_hidden), + OPT_WITH_ARG("--minion-freq", + opt_set_charp, NULL, &opt_minion_freq, + "Set minion chip frequencies in MHz, single value or comma list, range 100-1400 (default: 1200)"), + OPT_WITH_ARG("--minion-freqchange", + set_int_0_to_9999, opt_show_intval, &opt_minion_freqchange, + "Millisecond total time to do frequency changes (default: 1000)"), + OPT_WITH_ARG("--minion-freqpercent", + set_int_0_to_100, opt_show_intval, &opt_minion_freqpercent, + "Percentage to use when starting up a chip (default: 70%)"), + OPT_WITHOUT_ARG("--minion-idlecount", + opt_set_bool, &opt_minion_idlecount, + "Report when IdleCount is >0 or changes"), + OPT_WITH_ARG("--minion-ledcount", + set_int_0_to_100, opt_show_intval, &opt_minion_ledcount, + "Turn off led when more than this many chips below the ledlimit (default: 0)"), + OPT_WITH_ARG("--minion-ledlimit", + set_int_0_to_200, opt_show_intval, &opt_minion_ledlimit, + "Turn off led when chips GHs are below this (default: 90)"), + OPT_WITHOUT_ARG("--minion-noautofreq", + opt_set_bool, &opt_minion_noautofreq, + "Disable automatic frequency adjustment"), + OPT_WITHOUT_ARG("--minion-overheat", + opt_set_bool, &opt_minion_overheat, + "Enable directly halting any chip when the status exceeds 100C"), + OPT_WITH_ARG("--minion-spidelay", + set_int_0_to_9999, opt_show_intval, &opt_minion_spidelay, + "Add a delay in microseconds after each SPI I/O"), + OPT_WITH_ARG("--minion-spireset", + opt_set_charp, NULL, &opt_minion_spireset, + "SPI regular reset: iNNN for I/O count or sNNN for seconds - 0 means none"), + OPT_WITH_ARG("--minion-spisleep", + set_int_0_to_9999, opt_show_intval, &opt_minion_spisleep, + "Sleep time in milliseconds when doing an SPI reset"), + OPT_WITH_ARG("--minion-spiusec", + set_int_0_to_9999, NULL, &opt_minion_spiusec, + opt_hidden), + OPT_WITH_ARG("--minion-temp", + opt_set_charp, NULL, &opt_minion_temp, + "Set minion chip temperature threshold, single value or comma list, range 120-160 (default: 135C)"), +#endif #if defined(unix) || defined(__APPLE__) OPT_WITH_ARG("--monitor|-m", opt_set_charp, NULL, &opt_stderr_cmd, "Use custom pipe cmd for output messages"), #endif // defined(unix) +#ifdef USE_BITFURY + OPT_WITH_ARG("--nfu-bits", + set_int_32_to_63, opt_show_intval, &opt_nfu_bits, + "Set nanofury bits for overclocking, range 32-63"), +#endif OPT_WITHOUT_ARG("--net-delay", opt_set_bool, &opt_delaynet, "Impose small delays in networking to not overload slow routers"), - OPT_WITHOUT_ARG("--no-adl", - opt_set_bool, &opt_noadl, -#ifdef HAVE_ADL - "Disable the ATI display library used for monitoring and setting GPU parameters" -#else - opt_hidden -#endif - ), OPT_WITHOUT_ARG("--no-pool-disable", opt_set_invbool, &opt_disable_pool, opt_hidden), - OPT_WITHOUT_ARG("--no-restart", - opt_set_invbool, &opt_restart, -#ifdef HAVE_OPENCL - "Do not attempt to restart GPUs that hang" -#else - opt_hidden -#endif - ), OPT_WITHOUT_ARG("--no-submit-stale", opt_set_invbool, &opt_submit_stale, "Don't submit shares if they are detected as stale"), +#ifdef USE_BITFURY + OPT_WITH_ARG("--osm-led-mode", + set_int_0_to_4, opt_show_intval, &opt_osm_led_mode, + "Set LED mode for OneStringMiner devices"), +#endif OPT_WITH_ARG("--pass|-p", - set_pass, NULL, NULL, + set_pass, NULL, &opt_set_null, "Password for bitcoin JSON-RPC server"), OPT_WITHOUT_ARG("--per-device-stats", opt_set_bool, &want_per_device_stats, "Force verbose mode and output per-device statistics"), + OPT_WITH_ARG("--pools", + opt_set_bool, NULL, &opt_set_null, opt_hidden), OPT_WITHOUT_ARG("--protocol-dump|-P", opt_set_bool, &opt_protocol, "Verbose dump of protocol-level activities"), OPT_WITH_ARG("--queue|-Q", set_int_0_to_9999, opt_show_intval, &opt_queue, - "Minimum number of work items to have queued (0+)"), + "Maximum number of work items to have queued"), OPT_WITHOUT_ARG("--quiet|-q", opt_set_bool, &opt_quiet, "Disable logging output, display status and errors"), + OPT_WITH_ARG("--quota|-U", + set_quota, NULL, &opt_set_null, + "quota;URL combination for server with load-balance strategy quotas"), OPT_WITHOUT_ARG("--real-quiet", opt_set_bool, &opt_realquiet, "Disable all output"), - OPT_WITHOUT_ARG("--remove-disabled", - opt_set_bool, &opt_removedisabled, - "Remove disabled devices entirely, as if they didn't exist"), OPT_WITH_ARG("--retries", - set_null, NULL, NULL, + set_null, NULL, &opt_set_null, opt_hidden), OPT_WITH_ARG("--retry-pause", - set_null, NULL, NULL, + set_null, NULL, &opt_set_null, opt_hidden), +#ifdef USE_ICARUS + OPT_WITH_ARG("--rock-freq", + set_float_125_to_500, &opt_show_floatval, &opt_rock_freq, + "Set RockMiner frequency in MHz, range 125-500"), +#endif OPT_WITH_ARG("--rotate", - set_rotate, opt_show_intval, &opt_rotate_period, + set_rotate, NULL, &opt_set_null, "Change multipool strategy from failover to regularly rotate at N minutes"), OPT_WITHOUT_ARG("--round-robin", set_rr, &pool_strategy, "Change multipool strategy from failover to round robin on failure"), #ifdef USE_FPGA_SERIAL - OPT_WITH_ARG("--scan-serial|-S", - add_serial, NULL, NULL, + OPT_WITH_CBARG("--scan-serial|-S", + add_serial, NULL, &opt_add_serial, "Serial port to probe for Serial FPGA Mining device"), #endif OPT_WITH_ARG("--scan-time|-s", set_int_0_to_9999, opt_show_intval, &opt_scantime, "Upper bound on time spent scanning current work, in seconds"), - OPT_WITH_ARG("--sched-start", - set_schedtime, NULL, &schedstart, + OPT_WITH_CBARG("--sched-start", + set_sched_start, NULL, &opt_set_sched_start, "Set a time of day in HH:MM to start mining (a once off without a stop time)"), - OPT_WITH_ARG("--sched-stop", - set_schedtime, NULL, &schedstop, + OPT_WITH_CBARG("--sched-stop", + set_sched_stop, NULL, &opt_set_sched_stop, "Set a time of day in HH:MM to stop mining (will quit without a start time)"), -#ifdef USE_SCRYPT - OPT_WITHOUT_ARG("--scrypt", - opt_set_bool, &opt_scrypt, - "Use the scrypt algorithm for mining (litecoin only)"), - OPT_WITH_ARG("--shaders", - set_shaders, NULL, NULL, - "GPU shaders per card for tuning scrypt, comma separated"), -#endif - OPT_WITH_ARG("--sharelog", - set_sharelog, NULL, NULL, + OPT_WITH_CBARG("--sharelog", + set_sharelog, NULL, &opt_set_sharelog, "Append share log to file"), OPT_WITH_ARG("--shares", opt_set_intval, NULL, &opt_shares, @@ -1250,26 +1781,18 @@ static struct opt_table opt_config_table[] = { OPT_WITH_ARG("--socks-proxy", opt_set_charp, NULL, &opt_socks_proxy, "Set socks4 proxy (host:port)"), + OPT_WITH_ARG("--suggest-diff", + opt_set_intval, NULL, &opt_suggest_diff, + "Suggest miner difficulty for pool to user (default: none)"), #ifdef HAVE_SYSLOG_H OPT_WITHOUT_ARG("--syslog", opt_set_bool, &use_syslog, "Use system log for output messages (default: standard error)"), #endif -#if defined(HAVE_ADL) || defined(USE_BITFORCE) || defined(USE_MODMINER) || defined(USE_BFLSC) - OPT_WITH_ARG("--temp-cutoff", - set_temp_cutoff, opt_show_intval, &opt_cutofftemp, +#if defined(USE_BITFORCE) || defined(USE_MODMINER) || defined(USE_BFLSC) + OPT_WITH_CBARG("--temp-cutoff", + set_temp_cutoff, opt_show_intval, &opt_set_temp_cutoff, "Temperature where a device will be automatically disabled, one value or comma separated list"), -#endif -#ifdef HAVE_ADL - OPT_WITH_ARG("--temp-hysteresis", - set_int_1_to_10, opt_show_intval, &opt_hysteresis, - "Set how much the temperature can fluctuate outside limits when automanaging speeds"), - OPT_WITH_ARG("--temp-overheat", - set_temp_overheat, opt_show_intval, &opt_overheattemp, - "Overheat temperature when automatically managing fan and GPU speeds, one value or comma separated list"), - OPT_WITH_ARG("--temp-target", - set_temp_target, opt_show_intval, &opt_targettemp, - "Target temperature when automatically managing fan and GPU speeds, one value or comma separated list"), #endif OPT_WITHOUT_ARG("--text-only|-T", opt_set_invbool, &use_curses, @@ -1279,20 +1802,12 @@ static struct opt_table opt_config_table[] = { opt_hidden #endif ), -#ifdef USE_SCRYPT - OPT_WITH_ARG("--thread-concurrency", - set_thread_concurrency, NULL, NULL, - "Set GPU thread concurrency for scrypt mining, comma separated"), -#endif OPT_WITH_ARG("--url|-o", - set_url, NULL, NULL, + set_url, NULL, &opt_set_null, "URL for bitcoin JSON-RPC server"), - OPT_WITH_ARG("--user|-u", - set_user, NULL, NULL, - "Username for bitcoin JSON-RPC server"), #ifdef USE_USBUTILS OPT_WITH_ARG("--usb", - set_usb_select, NULL, NULL, + opt_set_charp, NULL, &opt_usb_select, "USB device selection"), OPT_WITH_ARG("--usb-dump", set_int_0_to_10, opt_show_intval, &opt_usbdump, @@ -1301,27 +1816,21 @@ static struct opt_table opt_config_table[] = { opt_set_bool, &opt_usb_list_all, opt_hidden), #endif -#ifdef HAVE_OPENCL - OPT_WITH_ARG("--vectors|-v", - set_vector, NULL, NULL, - "Override detected optimal vector (1, 2 or 4) - one value or comma separated list"), -#endif + OPT_WITH_ARG("--user|-u", + set_user, NULL, &opt_set_null, + "Username for bitcoin JSON-RPC server"), + OPT_WITH_ARG("--userpass|-O", + set_userpass, NULL, &opt_set_null, + "Username:Password pair for bitcoin JSON-RPC server"), OPT_WITHOUT_ARG("--verbose", opt_set_bool, &opt_log_output, "Log verbose output to stderr as well as status output"), -#ifdef HAVE_OPENCL - OPT_WITH_ARG("--worksize|-w", - set_worksize, NULL, NULL, - "Override detected optimal worksize - one value or comma separated list"), -#endif - OPT_WITH_ARG("--userpass|-O", - set_userpass, NULL, NULL, - "Username:Password pair for bitcoin JSON-RPC server"), + OPT_WITHOUT_ARG("--widescreen", + opt_set_bool, &opt_widescreen, + "Use extra wide display without toggling"), OPT_WITHOUT_ARG("--worktime", opt_set_bool, &opt_worktime, "Display extra work time debug information"), - OPT_WITH_ARG("--pools", - opt_set_bool, NULL, NULL, opt_hidden), OPT_ENDTABLE }; @@ -1333,6 +1842,7 @@ static char *parse_config(json_t *config, bool fileconf) { static char err_buf[200]; struct opt_table *opt; + const char *str; json_t *val; if (fileconf && !fileconf_load) @@ -1360,17 +1870,25 @@ static char *parse_config(json_t *config, bool fileconf) if (!val) continue; - if ((opt->type & OPT_HASARG) && json_is_string(val)) { - err = opt->cb_arg(json_string_value(val), - opt->u.arg); - } else if ((opt->type & OPT_HASARG) && json_is_array(val)) { - int n, size = json_array_size(val); - - for (n = 0; n < size && !err; n++) { - if (json_is_string(json_array_get(val, n))) - err = opt->cb_arg(json_string_value(json_array_get(val, n)), opt->u.arg); - else if (json_is_object(json_array_get(val, n))) - err = parse_config(json_array_get(val, n), false); + if ((opt->type & (OPT_HASARG | OPT_PROCESSARG)) && json_is_string(val)) { + str = json_string_value(val); + err = opt->cb_arg(str, opt->u.arg); + if (opt->type == OPT_PROCESSARG) + opt_set_charp(str, opt->u.arg); + } else if ((opt->type & (OPT_HASARG | OPT_PROCESSARG)) && json_is_array(val)) { + json_t *arr_val; + size_t index; + + json_array_foreach(val, index, arr_val) { + if (json_is_string(arr_val)) { + str = json_string_value(arr_val); + err = opt->cb_arg(str, opt->u.arg); + if (opt->type == OPT_PROCESSARG) + opt_set_charp(str, opt->u.arg); + } else if (json_is_object(arr_val)) + err = parse_config(arr_val, false); + if (err) + break; } } else if ((opt->type & OPT_NOARG) && json_is_true(val)) err = opt->cb(opt->u.arg); @@ -1403,6 +1921,26 @@ static char *parse_config(json_t *config, bool fileconf) char *cnfbuf = NULL; +#ifdef HAVE_LIBCURL +char conf_web1[] = "http://"; +char conf_web2[] = "https://"; + +static char *load_web_config(const char *arg) +{ + json_t *val = json_web_config(arg); + + if (!val || !json_is_object(val)) + return JSON_WEB_ERROR; + + if (!cnfbuf) + cnfbuf = strdup(arg); + + config_loaded = true; + + return parse_config(val, true); +} +#endif + static char *load_config(const char *arg, void __maybe_unused *unused) { json_error_t err; @@ -1410,17 +1948,19 @@ static char *load_config(const char *arg, void __maybe_unused *unused) char *json_error; size_t siz; +#ifdef HAVE_LIBCURL + if (strncasecmp(arg, conf_web1, sizeof(conf_web1)-1) == 0 || + strncasecmp(arg, conf_web2, sizeof(conf_web2)-1) == 0) + return load_web_config(arg); +#endif + if (!cnfbuf) cnfbuf = strdup(arg); if (++include_count > JSON_MAX_DEPTH) return JSON_MAX_DEPTH_ERR; -#if JANSSON_MAJOR_VERSION > 1 config = json_load_file(arg, 0, &err); -#else - config = json_load_file(arg, &err); -#endif if (!json_is_object(config)) { siz = JSON_LOAD_ERROR_LEN + strlen(arg) + strlen(err.text); json_error = malloc(siz); @@ -1466,30 +2006,67 @@ extern const char *opt_argv0; static char *opt_verusage_and_exit(const char *extra) { printf("%s\nBuilt with " +#ifdef USE_BMSC + "bmsc " +#endif +#ifdef USE_BITMAIN + "bitmain " +#endif #ifdef USE_AVALON "avalon " #endif +#ifdef USE_AVALON2 + "avalon2 " +#endif +#ifdef USE_AVALON4 + "avalon4 " +#endif #ifdef USE_BFLSC "bflsc " #endif #ifdef USE_BITFORCE "bitforce " #endif -#ifdef HAVE_OPENCL - "GPU " +#ifdef USE_BITFURY + "bitfury " +#endif +#ifdef USE_COINTERRA + "cointerra " +#endif +#ifdef USE_DRILLBIT + "drillbit " +#endif +#ifdef USE_HASHFAST + "hashfast " #endif #ifdef USE_ICARUS "icarus " #endif +#ifdef USE_KLONDIKE + "klondike " +#endif +#ifdef USE_KNC + "KnC " +#endif +#ifdef USE_BAB + "BaB " +#endif +#ifdef USE_MINION + "minion " +#endif #ifdef USE_MODMINER "modminer " #endif -#ifdef USE_SCRYPT - "scrypt " +#ifdef USE_BITMINE_A1 + "Bitmine.A1 " +#endif +#ifdef USE_SP10 + "spondoolies " #endif -#ifdef USE_ZTEX - "ztex " +#ifdef USE_SP30 + "sp30 " #endif + "mining support.\n" , packagename); printf("%s", opt_usage(opt_argv0, extra)); @@ -1497,16 +2074,11 @@ static char *opt_verusage_and_exit(const char *extra) exit(0); } -#if defined(HAVE_OPENCL) || defined(USE_USBUTILS) +#if defined(USE_USBUTILS) char *display_devs(int *ndevs) { *ndevs = 0; -#ifdef HAVE_OPENCL - print_ndevs(ndevs); -#endif -#ifdef USE_USBUTILS usb_all(0); -#endif exit(*ndevs); } #endif @@ -1514,27 +2086,20 @@ char *display_devs(int *ndevs) /* These options are available from commandline only */ static struct opt_table opt_cmdline_table[] = { OPT_WITH_ARG("--config|-c", - load_config, NULL, NULL, + load_config, NULL, &opt_set_null, "Load a JSON-format configuration file\n" "See example.conf for an example configuration."), OPT_WITH_ARG("--default-config", - set_default_config, NULL, NULL, + set_default_config, NULL, &opt_set_null, "Specify the filename of the default config file\n" "Loaded at start and used when saving without a name."), OPT_WITHOUT_ARG("--help|-h", opt_verusage_and_exit, NULL, "Print this message"), -#if defined(HAVE_OPENCL) || defined(USE_USBUTILS) +#if defined(USE_USBUTILS) OPT_WITHOUT_ARG("--ndevs|-n", display_devs, &nDevs, - "Display " -#ifdef HAVE_OPENCL - "number of detected GPUs, OpenCL platform information, " -#endif -#ifdef USE_USBUTILS - "all USB devices, " -#endif - "and exit"), + "Display all USB devices and exit"), #endif OPT_WITHOUT_ARG("--version|-V", opt_version_and_exit, packagename, @@ -1542,6 +2107,7 @@ static struct opt_table opt_cmdline_table[] = { OPT_ENDTABLE }; +#ifdef HAVE_LIBCURL static bool jobj_binary(const json_t *obj, const char *key, void *buf, size_t buflen, bool required) { @@ -1564,6 +2130,7 @@ static bool jobj_binary(const json_t *obj, const char *key, return true; } +#endif static void calc_midstate(struct work *work) { @@ -1578,6 +2145,18 @@ static void calc_midstate(struct work *work) endian_flip32(work->midstate, work->midstate); } +/* Returns the current value of total_work and increments it */ +static int total_work_inc(void) +{ + int ret; + + cg_wlock(&control_lock); + ret = total_work++; + cg_wunlock(&control_lock); + + return ret; +} + static struct work *make_work(void) { struct work *work = calloc(1, sizeof(struct work)); @@ -1585,9 +2164,7 @@ static struct work *make_work(void) if (unlikely(!work)) quit(1, "Failed to calloc work in make_work"); - cg_wlock(&control_lock); - work->id = total_work++; - cg_wunlock(&control_lock); + work->id = total_work_inc(); return work; } @@ -1605,95 +2182,45 @@ void clean_work(struct work *work) /* All dynamically allocated work structs should be freed here to not leak any * ram from arrays allocated within the work struct */ -void free_work(struct work *work) +void _free_work(struct work *work) { clean_work(work); free(work); } static void gen_hash(unsigned char *data, unsigned char *hash, int len); +static void calc_diff(struct work *work, double known); +char *workpadding = "000000800000000000000000000000000000000000000000000000000000000000000000000000000000000080020000"; +#ifdef HAVE_LIBCURL /* Process transactions with GBT by storing the binary value of the first * transaction, and the hashes of the remaining transactions since these * remain constant with an altered coinbase when generating work. Must be * entered under gbt_lock */ -static bool __build_gbt_txns(struct pool *pool, json_t *res_val) +static void gbt_merkle_bins(struct pool *pool, json_t *transaction_arr); + +static void __build_gbt_txns(struct pool *pool, json_t *res_val) { json_t *txn_array; - bool ret = false; - size_t cal_len; - int i; - - free(pool->txn_hashes); - pool->txn_hashes = NULL; - pool->gbt_txns = 0; txn_array = json_object_get(res_val, "transactions"); - if (!json_is_array(txn_array)) - goto out; - - ret = true; - pool->gbt_txns = json_array_size(txn_array); - if (!pool->gbt_txns) - goto out; - - pool->txn_hashes = calloc(32 * (pool->gbt_txns + 1), 1); - if (unlikely(!pool->txn_hashes)) - quit(1, "Failed to calloc txn_hashes in __build_gbt_txns"); - - for (i = 0; i < pool->gbt_txns; i++) { - json_t *txn_val = json_object_get(json_array_get(txn_array, i), "data"); - const char *txn = json_string_value(txn_val); - int txn_len = strlen(txn); - unsigned char *txn_bin; - - cal_len = txn_len; - align_len(&cal_len); - txn_bin = calloc(cal_len, 1); - if (unlikely(!txn_bin)) - quit(1, "Failed to calloc txn_bin in __build_gbt_txns"); - if (unlikely(!hex2bin(txn_bin, txn, txn_len / 2))) - quit(1, "Failed to hex2bin txn_bin"); - - gen_hash(txn_bin, pool->txn_hashes + (32 * i), txn_len / 2); - free(txn_bin); - } -out: - return ret; + gbt_merkle_bins(pool, txn_array); } -static unsigned char *__gbt_merkleroot(struct pool *pool) +static void __gbt_merkleroot(struct pool *pool, unsigned char *merkle_root) { - unsigned char *merkle_hash; - int i, txns; - - merkle_hash = calloc(32 * (pool->gbt_txns + 2), 1); - if (unlikely(!merkle_hash)) - quit(1, "Failed to calloc merkle_hash in __gbt_merkleroot"); - - gen_hash(pool->coinbase, merkle_hash, pool->coinbase_len); - - if (pool->gbt_txns) - memcpy(merkle_hash + 32, pool->txn_hashes, pool->gbt_txns * 32); - - txns = pool->gbt_txns + 1; - while (txns > 1) { - if (txns % 2) { - memcpy(&merkle_hash[txns * 32], &merkle_hash[(txns - 1) * 32], 32); - txns++; - } - for (i = 0; i < txns; i += 2){ - unsigned char hashout[32]; + unsigned char merkle_sha[64]; + int i; - gen_hash(merkle_hash + (i * 32), hashout, 64); - memcpy(merkle_hash + (i / 2 * 32), hashout, 32); - } - txns /= 2; + gen_hash(pool->coinbase, merkle_root, pool->coinbase_len); + memcpy(merkle_sha, merkle_root, 32); + for (i = 0; i < pool->merkles; i++) { + memcpy(merkle_sha + 32, pool->merklebin + i * 32, 32); + gen_hash(merkle_sha, merkle_root, 64); + memcpy(merkle_sha, merkle_root, 32); } - return merkle_hash; } -static void calc_diff(struct work *work, int known); static bool work_decode(struct pool *pool, struct work *work, json_t *val); static void update_gbt(struct pool *pool) @@ -1718,7 +2245,8 @@ static void update_gbt(struct pool *pool) if (rc) { applog(LOG_DEBUG, "Successfully retrieved and updated GBT from pool %u %s", pool->pool_no, pool->rpc_url); - cgtime(&pool->tv_idle); + if (pool == current_pool()) + opt_work_update = true; } else { applog(LOG_DEBUG, "Successfully retrieved but FAILED to decipher GBT from pool %u %s", pool->pool_no, pool->rpc_url); @@ -1732,22 +2260,22 @@ static void update_gbt(struct pool *pool) curl_easy_cleanup(curl); } -char *workpadding = "000000800000000000000000000000000000000000000000000000000000000000000000000000000000000080020000"; - static void gen_gbt_work(struct pool *pool, struct work *work) { - unsigned char *merkleroot; + unsigned char merkleroot[32]; struct timeval now; + uint64_t nonce2le; cgtime(&now); if (now.tv_sec - pool->tv_lastwork.tv_sec > 60) update_gbt(pool); cg_wlock(&pool->gbt_lock); - memcpy(pool->coinbase + pool->nonce2_offset, &pool->nonce2, 4); + nonce2le = htole64(pool->nonce2); + memcpy(pool->coinbase + pool->nonce2_offset, &nonce2le, pool->n2size); pool->nonce2++; cg_dwlock(&pool->gbt_lock); - merkleroot = __gbt_merkleroot(pool); + __gbt_merkleroot(pool, merkleroot); memcpy(work->data, &pool->gbt_version, 4); memcpy(work->data + 4, pool->previousblockhash, 32); @@ -1766,7 +2294,6 @@ static void gen_gbt_work(struct pool *pool, struct work *work) cg_runlock(&pool->gbt_lock); flip32(work->data + 4 + 32, merkleroot); - free(merkleroot); memset(work->data + 4 + 32 + 32 + 4 + 4, 0, 4); /* nonce */ hex2bin(work->data + 4 + 32 + 32 + 4 + 4 + 4, workpadding, 48); @@ -1783,10 +2310,11 @@ static void gen_gbt_work(struct pool *pool, struct work *work) local_work++; work->pool = pool; work->gbt = true; - work->id = total_work++; work->longpoll = false; work->getwork_mode = GETWORK_MODE_GBT; work->work_block = work_block; + /* Nominally allow a driver to ntime roll 60 seconds */ + work->drv_rolllimit = 60; calc_diff(work, 0); cgtime(&work->tv_staged); } @@ -1841,8 +2369,9 @@ static bool gbt_decode(struct pool *pool, json_t *res_val) free(pool->coinbasetxn); pool->coinbasetxn = strdup(coinbasetxn); cbt_len = strlen(pool->coinbasetxn) / 2; - pool->coinbase_len = cbt_len + 4; - /* We add 4 bytes of extra data corresponding to nonce2 of stratum */ + /* We add 8 bytes of extra data corresponding to nonce2 */ + pool->n2size = 8; + pool->coinbase_len = cbt_len + pool->n2size; cal_len = pool->coinbase_len + 1; align_len(&cal_len); free(pool->coinbase); @@ -1853,7 +2382,7 @@ static bool gbt_decode(struct pool *pool, json_t *res_val) extra_len = (uint8_t *)(pool->coinbase + 41); orig_len = *extra_len; hex2bin(pool->coinbase + 42, pool->coinbasetxn + 84, orig_len); - *extra_len += 4; + *extra_len += pool->n2size; hex2bin(pool->coinbase + 42 + *extra_len, pool->coinbasetxn + 84 + (orig_len * 2), cbt_len - orig_len - 42); pool->nonce2_offset = orig_len + 42; @@ -1908,7 +2437,249 @@ static bool getwork_decode(json_t *res_val, struct work *work) /* Returns whether the pool supports local work generation or not. */ static bool pool_localgen(struct pool *pool) { - return (pool->has_stratum || pool->has_gbt); + return (pool->has_stratum || pool->has_gbt || pool->gbt_solo); +} + +static void gbt_merkle_bins(struct pool *pool, json_t *transaction_arr) +{ + unsigned char *hashbin; + json_t *arr_val; + int i, j, binleft, binlen; + + free(pool->txn_data); + pool->txn_data = NULL; + pool->transactions = 0; + pool->merkles = 0; + pool->transactions = json_array_size(transaction_arr); + binlen = pool->transactions * 32 + 32; + hashbin = alloca(binlen + 32); + memset(hashbin, 0, 32); + binleft = binlen / 32; + if (pool->transactions) { + int len = 0, ofs = 0; + const char *txn; + + for (i = 0; i < pool->transactions; i++) { + arr_val = json_array_get(transaction_arr, i); + txn = json_string_value(json_object_get(arr_val, "data")); + if (!txn) { + applog(LOG_ERR, "Pool %d json_string_value fail - cannot find transaction data", + pool->pool_no); + return; + } + len += strlen(txn); + } + + pool->txn_data = malloc(len + 1); + if (unlikely(!pool->txn_data)) + quit(1, "Failed to calloc txn_data in gbt_merkle_bins"); + pool->txn_data[len] = '\0'; + + for (i = 0; i < pool->transactions; i++) { + unsigned char binswap[32]; + const char *hash; + + arr_val = json_array_get(transaction_arr, i); + hash = json_string_value(json_object_get(arr_val, "hash")); + txn = json_string_value(json_object_get(arr_val, "data")); + len = strlen(txn); + memcpy(pool->txn_data + ofs, txn, len); + ofs += len; + if (!hash) { + unsigned char *txn_bin; + int txn_len; + + txn_len = len / 2; + txn_bin = malloc(txn_len); + if (!txn_bin) + quit(1, "Failed to malloc txn_bin in gbt_merkle_bins"); + hex2bin(txn_bin, txn, txn_len); + /* This is needed for pooled mining since only + * transaction data and not hashes are sent */ + gen_hash(txn_bin, hashbin + 32 + 32 * i, txn_len); + continue; + } + if (!hex2bin(binswap, hash, 32)) { + applog(LOG_ERR, "Failed to hex2bin hash in gbt_merkle_bins"); + return; + } + swab256(hashbin + 32 + 32 * i, binswap); + } + } + if (binleft > 1) { + while (42) { + if (binleft == 1) + break; + memcpy(pool->merklebin + (pool->merkles * 32), hashbin + 32, 32); + pool->merkles++; + if (binleft % 2) { + memcpy(hashbin + binlen, hashbin + binlen - 32, 32); + binlen += 32; + binleft++; + } + for (i = 32, j = 64; j < binlen; i += 32, j += 64) { + gen_hash(hashbin + j, hashbin + i, 64); + } + binleft /= 2; + binlen = binleft * 32; + } + } + if (opt_debug) { + char hashhex[68]; + + for (i = 0; i < pool->merkles; i++) { + __bin2hex(hashhex, pool->merklebin + i * 32, 32); + applog(LOG_DEBUG, "MH%d %s",i, hashhex); + } + } + applog(LOG_INFO, "Stored %d transactions from pool %d", pool->transactions, + pool->pool_no); +} + +static double diff_from_target(void *target); + +static const char scriptsig_header[] = "01000000010000000000000000000000000000000000000000000000000000000000000000ffffffff"; +static unsigned char scriptsig_header_bin[41]; + +static bool gbt_solo_decode(struct pool *pool, json_t *res_val) +{ + json_t *transaction_arr, *coinbase_aux; + const char *previousblockhash; + unsigned char hash_swap[32]; + struct timeval now; + const char *target; + uint64_t coinbasevalue; + const char *flags; + const char *bits; + char header[228]; + int ofs = 0, len; + uint64_t *u64; + uint32_t *u32; + int version; + int curtime; + int height; + + previousblockhash = json_string_value(json_object_get(res_val, "previousblockhash")); + target = json_string_value(json_object_get(res_val, "target")); + transaction_arr = json_object_get(res_val, "transactions"); + version = json_integer_value(json_object_get(res_val, "version")); + curtime = json_integer_value(json_object_get(res_val, "curtime")); + bits = json_string_value(json_object_get(res_val, "bits")); + height = json_integer_value(json_object_get(res_val, "height")); + coinbasevalue = json_integer_value(json_object_get(res_val, "coinbasevalue")); + coinbase_aux = json_object_get(res_val, "coinbaseaux"); + flags = json_string_value(json_object_get(coinbase_aux, "flags")); + + if (!previousblockhash || !target || !version || !curtime || !bits || !coinbase_aux || !flags) { + applog(LOG_ERR, "Pool %d JSON failed to decode GBT", pool->pool_no); + return false; + } + + applog(LOG_DEBUG, "previousblockhash: %s", previousblockhash); + applog(LOG_DEBUG, "target: %s", target); + applog(LOG_DEBUG, "version: %d", version); + applog(LOG_DEBUG, "curtime: %d", curtime); + applog(LOG_DEBUG, "bits: %s", bits); + applog(LOG_DEBUG, "height: %d", height); + applog(LOG_DEBUG, "flags: %s", flags); + + cg_wlock(&pool->gbt_lock); + hex2bin(hash_swap, previousblockhash, 32); + swap256(pool->previousblockhash, hash_swap); + __bin2hex(pool->prev_hash, pool->previousblockhash, 32); + + hex2bin(hash_swap, target, 32); + swab256(pool->gbt_target, hash_swap); + pool->sdiff = diff_from_target(pool->gbt_target); + + pool->gbt_version = htobe32(version); + pool->curtime = htobe32(curtime); + snprintf(pool->ntime, 9, "%08x", curtime); + snprintf(pool->bbversion, 9, "%08x", version); + snprintf(pool->nbit, 9, "%s", bits); + pool->nValue = coinbasevalue; + hex2bin((unsigned char *)&pool->gbt_bits, bits, 4); + gbt_merkle_bins(pool, transaction_arr); + pool->height = height; + + memset(pool->scriptsig_base, 0, 42); + ofs++; // Leave room for template length + + /* Put block height at start of template. */ + ofs += ser_number(pool->scriptsig_base + ofs, height); // max 5 + + /* Followed by flags */ + len = strlen(flags) / 2; + pool->scriptsig_base[ofs++] = len; + hex2bin(pool->scriptsig_base + ofs, flags, len); + ofs += len; + + /* Followed by timestamp */ + cgtime(&now); + pool->scriptsig_base[ofs++] = 0xfe; // Encode seconds as u32 + u32 = (uint32_t *)&pool->scriptsig_base[ofs]; + *u32 = htole32(now.tv_sec); + ofs += 4; // sizeof uint32_t + pool->scriptsig_base[ofs++] = 0xfe; // Encode usecs as u32 + u32 = (uint32_t *)&pool->scriptsig_base[ofs]; + *u32 = htole32(now.tv_usec); + ofs += 4; // sizeof uint32_t + + memcpy(pool->scriptsig_base + ofs, "\x09\x63\x67\x6d\x69\x6e\x65\x72\x34\x32", 10); + ofs += 10; + + /* Followed by extranonce size, fixed at 8 */ + pool->scriptsig_base[ofs++] = 8; + pool->nonce2_offset = 41 + ofs; + ofs += 8; + + if (opt_btc_sig) { + len = strlen(opt_btc_sig); + if (len > 32) + len = 32; + pool->scriptsig_base[ofs++] = len; + memcpy(pool->scriptsig_base + ofs, opt_btc_sig, len); + ofs += len; + } + + pool->scriptsig_base[0] = ofs++; // Template length + pool->n1_len = ofs; + + len = 41 // prefix + + ofs // Template length + + 4 // txin sequence no + + 1 // transactions + + 8 // value + + 1 + 25 // txout + + 4; // lock + free(pool->coinbase); + pool->coinbase = calloc(len, 1); + if (unlikely(!pool->coinbase)) + quit(1, "Failed to calloc coinbase in gbt_solo_decode"); + + memcpy(pool->coinbase + 41, pool->scriptsig_base, ofs); + memcpy(pool->coinbase + 41 + ofs, "\xff\xff\xff\xff", 4); + pool->coinbase[41 + ofs + 4] = 1; + u64 = (uint64_t *)&(pool->coinbase[41 + ofs + 4 + 1]); + *u64 = htole64(coinbasevalue); + + pool->nonce2 = 0; + pool->n2size = 4; + pool->coinbase_len = 41 + ofs + 4 + 1 + 8 + 1 + 25 + 4; + cg_wunlock(&pool->gbt_lock); + + snprintf(header, 225, "%s%s%s%s%s%s%s", + pool->bbversion, + pool->prev_hash, + "0000000000000000000000000000000000000000000000000000000000000000", + pool->ntime, + pool->nbit, + "00000000", /* nonce */ + workpadding); + if (unlikely(!hex2bin(pool->header_bin, header, 112))) + quit(1, "Failed to hex2bin header in gbt_solo_decode"); + + return true; } static bool work_decode(struct pool *pool, struct work *work, json_t *val) @@ -1922,7 +2693,12 @@ static bool work_decode(struct pool *pool, struct work *work, json_t *val) goto out; } - if (pool->has_gbt) { + if (pool->gbt_solo) { + if (unlikely(!gbt_solo_decode(pool, res_val))) + goto out; + ret = true; + goto out; + } else if (pool->has_gbt) { if (unlikely(!gbt_decode(pool, res_val))) goto out; work->gbt = true; @@ -1940,6 +2716,13 @@ static bool work_decode(struct pool *pool, struct work *work, json_t *val) out: return ret; } +#else /* HAVE_LIBCURL */ +/* Always true with stratum */ +#define pool_localgen(pool) (true) +#define json_rpc_call(curl, url, userpass, rpc_req, probe, longpoll, rolltime, pool, share) (NULL) +#define work_decode(pool, work, val) (false) +#define gen_gbt_work(pool, work) {} +#endif /* HAVE_LIBCURL */ int dev_from_id(int thr_id) { @@ -1949,13 +2732,15 @@ int dev_from_id(int thr_id) } /* Create an exponentially decaying average over the opt_log_interval */ -void decay_time(double *f, double fadd, double fsecs) +void decay_time(double *f, double fadd, double fsecs, double interval) { double ftotal, fprop; - fprop = 1.0 - 1 / (exp(fsecs / (double)opt_log_interval)); + if (fsecs <= 0) + return; + fprop = 1.0 - 1 / (exp(fsecs / interval)); ftotal = 1.0 + fprop; - *f += (fadd * fprop); + *f += (fadd / fsecs * fprop); *f /= ftotal; } @@ -1979,6 +2764,7 @@ static int total_staged(void) WINDOW *mainwin, *statuswin, *logwin; #endif double total_secs = 1.0; +double last_total_secs = 1.0; static char statusline[256]; /* logstart is where the log window should start */ static int devcursor, logstart, logcursor; @@ -1986,9 +2772,6 @@ static int devcursor, logstart, logcursor; /* statusy is where the status window goes up to in cases where it won't fit at startup */ static int statusy; #endif -#ifdef HAVE_OPENCL -struct cgpu_info gpus[MAX_GPUDEVICES]; /* Maximum number apparently possible */ -#endif #ifdef HAVE_CURSES static inline void unlock_curses(void) @@ -2070,12 +2853,10 @@ static void suffix_string(uint64_t val, char *buf, size_t bufsiz, int sigdigits) } } -static void get_statline(char *buf, size_t bufsiz, struct cgpu_info *cgpu) +double cgpu_runtime(struct cgpu_info *cgpu) { - char displayed_hashes[16], displayed_rolling[16]; - uint64_t dh64, dr64; struct timeval now; - double dev_runtime, wu; + double dev_runtime; if (cgpu->dev_start_tv.tv_sec == 0) dev_runtime = total_secs; @@ -2086,6 +2867,32 @@ static void get_statline(char *buf, size_t bufsiz, struct cgpu_info *cgpu) if (dev_runtime < 1.0) dev_runtime = 1.0; + return dev_runtime; +} + +double tsince_restart(void) +{ + struct timeval now; + + cgtime(&now); + return tdiff(&now, &restart_tv_start); +} + +double tsince_update(void) +{ + struct timeval now; + + cgtime(&now); + return tdiff(&now, &update_tv_start); +} + +static void get_statline(char *buf, size_t bufsiz, struct cgpu_info *cgpu) +{ + char displayed_hashes[16], displayed_rolling[16]; + double dev_runtime, wu; + uint64_t dh64, dr64; + + dev_runtime = cgpu_runtime(cgpu); wu = cgpu->diff1 / dev_runtime * 60.0; @@ -2107,16 +2914,9 @@ static void get_statline(char *buf, size_t bufsiz, struct cgpu_info *cgpu) cgpu->drv->get_statline(buf, bufsiz, cgpu); } -static void text_print_status(int thr_id) +static bool shared_strategy(void) { - struct cgpu_info *cgpu; - char logline[256]; - - cgpu = get_thr_cgpu(thr_id); - if (cgpu) { - get_statline(logline, sizeof(logline), cgpu); - printf("%s\n", logline); - } + return (pool_strategy == POOL_LOADBALANCE || pool_strategy == POOL_BALANCE); } #ifdef HAVE_CURSES @@ -2136,19 +2936,31 @@ static void text_print_status(int thr_id) static void curses_print_status(void) { struct pool *pool = current_pool(); + int linewidth = opt_widescreen ? 100 : 80; wattron(statuswin, A_BOLD); cg_mvwprintw(statuswin, 0, 0, " " PACKAGE " version " VERSION " - Started: %s", datestamp); wattroff(statuswin, A_BOLD); - mvwhline(statuswin, 1, 0, '-', 80); + mvwhline(statuswin, 1, 0, '-', linewidth); cg_mvwprintw(statuswin, 2, 0, " %s", statusline); wclrtoeol(statuswin); - cg_mvwprintw(statuswin, 3, 0, " ST: %d SS: %d NB: %d LW: %d GF: %d RF: %d", - total_staged(), total_stale, new_blocks, - local_work, total_go, total_ro); + if (opt_widescreen) { + cg_mvwprintw(statuswin, 3, 0, " A:%.0f R:%.0f HW:%d WU:%.1f/m |" + " ST: %d SS: %"PRId64" NB: %d LW: %d GF: %d RF: %d", + total_diff_accepted, total_diff_rejected, hw_errors, + total_diff1 / total_secs * 60, + total_staged(), total_stale, new_blocks, local_work, total_go, total_ro); + } else if (alt_status) { + cg_mvwprintw(statuswin, 3, 0, " ST: %d SS: %"PRId64" NB: %d LW: %d GF: %d RF: %d", + total_staged(), total_stale, new_blocks, local_work, total_go, total_ro); + } else { + cg_mvwprintw(statuswin, 3, 0, " A:%.0f R:%.0f HW:%d WU:%.1f/m", + total_diff_accepted, total_diff_rejected, hw_errors, + total_diff1 / total_secs * 60); + } wclrtoeol(statuswin); - if ((pool_strategy == POOL_LOADBALANCE || pool_strategy == POOL_BALANCE) && total_pools > 1) { - cg_mvwprintw(statuswin, 4, 0, " Connected to multiple pools with%s LP", + if (shared_strategy() && total_pools > 1) { + cg_mvwprintw(statuswin, 4, 0, " Connected to multiple pools with%s block change notify", have_longpoll ? "": "out"); } else if (pool->has_stratum) { cg_mvwprintw(statuswin, 4, 0, " Connected to %s diff %s with stratum as user %s", @@ -2160,11 +2972,14 @@ static void curses_print_status(void) } wclrtoeol(statuswin); cg_mvwprintw(statuswin, 5, 0, " Block: %s... Diff:%s Started: %s Best share: %s ", - current_hash, block_diff, blocktime, best_share); - mvwhline(statuswin, 6, 0, '-', 80); - mvwhline(statuswin, statusy - 1, 0, '-', 80); - cg_mvwprintw(statuswin, devcursor - 1, 1, "[P]ool management %s[S]ettings [D]isplay options [Q]uit", - have_opencl ? "[G]PU management " : ""); + prev_block, block_diff, blocktime, best_share); + mvwhline(statuswin, 6, 0, '-', linewidth); + mvwhline(statuswin, statusy - 1, 0, '-', linewidth); +#ifdef USE_USBUTILS + cg_mvwprintw(statuswin, devcursor - 1, 1, "[U]SB management [P]ool management [S]ettings [D]isplay options [Q]uit"); +#else + cg_mvwprintw(statuswin, devcursor - 1, 1, "[P]ool management [S]ettings [D]isplay options [Q]uit"); +#endif } static void adj_width(int var, int *length) @@ -2179,16 +2994,16 @@ static void adj_fwidth(float var, int *length) (*length)++; } -static int dev_width; +#define STATBEFORELEN 23 +const char blanks[] = " "; -static void curses_print_devstatus(struct cgpu_info *cgpu, int count) +static void curses_print_devstatus(struct cgpu_info *cgpu, int devno, int count) { - static int dawidth = 1, drwidth = 1, hwwidth = 1, wuwidth = 1; - char logline[256]; - char displayed_hashes[16], displayed_rolling[16]; - uint64_t dh64, dr64; + static int devno_width = 1, dawidth = 1, drwidth = 1, hwwidth = 1, wuwidth = 1; + char logline[256], unique_id[12]; struct timeval now; double dev_runtime, wu; + unsigned int devstatlen; if (opt_compact) return; @@ -2213,15 +3028,22 @@ static void curses_print_devstatus(struct cgpu_info *cgpu, int count) wu = cgpu->diff1 / dev_runtime * 60; wmove(statuswin,devcursor + count, 0); - cg_wprintw(statuswin, " %s %*d: ", cgpu->drv->name, dev_width, cgpu->device_id); + adj_width(devno, &devno_width); + if (cgpu->unique_id) { + unique_id[8] = '\0'; + memcpy(unique_id, blanks, 8); + strncpy(unique_id, cgpu->unique_id, 8); + } else + sprintf(unique_id, "%-8d", cgpu->device_id); + cg_wprintw(statuswin, " %*d: %s %-8s: ", devno_width, devno, cgpu->drv->name, + unique_id); logline[0] = '\0'; cgpu->drv->get_statline_before(logline, sizeof(logline), cgpu); - cg_wprintw(statuswin, "%s", logline); + devstatlen = strlen(logline); + if (devstatlen < STATBEFORELEN) + strncat(logline, blanks, STATBEFORELEN - devstatlen); + cg_wprintw(statuswin, "%s | ", logline); - dh64 = (double)cgpu->total_mhashes / dev_runtime * 1000000ull; - dr64 = (double)cgpu->rolling * 1000000ull; - suffix_string(dh64, displayed_hashes, sizeof(displayed_hashes), 4); - suffix_string(dr64, displayed_rolling, sizeof(displayed_rolling), 4); #ifdef USE_USBUTILS if (cgpu->usbinfo.nodev) @@ -2236,19 +3058,45 @@ static void curses_print_devstatus(struct cgpu_info *cgpu, int count) cg_wprintw(statuswin, "OFF "); else if (cgpu->deven == DEV_RECOVER) cg_wprintw(statuswin, "REST "); - else - cg_wprintw(statuswin, "%6s", displayed_rolling); - adj_fwidth(cgpu->diff_accepted, &dawidth); - adj_fwidth(cgpu->diff_rejected, &drwidth); - adj_width(cgpu->hw_errors, &hwwidth); - adj_width(wu, &wuwidth); - - cg_wprintw(statuswin, "/%6sh/s | A:%*.0f R:%*.0f HW:%*d WU:%*.1f/m", - displayed_hashes, - dawidth, cgpu->diff_accepted, - drwidth, cgpu->diff_rejected, - hwwidth, cgpu->hw_errors, - wuwidth + 2, wu); + else if (opt_widescreen) { + char displayed_hashes[16], displayed_rolling[16]; + uint64_t d64; + + d64 = (double)cgpu->total_mhashes / dev_runtime * 1000000ull; + suffix_string(d64, displayed_hashes, sizeof(displayed_hashes), 4); + d64 = (double)cgpu->rolling * 1000000ull; + suffix_string(d64, displayed_rolling, sizeof(displayed_rolling), 4); + adj_width(wu, &wuwidth); + adj_fwidth(cgpu->diff_accepted, &dawidth); + adj_fwidth(cgpu->diff_rejected, &drwidth); + adj_width(cgpu->hw_errors, &hwwidth); + cg_wprintw(statuswin, "%6s / %6sh/s WU:%*.1f/m " + "A:%*.0f R:%*.0f HW:%*d", + displayed_rolling, + displayed_hashes, wuwidth + 2, wu, + dawidth, cgpu->diff_accepted, + drwidth, cgpu->diff_rejected, + hwwidth, cgpu->hw_errors); + } else if (!alt_status) { + char displayed_hashes[16], displayed_rolling[16]; + uint64_t d64; + + d64 = (double)cgpu->total_mhashes / dev_runtime * 1000000ull; + suffix_string(d64, displayed_hashes, sizeof(displayed_hashes), 4); + d64 = (double)cgpu->rolling * 1000000ull; + suffix_string(d64, displayed_rolling, sizeof(displayed_rolling), 4); + adj_width(wu, &wuwidth); + cg_wprintw(statuswin, "%6s / %6sh/s WU:%*.1f/m", displayed_rolling, + displayed_hashes, wuwidth + 2, wu); + } else { + adj_fwidth(cgpu->diff_accepted, &dawidth); + adj_fwidth(cgpu->diff_rejected, &drwidth); + adj_width(cgpu->hw_errors, &hwwidth); + cg_wprintw(statuswin, "A:%*.0f R:%*.0f HW:%*d", + dawidth, cgpu->diff_accepted, + drwidth, cgpu->diff_rejected, + hwwidth, cgpu->hw_errors); + } logline[0] = '\0'; cgpu->drv->get_statline(logline, sizeof(logline), cgpu); @@ -2258,12 +3106,6 @@ static void curses_print_devstatus(struct cgpu_info *cgpu, int count) } #endif -static void print_status(int thr_id) -{ - if (!curses_active) - text_print_status(thr_id); -} - #ifdef HAVE_CURSES /* Check for window resize. Called with curses mutex locked */ static inline void change_logwinsize(void) @@ -2304,7 +3146,7 @@ static void check_winsizes(void) statusy = LINES - 2; else statusy = logstart; - logcursor = statusy + 1; + logcursor = statusy; wresize(statuswin, statusy, x); getmaxyx(mainwin, y, x); y -= logcursor; @@ -2354,10 +3196,6 @@ void _wlogprint(const char *str) unlock_curses(); } } -#else -static void switch_logsize(bool __maybe_unused newdevs) -{ -} #endif #ifdef HAVE_CURSES @@ -2464,7 +3302,7 @@ share_result(json_t *val, json_t *res, json_t *err, const struct work *work, hashshow, cgpu->drv->name, cgpu->device_id, resubmit ? "(resubmit)" : "", worktime); } sharelog("accept", work); - if (opt_shares && total_accepted >= opt_shares) { + if (opt_shares && total_diff_accepted >= opt_shares) { applog(LOG_WARNING, "Successfully mined %d accepted shares as requested and exiting.", opt_shares); kill_work(); return; @@ -2519,13 +3357,18 @@ share_result(json_t *val, json_t *res, json_t *err, const struct work *work, reason[reasonLen + 2] = ')'; reason[reasonLen + 3] = '\0'; memcpy(disposition + 7, reasontmp, reasonLen); disposition[6] = ':'; disposition[reasonLen + 7] = '\0'; - } else if (work->stratum && err && json_is_array(err)) { - json_t *reason_val = json_array_get(err, 1); - char *reason_str; - - if (reason_val && json_is_string(reason_val)) { - reason_str = (char *)json_string_value(reason_val); - snprintf(reason, 31, " (%s)", reason_str); + } else if (work->stratum && err) { + if (json_is_array(err)) { + json_t *reason_val = json_array_get(err, 1); + char *reason_str; + + if (reason_val && json_is_string(reason_val)) { + reason_str = (char *)json_string_value(reason_val); + snprintf(reason, 31, " (%s)", reason_str); + } + } else if (json_is_string(err)) { + const char *s = json_string_value(err); + snprintf(reason, 31, " (%s)", s); } } @@ -2543,7 +3386,7 @@ share_result(json_t *val, json_t *res, json_t *err, const struct work *work, if (pool->seq_rejects > 10 && !work->stale && opt_disable_pool && enabled_pools > 1) { double utility = total_accepted / total_secs * 60; - if (pool->seq_rejects > utility * 3) { + if (pool->seq_rejects > utility * 3 && enabled_pools > 1) { applog(LOG_WARNING, "Pool %d rejected %d sequential shares, disabling!", pool->pool_no, pool->seq_rejects); reject_pool(pool); @@ -2555,9 +3398,49 @@ share_result(json_t *val, json_t *res, json_t *err, const struct work *work, } } +static void show_hash(struct work *work, char *hashshow) +{ + unsigned char rhash[32]; + char diffdisp[16]; + unsigned long h32; + uint32_t *hash32; + uint64_t uintdiff; + int ofs; + + swab256(rhash, work->hash); + for (ofs = 0; ofs <= 28; ofs ++) { + if (rhash[ofs]) + break; + } + hash32 = (uint32_t *)(rhash + ofs); + h32 = be32toh(*hash32); + uintdiff = round(work->work_difficulty); + suffix_string(work->share_diff, diffdisp, sizeof (diffdisp), 0); + snprintf(hashshow, 64, "%08lx Diff %s/%"PRIu64"%s", h32, diffdisp, uintdiff, + work->block? " BLOCK!" : ""); +} + +#ifdef HAVE_LIBCURL +static void text_print_status(int thr_id) +{ + struct cgpu_info *cgpu; + char logline[256]; + + cgpu = get_thr_cgpu(thr_id); + if (cgpu) { + get_statline(logline, sizeof(logline), cgpu); + printf("%s\n", logline); + } +} + +static void print_status(int thr_id) +{ + if (!curses_active) + text_print_status(thr_id); +} + static bool submit_upstream_work(struct work *work, CURL *curl, bool resubmit) { - char *hexstr = NULL; json_t *val, *res, *err; char *s; bool rc = false; @@ -2573,51 +3456,60 @@ static bool submit_upstream_work(struct work *work, CURL *curl, bool resubmit) cgpu = get_thr_cgpu(thr_id); - endian_flip128(work->data, work->data); - - /* build hex string */ - hexstr = bin2hex(work->data, sizeof(work->data)); - /* build JSON-RPC request */ if (work->gbt) { - char *gbt_block, *varint; + char gbt_block[1024], varint[12]; unsigned char data[80]; flip80(data, work->data); - gbt_block = bin2hex(data, 80); + __bin2hex(gbt_block, data, 80); // 160 length if (work->gbt_txns < 0xfd) { - uint8_t val = work->gbt_txns; + uint8_t val8 = work->gbt_txns; - varint = bin2hex((const unsigned char *)&val, 1); + __bin2hex(varint, (const unsigned char *)&val8, 1); } else if (work->gbt_txns <= 0xffff) { - uint16_t val = htole16(work->gbt_txns); + uint16_t val16 = htole16(work->gbt_txns); - gbt_block = realloc_strcat(gbt_block, "fd"); - varint = bin2hex((const unsigned char *)&val, 2); + strcat(gbt_block, "fd"); // +2 + __bin2hex(varint, (const unsigned char *)&val16, 2); } else { - uint32_t val = htole32(work->gbt_txns); + uint32_t val32 = htole32(work->gbt_txns); - gbt_block = realloc_strcat(gbt_block, "fe"); - varint = bin2hex((const unsigned char *)&val, 4); + strcat(gbt_block, "fe"); // +2 + __bin2hex(varint, (const unsigned char *)&val32, 4); } - gbt_block = realloc_strcat(gbt_block, varint); - free(varint); - gbt_block = realloc_strcat(gbt_block, work->coinbase); + strcat(gbt_block, varint); // +8 max + strcat(gbt_block, work->coinbase); - s = strdup("{\"id\": 0, \"method\": \"submitblock\", \"params\": [\""); - s = realloc_strcat(s, gbt_block); - if (work->job_id) { - s = realloc_strcat(s, "\", {\"workid\": \""); + s = malloc(1024); + if (unlikely(!s)) + quit(1, "Failed to malloc s in submit_upstream_work"); + sprintf(s, "{\"id\": 0, \"method\": \"submitblock\", \"params\": [\"%s", gbt_block); + /* Has submit/coinbase support */ + if (!pool->has_gbt) { + cg_rlock(&pool->gbt_lock); + if (pool->txn_data) + s = realloc_strcat(s, pool->txn_data); + cg_runlock(&pool->gbt_lock); + } + if (work->job_id) { + s = realloc_strcat(s, "\", {\"workid\": \""); s = realloc_strcat(s, work->job_id); s = realloc_strcat(s, "\"}]}"); } else - s = realloc_strcat(s, "\", {}]}"); - free(gbt_block); + s = realloc_strcat(s, "\"]}"); } else { + char *hexstr; + + endian_flip128(work->data, work->data); + + /* build hex string */ + hexstr = bin2hex(work->data, 118); s = strdup("{\"method\": \"getwork\", \"params\": [ \""); s = realloc_strcat(s, hexstr); s = realloc_strcat(s, "\" ], \"id\":1}"); + free(hexstr); } applog(LOG_DEBUG, "DBG: sending %s submit RPC call: %s", pool->rpc_url, s); s = realloc_strcat(s, "\n"); @@ -2648,20 +3540,7 @@ static bool submit_upstream_work(struct work *work, CURL *curl, bool resubmit) err = json_object_get(val, "error"); if (!QUIET) { - int intdiff = floor(work->work_difficulty); - char diffdisp[16], *outhash; - unsigned char rhash[32]; - - swab256(rhash, work->hash); - if (opt_scrypt) - outhash = bin2hex(rhash + 2, 4); - else - outhash = bin2hex(rhash + 4, 4); - suffix_string(work->share_diff, diffdisp, sizeof(diffdisp), 0); - snprintf(hashshow, sizeof(hashshow), "%s Diff %s/%d%s", - outhash, diffdisp, intdiff, - work->block? " BLOCK!" : ""); - free(outhash); + show_hash(work, hashshow); if (opt_worktime) { char workclone[20]; @@ -2697,8 +3576,8 @@ static bool submit_upstream_work(struct work *work, CURL *curl, bool resubmit) snprintf(worktime, sizeof(worktime), " <-%08lx.%08lx M:%c D:%1.*f G:%02d:%02d:%02d:%1.3f %s (%1.3f) W:%1.3f (%1.3f) S:%1.3f R:%02d:%02d:%02d", - (unsigned long)swab32(*(uint32_t *)&(work->data[opt_scrypt ? 32 : 28])), - (unsigned long)swab32(*(uint32_t *)&(work->data[opt_scrypt ? 28 : 24])), + (unsigned long)be32toh(*(uint32_t *)&(work->data[28])), + (unsigned long)be32toh(*(uint32_t *)&(work->data[24])), work->getwork_mode, diffplaces, work->work_difficulty, tm_getwork.tm_hour, tm_getwork.tm_min, tm_getwork.tm_sec, getwork_time, workclone, @@ -2735,10 +3614,65 @@ static bool submit_upstream_work(struct work *work, CURL *curl, bool resubmit) rc = true; out: - free(hexstr); return rc; } +static bool get_upstream_work(struct work *work, CURL *curl) +{ + struct pool *pool = work->pool; + struct cgminer_pool_stats *pool_stats = &(pool->cgminer_pool_stats); + struct timeval tv_elapsed; + json_t *val = NULL; + bool rc = false; + char *url; + + url = pool->rpc_url; + + applog(LOG_DEBUG, "DBG: sending %s get RPC call: %s", url, pool->rpc_req); + + cgtime(&work->tv_getwork); + + val = json_rpc_call(curl, url, pool->rpc_userpass, pool->rpc_req, false, + false, &work->rolltime, pool, false); + pool_stats->getwork_attempts++; + + if (likely(val)) { + rc = work_decode(pool, work, val); + if (unlikely(!rc)) + applog(LOG_DEBUG, "Failed to decode work in get_upstream_work"); + } else + applog(LOG_DEBUG, "Failed json_rpc_call in get_upstream_work"); + + cgtime(&work->tv_getwork_reply); + timersub(&(work->tv_getwork_reply), &(work->tv_getwork), &tv_elapsed); + pool_stats->getwork_wait_rolling += ((double)tv_elapsed.tv_sec + ((double)tv_elapsed.tv_usec / 1000000)) * 0.63; + pool_stats->getwork_wait_rolling /= 1.63; + + timeradd(&tv_elapsed, &(pool_stats->getwork_wait), &(pool_stats->getwork_wait)); + if (timercmp(&tv_elapsed, &(pool_stats->getwork_wait_max), >)) { + pool_stats->getwork_wait_max.tv_sec = tv_elapsed.tv_sec; + pool_stats->getwork_wait_max.tv_usec = tv_elapsed.tv_usec; + } + if (timercmp(&tv_elapsed, &(pool_stats->getwork_wait_min), <)) { + pool_stats->getwork_wait_min.tv_sec = tv_elapsed.tv_sec; + pool_stats->getwork_wait_min.tv_usec = tv_elapsed.tv_usec; + } + pool_stats->getwork_calls++; + + work->pool = pool; + work->longpoll = false; + work->getwork_mode = GETWORK_MODE_POOL; + calc_diff(work, 0); + total_getworks++; + pool->getwork_requested++; + + if (likely(val)) + json_decref(val); + + return rc; +} +#endif /* HAVE_LIBCURL */ + /* Specifies whether we can use this pool for work or not. */ static bool pool_unworkable(struct pool *pool) { @@ -2776,77 +3710,145 @@ static struct pool *select_balanced(struct pool *cp) return ret; } -/* Select any active pool in a rotating fashion when loadbalance is chosen */ +static struct pool *priority_pool(int choice); +static bool pool_unusable(struct pool *pool); + +/* Select any active pool in a rotating fashion when loadbalance is chosen if + * it has any quota left. */ static inline struct pool *select_pool(bool lagging) { static int rotating_pool = 0; struct pool *pool, *cp; - int tested; + bool avail = false; + int tested, i; cp = current_pool(); - if (pool_strategy == POOL_BALANCE) - return select_balanced(cp); + if (pool_strategy == POOL_BALANCE) { + pool = select_balanced(cp); + goto out; + } - if (pool_strategy != POOL_LOADBALANCE && (!lagging || opt_fail_only)) + if (pool_strategy != POOL_LOADBALANCE && (!lagging || opt_fail_only)) { pool = cp; - else + goto out; + } else pool = NULL; + for (i = 0; i < total_pools; i++) { + struct pool *tp = pools[i]; + + if (tp->quota_used < tp->quota_gcd) { + avail = true; + break; + } + } + + /* There are no pools with quota, so reset them. */ + if (!avail) { + for (i = 0; i < total_pools; i++) + pools[i]->quota_used = 0; + if (++rotating_pool >= total_pools) + rotating_pool = 0; + } + /* Try to find the first pool in the rotation that is usable */ tested = 0; while (!pool && tested++ < total_pools) { - if (++rotating_pool >= total_pools) - rotating_pool = 0; pool = pools[rotating_pool]; - if (!pool_unworkable(pool)) - break; + if (pool->quota_used++ < pool->quota_gcd) { + if (!pool_unworkable(pool)) + break; + /* Failover-only flag for load-balance means distribute + * unused quota to priority pool 0. */ + if (opt_fail_only) + priority_pool(0)->quota_used--; + } pool = NULL; + if (++rotating_pool >= total_pools) + rotating_pool = 0; + } + + /* If there are no alive pools with quota, choose according to + * priority. */ + if (!pool) { + for (i = 0; i < total_pools; i++) { + struct pool *tp = priority_pool(i); + + if (!pool_unusable(tp)) { + pool = tp; + break; + } + } } + /* If still nothing is usable, use the current pool */ if (!pool) pool = cp; - +out: + applog(LOG_DEBUG, "Selecting pool %d for work", pool->pool_no); return pool; } -static double DIFFEXACTONE = 26959946667150639794667015087019630673637144422540572481103610249215.0; -static const uint64_t diffone = 0xFFFF000000000000ull; +/* truediffone == 0x00000000FFFF0000000000000000000000000000000000000000000000000000 + * Generate a 256 bit binary LE target by cutting up diff into 64 bit sized + * portions or vice versa. */ +static const double truediffone = 26959535291011309493156476344723991336010898738574164086137773096960.0; +static const double bits192 = 6277101735386680763835789423207666416102355444464034512896.0; +static const double bits128 = 340282366920938463463374607431768211456.0; +static const double bits64 = 18446744073709551616.0; + +/* Converts a little endian 256 bit value to a double */ +static double le256todouble(const void *target) +{ + uint64_t *data64; + double dcut64; + + data64 = (uint64_t *)(target + 24); + dcut64 = le64toh(*data64) * bits192; + + data64 = (uint64_t *)(target + 16); + dcut64 += le64toh(*data64) * bits128; + + data64 = (uint64_t *)(target + 8); + dcut64 += le64toh(*data64) * bits64; + + data64 = (uint64_t *)(target); + dcut64 += le64toh(*data64); + + return dcut64; +} + +static double diff_from_target(void *target) +{ + double d64, dcut64; + + d64 = truediffone; + dcut64 = le256todouble(target); + if (unlikely(!dcut64)) + dcut64 = 1; + return d64 / dcut64; +} /* - * Calculate the work share difficulty + * Calculate the work->work_difficulty based on the work->target */ -static void calc_diff(struct work *work, int known) +static void calc_diff(struct work *work, double known) { struct cgminer_pool_stats *pool_stats = &(work->pool->cgminer_pool_stats); double difficulty; + uint64_t uintdiff; - if (opt_scrypt) { - uint64_t *data64, d64; - char rtarget[32]; - - swab256(rtarget, work->target); - data64 = (uint64_t *)(rtarget + 2); - d64 = be64toh(*data64); - if (unlikely(!d64)) - d64 = 1; - work->work_difficulty = diffone / d64; - } else if (!known) { - double targ = 0; - int i; - - for (i = 31; i >= 0; i--) { - targ *= 256; - targ += work->target[i]; - } - - work->work_difficulty = DIFFEXACTONE / (targ ? : DIFFEXACTONE); - } else + if (known) work->work_difficulty = known; + else + work->work_difficulty = diff_from_target(work->target); + difficulty = work->work_difficulty; pool_stats->last_diff = difficulty; - suffix_string((uint64_t)difficulty, work->pool->diff, sizeof(work->pool->diff), 0); + uintdiff = round(difficulty); + suffix_string(uintdiff, work->pool->diff, sizeof(work->pool->diff), 0); if (difficulty == pool_stats->min_diff) pool_stats->min_diff_count++; @@ -2863,77 +3865,170 @@ static void calc_diff(struct work *work, int known) } } +static unsigned char bench_hidiff_bins[16][160]; +static unsigned char bench_lodiff_bins[16][160]; +static unsigned char bench_target[32]; + +/* Iterate over the lo and hi diff benchmark work items such that we find one + * diff 32+ share every 32 work items. */ static void get_benchmark_work(struct work *work) { - // Use a random work block pulled from a pool - static uint8_t bench_block[] = { CGMINER_BENCHMARK_BLOCK }; - - size_t bench_size = sizeof(*work); - size_t work_size = sizeof(bench_block); - size_t min_size = (work_size < bench_size ? work_size : bench_size); - memset(work, 0, sizeof(*work)); - memcpy(work, &bench_block, min_size); + work->work_difficulty = 32; + memcpy(work->target, bench_target, 32); + work->drv_rolllimit = 0; work->mandatory = true; work->pool = pools[0]; cgtime(&work->tv_getwork); copy_time(&work->tv_getwork_reply, &work->tv_getwork); work->getwork_mode = GETWORK_MODE_BENCHMARK; - calc_diff(work, 0); } -static bool get_upstream_work(struct work *work, CURL *curl) +static void benchfile_dspwork(struct work *work, uint32_t nonce) { - struct pool *pool = work->pool; - struct cgminer_pool_stats *pool_stats = &(pool->cgminer_pool_stats); - struct timeval tv_elapsed; - json_t *val = NULL; - bool rc = false; - char *url; + char buf[1024]; + uint32_t dn; + int i; - applog(LOG_DEBUG, "DBG: sending %s get RPC call: %s", pool->rpc_url, pool->rpc_req); + dn = 0; + for (i = 0; i < 4; i++) { + dn *= 0x100; + dn += nonce & 0xff; + nonce /= 0x100; + } - url = pool->rpc_url; + if ((sizeof(work->data) * 2 + 1) > sizeof(buf)) + quithere(1, "BENCHFILE Invalid buf size"); - cgtime(&work->tv_getwork); + __bin2hex(buf, work->data, sizeof(work->data)); - val = json_rpc_call(curl, url, pool->rpc_userpass, pool->rpc_req, false, - false, &work->rolltime, pool, false); - pool_stats->getwork_attempts++; + applog(LOG_ERR, "BENCHFILE nonce %u=0x%08x for work=%s", + (unsigned int)dn, (unsigned int)dn, buf); - if (likely(val)) { - rc = work_decode(pool, work, val); - if (unlikely(!rc)) - applog(LOG_DEBUG, "Failed to decode work in get_upstream_work"); - } else - applog(LOG_DEBUG, "Failed json_rpc_call in get_upstream_work"); +} - cgtime(&work->tv_getwork_reply); - timersub(&(work->tv_getwork_reply), &(work->tv_getwork), &tv_elapsed); - pool_stats->getwork_wait_rolling += ((double)tv_elapsed.tv_sec + ((double)tv_elapsed.tv_usec / 1000000)) * 0.63; - pool_stats->getwork_wait_rolling /= 1.63; +static bool benchfile_get_work(struct work *work) +{ + char buf[1024]; + char item[1024]; + bool got = false; - timeradd(&tv_elapsed, &(pool_stats->getwork_wait), &(pool_stats->getwork_wait)); - if (timercmp(&tv_elapsed, &(pool_stats->getwork_wait_max), >)) { - pool_stats->getwork_wait_max.tv_sec = tv_elapsed.tv_sec; - pool_stats->getwork_wait_max.tv_usec = tv_elapsed.tv_usec; + if (!benchfile_in) { + if (opt_benchfile) + benchfile_in = fopen(opt_benchfile, "r"); + else + quit(1, "BENCHFILE Invalid benchfile NULL"); + + if (!benchfile_in) + quit(1, "BENCHFILE Failed to open benchfile '%s'", opt_benchfile); + + benchfile_line = 0; + + if (!fgets(buf, 1024, benchfile_in)) + quit(1, "BENCHFILE Failed to read benchfile '%s'", opt_benchfile); + + got = true; + benchfile_work = 0; } - if (timercmp(&tv_elapsed, &(pool_stats->getwork_wait_min), <)) { - pool_stats->getwork_wait_min.tv_sec = tv_elapsed.tv_sec; - pool_stats->getwork_wait_min.tv_usec = tv_elapsed.tv_usec; + + if (!got) { + if (!fgets(buf, 1024, benchfile_in)) { + if (benchfile_work == 0) + quit(1, "BENCHFILE No work in benchfile '%s'", opt_benchfile); + fclose(benchfile_in); + benchfile_in = NULL; + return benchfile_get_work(work); + } } - pool_stats->getwork_calls++; - work->pool = pool; - work->longpoll = false; - work->getwork_mode = GETWORK_MODE_POOL; - calc_diff(work, 0); - total_getworks++; - pool->getwork_requested++; + do { + benchfile_line++; + + // Empty lines and lines starting with '#' or '/' are ignored + if (*buf != '\0' && *buf != '#' && *buf != '/') { + char *commas[BENCHWORK_COUNT]; + int i, j, len; + long nonce_time; + + commas[0] = buf; + for (i = 1; i < BENCHWORK_COUNT; i++) { + commas[i] = strchr(commas[i-1], ','); + if (!commas[i]) { + quit(1, "BENCHFILE Invalid input file line %d" + " - field count is %d but should be %d", + benchfile_line, i, BENCHWORK_COUNT); + } + len = commas[i] - commas[i-1]; + if (benchfile_data[i-1].length && + (len != benchfile_data[i-1].length)) { + quit(1, "BENCHFILE Invalid input file line %d " + "field %d (%s) length is %d but should be %d", + benchfile_line, i, + benchfile_data[i-1].name, + len, benchfile_data[i-1].length); + } - if (likely(val)) - json_decref(val); + *(commas[i]++) = '\0'; + } - return rc; + // NonceTime may have LF's etc + len = strlen(commas[BENCHWORK_NONCETIME]); + if (len < benchfile_data[BENCHWORK_NONCETIME].length) { + quit(1, "BENCHFILE Invalid input file line %d field %d" + " (%s) length is %d but should be least %d", + benchfile_line, BENCHWORK_NONCETIME+1, + benchfile_data[BENCHWORK_NONCETIME].name, len, + benchfile_data[BENCHWORK_NONCETIME].length); + } + + sprintf(item, "0000000%c", commas[BENCHWORK_VERSION][0]); + + j = strlen(item); + for (i = benchfile_data[BENCHWORK_PREVHASH].length-8; i >= 0; i -= 8) { + sprintf(&(item[j]), "%.8s", &commas[BENCHWORK_PREVHASH][i]); + j += 8; + } + + for (i = benchfile_data[BENCHWORK_MERKLEROOT].length-8; i >= 0; i -= 8) { + sprintf(&(item[j]), "%.8s", &commas[BENCHWORK_MERKLEROOT][i]); + j += 8; + } + + nonce_time = atol(commas[BENCHWORK_NONCETIME]); + + sprintf(&(item[j]), "%08lx", nonce_time); + j += 8; + + strcpy(&(item[j]), commas[BENCHWORK_DIFFBITS]); + j += benchfile_data[BENCHWORK_DIFFBITS].length; + + memset(work, 0, sizeof(*work)); + + hex2bin(work->data, item, j >> 1); + + calc_midstate(work); + + benchfile_work++; + + return true; + } + } while (fgets(buf, 1024, benchfile_in)); + + if (benchfile_work == 0) + quit(1, "BENCHFILE No work in benchfile '%s'", opt_benchfile); + fclose(benchfile_in); + benchfile_in = NULL; + return benchfile_get_work(work); +} + +static void get_benchfile_work(struct work *work) +{ + benchfile_get_work(work); + work->mandatory = true; + work->pool = pools[0]; + cgtime(&work->tv_getwork); + copy_time(&work->tv_getwork_reply, &work->tv_getwork); + work->getwork_mode = GETWORK_MODE_BENCHMARK; + calc_diff(work, 0); } #ifdef HAVE_CURSES @@ -2948,9 +4043,22 @@ static void disable_curses_windows(void) delwin(statuswin); } +/* Force locking of curses console_lock on shutdown since a dead thread might + * have grabbed the lock. */ +static bool curses_active_forcelocked(void) +{ + bool ret; + + mutex_trylock(&console_lock); + ret = curses_active; + if (!ret) + unlock_curses(); + return ret; +} + static void disable_curses(void) { - if (curses_active_locked()) { + if (curses_active_forcelocked()) { use_curses = false; curses_active = false; disable_curses_windows(); @@ -2973,6 +4081,35 @@ static void disable_curses(void) } #endif +static void kill_timeout(struct thr_info *thr) +{ + cg_completion_timeout(&thr_info_cancel, thr, 1000); +} + +static void kill_mining(void) +{ + struct thr_info *thr; + int i; + + forcelog(LOG_DEBUG, "Killing off mining threads"); + /* Kill the mining threads*/ + for (i = 0; i < mining_threads; i++) { + pthread_t *pth = NULL; + + thr = get_thread(i); + if (thr && PTH(thr) != 0L) + pth = &thr->pth; + thr_info_cancel(thr); +#ifndef WIN32 + if (pth && *pth) + pthread_join(*pth, NULL); +#else + if (pth && pth->p) + pthread_join(*pth, NULL); +#endif + } +} + static void __kill_work(void) { struct thr_info *thr; @@ -2981,29 +4118,27 @@ static void __kill_work(void) if (!successful_connect) return; - applog(LOG_INFO, "Received kill message"); + forcelog(LOG_INFO, "Received kill message"); #ifdef USE_USBUTILS /* Best to get rid of it first so it doesn't * try to create any new devices */ - if (!opt_scrypt) { - applog(LOG_DEBUG, "Killing off HotPlug thread"); - thr = &control_thr[hotplug_thr_id]; - thr_info_cancel(thr); - } + forcelog(LOG_DEBUG, "Killing off HotPlug thread"); + thr = &control_thr[hotplug_thr_id]; + kill_timeout(thr); #endif - applog(LOG_DEBUG, "Killing off watchpool thread"); + forcelog(LOG_DEBUG, "Killing off watchpool thread"); /* Kill the watchpool thread */ thr = &control_thr[watchpool_thr_id]; - thr_info_cancel(thr); + kill_timeout(thr); - applog(LOG_DEBUG, "Killing off watchdog thread"); + forcelog(LOG_DEBUG, "Killing off watchdog thread"); /* Kill the watchdog thread */ thr = &control_thr[watchdog_thr_id]; - thr_info_cancel(thr); + kill_timeout(thr); - applog(LOG_DEBUG, "Shutting down mining threads"); + forcelog(LOG_DEBUG, "Shutting down mining threads"); for (i = 0; i < mining_threads; i++) { struct cgpu_info *cgpu; @@ -3019,44 +4154,22 @@ static void __kill_work(void) sleep(1); - applog(LOG_DEBUG, "Killing off mining threads"); - /* Kill the mining threads*/ - for (i = 0; i < mining_threads; i++) { - pthread_t *pth = NULL; - - thr = get_thread(i); - if (thr && PTH(thr) != 0L) - pth = &thr->pth; - thr_info_cancel(thr); -#ifndef WIN32 - if (pth && *pth) - pthread_join(*pth, NULL); -#else - if (pth && pth->p) - pthread_join(*pth, NULL); -#endif - } + cg_completion_timeout(&kill_mining, NULL, 3000); - applog(LOG_DEBUG, "Killing off stage thread"); /* Stop the others */ - thr = &control_thr[stage_thr_id]; - thr_info_cancel(thr); - - applog(LOG_DEBUG, "Killing off API thread"); + forcelog(LOG_DEBUG, "Killing off API thread"); thr = &control_thr[api_thr_id]; - thr_info_cancel(thr); + kill_timeout(thr); #ifdef USE_USBUTILS /* Release USB resources in case it's a restart * and not a QUIT */ - if (!opt_scrypt) { - applog(LOG_DEBUG, "Releasing all USB devices"); - usb_cleanup(); + forcelog(LOG_DEBUG, "Releasing all USB devices"); + cg_completion_timeout(&usb_cleanup, NULL, 1000); - applog(LOG_DEBUG, "Killing off usbres thread"); - thr = &control_thr[usbres_thr_id]; - thr_info_cancel(thr); - } + forcelog(LOG_DEBUG, "Killing off usbres thread"); + thr = &control_thr[usbres_thr_id]; + kill_timeout(thr); #endif } @@ -3064,7 +4177,7 @@ static void __kill_work(void) /* This should be the common exit path */ void kill_work(void) { - __kill_work(); + cg_completion_timeout(&__kill_work, NULL, 5000); quit(0, "Shutdown signal received."); } @@ -3075,14 +4188,14 @@ const #endif char **initial_args; -static void clean_up(void); +static void clean_up(bool restarting); void app_restart(void) { applog(LOG_WARNING, "Attempting to restart %s", packagename); - __kill_work(); - clean_up(); + cg_completion_timeout(&__kill_work, NULL, 5000); + clean_up(true); #if defined(unix) || defined(__APPLE__) if (forkpid > 0) { @@ -3103,6 +4216,63 @@ static void sighandler(int __maybe_unused sig) kill_work(); } +static void _stage_work(struct work *work); + +#define stage_work(WORK) do { \ + _stage_work(WORK); \ + WORK = NULL; \ +} while (0) + +/* Adjust an existing char ntime field with a relative noffset */ +static void modify_ntime(char *ntime, int noffset) +{ + unsigned char bin[4]; + uint32_t h32, *be32 = (uint32_t *)bin; + + hex2bin(bin, ntime, 4); + h32 = be32toh(*be32) + noffset; + *be32 = htobe32(h32); + __bin2hex(ntime, bin, 4); +} + +void roll_work(struct work *work) +{ + uint32_t *work_ntime; + uint32_t ntime; + + work_ntime = (uint32_t *)(work->data + 68); + ntime = be32toh(*work_ntime); + ntime++; + *work_ntime = htobe32(ntime); + local_work++; + work->rolls++; + work->nonce = 0; + applog(LOG_DEBUG, "Successfully rolled work"); + /* Change the ntime field if this is stratum work */ + if (work->ntime) + modify_ntime(work->ntime, 1); + + /* This is now a different work item so it needs a different ID for the + * hashtable */ + work->id = total_work_inc(); +} + +struct work *make_clone(struct work *work) +{ + struct work *work_clone = copy_work(work); + + work_clone->clone = true; + cgtime((struct timeval *)&(work_clone->tv_cloned)); + work_clone->longpoll = false; + work_clone->mandatory = false; + /* Make cloned work appear slightly older to bias towards keeping the + * master work item which can be further rolled */ + work_clone->tv_staged.tv_sec -= 1; + + return work_clone; +} + +#ifdef HAVE_LIBCURL /* Called with pool_lock held. Recruit an extra curl if none are available for * this pool. */ static void recruit_curl(struct pool *pool) @@ -3184,7 +4354,7 @@ static inline bool should_roll(struct work *work) cgtime(&now); if (now.tv_sec - work->tv_staged.tv_sec > expiry) return false; - + return true; } @@ -3196,74 +4366,49 @@ static inline bool can_roll(struct work *work) work->rolls < 7000 && !stale_work(work, false)); } -static void roll_work(struct work *work) +static void *submit_work_thread(void *userdata) { - uint32_t *work_ntime; - uint32_t ntime; - - work_ntime = (uint32_t *)(work->data + 68); - ntime = be32toh(*work_ntime); - ntime++; - *work_ntime = htobe32(ntime); - local_work++; - work->rolls++; - work->blk.nonce = 0; - applog(LOG_DEBUG, "Successfully rolled work"); + struct work *work = (struct work *)userdata; + struct pool *pool = work->pool; + bool resubmit = false; + struct curl_ent *ce; - /* This is now a different work item so it needs a different ID for the - * hashtable */ - work->id = total_work++; -} + pthread_detach(pthread_self()); -/* Duplicates any dynamically allocated arrays within the work struct to - * prevent a copied work struct from freeing ram belonging to another struct */ -void __copy_work(struct work *work, struct work *base_work) -{ - int id = work->id; + RenameThread("SubmitWork"); - clean_work(work); - memcpy(work, base_work, sizeof(struct work)); - /* Keep the unique new id assigned during make_work to prevent copied - * work from having the same id. */ - work->id = id; - if (base_work->job_id) - work->job_id = strdup(base_work->job_id); - if (base_work->nonce1) - work->nonce1 = strdup(base_work->nonce1); - if (base_work->ntime) - work->ntime = strdup(base_work->ntime); - if (base_work->coinbase) - work->coinbase = strdup(base_work->coinbase); -} - -/* Generates a copy of an existing work struct, creating fresh heap allocations - * for all dynamically allocated arrays within the struct */ -struct work *copy_work(struct work *base_work) -{ - struct work *work = make_work(); + applog(LOG_DEBUG, "Creating extra submit work thread"); - __copy_work(work, base_work); + ce = pop_curl_entry(pool); + /* submit solution to bitcoin via JSON-RPC */ + while (!submit_upstream_work(work, ce->curl, resubmit)) { + if (opt_lowmem) { + applog(LOG_NOTICE, "Pool %d share being discarded to minimise memory cache", pool->pool_no); + break; + } + resubmit = true; + if (stale_work(work, true)) { + applog(LOG_NOTICE, "Pool %d share became stale while retrying submit, discarding", pool->pool_no); - return work; -} + mutex_lock(&stats_lock); + total_stale++; + pool->stale_shares++; + total_diff_stale += work->work_difficulty; + pool->diff_stale += work->work_difficulty; + mutex_unlock(&stats_lock); -static struct work *make_clone(struct work *work) -{ - struct work *work_clone = copy_work(work); + free_work(work); + break; + } - work_clone->clone = true; - cgtime((struct timeval *)&(work_clone->tv_cloned)); - work_clone->longpoll = false; - work_clone->mandatory = false; - /* Make cloned work appear slightly older to bias towards keeping the - * master work item which can be further rolled */ - work_clone->tv_staged.tv_sec -= 1; + /* pause, then restart work-request loop */ + applog(LOG_INFO, "json_rpc_call failed on submit_work, retrying"); + } + push_curl_entry(ce, pool); - return work_clone; + return NULL; } -static void stage_work(struct work *work); - static bool clone_available(void) { struct work *work_clone = NULL, *work, *tmp; @@ -3293,7 +4438,126 @@ static bool clone_available(void) return cloned; } -static void pool_died(struct pool *pool) +/* Clones work by rolling it if possible, and returning a clone instead of the + * original work item which gets staged again to possibly be rolled again in + * the future */ +static struct work *clone_work(struct work *work) +{ + int mrs = mining_threads + opt_queue - total_staged(); + struct work *work_clone; + bool cloned; + + if (mrs < 1) + return work; + + cloned = false; + work_clone = make_clone(work); + while (mrs-- > 0 && can_roll(work) && should_roll(work)) { + applog(LOG_DEBUG, "Pushing rolled converted work to stage thread"); + stage_work(work_clone); + roll_work(work); + work_clone = make_clone(work); + /* Roll it again to prevent duplicates should this be used + * directly later on */ + roll_work(work); + cloned = true; + } + + if (cloned) { + stage_work(work); + return work_clone; + } + + free_work(work_clone); + + return work; +} + +#else /* HAVE_LIBCURL */ +static void *submit_work_thread(void __maybe_unused *userdata) +{ + pthread_detach(pthread_self()); + return NULL; +} +#endif /* HAVE_LIBCURL */ + +/* Return an adjusted ntime if we're submitting work that a device has + * internally offset the ntime. */ +static char *offset_ntime(const char *ntime, int noffset) +{ + unsigned char bin[4]; + uint32_t h32, *be32 = (uint32_t *)bin; + + hex2bin(bin, ntime, 4); + h32 = be32toh(*be32) + noffset; + *be32 = htobe32(h32); + + return bin2hex(bin, 4); +} + +/* Duplicates any dynamically allocated arrays within the work struct to + * prevent a copied work struct from freeing ram belonging to another struct */ +static void _copy_work(struct work *work, const struct work *base_work, int noffset) +{ + uint32_t id = work->id; + + clean_work(work); + memcpy(work, base_work, sizeof(struct work)); + /* Keep the unique new id assigned during make_work to prevent copied + * work from having the same id. */ + work->id = id; + if (base_work->job_id) + work->job_id = strdup(base_work->job_id); + if (base_work->nonce1) + work->nonce1 = strdup(base_work->nonce1); + if (base_work->ntime) { + /* If we are passed an noffset the binary work->data ntime and + * the work->ntime hex string need to be adjusted. */ + if (noffset) { + uint32_t *work_ntime = (uint32_t *)(work->data + 68); + uint32_t ntime = be32toh(*work_ntime); + + ntime += noffset; + *work_ntime = htobe32(ntime); + work->ntime = offset_ntime(base_work->ntime, noffset); + } else + work->ntime = strdup(base_work->ntime); + } else if (noffset) { + uint32_t *work_ntime = (uint32_t *)(work->data + 68); + uint32_t ntime = be32toh(*work_ntime); + + ntime += noffset; + *work_ntime = htobe32(ntime); + } + if (base_work->coinbase) + work->coinbase = strdup(base_work->coinbase); +} + +void set_work_ntime(struct work *work, int ntime) +{ + uint32_t *work_ntime = (uint32_t *)(work->data + 68); + + *work_ntime = htobe32(ntime); + if (work->ntime) { + free(work->ntime); + work->ntime = bin2hex((unsigned char *)work_ntime, 4); + } +} + +/* Generates a copy of an existing work struct, creating fresh heap allocations + * for all dynamically allocated arrays within the struct. noffset is used for + * when a driver has internally rolled the ntime, noffset is a relative value. + * The macro copy_work() calls this function with an noffset of 0. */ +struct work *copy_work_noffset(struct work *base_work, int noffset) +{ + struct work *work = make_work(); + + _copy_work(work, base_work, noffset); + + return work; +} + +void pool_died(struct pool *pool) { if (!pool_tset(pool, &pool->idle)) { cgtime(&pool->tv_idle); @@ -3312,7 +4576,7 @@ static bool stale_work(struct work *work, bool share) struct pool *pool; int getwork_delay; - if (opt_benchmark) + if (opt_benchmark || opt_benchfile) return false; if (work->work_block != work_block) { @@ -3373,21 +4637,18 @@ static bool stale_work(struct work *work, bool share) return false; } -static uint64_t share_diff(const struct work *work) +uint64_t share_diff(const struct work *work) { - uint64_t *data64, d64, ret; bool new_best = false; - char rhash[32]; + double d64, s64; + uint64_t ret; - swab256(rhash, work->hash); - if (opt_scrypt) - data64 = (uint64_t *)(rhash + 2); - else - data64 = (uint64_t *)(rhash + 4); - d64 = be64toh(*data64); - if (unlikely(!d64)) - d64 = 1; - ret = diffone / d64; + d64 = truediffone; + s64 = le256todouble(work->hash); + if (unlikely(!s64)) + s64 = 0; + + ret = round(d64 / s64); cg_wlock(&control_lock); if (unlikely(ret > best_diff)) { @@ -3405,6 +4666,23 @@ static uint64_t share_diff(const struct work *work) return ret; } +uint64_t share_ndiff(const struct work *work) +{ + double d64, s64; + uint64_t ret = 0; + + if(work != NULL) { + d64 = truediffone; + s64 = le256todouble(work->hash); + if (unlikely(!s64)) { + ret = 0; + } else { + ret = (d64 / s64); + } + } + return ret; +} + static void regen_hash(struct work *work) { uint32_t *data32 = (uint32_t *)(work->data); @@ -3414,71 +4692,11 @@ static void regen_hash(struct work *work) flip80(swap32, data32); sha256(swap, 80, hash1); - sha256(hash1, 32, (unsigned char *)(work->hash)); -} - -static void rebuild_hash(struct work *work) -{ - if (opt_scrypt) - scrypt_regenhash(work); - else - regen_hash(work); - - work->share_diff = share_diff(work); - if (unlikely(work->share_diff >= current_diff)) { - work->block = true; - work->pool->solved++; - found_blocks++; - work->mandatory = true; - applog(LOG_NOTICE, "Found block for pool %d!", work->pool->pool_no); - } -} - -static bool cnx_needed(struct pool *pool); - -static void *submit_work_thread(void *userdata) -{ - struct work *work = (struct work *)userdata; - struct pool *pool = work->pool; - bool resubmit = false; - struct curl_ent *ce; - - pthread_detach(pthread_self()); - - RenameThread("submit_work"); - - applog(LOG_DEBUG, "Creating extra submit work thread"); - - ce = pop_curl_entry(pool); - /* submit solution to bitcoin via JSON-RPC */ - while (!submit_upstream_work(work, ce->curl, resubmit)) { - if (opt_lowmem) { - applog(LOG_NOTICE, "Pool %d share being discarded to minimise memory cache", pool->pool_no); - break; - } - resubmit = true; - if (stale_work(work, true)) { - applog(LOG_NOTICE, "Pool %d share became stale while retrying submit, discarding", pool->pool_no); - - mutex_lock(&stats_lock); - total_stale++; - pool->stale_shares++; - total_diff_stale += work->work_difficulty; - pool->diff_stale += work->work_difficulty; - mutex_unlock(&stats_lock); - - free_work(work); - break; - } - - /* pause, then restart work-request loop */ - applog(LOG_INFO, "json_rpc_call failed on submit_work, retrying"); - } - push_curl_entry(ce, pool); - - return NULL; + sha256(hash1, 32, (unsigned char *)(work->hash)); } +static bool cnx_needed(struct pool *pool); + /* Find the pool that currently has the highest priority */ static struct pool *priority_pool(int choice) { @@ -3501,8 +4719,6 @@ static struct pool *priority_pool(int choice) return ret; } -static void clear_pool_work(struct pool *pool); - /* Specifies whether we can switch to this pool or not. */ static bool pool_unusable(struct pool *pool) { @@ -3535,7 +4751,7 @@ void switch_pools(struct pool *selected) } switch (pool_strategy) { - /* Both of these set to the master pool */ + /* All of these set to the master pool */ case POOL_BALANCE: case POOL_FAILOVER: case POOL_LOADBALANCE: @@ -3593,11 +4809,14 @@ void switch_pools(struct pool *selected) } -void discard_work(struct work *work) +void _discard_work(struct work *work) { if (!work->clone && !work->rolls && !work->mined) { - if (work->pool) + if (work->pool) { work->pool->discarded_work++; + work->pool->quota_used--; + work->pool->works--; + } total_discarded++; applog(LOG_DEBUG, "Discarded work"); } else @@ -3652,18 +4871,21 @@ int restart_wait(struct thr_info *thr, unsigned int mstime) mutex_lock(&restart_lock); if (thr->work_restart) - rc = ETIMEDOUT; + rc = 0; else rc = pthread_cond_timedwait(&restart_cond, &restart_lock, &abstime); mutex_unlock(&restart_lock); return rc; } - -static void restart_threads(void) + +static void *restart_thread(void __maybe_unused *arg) { struct pool *cp = current_pool(); - int i; + struct cgpu_info *cgpu; + int i, mt; + + pthread_detach(pthread_self()); /* Artificially set the lagging flag to avoid pool not providing work * fast enough messages after every long poll */ @@ -3673,33 +4895,75 @@ static void restart_threads(void) discard_stale(); rd_lock(&mining_thr_lock); - for (i = 0; i < mining_threads; i++) - mining_thr[i]->work_restart = true; + mt = mining_threads; rd_unlock(&mining_thr_lock); + for (i = 0; i < mt; i++) { + cgpu = mining_thr[i]->cgpu; + if (unlikely(!cgpu)) + continue; + if (cgpu->deven != DEV_ENABLED) + continue; + mining_thr[i]->work_restart = true; + flush_queue(cgpu); + cgpu->drv->flush_work(cgpu); + } + mutex_lock(&restart_lock); pthread_cond_broadcast(&restart_cond); mutex_unlock(&restart_lock); + +#ifdef USE_USBUTILS + /* Cancels any cancellable usb transfers. Flagged as such it means they + * are usualy waiting on a read result and it's safe to abort the read + * early. */ + cancel_usb_transfers(); +#endif + return NULL; +} + +/* In order to prevent a deadlock via the various drv->flush_work + * implementations we send the restart messages via a separate thread. */ +static void restart_threads(void) +{ + pthread_t rthread; + + cgtime(&restart_tv_start); + if (unlikely(pthread_create(&rthread, NULL, restart_thread, NULL))) + quit(1, "Failed to create restart thread"); } -static void set_curblock(char *hexstr, unsigned char *hash) +static void signal_work_update(void) { - unsigned char hash_swap[32]; - unsigned char block_hash_swap[32]; + int i; + + applog(LOG_INFO, "Work update message received"); + + cgtime(&update_tv_start); + rd_lock(&mining_thr_lock); + for (i = 0; i < mining_threads; i++) + mining_thr[i]->work_update = true; + rd_unlock(&mining_thr_lock); +} - strcpy(current_block, hexstr); - swap256(hash_swap, hash); - swap256(block_hash_swap, hash + 4); +static void set_curblock(char *hexstr, unsigned char *bedata) +{ + int ofs; cg_wlock(&ch_lock); cgtime(&block_timeval); - free(current_hash); - current_hash = bin2hex(hash_swap + 2, 8); - free(current_fullhash); - current_fullhash = bin2hex(block_hash_swap, 32); + strcpy(current_hash, hexstr); + memcpy(current_block, bedata, 32); get_timestamp(blocktime, sizeof(blocktime), &block_timeval); cg_wunlock(&ch_lock); + for (ofs = 0; ofs <= 56; ofs++) { + if (memcmp(¤t_hash[ofs], "0", 1)) + break; + } + strncpy(prev_block, ¤t_hash[ofs], 8); + prev_block[8] = '\0'; + applog(LOG_INFO, "New block: %s... diff %s", current_hash, block_diff); } @@ -3733,72 +4997,40 @@ static int block_sort(struct block *blocka, struct block *blockb) return blocka->block_no - blockb->block_no; } +/* Decode the current block difficulty which is in packed form */ static void set_blockdiff(const struct work *work) { - uint64_t *data64, d64, diff64; - double previous_diff; - uint32_t diffhash[8]; - uint32_t difficulty; - uint32_t diffbytes; - uint32_t diffvalue; - char rhash[32]; - int diffshift; + uint8_t pow = work->data[72]; + int powdiff = (8 * (0x1d - 3)) - (8 * (pow - 3)); + uint32_t diff32 = be32toh(*((uint32_t *)(work->data + 72))) & 0x00FFFFFF; + double numerator = 0xFFFFULL << powdiff; + double ddiff = numerator / (double)diff32; - difficulty = swab32(*((uint32_t *)(work->data + 72))); - - diffbytes = ((difficulty >> 24) & 0xff) - 3; - diffvalue = difficulty & 0x00ffffff; - - diffshift = (diffbytes % 4) * 8; - if (diffshift == 0) { - diffshift = 32; - diffbytes--; - } - - memset(diffhash, 0, 32); - diffbytes >>= 2; - if (unlikely(diffbytes > 6)) - return; - diffhash[diffbytes + 1] = diffvalue >> (32 - diffshift); - diffhash[diffbytes] = diffvalue << diffshift; - - swab256(rhash, diffhash); - - if (opt_scrypt) - data64 = (uint64_t *)(rhash + 2); - else - data64 = (uint64_t *)(rhash + 4); - d64 = bswap_64(*data64); - if (unlikely(!d64)) - d64 = 1; - - previous_diff = current_diff; - diff64 = diffone / d64; - suffix_string(diff64, block_diff, sizeof(block_diff), 0); - current_diff = (double)diffone / (double)d64; - if (unlikely(current_diff != previous_diff)) + if (unlikely(current_diff != ddiff)) { + suffix_string(ddiff, block_diff, sizeof(block_diff), 0); + current_diff = ddiff; applog(LOG_NOTICE, "Network diff set to %s", block_diff); + } } static bool test_work_current(struct work *work) { + struct pool *pool = work->pool; + unsigned char bedata[32]; + char hexstr[68]; bool ret = true; - char *hexstr; if (work->mandatory) return ret; - /* Hack to work around dud work sneaking into test */ - hexstr = bin2hex(work->data + 8, 18); - if (!strncmp(hexstr, "000000000000000000000000000000000000", 36)) - goto out_free; + swap256(bedata, work->data + 4); + __bin2hex(hexstr, bedata, 32); /* Search to see if this block exists yet and if not, consider it a * new block and set the current block details to this one */ if (!block_exists(hexstr)) { struct block *s = calloc(sizeof(struct block), 1); int deleted_block = 0; - ret = false; if (unlikely(!s)) quit (1, "test_work_current OOM"); @@ -3824,33 +5056,71 @@ static bool test_work_current(struct work *work) if (deleted_block) applog(LOG_DEBUG, "Deleted block %d from database", deleted_block); - set_curblock(hexstr, work->data); - if (unlikely(new_blocks == 1)) - goto out_free; + set_curblock(hexstr, bedata); + /* Copy the information to this pool's prev_block since it + * knows the new block exists. */ + memcpy(pool->prev_block, bedata, 32); + if (unlikely(new_blocks == 1)) { + ret = false; + goto out; + } work->work_block = ++work_block; - if (!work->stratum) { - if (work->longpoll) { + if (work->longpoll) { + if (work->stratum) { + applog(LOG_NOTICE, "Stratum from pool %d detected new block", + pool->pool_no); + } else { applog(LOG_NOTICE, "%sLONGPOLL from pool %d detected new block", work->gbt ? "GBT " : "", work->pool->pool_no); - } else if (have_longpoll) - applog(LOG_NOTICE, "New block detected on network before longpoll"); - else - applog(LOG_NOTICE, "New block detected on network"); - } + } + } else if (have_longpoll && !pool->gbt_solo) + applog(LOG_NOTICE, "New block detected on network before pool notification"); + else if (!pool->gbt_solo) + applog(LOG_NOTICE, "New block detected on network"); restart_threads(); - } else if (work->longpoll) { - work->work_block = ++work_block; - if (work->pool == current_pool()) { - applog(LOG_NOTICE, "%sLONGPOLL from pool %d requested work restart", - work->gbt ? "GBT " : "", work->pool->pool_no); - restart_threads(); + } else { + if (memcmp(pool->prev_block, bedata, 32)) { + /* Work doesn't match what this pool has stored as + * prev_block. Let's see if the work is from an old + * block or the pool is just learning about a new + * block. */ + if (memcmp(bedata, current_block, 32)) { + /* Doesn't match current block. It's stale */ + applog(LOG_DEBUG, "Stale data from pool %d", pool->pool_no); + ret = false; + } else { + /* Work is from new block and pool is up now + * current. */ + applog(LOG_INFO, "Pool %d now up to date", pool->pool_no); + memcpy(pool->prev_block, bedata, 32); + } + } +#if 0 + /* This isn't ideal, this pool is still on an old block but + * accepting shares from it. To maintain fair work distribution + * we work on it anyway. */ + if (memcmp(bedata, current_block, 32)) + applog(LOG_DEBUG, "Pool %d still on old block", pool->pool_no); +#endif + if (work->longpoll) { + work->work_block = ++work_block; + if (shared_strategy() || work->pool == current_pool()) { + if (work->stratum) { + applog(LOG_NOTICE, "Stratum from pool %d requested work restart", + pool->pool_no); + } else { + applog(LOG_NOTICE, "%sLONGPOLL from pool %d requested work restart", + work->gbt ? "GBT " : "", work->pool->pool_no); + } + restart_threads(); + } } } +out: work->longpoll = false; -out_free: - free(hexstr); + return ret; } @@ -3882,47 +5152,12 @@ static bool hash_push(struct work *work) return rc; } -static void *stage_thread(void *userdata) -{ - struct thr_info *mythr = userdata; - bool ok = true; - - pthread_setcanceltype(PTHREAD_CANCEL_ASYNCHRONOUS, NULL); - - RenameThread("stage"); - - while (ok) { - struct work *work = NULL; - - applog(LOG_DEBUG, "Popping work to stage thread"); - - work = tq_pop(mythr->q, NULL); - if (unlikely(!work)) { - applog(LOG_ERR, "Failed to tq_pop in stage_thread"); - ok = false; - break; - } - work->work_block = work_block; - - test_work_current(work); - - applog(LOG_DEBUG, "Pushing work to getwork queue"); - - if (unlikely(!hash_push(work))) { - applog(LOG_WARNING, "Failed to hash_push in stage_thread"); - continue; - } - } - - tq_freeze(mythr->q); - return NULL; -} - -static void stage_work(struct work *work) +static void _stage_work(struct work *work) { applog(LOG_DEBUG, "Pushing work from pool %d to hash queue", work->pool->pool_no); work->work_block = work_block; test_work_current(work); + work->pool->works++; hash_push(work); } @@ -3952,11 +5187,12 @@ static void display_pool_summary(struct pool *pool) wlog("Pool: %s\n", pool->rpc_url); if (pool->solved) wlog("SOLVED %d BLOCK%s!\n", pool->solved, pool->solved > 1 ? "S" : ""); - wlog("%s own long-poll support\n", pool->hdr_path ? "Has" : "Does not have"); + if (!pool->has_stratum) + wlog("%s own long-poll support\n", pool->hdr_path ? "Has" : "Does not have"); wlog(" Queued work requests: %d\n", pool->getwork_requested); - wlog(" Share submissions: %d\n", pool->accepted + pool->rejected); - wlog(" Accepted shares: %d\n", pool->accepted); - wlog(" Rejected shares: %d\n", pool->rejected); + wlog(" Share submissions: %"PRId64"\n", pool->accepted + pool->rejected); + wlog(" Accepted shares: %"PRId64"\n", pool->accepted); + wlog(" Rejected shares: %"PRId64"\n", pool->rejected); wlog(" Accepted difficulty shares: %1.f\n", pool->diff_accepted); wlog(" Rejected difficulty shares: %1.f\n", pool->diff_rejected); if (pool->accepted || pool->rejected) @@ -3965,6 +5201,7 @@ static void display_pool_summary(struct pool *pool) if (!pool_localgen(pool)) wlog(" Efficiency (accepted / queued): %.0f%%\n", efficiency); + wlog(" Items worked on: %d\n", pool->works); wlog(" Discarded work due to new blocks: %d\n", pool->discarded_work); wlog(" Stale submissions discarded due to new blocks: %d\n", pool->stale_shares); wlog(" Unable to get work from server occasions: %d\n", pool->getfail_occasions); @@ -4048,132 +5285,88 @@ static char *json_escape(char *str) void write_config(FILE *fcfg) { + struct opt_table *opt; int i; /* Write pool values */ fputs("{\n\"pools\" : [", fcfg); for(i = 0; i < total_pools; i++) { - fprintf(fcfg, "%s\n\t{\n\t\t\"url\" : \"%s%s%s%s\",", i > 0 ? "," : "", - pools[i]->rpc_proxy ? json_escape((char *)proxytype(pools[i]->rpc_proxytype)) : "", - pools[i]->rpc_proxy ? json_escape(pools[i]->rpc_proxy) : "", - pools[i]->rpc_proxy ? "|" : "", - json_escape(pools[i]->rpc_url)); - fprintf(fcfg, "\n\t\t\"user\" : \"%s\",", json_escape(pools[i]->rpc_user)); - fprintf(fcfg, "\n\t\t\"pass\" : \"%s\"\n\t}", json_escape(pools[i]->rpc_pass)); + struct pool *pool = priority_pool(i); + + if (pool->quota != 1) { + fprintf(fcfg, "%s\n\t{\n\t\t\"quota\" : \"%s%s%s%d;%s\",", i > 0 ? "," : "", + pool->rpc_proxy ? json_escape((char *)proxytype(pool->rpc_proxytype)) : "", + pool->rpc_proxy ? json_escape(pool->rpc_proxy) : "", + pool->rpc_proxy ? "|" : "", + pool->quota, + json_escape(pool->rpc_url)); + } else { + fprintf(fcfg, "%s\n\t{\n\t\t\"url\" : \"%s%s%s%s\",", i > 0 ? "," : "", + pool->rpc_proxy ? json_escape((char *)proxytype(pool->rpc_proxytype)) : "", + pool->rpc_proxy ? json_escape(pool->rpc_proxy) : "", + pool->rpc_proxy ? "|" : "", + json_escape(pool->rpc_url)); + } + fprintf(fcfg, "\n\t\t\"user\" : \"%s\",", json_escape(pool->rpc_user)); + fprintf(fcfg, "\n\t\t\"pass\" : \"%s\"\n\t}", json_escape(pool->rpc_pass)); } fputs("\n]\n", fcfg); -#ifdef HAVE_OPENCL - if (nDevs) { - /* Write GPU device values */ - fputs(",\n\"intensity\" : \"", fcfg); - for(i = 0; i < nDevs; i++) - fprintf(fcfg, gpus[i].dynamic ? "%sd" : "%s%d", i > 0 ? "," : "", gpus[i].intensity); - fputs("\",\n\"vectors\" : \"", fcfg); - for(i = 0; i < nDevs; i++) - fprintf(fcfg, "%s%d", i > 0 ? "," : "", - gpus[i].vwidth); - fputs("\",\n\"worksize\" : \"", fcfg); - for(i = 0; i < nDevs; i++) - fprintf(fcfg, "%s%d", i > 0 ? "," : "", - (int)gpus[i].work_size); - fputs("\",\n\"kernel\" : \"", fcfg); - for(i = 0; i < nDevs; i++) { - fprintf(fcfg, "%s", i > 0 ? "," : ""); - switch (gpus[i].kernel) { - case KL_NONE: // Shouldn't happen - break; - case KL_POCLBM: - fprintf(fcfg, "poclbm"); - break; - case KL_PHATK: - fprintf(fcfg, "phatk"); - break; - case KL_DIAKGCN: - fprintf(fcfg, "diakgcn"); - break; - case KL_DIABLO: - fprintf(fcfg, "diablo"); - break; - case KL_SCRYPT: - fprintf(fcfg, "scrypt"); - break; - } - } -#ifdef USE_SCRYPT - fputs("\",\n\"lookup-gap\" : \"", fcfg); - for(i = 0; i < nDevs; i++) - fprintf(fcfg, "%s%d", i > 0 ? "," : "", - (int)gpus[i].opt_lg); - fputs("\",\n\"thread-concurrency\" : \"", fcfg); - for(i = 0; i < nDevs; i++) - fprintf(fcfg, "%s%d", i > 0 ? "," : "", - (int)gpus[i].opt_tc); - fputs("\",\n\"shaders\" : \"", fcfg); - for(i = 0; i < nDevs; i++) - fprintf(fcfg, "%s%d", i > 0 ? "," : "", - (int)gpus[i].shaders); -#endif -#ifdef HAVE_ADL - fputs("\",\n\"gpu-engine\" : \"", fcfg); - for(i = 0; i < nDevs; i++) - fprintf(fcfg, "%s%d-%d", i > 0 ? "," : "", gpus[i].min_engine, gpus[i].gpu_engine); - fputs("\",\n\"gpu-fan\" : \"", fcfg); - for(i = 0; i < nDevs; i++) - fprintf(fcfg, "%s%d-%d", i > 0 ? "," : "", gpus[i].min_fan, gpus[i].gpu_fan); - fputs("\",\n\"gpu-memclock\" : \"", fcfg); - for(i = 0; i < nDevs; i++) - fprintf(fcfg, "%s%d", i > 0 ? "," : "", gpus[i].gpu_memclock); - fputs("\",\n\"gpu-memdiff\" : \"", fcfg); - for(i = 0; i < nDevs; i++) - fprintf(fcfg, "%s%d", i > 0 ? "," : "", gpus[i].gpu_memdiff); - fputs("\",\n\"gpu-powertune\" : \"", fcfg); - for(i = 0; i < nDevs; i++) - fprintf(fcfg, "%s%d", i > 0 ? "," : "", gpus[i].gpu_powertune); - fputs("\",\n\"gpu-vddc\" : \"", fcfg); - for(i = 0; i < nDevs; i++) - fprintf(fcfg, "%s%1.3f", i > 0 ? "," : "", gpus[i].gpu_vddc); - fputs("\",\n\"temp-cutoff\" : \"", fcfg); - for(i = 0; i < nDevs; i++) - fprintf(fcfg, "%s%d", i > 0 ? "," : "", gpus[i].cutofftemp); - fputs("\",\n\"temp-overheat\" : \"", fcfg); - for(i = 0; i < nDevs; i++) - fprintf(fcfg, "%s%d", i > 0 ? "," : "", gpus[i].adl.overtemp); - fputs("\",\n\"temp-target\" : \"", fcfg); - for(i = 0; i < nDevs; i++) - fprintf(fcfg, "%s%d", i > 0 ? "," : "", gpus[i].adl.targettemp); -#endif - fputs("\"", fcfg); - } -#endif -#ifdef HAVE_ADL - if (opt_reorder) - fprintf(fcfg, ",\n\"gpu-reorder\" : true"); -#endif - - /* Simple bool and int options */ - struct opt_table *opt; + /* Simple bool,int and char options */ for (opt = opt_config_table; opt->type != OPT_END; opt++) { char *p, *name = strdup(opt->names); + for (p = strtok(name, "|"); p; p = strtok(NULL, "|")) { if (p[1] != '-') continue; + + if (opt->desc == opt_hidden) + continue; + if (opt->type & OPT_NOARG && ((void *)opt->cb == (void *)opt_set_bool || (void *)opt->cb == (void *)opt_set_invbool) && - (*(bool *)opt->u.arg == ((void *)opt->cb == (void *)opt_set_bool))) + (*(bool *)opt->u.arg == ((void *)opt->cb == (void *)opt_set_bool))) { fprintf(fcfg, ",\n\"%s\" : true", p+2); + continue; + } if (opt->type & OPT_HASARG && - ((void *)opt->cb_arg == (void *)set_int_0_to_9999 || - (void *)opt->cb_arg == (void *)set_int_1_to_65535 || - (void *)opt->cb_arg == (void *)set_int_0_to_10 || - (void *)opt->cb_arg == (void *)set_int_1_to_10) && opt->desc != opt_hidden) + ((void *)opt->cb_arg == (void *)opt_set_intval || + (void *)opt->cb_arg == (void *)set_int_0_to_9999 || + (void *)opt->cb_arg == (void *)set_int_1_to_65535 || + (void *)opt->cb_arg == (void *)set_int_0_to_10 || + (void *)opt->cb_arg == (void *)set_int_1_to_10 || + (void *)opt->cb_arg == (void *)set_int_0_to_100 || + (void *)opt->cb_arg == (void *)set_int_0_to_255 || + (void *)opt->cb_arg == (void *)set_int_0_to_200 || + (void *)opt->cb_arg == (void *)set_int_0_to_4 || + (void *)opt->cb_arg == (void *)set_int_32_to_63 || + (void *)opt->cb_arg == (void *)set_int_22_to_55 || + (void *)opt->cb_arg == (void *)set_int_42_to_65)) { fprintf(fcfg, ",\n\"%s\" : \"%d\"", p+2, *(int *)opt->u.arg); + continue; + } + + if (opt->type & OPT_HASARG && + (((void *)opt->cb_arg == (void *)set_float_125_to_500) || + (void *)opt->cb_arg == (void *)set_float_100_to_250)) { + fprintf(fcfg, ",\n\"%s\" : \"%.1f\"", p+2, *(float *)opt->u.arg); + continue; + } + + if (opt->type & (OPT_HASARG | OPT_PROCESSARG) && + (opt->u.arg != &opt_set_null)) { + char *carg = *(char **)opt->u.arg; + + if (carg) + fprintf(fcfg, ",\n\"%s\" : \"%s\"", p+2, json_escape(carg)); + continue; + } } + free(name); } /* Special case options */ - fprintf(fcfg, ",\n\"shares\" : \"%d\"", opt_shares); if (pool_strategy == POOL_BALANCE) fputs(",\n\"balance\" : true", fcfg); if (pool_strategy == POOL_LOADBALANCE) @@ -4182,63 +5375,6 @@ void write_config(FILE *fcfg) fputs(",\n\"round-robin\" : true", fcfg); if (pool_strategy == POOL_ROTATE) fprintf(fcfg, ",\n\"rotate\" : \"%d\"", opt_rotate_period); -#if defined(unix) || defined(__APPLE__) - if (opt_stderr_cmd && *opt_stderr_cmd) - fprintf(fcfg, ",\n\"monitor\" : \"%s\"", json_escape(opt_stderr_cmd)); -#endif // defined(unix) - if (opt_kernel_path && *opt_kernel_path) { - char *kpath = strdup(opt_kernel_path); - if (kpath[strlen(kpath)-1] == '/') - kpath[strlen(kpath)-1] = 0; - fprintf(fcfg, ",\n\"kernel-path\" : \"%s\"", json_escape(kpath)); - } - if (schedstart.enable) - fprintf(fcfg, ",\n\"sched-time\" : \"%d:%d\"", schedstart.tm.tm_hour, schedstart.tm.tm_min); - if (schedstop.enable) - fprintf(fcfg, ",\n\"stop-time\" : \"%d:%d\"", schedstop.tm.tm_hour, schedstop.tm.tm_min); - if (opt_socks_proxy && *opt_socks_proxy) - fprintf(fcfg, ",\n\"socks-proxy\" : \"%s\"", json_escape(opt_socks_proxy)); - if (opt_devs_enabled) { - fprintf(fcfg, ",\n\"device\" : \""); - bool extra_devs = false; - - for (i = 0; i < MAX_DEVICES; i++) { - if (devices_enabled[i]) { - int startd = i; - - if (extra_devs) - fprintf(fcfg, ","); - while (i < MAX_DEVICES && devices_enabled[i + 1]) - ++i; - fprintf(fcfg, "%d", startd); - if (i > startd) - fprintf(fcfg, "-%d", i); - } - } - fprintf(fcfg, "\""); - } - if (opt_removedisabled) - fprintf(fcfg, ",\n\"remove-disabled\" : true"); - if (opt_api_allow) - fprintf(fcfg, ",\n\"api-allow\" : \"%s\"", json_escape(opt_api_allow)); - if (strcmp(opt_api_mcast_addr, API_MCAST_ADDR) != 0) - fprintf(fcfg, ",\n\"api-mcast-addr\" : \"%s\"", json_escape(opt_api_mcast_addr)); - if (strcmp(opt_api_mcast_code, API_MCAST_CODE) != 0) - fprintf(fcfg, ",\n\"api-mcast-code\" : \"%s\"", json_escape(opt_api_mcast_code)); - if (*opt_api_mcast_des) - fprintf(fcfg, ",\n\"api-mcast-des\" : \"%s\"", json_escape(opt_api_mcast_des)); - if (strcmp(opt_api_description, PACKAGE_STRING) != 0) - fprintf(fcfg, ",\n\"api-description\" : \"%s\"", json_escape(opt_api_description)); - if (opt_api_groups) - fprintf(fcfg, ",\n\"api-groups\" : \"%s\"", json_escape(opt_api_groups)); - if (opt_icarus_options) - fprintf(fcfg, ",\n\"icarus-options\" : \"%s\"", json_escape(opt_icarus_options)); - if (opt_icarus_timing) - fprintf(fcfg, ",\n\"icarus-timing\" : \"%s\"", json_escape(opt_icarus_timing)); -#ifdef USE_USBUTILS - if (opt_usb_select) - fprintf(fcfg, ",\n\"usb\" : \"%s\"", json_escape(opt_usb_select)); -#endif fputs("\n}\n", fcfg); json_escape_free(); @@ -4258,12 +5394,27 @@ void zero_bestshare(void) } } +static struct timeval tv_hashmeter; +static time_t hashdisplay_t; + void zero_stats(void) { int i; cgtime(&total_tv_start); + copy_time(&tv_hashmeter, &total_tv_start); + total_rolling = 0; + rolling1 = 0; + rolling5 = 0; + rolling15 = 0; total_mhashes_done = 0; + + for(i = 0; i < CG_LOCAL_MHASHES_MAX_NUM; i++) { + g_local_mhashes_dones[i] = 0; + } + g_local_mhashes_index = 0; + g_max_fan = 0; + g_max_temp = 0; total_getworks = 0; total_accepted = 0; total_rejected = 0; @@ -4274,6 +5425,7 @@ void zero_stats(void) total_go = 0; total_ro = 0; total_secs = 1.0; + last_total_secs = 1.0; total_diff1 = 0; found_blocks = 0; total_diff_accepted = 0; @@ -4303,6 +5455,8 @@ void zero_stats(void) for (i = 0; i < total_devices; ++i) { struct cgpu_info *cgpu = get_devices(i); + copy_time(&cgpu->dev_start_tv, &total_tv_start); + mutex_lock(&hash_lock); cgpu->total_mhashes = 0; cgpu->accepted = 0; @@ -4315,9 +5469,38 @@ void zero_stats(void) cgpu->diff_rejected = 0; cgpu->last_share_diff = 0; mutex_unlock(&hash_lock); + + /* Don't take any locks in the driver zero stats function, as + * it's called async from everything else and we don't want to + * deadlock. */ + cgpu->drv->zero_stats(cgpu); } } +static void set_highprio(void) +{ +#ifndef WIN32 + int ret = nice(-10); + + if (!ret) + applog(LOG_DEBUG, "Unable to set thread to high priority"); +#else + SetThreadPriority(GetCurrentThread(), THREAD_PRIORITY_HIGHEST); +#endif +} + +static void set_lowprio(void) +{ +#ifndef WIN32 + int ret = nice(10); + + if (!ret) + applog(LOG_INFO, "Unable to set thread to low priority"); +#else + SetThreadPriority(GetCurrentThread(), THREAD_PRIORITY_LOWEST); +#endif +} + #ifdef HAVE_CURSES static void display_pools(void) { @@ -4348,8 +5531,9 @@ static void display_pools(void) wlogprint("Rejecting "); break; } - wlogprint("%s Priority %d: %s User:%s\n", + wlogprint("%s Quota %d Prio %d: %s User:%s\n", pool->idle? "Dead" : "Alive", + pool->quota, pool->prio, pool->rpc_url, pool->rpc_user); wattroff(logwin, A_BOLD | A_DIM); @@ -4360,7 +5544,7 @@ static void display_pools(void) if (pool_strategy == POOL_ROTATE) wlogprint("Set to rotate every %d minutes\n", opt_rotate_period); wlogprint("[F]ailover only %s\n", opt_fail_only ? "enabled" : "disabled"); - wlogprint("[A]dd pool [R]emove pool [D]isable pool [E]nable pool\n"); + wlogprint("Pool [A]dd [R]emove [D]isable [E]nable [Q]uota change\n"); wlogprint("[C]hange management strategy [S]witch pool [I]nformation\n"); wlogprint("Or press any other key to continue\n"); logwin_update(); @@ -4454,6 +5638,21 @@ static void display_pools(void) pool = pools[selected]; display_pool_summary(pool); goto retry; + } else if (!strncasecmp(&input, "q", 1)) { + selected = curses_int("Select pool number"); + if (selected < 0 || selected >= total_pools) { + wlogprint("Invalid selection\n"); + goto retry; + } + pool = pools[selected]; + selected = curses_int("Set quota"); + if (selected < 0) { + wlogprint("Invalid negative quota\n"); + goto retry; + } + pool->quota = selected; + adjust_quota_gcd(); + goto updated; } else if (!strncasecmp(&input, "f", 1)) { opt_fail_only ^= true; goto updated; @@ -4476,7 +5675,10 @@ static void display_options(void) wlogprint("[N]ormal [C]lear [S]ilent mode (disable all output)\n"); wlogprint("[D]ebug:%s\n[P]er-device:%s\n[Q]uiet:%s\n[V]erbose:%s\n" "[R]PC debug:%s\n[W]orkTime details:%s\nco[M]pact: %s\n" - "[L]og interval:%d\n[Z]ero statistics\n", + "[T]oggle status switching:%s\n" + "w[I]descreen:%s\n" + "[Z]ero statistics\n" + "[L]og interval:%d\n", opt_debug ? "on" : "off", want_per_device_stats? "on" : "off", opt_quiet ? "on" : "off", @@ -4484,6 +5686,8 @@ static void display_options(void) opt_protocol ? "on" : "off", opt_worktime ? "on" : "off", opt_compact ? "on" : "off", + switch_status ? "enabled" : "disabled", + opt_widescreen ? "enabled" : "disabled", opt_log_interval); wlogprint("Select an option or any other key to return\n"); logwin_update(); @@ -4548,6 +5752,12 @@ static void display_options(void) opt_worktime ^= true; wlogprint("WorkTime details %s\n", opt_worktime ? "enabled" : "disabled"); goto retry; + } else if (!strncasecmp(&input, "t", 1)) { + switch_status ^= true; + goto retry; + } else if (!strncasecmp(&input, "i", 1)) { + opt_widescreen ^= true; + goto retry; } else if (!strncasecmp(&input, "z", 1)) { zero_stats(); goto retry; @@ -4605,6 +5815,8 @@ static void set_options(void) goto retry; } opt_queue = selected; + if (opt_queue < max_queue) + max_queue = opt_queue; goto retry; } else if (!strncasecmp(&input, "s", 1)) { selected = curses_int("Set scantime in seconds"); @@ -4666,11 +5878,213 @@ static void set_options(void) opt_loginput = false; } +#ifdef USE_USBUTILS +static void mt_enable(struct thr_info *mythr) +{ + cgsem_post(&mythr->sem); +} + +static void set_usb(void) +{ + int selected, i, mt, enabled, disabled, zombie, total, blacklisted; + struct cgpu_info *cgpu; + struct thr_info *thr; + double val; + char input; + + opt_loginput = true; + immedok(logwin, true); + clear_logwin(); + +retry: + enabled = 0; + disabled = 0; + zombie = 0; + total = 0; + blacklisted = 0; + + rd_lock(&mining_thr_lock); + mt = mining_threads; + rd_unlock(&mining_thr_lock); + + for (i = 0; i < mt; i++) { + cgpu = mining_thr[i]->cgpu; + if (unlikely(!cgpu)) + continue; + if (cgpu->usbinfo.nodev) + zombie++; + else if (cgpu->deven == DEV_DISABLED) + disabled++; + else + enabled++; + if (cgpu->blacklisted) + blacklisted++; + total++; + } + wlogprint("Hotplug interval:%d\n", hotplug_time); + wlogprint("%d USB devices, %d enabled, %d disabled, %d zombie\n", + total, enabled, disabled, zombie); + wlogprint("[S]ummary of device information\n"); + wlogprint("[E]nable device\n"); + wlogprint("[D]isable device\n"); + wlogprint("[U]nplug to allow hotplug restart\n"); + wlogprint("[R]eset device USB\n"); + wlogprint("[L]ist all known devices\n"); + wlogprint("[B]lacklist current device from current instance of cgminer\n"); + wlogprint("[W]hitelist previously blacklisted device\n"); + wlogprint("[H]otplug interval (0 to disable)\n"); + wlogprint("Select an option or any other key to return\n"); + logwin_update(); + input = getch(); + + if (!strncasecmp(&input, "s", 1)) { + selected = curses_int("Select device number"); + if (selected < 0 || selected >= mt) { + wlogprint("Invalid selection\n"); + goto retry; + } + cgpu = mining_thr[selected]->cgpu; + wlogprint("Device %03u:%03u\n", cgpu->usbinfo.bus_number, cgpu->usbinfo.device_address); + wlogprint("Name %s\n", cgpu->drv->name); + wlogprint("ID %d\n", cgpu->device_id); + wlogprint("Enabled: %s\n", cgpu->deven != DEV_DISABLED ? "Yes" : "No"); + wlogprint("Temperature %.1f\n", cgpu->temp); + wlogprint("MHS av %.0f\n", cgpu->total_mhashes / cgpu_runtime(cgpu)); + wlogprint("Accepted %d\n", cgpu->accepted); + wlogprint("Rejected %d\n", cgpu->rejected); + wlogprint("Hardware Errors %d\n", cgpu->hw_errors); + wlogprint("Last Share Pool %d\n", cgpu->last_share_pool_time > 0 ? cgpu->last_share_pool : -1); + wlogprint("Total MH %.1f\n", cgpu->total_mhashes); + wlogprint("Diff1 Work %"PRId64"\n", cgpu->diff1); + wlogprint("Difficulty Accepted %.1f\n", cgpu->diff_accepted); + wlogprint("Difficulty Rejected %.1f\n", cgpu->diff_rejected); + wlogprint("Last Share Difficulty %.1f\n", cgpu->last_share_diff); + wlogprint("No Device: %s\n", cgpu->usbinfo.nodev ? "True" : "False"); + wlogprint("Last Valid Work %"PRIu64"\n", (uint64_t)cgpu->last_device_valid_work); + val = 0; + if (cgpu->hw_errors + cgpu->diff1) + val = cgpu->hw_errors / (cgpu->hw_errors + cgpu->diff1); + wlogprint("Device Hardware %.1f%%\n", val); + val = 0; + if (cgpu->diff1) + val = cgpu->diff_rejected / cgpu->diff1; + wlogprint("Device Rejected %.1f%%\n", val); + goto retry; + } else if (!strncasecmp(&input, "e", 1)) { + selected = curses_int("Select device number"); + if (selected < 0 || selected >= mt) { + wlogprint("Invalid selection\n"); + goto retry; + } + cgpu = mining_thr[selected]->cgpu; + if (cgpu->usbinfo.nodev) { + wlogprint("Device removed, unable to re-enable!\n"); + goto retry; + } + thr = get_thread(selected); + cgpu->deven = DEV_ENABLED; + mt_enable(thr); + goto retry; + } else if (!strncasecmp(&input, "d", 1)) { + selected = curses_int("Select device number"); + if (selected < 0 || selected >= mt) { + wlogprint("Invalid selection\n"); + goto retry; + } + cgpu = mining_thr[selected]->cgpu; + cgpu->deven = DEV_DISABLED; + goto retry; + } else if (!strncasecmp(&input, "u", 1)) { + selected = curses_int("Select device number"); + if (selected < 0 || selected >= mt) { + wlogprint("Invalid selection\n"); + goto retry; + } + cgpu = mining_thr[selected]->cgpu; + if (cgpu->usbinfo.nodev) { + wlogprint("Device already removed, unable to unplug!\n"); + goto retry; + } + usb_nodev(cgpu); + goto retry; + } else if (!strncasecmp(&input, "r", 1)) { + selected = curses_int("Select device number"); + if (selected < 0 || selected >= mt) { + wlogprint("Invalid selection\n"); + goto retry; + } + cgpu = mining_thr[selected]->cgpu; + if (cgpu->usbinfo.nodev) { + wlogprint("Device already removed, unable to reset!\n"); + goto retry; + } + usb_reset(cgpu); + goto retry; + } else if (!strncasecmp(&input, "b", 1)) { + selected = curses_int("Select device number"); + if (selected < 0 || selected >= mt) { + wlogprint("Invalid selection\n"); + goto retry; + } + cgpu = mining_thr[selected]->cgpu; + if (cgpu->usbinfo.nodev) { + wlogprint("Device already removed, unable to blacklist!\n"); + goto retry; + } + blacklist_cgpu(cgpu); + goto retry; + } else if (!strncasecmp(&input, "w", 1)) { + if (!blacklisted) { + wlogprint("No blacklisted devices!\n"); + goto retry; + } + wlogprint("Blacklisted devices:\n"); + for (i = 0; i < mt; i++) { + cgpu = mining_thr[i]->cgpu; + if (unlikely(!cgpu)) + continue; + if (cgpu->blacklisted) { + wlogprint("%d: %s %d %03u:%03u\n", i, cgpu->drv->name, + cgpu->device_id, cgpu->usbinfo.bus_number, + cgpu->usbinfo.device_address); + } + } + selected = curses_int("Select device number"); + if (selected < 0 || selected >= mt) { + wlogprint("Invalid selection\n"); + goto retry; + } + cgpu = mining_thr[selected]->cgpu; + if (!cgpu->blacklisted) { + wlogprint("Device not blacklisted, unable to whitelist\n"); + goto retry; + } + whitelist_cgpu(cgpu); + goto retry; + } else if (!strncasecmp(&input, "h", 1)) { + selected = curses_int("Select hotplug interval in seconds (0 to disable)"); + if (selected < 0 || selected > 9999) { + wlogprint("Invalid value\n"); + goto retry; + } + hotplug_time = selected; + goto retry; + } else if (!strncasecmp(&input, "l", 1)) { + usb_list(); + goto retry; + } else + clear_logwin(); + + immedok(logwin, false); + opt_loginput = false; +} +#endif + static void *input_thread(void __maybe_unused *userdata) { pthread_setcanceltype(PTHREAD_CANCEL_ASYNCHRONOUS, NULL); - RenameThread("input"); + RenameThread("Input"); if (!curses_active) return NULL; @@ -4688,8 +6102,10 @@ static void *input_thread(void __maybe_unused *userdata) display_pools(); else if (!strncasecmp(&input, "s", 1)) set_options(); - else if (have_opencl && !strncasecmp(&input, "g", 1)) - manage_gpu(); +#ifdef USE_USBUTILS + else if (!strncasecmp(&input, "u", 1)) + set_usb(); +#endif if (opt_realquiet) { disable_curses(); break; @@ -4707,8 +6123,9 @@ static void *api_thread(void *userdata) pthread_detach(pthread_self()); pthread_setcanceltype(PTHREAD_CANCEL_ASYNCHRONOUS, NULL); - RenameThread("api"); + RenameThread("API"); + set_lowprio(); api(api_thr_id); PTH(mythr) = 0L; @@ -4738,107 +6155,146 @@ static void thread_reportout(struct thr_info *thr) thr->cgpu->device_last_well = time(NULL); } -static void hashmeter(int thr_id, struct timeval *diff, - uint64_t hashes_done) +static void hashmeter(int thr_id, uint64_t hashes_done) { - struct timeval temp_tv_end, total_diff; - double secs; - double local_secs; - static double local_mhashes_done = 0; - static double rolling = 0; - double local_mhashes; bool showlog = false; - char displayed_hashes[16], displayed_rolling[16]; - uint64_t dh64, dr64; - struct thr_info *thr; + double tv_tdiff; + time_t now_t; + int diff_t; - local_mhashes = (double)hashes_done / 1000000.0; - /* Update the last time this thread reported in */ - if (thr_id >= 0) { - thr = get_thread(thr_id); - cgtime(&(thr->last)); - thr->cgpu->device_last_well = time(NULL); - } + uint64_t local_mhashes_done = 0; + uint64_t local_mhashes_done_avg = 0; + int local_mhashes_done_count = 0; + int i = 0; - secs = (double)diff->tv_sec + ((double)diff->tv_usec / 1000000.0); + cgtime(&total_tv_end); + tv_tdiff = tdiff(&total_tv_end, &tv_hashmeter); + now_t = total_tv_end.tv_sec; + diff_t = now_t - hashdisplay_t; + if (diff_t >= opt_log_interval) { + alt_status ^= switch_status; + hashdisplay_t = now_t; + showlog = true; + } else if (thr_id < 0) { + /* hashmeter is called by non-mining threads in case nothing + * has reported in to allow hashrate to converge to zero , but + * we only update if it has been more than opt_log_interval */ + return; + } + copy_time(&tv_hashmeter, &total_tv_end); - /* So we can call hashmeter from a non worker thread */ if (thr_id >= 0) { + struct thr_info *thr = get_thread(thr_id); struct cgpu_info *cgpu = thr->cgpu; - double thread_rolling = 0.0; - int i; + double device_tdiff, thr_mhs; + + /* Update the last time this thread reported in */ + copy_time(&thr->last, &total_tv_end); + cgpu->device_last_well = now_t; + device_tdiff = tdiff(&total_tv_end, &cgpu->last_message_tv); + copy_time(&cgpu->last_message_tv, &total_tv_end); + thr_mhs = (double)hashes_done / device_tdiff / 1000000; + applog(LOG_DEBUG, "[thread %d: %"PRIu64" hashes, %.1f mhash/sec]", + thr_id, hashes_done, thr_mhs); + hashes_done /= 1000000; - applog(LOG_DEBUG, "[thread %d: %"PRIu64" hashes, %.1f khash/sec]", - thr_id, hashes_done, hashes_done / 1000 / secs); + mutex_lock(&hash_lock); + cgpu->total_mhashes += hashes_done; + decay_time(&cgpu->rolling, hashes_done, device_tdiff, opt_log_interval); + decay_time(&cgpu->rolling1, hashes_done, device_tdiff, 60.0); + decay_time(&cgpu->rolling5, hashes_done, device_tdiff, 300.0); + decay_time(&cgpu->rolling15, hashes_done, device_tdiff, 900.0); + mutex_unlock(&hash_lock); - /* Rolling average for each thread and each device */ - decay_time(&thr->rolling, local_mhashes / secs, secs); - for (i = 0; i < cgpu->threads; i++) - thread_rolling += cgpu->thr[i]->rolling; + if (want_per_device_stats && showlog) { + char logline[256]; + get_statline(logline, sizeof(logline), cgpu); + if (!curses_active) { + printf("%s \r", logline); + fflush(stdout); + } else + applog(LOG_INFO, "%s", logline); + } + } else { + /* No device has reported in, we have been called from the + * watchdog thread so decay all the hashrates */ mutex_lock(&hash_lock); - decay_time(&cgpu->rolling, thread_rolling, secs); - cgpu->total_mhashes += local_mhashes; + for (thr_id = 0; thr_id < mining_threads; thr_id++) { + struct thr_info *thr = get_thread(thr_id); + struct cgpu_info *cgpu = thr->cgpu; + double device_tdiff = tdiff(&total_tv_end, &cgpu->last_message_tv); + + copy_time(&cgpu->last_message_tv, &total_tv_end); + decay_time(&cgpu->rolling, 0, device_tdiff, opt_log_interval); + decay_time(&cgpu->rolling1, 0, device_tdiff, 60.0); + decay_time(&cgpu->rolling5, 0, device_tdiff, 300.0); + decay_time(&cgpu->rolling15, 0, device_tdiff, 900.0); + } mutex_unlock(&hash_lock); + } - // If needed, output detailed, per-device stats - if (want_per_device_stats) { - struct timeval now; - struct timeval elapsed; - - cgtime(&now); - timersub(&now, &thr->cgpu->last_message_tv, &elapsed); - if (opt_log_interval <= elapsed.tv_sec) { - struct cgpu_info *cgpu = thr->cgpu; - char logline[255]; - - cgpu->last_message_tv = now; - - get_statline(logline, sizeof(logline), cgpu); - if (!curses_active) { - printf("%s \r", logline); - fflush(stdout); - } else - applog(LOG_INFO, "%s", logline); + mutex_lock(&hash_lock); + total_mhashes_done += hashes_done; + if(showlog){ + g_local_mhashes_index++; + if(g_local_mhashes_index >= CG_LOCAL_MHASHES_MAX_NUM) + g_local_mhashes_index = 0; + + for(i = 0; i < CG_LOCAL_MHASHES_MAX_NUM; i++) { + if(g_local_mhashes_dones[i] >= 0) { + local_mhashes_done_avg += g_local_mhashes_dones[i]; + //applog(LOG_DEBUG, "g_local_mhashes_dones[%d] = %f,%d", i, g_local_mhashes_dones[i],g_local_mhashes_index); + local_mhashes_done_count++; } } + + if(local_mhashes_done_count > 0) { + local_mhashes_done = local_mhashes_done_avg / local_mhashes_done_count; + } else { + local_mhashes_done = hashes_done; + } + + decay_time(&total_rolling, local_mhashes_done, opt_log_interval, opt_log_interval); + decay_time(&rolling1, hashes_done, tv_tdiff, 60.0); + decay_time(&rolling5, hashes_done,tv_tdiff, 300.0); + decay_time(&rolling15, hashes_done, tv_tdiff, 900.0); + global_hashrate = llround(total_rolling) * 1000000; + g_local_mhashes_dones[g_local_mhashes_index] = 0; + } + g_local_mhashes_dones[g_local_mhashes_index] += hashes_done; + total_secs = tdiff(&total_tv_end, &total_tv_start); + + if(total_secs - last_total_secs > 86400) { + applog(LOG_ERR, "cgminer time error total_secs = %d last_total_secs = %d", total_secs, last_total_secs); + mutex_unlock(&hash_lock); + zero_stats(); + mutex_lock(&hash_lock); + } else { + last_total_secs = total_secs; + } + if (showlog) { + char displayed_hashes[16], displayed_rolling[16]; + char displayed_r1[16], displayed_r5[16], displayed_r15[16]; + uint64_t d64; + + d64 = (double)total_mhashes_done / total_secs * 1000000ull; + suffix_string(d64, displayed_hashes, sizeof(displayed_hashes), 4); + d64 = (double)total_rolling * 1000000ull; + g_displayed_rolling = total_rolling / 1000.0; + suffix_string(d64, displayed_rolling, sizeof(displayed_rolling), 4); + d64 = (double)rolling1 * 1000000ull; + suffix_string(d64, displayed_r1, sizeof(displayed_rolling), 4); + d64 = (double)rolling5 * 1000000ull; + suffix_string(d64, displayed_r5, sizeof(displayed_rolling), 4); + d64 = (double)rolling15 * 1000000ull; + suffix_string(d64, displayed_r15, sizeof(displayed_rolling), 4); + + snprintf(statusline, sizeof(statusline), + "(%ds):%s (1m):%s (5m):%s (15m):%s (avg):%sh/s", + opt_log_interval, displayed_rolling, displayed_r1, displayed_r5, + displayed_r15, displayed_hashes); } - - /* Totals are updated by all threads so can race without locking */ - mutex_lock(&hash_lock); - cgtime(&temp_tv_end); - timersub(&temp_tv_end, &total_tv_end, &total_diff); - - total_mhashes_done += local_mhashes; - local_mhashes_done += local_mhashes; - /* Only update with opt_log_interval */ - if (total_diff.tv_sec < opt_log_interval) - goto out_unlock; - showlog = true; - cgtime(&total_tv_end); - - local_secs = (double)total_diff.tv_sec + ((double)total_diff.tv_usec / 1000000.0); - decay_time(&rolling, local_mhashes_done / local_secs, local_secs); - global_hashrate = roundl(rolling) * 1000000; - - timersub(&total_tv_end, &total_tv_start, &total_diff); - total_secs = (double)total_diff.tv_sec + - ((double)total_diff.tv_usec / 1000000.0); - - dh64 = (double)total_mhashes_done / total_secs * 1000000ull; - dr64 = (double)rolling * 1000000ull; - suffix_string(dh64, displayed_hashes, sizeof(displayed_hashes), 4); - suffix_string(dr64, displayed_rolling, sizeof(displayed_rolling), 4); - - snprintf(statusline, sizeof(statusline), - "%s(%ds):%s (avg):%sh/s | A:%.0f R:%.0f HW:%d WU:%.1f/m", - want_per_device_stats ? "ALL " : "", - opt_log_interval, displayed_rolling, displayed_hashes, - total_diff_accepted, total_diff_rejected, hw_errors, - total_diff1 / total_secs * 60); - - local_mhashes_done = 0; -out_unlock: mutex_unlock(&hash_lock); if (showlog) { @@ -4854,17 +6310,16 @@ static void stratum_share_result(json_t *val, json_t *res_val, json_t *err_val, struct stratum_share *sshare) { struct work *work = sshare->work; + time_t now_t = time(NULL); char hashshow[64]; - uint32_t *hash32; - char diffdisp[16]; - int intdiff; + int srdiff; - hash32 = (uint32_t *)(work->hash); - intdiff = floor(work->work_difficulty); - suffix_string(work->share_diff, diffdisp, sizeof (diffdisp), 0); - snprintf(hashshow, sizeof(hashshow), - "%08lx Diff %s/%d%s", (unsigned long)htole32(hash32[6]), diffdisp, intdiff, - work->block? " BLOCK!" : ""); + srdiff = now_t - sshare->sshare_sent; + if (opt_debug || srdiff > 0) { + applog(LOG_INFO, "Pool %d stratum share result lag time %d seconds", + work->pool->pool_no, srdiff); + } + show_hash(work, hashshow); share_result(val, res_val, err_val, work, hashshow, false, ""); } @@ -4873,7 +6328,7 @@ static void stratum_share_result(json_t *val, json_t *res_val, json_t *err_val, static bool parse_stratum_response(struct pool *pool, char *s) { json_t *val = NULL, *err_val, *res_val, *id_val; - struct stratum_share *sshare; + struct stratum_share *sshare = NULL; json_error_t err; bool ret = false; int id; @@ -4916,10 +6371,12 @@ static bool parse_stratum_response(struct pool *pool, char *s) if (!sshare) { double pool_diff; + if (!res_val) + goto out; /* Since the share is untracked, we can only guess at what the * work difficulty is based on the current pool diff. */ cg_rlock(&pool->data_lock); - pool_diff = pool->swork.diff; + pool_diff = pool->sdiff; cg_runlock(&pool->data_lock); if (json_is_true(res_val)) { @@ -4985,7 +6442,7 @@ void clear_stratum_shares(struct pool *pool) } } -static void clear_pool_work(struct pool *pool) +void clear_pool_work(struct pool *pool) { struct work *work, *tmp; int cleared = 0; @@ -4999,6 +6456,9 @@ static void clear_pool_work(struct pool *pool) } } mutex_unlock(stgd_lock); + + if (cleared) + applog(LOG_INFO, "Cleared %d work items due to stratum disconnect on pool %d", cleared, pool->pool_no); } static int cp_prio(void) @@ -5047,8 +6507,6 @@ static bool cnx_needed(struct pool *pool) * it. */ if (pool_strategy == POOL_FAILOVER && pool->prio < cp_prio()) return true; - if (pool_unworkable(cp)) - return true; /* We've run out of work, bring anything back to life. */ if (no_work) return true; @@ -5059,10 +6517,8 @@ static void wait_lpcurrent(struct pool *pool); static void pool_resus(struct pool *pool); static void gen_stratum_work(struct pool *pool, struct work *work); -static void stratum_resumed(struct pool *pool) +void stratum_resumed(struct pool *pool) { - if (!pool->stratum_notify) - return; if (pool_tclear(pool, &pool->idle)) { applog(LOG_INFO, "Stratum connection to pool %d resumed", pool->pool_no); pool_resus(pool); @@ -5091,7 +6547,7 @@ static void *stratum_rthread(void *userdata) pthread_detach(pthread_self()); - snprintf(threadname, 16, "StratumR/%d", pool->pool_no); + snprintf(threadname, sizeof(threadname), "%d/RStratum", pool->pool_no); RenameThread(threadname); while (42) { @@ -5100,8 +6556,10 @@ static void *stratum_rthread(void *userdata) fd_set rd; char *s; - if (unlikely(pool->removed)) + if (unlikely(pool->removed)) { + suspend_stratum(pool); break; + } /* Check to see whether we need to maintain this connection * indefinitely or just bring it up when we switch to this @@ -5112,13 +6570,13 @@ static void *stratum_rthread(void *userdata) clear_pool_work(pool); wait_lpcurrent(pool); - if (!restart_stratum(pool)) { - pool_died(pool); - while (!restart_stratum(pool)) { - if (pool->removed) - goto out; - cgsleep_ms(30000); - } + while (!restart_stratum(pool)) { + if (pool->removed) + goto out; + if (enabled_pools > 1) + cgsleep_ms(30000); + else + cgsleep_ms(3000); } } @@ -5150,16 +6608,11 @@ static void *stratum_rthread(void *userdata) if (pool == current_pool()) restart_threads(); - if (restart_stratum(pool)) - continue; - - pool_died(pool); while (!restart_stratum(pool)) { if (pool->removed) goto out; cgsleep_ms(30000); } - stratum_resumed(pool); continue; } @@ -5169,25 +6622,20 @@ static void *stratum_rthread(void *userdata) if (!parse_method(pool, s) && !parse_stratum_response(pool, s)) applog(LOG_INFO, "Unknown stratum msg: %s", s); - free(s); - if (pool->swork.clean) { + else if (pool->swork.clean) { struct work *work = make_work(); /* Generate a single work item to update the current * block database */ pool->swork.clean = false; gen_stratum_work(pool, work); - if (test_work_current(work)) { - /* Only accept a work restart if this stratum - * connection is from the current pool */ - if (pool == current_pool()) { - restart_threads(); - applog(LOG_NOTICE, "Stratum from pool %d requested work restart", pool->pool_no); - } - } else - applog(LOG_NOTICE, "Stratum from pool %d detected new block", pool->pool_no); + work->longpoll = true; + /* Return value doesn't matter. We're just informing + * that we may need to restart. */ + test_work_current(work); free_work(work); } + free(s); } out: @@ -5200,11 +6648,13 @@ static void *stratum_rthread(void *userdata) static void *stratum_sthread(void *userdata) { struct pool *pool = (struct pool *)userdata; + uint64_t last_nonce2 = 0; + uint32_t last_nonce = 0; char threadname[16]; pthread_detach(pthread_self()); - snprintf(threadname, 16, "StratumS/%d", pool->pool_no); + snprintf(threadname, sizeof(threadname), "%d/SStratum", pool->pool_no); RenameThread(threadname); pool->stratum_q = tq_new(); @@ -5212,12 +6662,13 @@ static void *stratum_sthread(void *userdata) quit(1, "Failed to create stratum_q in stratum_sthread"); while (42) { - char *noncehex, *nonce2, *nonce2hex; + char noncehex[12], nonce2hex[20], s[1024]; struct stratum_share *sshare; uint32_t *hash32, nonce; + unsigned char nonce2[8]; + uint64_t *nonce2_64; struct work *work; bool submitted; - char s[1024]; if (unlikely(pool->removed)) break; @@ -5226,6 +6677,29 @@ static void *stratum_sthread(void *userdata) if (unlikely(!work)) quit(1, "Stratum q returned empty work"); + if (unlikely(work->nonce2_len > 8)) { + applog(LOG_ERR, "Pool %d asking for inappropriately long nonce2 length %d", + pool->pool_no, (int)work->nonce2_len); + applog(LOG_ERR, "Not attempting to submit shares"); + free_work(work); + continue; + } + + nonce = *((uint32_t *)(work->data + 76)); + nonce2_64 = (uint64_t *)nonce2; + *nonce2_64 = htole64(work->nonce2); + /* Filter out duplicate shares */ + if (unlikely(nonce == last_nonce && *nonce2_64 == last_nonce2)) { + applog(LOG_INFO, "Filtering duplicate share to pool %d", + pool->pool_no); + free_work(work); + continue; + } + last_nonce = nonce; + last_nonce2 = *nonce2_64; + __bin2hex(noncehex, (const unsigned char *)&nonce, 4); + __bin2hex(nonce2hex, nonce2, work->nonce2_len); + sshare = calloc(sizeof(struct stratum_share), 1); hash32 = (uint32_t *)work->hash; submitted = false; @@ -5233,8 +6707,6 @@ static void *stratum_sthread(void *userdata) sshare->sshare_time = time(NULL); /* This work item is freed in parse_stratum_response */ sshare->work = work; - nonce = *((uint32_t *)(work->data + 76)); - noncehex = bin2hex((const unsigned char *)&nonce, 4); memset(s, 0, 1024); mutex_lock(&sshare_lock); @@ -5242,18 +6714,9 @@ static void *stratum_sthread(void *userdata) sshare->id = swork_id++; mutex_unlock(&sshare_lock); - nonce2 = alloca(work->nonce2_len); - memset(nonce2, 0, work->nonce2_len); - memcpy(nonce2, &work->nonce2, sizeof(uint32_t)); - nonce2hex = bin2hex((const unsigned char *)nonce2, work->nonce2_len); - if (unlikely(!nonce2hex)) - quit(1, "Failed to bin2hex nonce2 in stratum_thread"); - snprintf(s, sizeof(s), "{\"params\": [\"%s\", \"%s\", \"%s\", \"%s\", \"%s\"], \"id\": %d, \"method\": \"mining.submit\"}", pool->rpc_user, work->job_id, nonce2hex, work->ntime, noncehex, sshare->id); - free(noncehex); - free(nonce2hex); applog(LOG_INFO, "Submitting share %08lx to pool %d", (long unsigned int)htole32(hash32[6]), pool->pool_no); @@ -5265,14 +6728,13 @@ static void *stratum_sthread(void *userdata) bool sessionid_match; if (likely(stratum_send(pool, s, strlen(s)))) { - if (pool_tclear(pool, &pool->submit_fail)) - applog(LOG_WARNING, "Pool %d communication resumed, submitting work", pool->pool_no); - mutex_lock(&sshare_lock); HASH_ADD_INT(stratum_shares, id, sshare); pool->sshares++; mutex_unlock(&sshare_lock); + if (pool_tclear(pool, &pool->submit_fail)) + applog(LOG_WARNING, "Pool %d communication resumed, submitting work", pool->pool_no); applog(LOG_DEBUG, "Successfully submitted, adding to stratum_shares db"); submitted = true; break; @@ -5306,6 +6768,15 @@ static void *stratum_sthread(void *userdata) free(sshare); pool->stale_shares++; total_stale++; + } else { + int ssdiff; + + sshare->sshare_sent = time(NULL); + ssdiff = sshare->sshare_sent - sshare->sshare_time; + if (opt_debug || ssdiff > 0) { + applog(LOG_INFO, "Pool %d stratum share submission lag time %d seconds", + pool->pool_no, ssdiff); + } } } @@ -5318,6 +6789,8 @@ static void *stratum_sthread(void *userdata) static void init_stratum_threads(struct pool *pool) { + have_longpoll = true; + if (unlikely(pthread_create(&pool->stratum_sthread, NULL, stratum_sthread, (void *)pool))) quit(1, "Failed to create stratum sthread"); if (unlikely(pthread_create(&pool->stratum_rthread, NULL, stratum_rthread, (void *)pool))) @@ -5329,7 +6802,8 @@ static void *longpoll_thread(void *userdata); static bool stratum_works(struct pool *pool) { applog(LOG_INFO, "Testing pool %d stratum %s", pool->pool_no, pool->stratum_url); - if (!extract_sockaddr(pool, pool->stratum_url)) + check_extranonce_option(pool, pool->stratum_url); + if (!extract_sockaddr(pool->stratum_url, &pool->sockaddr_url, &pool->stratum_port)) return false; if (!initiate_stratum(pool)) @@ -5338,13 +6812,87 @@ static bool stratum_works(struct pool *pool) return true; } +#ifdef HAVE_LIBCURL +static void __setup_gbt_solo(struct pool *pool) +{ + cg_wlock(&pool->gbt_lock); + memcpy(pool->coinbase, scriptsig_header_bin, 41); + pool->coinbase[41 + pool->n1_len + 4 + 1 + 8] = 25; + memcpy(pool->coinbase + 41 + pool->n1_len + 4 + 1 + 8 + 1, pool->script_pubkey, 25); + cg_wunlock(&pool->gbt_lock); +} + +static bool setup_gbt_solo(CURL *curl, struct pool *pool) +{ + char s[256]; + int uninitialised_var(rolltime); + bool ret = false; + json_t *val = NULL, *res_val, *valid_val; + + if (!opt_btc_address) { + applog(LOG_ERR, "No BTC address specified, unable to mine solo on %s", + pool->rpc_url); + goto out; + } + snprintf(s, 256, "{\"method\": \"validateaddress\", \"params\": [\"%s\"]}\n", opt_btc_address); + val = json_rpc_call(curl, pool->rpc_url, pool->rpc_userpass, s, true, + false, &rolltime, pool, false); + if (!val) + goto out; + res_val = json_object_get(val, "result"); + if (!res_val) + goto out; + valid_val = json_object_get(res_val, "isvalid"); + if (!valid_val) + goto out; + if (!json_is_true(valid_val)) { + applog(LOG_ERR, "Bitcoin address %s is NOT valid", opt_btc_address); + goto out; + } + applog(LOG_NOTICE, "Solo mining to valid address: %s", opt_btc_address); + ret = true; + address_to_pubkeyhash(pool->script_pubkey, opt_btc_address); + hex2bin(scriptsig_header_bin, scriptsig_header, 41); + __setup_gbt_solo(pool); + + if (opt_debug) { + char *cb = bin2hex(pool->coinbase, pool->coinbase_len); + + applog(LOG_DEBUG, "Pool %d coinbase %s", pool->pool_no, cb); + free(cb); + } + pool->gbt_curl = curl_easy_init(); + if (unlikely(!pool->gbt_curl)) + quit(1, "GBT CURL initialisation failed"); + +out: + if (val) + json_decref(val); + return ret; +} +#else +static bool setup_gbt_solo(CURL __maybe_unused *curl, struct pool __maybe_unused *pool) +{ + return false; +} +#endif + +static void pool_start_lp(struct pool *pool) +{ + if (!pool->lp_started) { + pool->lp_started = true; + if (unlikely(pthread_create(&pool->longpoll_thread, NULL, longpoll_thread, (void *)pool))) + quit(1, "Failed to create pool longpoll thread"); + } +} + static bool pool_active(struct pool *pool, bool pinging) { struct timeval tv_getwork, tv_getwork_reply; + json_t *val = NULL; bool ret = false; - json_t *val; CURL *curl; - int rolltime; + int uninitialised_var(rolltime); if (pool->has_gbt) applog(LOG_DEBUG, "Retrieving block template from pool %s", pool->rpc_url); @@ -5362,7 +6910,7 @@ static bool pool_active(struct pool *pool, bool pinging) if (!init) { bool ret = initiate_stratum(pool) && auth_stratum(pool); - + extranonce_subscribe_stratum(pool); if (ret) init_stratum_threads(pool); else @@ -5379,12 +6927,12 @@ static bool pool_active(struct pool *pool, bool pinging) } /* Probe for GBT support on first pass */ - if (!pool->probed && !opt_fix_protocol) { + if (!pool->probed) { applog(LOG_DEBUG, "Probing for GBT support"); val = json_rpc_call(curl, pool->rpc_url, pool->rpc_userpass, gbt_req, true, false, &rolltime, pool, false); if (val) { - bool append = false, submit = false; + bool append = false, submit = false, transactions = false; json_t *res_val, *mutables; int i, mutsize = 0; @@ -5404,6 +6952,8 @@ static bool pool_active(struct pool *pool, bool pinging) append = true; else if (!strncasecmp(mutable, "submit/coinbase", 15)) submit = true; + else if (!strncasecmp(mutable, "transactions", 12)) + transactions = true; } } json_decref(val); @@ -5413,6 +6963,9 @@ static bool pool_active(struct pool *pool, bool pinging) if (append && submit) { pool->has_gbt = true; pool->rpc_req = gbt_req; + } else if (transactions) { + pool->gbt_solo = true; + pool->rpc_req = gbt_solo_req; } } /* Reset this so we can probe fully just after this. It will be @@ -5421,6 +6974,8 @@ static bool pool_active(struct pool *pool, bool pinging) if (pool->has_gbt) applog(LOG_DEBUG, "GBT coinbase + append support found, switching to GBT protocol"); + else if (pool->gbt_solo) + applog(LOG_DEBUG, "GBT coinbase without append found, switching to GBT solo protocol"); else applog(LOG_DEBUG, "No GBT coinbase + append support found, using getwork protocol"); } @@ -5448,6 +7003,13 @@ static bool pool_active(struct pool *pool, bool pinging) rc = work_decode(pool, work, val); if (rc) { + if (pool->gbt_solo) { + ret = setup_gbt_solo(curl, pool); + if (ret) + pool_start_lp(pool); + free_work(work); + goto out; + } applog(LOG_DEBUG, "Successfully retrieved and deciphered work from pool %u %s", pool->pool_no, pool->rpc_url); work->pool = pool; @@ -5458,17 +7020,15 @@ static bool pool_active(struct pool *pool, bool pinging) calc_diff(work, 0); applog(LOG_DEBUG, "Pushing pooltest work to base pool"); - tq_push(control_thr[stage_thr_id].q, work); + stage_work(work); total_getworks++; pool->getwork_requested++; ret = true; - cgtime(&pool->tv_idle); } else { applog(LOG_DEBUG, "Successfully retrieved but FAILED to decipher work from pool %u %s", pool->pool_no, pool->rpc_url); free_work(work); } - json_decref(val); if (pool->lp_url) goto out; @@ -5501,11 +7061,7 @@ static bool pool_active(struct pool *pool, bool pinging) } else pool->lp_url = NULL; - if (!pool->lp_started) { - pool->lp_started = true; - if (unlikely(pthread_create(&pool->longpoll_thread, NULL, longpoll_thread, (void *)pool))) - quit(1, "Failed to create pool longpoll thread"); - } + pool_start_lp(pool); } else { /* If we failed to parse a getwork, this could be a stratum * url without the prefix stratum+tcp:// so let's check it */ @@ -5515,48 +7071,64 @@ static bool pool_active(struct pool *pool, bool pinging) } applog(LOG_DEBUG, "FAILED to retrieve work from pool %u %s", pool->pool_no, pool->rpc_url); - if (!pinging) + if (!pinging && !pool->idle) applog(LOG_WARNING, "Pool %u slow/down or URL or credentials invalid", pool->pool_no); } out: + if (val) + json_decref(val); curl_easy_cleanup(curl); return ret; } static void pool_resus(struct pool *pool) { - if (pool_strategy == POOL_FAILOVER && pool->prio < cp_prio()) { - applog(LOG_WARNING, "Pool %d %s alive", pool->pool_no, pool->rpc_url); - switch_pools(NULL); - } else + pool->seq_getfails = 0; + if (pool_strategy == POOL_FAILOVER && pool->prio < cp_prio()) + applog(LOG_WARNING, "Pool %d %s alive, testing stability", pool->pool_no, pool->rpc_url); + else applog(LOG_INFO, "Pool %d %s alive", pool->pool_no, pool->rpc_url); } -static struct work *hash_pop(void) +static bool work_filled; +static bool work_emptied; + +/* If this is called non_blocking, it will return NULL for work so that must + * be handled. */ +static struct work *hash_pop(bool blocking) { struct work *work = NULL, *tmp; int hc; mutex_lock(stgd_lock); - while (!HASH_COUNT(staged_work)) { - if (!no_work) { + if (!HASH_COUNT(staged_work)) { + /* Increase the queue if we reach zero and we know we can reach + * the maximum we're asking for. */ + if (work_filled && max_queue < opt_queue) { + max_queue++; + work_filled = false; + } + work_emptied = true; + if (!blocking) + goto out_unlock; + do { struct timespec then; struct timeval now; int rc; cgtime(&now); - then.tv_sec = now.tv_sec + 5; + then.tv_sec = now.tv_sec + 10; then.tv_nsec = now.tv_usec * 1000; + pthread_cond_signal(&gws_cond); rc = pthread_cond_timedwait(&getq->cond, stgd_lock, &then); /* Check again for !no_work as multiple threads may be - * waiting on this condition and another may set the - * bool separately. */ + * waiting on this condition and another may set the + * bool separately. */ if (rc && !no_work) { - applog(LOG_WARNING, "Waiting for work to be available from pools."); no_work = true; + applog(LOG_WARNING, "Waiting for work to be available from pools."); } - } else - pthread_cond_wait(&getq->cond, stgd_lock); + } while (!HASH_COUNT(staged_work)); } if (no_work) { @@ -5582,120 +7154,289 @@ static struct work *hash_pop(void) /* Signal hash_pop again in case there are mutliple hash_pop waiters */ pthread_cond_signal(&getq->cond); + + /* Keep track of last getwork grabbed */ + last_getwork = time(NULL); +out_unlock: mutex_unlock(stgd_lock); return work; } -/* Clones work by rolling it if possible, and returning a clone instead of the - * original work item which gets staged again to possibly be rolled again in - * the future */ -static struct work *clone_work(struct work *work) +static void gen_hash(unsigned char *data, unsigned char *hash, int len) { - int mrs = mining_threads + opt_queue - total_staged(); - struct work *work_clone; - bool cloned; + unsigned char hash1[32]; - if (mrs < 1) - return work; + sha256(data, len, hash1); + sha256(hash1, 32, hash); +} - cloned = false; - work_clone = make_clone(work); - while (mrs-- > 0 && can_roll(work) && should_roll(work)) { - applog(LOG_DEBUG, "Pushing rolled converted work to stage thread"); - stage_work(work_clone); - roll_work(work); - work_clone = make_clone(work); - /* Roll it again to prevent duplicates should this be used - * directly later on */ +void set_target(unsigned char *dest_target, double diff) +{ + unsigned char target[32]; + uint64_t *data64, h64; + double d64, dcut64; + + if (unlikely(diff == 0.0)) { + /* This shouldn't happen but best we check to prevent a crash */ + applog(LOG_ERR, "Diff zero passed to set_target"); + diff = 1.0; + } + + d64 = truediffone; + d64 /= diff; + + dcut64 = d64 / bits192; + h64 = dcut64; + data64 = (uint64_t *)(target + 24); + *data64 = htole64(h64); + dcut64 = h64; + dcut64 *= bits192; + d64 -= dcut64; + + dcut64 = d64 / bits128; + h64 = dcut64; + data64 = (uint64_t *)(target + 16); + *data64 = htole64(h64); + dcut64 = h64; + dcut64 *= bits128; + d64 -= dcut64; + + dcut64 = d64 / bits64; + h64 = dcut64; + data64 = (uint64_t *)(target + 8); + *data64 = htole64(h64); + dcut64 = h64; + dcut64 *= bits64; + d64 -= dcut64; + + h64 = d64; + data64 = (uint64_t *)(target); + *data64 = htole64(h64); + + if (opt_debug) { + char *htarget = bin2hex(target, 32); + + applog(LOG_DEBUG, "Generated target %s", htarget); + free(htarget); + } + memcpy(dest_target, target, 32); +} + +#if defined (USE_AVALON2) || defined (USE_AVALON4) || 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) +{ + const int thr_id = thr->id; + struct cgpu_info *cgpu = thr->cgpu; + struct device_drv *drv = cgpu->drv; + struct work *work = make_work(); + bool ret; + + cg_wlock(&pool->data_lock); + pool->nonce2 = nonce2; + cg_wunlock(&pool->data_lock); + + gen_stratum_work(pool, work); + while (ntime--) { roll_work(work); - cloned = true; } - if (cloned) { - stage_work(work); - return work_clone; + work->pool = real_pool; + + work->thr_id = thr_id; + work->work_block = work_block; + work->pool->works++; + + work->mined = true; + work->device_diff = MIN(drv->max_diff, work->work_difficulty); + work->device_diff = MAX(drv->min_diff, work->device_diff); + + ret = submit_nonce(thr, work, nonce); + free_work(work); + return ret; +} +#endif + +/* Generates stratum based work based on the most recent notify information + * from the pool. This will keep generating work while a pool is down so we use + * other means to detect when the pool has died in stratum_thread */ +static void gen_stratum_work(struct pool *pool, struct work *work) +{ + unsigned char merkle_root[32], merkle_sha[64]; + uint32_t *data32, *swap32; + uint64_t nonce2le; + int i; + + cg_wlock(&pool->data_lock); + + /* Update coinbase. Always use an LE encoded nonce2 to fill in values + * from left to right and prevent overflow errors with small n2sizes */ + nonce2le = htole64(pool->nonce2); + memcpy(pool->coinbase + pool->nonce2_offset, &nonce2le, pool->n2size); + work->nonce2 = pool->nonce2++; + work->nonce2_len = pool->n2size; + + /* Downgrade to a read lock to read off the pool variables */ + cg_dwlock(&pool->data_lock); + + /* Generate merkle root */ + gen_hash(pool->coinbase, merkle_root, pool->coinbase_len); + memcpy(merkle_sha, merkle_root, 32); + for (i = 0; i < pool->merkles; i++) { + memcpy(merkle_sha + 32, pool->swork.merkle_bin[i], 32); + gen_hash(merkle_sha, merkle_root, 64); + memcpy(merkle_sha, merkle_root, 32); } + data32 = (uint32_t *)merkle_sha; + swap32 = (uint32_t *)merkle_root; + flip32(swap32, data32); - free_work(work_clone); + /* Copy the data template from header_bin */ + memcpy(work->data, pool->header_bin, 112); + memcpy(work->data + 36, merkle_root, 32); - return work; + /* Store the stratum work diff to check it still matches the pool's + * stratum diff when submitting shares */ + work->sdiff = pool->sdiff; + + /* Copy parameters required for share submission */ + work->job_id = strdup(pool->swork.job_id); + work->nonce1 = strdup(pool->nonce1); + work->ntime = strdup(pool->ntime); + cg_runlock(&pool->data_lock); + + if (opt_debug) { + char *header, *merkle_hash; + + header = bin2hex(work->data, 112); + merkle_hash = bin2hex((const unsigned char *)merkle_root, 32); + applog(LOG_DEBUG, "Generated stratum merkle %s", merkle_hash); + applog(LOG_DEBUG, "Generated stratum header %s", header); + applog(LOG_DEBUG, "Work job_id %s nonce2 %"PRIu64" ntime %s", work->job_id, + work->nonce2, work->ntime); + free(header); + free(merkle_hash); + } + + calc_midstate(work); + set_target(work->target, work->sdiff); + + local_work++; + if((time(NULL) - local_work_lasttime) > 5) { + int diff = local_work - local_work_last; + applog(LOG_DEBUG, "local_work 5s gen work count:%d", diff/(time(NULL) - local_work_lasttime)); + local_work_lasttime = time(NULL); + local_work_last = local_work; + } + + work->pool = pool; + work->stratum = true; + work->nonce = 0; + work->longpoll = false; + work->getwork_mode = GETWORK_MODE_STRATUM; + work->work_block = work_block; + /* Nominally allow a driver to ntime roll 60 seconds */ + work->drv_rolllimit = 60; + calc_diff(work, work->sdiff); + + cgtime(&work->tv_staged); } -static void gen_hash(unsigned char *data, unsigned char *hash, int len) +#ifdef HAVE_LIBCURL +static void gen_solo_work(struct pool *pool, struct work *work); + +/* Use the one instance of gbt_curl, protecting the bool with the gbt_lock but + * avoiding holding the lock once we've set the bool. */ +static void get_gbt_curl(struct pool *pool, int poll) { - unsigned char hash1[32]; + cg_ilock(&pool->gbt_lock); + while (pool->gbt_curl_inuse) { + cg_uilock(&pool->gbt_lock); + cgsleep_ms(poll); + cg_ilock(&pool->gbt_lock); + } + cg_ulock(&pool->gbt_lock); + pool->gbt_curl_inuse = true; + cg_wunlock(&pool->gbt_lock); +} - sha256(data, len, hash1); - sha256(hash1, 32, hash); +/* No need for locking here */ +static inline void release_gbt_curl(struct pool *pool) +{ + pool->gbt_curl_inuse = false; } -/* Diff 1 is a 256 bit unsigned integer of - * 0x00000000ffff0000000000000000000000000000000000000000000000000000 - * so we use a big endian 64 bit unsigned integer centred on the 5th byte to - * cover a huge range of difficulty targets, though not all 256 bits' worth */ -void set_target(unsigned char *dest_target, double diff) +static void update_gbt_solo(struct pool *pool) { - unsigned char target[32]; - uint64_t *data64, h64; - double d64; + struct work *work = make_work(); + int rolltime; + json_t *val; - d64 = diffone; - d64 /= diff; - h64 = d64; + get_gbt_curl(pool, 10); +retry: + /* Bitcoind doesn't like many open RPC connections. */ + curl_easy_setopt(pool->gbt_curl, CURLOPT_FORBID_REUSE, 1); + val = json_rpc_call(pool->gbt_curl, pool->rpc_url, pool->rpc_userpass, pool->rpc_req, + true, false, &rolltime, pool, false); - memset(target, 0, 32); - if (h64) { - unsigned char rtarget[32]; + if (likely(val)) { + bool rc = work_decode(pool, work, val); - memset(rtarget, 0, 32); - if (opt_scrypt) - data64 = (uint64_t *)(rtarget + 2); - else - data64 = (uint64_t *)(rtarget + 4); - *data64 = htobe64(h64); - swab256(target, rtarget); + if (rc) { + __setup_gbt_solo(pool); + gen_solo_work(pool, work); + stage_work(work); + } else + free_work(work); + json_decref(val); } else { - /* Support for the classic all FFs just-below-1 diff */ - if (opt_scrypt) - memset(target, 0xff, 30); - else - memset(target, 0xff, 28); - } - - if (opt_debug) { - char *htarget = bin2hex(target, 32); - - applog(LOG_DEBUG, "Generated target %s", htarget); - free(htarget); + applog(LOG_DEBUG, "Pool %d json_rpc_call failed on get gbt, retrying in 5s", + pool->pool_no); + if (++pool->seq_getfails > 5) { + pool_died(pool); + goto out; + } + cgsleep_ms(5000); + goto retry; } - memcpy(dest_target, target, 32); +out: + release_gbt_curl(pool); } -/* Generates stratum based work based on the most recent notify information - * from the pool. This will keep generating work while a pool is down so we use - * other means to detect when the pool has died in stratum_thread */ -static void gen_stratum_work(struct pool *pool, struct work *work) +static void gen_solo_work(struct pool *pool, struct work *work) { unsigned char merkle_root[32], merkle_sha[64]; uint32_t *data32, *swap32; + struct timeval now; + uint64_t nonce2le; int i; - cg_wlock(&pool->data_lock); + cgtime(&now); + if (now.tv_sec - pool->tv_lastwork.tv_sec > 60) + update_gbt_solo(pool); - /* Update coinbase */ - memcpy(pool->coinbase + pool->nonce2_offset, &pool->nonce2, sizeof(uint32_t)); + cg_wlock(&pool->gbt_lock); + + /* Update coinbase. Always use an LE encoded nonce2 to fill in values + * from left to right and prevent overflow errors with small n2sizes */ + nonce2le = htole64(pool->nonce2); + memcpy(pool->coinbase + pool->nonce2_offset, &nonce2le, pool->n2size); work->nonce2 = pool->nonce2++; work->nonce2_len = pool->n2size; + work->gbt_txns = pool->transactions + 1; /* Downgrade to a read lock to read off the pool variables */ - cg_dwlock(&pool->data_lock); - + cg_dwlock(&pool->gbt_lock); + work->coinbase = bin2hex(pool->coinbase, pool->coinbase_len); /* Generate merkle root */ - gen_hash(pool->coinbase, merkle_root, pool->swork.cb_len); + gen_hash(pool->coinbase, merkle_root, pool->coinbase_len); memcpy(merkle_sha, merkle_root, 32); - for (i = 0; i < pool->swork.merkles; i++) { - memcpy(merkle_sha + 32, pool->swork.merkle_bin[i], 32); + for (i = 0; i < pool->merkles; i++) { + unsigned char *merkle_bin; + + merkle_bin = pool->merklebin + (i * 32); + memcpy(merkle_sha + 32, merkle_bin, 32); gen_hash(merkle_sha, merkle_root, 64); memcpy(merkle_sha, merkle_root, 32); } @@ -5704,77 +7445,127 @@ static void gen_stratum_work(struct pool *pool, struct work *work) flip32(swap32, data32); /* Copy the data template from header_bin */ - memcpy(work->data, pool->header_bin, 128); - memcpy(work->data + pool->merkle_offset, merkle_root, 32); + memcpy(work->data, pool->header_bin, 112); + memcpy(work->data + 36, merkle_root, 32); - /* Store the stratum work diff to check it still matches the pool's - * stratum diff when submitting shares */ - work->sdiff = pool->swork.diff; + work->sdiff = pool->sdiff; /* Copy parameters required for share submission */ - work->job_id = strdup(pool->swork.job_id); - work->nonce1 = strdup(pool->nonce1); - work->ntime = strdup(pool->swork.ntime); - cg_runlock(&pool->data_lock); + work->ntime = strdup(pool->ntime); + memcpy(work->target, pool->gbt_target, 32); + cg_runlock(&pool->gbt_lock); if (opt_debug) { char *header, *merkle_hash; - header = bin2hex(work->data, 128); + header = bin2hex(work->data, 112); merkle_hash = bin2hex((const unsigned char *)merkle_root, 32); - applog(LOG_DEBUG, "Generated stratum merkle %s", merkle_hash); - applog(LOG_DEBUG, "Generated stratum header %s", header); - applog(LOG_DEBUG, "Work job_id %s nonce2 %d ntime %s", work->job_id, work->nonce2, work->ntime); + applog(LOG_DEBUG, "Generated GBT solo merkle %s", merkle_hash); + applog(LOG_DEBUG, "Generated GBT solo header %s", header); + applog(LOG_DEBUG, "Work nonce2 %"PRIu64" ntime %s", work->nonce2, + work->ntime); free(header); free(merkle_hash); } calc_midstate(work); - set_target(work->target, work->sdiff); local_work++; + work->gbt = true; work->pool = pool; - work->stratum = true; - work->blk.nonce = 0; - work->id = total_work++; + work->nonce = 0; work->longpoll = false; - work->getwork_mode = GETWORK_MODE_STRATUM; + work->getwork_mode = GETWORK_MODE_SOLO; work->work_block = work_block; + /* Nominally allow a driver to ntime roll 60 seconds */ + work->drv_rolllimit = 60; calc_diff(work, work->sdiff); cgtime(&work->tv_staged); } +#endif + +/* The time difference in seconds between when this device last got work via + * get_work() and generated a valid share. */ +int share_work_tdiff(struct cgpu_info *cgpu) +{ + return last_getwork - cgpu->last_device_valid_work; +} + +static void set_benchmark_work(struct cgpu_info *cgpu, struct work *work) +{ + cgpu->lodiff += cgpu->direction; + if (cgpu->lodiff < 1) + cgpu->direction = 1; + if (cgpu->lodiff > 15) { + cgpu->direction = -1; + if (++cgpu->hidiff > 15) + cgpu->hidiff = 0; + memcpy(work, &bench_hidiff_bins[cgpu->hidiff][0], 160); + } else + memcpy(work, &bench_lodiff_bins[cgpu->lodiff][0], 160); +} -static struct work *get_work(struct thr_info *thr, const int thr_id) +struct work *get_work(struct thr_info *thr, const int thr_id) { + struct cgpu_info *cgpu = thr->cgpu; struct work *work = NULL; + time_t diff_t; thread_reportout(thr); applog(LOG_DEBUG, "Popping work from get queue to get work"); + diff_t = time(NULL); while (!work) { - work = hash_pop(); + work = hash_pop(true); if (stale_work(work, false)) { discard_work(work); - work = NULL; wake_gws(); } } + diff_t = time(NULL) - diff_t; + /* Since this is a blocking function, we need to add grace time to + * the device's last valid work to not make outages appear to be + * device failures. */ + if (diff_t > 0) { + applog(LOG_DEBUG, "Get work blocked for %d seconds", (int)diff_t); + cgpu->last_device_valid_work += diff_t; + } applog(LOG_DEBUG, "Got work from get queue to get work for thread %d", thr_id); work->thr_id = thr_id; + if (opt_benchmark) + set_benchmark_work(cgpu, work); + thread_reportin(thr); work->mined = true; + work->device_diff = MIN(cgpu->drv->max_diff, work->work_difficulty); + work->device_diff = MAX(cgpu->drv->min_diff, work->device_diff); return work; } -static void submit_work_async(struct work *work_in, struct timeval *tv_work_found) +/* Submit a copy of the tested, statistic recorded work item asynchronously */ +static void submit_work_async(struct work *work) { - struct work *work = copy_work(work_in); struct pool *pool = work->pool; pthread_t submit_thread; - if (tv_work_found) - copy_time(&work->tv_work_found, tv_work_found); + cgtime(&work->tv_work_found); + if (opt_benchmark) { + struct cgpu_info *cgpu = get_thr_cgpu(work->thr_id); + + mutex_lock(&stats_lock); + cgpu->accepted++; + total_accepted++; + pool->accepted++; + cgpu->diff_accepted += work->work_difficulty; + total_diff_accepted += work->work_difficulty; + pool->diff_accepted += work->work_difficulty; + mutex_unlock(&stats_lock); + + applog(LOG_NOTICE, "Accepted %s %d benchmark share nonce %08x", + cgpu->drv->name, cgpu->device_id, *(uint32_t *)(work->data + 64 + 12)); + return; + } if (stale_work(work, true)) { if (opt_submit_stale) @@ -5813,6 +7604,9 @@ static void submit_work_async(struct work *work_in, struct timeval *tv_work_foun void inc_hw_errors(struct thr_info *thr) { + applog(LOG_INFO, "%s %d: invalid nonce - HW error", thr->cgpu->drv->name, + thr->cgpu->device_id); + mutex_lock(&stats_lock); hw_errors++; thr->cgpu->hw_errors++; @@ -5821,31 +7615,57 @@ void inc_hw_errors(struct thr_info *thr) thr->cgpu->drv->hw_error(thr); } -/* Returns true if nonce for work was a valid share */ -bool submit_nonce(struct thr_info *thr, struct work *work, uint32_t nonce) +void inc_dev_status(int max_fan, int max_temp) +{ + mutex_lock(&stats_lock); + g_max_fan = max_fan; + g_max_temp = max_temp; + mutex_unlock(&stats_lock); +} + +/* Fills in the work nonce and builds the output data in work->hash */ +static void rebuild_nonce(struct work *work, uint32_t nonce) { uint32_t *work_nonce = (uint32_t *)(work->data + 64 + 12); - struct timeval tv_work_found; - unsigned char hash2[32]; - uint32_t *hash2_32 = (uint32_t *)hash2; - uint32_t diff1targ; - bool ret = true; - cgtime(&tv_work_found); *work_nonce = htole32(nonce); - /* Do one last check before attempting to submit the work */ - rebuild_hash(work); - flip32(hash2_32, work->hash); + regen_hash(work); +} - diff1targ = opt_scrypt ? 0x0000ffffUL : 0; - if (be32toh(hash2_32[7]) > diff1targ) { - applog(LOG_INFO, "%s%d: invalid nonce - HW error", - thr->cgpu->drv->name, thr->cgpu->device_id); +/* For testing a nonce against diff 1 */ +bool test_nonce(struct work *work, uint32_t nonce) +{ + uint32_t *hash_32 = (uint32_t *)(work->hash + 28); - inc_hw_errors(thr); - ret = false; - goto out; + rebuild_nonce(work, nonce); + return (*hash_32 == 0); +} + +/* For testing a nonce against an arbitrary diff */ +bool test_nonce_diff(struct work *work, uint32_t nonce, double diff) +{ + uint64_t *hash64 = (uint64_t *)(work->hash + 24), diff64; + + rebuild_nonce(work, nonce); + diff64 = 0x00000000ffff0000ULL; + diff64 /= diff; + + return (le64toh(*hash64) <= diff64); +} + +static void update_work_stats(struct thr_info *thr, struct work *work) +{ + double test_diff = current_diff; + + work->share_diff = share_diff(work); + + if (unlikely(work->share_diff >= test_diff)) { + work->block = true; + work->pool->solved++; + found_blocks++; + work->mandatory = true; + applog(LOG_NOTICE, "Found block for pool %d!", work->pool->pool_no); } mutex_lock(&stats_lock); @@ -5854,22 +7674,145 @@ bool submit_nonce(struct thr_info *thr, struct work *work, uint32_t nonce) work->pool->diff1 += work->device_diff; thr->cgpu->last_device_valid_work = time(NULL); mutex_unlock(&stats_lock); +} + +void inc_work_stats(struct thr_info *thr, struct pool *pool, int diff1) +{ + mutex_lock(&stats_lock); + total_diff1 += diff1; + thr->cgpu->diff1 += diff1; + if(pool) { + pool->diff1 += diff1; + } else { + pool = current_pool(); + pool->diff1 += diff1; + } + thr->cgpu->last_device_valid_work = time(NULL); + mutex_unlock(&stats_lock); +} + + +/* To be used once the work has been tested to be meet diff1 and has had its + * nonce adjusted. Returns true if the work target is met. */ +bool submit_tested_work(struct thr_info *thr, struct work *work) +{ + struct work *work_out; + update_work_stats(thr, work); + + if (!fulltest(work->hash, work->target)) { + applog(LOG_INFO, "%s %d: Share above target", thr->cgpu->drv->name, + thr->cgpu->device_id); + return false; + } + work_out = copy_work(work); + submit_work_async(work_out); + return true; +} + +/* Rudimentary test to see if cgpu has returned the same nonce twice in a row which is + * always going to be a duplicate which should be reported as a hw error. */ +static bool new_nonce(struct thr_info *thr, uint32_t nonce) +{ + struct cgpu_info *cgpu = thr->cgpu; + + if (unlikely(cgpu->last_nonce == nonce)) { + applog(LOG_INFO, "%s %d duplicate share detected as HW error", + cgpu->drv->name, cgpu->device_id); + return false; + } + cgpu->last_nonce = nonce; + return true; +} + +/* Returns true if nonce for work was a valid share and not a dupe of the very last + * nonce submitted by this device. */ +bool submit_nonce(struct thr_info *thr, struct work *work, uint32_t nonce) +{ + if (new_nonce(thr, nonce) && test_nonce(work, nonce)) + submit_tested_work(thr, work); + else { + inc_hw_errors(thr); + return false; + } + + if (opt_benchfile && opt_benchfile_display) + benchfile_dspwork(work, nonce); + + return true; +} + +bool submit_nonce_1(struct thr_info *thr, struct work *work, uint32_t nonce, int * nofull) +{ + if(nofull) *nofull = 0; + if (test_nonce(work, nonce)) { + update_work_stats(thr, work); + if (!fulltest(work->hash, work->target)) { + if(nofull) *nofull = 1; + applog(LOG_INFO, "Share above target"); + return false; + } + } else { + inc_hw_errors(thr); + return false; + } + return true; +} + +void submit_nonce_2(struct work *work) +{ + struct work *work_out; + work_out = copy_work(work); + submit_work_async(work_out); +} + +bool submit_nonce_direct(struct thr_info *thr, struct work *work, uint32_t nonce) +{ + struct work *work_out; + uint32_t *work_nonce = (uint32_t *)(work->data + 64 + 12); + *work_nonce = htole32(nonce); + + work_out = copy_work(work); + submit_work_async(work_out); + return true; +} - if (!fulltest(hash2, work->target)) { - applog(LOG_INFO, "Share below target"); +/* Allows drivers to submit work items where the driver has changed the ntime + * value by noffset. Must be only used with a work protocol that does not ntime + * roll itself intrinsically to generate work (eg stratum). We do not touch + * the original work struct, but the copy of it only. */ +bool submit_noffset_nonce(struct thr_info *thr, struct work *work_in, uint32_t nonce, + int noffset) +{ + struct work *work = make_work(); + bool ret = false; + + _copy_work(work, work_in, noffset); + if (!test_nonce(work, nonce)) { + free_work(work); + inc_hw_errors(thr); goto out; } + update_work_stats(thr, work); + + if (opt_benchfile && opt_benchfile_display) + benchfile_dspwork(work, nonce); + + ret = true; + if (!fulltest(work->hash, work->target)) { + free_work(work); + applog(LOG_INFO, "%s %d: Share above target", thr->cgpu->drv->name, + thr->cgpu->device_id); + goto out; + } + submit_work_async(work); - submit_work_async(work, &tv_work_found); out: return ret; } static inline bool abandon_work(struct work *work, struct timeval *wdiff, uint64_t hashes) { - if (wdiff->tv_sec > opt_scantime || - work->blk.nonce >= MAXTHREADS - hashes || - hashes >= 0xfffffffe || + if (wdiff->tv_sec > opt_scantime || hashes >= 0xfffffffe || stale_work(work, false)) return true; return false; @@ -5879,7 +7822,7 @@ static void mt_disable(struct thr_info *mythr, const int thr_id, struct device_drv *drv) { applog(LOG_WARNING, "Thread %d being disabled", thr_id); - mythr->rolling = mythr->cgpu->rolling = 0; + mythr->cgpu->rolling = 0; applog(LOG_DEBUG, "Waiting on sem in miner thread"); cgsem_wait(&mythr->sem); applog(LOG_WARNING, "Thread %d being re-enabled", thr_id); @@ -5917,33 +7860,15 @@ static void hash_sole_work(struct thr_info *mythr) cgpu->new_work = true; cgtime(&tv_workstart); - work->blk.nonce = 0; + work->nonce = 0; cgpu->max_hashes = 0; if (!drv->prepare_work(mythr, work)) { applog(LOG_ERR, "work prepare failed, exiting " "mining thread %d", thr_id); break; } - work->device_diff = MIN(drv->working_diff, work->work_difficulty); -#ifdef USE_SCRYPT - /* Dynamically adjust the working diff even if the target - * diff is very high to ensure we can still validate scrypt is - * returning shares. */ - if (opt_scrypt) { - double wu; - - wu = total_diff1 / total_secs * 60; - if (wu > 30 && drv->working_diff < drv->max_diff && - drv->working_diff < work->work_difficulty) { - drv->working_diff++; - applog(LOG_DEBUG, "Driver %s working diff changed to %.0f", - drv->dname, drv->working_diff); - work->device_diff = MIN(drv->working_diff, work->work_difficulty); - } else if (drv->working_diff > work->work_difficulty) - drv->working_diff = work->work_difficulty; - set_target(work->device_target, work->device_diff); - } -#endif + work->device_diff = MIN(drv->max_diff, work->work_difficulty); + work->device_diff = MAX(drv->min_diff, work->device_diff); do { cgtime(&tv_start); @@ -5973,7 +7898,7 @@ static void hash_sole_work(struct thr_info *mythr) pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, NULL); thread_reportin(mythr); - hashes = drv->scanhash(mythr, work, work->blk.nonce + max_nonce); + hashes = drv->scanhash(mythr, work, work->nonce + max_nonce); thread_reportout(mythr); pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, NULL); @@ -5986,7 +7911,8 @@ static void hash_sole_work(struct thr_info *mythr) applog(LOG_ERR, "%s %d failure, disabling!", drv->name, cgpu->device_id); cgpu->deven = DEV_DISABLED; dev_error(cgpu, REASON_THREAD_ZERO_HASH); - mt_disable(mythr, thr_id, drv); + cgpu->shutdown = true; + break; } hashes_done += hashes; @@ -6024,7 +7950,7 @@ static void hash_sole_work(struct thr_info *mythr) /* Update the hashmeter at most 5 times per second */ if ((hashes_done && (diff.tv_sec > 0 || diff.tv_usec > 200000)) || diff.tv_sec >= opt_log_interval) { - hashmeter(thr_id, &diff, hashes_done); + hashmeter(thr_id, hashes_done); hashes_done = 0; copy_time(&tv_lastupdate, tv_end); } @@ -6054,29 +7980,34 @@ static void hash_sole_work(struct thr_info *mythr) cgpu->deven = DEV_DISABLED; } -/* Create a hashtable of work items for devices with a queue. The device - * driver must have a custom queue_full function or it will default to true - * and put only one work item in the queue. Work items should not be removed - * from this hashtable until they are no longer in use anywhere. Once a work - * item is physically queued on the device itself, the work->queued flag - * should be set under cgpu->qlock write lock to prevent it being dereferenced - * while still in use. */ +/* Put a new unqueued work item in cgpu->unqueued_work under cgpu->qlock till + * the driver tells us it's full so that it may extract the work item using + * the get_queued() function which adds it to the hashtable on + * cgpu->queued_work. */ static void fill_queue(struct thr_info *mythr, struct cgpu_info *cgpu, struct device_drv *drv, const int thr_id) { do { bool need_work; - rd_lock(&cgpu->qlock); - need_work = (HASH_COUNT(cgpu->queued_work) == cgpu->queued_count); - rd_unlock(&cgpu->qlock); + /* Do this lockless just to know if we need more unqueued work. */ + need_work = (!cgpu->unqueued_work); + /* get_work is a blocking function so do it outside of lock + * to prevent deadlocks with other locks. */ if (need_work) { struct work *work = get_work(mythr, thr_id); - work->device_diff = MIN(drv->max_diff, work->work_difficulty); wr_lock(&cgpu->qlock); - HASH_ADD_INT(cgpu->queued_work, id, work); + /* Check we haven't grabbed work somehow between + * checking and picking up the lock. */ + if (likely(!cgpu->unqueued_work)) + cgpu->unqueued_work = work; + else + need_work = false; wr_unlock(&cgpu->qlock); + + if (unlikely(!need_work)) + discard_work(work); } /* The queue_full function should be used by the driver to * actually place work items on the physical device if it @@ -6084,24 +8015,104 @@ static void fill_queue(struct thr_info *mythr, struct cgpu_info *cgpu, struct de } while (!drv->queue_full(cgpu)); } -/* This function is for retrieving one work item from the queued hashtable of - * available work items that are not yet physically on a device (which is - * flagged with the work->queued bool). Code using this function must be able - * to handle NULL as a return which implies there is no work available. */ -struct work *get_queued(struct cgpu_info *cgpu) +/* Add a work item to a cgpu's queued hashlist */ +void __add_queued(struct cgpu_info *cgpu, struct work *work) +{ + cgpu->queued_count++; + HASH_ADD_INT(cgpu->queued_work, id, work); +} + +struct work *__get_queued(struct cgpu_info *cgpu) +{ + struct work *work = NULL; + + if (cgpu->unqueued_work) { + work = cgpu->unqueued_work; + if (unlikely(stale_work(work, false))) { + discard_work(work); + wake_gws(); + } else + __add_queued(cgpu, work); + cgpu->unqueued_work = NULL; + } + + return work; +} + +/* This function is for retrieving one work item from the unqueued pointer and + * adding it to the hashtable of queued work. Code using this function must be + * able to handle NULL as a return which implies there is no work available. */ +struct work *get_queued(struct cgpu_info *cgpu) +{ + struct work *work; + + wr_lock(&cgpu->qlock); + work = __get_queued(cgpu); + wr_unlock(&cgpu->qlock); + + return work; +} + +void add_queued(struct cgpu_info *cgpu, struct work *work) +{ + wr_lock(&cgpu->qlock); + __add_queued(cgpu, work); + wr_unlock(&cgpu->qlock); +} + +/* Get fresh work and add it to cgpu's queued hashlist */ +struct work *get_queue_work(struct thr_info *thr, struct cgpu_info *cgpu, int thr_id) +{ + struct work *work = get_work(thr, thr_id); + + add_queued(cgpu, work); + return work; +} + +/* This function is for finding an already queued work item in the + * given que hashtable. Code using this function must be able + * to handle NULL as a return which implies there is no matching work. + * The calling function must lock access to the que if it is required. + * The common values for midstatelen, offset, datalen are 32, 64, 12 */ +struct work *__find_work_bymidstate(struct work *que, char *midstate, size_t midstatelen, char *data, int offset, size_t datalen) +{ + struct work *work, *tmp, *ret = NULL; + + HASH_ITER(hh, que, work, tmp) { + if (memcmp(work->midstate, midstate, midstatelen) == 0 && + memcmp(work->data + offset, data, datalen) == 0) { + ret = work; + break; + } + } + + return ret; +} + +/* This function is for finding an already queued work item in the + * device's queued_work hashtable. Code using this function must be able + * to handle NULL as a return which implies there is no matching work. + * The common values for midstatelen, offset, datalen are 32, 64, 12 */ +struct work *find_queued_work_bymidstate(struct cgpu_info *cgpu, char *midstate, size_t midstatelen, char *data, int offset, size_t datalen) +{ + struct work *ret; + + rd_lock(&cgpu->qlock); + ret = __find_work_bymidstate(cgpu->queued_work, midstate, midstatelen, data, offset, datalen); + rd_unlock(&cgpu->qlock); + + return ret; +} + +struct work *clone_queued_work_bymidstate(struct cgpu_info *cgpu, char *midstate, size_t midstatelen, char *data, int offset, size_t datalen) { - struct work *work, *tmp, *ret = NULL; + struct work *work, *ret = NULL; - wr_lock(&cgpu->qlock); - HASH_ITER(hh, cgpu->queued_work, work, tmp) { - if (!work->queued) { - work->queued = true; - cgpu->queued_count++; - ret = work; - break; - } - } - wr_unlock(&cgpu->qlock); + rd_lock(&cgpu->qlock); + work = __find_work_bymidstate(cgpu->queued_work, midstate, midstatelen, data, offset, datalen); + if (work) + ret = copy_work(work); + rd_unlock(&cgpu->qlock); return ret; } @@ -6109,16 +8120,13 @@ struct work *get_queued(struct cgpu_info *cgpu) /* This function is for finding an already queued work item in the * given que hashtable. Code using this function must be able * to handle NULL as a return which implies there is no matching work. - * The calling function must lock access to the que if it is required. - * The common values for midstatelen, offset, datalen are 32, 64, 12 */ -struct work *__find_work_bymidstate(struct work *que, char *midstate, size_t midstatelen, char *data, int offset, size_t datalen) + * The calling function must lock access to the que if it is required. */ +struct work *__find_work_byid(struct work *que, uint32_t id) { struct work *work, *tmp, *ret = NULL; HASH_ITER(hh, que, work, tmp) { - if (work->queued && - memcmp(work->midstate, midstate, midstatelen) == 0 && - memcmp(work->data + offset, data, datalen) == 0) { + if (work->id == id) { ret = work; break; } @@ -6127,27 +8135,23 @@ struct work *__find_work_bymidstate(struct work *que, char *midstate, size_t mid return ret; } -/* This function is for finding an already queued work item in the - * device's queued_work hashtable. Code using this function must be able - * to handle NULL as a return which implies there is no matching work. - * The common values for midstatelen, offset, datalen are 32, 64, 12 */ -struct work *find_queued_work_bymidstate(struct cgpu_info *cgpu, char *midstate, size_t midstatelen, char *data, int offset, size_t datalen) +struct work *find_queued_work_byid(struct cgpu_info *cgpu, uint32_t id) { struct work *ret; rd_lock(&cgpu->qlock); - ret = __find_work_bymidstate(cgpu->queued_work, midstate, midstatelen, data, offset, datalen); + ret = __find_work_byid(cgpu->queued_work, id); rd_unlock(&cgpu->qlock); return ret; } -struct work *clone_queued_work_bymidstate(struct cgpu_info *cgpu, char *midstate, size_t midstatelen, char *data, int offset, size_t datalen) +struct work *clone_queued_work_byid(struct cgpu_info *cgpu, uint32_t id) { struct work *work, *ret = NULL; rd_lock(&cgpu->qlock); - work = __find_work_bymidstate(cgpu->queued_work, midstate, midstatelen, data, offset, datalen); + work = __find_work_byid(cgpu->queued_work, id); if (work) ret = copy_work(work); rd_unlock(&cgpu->qlock); @@ -6155,12 +8159,36 @@ struct work *clone_queued_work_bymidstate(struct cgpu_info *cgpu, char *midstate return ret; } -static void __work_completed(struct cgpu_info *cgpu, struct work *work) +void __work_completed(struct cgpu_info *cgpu, struct work *work) { - if (work->queued) - cgpu->queued_count--; + cgpu->queued_count--; HASH_DEL(cgpu->queued_work, work); } + +/* This iterates over a queued hashlist finding work started more than secs + * seconds ago and discards the work as completed. The driver must set the + * work->tv_work_start value appropriately. Returns the number of items aged. */ +int age_queued_work(struct cgpu_info *cgpu, double secs) +{ + struct work *work, *tmp; + struct timeval tv_now; + int aged = 0; + + cgtime(&tv_now); + + wr_lock(&cgpu->qlock); + HASH_ITER(hh, cgpu->queued_work, work, tmp) { + if (tdiff(&tv_now, &work->tv_work_start) > secs) { + __work_completed(cgpu, work); + free_work(work); + aged++; + } + } + wr_unlock(&cgpu->qlock); + + return aged; +} + /* This function should be used by queued device drivers when they're sure * the work struct is no longer in use. */ void work_completed(struct cgpu_info *cgpu, struct work *work) @@ -6187,25 +8215,25 @@ struct work *take_queued_work_bymidstate(struct cgpu_info *cgpu, char *midstate, return work; } -static void flush_queue(struct cgpu_info *cgpu) +void flush_queue(struct cgpu_info *cgpu) { - struct work *work, *tmp; - int discarded = 0; + struct work *work = NULL; - wr_lock(&cgpu->qlock); - HASH_ITER(hh, cgpu->queued_work, work, tmp) { - /* Can only discard the work items if they're not physically - * queued on the device. */ - if (!work->queued) { - HASH_DEL(cgpu->queued_work, work); - discard_work(work); - discarded++; - } - } + if (unlikely(!cgpu)) + return; + + /* Use only a trylock in case we get into a deadlock with a queueing + * function holding the read lock when we're called. */ + if (wr_trylock(&cgpu->qlock)) + return; + work = cgpu->unqueued_work; + cgpu->unqueued_work = NULL; wr_unlock(&cgpu->qlock); - if (discarded) - applog(LOG_DEBUG, "Discarded %d queued work items", discarded); + if (work) { + free_work(work); + applog(LOG_DEBUG, "Discarded queued work item"); + } } /* This version of hash work is for devices that are fast enough to always @@ -6224,17 +8252,21 @@ void hash_queued_work(struct thr_info *mythr) struct timeval diff; int64_t hashes; - mythr->work_restart = false; + mythr->work_update = false; fill_queue(mythr, cgpu, drv, thr_id); hashes = drv->scanwork(mythr); + /* Reset the bool here in case the driver looks for it + * synchronously in the scanwork loop. */ + mythr->work_restart = false; + if (unlikely(hashes == -1 )) { applog(LOG_ERR, "%s %d failure, disabling!", drv->name, cgpu->device_id); cgpu->deven = DEV_DISABLED; dev_error(cgpu, REASON_THREAD_ZERO_HASH); - mt_disable(mythr, thr_id, drv); + break; } hashes_done += hashes; @@ -6243,7 +8275,7 @@ void hash_queued_work(struct thr_info *mythr) /* Update the hashmeter at most 5 times per second */ if ((hashes_done && (diff.tv_sec > 0 || diff.tv_usec > 200000)) || diff.tv_sec >= opt_log_interval) { - hashmeter(thr_id, &diff, hashes_done); + hashmeter(thr_id, hashes_done); hashes_done = 0; copy_time(&tv_start, &tv_end); } @@ -6251,10 +8283,59 @@ void hash_queued_work(struct thr_info *mythr) if (unlikely(mythr->pause || cgpu->deven != DEV_ENABLED)) mt_disable(mythr, thr_id, drv); - if (unlikely(mythr->work_restart)) { - flush_queue(cgpu); - drv->flush_work(cgpu); + if (mythr->work_update) + drv->update_work(cgpu); + } + cgpu->deven = DEV_DISABLED; +} + +/* This version of hash_work is for devices drivers that want to do their own + * work management entirely, usually by using get_work(). Note that get_work + * is a blocking function and will wait indefinitely if no work is available + * so this must be taken into consideration in the driver. */ +void hash_driver_work(struct thr_info *mythr) +{ + struct timeval tv_start = {0, 0}, tv_end; + struct cgpu_info *cgpu = mythr->cgpu; + struct device_drv *drv = cgpu->drv; + const int thr_id = mythr->id; + int64_t hashes_done = 0; + + while (likely(!cgpu->shutdown)) { + struct timeval diff; + int64_t hashes; + + mythr->work_update = false; + + hashes = drv->scanwork(mythr); + + /* Reset the bool here in case the driver looks for it + * synchronously in the scanwork loop. */ + mythr->work_restart = false; + + if (unlikely(hashes == -1 )) { + applog(LOG_ERR, "%s %d failure, disabling!", drv->name, cgpu->device_id); + cgpu->deven = DEV_DISABLED; + dev_error(cgpu, REASON_THREAD_ZERO_HASH); + break; + } + + hashes_done += hashes; + cgtime(&tv_end); + timersub(&tv_end, &tv_start, &diff); + /* Update the hashmeter at most 5 times per second */ + if ((hashes_done && (diff.tv_sec > 0 || diff.tv_usec > 200000)) || + diff.tv_sec >= opt_log_interval) { + hashmeter(thr_id, hashes_done); + hashes_done = 0; + copy_time(&tv_start, &tv_end); } + + if (unlikely(mythr->pause || cgpu->deven != DEV_ENABLED)) + mt_disable(mythr, thr_id, drv); + + if (mythr->work_update) + drv->update_work(cgpu); } cgpu->deven = DEV_DISABLED; } @@ -6265,9 +8346,9 @@ void *miner_thread(void *userdata) const int thr_id = mythr->id; struct cgpu_info *cgpu = mythr->cgpu; struct device_drv *drv = cgpu->drv; - char threadname[24]; + char threadname[16]; - snprintf(threadname, 24, "miner/%d", thr_id); + snprintf(threadname, sizeof(threadname), "%d/Miner", thr_id); RenameThread(threadname); thread_reportout(mythr); @@ -6279,12 +8360,10 @@ void *miner_thread(void *userdata) applog(LOG_DEBUG, "Waiting on sem in miner thread"); cgsem_wait(&mythr->sem); + cgpu->last_device_valid_work = time(NULL); drv->hash_work(mythr); -out: drv->thread_shutdown(mythr); - - applog(LOG_ERR, "Thread %d failure, exiting", thr_id); - +out: return NULL; } @@ -6294,6 +8373,7 @@ enum { FAILURE_INTERVAL = 30, }; +#ifdef HAVE_LIBCURL /* Stage another work item from the work returned in a longpoll */ static void convert_to_work(json_t *val, int rolltime, struct pool *pool, struct timeval *tv_lp, struct timeval *tv_lp_reply) { @@ -6354,7 +8434,7 @@ static struct pool *select_longpoll_pool(struct pool *cp) { int i; - if (cp->hdr_path || cp->has_gbt) + if (cp->hdr_path || cp->has_gbt || cp->gbt_solo) return cp; for (i = 0; i < total_pools; i++) { struct pool *pool = pools[i]; @@ -6364,6 +8444,7 @@ static struct pool *select_longpoll_pool(struct pool *cp) } return NULL; } +#endif /* HAVE_LIBCURL */ /* This will make the longpoll thread wait till it's the current pool, or it * has been flagged as rejecting, before attempting to open any connections. @@ -6379,6 +8460,7 @@ static void wait_lpcurrent(struct pool *pool) } } +#ifdef HAVE_LIBCURL static void *longpoll_thread(void *userdata) { struct pool *cp = (struct pool *)userdata; @@ -6393,15 +8475,9 @@ static void *longpoll_thread(void *userdata) char *lp_url; int rolltime; - snprintf(threadname, 16, "longpoll/%d", cp->pool_no); + snprintf(threadname, sizeof(threadname), "%d/Longpoll", cp->pool_no); RenameThread(threadname); - curl = curl_easy_init(); - if (unlikely(!curl)) { - applog(LOG_ERR, "CURL initialisation failed"); - return NULL; - } - retry_pool: pool = select_longpoll_pool(cp); if (!pool) { @@ -6418,6 +8494,79 @@ static void *longpoll_thread(void *userdata) goto out; } + if (pool->gbt_solo) { + applog(LOG_WARNING, "Block change for %s detection via getblockcount polling", + cp->rpc_url); + while (42) { + json_t *val, *res_val = NULL; + + if (unlikely(pool->removed)) + return NULL; + + cgtime(&start); + wait_lpcurrent(cp); + sprintf(lpreq, "{\"id\": 0, \"method\": \"getblockcount\"}\n"); + + /* We will be making another call immediately after this + * one to get the height so allow this curl to be reused.*/ + get_gbt_curl(pool, 500); + curl_easy_setopt(pool->gbt_curl, CURLOPT_FORBID_REUSE, 0); + val = json_rpc_call(pool->gbt_curl, pool->rpc_url, pool->rpc_userpass, lpreq, true, + false, &rolltime, pool, false); + release_gbt_curl(pool); + + if (likely(val)) + res_val = json_object_get(val, "result"); + if (likely(res_val)) { + int height = json_integer_value(res_val); + const char *prev_hash; + + failures = 0; + json_decref(val); + if (height >= cp->height) { + applog(LOG_WARNING, "Block height change to %d detected on pool %d", + height, cp->pool_no); + update_gbt_solo(pool); + continue; + } + + sprintf(lpreq, "{\"id\": 0, \"method\": \"getblockhash\", \"params\": [%d]}\n", height); + get_gbt_curl(pool, 500); + curl_easy_setopt(pool->gbt_curl, CURLOPT_FORBID_REUSE, 1); + val = json_rpc_call(pool->gbt_curl, pool->rpc_url, pool->rpc_userpass, + lpreq, true, false, &rolltime, pool, false); + release_gbt_curl(pool); + + if (val) { + /* Do a comparison on a short stretch of + * the hash to make sure it hasn't changed + * due to mining on an orphan branch. */ + prev_hash = json_string_value(json_object_get(val, "result")); + if (unlikely(prev_hash && strncasecmp(prev_hash + 56, pool->prev_hash, 8))) { + applog(LOG_WARNING, "Mining on orphan branch detected, switching!"); + update_gbt_solo(pool); + } + json_decref(val); + } + + cgsleep_ms(500); + } else { + if (val) + json_decref(val); + cgtime(&end); + if (end.tv_sec - start.tv_sec > 30) + continue; + if (failures == 1) + applog(LOG_WARNING, "longpoll failed for %s, retrying every 30s", lp_url); + cgsleep_ms(30000); + } + } + } + + curl = curl_easy_init(); + if (unlikely(!curl)) + quit (1, "Longpoll CURL initialisation failed"); + /* Any longpoll from any pool is enough for this to be true */ have_longpoll = true; @@ -6506,9 +8655,24 @@ static void *longpoll_thread(void *userdata) return NULL; } +#else /* HAVE_LIBCURL */ +static void *longpoll_thread(void __maybe_unused *userdata) +{ + pthread_detach(pthread_self()); + return NULL; +} +#endif /* HAVE_LIBCURL */ void reinit_device(struct cgpu_info *cgpu) { + if (cgpu->deven == DEV_DISABLED) + return; + +#ifdef USE_USBUTILS + /* Attempt a usb device reset if the device has gone sick */ + if (cgpu->usbdev && cgpu->usbdev->handle) + libusb_reset_device(cgpu->usbdev->handle); +#endif cgpu->drv->reinit_device(cgpu); } @@ -6541,13 +8705,43 @@ static void reap_curl(struct pool *pool) applog(LOG_DEBUG, "Reaped %d curl%s from pool %d", reaped, reaped > 1 ? "s" : "", pool->pool_no); } +/* Prune old shares we haven't had a response about for over 2 minutes in case + * the pool never plans to respond and we're just leaking memory. If we get a + * response beyond that time they will be seen as untracked shares. */ +static void prune_stratum_shares(struct pool *pool) +{ + struct stratum_share *sshare, *tmpshare; + time_t current_time = time(NULL); + int cleared = 0; + + mutex_lock(&sshare_lock); + HASH_ITER(hh, stratum_shares, sshare, tmpshare) { + if (sshare->work->pool == pool && current_time > sshare->sshare_time + 120) { + HASH_DEL(stratum_shares, sshare); + free_work(sshare->work); + free(sshare); + cleared++; + } + } + mutex_unlock(&sshare_lock); + + if (cleared) { + applog(LOG_WARNING, "Lost %d shares due to no stratum share response from pool %d", + cleared, pool->pool_no); + pool->stale_shares += cleared; + total_stale += cleared; + } +} + static void *watchpool_thread(void __maybe_unused *userdata) { int intervals = 0; pthread_setcanceltype(PTHREAD_CANCEL_ASYNCHRONOUS, NULL); - RenameThread("watchpool"); + RenameThread("Watchpool"); + + set_lowprio(); while (42) { struct timeval now; @@ -6560,36 +8754,50 @@ static void *watchpool_thread(void __maybe_unused *userdata) for (i = 0; i < total_pools; i++) { struct pool *pool = pools[i]; - if (!opt_benchmark) + if (!opt_benchmark && !opt_benchfile) { reap_curl(pool); + prune_stratum_shares(pool); + } /* Get a rolling utility per pool over 10 mins */ if (intervals > 19) { - int shares = pool->diff1 - pool->last_shares; + double shares = pool->diff1 - pool->last_shares; pool->last_shares = pool->diff1; - pool->utility = (pool->utility + (double)shares * 0.63) / 1.63; + pool->utility = (pool->utility + shares * 0.63) / 1.63; pool->shares = pool->utility; } if (pool->enabled == POOL_DISABLED) continue; - /* Don't start testing any pools if the test threads - * from startup are still doing their first attempt. */ - if (unlikely(pool->testing)) { - pthread_join(pool->test_thread, NULL); - pool->testing = false; - } + /* Don't start testing a pool if its test thread + * from startup is still doing its first attempt. */ + if (unlikely(pool->testing)) + continue; /* Test pool is idle once every minute */ if (pool->idle && now.tv_sec - pool->tv_idle.tv_sec > 30) { - cgtime(&pool->tv_idle); if (pool_active(pool, true) && pool_tclear(pool, &pool->idle)) pool_resus(pool); + else + cgtime(&pool->tv_idle); + } + + /* Only switch pools if the failback pool has been + * alive for more than 5 minutes to prevent + * intermittently failing pools from being used. */ + if (!pool->idle && pool_strategy == POOL_FAILOVER && pool->prio < cp_prio() && + now.tv_sec - pool->tv_idle.tv_sec > 300) { + applog(LOG_WARNING, "Pool %d %s stable for 5 mins", + pool->pool_no, pool->rpc_url); + switch_pools(NULL); } } + if (current_pool()->idle) + switch_pools(NULL); + if (pool_strategy == POOL_ROTATE && now.tv_sec - rotate_tv.tv_sec > 60 * opt_rotate_period) { cgtime(&rotate_tv); switch_pools(NULL); @@ -6617,8 +8825,9 @@ static void *watchdog_thread(void __maybe_unused *userdata) pthread_setcanceltype(PTHREAD_CANCEL_ASYNCHRONOUS, NULL); - RenameThread("watchdog"); + RenameThread("Watchdog"); + set_lowprio(); memset(&zero_tv, 0, sizeof(struct timeval)); cgtime(&rotate_tv); @@ -6630,7 +8839,7 @@ static void *watchdog_thread(void __maybe_unused *userdata) discard_stale(); - hashmeter(-1, &zero_tv, 0); + hashmeter(-1, 0); #ifdef HAVE_CURSES if (curses_active_locked()) { @@ -6647,13 +8856,13 @@ static void *watchdog_thread(void __maybe_unused *userdata) #else if (cgpu && !cgpu->usbinfo.nodev) #endif - curses_print_devstatus(cgpu, count++); + curses_print_devstatus(cgpu, i, count++); } #ifdef USE_USBUTILS for (i = 0; i < total_devices; i++) { cgpu = get_devices(i); if (cgpu && cgpu->usbinfo.nodev) - curses_print_devstatus(cgpu, count++); + curses_print_devstatus(cgpu, i, count++); } #endif touchwin(statuswin); @@ -6709,27 +8918,15 @@ static void *watchdog_thread(void __maybe_unused *userdata) struct thr_info *thr = cgpu->thr[0]; enum dev_enable *denable; char dev_str[8]; - int gpu; + + if (!thr) + continue; cgpu->drv->get_stats(cgpu); - gpu = cgpu->device_id; denable = &cgpu->deven; - snprintf(dev_str, sizeof(dev_str), "%s%d", cgpu->drv->name, gpu); - -#ifdef HAVE_ADL - if (adl_active && cgpu->has_adl) - gpu_autotune(gpu, denable); - if (opt_debug && cgpu->has_adl) { - int engineclock = 0, memclock = 0, activity = 0, fanspeed = 0, fanpercent = 0, powertune = 0; - float temp = 0, vddc = 0; - - if (gpu_stats(gpu, &temp, &engineclock, &memclock, &vddc, &activity, &fanspeed, &fanpercent, &powertune)) - applog(LOG_DEBUG, "%.1f C F: %d%%(%dRPM) E: %dMHz M: %dMhz V: %.3fV A: %d%% P: %d%%", - temp, fanpercent, fanspeed, engineclock, memclock, vddc, activity, powertune); - } -#endif - + snprintf(dev_str, sizeof(dev_str), "%s %d", cgpu->drv->name, cgpu->device_id); + /* Thread is waiting on getwork or disabled */ if (thr->getwork || *denable == DEV_DISABLED) continue; @@ -6740,18 +8937,12 @@ static void *watchdog_thread(void __maybe_unused *userdata) cgpu->status = LIFE_WELL; cgpu->device_last_well = time(NULL); } else if (cgpu->status == LIFE_WELL && (now.tv_sec - thr->last.tv_sec > WATCHDOG_SICK_TIME)) { - thr->rolling = cgpu->rolling = 0; + cgpu->rolling = 0; cgpu->status = LIFE_SICK; applog(LOG_ERR, "%s: Idle for more than 60 seconds, declaring SICK!", dev_str); cgtime(&thr->sick); dev_error(cgpu, REASON_DEV_SICK_IDLE_60); -#ifdef HAVE_ADL - if (adl_active && cgpu->has_adl && gpu_activity(gpu) > 50) { - applog(LOG_ERR, "GPU still showing activity suggesting a hard hang."); - applog(LOG_ERR, "Will not attempt to auto-restart it."); - } else -#endif if (opt_restart) { applog(LOG_ERR, "%s: Attempting to restart", dev_str); reinit_device(cgpu); @@ -6766,11 +8957,6 @@ static void *watchdog_thread(void __maybe_unused *userdata) (cgpu->status == LIFE_SICK || cgpu->status == LIFE_DEAD)) { /* Attempt to restart a GPU that's sick or dead once every minute */ cgtime(&thr->sick); -#ifdef HAVE_ADL - if (adl_active && cgpu->has_adl && gpu_activity(gpu) > 50) { - /* Again do not attempt to restart a device that may have hard hung */ - } else -#endif if (opt_restart) reinit_device(cgpu); } @@ -6788,12 +8974,14 @@ static void log_print_status(struct cgpu_info *cgpu) applog(LOG_WARNING, "%s", logline); } +static void noop_get_statline(char __maybe_unused *buf, size_t __maybe_unused bufsiz, struct cgpu_info __maybe_unused *cgpu); +void blank_get_statline_before(char *buf, size_t bufsiz, struct cgpu_info __maybe_unused *cgpu); + void print_summary(void) { struct timeval diff; int hours, mins, secs, i; double utility, displayed_hashes, work_util; - bool mhash_base = true; timersub(&total_tv_end, &total_tv_start, &diff); hours = diff.tv_sec / 3600; @@ -6809,17 +8997,13 @@ void print_summary(void) applog(LOG_WARNING, "Pool: %s", pools[0]->rpc_url); applog(LOG_WARNING, "Runtime: %d hrs : %d mins : %d secs", hours, mins, secs); displayed_hashes = total_mhashes_done / total_secs; - if (displayed_hashes < 1) { - displayed_hashes *= 1000; - mhash_base = false; - } - applog(LOG_WARNING, "Average hashrate: %.1f %shash/s", displayed_hashes, mhash_base? "Mega" : "Kilo"); + applog(LOG_WARNING, "Average hashrate: %.1f Mhash/s", displayed_hashes); applog(LOG_WARNING, "Solved blocks: %d", found_blocks); applog(LOG_WARNING, "Best share difficulty: %s", best_share); - applog(LOG_WARNING, "Share submissions: %d", total_accepted + total_rejected); - applog(LOG_WARNING, "Accepted shares: %d", total_accepted); - applog(LOG_WARNING, "Rejected shares: %d", total_rejected); + applog(LOG_WARNING, "Share submissions: %"PRId64, total_accepted + total_rejected); + applog(LOG_WARNING, "Accepted shares: %"PRId64, total_accepted); + applog(LOG_WARNING, "Rejected shares: %"PRId64, total_rejected); applog(LOG_WARNING, "Accepted difficulty shares: %1.f", total_diff_accepted); applog(LOG_WARNING, "Rejected difficulty shares: %1.f", total_diff_rejected); if (total_accepted || total_rejected) @@ -6828,7 +9012,7 @@ void print_summary(void) applog(LOG_WARNING, "Utility (accepted shares / min): %.2f/min", utility); applog(LOG_WARNING, "Work Utility (diff1 shares solved / min): %.2f/min\n", work_util); - applog(LOG_WARNING, "Stale submissions discarded due to new blocks: %d", total_stale); + applog(LOG_WARNING, "Stale submissions discarded due to new blocks: %"PRId64, total_stale); applog(LOG_WARNING, "Unable to get work from server occasions: %d", total_go); applog(LOG_WARNING, "Work items generated locally: %d", local_work); applog(LOG_WARNING, "Submitting work remotely delay occasions: %d", total_ro); @@ -6841,14 +9025,15 @@ void print_summary(void) applog(LOG_WARNING, "Pool: %s", pool->rpc_url); if (pool->solved) applog(LOG_WARNING, "SOLVED %d BLOCK%s!", pool->solved, pool->solved > 1 ? "S" : ""); - applog(LOG_WARNING, " Share submissions: %d", pool->accepted + pool->rejected); - applog(LOG_WARNING, " Accepted shares: %d", pool->accepted); - applog(LOG_WARNING, " Rejected shares: %d", pool->rejected); + applog(LOG_WARNING, " Share submissions: %"PRId64, pool->accepted + pool->rejected); + applog(LOG_WARNING, " Accepted shares: %"PRId64, pool->accepted); + applog(LOG_WARNING, " Rejected shares: %"PRId64, pool->rejected); applog(LOG_WARNING, " Accepted difficulty shares: %1.f", pool->diff_accepted); applog(LOG_WARNING, " Rejected difficulty shares: %1.f", pool->diff_rejected); if (pool->accepted || pool->rejected) applog(LOG_WARNING, " Reject ratio: %.1f%%", (double)(pool->rejected * 100) / (double)(pool->accepted + pool->rejected)); + applog(LOG_WARNING, " Items worked on: %d", pool->works); applog(LOG_WARNING, " Stale submissions discarded due to new blocks: %d", pool->stale_shares); applog(LOG_WARNING, " Unable to get work from server occasions: %d", pool->getfail_occasions); applog(LOG_WARNING, " Submitting work remotely delay occasions: %d\n", pool->remotefail_occasions); @@ -6859,13 +9044,15 @@ void print_summary(void) for (i = 0; i < total_devices; ++i) { struct cgpu_info *cgpu = get_devices(i); + cgpu->drv->get_statline_before = &blank_get_statline_before; + cgpu->drv->get_statline = &noop_get_statline; log_print_status(cgpu); } if (opt_shares) { - applog(LOG_WARNING, "Mined %d accepted shares of %d requested\n", total_accepted, opt_shares); - if (opt_shares > total_accepted) - applog(LOG_WARNING, "WARNING - Mined only %d shares of %d requested.", total_accepted, opt_shares); + applog(LOG_WARNING, "Mined %.0f accepted shares of %d requested\n", total_diff_accepted, opt_shares); + if (opt_shares > total_diff_accepted) + applog(LOG_WARNING, "WARNING - Mined only %.0f shares of %d requested.", total_diff_accepted, opt_shares); } applog(LOG_WARNING, " "); @@ -6873,12 +9060,11 @@ void print_summary(void) fflush(stdout); } -static void clean_up(void) +static void clean_up(bool restarting) { -#ifdef HAVE_OPENCL - clear_adl(nDevs); -#endif #ifdef USE_USBUTILS + usb_polling = false; + pthread_join(usb_poll_thread, NULL); libusb_exit(NULL); #endif @@ -6889,15 +9075,36 @@ static void clean_up(void) #ifdef HAVE_CURSES disable_curses(); #endif - if (!opt_realquiet && successful_connect) + if (!restarting && !opt_realquiet && successful_connect) print_summary(); curl_global_cleanup(); } -void _quit(int status) +/* Should all else fail and we're unable to clean up threads due to locking + * issues etc, just silently exit. */ +static void *killall_thread(void __maybe_unused *arg) +{ + pthread_detach(pthread_self()); + pthread_setcanceltype(PTHREAD_CANCEL_ASYNCHRONOUS, NULL); + sleep(5); + exit(1); + return NULL; +} + +void __quit(int status, bool clean) { - clean_up(); + pthread_t killall_t; + + if (unlikely(pthread_create(&killall_t, NULL, killall_thread, NULL))) + exit(1); + + if (clean) + clean_up(false); +#ifdef HAVE_CURSES + else + disable_curses(); +#endif #if defined(unix) || defined(__APPLE__) if (forkpid > 0) { @@ -6905,10 +9112,16 @@ void _quit(int status) forkpid = 0; } #endif + pthread_cancel(killall_t); exit(status); } +void _quit(int status) +{ + __quit(status, true); +} + #ifdef HAVE_CURSES char *curses_input(const char *query) { @@ -6935,6 +9148,9 @@ static void *test_pool_thread(void *arg) { struct pool *pool = (struct pool *)arg; + if (!pool->blocking) + pthread_detach(pthread_self()); +retry: if (pool_active(pool, false)) { pool_tset(pool, &pool->lagging); pool_tclear(pool, &pool->idle); @@ -6953,8 +9169,14 @@ static void *test_pool_thread(void *arg) applog(LOG_NOTICE, "Switching to pool %d %s - first alive pool", pool->pool_no, pool->rpc_url); pool_resus(pool); - } else + switch_pools(NULL); + } else { pool_died(pool); + sleep(5); + goto retry; + } + + pool->testing = false; return NULL; } @@ -6979,12 +9201,12 @@ bool add_pool_details(struct pool *pool, bool live, char *url, char *user, char pool->testing = true; pool->idle = true; + pool->blocking = !live; enable_pool(pool); pthread_create(&pool->test_thread, NULL, test_pool_thread, (void *)pool); if (!live) { pthread_join(pool->test_thread, NULL); - pool->testing = false; return pools_active; } return true; @@ -7001,16 +9223,18 @@ static bool input_pool(bool live) wlogprint("Input server details.\n"); url = curses_input("URL"); - if (!url) + if (!strcmp(url, "-1")) goto out; user = curses_input("Username"); - if (!user) + if (!strcmp(user, "-1")) goto out; pass = curses_input("Password"); - if (!pass) - goto out; + if (!strcmp(pass, "-1")) { + free(pass); + pass = strdup(""); + } pool = add_pool(); @@ -7032,12 +9256,9 @@ static bool input_pool(bool live) immedok(logwin, false); if (!ret) { - if (url) - free(url); - if (user) - free(user); - if (pass) - free(pass); + free(url); + free(user); + free(pass); } return ret; } @@ -7142,30 +9363,6 @@ void enable_curses(void) { } #endif -#ifdef USE_BFLSC -extern struct device_drv bflsc_drv; -#endif - -#ifdef USE_BITFORCE -extern struct device_drv bitforce_drv; -#endif - -#ifdef USE_ICARUS -extern struct device_drv icarus_drv; -#endif - -#ifdef USE_AVALON -extern struct device_drv avalon_drv; -#endif - -#ifdef USE_MODMINER -extern struct device_drv modminer_drv; -#endif - -#ifdef USE_ZTEX -extern struct device_drv ztex_drv; -#endif - static int cgminer_id_count = 0; /* Various noop functions for drivers that don't support or need their @@ -7174,9 +9371,8 @@ static void noop_reinit_device(struct cgpu_info __maybe_unused *cgpu) { } -void blank_get_statline_before(char *buf, size_t bufsiz, struct cgpu_info __maybe_unused *cgpu) +void blank_get_statline_before(char __maybe_unused *buf,size_t __maybe_unused bufsiz, struct cgpu_info __maybe_unused *cgpu) { - tailsprintf(buf, bufsiz, " | "); } static void noop_get_statline(char __maybe_unused *buf, size_t __maybe_unused bufsiz, struct cgpu_info __maybe_unused *cgpu) @@ -7220,14 +9416,30 @@ static void noop_thread_enable(struct thr_info __maybe_unused *thr) { } +static void noop_detect(bool __maybe_unused hotplug) +{ +} + +static struct api_data *noop_get_api_stats(struct cgpu_info __maybe_unused *cgpu) +{ + return NULL; +} + +static void noop_hash_work(struct thr_info __maybe_unused *thr) +{ +} + #define noop_flush_work noop_reinit_device +#define noop_update_work noop_reinit_device #define noop_queue_full noop_get_stats +#define noop_zero_stats noop_reinit_device +#define noop_identify_device noop_reinit_device /* Fill missing driver drv functions with noops */ -void fill_device_drv(struct cgpu_info *cgpu) +void fill_device_drv(struct device_drv *drv) { - struct device_drv *drv = cgpu->drv; - + if (!drv->drv_detect) + drv->drv_detect = &noop_detect; if (!drv->reinit_device) drv->reinit_device = &noop_reinit_device; if (!drv->get_statline_before) @@ -7254,12 +9466,52 @@ void fill_device_drv(struct cgpu_info *cgpu) drv->hash_work = &hash_sole_work; if (!drv->flush_work) drv->flush_work = &noop_flush_work; + if (!drv->update_work) + drv->update_work = &noop_update_work; if (!drv->queue_full) drv->queue_full = &noop_queue_full; + if (!drv->zero_stats) + drv->zero_stats = &noop_zero_stats; + /* If drivers support internal diff they should set a max_diff or + * we will assume they don't and set max to 1. */ if (!drv->max_diff) drv->max_diff = 1; - if (!drv->working_diff) - drv->working_diff = 1; +} + +void null_device_drv(struct device_drv *drv) +{ + drv->drv_detect = &noop_detect; + drv->reinit_device = &noop_reinit_device; + drv->get_statline_before = &blank_get_statline_before; + drv->get_statline = &noop_get_statline; + drv->get_api_stats = &noop_get_api_stats; + drv->get_stats = &noop_get_stats; + drv->identify_device = &noop_identify_device; + drv->set_device = NULL; + + drv->thread_prepare = &noop_thread_prepare; + drv->can_limit_work = &noop_can_limit_work; + drv->thread_init = &noop_thread_init; + drv->prepare_work = &noop_prepare_work; + + /* This should make the miner thread just exit */ + drv->hash_work = &noop_hash_work; + + drv->hw_error = &noop_hw_error; + drv->thread_shutdown = &noop_thread_shutdown; + drv->thread_enable = &noop_thread_enable; + + drv->zero_stats = &noop_zero_stats; + + drv->hash_work = &noop_hash_work; + + drv->queue_full = &noop_queue_full; + drv->flush_work = &noop_flush_work; + drv->update_work = &noop_update_work; + + drv->zero_stats = &noop_zero_stats; + drv->max_diff = 1; + drv->min_diff = 1; } void enable_device(struct cgpu_info *cgpu) @@ -7270,22 +9522,11 @@ void enable_device(struct cgpu_info *cgpu) devices[cgpu->cgminer_id = cgminer_id_count++] = cgpu; wr_unlock(&devices_lock); - if (hotplug_mode) { + if (hotplug_mode) new_threads += cgpu->threads; -#ifdef HAVE_CURSES - adj_width(mining_threads + new_threads, &dev_width); -#endif - } else { + else mining_threads += cgpu->threads; -#ifdef HAVE_CURSES - adj_width(mining_threads, &dev_width); -#endif - } -#ifdef HAVE_OPENCL - if (cgpu->drv->drv_id == DRIVER_OPENCL) { - gpu_threads += cgpu->threads; - } -#endif + rwlock_init(&cgpu->qlock); cgpu->queued_work = NULL; } @@ -7302,6 +9543,20 @@ static void adjust_mostdevs(void) most_devices = total_devices - zombie_devs; } +#ifdef USE_ICARUS +bool icarus_get_device_id(struct cgpu_info *cgpu) +{ + static struct _cgpu_devid_counter *devids = NULL; + struct _cgpu_devid_counter *d; + + HASH_FIND_STR(devids, cgpu->drv->name, d); + if (d) + return (d->lastid + 1); + else + return 0; +} +#endif + bool add_cgpu(struct cgpu_info *cgpu) { static struct _cgpu_devid_counter *devids = NULL; @@ -7325,14 +9580,17 @@ bool add_cgpu(struct cgpu_info *cgpu) cgpu->last_device_valid_work = time(NULL); mutex_unlock(&stats_lock); - fill_device_drv(cgpu); - if (hotplug_mode) devices[total_devices + new_devices++] = cgpu; else devices[total_devices++] = cgpu; adjust_mostdevs(); +#ifdef USE_USBUTILS + if (cgpu->usbdev && !cgpu->unique_id && cgpu->usbdev->serial_string && + strlen(cgpu->usbdev->serial_string) > 4) + cgpu->unique_id = str_text(cgpu->usbdev->serial_string); +#endif return true; } @@ -7350,7 +9608,7 @@ struct device_drv *copy_drv(struct device_drv *drv) } #ifdef USE_USBUTILS -static void hotplug_process() +static void hotplug_process(void) { struct thr_info *thr; int i, j; @@ -7360,15 +9618,13 @@ static void hotplug_process() int dev_no = total_devices + i; cgpu = devices[dev_no]; - if (!opt_devs_enabled || (opt_devs_enabled && devices_enabled[dev_no])) - enable_device(cgpu); + enable_device(cgpu); cgpu->cgminer_stats.getwork_wait_min.tv_sec = MIN_SEC_UNSET; cgpu->rolling = cgpu->total_mhashes = 0; } wr_lock(&mining_thr_lock); mining_thr = realloc(mining_thr, sizeof(thr) * (mining_threads + new_threads + 1)); - wr_unlock(&mining_thr_lock); if (!mining_thr) quit(1, "Failed to hotplug realloc mining_thr"); @@ -7387,13 +9643,16 @@ static void hotplug_process() cgtime(&(cgpu->dev_start_tv)); for (j = 0; j < cgpu->threads; ++j) { - thr = get_thread(mining_threads); + thr = __get_thread(mining_threads); thr->id = mining_threads; thr->cgpu = cgpu; thr->device_thread = j; - if (cgpu->drv->thread_prepare && !cgpu->drv->thread_prepare(thr)) + if (!cgpu->drv->thread_prepare(thr)) { + null_device_drv(cgpu->drv); + cgpu->deven = DEV_DISABLED; continue; + } if (unlikely(thr_info_create(thr, NULL, miner_thread, thr))) quit(1, "hotplug thread %d create failed", thr->id); @@ -7412,16 +9671,21 @@ static void hotplug_process() total_devices++; applog(LOG_WARNING, "Hotplug: %s added %s %i", cgpu->drv->dname, cgpu->drv->name, cgpu->device_id); } + wr_unlock(&mining_thr_lock); adjust_mostdevs(); +#ifdef HAVE_CURSES switch_logsize(true); +#endif } +#define DRIVER_DRV_DETECT_HOTPLUG(X) X##_drv.drv_detect(true); + static void *hotplug_thread(void __maybe_unused *userdata) { pthread_setcanceltype(PTHREAD_CANCEL_ASYNCHRONOUS, NULL); - RenameThread("hotplug"); + RenameThread("Hotplug"); hotplug_mode = true; @@ -7436,25 +9700,9 @@ static void *hotplug_thread(void __maybe_unused *userdata) new_devices = 0; new_threads = 0; -#ifdef USE_ICARUS - icarus_drv.drv_detect(); -#endif - -#ifdef USE_BFLSC - bflsc_drv.drv_detect(); -#endif - -#ifdef USE_BITFORCE - bitforce_drv.drv_detect(); -#endif - -#ifdef USE_MODMINER - modminer_drv.drv_detect(); -#endif - -#ifdef USE_AVALON - avalon_drv.drv_detect(); -#endif + /* Use the DRIVER_PARSE_COMMANDS macro to detect all + * devices */ + DRIVER_PARSE_COMMANDS(DRIVER_DRV_DETECT_HOTPLUG) if (new_devices) hotplug_process(); @@ -7480,37 +9728,85 @@ static void probe_pools(void) } } +#define DRIVER_FILL_DEVICE_DRV(X) fill_device_drv(&X##_drv); +#define DRIVER_DRV_DETECT_ALL(X) X##_drv.drv_detect(false); + +#ifdef USE_USBUTILS +static void *libusb_poll_thread(void __maybe_unused *arg) +{ + struct timeval tv_end = {1, 0}; + + RenameThread("USBPoll"); + + while (usb_polling) + libusb_handle_events_timeout_completed(NULL, &tv_end, NULL); + + /* Cancel any cancellable usb transfers */ + cancel_usb_transfers(); + + /* Keep event handling going until there are no async transfers in + * flight. */ + do { + libusb_handle_events_timeout_completed(NULL, &tv_end, NULL); + } while (async_usb_transfers()); + + return NULL; +} + +static void initialise_usb(void) { + int err = libusb_init(NULL); + + if (err) { + fprintf(stderr, "libusb_init() failed err %d", err); + fflush(stderr); + quit(1, "libusb_init() failed"); + } + initialise_usblocks(); + usb_polling = true; + pthread_create(&usb_poll_thread, NULL, libusb_poll_thread, NULL); +} +#else +#define initialise_usb() {} +#endif + int main(int argc, char *argv[]) { struct sigaction handler; + struct work *work = NULL; + bool pool_msg = false; struct thr_info *thr; struct block *block; + int i, j, slept = 0; unsigned int k; - int i, j; char *s; + g_logfile_enable = false; + strcpy(g_logfile_path, "cgminer.log"); + strcpy(g_logfile_openflag, "a+"); /* This dangerous functions tramples random dynamically allocated * variables so do it before anything at all */ if (unlikely(curl_global_init(CURL_GLOBAL_ALL))) - quit(1, "Failed to curl_global_init"); + early_quit(1, "Failed to curl_global_init"); + +# ifdef __linux + /* If we're on a small lowspec platform with only one CPU, we should + * yield after dropping a lock to allow a thread waiting for it to be + * able to get CPU time to grab the lock. */ + if (sysconf(_SC_NPROCESSORS_ONLN) == 1) + selective_yield = &sched_yield; +#endif + +#if LOCK_TRACKING + // Must be first + if (unlikely(pthread_mutex_init(&lockstat_lock, NULL))) + quithere(1, "Failed to pthread_mutex_init lockstat_lock errno=%d", errno); +#endif initial_args = malloc(sizeof(char *) * (argc + 1)); for (i = 0; i < argc; i++) initial_args[i] = strdup(argv[i]); initial_args[argc] = NULL; -#ifdef USE_USBUTILS - int err = libusb_init(NULL); - if (err) { - fprintf(stderr, "libusb_init() failed err %d", err); - fflush(stderr); - quit(1, "libusb_init() failed"); - } - mutex_init(&cgusb_lock); - mutex_init(&cgusbres_lock); - cglock_init(&cgusb_fd_lock); -#endif - mutex_init(&hash_lock); mutex_init(&console_lock); cglock_init(&control_lock); @@ -7525,14 +9821,23 @@ int main(int argc, char *argv[]) mutex_init(&lp_lock); if (unlikely(pthread_cond_init(&lp_cond, NULL))) - quit(1, "Failed to pthread_cond_init lp_cond"); + early_quit(1, "Failed to pthread_cond_init lp_cond"); mutex_init(&restart_lock); if (unlikely(pthread_cond_init(&restart_cond, NULL))) - quit(1, "Failed to pthread_cond_init restart_cond"); + early_quit(1, "Failed to pthread_cond_init restart_cond"); if (unlikely(pthread_cond_init(&gws_cond, NULL))) - quit(1, "Failed to pthread_cond_init gws_cond"); + early_quit(1, "Failed to pthread_cond_init gws_cond"); + + /* Create a unique get work queue */ + getq = tq_new(); + if (!getq) + early_quit(1, "Failed to create getq"); + /* We use the getq mutex as the staged lock */ + stgd_lock = &getq->mutex; + + initialise_usb(); snprintf(packagename, sizeof(packagename), "%s %s", PACKAGE, VERSION); @@ -7564,16 +9869,10 @@ int main(int argc, char *argv[]) for (i = 0; i < 36; i++) strcat(block->hash, "0"); HASH_ADD_STR(blocks, hash, block); - strcpy(current_block, block->hash); + strcpy(current_hash, block->hash); INIT_LIST_HEAD(&scan_devices); -#ifdef HAVE_OPENCL - memset(gpus, 0, sizeof(gpus)); - for (i = 0; i < MAX_GPUDEVICES; i++) - gpus[i].dynamic = true; -#endif - /* parse command line */ opt_register_table(opt_config_table, "Options for both config file and command line"); @@ -7582,26 +9881,118 @@ int main(int argc, char *argv[]) opt_parse(&argc, argv, applog_and_exit); if (argc != 1) - quit(1, "Unexpected extra commandline arguments"); + early_quit(1, "Unexpected extra commandline arguments"); if (!config_loaded) load_default_config(); - if (opt_benchmark) { + if (opt_benchmark || opt_benchfile) { struct pool *pool; - if (opt_scrypt) - quit(1, "Cannot use benchmark mode with scrypt"); pool = add_pool(); pool->rpc_url = malloc(255); - strcpy(pool->rpc_url, "Benchmark"); + if (opt_benchfile) + strcpy(pool->rpc_url, "Benchfile"); + else + strcpy(pool->rpc_url, "Benchmark"); pool->rpc_user = pool->rpc_url; pool->rpc_pass = pool->rpc_url; + pool->rpc_userpass = pool->rpc_url; + pool->sockaddr_url = pool->rpc_url; + strncpy(pool->diff, "?", sizeof(pool->diff)-1); + pool->diff[sizeof(pool->diff)-1] = '\0'; enable_pool(pool); pool->idle = false; successful_connect = true; + + for (i = 0; i < 16; i++) { + hex2bin(&bench_hidiff_bins[i][0], &bench_hidiffs[i][0], 160); + hex2bin(&bench_lodiff_bins[i][0], &bench_lodiffs[i][0], 160); + } + set_target(bench_target, 32); + } + + if(opt_version_path) { + FILE * fpversion = fopen(opt_version_path, "rb"); + char tmp[256] = {0}; + int len = 0; + char * start = 0; + if(fpversion == NULL) { + applog(LOG_ERR, "Open miner version file %s error", opt_version_path); + } else { + len = fread(tmp, 1, 256, fpversion); + if(len <= 0) { + applog(LOG_ERR, "Read miner version file %s error %d", opt_version_path, len); + } else { + start = strstr(tmp, "\n"); + if(start == NULL) { + strcpy(g_miner_compiletime, tmp); + } else { + memcpy(g_miner_compiletime, tmp, start-tmp); + strcpy(g_miner_type, start+1); + } + if(g_miner_compiletime[strlen(g_miner_compiletime)-1] == '\n') + g_miner_compiletime[strlen(g_miner_compiletime)-1] = 0; + if(g_miner_compiletime[strlen(g_miner_compiletime)-1] == '\r') + g_miner_compiletime[strlen(g_miner_compiletime)-1] = 0; + if(g_miner_type[strlen(g_miner_type)-1] == '\n') + g_miner_type[strlen(g_miner_type)-1] = 0; + if(g_miner_type[strlen(g_miner_type)-1] == '\r') + g_miner_type[strlen(g_miner_type)-1] = 0; + } + } + applog(LOG_ERR, "Miner compile time: %s type: %s", g_miner_compiletime, g_miner_type); + } + + if(opt_logfile_path) { + g_logfile_enable = true; + strcpy(g_logfile_path, opt_logfile_path); + if(opt_logfile_openflag) { + strcpy(g_logfile_openflag, opt_logfile_openflag); + } + applog(LOG_ERR, "Log file path: %s Open flag: %s", g_logfile_path, g_logfile_openflag); } + if(opt_logwork_path) { + char szfilepath[256] = {0}; + if(opt_logwork_asicnum) { + if(strlen(opt_logwork_asicnum) <= 0) { + quit(1, "Log work asic num empty"); + } + g_logwork_asicnum = atoi(opt_logwork_asicnum); + if(g_logwork_asicnum != 1 && g_logwork_asicnum != 32 && g_logwork_asicnum != 64) { + quit(1, "Log work asic num must be 1, 32, 64"); + } + applog(LOG_ERR, "Log work path: %s Asic num: %s", opt_logwork_path, opt_logwork_asicnum); + } else { + applog(LOG_ERR, "Log work path: %s", opt_logwork_path); + } + + sprintf(szfilepath, "%s.txt", opt_logwork_path); + g_logwork_file = fopen(szfilepath, "a+"); + applog(LOG_ERR, "Log work open file %s", szfilepath); + + if(g_logwork_asicnum == 1) { + sprintf(szfilepath, "%s%02d.txt", opt_logwork_path, g_logwork_asicnum); + g_logwork_files[0] = fopen(szfilepath, "a+"); + applog(LOG_ERR, "Log work open asic %d file %s", g_logwork_asicnum, szfilepath); + } else if(g_logwork_asicnum == 32 || g_logwork_asicnum == 64) { + for(i = 0; i <= g_logwork_asicnum; i++) { + sprintf(szfilepath, "%s%02d_%02d.txt", opt_logwork_path, g_logwork_asicnum, i); + g_logwork_files[i] = fopen(szfilepath, "a+"); + applog(LOG_ERR, "Log work open asic %d file %s", g_logwork_asicnum, szfilepath); + } + } + + if(opt_logwork_diff) { + for(i = 0; i <= 64; i++) { + sprintf(szfilepath, "%s_diff_%02d.txt", opt_logwork_path, i); + g_logwork_diffs[i] = fopen(szfilepath, "a+"); + applog(LOG_ERR, "Log work open diff file %s", szfilepath); + } + } + } + #ifdef HAVE_CURSES if (opt_realquiet || opt_display_devs) use_curses = false; @@ -7635,14 +10026,13 @@ int main(int argc, char *argv[]) if (want_per_device_stats) opt_log_output = true; - /* Use a shorter scantime for scrypt */ if (opt_scantime < 0) - opt_scantime = opt_scrypt ? 30 : 60; + opt_scantime = 60; - total_control_threads = 9; + total_control_threads = 8; control_thr = calloc(total_control_threads, sizeof(*thr)); if (!control_thr) - quit(1, "Failed to calloc control_thr"); + early_quit(1, "Failed to calloc control_thr"); gwsched_thr_id = 0; @@ -7650,53 +10040,19 @@ int main(int argc, char *argv[]) usb_initialise(); // before device detection - if (!opt_scrypt) { - cgsem_init(&usb_resource_sem); - usbres_thr_id = 1; - thr = &control_thr[usbres_thr_id]; - if (thr_info_create(thr, NULL, usb_resource_thread, thr)) - quit(1, "usb resource thread create failed"); - pthread_detach(thr->pth); - } -#endif - -#ifdef HAVE_OPENCL - if (!opt_nogpu) - opencl_drv.drv_detect(); - gpu_threads = 0; -#endif - -#ifdef USE_ICARUS - if (!opt_scrypt) - icarus_drv.drv_detect(); -#endif - -#ifdef USE_BFLSC - if (!opt_scrypt) - bflsc_drv.drv_detect(); -#endif - -#ifdef USE_BITFORCE - if (!opt_scrypt) - bitforce_drv.drv_detect(); -#endif - -#ifdef USE_MODMINER - if (!opt_scrypt) - modminer_drv.drv_detect(); + cgsem_init(&usb_resource_sem); + usbres_thr_id = 1; + thr = &control_thr[usbres_thr_id]; + if (thr_info_create(thr, NULL, usb_resource_thread, thr)) + early_quit(1, "usb resource thread create failed"); + pthread_detach(thr->pth); #endif -#ifdef USE_ZTEX - if (!opt_scrypt) - ztex_drv.drv_detect(); -#endif + /* Use the DRIVER_PARSE_COMMANDS macro to fill all the device_drvs */ + DRIVER_PARSE_COMMANDS(DRIVER_FILL_DEVICE_DRV) - /* Detect avalon last since it will try to claim the device regardless - * as detection is unreliable. */ -#ifdef USE_AVALON - if (!opt_scrypt) - avalon_drv.drv_detect(); -#endif + /* Use the DRIVER_PARSE_COMMANDS macro to detect all devices */ + DRIVER_PARSE_COMMANDS(DRIVER_DRV_DETECT_ALL) if (opt_display_devs) { applog(LOG_ERR, "Devices detected:"); @@ -7707,27 +10063,12 @@ int main(int argc, char *argv[]) else applog(LOG_ERR, " %2d. %s %d (driver: %s)", i, cgpu->drv->name, cgpu->device_id, cgpu->drv->dname); } - quit(0, "%d devices listed", total_devices); + early_quit(0, "%d devices listed", total_devices); } mining_threads = 0; - if (opt_devs_enabled) { - for (i = 0; i < MAX_DEVICES; i++) { - if (devices_enabled[i]) { - if (i >= total_devices) - quit (1, "Command line options set a device that doesn't exist"); - enable_device(devices[i]); - } else if (i < total_devices) { - if (!opt_removedisabled) - enable_device(devices[i]); - devices[i]->deven = DEV_DISABLED; - } - } - total_devices = cgminer_id_count; - } else { - for (i = 0; i < total_devices; ++i) - enable_device(devices[i]); - } + for (i = 0; i < total_devices; ++i) + enable_device(devices[i]); #ifdef USE_USBUTILS if (!total_devices) { @@ -7736,7 +10077,7 @@ int main(int argc, char *argv[]) } #else if (!total_devices) - quit(1, "All devices disabled, cannot mine!"); + early_quit(1, "All devices disabled, cannot mine!"); #endif most_devices = total_devices; @@ -7759,7 +10100,7 @@ int main(int argc, char *argv[]) #ifdef HAVE_CURSES if (!use_curses || !input_pool(false)) #endif - quit(1, "Pool setup failed"); + early_quit(1, "Pool setup failed"); } for (i = 0; i < total_pools; i++) { @@ -7770,12 +10111,14 @@ int main(int argc, char *argv[]) pool->cgminer_pool_stats.getwork_wait_min.tv_sec = MIN_SEC_UNSET; if (!pool->rpc_userpass) { - if (!pool->rpc_user || !pool->rpc_pass) - quit(1, "No login credentials supplied for pool %u %s", i, pool->rpc_url); + if (!pool->rpc_pass) + pool->rpc_pass = strdup(""); + if (!pool->rpc_user) + early_quit(1, "No login credentials supplied for pool %u %s", i, pool->rpc_url); siz = strlen(pool->rpc_user) + strlen(pool->rpc_pass) + 2; pool->rpc_userpass = malloc(siz); if (!pool->rpc_userpass) - quit(1, "Failed to malloc userpass"); + early_quit(1, "Failed to malloc userpass"); snprintf(pool->rpc_userpass, siz, "%s:%s", pool->rpc_user, pool->rpc_pass); } } @@ -7794,31 +10137,45 @@ int main(int argc, char *argv[]) mining_thr = calloc(mining_threads, sizeof(thr)); if (!mining_thr) - quit(1, "Failed to calloc mining_thr"); + early_quit(1, "Failed to calloc mining_thr"); for (i = 0; i < mining_threads; i++) { mining_thr[i] = calloc(1, sizeof(*thr)); if (!mining_thr[i]) - quit(1, "Failed to calloc mining_thr[%d]", i); + early_quit(1, "Failed to calloc mining_thr[%d]", i); } - stage_thr_id = 2; - thr = &control_thr[stage_thr_id]; - thr->q = tq_new(); - if (!thr->q) - quit(1, "Failed to tq_new"); - /* start stage thread */ - if (thr_info_create(thr, NULL, stage_thread, thr)) - quit(1, "stage thread create failed"); - pthread_detach(thr->pth); + // Start threads + k = 0; + for (i = 0; i < total_devices; ++i) { + struct cgpu_info *cgpu = devices[i]; + cgpu->thr = malloc(sizeof(*cgpu->thr) * (cgpu->threads+1)); + cgpu->thr[cgpu->threads] = NULL; + cgpu->status = LIFE_INIT; - /* Create a unique get work queue */ - getq = tq_new(); - if (!getq) - quit(1, "Failed to create getq"); - /* We use the getq mutex as the staged lock */ - stgd_lock = &getq->mutex; + for (j = 0; j < cgpu->threads; ++j, ++k) { + thr = get_thread(k); + thr->id = k; + thr->cgpu = cgpu; + thr->device_thread = j; - if (opt_benchmark) + if (!cgpu->drv->thread_prepare(thr)) + continue; + + if (unlikely(thr_info_create(thr, NULL, miner_thread, thr))) + early_quit(1, "thread %d create failed", thr->id); + + cgpu->thr[j] = thr; + + /* Enable threads for devices set not to mine but disable + * their queue in case we wish to enable them later */ + if (cgpu->deven != DEV_DISABLED) { + applog(LOG_DEBUG, "Pushing sem post to thread %d", thr->id); + cgsem_post(&thr->sem); + } + } + } + + if (opt_benchmark || opt_benchfile) goto begin_bench; for (i = 0; i < total_pools; i++) { @@ -7828,43 +10185,47 @@ int main(int argc, char *argv[]) pool->idle = true; } + /* Look for at least one active pool before starting */ applog(LOG_NOTICE, "Probing for an alive pool"); + probe_pools(); do { - int slept = 0; - - /* Look for at least one active pool before starting */ - probe_pools(); - do { - sleep(1); - slept++; - } while (!pools_active && slept < 60); + sleep(1); + slept++; + } while (!pools_active && slept < 60); - if (!pools_active) { + while (!pools_active) { + if (!pool_msg) { applog(LOG_ERR, "No servers were found that could be used to get work from."); applog(LOG_ERR, "Please check the details from the list below of the servers you have input"); applog(LOG_ERR, "Most likely you have input the wrong URL, forgotten to add a port, or have not set up workers"); for (i = 0; i < total_pools; i++) { - struct pool *pool; + struct pool *pool = pools[i]; - pool = pools[i]; applog(LOG_WARNING, "Pool: %d URL: %s User: %s Password: %s", - i, pool->rpc_url, pool->rpc_user, pool->rpc_pass); + i, pool->rpc_url, pool->rpc_user, pool->rpc_pass); } + pool_msg = true; + if (use_curses) + applog(LOG_ERR, "Press any key to exit, or cgminer will wait indefinitely for an alive pool."); + } + if (!use_curses) + early_quit(0, "No servers could be used! Exiting."); #ifdef HAVE_CURSES - if (use_curses) { - halfdelay(150); - applog(LOG_ERR, "Press any key to exit, or cgminer will try again in 15s."); - if (getch() != ERR) - quit(0, "No servers could be used! Exiting."); - cbreak(); - } else + touchwin(logwin); + wrefresh(logwin); + halfdelay(10); + if (getch() != ERR) + early_quit(0, "No servers could be used! Exiting."); + cbreak(); #endif - quit(0, "No servers could be used! Exiting."); - } - } while (!pools_active); + }; begin_bench: total_mhashes_done = 0; + for(i = 0; i < CG_LOCAL_MHASHES_MAX_NUM; i++) { + g_local_mhashes_dones[i] = 0; + } + g_local_mhashes_index = 0; for (i = 0; i < total_devices; i++) { struct cgpu_info *cgpu = devices[i]; @@ -7873,112 +10234,63 @@ int main(int argc, char *argv[]) cgtime(&total_tv_start); cgtime(&total_tv_end); + cgtime(&tv_hashmeter); get_datestamp(datestamp, sizeof(datestamp), &total_tv_start); - // Start threads - k = 0; - for (i = 0; i < total_devices; ++i) { - struct cgpu_info *cgpu = devices[i]; - cgpu->thr = malloc(sizeof(*cgpu->thr) * (cgpu->threads+1)); - cgpu->thr[cgpu->threads] = NULL; - cgpu->status = LIFE_INIT; - - for (j = 0; j < cgpu->threads; ++j, ++k) { - thr = get_thread(k); - thr->id = k; - thr->cgpu = cgpu; - thr->device_thread = j; - - if (!cgpu->drv->thread_prepare(thr)) - continue; - - if (unlikely(thr_info_create(thr, NULL, miner_thread, thr))) - quit(1, "thread %d create failed", thr->id); - - cgpu->thr[j] = thr; - - /* Enable threads for devices set not to mine but disable - * their queue in case we wish to enable them later */ - if (cgpu->deven != DEV_DISABLED) { - applog(LOG_DEBUG, "Pushing sem post to thread %d", thr->id); - cgsem_post(&thr->sem); - } - } - } - -#ifdef HAVE_OPENCL - applog(LOG_INFO, "%d gpu miner threads started", gpu_threads); - for (i = 0; i < nDevs; i++) - pause_dynamic_threads(i); -#endif - - cgtime(&total_tv_start); - cgtime(&total_tv_end); - - watchpool_thr_id = 3; + watchpool_thr_id = 2; thr = &control_thr[watchpool_thr_id]; /* start watchpool thread */ if (thr_info_create(thr, NULL, watchpool_thread, NULL)) - quit(1, "watchpool thread create failed"); + early_quit(1, "watchpool thread create failed"); pthread_detach(thr->pth); - watchdog_thr_id = 4; + watchdog_thr_id = 3; thr = &control_thr[watchdog_thr_id]; /* start watchdog thread */ if (thr_info_create(thr, NULL, watchdog_thread, NULL)) - quit(1, "watchdog thread create failed"); + early_quit(1, "watchdog thread create failed"); pthread_detach(thr->pth); -#ifdef HAVE_OPENCL - /* Create reinit gpu thread */ - gpur_thr_id = 5; - thr = &control_thr[gpur_thr_id]; - thr->q = tq_new(); - if (!thr->q) - quit(1, "tq_new failed for gpur_thr_id"); - if (thr_info_create(thr, NULL, reinit_gpu, thr)) - quit(1, "reinit_gpu thread create failed"); -#endif - /* Create API socket thread */ - api_thr_id = 6; + api_thr_id = 5; thr = &control_thr[api_thr_id]; if (thr_info_create(thr, NULL, api_thread, thr)) - quit(1, "API thread create failed"); + early_quit(1, "API thread create failed"); #ifdef USE_USBUTILS - if (!opt_scrypt) { - hotplug_thr_id = 7; - thr = &control_thr[hotplug_thr_id]; - if (thr_info_create(thr, NULL, hotplug_thread, thr)) - quit(1, "hotplug thread create failed"); - pthread_detach(thr->pth); - } + hotplug_thr_id = 6; + thr = &control_thr[hotplug_thr_id]; + if (thr_info_create(thr, NULL, hotplug_thread, thr)) + early_quit(1, "hotplug thread create failed"); + pthread_detach(thr->pth); #endif #ifdef HAVE_CURSES /* Create curses input thread for keyboard input. Create this last so * that we know all threads are created since this can call kill_work * to try and shut down all previous threads. */ - input_thr_id = 8; + input_thr_id = 7; thr = &control_thr[input_thr_id]; if (thr_info_create(thr, NULL, input_thread, thr)) - quit(1, "input thread create failed"); + early_quit(1, "input thread create failed"); pthread_detach(thr->pth); #endif /* Just to be sure */ - if (total_control_threads != 9) - quit(1, "incorrect total_control_threads (%d) should be 9", total_control_threads); + if (total_control_threads != 8) + early_quit(1, "incorrect total_control_threads (%d) should be 8", total_control_threads); + + set_highprio(); /* Once everything is set up, main() becomes the getwork scheduler */ while (42) { - int ts, max_staged = opt_queue; + int ts, max_staged = max_queue; struct pool *pool, *cp; bool lagging = false; - struct curl_ent *ce; - struct work *work; + if (opt_work_update) + signal_work_update(); + opt_work_update = false; cp = current_pool(); /* If the primary pool is a getwork pool and cannot roll work, @@ -7994,20 +10306,41 @@ int main(int argc, char *argv[]) /* Wait until hash_pop tells us we need to create more work */ if (ts > max_staged) { + if (work_emptied && max_queue < opt_queue) { + max_queue++; + work_emptied = false; + } + work_filled = true; pthread_cond_wait(&gws_cond, stgd_lock); ts = __total_staged(); } mutex_unlock(stgd_lock); - if (ts > max_staged) + if (ts > max_staged) { + /* Keeps slowly generating work even if it's not being + * used to keep last_getwork incrementing and to see + * if pools are still alive. */ + if (work_emptied && max_queue < opt_queue) { + max_queue++; + work_emptied = false; + } + work_filled = true; + work = hash_pop(false); + if (work) + discard_work(work); continue; + } + if (work) + discard_work(work); work = make_work(); if (lagging && !pool_tset(cp, &cp->lagging)) { applog(LOG_WARNING, "Pool %d not providing work fast enough", cp->pool_no); cp->getfail_occasions++; total_go++; + if (!pool_localgen(cp) && max_queue < opt_queue) + applog(LOG_INFO, "Increasing queue to %d", ++max_queue); } pool = select_pool(lagging); retry: @@ -8027,6 +10360,37 @@ int main(int argc, char *argv[]) continue; } + if (opt_benchfile) { + get_benchfile_work(work); + applog(LOG_DEBUG, "Generated benchfile work"); + stage_work(work); + continue; + } else if (opt_benchmark) { + get_benchmark_work(work); + applog(LOG_DEBUG, "Generated benchmark work"); + stage_work(work); + continue; + } + +#ifdef HAVE_LIBCURL + struct curl_ent *ce; + + if (pool->gbt_solo) { + while (pool->idle) { + struct pool *altpool = select_pool(true); + + cgsleep_ms(5000); + if (altpool != pool) { + pool = altpool; + goto retry; + } + } + gen_solo_work(pool, work); + applog(LOG_DEBUG, "Generated GBT SOLO work"); + stage_work(work); + continue; + } + if (pool->has_gbt) { while (pool->idle) { struct pool *altpool = select_pool(true); @@ -8049,13 +10413,6 @@ int main(int argc, char *argv[]) continue; } - if (opt_benchmark) { - get_benchmark_work(work); - applog(LOG_DEBUG, "Generated benchmark work"); - stage_work(work); - continue; - } - work->pool = pool; ce = pop_curl_entry(pool); /* obtain new work from bitcoin via JSON-RPC */ @@ -8068,6 +10425,7 @@ int main(int argc, char *argv[]) cgsleep_ms(5000); push_curl_entry(ce, pool); pool = select_pool(!opt_fail_only); + free_work(work); goto retry; } if (ts >= max_staged) @@ -8078,6 +10436,7 @@ int main(int argc, char *argv[]) applog(LOG_DEBUG, "Generated getwork work"); stage_work(work); push_curl_entry(ce, pool); +#endif } return 0; diff --git a/compat/.gitignore b/compat/.gitignore new file mode 100644 index 0000000000..8e687f879b --- /dev/null +++ b/compat/.gitignore @@ -0,0 +1,3 @@ +libusb-1.0/libusb/libusb-1.0.la +libusb-1.0/libusb/*.lo +libusb-1.0/libusb/os/*.lo diff --git a/compat/Makefile.am b/compat/Makefile.am index 7c695276f4..5a322148cb 100644 --- a/compat/Makefile.am +++ b/compat/Makefile.am @@ -1,6 +1,8 @@ -SUBDIRS = jansson +SUBDIRS = jansson-2.6 if WANT_USBUTILS +if WANT_STATIC_LIBUSB SUBDIRS += libusb-1.0 endif +endif diff --git a/compat/jansson-2.6/CHANGES b/compat/jansson-2.6/CHANGES new file mode 100644 index 0000000000..99d1647d75 --- /dev/null +++ b/compat/jansson-2.6/CHANGES @@ -0,0 +1,583 @@ +Version 2.6 +=========== + +Released 2014-02-11 + +* Security: + + - CVE-2013-6401: The hash function used by the hashtable + implementation has been changed, and is automatically seeded with + random data when the first JSON object is created. This prevents + an attacker from causing large JSON objects with specially crafted + keys perform poorly. + +* New features: + + - `json_object_seed()`: Set the seed value of the hash function. + +* Bug fixes: + + - Include CMake specific files in the release tarball. + +* Documentation: + + - Fix tutorial source to send a User-Agent header, which is now + required by the GitHub API. + + - Set all memory to zero in secure_free() example. + + +Version 2.5 +=========== + +Released 2013-09-19 + +* New features: + + - `json_pack()` and friends: Add format specifiers ``s#``, ``+`` and + ``+#``. + + - Add ``JSON_DECODE_INT_AS_REAL`` decoding flag to treat all numbers + as real in the decoder (#123). + + - Add `json_array_foreach()`, paralleling `json_object_foreach()` + (#118). + +* Bug fixes: + + - `json_dumps()` and friends: Don't crash if json is *NULL* and + ``JSON_ENCODE_ANY`` is set. + + - Fix a theoretical integer overflow in `jsonp_strdup()`. + + - Fix `l_isxdigit()` macro (#97). + + - Fix an off-by-one error in `json_array_remove()`. + +* Build: + + - Support CMake in addition to GNU Autotools (#106, #107, #112, + #115, #120, #127). + + - Support building for Android (#109). + + - Don't use ``-Werror`` by default. + + - Support building and testing with VPATH (#93). + + - Fix compilation when ``NDEBUG`` is defined (#128) + +* Tests: + + - Fix a refleak in ``test/bin/json_process.c``. + +* Documentation: + + - Clarify the return value of `json_load_callback_t`. + + - Document how to circumvent problems with separate heaps on Windows. + + - Fix memory leaks and warnings in ``github_commits.c``. + + - Use `json_decref()` properly in tutorial. + +* Other: + + - Make it possible to forward declare ``struct json_t``. + + +Version 2.4 +=========== + +Released 2012-09-23 + +* New features: + + - Add `json_boolean()` macro that returns the JSON true or false + value based on its argument (#86). + + - Add `json_load_callback()` that calls a callback function + repeatedly to read the JSON input (#57). + + - Add JSON_ESCAPE_SLASH encoding flag to escape all occurences of + ``/`` with ``\/``. + +* Bug fixes: + + - Check for and reject NaN and Inf values for reals. Encoding these + values resulted in invalid JSON. + + - Fix `json_real_set()` to return -1 on error. + +* Build: + + - Jansson now builds on Windows with Visual Studio 2010, and + includes solution and project files in ``win32/vs2010/`` + directory. + + - Fix build warnings (#77, #78). + + - Add ``-no-undefined`` to LDFLAGS (#90). + +* Tests: + + - Fix the symbol exports test on Linux/PPC64 (#88). + +* Documentation: + + - Fix typos (#73, #84). + + +Version 2.3.1 +============= + +Released 2012-04-20 + +* Build issues: + + - Only use ``long long`` if ``strtoll()`` is also available. + +* Documentation: + + - Fix the names of library version constants in documentation. (#52) + + - Change the tutorial to use GitHub API v3. (#65) + +* Tests: + + - Make some tests locale independent. (#51) + + - Distribute the library exports test in the tarball. + + - Make test run on shells that don't support the ``export FOO=bar`` + syntax. + + +Version 2.3 +=========== + +Released 2012-01-27 + +* New features: + + - `json_unpack()` and friends: Add support for optional object keys + with the ``{s?o}`` syntax. + + - Add `json_object_update_existing()` and + `json_object_update_missing()`, for updating only existing keys or + only adding missing keys to an object. (#37) + + - Add `json_object_foreach()` for more convenient iteration over + objects. (#45, #46) + + - When decoding JSON, write the number of bytes that were read from + input to ``error.position`` also on success. This is handy with + ``JSON_DISABLE_EOF_CHECK``. + + - Add support for decoding any JSON value, not just arrays or + objects. The support is enabled with the new ``JSON_DECODE_ANY`` + flag. Patch by Andrea Marchesini. (#4) + +* Bug fixes + + - Avoid problems with object's serial number growing too big. (#40, + #41) + + - Decoding functions now return NULL if the first argument is NULL. + Patch by Andrea Marchesini. + + - Include ``jansson_config.h.win32`` in the distribution tarball. + + - Remove ``+`` and leading zeros from exponents in the encoder. + (#39) + + - Make Jansson build and work on MinGW. (#39, #38) + +* Documentation + + - Note that the same JSON values must not be encoded in parallel by + separate threads. (#42) + + - Document MinGW support. + + +Version 2.2.1 +============= + +Released 2011-10-06 + +* Bug fixes: + + - Fix real number encoding and decoding under non-C locales. (#32) + + - Fix identifier decoding under non-UTF-8 locales. (#35) + + - `json_load_file()`: Open the input file in binary mode for maximum + compatiblity. + +* Documentation: + + - Clarify the lifecycle of the result of the ``s`` fromat of + `json_unpack()`. (#31) + + - Add some portability info. (#36) + + - Little clarifications here and there. + +* Other: + + - Some style fixes, issues detected by static analyzers. + + +Version 2.2 +=========== + +Released 2011-09-03 + +* New features: + + - `json_dump_callback()`: Pass the encoder output to a callback + function in chunks. + +* Bug fixes: + + - `json_string_set()`: Check that target is a string and value is + not NULL. + +* Other: + + - Documentation typo fixes and clarifications. + + +Version 2.1 +=========== + +Released 2011-06-10 + +* New features: + + - `json_loadb()`: Decode a string with a given size, useful if the + string is not null terminated. + + - Add ``JSON_ENCODE_ANY`` encoding flag to allow encoding any JSON + value. By default, only arrays and objects can be encoded. (#19) + + - Add ``JSON_REJECT_DUPLICATES`` decoding flag to issue a decoding + error if any JSON object in the input contins duplicate keys. (#3) + + - Add ``JSON_DISABLE_EOF_CHECK`` decoding flag to stop decoding after a + valid JSON input. This allows other data after the JSON data. + +* Bug fixes: + + - Fix an additional memory leak when memory allocation fails in + `json_object_set()` and friends. + + - Clear errno before calling `strtod()` for better portability. (#27) + +* Building: + + - Avoid set-but-not-used warning/error in a test. (#20) + +* Other: + + - Minor clarifications to documentation. + + +Version 2.0.1 +============= + +Released 2011-03-31 + +* Bug fixes: + + - Replace a few `malloc()` and `free()` calls with their + counterparts that support custom memory management. + + - Fix object key hashing in json_unpack() strict checking mode. + + - Fix the parentheses in ``JANSSON_VERSION_HEX`` macro. + + - Fix `json_object_size()` return value. + + - Fix a few compilation issues. + +* Portability: + + - Enhance portability of `va_copy()`. + + - Test framework portability enhancements. + +* Documentation: + + - Distribute ``doc/upgrading.rst`` with the source tarball. + + - Build documentation in strict mode in ``make distcheck``. + + +Version 2.0 +=========== + +Released 2011-02-28 + +This release is backwards incompatible with the 1.x release series. +See the chapter "Upgrading from older versions" in documentation for +details. + +* Backwards incompatible changes: + + - Unify unsigned integer usage in the API: All occurences of + unsigned int and unsigned long have been replaced with size_t. + + - Change JSON integer's underlying type to the widest signed integer + type available, i.e. long long if it's supported, otherwise long. + Add a typedef json_int_t that defines the type. + + - Change the maximum indentation depth to 31 spaces in encoder. This + frees up bits from the flags parameter of encoding functions + `json_dumpf()`, `json_dumps()` and `json_dump_file()`. + + - For future needs, add a flags parameter to all decoding functions + `json_loadf()`, `json_loads()` and `json_load_file()`. + +* New features + + - `json_pack()`, `json_pack_ex()`, `json_vpack_ex()`: Create JSON + values based on a format string. + + - `json_unpack()`, `json_unpack_ex()`, `json_vunpack_ex()`: Simple + value extraction and validation functionality based on a format + string. + + - Add column, position and source fields to the ``json_error_t`` + struct. + + - Enhance error reporting in the decoder. + + - ``JANSSON_VERSION`` et al.: Preprocessor constants that define the + library version. + + - `json_set_alloc_funcs()`: Set custom memory allocation functions. + +* Fix many portability issues, especially on Windows. + +* Configuration + + - Add file ``jansson_config.h`` that contains site specific + configuration. It's created automatically by the configure script, + or can be created by hand if the configure script cannot be used. + The file ``jansson_config.h.win32`` can be used without + modifications on Windows systems. + + - Add a section to documentation describing how to build Jansson on + Windows. + + - Documentation now requires Sphinx 1.0 or newer. + + +Version 1.3 +=========== + +Released 2010-06-13 + +* New functions: + + - `json_object_iter_set()`, `json_object_iter_set_new()`: Change + object contents while iterating over it. + + - `json_object_iter_at()`: Return an iterator that points to a + specific object item. + +* New encoding flags: + + - ``JSON_PRESERVE_ORDER``: Preserve the insertion order of object + keys. + +* Bug fixes: + + - Fix an error that occured when an array or object was first + encoded as empty, then populated with some data, and then + re-encoded + + - Fix the situation like above, but when the first encoding resulted + in an error + +* Documentation: + + - Clarify the documentation on reference stealing, providing an + example usage pattern + + +Version 1.2.1 +============= + +Released 2010-04-03 + +* Bug fixes: + + - Fix reference counting on ``true``, ``false`` and ``null`` + - Estimate real number underflows in decoder with 0.0 instead of + issuing an error + +* Portability: + + - Make ``int32_t`` available on all systems + - Support compilers that don't have the ``inline`` keyword + - Require Autoconf 2.60 (for ``int32_t``) + +* Tests: + + - Print test names correctly when ``VERBOSE=1`` + - ``test/suites/api``: Fail when a test fails + - Enhance tests for iterators + - Enhance tests for decoding texts that contain null bytes + +* Documentation: + + - Don't remove ``changes.rst`` in ``make clean`` + - Add a chapter on RFC conformance + + +Version 1.2 +=========== + +Released 2010-01-21 + +* New functions: + + - `json_equal()`: Test whether two JSON values are equal + - `json_copy()` and `json_deep_copy()`: Make shallow and deep copies + of JSON values + - Add a version of all functions taking a string argument that + doesn't check for valid UTF-8: `json_string_nocheck()`, + `json_string_set_nocheck()`, `json_object_set_nocheck()`, + `json_object_set_new_nocheck()` + +* New encoding flags: + + - ``JSON_SORT_KEYS``: Sort objects by key + - ``JSON_ENSURE_ASCII``: Escape all non-ASCII Unicode characters + - ``JSON_COMPACT``: Use a compact representation with all unneeded + whitespace stripped + +* Bug fixes: + + - Revise and unify whitespace usage in encoder: Add spaces between + array and object items, never append newline to output. + - Remove const qualifier from the ``json_t`` parameter in + `json_string_set()`, `json_integer_set()` and `json_real_set`. + - Use ``int32_t`` internally for representing Unicode code points + (int is not enough on all platforms) + +* Other changes: + + - Convert ``CHANGES`` (this file) to reStructured text and add it to + HTML documentation + - The test system has been refactored. Python is no longer required + to run the tests. + - Documentation can now be built by invoking ``make html`` + - Support for pkg-config + + +Version 1.1.3 +============= + +Released 2009-12-18 + +* Encode reals correctly, so that first encoding and then decoding a + real always produces the same value +* Don't export private symbols in ``libjansson.so`` + + +Version 1.1.2 +============= + +Released 2009-11-08 + +* Fix a bug where an error message was not produced if the input file + could not be opened in `json_load_file()` +* Fix an assertion failure in decoder caused by a minus sign without a + digit after it +* Remove an unneeded include of ``stdint.h`` in ``jansson.h`` + + +Version 1.1.1 +============= + +Released 2009-10-26 + +* All documentation files were not distributed with v1.1; build + documentation in make distcheck to prevent this in the future +* Fix v1.1 release date in ``CHANGES`` + + +Version 1.1 +=========== + +Released 2009-10-20 + +* API additions and improvements: + + - Extend array and object APIs + - Add functions to modify integer, real and string values + - Improve argument validation + - Use unsigned int instead of ``uint32_t`` for encoding flags + +* Enhance documentation + + - Add getting started guide and tutorial + - Fix some typos + - General clarifications and cleanup + +* Check for integer and real overflows and underflows in decoder +* Make singleton values thread-safe (``true``, ``false`` and ``null``) +* Enhance circular reference handling +* Don't define ``-std=c99`` in ``AM_CFLAGS`` +* Add C++ guards to ``jansson.h`` +* Minor performance and portability improvements +* Expand test coverage + + +Version 1.0.4 +============= + +Released 2009-10-11 + +* Relax Autoconf version requirement to 2.59 +* Make Jansson compile on platforms where plain ``char`` is unsigned +* Fix API tests for object + + +Version 1.0.3 +============= + +Released 2009-09-14 + +* Check for integer and real overflows and underflows in decoder +* Use the Python json module for tests, or simplejson if the json + module is not found +* Distribute changelog (this file) + + +Version 1.0.2 +============= + +Released 2009-09-08 + +* Handle EOF correctly in decoder + + +Version 1.0.1 +============= + +Released 2009-09-04 + +* Fixed broken `json_is_boolean()` + + +Version 1.0 +=========== + +Released 2009-08-25 + +* Initial release diff --git a/compat/jansson/LICENSE b/compat/jansson-2.6/LICENSE similarity index 94% rename from compat/jansson/LICENSE rename to compat/jansson-2.6/LICENSE index 552b3498b0..a8fb5b82f3 100644 --- a/compat/jansson/LICENSE +++ b/compat/jansson-2.6/LICENSE @@ -1,4 +1,4 @@ -Copyright (c) 2009, 2010 Petri Lehtinen +Copyright (c) 2009-2013 Petri Lehtinen Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/compat/jansson-2.6/Makefile.am b/compat/jansson-2.6/Makefile.am new file mode 100644 index 0000000000..a6efeb0aa2 --- /dev/null +++ b/compat/jansson-2.6/Makefile.am @@ -0,0 +1,17 @@ +ACLOCAL_AMFLAGS = -I m4 + +EXTRA_DIST = CHANGES LICENSE README.rst +SUBDIRS = src + +# "make distcheck" builds the dvi target, so use it to check that the +# documentation is built correctly. +dvi: + $(MAKE) SPHINXOPTS_EXTRA=-W html + +pkgconfigdir = $(libdir)/pkgconfig +pkgconfig_DATA = jansson.pc + +if GCC +# These flags are gcc specific +export AM_CFLAGS = -Wall -Wextra -Wdeclaration-after-statement +endif diff --git a/compat/jansson-2.6/README.rst b/compat/jansson-2.6/README.rst new file mode 100644 index 0000000000..a01cbc02d0 --- /dev/null +++ b/compat/jansson-2.6/README.rst @@ -0,0 +1,63 @@ +Jansson README +============== + +.. image:: https://travis-ci.org/akheron/jansson.png + :alt: Build status + :target: https://travis-ci.org/akheron/jansson + +Jansson_ is a C library for encoding, decoding and manipulating JSON +data. Its main features and design principles are: + +- Simple and intuitive API and data model + +- Comprehensive documentation + +- No dependencies on other libraries + +- Full Unicode support (UTF-8) + +- Extensive test suite + +Jansson is licensed under the `MIT license`_; see LICENSE in the +source distribution for details. + + +Compilation and Installation +---------------------------- + +If you obtained a source tarball, just use the standard autotools +commands:: + + $ ./configure + $ make + $ make install + +To run the test suite, invoke:: + + $ make check + +If the source has been checked out from a Git repository, the +./configure script has to be generated first. The easiest way is to +use autoreconf:: + + $ autoreconf -i + + +Documentation +------------- + +Prebuilt HTML documentation is available at +http://www.digip.org/jansson/doc/. + +The documentation source is in the ``doc/`` subdirectory. To generate +HTML documentation, invoke:: + + $ make html + +Then, point your browser to ``doc/_build/html/index.html``. Sphinx_ +1.0 or newer is required to generate the documentation. + + +.. _Jansson: http://www.digip.org/jansson/ +.. _`MIT license`: http://www.opensource.org/licenses/mit-license.php +.. _Sphinx: http://sphinx.pocoo.org/ diff --git a/compat/jansson-2.6/configure.ac b/compat/jansson-2.6/configure.ac new file mode 100644 index 0000000000..1977aa62ea --- /dev/null +++ b/compat/jansson-2.6/configure.ac @@ -0,0 +1,101 @@ +AC_PREREQ([2.60]) +AC_INIT([jansson], [2.6], [petri@digip.org]) + +AC_CONFIG_MACRO_DIR([m4]) + +AM_INIT_AUTOMAKE([1.10 foreign]) +m4_ifdef([AM_SILENT_RULES], [AM_SILENT_RULES([yes])]) + +AC_CONFIG_SRCDIR([src/value.c]) +AC_CONFIG_HEADERS([config.h]) + +# Checks for programs. +AC_PROG_CC +AC_PROG_LIBTOOL +AM_CONDITIONAL([GCC], [test x$GCC = xyes]) + +# Checks for libraries. + +# Checks for header files. +AC_CHECK_HEADERS([endian.h fcntl.h locale.h sched.h unistd.h sys/param.h sys/stat.h sys/time.h sys/types.h]) + +# Checks for typedefs, structures, and compiler characteristics. +AC_TYPE_INT32_T +AC_TYPE_UINT32_T +AC_TYPE_LONG_LONG_INT + +AC_C_INLINE +case $ac_cv_c_inline in + yes) json_inline=inline;; + no) json_inline=;; + *) json_inline=$ac_cv_c_inline;; +esac +AC_SUBST([json_inline]) + +# Checks for library functions. +AC_CHECK_FUNCS([close getpid gettimeofday localeconv open read sched_yield strtoll]) + +AC_MSG_CHECKING([for gcc __sync builtins]) +have_sync_builtins=no +AC_TRY_LINK( + [], [unsigned long val; __sync_bool_compare_and_swap(&val, 0, 1);], + [have_sync_builtins=yes], +) +if test "x$have_sync_builtins" = "xyes"; then + AC_DEFINE([HAVE_SYNC_BUILTINS], [1], + [Define to 1 if gcc's __sync builtins are available]) +fi +AC_MSG_RESULT([$have_sync_builtins]) + +AC_MSG_CHECKING([for gcc __atomic builtins]) +have_atomic_builtins=no +AC_TRY_LINK( + [], [char l; unsigned long v; __atomic_test_and_set(&l, __ATOMIC_RELAXED); __atomic_store_n(&v, 1, __ATOMIC_ACQ_REL); __atomic_load_n(&v, __ATOMIC_ACQUIRE);], + [have_atomic_builtins=yes], +) +if test "x$have_atomic_builtins" = "xyes"; then + AC_DEFINE([HAVE_ATOMIC_BUILTINS], [1], + [Define to 1 if gcc's __atomic builtins are available]) +fi +AC_MSG_RESULT([$have_atomic_builtins]) + +case "$ac_cv_type_long_long_int$ac_cv_func_strtoll" in + yesyes) json_have_long_long=1;; + *) json_have_long_long=0;; +esac +AC_SUBST([json_have_long_long]) + +case "$ac_cv_header_locale_h$ac_cv_func_localeconv" in + yesyes) json_have_localeconv=1;; + *) json_have_localeconv=0;; +esac +AC_SUBST([json_have_localeconv]) + +# Features +AC_ARG_ENABLE([urandom], + [AS_HELP_STRING([--disable-urandom], + [Don't use /dev/urandom to seed the hash function])], + [use_urandom=$enableval], [use_urandom=yes]) + +if test "x$use_urandom" = xyes; then +AC_DEFINE([USE_URANDOM], [1], + [Define to 1 if /dev/urandom should be used for seeding the hash function]) +fi + +AC_ARG_ENABLE([windows-cryptoapi], + [AS_HELP_STRING([--disable-windows-cryptoapi], + [Don't use CryptGenRandom to seed the hash function])], + [use_windows_cryptoapi=$enableval], [use_windows_cryptoapi=yes]) + +if test "x$use_windows_cryptoapi" = xyes; then +AC_DEFINE([USE_WINDOWS_CRYPTOAPI], [1], + [Define to 1 if CryptGenRandom should be used for seeding the hash function]) +fi + +AC_CONFIG_FILES([ + jansson.pc + Makefile + src/Makefile + src/jansson_config.h +]) +AC_OUTPUT diff --git a/compat/jansson-2.6/jansson.pc.in b/compat/jansson-2.6/jansson.pc.in new file mode 100644 index 0000000000..d9bf4dade6 --- /dev/null +++ b/compat/jansson-2.6/jansson.pc.in @@ -0,0 +1,10 @@ +prefix=@prefix@ +exec_prefix=@exec_prefix@ +libdir=@libdir@ +includedir=${prefix}/include + +Name: Jansson +Description: Library for encoding, decoding and manipulating JSON data +Version: @VERSION@ +Libs: -L${libdir} -ljansson +Cflags: -I${includedir} diff --git a/compat/jansson-2.6/m4/.gitignore b/compat/jansson-2.6/m4/.gitignore new file mode 100644 index 0000000000..e69de29bb2 diff --git a/compat/jansson-2.6/src/Makefile.am b/compat/jansson-2.6/src/Makefile.am new file mode 100644 index 0000000000..e1a549385a --- /dev/null +++ b/compat/jansson-2.6/src/Makefile.am @@ -0,0 +1,24 @@ +EXTRA_DIST = jansson.def + +include_HEADERS = jansson.h jansson_config.h + +lib_LTLIBRARIES = libjansson.la +libjansson_la_SOURCES = \ + dump.c \ + error.c \ + hashtable.c \ + hashtable.h \ + jansson_private.h \ + load.c \ + memory.c \ + pack_unpack.c \ + strbuffer.c \ + strbuffer.h \ + strconv.c \ + utf.c \ + utf.h \ + value.c +libjansson_la_LDFLAGS = \ + -no-undefined \ + -export-symbols-regex '^json_' \ + -version-info 9:0:5 diff --git a/compat/jansson/dump.c b/compat/jansson-2.6/src/dump.c similarity index 98% rename from compat/jansson/dump.c rename to compat/jansson-2.6/src/dump.c index 2c7dee9e51..3b19c73be1 100644 --- a/compat/jansson/dump.c +++ b/compat/jansson-2.6/src/dump.c @@ -1,11 +1,14 @@ /* - * Copyright (c) 2009-2012 Petri Lehtinen + * Copyright (c) 2009-2013 Petri Lehtinen * * Jansson is free software; you can redistribute it and/or modify * it under the terms of the MIT license. See LICENSE for details. */ +#ifndef _GNU_SOURCE #define _GNU_SOURCE +#endif + #include #include #include @@ -38,7 +41,7 @@ static int dump_to_file(const char *buffer, size_t size, void *data) } /* 32 spaces (the maximum indentation size) */ -static char whitespace[] = " "; +static const char whitespace[] = " "; static int dump_indent(size_t flags, int depth, int space, json_dump_callback_t dump, void *data) { @@ -171,6 +174,9 @@ static int object_key_compare_serials(const void *key1, const void *key2) static int do_dump(const json_t *json, size_t flags, int depth, json_dump_callback_t dump, void *data) { + if(!json) + return -1; + switch(json_typeof(json)) { case JSON_NULL: return dump("null", 4, data); diff --git a/compat/jansson/error.c b/compat/jansson-2.6/src/error.c similarity index 100% rename from compat/jansson/error.c rename to compat/jansson-2.6/src/error.c diff --git a/compat/jansson/hashtable.c b/compat/jansson-2.6/src/hashtable.c similarity index 98% rename from compat/jansson/hashtable.c rename to compat/jansson-2.6/src/hashtable.c index 76cf69bfe6..5fb04679a1 100644 --- a/compat/jansson/hashtable.c +++ b/compat/jansson-2.6/src/hashtable.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2009-2012 Petri Lehtinen + * Copyright (c) 2009-2013 Petri Lehtinen * * This library is free software; you can redistribute it and/or modify * it under the terms of the MIT license. See LICENSE for details. @@ -74,7 +74,7 @@ static void insert_to_bucket(hashtable_t *hashtable, bucket_t *bucket, } } -static size_t primes[] = { +static const size_t primes[] = { 5, 13, 23, 53, 97, 193, 389, 769, 1543, 3079, 6151, 12289, 24593, 49157, 98317, 196613, 393241, 786433, 1572869, 3145739, 6291469, 12582917, 25165843, 50331653, 100663319, 201326611, 402653189, diff --git a/compat/jansson/hashtable.h b/compat/jansson-2.6/src/hashtable.h similarity index 98% rename from compat/jansson/hashtable.h rename to compat/jansson-2.6/src/hashtable.h index de1df2603d..4a7ce6f672 100644 --- a/compat/jansson/hashtable.h +++ b/compat/jansson-2.6/src/hashtable.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2009-2012 Petri Lehtinen + * Copyright (c) 2009-2013 Petri Lehtinen * * This library is free software; you can redistribute it and/or modify * it under the terms of the MIT license. See LICENSE for details. diff --git a/compat/jansson/jansson.def b/compat/jansson-2.6/src/jansson.def similarity index 98% rename from compat/jansson/jansson.def rename to compat/jansson-2.6/src/jansson.def index 6b2c8a728e..8cc2e9cb5f 100644 --- a/compat/jansson/jansson.def +++ b/compat/jansson-2.6/src/jansson.def @@ -1,5 +1,3 @@ -LIBRARY "jansson" - EXPORTS json_delete json_true diff --git a/compat/jansson/jansson.h b/compat/jansson-2.6/src/jansson.h similarity index 90% rename from compat/jansson/jansson.h rename to compat/jansson-2.6/src/jansson.h index 352c6ce525..52c8077d2e 100644 --- a/compat/jansson/jansson.h +++ b/compat/jansson-2.6/src/jansson.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2009-2012 Petri Lehtinen + * Copyright (c) 2009-2013 Petri Lehtinen * * Jansson is free software; you can redistribute it and/or modify * it under the terms of the MIT license. See LICENSE for details. @@ -21,11 +21,11 @@ extern "C" { /* version */ #define JANSSON_MAJOR_VERSION 2 -#define JANSSON_MINOR_VERSION 4 +#define JANSSON_MINOR_VERSION 5 #define JANSSON_MICRO_VERSION 0 /* Micro version is omitted if it's 0 */ -#define JANSSON_VERSION "2.4" +#define JANSSON_VERSION "2.5" /* Version as a 3-byte hex number, e.g. 0x010201 == 1.2.1. Use this for numeric comparisons, e.g. #if JANSSON_VERSION_HEX >= ... */ @@ -47,11 +47,12 @@ typedef enum { JSON_NULL } json_type; -typedef struct { +typedef struct json_t { json_type type; size_t refcount; } json_t; +#ifndef JANSSON_USING_CMAKE /* disabled if using cmake */ #if JSON_INTEGER_IS_LONG_LONG #ifdef _WIN32 #define JSON_INTEGER_FORMAT "I64d" @@ -63,6 +64,7 @@ typedef long long json_int_t; #define JSON_INTEGER_FORMAT "ld" typedef long json_int_t; #endif /* JSON_INTEGER_IS_LONG_LONG */ +#endif #define json_typeof(json) ((json)->type) #define json_is_object(json) (json && json_typeof(json) == JSON_OBJECT) @@ -146,6 +148,11 @@ int json_object_iter_set_new(json_t *object, void *iter, json_t *value); key && (value = json_object_iter_value(json_object_key_to_iter(key))); \ key = json_object_iter_key(json_object_iter_next(object, json_object_key_to_iter(key)))) +#define json_array_foreach(array, index, value) \ + for(index = 0; \ + index < json_array_size(array) && (value = json_array_get(array, index)); \ + index++) + static JSON_INLINE int json_object_set(json_t *object, const char *key, json_t *value) { @@ -174,9 +181,9 @@ int json_array_clear(json_t *array); int json_array_extend(json_t *array, json_t *other); static JSON_INLINE -int json_array_set(json_t *array, size_t index, json_t *value) +int json_array_set(json_t *array, size_t ind, json_t *value) { - return json_array_set_new(array, index, json_incref(value)); + return json_array_set_new(array, ind, json_incref(value)); } static JSON_INLINE @@ -186,9 +193,9 @@ int json_array_append(json_t *array, json_t *value) } static JSON_INLINE -int json_array_insert(json_t *array, size_t index, json_t *value) +int json_array_insert(json_t *array, size_t ind, json_t *value) { - return json_array_insert_new(array, index, json_incref(value)); + return json_array_insert_new(array, ind, json_incref(value)); } const char *json_string_value(const json_t *string); @@ -224,14 +231,15 @@ int json_equal(json_t *value1, json_t *value2); /* copying */ json_t *json_copy(json_t *value); -json_t *json_deep_copy(json_t *value); +json_t *json_deep_copy(const json_t *value); /* decoding */ -#define JSON_REJECT_DUPLICATES 0x1 -#define JSON_DISABLE_EOF_CHECK 0x2 -#define JSON_DECODE_ANY 0x4 +#define JSON_REJECT_DUPLICATES 0x1 +#define JSON_DISABLE_EOF_CHECK 0x2 +#define JSON_DECODE_ANY 0x4 +#define JSON_DECODE_INT_AS_REAL 0x8 typedef size_t (*json_load_callback_t)(void *buffer, size_t buflen, void *data); diff --git a/compat/jansson/jansson_config.h.in b/compat/jansson-2.6/src/jansson_config.h.in similarity index 95% rename from compat/jansson/jansson_config.h.in rename to compat/jansson-2.6/src/jansson_config.h.in index a566e8b603..785801f27c 100644 --- a/compat/jansson/jansson_config.h.in +++ b/compat/jansson-2.6/src/jansson_config.h.in @@ -1,5 +1,5 @@ /* - * Copyright (c) 2010-2012 Petri Lehtinen + * Copyright (c) 2010-2013 Petri Lehtinen * * Jansson is free software; you can redistribute it and/or modify * it under the terms of the MIT license. See LICENSE for details. diff --git a/compat/jansson/jansson_private.h b/compat/jansson-2.6/src/jansson_private.h similarity index 95% rename from compat/jansson/jansson_private.h rename to compat/jansson-2.6/src/jansson_private.h index 7d6d09d05e..403b53a4d6 100644 --- a/compat/jansson/jansson_private.h +++ b/compat/jansson-2.6/src/jansson_private.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2009-2012 Petri Lehtinen + * Copyright (c) 2009-2013 Petri Lehtinen * * Jansson is free software; you can redistribute it and/or modify * it under the terms of the MIT license. See LICENSE for details. @@ -81,6 +81,7 @@ int jsonp_dtostr(char *buffer, size_t size, double value); /* Wrappers for custom memory functions */ void* jsonp_malloc(size_t size); void jsonp_free(void *ptr); +char *jsonp_strndup(const char *str, size_t length); char *jsonp_strdup(const char *str); /* Windows compatibility */ diff --git a/compat/jansson/load.c b/compat/jansson-2.6/src/load.c similarity index 96% rename from compat/jansson/load.c rename to compat/jansson-2.6/src/load.c index d88a70402b..c5536f586c 100644 --- a/compat/jansson/load.c +++ b/compat/jansson-2.6/src/load.c @@ -1,11 +1,14 @@ /* - * Copyright (c) 2009-2012 Petri Lehtinen + * Copyright (c) 2009-2013 Petri Lehtinen * * Jansson is free software; you can redistribute it and/or modify * it under the terms of the MIT license. See LICENSE for details. */ +#ifndef _GNU_SOURCE #define _GNU_SOURCE +#endif + #include #include #include @@ -37,7 +40,7 @@ #define l_isalpha(c) (l_isupper(c) || l_islower(c)) #define l_isdigit(c) ('0' <= (c) && (c) <= '9') #define l_isxdigit(c) \ - (l_isdigit(c) || 'A' <= (c) || (c) <= 'F' || 'a' <= (c) || (c) <= 'f') + (l_isdigit(c) || ('A' <= (c) && (c) <= 'F') || ('a' <= (c) && (c) <= 'f')) /* Read one byte from stream, convert to unsigned char, then int, and return. return EOF on end of file. This corresponds to the @@ -250,9 +253,18 @@ static void lex_unget(lex_t *lex, int c) static void lex_unget_unsave(lex_t *lex, int c) { if(c != STREAM_STATE_EOF && c != STREAM_STATE_ERROR) { + /* Since we treat warnings as errors, when assertions are turned + * off the "d" variable would be set but never used. Which is + * treated as an error by GCC. + */ + #ifndef NDEBUG char d; + #endif stream_unget(&lex->stream, c); - d = strbuffer_pop(&lex->saved_text); + #ifndef NDEBUG + d = + #endif + strbuffer_pop(&lex->saved_text); assert(c == d); } } @@ -446,8 +458,9 @@ static void lex_scan_string(lex_t *lex, json_error_t *error) jsonp_free(lex->value.string); } +#ifndef JANSSON_USING_CMAKE /* disabled if using cmake */ #if JSON_INTEGER_IS_LONG_LONG -#ifdef _MSC_VER // Microsoft Visual Studio +#ifdef _MSC_VER /* Microsoft Visual Studio */ #define json_strtoint _strtoi64 #else #define json_strtoint strtoll @@ -455,6 +468,7 @@ static void lex_scan_string(lex_t *lex, json_error_t *error) #else #define json_strtoint strtol #endif +#endif static int lex_scan_number(lex_t *lex, int c, json_error_t *error) { @@ -770,6 +784,7 @@ static json_t *parse_array(lex_t *lex, size_t flags, json_error_t *error) static json_t *parse_value(lex_t *lex, size_t flags, json_error_t *error) { json_t *json; + double value; switch(lex->token) { case TOKEN_STRING: { @@ -778,7 +793,15 @@ static json_t *parse_value(lex_t *lex, size_t flags, json_error_t *error) } case TOKEN_INTEGER: { - json = json_integer(lex->value.integer); + if (flags & JSON_DECODE_INT_AS_REAL) { + if(jsonp_strtod(&lex->saved_text, &value)) { + error_set(error, lex, "real number overflow"); + return NULL; + } + json = json_real(value); + } else { + json = json_integer(lex->value.integer); + } break; } diff --git a/compat/jansson/memory.c b/compat/jansson-2.6/src/memory.c similarity index 79% rename from compat/jansson/memory.c rename to compat/jansson-2.6/src/memory.c index 543ecc4de0..eb6cec542e 100644 --- a/compat/jansson/memory.c +++ b/compat/jansson-2.6/src/memory.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2009-2012 Petri Lehtinen + * Copyright (c) 2009-2013 Petri Lehtinen * Copyright (c) 2011-2012 Basile Starynkevitch * * Jansson is free software; you can redistribute it and/or modify it @@ -35,12 +35,17 @@ void jsonp_free(void *ptr) char *jsonp_strdup(const char *str) { char *new_str; + size_t len; - new_str = jsonp_malloc(strlen(str) + 1); + len = strlen(str); + if(len == (size_t)-1) + return NULL; + + new_str = jsonp_malloc(len + 1); if(!new_str) return NULL; - strcpy(new_str, str); + memcpy(new_str, str, len + 1); return new_str; } diff --git a/compat/jansson/pack_unpack.c b/compat/jansson-2.6/src/pack_unpack.c similarity index 76% rename from compat/jansson/pack_unpack.c rename to compat/jansson-2.6/src/pack_unpack.c index 39db9b8d22..0d932f791d 100644 --- a/compat/jansson/pack_unpack.c +++ b/compat/jansson-2.6/src/pack_unpack.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2009-2012 Petri Lehtinen + * Copyright (c) 2009-2013 Petri Lehtinen * Copyright (c) 2011-2012 Graeme Smecher * * Jansson is free software; you can redistribute it and/or modify @@ -11,17 +11,29 @@ #include "jansson_private.h" #include "utf.h" +typedef struct { + int line; + int column; + size_t pos; + char token; +} token_t; + typedef struct { const char *start; const char *fmt; - char token; + token_t prev_token; + token_t token; + token_t next_token; json_error_t *error; size_t flags; int line; int column; + size_t pos; } scanner_t; -static const char *type_names[] = { +#define token(scanner) ((scanner)->token.token) + +static const char * const type_names[] = { "object", "array", "string", @@ -34,7 +46,7 @@ static const char *type_names[] = { #define type_name(x) type_names[json_typeof(x)] -static const char *unpack_value_starters = "{[siIbfFOon"; +static const char unpack_value_starters[] = "{[siIbfFOon"; static void scanner_init(scanner_t *s, json_error_t *error, @@ -43,14 +55,28 @@ static void scanner_init(scanner_t *s, json_error_t *error, s->error = error; s->flags = flags; s->fmt = s->start = fmt; + memset(&s->prev_token, 0, sizeof(token_t)); + memset(&s->token, 0, sizeof(token_t)); + memset(&s->next_token, 0, sizeof(token_t)); s->line = 1; s->column = 0; + s->pos = 0; } static void next_token(scanner_t *s) { - const char *t = s->fmt; + const char *t; + s->prev_token = s->token; + + if(s->next_token.line) { + s->token = s->next_token; + s->next_token.line = 0; + return; + } + + t = s->fmt; s->column++; + s->pos++; /* skip space and ignored chars */ while(*t == ' ' || *t == '\t' || *t == '\n' || *t == ',' || *t == ':') { @@ -61,23 +87,32 @@ static void next_token(scanner_t *s) else s->column++; + s->pos++; t++; } - s->token = *t; + s->token.token = *t; + s->token.line = s->line; + s->token.column = s->column; + s->token.pos = s->pos; t++; s->fmt = t; } +static void prev_token(scanner_t *s) +{ + s->next_token = s->token; + s->token = s->prev_token; +} + static void set_error(scanner_t *s, const char *source, const char *fmt, ...) { va_list ap; - size_t pos; va_start(ap, fmt); - pos = (size_t)(s->fmt - s->start); - jsonp_error_vset(s->error, s->line, s->column, pos, fmt, ap); + jsonp_error_vset(s->error, s->token.line, s->token.column, s->token.pos, + fmt, ap); jsonp_error_set_source(s->error, source); @@ -86,35 +121,107 @@ static void set_error(scanner_t *s, const char *source, const char *fmt, ...) static json_t *pack(scanner_t *s, va_list *ap); + +/* ours will be set to 1 if jsonp_free() must be called for the result + afterwards */ +static char *read_string(scanner_t *s, va_list *ap, + const char *purpose, int *ours) +{ + char t; + strbuffer_t strbuff; + const char *str; + size_t length; + char *result; + + next_token(s); + t = token(s); + prev_token(s); + + if(t != '#' && t != '+') { + /* Optimize the simple case */ + str = va_arg(*ap, const char *); + + if(!str) { + set_error(s, "", "NULL string argument"); + return NULL; + } + + if(!utf8_check_string(str, -1)) { + set_error(s, "", "Invalid UTF-8 %s", purpose); + return NULL; + } + + *ours = 0; + return (char *)str; + } + + strbuffer_init(&strbuff); + + while(1) { + str = va_arg(*ap, const char *); + if(!str) { + set_error(s, "", "NULL string argument"); + strbuffer_close(&strbuff); + return NULL; + } + + next_token(s); + + if(token(s) == '#') { + length = va_arg(*ap, int); + } + else { + prev_token(s); + length = strlen(str); + } + + if(strbuffer_append_bytes(&strbuff, str, length) == -1) { + set_error(s, "", "Out of memory"); + strbuffer_close(&strbuff); + return NULL; + } + + next_token(s); + if(token(s) != '+') { + prev_token(s); + break; + } + } + + result = strbuffer_steal_value(&strbuff); + + if(!utf8_check_string(result, -1)) { + set_error(s, "", "Invalid UTF-8 %s", purpose); + return NULL; + } + + *ours = 1; + return result; +} + static json_t *pack_object(scanner_t *s, va_list *ap) { json_t *object = json_object(); next_token(s); - while(s->token != '}') { - const char *key; + while(token(s) != '}') { + char *key; + int ours; json_t *value; - if(!s->token) { + if(!token(s)) { set_error(s, "", "Unexpected end of format string"); goto error; } - if(s->token != 's') { - set_error(s, "", "Expected format 's', got '%c'", s->token); + if(token(s) != 's') { + set_error(s, "", "Expected format 's', got '%c'", token(s)); goto error; } - key = va_arg(*ap, const char *); - if(!key) { - set_error(s, "", "NULL object key"); + key = read_string(s, ap, "object key", &ours); + if(!key) goto error; - } - - if(!utf8_check_string(key, -1)) { - set_error(s, "", "Invalid UTF-8 in object key"); - goto error; - } next_token(s); @@ -123,10 +230,16 @@ static json_t *pack_object(scanner_t *s, va_list *ap) goto error; if(json_object_set_new_nocheck(object, key, value)) { + if(ours) + jsonp_free(key); + set_error(s, "", "Unable to add key \"%s\"", key); goto error; } + if(ours) + jsonp_free(key); + next_token(s); } @@ -142,10 +255,10 @@ static json_t *pack_array(scanner_t *s, va_list *ap) json_t *array = json_array(); next_token(s); - while(s->token != ']') { + while(token(s) != ']') { json_t *value; - if(!s->token) { + if(!token(s)) { set_error(s, "", "Unexpected end of format string"); goto error; } @@ -170,25 +283,27 @@ static json_t *pack_array(scanner_t *s, va_list *ap) static json_t *pack(scanner_t *s, va_list *ap) { - switch(s->token) { + switch(token(s)) { case '{': return pack_object(s, ap); case '[': return pack_array(s, ap); - case 's': /* string */ - { - const char *str = va_arg(*ap, const char *); - if(!str) { - set_error(s, "", "NULL string argument"); - return NULL; - } - if(!utf8_check_string(str, -1)) { - set_error(s, "", "Invalid UTF-8 string"); + case 's': { /* string */ + char *str; + int ours; + json_t *result; + + str = read_string(s, ap, "string", &ours); + if(!str) return NULL; - } - return json_string_nocheck(str); + + result = json_string_nocheck(str); + if(ours) + jsonp_free(str); + + return result; } case 'n': /* null */ @@ -214,7 +329,7 @@ static json_t *pack(scanner_t *s, va_list *ap) default: set_error(s, "", "Unexpected format character '%c'", - s->token); + token(s)); return NULL; } } @@ -245,30 +360,30 @@ static int unpack_object(scanner_t *s, json_t *root, va_list *ap) } next_token(s); - while(s->token != '}') { + while(token(s) != '}') { const char *key; json_t *value; int opt = 0; if(strict != 0) { set_error(s, "", "Expected '}' after '%c', got '%c'", - (strict == 1 ? '!' : '*'), s->token); + (strict == 1 ? '!' : '*'), token(s)); goto out; } - if(!s->token) { + if(!token(s)) { set_error(s, "", "Unexpected end of format string"); goto out; } - if(s->token == '!' || s->token == '*') { - strict = (s->token == '!' ? 1 : -1); + if(token(s) == '!' || token(s) == '*') { + strict = (token(s) == '!' ? 1 : -1); next_token(s); continue; } - if(s->token != 's') { - set_error(s, "", "Expected format 's', got '%c'", s->token); + if(token(s) != 's') { + set_error(s, "", "Expected format 's', got '%c'", token(s)); goto out; } @@ -280,7 +395,7 @@ static int unpack_object(scanner_t *s, json_t *root, va_list *ap) next_token(s); - if(s->token == '?') { + if(token(s) == '?') { opt = 1; next_token(s); } @@ -331,30 +446,30 @@ static int unpack_array(scanner_t *s, json_t *root, va_list *ap) } next_token(s); - while(s->token != ']') { + while(token(s) != ']') { json_t *value; if(strict != 0) { set_error(s, "", "Expected ']' after '%c', got '%c'", (strict == 1 ? '!' : '*'), - s->token); + token(s)); return -1; } - if(!s->token) { + if(!token(s)) { set_error(s, "", "Unexpected end of format string"); return -1; } - if(s->token == '!' || s->token == '*') { - strict = (s->token == '!' ? 1 : -1); + if(token(s) == '!' || token(s) == '*') { + strict = (token(s) == '!' ? 1 : -1); next_token(s); continue; } - if(!strchr(unpack_value_starters, s->token)) { + if(!strchr(unpack_value_starters, token(s))) { set_error(s, "", "Unexpected format character '%c'", - s->token); + token(s)); return -1; } @@ -392,7 +507,7 @@ static int unpack_array(scanner_t *s, json_t *root, va_list *ap) static int unpack(scanner_t *s, json_t *root, va_list *ap) { - switch(s->token) + switch(token(s)) { case '{': return unpack_object(s, root, ap); @@ -521,7 +636,7 @@ static int unpack(scanner_t *s, json_t *root, va_list *ap) default: set_error(s, "", "Unexpected format character '%c'", - s->token); + token(s)); return -1; } } @@ -551,7 +666,7 @@ json_t *json_vpack_ex(json_error_t *error, size_t flags, return NULL; next_token(&s); - if(s.token) { + if(token(&s)) { json_decref(value); set_error(&s, "", "Garbage after format string"); return NULL; @@ -614,7 +729,7 @@ int json_vunpack_ex(json_t *root, json_error_t *error, size_t flags, va_end(ap_copy); next_token(&s); - if(s.token) { + if(token(&s)) { set_error(&s, "", "Garbage after format string"); return -1; } diff --git a/compat/jansson/strbuffer.c b/compat/jansson-2.6/src/strbuffer.c similarity index 93% rename from compat/jansson/strbuffer.c rename to compat/jansson-2.6/src/strbuffer.c index 6d4edd65e3..2d6ff31050 100644 --- a/compat/jansson/strbuffer.c +++ b/compat/jansson-2.6/src/strbuffer.c @@ -1,11 +1,14 @@ /* - * Copyright (c) 2009-2012 Petri Lehtinen + * Copyright (c) 2009-2013 Petri Lehtinen * * Jansson is free software; you can redistribute it and/or modify * it under the terms of the MIT license. See LICENSE for details. */ +#ifndef _GNU_SOURCE #define _GNU_SOURCE +#endif + #include #include #include "jansson_private.h" @@ -31,7 +34,9 @@ int strbuffer_init(strbuffer_t *strbuff) void strbuffer_close(strbuffer_t *strbuff) { - jsonp_free(strbuff->value); + if(strbuff->value) + jsonp_free(strbuff->value); + strbuff->size = 0; strbuff->length = 0; strbuff->value = NULL; @@ -51,7 +56,7 @@ const char *strbuffer_value(const strbuffer_t *strbuff) char *strbuffer_steal_value(strbuffer_t *strbuff) { char *result = strbuff->value; - strbuffer_init(strbuff); + strbuff->value = NULL; return result; } diff --git a/compat/jansson/strbuffer.h b/compat/jansson-2.6/src/strbuffer.h similarity index 88% rename from compat/jansson/strbuffer.h rename to compat/jansson-2.6/src/strbuffer.h index 23f8ffff68..06fd065bb1 100644 --- a/compat/jansson/strbuffer.h +++ b/compat/jansson-2.6/src/strbuffer.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2009-2012 Petri Lehtinen + * Copyright (c) 2009-2013 Petri Lehtinen * * Jansson is free software; you can redistribute it and/or modify * it under the terms of the MIT license. See LICENSE for details. @@ -20,6 +20,8 @@ void strbuffer_close(strbuffer_t *strbuff); void strbuffer_clear(strbuffer_t *strbuff); const char *strbuffer_value(const strbuffer_t *strbuff); + +/* Steal the value and close the strbuffer */ char *strbuffer_steal_value(strbuffer_t *strbuff); int strbuffer_append(strbuffer_t *strbuff, const char *string); diff --git a/compat/jansson/strconv.c b/compat/jansson-2.6/src/strconv.c similarity index 96% rename from compat/jansson/strconv.c rename to compat/jansson-2.6/src/strconv.c index caa9ab8b46..3e2cb7c4ce 100644 --- a/compat/jansson/strconv.c +++ b/compat/jansson-2.6/src/strconv.c @@ -5,6 +5,11 @@ #include "jansson_private.h" #include "strbuffer.h" +/* need config.h to get the correct snprintf */ +#ifdef HAVE_CONFIG_H +#include +#endif + #if JSON_HAVE_LOCALECONV #include diff --git a/compat/jansson/utf.c b/compat/jansson-2.6/src/utf.c similarity index 98% rename from compat/jansson/utf.c rename to compat/jansson-2.6/src/utf.c index 0359ee2b91..65b849b89e 100644 --- a/compat/jansson/utf.c +++ b/compat/jansson-2.6/src/utf.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2009-2012 Petri Lehtinen + * Copyright (c) 2009-2013 Petri Lehtinen * * Jansson is free software; you can redistribute it and/or modify * it under the terms of the MIT license. See LICENSE for details. diff --git a/compat/jansson/utf.h b/compat/jansson-2.6/src/utf.h similarity index 94% rename from compat/jansson/utf.h rename to compat/jansson-2.6/src/utf.h index 2495cdd1f8..cb10c24c16 100644 --- a/compat/jansson/utf.h +++ b/compat/jansson-2.6/src/utf.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2009-2012 Petri Lehtinen + * Copyright (c) 2009-2013 Petri Lehtinen * * Jansson is free software; you can redistribute it and/or modify * it under the terms of the MIT license. See LICENSE for details. diff --git a/compat/jansson/value.c b/compat/jansson-2.6/src/value.c similarity index 94% rename from compat/jansson/value.c rename to compat/jansson-2.6/src/value.c index ba9908edd0..582849be31 100644 --- a/compat/jansson/value.c +++ b/compat/jansson-2.6/src/value.c @@ -1,11 +1,13 @@ /* - * Copyright (c) 2009-2012 Petri Lehtinen + * Copyright (c) 2009-2013 Petri Lehtinen * * Jansson is free software; you can redistribute it and/or modify * it under the terms of the MIT license. See LICENSE for details. */ +#ifndef _GNU_SOURCE #define _GNU_SOURCE +#endif #include #include @@ -290,19 +292,27 @@ static json_t *json_object_copy(json_t *object) return result; } -static json_t *json_object_deep_copy(json_t *object) +static json_t *json_object_deep_copy(const json_t *object) { json_t *result; - - const char *key; - json_t *value; + void *iter; result = json_object(); if(!result) return NULL; - json_object_foreach(object, key, value) + /* Cannot use json_object_foreach because object has to be cast + non-const */ + iter = json_object_iter((json_t *)object); + while(iter) { + const char *key; + const json_t *value; + key = json_object_iter_key(iter); + value = json_object_iter_value(iter); + json_object_set_new_nocheck(result, key, json_deep_copy(value)); + iter = json_object_iter_next((json_t *)object, iter); + } return result; } @@ -509,7 +519,10 @@ int json_array_remove(json_t *json, size_t index) json_decref(array->table[index]); - array_move(array, index, index + 1, array->entries - index); + /* If we're removing the last element, nothing has to be moved */ + if(index < array->entries - 1) + array_move(array, index, index + 1, array->entries - index - 1); + array->entries--; return 0; @@ -590,7 +603,7 @@ static json_t *json_array_copy(json_t *array) return result; } -static json_t *json_array_deep_copy(json_t *array) +static json_t *json_array_deep_copy(const json_t *array) { json_t *result; size_t i; @@ -682,7 +695,7 @@ static int json_string_equal(json_t *string1, json_t *string2) return strcmp(json_string_value(string1), json_string_value(string2)) == 0; } -static json_t *json_string_copy(json_t *string) +static json_t *json_string_copy(const json_t *string) { return json_string_nocheck(json_string_value(string)); } @@ -729,7 +742,7 @@ static int json_integer_equal(json_t *integer1, json_t *integer2) return json_integer_value(integer1) == json_integer_value(integer2); } -static json_t *json_integer_copy(json_t *integer) +static json_t *json_integer_copy(const json_t *integer) { return json_integer(json_integer_value(integer)); } @@ -781,7 +794,7 @@ static int json_real_equal(json_t *real1, json_t *real2) return json_real_value(real1) == json_real_value(real2); } -static json_t *json_real_copy(json_t *real) +static json_t *json_real_copy(const json_t *real) { return json_real(json_real_value(real)); } @@ -907,7 +920,7 @@ json_t *json_copy(json_t *json) return NULL; } -json_t *json_deep_copy(json_t *json) +json_t *json_deep_copy(const json_t *json) { if(!json) return NULL; @@ -931,7 +944,7 @@ json_t *json_deep_copy(json_t *json) return json_real_copy(json); if(json_is_true(json) || json_is_false(json) || json_is_null(json)) - return json; + return (json_t *)json; return NULL; } diff --git a/compat/jansson/.gitignore b/compat/jansson/.gitignore deleted file mode 100644 index 173737b67f..0000000000 --- a/compat/jansson/.gitignore +++ /dev/null @@ -1,3 +0,0 @@ - -libjansson.a - diff --git a/compat/jansson/Makefile.am b/compat/jansson/Makefile.am deleted file mode 100644 index f650fab9cf..0000000000 --- a/compat/jansson/Makefile.am +++ /dev/null @@ -1,21 +0,0 @@ -EXTRA_DIST = jansson.def - -include_HEADERS = jansson.h jansson_config.h - -noinst_LIBRARIES = libjansson.a - -libjansson_a_SOURCES = \ - dump.c \ - error.c \ - hashtable.c \ - hashtable.h \ - jansson_private.h \ - load.c \ - memory.c \ - pack_unpack.c \ - strbuffer.c \ - strbuffer.h \ - strconv.c \ - utf.c \ - utf.h \ - value.c diff --git a/compat/jansson/jansson_config.h b/compat/jansson/jansson_config.h deleted file mode 100644 index e576cd1a0b..0000000000 --- a/compat/jansson/jansson_config.h +++ /dev/null @@ -1,39 +0,0 @@ -/* - * Copyright (c) 2010-2012 Petri Lehtinen - * - * Jansson is free software; you can redistribute it and/or modify - * it under the terms of the MIT license. See LICENSE for details. - * - * - * This file specifies a part of the site-specific configuration for - * Jansson, namely those things that affect the public API in - * jansson.h. - * - * The configure script copies this file to jansson_config.h and - * replaces @var@ substitutions by values that fit your system. If you - * cannot run the configure script, you can do the value substitution - * by hand. - */ - -#ifndef JANSSON_CONFIG_H -#define JANSSON_CONFIG_H - -/* If your compiler supports the inline keyword in C, JSON_INLINE is - defined to `inline', otherwise empty. In C++, the inline is always - supported. */ -#ifdef __cplusplus -#define JSON_INLINE inline -#else -#define JSON_INLINE inline -#endif - -/* If your compiler supports the `long long` type and the strtoll() - library function, JSON_INTEGER_IS_LONG_LONG is defined to 1, - otherwise to 0. */ -#define JSON_INTEGER_IS_LONG_LONG 1 - -/* If locale.h and localeconv() are available, define to 1, - otherwise to 0. */ -#define JSON_HAVE_LOCALECONV 1 - -#endif diff --git a/compat/jansson/util.h b/compat/jansson/util.h deleted file mode 100644 index 06a547b8aa..0000000000 --- a/compat/jansson/util.h +++ /dev/null @@ -1,13 +0,0 @@ -/* - * Copyright (c) 2009, 2010 Petri Lehtinen - * - * Jansson is free software; you can redistribute it and/or modify - * it under the terms of the MIT license. See LICENSE for details. - */ - -#ifndef UTIL_H -#define UTIL_H - -#define max(a, b) ((a) > (b) ? (a) : (b)) - -#endif diff --git a/compat/libusb-1.0/configure.ac b/compat/libusb-1.0/configure.ac index c21c344b19..1fccea70ff 100644 --- a/compat/libusb-1.0/configure.ac +++ b/compat/libusb-1.0/configure.ac @@ -26,7 +26,7 @@ lt_revision="0" lt_age="0" LTLDFLAGS="-version-info ${lt_current}:${lt_revision}:${lt_age}" -AM_INIT_AUTOMAKE +AM_INIT_AUTOMAKE([foreign subdir-objects]) AM_MAINTAINER_MODE AC_CONFIG_SRCDIR([libusb/core.c]) diff --git a/compat/libusb-1.0/doc/Makefile.am b/compat/libusb-1.0/doc/Makefile.am deleted file mode 100644 index 931a7c0f29..0000000000 --- a/compat/libusb-1.0/doc/Makefile.am +++ /dev/null @@ -1,10 +0,0 @@ -EXTRA_DIST = doxygen.cfg.in - -docs: doxygen.cfg - doxygen $^ - -docs-upload: docs - ln -s html api-1.0 - rsync -av api-1.0/ web.sourceforge.net:htdocs/api-1.0/ - rm -f api-1.0 - diff --git a/compat/libusb-1.0/doc/doxygen.cfg.in b/compat/libusb-1.0/doc/doxygen.cfg.in deleted file mode 100644 index 00452204e5..0000000000 --- a/compat/libusb-1.0/doc/doxygen.cfg.in +++ /dev/null @@ -1,1294 +0,0 @@ -# Doxyfile 1.5.3 - -# This file describes the settings to be used by the documentation system -# doxygen (www.doxygen.org) for a project -# -# All text after a hash (#) is considered a comment and will be ignored -# The format is: -# TAG = value [value, ...] -# For lists items can also be appended using: -# TAG += value [value, ...] -# Values that contain spaces should be placed between quotes (" ") - -#--------------------------------------------------------------------------- -# Project related configuration options -#--------------------------------------------------------------------------- - -# This tag specifies the encoding used for all characters in the config file that -# follow. The default is UTF-8 which is also the encoding used for all text before -# the first occurrence of this tag. Doxygen uses libiconv (or the iconv built into -# libc) for the transcoding. See http://www.gnu.org/software/libiconv for the list of -# possible encodings. - -DOXYFILE_ENCODING = UTF-8 - -# The PROJECT_NAME tag is a single word (or a sequence of words surrounded -# by quotes) that should identify the project. - -PROJECT_NAME = libusb - -# The PROJECT_NUMBER tag can be used to enter a project or revision number. -# This could be handy for archiving the generated documentation or -# if some version control system is used. - -PROJECT_NUMBER = - -# The OUTPUT_DIRECTORY tag is used to specify the (relative or absolute) -# base path where the generated documentation will be put. -# If a relative path is entered, it will be relative to the location -# where doxygen was started. If left blank the current directory will be used. - -OUTPUT_DIRECTORY = - -# If the CREATE_SUBDIRS tag is set to YES, then doxygen will create -# 4096 sub-directories (in 2 levels) under the output directory of each output -# format and will distribute the generated files over these directories. -# Enabling this option can be useful when feeding doxygen a huge amount of -# source files, where putting all generated files in the same directory would -# otherwise cause performance problems for the file system. - -CREATE_SUBDIRS = NO - -# The OUTPUT_LANGUAGE tag is used to specify the language in which all -# documentation generated by doxygen is written. Doxygen will use this -# information to generate all constant output in the proper language. -# The default language is English, other supported languages are: -# Afrikaans, Arabic, Brazilian, Catalan, Chinese, Chinese-Traditional, -# Croatian, Czech, Danish, Dutch, Finnish, French, German, Greek, Hungarian, -# Italian, Japanese, Japanese-en (Japanese with English messages), Korean, -# Korean-en, Lithuanian, Norwegian, Polish, Portuguese, Romanian, Russian, -# Serbian, Slovak, Slovene, Spanish, Swedish, and Ukrainian. - -OUTPUT_LANGUAGE = English - -# If the BRIEF_MEMBER_DESC tag is set to YES (the default) Doxygen will -# include brief member descriptions after the members that are listed in -# the file and class documentation (similar to JavaDoc). -# Set to NO to disable this. - -BRIEF_MEMBER_DESC = YES - -# If the REPEAT_BRIEF tag is set to YES (the default) Doxygen will prepend -# the brief description of a member or function before the detailed description. -# Note: if both HIDE_UNDOC_MEMBERS and BRIEF_MEMBER_DESC are set to NO, the -# brief descriptions will be completely suppressed. - -REPEAT_BRIEF = YES - -# This tag implements a quasi-intelligent brief description abbreviator -# that is used to form the text in various listings. Each string -# in this list, if found as the leading text of the brief description, will be -# stripped from the text and the result after processing the whole list, is -# used as the annotated text. Otherwise, the brief description is used as-is. -# If left blank, the following values are used ("$name" is automatically -# replaced with the name of the entity): "The $name class" "The $name widget" -# "The $name file" "is" "provides" "specifies" "contains" -# "represents" "a" "an" "the" - -ABBREVIATE_BRIEF = - -# If the ALWAYS_DETAILED_SEC and REPEAT_BRIEF tags are both set to YES then -# Doxygen will generate a detailed section even if there is only a brief -# description. - -ALWAYS_DETAILED_SEC = NO - -# If the INLINE_INHERITED_MEMB tag is set to YES, doxygen will show all -# inherited members of a class in the documentation of that class as if those -# members were ordinary class members. Constructors, destructors and assignment -# operators of the base classes will not be shown. - -INLINE_INHERITED_MEMB = NO - -# If the FULL_PATH_NAMES tag is set to YES then Doxygen will prepend the full -# path before files name in the file list and in the header files. If set -# to NO the shortest path that makes the file name unique will be used. - -FULL_PATH_NAMES = NO - -# If the FULL_PATH_NAMES tag is set to YES then the STRIP_FROM_PATH tag -# can be used to strip a user-defined part of the path. Stripping is -# only done if one of the specified strings matches the left-hand part of -# the path. The tag can be used to show relative paths in the file list. -# If left blank the directory from which doxygen is run is used as the -# path to strip. - -STRIP_FROM_PATH = - -# The STRIP_FROM_INC_PATH tag can be used to strip a user-defined part of -# the path mentioned in the documentation of a class, which tells -# the reader which header file to include in order to use a class. -# If left blank only the name of the header file containing the class -# definition is used. Otherwise one should specify the include paths that -# are normally passed to the compiler using the -I flag. - -STRIP_FROM_INC_PATH = - -# If the SHORT_NAMES tag is set to YES, doxygen will generate much shorter -# (but less readable) file names. This can be useful is your file systems -# doesn't support long names like on DOS, Mac, or CD-ROM. - -SHORT_NAMES = NO - -# If the JAVADOC_AUTOBRIEF tag is set to YES then Doxygen -# will interpret the first line (until the first dot) of a JavaDoc-style -# comment as the brief description. If set to NO, the JavaDoc -# comments will behave just like regular Qt-style comments -# (thus requiring an explicit @brief command for a brief description.) - -JAVADOC_AUTOBRIEF = YES - -# If the QT_AUTOBRIEF tag is set to YES then Doxygen will -# interpret the first line (until the first dot) of a Qt-style -# comment as the brief description. If set to NO, the comments -# will behave just like regular Qt-style comments (thus requiring -# an explicit \brief command for a brief description.) - -QT_AUTOBRIEF = NO - -# The MULTILINE_CPP_IS_BRIEF tag can be set to YES to make Doxygen -# treat a multi-line C++ special comment block (i.e. a block of //! or /// -# comments) as a brief description. This used to be the default behaviour. -# The new default is to treat a multi-line C++ comment block as a detailed -# description. Set this tag to YES if you prefer the old behaviour instead. - -MULTILINE_CPP_IS_BRIEF = NO - -# If the DETAILS_AT_TOP tag is set to YES then Doxygen -# will output the detailed description near the top, like JavaDoc. -# If set to NO, the detailed description appears after the member -# documentation. - -DETAILS_AT_TOP = NO - -# If the INHERIT_DOCS tag is set to YES (the default) then an undocumented -# member inherits the documentation from any documented member that it -# re-implements. - -INHERIT_DOCS = YES - -# If the SEPARATE_MEMBER_PAGES tag is set to YES, then doxygen will produce -# a new page for each member. If set to NO, the documentation of a member will -# be part of the file/class/namespace that contains it. - -SEPARATE_MEMBER_PAGES = NO - -# The TAB_SIZE tag can be used to set the number of spaces in a tab. -# Doxygen uses this value to replace tabs by spaces in code fragments. - -TAB_SIZE = 4 - -# This tag can be used to specify a number of aliases that acts -# as commands in the documentation. An alias has the form "name=value". -# For example adding "sideeffect=\par Side Effects:\n" will allow you to -# put the command \sideeffect (or @sideeffect) in the documentation, which -# will result in a user-defined paragraph with heading "Side Effects:". -# You can put \n's in the value part of an alias to insert newlines. - -ALIASES = - -# Set the OPTIMIZE_OUTPUT_FOR_C tag to YES if your project consists of C -# sources only. Doxygen will then generate output that is more tailored for C. -# For instance, some of the names that are used will be different. The list -# of all members will be omitted, etc. - -OPTIMIZE_OUTPUT_FOR_C = YES - -# Set the OPTIMIZE_OUTPUT_JAVA tag to YES if your project consists of Java -# sources only. Doxygen will then generate output that is more tailored for Java. -# For instance, namespaces will be presented as packages, qualified scopes -# will look different, etc. - -OPTIMIZE_OUTPUT_JAVA = NO - -# If you use STL classes (i.e. std::string, std::vector, etc.) but do not want to -# include (a tag file for) the STL sources as input, then you should -# set this tag to YES in order to let doxygen match functions declarations and -# definitions whose arguments contain STL classes (e.g. func(std::string); v.s. -# func(std::string) {}). This also make the inheritance and collaboration -# diagrams that involve STL classes more complete and accurate. - -BUILTIN_STL_SUPPORT = NO - -# If you use Microsoft's C++/CLI language, you should set this option to YES to -# enable parsing support. - -CPP_CLI_SUPPORT = NO - -# If member grouping is used in the documentation and the DISTRIBUTE_GROUP_DOC -# tag is set to YES, then doxygen will reuse the documentation of the first -# member in the group (if any) for the other members of the group. By default -# all members of a group must be documented explicitly. - -DISTRIBUTE_GROUP_DOC = NO - -# Set the SUBGROUPING tag to YES (the default) to allow class member groups of -# the same type (for instance a group of public functions) to be put as a -# subgroup of that type (e.g. under the Public Functions section). Set it to -# NO to prevent subgrouping. Alternatively, this can be done per class using -# the \nosubgrouping command. - -SUBGROUPING = YES - -#--------------------------------------------------------------------------- -# Build related configuration options -#--------------------------------------------------------------------------- - -# If the EXTRACT_ALL tag is set to YES doxygen will assume all entities in -# documentation are documented, even if no documentation was available. -# Private class members and static file members will be hidden unless -# the EXTRACT_PRIVATE and EXTRACT_STATIC tags are set to YES - -EXTRACT_ALL = NO - -# If the EXTRACT_PRIVATE tag is set to YES all private members of a class -# will be included in the documentation. - -EXTRACT_PRIVATE = NO - -# If the EXTRACT_STATIC tag is set to YES all static members of a file -# will be included in the documentation. - -EXTRACT_STATIC = YES - -# If the EXTRACT_LOCAL_CLASSES tag is set to YES classes (and structs) -# defined locally in source files will be included in the documentation. -# If set to NO only classes defined in header files are included. - -EXTRACT_LOCAL_CLASSES = YES - -# This flag is only useful for Objective-C code. When set to YES local -# methods, which are defined in the implementation section but not in -# the interface are included in the documentation. -# If set to NO (the default) only methods in the interface are included. - -EXTRACT_LOCAL_METHODS = NO - -# If this flag is set to YES, the members of anonymous namespaces will be extracted -# and appear in the documentation as a namespace called 'anonymous_namespace{file}', -# where file will be replaced with the base name of the file that contains the anonymous -# namespace. By default anonymous namespace are hidden. - -EXTRACT_ANON_NSPACES = NO - -# If the HIDE_UNDOC_MEMBERS tag is set to YES, Doxygen will hide all -# undocumented members of documented classes, files or namespaces. -# If set to NO (the default) these members will be included in the -# various overviews, but no documentation section is generated. -# This option has no effect if EXTRACT_ALL is enabled. - -HIDE_UNDOC_MEMBERS = NO - -# If the HIDE_UNDOC_CLASSES tag is set to YES, Doxygen will hide all -# undocumented classes that are normally visible in the class hierarchy. -# If set to NO (the default) these classes will be included in the various -# overviews. This option has no effect if EXTRACT_ALL is enabled. - -HIDE_UNDOC_CLASSES = NO - -# If the HIDE_FRIEND_COMPOUNDS tag is set to YES, Doxygen will hide all -# friend (class|struct|union) declarations. -# If set to NO (the default) these declarations will be included in the -# documentation. - -HIDE_FRIEND_COMPOUNDS = NO - -# If the HIDE_IN_BODY_DOCS tag is set to YES, Doxygen will hide any -# documentation blocks found inside the body of a function. -# If set to NO (the default) these blocks will be appended to the -# function's detailed documentation block. - -HIDE_IN_BODY_DOCS = NO - -# The INTERNAL_DOCS tag determines if documentation -# that is typed after a \internal command is included. If the tag is set -# to NO (the default) then the documentation will be excluded. -# Set it to YES to include the internal documentation. - -INTERNAL_DOCS = NO - -# If the CASE_SENSE_NAMES tag is set to NO then Doxygen will only generate -# file names in lower-case letters. If set to YES upper-case letters are also -# allowed. This is useful if you have classes or files whose names only differ -# in case and if your file system supports case sensitive file names. Windows -# and Mac users are advised to set this option to NO. - -CASE_SENSE_NAMES = YES - -# If the HIDE_SCOPE_NAMES tag is set to NO (the default) then Doxygen -# will show members with their full class and namespace scopes in the -# documentation. If set to YES the scope will be hidden. - -HIDE_SCOPE_NAMES = NO - -# If the SHOW_INCLUDE_FILES tag is set to YES (the default) then Doxygen -# will put a list of the files that are included by a file in the documentation -# of that file. - -SHOW_INCLUDE_FILES = YES - -# If the INLINE_INFO tag is set to YES (the default) then a tag [inline] -# is inserted in the documentation for inline members. - -INLINE_INFO = YES - -# If the SORT_MEMBER_DOCS tag is set to YES (the default) then doxygen -# will sort the (detailed) documentation of file and class members -# alphabetically by member name. If set to NO the members will appear in -# declaration order. - -SORT_MEMBER_DOCS = NO - -# If the SORT_BRIEF_DOCS tag is set to YES then doxygen will sort the -# brief documentation of file, namespace and class members alphabetically -# by member name. If set to NO (the default) the members will appear in -# declaration order. - -SORT_BRIEF_DOCS = NO - -# If the SORT_BY_SCOPE_NAME tag is set to YES, the class list will be -# sorted by fully-qualified names, including namespaces. If set to -# NO (the default), the class list will be sorted only by class name, -# not including the namespace part. -# Note: This option is not very useful if HIDE_SCOPE_NAMES is set to YES. -# Note: This option applies only to the class list, not to the -# alphabetical list. - -SORT_BY_SCOPE_NAME = NO - -# The GENERATE_TODOLIST tag can be used to enable (YES) or -# disable (NO) the todo list. This list is created by putting \todo -# commands in the documentation. - -GENERATE_TODOLIST = YES - -# The GENERATE_TESTLIST tag can be used to enable (YES) or -# disable (NO) the test list. This list is created by putting \test -# commands in the documentation. - -GENERATE_TESTLIST = YES - -# The GENERATE_BUGLIST tag can be used to enable (YES) or -# disable (NO) the bug list. This list is created by putting \bug -# commands in the documentation. - -GENERATE_BUGLIST = YES - -# The GENERATE_DEPRECATEDLIST tag can be used to enable (YES) or -# disable (NO) the deprecated list. This list is created by putting -# \deprecated commands in the documentation. - -GENERATE_DEPRECATEDLIST= YES - -# The ENABLED_SECTIONS tag can be used to enable conditional -# documentation sections, marked by \if sectionname ... \endif. - -ENABLED_SECTIONS = - -# The MAX_INITIALIZER_LINES tag determines the maximum number of lines -# the initial value of a variable or define consists of for it to appear in -# the documentation. If the initializer consists of more lines than specified -# here it will be hidden. Use a value of 0 to hide initializers completely. -# The appearance of the initializer of individual variables and defines in the -# documentation can be controlled using \showinitializer or \hideinitializer -# command in the documentation regardless of this setting. - -MAX_INITIALIZER_LINES = 30 - -# Set the SHOW_USED_FILES tag to NO to disable the list of files generated -# at the bottom of the documentation of classes and structs. If set to YES the -# list will mention the files that were used to generate the documentation. - -SHOW_USED_FILES = YES - -# If the sources in your project are distributed over multiple directories -# then setting the SHOW_DIRECTORIES tag to YES will show the directory hierarchy -# in the documentation. The default is NO. - -SHOW_DIRECTORIES = NO - -# The FILE_VERSION_FILTER tag can be used to specify a program or script that -# doxygen should invoke to get the current version for each file (typically from the -# version control system). Doxygen will invoke the program by executing (via -# popen()) the command , where is the value of -# the FILE_VERSION_FILTER tag, and is the name of an input file -# provided by doxygen. Whatever the program writes to standard output -# is used as the file version. See the manual for examples. - -FILE_VERSION_FILTER = - -#--------------------------------------------------------------------------- -# configuration options related to warning and progress messages -#--------------------------------------------------------------------------- - -# The QUIET tag can be used to turn on/off the messages that are generated -# by doxygen. Possible values are YES and NO. If left blank NO is used. - -QUIET = NO - -# The WARNINGS tag can be used to turn on/off the warning messages that are -# generated by doxygen. Possible values are YES and NO. If left blank -# NO is used. - -WARNINGS = YES - -# If WARN_IF_UNDOCUMENTED is set to YES, then doxygen will generate warnings -# for undocumented members. If EXTRACT_ALL is set to YES then this flag will -# automatically be disabled. - -WARN_IF_UNDOCUMENTED = YES - -# If WARN_IF_DOC_ERROR is set to YES, doxygen will generate warnings for -# potential errors in the documentation, such as not documenting some -# parameters in a documented function, or documenting parameters that -# don't exist or using markup commands wrongly. - -WARN_IF_DOC_ERROR = YES - -# This WARN_NO_PARAMDOC option can be abled to get warnings for -# functions that are documented, but have no documentation for their parameters -# or return value. If set to NO (the default) doxygen will only warn about -# wrong or incomplete parameter documentation, but not about the absence of -# documentation. - -WARN_NO_PARAMDOC = NO - -# The WARN_FORMAT tag determines the format of the warning messages that -# doxygen can produce. The string should contain the $file, $line, and $text -# tags, which will be replaced by the file and line number from which the -# warning originated and the warning text. Optionally the format may contain -# $version, which will be replaced by the version of the file (if it could -# be obtained via FILE_VERSION_FILTER) - -WARN_FORMAT = "$file:$line: $text" - -# The WARN_LOGFILE tag can be used to specify a file to which warning -# and error messages should be written. If left blank the output is written -# to stderr. - -WARN_LOGFILE = - -#--------------------------------------------------------------------------- -# configuration options related to the input files -#--------------------------------------------------------------------------- - -# The INPUT tag can be used to specify the files and/or directories that contain -# documented source files. You may enter file names like "myfile.cpp" or -# directories like "/usr/src/myproject". Separate the files or directories -# with spaces. - -INPUT = @top_srcdir@/libusb - -# This tag can be used to specify the character encoding of the source files that -# doxygen parses. Internally doxygen uses the UTF-8 encoding, which is also the default -# input encoding. Doxygen uses libiconv (or the iconv built into libc) for the transcoding. -# See http://www.gnu.org/software/libiconv for the list of possible encodings. - -INPUT_ENCODING = UTF-8 - -# If the value of the INPUT tag contains directories, you can use the -# FILE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp -# and *.h) to filter out the source-files in the directories. If left -# blank the following patterns are tested: -# *.c *.cc *.cxx *.cpp *.c++ *.java *.ii *.ixx *.ipp *.i++ *.inl *.h *.hh *.hxx -# *.hpp *.h++ *.idl *.odl *.cs *.php *.php3 *.inc *.m *.mm *.py - -FILE_PATTERNS = - -# The RECURSIVE tag can be used to turn specify whether or not subdirectories -# should be searched for input files as well. Possible values are YES and NO. -# If left blank NO is used. - -RECURSIVE = NO - -# The EXCLUDE tag can be used to specify files and/or directories that should -# excluded from the INPUT source files. This way you can easily exclude a -# subdirectory from a directory tree whose root is specified with the INPUT tag. - -EXCLUDE = @top_srcdir@/libusb/libusbi.h @top_srcdir@/libusb/hotplug.h - -# The EXCLUDE_SYMLINKS tag can be used select whether or not files or -# directories that are symbolic links (a Unix filesystem feature) are excluded -# from the input. - -EXCLUDE_SYMLINKS = NO - -# If the value of the INPUT tag contains directories, you can use the -# EXCLUDE_PATTERNS tag to specify one or more wildcard patterns to exclude -# certain files from those directories. Note that the wildcards are matched -# against the file with absolute path, so to exclude all test directories -# for example use the pattern */test/* - -EXCLUDE_PATTERNS = - -# The EXCLUDE_SYMBOLS tag can be used to specify one or more symbol names -# (namespaces, classes, functions, etc.) that should be excluded from the output. -# The symbol name can be a fully qualified name, a word, or if the wildcard * is used, -# a substring. Examples: ANamespace, AClass, AClass::ANamespace, ANamespace::*Test - -EXCLUDE_SYMBOLS = - -# The EXAMPLE_PATH tag can be used to specify one or more files or -# directories that contain example code fragments that are included (see -# the \include command). - -EXAMPLE_PATH = - -# If the value of the EXAMPLE_PATH tag contains directories, you can use the -# EXAMPLE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp -# and *.h) to filter out the source-files in the directories. If left -# blank all files are included. - -EXAMPLE_PATTERNS = - -# If the EXAMPLE_RECURSIVE tag is set to YES then subdirectories will be -# searched for input files to be used with the \include or \dontinclude -# commands irrespective of the value of the RECURSIVE tag. -# Possible values are YES and NO. If left blank NO is used. - -EXAMPLE_RECURSIVE = NO - -# The IMAGE_PATH tag can be used to specify one or more files or -# directories that contain image that are included in the documentation (see -# the \image command). - -IMAGE_PATH = - -# The INPUT_FILTER tag can be used to specify a program that doxygen should -# invoke to filter for each input file. Doxygen will invoke the filter program -# by executing (via popen()) the command , where -# is the value of the INPUT_FILTER tag, and is the name of an -# input file. Doxygen will then use the output that the filter program writes -# to standard output. If FILTER_PATTERNS is specified, this tag will be -# ignored. - -INPUT_FILTER = - -# The FILTER_PATTERNS tag can be used to specify filters on a per file pattern -# basis. Doxygen will compare the file name with each pattern and apply the -# filter if there is a match. The filters are a list of the form: -# pattern=filter (like *.cpp=my_cpp_filter). See INPUT_FILTER for further -# info on how filters are used. If FILTER_PATTERNS is empty, INPUT_FILTER -# is applied to all files. - -FILTER_PATTERNS = - -# If the FILTER_SOURCE_FILES tag is set to YES, the input filter (if set using -# INPUT_FILTER) will be used to filter the input files when producing source -# files to browse (i.e. when SOURCE_BROWSER is set to YES). - -FILTER_SOURCE_FILES = NO - -#--------------------------------------------------------------------------- -# configuration options related to source browsing -#--------------------------------------------------------------------------- - -# If the SOURCE_BROWSER tag is set to YES then a list of source files will -# be generated. Documented entities will be cross-referenced with these sources. -# Note: To get rid of all source code in the generated output, make sure also -# VERBATIM_HEADERS is set to NO. If you have enabled CALL_GRAPH or CALLER_GRAPH -# then you must also enable this option. If you don't then doxygen will produce -# a warning and turn it on anyway - -SOURCE_BROWSER = NO - -# Setting the INLINE_SOURCES tag to YES will include the body -# of functions and classes directly in the documentation. - -INLINE_SOURCES = NO - -# Setting the STRIP_CODE_COMMENTS tag to YES (the default) will instruct -# doxygen to hide any special comment blocks from generated source code -# fragments. Normal C and C++ comments will always remain visible. - -STRIP_CODE_COMMENTS = YES - -# If the REFERENCED_BY_RELATION tag is set to YES (the default) -# then for each documented function all documented -# functions referencing it will be listed. - -REFERENCED_BY_RELATION = NO - -# If the REFERENCES_RELATION tag is set to YES (the default) -# then for each documented function all documented entities -# called/used by that function will be listed. - -REFERENCES_RELATION = NO - -# If the REFERENCES_LINK_SOURCE tag is set to YES (the default) -# and SOURCE_BROWSER tag is set to YES, then the hyperlinks from -# functions in REFERENCES_RELATION and REFERENCED_BY_RELATION lists will -# link to the source code. Otherwise they will link to the documentstion. - -REFERENCES_LINK_SOURCE = YES - -# If the USE_HTAGS tag is set to YES then the references to source code -# will point to the HTML generated by the htags(1) tool instead of doxygen -# built-in source browser. The htags tool is part of GNU's global source -# tagging system (see http://www.gnu.org/software/global/global.html). You -# will need version 4.8.6 or higher. - -USE_HTAGS = NO - -# If the VERBATIM_HEADERS tag is set to YES (the default) then Doxygen -# will generate a verbatim copy of the header file for each class for -# which an include is specified. Set to NO to disable this. - -VERBATIM_HEADERS = YES - -#--------------------------------------------------------------------------- -# configuration options related to the alphabetical class index -#--------------------------------------------------------------------------- - -# If the ALPHABETICAL_INDEX tag is set to YES, an alphabetical index -# of all compounds will be generated. Enable this if the project -# contains a lot of classes, structs, unions or interfaces. - -ALPHABETICAL_INDEX = YES - -# If the alphabetical index is enabled (see ALPHABETICAL_INDEX) then -# the COLS_IN_ALPHA_INDEX tag can be used to specify the number of columns -# in which this list will be split (can be a number in the range [1..20]) - -COLS_IN_ALPHA_INDEX = 5 - -# In case all classes in a project start with a common prefix, all -# classes will be put under the same header in the alphabetical index. -# The IGNORE_PREFIX tag can be used to specify one or more prefixes that -# should be ignored while generating the index headers. - -IGNORE_PREFIX = - -#--------------------------------------------------------------------------- -# configuration options related to the HTML output -#--------------------------------------------------------------------------- - -# If the GENERATE_HTML tag is set to YES (the default) Doxygen will -# generate HTML output. - -GENERATE_HTML = YES - -# The HTML_OUTPUT tag is used to specify where the HTML docs will be put. -# If a relative path is entered the value of OUTPUT_DIRECTORY will be -# put in front of it. If left blank `html' will be used as the default path. - -HTML_OUTPUT = html - -# The HTML_FILE_EXTENSION tag can be used to specify the file extension for -# each generated HTML page (for example: .htm,.php,.asp). If it is left blank -# doxygen will generate files with .html extension. - -HTML_FILE_EXTENSION = .html - -# The HTML_HEADER tag can be used to specify a personal HTML header for -# each generated HTML page. If it is left blank doxygen will generate a -# standard header. - -HTML_HEADER = - -# The HTML_FOOTER tag can be used to specify a personal HTML footer for -# each generated HTML page. If it is left blank doxygen will generate a -# standard footer. - -HTML_FOOTER = - -# The HTML_STYLESHEET tag can be used to specify a user-defined cascading -# style sheet that is used by each HTML page. It can be used to -# fine-tune the look of the HTML output. If the tag is left blank doxygen -# will generate a default style sheet. Note that doxygen will try to copy -# the style sheet file to the HTML output directory, so don't put your own -# stylesheet in the HTML output directory as well, or it will be erased! - -HTML_STYLESHEET = - -# If the HTML_ALIGN_MEMBERS tag is set to YES, the members of classes, -# files or namespaces will be aligned in HTML using tables. If set to -# NO a bullet list will be used. - -HTML_ALIGN_MEMBERS = YES - -# If the GENERATE_HTMLHELP tag is set to YES, additional index files -# will be generated that can be used as input for tools like the -# Microsoft HTML help workshop to generate a compressed HTML help file (.chm) -# of the generated HTML documentation. - -GENERATE_HTMLHELP = NO - -# If the HTML_DYNAMIC_SECTIONS tag is set to YES then the generated HTML -# documentation will contain sections that can be hidden and shown after the -# page has loaded. For this to work a browser that supports -# JavaScript and DHTML is required (for instance Mozilla 1.0+, Firefox -# Netscape 6.0+, Internet explorer 5.0+, Konqueror, or Safari). - -HTML_DYNAMIC_SECTIONS = YES - -# If the GENERATE_HTMLHELP tag is set to YES, the CHM_FILE tag can -# be used to specify the file name of the resulting .chm file. You -# can add a path in front of the file if the result should not be -# written to the html output directory. - -CHM_FILE = - -# If the GENERATE_HTMLHELP tag is set to YES, the HHC_LOCATION tag can -# be used to specify the location (absolute path including file name) of -# the HTML help compiler (hhc.exe). If non-empty doxygen will try to run -# the HTML help compiler on the generated index.hhp. - -HHC_LOCATION = - -# If the GENERATE_HTMLHELP tag is set to YES, the GENERATE_CHI flag -# controls if a separate .chi index file is generated (YES) or that -# it should be included in the master .chm file (NO). - -GENERATE_CHI = NO - -# If the GENERATE_HTMLHELP tag is set to YES, the BINARY_TOC flag -# controls whether a binary table of contents is generated (YES) or a -# normal table of contents (NO) in the .chm file. - -BINARY_TOC = NO - -# The TOC_EXPAND flag can be set to YES to add extra items for group members -# to the contents of the HTML help documentation and to the tree view. - -TOC_EXPAND = NO - -# The DISABLE_INDEX tag can be used to turn on/off the condensed index at -# top of each HTML page. The value NO (the default) enables the index and -# the value YES disables it. - -DISABLE_INDEX = NO - -# This tag can be used to set the number of enum values (range [1..20]) -# that doxygen will group on one line in the generated HTML documentation. - -ENUM_VALUES_PER_LINE = 4 - -# If the GENERATE_TREEVIEW tag is set to YES, a side panel will be -# generated containing a tree-like index structure (just like the one that -# is generated for HTML Help). For this to work a browser that supports -# JavaScript, DHTML, CSS and frames is required (for instance Mozilla 1.0+, -# Netscape 6.0+, Internet explorer 5.0+, or Konqueror). Windows users are -# probably better off using the HTML help feature. - -GENERATE_TREEVIEW = NO - -# If the treeview is enabled (see GENERATE_TREEVIEW) then this tag can be -# used to set the initial width (in pixels) of the frame in which the tree -# is shown. - -TREEVIEW_WIDTH = 250 - -#--------------------------------------------------------------------------- -# configuration options related to the LaTeX output -#--------------------------------------------------------------------------- - -# If the GENERATE_LATEX tag is set to YES (the default) Doxygen will -# generate Latex output. - -GENERATE_LATEX = NO - -# The LATEX_OUTPUT tag is used to specify where the LaTeX docs will be put. -# If a relative path is entered the value of OUTPUT_DIRECTORY will be -# put in front of it. If left blank `latex' will be used as the default path. - -LATEX_OUTPUT = latex - -# The LATEX_CMD_NAME tag can be used to specify the LaTeX command name to be -# invoked. If left blank `latex' will be used as the default command name. - -LATEX_CMD_NAME = latex - -# The MAKEINDEX_CMD_NAME tag can be used to specify the command name to -# generate index for LaTeX. If left blank `makeindex' will be used as the -# default command name. - -MAKEINDEX_CMD_NAME = makeindex - -# If the COMPACT_LATEX tag is set to YES Doxygen generates more compact -# LaTeX documents. This may be useful for small projects and may help to -# save some trees in general. - -COMPACT_LATEX = NO - -# The PAPER_TYPE tag can be used to set the paper type that is used -# by the printer. Possible values are: a4, a4wide, letter, legal and -# executive. If left blank a4wide will be used. - -PAPER_TYPE = a4wide - -# The EXTRA_PACKAGES tag can be to specify one or more names of LaTeX -# packages that should be included in the LaTeX output. - -EXTRA_PACKAGES = - -# The LATEX_HEADER tag can be used to specify a personal LaTeX header for -# the generated latex document. The header should contain everything until -# the first chapter. If it is left blank doxygen will generate a -# standard header. Notice: only use this tag if you know what you are doing! - -LATEX_HEADER = - -# If the PDF_HYPERLINKS tag is set to YES, the LaTeX that is generated -# is prepared for conversion to pdf (using ps2pdf). The pdf file will -# contain links (just like the HTML output) instead of page references -# This makes the output suitable for online browsing using a pdf viewer. - -PDF_HYPERLINKS = NO - -# If the USE_PDFLATEX tag is set to YES, pdflatex will be used instead of -# plain latex in the generated Makefile. Set this option to YES to get a -# higher quality PDF documentation. - -USE_PDFLATEX = NO - -# If the LATEX_BATCHMODE tag is set to YES, doxygen will add the \\batchmode. -# command to the generated LaTeX files. This will instruct LaTeX to keep -# running if errors occur, instead of asking the user for help. -# This option is also used when generating formulas in HTML. - -LATEX_BATCHMODE = NO - -# If LATEX_HIDE_INDICES is set to YES then doxygen will not -# include the index chapters (such as File Index, Compound Index, etc.) -# in the output. - -LATEX_HIDE_INDICES = NO - -#--------------------------------------------------------------------------- -# configuration options related to the RTF output -#--------------------------------------------------------------------------- - -# If the GENERATE_RTF tag is set to YES Doxygen will generate RTF output -# The RTF output is optimized for Word 97 and may not look very pretty with -# other RTF readers or editors. - -GENERATE_RTF = NO - -# The RTF_OUTPUT tag is used to specify where the RTF docs will be put. -# If a relative path is entered the value of OUTPUT_DIRECTORY will be -# put in front of it. If left blank `rtf' will be used as the default path. - -RTF_OUTPUT = rtf - -# If the COMPACT_RTF tag is set to YES Doxygen generates more compact -# RTF documents. This may be useful for small projects and may help to -# save some trees in general. - -COMPACT_RTF = NO - -# If the RTF_HYPERLINKS tag is set to YES, the RTF that is generated -# will contain hyperlink fields. The RTF file will -# contain links (just like the HTML output) instead of page references. -# This makes the output suitable for online browsing using WORD or other -# programs which support those fields. -# Note: wordpad (write) and others do not support links. - -RTF_HYPERLINKS = NO - -# Load stylesheet definitions from file. Syntax is similar to doxygen's -# config file, i.e. a series of assignments. You only have to provide -# replacements, missing definitions are set to their default value. - -RTF_STYLESHEET_FILE = - -# Set optional variables used in the generation of an rtf document. -# Syntax is similar to doxygen's config file. - -RTF_EXTENSIONS_FILE = - -#--------------------------------------------------------------------------- -# configuration options related to the man page output -#--------------------------------------------------------------------------- - -# If the GENERATE_MAN tag is set to YES (the default) Doxygen will -# generate man pages - -GENERATE_MAN = NO - -# The MAN_OUTPUT tag is used to specify where the man pages will be put. -# If a relative path is entered the value of OUTPUT_DIRECTORY will be -# put in front of it. If left blank `man' will be used as the default path. - -MAN_OUTPUT = man - -# The MAN_EXTENSION tag determines the extension that is added to -# the generated man pages (default is the subroutine's section .3) - -MAN_EXTENSION = .3 - -# If the MAN_LINKS tag is set to YES and Doxygen generates man output, -# then it will generate one additional man file for each entity -# documented in the real man page(s). These additional files -# only source the real man page, but without them the man command -# would be unable to find the correct page. The default is NO. - -MAN_LINKS = NO - -#--------------------------------------------------------------------------- -# configuration options related to the XML output -#--------------------------------------------------------------------------- - -# If the GENERATE_XML tag is set to YES Doxygen will -# generate an XML file that captures the structure of -# the code including all documentation. - -GENERATE_XML = NO - -# The XML_OUTPUT tag is used to specify where the XML pages will be put. -# If a relative path is entered the value of OUTPUT_DIRECTORY will be -# put in front of it. If left blank `xml' will be used as the default path. - -XML_OUTPUT = xml - -# The XML_SCHEMA tag can be used to specify an XML schema, -# which can be used by a validating XML parser to check the -# syntax of the XML files. - -XML_SCHEMA = - -# The XML_DTD tag can be used to specify an XML DTD, -# which can be used by a validating XML parser to check the -# syntax of the XML files. - -XML_DTD = - -# If the XML_PROGRAMLISTING tag is set to YES Doxygen will -# dump the program listings (including syntax highlighting -# and cross-referencing information) to the XML output. Note that -# enabling this will significantly increase the size of the XML output. - -XML_PROGRAMLISTING = YES - -#--------------------------------------------------------------------------- -# configuration options for the AutoGen Definitions output -#--------------------------------------------------------------------------- - -# If the GENERATE_AUTOGEN_DEF tag is set to YES Doxygen will -# generate an AutoGen Definitions (see autogen.sf.net) file -# that captures the structure of the code including all -# documentation. Note that this feature is still experimental -# and incomplete at the moment. - -GENERATE_AUTOGEN_DEF = NO - -#--------------------------------------------------------------------------- -# configuration options related to the Perl module output -#--------------------------------------------------------------------------- - -# If the GENERATE_PERLMOD tag is set to YES Doxygen will -# generate a Perl module file that captures the structure of -# the code including all documentation. Note that this -# feature is still experimental and incomplete at the -# moment. - -GENERATE_PERLMOD = NO - -# If the PERLMOD_LATEX tag is set to YES Doxygen will generate -# the necessary Makefile rules, Perl scripts and LaTeX code to be able -# to generate PDF and DVI output from the Perl module output. - -PERLMOD_LATEX = NO - -# If the PERLMOD_PRETTY tag is set to YES the Perl module output will be -# nicely formatted so it can be parsed by a human reader. This is useful -# if you want to understand what is going on. On the other hand, if this -# tag is set to NO the size of the Perl module output will be much smaller -# and Perl will parse it just the same. - -PERLMOD_PRETTY = YES - -# The names of the make variables in the generated doxyrules.make file -# are prefixed with the string contained in PERLMOD_MAKEVAR_PREFIX. -# This is useful so different doxyrules.make files included by the same -# Makefile don't overwrite each other's variables. - -PERLMOD_MAKEVAR_PREFIX = - -#--------------------------------------------------------------------------- -# Configuration options related to the preprocessor -#--------------------------------------------------------------------------- - -# If the ENABLE_PREPROCESSING tag is set to YES (the default) Doxygen will -# evaluate all C-preprocessor directives found in the sources and include -# files. - -ENABLE_PREPROCESSING = YES - -# If the MACRO_EXPANSION tag is set to YES Doxygen will expand all macro -# names in the source code. If set to NO (the default) only conditional -# compilation will be performed. Macro expansion can be done in a controlled -# way by setting EXPAND_ONLY_PREDEF to YES. - -MACRO_EXPANSION = YES - -# If the EXPAND_ONLY_PREDEF and MACRO_EXPANSION tags are both set to YES -# then the macro expansion is limited to the macros specified with the -# PREDEFINED and EXPAND_AS_DEFINED tags. - -EXPAND_ONLY_PREDEF = YES - -# If the SEARCH_INCLUDES tag is set to YES (the default) the includes files -# in the INCLUDE_PATH (see below) will be search if a #include is found. - -SEARCH_INCLUDES = YES - -# The INCLUDE_PATH tag can be used to specify one or more directories that -# contain include files that are not input files but should be processed by -# the preprocessor. - -INCLUDE_PATH = - -# You can use the INCLUDE_FILE_PATTERNS tag to specify one or more wildcard -# patterns (like *.h and *.hpp) to filter out the header-files in the -# directories. If left blank, the patterns specified with FILE_PATTERNS will -# be used. - -INCLUDE_FILE_PATTERNS = - -# The PREDEFINED tag can be used to specify one or more macro names that -# are defined before the preprocessor is started (similar to the -D option of -# gcc). The argument of the tag is a list of macros of the form: name -# or name=definition (no spaces). If the definition and the = are -# omitted =1 is assumed. To prevent a macro definition from being -# undefined via #undef or recursively expanded use the := operator -# instead of the = operator. - -PREDEFINED = API_EXPORTED= LIBUSB_CALL= DEFAULT_VISIBILITY= - -# If the MACRO_EXPANSION and EXPAND_ONLY_PREDEF tags are set to YES then -# this tag can be used to specify a list of macro names that should be expanded. -# The macro definition that is found in the sources will be used. -# Use the PREDEFINED tag if you want to use a different macro definition. - -EXPAND_AS_DEFINED = - -# If the SKIP_FUNCTION_MACROS tag is set to YES (the default) then -# doxygen's preprocessor will remove all function-like macros that are alone -# on a line, have an all uppercase name, and do not end with a semicolon. Such -# function macros are typically used for boiler-plate code, and will confuse -# the parser if not removed. - -SKIP_FUNCTION_MACROS = YES - -#--------------------------------------------------------------------------- -# Configuration::additions related to external references -#--------------------------------------------------------------------------- - -# The TAGFILES option can be used to specify one or more tagfiles. -# Optionally an initial location of the external documentation -# can be added for each tagfile. The format of a tag file without -# this location is as follows: -# TAGFILES = file1 file2 ... -# Adding location for the tag files is done as follows: -# TAGFILES = file1=loc1 "file2 = loc2" ... -# where "loc1" and "loc2" can be relative or absolute paths or -# URLs. If a location is present for each tag, the installdox tool -# does not have to be run to correct the links. -# Note that each tag file must have a unique name -# (where the name does NOT include the path) -# If a tag file is not located in the directory in which doxygen -# is run, you must also specify the path to the tagfile here. - -TAGFILES = - -# When a file name is specified after GENERATE_TAGFILE, doxygen will create -# a tag file that is based on the input files it reads. - -GENERATE_TAGFILE = - -# If the ALLEXTERNALS tag is set to YES all external classes will be listed -# in the class index. If set to NO only the inherited external classes -# will be listed. - -ALLEXTERNALS = NO - -# If the EXTERNAL_GROUPS tag is set to YES all external groups will be listed -# in the modules index. If set to NO, only the current project's groups will -# be listed. - -EXTERNAL_GROUPS = YES - -# The PERL_PATH should be the absolute path and name of the perl script -# interpreter (i.e. the result of `which perl'). - -PERL_PATH = /usr/bin/perl - -#--------------------------------------------------------------------------- -# Configuration options related to the dot tool -#--------------------------------------------------------------------------- - -# If the CLASS_DIAGRAMS tag is set to YES (the default) Doxygen will -# generate a inheritance diagram (in HTML, RTF and LaTeX) for classes with base -# or super classes. Setting the tag to NO turns the diagrams off. Note that -# this option is superseded by the HAVE_DOT option below. This is only a -# fallback. It is recommended to install and use dot, since it yields more -# powerful graphs. - -CLASS_DIAGRAMS = YES - -# You can define message sequence charts within doxygen comments using the \msc -# command. Doxygen will then run the mscgen tool (see http://www.mcternan.me.uk/mscgen/) to -# produce the chart and insert it in the documentation. The MSCGEN_PATH tag allows you to -# specify the directory where the mscgen tool resides. If left empty the tool is assumed to -# be found in the default search path. - -MSCGEN_PATH = - -# If set to YES, the inheritance and collaboration graphs will hide -# inheritance and usage relations if the target is undocumented -# or is not a class. - -HIDE_UNDOC_RELATIONS = YES - -# If you set the HAVE_DOT tag to YES then doxygen will assume the dot tool is -# available from the path. This tool is part of Graphviz, a graph visualization -# toolkit from AT&T and Lucent Bell Labs. The other options in this section -# have no effect if this option is set to NO (the default) - -HAVE_DOT = NO - -# If the CLASS_GRAPH and HAVE_DOT tags are set to YES then doxygen -# will generate a graph for each documented class showing the direct and -# indirect inheritance relations. Setting this tag to YES will force the -# the CLASS_DIAGRAMS tag to NO. - -CLASS_GRAPH = YES - -# If the COLLABORATION_GRAPH and HAVE_DOT tags are set to YES then doxygen -# will generate a graph for each documented class showing the direct and -# indirect implementation dependencies (inheritance, containment, and -# class references variables) of the class with other documented classes. - -COLLABORATION_GRAPH = YES - -# If the GROUP_GRAPHS and HAVE_DOT tags are set to YES then doxygen -# will generate a graph for groups, showing the direct groups dependencies - -GROUP_GRAPHS = YES - -# If the UML_LOOK tag is set to YES doxygen will generate inheritance and -# collaboration diagrams in a style similar to the OMG's Unified Modeling -# Language. - -UML_LOOK = NO - -# If set to YES, the inheritance and collaboration graphs will show the -# relations between templates and their instances. - -TEMPLATE_RELATIONS = NO - -# If the ENABLE_PREPROCESSING, SEARCH_INCLUDES, INCLUDE_GRAPH, and HAVE_DOT -# tags are set to YES then doxygen will generate a graph for each documented -# file showing the direct and indirect include dependencies of the file with -# other documented files. - -INCLUDE_GRAPH = YES - -# If the ENABLE_PREPROCESSING, SEARCH_INCLUDES, INCLUDED_BY_GRAPH, and -# HAVE_DOT tags are set to YES then doxygen will generate a graph for each -# documented header file showing the documented files that directly or -# indirectly include this file. - -INCLUDED_BY_GRAPH = YES - -# If the CALL_GRAPH, SOURCE_BROWSER and HAVE_DOT tags are set to YES then doxygen will -# generate a call dependency graph for every global function or class method. -# Note that enabling this option will significantly increase the time of a run. -# So in most cases it will be better to enable call graphs for selected -# functions only using the \callgraph command. - -CALL_GRAPH = NO - -# If the CALLER_GRAPH, SOURCE_BROWSER and HAVE_DOT tags are set to YES then doxygen will -# generate a caller dependency graph for every global function or class method. -# Note that enabling this option will significantly increase the time of a run. -# So in most cases it will be better to enable caller graphs for selected -# functions only using the \callergraph command. - -CALLER_GRAPH = NO - -# If the GRAPHICAL_HIERARCHY and HAVE_DOT tags are set to YES then doxygen -# will graphical hierarchy of all classes instead of a textual one. - -GRAPHICAL_HIERARCHY = YES - -# If the DIRECTORY_GRAPH, SHOW_DIRECTORIES and HAVE_DOT tags are set to YES -# then doxygen will show the dependencies a directory has on other directories -# in a graphical way. The dependency relations are determined by the #include -# relations between the files in the directories. - -DIRECTORY_GRAPH = YES - -# The DOT_IMAGE_FORMAT tag can be used to set the image format of the images -# generated by dot. Possible values are png, jpg, or gif -# If left blank png will be used. - -DOT_IMAGE_FORMAT = png - -# The tag DOT_PATH can be used to specify the path where the dot tool can be -# found. If left blank, it is assumed the dot tool can be found in the path. - -DOT_PATH = - -# The DOTFILE_DIRS tag can be used to specify one or more directories that -# contain dot files that are included in the documentation (see the -# \dotfile command). - -DOTFILE_DIRS = - -# The MAX_DOT_GRAPH_MAX_NODES tag can be used to set the maximum number of -# nodes that will be shown in the graph. If the number of nodes in a graph -# becomes larger than this value, doxygen will truncate the graph, which is -# visualized by representing a node as a red box. Note that doxygen if the number -# of direct children of the root node in a graph is already larger than -# MAX_DOT_GRAPH_NOTES then the graph will not be shown at all. Also note -# that the size of a graph can be further restricted by MAX_DOT_GRAPH_DEPTH. - -DOT_GRAPH_MAX_NODES = 50 - -# The MAX_DOT_GRAPH_DEPTH tag can be used to set the maximum depth of the -# graphs generated by dot. A depth value of 3 means that only nodes reachable -# from the root by following a path via at most 3 edges will be shown. Nodes -# that lay further from the root node will be omitted. Note that setting this -# option to 1 or 2 may greatly reduce the computation time needed for large -# code bases. Also note that the size of a graph can be further restricted by -# DOT_GRAPH_MAX_NODES. Using a depth of 0 means no depth restriction. - -MAX_DOT_GRAPH_DEPTH = 0 - -# Set the DOT_TRANSPARENT tag to YES to generate images with a transparent -# background. This is disabled by default, which results in a white background. -# Warning: Depending on the platform used, enabling this option may lead to -# badly anti-aliased labels on the edges of a graph (i.e. they become hard to -# read). - -DOT_TRANSPARENT = NO - -# Set the DOT_MULTI_TARGETS tag to YES allow dot to generate multiple output -# files in one run (i.e. multiple -o and -T options on the command line). This -# makes dot run faster, but since only newer versions of dot (>1.8.10) -# support this, this feature is disabled by default. - -DOT_MULTI_TARGETS = NO - -# If the GENERATE_LEGEND tag is set to YES (the default) Doxygen will -# generate a legend page explaining the meaning of the various boxes and -# arrows in the dot generated graphs. - -GENERATE_LEGEND = YES - -# If the DOT_CLEANUP tag is set to YES (the default) Doxygen will -# remove the intermediate dot files that are used to generate -# the various graphs. - -DOT_CLEANUP = YES - -#--------------------------------------------------------------------------- -# Configuration::additions related to the search engine -#--------------------------------------------------------------------------- - -# The SEARCHENGINE tag specifies whether or not a search engine should be -# used. If set to NO the values of all tags below this one will be ignored. - -SEARCHENGINE = NO diff --git a/compat/libusb-1.0/examples/Makefile.am b/compat/libusb-1.0/examples/Makefile.am deleted file mode 100644 index 037b886a15..0000000000 --- a/compat/libusb-1.0/examples/Makefile.am +++ /dev/null @@ -1,15 +0,0 @@ -AM_CPPFLAGS = -I$(top_srcdir)/libusb -LDADD = ../libusb/libusb-1.0.la -noinst_PROGRAMS = listdevs hotplugtest testlibusb1 - -if HAVE_SIGACTION -noinst_PROGRAMS += dpfp - -if THREADS_POSIX -dpfp_threaded_CFLAGS = $(AM_CFLAGS) -noinst_PROGRAMS += dpfp_threaded -endif - -sam3u_benchmark_SOURCES = sam3u_benchmark.c -noinst_PROGRAMS += sam3u_benchmark -endif diff --git a/compat/libusb-1.0/examples/dpfp.c b/compat/libusb-1.0/examples/dpfp.c deleted file mode 100644 index ecd5a92355..0000000000 --- a/compat/libusb-1.0/examples/dpfp.c +++ /dev/null @@ -1,507 +0,0 @@ -/* - * libusb example program to manipulate U.are.U 4000B fingerprint scanner. - * Copyright (C) 2007 Daniel Drake - * - * Basic image capture program only, does not consider the powerup quirks or - * the fact that image encryption may be enabled. Not expected to work - * flawlessly all of the time. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - */ - -#include -#include -#include -#include -#include - -#include - -#define EP_INTR (1 | LIBUSB_ENDPOINT_IN) -#define EP_DATA (2 | LIBUSB_ENDPOINT_IN) -#define CTRL_IN (LIBUSB_REQUEST_TYPE_VENDOR | LIBUSB_ENDPOINT_IN) -#define CTRL_OUT (LIBUSB_REQUEST_TYPE_VENDOR | LIBUSB_ENDPOINT_OUT) -#define USB_RQ 0x04 -#define INTR_LENGTH 64 - -enum { - MODE_INIT = 0x00, - MODE_AWAIT_FINGER_ON = 0x10, - MODE_AWAIT_FINGER_OFF = 0x12, - MODE_CAPTURE = 0x20, - MODE_SHUT_UP = 0x30, - MODE_READY = 0x80, -}; - -static int next_state(void); - -enum { - STATE_AWAIT_MODE_CHANGE_AWAIT_FINGER_ON = 1, - STATE_AWAIT_IRQ_FINGER_DETECTED, - STATE_AWAIT_MODE_CHANGE_CAPTURE, - STATE_AWAIT_IMAGE, - STATE_AWAIT_MODE_CHANGE_AWAIT_FINGER_OFF, - STATE_AWAIT_IRQ_FINGER_REMOVED, -}; - -static int state = 0; -static struct libusb_device_handle *devh = NULL; -static unsigned char imgbuf[0x1b340]; -static unsigned char irqbuf[INTR_LENGTH]; -static struct libusb_transfer *img_transfer = NULL; -static struct libusb_transfer *irq_transfer = NULL; -static int img_idx = 0; -static int do_exit = 0; - -static int find_dpfp_device(void) -{ - devh = libusb_open_device_with_vid_pid(NULL, 0x05ba, 0x000a); - return devh ? 0 : -EIO; -} - -static int print_f0_data(void) -{ - unsigned char data[0x10]; - int r; - unsigned int i; - - r = libusb_control_transfer(devh, CTRL_IN, USB_RQ, 0xf0, 0, data, - sizeof(data), 0); - if (r < 0) { - fprintf(stderr, "F0 error %d\n", r); - return r; - } - if ((unsigned int) r < sizeof(data)) { - fprintf(stderr, "short read (%d)\n", r); - return -1; - } - - printf("F0 data:"); - for (i = 0; i < sizeof(data); i++) - printf("%02x ", data[i]); - printf("\n"); - return 0; -} - -static int get_hwstat(unsigned char *status) -{ - int r; - - r = libusb_control_transfer(devh, CTRL_IN, USB_RQ, 0x07, 0, status, 1, 0); - if (r < 0) { - fprintf(stderr, "read hwstat error %d\n", r); - return r; - } - if ((unsigned int) r < 1) { - fprintf(stderr, "short read (%d)\n", r); - return -1; - } - - printf("hwstat reads %02x\n", *status); - return 0; -} - -static int set_hwstat(unsigned char data) -{ - int r; - - printf("set hwstat to %02x\n", data); - r = libusb_control_transfer(devh, CTRL_OUT, USB_RQ, 0x07, 0, &data, 1, 0); - if (r < 0) { - fprintf(stderr, "set hwstat error %d\n", r); - return r; - } - if ((unsigned int) r < 1) { - fprintf(stderr, "short write (%d)", r); - return -1; - } - - return 0; -} - -static int set_mode(unsigned char data) -{ - int r; - printf("set mode %02x\n", data); - - r = libusb_control_transfer(devh, CTRL_OUT, USB_RQ, 0x4e, 0, &data, 1, 0); - if (r < 0) { - fprintf(stderr, "set mode error %d\n", r); - return r; - } - if ((unsigned int) r < 1) { - fprintf(stderr, "short write (%d)", r); - return -1; - } - - return 0; -} - -static void LIBUSB_CALL cb_mode_changed(struct libusb_transfer *transfer) -{ - if (transfer->status != LIBUSB_TRANSFER_COMPLETED) { - fprintf(stderr, "mode change transfer not completed!\n"); - do_exit = 2; - } - - printf("async cb_mode_changed length=%d actual_length=%d\n", - transfer->length, transfer->actual_length); - if (next_state() < 0) - do_exit = 2; -} - -static int set_mode_async(unsigned char data) -{ - unsigned char *buf = malloc(LIBUSB_CONTROL_SETUP_SIZE + 1); - struct libusb_transfer *transfer; - - if (!buf) - return -ENOMEM; - - transfer = libusb_alloc_transfer(0); - if (!transfer) { - free(buf); - return -ENOMEM; - } - - printf("async set mode %02x\n", data); - libusb_fill_control_setup(buf, CTRL_OUT, USB_RQ, 0x4e, 0, 1); - buf[LIBUSB_CONTROL_SETUP_SIZE] = data; - libusb_fill_control_transfer(transfer, devh, buf, cb_mode_changed, NULL, - 1000); - - transfer->flags = LIBUSB_TRANSFER_SHORT_NOT_OK - | LIBUSB_TRANSFER_FREE_BUFFER | LIBUSB_TRANSFER_FREE_TRANSFER; - return libusb_submit_transfer(transfer); -} - -static int do_sync_intr(unsigned char *data) -{ - int r; - int transferred; - - r = libusb_interrupt_transfer(devh, EP_INTR, data, INTR_LENGTH, - &transferred, 1000); - if (r < 0) { - fprintf(stderr, "intr error %d\n", r); - return r; - } - if (transferred < INTR_LENGTH) { - fprintf(stderr, "short read (%d)\n", r); - return -1; - } - - printf("recv interrupt %04x\n", *((uint16_t *) data)); - return 0; -} - -static int sync_intr(unsigned char type) -{ - int r; - unsigned char data[INTR_LENGTH]; - - while (1) { - r = do_sync_intr(data); - if (r < 0) - return r; - if (data[0] == type) - return 0; - } -} - -static int save_to_file(unsigned char *data) -{ - FILE *fd; - char filename[64]; - - snprintf(filename, sizeof(filename), "finger%d.pgm", img_idx++); - fd = fopen(filename, "w"); - if (!fd) - return -1; - - fputs("P5 384 289 255 ", fd); - (void) fwrite(data + 64, 1, 384*289, fd); - fclose(fd); - printf("saved image to %s\n", filename); - return 0; -} - -static int next_state(void) -{ - int r = 0; - printf("old state: %d\n", state); - switch (state) { - case STATE_AWAIT_IRQ_FINGER_REMOVED: - state = STATE_AWAIT_MODE_CHANGE_AWAIT_FINGER_ON; - r = set_mode_async(MODE_AWAIT_FINGER_ON); - break; - case STATE_AWAIT_MODE_CHANGE_AWAIT_FINGER_ON: - state = STATE_AWAIT_IRQ_FINGER_DETECTED; - break; - case STATE_AWAIT_IRQ_FINGER_DETECTED: - state = STATE_AWAIT_MODE_CHANGE_CAPTURE; - r = set_mode_async(MODE_CAPTURE); - break; - case STATE_AWAIT_MODE_CHANGE_CAPTURE: - state = STATE_AWAIT_IMAGE; - break; - case STATE_AWAIT_IMAGE: - state = STATE_AWAIT_MODE_CHANGE_AWAIT_FINGER_OFF; - r = set_mode_async(MODE_AWAIT_FINGER_OFF); - break; - case STATE_AWAIT_MODE_CHANGE_AWAIT_FINGER_OFF: - state = STATE_AWAIT_IRQ_FINGER_REMOVED; - break; - default: - printf("unrecognised state %d\n", state); - } - if (r < 0) { - fprintf(stderr, "error detected changing state\n"); - return r; - } - - printf("new state: %d\n", state); - return 0; -} - -static void LIBUSB_CALL cb_irq(struct libusb_transfer *transfer) -{ - unsigned char irqtype = transfer->buffer[0]; - - if (transfer->status != LIBUSB_TRANSFER_COMPLETED) { - fprintf(stderr, "irq transfer status %d?\n", transfer->status); - do_exit = 2; - libusb_free_transfer(transfer); - irq_transfer = NULL; - return; - } - - printf("IRQ callback %02x\n", irqtype); - switch (state) { - case STATE_AWAIT_IRQ_FINGER_DETECTED: - if (irqtype == 0x01) { - if (next_state() < 0) { - do_exit = 2; - return; - } - } else { - printf("finger-on-sensor detected in wrong state!\n"); - } - break; - case STATE_AWAIT_IRQ_FINGER_REMOVED: - if (irqtype == 0x02) { - if (next_state() < 0) { - do_exit = 2; - return; - } - } else { - printf("finger-on-sensor detected in wrong state!\n"); - } - break; - } - if (libusb_submit_transfer(irq_transfer) < 0) - do_exit = 2; -} - -static void LIBUSB_CALL cb_img(struct libusb_transfer *transfer) -{ - if (transfer->status != LIBUSB_TRANSFER_COMPLETED) { - fprintf(stderr, "img transfer status %d?\n", transfer->status); - do_exit = 2; - libusb_free_transfer(transfer); - img_transfer = NULL; - return; - } - - printf("Image callback\n"); - save_to_file(imgbuf); - if (next_state() < 0) { - do_exit = 2; - return; - } - if (libusb_submit_transfer(img_transfer) < 0) - do_exit = 2; -} - -static int init_capture(void) -{ - int r; - - r = libusb_submit_transfer(irq_transfer); - if (r < 0) - return r; - - r = libusb_submit_transfer(img_transfer); - if (r < 0) { - libusb_cancel_transfer(irq_transfer); - while (irq_transfer) - if (libusb_handle_events(NULL) < 0) - break; - return r; - } - - /* start state machine */ - state = STATE_AWAIT_IRQ_FINGER_REMOVED; - return next_state(); -} - -static int do_init(void) -{ - unsigned char status; - int r; - - r = get_hwstat(&status); - if (r < 0) - return r; - - if (!(status & 0x80)) { - r = set_hwstat(status | 0x80); - if (r < 0) - return r; - r = get_hwstat(&status); - if (r < 0) - return r; - } - - status &= ~0x80; - r = set_hwstat(status); - if (r < 0) - return r; - - r = get_hwstat(&status); - if (r < 0) - return r; - - r = sync_intr(0x56); - if (r < 0) - return r; - - return 0; -} - -static int alloc_transfers(void) -{ - img_transfer = libusb_alloc_transfer(0); - if (!img_transfer) - return -ENOMEM; - - irq_transfer = libusb_alloc_transfer(0); - if (!irq_transfer) - return -ENOMEM; - - libusb_fill_bulk_transfer(img_transfer, devh, EP_DATA, imgbuf, - sizeof(imgbuf), cb_img, NULL, 0); - libusb_fill_interrupt_transfer(irq_transfer, devh, EP_INTR, irqbuf, - sizeof(irqbuf), cb_irq, NULL, 0); - - return 0; -} - -static void sighandler(int signum) -{ - do_exit = 1; -} - -int main(void) -{ - struct sigaction sigact; - int r = 1; - - r = libusb_init(NULL); - if (r < 0) { - fprintf(stderr, "failed to initialise libusb\n"); - exit(1); - } - - r = find_dpfp_device(); - if (r < 0) { - fprintf(stderr, "Could not find/open device\n"); - goto out; - } - - r = libusb_claim_interface(devh, 0); - if (r < 0) { - fprintf(stderr, "usb_claim_interface error %d\n", r); - goto out; - } - printf("claimed interface\n"); - - r = print_f0_data(); - if (r < 0) - goto out_release; - - r = do_init(); - if (r < 0) - goto out_deinit; - - /* async from here onwards */ - - r = alloc_transfers(); - if (r < 0) - goto out_deinit; - - r = init_capture(); - if (r < 0) - goto out_deinit; - - sigact.sa_handler = sighandler; - sigemptyset(&sigact.sa_mask); - sigact.sa_flags = 0; - sigaction(SIGINT, &sigact, NULL); - sigaction(SIGTERM, &sigact, NULL); - sigaction(SIGQUIT, &sigact, NULL); - - while (!do_exit) { - r = libusb_handle_events(NULL); - if (r < 0) - goto out_deinit; - } - - printf("shutting down...\n"); - - if (irq_transfer) { - r = libusb_cancel_transfer(irq_transfer); - if (r < 0) - goto out_deinit; - } - - if (img_transfer) { - r = libusb_cancel_transfer(img_transfer); - if (r < 0) - goto out_deinit; - } - - while (irq_transfer || img_transfer) - if (libusb_handle_events(NULL) < 0) - break; - - if (do_exit == 1) - r = 0; - else - r = 1; - -out_deinit: - libusb_free_transfer(img_transfer); - libusb_free_transfer(irq_transfer); - set_mode(0); - set_hwstat(0x80); -out_release: - libusb_release_interface(devh, 0); -out: - libusb_close(devh); - libusb_exit(NULL); - return r >= 0 ? r : -r; -} - diff --git a/compat/libusb-1.0/examples/dpfp_threaded.c b/compat/libusb-1.0/examples/dpfp_threaded.c deleted file mode 100644 index 93de9d759f..0000000000 --- a/compat/libusb-1.0/examples/dpfp_threaded.c +++ /dev/null @@ -1,545 +0,0 @@ -/* - * libusb example program to manipulate U.are.U 4000B fingerprint scanner. - * Copyright (C) 2007 Daniel Drake - * - * Basic image capture program only, does not consider the powerup quirks or - * the fact that image encryption may be enabled. Not expected to work - * flawlessly all of the time. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - */ - -#include -#include -#include -#include -#include -#include - -#include - -#define EP_INTR (1 | LIBUSB_ENDPOINT_IN) -#define EP_DATA (2 | LIBUSB_ENDPOINT_IN) -#define CTRL_IN (LIBUSB_REQUEST_TYPE_VENDOR | LIBUSB_ENDPOINT_IN) -#define CTRL_OUT (LIBUSB_REQUEST_TYPE_VENDOR | LIBUSB_ENDPOINT_OUT) -#define USB_RQ 0x04 -#define INTR_LENGTH 64 - -enum { - MODE_INIT = 0x00, - MODE_AWAIT_FINGER_ON = 0x10, - MODE_AWAIT_FINGER_OFF = 0x12, - MODE_CAPTURE = 0x20, - MODE_SHUT_UP = 0x30, - MODE_READY = 0x80, -}; - -static int next_state(void); - -enum { - STATE_AWAIT_MODE_CHANGE_AWAIT_FINGER_ON = 1, - STATE_AWAIT_IRQ_FINGER_DETECTED, - STATE_AWAIT_MODE_CHANGE_CAPTURE, - STATE_AWAIT_IMAGE, - STATE_AWAIT_MODE_CHANGE_AWAIT_FINGER_OFF, - STATE_AWAIT_IRQ_FINGER_REMOVED, -}; - -static int state = 0; -static struct libusb_device_handle *devh = NULL; -static unsigned char imgbuf[0x1b340]; -static unsigned char irqbuf[INTR_LENGTH]; -static struct libusb_transfer *img_transfer = NULL; -static struct libusb_transfer *irq_transfer = NULL; -static int img_idx = 0; -static int do_exit = 0; - -static pthread_t poll_thread; -static pthread_cond_t exit_cond = PTHREAD_COND_INITIALIZER; -static pthread_mutex_t exit_cond_lock = PTHREAD_MUTEX_INITIALIZER; - -static void request_exit(int code) -{ - do_exit = code; - pthread_cond_signal(&exit_cond); -} - -static void *poll_thread_main(void *arg) -{ - int r = 0; - printf("poll thread running\n"); - - while (!do_exit) { - struct timeval tv = { 1, 0 }; - r = libusb_handle_events_timeout(NULL, &tv); - if (r < 0) { - request_exit(2); - break; - } - } - - printf("poll thread shutting down\n"); - return NULL; -} - -static int find_dpfp_device(void) -{ - devh = libusb_open_device_with_vid_pid(NULL, 0x05ba, 0x000a); - return devh ? 0 : -EIO; -} - -static int print_f0_data(void) -{ - unsigned char data[0x10]; - int r; - unsigned int i; - - r = libusb_control_transfer(devh, CTRL_IN, USB_RQ, 0xf0, 0, data, - sizeof(data), 0); - if (r < 0) { - fprintf(stderr, "F0 error %d\n", r); - return r; - } - if ((unsigned int) r < sizeof(data)) { - fprintf(stderr, "short read (%d)\n", r); - return -1; - } - - printf("F0 data:"); - for (i = 0; i < sizeof(data); i++) - printf("%02x ", data[i]); - printf("\n"); - return 0; -} - -static int get_hwstat(unsigned char *status) -{ - int r; - - r = libusb_control_transfer(devh, CTRL_IN, USB_RQ, 0x07, 0, status, 1, 0); - if (r < 0) { - fprintf(stderr, "read hwstat error %d\n", r); - return r; - } - if ((unsigned int) r < 1) { - fprintf(stderr, "short read (%d)\n", r); - return -1; - } - - printf("hwstat reads %02x\n", *status); - return 0; -} - -static int set_hwstat(unsigned char data) -{ - int r; - - printf("set hwstat to %02x\n", data); - r = libusb_control_transfer(devh, CTRL_OUT, USB_RQ, 0x07, 0, &data, 1, 0); - if (r < 0) { - fprintf(stderr, "set hwstat error %d\n", r); - return r; - } - if ((unsigned int) r < 1) { - fprintf(stderr, "short write (%d)", r); - return -1; - } - - return 0; -} - -static int set_mode(unsigned char data) -{ - int r; - printf("set mode %02x\n", data); - - r = libusb_control_transfer(devh, CTRL_OUT, USB_RQ, 0x4e, 0, &data, 1, 0); - if (r < 0) { - fprintf(stderr, "set mode error %d\n", r); - return r; - } - if ((unsigned int) r < 1) { - fprintf(stderr, "short write (%d)", r); - return -1; - } - - return 0; -} - -static void LIBUSB_CALL cb_mode_changed(struct libusb_transfer *transfer) -{ - if (transfer->status != LIBUSB_TRANSFER_COMPLETED) { - fprintf(stderr, "mode change transfer not completed!\n"); - request_exit(2); - } - - printf("async cb_mode_changed length=%d actual_length=%d\n", - transfer->length, transfer->actual_length); - if (next_state() < 0) - request_exit(2); -} - -static int set_mode_async(unsigned char data) -{ - unsigned char *buf = malloc(LIBUSB_CONTROL_SETUP_SIZE + 1); - struct libusb_transfer *transfer; - - if (!buf) - return -ENOMEM; - - transfer = libusb_alloc_transfer(0); - if (!transfer) { - free(buf); - return -ENOMEM; - } - - printf("async set mode %02x\n", data); - libusb_fill_control_setup(buf, CTRL_OUT, USB_RQ, 0x4e, 0, 1); - buf[LIBUSB_CONTROL_SETUP_SIZE] = data; - libusb_fill_control_transfer(transfer, devh, buf, cb_mode_changed, NULL, - 1000); - - transfer->flags = LIBUSB_TRANSFER_SHORT_NOT_OK - | LIBUSB_TRANSFER_FREE_BUFFER | LIBUSB_TRANSFER_FREE_TRANSFER; - return libusb_submit_transfer(transfer); -} - -static int do_sync_intr(unsigned char *data) -{ - int r; - int transferred; - - r = libusb_interrupt_transfer(devh, EP_INTR, data, INTR_LENGTH, - &transferred, 1000); - if (r < 0) { - fprintf(stderr, "intr error %d\n", r); - return r; - } - if (transferred < INTR_LENGTH) { - fprintf(stderr, "short read (%d)\n", r); - return -1; - } - - printf("recv interrupt %04x\n", *((uint16_t *) data)); - return 0; -} - -static int sync_intr(unsigned char type) -{ - int r; - unsigned char data[INTR_LENGTH]; - - while (1) { - r = do_sync_intr(data); - if (r < 0) - return r; - if (data[0] == type) - return 0; - } -} - -static int save_to_file(unsigned char *data) -{ - FILE *fd; - char filename[64]; - - snprintf(filename, sizeof(filename), "finger%d.pgm", img_idx++); - fd = fopen(filename, "w"); - if (!fd) - return -1; - - fputs("P5 384 289 255 ", fd); - (void) fwrite(data + 64, 1, 384*289, fd); - fclose(fd); - printf("saved image to %s\n", filename); - return 0; -} - -static int next_state(void) -{ - int r = 0; - printf("old state: %d\n", state); - switch (state) { - case STATE_AWAIT_IRQ_FINGER_REMOVED: - state = STATE_AWAIT_MODE_CHANGE_AWAIT_FINGER_ON; - r = set_mode_async(MODE_AWAIT_FINGER_ON); - break; - case STATE_AWAIT_MODE_CHANGE_AWAIT_FINGER_ON: - state = STATE_AWAIT_IRQ_FINGER_DETECTED; - break; - case STATE_AWAIT_IRQ_FINGER_DETECTED: - state = STATE_AWAIT_MODE_CHANGE_CAPTURE; - r = set_mode_async(MODE_CAPTURE); - break; - case STATE_AWAIT_MODE_CHANGE_CAPTURE: - state = STATE_AWAIT_IMAGE; - break; - case STATE_AWAIT_IMAGE: - state = STATE_AWAIT_MODE_CHANGE_AWAIT_FINGER_OFF; - r = set_mode_async(MODE_AWAIT_FINGER_OFF); - break; - case STATE_AWAIT_MODE_CHANGE_AWAIT_FINGER_OFF: - state = STATE_AWAIT_IRQ_FINGER_REMOVED; - break; - default: - printf("unrecognised state %d\n", state); - } - if (r < 0) { - fprintf(stderr, "error detected changing state\n"); - return r; - } - - printf("new state: %d\n", state); - return 0; -} - -static void LIBUSB_CALL cb_irq(struct libusb_transfer *transfer) -{ - unsigned char irqtype = transfer->buffer[0]; - - if (transfer->status != LIBUSB_TRANSFER_COMPLETED) { - fprintf(stderr, "irq transfer status %d?\n", transfer->status); - irq_transfer = NULL; - request_exit(2); - return; - } - - printf("IRQ callback %02x\n", irqtype); - switch (state) { - case STATE_AWAIT_IRQ_FINGER_DETECTED: - if (irqtype == 0x01) { - if (next_state() < 0) { - request_exit(2); - return; - } - } else { - printf("finger-on-sensor detected in wrong state!\n"); - } - break; - case STATE_AWAIT_IRQ_FINGER_REMOVED: - if (irqtype == 0x02) { - if (next_state() < 0) { - request_exit(2); - return; - } - } else { - printf("finger-on-sensor detected in wrong state!\n"); - } - break; - } - if (libusb_submit_transfer(irq_transfer) < 0) - request_exit(2); -} - -static void LIBUSB_CALL cb_img(struct libusb_transfer *transfer) -{ - if (transfer->status != LIBUSB_TRANSFER_COMPLETED) { - fprintf(stderr, "img transfer status %d?\n", transfer->status); - img_transfer = NULL; - request_exit(2); - return; - } - - printf("Image callback\n"); - save_to_file(imgbuf); - if (next_state() < 0) { - request_exit(2); - return; - } - if (libusb_submit_transfer(img_transfer) < 0) - request_exit(2); -} - -static int init_capture(void) -{ - int r; - - r = libusb_submit_transfer(irq_transfer); - if (r < 0) - return r; - - r = libusb_submit_transfer(img_transfer); - if (r < 0) { - libusb_cancel_transfer(irq_transfer); - while (irq_transfer) - if (libusb_handle_events(NULL) < 0) - break; - return r; - } - - /* start state machine */ - state = STATE_AWAIT_IRQ_FINGER_REMOVED; - return next_state(); -} - -static int do_init(void) -{ - unsigned char status; - int r; - - r = get_hwstat(&status); - if (r < 0) - return r; - - if (!(status & 0x80)) { - r = set_hwstat(status | 0x80); - if (r < 0) - return r; - r = get_hwstat(&status); - if (r < 0) - return r; - } - - status &= ~0x80; - r = set_hwstat(status); - if (r < 0) - return r; - - r = get_hwstat(&status); - if (r < 0) - return r; - - r = sync_intr(0x56); - if (r < 0) - return r; - - return 0; -} - -static int alloc_transfers(void) -{ - img_transfer = libusb_alloc_transfer(0); - if (!img_transfer) - return -ENOMEM; - - irq_transfer = libusb_alloc_transfer(0); - if (!irq_transfer) - return -ENOMEM; - - libusb_fill_bulk_transfer(img_transfer, devh, EP_DATA, imgbuf, - sizeof(imgbuf), cb_img, NULL, 0); - libusb_fill_interrupt_transfer(irq_transfer, devh, EP_INTR, irqbuf, - sizeof(irqbuf), cb_irq, NULL, 0); - - return 0; -} - -static void sighandler(int signum) -{ - request_exit(1); -} - -int main(void) -{ - struct sigaction sigact; - int r = 1; - - r = libusb_init(NULL); - if (r < 0) { - fprintf(stderr, "failed to initialise libusb\n"); - exit(1); - } - - r = find_dpfp_device(); - if (r < 0) { - fprintf(stderr, "Could not find/open device\n"); - goto out; - } - - r = libusb_claim_interface(devh, 0); - if (r < 0) { - fprintf(stderr, "usb_claim_interface error %d %s\n", r, strerror(-r)); - goto out; - } - printf("claimed interface\n"); - - r = print_f0_data(); - if (r < 0) - goto out_release; - - r = do_init(); - if (r < 0) - goto out_deinit; - - /* async from here onwards */ - - sigact.sa_handler = sighandler; - sigemptyset(&sigact.sa_mask); - sigact.sa_flags = 0; - sigaction(SIGINT, &sigact, NULL); - sigaction(SIGTERM, &sigact, NULL); - sigaction(SIGQUIT, &sigact, NULL); - - r = pthread_create(&poll_thread, NULL, poll_thread_main, NULL); - if (r) - goto out_deinit; - - r = alloc_transfers(); - if (r < 0) { - request_exit(1); - pthread_join(poll_thread, NULL); - goto out_deinit; - } - - r = init_capture(); - if (r < 0) { - request_exit(1); - pthread_join(poll_thread, NULL); - goto out_deinit; - } - - while (!do_exit) { - pthread_mutex_lock(&exit_cond_lock); - pthread_cond_wait(&exit_cond, &exit_cond_lock); - pthread_mutex_unlock(&exit_cond_lock); - } - - printf("shutting down...\n"); - pthread_join(poll_thread, NULL); - - r = libusb_cancel_transfer(irq_transfer); - if (r < 0) { - request_exit(1); - goto out_deinit; - } - - r = libusb_cancel_transfer(img_transfer); - if (r < 0) { - request_exit(1); - goto out_deinit; - } - - while (img_transfer || irq_transfer) - if (libusb_handle_events(NULL) < 0) - break; - - if (do_exit == 1) - r = 0; - else - r = 1; - -out_deinit: - libusb_free_transfer(img_transfer); - libusb_free_transfer(irq_transfer); - set_mode(0); - set_hwstat(0x80); -out_release: - libusb_release_interface(devh, 0); -out: - libusb_close(devh); - libusb_exit(NULL); - return r >= 0 ? r : -r; -} - diff --git a/compat/libusb-1.0/examples/hotplugtest.c b/compat/libusb-1.0/examples/hotplugtest.c deleted file mode 100644 index c804aea1fd..0000000000 --- a/compat/libusb-1.0/examples/hotplugtest.c +++ /dev/null @@ -1,95 +0,0 @@ -/* - * libusb example program for hotplug API - * Copyright (C) 2012-2013 Nathan Hjelm - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - */ - -#include -#include -#include -#include - -#include - -int done = 0; -libusb_device_handle *handle; - -static int hotplug_callback (libusb_context *ctx, libusb_device *dev, libusb_hotplug_event event, void *user_data) { - struct libusb_device_descriptor desc; - int rc; - - rc = libusb_get_device_descriptor(dev, &desc); - if (LIBUSB_SUCCESS != rc) { - fprintf (stderr, "Error getting device descriptor\n"); - } - - printf ("Device attach: %04x:%04x\n", desc.idVendor, desc.idProduct); - - libusb_open (dev, &handle); - - done++; - - return 0; -} - -static int hotplug_callback_detach (libusb_context *ctx, libusb_device *dev, libusb_hotplug_event event, void *user_data) { - printf ("Device detached\n"); - - libusb_close (handle); - - done++; - return 0; -} - -int main (int argc, char *argv[]) { - libusb_hotplug_callback_handle hp[2]; - int product_id, vendor_id, class_id; - int rc; - - vendor_id = (argc > 1) ? strtol (argv[1], NULL, 0) : 0x045a; - product_id = (argc > 2) ? strtol (argv[2], NULL, 0) : 0x5005; - class_id = (argc > 3) ? strtol (argv[3], NULL, 0) : LIBUSB_HOTPLUG_MATCH_ANY; - - libusb_init (NULL); - - if (!libusb_has_capability (LIBUSB_CAP_HAS_HOTPLUG)) { - printf ("Hotplug not supported by this build of libusb\n"); - libusb_exit (NULL); - return EXIT_FAILURE; - } - - rc = libusb_hotplug_register_callback (NULL, LIBUSB_HOTPLUG_EVENT_DEVICE_ARRIVED, 0, vendor_id, - product_id, class_id, hotplug_callback, NULL, &hp[0]); - if (LIBUSB_SUCCESS != rc) { - fprintf (stderr, "Error registering callback 0\n"); - libusb_exit (NULL); - return EXIT_FAILURE; - } - - rc = libusb_hotplug_register_callback (NULL, LIBUSB_HOTPLUG_EVENT_DEVICE_LEFT, 0, vendor_id, - product_id,class_id, hotplug_callback_detach, NULL, &hp[1]); - if (LIBUSB_SUCCESS != rc) { - fprintf (stderr, "Error registering callback 1\n"); - libusb_exit (NULL); - return EXIT_FAILURE; - } - - while (done < 2) { - libusb_handle_events (NULL); - } - - libusb_exit (NULL); -} diff --git a/compat/libusb-1.0/examples/listdevs.c b/compat/libusb-1.0/examples/listdevs.c deleted file mode 100644 index 6ab891725a..0000000000 --- a/compat/libusb-1.0/examples/listdevs.c +++ /dev/null @@ -1,64 +0,0 @@ -/* - * libusb example program to list devices on the bus - * Copyright (C) 2007 Daniel Drake - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - */ - -#include -#include - -#include - -static void print_devs(libusb_device **devs) -{ - libusb_device *dev; - int i = 0; - - while ((dev = devs[i++]) != NULL) { - struct libusb_device_descriptor desc; - int r = libusb_get_device_descriptor(dev, &desc); - if (r < 0) { - fprintf(stderr, "failed to get device descriptor"); - return; - } - - printf("%04x:%04x (bus %d, device %d)\n", - desc.idVendor, desc.idProduct, - libusb_get_bus_number(dev), libusb_get_device_address(dev)); - } -} - -int main(void) -{ - libusb_device **devs; - int r; - ssize_t cnt; - - r = libusb_init(NULL); - if (r < 0) - return r; - - cnt = libusb_get_device_list(NULL, &devs); - if (cnt < 0) - return (int) cnt; - - print_devs(devs); - libusb_free_device_list(devs, 1); - - libusb_exit(NULL); - return 0; -} - diff --git a/compat/libusb-1.0/examples/sam3u_benchmark.c b/compat/libusb-1.0/examples/sam3u_benchmark.c deleted file mode 100644 index 6a1f5eeb66..0000000000 --- a/compat/libusb-1.0/examples/sam3u_benchmark.c +++ /dev/null @@ -1,193 +0,0 @@ -/* - * libusb example program to measure Atmel SAM3U isochronous performance - * Copyright (C) 2012 Harald Welte - * - * Copied with the author's permission under LGPL-2.1 from - * http://git.gnumonks.org/cgi-bin/gitweb.cgi?p=sam3u-tests.git;a=blob;f=usb-benchmark-project/host/benchmark.c;h=74959f7ee88f1597286cd435f312a8ff52c56b7e - * - * An Atmel SAM3U test firmware is also available in the above repository. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - */ - -#include -#include -#include -#include -#include - -#include - - -#define EP_DATA_IN 0x82 -#define EP_ISO_IN 0x86 - -static int do_exit = 0; -static struct libusb_device_handle *devh = NULL; - -static unsigned long num_bytes = 0, num_xfer = 0; -static struct timeval tv_start; - -static void cb_xfr(struct libusb_transfer *xfr) -{ - unsigned int i; - - if (xfr->status != LIBUSB_TRANSFER_COMPLETED) { - fprintf(stderr, "transfer status %d\n", xfr->status); - libusb_free_transfer(xfr); - exit(3); - } - - if (xfr->type == LIBUSB_TRANSFER_TYPE_ISOCHRONOUS) { - for (i = 0; i < xfr->num_iso_packets; i++) { - struct libusb_iso_packet_descriptor *pack = &xfr->iso_packet_desc[i]; - - if (pack->status != LIBUSB_TRANSFER_COMPLETED) { - fprintf(stderr, "Error: pack %u status %d\n", i, pack->status); - exit(5); - } - - printf("pack%u length:%u, actual_length:%u\n", i, pack->length, pack->actual_length); - } - } - - printf("length:%u, actual_length:%u\n", xfr->length, xfr->actual_length); - for (i = 0; i < xfr->actual_length; i++) { - printf("%02x", xfr->buffer[i]); - if (i % 16) - printf("\n"); - else if (i % 8) - printf(" "); - else - printf(" "); - } - num_bytes += xfr->actual_length; - num_xfer++; - - if (libusb_submit_transfer(xfr) < 0) { - fprintf(stderr, "error re-submitting URB\n"); - exit(1); - } -} - -static int benchmark_in(uint8_t ep) -{ - static uint8_t buf[2048]; - static struct libusb_transfer *xfr; - int num_iso_pack = 0; - - if (ep == EP_ISO_IN) - num_iso_pack = 16; - - xfr = libusb_alloc_transfer(num_iso_pack); - if (!xfr) - return -ENOMEM; - - if (ep == EP_ISO_IN) { - libusb_fill_iso_transfer(xfr, devh, ep, buf, - sizeof(buf), num_iso_pack, cb_xfr, NULL, 0); - libusb_set_iso_packet_lengths(xfr, sizeof(buf)/num_iso_pack); - } else - libusb_fill_bulk_transfer(xfr, devh, ep, buf, - sizeof(buf), cb_xfr, NULL, 0); - - gettimeofday(&tv_start, NULL); - - /* NOTE: To reach maximum possible performance the program must - * submit *multiple* transfers here, not just one. - * - * When only one transfer is submitted there is a gap in the bus - * schedule from when the transfer completes until a new transfer - * is submitted by the callback. This causes some jitter for - * isochronous transfers and loss of throughput for bulk transfers. - * - * This is avoided by queueing multiple transfers in advance, so - * that the host controller is always kept busy, and will schedule - * more transfers on the bus while the callback is running for - * transfers which have completed on the bus. - */ - - return libusb_submit_transfer(xfr); -} - -static void measure(void) -{ - struct timeval tv_stop; - unsigned int diff_msec; - - gettimeofday(&tv_stop, NULL); - - diff_msec = (tv_stop.tv_sec - tv_start.tv_sec)*1000; - diff_msec += (tv_stop.tv_usec - tv_start.tv_usec)/1000; - - printf("%lu transfers (total %lu bytes) in %u miliseconds => %lu bytes/sec\n", - num_xfer, num_bytes, diff_msec, (num_bytes*1000)/diff_msec); -} - -static void sig_hdlr(int signum) -{ - switch (signum) { - case SIGINT: - measure(); - do_exit = 1; - break; - } -} - -int main(int argc, char **argv) -{ - int rc; - struct sigaction sigact; - - sigact.sa_handler = sig_hdlr; - sigemptyset(&sigact.sa_mask); - sigact.sa_flags = 0; - sigaction(SIGINT, &sigact, NULL); - - rc = libusb_init(NULL); - if (rc < 0) { - fprintf(stderr, "Error initializing libusb: %s\n", libusb_error_name(rc)); - exit(1); - } - - devh = libusb_open_device_with_vid_pid(NULL, 0x16c0, 0x0763); - if (!devh) { - fprintf(stderr, "Error finding USB device\n"); - goto out; - } - - rc = libusb_claim_interface(devh, 2); - if (rc < 0) { - fprintf(stderr, "Error claiming interface: %s\n", libusb_error_name(rc)); - goto out; - } - - benchmark_in(EP_ISO_IN); - - while (!do_exit) { - rc = libusb_handle_events(NULL); - if (rc != LIBUSB_SUCCESS) - break; - } - - /* Measurement has already been done by the signal handler. */ - - libusb_release_interface(devh, 0); -out: - if (devh) - libusb_close(devh); - libusb_exit(NULL); - return rc; -} diff --git a/compat/libusb-1.0/examples/testlibusb1.c b/compat/libusb-1.0/examples/testlibusb1.c deleted file mode 100644 index ac95771d39..0000000000 --- a/compat/libusb-1.0/examples/testlibusb1.c +++ /dev/null @@ -1,256 +0,0 @@ -/* - * Test suite program based of libusb-0.1-compat testlibusb - * Copyright (c) 2013 Nathan Hjelm - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - */ - -#include -#include -#include - -int verbose = 0; - -static void print_endpoint_comp(const struct libusb_ss_endpoint_companion_descriptor *ep_comp) -{ - printf(" USB 3.0 Endpoint Companion:\n"); - printf(" bMaxBurst: %d\n", ep_comp->bMaxBurst); - printf(" bmAttributes: 0x%02x\n", ep_comp->bmAttributes); - printf(" wBytesPerInterval: %d\n", ep_comp->wBytesPerInterval); -} - -static void print_endpoint(const struct libusb_endpoint_descriptor *endpoint) -{ - int i, ret; - - printf(" Endpoint:\n"); - printf(" bEndpointAddress: %02xh\n", endpoint->bEndpointAddress); - printf(" bmAttributes: %02xh\n", endpoint->bmAttributes); - printf(" wMaxPacketSize: %d\n", endpoint->wMaxPacketSize); - printf(" bInterval: %d\n", endpoint->bInterval); - printf(" bRefresh: %d\n", endpoint->bRefresh); - printf(" bSynchAddress: %d\n", endpoint->bSynchAddress); - - for (i = 0 ; i < endpoint->extra_length ; ) { - if (LIBUSB_DT_SS_ENDPOINT_COMPANION == endpoint->extra[i+1]) { - struct libusb_ss_endpoint_companion_descriptor *ep_comp; - - ret = libusb_parse_ss_endpoint_comp(endpoint->extra+i, endpoint->extra[0], &ep_comp); - if (LIBUSB_SUCCESS != ret) { - continue; - } - - print_endpoint_comp(ep_comp); - - libusb_free_ss_endpoint_comp(ep_comp); - } - - i += endpoint->extra[i]; - } -} - -static void print_altsetting(const struct libusb_interface_descriptor *interface) -{ - int i; - - printf(" Interface:\n"); - printf(" bInterfaceNumber: %d\n", interface->bInterfaceNumber); - printf(" bAlternateSetting: %d\n", interface->bAlternateSetting); - printf(" bNumEndpoints: %d\n", interface->bNumEndpoints); - printf(" bInterfaceClass: %d\n", interface->bInterfaceClass); - printf(" bInterfaceSubClass: %d\n", interface->bInterfaceSubClass); - printf(" bInterfaceProtocol: %d\n", interface->bInterfaceProtocol); - printf(" iInterface: %d\n", interface->iInterface); - - for (i = 0; i < interface->bNumEndpoints; i++) - print_endpoint(&interface->endpoint[i]); -} - -static void print_2_0_ext_cap(struct libusb_usb_2_0_device_capability_descriptor *usb_2_0_ext_cap) -{ - printf(" USB 2.0 Extension Capabilities:\n"); - printf(" bDevCapabilityType: %d\n", usb_2_0_ext_cap->bDevCapabilityType); - printf(" bmAttributes: 0x%x\n", usb_2_0_ext_cap->bmAttributes); -} - -static void print_ss_usb_cap(struct libusb_ss_usb_device_capability_descriptor *ss_usb_cap) -{ - printf(" USB 3.0 Capabilities:\n"); - printf(" bDevCapabilityType: %d\n", ss_usb_cap->bDevCapabilityType); - printf(" bmAttributes: 0x%x\n", ss_usb_cap->bmAttributes); - printf(" wSpeedSupported: 0x%x\n", ss_usb_cap->wSpeedSupported); - printf(" bFunctionalitySupport: %d\n", ss_usb_cap->bFunctionalitySupport); - printf(" bU1devExitLat: %d\n", ss_usb_cap->bU1DevExitLat); - printf(" bU2devExitLat: %d\n", ss_usb_cap->bU2DevExitLat); -} - -static void print_bos(libusb_device_handle *handle) -{ - unsigned char buffer[128]; - struct libusb_bos_descriptor *bos; - int ret; - - ret = libusb_get_descriptor(handle, LIBUSB_DT_BOS, 0, buffer, 128); - if (0 > ret) { - return; - } - - ret = libusb_parse_bos_descriptor(buffer, 128, &bos); - if (0 > ret) { - return; - } - - printf(" Binary Object Store (BOS):\n"); - printf(" wTotalLength: %d\n", bos->wTotalLength); - printf(" bNumDeviceCaps: %d\n", bos->bNumDeviceCaps); - if (bos->usb_2_0_ext_cap) { - print_2_0_ext_cap(bos->usb_2_0_ext_cap); - } - - if (bos->ss_usb_cap) { - print_ss_usb_cap(bos->ss_usb_cap); - } -} - -static void print_interface(const struct libusb_interface *interface) -{ - int i; - - for (i = 0; i < interface->num_altsetting; i++) - print_altsetting(&interface->altsetting[i]); -} - -static void print_configuration(struct libusb_config_descriptor *config) -{ - int i; - - printf(" Configuration:\n"); - printf(" wTotalLength: %d\n", config->wTotalLength); - printf(" bNumInterfaces: %d\n", config->bNumInterfaces); - printf(" bConfigurationValue: %d\n", config->bConfigurationValue); - printf(" iConfiguration: %d\n", config->iConfiguration); - printf(" bmAttributes: %02xh\n", config->bmAttributes); - printf(" MaxPower: %d\n", config->MaxPower); - - for (i = 0; i < config->bNumInterfaces; i++) - print_interface(&config->interface[i]); -} - -static int print_device(libusb_device *dev, int level) -{ - struct libusb_device_descriptor desc; - libusb_device_handle *handle = NULL; - char description[256]; - char string[256]; - int ret, i; - - ret = libusb_get_device_descriptor(dev, &desc); - if (ret < 0) { - fprintf(stderr, "failed to get device descriptor"); - return -1; - } - - ret = libusb_open(dev, &handle); - if (LIBUSB_SUCCESS == ret) { - if (desc.iManufacturer) { - ret = libusb_get_string_descriptor_ascii(handle, desc.iManufacturer, string, sizeof(string)); - if (ret > 0) - snprintf(description, sizeof(description), "%s - ", string); - else - snprintf(description, sizeof(description), "%04X - ", - desc.idVendor); - } else - snprintf(description, sizeof(description), "%04X - ", - desc.idVendor); - - if (desc.iProduct) { - ret = libusb_get_string_descriptor_ascii(handle, desc.iProduct, string, sizeof(string)); - if (ret > 0) - snprintf(description + strlen(description), sizeof(description) - - strlen(description), "%s", string); - else - snprintf(description + strlen(description), sizeof(description) - - strlen(description), "%04X", desc.idProduct); - } else - snprintf(description + strlen(description), sizeof(description) - - strlen(description), "%04X", desc.idProduct); - } else { - snprintf(description, sizeof(description), "%04X - %04X", - desc.idVendor, desc.idProduct); - } - - printf("%.*sDev (bus %d, device %d): %s\n", level * 2, " ", - libusb_get_bus_number(dev), libusb_get_device_address(dev), description); - - if (handle && verbose) { - if (desc.iSerialNumber) { - ret = libusb_get_string_descriptor_ascii(handle, desc.iSerialNumber, string, sizeof(string)); - if (ret > 0) - printf("%.*s - Serial Number: %s\n", level * 2, - " ", string); - } - } - - if (verbose) { - for (i = 0; i < desc.bNumConfigurations; i++) { - struct libusb_config_descriptor *config; - ret = libusb_get_config_descriptor(dev, i, &config); - if (LIBUSB_SUCCESS != ret) { - printf(" Couldn't retrieve descriptors\n"); - continue; - } - - print_configuration(config); - - libusb_free_config_descriptor(config); - } - - if (handle && desc.bcdUSB >= 0x0201) { - print_bos(handle); - } - } - - if (handle) - libusb_close(handle); - - return 0; -} - -int main(int argc, char *argv[]) -{ - libusb_device **devs; - ssize_t cnt; - int r, i; - - if (argc > 1 && !strcmp(argv[1], "-v")) - verbose = 1; - - r = libusb_init(NULL); - if (r < 0) - return r; - - cnt = libusb_get_device_list(NULL, &devs); - if (cnt < 0) - return (int) cnt; - - for (i = 0 ; devs[i] ; ++i) { - print_device(devs[i], 0); - } - - libusb_free_device_list(devs, 1); - - libusb_exit(NULL); - return 0; -} diff --git a/compat/libusb-1.0/libusb/io.c b/compat/libusb-1.0/libusb/io.c index f488478ba1..55b17f12d6 100644 --- a/compat/libusb-1.0/libusb/io.c +++ b/compat/libusb-1.0/libusb/io.c @@ -1478,7 +1478,10 @@ int usbi_handle_transfer_completion(struct usbi_transfer *itransfer, * the shortest timeout. */ usbi_mutex_lock(&ctx->flying_transfers_lock); - list_del(&itransfer->list); + /* FIXME: Sanity check for some race where this entry has already been + * removed! */ + if ((&itransfer->list)->next) + list_del(&itransfer->list); if (usbi_using_timerfd(ctx)) { r = arm_timerfd_for_next_timeout(ctx); if (0 == r) diff --git a/compat/libusb-1.0/libusb/os/linux_usbfs.c b/compat/libusb-1.0/libusb/os/linux_usbfs.c index 57603e600c..bd847c22cb 100644 --- a/compat/libusb-1.0/libusb/os/linux_usbfs.c +++ b/compat/libusb-1.0/libusb/os/linux_usbfs.c @@ -1144,6 +1144,7 @@ void linux_hotplug_enumerate(uint8_t busnum, uint8_t devaddr, const char *sys_na { struct libusb_context *ctx; + usbi_mutex_lock(&active_contexts_lock); list_for_each_entry(ctx, &active_contexts_list, list, struct libusb_context) { if (usbi_get_device_by_session_id(ctx, busnum << 8 | devaddr)) { /* device already exists in the context */ @@ -1153,14 +1154,16 @@ void linux_hotplug_enumerate(uint8_t busnum, uint8_t devaddr, const char *sys_na linux_enumerate_device(ctx, busnum, devaddr, sys_name); } + usbi_mutex_unlock(&active_contexts_lock); } void linux_hotplug_disconnected(uint8_t busnum, uint8_t devaddr, const char *sys_name) { - struct libusb_context *ctx; + struct libusb_context *ctx, *tmp; struct libusb_device *dev; - list_for_each_entry(ctx, &active_contexts_list, list, struct libusb_context) { + usbi_mutex_lock(&active_contexts_lock); + list_for_each_entry_safe(ctx, tmp, &active_contexts_list, list, struct libusb_context) { dev = usbi_get_device_by_session_id (ctx, busnum << 8 | devaddr); if (NULL != dev) { usbi_disconnect_device (dev); @@ -1168,6 +1171,7 @@ void linux_hotplug_disconnected(uint8_t busnum, uint8_t devaddr, const char *sys usbi_err(ctx, "device not found for session %x %s", busnum << 8 | devaddr, sys_name); } } + usbi_mutex_unlock(&active_contexts_lock); } #if !defined(USE_UDEV) diff --git a/compat/libusb-1.0/libusb/os/windows_usb.c b/compat/libusb-1.0/libusb/os/windows_usb.c index afc4dfcec5..873905cfdc 100644 --- a/compat/libusb-1.0/libusb/os/windows_usb.c +++ b/compat/libusb-1.0/libusb/os/windows_usb.c @@ -1822,9 +1822,6 @@ static int windows_submit_transfer(struct usbi_transfer *itransfer) return submit_control_transfer(itransfer); case LIBUSB_TRANSFER_TYPE_BULK: case LIBUSB_TRANSFER_TYPE_INTERRUPT: - if (IS_XFEROUT(transfer) && - transfer->flags & LIBUSB_TRANSFER_ADD_ZERO_PACKET) - return LIBUSB_ERROR_NOT_SUPPORTED; return submit_bulk_transfer(itransfer); case LIBUSB_TRANSFER_TYPE_ISOCHRONOUS: return submit_iso_transfer(itransfer); @@ -2189,7 +2186,7 @@ static int unsupported_copy_transfer_data(struct usbi_transfer *itransfer, uint3 } // These names must be uppercase -const char* hub_driver_names[] = {"USBHUB", "USBHUB3", "NUSB3HUB", "FLXHCIH", "TIHUB3", "ETRONHUB3", "VIAHUB3", "ASMTHUB3"}; +const char* hub_driver_names[] = {"USBHUB", "USBHUB3", "USB3HUB", "NUSB3HUB", "RUSB3HUB", "FLXHCIH", "TIHUB3", "ETRONHUB3", "VIAHUB3", "ASMTHUB3", "IUSB3HUB", "VUSB3HUB", "AMDHUB30"}; const char* composite_driver_names[] = {"USBCCGP"}; const char* winusb_driver_names[] = {"WINUSB"}; const struct windows_usb_api_backend usb_api_backend[USB_API_MAX] = { @@ -2391,20 +2388,8 @@ static int winusb_configure_endpoints(struct libusb_device_handle *dev_handle, i PIPE_TRANSFER_TIMEOUT, sizeof(ULONG), &timeout)) { usbi_dbg("failed to set PIPE_TRANSFER_TIMEOUT for control endpoint %02X", endpoint_address); } - if (i == -1) continue; // Other policies don't apply to control endpoint - policy = false; - if (!WinUsb_SetPipePolicy(winusb_handle, endpoint_address, - SHORT_PACKET_TERMINATE, sizeof(UCHAR), &policy)) { - usbi_dbg("failed to disable SHORT_PACKET_TERMINATE for endpoint %02X", endpoint_address); - } - if (!WinUsb_SetPipePolicy(winusb_handle, endpoint_address, - IGNORE_SHORT_PACKETS, sizeof(UCHAR), &policy)) { - usbi_dbg("failed to disable IGNORE_SHORT_PACKETS for endpoint %02X", endpoint_address); - } - if (!WinUsb_SetPipePolicy(winusb_handle, endpoint_address, - ALLOW_PARTIAL_READS, sizeof(UCHAR), &policy)) { - usbi_dbg("failed to disable ALLOW_PARTIAL_READS for endpoint %02X", endpoint_address); - } + if (i == -1) + continue; // Other policies don't apply to control endpoint policy = true; if (!WinUsb_SetPipePolicy(winusb_handle, endpoint_address, AUTO_CLEAR_STALL, sizeof(UCHAR), &policy)) { @@ -2649,6 +2634,8 @@ static int winusb_submit_bulk_transfer(struct usbi_transfer *itransfer) bool ret; int current_interface; struct winfd wfd; + ULONG ppolicy = sizeof(UCHAR); + UCHAR policy; CHECK_WINUSB_AVAILABLE; @@ -2671,9 +2658,20 @@ static int winusb_submit_bulk_transfer(struct usbi_transfer *itransfer) } if (IS_XFERIN(transfer)) { - usbi_dbg("reading %d bytes", transfer->length); + WinUsb_GetPipePolicy(wfd.handle, transfer->endpoint, AUTO_CLEAR_STALL, &ppolicy, &policy); + if (!policy) { + policy = TRUE; + WinUsb_SetPipePolicy(wfd.handle, transfer->endpoint, AUTO_CLEAR_STALL, ppolicy, &policy); + } ret = WinUsb_ReadPipe(wfd.handle, transfer->endpoint, transfer->buffer, transfer->length, NULL, wfd.overlapped); } else { + if (transfer->flags & LIBUSB_TRANSFER_ADD_ZERO_PACKET) { + WinUsb_GetPipePolicy(wfd.handle, transfer->endpoint, SHORT_PACKET_TERMINATE, &ppolicy, &policy); + if (!policy) { + policy = TRUE; + WinUsb_SetPipePolicy(wfd.handle, transfer->endpoint, SHORT_PACKET_TERMINATE, ppolicy, &policy); + } + } usbi_dbg("writing %d bytes", transfer->length); ret = WinUsb_WritePipe(wfd.handle, transfer->endpoint, transfer->buffer, transfer->length, NULL, wfd.overlapped); } diff --git a/configure.ac b/configure.ac index 3be4c2ae97..eda7ad0590 100644 --- a/configure.ac +++ b/configure.ac @@ -1,8 +1,8 @@ ##--##--##--##--##--##--##--##--##--##--##--##--##--##--##--##--## ##--##--##--##--##--##--##--##--##--##--##--##--##--##--##--##--## -m4_define([v_maj], [3]) -m4_define([v_min], [4]) -m4_define([v_mic], [2]) +m4_define([v_maj], [4]) +m4_define([v_min], [9]) +m4_define([v_mic], [0]) ##--##--##--##--##--##--##--##--##--##--##--##--##--##--##--##--## m4_define([v_ver], [v_maj.v_min.v_mic]) m4_define([lt_rev], m4_eval(v_maj + v_min)) @@ -19,7 +19,7 @@ AC_CONFIG_MACRO_DIR([m4]) AC_CONFIG_SRCDIR([cgminer.c]) AC_CONFIG_HEADERS([config.h]) -AM_INIT_AUTOMAKE([foreign]) +AM_INIT_AUTOMAKE([foreign subdir-objects]) m4_ifdef([AM_SILENT_RULES], [AM_SILENT_RULES([yes])]) AC_USE_SYSTEM_EXTENSIONS @@ -63,7 +63,6 @@ AC_FUNC_ALLOCA have_win32=false PTHREAD_FLAGS="-lpthread" DLOPEN_FLAGS="-ldl" -OPENCL_LIBS="-lOpenCL" WS2_LIBS="" MM_LIBS="" MATH_LIBS="-lm" @@ -95,128 +94,114 @@ case $target in AC_DEFINE([_WIN32_WINNT], [0x0501], "WinNT version for XP+ support") ;; powerpc-*-darwin*) + have_darwin=true CFLAGS="$CFLAGS -faltivec" - OPENCL_LIBS="" PTHREAD_FLAGS="" RT_LIBS="" ;; *-*-darwin*) - OPENCL_LIBS="-framework OpenCL" + have_darwin=true PTHREAD_FLAGS="" RT_LIBS="" ;; esac +has_winpthread=false +if test "x$have_win32" = xtrue; then + has_winpthread=true + AC_CHECK_LIB(winpthread, nanosleep, , has_winpthread=false) + PTHREAD_LIBS=-lwinpthread +fi -if test "x$have_win32" != xtrue; then - if test "x$have_x86_64" = xtrue; then - ARCH_DIR=x86_64 - else - ARCH_DIR=x86 - fi +if test "x$has_winpthread" != xtrue; then + AC_CHECK_LIB(pthread, pthread_create, , + AC_MSG_ERROR([Could not find pthread library - please install libpthread])) + PTHREAD_LIBS=-lpthread +fi - if test "x$ATISTREAMSDKROOT" != x; then - OPENCL_FLAGS="-I$ATISTREAMSDKROOT/include $OPENCL_FLAGS" - OPENCL_LIBS="-L$ATISTREAMSDKROOT/lib/$ARCH_DIR $OPENCL_LIBS" - fi +# Drivers that are designed to be run on dedicated hardware should set standalone to yes +# All drivers should prepend an x to the drivercount - if test "x$AMDAPPSDKROOT" != x; then - OPENCL_FLAGS="-I$AMDAPPSDKROOT/include $OPENCL_FLAGS" - OPENCL_LIBS="-L$AMDAPPSDKROOT/lib/$ARCH_DIR $OPENCL_LIBS" - fi -fi +standalone="no" +bmsc="no" +drivercount="" -have_cgminer_sdk=false -if test -n "$CGMINER_SDK"; then - have_cgminer_sdk=true - CPPFLAGS="-I$CGMINER_SDK/include $CPPFLAGS" - LDFLAGS="-L$CGMINER_SDK/lib/$target $LDFLAGS" +AC_ARG_ENABLE([bmsc], + [AC_HELP_STRING([--enable-bmsc],[Compile support for Bitmain Single Chain (default disabled)])], + [bmsc=$enableval] + ) +if test "x$bmsc" = xyes; then + AC_DEFINE([USE_BMSC], [1], [Defined to 1 if Bitmain Single Chain support is wanted]) + drivercount=x$drivercount + standalone="yes" fi +AM_CONDITIONAL([HAS_BMSC], [test x$bmsc = xyes]) + -opencl="yes" +bitmain="no" -AC_ARG_ENABLE([opencl], - [AC_HELP_STRING([--disable-opencl],[Override detection and disable building with opencl])], - [opencl=$enableval] +AC_ARG_ENABLE([bitmain], + [AC_HELP_STRING([--enable-bitmain],[Compile support for Bitmain Multi Chain (default disabled)])], + [bitmain=$enableval] ) -if test "x$opencl" != xno; then - # Check for OpenCL (the long way needed on mingw32 due to calling conventions) - AC_MSG_CHECKING([for OpenCL]) - SAVED_LIBS=$LIBS - SAVED_CFLAGS=$CFLAGS - LIBS="$LIBS $OPENCL_LIBS" - CFLAGS="$CFLAGS $OPENCL_FLAGS" - AC_LINK_IFELSE( - [AC_LANG_PROGRAM([[ - #ifdef __APPLE_CC__ - #include - #else - #include - #endif - ]], - [[return clSetKernelArg(0, 0, 0, 0); ]])], - [AC_MSG_RESULT(yes) - AC_DEFINE([HAVE_OPENCL], [1], [Defined to 1 if OpenCL is present on the system.]) - found_opencl=1 - ], - [AC_MSG_RESULT(no) - OPENCL_FLAGS= - OPENCL_LIBS= - found_opencl=0]) - LIBS=$SAVED_LIBS - CFLAGS=$SAVED_CFLAGS -else - OPENCL_FLAGS="" - OPENCL_LIBS="" +if test "x$bitmain" = xyes; then + AC_DEFINE([USE_BITMAIN], [1], [Defined to 1 if Bitmain Multi Chain support is wanted]) + drivercount=x$drivercount + standalone="yes" fi +AM_CONDITIONAL([HAS_BITMAIN], [test x$bitmain = xyes]) -has_winpthread=false -if test "x$have_win32" = xtrue; then - has_winpthread=true - AC_CHECK_LIB(winpthread, nanosleep, , has_winpthread=false) - PTHREAD_LIBS=-lwinpthread +avalon="no" + +AC_ARG_ENABLE([avalon], + [AC_HELP_STRING([--enable-avalon],[Compile support for Avalon (default disabled)])], + [avalon=$enableval] + ) +if test "x$avalon" = xyes; then + AC_DEFINE([USE_AVALON], [1], [Defined to 1 if Avalon support is wanted]) + drivercount=x$drivercount fi +AM_CONDITIONAL([HAS_AVALON], [test x$avalon = xyes]) -if test "x$has_winpthread" != xtrue; then - AC_CHECK_LIB(pthread, pthread_create, , - AC_MSG_ERROR([Could not find pthread library - please install libpthread])) - PTHREAD_LIBS=-lpthread + +avalon2="no" + +AC_ARG_ENABLE([avalon2], + [AC_HELP_STRING([--enable-avalon2],[Compile support for Avalon2 (default disabled)])], + [avalon2=$enableval] + ) +if test "x$avalon2" = xyes; then + AC_DEFINE([USE_AVALON2], [1], [Defined to 1 if Avalon2 support is wanted]) + drivercount=x$drivercount fi +AM_CONDITIONAL([HAS_AVALON2], [test x$avalon2 = xyes]) + -AC_ARG_ENABLE([adl], - [AC_HELP_STRING([--disable-adl],[Override detection and disable building with adl])], - [adl=$enableval] +avalon4="no" + +AC_ARG_ENABLE([avalon4], + [AC_HELP_STRING([--enable-avalon4],[Compile support for Avalon4 (default disabled)])], + [avalon4=$enableval] ) +if test "x$avalon4" = xyes; then + AC_DEFINE([USE_AVALON4], [1], [Defined to 1 if Avalon4 support is wanted]) +fi +AM_CONDITIONAL([HAS_AVALON4], [test x$avalon4 = xyes]) -scrypt="no" -if test "$found_opencl" = 1; then - if test "x$adl" != xno; then - ADL_CPPFLAGS= - AC_CHECK_FILE([$srcdir/ADL_SDK/adl_sdk.h], [have_adl=true; ADL_CPPFLAGS=-I$srcdir], have_adl=false,) - if test x$have_adl+$have_cgminer_sdk = xfalse+true; then - AC_CHECK_FILE([$CGMINER_SDK/include/ADL_SDK/adl_sdk.h], [have_adl=true; ADL_CPPFLAGS=-I$CGMINER_SDK/include], have_adl=false,) - fi - if test x$have_adl = xtrue - then - AC_DEFINE([HAVE_ADL], [1], [Defined if ADL headers were found]) - else - DLOPEN_FLAGS="" - fi - fi +bab="no" - AC_ARG_ENABLE([scrypt], - [AC_HELP_STRING([--enable-scrypt],[Compile support for scrypt litecoin mining (default disabled)])], - [scrypt=$enableval] - ) - if test "x$scrypt" = xyes; then - AC_DEFINE([USE_SCRYPT], [1], [Defined to 1 if scrypt support is wanted]) - fi -else - DLOPEN_FLAGS="" +AC_ARG_ENABLE([bab], + [AC_HELP_STRING([--enable-bab],[Compile support for BlackArrow Bitfury STANDALONE(default disabled)])], + [bab=$enableval] + ) +if test "x$bab" = xyes; then + AC_DEFINE([USE_BAB], [1], [Defined to 1 if BlackArrow Bitfury support is wanted]) + drivercount=x$drivercount + standalone="yes" fi +AM_CONDITIONAL([HAS_BAB], [test x$bab = xyes]) -AM_CONDITIONAL([HAS_SCRYPT], [test x$scrypt = xyes]) bflsc="no" @@ -226,9 +211,11 @@ AC_ARG_ENABLE([bflsc], ) if test "x$bflsc" = xyes; then AC_DEFINE([USE_BFLSC], [1], [Defined to 1 if BFL ASIC support is wanted]) + drivercount=x$drivercount fi AM_CONDITIONAL([HAS_BFLSC], [test x$bflsc = xyes]) + bitforce="no" AC_ARG_ENABLE([bitforce], @@ -237,9 +224,103 @@ AC_ARG_ENABLE([bitforce], ) if test "x$bitforce" = xyes; then AC_DEFINE([USE_BITFORCE], [1], [Defined to 1 if BitForce support is wanted]) + drivercount=x$drivercount fi AM_CONDITIONAL([HAS_BITFORCE], [test x$bitforce = xyes]) + +bitfury="no" + +AC_ARG_ENABLE([bitfury], + [AC_HELP_STRING([--enable-bitfury],[Compile support for BitFury ASICs (default disabled)])], + [bitfury=$enableval] + ) +if test "x$bitfury" = xyes; then + AC_DEFINE([USE_BITFURY], [1], [Defined to 1 if BitFury ASIC support is wanted]) + drivercount=x$drivercount +fi +AM_CONDITIONAL([HAS_BITFURY], [test x$bitfury = xyes]) + + +bitmine_A1="no" + +AC_ARG_ENABLE([bitmine_A1], + [AC_HELP_STRING([--enable-bitmine_A1],[Compile support for Bitmine.ch A1 ASICs STANDALONE(default disabled)])], + [bitmine_A1=$enableval] + ) +if test "x$bitmine_A1" = xyes; then + AC_DEFINE([USE_BITMINE_A1], [1], [Defined to 1 if Bitmine A1 support is wanted]) + drivercount=x$drivercount + standalone="yes" +fi +AM_CONDITIONAL([HAS_BITMINE_A1], [test x$bitmine_A1 = xyes]) + + +blockerupter="no" + +AC_ARG_ENABLE([blockerupter], + [AC_HELP_STRING([--enable-blockerupter],[Compile support for BlockErupter (default disabled)])], + [blockerupter=$enableval] + ) +if test "x$blockerupter" = xyes; then + AC_DEFINE([USE_BLOCKERUPTER], [1], [Defined to 1 if BlockErupter support is wanted]) + drivercount=x$drivercount +fi +AM_CONDITIONAL([HAS_BLOCKERUPTER], [test x$blockerupter = xyes]) + + +cointerra="no" + +AC_ARG_ENABLE([cointerra], + [AC_HELP_STRING([--enable-cointerra],[Compile support for Cointerra ASICs (default disabled)])], + [cointerra=$enableval] + ) +if test "x$cointerra" = xyes; then + AC_DEFINE([USE_COINTERRA], [1], [Defined to 1 if Cointerra support is wanted]) + drivercount=x$drivercount +fi +AM_CONDITIONAL([HAS_COINTERRA], [test x$cointerra = xyes]) + + +drillbit="no" + +AC_ARG_ENABLE([drillbit], + [AC_HELP_STRING([--enable-drillbit],[Compile support for Drillbit BitFury ASICs (default disabled)])], + [drillbit=$enableval] + ) +if test "x$drillbit" = xyes; then + AC_DEFINE([USE_DRILLBIT], [1], [Defined to 1 if Drillbit BitFury support is wanted]) + drivercount=x$drivercount +fi +AM_CONDITIONAL([HAS_DRILLBIT], [test x$drillbit = xyes]) + + +hashfast="no" + +AC_ARG_ENABLE([hashfast], + [AC_HELP_STRING([--enable-hashfast],[Compile support for Hashfast (default disabled)])], + [hashfast=$enableval] + ) +if test "x$hashfast" = xyes; then + AC_DEFINE([USE_HASHFAST], [1], [Defined to 1 if Hashfast support is wanted]) + drivercount=x$drivercount +fi +AM_CONDITIONAL([HAS_HASHFAST], [test x$hashfast = xyes]) + + +hashratio="no" + +AC_ARG_ENABLE([hashratio], + [AC_HELP_STRING([--enable-hashratio],[Compile support for Hashratio (default disabled)])], + [hashratio=$enableval] + ) +if test "x$hashratio" = xyes; then + AC_DEFINE([USE_HASHRATIO], [1], [Defined to 1 if Hashratiosupport is wanted]) + drivercount=x$drivercount +fi +AM_CONDITIONAL([HAS_HASHRATIO], [test x$hashratio = xyes]) + + icarus="no" AC_ARG_ENABLE([icarus], @@ -248,19 +329,51 @@ AC_ARG_ENABLE([icarus], ) if test "x$icarus" = xyes; then AC_DEFINE([USE_ICARUS], [1], [Defined to 1 if Icarus support is wanted]) + drivercount=x$drivercount fi AM_CONDITIONAL([HAS_ICARUS], [test x$icarus = xyes]) -avalon="no" -AC_ARG_ENABLE([avalon], - [AC_HELP_STRING([--enable-avalon],[Compile support for Avalon (default disabled)])], - [avalon=$enableval] +klondike="no" + +AC_ARG_ENABLE([klondike], + [AC_HELP_STRING([--enable-klondike],[Compile support for Klondike (default disabled)])], + [klondike=$enableval] ) -if test "x$avalon" = xyes; then - AC_DEFINE([USE_AVALON], [1], [Defined to 1 if Avalon support is wanted]) +if test "x$klondike" = xyes; then + AC_DEFINE([USE_KLONDIKE], [1], [Defined to 1 if Klondike support is wanted]) + drivercount=x$drivercount fi -AM_CONDITIONAL([HAS_AVALON], [test x$avalon = xyes]) +AM_CONDITIONAL([HAS_KLONDIKE], [test x$klondike = xyes]) + + +knc="no" + +AC_ARG_ENABLE([knc], + [AC_HELP_STRING([--enable-knc],[Compile support for KnC miners STANDALONE(default disabled)])], + [knc=$enableval] + ) +if test "x$knc" = xyes; then + AC_DEFINE([USE_KNC], [1], [Defined to 1 if KnC miner support is wanted]) + drivercount=x$drivercount + standalone="yes" +fi +AM_CONDITIONAL([HAS_KNC], [test x$knc = xyes]) + + +minion="no" + +AC_ARG_ENABLE([minion], + [AC_HELP_STRING([--enable-minion],[Compile support for Minion BlackArrow ASIC STANDALONE(default disabled)])], + [minion=$enableval] + ) +if test "x$minion" = xyes; then + AC_DEFINE([USE_MINION], [1], [Defined to 1 if Minion BlackArrow ASIC support is wanted]) + drivercount=x$drivercount + standalone="yes" +fi +AM_CONDITIONAL([HAS_MINION], [test x$minion = xyes]) + modminer="no" @@ -270,19 +383,49 @@ AC_ARG_ENABLE([modminer], ) if test "x$modminer" = xyes; then AC_DEFINE([USE_MODMINER], [1], [Defined to 1 if ModMiner support is wanted]) + drivercount=x$drivercount fi AM_CONDITIONAL([HAS_MODMINER], [test x$modminer = xyes]) -ztex="no" -AC_ARG_ENABLE([ztex], - [AC_HELP_STRING([--enable-ztex],[Compile support for Ztex (default disabled)])], - [ztex=$enableval] +sp10="no" + +AC_ARG_ENABLE([sp10], + [AC_HELP_STRING([--enable-sp10],[Compile support for Spondoolies SP10 STANDALONE(default disabled)])], + [sp10=$enableval] + ) +if test "x$sp10" = xyes; then + AC_DEFINE([USE_SP10], [1], [Defined to 1 if Spondoolies SP10 support is wanted]) + drivercount=x$drivercount + standalone="yes" +fi +AM_CONDITIONAL([HAS_SP10], [test x$sp10 = xyes]) + + + +sp30="no" + +AC_ARG_ENABLE([sp30], + [AC_HELP_STRING([--enable-sp30],[Compile support for Spondoolies SP30 STANDALONE(default disabled)])], + [sp30=$enableval] + ) +if test "x$sp30" = xyes; then + AC_DEFINE([USE_SP30], [1], [Defined to 1 if SP30 support is wanted]) + drivercount=x$drivercount + standalone="yes" +fi +AM_CONDITIONAL([HAS_SP30], [test x$sp30 = xyes]) + + +forcecombo="no" + +AC_ARG_ENABLE([forcecombo], + [AC_HELP_STRING([--enable-forcecombo],[Allow combinations of drivers not intended to be built together(default disabled)])], + [forcecombo=$enableval] ) -if test "x$ztex" = xyes; then - AC_DEFINE([USE_ZTEX], [1], [Defined to 1 if Ztex support is wanted]) +if test "x$forcecombo" = xyes; then + standalone="no" fi -AM_CONDITIONAL([HAS_ZTEX], [test x$ztex = xyes]) curses="auto" @@ -308,30 +451,79 @@ else ]) fi -if test x$avalon$bitforce$modminer$bflsc$icarus != xnonononono; then + +#Add a new device to this list if it needs libusb, along with a no on the end. +if test x$avalon$avalon2$avalon4$bitforce$bitfury$blockerupter$modminer$bflsc$icarus$hashfast$hashratio$klondike$drillbit$cointerra$bmsc$bitmain != xnononononononononononononononono; then want_usbutils=true else want_usbutils=false fi -AM_CONDITIONAL([NEED_FPGAUTILS], [test x$modminer$ztex != xnono]) +if test x$bitfury != xno; then + want_libbitfury=true +else + want_libbitfury=false +fi + +if test x$avalon2$avalon4$hashratio != xnonono; then + want_crc16=true +else + want_crc16=false +fi + +AM_CONDITIONAL([NEED_FPGAUTILS], [test x$modminer != xno]) AM_CONDITIONAL([WANT_USBUTILS], [test x$want_usbutils != xfalse]) +AM_CONDITIONAL([WANT_LIBBITFURY], [test x$want_libbitfury != xfalse]) 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]) -AC_CONFIG_SUBDIRS([compat/libusb-1.0]) if test "x$want_usbutils" != xfalse; then + dlibusb="no" AC_DEFINE([USE_USBUTILS], [1], [Defined to 1 if usbutils support required]) - LIBUSB_LIBS="compat/libusb-1.0/libusb/.libs/libusb-1.0.a" - if test "x$have_linux" = "xtrue"; then - PKG_CHECK_MODULES([UDEV], [libudev], LIBUSB_LIBS+=" -ludev", [AC_MSG_ERROR([Missing required libudev dev])]) + AC_ARG_WITH([system-libusb], + [AC_HELP_STRING([--with-system-libusb],[NOT RECOMMENDED! Compile against dynamic system libusb. (Default use included static libusb)])], + [dlibusb=$withval] + ) + + if test "x$dlibusb" != xno; then + case $target in + *-*-freebsd*) + LIBUSB_LIBS="-lusb" + LIBUSB_CFLAGS="" + AC_DEFINE(HAVE_LIBUSB, 1, [Define if you have libusb-1.0]) + ;; + *) + PKG_CHECK_MODULES(LIBUSB, libusb-1.0, [AC_DEFINE(HAVE_LIBUSB, 1, [Define if you have libusb-1.0])], [AC_MSG_ERROR([Could not find usb library - please install libusb-1.0])]) + ;; + esac + else + AC_CONFIG_SUBDIRS([compat/libusb-1.0]) + LIBUSB_LIBS="compat/libusb-1.0/libusb/.libs/libusb-1.0.a" + if test "x$have_linux" = "xtrue"; then + AC_ARG_ENABLE([udev], + [AC_HELP_STRING([--disable-udev],[Disable building libusb with udev])], + [udev=$enableval] + ) + + if test "x$udev" != xno; then + LIBUSB_LIBS+=" -ludev" + fi + fi + if test "x$have_darwin" = "xtrue"; then + LIBUSB_LIBS+=" -lobjc" + LDFLAGS+=" -framework CoreFoundation -framework IOKit" + fi fi else LIBUSB_LIBS="" fi -JANSSON_LIBS="compat/jansson/libjansson.a" +AM_CONDITIONAL([WANT_STATIC_LIBUSB], [test x$dlibusb = xno]) + +AC_CONFIG_SUBDIRS([compat/jansson-2.6]) +JANSSON_LIBS="compat/jansson-2.6/src/.libs/libjansson.a" PKG_PROG_PKG_CONFIG() @@ -348,15 +540,26 @@ fi AC_SUBST(LIBUSB_LIBS) AC_SUBST(LIBUSB_CFLAGS) -if test "x$have_win32" != xtrue; then - PKG_CHECK_MODULES([LIBCURL], [libcurl >= 7.25.0], [AC_DEFINE([CURL_HAS_KEEPALIVE], [1], [Defined if version of curl supports keepalive.])], - [PKG_CHECK_MODULES([LIBCURL], [libcurl >= 7.18.2], ,[AC_MSG_ERROR([Missing required libcurl dev >= 7.18.2])])]) +AC_ARG_ENABLE([libcurl], + [AC_HELP_STRING([--disable-libcurl],[Disable building with libcurl for getwork and GBT support])], + [libcurl=$enableval] + ) + +if test "x$libcurl" != xno; then + if test "x$have_win32" != xtrue; then + PKG_CHECK_MODULES([LIBCURL], [libcurl >= 7.25.0], [AC_DEFINE([CURL_HAS_KEEPALIVE], [1], [Defined if version of curl supports keepalive.])], + [PKG_CHECK_MODULES([LIBCURL], [libcurl >= 7.18.2], ,[AC_MSG_ERROR([Missing required libcurl dev >= 7.18.2])])]) + else + PKG_CHECK_MODULES([LIBCURL], [libcurl >= 7.25.0], ,[AC_MSG_ERROR([Missing required libcurl dev >= 7.25.0])]) + AC_DEFINE([CURL_HAS_KEEPALIVE], [1]) + fi + AC_DEFINE([HAVE_LIBCURL], [1], [Defined to 1 if libcurl support built in]) else - PKG_CHECK_MODULES([LIBCURL], [libcurl >= 7.25.0], ,[AC_MSG_ERROR([Missing required libcurl dev >= 7.25.0])]) - AC_DEFINE([CURL_HAS_KEEPALIVE], [1]) + LIBCURL_LIBS="" fi AC_SUBST(LIBCURL_LIBS) + #check execv signature AC_COMPILE_IFELSE([AC_LANG_SOURCE([ #include @@ -403,15 +606,6 @@ fi AC_DEFINE_UNQUOTED([CGMINER_PREFIX], ["$prefix/bin"], [Path to cgminer install]) -AC_DEFINE_UNQUOTED([PHATK_KERNNAME], ["phatk121016"], [Filename for phatk kernel]) -AC_DEFINE_UNQUOTED([POCLBM_KERNNAME], ["poclbm130302"], [Filename for poclbm kernel]) -AC_DEFINE_UNQUOTED([DIAKGCN_KERNNAME], ["diakgcn121016"], [Filename for diakgcn kernel]) -AC_DEFINE_UNQUOTED([DIABLO_KERNNAME], ["diablo130302"], [Filename for diablo kernel]) -AC_DEFINE_UNQUOTED([SCRYPT_KERNNAME], ["scrypt130511"], [Filename for scrypt kernel]) - - -AC_SUBST(OPENCL_LIBS) -AC_SUBST(OPENCL_FLAGS) AC_SUBST(JANSSON_LIBS) AC_SUBST(PTHREAD_FLAGS) AC_SUBST(DLOPEN_FLAGS) @@ -422,12 +616,10 @@ AC_SUBST(WS2_LIBS) AC_SUBST(MM_LIBS) AC_SUBST(MATH_LIBS) AC_SUBST(RT_LIBS) -AC_SUBST(ADL_CPPFLAGS) AC_CONFIG_FILES([ Makefile compat/Makefile - compat/jansson/Makefile ccan/Makefile lib/Makefile ]) @@ -445,49 +637,58 @@ echo echo "Configuration Options Summary:" echo +if test "x$libcurl" != xno; then + echo " libcurl(GBT+getwork).: Enabled: $LIBCURL_LIBS" +else + echo " libcurl(GBT+getwork).: Disabled" +fi + echo " curses.TUI...........: $cursesmsg" -if test "x$opencl" != xno; then - if test $found_opencl = 1; then - echo " OpenCL...............: FOUND. GPU mining support enabled" - if test "x$scrypt" != xno; then - echo " scrypt...............: Enabled" - else - echo " scrypt...............: Disabled" - fi - else - echo " OpenCL...............: NOT FOUND. GPU mining support DISABLED" - if test "x$bitforce$avalon$icarus$ztex$modminer$bflsc" = xnononononono; then - AC_MSG_ERROR([No mining configured in]) - fi - echo " scrypt...............: Disabled (needs OpenCL)" - fi +echo +if test "x$bmsc" = xyes; then + echo " Bitmain.SingleChain..: Enabled" else - echo " OpenCL...............: Detection overrided. GPU mining support DISABLED" - if test "x$bitforce$icarus$avalon$ztex$modminer$bflsc" = xnononononono; then - AC_MSG_ERROR([No mining configured in]) - fi - echo " scrypt...............: Disabled (needs OpenCL)" + echo " Bitmain.SingleChain..: Disabled" fi -if test "x$adl" != xno; then - if test x$have_adl = xtrue; then - echo " ADL..................: SDK found, GPU monitoring support enabled" - else - echo " ADL..................: SDK NOT found, GPU monitoring support DISABLED" - fi +if test "x$bitmain" = xyes; then + echo " Bitmain.MultiChain...: Enabled" else - echo " ADL..................: Detection overrided. GPU monitoring support DISABLED" + echo " Bitmain.MultiChain...: Disabled" fi -echo if test "x$avalon" = xyes; then echo " Avalon.ASICs.........: Enabled" else echo " Avalon.ASICs.........: Disabled" fi +if test "x$avalon2" = xyes; then + echo " Avalon2.ASICs........: Enabled" +else + echo " Avalon2.ASICs........: Disabled" +fi + +if test "x$avalon4" = xyes; then + echo " Avalon4.ASICs........: Enabled" +else + echo " Avalon4.ASICs........: Disabled" +fi + +if test "x$minion" = xyes; then + echo " BlackArrowMinion.ASIC: Enabled" +else + echo " BlackArrowMinion.ASIC: Disabled" +fi + +if test "x$bab" = xyes; then + echo " BlackArrow.ASICs.....: Enabled" +else + echo " BlackArrow.ASICs.....: Disabled" +fi + if test "x$bflsc" = xyes; then echo " BFL.ASICs............: Enabled" else @@ -500,10 +701,78 @@ else echo " BitForce.FPGAs.......: Disabled" fi +if test "x$bitfury" = xyes; then + echo " BitFury.ASICs........: Enabled" +else + echo " BitFury.ASICs........: Disabled" +fi + +if test "x$blockerupter" = xyes; then + echo " BlockErupter.ASICs...: Enabled" +else + echo " BlockErupter.ASICs...: Disabled" +fi + +if test "x$cointerra" = xyes; then + echo " Cointerra.ASICs......: Enabled" +else + echo " Cointerra.ASICs......: Disabled" +fi + +if test "x$sp10" = xyes; then + echo " Spond-sp10.ASICs.....: Enabled" +else + echo " Spond-sp10.ASICs.....: Disabled" +fi + + +if test "x$sp30" = xyes; then + echo " Spond-sp30.ASICs.....: Enabled" +else + echo " Spond-sp30.ASICs.....: Disabled" +fi + + +if test "x$bitmine_A1" = xyes; then + echo " Bitmine-A1.ASICs.....: Enabled" +else + echo " Bitmine-A1.ASICs.....: Disabled" +fi + +if test "x$drillbit" = xyes; then + echo " Drillbit.BitFury.....: Enabled" +else + echo " Drillbit.BitFury.....: Disabled" +fi + +if test "x$hashfast" = xyes; then + echo " Hashfast.ASICs.......: Enabled" +else + echo " Hashfast.ASICs.......: Disabled" +fi + +if test "x$hashratio" = xyes; then + echo " Hashratio.ASICs......: Enabled" +else + echo " Hashratio.ASICs......: Disabled" +fi + if test "x$icarus" = xyes; then - echo " Icarus.FPGAs.........: Enabled" + echo " Icarus.ASICs/FPGAs...: Enabled" else - echo " Icarus.FPGAs.........: Disabled" + echo " Icarus.ASICs/FPGAs...: Disabled" +fi + +if test "x$klondike" = xyes; then + echo " Klondike.ASICs.......: Enabled" +else + echo " Klondike.ASICs.......: Disabled" +fi + +if test "x$knc" = xyes; then + echo " KnC.ASICs............: Enabled" +else + echo " KnC.ASICs............: Disabled" fi if test "x$modminer" = xyes; then @@ -512,10 +781,19 @@ else echo " ModMiner.FPGAs.......: Disabled" fi -if test "x$ztex" = xyes; then - echo " Ztex.FPGAs...........: Enabled" -else - echo " Ztex.FPGAs...........: Disabled" +#Add any new device to this, along with a no on the end of the test +if test "x$avalon$avalon2$bab$bflsc$bitforce$bitfury$blockerupter$hashfast$hashratio$icarus$klondike$knc$modminer$drillbit$minion$cointerra$bitmine_A1$bmsc$bitmain$sp10$sp30" = xnonononononononononononononononononononono; then + echo + AC_MSG_ERROR([No mining devices configured in]) + echo +fi + +if test "x$standalone" = xyes; then + if test $drivercount != x; then + echo + AC_MSG_ERROR([You have configured more than one driver in with a driver that is designed to be standalone only (see ./configure --help)]) + echo + fi fi echo @@ -523,9 +801,12 @@ echo "Compilation............: make (or gmake)" echo " CPPFLAGS.............: $CPPFLAGS" echo " CFLAGS...............: $CFLAGS" echo " LDFLAGS..............: $LDFLAGS $PTHREAD_FLAGS" -echo " LDADD................: $DLOPEN_FLAGS $LIBCURL_LIBS $JANSSON_LIBS $PTHREAD_LIBS $OPENCL_LIBS $NCURSES_LIBS $PDCURSES_LIBS $WS2_LIBS $MATH_LIBS $LIBUSB_LIBS $RT_LIBS" +echo " LDADD................: $DLOPEN_FLAGS $LIBCURL_LIBS $JANSSON_LIBS $PTHREAD_LIBS $NCURSES_LIBS $PDCURSES_LIBS $WS2_LIBS $MATH_LIBS $LIBUSB_LIBS $RT_LIBS" echo echo "Installation...........: make install (as root if needed, with 'su' or 'sudo')" echo " prefix...............: $prefix" echo - +if test "x$want_usbutils$dlibusb" = xyesyes; then +echo "*** SYSTEM LIBUSB BEING ADDED - NOT RECOMMENDED UNLESS YOU ARE UNABLE TO COMPILE THE INCLUDED LIBUSB ***" +echo +fi diff --git a/crc.h b/crc.h new file mode 100644 index 0000000000..820fbe77cd --- /dev/null +++ b/crc.h @@ -0,0 +1,23 @@ +/* + * Milkymist SoC (Software) + * Copyright (C) 2007, 2008, 2009 Sebastien Bourdeauducq + * + * 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, version 3 of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#ifndef _CRC_H_ +#define _CRC_H_ + +unsigned short crc16(const unsigned char *buffer, int len); + +#endif /* _CRC_H_ */ diff --git a/crc16.c b/crc16.c new file mode 100644 index 0000000000..7d8374e7b3 --- /dev/null +++ b/crc16.c @@ -0,0 +1,45 @@ +unsigned int crc16_table[256] = { + 0x0000, 0x1021, 0x2042, 0x3063, 0x4084, 0x50A5, 0x60C6, 0x70E7, + 0x8108, 0x9129, 0xA14A, 0xB16B, 0xC18C, 0xD1AD, 0xE1CE, 0xF1EF, + 0x1231, 0x0210, 0x3273, 0x2252, 0x52B5, 0x4294, 0x72F7, 0x62D6, + 0x9339, 0x8318, 0xB37B, 0xA35A, 0xD3BD, 0xC39C, 0xF3FF, 0xE3DE, + 0x2462, 0x3443, 0x0420, 0x1401, 0x64E6, 0x74C7, 0x44A4, 0x5485, + 0xA56A, 0xB54B, 0x8528, 0x9509, 0xE5EE, 0xF5CF, 0xC5AC, 0xD58D, + 0x3653, 0x2672, 0x1611, 0x0630, 0x76D7, 0x66F6, 0x5695, 0x46B4, + 0xB75B, 0xA77A, 0x9719, 0x8738, 0xF7DF, 0xE7FE, 0xD79D, 0xC7BC, + 0x48C4, 0x58E5, 0x6886, 0x78A7, 0x0840, 0x1861, 0x2802, 0x3823, + 0xC9CC, 0xD9ED, 0xE98E, 0xF9AF, 0x8948, 0x9969, 0xA90A, 0xB92B, + 0x5AF5, 0x4AD4, 0x7AB7, 0x6A96, 0x1A71, 0x0A50, 0x3A33, 0x2A12, + 0xDBFD, 0xCBDC, 0xFBBF, 0xEB9E, 0x9B79, 0x8B58, 0xBB3B, 0xAB1A, + 0x6CA6, 0x7C87, 0x4CE4, 0x5CC5, 0x2C22, 0x3C03, 0x0C60, 0x1C41, + 0xEDAE, 0xFD8F, 0xCDEC, 0xDDCD, 0xAD2A, 0xBD0B, 0x8D68, 0x9D49, + 0x7E97, 0x6EB6, 0x5ED5, 0x4EF4, 0x3E13, 0x2E32, 0x1E51, 0x0E70, + 0xFF9F, 0xEFBE, 0xDFDD, 0xCFFC, 0xBF1B, 0xAF3A, 0x9F59, 0x8F78, + 0x9188, 0x81A9, 0xB1CA, 0xA1EB, 0xD10C, 0xC12D, 0xF14E, 0xE16F, + 0x1080, 0x00A1, 0x30C2, 0x20E3, 0x5004, 0x4025, 0x7046, 0x6067, + 0x83B9, 0x9398, 0xA3FB, 0xB3DA, 0xC33D, 0xD31C, 0xE37F, 0xF35E, + 0x02B1, 0x1290, 0x22F3, 0x32D2, 0x4235, 0x5214, 0x6277, 0x7256, + 0xB5EA, 0xA5CB, 0x95A8, 0x8589, 0xF56E, 0xE54F, 0xD52C, 0xC50D, + 0x34E2, 0x24C3, 0x14A0, 0x0481, 0x7466, 0x6447, 0x5424, 0x4405, + 0xA7DB, 0xB7FA, 0x8799, 0x97B8, 0xE75F, 0xF77E, 0xC71D, 0xD73C, + 0x26D3, 0x36F2, 0x0691, 0x16B0, 0x6657, 0x7676, 0x4615, 0x5634, + 0xD94C, 0xC96D, 0xF90E, 0xE92F, 0x99C8, 0x89E9, 0xB98A, 0xA9AB, + 0x5844, 0x4865, 0x7806, 0x6827, 0x18C0, 0x08E1, 0x3882, 0x28A3, + 0xCB7D, 0xDB5C, 0xEB3F, 0xFB1E, 0x8BF9, 0x9BD8, 0xABBB, 0xBB9A, + 0x4A75, 0x5A54, 0x6A37, 0x7A16, 0x0AF1, 0x1AD0, 0x2AB3, 0x3A92, + 0xFD2E, 0xED0F, 0xDD6C, 0xCD4D, 0xBDAA, 0xAD8B, 0x9DE8, 0x8DC9, + 0x7C26, 0x6C07, 0x5C64, 0x4C45, 0x3CA2, 0x2C83, 0x1CE0, 0x0CC1, + 0xEF1F, 0xFF3E, 0xCF5D, 0xDF7C, 0xAF9B, 0xBFBA, 0x8FD9, 0x9FF8, + 0x6E17, 0x7E36, 0x4E55, 0x5E74, 0x2E93, 0x3EB2, 0x0ED1, 0x1EF0 +}; + +unsigned short crc16(const unsigned char *buffer, int len) +{ + unsigned short crc; + + crc = 0; + while(len-- > 0) + crc = crc16_table[((crc >> 8) ^ (*buffer++)) & 0xFF] ^ (crc << 8); + + return crc; +} diff --git a/diablo130302.cl b/diablo130302.cl deleted file mode 100644 index a72180319b..0000000000 --- a/diablo130302.cl +++ /dev/null @@ -1,1361 +0,0 @@ -/* - * DiabloMiner - OpenCL miner for BitCoin - * Copyright (C) 2012, 2013 Con Kolivas - * Copyright (C) 2010, 2011, 2012 Patrick McFarland - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more detail). - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -#ifdef VECTORS4 - typedef uint4 z; -#elif defined(VECTORS2) - typedef uint2 z; -#else - typedef uint z; -#endif - -#ifdef BITALIGN -#pragma OPENCL EXTENSION cl_amd_media_ops : enable -#define Zrotr(a, b) amd_bitalign((z)a, (z)a, (z)(32 - b)) -#else -#define Zrotr(a, b) rotate((z)a, (z)b) -#endif - -#ifdef BFI_INT -#define ZCh(a, b, c) amd_bytealign(a, b, c) -#define ZMa(a, b, c) amd_bytealign((c ^ a), (b), (a)) -#else -#define ZCh(a, b, c) bitselect((z)c, (z)b, (z)a) -#define ZMa(a, b, c) bitselect((z)a, (z)b, (z)c ^ (z)a) -#endif - -/* These constants are not the classic SHA256 constants but the order that - * constants are used in this kernel. - */ -__constant uint K[] = { - 0xd807aa98U, - 0x12835b01U, - 0x243185beU, - 0x550c7dc3U, - 0x72be5d74U, - 0x80deb1feU, - 0x9bdc06a7U, - 0xc19bf3f4U, - 0x0fc19dc6U, - 0x240ca1ccU, - 0x80000000U, // 10 - 0x2de92c6fU, - 0x4a7484aaU, - 0x00000280U, - 0x5cb0a9dcU, - 0x76f988daU, - 0x983e5152U, - 0xa831c66dU, - 0xb00327c8U, - 0xbf597fc7U, - 0xc6e00bf3U, // 20 - 0x00A00055U, - 0xd5a79147U, - 0x06ca6351U, - 0x14292967U, - 0x27b70a85U, - 0x2e1b2138U, - 0x4d2c6dfcU, - 0x53380d13U, - 0x650a7354U, - 0x766a0abbU, // 30 - 0x81c2c92eU, - 0x92722c85U, - 0xa2bfe8a1U, - 0xa81a664bU, - 0xc24b8b70U, - 0xc76c51a3U, - 0xd192e819U, - 0xd6990624U, - 0xf40e3585U, - 0x106aa070U, // 40 - 0x19a4c116U, - 0x1e376c08U, - 0x2748774cU, - 0x34b0bcb5U, - 0x391c0cb3U, - 0x4ed8aa4aU, - 0x5b9cca4fU, - 0x682e6ff3U, - 0x748f82eeU, - 0x78a5636fU, // 50 - 0x84c87814U, - 0x8cc70208U, - 0x90befffaU, - 0xa4506cebU, - 0xbef9a3f7U, - 0xc67178f2U, - 0x98c7e2a2U, - 0x90bb1e3cU, - 0x510e527fU, - 0x9b05688cU, // 60 - 0xfc08884dU, - 0x3c6ef372U, - 0x50c6645bU, - 0x6a09e667U, - 0xbb67ae85U, - 0x3ac42e24U, - 0xd21ea4fdU, - 0x59f111f1U, - 0x923f82a4U, - 0xab1c5ed5U, // 70 - 0x5807aa98U, - 0xc19bf274U, - 0xe49b69c1U, - 0x00a00000U, - 0xefbe4786U, - 0x00000100U, - 0x11002000U, - 0x00400022U, - 0x136032EDU -}; - -#define ZR25(n) ((Zrotr((n), 25) ^ Zrotr((n), 14) ^ ((n) >> 3U))) -#define ZR15(n) ((Zrotr((n), 15) ^ Zrotr((n), 13) ^ ((n) >> 10U))) -#define ZR26(n) ((Zrotr((n), 26) ^ Zrotr((n), 21) ^ Zrotr((n), 7))) -#define ZR30(n) ((Zrotr((n), 30) ^ Zrotr((n), 19) ^ Zrotr((n), 10))) - -__kernel -__attribute__((vec_type_hint(z))) -__attribute__((reqd_work_group_size(WORKSIZE, 1, 1))) -void search( -#ifndef GOFFSET - const z base, -#endif - const uint PreVal4_state0, const uint PreVal4_state0_k7, - const uint PreVal4_T1, - const uint W18, const uint W19, - const uint W16, const uint W17, - const uint W16_plus_K16, const uint W17_plus_K17, - const uint W31, const uint W32, - const uint d1, const uint b1, const uint c1, - const uint h1, const uint f1, const uint g1, - const uint c1_plus_k5, const uint b1_plus_k6, - const uint state0, const uint state1, const uint state2, const uint state3, - const uint state4, const uint state5, const uint state6, const uint state7, - volatile __global uint * output) -{ - - z ZA[930]; - -#ifdef GOFFSET - const z Znonce = (uint)(get_global_id(0)); -#else - const z Znonce = base + (uint)(get_global_id(0)); -#endif - - ZA[15] = Znonce + PreVal4_state0; - - ZA[16] = (ZCh(ZA[15], b1, c1) + d1) + ZR26(ZA[15]); - ZA[26] = Znonce + PreVal4_T1; - - ZA[27] = ZMa(f1, g1, ZA[26]) + ZR30(ZA[26]); - ZA[17] = ZA[16] + h1; - - ZA[19] = (ZCh(ZA[17], ZA[15], b1) + c1_plus_k5) + ZR26(ZA[17]); - ZA[28] = ZA[27] + ZA[16]; - - ZA[548] = ZMa(ZA[26], f1, ZA[28]) + ZR30(ZA[28]); - ZA[20] = ZA[19] + g1; - - ZA[22] = (ZCh(ZA[20], ZA[17], ZA[15]) + b1_plus_k6) + ZR26(ZA[20]); - ZA[29] = ZA[548] + ZA[19]; - - ZA[549] = ZMa(ZA[28], ZA[26], ZA[29]) + ZR30(ZA[29]); - ZA[23] = ZA[22] + f1; - - ZA[24] = ZCh(ZA[23], ZA[20], ZA[17]) + ZR26(ZA[23]); - ZA[180] = Znonce + PreVal4_state0_k7; - ZA[30] = ZA[549] + ZA[22]; - - ZA[31] = ZMa(ZA[29], ZA[28], ZA[30]) + ZR30(ZA[30]); - ZA[181] = ZA[180] + ZA[24]; - - ZA[182] = ZA[181] + ZA[26]; - ZA[183] = ZA[181] + ZA[31]; - ZA[18] = ZA[17] + K[0]; - - ZA[186] = (ZCh(ZA[182], ZA[23], ZA[20]) + ZA[18]) + ZR26(ZA[182]); - ZA[184] = ZMa(ZA[30], ZA[29], ZA[183]) + ZR30(ZA[183]); - - ZA[187] = ZA[186] + ZA[28]; - ZA[188] = ZA[186] + ZA[184]; - ZA[21] = ZA[20] + K[1]; - - ZA[191] = (ZCh(ZA[187], ZA[182], ZA[23]) + ZA[21]) + ZR26(ZA[187]); - ZA[189] = ZMa(ZA[183], ZA[30], ZA[188]) + ZR30(ZA[188]); - - ZA[192] = ZA[191] + ZA[29]; - ZA[193] = ZA[191] + ZA[189]; - ZA[25] = ZA[23] + K[2]; - - ZA[196] = (ZCh(ZA[192], ZA[187], ZA[182]) + ZA[25]) + ZR26(ZA[192]); - ZA[194] = ZMa(ZA[188], ZA[183], ZA[193]) + ZR30(ZA[193]); - - ZA[197] = ZA[196] + ZA[30]; - ZA[198] = ZA[196] + ZA[194]; - ZA[185] = ZA[182] + K[3]; - - ZA[201] = (ZCh(ZA[197], ZA[192], ZA[187]) + ZA[185]) + ZR26(ZA[197]); - ZA[199] = ZMa(ZA[193], ZA[188], ZA[198]) + ZR30(ZA[198]); - - ZA[202] = ZA[201] + ZA[183]; - ZA[203] = ZA[201] + ZA[199]; - ZA[190] = ZA[187] + K[4]; - - ZA[206] = (ZCh(ZA[202], ZA[197], ZA[192]) + ZA[190]) + ZR26(ZA[202]); - ZA[204] = ZMa(ZA[198], ZA[193], ZA[203]) + ZR30(ZA[203]); - - ZA[207] = ZA[206] + ZA[188]; - ZA[208] = ZA[206] + ZA[204]; - ZA[195] = ZA[192] + K[5]; - - ZA[211] = (ZCh(ZA[207], ZA[202], ZA[197]) + ZA[195]) + ZR26(ZA[207]); - ZA[209] = ZMa(ZA[203], ZA[198], ZA[208]) + ZR30(ZA[208]); - - ZA[212] = ZA[193] + ZA[211]; - ZA[213] = ZA[211] + ZA[209]; - ZA[200] = ZA[197] + K[6]; - - ZA[216] = (ZCh(ZA[212], ZA[207], ZA[202]) + ZA[200]) + ZR26(ZA[212]); - ZA[214] = ZMa(ZA[208], ZA[203], ZA[213]) + ZR30(ZA[213]); - - ZA[217] = ZA[198] + ZA[216]; - ZA[218] = ZA[216] + ZA[214]; - ZA[205] = ZA[202] + K[7]; - - ZA[220] = (ZCh(ZA[217], ZA[212], ZA[207]) + ZA[205]) + ZR26(ZA[217]); - ZA[219] = ZMa(ZA[213], ZA[208], ZA[218]) + ZR30(ZA[218]); - - ZA[222] = ZA[203] + ZA[220]; - ZA[223] = ZA[220] + ZA[219]; - ZA[210] = ZA[207] + W16_plus_K16; - - ZA[226] = (ZCh(ZA[222], ZA[217], ZA[212]) + ZA[210]) + ZR26(ZA[222]); - ZA[225] = ZMa(ZA[218], ZA[213], ZA[223]) + ZR30(ZA[223]); - - ZA[0] = ZR25(Znonce) + W18; - ZA[228] = ZA[226] + ZA[225]; - ZA[227] = ZA[208] + ZA[226]; - ZA[215] = ZA[212] + W17_plus_K17; - - ZA[231] = (ZCh(ZA[227], ZA[222], ZA[217]) + ZA[215]) + ZR26(ZA[227]); - ZA[229] = ZMa(ZA[223], ZA[218], ZA[228]) + ZR30(ZA[228]); - ZA[1] = ZA[0] + K[8]; - - ZA[232] = ZA[213] + ZA[231]; - ZA[233] = ZA[231] + ZA[229]; - ZA[221] = ZA[217] + ZA[1]; - ZA[32] = Znonce + W19; - - ZA[236] = (ZCh(ZA[232], ZA[227], ZA[222]) + ZA[221]) + ZR26(ZA[232]); - ZA[234] = ZMa(ZA[228], ZA[223], ZA[233]) + ZR30(ZA[233]); - ZA[33] = ZA[32] + K[9]; - - ZA[3] = ZR15(ZA[0]) + K[10]; - ZA[238] = ZA[236] + ZA[234]; - ZA[237] = ZA[218] + ZA[236]; - ZA[224] = ZA[222] + ZA[33]; - - ZA[241] = (ZCh(ZA[237], ZA[232], ZA[227]) + ZA[224]) + ZR26(ZA[237]); - ZA[239] = ZMa(ZA[233], ZA[228], ZA[238]) + ZR30(ZA[238]); - ZA[4] = ZA[3] + K[11]; - - ZA[35] = ZR15(ZA[32]); - ZA[243] = ZA[241] + ZA[239]; - ZA[242] = ZA[223] + ZA[241]; - ZA[230] = ZA[227] + ZA[4]; - - ZA[246] = (ZCh(ZA[242], ZA[237], ZA[232]) + ZA[230]) + ZR26(ZA[242]); - ZA[244] = ZMa(ZA[238], ZA[233], ZA[243]) + ZR30(ZA[243]); - ZA[36] = ZA[35] + K[12]; - - ZA[7] = ZR15(ZA[3]) + K[13]; - ZA[248] = ZA[246] + ZA[244]; - ZA[247] = ZA[228] + ZA[246]; - ZA[235] = ZA[232] + ZA[36]; - - ZA[251] = (ZCh(ZA[247], ZA[242], ZA[237]) + ZA[235]) + ZR26(ZA[247]); - ZA[249] = ZMa(ZA[243], ZA[238], ZA[248]) + ZR30(ZA[248]); - ZA[8] = ZA[7] + K[14]; - - ZA[38] = ZR15(ZA[35]) + W16; - ZA[253] = ZA[251] + ZA[249]; - ZA[252] = ZA[233] + ZA[251]; - ZA[240] = ZA[237] + ZA[8]; - - ZA[256] = (ZCh(ZA[252], ZA[247], ZA[242]) + ZA[240]) + ZR26(ZA[252]); - ZA[254] = ZMa(ZA[248], ZA[243], ZA[253]) + ZR30(ZA[253]); - ZA[40] = ZA[38] + K[15]; - - ZA[10] = ZR15(ZA[7]) + W17; - ZA[258] = ZA[256] + ZA[254]; - ZA[257] = ZA[238] + ZA[256]; - ZA[245] = ZA[242] + ZA[40]; - - ZA[261] = (ZCh(ZA[257], ZA[252], ZA[247]) + ZA[245]) + ZR26(ZA[257]); - ZA[259] = ZMa(ZA[253], ZA[248], ZA[258]) + ZR30(ZA[258]); - ZA[13] = ZA[10] + K[16]; - - ZA[43] = ZR15(ZA[38]) + ZA[0]; - ZA[263] = ZA[261] + ZA[259]; - ZA[262] = ZA[243] + ZA[261]; - ZA[250] = ZA[247] + ZA[13]; - - ZA[266] = (ZCh(ZA[262], ZA[257], ZA[252]) + ZA[250]) + ZR26(ZA[262]); - ZA[264] = ZMa(ZA[258], ZA[253], ZA[263]) + ZR30(ZA[263]); - ZA[11] = ZR15(ZA[10]); - ZA[45] = ZA[43] + K[17]; - - ZA[52] = ZA[11] + ZA[32]; - ZA[267] = ZA[248] + ZA[266]; - ZA[255] = ZA[252] + ZA[45]; - ZA[268] = ZA[266] + ZA[264]; - - ZA[271] = (ZCh(ZA[267], ZA[262], ZA[257]) + ZA[255]) + ZR26(ZA[267]); - ZA[269] = ZMa(ZA[263], ZA[258], ZA[268]) + ZR30(ZA[268]); - ZA[54] = ZA[52] + K[18]; - - ZA[48] = ZR15(ZA[43]) + ZA[3]; - ZA[273] = ZA[271] + ZA[269]; - ZA[272] = ZA[253] + ZA[271]; - ZA[260] = ZA[257] + ZA[54]; - - ZA[276] = (ZCh(ZA[272], ZA[267], ZA[262]) + ZA[260]) + ZR26(ZA[272]); - ZA[274] = ZMa(ZA[268], ZA[263], ZA[273]) + ZR30(ZA[273]); - ZA[49] = ZA[48] + K[19]; - - ZA[61] = ZR15(ZA[52]) + ZA[35]; - ZA[278] = ZA[276] + ZA[274]; - ZA[277] = ZA[258] + ZA[276]; - ZA[265] = ZA[262] + ZA[49]; - - ZA[281] = (ZCh(ZA[277], ZA[272], ZA[267]) + ZA[265]) + ZR26(ZA[277]); - ZA[279] = ZMa(ZA[273], ZA[268], ZA[278]) + ZR30(ZA[278]); - ZA[62] = ZA[61] + K[20]; - - ZA[53] = ZR15(ZA[48]) + ZA[7]; - ZA[283] = ZA[281] + ZA[279]; - ZA[282] = ZA[263] + ZA[281]; - ZA[270] = ZA[267] + ZA[62]; - - ZA[286] = (ZCh(ZA[282], ZA[277], ZA[272]) + ZA[270]) + ZR26(ZA[282]); - ZA[284] = ZMa(ZA[278], ZA[273], ZA[283]) + ZR30(ZA[283]); - ZA[39] = ZA[38] + K[21]; - ZA[55] = ZA[53] + K[22]; - - ZA[66] = ZR15(ZA[61]) + ZA[39]; - ZA[288] = ZA[286] + ZA[284]; - ZA[287] = ZA[268] + ZA[286]; - ZA[275] = ZA[272] + ZA[55]; - - ZA[291] = (ZCh(ZA[287], ZA[282], ZA[277]) + ZA[275]) + ZR26(ZA[287]); - ZA[289] = ZMa(ZA[283], ZA[278], ZA[288]) + ZR30(ZA[288]); - ZA[12] = ZA[10] + W31; - ZA[68] = ZA[66] + K[23]; - - ZA[67] = ZR15(ZA[53]) + ZA[12]; - ZA[293] = ZA[291] + ZA[289]; - ZA[292] = ZA[273] + ZA[291]; - ZA[280] = ZA[277] + ZA[68]; - - ZA[296] = (ZCh(ZA[292], ZA[287], ZA[282]) + ZA[280]) + ZR26(ZA[292]); - ZA[294] = ZMa(ZA[288], ZA[283], ZA[293]) + ZR30(ZA[293]); - ZA[2] = ZR25(ZA[0]); - ZA[69] = ZA[67] + K[24]; - ZA[44] = ZA[43] + W32; - - ZA[75] = ZR15(ZA[66]) + ZA[44]; - ZA[298] = ZA[296] + ZA[294]; - ZA[297] = ZA[278] + ZA[296]; - ZA[285] = ZA[282] + ZA[69]; - ZA[5] = ZA[2] + W17; - - ZA[301] = (ZCh(ZA[297], ZA[292], ZA[287]) + ZA[285]) + ZR26(ZA[297]); - ZA[299] = ZMa(ZA[293], ZA[288], ZA[298]) + ZR30(ZA[298]); - ZA[56] = ZA[52] + ZA[5]; - ZA[76] = ZA[75] + K[25]; - - ZA[34] = ZR25(ZA[32]) + ZA[0]; - ZA[70] = ZR15(ZA[67]) + ZA[56]; - ZA[302] = ZA[283] + ZA[301]; - ZA[303] = ZA[301] + ZA[299]; - ZA[290] = ZA[287] + ZA[76]; - - ZA[306] = (ZCh(ZA[302], ZA[297], ZA[292]) + ZA[290]) + ZR26(ZA[302]); - ZA[304] = ZMa(ZA[298], ZA[293], ZA[303]) + ZR30(ZA[303]); - ZA[6] = ZR25(ZA[3]); - ZA[77] = ZA[70] + K[26]; - ZA[50] = ZA[34] + ZA[48]; - - ZA[78] = ZR15(ZA[75]) + ZA[50]; - ZA[308] = ZA[306] + ZA[304]; - ZA[307] = ZA[288] + ZA[306]; - ZA[295] = ZA[292] + ZA[77]; - ZA[41] = ZA[32] + ZA[6]; - - ZA[311] = (ZCh(ZA[307], ZA[302], ZA[297]) + ZA[295]) + ZR26(ZA[307]); - ZA[309] = ZMa(ZA[303], ZA[298], ZA[308]) + ZR30(ZA[308]); - ZA[63] = ZA[41] + ZA[61]; - ZA[85] = ZA[78] + K[27]; - - ZA[37] = ZR25(ZA[35]) + ZA[3]; - ZA[79] = ZR15(ZA[70]) + ZA[63]; - ZA[312] = ZA[293] + ZA[311]; - ZA[313] = ZA[311] + ZA[309]; - ZA[300] = ZA[297] + ZA[85]; - - ZA[316] = (ZCh(ZA[312], ZA[307], ZA[302]) + ZA[300]) + ZR26(ZA[312]); - ZA[314] = ZMa(ZA[308], ZA[303], ZA[313]) + ZR30(ZA[313]); - ZA[9] = ZR25(ZA[7]); - ZA[86] = ZA[79] + K[28]; - ZA[57] = ZA[37] + ZA[53]; - - ZA[87] = ZR15(ZA[78]) + ZA[57]; - ZA[318] = ZA[316] + ZA[314]; - ZA[317] = ZA[298] + ZA[316]; - ZA[305] = ZA[302] + ZA[86]; - ZA[46] = ZA[35] + ZA[9]; - - ZA[321] = (ZCh(ZA[317], ZA[312], ZA[307]) + ZA[305]) + ZR26(ZA[317]); - ZA[319] = ZMa(ZA[313], ZA[308], ZA[318]) + ZR30(ZA[318]); - ZA[71] = ZA[46] + ZA[66]; - ZA[92] = ZA[87] + K[29]; - - ZA[42] = ZR25(ZA[38]) + ZA[7]; - ZA[88] = ZR15(ZA[79]) + ZA[71]; - ZA[322] = ZA[303] + ZA[321]; - ZA[323] = ZA[321] + ZA[319]; - ZA[310] = ZA[307] + ZA[92]; - - ZA[326] = (ZCh(ZA[322], ZA[317], ZA[312]) + ZA[310]) + ZR26(ZA[322]); - ZA[324] = ZMa(ZA[318], ZA[313], ZA[323]) + ZR30(ZA[323]); - ZA[14] = ZR25(ZA[10]); - ZA[93] = ZA[88] + K[30]; - ZA[72] = ZA[42] + ZA[67]; - - ZA[94] = ZR15(ZA[87]) + ZA[72]; - ZA[328] = ZA[326] + ZA[324]; - ZA[327] = ZA[308] + ZA[326]; - ZA[315] = ZA[312] + ZA[93]; - ZA[51] = ZA[38] + ZA[14]; - - ZA[331] = (ZCh(ZA[327], ZA[322], ZA[317]) + ZA[315]) + ZR26(ZA[327]); - ZA[329] = ZMa(ZA[323], ZA[318], ZA[328]) + ZR30(ZA[328]); - ZA[80] = ZA[51] + ZA[75]; - ZA[100] = ZA[94] + K[31]; - - ZA[47] = ZR25(ZA[43]) + ZA[10]; - ZA[95] = ZR15(ZA[88]) + ZA[80]; - ZA[332] = ZA[313] + ZA[331]; - ZA[333] = ZA[331] + ZA[329]; - ZA[320] = ZA[317] + ZA[100]; - - ZA[336] = (ZCh(ZA[332], ZA[327], ZA[322]) + ZA[320]) + ZR26(ZA[332]); - ZA[334] = ZMa(ZA[328], ZA[323], ZA[333]) + ZR30(ZA[333]); - ZA[81] = ZA[47] + ZA[70]; - ZA[101] = ZA[95] + K[32]; - - ZA[58] = ZR25(ZA[52]) + ZA[43]; - ZA[102] = ZR15(ZA[94]) + ZA[81]; - ZA[337] = ZA[318] + ZA[336]; - ZA[338] = ZA[336] + ZA[334]; - ZA[325] = ZA[322] + ZA[101]; - - ZA[341] = (ZCh(ZA[337], ZA[332], ZA[327]) + ZA[325]) + ZR26(ZA[337]); - ZA[339] = ZMa(ZA[333], ZA[328], ZA[338]) + ZR30(ZA[338]); - ZA[89] = ZA[58] + ZA[78]; - ZA[108] = ZA[102] + K[33]; - - ZA[59] = ZR25(ZA[48]) + ZA[52]; - ZA[103] = ZR15(ZA[95]) + ZA[89]; - ZA[342] = ZA[323] + ZA[341]; - ZA[343] = ZA[341] + ZA[339]; - ZA[330] = ZA[327] + ZA[108]; - - ZA[346] = (ZCh(ZA[342], ZA[337], ZA[332]) + ZA[330]) + ZR26(ZA[342]); - ZA[344] = ZMa(ZA[338], ZA[333], ZA[343]) + ZR30(ZA[343]); - ZA[90] = ZA[59] + ZA[79]; - ZA[109] = ZA[103] + K[34]; - - ZA[64] = ZR25(ZA[61]) + ZA[48]; - ZA[110] = ZR15(ZA[102]) + ZA[90]; - ZA[347] = ZA[328] + ZA[346]; - ZA[348] = ZA[346] + ZA[344]; - ZA[335] = ZA[332] + ZA[109]; - - ZA[351] = (ZCh(ZA[347], ZA[342], ZA[337]) + ZA[335]) + ZR26(ZA[347]); - ZA[349] = ZMa(ZA[343], ZA[338], ZA[348]) + ZR30(ZA[348]); - ZA[60] = ZR25(ZA[53]); - ZA[116] = ZA[110] + K[35]; - ZA[96] = ZA[87] + ZA[64]; - - ZA[111] = ZR15(ZA[103]) + ZA[96]; - ZA[353] = ZA[351] + ZA[349]; - ZA[352] = ZA[333] + ZA[351]; - ZA[340] = ZA[337] + ZA[116]; - ZA[65] = ZA[60] + ZA[61]; - - ZA[356] = (ZCh(ZA[352], ZA[347], ZA[342]) + ZA[340]) + ZR26(ZA[352]); - ZA[354] = ZMa(ZA[348], ZA[343], ZA[353]) + ZR30(ZA[353]); - ZA[97] = ZA[88] + ZA[65]; - ZA[117] = ZA[111] + K[36]; - - ZA[73] = ZR25(ZA[66]) + ZA[53]; - ZA[118] = ZR15(ZA[110]) + ZA[97]; - ZA[357] = ZA[338] + ZA[356]; - ZA[358] = ZA[356] + ZA[354]; - ZA[345] = ZA[342] + ZA[117]; - - ZA[361] = (ZCh(ZA[357], ZA[352], ZA[347]) + ZA[345]) + ZR26(ZA[357]); - ZA[359] = ZMa(ZA[353], ZA[348], ZA[358]) + ZR30(ZA[358]); - ZA[104] = ZA[73] + ZA[94]; - ZA[124] = ZA[118] + K[37]; - - ZA[74] = ZR25(ZA[67]) + ZA[66]; - ZA[119] = ZR15(ZA[111]) + ZA[104]; - ZA[362] = ZA[343] + ZA[361]; - ZA[363] = ZA[361] + ZA[359]; - ZA[350] = ZA[347] + ZA[124]; - - ZA[366] = (ZCh(ZA[362], ZA[357], ZA[352]) + ZA[350]) + ZR26(ZA[362]); - ZA[364] = ZMa(ZA[358], ZA[353], ZA[363]) + ZR30(ZA[363]); - ZA[105] = ZA[74] + ZA[95]; - ZA[125] = ZA[119] + K[38]; - - ZA[82] = ZR25(ZA[75]) + ZA[67]; - ZA[126] = ZR15(ZA[118]) + ZA[105]; - ZA[367] = ZA[348] + ZA[366]; - ZA[368] = ZA[366] + ZA[364]; - ZA[355] = ZA[352] + ZA[125]; - - ZA[371] = (ZCh(ZA[367], ZA[362], ZA[357]) + ZA[355]) + ZR26(ZA[367]); - ZA[369] = ZMa(ZA[363], ZA[358], ZA[368]) + ZR30(ZA[368]); - ZA[112] = ZA[102] + ZA[82]; - ZA[132] = ZA[126] + K[39]; - - ZA[83] = ZR25(ZA[70]) + ZA[75]; - ZA[127] = ZR15(ZA[119]) + ZA[112]; - ZA[372] = ZA[353] + ZA[371]; - ZA[373] = ZA[371] + ZA[369]; - ZA[360] = ZA[357] + ZA[132]; - - ZA[376] = (ZCh(ZA[372], ZA[367], ZA[362]) + ZA[360]) + ZR26(ZA[372]); - ZA[374] = ZMa(ZA[368], ZA[363], ZA[373]) + ZR30(ZA[373]); - ZA[113] = ZA[103] + ZA[83]; - ZA[133] = ZA[127] + K[40]; - - ZA[84] = ZR25(ZA[78]) + ZA[70]; - ZA[134] = ZR15(ZA[126]) + ZA[113]; - ZA[377] = ZA[358] + ZA[376]; - ZA[378] = ZA[376] + ZA[374]; - ZA[365] = ZA[362] + ZA[133]; - - ZA[381] = (ZCh(ZA[377], ZA[372], ZA[367]) + ZA[365]) + ZR26(ZA[377]); - ZA[379] = ZMa(ZA[373], ZA[368], ZA[378]) + ZR30(ZA[378]); - ZA[120] = ZA[110] + ZA[84]; - ZA[140] = ZA[134] + K[41]; - - ZA[91] = ZR25(ZA[79]) + ZA[78]; - ZA[135] = ZR15(ZA[127]) + ZA[120]; - ZA[382] = ZA[363] + ZA[381]; - ZA[383] = ZA[381] + ZA[379]; - ZA[370] = ZA[367] + ZA[140]; - - ZA[386] = (ZCh(ZA[382], ZA[377], ZA[372]) + ZA[370]) + ZR26(ZA[382]); - ZA[384] = ZMa(ZA[378], ZA[373], ZA[383]) + ZR30(ZA[383]); - ZA[121] = ZA[111] + ZA[91]; - ZA[141] = ZA[135] + K[42]; - - ZA[98] = ZR25(ZA[87]) + ZA[79]; - ZA[142] = ZR15(ZA[134]) + ZA[121]; - ZA[387] = ZA[368] + ZA[386]; - ZA[388] = ZA[386] + ZA[384]; - ZA[375] = ZA[372] + ZA[141]; - - ZA[391] = (ZCh(ZA[387], ZA[382], ZA[377]) + ZA[375]) + ZR26(ZA[387]); - ZA[389] = ZMa(ZA[383], ZA[378], ZA[388]) + ZR30(ZA[388]); - ZA[128] = ZA[118] + ZA[98]; - ZA[147] = ZA[142] + K[43]; - - ZA[99] = ZR25(ZA[88]) + ZA[87]; - ZA[143] = ZR15(ZA[135]) + ZA[128]; - ZA[392] = ZA[373] + ZA[391]; - ZA[393] = ZA[391] + ZA[389]; - ZA[380] = ZA[377] + ZA[147]; - - ZA[396] = (ZCh(ZA[392], ZA[387], ZA[382]) + ZA[380]) + ZR26(ZA[392]); - ZA[394] = ZMa(ZA[388], ZA[383], ZA[393]) + ZR30(ZA[393]); - ZA[129] = ZA[119] + ZA[99]; - ZA[148] = ZA[143] + K[44]; - - ZA[106] = ZR25(ZA[94]) + ZA[88]; - ZA[149] = ZR15(ZA[142]) + ZA[129]; - ZA[397] = ZA[378] + ZA[396]; - ZA[398] = ZA[396] + ZA[394]; - ZA[385] = ZA[382] + ZA[148]; - - ZA[401] = (ZCh(ZA[397], ZA[392], ZA[387]) + ZA[385]) + ZR26(ZA[397]); - ZA[399] = ZMa(ZA[393], ZA[388], ZA[398]) + ZR30(ZA[398]); - ZA[136] = ZA[126] + ZA[106]; - ZA[153] = ZA[149] + K[45]; - - ZA[107] = ZR25(ZA[95]) + ZA[94]; - ZA[150] = ZR15(ZA[143]) + ZA[136]; - ZA[402] = ZA[383] + ZA[401]; - ZA[403] = ZA[401] + ZA[399]; - ZA[390] = ZA[387] + ZA[153]; - - ZA[406] = (ZCh(ZA[402], ZA[397], ZA[392]) + ZA[390]) + ZR26(ZA[402]); - ZA[404] = ZMa(ZA[398], ZA[393], ZA[403]) + ZR30(ZA[403]); - ZA[137] = ZA[127] + ZA[107]; - ZA[154] = ZA[150] + K[46]; - - ZA[114] = ZR25(ZA[102]) + ZA[95]; - ZA[155] = ZR15(ZA[149]) + ZA[137]; - ZA[407] = ZA[388] + ZA[406]; - ZA[408] = ZA[406] + ZA[404]; - ZA[395] = ZA[392] + ZA[154]; - - ZA[411] = (ZCh(ZA[407], ZA[402], ZA[397]) + ZA[395]) + ZR26(ZA[407]); - ZA[409] = ZMa(ZA[403], ZA[398], ZA[408]) + ZR30(ZA[408]); - ZA[144] = ZA[134] + ZA[114]; - ZA[159] = ZA[155] + K[47]; - - ZA[115] = ZR25(ZA[103]) + ZA[102]; - ZA[156] = ZR15(ZA[150]) + ZA[144]; - ZA[412] = ZA[393] + ZA[411]; - ZA[413] = ZA[411] + ZA[409]; - ZA[400] = ZA[397] + ZA[159]; - - ZA[416] = (ZCh(ZA[412], ZA[407], ZA[402]) + ZA[400]) + ZR26(ZA[412]); - ZA[414] = ZMa(ZA[408], ZA[403], ZA[413]) + ZR30(ZA[413]); - ZA[145] = ZA[135] + ZA[115]; - ZA[160] = ZA[156] + K[48]; - - ZA[122] = ZR25(ZA[110]) + ZA[103]; - ZA[161] = ZR15(ZA[155]) + ZA[145]; - ZA[417] = ZA[398] + ZA[416]; - ZA[418] = ZA[416] + ZA[414]; - ZA[405] = ZA[402] + ZA[160]; - - ZA[421] = (ZCh(ZA[417], ZA[412], ZA[407]) + ZA[405]) + ZR26(ZA[417]); - ZA[419] = ZMa(ZA[413], ZA[408], ZA[418]) + ZR30(ZA[418]); - ZA[151] = ZA[142] + ZA[122]; - ZA[165] = ZA[161] + K[49]; - - ZA[123] = ZR25(ZA[111]) + ZA[110]; - ZA[162] = ZR15(ZA[156]) + ZA[151]; - ZA[422] = ZA[403] + ZA[421]; - ZA[423] = ZA[421] + ZA[419]; - ZA[410] = ZA[407] + ZA[165]; - - ZA[426] = (ZCh(ZA[422], ZA[417], ZA[412]) + ZA[410]) + ZR26(ZA[422]); - ZA[424] = ZMa(ZA[418], ZA[413], ZA[423]) + ZR30(ZA[423]); - ZA[152] = ZA[143] + ZA[123]; - ZA[166] = ZA[162] + K[50]; - - ZA[130] = ZR25(ZA[118]) + ZA[111]; - ZA[167] = ZR15(ZA[161]) + ZA[152]; - ZA[427] = ZA[408] + ZA[426]; - ZA[428] = ZA[426] + ZA[424]; - ZA[415] = ZA[412] + ZA[166]; - - ZA[431] = (ZCh(ZA[427], ZA[422], ZA[417]) + ZA[415]) + ZR26(ZA[427]); - ZA[429] = ZMa(ZA[423], ZA[418], ZA[428]) + ZR30(ZA[428]); - ZA[157] = ZA[149] + ZA[130]; - ZA[170] = ZA[167] + K[51]; - - ZA[131] = ZR25(ZA[119]) + ZA[118]; - ZA[168] = ZR15(ZA[162]) + ZA[157]; - ZA[432] = ZA[413] + ZA[431]; - ZA[433] = ZA[431] + ZA[429]; - ZA[420] = ZA[417] + ZA[170]; - - ZA[436] = (ZCh(ZA[432], ZA[427], ZA[422]) + ZA[420]) + ZR26(ZA[432]); - ZA[434] = ZMa(ZA[428], ZA[423], ZA[433]) + ZR30(ZA[433]); - ZA[158] = ZA[150] + ZA[131]; - ZA[171] = ZA[168] + K[52]; - - ZA[138] = ZR25(ZA[126]) + ZA[119]; - ZA[172] = ZR15(ZA[167]) + ZA[158]; - ZA[437] = ZA[418] + ZA[436]; - ZA[438] = ZA[436] + ZA[434]; - ZA[425] = ZA[422] + ZA[171]; - - ZA[441] = (ZCh(ZA[437], ZA[432], ZA[427]) + ZA[425]) + ZR26(ZA[437]); - ZA[439] = ZMa(ZA[433], ZA[428], ZA[438]) + ZR30(ZA[438]); - ZA[163] = ZA[155] + ZA[138]; - ZA[174] = ZA[172] + K[53]; - - ZA[139] = ZR25(ZA[127]) + ZA[126]; - ZA[173] = ZR15(ZA[168]) + ZA[163]; - ZA[442] = ZA[423] + ZA[441]; - ZA[443] = ZA[441] + ZA[439]; - ZA[430] = ZA[427] + ZA[174]; - - ZA[445] = (ZCh(ZA[442], ZA[437], ZA[432]) + ZA[430]) + ZR26(ZA[442]); - ZA[444] = ZMa(ZA[438], ZA[433], ZA[443]) + ZR30(ZA[443]); - ZA[164] = ZA[156] + ZA[139]; - ZA[175] = ZA[173] + K[54]; - - ZA[146] = ZR25(ZA[134]) + ZA[127]; - ZA[176] = ZR15(ZA[172]) + ZA[164]; - ZA[446] = ZA[428] + ZA[445]; - ZA[447] = ZA[445] + ZA[444]; - ZA[435] = ZA[432] + ZA[175]; - - ZA[449] = (ZCh(ZA[446], ZA[442], ZA[437]) + ZA[435]) + ZR26(ZA[446]); - ZA[448] = ZMa(ZA[443], ZA[438], ZA[447]) + ZR30(ZA[447]); - ZA[169] = ZA[161] + ZA[146]; - ZA[178] = ZA[176] + K[55]; - - ZA[177] = ZR15(ZA[173]) + ZA[169]; - ZA[451] = ZA[449] + ZA[448]; - ZA[450] = ZA[433] + ZA[449]; - ZA[440] = ZA[437] + ZA[178]; - - ZA[453] = (ZCh(ZA[450], ZA[446], ZA[442]) + ZA[440]) + ZR26(ZA[450]); - ZA[452] = ZMa(ZA[447], ZA[443], ZA[451]) + ZR30(ZA[451]); - ZA[179] = ZA[177] + K[56]; - - ZA[454] = ZA[438] + ZA[453]; - ZA[494] = ZA[442] + ZA[179]; - ZA[455] = ZA[453] + ZA[452]; - - ZA[457] = (ZCh(ZA[454], ZA[450], ZA[446]) + ZA[494]) + ZR26(ZA[454]); - ZA[456] = ZMa(ZA[451], ZA[447], ZA[455]) + ZR30(ZA[455]); - - ZA[459] = ZA[457] + ZA[456]; - - ZA[461] = ZA[455] + state1; - ZA[460] = ZA[459] + state0; - - ZA[495] = ZA[460] + K[57]; - ZA[469] = ZA[461] + K[58]; - - ZA[498] = (ZCh(ZA[495], K[59], K[60]) + ZA[469]) + ZR26(ZA[495]); - ZA[462] = ZA[451] + state2; - - ZA[496] = ZA[460] + K[61]; - ZA[506] = ZA[498] + K[62]; - ZA[470] = ZA[462] + K[63]; - - ZA[507] = (ZCh(ZA[506], ZA[495], K[59]) + ZA[470]) + ZR26(ZA[506]); - ZA[500] = ZMa(K[64], K[65], ZA[496]) + ZR30(ZA[496]); - ZA[463] = ZA[447] + state3; - - ZA[458] = ZA[443] + ZA[457]; - ZA[499] = ZA[498] + ZA[500]; - ZA[508] = ZA[507] + K[65]; - ZA[473] = ZA[463] + K[66]; - - ZA[510] = (ZCh(ZA[508], ZA[506], ZA[495]) + ZA[473]) + ZR26(ZA[508]); - ZA[928] = ZMa(ZA[496], K[64], ZA[499]) + ZR30(ZA[499]); - ZA[464] = ZA[458] + state4; - - ZA[476] = ZA[464] + ZA[460] + K[67]; - ZA[511] = ZA[510] + K[64]; - ZA[509] = ZA[928] + ZA[507]; - ZA[465] = ZA[454] + state5; - - ZA[514] = (ZCh(ZA[511], ZA[508], ZA[506]) + ZA[476]) + ZR26(ZA[511]); - ZA[512] = ZMa(ZA[499], ZA[496], ZA[509]) + ZR30(ZA[509]); - ZA[478] = ZA[465] + K[68]; - - ZA[519] = ZA[506] + ZA[478]; - ZA[516] = ZA[496] + ZA[514]; - ZA[513] = ZA[510] + ZA[512]; - ZA[466] = ZA[450] + state6; - - ZA[520] = (ZCh(ZA[516], ZA[511], ZA[508]) + ZA[519]) + ZR26(ZA[516]); - ZA[515] = ZMa(ZA[509], ZA[499], ZA[513]) + ZR30(ZA[513]); - ZA[480] = ZA[466] + K[69]; - - ZA[524] = ZA[508] + ZA[480]; - ZA[521] = ZA[499] + ZA[520]; - ZA[517] = ZA[514] + ZA[515]; - ZA[467] = ZA[446] + state7; - - ZA[525] = (ZCh(ZA[521], ZA[516], ZA[511]) + ZA[524]) + ZR26(ZA[521]); - ZA[522] = ZMa(ZA[513], ZA[509], ZA[517]) + ZR30(ZA[517]); - ZA[484] = ZA[467] + K[70]; - - ZA[529] = ZA[511] + ZA[484]; - ZA[526] = ZA[509] + ZA[525]; - ZA[523] = ZA[520] + ZA[522]; - - ZA[530] = (ZCh(ZA[526], ZA[521], ZA[516]) + ZA[529]) + ZR26(ZA[526]); - ZA[550] = ZMa(ZA[517], ZA[513], ZA[523]) + ZR30(ZA[523]); - - ZA[531] = ZA[513] + ZA[530]; - ZA[533] = ZA[516] + K[71]; - ZA[527] = ZA[550] + ZA[525]; - - ZA[534] = (ZCh(ZA[531], ZA[526], ZA[521]) + ZA[533]) + ZR26(ZA[531]); - ZA[551] = ZMa(ZA[523], ZA[517], ZA[527]) + ZR30(ZA[527]); - - ZA[535] = ZA[517] + ZA[534]; - ZA[538] = ZA[521] + K[1]; - ZA[532] = ZA[551] + ZA[530]; - - ZA[539] = (ZCh(ZA[535], ZA[531], ZA[526]) + ZA[538]) + ZR26(ZA[535]); - ZA[552] = ZMa(ZA[527], ZA[523], ZA[532]) + ZR30(ZA[532]); - - ZA[540] = ZA[523] + ZA[539]; - ZA[542] = ZA[526] + K[2]; - ZA[536] = ZA[552] + ZA[534]; - - ZA[543] = (ZCh(ZA[540], ZA[535], ZA[531]) + ZA[542]) + ZR26(ZA[540]); - ZA[553] = ZMa(ZA[532], ZA[527], ZA[536]) + ZR30(ZA[536]); - - ZA[544] = ZA[527] + ZA[543]; - ZA[555] = ZA[531] + K[3]; - ZA[541] = ZA[553] + ZA[539]; - - ZA[558] = (ZCh(ZA[544], ZA[540], ZA[535]) + ZA[555]) + ZR26(ZA[544]); - ZA[547] = ZMa(ZA[536], ZA[532], ZA[541]) + ZR30(ZA[541]); - - ZA[559] = ZA[532] + ZA[558]; - ZA[556] = ZA[535] + K[4]; - ZA[545] = ZA[547] + ZA[543]; - - ZA[562] = (ZCh(ZA[559], ZA[544], ZA[540]) + ZA[556]) + ZR26(ZA[559]); - ZA[561] = ZMa(ZA[541], ZA[536], ZA[545]) + ZR30(ZA[545]); - - ZA[563] = ZA[536] + ZA[562]; - ZA[560] = ZA[561] + ZA[558]; - ZA[557] = ZA[540] + K[5]; - - ZA[568] = (ZCh(ZA[563], ZA[559], ZA[544]) + ZA[557]) + ZR26(ZA[563]); - ZA[564] = ZMa(ZA[545], ZA[541], ZA[560]) + ZR30(ZA[560]); - - ZA[569] = ZA[541] + ZA[568]; - ZA[572] = ZA[544] + K[6]; - ZA[565] = ZA[562] + ZA[564]; - - ZA[574] = (ZCh(ZA[569], ZA[563], ZA[559]) + ZA[572]) + ZR26(ZA[569]); - ZA[570] = ZMa(ZA[560], ZA[545], ZA[565]) + ZR30(ZA[565]); - ZA[468] = ZR25(ZA[461]); - - ZA[497] = ZA[468] + ZA[460]; - ZA[575] = ZA[545] + ZA[574]; - ZA[571] = ZA[568] + ZA[570]; - ZA[573] = ZA[559] + K[72]; - - ZA[578] = (ZCh(ZA[575], ZA[569], ZA[563]) + ZA[573]) + ZR26(ZA[575]); - ZA[576] = ZMa(ZA[565], ZA[560], ZA[571]) + ZR30(ZA[571]); - ZA[929] = ZR25(ZA[462]); - ZA[503] = ZA[497] + 0xe49b69c1U; - - ZA[471] = ZA[929] + ZA[461] + K[74]; - ZA[582] = ZA[563] + ZA[503]; - ZA[579] = ZA[560] + ZA[578]; - ZA[577] = ZA[574] + ZA[576]; - - ZA[583] = (ZCh(ZA[579], ZA[575], ZA[569]) + ZA[582]) + ZR26(ZA[579]); - ZA[580] = ZMa(ZA[571], ZA[565], ZA[577]) + ZR30(ZA[577]); - ZA[488] = ZA[471] + K[75]; - - ZA[472] = ZR25(ZA[463]) + ZA[462]; - ZA[587] = ZA[569] + ZA[488]; - ZA[584] = ZA[565] + ZA[583]; - ZA[581] = ZA[578] + ZA[580]; - - ZA[588] = (ZCh(ZA[584], ZA[579], ZA[575]) + ZA[587]) + ZR26(ZA[584]); - ZA[586] = ZMa(ZA[577], ZA[571], ZA[581]) + ZR30(ZA[581]); - ZA[501] = ZR15(ZA[497]) + ZA[472]; - ZA[475] = ZR15(ZA[471]); - ZA[926] = ZA[575] + K[8]; - - ZA[474] = ZA[475] + ZA[463] + ZR25(ZA[464]); - ZA[927] = ZA[926] + ZA[501]; - ZA[589] = ZA[571] + ZA[588]; - ZA[585] = ZA[583] + ZA[586]; - - ZA[592] = (ZCh(ZA[589], ZA[584], ZA[579]) + ZA[927]) + ZR26(ZA[589]); - ZA[590] = ZMa(ZA[581], ZA[577], ZA[585]) + ZR30(ZA[585]); - ZA[477] = ZR25(ZA[465]) + ZA[464]; - ZA[489] = ZA[474] + K[9]; - - ZA[518] = ZR15(ZA[501]) + ZA[477]; - ZA[479] = ZR25(ZA[466]); - ZA[596] = ZA[579] + ZA[489]; - ZA[593] = ZA[577] + ZA[592]; - ZA[591] = ZA[588] + ZA[590]; - - ZA[597] = (ZCh(ZA[593], ZA[589], ZA[584]) + ZA[596]) + ZR26(ZA[593]); - ZA[594] = ZMa(ZA[585], ZA[581], ZA[591]) + ZR30(ZA[591]); - ZA[481] = ZA[479] + ZA[465]; - ZA[601] = ZA[518] + K[11]; - - ZA[482] = ZR15(ZA[474]) + ZA[481]; - ZA[602] = ZA[584] + ZA[601]; - ZA[598] = ZA[581] + ZA[597]; - ZA[595] = ZA[592] + ZA[594]; - - ZA[632] = (ZCh(ZA[598], ZA[593], ZA[589]) + ZA[602]) + ZR26(ZA[598]); - ZA[599] = ZMa(ZA[591], ZA[585], ZA[595]) + ZR30(ZA[595]); - ZA[483] = ZA[466] + K[76] + ZR25(ZA[467]); - ZA[490] = ZA[482] + K[12]; - - ZA[528] = ZR15(ZA[518]) + ZA[483]; - ZA[736] = ZA[585] + ZA[632]; - ZA[605] = ZA[589] + ZA[490]; - ZA[600] = ZA[597] + ZA[599]; - ZA[485] = ZA[467] + K[77]; - - ZA[738] = (ZCh(ZA[736], ZA[598], ZA[593]) + ZA[605]) + ZR26(ZA[736]); - ZA[744] = ZMa(ZA[595], ZA[591], ZA[600]) + ZR30(ZA[600]); - ZA[487] = ZR15(ZA[482]) + ZA[485]; - ZA[603] = ZA[528] + K[14]; - - ZA[502] = ZA[497] + ZA[487]; - ZA[739] = ZA[591] + ZA[738]; - ZA[604] = ZA[593] + ZA[603]; - ZA[737] = ZA[744] + ZA[632]; - - ZA[741] = (ZCh(ZA[739], ZA[736], ZA[598]) + ZA[604]) + ZR26(ZA[739]); - ZA[745] = ZMa(ZA[600], ZA[595], ZA[737]) + ZR30(ZA[737]); - ZA[486] = ZA[471] + K[10]; - ZA[606] = ZA[502] + K[15]; - - ZA[537] = ZR15(ZA[528]) + ZA[486]; - ZA[742] = ZA[595] + ZA[741]; - ZA[613] = ZA[598] + ZA[606]; - ZA[740] = ZA[745] + ZA[738]; - - ZA[747] = (ZCh(ZA[742], ZA[739], ZA[736]) + ZA[613]) + ZR26(ZA[742]); - ZA[746] = ZMa(ZA[737], ZA[600], ZA[740]) + ZR30(ZA[740]); - ZA[607] = ZA[537] + K[16]; - - ZA[546] = ZR15(ZA[502]) + ZA[501]; - ZA[751] = ZA[736] + ZA[607]; - ZA[748] = ZA[600] + ZA[747]; - ZA[743] = ZA[746] + ZA[741]; - - ZA[752] = (ZCh(ZA[748], ZA[742], ZA[739]) + ZA[751]) + ZR26(ZA[748]); - ZA[749] = ZMa(ZA[740], ZA[737], ZA[743]) + ZR30(ZA[743]); - ZA[608] = ZA[546] + K[17]; - - ZA[554] = ZR15(ZA[537]) + ZA[474]; - ZA[756] = ZA[739] + ZA[608]; - ZA[753] = ZA[737] + ZA[752]; - ZA[750] = ZA[747] + ZA[749]; - - ZA[757] = (ZCh(ZA[753], ZA[748], ZA[742]) + ZA[756]) + ZR26(ZA[753]); - ZA[754] = ZMa(ZA[743], ZA[740], ZA[750]) + ZR30(ZA[750]); - ZA[609] = ZA[554] + K[18]; - - ZA[566] = ZR15(ZA[546]) + ZA[518]; - ZA[761] = ZA[742] + ZA[609]; - ZA[758] = ZA[740] + ZA[757]; - ZA[755] = ZA[752] + ZA[754]; - - ZA[762] = (ZCh(ZA[758], ZA[753], ZA[748]) + ZA[761]) + ZR26(ZA[758]); - ZA[759] = ZMa(ZA[750], ZA[743], ZA[755]) + ZR30(ZA[755]); - ZA[610] = ZA[566] + K[19]; - - ZA[567] = ZR15(ZA[554]) + ZA[482]; - ZA[766] = ZA[748] + ZA[610]; - ZA[763] = ZA[743] + ZA[762]; - ZA[760] = ZA[757] + ZA[759]; - - ZA[767] = (ZCh(ZA[763], ZA[758], ZA[753]) + ZA[766]) + ZR26(ZA[763]); - ZA[764] = ZMa(ZA[755], ZA[750], ZA[760]) + ZR30(ZA[760]); - ZA[611] = ZA[567] + K[20]; - - ZA[614] = ZR15(ZA[566]) + ZA[528]; - ZA[771] = ZA[753] + ZA[611]; - ZA[768] = ZA[750] + ZA[767]; - ZA[765] = ZA[762] + ZA[764]; - - ZA[772] = (ZCh(ZA[768], ZA[763], ZA[758]) + ZA[771]) + ZR26(ZA[768]); - ZA[769] = ZMa(ZA[760], ZA[755], ZA[765]) + ZR30(ZA[765]); - ZA[612] = ZA[502] + K[78]; - ZA[615] = ZA[614] + K[22]; - - ZA[616] = ZR15(ZA[567]) + ZA[612]; - ZA[504] = ZR25(ZA[497]) + K[76]; - ZA[776] = ZA[758] + ZA[615]; - ZA[773] = ZA[755] + ZA[772]; - ZA[770] = ZA[767] + ZA[769]; - - ZA[777] = (ZCh(ZA[773], ZA[768], ZA[763]) + ZA[776]) + ZR26(ZA[773]); - ZA[774] = ZMa(ZA[765], ZA[760], ZA[770]) + ZR30(ZA[770]); - ZA[492] = ZR25(ZA[471]); - ZA[618] = ZA[537] + ZA[504]; - ZA[617] = ZA[616] + K[23]; - - ZA[619] = ZR15(ZA[614]) + ZA[618]; - ZA[781] = ZA[763] + ZA[617]; - ZA[778] = ZA[760] + ZA[777]; - ZA[775] = ZA[772] + ZA[774]; - ZA[505] = ZA[492] + ZA[497]; - - ZA[782] = (ZCh(ZA[778], ZA[773], ZA[768]) + ZA[781]) + ZR26(ZA[778]); - ZA[779] = ZMa(ZA[770], ZA[765], ZA[775]) + ZR30(ZA[775]); - ZA[621] = ZA[505] + ZA[546]; - ZA[620] = ZA[619] + K[24]; - - ZA[622] = ZR15(ZA[616]) + ZA[621]; - ZA[625] = ZR25(ZA[501]); - ZA[786] = ZA[768] + ZA[620]; - ZA[783] = ZA[765] + ZA[782]; - ZA[624] = ZA[554] + ZA[471]; - ZA[780] = ZA[777] + ZA[779]; - - ZA[787] = (ZCh(ZA[783], ZA[778], ZA[773]) + ZA[786]) + ZR26(ZA[783]); - ZA[784] = ZMa(ZA[775], ZA[770], ZA[780]) + ZR30(ZA[780]); - ZA[493] = ZR25(ZA[474]); - ZA[626] = ZA[625] + ZA[624]; - ZA[623] = ZA[622] + K[25]; - - ZA[627] = ZR15(ZA[619]) + ZA[626]; - ZA[791] = ZA[773] + ZA[623]; - ZA[788] = ZA[770] + ZA[787]; - ZA[785] = ZA[782] + ZA[784]; - ZA[629] = ZA[493] + ZA[501]; - - ZA[792] = (ZCh(ZA[788], ZA[783], ZA[778]) + ZA[791]) + ZR26(ZA[788]); - ZA[789] = ZMa(ZA[780], ZA[775], ZA[785]) + ZR30(ZA[785]); - ZA[630] = ZA[566] + ZA[629]; - ZA[628] = ZA[627] + K[26]; - - ZA[634] = ZR25(ZA[518]) + ZA[474]; - ZA[631] = ZR15(ZA[622]) + ZA[630]; - ZA[796] = ZA[778] + ZA[628]; - ZA[793] = ZA[775] + ZA[792]; - ZA[790] = ZA[787] + ZA[789]; - - ZA[797] = (ZCh(ZA[793], ZA[788], ZA[783]) + ZA[796]) + ZR26(ZA[793]); - ZA[794] = ZMa(ZA[785], ZA[780], ZA[790]) + ZR30(ZA[790]); - ZA[491] = ZR25(ZA[482]); - ZA[635] = ZA[567] + ZA[634]; - ZA[633] = ZA[631] + K[27]; - - ZA[636] = ZR15(ZA[627]) + ZA[635]; - ZA[801] = ZA[783] + ZA[633]; - ZA[798] = ZA[780] + ZA[797]; - ZA[795] = ZA[792] + ZA[794]; - ZA[638] = ZA[491] + ZA[518]; - - ZA[802] = (ZCh(ZA[798], ZA[793], ZA[788]) + ZA[801]) + ZR26(ZA[798]); - ZA[799] = ZMa(ZA[790], ZA[785], ZA[795]) + ZR30(ZA[795]); - ZA[639] = ZA[638] + ZA[614]; - ZA[637] = ZA[636] + K[28]; - - ZA[642] = ZR25(ZA[528]) + ZA[482]; - ZA[640] = ZR15(ZA[631]) + ZA[639]; - ZA[806] = ZA[788] + ZA[637]; - ZA[803] = ZA[785] + ZA[802]; - ZA[800] = ZA[797] + ZA[799]; - - ZA[807] = (ZCh(ZA[803], ZA[798], ZA[793]) + ZA[806]) + ZR26(ZA[803]); - ZA[804] = ZMa(ZA[795], ZA[790], ZA[800]) + ZR30(ZA[800]); - ZA[643] = ZA[616] + ZA[642]; - ZA[641] = ZA[640] + K[29]; - - ZA[646] = ZR25(ZA[502]) + ZA[528]; - ZA[644] = ZR15(ZA[636]) + ZA[643]; - ZA[811] = ZA[793] + ZA[641]; - ZA[808] = ZA[790] + ZA[807]; - ZA[805] = ZA[802] + ZA[804]; - - ZA[812] = (ZCh(ZA[808], ZA[803], ZA[798]) + ZA[811]) + ZR26(ZA[808]); - ZA[809] = ZMa(ZA[800], ZA[795], ZA[805]) + ZR30(ZA[805]); - ZA[647] = ZA[619] + ZA[646]; - ZA[645] = ZA[644] + K[30]; - - ZA[650] = ZR25(ZA[537]) + ZA[502]; - ZA[648] = ZR15(ZA[640]) + ZA[647]; - ZA[816] = ZA[798] + ZA[645]; - ZA[813] = ZA[795] + ZA[812]; - ZA[810] = ZA[807] + ZA[809]; - - ZA[817] = (ZCh(ZA[813], ZA[808], ZA[803]) + ZA[816]) + ZR26(ZA[813]); - ZA[814] = ZMa(ZA[805], ZA[800], ZA[810]) + ZR30(ZA[810]); - ZA[925] = ZA[622] + ZA[650]; - ZA[649] = ZA[648] + K[31]; - - ZA[653] = ZR25(ZA[546]) + ZA[537]; - ZA[651] = ZR15(ZA[644]) + ZA[925]; - ZA[821] = ZA[803] + ZA[649]; - ZA[818] = ZA[800] + ZA[817]; - ZA[815] = ZA[812] + ZA[814]; - - ZA[822] = (ZCh(ZA[818], ZA[813], ZA[808]) + ZA[821]) + ZR26(ZA[818]); - ZA[819] = ZMa(ZA[810], ZA[805], ZA[815]) + ZR30(ZA[815]); - ZA[654] = ZA[627] + ZA[653]; - ZA[652] = ZA[651] + K[32]; - - ZA[657] = ZR25(ZA[554]) + ZA[546]; - ZA[655] = ZR15(ZA[648]) + ZA[654]; - ZA[826] = ZA[808] + ZA[652]; - ZA[823] = ZA[805] + ZA[822]; - ZA[820] = ZA[817] + ZA[819]; - - ZA[827] = (ZCh(ZA[823], ZA[818], ZA[813]) + ZA[826]) + ZR26(ZA[823]); - ZA[824] = ZMa(ZA[815], ZA[810], ZA[820]) + ZR30(ZA[820]); - ZA[658] = ZA[631] + ZA[657]; - ZA[656] = ZA[655] + K[33]; - - ZA[661] = ZR25(ZA[566]) + ZA[554]; - ZA[659] = ZR15(ZA[651]) + ZA[658]; - ZA[831] = ZA[813] + ZA[656]; - ZA[828] = ZA[810] + ZA[827]; - ZA[825] = ZA[822] + ZA[824]; - - ZA[832] = (ZCh(ZA[828], ZA[823], ZA[818]) + ZA[831]) + ZR26(ZA[828]); - ZA[829] = ZMa(ZA[820], ZA[815], ZA[825]) + ZR30(ZA[825]); - ZA[662] = ZA[636] + ZA[661]; - ZA[660] = ZA[659] + K[34]; - - ZA[665] = ZR25(ZA[567]) + ZA[566]; - ZA[663] = ZR15(ZA[655]) + ZA[662]; - ZA[836] = ZA[818] + ZA[660]; - ZA[833] = ZA[815] + ZA[832]; - ZA[830] = ZA[827] + ZA[829]; - - ZA[837] = (ZCh(ZA[833], ZA[828], ZA[823]) + ZA[836]) + ZR26(ZA[833]); - ZA[834] = ZMa(ZA[825], ZA[820], ZA[830]) + ZR30(ZA[830]); - ZA[666] = ZA[640] + ZA[665]; - ZA[664] = ZA[663] + K[35]; - - ZA[669] = ZR25(ZA[614]) + ZA[567]; - ZA[667] = ZR15(ZA[659]) + ZA[666]; - ZA[841] = ZA[823] + ZA[664]; - ZA[838] = ZA[820] + ZA[837]; - ZA[835] = ZA[832] + ZA[834]; - - ZA[842] = (ZCh(ZA[838], ZA[833], ZA[828]) + ZA[841]) + ZR26(ZA[838]); - ZA[839] = ZMa(ZA[830], ZA[825], ZA[835]) + ZR30(ZA[835]); - ZA[670] = ZA[644] + ZA[669]; - ZA[668] = ZA[667] + K[36]; - - ZA[677] = ZR25(ZA[616]) + ZA[614]; - ZA[671] = ZR15(ZA[663]) + ZA[670]; - ZA[846] = ZA[828] + ZA[668]; - ZA[843] = ZA[825] + ZA[842]; - ZA[840] = ZA[837] + ZA[839]; - - ZA[847] = (ZCh(ZA[843], ZA[838], ZA[833]) + ZA[846]) + ZR26(ZA[843]); - ZA[844] = ZMa(ZA[835], ZA[830], ZA[840]) + ZR30(ZA[840]); - ZA[678] = ZA[648] + ZA[677]; - ZA[676] = ZA[671] + K[37]; - - ZA[682] = ZR25(ZA[619]) + ZA[616]; - ZA[679] = ZR15(ZA[667]) + ZA[678]; - ZA[851] = ZA[833] + ZA[676]; - ZA[848] = ZA[830] + ZA[847]; - ZA[845] = ZA[842] + ZA[844]; - - ZA[852] = (ZCh(ZA[848], ZA[843], ZA[838]) + ZA[851]) + ZR26(ZA[848]); - ZA[849] = ZMa(ZA[840], ZA[835], ZA[845]) + ZR30(ZA[845]); - ZA[683] = ZA[651] + ZA[682]; - ZA[680] = ZA[679] + K[38]; - - ZA[686] = ZR25(ZA[622]) + ZA[619]; - ZA[684] = ZR15(ZA[671]) + ZA[683]; - ZA[856] = ZA[838] + ZA[680]; - ZA[853] = ZA[835] + ZA[852]; - ZA[850] = ZA[847] + ZA[849]; - - ZA[857] = (ZCh(ZA[853], ZA[848], ZA[843]) + ZA[856]) + ZR26(ZA[853]); - ZA[854] = ZMa(ZA[845], ZA[840], ZA[850]) + ZR30(ZA[850]); - ZA[687] = ZA[655] + ZA[686]; - ZA[685] = ZA[684] + K[39]; - - ZA[690] = ZR25(ZA[627]) + ZA[622]; - ZA[688] = ZR15(ZA[679]) + ZA[687]; - ZA[861] = ZA[843] + ZA[685]; - ZA[858] = ZA[840] + ZA[857]; - ZA[855] = ZA[852] + ZA[854]; - - ZA[862] = (ZCh(ZA[858], ZA[853], ZA[848]) + ZA[861]) + ZR26(ZA[858]); - ZA[859] = ZMa(ZA[850], ZA[845], ZA[855]) + ZR30(ZA[855]); - ZA[691] = ZA[659] + ZA[690]; - ZA[689] = ZA[688] + K[40]; - - ZA[694] = ZR25(ZA[631]) + ZA[627]; - ZA[692] = ZR15(ZA[684]) + ZA[691]; - ZA[866] = ZA[848] + ZA[689]; - ZA[863] = ZA[845] + ZA[862]; - ZA[860] = ZA[857] + ZA[859]; - - ZA[867] = (ZCh(ZA[863], ZA[858], ZA[853]) + ZA[866]) + ZR26(ZA[863]); - ZA[864] = ZMa(ZA[855], ZA[850], ZA[860]) + ZR30(ZA[860]); - ZA[695] = ZA[663] + ZA[694]; - ZA[693] = ZA[692] + K[41]; - - ZA[698] = ZR25(ZA[636]) + ZA[631]; - ZA[696] = ZR15(ZA[688]) + ZA[695]; - ZA[871] = ZA[853] + ZA[693]; - ZA[868] = ZA[850] + ZA[867]; - ZA[865] = ZA[862] + ZA[864]; - - ZA[873] = (ZCh(ZA[868], ZA[863], ZA[858]) + ZA[871]) + ZR26(ZA[868]); - ZA[869] = ZMa(ZA[860], ZA[855], ZA[865]) + ZR30(ZA[865]); - ZA[699] = ZA[667] + ZA[698]; - ZA[697] = ZA[696] + K[42]; - - ZA[702] = ZR25(ZA[640]) + ZA[636]; - ZA[700] = ZR15(ZA[692]) + ZA[699]; - ZA[877] = ZA[858] + ZA[697]; - ZA[874] = ZA[855] + ZA[873]; - ZA[870] = ZA[867] + ZA[869]; - - ZA[878] = (ZCh(ZA[874], ZA[868], ZA[863]) + ZA[877]) + ZR26(ZA[874]); - ZA[875] = ZMa(ZA[865], ZA[860], ZA[870]) + ZR30(ZA[870]); - ZA[703] = ZA[671] + ZA[702]; - ZA[701] = ZA[700] + K[43]; - - ZA[706] = ZR25(ZA[644]) + ZA[640]; - ZA[704] = ZR15(ZA[696]) + ZA[703]; - ZA[882] = ZA[863] + ZA[701]; - ZA[879] = ZA[860] + ZA[878]; - ZA[876] = ZA[873] + ZA[875]; - - ZA[883] = (ZCh(ZA[879], ZA[874], ZA[868]) + ZA[882]) + ZR26(ZA[879]); - ZA[880] = ZMa(ZA[870], ZA[865], ZA[876]) + ZR30(ZA[876]); - ZA[707] = ZA[679] + ZA[706]; - ZA[705] = ZA[704] + K[44]; - - ZA[710] = ZR25(ZA[648]) + ZA[644]; - ZA[708] = ZR15(ZA[700]) + ZA[707]; - ZA[887] = ZA[868] + ZA[705]; - ZA[884] = ZA[865] + ZA[883]; - ZA[881] = ZA[878] + ZA[880]; - - ZA[888] = (ZCh(ZA[884], ZA[879], ZA[874]) + ZA[887]) + ZR26(ZA[884]); - ZA[885] = ZMa(ZA[876], ZA[870], ZA[881]) + ZR30(ZA[881]); - ZA[711] = ZA[684] + ZA[710]; - ZA[709] = ZA[708] + K[45]; - - ZA[714] = ZR25(ZA[651]) + ZA[648]; - ZA[712] = ZR15(ZA[704]) + ZA[711]; - ZA[892] = ZA[874] + ZA[709]; - ZA[889] = ZA[870] + ZA[888]; - ZA[886] = ZA[883] + ZA[885]; - - ZA[893] = (ZCh(ZA[889], ZA[884], ZA[879]) + ZA[892]) + ZR26(ZA[889]); - ZA[890] = ZMa(ZA[881], ZA[876], ZA[886]) + ZR30(ZA[886]); - ZA[715] = ZA[688] + ZA[714]; - ZA[713] = ZA[712] + K[46]; - - ZA[718] = ZR25(ZA[655]) + ZA[651]; - ZA[716] = ZR15(ZA[708]) + ZA[715]; - ZA[897] = ZA[879] + ZA[713]; - ZA[894] = ZA[876] + ZA[893]; - ZA[891] = ZA[888] + ZA[890]; - - ZA[898] = (ZCh(ZA[894], ZA[889], ZA[884]) + ZA[897]) + ZR26(ZA[894]); - ZA[895] = ZMa(ZA[886], ZA[881], ZA[891]) + ZR30(ZA[891]); - ZA[719] = ZA[692] + ZA[718]; - ZA[717] = ZA[716] + K[47]; - - ZA[722] = ZR25(ZA[659]) + ZA[655]; - ZA[720] = ZR15(ZA[712]) + ZA[719]; - ZA[902] = ZA[884] + ZA[717]; - ZA[899] = ZA[881] + ZA[898]; - ZA[896] = ZA[893] + ZA[895]; - - ZA[903] = (ZCh(ZA[899], ZA[894], ZA[889]) + ZA[902]) + ZR26(ZA[899]); - ZA[900] = ZMa(ZA[891], ZA[886], ZA[896]) + ZR30(ZA[896]); - ZA[723] = ZA[696] + ZA[722]; - ZA[721] = ZA[720] + K[48]; - - ZA[672] = ZR25(ZA[663]) + ZA[659]; - ZA[724] = ZR15(ZA[716]) + ZA[723]; - ZA[907] = ZA[889] + ZA[721]; - ZA[904] = ZA[886] + ZA[903]; - ZA[901] = ZA[898] + ZA[900]; - - ZA[908] = (ZCh(ZA[904], ZA[899], ZA[894]) + ZA[907]) + ZR26(ZA[904]); - ZA[905] = ZMa(ZA[896], ZA[891], ZA[901]) + ZR30(ZA[901]); - ZA[673] = ZR25(ZA[667]) + ZA[663]; - ZA[726] = ZA[700] + ZA[672]; - ZA[725] = ZA[724] + K[49]; - - ZA[727] = ZR15(ZA[720]) + ZA[726]; - ZA[912] = ZA[894] + ZA[725]; - ZA[909] = ZA[891] + ZA[908]; - ZA[906] = ZA[903] + ZA[905]; - ZA[675] = ZA[667] + K[52]; - ZA[729] = ZA[704] + ZA[673]; - - ZA[913] = (ZCh(ZA[909], ZA[904], ZA[899]) + ZA[912]) + ZR26(ZA[909]); - ZA[910] = ZMa(ZA[901], ZA[896], ZA[906]) + ZR30(ZA[906]); - ZA[674] = ZR25(ZA[671]) + ZA[675]; - ZA[730] = ZR15(ZA[724]) + ZA[729]; - ZA[728] = ZA[727] + K[50]; - - ZA[681] = ZR25(ZA[679]) + ZA[671]; - ZA[917] = ZA[899] + ZA[901] + ZA[728]; - ZA[914] = ZA[896] + ZA[913]; - ZA[911] = ZA[908] + ZA[910]; - ZA[732] = ZA[708] + ZA[674]; - ZA[731] = ZA[730] + K[51]; - - ZA[918] = (ZCh(ZA[914], ZA[909], ZA[904]) + ZA[917]) + ZR26(ZA[914]); - ZA[915] = ZMa(ZA[906], ZA[901], ZA[911]) + ZR30(ZA[911]); - ZA[733] = ZR15(ZA[727]) + ZA[732]; - ZA[919] = ZA[906] + ZA[904] + ZA[731]; - ZA[734] = ZA[712] + ZA[681]; - - ZA[920] = (ZCh(ZA[918], ZA[914], ZA[909]) + ZA[919]) + ZR26(ZA[918]); - ZA[735] = ZR15(ZA[730]) + ZA[734]; - ZA[921] = ZA[911] + ZA[909] + ZA[733]; - ZA[916] = ZA[913] + ZA[915]; - - ZA[922] = (ZCh(ZA[920], ZA[918], ZA[914]) + ZA[921]) + ZR26(ZA[920]); - ZA[923] = ZA[916] + ZA[914] + ZA[735]; - - ZA[924] = (ZCh(ZA[922], ZA[920], ZA[918]) + ZA[923]) + ZR26(ZA[922]); - -#define FOUND (0x0F) -#define SETFOUND(Xnonce) output[output[FOUND]++] = Xnonce - -#if defined(VECTORS4) - bool result = any(ZA[924] == K[79]); - - if (result) { - if (ZA[924].x == K[79]) - SETFOUND(Znonce.x); - if (ZA[924].y == K[79]) - SETFOUND(Znonce.y); - if (ZA[924].z == K[79]) - SETFOUND(Znonce.z); - if (ZA[924].w == K[79]) - SETFOUND(Znonce.w); - } -#elif defined(VECTORS2) - bool result = any(ZA[924] == K[79]); - - if (result) { - if (ZA[924].x == K[79]) - SETFOUND(Znonce.x); - if (ZA[924].y == K[79]) - SETFOUND(Znonce.y); - } -#else - if (ZA[924] == K[79]) - SETFOUND(Znonce); -#endif -} diff --git a/diakgcn121016.cl b/diakgcn121016.cl deleted file mode 100644 index b87fbde962..0000000000 --- a/diakgcn121016.cl +++ /dev/null @@ -1,599 +0,0 @@ -// DiaKGCN 27-04-2012 - OpenCL kernel by Diapolo -// -// Parts and / or ideas for this kernel are based upon the public-domain poclbm project, the phatk kernel by Phateus and the DiabloMiner kernel by DiabloD3. -// The kernel was rewritten by me (Diapolo) and is still public-domain! - -#ifdef VECTORS4 - typedef uint4 u; -#elif defined VECTORS2 - typedef uint2 u; -#else - typedef uint u; -#endif - -#ifdef BITALIGN - #pragma OPENCL EXTENSION cl_amd_media_ops : enable - #ifdef BFI_INT - #define ch(x, y, z) amd_bytealign(x, y, z) - #define ma(x, y, z) amd_bytealign(z ^ x, y, x) - #else - #define ch(x, y, z) bitselect(z, y, x) - #define ma(z, x, y) bitselect(z, y, z ^ x) - #endif -#else - #define ch(x, y, z) (z ^ (x & (y ^ z))) - #define ma(x, y, z) ((x & z) | (y & (x | z))) -#endif - -#define rotr15(n) (rotate(n, 15U) ^ rotate(n, 13U) ^ (n >> 10U)) -#define rotr25(n) (rotate(n, 25U) ^ rotate(n, 14U) ^ (n >> 3U)) -#define rotr26(n) (rotate(n, 26U) ^ rotate(n, 21U) ^ rotate(n, 7U)) -#define rotr30(n) (rotate(n, 30U) ^ rotate(n, 19U) ^ rotate(n, 10U)) - -__kernel - __attribute__((reqd_work_group_size(WORKSIZE, 1, 1))) - void search( -#ifndef GOFFSET - const u base, -#endif - const uint PreVal0, const uint PreVal4, - const uint H1, const uint D1A, const uint B1, const uint C1, - const uint F1, const uint G1, const uint C1addK5, const uint B1addK6, const uint PreVal0addK7, - const uint W16addK16, const uint W17addK17, - const uint PreW18, const uint PreW19, - const uint W16, const uint W17, - const uint PreW31, const uint PreW32, - const uint state0, const uint state1, const uint state2, const uint state3, - const uint state4, const uint state5, const uint state6, const uint state7, - const uint state0A, const uint state0B, - const uint state1A, const uint state2A, const uint state3A, const uint state4A, - const uint state5A, const uint state6A, const uint state7A, - volatile __global uint * output) -{ - u V[8]; - u W[16]; - -#ifdef VECTORS4 - const u nonce = (uint)(get_local_id(0)) * 4U + (uint)(get_group_id(0)) * (uint)(WORKVEC) + base; -#elif defined VECTORS2 - const u nonce = (uint)(get_local_id(0)) * 2U + (uint)(get_group_id(0)) * (uint)(WORKVEC) + base; -#else - #ifdef GOFFSET - const u nonce = (uint)(get_global_id(0)); - #else - const u nonce = (uint)(get_local_id(0)) + (uint)(get_group_id(0)) * (uint)(WORKSIZE) + base; - #endif -#endif - - V[0] = PreVal0 + nonce; - V[1] = B1; - V[2] = C1; - V[3] = D1A; - V[4] = PreVal4 + nonce; - V[5] = F1; - V[6] = G1; - V[7] = H1; - - V[7] += V[3] + ch(V[0], V[1], V[2]) + rotr26(V[0]); - V[3] = V[3] + ch(V[0], V[1], V[2]) + rotr26(V[0]) + rotr30(V[4]) + ma(V[5], V[6], V[4]); - - V[6] += C1addK5 + ch(V[7], V[0], V[1]) + rotr26(V[7]); - V[2] = C1addK5 + ch(V[7], V[0], V[1]) + rotr26(V[7]) + rotr30(V[3]) + ma(V[4], V[5], V[3]); - - V[5] += B1addK6 + ch(V[6], V[7], V[0]) + rotr26(V[6]); - V[1] = B1addK6 + ch(V[6], V[7], V[0]) + rotr26(V[6]) + rotr30(V[2]) + ma(V[3], V[4], V[2]); - - V[4] += PreVal0addK7 + nonce + ch(V[5], V[6], V[7]) + rotr26(V[5]); - V[0] = PreVal0addK7 + nonce + ch(V[5], V[6], V[7]) + rotr26(V[5]) + rotr30(V[1]) + ma(V[2], V[3], V[1]); - - V[3] += 0xd807aa98U + V[7] + ch(V[4], V[5], V[6]) + rotr26(V[4]); - V[7] = 0xd807aa98U + V[7] + ch(V[4], V[5], V[6]) + rotr26(V[4]) + rotr30(V[0]) + ma(V[1], V[2], V[0]); - - V[2] += 0x12835b01U + V[6] + ch(V[3], V[4], V[5]) + rotr26(V[3]); - V[6] = 0x12835b01U + V[6] + ch(V[3], V[4], V[5]) + rotr26(V[3]) + rotr30(V[7]) + ma(V[0], V[1], V[7]); - - V[1] += 0x243185beU + V[5] + ch(V[2], V[3], V[4]) + rotr26(V[2]); - V[5] = 0x243185beU + V[5] + ch(V[2], V[3], V[4]) + rotr26(V[2]) + rotr30(V[6]) + ma(V[7], V[0], V[6]); - - V[0] += 0x550c7dc3U + V[4] + ch(V[1], V[2], V[3]) + rotr26(V[1]); - V[4] = 0x550c7dc3U + V[4] + ch(V[1], V[2], V[3]) + rotr26(V[1]) + rotr30(V[5]) + ma(V[6], V[7], V[5]); - - V[7] += 0x72be5d74U + V[3] + ch(V[0], V[1], V[2]) + rotr26(V[0]); - V[3] = 0x72be5d74U + V[3] + ch(V[0], V[1], V[2]) + rotr26(V[0]) + rotr30(V[4]) + ma(V[5], V[6], V[4]); - - V[6] += 0x80deb1feU + V[2] + ch(V[7], V[0], V[1]) + rotr26(V[7]); - V[2] = 0x80deb1feU + V[2] + ch(V[7], V[0], V[1]) + rotr26(V[7]) + rotr30(V[3]) + ma(V[4], V[5], V[3]); - - V[5] += 0x9bdc06a7U + V[1] + ch(V[6], V[7], V[0]) + rotr26(V[6]); - V[1] = 0x9bdc06a7U + V[1] + ch(V[6], V[7], V[0]) + rotr26(V[6]) + rotr30(V[2]) + ma(V[3], V[4], V[2]); - - V[4] += 0xc19bf3f4U + V[0] + ch(V[5], V[6], V[7]) + rotr26(V[5]); - V[0] = 0xc19bf3f4U + V[0] + ch(V[5], V[6], V[7]) + rotr26(V[5]) + rotr30(V[1]) + ma(V[2], V[3], V[1]); - - V[3] += W16addK16 + V[7] + ch(V[4], V[5], V[6]) + rotr26(V[4]); - V[7] = W16addK16 + V[7] + ch(V[4], V[5], V[6]) + rotr26(V[4]) + rotr30(V[0]) + ma(V[1], V[2], V[0]); - - V[2] += W17addK17 + V[6] + ch(V[3], V[4], V[5]) + rotr26(V[3]); - V[6] = W17addK17 + V[6] + ch(V[3], V[4], V[5]) + rotr26(V[3]) + rotr30(V[7]) + ma(V[0], V[1], V[7]); - -//---------------------------------------------------------------------------------- - -#ifdef VECTORS4 - W[0] = PreW18 + (u)(rotr25(nonce.x), rotr25(nonce.x) ^ 0x2004000U, rotr25(nonce.x) ^ 0x4008000U, rotr25(nonce.x) ^ 0x600c000U); -#elif defined VECTORS2 - W[0] = PreW18 + (u)(rotr25(nonce.x), rotr25(nonce.x) ^ 0x2004000U); -#else - W[0] = PreW18 + rotr25(nonce); -#endif - W[1] = PreW19 + nonce; - W[2] = 0x80000000U + rotr15(W[0]); - W[3] = rotr15(W[1]); - W[4] = 0x00000280U + rotr15(W[2]); - W[5] = W16 + rotr15(W[3]); - W[6] = W17 + rotr15(W[4]); - W[7] = W[0] + rotr15(W[5]); - W[8] = W[1] + rotr15(W[6]); - W[9] = W[2] + rotr15(W[7]); - W[10] = W[3] + rotr15(W[8]); - W[11] = W[4] + rotr15(W[9]); - W[12] = W[5] + 0x00a00055U + rotr15(W[10]); - W[13] = W[6] + PreW31 + rotr15(W[11]); - W[14] = W[7] + PreW32 + rotr15(W[12]); - W[15] = W[8] + W17 + rotr15(W[13]) + rotr25(W[0]); - - V[1] += 0x0fc19dc6U + V[5] + ch(V[2], V[3], V[4]) + rotr26(V[2]) + W[0]; - V[5] = 0x0fc19dc6U + V[5] + ch(V[2], V[3], V[4]) + rotr26(V[2]) + W[0] + rotr30(V[6]) + ma(V[7], V[0], V[6]); - - V[0] += 0x240ca1ccU + V[4] + W[1] + ch(V[1], V[2], V[3]) + rotr26(V[1]); - V[4] = 0x240ca1ccU + V[4] + W[1] + ch(V[1], V[2], V[3]) + rotr26(V[1]) + rotr30(V[5]) + ma(V[6], V[7], V[5]); - - V[7] += 0x2de92c6fU + V[3] + W[2] + ch(V[0], V[1], V[2]) + rotr26(V[0]); - V[3] = 0x2de92c6fU + V[3] + W[2] + ch(V[0], V[1], V[2]) + rotr26(V[0]) + rotr30(V[4]) + ma(V[5], V[6], V[4]); - - V[6] += 0x4a7484aaU + V[2] + W[3] + ch(V[7], V[0], V[1]) + rotr26(V[7]); - V[2] = 0x4a7484aaU + V[2] + W[3] + ch(V[7], V[0], V[1]) + rotr26(V[7]) + rotr30(V[3]) + ma(V[4], V[5], V[3]); - - V[5] += 0x5cb0a9dcU + V[1] + W[4] + ch(V[6], V[7], V[0]) + rotr26(V[6]); - V[1] = 0x5cb0a9dcU + V[1] + W[4] + ch(V[6], V[7], V[0]) + rotr26(V[6]) + rotr30(V[2]) + ma(V[3], V[4], V[2]); - - V[4] += 0x76f988daU + V[0] + W[5] + ch(V[5], V[6], V[7]) + rotr26(V[5]); - V[0] = 0x76f988daU + V[0] + W[5] + ch(V[5], V[6], V[7]) + rotr26(V[5]) + rotr30(V[1]) + ma(V[2], V[3], V[1]); - - V[3] += 0x983e5152U + V[7] + W[6] + ch(V[4], V[5], V[6]) + rotr26(V[4]); - V[7] = 0x983e5152U + V[7] + W[6] + ch(V[4], V[5], V[6]) + rotr26(V[4]) + rotr30(V[0]) + ma(V[1], V[2], V[0]); - - V[2] += 0xa831c66dU + V[6] + W[7] + ch(V[3], V[4], V[5]) + rotr26(V[3]); - V[6] = 0xa831c66dU + V[6] + W[7] + ch(V[3], V[4], V[5]) + rotr26(V[3]) + rotr30(V[7]) + ma(V[0], V[1], V[7]); - - V[1] += 0xb00327c8U + V[5] + W[8] + ch(V[2], V[3], V[4]) + rotr26(V[2]); - V[5] = 0xb00327c8U + V[5] + W[8] + ch(V[2], V[3], V[4]) + rotr26(V[2]) + rotr30(V[6]) + ma(V[7], V[0], V[6]); - - V[0] += 0xbf597fc7U + V[4] + W[9] + ch(V[1], V[2], V[3]) + rotr26(V[1]); - V[4] = 0xbf597fc7U + V[4] + W[9] + ch(V[1], V[2], V[3]) + rotr26(V[1]) + rotr30(V[5]) + ma(V[6], V[7], V[5]); - - V[7] += 0xc6e00bf3U + V[3] + W[10] + ch(V[0], V[1], V[2]) + rotr26(V[0]); - V[3] = 0xc6e00bf3U + V[3] + W[10] + ch(V[0], V[1], V[2]) + rotr26(V[0]) + rotr30(V[4]) + ma(V[5], V[6], V[4]); - - V[6] += 0xd5a79147U + V[2] + W[11] + ch(V[7], V[0], V[1]) + rotr26(V[7]); - V[2] = 0xd5a79147U + V[2] + W[11] + ch(V[7], V[0], V[1]) + rotr26(V[7]) + rotr30(V[3]) + ma(V[4], V[5], V[3]); - - V[5] += 0x06ca6351U + V[1] + W[12] + ch(V[6], V[7], V[0]) + rotr26(V[6]); - V[1] = 0x06ca6351U + V[1] + W[12] + ch(V[6], V[7], V[0]) + rotr26(V[6]) + rotr30(V[2]) + ma(V[3], V[4], V[2]); - - V[4] += 0x14292967U + V[0] + W[13] + ch(V[5], V[6], V[7]) + rotr26(V[5]); - V[0] = 0x14292967U + V[0] + W[13] + ch(V[5], V[6], V[7]) + rotr26(V[5]) + rotr30(V[1]) + ma(V[2], V[3], V[1]); - - V[3] += 0x27b70a85U + V[7] + W[14] + ch(V[4], V[5], V[6]) + rotr26(V[4]); - V[7] = 0x27b70a85U + V[7] + W[14] + ch(V[4], V[5], V[6]) + rotr26(V[4]) + rotr30(V[0]) + ma(V[1], V[2], V[0]); - - V[2] += 0x2e1b2138U + V[6] + W[15] + ch(V[3], V[4], V[5]) + rotr26(V[3]); - V[6] = 0x2e1b2138U + V[6] + W[15] + ch(V[3], V[4], V[5]) + rotr26(V[3]) + rotr30(V[7]) + ma(V[0], V[1], V[7]); - -//---------------------------------------------------------------------------------- - - W[0] = W[0] + W[9] + rotr15(W[14]) + rotr25( W[1]); - W[1] = W[1] + W[10] + rotr15(W[15]) + rotr25( W[2]); - W[2] = W[2] + W[11] + rotr15( W[0]) + rotr25( W[3]); - W[3] = W[3] + W[12] + rotr15( W[1]) + rotr25( W[4]); - W[4] = W[4] + W[13] + rotr15( W[2]) + rotr25( W[5]); - W[5] = W[5] + W[14] + rotr15( W[3]) + rotr25( W[6]); - W[6] = W[6] + W[15] + rotr15( W[4]) + rotr25( W[7]); - W[7] = W[7] + W[0] + rotr15( W[5]) + rotr25( W[8]); - W[8] = W[8] + W[1] + rotr15( W[6]) + rotr25( W[9]); - W[9] = W[9] + W[2] + rotr15( W[7]) + rotr25(W[10]); - W[10] = W[10] + W[3] + rotr15( W[8]) + rotr25(W[11]); - W[11] = W[11] + W[4] + rotr15( W[9]) + rotr25(W[12]); - W[12] = W[12] + W[5] + rotr15(W[10]) + rotr25(W[13]); - W[13] = W[13] + W[6] + rotr15(W[11]) + rotr25(W[14]); - W[14] = W[14] + W[7] + rotr15(W[12]) + rotr25(W[15]); - W[15] = W[15] + W[8] + rotr15(W[13]) + rotr25( W[0]); - - V[1] += 0x4d2c6dfcU + V[5] + W[0] + ch(V[2], V[3], V[4]) + rotr26(V[2]); - V[5] = 0x4d2c6dfcU + V[5] + W[0] + ch(V[2], V[3], V[4]) + rotr26(V[2]) + rotr30(V[6]) + ma(V[7], V[0], V[6]); - - V[0] += 0x53380d13U + V[4] + W[1] + ch(V[1], V[2], V[3]) + rotr26(V[1]); - V[4] = 0x53380d13U + V[4] + W[1] + ch(V[1], V[2], V[3]) + rotr26(V[1]) + rotr30(V[5]) + ma(V[6], V[7], V[5]); - - V[7] += 0x650a7354U + V[3] + W[2] + ch(V[0], V[1], V[2]) + rotr26(V[0]); - V[3] = 0x650a7354U + V[3] + W[2] + ch(V[0], V[1], V[2]) + rotr26(V[0]) + rotr30(V[4]) + ma(V[5], V[6], V[4]); - - V[6] += 0x766a0abbU + V[2] + W[3] + ch(V[7], V[0], V[1]) + rotr26(V[7]); - V[2] = 0x766a0abbU + V[2] + W[3] + ch(V[7], V[0], V[1]) + rotr26(V[7]) + rotr30(V[3]) + ma(V[4], V[5], V[3]); - - V[5] += 0x81c2c92eU + V[1] + W[4] + ch(V[6], V[7], V[0]) + rotr26(V[6]); - V[1] = 0x81c2c92eU + V[1] + W[4] + ch(V[6], V[7], V[0]) + rotr26(V[6]) + rotr30(V[2]) + ma(V[3], V[4], V[2]); - - V[4] += 0x92722c85U + V[0] + W[5] + ch(V[5], V[6], V[7]) + rotr26(V[5]); - V[0] = 0x92722c85U + V[0] + W[5] + ch(V[5], V[6], V[7]) + rotr26(V[5]) + rotr30(V[1]) + ma(V[2], V[3], V[1]); - - V[3] += 0xa2bfe8a1U + V[7] + W[6] + ch(V[4], V[5], V[6]) + rotr26(V[4]); - V[7] = 0xa2bfe8a1U + V[7] + W[6] + ch(V[4], V[5], V[6]) + rotr26(V[4]) + rotr30(V[0]) + ma(V[1], V[2], V[0]); - - V[2] += 0xa81a664bU + V[6] + W[7] + ch(V[3], V[4], V[5]) + rotr26(V[3]); - V[6] = 0xa81a664bU + V[6] + W[7] + ch(V[3], V[4], V[5]) + rotr26(V[3]) + rotr30(V[7]) + ma(V[0], V[1], V[7]); - - V[1] += 0xc24b8b70U + V[5] + W[8] + ch(V[2], V[3], V[4]) + rotr26(V[2]); - V[5] = 0xc24b8b70U + V[5] + W[8] + ch(V[2], V[3], V[4]) + rotr26(V[2]) + rotr30(V[6]) + ma(V[7], V[0], V[6]); - - V[0] += 0xc76c51a3U + V[4] + W[9] + ch(V[1], V[2], V[3]) + rotr26(V[1]); - V[4] = 0xc76c51a3U + V[4] + W[9] + ch(V[1], V[2], V[3]) + rotr26(V[1]) + rotr30(V[5]) + ma(V[6], V[7], V[5]); - - V[7] += 0xd192e819U + V[3] + W[10] + ch(V[0], V[1], V[2]) + rotr26(V[0]); - V[3] = 0xd192e819U + V[3] + W[10] + ch(V[0], V[1], V[2]) + rotr26(V[0]) + rotr30(V[4]) + ma(V[5], V[6], V[4]); - - V[6] += 0xd6990624U + V[2] + W[11] + ch(V[7], V[0], V[1]) + rotr26(V[7]); - V[2] = 0xd6990624U + V[2] + W[11] + ch(V[7], V[0], V[1]) + rotr26(V[7]) + rotr30(V[3]) + ma(V[4], V[5], V[3]); - - V[5] += 0xf40e3585U + V[1] + W[12] + ch(V[6], V[7], V[0]) + rotr26(V[6]); - V[1] = 0xf40e3585U + V[1] + W[12] + ch(V[6], V[7], V[0]) + rotr26(V[6]) + rotr30(V[2]) + ma(V[3], V[4], V[2]); - - V[4] += 0x106aa070U + V[0] + W[13] + ch(V[5], V[6], V[7]) + rotr26(V[5]); - V[0] = 0x106aa070U + V[0] + W[13] + ch(V[5], V[6], V[7]) + rotr26(V[5]) + rotr30(V[1]) + ma(V[2], V[3], V[1]); - - V[3] += 0x19a4c116U + V[7] + W[14] + ch(V[4], V[5], V[6]) + rotr26(V[4]); - V[7] = 0x19a4c116U + V[7] + W[14] + ch(V[4], V[5], V[6]) + rotr26(V[4]) + rotr30(V[0]) + ma(V[1], V[2], V[0]); - - V[2] += 0x1e376c08U + V[6] + W[15] + ch(V[3], V[4], V[5]) + rotr26(V[3]); - V[6] = 0x1e376c08U + V[6] + W[15] + ch(V[3], V[4], V[5]) + rotr26(V[3]) + rotr30(V[7]) + ma(V[0], V[1], V[7]); - -//---------------------------------------------------------------------------------- - - W[0] = W[0] + W[9] + rotr15(W[14]) + rotr25( W[1]); - W[1] = W[1] + W[10] + rotr15(W[15]) + rotr25( W[2]); - W[2] = W[2] + W[11] + rotr15( W[0]) + rotr25( W[3]); - W[3] = W[3] + W[12] + rotr15( W[1]) + rotr25( W[4]); - W[4] = W[4] + W[13] + rotr15( W[2]) + rotr25( W[5]); - W[5] = W[5] + W[14] + rotr15( W[3]) + rotr25( W[6]); - W[6] = W[6] + W[15] + rotr15( W[4]) + rotr25( W[7]); - W[7] = W[7] + W[0] + rotr15( W[5]) + rotr25( W[8]); - W[8] = W[8] + W[1] + rotr15( W[6]) + rotr25( W[9]); - W[9] = W[9] + W[2] + rotr15( W[7]) + rotr25(W[10]); - W[10] = W[10] + W[3] + rotr15( W[8]) + rotr25(W[11]); - W[11] = W[11] + W[4] + rotr15( W[9]) + rotr25(W[12]); - W[12] = W[12] + W[5] + rotr15(W[10]) + rotr25(W[13]); - W[13] = W[13] + W[6] + rotr15(W[11]) + rotr25(W[14]); - - V[1] += 0x2748774cU + V[5] + W[0] + ch(V[2], V[3], V[4]) + rotr26(V[2]); - V[5] = 0x2748774cU + V[5] + W[0] + ch(V[2], V[3], V[4]) + rotr26(V[2]) + rotr30(V[6]) + ma(V[7], V[0], V[6]); - - V[0] += 0x34b0bcb5U + V[4] + W[1] + ch(V[1], V[2], V[3]) + rotr26(V[1]); - V[4] = 0x34b0bcb5U + V[4] + W[1] + ch(V[1], V[2], V[3]) + rotr26(V[1]) + rotr30(V[5]) + ma(V[6], V[7], V[5]); - - V[7] += 0x391c0cb3U + V[3] + W[2] + ch(V[0], V[1], V[2]) + rotr26(V[0]); - V[3] = 0x391c0cb3U + V[3] + W[2] + ch(V[0], V[1], V[2]) + rotr26(V[0]) + rotr30(V[4]) + ma(V[5], V[6], V[4]); - - V[6] += 0x4ed8aa4aU + V[2] + W[3] + ch(V[7], V[0], V[1]) + rotr26(V[7]); - V[2] = 0x4ed8aa4aU + V[2] + W[3] + ch(V[7], V[0], V[1]) + rotr26(V[7]) + rotr30(V[3]) + ma(V[4], V[5], V[3]); - - V[5] += 0x5b9cca4fU + V[1] + W[4] + ch(V[6], V[7], V[0]) + rotr26(V[6]); - V[1] = 0x5b9cca4fU + V[1] + W[4] + ch(V[6], V[7], V[0]) + rotr26(V[6]) + rotr30(V[2]) + ma(V[3], V[4], V[2]); - - V[4] += 0x682e6ff3U + V[0] + W[5] + ch(V[5], V[6], V[7]) + rotr26(V[5]); - V[0] = 0x682e6ff3U + V[0] + W[5] + ch(V[5], V[6], V[7]) + rotr26(V[5]) + rotr30(V[1]) + ma(V[2], V[3], V[1]); - - V[3] += 0x748f82eeU + V[7] + W[6] + ch(V[4], V[5], V[6]) + rotr26(V[4]); - V[7] = 0x748f82eeU + V[7] + W[6] + ch(V[4], V[5], V[6]) + rotr26(V[4]) + rotr30(V[0]) + ma(V[1], V[2], V[0]); - - V[2] += 0x78a5636fU + V[6] + W[7] + ch(V[3], V[4], V[5]) + rotr26(V[3]); - V[6] = 0x78a5636fU + V[6] + W[7] + ch(V[3], V[4], V[5]) + rotr26(V[3]) + rotr30(V[7]) + ma(V[0], V[1], V[7]); - - V[1] += 0x84c87814U + V[5] + W[8] + ch(V[2], V[3], V[4]) + rotr26(V[2]); - V[5] = 0x84c87814U + V[5] + W[8] + ch(V[2], V[3], V[4]) + rotr26(V[2]) + rotr30(V[6]) + ma(V[7], V[0], V[6]); - - V[0] += 0x8cc70208U + V[4] + W[9] + ch(V[1], V[2], V[3]) + rotr26(V[1]); - V[4] = 0x8cc70208U + V[4] + W[9] + ch(V[1], V[2], V[3]) + rotr26(V[1]) + rotr30(V[5]) + ma(V[6], V[7], V[5]); - - V[7] += 0x90befffaU + V[3] + W[10] + ch(V[0], V[1], V[2]) + rotr26(V[0]); - V[3] = 0x90befffaU + V[3] + W[10] + ch(V[0], V[1], V[2]) + rotr26(V[0]) + rotr30(V[4]) + ma(V[5], V[6], V[4]); - - V[6] += 0xa4506cebU + V[2] + W[11] + ch(V[7], V[0], V[1]) + rotr26(V[7]); - V[2] = 0xa4506cebU + V[2] + W[11] + ch(V[7], V[0], V[1]) + rotr26(V[7]) + rotr30(V[3]) + ma(V[4], V[5], V[3]); - - V[5] += 0xbef9a3f7U + V[1] + W[12] + ch(V[6], V[7], V[0]) + rotr26(V[6]); - V[1] = 0xbef9a3f7U + V[1] + W[12] + ch(V[6], V[7], V[0]) + rotr26(V[6]) + rotr30(V[2]) + ma(V[3], V[4], V[2]); - - V[4] += 0xc67178f2U + V[0] + W[13] + ch(V[5], V[6], V[7]) + rotr26(V[5]); - V[0] = 0xc67178f2U + V[0] + W[13] + ch(V[5], V[6], V[7]) + rotr26(V[5]) + rotr30(V[1]) + ma(V[2], V[3], V[1]); - -//---------------------------------------------------------------------------------- - - W[0] = state0 + V[0] + rotr25(state1 + V[1]); - W[1] = state1 + V[1] + 0x00a00000U + rotr25(state2 + V[2]); - W[2] = state2 + V[2] + rotr15(W[0]) + rotr25(state3 + V[3]); - W[3] = state3 + V[3] + rotr15(W[1]) + rotr25(state4 + V[4]); - W[4] = state4 + V[4] + rotr15(W[2]) + rotr25(state5 + V[5]); - W[5] = state5 + V[5] + rotr15(W[3]) + rotr25(state6 + V[6]); - W[6] = state6 + V[6] + 0x00000100U + rotr15(W[4]) + rotr25(state7 + V[7]); - W[7] = state7 + V[7] + W[0] + 0x11002000U + rotr15(W[5]); - W[8] = W[1] + 0x80000000U + rotr15(W[6]); - W[9] = W[2] + rotr15(W[7]); - W[10] = W[3] + rotr15(W[8]); - W[11] = W[4] + rotr15(W[9]); - W[12] = W[5] + rotr15(W[10]); - W[13] = W[6] + rotr15(W[11]); - W[14] = W[7] + 0x00400022U + rotr15(W[12]); - W[15] = W[8] + 0x00000100U + rotr15(W[13]) + rotr25(W[0]); - - // 0x71374491U + 0x1f83d9abU + state1 - const u state1AaddV1 = state1A + V[1]; - // 0xb5c0fbcfU + 0x9b05688cU + state2 - const u state2AaddV2 = state2A + V[2]; - // 0x510e527fU + 0xe9b5dba5U + state3 - const u state3AaddV3 = state3A + V[3]; - // 0x3956c25bU + state4 - const u state4AaddV4 = state4A + V[4]; - // 0x59f111f1U + state5 - const u state5AaddV5 = state5A + V[5]; - // 0x923f82a4U + state6 - const u state6AaddV6 = state6A + V[6]; - // 0xab1c5ed5U + state7 - const u state7AaddV7 = state7A + V[7]; - - // 0x98c7e2a2U + state0 - V[3] = state0A + V[0]; - // 0xfc08884dU + state0 - V[7] = state0B + V[0]; - V[0] = 0x6a09e667U; - V[1] = 0xbb67ae85U; - V[2] = 0x3c6ef372U; - V[4] = 0x510e527fU; - V[5] = 0x9b05688cU; - V[6] = 0x1f83d9abU; - - V[2] += state1AaddV1 + ch(V[3], V[4], V[5]) + rotr26(V[3]); - V[6] = state1AaddV1 + ch(V[3], V[4], V[5]) + rotr26(V[3]) + rotr30(V[7]) + ma(V[0], V[1], V[7]); - - V[1] += state2AaddV2 + ch(V[2], V[3], V[4]) + rotr26(V[2]); - V[5] = state2AaddV2 + ch(V[2], V[3], V[4]) + rotr26(V[2]) + rotr30(V[6]) + ma(V[7], V[0], V[6]); - - V[0] += state3AaddV3 + ch(V[1], V[2], V[3]) + rotr26(V[1]); - V[4] = state3AaddV3 + ch(V[1], V[2], V[3]) + rotr26(V[1]) + rotr30(V[5]) + ma(V[6], V[7], V[5]); - - V[7] += state4AaddV4 + V[3] + ch(V[0], V[1], V[2]) + rotr26(V[0]); - V[3] = state4AaddV4 + V[3] + ch(V[0], V[1], V[2]) + rotr26(V[0]) + rotr30(V[4]) + ma(V[5], V[6], V[4]); - - V[6] += state5AaddV5 + V[2] + ch(V[7], V[0], V[1]) + rotr26(V[7]); - V[2] = state5AaddV5 + V[2] + ch(V[7], V[0], V[1]) + rotr26(V[7]) + rotr30(V[3]) + ma(V[4], V[5], V[3]); - - V[5] += state6AaddV6 + V[1] + ch(V[6], V[7], V[0]) + rotr26(V[6]); - V[1] = state6AaddV6 + V[1] + ch(V[6], V[7], V[0]) + rotr26(V[6]) + rotr30(V[2]) + ma(V[3], V[4], V[2]); - - V[4] += state7AaddV7 + V[0] + ch(V[5], V[6], V[7]) + rotr26(V[5]); - V[0] = state7AaddV7 + V[0] + ch(V[5], V[6], V[7]) + rotr26(V[5]) + rotr30(V[1]) + ma(V[2], V[3], V[1]); - - V[3] += 0x5807aa98U + V[7] + ch(V[4], V[5], V[6]) + rotr26(V[4]); - V[7] = 0x5807aa98U + V[7] + ch(V[4], V[5], V[6]) + rotr26(V[4]) + rotr30(V[0]) + ma(V[1], V[2], V[0]); - - V[2] += 0x12835b01U + V[6] + ch(V[3], V[4], V[5]) + rotr26(V[3]); - V[6] = 0x12835b01U + V[6] + ch(V[3], V[4], V[5]) + rotr26(V[3]) + rotr30(V[7]) + ma(V[0], V[1], V[7]); - - V[1] += 0x243185beU + V[5] + ch(V[2], V[3], V[4]) + rotr26(V[2]); - V[5] = 0x243185beU + V[5] + ch(V[2], V[3], V[4]) + rotr26(V[2]) + rotr30(V[6]) + ma(V[7], V[0], V[6]); - - V[0] += 0x550c7dc3U + V[4] + ch(V[1], V[2], V[3]) + rotr26(V[1]); - V[4] = 0x550c7dc3U + V[4] + ch(V[1], V[2], V[3]) + rotr26(V[1]) + rotr30(V[5]) + ma(V[6], V[7], V[5]); - - V[7] += 0x72be5d74U + V[3] + ch(V[0], V[1], V[2]) + rotr26(V[0]); - V[3] = 0x72be5d74U + V[3] + ch(V[0], V[1], V[2]) + rotr26(V[0]) + rotr30(V[4]) + ma(V[5], V[6], V[4]); - - V[6] += 0x80deb1feU + V[2] + ch(V[7], V[0], V[1]) + rotr26(V[7]); - V[2] = 0x80deb1feU + V[2] + ch(V[7], V[0], V[1]) + rotr26(V[7]) + rotr30(V[3]) + ma(V[4], V[5], V[3]); - - V[5] += 0x9bdc06a7U + V[1] + ch(V[6], V[7], V[0]) + rotr26(V[6]); - V[1] = 0x9bdc06a7U + V[1] + ch(V[6], V[7], V[0]) + rotr26(V[6]) + rotr30(V[2]) + ma(V[3], V[4], V[2]); - - V[4] += 0xc19bf274U + V[0] + ch(V[5], V[6], V[7]) + rotr26(V[5]); - V[0] = 0xc19bf274U + V[0] + ch(V[5], V[6], V[7]) + rotr26(V[5]) + rotr30(V[1]) + ma(V[2], V[3], V[1]); - - V[3] += 0xe49b69c1U + V[7] + W[0] + ch(V[4], V[5], V[6]) + rotr26(V[4]); - V[7] = 0xe49b69c1U + V[7] + W[0] + ch(V[4], V[5], V[6]) + rotr26(V[4]) + rotr30(V[0]) + ma(V[1], V[2], V[0]); - - V[2] += 0xefbe4786U + V[6] + W[1] + ch(V[3], V[4], V[5]) + rotr26(V[3]); - V[6] = 0xefbe4786U + V[6] + W[1] + ch(V[3], V[4], V[5]) + rotr26(V[3]) + rotr30(V[7]) + ma(V[0], V[1], V[7]); - - V[1] += 0x0fc19dc6U + V[5] + W[2] + ch(V[2], V[3], V[4]) + rotr26(V[2]); - V[5] = 0x0fc19dc6U + V[5] + W[2] + ch(V[2], V[3], V[4]) + rotr26(V[2]) + rotr30(V[6]) + ma(V[7], V[0], V[6]); - - V[0] += 0x240ca1ccU + V[4] + W[3] + ch(V[1], V[2], V[3]) + rotr26(V[1]); - V[4] = 0x240ca1ccU + V[4] + W[3] + ch(V[1], V[2], V[3]) + rotr26(V[1]) + rotr30(V[5]) + ma(V[6], V[7], V[5]); - - V[7] += 0x2de92c6fU + V[3] + W[4] + ch(V[0], V[1], V[2]) + rotr26(V[0]); - V[3] = 0x2de92c6fU + V[3] + W[4] + ch(V[0], V[1], V[2]) + rotr26(V[0]) + rotr30(V[4]) + ma(V[5], V[6], V[4]); - - V[6] += 0x4a7484aaU + V[2] + W[5] + ch(V[7], V[0], V[1]) + rotr26(V[7]); - V[2] = 0x4a7484aaU + V[2] + W[5] + ch(V[7], V[0], V[1]) + rotr26(V[7]) + rotr30(V[3]) + ma(V[4], V[5], V[3]); - - V[5] += 0x5cb0a9dcU + V[1] + W[6] + ch(V[6], V[7], V[0]) + rotr26(V[6]); - V[1] = 0x5cb0a9dcU + V[1] + W[6] + ch(V[6], V[7], V[0]) + rotr26(V[6]) + rotr30(V[2]) + ma(V[3], V[4], V[2]); - - V[4] += 0x76f988daU + V[0] + W[7] + ch(V[5], V[6], V[7]) + rotr26(V[5]); - V[0] = 0x76f988daU + V[0] + W[7] + ch(V[5], V[6], V[7]) + rotr26(V[5]) + rotr30(V[1]) + ma(V[2], V[3], V[1]); - - V[3] += 0x983e5152U + V[7] + W[8] + ch(V[4], V[5], V[6]) + rotr26(V[4]); - V[7] = 0x983e5152U + V[7] + W[8] + ch(V[4], V[5], V[6]) + rotr26(V[4]) + rotr30(V[0]) + ma(V[1], V[2], V[0]); - - V[2] += 0xa831c66dU + V[6] + W[9] + ch(V[3], V[4], V[5]) + rotr26(V[3]); - V[6] = 0xa831c66dU + V[6] + W[9] + ch(V[3], V[4], V[5]) + rotr26(V[3]) + rotr30(V[7]) + ma(V[0], V[1], V[7]); - - V[1] += 0xb00327c8U + V[5] + W[10] + ch(V[2], V[3], V[4]) + rotr26(V[2]); - V[5] = 0xb00327c8U + V[5] + W[10] + ch(V[2], V[3], V[4]) + rotr26(V[2]) + rotr30(V[6]) + ma(V[7], V[0], V[6]); - - V[0] += 0xbf597fc7U + V[4] + W[11] + ch(V[1], V[2], V[3]) + rotr26(V[1]); - V[4] = 0xbf597fc7U + V[4] + W[11] + ch(V[1], V[2], V[3]) + rotr26(V[1]) + rotr30(V[5]) + ma(V[6], V[7], V[5]); - - V[7] += 0xc6e00bf3U + V[3] + W[12] + ch(V[0], V[1], V[2]) + rotr26(V[0]); - V[3] = 0xc6e00bf3U + V[3] + W[12] + ch(V[0], V[1], V[2]) + rotr26(V[0]) + rotr30(V[4]) + ma(V[5], V[6], V[4]); - - V[6] += 0xd5a79147U + V[2] + W[13] + ch(V[7], V[0], V[1]) + rotr26(V[7]); - V[2] = 0xd5a79147U + V[2] + W[13] + ch(V[7], V[0], V[1]) + rotr26(V[7]) + rotr30(V[3]) + ma(V[4], V[5], V[3]); - - V[5] += 0x06ca6351U + V[1] + W[14] + ch(V[6], V[7], V[0]) + rotr26(V[6]); - V[1] = 0x06ca6351U + V[1] + W[14] + ch(V[6], V[7], V[0]) + rotr26(V[6]) + rotr30(V[2]) + ma(V[3], V[4], V[2]); - - V[4] += 0x14292967U + V[0] + W[15] + ch(V[5], V[6], V[7]) + rotr26(V[5]); - V[0] = 0x14292967U + V[0] + W[15] + ch(V[5], V[6], V[7]) + rotr26(V[5]) + rotr30(V[1]) + ma(V[2], V[3], V[1]); - -//---------------------------------------------------------------------------------- - - W[0] = W[0] + W[9] + rotr15(W[14]) + rotr25( W[1]); - W[1] = W[1] + W[10] + rotr15(W[15]) + rotr25( W[2]); - W[2] = W[2] + W[11] + rotr15( W[0]) + rotr25( W[3]); - W[3] = W[3] + W[12] + rotr15( W[1]) + rotr25( W[4]); - W[4] = W[4] + W[13] + rotr15( W[2]) + rotr25( W[5]); - W[5] = W[5] + W[14] + rotr15( W[3]) + rotr25( W[6]); - W[6] = W[6] + W[15] + rotr15( W[4]) + rotr25( W[7]); - W[7] = W[7] + W[0] + rotr15( W[5]) + rotr25( W[8]); - W[8] = W[8] + W[1] + rotr15( W[6]) + rotr25( W[9]); - W[9] = W[9] + W[2] + rotr15( W[7]) + rotr25(W[10]); - W[10] = W[10] + W[3] + rotr15( W[8]) + rotr25(W[11]); - W[11] = W[11] + W[4] + rotr15( W[9]) + rotr25(W[12]); - W[12] = W[12] + W[5] + rotr15(W[10]) + rotr25(W[13]); - W[13] = W[13] + W[6] + rotr15(W[11]) + rotr25(W[14]); - W[14] = W[14] + W[7] + rotr15(W[12]) + rotr25(W[15]); - W[15] = W[15] + W[8] + rotr15(W[13]) + rotr25( W[0]); - - V[3] += 0x27b70a85U + V[7] + W[0] + ch(V[4], V[5], V[6]) + rotr26(V[4]); - V[7] = 0x27b70a85U + V[7] + W[0] + ch(V[4], V[5], V[6]) + rotr26(V[4]) + rotr30(V[0]) + ma(V[1], V[2], V[0]); - - V[2] += 0x2e1b2138U + V[6] + W[1] + ch(V[3], V[4], V[5]) + rotr26(V[3]); - V[6] = 0x2e1b2138U + V[6] + W[1] + ch(V[3], V[4], V[5]) + rotr26(V[3]) + rotr30(V[7]) + ma(V[0], V[1], V[7]); - - V[1] += 0x4d2c6dfcU + V[5] + W[2] + ch(V[2], V[3], V[4]) + rotr26(V[2]); - V[5] = 0x4d2c6dfcU + V[5] + W[2] + ch(V[2], V[3], V[4]) + rotr26(V[2]) + rotr30(V[6]) + ma(V[7], V[0], V[6]); - - V[0] += 0x53380d13U + V[4] + W[3] + ch(V[1], V[2], V[3]) + rotr26(V[1]); - V[4] = 0x53380d13U + V[4] + W[3] + ch(V[1], V[2], V[3]) + rotr26(V[1]) + rotr30(V[5]) + ma(V[6], V[7], V[5]); - - V[7] += 0x650a7354U + V[3] + W[4] + ch(V[0], V[1], V[2]) + rotr26(V[0]); - V[3] = 0x650a7354U + V[3] + W[4] + ch(V[0], V[1], V[2]) + rotr26(V[0]) + rotr30(V[4]) + ma(V[5], V[6], V[4]); - - V[6] += 0x766a0abbU + V[2] + W[5] + ch(V[7], V[0], V[1]) + rotr26(V[7]); - V[2] = 0x766a0abbU + V[2] + W[5] + ch(V[7], V[0], V[1]) + rotr26(V[7]) + rotr30(V[3]) + ma(V[4], V[5], V[3]); - - V[5] += 0x81c2c92eU + V[1] + W[6] + ch(V[6], V[7], V[0]) + rotr26(V[6]); - V[1] = 0x81c2c92eU + V[1] + W[6] + ch(V[6], V[7], V[0]) + rotr26(V[6]) + rotr30(V[2]) + ma(V[3], V[4], V[2]); - - V[4] += 0x92722c85U + V[0] + W[7] + ch(V[5], V[6], V[7]) + rotr26(V[5]); - V[0] = 0x92722c85U + V[0] + W[7] + ch(V[5], V[6], V[7]) + rotr26(V[5]) + rotr30(V[1]) + ma(V[2], V[3], V[1]); - - V[3] += 0xa2bfe8a1U + V[7] + W[8] + ch(V[4], V[5], V[6]) + rotr26(V[4]); - V[7] = 0xa2bfe8a1U + V[7] + W[8] + ch(V[4], V[5], V[6]) + rotr26(V[4]) + rotr30(V[0]) + ma(V[1], V[2], V[0]); - - V[2] += 0xa81a664bU + V[6] + W[9] + ch(V[3], V[4], V[5]) + rotr26(V[3]); - V[6] = 0xa81a664bU + V[6] + W[9] + ch(V[3], V[4], V[5]) + rotr26(V[3]) + rotr30(V[7]) + ma(V[0], V[1], V[7]); - - V[1] += 0xc24b8b70U + V[5] + W[10] + ch(V[2], V[3], V[4]) + rotr26(V[2]); - V[5] = 0xc24b8b70U + V[5] + W[10] + ch(V[2], V[3], V[4]) + rotr26(V[2]) + rotr30(V[6]) + ma(V[7], V[0], V[6]); - - V[0] += 0xc76c51a3U + V[4] + W[11] + ch(V[1], V[2], V[3]) + rotr26(V[1]); - V[4] = 0xc76c51a3U + V[4] + W[11] + ch(V[1], V[2], V[3]) + rotr26(V[1]) + rotr30(V[5]) + ma(V[6], V[7], V[5]); - - V[7] += 0xd192e819U + V[3] + W[12] + ch(V[0], V[1], V[2]) + rotr26(V[0]); - V[3] = 0xd192e819U + V[3] + W[12] + ch(V[0], V[1], V[2]) + rotr26(V[0]) + rotr30(V[4]) + ma(V[5], V[6], V[4]); - - V[6] += 0xd6990624U + V[2] + W[13] + ch(V[7], V[0], V[1]) + rotr26(V[7]); - V[2] = 0xd6990624U + V[2] + W[13] + ch(V[7], V[0], V[1]) + rotr26(V[7]) + rotr30(V[3]) + ma(V[4], V[5], V[3]); - - V[5] += 0xf40e3585U + V[1] + W[14] + ch(V[6], V[7], V[0]) + rotr26(V[6]); - V[1] = 0xf40e3585U + V[1] + W[14] + ch(V[6], V[7], V[0]) + rotr26(V[6]) + rotr30(V[2]) + ma(V[3], V[4], V[2]); - - V[4] += 0x106aa070U + V[0] + W[15] + ch(V[5], V[6], V[7]) + rotr26(V[5]); - V[0] = 0x106aa070U + V[0] + W[15] + ch(V[5], V[6], V[7]) + rotr26(V[5]) + rotr30(V[1]) + ma(V[2], V[3], V[1]); - -//---------------------------------------------------------------------------------- - - W[0] = W[0] + W[9] + rotr15(W[14]) + rotr25( W[1]); - W[1] = W[1] + W[10] + rotr15(W[15]) + rotr25( W[2]); - W[2] = W[2] + W[11] + rotr15( W[0]) + rotr25( W[3]); - W[3] = W[3] + W[12] + rotr15( W[1]) + rotr25( W[4]); - W[4] = W[4] + W[13] + rotr15( W[2]) + rotr25( W[5]); - W[5] = W[5] + W[14] + rotr15( W[3]) + rotr25( W[6]); - W[6] = W[6] + W[15] + rotr15( W[4]) + rotr25( W[7]); - W[7] = W[7] + W[0] + rotr15( W[5]) + rotr25( W[8]); - W[8] = W[8] + W[1] + rotr15( W[6]) + rotr25( W[9]); - W[9] = W[9] + W[2] + rotr15( W[7]) + rotr25(W[10]); - W[10] = W[10] + W[3] + rotr15( W[8]) + rotr25(W[11]); - W[11] = W[11] + W[4] + rotr15( W[9]) + rotr25(W[12]); - W[12] = W[12] + W[5] + rotr15(W[10]) + rotr25(W[13]); - - V[3] += 0x19a4c116U + V[7] + W[0] + ch(V[4], V[5], V[6]) + rotr26(V[4]); - V[7] = 0x19a4c116U + V[7] + W[0] + ch(V[4], V[5], V[6]) + rotr26(V[4]) + rotr30(V[0]) + ma(V[1], V[2], V[0]); - - V[2] += 0x1e376c08U + V[6] + W[1] + ch(V[3], V[4], V[5]) + rotr26(V[3]); - V[6] = 0x1e376c08U + V[6] + W[1] + ch(V[3], V[4], V[5]) + rotr26(V[3]) + rotr30(V[7]) + ma(V[0], V[1], V[7]); - - V[1] += 0x2748774cU + V[5] + W[2] + ch(V[2], V[3], V[4]) + rotr26(V[2]); - V[5] = 0x2748774cU + V[5] + W[2] + ch(V[2], V[3], V[4]) + rotr26(V[2]) + rotr30(V[6]) + ma(V[7], V[0], V[6]); - - V[0] += 0x34b0bcb5U + V[4] + W[3] + ch(V[1], V[2], V[3]) + rotr26(V[1]); - V[4] = 0x34b0bcb5U + V[4] + W[3] + ch(V[1], V[2], V[3]) + rotr26(V[1]) + rotr30(V[5]) + ma(V[6], V[7], V[5]); - - V[7] += 0x391c0cb3U + V[3] + W[4] + ch(V[0], V[1], V[2]) + rotr26(V[0]); - V[3] = 0x391c0cb3U + V[3] + W[4] + ch(V[0], V[1], V[2]) + rotr26(V[0]) + rotr30(V[4]) + ma(V[5], V[6], V[4]); - - V[6] += 0x4ed8aa4aU + V[2] + W[5] + ch(V[7], V[0], V[1]) + rotr26(V[7]); - V[2] = 0x4ed8aa4aU + V[2] + W[5] + ch(V[7], V[0], V[1]) + rotr26(V[7]) + rotr30(V[3]) + ma(V[4], V[5], V[3]); - - V[5] += 0x5b9cca4fU + V[1] + W[6] + ch(V[6], V[7], V[0]) + rotr26(V[6]); - V[1] = 0x5b9cca4fU + V[1] + W[6] + ch(V[6], V[7], V[0]) + rotr26(V[6]) + rotr30(V[2]) + ma(V[3], V[4], V[2]); - - V[4] += 0x682e6ff3U + V[0] + W[7] + ch(V[5], V[6], V[7]) + rotr26(V[5]); - V[0] = 0x682e6ff3U + V[0] + W[7] + ch(V[5], V[6], V[7]) + rotr26(V[5]) + rotr30(V[1]) + ma(V[2], V[3], V[1]); - - V[3] += 0x748f82eeU + V[7] + W[8] + ch(V[4], V[5], V[6]) + rotr26(V[4]); - V[7] = 0x748f82eeU + V[7] + W[8] + ch(V[4], V[5], V[6]) + rotr26(V[4]) + rotr30(V[0]) + ma(V[1], V[2], V[0]); - - V[2] += 0x78a5636fU + V[6] + W[9] + ch(V[3], V[4], V[5]) + rotr26(V[3]); - - V[1] += 0x84c87814U + V[5] + W[10] + ch(V[2], V[3], V[4]) + rotr26(V[2]); - - V[0] += 0x8cc70208U + V[4] + W[11] + ch(V[1], V[2], V[3]) + rotr26(V[1]); - - V[7] += V[3] + W[12] + ch(V[0], V[1], V[2]) + rotr26(V[0]); - -#define FOUND (0x0F) -#define SETFOUND(Xnonce) output[output[FOUND]++] = Xnonce - -#ifdef VECTORS4 - if ((V[7].x == 0x136032edU) ^ (V[7].y == 0x136032edU) ^ (V[7].z == 0x136032edU) ^ (V[7].w == 0x136032edU)) { - if (V[7].x == 0x136032edU) - SETFOUND(nonce.x); - if (V[7].y == 0x136032edU) - SETFOUND(nonce.y); - if (V[7].z == 0x136032edU) - SETFOUND(nonce.z); - if (V[7].w == 0x136032edU) - SETFOUND(nonce.w); - } -#elif defined VECTORS2 - if ((V[7].x == 0x136032edU) + (V[7].y == 0x136032edU)) { - if (V[7].x == 0x136032edU) - SETFOUND(nonce.x); - if (V[7].y == 0x136032edU) - SETFOUND(nonce.y); - } -#else - if (V[7] == 0x136032edU) - SETFOUND(nonce); -#endif -} diff --git a/driver-SPI-bitmine-A1.c b/driver-SPI-bitmine-A1.c new file mode 100644 index 0000000000..21d2545ded --- /dev/null +++ b/driver-SPI-bitmine-A1.c @@ -0,0 +1,1123 @@ +/* + * cgminer SPI driver for Bitmine.ch A1 devices + * + * Copyright 2013, 2014 Zefir Kurtisi + * + * 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 +#include +#include +#include +#include + +#include "spi-context.h" +#include "logging.h" +#include "miner.h" +#include "util.h" + +#include "A1-common.h" +#include "A1-board-selector.h" +#include "A1-trimpot-mcp4x.h" + +/* one global board_selector and spi context is enough */ +static struct board_selector *board_selector; +static struct spi_ctx *spi; + +/********** work queue */ +static bool wq_enqueue(struct work_queue *wq, struct work *work) +{ + if (work == NULL) + return false; + struct work_ent *we = malloc(sizeof(*we)); + assert(we != NULL); + + we->work = work; + INIT_LIST_HEAD(&we->head); + list_add_tail(&we->head, &wq->head); + wq->num_elems++; + return true; +} + +static struct work *wq_dequeue(struct work_queue *wq) +{ + if (wq == NULL) + return NULL; + if (wq->num_elems == 0) + return NULL; + struct work_ent *we; + we = list_entry(wq->head.next, struct work_ent, head); + struct work *work = we->work; + + list_del(&we->head); + free(we); + wq->num_elems--; + return work; +} + +/* + * if not cooled sufficiently, communication fails and chip is temporary + * disabled. we let it inactive for 30 seconds to cool down + * + * TODO: to be removed after bring up / test phase + */ +#define COOLDOWN_MS (30 * 1000) +/* if after this number of retries a chip is still inaccessible, disable it */ +#define DISABLE_CHIP_FAIL_THRESHOLD 3 + + +enum A1_command { + A1_BIST_START = 0x01, + A1_BIST_FIX = 0x03, + A1_RESET = 0x04, + A1_WRITE_JOB = 0x07, + A1_READ_RESULT = 0x08, + A1_WRITE_REG = 0x09, + A1_READ_REG = 0x0a, + A1_READ_REG_RESP = 0x1a, +}; + +/* + * for now, we have one global config, defaulting values: + * - ref_clk 16MHz / sys_clk 800MHz + * - 2000 kHz SPI clock + */ +struct A1_config_options A1_config_options = { + .ref_clk_khz = 16000, .sys_clk_khz = 800000, .spi_clk_khz = 2000, +}; + +/* override values with --bitmine-a1-options ref:sys:spi: - use 0 for default */ +static struct A1_config_options *parsed_config_options; + +/********** temporary helper for hexdumping SPI traffic */ +static void applog_hexdump(char *prefix, uint8_t *buff, int len, int level) +{ + static char line[256]; + char *pos = line; + int i; + if (len < 1) + return; + + pos += sprintf(pos, "%s: %d bytes:", prefix, len); + for (i = 0; i < len; i++) { + if (i > 0 && (i % 32) == 0) { + applog(LOG_DEBUG, "%s", line); + pos = line; + pos += sprintf(pos, "\t"); + } + pos += sprintf(pos, "%.2X ", buff[i]); + } + applog(level, "%s", line); +} + +static void hexdump(char *prefix, uint8_t *buff, int len) +{ + applog_hexdump(prefix, buff, len, LOG_DEBUG); +} + +static void hexdump_error(char *prefix, uint8_t *buff, int len) +{ + applog_hexdump(prefix, buff, len, LOG_ERR); +} + +static void flush_spi(struct A1_chain *a1) +{ + memset(a1->spi_tx, 0, 64); + spi_transfer(a1->spi_ctx, a1->spi_tx, a1->spi_rx, 64); +} + + +/********** upper layer SPI functions */ +static uint8_t *exec_cmd(struct A1_chain *a1, + uint8_t cmd, uint8_t chip_id, + uint8_t *data, uint8_t len, + uint8_t resp_len) +{ + int tx_len = 4 + len; + memset(a1->spi_tx, 0, tx_len); + a1->spi_tx[0] = cmd; + a1->spi_tx[1] = chip_id; + + if (data != NULL) + memcpy(a1->spi_tx + 2, data, len); + + assert(spi_transfer(a1->spi_ctx, a1->spi_tx, a1->spi_rx, tx_len)); + hexdump("send: TX", a1->spi_tx, tx_len); + hexdump("send: RX", a1->spi_rx, tx_len); + + int poll_len = resp_len; + if (chip_id == 0) { + if (a1->num_chips == 0) { + applog(LOG_INFO, "%d: unknown chips in chain, " + "assuming 8", a1->chain_id); + poll_len += 32; + } + poll_len += 4 * a1->num_chips; + } + else { + poll_len += 4 * chip_id - 2; + } + + assert(spi_transfer(a1->spi_ctx, NULL, a1->spi_rx + tx_len, poll_len)); + hexdump("poll: RX", a1->spi_rx + tx_len, poll_len); + int ack_len = tx_len + resp_len; + int ack_pos = tx_len + poll_len - ack_len; + hexdump("poll: ACK", a1->spi_rx + ack_pos, ack_len - 2); + + return (a1->spi_rx + ack_pos); +} + + +/********** A1 SPI commands */ +static uint8_t *cmd_BIST_FIX_BCAST(struct A1_chain *a1) +{ + uint8_t *ret = exec_cmd(a1, A1_BIST_FIX, 0x00, NULL, 0, 0); + if (ret == NULL || ret[0] != A1_BIST_FIX) { + applog(LOG_ERR, "%d: cmd_BIST_FIX_BCAST failed", a1->chain_id); + return NULL; + } + return ret; +} + +static uint8_t *cmd_RESET_BCAST(struct A1_chain *a1, uint8_t strategy) +{ + static uint8_t s[2]; + s[0] = strategy; + s[1] = strategy; + uint8_t *ret = exec_cmd(a1, A1_RESET, 0x00, s, 2, 0); + if (ret == NULL || (ret[0] != A1_RESET && a1->num_chips != 0)) { + applog(LOG_ERR, "%d: cmd_RESET_BCAST failed", a1->chain_id); + return NULL; + } + return ret; +} + +static uint8_t *cmd_READ_RESULT_BCAST(struct A1_chain *a1) +{ + int tx_len = 8; + memset(a1->spi_tx, 0, tx_len); + a1->spi_tx[0] = A1_READ_RESULT; + + assert(spi_transfer(a1->spi_ctx, a1->spi_tx, a1->spi_rx, tx_len)); + hexdump("send: TX", a1->spi_tx, tx_len); + hexdump("send: RX", a1->spi_rx, tx_len); + + int poll_len = tx_len + 4 * a1->num_chips; + assert(spi_transfer(a1->spi_ctx, NULL, a1->spi_rx + tx_len, poll_len)); + hexdump("poll: RX", a1->spi_rx + tx_len, poll_len); + + uint8_t *scan = a1->spi_rx; + int i; + for (i = 0; i < poll_len; i += 2) { + if ((scan[i] & 0x0f) == A1_READ_RESULT) + return scan + i; + } + applog(LOG_ERR, "%d: cmd_READ_RESULT_BCAST failed", a1->chain_id); + return NULL; +} + +static uint8_t *cmd_WRITE_REG(struct A1_chain *a1, uint8_t chip, uint8_t *reg) +{ + uint8_t *ret = exec_cmd(a1, A1_WRITE_REG, chip, reg, 6, 0); + if (ret == NULL || ret[0] != A1_WRITE_REG) { + applog(LOG_ERR, "%d: cmd_WRITE_REG failed", a1->chain_id); + return NULL; + } + return ret; +} + +static uint8_t *cmd_READ_REG(struct A1_chain *a1, uint8_t chip) +{ + uint8_t *ret = exec_cmd(a1, A1_READ_REG, chip, NULL, 0, 6); + if (ret == NULL || ret[0] != A1_READ_REG_RESP || ret[1] != chip) { + applog(LOG_ERR, "%d: cmd_READ_REG chip %d failed", + a1->chain_id, chip); + return NULL; + } + memcpy(a1->spi_rx, ret, 8); + return ret; +} + +static uint8_t *cmd_WRITE_JOB(struct A1_chain *a1, uint8_t chip_id, + uint8_t *job) +{ + /* ensure we push the SPI command to the last chip in chain */ + int tx_len = WRITE_JOB_LENGTH + 2; + memcpy(a1->spi_tx, job, WRITE_JOB_LENGTH); + memset(a1->spi_tx + WRITE_JOB_LENGTH, 0, tx_len - WRITE_JOB_LENGTH); + + assert(spi_transfer(a1->spi_ctx, a1->spi_tx, a1->spi_rx, tx_len)); + hexdump("send: TX", a1->spi_tx, tx_len); + hexdump("send: RX", a1->spi_rx, tx_len); + + int poll_len = 4 * chip_id - 2; + + assert(spi_transfer(a1->spi_ctx, NULL, a1->spi_rx + tx_len, poll_len)); + hexdump("poll: RX", a1->spi_rx + tx_len, poll_len); + + int ack_len = tx_len; + int ack_pos = tx_len + poll_len - ack_len; + hexdump("poll: ACK", a1->spi_rx + ack_pos, tx_len); + + uint8_t *ret = a1->spi_rx + ack_pos; + if (ret[0] != a1->spi_tx[0] || ret[1] != a1->spi_tx[1]){ + applog(LOG_ERR, "%d: cmd_WRITE_JOB failed: " + "0x%02x%02x/0x%02x%02x", a1->chain_id, + ret[0], ret[1], a1->spi_tx[0], a1->spi_tx[1]); + return NULL; + } + return ret; +} + +/********** A1 low level functions */ +#define MAX_PLL_WAIT_CYCLES 25 +#define PLL_CYCLE_WAIT_TIME 40 +static bool check_chip_pll_lock(struct A1_chain *a1, int chip_id, uint8_t *wr) +{ + int n; + for (n = 0; n < MAX_PLL_WAIT_CYCLES; n++) { + /* check for PLL lock status */ + if (cmd_READ_REG(a1, chip_id) && (a1->spi_rx[4] & 1) == 1) + /* double check that we read back what we set before */ + return wr[0] == a1->spi_rx[2] && wr[1] == a1->spi_rx[3]; + + cgsleep_ms(PLL_CYCLE_WAIT_TIME); + } + applog(LOG_ERR, "%d: chip %d failed PLL lock", a1->chain_id, chip_id); + return false; +} + +static uint8_t *get_pll_reg(struct A1_chain *a1, int ref_clock_khz, + int sys_clock_khz) +{ + /* + * PLL parameters after: + * sys_clk = (ref_clk * pll_fbdiv) / (pll_prediv * 2^(pll_postdiv - 1)) + * + * with a higher pll_postdiv being desired over a higher pll_prediv + */ + + static uint8_t writereg[6] = { 0x00, 0x00, 0x21, 0x84, }; + uint8_t pre_div = 1; + uint8_t post_div = 1; + uint32_t fb_div; + + int cid = a1->chain_id; + + applog(LOG_WARNING, "%d: Setting PLL: CLK_REF=%dMHz, SYS_CLK=%dMHz", + cid, ref_clock_khz / 1000, sys_clock_khz / 1000); + + /* Euclidean search for GCD */ + int a = ref_clock_khz; + int b = sys_clock_khz; + while (b != 0) { + int h = a % b; + a = b; + b = h; + } + fb_div = sys_clock_khz / a; + int n = ref_clock_khz / a; + /* approximate multiplier if not exactly matchable */ + if (fb_div > 511) { + int f = fb_div / n; + int m = (f < 32) ? 16 : (f < 64) ? 8 : + (f < 128) ? 4 : (256 < 2) ? 2 : 1; + fb_div = m * fb_div / n; + n = m; + } + /* try to maximize post divider */ + if ((n & 3) == 0) + post_div = 3; + else if ((n & 1) == 0) + post_div = 2; + else + post_div = 1; + /* remainder goes to pre_div */ + pre_div = n / (1 << (post_div - 1)); + /* correct pre_div overflow */ + if (pre_div > 31) { + fb_div = 31 * fb_div / pre_div; + pre_div = 31; + } + writereg[0] = (post_div << 6) | (pre_div << 1) | (fb_div >> 8); + writereg[1] = fb_div & 0xff; + applog(LOG_WARNING, "%d: setting PLL: pre_div=%d, post_div=%d, " + "fb_div=%d: 0x%02x 0x%02x 0x%02x 0x%02x 0x%02x 0x%02x", cid, + pre_div, post_div, fb_div, + writereg[0], writereg[1], writereg[2], + writereg[3], writereg[4], writereg[5]); + return writereg; +} + +static bool set_pll_config(struct A1_chain *a1, int chip_id, + int ref_clock_khz, int sys_clock_khz) +{ + uint8_t *writereg = get_pll_reg(a1, ref_clock_khz, sys_clock_khz); + if (writereg == NULL) + return false; + if (!cmd_WRITE_REG(a1, chip_id, writereg)) + return false; + + int from = (chip_id == 0) ? 0 : chip_id - 1; + int to = (chip_id == 0) ? a1->num_active_chips : chip_id - 1; + + int i; + for (i = from; i < to; i++) { + int cid = i + 1; + if (!check_chip_pll_lock(a1, chip_id, writereg)) { + applog(LOG_ERR, "%d: chip %d failed PLL lock", + a1->chain_id, cid); + return false; + } + } + return true; +} + +#define WEAK_CHIP_THRESHOLD 30 +#define BROKEN_CHIP_THRESHOLD 26 +#define WEAK_CHIP_SYS_CLK (600 * 1000) +#define BROKEN_CHIP_SYS_CLK (400 * 1000) +static bool check_chip(struct A1_chain *a1, int i) +{ + int chip_id = i + 1; + int cid = a1->chain_id; + if (!cmd_READ_REG(a1, chip_id)) { + applog(LOG_WARNING, "%d: Failed to read register for " + "chip %d -> disabling", cid, chip_id); + a1->chips[i].num_cores = 0; + a1->chips[i].disabled = 1; + return false;; + } + a1->chips[i].num_cores = a1->spi_rx[7]; + a1->num_cores += a1->chips[i].num_cores; + applog(LOG_WARNING, "%d: Found chip %d with %d active cores", + cid, chip_id, a1->chips[i].num_cores); + if (a1->chips[i].num_cores < BROKEN_CHIP_THRESHOLD) { + applog(LOG_WARNING, "%d: broken chip %d with %d active " + "cores (threshold = %d)", cid, chip_id, + a1->chips[i].num_cores, BROKEN_CHIP_THRESHOLD); + set_pll_config(a1, chip_id, A1_config_options.ref_clk_khz, + BROKEN_CHIP_SYS_CLK); + cmd_READ_REG(a1, chip_id); + hexdump_error("new.PLL", a1->spi_rx, 8); + a1->chips[i].disabled = true; + a1->num_cores -= a1->chips[i].num_cores; + return false; + } + + if (a1->chips[i].num_cores < WEAK_CHIP_THRESHOLD) { + applog(LOG_WARNING, "%d: weak chip %d with %d active " + "cores (threshold = %d)", cid, + chip_id, a1->chips[i].num_cores, WEAK_CHIP_THRESHOLD); + set_pll_config(a1, chip_id, A1_config_options.ref_clk_khz, + WEAK_CHIP_SYS_CLK); + cmd_READ_REG(a1, chip_id); + hexdump_error("new.PLL", a1->spi_rx, 8); + return false; + } + return true; +} + +/* + * BIST_START works only once after HW reset, on subsequent calls it + * returns 0 as number of chips. + */ +static int chain_detect(struct A1_chain *a1) +{ + int tx_len = 6; + + memset(a1->spi_tx, 0, tx_len); + a1->spi_tx[0] = A1_BIST_START; + a1->spi_tx[1] = 0; + + if (!spi_transfer(a1->spi_ctx, a1->spi_tx, a1->spi_rx, tx_len)) + return 0; + hexdump("TX", a1->spi_tx, 6); + hexdump("RX", a1->spi_rx, 6); + + int i; + int cid = a1->chain_id; + int max_poll_words = MAX_CHAIN_LENGTH * 2; + for(i = 1; i < max_poll_words; i++) { + if (a1->spi_rx[0] == A1_BIST_START && a1->spi_rx[1] == 0) { + spi_transfer(a1->spi_ctx, NULL, a1->spi_rx, 2); + hexdump("RX", a1->spi_rx, 2); + uint8_t n = a1->spi_rx[1]; + a1->num_chips = (i / 2) + 1; + if (a1->num_chips != n) { + applog(LOG_ERR, "%d: enumeration: %d <-> %d", + cid, a1->num_chips, n); + if (n != 0) + a1->num_chips = n; + } + applog(LOG_WARNING, "%d: detected %d chips", + cid, a1->num_chips); + return a1->num_chips; + } + bool s = spi_transfer(a1->spi_ctx, NULL, a1->spi_rx, 2); + hexdump("RX", a1->spi_rx, 2); + if (!s) + return 0; + } + applog(LOG_WARNING, "%d: no A1 chip-chain detected", cid); + return 0; +} + +/********** disable / re-enable related section (temporary for testing) */ +static int get_current_ms(void) +{ + cgtimer_t ct; + cgtimer_time(&ct); + return cgtimer_to_ms(&ct); +} + +static bool is_chip_disabled(struct A1_chain *a1, uint8_t chip_id) +{ + struct A1_chip *chip = &a1->chips[chip_id - 1]; + return chip->disabled || chip->cooldown_begin != 0; +} + +/* check and disable chip, remember time */ +static void disable_chip(struct A1_chain *a1, uint8_t chip_id) +{ + flush_spi(a1); + struct A1_chip *chip = &a1->chips[chip_id - 1]; + int cid = a1->chain_id; + if (is_chip_disabled(a1, chip_id)) { + applog(LOG_WARNING, "%d: chip %d already disabled", + cid, chip_id); + return; + } + applog(LOG_WARNING, "%d: temporary disabling chip %d", cid, chip_id); + chip->cooldown_begin = get_current_ms(); +} + +/* check if disabled chips can be re-enabled */ +void check_disabled_chips(struct A1_chain *a1) +{ + int i; + int cid = a1->chain_id; + for (i = 0; i < a1->num_active_chips; i++) { + int chip_id = i + 1; + struct A1_chip *chip = &a1->chips[i]; + if (!is_chip_disabled(a1, chip_id)) + continue; + /* do not re-enable fully disabled chips */ + if (chip->disabled) + continue; + if (chip->cooldown_begin + COOLDOWN_MS > get_current_ms()) + continue; + if (!cmd_READ_REG(a1, chip_id)) { + chip->fail_count++; + applog(LOG_WARNING, "%d: chip %d not yet working - %d", + cid, chip_id, chip->fail_count); + if (chip->fail_count > DISABLE_CHIP_FAIL_THRESHOLD) { + applog(LOG_WARNING, + "%d: completely disabling chip %d at %d", + cid, chip_id, chip->fail_count); + chip->disabled = true; + a1->num_cores -= chip->num_cores; + continue; + } + /* restart cooldown period */ + chip->cooldown_begin = get_current_ms(); + continue; + } + applog(LOG_WARNING, "%d: chip %d is working again", + cid, chip_id); + chip->cooldown_begin = 0; + chip->fail_count = 0; + } +} + +/********** job creation and result evaluation */ +uint32_t get_diff(double diff) +{ + uint32_t n_bits; + int shift = 29; + double f = (double) 0x0000ffff / diff; + while (f < (double) 0x00008000) { + shift--; + f *= 256.0; + } + while (f >= (double) 0x00800000) { + shift++; + f /= 256.0; + } + n_bits = (int) f + (shift << 24); + return n_bits; +} + +static uint8_t *create_job(uint8_t chip_id, uint8_t job_id, struct work *work) +{ + static uint8_t job[WRITE_JOB_LENGTH] = { + /* command */ + 0x00, 0x00, + /* midstate */ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + /* wdata */ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + /* start nonce */ + 0x00, 0x00, 0x00, 0x00, + /* difficulty 1 */ + 0xff, 0xff, 0x00, 0x1d, + /* end nonce */ + 0xff, 0xff, 0xff, 0xff, + }; + uint8_t *midstate = work->midstate; + uint8_t *wdata = work->data + 64; + + uint32_t *p1 = (uint32_t *) &job[34]; + uint32_t *p2 = (uint32_t *) wdata; + + job[0] = (job_id << 4) | A1_WRITE_JOB; + job[1] = chip_id; + + swab256(job + 2, midstate); + p1[0] = bswap_32(p2[0]); + p1[1] = bswap_32(p2[1]); + p1[2] = bswap_32(p2[2]); +#ifdef USE_REAL_DIFF + p1[4] = get_diff(work->sdiff); +#endif + return job; +} + +/* set work for given chip, returns true if a nonce range was finished */ +static bool set_work(struct A1_chain *a1, uint8_t chip_id, struct work *work, + uint8_t queue_states) +{ + int cid = a1->chain_id; + struct A1_chip *chip = &a1->chips[chip_id - 1]; + bool retval = false; + + int job_id = chip->last_queued_id + 1; + + applog(LOG_INFO, "%d: queuing chip %d with job_id %d, state=0x%02x", + cid, chip_id, job_id, queue_states); + if (job_id == (queue_states & 0x0f) || job_id == (queue_states >> 4)) + applog(LOG_WARNING, "%d: job overlap: %d, 0x%02x", + cid, job_id, queue_states); + + if (chip->work[chip->last_queued_id] != NULL) { + work_completed(a1->cgpu, chip->work[chip->last_queued_id]); + chip->work[chip->last_queued_id] = NULL; + retval = true; + } + uint8_t *jobdata = create_job(chip_id, job_id, work); + if (!cmd_WRITE_JOB(a1, chip_id, jobdata)) { + /* give back work */ + work_completed(a1->cgpu, work); + + applog(LOG_ERR, "%d: failed to set work for chip %d.%d", + cid, chip_id, job_id); + disable_chip(a1, chip_id); + } else { + chip->work[chip->last_queued_id] = work; + chip->last_queued_id++; + chip->last_queued_id &= 3; + } + return retval; +} + +static bool get_nonce(struct A1_chain *a1, uint8_t *nonce, + uint8_t *chip, uint8_t *job_id) +{ + uint8_t *ret = cmd_READ_RESULT_BCAST(a1); + if (ret == NULL) + return false; + if (ret[1] == 0) { + applog(LOG_DEBUG, "%d: output queue empty", a1->chain_id); + return false; + } + *job_id = ret[0] >> 4; + *chip = ret[1]; + memcpy(nonce, ret + 2, 4); + return true; +} + +/* reset input work queues in chip chain */ +static bool abort_work(struct A1_chain *a1) +{ + /* drop jobs already queued: reset strategy 0xed */ + return cmd_RESET_BCAST(a1, 0xed); +} + +/********** driver interface */ +void exit_A1_chain(struct A1_chain *a1) +{ + if (a1 == NULL) + return; + free(a1->chips); + a1->chips = NULL; + a1->spi_ctx = NULL; + free(a1); +} + +struct A1_chain *init_A1_chain(struct spi_ctx *ctx, int chain_id) +{ + int i; + struct A1_chain *a1 = malloc(sizeof(*a1)); + assert(a1 != NULL); + + applog(LOG_DEBUG, "%d: A1 init chain", chain_id); + memset(a1, 0, sizeof(*a1)); + a1->spi_ctx = ctx; + a1->chain_id = chain_id; + + a1->num_chips = chain_detect(a1); + if (a1->num_chips == 0) + goto failure; + + applog(LOG_WARNING, "spidev%d.%d: %d: Found %d A1 chips", + a1->spi_ctx->config.bus, a1->spi_ctx->config.cs_line, + a1->chain_id, a1->num_chips); + + if (!set_pll_config(a1, 0, A1_config_options.ref_clk_khz, + A1_config_options.sys_clk_khz)) + goto failure; + + /* override max number of active chips if requested */ + a1->num_active_chips = a1->num_chips; + if (A1_config_options.override_chip_num > 0 && + a1->num_chips > A1_config_options.override_chip_num) { + a1->num_active_chips = A1_config_options.override_chip_num; + applog(LOG_WARNING, "%d: limiting chain to %d chips", + a1->chain_id, a1->num_active_chips); + } + + a1->chips = calloc(a1->num_active_chips, sizeof(struct A1_chip)); + assert (a1->chips != NULL); + + if (!cmd_BIST_FIX_BCAST(a1)) + goto failure; + + for (i = 0; i < a1->num_active_chips; i++) + check_chip(a1, i); + + applog(LOG_WARNING, "%d: found %d chips with total %d active cores", + a1->chain_id, a1->num_active_chips, a1->num_cores); + + mutex_init(&a1->lock); + INIT_LIST_HEAD(&a1->active_wq.head); + + return a1; + +failure: + exit_A1_chain(a1); + return NULL; +} + +static bool detect_single_chain(void) +{ + board_selector = (struct board_selector*)&dummy_board_selector; + applog(LOG_WARNING, "A1: checking single chain"); + struct A1_chain *a1 = init_A1_chain(spi, 0); + if (a1 == NULL) + return false; + + struct cgpu_info *cgpu = malloc(sizeof(*cgpu)); + assert(cgpu != NULL); + + memset(cgpu, 0, sizeof(*cgpu)); + cgpu->drv = &bitmineA1_drv; + cgpu->name = "BitmineA1.SingleChain"; + cgpu->threads = 1; + + cgpu->device_data = a1; + + a1->cgpu = cgpu; + add_cgpu(cgpu); + applog(LOG_WARNING, "Detected single A1 chain with %d chips / %d cores", + a1->num_active_chips, a1->num_cores); + return true; +} + +bool detect_coincraft_desk(void) +{ + static const uint8_t mcp4x_mapping[] = { 0x2c, 0x2b, 0x2a, 0x29, 0x28 }; + board_selector = ccd_board_selector_init(); + if (board_selector == NULL) { + applog(LOG_INFO, "No CoinCrafd Desk backplane detected."); + return false; + } + board_selector->reset_all(); + + int boards_detected = 0; + int board_id; + for (board_id = 0; board_id < CCD_MAX_CHAINS; board_id++) { + uint8_t mcp_slave = mcp4x_mapping[board_id]; + struct mcp4x *mcp = mcp4x_init(mcp_slave); + if (mcp == NULL) + continue; + + if (A1_config_options.wiper != 0) + mcp->set_wiper(mcp, 0, A1_config_options.wiper); + + applog(LOG_WARNING, "checking board %d...", board_id); + board_selector->select(board_id); + + struct A1_chain *a1 = init_A1_chain(spi, board_id); + board_selector->release(); + if (a1 == NULL) + continue; + + struct cgpu_info *cgpu = malloc(sizeof(*cgpu)); + assert(cgpu != NULL); + + memset(cgpu, 0, sizeof(*cgpu)); + cgpu->drv = &bitmineA1_drv; + cgpu->name = "BitmineA1.CCD"; + cgpu->threads = 1; + + cgpu->device_data = a1; + + a1->cgpu = cgpu; + add_cgpu(cgpu); + boards_detected++; + } + if (boards_detected == 0) + return false; + + applog(LOG_WARNING, "Detected CoinCraft Desk with %d boards", + boards_detected); + return true; +} + +bool detect_coincraft_rig_v3(void) +{ + board_selector = ccr_board_selector_init(); + if (board_selector == NULL) + return false; + + board_selector->reset_all(); + int chains_detected = 0; + int c; + for (c = 0; c < CCR_MAX_CHAINS; c++) { + applog(LOG_WARNING, "checking RIG chain %d...", c); + + if (!board_selector->select(c)) + continue; + + struct A1_chain *a1 = init_A1_chain(spi, c); + board_selector->release(); + + if (a1 == NULL) + continue; + + if (A1_config_options.wiper != 0 && (c & 1) == 0) { + struct mcp4x *mcp = mcp4x_init(0x28); + if (mcp == NULL) { + applog(LOG_ERR, "%d: Cant access poti", c); + } else { + mcp->set_wiper(mcp, 0, A1_config_options.wiper); + mcp->set_wiper(mcp, 1, A1_config_options.wiper); + mcp->exit(mcp); + applog(LOG_WARNING, "%d: set wiper to 0x%02x", + c, A1_config_options.wiper); + } + } + + struct cgpu_info *cgpu = malloc(sizeof(*cgpu)); + assert(cgpu != NULL); + + memset(cgpu, 0, sizeof(*cgpu)); + cgpu->drv = &bitmineA1_drv; + cgpu->name = "BitmineA1.CCR"; + cgpu->threads = 1; + + cgpu->device_data = a1; + + a1->cgpu = cgpu; + add_cgpu(cgpu); + chains_detected++; + } + if (chains_detected == 0) + return false; + + applog(LOG_WARNING, "Detected CoinCraft Rig with %d chains", + chains_detected); + return true; +} + +/* Probe SPI channel and register chip chain */ +void A1_detect(bool hotplug) +{ + /* no hotplug support for SPI */ + if (hotplug) + return; + + /* parse bimine-a1-options */ + if (opt_bitmine_a1_options != NULL && parsed_config_options == NULL) { + int ref_clk = 0; + int sys_clk = 0; + int spi_clk = 0; + int override_chip_num = 0; + int wiper = 0; + + sscanf(opt_bitmine_a1_options, "%d:%d:%d:%d:%d", + &ref_clk, &sys_clk, &spi_clk, &override_chip_num, + &wiper); + if (ref_clk != 0) + A1_config_options.ref_clk_khz = ref_clk; + if (sys_clk != 0) { + if (sys_clk < 100000) + quit(1, "system clock must be above 100MHz"); + A1_config_options.sys_clk_khz = sys_clk; + } + if (spi_clk != 0) + A1_config_options.spi_clk_khz = spi_clk; + if (override_chip_num != 0) + A1_config_options.override_chip_num = override_chip_num; + if (wiper != 0) + A1_config_options.wiper = wiper; + + /* config options are global, scan them once */ + parsed_config_options = &A1_config_options; + } + applog(LOG_DEBUG, "A1 detect"); + + /* register global SPI context */ + struct spi_config cfg = default_spi_config; + cfg.mode = SPI_MODE_1; + cfg.speed = A1_config_options.spi_clk_khz * 1000; + spi = spi_init(&cfg); + if (spi == NULL) + return; + + /* detect and register supported products */ + if (detect_coincraft_desk()) + return; + if (detect_coincraft_rig_v3()) + return; + if (detect_single_chain()) + return; + /* release SPI context if no A1 products found */ + spi_exit(spi); +} + +#define TEMP_UPDATE_INT_MS 2000 +static int64_t A1_scanwork(struct thr_info *thr) +{ + int i; + struct cgpu_info *cgpu = thr->cgpu; + struct A1_chain *a1 = cgpu->device_data; + int32_t nonce_ranges_processed = 0; + + if (a1->num_cores == 0) { + cgpu->deven = DEV_DISABLED; + return 0; + } + board_selector->select(a1->chain_id); + + applog(LOG_DEBUG, "A1 running scanwork"); + + uint32_t nonce; + uint8_t chip_id; + uint8_t job_id; + bool work_updated = false; + + mutex_lock(&a1->lock); + + if (a1->last_temp_time + TEMP_UPDATE_INT_MS < get_current_ms()) { + a1->temp = board_selector->get_temp(0); + a1->last_temp_time = get_current_ms(); + } + int cid = a1->chain_id; + /* poll queued results */ + while (true) { + if (!get_nonce(a1, (uint8_t*)&nonce, &chip_id, &job_id)) + break; + nonce = bswap_32(nonce); + work_updated = true; + if (chip_id < 1 || chip_id > a1->num_active_chips) { + applog(LOG_WARNING, "%d: wrong chip_id %d", + cid, chip_id); + continue; + } + if (job_id < 1 && job_id > 4) { + applog(LOG_WARNING, "%d: chip %d: result has wrong " + "job_id %d", cid, chip_id, job_id); + flush_spi(a1); + continue; + } + + struct A1_chip *chip = &a1->chips[chip_id - 1]; + struct work *work = chip->work[job_id - 1]; + if (work == NULL) { + /* already been flushed => stale */ + applog(LOG_WARNING, "%d: chip %d: stale nonce 0x%08x", + cid, chip_id, nonce); + chip->stales++; + continue; + } + if (!submit_nonce(thr, work, nonce)) { + applog(LOG_WARNING, "%d: chip %d: invalid nonce 0x%08x", + cid, chip_id, nonce); + chip->hw_errors++; + /* add a penalty of a full nonce range on HW errors */ + nonce_ranges_processed--; + continue; + } + applog(LOG_DEBUG, "YEAH: %d: chip %d / job_id %d: nonce 0x%08x", + cid, chip_id, job_id, nonce); + chip->nonces_found++; + } + + /* check for completed works */ + for (i = a1->num_active_chips; i > 0; i--) { + uint8_t c = i; + if (is_chip_disabled(a1, c)) + continue; + if (!cmd_READ_REG(a1, c)) { + disable_chip(a1, c); + continue; + } + uint8_t qstate = a1->spi_rx[5] & 3; + uint8_t qbuff = a1->spi_rx[6]; + struct work *work; + struct A1_chip *chip = &a1->chips[i - 1]; + switch(qstate) { + case 3: + continue; + case 2: + applog(LOG_ERR, "%d: chip %d: invalid state = 2", + cid, c); + continue; + case 1: + /* fall through */ + case 0: + work_updated = true; + + work = wq_dequeue(&a1->active_wq); + if (work == NULL) { + applog(LOG_INFO, "%d: chip %d: work underflow", + cid, c); + break; + } + if (set_work(a1, c, work, qbuff)) { + chip->nonce_ranges_done++; + nonce_ranges_processed++; + } + applog(LOG_DEBUG, "%d: chip %d: job done: %d/%d/%d/%d", + cid, c, + chip->nonce_ranges_done, chip->nonces_found, + chip->hw_errors, chip->stales); + break; + } + } + check_disabled_chips(a1); + mutex_unlock(&a1->lock); + + board_selector->release(); + + if (nonce_ranges_processed < 0) + nonce_ranges_processed = 0; + + if (nonce_ranges_processed != 0) { + applog(LOG_DEBUG, "%d, nonces processed %d", + cid, nonce_ranges_processed); + } + /* in case of no progress, prevent busy looping */ + if (!work_updated) + cgsleep_ms(40); + + return (int64_t)nonce_ranges_processed << 32; +} + + +/* queue two work items per chip in chain */ +static bool A1_queue_full(struct cgpu_info *cgpu) +{ + struct A1_chain *a1 = cgpu->device_data; + int queue_full = false; + + mutex_lock(&a1->lock); + applog(LOG_DEBUG, "%d, A1 running queue_full: %d/%d", + a1->chain_id, a1->active_wq.num_elems, a1->num_active_chips); + + if (a1->active_wq.num_elems >= a1->num_active_chips * 2) + queue_full = true; + else + wq_enqueue(&a1->active_wq, get_queued(cgpu)); + + mutex_unlock(&a1->lock); + + return queue_full; +} + +static void A1_flush_work(struct cgpu_info *cgpu) +{ + struct A1_chain *a1 = cgpu->device_data; + int cid = a1->chain_id; + board_selector->select(cid); + + applog(LOG_DEBUG, "%d: A1 running flushwork", cid); + + int i; + + mutex_lock(&a1->lock); + /* stop chips hashing current work */ + if (!abort_work(a1)) { + applog(LOG_ERR, "%d: failed to abort work in chip chain!", cid); + } + /* flush the work chips were currently hashing */ + for (i = 0; i < a1->num_active_chips; i++) { + int j; + struct A1_chip *chip = &a1->chips[i]; + for (j = 0; j < 4; j++) { + struct work *work = chip->work[j]; + if (work == NULL) + continue; + applog(LOG_DEBUG, "%d: flushing chip %d, work %d: 0x%p", + cid, i, j + 1, work); + work_completed(cgpu, work); + chip->work[j] = NULL; + } + chip->last_queued_id = 0; + } + /* flush queued work */ + applog(LOG_DEBUG, "%d: flushing queued work...", cid); + while (a1->active_wq.num_elems > 0) { + struct work *work = wq_dequeue(&a1->active_wq); + assert(work != NULL); + work_completed(cgpu, work); + } + mutex_unlock(&a1->lock); + + board_selector->release(); +} + +static void A1_get_statline_before(char *buf, size_t len, + struct cgpu_info *cgpu) +{ + struct A1_chain *a1 = cgpu->device_data; + char temp[10]; + if (a1->temp != 0) + snprintf(temp, 9, "%2dC", a1->temp); + tailsprintf(buf, len, " %2d:%2d/%3d %s", + a1->chain_id, a1->num_active_chips, a1->num_cores, + a1->temp == 0 ? " " : temp); +} + +struct device_drv bitmineA1_drv = { + .drv_id = DRIVER_bitmineA1, + .dname = "BitmineA1", + .name = "BA1", + .drv_detect = A1_detect, + + .hash_work = hash_queued_work, + .scanwork = A1_scanwork, + .queue_full = A1_queue_full, + .flush_work = A1_flush_work, + .get_statline_before = A1_get_statline_before, +}; diff --git a/driver-avalon.c b/driver-avalon.c index 83cff74f76..1d00a5fa09 100644 --- a/driver-avalon.c +++ b/driver-avalon.c @@ -1,5 +1,5 @@ /* - * Copyright 2013 Con Kolivas + * Copyright 2013-2014 Con Kolivas * Copyright 2012-2013 Xiangfu * Copyright 2012 Luke Dashjr * Copyright 2012 Andrew Smith @@ -49,16 +49,17 @@ int opt_avalon_fan_max = AVALON_DEFAULT_FAN_MAX_PWM; int opt_avalon_freq_min = AVALON_MIN_FREQUENCY; int opt_avalon_freq_max = AVALON_MAX_FREQUENCY; int opt_bitburner_core_voltage = BITBURNER_DEFAULT_CORE_VOLTAGE; +int opt_bitburner_fury_core_voltage = BITBURNER_FURY_DEFAULT_CORE_VOLTAGE; bool opt_avalon_auto; static int option_offset = -1; -struct device_drv avalon_drv; +static int bbf_option_offset = -1; static int avalon_init_task(struct avalon_task *at, uint8_t reset, uint8_t ff, uint8_t fan, uint8_t timeout, uint8_t asic_num, uint8_t miner_num, uint8_t nonce_elf, - uint8_t gate_miner, int frequency) + uint8_t gate_miner, int frequency, int asic) { uint16_t *lefreq16; uint8_t *buf; @@ -108,8 +109,14 @@ static int avalon_init_task(struct avalon_task *at, buf[9] = 0x01; buf[10] = 0x00; buf[11] = 0x00; + + /* With 55nm, this is the real clock in Mhz, 1Mhz means 2Mhs */ lefreq16 = (uint16_t *)&buf[6]; - *lefreq16 = htole16(frequency * 8); + if (asic == AVALON_A3256) + frequency *= 8; + else + frequency = frequency * 32 / 50 + 0x7FE0; + *lefreq16 = htole16(frequency); return 0; } @@ -141,13 +148,12 @@ static int avalon_write(struct cgpu_info *avalon, char *buf, ssize_t len, int ep return AVA_SEND_OK; } -static int avalon_send_task(const struct avalon_task *at, struct cgpu_info *avalon) +static int avalon_send_task(const struct avalon_task *at, struct cgpu_info *avalon, + struct avalon_info *info) { uint8_t buf[AVALON_WRITE_SIZE + 4 * AVALON_DEFAULT_ASIC_NUM]; int delay, ret, i, ep = C_AVALON_TASK; - struct avalon_info *info; - cgtimer_t ts_start; uint32_t nonce_range; size_t nr_len; @@ -188,7 +194,6 @@ static int avalon_send_task(const struct avalon_task *at, struct cgpu_info *aval tt |= ((buf[4] & 0x80) ? (1 << 0) : 0); buf[4] = tt; #endif - info = avalon->device_data; delay = nr_len * 10 * 1000000; delay = delay / info->baud; delay += 4000; @@ -201,11 +206,63 @@ static int avalon_send_task(const struct avalon_task *at, struct cgpu_info *aval applog(LOG_DEBUG, "Avalon: Sent(%u):", (unsigned int)nr_len); hexdump(buf, nr_len); } - cgsleep_prepare_r(&ts_start); + /* Sleep from the last time we sent data */ + cgsleep_us_r(&info->cgsent, info->send_delay); + + cgsleep_prepare_r(&info->cgsent); ret = avalon_write(avalon, (char *)buf, nr_len, ep); - cgsleep_us_r(&ts_start, delay); - applog(LOG_DEBUG, "Avalon: Sent: Buffer delay: %dus", delay); + applog(LOG_DEBUG, "Avalon: Sent: Buffer delay: %dus", info->send_delay); + info->send_delay = delay; + + return ret; +} + +static int bitburner_send_task(const struct avalon_task *at, struct cgpu_info *avalon) + +{ + uint8_t buf[AVALON_WRITE_SIZE + 4 * AVALON_DEFAULT_ASIC_NUM]; + int ret, ep = C_AVALON_TASK; + cgtimer_t ts_start; + size_t nr_len; + + if (at->nonce_elf) + nr_len = AVALON_WRITE_SIZE + 4 * at->asic_num; + else + nr_len = AVALON_WRITE_SIZE; + + memset(buf, 0, nr_len); + memcpy(buf, at, AVALON_WRITE_SIZE); + +#if defined(__BIG_ENDIAN__) || defined(MIPSEB) + uint8_t tt = 0; + + tt = (buf[0] & 0x0f) << 4; + tt |= ((buf[0] & 0x10) ? (1 << 3) : 0); + tt |= ((buf[0] & 0x20) ? (1 << 2) : 0); + tt |= ((buf[0] & 0x40) ? (1 << 1) : 0); + tt |= ((buf[0] & 0x80) ? (1 << 0) : 0); + buf[0] = tt; + + tt = (buf[4] & 0x0f) << 4; + tt |= ((buf[4] & 0x10) ? (1 << 3) : 0); + tt |= ((buf[4] & 0x20) ? (1 << 2) : 0); + tt |= ((buf[4] & 0x40) ? (1 << 1) : 0); + tt |= ((buf[4] & 0x80) ? (1 << 0) : 0); + buf[4] = tt; +#endif + + if (at->reset) { + ep = C_AVALON_RESET; + nr_len = 1; + } + if (opt_debug) { + applog(LOG_DEBUG, "Avalon: Sent(%u):", (unsigned int)nr_len); + hexdump(buf, nr_len); + } + cgsleep_prepare_r(&ts_start); + ret = avalon_write(avalon, (char *)buf, nr_len, ep); + cgsleep_us_r(&ts_start, 3000); // 3 ms = 333 tasks per second, or 1.4 TH/s return ret; } @@ -219,6 +276,8 @@ static bool avalon_decode_nonce(struct thr_info *thr, struct cgpu_info *avalon, info = avalon->device_data; info->matching_work[work->subid]++; nonce = htole32(ar->nonce); + if (info->asic == AVALON_A3255) + nonce -= 0xc0; applog(LOG_DEBUG, "Avalon: nonce = %0x08x", nonce); return submit_nonce(thr, work, nonce); } @@ -238,16 +297,17 @@ static inline bool avalon_cts(char c) return (c & AVALON_CTS); } -static int avalon_read(struct cgpu_info *avalon, unsigned char *buf, - size_t bufsize, int timeout, int ep) +static int avalon_read(struct cgpu_info *avalon, char *buf, size_t bufsize, int ep) { size_t total = 0, readsize = bufsize + 2; char readbuf[AVALON_READBUF_SIZE]; int err, amount, ofs = 2, cp; - err = usb_read_once_timeout(avalon, readbuf, readsize, &amount, timeout, ep); + err = usb_read_once(avalon, readbuf, readsize, &amount, ep); applog(LOG_DEBUG, "%s%i: Get avalon read got err %d", avalon->drv->name, avalon->device_id, err); + if (err && err != LIBUSB_ERROR_TIMEOUT) + return err; if (amount < 2) goto out; @@ -273,6 +333,7 @@ static int avalon_reset(struct cgpu_info *avalon, bool initial) struct avalon_task at; uint8_t *buf, *tmp; struct timespec p; + struct avalon_info *info = avalon->device_data; /* Send reset, then check for result */ avalon_init_task(&at, 1, 0, @@ -281,10 +342,11 @@ static int avalon_reset(struct cgpu_info *avalon, bool initial) AVALON_DEFAULT_ASIC_NUM, AVALON_DEFAULT_MINER_NUM, 0, 0, - AVALON_DEFAULT_FREQUENCY); + AVALON_DEFAULT_FREQUENCY, + AVALON_A3256); wait_avalon_ready(avalon); - ret = avalon_send_task(&at, avalon); + ret = avalon_send_task(&at, avalon, info); if (unlikely(ret == AVA_SEND_ERROR)) return -1; @@ -293,8 +355,7 @@ static int avalon_reset(struct cgpu_info *avalon, bool initial) return 0; } - ret = avalon_read(avalon, (unsigned char *)&ar, AVALON_READ_SIZE, - AVALON_RESET_TIMEOUT, C_GET_AVALON_RESET); + ret = avalon_read(avalon, (char *)&ar, AVALON_READ_SIZE, C_GET_AVALON_RESET); /* What do these sleeps do?? */ p.tv_sec = 0; @@ -329,9 +390,17 @@ static int avalon_reset(struct cgpu_info *avalon, bool initial) " (%d: %02x %02x %02x %02x)", avalon->drv->name, avalon->device_id, i, buf[0], buf[1], buf[2], buf[3]); /* FIXME: return 1; */ - } else - applog(LOG_WARNING, "%s%d: Reset succeeded", - avalon->drv->name, avalon->device_id); + } else { + /* buf[44]: minor + * buf[45]: day + * buf[46]: year,month, d6: 201306 + */ + info->ctlr_ver = ((buf[46] >> 4) + 2000) * 1000000 + + (buf[46] & 0x0f) * 10000 + + buf[45] * 100 + buf[44]; + applog(LOG_WARNING, "%s%d: Reset succeeded (Controller version: %d)", + avalon->drv->name, avalon->device_id, info->ctlr_ver); + } return 0; } @@ -342,18 +411,19 @@ static int avalon_calc_timeout(int frequency) } static bool get_options(int this_option_offset, int *baud, int *miner_count, - int *asic_count, int *timeout, int *frequency) + int *asic_count, int *timeout, int *frequency, int *asic, + char *options) { char buf[BUFSIZ+1]; - char *ptr, *comma, *colon, *colon2, *colon3, *colon4; + char *ptr, *comma, *colon, *colon2, *colon3, *colon4, *colon5; bool timeout_default; size_t max; int i, tmp; - if (opt_avalon_options == NULL) + if (options == NULL) buf[0] = '\0'; else { - ptr = opt_avalon_options; + ptr = options; for (i = 0; i < this_option_offset; i++) { comma = strchr(ptr, ','); if (comma == NULL) @@ -406,12 +476,12 @@ static bool get_options(int this_option_offset, int *baud, int *miner_count, if (*colon) { tmp = atoi(colon); - if (tmp > 0 && tmp <= AVALON_DEFAULT_MINER_NUM) { + if (tmp > 0 && tmp <= AVALON_MAX_MINER_NUM) { *miner_count = tmp; } else { quit(1, "Invalid avalon-options for " "miner_count (%s) must be 1 ~ %d", - colon, AVALON_DEFAULT_MINER_NUM); + colon, AVALON_MAX_MINER_NUM); } } @@ -448,6 +518,10 @@ static bool get_options(int this_option_offset, int *baud, int *miner_count, } } if (colon4 && *colon4) { + colon5 = strchr(colon4, ':'); + if (colon5) + *(colon5++) = '\0'; + tmp = atoi(colon4); if (tmp < AVALON_MIN_FREQUENCY || tmp > AVALON_MAX_FREQUENCY) { quit(1, "Invalid avalon-options for frequency, must be %d <= frequency <= %d", @@ -456,6 +530,12 @@ static bool get_options(int this_option_offset, int *baud, int *miner_count, *frequency = tmp; if (timeout_default) *timeout = avalon_calc_timeout(*frequency); + if (colon5 && *colon5) { + tmp = atoi(colon5); + if (tmp != AVALON_A3256 && tmp != AVALON_A3255) + quit(1, "Invalid avalon-options for asic, must be 110 or 55"); + *asic = tmp; + } } } } @@ -517,8 +597,9 @@ static void avalon_idle(struct cgpu_info *avalon, struct avalon_info *info) info->idle++; avalon_init_task(&at, 0, 0, info->fan_pwm, info->timeout, info->asic_count, info->miner_count, 1, 1, - info->frequency); - avalon_send_task(&at, avalon); + info->frequency, info->asic); + if (avalon_send_task(&at, avalon, info) == AVA_SEND_ERROR) + break; } applog(LOG_WARNING, "%s%i: Idling %d miners", avalon->drv->name, avalon->device_id, i); wait_avalon_ready(avalon); @@ -531,7 +612,7 @@ static void avalon_initialise(struct cgpu_info *avalon) if (avalon->usbinfo.nodev) return; - interface = avalon->usbdev->found->interface; + interface = usb_interface(avalon); // Reset err = usb_transfer(avalon, FTDI_TYPE_OUT, FTDI_REQUEST_RESET, FTDI_VALUE_RESET, interface, C_RESET); @@ -612,12 +693,20 @@ static void avalon_initialise(struct cgpu_info *avalon) avalon->drv->name, avalon->device_id, err); } +static bool is_bitburner(struct cgpu_info *avalon) +{ + enum sub_ident ident; + + ident = usb_ident(avalon); + return ident == IDENT_BTB || ident == IDENT_BBF; +} + static bool bitburner_set_core_voltage(struct cgpu_info *avalon, int core_voltage) { uint8_t buf[2]; int err; - if (usb_ident(avalon) == IDENT_BTB) { + if (is_bitburner(avalon)) { buf[0] = (uint8_t)core_voltage; buf[1] = (uint8_t)(core_voltage >> 8); err = usb_transfer_data(avalon, FTDI_TYPE_OUT, BITBURNER_REQUEST, @@ -643,7 +732,7 @@ static int bitburner_get_core_voltage(struct cgpu_info *avalon) int err; int amount; - if (usb_ident(avalon) == IDENT_BTB) { + if (is_bitburner(avalon)) { err = usb_transfer_read(avalon, FTDI_TYPE_IN, BITBURNER_REQUEST, BITBURNER_VALUE, BITBURNER_INDEX_GET_VOLTAGE, (char *)buf, sizeof(buf), &amount, @@ -685,10 +774,10 @@ static void bitburner_get_version(struct cgpu_info *avalon) } } -static bool avalon_detect_one(libusb_device *dev, struct usb_find_devices *found) +static struct cgpu_info *avalon_detect_one(libusb_device *dev, struct usb_find_devices *found) { - int baud, miner_count, asic_count, timeout, frequency; - int this_option_offset = ++option_offset; + int baud, miner_count, asic_count, timeout, frequency, asic; + int this_option_offset; struct avalon_info *info; struct cgpu_info *avalon; bool configured; @@ -701,17 +790,19 @@ static bool avalon_detect_one(libusb_device *dev, struct usb_find_devices *found asic_count = AVALON_DEFAULT_ASIC_NUM; timeout = AVALON_DEFAULT_TIMEOUT; frequency = AVALON_DEFAULT_FREQUENCY; - - configured = get_options(this_option_offset, &baud, &miner_count, - &asic_count, &timeout, &frequency); + asic = AVALON_A3256; if (!usb_init(avalon, dev, found)) goto shin; + this_option_offset = usb_ident(avalon) == IDENT_BBF ? ++bbf_option_offset : ++option_offset; + configured = get_options(this_option_offset, &baud, &miner_count, + &asic_count, &timeout, &frequency, &asic, + (usb_ident(avalon) == IDENT_BBF && opt_bitburner_fury_options != NULL) ? opt_bitburner_fury_options : opt_avalon_options); + /* Even though this is an FTDI type chip, we want to do the parsing * all ourselves so set it to std usb type */ avalon->usbdev->usb_type = USB_TYPE_STD; - avalon->usbdev->PrefPacketSize = AVALON_USB_PACKETSIZE; /* We have a real Avalon! */ avalon_initialise(avalon); @@ -722,23 +813,39 @@ static bool avalon_detect_one(libusb_device *dev, struct usb_find_devices *found info = avalon->device_data; if (configured) { + info->asic = asic; info->baud = baud; info->miner_count = miner_count; info->asic_count = asic_count; info->timeout = timeout; info->frequency = frequency; } else { + info->asic = AVALON_A3256; info->baud = AVALON_IO_SPEED; - info->miner_count = AVALON_DEFAULT_MINER_NUM; info->asic_count = AVALON_DEFAULT_ASIC_NUM; - info->timeout = AVALON_DEFAULT_TIMEOUT; - info->frequency = AVALON_DEFAULT_FREQUENCY; + switch (usb_ident(avalon)) { + case IDENT_BBF: + info->miner_count = BITBURNER_FURY_DEFAULT_MINER_NUM; + info->timeout = BITBURNER_FURY_DEFAULT_TIMEOUT; + info->frequency = BITBURNER_FURY_DEFAULT_FREQUENCY; + break; + default: + info->miner_count = AVALON_DEFAULT_MINER_NUM; + info->timeout = AVALON_DEFAULT_TIMEOUT; + info->frequency = AVALON_DEFAULT_FREQUENCY; + } + } + if (info->asic == AVALON_A3255) + info->increment = info->decrement = 50; + else { + info->increment = 2; + info->decrement = 1; } info->fan_pwm = AVALON_DEFAULT_FAN_MIN_PWM; - info->temp_max = 0; /* This is for check the temp/fan every 3~4s */ - info->temp_history_count = (4 / (float)((float)info->timeout * ((float)1.67/0x32))) + 1; + info->temp_history_count = + (4 / (float)((float)info->timeout * (AVALON_A3256 / info->asic) * ((float)1.67/0x32))) + 1; if (info->temp_history_count <= 0) info->temp_history_count = 1; @@ -758,9 +865,9 @@ static bool avalon_detect_one(libusb_device *dev, struct usb_find_devices *found avalon_idle(avalon, info); applog(LOG_DEBUG, "Avalon Detected: %s " - "(miner_count=%d asic_count=%d timeout=%d frequency=%d)", + "(miner_count=%d asic_count=%d timeout=%d frequency=%d chip=%d)", avalon->device_path, info->miner_count, info->asic_count, info->timeout, - info->frequency); + info->frequency, info->asic); if (usb_ident(avalon) == IDENT_BTB) { if (opt_bitburner_core_voltage < BITBURNER_MIN_COREMV || @@ -771,11 +878,22 @@ static bool avalon_detect_one(libusb_device *dev, struct usb_find_devices *found BITBURNER_MAX_COREMV); } else bitburner_set_core_voltage(avalon, opt_bitburner_core_voltage); + } else if (usb_ident(avalon) == IDENT_BBF) { + if (opt_bitburner_fury_core_voltage < BITBURNER_FURY_MIN_COREMV || + opt_bitburner_fury_core_voltage > BITBURNER_FURY_MAX_COREMV) { + quit(1, "Invalid bitburner-fury-voltage %d must be %dmv - %dmv", + opt_bitburner_fury_core_voltage, + BITBURNER_FURY_MIN_COREMV, + BITBURNER_FURY_MAX_COREMV); + } else + bitburner_set_core_voltage(avalon, opt_bitburner_fury_core_voltage); + } + if (is_bitburner(avalon)) { bitburner_get_version(avalon); } - return true; + return avalon; unshin: @@ -788,10 +906,10 @@ static bool avalon_detect_one(libusb_device *dev, struct usb_find_devices *found avalon = usb_free_cgpu(avalon); - return false; + return NULL; } -static void avalon_detect(void) +static void avalon_detect(bool __maybe_unused hotplug) { usb_detect(&avalon_drv, avalon_detect_one); } @@ -892,16 +1010,14 @@ static void *avalon_get_results(void *userdata) const int rsize = AVALON_FTDI_READSIZE; char readbuf[AVALON_READBUF_SIZE]; struct thr_info *thr = info->thr; - cgtimer_t ts_start; int offset = 0, ret = 0; - char threadname[24]; + char threadname[16]; - snprintf(threadname, 24, "ava_recv/%d", avalon->device_id); + snprintf(threadname, sizeof(threadname), "%d/AvaRecv", avalon->device_id); RenameThread(threadname); - cgsleep_prepare_r(&ts_start); while (likely(!avalon->shutdown)) { - unsigned char buf[rsize]; + char buf[rsize]; if (offset >= (int)AVALON_READ_SIZE) avalon_parse_results(avalon, info, thr, readbuf, &offset); @@ -918,16 +1034,10 @@ static void *avalon_get_results(void *userdata) offset = 0; } - /* As the usb read returns after just 1ms, sleep long enough - * to leave the interface idle for writes to occur, but do not - * sleep if we have been receiving data, and we do not yet have - * a full result as more may be coming. */ - if (ret < 1 || offset == 0) - cgsleep_ms_r(&ts_start, AVALON_READ_TIMEOUT); + ret = avalon_read(avalon, buf, rsize, C_AVALON_READ); - cgsleep_prepare_r(&ts_start); - ret = avalon_read(avalon, buf, rsize, AVALON_READ_TIMEOUT, - C_AVALON_READ); + if (unlikely(ret < 0)) + break; if (ret < 1) continue; @@ -943,11 +1053,13 @@ static void *avalon_get_results(void *userdata) return NULL; } -static void avalon_rotate_array(struct cgpu_info *avalon) +static void avalon_rotate_array(struct cgpu_info *avalon, struct avalon_info *info) { + mutex_lock(&info->qlock); avalon->queued = 0; if (++avalon->work_array >= AVALON_ARRAY_SIZE) avalon->work_array = 0; + mutex_unlock(&info->qlock); } static void bitburner_rotate_array(struct cgpu_info *avalon) @@ -979,7 +1091,7 @@ static void avalon_set_freq(struct cgpu_info *avalon, int frequency) static void avalon_inc_freq(struct avalon_info *info) { - info->frequency += 2; + info->frequency += info->increment; if (info->frequency > opt_avalon_freq_max) info->frequency = opt_avalon_freq_max; avalon_set_timeout(info); @@ -989,7 +1101,7 @@ static void avalon_inc_freq(struct avalon_info *info) static void avalon_dec_freq(struct avalon_info *info) { - info->frequency -= 1; + info->frequency -= info->decrement; if (info->frequency < opt_avalon_freq_min) info->frequency = opt_avalon_freq_min; avalon_set_timeout(info); @@ -1015,15 +1127,14 @@ static void avalon_adjust_freq(struct avalon_info *info, struct cgpu_info *avalo avalon->drv->name, avalon->device_id); avalon_dec_freq(info); } - } else if (info->auto_nonces >= (AVALON_AUTO_CYCLE * 19 / 20) && - info->auto_nonces <= (AVALON_AUTO_CYCLE * 21 / 20)) { - int total = info->auto_nonces + info->auto_hw; - - /* Try to keep hw errors < 2% */ - if (info->auto_hw * 100 < total) - avalon_inc_freq(info); - else if (info->auto_hw * 66 > total) - avalon_dec_freq(info); + } else if (info->auto_nonces >= AVALON_AUTO_CYCLE / 2) { + int total = info->auto_nonces + info->auto_hw; + + /* Try to keep hw errors < 2% */ + if (info->auto_hw * 100 < total) + avalon_inc_freq(info); + else if (info->auto_hw * 66 > total) + avalon_dec_freq(info); } avalon_reset_auto(info); mutex_unlock(&info->lock); @@ -1035,9 +1146,9 @@ static void *avalon_send_tasks(void *userdata) struct cgpu_info *avalon = (struct cgpu_info *)userdata; struct avalon_info *info = avalon->device_data; const int avalon_get_work_count = info->miner_count; - char threadname[24]; + char threadname[16]; - snprintf(threadname, 24, "ava_send/%d", avalon->device_id); + snprintf(threadname, sizeof(threadname), "%d/AvaSend", avalon->device_id); RenameThread(threadname); while (likely(!avalon->shutdown)) { @@ -1056,7 +1167,6 @@ static void *avalon_send_tasks(void *userdata) us_timeout = 0x100000000ll / info->asic_count / info->frequency; cgsleep_prepare_r(&ts_start); - mutex_lock(&info->qlock); start_count = avalon->work_array * avalon_get_work_count; end_count = start_count + avalon_get_work_count; for (i = start_count, j = 0; i < end_count; i++, j++) { @@ -1067,10 +1177,11 @@ static void *avalon_send_tasks(void *userdata) break; } + mutex_lock(&info->qlock); if (likely(j < avalon->queued && !info->overheat && avalon->works[i])) { avalon_init_task(&at, 0, 0, info->fan_pwm, info->timeout, info->asic_count, - info->miner_count, 1, 0, info->frequency); + info->miner_count, 1, 0, info->frequency, info->asic); avalon_create_task(&at, avalon->works[i]); info->auto_queued++; } else { @@ -1082,26 +1193,27 @@ static void *avalon_send_tasks(void *userdata) idle_freq = AVALON_MIN_FREQUENCY; avalon_init_task(&at, 0, 0, info->fan_pwm, info->timeout, info->asic_count, - info->miner_count, 1, 1, idle_freq); + info->miner_count, 1, 1, idle_freq, info->asic); /* Reset the auto_queued count if we end up * idling any miners. */ avalon_reset_auto(info); } + mutex_unlock(&info->qlock); - ret = avalon_send_task(&at, avalon); + ret = avalon_send_task(&at, avalon, info); if (unlikely(ret == AVA_SEND_ERROR)) { + /* Send errors are fatal */ applog(LOG_ERR, "%s%i: Comms error(buffer)", avalon->drv->name, avalon->device_id); dev_error(avalon, REASON_DEV_COMMS_ERROR); - info->reset = true; - break; + goto out; } } - avalon_rotate_array(avalon); - pthread_cond_signal(&info->qcond); - mutex_unlock(&info->qlock); + avalon_rotate_array(avalon, info); + + cgsem_post(&info->qsem); if (unlikely(idled)) { applog(LOG_WARNING, "%s%i: Idled %d miners", @@ -1114,6 +1226,7 @@ static void *avalon_send_tasks(void *userdata) * fall short of the full duration. */ cgsleep_us_r(&ts_start, us_timeout); } +out: return NULL; } @@ -1122,9 +1235,9 @@ static void *bitburner_send_tasks(void *userdata) struct cgpu_info *avalon = (struct cgpu_info *)userdata; struct avalon_info *info = avalon->device_data; const int avalon_get_work_count = info->miner_count; - char threadname[24]; + char threadname[16]; - snprintf(threadname, 24, "ava_send/%d", avalon->device_id); + snprintf(threadname, sizeof(threadname), "%d/AvaSend", avalon->device_id); RenameThread(threadname); while (likely(!avalon->shutdown)) { @@ -1154,7 +1267,7 @@ static void *bitburner_send_tasks(void *userdata) if (likely(j < avalon->queued && !info->overheat && avalon->works[i])) { avalon_init_task(&at, 0, 0, info->fan_pwm, info->timeout, info->asic_count, - info->miner_count, 1, 0, info->frequency); + info->miner_count, 1, 0, info->frequency, info->asic); avalon_create_task(&at, avalon->works[i]); info->auto_queued++; } else { @@ -1166,13 +1279,13 @@ static void *bitburner_send_tasks(void *userdata) idle_freq = AVALON_MIN_FREQUENCY; avalon_init_task(&at, 0, 0, info->fan_pwm, info->timeout, info->asic_count, - info->miner_count, 1, 1, idle_freq); + info->miner_count, 1, 1, idle_freq, info->asic); /* Reset the auto_queued count if we end up * idling any miners. */ avalon_reset_auto(info); } - ret = avalon_send_task(&at, avalon); + ret = bitburner_send_task(&at, avalon); if (unlikely(ret == AVA_SEND_ERROR)) { applog(LOG_ERR, "%s%i: Comms error(buffer)", @@ -1184,9 +1297,10 @@ static void *bitburner_send_tasks(void *userdata) } bitburner_rotate_array(avalon); - pthread_cond_signal(&info->qcond); mutex_unlock(&info->qlock); + cgsem_post(&info->qsem); + if (unlikely(idled)) { applog(LOG_WARNING, "%s%i: Idled %d miners", avalon->drv->name, avalon->device_id, idled); @@ -1202,7 +1316,7 @@ static bool avalon_prepare(struct thr_info *thr) int array_size = AVALON_ARRAY_SIZE; void *(*write_thread_fn)(void *) = avalon_send_tasks; - if (usb_ident(avalon) == IDENT_BTB) { + if (is_bitburner(avalon)) { array_size = BITBURNER_ARRAY_SIZE; write_thread_fn = bitburner_send_tasks; } @@ -1216,8 +1330,7 @@ static bool avalon_prepare(struct thr_info *thr) info->thr = thr; mutex_init(&info->lock); mutex_init(&info->qlock); - if (unlikely(pthread_cond_init(&info->qcond, NULL))) - quit(1, "Failed to pthread_cond_init avalon qcond"); + cgsem_init(&info->qsem); if (pthread_create(&info->read_thr, NULL, avalon_get_results, (void *)avalon)) quit(1, "Failed to create avalon read_thr"); @@ -1230,20 +1343,11 @@ static bool avalon_prepare(struct thr_info *thr) return true; } -static void do_avalon_close(struct thr_info *thr) +static inline void record_temp_fan(struct cgpu_info *avalon, struct avalon_info *info, + struct avalon_result *ar) { - struct cgpu_info *avalon = thr->cgpu; - struct avalon_info *info = avalon->device_data; + double temp_max; - pthread_join(info->read_thr, NULL); - pthread_join(info->write_thr, NULL); - avalon_running_reset(avalon, info); - - info->no_matching_work = 0; -} - -static inline void record_temp_fan(struct avalon_info *info, struct avalon_result *ar, float *temp_avg) -{ info->fan0 = ar->fan0 * AVALON_FAN_FACTOR; info->fan1 = ar->fan1 * AVALON_FAN_FACTOR; info->fan2 = ar->fan2 * AVALON_FAN_FACTOR; @@ -1264,14 +1368,12 @@ static inline void record_temp_fan(struct avalon_info *info, struct avalon_resul info->temp2 = 0 - ((~ar->temp2 & 0x7f) + 1); } - *temp_avg = info->temp2 > info->temp1 ? info->temp2 : info->temp1; - - if (info->temp0 > info->temp_max) - info->temp_max = info->temp0; - if (info->temp1 > info->temp_max) - info->temp_max = info->temp1; - if (info->temp2 > info->temp_max) - info->temp_max = info->temp2; + temp_max = info->temp0; + if (info->temp1 > temp_max) + temp_max = info->temp1; + if (info->temp2 > temp_max) + temp_max = info->temp2; + avalon->temp = avalon->temp * 0.63 + temp_max * 0.37; } static void temp_rise(struct avalon_info *info, int temp) @@ -1337,17 +1439,17 @@ static inline void adjust_fan(struct avalon_info *info) static void avalon_update_temps(struct cgpu_info *avalon, struct avalon_info *info, struct avalon_result *ar) { - record_temp_fan(info, ar, &(avalon->temp)); + record_temp_fan(avalon, info, ar); applog(LOG_INFO, "Avalon: Fan1: %d/m, Fan2: %d/m, Fan3: %d/m\t" - "Temp1: %dC, Temp2: %dC, Temp3: %dC, TempMAX: %dC", + "Temp1: %dC, Temp2: %dC, Temp3: %dC, TempMAX: %.0fC", info->fan0, info->fan1, info->fan2, - info->temp0, info->temp1, info->temp2, info->temp_max); + info->temp0, info->temp1, info->temp2, avalon->temp); info->temp_history_index++; info->temp_sum += avalon->temp; applog(LOG_DEBUG, "Avalon: temp_index: %d, temp_count: %d, temp_old: %d", info->temp_history_index, info->temp_history_count, info->temp_old); - if (usb_ident(avalon) == IDENT_BTB) { + if (is_bitburner(avalon)) { info->core_voltage = bitburner_get_core_voltage(avalon); } if (info->temp_history_index == info->temp_history_count) { @@ -1369,7 +1471,7 @@ static void get_avalon_statline_before(char *buf, size_t bufsiz, struct cgpu_inf struct avalon_info *info = avalon->device_data; int lowfan = 10000; - if (usb_ident(avalon) == IDENT_BTB) { + if (is_bitburner(avalon)) { int temp = info->temp0; if (info->temp2 > temp) temp = info->temp2; @@ -1377,7 +1479,7 @@ static void get_avalon_statline_before(char *buf, size_t bufsiz, struct cgpu_inf temp = 99; if (temp < 0) temp = 0; - tailsprintf(buf, bufsiz, "%2dC %3d %4dmV | ", temp, info->frequency, info->core_voltage); + tailsprintf(buf, bufsiz, "%2dC %3dMHz %4dmV", temp, info->frequency, info->core_voltage); } else { /* Find the lowest fan speed of the ASIC cooling fans. */ if (info->fan1 >= 0 && info->fan1 < lowfan) @@ -1385,7 +1487,7 @@ static void get_avalon_statline_before(char *buf, size_t bufsiz, struct cgpu_inf if (info->fan2 >= 0 && info->fan2 < lowfan) lowfan = info->fan2; - tailsprintf(buf, bufsiz, "%2dC/%3dC %04dR | ", info->temp0, info->temp2, lowfan); + tailsprintf(buf, bufsiz, "%2dC/%3dC %04dR", info->temp0, info->temp2, lowfan); } } @@ -1426,27 +1528,20 @@ static int64_t avalon_scanhash(struct thr_info *thr) struct cgpu_info *avalon = thr->cgpu; struct avalon_info *info = avalon->device_data; const int miner_count = info->miner_count; - struct timeval now, then, tdiff; - int64_t hash_count, us_timeout; - struct timespec abstime; + int64_t hash_count, ms_timeout; /* Half nonce range */ - us_timeout = 0x80000000ll / info->asic_count / info->frequency; - us_to_timeval(&tdiff, us_timeout); - cgtime(&now); - timeradd(&now, &tdiff, &then); - timeval_to_spec(&abstime, &then); + ms_timeout = 0x80000000ll / info->asic_count / info->frequency / 1000; /* Wait until avalon_send_tasks signals us that it has completed - * sending its work or a full nonce range timeout has occurred */ - mutex_lock(&info->qlock); - pthread_cond_timedwait(&info->qcond, &info->qlock, &abstime); - mutex_unlock(&info->qlock); + * sending its work or a full nonce range timeout has occurred. We use + * cgsems to never miss a wakeup. */ + cgsem_mswait(&info->qsem, ms_timeout); mutex_lock(&info->lock); hash_count = 0xffffffffull * (uint64_t)info->nonces; - avalon->results += info->nonces + info->idle; - if (avalon->results > miner_count) + avalon->results += info->nonces; + if (avalon->results > miner_count || info->idle) avalon->results = miner_count; if (!info->reset) avalon->results--; @@ -1455,10 +1550,11 @@ static int64_t avalon_scanhash(struct thr_info *thr) /* Check for nothing but consecutive bad results or consistently less * results than we should be getting and reset the FPGA if necessary */ - if (usb_ident(avalon) != IDENT_BTB) { + if (!is_bitburner(avalon)) { if (avalon->results < -miner_count && !info->reset) { applog(LOG_ERR, "%s%d: Result return rate low, resetting!", avalon->drv->name, avalon->device_id); + avalon->results = miner_count; info->reset = true; } } @@ -1466,7 +1562,7 @@ static int64_t avalon_scanhash(struct thr_info *thr) if (unlikely(avalon->usbinfo.nodev)) { applog(LOG_ERR, "%s%d: Device disappeared, shutting down thread", avalon->drv->name, avalon->device_id); - avalon->shutdown = true; + hash_count = -1; } /* This hashmeter is just a utility counter based on returned shares */ @@ -1477,11 +1573,12 @@ static void avalon_flush_work(struct cgpu_info *avalon) { struct avalon_info *info = avalon->device_data; - mutex_lock(&info->qlock); - /* Will overwrite any work queued */ + /* Will overwrite any work queued. Do this unlocked since it's just + * changing a single non-critical value and prevents deadlocks */ avalon->queued = 0; - pthread_cond_signal(&info->qcond); - mutex_unlock(&info->qlock); + + /* Signal main loop we need more work */ + cgsem_post(&info->qsem); } static struct api_data *avalon_api_stats(struct cgpu_info *cgpu) @@ -1506,7 +1603,7 @@ static struct api_data *avalon_api_stats(struct cgpu_info *cgpu) root = api_add_int(root, "temp1", &(info->temp0), false); root = api_add_int(root, "temp2", &(info->temp1), false); root = api_add_int(root, "temp3", &(info->temp2), false); - root = api_add_int(root, "temp_max", &(info->temp_max), false); + root = api_add_double(root, "temp_max", &cgpu->temp, false); root = api_add_percent(root, "Device Hardware%", &hwp, true); root = api_add_int(root, "no_matching_work", &(info->no_matching_work), false); @@ -1516,19 +1613,31 @@ static struct api_data *avalon_api_stats(struct cgpu_info *cgpu) sprintf(mcw, "match_work_count%d", i + 1); root = api_add_int(root, mcw, &(info->matching_work[i]), false); } - if (usb_ident(cgpu) == IDENT_BTB) { + if (is_bitburner(cgpu)) { root = api_add_int(root, "core_voltage", &(info->core_voltage), false); snprintf(buf, sizeof(buf), "%"PRIu8".%"PRIu8".%"PRIu8, info->version1, info->version2, info->version3); root = api_add_string(root, "version", buf, true); } + root = api_add_uint32(root, "Controller Version", &(info->ctlr_ver), false); + root = api_add_uint32(root, "Avalon Chip", &(info->asic), false); return root; } static void avalon_shutdown(struct thr_info *thr) { - do_avalon_close(thr); + struct cgpu_info *avalon = thr->cgpu; + struct avalon_info *info = avalon->device_data; + + pthread_join(info->read_thr, NULL); + pthread_join(info->write_thr, NULL); + avalon_running_reset(avalon, info); + cgsem_destroy(&info->qsem); + mutex_destroy(&info->qlock); + mutex_destroy(&info->lock); + free(avalon->works); + avalon->works = NULL; } static char *avalon_set_device(struct cgpu_info *avalon, char *option, char *setting, char *replybuf) @@ -1543,7 +1652,7 @@ static char *avalon_set_device(struct cgpu_info *avalon, char *option, char *set } if (strcasecmp(option, "millivolts") == 0 || strcasecmp(option, "mv") == 0) { - if (usb_ident(avalon) != IDENT_BTB) { + if (!is_bitburner(avalon)) { sprintf(replybuf, "%s cannot set millivolts", avalon->drv->name); return replybuf; } @@ -1590,7 +1699,7 @@ static char *avalon_set_device(struct cgpu_info *avalon, char *option, char *set } struct device_drv avalon_drv = { - .drv_id = DRIVER_AVALON, + .drv_id = DRIVER_avalon, .dname = "avalon", .name = "AVA", .drv_detect = avalon_detect, diff --git a/driver-avalon.h b/driver-avalon.h index 3f7c69601e..c718366f5d 100644 --- a/driver-avalon.h +++ b/driver-avalon.h @@ -1,6 +1,6 @@ /* * Copyright 2013 Avalon project - * Copyright 2013 Con Kolivas + * Copyright 2013-2014 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 @@ -22,6 +22,10 @@ #define AVALON_HASH_TIME_FACTOR ((float)1.67/0x32) #define AVALON_RESET_PITCH (300*1000*1000) + +#define AVALON_A3256 110 +#define AVALON_A3255 55 + #define AVALON_FAN_FACTOR 120 #define AVALON_PWM_MAX 0xA0 #define AVALON_DEFAULT_FAN_MIN 20 @@ -33,28 +37,40 @@ #define AVALON_TEMP_HYSTERESIS 3 #define AVALON_TEMP_OVERHEAT 60 +/* Avalon-based BitBurner. */ #define BITBURNER_DEFAULT_CORE_VOLTAGE 1200 /* in millivolts */ #define BITBURNER_MIN_COREMV 1000 /* change here if you want to risk killing it :) */ #define BITBURNER_MAX_COREMV 1400 +/* BitFury-based BitBurner. */ +#define BITBURNER_FURY_DEFAULT_CORE_VOLTAGE 900 /* in millivolts */ +#define BITBURNER_FURY_MIN_COREMV 700 +/* change here if you want to risk killing it :) */ +#define BITBURNER_FURY_MAX_COREMV 1100 + #define AVALON_DEFAULT_TIMEOUT 0x2D #define AVALON_MIN_FREQUENCY 256 -#define AVALON_MAX_FREQUENCY 1024 +#define AVALON_MAX_FREQUENCY 2000 #define AVALON_TIMEOUT_FACTOR 12690 #define AVALON_DEFAULT_FREQUENCY 282 #define AVALON_DEFAULT_MINER_NUM 0x20 +#define AVALON_MAX_MINER_NUM 0x100 #define AVALON_DEFAULT_ASIC_NUM 0xA +/* Default number of miners for Bitburner Fury is for a stack of 8 boards, + but it will work acceptably for smaller stacks, too */ +#define BITBURNER_FURY_DEFAULT_MINER_NUM 128 +#define BITBURNER_FURY_DEFAULT_FREQUENCY 256 +#define BITBURNER_FURY_DEFAULT_TIMEOUT 50 + #define AVALON_AUTO_CYCLE 1024 #define AVALON_FTDI_READSIZE 510 -#define AVALON_USB_PACKETSIZE 512 #define AVALON_READBUF_SIZE 8192 -#define AVALON_RESET_TIMEOUT 100 -#define AVALON_READ_TIMEOUT 18 /* Enough to only half fill the buffer */ -#define AVALON_LATENCY 1 +/* Set latency to just less than full 64 byte packet size at 115200 baud */ +#define AVALON_LATENCY 4 struct avalon_task { uint8_t reset :1; @@ -112,7 +128,6 @@ struct avalon_info { int temp0; int temp1; int temp2; - int temp_max; int temp_history_count; int temp_history_index; int temp_sum; @@ -122,21 +137,27 @@ struct avalon_info { int core_voltage; int no_matching_work; - int matching_work[AVALON_DEFAULT_MINER_NUM]; + int matching_work[AVALON_MAX_MINER_NUM]; int frequency; + uint32_t asic; + uint32_t ctlr_ver; struct thr_info *thr; pthread_t read_thr; pthread_t write_thr; pthread_mutex_t lock; pthread_mutex_t qlock; - pthread_cond_t qcond; - int nonces; + cgsem_t qsem; + cgtimer_t cgsent; + int send_delay; + int nonces; int auto_queued; int auto_nonces; int auto_hw; + int increment; + int decrement; int idle; bool reset; @@ -178,6 +199,7 @@ extern int opt_avalon_freq_min; extern int opt_avalon_freq_max; extern bool opt_avalon_auto; extern int opt_bitburner_core_voltage; +extern int opt_bitburner_fury_core_voltage; extern char *set_avalon_fan(char *arg); extern char *set_avalon_freq(char *arg); diff --git a/driver-avalon2.c b/driver-avalon2.c new file mode 100644 index 0000000000..2ce216d6c6 --- /dev/null +++ b/driver-avalon2.c @@ -0,0 +1,1097 @@ +/* + * Copyright 2013-2014 Con Kolivas + * Copyright 2012-2014 Xiangfu + * Copyright 2012 Luke Dashjr + * Copyright 2012 Andrew Smith + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the Free + * Software Foundation; either version 3 of the License, or (at your option) + * any later version. See COPYING for more details. + */ + +#include "config.h" + +#include +#include +#include +#include +#include +#include +#include +#ifndef WIN32 + #include + #include + #include + #ifndef O_CLOEXEC + #define O_CLOEXEC 0 + #endif +#else + #include + #include +#endif + +#include "elist.h" +#include "miner.h" +#include "fpgautils.h" +#include "driver-avalon2.h" +#include "crc.h" +#include "sha2.h" + +#define ASSERT1(condition) __maybe_unused static char sizeof_uint32_t_must_be_4[(condition)?1:-1] +ASSERT1(sizeof(uint32_t) == 4); + +#define get_fan_pwm(v) (AVA2_PWM_MAX - (v) * AVA2_PWM_MAX / 100) + +int opt_avalon2_freq_min; +int opt_avalon2_freq_max; + +int opt_avalon2_fan_min = AVA2_DEFAULT_FAN_MIN; +int opt_avalon2_fan_max = AVA2_DEFAULT_FAN_MAX; +static int avalon2_fan_min = get_fan_pwm(AVA2_DEFAULT_FAN_MIN); +static int avalon2_fan_max = get_fan_pwm(AVA2_DEFAULT_FAN_MAX); + +int opt_avalon2_voltage_min; +int opt_avalon2_voltage_max; + +int opt_avalon2_overheat = AVALON2_TEMP_OVERHEAT; +int opt_avalon2_polling_delay = AVALON2_DEFAULT_POLLING_DELAY; + +enum avalon2_fan_fixed opt_avalon2_fan_fixed = FAN_AUTO; + +#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 void sha256_prehash(const unsigned char *message, unsigned int len, unsigned char *digest) +{ + sha256_ctx ctx; + int i; + sha256_init(&ctx); + sha256_update(&ctx, message, len); + + for (i = 0; i < 8; i++) { + UNPACK32(ctx.h[i], &digest[i << 2]); + } +} + +static inline uint8_t rev8(uint8_t d) +{ + int i; + uint8_t out = 0; + + /* (from left to right) */ + for (i = 0; i < 8; i++) + if (d & (1 << i)) + out |= (1 << (7 - i)); + + return out; +} + +char *set_avalon2_fan(char *arg) +{ + int val1, val2, ret; + + ret = sscanf(arg, "%d-%d", &val1, &val2); + if (ret < 1) + return "No values passed to avalon2-fan"; + if (ret == 1) + val2 = val1; + + if (val1 < 0 || val1 > 100 || val2 < 0 || val2 > 100 || val2 < val1) + return "Invalid value passed to avalon2-fan"; + + opt_avalon2_fan_min = val1; + opt_avalon2_fan_max = val2; + avalon2_fan_min = get_fan_pwm(val1); + avalon2_fan_max = get_fan_pwm(val2); + + return NULL; +} + +char *set_avalon2_fixed_speed(enum avalon2_fan_fixed *f) +{ + *f = FAN_FIXED; + return NULL; +} + +char *set_avalon2_freq(char *arg) +{ + int val1, val2, ret; + + ret = sscanf(arg, "%d-%d", &val1, &val2); + if (ret < 1) + return "No values passed to avalon2-freq"; + if (ret == 1) + val2 = val1; + + if (val1 < AVA2_DEFAULT_FREQUENCY_MIN || val1 > AVA2_DEFAULT_FREQUENCY_MAX || + val2 < AVA2_DEFAULT_FREQUENCY_MIN || val2 > AVA2_DEFAULT_FREQUENCY_MAX || + val2 < val1) + return "Invalid value passed to avalon2-freq"; + + opt_avalon2_freq_min = val1; + opt_avalon2_freq_max = val2; + + return NULL; +} + +char *set_avalon2_voltage(char *arg) +{ + int val1, val2, ret; + + ret = sscanf(arg, "%d-%d", &val1, &val2); + if (ret < 1) + return "No values passed to avalon2-voltage"; + if (ret == 1) + val2 = val1; + + if (val1 < AVA2_DEFAULT_VOLTAGE_MIN || val1 > AVA2_DEFAULT_VOLTAGE_MAX || + val2 < AVA2_DEFAULT_VOLTAGE_MIN || val2 > AVA2_DEFAULT_VOLTAGE_MAX || + val2 < val1) + return "Invalid value passed to avalon2-voltage"; + + opt_avalon2_voltage_min = val1; + opt_avalon2_voltage_max = val2; + + return NULL; +} + +static int avalon2_init_pkg(struct avalon2_pkg *pkg, uint8_t type, uint8_t idx, uint8_t cnt) +{ + unsigned short crc; + + pkg->head[0] = AVA2_H1; + pkg->head[1] = AVA2_H2; + + pkg->type = type; + pkg->idx = idx; + pkg->cnt = cnt; + + crc = crc16(pkg->data, AVA2_P_DATA_LEN); + + pkg->crc[0] = (crc & 0xff00) >> 8; + pkg->crc[1] = crc & 0x00ff; + 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, "Avalon2: job_id not match! [%04x:%04x (%s)]", + crc, crc_expect, pool_job_id); + + return 1; +} + +static inline int get_temp_max(struct avalon2_info *info) +{ + int i; + for (i = 0; i < 2 * AVA2_DEFAULT_MODULARS; i++) { + if (info->temp_max <= info->temp[i]) + info->temp_max = info->temp[i]; + } + return info->temp_max; +} + +static inline int get_current_temp_max(struct avalon2_info *info) +{ + int i; + int t = info->temp[0]; + + for (i = 1; i < 2 * AVA2_DEFAULT_MODULARS; i++) { + if (info->temp[i] > t) + t = info->temp[i]; + } + return t; +} + +/* http://www.onsemi.com/pub_link/Collateral/ADP3208D.PDF */ +static inline uint32_t encode_voltage(uint32_t v) +{ + return rev8((0x78 - v / 125) << 1 | 1) << 8; +} + +static inline uint32_t decode_voltage(uint32_t v) +{ + return (0x78 - (rev8(v >> 8) >> 1)) * 125; +} + +static void adjust_fan(struct avalon2_info *info) +{ + int t; + + if (opt_avalon2_fan_fixed == FAN_FIXED) { + info->fan_pct = opt_avalon2_fan_min; + info->fan_pwm = get_fan_pwm(info->fan_pct); + return; + } + + t = get_current_temp_max(info); + + /* TODO: Add options for temperature range and fan adjust function */ + if (t < 60) + info->fan_pct = opt_avalon2_fan_min; + else if (t > 80) + info->fan_pct = opt_avalon2_fan_max; + else + info->fan_pct = (t - 60) * (opt_avalon2_fan_max - opt_avalon2_fan_min) / 20 + opt_avalon2_fan_min; + + info->fan_pwm = get_fan_pwm(info->fan_pct); +} + +static inline int mm_cmp_1404(struct avalon2_info *info, int modular) +{ + /* <= 1404 return 1 */ + char *mm_1404 = "1404"; + return strncmp(info->mm_version[modular] + 2, mm_1404, 4) > 0 ? 0 : 1; +} + +static inline int mm_cmp_1406(struct avalon2_info *info) +{ + /* <= 1406 return 1 */ + char *mm_1406 = "1406"; + int i; + for (i = 0; i < AVA2_DEFAULT_MODULARS; i++) { + if (info->enable[i] && + strncmp(info->mm_version[i] + 2, mm_1406, 4) <= 0) + return 1; + } + + return 0; +} + +static int decode_pkg(struct thr_info *thr, struct avalon2_ret *ar, uint8_t *pkg) +{ + struct cgpu_info *avalon2 = thr->cgpu; + struct avalon2_info *info = avalon2->device_data; + struct pool *pool, *real_pool, *pool_stratum = &info->pool; + + unsigned int expected_crc; + unsigned int actual_crc; + uint32_t nonce, nonce2, miner, modular_id; + int pool_no; + uint8_t job_id[4]; + int tmp; + + int type = AVA2_GETS_ERROR; + + memcpy((uint8_t *)ar, pkg, AVA2_READ_SIZE); + + if (ar->head[0] == AVA2_H1 && ar->head[1] == AVA2_H2) { + expected_crc = crc16(ar->data, AVA2_P_DATA_LEN); + actual_crc = (ar->crc[0] & 0xff) | + ((ar->crc[1] & 0xff) << 8); + + type = ar->type; + applog(LOG_DEBUG, "Avalon2: %d: expected crc(%04x), actual_crc(%04x)", + type, expected_crc, actual_crc); + if (expected_crc != actual_crc) + goto out; + + memcpy(&modular_id, ar->data + 28, 4); + modular_id = be32toh(modular_id); + if (modular_id > 3) + modular_id = 0; + + switch(type) { + case AVA2_P_NONCE: + applog(LOG_DEBUG, "Avalon2: AVA2_P_NONCE"); + memcpy(&miner, ar->data + 0, 4); + memcpy(&pool_no, ar->data + 4, 4); + memcpy(&nonce2, ar->data + 8, 4); + /* Calc time ar->data + 12 */ + memcpy(&nonce, ar->data + 16, 4); + memcpy(job_id, ar->data + 20, 4); + + miner = be32toh(miner); + pool_no = be32toh(pool_no); + if (miner >= AVA2_DEFAULT_MINERS || + modular_id >= AVA2_DEFAULT_MINERS || + pool_no >= total_pools || + pool_no < 0) { + applog(LOG_DEBUG, "Avalon2: Wrong miner/pool/id no %d,%d,%d", miner, pool_no, modular_id); + break; + } else + info->matching_work[modular_id * AVA2_DEFAULT_MINERS + miner]++; + nonce2 = be32toh(nonce2); + nonce = be32toh(nonce); + nonce -= 0x180; + + applog(LOG_DEBUG, "Avalon2: Found! %d: (%08x) (%08x)", + pool_no, nonce2, nonce); + + real_pool = pool = pools[pool_no]; + if (job_idcmp(job_id, pool->swork.job_id)) { + if (!job_idcmp(job_id, pool_stratum->swork.job_id)) { + applog(LOG_DEBUG, "Avalon2: Match to previous stratum! (%s)", pool_stratum->swork.job_id); + pool = pool_stratum; + } else { + applog(LOG_ERR, "Avalon2: Cannot match to any stratum! (%s)", pool->swork.job_id); + break; + } + } + + if (submit_nonce2_nonce(thr, pool, real_pool, nonce2, nonce, 0)) + info->failing = false; + break; + case AVA2_P_STATUS: + applog(LOG_DEBUG, "Avalon2: AVA2_P_STATUS"); + memcpy(&tmp, ar->data, 4); + tmp = be32toh(tmp); + info->temp[0 + modular_id * 2] = tmp >> 16; + info->temp[1 + modular_id * 2] = tmp & 0xffff; + + memcpy(&tmp, ar->data + 4, 4); + tmp = be32toh(tmp); + info->fan[0 + modular_id * 2] = tmp >> 16; + info->fan[1 + modular_id * 2] = tmp & 0xffff; + + memcpy(&(info->get_frequency[modular_id]), ar->data + 8, 4); + memcpy(&(info->get_voltage[modular_id]), ar->data + 12, 4); + memcpy(&(info->local_work[modular_id]), ar->data + 16, 4); + memcpy(&(info->hw_work[modular_id]), ar->data + 20, 4); + memcpy(&(info->power_good[modular_id]), ar->data + 24, 4); + + info->get_frequency[modular_id] = be32toh(info->get_frequency[modular_id]); + if (info->dev_type[modular_id] == AVA2_ID_AVA3) + info->get_frequency[modular_id] = info->get_frequency[modular_id] * 768 / 65; + info->get_voltage[modular_id] = be32toh(info->get_voltage[modular_id]); + info->local_work[modular_id] = be32toh(info->local_work[modular_id]); + info->hw_work[modular_id] = be32toh(info->hw_work[modular_id]); + + info->local_works[modular_id] += info->local_work[modular_id]; + info->hw_works[modular_id] += info->hw_work[modular_id]; + + info->get_voltage[modular_id] = decode_voltage(info->get_voltage[modular_id]); + info->power_good[modular_id] = info->power_good[modular_id] >> 24; + + avalon2->temp = get_temp_max(info); + break; + case AVA2_P_ACKDETECT: + applog(LOG_DEBUG, "Avalon2: AVA2_P_ACKDETECT"); + break; + case AVA2_P_ACK: + applog(LOG_DEBUG, "Avalon2: AVA2_P_ACK"); + break; + case AVA2_P_NAK: + applog(LOG_DEBUG, "Avalon2: AVA2_P_NAK"); + break; + default: + applog(LOG_DEBUG, "Avalon2: Unknown response"); + type = AVA2_GETS_ERROR; + break; + } + } + +out: + return type; +} + +static inline int avalon2_gets(struct cgpu_info *avalon2, uint8_t *buf) +{ + int read_amount = AVA2_READ_SIZE, ret = 0; + uint8_t *buf_back = buf; + + while (true) { + int err; + + do { + memset(buf, 0, read_amount); + err = usb_read(avalon2, (char *)buf, read_amount, &ret, C_AVA2_READ); + if (unlikely(err && err != LIBUSB_ERROR_TIMEOUT)) { + applog(LOG_ERR, "Avalon2: Error %d on read in avalon_gets got %d", err, ret); + return AVA2_GETS_ERROR; + } + if (likely(ret >= read_amount)) { + if (unlikely(buf_back[0] != AVA2_H1 || buf_back[1] != AVA2_H2)) + return AVA2_GETS_ERROR; + return AVA2_GETS_OK; + } + buf += ret; + read_amount -= ret; + } while (ret > 0); + + return AVA2_GETS_TIMEOUT; + } +} + +static int avalon2_send_pkg(struct cgpu_info *avalon2, const struct avalon2_pkg *pkg) +{ + int err, amount; + uint8_t buf[AVA2_WRITE_SIZE]; + int nr_len = AVA2_WRITE_SIZE; + + if (unlikely(avalon2->usbinfo.nodev)) + return AVA2_SEND_ERROR; + + memcpy(buf, pkg, AVA2_WRITE_SIZE); + err = usb_write(avalon2, (char *)buf, nr_len, &amount, C_AVA2_WRITE); + if (err || amount != nr_len) { + applog(LOG_DEBUG, "Avalon2: Send(%d)!", amount); + usb_nodev(avalon2); + return AVA2_SEND_ERROR; + } + + return AVA2_SEND_OK; +} + +static void avalon2_stratum_pkgs(struct cgpu_info *avalon2, struct pool *pool) +{ + const int merkle_offset = 36; + struct avalon2_pkg pkg; + int i, a, b, tmp; + unsigned char target[32]; + int job_id_len, n2size; + unsigned short crc; + int diff; + + /* Cap maximum diff in order to still get shares */ + diff = pool->swork.diff; + if (diff > 64) + diff = 64; + else if (unlikely(diff < 1)) + diff = 1; + + /* Send out the first stratum message STATIC */ + applog(LOG_DEBUG, "Avalon2: Pool stratum message STATIC: %d, %d, %d, %d, %d", + pool->coinbase_len, + pool->nonce2_offset, + pool->n2size, + merkle_offset, + pool->merkles); + memset(pkg.data, 0, AVA2_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); + + tmp = be32toh(diff); + memcpy(pkg.data + 20, &tmp, 4); + + tmp = be32toh((int)pool->pool_no); + memcpy(pkg.data + 24, &tmp, 4); + + avalon2_init_pkg(&pkg, AVA2_P_STATIC, 1, 1); + if (avalon2_send_pkg(avalon2, &pkg)) + return; + + set_target(target, pool->sdiff); + memcpy(pkg.data, target, 32); + if (opt_debug) { + char *target_str; + target_str = bin2hex(target, 32); + applog(LOG_DEBUG, "Avalon2: Pool stratum target: %s", target_str); + free(target_str); + } + avalon2_init_pkg(&pkg, AVA2_P_TARGET, 1, 1); + if (avalon2_send_pkg(avalon2, &pkg)) + return; + + applog(LOG_DEBUG, "Avalon2: Pool stratum message JOBS_ID: %s", + pool->swork.job_id); + memset(pkg.data, 0, AVA2_P_DATA_LEN); + + job_id_len = strlen(pool->swork.job_id); + crc = crc16((unsigned char *)pool->swork.job_id, job_id_len); + pkg.data[0] = (crc & 0xff00) >> 8; + pkg.data[1] = crc & 0x00ff; + avalon2_init_pkg(&pkg, AVA2_P_JOB_ID, 1, 1); + if (avalon2_send_pkg(avalon2, &pkg)) + return; + + if (pool->coinbase_len > AVA2_P_COINBASE_SIZE) { + int coinbase_len_posthash, coinbase_len_prehash; + uint8_t coinbase_prehash[32]; + 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 / AVA2_P_DATA_LEN) + 1; + b = coinbase_len_posthash % AVA2_P_DATA_LEN; + memcpy(pkg.data, coinbase_prehash, 32); + avalon2_init_pkg(&pkg, AVA2_P_COINBASE, 1, a + (b ? 1 : 0)); + if (avalon2_send_pkg(avalon2, &pkg)) + return; + applog(LOG_DEBUG, "Avalon2: Pool stratum message modified COINBASE: %d %d", a, b); + for (i = 1; i < a; i++) { + memcpy(pkg.data, pool->coinbase + coinbase_len_prehash + i * 32 - 32, 32); + avalon2_init_pkg(&pkg, AVA2_P_COINBASE, i + 1, a + (b ? 1 : 0)); + if (avalon2_send_pkg(avalon2, &pkg)) + return; + } + if (b) { + memset(pkg.data, 0, AVA2_P_DATA_LEN); + memcpy(pkg.data, pool->coinbase + coinbase_len_prehash + i * 32 - 32, b); + avalon2_init_pkg(&pkg, AVA2_P_COINBASE, i + 1, i + 1); + if (avalon2_send_pkg(avalon2, &pkg)) + return; + } + } else { + a = pool->coinbase_len / AVA2_P_DATA_LEN; + b = pool->coinbase_len % AVA2_P_DATA_LEN; + applog(LOG_DEBUG, "Avalon2: Pool stratum message COINBASE: %d %d", a, b); + for (i = 0; i < a; i++) { + memcpy(pkg.data, pool->coinbase + i * 32, 32); + avalon2_init_pkg(&pkg, AVA2_P_COINBASE, i + 1, a + (b ? 1 : 0)); + if (avalon2_send_pkg(avalon2, &pkg)) + return; + } + if (b) { + memset(pkg.data, 0, AVA2_P_DATA_LEN); + memcpy(pkg.data, pool->coinbase + i * 32, b); + avalon2_init_pkg(&pkg, AVA2_P_COINBASE, i + 1, i + 1); + if (avalon2_send_pkg(avalon2, &pkg)) + return; + } + } + + + b = pool->merkles; + applog(LOG_DEBUG, "Avalon2: Pool stratum message MERKLES: %d", b); + for (i = 0; i < b; i++) { + memset(pkg.data, 0, AVA2_P_DATA_LEN); + memcpy(pkg.data, pool->swork.merkle_bin[i], 32); + avalon2_init_pkg(&pkg, AVA2_P_MERKLES, i + 1, b); + if (avalon2_send_pkg(avalon2, &pkg)) + return; + } + + applog(LOG_DEBUG, "Avalon2: Pool stratum message HEADER: 4"); + for (i = 0; i < 4; i++) { + memset(pkg.data, 0, AVA2_P_HEADER); + memcpy(pkg.data, pool->header_bin + i * 32, 32); + avalon2_init_pkg(&pkg, AVA2_P_HEADER, i + 1, 4); + if (avalon2_send_pkg(avalon2, &pkg)) + return; + } +} + +static void avalon2_initialise(struct cgpu_info *avalon2) +{ + uint32_t ava2_data[2] = { PL2303_VALUE_LINE0, PL2303_VALUE_LINE1 }; + int interface; + + if (avalon2->usbinfo.nodev) + return; + + interface = usb_interface(avalon2); + // Set Data Control + usb_transfer(avalon2, PL2303_VENDOR_OUT, PL2303_REQUEST_VENDOR, 8, + interface, C_VENDOR); + if (avalon2->usbinfo.nodev) + return; + + usb_transfer(avalon2, PL2303_VENDOR_OUT, PL2303_REQUEST_VENDOR, 9, + interface, C_VENDOR); + + if (avalon2->usbinfo.nodev) + return; + + // Set Line Control + usb_transfer_data(avalon2, PL2303_CTRL_OUT, PL2303_REQUEST_LINE, PL2303_VALUE_LINE, + interface, ava2_data, PL2303_VALUE_LINE_SIZE, C_SETLINE); + if (avalon2->usbinfo.nodev) + return; + + // Vendor + usb_transfer(avalon2, PL2303_VENDOR_OUT, PL2303_REQUEST_VENDOR, PL2303_VALUE_VENDOR, + interface, C_VENDOR); + + if (avalon2->usbinfo.nodev) + return; + + // Set More Line Control ? + usb_transfer(avalon2, PL2303_CTRL_OUT, PL2303_REQUEST_CTRL, 3, interface, C_SETLINE); +} + +static struct cgpu_info *avalon2_detect_one(struct libusb_device *dev, struct usb_find_devices *found) +{ + struct avalon2_info *info; + int ackdetect; + int err, amount; + int tmp, i, j, modular[AVA2_DEFAULT_MODULARS] = {}; + char mm_version[AVA2_DEFAULT_MODULARS][16]; + + struct cgpu_info *avalon2 = usb_alloc_cgpu(&avalon2_drv, 1); + struct avalon2_pkg detect_pkg; + struct avalon2_ret ret_pkg; + + if (!usb_init(avalon2, dev, found)) { + applog(LOG_ERR, "Avalon2 failed usb_init"); + avalon2 = usb_free_cgpu(avalon2); + return NULL; + } + avalon2_initialise(avalon2); + + for (j = 0; j < 2; j++) { + for (i = 0; i < AVA2_DEFAULT_MODULARS; i++) { + strcpy(mm_version[i], AVA2_MM_VERNULL); + /* Send out detect pkg */ + memset(detect_pkg.data, 0, AVA2_P_DATA_LEN); + tmp = be32toh(i); + memcpy(detect_pkg.data + 28, &tmp, 4); + + avalon2_init_pkg(&detect_pkg, AVA2_P_DETECT, 1, 1); + avalon2_send_pkg(avalon2, &detect_pkg); + err = usb_read(avalon2, (char *)&ret_pkg, AVA2_READ_SIZE, &amount, C_AVA2_READ); + if (err < 0 || amount != AVA2_READ_SIZE) { + applog(LOG_DEBUG, "%s %d: Avalon2 failed usb_read with err %d amount %d", + avalon2->drv->name, avalon2->device_id, err, amount); + continue; + } + ackdetect = ret_pkg.type; + applog(LOG_DEBUG, "Avalon2 Detect ID[%d]: %d", i, ackdetect); + if (ackdetect != AVA2_P_ACKDETECT && modular[i] == 0) + continue; + modular[i] = 1; + memcpy(mm_version[i], ret_pkg.data, 15); + mm_version[i][15] = '\0'; + } + } + if (!modular[0] && !modular[1] && !modular[2] && !modular[3]) { + applog(LOG_DEBUG, "Not an Avalon2 device"); + usb_uninit(avalon2); + usb_free_cgpu(avalon2); + return NULL; + } + + /* We have a real Avalon! */ + avalon2->threads = AVA2_MINER_THREADS; + add_cgpu(avalon2); + + update_usb_stats(avalon2); + + applog(LOG_INFO, "%s %d: Found at %s", avalon2->drv->name, avalon2->device_id, + avalon2->device_path); + + avalon2->device_data = calloc(sizeof(struct avalon2_info), 1); + if (unlikely(!(avalon2->device_data))) + quit(1, "Failed to calloc avalon2_info"); + + info = avalon2->device_data; + + info->fan_pwm = get_fan_pwm(AVA2_DEFAULT_FAN_PWM); + info->temp_max = 0; + + for (i = 0; i < AVA2_DEFAULT_MODULARS; i++) { + strcpy(info->mm_version[i], mm_version[i]); + info->modulars[i] = modular[i]; /* Enable modular */ + info->enable[i] = modular[i]; + info->dev_type[i] = AVA2_ID_AVAX; + + if (!strncmp((char *)&(info->mm_version[i]), AVA2_FW2_PREFIXSTR, 2)) { + info->dev_type[i] = AVA2_ID_AVA2; + info->set_voltage = AVA2_DEFAULT_VOLTAGE_MIN; + info->set_frequency = AVA2_DEFAULT_FREQUENCY; + } + if (!strncmp((char *)&(info->mm_version[i]), AVA2_FW3_PREFIXSTR, 2)) { + info->dev_type[i] = AVA2_ID_AVA3; + info->set_voltage = AVA2_AVA3_VOLTAGE; + info->set_frequency = AVA2_AVA3_FREQUENCY; + } + } + + if (!opt_avalon2_voltage_min) + opt_avalon2_voltage_min = opt_avalon2_voltage_max = info->set_voltage; + if (!opt_avalon2_freq_min) + opt_avalon2_freq_min = opt_avalon2_freq_max = info->set_frequency; + + return avalon2; +} + +static inline void avalon2_detect(bool __maybe_unused hotplug) +{ + usb_detect(&avalon2_drv, avalon2_detect_one); +} + +static bool avalon2_prepare(struct thr_info *thr) +{ + struct cgpu_info *avalon2 = thr->cgpu; + struct avalon2_info *info = avalon2->device_data; + + cglock_init(&info->pool.data_lock); + + return true; +} + +static int polling(struct thr_info *thr, struct cgpu_info *avalon2, struct avalon2_info *info) +{ + struct avalon2_pkg send_pkg; + struct avalon2_ret ar; + int i, tmp; + + for (i = 0; i < AVA2_DEFAULT_MODULARS; i++) { + if (info->modulars[i] && info->enable[i]) { + uint8_t result[AVA2_READ_SIZE]; + int ret; + + cgsleep_ms(opt_avalon2_polling_delay); + memset(send_pkg.data, 0, AVA2_P_DATA_LEN); + + tmp = be32toh(info->led_red[i]); /* RED LED */ + memcpy(send_pkg.data + 12, &tmp, 4); + + tmp = be32toh(i); /* ID */ + memcpy(send_pkg.data + 28, &tmp, 4); + if (info->led_red[i] && mm_cmp_1404(info, i)) { + avalon2_init_pkg(&send_pkg, AVA2_P_TEST, 1, 1); + avalon2_send_pkg(avalon2, &send_pkg); + info->enable[i] = 0; + continue; + } else + avalon2_init_pkg(&send_pkg, AVA2_P_POLLING, 1, 1); + + avalon2_send_pkg(avalon2, &send_pkg); + ret = avalon2_gets(avalon2, result); + if (ret == AVA2_GETS_OK) + decode_pkg(thr, &ar, result); + } + } + + return 0; +} + +static void copy_pool_stratum(struct avalon2_info *info, struct pool *pool) +{ + int i; + int merkles = pool->merkles; + size_t coinbase_len = pool->coinbase_len; + struct pool *pool_stratum = &info->pool; + + if (!job_idcmp((unsigned char *)pool->swork.job_id, pool_stratum->swork.job_id)) + return; + + cg_wlock(&pool_stratum->data_lock); + free(pool_stratum->swork.job_id); + free(pool_stratum->nonce1); + free(pool_stratum->coinbase); + + align_len(&coinbase_len); + pool_stratum->coinbase = calloc(coinbase_len, 1); + if (unlikely(!pool_stratum->coinbase)) + quit(1, "Failed to calloc pool_stratum coinbase in avalon2"); + 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 = realloc(pool_stratum->swork.merkle_bin, + sizeof(char *) * merkles + 1); + for (i = 0; i < merkles; i++) { + pool_stratum->swork.merkle_bin[i] = malloc(32); + if (unlikely(!pool_stratum->swork.merkle_bin[i])) + quit(1, "Failed to malloc pool_stratum swork merkle_bin"); + 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 avalon2_update(struct cgpu_info *avalon2) +{ + struct avalon2_info *info = avalon2->device_data; + struct thr_info *thr = avalon2->thr[0]; + struct avalon2_pkg send_pkg; + uint32_t tmp, range, start; + struct work *work; + struct pool *pool; + + applog(LOG_DEBUG, "Avalon2: New stratum: restart: %d, update: %d", + thr->work_restart, thr->work_update); + thr->work_update = false; + thr->work_restart = false; + + work = get_work(thr, thr->id); /* Make sure pool is ready */ + discard_work(work); /* Don't leak memory */ + + pool = current_pool(); + if (!pool->has_stratum) + quit(1, "Avalon2: MM have to use stratum pool"); + + if (pool->coinbase_len > AVA2_P_COINBASE_SIZE) { + applog(LOG_INFO, "Avalon2: MM pool coinbase length(%d) is more than %d", + pool->coinbase_len, AVA2_P_COINBASE_SIZE); + if (mm_cmp_1406(info)) { + applog(LOG_ERR, "Avalon2: MM version less then 1406"); + return; + } + if ((pool->coinbase_len - pool->nonce2_offset + 64) > AVA2_P_COINBASE_SIZE) { + applog(LOG_ERR, "Avalon2: MM pool modified coinbase length(%d) is more than %d", + pool->coinbase_len - pool->nonce2_offset + 64, AVA2_P_COINBASE_SIZE); + return; + } + } + if (pool->merkles > AVA2_P_MERKLES_COUNT) { + applog(LOG_ERR, "Avalon2: MM merkles have to less then %d", AVA2_P_MERKLES_COUNT); + return; + } + if (pool->n2size < 3) { + applog(LOG_ERR, "Avalon2: MM nonce2 size have to >= 3 (%d)", pool->n2size); + return; + } + + cgtime(&info->last_stratum); + cg_rlock(&pool->data_lock); + info->pool_no = pool->pool_no; + copy_pool_stratum(info, pool); + avalon2_stratum_pkgs(avalon2, pool); + cg_runlock(&pool->data_lock); + + /* Configuer the parameter from outside */ + adjust_fan(info); + info->set_voltage = opt_avalon2_voltage_min; + info->set_frequency = opt_avalon2_freq_min; + + /* Set the Fan, Voltage and Frequency */ + memset(send_pkg.data, 0, AVA2_P_DATA_LEN); + + tmp = be32toh(info->fan_pwm); + memcpy(send_pkg.data, &tmp, 4); + + applog(LOG_INFO, "Avalon2: Temp max: %d, Cut off temp: %d", + get_current_temp_max(info), opt_avalon2_overheat); + if (get_current_temp_max(info) >= opt_avalon2_overheat) + tmp = encode_voltage(0); + else + tmp = encode_voltage(info->set_voltage); + tmp = be32toh(tmp); + memcpy(send_pkg.data + 4, &tmp, 4); + + tmp = be32toh(info->set_frequency); + memcpy(send_pkg.data + 8, &tmp, 4); + + /* Configure the nonce2 offset and range */ + if (pool->n2size == 3) + range = 0xffffff / (total_devices + 1); + else + range = 0xffffffff / (total_devices + 1); + start = range * (avalon2->device_id + 1); + + tmp = be32toh(start); + memcpy(send_pkg.data + 12, &tmp, 4); + + tmp = be32toh(range); + memcpy(send_pkg.data + 16, &tmp, 4); + + /* Package the data */ + avalon2_init_pkg(&send_pkg, AVA2_P_SET, 1, 1); + avalon2_send_pkg(avalon2, &send_pkg); +} + +static int64_t avalon2_scanhash(struct thr_info *thr) +{ + struct timeval current_stratum; + struct cgpu_info *avalon2 = thr->cgpu; + struct avalon2_info *info = avalon2->device_data; + int stdiff; + int64_t h; + int i; + + if (unlikely(avalon2->usbinfo.nodev)) { + applog(LOG_ERR, "%s %d: Device disappeared, shutting down thread", + avalon2->drv->name, avalon2->device_id); + return -1; + } + + /* Stop polling the device if there is no stratum in 3 minutes, network is down */ + cgtime(¤t_stratum); + if (tdiff(¤t_stratum, &(info->last_stratum)) > (double)(3.0 * 60.0)) + return 0; + + polling(thr, avalon2, info); + + stdiff = share_work_tdiff(avalon2); + if (unlikely(info->failing)) { + if (stdiff > 120) { + applog(LOG_ERR, "%s %d: No valid shares for over 2 minutes, shutting down thread", + avalon2->drv->name, avalon2->device_id); + return -1; + } + } else if (stdiff > 60) { + applog(LOG_ERR, "%s %d: No valid shares for over 1 minute, issuing a USB reset", + avalon2->drv->name, avalon2->device_id); + usb_reset(avalon2); + info->failing = true; + + } + + h = 0; + for (i = 0; i < AVA2_DEFAULT_MODULARS; i++) { + h += info->enable[i] ? (info->local_work[i] - info->hw_work[i]) : 0; + } + return h * 0xffffffff; +} + +static struct api_data *avalon2_api_stats(struct cgpu_info *cgpu) +{ + struct api_data *root = NULL; + struct avalon2_info *info = cgpu->device_data; + int i, j, a, b; + char buf[24]; + double hwp; + int minerindex, minercount; + + for (i = 0; i < AVA2_DEFAULT_MODULARS; i++) { + if(info->dev_type[i] == AVA2_ID_AVAX) + continue; + sprintf(buf, "ID%d MM Version", i + 1); + root = api_add_string(root, buf, (char *)&(info->mm_version[i]), false); + } + + minerindex = 0; + minercount = 0; + for (i = 0; i < AVA2_DEFAULT_MODULARS; i++) { + if (info->dev_type[i] == AVA2_ID_AVAX) { + minerindex += AVA2_DEFAULT_MINERS; + continue; + } + + if (info->dev_type[i] == AVA2_ID_AVA2) + minercount = AVA2_DEFAULT_MINERS; + + if (info->dev_type[i] == AVA2_ID_AVA3) + minercount = AVA2_AVA3_MINERS; + + for (j = minerindex; j < (minerindex + minercount); j++) { + sprintf(buf, "Match work count%02d", j+1); + root = api_add_int(root, buf, &(info->matching_work[j]), false); + } + minerindex += AVA2_DEFAULT_MINERS; + } + + for (i = 0; i < AVA2_DEFAULT_MODULARS; i++) { + if(info->dev_type[i] == AVA2_ID_AVAX) + continue; + sprintf(buf, "Local works%d", i + 1); + root = api_add_int(root, buf, &(info->local_works[i]), false); + } + for (i = 0; i < AVA2_DEFAULT_MODULARS; i++) { + if(info->dev_type[i] == AVA2_ID_AVAX) + continue; + sprintf(buf, "Hardware error works%d", i + 1); + root = api_add_int(root, buf, &(info->hw_works[i]), false); + } + for (i = 0; i < AVA2_DEFAULT_MODULARS; i++) { + if(info->dev_type[i] == AVA2_ID_AVAX) + continue; + a = info->hw_works[i]; + b = info->local_works[i]; + hwp = b ? ((double)a / (double)b) : 0; + + sprintf(buf, "Device hardware error%d%%", i + 1); + root = api_add_percent(root, buf, &hwp, true); + } + for (i = 0; i < 2 * AVA2_DEFAULT_MODULARS; i++) { + if(info->dev_type[i/2] == AVA2_ID_AVAX) + continue; + sprintf(buf, "Temperature%d", i + 1); + root = api_add_int(root, buf, &(info->temp[i]), false); + } + for (i = 0; i < 2 * AVA2_DEFAULT_MODULARS; i++) { + if(info->dev_type[i/2] == AVA2_ID_AVAX) + continue; + sprintf(buf, "Fan%d", i + 1); + root = api_add_int(root, buf, &(info->fan[i]), false); + } + for (i = 0; i < AVA2_DEFAULT_MODULARS; i++) { + if(info->dev_type[i] == AVA2_ID_AVAX) + continue; + sprintf(buf, "Voltage%d", i + 1); + root = api_add_int(root, buf, &(info->get_voltage[i]), false); + } + for (i = 0; i < AVA2_DEFAULT_MODULARS; i++) { + if(info->dev_type[i] == AVA2_ID_AVAX) + continue; + sprintf(buf, "Frequency%d", i + 1); + root = api_add_int(root, buf, &(info->get_frequency[i]), false); + } + for (i = 0; i < AVA2_DEFAULT_MODULARS; i++) { + if(info->dev_type[i] == AVA2_ID_AVAX) + continue; + sprintf(buf, "Power good %02x", i + 1); + root = api_add_int(root, buf, &(info->power_good[i]), false); + } + for (i = 0; i < AVA2_DEFAULT_MODULARS; i++) { + if(info->dev_type[i] == AVA2_ID_AVAX) + continue; + sprintf(buf, "Led %02x", i + 1); + root = api_add_int(root, buf, &(info->led_red[i]), false); + } + + return root; +} + +static void avalon2_statline_before(char *buf, size_t bufsiz, struct cgpu_info *avalon2) +{ + struct avalon2_info *info = avalon2->device_data; + int temp = get_current_temp_max(info); + float volts = (float)info->set_voltage / 10000; + + tailsprintf(buf, bufsiz, "%4dMhz %2dC %3d%% %.3fV", info->set_frequency, + temp, info->fan_pct, volts); +} + +static void avalon2_shutdown(struct thr_info *thr) +{ + struct cgpu_info *avalon2 = thr->cgpu; + int interface = usb_interface(avalon2); + + usb_transfer(avalon2, PL2303_CTRL_OUT, PL2303_REQUEST_CTRL, 0, interface, C_SETLINE); +} + +struct device_drv avalon2_drv = { + .drv_id = DRIVER_avalon2, + .dname = "avalon2", + .name = "AV2", + .get_api_stats = avalon2_api_stats, + .get_statline_before = avalon2_statline_before, + .drv_detect = avalon2_detect, + .thread_prepare = avalon2_prepare, + .hash_work = hash_driver_work, + .flush_work = avalon2_update, + .update_work = avalon2_update, + .scanwork = avalon2_scanhash, + .thread_shutdown = avalon2_shutdown, +}; diff --git a/driver-avalon2.h b/driver-avalon2.h new file mode 100644 index 0000000000..17f1c2ef3d --- /dev/null +++ b/driver-avalon2.h @@ -0,0 +1,163 @@ +/* + * Copyright 2013-2014 Con Kolivas + * Copyright 2012-2014 Xiangfu + * + * 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 _AVALON2_H_ +#define _AVALON2_H_ + +#include "util.h" +#include "fpgautils.h" + +#ifdef USE_AVALON2 + +#define AVA2_MINER_THREADS 1 + +#define AVA2_RESET_FAULT_DECISECONDS 10 +#define AVA2_IO_SPEED 115200 + +#define AVA2_DEFAULT_MODULARS 4 + +#define AVA2_PWM_MAX 0x3FF +#define AVA2_DEFAULT_FAN_PWM 15 /* % */ +#define AVA2_DEFAULT_FAN_MIN 10 +#define AVA2_DEFAULT_FAN_MAX 85 + +#define AVALON2_TEMP_OVERHEAT 98 +#define AVALON2_DEFAULT_POLLING_DELAY 20 /* ms */ + +#define AVA2_DEFAULT_VOLTAGE_MIN 6000 +#define AVA2_DEFAULT_VOLTAGE_MAX 11000 + +#define AVA2_DEFAULT_FREQUENCY_MIN 300 +#define AVA2_DEFAULT_FREQUENCY_MAX 2000 + +/* Avalon2 default values */ +#define AVA2_DEFAULT_MINERS 10 +#define AVA2_DEFAULT_VOLTAGE 10000 /* v * 10000 */ +#define AVA2_DEFAULT_FREQUENCY 1500 /* In MHs */ + +/* Avalon3 default values */ +#define AVA2_AVA3_MINERS 5 +#define AVA2_AVA3_VOLTAGE 6660 /* 0.666v */ +#define AVA2_AVA3_FREQUENCY 450 /* MHz * 11.8 = MHs: 450MHz means ~5.3GHs */ + +/* Avalon2 protocol package type */ +#define AVA2_H1 'A' +#define AVA2_H2 'V' + +#define AVA2_P_COINBASE_SIZE (6 * 1024) +#define AVA2_P_MERKLES_COUNT 20 + +#define AVA2_P_COUNT 39 +#define AVA2_P_DATA_LEN (AVA2_P_COUNT - 7) + +#define AVA2_P_DETECT 10 +#define AVA2_P_STATIC 11 +#define AVA2_P_JOB_ID 12 +#define AVA2_P_COINBASE 13 +#define AVA2_P_MERKLES 14 +#define AVA2_P_HEADER 15 +#define AVA2_P_POLLING 16 +#define AVA2_P_TARGET 17 +#define AVA2_P_REQUIRE 18 +#define AVA2_P_SET 19 +#define AVA2_P_TEST 20 + +#define AVA2_P_ACK 21 +#define AVA2_P_NAK 22 +#define AVA2_P_NONCE 23 +#define AVA2_P_STATUS 24 +#define AVA2_P_ACKDETECT 25 +#define AVA2_P_TEST_RET 26 +/* Avalon2 protocol package type */ + +/* Avalon2/3 firmware prefix */ +#define AVA2_FW2_PREFIXSTR "20" +#define AVA2_FW3_PREFIXSTR "33" + +#define AVA2_MM_VERNULL "NONE" + +#define AVA2_ID_AVA2 3255 +#define AVA2_ID_AVA3 3233 +#define AVA2_ID_AVAX 3200 + +enum avalon2_fan_fixed { + FAN_FIXED, + FAN_AUTO, +}; + +struct avalon2_pkg { + uint8_t head[2]; + uint8_t type; + uint8_t idx; + uint8_t cnt; + uint8_t data[32]; + uint8_t crc[2]; +}; +#define avalon2_ret avalon2_pkg + +struct avalon2_info { + struct timeval last_stratum; + struct pool pool; + int pool_no; + + int modulars[AVA2_DEFAULT_MODULARS]; + char mm_version[AVA2_DEFAULT_MODULARS][16]; + int dev_type[AVA2_DEFAULT_MODULARS]; + bool enable[AVA2_DEFAULT_MODULARS]; + + int set_frequency; + int set_voltage; + + int get_voltage[AVA2_DEFAULT_MODULARS]; + int get_frequency[AVA2_DEFAULT_MODULARS]; + int power_good[AVA2_DEFAULT_MODULARS]; + + int fan_pwm; + int fan_pct; + int temp_max; + + int fan[2 * AVA2_DEFAULT_MODULARS]; + int temp[2 * AVA2_DEFAULT_MODULARS]; + + int local_works[AVA2_DEFAULT_MODULARS]; + int hw_works[AVA2_DEFAULT_MODULARS]; + + int local_work[AVA2_DEFAULT_MODULARS]; + int hw_work[AVA2_DEFAULT_MODULARS]; + int matching_work[AVA2_DEFAULT_MINERS * AVA2_DEFAULT_MODULARS]; + + int led_red[AVA2_DEFAULT_MODULARS]; + + bool failing; +}; + +#define AVA2_WRITE_SIZE (sizeof(struct avalon2_pkg)) +#define AVA2_READ_SIZE AVA2_WRITE_SIZE + +#define AVA2_GETS_OK 0 +#define AVA2_GETS_TIMEOUT -1 +#define AVA2_GETS_RESTART -2 +#define AVA2_GETS_ERROR -3 + +#define AVA2_SEND_OK 0 +#define AVA2_SEND_ERROR -1 + +#define avalon2_open(devpath, baud, purge) serial_open(devpath, baud, AVA2_RESET_FAULT_DECISECONDS, purge) +#define avalon2_close(fd) close(fd) + +extern char *set_avalon2_fan(char *arg); +extern char *set_avalon2_freq(char *arg); +extern char *set_avalon2_voltage(char *arg); +extern char *set_avalon2_fixed_speed(enum avalon2_fan_fixed *f); +extern enum avalon2_fan_fixed opt_avalon2_fan_fixed; +extern int opt_avalon2_overheat; +extern int opt_avalon2_polling_delay; +#endif /* USE_AVALON2 */ +#endif /* _AVALON2_H_ */ diff --git a/driver-avalon4.c b/driver-avalon4.c new file mode 100644 index 0000000000..34329ae5b2 --- /dev/null +++ b/driver-avalon4.c @@ -0,0 +1,1637 @@ +/* + * Copyright 2014 Mikeqin + * Copyright 2013-2014 Con Kolivas + * Copyright 2012-2014 Xiangfu + * Copyright 2012 Luke Dashjr + * Copyright 2012 Andrew Smith + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the Free + * Software Foundation; either version 3 of the License, or (at your option) + * any later version. See COPYING for more details. + */ + +#include "config.h" + +#include "miner.h" +#include "driver-avalon4.h" +#include "crc.h" +#include "sha2.h" +#include "hexdump.c" + +#define get_fan_pwm(v) (AVA4_PWM_MAX - (v) * AVA4_PWM_MAX / 100) + +int opt_avalon4_temp_target = AVA4_DEFAULT_TEMP_TARGET; +int opt_avalon4_overheat = AVA4_DEFAULT_TEMP_OVERHEAT; + +int opt_avalon4_fan_min = AVA4_DEFAULT_FAN_MIN; +int opt_avalon4_fan_max = AVA4_DEFAULT_FAN_MAX; + +bool opt_avalon4_autov; +int opt_avalon4_voltage_min = AVA4_DEFAULT_VOLTAGE; +int opt_avalon4_voltage_max = AVA4_DEFAULT_VOLTAGE; +int opt_avalon4_freq[3] = {AVA4_DEFAULT_FREQUENCY, + AVA4_DEFAULT_FREQUENCY, + AVA4_DEFAULT_FREQUENCY}; + +int opt_avalon4_polling_delay = AVA4_DEFAULT_POLLING_DELAY; + +int opt_avalon4_aucspeed = AVA4_AUC_SPEED; +int opt_avalon4_aucxdelay = AVA4_AUC_XDELAY; + +int opt_avalon4_ntime_offset = AVA4_DEFAULT_ASIC_COUNT; + +#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) +{ + sha256_ctx ctx; + int i; + sha256_init(&ctx); + sha256_update(&ctx, message, len); + + for (i = 0; i < 8; i++) { + UNPACK32(ctx.h[i], &digest[i << 2]); + } +} + +static inline uint8_t rev8(uint8_t d) +{ + int i; + uint8_t out = 0; + + /* (from left to right) */ + for (i = 0; i < 8; i++) + if (d & (1 << i)) + out |= (1 << (7 - i)); + + return out; +} + +char *set_avalon4_fan(char *arg) +{ + int val1, val2, ret; + + ret = sscanf(arg, "%d-%d", &val1, &val2); + if (ret < 1) + return "No values passed to avalon4-fan"; + if (ret == 1) + val2 = val1; + + if (val1 < 0 || val1 > 100 || val2 < 0 || val2 > 100 || val2 < val1) + return "Invalid value passed to avalon4-fan"; + + opt_avalon4_fan_min = val1; + opt_avalon4_fan_max = val2; + + return NULL; +} + +char *set_avalon4_freq(char *arg) +{ + char *colon1, *colon2; + int val1 = 0, val2 = 0, val3 = 0; + + if (!(*arg)) + return NULL; + + colon1 = strchr(arg, ':'); + if (colon1) + *(colon1++) = '\0'; + + if (*arg) { + val1 = atoi(arg); + if (val1 < AVA4_DEFAULT_FREQUENCY_MIN || val1 > AVA4_DEFAULT_FREQUENCY_MAX) + return "Invalid value1 passed to avalon4-freq"; + } + + if (colon1 && *colon1) { + colon2 = strchr(colon1, ':'); + if (colon2) + *(colon2++) = '\0'; + + if (*colon1) { + val2 = atoi(colon1); + if (val2 < AVA4_DEFAULT_FREQUENCY_MIN || val2 > AVA4_DEFAULT_FREQUENCY_MAX) + return "Invalid value2 passed to avalon4-freq"; + } + + if (colon2 && *colon2) { + val3 = atoi(colon2); + if (val3 < AVA4_DEFAULT_FREQUENCY_MIN || val3 > AVA4_DEFAULT_FREQUENCY_MAX) + return "Invalid value3 passed to avalon4-freq"; + } + } + + if (!val1) + val3 = val2 = val1 = AVA4_DEFAULT_FREQUENCY; + + if (!val2) + val3 = val2 = val1; + + if (!val3) + val3 = val2; + + opt_avalon4_freq[0] = val1; + opt_avalon4_freq[1] = val2; + opt_avalon4_freq[2] = val3; + + return NULL; +} + +char *set_avalon4_voltage(char *arg) +{ + int val1, val2, ret; + + ret = sscanf(arg, "%d-%d", &val1, &val2); + if (ret < 1) + return "No values passed to avalon4-voltage"; + if (ret == 1) + val2 = val1; + + if (val1 < AVA4_DEFAULT_VOLTAGE_MIN || val1 > AVA4_DEFAULT_VOLTAGE_MAX || + val2 < AVA4_DEFAULT_VOLTAGE_MIN || val2 > AVA4_DEFAULT_VOLTAGE_MAX || + val2 < val1) + return "Invalid value passed to avalon4-voltage"; + + opt_avalon4_voltage_min = val1; + opt_avalon4_voltage_max = val2; + + return NULL; +} + +static int avalon4_init_pkg(struct avalon4_pkg *pkg, uint8_t type, uint8_t idx, uint8_t cnt) +{ + unsigned short crc; + + pkg->head[0] = AVA4_H1; + pkg->head[1] = AVA4_H2; + + pkg->type = type; + pkg->opt = 0; + pkg->idx = idx; + pkg->cnt = cnt; + + crc = crc16(pkg->data, AVA4_P_DATA_LEN); + + pkg->crc[0] = (crc & 0xff00) >> 8; + pkg->crc[1] = crc & 0x00ff; + 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, "Avalon4: job_id doesn't match! [%04x:%04x (%s)]", + crc, crc_expect, pool_job_id); + + return 1; +} + +static inline int get_current_temp_max(struct avalon4_info *info) +{ + int i; + int t = info->temp[0]; + + for (i = 1; i < AVA4_DEFAULT_MODULARS; i++) { + if (info->temp[i] > t) + t = info->temp[i]; + } + return t; +} + +/* http://www.onsemi.com/pub_link/Collateral/ADP3208D.PDF */ +static uint32_t encode_voltage_adp3208d(uint32_t v) +{ + return rev8((0x78 - v / 125) << 1 | 1) << 8; +} + +static uint32_t decode_voltage_adp3208d(uint32_t v) +{ + return (0x78 - (rev8(v >> 8) >> 1)) * 125; +} + +/* http://www.onsemi.com/pub/Collateral/NCP5392P-D.PDF */ +static uint32_t encode_voltage_ncp5392p(uint32_t v) +{ + if (v == 0) + return 0xff00; + + return rev8(((0x59 - (v - 5000) / 125) & 0xff) << 1 | 1) << 8; +} + +static uint32_t decode_voltage_ncp5392p(uint32_t v) +{ + if (v == 0xff00) + return 0; + + return (0x59 - (rev8(v >> 8) >> 1)) * 125 + 5000; +} + +static inline uint32_t adjust_fan(struct avalon4_info *info, int id) +{ + uint32_t pwm; + int t = info->temp[id]; + + if (t < opt_avalon4_temp_target - 10) + info->fan_pct[id] = opt_avalon4_fan_min; + else if (t > opt_avalon4_temp_target + 10 || t > opt_avalon4_overheat - 3) + info->fan_pct[id] = opt_avalon4_fan_max; + else if (t > opt_avalon4_temp_target + 1) + info->fan_pct[id] += 2; + else if (t < opt_avalon4_temp_target - 1) + info->fan_pct[id] -= 2; + + if (info->fan_pct[id] < opt_avalon4_fan_min) + info->fan_pct[id] = opt_avalon4_fan_min; + if (info->fan_pct[id] > opt_avalon4_fan_max) + info->fan_pct[id] = opt_avalon4_fan_max; + + pwm = get_fan_pwm(info->fan_pct[id]); + applog(LOG_DEBUG, "[%d], Adjust_fan: %dC-%d%%(%03x)", id, t, info->fan_pct[id], pwm); + + return pwm; +} + +static int decode_pkg(struct thr_info *thr, struct avalon4_ret *ar, int modular_id) +{ + struct cgpu_info *avalon4 = thr->cgpu; + struct avalon4_info *info = avalon4->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; + + unsigned int expected_crc; + unsigned int actual_crc; + uint32_t nonce, nonce2, ntime, miner, chip_id, volt, tmp; + uint8_t job_id[4]; + int pool_no; + + if (ar->head[0] != AVA4_H1 && ar->head[1] != AVA4_H2) { + applog(LOG_DEBUG, "Avalon4: H1 %02x, H2 %02x", ar->head[0], ar->head[1]); + hexdump(ar->data, 32); + return 1; + } + + expected_crc = crc16(ar->data, AVA4_P_DATA_LEN); + actual_crc = (ar->crc[0] & 0xff) | ((ar->crc[1] & 0xff) << 8); + if (expected_crc != actual_crc) { + applog(LOG_DEBUG, "Avalon4: %02x: expected crc(%04x), actual_crc(%04x)", + ar->type, expected_crc, actual_crc); + return 1; + } + + switch(ar->type) { + case AVA4_P_NONCE: + applog(LOG_DEBUG, "Avalon4: AVA4_P_NONCE"); + memcpy(&miner, ar->data + 0, 4); + memcpy(&pool_no, ar->data + 4, 4); + memcpy(&nonce2, ar->data + 8, 4); + memcpy(&ntime, ar->data + 12, 4); + memcpy(&nonce, ar->data + 16, 4); + memcpy(job_id, ar->data + 20, 4); + + miner = be32toh(miner); + chip_id = (miner >> 16) & 0xffff; + miner &= 0xffff; + pool_no = be32toh(pool_no); + ntime = be32toh(ntime); + if (miner >= AVA4_DEFAULT_MINERS || + pool_no >= total_pools || pool_no < 0) { + applog(LOG_DEBUG, "Avalon4: Wrong miner/pool_no %d/%d", miner, pool_no); + break; + } else { + info->matching_work[modular_id][miner]++; + info->chipmatching_work[modular_id][miner][chip_id]++; + } + nonce2 = be32toh(nonce2); + nonce = be32toh(nonce); + nonce -= 0x4000; + + applog(LOG_DEBUG, "%s-%d-%d: Found! P:%d - N2:%08x N:%08x NR:%d [M:%d - MW: %d(%d,%d,%d,%d)]", + avalon4->drv->name, avalon4->device_id, modular_id, + pool_no, nonce2, nonce, ntime, + miner, info->matching_work[modular_id][miner], + info->chipmatching_work[modular_id][miner][0], + info->chipmatching_work[modular_id][miner][1], + info->chipmatching_work[modular_id][miner][2], + info->chipmatching_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, "Avalon4: Match to previous stratum0! (%s)", pool_stratum0->swork.job_id); + pool = pool_stratum0; + } else if (!job_idcmp(job_id, pool_stratum1->swork.job_id)) { + applog(LOG_DEBUG, "Avalon4: Match to previous stratum1! (%s)", pool_stratum1->swork.job_id); + pool = pool_stratum1; + } else if (!job_idcmp(job_id, pool_stratum2->swork.job_id)) { + applog(LOG_DEBUG, "Avalon4: Match to previous stratum2! (%s)", pool_stratum2->swork.job_id); + pool = pool_stratum2; + } else { + applog(LOG_ERR, "Avalon4: Cannot match to any stratum! (%s)", pool->swork.job_id); + inc_hw_errors(thr); + break; + } + } + + submit_nonce2_nonce(thr, pool, real_pool, nonce2, nonce, ntime); + break; + case AVA4_P_STATUS: + applog(LOG_DEBUG, "Avalon4: AVA4_P_STATUS"); + hexdump(ar->data, 32); + memcpy(&tmp, ar->data, 4); + tmp = be32toh(tmp); + info->temp[modular_id] = tmp; + + memcpy(&tmp, ar->data + 4, 4); + tmp = be32toh(tmp); + info->fan[modular_id] = tmp; + + memcpy(&(info->get_frequency[modular_id]), ar->data + 8, 4); + memcpy(&(info->get_voltage[modular_id]), ar->data + 12, 4); + memcpy(&(info->local_work[modular_id]), ar->data + 16, 4); + memcpy(&(info->hw_work[modular_id]), ar->data + 20, 4); + memcpy(&(info->power_good[modular_id]), ar->data + 24, 4); + + info->get_frequency[modular_id] = be32toh(info->get_frequency[modular_id]) * 3968 / 65; + info->get_voltage[modular_id] = be32toh(info->get_voltage[modular_id]); + info->local_work[modular_id] = be32toh(info->local_work[modular_id]); + info->hw_work[modular_id] = be32toh(info->hw_work[modular_id]); + info->power_good[modular_id] = be32toh(info->power_good[modular_id]); + + volt = info->get_voltage[modular_id]; + if (info->mod_type[modular_id] == AVA4_TYPE_MM40) + tmp = decode_voltage_adp3208d(volt); + if (info->mod_type[modular_id] == AVA4_TYPE_MM41) + tmp = decode_voltage_ncp5392p(volt); + info->get_voltage[modular_id] = tmp; + + info->local_works[modular_id] += info->local_work[modular_id]; + info->hw_works[modular_id] += info->hw_work[modular_id]; + + info->lw5[modular_id][info->i_1m] += info->local_work[modular_id]; + info->hw5[modular_id][info->i_1m] += info->hw_work[modular_id]; + + avalon4->temp = get_current_temp_max(info); + break; + case AVA4_P_ACKDETECT: + applog(LOG_DEBUG, "Avalon4: AVA4_P_ACKDETECT"); + break; + default: + applog(LOG_DEBUG, "Avalon4: Unknown response"); + 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 avalon4_iic_init_pkg(uint8_t *iic_pkg, struct avalon4_iic_info *iic_info, uint8_t *buf, int wlen, int rlen) +{ + memset(iic_pkg, 0, AVA4_AUC_P_SIZE); + + switch (iic_info->iic_op) { + case AVA4_IIC_INIT: + iic_pkg[0] = 12; /* 4 bytes IIC header + 4 bytes speed + 4 bytes xfer delay */ + iic_pkg[3] = AVA4_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 AVA4_IIC_XFER: + iic_pkg[0] = 8 + wlen; + iic_pkg[3] = AVA4_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 AVA4_IIC_RESET: + case AVA4_IIC_DEINIT: + case AVA4_IIC_INFO: + iic_pkg[0] = 4; + iic_pkg[3] = iic_info->iic_op; + break; + + default: + break; + } + + return 0; +} + +static int avalon4_iic_xfer(struct cgpu_info *avalon4, + uint8_t *wbuf, int wlen, int *write, + uint8_t *rbuf, int rlen, int *read) +{ + int err = -1; + + if (unlikely(avalon4->usbinfo.nodev)) + goto out; + + err = usb_write(avalon4, (char *)wbuf, wlen, write, C_AVA4_WRITE); + if (err || *write != wlen) { + applog(LOG_DEBUG, "Avalon4: AUC xfer %d, w(%d-%d)!", err, wlen, *write); + usb_nodev(avalon4); + goto out; + } + + cgsleep_ms(opt_avalon4_aucxdelay / 4800 + 1); + + rlen += 4; /* Add 4 bytes IIC header */ + err = usb_read(avalon4, (char *)rbuf, rlen, read, C_AVA4_READ); + if (err || *read != rlen) { + applog(LOG_DEBUG, "Avalon4: AUC xfer %d, r(%d-%d)!", err, rlen - 4, *read); + hexdump(rbuf, rlen); + } + + *read = rbuf[0] - 4; /* Remove 4 bytes IIC header */ +out: + return err; +} + +static int avalon4_auc_init(struct cgpu_info *avalon4, char *ver) +{ + struct avalon4_iic_info iic_info; + int err, wlen, rlen; + uint8_t wbuf[AVA4_AUC_P_SIZE]; + uint8_t rbuf[AVA4_AUC_P_SIZE]; + + if (unlikely(avalon4->usbinfo.nodev)) + return 1; + + /* Try to clean the AUC buffer */ + err = usb_read(avalon4, (char *)rbuf, AVA4_AUC_P_SIZE, &rlen, C_AVA4_READ); + applog(LOG_DEBUG, "Avalon4: AUC usb_read %d, %d!", err, rlen); + hexdump(rbuf, AVA4_AUC_P_SIZE); + + /* Reset */ + iic_info.iic_op = AVA4_IIC_RESET; + rlen = 0; + avalon4_iic_init_pkg(wbuf, &iic_info, NULL, 0, rlen); + + memset(rbuf, 0, AVA4_AUC_P_SIZE); + err = avalon4_iic_xfer(avalon4, wbuf, AVA4_AUC_P_SIZE, &wlen, rbuf, rlen, &rlen); + if (err) { + applog(LOG_ERR, "Avalon4: Failed to reset Avalon USB2IIC Converter"); + return 1; + } + + /* Deinit */ + iic_info.iic_op = AVA4_IIC_DEINIT; + rlen = 0; + avalon4_iic_init_pkg(wbuf, &iic_info, NULL, 0, rlen); + + memset(rbuf, 0, AVA4_AUC_P_SIZE); + err = avalon4_iic_xfer(avalon4, wbuf, AVA4_AUC_P_SIZE, &wlen, rbuf, rlen, &rlen); + if (err) { + applog(LOG_ERR, "Avalon4: Failed to deinit Avalon USB2IIC Converter"); + return 1; + } + + /* Init */ + iic_info.iic_op = AVA4_IIC_INIT; + iic_info.iic_param.aucParam[0] = opt_avalon4_aucspeed; + iic_info.iic_param.aucParam[1] = opt_avalon4_aucxdelay; + rlen = AVA4_AUC_VER_LEN; + avalon4_iic_init_pkg(wbuf, &iic_info, NULL, 0, rlen); + + memset(rbuf, 0, AVA4_AUC_P_SIZE); + err = avalon4_iic_xfer(avalon4, wbuf, AVA4_AUC_P_SIZE, &wlen, rbuf, rlen, &rlen); + if (err) { + applog(LOG_ERR, "Avalon4: Failed to init Avalon USB2IIC Converter"); + return 1; + } + + hexdump(rbuf, AVA4_AUC_P_SIZE); + + memcpy(ver, rbuf + 4, AVA4_AUC_VER_LEN); + ver[AVA4_AUC_VER_LEN] = '\0'; + + applog(LOG_DEBUG, "Avalon4: USB2IIC Converter version: %s!", ver); + return 0; +} + +static int avalon4_auc_getinfo(struct cgpu_info *avalon4) +{ + struct avalon4_iic_info iic_info; + int err, wlen, rlen; + uint8_t wbuf[AVA4_AUC_P_SIZE]; + uint8_t rbuf[AVA4_AUC_P_SIZE]; + uint8_t *pdata = rbuf + 4; + uint16_t adc_val; + struct avalon4_info *info = avalon4->device_data; + + iic_info.iic_op = AVA4_IIC_INFO; + /* Device info: (9 bytes) + * tempadc(2), reqRdIndex, reqWrIndex, + * respRdIndex, respWrIndex, tx_flags, state + * */ + rlen = 7; + avalon4_iic_init_pkg(wbuf, &iic_info, NULL, 0, rlen); + + memset(rbuf, 0, AVA4_AUC_P_SIZE); + err = avalon4_iic_xfer(avalon4, wbuf, AVA4_AUC_P_SIZE, &wlen, rbuf, rlen, &rlen); + if (err) { + applog(LOG_ERR, "Avalon4: AUC Failed to get info "); + return 1; + } + + applog(LOG_DEBUG, "Avalon4: AUC tempADC(%03d), reqcnt(%d), respcnt(%d), txflag(%d), state(%d)", + 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_temp = 3.3 * adc_val * 10000 / 1023; + return 0; +} + +static int avalon4_iic_xfer_pkg(struct cgpu_info *avalon4, uint8_t slave_addr, + const struct avalon4_pkg *pkg, struct avalon4_ret *ret) +{ + struct avalon4_iic_info iic_info; + int err, wcnt, rcnt, rlen = 0; + uint8_t wbuf[AVA4_AUC_P_SIZE]; + uint8_t rbuf[AVA4_AUC_P_SIZE]; + + struct avalon4_info *info = avalon4->device_data; + + iic_info.iic_op = AVA4_IIC_XFER; + iic_info.iic_param.slave_addr = slave_addr; + if (ret) + rlen = AVA4_READ_SIZE; + + avalon4_iic_init_pkg(wbuf, &iic_info, (uint8_t *)pkg, AVA4_WRITE_SIZE, rlen); + err = avalon4_iic_xfer(avalon4, wbuf, wbuf[0], &wcnt, rbuf, rlen, &rcnt); + if ((pkg->type != AVA4_P_DETECT) && err == -7 && !rcnt && rlen) { + avalon4_iic_init_pkg(wbuf, &iic_info, NULL, 0, rlen); + err = avalon4_iic_xfer(avalon4, wbuf, wbuf[0], &wcnt, rbuf, rlen, &rcnt); + applog(LOG_DEBUG, "Avalon4: IIC read again!(err:%d)", err); + } + if (err || rcnt != rlen) { + if (info->xfer_err_cnt++ == 100) { + applog(LOG_DEBUG, "Avalon4: AUC xfer_err_cnt reach err = %d, rcnt = %d, rlen = %d", err, rcnt, rlen); + + cgsleep_ms(5 * 1000); /* Wait MM reset */ + avalon4_auc_init(avalon4, info->auc_version); + } + return AVA4_SEND_ERROR; + } + + if (ret) + memcpy((char *)ret, rbuf + 4, AVA4_READ_SIZE); + + info->xfer_err_cnt = 0; + return AVA4_SEND_OK; +} + +static int avalon4_send_bc_pkgs(struct cgpu_info *avalon4, const struct avalon4_pkg *pkg) +{ + int ret; + + do { + if (unlikely(avalon4->usbinfo.nodev)) + return -1; + ret = avalon4_iic_xfer_pkg(avalon4, AVA4_MODULE_BROADCAST, pkg, NULL); + } while (ret != AVA4_SEND_OK); + + return 0; +} + +static void avalon4_stratum_pkgs(struct cgpu_info *avalon4, struct pool *pool) +{ + const int merkle_offset = 36; + struct avalon4_pkg pkg; + int i, a, b, 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]; + + /* Send out the first stratum message STATIC */ + applog(LOG_DEBUG, "Avalon4: Pool stratum message STATIC: %d, %d, %d, %d, %d", + pool->coinbase_len, + pool->nonce2_offset, + pool->n2size, + merkle_offset, + pool->merkles); + memset(pkg.data, 0, AVA4_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); + + tmp = be32toh((int)pool->swork.diff); + memcpy(pkg.data + 20, &tmp, 4); + + tmp = be32toh((int)pool->pool_no); + memcpy(pkg.data + 24, &tmp, 4); + + avalon4_init_pkg(&pkg, AVA4_P_STATIC, 1, 1); + if (avalon4_send_bc_pkgs(avalon4, &pkg)) + return; + + set_target(target, pool->sdiff); + memcpy(pkg.data, target, 32); + if (opt_debug) { + char *target_str; + target_str = bin2hex(target, 32); + applog(LOG_DEBUG, "Avalon4: Pool stratum target: %s", target_str); + free(target_str); + } + avalon4_init_pkg(&pkg, AVA4_P_TARGET, 1, 1); + if (avalon4_send_bc_pkgs(avalon4, &pkg)) + return; + + memset(pkg.data, 0, AVA4_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, "Avalon4: Pool stratum message JOBS_ID[%04x]: %s", + crc, pool->swork.job_id); + + pkg.data[0] = (crc & 0xff00) >> 8; + pkg.data[1] = crc & 0x00ff; + avalon4_init_pkg(&pkg, AVA4_P_JOB_ID, 1, 1); + if (avalon4_send_bc_pkgs(avalon4, &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 / AVA4_P_DATA_LEN) + 1; + b = coinbase_len_posthash % AVA4_P_DATA_LEN; + memcpy(pkg.data, coinbase_prehash, 32); + avalon4_init_pkg(&pkg, AVA4_P_COINBASE, 1, a + (b ? 1 : 0)); + if (avalon4_send_bc_pkgs(avalon4, &pkg)) + return; + applog(LOG_DEBUG, "Avalon4: Pool stratum message modified COINBASE: %d %d", a, b); + for (i = 1; i < a; i++) { + memcpy(pkg.data, pool->coinbase + coinbase_len_prehash + i * 32 - 32, 32); + avalon4_init_pkg(&pkg, AVA4_P_COINBASE, i + 1, a + (b ? 1 : 0)); + if (avalon4_send_bc_pkgs(avalon4, &pkg)) + return; + } + if (b) { + memset(pkg.data, 0, AVA4_P_DATA_LEN); + memcpy(pkg.data, pool->coinbase + coinbase_len_prehash + i * 32 - 32, b); + avalon4_init_pkg(&pkg, AVA4_P_COINBASE, i + 1, i + 1); + if (avalon4_send_bc_pkgs(avalon4, &pkg)) + return; + } + + b = pool->merkles; + applog(LOG_DEBUG, "Avalon4: Pool stratum message MERKLES: %d", b); + for (i = 0; i < b; i++) { + memset(pkg.data, 0, AVA4_P_DATA_LEN); + memcpy(pkg.data, pool->swork.merkle_bin[i], 32); + avalon4_init_pkg(&pkg, AVA4_P_MERKLES, i + 1, b); + if (avalon4_send_bc_pkgs(avalon4, &pkg)) + return; + } + + applog(LOG_DEBUG, "Avalon4: Pool stratum message HEADER: 4"); + for (i = 0; i < 4; i++) { + memset(pkg.data, 0, AVA4_P_DATA_LEN); + memcpy(pkg.data, pool->header_bin + i * 32, 32); + avalon4_init_pkg(&pkg, AVA4_P_HEADER, i + 1, 4); + if (avalon4_send_bc_pkgs(avalon4, &pkg)) + return; + } + + avalon4_auc_getinfo(avalon4); +} + +static struct cgpu_info *avalon4_auc_detect(struct libusb_device *dev, struct usb_find_devices *found) +{ + int i; + struct avalon4_info *info; + struct cgpu_info *avalon4 = usb_alloc_cgpu(&avalon4_drv, 1); + char auc_ver[AVA4_AUC_VER_LEN]; + + if (!usb_init(avalon4, dev, found)) { + applog(LOG_ERR, "Avalon4 failed usb_init"); + avalon4 = usb_free_cgpu(avalon4); + return NULL; + } + + /* Avalon4 prefers not to use zero length packets */ + avalon4->nozlp = true; + + /* We try twice on AUC init */ + if (avalon4_auc_init(avalon4, auc_ver) && avalon4_auc_init(avalon4, auc_ver)) + return NULL; + + /* We have an Avalon4 AUC connected */ + avalon4->threads = 1; + add_cgpu(avalon4); + + update_usb_stats(avalon4); + applog(LOG_INFO, "%s-%d: Found at %s", avalon4->drv->name, avalon4->device_id, + avalon4->device_path); + + avalon4->device_data = calloc(sizeof(struct avalon4_info), 1); + if (unlikely(!(avalon4->device_data))) + quit(1, "Failed to calloc avalon4_info"); + + info = avalon4->device_data; + memcpy(info->auc_version, auc_ver, AVA4_AUC_VER_LEN); + info->auc_version[AVA4_AUC_VER_LEN] = '\0'; + info->auc_speed = opt_avalon4_aucspeed; + info->auc_xdelay = opt_avalon4_aucxdelay; + + info->polling_first = 1; + + info->set_voltage_broadcat = 1; + + for (i = 0; i < AVA4_DEFAULT_MODULARS; i++) { + info->enable[i] = 0; + info->mod_type[i] = AVA4_TYPE_NULL; + info->fan_pct[i] = AVA4_DEFAULT_FAN_START; + info->set_voltage[i] = opt_avalon4_voltage_min; + } + + info->enable[0] = 1; + info->mod_type[0] = AVA4_TYPE_MM40; + + info->set_frequency[0] = opt_avalon4_freq[0]; + info->set_frequency[1] = opt_avalon4_freq[1]; + info->set_frequency[2] = opt_avalon4_freq[2]; + + return avalon4; +} + +static inline void avalon4_detect(bool __maybe_unused hotplug) +{ + usb_detect(&avalon4_drv, avalon4_auc_detect); +} + +static bool avalon4_prepare(struct thr_info *thr) +{ + int i; + struct cgpu_info *avalon4 = thr->cgpu; + struct avalon4_info *info = avalon4->device_data; + + info->polling_first = 1; + + cgtime(&(info->last_fan)); + cgtime(&(info->last_5m)); + cgtime(&(info->last_1m)); + + cglock_init(&info->update_lock); + cglock_init(&info->pool0.data_lock); + cglock_init(&info->pool1.data_lock); + cglock_init(&info->pool2.data_lock); + + info->set_voltage_broadcat = 1; + + for (i = 0; i < AVA4_DEFAULT_MODULARS; i++) + info->fan_pct[i] = AVA4_DEFAULT_FAN_START; + + + return true; +} + +static void detect_modules(struct cgpu_info *avalon4) +{ + struct avalon4_info *info = avalon4->device_data; + struct thr_info *thr = avalon4->thr[0]; + + struct avalon4_pkg detect_pkg; + struct avalon4_ret ret_pkg; + uint32_t tmp; + int i, err; + + /* Detect new modules here */ + for (i = 1; i < AVA4_DEFAULT_MODULARS; i++) { + if (info->enable[i]) + continue; + + /* Send out detect pkg */ + applog(LOG_DEBUG, "%s %d: AVA4_P_DETECT ID[%d]", + avalon4->drv->name, avalon4->device_id, i); + memset(detect_pkg.data, 0, AVA4_P_DATA_LEN); + tmp = be32toh(i); /* ID */ + memcpy(detect_pkg.data + 28, &tmp, 4); + avalon4_init_pkg(&detect_pkg, AVA4_P_DETECT, 1, 1); + err = avalon4_iic_xfer_pkg(avalon4, AVA4_MODULE_BROADCAST, &detect_pkg, &ret_pkg); + if (err == AVA4_SEND_OK) { + if (decode_pkg(thr, &ret_pkg, AVA4_MODULE_BROADCAST)) { + applog(LOG_DEBUG, "%s %d: Should be AVA4_P_ACKDETECT(%d), but %d", + avalon4->drv->name, avalon4->device_id, AVA4_P_ACKDETECT, ret_pkg.type); + continue; + } + } + + if (err != AVA4_SEND_OK) { + applog(LOG_DEBUG, "%s %d: AVA4_P_DETECT: Failed AUC xfer data with err %d", + avalon4->drv->name, avalon4->device_id, err); + break; + } + + applog(LOG_DEBUG, "%s %d: Module detect ID[%d]: %d", + avalon4->drv->name, avalon4->device_id, i, ret_pkg.type); + if (ret_pkg.type != AVA4_P_ACKDETECT) + break; + + cgtime(&info->elapsed[i]); + info->enable[i] = 1; + memcpy(info->mm_dna[i], ret_pkg.data, AVA4_MM_DNA_LEN); + info->mm_dna[i][AVA4_MM_DNA_LEN] = '\0'; + memcpy(info->mm_version[i], ret_pkg.data + AVA4_MM_DNA_LEN, AVA4_MM_VER_LEN); + info->mm_version[i][AVA4_MM_VER_LEN] = '\0'; + if (!strncmp((char *)&(info->mm_version[i]), AVA4_MM40_PREFIXSTR, 2)) + info->mod_type[i] = AVA4_TYPE_MM40; + if (!strncmp((char *)&(info->mm_version[i]), AVA4_MM41_PREFIXSTR, 2)) + info->mod_type[i] = AVA4_TYPE_MM41; + + info->fan_pct[i] = AVA4_DEFAULT_FAN_START; + info->set_voltage[i] = opt_avalon4_voltage_min; + info->led_red[i] = 0; + applog(LOG_NOTICE, "%s %d: New module detect! ID[%d]", + avalon4->drv->name, avalon4->device_id, i); + } +} + +static int polling(struct thr_info *thr, struct cgpu_info *avalon4, struct avalon4_info *info) +{ + struct avalon4_pkg send_pkg; + struct avalon4_ret ar; + int i, j, tmp, ret, decode_err = 0, do_polling = 0; + struct timeval current_fan; + int do_adjust_fan = 0; + uint32_t fan_pwm; + double device_tdiff; + + if (info->polling_first) { + cgsleep_ms(300); + info->polling_first = 0; + } + + cgtime(¤t_fan); + device_tdiff = tdiff(¤t_fan, &(info->last_fan)); + if (device_tdiff > 5.0 || device_tdiff < 0) { + cgtime(&info->last_fan); + do_adjust_fan = 1; + } + + for (i = 1; i < AVA4_DEFAULT_MODULARS; i++) { + if (!info->enable[i]) + continue; + + do_polling = 1; + cgsleep_ms(opt_avalon4_polling_delay); + + memset(send_pkg.data, 0, AVA4_P_DATA_LEN); + /* Red LED */ + tmp = be32toh(info->led_red[i]); + memcpy(send_pkg.data, &tmp, 4); + + /* Adjust fan every 10 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); + } + + avalon4_init_pkg(&send_pkg, AVA4_P_POLLING, 1, 1); + ret = avalon4_iic_xfer_pkg(avalon4, i, &send_pkg, &ar); + if (ret == AVA4_SEND_OK) + decode_err = decode_pkg(thr, &ar, i); + + if (ret != AVA4_SEND_OK || decode_err) { + info->polling_err_cnt[i]++; + if (info->polling_err_cnt[i] >= 4) { + info->polling_err_cnt[i] = 0; + info->mod_type[i] = AVA4_TYPE_NULL; + info->enable[i] = 0; + info->get_voltage[i] = 0; + info->get_frequency[i] = 0; + info->power_good[i] = 0; + info->local_work[i] = 0; + info->local_works[i] = 0; + info->hw_work[i] = 0; + info->hw_works[i] = 0; + for (j = 0; j < 6; j++) { + info->lw5[i][j] = 0; + info->hw5[i][j] = 0; + } + + for (j = 0; j < AVA4_DEFAULT_MINERS; j++) { + info->matching_work[i][j] = 0; + info->chipmatching_work[i][j][0] = 0; + info->chipmatching_work[i][j][1] = 0; + info->chipmatching_work[i][j][2] = 0; + info->chipmatching_work[i][j][3] = 0; + } + applog(LOG_NOTICE, "%s %d: Module detached! ID[%d]", + avalon4->drv->name, avalon4->device_id, i); + } + } + + if (ret == AVA4_SEND_OK && !decode_err) + info->polling_err_cnt[i] = 0; + } + + if (!do_polling) + detect_modules(avalon4); + + return 0; +} + +static void copy_pool_stratum(struct pool *pool_stratum, struct pool *pool) +{ + int i; + int merkles = pool->merkles; + size_t coinbase_len = pool->coinbase_len; + + if (!pool->swork.job_id) + return; + + if (!job_idcmp((unsigned char *)pool->swork.job_id, pool_stratum->swork.job_id)) + return; + + cg_wlock(&pool_stratum->data_lock); + free(pool_stratum->swork.job_id); + free(pool_stratum->nonce1); + free(pool_stratum->coinbase); + + align_len(&coinbase_len); + pool_stratum->coinbase = calloc(coinbase_len, 1); + if (unlikely(!pool_stratum->coinbase)) + quit(1, "Failed to calloc pool_stratum coinbase in avalon4"); + 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 = realloc(pool_stratum->swork.merkle_bin, + sizeof(char *) * merkles + 1); + for (i = 0; i < merkles; i++) { + pool_stratum->swork.merkle_bin[i] = malloc(32); + if (unlikely(!pool_stratum->swork.merkle_bin[i])) + quit(1, "Failed to malloc pool_stratum swork merkle_bin"); + 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 avalon4_stratum_set(struct cgpu_info *avalon4, struct pool *pool, int addr, int cutoff) +{ + struct avalon4_info *info = avalon4->device_data; + struct avalon4_pkg send_pkg; + uint32_t tmp = 0, range, start, volt; + + info->set_frequency[0] = opt_avalon4_freq[0]; + info->set_frequency[1] = opt_avalon4_freq[1]; + info->set_frequency[2] = opt_avalon4_freq[2]; + + /* Set the NTime, Voltage and Frequency */ + memset(send_pkg.data, 0, AVA4_P_DATA_LEN); + + if (opt_avalon4_ntime_offset != AVA4_DEFAULT_ASIC_COUNT) { + tmp = opt_avalon4_ntime_offset | 0x80000000; + tmp = be32toh(tmp); + memcpy(send_pkg.data, &tmp, 4); + } + + volt = info->set_voltage[addr]; + if (cutoff) + volt = 0; + if (info->mod_type[addr] == AVA4_TYPE_MM40) + tmp = encode_voltage_adp3208d(volt); + if (info->mod_type[addr] == AVA4_TYPE_MM41) + tmp = encode_voltage_ncp5392p(volt); + tmp = be32toh(tmp); + memcpy(send_pkg.data + 4, &tmp, 4); + + tmp = info->set_frequency[0] | (info->set_frequency[1] << 10) | (info->set_frequency[2] << 20); + tmp = be32toh(tmp); + memcpy(send_pkg.data + 8, &tmp, 4); + + /* Configure the nonce2 offset and range */ + if (pool->n2size == 3) + range = 0xffffff / (total_devices + 1); + else + range = 0xffffffff / (total_devices + 1); + start = range * (avalon4->device_id + 1); + + tmp = be32toh(start); + memcpy(send_pkg.data + 12, &tmp, 4); + + tmp = be32toh(range); + memcpy(send_pkg.data + 16, &tmp, 4); + + /* Package the data */ + avalon4_init_pkg(&send_pkg, AVA4_P_SET, 1, 1); + if (addr == AVA4_MODULE_BROADCAST) + avalon4_send_bc_pkgs(avalon4, &send_pkg); + else + avalon4_iic_xfer_pkg(avalon4, addr, &send_pkg, NULL); +} + +static void avalon4_stratum_finish(struct cgpu_info *avalon4) +{ + struct avalon4_pkg send_pkg; + + memset(send_pkg.data, 0, AVA4_P_DATA_LEN); + avalon4_init_pkg(&send_pkg, AVA4_P_FINISH, 1, 1); + avalon4_send_bc_pkgs(avalon4, &send_pkg); +} + +static void avalon4_update(struct cgpu_info *avalon4) +{ + struct avalon4_info *info = avalon4->device_data; + struct thr_info *thr = avalon4->thr[0]; + struct work *work; + struct pool *pool; + int coinbase_len_posthash, coinbase_len_prehash; + int i, count = 0; + + applog(LOG_DEBUG, "Avalon4: New stratum: restart: %d, update: %d", + thr->work_restart, thr->work_update); + thr->work_update = false; + thr->work_restart = false; + + /* Step 1: Make sure pool is ready */ + work = get_work(thr, thr->id); + discard_work(work); /* Don't leak memory */ + + /* Step 2: MM protocol check */ + pool = current_pool(); + if (!pool->has_stratum) + quit(1, "Avalon4: MM has to use stratum pools"); + + 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 > AVA4_P_COINBASE_SIZE) { + applog(LOG_ERR, "Avalon4: MM pool modified coinbase length(%d) is more than %d", + coinbase_len_posthash + SHA256_BLOCK_SIZE, AVA4_P_COINBASE_SIZE); + return; + } + if (pool->merkles > AVA4_P_MERKLES_COUNT) { + applog(LOG_ERR, "Avalon4: MM merkles has to be less then %d", AVA4_P_MERKLES_COUNT); + return; + } + if (pool->n2size < 3) { + applog(LOG_ERR, "Avalon4: MM nonce2 size has to be >= 3 (%d)", pool->n2size); + return; + } + + /* Step 3: Send out stratum pkgs */ + cg_wlock(&info->update_lock); + cg_rlock(&pool->data_lock); + + cgtime(&info->last_stratum); + 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); + avalon4_stratum_pkgs(avalon4, pool); + + cg_runlock(&pool->data_lock); + cg_wunlock(&info->update_lock); + + /* Step 4: Try to detect new modules */ + detect_modules(avalon4); + + /* Step 5: Configure the parameter from outside */ + avalon4_stratum_set(avalon4, pool, AVA4_MODULE_BROADCAST, 0); + + if (!info->set_voltage_broadcat) { + for (i = 1; i < AVA4_DEFAULT_MODULARS; i++) { + if (!info->enable[i]) + continue; + if (info->set_voltage[i] == info->set_voltage[0]) + continue; + + avalon4_stratum_set(avalon4, pool, i, 0); + } + } else { + for (i = 1; i < AVA4_DEFAULT_MODULARS; i++) { + if (!info->enable[i]) + continue; + if (info->mod_type[i] == AVA4_TYPE_MM40) + continue; + + avalon4_stratum_set(avalon4, pool, i, 0); + } + } + + for (i = 1; i < AVA4_DEFAULT_MODULARS; i++) { + if (!info->enable[i]) + continue; + + count++; + if (info->temp[i] < opt_avalon4_overheat) + continue; + + avalon4_stratum_set(avalon4, pool, i, 1); + } + info->mm_count = count; + + /* Step 6: Send out finish pkg */ + avalon4_stratum_finish(avalon4); +} + +static int64_t avalon4_scanhash(struct thr_info *thr) +{ + struct cgpu_info *avalon4 = thr->cgpu; + struct avalon4_info *info = avalon4->device_data; + struct timeval current; + double device_tdiff, hwp; + uint32_t a = 0, b = 0; + uint64_t h; + int i, j; + + if (unlikely(avalon4->usbinfo.nodev)) { + applog(LOG_ERR, "%s-%d: Device disappeared, shutting down thread", + avalon4->drv->name, avalon4->device_id); + return -1; + } + + /* Stop polling the device if there is no stratum in 3 minutes, network is down */ + cgtime(¤t); + if (tdiff(¤t, &(info->last_stratum)) > 180.0) + return 0; + + cg_rlock(&info->update_lock); + polling(thr, avalon4, info); + cg_runlock(&info->update_lock); + + cgtime(¤t); + device_tdiff = tdiff(¤t, &(info->last_1m)); + if (device_tdiff >= 60.0 || device_tdiff < 0) { + copy_time(&info->last_1m, ¤t); + if (info->i_1m++ >= 6) + info->i_1m = 0; + + for (i = 1; i < AVA4_DEFAULT_MODULARS; i++) { + if (!info->enable[i]) + continue; + + info->lw5[i][info->i_1m] = 0; + info->hw5[i][info->i_1m] = 0; + } + } + + cgtime(¤t); + device_tdiff = tdiff(¤t, &(info->last_5m)); + if (opt_avalon4_autov && (device_tdiff > 480.0 || device_tdiff < 0)) { + copy_time(&info->last_5m, ¤t); + + for (i = 1; i < AVA4_DEFAULT_MODULARS; i++) { + if (!info->enable[i]) + continue; + + a = 0; + b = 0; + for (j = 0; j < 6; j++) { + a += info->lw5[i][j]; + b += info->hw5[i][j]; + } + + hwp = a ? (double)b / (double)a : 0; + if (hwp > AVA4_DH_INC && (info->set_voltage[i] < info->set_voltage[0] + 125)) { + info->set_voltage[i] += 125; + applog(LOG_NOTICE, "%s %d: Automatic increase module[%d] voltage to %d", + avalon4->drv->name, avalon4->device_id, i, info->set_voltage[i]); + } + if (hwp < AVA4_DH_DEC && (info->set_voltage[i] > info->set_voltage[0] - (4 * 125))) { + info->set_voltage[i] -= 125; + applog(LOG_NOTICE, "%s %d: Automatic decrease module[%d] voltage to %d", + avalon4->drv->name, avalon4->device_id, i, info->set_voltage[i]); + } + + } + } + + for (i = 1; i < AVA4_DEFAULT_MODULARS; i++) { + if (info->set_voltage[i] != info->set_voltage[0]) + break; + } + + if (i < AVA4_DEFAULT_MODULARS) + info->set_voltage_broadcat = 0; + else + info->set_voltage_broadcat = 1; + + h = 0; + for (i = 1; i < AVA4_DEFAULT_MODULARS; i++) { + h += info->enable[i] ? (info->local_work[i] - info->hw_work[i]) : 0; + info->local_work[i] = 0; + info->hw_work[i] = 0; + } + return h * 0xffffffffull; +} + +#define STATBUFLEN 512 +static struct api_data *avalon4_api_stats(struct cgpu_info *cgpu) +{ + struct api_data *root = NULL; + struct avalon4_info *info = cgpu->device_data; + int i, j; + uint32_t a,b ; + double hwp, diff; + char buf[256]; + char statbuf[AVA4_DEFAULT_MODULARS][STATBUFLEN]; + struct timeval current; + + memset(statbuf, 0, AVA4_DEFAULT_MODULARS * STATBUFLEN); + + for (i = 1; i < AVA4_DEFAULT_MODULARS; i++) { + if(info->mod_type[i] == AVA4_TYPE_NULL) + continue; + sprintf(buf, "Ver[%s]", info->mm_version[i]); + strcat(statbuf[i], buf); + } + + for (i = 1; i < AVA4_DEFAULT_MODULARS; i++) { + if(info->mod_type[i] == AVA4_TYPE_NULL) + continue; + 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[i], buf); + } + + for (i = 1; i < AVA4_DEFAULT_MODULARS; i++) { + struct timeval now; + if (info->mod_type[i] == AVA4_TYPE_NULL) + continue; + + cgtime(&now); + sprintf(buf, " Elapsed[%.0f]", tdiff(&now, &(info->elapsed[i]))); + strcat(statbuf[i], buf); + } + +#if 0 + for (i = 1; i < AVA4_DEFAULT_MODULARS; i++) { + if (info->mod_type[i] == AVA4_TYPE_NULL) + continue; + + strcat(statbuf[i], " MW["); + for (j = 0; j < AVA4_DEFAULT_MINERS; j++) { + sprintf(buf, "%d ", info->matching_work[i][j]); + strcat(statbuf[i], buf); + } + statbuf[i][strlen(statbuf[i]) - 1] = ']'; + } +#endif + + for (i = 1; i < AVA4_DEFAULT_MODULARS; i++) { + if(info->mod_type[i] == AVA4_TYPE_NULL) + continue; + sprintf(buf, " LW[%"PRIu64"]", info->local_works[i]); + strcat(statbuf[i], buf); + } + for (i = 1; i < AVA4_DEFAULT_MODULARS; i++) { + if(info->mod_type[i] == AVA4_TYPE_NULL) + continue; + sprintf(buf, " HW[%"PRIu64"]", info->hw_works[i]); + strcat(statbuf[i], buf); + } + for (i = 1; i < AVA4_DEFAULT_MODULARS; i++) { + if(info->mod_type[i] == AVA4_TYPE_NULL) + continue; + a = info->hw_works[i]; + b = info->local_works[i]; + hwp = b ? ((double)a / (double)b) * 100: 0; + + sprintf(buf, " DH[%.3f%%]", hwp); + strcat(statbuf[i], buf); + } + for (i = 1; i < AVA4_DEFAULT_MODULARS; i++) { + if(info->mod_type[i] == AVA4_TYPE_NULL) + continue; + + a = 0; + b = 0; + for (j = 0; j < 6; j++) { + a += info->lw5[i][j]; + b += info->hw5[i][j]; + } + + cgtime(¤t); + diff = tdiff(¤t, &(info->last_1m)) + 300.0; + + hwp = a ? (double)b / (double)a * 100 : 0; + + sprintf(buf, " GHS5m[%.2f] DH5m[%.3f%%]", ((double)a - (double)b) * 4.295 / diff, hwp); + strcat(statbuf[i], buf); + } + for (i = 1; i < AVA4_DEFAULT_MODULARS; i++) { + if(info->mod_type[i] == AVA4_TYPE_NULL) + continue; + sprintf(buf, " Temp[%d]", info->temp[i]); + strcat(statbuf[i], buf); + } + for (i = 1; i < AVA4_DEFAULT_MODULARS; i++) { + if(info->mod_type[i] == AVA4_TYPE_NULL) + continue; + sprintf(buf, " Fan[%d]", info->fan[i]); + strcat(statbuf[i], buf); + } + for (i = 1; i < AVA4_DEFAULT_MODULARS; i++) { + if(info->mod_type[i] == AVA4_TYPE_NULL) + continue; + sprintf(buf, " Vol[%.4f]", (float)info->get_voltage[i] / 10000); + strcat(statbuf[i], buf); + } + for (i = 1; i < AVA4_DEFAULT_MODULARS; i++) { + if(info->mod_type[i] == AVA4_TYPE_NULL) + continue; + sprintf(buf, " Freq[%.2f]", (float)info->get_frequency[i] / 1000); + strcat(statbuf[i], buf); + } + for (i = 1; i < AVA4_DEFAULT_MODULARS; i++) { + if(info->mod_type[i] == AVA4_TYPE_NULL) + continue; + sprintf(buf, " PG[%d]", info->power_good[i]); + strcat(statbuf[i], buf); + } + for (i = 1; i < AVA4_DEFAULT_MODULARS; i++) { + if(info->mod_type[i] == AVA4_TYPE_NULL) + continue; + sprintf(buf, " Led[%d]", info->led_red[i]); + strcat(statbuf[i], buf); + } + + for (i = 1; i < AVA4_DEFAULT_MODULARS; i++) { + if(info->mod_type[i] == AVA4_TYPE_NULL) + continue; + sprintf(buf, "MM ID%d", i); + root = api_add_string(root, buf, statbuf[i], true); + } + + root = api_add_int(root, "MM Count", &(info->mm_count), true); + root = api_add_bool(root, "Automatic Voltage", &opt_avalon4_autov, 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 ADC", &(info->auc_temp), true); + + return root; +} + +static char *avalon4_set_device(struct cgpu_info *avalon4, char *option, char *setting, char *replybuf) +{ + int val, i; + struct avalon4_info *info = avalon4->device_data; + + if (strcasecmp(option, "help") == 0) { + sprintf(replybuf, "led|fan|voltage|frequency|pdelay"); + return replybuf; + } + + if (strcasecmp(option, "pdelay") == 0) { + if (!setting || !*setting) { + sprintf(replybuf, "missing polling delay setting"); + return replybuf; + } + + val = atoi(setting); + if (val < 1 || val > 65535) { + sprintf(replybuf, "invalid polling delay: %d, valid range 1-65535", val); + return replybuf; + } + + opt_avalon4_polling_delay = val; + + applog(LOG_NOTICE, "%s %d: Update polling delay to: %d", + avalon4->drv->name, avalon4->device_id, val); + + return NULL; + } + + if (strcasecmp(option, "fan") == 0) { + if (!setting || !*setting) { + sprintf(replybuf, "missing fan value"); + return replybuf; + } + + if (set_avalon4_fan(setting)) { + sprintf(replybuf, "invalid fan value, valid range 0-100"); + return replybuf; + } + + applog(LOG_NOTICE, "%s %d: Update fan to %d-%d", + avalon4->drv->name, avalon4->device_id, + opt_avalon4_fan_min, opt_avalon4_fan_max); + + return NULL; + } + + if (strcasecmp(option, "frequency") == 0) { + if (!setting || !*setting) { + sprintf(replybuf, "missing frequency value"); + return replybuf; + } + + if (set_avalon4_freq(setting)) { + sprintf(replybuf, "invalid frequency value, valid range %d-%d", + AVA4_DEFAULT_FREQUENCY_MIN, AVA4_DEFAULT_FREQUENCY_MAX); + return replybuf; + } + + applog(LOG_NOTICE, "%s %d: Update frequency to %d", + avalon4->drv->name, avalon4->device_id, + (opt_avalon4_freq[0] * 4 + opt_avalon4_freq[1] * 4 + opt_avalon4_freq[2]) / 9); + + return NULL; + } + + if (strcasecmp(option, "led") == 0) { + if (!setting || !*setting) { + sprintf(replybuf, "missing module_id setting"); + return replybuf; + } + + val = atoi(setting); + if (val < 1 || val >= AVA4_DEFAULT_MODULARS) { + sprintf(replybuf, "invalid module_id: %d, valid range 1-%d", val, AVA4_DEFAULT_MODULARS); + return replybuf; + } + + if (!info->enable[val]) { + sprintf(replybuf, "the current module was disabled %d", val); + return replybuf; + } + + info->led_red[val] = !info->led_red[val]; + + applog(LOG_NOTICE, "%s %d: Module:%d, LED: %s", + avalon4->drv->name, avalon4->device_id, + val, info->led_red[val] ? "on" : "off"); + + return NULL; + } + + if (strcasecmp(option, "voltage") == 0) { + int val_mod, val_volt, ret; + + if (!setting || !*setting) { + sprintf(replybuf, "missing voltage value"); + return replybuf; + } + + ret = sscanf(setting, "%d-%d", &val_mod, &val_volt); + if (ret != 2) { + sprintf(replybuf, "invalid voltage parameter, format: moduleid-voltage"); + return replybuf; + } + + if (val_mod < 0 || val_mod >= AVA4_DEFAULT_MODULARS || + val_volt < AVA4_DEFAULT_VOLTAGE_MIN || val_volt > AVA4_DEFAULT_VOLTAGE_MAX) { + sprintf(replybuf, "invalid module_id or voltage value, valid module_id range %d-%d, valid voltage range %d-%d", + 0, AVA4_DEFAULT_MODULARS, + AVA4_DEFAULT_VOLTAGE_MIN, AVA4_DEFAULT_VOLTAGE_MAX); + return replybuf; + } + + if (!info->enable[val_mod]) { + sprintf(replybuf, "the current module was disabled %d", val_mod); + return replybuf; + } + + info->set_voltage[val_mod] = val_volt; + + if (val_mod == AVA4_MODULE_BROADCAST) { + for (i = 1; i < AVA4_DEFAULT_MODULARS; i++) + info->set_voltage[i] = val_volt; + info->set_voltage_broadcat = 1; + } else + info->set_voltage_broadcat = 0; + + applog(LOG_NOTICE, "%s %d: Update module[%d] voltage to %d", + avalon4->drv->name, avalon4->device_id, val_mod, val_volt); + + return NULL; + } + + sprintf(replybuf, "Unknown option: %s", option); + return replybuf; +} + +static void avalon4_statline_before(char *buf, size_t bufsiz, struct cgpu_info *avalon4) +{ + struct avalon4_info *info = avalon4->device_data; + int temp = get_current_temp_max(info); + int voltsmin = AVA4_DEFAULT_VOLTAGE_MAX, voltsmax = AVA4_DEFAULT_VOLTAGE_MIN; + int fanmin = AVA4_DEFAULT_FAN_MAX, fanmax = AVA4_DEFAULT_FAN_MIN; + int i, frequency; + + for (i = 1; i < AVA4_DEFAULT_MODULARS; i++) { + if (!info->enable[i]) + continue; + + if (fanmax <= info->fan_pct[i]) + fanmax = info->fan_pct[i]; + if (fanmin >= info->fan_pct[i]) + fanmin = info->fan_pct[i]; + + if (voltsmax <= info->get_voltage[i]) + voltsmax = info->get_voltage[i]; + if (voltsmin >= info->get_voltage[i]) + voltsmin = info->get_voltage[i]; + } +#if 0 + tailsprintf(buf, bufsiz, "%2dMMs %.4fV-%.4fV %4dMhz %2dC %3d%%-%3d%%", + info->mm_count, (float)voltsmin / 10000, (float)voltsmax / 10000, + (info->set_frequency[0] * 4 + info->set_frequency[1] * 4 + info->set_frequency[2]) / 9, + temp, fanmin, fanmax); +#endif + frequency = (info->set_frequency[0] * 4 + info->set_frequency[1] * 4 + info->set_frequency[2]) / 9; + tailsprintf(buf, bufsiz, "%4dMhz %2dC %3d%% %.3fV", frequency, + temp, fanmin, (float)voltsmax / 10000); +} + +struct device_drv avalon4_drv = { + .drv_id = DRIVER_avalon4, + .dname = "avalon4", + .name = "AV4", + .set_device = avalon4_set_device, + .get_api_stats = avalon4_api_stats, + .get_statline_before = avalon4_statline_before, + .drv_detect = avalon4_detect, + .thread_prepare = avalon4_prepare, + .hash_work = hash_driver_work, + .flush_work = avalon4_update, + .update_work = avalon4_update, + .scanwork = avalon4_scanhash, +}; diff --git a/driver-avalon4.h b/driver-avalon4.h new file mode 100644 index 0000000000..3bc7da00bf --- /dev/null +++ b/driver-avalon4.h @@ -0,0 +1,200 @@ +/* + * Copyright 2013-2014 Con Kolivas + * Copyright 2012-2014 Xiangfu + * + * 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 _AVALON4_H_ +#define _AVALON4_H_ + +#include "util.h" + +#ifdef USE_AVALON4 + +#define AVA4_DEFAULT_FAN_MIN 5 /* % */ +#define AVA4_DEFAULT_FAN_MAX 85 +/* Percentage required to make sure fan starts spinning, then we can go down */ +#define AVA4_DEFAULT_FAN_START 15 + +#define AVA4_DEFAULT_TEMP_TARGET 42 +#define AVA4_DEFAULT_TEMP_OVERHEAT 65 + +#define AVA4_DEFAULT_VOLTAGE_MIN 4000 +#define AVA4_DEFAULT_VOLTAGE_MAX 9000 + +#define AVA4_DEFAULT_FREQUENCY_MIN 100 +#define AVA4_DEFAULT_FREQUENCY_MAX 1000 + +#define AVA4_DEFAULT_MODULARS 64 +#define AVA4_DEFAULT_MINERS 10 +#define AVA4_DEFAULT_ASIC_COUNT 4 + +#define AVA4_DEFAULT_VOLTAGE 6875 +#define AVA4_DEFAULT_FREQUENCY 200 +#define AVA4_DEFAULT_POLLING_DELAY 20 /* ms */ + +#define AVA4_DH_INC 0.03 +#define AVA4_DH_DEC 0.001 + +#define AVA4_PWM_MAX 0x3FF + +#define AVA4_AUC_VER_LEN 12 /* Version length: 12 (AUC-YYYYMMDD) */ +#define AVA4_AUC_SPEED 400000 +#define AVA4_AUC_XDELAY 9600 /* 4800 = 1ms in AUC (11U14) */ +#define AVA4_AUC_P_SIZE 64 + + +/* Avalon4 protocol package type from MM protocol.h + * https://github.com/Canaan-Creative/MM/blob/avalon4/firmware/protocol.h */ +#define AVA4_MM_VER_LEN 15 +#define AVA4_MM_DNA_LEN 8 +#define AVA4_H1 'C' +#define AVA4_H2 'N' + +#define AVA4_P_COINBASE_SIZE (6 * 1024 + 64) +#define AVA4_P_MERKLES_COUNT 30 + +#define AVA4_P_COUNT 40 +#define AVA4_P_DATA_LEN 32 + +/* Broadcase with block iic_write*/ +#define AVA4_P_DETECT 0x10 + +/* Broadcase With non-block iic_write*/ +#define AVA4_P_STATIC 0x11 +#define AVA4_P_JOB_ID 0x12 +#define AVA4_P_COINBASE 0x13 +#define AVA4_P_MERKLES 0x14 +#define AVA4_P_HEADER 0x15 +#define AVA4_P_TARGET 0x16 + +/* Broadcase or Address */ +#define AVA4_P_SET 0x20 +#define AVA4_P_FINISH 0x21 + +/* Have to with I2C address */ +#define AVA4_P_POLLING 0x30 +#define AVA4_P_REQUIRE 0x31 +#define AVA4_P_TEST 0x32 + +/* Back to host */ +#define AVA4_P_ACKDETECT 0x40 +#define AVA4_P_STATUS 0x41 +#define AVA4_P_NONCE 0x42 +#define AVA4_P_TEST_RET 0x43 + +#define AVA4_MODULE_BROADCAST 0 +/* Endof Avalon4 protocol package type */ + +#define AVA4_MM40_PREFIXSTR "40" +#define AVA4_MM41_PREFIXSTR "41" +#define AVA4_MM_VERNULL "NONE" + +#define AVA4_TYPE_MM40 40 +#define AVA4_TYPE_MM41 41 +#define AVA4_TYPE_NULL 00 + +#define AVA4_IIC_RESET 0xa0 +#define AVA4_IIC_INIT 0xa1 +#define AVA4_IIC_DEINIT 0xa2 +#define AVA4_IIC_XFER 0xa5 +#define AVA4_IIC_INFO 0xa6 + +struct avalon4_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 avalon4_ret avalon4_pkg + +struct avalon4_info { + cglock_t update_lock; + + int polling_first; + int polling_err_cnt[AVA4_DEFAULT_MODULARS]; + int xfer_err_cnt; + + int pool_no; + struct pool pool0; + struct pool pool1; + struct pool pool2; + + struct timeval last_fan; + struct timeval last_stratum; + + char auc_version[AVA4_AUC_VER_LEN + 1]; + int auc_speed; + int auc_xdelay; + int auc_temp; + + int mm_count; + + int set_frequency[3]; + int set_voltage[AVA4_DEFAULT_MODULARS]; + int set_voltage_broadcat; + + int mod_type[AVA4_DEFAULT_MODULARS]; + bool enable[AVA4_DEFAULT_MODULARS]; + + struct timeval elapsed[AVA4_DEFAULT_MODULARS]; + char mm_version[AVA4_DEFAULT_MODULARS][AVA4_MM_VER_LEN + 1]; + uint8_t mm_dna[AVA4_DEFAULT_MODULARS][AVA4_MM_DNA_LEN + 1]; + int get_voltage[AVA4_DEFAULT_MODULARS]; + int get_frequency[AVA4_DEFAULT_MODULARS]; + int power_good[AVA4_DEFAULT_MODULARS]; + int fan_pct[AVA4_DEFAULT_MODULARS]; + int fan[AVA4_DEFAULT_MODULARS]; + int temp[AVA4_DEFAULT_MODULARS]; + int led_red[AVA4_DEFAULT_MODULARS]; + + uint64_t local_works[AVA4_DEFAULT_MODULARS]; + uint64_t hw_works[AVA4_DEFAULT_MODULARS]; + + uint32_t local_work[AVA4_DEFAULT_MODULARS]; + uint32_t hw_work[AVA4_DEFAULT_MODULARS]; + + uint32_t lw5[AVA4_DEFAULT_MODULARS][6]; + uint32_t hw5[AVA4_DEFAULT_MODULARS][6]; + int i_1m; + struct timeval last_5m; + struct timeval last_1m; + + int matching_work[AVA4_DEFAULT_MODULARS][AVA4_DEFAULT_MINERS]; + int chipmatching_work[AVA4_DEFAULT_MODULARS][AVA4_DEFAULT_MINERS][4]; +}; + +struct avalon4_iic_info { + uint8_t iic_op; + union { + uint32_t aucParam[2]; + uint8_t slave_addr; + } iic_param; +}; + +#define AVA4_WRITE_SIZE (sizeof(struct avalon4_pkg)) +#define AVA4_READ_SIZE AVA4_WRITE_SIZE + +#define AVA4_SEND_OK 0 +#define AVA4_SEND_ERROR -1 + +extern char *set_avalon4_fan(char *arg); +extern char *set_avalon4_temp(char *arg); +extern char *set_avalon4_freq(char *arg); +extern char *set_avalon4_voltage(char *arg); +extern bool opt_avalon4_autov; +extern int opt_avalon4_temp_target; +extern int opt_avalon4_overheat; +extern int opt_avalon4_polling_delay; +extern int opt_avalon4_aucspeed; +extern int opt_avalon4_aucxdelay; +extern int opt_avalon4_ntime_offset; +#endif /* USE_AVALON4 */ +#endif /* _AVALON4_H_ */ diff --git a/driver-bab.c b/driver-bab.c new file mode 100644 index 0000000000..1bba8e0878 --- /dev/null +++ b/driver-bab.c @@ -0,0 +1,3062 @@ +/* + * Copyright 2013-2014 Andrew Smith + * Copyright 2013 bitfury + * + * BitFury GPIO code originally based on chainminer code: + * https://github.com/bfsb/chainminer + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the Free + * Software Foundation; either version 3 of the License, or (at your option) + * any later version. See COPYING for more details. + */ + +#include "config.h" +#include "compat.h" +#include "miner.h" +#include "sha2.h" +#include "klist.h" +#include + +/* + * Tested on RPi running both Raspbian and Arch + * with BlackArrow BitFury V1 & V2 GPIO Controller + * with 16 chip BlackArrow BitFury boards + */ + +#ifndef LINUX +static void bab_detect(__maybe_unused bool hotplug) +{ +} +#else + +#include +#include +#include +#include +#include + +#define BAB_SPI_BUS 0 +#define BAB_SPI_CHIP 0 + +#define BAB_SPI_SPEED 96000 +#define BAB_SPI_BUFSIZ 1024 + +#define BAB_DELAY_USECS 0 +#define BAB_TRF_DELAY 0 + +#define BAB_ADDR(_n) (*((babinfo->gpio) + (_n))) + +#define BAB_INP_GPIO(_n) BAB_ADDR((_n) / 10) &= (~(7 << (((_n) % 10) * 3))) +#define BAB_OUT_GPIO(_n) BAB_ADDR((_n) / 10) |= (1 << (((_n) % 10) * 3)) +#define BAB_OUT_GPIO_V(_n, _v) BAB_ADDR((_n) / 10) |= (((_v) <= 3 ? (_v) + 4 : \ + ((_v) == 4 ? 3 : 2)) << (((_n) % 10) * 3)) + +#define BAB_GPIO_SET BAB_ADDR(7) +#define BAB_GPIO_CLR BAB_ADDR(10) +#define BAB_GPIO_LEVEL BAB_ADDR(13) + +// If the V1 test of this many chips finds no chips it will try V2 +#define BAB_V1_CHIP_TEST 32 + +//maximum number of chips per board +#define BAB_BOARDCHIPS 16 +#define BAB_MAXBUF (BAB_MAXCHIPS * 512) +#define BAB_V1_BANK 0 +//maximum number of alternative banks +#define BAB_MAXBANKS 4 +//maximum number of boards in a bank +#define BAB_BANKBOARDS 6 +//maximum number of chips on alternative bank +#define BAB_BANKCHIPS (BAB_BOARDCHIPS * BAB_BANKBOARDS) +//maximum number of chips +#define BAB_MAXCHIPS (BAB_MAXBANKS * BAB_BANKCHIPS) +#define BAB_CORES 16 +#define BAB_X_COORD 21 +#define BAB_Y_COORD 36 + +#define BAB_NOOP 0 +#define BAB_BREAK ((uint8_t *)"\04") +#define BAB_ASYNC ((uint8_t *)"\05") +#define BAB_SYNC ((uint8_t *)"\06") + +#define BAB_FFL " - from %s %s() line %d" +#define BAB_FFL_HERE __FILE__, __func__, __LINE__ +#define BAB_FFL_PASS file, func, line + +#define bab_reset(_bank, _times) _bab_reset(babcgpu, babinfo, _bank, _times) +#define bab_txrx(_item, _det) _bab_txrx(babcgpu, babinfo, _item, _det, BAB_FFL_HERE) +#define bab_add_buf(_item, _data) _bab_add_buf(_item, _data, sizeof(_data)-1, BAB_FFL_HERE) +#define BAB_ADD_BREAK(_item) _bab_add_buf(_item, BAB_BREAK, 1, BAB_FFL_HERE) +#define BAB_ADD_ASYNC(_item) _bab_add_buf(_item, BAB_ASYNC, 1, BAB_FFL_HERE) +#define bab_config_reg(_item, _reg, _ena) _bab_config_reg(_item, _reg, _ena, BAB_FFL_HERE) +#define bab_add_data(_item, _addr, _data, _siz) _bab_add_data(_item, _addr, (const uint8_t *)(_data), _siz, BAB_FFL_HERE) + +#define BAB_ADD_NOOPs(_item, _count) _bab_add_noops(_item, _count, BAB_FFL_HERE) + +#define BAB_ADD_MIN 4 +#define BAB_ADD_MAX 128 + +#define BAB_BASEA 4 +#define BAB_BASEB 61 +#define BAB_COUNTERS 16 +static const uint8_t bab_counters[BAB_COUNTERS] = { + 64, 64, + BAB_BASEA, BAB_BASEA+4, + BAB_BASEA+2, BAB_BASEA+2+16, + BAB_BASEA, BAB_BASEA+1, + (BAB_BASEB)%65, (BAB_BASEB+1)%65, + (BAB_BASEB+3)%65, (BAB_BASEB+3+16)%65, + (BAB_BASEB+4)%65, (BAB_BASEB+4+4)%65, + (BAB_BASEB+3+3)%65, (BAB_BASEB+3+1+3)%65 +}; + +#define BAB_W1 16 +static const uint32_t bab_w1[BAB_W1] = { + 0, 0, 0, 0xffffffff, + 0x80000000, 0, 0, 0, + 0, 0, 0, 0, + 0, 0, 0, 0x00000280 +}; + +#define BAB_W2 8 +static const uint32_t bab_w2[BAB_W2] = { + 0x80000000, 0, 0, 0, + 0, 0, 0, 0x00000100 +}; + +#define BAB_TEST_DATA 19 +static const uint32_t bab_test_data[BAB_TEST_DATA] = { + 0xb0e72d8e, 0x1dc5b862, 0xe9e7c4a6, 0x3050f1f5, + 0x8a1a6b7e, 0x7ec384e8, 0x42c1c3fc, 0x8ed158a1, + 0x8a1a6b7e, 0x6f484872, 0x4ff0bb9b, 0x12c97f07, + 0xb0e72d8e, 0x55d979bc, 0x39403296, 0x40f09e84, + 0x8a0bb7b7, 0x33af304f, 0x0b290c1a //, 0xf0c4e61f +}; + +/* + * maximum chip speed available for auto tuner + * speed/nrate/hrate/watt + * 53/ 97/ 100/ 84 + * 54/ 98/ 107/ 88 + * 55/ 99/ 115/ 93 + * 56/ 101/ 125/ 99 + */ +#define BAB_MAXSPEED 57 +#define BAB_DEFMAXSPEED 55 +#define BAB_DEFSPEED 53 +#define BAB_MINSPEED 52 +#define BAB_ABSMINSPEED 32 + +/* + * % of errors to tune the speed up or down + * 1.0 to 10.0 should average around 5.5% errors + */ +#define BAB_TUNEUP 1.0 +#define BAB_TUNEDOWN 10.0 + +#define MIDSTATE_BYTES 32 +#define MERKLE_OFFSET 64 +#define MERKLE_BYTES 12 +#define BLOCK_HEADER_BYTES 80 + +#define MIDSTATE_UINTS (MIDSTATE_BYTES / sizeof(uint32_t)) +#define DATA_UINTS ((BLOCK_HEADER_BYTES / sizeof(uint32_t)) - 1) + +// Auto adjust +#define BAB_AUTO_REG 0 +#define BAB_AUTO_VAL 0x01 +// iclk +#define BAB_ICLK_REG 1 +#define BAB_ICLK_VAL 0x02 +// No fast clock +#define BAB_FAST_REG 2 +#define BAB_FAST_VAL 0x04 +// Divide by 2 +#define BAB_DIV2_REG 3 +#define BAB_DIV2_VAL 0x08 +// Slow Clock +#define BAB_SLOW_REG 4 +#define BAB_SLOW_VAL 0x10 +// No oclk +#define BAB_OCLK_REG 6 +#define BAB_OCLK_VAL 0x20 +// Has configured +#define BAB_CFGD_VAL 0x40 + +#define BAB_DEFCONF (BAB_AUTO_VAL | \ + BAB_ICLK_VAL | \ + BAB_DIV2_VAL | \ + BAB_SLOW_VAL) + +#define BAB_REG_CLR_FROM 7 +#define BAB_REG_CLR_TO 11 + +#define BAB_AUTO_SET(_c) ((_c) & BAB_AUTO_VAL) +#define BAB_ICLK_SET(_c) ((_c) & BAB_ICLK_VAL) +#define BAB_FAST_SET(_c) ((_c) & BAB_FAST_VAL) +#define BAB_DIV2_SET(_c) ((_c) & BAB_DIV2_VAL) +#define BAB_SLOW_SET(_c) ((_c) & BAB_SLOW_VAL) +#define BAB_OCLK_SET(_c) ((_c) & BAB_OCLK_VAL) +#define BAB_CFGD_SET(_c) ((_c) & BAB_CFGD_VAL) + +#define BAB_AUTO_BIT(_c) (BAB_AUTO_SET(_c) ? true : false) +#define BAB_ICLK_BIT(_c) (BAB_ICLK_SET(_c) ? false : true) +#define BAB_FAST_BIT(_c) (BAB_FAST_SET(_c) ? true : false) +#define BAB_DIV2_BIT(_c) (BAB_DIV2_SET(_c) ? false : true) +#define BAB_SLOW_BIT(_c) (BAB_SLOW_SET(_c) ? true : false) +#define BAB_OCLK_BIT(_c) (BAB_OCLK_SET(_c) ? true : false) + +#define BAB_COUNT_ADDR 0x0100 +#define BAB_W1A_ADDR 0x1000 +#define BAB_W1B_ADDR 0x1400 +#define BAB_W2_ADDR 0x1900 +#define BAB_INP_ADDR 0x3000 +#define BAB_OSC_ADDR 0x6000 +#define BAB_REG_ADDR 0x7000 + +/* + * valid: 0x01 0x03 0x07 0x0F 0x1F 0x3F 0x7F 0xFF + * max { 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x7F, 0x00 } + * max { 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x3F, 0x00 } + * avg { 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x01, 0x00, 0x00 } + * slo { 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x3F, 0x00 } + * min { 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 } + * good: 0x1F (97) 0x3F (104) 0x7F (109) 0xFF (104) + */ + +#define BAB_OSC 8 +static const uint8_t bab_osc_bits[BAB_OSC] = + { 0x01, 0x03, 0x07, 0x0F, 0x1F, 0x3F, 0x7F, 0xFF }; + +static const uint8_t bab_reg_ena[4] = { 0xc1, 0x6a, 0x59, 0xe3 }; +static const uint8_t bab_reg_dis[4] = { 0x00, 0x00, 0x00, 0x00 }; + +#define BAB_NONCE_OFFSETS 3 +#define BAB_OFF_0x1C_STA 2 +#define BAB_OFF_0x1C_FIN 2 +#define BAB_OFF_OTHER_STA 0 +#define BAB_OFF_OTHER_FIN 1 + +#define BAB_EVIL_NONCE 0xe0 +#define BAB_EVIL_MASK 0xff + +static const uint32_t bab_nonce_offsets[] = {-0x800000, 0, -0x400000}; + +struct bab_work_send { + uint32_t midstate[MIDSTATE_UINTS]; + uint32_t ms3steps[MIDSTATE_UINTS]; + uint32_t merkle7; + uint32_t ntime; + uint32_t bits; +}; + +#define BAB_REPLY_NONCES 16 +struct bab_work_reply { + uint32_t nonce[BAB_REPLY_NONCES]; + uint32_t jobsel; + uint32_t spichk; +}; + +#define BAB_CHIP_MIN (sizeof(struct bab_work_reply)+16) + +#define ALLOC_WITEMS 1024 +#define LIMIT_WITEMS 0 + +// Work +typedef struct witem { + struct work *work; + struct bab_work_send chip_input; + bool ci_setup; + bool rolled; + int nonces; + struct timeval work_start; +} WITEM; + +#define ALLOC_SITEMS 8 +#define LIMIT_SITEMS 0 + +// SPI I/O +typedef struct sitem { + uint32_t siz; + uint8_t wbuf[BAB_MAXBUF]; + uint8_t rbuf[BAB_MAXBUF]; + uint32_t chip_off[BAB_MAXCHIPS+1]; + uint32_t bank_off[BAB_MAXBANKS+2]; + // WITEMs used to build the work + K_ITEM *witems[BAB_MAXCHIPS]; + struct timeval work_start; +} SITEM; + +#define ALLOC_RITEMS 256 +#define LIMIT_RITEMS 0 + +// Results +typedef struct ritem { + int chip; + int nonces; + uint32_t nonce[BAB_REPLY_NONCES]; + bool not_first_reply; + struct timeval when; +} RITEM; + +#define ALLOC_NITEMS 102400 +#define LIMIT_NITEMS 0 + +// Nonce History +typedef struct nitem { + struct timeval found; +} NITEM; + +#define DATAW(_item) ((WITEM *)(_item->data)) +#define DATAS(_item) ((SITEM *)(_item->data)) +#define DATAR(_item) ((RITEM *)(_item->data)) +#define DATAN(_item) ((NITEM *)(_item->data)) + +// Record the number of each band between work sends +#define BAB_DELAY_BANDS 10 +#define BAB_DELAY_BASE 0.5 +#define BAB_DELAY_STEP 0.2 + +#define BAB_CHIP_SPEEDS 6 +// less than or equal GH/s +static double chip_speed_ranges[BAB_CHIP_SPEEDS - 1] = + { 0.0, 0.8, 1.6, 2.2, 2.8 }; +// Greater than the last one above means it's the last speed +static char *chip_speed_names[BAB_CHIP_SPEEDS] = + { "Bad", "V.Slow", "Slow", "OK", "Good", "Fast" }; + +/* + * This is required to do chip tuning + * If disabled, it will simply run the chips at default speed + * unless they never return valid results + */ +#define UPDATE_HISTORY 1 + +struct bab_info { + struct thr_info spi_thr; + struct thr_info res_thr; + + pthread_mutex_t did_lock; + pthread_mutex_t nonce_lock; + + // All GPIO goes through this + volatile unsigned *gpio; + + int version; + int spifd; + int chips; + int chips_per_bank[BAB_MAXBANKS+1]; + int missing_chips_per_bank[BAB_MAXBANKS+1]; + int bank_first_chip[BAB_MAXBANKS+1]; + int bank_last_chip[BAB_MAXBANKS+1]; + int boards; + int banks; + uint32_t chip_spis[BAB_MAXCHIPS+1]; + + int reply_wait; + uint64_t reply_waits; + + cgsem_t scan_work; + cgsem_t spi_work; + cgsem_t spi_reply; + cgsem_t process_reply; + + bool disabled[BAB_MAXCHIPS]; + int total_disabled; + + struct bab_work_reply chip_results[BAB_MAXCHIPS]; + struct bab_work_reply chip_prev[BAB_MAXCHIPS]; + + uint8_t chip_fast[BAB_MAXCHIPS]; + uint8_t chip_conf[BAB_MAXCHIPS]; + uint8_t old_fast[BAB_MAXCHIPS]; + uint8_t old_conf[BAB_MAXCHIPS]; + uint8_t chip_bank[BAB_MAXCHIPS+1]; + + uint8_t osc[BAB_OSC]; + + /* + * Ignore errors in the first work reply since + * they may be from a previous run or random junk + * There can be >100 with just one 16 chip board + */ + uint32_t initial_ignored; + bool not_first_reply[BAB_MAXCHIPS]; + + // Stats + uint64_t core_good[BAB_MAXCHIPS][BAB_CORES]; + uint64_t core_bad[BAB_MAXCHIPS][BAB_CORES]; + uint64_t chip_spie[BAB_MAXCHIPS]; // spi errors + uint64_t chip_miso[BAB_MAXCHIPS]; // msio errors + uint64_t chip_nonces[BAB_MAXCHIPS]; + uint64_t chip_good[BAB_MAXCHIPS]; + uint64_t chip_bad[BAB_MAXCHIPS]; + uint64_t chip_ncore[BAB_MAXCHIPS][BAB_X_COORD][BAB_Y_COORD]; + + uint64_t chip_cont_bad[BAB_MAXCHIPS]; + uint64_t chip_max_bad[BAB_MAXCHIPS]; + + uint64_t discarded_e0s; + + uint64_t untested_nonces; + uint64_t tested_nonces; + + uint64_t new_nonces; + uint64_t ok_nonces; + + uint64_t nonce_offset_count[BAB_NONCE_OFFSETS]; + uint64_t total_tests; + uint64_t max_tests_per_nonce; + uint64_t total_links; + uint64_t total_proc_links; + uint64_t max_links; + uint64_t max_proc_links; + uint64_t total_work_links; + + uint64_t fail; + uint64_t fail_total_tests; + uint64_t fail_total_links; + uint64_t fail_total_work_links; + + uint64_t ign_total_tests; + uint64_t ign_total_links; + uint64_t ign_total_work_links; + + struct timeval last_sent_work; + uint64_t delay_count; + double delay_min; + double delay_max; + /* + * 0 is below band ranges + * BAB_DELAY_BANDS+1 is above band ranges + */ + uint64_t delay_bands[BAB_DELAY_BANDS+2]; + + uint64_t send_count; + double send_total; + double send_min; + double send_max; + + // Work + K_LIST *wfree_list; + K_STORE *available_work; + K_STORE *chip_work[BAB_MAXCHIPS]; + + // SPI I/O + K_LIST *sfree_list; + // Waiting to send + K_STORE *spi_list; + // Sent + K_STORE *spi_sent; + + // Results + K_LIST *rfree_list; + K_STORE *res_list; + + // Nonce History + K_LIST *nfree_list; + K_STORE *good_nonces[BAB_MAXCHIPS]; + K_STORE *bad_nonces[BAB_MAXCHIPS]; + + struct timeval first_work[BAB_MAXCHIPS]; +#if UPDATE_HISTORY + uint32_t work_count[BAB_MAXCHIPS]; + struct timeval last_tune[BAB_MAXCHIPS]; + uint8_t bad_fast[BAB_MAXCHIPS]; + bool bad_msg[BAB_MAXCHIPS]; +#endif + uint64_t work_unrolled; + uint64_t work_rolled; + + // bab-options (in order) + uint8_t max_speed; + uint8_t def_speed; + uint8_t min_speed; + double tune_up; + double tune_down; + uint32_t speed_hz; + uint16_t delay_usecs; + uint64_t trf_delay; + + struct timeval last_did; + + bool initialised; +}; + +/* + * Amount of time for history + * Older items in nonce_history are discarded + * 300s / 5 minutes + */ +#define HISTORY_TIME_S 300 + +/* + * If the SPI I/O thread waits longer than this long for work + * it will report an error saying how long it's waiting + * and again every BAB_STD_WAIT_mS after that + */ +#define BAB_LONG_uS 1200000 + +/* + * If work wasn't available early enough, + * report every BAB_LONG_WAIT_mS until it is + */ +#define BAB_LONG_WAIT_mS 888 + +/* + * Some amount of time to wait for work + * before checking how long we've waited + */ +#define BAB_STD_WAIT_mS 888 + +/* + * How long to wait for the ioctl() to complete (per BANK) + * This is a failsafe in case the ioctl() fails + * since bab_txrx() will already post a wakeup when it completes + * V1 is set to this x 2 + * V2 is set to this x active banks + */ +#define BAB_REPLY_WAIT_mS 160 + +/* + * Work items older than this should not expect results + * It has to allow for the result buffer returned with the next result + * 0.75GH/s takes 5.727s to do a full nonce range + * If HW is too high, consider increasing this to see if work is being + * expired too early (due to slow chips) + */ +#define BAB_WORK_EXPIRE_mS 7800 + +// Don't send work more often than this +#define BAB_EXPECTED_WORK_DELAY_mS 899 + +/* + * If a chip only has bad results after this time limit in seconds, + * then switch it down to min_speed + */ +#define BAB_BAD_TO_MIN (HISTORY_TIME_S + 10) + +/* + * Also, just to be sure it's actually mining, it must have got this + * many bad results before considering disabling it + */ +#define BAB_BAD_COUNT 100 + +/* + * If a chip only has bad results after this time limit in seconds, + * then disable it + * A chip only returning bad results will use a lot more CPU than + * an ok chip since all results will be tested against all unexpired + * work that's been sent to the chip + */ +#define BAB_BAD_DEAD (BAB_BAD_TO_MIN * 2) + +/* + * Maximum bab_queue_full() will roll work if it is allowed to + * Since work can somtimes (rarely) queue up with many chips, + * limit it to avoid it getting too much range in the pending work + */ +#define BAB_MAX_ROLLTIME 42 + +static void bab_ms3steps(uint32_t *p) +{ + uint32_t a, b, c, d, e, f, g, h, new_e, new_a; + int i; + + a = p[0]; + b = p[1]; + c = p[2]; + d = p[3]; + e = p[4]; + f = p[5]; + g = p[6]; + h = p[7]; + for (i = 0; i < 3; i++) { + new_e = p[i+16] + sha256_k[i] + h + CH(e,f,g) + SHA256_F2(e) + d; + new_a = p[i+16] + sha256_k[i] + h + CH(e,f,g) + SHA256_F2(e) + + SHA256_F1(a) + MAJ(a,b,c); + d = c; + c = b; + b = a; + a = new_a; + h = g; + g = f; + f = e; + e = new_e; + } + p[15] = a; + p[14] = b; + p[13] = c; + p[12] = d; + p[11] = e; + p[10] = f; + p[9] = g; + p[8] = h; +} + +static uint32_t bab_decnonce(uint32_t in) +{ + uint32_t out; + + /* First part load */ + out = (in & 0xFF) << 24; + in >>= 8; + + /* Byte reversal */ + in = (((in & 0xaaaaaaaa) >> 1) | ((in & 0x55555555) << 1)); + in = (((in & 0xcccccccc) >> 2) | ((in & 0x33333333) << 2)); + in = (((in & 0xf0f0f0f0) >> 4) | ((in & 0x0f0f0f0f) << 4)); + + out |= (in >> 2) & 0x3FFFFF; + + /* Extraction */ + if (in & 1) + out |= (1 << 23); + if (in & 2) + out |= (1 << 22); + + out -= 0x800004; + return out; +} + +static void cleanup_older(struct cgpu_info *babcgpu, int chip, K_ITEM *witem) +{ + struct bab_info *babinfo = (struct bab_info *)(babcgpu->device_data); + struct timeval now; + bool expired_item; + K_ITEM *tail; + + cgtime(&now); + + K_WLOCK(babinfo->chip_work[chip]); + tail = babinfo->chip_work[chip]->tail; + expired_item = false; + // Discard expired work + while (tail) { + if (ms_tdiff(&now, &(DATAW(tail)->work_start)) < BAB_WORK_EXPIRE_mS) + break; + + if (tail == witem) + expired_item = true; + + k_unlink_item(babinfo->chip_work[chip], tail); + K_WUNLOCK(babinfo->chip_work[chip]); + if (DATAW(tail)->rolled) + free_work(DATAW(tail)->work); + else + work_completed(babcgpu, DATAW(tail)->work); + K_WLOCK(babinfo->chip_work[chip]); + k_add_head(babinfo->wfree_list, tail); + tail = babinfo->chip_work[chip]->tail; + } + // If we didn't expire witem, then remove all older than it + if (!expired_item && witem && witem->next) { + tail = babinfo->chip_work[chip]->tail; + while (tail && tail != witem) { + k_unlink_item(babinfo->chip_work[chip], tail); + K_WUNLOCK(babinfo->chip_work[chip]); + if (DATAW(tail)->rolled) + free_work(DATAW(tail)->work); + else + work_completed(babcgpu, DATAW(tail)->work); + K_WLOCK(babinfo->chip_work[chip]); + k_add_head(babinfo->wfree_list, tail); + tail = babinfo->chip_work[chip]->tail; + } + } + K_WUNLOCK(babinfo->chip_work[chip]); +} + +static void _bab_reset(__maybe_unused struct cgpu_info *babcgpu, struct bab_info *babinfo, int bank, int times) +{ + const int banks[BAB_MAXBANKS] = { 18, 23, 24, 25 }; + int i; + + BAB_INP_GPIO(10); + BAB_OUT_GPIO(10); + BAB_INP_GPIO(11); + BAB_OUT_GPIO(11); + + if (bank) { + for (i = 0; i < BAB_MAXBANKS; i++) { + BAB_INP_GPIO(banks[i]); + BAB_OUT_GPIO(banks[i]); + if (bank == i+1) + BAB_GPIO_SET = 1 << banks[i]; + else + BAB_GPIO_CLR = 1 << banks[i]; + } + cgsleep_us(4096); + } else { + for (i = 0; i < BAB_MAXBANKS; i++) + BAB_INP_GPIO(banks[i]); + } + + BAB_GPIO_SET = 1 << 11; + for (i = 0; i < times; i++) { // 1us = 1MHz + BAB_GPIO_SET = 1 << 10; + cgsleep_us(1); + BAB_GPIO_CLR = 1 << 10; + cgsleep_us(1); + } + BAB_GPIO_CLR = 1 << 11; + BAB_INP_GPIO(11); + BAB_INP_GPIO(10); + BAB_INP_GPIO(9); + BAB_OUT_GPIO_V(11, 0); + BAB_OUT_GPIO_V(10, 0); + BAB_OUT_GPIO_V(9, 0); +} + +// TODO: handle a false return where this is called? +static bool _bab_txrx(struct cgpu_info *babcgpu, struct bab_info *babinfo, K_ITEM *item, bool detect_ignore, const char *file, const char *func, const int line) +{ + int bank, i, count, chip1, chip2; + uint32_t siz, pos; + struct spi_ioc_transfer tran; + uintptr_t rbuf, wbuf; + + wbuf = (uintptr_t)(DATAS(item)->wbuf); + rbuf = (uintptr_t)(DATAS(item)->rbuf); + siz = (uint32_t)(DATAS(item)->siz); + + memset(&tran, 0, sizeof(tran)); + tran.speed_hz = babinfo->speed_hz; + tran.delay_usecs = babinfo->delay_usecs; + + i = 0; + pos = 0; + for (bank = 0; bank <= BAB_MAXBANKS; bank++) { + if (DATAS(item)->bank_off[bank]) { + bab_reset(bank, 64); + break; + } + } + + if (unlikely(bank > BAB_MAXBANKS)) { + applog(LOG_ERR, "%s%d: %s() failed to find a bank" BAB_FFL, + babcgpu->drv->name, babcgpu->device_id, + __func__, BAB_FFL_PASS); + return false; + } + + count = 0; + while (siz > 0) { + tran.tx_buf = wbuf; + tran.rx_buf = rbuf; + tran.speed_hz = BAB_SPI_SPEED; + if (pos == DATAS(item)->bank_off[bank]) { + for (; ++bank <= BAB_MAXBANKS; ) { + if (DATAS(item)->bank_off[bank] > pos) { + bab_reset(bank, 64); + break; + } + } + } + if (siz < BAB_SPI_BUFSIZ) + tran.len = siz; + else + tran.len = BAB_SPI_BUFSIZ; + + if (pos < DATAS(item)->bank_off[bank] && + DATAS(item)->bank_off[bank] < (pos + tran.len)) + tran.len = DATAS(item)->bank_off[bank] - pos; + + for (; i < babinfo->chips; i++) { + if (!DATAS(item)->chip_off[i]) + continue; + if (DATAS(item)->chip_off[i] >= pos + tran.len) { + tran.speed_hz = babinfo->chip_spis[i]; + break; + } + } + + if (unlikely(i > babinfo->chips)) { + applog(LOG_ERR, "%s%d: %s() failed to find chip" BAB_FFL, + babcgpu->drv->name, babcgpu->device_id, + __func__, BAB_FFL_PASS); + return false; + } + + if (unlikely(babinfo->chip_spis[i] == BAB_SPI_SPEED)) { + applog(LOG_DEBUG, "%s%d: %s() chip[%d] speed %d shouldn't be %d" BAB_FFL, + babcgpu->drv->name, babcgpu->device_id, + __func__, i, (int)babinfo->chip_spis[i], + BAB_SPI_SPEED, BAB_FFL_PASS); + } + + if (unlikely(tran.speed_hz == BAB_SPI_SPEED)) { + applog(LOG_DEBUG, "%s%d: %s() transfer speed %d shouldn't be %d" BAB_FFL, + babcgpu->drv->name, babcgpu->device_id, + __func__, (int)tran.speed_hz, + BAB_SPI_SPEED, BAB_FFL_PASS); + } + + count++; + if (ioctl(babinfo->spifd, SPI_IOC_MESSAGE(1), (void *)&tran) < 0) { + if (!detect_ignore || errno != 110) { + for (bank = BAB_MAXBANKS; bank >= 0; bank--) { + if (DATAS(item)->bank_off[bank] && + pos >= DATAS(item)->bank_off[bank]) { + break; + } + } + for (chip1 = babinfo->chips-1; chip1 >= 0; chip1--) { + if (DATAS(item)->chip_off[chip1] && + pos >= DATAS(item)->chip_off[chip1]) { + break; + } + } + for (chip2 = babinfo->chips-1; chip2 >= 0; chip2--) { + if (DATAS(item)->chip_off[chip2] && + (pos + tran.len) >= DATAS(item)->chip_off[chip2]) { + break; + } + } + applog(LOG_ERR, "%s%d: ioctl (%d) siz=%d bank=%d chip=%d-%d" + " failed err=%d" BAB_FFL, + babcgpu->drv->name, + babcgpu->device_id, + count, (int)(tran.len), + bank, chip1, chip2, + errno, BAB_FFL_PASS); + } + return false; + } + + siz -= tran.len; + wbuf += tran.len; + rbuf += tran.len; + pos += tran.len; + + if (siz > 0 && babinfo->trf_delay > 0) + cgsleep_us(babinfo->trf_delay); + } + cgtime(&(DATAS(item)->work_start)); + mutex_lock(&(babinfo->did_lock)); + cgtime(&(babinfo->last_did)); + mutex_unlock(&(babinfo->did_lock)); + return true; +} + +static void _bab_add_buf_rev(K_ITEM *item, const uint8_t *data, uint32_t siz, const char *file, const char *func, const int line) +{ + uint32_t now_used, i; + uint8_t tmp; + + now_used = DATAS(item)->siz; + if (now_used + siz >= BAB_MAXBUF) { + quitfrom(1, file, func, line, + "%s() buffer limit of %d exceeded=%d siz=%d", + __func__, BAB_MAXBUF, (int)(now_used + siz), (int)siz); + } + + for (i = 0; i < siz; i++) { + tmp = data[i]; + tmp = ((tmp & 0xaa)>>1) | ((tmp & 0x55) << 1); + tmp = ((tmp & 0xcc)>>2) | ((tmp & 0x33) << 2); + tmp = ((tmp & 0xf0)>>4) | ((tmp & 0x0f) << 4); + DATAS(item)->wbuf[now_used + i] = tmp; + } + + DATAS(item)->siz += siz; +} + +static void _bab_add_buf(K_ITEM *item, const uint8_t *data, size_t siz, const char *file, const char *func, const int line) +{ + uint32_t now_used; + + now_used = DATAS(item)->siz; + if (now_used + siz >= BAB_MAXBUF) { + quitfrom(1, file, func, line, + "%s() DATAS buffer limit of %d exceeded=%d siz=%d", + __func__, BAB_MAXBUF, (int)(now_used + siz), (int)siz); + } + + memcpy(&(DATAS(item)->wbuf[now_used]), data, siz); + DATAS(item)->siz += siz; +} + +static void _bab_add_noops(K_ITEM *item, size_t siz, const char *file, const char *func, const int line) +{ + uint32_t now_used; + + now_used = DATAS(item)->siz; + if (now_used + siz >= BAB_MAXBUF) { + quitfrom(1, file, func, line, + "%s() DATAS buffer limit of %d exceeded=%d siz=%d", + __func__, BAB_MAXBUF, (int)(now_used + siz), (int)siz); + } + + memset(&(DATAS(item)->wbuf[now_used]), BAB_NOOP, siz); + DATAS(item)->siz += siz; +} + +static void _bab_add_data(K_ITEM *item, uint32_t addr, const uint8_t *data, size_t siz, const char *file, const char *func, const int line) +{ + uint8_t tmp[3]; + int trf_siz; + + if (siz < BAB_ADD_MIN || siz > BAB_ADD_MAX) { + quitfrom(1, file, func, line, + "%s() called with invalid siz=%d (min=%d max=%d)", + __func__, (int)siz, BAB_ADD_MIN, BAB_ADD_MAX); + } + trf_siz = siz / 4; + tmp[0] = (trf_siz - 1) | 0xE0; + tmp[1] = (addr >> 8) & 0xff; + tmp[2] = addr & 0xff; + _bab_add_buf(item, tmp, sizeof(tmp), BAB_FFL_PASS); + _bab_add_buf_rev(item, data, siz, BAB_FFL_PASS); +} + +static void _bab_config_reg(K_ITEM *item, uint32_t reg, bool enable, const char *file, const char *func, const int line) +{ + if (enable) { + _bab_add_data(item, BAB_REG_ADDR + reg*32, + bab_reg_ena, sizeof(bab_reg_ena), BAB_FFL_PASS); + } else { + _bab_add_data(item, BAB_REG_ADDR + reg*32, + bab_reg_dis, sizeof(bab_reg_dis), BAB_FFL_PASS); + } + +} + +static void bab_set_osc(struct bab_info *babinfo, int chip) +{ + int fast, i; + + fast = babinfo->chip_fast[chip]; + + for (i = 0; i < BAB_OSC && fast > BAB_OSC; i++, fast -= BAB_OSC) { + babinfo->osc[i] = 0xff; + } + if (i < BAB_OSC && fast > 0 && fast <= BAB_OSC) + babinfo->osc[i++] = bab_osc_bits[fast - 1]; + for (; i < BAB_OSC; i++) + babinfo->osc[i] = 0x00; + + applog(LOG_DEBUG, "@osc(chip=%d) fast=%d 0x%02x 0x%02x 0x%02x 0x%02x 0x%02x 0x%02x 0x%02x 0x%02x", chip, fast, babinfo->osc[0], babinfo->osc[1], babinfo->osc[2], babinfo->osc[3], babinfo->osc[4], babinfo->osc[5], babinfo->osc[6], babinfo->osc[7]); +} + +static void bab_put(struct bab_info *babinfo, K_ITEM *sitem) +{ + struct bab_work_send *chip_input; + int i, reg, bank = 0; + size_t chip_siz; + + BAB_ADD_BREAK(sitem); + for (i = 0; i < babinfo->chips; i++) { + if (babinfo->chip_bank[i] != bank) { + DATAS(sitem)->bank_off[bank] = DATAS(sitem)->siz; + bank = babinfo->chip_bank[i]; + BAB_ADD_BREAK(sitem); + } + if (!(babinfo->disabled[i])) { + if (BAB_CFGD_SET(babinfo->chip_conf[i]) || !babinfo->chip_conf[i]) { + bab_set_osc(babinfo, i); + bab_add_data(sitem, BAB_OSC_ADDR, babinfo->osc, sizeof(babinfo->osc)); + bab_config_reg(sitem, BAB_ICLK_REG, BAB_ICLK_BIT(babinfo->chip_conf[i])); + bab_config_reg(sitem, BAB_FAST_REG, BAB_FAST_BIT(babinfo->chip_conf[i])); + bab_config_reg(sitem, BAB_DIV2_REG, BAB_DIV2_BIT(babinfo->chip_conf[i])); + bab_config_reg(sitem, BAB_SLOW_REG, BAB_SLOW_BIT(babinfo->chip_conf[i])); + bab_config_reg(sitem, BAB_OCLK_REG, BAB_OCLK_BIT(babinfo->chip_conf[i])); + for (reg = BAB_REG_CLR_FROM; reg <= BAB_REG_CLR_TO; reg++) + bab_config_reg(sitem, reg, false); + if (babinfo->chip_conf[i]) { + bab_add_data(sitem, BAB_COUNT_ADDR, bab_counters, sizeof(bab_counters)); + bab_add_data(sitem, BAB_W1A_ADDR, bab_w1, sizeof(bab_w1)); + bab_add_data(sitem, BAB_W1B_ADDR, bab_w1, sizeof(bab_w1)/2); + bab_add_data(sitem, BAB_W2_ADDR, bab_w2, sizeof(bab_w2)); + babinfo->chip_conf[i] ^= BAB_CFGD_VAL; + } + babinfo->old_fast[i] = babinfo->chip_fast[i]; + babinfo->old_conf[i] = babinfo->chip_conf[i]; + } else { + if (babinfo->old_fast[i] != babinfo->chip_fast[i]) { + bab_set_osc(babinfo, i); + bab_add_data(sitem, BAB_OSC_ADDR, babinfo->osc, sizeof(babinfo->osc)); + babinfo->old_fast[i] = babinfo->chip_fast[i]; + } + if (babinfo->old_conf[i] != babinfo->chip_conf[i]) { + if (BAB_ICLK_SET(babinfo->old_conf[i]) != + BAB_ICLK_SET(babinfo->chip_conf[i])) + bab_config_reg(sitem, BAB_ICLK_REG, + BAB_ICLK_BIT(babinfo->chip_conf[i])); + if (BAB_FAST_SET(babinfo->old_conf[i]) != + BAB_FAST_SET(babinfo->chip_conf[i])) + bab_config_reg(sitem, BAB_FAST_REG, + BAB_FAST_BIT(babinfo->chip_conf[i])); + if (BAB_DIV2_SET(babinfo->old_conf[i]) != + BAB_DIV2_SET(babinfo->chip_conf[i])) + bab_config_reg(sitem, BAB_DIV2_REG, + BAB_DIV2_BIT(babinfo->chip_conf[i])); + if (BAB_SLOW_SET(babinfo->old_conf[i]) != + BAB_SLOW_SET(babinfo->chip_conf[i])) + bab_config_reg(sitem, BAB_SLOW_REG, + BAB_SLOW_BIT(babinfo->chip_conf[i])); + if (BAB_OCLK_SET(babinfo->old_conf[i]) != + BAB_OCLK_SET(babinfo->chip_conf[i])) + bab_config_reg(sitem, BAB_OCLK_REG, + BAB_OCLK_BIT(babinfo->chip_conf[i])); + babinfo->old_conf[i] = babinfo->chip_conf[i]; + } + } + DATAS(sitem)->chip_off[i] = DATAS(sitem)->siz + 3; + chip_input = &(DATAW(DATAS(sitem)->witems[i])->chip_input); + + if (babinfo->chip_conf[i]) + bab_add_data(sitem, BAB_INP_ADDR, (uint8_t *)chip_input, sizeof(*chip_input)); + + chip_siz = DATAS(sitem)->siz - babinfo->chip_conf[i]; + if (chip_siz < BAB_CHIP_MIN) + BAB_ADD_NOOPs(sitem, BAB_CHIP_MIN - chip_siz); + } + BAB_ADD_ASYNC(sitem); + } + DATAS(sitem)->chip_off[i] = DATAS(sitem)->siz; + DATAS(sitem)->bank_off[bank] = DATAS(sitem)->siz; + + K_WLOCK(babinfo->spi_list); + k_add_head(babinfo->spi_list, sitem); + K_WUNLOCK(babinfo->spi_list); + + cgsem_post(&(babinfo->spi_work)); +} + +static bool bab_get(__maybe_unused struct cgpu_info *babcgpu, struct bab_info *babinfo, struct timeval *when) +{ + K_ITEM *item; + bool delayed; + int i; + + item = NULL; + delayed = false; + while (item == NULL) { + cgsem_mswait(&(babinfo->spi_reply), babinfo->reply_wait); + + K_WLOCK(babinfo->spi_sent); + item = k_unlink_tail(babinfo->spi_sent); + K_WUNLOCK(babinfo->spi_sent); + + if (!item) { + if (!delayed) { + applog(LOG_WARNING, "%s%d: Delay getting work reply ...", + babcgpu->drv->name, + babcgpu->device_id); + delayed = true; + babinfo->reply_waits++; + } + } + } + + for (i = 0; i < babinfo->chips; i++) { + if (babinfo->chip_conf[i] & 0x7f) { + memcpy((void *)&(babinfo->chip_results[i]), + (void *)(DATAS(item)->rbuf + DATAS(item)->chip_off[i]), + sizeof(babinfo->chip_results[0])); + } + } + + // work_start is also the time the results were read + memcpy(when, &(DATAS(item)->work_start), sizeof(*when)); + + K_WLOCK(babinfo->sfree_list); + k_add_head(babinfo->sfree_list, item); + K_WUNLOCK(babinfo->sfree_list); + + return true; +} + +void bab_detect_chips(struct cgpu_info *babcgpu, struct bab_info *babinfo, int bank, int first, int last) +{ + int i, reg, j; + K_ITEM *item; + + if (sizeof(struct bab_work_send) != sizeof(bab_test_data)) { + quithere(1, "struct bab_work_send (%d) and bab_test_data (%d)" + " must be the same size", + (int)sizeof(struct bab_work_send), + (int)sizeof(bab_test_data)); + } + + K_WLOCK(babinfo->sfree_list); + item = k_unlink_head_zero(babinfo->sfree_list); + K_WUNLOCK(babinfo->sfree_list); + BAB_ADD_BREAK(item); + for (i = first; i < last && i < BAB_MAXCHIPS; i++) { + bab_set_osc(babinfo, i); + bab_add_data(item, BAB_OSC_ADDR, babinfo->osc, sizeof(babinfo->osc)); + bab_config_reg(item, BAB_ICLK_REG, BAB_ICLK_BIT(babinfo->chip_conf[i])); + bab_config_reg(item, BAB_FAST_REG, BAB_FAST_BIT(babinfo->chip_conf[i])); + bab_config_reg(item, BAB_DIV2_REG, BAB_DIV2_BIT(babinfo->chip_conf[i])); + bab_config_reg(item, BAB_SLOW_REG, BAB_SLOW_BIT(babinfo->chip_conf[i])); + bab_config_reg(item, BAB_OCLK_REG, BAB_OCLK_BIT(babinfo->chip_conf[i])); + for (reg = BAB_REG_CLR_FROM; reg <= BAB_REG_CLR_TO; reg++) + bab_config_reg(item, reg, false); + bab_add_data(item, BAB_COUNT_ADDR, bab_counters, sizeof(bab_counters)); + bab_add_data(item, BAB_W1A_ADDR, bab_w1, sizeof(bab_w1)); + bab_add_data(item, BAB_W1B_ADDR, bab_w1, sizeof(bab_w1)/2); + bab_add_data(item, BAB_W2_ADDR, bab_w2, sizeof(bab_w2)); + DATAS(item)->chip_off[i] = DATAS(item)->siz + 3; + bab_add_data(item, BAB_INP_ADDR, bab_test_data, sizeof(bab_test_data)); + DATAS(item)->chip_off[i+1] = DATAS(item)->siz; + DATAS(item)->bank_off[bank] = DATAS(item)->siz; + babinfo->chips = i + 1; + bab_txrx(item, false); + DATAS(item)->siz = 0; + BAB_ADD_BREAK(item); + for (j = first; j <= i; j++) { + DATAS(item)->chip_off[j] = DATAS(item)->siz + 3; + BAB_ADD_ASYNC(item); + } + } + + memset(item->data, 0, babinfo->sfree_list->siz); + BAB_ADD_BREAK(item); + for (i = first; i < last && i < BAB_MAXCHIPS; i++) { + DATAS(item)->chip_off[i] = DATAS(item)->siz + 3; + bab_add_data(item, BAB_INP_ADDR, bab_test_data, sizeof(bab_test_data)); + BAB_ADD_ASYNC(item); + } + DATAS(item)->chip_off[i] = DATAS(item)->siz; + DATAS(item)->bank_off[bank] = DATAS(item)->siz; + babinfo->chips = i; + bab_txrx(item, true); + DATAS(item)->siz = 0; + babinfo->chips = first; + for (i = first; i < last && i < BAB_MAXCHIPS; i++) { + uint32_t tmp[DATA_UINTS-1]; + memcpy(tmp, DATAS(item)->rbuf + DATAS(item)->chip_off[i], sizeof(tmp)); + DATAS(item)->chip_off[i] = 0; + for (j = 0; j < BAB_REPLY_NONCES; j++) { + if (tmp[j] != 0xffffffff && tmp[j] != 0x00000000) { + babinfo->chip_bank[i] = bank; + babinfo->chips = i + 1; + break; + } + } + } + for (i = first ; i < babinfo->chips; i++) + babinfo->chip_bank[i] = bank; + + K_WLOCK(babinfo->sfree_list); + k_add_head(babinfo->sfree_list, item); + K_WUNLOCK(babinfo->sfree_list); +} + +static const char *bab_modules[] = { + "i2c-dev", + "i2c-bcm2708", + "spidev", + "spi-bcm2708", + NULL +}; + +static const char *bab_memory = "/dev/mem"; + +static int bab_memory_addr = 0x20200000; + +// TODO: add --bab-options for SPEED_HZ, tran.delay_usecs and an inter transfer delay (default 0) +static struct { + int request; + int value; +} bab_ioc[] = { + { SPI_IOC_RD_MODE, 0 }, + { SPI_IOC_WR_MODE, 0 }, + { SPI_IOC_RD_BITS_PER_WORD, 8 }, + { SPI_IOC_WR_BITS_PER_WORD, 8 }, + { SPI_IOC_RD_MAX_SPEED_HZ, 1000000 }, + { SPI_IOC_WR_MAX_SPEED_HZ, 1000000 }, + { -1, -1 } +}; + +static bool bab_init_gpio(struct cgpu_info *babcgpu, struct bab_info *babinfo, int bus, int chip) +{ + int i, err, memfd, data; + char buf[64]; + + bab_ioc[4].value = (int)(babinfo->speed_hz); + bab_ioc[5].value = (int)(babinfo->speed_hz); + + for (i = 0; bab_modules[i]; i++) { + snprintf(buf, sizeof(buf), "modprobe %s", bab_modules[i]); + err = system(buf); + if (err) { + applog(LOG_ERR, "%s failed to modprobe %s (%d) - you need to be root?", + babcgpu->drv->dname, + bab_modules[i], err); + goto bad_out; + } + } + + memfd = open(bab_memory, O_RDWR | O_SYNC); + if (memfd < 0) { + applog(LOG_ERR, "%s failed open %s (%d)", + babcgpu->drv->dname, + bab_memory, errno); + goto bad_out; + } + + babinfo->gpio = (volatile unsigned *)mmap(NULL, BAB_SPI_BUFSIZ, + PROT_READ | PROT_WRITE, + MAP_SHARED, memfd, + bab_memory_addr); + if (babinfo->gpio == MAP_FAILED) { + close(memfd); + applog(LOG_ERR, "%s failed mmap gpio (%d)", + babcgpu->drv->dname, + errno); + goto bad_out; + } + + close(memfd); + + snprintf(buf, sizeof(buf), "/dev/spidev%d.%d", bus, chip); + babinfo->spifd = open(buf, O_RDWR); + if (babinfo->spifd < 0) { + applog(LOG_ERR, "%s failed to open spidev (%d)", + babcgpu->drv->dname, + errno); + goto map_out; + } + + babcgpu->device_path = strdup(buf); + + for (i = 0; bab_ioc[i].value != -1; i++) { + data = bab_ioc[i].value; + err = ioctl(babinfo->spifd, bab_ioc[i].request, (void *)&data); + if (err < 0) { + applog(LOG_ERR, "%s failed ioctl (%d) (%d)", + babcgpu->drv->dname, + i, errno); + goto close_out; + } + } + + for (i = 0; i < BAB_MAXCHIPS; i++) + babinfo->chip_spis[i] = (int)((1000000.0 / (100.0 + 31.0 * (i + 1))) * 1000); + + return true; + +close_out: + close(babinfo->spifd); + babinfo->spifd = 0; + free(babcgpu->device_path); + babcgpu->device_path = NULL; +map_out: + munmap((void *)(babinfo->gpio), BAB_SPI_BUFSIZ); + babinfo->gpio = NULL; +bad_out: + return false; +} + +static void bab_init_chips(struct cgpu_info *babcgpu, struct bab_info *babinfo) +{ + int chip, chipoff, bank, chips, new_chips, boards, mis; + + applog(LOG_WARNING, "%s V1 first test for %d chips ...", + babcgpu->drv->dname, BAB_V1_CHIP_TEST); + + bab_detect_chips(babcgpu, babinfo, 0, 0, BAB_V1_CHIP_TEST); + if (babinfo->chips > 0) { + babinfo->version = 1; + babinfo->banks = 0; + if (babinfo->chips == BAB_V1_CHIP_TEST) { + applog(LOG_WARNING, "%s V1 test for %d more chips ...", + babcgpu->drv->dname, BAB_MAXCHIPS - BAB_V1_CHIP_TEST); + + bab_detect_chips(babcgpu, babinfo, 0, BAB_V1_CHIP_TEST, BAB_MAXCHIPS); + } + babinfo->chips_per_bank[BAB_V1_BANK] = babinfo->chips; + babinfo->bank_first_chip[BAB_V1_BANK] = 0; + babinfo->bank_last_chip[BAB_V1_BANK] = babinfo->chips - 1; + babinfo->boards = (int)((float)(babinfo->chips - 1) / BAB_BOARDCHIPS) + 1; + babinfo->reply_wait = BAB_REPLY_WAIT_mS * 2; + + if ((chip = (babinfo->chips_per_bank[BAB_V1_BANK] % BAB_BOARDCHIPS))) { + mis = BAB_BOARDCHIPS - chip; + babinfo->missing_chips_per_bank[BAB_V1_BANK] = mis; + applog(LOG_WARNING, "%s V1: missing %d chip%s", + babcgpu->drv->dname, mis, + (mis == 1) ? "" : "s"); + } + } else { + applog(LOG_WARNING, "%s no chips found with V1", babcgpu->drv->dname); + applog(LOG_WARNING, "%s V2 test %d banks %d chips ...", + babcgpu->drv->dname, BAB_MAXBANKS, BAB_MAXCHIPS); + + chips = 0; + babinfo->version = 2; + babinfo->banks = 0; + for (bank = 1; bank <= BAB_MAXBANKS; bank++) { + for (chipoff = 0; chipoff < BAB_BANKCHIPS; chipoff++) { + chip = babinfo->chips + chipoff; + babinfo->chip_spis[chip] = 625000; + } + bab_reset(bank, 64); + bab_detect_chips(babcgpu, babinfo, bank, babinfo->chips, babinfo->chips + BAB_BANKCHIPS); + new_chips = babinfo->chips - chips; + babinfo->chips_per_bank[bank] = new_chips; + if (new_chips > 0) { + babinfo->bank_first_chip[bank] = babinfo->chips - new_chips; + babinfo->bank_last_chip[bank] = babinfo->chips - 1; + } + chips = babinfo->chips; + if (new_chips == 0) + boards = 0; + else { + boards = (int)((float)(new_chips - 1) / BAB_BOARDCHIPS) + 1; + babinfo->banks++; + } + applog(LOG_WARNING, "%s V2 bank %d: %d chips %d board%s", + babcgpu->drv->dname, bank, new_chips, + boards, (boards == 1) ? "" : "s"); + babinfo->boards += boards; + + if ((chip = (babinfo->chips_per_bank[bank] % BAB_BOARDCHIPS))) { + mis = BAB_BOARDCHIPS - chip; + babinfo->missing_chips_per_bank[bank] = mis; + applog(LOG_WARNING, "%s V2: bank %d missing %d chip%s", + babcgpu->drv->dname, bank, + mis, (mis == 1) ? "" : "s"); + } + } + babinfo->reply_wait = BAB_REPLY_WAIT_mS * babinfo->banks; + bab_reset(0, 8); + } + + memcpy(babinfo->old_conf, babinfo->chip_conf, sizeof(babinfo->old_conf)); + memcpy(babinfo->old_fast, babinfo->chip_fast, sizeof(babinfo->old_fast)); +} + +static char *bab_options[] = { + "MaxSpeed", + "DefaultSpeed", + "MinSpeed", + "TuneUp", + "TuneDown", + "SPISpeed", + "SPIDelayuS", + "TransferDelayuS" +}; + +#define INVOP " Invalid Option " + +static void bab_get_options(struct cgpu_info *babcgpu, struct bab_info *babinfo) +{ + char *ptr, *colon; + int which, val; + double fval; + long lval; + + if (opt_bab_options == NULL) + return; + + which = 0; + ptr = opt_bab_options; + while (ptr && *ptr) { + colon = strchr(ptr, ':'); + if (colon) + *(colon++) = '\0'; + + switch (which) { + case 0: + if (*ptr && tolower(*ptr) != 'd') { + val = atoi(ptr); + if (!isdigit(*ptr) || val < BAB_ABSMINSPEED || val > BAB_MAXSPEED) { + quit(1, "%s"INVOP"%s '%s' must be %d <= %s <= %d", + babcgpu->drv->dname, + bab_options[which], + ptr, BAB_ABSMINSPEED, + bab_options[which], + BAB_MAXSPEED); + } + babinfo->max_speed = (uint8_t)val; + // Adjust def,min down if they are above max specified + if (babinfo->def_speed > babinfo->max_speed) + babinfo->def_speed = babinfo->max_speed; + if (babinfo->min_speed > babinfo->max_speed) + babinfo->min_speed = babinfo->max_speed; + } + break; + case 1: + if (*ptr && tolower(*ptr) != 'd') { + val = atoi(ptr); + if (!isdigit(*ptr) || val < BAB_ABSMINSPEED || val > babinfo->max_speed) { + quit(1, "%s"INVOP"%s '%s' must be %d <= %s <= %d", + babcgpu->drv->dname, + bab_options[which], + ptr, BAB_ABSMINSPEED, + bab_options[which], + babinfo->max_speed); + } + babinfo->def_speed = (uint8_t)val; + // Adjust min down if is is above def specified + if (babinfo->min_speed > babinfo->def_speed) + babinfo->min_speed = babinfo->def_speed; + } + break; + case 2: + if (*ptr && tolower(*ptr) != 'd') { + val = atoi(ptr); + if (!isdigit(*ptr) || val < BAB_ABSMINSPEED || val > babinfo->def_speed) { + quit(1, "%s"INVOP"%s '%s' must be %d <= %s <= %d", + babcgpu->drv->dname, + bab_options[which], + ptr, BAB_ABSMINSPEED, + bab_options[which], + babinfo->def_speed); + } + babinfo->min_speed = (uint8_t)val; + } + break; + case 3: + if (*ptr && tolower(*ptr) != 'd') { + fval = atof(ptr); + if (!isdigit(*ptr) || fval < 0.0 || fval > 100.0) { + quit(1, "%s"INVOP"%s '%s' must be 0.0 <= %s <= 100.0", + babcgpu->drv->dname, + bab_options[which], ptr, + bab_options[which]); + } + babinfo->tune_up = fval; + } + break; + case 4: + if (*ptr && tolower(*ptr) != 'd') { + fval = atof(ptr); + if (!isdigit(*ptr) || fval < 0.0 || fval > 100.0) { + quit(1, "%s"INVOP"%s '%s' must be %f <= %s <= 100.0", + babcgpu->drv->dname, + bab_options[which], + ptr, babinfo->tune_up, + bab_options[which]); + } + babinfo->tune_down = fval; + } + break; + case 5: + if (*ptr && tolower(*ptr) != 'd') { + val = atoi(ptr); + if (!isdigit(*ptr) || val < 10000 || val > 10000000) { + quit(1, "%s"INVOP"%s '%s' must be 10,000 <= %s <= 10,000,000", + babcgpu->drv->dname, + bab_options[which], ptr, + bab_options[which]); + } + babinfo->speed_hz = (uint32_t)val; + } + break; + case 6: + if (*ptr && tolower(*ptr) != 'd') { + val = atoi(ptr); + if (!isdigit(*ptr) || val < 0 || val > 65535) { + quit(1, "%s"INVOP"%s '%s' must be 0 <= %s <= 65535", + babcgpu->drv->dname, + bab_options[which], ptr, + bab_options[which]); + } + babinfo->delay_usecs = (uint16_t)val; + } + break; + case 7: + if (*ptr && tolower(*ptr) != 'd') { + lval = atol(ptr); + if (!isdigit(*ptr) || lval < 0) { + quit(1, "%s"INVOP"%s '%s' must be %s >= 0", + babcgpu->drv->dname, + bab_options[which], ptr, + bab_options[which]); + } + babinfo->trf_delay = (uint64_t)lval; + } + break; + default: + break; + } + ptr = colon; + which++; + } +} + +static void bab_detect(bool hotplug) +{ + struct cgpu_info *babcgpu = NULL; + struct bab_info *babinfo = NULL; + int i; + + if (hotplug) + return; + + babcgpu = calloc(1, sizeof(*babcgpu)); + if (unlikely(!babcgpu)) + quithere(1, "Failed to calloc babcgpu"); + + babcgpu->drv = &bab_drv; + babcgpu->deven = DEV_ENABLED; + babcgpu->threads = 1; + + babinfo = calloc(1, sizeof(*babinfo)); + if (unlikely(!babinfo)) + quithere(1, "Failed to calloc babinfo"); + babcgpu->device_data = (void *)babinfo; + + babinfo->max_speed = BAB_DEFMAXSPEED; + babinfo->def_speed = BAB_DEFSPEED; + babinfo->min_speed = BAB_ABSMINSPEED; + + babinfo->tune_up = BAB_TUNEUP; + babinfo->tune_down = BAB_TUNEDOWN; + + babinfo->speed_hz = BAB_SPI_SPEED; + babinfo->delay_usecs = BAB_DELAY_USECS; + babinfo->trf_delay = BAB_TRF_DELAY; + + bab_get_options(babcgpu, babinfo); + + for (i = 0; i < BAB_MAXCHIPS; i++) { + babinfo->chip_conf[i] = BAB_DEFCONF; + babinfo->chip_fast[i] = babinfo->def_speed; +#if UPDATE_HISTORY + babinfo->bad_fast[i] = babinfo->max_speed + 1; +#endif + } + + if (!bab_init_gpio(babcgpu, babinfo, BAB_SPI_BUS, BAB_SPI_CHIP)) + goto unalloc; + + babinfo->sfree_list = k_new_list("SPI I/O", sizeof(SITEM), + ALLOC_SITEMS, LIMIT_SITEMS, true); + babinfo->spi_list = k_new_store(babinfo->sfree_list); + babinfo->spi_sent = k_new_store(babinfo->sfree_list); + + for (i = 0; i <= BAB_MAXBANKS; i++) { + babinfo->bank_first_chip[i] = -1; + babinfo->bank_last_chip[i] = -1; + } + + bab_init_chips(babcgpu, babinfo); + + if (babinfo->boards) { + applog(LOG_WARNING, "%s found %d chips %d board%s", + babcgpu->drv->dname, babinfo->chips, + babinfo->boards, + (babinfo->boards == 1) ? "" : "s"); + } else { + applog(LOG_WARNING, "%s found %d chips", + babcgpu->drv->dname, babinfo->chips); + } + + if (babinfo->chips == 0) + goto cleanup; + + if (!add_cgpu(babcgpu)) + goto cleanup; + + cgsem_init(&(babinfo->scan_work)); + cgsem_init(&(babinfo->spi_work)); + cgsem_init(&(babinfo->spi_reply)); + cgsem_init(&(babinfo->process_reply)); + + mutex_init(&babinfo->did_lock); + mutex_init(&babinfo->nonce_lock); + + babinfo->rfree_list = k_new_list("Results", sizeof(RITEM), + ALLOC_RITEMS, LIMIT_RITEMS, true); + babinfo->res_list = k_new_store(babinfo->rfree_list); + + babinfo->wfree_list = k_new_list("Work", sizeof(WITEM), + ALLOC_WITEMS, LIMIT_WITEMS, true); + babinfo->available_work = k_new_store(babinfo->wfree_list); + for (i = 0; i < BAB_MAXCHIPS; i++) + babinfo->chip_work[i] = k_new_store(babinfo->wfree_list); + + babinfo->nfree_list = k_new_list("Nonce History", sizeof(WITEM), + ALLOC_NITEMS, LIMIT_NITEMS, true); + for (i = 0; i < BAB_MAXCHIPS; i++) { + babinfo->good_nonces[i] = k_new_store(babinfo->nfree_list); + babinfo->bad_nonces[i] = k_new_store(babinfo->nfree_list); + } + + // Exclude detection + cgtime(&(babcgpu->dev_start_tv)); + // Ignore detection tests + babinfo->last_did.tv_sec = 0; + + babinfo->initialised = true; + + return; + +cleanup: + close(babinfo->spifd); + munmap((void *)(babinfo->gpio), BAB_SPI_BUFSIZ); +unalloc: + free(babinfo); + free(babcgpu); +} + +static void bab_identify(__maybe_unused struct cgpu_info *babcgpu) +{ +} + +// thread to do spi txrx +static void *bab_spi(void *userdata) +{ + struct cgpu_info *babcgpu = (struct cgpu_info *)userdata; + struct bab_info *babinfo = (struct bab_info *)(babcgpu->device_data); + struct timeval start, stop, send, now; + K_ITEM *sitem, *witem; + double wait, delay; + int chip, band; + + applog(LOG_DEBUG, "%s%i: SPIing...", + babcgpu->drv->name, babcgpu->device_id); + + // Wait until we're ready + while (babcgpu->shutdown == false) { + if (babinfo->initialised) { + break; + } + cgsleep_ms(3); + } + + cgtime(&start); + while (babcgpu->shutdown == false) { + K_WLOCK(babinfo->spi_list); + sitem = k_unlink_tail(babinfo->spi_list); + K_WUNLOCK(babinfo->spi_list); + + if (!sitem) { + cgtime(&stop); + wait = us_tdiff(&stop, &start); + if (wait > BAB_LONG_uS) { + applog(LOG_WARNING, "%s%i: SPI waiting %fs ...", + babcgpu->drv->name, + babcgpu->device_id, + (float)wait / 1000000.0); + cgsem_mswait(&(babinfo->spi_work), BAB_LONG_WAIT_mS); + } else + cgsem_mswait(&(babinfo->spi_work), (int)((BAB_LONG_uS - wait) / 1000)); + continue; + } + + // TODO: need an LP/urgent flag to skip this possible cgsem_mswait() + // maybe zero last_sent_work.tv_sec ? + while (babinfo->last_sent_work.tv_sec) { + cgtime(&now); + delay = tdiff(&now, &(babinfo->last_sent_work)) * 1000.0; + if (delay < BAB_EXPECTED_WORK_DELAY_mS) + cgsem_mswait(&(babinfo->spi_work), BAB_EXPECTED_WORK_DELAY_mS - delay); + else + break; + } + + /* + * TODO: handle if an LP happened after bab_do_work() started + * i.e. we don't want to send the work + * Have an LP counter that at this point would show the work + * is stale - so don't send it + */ + cgtime(&send); + bab_txrx(sitem, false); + cgtime(&start); + + // The work isn't added to the chip until it has been sent + K_WLOCK(babinfo->wfree_list); + for (chip = 0; chip < babinfo->chips; chip++) { + witem = DATAS(sitem)->witems[chip]; + if (witem) { + memcpy(&(DATAW(witem)->work_start), &(DATAS(sitem)->work_start), + sizeof(DATAW(witem)->work_start)); + k_add_head(babinfo->chip_work[chip], witem); +#if UPDATE_HISTORY + babinfo->work_count[chip]++; +#endif + if (babinfo->first_work[chip].tv_sec == 0) + memcpy(&(babinfo->first_work[chip]), &send, sizeof(send)); + } + } + K_WUNLOCK(babinfo->wfree_list); + + K_WLOCK(babinfo->spi_sent); + k_add_head(babinfo->spi_sent, sitem); + K_WUNLOCK(babinfo->spi_sent); + + cgsem_post(&(babinfo->spi_reply)); + + // Store stats + if (babinfo->last_sent_work.tv_sec) { + delay = tdiff(&send, &(babinfo->last_sent_work)); + babinfo->delay_count++; + if (babinfo->delay_min == 0 || babinfo->delay_min > delay) + babinfo->delay_min = delay; + if (babinfo->delay_max < delay) + babinfo->delay_max = delay; + if (delay < BAB_DELAY_BASE) + band = 0; + else if (delay >= (BAB_DELAY_BASE+BAB_DELAY_STEP*(BAB_DELAY_BANDS+1))) + band = BAB_DELAY_BANDS+1; + else + band = (int)(((double)delay - BAB_DELAY_BASE) / BAB_DELAY_STEP) + 1; + babinfo->delay_bands[band]++; + } + memcpy(&(babinfo->last_sent_work), &send, sizeof(start)); + + delay = tdiff(&start, &send); + babinfo->send_count++; + babinfo->send_total += delay; + if (babinfo->send_min == 0 || babinfo->send_min > delay) + babinfo->send_min = delay; + if (babinfo->send_max < delay) + babinfo->send_max = delay; + + cgsem_mswait(&(babinfo->spi_work), BAB_STD_WAIT_mS); + } + + return NULL; +} + +static void bab_flush_work(struct cgpu_info *babcgpu) +{ + struct bab_info *babinfo = (struct bab_info *)(babcgpu->device_data); + + applog(LOG_DEBUG, "%s%i: flushing work", + babcgpu->drv->name, babcgpu->device_id); + + mutex_lock(&(babinfo->did_lock)); + babinfo->last_did.tv_sec = 0; + mutex_unlock(&(babinfo->did_lock)); + + cgsem_post(&(babinfo->scan_work)); +} + +#define DATA_MERKLE7 16 +#define DATA_NTIME 17 +#define DATA_BITS 18 +#define DATA_NONCE 19 + +#define WORK_MERKLE7 (16*4) +#define WORK_NTIME (17*4) +#define WORK_BITS (18*4) +#define WORK_NONCE (19*4) + +#if UPDATE_HISTORY +static void process_history(struct cgpu_info *babcgpu, int chip, struct timeval *when, bool good, struct timeval *now) +{ + struct bab_info *babinfo = (struct bab_info *)(babcgpu->device_data); + uint64_t good_nonces, bad_nonces; + uint8_t chip_fast; + double tune; + K_ITEM *item; + int i; + + K_WLOCK(babinfo->nfree_list); + item = k_unlink_head(babinfo->nfree_list); + memcpy(&(DATAN(item)->found), when, sizeof(*when)); + if (good) + k_add_head(babinfo->good_nonces[chip], item); + else + k_add_head(babinfo->bad_nonces[chip], item); + + // Remove all expired history + for (i = 0; i < babinfo->chips; i++) { + item = babinfo->good_nonces[i]->tail; + while (item) { + if (tdiff(now, &(DATAN(item)->found)) < HISTORY_TIME_S) + break; + + k_unlink_item(babinfo->good_nonces[i], item); + k_add_head(babinfo->nfree_list, item); + + item = babinfo->good_nonces[i]->tail; + } + + item = babinfo->bad_nonces[i]->tail; + while (item) { + if (tdiff(now, &(DATAN(item)->found)) < HISTORY_TIME_S) + break; + + k_unlink_item(babinfo->bad_nonces[i], item); + k_add_head(babinfo->nfree_list, item); + + item = babinfo->bad_nonces[i]->tail; + } + } + good_nonces = babinfo->good_nonces[chip]->count; + bad_nonces = babinfo->bad_nonces[chip]->count; + + K_WUNLOCK(babinfo->nfree_list); + + // Tuning ... + if (tdiff(now, &(babinfo->first_work[chip])) >= HISTORY_TIME_S && + tdiff(now, &(babinfo->last_tune[chip])) >= HISTORY_TIME_S && + (good_nonces + bad_nonces) > 0) { + + chip_fast = babinfo->chip_fast[chip]; + + /* + * If bad then step it down and remember the speed + * TODO: does a speed change reset the chip? Or is there a reset? + */ + if (good_nonces == 0) { + if (chip_fast > babinfo->min_speed) { + if (babinfo->bad_fast[chip] > chip_fast) + babinfo->bad_fast[chip] = chip_fast; + + babinfo->chip_fast[chip]--; + + applog(LOG_WARNING, "%s%d: Chip %d BAD - speed down from %d to %d", + babcgpu->drv->name, babcgpu->device_id, + chip, (int)chip_fast, (int)chip_fast - 1); + } else { + /* + * Permanently BAD since we're already at the minumum speed + * but only getting bad nonces + */ + if (babinfo->bad_msg[chip] == false) { + applog(LOG_WARNING, "%s%d: Chip %d BAD - at min speed %d", + babcgpu->drv->name, babcgpu->device_id, + chip, (int)chip_fast); + + babinfo->bad_msg[chip] = true; + } + } + goto tune_over; + } + + /* + * It 'was' permanently BAD but a good nonce came back! + */ + if (babinfo->bad_msg[chip]) { + applog(LOG_WARNING, "%s%d: Chip %d REVIVED - at speed %d", + babcgpu->drv->name, babcgpu->device_id, + chip, (int)chip_fast); + + babinfo->bad_msg[chip] = false; + } + + /* + * Since we have found 'some' nonces - + * make sure bad_fast is higher than current chip_fast + */ + if (babinfo->bad_fast[chip] <= chip_fast) + babinfo->bad_fast[chip] = chip_fast + 1; + + tune = (double)bad_nonces / (double)(good_nonces + bad_nonces) * 100.0; + + /* + * TODO: it appears some chips just get a % bad at low speeds + * so we should handle them by weighting the speed reduction vs + * the HW% gained from the reduction (i.e. GH/s) + * Maybe handle that when they hit min_speed, then do a gradual speed + * up verifying if it is really making GH/s worse or better + */ + + // Tune it down if error rate is too high (and it's above min) + if (tune >= babinfo->tune_down && chip_fast > babinfo->min_speed) { + babinfo->chip_fast[chip]--; + + applog(LOG_WARNING, "%s%d: Chip %d High errors %.2f%% - speed down %d to %d", + babcgpu->drv->name, babcgpu->device_id, + chip, tune, (int)chip_fast, (int)chip_fast - 1); + + goto tune_over; + } + + /* + * TODO: if we are at bad_fast-1 and tune_up + * and bad_fast was set more than some time limit ago + * then consider increasing bad_fast by 1? + */ + + // Tune it up if error rate is low enough + if (tune <= babinfo->tune_up && + chip_fast < babinfo->max_speed && + chip_fast < (babinfo->bad_fast[chip] - 1)) { + babinfo->chip_fast[chip]++; + + applog(LOG_WARNING, "%s%d: Chip %d Low errors %.2f%% - speed up %d to %d", + babcgpu->drv->name, babcgpu->device_id, + chip, tune, (int)chip_fast, (int)chip_fast + 1); + + goto tune_over; + } +tune_over: + cgtime(&(babinfo->last_tune[chip])); + } +} +#endif + +/* + * Find the matching work item by checking each nonce against + * work items for the nonces chip + */ +static K_ITEM *process_nonce(struct thr_info *thr, struct cgpu_info *babcgpu, K_ITEM *ritem, uint32_t raw_nonce, K_ITEM *newest_witem) +{ + struct bab_info *babinfo = (struct bab_info *)(babcgpu->device_data); + unsigned int links, proc_links, work_links, tests; + int try_sta, try_fin, offset; + K_ITEM *witem, *wtail; + struct timeval now; + bool not_first_reply; + uint32_t nonce; + int chip; + + chip = DATAR(ritem)->chip; + not_first_reply = DATAR(ritem)->not_first_reply; + + babinfo->chip_nonces[chip]++; + + /* + * We can grab the head of the chip work queue and then release + * the lock and follow it to the end and back, since the other + * thread will only add items above the head - it wont touch + * any of the prev/next pointers from the head to the end - + * except the head->prev pointer may get changed + */ + K_RLOCK(babinfo->chip_work[chip]); + witem = babinfo->chip_work[chip]->head; + K_RUNLOCK(babinfo->chip_work[chip]); + + if (!witem) { + applog(LOG_ERR, "%s%i: chip %d has no work, 1 nonce discarded!", + babcgpu->drv->name, babcgpu->device_id, chip); + babinfo->untested_nonces++; + return newest_witem; + } + + babinfo->tested_nonces++; + + if ((raw_nonce & 0xff) < 0x1c) { + // Will only be this offset + try_sta = BAB_OFF_0x1C_STA; + try_fin = BAB_OFF_0x1C_FIN; + } else { + // Will only be one of the other offsets + try_sta = BAB_OFF_OTHER_STA; + try_fin = BAB_OFF_OTHER_FIN; + } + + nonce = bab_decnonce(raw_nonce); + + cgtime(&now); + + tests = links = proc_links = work_links = 0; + wtail = witem; + while (wtail && wtail->next) { + work_links++; + wtail = wtail->next; + } + while (wtail) { + if (!(DATAW(wtail)->work)) { + applog(LOG_ERR, "%s%i: chip %d witem links %d has no work!", + babcgpu->drv->name, + babcgpu->device_id, + chip, links); + } else { + if (ms_tdiff(&now, &(DATAW(wtail)->work_start)) >= BAB_WORK_EXPIRE_mS) + proc_links--; + else { + for (offset = try_sta; offset <= try_fin; offset++) { + tests++; + if (test_nonce(DATAW(wtail)->work, nonce + bab_nonce_offsets[offset])) { + submit_tested_work(thr, DATAW(wtail)->work); + babinfo->nonce_offset_count[offset]++; + babinfo->chip_good[chip]++; + DATAW(wtail)->nonces++; + + mutex_lock(&(babinfo->nonce_lock)); + babinfo->new_nonces++; + mutex_unlock(&(babinfo->nonce_lock)); + + babinfo->ok_nonces++; + babinfo->total_tests += tests; + if (babinfo->max_tests_per_nonce < tests) + babinfo->max_tests_per_nonce = tests; + babinfo->total_links += links; + babinfo->total_proc_links += proc_links; + if (babinfo->max_links < links) + babinfo->max_links = links; + if (babinfo->max_proc_links < proc_links) + babinfo->max_proc_links = proc_links; + babinfo->total_work_links += work_links; + + babinfo->chip_cont_bad[chip] = 0; +#if UPDATE_HISTORY + process_history(babcgpu, chip, + &(DATAR(ritem)->when), + true, &now); +#endif + + if (newest_witem == NULL || + ms_tdiff(&(DATAW(wtail)->work_start), + &(DATAW(newest_witem)->work_start)) < 0) + return wtail; + + return newest_witem; + } + } + } + } + if (wtail == witem) + break; + wtail = wtail->prev; + links++; + proc_links++; + } + + if (not_first_reply) { + babinfo->chip_bad[chip]++; + inc_hw_errors(thr); + + babinfo->fail++; + babinfo->fail_total_tests += tests; + babinfo->fail_total_links += links; + babinfo->fail_total_work_links += work_links; + + babinfo->chip_cont_bad[chip]++; + if (babinfo->chip_max_bad[chip] < babinfo->chip_cont_bad[chip]) + babinfo->chip_max_bad[chip] = babinfo->chip_cont_bad[chip]; + + // Handle chips with only bad results + if (babinfo->disabled[chip] == false && + babinfo->chip_good[chip] == 0 && + babinfo->chip_bad[chip] >= BAB_BAD_COUNT && + tdiff(&now, &(babinfo->first_work[chip])) >= BAB_BAD_TO_MIN) { + if (babinfo->chip_fast[chip] > babinfo->min_speed) + babinfo->chip_fast[chip] = babinfo->min_speed; + else if (tdiff(&now, &(babinfo->first_work[chip])) > BAB_BAD_DEAD) { + babinfo->disabled[chip] = true; + babinfo->total_disabled++; + applog(LOG_ERR, "%s%i: chip %d disabled!", + babcgpu->drv->name, + babcgpu->device_id, + chip); + } + } +#if UPDATE_HISTORY + process_history(babcgpu, chip, &(DATAR(ritem)->when), false, &now); +#endif + } else { + babinfo->initial_ignored++; + babinfo->ign_total_tests += tests; + babinfo->ign_total_links += links; + babinfo->ign_total_work_links += work_links; + } + + return newest_witem; +} + +/* + * On completion discard any work items older than BAB_WORK_EXPIRE_mS + * and any work items of the chip older than the work of the newest nonce work item + */ +static void oknonces(struct thr_info *thr, struct cgpu_info *babcgpu, K_ITEM *ritem) +{ + uint32_t raw_nonce; + K_ITEM *witem; + int nonces; + + witem = NULL; + + for (nonces = 0; nonces < DATAR(ritem)->nonces; nonces++) { + raw_nonce = DATAR(ritem)->nonce[nonces]; + + witem = process_nonce(thr, babcgpu, ritem, raw_nonce, witem); + } + + cleanup_older(babcgpu, DATAR(ritem)->chip, witem); +} + +// Check at least every ... +#define BAB_RESULT_DELAY_mS 999 + +// Results checking thread +static void *bab_res(void *userdata) +{ + struct cgpu_info *babcgpu = (struct cgpu_info *)userdata; + struct bab_info *babinfo = (struct bab_info *)(babcgpu->device_data); + struct thr_info *thr = babcgpu->thr[0]; + K_ITEM *ritem; + + applog(LOG_DEBUG, "%s%i: Results...", + babcgpu->drv->name, babcgpu->device_id); + + // Wait until we're ready + while (babcgpu->shutdown == false) { + if (babinfo->initialised) { + break; + } + cgsleep_ms(3); + } + + ritem = NULL; + while (babcgpu->shutdown == false) { + K_WLOCK(babinfo->res_list); + if (ritem) { + // Release the old one + k_add_head(babinfo->rfree_list, ritem); + ritem = NULL; + } + // Check for a new one + ritem = k_unlink_tail(babinfo->res_list); + K_WUNLOCK(babinfo->res_list); + + if (!ritem) { + cgsem_mswait(&(babinfo->process_reply), BAB_RESULT_DELAY_mS); + continue; + } + + oknonces(thr, babcgpu, ritem); + } + + return NULL; +} + +/* + * 1.0s per nonce = 4.2GH/s + * 0.9s per nonce = 4.8GH/s + * On a slow machine, reducing this may resolve: + * BaB0: SPI waiting 1.2...s + */ +#define BAB_STD_WORK_DELAY_uS 900000 + +static bool bab_do_work(struct cgpu_info *babcgpu) +{ + struct bab_info *babinfo = (struct bab_info *)(babcgpu->device_data); + int work_items = 0; + K_ITEM *witem, *sitem, *ritem; + struct timeval when, now; + double delay; + int chip, rep, j, nonces, spie = 0, miso = 0; + uint32_t nonce, spichk; + bool res; + + cgtime(&now); + mutex_lock(&(babinfo->did_lock)); + delay = us_tdiff(&now, &(babinfo->last_did)); + mutex_unlock(&(babinfo->did_lock)); + if (delay < BAB_STD_WORK_DELAY_uS) + return false; + + K_WLOCK(babinfo->sfree_list); + sitem = k_unlink_head_zero(babinfo->sfree_list); + K_WUNLOCK(babinfo->sfree_list); + + for (chip = 0; chip < babinfo->chips; chip++) { + if (!(babinfo->disabled[chip])) { + // TODO: ignore stale work + K_WLOCK(babinfo->available_work); + witem = k_unlink_tail(babinfo->available_work); + K_WUNLOCK(babinfo->available_work); + if (!witem) { + applog(LOG_ERR, "%s%i: short work list (%d) %d expected %d - reset", + babcgpu->drv->name, babcgpu->device_id, + chip, work_items, + babinfo->chips - babinfo->total_disabled); + + // Put them back in the order they were taken + K_WLOCK(babinfo->available_work); + for (j = chip-1; j >= 0; j--) { + witem = DATAS(sitem)->witems[j]; + if (witem) + k_add_tail(babinfo->available_work, witem); + } + K_WUNLOCK(babinfo->available_work); + + K_WLOCK(babinfo->sfree_list); + k_add_head(babinfo->sfree_list, sitem); + K_WUNLOCK(babinfo->sfree_list); + + return false; + } + + /* + * TODO: do this when we get work except on LP? + * (not LP so we only do ms3steps for work required) + * Though that may more likely trigger the applog(short work list) above? + */ + if (DATAW(witem)->ci_setup == false) { + memcpy((void *)&(DATAW(witem)->chip_input.midstate[0]), + DATAW(witem)->work->midstate, + sizeof(DATAW(witem)->work->midstate)); + memcpy((void *)&(DATAW(witem)->chip_input.merkle7), + (void *)&(DATAW(witem)->work->data[WORK_MERKLE7]), + MERKLE_BYTES); + + bab_ms3steps((void *)&(DATAW(witem)->chip_input)); + + DATAW(witem)->ci_setup = true; + } + + DATAS(sitem)->witems[chip] = witem; + work_items++; + } + } + + // Send + bab_put(babinfo, sitem); + + // Receive + res = bab_get(babcgpu, babinfo, &when); + if (!res) { + applog(LOG_DEBUG, "%s%i: didn't get work reply ...", + babcgpu->drv->name, babcgpu->device_id); + return false; + } + + applog(LOG_DEBUG, "%s%i: Did get work reply ...", + babcgpu->drv->name, babcgpu->device_id); + + for (chip = 0; chip < babinfo->chips; chip++) { + if (!(babinfo->disabled[chip])) { + K_WLOCK(babinfo->rfree_list); + ritem = k_unlink_head(babinfo->rfree_list); + K_WUNLOCK(babinfo->rfree_list); + + DATAR(ritem)->chip = chip; + DATAR(ritem)->not_first_reply = babinfo->not_first_reply[chip]; + memcpy(&(DATAR(ritem)->when), &when, sizeof(when)); + + spichk = babinfo->chip_results[chip].spichk; + if (spichk != 0 && spichk != 0xffffffff) { + babinfo->chip_spie[chip]++; + spie++; + // Test the results anyway + } + + nonces = 0; + for (rep = 0; rep < BAB_REPLY_NONCES; rep++) { + nonce = babinfo->chip_results[chip].nonce[rep]; + if (nonce != babinfo->chip_prev[chip].nonce[rep]) { + if ((nonce & BAB_EVIL_MASK) == BAB_EVIL_NONCE) + babinfo->discarded_e0s++; + else + DATAR(ritem)->nonce[nonces++] = nonce; + } + } + + if (nonces == BAB_REPLY_NONCES) { + babinfo->chip_miso[chip]++; + miso++; + // Test the results anyway + } + + /* + * Send even with zero nonces + * so cleanup_older() is called for the chip + */ + DATAR(ritem)->nonces = nonces; + K_WLOCK(babinfo->res_list); + k_add_head(babinfo->res_list, ritem); + K_WUNLOCK(babinfo->res_list); + + cgsem_post(&(babinfo->process_reply)); + + babinfo->not_first_reply[chip] = true; + + memcpy((void *)(&(babinfo->chip_prev[chip])), + (void *)(&(babinfo->chip_results[chip])), + sizeof(struct bab_work_reply)); + } + + } + + applog(LOG_DEBUG, "Work: items:%d spie:%d miso:%d", work_items, spie, miso); + + return true; +} + +static bool bab_thread_prepare(struct thr_info *thr) +{ + struct cgpu_info *babcgpu = thr->cgpu; + struct bab_info *babinfo = (struct bab_info *)(babcgpu->device_data); + + if (thr_info_create(&(babinfo->spi_thr), NULL, bab_spi, (void *)babcgpu)) { + applog(LOG_ERR, "%s%i: SPI thread create failed", + babcgpu->drv->name, babcgpu->device_id); + return false; + } + pthread_detach(babinfo->spi_thr.pth); + + /* + * We require a seperate results checking thread since there is a lot + * of work done checking the results multiple times - thus we don't + * want that delay affecting sending/receiving work to/from the device + */ + if (thr_info_create(&(babinfo->res_thr), NULL, bab_res, (void *)babcgpu)) { + applog(LOG_ERR, "%s%i: Results thread create failed", + babcgpu->drv->name, babcgpu->device_id); + return false; + } + pthread_detach(babinfo->res_thr.pth); + + return true; +} + +static void bab_shutdown(struct thr_info *thr) +{ + struct cgpu_info *babcgpu = thr->cgpu; + struct bab_info *babinfo = (struct bab_info *)(babcgpu->device_data); + int i; + + applog(LOG_DEBUG, "%s%i: shutting down", + babcgpu->drv->name, babcgpu->device_id); + + for (i = 0; i < babinfo->chips; i++) +// TODO: bab_shutdown(babcgpu, babinfo, i); + ; + + babcgpu->shutdown = true; +} + +static bool bab_queue_full(struct cgpu_info *babcgpu) +{ + struct bab_info *babinfo = (struct bab_info *)(babcgpu->device_data); + int roll, roll_limit = BAB_MAX_ROLLTIME; + struct work *work, *usework; + K_ITEM *item; + int count, need; + bool ret, rolled; + + K_RLOCK(babinfo->available_work); + count = babinfo->available_work->count; + K_RUNLOCK(babinfo->available_work); + + if (count >= (babinfo->chips - babinfo->total_disabled)) + ret = true; + else { + need = (babinfo->chips - babinfo->total_disabled) - count; + work = get_queued(babcgpu); + if (work) { + if (roll_limit > work->drv_rolllimit) + roll_limit = work->drv_rolllimit; + roll = 0; + do { + if (roll == 0) { + usework = work; + babinfo->work_unrolled++; + rolled = false; + } else { + usework = copy_work_noffset(work, roll); + babinfo->work_rolled++; + rolled = true; + } + + K_WLOCK(babinfo->wfree_list); + item = k_unlink_head_zero(babinfo->wfree_list); + DATAW(item)->work = usework; + DATAW(item)->rolled = rolled; + k_add_head(babinfo->available_work, item); + K_WUNLOCK(babinfo->wfree_list); + } while (--need > 0 && ++roll <= roll_limit); + } else { + // Avoid a hard loop when we can't get work fast enough + cgsleep_us(42); + } + + if (need > 0) + ret = false; + else + ret = true; + } + + return ret; +} + +#define BAB_STD_DELAY_mS 100 + +/* + * TODO: allow this to run through more than once - the second+ + * time not sending any new work unless a flush occurs since: + * at the moment we have BAB_STD_WORK_mS latency added to earliest replies + */ +static int64_t bab_scanwork(__maybe_unused struct thr_info *thr) +{ + struct cgpu_info *babcgpu = thr->cgpu; + struct bab_info *babinfo = (struct bab_info *)(babcgpu->device_data); + int64_t hashcount = 0; + int count; + + bab_do_work(babcgpu); + + K_RLOCK(babinfo->available_work); + count = babinfo->available_work->count; + K_RUNLOCK(babinfo->available_work); + + if (count >= babinfo->chips) + cgsem_mswait(&(babinfo->scan_work), BAB_STD_DELAY_mS); + + mutex_lock(&(babinfo->nonce_lock)); + if (babinfo->new_nonces) { + hashcount += 0xffffffffull * babinfo->new_nonces; + babinfo->new_nonces = 0; + } + mutex_unlock(&(babinfo->nonce_lock)); + + return hashcount; +} + +#define CHIPS_PER_STAT 16 +#define FMT_RANGE "%d-%d" + +static struct api_data *bab_api_stats(struct cgpu_info *babcgpu) +{ + struct bab_info *babinfo = (struct bab_info *)(babcgpu->device_data); + uint64_t history_good[BAB_MAXCHIPS], history_bad[BAB_MAXCHIPS]; + uint64_t his_good_tot, his_bad_tot; + double history_elapsed[BAB_MAXCHIPS], diff; + bool elapsed_is_good[BAB_MAXCHIPS]; + int speeds[BAB_CHIP_SPEEDS]; + struct api_data *root = NULL; + char data[2048]; + char buf[32]; + int spi_work, chip_work, sp, chip, bank, chip_off, board, last_board; + int i, to, j, k; + bool bad; + struct timeval now; + double elapsed, ghs; + float ghs_sum, his_ghs_tot; + float tot, hw; + K_ITEM *item; + + if (babinfo->initialised == false) + return NULL; + + memset(&speeds, 0, sizeof(speeds)); + + root = api_add_int(root, "Version", &(babinfo->version), true); + root = api_add_int(root, "Chips", &(babinfo->chips), true); + root = api_add_int(root, "Boards", &(babinfo->boards), true); + root = api_add_int(root, "Banks", &(babinfo->banks), true); + + data[0] = '\0'; + for (i = 0; i <= BAB_MAXBANKS; i++) { + snprintf(buf, sizeof(buf), "%s%d", + (i == 0) ? "" : " ", + babinfo->chips_per_bank[i]); + strcat(data, buf); + } + root = api_add_string(root, "Chips Per Bank", data, true); + + data[0] = '\0'; + for (i = 0; i <= BAB_MAXBANKS; i++) { + snprintf(buf, sizeof(buf), "%s%d", + (i == 0) ? "" : " ", + babinfo->missing_chips_per_bank[i]); + strcat(data, buf); + } + root = api_add_string(root, "Missing Chips Per Bank", data, true); + + cgtime(&now); + elapsed = tdiff(&now, &(babcgpu->dev_start_tv)); + + root = api_add_elapsed(root, "Device Elapsed", &elapsed, true); + + root = api_add_string(root, "History Enabled", +#if UPDATE_HISTORY + "true", +#else + "false", +#endif + true); + + int chs = HISTORY_TIME_S; + root = api_add_int(root, "Chip History Limit", &chs, true); + + K_RLOCK(babinfo->nfree_list); + for (i = 0; i < babinfo->chips; i++) { + item = babinfo->good_nonces[i]->tail; + elapsed_is_good[i] = true; + if (!item) + history_elapsed[i] = 0; + else + history_elapsed[i] = tdiff(&now, &(DATAN(item)->found)); + + item = babinfo->bad_nonces[i]->tail; + if (item) { + diff = tdiff(&now, &(DATAN(item)->found)); + if (history_elapsed[i] < diff) { + history_elapsed[i] = diff; + elapsed_is_good[i] = false; + } + } + history_good[i] = babinfo->good_nonces[i]->count; + history_bad[i] = babinfo->bad_nonces[i]->count; + } + K_RUNLOCK(babinfo->nfree_list); + + his_ghs_tot = 0; + for (i = 0; i < babinfo->chips; i += CHIPS_PER_STAT) { + to = i + CHIPS_PER_STAT - 1; + if (to >= babinfo->chips) + to = babinfo->chips - 1; + + data[0] = '\0'; + for (j = i; j <= to; j++) { + snprintf(buf, sizeof(buf), + "%s%"PRIu64, + j == i ? "" : " ", + babinfo->chip_nonces[j]); + strcat(data, buf); + } + snprintf(buf, sizeof(buf), "Nonces "FMT_RANGE, i, to); + root = api_add_string(root, buf, data, true); + + data[0] = '\0'; + for (j = i; j <= to; j++) { + snprintf(buf, sizeof(buf), + "%s%"PRIu64, + j == i ? "" : " ", + babinfo->chip_good[j]); + strcat(data, buf); + } + snprintf(buf, sizeof(buf), "Good "FMT_RANGE, i, to); + root = api_add_string(root, buf, data, true); + + data[0] = '\0'; + for (j = i; j <= to; j++) { + snprintf(buf, sizeof(buf), + "%s%"PRIu64, + j == i ? "" : " ", + babinfo->chip_bad[j]); + strcat(data, buf); + } + snprintf(buf, sizeof(buf), "Bad "FMT_RANGE, i, to); + root = api_add_string(root, buf, data, true); + + data[0] = '\0'; + for (j = i; j <= to; j++) { + snprintf(buf, sizeof(buf), + "%s0x%02x", + j == i ? "" : " ", + (int)(babinfo->chip_conf[j])); + strcat(data, buf); + } + snprintf(buf, sizeof(buf), "Conf "FMT_RANGE, i, to); + root = api_add_string(root, buf, data, true); + + data[0] = '\0'; + for (j = i; j <= to; j++) { + snprintf(buf, sizeof(buf), + "%s%d", + j == i ? "" : " ", + (int)(babinfo->chip_fast[j])); + strcat(data, buf); + } + snprintf(buf, sizeof(buf), "Fast "FMT_RANGE, i, to); + root = api_add_string(root, buf, data, true); + + data[0] = '\0'; + for (j = i; j <= to; j++) { + snprintf(buf, sizeof(buf), + "%s%d", + j == i ? "" : " ", + (int)(babinfo->chip_spie[j])); + strcat(data, buf); + } + snprintf(buf, sizeof(buf), "Spie "FMT_RANGE, i, to); + root = api_add_string(root, buf, data, true); + + data[0] = '\0'; + for (j = i; j <= to; j++) { + snprintf(buf, sizeof(buf), + "%s%d", + j == i ? "" : " ", + (int)(babinfo->chip_miso[j])); + strcat(data, buf); + } + snprintf(buf, sizeof(buf), "Miso "FMT_RANGE, i, to); + root = api_add_string(root, buf, data, true); + + data[0] = '\0'; + for (j = i; j <= to; j++) { + tot = (float)(babinfo->chip_good[j] + babinfo->chip_bad[j]); + if (tot != 0) + hw = 100.0 * (float)(babinfo->chip_bad[j]) / tot; + else + hw = 0; + snprintf(buf, sizeof(buf), + "%s%.3f", + j == i ? "" : " ", hw); + strcat(data, buf); + } + snprintf(buf, sizeof(buf), "HW%% "FMT_RANGE, i, to); + root = api_add_string(root, buf, data, true); + + ghs_sum = 0; + data[0] = '\0'; + for (j = i; j <= to; j++) { + if (elapsed > 0) { + ghs = (double)(babinfo->chip_good[j]) * 0xffffffffull / + elapsed / 1000000000.0; + } else + ghs = 0; + + snprintf(buf, sizeof(buf), + "%s%.3f", + j == i ? "" : " ", ghs); + strcat(data, buf); + ghs_sum += (float)ghs; + } + snprintf(buf, sizeof(buf), "GHs "FMT_RANGE, i, to); + root = api_add_string(root, buf, data, true); + + snprintf(buf, sizeof(buf), "Sum GHs "FMT_RANGE, i, to); + root = api_add_avg(root, buf, &ghs_sum, true); + + data[0] = '\0'; + for (j = i; j <= to; j++) { + snprintf(buf, sizeof(buf), + "%s%"PRIu64, + j == i ? "" : " ", + babinfo->chip_cont_bad[j]); + strcat(data, buf); + } + snprintf(buf, sizeof(buf), "Cont-Bad "FMT_RANGE, i, to); + root = api_add_string(root, buf, data, true); + + data[0] = '\0'; + for (j = i; j <= to; j++) { + snprintf(buf, sizeof(buf), + "%s%"PRIu64, + j == i ? "" : " ", + babinfo->chip_max_bad[j]); + strcat(data, buf); + } + snprintf(buf, sizeof(buf), "Max-Bad "FMT_RANGE, i, to); + root = api_add_string(root, buf, data, true); + + data[0] = '\0'; + for (j = i; j <= to; j++) { + snprintf(buf, sizeof(buf), + "%s%"PRIu64, + j == i ? "" : " ", + history_good[j]); + strcat(data, buf); + } + snprintf(buf, sizeof(buf), "History Good "FMT_RANGE, i, to); + root = api_add_string(root, buf, data, true); + + data[0] = '\0'; + for (j = i; j <= to; j++) { + snprintf(buf, sizeof(buf), + "%s%"PRIu64, + j == i ? "" : " ", + history_bad[j]); + strcat(data, buf); + } + snprintf(buf, sizeof(buf), "History Bad "FMT_RANGE, i, to); + root = api_add_string(root, buf, data, true); + + data[0] = '\0'; + for (j = i; j <= to; j++) { + tot = (float)(history_good[j] + history_bad[j]); + if (tot != 0) + hw = 100.0 * (float)(history_bad[j]) / tot; + else + hw = 0; + snprintf(buf, sizeof(buf), + "%s%.3f", + j == i ? "" : " ", hw); + strcat(data, buf); + } + snprintf(buf, sizeof(buf), "History HW%% "FMT_RANGE, i, to); + root = api_add_string(root, buf, data, true); + + ghs_sum = 0; + data[0] = '\0'; + for (j = i; j <= to; j++) { + if (history_elapsed[j] > 0) { + double num = history_good[j]; + // exclude the first nonce? + if (elapsed_is_good[j]) + num--; + ghs = num * 0xffffffffull / + history_elapsed[j] / 1000000000.0; + } else + ghs = 0; + + snprintf(buf, sizeof(buf), + "%s%.3f", + j == i ? "" : " ", ghs); + strcat(data, buf); + + ghs_sum += (float)ghs; + + // Setup speed range data + for (sp = 0; sp < BAB_CHIP_SPEEDS - 1; sp++) { + if (ghs <= chip_speed_ranges[sp]) { + speeds[sp]++; + break; + } + } + if (sp >= (BAB_CHIP_SPEEDS - 1)) + speeds[BAB_CHIP_SPEEDS - 1]++; + } + snprintf(buf, sizeof(buf), "History GHs "FMT_RANGE, i, to); + root = api_add_string(root, buf, data, true); + + snprintf(buf, sizeof(buf), "Sum History GHs "FMT_RANGE, i, to); + root = api_add_avg(root, buf, &ghs_sum, true); + + his_ghs_tot += ghs_sum; + } + + root = api_add_avg(root, "Total History GHs", &his_ghs_tot, true); + + his_good_tot = his_bad_tot = 0; + for (i = 0; i < babinfo->chips; i++) { + his_good_tot += history_good[i]; + his_bad_tot += history_bad[i]; + } + if (his_good_tot + his_bad_tot) + tot = 100.0 * (float)his_bad_tot / (float)(his_good_tot + his_bad_tot); + else + tot = 0.0; + root = api_add_avg(root, "Total History HW%", &tot, true); + + for (sp = 0; sp < BAB_CHIP_SPEEDS; sp++) { + if (sp < (BAB_CHIP_SPEEDS - 1)) + ghs = chip_speed_ranges[sp]; + else + ghs = chip_speed_ranges[BAB_CHIP_SPEEDS - 2]; + + snprintf(buf, sizeof(buf), "History Speed %s%.1f %s", + (sp < (BAB_CHIP_SPEEDS - 1)) ? "" : ">", + ghs, chip_speed_names[sp]); + + root = api_add_int(root, buf, &(speeds[sp]), true); + } + + int len, str, siz = 1024; + char *tmp = malloc(siz); + if (!tmp) + quithere(1, "OOM tmp1"); + for (sp = 0; sp < 2; sp++) { + tmp[0] = '\0'; + len = 0; + for (i = 0; i < babinfo->chips; i++) { + if (history_elapsed[i] > 0) { + double num = history_good[i]; + // exclude the first nonce? + if (elapsed_is_good[i]) + num--; + ghs = num * 0xffffffffull / + history_elapsed[i] / 1000000000.0; + } else + ghs = 0; + + if ((sp == 0 || ghs > chip_speed_ranges[sp-1]) && + (ghs <= chip_speed_ranges[sp])) { + bank = babinfo->chip_bank[i]; + chip_off = i; + for (j = 0; j < babinfo->chip_bank[i]; j++) + chip_off -= babinfo->chips_per_bank[j]; + /* + * Bank/Board/Chip are all 1 based + * except V1 Bank = BAB_V1_BANK (0) + * If the bank has any missing chips then a "?" + * is placed after the board number + */ + snprintf(buf, sizeof(buf), "%s%d/%d%s/%d", + len ? " " : "", bank, + (int)(chip_off / BAB_BOARDCHIPS)+1, + babinfo->missing_chips_per_bank[bank] ? + "?" : "", + (chip_off % BAB_BOARDCHIPS)+1); + str = strlen(buf); + while ((len + str + 1) > siz) { + siz += 1024; + tmp = realloc(tmp, siz); + if (!tmp) + quithere(1, "OOM tmp2"); + } + strcpy(tmp + len, buf); + len += str; + } + } + snprintf(buf, sizeof(buf), "History %s", chip_speed_names[sp]); + + root = api_add_string(root, buf, len ? tmp : "None", true); + } + free(tmp); + tmp = NULL; + + switch (babinfo->version) { + case 1: + i = j = BAB_V1_BANK; + break; + case 2: + i = 1; + j = BAB_MAXBANKS; + break; + } + data[0] = '\0'; + for (bank = i; bank <= j; bank++) { + if (babinfo->bank_first_chip[bank] >= 0) { + chip = babinfo->bank_first_chip[bank]; + to = babinfo->bank_last_chip[bank]; + for (; chip <= to; chip += BAB_BOARDCHIPS) { + bad = true; + for (k = chip; (k <= to) && (k < (chip+BAB_BOARDCHIPS)); k++) { + if (history_elapsed[k] > 0) { + double num = history_good[k]; + // exclude the first nonce? + if (elapsed_is_good[k]) + num--; + ghs = num * 0xffffffffull / + history_elapsed[k] / 1000000000.0; + } else + ghs = 0; + + if (ghs > 0.0) { + bad = false; + break; + } + } + if (bad) { + board = (int)((float)(chip - babinfo->bank_first_chip[bank]) / + BAB_BOARDCHIPS) + 1; + snprintf(buf, sizeof(buf), + "%s%d/%d%s", + data[0] ? " " : "", + bank, board, + babinfo->missing_chips_per_bank[bank] ? + "?" : ""); + strcat(data, buf); + } + } + } + } + root = api_add_string(root, "History Bad Boards", data[0] ? data : "None", true); + + data[0] = '\0'; + for (bank = i; bank <= j; bank++) { + if (babinfo->bank_first_chip[bank] >= 0) { + to = babinfo->bank_first_chip[bank]; + chip = babinfo->bank_last_chip[bank]; + for (; chip >= to; chip--) { + bad = true; + if (history_elapsed[chip] > 0) { + double num = history_good[chip]; + // exclude the first nonce? + if (elapsed_is_good[chip]) + num--; + ghs = num * 0xffffffffull / + history_elapsed[chip] / 1000000000.0; + } else + ghs = 0; + + if (ghs > 0.0) + break; + } + /* + * The output here is: a/b+c/d + * a/b is the SPI/board that starts the Bad Chain + * c is the number of boards after a + * d is the total number of chips in the Bad Chain + * A Bad Chain is a continous set of bad chips that + * finish at the end of an SPI chain of boards + * This might be caused by the first board, or the cables attached + * to the first board, in the Bad Chain i.e. a/b + * If c is zero, it's just the last board, so it's the same as any + * other board having bad chips + */ + if (chip < babinfo->bank_last_chip[bank]) { + board = (int)((float)(chip - babinfo->bank_first_chip[bank]) / + BAB_BOARDCHIPS) + 1; + last_board = (int)((float)(babinfo->bank_last_chip[bank] - + babinfo->bank_first_chip[bank]) / + BAB_BOARDCHIPS) + 1; + snprintf(buf, sizeof(buf), + "%s%d/%d%s+%d/%d", + data[0] ? " " : "", + bank, board, + babinfo->missing_chips_per_bank[bank] ? + "?" : "", + last_board - board, + babinfo->bank_last_chip[bank] - chip); + strcat(data, buf); + } + } + } + root = api_add_string(root, "History Bad Chains", data[0] ? data : "None", true); + + root = api_add_int(root, "Disabled Chips", &(babinfo->total_disabled), true); + + for (i = 0; i < BAB_NONCE_OFFSETS; i++) { + snprintf(buf, sizeof(buf), "Nonce Offset 0x%08x", bab_nonce_offsets[i]); + root = api_add_uint64(root, buf, &(babinfo->nonce_offset_count[i]), true); + } + + root = api_add_uint64(root, "Discarded E0s", &(babinfo->discarded_e0s), true); + root = api_add_uint64(root, "Tested", &(babinfo->tested_nonces), true); + root = api_add_uint64(root, "OK", &(babinfo->ok_nonces), true); + root = api_add_uint64(root, "Total Tests", &(babinfo->total_tests), true); + root = api_add_uint64(root, "Max Tests", &(babinfo->max_tests_per_nonce), true); + float avg = babinfo->ok_nonces ? (float)(babinfo->total_tests) / + (float)(babinfo->ok_nonces) : 0; + root = api_add_avg(root, "Avg Tests", &avg, true); + root = api_add_uint64(root, "Untested", &(babinfo->untested_nonces), true); + + root = api_add_uint64(root, "Work Links", &(babinfo->total_links), true); + root = api_add_uint64(root, "Work Processed Links", &(babinfo->total_proc_links), true); + root = api_add_uint64(root, "Max Links", &(babinfo->max_links), true); + root = api_add_uint64(root, "Max Processed Links", &(babinfo->max_proc_links), true); + root = api_add_uint64(root, "Total Work Links", &(babinfo->total_work_links), true); + avg = babinfo->ok_nonces ? (float)(babinfo->total_links) / + (float)(babinfo->ok_nonces) : 0; + root = api_add_avg(root, "Avg Links", &avg, true); + avg = babinfo->ok_nonces ? (float)(babinfo->total_proc_links) / + (float)(babinfo->ok_nonces) : 0; + root = api_add_avg(root, "Avg Proc Links", &avg, true); + avg = babinfo->ok_nonces ? (float)(babinfo->total_work_links) / + (float)(babinfo->ok_nonces) : 0; + root = api_add_avg(root, "Avg Work Links", &avg, true); + + root = api_add_uint64(root, "Fail", &(babinfo->fail), true); + root = api_add_uint64(root, "Fail Total Tests", &(babinfo->fail_total_tests), true); + avg = babinfo->fail ? (float)(babinfo->fail_total_tests) / + (float)(babinfo->fail) : 0; + root = api_add_avg(root, "Fail Avg Tests", &avg, true); + root = api_add_uint64(root, "Fail Work Links", &(babinfo->fail_total_links), true); + root = api_add_uint64(root, "Fail Total Work Links", &(babinfo->fail_total_work_links), true); + + root = api_add_uint32(root, "Initial Ignored", &(babinfo->initial_ignored), true); + root = api_add_uint64(root, "Ign Total Tests", &(babinfo->ign_total_tests), true); + root = api_add_uint64(root, "Ign Work Links", &(babinfo->ign_total_links), true); + root = api_add_uint64(root, "Ign Total Work Links", &(babinfo->ign_total_work_links), true); + + chip_work = 0; + for (i = 0; i < babinfo->chips; i++) + chip_work += babinfo->chip_work[i]->count; + spi_work = babinfo->spi_list->count * babinfo->chips; + + root = api_add_int(root, "WFree Total", &(babinfo->wfree_list->total), true); + root = api_add_int(root, "WFree Count", &(babinfo->wfree_list->count), true); + root = api_add_int(root, "Available Work", &(babinfo->available_work->count), true); + root = api_add_int(root, "SPI Work", &spi_work, true); + root = api_add_int(root, "Chip Work", &chip_work, true); + + root = api_add_int(root, "SFree Total", &(babinfo->sfree_list->total), true); + root = api_add_int(root, "SFree Count", &(babinfo->sfree_list->count), true); + root = api_add_int(root, "SPI Waiting", &(babinfo->spi_list->count), true); + root = api_add_int(root, "SPI Sent", &(babinfo->spi_sent->count), true); + + root = api_add_int(root, "RFree Total", &(babinfo->rfree_list->total), true); + root = api_add_int(root, "RFree Count", &(babinfo->rfree_list->count), true); + root = api_add_int(root, "Result Count", &(babinfo->res_list->count), true); + + int used = babinfo->nfree_list->total - babinfo->nfree_list->count; + root = api_add_int(root, "NFree Total", &(babinfo->nfree_list->total), true); + root = api_add_int(root, "NFree Used", &used, true); + + root = api_add_uint64(root, "Delay Count", &(babinfo->delay_count), true); + root = api_add_double(root, "Delay Min", &(babinfo->delay_min), true); + root = api_add_double(root, "Delay Max", &(babinfo->delay_max), true); + + data[0] = '\0'; + for (i = 0; i <= BAB_DELAY_BANDS; i++) { + snprintf(buf, sizeof(buf), + "%s<%.1f=%"PRIu64, + i == 0 ? "" : " ", + BAB_DELAY_BASE+(BAB_DELAY_STEP*i), + babinfo->delay_bands[i]); + strcat(data, buf); + } + snprintf(buf, sizeof(buf), + " >=%.1f=%"PRIu64, + BAB_DELAY_BASE+BAB_DELAY_STEP*(BAB_DELAY_BANDS+1), + babinfo->delay_bands[BAB_DELAY_BANDS+1]); + strcat(data, buf); + root = api_add_string(root, "Delay Bands", data, true); + + root = api_add_uint64(root, "Send Count", &(babinfo->send_count), true); + root = api_add_double(root, "Send Total", &(babinfo->send_total), true); + avg = babinfo->send_count ? (float)(babinfo->send_total) / + (float)(babinfo->send_count) : 0; + root = api_add_avg(root, "Send Avg", &avg, true); + root = api_add_double(root, "Send Min", &(babinfo->send_min), true); + root = api_add_double(root, "Send Max", &(babinfo->send_max), true); + + root = api_add_int(root, "Reply Wait", &(babinfo->reply_wait), true); + root = api_add_uint64(root, "Reply Waits", &(babinfo->reply_waits), true); + + root = api_add_uint64(root, "Work Unrolled", &(babinfo->work_unrolled), true); + root = api_add_uint64(root, "Work Rolled", &(babinfo->work_rolled), true); + + i = (int)(babinfo->max_speed); + root = api_add_int(root, bab_options[0], &i, true); + i = (int)(babinfo->def_speed); + root = api_add_int(root, bab_options[1], &i, true); + i = (int)(babinfo->min_speed); + root = api_add_int(root, bab_options[2], &i, true); + root = api_add_double(root, bab_options[3], &(babinfo->tune_up), true); + root = api_add_double(root, bab_options[4], &(babinfo->tune_down), true); + i = (int)(babinfo->speed_hz); + root = api_add_int(root, bab_options[5], &i, true); + i = (int)(babinfo->delay_usecs); + root = api_add_int(root, bab_options[6], &i, true); + root = api_add_uint64(root, bab_options[7], &(babinfo->trf_delay), true); + + return root; +} + +static void bab_get_statline_before(char *buf, size_t bufsiz, struct cgpu_info *babcgpu) +{ + struct bab_info *babinfo = (struct bab_info *)(babcgpu->device_data); +#if UPDATE_HISTORY + struct timeval now; + double elapsed; + int i, bad = 0; + + cgtime(&now); + elapsed = tdiff(&now, &(babcgpu->dev_start_tv)); + + // At least get 15s of nonces before saying anything is bad + if (elapsed > 15.0) { + K_RLOCK(babinfo->nfree_list); + for (i = 0; i < babinfo->chips; i++) { + if (babinfo->good_nonces[i]->count == 0 && + babinfo->bad_nonces[i]->count > 1) + bad++; + } + K_RUNLOCK(babinfo->nfree_list); + } + + tailsprintf(buf, bufsiz, "%d.%02d.%03d B:%03d D:%03d", + babinfo->banks, + babinfo->boards, + babinfo->chips, + bad, + babinfo->total_disabled); +#else + tailsprintf(buf, bufsiz, "%d.%02d.%03d D:%03d", + babinfo->banks, + babinfo->boards, + babinfo->chips, + babinfo->total_disabled); +#endif +} +#endif + +struct device_drv bab_drv = { + .drv_id = DRIVER_bab, + .dname = "BlackArrowBitFuryGPIO", + .name = "BaB", + .drv_detect = bab_detect, +#ifdef LINUX + .get_api_stats = bab_api_stats, + .get_statline_before = bab_get_statline_before, + .identify_device = bab_identify, + .thread_prepare = bab_thread_prepare, + .hash_work = hash_queued_work, + .scanwork = bab_scanwork, + .queue_full = bab_queue_full, + .flush_work = bab_flush_work, + .thread_shutdown = bab_shutdown +#endif +}; diff --git a/driver-bflsc.c b/driver-bflsc.c index 570a65ad6a..f14538e752 100644 --- a/driver-bflsc.c +++ b/driver-bflsc.c @@ -1,6 +1,6 @@ /* * Copyright 2013 Andrew Smith - * Copyright 2013 Con Kolivas + * Copyright 2013-2014 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 @@ -8,6 +8,8 @@ * any later version. See COPYING for more details. */ +#include "config.h" + #include #include #include @@ -18,8 +20,6 @@ #include #include -#include "config.h" - #ifdef WIN32 #include #endif @@ -27,14 +27,13 @@ #include "compat.h" #include "miner.h" #include "usbutils.h" +#include "uthash.h" #include "driver-bflsc.h" int opt_bflsc_overheat = BFLSC_TEMP_OVERHEAT; static const char *blank = ""; -struct device_drv bflsc_drv; - static enum driver_version drv_ver(struct cgpu_info *bflsc, const char *ver) { char *tmp; @@ -52,7 +51,7 @@ static enum driver_version drv_ver(struct cgpu_info *bflsc, const char *ver) return BFLSC_DRV2; tmp = str_text((char *)ver); - applog(LOG_WARNING, "%s detect (%s) Warning unknown firmware '%s' using Ver2", + applog(LOG_INFO, "%s detect (%s) Warning unknown firmware '%s' using Ver2", bflsc->drv->dname, bflsc->device_path, tmp); free(tmp); return BFLSC_DRV2; @@ -208,9 +207,10 @@ static bool isokerr(int err, char *buf, int amount) if (err < 0 || amount < (int)BFLSC_OK_LEN) return false; else { - if (strstr(buf, BFLSC_ANERR)) + if (strstr(buf, BFLSC_ANERR)) { + applog(LOG_INFO, "BFLSC not ok err: %s", buf); return false; - else + } else return true; } } @@ -452,7 +452,7 @@ static bool bflsc_qres(struct cgpu_info *bflsc, char *buf, size_t bufsiz, int de static void __bflsc_initialise(struct cgpu_info *bflsc) { - int err; + int err, interface; // TODO: does x-link bypass the other device FTDI? (I think it does) // So no initialisation required except for the master device? @@ -460,9 +460,10 @@ static void __bflsc_initialise(struct cgpu_info *bflsc) if (bflsc->usbinfo.nodev) return; + interface = usb_interface(bflsc); // Reset err = usb_transfer(bflsc, FTDI_TYPE_OUT, FTDI_REQUEST_RESET, - FTDI_VALUE_RESET, bflsc->usbdev->found->interface, C_RESET); + FTDI_VALUE_RESET, interface, C_RESET); applog(LOG_DEBUG, "%s%i: reset got err %d", bflsc->drv->name, bflsc->device_id, err); @@ -477,7 +478,7 @@ static void __bflsc_initialise(struct cgpu_info *bflsc) // Set data control err = usb_transfer(bflsc, FTDI_TYPE_OUT, FTDI_REQUEST_DATA, - FTDI_VALUE_DATA_BAS, bflsc->usbdev->found->interface, C_SETDATA); + FTDI_VALUE_DATA_BAS, interface, C_SETDATA); applog(LOG_DEBUG, "%s%i: setdata got err %d", bflsc->drv->name, bflsc->device_id, err); @@ -487,7 +488,7 @@ static void __bflsc_initialise(struct cgpu_info *bflsc) // Set the baud err = usb_transfer(bflsc, FTDI_TYPE_OUT, FTDI_REQUEST_BAUD, FTDI_VALUE_BAUD_BAS, - (FTDI_INDEX_BAUD_BAS & 0xff00) | bflsc->usbdev->found->interface, + (FTDI_INDEX_BAUD_BAS & 0xff00) | interface, C_SETBAUD); applog(LOG_DEBUG, "%s%i: setbaud got err %d", @@ -498,7 +499,7 @@ static void __bflsc_initialise(struct cgpu_info *bflsc) // Set Flow Control err = usb_transfer(bflsc, FTDI_TYPE_OUT, FTDI_REQUEST_FLOW, - FTDI_VALUE_FLOW, bflsc->usbdev->found->interface, C_SETFLOW); + FTDI_VALUE_FLOW, interface, C_SETFLOW); applog(LOG_DEBUG, "%s%i: setflowctrl got err %d", bflsc->drv->name, bflsc->device_id, err); @@ -508,7 +509,7 @@ static void __bflsc_initialise(struct cgpu_info *bflsc) // Set Modem Control err = usb_transfer(bflsc, FTDI_TYPE_OUT, FTDI_REQUEST_MODEM, - FTDI_VALUE_MODEM, bflsc->usbdev->found->interface, C_SETMODEM); + FTDI_VALUE_MODEM, interface, C_SETMODEM); applog(LOG_DEBUG, "%s%i: setmodemctrl got err %d", bflsc->drv->name, bflsc->device_id, err); @@ -518,7 +519,7 @@ static void __bflsc_initialise(struct cgpu_info *bflsc) // Clear any sent data err = usb_transfer(bflsc, FTDI_TYPE_OUT, FTDI_REQUEST_RESET, - FTDI_VALUE_PURGE_TX, bflsc->usbdev->found->interface, C_PURGETX); + FTDI_VALUE_PURGE_TX, interface, C_PURGETX); applog(LOG_DEBUG, "%s%i: purgetx got err %d", bflsc->drv->name, bflsc->device_id, err); @@ -528,7 +529,7 @@ static void __bflsc_initialise(struct cgpu_info *bflsc) // Clear any received data err = usb_transfer(bflsc, FTDI_TYPE_OUT, FTDI_REQUEST_RESET, - FTDI_VALUE_PURGE_RX, bflsc->usbdev->found->interface, C_PURGERX); + FTDI_VALUE_PURGE_RX, interface, C_PURGERX); applog(LOG_DEBUG, "%s%i: purgerx got err %d", bflsc->drv->name, bflsc->device_id, err); @@ -630,7 +631,7 @@ static bool getinfo(struct cgpu_info *bflsc, int dev) sc_dev.firmware = strdup(fields[0]); sc_info->driver_version = drv_ver(bflsc, sc_dev.firmware); } - else if (strstr(firstname, BFLSC_DI_ENGINES)) { + else if (Strcasestr(firstname, BFLSC_DI_ENGINES)) { sc_dev.engines = atoi(fields[0]); if (sc_dev.engines < 1) { tmp = str_text(items[i]); @@ -646,18 +647,25 @@ static bool getinfo(struct cgpu_info *bflsc, int dev) else if (strstr(firstname, BFLSC_DI_XLINKPRESENT)) sc_dev.xlink_present = strdup(fields[0]); else if (strstr(firstname, BFLSC_DI_DEVICESINCHAIN)) { - sc_info->sc_count = atoi(fields[0]); + if (fields[0][0] == '0' || + (fields[0][0] == ' ' && fields[0][1] == '0')) + sc_info->sc_count = 1; + else + sc_info->sc_count = atoi(fields[0]); if (sc_info->sc_count < 1 || sc_info->sc_count > 30) { tmp = str_text(items[i]); applogsiz(LOG_WARNING, BFLSC_APPLOGSIZ, - "%s detect (%s) invalid s-link count: '%s'", + "%s detect (%s) invalid x-link count: '%s'", bflsc->drv->dname, bflsc->device_path, tmp); free(tmp); goto mata; } + } else if (strstr(firstname, BFLSC_DI_CHIPS)) sc_dev.chips = strdup(fields[0]); - } + else if (strstr(firstname, BFLSC28_DI_ASICS)) + sc_dev.chips = strdup(fields[0]); + freebreakdown(&count, &firstname, &fields); } @@ -684,7 +692,9 @@ static bool getinfo(struct cgpu_info *bflsc, int dev) return ok; } -static bool bflsc_detect_one(struct libusb_device *dev, struct usb_find_devices *found) +static bool bflsc28_queue_full(struct cgpu_info *bflsc); + +static struct cgpu_info *bflsc_detect_one(struct libusb_device *dev, struct usb_find_devices *found) { struct bflsc_info *sc_info = NULL; char buf[BFLSC_BUFSIZ+1]; @@ -755,7 +765,7 @@ static bool bflsc_detect_one(struct libusb_device *dev, struct usb_find_devices } buf[amount] = '\0'; - if (unlikely(!strstr(buf, BFLSC_BFLSC))) { + if (unlikely(!strstr(buf, BFLSC_BFLSC) && !strstr(buf, BFLSC_BFLSC28))) { applog(LOG_DEBUG, "%s detect (%s) found an FPGA '%s' ignoring", bflsc->drv->dname, bflsc->device_path, buf); goto unshin; @@ -814,6 +824,19 @@ static bool bflsc_detect_one(struct libusb_device *dev, struct usb_find_devices break; } + // Set parallelization based on the getinfo() response if it is present + if (sc_info->sc_devs[0].chips && strlen(sc_info->sc_devs[0].chips)) { + if (strstr(sc_info->sc_devs[0].chips, BFLSC_DI_CHIPS_PARALLEL)) { + sc_info->que_noncecount = QUE_NONCECOUNT_V2; + sc_info->que_fld_min = QUE_FLD_MIN_V2; + sc_info->que_fld_max = QUE_FLD_MAX_V2; + } else { + sc_info->que_noncecount = QUE_NONCECOUNT_V1; + sc_info->que_fld_min = QUE_FLD_MIN_V1; + sc_info->que_fld_max = QUE_FLD_MAX_V1; + } + } + sc_info->scan_sleep_time = BAS_SCAN_TIME; sc_info->results_sleep_time = BFLSC_RES_TIME; sc_info->default_ms_work = BAS_WORK_TIME; @@ -845,6 +868,14 @@ static bool bflsc_detect_one(struct libusb_device *dev, struct usb_find_devices } } + sc_info->ident = usb_ident(bflsc); + if (sc_info->ident == IDENT_BMA) { + bflsc->drv->queue_full = &bflsc28_queue_full; + sc_info->scan_sleep_time = BMA_SCAN_TIME; + sc_info->default_ms_work = BMA_WORK_TIME; + sc_info->results_sleep_time = BMA_RES_TIME; + } + if (latency != bflsc->usbdev->found->latency) { bflsc->usbdev->found->latency = latency; usb_ftdi_set_latency(bflsc); @@ -871,9 +902,7 @@ static bool bflsc_detect_one(struct libusb_device *dev, struct usb_find_devices mutex_init(&bflsc->device_mutex); rwlock_init(&sc_info->stat_lock); - usb_buffer_enable(bflsc); - - return true; + return bflsc; unshin: @@ -891,10 +920,10 @@ static bool bflsc_detect_one(struct libusb_device *dev, struct usb_find_devices bflsc = usb_free_cgpu(bflsc); - return false; + return NULL; } -static void bflsc_detect(void) +static void bflsc_detect(bool __maybe_unused hotplug) { usb_detect(&bflsc_drv, bflsc_detect_one); } @@ -903,7 +932,7 @@ static void get_bflsc_statline_before(char *buf, size_t bufsiz, struct cgpu_info { struct bflsc_info *sc_info = (struct bflsc_info *)(bflsc->device_data); float temp = 0; - float vcc1 = 0; + float vcc2 = 0; int i; rd_lock(&(sc_info->stat_lock)); @@ -912,12 +941,12 @@ static void get_bflsc_statline_before(char *buf, size_t bufsiz, struct cgpu_info temp = sc_info->sc_devs[i].temp1; if (sc_info->sc_devs[i].temp2 > temp) temp = sc_info->sc_devs[i].temp2; - if (sc_info->sc_devs[i].vcc1 > vcc1) - vcc1 = sc_info->sc_devs[i].vcc1; + if (sc_info->sc_devs[i].vcc2 > vcc2) + vcc2 = sc_info->sc_devs[i].vcc2; } rd_unlock(&(sc_info->stat_lock)); - tailsprintf(buf, bufsiz, " max%3.0fC %4.2fV | ", temp, vcc1); + tailsprintf(buf, bufsiz, "max%3.0fC %4.2fV", temp, vcc2); } static void flush_one_dev(struct cgpu_info *bflsc, int dev) @@ -931,7 +960,7 @@ static void flush_one_dev(struct cgpu_info *bflsc, int dev) rd_lock(&bflsc->qlock); HASH_ITER(hh, bflsc->queued_work, work, tmp) { - if (work->queued && work->subid == dev) { + if (work->subid == dev) { // devflag is used to flag stale work work->devflag = true; did = true; @@ -958,6 +987,70 @@ static void bflsc_flush_work(struct cgpu_info *bflsc) flush_one_dev(bflsc, dev); } +static void bflsc_set_volt(struct cgpu_info *bflsc, int dev) +{ + struct bflsc_info *sc_info = (struct bflsc_info *)(bflsc->device_data); + char buf[BFLSC_BUFSIZ+1]; + char msg[16]; + int err, amount; + bool sent; + + // Device is gone + if (bflsc->usbinfo.nodev) + return; + + snprintf(msg, sizeof(msg), "V%dX", sc_info->volt_next); + + mutex_lock(&bflsc->device_mutex); + + err = send_recv_ss(bflsc, dev, &sent, &amount, + msg, strlen(msg), C_SETVOLT, + buf, sizeof(buf)-1, C_REPLYSETVOLT, READ_NL); + mutex_unlock(&(bflsc->device_mutex)); + + if (!sent) + bflsc_applog(bflsc, dev, C_SETVOLT, amount, err); + else { + // Don't care + } + + sc_info->volt_next_stat = false; + + return; +} + +static void bflsc_set_clock(struct cgpu_info *bflsc, int dev) +{ + struct bflsc_info *sc_info = (struct bflsc_info *)(bflsc->device_data); + char buf[BFLSC_BUFSIZ+1]; + char msg[16]; + int err, amount; + bool sent; + + // Device is gone + if (bflsc->usbinfo.nodev) + return; + + snprintf(msg, sizeof(msg), "F%XX", sc_info->clock_next); + + mutex_lock(&bflsc->device_mutex); + + err = send_recv_ss(bflsc, dev, &sent, &amount, + msg, strlen(msg), C_SETCLOCK, + buf, sizeof(buf)-1, C_REPLYSETCLOCK, READ_NL); + mutex_unlock(&(bflsc->device_mutex)); + + if (!sent) + bflsc_applog(bflsc, dev, C_SETCLOCK, amount, err); + else { + // Don't care + } + + sc_info->clock_next_stat = false; + + return; +} + static void bflsc_flash_led(struct cgpu_info *bflsc, int dev) { struct bflsc_info *sc_info = (struct bflsc_info *)(bflsc->device_data); @@ -992,6 +1085,36 @@ static void bflsc_flash_led(struct cgpu_info *bflsc, int dev) return; } +/* Flush and stop all work if the device reaches the thermal cutoff temp, or + * temporarily stop queueing work if it's in the throttling range. */ +static void bflsc_manage_temp(struct cgpu_info *bflsc, struct bflsc_dev *sc_dev, + int dev, float temp) +{ + bflsc->temp = temp; + if (bflsc->cutofftemp > 0) { + int cutoff = bflsc->cutofftemp; + int throttle = cutoff - BFLSC_TEMP_THROTTLE; + int recover = cutoff - BFLSC_TEMP_RECOVER; + + if (sc_dev->overheat) { + if (temp < recover) + sc_dev->overheat = false; + } else if (temp > throttle) { + sc_dev->overheat = true; + if (temp > cutoff) { + applog(LOG_WARNING, "%s%i: temp (%.1f) hit thermal cutoff limit %d, stopping work!", + bflsc->drv->name, bflsc->device_id, temp, cutoff); + dev_error(bflsc, REASON_DEV_THERMAL_CUTOFF); + flush_one_dev(bflsc, dev); + + } else { + applog(LOG_NOTICE, "%s%i: temp (%.1f) hit thermal throttle limit %d, throttling", + bflsc->drv->name, bflsc->device_id, temp, throttle); + } + } + } +} + static bool bflsc_get_temp(struct cgpu_info *bflsc, int dev) { struct bflsc_info *sc_info = (struct bflsc_info *)(bflsc->device_data); @@ -1017,6 +1140,14 @@ static bool bflsc_get_temp(struct cgpu_info *bflsc, int dev) return false; } + if (sc_info->volt_next_stat || sc_info->clock_next_stat) { + if (sc_info->volt_next_stat) + bflsc_set_volt(bflsc, dev); + if (sc_info->clock_next_stat) + bflsc_set_clock(bflsc, dev); + return true; + } + // Flash instead of Temp if (sc_info->flash_led) { bflsc_flash_led(bflsc, dev); @@ -1081,7 +1212,7 @@ static bool bflsc_get_temp(struct cgpu_info *bflsc, int dev) res = breakdown(ALLCOLON, temp_buf, &count, &firstname, &fields, &lf); if (lf) *lf = '\0'; - if (!res || count != 2 || !lf) { + if (!res || count < 2 || !lf) { tmp = str_text(temp_buf); applog(LOG_WARNING, "%s%i: Invalid%s temp reply: '%s'", bflsc->drv->name, bflsc->device_id, xlink, tmp); @@ -1185,34 +1316,67 @@ static bool bflsc_get_temp(struct cgpu_info *bflsc, int dev) if (temp < temp2) temp = temp2; - bflsc->temp = temp; + bflsc_manage_temp(bflsc, sc_dev, dev, temp); + } - if (bflsc->cutofftemp > 0 && temp >= bflsc->cutofftemp) { - applog(LOG_WARNING, "%s%i:%s temp (%.1f) hit thermal cutoff limit %d, stopping work!", - bflsc->drv->name, bflsc->device_id, xlink, - temp, bflsc->cutofftemp); - dev_error(bflsc, REASON_DEV_THERMAL_CUTOFF); - sc_dev->overheat = true; - flush_one_dev(bflsc, dev); - return false; - } + return true; +} - if (bflsc->cutofftemp > 0 && temp < (bflsc->cutofftemp - BFLSC_TEMP_RECOVER)) - sc_dev->overheat = false; +static void inc_core_errors(struct bflsc_info *info, int8_t core) +{ + if (info->ident == IDENT_BMA) { + if (core >= 0) + info->cortex_hw[core]++; + } else { + if (core >= 0 && core < 16) + info->core_hw[core]++; } +} - return true; +static void inc_bflsc_errors(struct thr_info *thr, struct bflsc_info *info, int8_t core) +{ + inc_hw_errors(thr); + inc_core_errors(info, core); +} + +static void inc_bflsc_nonces(struct bflsc_info *info, int8_t core) +{ + if (info->ident == IDENT_BMA) { + if (core >= 0) + info->cortex_nonces[core]++; + } else { + if (core >= 0 && core < 16) + info->core_nonces[core]++; + } +} + +struct work *bflsc_work_by_uid(struct cgpu_info *bflsc, struct bflsc_info *sc_info, int id) +{ + struct bflsc_work *bwork; + struct work *work = NULL; + + wr_lock(&bflsc->qlock); + HASH_FIND_INT(sc_info->bworks, &id, bwork); + if (likely(bwork)) { + HASH_DEL(sc_info->bworks, bwork); + work = bwork->work; + free(bwork); + } + wr_unlock(&bflsc->qlock); + + return work; } static void process_nonces(struct cgpu_info *bflsc, int dev, char *xlink, char *data, int count, char **fields, int *nonces) { struct bflsc_info *sc_info = (struct bflsc_info *)(bflsc->device_data); - char midstate[MIDSTATE_BYTES], blockdata[MERKLE_BYTES]; - struct work *work; + struct thr_info *thr = bflsc->thr[0]; + struct work *work = NULL; + int8_t core = -1; uint32_t nonce; int i, num, x; - bool res; char *tmp; + bool res; if (count < sc_info->que_fld_min) { tmp = str_text(data); @@ -1220,15 +1384,27 @@ static void process_nonces(struct cgpu_info *bflsc, int dev, char *xlink, char * "%s%i:%s work returned too small (%d,%s)", bflsc->drv->name, bflsc->device_id, xlink, count, tmp); free(tmp); - inc_hw_errors(bflsc->thr[0]); + inc_bflsc_errors(thr, sc_info, core); return; } + if (sc_info->ident == IDENT_BMA) { + unsigned int ucore; + + if (sscanf(fields[QUE_CC], "%x", &ucore) == 1) + core = ucore; + } else if (sc_info->que_noncecount != QUE_NONCECOUNT_V1) { + unsigned int ucore; + + if (sscanf(fields[QUE_CHIP_V2], "%x", &ucore) == 1) + core = ucore; + } + if (count > sc_info->que_fld_max) { applog(LOG_INFO, "%s%i:%s work returned too large (%d) processing %d anyway", bflsc->drv->name, bflsc->device_id, xlink, count, sc_info->que_fld_max); count = sc_info->que_fld_max; - inc_hw_errors(bflsc->thr[0]); + inc_bflsc_errors(thr, sc_info, core); } num = atoi(fields[sc_info->que_noncecount]); @@ -1239,26 +1415,33 @@ static void process_nonces(struct cgpu_info *bflsc, int dev, char *xlink, char * bflsc->drv->name, bflsc->device_id, xlink, num, count - sc_info->que_fld_max, tmp); free(tmp); - inc_hw_errors(bflsc->thr[0]); + inc_bflsc_errors(thr, sc_info, core); } - memset(midstate, 0, MIDSTATE_BYTES); - memset(blockdata, 0, MERKLE_BYTES); - if (!hex2bin((unsigned char *)midstate, fields[QUE_MIDSTATE], MIDSTATE_BYTES) || - !hex2bin((unsigned char *)blockdata, fields[QUE_BLOCKDATA], MERKLE_BYTES)) { - applog(LOG_INFO, "%s%i:%s Failed to convert binary data to hex result - ignored", - bflsc->drv->name, bflsc->device_id, xlink); - inc_hw_errors(bflsc->thr[0]); - return; - } + if (sc_info->ident == IDENT_BMA) { + int uid; + + if (sscanf(fields[QUE_UID], "%04x", &uid) == 1) + work = bflsc_work_by_uid(bflsc, sc_info, uid); + } else { + char midstate[MIDSTATE_BYTES] = {}, blockdata[MERKLE_BYTES] = {}; + + if (!hex2bin((unsigned char *)midstate, fields[QUE_MIDSTATE], MIDSTATE_BYTES) || + !hex2bin((unsigned char *)blockdata, fields[QUE_BLOCKDATA], MERKLE_BYTES)) { + applog(LOG_INFO, "%s%i:%s Failed to convert binary data to hex result - ignored", + bflsc->drv->name, bflsc->device_id, xlink); + inc_bflsc_errors(thr, sc_info, core); + return; + } - work = take_queued_work_bymidstate(bflsc, midstate, MIDSTATE_BYTES, - blockdata, MERKLE_OFFSET, MERKLE_BYTES); + work = take_queued_work_bymidstate(bflsc, midstate, MIDSTATE_BYTES, + blockdata, MERKLE_OFFSET, MERKLE_BYTES); + } if (!work) { if (sc_info->not_first_work) { applog(LOG_INFO, "%s%i:%s failed to find nonce work - can't be processed - ignored", bflsc->drv->name, bflsc->device_id, xlink); - inc_hw_errors(bflsc->thr[0]); + inc_bflsc_errors(thr, sc_info, core); } return; } @@ -1276,7 +1459,7 @@ static void process_nonces(struct cgpu_info *bflsc, int dev, char *xlink, char * hex2bin((void*)&nonce, fields[i], 4); nonce = htobe32(nonce); - res = submit_nonce(bflsc->thr[0], work, nonce); + res = submit_nonce(thr, work, nonce); if (res) { wr_lock(&(sc_info->stat_lock)); sc_info->sc_devs[dev].nonces_found++; @@ -1284,7 +1467,9 @@ static void process_nonces(struct cgpu_info *bflsc, int dev, char *xlink, char * (*nonces)++; x++; - } + inc_bflsc_nonces(sc_info, core); + } else + inc_core_errors(sc_info, core); } wr_lock(&(sc_info->stat_lock)); @@ -1303,7 +1488,7 @@ static void process_nonces(struct cgpu_info *bflsc, int dev, char *xlink, char * free_work(work); } -static int process_results(struct cgpu_info *bflsc, int dev, char *pbuf, int *nonces) +static int process_results(struct cgpu_info *bflsc, int dev, char *pbuf, int *nonces, int *in_process) { struct bflsc_info *sc_info = (struct bflsc_info *)(bflsc->device_data); char **items, *firstname, **fields, *lf; @@ -1313,12 +1498,14 @@ static int process_results(struct cgpu_info *bflsc, int dev, char *pbuf, int *no bool res; *nonces = 0; + *in_process = 0; xlinkstr(xlink, sizeof(xlink), dev, sc_info); buf = strdup(pbuf); + if (!strncmp(buf, "INPROCESS", 9)) + sscanf(buf, "INPROCESS:%d\n%s", in_process, pbuf); res = tolines(bflsc, dev, buf, &lines, &items, C_GETRESULTS); - free(buf); if (!res || lines < 1) { tmp = str_text(pbuf); applogsiz(LOG_ERR, BFLSC_APPLOGSIZ, @@ -1389,6 +1576,7 @@ static int process_results(struct cgpu_info *bflsc, int dev, char *pbuf, int *no arigatou: freetolines(&lines, &items); + free(buf); return que; } @@ -1417,6 +1605,7 @@ static void *bflsc_get_results(void *userdata) while (sc_info->shutdown == false) { cgtimer_t ts_start; + int in_process; if (bflsc->usbinfo.nodev) return NULL; @@ -1448,13 +1637,16 @@ static void *bflsc_get_results(void *userdata) if (err < 0 || (!readok && amount != BFLSC_QRES_LEN) || (readok && amount < 1)) { // TODO: do what else? } else { - que = process_results(bflsc, dev, buf, &nonces); + que = process_results(bflsc, dev, buf, &nonces, &in_process); sc_info->not_first_work = true; // in case it failed processing it if (que > 0) cgtime(&(sc_info->sc_devs[dev].last_dev_result)); if (nonces > 0) cgtime(&(sc_info->sc_devs[dev].last_nonce_result)); + /* There are more results queued so do not sleep */ + if (in_process) + continue; // TODO: if not getting results ... reinit? } @@ -1599,6 +1791,146 @@ static bool bflsc_send_work(struct cgpu_info *bflsc, int dev, bool mandatory) return ret; } +#define JP_COMMAND 0 +#define JP_STREAMLENGTH 2 +#define JP_SIGNATURE 4 +#define JP_JOBSINARRY 5 +#define JP_JOBSARRY 6 +#define JP_ARRAYSIZE 45 + +static bool bflsc28_queue_full(struct cgpu_info *bflsc) +{ + struct bflsc_info *sc_info = bflsc->device_data; + int created, queued = 0, create, i, offset; + struct work *base_work, *work, *works[10]; + char *buf, *field, *ptr; + bool sent, ret = false; + uint16_t *streamlen; + uint8_t *job_pack; + int err, amount; + + job_pack = alloca(2 + // Command + 2 + // StreamLength + 1 + // Signature + 1 + // JobsInArray + JP_ARRAYSIZE * 10 +// Array of up to 10 Job Structs + 1 // EndOfWrapper + ); + + if (bflsc->usbinfo.nodev) + return true; + + /* Don't send any work if this device is overheating */ + if (sc_info->sc_devs[0].overheat == true) + return true; + + wr_lock(&bflsc->qlock); + base_work = __get_queued(bflsc); + if (likely(base_work)) + __work_completed(bflsc, base_work); + wr_unlock(&bflsc->qlock); + + if (unlikely(!base_work)) + return ret; + created = 1; + + create = 9; + if (base_work->drv_rolllimit < create) + create = base_work->drv_rolllimit; + + works[0] = base_work; + for (i = 1; i <= create ; i++) { + created++; + work = make_clone(base_work); + roll_work(base_work); + works[i] = work; + } + + memcpy(job_pack, "WX", 2); + streamlen = (uint16_t *)&job_pack[JP_STREAMLENGTH]; + *streamlen = created * JP_ARRAYSIZE + 7; + job_pack[JP_SIGNATURE] = 0xc1; + job_pack[JP_JOBSINARRY] = created; + offset = JP_JOBSARRY; + + /* Create the maximum number of work items we can queue by nrolling one */ + for (i = 0; i < created; i++) { + work = works[i]; + memcpy(job_pack + offset, work->midstate, MIDSTATE_BYTES); + offset += MIDSTATE_BYTES; + memcpy(job_pack + offset, work->data + MERKLE_OFFSET, MERKLE_BYTES); + offset += MERKLE_BYTES; + job_pack[offset] = 0xaa; // EndOfBlock signature + offset++; + } + job_pack[offset++] = 0xfe; // EndOfWrapper + + buf = alloca(BFLSC_BUFSIZ + 1); + mutex_lock(&bflsc->device_mutex); + err = send_recv_ss(bflsc, 0, &sent, &amount, (char *)job_pack, offset, + C_REQUESTQUEJOB, buf, BFLSC_BUFSIZ, C_REQUESTQUEJOBSTATUS, READ_NL); + mutex_unlock(&bflsc->device_mutex); + + if (!isokerr(err, buf, amount)) { + if (!strncasecmp(buf, "ERR:QUEUE FULL", 14)) { + applog(LOG_DEBUG, "%s%d: Queue full", + bflsc->drv->name, bflsc->device_id); + ret = true; + } else { + applog(LOG_WARNING, "%s%d: Queue response not ok %s", + bflsc->drv->name, bflsc->device_id, buf); + } + goto out; + } + + ptr = alloca(strlen(buf)); + if (sscanf(buf, "OK:QUEUED %d:%s", &queued, ptr) != 2) { + applog(LOG_WARNING, "%s%d: Failed to parse queue response %s", + bflsc->drv->name, bflsc->device_id, buf); + goto out; + } + if (queued < 1 || queued > 10) { + applog(LOG_WARNING, "%s%d: Invalid queued count %d", + bflsc->drv->name, bflsc->device_id, queued); + queued = 0; + goto out; + } + for (i = 0; i < queued; i++) { + struct bflsc_work *bwork, *oldbwork; + unsigned int uid; + + work = works[i]; + field = Strsep(&ptr, ","); + if (!field) { + applog(LOG_WARNING, "%s%d: Ran out of queued IDs after %d of %d", + bflsc->drv->name, bflsc->device_id, i, queued); + queued = i - 1; + goto out; + } + sscanf(field, "%04x", &uid); + bwork = calloc(sizeof(struct bflsc_work), 1); + bwork->id = uid; + bwork->work = work; + + wr_lock(&bflsc->qlock); + HASH_REPLACE_INT(sc_info->bworks, id, bwork, oldbwork); + if (oldbwork) { + free_work(oldbwork->work); + free(oldbwork); + } + wr_unlock(&bflsc->qlock); + sc_info->sc_devs[0].work_queued++; + } + if (queued < created) + ret = true; +out: + for (i = queued; i < created; i++) { + work = works[i]; + discard_work(work); + } + return ret; +} + static bool bflsc_queue_full(struct cgpu_info *bflsc) { struct bflsc_info *sc_info = (struct bflsc_info *)(bflsc->device_data); @@ -1716,7 +2048,7 @@ static int64_t bflsc_scanwork(struct thr_info *thr) } waited = restart_wait(thr, sc_info->scan_sleep_time); - if (waited == ETIMEDOUT) { + if (waited == ETIMEDOUT && sc_info->ident != IDENT_BMA) { unsigned int old_sleep_time, new_sleep_time = 0; int min_queued = sc_info->que_size; /* Only adjust the scan_sleep_time if we did not receive a @@ -1770,7 +2102,7 @@ static int64_t bflsc_scanwork(struct thr_info *thr) return ret; } -#define BFLSC_OVER_TEMP 60 +#define BFLSC_OVER_TEMP 75 /* Set the fanspeed to auto for any valid value <= BFLSC_OVER_TEMP, * or max for any value > BFLSC_OVER_TEMP or if we don't know the temperature. */ @@ -1832,6 +2164,61 @@ static bool bflsc_get_stats(struct cgpu_info *bflsc) return allok; } +static char *bflsc_set(struct cgpu_info *bflsc, char *option, char *setting, char *replybuf) +{ + struct bflsc_info *sc_info = (struct bflsc_info *)(bflsc->device_data); + int val; + + if (sc_info->ident != IDENT_BMA) { + strcpy(replybuf, "no set options available"); + return replybuf; + } + + if (strcasecmp(option, "help") == 0) { + sprintf(replybuf, "volt: range 0-9 clock: range 0-15"); + return replybuf; + } + + if (strcasecmp(option, "volt") == 0) { + if (!setting || !*setting) { + sprintf(replybuf, "missing volt setting"); + return replybuf; + } + + val = atoi(setting); + if (val < 0 || val > 9) { + sprintf(replybuf, "invalid volt: '%s' valid range 0-9", + setting); + } + + sc_info->volt_next = val; + sc_info->volt_next_stat = true; + + return NULL; + } + + if (strcasecmp(option, "clock") == 0) { + if (!setting || !*setting) { + sprintf(replybuf, "missing clock setting"); + return replybuf; + } + + val = atoi(setting); + if (val < 0 || val > 15) { + sprintf(replybuf, "invalid clock: '%s' valid range 0-15", + setting); + } + + sc_info->clock_next = val; + sc_info->clock_next_stat = true; + + return NULL; + } + + sprintf(replybuf, "Unknown option: %s", option); + return replybuf; +} + static void bflsc_identify(struct cgpu_info *bflsc) { struct bflsc_info *sc_info = (struct bflsc_info *)(bflsc->device_data); @@ -1860,8 +2247,10 @@ static struct api_data *bflsc_api_stats(struct cgpu_info *bflsc) { struct bflsc_info *sc_info = (struct bflsc_info *)(bflsc->device_data); struct api_data *root = NULL; + char data[4096]; char buf[256]; - int i; + int i, j, off; + size_t len; //if no x-link ... etc rd_lock(&(sc_info->stat_lock)); @@ -1909,18 +2298,66 @@ else a whole lot of something like these ... etc root = api_add_volts(root, "X-%d-Vcc2", &(sc_info->vcc2), false); root = api_add_volts(root, "X-%d-Vmain", &(sc_info->vmain), false); */ + if (sc_info->ident == IDENT_BMA) { + for (i = 0; i < 128; i += 16) { + data[0] = '\0'; + off = 0; + for (j = 0; j < 16; j++) { + len = snprintf(data+off, sizeof(data)-off, + "%s%"PRIu64, + j > 0 ? " " : "", + sc_info->cortex_nonces[i+j]); + if (len >= (sizeof(data)-off)) + off = sizeof(data)-1; + else { + if (len > 0) + off += len; + } + } + sprintf(buf, "Cortex %02x-%02x Nonces", i, i+15); + root = api_add_string(root, buf, data, true); + } + for (i = 0; i < 128; i += 16) { + data[0] = '\0'; + off = 0; + for (j = 0; j < 16; j++) { + len = snprintf(data+off, sizeof(data)-off, + "%s%"PRIu64, + j > 0 ? " " : "", + sc_info->cortex_hw[i+j]); + if (len >= (sizeof(data)-off)) + off = sizeof(data)-1; + else { + if (len > 0) + off += len; + } + } + sprintf(buf, "Cortex %02x-%02x HW Errors", i, i+15); + root = api_add_string(root, buf, data, true); + } + } else if (sc_info->que_noncecount != QUE_NONCECOUNT_V1) { + for (i = 0; i < 16; i++) { + sprintf(buf, "Core%d Nonces", i); + root = api_add_uint64(root, buf, &sc_info->core_nonces[i], false); + } + for (i = 0; i < 16; i++) { + sprintf(buf, "Core%d HW Errors", i); + root = api_add_uint64(root, buf, &sc_info->core_hw[i], false); + } + } return root; } struct device_drv bflsc_drv = { - .drv_id = DRIVER_BFLSC, + .drv_id = DRIVER_bflsc, .dname = "BitForceSC", .name = BFLSC_SINGLE, .drv_detect = bflsc_detect, .get_api_stats = bflsc_api_stats, .get_statline_before = get_bflsc_statline_before, .get_stats = bflsc_get_stats, + .set_device = bflsc_set, .identify_device = bflsc_identify, .thread_prepare = bflsc_thread_prepare, .thread_init = bflsc_thread_init, diff --git a/driver-bflsc.h b/driver-bflsc.h index baa368748f..4778993e99 100644 --- a/driver-bflsc.h +++ b/driver-bflsc.h @@ -1,5 +1,5 @@ /* - * Copyright 2013 Con Kolivas + * Copyright 2013-2014 Con Kolivas * Copyright 2013 Andrew Smith * * This program is free software; you can redistribute it and/or modify it @@ -59,6 +59,8 @@ enum driver_version { #define BFLSC_DI_DEVICESINCHAIN "DEVICES IN CHAIN" #define BFLSC_DI_CHAINPRESENCE "CHAIN PRESENCE MASK" #define BFLSC_DI_CHIPS "CHIP PARALLELIZATION" +#define BFLSC_DI_CHIPS_PARALLEL "YES" +#define BFLSC28_DI_ASICS "ASIC Installed" #define FULLNONCE 0x100000000ULL @@ -115,7 +117,14 @@ struct bflsc_dev { #define QUE_MAX_RESULTS 8 +struct bflsc_work { + UT_hash_handle hh; + int id; + struct work *work; +}; + struct bflsc_info { + enum sub_ident ident; enum driver_version driver_version; pthread_rwlock_t stat_lock; struct thr_info results_thr; @@ -138,9 +147,20 @@ struct bflsc_info { int que_noncecount; int que_fld_min; int que_fld_max; + uint64_t core_nonces[17]; + uint64_t core_hw[17]; int flush_size; // count of given size, [+2] is for any > QUE_MAX_RESULTS uint64_t result_size[QUE_MAX_RESULTS+2]; + + struct bflsc_work *bworks; + uint64_t cortex_nonces[0x80]; + uint64_t cortex_hw[0x80]; + + int volt_next; + bool volt_next_stat; + int clock_next; + bool clock_next_stat; }; #define BFLSC_XLINKHDR '@' @@ -172,6 +192,9 @@ struct QueueJobStructure { #define QUE_MIDSTATE 0 #define QUE_BLOCKDATA 1 +#define QUE_UID 0 +#define QUE_CC 1 + #define QUE_NONCECOUNT_V1 2 #define QUE_FLD_MIN_V1 3 #define QUE_FLD_MAX_V1 (QUE_MAX_RESULTS+QUE_FLD_MIN_V1) @@ -244,6 +267,7 @@ struct SaveString { // Replies #define BFLSC_IDENTITY "BitFORCE SC" #define BFLSC_BFLSC "SHA256 SC" +#define BFLSC_BFLSC28 "SC-28nm" #define BFLSC_OK "OK\n" #define BFLSC_OK_LEN (sizeof(BFLSC_OK)-1) @@ -304,6 +328,7 @@ struct SaveString { #define BFLSC_SINGLE "BAS" #define BFLSC_LITTLESINGLE "BAL" #define BFLSC_JALAPENO "BAJ" +#define BFLSC_MONARCH "BMA" // Default expected time for a nonce range // - thus no need to check until this + last time work was found @@ -314,15 +339,18 @@ struct SaveString { #define BAL_WORK_TIME 143.17 // 4.5GH/s Jalapeno #define BAJ_WORK_TIME 954.44 +#define BMA_WORK_TIME 35 // ??? // Defaults (slightly over half the work time) but ensure none are above 100 // SCAN_TIME - delay after sending work // RES_TIME - delay between checking for results #define BAM_SCAN_TIME 20 +#define BMA_SCAN_TIME 50 #define BAS_SCAN_TIME 360 #define BAL_SCAN_TIME 720 #define BAJ_SCAN_TIME 1000 #define BFLSC_RES_TIME 100 +#define BMA_RES_TIME 50 #define BFLSC_MAX_SLEEP 2000 #define BAJ_LATENCY LATENCY_STD @@ -345,8 +373,10 @@ struct SaveString { #define BFLSC_QUE_WATERMARK_V2 32 #define BFLSC_QUE_LOW_V2 16 -#define BFLSC_TEMP_OVERHEAT 90 -// Must drop this far below cutoff before resuming work +#define BFLSC_TEMP_OVERHEAT 85 +// Will start throttling this much below overheat +#define BFLSC_TEMP_THROTTLE 3 +// Must drop this far below overheat before resuming work #define BFLSC_TEMP_RECOVER 5 // If initialisation fails the first time, diff --git a/driver-bitforce.c b/driver-bitforce.c index 41deae394f..34f1ef9b2b 100644 --- a/driver-bitforce.c +++ b/driver-bitforce.c @@ -77,11 +77,9 @@ static const char *blank = ""; -struct device_drv bitforce_drv; - static void bitforce_initialise(struct cgpu_info *bitforce, bool lock) { - int err; + int err, interface; if (lock) mutex_lock(&bitforce->device_mutex); @@ -89,9 +87,10 @@ static void bitforce_initialise(struct cgpu_info *bitforce, bool lock) if (bitforce->usbinfo.nodev) goto failed; + interface = usb_interface(bitforce); // Reset err = usb_transfer(bitforce, FTDI_TYPE_OUT, FTDI_REQUEST_RESET, - FTDI_VALUE_RESET, bitforce->usbdev->found->interface, C_RESET); + FTDI_VALUE_RESET, interface, C_RESET); if (opt_debug) applog(LOG_DEBUG, "%s%i: reset got err %d", bitforce->drv->name, bitforce->device_id, err); @@ -101,7 +100,7 @@ static void bitforce_initialise(struct cgpu_info *bitforce, bool lock) // Set data control err = usb_transfer(bitforce, FTDI_TYPE_OUT, FTDI_REQUEST_DATA, - FTDI_VALUE_DATA_BFL, bitforce->usbdev->found->interface, C_SETDATA); + FTDI_VALUE_DATA_BFL, interface, C_SETDATA); if (opt_debug) applog(LOG_DEBUG, "%s%i: setdata got err %d", bitforce->drv->name, bitforce->device_id, err); @@ -111,7 +110,7 @@ static void bitforce_initialise(struct cgpu_info *bitforce, bool lock) // Set the baud err = usb_transfer(bitforce, FTDI_TYPE_OUT, FTDI_REQUEST_BAUD, FTDI_VALUE_BAUD_BFL, - (FTDI_INDEX_BAUD_BFL & 0xff00) | bitforce->usbdev->found->interface, + (FTDI_INDEX_BAUD_BFL & 0xff00) | interface, C_SETBAUD); if (opt_debug) applog(LOG_DEBUG, "%s%i: setbaud got err %d", @@ -122,7 +121,7 @@ static void bitforce_initialise(struct cgpu_info *bitforce, bool lock) // Set Flow Control err = usb_transfer(bitforce, FTDI_TYPE_OUT, FTDI_REQUEST_FLOW, - FTDI_VALUE_FLOW, bitforce->usbdev->found->interface, C_SETFLOW); + FTDI_VALUE_FLOW, interface, C_SETFLOW); if (opt_debug) applog(LOG_DEBUG, "%s%i: setflowctrl got err %d", bitforce->drv->name, bitforce->device_id, err); @@ -132,7 +131,7 @@ static void bitforce_initialise(struct cgpu_info *bitforce, bool lock) // Set Modem Control err = usb_transfer(bitforce, FTDI_TYPE_OUT, FTDI_REQUEST_MODEM, - FTDI_VALUE_MODEM, bitforce->usbdev->found->interface, C_SETMODEM); + FTDI_VALUE_MODEM, interface, C_SETMODEM); if (opt_debug) applog(LOG_DEBUG, "%s%i: setmodemctrl got err %d", bitforce->drv->name, bitforce->device_id, err); @@ -142,7 +141,7 @@ static void bitforce_initialise(struct cgpu_info *bitforce, bool lock) // Clear any sent data err = usb_transfer(bitforce, FTDI_TYPE_OUT, FTDI_REQUEST_RESET, - FTDI_VALUE_PURGE_TX, bitforce->usbdev->found->interface, C_PURGETX); + FTDI_VALUE_PURGE_TX, interface, C_PURGETX); if (opt_debug) applog(LOG_DEBUG, "%s%i: purgetx got err %d", bitforce->drv->name, bitforce->device_id, err); @@ -152,7 +151,7 @@ static void bitforce_initialise(struct cgpu_info *bitforce, bool lock) // Clear any received data err = usb_transfer(bitforce, FTDI_TYPE_OUT, FTDI_REQUEST_RESET, - FTDI_VALUE_PURGE_RX, bitforce->usbdev->found->interface, C_PURGERX); + FTDI_VALUE_PURGE_RX, interface, C_PURGERX); if (opt_debug) applog(LOG_DEBUG, "%s%i: purgerx got err %d", bitforce->drv->name, bitforce->device_id, err); @@ -163,7 +162,7 @@ static void bitforce_initialise(struct cgpu_info *bitforce, bool lock) mutex_unlock(&bitforce->device_mutex); } -static bool bitforce_detect_one(struct libusb_device *dev, struct usb_find_devices *found) +static struct cgpu_info *bitforce_detect_one(struct libusb_device *dev, struct usb_find_devices *found) { char buf[BITFORCE_BUFSIZ+1]; int err, amount; @@ -271,7 +270,7 @@ static bool bitforce_detect_one(struct libusb_device *dev, struct usb_find_devic mutex_init(&bitforce->device_mutex); - return true; + return bitforce; unshin: @@ -286,10 +285,10 @@ static bool bitforce_detect_one(struct libusb_device *dev, struct usb_find_devic bitforce = usb_free_cgpu(bitforce); - return false; + return NULL; } -static void bitforce_detect(void) +static void bitforce_detect(bool __maybe_unused hotplug) { usb_detect(&bitforce_drv, bitforce_detect_one); } @@ -299,11 +298,7 @@ static void get_bitforce_statline_before(char *buf, size_t bufsiz, struct cgpu_i float gt = bitforce->temp; if (gt > 0) - tailsprintf(buf, bufsiz, "%5.1fC ", gt); - else - tailsprintf(buf, bufsiz, " "); - - tailsprintf(buf, bufsiz, " | "); + tailsprintf(buf, bufsiz, "%5.1fC", gt); } static bool bitforce_thread_prepare(__maybe_unused struct thr_info *thr) @@ -484,18 +479,18 @@ static bool bitforce_send_work(struct thr_info *thr, struct work *work) memcpy(ob + 8 + 32, work->data + 64, 12); if (!bitforce->nonce_range) { sprintf((char *)ob + 8 + 32 + 12, ">>>>>>>>"); - work->blk.nonce = bitforce->nonces = 0xffffffff; + work->nonce = bitforce->nonces = 0xffffffff; len = 60; } else { uint32_t *nonce; nonce = (uint32_t *)(ob + 8 + 32 + 12); - *nonce = htobe32(work->blk.nonce); + *nonce = htobe32(work->nonce); nonce = (uint32_t *)(ob + 8 + 32 + 12 + 4); /* Split work up into 1/5th nonce ranges */ bitforce->nonces = 0x33333332; - *nonce = htobe32(work->blk.nonce + bitforce->nonces); - work->blk.nonce += bitforce->nonces + 1; + *nonce = htobe32(work->nonce + bitforce->nonces); + work->nonce += bitforce->nonces + 1; sprintf((char *)ob + 8 + 32 + 12 + 8, ">>>>>>>>"); len = 68; } @@ -635,12 +630,12 @@ static int64_t bitforce_get_result(struct thr_info *thr, struct work *work) #ifndef __BIG_ENDIAN__ nonce = swab32(nonce); #endif - if (unlikely(bitforce->nonce_range && (nonce >= work->blk.nonce || - (work->blk.nonce > 0 && nonce < work->blk.nonce - bitforce->nonces - 1)))) { + if (unlikely(bitforce->nonce_range && (nonce >= work->nonce || + (work->nonce > 0 && nonce < work->nonce - bitforce->nonces - 1)))) { applog(LOG_WARNING, "%s%i: Disabling broken nonce range support", bitforce->drv->name, bitforce->device_id); bitforce->nonce_range = false; - work->blk.nonce = 0xffffffff; + work->nonce = 0xffffffff; bitforce->sleep_ms *= 5; bitforce->kname = KNAME_WORK; } @@ -741,7 +736,7 @@ static struct api_data *bitforce_api_stats(struct cgpu_info *cgpu) } struct device_drv bitforce_drv = { - .drv_id = DRIVER_BITFORCE, + .drv_id = DRIVER_bitforce, .dname = "BitForce", .name = "BFL", .drv_detect = bitforce_detect, diff --git a/driver-bitfury.c b/driver-bitfury.c new file mode 100644 index 0000000000..6951ccfd9e --- /dev/null +++ b/driver-bitfury.c @@ -0,0 +1,1660 @@ +/* + * Copyright 2013-2014 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 "config.h" + +#include "miner.h" +#include "driver-bitfury.h" +#include "sha2.h" +#include "mcp2210.h" +#include "libbitfury.h" + +int opt_bxf_temp_target = BXF_TEMP_TARGET / 10; +int opt_nfu_bits = 50; +int opt_bxm_bits = 54; +int opt_bxf_bits = 54; +int opt_bxf_debug; +int opt_osm_led_mode = 4; + +/* Wait longer 1/3 longer than it would take for a full nonce range */ +#define BF1WAIT 1600 +#define BF1MSGSIZE 7 +#define BF1INFOSIZE 14 + +#define TWELVE_MHZ 12000000 + +//Low port pins +#define SK 1 +#define DO 2 +#define DI 4 +#define CS 8 +#define GPIO0 16 +#define GPIO1 32 +#define GPIO2 64 +#define GPIO3 128 + +//GPIO pins +#define GPIOL0 0 +#define GPIOL1 1 +#define GPIOL2 2 +#define GPIOL3 3 +#define GPIOH 4 +#define GPIOH1 5 +#define GPIOH2 6 +#define GPIOH3 7 +#define GPIOH4 8 +#define GPIOH5 9 +#define GPIOH6 10 +#define GPIOH7 11 + +#define DEFAULT_DIR (SK | DO | CS | GPIO0 | GPIO1 | GPIO2 | GPIO3) /* Setup default input or output state per FTDI for SPI */ +#define DEFAULT_STATE (CS) /* CS idles high, CLK idles LOW for SPI0 */ + +//MPSSE commands from FTDI AN_108 +#define INVALID_COMMAND 0xAB +#define ENABLE_ADAPTIVE_CLOCK 0x96 +#define DISABLE_ADAPTIVE_CLOCK 0x97 +#define ENABLE_3_PHASE_CLOCK 0x8C +#define DISABLE_3_PHASE_CLOCK 0x8D +#define TCK_X5 0x8A +#define TCK_D5 0x8B +#define CLOCK_N_CYCLES 0x8E +#define CLOCK_N8_CYCLES 0x8F +#define PULSE_CLOCK_IO_HIGH 0x94 +#define PULSE_CLOCK_IO_LOW 0x95 +#define CLOCK_N8_CYCLES_IO_HIGH 0x9C +#define CLOCK_N8_CYCLES_IO_LOW 0x9D +#define TRISTATE_IO 0x9E +#define TCK_DIVISOR 0x86 +#define LOOPBACK_END 0x85 +#define SET_OUT_ADBUS 0x80 +#define SET_OUT_ACBUS 0x82 +#define WRITE_BYTES_SPI0 0x11 +#define READ_WRITE_BYTES_SPI0 0x31 + +static void bf1_empty_buffer(struct cgpu_info *bitfury) +{ + char buf[512]; + int amount; + + do { + usb_read_once(bitfury, buf, 512, &amount, C_BF1_FLUSH); + } while (amount); +} + +static bool bf1_open(struct cgpu_info *bitfury) +{ + uint32_t buf[2]; + int err; + + bf1_empty_buffer(bitfury); + /* Magic sequence to reset device only really needed for windows but + * harmless on linux. */ + buf[0] = 0x80250000; + buf[1] = 0x00000800; + err = usb_transfer(bitfury, 0, 9, 1, 0, C_ATMEL_RESET); + if (!err) + err = usb_transfer(bitfury, 0x21, 0x22, 0, 0, C_ATMEL_OPEN); + if (!err) { + err = usb_transfer_data(bitfury, 0x21, 0x20, 0x0000, 0, buf, + BF1MSGSIZE, C_ATMEL_INIT); + } + + if (err < 0) { + applog(LOG_INFO, "%s %d: Failed to open with error %s", bitfury->drv->name, + bitfury->device_id, libusb_error_name(err)); + } + return (err == BF1MSGSIZE); +} + +static void bf1_close(struct cgpu_info *bitfury) +{ + bf1_empty_buffer(bitfury); +} + +static void bf1_identify(struct cgpu_info *bitfury) +{ + int amount; + + usb_write(bitfury, "L", 1, &amount, C_BF1_IDENTIFY); +} + +static void bitfury_identify(struct cgpu_info *bitfury) +{ + struct bitfury_info *info = bitfury->device_data; + + switch(info->ident) { + case IDENT_BF1: + bf1_identify(bitfury); + break; + case IDENT_BXF: + case IDENT_OSM: + default: + break; + } +} + +static bool bf1_getinfo(struct cgpu_info *bitfury, struct bitfury_info *info) +{ + int amount, err; + char buf[16]; + + err = usb_write(bitfury, "I", 1, &amount, C_BF1_REQINFO); + if (err) { + applog(LOG_INFO, "%s %d: Failed to write REQINFO", + bitfury->drv->name, bitfury->device_id); + return false; + } + err = usb_read(bitfury, buf, BF1INFOSIZE, &amount, C_BF1_GETINFO); + if (err) { + applog(LOG_INFO, "%s %d: Failed to read GETINFO", + bitfury->drv->name, bitfury->device_id); + return false; + } + if (amount != BF1INFOSIZE) { + applog(LOG_INFO, "%s %d: Getinfo received %d bytes instead of %d", + bitfury->drv->name, bitfury->device_id, amount, BF1INFOSIZE); + return false; + } + info->version = buf[1]; + memcpy(&info->product, buf + 2, 8); + memcpy(&info->serial, buf + 10, 4); + bitfury->unique_id = bin2hex((unsigned char *)buf + 10, 4); + + applog(LOG_INFO, "%s %d: Getinfo returned version %d, product %s serial %s", bitfury->drv->name, + bitfury->device_id, info->version, info->product, bitfury->unique_id); + bf1_empty_buffer(bitfury); + return true; +} + +static bool bf1_reset(struct cgpu_info *bitfury) +{ + int amount, err; + char buf[16]; + + err = usb_write(bitfury, "R", 1, &amount, C_BF1_REQRESET); + if (err) { + applog(LOG_INFO, "%s %d: Failed to write REQRESET", + bitfury->drv->name, bitfury->device_id); + return false; + } + err = usb_read_timeout(bitfury, buf, BF1MSGSIZE, &amount, BF1WAIT, + C_BF1_GETRESET); + if (err) { + applog(LOG_INFO, "%s %d: Failed to read GETRESET", + bitfury->drv->name, bitfury->device_id); + return false; + } + if (amount != BF1MSGSIZE) { + applog(LOG_INFO, "%s %d: Getreset received %d bytes instead of %d", + bitfury->drv->name, bitfury->device_id, amount, BF1MSGSIZE); + return false; + } + applog(LOG_DEBUG, "%s %d: Getreset returned %s", bitfury->drv->name, + bitfury->device_id, buf); + bf1_empty_buffer(bitfury); + return true; +} + +static bool bxf_send_msg(struct cgpu_info *bitfury, char *buf, enum usb_cmds cmd) +{ + int err, amount, len; + + if (unlikely(bitfury->usbinfo.nodev)) + return false; + + if (opt_bxf_debug) { + char *strbuf = str_text(buf); + + applog(LOG_ERR, "%s %d: >BXF [%s]", bitfury->drv->name, bitfury->device_id, strbuf); + free(strbuf); + } + + len = strlen(buf); + applog(LOG_DEBUG, "%s %d: Sending %s", bitfury->drv->name, bitfury->device_id, buf); + err = usb_write(bitfury, buf, len, &amount, cmd); + if (err || amount != len) { + applog(LOG_WARNING, "%s %d: Error %d sending %s sent %d of %d", bitfury->drv->name, + bitfury->device_id, err, usb_cmdname(cmd), amount, len); + return false; + } + return true; +} + +static bool bxf_send_debugmode(struct cgpu_info *bitfury) +{ + char buf[16]; + + sprintf(buf, "debug-mode %d\n", opt_bxf_debug); + return bxf_send_msg(bitfury, buf, C_BXF_DEBUGMODE); +} + +static bool bxf_send_ledmode(struct cgpu_info *bitfury) +{ + char buf[16]; + + sprintf(buf, "led-mode %d\n", opt_osm_led_mode); + return bxf_send_msg(bitfury, buf, C_BXF_LEDMODE); +} + +/* Returns the amount received only if we receive a full message, otherwise + * it returns the err value. */ +static int bxf_recv_msg(struct cgpu_info *bitfury, char *buf) +{ + int err, amount; + + err = usb_read_nl(bitfury, buf, 512, &amount, C_BXF_READ); + if (amount) + applog(LOG_DEBUG, "%s %d: Received %s", bitfury->drv->name, bitfury->device_id, buf); + if (!err) + return amount; + return err; +} + +/* Keep reading till the first timeout or error */ +static void bxf_clear_buffer(struct cgpu_info *bitfury) +{ + int err, retries = 0; + char buf[512]; + + do { + err = bxf_recv_msg(bitfury, buf); + usb_buffer_clear(bitfury); + if (err < 0) + break; + } while (retries++ < 10); +} + +static bool bxf_send_flush(struct cgpu_info *bitfury) +{ + char buf[8]; + + sprintf(buf, "flush\n"); + return bxf_send_msg(bitfury, buf, C_BXF_FLUSH); +} + +static bool bxf_detect_one(struct cgpu_info *bitfury, struct bitfury_info *info) +{ + int err, retries = 0; + char buf[512]; + + if (!bxf_send_flush(bitfury)) + return false; + + bxf_clear_buffer(bitfury); + + sprintf(buf, "version\n"); + if (!bxf_send_msg(bitfury, buf, C_BXF_VERSION)) + return false; + + do { + err = bxf_recv_msg(bitfury, buf); + if (err < 0 && err != LIBUSB_ERROR_TIMEOUT) + return false; + if (err > 0 && !strncmp(buf, "version", 7)) { + sscanf(&buf[8], "%d.%d rev %d chips %d", &info->ver_major, + &info->ver_minor, &info->hw_rev, &info->chips); + applog(LOG_INFO, "%s %d: Version %d.%d rev %d chips %d", + bitfury->drv->name, bitfury->device_id, info->ver_major, + info->ver_minor, info->hw_rev, info->chips); + break; + } + /* Keep parsing if the buffer is full without counting it as + * a retry. */ + if (usb_buffer_size(bitfury)) + continue; + } while (retries++ < 10); + + if (!add_cgpu(bitfury)) + quit(1, "Failed to add_cgpu in bxf_detect_one"); + + update_usb_stats(bitfury); + applog(LOG_INFO, "%s %d: Successfully initialised %s", + bitfury->drv->name, bitfury->device_id, bitfury->device_path); + + /* Sanity check and recognise variations */ + if (info->chips <= 2 || info->chips > 999) + info->chips = 2; + else if (info->chips <= 6 && info->ident == IDENT_BXF) + bitfury->drv->name = "HXF"; + else if (info->chips > 6 && info->ident == IDENT_BXF) + bitfury->drv->name = "MXF"; + info->filtered_hw = calloc(sizeof(int), info->chips); + info->job = calloc(sizeof(int), info->chips); + info->submits = calloc(sizeof(int), info->chips); + if (!info->filtered_hw || !info->job || !info->submits) + quit(1, "Failed to calloc bxf chip arrays"); + info->total_nonces = 1; + info->temp_target = opt_bxf_temp_target * 10; + /* This unsets it to make sure it gets set on the first pass */ + info->maxroll = -1; + + return true; +} + +static bool bf1_detect_one(struct cgpu_info *bitfury, struct bitfury_info *info) +{ + if (!bf1_open(bitfury)) + goto out_close; + + /* Send getinfo request */ + if (!bf1_getinfo(bitfury, info)) + goto out_close; + + /* Send reset request */ + if (!bf1_reset(bitfury)) + goto out_close; + + bf1_identify(bitfury); + bf1_empty_buffer(bitfury); + + if (!add_cgpu(bitfury)) + quit(1, "Failed to add_cgpu in bf1_detect_one"); + + update_usb_stats(bitfury); + applog(LOG_INFO, "%s %d: Successfully initialised %s", + bitfury->drv->name, bitfury->device_id, bitfury->device_path); + + /* This does not artificially raise hashrate, it simply allows the + * hashrate to adapt quickly on starting. */ + info->total_nonces = 1; + + return true; +out_close: + bf1_close(bitfury); + return false; +} + +static void nfu_close(struct cgpu_info *bitfury) +{ + struct bitfury_info *info = bitfury->device_data; + struct mcp_settings *mcp = &info->mcp; + int i; + + mcp2210_spi_cancel(bitfury); + + /* Set all pins to input mode, ignoring return code */ + for (i = 0; i < 9; i++) { + mcp->direction.pin[i] = MCP2210_GPIO_INPUT; + mcp->value.pin[i] = MCP2210_GPIO_PIN_LOW; + } + mcp2210_set_gpio_settings(bitfury, mcp); +} + +static bool nfu_reinit(struct cgpu_info *bitfury, struct bitfury_info *info) +{ + bool ret = true; + int i; + + for (i = 0; i < info->chips; i++) { + spi_clear_buf(info); + spi_add_break(info); + spi_add_fasync(info, i); + spi_set_freq(info); + spi_send_conf(info); + spi_send_init(info); + spi_reset(bitfury, info); + ret = info->spi_txrx(bitfury, info); + if (!ret) + break; + } + return ret; +} + +static bool nfu_set_spi_settings(struct cgpu_info *bitfury, struct bitfury_info *info) +{ + struct mcp_settings *mcp = &info->mcp; + + return mcp2210_set_spi_transfer_settings(bitfury, mcp->bitrate, mcp->icsv, + mcp->acsv, mcp->cstdd, mcp->ldbtcsd, mcp->sdbd, mcp->bpst, mcp->spimode); +} + +static void nfu_alloc_arrays(struct bitfury_info *info) +{ + info->payload = calloc(sizeof(struct bitfury_payload), info->chips); + info->oldbuf = calloc(sizeof(unsigned int) * 17, info->chips); + info->job_switched = calloc(sizeof(bool), info->chips); + info->second_run = calloc(sizeof(bool), info->chips); + info->work = calloc(sizeof(struct work *), info->chips); + info->owork = calloc(sizeof(struct work *), info->chips); + info->submits = calloc(sizeof(int *), info->chips); +} + +static bool nfu_detect_one(struct cgpu_info *bitfury, struct bitfury_info *info) +{ + struct mcp_settings *mcp = &info->mcp; + char buf[MCP2210_BUFFER_LENGTH]; + unsigned int length; + bool ret = false; + int i, val; + + /* Identify number of chips, and use it in device name if it can fit + * into 3 chars, otherwise use generic NFU name. */ + val = sscanf(bitfury->usbdev->prod_string, "NanoFury NF%u ", &info->chips); + if (val < 1) + info->chips = 1; + else if (info->chips < 10) { + sprintf(info->product, "NF%u", info->chips); + bitfury->drv->name = info->product; + } + nfu_alloc_arrays(info); + + info->spi_txrx = &mcp_spi_txrx; + mcp2210_get_gpio_settings(bitfury, mcp); + + for (i = 0; i < 9; i++) { + /* Set all pins to GPIO mode */ + mcp->designation.pin[i] = MCP2210_PIN_GPIO; + /* Set all pins to input mode */ + mcp->direction.pin[i] = MCP2210_GPIO_INPUT; + mcp->value.pin[i] = MCP2210_GPIO_PIN_LOW; + } + + /* Set LED and PWR pins to output and high */ + mcp->direction.pin[NFU_PIN_LED] = mcp->direction.pin[NFU_PIN_PWR_EN] = MCP2210_GPIO_OUTPUT; + mcp->value.pin[NFU_PIN_LED] = mcp->value.pin[NFU_PIN_PWR_EN] = MCP2210_GPIO_PIN_HIGH; + mcp->direction.pin[NFU_PIN_PWR_EN0] = MCP2210_GPIO_OUTPUT; + mcp->value.pin[NFU_PIN_PWR_EN0] = MCP2210_GPIO_PIN_LOW; + + mcp->direction.pin[4] = MCP2210_GPIO_OUTPUT; + mcp->designation.pin[4] = MCP2210_PIN_CS; + + if (!mcp2210_set_gpio_settings(bitfury, mcp)) + goto out; + + if (opt_debug) { + struct gpio_pin gp; + + mcp2210_get_gpio_pindirs(bitfury, &gp); + for (i = 0; i < 9; i++) { + applog(LOG_DEBUG, "%s %d: Pin dir %d %d", bitfury->drv->name, + bitfury->device_id, i, gp.pin[i]); + } + mcp2210_get_gpio_pinvals(bitfury, &gp); + for (i = 0; i < 9; i++) { + applog(LOG_DEBUG, "%s %d: Pin val %d %d", bitfury->drv->name, + bitfury->device_id, i, gp.pin[i]); + } + mcp2210_get_gpio_pindes(bitfury, &gp); + for (i = 0; i < 9; i++) { + applog(LOG_DEBUG, "%s %d: Pin des %d %d", bitfury->drv->name, + bitfury->device_id, i, gp.pin[i]); + } + } + + /* Cancel any transfers in progress */ + if (!mcp2210_spi_cancel(bitfury)) + goto out; + if (!mcp2210_get_spi_transfer_settings(bitfury, &mcp->bitrate, &mcp->icsv, + &mcp->acsv, &mcp->cstdd, &mcp->ldbtcsd, &mcp->sdbd, &mcp->bpst, &mcp->spimode)) + goto out; + mcp->bitrate = 200000; // default to 200kHz + mcp->icsv = 0xffff; + mcp->acsv = 0xffef; + mcp->cstdd = mcp->ldbtcsd = mcp->sdbd = mcp->spimode = 0; + mcp->bpst = 1; + if (!nfu_set_spi_settings(bitfury, info)) + goto out; + + buf[0] = 0; + length = 1; + if (!mcp2210_spi_transfer(bitfury, mcp, buf, &length)) + goto out; + /* after this command SCK_OVRRIDE should read the same as current SCK + * value (which for mode 0 should be 0) */ + if (!mcp2210_get_gpio_pinval(bitfury, NFU_PIN_SCK_OVR, &val)) + goto out; + if (val != MCP2210_GPIO_PIN_LOW) + goto out; + + /* switch SCK to polarity (default SCK=1 in mode 2) */ + mcp->spimode = 2; + if (!nfu_set_spi_settings(bitfury, info)) + goto out; + buf[0] = 0; + length = 1; + if (!mcp2210_spi_transfer(bitfury, mcp, buf, &length)) + goto out; + /* after this command SCK_OVRRIDE should read the same as current SCK + * value (which for mode 2 should be 1) */ + if (!mcp2210_get_gpio_pinval(bitfury, NFU_PIN_SCK_OVR, &val)) + goto out; + if (val != MCP2210_GPIO_PIN_HIGH) + goto out; + + /* switch SCK to polarity (default SCK=0 in mode 0) */ + mcp->spimode = 0; + if (!nfu_set_spi_settings(bitfury, info)) + goto out; + buf[0] = 0; + length = 1; + if (!mcp2210_spi_transfer(bitfury, mcp, buf, &length)) + goto out; + if (!mcp2210_get_gpio_pinval(bitfury, NFU_PIN_SCK_OVR, &val)) + goto out; + if (val != MCP2210_GPIO_PIN_LOW) + goto out; + + info->osc6_bits = opt_nfu_bits; + if (!nfu_reinit(bitfury, info)) + goto out; + + ret = true; + if (!add_cgpu(bitfury)) + quit(1, "Failed to add_cgpu in nfu_detect_one"); + + update_usb_stats(bitfury); + applog(LOG_INFO, "%s %d: Successfully initialised %s", + bitfury->drv->name, bitfury->device_id, bitfury->device_path); + spi_clear_buf(info); + + info->total_nonces = info->chips; +out: + if (!ret) + nfu_close(bitfury); + + return ret; +} + +static bool bxm_purge_buffers(struct cgpu_info *bitfury) +{ + int err; + + err = usb_transfer(bitfury, FTDI_TYPE_OUT, SIO_RESET_REQUEST, SIO_RESET_PURGE_RX, 1, C_BXM_PURGERX); + if (err) + return false; + err = usb_transfer(bitfury, FTDI_TYPE_OUT, SIO_RESET_REQUEST, SIO_RESET_PURGE_TX, 1, C_BXM_PURGETX); + if (err) + return false; + return true; +} + +/* Calculate required divisor for desired frequency see FTDI AN_108 page 19*/ +static uint16_t calc_divisor(uint32_t system_clock, uint32_t freq) +{ + uint16_t divisor = system_clock / freq; + + divisor /= 2; + divisor -= 1; + return divisor; +} + +static void bxm_shutdown(struct cgpu_info *bitfury, struct bitfury_info *info) +{ + int chip_n; + + for (chip_n = 0; chip_n < 2; chip_n++) { + spi_clear_buf(info); + spi_add_break(info); + spi_add_fasync(info, chip_n); + spi_config_reg(info, 4, 0); + info->spi_txrx(bitfury, info); + } +} + +static void bxm_close(struct cgpu_info *bitfury, struct bitfury_info *info) +{ + unsigned char bitmask = 0; + unsigned char mode = BITMODE_RESET; + unsigned short usb_val = bitmask; + + bxm_shutdown(bitfury, info); + + //Need to do BITMODE_RESET before usb close per FTDI + usb_val |= (mode << 8); + usb_transfer(bitfury, FTDI_TYPE_OUT, SIO_SET_BITMODE_REQUEST, usb_val, 1, C_BXM_SETBITMODE); +} + +static bool bxm_open(struct cgpu_info *bitfury) +{ + unsigned char mode = BITMODE_RESET; + unsigned char bitmask = 0; + unsigned short usb_val = bitmask; + uint32_t system_clock = TWELVE_MHZ; + uint32_t freq = 200000; + uint16_t divisor = calc_divisor(system_clock,freq); + int amount, err; + char buf[4]; + + /* Enable the transaction translator emulator for these devices + * otherwise we may write to them too quickly. */ + bitfury->usbdev->tt = true; + + err = usb_transfer(bitfury, FTDI_TYPE_OUT, SIO_RESET_REQUEST, SIO_RESET_SIO, 1, C_BXM_SRESET); + if (err) + return false; + err = usb_transfer(bitfury, FTDI_TYPE_OUT, SIO_SET_LATENCY_TIMER_REQUEST, BXM_LATENCY_MS, 1, C_BXM_SETLATENCY); + if (err) + return false; + err = usb_transfer(bitfury, FTDI_TYPE_OUT, SIO_SET_EVENT_CHAR_REQUEST, 0x00, 1, C_BXM_SECR); + if (err) + return false; + + //Do a BITMODE_RESET + usb_val |= (mode << 8); + err = usb_transfer(bitfury, FTDI_TYPE_OUT, SIO_SET_BITMODE_REQUEST, usb_val, 1, C_BXM_SETBITMODE); + if (err) + return false; + //Now set to MPSSE mode + bitmask = 0; + mode = BITMODE_MPSSE; + usb_val = bitmask; + usb_val |= (mode << 8); + err = usb_transfer(bitfury, FTDI_TYPE_OUT, SIO_SET_BITMODE_REQUEST, usb_val, 1, C_BXM_SETBITMODE); + if (err) + return false; + + //Now set the clock divisor + //First send just the 0x8B command to set the system clock to 12MHz + memset(buf, 0, 4); + buf[0] = TCK_D5; + err = usb_write(bitfury, buf, 1, &amount, C_BXM_CLOCK); + if (err || amount != 1) + return false; + + buf[0] = TCK_DIVISOR; + buf[1] = (divisor & 0xFF); + buf[2] = ((divisor >> 8) & 0xFF); + err = usb_write(bitfury, buf, 3, &amount, C_BXM_CLOCKDIV); + if (err || amount != 3) + return false; + + //Disable internal loopback + buf[0] = LOOPBACK_END; + err = usb_write(bitfury, buf, 1, &amount, C_BXM_LOOP); + if (err || amount != 1) + return false; + + //Now set direction and idle (initial) states for the pins + buf[0] = SET_OUT_ADBUS; + buf[1] = DEFAULT_STATE; //Bitmask for LOW_PORT + buf[2] = DEFAULT_DIR; + err = usb_write(bitfury, buf, 3, &amount, C_BXM_ADBUS); + if (err || amount != 3) + return false; + + //Set the pin states for the HIGH_BITS port as all outputs, all low + buf[0] = SET_OUT_ACBUS; + buf[1] = 0x00; //Bitmask for HIGH_PORT + buf[2] = 0xFF; + err = usb_write(bitfury, buf, 3, &amount, C_BXM_ACBUS); + if (err || amount != 3) + return false; + + return true; +} + +static bool bxm_set_CS_low(struct cgpu_info *bitfury) +{ + char buf[4] = { 0 }; + int err, amount; + + buf[0] = SET_OUT_ADBUS; + buf[1] &= ~DEFAULT_STATE; //Bitmask for LOW_PORT + buf[2] = DEFAULT_DIR; + err = usb_write(bitfury, buf, 3, &amount, C_BXM_CSLOW); + if (err || amount != 3) + return false; + + return true; +} + +static bool bxm_set_CS_high(struct cgpu_info *bitfury) +{ + char buf[4] = { 0 }; + int err, amount; + + buf[0] = SET_OUT_ADBUS; + buf[1] = DEFAULT_STATE; //Bitmask for LOW_PORT + buf[2] = DEFAULT_DIR; + err = usb_write(bitfury, buf, 3, &amount, C_BXM_CSHIGH); + if (err || amount != 3) + return false; + + return true; +} + +static bool bxm_reset_bitfury(struct cgpu_info *bitfury) +{ + char buf[20] = { 0 }; + char rst_buf[8] = {0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00}; + int err, amount; + + //Set the FTDI CS pin HIGH. This will gate the clock to the Bitfury chips so we can send the reset sequence. + if (!bxm_set_CS_high(bitfury)) + return false; + + buf[0] = WRITE_BYTES_SPI0; + buf[1] = (uint8_t)16 - (uint8_t)1; + buf[2] = 0; + memcpy(&buf[3], rst_buf, 8); + memcpy(&buf[11], rst_buf, 8); + err = usb_write(bitfury, buf, 19, &amount, C_BXM_RESET); + if (err || amount != 19) + return false; + + if (!bxm_set_CS_low(bitfury)) + return false; + + return true; +} + +static bool bxm_reinit(struct cgpu_info *bitfury, struct bitfury_info *info) +{ + bool ret; + int i; + + for (i = 0; i < 2; i++) { + spi_clear_buf(info); + spi_add_break(info); + spi_add_fasync(info, i); + spi_set_freq(info); + spi_send_conf(info); + spi_send_init(info); + ret = info->spi_txrx(bitfury, info); + if (!ret) + break; + } + return ret; +} + +static bool bxm_detect_one(struct cgpu_info *bitfury, struct bitfury_info *info) +{ + bool ret; + + info->spi_txrx = &ftdi_spi_txrx; + ret = bxm_open(bitfury); + if (!ret) + goto out; + ret = bxm_purge_buffers(bitfury); + if (!ret) + goto out; + ret = bxm_reset_bitfury(bitfury); + if (!ret) + goto out; + ret = bxm_purge_buffers(bitfury); + if (!ret) + goto out; + + /* Do a dummy read */ + memset(info->spibuf, 0, 80); + info->spibufsz = 80; + ret = info->spi_txrx(bitfury, info); + if (!ret) + goto out; + info->osc6_bits = opt_bxm_bits; + /* Only have 2 chip devices for now */ + info->chips = 2; + nfu_alloc_arrays(info); + + ret = bxm_reinit(bitfury, info); + if (!ret) + goto out; + + if (!add_cgpu(bitfury)) + quit(1, "Failed to add_cgpu in bxm_detect_one"); + + update_usb_stats(bitfury); + applog(LOG_INFO, "%s %d: Successfully initialised %s", + bitfury->drv->name, bitfury->device_id, bitfury->device_path); + spi_clear_buf(info); + + info->total_nonces = 1; +out: + if (!ret) + bxm_close(bitfury, info); + return ret; +} + +static struct cgpu_info *bitfury_detect_one(struct libusb_device *dev, struct usb_find_devices *found) +{ + struct cgpu_info *bitfury; + struct bitfury_info *info; + enum sub_ident ident; + bool ret = false; + + bitfury = usb_alloc_cgpu(&bitfury_drv, 1); + + if (!usb_init(bitfury, dev, found)) + goto out; + applog(LOG_INFO, "%s %d: Found at %s", bitfury->drv->name, + bitfury->device_id, bitfury->device_path); + + info = calloc(sizeof(struct bitfury_info), 1); + if (!info) + quit(1, "Failed to calloc info in bitfury_detect_one"); + bitfury->device_data = info; + info->ident = ident = usb_ident(bitfury); + switch (ident) { + case IDENT_BF1: + ret = bf1_detect_one(bitfury, info); + break; + case IDENT_BXF: + case IDENT_OSM: + ret = bxf_detect_one(bitfury, info); + break; + case IDENT_NFU: + ret = nfu_detect_one(bitfury, info); + break; + case IDENT_BXM: + ret = bxm_detect_one(bitfury, info); + break; + default: + applog(LOG_INFO, "%s %d: Unrecognised bitfury device", + bitfury->drv->name, bitfury->device_id); + break; + } + + if (!ret) { + free(info); + usb_uninit(bitfury); +out: + bitfury = usb_free_cgpu(bitfury); + } + return bitfury; +} + +static void bitfury_detect(bool __maybe_unused hotplug) +{ + usb_detect(&bitfury_drv, bitfury_detect_one); +} + +static void adjust_bxf_chips(struct cgpu_info *bitfury, struct bitfury_info *info, int chip) +{ + int chips = chip + 1; + size_t old, new; + + if (likely(chips <= info->chips)) + return; + if (chips > 999) + return; + old = sizeof(int) * info->chips; + new = sizeof(int) * chips; + applog(LOG_INFO, "%s %d: Adjust chip size to %d", bitfury->drv->name, bitfury->device_id, + chips); + + recalloc(info->filtered_hw, old, new); + recalloc(info->job, old, new); + recalloc(info->submits, old, new); + if (info->chips == 2 && chips <= 6 && info->ident == IDENT_BXF) + bitfury->drv->name = "HXF"; + else if (info->chips <= 6 && chips > 6 && info->ident == IDENT_BXF) + bitfury->drv->name = "MXF"; + info->chips = chips; +} + +static void parse_bxf_submit(struct cgpu_info *bitfury, struct bitfury_info *info, char *buf) +{ + struct work *match_work, *tmp, *work = NULL; + struct thr_info *thr = info->thr; + uint32_t nonce, timestamp; + int workid, chip = -1; + + if (!sscanf(&buf[7], "%x %x %x %d", &nonce, &workid, ×tamp, &chip)) { + applog(LOG_WARNING, "%s %d: Failed to parse submit response", + bitfury->drv->name, bitfury->device_id); + return; + } + adjust_bxf_chips(bitfury, info, chip); + if (unlikely(chip >= info->chips || chip < 0)) { + applog(LOG_INFO, "%s %d: Invalid submit chip number %d", + bitfury->drv->name, bitfury->device_id, chip); + } else + info->submits[chip]++; + + applog(LOG_DEBUG, "%s %d: Parsed nonce %u workid %d timestamp %u", + bitfury->drv->name, bitfury->device_id, nonce, workid, timestamp); + + rd_lock(&bitfury->qlock); + HASH_ITER(hh, bitfury->queued_work, match_work, tmp) { + if (match_work->subid == workid) { + work = copy_work(match_work); + break; + } + } + rd_unlock(&bitfury->qlock); + + if (!work) { + /* Discard first results from any previous run */ + if (unlikely(!info->valid)) + return; + + applog(LOG_INFO, "%s %d: No matching work", bitfury->drv->name, bitfury->device_id); + + mutex_lock(&info->lock); + info->no_matching_work++; + mutex_unlock(&info->lock); + + inc_hw_errors(thr); + return; + } + /* Set the device start time from when we first get valid results */ + if (unlikely(!info->valid)) { + info->valid = true; + cgtime(&bitfury->dev_start_tv); + } + set_work_ntime(work, timestamp); + if (submit_nonce(thr, work, nonce)) { + mutex_lock(&info->lock); + info->nonces++; + mutex_unlock(&info->lock); + } + free_work(work); +} + +static bool bxf_send_clock(struct cgpu_info *bitfury, struct bitfury_info *info, + uint8_t clockspeed) +{ + char buf[64]; + + info->clocks = clockspeed; + sprintf(buf, "clock %d %d\n", clockspeed, clockspeed); + return bxf_send_msg(bitfury, buf, C_BXF_CLOCK); +} + +static void parse_bxf_temp(struct cgpu_info *bitfury, struct bitfury_info *info, char *buf) +{ + uint8_t clockspeed = info->clocks; + int decitemp; + + if (!sscanf(&buf[5], "%d", &decitemp)) { + applog(LOG_INFO, "%s %d: Failed to parse temperature", + bitfury->drv->name, bitfury->device_id); + return; + } + + mutex_lock(&info->lock); + bitfury->temp = (double)decitemp / 10; + if (decitemp > info->max_decitemp) { + info->max_decitemp = decitemp; + applog(LOG_DEBUG, "%s %d: New max decitemp %d", bitfury->drv->name, + bitfury->device_id, decitemp); + } + mutex_unlock(&info->lock); + + if (decitemp > info->temp_target + BXF_TEMP_HYSTERESIS) { + if (info->clocks <= BXF_CLOCK_MIN) + goto out; + applog(LOG_WARNING, "%s %d: Hit overheat temperature of %d, throttling!", + bitfury->drv->name, bitfury->device_id, decitemp); + clockspeed = BXF_CLOCK_MIN; + goto out; + } + if (decitemp > info->temp_target) { + if (info->clocks <= BXF_CLOCK_MIN) + goto out; + if (decitemp < info->last_decitemp) + goto out; + applog(LOG_INFO, "%s %d: Temp %d over target and not falling, decreasing clock", + bitfury->drv->name, bitfury->device_id, decitemp); + clockspeed = info->clocks - 1; + goto out; + } + if (decitemp <= info->temp_target && decitemp >= info->temp_target - BXF_TEMP_HYSTERESIS) { + if (decitemp == info->last_decitemp) + goto out; + if (decitemp > info->last_decitemp) { + if (info->clocks <= BXF_CLOCK_MIN) + goto out; + applog(LOG_DEBUG, "%s %d: Temp %d in target and rising, decreasing clock", + bitfury->drv->name, bitfury->device_id, decitemp); + clockspeed = info->clocks - 1; + goto out; + } + /* implies: decitemp < info->last_decitemp */ + if (info->clocks >= opt_bxf_bits) + goto out; + applog(LOG_DEBUG, "%s %d: Temp %d in target and falling, increasing clock", + bitfury->drv->name, bitfury->device_id, decitemp); + clockspeed = info->clocks + 1; + goto out; + } + /* implies: decitemp < info->temp_target - BXF_TEMP_HYSTERESIS */ + if (info->clocks >= opt_bxf_bits) + goto out; + applog(LOG_DEBUG, "%s %d: Temp %d below target, increasing clock", + bitfury->drv->name, bitfury->device_id, decitemp); + clockspeed = info->clocks + 1; +out: + bxf_send_clock(bitfury, info, clockspeed); + info->last_decitemp = decitemp; +} + +static void bxf_update_work(struct cgpu_info *bitfury, struct bitfury_info *info); + +static void parse_bxf_needwork(struct cgpu_info *bitfury, struct bitfury_info *info, + char *buf) +{ + int needed; + + if (!sscanf(&buf[9], "%d", &needed)) { + applog(LOG_INFO, "%s %d: Failed to parse needwork", + bitfury->drv->name, bitfury->device_id); + return; + } + while (needed-- > 0) + bxf_update_work(bitfury, info); +} + +static void parse_bxf_job(struct cgpu_info *bitfury, struct bitfury_info *info, char *buf) +{ + int job_id, timestamp, chip; + + if (sscanf(&buf[4], "%x %x %x", &job_id, ×tamp, &chip) != 3) { + applog(LOG_INFO, "%s %d: Failed to parse job", + bitfury->drv->name, bitfury->device_id); + return; + } + adjust_bxf_chips(bitfury, info, chip); + if (chip >= info->chips || chip < 0) { + applog(LOG_INFO, "%s %d: Invalid job chip number %d", + bitfury->drv->name, bitfury->device_id, chip); + return; + } + ++info->job[chip]; +} + +static void parse_bxf_hwerror(struct cgpu_info *bitfury, struct bitfury_info *info, char *buf) +{ + int chip; + + if (!sscanf(&buf[8], "%d", &chip)) { + applog(LOG_INFO, "%s %d: Failed to parse hwerror", + bitfury->drv->name, bitfury->device_id); + return; + } + adjust_bxf_chips(bitfury, info, chip); + if (chip >= info->chips || chip < 0) { + applog(LOG_INFO, "%s %d: Invalid hwerror chip number %d", + bitfury->drv->name, bitfury->device_id, chip); + return; + } + ++info->filtered_hw[chip]; +} + +#define PARSE_BXF_MSG(MSG) \ + msg = strstr(buf, #MSG); \ + if (msg) { \ + parse_bxf_##MSG(bitfury, info, msg); \ + continue; \ + } + +static void *bxf_get_results(void *userdata) +{ + struct cgpu_info *bitfury = userdata; + struct bitfury_info *info = bitfury->device_data; + char threadname[24], buf[512]; + + snprintf(threadname, 24, "bxf_recv/%d", bitfury->device_id); + + /* We operate the device at lowest diff since it's not a lot of results + * to process and gives us a better indicator of the nonce return rate + * and hardware errors. */ + sprintf(buf, "target ffffffff\n"); + if (!bxf_send_msg(bitfury, buf, C_BXF_TARGET)) + goto out; + + /* Read thread sends the first work item to get the device started + * since it will roll ntime and make work itself from there on. */ + bxf_update_work(bitfury, info); + bxf_update_work(bitfury, info); + + while (likely(!bitfury->shutdown)) { + char *msg, *strbuf; + int err; + + if (unlikely(bitfury->usbinfo.nodev)) + break; + + err = bxf_recv_msg(bitfury, buf); + if (err < 0) { + if (err != LIBUSB_ERROR_TIMEOUT) + break; + continue; + } + if (!err) + continue; + + if (opt_bxf_debug) { + strbuf = str_text(buf); + applog(LOG_ERR, "%s %d: < [%s]", + bitfury->drv->name, bitfury->device_id, strbuf); + free(strbuf); + } + + PARSE_BXF_MSG(submit); + PARSE_BXF_MSG(temp); + PARSE_BXF_MSG(needwork); + PARSE_BXF_MSG(job); + PARSE_BXF_MSG(hwerror); + + if (buf[0] != '#') { + strbuf = str_text(buf); + applog(LOG_DEBUG, "%s %d: Unrecognised string %s", + bitfury->drv->name, bitfury->device_id, strbuf); + free(strbuf); + } + } +out: + return NULL; +} + +static bool bxf_prepare(struct cgpu_info *bitfury, struct bitfury_info *info) +{ + bxf_send_ledmode(bitfury); + bxf_send_debugmode(bitfury); + + mutex_init(&info->lock); + if (pthread_create(&info->read_thr, NULL, bxf_get_results, (void *)bitfury)) + quit(1, "Failed to create bxf read_thr"); + + return bxf_send_clock(bitfury, info, opt_bxf_bits); +} + +static bool bitfury_prepare(struct thr_info *thr) +{ + struct cgpu_info *bitfury = thr->cgpu; + struct bitfury_info *info = bitfury->device_data; + + info->thr = thr; + + switch(info->ident) { + case IDENT_BXF: + case IDENT_OSM: + return bxf_prepare(bitfury, info); + break; + case IDENT_BF1: + default: + return true; + } +} + +static int64_t bitfury_rate(struct bitfury_info *info) +{ + double nonce_rate; + int64_t ret = 0; + + info->cycles++; + info->total_nonces += info->nonces; + info->saved_nonces += info->nonces; + info->nonces = 0; + nonce_rate = (double)info->total_nonces / (double)info->cycles; + if (info->saved_nonces >= nonce_rate) { + info->saved_nonces -= nonce_rate; + ret = (double)0xffffffff * nonce_rate; + } + return ret; +} + +static int64_t bf1_scan(struct thr_info *thr, struct cgpu_info *bitfury, + struct bitfury_info *info) +{ + int amount, i, aged, total = 0, ms_diff; + char readbuf[512], buf[45]; + struct work *work, *tmp; + struct timeval tv_now; + int64_t ret = 0; + + work = get_queue_work(thr, bitfury, thr->id); + if (unlikely(thr->work_restart)) { + work_completed(bitfury, work); + goto out; + } + + buf[0] = 'W'; + memcpy(buf + 1, work->midstate, 32); + memcpy(buf + 33, work->data + 64, 12); + + /* New results may spill out from the latest work, making us drop out + * too early so read whatever we get for the first half nonce and then + * look for the results to prev work. */ + cgtime(&tv_now); + ms_diff = 600 - ms_tdiff(&tv_now, &info->tv_start); + if (ms_diff > 0) { + usb_read_timeout_cancellable(bitfury, readbuf, 512, &amount, ms_diff, + C_BF1_GETRES); + total += amount; + } + + /* Now look for the bulk of the previous work results, they will come + * in a batch following the first data. */ + cgtime(&tv_now); + ms_diff = BF1WAIT - ms_tdiff(&tv_now, &info->tv_start); + /* If a work restart was sent, just empty the buffer. */ + if (unlikely(ms_diff < 10 || thr->work_restart)) + ms_diff = 10; + usb_read_once_timeout_cancellable(bitfury, readbuf + total, BF1MSGSIZE, + &amount, ms_diff, C_BF1_GETRES); + total += amount; + while (amount) { + usb_read_once_timeout(bitfury, readbuf + total, 512 - total, &amount, 10, + C_BF1_GETRES); + total += amount; + }; + + /* Don't send whatever work we've stored if we got a restart */ + if (unlikely(thr->work_restart)) + goto out; + + /* Send work */ + cgtime(&work->tv_work_start); + usb_write(bitfury, buf, 45, &amount, C_BF1_REQWORK); + cgtime(&info->tv_start); + + /* Get response acknowledging work */ + usb_read(bitfury, buf, BF1MSGSIZE, &amount, C_BF1_GETWORK); + +out: + /* Search for what work the nonce matches in order of likelihood. Last + * entry is end of result marker. */ + for (i = 0; i < total - BF1MSGSIZE; i += BF1MSGSIZE) { + bool found = false; + uint32_t nonce; + + /* Ignore state & switched data in results for now. */ + memcpy(&nonce, readbuf + i + 3, 4); + nonce = decnonce(nonce); + + rd_lock(&bitfury->qlock); + HASH_ITER(hh, bitfury->queued_work, work, tmp) { + if (bitfury_checkresults(thr, work, nonce)) { + info->nonces++; + found = true; + break; + } + } + rd_unlock(&bitfury->qlock); + + if (!found) { + if (likely(info->valid)) + inc_hw_errors(thr); + } else if (unlikely(!info->valid)) { + info->valid = true; + cgtime(&bitfury->dev_start_tv); + } + } + + cgtime(&tv_now); + + /* This iterates over the hashlist finding work started more than 6 + * seconds ago. */ + aged = age_queued_work(bitfury, 6.0); + if (aged) { + applog(LOG_DEBUG, "%s %d: Aged %d work items", bitfury->drv->name, + bitfury->device_id, aged); + } + + ret = bitfury_rate(info); + + if (unlikely(bitfury->usbinfo.nodev)) { + applog(LOG_WARNING, "%s %d: Device disappeared, disabling thread", + bitfury->drv->name, bitfury->device_id); + ret = -1; + } + return ret; +} + +static int64_t bxf_scan(struct cgpu_info *bitfury, struct bitfury_info *info) +{ + int ms, aged; + int64_t ret; + + bxf_update_work(bitfury, info); + ms = 1200 / info->chips; + if (ms < 100) + ms = 100; + cgsleep_ms(ms); + + mutex_lock(&info->lock); + ret = bitfury_rate(info); + mutex_unlock(&info->lock); + + /* Keep no more than the last 90 seconds worth of work items in the + * hashlist */ + aged = age_queued_work(bitfury, 90.0); + if (aged) { + applog(LOG_DEBUG, "%s %d: Aged %d work items", bitfury->drv->name, + bitfury->device_id, aged); + } + + if (unlikely(bitfury->usbinfo.nodev)) { + applog(LOG_WARNING, "%s %d: Device disappeared, disabling thread", + bitfury->drv->name, bitfury->device_id); + ret = -1; + } + return ret; +} + +static void bitfury_check_work(struct thr_info *thr, struct cgpu_info *bitfury, + struct bitfury_info *info, int chip_n) +{ + if (!info->work[chip_n]) { + info->work[chip_n] = get_work(thr, thr->id); + if (unlikely(thr->work_restart)) { + free_work(info->work[chip_n]); + info->work[chip_n] = NULL; + return; + } + bitfury_work_to_payload(&info->payload[chip_n], info->work[chip_n]); + } + + if (unlikely(bitfury->usbinfo.nodev)) + return; + + if (!libbitfury_sendHashData(thr, bitfury, info, chip_n)) + usb_nodev(bitfury); + + if (info->job_switched[chip_n]) { + if (likely(info->owork[chip_n])) + free_work(info->owork[chip_n]); + info->owork[chip_n] = info->work[chip_n]; + info->work[chip_n] = NULL; + } + +} + +static int64_t nfu_scan(struct thr_info *thr, struct cgpu_info *bitfury, + struct bitfury_info *info) +{ + int64_t ret = 0; + int i; + + for (i = 0; i < info->chips; i++) + bitfury_check_work(thr, bitfury, info, i); + + ret = bitfury_rate(info); + + if (unlikely(bitfury->usbinfo.nodev)) { + applog(LOG_WARNING, "%s %d: Device disappeared, disabling thread", + bitfury->drv->name, bitfury->device_id); + ret = -1; + } + + return ret; +} + +static int64_t bitfury_scanwork(struct thr_info *thr) +{ + struct cgpu_info *bitfury = thr->cgpu; + struct bitfury_info *info = bitfury->device_data; + int64_t ret = -1; + + if (unlikely(share_work_tdiff(bitfury) > 60)) { + if (info->failing) { + if (share_work_tdiff(bitfury) > 120) { + applog(LOG_ERR, "%s %d: Device failed to respond to restart", + bitfury->drv->name, bitfury->device_id); + return ret; + } + } else { + applog(LOG_WARNING, "%s %d: No valid hashes for over 1 minute, attempting to reset", + bitfury->drv->name, bitfury->device_id); + usb_reset(bitfury); + info->failing = true; + } + } + + if (unlikely(bitfury->usbinfo.nodev)) + return ret; + + switch(info->ident) { + case IDENT_BF1: + ret = bf1_scan(thr, bitfury, info); + break; + case IDENT_BXF: + case IDENT_OSM: + ret = bxf_scan(bitfury, info); + break; + case IDENT_NFU: + case IDENT_BXM: + ret = nfu_scan(thr, bitfury, info); + break; + default: + ret = 0; + break; + } + if (ret > 0) + info->failing = false; + return ret; +} + +static void bxf_send_maxroll(struct cgpu_info *bitfury, int maxroll) +{ + char buf[20]; + + sprintf(buf, "maxroll %d\n", maxroll); + bxf_send_msg(bitfury, buf, C_BXF_MAXROLL); +} + +static bool bxf_send_work(struct cgpu_info *bitfury, struct work *work) +{ + char buf[512], hexwork[156]; + + __bin2hex(hexwork, work->data, 76); + sprintf(buf, "work %s %x\n", hexwork, work->subid); + return bxf_send_msg(bitfury, buf, C_BXF_WORK); +} + +static void bxf_update_work(struct cgpu_info *bitfury, struct bitfury_info *info) +{ + struct thr_info *thr = info->thr; + struct work *work; + + if (unlikely(bitfury->usbinfo.nodev)) + return; + + work = get_queue_work(thr, bitfury, thr->id); + if (work->drv_rolllimit != info->maxroll) { + info->maxroll = work->drv_rolllimit; + bxf_send_maxroll(bitfury, info->maxroll); + } + + mutex_lock(&info->lock); + work->subid = ++info->work_id; + mutex_unlock(&info->lock); + + cgtime(&work->tv_work_start); + bxf_send_work(bitfury, work); +} + +static void bitfury_flush_work(struct cgpu_info *bitfury) +{ + struct bitfury_info *info = bitfury->device_data; + + switch(info->ident) { + case IDENT_BXF: + case IDENT_OSM: + bxf_send_flush(bitfury); + bxf_update_work(bitfury, info); + bxf_update_work(bitfury, info); + case IDENT_BF1: + default: + break; + } +} + +static void bitfury_update_work(struct cgpu_info *bitfury) +{ + struct bitfury_info *info = bitfury->device_data; + + switch(info->ident) { + case IDENT_BXF: + case IDENT_OSM: + bxf_update_work(bitfury, info); + case IDENT_BF1: + default: + break; + } +} + +static struct api_data *bf1_api_stats(struct bitfury_info *info) +{ + struct api_data *root = NULL; + double nonce_rate; + char serial[16]; + int version; + + version = info->version; + root = api_add_int(root, "Version", &version, true); + root = api_add_string(root, "Product", info->product, false); + sprintf(serial, "%08x", info->serial); + root = api_add_string(root, "Serial", serial, true); + nonce_rate = (double)info->total_nonces / (double)info->cycles; + root = api_add_double(root, "NonceRate", &nonce_rate, true); + + return root; +} + +static struct api_data *bxf_api_stats(struct cgpu_info *bitfury, struct bitfury_info *info) +{ + struct api_data *root = NULL; + double nonce_rate; + char buf[32]; + int i; + + sprintf(buf, "%d.%d", info->ver_major, info->ver_minor); + root = api_add_string(root, "Version", buf, true); + root = api_add_int(root, "Revision", &info->hw_rev, false); + root = api_add_int(root, "Chips", &info->chips, false); + nonce_rate = (double)info->total_nonces / (double)info->cycles; + root = api_add_double(root, "NonceRate", &nonce_rate, true); + root = api_add_int(root, "NoMatchingWork", &info->no_matching_work, false); + root = api_add_double(root, "Temperature", &bitfury->temp, false); + root = api_add_int(root, "Max DeciTemp", &info->max_decitemp, false); + root = api_add_uint8(root, "Clock", &info->clocks, false); + for (i = 0; i < info->chips; i++) { + sprintf(buf, "Core%d hwerror", i); + root = api_add_int(root, buf, &info->filtered_hw[i], false); + sprintf(buf, "Core%d jobs", i); + root = api_add_int(root, buf, &info->job[i], false); + sprintf(buf, "Core%d submits", i); + root = api_add_int(root, buf, &info->submits[i], false); + } + + return root; +} + +static struct api_data *nfu_api_stats(struct bitfury_info *info) +{ + struct api_data *root = NULL; + char buf[32]; + int i; + + root = api_add_int(root, "Chips", &info->chips, false); + for (i = 0; i < info->chips; i++) { + sprintf(buf, "Core%d submits", i); + root = api_add_int(root, buf, &info->submits[i], false); + } + return root; +} + +static struct api_data *bitfury_api_stats(struct cgpu_info *cgpu) +{ + struct bitfury_info *info = cgpu->device_data; + + switch(info->ident) { + case IDENT_BF1: + return bf1_api_stats(info); + break; + case IDENT_BXF: + case IDENT_OSM: + return bxf_api_stats(cgpu, info); + break; + case IDENT_NFU: + case IDENT_BXM: + return nfu_api_stats(info); + break; + default: + break; + } + return NULL; +} + +static void bitfury_get_statline_before(char *buf, size_t bufsiz, struct cgpu_info *cgpu) +{ + struct bitfury_info *info = cgpu->device_data; + + switch(info->ident) { + case IDENT_BXF: + case IDENT_OSM: + tailsprintf(buf, bufsiz, "%5.1fC", cgpu->temp); + break; + default: + break; + } +} + +static void bf1_init(struct cgpu_info *bitfury) +{ + bf1_close(bitfury); + bf1_open(bitfury); + bf1_reset(bitfury); +} + +static void bitfury_init(struct cgpu_info *bitfury) +{ + struct bitfury_info *info = bitfury->device_data; + + switch(info->ident) { + case IDENT_BF1: + bf1_init(bitfury); + break; + default: + break; + } +} + +static void bxf_close(struct bitfury_info *info) +{ + pthread_join(info->read_thr, NULL); + mutex_destroy(&info->lock); +} + +static void bitfury_shutdown(struct thr_info *thr) +{ + struct cgpu_info *bitfury = thr->cgpu; + struct bitfury_info *info = bitfury->device_data; + + switch(info->ident) { + case IDENT_BF1: + bf1_close(bitfury); + break; + case IDENT_BXF: + case IDENT_OSM: + bxf_close(info); + break; + case IDENT_NFU: + nfu_close(bitfury); + break; + case IDENT_BXM: + bxm_close(bitfury, info); + break; + default: + break; + } + usb_nodev(bitfury); +} + +/* Currently hardcoded to BF1 devices */ +struct device_drv bitfury_drv = { + .drv_id = DRIVER_bitfury, + .dname = "bitfury", + .name = "BF1", + .drv_detect = bitfury_detect, + .thread_prepare = bitfury_prepare, + .hash_work = &hash_driver_work, + .scanwork = bitfury_scanwork, + .flush_work = bitfury_flush_work, + .update_work = bitfury_update_work, + .get_api_stats = bitfury_api_stats, + .get_statline_before = bitfury_get_statline_before, + .reinit_device = bitfury_init, + .thread_shutdown = bitfury_shutdown, + .identify_device = bitfury_identify +}; diff --git a/driver-bitfury.h b/driver-bitfury.h new file mode 100644 index 0000000000..6cbbc0ae7c --- /dev/null +++ b/driver-bitfury.h @@ -0,0 +1,116 @@ +/* + * Copyright 2013-2014 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 BITFURY_H +#define BITFURY_H + +#include "miner.h" +#include "usbutils.h" +#include "mcp2210.h" + +#define BXF_CLOCK_OFF 0 +#define BXF_CLOCK_MIN 32 +#define BXF_CLOCK_MAX 63 // Not really used since we only get hw errors above default + +/* In tenths of a degree */ +#define BXF_TEMP_TARGET 820 +#define BXF_TEMP_HYSTERESIS 30 + +extern int opt_bxf_temp_target; +extern int opt_nfu_bits; +extern int opt_bxm_bits; +extern int opt_bxf_bits; +extern int opt_bxf_debug; +extern int opt_osm_led_mode; + +#define NFU_PIN_LED 0 +#define NFU_PIN_SCK_OVR 5 +#define NFU_PIN_PWR_EN 6 +#define NFU_PIN_PWR_EN0 7 + +#define SPIBUF_SIZE 16384 +#define BITFURY_REFRESH_DELAY 100 + +#define SIO_RESET_REQUEST 0 +#define SIO_SET_LATENCY_TIMER_REQUEST 0x09 +#define SIO_SET_EVENT_CHAR_REQUEST 0x06 +#define SIO_SET_ERROR_CHAR_REQUEST 0x07 +#define SIO_SET_BITMODE_REQUEST 0x0B +#define SIO_RESET_PURGE_RX 1 +#define SIO_RESET_PURGE_TX 2 + +#define BITMODE_RESET 0x00 +#define BITMODE_MPSSE 0x02 +#define SIO_RESET_SIO 0 + +#define BXM_LATENCY_MS 2 + +struct bitfury_payload { + unsigned char midstate[32]; + unsigned int junk[8]; + unsigned m7; + unsigned ntime; + unsigned nbits; + unsigned nnonce; +}; + +struct bitfury_info { + struct cgpu_info *base_cgpu; + struct thr_info *thr; + enum sub_ident ident; + int nonces; + int total_nonces; + double saved_nonces; + int cycles; + bool valid; /* Set on first valid data being found */ + bool failing; /* Set when an attempted restart has been sent */ + + int chips; + char product[8]; + + /* BF1 specific data */ + uint8_t version; + uint32_t serial; + struct timeval tv_start; + + /* BXF specific data */ + pthread_mutex_t lock; + pthread_t read_thr; + int last_decitemp; + int max_decitemp; + int temp_target; + int work_id; // Current work->subid + int no_matching_work; + int maxroll; // Last maxroll sent to device + int ver_major; + int ver_minor; + int hw_rev; + uint8_t clocks; // There are two but we set them equal + int *filtered_hw; // Hardware errors we're told about but are filtered + int *job; // Completed jobs we're told about + int *submits; // Submitted responses + + /* NFU specific data */ + struct mcp_settings mcp; + char spibuf[SPIBUF_SIZE]; + unsigned int spibufsz; + int osc6_bits; + + /* Chip sized arrays */ + struct bitfury_payload *payload; + unsigned int *oldbuf; // 17 vals per chip + bool *job_switched; + bool *second_run; + struct work **work; + struct work **owork; + + bool (*spi_txrx)(struct cgpu_info *, struct bitfury_info *info); +}; + +#endif /* BITFURY_H */ diff --git a/driver-bitmain.c b/driver-bitmain.c new file mode 100644 index 0000000000..0b794dc96c --- /dev/null +++ b/driver-bitmain.c @@ -0,0 +1,2463 @@ +/* + * Copyright 2012-2013 Lingchao Xu + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the Free + * Software Foundation; either version 3 of the License, or (at your option) + * any later version. See COPYING for more details. + */ + +#include "config.h" + +#include +#include +#include +#include +#include +#include +#include +#ifndef WIN32 + #include + #include + #include + #include + #ifndef O_CLOEXEC + #define O_CLOEXEC 0 + #endif +#else + #include "compat.h" + #include + #include +#endif + +#include "elist.h" +#include "miner.h" +#include "usbutils.h" +#include "driver-bitmain.h" +#include "hexdump.c" +#include "util.h" + +#define BITMAIN_CALC_DIFF1 1 + +#ifdef WIN32 +#define BITMAIN_TEST +#endif + +#define BITMAIN_TEST_PRINT_WORK 0 +#ifdef BITMAIN_TEST +#define BITMAIN_TEST_NUM 19 +#define BITMAIN_TEST_USENUM 1 +int g_test_index = 0; +const char btm_work_test_data[BITMAIN_TEST_NUM][256] = { + "00000002ddc1ce5579dbec17f17fbb8f31ae218a814b2a0c1900f0d90000000100000000b58aa6ca86546b07a5a46698f736c7ca9c0eedc756d8f28ac33c20cc24d792675276f879190afc85b6888022000000800000000000000000000000000000000000000000000000000000000000000000", + "0000000256ccc4c8aeae2b1e41490bc352893605f284e4be043f7b190000000000000000eb2d45233c5b02de50ddcb9049ba16040e0ba00e9750a474eec75891571d925b52dfda4a190266667145b02f000000800000000000000000000000000000000000000000000000000000000000000000", + "0000000256ccc4c8aeae2b1e41490bc352893605f284e4be043f7b19000000000000000090c7d3743e0b0562e4f56d3dd35cece3c5e8275d0abb21bf7e503cb72bd7ed3b52dfda4a190266667bbb58d7000000800000000000000000000000000000000000000000000000000000000000000000", + "0000000256ccc4c8aeae2b1e41490bc352893605f284e4be043f7b1900000000000000006e0561da06022bfbb42c5ecd74a46bfd91934f201b777e9155cc6c3674724ec652dfda4a19026666a0cd827b000000800000000000000000000000000000000000000000000000000000000000000000", + "0000000256ccc4c8aeae2b1e41490bc352893605f284e4be043f7b1900000000000000000312f42ce4964cc23f2d8c039f106f25ddd58e10a1faed21b3bba4b0e621807b52dfda4a1902666629c9497d000000800000000000000000000000000000000000000000000000000000000000000000", + "0000000256ccc4c8aeae2b1e41490bc352893605f284e4be043f7b19000000000000000033093a6540dbe8f7f3d19e3d2af05585ac58dafad890fa9a942e977334a23d6e52dfda4a190266665ae95079000000800000000000000000000000000000000000000000000000000000000000000000", + "0000000256ccc4c8aeae2b1e41490bc352893605f284e4be043f7b190000000000000000bd7893057d06e69705bddf9a89c7bac6b40c5b32f15e2295fc8c5edf491ea24952dfda4a190266664b89b4d3000000800000000000000000000000000000000000000000000000000000000000000000", + "0000000256ccc4c8aeae2b1e41490bc352893605f284e4be043f7b19000000000000000075e66f533e53837d14236a793ee4e493985642bc39e016b9e63adf14a584a2aa52dfda4a19026666ab5d638d000000800000000000000000000000000000000000000000000000000000000000000000", + "0000000256ccc4c8aeae2b1e41490bc352893605f284e4be043f7b190000000000000000d936f90c5db5f0fe1d017344443854fbf9e40a07a9b7e74fedc8661c23162bff52dfda4a19026666338e79cb000000800000000000000000000000000000000000000000000000000000000000000000", + "0000000256ccc4c8aeae2b1e41490bc352893605f284e4be043f7b190000000000000000d2c1a7d279a4355b017bc0a4b0a9425707786729f21ee18add3fda4252a31a4152dfda4a190266669bc90806000000800000000000000000000000000000000000000000000000000000000000000000", + "0000000256ccc4c8aeae2b1e41490bc352893605f284e4be043f7b190000000000000000ad36d19f33d04ca779942843890bc3b083cec83a4b60b6c45cf7d21fc187746552dfda4a1902666675d81ab7000000800000000000000000000000000000000000000000000000000000000000000000", + "0000000256ccc4c8aeae2b1e41490bc352893605f284e4be043f7b19000000000000000093b809cf82b76082eacb55bc35b79f31882ed0976fd102ef54783cd24341319b52dfda4a1902666642ab4e42000000800000000000000000000000000000000000000000000000000000000000000000", + "0000000256ccc4c8aeae2b1e41490bc352893605f284e4be043f7b1900000000000000007411ff315430a7bbf41de8a685d457e82d5177c05640d6a4436a40f39e99667852dfda4a190266662affa4b5000000800000000000000000000000000000000000000000000000000000000000000000", + "0000000256ccc4c8aeae2b1e41490bc352893605f284e4be043f7b1900000000000000001ad0db5b9e1e2b57c8d3654c160f5a51067521eab7e340a270639d97f00a3fa252dfda4a1902666601a47bb6000000800000000000000000000000000000000000000000000000000000000000000000", + "0000000256ccc4c8aeae2b1e41490bc352893605f284e4be043f7b19000000000000000022e055c442c46bbe16df68603a26891f6e4cf85b90102b39fd7cadb602b4e34552dfda4a1902666695d33cea000000800000000000000000000000000000000000000000000000000000000000000000", + "0000000256ccc4c8aeae2b1e41490bc352893605f284e4be043f7b1900000000000000009c8baf5a8a1e16de2d6ae949d5fec3ed751f10dcd4c99810f2ce08040fb9e31d52dfda4a19026666fe78849d000000800000000000000000000000000000000000000000000000000000000000000000", + "0000000256ccc4c8aeae2b1e41490bc352893605f284e4be043f7b190000000000000000e5655532b414887f35eb4652bc7b11ebac12891f65bc08cbe0ce5b277b9e795152dfda4a19026666fcc0d1d1000000800000000000000000000000000000000000000000000000000000000000000000", + "0000000256ccc4c8aeae2b1e41490bc352893605f284e4be043f7b190000000000000000f272c5508704e2b62dd1c30ea970372c40bf00f9203f9bf69d456b4a7fbfffe352dfda4a19026666c03d4399000000800000000000000000000000000000000000000000000000000000000000000000", + "0000000256ccc4c8aeae2b1e41490bc352893605f284e4be043f7b190000000000000000fca3b4531ba627ad9b0e23cdd84c888952c23810df196e9c6db0bcecba6a830952dfda4a19026666c14009cb000000800000000000000000000000000000000000000000000000000000000000000000" +}; +const char btm_work_test_midstate[BITMAIN_TEST_NUM][256] = { + "2d8738e7f5bcf76dcb8316fec772e20e240cd58c88d47f2d3f5a6a9547ed0a35", + "d31b6ce09c0bfc2af6f3fe3a03475ebefa5aa191fa70a327a354b2c22f9692f1", + "84a8c8224b80d36caeb42eff2a100f634e1ff873e83fd02ef1306a34abef9dbe", + "059882159439b9b32968c79a93c5521e769dbea9d840f56c2a17b9ad87e530b8", + "17fa435d05012574f8f1da26994cc87b6cb9660b5e82072dc6a0881cec150a0d", + "92a28cc5ec4ba6a2688471dfe2032b5fe97c805ca286c503e447d6749796c6af", + "1677a03516d6e9509ac37e273d2482da9af6e077abe8392cdca6a30e916a7ae9", + "50bbe09f1b8ac18c97aeb745d5d2c3b5d669b6ac7803e646f65ac7b763a392d1", + "e46a0022ebdc303a7fb1a0ebfa82b523946c312e745e5b8a116b17ae6b4ce981", + "8f2f61e7f5b4d76d854e6d266acfff4d40347548216838ccc4ef3b9e43d3c9ea", + "0a450588ae99f75d676a08d0326e1ea874a3497f696722c78a80c7b6ee961ea6", + "3c4c0fc2cf040b806c51b46de9ec0dcc678a7cc5cf3eff11c6c03de3bc7818cc", + "f6c7c785ab5daddb8f98e5f854f2cb41879fcaf47289eb2b4196fefc1b28316f", + "005312351ccb0d0794779f5023e4335b5cad221accf0dfa3da7b881266fa9f5a", + "7b26d189c6bba7add54143179aadbba7ccaeff6887bd8d5bec9597d5716126e6", + "a4718f4c801e7ddf913a9474eb71774993525684ffea1915f767ab16e05e6889", + "6b6226a8c18919d0e55684638d33a6892a00d22492cc2f5906ca7a4ac21c74a7", + "383114dccd1cb824b869158aa2984d157fcb02f46234ceca65943e919329e697", + "d4d478df3016852b27cb1ae9e1e98d98617f8d0943bf9dc1217f47f817236222" +}; +#endif + +char opt_bitmain_dev[256] = {0}; +bool opt_bitmain_hwerror = false; +bool opt_bitmain_checkall = false; +bool opt_bitmain_checkn2diff = false; +bool opt_bitmain_dev_usb = true; +bool opt_bitmain_nobeeper = false; +bool opt_bitmain_notempoverctrl = false; +bool opt_bitmain_homemode = false; +int opt_bitmain_temp = BITMAIN_TEMP_TARGET; +int opt_bitmain_overheat = BITMAIN_TEMP_OVERHEAT; +int opt_bitmain_fan_min = BITMAIN_DEFAULT_FAN_MIN_PWM; +int opt_bitmain_fan_max = BITMAIN_DEFAULT_FAN_MAX_PWM; +int opt_bitmain_freq_min = BITMAIN_MIN_FREQUENCY; +int opt_bitmain_freq_max = BITMAIN_MAX_FREQUENCY; +bool opt_bitmain_auto; + +static int option_offset = -1; + +// -------------------------------------------------------------- +// CRC16 check table +// -------------------------------------------------------------- +const uint8_t chCRCHTalbe[] = // CRC high byte table +{ + 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0, 0x80, 0x41, + 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, + 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0, 0x80, 0x41, + 0x00, 0xC1, 0x81, 0x40, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, + 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0, 0x80, 0x41, + 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, + 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, + 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, + 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0, 0x80, 0x41, + 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, + 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0, 0x80, 0x41, + 0x00, 0xC1, 0x81, 0x40, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, + 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0, 0x80, 0x41, + 0x00, 0xC1, 0x81, 0x40, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, + 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, + 0x00, 0xC1, 0x81, 0x40, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, + 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0, 0x80, 0x41, + 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, + 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0, 0x80, 0x41, + 0x00, 0xC1, 0x81, 0x40, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, + 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0, 0x80, 0x41, + 0x00, 0xC1, 0x81, 0x40 +}; + +const uint8_t chCRCLTalbe[] = // CRC low byte table +{ + 0x00, 0xC0, 0xC1, 0x01, 0xC3, 0x03, 0x02, 0xC2, 0xC6, 0x06, 0x07, 0xC7, + 0x05, 0xC5, 0xC4, 0x04, 0xCC, 0x0C, 0x0D, 0xCD, 0x0F, 0xCF, 0xCE, 0x0E, + 0x0A, 0xCA, 0xCB, 0x0B, 0xC9, 0x09, 0x08, 0xC8, 0xD8, 0x18, 0x19, 0xD9, + 0x1B, 0xDB, 0xDA, 0x1A, 0x1E, 0xDE, 0xDF, 0x1F, 0xDD, 0x1D, 0x1C, 0xDC, + 0x14, 0xD4, 0xD5, 0x15, 0xD7, 0x17, 0x16, 0xD6, 0xD2, 0x12, 0x13, 0xD3, + 0x11, 0xD1, 0xD0, 0x10, 0xF0, 0x30, 0x31, 0xF1, 0x33, 0xF3, 0xF2, 0x32, + 0x36, 0xF6, 0xF7, 0x37, 0xF5, 0x35, 0x34, 0xF4, 0x3C, 0xFC, 0xFD, 0x3D, + 0xFF, 0x3F, 0x3E, 0xFE, 0xFA, 0x3A, 0x3B, 0xFB, 0x39, 0xF9, 0xF8, 0x38, + 0x28, 0xE8, 0xE9, 0x29, 0xEB, 0x2B, 0x2A, 0xEA, 0xEE, 0x2E, 0x2F, 0xEF, + 0x2D, 0xED, 0xEC, 0x2C, 0xE4, 0x24, 0x25, 0xE5, 0x27, 0xE7, 0xE6, 0x26, + 0x22, 0xE2, 0xE3, 0x23, 0xE1, 0x21, 0x20, 0xE0, 0xA0, 0x60, 0x61, 0xA1, + 0x63, 0xA3, 0xA2, 0x62, 0x66, 0xA6, 0xA7, 0x67, 0xA5, 0x65, 0x64, 0xA4, + 0x6C, 0xAC, 0xAD, 0x6D, 0xAF, 0x6F, 0x6E, 0xAE, 0xAA, 0x6A, 0x6B, 0xAB, + 0x69, 0xA9, 0xA8, 0x68, 0x78, 0xB8, 0xB9, 0x79, 0xBB, 0x7B, 0x7A, 0xBA, + 0xBE, 0x7E, 0x7F, 0xBF, 0x7D, 0xBD, 0xBC, 0x7C, 0xB4, 0x74, 0x75, 0xB5, + 0x77, 0xB7, 0xB6, 0x76, 0x72, 0xB2, 0xB3, 0x73, 0xB1, 0x71, 0x70, 0xB0, + 0x50, 0x90, 0x91, 0x51, 0x93, 0x53, 0x52, 0x92, 0x96, 0x56, 0x57, 0x97, + 0x55, 0x95, 0x94, 0x54, 0x9C, 0x5C, 0x5D, 0x9D, 0x5F, 0x9F, 0x9E, 0x5E, + 0x5A, 0x9A, 0x9B, 0x5B, 0x99, 0x59, 0x58, 0x98, 0x88, 0x48, 0x49, 0x89, + 0x4B, 0x8B, 0x8A, 0x4A, 0x4E, 0x8E, 0x8F, 0x4F, 0x8D, 0x4D, 0x4C, 0x8C, + 0x44, 0x84, 0x85, 0x45, 0x87, 0x47, 0x46, 0x86, 0x82, 0x42, 0x43, 0x83, + 0x41, 0x81, 0x80, 0x40 +}; + +static uint16_t CRC16(const uint8_t* p_data, uint16_t w_len) +{ + uint8_t chCRCHi = 0xFF; // CRC high byte initialize + uint8_t chCRCLo = 0xFF; // CRC low byte initialize + uint16_t wIndex = 0; // CRC cycling index + + while (w_len--) { + wIndex = chCRCLo ^ *p_data++; + chCRCLo = chCRCHi ^ chCRCHTalbe[wIndex]; + chCRCHi = chCRCLTalbe[wIndex]; + } + return ((chCRCHi << 8) | chCRCLo); +} + +static uint32_t num2bit(int num) { + switch(num) { + case 0: return 0x80000000; + case 1: return 0x40000000; + case 2: return 0x20000000; + case 3: return 0x10000000; + case 4: return 0x08000000; + case 5: return 0x04000000; + case 6: return 0x02000000; + case 7: return 0x01000000; + case 8: return 0x00800000; + case 9: return 0x00400000; + case 10: return 0x00200000; + case 11: return 0x00100000; + case 12: return 0x00080000; + case 13: return 0x00040000; + case 14: return 0x00020000; + case 15: return 0x00010000; + case 16: return 0x00008000; + case 17: return 0x00004000; + case 18: return 0x00002000; + case 19: return 0x00001000; + case 20: return 0x00000800; + case 21: return 0x00000400; + case 22: return 0x00000200; + case 23: return 0x00000100; + case 24: return 0x00000080; + case 25: return 0x00000040; + case 26: return 0x00000020; + case 27: return 0x00000010; + case 28: return 0x00000008; + case 29: return 0x00000004; + case 30: return 0x00000002; + case 31: return 0x00000001; + default: return 0x00000000; + } +} + +static bool get_options(int this_option_offset, int *baud, int *chain_num, + int *asic_num, int *timeout, int *frequency, char * frequency_t, uint8_t * reg_data, uint8_t * voltage, char * voltage_t) +{ + char buf[BUFSIZ+1]; + char *ptr, *comma, *colon, *colon2, *colon3, *colon4, *colon5, *colon6; + size_t max; + int i, tmp; + + if (opt_bitmain_options == NULL) + buf[0] = '\0'; + else { + ptr = opt_bitmain_options; + for (i = 0; i < this_option_offset; i++) { + comma = strchr(ptr, ','); + if (comma == NULL) + break; + ptr = comma + 1; + } + + comma = strchr(ptr, ','); + if (comma == NULL) + max = strlen(ptr); + else + max = comma - ptr; + + if (max > BUFSIZ) + max = BUFSIZ; + strncpy(buf, ptr, max); + buf[max] = '\0'; + } + + if (!(*buf)) + return false; + + colon = strchr(buf, ':'); + if (colon) + *(colon++) = '\0'; + + tmp = atoi(buf); + switch (tmp) { + case 115200: + *baud = 115200; + break; + case 57600: + *baud = 57600; + break; + case 38400: + *baud = 38400; + break; + case 19200: + *baud = 19200; + break; + default: + quit(1, "Invalid bitmain-options for baud (%s) " + "must be 115200, 57600, 38400 or 19200", buf); + } + + if (colon && *colon) { + colon2 = strchr(colon, ':'); + if (colon2) + *(colon2++) = '\0'; + + if (*colon) { + tmp = atoi(colon); + if (tmp > 0) { + *chain_num = tmp; + } else { + quit(1, "Invalid bitmain-options for " + "chain_num (%s) must be 1 ~ %d", + colon, BITMAIN_DEFAULT_CHAIN_NUM); + } + } + + if (colon2 && *colon2) { + colon3 = strchr(colon2, ':'); + if (colon3) + *(colon3++) = '\0'; + + tmp = atoi(colon2); + if (tmp > 0 && tmp <= BITMAIN_DEFAULT_ASIC_NUM) + *asic_num = tmp; + else { + quit(1, "Invalid bitmain-options for " + "asic_num (%s) must be 1 ~ %d", + colon2, BITMAIN_DEFAULT_ASIC_NUM); + } + + if (colon3 && *colon3) { + colon4 = strchr(colon3, ':'); + if (colon4) + *(colon4++) = '\0'; + + tmp = atoi(colon3); + if (tmp > 0 && tmp <= 0xff) + *timeout = tmp; + else { + quit(1, "Invalid bitmain-options for " + "timeout (%s) must be 1 ~ %d", + colon3, 0xff); + } + if (colon4 && *colon4) { + colon5 = strchr(colon4, ':'); + if(colon5) + *(colon5++) = '\0'; + + tmp = atoi(colon4); + if (tmp < BITMAIN_MIN_FREQUENCY || tmp > BITMAIN_MAX_FREQUENCY) { + quit(1, "Invalid bitmain-options for frequency, must be %d <= frequency <= %d", + BITMAIN_MIN_FREQUENCY, BITMAIN_MAX_FREQUENCY); + } else { + *frequency = tmp; + strcpy(frequency_t, colon4); + } + if (colon5 && *colon5) { + colon6 = strchr(colon5, ':'); + if(colon6) + *(colon6++) = '\0'; + + if(strlen(colon5) > 8 || strlen(colon5)%2 != 0 || strlen(colon5)/2 == 0) { + quit(1, "Invalid bitmain-options for reg data, must be hex now: %s", + colon5); + } + memset(reg_data, 0, 4); + if(!hex2bin(reg_data, colon5, strlen(colon5)/2)) { + quit(1, "Invalid bitmain-options for reg data, hex2bin error now: %s", + colon5); + } + + if (colon6 && *colon6) { + if(strlen(colon6) > 4 || strlen(colon6)%2 != 0 || strlen(colon6)/2 == 0) { + quit(1, "Invalid bitmain-options for voltage data, must be hex now: %s", + colon6); + } + memset(voltage, 0, 2); + if(!hex2bin(voltage, colon6, strlen(colon6)/2)) { + quit(1, "Invalid bitmain-options for voltage data, hex2bin error now: %s", + colon5); + } else { + sprintf(voltage_t, "%02x%02x", voltage[0], voltage[1]); + voltage_t[5] = 0; + voltage_t[4] = voltage_t[3]; + voltage_t[3] = voltage_t[2]; + voltage_t[2] = voltage_t[1]; + voltage_t[1] = '.'; + } + } + } + } + } + } + } + return true; +} + +static bool get_option_freq(int *timeout, int *frequency, char * frequency_t, uint8_t * reg_data) +{ + char buf[BUFSIZ+1]; + char *ptr, *comma, *colon, *colon2; + size_t max; + int i, tmp; + + if (opt_bitmain_freq == NULL) + return true; + else { + ptr = opt_bitmain_freq; + + comma = strchr(ptr, ','); + if (comma == NULL) + max = strlen(ptr); + else + max = comma - ptr; + + if (max > BUFSIZ) + max = BUFSIZ; + strncpy(buf, ptr, max); + buf[max] = '\0'; + } + + if (!(*buf)) + return false; + + colon = strchr(buf, ':'); + if (colon) + *(colon++) = '\0'; + + tmp = atoi(buf); + if (tmp > 0 && tmp <= 0xff) + *timeout = tmp; + else { + quit(1, "Invalid bitmain-freq for " + "timeout (%s) must be 1 ~ %d", + buf, 0xff); + } + + if (colon && *colon) { + colon2 = strchr(colon, ':'); + if (colon2) + *(colon2++) = '\0'; + + tmp = atoi(colon); + if (tmp < BITMAIN_MIN_FREQUENCY || tmp > BITMAIN_MAX_FREQUENCY) { + quit(1, "Invalid bitmain-freq for frequency, must be %d <= frequency <= %d", + BITMAIN_MIN_FREQUENCY, BITMAIN_MAX_FREQUENCY); + } else { + *frequency = tmp; + strcpy(frequency_t, colon); + } + + if (colon2 && *colon2) { + if(strlen(colon2) > 8 || strlen(colon2)%2 != 0 || strlen(colon2)/2 == 0) { + quit(1, "Invalid bitmain-freq for reg data, must be hex now: %s", + colon2); + } + memset(reg_data, 0, 4); + if(!hex2bin(reg_data, colon2, strlen(colon2)/2)) { + quit(1, "Invalid bitmain-freq for reg data, hex2bin error now: %s", + colon2); + } + } + } + return true; +} + +static bool get_option_voltage(uint8_t * voltage, char * voltage_t) +{ + if(opt_bitmain_voltage) { + if(strlen(opt_bitmain_voltage) > 4 || strlen(opt_bitmain_voltage)%2 != 0 || strlen(opt_bitmain_voltage)/2 == 0) { + applog(LOG_ERR, "Invalid bitmain-voltage for voltage data, must be hex now: %s,set default_volttage", + opt_bitmain_voltage); + return false; + } + memset(voltage, 0, 2); + if(!hex2bin(voltage, opt_bitmain_voltage, strlen(opt_bitmain_voltage)/2)) { + quit(1, "Invalid bitmain-voltage for voltage data, hex2bin error now: %s", + opt_bitmain_voltage); + } else { + sprintf(voltage_t, "%02x%02x", voltage[0], voltage[1]); + voltage_t[5] = 0; + voltage_t[4] = voltage_t[3]; + voltage_t[3] = voltage_t[2]; + voltage_t[2] = voltage_t[1]; + voltage_t[1] = '.'; + } + } + return true; +} + +static int bitmain_set_txconfig(struct bitmain_txconfig_token *bm, + uint8_t reset, uint8_t fan_eft, uint8_t timeout_eft, uint8_t frequency_eft, + uint8_t voltage_eft, uint8_t chain_check_time_eft, uint8_t chip_config_eft, uint8_t hw_error_eft, + uint8_t beeper_ctrl, uint8_t temp_over_ctrl,uint8_t fan_home_mode, + uint8_t chain_num, uint8_t asic_num, uint8_t fan_pwm_data, uint8_t timeout_data, + uint16_t frequency, uint8_t * voltage, uint8_t chain_check_time, + uint8_t chip_address, uint8_t reg_address, uint8_t * reg_data) +{ + uint16_t crc = 0; + int datalen = 0; + uint8_t version = 0; + uint8_t * sendbuf = (uint8_t *)bm; + if (unlikely(!bm)) { + applog(LOG_WARNING, "bitmain_set_txconfig bitmain_txconfig_token is null"); + return -1; + } + + if (unlikely(timeout_data <= 0 || asic_num <= 0 || chain_num <= 0)) { + applog(LOG_WARNING, "bitmain_set_txconfig parameter invalid timeout_data(%d) asic_num(%d) chain_num(%d)", + timeout_data, asic_num, chain_num); + return -1; + } + + datalen = sizeof(struct bitmain_txconfig_token); + memset(bm, 0, datalen); + + bm->token_type = BITMAIN_TOKEN_TYPE_TXCONFIG; + bm->version = version; + bm->length = datalen-4; + bm->length = htole16(bm->length); + + bm->reset = reset; + bm->fan_eft = fan_eft; + bm->timeout_eft = timeout_eft; + bm->frequency_eft = frequency_eft; + bm->voltage_eft = voltage_eft; + bm->chain_check_time_eft = chain_check_time_eft; + bm->chip_config_eft = chip_config_eft; + bm->hw_error_eft = hw_error_eft; + bm->beeper_ctrl = beeper_ctrl; + bm->temp_over_ctrl = temp_over_ctrl; + bm->fan_home_mode = fan_home_mode; + + sendbuf[4] = htole8(sendbuf[4]); + sendbuf[5] = htole8(sendbuf[5]); + + bm->chain_num = chain_num; + bm->asic_num = asic_num; + bm->fan_pwm_data = fan_pwm_data; + bm->timeout_data = timeout_data; + + bm->frequency = htole16(frequency); + memcpy(bm->voltage, voltage, 2); + bm->chain_check_time = chain_check_time; + + memcpy(bm->reg_data, reg_data, 4); + bm->chip_address = chip_address; + bm->reg_address = reg_address; + + crc = CRC16((uint8_t *)bm, datalen-2); + bm->crc = htole16(crc); + + applog(LOG_ERR, "BTM TxConfigToken:v(%d) reset(%d) fan_e(%d) tout_e(%d) fq_e(%d) vt_e(%d) chainc_e(%d) chipc_e(%d) hw_e(%d) b_c(%d) t_c(%d) f_m(%d) mnum(%d) anum(%d) fanpwmdata(%d) toutdata(%d) freq(%d) volt(%02x%02x) chainctime(%d) regdata(%02x%02x%02x%02x) chipaddr(%02x) regaddr(%02x) crc(%04x)", + version, reset, fan_eft, timeout_eft, frequency_eft, voltage_eft, + chain_check_time_eft, chip_config_eft, hw_error_eft, beeper_ctrl, temp_over_ctrl,fan_home_mode,chain_num, asic_num, + fan_pwm_data, timeout_data, frequency, voltage[0], voltage[1], + chain_check_time, reg_data[0], reg_data[1], reg_data[2], reg_data[3], chip_address, reg_address, crc); + + return datalen; +} + +static int bitmain_set_txtask(uint8_t * sendbuf, + unsigned int * last_work_block, struct work **works, int work_array_size, int work_array, int sendworkcount, int * sendcount) +{ + uint16_t crc = 0; + uint32_t work_id = 0; + uint8_t version = 0; + int datalen = 0; + int i = 0; + int index = work_array; + uint8_t new_block= 0; + char * ob_hex = NULL; + struct bitmain_txtask_token *bm = (struct bitmain_txtask_token *)sendbuf; + *sendcount = 0; + int cursendcount = 0; + int diff = 0; + unsigned int difftmp = 0; + unsigned int pooldiff = 0; + uint64_t netdifftmp = 0; + int netdiff = 0; + if (unlikely(!bm)) { + applog(LOG_WARNING, "bitmain_set_txtask bitmain_txtask_token is null"); + return -1; + } + if (unlikely(!works)) { + applog(LOG_WARNING, "bitmain_set_txtask work is null"); + return -1; + } + memset(bm, 0, sizeof(struct bitmain_txtask_token)); + + bm->token_type = BITMAIN_TOKEN_TYPE_TXTASK; + bm->version = version; + + datalen = 10; + applog(LOG_DEBUG, "BTM send work count %d -----", sendworkcount); + for(i = 0; i < sendworkcount; i++) { + if(index > work_array_size) { + index = 0; + } + if(works[index]) { + if(works[index]->work_block > *last_work_block) { + applog(LOG_ERR, "BTM send task new block %d old(%d)", works[index]->work_block, *last_work_block); + new_block = 1; + *last_work_block = works[index]->work_block; + } +#ifdef BITMAIN_TEST + if(!hex2bin(works[index]->data, btm_work_test_data[g_test_index], 128)) { + applog(LOG_DEBUG, "BTM send task set test data error"); + } + if(!hex2bin(works[index]->midstate, btm_work_test_midstate[g_test_index], 32)) { + applog(LOG_DEBUG, "BTM send task set test midstate error"); + } + g_test_index++; + if(g_test_index >= BITMAIN_TEST_USENUM) { + g_test_index = 0; + } + applog(LOG_DEBUG, "BTM test index = %d", g_test_index); +#endif + work_id = works[index]->id; + bm->works[cursendcount].work_id = htole32(work_id); + applog(LOG_DEBUG, "BTM send task work id:%d %d", bm->works[cursendcount].work_id, work_id); + memcpy(bm->works[cursendcount].midstate, works[index]->midstate, 32); + memcpy(bm->works[cursendcount].data2, works[index]->data + 64, 12); + + if(cursendcount == 0) { + pooldiff = (unsigned int)(works[index]->sdiff); + difftmp = pooldiff; + while(1) { + difftmp = difftmp >> 1; + if(difftmp > 0) { + diff++; + if(diff >= 255) { + break; + } + } else { + break; + } + } + } + + if(BITMAIN_TEST_PRINT_WORK) { + ob_hex = bin2hex(works[index]->data, 76); + applog(LOG_ERR, "work %d data: %s", works[index]->id, ob_hex); + free(ob_hex); + } + + cursendcount++; + } + index++; + } + if(cursendcount <= 0) { + applog(LOG_ERR, "BTM send work count %d", cursendcount); + return 0; + } + + netdifftmp = current_diff; + while(netdifftmp > 0) { + netdifftmp = netdifftmp >> 1; + netdiff++; + } + datalen += 48*cursendcount; + + bm->length = datalen-4; + bm->length = htole16(bm->length); + //len = datalen-3; + //len = htole16(len); + //memcpy(sendbuf+1, &len, 2); + bm->new_block = new_block; + bm->diff = diff; + bm->net_diff = htole16(netdiff); + + sendbuf[4] = htole8(sendbuf[4]); + + applog(LOG_DEBUG, "BitMain TxTask Token: %d %d %02x%02x%02x%02x%02x%02x", + datalen, bm->length, sendbuf[0],sendbuf[1],sendbuf[2],sendbuf[3],sendbuf[4],sendbuf[5]); + + *sendcount = cursendcount; + + crc = CRC16(sendbuf, datalen-2); + crc = htole16(crc); + memcpy(sendbuf+datalen-2, &crc, 2); + + applog(LOG_DEBUG, "BitMain TxTask Token: v(%d) new_block(%d) diff(%d pool:%d net:%d) work_num(%d) crc(%04x)", + version, new_block, diff, pooldiff,netdiff, cursendcount, crc); + applog(LOG_DEBUG, "BitMain TxTask Token: %d %d %02x%02x%02x%02x%02x%02x", + datalen, bm->length, sendbuf[0],sendbuf[1],sendbuf[2],sendbuf[3],sendbuf[4],sendbuf[5]); + + return datalen; +} + +static int bitmain_set_rxstatus(struct bitmain_rxstatus_token *bm, + uint8_t chip_status_eft, uint8_t detect_get, uint8_t chip_address, uint8_t reg_address) +{ + uint16_t crc = 0; + uint8_t version = 0; + int datalen = 0; + uint8_t * sendbuf = (uint8_t *)bm; + + if (unlikely(!bm)) { + applog(LOG_WARNING, "bitmain_set_rxstatus bitmain_rxstatus_token is null"); + return -1; + } + + datalen = sizeof(struct bitmain_rxstatus_token); + memset(bm, 0, datalen); + + bm->token_type = BITMAIN_TOKEN_TYPE_RXSTATUS; + bm->version = version; + bm->length = datalen-4; + bm->length = htole16(bm->length); + + bm->chip_status_eft = chip_status_eft; + bm->detect_get = detect_get; + + sendbuf[4] = htole8(sendbuf[4]); + + bm->chip_address = chip_address; + bm->reg_address = reg_address; + + crc = CRC16((uint8_t *)bm, datalen-2); + bm->crc = htole16(crc); + + applog(LOG_ERR, "BitMain RxStatus Token: v(%d) chip_status_eft(%d) detect_get(%d) chip_address(%02x) reg_address(%02x) crc(%04x)", + version, chip_status_eft, detect_get, chip_address, reg_address, crc); + + return datalen; +} + +static int bitmain_parse_rxstatus(const uint8_t * data, int datalen, struct bitmain_rxstatus_data *bm) +{ + uint16_t crc = 0; + uint8_t version = 0; + int i = 0, j = 0; + int asic_num = 0; + int dataindex = 0; + uint8_t tmp = 0x01; + if (unlikely(!bm)) { + applog(LOG_WARNING, "bitmain_parse_rxstatus bitmain_rxstatus_data is null"); + return -1; + } + if (unlikely(!data || datalen <= 0)) { + applog(LOG_WARNING, "bitmain_parse_rxstatus parameter invalid data is null or datalen(%d) error", datalen); + return -1; + } + memset(bm, 0, sizeof(struct bitmain_rxstatus_data)); + memcpy(bm, data, 28); + if (bm->data_type != BITMAIN_DATA_TYPE_RXSTATUS) { + applog(LOG_ERR, "bitmain_parse_rxstatus datatype(%02x) error", bm->data_type); + return -1; + } + if (bm->version != version) { + applog(LOG_ERR, "bitmain_parse_rxstatus version(%02x) error", bm->version); + return -1; + } + bm->length = htole16(bm->length); + if (bm->length+4 != datalen) { + applog(LOG_ERR, "bitmain_parse_rxstatus length(%d) datalen(%d) error", bm->length, datalen); + return -1; + } + crc = CRC16(data, datalen-2); + memcpy(&(bm->crc), data+datalen-2, 2); + bm->crc = htole16(bm->crc); + if(crc != bm->crc) { + applog(LOG_ERR, "bitmain_parse_rxstatus check crc(%d) != bm crc(%d) datalen(%d)", crc, bm->crc, datalen); + return -1; + } + bm->fifo_space = htole16(bm->fifo_space); + bm->fan_exist = htole16(bm->fan_exist); + bm->temp_exist = htole32(bm->temp_exist); + bm->nonce_error = htole32(bm->nonce_error); + if(bm->chain_num > BITMAIN_MAX_CHAIN_NUM) { + applog(LOG_ERR, "bitmain_parse_rxstatus chain_num=%d error", bm->chain_num); + return -1; + } + dataindex = 28; + if(bm->chain_num > 0) { + memcpy(bm->chain_asic_num, data+datalen-2-bm->chain_num-bm->temp_num-bm->fan_num, bm->chain_num); + } + for(i = 0; i < bm->chain_num; i++) { + asic_num = bm->chain_asic_num[i]; + if(asic_num <= 0) { + asic_num = 1; + } else { + if(asic_num % 32 == 0) { + asic_num = asic_num / 32; + } else { + asic_num = asic_num / 32 + 1; + } + } + memcpy((uint8_t *)bm->chain_asic_exist+i*32, data+dataindex, asic_num*4); + dataindex += asic_num*4; + } + for(i = 0; i < bm->chain_num; i++) { + asic_num = bm->chain_asic_num[i]; + if(asic_num <= 0) { + asic_num = 1; + } else { + if(asic_num % 32 == 0) { + asic_num = asic_num / 32; + } else { + asic_num = asic_num / 32 + 1; + } + } + memcpy((uint8_t *)bm->chain_asic_status+i*32, data+dataindex, asic_num*4); + dataindex += asic_num*4; + } + dataindex += bm->chain_num; + if(dataindex + bm->temp_num + bm->fan_num + 2 != datalen) { + applog(LOG_ERR, "bitmain_parse_rxstatus dataindex(%d) chain_num(%d) temp_num(%d) fan_num(%d) not match datalen(%d)", + dataindex, bm->chain_num, bm->temp_num, bm->fan_num, datalen); + return -1; + } + for(i = 0; i < bm->chain_num; i++) { + //bm->chain_asic_status[i] = swab32(bm->chain_asic_status[i]); + for(j = 0; j < 8; j++) { + bm->chain_asic_exist[i*8+j] = htole32(bm->chain_asic_exist[i*8+j]); + bm->chain_asic_status[i*8+j] = htole32(bm->chain_asic_status[i*8+j]); + } + } + if(bm->temp_num > 0) { + memcpy(bm->temp, data+dataindex, bm->temp_num); + dataindex += bm->temp_num; + } + if(bm->fan_num > 0) { + memcpy(bm->fan, data+dataindex, bm->fan_num); + dataindex += bm->fan_num; + } + if(!opt_bitmain_checkall){ + if(tmp != htole8(tmp)){ + applog(LOG_ERR, "BitMain RxStatus byte4 0x%02x chip_value_eft %d reserved %d get_blk_num %d ",*((uint8_t* )bm +4),bm->chip_value_eft,bm->reserved1,bm->get_blk_num); + memcpy(&tmp,data+4,1); + bm->chip_value_eft = tmp >>7; + bm->get_blk_num = tmp >> 4; + bm->reserved1 = ((tmp << 4) & 0xff) >> 5; + } + found_blocks = bm->get_blk_num; + applog(LOG_ERR, "BitMain RxStatus tmp :0x%02x byte4 0x%02x chip_value_eft %d reserved %d get_blk_num %d ",tmp,*((uint8_t* )bm +4),bm->chip_value_eft,bm->reserved1,bm->get_blk_num); + } + applog(LOG_DEBUG, "BitMain RxStatusData: chipv_e(%d) chainnum(%d) fifos(%d) v1(%d) v2(%d) v3(%d) v4(%d) fann(%d) tempn(%d) fanet(%04x) tempet(%08x) ne(%d) regvalue(%d) crc(%04x)", + bm->chip_value_eft, bm->chain_num, bm->fifo_space, bm->hw_version[0], bm->hw_version[1], bm->hw_version[2], bm->hw_version[3], bm->fan_num, bm->temp_num, bm->fan_exist, bm->temp_exist, bm->nonce_error, bm->reg_value, bm->crc); + applog(LOG_DEBUG, "BitMain RxStatus Data chain info:"); + for(i = 0; i < bm->chain_num; i++) { + applog(LOG_DEBUG, "BitMain RxStatus Data chain(%d) asic num=%d asic_exist=%08x asic_status=%08x", i+1, bm->chain_asic_num[i], bm->chain_asic_exist[i*8], bm->chain_asic_status[i*8]); + } + applog(LOG_DEBUG, "BitMain RxStatus Data temp info:"); + for(i = 0; i < bm->temp_num; i++) { + applog(LOG_DEBUG, "BitMain RxStatus Data temp(%d) temp=%d", i+1, bm->temp[i]); + } + applog(LOG_DEBUG, "BitMain RxStatus Data fan info:"); + for(i = 0; i < bm->fan_num; i++) { + applog(LOG_DEBUG, "BitMain RxStatus Data fan(%d) fan=%d", i+1, bm->fan[i]); + } + return 0; +} + +static int bitmain_parse_rxnonce(const uint8_t * data, int datalen, struct bitmain_rxnonce_data *bm, int * nonce_num) +{ + int i = 0; + uint16_t crc = 0; + uint8_t version = 0; + int curnoncenum = 0; + if (unlikely(!bm)) { + applog(LOG_ERR, "bitmain_parse_rxnonce bitmain_rxstatus_data null"); + return -1; + } + if (unlikely(!data || datalen <= 0)) { + applog(LOG_ERR, "bitmain_parse_rxnonce data null or datalen(%d) error", datalen); + return -1; + } + memcpy(bm, data, sizeof(struct bitmain_rxnonce_data)); + if (bm->data_type != BITMAIN_DATA_TYPE_RXNONCE) { + applog(LOG_ERR, "bitmain_parse_rxnonce datatype(%02x) error", bm->data_type); + return -1; + } + if (bm->version != version) { + applog(LOG_ERR, "bitmain_parse_rxnonce version(%02x) error", bm->version); + return -1; + } + bm->length = htole16(bm->length); + if (bm->length+4 != datalen) { + applog(LOG_ERR, "bitmain_parse_rxnonce length(%d) error", bm->length); + return -1; + } + crc = CRC16(data, datalen-2); + memcpy(&(bm->crc), data+datalen-2, 2); + bm->crc = htole16(bm->crc); + if(crc != bm->crc) { + applog(LOG_ERR, "bitmain_parse_rxnonce check crc(%d) != bm crc(%d) datalen(%d)", crc, bm->crc, datalen); + return -1; + } + bm->fifo_space = htole16(bm->fifo_space); + bm->diff = htole16(bm->diff); + bm->total_nonce_num = htole64(bm->total_nonce_num); + curnoncenum = (datalen-14)/8; + applog(LOG_DEBUG, "BitMain RxNonce Data: nonce_num(%d) fifo_space(%d) diff(%d) tnn(%lld)", curnoncenum, bm->fifo_space, bm->diff, bm->total_nonce_num); + for(i = 0; i < curnoncenum; i++) { + bm->nonces[i].work_id = htole32(bm->nonces[i].work_id); + bm->nonces[i].nonce = htole32(bm->nonces[i].nonce); + + applog(LOG_DEBUG, "BitMain RxNonce Data %d: work_id(%d) nonce(%08x)(%d)", + i, bm->nonces[i].work_id, bm->nonces[i].nonce, bm->nonces[i].nonce); + } + *nonce_num = curnoncenum; + return 0; +} + +static int bitmain_read(struct cgpu_info *bitmain, unsigned char *buf, + size_t bufsize, int timeout, int ep) +{ + int err = 0, readlen = 0; + size_t total = 0; + + if(bitmain == NULL || buf == NULL || bufsize <= 0) { + applog(LOG_WARNING, "bitmain_read parameter error bufsize(%d)", bufsize); + return -1; + } + if(opt_bitmain_dev_usb) { +#ifdef WIN32 + char readbuf[BITMAIN_READBUF_SIZE]; + int ofs = 2, cp = 0; + + err = usb_read_once_timeout(bitmain, readbuf, bufsize, &readlen, timeout, ep); + applog(LOG_DEBUG, "%s%i: Get bitmain read got readlen %d err %d", + bitmain->drv->name, bitmain->device_id, readlen, err); + + if (readlen < 2) + goto out; + + while (readlen > 2) { + cp = readlen - 2; + if (cp > 62) + cp = 62; + memcpy(&buf[total], &readbuf[ofs], cp); + total += cp; + readlen -= cp + 2; + ofs += 64; + } +#else + err = usb_read_once_timeout(bitmain, buf, bufsize, &readlen, timeout, ep); + applog(LOG_DEBUG, "%s%i: Get bitmain read got readlen %d err %d", + bitmain->drv->name, bitmain->device_id, readlen, err); + total = readlen; +#endif + } else { + err = btm_read(bitmain, buf, bufsize); + total = err; + } +out: + return total; +} + +static int bitmain_write(struct cgpu_info *bitmain, char *buf, ssize_t len, int ep) +{ + int err, amount; + if(opt_bitmain_dev_usb) { + err = usb_write(bitmain, buf, len, &amount, ep); + applog(LOG_DEBUG, "%s%i: usb_write got err %d", bitmain->drv->name, + bitmain->device_id, err); + + if (unlikely(err != 0)) { + applog(LOG_ERR, "usb_write error on bitmain_write err=%d", err); + return BTM_SEND_ERROR; + } + if (amount != len) { + applog(LOG_ERR, "usb_write length mismatch on bitmain_write amount=%d len=%d", amount, len); + return BTM_SEND_ERROR; + } + } else { + int havelen = 0; + while(havelen < len) { + err = btm_write(bitmain, buf+havelen, len-havelen); + if(err < 0) { + applog(LOG_DEBUG, "%s%i: btm_write got err %d", bitmain->drv->name, + bitmain->device_id, err); + applog(LOG_WARNING, "usb_write error on bitmain_write"); + return BTM_SEND_ERROR; + } else { + havelen += err; + } + } + } + return BTM_SEND_OK; +} + +static int bitmain_send_data(const uint8_t * data, int datalen, struct cgpu_info *bitmain) +{ + int delay, ret, ep = C_BITMAIN_SEND; + struct bitmain_info *info = NULL; + cgtimer_t ts_start; + + if(datalen <= 0) { + return 0; + } + + if(data[0] == BITMAIN_TOKEN_TYPE_TXCONFIG) { + ep = C_BITMAIN_TOKEN_TXCONFIG; + } else if(data[0] == BITMAIN_TOKEN_TYPE_TXTASK) { + ep = C_BITMAIN_TOKEN_TXTASK; + } else if(data[0] == BITMAIN_TOKEN_TYPE_RXSTATUS) { + ep = C_BITMAIN_TOKEN_RXSTATUS; + } + + info = bitmain->device_data; + //delay = datalen * 10 * 1000000; + //delay = delay / info->baud; + //delay += 4000; + + if(opt_debug) { + applog(LOG_DEBUG, "BitMain: Sent(%d):", datalen); + hexdump(data, datalen); + } + + //cgsleep_prepare_r(&ts_start); + //applog(LOG_DEBUG, "----bitmain_send_data start"); + ret = bitmain_write(bitmain, (char *)data, datalen, ep); + applog(LOG_DEBUG, "----bitmain_send_data stop ret=%d datalen=%d", ret, datalen); + //cgsleep_us_r(&ts_start, delay); + + //applog(LOG_DEBUG, "BitMain: Sent: Buffer delay: %dus", delay); + + return ret; +} + +static bool bitmain_decode_nonce(struct thr_info *thr, struct cgpu_info *bitmain, + struct bitmain_info *info, uint32_t nonce, struct work *work) +{ + info = bitmain->device_data; + //info->matching_work[work->subid]++; + if(opt_bitmain_hwerror) { + applog(LOG_DEBUG, "BitMain: submit direct nonce = %08x", nonce); + if(opt_bitmain_checkall) { + applog(LOG_DEBUG, "BitMain check all"); + return submit_nonce(thr, work, nonce); + } else { + if(opt_bitmain_checkn2diff) { + int diff = 0; + diff = work->sdiff; + if(diff&&(diff&(diff-1))) { + applog(LOG_DEBUG, "BitMain %d not diff 2 submit_nonce", diff); + return submit_nonce(thr, work, nonce); + } else { + applog(LOG_DEBUG, "BitMain %d diff 2 submit_nonce_direct", diff); + return submit_nonce_direct(thr, work, nonce); + } + } else { + return submit_nonce_direct(thr, work, nonce); + } + } + } else { + applog(LOG_DEBUG, "BitMain: submit nonce = %08x", nonce); + return submit_nonce(thr, work, nonce); + } +} + +static void bitmain_inc_nvw(struct bitmain_info *info, struct thr_info *thr) +{ + applog(LOG_INFO, "%s%d: No matching work - HW error", + thr->cgpu->drv->name, thr->cgpu->device_id); + + inc_hw_errors(thr); + info->no_matching_work++; +} + +static inline void record_temp_fan(struct bitmain_info *info, struct bitmain_rxstatus_data *bm, double *temp_avg) +{ + int i = 0; + int maxfan = 0, maxtemp = 0; + *temp_avg = 0; + + info->fan_num = bm->fan_num; + for(i = 0; i < bm->fan_num; i++) { + info->fan[i] = bm->fan[i] * BITMAIN_FAN_FACTOR; + + if(info->fan[i] > maxfan) + maxfan = info->fan[i]; + } + info->temp_num = bm->temp_num; + for(i = 0; i < bm->temp_num; i++) { + info->temp[i] = bm->temp[i]; + /* + if(bm->temp[i] & 0x80) { + bm->temp[i] &= 0x7f; + info->temp[i] = 0 - ((~bm->temp[i] & 0x7f) + 1); + }*/ + *temp_avg += info->temp[i]; + + if(info->temp[i] > info->temp_max) { + info->temp_max = info->temp[i]; + } + if(info->temp[i] > maxtemp) + maxtemp = info->temp[i]; + } + + if(bm->temp_num > 0) { + *temp_avg = *temp_avg / bm->temp_num; + info->temp_avg = *temp_avg; + } + + inc_dev_status(maxfan, maxtemp); +} + +static void bitmain_update_temps(struct cgpu_info *bitmain, struct bitmain_info *info, + struct bitmain_rxstatus_data *bm) +{ + char tmp[64] = {0}; + char msg[10240] = {0}; + int i = 0; + record_temp_fan(info, bm, &(bitmain->temp)); + + strcpy(msg, "BitMain: "); + for(i = 0; i < bm->fan_num; i++) { + if(i != 0) { + strcat(msg, ", "); + } + sprintf(tmp, "Fan%d: %d/m", i+1, info->fan[i]); + strcat(msg, tmp); + } + strcat(msg, "\t"); + for(i = 0; i < bm->temp_num; i++) { + if(i != 0) { + strcat(msg, ", "); + } + sprintf(tmp, "Temp%d: %dC", i+1, info->temp[i]); + strcat(msg, tmp); + } + sprintf(tmp, ", TempMAX: %dC", info->temp_max); + strcat(msg, tmp); + applog(LOG_INFO, msg); + info->temp_history_index++; + info->temp_sum += bitmain->temp; + applog(LOG_DEBUG, "BitMain: temp_index: %d, temp_count: %d, temp_old: %d", + info->temp_history_index, info->temp_history_count, info->temp_old); + if (info->temp_history_index == info->temp_history_count) { + info->temp_history_index = 0; + info->temp_sum = 0; + } + if (unlikely(info->temp_old >= opt_bitmain_overheat)) { + applog(LOG_WARNING, "BTM%d overheat! Idling", bitmain->device_id); + info->overheat = true; + } else if (info->overheat && info->temp_old <= opt_bitmain_temp) { + applog(LOG_WARNING, "BTM%d cooled, restarting", bitmain->device_id); + info->overheat = false; + } +} + +extern void cg_logwork_uint32(struct work *work, uint32_t nonce, bool ok); + +static void bitmain_parse_results(struct cgpu_info *bitmain, struct bitmain_info *info, + struct thr_info *thr, uint8_t *buf, int *offset) +{ + int i, j, n, m, r, errordiff, spare = BITMAIN_READ_SIZE; + uint32_t checkbit = 0x00000000; + bool found = false; + struct work *work = NULL; + char * ob_hex = NULL; + struct bitmain_packet_head packethead; + int asicnum = 0; + int idiff = 0; + int mod = 0,tmp = 0; + + for (i = 0; i <= spare; i++) { + if(buf[i] == 0xa1) { + struct bitmain_rxstatus_data rxstatusdata; + applog(LOG_DEBUG, "bitmain_parse_results RxStatus Data"); + if(*offset < 4) { + return; + } + memcpy(&packethead, buf+i, sizeof(struct bitmain_packet_head)); + packethead.length = htole16(packethead.length); + if(packethead.length > 1130) { + applog(LOG_ERR, "bitmain_parse_results bitmain_parse_rxstatus datalen=%d error", packethead.length+4); + continue; + } + if(*offset < packethead.length + 4) { + return; + } + if(bitmain_parse_rxstatus(buf+i, packethead.length+4, &rxstatusdata) != 0) { + applog(LOG_ERR, "bitmain_parse_results bitmain_parse_rxstatus error len=%d", packethead.length+4); + } else { + mutex_lock(&info->qlock); + info->chain_num = rxstatusdata.chain_num; + info->fifo_space = rxstatusdata.fifo_space; + info->hw_version[0] = rxstatusdata.hw_version[0]; + info->hw_version[1] = rxstatusdata.hw_version[1]; + info->hw_version[2] = rxstatusdata.hw_version[2]; + info->hw_version[3] = rxstatusdata.hw_version[3]; + info->nonce_error = rxstatusdata.nonce_error; + errordiff = info->nonce_error-info->last_nonce_error; + //sprintf(g_miner_version, "%d.%d.%d.%d", info->hw_version[0], info->hw_version[1], info->hw_version[2], info->hw_version[3]); + applog(LOG_ERR, "bitmain_parse_results v=%d chain=%d fifo=%d hwv1=%d hwv2=%d hwv3=%d hwv4=%d nerr=%d-%d freq=%d chain info:", + rxstatusdata.version, info->chain_num, info->fifo_space, info->hw_version[0], info->hw_version[1], info->hw_version[2], info->hw_version[3], + info->last_nonce_error, info->nonce_error, info->frequency); + memcpy(info->chain_asic_exist, rxstatusdata.chain_asic_exist, BITMAIN_MAX_CHAIN_NUM*32); + memcpy(info->chain_asic_status, rxstatusdata.chain_asic_status, BITMAIN_MAX_CHAIN_NUM*32); + for(n = 0; n < rxstatusdata.chain_num; n++) { + info->chain_asic_num[n] = rxstatusdata.chain_asic_num[n]; + memset(info->chain_asic_status_t[n], 0, 320); + j = 0; + + mod = 0; + if(info->chain_asic_num[n] <= 0) { + asicnum = 0; + } else { + mod = info->chain_asic_num[n] % 32; + if(mod == 0) { + asicnum = info->chain_asic_num[n] / 32; + } else { + asicnum = info->chain_asic_num[n] / 32 + 1; + } + } + if(asicnum > 0) { + for(m = asicnum-1; m >= 0; m--) { + tmp = mod ? (32-mod): 0; + for(r = tmp;r < 32;r++){ + if((r-tmp)%8 == 0 && (r-tmp) !=0){ + info->chain_asic_status_t[n][j] = ' '; + j++; + } + checkbit = num2bit(r); + if(rxstatusdata.chain_asic_exist[n*8+m] & checkbit) { + if(rxstatusdata.chain_asic_status[n*8+m] & checkbit) { + info->chain_asic_status_t[n][j] = 'o'; + } else { + info->chain_asic_status_t[n][j] = 'x'; + } + } else { + info->chain_asic_status_t[n][j] = '-'; + } + j++; + } + info->chain_asic_status_t[n][j] = ' '; + j++; + mod = 0; + } + } + applog(LOG_DEBUG, "bitmain_parse_results chain(%d) asic_num=%d asic_exist=%08x%08x%08x%08x%08x%08x%08x%08x asic_status=%08x%08x%08x%08x%08x%08x%08x%08x", + n, info->chain_asic_num[n], + info->chain_asic_exist[n*8+0], info->chain_asic_exist[n*8+1], info->chain_asic_exist[n*8+2], info->chain_asic_exist[n*8+3], info->chain_asic_exist[n*8+4], info->chain_asic_exist[n*8+5], info->chain_asic_exist[n*8+6], info->chain_asic_exist[n*8+7], + info->chain_asic_status[n*8+0], info->chain_asic_status[n*8+1], info->chain_asic_status[n*8+2], info->chain_asic_status[n*8+3], info->chain_asic_status[n*8+4], info->chain_asic_status[n*8+5], info->chain_asic_status[n*8+6], info->chain_asic_status[n*8+7]); + applog(LOG_ERR, "bitmain_parse_results chain(%d) asic_num=%d asic_status=%s", n, info->chain_asic_num[n], info->chain_asic_status_t[n]); + } + mutex_unlock(&info->qlock); + + if(errordiff > 0) { + for(j = 0; j < errordiff; j++) { + bitmain_inc_nvw(info, thr); + } + mutex_lock(&info->qlock); + info->last_nonce_error += errordiff; + mutex_unlock(&info->qlock); + } + bitmain_update_temps(bitmain, info, &rxstatusdata); + } + + found = true; + spare = packethead.length + 4 + i; + if(spare > *offset) { + applog(LOG_ERR, "bitmain_parse_rxresults space(%d) > offset(%d)", spare, *offset); + spare = *offset; + } + break; + } else if(buf[i] == 0xa2) { + struct bitmain_rxnonce_data rxnoncedata; + int nonce_num = 0; + applog(LOG_DEBUG, "bitmain_parse_results RxNonce Data"); + if(*offset < 4) { + return; + } + memcpy(&packethead, buf+i, sizeof(struct bitmain_packet_head)); + packethead.length = htole16(packethead.length); + if(packethead.length > 1030) { + applog(LOG_ERR, "bitmain_parse_results bitmain_parse_rxnonce datalen=%d error", packethead.length+4); + continue; + } + if(*offset < packethead.length + 4) { + return; + } + if(bitmain_parse_rxnonce(buf+i, packethead.length+4, &rxnoncedata, &nonce_num) != 0) { + applog(LOG_ERR, "bitmain_parse_results bitmain_parse_rxnonce error len=%d", packethead.length+4); + } else { + struct pool * pool = NULL; + for(j = 0; j < nonce_num; j++) { + work = clone_queued_work_byid(bitmain, rxnoncedata.nonces[j].work_id); + if(work) { + pool = work->pool; + if(BITMAIN_TEST_PRINT_WORK) { + applog(LOG_ERR, "bitmain_parse_results nonce find work(%d-%d)(%08x)", work->id, rxnoncedata.nonces[j].work_id, rxnoncedata.nonces[j].nonce); + + ob_hex = bin2hex(work->midstate, 32); + applog(LOG_ERR, "work %d midstate: %s", work->id, ob_hex); + free(ob_hex); + + ob_hex = bin2hex(work->data+64, 12); + applog(LOG_ERR, "work %d data2: %s", work->id, ob_hex); + free(ob_hex); + } + + if(work->work_block < info->last_work_block) { + applog(LOG_ERR, "BitMain: bitmain_parse_rxnonce work(%d) nonce stale", rxnoncedata.nonces[j].work_id); + } else { + if (bitmain_decode_nonce(thr, bitmain, info, rxnoncedata.nonces[j].nonce, work)) { + cg_logwork_uint32(work, rxnoncedata.nonces[j].nonce, true); + if(opt_bitmain_hwerror) { +#ifndef BITMAIN_CALC_DIFF1 + mutex_lock(&info->qlock); + idiff = (int)work->sdiff; + info->nonces+=idiff; + info->auto_nonces+=idiff; + mutex_unlock(&info->qlock); + inc_work_status(thr, pool, idiff); +#endif + } else { + mutex_lock(&info->qlock); + info->nonces++; + info->auto_nonces++; + mutex_unlock(&info->qlock); + } + } else { + //bitmain_inc_nvw(info, thr); + applog(LOG_ERR, "BitMain: bitmain_decode_nonce error work(%d)", rxnoncedata.nonces[j].work_id); + } + } + free_work(work); + } else { + //bitmain_inc_nvw(info, thr); + applog(LOG_ERR, "BitMain: Nonce not find work(%d)", rxnoncedata.nonces[j].work_id); + } + } +#ifdef BITMAIN_CALC_DIFF1 + if(opt_bitmain_hwerror) { + int difftmp = 0; + difftmp = rxnoncedata.diff; + idiff = 1; + while(difftmp > 0) { + difftmp--; + idiff = idiff << 1; + } + mutex_lock(&info->qlock); + difftmp = idiff*(rxnoncedata.total_nonce_num-info->total_nonce_num); + if(difftmp < 0) + difftmp = 0; + + info->nonces = info->nonces+difftmp; + info->auto_nonces = info->auto_nonces+difftmp; + info->total_nonce_num = rxnoncedata.total_nonce_num; + info->fifo_space = rxnoncedata.fifo_space; + mutex_unlock(&info->qlock); + inc_work_stats(thr, pool, difftmp); + + applog(LOG_DEBUG, "bitmain_parse_rxnonce fifo space=%d diff=%d rxtnn=%lld tnn=%lld", info->fifo_space, idiff, rxnoncedata.total_nonce_num, info->total_nonce_num); + } else { + mutex_lock(&info->qlock); + info->fifo_space = rxnoncedata.fifo_space; + mutex_unlock(&info->qlock); + applog(LOG_DEBUG, "bitmain_parse_rxnonce fifo space=%d", info->fifo_space); + } +#else + mutex_lock(&info->qlock); + info->fifo_space = rxnoncedata.fifo_space; + mutex_unlock(&info->qlock); + applog(LOG_DEBUG, "bitmain_parse_rxnonce fifo space=%d", info->fifo_space); +#endif + +#ifndef WIN32 + if(nonce_num < BITMAIN_MAX_NONCE_NUM) + cgsleep_ms(5); +#endif + } + + found = true; + spare = packethead.length + 4 + i; + if(spare > *offset) { + applog(LOG_ERR, "bitmain_parse_rxnonce space(%d) > offset(%d)", spare, *offset); + spare = *offset; + } + break; + } else { + applog(LOG_ERR, "bitmain_parse_results data type error=%02x", buf[i]); + } + } + if (!found) { + spare = *offset - BITMAIN_READ_SIZE; + /* We are buffering and haven't accumulated one more corrupt + * work result. */ + if (spare < (int)BITMAIN_READ_SIZE) + return; + bitmain_inc_nvw(info, thr); + } + + *offset -= spare; + memmove(buf, buf + spare, *offset); +} + +static void bitmain_running_reset(struct cgpu_info *bitmain, struct bitmain_info *info) +{ + bitmain->results = 0; + info->reset = false; +} + +static void *bitmain_get_results(void *userdata) +{ + struct cgpu_info *bitmain = (struct cgpu_info *)userdata; + struct bitmain_info *info = bitmain->device_data; + int offset = 0, read_delay = 0, ret = 0; + const int rsize = BITMAIN_FTDI_READSIZE; + char readbuf[BITMAIN_READBUF_SIZE]; + struct thr_info *thr = info->thr; + char threadname[24]; + int errorcount = 0; + + snprintf(threadname, 24, "btm_recv/%d", bitmain->device_id); + RenameThread(threadname); + + while (likely(!bitmain->shutdown)) { + unsigned char buf[rsize]; + + //applog(LOG_DEBUG, "+++++++bitmain_get_results offset=%d", offset); + + if (offset >= (int)BITMAIN_READ_SIZE) { + //applog(LOG_DEBUG, "======start bitmain_get_results "); + bitmain_parse_results(bitmain, info, thr, readbuf, &offset); + //applog(LOG_DEBUG, "======stop bitmain_get_results "); + } + + if (unlikely(offset + rsize >= BITMAIN_READBUF_SIZE)) { + /* This should never happen */ + applog(LOG_DEBUG, "BitMain readbuf overflow, resetting buffer"); + offset = 0; + } + + if (unlikely(info->reset)) { + bitmain_running_reset(bitmain, info); + /* Discard anything in the buffer */ + offset = 0; + } + + /* As the usb read returns after just 1ms, sleep long enough + * to leave the interface idle for writes to occur, but do not + * sleep if we have been receiving data as more may be coming. */ + //if (offset == 0) { + // cgsleep_ms_r(&ts_start, BITMAIN_READ_TIMEOUT); + //} + + //cgsleep_prepare_r(&ts_start); + //applog(LOG_DEBUG, "======start bitmain_get_results bitmain_read"); + ret = bitmain_read(bitmain, buf, rsize, BITMAIN_READ_TIMEOUT, C_BITMAIN_READ); + //applog(LOG_DEBUG, "======stop bitmain_get_results bitmain_read=%d", ret); + + if ((ret < 1) || (ret == 18)) { + errorcount++; +#ifdef WIN32 + if(errorcount > 200) { + //applog(LOG_ERR, "bitmain_read errorcount ret=%d", ret); + cgsleep_ms(20); + errorcount = 0; + } +#else + if(errorcount > 3) { + //applog(LOG_ERR, "bitmain_read errorcount ret=%d", ret); + cgsleep_ms(20); + errorcount = 0; + } +#endif + if(ret < 1) + { + cgsleep_ms(1); // add by clement : we just wait a little time for RX data... + continue; + } + } + + if (opt_debug) { + applog(LOG_DEBUG, "BitMain: get:"); + hexdump((uint8_t *)buf, ret); + } + + memcpy(readbuf+offset, buf, ret); + offset += ret; + } + return NULL; +} + +static void bitmain_set_timeout(struct bitmain_info *info) +{ + info->timeout = BITMAIN_TIMEOUT_FACTOR / info->frequency; +} + +static void *bitmain_send_tasks(void *userdata) +{ + return NULL; +} + +static void bitmain_init(struct cgpu_info *bitmain) +{ + applog(LOG_INFO, "BitMain: Opened on %s", bitmain->device_path); +} + +static bool bitmain_prepare(struct thr_info *thr) +{ + struct cgpu_info *bitmain = thr->cgpu; + struct bitmain_info *info = bitmain->device_data; + + free(bitmain->works); + bitmain->works = calloc(BITMAIN_MAX_WORK_NUM * sizeof(struct work *), + BITMAIN_ARRAY_SIZE); + if (!bitmain->works) + quit(1, "Failed to calloc bitmain works in bitmain_prepare"); + + info->thr = thr; + mutex_init(&info->lock); + mutex_init(&info->qlock); + if (unlikely(pthread_cond_init(&info->qcond, NULL))) + quit(1, "Failed to pthread_cond_init bitmain qcond"); + cgsem_init(&info->write_sem); + + if (pthread_create(&info->read_thr, NULL, bitmain_get_results, (void *)bitmain)) + quit(1, "Failed to create bitmain read_thr"); + + //if (pthread_create(&info->write_thr, NULL, bitmain_send_tasks, (void *)bitmain)) + // quit(1, "Failed to create bitmain write_thr"); + + bitmain_init(bitmain); + + return true; +} + +static int bitmain_initialize(struct cgpu_info *bitmain) +{ + uint8_t data[BITMAIN_READBUF_SIZE]; + struct bitmain_info *info = NULL; + int ret = 0, spare = 0; + uint8_t sendbuf[BITMAIN_SENDBUF_SIZE]; + int readlen = 0; + int sendlen = 0; + int trycount = 3; + struct timespec p; + struct bitmain_rxstatus_data rxstatusdata; + int i = 0, j = 0, m = 0, r = 0, statusok = 0; + uint32_t checkbit = 0x00000000; + int hwerror_eft = 0; + int beeper_ctrl = 1; + int tempover_ctrl = 1; + int home_mode = 0; + struct bitmain_packet_head packethead; + int asicnum = 0; + int mod = 0,tmp = 0; + + /* Send reset, then check for result */ + if(!bitmain) { + applog(LOG_WARNING, "bitmain_initialize cgpu_info is null"); + return -1; + } + info = bitmain->device_data; + + /* clear read buf */ + ret = bitmain_read(bitmain, data, BITMAIN_READBUF_SIZE, + BITMAIN_RESET_TIMEOUT, C_BITMAIN_READ); + if(ret > 0) { + if (opt_debug) { + applog(LOG_DEBUG, "BTM%d Clear Read(%d):", bitmain->device_id, ret); + hexdump(data, ret); + } + } + + sendlen = bitmain_set_rxstatus((struct bitmain_rxstatus_token *)sendbuf, 0, 1, 0, 0); + if(sendlen <= 0) { + applog(LOG_ERR, "bitmain_initialize bitmain_set_rxstatus error(%d)", sendlen); + return -1; + } + + ret = bitmain_send_data(sendbuf, sendlen, bitmain); + if (unlikely(ret == BTM_SEND_ERROR)) { + applog(LOG_ERR, "bitmain_initialize bitmain_send_data error"); + return -1; + } + while(trycount >= 0) { + ret = bitmain_read(bitmain, data+readlen, BITMAIN_READBUF_SIZE, BITMAIN_RESET_TIMEOUT, C_BITMAIN_DATA_RXSTATUS); + if(ret > 0) { + readlen += ret; + if(readlen > BITMAIN_READ_SIZE) { + for(i = 0; i < readlen; i++) { + if(data[i] == 0xa1) { + if (opt_debug) { + applog(LOG_DEBUG, "%s%d initset: get:", bitmain->drv->name, bitmain->device_id); + hexdump(data, readlen); + } + memcpy(&packethead, data+i, sizeof(struct bitmain_packet_head)); + packethead.length = htole16(packethead.length); + + if(packethead.length > 1130) { + applog(LOG_ERR, "bitmain_initialize rxstatus datalen=%d error", packethead.length+4); + continue; + } + if(readlen-i < packethead.length+4) { + applog(LOG_ERR, "bitmain_initialize rxstatus datalen=%d<%d low", readlen-i, packethead.length+4); + continue; + } + if (bitmain_parse_rxstatus(data+i, packethead.length+4, &rxstatusdata) != 0) { + applog(LOG_ERR, "bitmain_initialize bitmain_parse_rxstatus error"); + continue; + } + info->chain_num = rxstatusdata.chain_num; + info->fifo_space = rxstatusdata.fifo_space; + info->hw_version[0] = rxstatusdata.hw_version[0]; + info->hw_version[1] = rxstatusdata.hw_version[1]; + info->hw_version[2] = rxstatusdata.hw_version[2]; + info->hw_version[3] = rxstatusdata.hw_version[3]; + info->nonce_error = 0; + info->last_nonce_error = 0; + sprintf(g_miner_version, "%d.%d.%d.%d", info->hw_version[0], info->hw_version[1], info->hw_version[2], info->hw_version[3]); + applog(LOG_ERR, "bitmain_initialize rxstatus v(%d) chain(%d) fifo(%d) hwv1(%d) hwv2(%d) hwv3(%d) hwv4(%d) nerr(%d) freq=%d", + rxstatusdata.version, info->chain_num, info->fifo_space, info->hw_version[0], info->hw_version[1], info->hw_version[2], info->hw_version[3], + rxstatusdata.nonce_error, info->frequency); + + memcpy(info->chain_asic_exist, rxstatusdata.chain_asic_exist, BITMAIN_MAX_CHAIN_NUM*32); + memcpy(info->chain_asic_status, rxstatusdata.chain_asic_status, BITMAIN_MAX_CHAIN_NUM*32); + for(i = 0; i < rxstatusdata.chain_num; i++) { + info->chain_asic_num[i] = rxstatusdata.chain_asic_num[i]; + memset(info->chain_asic_status_t[i], 0, 320); + j = 0; + mod = 0; + + if(info->chain_asic_num[i] <= 0) { + asicnum = 0; + } else { + mod = info->chain_asic_num[i] % 32; + if(mod == 0) { + asicnum = info->chain_asic_num[i] / 32; + } else { + asicnum = info->chain_asic_num[i] / 32 + 1; + } + } + if(asicnum > 0) { + for(m = asicnum-1; m >= 0; m--) { + tmp = mod ? (32-mod):0; + for(r = tmp;r < 32;r++){ + if((r-tmp)%8 == 0 && (r-tmp) !=0){ + info->chain_asic_status_t[i][j] = ' '; + j++; + } + checkbit = num2bit(r); + if(rxstatusdata.chain_asic_exist[i*8+m] & checkbit) { + if(rxstatusdata.chain_asic_status[i*8+m] & checkbit) { + info->chain_asic_status_t[i][j] = 'o'; + } else { + info->chain_asic_status_t[i][j] = 'x'; + } + } else { + info->chain_asic_status_t[i][j] = '-'; + } + j++; + } + info->chain_asic_status_t[i][j] = ' '; + j++; + mod = 0; + } + } + applog(LOG_DEBUG, "bitmain_initialize chain(%d) asic_num=%d asic_exist=%08x%08x%08x%08x%08x%08x%08x%08x asic_status=%08x%08x%08x%08x%08x%08x%08x%08x", + i, info->chain_asic_num[i], + info->chain_asic_exist[i*8+0], info->chain_asic_exist[i*8+1], info->chain_asic_exist[i*8+2], info->chain_asic_exist[i*8+3], info->chain_asic_exist[i*8+4], info->chain_asic_exist[i*8+5], info->chain_asic_exist[i*8+6], info->chain_asic_exist[i*8+7], + info->chain_asic_status[i*8+0], info->chain_asic_status[i*8+1], info->chain_asic_status[i*8+2], info->chain_asic_status[i*8+3], info->chain_asic_status[i*8+4], info->chain_asic_status[i*8+5], info->chain_asic_status[i*8+6], info->chain_asic_status[i*8+7]); + applog(LOG_ERR, "bitmain_initialize chain(%d) asic_num=%d asic_status=%s", i, info->chain_asic_num[i], info->chain_asic_status_t[i]); + } + bitmain_update_temps(bitmain, info, &rxstatusdata); + statusok = 1; + break; + } + } + if(statusok) { + break; + } + } + } + trycount--; + p.tv_sec = 0; + p.tv_nsec = BITMAIN_RESET_PITCH; + nanosleep(&p, NULL); + } + + p.tv_sec = 0; + p.tv_nsec = BITMAIN_RESET_PITCH; + nanosleep(&p, NULL); + + cgtime(&info->last_status_time); + + if(statusok) { + applog(LOG_ERR, "bitmain_initialize start send txconfig"); + if(opt_bitmain_hwerror) + hwerror_eft = 1; + else + hwerror_eft = 0; + if(opt_bitmain_nobeeper) + beeper_ctrl = 0; + else + beeper_ctrl = 1; + if(opt_bitmain_notempoverctrl) + tempover_ctrl = 0; + else + tempover_ctrl = 1; + if(opt_bitmain_homemode) + home_mode= 1; + else + home_mode= 0; + sendlen = bitmain_set_txconfig((struct bitmain_txconfig_token *)sendbuf, 1, 1, 1, 1, 1, 0, 1, hwerror_eft, beeper_ctrl, tempover_ctrl,home_mode, + info->chain_num, info->asic_num, BITMAIN_DEFAULT_FAN_MAX_PWM, info->timeout, + info->frequency, info->voltage, 0, 0, 0x04, info->reg_data); + if(sendlen <= 0) { + applog(LOG_ERR, "bitmain_initialize bitmain_set_txconfig error(%d)", sendlen); + return -1; + } + + ret = bitmain_send_data(sendbuf, sendlen, bitmain); + if (unlikely(ret == BTM_SEND_ERROR)) { + applog(LOG_ERR, "bitmain_initialize bitmain_send_data error"); + return -1; + } + applog(LOG_WARNING, "BMM%d: InitSet succeeded", bitmain->device_id); + } else { + applog(LOG_WARNING, "BMS%d: InitSet error", bitmain->device_id); + return -1; + } + return 0; +} + +static void bitmain_usb_init(struct cgpu_info *bitmain) +{ + int err, interface; + +#ifndef WIN32 + return; +#endif + + if (bitmain->usbinfo.nodev) + return; + + interface = usb_interface(bitmain); + + // Reset + err = usb_transfer(bitmain, FTDI_TYPE_OUT, FTDI_REQUEST_RESET, + FTDI_VALUE_RESET, interface, C_RESET); + + applog(LOG_DEBUG, "%s%i: reset got err %d", + bitmain->drv->name, bitmain->device_id, err); + + if (bitmain->usbinfo.nodev) + return; + + // Set latency + err = usb_transfer(bitmain, FTDI_TYPE_OUT, FTDI_REQUEST_LATENCY, + BITMAIN_LATENCY, interface, C_LATENCY); + + applog(LOG_DEBUG, "%s%i: latency got err %d", + bitmain->drv->name, bitmain->device_id, err); + + if (bitmain->usbinfo.nodev) + return; + + // Set data + err = usb_transfer(bitmain, FTDI_TYPE_OUT, FTDI_REQUEST_DATA, + FTDI_VALUE_DATA_BTM, interface, C_SETDATA); + + applog(LOG_DEBUG, "%s%i: data got err %d", + bitmain->drv->name, bitmain->device_id, err); + + if (bitmain->usbinfo.nodev) + return; + + // Set the baud + err = usb_transfer(bitmain, FTDI_TYPE_OUT, FTDI_REQUEST_BAUD, FTDI_VALUE_BAUD_BTM, + (FTDI_INDEX_BAUD_BTM & 0xff00) | interface, + C_SETBAUD); + + applog(LOG_DEBUG, "%s%i: setbaud got err %d", + bitmain->drv->name, bitmain->device_id, err); + + if (bitmain->usbinfo.nodev) + return; + + // Set Modem Control + err = usb_transfer(bitmain, FTDI_TYPE_OUT, FTDI_REQUEST_MODEM, + FTDI_VALUE_MODEM, interface, C_SETMODEM); + + applog(LOG_DEBUG, "%s%i: setmodemctrl got err %d", + bitmain->drv->name, bitmain->device_id, err); + + if (bitmain->usbinfo.nodev) + return; + + // Set Flow Control + err = usb_transfer(bitmain, FTDI_TYPE_OUT, FTDI_REQUEST_FLOW, + FTDI_VALUE_FLOW, interface, C_SETFLOW); + + applog(LOG_DEBUG, "%s%i: setflowctrl got err %d", + bitmain->drv->name, bitmain->device_id, err); + + if (bitmain->usbinfo.nodev) + return; + + /* BitMain repeats the following */ + // Set Modem Control + err = usb_transfer(bitmain, FTDI_TYPE_OUT, FTDI_REQUEST_MODEM, + FTDI_VALUE_MODEM, interface, C_SETMODEM); + + applog(LOG_DEBUG, "%s%i: setmodemctrl 2 got err %d", + bitmain->drv->name, bitmain->device_id, err); + + if (bitmain->usbinfo.nodev) + return; + + // Set Flow Control + err = usb_transfer(bitmain, FTDI_TYPE_OUT, FTDI_REQUEST_FLOW, + FTDI_VALUE_FLOW, interface, C_SETFLOW); + + applog(LOG_DEBUG, "%s%i: setflowctrl 2 got err %d", + bitmain->drv->name, bitmain->device_id, err); +} + +static struct cgpu_info * bitmain_usb_detect_one(libusb_device *dev, struct usb_find_devices *found) +{ + int baud, chain_num, asic_num, timeout, frequency = 0; + char frequency_t[256] = {0}; + uint8_t reg_data[4] = {0}; + uint8_t voltage[2] = {0}; + char voltage_t[8] = {0}; + int this_option_offset = ++option_offset; + struct bitmain_info *info; + struct cgpu_info *bitmain; + bool configured; + int ret; + + if (opt_bitmain_options == NULL) + return NULL; + + bitmain = usb_alloc_cgpu(&bitmain_drv, BITMAIN_MINER_THREADS); + + baud = BITMAIN_IO_SPEED; + chain_num = BITMAIN_DEFAULT_CHAIN_NUM; + asic_num = BITMAIN_DEFAULT_ASIC_NUM; + timeout = BITMAIN_DEFAULT_TIMEOUT; + frequency = BITMAIN_DEFAULT_FREQUENCY; + + if (!usb_init(bitmain, dev, found)) + goto shin; + + configured = get_options(this_option_offset, &baud, &chain_num, + &asic_num, &timeout, &frequency, frequency_t, reg_data, voltage, voltage_t); + get_option_freq(&timeout, &frequency, frequency_t, reg_data); + get_option_voltage(voltage, voltage_t); + + /* Even though this is an FTDI type chip, we want to do the parsing + * all ourselves so set it to std usb type */ + bitmain->usbdev->usb_type = USB_TYPE_STD; + + /* We have a real BitMain! */ + bitmain_usb_init(bitmain); + + bitmain->device_data = calloc(sizeof(struct bitmain_info), 1); + if (unlikely(!(bitmain->device_data))) + quit(1, "Failed to calloc bitmain_info data"); + info = bitmain->device_data; + + if (configured) { + info->baud = baud; + info->chain_num = chain_num; + info->asic_num = asic_num; + info->timeout = timeout; + info->frequency = frequency; + strcpy(info->frequency_t, frequency_t); + memcpy(info->reg_data, reg_data, 4); + memcpy(info->voltage, voltage, 2); + strcpy(info->voltage_t, voltage_t); + } else { + info->baud = BITMAIN_IO_SPEED; + info->chain_num = BITMAIN_DEFAULT_CHAIN_NUM; + info->asic_num = BITMAIN_DEFAULT_ASIC_NUM; + info->timeout = BITMAIN_DEFAULT_TIMEOUT; + info->frequency = BITMAIN_DEFAULT_FREQUENCY; + sprintf(info->frequency_t, "%d", BITMAIN_DEFAULT_FREQUENCY); + memset(info->reg_data, 0, 4); + info->voltage[0] = BITMAIN_DEFAULT_VOLTAGE0; + info->voltage[1] = BITMAIN_DEFAULT_VOLTAGE1; + strcpy(info->voltage_t, BITMAIN_DEFAULT_VOLTAGE_T); + } + + info->fan_pwm = BITMAIN_DEFAULT_FAN_MIN_PWM; + info->temp_max = 0; + /* This is for check the temp/fan every 3~4s */ + info->temp_history_count = (4 / (float)((float)info->timeout * ((float)1.67/0x32))) + 1; + if (info->temp_history_count <= 0) + info->temp_history_count = 1; + + info->temp_history_index = 0; + info->temp_sum = 0; + info->temp_old = 0; + + if (!add_cgpu(bitmain)) + goto unshin; + + applog(LOG_ERR, "------bitmain usb detect one------"); + ret = bitmain_initialize(bitmain); + if (ret && !configured) + goto unshin; + + update_usb_stats(bitmain); + + info->errorcount = 0; + + applog(LOG_DEBUG, "BitMain Detected: %s " + "(chain_num=%d asic_num=%d timeout=%d frequency=%d)", + bitmain->device_path, info->chain_num, info->asic_num, info->timeout, + info->frequency); + + return bitmain; + +unshin: + + usb_uninit(bitmain); + +shin: + + free(bitmain->device_data); + bitmain->device_data = NULL; + + bitmain = usb_free_cgpu(bitmain); + + return NULL; +} + +static bool bitmain_detect_one(const char * devpath) +{ + int baud, chain_num, asic_num, timeout, frequency = 0; + char frequency_t[256] = {0}; + uint8_t reg_data[4] = {0}; + uint8_t voltage[2] = {0}; + char voltage_t[8] = {0}; + int this_option_offset = ++option_offset; + struct bitmain_info *info; + struct cgpu_info *bitmain; + bool configured; + int ret; + + if (opt_bitmain_options == NULL) + return false; + + bitmain = btm_alloc_cgpu(&bitmain_drv, BITMAIN_MINER_THREADS); + + configured = get_options(this_option_offset, &baud, &chain_num, + &asic_num, &timeout, &frequency, frequency_t, reg_data, voltage, voltage_t); + get_option_freq(&timeout, &frequency, frequency_t, reg_data); + get_option_voltage(voltage, voltage_t); + + if (!btm_init(bitmain, opt_bitmain_dev)) + goto shin; + applog(LOG_ERR, "bitmain_detect_one btm init ok"); + + bitmain->device_data = calloc(sizeof(struct bitmain_info), 1); + /* make sure initialize successfully*/ + memset(bitmain->device_data,0,sizeof(struct bitmain_info)); + if (unlikely(!(bitmain->device_data))) + quit(1, "Failed to calloc bitmain_info data"); + info = bitmain->device_data; + + if (configured) { + info->baud = baud; + info->chain_num = chain_num; + info->asic_num = asic_num; + info->timeout = timeout; + info->frequency = frequency; + strcpy(info->frequency_t, frequency_t); + memcpy(info->reg_data, reg_data, 4); + memcpy(info->voltage, voltage, 2); + strcpy(info->voltage_t, voltage_t); + } else { + info->baud = BITMAIN_IO_SPEED; + info->chain_num = BITMAIN_DEFAULT_CHAIN_NUM; + info->asic_num = BITMAIN_DEFAULT_ASIC_NUM; + info->timeout = BITMAIN_DEFAULT_TIMEOUT; + info->frequency = BITMAIN_DEFAULT_FREQUENCY; + sprintf(info->frequency_t, "%d", BITMAIN_DEFAULT_FREQUENCY); + memset(info->reg_data, 0, 4); + info->voltage[0] = BITMAIN_DEFAULT_VOLTAGE0; + info->voltage[1] = BITMAIN_DEFAULT_VOLTAGE1; + strcpy(info->voltage_t, BITMAIN_DEFAULT_VOLTAGE_T); + } + + info->fan_pwm = BITMAIN_DEFAULT_FAN_MIN_PWM; + info->temp_max = 0; + /* This is for check the temp/fan every 3~4s */ + info->temp_history_count = (4 / (float)((float)info->timeout * ((float)1.67/0x32))) + 1; + if (info->temp_history_count <= 0) + info->temp_history_count = 1; + + info->temp_history_index = 0; + info->temp_sum = 0; + info->temp_old = 0; + + if (!add_cgpu(bitmain)) + goto unshin; + + ret = bitmain_initialize(bitmain); + applog(LOG_ERR, "bitmain_detect_one stop bitmain_initialize %d", ret); + if (ret && !configured) + goto unshin; + + info->errorcount = 0; + + applog(LOG_ERR, "BitMain Detected: %s " + "(chain_num=%d asic_num=%d timeout=%d freq=%d-%s volt=%02x%02x-%s)", + bitmain->device_path, info->chain_num, info->asic_num, info->timeout, + info->frequency, info->frequency_t, info->voltage[0], info->voltage[1], info->voltage_t); + + return true; + +unshin: + btm_uninit(bitmain); + +shin: + free(bitmain->device_data); + bitmain->device_data = NULL; + + bitmain = usb_free_cgpu(bitmain); + + return false; +} + +static void bitmain_detect(bool __maybe_unused hotplug) +{ + applog(LOG_DEBUG, "BTM detect dev: %s", opt_bitmain_dev); + if(strlen(opt_bitmain_dev) <= 0) { + opt_bitmain_dev_usb = true; + } else { + opt_bitmain_dev_usb = false; + } + if(opt_bitmain_dev_usb) { + usb_detect(&bitmain_drv, bitmain_usb_detect_one); + } else { + btm_detect(&bitmain_drv, bitmain_detect_one); + } +} + +static void do_bitmain_close(struct thr_info *thr) +{ + struct cgpu_info *bitmain = thr->cgpu; + struct bitmain_info *info = bitmain->device_data; + + pthread_join(info->read_thr, NULL); + pthread_join(info->write_thr, NULL); + bitmain_running_reset(bitmain, info); + + info->no_matching_work = 0; + + cgsem_destroy(&info->write_sem); +} + +static void get_bitmain_statline_before(char *buf, size_t bufsiz, struct cgpu_info *bitmain) +{ + struct bitmain_info *info = bitmain->device_data; + int lowfan = 10000; + int i = 0; + + /* Find the lowest fan speed of the ASIC cooling fans. */ + for(i = 0; i < info->fan_num; i++) { + if (info->fan[i] >= 0 && info->fan[i] < lowfan) + lowfan = info->fan[i]; + } + + tailsprintf(buf, bufsiz, "%2d/%3dC %04dR | ", info->temp_avg, info->temp_max, lowfan); +} + +/* We use a replacement algorithm to only remove references to work done from + * the buffer when we need the extra space for new work. */ +static bool bitmain_fill(struct cgpu_info *bitmain) +{ + struct bitmain_info *info = bitmain->device_data; + int subid, slot; + struct work *work; + bool ret = true; + int sendret = 0, sendcount = 0, neednum = 0, queuednum = 0, sendnum = 0, sendlen = 0; + uint8_t sendbuf[BITMAIN_SENDBUF_SIZE]; + cgtimer_t ts_start; + int senderror = 0; + struct timeval now; + int timediff = 0; + int needwait=0; // add by clement. use a flag to indicate need sleep or not. + + //applog(LOG_DEBUG, "BTM bitmain_fill start--------"); + mutex_lock(&info->qlock); + if(info->fifo_space <= 0) { + //applog(LOG_DEBUG, "BTM bitmain_fill fifo space empty--------"); + ret = true; + needwait=1; // add by clement. DEVICE FIFO is full, no space for new works. So we need sleep. + goto out_unlock; + } + if (bitmain->queued >= BITMAIN_MAX_WORK_QUEUE_NUM) { + ret = true; + } else { + ret = false; + } + while(info->fifo_space > 0) { + neednum = info->fifo_spacefifo_space:BITMAIN_MAX_WORK_NUM; + queuednum = bitmain->queued; + applog(LOG_DEBUG, "BTM: Work task queued(%d) fifo space(%d) needsend(%d)", queuednum, info->fifo_space, neednum); + if(queuednum < neednum) { + while(true) { + work = get_queued(bitmain); + if (unlikely(!work)) { + break; + } else { + applog(LOG_DEBUG, "BTM get work queued number:%d neednum:%d", queuednum, neednum); + subid = bitmain->queued++; + work->subid = subid; + slot = bitmain->work_array + subid; + if (slot >= BITMAIN_ARRAY_SIZE) { // slot=edited by clement , old code is if (slot > BITMAIN_ARRAY_SIZE), not sure ,just fixed it. + applog(LOG_DEBUG, "bitmain_fill array cyc %d", BITMAIN_ARRAY_SIZE); + slot = 0; + } + if (likely(bitmain->works[slot])) { + applog(LOG_DEBUG, "bitmain_fill work_completed %d", slot); + work_completed(bitmain, bitmain->works[slot]); + } + bitmain->works[slot] = work; + queuednum++; + if(queuednum >= neednum) { + break; + } + } + } + } + if(queuednum < BITMAIN_MAX_DEAL_QUEUE_NUM) { + /* by clement + if(queuednum < neednum) { + applog(LOG_DEBUG, "BTM: No enough work to send, queue num=%d", queuednum); + break; + } + */ + + needwait=1; // if queuednum is not enough, we just wait and sleep. queuednum must be >= BITMAIN_MAX_DEAL_QUEUE_NUM, then send to device + break; + } + + sendnum = queuednum < neednum ? queuednum : neednum; + sendlen = bitmain_set_txtask(sendbuf, &(info->last_work_block), bitmain->works, BITMAIN_ARRAY_SIZE, bitmain->work_array, sendnum, &sendcount); + bitmain->queued -= sendnum; + info->send_full_space += sendnum; + if (bitmain->queued < 0) + bitmain->queued = 0; + if (bitmain->work_array + sendnum > BITMAIN_ARRAY_SIZE) { + bitmain->work_array = bitmain->work_array + sendnum-BITMAIN_ARRAY_SIZE; + } else { + bitmain->work_array += sendnum; + } + applog(LOG_DEBUG, "BTM: Send work array %d", bitmain->work_array); + if (sendlen > 0) { + info->fifo_space -= sendcount; + if (info->fifo_space < 0) + info->fifo_space = 0; + sendret = bitmain_send_data(sendbuf, sendlen, bitmain); + if (unlikely(sendret == BTM_SEND_ERROR)) { + applog(LOG_ERR, "BTM%i: Comms error(buffer)", bitmain->device_id); + //dev_error(bitmain, REASON_DEV_COMMS_ERROR); + info->reset = true; + info->errorcount++; + senderror = 1; + if (info->errorcount > 1000) { + info->errorcount = 0; + applog(LOG_ERR, "%s%d: Device disappeared, shutting down thread", bitmain->drv->name, bitmain->device_id); + bitmain->shutdown = true; + } + break; + } else { + applog(LOG_DEBUG, "bitmain_send_data send ret=%d", sendret); + info->errorcount = 0; + } + } else { + applog(LOG_DEBUG, "BTM: Send work bitmain_set_txtask error: %d", sendlen); + break; + } + } + +out_unlock: + cgtime(&now); + timediff = now.tv_sec - info->last_status_time.tv_sec; + if(timediff < 0) timediff = -timediff; + if (timediff > BITMAIN_SEND_STATUS_TIME) { + applog(LOG_DEBUG, "BTM: Send RX Status Token fifo_space(%d) timediff(%d)", info->fifo_space, timediff); + copy_time(&(info->last_status_time), &now); + + sendlen = bitmain_set_rxstatus((struct bitmain_rxstatus_token *) sendbuf, 0, 0, 0, 0); + if (sendlen > 0) { + sendret = bitmain_send_data(sendbuf, sendlen, bitmain); + if (unlikely(sendret == BTM_SEND_ERROR)) { + applog(LOG_ERR, "BTM%i: Comms error(buffer)", bitmain->device_id); + //dev_error(bitmain, REASON_DEV_COMMS_ERROR); + info->reset = true; + info->errorcount++; + senderror = 1; + if (info->errorcount > 1000) { + info->errorcount = 0; + applog(LOG_ERR, "%s%d: Device disappeared, shutting down thread", bitmain->drv->name, bitmain->device_id); + bitmain->shutdown = true; + } + } else { + info->errorcount = 0; + if (info->fifo_space <= 0) { + senderror = 1; + } + } + } + } + + if(info->send_full_space > BITMAIN_SEND_FULL_SPACE) { + info->send_full_space = 0; + mutex_unlock(&info->qlock); // add by clement. we need unlock first, then sleep. So we can let other thread run as soon as possible. + + ret = true; + cgsleep_ms(1); // just sleep a liitle. + } + else + { + mutex_unlock(&info->qlock); // add by clement. we need unlock first, then sleep. So we can let other thread run as soon as possible. + + if(needwait) + cgsleep_ms(1); // add by clement. if there is no work on queue, we need wait for a little time. Or this thread will hold CPU near 99%. + // In fact, we need more time in gen_hash thread!!! + } + + if(senderror) { + ret = true; + applog(LOG_DEBUG, "bitmain_fill send task sleep"); + //cgsleep_ms(1); + } + return ret; +} + +static int64_t bitmain_scanhash(struct thr_info *thr) +{ + struct cgpu_info *bitmain = thr->cgpu; + struct bitmain_info *info = bitmain->device_data; + const int chain_num = info->chain_num; + struct timeval now, then, tdiff; + int64_t hash_count, us_timeout; + struct timespec abstime; + int ret; + + /* Half nonce range */ + us_timeout = 0x80000000ll / info->asic_num / info->frequency; + tdiff.tv_sec = us_timeout / 1000000; + tdiff.tv_usec = us_timeout - (tdiff.tv_sec * 1000000); + cgtime(&now); + timeradd(&now, &tdiff, &then); + abstime.tv_sec = then.tv_sec; + abstime.tv_nsec = then.tv_usec * 1000; + + //applog(LOG_DEBUG, "bitmain_scanhash info->qlock start"); + mutex_lock(&info->qlock); + hash_count = 0xffffffffull * (uint64_t)info->nonces; + bitmain->results += info->nonces + info->idle; + if (bitmain->results > chain_num) + bitmain->results = chain_num; + if (!info->reset) + bitmain->results--; + info->nonces = info->idle = 0; + mutex_unlock(&info->qlock); + //applog(LOG_DEBUG, "bitmain_scanhash info->qlock stop"); + + /* Check for nothing but consecutive bad results or consistently less + * results than we should be getting and reset the FPGA if necessary */ + //if (bitmain->results < -chain_num && !info->reset) { + // applog(LOG_ERR, "BTM%d: Result return rate low, resetting!", + // bitmain->device_id); + // info->reset = true; + //} + + if (unlikely(bitmain->usbinfo.nodev)) { + applog(LOG_ERR, "BTM%d: Device disappeared, shutting down thread", + bitmain->device_id); + bitmain->shutdown = true; + } + + /* This hashmeter is just a utility counter based on returned shares */ + return hash_count; +} + +static void bitmain_flush_work(struct cgpu_info *bitmain) +{ + struct bitmain_info *info = bitmain->device_data; + int i = 0; + + mutex_lock(&info->qlock); + /* Will overwrite any work queued */ + applog(LOG_ERR, "bitmain_flush_work queued=%d array=%d", bitmain->queued, bitmain->work_array); + if(bitmain->queued > 0) { + if (bitmain->work_array + bitmain->queued > BITMAIN_ARRAY_SIZE) { + bitmain->work_array = bitmain->work_array + bitmain->queued-BITMAIN_ARRAY_SIZE; + } else { + bitmain->work_array += bitmain->queued; + } + } + bitmain->queued = 0; + //bitmain->work_array = 0; + //for(i = 0; i < BITMAIN_ARRAY_SIZE; i++) { + // bitmain->works[i] = NULL; + //} + //pthread_cond_signal(&info->qcond); + mutex_unlock(&info->qlock); +} + +static struct api_data *bitmain_api_stats(struct cgpu_info *cgpu) +{ + struct api_data *root = NULL; + struct bitmain_info *info = cgpu->device_data; + char buf[64]; + int i = 0; + double hwp = (cgpu->hw_errors + cgpu->diff1) ? + (double)(cgpu->hw_errors) / (double)(cgpu->hw_errors + cgpu->diff1) : 0; + + root = api_add_int(root, "baud", &(info->baud), false); + root = api_add_int(root, "miner_count", &(info->chain_num), false); + root = api_add_int(root, "asic_count", &(info->asic_num), false); + root = api_add_int(root, "timeout", &(info->timeout), false); + root = api_add_string(root, "frequency", info->frequency_t, false); + root = api_add_string(root, "voltage", info->voltage_t, false); + root = api_add_int(root, "hwv1", &(info->hw_version[0]), false); + root = api_add_int(root, "hwv2", &(info->hw_version[1]), false); + root = api_add_int(root, "hwv3", &(info->hw_version[2]), false); + root = api_add_int(root, "hwv4", &(info->hw_version[3]), false); + + root = api_add_int(root, "fan_num", &(info->fan_num), false); + root = api_add_int(root, "fan1", &(info->fan[0]), false); + root = api_add_int(root, "fan2", &(info->fan[1]), false); + root = api_add_int(root, "fan3", &(info->fan[2]), false); + root = api_add_int(root, "fan4", &(info->fan[3]), false); + root = api_add_int(root, "fan5", &(info->fan[4]), false); + root = api_add_int(root, "fan6", &(info->fan[5]), false); + root = api_add_int(root, "fan7", &(info->fan[6]), false); + root = api_add_int(root, "fan8", &(info->fan[7]), false); + root = api_add_int(root, "fan9", &(info->fan[8]), false); + root = api_add_int(root, "fan10", &(info->fan[9]), false); + root = api_add_int(root, "fan11", &(info->fan[10]), false); + root = api_add_int(root, "fan12", &(info->fan[11]), false); + root = api_add_int(root, "fan13", &(info->fan[12]), false); + root = api_add_int(root, "fan14", &(info->fan[13]), false); + root = api_add_int(root, "fan15", &(info->fan[14]), false); + root = api_add_int(root, "fan16", &(info->fan[15]), false); + + root = api_add_int(root, "temp_num", &(info->temp_num), false); + root = api_add_int(root, "temp1", &(info->temp[0]), false); + root = api_add_int(root, "temp2", &(info->temp[1]), false); + root = api_add_int(root, "temp3", &(info->temp[2]), false); + root = api_add_int(root, "temp4", &(info->temp[3]), false); + root = api_add_int(root, "temp5", &(info->temp[4]), false); + root = api_add_int(root, "temp6", &(info->temp[5]), false); + root = api_add_int(root, "temp7", &(info->temp[6]), false); + root = api_add_int(root, "temp8", &(info->temp[7]), false); + root = api_add_int(root, "temp9", &(info->temp[8]), false); + root = api_add_int(root, "temp10", &(info->temp[9]), false); + root = api_add_int(root, "temp11", &(info->temp[10]), false); + root = api_add_int(root, "temp12", &(info->temp[11]), false); + root = api_add_int(root, "temp13", &(info->temp[12]), false); + root = api_add_int(root, "temp14", &(info->temp[13]), false); + root = api_add_int(root, "temp15", &(info->temp[14]), false); + root = api_add_int(root, "temp16", &(info->temp[15]), false); + root = api_add_int(root, "temp_avg", &(info->temp_avg), false); + root = api_add_int(root, "temp_max", &(info->temp_max), false); + root = api_add_percent(root, "Device Hardware%", &hwp, true); + root = api_add_int(root, "no_matching_work", &(info->no_matching_work), false); + /* + for (i = 0; i < info->chain_num; i++) { + char mcw[24]; + + sprintf(mcw, "match_work_count%d", i + 1); + root = api_add_int(root, mcw, &(info->matching_work[i]), false); + }*/ + + root = api_add_int(root, "chain_acn1", &(info->chain_asic_num[0]), false); + root = api_add_int(root, "chain_acn2", &(info->chain_asic_num[1]), false); + root = api_add_int(root, "chain_acn3", &(info->chain_asic_num[2]), false); + root = api_add_int(root, "chain_acn4", &(info->chain_asic_num[3]), false); + root = api_add_int(root, "chain_acn5", &(info->chain_asic_num[4]), false); + root = api_add_int(root, "chain_acn6", &(info->chain_asic_num[5]), false); + root = api_add_int(root, "chain_acn7", &(info->chain_asic_num[6]), false); + root = api_add_int(root, "chain_acn8", &(info->chain_asic_num[7]), false); + root = api_add_int(root, "chain_acn9", &(info->chain_asic_num[8]), false); + root = api_add_int(root, "chain_acn10", &(info->chain_asic_num[9]), false); + root = api_add_int(root, "chain_acn11", &(info->chain_asic_num[10]), false); + root = api_add_int(root, "chain_acn12", &(info->chain_asic_num[11]), false); + root = api_add_int(root, "chain_acn13", &(info->chain_asic_num[12]), false); + root = api_add_int(root, "chain_acn14", &(info->chain_asic_num[13]), false); + root = api_add_int(root, "chain_acn15", &(info->chain_asic_num[14]), false); + root = api_add_int(root, "chain_acn16", &(info->chain_asic_num[15]), false); + + //applog(LOG_ERR, "chain asic status:%s", info->chain_asic_status_t[0]); + root = api_add_string(root, "chain_acs1", info->chain_asic_status_t[0], false); + root = api_add_string(root, "chain_acs2", info->chain_asic_status_t[1], false); + root = api_add_string(root, "chain_acs3", info->chain_asic_status_t[2], false); + root = api_add_string(root, "chain_acs4", info->chain_asic_status_t[3], false); + root = api_add_string(root, "chain_acs5", info->chain_asic_status_t[4], false); + root = api_add_string(root, "chain_acs6", info->chain_asic_status_t[5], false); + root = api_add_string(root, "chain_acs7", info->chain_asic_status_t[6], false); + root = api_add_string(root, "chain_acs8", info->chain_asic_status_t[7], false); + root = api_add_string(root, "chain_acs9", info->chain_asic_status_t[8], false); + root = api_add_string(root, "chain_acs10", info->chain_asic_status_t[9], false); + root = api_add_string(root, "chain_acs11", info->chain_asic_status_t[10], false); + root = api_add_string(root, "chain_acs12", info->chain_asic_status_t[11], false); + root = api_add_string(root, "chain_acs13", info->chain_asic_status_t[12], false); + root = api_add_string(root, "chain_acs14", info->chain_asic_status_t[13], false); + root = api_add_string(root, "chain_acs15", info->chain_asic_status_t[14], false); + root = api_add_string(root, "chain_acs16", info->chain_asic_status_t[15], false); + + //root = api_add_int(root, "chain_acs1", &(info->chain_asic_status[0]), false); + //root = api_add_int(root, "chain_acs2", &(info->chain_asic_status[1]), false); + //root = api_add_int(root, "chain_acs3", &(info->chain_asic_status[2]), false); + //root = api_add_int(root, "chain_acs4", &(info->chain_asic_status[3]), false); + + return root; +} + +static void bitmain_shutdown(struct thr_info *thr) +{ + do_bitmain_close(thr); +} + +char *set_bitmain_dev(char *arg) +{ + if(arg == NULL || strlen(arg) <= 0) { + memcpy(opt_bitmain_dev, 0, 256); + } else { + strncpy(opt_bitmain_dev, arg, 256); + } + applog(LOG_DEBUG, "BTM set device: %s", opt_bitmain_dev); + return NULL; +} + +char *set_bitmain_fan(char *arg) +{ + int val1, val2, ret; + + ret = sscanf(arg, "%d-%d", &val1, &val2); + if (ret < 1) + return "No values passed to bitmain-fan"; + if (ret == 1) + val2 = val1; + + if (val1 < 0 || val1 > 100 || val2 < 0 || val2 > 100 || val2 < val1) + return "Invalid value passed to bitmain-fan"; + + opt_bitmain_fan_min = val1 * BITMAIN_PWM_MAX / 100; + opt_bitmain_fan_max = val2 * BITMAIN_PWM_MAX / 100; + + return NULL; +} + +char *set_bitmain_freq(char *arg) +{ + int val1, val2, ret; + + ret = sscanf(arg, "%d-%d", &val1, &val2); + if (ret < 1) + return "No values passed to bitmain-freq"; + if (ret == 1) + val2 = val1; + + if (val1 < BITMAIN_MIN_FREQUENCY || val1 > BITMAIN_MAX_FREQUENCY || + val2 < BITMAIN_MIN_FREQUENCY || val2 > BITMAIN_MAX_FREQUENCY || + val2 < val1) + return "Invalid value passed to bitmain-freq"; + + opt_bitmain_freq_min = val1; + opt_bitmain_freq_max = val2; + + return NULL; +} + +struct device_drv bitmain_drv = { + .drv_id = DRIVER_bitmain, + .dname = "Bitmain", + .name = "BTM", + .drv_detect = bitmain_detect, + .thread_prepare = bitmain_prepare, + .hash_work = hash_queued_work, + .queue_full = bitmain_fill, + .scanwork = bitmain_scanhash, + .flush_work = bitmain_flush_work, + .get_api_stats = bitmain_api_stats, + .get_statline_before = get_bitmain_statline_before, + .reinit_device = bitmain_init, + .thread_shutdown = bitmain_shutdown, +}; diff --git a/driver-bitmain.h b/driver-bitmain.h new file mode 100644 index 0000000000..7774ec5281 --- /dev/null +++ b/driver-bitmain.h @@ -0,0 +1,318 @@ +/* + * Copyright 2013 BitMain project + * Copyright 2013 BitMain + * + * 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 BITMAIN_H +#define BITMAIN_H + +#ifdef USE_BITMAIN + +#include "util.h" + +//#define BITMAIN_TYPE_S1 +//#define BITMAIN_TYPE_S2 +//#define BITMAIN_TYPE_S3 +#define BITMAIN_TYPE_S4 + +#define BITMAIN_RESET_FAULT_DECISECONDS 1 +#define BITMAIN_MINER_THREADS 1 + +#define BITMAIN_IO_SPEED 115200 +#define BITMAIN_HASH_TIME_FACTOR ((float)1.67/0x32) +#define BITMAIN_RESET_PITCH (300*1000*1000) + +#define BITMAIN_TOKEN_TYPE_TXCONFIG 0x51 +#define BITMAIN_TOKEN_TYPE_TXTASK 0x52 +#define BITMAIN_TOKEN_TYPE_RXSTATUS 0x53 + +#define BITMAIN_DATA_TYPE_RXSTATUS 0xa1 +#define BITMAIN_DATA_TYPE_RXNONCE 0xa2 + +#define BITMAIN_FAN_FACTOR 60 +#define BITMAIN_PWM_MAX 0xA0 +#define BITMAIN_DEFAULT_FAN_MIN 20 +#define BITMAIN_DEFAULT_FAN_MAX 100 +#define BITMAIN_DEFAULT_FAN_MAX_PWM 0xA0 /* 100% */ +#define BITMAIN_DEFAULT_FAN_MIN_PWM 0x20 /* 20% */ + +#define BITMAIN_TEMP_TARGET 50 +#define BITMAIN_TEMP_HYSTERESIS 3 +#define BITMAIN_TEMP_OVERHEAT 60 + +#define BITMAIN_DEFAULT_TIMEOUT 0x2D +#define BITMAIN_MIN_FREQUENCY 10 +#define BITMAIN_MAX_FREQUENCY 1000000 +#define BITMAIN_TIMEOUT_FACTOR 12690 +#define BITMAIN_DEFAULT_FREQUENCY 282 +#define BITMAIN_DEFAULT_VOLTAGE_T "0725" +#define BITMAIN_DEFAULT_VOLTAGE0 0x07 +#define BITMAIN_DEFAULT_VOLTAGE1 0x25 +#define BITMAIN_DEFAULT_CHAIN_NUM 8 +#define BITMAIN_DEFAULT_ASIC_NUM 32 +#define BITMAIN_DEFAULT_REG_DATA 0 + +#define BITMAIN_AUTO_CYCLE 1024 + +#define BITMAIN_FTDI_READSIZE 2048 +#define BITMAIN_USB_PACKETSIZE 512 +#define BITMAIN_SENDBUF_SIZE 8192 +#define BITMAIN_READBUF_SIZE 8192 +#define BITMAIN_RESET_TIMEOUT 100 +#define BITMAIN_READ_TIMEOUT 18 /* Enough to only half fill the buffer */ +#define BITMAIN_LATENCY 1 + +#ifdef BITMAIN_TYPE_S1 +#define BITMAIN_MAX_WORK_NUM 8 +#define BITMAIN_MAX_WORK_QUEUE_NUM 64 +#define BITMAIN_MAX_DEAL_QUEUE_NUM 1 +#define BITMAIN_MAX_NONCE_NUM 8 +#define BITMAIN_MAX_CHAIN_NUM 8 +#define BITMAIN_MAX_TEMP_NUM 32 +#define BITMAIN_MAX_FAN_NUM 32 +#define BITMAIN_ARRAY_SIZE 16384 +#define BITMAIN_SEND_STATUS_TIME 10 //s +#define BITMAIN_SEND_FULL_SPACE 128 +#endif + +#ifdef BITMAIN_TYPE_S2 +#define BITMAIN_MAX_WORK_NUM 64 +#define BITMAIN_MAX_WORK_QUEUE_NUM 4096 +#define BITMAIN_MAX_DEAL_QUEUE_NUM 32 +#define BITMAIN_MAX_NONCE_NUM 128 +#define BITMAIN_MAX_CHAIN_NUM 16 +#define BITMAIN_MAX_TEMP_NUM 32 +#define BITMAIN_MAX_FAN_NUM 32 +#define BITMAIN_ARRAY_SIZE 16384 +#define BITMAIN_SEND_STATUS_TIME 15 //s +#define BITMAIN_SEND_FULL_SPACE 512 +#endif + +#ifdef BITMAIN_TYPE_S3 +#define BITMAIN_MAX_WORK_NUM 8 +#define BITMAIN_MAX_WORK_QUEUE_NUM 1024 +#define BITMAIN_MAX_DEAL_QUEUE_NUM 2 +#define BITMAIN_MAX_NONCE_NUM 128 +#define BITMAIN_MAX_CHAIN_NUM 8 +#define BITMAIN_MAX_TEMP_NUM 32 +#define BITMAIN_MAX_FAN_NUM 32 +#define BITMAIN_ARRAY_SIZE 16384 +#define BITMAIN_SEND_STATUS_TIME 15 //s +#define BITMAIN_SEND_FULL_SPACE 256 +#endif + +#ifdef BITMAIN_TYPE_S4 +#define BITMAIN_MAX_WORK_NUM 64 +#define BITMAIN_MAX_WORK_QUEUE_NUM 4096 +#define BITMAIN_MAX_DEAL_QUEUE_NUM 32 +#define BITMAIN_MAX_NONCE_NUM 128 +#define BITMAIN_MAX_CHAIN_NUM 16 +#define BITMAIN_MAX_TEMP_NUM 32 +#define BITMAIN_MAX_FAN_NUM 32 +#define BITMAIN_ARRAY_SIZE 16384*2 +#define BITMAIN_SEND_STATUS_TIME 15 //s +#define BITMAIN_SEND_FULL_SPACE 512 +#endif + +struct bitmain_packet_head { + uint8_t token_type; + uint8_t version; + uint16_t length; +} __attribute__((packed, aligned(4))); + +struct bitmain_txconfig_token { + uint8_t token_type; + uint8_t version; + uint16_t length; + uint8_t reset :1; + uint8_t fan_eft :1; + uint8_t timeout_eft :1; + uint8_t frequency_eft :1; + uint8_t voltage_eft :1; + uint8_t chain_check_time_eft :1; + uint8_t chip_config_eft :1; + uint8_t hw_error_eft :1; + uint8_t beeper_ctrl :1; + uint8_t temp_over_ctrl :1; + uint8_t fan_home_mode :1; + uint8_t reserved1 :5; + uint8_t chain_check_time; + uint8_t reserved2; + + uint8_t chain_num; + uint8_t asic_num; + uint8_t fan_pwm_data; + uint8_t timeout_data; + + uint16_t frequency; + uint8_t voltage[2]; + + uint8_t reg_data[4]; + uint8_t chip_address; + uint8_t reg_address; + uint16_t crc; +} __attribute__((packed, aligned(4))); + +struct bitmain_txtask_work { + uint32_t work_id; + uint8_t midstate[32]; + uint8_t data2[12]; +} __attribute__((packed, aligned(4))); + +struct bitmain_txtask_token { + uint8_t token_type; + uint8_t version; + uint16_t length; + uint8_t new_block :1; + uint8_t reserved1 :7; + uint8_t diff; + uint16_t net_diff; + struct bitmain_txtask_work works[BITMAIN_MAX_WORK_NUM]; + uint16_t crc; +} __attribute__((packed, aligned(4))); + +struct bitmain_rxstatus_token { + uint8_t token_type; + uint8_t version; + uint16_t length; + uint8_t chip_status_eft :1; + uint8_t detect_get :1; + uint8_t reserved1 :6; + uint8_t reserved2[3]; + + uint8_t chip_address; + uint8_t reg_address; + uint16_t crc; +} __attribute__((packed, aligned(4))); + +struct bitmain_rxstatus_data { + uint8_t data_type; + uint8_t version; + uint16_t length; + uint8_t chip_value_eft :1; + uint8_t reserved1 :3; + uint8_t get_blk_num :4; + uint8_t chain_num; + uint16_t fifo_space; + uint8_t hw_version[4]; + uint8_t fan_num; + uint8_t temp_num; + uint16_t fan_exist; + uint32_t temp_exist; + uint32_t nonce_error; + uint32_t reg_value; + uint32_t chain_asic_exist[BITMAIN_MAX_CHAIN_NUM*8]; + uint32_t chain_asic_status[BITMAIN_MAX_CHAIN_NUM*8]; + uint8_t chain_asic_num[BITMAIN_MAX_CHAIN_NUM]; + uint8_t temp[BITMAIN_MAX_TEMP_NUM]; + uint8_t fan[BITMAIN_MAX_FAN_NUM]; + uint16_t crc; +} __attribute__((packed, aligned(4))); + +struct bitmain_rxnonce_nonce { + uint32_t work_id; + uint32_t nonce; +} __attribute__((packed, aligned(4))); + +struct bitmain_rxnonce_data { + uint8_t data_type; + uint8_t version; + uint16_t length; + uint16_t fifo_space; + uint16_t diff; + uint64_t total_nonce_num; + struct bitmain_rxnonce_nonce nonces[BITMAIN_MAX_NONCE_NUM]; + uint16_t crc; +} __attribute__((packed, aligned(4))); + +struct bitmain_info { + int baud; + int chain_num; + int asic_num; + int chain_asic_num[BITMAIN_MAX_CHAIN_NUM]; + uint32_t chain_asic_exist[BITMAIN_MAX_CHAIN_NUM*8]; + uint32_t chain_asic_status[BITMAIN_MAX_CHAIN_NUM*8]; + char chain_asic_status_t[BITMAIN_MAX_CHAIN_NUM][320]; + int timeout; + int errorcount; + uint32_t nonce_error; + uint32_t last_nonce_error; + uint8_t reg_data[4]; + + int fan_num; + int fan[BITMAIN_MAX_FAN_NUM]; + int temp_num; + int temp[BITMAIN_MAX_TEMP_NUM]; + + int temp_max; + int temp_avg; + int temp_history_count; + int temp_history_index; + int temp_sum; + int temp_old; + int fan_pwm; + uint64_t total_nonce_num; + + int frequency; + char frequency_t[256]; + uint8_t voltage[2]; + char voltage_t[8]; + + int diff; + + int no_matching_work; + //int matching_work[BITMAIN_DEFAULT_CHAIN_NUM]; + + struct thr_info *thr; + pthread_t read_thr; + pthread_t write_thr; + pthread_mutex_t lock; + pthread_mutex_t qlock; + pthread_cond_t qcond; + cgsem_t write_sem; + int nonces; + int fifo_space; + int hw_version[4]; + unsigned int last_work_block; + struct timeval last_status_time; + int send_full_space; + + int auto_queued; + int auto_nonces; + int auto_hw; + + int idle; + bool reset; + bool overheat; + bool optimal; +}; + +#define BITMAIN_READ_SIZE 12 + +#define BTM_GETS_ERROR -1 +#define BTM_GETS_OK 0 + +#define BTM_SEND_ERROR -1 +#define BTM_SEND_OK 0 + +#define BITMAIN_READ_TIME(baud) ((double)BITMAIN_READ_SIZE * (double)8.0 / (double)(baud)) +#define ASSERT1(condition) __maybe_unused static char sizeof_uint32_t_must_be_4[(condition)?1:-1] +ASSERT1(sizeof(uint32_t) == 4); + +extern struct bitmain_info **bitmain_info; +extern char opt_bitmain_dev[256]; +extern int opt_bitmain_temp; +extern int opt_bitmain_overheat; +extern int opt_bitmain_fan_min; +extern int opt_bitmain_fan_max; +extern bool opt_bitmain_auto; +extern char *set_bitmain_dev(char *arg); +extern char *set_bitmain_fan(char *arg); + +#endif /* USE_BITMAIN */ +#endif /* BITMAIN_H */ diff --git a/driver-blockerupter.c b/driver-blockerupter.c new file mode 100644 index 0000000000..0c57ec8fa0 --- /dev/null +++ b/driver-blockerupter.c @@ -0,0 +1,501 @@ +#include "config.h" +#include +#include +#include +#include +#include +#include +#include +#include +#ifndef WIN32 +#include +#include +#include +#ifndef O_CLOEXEC +#define O_CLOEXEC 0 +#endif +#else +#include +#include +#endif + +#include "elist.h" +#include "miner.h" +#include "driver-blockerupter.h" +#include "usbutils.h" + +static void blockerupter_space_mode(struct cgpu_info *blockerupter) +{ + int interface; + unsigned int bits = 0; + + interface = usb_interface(blockerupter); + + bits |= CP210X_BITS_DATA_8; + bits |= CP210X_BITS_PARITY_SPACE; + + usb_transfer_data(blockerupter, CP210X_TYPE_OUT, CP210X_SET_LINE_CTL, bits, interface, NULL, 0, C_SETPARITY); +} + +static void blockerupter_mark_mode(struct cgpu_info *blockerupter) +{ + int interface; + unsigned int bits = 0; + + interface = usb_interface(blockerupter); + + bits |= CP210X_BITS_DATA_8; + bits |= CP210X_BITS_PARITY_MARK; + + usb_transfer_data(blockerupter, CP210X_TYPE_OUT, CP210X_SET_LINE_CTL, bits, interface, NULL, 0, C_SETPARITY); + +} + +static void blockerupter_init_com(struct cgpu_info *blockerupter) +{ + uint32_t baudrate; + int interface; + + if (blockerupter->usbinfo.nodev) + return; + + interface = usb_interface(blockerupter); + + // Enable the UART + usb_transfer_data(blockerupter, CP210X_TYPE_OUT, CP210X_REQUEST_IFC_ENABLE, + CP210X_VALUE_UART_ENABLE, interface, NULL, 0, C_ENABLE_UART); + if (blockerupter->usbinfo.nodev) + return; + + // Set data control + usb_transfer_data(blockerupter, CP210X_TYPE_OUT, CP210X_REQUEST_DATA, CP210X_VALUE_DATA, + interface, NULL, 0, C_SETDATA); + + if (blockerupter->usbinfo.nodev) + return; + + // Set the baud + baudrate = BET_BAUD; + + usb_transfer_data(blockerupter, CP210X_TYPE_OUT, CP210X_REQUEST_BAUD, 0, + interface, &baudrate, sizeof (baudrate), C_SETBAUD); + + // Set space mode + blockerupter_space_mode(blockerupter); +} + +static int blockerupter_send(struct cgpu_info *blockerupter, char *data, int len) +{ + int err; + int bytes_sent; + + if (unlikely(blockerupter->usbinfo.nodev)) + return SEND_FAIL; + + err = usb_write(blockerupter, data, len, &bytes_sent, C_BET_WRITE); + + if (err || bytes_sent != len) { + applog(LOG_DEBUG, "blockerupter: Send (%d/%d)", bytes_sent, len); + return SEND_FAIL; + } + + return SEND_OK; +} + +static int blockerupter_read(struct cgpu_info *blockerupter, char *data, int len) +{ + int err; + int bytes_read; + + if (unlikely(blockerupter->usbinfo.nodev)) + return READ_FAIL; + + err = usb_read_timeout(blockerupter, data, len, &bytes_read, 2, C_BET_READ); + + if (err || bytes_read != len) { + applog(LOG_DEBUG, "blockerupter: Read (%d/%d)", bytes_read, len); + return READ_FAIL; + } + + return READ_OK; +} + +static void blockerupter_setclock(struct cgpu_info *blockerupter, uint8_t clock) +{ + struct blockerupter_info *info; + info = blockerupter->device_data; + char command; + int err; + + command = C_GCK | clock; + info->clock = clock; + err = blockerupter_send(blockerupter, &command, 1); + if (!err) + applog(LOG_DEBUG, "%s%d: Set Clock to %d MHz", blockerupter->drv->name, + blockerupter->device_id, (clock + 1) * 10 / 2); +} + +static void blockerupter_setdiff(struct cgpu_info *blockerupter, int diff) +{ + struct blockerupter_info *info; + info = blockerupter->device_data; + char command,bits; + int err; + int local_diff; + + // min_diff for driver is 64 + if (diff >= 262144) { + bits = 3; + local_diff = 262144; + } else if (diff >= 4096) { + bits = 2; + local_diff = 4096; + } else { + bits = 1; + local_diff = 64; + } + + if (local_diff == info->diff) + return; + + command = C_DIF | bits; + err = blockerupter_send(blockerupter, &command, 1); + if (!err) { + applog(LOG_DEBUG, "%s%d: Set Diff Bits to %d", blockerupter->drv->name, + blockerupter->device_id, bits); + info->diff = local_diff; + } +} + +static void blockerupter_setrolling(struct cgpu_info *blockerupter, uint8_t rolling) +{ + struct blockerupter_info *info; + info = blockerupter->device_data; + char command; + int err; + + command = C_LPO | rolling; + err = blockerupter_send(blockerupter, &command, 1); + + if (!err) { + applog(LOG_DEBUG, "%s%d: Set nTime Rolling to %d seconds", blockerupter->drv->name, + blockerupter->device_id, (rolling + 1) * 30); + info->rolling = (rolling + 1) * 30; + } +} + +static void blockerupter_init(struct cgpu_info *blockerupter) +{ + struct blockerupter_info *info; + + info = blockerupter->device_data; + // Set Clock + if (!opt_bet_clk || opt_bet_clk< 19 || opt_bet_clk > 31) { + opt_bet_clk = BET_CLOCK_DEFAULT; + } + blockerupter_setclock(blockerupter, opt_bet_clk); + info->clock = (opt_bet_clk + 1) * 10; + info->expected = info->clock * 24 * 32 * info->found / 1000.0; + // Set Diff + blockerupter_setdiff(blockerupter, BET_DIFF_DEFAULT); + info->diff = BET_DIFF_DEFAULT; + // Set nTime Rolling + blockerupter_setrolling(blockerupter, BET_ROLLING_DEFAULT); + info->rolling = (BET_ROLLING_DEFAULT + 1) * 30; + cgtime(&info->start_time); +} + +static struct cgpu_info *blockerupter_detect_one(struct libusb_device *dev, struct usb_find_devices *found) +{ + struct blockerupter_info *info; + struct cgpu_info *blockerupter = usb_alloc_cgpu(&blockerupter_drv, 1); + int i, err; + char reset = C_RES; + + if (!usb_init(blockerupter, dev, found)) { + applog(LOG_ERR, "Blockerupter usb init failed"); + blockerupter = usb_free_cgpu(blockerupter); + return NULL; + } + + blockerupter->device_data = (struct blockerupter_info *) malloc(sizeof(struct blockerupter_info)); + info = blockerupter->device_data; + memset(info, 0, sizeof(blockerupter_info)); + blockerupter_init_com(blockerupter); + + err = blockerupter_send(blockerupter, &reset, 1); + if (err) { + applog(LOG_ERR, "Blockerupter detect failed"); + blockerupter = usb_free_cgpu(blockerupter); + return NULL; + } + cgsleep_ms(5000); + + + for (i = 0; i < BET_MAXBOARDS; i++) { + char detect, answer; + + answer = 0; + detect = C_ASK | (uint8_t)i; + blockerupter_send(blockerupter, &detect, 1); + blockerupter_read(blockerupter, &answer, 1); + if (answer == A_WAL) { + applog(LOG_DEBUG, "BlockErupter found Board: %d", i); + info->boards[i] = 1; + info->found++; + } else { + applog(LOG_DEBUG, "BlockErupter missing board: %d, received %02x", + i, answer); + } + } + + if (!info->found) { + usb_free_cgpu(blockerupter); + free(info); + return NULL; + } else { + blockerupter->threads = 1; + add_cgpu(blockerupter); + applog(LOG_DEBUG, "Add BlockErupter with %d/%d Boards", info->found, + BET_MAXBOARDS); + blockerupter_init(blockerupter); + return blockerupter; + } +} + +static inline void blockerupter_detect(bool __maybe_unused hotplug) +{ + usb_detect(&blockerupter_drv, blockerupter_detect_one); +} + +static struct api_data *blockerupter_api_stats(struct cgpu_info *blockerupter) +{ + struct blockerupter_info *info = blockerupter->device_data; + struct api_data *root = NULL; + struct timeval now, elapsed; + char buf[32]; + int i; + + cgtime(&now); + timersub(&now, &info->start_time, &elapsed); + + info->hashrate = elapsed.tv_sec ? info->hashes * 4.295 / elapsed.tv_sec : 0; + info->eff = info->hashrate / info->expected; + + root = api_add_int(root, "Nonces", &info->nonces, false); + root = api_add_uint8(root, "Board", &info->found, false); + root = api_add_int(root, "Clock", &info->clock, false); + root = api_add_int(root,"Accepted", &info->accepted, false); + root = api_add_double(root, "HashRate", &info->hashrate , false); + root = api_add_double(root, "Expected", &info->expected , false); + root = api_add_double(root, "Efficiency", &info->eff, false); + for (i = 0; i < BET_MAXBOARDS; i++) { + double brd_hashrate; + + if (info->boards[i]) { + sprintf(buf, "Board%02d accepted", i); + root = api_add_int(root, buf, &info->b_info[i].accepted, false); + sprintf(buf, "Board%02d nonces", i); + root = api_add_int(root, buf, &info->b_info[i].nonces, false); + sprintf(buf, "Board%02d hwerror", i); + root = api_add_double(root, buf, &info->b_info[i].hwe, false); + sprintf(buf, "Board%02d hashrate", i); + brd_hashrate = elapsed.tv_sec ? info->b_info[i].hashes * 4.295 / elapsed.tv_sec : 0; + root = api_add_double(root, buf, &brd_hashrate, false); + } + } + + return root; +} + +static bool blockerupter_prepare(struct thr_info *thr) +{ + struct cgpu_info *blockerupter = thr->cgpu; + struct blockerupter_info *info = blockerupter->device_data; + + cglock_init(&(info->pool.data_lock)); + + return true; +} + +static void blockerupter_sendjob(struct cgpu_info *blockerupter, int board) +{ + struct blockerupter_info *info = blockerupter->device_data; + struct thr_info *thr = blockerupter->thr[0]; + struct work *work; + uint8_t command, answer; + int err; + + work = get_work(thr, thr->id); + memcpy(&info->works[info->work_idx],work,sizeof(struct work)); + + blockerupter_setdiff(blockerupter,floor(work->work_difficulty)); + + command = C_JOB | (uint8_t)board; + blockerupter_send(blockerupter, (char *)&command, 1); + blockerupter_mark_mode(blockerupter); + cgsleep_ms(1); + + blockerupter_send(blockerupter, (char *)(work->midstate), 32); + blockerupter_send(blockerupter, (char *)&(work->data[64]), 12); + blockerupter_send(blockerupter, (char *)&work->nonce2, 4); + blockerupter_send(blockerupter, (char *)&info->work_idx, 1); + + cgsleep_ms(1); + blockerupter_space_mode(blockerupter); + + answer = 0; + err = blockerupter_read(blockerupter, (char *)&answer, 1); + + cgtime(&info->last_job); + + if (err || answer != A_GET) { + applog(LOG_ERR, "%s%d: Sync Error", blockerupter->drv->name, blockerupter->device_id); + } else { + info->b_info[board].job_count++; + applog(LOG_DEBUG, "%s%d: Sent work %d to board %d", blockerupter->drv->name, + blockerupter->device_id, info->work_idx, board); + } + + info->work_idx++; + if (info->work_idx >= BET_WORK_FIFO) + info->work_idx = 0; +} + +static uint64_t blockerupter_checknonce(struct cgpu_info *blockerupter, struct blockerupter_response *resp, int board) +{ + uint8_t test; + struct blockerupter_info *info; + struct thr_info *thr = blockerupter->thr[0]; + struct work work; + uint32_t nonce; + uint64_t hashes=0; + int i; + struct board_info *cur_brd; + struct asic_info *cur_asic; + + info = blockerupter->device_data; + work = info->works[resp->work_idx]; + + nonce = *(uint32_t *)resp->nonce; + + applog(LOG_DEBUG, "%s%d: Nonce %08x from board %d, asic %d for work %d", + blockerupter->drv->name, blockerupter->device_id, *(uint32_t *) resp->nonce, + board, resp->chip, resp->work_idx); + + memcpy(work.data + 4 + 32 + 32, resp->ntime, 4); + __bin2hex(work.ntime, resp->ntime, 4); + + info->nonces++; + cur_brd = &info->b_info[board]; + cur_brd->nonces++; + cur_asic = &info->b_info[board].asics[resp->chip]; + cur_asic->nonces++; + + for (i = 0; i < BET_NONCE_FIX; i++) { + test = test_nonce_diff(&work, nonce + i, (double)info->diff); + if (test) { + applog(LOG_DEBUG, "%s%d: Nonce Fix Pass @%d", blockerupter->drv->name, + blockerupter->device_id, i); + info->hashes += info->diff; + cur_brd->hashes += info->diff; + cur_asic->hashes += info->diff; + if (test_nonce_diff(&work, nonce + i, work.work_difficulty)) { + if (submit_nonce(thr, &work, nonce + i)) { + hashes += floor(work.work_difficulty) * (uint64_t) 0xffffffff; + info->accepted++; + cur_brd->accepted++; + cur_asic->accepted++; + } + } + break; + } + } + + if (i == BET_NONCE_FIX) { + applog(LOG_DEBUG, "%s%d: Nonce Fix Failed", blockerupter->drv->name, + blockerupter->device_id); + cur_brd->bad++; + cur_brd->hwe = cur_brd->nonces ? (double)cur_brd->bad / cur_brd->nonces : 0; + cur_asic->bad++; + cur_asic->hwe = cur_asic->nonces ? (double)cur_asic->bad / cur_asic->nonces : 0; + } + return hashes; +} + +static uint64_t blockerupter_getresp(struct cgpu_info *blockerupter, int board) +{ + struct blockerupter_response *resp; + int err; + uint64_t hashes = 0; + + resp = (struct blockerupter_response *) malloc(BET_RESP_SZ); + err = blockerupter_read(blockerupter, (char *)resp, BET_RESP_SZ); + if (!err) + hashes = blockerupter_checknonce(blockerupter, resp, board); + free(resp); + return hashes; +} + +static int64_t blockerupter_scanhash(struct thr_info *thr) +{ + struct cgpu_info *blockerupter = thr->cgpu; + struct blockerupter_info *info = blockerupter->device_data; + char ask; + uint8_t answer; + int i; + int64_t hashes=0; + + if (unlikely(blockerupter->usbinfo.nodev)) { + applog(LOG_ERR, "%s%d: Device disappeared, shutting down thread", + blockerupter->drv->name, blockerupter->device_id); + return -1; + } + + for (i = 0; i < BET_MAXBOARDS; i++) { + if (!info->boards[i]) + continue; + ask = C_ASK | (uint8_t)i; + blockerupter_send(blockerupter, &ask, 1); + cgsleep_ms(1); + answer = 0; + blockerupter_read(blockerupter, (char *)&answer, 1); + + switch (answer) { + case A_WAL: + blockerupter_sendjob(blockerupter, i); + break; + case A_YES: + hashes += blockerupter_getresp(blockerupter, i); + break; + case A_NO: + break; + default: + applog(LOG_ERR, "%s%d: Unexpected value %02x received", blockerupter->drv->name, + blockerupter->device_id, answer); + break; + } + } + + return hashes; +} + +static void blockerupter_flush_work(struct cgpu_info *blockerupter) +{ + uint8_t command = C_LPO | BET_ROLLING_DEFAULT; + + blockerupter_send(blockerupter, (char *)&command, 1); +} + +struct device_drv blockerupter_drv = { + .drv_id = DRIVER_blockerupter, + .dname = "blockerupter", + .name = "BET", + .min_diff = 64, + .get_api_stats = blockerupter_api_stats, + .drv_detect = blockerupter_detect, + .thread_prepare = blockerupter_prepare, + .hash_work = hash_driver_work, + .flush_work = blockerupter_flush_work, + .scanwork = blockerupter_scanhash +}; diff --git a/driver-blockerupter.h b/driver-blockerupter.h new file mode 100644 index 0000000000..4e15f1eece --- /dev/null +++ b/driver-blockerupter.h @@ -0,0 +1,130 @@ +#ifndef _BLOCKERUPTER_H +#define _BLOCKERUPTER_H + +/* +WIN32 Build + +1. Install mxe (check tutorial on http://mxe.cc) + +2. After install mxe +export PATH={PATH_TO_MXE}/usr/bin:$PATH +autoreconf -fi +./configure --host=i686-pc-mingw32 --enable-blockerupter --without-curses CFLAGS=-DCURL_STATICLIB +make + +3. Before starting cgminer +install WinUSB driver for detected CP2102x device with Zadig (Some users might need to reboot) +*/ + +#include "miner.h" +#include "util.h" + +#define BET_MAXBOARDS 32 +#define BET_MAXASICS 48 +#define BET_BAUD 460800 + +#define BET_CLOCK_MAX 29 +#define BET_CLOCK_DEFAULT 23 +#define BET_DIFF_DEFAULT 64 +#define BET_ROLLING_DEFAULT 5 +extern int opt_bet_clk; + +#define BET_WORK_FIFO 128 +#define BET_NONCE_FIX 4 + +#define SEND_OK 0 +#define SEND_FAIL 1 +#define READ_OK 0 +#define READ_FAIL 1 + +// Global Commands +// resets all mega88, recv nothing +#define C_RES (0 << 5) +// stop jobs on all boards, set nTime rolling to (BoardID+1)*30, recv nothing +#define C_LPO (1 << 5) +// set clock for all boards, clock = (BoardID+1)*5, recv nothing +#define C_GCK (2 << 5) +// set difficulty bits for all boards with last 2bits from BoardID, recv nothing +#define C_DIF (3 << 5) + +// Board Specific Commands (CMD|BoardID) +// Send midstate(32 bytes), remaining block header(12 bytes), extranonce2(4 bytes) and job index(1 byte) to board +// Recv 0x58 +#define C_JOB (4 << 5) +// Recv current status of board +#define C_ASK (5 << 5) +// Recv (max_asics) bytes of chip test result, (max asics) bytes of clocks, 1 byte of diff bits, 1 byte of max nTime rolling, 1 byte of firmware version. Total (max asics)*2+3 bytes +#define C_TRS (6 << 5) + +// answers on C_ASK|BoardID +// Idle, waiting for new job +#define A_WAL 0x56 +// Mining but no nonce yet +#define A_NO 0xa6 +// Found nonce, followed with midstate(32 bytes), remaining block header(12 bytes), extranonce2(4 bytes), nonce(4 bytes), job index(1 byte), chip index(1 byte). Total 54 bytes. +#define A_YES 0x5A + +// answer on C_JOB|BoardID +#define A_GET 0x58 + +#pragma pack(1) + +typedef struct asic_info { + int bad; + int accepted; + int nonces; + int hashes; + double hwe; +} asic_info; + +#pragma pack(1) + +typedef struct board_info { + int bad; + int job_count; + int nonces; + int accepted; + int hashes; + double hashrate; + double hwe; + struct asic_info asics[BET_MAXASICS]; +} board_info; + +#pragma pack(1) + +typedef struct blockerupter_info { + struct pool pool; + uint8_t found; + int clock; + int nonces; + int diff; + int rolling; + int accepted; + int hashes; + double hashrate; + double expected; + double eff; + uint8_t work_idx; + struct work works[BET_WORK_FIFO]; + uint8_t boards[BET_MAXBOARDS]; + board_info b_info[BET_MAXBOARDS]; + struct timeval start_time; + struct timeval last_job; +} blockerupter_info; + + +#pragma pack(1) + +typedef struct blockerupter_response { + uint8_t midstate[32]; + uint8_t merkle[4]; + uint8_t ntime[4]; + uint8_t diff[4]; + uint8_t exnonc2[4]; + uint8_t nonce[4]; + uint8_t work_idx; + uint8_t chip; +} blockerupter_response; +#define BET_RESP_SZ (sizeof(blockerupter_response)) + +#endif diff --git a/driver-bmsc.c b/driver-bmsc.c new file mode 100644 index 0000000000..ad5a9808c8 --- /dev/null +++ b/driver-bmsc.c @@ -0,0 +1,2031 @@ +/* + * Copyright 2012-2013 Andrew Smith + * Copyright 2013 Con Kolivas + * Copyright 2013 Lingchao Xu + * + * 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. + */ + +/* + * Those code should be works fine with AntMiner U1 of Bmsc. + * Operation: + * No detection implement. + * Input: 64B = 32B midstate + 20B fill bytes + last 12 bytes of block head. + * Return: send back 40bits immediately when Bmsc found a valid nonce. + * no query protocol implemented here, if no data send back in ~11.3 + * seconds (full cover time on 32bit nonce range by 380MH/s speed) + * just send another work. + * Notice: + * 1. Bmsc will start calculate when you push a work to them, even they + * are busy. + * 2. Bmsc will stop work when: a valid nonce has been found or 40 bits + * nonce range is completely calculated. + */ + + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "config.h" + +#ifdef WIN32 +#include +#endif + +#include "compat.h" +#include "miner.h" +#include "usbutils.h" + +// The serial I/O speed - Linux uses a define 'B115200' in bits/termios.h +#define BMSC_IO_SPEED 115200 + +#define BMSC_NONCE_ARRAY_SIZE 6 + +// The size of a successful nonce read +#define BMSC_READ_SIZE 5 + +// Ensure the sizes are correct for the Serial read +#if (BMSC_READ_SIZE != 5) +#error BMSC_READ_SIZE must be 5 +#endif +#define ASSERT1(condition) __maybe_unused static char sizeof_uint32_t_must_be_4[(condition)?1:-1] +ASSERT1(sizeof(uint32_t) == 4); + +// TODO: USB? Different calculation? - see usbstats to work it out e.g. 1/2 of normal send time +// or even use that number? 1/2 +// #define BMSC_READ_TIME(baud) ((double)BMSC_READ_SIZE * (double)8.0 / (double)(baud)) +// maybe 1ms? +#define BMSC_READ_TIME(baud) (0.001) + +// USB ms timeout to wait - user specified timeouts are multiples of this +#define BMSC_WAIT_TIMEOUT 100 +#define BMSC_CMR2_TIMEOUT 1 +#define BMSC_READ_BUF_LEN 8192 + +// Defined in multiples of BMSC_WAIT_TIMEOUT +// Must of course be greater than BMSC_READ_COUNT_TIMING/BMSC_WAIT_TIMEOUT +// There's no need to have this bigger, since the overhead/latency of extra work +// is pretty small once you get beyond a 10s nonce range time and 10s also +// means that nothing slower than 429MH/s can go idle so most bmsc devices +// will always mine without idling +#define BMSC_READ_TIME_LIMIT_MAX 100 + +// In timing mode: Default starting value until an estimate can be obtained +// 5000 ms allows for up to a ~840MH/s device +#define BMSC_READ_COUNT_TIMING 5000 +#define BMSC_READ_COUNT_MIN BMSC_WAIT_TIMEOUT +#define SECTOMS(s) ((int)((s) * 1000)) +// How many ms below the expected completion time to abort work +// extra in case the last read is delayed +#define BMSC_READ_REDUCE ((int)(BMSC_WAIT_TIMEOUT * 1.5)) + +// For a standard Bmsc (to 5 places) +// Since this rounds up a the last digit - it is a slight overestimate +// Thus the hash rate will be a VERY slight underestimate +// (by a lot less than the displayed accuracy) +// Minor inaccuracy of these numbers doesn't affect the work done, +// only the displayed MH/s +#define BMSC_REV3_HASH_TIME 0.0000000026316 +#define LANCELOT_HASH_TIME 0.0000000025000 +#define ASICMINERUSB_HASH_TIME 0.0000000029761 +// TODO: What is it? +#define CAIRNSMORE1_HASH_TIME 0.0000000027000 +// Per FPGA +#define CAIRNSMORE2_HASH_TIME 0.0000000066600 +#define NANOSEC 1000000000.0 + +#define CAIRNSMORE2_INTS 4 + +// Bmsc doesn't send a completion message when it finishes +// the full nonce range, so to avoid being idle we must abort the +// work (by starting a new work item) shortly before it finishes +// +// Thus we need to estimate 2 things: +// 1) How many hashes were done if the work was aborted +// 2) How high can the timeout be before the Bmsc is idle, +// to minimise the number of work items started +// We set 2) to 'the calculated estimate' - BMSC_READ_REDUCE +// to ensure the estimate ends before idle +// +// The simple calculation used is: +// Tn = Total time in seconds to calculate n hashes +// Hs = seconds per hash +// Xn = number of hashes +// W = code/usb overhead per work +// +// Rough but reasonable estimate: +// Tn = Hs * Xn + W (of the form y = mx + b) +// +// Thus: +// Line of best fit (using least squares) +// +// Hs = (n*Sum(XiTi)-Sum(Xi)*Sum(Ti))/(n*Sum(Xi^2)-Sum(Xi)^2) +// W = Sum(Ti)/n - (Hs*Sum(Xi))/n +// +// N.B. W is less when aborting work since we aren't waiting for the reply +// to be transferred back (BMSC_READ_TIME) +// Calculating the hashes aborted at n seconds is thus just n/Hs +// (though this is still a slight overestimate due to code delays) +// + +// Both below must be exceeded to complete a set of data +// Minimum how long after the first, the last data point must be +#define HISTORY_SEC 60 +// Minimum how many points a single BMSC_HISTORY should have +#define MIN_DATA_COUNT 5 +// The value MIN_DATA_COUNT used is doubled each history until it exceeds: +#define MAX_MIN_DATA_COUNT 100 + +static struct timeval history_sec = { HISTORY_SEC, 0 }; + +// Store the last INFO_HISTORY data sets +// [0] = current data, not yet ready to be included as an estimate +// Each new data set throws the last old set off the end thus +// keeping a ongoing average of recent data +#define INFO_HISTORY 10 + +#define BMSC_WORK_QUEUE_NUM 36 + +struct BMSC_HISTORY { + struct timeval finish; + double sumXiTi; + double sumXi; + double sumTi; + double sumXi2; + uint32_t values; + uint32_t hash_count_min; + uint32_t hash_count_max; +}; + +enum timing_mode { MODE_DEFAULT, MODE_SHORT, MODE_LONG, MODE_VALUE }; + +static const char *MODE_DEFAULT_STR = "default"; +static const char *MODE_SHORT_STR = "short"; +static const char *MODE_SHORT_STREQ = "short="; +static const char *MODE_LONG_STR = "long"; +static const char *MODE_LONG_STREQ = "long="; +static const char *MODE_VALUE_STR = "value"; +static const char *MODE_UNKNOWN_STR = "unknown"; + +struct BMSC_INFO { + enum sub_ident ident; + int intinfo; + + // time to calculate the golden_ob + uint64_t golden_hashes; + struct timeval golden_tv; + + struct BMSC_HISTORY history[INFO_HISTORY+1]; + uint32_t min_data_count; + + int timeout; + + // seconds per Hash + double Hs; + // ms til we abort + int read_time; + // ms limit for (short=/long=) read_time + int read_time_limit; + + enum timing_mode timing_mode; + bool do_bmsc_timing; + + bool start; + + double fullnonce; + int count; + double W; + uint32_t values; + uint64_t hash_count_range; + + // Determine the cost of history processing + // (which will only affect W) + uint64_t history_count; + struct timeval history_time; + + // bmsc-options + int baud; + int work_division; + int fpga_count; + uint32_t nonce_mask; + + uint8_t cmr2_speed; + bool speed_next_work; + bool flash_next_work; + + struct work * work_queue[BMSC_WORK_QUEUE_NUM]; + int work_queue_index; + + unsigned char nonce_bin[BMSC_NONCE_ARRAY_SIZE][BMSC_READ_SIZE+1]; + int nonce_index; +}; + +#define BMSC_MIDSTATE_SIZE 32 +#define BMSC_UNUSED_SIZE 15 +#define BMSC_WORK_SIZE 12 + +#define BMSC_WORK_DATA_OFFSET 64 + +#define BMSC_CMR2_SPEED_FACTOR 2.5 +#define BMSC_CMR2_SPEED_MIN_INT 100 +#define BMSC_CMR2_SPEED_DEF_INT 180 +#define BMSC_CMR2_SPEED_MAX_INT 220 +#define CMR2_INT_TO_SPEED(_speed) ((uint8_t)((float)_speed / BMSC_CMR2_SPEED_FACTOR)) +#define BMSC_CMR2_SPEED_MIN CMR2_INT_TO_SPEED(BMSC_CMR2_SPEED_MIN_INT) +#define BMSC_CMR2_SPEED_DEF CMR2_INT_TO_SPEED(BMSC_CMR2_SPEED_DEF_INT) +#define BMSC_CMR2_SPEED_MAX CMR2_INT_TO_SPEED(BMSC_CMR2_SPEED_MAX_INT) +#define BMSC_CMR2_SPEED_INC 1 +#define BMSC_CMR2_SPEED_DEC -1 +#define BMSC_CMR2_SPEED_FAIL -10 + +#define BMSC_CMR2_PREFIX ((uint8_t)0xB7) +#define BMSC_CMR2_CMD_SPEED ((uint8_t)0) +#define BMSC_CMR2_CMD_FLASH ((uint8_t)1) +#define BMSC_CMR2_DATA_FLASH_OFF ((uint8_t)0) +#define BMSC_CMR2_DATA_FLASH_ON ((uint8_t)1) +#define BMSC_CMR2_CHECK ((uint8_t)0x6D) + +struct BMSC_WORK { + uint8_t midstate[BMSC_MIDSTATE_SIZE]; + // These 4 bytes are for CMR2 bitstreams that handle MHz adjustment + uint8_t check; + uint8_t data; + uint8_t cmd; + uint8_t prefix; + uint8_t unused[BMSC_UNUSED_SIZE]; + uint8_t workid; + uint8_t work[BMSC_WORK_SIZE]; +}; + +#define END_CONDITION 0x0000ffff + +// Looking for options in --bmsc-timing and --bmsc-options: +// +// Code increments this each time we start to look at a device +// However, this means that if other devices are checked by +// the Bmsc code (e.g. Avalon only as at 20130517) +// they will count in the option offset +// +// This, however, is deterministic so that's OK +// +// If we were to increment after successfully finding an Bmsc +// that would be random since an Bmsc may fail and thus we'd +// not be able to predict the option order +// +// Devices are checked in the order libusb finds them which is ? +// +static int option_offset = -1; + +unsigned char CRC5(unsigned char *ptr, unsigned char len) +{ + unsigned char i, j, k; + unsigned char crc = 0x1f; + + unsigned char crcin[5] = {1, 1, 1, 1, 1}; + unsigned char crcout[5] = {1, 1, 1, 1, 1}; + unsigned char din = 0; + + j = 0x80; + k = 0; + for (i = 0; i < len; i++) + { + if (*ptr & j) { + din = 1; + } else { + din = 0; + } + crcout[0] = crcin[4] ^ din; + crcout[1] = crcin[0]; + crcout[2] = crcin[1] ^ crcin[4] ^ din; + crcout[3] = crcin[2]; + crcout[4] = crcin[3]; + + j = j >> 1; + k++; + if (k == 8) + { + j = 0x80; + k = 0; + ptr++; + } + memcpy(crcin, crcout, 5); + } + crc = 0; + if(crcin[4]) { + crc |= 0x10; + } + if(crcin[3]) { + crc |= 0x08; + } + if(crcin[2]) { + crc |= 0x04; + } + if(crcin[1]) { + crc |= 0x02; + } + if(crcin[0]) { + crc |= 0x01; + } + return crc; +} + +static void _transfer(struct cgpu_info *bmsc, uint8_t request_type, uint8_t bRequest, uint16_t wValue, uint16_t wIndex, uint32_t *data, int siz, enum usb_cmds cmd) +{ + int err; + + err = usb_transfer_data(bmsc, request_type, bRequest, wValue, wIndex, data, siz, cmd); + + applog(LOG_DEBUG, "%s: cgid %d %s got err %d", + bmsc->drv->name, bmsc->cgminer_id, + usb_cmdname(cmd), err); +} + +#define transfer(bmsc, request_type, bRequest, wValue, wIndex, cmd) \ + _transfer(bmsc, request_type, bRequest, wValue, wIndex, NULL, 0, cmd) + +static void bmsc_initialise(struct cgpu_info *bmsc, int baud) +{ + struct BMSC_INFO *info = (struct BMSC_INFO *)(bmsc->device_data); + uint16_t wValue, wIndex; + enum sub_ident ident; + int interface; + + if (bmsc->usbinfo.nodev) + return; + + interface = _usb_interface(bmsc, info->intinfo); + ident = usb_ident(bmsc); + + switch (ident) { + case IDENT_BLT: + case IDENT_LLT: + case IDENT_CMR1: + case IDENT_CMR2: + // Reset + transfer(bmsc, FTDI_TYPE_OUT, FTDI_REQUEST_RESET, FTDI_VALUE_RESET, + interface, C_RESET); + + if (bmsc->usbinfo.nodev) + return; + + // Latency + _usb_ftdi_set_latency(bmsc, info->intinfo); + + if (bmsc->usbinfo.nodev) + return; + + // Set data control + transfer(bmsc, FTDI_TYPE_OUT, FTDI_REQUEST_DATA, FTDI_VALUE_DATA_BLT, + interface, C_SETDATA); + + if (bmsc->usbinfo.nodev) + return; + + // default to BLT/LLT 115200 + wValue = FTDI_VALUE_BAUD_BLT; + wIndex = FTDI_INDEX_BAUD_BLT; + + if (ident == IDENT_CMR1 || ident == IDENT_CMR2) { + switch (baud) { + case 115200: + wValue = FTDI_VALUE_BAUD_CMR_115; + wIndex = FTDI_INDEX_BAUD_CMR_115; + break; + case 57600: + wValue = FTDI_VALUE_BAUD_CMR_57; + wIndex = FTDI_INDEX_BAUD_CMR_57; + break; + default: + quit(1, "bmsc_intialise() invalid baud (%d) for Cairnsmore1", baud); + break; + } + } + + // Set the baud + transfer(bmsc, FTDI_TYPE_OUT, FTDI_REQUEST_BAUD, wValue, + (wIndex & 0xff00) | interface, C_SETBAUD); + + if (bmsc->usbinfo.nodev) + return; + + // Set Modem Control + transfer(bmsc, FTDI_TYPE_OUT, FTDI_REQUEST_MODEM, FTDI_VALUE_MODEM, + interface, C_SETMODEM); + + if (bmsc->usbinfo.nodev) + return; + + // Set Flow Control + transfer(bmsc, FTDI_TYPE_OUT, FTDI_REQUEST_FLOW, FTDI_VALUE_FLOW, + interface, C_SETFLOW); + + if (bmsc->usbinfo.nodev) + return; + + // Clear any sent data + transfer(bmsc, FTDI_TYPE_OUT, FTDI_REQUEST_RESET, FTDI_VALUE_PURGE_TX, + interface, C_PURGETX); + + if (bmsc->usbinfo.nodev) + return; + + // Clear any received data + transfer(bmsc, FTDI_TYPE_OUT, FTDI_REQUEST_RESET, FTDI_VALUE_PURGE_RX, + interface, C_PURGERX); + break; + case IDENT_ICA: + // Set Data Control + transfer(bmsc, PL2303_CTRL_OUT, PL2303_REQUEST_CTRL, PL2303_VALUE_CTRL, + interface, C_SETDATA); + + if (bmsc->usbinfo.nodev) + return; + + // Set Line Control + uint32_t ica_data[2] = { PL2303_VALUE_LINE0, PL2303_VALUE_LINE1 }; + _transfer(bmsc, PL2303_CTRL_OUT, PL2303_REQUEST_LINE, PL2303_VALUE_LINE, + interface, &ica_data[0], PL2303_VALUE_LINE_SIZE, C_SETLINE); + + if (bmsc->usbinfo.nodev) + return; + + // Vendor + transfer(bmsc, PL2303_VENDOR_OUT, PL2303_REQUEST_VENDOR, PL2303_VALUE_VENDOR, + interface, C_VENDOR); + break; + case IDENT_AMU: + // Enable the UART + transfer(bmsc, CP210X_TYPE_OUT, CP210X_REQUEST_IFC_ENABLE, + CP210X_VALUE_UART_ENABLE, + interface, C_ENABLE_UART); + + if (bmsc->usbinfo.nodev) + return; + + // Set data control + transfer(bmsc, CP210X_TYPE_OUT, CP210X_REQUEST_DATA, CP210X_VALUE_DATA, + interface, C_SETDATA); + + if (bmsc->usbinfo.nodev) + return; + + // Set the baud + uint32_t data = CP210X_DATA_BAUD; + _transfer(bmsc, CP210X_TYPE_OUT, CP210X_REQUEST_BAUD, 0, + interface, &data, sizeof(data), C_SETBAUD); + break; + default: + quit(1, "bmsc_intialise() called with invalid %s cgid %i ident=%d", + bmsc->drv->name, bmsc->cgminer_id, ident); + } +} + +#define BTM_NONCE_ERROR -1 +#define BTM_NONCE_OK 0 +#define BTM_NONCE_RESTART 1 +#define BTM_NONCE_TIMEOUT 2 + +static int bmsc_get_nonce(struct cgpu_info *bmsc, unsigned char *buf, struct timeval *tv_start, + struct timeval *tv_finish, struct thr_info *thr, int read_time) +{ + struct BMSC_INFO *info = (struct BMSC_INFO *)(bmsc->device_data); + int err, amt, rc; + + if (bmsc->usbinfo.nodev) + return BTM_NONCE_ERROR; + + cgtime(tv_start); + err = usb_read_ii_timeout_cancellable(bmsc, info->intinfo, (char *)buf, + BMSC_READ_SIZE, &amt, read_time, + C_GETRESULTS); + cgtime(tv_finish); + + if (err < 0 && err != LIBUSB_ERROR_TIMEOUT) { + applog(LOG_ERR, "%s%i: Comms error (rerr=%d amt=%d)", bmsc->drv->name, + bmsc->device_id, err, amt); + dev_error(bmsc, REASON_DEV_COMMS_ERROR); + return BTM_NONCE_ERROR; + } + + if (amt >= BMSC_READ_SIZE) + return BTM_NONCE_OK; + + rc = SECTOMS(tdiff(tv_finish, tv_start)); + if (thr && thr->work_restart) { + applog(LOG_DEBUG, "Bmsc Read: Work restart at %d ms", rc); + return BTM_NONCE_RESTART; + } + + if (amt > 0) + applog(LOG_DEBUG, "Bmsc Read: Timeout reading for %d ms", rc); + else + applog(LOG_DEBUG, "Bmsc Read: No data for %d ms", rc); + return BTM_NONCE_TIMEOUT; +} + +static const char *timing_mode_str(enum timing_mode timing_mode) +{ + switch(timing_mode) { + case MODE_DEFAULT: + return MODE_DEFAULT_STR; + case MODE_SHORT: + return MODE_SHORT_STR; + case MODE_LONG: + return MODE_LONG_STR; + case MODE_VALUE: + return MODE_VALUE_STR; + default: + return MODE_UNKNOWN_STR; + } +} + +static void set_timing_mode(int this_option_offset, struct cgpu_info *bmsc, float readtimeout) +{ + struct BMSC_INFO *info = (struct BMSC_INFO *)(bmsc->device_data); + enum sub_ident ident; + double Hs; + char buf[BUFSIZ+1]; + char *ptr, *comma, *eq; + size_t max; + int i; + + ident = usb_ident(bmsc); + switch (ident) { + case IDENT_ICA: + info->Hs = BMSC_REV3_HASH_TIME; + break; + case IDENT_BLT: + case IDENT_LLT: + info->Hs = LANCELOT_HASH_TIME; + break; + case IDENT_AMU: + info->Hs = ASICMINERUSB_HASH_TIME; + break; + case IDENT_CMR1: + info->Hs = CAIRNSMORE1_HASH_TIME; + break; + case IDENT_CMR2: + info->Hs = CAIRNSMORE2_HASH_TIME; + break; + default: + quit(1, "Bmsc get_options() called with invalid %s ident=%d", + bmsc->drv->name, ident); + } + + info->read_time = 0; + info->read_time_limit = 0; // 0 = no limit + + info->fullnonce = info->Hs * (((double) 0xffffffff) + 1); + info->read_time = (int)(readtimeout * BMSC_WAIT_TIMEOUT); + + if(info->read_time < 0) + info->read_time = 1; + + info->timing_mode = MODE_DEFAULT; + info->do_bmsc_timing = false; + + info->min_data_count = MIN_DATA_COUNT; + + // All values are in multiples of BMSC_WAIT_TIMEOUT + info->read_time_limit *= BMSC_WAIT_TIMEOUT; + + applog(LOG_ERR, "%s%d Init: mode=%s read_time=%dms limit=%dms Hs=%e", + bmsc->drv->name, bmsc->cgminer_id, + timing_mode_str(info->timing_mode), + info->read_time, info->read_time_limit, info->Hs); +} + +static uint32_t mask(int work_division) +{ + uint32_t nonce_mask = 0x7fffffff; + + // yes we can calculate these, but this way it's easy to see what they are + switch (work_division) { + case 1: + nonce_mask = 0xffffffff; + break; + case 2: + nonce_mask = 0x7fffffff; + break; + case 4: + nonce_mask = 0x3fffffff; + break; + case 8: + nonce_mask = 0x1fffffff; + break; + default: + quit(1, "Invalid2 bmsc-options for work_division (%d) must be 1, 2, 4 or 8", work_division); + } + + return nonce_mask; +} + +static void get_options(int this_option_offset, struct cgpu_info *bmsc, int *baud, float *readtimeout) +{ + char buf[BUFSIZ+1]; + char *ptr, *comma, *colon, *colon2; + enum sub_ident ident; + size_t max; + int i, tmp; + float tmpf; + + if (opt_bmsc_options == NULL) + buf[0] = '\0'; + else { + ptr = opt_bmsc_options; + for (i = 0; i < this_option_offset; i++) { + comma = strchr(ptr, ','); + if (comma == NULL) + break; + ptr = comma + 1; + } + + comma = strchr(ptr, ','); + if (comma == NULL) + max = strlen(ptr); + else + max = comma - ptr; + + if (max > BUFSIZ) + max = BUFSIZ; + strncpy(buf, ptr, max); + buf[max] = '\0'; + } + + ident = usb_ident(bmsc); + switch (ident) { + case IDENT_ICA: + case IDENT_BLT: + case IDENT_LLT: + *baud = BMSC_IO_SPEED; + break; + case IDENT_AMU: + *baud = BMSC_IO_SPEED; + break; + case IDENT_CMR1: + *baud = BMSC_IO_SPEED; + break; + case IDENT_CMR2: + *baud = BMSC_IO_SPEED; + break; + default: + quit(1, "Bmsc get_options() called with invalid %s ident=%d", + bmsc->drv->name, ident); + } + + if (*buf) { + colon = strchr(buf, ':'); + if (colon) + *(colon++) = '\0'; + + if (*buf) { + tmp = atoi(buf); + switch (tmp) { + case 115200: + *baud = 115200; + break; + case 57600: + *baud = 57600; + break; + default: + quit(1, "Invalid bmsc-options for baud (%s) must be 115200 or 57600", buf); + } + } + + if (colon && *colon) { + tmpf = atof(colon); + if (tmpf > 0) { + *readtimeout = tmpf; + } else { + quit(1, "Invalid bmsc-options for timeout (%s) must be > 0", colon); + } + } + } +} + +static void get_bandops(unsigned char * core_buf, int *corenum, char *coreenable, int *coresleep) +{ + char buf[512] = {0}; + char *colon, *colon2, * colon3; + int i, len; + + if (opt_bmsc_bandops) { + len = strlen(opt_bmsc_bandops); + if(len <= 0 || len >= 512) { + quit(1, "Invalid bmsc-bandops %s %d", opt_bmsc_bandops, len); + } + strcpy(buf, opt_bmsc_bandops); + colon = strchr(buf, ':'); + if (colon) + *(colon++) = '\0'; + + if (*buf) { + if(strlen(buf) > 8 || strlen(buf)%2 != 0 || strlen(buf)/2 == 0) { + quit(1, "Invalid bitmain-options for core command, must be hex now: %s", buf); + } + memset(core_buf, 0, 4); + if(!hex2bin(core_buf, buf, strlen(buf)/2)) { + quit(1, "Invalid bitmain-options for core command, hex2bin error now: %s", buf); + } + } + + if (colon && *colon) { + colon2 = strchr(colon, ':'); + if (colon2) + *(colon2++) = '\0'; + + if (*colon) { + *corenum = atoi(colon); + if(*corenum <= 0 || *corenum >= 256) { + quit(1, "Invalid bitmain-bandops for asic core num, must %d be > 0 and < 256", *corenum); + } + } + + if(colon2 && *colon2) { + colon3 = strchr(colon2, ':'); + if (colon3) + *(colon3++) = '\0'; + + if(*colon2) { + strcpy(coreenable, colon2); + if(strlen(coreenable) != *corenum) { + quit(1, "Invalid bitmain-bandops for asic core enable, must be equal core num %d", *corenum); + } + } + + if (colon3 && *colon3) { + *coresleep = atoi(colon3); + } + } + } + } +} + +static struct cgpu_info *bmsc_detect_one(struct libusb_device *dev, struct usb_find_devices *found) +{ + int this_option_offset = ++option_offset; + struct BMSC_INFO *info; + struct timeval tv_start, tv_finish; + + // Block 171874 nonce = (0xa2870100) = 0x000187a2 + // N.B. golden_ob MUST take less time to calculate + // than the timeout set in bmsc_open() + // This one takes ~0.53ms on Rev3 Bmsc + const char golden_ob[] = + "4679ba4ec99876bf4bfe086082b40025" + "4df6c356451471139a3afa71e48f544a" + "00000000000000004000000000000000" + "0000001f87320b1a1426674f2fa722ce"; + const char golden_ob1[] = + "e1eb393a50f6ae97e306ea87c1c47eae" + "1f9ad02d729d9f86bd48a213a4600144" + "00000000000000004000000000000000" + "0000001ffb0b0719aaf19752dd5e83a4"; + const char golden_ob2[] = + "b65911ea2c4b0c52958cb408caebff32" + "8dece4e6a002fe2693ba9906ffde7e8a" + "00000000000000004000000000000000" + "0000001f20dc1c190642455201756658"; + const char golden_ob3[] = + "c99da189374bcc69a1134d6f4953addc" + "7420499b132b7f8f999b0c71fe7efbf2" + "00000000000000004000000000000000" + "0000001f20dc1c198e4145526d74dee3"; + const char golden_ob4[] = + "696af96144b6079c1b437fbc6e539e4d" + "996d25b027ea9eefdfaf4eff6add6986" + "00000000000000004000000000000000" + "0000001f20dc1c19f84e4552ac86dc14"; + + char bandops_ob[] = + "00000000000000000000000000000000" + "00000000000000000000000000000000" + "00000000000000000000000000000000" + "00000000000000000000000000000000"; + + const char golden_nonce[] = "000187a2"; + const char golden_nonce1[] = "0345182b"; + const char golden_nonce2[] = "466b30a5"; + const char golden_nonce3[] = "857e65ee"; + const char golden_nonce4[] = "c6f70284"; + const uint32_t golden_nonce_val = 0x000187a2; + unsigned char nonce_bin[BMSC_READ_SIZE]; + struct BMSC_WORK workdata; + char *nonce_hex; + int baud = 115200, work_division = 1, fpga_count = 1; + float readtimeout = 1.0; + struct cgpu_info *bmsc; + int ret, err, amount, tries, i; + bool ok; + bool cmr2_ok[CAIRNSMORE2_INTS]; + int cmr2_count; + + unsigned char cmd_buf[4] = {0}; + unsigned char rdreg_buf[4] = {0}; + unsigned char reg_data[4] = {0}; + unsigned char voltage_data[2] = {0}; + + unsigned char rebuf[BMSC_READ_BUF_LEN] = {0}; + int relen = 0; + int realllen = 0; + int nodata = 0; + char msg[10240] = {0}; + int sendfreqstatus = 1; + int k = 0; + + unsigned char core_cmd[4] = {0}; + int corenum = 0; + char coreenable[256] = {0}; + int coresleep = 0; + + if (opt_bmsc_options == NULL) + return NULL; + + if ((sizeof(workdata) << 1) != (sizeof(golden_ob) - 1)) + quithere(1, "Data and golden_ob sizes don't match"); + if ((sizeof(workdata) << 1) != (sizeof(bandops_ob) - 1)) + quithere(1, "Data and bandops_ob sizes don't match"); + + bmsc = usb_alloc_cgpu(&bmsc_drv, 1); + + if (!usb_init(bmsc, dev, found)) + goto shin; + + get_options(this_option_offset, bmsc, &baud, &readtimeout); + get_bandops(core_cmd, &corenum, coreenable, &coresleep); + + info = (struct BMSC_INFO *)calloc(1, sizeof(struct BMSC_INFO)); + if (unlikely(!info)) + quit(1, "Failed to malloc BMSC_INFO"); + bmsc->device_data = (void *)info; + + info->ident = usb_ident(bmsc); + info->start = true; + switch (info->ident) { + case IDENT_ICA: + case IDENT_BLT: + case IDENT_LLT: + case IDENT_AMU: + case IDENT_CMR1: + info->timeout = BMSC_WAIT_TIMEOUT; + break; + case IDENT_CMR2: + if (found->intinfo_count != CAIRNSMORE2_INTS) { + quithere(1, "CMR2 Interface count (%d) isn't expected: %d", + found->intinfo_count, + CAIRNSMORE2_INTS); + } + info->timeout = BMSC_CMR2_TIMEOUT; + cmr2_count = 0; + for (i = 0; i < CAIRNSMORE2_INTS; i++) + cmr2_ok[i] = false; + break; + default: + quit(1, "%s bmsc_detect_one() invalid %s ident=%d", + bmsc->drv->dname, bmsc->drv->dname, info->ident); + } +// For CMR2 test each USB Interface +cmr2_retry: + tries = 2; + ok = false; + while (!ok && tries-- > 0) { + bmsc_initialise(bmsc, baud); + + if(opt_bmsc_bootstart) { + applog(LOG_ERR, "---------------------start bootstart----------------------"); + cmd_buf[0] = 0xbb; + cmd_buf[1] = 0x00; + cmd_buf[2] = 0x00; + cmd_buf[3] = 0x00; //0-7 + cmd_buf[3] = CRC5(cmd_buf, 27); + cmd_buf[3] |= 0x80; + + cgsleep_ms(500); + applog(LOG_ERR, "Send bootstart off %02x%02x%02x%02x", cmd_buf[0], cmd_buf[1], cmd_buf[2], cmd_buf[3]); + err = usb_write(bmsc, (char * )cmd_buf, 4, &amount, C_SENDTESTWORK); + if (err != LIBUSB_SUCCESS || amount != 4) { + applog(LOG_ERR, "Write bootstart Comms error (werr=%d amount=%d)", err, amount); + continue; + } + + cmd_buf[0] = 0xbb; + cmd_buf[1] = 0x08; + cmd_buf[2] = 0x00; + cmd_buf[3] = 0x00; //0-7 + cmd_buf[3] = CRC5(cmd_buf, 27); + cmd_buf[3] |= 0x80; + + cgsleep_ms(500); + applog(LOG_ERR, "Send bootstart on %02x%02x%02x%02x", cmd_buf[0], cmd_buf[1], cmd_buf[2], cmd_buf[3]); + err = usb_write(bmsc, (char * )cmd_buf, 4, &amount, C_SENDTESTWORK); + if (err != LIBUSB_SUCCESS || amount != 4) { + applog(LOG_ERR, "Write bootstart Comms error (werr=%d amount=%d)", err, amount); + continue; + } + applog(LOG_ERR, "Send bootstart ok"); + } + + if(opt_bmsc_voltage) { + if(strlen(opt_bmsc_voltage) > 4 || strlen(opt_bmsc_voltage)%2 != 0 || strlen(opt_bmsc_voltage)/2 == 0) { + quit(1, "Invalid options for voltage data, must be hex now: %s", opt_bmsc_voltage); + } + memset(voltage_data, 0, 2); + if(!hex2bin(voltage_data, opt_bmsc_voltage, strlen(opt_bmsc_voltage)/2)) { + quit(1, "Invalid options for voltage data, hex2bin error now: %s", opt_bmsc_voltage); + } + cmd_buf[0] = 0xaa; + cmd_buf[1] = voltage_data[0]; + cmd_buf[1] &=0x0f; + cmd_buf[1] |=0xb0; + cmd_buf[2] = voltage_data[1]; + cmd_buf[3] = 0x00; //0-7 + cmd_buf[3] = CRC5(cmd_buf, 4*8 - 5); + cmd_buf[3] |= 0xc0; + + applog(LOG_ERR, "---------------------start voltage----------------------"); + cgsleep_ms(500); + applog(LOG_ERR, "Send voltage %02x%02x%02x%02x", cmd_buf[0], cmd_buf[1], cmd_buf[2], cmd_buf[3]); + err = usb_write(bmsc, (char * )cmd_buf, 4, &amount, C_SENDTESTWORK); + if (err != LIBUSB_SUCCESS || amount != 4) { + applog(LOG_ERR, "Write voltage Comms error (werr=%d amount=%d)", err, amount); + continue; + } + applog(LOG_ERR, "Send voltage ok"); + } + + if (opt_bmsc_gray) { + cmd_buf[0] = 3; + cmd_buf[0] |= 0x80; + cmd_buf[1] = 0; //16-23 + cmd_buf[2] = 0x80; //8-15 + cmd_buf[3] = 0x80; //0-7 + cmd_buf[3] = CRC5(cmd_buf, 27); + cmd_buf[3] |= 0x80; + + applog(LOG_ERR, "-----------------start gray-------------------"); + cgsleep_ms(500); + applog(LOG_ERR, "Send gray %02x%02x%02x%02x", cmd_buf[0], cmd_buf[1], cmd_buf[2], cmd_buf[3]); + err = usb_write_ii(bmsc, info->intinfo, (char * )cmd_buf, 4, &amount, C_SENDWORK); + if (err != LIBUSB_SUCCESS || amount != 4) { + applog(LOG_ERR, "%s%i: Write freq Comms error (werr=%d amount=%d)", bmsc->drv->name, bmsc->device_id, err, amount); + continue; + } + applog(LOG_DEBUG, "Send gray ok"); + } + + if (opt_bmsc_freq) { + if (strcmp(opt_bmsc_freq, "0") != 0) { + applog(LOG_DEBUG, "Device detect freq parameter=%s", opt_bmsc_freq); + if (strlen(opt_bmsc_freq) > 8 || strlen(opt_bmsc_freq) % 2 != 0 || strlen(opt_bmsc_freq) / 2 == 0) { + quit(1, "Invalid bmsc_freq for freq data, must be hex now: %s", opt_bmsc_freq); + } + memset(reg_data, 0, 4); + if (!hex2bin(reg_data, opt_bmsc_freq, strlen(opt_bmsc_freq) / 2)) { + quit(1, "Invalid bmsc_freq for freq data, hex2bin error now: %s", opt_bmsc_freq); + } + cmd_buf[0] = 2; + cmd_buf[0] |= 0x80; + cmd_buf[1] = reg_data[0]; //16-23 + cmd_buf[2] = reg_data[1]; //8-15 + cmd_buf[3] = 0; + cmd_buf[3] = CRC5(cmd_buf, 27); + applog(LOG_DEBUG, "Set_frequency cmd_buf[1]{%02x}cmd_buf[2]{%02x}", cmd_buf[1], cmd_buf[2]); + + rdreg_buf[0] = 4; + rdreg_buf[0] |= 0x80; + rdreg_buf[1] = 0; //16-23 + rdreg_buf[2] = 0x04; //8-15 + rdreg_buf[3] = 0; + rdreg_buf[3] = CRC5(rdreg_buf, 27); + + applog(LOG_ERR, "-----------------start freq-------------------"); + cgsleep_ms(500); + + applog(LOG_ERR, "Send frequency %02x%02x%02x%02x", cmd_buf[0], cmd_buf[1], cmd_buf[2], cmd_buf[3]); + err = usb_write_ii(bmsc, info->intinfo, (char * )cmd_buf, 4, &amount, C_SENDWORK); + if (err != LIBUSB_SUCCESS || amount != 4) { + applog(LOG_ERR, "%s%i: Write freq Comms error (werr=%d amount=%d)", bmsc->drv->name, bmsc->device_id, err, amount); + continue; + } + applog(LOG_DEBUG, "Send frequency ok"); + + cgsleep_ms(500); + + applog(LOG_ERR, "Send freq getstatus %02x%02x%02x%02x", rdreg_buf[0], rdreg_buf[1], rdreg_buf[2], rdreg_buf[3]); + + for(i = 0; i < 10; i++) { + usb_read_ii_timeout_cancellable(bmsc, info->intinfo, (char * )rebuf, BMSC_READ_SIZE, &relen, 100, C_GETRESULTS); + } + + err = usb_write_ii(bmsc, info->intinfo, (char * )rdreg_buf, 4, &amount, C_SENDWORK); + if (err != LIBUSB_SUCCESS || amount != 4) { + applog(LOG_ERR, "%s%i: Write freq getstatus Comms error (werr=%d amount=%d)", bmsc->drv->name, bmsc->device_id, err, amount); + continue; + } + applog(LOG_DEBUG, "Send freq getstatus ok"); + + nodata = 0; + realllen = 0; + while (1) { + relen = 0; + err = usb_read_ii_timeout_cancellable(bmsc, info->intinfo, (char * )rebuf + realllen, BMSC_READ_SIZE, &relen, 200, C_GETRESULTS); + if (err < 0 && err != LIBUSB_ERROR_TIMEOUT) { + applog(LOG_ERR, "%s%i: Read freq Comms error (rerr=%d relen=%d)", bmsc->drv->name, bmsc->device_id, err, relen); + break; + } else if (err == LIBUSB_ERROR_TIMEOUT) { + applog(LOG_DEBUG, "%s%i: Read freq Comms timeout (rerr=%d relen=%d)", bmsc->drv->name, bmsc->device_id, err, relen); + + nodata++; + if (nodata > 5) { + if (realllen <= 0) { + if (sendfreqstatus) { + sendfreqstatus = 0; + applog(LOG_ERR, "Send freq getstatus %02x%02x%02x%02x", rdreg_buf[0], rdreg_buf[1], rdreg_buf[2], rdreg_buf[3]); + usb_read_ii_timeout_cancellable(bmsc, info->intinfo, (char * )rebuf, BMSC_READ_SIZE, &relen, 200, C_GETRESULTS); + err = usb_write_ii(bmsc, info->intinfo, (char * )rdreg_buf, 4, &amount, C_SENDWORK); + if (err != LIBUSB_SUCCESS || amount != 4) { + applog(LOG_ERR, "%s%i: Write freq getstatus Comms error (werr=%d amount=%d)", bmsc->drv->name, bmsc->device_id, err, amount); + continue; + } + applog(LOG_DEBUG, "Send freq getstatus ok"); + } else { + applog(LOG_ERR, "------recv freq getstatus no data finish------"); + break; + } + } else { + applog(LOG_DEBUG, "Recv freq getstatus len=%d", realllen); + for (i = 0; i < realllen; i += 5) { + applog(LOG_ERR, "Recv %d freq getstatus=%02x%02x%02x%02x%02x", i / 5 + 1, rebuf[i], rebuf[i + 1], rebuf[i + 2], rebuf[i + 3], rebuf[i + 4]); + } + applog(LOG_ERR, "--------recv freq getstatus ok finish---------"); + break; + } + } + continue; + } else { + nodata = 0; + realllen += relen; + for (i = 0; i < relen; i++) { + sprintf(msg + i * 2, "%02x", rebuf[i]); + } + applog(LOG_DEBUG, "Read data(%d):%s", relen, msg); + } + } + } else { + applog(LOG_ERR, "Device detect freq 0 parameter"); + } + } + + if (opt_bmsc_rdreg) { + applog(LOG_DEBUG, "Device detect rdreg parameter=%s", opt_bmsc_rdreg); + if (strlen(opt_bmsc_rdreg) > 8 || strlen(opt_bmsc_rdreg) % 2 != 0 || strlen(opt_bmsc_rdreg) / 2 == 0) { + quit(1, "Invalid bmsc_rdreg for reg data, must be hex now: %s", opt_bmsc_rdreg); + } + memset(reg_data, 0, 4); + if (!hex2bin(reg_data, opt_bmsc_rdreg, strlen(opt_bmsc_rdreg) / 2)) { + quit(1, "Invalid bmsc_rdreg for reg data, hex2bin error now: %s", opt_bmsc_rdreg); + } + rdreg_buf[0] = 4; + rdreg_buf[0] |= 0x80; + rdreg_buf[1] = 0; //16-23 + rdreg_buf[2] = reg_data[0]; //8-15 + rdreg_buf[3] = 0; + rdreg_buf[3] = CRC5(rdreg_buf, 27); + applog(LOG_DEBUG, "Get_status rdreg_buf[1]{%02x}rdreg_buf[2]{%02x}", rdreg_buf[1], rdreg_buf[2]); + + applog(LOG_ERR, "-----------------start rdreg------------------"); + applog(LOG_ERR, "Send getstatus %02x%02x%02x%02x", rdreg_buf[0], rdreg_buf[1], rdreg_buf[2], rdreg_buf[3]); + + for(i = 0; i < 10; i++) { + usb_read_ii_timeout_cancellable(bmsc, info->intinfo, (char * )rebuf, BMSC_READ_SIZE, &relen, 100, C_GETRESULTS); + } + + err = usb_write_ii(bmsc, info->intinfo, (char * )rdreg_buf, 4, &amount, C_SENDWORK); + if (err != LIBUSB_SUCCESS || amount != 4) { + applog(LOG_ERR, "%s%i: Write rdreg Comms error (werr=%d amount=%d)", bmsc->drv->name, bmsc->device_id, err, amount); + continue; + } + applog(LOG_DEBUG, "Send getstatus ok"); + + nodata = 0; + realllen = 0; + while (1) { + relen = 0; + err = usb_read_ii_timeout_cancellable(bmsc, info->intinfo, (char * )rebuf + realllen, BMSC_READ_SIZE, &relen, 200, C_GETRESULTS); + if (err < 0 && err != LIBUSB_ERROR_TIMEOUT) { + applog(LOG_ERR, "%s%i: Read rdreg Comms error (rerr=%d relen=%d)", bmsc->drv->name, bmsc->device_id, err, relen); + break; + } else if (err == LIBUSB_ERROR_TIMEOUT) { + applog(LOG_DEBUG, "%s%i: Read rdreg Comms timeout (rerr=%d relen=%d)", bmsc->drv->name, bmsc->device_id, err, relen); + + nodata++; + if (nodata > 5) { + applog(LOG_DEBUG, "Recv rdreg getstatus len=%d", realllen); + for (i = 0; i < realllen; i += 5) { + applog(LOG_ERR, "Recv %d rdreg getstatus=%02x%02x%02x%02x%02x", i / 5 + 1, rebuf[i], rebuf[i + 1], rebuf[i + 2], rebuf[i + 3], rebuf[i + 4]); + } + applog(LOG_ERR, "---------recv rdreg getstatus finish----------"); + break; + } + continue; + } else { + nodata = 0; + realllen += relen; + for (i = 0; i < relen; i++) { + sprintf(msg + i * 2, "%02x", rebuf[i]); + } + applog(LOG_DEBUG, "Read data(%d):%s", relen, msg); + } + } + } + + if (opt_bmsc_bandops) { + unsigned char tmpbyte = 0; + cmd_buf[0] = core_cmd[0]; + cmd_buf[1] = core_cmd[1]; + cmd_buf[2] = core_cmd[2]; + tmpbyte = core_cmd[3] & 0xE0; + cmd_buf[3] = tmpbyte; + cmd_buf[3] = CRC5(cmd_buf, 27); + cmd_buf[3] |= tmpbyte; + + applog(LOG_ERR, "-----------------start bandops-------------------"); + applog(LOG_ERR, "SetBandOPS cmd:%02x%02x%02x%02x corenum:%d enable:%s sleep:%d", core_cmd[0], core_cmd[1], core_cmd[2], core_cmd[3], corenum, coreenable, coresleep); + cgsleep_ms(500); + applog(LOG_ERR, "Send bandops %02x%02x%02x%02x", cmd_buf[0], cmd_buf[1], cmd_buf[2], cmd_buf[3]); + err = usb_write_ii(bmsc, info->intinfo, (char * )cmd_buf, 4, &amount, C_SENDWORK); + if (err != LIBUSB_SUCCESS || amount != 4) { + applog(LOG_ERR, "%s%i: Write BandOPS Comms error (werr=%d amount=%d)", bmsc->drv->name, bmsc->device_id, err, amount); + continue; + } + applog(LOG_DEBUG, "Send bandops command ok"); + for(i = 0; i < corenum; i++) { + if(coreenable[i] == '1') { + bandops_ob[127] = '1'; + } else { + bandops_ob[127] = '0'; + } + amount = 0; + hex2bin((void *)(&workdata), bandops_ob, sizeof(workdata)); + applog(LOG_ERR, "Send %d %s", i, bandops_ob); + err = usb_write_ii(bmsc, info->intinfo, (char *)(&workdata), sizeof(workdata), &amount, C_SENDWORK); + if (err != LIBUSB_SUCCESS || amount != sizeof(workdata)) { + applog(LOG_ERR, "%d %s%i: Write BandOPS Enable Comms error (werr=%d amount=%d)", i, bmsc->drv->name, bmsc->device_id, err, amount); + break; + } + if(coresleep > 0) { + cgsleep_ms(coresleep); + } + } + if(i >= corenum) { + applog(LOG_DEBUG, "Send bandops core enable ok"); + } else { + continue; + } + } + cgsleep_ms(1000); + + applog(LOG_ERR, "-----------------start nonce------------------"); +#if 0 + applog(LOG_ERR, "Bmsc send golden nonce"); + + hex2bin((void *)(&workdata), golden_ob, sizeof(workdata)); + err = usb_write_ii(bmsc, info->intinfo, (char *)(&workdata), sizeof(workdata), &amount, C_SENDWORK); + if (err != LIBUSB_SUCCESS || amount != sizeof(workdata)) + continue; + + memset(nonce_bin, 0, sizeof(nonce_bin)); + ret = bmsc_get_nonce(bmsc, nonce_bin, &tv_start, &tv_finish, NULL, 500); + if (ret != BTM_NONCE_OK) { + applog(LOG_ERR, "Bmsc recv golden nonce timeout"); + continue; + } + + nonce_hex = bin2hex(nonce_bin, sizeof(nonce_bin)); + if (strncmp(nonce_hex, golden_nonce, 8) == 0) + ok = true; + else { + applog(LOG_ERR, "Bmsc recv golden nonce %s != %s and retry", nonce_hex, golden_nonce); + if (tries < 0 && info->ident != IDENT_CMR2) { + applog(LOG_ERR, "Bmsc Detect: Test failed at %s: get %s, should: %s", + bmsc->device_path, nonce_hex, golden_nonce); + } + } + + applog(LOG_ERR, "Bmsc recv golden nonce %s -- %s ", nonce_hex, golden_nonce); +#else + applog(LOG_ERR, "Bmsc send golden nonce1"); + hex2bin((void *)(&workdata), golden_ob1, sizeof(workdata)); + err = usb_write_ii(bmsc, info->intinfo, (char *)(&workdata), sizeof(workdata), &amount, C_SENDWORK); + if (err != LIBUSB_SUCCESS || amount != sizeof(workdata)) + continue; + + memset(nonce_bin, 0, sizeof(nonce_bin)); + ret = bmsc_get_nonce(bmsc, nonce_bin, &tv_start, &tv_finish, NULL, 500); + if (ret != BTM_NONCE_OK) { + applog(LOG_ERR, "Bmsc recv golden nonce timeout"); + continue; + } + + nonce_hex = bin2hex(nonce_bin, sizeof(nonce_bin)); + if (strncmp(nonce_hex, golden_nonce1, 8) == 0) + ok = true; + else { + applog(LOG_ERR, "Bmsc recv golden nonce %s != %s and retry", nonce_hex, golden_nonce1); + applog(LOG_ERR,"The first chip may not work,reconnrect the device will get better stats"); + cgsleep_ms(1000); + if (tries < 0 && info->ident != IDENT_CMR2) { + applog(LOG_ERR, "Bmsc Detect: Test failed at %s: get %s, should: %s", + bmsc->device_path, nonce_hex, golden_nonce1); + } + } + applog(LOG_ERR, "Bmsc recv golden nonce1 %s -- %s ", nonce_hex, golden_nonce1); + + applog(LOG_ERR, "Bmsc send golden nonce2"); + + hex2bin((void *)(&workdata), golden_ob2, sizeof(workdata)); + err = usb_write_ii(bmsc, info->intinfo, (char *)(&workdata), sizeof(workdata), &amount, C_SENDWORK); + if (err != LIBUSB_SUCCESS || amount != sizeof(workdata)) + continue; + + memset(nonce_bin, 0, sizeof(nonce_bin)); + ret = bmsc_get_nonce(bmsc, nonce_bin, &tv_start, &tv_finish, NULL, 500); + if (ret != BTM_NONCE_OK) { + applog(LOG_ERR, "Bmsc recv golden nonce timeout"); + continue; + } + + nonce_hex = bin2hex(nonce_bin, sizeof(nonce_bin)); + if (strncmp(nonce_hex, golden_nonce2, 8) == 0) + ok = true; + else { + applog(LOG_ERR, "Bmsc recv golden nonce %s != %s and retry", nonce_hex, golden_nonce2); + applog(LOG_ERR,"The second chip may not work,reconnrect the device will get better stats"); + cgsleep_ms(1000); + if (tries < 0 && info->ident != IDENT_CMR2) { + applog(LOG_ERR, "Bmsc Detect: Test failed at %s: get %s, should: %s", + bmsc->device_path, nonce_hex, golden_nonce2); + } + } + applog(LOG_ERR, "Bmsc recv golden nonce2 %s -- %s ", nonce_hex, golden_nonce2); + applog(LOG_ERR, "Bmsc send golden nonce3"); + hex2bin((void *)(&workdata), golden_ob3, sizeof(workdata)); + err = usb_write_ii(bmsc, info->intinfo, (char *)(&workdata), sizeof(workdata), &amount, C_SENDWORK); + if (err != LIBUSB_SUCCESS || amount != sizeof(workdata)) + continue; + + memset(nonce_bin, 0, sizeof(nonce_bin)); + ret = bmsc_get_nonce(bmsc, nonce_bin, &tv_start, &tv_finish, NULL, 500); + if (ret != BTM_NONCE_OK) { + applog(LOG_ERR, "Bmsc recv golden nonce timeout"); + continue; + } + + nonce_hex = bin2hex(nonce_bin, sizeof(nonce_bin)); + if (strncmp(nonce_hex, golden_nonce3, 8) == 0) + ok = true; + else { + applog(LOG_ERR, "Bmsc recv golden nonce %s != %s and retry", nonce_hex, golden_nonce3); + applog(LOG_ERR,"The third chip may not work,reconnrect the device will get better stats"); + cgsleep_ms(1000); + if (tries < 0 && info->ident != IDENT_CMR2) { + applog(LOG_ERR, "Bmsc Detect: Test failed at %s: get %s, should: %s", + bmsc->device_path, nonce_hex, golden_nonce3); + } + } + + applog(LOG_ERR, "Bmsc recv golden nonce %s -- %s ", nonce_hex, golden_nonce3); + applog(LOG_ERR, "Bmsc send golden nonce4"); + + hex2bin((void *)(&workdata), golden_ob4, sizeof(workdata)); + err = usb_write_ii(bmsc, info->intinfo, (char *)(&workdata), sizeof(workdata), &amount, C_SENDWORK); + if (err != LIBUSB_SUCCESS || amount != sizeof(workdata)) + continue; + + memset(nonce_bin, 0, sizeof(nonce_bin)); + ret = bmsc_get_nonce(bmsc, nonce_bin, &tv_start, &tv_finish, NULL, 500); + if (ret != BTM_NONCE_OK) { + applog(LOG_ERR, "Bmsc recv golden nonce4 timeout"); + continue; + } + + nonce_hex = bin2hex(nonce_bin, sizeof(nonce_bin)); + if (strncmp(nonce_hex, golden_nonce4, 8) == 0) + ok = true; + + else { + applog(LOG_ERR, "Bmsc recv golden nonce %s != %s and retry", nonce_hex, golden_nonce4); + applog(LOG_ERR,"The fourth chip may not work,reconnrect the device will get better stats"); + cgsleep_ms(1000); + if (tries < 0 && info->ident != IDENT_CMR2) { + applog(LOG_ERR, "Bmsc Detect: Test failed at %s: get %s, should: %s", + bmsc->device_path, nonce_hex, golden_nonce4); + } + } + + applog(LOG_ERR, "Bmsc recv golden nonce %s -- %s ", nonce_hex, golden_nonce4); +#endif + free(nonce_hex); + } + + if (!ok) { + if (info->ident != IDENT_CMR2) + goto unshin; + + if (info->intinfo < CAIRNSMORE2_INTS-1) { + info->intinfo++; + goto cmr2_retry; + } + } else { + if (info->ident == IDENT_CMR2) { + applog(LOG_DEBUG, + "Bmsc Detect: " + "Test succeeded at %s i%d: got %s", + bmsc->device_path, info->intinfo, golden_nonce); + + cmr2_ok[info->intinfo] = true; + cmr2_count++; + if (info->intinfo < CAIRNSMORE2_INTS-1) { + info->intinfo++; + goto cmr2_retry; + } + } + } + + if (info->ident == IDENT_CMR2) { + if (cmr2_count == 0) { + applog(LOG_ERR, + "Bmsc Detect: Test failed at %s: for all %d CMR2 Interfaces", + bmsc->device_path, CAIRNSMORE2_INTS); + goto unshin; + } + + // set the interface to the first one that succeeded + for (i = 0; i < CAIRNSMORE2_INTS; i++) + if (cmr2_ok[i]) { + info->intinfo = i; + break; + } + } else { + applog(LOG_DEBUG, + "Bmsc Detect: " + "Test succeeded at %s: got %s", + bmsc->device_path, golden_nonce); + } + + /* We have a real Bmsc! */ + if (!add_cgpu(bmsc)) + goto unshin; + + update_usb_stats(bmsc); + + applog(LOG_INFO, "%s%d: Found at %s", + bmsc->drv->name, bmsc->device_id, bmsc->device_path); + + if (info->ident == IDENT_CMR2) { + applog(LOG_INFO, "%s%d: with %d Interface%s", + bmsc->drv->name, bmsc->device_id, + cmr2_count, cmr2_count > 1 ? "s" : ""); + + // Assume 1 or 2 are running FPGA pairs + if (cmr2_count < 3) { + work_division = fpga_count = 2; + info->Hs /= 2; + } + } + + applog(LOG_DEBUG, "%s%d: Init baud=%d work_division=%d fpga_count=%d readtimeout=%f", + bmsc->drv->name, bmsc->device_id, baud, work_division, fpga_count, readtimeout); + + info->baud = baud; + info->work_division = work_division; + info->fpga_count = fpga_count; + info->nonce_mask = mask(work_division); + info->work_queue_index = 0; + for(k = 0; k < BMSC_WORK_QUEUE_NUM; k++) { + info->work_queue[k] = NULL; + } + + info->golden_hashes = (golden_nonce_val & info->nonce_mask) * fpga_count; + timersub(&tv_finish, &tv_start, &(info->golden_tv)); + + set_timing_mode(this_option_offset, bmsc, readtimeout); + + if (info->ident == IDENT_CMR2) { + int i; + for (i = info->intinfo + 1; i < bmsc->usbdev->found->intinfo_count; i++) { + struct cgpu_info *cgtmp; + struct BMSC_INFO *intmp; + + if (!cmr2_ok[i]) + continue; + + cgtmp = usb_copy_cgpu(bmsc); + if (!cgtmp) { + applog(LOG_ERR, "%s%d: Init failed initinfo %d", + bmsc->drv->name, bmsc->device_id, i); + continue; + } + + cgtmp->usbinfo.usbstat = USB_NOSTAT; + + intmp = (struct BMSC_INFO *)malloc(sizeof(struct BMSC_INFO)); + if (unlikely(!intmp)) + quit(1, "Failed2 to malloc BMSC_INFO"); + + cgtmp->device_data = (void *)intmp; + + // Initialise everything to match + memcpy(intmp, info, sizeof(struct BMSC_INFO)); + + intmp->intinfo = i; + + bmsc_initialise(cgtmp, baud); + + if (!add_cgpu(cgtmp)) { + usb_uninit(cgtmp); + free(intmp); + continue; + } + + update_usb_stats(cgtmp); + } + } + + return bmsc; + +unshin: + + usb_uninit(bmsc); + free(info); + bmsc->device_data = NULL; + +shin: + + bmsc = usb_free_cgpu(bmsc); + + return NULL; +} + +static void bmsc_detect(bool __maybe_unused hotplug) +{ + usb_detect(&bmsc_drv, bmsc_detect_one); +} + +static bool bmsc_prepare(__maybe_unused struct thr_info *thr) +{ +// struct cgpu_info *bmsc = thr->cgpu; + return true; +} + +static void cmr2_command(struct cgpu_info *bmsc, uint8_t cmd, uint8_t data) +{ + struct BMSC_INFO *info = (struct BMSC_INFO *)(bmsc->device_data); + struct BMSC_WORK workdata; + int amount; + + memset((void *)(&workdata), 0, sizeof(workdata)); + + workdata.prefix = BMSC_CMR2_PREFIX; + workdata.cmd = cmd; + workdata.data = data; + workdata.check = workdata.data ^ workdata.cmd ^ workdata.prefix ^ BMSC_CMR2_CHECK; + + usb_write_ii(bmsc, info->intinfo, (char *)(&workdata), sizeof(workdata), &amount, C_SENDWORK); +} + +static void cmr2_commands(struct cgpu_info *bmsc) +{ + struct BMSC_INFO *info = (struct BMSC_INFO *)(bmsc->device_data); + + if (info->speed_next_work) { + info->speed_next_work = false; + cmr2_command(bmsc, BMSC_CMR2_CMD_SPEED, info->cmr2_speed); + return; + } + + if (info->flash_next_work) { + info->flash_next_work = false; + cmr2_command(bmsc, BMSC_CMR2_CMD_FLASH, BMSC_CMR2_DATA_FLASH_ON); + cgsleep_ms(250); + cmr2_command(bmsc, BMSC_CMR2_CMD_FLASH, BMSC_CMR2_DATA_FLASH_OFF); + cgsleep_ms(250); + cmr2_command(bmsc, BMSC_CMR2_CMD_FLASH, BMSC_CMR2_DATA_FLASH_ON); + cgsleep_ms(250); + cmr2_command(bmsc, BMSC_CMR2_CMD_FLASH, BMSC_CMR2_DATA_FLASH_OFF); + return; + } +} + +static int64_t bmsc_scanwork(struct thr_info *thr) +{ + struct cgpu_info *bmsc = thr->cgpu; + struct BMSC_INFO *info = (struct BMSC_INFO *)(bmsc->device_data); + int ret, err, amount; + unsigned char nonce_bin[BMSC_READ_SIZE]; + struct BMSC_WORK workdata; + char *ob_hex; + uint32_t nonce; + int64_t hash_count = 0; + struct timeval tv_start, tv_finish, elapsed; + struct timeval tv_history_start, tv_history_finish; + double Ti, Xi; + int curr_hw_errors, i; + bool was_hw_error; + struct work *work = NULL; + struct work *worktmp = NULL; + + struct BMSC_HISTORY *history0, *history; + int count; + double Hs, W, fullnonce; + int read_time; + bool limited; + int64_t estimate_hashes; + uint32_t values; + int64_t hash_count_range; + unsigned char workid = 0; + int submitfull = 0; + bool submitnonceok = true; + + // Device is gone + if (bmsc->usbinfo.nodev) + return -1; + + elapsed.tv_sec = elapsed.tv_usec = 0; + +retry: + work = get_work(thr, thr->id); + memset((void *)(&workdata), 0, sizeof(workdata)); + memcpy(&(workdata.midstate), work->midstate, BMSC_MIDSTATE_SIZE); + memcpy(&(workdata.work), work->data + BMSC_WORK_DATA_OFFSET, BMSC_WORK_SIZE); + rev((void *)(&(workdata.midstate)), BMSC_MIDSTATE_SIZE); + rev((void *)(&(workdata.work)), BMSC_WORK_SIZE); + + if(work->midstate[BMSC_MIDSTATE_SIZE-1] == 0xaa) + goto retry; + workdata.workid = work->id; + workid = work->id; + workid = workid & 0x1F; + + // We only want results for the work we are about to send + usb_buffer_clear(bmsc); + + if(info->work_queue[workid]) { + free(info->work_queue[workid]); + info->work_queue[workid] = NULL; + } + info->work_queue[workid] = copy_work(work); + + err = usb_write_ii(bmsc, info->intinfo, (char *)(&workdata), sizeof(workdata), &amount, C_SENDWORK); + if (err < 0 || amount != sizeof(workdata)) { + applog(LOG_ERR, "%s%i: Comms error (werr=%d amt=%d)", bmsc->drv->name, bmsc->device_id, err, amount); + dev_error(bmsc, REASON_DEV_COMMS_ERROR); + bmsc_initialise(bmsc, info->baud); + goto out; + } + + if (opt_debug) { + ob_hex = bin2hex((void *)(&workdata), sizeof(workdata)); + applog(LOG_DEBUG, "%s%d: sent %s", bmsc->drv->name, bmsc->device_id, ob_hex); + free(ob_hex); + } + + /* Bmsc will return 4 bytes (BMSC_READ_SIZE) nonces or nothing */ + memset(nonce_bin, 0, sizeof(nonce_bin)); + ret = bmsc_get_nonce(bmsc, nonce_bin, &tv_start, &tv_finish, thr, info->read_time); + if (ret == BTM_NONCE_ERROR) + goto out; + + // aborted before becoming idle, get new work + if (ret == BTM_NONCE_TIMEOUT || ret == BTM_NONCE_RESTART) { + timersub(&tv_finish, &tv_start, &elapsed); + + // ONLY up to just when it aborted + // We didn't read a reply so we don't subtract BMSC_READ_TIME + estimate_hashes = ((double)(elapsed.tv_sec) + ((double)(elapsed.tv_usec))/((double)1000000)) / info->Hs; + + // If some Serial-USB delay allowed the full nonce range to + // complete it can't have done more than a full nonce + if (unlikely(estimate_hashes > 0xffffffff)) + estimate_hashes = 0xffffffff; + + applog(LOG_DEBUG, "%s%d: no nonce = 0x%08lX hashes (%ld.%06lds)", bmsc->drv->name, bmsc->device_id, (long unsigned int)estimate_hashes, elapsed.tv_sec, elapsed.tv_usec); + + hash_count = 0; + goto out; + } + + memcpy((char *)&nonce, nonce_bin, sizeof(nonce_bin)); + nonce = htobe32(nonce); + curr_hw_errors = bmsc->hw_errors; + + workid = nonce_bin[4]; + workid = workid & 0x1F; + worktmp = info->work_queue[workid]; + if(info->start && workid == 0x1f){ + goto out; + }else{ + info->start = false; + } + if(worktmp) { + submitfull = 0; + if(submit_nonce_1(thr, worktmp, nonce, &submitfull)) { + submitnonceok = true; + submit_nonce_2(worktmp); + } else { + if(submitfull) { + submitnonceok = true; + } else { + submitnonceok = false; + } + } + cg_logwork(worktmp, nonce_bin, submitnonceok); + } else { + applog(LOG_ERR, "%s%d: work %02x not find error", bmsc->drv->name, bmsc->device_id, workid); + } + + was_hw_error = (curr_hw_errors > bmsc->hw_errors); + + hash_count = (nonce & info->nonce_mask); + hash_count++; + hash_count *= info->fpga_count; + + hash_count = 0xffffffff; + + if (opt_debug || info->do_bmsc_timing) + timersub(&tv_finish, &tv_start, &elapsed); + + applog(LOG_DEBUG, "%s%d: nonce = 0x%08x = 0x%08lX hashes (%ld.%06lds)", bmsc->drv->name, bmsc->device_id, nonce, (long unsigned int)hash_count, elapsed.tv_sec, elapsed.tv_usec); + +out: + free_work(work); + return hash_count; +} +/* +static int64_t bmsc_scanwork(struct thr_info *thr) +{ + struct cgpu_info *bmsc = thr->cgpu; + struct BMSC_INFO *info = (struct BMSC_INFO *)(bmsc->device_data); + int ret, err, amount; + unsigned char nonce_bin[BMSC_READ_SIZE]; + struct BMSC_WORK workdata; + char *ob_hex; + uint32_t nonce; + int64_t hash_count = 0; + int64_t hash_done = 0; + struct timeval tv_start, tv_finish, elapsed; + struct timeval tv_history_start, tv_history_finish; + double Ti, Xi; + int curr_hw_errors; + bool was_hw_error; + struct work *work; + + struct BMSC_HISTORY *history0, *history; + double Hs, W, fullnonce; + bool limited; + int64_t estimate_hashes; + uint32_t values; + int64_t hash_count_range; + + int i = 0, count = 0, nofullcount = 0, readalllen = 0, readlen = 0, read_time = 0, nofull = 0; + bool nonceok = false; + bool noncedup = false; + + char testbuf[256] = {0}; + char testtmp[256] = {0}; + int asicnum = 0; + int k = 0; + + // Device is gone + if (bmsc->usbinfo.nodev) + return -1; + + elapsed.tv_sec = elapsed.tv_usec = 0; + + work = get_work(thr, thr->id); + memset((void *)(&workdata), 0, sizeof(workdata)); + memcpy(&(workdata.midstate), work->midstate, BMSC_MIDSTATE_SIZE); + memcpy(&(workdata.work), work->data + BMSC_WORK_DATA_OFFSET, BMSC_WORK_SIZE); + rev((void *)(&(workdata.midstate)), BMSC_MIDSTATE_SIZE); + rev((void *)(&(workdata.work)), BMSC_WORK_SIZE); + + applog(LOG_DEBUG, "bmsc_scanhash start ------------"); + + readalllen = 0; + readlen = 0; + if(info->work_queue != NULL) { + while (true) { + if (bmsc->usbinfo.nodev) + return -1; + amount = 0; + memset(nonce_bin, 0, sizeof(nonce_bin)); + err = usb_read_once_timeout(bmsc, (char *)nonce_bin+readlen, 5-readlen, &amount, BMSC_WAIT_TIMEOUT, C_GETRESULTS); + if (err < 0 && err != LIBUSB_ERROR_TIMEOUT) { + applog(LOG_ERR, "%s%i: Comms error (rerr=%d amt=%d)", bmsc->drv->name, bmsc->device_id, err, amount); + dev_error(bmsc, REASON_DEV_COMMS_ERROR); + return 0; + } + if (amount > 0) { + readalllen += amount; + readlen += amount; + if (readlen >= 5) { + nonceok = false; + + memcpy((char *) &nonce, nonce_bin, BMSC_READ_SIZE); + noncedup = false; + for(i = 0; i < BMSC_NONCE_ARRAY_SIZE; i++) { + if(memcmp(nonce_bin, info->nonce_bin[i], BMSC_READ_SIZE) == 0) { + noncedup = true; + break; + } + } + if (!noncedup) { + if(info->nonce_index < 0 || info->nonce_index >= BMSC_NONCE_ARRAY_SIZE) + info->nonce_index = 0; + + memcpy(info->nonce_bin[info->nonce_index], nonce_bin, BMSC_READ_SIZE); + info->nonce_index++; + + nonce = htobe32(nonce); + + nofull = 0; + if (submit_nonce_1(thr, info->work_queue, nonce, &nofull)) { + applog(LOG_DEBUG, "Bmsc nonce(0x%08x) match old work", nonce); + submit_nonce_2(info->work_queue); + nonceok = true; + } else { + if(!nofull) { + applog(LOG_DEBUG, "Bmsc nonce(0x%08x) not match old work", nonce); + usb_buffer_clear(bmsc); + inc_hw_errors(thr); + break; + } else { + nofullcount++; + } + } + } else { + applog(LOG_DEBUG, "Bmsc nonce duplication"); + } + + if (nonceok) { + count++; + hash_count = (nonce & info->nonce_mask); + hash_count++; + hash_count *= info->fpga_count; + hash_done += 0xffffffff;//hash_count; + + applog(LOG_DEBUG, "%s%d: nonce = 0x%08x = 0x%08lX hashes (%ld.%06lds)", + bmsc->drv->name, bmsc->device_id, nonce, (long unsigned int )hash_count, elapsed.tv_sec, elapsed.tv_usec); + } + readlen = 0; + } + } else { + //usb_buffer_clear(bmsc); + applog(LOG_DEBUG, "bmsc_scanhash usb_read_once_timeout read time out"); + break; + } + } + } + + err = usb_write(bmsc, (char *)(&workdata), sizeof(workdata), &amount, C_SENDWORK); + if (err < 0 || amount != sizeof(workdata)) { + applog(LOG_ERR, "%s%i: Comms error (werr=%d amt=%d)", bmsc->drv->name, bmsc->device_id, err, amount); + dev_error(bmsc, REASON_DEV_COMMS_ERROR); + bmsc_initialise(bmsc, info->baud); + return 0; + } + + if (opt_debug) { + ob_hex = bin2hex((char *)&workdata, sizeof(workdata)); + applog(LOG_DEBUG, "%s%d: sent %s", bmsc->drv->name, bmsc->device_id, ob_hex); + free(ob_hex); + } + + cgtime(&tv_start); + readlen = 0; + while(true) { + if (bmsc->usbinfo.nodev) + return -1; + amount = 0; + memset(nonce_bin, 0, sizeof(nonce_bin)); + err = usb_read_once_timeout(bmsc, (char *)nonce_bin+readlen, 5-readlen, &amount, BMSC_WAIT_TIMEOUT, C_GETRESULTS); + cgtime(&tv_finish); + if (err < 0 && err != LIBUSB_ERROR_TIMEOUT) { + applog(LOG_ERR, "%s%i: Comms error (rerr=%d amt=%d)", bmsc->drv->name, bmsc->device_id, err, amount); + dev_error(bmsc, REASON_DEV_COMMS_ERROR); + return 0; + } + if(amount > 0) { + readalllen += amount; + readlen += amount; + if (readlen >= 5) { + nonceok = false; + + memcpy((char *) &nonce, nonce_bin, BMSC_READ_SIZE); + noncedup = false; + for(i = 0; i < BMSC_NONCE_ARRAY_SIZE; i++) { + if(memcmp(nonce_bin, info->nonce_bin[i], BMSC_READ_SIZE) == 0) { + noncedup = true; + break; + } + } + if(!noncedup) { + if(info->nonce_index < 0 || info->nonce_index >= BMSC_NONCE_ARRAY_SIZE) + info->nonce_index = 0; + + memcpy(info->nonce_bin[info->nonce_index], nonce_bin, BMSC_READ_SIZE); + info->nonce_index++; + + nonce = htobe32(nonce); + nofull = 0; + if (submit_nonce_1(thr, work, nonce, &nofull)) { + applog(LOG_DEBUG, "Bmsc nonce(0x%08x) match current work", nonce); + submit_nonce_2(work); + nonceok = true; + } else { + if(!nofull) { + if (info->work_queue != NULL) { + nofull = 0; + if (submit_nonce_1(thr, info->work_queue, nonce, &nofull)) { + applog(LOG_DEBUG, "Bmsc nonce(0x%08x) match old work", nonce); + submit_nonce_2(info->work_queue); + nonceok = true; + } else { + if(!nofull) { + applog(LOG_DEBUG, "Bmsc nonce(0x%08x) not match work", nonce); + usb_buffer_clear(bmsc); + inc_hw_errors(thr); + break; + } else { + nofullcount++; + } + } + } else { + applog(LOG_DEBUG, "Bmsc nonce(0x%08x) no old work", nonce); + } + } else { + nofullcount++; + } + } + } else { + applog(LOG_DEBUG, "Bmsc nonce duplication"); + } + + if(nonceok) { + count++; + hash_count = (nonce & info->nonce_mask); + hash_count++; + hash_count *= info->fpga_count; + hash_done += 0xffffffff;//hash_count; + + applog(LOG_DEBUG, "%s%d: nonce = 0x%08x = 0x%08lX hashes (%ld.%06lds)", + bmsc->drv->name, bmsc->device_id, nonce, (long unsigned int )hash_count, elapsed.tv_sec, elapsed.tv_usec); + } + readlen = 0; + } + } else { + applog(LOG_DEBUG, "bmsc_scanhash usb_read_once_timeout read time out"); + } + + read_time = SECTOMS(tdiff(&tv_finish, &tv_start)); + if(read_time >= info->read_time) { + if (readalllen > 0) + applog(LOG_DEBUG, "Bmsc Read: Nonce ok:%d below:%d in %d ms", count, nofullcount, read_time); + else + applog(LOG_DEBUG, "Bmsc Read: No nonce work %d for %d ms", work->id, read_time); + + break; + } + } + + if(info->work_queue != NULL) { + free_work(info->work_queue); + info->work_queue = NULL; + } + + info->work_queue = copy_work(work); + + applog(LOG_DEBUG, "bmsc_scanhash stop ------------"); +out: + free_work(work); + return hash_count; +}*/ + +static struct api_data *bmsc_api_stats(struct cgpu_info *cgpu) +{ + struct api_data *root = NULL; + struct BMSC_INFO *info = (struct BMSC_INFO *)(cgpu->device_data); + + // Warning, access to these is not locked - but we don't really + // care since hashing performance is way more important than + // locking access to displaying API debug 'stats' + // If locking becomes an issue for any of them, use copy_data=true also + root = api_add_int(root, "read_time", &(info->read_time), false); + root = api_add_int(root, "read_time_limit", &(info->read_time_limit), false); + root = api_add_double(root, "fullnonce", &(info->fullnonce), false); + root = api_add_int(root, "count", &(info->count), false); + root = api_add_hs(root, "Hs", &(info->Hs), false); + root = api_add_double(root, "W", &(info->W), false); + root = api_add_uint(root, "total_values", &(info->values), false); + root = api_add_uint64(root, "range", &(info->hash_count_range), false); + root = api_add_uint64(root, "history_count", &(info->history_count), false); + root = api_add_timeval(root, "history_time", &(info->history_time), false); + root = api_add_uint(root, "min_data_count", &(info->min_data_count), false); + root = api_add_uint(root, "timing_values", &(info->history[0].values), false); + root = api_add_const(root, "timing_mode", timing_mode_str(info->timing_mode), false); + root = api_add_bool(root, "is_timing", &(info->do_bmsc_timing), false); + root = api_add_int(root, "baud", &(info->baud), false); + root = api_add_int(root, "work_division", &(info->work_division), false); + root = api_add_int(root, "fpga_count", &(info->fpga_count), false); + + return root; +} + +static void bmsc_statline_before(char *buf, size_t bufsiz, struct cgpu_info *cgpu) +{ + struct BMSC_INFO *info = (struct BMSC_INFO *)(cgpu->device_data); + + if (info->ident == IDENT_CMR2 && info->cmr2_speed > 0) + tailsprintf(buf, bufsiz, "%5.1fMhz", (float)(info->cmr2_speed) * BMSC_CMR2_SPEED_FACTOR); + else + tailsprintf(buf, bufsiz, " "); + + tailsprintf(buf, bufsiz, " | "); +} + +static void bmsc_shutdown(__maybe_unused struct thr_info *thr) +{ + // TODO: ? +} + +static void bmsc_identify(struct cgpu_info *cgpu) +{ + struct BMSC_INFO *info = (struct BMSC_INFO *)(cgpu->device_data); + + if (info->ident == IDENT_CMR2) + info->flash_next_work = true; +} + +static char *bmsc_set(struct cgpu_info *cgpu, char *option, char *setting, char *replybuf) +{ + struct BMSC_INFO *info = (struct BMSC_INFO *)(cgpu->device_data); + int val; + + if (info->ident != IDENT_CMR2) { + strcpy(replybuf, "no set options available"); + return replybuf; + } + + if (strcasecmp(option, "help") == 0) { + sprintf(replybuf, "clock: range %d-%d", + BMSC_CMR2_SPEED_MIN_INT, BMSC_CMR2_SPEED_MAX_INT); + return replybuf; + } + + if (strcasecmp(option, "clock") == 0) { + if (!setting || !*setting) { + sprintf(replybuf, "missing clock setting"); + return replybuf; + } + + val = atoi(setting); + if (val < BMSC_CMR2_SPEED_MIN_INT || val > BMSC_CMR2_SPEED_MAX_INT) { + sprintf(replybuf, "invalid clock: '%s' valid range %d-%d", + setting, + BMSC_CMR2_SPEED_MIN_INT, + BMSC_CMR2_SPEED_MAX_INT); + } + + info->cmr2_speed = CMR2_INT_TO_SPEED(val); + info->speed_next_work = true; + + return NULL; + } + + sprintf(replybuf, "Unknown option: %s", option); + return replybuf; +} + +struct device_drv bmsc_drv = { + .drv_id = DRIVER_bmsc, + .dname = "Bitmain", + .name = "BTM", + .drv_detect = bmsc_detect, + .hash_work = &hash_driver_work, + .get_api_stats = bmsc_api_stats, + .get_statline_before = bmsc_statline_before, + .set_device = bmsc_set, + .identify_device = bmsc_identify, + .thread_prepare = bmsc_prepare, + .scanwork = bmsc_scanwork, + .thread_shutdown = bmsc_shutdown, +}; diff --git a/driver-cointerra.c b/driver-cointerra.c new file mode 100644 index 0000000000..e6b12edd2d --- /dev/null +++ b/driver-cointerra.c @@ -0,0 +1,1376 @@ +/* + * Copyright 2013-2014 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 "config.h" + +#include "miner.h" +#include "driver-cointerra.h" + +static const char *cointerra_hdr = "ZZ"; + +int opt_ps_load; + +static void cta_gen_message(char *msg, char type) +{ + memset(msg, 0, CTA_MSG_SIZE); + memcpy(msg, cointerra_hdr, 2); + msg[CTA_MSG_TYPE] = type; +} + +/* Find the number of leading zero bits in diff */ +static uint8_t diff_to_bits(double diff) +{ + uint64_t diff64; + uint8_t i; + + diff /= 0.9999847412109375; + diff *= (double)2147483648.0; + if (diff > 0x8000000000000000ULL) + diff = 0x8000000000000000ULL; + /* Convert it to an integer */ + diff64 = diff; + for (i = 0; diff64; i++, diff64 >>= 1); + + return i; +} + +static double bits_to_diff(uint8_t bits) +{ + double ret = 1.0; + + if (likely(bits > 32)) + ret *= 1ull << (bits - 32); + else if (unlikely(bits < 32)) + ret /= 1ull << (32 - bits); + return ret; +} + +static bool cta_reset_init(char *buf) +{ + return ((buf[CTA_MSG_TYPE] == CTA_RECV_RDONE) && ((buf[CTA_RESET_TYPE]&0x3) == CTA_RESET_INIT)); +} + +static char *mystrstr(char *haystack, int size, const char *needle) +{ + int loop = 0; + + while (loop < (size-1)) { + if ((haystack[loop] == needle[0])&& + (haystack[loop+1] == needle[1])) + return &haystack[loop]; + loop++; + } + return NULL; +} + +static bool cta_open(struct cgpu_info *cointerra) +{ + int err, amount, offset = 0; + char buf[CTA_MSG_SIZE]; + cgtimer_t ts_start; + bool ret = false; + + if (cointerra->usbinfo.nodev) + return false; + + applog(LOG_INFO, "CTA_OPEN"); + + cta_gen_message(buf, CTA_SEND_RESET); + // set the initial difficulty + buf[CTA_RESET_TYPE] = CTA_RESET_INIT | CTA_RESET_DIFF; + buf[CTA_RESET_DIFF] = diff_to_bits(CTA_INIT_DIFF); + buf[CTA_RESET_LOAD] = opt_cta_load ? opt_cta_load : 255; + buf[CTA_RESET_PSLOAD] = opt_ps_load; + + if (cointerra->usbinfo.nodev) + return ret; + + err = usb_write(cointerra, buf, CTA_MSG_SIZE, &amount, C_CTA_WRITE); + if (err) { + applog(LOG_INFO, "Write error %d, wrote %d of %d", err, amount, CTA_MSG_SIZE); + return ret; + } + + cgtimer_time(&ts_start); + + /* Read from the device for up to 2 seconds discarding any data that + * doesn't match a reset complete acknowledgement. */ + while (42) { + cgtimer_t ts_now, ts_diff; + char *msg; + + cgtimer_time(&ts_now); + cgtimer_sub(&ts_now, &ts_start, &ts_diff); + if (cgtimer_to_ms(&ts_diff) > 2000) { + applog(LOG_DEBUG, "%s %d: Timed out waiting for response to reset init", + cointerra->drv->name, cointerra->device_id); + break; + } + + if (cointerra->usbinfo.nodev) + break; + + err = usb_read(cointerra, buf + offset, CTA_MSG_SIZE - offset, &amount, C_CTA_READ); + if (err && err != LIBUSB_ERROR_TIMEOUT) { + applog(LOG_INFO, "%s %d: Read error %d, read %d", cointerra->drv->name, + cointerra->device_id, err, amount); + break; + } + if (!amount) + continue; + + msg = mystrstr(buf, amount, cointerra_hdr); + if (!msg) { + /* Keep the last byte in case it's the first byte of + * the 2 byte header. */ + offset = 1; + memmove(buf, buf + amount - 1, offset); + continue; + } + + if (msg > buf) { + /* length of message = offset for next usb_read after moving */ + offset = CTA_MSG_SIZE - (msg - buf); + memmove(buf, msg, offset); + continue; + } + + /* We have a full sized message starting with the header now */ + if (cta_reset_init(buf)) { + /* We can't store any other data returned with this + * reset since we have not allocated any memory for + * a cointerra_info structure yet. */ + applog(LOG_INFO, "%s %d: Successful reset init received", + cointerra->drv->name, cointerra->device_id); + ret = true; + break; + } + } + + return ret; +} + +static void cta_clear_work(struct cgpu_info *cgpu) +{ + struct work *work, *tmp; + + wr_lock(&cgpu->qlock); + HASH_ITER(hh, cgpu->queued_work, work, tmp) { + __work_completed(cgpu, work); + free_work(work); + } + wr_unlock(&cgpu->qlock); +} + +static void cta_close(struct cgpu_info *cointerra) +{ + struct cointerra_info *info = cointerra->device_data; + + /* Wait for read thread to die */ + pthread_join(info->read_thr, NULL); + + /* Open does the same reset init followed by response as is required to + * close the device. */ + if (!cta_open(cointerra)) { + applog(LOG_INFO, "%s %d: Reset on close failed", cointerra->drv->name, + cointerra->device_id); + } + + mutex_destroy(&info->lock); + mutex_destroy(&info->sendlock); + /* Don't free info here to avoid trying to access dereferenced members + * once a device is unplugged. */ + cta_clear_work(cointerra); +} + +static struct cgpu_info *cta_detect_one(struct libusb_device *dev, struct usb_find_devices *found) +{ + struct cgpu_info *cointerra = usb_alloc_cgpu(&cointerra_drv, 1); + int tries = 0; + + if (!usb_init(cointerra, dev, found)) + goto fail; + applog(LOG_INFO, "%s %d: Found at %s", cointerra->drv->name, + cointerra->device_id, cointerra->device_path); + + while (!cta_open(cointerra) && !cointerra->usbinfo.nodev) { + if (tries++ > 3) + goto failed_open; + applog(LOG_INFO, "%s %d: Failed to open %d times, retrying", cointerra->drv->name, + cointerra->device_id, tries); + } + + if (!add_cgpu(cointerra)) + goto fail_close; + + update_usb_stats(cointerra); + applog(LOG_INFO, "%s %d: Successfully set up %s", cointerra->drv->name, + cointerra->device_id, cointerra->device_path); + return cointerra; + +fail_close: + cta_close(cointerra); +failed_open: + applog(LOG_INFO, "%s %d: Failed to initialise %s", cointerra->drv->name, + cointerra->device_id, cointerra->device_path); +fail: + usb_free_cgpu(cointerra); + return NULL; +} + +static void cta_detect(bool __maybe_unused hotplug) +{ + usb_detect(&cointerra_drv, cta_detect_one); +} + +/* This function will remove a work item from the hashtable if it matches the + * id in work->subid and return a pointer to the work but it will not free the + * work. It may return NULL if it cannot find matching work. */ +static struct work *take_work_by_id(struct cgpu_info *cgpu, uint16_t id) +{ + struct work *work, *tmp, *ret = NULL; + + wr_lock(&cgpu->qlock); + HASH_ITER(hh, cgpu->queued_work, work, tmp) { + if (work->subid == id) { + ret = work; + break; + } + } + if (ret) + __work_completed(cgpu, ret); + wr_unlock(&cgpu->qlock); + + return ret; +} + +/* This function will look up a work item in the hashtable if it matches the + * id in work->subid and return a cloned work item if it matches. It may return + * NULL if it cannot find matching work. */ +static struct work *clone_work_by_id(struct cgpu_info *cgpu, uint16_t id) +{ + struct work *work, *tmp, *ret = NULL; + + rd_lock(&cgpu->qlock); + HASH_ITER(hh, cgpu->queued_work, work, tmp) { + if (work->subid == id) { + ret = work; + break; + } + } + if (ret) + ret = copy_work(ret); + rd_unlock(&cgpu->qlock); + + return ret; +} + +static bool cta_send_msg(struct cgpu_info *cointerra, char *buf); + +static uint16_t hu16_from_msg(char *buf, int msg) +{ + return le16toh(*(uint16_t *)&buf[msg]); +} + +static uint32_t hu32_from_msg(char *buf, int msg) +{ + return le32toh(*(uint32_t *)&buf[msg]); +} + +static uint64_t hu64_from_msg(char *buf, int msg) +{ + return le64toh(*(uint64_t *)&buf[msg]); +} + +static uint8_t u8_from_msg(char *buf, int msg) +{ + return *(uint8_t *)&buf[msg]; +} + +static void msg_from_hu16(char *buf, int msg, uint16_t val) +{ + *(uint16_t *)&buf[msg] = htole16(val); +} + +static void cta_parse_reqwork(struct cgpu_info *cointerra, struct cointerra_info *info, + char *buf) +{ + uint16_t retwork; + + retwork = hu16_from_msg(buf, CTA_REQWORK_REQUESTS); + applog(LOG_DEBUG, "%s %d: Request work message for %u items received", + cointerra->drv->name, cointerra->device_id, retwork); + + mutex_lock(&info->lock); + info->requested = retwork; + /* Wake up the main scanwork loop since we need more + * work. */ + pthread_cond_signal(&info->wake_cond); + mutex_unlock(&info->lock); +} + +static void cta_parse_recvmatch(struct thr_info *thr, struct cgpu_info *cointerra, + struct cointerra_info *info, char *buf) +{ + uint32_t timestamp_offset, mcu_tag; + uint16_t retwork; + struct work *work; + + /* No endian switch needs doing here since it's sent and returned as + * the same 4 bytes */ + retwork = *(uint16_t *)(&buf[CTA_DRIVER_TAG]); + mcu_tag = hu32_from_msg(buf, CTA_MCU_TAG); + applog(LOG_DEBUG, "%s %d: Match message for id 0x%04x MCU id 0x%08x received", + cointerra->drv->name, cointerra->device_id, retwork, mcu_tag); + + work = clone_work_by_id(cointerra, retwork); + if (likely(work)) { + uint8_t wdiffbits = u8_from_msg(buf, CTA_WORK_DIFFBITS); + uint32_t nonce = hu32_from_msg(buf, CTA_MATCH_NONCE); + unsigned char rhash[32]; + char outhash[16]; + double wdiff; + bool ret; + + timestamp_offset = hu32_from_msg(buf, CTA_MATCH_NOFFSET); + if (timestamp_offset) { + struct work *base_work = work; + + work = copy_work_noffset(base_work, timestamp_offset); + free_work(base_work); + } + + /* Test against the difficulty we asked for along with the work */ + wdiff = bits_to_diff(wdiffbits); + ret = test_nonce_diff(work, nonce, wdiff); + + if (opt_debug) { + /* Debugging, remove me */ + swab256(rhash, work->hash); + __bin2hex(outhash, rhash, 8); + applog(LOG_WARNING, "submit work %s 0x%04x 0x%08x %d 0x%08x", + outhash, retwork, mcu_tag, timestamp_offset, nonce); + } + + if (likely(ret)) { + uint8_t asic, core, pipe, coreno; + int pipeno, bitchar, bitbit; + uint64_t hashes; + + asic = u8_from_msg(buf, CTA_MCU_ASIC); + core = u8_from_msg(buf, CTA_MCU_CORE); + pipe = u8_from_msg(buf, CTA_MCU_PIPE); + pipeno = asic * 512 + core * 128 + pipe; + coreno = asic * 4 + core; + if (unlikely(asic > 1 || core > 3 || pipe > 127 || pipeno > 1023)) { + applog(LOG_WARNING, "%s %d: MCU invalid pipe asic %d core %d pipe %d", + cointerra->drv->name, cointerra->device_id, asic, core, pipe); + coreno = 0; + } else { + info->last_pipe_nonce[pipeno] = time(NULL); + bitchar = pipeno / 8; + bitbit = pipeno % 8; + info->pipe_bitmap[bitchar] |= 0x80 >> bitbit; + } + + applog(LOG_DEBUG, "%s %d: Submitting tested work job_id %s work_id %u", + cointerra->drv->name, cointerra->device_id, work->job_id, work->subid); + ret = submit_tested_work(thr, work); + + hashes = (uint64_t)wdiff * 0x100000000ull; + mutex_lock(&info->lock); + info->share_hashes += hashes; + info->tot_core_hashes[coreno] += hashes; + info->hashes += nonce; + mutex_unlock(&info->lock); + } else { + char sendbuf[CTA_MSG_SIZE]; + uint8_t asic, core, coreno; + asic = u8_from_msg(buf, CTA_MCU_ASIC); + core = u8_from_msg(buf, CTA_MCU_CORE); + coreno = asic * 4 + core; + inc_hw_errors(thr); + + applog(LOG_WARNING, "%s %d: Notify bad match work", + cointerra->drv->name, cointerra->device_id); + if (coreno < CTA_CORES) + info->fmatch_errors[coreno]++; + if (opt_debug) { + uint64_t sdiff = share_diff(work); + unsigned char midstate[32], wdata[12]; + char hexmidstate[68], hexwdata[28]; + uint16_t wid; + + memcpy(&wid, &info->work_id, 2); + flip32(midstate, work->midstate); + __bin2hex(hexmidstate, midstate, 32); + flip12(wdata, &work->data[64]); + __bin2hex(hexwdata, wdata, 12); + applog(LOG_DEBUG, "False match sent: work id %u midstate %s blkhdr %s", + wid, hexmidstate, hexwdata); + applog(LOG_DEBUG, "False match reports: work id 0x%04x MCU id 0x%08x work diff %.1f", + retwork, mcu_tag, wdiff); + applog(LOG_DEBUG, "False match tested: nonce 0x%08x noffset %d %s", + nonce, timestamp_offset, outhash); + applog(LOG_DEBUG, "False match devdiff set to %.1f share diff calc %"PRIu64, + work->device_diff, sdiff); + } + + /* Tell the device we got a false match */ + cta_gen_message(sendbuf, CTA_SEND_FMATCH); + memcpy(sendbuf + 3, buf + 3, CTA_MSG_SIZE - 3); + cta_send_msg(cointerra, sendbuf); + } + free_work(work); + } else { + applog(LOG_WARNING, "%s %d: Matching work id 0x%X %d not found", cointerra->drv->name, + cointerra->device_id, retwork, __LINE__); + inc_hw_errors(thr); + + mutex_lock(&info->lock); + info->no_matching_work++; + mutex_unlock(&info->lock); + } +} + +static void cta_parse_wdone(struct thr_info *thr, struct cgpu_info *cointerra, + struct cointerra_info *info, char *buf) +{ + uint16_t retwork = *(uint16_t *)(&buf[CTA_DRIVER_TAG]); + struct work *work = take_work_by_id(cointerra, retwork); + uint64_t hashes; + + if (likely(work)) { + free_work(work); + applog(LOG_DEBUG, "%s %d: Done work found id 0x%X %d", + cointerra->drv->name, cointerra->device_id, retwork, __LINE__); + } else { + applog(LOG_WARNING, "%s %d: Done work not found id 0x%X %d", + cointerra->drv->name, cointerra->device_id, retwork, __LINE__); + inc_hw_errors(thr); + } + + /* Removing hashes from work done message */ + hashes = hu64_from_msg(buf, CTA_WDONE_NONCES); + if (unlikely(hashes > (61 * 0x100000000ull))) { + applog(LOG_INFO, "%s Invalid hash returned %"PRIu64"x %"PRIu64"x %"PRIu64"X", + __func__, info->hashes, hashes, hashes); + hashes = 0; + } + + mutex_lock(&info->lock); + info->hashes += hashes; + mutex_unlock(&info->lock); +} + +static void u16array_from_msg(uint16_t *u16, int entries, int var, char *buf) +{ + int i, j; + + for (i = 0, j = 0; i < entries; i++, j += sizeof(uint16_t)) + u16[i] = hu16_from_msg(buf, var + j); +} + +static void cta_parse_statread(struct cgpu_info *cointerra, struct cointerra_info *info, + char *buf) +{ + float max_temp = 0; + int i; + + mutex_lock(&info->lock); + u16array_from_msg(info->coretemp, CTA_CORES, CTA_STAT_CORETEMPS, buf); + info->ambtemp_low = hu16_from_msg(buf, CTA_STAT_AMBTEMP_LOW); + info->ambtemp_avg = hu16_from_msg(buf, CTA_STAT_AMBTEMP_AVG); + info->ambtemp_high = hu16_from_msg(buf, CTA_STAT_AMBTEMP_HIGH); + u16array_from_msg(info->pump_tachs, CTA_PUMPS, CTA_STAT_PUMP_TACHS, buf); + u16array_from_msg(info->fan_tachs, CTA_FANS, CTA_STAT_FAN_TACHS, buf); + u16array_from_msg(info->corevolts, CTA_CORES, CTA_STAT_CORE_VOLTS, buf); + info->volts33 = hu16_from_msg(buf, CTA_STAT_VOLTS33); + info->volts12 = hu16_from_msg(buf, CTA_STAT_VOLTS12); + info->inactive = hu16_from_msg(buf, CTA_STAT_INACTIVE); + info->active = hu16_from_msg(buf, CTA_STAT_ACTIVE); + mutex_unlock(&info->lock); + + for (i = 0; i < CTA_CORES; i++) { + if (info->coretemp[i] > max_temp) + max_temp = info->coretemp[i]; + } + max_temp /= 100.0; + /* Store the max temperature in the cgpu struct as an exponentially + * changing value. */ + cointerra->temp = cointerra->temp * 0.63 + max_temp * 0.37; +} + +static void u8array_from_msg(uint8_t *u8, int entries, int var, char *buf) +{ + int i; + + for (i = 0; i < entries; i++) + u8[i] = u8_from_msg(buf, var + i); +} + +static void cta_parse_statset(struct cointerra_info *info, char *buf) +{ + mutex_lock(&info->lock); + u8array_from_msg(info->coreperf, CTA_CORES, CTA_STAT_PERFMODE, buf); + u8array_from_msg(info->fanspeed, CTA_FANS, CTA_STAT_FANSPEEDS, buf); + info->dies_active = u8_from_msg(buf, CTA_STAT_DIES_ACTIVE); + u8array_from_msg(info->pipes_enabled, CTA_CORES, CTA_STAT_PIPES_ENABLED, buf); + u16array_from_msg(info->corefreqs, CTA_CORES, CTA_STAT_CORE_FREQS, buf); + info->uptime = hu32_from_msg(buf,CTA_STAT_UPTIME); + mutex_unlock(&info->lock); +} + +static void cta_parse_irstat(struct cointerra_info *info, char *buf) +{ + uint8_t channel = u8_from_msg(buf,CTA_IRSTAT_CHANNEL); + + if (channel >= CTA_CORES) + return; + + mutex_lock(&info->lock); + info->irstat_vin[channel] = hu16_from_msg(buf,CTA_IRSTAT_VIN); + info->irstat_iin[channel] = hu16_from_msg(buf,CTA_IRSTAT_IIN); + info->irstat_vout[channel] = hu16_from_msg(buf,CTA_IRSTAT_VOUT); + info->irstat_iout[channel] = hu16_from_msg(buf,CTA_IRSTAT_IOUT); + info->irstat_temp1[channel] = hu16_from_msg(buf,CTA_IRSTAT_TEMP1); + info->irstat_temp2[channel] = hu16_from_msg(buf,CTA_IRSTAT_TEMP2); + info->irstat_pout[channel] = hu16_from_msg(buf,CTA_IRSTAT_POUT); + info->irstat_pin[channel] = hu16_from_msg(buf,CTA_IRSTAT_PIN); + info->irstat_efficiency[channel] = hu16_from_msg(buf,CTA_IRSTAT_EFF); + info->irstat_status[channel] = hu16_from_msg(buf,CTA_IRSTAT_STATUS); + mutex_unlock(&info->lock); +} + +static void cta_parse_info(struct cgpu_info *cointerra, struct cointerra_info *info, + char *buf) +{ + mutex_lock(&info->lock); + info->hwrev = hu64_from_msg(buf, CTA_INFO_HWREV); + info->serial = hu32_from_msg(buf, CTA_INFO_SERNO); + info->asics = u8_from_msg(buf, CTA_INFO_NUMASICS); + info->dies = u8_from_msg(buf, CTA_INFO_NUMDIES); + info->cores = hu16_from_msg(buf, CTA_INFO_NUMCORES); + info->board_number = u8_from_msg(buf, CTA_INFO_BOARDNUMBER); + info->fwrev[0] = u8_from_msg(buf, CTA_INFO_FWREV_MAJ); + info->fwrev[1] = u8_from_msg(buf, CTA_INFO_FWREV_MIN); + info->fwrev[2] = u8_from_msg(buf, CTA_INFO_FWREV_MIC); + info->fw_year = hu16_from_msg(buf, CTA_INFO_FWDATE_YEAR); + info->fw_month = u8_from_msg(buf, CTA_INFO_FWDATE_MONTH); + info->fw_day = u8_from_msg(buf, CTA_INFO_FWDATE_DAY); + info->init_diffbits = u8_from_msg(buf, CTA_INFO_INITDIFFBITS); + info->min_diffbits = u8_from_msg(buf, CTA_INFO_MINDIFFBITS); + info->max_diffbits = u8_from_msg(buf, CTA_INFO_MAXDIFFBITS); + mutex_unlock(&info->lock); + + if (!cointerra->unique_id) { + uint32_t b32 = htobe32(info->serial); + + cointerra->unique_id = bin2hex((unsigned char *)&b32, 4); + } +} + +static void cta_parse_rdone(struct cgpu_info *cointerra, struct cointerra_info *info, + char *buf) +{ + uint8_t reset_type, diffbits; + uint64_t wdone; + + reset_type = buf[CTA_RESET_TYPE]; + diffbits = buf[CTA_RESET_DIFF]; + wdone = hu64_from_msg(buf, CTA_WDONE_NONCES); + + if (wdone) { + applog(LOG_INFO, "%s %d: Reset done type %u message %u diffbits %"PRIu64" done received", + cointerra->drv->name, cointerra->device_id, reset_type, diffbits, wdone); + + mutex_lock(&info->lock); + info->hashes += wdone; + mutex_unlock(&info->lock); + } + + /* Note that the cgsem that is posted here must not be waited on while + * holding the info->lock to not get into a livelock since this + * function also grabs the lock first and it's always best to not sleep + * while holding a lock. */ + if (reset_type == CTA_RESET_NEW) { + cta_clear_work(cointerra); + /* Tell reset sender that the reset is complete + * and it may resume. */ + cgsem_post(&info->reset_sem); + } +} + +static void cta_zero_stats(struct cgpu_info *cointerra); + +static void cta_parse_debug(struct cointerra_info *info, char *buf) +{ + mutex_lock(&info->lock); + + info->tot_underruns = hu16_from_msg(buf, CTA_STAT_UNDERRUNS); + u16array_from_msg(info->tot_hw_errors, CTA_CORES, CTA_STAT_HW_ERRORS, buf); + info->tot_hashes = hu64_from_msg(buf, CTA_STAT_HASHES); + info->tot_flushed_hashes = hu64_from_msg(buf, CTA_STAT_FLUSHED_HASHES); + info->autovoltage = u8_from_msg(buf, CTA_STAT_AUTOVOLTAGE); + info->current_ps_percent = u8_from_msg(buf, CTA_STAT_POWER_PERCENT); + info->power_used = hu16_from_msg(buf,CTA_STAT_POWER_USED); + info->power_voltage = hu16_from_msg(buf,CTA_STAT_VOLTAGE); + info->ipower_used = hu16_from_msg(buf,CTA_STAT_IPOWER_USED); + info->ipower_voltage = hu16_from_msg(buf,CTA_STAT_IVOLTAGE); + info->power_temps[0] = hu16_from_msg(buf,CTA_STAT_PS_TEMP1); + info->power_temps[1] = hu16_from_msg(buf,CTA_STAT_PS_TEMP2); + + mutex_unlock(&info->lock); + + /* Autovoltage is positive only once at startup and eventually drops + * to zero. After that time we reset the stats since they're unreliable + * till then. */ + if (unlikely(!info->autovoltage_complete && !info->autovoltage)) { + struct cgpu_info *cointerra = info->thr->cgpu; + + info->autovoltage_complete = true; + cgtime(&cointerra->dev_start_tv); + cta_zero_stats(cointerra); + cointerra->total_mhashes = 0; + cointerra->accepted = 0; + cointerra->rejected = 0; + cointerra->hw_errors = 0; + cointerra->utility = 0.0; + cointerra->last_share_pool_time = 0; + cointerra->diff1 = 0; + cointerra->diff_accepted = 0; + cointerra->diff_rejected = 0; + cointerra->last_share_diff = 0; + } +} + +static int verify_checksum(char *buf) +{ + unsigned char checksum = 0; + unsigned char i; + + for (i = 0; i < 63; i++) + checksum += buf[i]; + + return (checksum == buf[63]); +} + +static void cta_parse_msg(struct thr_info *thr, struct cgpu_info *cointerra, + struct cointerra_info *info, char *buf) +{ + if ((buf[CTA_MSG_TYPE] != CTA_RECV_MATCH)&& + (buf[CTA_MSG_TYPE] != CTA_RECV_WDONE)) { + if (unlikely(verify_checksum(buf) == 0)) { + inc_hw_errors(thr); + applog(LOG_INFO, "%s %d: checksum bad",cointerra->drv->name,cointerra->device_id); + } + } + + switch (buf[CTA_MSG_TYPE]) { + default: + case CTA_RECV_UNUSED: + applog(LOG_INFO, "%s %d: Unidentified message type %u", + cointerra->drv->name, cointerra->device_id, buf[CTA_MSG_TYPE]); + break; + case CTA_RECV_REQWORK: + cta_parse_reqwork(cointerra, info, buf); + break; + case CTA_RECV_MATCH: + cta_parse_recvmatch(thr, cointerra, info, buf); + break; + case CTA_RECV_WDONE: + applog(LOG_DEBUG, "%s %d: Work done message received", + cointerra->drv->name, cointerra->device_id); + cta_parse_wdone(thr, cointerra, info, buf); + break; + case CTA_RECV_STATREAD: + applog(LOG_DEBUG, "%s %d: Status readings message received", + cointerra->drv->name, cointerra->device_id); + cta_parse_statread(cointerra, info, buf); + break; + case CTA_RECV_STATSET: + applog(LOG_DEBUG, "%s %d: Status settings message received", + cointerra->drv->name, cointerra->device_id); + cta_parse_statset(info, buf); + break; + case CTA_RECV_INFO: + applog(LOG_DEBUG, "%s %d: Info message received", + cointerra->drv->name, cointerra->device_id); + cta_parse_info(cointerra, info, buf); + break; + case CTA_RECV_MSG: + applog(LOG_NOTICE, "%s %d: MSG: %s", + cointerra->drv->name, cointerra->device_id, &buf[CTA_MSG_RECVD]); + break; + case CTA_RECV_RDONE: + cta_parse_rdone(cointerra, info, buf); + break; + case CTA_RECV_STATDEBUG: + cta_parse_debug(info, buf); + break; + case CTA_RECV_IRSTAT: + cta_parse_irstat(info, buf); + break; + } +} + +static void *cta_recv_thread(void *arg) +{ + struct thr_info *thr = (struct thr_info *)arg; + struct cgpu_info *cointerra = thr->cgpu; + struct cointerra_info *info = cointerra->device_data; + char threadname[24]; + int offset = 0; + + snprintf(threadname, 24, "cta_recv/%d", cointerra->device_id); + RenameThread(threadname); + + while (likely(!cointerra->shutdown)) { + char buf[CTA_READBUF_SIZE]; + int amount, err; + + if (unlikely(cointerra->usbinfo.nodev)) { + applog(LOG_DEBUG, "%s %d: Device disappeared, disabling recv thread", + cointerra->drv->name, cointerra->device_id); + break; + } + + err = usb_read(cointerra, buf + offset, CTA_MSG_SIZE, &amount, C_CTA_READ); + if (err && err != LIBUSB_ERROR_TIMEOUT) { + applog(LOG_ERR, "%s %d: Read error %d, read %d", cointerra->drv->name, + cointerra->device_id, err, amount); + break; + } + offset += amount; + + while (offset >= CTA_MSG_SIZE) { + char *msg = mystrstr(buf, offset, cointerra_hdr); + int begin; + + if (unlikely(!msg)) { + applog(LOG_WARNING, "%s %d: No message header found, discarding buffer", + cointerra->drv->name, cointerra->device_id); + inc_hw_errors(thr); + /* Save the last byte in case it's the fist + * byte of a header. */ + begin = CTA_MSG_SIZE - 1; + offset -= begin; + memmove(buf, buf + begin, offset); + continue; + } + + if (unlikely(msg != buf)) { + begin = msg - buf; + applog(LOG_WARNING, "%s %d: Reads out of sync, discarding %d bytes", + cointerra->drv->name, cointerra->device_id, begin); + inc_hw_errors(thr); + offset -= begin; + memmove(buf, msg, offset); + if (offset < CTA_MSG_SIZE) + break; + } + + /* We have enough buffer for a full message, parse now */ + cta_parse_msg(thr, cointerra, info, msg); + offset -= CTA_MSG_SIZE; + if (offset > 0) + memmove(buf, buf + CTA_MSG_SIZE, offset); + } + } + + return NULL; +} + +static bool cta_send_msg(struct cgpu_info *cointerra, char *buf) +{ + struct cointerra_info *info = cointerra->device_data; + int amount, err; + + if (unlikely(cointerra->usbinfo.nodev)) + return false; + + /* Serialise usb writes to prevent overlap in case multiple threads + * send messages */ + mutex_lock(&info->sendlock); + err = usb_write(cointerra, buf, CTA_MSG_SIZE, &amount, C_CTA_WRITE); + mutex_unlock(&info->sendlock); + + if (unlikely(err || amount != CTA_MSG_SIZE)) { + applog(LOG_ERR, "%s %d: Write error %d, wrote %d of %d", cointerra->drv->name, + cointerra->device_id, err, amount, CTA_MSG_SIZE); + return false; + } + return true; +} + +static bool cta_prepare(struct thr_info *thr) +{ + struct cgpu_info *cointerra = thr->cgpu; + struct cointerra_info *info = calloc(sizeof(struct cointerra_info), 1); + char buf[CTA_MSG_SIZE]; + + if (unlikely(cointerra->usbinfo.nodev)) + return false; + + if (unlikely(!info)) + quit(1, "Failed to calloc info in cta_detect_one"); + cointerra->device_data = info; + /* Nominally set a requested value when starting, preempting the need + * for a req-work message. */ + info->requested = CTA_MAX_QUEUE; + + info->thr = thr; + mutex_init(&info->lock); + mutex_init(&info->sendlock); + if (unlikely(pthread_cond_init(&info->wake_cond, NULL))) + quit(1, "Failed to create cta pthread cond"); + cgsem_init(&info->reset_sem); + if (pthread_create(&info->read_thr, NULL, cta_recv_thread, (void *)thr)) + quit(1, "Failed to create cta_recv_thread"); + + /* Request a single status setting message */ + cta_gen_message(buf, CTA_SEND_REQUEST); + msg_from_hu16(buf, CTA_REQ_MSGTYPE, CTA_RECV_STATSET); + msg_from_hu16(buf, CTA_REQ_INTERVAL, 0); + if (!cta_send_msg(cointerra, buf)) + return false; + + /* Request status debug messages every 60 seconds */ + cta_gen_message(buf, CTA_SEND_REQUEST); + msg_from_hu16(buf, CTA_REQ_MSGTYPE, CTA_RECV_STATDEBUG); + msg_from_hu16(buf, CTA_REQ_INTERVAL, 6000); + if (!cta_send_msg(cointerra, buf)) + return false; + + cgtime(&info->core_hash_start); + + return true; +} + +static void cta_send_reset(struct cgpu_info *cointerra, struct cointerra_info *info, + uint8_t reset_type, uint8_t diffbits); +static void cta_flush_work(struct cgpu_info *cointerra); + +/* *_fill and *_scanwork are serialised wrt to each other */ +static bool cta_fill(struct cgpu_info *cointerra) +{ + struct cointerra_info *info = cointerra->device_data; + bool ret = true; + char buf[CTA_MSG_SIZE]; + struct work *work = NULL; + unsigned short nroll_limit; + uint32_t swab[8]; + uint8_t diffbits; + + //applog(LOG_WARNING, "%s %d: cta_fill %d", cointerra->drv->name, cointerra->device_id,__LINE__); + + if (unlikely(info->thr->work_restart)) + cta_flush_work(cointerra); + + mutex_lock(&info->lock); + if (!info->requested) + goto out_unlock; + work = get_queued(cointerra); + if (unlikely(!work)) { + ret = false; + goto out_unlock; + } + if (--info->requested > 0) + ret = false; + + /* It does not matter what endian this uint16_t is since it will be + * the same value on sending to the MC as returning in match/done. This + * will automatically wrap as a uint16_t. It cannot be zero for the MCU + * though. */ + if (unlikely(++info->work_id == 0)) + info->work_id = 1; + work->subid = info->work_id; + + diffbits = diff_to_bits(work->device_diff); + + cta_gen_message(buf, CTA_SEND_WORK); + + memcpy(buf + CTA_DRIVER_TAG, &info->work_id, 2); + + flip32(swab, work->midstate); + memcpy(buf + CTA_WORK_MIDSTATE, swab, 32); + + flip12(swab, &work->data[64]); + memcpy(buf + CTA_WORK_DATA, swab, 12); + + nroll_limit = htole16(work->drv_rolllimit); + memcpy(buf + CTA_WORK_NROLL, &nroll_limit, 2); + + memcpy(buf + CTA_WORK_DIFFBITS, &diffbits, 1); + +out_unlock: + mutex_unlock(&info->lock); + + if (work) { + cgtime(&work->tv_work_start); + applog(LOG_DEBUG, "%s %d: Sending work job_id %s work_id %u", cointerra->drv->name, + cointerra->device_id, work->job_id, work->subid); + if (unlikely(!cta_send_msg(cointerra, buf))) { + work_completed(cointerra, work); + applog(LOG_INFO, "%s %d: Failed to send work", + cointerra->drv->name, cointerra->device_id); + /* The device will fail after this */ + } + } + + return ret; +} + +static void cta_send_reset(struct cgpu_info *cointerra, struct cointerra_info *info, + uint8_t reset_type, uint8_t diffbits) +{ + char buf[CTA_MSG_SIZE]; + int ret, retries = 0; + + /* Clear any accumulated messages in case we've gotten out of sync. */ + cgsem_reset(&info->reset_sem); +resend: + cta_gen_message(buf, CTA_SEND_RESET); + + buf[CTA_RESET_TYPE] = reset_type; + buf[CTA_RESET_LOAD] = opt_cta_load ? opt_cta_load : 255; + buf[CTA_RESET_PSLOAD] = opt_ps_load; + + applog(LOG_INFO, "%s %d: Sending Reset type %u with diffbits %u", cointerra->drv->name, + cointerra->device_id, reset_type, diffbits); + cta_send_msg(cointerra, buf); + + /* Wait for read thread to parse a reset message and signal us we may + * return to submitting other messages. Use a timeout in case we have + * a problem and the reset done message never returns. */ + if (reset_type == CTA_RESET_NEW) { + ret = cgsem_mswait(&info->reset_sem, CTA_RESET_TIMEOUT); + if (ret) { + if (++retries < 5) { + applog(LOG_INFO, "%s %d: Timed out waiting for reset done msg, retrying", + cointerra->drv->name, cointerra->device_id); + goto resend; + } + applog(LOG_WARNING, "%s %d: Timed out waiting for reset done msg", + cointerra->drv->name, cointerra->device_id); + } + /* Good place to flush any work we have */ + flush_queue(cointerra); + } +} + +static void cta_flush_work(struct cgpu_info *cointerra) +{ + struct cointerra_info *info = cointerra->device_data; + + applog(LOG_INFO, "%s %d: cta_flush_work %d", cointerra->drv->name, cointerra->device_id, + __LINE__); + cta_send_reset(cointerra, info, CTA_RESET_NEW, 0); + info->thr->work_restart = false; +} + +static void cta_update_work(struct cgpu_info *cointerra) +{ + struct cointerra_info *info = cointerra->device_data; + + applog(LOG_INFO, "%s %d: Update work", cointerra->drv->name, cointerra->device_id); + cta_send_reset(cointerra, info, CTA_RESET_UPDATE, 0); +} + +static void cta_zero_corehashes(struct cointerra_info *info) +{ + int i; + + for (i = 0; i < CTA_CORES; i++) + info->tot_core_hashes[i] = 0; + cgtime(&info->core_hash_start); +} + +/* Send per core hashrate calculations at regular intervals ~every 5 minutes */ +static void cta_send_corehashes(struct cgpu_info *cointerra, struct cointerra_info *info, + double corehash_time) +{ + uint16_t core_ghs[CTA_CORES]; + double k[CTA_CORES]; + char buf[CTA_MSG_SIZE]; + int i, offset; + + for (i = 0; i < CTA_CORES; i++) { + k[i] = (double)info->tot_core_hashes[i]; +#if 0 + k[i] /= ((double)32 * (double)0x100000000ull); + k[i] = sqrt(k[i]) + 1; + k[i] *= k[i]; + k[i] = k[i] * 32 * ((double)0x100000000ull ); +#endif + k[i] /= ((double)1000000000 * corehash_time); + core_ghs[i] = k[i]; + } + cta_gen_message(buf, CTA_SEND_COREHASHRATE); + offset = CTA_CORE_HASHRATES; + for (i = 0; i < CTA_CORES; i++) { + msg_from_hu16(buf, offset, core_ghs[i]); + offset += 2; // uint16_t + } + cta_send_msg(cointerra, buf); +} + +static int64_t cta_scanwork(struct thr_info *thr) +{ + struct cgpu_info *cointerra = thr->cgpu; + struct cointerra_info *info = cointerra->device_data; + double corehash_time; + struct timeval now; + uint32_t runtime; + int64_t hashes; + + applog(LOG_DEBUG, "%s %d: cta_scanwork %d", cointerra->drv->name, cointerra->device_id,__LINE__); + + if (unlikely(cointerra->usbinfo.nodev)) { + hashes = -1; + goto out; + } + + cgtime(&now); + + if (unlikely(thr->work_restart)) { + applog(LOG_INFO, "%s %d: Flush work line %d", + cointerra->drv->name, cointerra->device_id,__LINE__); + cta_flush_work(cointerra); + } else { + struct timespec abstime, tsdiff = {0, 500000000}; + time_t now_t; + int i; + + timeval_to_spec(&abstime, &now); + timeraddspec(&abstime, &tsdiff); + + /* Discard work that was started more than 5 minutes ago as + * a safety precaution backup in case the hardware failed to + * return a work done message for some work items. */ + age_queued_work(cointerra, 300.0); + + /* Each core should be 1.7MH so at max diff of 32 should + * average a share every ~80 seconds.Use this opportunity to + * unset the bits in any pipes that have not returned a valid + * nonce for over 30 full nonce ranges or 2400s. */ + now_t = time(NULL); + for (i = 0; i < 1024; i++) { + if (unlikely(now_t > info->last_pipe_nonce[i] + 2400)) { + int bitchar = i / 8, bitbit = i % 8; + + info->pipe_bitmap[bitchar] &= ~(0x80 >> bitbit); + } + } + + /* Sleep for up to 0.5 seconds, waking if we need work or + * have received a restart message. */ + mutex_lock(&info->lock); + pthread_cond_timedwait(&info->wake_cond, &info->lock, &abstime); + mutex_unlock(&info->lock); + + if (thr->work_restart) { + applog(LOG_INFO, "%s %d: Flush work line %d", + cointerra->drv->name, cointerra->device_id,__LINE__); + cta_flush_work(cointerra); + } + } + + corehash_time = tdiff(&now, &info->core_hash_start); + if (corehash_time > 300) { + cta_send_corehashes(cointerra, info, corehash_time); + cta_zero_corehashes(info); + } + + mutex_lock(&info->lock); + hashes = info->share_hashes; + info->tot_share_hashes += info->share_hashes; + info->tot_calc_hashes += info->hashes; + runtime = cgpu_runtime(thr->cgpu); + runtime /= 30; + info->old_hashes[runtime % 32] = info->tot_calc_hashes; + info->hashes = info->share_hashes = 0; + mutex_unlock(&info->lock); + + if (unlikely(cointerra->usbinfo.nodev)) + hashes = -1; +out: + return hashes; +} + +/* This is used for a work restart. We don't actually perform the work restart + * here but wake up the scanwork loop if it's waiting on the conditional so + * that it can test for the restart message. */ +static void cta_wake(struct cgpu_info *cointerra) +{ + struct cointerra_info *info = cointerra->device_data; + + mutex_lock(&info->lock); + pthread_cond_signal(&info->wake_cond); + mutex_unlock(&info->lock); +} + +static void cta_shutdown(struct thr_info *thr) +{ + struct cgpu_info *cointerra = thr->cgpu; + + cta_close(cointerra); +} + +static void cta_zero_stats(struct cgpu_info *cointerra) +{ + struct cointerra_info *info = cointerra->device_data; + int i; + + info->tot_calc_hashes = 0; + info->tot_reset_hashes = info->tot_hashes; + info->tot_share_hashes = 0; + cta_zero_corehashes(info); + + for (i = 0; i < 16 * 2; i++) + info->old_hashes[i] = 0; +} + +static int bits_set(char v) +{ + int c; + + for (c = 0; v; c++) + v &= v - 1; + return c; +} + +static struct api_data *cta_api_stats(struct cgpu_info *cgpu) +{ + struct api_data *root = NULL; + struct cointerra_info *info = cgpu->device_data; + double dev_runtime = cgpu_runtime(cgpu); + int i, asic, core, coreno = 0; + struct timeval now; + char bitmaphex[36]; + uint64_t ghs, val; + char buf[64]; + uint32_t runtime = cgpu_runtime(cgpu); + + /* Info data */ + root = api_add_uint16(root, "HW Revision", &info->hwrev, false); + root = api_add_uint32(root, "Serial", &info->serial, false); + root = api_add_uint8(root, "Asics", &info->asics, false); + root = api_add_uint8(root, "Dies", &info->dies, false); + root = api_add_uint16(root, "Cores", &info->cores, false); + root = api_add_uint8(root, "Board number", &info->board_number, false); + sprintf(buf, "%u.%u.%u", info->fwrev[0], info->fwrev[1], info->fwrev[2]); + root = api_add_string(root, "FW Revision", buf, true); + sprintf(buf, "%04u-%02u-%02u", info->fw_year, info->fw_month, info->fw_day); + root = api_add_string(root, "FW Date", buf, true); + root = api_add_uint8(root, "Init diffbits", &info->init_diffbits, false); + root = api_add_uint8(root, "Min diffbits", &info->min_diffbits, false); + root = api_add_uint8(root, "Max diffbits", &info->max_diffbits, false); + + /* Status readings */ + for (i = 0; i < CTA_CORES; i++) { + sprintf(buf, "CoreTemp%d", i); + root = api_add_int16(root, buf, &info->coretemp[i], false); + } + root = api_add_int16(root, "Ambient Low", &info->ambtemp_low, false); + root = api_add_int16(root, "Ambient Avg", &info->ambtemp_avg, false); + root = api_add_int16(root, "Ambient High", &info->ambtemp_high, false); + for (i = 0; i < CTA_PUMPS; i++) { + sprintf(buf, "PumpRPM%d", i); + root = api_add_uint16(root, buf, &info->pump_tachs[i], false); + } + for (i = 0; i < CTA_FANS; i++) { + sprintf(buf, "FanRPM%d", i); + root = api_add_uint16(root, buf, &info->fan_tachs[i], false); + } + for (i = 0; i < CTA_CORES; i++) { + sprintf(buf, "CoreFreqs%d", i); + root = api_add_uint16(root, buf, &info->corefreqs[i], false); + } + + for (i = 0; i < CTA_CORES; i++) { + sprintf(buf, "CoreVolts%d", i); + root = api_add_uint16(root, buf, &info->corevolts[i], false); + } + root = api_add_uint16(root, "Volts3.3", &info->volts33, false); + root = api_add_uint16(root, "Volts12", &info->volts12, false); + root = api_add_uint16(root, "Inactive", &info->inactive, false); + root = api_add_uint16(root, "Active", &info->active, false); + + /* Status settings */ + for (i = 0; i < CTA_CORES; i++) { + sprintf(buf, "CorePerfMode%d", i); + root = api_add_uint8(root, buf, &info->coreperf[i], false); + } + for (i = 0; i < CTA_FANS; i++) { + sprintf(buf, "FanSpeed%d", i); + root = api_add_uint8(root, buf, &info->fanspeed[i], false); + } + root = api_add_uint8(root, "DiesActive", &info->dies_active, false); + for (i = 0; i < CTA_CORES; i++) { + sprintf(buf, "PipesEnabled%d", i); + root = api_add_uint8(root, buf, &info->pipes_enabled[i], false); + } + + /* Status debug */ + root = api_add_int(root, "Underruns", &info->tot_underruns, false); + for (i = 0; i < CTA_CORES; i++) { + sprintf(buf, "HWErrors%d", i); + root = api_add_uint16(root, buf, &info->tot_hw_errors[i], false); + } + ghs = info->tot_calc_hashes / dev_runtime; + root = api_add_uint64(root, "Calc hashrate", &ghs, true); + ghs = (info->tot_hashes - info->tot_reset_hashes) / dev_runtime; + root = api_add_uint64(root, "Hashrate", &ghs, true); + //root = api_add_uint64(root, "cgminer 15m Hashrate", &cgpu->rolling15, true); + // get runtime in 30 second steps + runtime = runtime / 30; + // store the current hashes + info->old_hashes[runtime%32] = info->tot_calc_hashes; + // calc the 15 minute average hashrate + ghs = (info->old_hashes[(runtime+31)%32] - info->old_hashes[(runtime+1)%32])/(15*60); + root = api_add_uint64(root, "15m Hashrate", &ghs, true); + ghs = info->tot_share_hashes / dev_runtime; + root = api_add_uint64(root, "Share hashrate", &ghs, true); + root = api_add_uint64(root, "Total calc hashes", &info->tot_calc_hashes, false); + ghs = info->tot_hashes - info->tot_reset_hashes; + root = api_add_uint64(root, "Total hashes", &ghs, true); + root = api_add_uint64(root, "Total raw hashes", &info->tot_hashes, false); + root = api_add_uint64(root, "Total share hashes", &info->tot_share_hashes, false); + root = api_add_uint64(root, "Total flushed hashes", &info->tot_flushed_hashes, false); + val = cgpu->diff_accepted * 0x100000000ull; + root = api_add_uint64(root, "Accepted hashes", &val, true); + ghs = val / dev_runtime; + root = api_add_uint64(root, "Accepted hashrate", &ghs, true); + val = cgpu->diff_rejected * 0x100000000ull; + root = api_add_uint64(root, "Rejected hashes", &val, true); + ghs = val / dev_runtime; + root = api_add_uint64(root, "Rejected hashrate", &ghs, true); + + cgtime(&now); + dev_runtime = tdiff(&now, &info->core_hash_start); + if (dev_runtime < 1) + dev_runtime = 1; + for (i = 0; i < CTA_CORES; i++) { + sprintf(buf, "Core%d hashrate", i); + ghs = info->tot_core_hashes[i] / dev_runtime; + root = api_add_uint64(root, buf, &ghs, true); + } + root = api_add_uint32(root, "Uptime",&info->uptime,false); + for (asic = 0; asic < 2; asic++) { + for (core = 0; core < 4; core++) { + char bitmapcount[40], asiccore[12]; + int count = 0; + + sprintf(asiccore, "Asic%dCore%d", asic, core); + __bin2hex(bitmaphex, &info->pipe_bitmap[coreno], 16); + for (i = coreno; i < coreno + 16; i++) + count += bits_set(info->pipe_bitmap[i]); + snprintf(bitmapcount, 40, "%d:%s", count, bitmaphex); + root = api_add_string(root, asiccore, bitmapcount, true); + coreno += 16; + } + } + root = api_add_uint8(root, "AV", &info->autovoltage, false); + root = api_add_uint8(root, "Power Supply Percent", &info->current_ps_percent, false); + //if (info->power_used != 0) { + { + double value = info->power_used/100.0; + + value *= (info->power_voltage/100.0); + root = api_add_double(root, "Power Used", &value, true); + } + root = api_add_uint16(root, "IOUT", &info->power_used, false); + root = api_add_uint16(root, "VOUT", &info->power_voltage, false); + root = api_add_uint16(root, "IIN", &info->ipower_used, false); + root = api_add_uint16(root, "VIN", &info->ipower_voltage, false); + root = api_add_uint16(root, "PSTemp1", &info->power_temps[0], false); + root = api_add_uint16(root, "PSTemp2", &info->power_temps[1], false); + //} + + for (core = 0; core < CTA_CORES; core++) { + char name[20]; + char str[20]; + double value; + + sprintf(name,"IRVIN%d",core+1); + value = info->irstat_vin[core]/100.0; + root = api_add_double(root,name,&value,true); + sprintf(name,"IRIIN%d",core+1); + value = info->irstat_iin[core]/100.0; + root = api_add_double(root,name,&value,true); + sprintf(name,"IRVOUT%d",core+1); + value = info->irstat_vout[core]/100.0; + root = api_add_double(root,name,&value,true); + sprintf(name,"IRIOUT%d",core+1); + value = info->irstat_iout[core]/100.0; + root = api_add_double(root,name,&value,true); + sprintf(name,"IRTEMP1_%d",core+1); + value = info->irstat_temp1[core]/100.0; + root = api_add_double(root,name,&value,true); + sprintf(name,"IRTEMP2_%d",core+1); + value = info->irstat_temp2[core]/100.0; + root = api_add_double(root,name,&value,true); + sprintf(name,"IRPOUT%d",core+1); + value = info->irstat_pout[core]/100.0; + root = api_add_double(root,name,&value,true); + sprintf(name,"IRPIN%d",core+1); + value = info->irstat_pin[core]/100.0; + root = api_add_double(root,name,&value,true); + sprintf(name,"IREFFICIENCY%d",core+1); + value = info->irstat_efficiency[core]/100.0; + root = api_add_double(root,name,&value,true); + sprintf(name,"IRSTATUS%d",core+1); + //root = api_add_uint16(root,name,&info->irstat_status[core],false); + sprintf(str,"0x%04X",info->irstat_status[core]); + root = api_add_string(root, name, str, true); + } + + for (i = 0; i < CTA_CORES; i++) { + sprintf(buf, "CoreFmatch%d", i); + root = api_add_uint16(root, buf, &info->fmatch_errors[i], false); + } + + return root; +} + +static void cta_statline_before(char *buf, size_t bufsiz, struct cgpu_info *cointerra) +{ + struct cointerra_info *info = cointerra->device_data; + double max_volt = 0; + int freq = 0, i; + + for (i = 0; i < CTA_CORES; i++) { + if (info->corevolts[i] > max_volt) + max_volt = info->corevolts[i]; + if (info->corefreqs[i] > freq) + freq = info->corefreqs[i]; + } + max_volt /= 1000; + + tailsprintf(buf, bufsiz, "%3dMHz %3.1fC %3.2fV", freq, cointerra->temp, max_volt); +} + +struct device_drv cointerra_drv = { + .drv_id = DRIVER_cointerra, + .dname = "cointerra", + .name = "CTA", + .drv_detect = cta_detect, + .thread_prepare = cta_prepare, + .hash_work = hash_queued_work, + .queue_full = cta_fill, + .update_work = cta_update_work, + .scanwork = cta_scanwork, + .flush_work = cta_wake, + .get_api_stats = cta_api_stats, + .get_statline_before = cta_statline_before, + .thread_shutdown = cta_shutdown, + .zero_stats = cta_zero_stats, + .max_diff = 64, // Set it below the actual limit to check nonces +}; diff --git a/driver-cointerra.h b/driver-cointerra.h new file mode 100644 index 0000000000..6f0649ccc2 --- /dev/null +++ b/driver-cointerra.h @@ -0,0 +1,251 @@ +/* + * Copyright 2013-2014 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 COINTERRA_H +#define COINTERRA_H + +#define CTA_READBUF_SIZE 8192 +#define CTA_MSG_SIZE 64 +#define CTA_READ_TIMEOUT 1 +#define CTA_READ_INTERVAL 100 +#define CTA_SCAN_INTERVAL 500 +#define CTA_RESET_TIMEOUT 1000 + +#define CTA_INIT_DIFF 32*0.9999847412109375 + +#if 0 +/* FIXME: how big should this be? */ +#define CTA_MAX_QUEUE 2300 +#else +#define CTA_MAX_QUEUE (32 / CTA_NROLL_TIME) +#endif + +#define CTA_NROLL_TIME 2 + +/* Offsets into buffer */ +#define CTA_MSG_TYPE 2 +#define CTA_RESET_TYPE 3 +#define CTA_RESET_DIFF 4 +#define CTA_RESET_LOAD 5 +#define CTA_RESET_PSLOAD 6 +#define CTA_DRIVER_TAG 3 +#define CTA_MCU_TAG 5 +#define CTA_MCU_CORE 5 +#define CTA_MCU_ASIC 6 +#define CTA_MCU_PIPE 8 +#define CTA_MATCH_NOFFSET 45 +#define CTA_MATCH_NONCE 60 +#define CTA_WDONE_NONCES 11 +#define CTA_MSG_RECVD 3 +#define CTA_WORK_MIDSTATE 9 +#define CTA_WORK_DATA 41 +#define CTA_WORK_NROLL 53 +#define CTA_WORK_DIFFBITS 55 +#define CTA_REQWORK_REQUESTS 3 +#define CTA_CORE_HASHRATES 3 + +/* Received message types */ +#define CTA_RECV_UNUSED 0 +#define CTA_RECV_REQWORK 1 +#define CTA_RECV_MATCH 2 +#define CTA_RECV_WDONE 3 +#define CTA_RECV_STATREAD 4 +#define CTA_RECV_STATSET 5 +#define CTA_RECV_INFO 6 +#define CTA_RECV_MSG 7 +#define CTA_RECV_RDONE 8 +#define CTA_RECV_STATDEBUG 10 +#define CTA_RECV_IRSTAT 11 + +/* Sent message types */ +#define CTA_SEND_UNUSED 0 +#define CTA_SEND_RESET 1 +#define CTA_SEND_WORK 2 +#define CTA_SEND_SETPERF 3 +#define CTA_SEND_REQUEST 4 +#define CTA_SEND_FMATCH 5 +#define CTA_SEND_IDENTIFY 6 +#define CTA_SEND_COREHASHRATE 7 + +/* Types of reset in CTA_RESET_TYPE */ +#define CTA_RESET_NONE 0 +#define CTA_RESET_UPDATE 1 +#define CTA_RESET_NEW 2 +#define CTA_RESET_INIT 3 + +#define CTA_INFO_HWREV 3 +#define CTA_INFO_SERNO 5 +#define CTA_INFO_NUMASICS 9 +#define CTA_INFO_NUMDIES 10 +#define CTA_INFO_NUMCORES 11 +#define CTA_INFO_BOARDNUMBER 13 +#define CTA_INFO_FWREV_MAJ 19 +#define CTA_INFO_FWREV_MIN 20 +#define CTA_INFO_FWREV_MIC 21 +#define CTA_INFO_FWDATE_YEAR 23 +#define CTA_INFO_FWDATE_MONTH 25 +#define CTA_INFO_FWDATE_DAY 26 +#define CTA_INFO_INITDIFFBITS 27 +#define CTA_INFO_MINDIFFBITS 28 +#define CTA_INFO_MAXDIFFBITS 29 + +#define CTA_STAT_CORETEMPS 3 +#define CTA_STAT_AMBTEMP_LOW 19 +#define CTA_STAT_AMBTEMP_AVG 21 +#define CTA_STAT_AMBTEMP_HIGH 23 +#define CTA_STAT_PUMP_TACHS 25 +#define CTA_STAT_FAN_TACHS 29 +#define CTA_STAT_CORE_VOLTS 37 +#define CTA_STAT_VOLTS33 53 +#define CTA_STAT_VOLTS12 55 +#define CTA_STAT_INACTIVE 57 +#define CTA_STAT_ACTIVE 59 + +#define CTA_STAT_PERFMODE 3 +#define CTA_STAT_FANSPEEDS 11 +#define CTA_STAT_DIES_ACTIVE 15 +#define CTA_STAT_PIPES_ENABLED 16 +#define CTA_STAT_MIN_FAN_SPEED 24 +#define CTA_STAT_UPTIME 25 +#define CTA_STAT_HEARTBEATS 29 +#define CTA_STAT_CORE_FREQS 45 + +#define CTA_STAT_UNDERRUNS 3 +#define CTA_STAT_HW_ERRORS 5 +#define CTA_STAT_UPTIME_MS 21 +#define CTA_STAT_HASHES 25 +#define CTA_STAT_FLUSHED_HASHES 33 +#define CTA_STAT_AUTOVOLTAGE 41 +#define CTA_STAT_POWER_PERCENT 42 +#define CTA_STAT_POWER_USED 43 +#define CTA_STAT_VOLTAGE 45 +#define CTA_STAT_IPOWER_USED 47 +#define CTA_STAT_IVOLTAGE 49 +#define CTA_STAT_PS_TEMP1 51 +#define CTA_STAT_PS_TEMP2 53 + +#define CTA_IRSTAT_CHANNEL 3 +#define CTA_IRSTAT_VIN 4 +#define CTA_IRSTAT_IIN 6 +#define CTA_IRSTAT_VOUT 8 +#define CTA_IRSTAT_IOUT 10 +#define CTA_IRSTAT_TEMP1 12 +#define CTA_IRSTAT_TEMP2 14 +#define CTA_IRSTAT_POUT 16 +#define CTA_IRSTAT_PIN 18 +#define CTA_IRSTAT_EFF 20 +#define CTA_IRSTAT_STATUS 22 + + +#define CTA_CORES 8 +#define CTA_PUMPS 2 +#define CTA_FANS 4 + +#define CTA_REQ_MSGTYPE 3 +#define CTA_REQ_INTERVAL 5 + + +int opt_cta_load; +int opt_ps_load; + +struct cointerra_info { + /* Info data */ + uint16_t hwrev; + uint32_t serial; + uint8_t asics; + uint8_t dies; + uint16_t cores; + uint8_t board_number; + uint8_t fwrev[3]; + uint16_t fw_year; + uint8_t fw_month; + uint8_t fw_day; + uint8_t init_diffbits; + uint8_t min_diffbits; + uint8_t max_diffbits; + + /* Status readings data */ + uint16_t coretemp[CTA_CORES]; + uint16_t ambtemp_low; + uint16_t ambtemp_avg; + uint16_t ambtemp_high; + uint16_t pump_tachs[CTA_PUMPS]; + uint16_t fan_tachs[CTA_FANS]; + uint16_t corevolts[CTA_CORES]; + uint16_t volts33; + uint16_t volts12; + uint16_t inactive; + uint16_t active; + uint16_t corefreqs[CTA_CORES]; + uint32_t uptime; + + /* Status settings data */ + uint8_t coreperf[CTA_CORES]; + uint8_t fanspeed[CTA_FANS]; + uint8_t dies_active; + uint8_t pipes_enabled[CTA_CORES]; + + /* Status debug data */ + uint16_t underruns; + uint16_t hw_errors[CTA_CORES]; + uint16_t fmatch_errors[CTA_CORES]; + + /* Running total from debug messages */ + int tot_underruns; + uint16_t tot_hw_errors[CTA_CORES]; + uint64_t tot_hashes; + uint64_t tot_reset_hashes; + uint64_t tot_flushed_hashes; + uint8_t autovoltage; + uint8_t current_ps_percent; + uint16_t power_used; + uint16_t power_voltage; + uint16_t ipower_used; + uint16_t ipower_voltage; + uint16_t power_temps[2]; + + bool autovoltage_complete; + + /* Calculated totals based on work done and nonces found */ + uint64_t hashes; + uint64_t tot_calc_hashes; + + /* Calculated totals based on shares returned */ + uint64_t share_hashes; + uint64_t tot_core_hashes[CTA_CORES]; + uint64_t tot_share_hashes; + struct timeval core_hash_start; + int requested; + uint16_t work_id; + int no_matching_work; + time_t last_pipe_nonce[1024]; + unsigned char pipe_bitmap[128]; + + struct thr_info *thr; + pthread_mutex_t lock; + pthread_mutex_t sendlock; + pthread_cond_t wake_cond; + pthread_t read_thr; + cgsem_t reset_sem; + + uint16_t irstat_vin[CTA_CORES]; + uint16_t irstat_iin[CTA_CORES]; + uint16_t irstat_vout[CTA_CORES]; + uint16_t irstat_iout[CTA_CORES]; + uint16_t irstat_temp1[CTA_CORES]; + uint16_t irstat_temp2[CTA_CORES]; + uint16_t irstat_pout[CTA_CORES]; + uint16_t irstat_pin[CTA_CORES]; + uint16_t irstat_efficiency[CTA_CORES]; + uint16_t irstat_status[CTA_CORES]; + + uint64_t old_hashes[16 * 2]; +}; + +#endif /* COINTERRA_H */ diff --git a/driver-drillbit.c b/driver-drillbit.c new file mode 100644 index 0000000000..a838e07790 --- /dev/null +++ b/driver-drillbit.c @@ -0,0 +1,1092 @@ +/* + * Copyright 2013 Con Kolivas + * Copyright 2013 Angus Gratton + * Copyright 2013 James Nichols + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the Free + * Software Foundation; either version 3 of the License, or (at your option) + * any later version. See COPYING for more details. + */ + +#include "config.h" + +#include "miner.h" +#include "driver-drillbit.h" +#include "sha2.h" + +#define TIMEOUT 3000 +#define RESULT_TIMEOUT 5000 +#define MAX_RESULTS 16 // max results from a single chip + +#define drvlog(prio, fmt, ...) do { \ + if (drillbit->device_id == -1) { \ + applog(prio, "%s: "fmt, \ + drillbit->drv->dname, \ + ##__VA_ARGS__); \ + } else { \ + applog(prio, "%s %d: "fmt, \ + drillbit->drv->name, \ + drillbit->device_id, \ + ##__VA_ARGS__); \ + } \ +} while (0) + +/* Request and response structs for firmware */ + +typedef struct { + uint16_t chip_id; + uint8_t midstate[32]; + uint8_t data[12]; +} WorkRequest; + +#define SZ_SERIALISED_WORKREQUEST 46 +static void serialise_work_request(char *buf, uint16_t chip_id, const struct work *wr); + +typedef struct { + uint16_t chip_id; + uint8_t num_nonces; + uint8_t is_idle; + uint32_t nonce[MAX_RESULTS]; +} WorkResult; + +#define SZ_SERIALISED_WORKRESULT (4+4*MAX_RESULTS) +static void deserialise_work_result(WorkResult *work_result, const char *buf); + +/* V4 config is the preferred one, used internally, non-ASIC-specific */ +typedef struct { + uint16_t core_voltage; // Millivolts + uint16_t clock_freq; // Clock frequency in MHz (or clock level 30-48 for Bitfury internal clock level) + uint8_t clock_div2; // Apply the /2 clock divider (both internal and external), where available + uint8_t use_ext_clock; // Flag. Ignored on boards without external clocks +} BoardConfig; + +typedef struct +{ + uint16_t chip_id; + uint8_t increase_clock; +} AutoTuneRequest; + +#define SZ_SERIALISED_AUTOTUNEREQUEST 3 +static void serialise_autotune_request(char *buf, AutoTuneRequest *aq); + +#define CONFIG_PW1 (1<<0) +#define CONFIG_PW2 (1<<1) + +// Possible core voltage settings on PW1 & PW2, used by legacy V3 config only +#define CONFIG_CORE_065V 0 +#define CONFIG_CORE_075V CONFIG_PW2 +#define CONFIG_CORE_085V CONFIG_PW1 +#define CONFIG_CORE_095V (CONFIG_PW1|CONFIG_PW2) + +/* V3 config is for backwards compatibility with older firmwares */ +typedef struct { + uint8_t core_voltage; // Set to flags defined above + uint8_t int_clock_level; // Clock level (30-48 without divider), see asic.c for details + uint8_t clock_div2; // Apply the /2 clock divider (both internal and external) + uint8_t use_ext_clock; // Ignored on boards without external clocks + uint16_t ext_clock_freq; + } BoardConfigV3; + +#define SZ_SERIALISED_BOARDCONFIG 6 +static void serialise_board_configV4(char *buf, BoardConfig *boardconfig); +static void serialise_board_configV3(char *buf, BoardConfigV3 *boardconfig); + +typedef struct { + uint8_t protocol_version; + char product[8]; + uint32_t serial; + uint8_t num_chips; + uint16_t capabilities; +} Identity; + +/* Capabilities flags known to cgminer */ +#define CAP_TEMP (1<<0) +#define CAP_EXT_CLOCK (1<<1) +#define CAP_IS_AVALON (1<<2) +#define CAP_LIMITER_REMOVED (1<<3) + +#define SZ_SERIALISED_IDENTITY 16 +static void deserialise_identity(Identity *identity, const char *buf); + +// Hashable structure of per-device config settings +typedef struct { + char key[9]; + BoardConfig config; + UT_hash_handle hh; +} config_setting; + +static config_setting *settings; + +static void drillbit_empty_buffer(struct cgpu_info *drillbit); + +/* Automatic tuning parameters */ +static uint32_t auto_every = 100; +static uint32_t auto_good = 1; +static uint32_t auto_bad = 3; +static uint32_t auto_max = 10; + +/* Return a pointer to the chip_info structure for a given chip id, or NULL otherwise */ +static struct drillbit_chip_info *find_chip(struct drillbit_info *info, uint16_t chip_id) { + int i; + + for (i = 0; i < info->num_chips; i++) { + if (info->chips[i].chip_id == chip_id) + return &info->chips[i]; + } + return NULL; +} + +/* Read a fixed size buffer back from USB, returns true on success */ +static bool usb_read_fixed_size(struct cgpu_info *drillbit, void *result, size_t result_size, int timeout, enum usb_cmds command_name) { + char *res = (char *)result; + int ms_left; + size_t count; + struct timeval tv_now, tv_start; + int amount; + + cgtime(&tv_start); + ms_left = timeout; + + amount = 1; + count = 0; + while (count < result_size && ms_left > 0) { + usb_read_timeout(drillbit, &res[count], result_size-count, &amount, ms_left, command_name); + count += amount; + cgtime(&tv_now); + ms_left = timeout - ms_tdiff(&tv_now, &tv_start); + } + if (count == result_size) { + return true; + } + drvlog(LOG_ERR, "Read incomplete fixed size packet - got %d bytes / %d (timeout %d)", + (int)count, (int)result_size, timeout); + drillbit_empty_buffer(drillbit); + return false; +} + +static bool usb_read_simple_response(struct cgpu_info *drillbit, char command, enum usb_cmds command_name); + +/* Write a simple one-byte command and expect a simple one-byte response + Returns true on success +*/ +static bool usb_send_simple_command(struct cgpu_info *drillbit, char command, enum usb_cmds command_name) { + int amount; + + usb_write_timeout(drillbit, &command, 1, &amount, TIMEOUT, C_BF_REQWORK); + if (amount != 1) { + drvlog(LOG_ERR, "Failed to write command %c", command); + return false; + } + return usb_read_simple_response(drillbit, command, command_name); +} + +/* Read a simple single-byte response and check it matches the correct command character + Return true on success +*/ +static bool usb_read_simple_response(struct cgpu_info *drillbit, char command, enum usb_cmds command_name) { + int amount; + char response; + /* Expect a single byte, matching the command, as acknowledgement */ + usb_read_timeout(drillbit, &response, 1, &amount, TIMEOUT, command_name); + if (amount != 1) { + drvlog(LOG_ERR, "Got no response to command %c", command); + return false; + } + if (response != command) { + drvlog(LOG_ERR, "Got unexpected response %c to command %c", response, command); + return false; + } + return true; +} + +#define EMPTY_TIMEOUT 5 + +static void drillbit_empty_buffer(struct cgpu_info *drillbit) +{ + char buf[512]; + int amount; + + do { + usb_read_timeout(drillbit, buf, sizeof(buf), &amount, EMPTY_TIMEOUT, C_BF_FLUSH); + } while (amount); +} + +static void drillbit_open(struct cgpu_info *drillbit) +{ + drillbit_empty_buffer(drillbit); +} + +static void drillbit_close(struct cgpu_info *drillbit) +{ + struct drillbit_info *info = drillbit->device_data; + drillbit_empty_buffer(drillbit); + if (info->chips) + free(info->chips); +} + +static void drillbit_identify(struct cgpu_info *drillbit) +{ + usb_send_simple_command(drillbit, 'L', C_BF_IDENTIFY); +} + +#define ID_TIMEOUT 1000 + +static bool drillbit_getinfo(struct cgpu_info *drillbit, struct drillbit_info *info) +{ + int err; + int amount; + char buf[SZ_SERIALISED_IDENTITY]; + Identity identity; + + drillbit_empty_buffer(drillbit); + err = usb_write_timeout(drillbit, "I", 1, &amount, TIMEOUT, C_BF_REQINFO); + if (err) { + drvlog(LOG_INFO, "Failed to write REQINFO"); + return false; + } + // can't call usb_read_fixed_size here as stats not initialised + err = usb_read_timeout(drillbit, buf, SZ_SERIALISED_IDENTITY, &amount, ID_TIMEOUT, C_BF_GETINFO); + if (err) { + drvlog(LOG_ERR, "Failed to read GETINFO"); + return false; + } + if (amount != SZ_SERIALISED_IDENTITY) { + drvlog(LOG_ERR, "Getinfo received %d bytes instead of %d", + amount, (int)sizeof(Identity)); + return false; + } + deserialise_identity(&identity, buf); + + // sanity checks on the identity buffer we get back + if (strlen(identity.product) == 0 || identity.serial == 0 || identity.num_chips == 0) { + drvlog(LOG_ERR, "Got invalid contents for GETINFO identity response"); + return false; + } + + const int MIN_VERSION = 2; + const int MAX_VERSION = 4; + if (identity.protocol_version < MIN_VERSION) { + drvlog(LOG_ERR, "Unknown device protocol version %d.", identity.protocol_version); + return false; + } + if (identity.protocol_version > MAX_VERSION) { + drvlog(LOG_ERR, "Device firmware uses newer Drillbit protocol %d. We only support up to %d. Find a newer cgminer!", identity.protocol_version, MAX_VERSION); + return false; + } + + if (identity.protocol_version == 2 && identity.num_chips == 1) { + // Production firmware Thumbs don't set any capability bits, so fill in the EXT_CLOCK one + identity.capabilities = CAP_EXT_CLOCK; + } + + // load identity data into device info structure + info->protocol_version = identity.protocol_version; + if (strncmp(identity.product, "DRILLBIT", sizeof(identity.product)) == 0) { + // Hack: first production firmwares all described themselves as DRILLBIT, so fill in the gaps + if (identity.num_chips == 1) + strcpy(info->product, "Thumb"); + else + strcpy(info->product, "Eight"); + } else { + memcpy(info->product, identity.product, sizeof(identity.product)); + } + info->serial = identity.serial; + info->num_chips = identity.num_chips; + info->capabilities = identity.capabilities; + + drvlog(LOG_INFO, "Getinfo returned version %d, product %s serial %08x num_chips %d", + info->protocol_version, info->product, info->serial, info->num_chips); + + drillbit_empty_buffer(drillbit); + return true; +} + +static bool drillbit_reset(struct cgpu_info *drillbit) +{ + struct drillbit_info *info = drillbit->device_data; + struct drillbit_chip_info *chip; + int i, k, res; + + res = usb_send_simple_command(drillbit, 'R', C_BF_REQRESET); + + for (i = 0; i < info->num_chips; i++) { + chip = &info->chips[i]; + chip->state = IDLE; + chip->work_sent_count = 0; + for (k = 0; k < WORK_HISTORY_LEN-1; k++) { + if (chip->current_work[k]) { + work_completed(drillbit, chip->current_work[k]); + chip->current_work[k] = NULL; + } + } + } + + drillbit_empty_buffer(drillbit); + return res; +} + +static config_setting *find_settings(struct cgpu_info *drillbit) +{ + struct drillbit_info *info = drillbit->device_data; + config_setting *setting; + char search_key[9]; + + if (!settings) { + drvlog(LOG_INFO, "Keeping onboard defaults for device %s (serial %08x)", + info->product, info->serial); + return NULL; + } + + // Search by serial + sprintf(search_key, "%08x", info->serial); + HASH_FIND_STR(settings, search_key, setting); + if (setting) { + drvlog(LOG_INFO, "Using serial specific settings for serial %s", search_key); + return setting; + } + + // Search by DRBxxx + snprintf(search_key, 9, "DRB%d", drillbit->device_id); + HASH_FIND_STR(settings, search_key, setting); + if (setting) { + drvlog(LOG_INFO, "Using device_id specific settings for device"); + return setting; + } + + // Failing that, search by product name + HASH_FIND_STR(settings, info->product, setting); + if (setting) { + drvlog(LOG_INFO, "Using product-specific settings for device %s", info->product); + return setting; + } + + // Search by "short" product name + snprintf(search_key, 9, "%c%d", info->product[0], info->num_chips); + HASH_FIND_STR(settings, search_key, setting); + if (setting) { + drvlog(LOG_INFO, "Using product-specific settings for device %s", info->product); + return setting; + } + + // Check for a generic/catchall drillbit-options argument (key set to NULL) + search_key[0] = 0; + HASH_FIND_STR(settings, search_key, setting); + if (setting) { + drvlog(LOG_INFO, "Using non-specific settings for device %s (serial %08x)", info->product, + info->serial); + return setting; + } + + drvlog(LOG_WARNING, "Keeping onboard defaults for device %s (serial %08x)", + info->product, info->serial); + return NULL; +} + +static void drillbit_send_config(struct cgpu_info *drillbit) +{ + struct drillbit_info *info = drillbit->device_data; + int amount; + char buf[SZ_SERIALISED_BOARDCONFIG+1]; + config_setting *setting; + BoardConfigV3 v3_config; + + // Find the relevant board config + setting = find_settings(drillbit); + if (!setting) + return; // Don't update board config from defaults + drvlog(LOG_NOTICE, "Config: %s:%d:%d:%d Serial: %08x", + setting->config.use_ext_clock ? "ext" : "int", + setting->config.clock_freq, + setting->config.clock_div2 ? 2 : 1, + setting->config.core_voltage, + info->serial); + + if (setting->config.use_ext_clock && !(info->capabilities & CAP_EXT_CLOCK)) { + drvlog(LOG_WARNING, "Chosen configuration specifies external clock but this device (serial %08x) has no external clock!", info->serial); + } + + if (info->protocol_version <= 3) { + /* Make up a backwards compatible V3 config structure to send to the miner */ + if (setting->config.core_voltage >= 950) + v3_config.core_voltage = CONFIG_CORE_095V; + else if (setting->config.core_voltage >= 850) + v3_config.core_voltage = CONFIG_CORE_085V; + else if (setting->config.core_voltage >= 750) + v3_config.core_voltage = CONFIG_CORE_075V; + else + v3_config.core_voltage = CONFIG_CORE_065V; + if (setting->config.clock_freq > 64) + v3_config.int_clock_level = setting->config.clock_freq / 5; + else + v3_config.int_clock_level = setting->config.clock_freq; + v3_config.clock_div2 = setting->config.clock_div2; + v3_config.use_ext_clock = setting->config.use_ext_clock; + v3_config.ext_clock_freq = setting->config.clock_freq; + serialise_board_configV3(&buf[1], &v3_config); + } else { + serialise_board_configV4(&buf[1], &setting->config); + } + buf[0] = 'C'; + usb_write_timeout(drillbit, buf, sizeof(buf), &amount, TIMEOUT, C_BF_CONFIG); + + /* Expect a single 'C' byte as acknowledgement */ + usb_read_simple_response(drillbit, 'C', C_BF_CONFIG); // TODO: verify response +} + +static void drillbit_updatetemps(struct thr_info *thr) +{ + struct cgpu_info *drillbit = thr->cgpu; + struct drillbit_info *info = drillbit->device_data; + char cmd; + int amount; + uint16_t temp; + struct timeval tv_now; + + if (!(info->capabilities & CAP_TEMP)) + return; + + cgtime(&tv_now); + if (ms_tdiff(&tv_now, &info->tv_lasttemp) < 1000) + return; // Only update temps once a second + info->tv_lasttemp = tv_now; + + cmd = 'T'; + usb_write_timeout(drillbit, &cmd, 1, &amount, TIMEOUT, C_BF_GETTEMP); + + if (!usb_read_fixed_size(drillbit, &temp, sizeof(temp), TIMEOUT, C_BF_GETTEMP)) { + drvlog(LOG_ERR, "Got no response to request for current temperature"); + return; + } + + drvlog(LOG_INFO, "Got temperature reading %d.%dC", temp/10, temp%10); + info->temp = temp; + if (temp > info->max_temp) + info->max_temp = temp; +} + +static void drillbit_get_statline_before(char *buf, size_t bufsiz, struct cgpu_info *drillbit) +{ + struct drillbit_info *info = drillbit->device_data; + + if ((info->capabilities & CAP_TEMP) && info->temp != 0) { + tailsprintf(buf, bufsiz, "%c%2d %.1fC max%.1fC", + info->product[0], + info->num_chips, + (float)(info->temp/10.0), + (float)(info->max_temp/10.0)); + } else { + tailsprintf(buf, bufsiz, "%c%2d", + info->product[0], + info->num_chips); + } +} + + +static bool drillbit_parse_options(__maybe_unused struct cgpu_info *drillbit) +{ + /* Read configuration options (currently global not per-ASIC or per-board) */ + if (settings != NULL) + return true; // Already initialised + + char *next_opt = opt_drillbit_options; + while (next_opt && strlen(next_opt)) { + BoardConfig parsed_config; + config_setting *new_setting; + char key[9]; + int count, freq, clockdiv, voltage; + char clksrc[4]; + + // Try looking for an option tagged with a key, first + count = sscanf(next_opt, "%8[^:]:%3s:%d:%d:%d", key, + clksrc, &freq, &clockdiv, &voltage); + if (count < 5) { + key[0] = 0; + count = sscanf(next_opt, "%3s:%d:%d:%d", + clksrc, &freq, &clockdiv, &voltage); + if (count < 4) { + quithere(1, "Failed to parse drillbit-options. Invalid options string: '%s'", next_opt); + } + } + + if (clockdiv != 1 && clockdiv != 2) { + quithere(1, "Invalid clock divider value %d. Valid values are 1 & 2.", clockdiv); + } + parsed_config.clock_div2 = count > 2 && clockdiv == 2; + + if (!strcmp("int",clksrc)) { + parsed_config.use_ext_clock = 0; + } + else if (!strcmp("ext", clksrc)) { + parsed_config.use_ext_clock = 1; + } else + quithere(1, "Invalid clock source. Valid choices are int, ext."); + + parsed_config.clock_freq = freq; + parsed_config.core_voltage = voltage; + + // Add the new set of settings to the configuration choices hash table + new_setting = (config_setting *)calloc(sizeof(config_setting), 1); + memcpy(&new_setting->config, &parsed_config, sizeof(BoardConfig)); + memcpy(&new_setting->key, key, 8); + config_setting *ignore; + HASH_REPLACE_STR(settings, key, new_setting, ignore); + + // Look for next comma-delimited Drillbit option + next_opt = strstr(next_opt, ","); + if (next_opt) + next_opt++; + } + + if (opt_drillbit_auto) { + sscanf(opt_drillbit_auto, "%d:%d:%d:%d", + &auto_every, &auto_good, &auto_bad, &auto_max); + if (auto_max < auto_bad) { + quithere(1, "Bad drillbit-auto: MAX limit must be greater than BAD limit"); + } + if (auto_bad < auto_good) { + quithere(1, "Bad drillbit-auto: GOOD limit must be greater than BAD limit"); + } + } + + return true; +} + +static struct cgpu_info *drillbit_detect_one(struct libusb_device *dev, struct usb_find_devices *found) +{ + struct cgpu_info *drillbit; + struct drillbit_info *info; + int i; + + drillbit = usb_alloc_cgpu(&drillbit_drv, 1); + drillbit->device_id = -1; // so drvlog() prints dname + + if (!drillbit_parse_options(drillbit)) + goto out; + + if (!usb_init(drillbit, dev, found)) + goto out; + + drvlog(LOG_INFO, "Device found at %s", drillbit->device_path); + + info = calloc(sizeof(struct drillbit_info), 1); + if (!info) + quit(1, "Failed to calloc info in %s", __func__); + drillbit->device_data = info; + + drillbit_open(drillbit); + + /* Send getinfo request */ + if (!drillbit_getinfo(drillbit, info)) + goto out_close; + + /* TODO: Add detection for actual chip ids based on command/response, + not prefill assumption about chip layout based on info structure */ + info->chips = calloc(sizeof(struct drillbit_chip_info), info->num_chips); + for (i = 0; i < info->num_chips; i++) { + info->chips[i].chip_id = i; + info->chips[i].auto_max = 999; + } + + /* Send reset request */ + if (!drillbit_reset(drillbit)) + goto out_close; + + drillbit_identify(drillbit); + drillbit_empty_buffer(drillbit); + + cgtime(&info->tv_lastchipinfo); + + if (!add_cgpu(drillbit)) + goto out_close; + + update_usb_stats(drillbit); + + if (info->capabilities & CAP_LIMITER_REMOVED) { + drvlog(LOG_WARNING, "Recommended limits have been disabled on this board, take care when changing settings."); + } + + drillbit_send_config(drillbit); + + drvlog(LOG_INFO, "Successfully initialised %s", + drillbit->device_path); + + return drillbit; +out_close: + drillbit_close(drillbit); + usb_uninit(drillbit); +out: + drillbit = usb_free_cgpu(drillbit); + return drillbit; +} + +static void drillbit_detect(bool __maybe_unused hotplug) +{ + usb_detect(&drillbit_drv, drillbit_detect_one); +} + +static uint32_t decnonce(uint32_t in) +{ + uint32_t out; + + /* First part load */ + out = (in & 0xFF) << 24; in >>= 8; + + /* Byte reversal */ + in = (((in & 0xaaaaaaaa) >> 1) | ((in & 0x55555555) << 1)); + in = (((in & 0xcccccccc) >> 2) | ((in & 0x33333333) << 2)); + in = (((in & 0xf0f0f0f0) >> 4) | ((in & 0x0f0f0f0f) << 4)); + + out |= (in >> 2)&0x3FFFFF; + + /* Extraction */ + if (in & 1) out |= (1 << 23); + if (in & 2) out |= (1 << 22); + + out -= 0x800004; + return out; +} + +#define BF_OFFSETS 3 +static const uint32_t bf_offsets[] = {-0x800000, 0, -0x400000}; + +static bool drillbit_checkresults(struct thr_info *thr, struct work *work, uint32_t nonce) +{ + struct cgpu_info *drillbit = thr->cgpu; + struct drillbit_info *info = drillbit->device_data; + int i; + + if (info->capabilities & CAP_IS_AVALON) { + if (test_nonce(work, nonce)) { + submit_tested_work(thr, work); + return true; + } + } + else { /* Bitfury */ + nonce = decnonce(nonce); + for (i = 0; i < BF_OFFSETS; i++) { + if (test_nonce(work, nonce + bf_offsets[i])) { + submit_tested_work(thr, work); + return true; + } + } + } + return false; +} + +/* Check if this ASIC should be tweaked up or down in clock speed */ +static void drillbit_check_auto(struct thr_info *thr, struct drillbit_chip_info *chip) +{ + struct cgpu_info *drillbit = thr->cgpu; + AutoTuneRequest request; + char buf[SZ_SERIALISED_AUTOTUNEREQUEST+1]; + int amount; + bool tune_up, tune_down; + + /* + Only check automatic tuning every "auto_every" work units, + or if the error count exceeds the 'max' count + */ + if (chip->success_auto + chip->error_auto < auto_every && + (chip->error_auto < auto_max)) + return; + + tune_up = chip->error_auto < auto_good && chip->auto_delta < chip->auto_max; + tune_down = chip->error_auto > auto_bad; + + + drvlog(tune_up||tune_down ? LOG_NOTICE : LOG_DEBUG, + "Chip id %d has %d/%d error rate %s", chip->chip_id, chip->error_auto, + chip->error_auto + chip->success_auto, + tune_up ? " - tuning up" : tune_down ? " - tuning down" : " - no change"); + + if (tune_up || tune_down) { + /* Value should be tweaked */ + buf[0] = 'A'; + request.chip_id = chip->chip_id; + request.increase_clock = tune_up; + serialise_autotune_request(&buf[1], &request); + usb_write_timeout(drillbit, buf, sizeof(buf), &amount, TIMEOUT, C_BF_AUTOTUNE); + usb_read_simple_response(drillbit, 'A', C_BF_AUTOTUNE); + if (tune_up) { + chip->auto_delta++; + } else { + chip->auto_delta--; + if (chip->error_auto >= auto_max + && chip->success_count + chip->error_count > auto_every) { + drvlog(LOG_ERR, "Chip id %d capping auto delta at max %d",chip->chip_id, + chip->auto_delta); + chip->auto_max = chip->auto_delta; + } + } + } + + chip->success_auto = 0; + chip->error_auto = 0; +} + +// Check and submit back any pending work results from firmware, +// returns number of successful results found +static int check_for_results(struct thr_info *thr) +{ + struct cgpu_info *drillbit = thr->cgpu; + struct drillbit_info *info = drillbit->device_data; + struct drillbit_chip_info *chip; + char cmd; + int amount, i, k, found; + uint8_t j; + int successful_results = 0; + uint32_t result_count; + char buf[SZ_SERIALISED_WORKRESULT]; + WorkResult *responses = NULL; + WorkResult *response; + + if (unlikely(thr->work_restart)) + goto cleanup; + + // Send request for completed work + cmd = 'E'; + usb_write_timeout(drillbit, &cmd, 1, &amount, TIMEOUT, C_BF_GETRES); + + // Receive count for work results + if (!usb_read_fixed_size(drillbit, &result_count, sizeof(result_count), TIMEOUT, C_BF_GETRES)) { + drvlog(LOG_ERR, "Got no response to request for work results"); + goto cleanup; + } + if (unlikely(drillbit->usbinfo.nodev)) + goto cleanup; + if (result_count) + drvlog(LOG_DEBUG, "Result count %d",result_count); + + if (result_count > 1024) { + drvlog(LOG_ERR, "Got implausible result count %d - treating as error!", result_count); + goto cleanup; + } + + if (result_count == 0) { + // Short circuit reading any work results + return 0; + } + + responses = calloc(result_count, sizeof(WorkResult)); + + // Receive work results (0 or more) into buffer + for (j = 0; j < result_count; j++) { + if (unlikely(drillbit->usbinfo.nodev)) + goto cleanup; + if (!usb_read_fixed_size(drillbit, buf, SZ_SERIALISED_WORKRESULT, TIMEOUT, C_BF_GETRES)) { + drvlog(LOG_ERR, "Failed to read response data packet idx %d count 0x%x", j, result_count); + drillbit_empty_buffer(drillbit); + goto cleanup; + } + deserialise_work_result(&responses[j], buf); + } + + for (j = 0; j < result_count; j++) { + if (unlikely(thr->work_restart)) + goto cleanup; + + response = &responses[j]; + drvlog(LOG_DEBUG, "Got response packet chip_id %d nonces %d is_idle %d", response->chip_id, response->num_nonces, response->is_idle); + chip = find_chip(info, response->chip_id); + if (!chip) { + drvlog(LOG_ERR, "Got work result for unknown chip id %d", response->chip_id); + drillbit_empty_buffer(drillbit); + continue; + } + if (chip->state == IDLE) { + drvlog(LOG_WARNING, "Got spurious work results for idle ASIC %d", response->chip_id); + } + if (response->num_nonces > MAX_RESULTS) { + drvlog(LOG_ERR, "Got invalid number of result nonces (%d) for chip id %d", response->num_nonces, response->chip_id); + drillbit_empty_buffer(drillbit); + goto cleanup; + } + + found = false; + for (i = 0; i < response->num_nonces; i++) { + if (unlikely(thr->work_restart)) + goto cleanup; + for (k = 0; k < WORK_HISTORY_LEN; k++) { + /* NB we deliberately check all results against all work because sometimes ASICs seem to give multiple "valid" nonces, + and this seems to avoid some result that would otherwise be rejected by the pool. + */ + if (chip->current_work[k] && drillbit_checkresults(thr, chip->current_work[k], response->nonce[i])) { + chip->success_count++; + chip->success_auto++; + successful_results++; + found = true; + } + } + } + drvlog(LOG_DEBUG, "%s nonce %08x", (found ? "Good":"Bad"), response->num_nonces ? response->nonce[0] : 0); + if (!found && chip->state != IDLE && response->num_nonces > 0) { + /* all nonces we got back from this chip were invalid */ + inc_hw_errors(thr); + chip->error_count++; + chip->error_auto++; + } + if (chip->state == WORKING_QUEUED && !response->is_idle) + chip->state = WORKING_NOQUEUED; // Time to queue up another piece of "next work" + else + chip->state = IDLE; // Uh-oh, we're totally out of work for this ASIC! + + if (opt_drillbit_auto && info->protocol_version >= 4) + drillbit_check_auto(thr, chip); + } + +cleanup: + if (responses) + free(responses); + return successful_results; +} + +static void drillbit_send_work_to_chip(struct thr_info *thr, struct drillbit_chip_info *chip) +{ + struct cgpu_info *drillbit = thr->cgpu; + struct work *work; + char buf[SZ_SERIALISED_WORKREQUEST+1]; + int amount, i; + + /* Get some new work for the chip */ + work = get_queue_work(thr, drillbit, thr->id); + if (unlikely(thr->work_restart)) { + work_completed(drillbit, work); + return; + } + + drvlog(LOG_DEBUG, "Sending work to chip_id %d", chip->chip_id); + serialise_work_request(&buf[1], chip->chip_id, work); + + /* Send work to cgminer */ + buf[0] = 'W'; + usb_write_timeout(drillbit, buf, sizeof(buf), &amount, TIMEOUT, C_BF_REQWORK); + + /* Expect a single 'W' byte as acknowledgement */ + usb_read_simple_response(drillbit, 'W', C_BF_REQWORK); + if (chip->state == WORKING_NOQUEUED) + chip->state = WORKING_QUEUED; + else + chip->state = WORKING_NOQUEUED; + + if (unlikely(thr->work_restart)) { + work_completed(drillbit, work); + return; + } + + // Read into work history + if (chip->current_work[0]) + work_completed(drillbit, chip->current_work[0]); + for (i = 0; i < WORK_HISTORY_LEN-1; i++) + chip->current_work[i] = chip->current_work[i+1]; + chip->current_work[WORK_HISTORY_LEN-1] = work; + cgtime(&chip->tv_start); + + chip->work_sent_count++; +} + +static int64_t drillbit_scanwork(struct thr_info *thr) +{ + struct cgpu_info *drillbit = thr->cgpu; + struct drillbit_info *info = drillbit->device_data; + struct drillbit_chip_info *chip; + struct timeval tv_now; + int amount, i, j, ms_diff, result_count = 0, sent_count = 0;; + char buf[200]; + + /* send work to an any chip without queued work */ + for (i = 0; i < info->num_chips && sent_count < 8; i++) { + if (info->chips[i].state != WORKING_QUEUED) { + drillbit_send_work_to_chip(thr, &info->chips[i]); + sent_count++; + } + if (unlikely(thr->work_restart) || unlikely(drillbit->usbinfo.nodev)) + goto cascade; + } + + /* check for any chips that have timed out on sending results */ + cgtime(&tv_now); + for (i = 0; i < info->num_chips; i++) { + if (info->chips[i].state == IDLE) + continue; + ms_diff = ms_tdiff(&tv_now, &info->chips[i].tv_start); + if (ms_diff > RESULT_TIMEOUT) { + if (info->chips[i].work_sent_count > 4) { + /* Only count ASIC timeouts after the pool has started to send work in earnest, + some pools can create unusual delays early on */ + drvlog(LOG_ERR, "Timing out unresponsive ASIC %d", info->chips[i].chip_id); + info->chips[i].timeout_count++; + info->chips[i].error_auto++; + } + info->chips[i].state = IDLE; + drillbit_send_work_to_chip(thr, &info->chips[i]); + } + if (unlikely(thr->work_restart) || unlikely(drillbit->usbinfo.nodev)) + goto cascade; + } + + /* Check for results */ + result_count = check_for_results(thr); + + /* Print a per-chip info line every 30 seconds */ + cgtime(&tv_now); + if (opt_log_level <= LOG_INFO && ms_tdiff(&tv_now, &info->tv_lastchipinfo) > 30000) { + /* TODO: this output line may get truncated (max debug is 256 bytes) once we get more + chips in a single device + */ + amount = sprintf(buf, "%s %d: S/E/T", drillbit->drv->name, drillbit->device_id); + if (amount > 0) { + for (i = 0; i < info->num_chips; i++) { + chip= &info->chips[i]; + j = snprintf(&buf[amount], sizeof(buf)-(size_t)amount, "%u:%u/%u/%u", + chip->chip_id, chip->success_count, chip->error_count, + chip->timeout_count); + if (j < 0) + break; + amount += j; + if ((size_t)amount >= sizeof(buf)) + break; + } + drvlog(LOG_INFO, "%s", buf); + cgtime(&info->tv_lastchipinfo); + } + } + + drillbit_updatetemps(thr); + +cascade: + if (unlikely(drillbit->usbinfo.nodev)) { + drvlog(LOG_WARNING, "Device disappeared, disabling thread"); + return -1; + } + + if (unlikely(thr->work_restart)) { + /* Issue an ASIC reset as we won't be coming back for any of these results */ + drvlog(LOG_DEBUG, "Received work restart, resetting ASIC"); + drillbit_reset(drillbit); + } + + return 0xffffffffULL * result_count; +} + +static struct api_data *drillbit_api_stats(struct cgpu_info *cgpu) +{ + struct drillbit_info *info = cgpu->device_data; + struct api_data *root = NULL; + char serial[16]; + int version; + + version = info->protocol_version; + root = api_add_int(root, "Protocol Version", &version, true); + root = api_add_string(root, "Product", info->product, false); + sprintf(serial, "%08x", info->serial); + root = api_add_string(root, "Serial", serial, true); + root = api_add_uint8(root, "ASIC Count", &info->num_chips, true); + if (info->capabilities & CAP_TEMP) { + float temp = (float)info->temp/10; + root = api_add_temp(root, "Temp", &temp, true); + temp = (float)info->max_temp/10; + root = api_add_temp(root, "Temp Max", &temp, true); + } + + return root; +} + +static void drillbit_reinit(struct cgpu_info *drillbit) +{ + drillbit_close(drillbit); + drillbit_open(drillbit); + drillbit_reset(drillbit); +} + +static void drillbit_shutdown(struct thr_info *thr) +{ + struct cgpu_info *drillbit = thr->cgpu; + + drillbit_close(drillbit); +} + +/* Currently hardcoded to BF1 devices */ +struct device_drv drillbit_drv = { + .drv_id = DRIVER_drillbit, + .dname = "Drillbit", + .name = "DRB", + .drv_detect = drillbit_detect, + .hash_work = &hash_driver_work, + .scanwork = drillbit_scanwork, + .get_api_stats = drillbit_api_stats, + .get_statline_before = drillbit_get_statline_before, + .reinit_device = drillbit_reinit, + .thread_shutdown = drillbit_shutdown, + .identify_device = drillbit_identify, +}; + + +/* Structure serialisation/deserialisation */ + +#define SERIALISE(FIELD) do { \ + memcpy(&buf[offset], &FIELD, sizeof(FIELD)); \ + offset += sizeof(FIELD); \ + } while (0) + +#define DESERIALISE(FIELD) do { \ + memcpy(&FIELD, &buf[offset], sizeof(FIELD)); \ + offset += sizeof(FIELD); \ + } while (0) + +static void serialise_work_request(char *buf, uint16_t chip_id, const struct work *work) +{ + size_t offset = 0; + SERIALISE(chip_id); + memcpy(&buf[offset], work->midstate, 32); + offset += 32; + memcpy(&buf[offset], work->data + 64, 12); + //offset += 12; +} + +static void deserialise_work_result(WorkResult *wr, const char *buf) +{ + int i; + size_t offset = 0; + DESERIALISE(wr->chip_id); + DESERIALISE(wr->num_nonces); + DESERIALISE(wr->is_idle); + for (i = 0; i < MAX_RESULTS; i++) + DESERIALISE(wr->nonce[i]); +} + +static void serialise_board_configV3(char *buf, BoardConfigV3 *bc) +{ + size_t offset = 0; + SERIALISE(bc->core_voltage); + SERIALISE(bc->int_clock_level); + SERIALISE(bc->clock_div2); + SERIALISE(bc->use_ext_clock); + SERIALISE(bc->ext_clock_freq); +} + +static void serialise_board_configV4(char *buf, BoardConfig *bc) +{ + size_t offset = 0; + SERIALISE(bc->core_voltage); + SERIALISE(bc->clock_freq); + SERIALISE(bc->clock_div2); + SERIALISE(bc->use_ext_clock); +} + +static void serialise_autotune_request(char *buf, AutoTuneRequest *aq) +{ + size_t offset = 0; + SERIALISE(aq->chip_id); + SERIALISE(aq->increase_clock); +} + +static void deserialise_identity(Identity *id, const char *buf) +{ + size_t offset = 0; + DESERIALISE(id->protocol_version); + DESERIALISE(id->product); + DESERIALISE(id->serial); + DESERIALISE(id->num_chips); + DESERIALISE(id->capabilities); +} diff --git a/driver-drillbit.h b/driver-drillbit.h new file mode 100644 index 0000000000..b38610763b --- /dev/null +++ b/driver-drillbit.h @@ -0,0 +1,56 @@ +/* + * Copyright 2013 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 BITFURY_H +#define BITFURY_H + +#include "miner.h" +#include "usbutils.h" + +#define WORK_HISTORY_LEN 4 + +struct drillbit_chip_info; + +/* drillbit_info structure applies to entire device */ +struct drillbit_info { + struct cgpu_info *base_cgpu; + uint8_t protocol_version; + uint8_t num_chips; + uint16_t capabilities; + char product[8]; + uint32_t serial; + struct drillbit_chip_info *chips; + struct timeval tv_lastchipinfo; + struct timeval tv_lasttemp; + uint16_t temp; + uint16_t max_temp; +}; + +enum drillbit_chip_state { + IDLE, /* Has no work */ + WORKING_NOQUEUED, /* Has current work but nothing queued as "next work" */ + WORKING_QUEUED /* Has current work and a piece of work queued for after that */ +}; + +struct drillbit_chip_info { + uint16_t chip_id; + struct work *current_work[WORK_HISTORY_LEN]; + enum drillbit_chip_state state; + struct timeval tv_start; + uint32_t success_count; + uint32_t error_count; + uint32_t timeout_count; + uint32_t work_sent_count; + uint32_t success_auto; + uint32_t error_auto; + int auto_delta; + int auto_max; +}; + +#endif /* BITFURY_H */ diff --git a/driver-hashfast.c b/driver-hashfast.c new file mode 100644 index 0000000000..4175adee42 --- /dev/null +++ b/driver-hashfast.c @@ -0,0 +1,2066 @@ +/* + * Copyright 2013-2014 Con Kolivas + * Copyright 2013 Hashfast Inc. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the Free + * Software Foundation; either version 3 of the License, or (at your option) + * any later version. See COPYING for more details. + */ + +#include "config.h" + +#include +#include + +#include "miner.h" +#include "usbutils.h" + +#include "driver-hashfast.h" + +int opt_hfa_ntime_roll = 1; +int opt_hfa_hash_clock = HFA_CLOCK_DEFAULT; +int opt_hfa_overheat = HFA_TEMP_OVERHEAT; +int opt_hfa_target = HFA_TEMP_TARGET; +bool opt_hfa_pll_bypass; +bool opt_hfa_dfu_boot; +int opt_hfa_fan_default = HFA_FAN_DEFAULT; +int opt_hfa_fan_max = HFA_FAN_MAX; +int opt_hfa_fan_min = HFA_FAN_MIN; +int opt_hfa_fail_drop = 10; +bool opt_hfa_noshed; + +char *opt_hfa_name; +char *opt_hfa_options; + +//////////////////////////////////////////////////////////////////////////////// +// Support for the CRC's used in header (CRC-8) and packet body (CRC-32) +//////////////////////////////////////////////////////////////////////////////// + +#define GP8 0x107 /* x^8 + x^2 + x + 1 */ +#define DI8 0x07 + +static bool hfa_crc8_set; + +char *set_hfa_fan(char *arg) +{ + int val1, val2, ret; + + ret = sscanf(arg, "%d-%d", &val1, &val2); + if (ret < 1) + return "No values passed to hfa-fan"; + if (ret == 1) + val2 = val1; + + if (val1 < 0 || val1 > 100 || val2 < 0 || val2 > 100 || val2 < val1) + return "Invalid value passed to hfa-fan"; + + opt_hfa_fan_min = val1; + opt_hfa_fan_max = val2; + if (opt_hfa_fan_min > opt_hfa_fan_default) + opt_hfa_fan_default = opt_hfa_fan_min; + if (opt_hfa_fan_max < opt_hfa_fan_default) + opt_hfa_fan_default = opt_hfa_fan_max; + + return NULL; +} + +static unsigned char crc8_table[256]; /* CRC-8 table */ + +static void hfa_init_crc8(void) +{ + int i,j; + unsigned char crc; + + hfa_crc8_set = true; + for (i = 0; i < 256; i++) { + crc = i; + for (j = 0; j < 8; j++) + crc = (crc << 1) ^ ((crc & 0x80) ? DI8 : 0); + crc8_table[i] = crc & 0xFF; + } +} + +static unsigned char hfa_crc8(unsigned char *h) +{ + int i; + unsigned char crc; + + h++; // Preamble not included + for (i = 1, crc = 0xff; i < 7; i++) + crc = crc8_table[crc ^ *h++]; + + return crc; +} + +struct hfa_cmd { + uint8_t cmd; + char *cmd_name; + enum usb_cmds usb_cmd; +}; + +/* Entries in this array need to align with the actual op values specified + * in hf_protocol.h */ +#define C_NULL C_MAX +static const struct hfa_cmd hfa_cmds[] = { + {OP_NULL, "OP_NULL", C_NULL}, // 0 + {OP_ROOT, "OP_ROOT", C_NULL}, + {OP_RESET, "OP_RESET", C_HF_RESET}, + {OP_PLL_CONFIG, "OP_PLL_CONFIG", C_HF_PLL_CONFIG}, + {OP_ADDRESS, "OP_ADDRESS", C_HF_ADDRESS}, + {OP_READDRESS, "OP_READDRESS", C_NULL}, + {OP_HIGHEST, "OP_HIGHEST", C_NULL}, + {OP_BAUD, "OP_BAUD", C_HF_BAUD}, + {OP_UNROOT, "OP_UNROOT", C_NULL}, // 8 + {OP_HASH, "OP_HASH", C_HF_HASH}, + {OP_NONCE, "OP_NONCE", C_HF_NONCE}, + {OP_ABORT, "OP_ABORT", C_HF_ABORT}, + {OP_STATUS, "OP_STATUS", C_HF_STATUS}, + {OP_GPIO, "OP_GPIO", C_NULL}, + {OP_CONFIG, "OP_CONFIG", C_HF_CONFIG}, + {OP_STATISTICS, "OP_STATISTICS", C_HF_STATISTICS}, + {OP_GROUP, "OP_GROUP", C_NULL}, // 16 + {OP_CLOCKGATE, "OP_CLOCKGATE", C_HF_CLOCKGATE}, + + {OP_USB_INIT, "OP_USB_INIT", C_HF_USB_INIT}, // 18 + {OP_GET_TRACE, "OP_GET_TRACE", C_NULL}, + {OP_LOOPBACK_USB, "OP_LOOPBACK_USB", C_NULL}, + {OP_LOOPBACK_UART, "OP_LOOPBACK_UART", C_NULL}, + {OP_DFU, "OP_DFU", C_HF_DFU}, + {OP_USB_SHUTDOWN, "OP_USB_SHUTDOWN", C_NULL}, + {OP_DIE_STATUS, "OP_DIE_STATUS", C_HF_DIE_STATUS}, // 24 + {OP_GWQ_STATUS, "OP_GWQ_STATUS", C_HF_GWQ_STATUS}, + {OP_WORK_RESTART, "OP_WORK_RESTART", C_HF_WORK_RESTART}, + {OP_USB_STATS1, "OP_USB_STATS1", C_NULL}, + {OP_USB_GWQSTATS, "OP_USB_GWQSTATS", C_HF_GWQSTATS}, + {OP_USB_NOTICE, "OP_USB_NOTICE", C_HF_NOTICE}, + {OP_PING, "OP_PING", C_HF_PING}, + {OP_CORE_MAP, "OP_CORE_MAP", C_NULL}, + {OP_VERSION, "OP_VERSION", C_NULL}, // 32 + {OP_FAN, "OP_FAN", C_HF_FAN}, + {OP_NAME, "OP_NAME", C_OP_NAME} +}; + +#define HF_USB_CMD_OFFSET (128 - 18) +#define HF_USB_CMD(X) (X - HF_USB_CMD_OFFSET) + +/* Send an arbitrary frame, consisting of an 8 byte header and an optional + * packet body. */ +static bool __hfa_send_frame(struct cgpu_info *hashfast, uint8_t opcode, int tx_length, + uint8_t *packet) +{ + struct hashfast_info *info = hashfast->device_data; + int ret, amount; + bool retried = false; + + if (unlikely(hashfast->usbinfo.nodev)) + return false; + + info->last_send = time(NULL); + applog(LOG_DEBUG, "%s %s: Sending %s frame", hashfast->drv->name, hashfast->unique_id, hfa_cmds[opcode].cmd_name); +retry: + ret = usb_write(hashfast, (char *)packet, tx_length, &amount, + hfa_cmds[opcode].usb_cmd); + if (unlikely(ret < 0 || amount != tx_length)) { + if (hashfast->usbinfo.nodev) + return false; + if (!retried) { + applog(LOG_ERR, "%s %s: hfa_send_frame: USB Send error, ret %d amount %d vs. tx_length %d, retrying", + hashfast->drv->name, hashfast->unique_id, ret, amount, tx_length); + retried = true; + goto retry; + } + applog(LOG_ERR, "%s %s: hfa_send_frame: USB Send error, ret %d amount %d vs. tx_length %d", + hashfast->drv->name, hashfast->unique_id, ret, amount, tx_length); + return false; + } + + if (retried) + applog(LOG_WARNING, "%s %s: hfa_send_frame: recovered OK", hashfast->drv->name, hashfast->unique_id); + + return true; +} + +static bool hfa_send_generic_frame(struct cgpu_info *hashfast, uint8_t opcode, uint8_t chip_address, + uint8_t core_address, uint16_t hdata, uint8_t *data, int len) +{ + uint8_t packet[256]; + struct hf_header *p = (struct hf_header *)packet; + int tx_length, ret, amount; + + p->preamble = HF_PREAMBLE; + p->operation_code = opcode; + p->chip_address = chip_address; + p->core_address = core_address; + p->hdata = htole16(hdata); + p->data_length = len / 4; + p->crc8 = hfa_crc8(packet); + + if (len) + memcpy(&packet[sizeof(struct hf_header)], data, len); + tx_length = sizeof(struct hf_header) + len; + + ret = usb_write(hashfast, (char *)packet, tx_length, &amount, C_NULL); + + return ((ret >= 0) && (amount == tx_length)); +} + +static bool hfa_send_frame(struct cgpu_info *hashfast, uint8_t opcode, uint16_t hdata, + uint8_t *data, int len) +{ + uint8_t packet[256]; + struct hf_header *p = (struct hf_header *)packet; + int tx_length; + + p->preamble = HF_PREAMBLE; + p->operation_code = hfa_cmds[opcode].cmd; + p->chip_address = HF_GWQ_ADDRESS; + p->core_address = 0; + p->hdata = htole16(hdata); + p->data_length = len / 4; + p->crc8 = hfa_crc8(packet); + + if (len) + memcpy(&packet[sizeof(struct hf_header)], data, len); + tx_length = sizeof(struct hf_header) + len; + + return (__hfa_send_frame(hashfast, opcode, tx_length, packet)); +} + +/* Send an already assembled packet, consisting of an 8 byte header which may + * or may not be followed by a packet body. */ + +static bool hfa_send_packet(struct cgpu_info *hashfast, struct hf_header *h, int cmd) +{ + int amount, ret, len; + + if (unlikely(hashfast->usbinfo.nodev)) + return false; + + len = sizeof(*h) + h->data_length * 4; + ret = usb_write(hashfast, (char *)h, len, &amount, hfa_cmds[cmd].usb_cmd); + if (ret < 0 || amount != len) { + applog(LOG_WARNING, "%s %s: send_packet: %s USB Send error, ret %d amount %d vs. length %d", + hashfast->drv->name, hashfast->unique_id, hfa_cmds[cmd].cmd_name, ret, amount, len); + return false; + } + return true; +} + +#define HFA_GET_HEADER_BUFSIZE 512 + +static bool hfa_get_header(struct cgpu_info *hashfast, struct hf_header *h, uint8_t *computed_crc) +{ + int amount, ret, orig_len, len, ofs = 0; + cgtimer_t ts_start; + char buf[HFA_GET_HEADER_BUFSIZE]; + char *header; + + if (unlikely(hashfast->usbinfo.nodev)) + return false; + + orig_len = len = sizeof(*h); + + /* Read for up to 500ms till we find the first occurrence of HF_PREAMBLE + * though it should be the first byte unless we get woefully out of + * sync. */ + cgtimer_time(&ts_start); + do { + cgtimer_t ts_now, ts_diff; + + cgtimer_time(&ts_now); + cgtimer_sub(&ts_now, &ts_start, &ts_diff); + if (cgtimer_to_ms(&ts_diff) > 500) + return false; + + if (unlikely(hashfast->usbinfo.nodev)) + return false; + if(ofs + len > HFA_GET_HEADER_BUFSIZE) { + // Not expected to happen. + applog(LOG_WARNING, "hfa_get_header() tried to overflow buf[]."); + return false; + } + ret = usb_read(hashfast, buf + ofs, len, &amount, C_HF_GETHEADER); + + if (unlikely(ret && ret != LIBUSB_ERROR_TIMEOUT)) + return false; + ofs += amount; + header = memchr(buf, HF_PREAMBLE, ofs); + if (header) { + /* Toss any leading data we can't use */ + if (header != buf) { + memmove(buf, header, ofs); + ofs -= header - buf; + } + len -= ofs; + } + else { + /* HF_PREAMBLE not found, toss all the useless leading data. */ + ofs = 0; + len = sizeof(*h); + } + } while (len > 0); + + memcpy(h, header, orig_len); + *computed_crc = hfa_crc8((uint8_t *)h); + + return true; +} + +static bool hfa_get_data(struct cgpu_info *hashfast, char *buf, int len4) +{ + int amount, ret, len = len4 * 4; + + if (unlikely(hashfast->usbinfo.nodev)) + return false; + ret = usb_read(hashfast, buf, len, &amount, C_HF_GETDATA); + if (ret) + return false; + if (amount != len) { + applog(LOG_WARNING, "%s %s: get_data: Strange amount returned %d vs. expected %d", + hashfast->drv->name, hashfast->unique_id, amount, len); + return false; + } + return true; +} + +static const char *hf_usb_init_errors[] = { + "Success", + "Reset timeout", + "Address cycle timeout", + "Clockgate operation timeout", + "Configuration operation timeout", + "Excessive core failures", + "All cores failed diagnostics", + "Too many groups configured - increase ntime roll amount", + "Chaining connections detected but secondary board(s) did not respond", + "Secondary board communication error", + "Main board 12V power is bad", + "Secondary board(s) 12V power is bad", + "Main board FPGA programming error", + "Main board FPGA SPI read timeout", + "Main board FPGA Bad magic number", + "Main board FPGA SPI write timeout", + "Main board FPGA register read/write test failed", + "ASIC core power fault", + "Dynamic baud rate change timeout", + "Address failure", + "Regulator programming error", + "Address range inconsistent after mixed reconfiguration", + "Timeout after mixed reconfiguration" +}; + +static bool hfa_clear_readbuf(struct cgpu_info *hashfast); + +struct op_nameframe { + struct hf_header h; + char name[32]; +} __attribute__((packed)); + +static void hfa_write_opname(struct cgpu_info *hashfast, struct hashfast_info *info) +{ + const uint8_t opcode = HF_USB_CMD(OP_NAME); + struct op_nameframe nameframe; + struct hf_header *h = (struct hf_header *)&nameframe; + const int tx_length = sizeof(struct op_nameframe); + + memset(&nameframe, 0, sizeof(nameframe)); + strncpy(nameframe.name, info->op_name, 30); + h->preamble = HF_PREAMBLE; + h->operation_code = hfa_cmds[opcode].cmd; + h->core_address = 1; + h->data_length = 32 / 4; + h->crc8 = hfa_crc8((unsigned char *)h); + applog(LOG_DEBUG, "%s %d: Opname being set to %s", hashfast->drv->name, + hashfast->device_id, info->op_name); + __hfa_send_frame(hashfast, opcode, tx_length, (uint8_t *)&nameframe); +} + +/* If no opname or an invalid opname is set, change it to the serial number if + * it exists, or a random name based on timestamp if not. */ +static void hfa_choose_opname(struct cgpu_info *hashfast, struct hashfast_info *info) +{ + uint64_t usecs; + + if (info->serial_number) + sprintf(info->op_name, "%08x", info->serial_number); + else { + struct timeval tv_now; + + cgtime(&tv_now); + usecs = (uint64_t)(tv_now.tv_sec) * (uint64_t)1000000 + (uint64_t)tv_now.tv_usec; + sprintf(info->op_name, "%lx", (long unsigned int)usecs); + } + hfa_write_opname(hashfast, info); +} + +// Generic setting header +struct hf_settings_data { + uint8_t revision; + uint8_t ref_frequency; + uint16_t magic; + uint16_t frequency0; + uint16_t voltage0; + uint16_t frequency1; + uint16_t voltage1; + uint16_t frequency2; + uint16_t voltage2; + uint16_t frequency3; + uint16_t voltage3; +} __attribute__((packed,aligned(4))); + +static bool hfa_set_voltages(struct cgpu_info *hashfast, struct hashfast_info *info) +{ + struct hf_settings_data op_settings_data; + + op_settings_data.revision = 1; + op_settings_data.ref_frequency = 25; + op_settings_data.magic = HFA_MAGIC_SETTINGS_VALUE; + + op_settings_data.frequency0 = info->hash_clock_rate; + op_settings_data.voltage0 = info->hash_voltage; + op_settings_data.frequency1 = info->hash_clock_rate; + op_settings_data.voltage1 = info->hash_voltage; + op_settings_data.frequency2 = info->hash_clock_rate; + op_settings_data.voltage2 = info->hash_voltage; + op_settings_data.frequency3 = info->hash_clock_rate; + op_settings_data.voltage3 = info->hash_voltage; + + hfa_send_generic_frame(hashfast, OP_SETTINGS, 0x00, 0x01, HFA_MAGIC_SETTINGS_VALUE, + (uint8_t *)&op_settings_data, sizeof(op_settings_data)); + // reset the board once to switch to new voltage settings + hfa_send_generic_frame(hashfast, OP_POWER, 0xff, 0x00, 0x1, NULL, 0); + hfa_send_generic_frame(hashfast, OP_POWER, 0xff, 0x00, 0x2, NULL, 0); + + return true; +} + +static bool hfa_send_shutdown(struct cgpu_info *hashfast); + +static bool hfa_reset(struct cgpu_info *hashfast, struct hashfast_info *info) +{ + struct hf_usb_init_header usb_init[2], *hu = usb_init; + struct hf_usb_init_base *db; + struct hf_usb_init_options *ho; + int retries = 0, i; + bool ret = false; + char buf[1024]; + struct hf_header *h = (struct hf_header *)buf; + uint8_t hcrc; + + /* Hash clock rate in Mhz. Set to opt_hfa_hash_clock if it has not + * been inherited across a restart. */ + if (!info->hash_clock_rate) + info->hash_clock_rate = opt_hfa_hash_clock; + info->group_ntime_roll = opt_hfa_ntime_roll; + info->core_ntime_roll = 1; + + // Assemble the USB_INIT request + memset(hu, 0, sizeof(*hu)); + hu->preamble = HF_PREAMBLE; + hu->operation_code = OP_USB_INIT; + hu->protocol = PROTOCOL_GLOBAL_WORK_QUEUE; // Protocol to use + if (!opt_hfa_noshed) + hu->shed_supported = true; + // Force PLL bypass + hu->pll_bypass = opt_hfa_pll_bypass; + hu->hash_clock = info->hash_clock_rate; // Hash clock rate in Mhz + if (info->group_ntime_roll > 1 && info->core_ntime_roll) { + ho = (struct hf_usb_init_options *)(hu + 1); + memset(ho, 0, sizeof(*ho)); + ho->group_ntime_roll = info->group_ntime_roll; + ho->core_ntime_roll = info->core_ntime_roll; + hu->data_length = sizeof(*ho) / 4; + } + hu->crc8 = hfa_crc8((uint8_t *)hu); + applog(LOG_INFO, "%s %s: Sending OP_USB_INIT with GWQ protocol specified", + hashfast->drv->name, hashfast->unique_id); +resend: + if (unlikely(hashfast->usbinfo.nodev)) + goto out; + + if (!hfa_clear_readbuf(hashfast)) + goto out; + + if (!hfa_send_packet(hashfast, (struct hf_header *)hu, HF_USB_CMD(OP_USB_INIT))) + goto out; + + // Check for the correct response. + // We extend the normal timeout - a complete device initialization, including + // bringing power supplies up from standby, etc., can take over a second. +tryagain: + for (i = 0; i < 10; i++) { + ret = hfa_get_header(hashfast, h, &hcrc); + if (unlikely(hashfast->usbinfo.nodev)) + goto out; + if (ret) + break; + } + if (!ret) { + if (retries++ < 3) + goto resend; + applog(LOG_WARNING, "%s %s: OP_USB_INIT failed!", hashfast->drv->name, hashfast->unique_id); + goto out; + } + if (h->crc8 != hcrc) { + applog(LOG_WARNING, "%s %s: OP_USB_INIT failed! CRC mismatch", hashfast->drv->name, hashfast->unique_id); + ret = false; + goto out; + } + if (h->operation_code != OP_USB_INIT) { + // This can happen if valid packet(s) were in transit *before* the OP_USB_INIT arrived + // at the device, so we just toss the packets and keep looking for the response. + applog(LOG_WARNING, "%s %s: OP_USB_INIT: Tossing packet, valid but unexpected type %d", + hashfast->drv->name, hashfast->unique_id, h->operation_code); + hfa_get_data(hashfast, buf, h->data_length); + if (retries++ < 3) + goto tryagain; + ret = false; + goto out; + } + + applog(LOG_DEBUG, "%s %s: Good reply to OP_USB_INIT", hashfast->drv->name, hashfast->unique_id); + applog(LOG_DEBUG, "%s %s: OP_USB_INIT: %d die in chain, %d cores, device_type %d, refclk %d Mhz", + hashfast->drv->name, hashfast->unique_id, h->chip_address, h->core_address, h->hdata & 0xff, (h->hdata >> 8) & 0xff); + + // Save device configuration + info->asic_count = h->chip_address; + info->core_count = h->core_address; + info->device_type = (uint8_t)h->hdata; + info->ref_frequency = (uint8_t)(h->hdata >> 8); + info->hash_sequence_head = 0; + info->hash_sequence_tail = 0; + info->device_sequence_tail = 0; + + if (info->asic_count == 12) + hashfast->drv->name = "HFS"; + else if (info->asic_count == 4) + hashfast->drv->name = "HFB"; + + // Size in bytes of the core bitmap in bytes + info->core_bitmap_size = (((info->asic_count * info->core_count) + 31) / 32) * 4; + + // Get the usb_init_base structure + if (!hfa_get_data(hashfast, (char *)&info->usb_init_base, U32SIZE(info->usb_init_base))) { + applog(LOG_WARNING, "%s %s: OP_USB_INIT failed! Failure to get usb_init_base data", + hashfast->drv->name, hashfast->unique_id); + ret = false; + goto out; + } + db = &info->usb_init_base; + info->firmware_version = ((db->firmware_rev >> 8) & 0xff) + (double)(db->firmware_rev & 0xff) / 10.0; + info->hardware_version = ((db->hardware_rev >> 8) & 0xff) + (double)(db->hardware_rev & 0xff) / 10.0; + applog(LOG_INFO, "%s %s: firmware_rev: %.1f", hashfast->drv->name, hashfast->unique_id, + info->firmware_version); + applog(LOG_INFO, "%s %s: hardware_rev: %.1f", hashfast->drv->name, hashfast->unique_id, + info->hardware_version); + applog(LOG_INFO, "%s %s: serial number: %08x", hashfast->drv->name, hashfast->unique_id, + db->serial_number); + applog(LOG_INFO, "%s %s: hash clockrate: %d Mhz", hashfast->drv->name, hashfast->unique_id, + db->hash_clockrate); + applog(LOG_INFO, "%s %s: inflight_target: %d", hashfast->drv->name, hashfast->unique_id, + db->inflight_target); + applog(LOG_INFO, "%s %s: sequence_modulus: %d", hashfast->drv->name, hashfast->unique_id, + db->sequence_modulus); + + // Now a copy of the config data used + if (!hfa_get_data(hashfast, (char *)&info->config_data, U32SIZE(info->config_data))) { + applog(LOG_WARNING, "%s %s: OP_USB_INIT failed! Failure to get config_data", + hashfast->drv->name, hashfast->unique_id); + ret = false; + goto out; + } + + // Now the core bitmap + info->core_bitmap = malloc(info->core_bitmap_size); + if (!info->core_bitmap) + quit(1, "Failed to malloc info core bitmap in hfa_reset"); + if (!hfa_get_data(hashfast, (char *)info->core_bitmap, info->core_bitmap_size / 4)) { + applog(LOG_WARNING, "%s %s: OP_USB_INIT failed! Failure to get core_bitmap", hashfast->drv->name, hashfast->unique_id); + ret = false; + goto out; + } + + // See if the initialization suceeded + if (db->operation_status) { + applog(LOG_ERR, "%s %s: OP_USB_INIT failed! Operation status %d (%s)", + hashfast->drv->name, hashfast->unique_id, db->operation_status, + (db->operation_status < sizeof(hf_usb_init_errors)/sizeof(hf_usb_init_errors[0])) ? + hf_usb_init_errors[db->operation_status] : "Unknown error code"); + ret = false; + switch (db->operation_status) { + case E_CORE_POWER_FAULT: + for (i = 0; i < 4; i++) { + if (((db->extra_status_1 >> i) & 0x11) == 0x1) { + applog(LOG_ERR, "%s %s: OP_USB_INIT: Quadrant %d (of 4) regulator failure", + hashfast->drv->name, hashfast->unique_id, i + 1); + } + } + break; + default: + break; + } + goto out; + } + + if (!db->hash_clockrate) { + applog(LOG_INFO, "%s %s: OP_USB_INIT failed! Clockrate reported as zero", + hashfast->drv->name, hashfast->unique_id); + ret = false; + goto out; + } + info->num_sequence = db->sequence_modulus; + info->serial_number = db->serial_number; + info->base_clock = db->hash_clockrate; + + ret = hfa_clear_readbuf(hashfast); +out: + if (!ret) { + hfa_send_shutdown(hashfast); + usb_nodev(hashfast); + } + return ret; +} + +static bool hfa_clear_readbuf(struct cgpu_info *hashfast) +{ + int amount, ret = 0; + char buf[512]; + + do { + if (hashfast->usbinfo.nodev) { + ret = LIBUSB_ERROR_NO_DEVICE; + break; + } + ret = usb_read(hashfast, buf, 512, &amount, C_HF_CLEAR_READ); + } while (!ret && amount); + + if (ret && ret != LIBUSB_ERROR_TIMEOUT) + return false; + return true; +} + +static bool hfa_send_shutdown(struct cgpu_info *hashfast) +{ + bool ret = false; + + if (hashfast->usbinfo.nodev) + return ret; + /* Send a restart before the shutdown frame to tell the device to + * discard any work it thinks is in flight for a cleaner restart. */ + if (!hfa_send_frame(hashfast, HF_USB_CMD(OP_WORK_RESTART), 0, (uint8_t *)NULL, 0)) + return ret; + if (hfa_send_frame(hashfast, HF_USB_CMD(OP_USB_SHUTDOWN), 0, NULL, 0)) { + /* Wait to allow device to properly shut down. */ + cgsleep_ms(1000); + ret = true; + } + return ret; +} + +static struct cgpu_info *hfa_old_device(struct cgpu_info *hashfast, struct hashfast_info *info) +{ + struct cgpu_info *cgpu, *found = NULL; + struct hashfast_info *cinfo = NULL; + int i; + + /* See if we can find a zombie instance of the same device */ + for (i = 0; i < mining_threads; i++) { + cgpu = mining_thr[i]->cgpu; + if (!cgpu) + continue; + if (cgpu == hashfast) + continue; + if (cgpu->drv->drv_id != DRIVER_hashfast) + continue; + if (!cgpu->usbinfo.nodev) + continue; + cinfo = cgpu->device_data; + if (!cinfo) + continue; + if (info->op_name[0] != '\0' && !strncmp(info->op_name, cinfo->op_name, 32)) { + found = cgpu; + break; + } + if (info->serial_number && info->serial_number == cinfo->serial_number) { + found = cgpu; + break; + } + } + return found; +} + +static void hfa_set_clock(struct cgpu_info *hashfast, struct hashfast_info *info) +{ + uint16_t hdata; + int i; + + hdata = (WR_CLOCK_VALUE << WR_COMMAND_SHIFT) | info->hash_clock_rate; + + hfa_send_frame(hashfast, HF_USB_CMD(OP_WORK_RESTART), hdata, (uint8_t *)NULL, 0); + /* We won't know what the real clock is in this case without a + * usb_init_base message so we have to assume it's what we asked. */ + info->base_clock = info->hash_clock_rate; + for (i = 0; i < info->asic_count; i++) + info->die_data[i].hash_clock = info->base_clock; +} + +/* Look for an op name match and apply any options to its first attempted + * init sequence. This function allows any arbitrary number of extra parameters + * to be added in the future. */ +static void hfa_check_options(struct hashfast_info *info) +{ + char *p, *options, *found = NULL, *marker; + int maxlen, option = 0; + + if (!opt_hfa_options) + return; + + if (!info->op_name) + return; + + maxlen = strlen(info->op_name); + + options = strdup(opt_hfa_options); + for (p = strtok(options, ","); p; p = strtok(NULL, ",")) { + int cmplen = strlen(p); + + if (maxlen < cmplen) + cmplen = maxlen; + if (cmplen < maxlen) + continue; + if (!strncmp(info->op_name, p, cmplen)) { + found = strdup(p); + break; + } + } + free(options); + if (!found) + return; + + for (p = strtok(found, ":"); p; p = strtok(NULL, ":")) { + long lval; + + /* Parse each option in order, leaving room to add more */ + switch(option++) { + default: + break; + case 1: + lval = strtol(p, NULL, 10); + if (lval < HFA_CLOCK_MIN || lval > HFA_CLOCK_MAX) { + applog(LOG_ERR, "Invalid clock speed %ld set with hashfast option for %s", + lval, info->op_name); + break; + } + info->hash_clock_rate = lval; + marker = strchr(p,'@'); + if (marker != NULL) { + lval = strtol(marker+1, NULL, 10); + if (lval < HFA_VOLTAGE_MIN || lval > HFA_VOLTAGE_MAX) { + applog(LOG_ERR, "Invalid core voltage %ld set with hashfast option for %s", + lval, info->op_name); + break; + } + info->hash_voltage = lval; + } + break; + } + } + free(found); +} + +static bool hfa_detect_common(struct cgpu_info *hashfast) +{ + struct hashfast_info *info; + char buf[1024]; + struct hf_header *h = (struct hf_header *)buf; + uint8_t hcrc; + bool ret; + int i; + + info = calloc(sizeof(struct hashfast_info), 1); + if (!info) + quit(1, "Failed to calloc hashfast_info in hfa_detect_common"); + hashfast->device_data = info; + + /* Try sending and receiving an OP_NAME */ + ret = hfa_send_frame(hashfast, HF_USB_CMD(OP_NAME), 0, (uint8_t *)NULL, 0); + if (hashfast->usbinfo.nodev) { + ret = false; + goto out; + } + if (!ret) { + applog(LOG_WARNING, "%s %d: Failed to send OP_NAME!", hashfast->drv->name, + hashfast->device_id); + goto out; + } + ret = hfa_get_header(hashfast, h, &hcrc); + if (hashfast->usbinfo.nodev) { + ret = false; + goto out; + } + if (!ret) { + /* We should receive a valid header even if OP_NAME isn't + * supported by the firmware. */ + applog(LOG_NOTICE, "%s %d: No response to name query - failed init or firmware upgrade required.", + hashfast->drv->name, hashfast->device_id); + ret = true; + } else { + /* Only try to parse the name if the firmware supports OP_NAME */ + if (h->operation_code == OP_NAME) { + if (!hfa_get_data(hashfast, info->op_name, 32 / 4)) { + applog(LOG_WARNING, "%s %d: OP_NAME failed! Failure to get op_name data", + hashfast->drv->name, hashfast->device_id); + goto out; + } + info->has_opname = info->opname_valid = true; + applog(LOG_DEBUG, "%s: Returned an OP_NAME", hashfast->drv->name); + for (i = 0; i < 32; i++) { + if (i > 0 && info->op_name[i] == '\0') + break; + /* Make sure the op_name is valid ascii only */ + if (info->op_name[i] < 32 || info->op_name[i] > 126) { + info->opname_valid = false; + break; + } + } + } + } + + info->cgpu = hashfast; + /* Look for a matching zombie instance and inherit values from it if it + * exists. */ + if (info->has_opname && info->opname_valid) { + info->old_cgpu = hfa_old_device(hashfast, info); + if (info->old_cgpu) { + struct hashfast_info *cinfo = info->old_cgpu->device_data; + + applog(LOG_NOTICE, "%s: Found old instance by op name %s at device %d", + hashfast->drv->name, info->op_name, info->old_cgpu->device_id); + info->resets = ++cinfo->resets; + info->hash_clock_rate = cinfo->hash_clock_rate; + } else { + applog(LOG_NOTICE, "%s: Found device with name %s", hashfast->drv->name, + info->op_name); + hfa_check_options(info); + } + } + +out: + if (!ret) { + if (!hashfast->usbinfo.nodev) + hfa_clear_readbuf(hashfast); + hashfast->device_data = NULL; + free(info); + } + return ret; +} + +static bool hfa_initialise(struct cgpu_info *hashfast) +{ + int err = 7; + + if (hashfast->usbinfo.nodev) + return false; + + if (!hfa_clear_readbuf(hashfast)) + return false; +#ifdef WIN32 + err = usb_transfer(hashfast, 0, 9, 1, 0, C_ATMEL_RESET); + if (!err) + err = usb_transfer(hashfast, 0x21, 0x22, 0, 0, C_ATMEL_OPEN); + if (!err) { + uint32_t buf[2]; + + /* Magic sequence to reset device only really needed for windows + * but harmless on linux. */ + buf[0] = 0x80250000; + buf[1] = 0x00000800; + err = usb_transfer_data(hashfast, 0x21, 0x20, 0x0000, 0, buf, + 7, C_ATMEL_INIT); + } + if (err < 0) { + applog(LOG_INFO, "%s %s: Failed to open with error %s", + hashfast->drv->name, hashfast->unique_id, libusb_error_name(err)); + } +#endif + /* Must have transmitted init sequence sized buffer */ + return (err == 7); +} + +static void hfa_dfu_boot(struct cgpu_info *hashfast) +{ + bool ret; + + if (unlikely(hashfast->usbinfo.nodev)) + return; + + ret = hfa_send_frame(hashfast, HF_USB_CMD(OP_DFU), 0, NULL, 0); + applog(LOG_WARNING, "%s %s: %03d:%03d DFU Boot %s", hashfast->drv->name, hashfast->unique_id, + hashfast->usbinfo.bus_number, hashfast->usbinfo.device_address, + ret ? "Succeeded" : "Failed"); +} + +static struct cgpu_info *hfa_detect_one(libusb_device *dev, struct usb_find_devices *found) +{ + struct cgpu_info *hashfast; + + hashfast = usb_alloc_cgpu(&hashfast_drv, HASHFAST_MINER_THREADS); + if (!hashfast) + quit(1, "Failed to usb_alloc_cgpu hashfast"); + hashfast->unique_id = ""; + + if (!usb_init(hashfast, dev, found)) { + hashfast = usb_free_cgpu(hashfast); + return NULL; + } + + hashfast->usbdev->usb_type = USB_TYPE_STD; + + if (!hfa_initialise(hashfast)) { + hashfast = usb_free_cgpu(hashfast); + return NULL; + } + if (opt_hfa_dfu_boot) { + hfa_dfu_boot(hashfast); + hashfast = usb_free_cgpu(hashfast); + opt_hfa_dfu_boot = false; + return NULL; + } + if (!hfa_detect_common(hashfast)) { + usb_uninit(hashfast); + hashfast = usb_free_cgpu(hashfast); + return NULL; + } + if (!add_cgpu(hashfast)) + return NULL; + + if (opt_hfa_name) { + struct hashfast_info *info = hashfast->device_data; + + strncpy(info->op_name, opt_hfa_name, 30); + applog(LOG_NOTICE, "%s %d %03d:%03d: Writing name %s", hashfast->drv->name, + hashfast->device_id, hashfast->usbinfo.bus_number, hashfast->usbinfo.device_address, + info->op_name); + hfa_write_opname(hashfast, info); + opt_hfa_name = NULL; + } + + return hashfast; +} + +static void hfa_detect(bool __maybe_unused hotplug) +{ + /* Set up the CRC tables only once. */ + if (!hfa_crc8_set) + hfa_init_crc8(); + usb_detect(&hashfast_drv, hfa_detect_one); +} + +static bool hfa_get_packet(struct cgpu_info *hashfast, struct hf_header *h) +{ + uint8_t hcrc; + bool ret; + + if (unlikely(hashfast->usbinfo.nodev)) + return false; + + ret = hfa_get_header(hashfast, h, &hcrc); + if (unlikely(!ret)) + goto out; + if (unlikely(h->crc8 != hcrc)) { + applog(LOG_WARNING, "%s %s: Bad CRC %d vs %d, discarding packet", + hashfast->drv->name, hashfast->unique_id, h->crc8, hcrc); + ret = false; + goto out; + } + if (h->data_length > 0) + ret = hfa_get_data(hashfast, (char *)(h + 1), h->data_length); + if (unlikely(!ret)) { + applog(LOG_WARNING, "%s %s: Failed to get data associated with header", + hashfast->drv->name, hashfast->unique_id); + } + +out: + return ret; +} + +static void hfa_running_shutdown(struct cgpu_info *hashfast, struct hashfast_info *info); + +static void hfa_parse_gwq_status(struct cgpu_info *hashfast, struct hashfast_info *info, + struct hf_header *h) +{ + struct hf_gwq_data *g = (struct hf_gwq_data *)(h + 1); + struct work *work; + + applog(LOG_DEBUG, "%s %s: OP_GWQ_STATUS, device_head %4d tail %4d my tail %4d shed %3d inflight %4d", + hashfast->drv->name, hashfast->unique_id, g->sequence_head, g->sequence_tail, info->hash_sequence_tail, + g->shed_count, HF_SEQUENCE_DISTANCE(info->hash_sequence_head,g->sequence_tail)); + + /* This is a special flag that the thermal overload has been tripped */ + if (unlikely(h->core_address & 0x80)) { + applog(LOG_ERR, "%s %s: Thermal overload tripped! Shutting down device", + hashfast->drv->name, hashfast->unique_id); + hfa_running_shutdown(hashfast, info); + usb_nodev(hashfast); + return; + } + + mutex_lock(&info->lock); + info->raw_hashes += g->hash_count; + info->device_sequence_head = g->sequence_head; + info->device_sequence_tail = g->sequence_tail; + info->shed_count = g->shed_count; + /* Free any work that is no longer required */ + while (info->device_sequence_tail != info->hash_sequence_tail) { + if (++info->hash_sequence_tail >= info->num_sequence) + info->hash_sequence_tail = 0; + if (unlikely(!(work = info->works[info->hash_sequence_tail]))) { + applog(LOG_ERR, "%s %s: Bad work sequence tail %d head %d devhead %d devtail %d sequence %d", + hashfast->drv->name, hashfast->unique_id, info->hash_sequence_tail, + info->hash_sequence_head, info->device_sequence_head, + info->device_sequence_tail, info->num_sequence); + hashfast->shutdown = true; + usb_nodev(hashfast); + break; + } + applog(LOG_DEBUG, "%s %s: Completing work on hash_sequence_tail %d", + hashfast->drv->name, hashfast->unique_id, info->hash_sequence_tail); + free_work(work); + info->works[info->hash_sequence_tail] = NULL; + } + mutex_unlock(&info->lock); +} + +/* Board temperature conversion */ +static float board_temperature(uint16_t adc) +{ + float t, r, f, b; + + if (adc < 40 || adc > 650) + return((float) 0.0); // Bad count + + b = 3590.0; + f = (float)adc / 1023.0; + r = 1.0 / (1.0 / f - 1.0); + t = log(r) / b; + t += 1.0 / (25.0 + 273.15); + t = 1.0 / t - 273.15; + + return t; +} + +static void hfa_update_die_status(struct cgpu_info *hashfast, struct hashfast_info *info, + struct hf_header *h) +{ + struct hf_g1_die_data *d = (struct hf_g1_die_data *)(h + 1), *ds; + int num_included = (h->data_length * 4) / sizeof(struct hf_g1_die_data); + int i, j, die = h->chip_address; + + float die_temperature, board_temp; + float core_voltage[6]; + + // Copy in the data. They're numbered sequentially from the starting point + ds = info->die_status + h->chip_address; + for (i = 0; i < num_included; i++) + memcpy(ds++, d++, sizeof(struct hf_g1_die_data)); + + for (i = 0, d = &info->die_status[h->chip_address]; i < num_included; i++, d++) { + die += i; + die_temperature = GN_DIE_TEMPERATURE(d->die.die_temperature); + /* Sanity checking */ + if (unlikely(die_temperature > 255)) + die_temperature = info->die_data[die].temp; + else + info->die_data[die].temp = die_temperature; + board_temp = board_temperature(d->temperature); + if (unlikely(board_temp > 255)) + board_temp = info->die_data[die].board_temp; + else + info->die_data[die].board_temp = board_temp; + for (j = 0; j < 6; j++) + core_voltage[j] = GN_CORE_VOLTAGE(d->die.core_voltage[j]); + + applog(LOG_DEBUG, "%s %s: die %2d: OP_DIE_STATUS Temps die %.1fC board %.1fC vdd's %.2f %.2f %.2f %.2f %.2f %.2f", + hashfast->drv->name, hashfast->unique_id, die, die_temperature, board_temp, + core_voltage[0], core_voltage[1], core_voltage[2], + core_voltage[3], core_voltage[4], core_voltage[5]); + // XXX Convert board phase currents, voltage, temperature + } + if (die == info->asic_count - 1) { + /* We have a full set of die temperatures, find the highest + * current temperature. */ + float max_temp = 0; + + info->temp_updates++; + + for (die = 0; die < info->asic_count; die++) { + if (info->die_data[die].temp > max_temp) + max_temp = info->die_data[die].temp; + if (info->die_data[die].board_temp > max_temp) + max_temp = info->die_data[die].board_temp; + } + /* Exponentially change the max_temp to smooth out troughs. */ + hashfast->temp = hashfast->temp * 0.63 + max_temp * 0.37; + } + + if (unlikely(hashfast->temp >= opt_hfa_overheat)) { + /* -1 means new overheat condition */ + if (!info->overheat) + info->overheat = -1; + } else if (unlikely(info->overheat && hashfast->temp < opt_hfa_overheat - HFA_TEMP_HYSTERESIS)) + info->overheat = 0; +} + +static void hfa_parse_nonce(struct thr_info *thr, struct cgpu_info *hashfast, + struct hashfast_info *info, struct hf_header *h) +{ + struct hf_candidate_nonce *n = (struct hf_candidate_nonce *)(h + 1); + int i, num_nonces = h->data_length / U32SIZE(sizeof(struct hf_candidate_nonce)); + + applog(LOG_DEBUG, "%s %s: OP_NONCE: %2d/%2d:, num_nonces %d hdata 0x%04x", + hashfast->drv->name, hashfast->unique_id, h->chip_address, h->core_address, num_nonces, h->hdata); + for (i = 0; i < num_nonces; i++, n++) { + struct work *work = NULL; + + applog(LOG_DEBUG, "%s %s: OP_NONCE: %2d: %2d: ntime %2d sequence %4d nonce 0x%08x", + hashfast->drv->name, hashfast->unique_id, h->chip_address, i, n->ntime & HF_NTIME_MASK, n->sequence, n->nonce); + + if (n->sequence < info->usb_init_base.sequence_modulus) { + // Find the job from the sequence number + mutex_lock(&info->lock); + work = info->works[n->sequence]; + mutex_unlock(&info->lock); + } else { + applog(LOG_INFO, "%s %s: OP_NONCE: Sequence out of range %4d max %4d", + hashfast->drv->name, hashfast->unique_id, n->sequence, info->usb_init_base.sequence_modulus); + } + + if (unlikely(!work)) { + info->no_matching_work++; + applog(LOG_INFO, "%s %s: No matching work!", hashfast->drv->name, hashfast->unique_id); + } else { + applog(LOG_DEBUG, "%s %s: OP_NONCE: sequence %d: submitting nonce 0x%08x ntime %d", + hashfast->drv->name, hashfast->unique_id, n->sequence, n->nonce, n->ntime & HF_NTIME_MASK); + if (submit_noffset_nonce(thr, work, n->nonce, n->ntime & HF_NTIME_MASK)) { + mutex_lock(&info->lock); + info->hash_count += 0xffffffffull * work->device_diff; + mutex_unlock(&info->lock); + } +#if 0 /* Not used */ + if (unlikely(n->ntime & HF_NONCE_SEARCH)) { + /* This tells us there is another share in the + * next 128 nonces */ + applog(LOG_DEBUG, "%s %s: OP_NONCE: SEARCH PROXIMITY EVENT FOUND", + hashfast->drv->name, hashfast->unique_id); + } +#endif + } + } +} + +static void hfa_update_die_statistics(struct hashfast_info *info, struct hf_header *h) +{ + struct hf_statistics *s = (struct hf_statistics *)(h + 1); + struct hf_long_statistics *l; + + // Accumulate the data + l = info->die_statistics + h->chip_address; + + l->rx_header_crc += s->rx_header_crc; + l->rx_body_crc += s->rx_body_crc; + l->rx_header_timeouts += s->rx_header_timeouts; + l->rx_body_timeouts += s->rx_body_timeouts; + l->core_nonce_fifo_full += s->core_nonce_fifo_full; + l->array_nonce_fifo_full += s->array_nonce_fifo_full; + l->stats_overrun += s->stats_overrun; +} + +static void hfa_update_stats1(struct cgpu_info *hashfast, struct hashfast_info *info, + struct hf_header *h) +{ + struct hf_long_usb_stats1 *s1 = &info->stats1; + struct hf_usb_stats1 *sd = (struct hf_usb_stats1 *)(h + 1); + + s1->usb_rx_preambles += sd->usb_rx_preambles; + s1->usb_rx_receive_byte_errors += sd->usb_rx_receive_byte_errors; + s1->usb_rx_bad_hcrc += sd->usb_rx_bad_hcrc; + + s1->usb_tx_attempts += sd->usb_tx_attempts; + s1->usb_tx_packets += sd->usb_tx_packets; + s1->usb_tx_timeouts += sd->usb_tx_timeouts; + s1->usb_tx_incompletes += sd->usb_tx_incompletes; + s1->usb_tx_endpointstalled += sd->usb_tx_endpointstalled; + s1->usb_tx_disconnected += sd->usb_tx_disconnected; + s1->usb_tx_suspended += sd->usb_tx_suspended; +#if 0 + /* We don't care about UART stats so they're not in our struct */ + s1->uart_tx_queue_dma += sd->uart_tx_queue_dma; + s1->uart_tx_interrupts += sd->uart_tx_interrupts; + + s1->uart_rx_preamble_ints += sd->uart_rx_preamble_ints; + s1->uart_rx_missed_preamble_ints += sd->uart_rx_missed_preamble_ints; + s1->uart_rx_header_done += sd->uart_rx_header_done; + s1->uart_rx_data_done += sd->uart_rx_data_done; + s1->uart_rx_bad_hcrc += sd->uart_rx_bad_hcrc; + s1->uart_rx_bad_dma += sd->uart_rx_bad_dma; + s1->uart_rx_short_dma += sd->uart_rx_short_dma; + s1->uart_rx_buffers_full += sd->uart_rx_buffers_full; +#endif + if (sd->max_tx_buffers > s1->max_tx_buffers) + s1->max_tx_buffers = sd->max_tx_buffers; + if (sd->max_rx_buffers > s1->max_rx_buffers) + s1->max_rx_buffers = sd->max_rx_buffers; + + applog(LOG_DEBUG, "%s %s: OP_USB_STATS1:", hashfast->drv->name, hashfast->unique_id); + applog(LOG_DEBUG, " usb_rx_preambles: %6d", sd->usb_rx_preambles); + applog(LOG_DEBUG, " usb_rx_receive_byte_errors: %6d", sd->usb_rx_receive_byte_errors); + applog(LOG_DEBUG, " usb_rx_bad_hcrc: %6d", sd->usb_rx_bad_hcrc); + + applog(LOG_DEBUG, " usb_tx_attempts: %6d", sd->usb_tx_attempts); + applog(LOG_DEBUG, " usb_tx_packets: %6d", sd->usb_tx_packets); + applog(LOG_DEBUG, " usb_tx_timeouts: %6d", sd->usb_tx_timeouts); + applog(LOG_DEBUG, " usb_tx_incompletes: %6d", sd->usb_tx_incompletes); + applog(LOG_DEBUG, " usb_tx_endpointstalled: %6d", sd->usb_tx_endpointstalled); + applog(LOG_DEBUG, " usb_tx_disconnected: %6d", sd->usb_tx_disconnected); + applog(LOG_DEBUG, " usb_tx_suspended: %6d", sd->usb_tx_suspended); +#if 0 + applog(LOG_DEBUG, " uart_tx_queue_dma: %6d", sd->uart_tx_queue_dma); + applog(LOG_DEBUG, " uart_tx_interrupts: %6d", sd->uart_tx_interrupts); + + applog(LOG_DEBUG, " uart_rx_preamble_ints: %6d", sd->uart_rx_preamble_ints); + applog(LOG_DEBUG, " uart_rx_missed_preamble_ints: %6d", sd->uart_rx_missed_preamble_ints); + applog(LOG_DEBUG, " uart_rx_header_done: %6d", sd->uart_rx_header_done); + applog(LOG_DEBUG, " uart_rx_data_done: %6d", sd->uart_rx_data_done); + applog(LOG_DEBUG, " uart_rx_bad_hcrc: %6d", sd->uart_rx_bad_hcrc); + applog(LOG_DEBUG, " uart_rx_bad_dma: %6d", sd->uart_rx_bad_dma); + applog(LOG_DEBUG, " uart_rx_short_dma: %6d", sd->uart_rx_short_dma); + applog(LOG_DEBUG, " uart_rx_buffers_full: %6d", sd->uart_rx_buffers_full); +#endif + applog(LOG_DEBUG, " max_tx_buffers: %6d", sd->max_tx_buffers); + applog(LOG_DEBUG, " max_rx_buffers: %6d", sd->max_rx_buffers); +} + +static void hfa_parse_notice(struct cgpu_info *hashfast, struct hf_header *h) +{ + struct hf_usb_notice_data *d; + + if (h->data_length == 0) { + applog(LOG_DEBUG, "%s %s: Received OP_USB_NOTICE with zero data length", + hashfast->drv->name, hashfast->unique_id); + return; + } + d = (struct hf_usb_notice_data *)(h + 1); + /* FIXME Do something with the notification code d->extra_data here */ + applog(LOG_NOTICE, "%s %s NOTICE: %s", hashfast->drv->name, hashfast->unique_id, d->message); +} + +static void hfa_parse_settings(struct cgpu_info *hashfast, struct hf_header *h) +{ + struct hashfast_info *info = hashfast->device_data; + struct hf_settings_data *op_settings_data = (struct hf_settings_data *)(h + 1); + + // Check if packet size, revision and magic are matching + if ((h->data_length * 4 == sizeof(struct hf_settings_data)) && + (h->core_address == 0) && + (op_settings_data->revision == 1) && + (op_settings_data->magic == HFA_MAGIC_SETTINGS_VALUE)) + { + applog(LOG_NOTICE, "%s: Device settings (%dMHz@%dmV,%dMHz@%dmV,%dMHz@%dmV,%dMHz@%dmV)", hashfast->drv->name, + op_settings_data->frequency0, op_settings_data->voltage0, + op_settings_data->frequency1, op_settings_data->voltage1, + op_settings_data->frequency2, op_settings_data->voltage2, + op_settings_data->frequency3, op_settings_data->voltage3); + // Set voltage only when current voltage values are different + if ((info->hash_voltage != 0) && + ((op_settings_data->voltage0 != info->hash_voltage) || + (op_settings_data->voltage1 != info->hash_voltage) || + (op_settings_data->voltage2 != info->hash_voltage) || + (op_settings_data->voltage3 != info->hash_voltage))) { + applog(LOG_NOTICE, "%s: Setting default clock and voltage to %dMHz@%dmV", + hashfast->drv->name, info->hash_clock_rate, info->hash_voltage); + hfa_set_voltages(hashfast, info); + } + } +} + +static void *hfa_read(void *arg) +{ + struct thr_info *thr = (struct thr_info *)arg; + struct cgpu_info *hashfast = thr->cgpu; + struct hashfast_info *info = hashfast->device_data; + char threadname[16]; + + snprintf(threadname, sizeof(threadname), "%d/%sRead", hashfast->device_id, hashfast->drv->name); + RenameThread(threadname); + + while (likely(!hashfast->shutdown)) { + char buf[512]; + struct hf_header *h = (struct hf_header *)buf; + bool ret; + + mutex_lock(&info->rlock); + ret = hfa_get_packet(hashfast, h); + mutex_unlock(&info->rlock); + + if (unlikely(hashfast->usbinfo.nodev)) + break; + + if (unlikely(!ret)) + continue; + + switch (h->operation_code) { + case OP_GWQ_STATUS: + hfa_parse_gwq_status(hashfast, info, h); + break; + case OP_DIE_STATUS: + hfa_update_die_status(hashfast, info, h); + break; + case OP_NONCE: + hfa_parse_nonce(thr, hashfast, info, h); + break; + case OP_STATISTICS: + hfa_update_die_statistics(info, h); + break; + case OP_USB_STATS1: + hfa_update_stats1(hashfast, info, h); + break; + case OP_USB_NOTICE: + hfa_parse_notice(hashfast, h); + break; + case OP_SETTINGS: + hfa_parse_settings(hashfast, h); + break; + case OP_POWER: + case OP_PING: + /* Do nothing */ + break; + default: + if (h->operation_code == OP_FAN) { + applog(LOG_NOTICE, "%s %s: Firmware upgrade required to support fan control", + hashfast->drv->name, hashfast->unique_id); + opt_hfa_target = 0; + break; + } + applog(LOG_WARNING, "%s %s: Unhandled operation code %d", + hashfast->drv->name, hashfast->unique_id, h->operation_code); + break; + } + /* Make sure we send something to the device at least every 5 + * seconds so it knows the driver is still alive for when we + * run out of work. The read thread never blocks so is the + * best place to do this. */ + if (time(NULL) - info->last_send > 5) + hfa_send_frame(hashfast, HF_USB_CMD(OP_PING), 0, NULL, 0); + } + applog(LOG_DEBUG, "%s %s: Shutting down read thread", hashfast->drv->name, hashfast->unique_id); + + return NULL; +} + +static void hfa_set_fanspeed(struct cgpu_info *hashfast, struct hashfast_info *info, + int fanspeed); + +static bool hfa_init(struct thr_info *thr) +{ + struct cgpu_info *hashfast = thr->cgpu; + struct hashfast_info *info = hashfast->device_data; + struct timeval now; + bool ret; + int i; + + if (hashfast->usbinfo.nodev) + return false; + + /* hashfast_reset should fill in details for info */ + ret = hfa_reset(hashfast, info); + + // The per-die status array + info->die_status = calloc(info->asic_count, sizeof(struct hf_g1_die_data)); + if (unlikely(!(info->die_status))) + quit(1, "Failed to calloc die_status"); + + info->die_data = calloc(info->asic_count, sizeof(struct hf_die_data)); + if (unlikely(!(info->die_data))) + quit(1, "Failed to calloc die_data"); + for (i = 0; i < info->asic_count; i++) + info->die_data[i].hash_clock = info->base_clock; + + // The per-die statistics array + info->die_statistics = calloc(info->asic_count, sizeof(struct hf_long_statistics)); + if (unlikely(!(info->die_statistics))) + quit(1, "Failed to calloc die_statistics"); + + info->works = calloc(sizeof(struct work *), info->num_sequence); + if (!info->works) + quit(1, "Failed to calloc info works in hfa_detect_common"); + if (!ret) + goto out; + + /* We will have extracted the serial number by now */ + if (info->has_opname && !info->opname_valid) + hfa_choose_opname(hashfast, info); + + /* Use the opname as the displayed unique identifier */ + hashfast->unique_id = info->op_name; + + /* Inherit the old device id */ + if (info->old_cgpu) + hashfast->device_id = info->old_cgpu->device_id; + + /* If we haven't found a matching old instance, we might not have + * a valid op_name yet or lack support so try to match based on + * serial number. */ + if (!info->old_cgpu) + info->old_cgpu = hfa_old_device(hashfast, info); + + if (!info->has_opname && info->old_cgpu) { + struct hashfast_info *cinfo = info->old_cgpu->device_data; + + applog(LOG_NOTICE, "%s: Found old instance by serial number %08x at device %d", + hashfast->drv->name, info->serial_number, info->old_cgpu->device_id); + info->resets = ++cinfo->resets; + /* Set the device with the last hash_clock_rate if it's + * different. */ + if (info->hash_clock_rate != cinfo->hash_clock_rate) { + info->hash_clock_rate = cinfo->hash_clock_rate; + hfa_set_clock(hashfast, info); + } + } + + // Read current device settings if voltage was set in options + if (info->hash_voltage != 0) + hfa_send_generic_frame(hashfast, OP_SETTINGS, 0x00, 0x00, HFA_MAGIC_SETTINGS_VALUE, NULL, 0); + + mutex_init(&info->lock); + mutex_init(&info->rlock); + if (pthread_create(&info->read_thr, NULL, hfa_read, (void *)thr)) + quit(1, "Failed to pthread_create read thr in hfa_prepare"); + + cgtime(&now); + get_datestamp(hashfast->init, sizeof(hashfast->init), &now); + hashfast->last_device_valid_work = time(NULL); + hfa_set_fanspeed(hashfast, info, opt_hfa_fan_default); +out: + if (hashfast->usbinfo.nodev) + ret = false; + + if (!ret) { + hfa_clear_readbuf(hashfast); + free(info); + hashfast->device_data = NULL; + usb_nodev(hashfast); + } + + return ret; +} + +/* If this ever returns 0 it means we have shed all the cores which will lead + * to no work being done which will trigger the watchdog. */ +static inline int hfa_basejobs(struct hashfast_info *info) +{ + return info->usb_init_base.inflight_target - info->shed_count; +} + +/* Figure out how many jobs to send. */ +static int hfa_jobs(struct cgpu_info *hashfast, struct hashfast_info *info) +{ + int ret = 0; + + if (unlikely(info->overheat)) { + /* Acknowledge and notify of new condition.*/ + if (info->overheat < 0) { + applog(LOG_WARNING, "%s %s: Hit overheat temp %.1f, throttling!", + hashfast->drv->name, hashfast->unique_id, hashfast->temp); + /* Value of 1 means acknowledged overheat */ + info->overheat = 1; + } + goto out; + } + + mutex_lock(&info->lock); + ret = hfa_basejobs(info) - HF_SEQUENCE_DISTANCE(info->hash_sequence_head, info->device_sequence_tail); + /* Place an upper limit on how many jobs to queue to prevent sending + * more work than the device can use after a period of outage. */ + if (ret > hfa_basejobs(info)) + ret = hfa_basejobs(info); + mutex_unlock(&info->lock); + + if (unlikely(ret < 0)) + ret = 0; + +out: + return ret; +} + +static void hfa_set_fanspeed(struct cgpu_info *hashfast, struct hashfast_info *info, + int fandiff) +{ + const uint8_t opcode = HF_USB_CMD(OP_FAN); + uint8_t packet[256]; + struct hf_header *p = (struct hf_header *)packet; + const int tx_length = sizeof(struct hf_header); + uint16_t hdata; + int fandata; + + info->fanspeed += fandiff; + if (info->fanspeed > opt_hfa_fan_max) + info->fanspeed = opt_hfa_fan_max; + else if (info->fanspeed < opt_hfa_fan_min) + info->fanspeed = opt_hfa_fan_min; + fandata = info->fanspeed * 255 / 100; // Fanspeed is in percent, hdata 0-255 + hdata = fandata; // Use an int first to avoid overflowing uint16_t + p->preamble = HF_PREAMBLE; + p->operation_code = hfa_cmds[opcode].cmd; + p->chip_address = 0xff; + p->core_address = 1; + p->hdata = htole16(hdata); + p->data_length = 0; + p->crc8 = hfa_crc8(packet); + + __hfa_send_frame(hashfast, opcode, tx_length, packet); +} + +static void hfa_increase_clock(struct cgpu_info *hashfast, struct hashfast_info *info, + int die) +{ + int i, high_clock = 0, low_clock = info->hash_clock_rate; + struct hf_die_data *hdd = &info->die_data[die]; + uint32_t diebit = 0x00000001ul << die; + uint16_t hdata, increase = 10; + + if (hdd->hash_clock + increase > info->hash_clock_rate) + increase = info->hash_clock_rate - hdd->hash_clock; + hdd->hash_clock += increase; + hdata = (WR_MHZ_INCREASE << 12) | increase; + if (info->clock_offset) { + for (i = 0; i < info->asic_count; i++) { + if (info->die_data[i].hash_clock > high_clock) + high_clock = info->die_data[i].hash_clock; + if (info->die_data[i].hash_clock < low_clock) + low_clock = info->die_data[i].hash_clock; + } + if (info->firmware_version < 0.5 && low_clock + HFA_CLOCK_MAXDIFF > high_clock) { + /* We can increase all clocks again */ + for (i = 0; i < info->asic_count; i++) { + if (i == die) /* We've already added to this die */ + continue; + info->die_data[i].hash_clock += increase; + } + applog(LOG_INFO, "%s %s: Die %d temp below range %.1f, increasing ALL dies by %d", + hashfast->drv->name, hashfast->unique_id, die, info->die_data[die].temp, increase); + hfa_send_frame(hashfast, HF_USB_CMD(OP_WORK_RESTART), hdata, (uint8_t *)NULL, 0); + info->clock_offset -= increase; + return; + } + } + applog(LOG_INFO, "%s %s: Die temp below range %.1f, increasing die %d clock to %d", + hashfast->drv->name, hashfast->unique_id, info->die_data[die].temp, die, hdd->hash_clock); + hfa_send_frame(hashfast, HF_USB_CMD(OP_WORK_RESTART), hdata, (uint8_t *)&diebit, 4); +} + +static void hfa_decrease_clock(struct cgpu_info *hashfast, struct hashfast_info *info, + int die) +{ + struct hf_die_data *hdd = &info->die_data[die]; + uint32_t diebit = 0x00000001ul << die; + uint16_t hdata, decrease = 20; + int i, high_clock = 0; + + /* Find the fastest die for comparison */ + for (i = 0; i < info->asic_count; i++) { + if (info->die_data[i].hash_clock > high_clock) + high_clock = info->die_data[i].hash_clock; + } + if (hdd->hash_clock - decrease < HFA_CLOCK_MIN) + decrease = hdd->hash_clock - HFA_CLOCK_MIN; + hdata = (WR_MHZ_DECREASE << 12) | decrease; + if (info->firmware_version < 0.5 && high_clock >= hdd->hash_clock + HFA_CLOCK_MAXDIFF) { + /* We can't have huge differences in clocks as it will lead to + * starvation of the faster cores so we have no choice but to + * slow down all dies to tame this one. */ + for (i = 0; i < info->asic_count; i++) + info->die_data[i].hash_clock -= decrease; + applog(LOG_INFO, "%s %s: Die %d temp above range %.1f, decreasing ALL die clocks by %d", + hashfast->drv->name, hashfast->unique_id, die, info->die_data[die].temp, decrease); + hfa_send_frame(hashfast, HF_USB_CMD(OP_WORK_RESTART), hdata, (uint8_t *)NULL, 0); + info->clock_offset += decrease; + return; + + } + hdd->hash_clock -= decrease; + applog(LOG_INFO, "%s %s: Die temp above range %.1f, decreasing die %d clock to %d", + hashfast->drv->name, hashfast->unique_id, info->die_data[die].temp, die, hdd->hash_clock); + hfa_send_frame(hashfast, HF_USB_CMD(OP_WORK_RESTART), hdata, (uint8_t *)&diebit, 4); +} + +/* Adjust clock according to temperature if need be by changing the clock + * setting and issuing a work restart with the new clock speed. */ +static void hfa_temp_clock(struct cgpu_info *hashfast, struct hashfast_info *info) +{ + int temp_change, i, low_clock; + time_t now_t = time(NULL); + bool throttled = false; + + if (!opt_hfa_target) + return; + + /* First find out if any dies are throttled before trying to optimise + * fanspeed, and find the slowest clock. */ + low_clock = info->hash_clock_rate; + for (i = 0; i < info->asic_count ; i++) { + struct hf_die_data *hdd = &info->die_data[i]; + + if (hdd->hash_clock < info->hash_clock_rate) + throttled = true; + if (hdd->hash_clock < low_clock) + low_clock = hdd->hash_clock; + } + + /* Find the direction of temperature change since we last checked */ + if (info->temp_updates < 5) + goto dies_only; + info->temp_updates = 0; + temp_change = hashfast->temp - info->last_max_temp; + info->last_max_temp = hashfast->temp; + + /* Adjust fanspeeds first if possible before die speeds, increasing + * speed quickly and lowering speed slowly */ + if (hashfast->temp > opt_hfa_target || + (throttled && hashfast->temp >= opt_hfa_target - HFA_TEMP_HYSTERESIS)) { + /* We should be trying to decrease temperature, if it's not on + * its way down. */ + if (info->fanspeed < opt_hfa_fan_max) { + if (!temp_change) + hfa_set_fanspeed(hashfast, info, 5); + else if (temp_change > 0) + hfa_set_fanspeed(hashfast, info, 10); + } + } else if (hashfast->temp >= opt_hfa_target - HFA_TEMP_HYSTERESIS) { + /* In optimal range, try and maintain the same temp */ + if (temp_change > 0) { + /* Temp rising, tweak fanspeed up */ + if (info->fanspeed < opt_hfa_fan_max) + hfa_set_fanspeed(hashfast, info, 2); + } else if (temp_change < 0) { + /* Temp falling, tweak fanspeed down */ + if (info->fanspeed > opt_hfa_fan_min) + hfa_set_fanspeed(hashfast, info, -1); + } + } else { + /* Below optimal range, try and increase temp */ + if (temp_change <= 0 && !throttled) { + if (info->fanspeed > opt_hfa_fan_min) + hfa_set_fanspeed(hashfast, info, -1); + } + } + +dies_only: + /* Do no restarts at all if there has been one less than 15 seconds + * ago */ + if (now_t - info->last_restart < 15) + return; + + for (i = 1; i <= info->asic_count ; i++) { + int die = (info->last_die_adjusted + i) % info->asic_count; + struct hf_die_data *hdd = &info->die_data[die]; + + /* Sanity check */ + if (unlikely(hdd->temp == 0.0 || hdd->temp > 255)) + continue; + + /* In target temperature */ + if (hdd->temp >= opt_hfa_target - HFA_TEMP_HYSTERESIS && hdd->temp <= opt_hfa_target) + continue; + + if (hdd->temp > opt_hfa_target) { + /* Temp above target range */ + + /* Already at min speed */ + if (hdd->hash_clock == HFA_CLOCK_MIN) + continue; + /* Have some leeway before throttling speed */ + if (hdd->temp < opt_hfa_target + HFA_TEMP_HYSTERESIS) + break; + hfa_decrease_clock(hashfast, info, die); + } else { + /* Temp below target range. Only send a restart to + * increase speed no more than every 60 seconds. */ + if (now_t - hdd->last_restart < 60) + continue; + + /* Already at max speed */ + if (hdd->hash_clock == info->hash_clock_rate) + continue; + /* Do not increase the clocks on any dies if we have + * a forced offset due to wild differences in clocks, + * unless this is the slowest one. */ + if (info->clock_offset && hdd->hash_clock > low_clock) + continue; + hfa_increase_clock(hashfast, info, die); + } + /* Keep track of the last die adjusted since we only adjust + * one at a time to ensure we end up iterating over all of + * them. */ + info->last_restart = hdd->last_restart = now_t; + info->last_die_adjusted = die; + break; + } +} + +static void hfa_running_shutdown(struct cgpu_info *hashfast, struct hashfast_info *info) +{ + int iruntime = cgpu_runtime(hashfast); + + /* If the device has already disapperaed, don't drop the clock in case + * it was just unplugged as opposed to a failure. */ + if (hashfast->usbinfo.nodev) + return; + + /* Only decrease the clock speed if the device has run at this speed + * for less than an hour before failing, otherwise the hashrate gains + * are worth the occasional restart which takes at most a minute. */ + if (iruntime < 3600 && info->hash_clock_rate > HFA_CLOCK_DEFAULT && opt_hfa_fail_drop) { + info->hash_clock_rate -= opt_hfa_fail_drop; + if (info->hash_clock_rate < HFA_CLOCK_DEFAULT) + info->hash_clock_rate = HFA_CLOCK_DEFAULT; + if (info->old_cgpu && info->old_cgpu->device_data) { + struct hashfast_info *cinfo = info->old_cgpu->device_data; + + /* Set the master device's clock speed if this is a copy */ + cinfo->hash_clock_rate = info->hash_clock_rate; + } + applog(LOG_WARNING, "%s %s: Decreasing clock speed to %d with reset", + hashfast->drv->name, hashfast->unique_id, info->hash_clock_rate); + } + + if (!hfa_send_shutdown(hashfast)) + return; + + if (hashfast->usbinfo.nodev) + return; + + mutex_lock(&info->rlock); + hfa_clear_readbuf(hashfast); + mutex_unlock(&info->rlock); + + usb_nodev(hashfast); +} + +static int64_t hfa_scanwork(struct thr_info *thr) +{ + struct cgpu_info *hashfast = thr->cgpu; + struct hashfast_info *info = hashfast->device_data; + struct work *base_work = NULL; + int jobs, ret, cycles = 0; + double fail_time; + int64_t hashes; + + if (unlikely(hashfast->usbinfo.nodev)) { + applog(LOG_WARNING, "%s %s: device disappeared, disabling", + hashfast->drv->name, hashfast->unique_id); + return -1; + } + + /* Base the fail time on no valid nonces for 25 full nonce ranges at + * the current expected hashrate. */ + fail_time = 25.0 * (double)hashfast->drv->max_diff * 0xffffffffull / + (double)(info->base_clock * 1000000) / hfa_basejobs(info); + if (unlikely(share_work_tdiff(hashfast) > fail_time)) { + applog(LOG_WARNING, "%s %s: No valid hashes for over %.0f seconds, shutting down thread", + hashfast->drv->name, hashfast->unique_id, fail_time); + hfa_running_shutdown(hashfast, info); + return -1; + } + + if (unlikely(thr->work_restart)) { +restart: + info->last_restart = time(NULL); + thr->work_restart = false; + ret = hfa_send_frame(hashfast, HF_USB_CMD(OP_WORK_RESTART), 0, (uint8_t *)NULL, 0); + if (unlikely(!ret)) { + hfa_running_shutdown(hashfast, info); + return -1; + } + /* Give a full allotment of jobs after a restart, not waiting + * for the status update telling us how much to give. */ + jobs = hfa_basejobs(info); + } else { + /* Only adjust die clocks if there's no restart since two + * restarts back to back get ignored. */ + hfa_temp_clock(hashfast, info); + jobs = hfa_jobs(hashfast, info); + } + + /* Wait on restart_wait for up to 0.5 seconds or submit jobs as soon as + * they're required. */ + while (!jobs && ++cycles < 5) { + ret = restart_wait(thr, 100); + if (unlikely(!ret)) + goto restart; + jobs = hfa_jobs(hashfast, info); + } + + if (jobs) { + applog(LOG_DEBUG, "%s %s: Sending %d new jobs", hashfast->drv->name, hashfast->unique_id, + jobs); + } + + while (jobs-- > 0) { + struct hf_hash_usb op_hash_data; + struct work *work; + uint64_t intdiff; + int i, sequence; + uint32_t *p; + + /* This is a blocking function if there's no work */ + if (!base_work) + base_work = get_work(thr, thr->id); + + /* HFA hardware actually had ntime rolling disabled so we + * can roll the work ourselves here to minimise the amount of + * work we need to generate. */ + if (base_work->drv_rolllimit > jobs) { + base_work->drv_rolllimit--; + roll_work(base_work); + work = make_clone(base_work); + } else { + work = base_work; + base_work = NULL; + } + + /* Assemble the data frame and send the OP_HASH packet */ + memcpy(op_hash_data.midstate, work->midstate, sizeof(op_hash_data.midstate)); + memcpy(op_hash_data.merkle_residual, work->data + 64, 4); + p = (uint32_t *)(work->data + 64 + 4); + op_hash_data.timestamp = *p++; + op_hash_data.bits = *p++; + op_hash_data.starting_nonce = 0; + op_hash_data.nonce_loops = 0; + op_hash_data.ntime_loops = 0; + + /* Set the number of leading zeroes to look for based on diff. + * Diff 1 = 32, Diff 2 = 33, Diff 4 = 34 etc. */ + intdiff = (uint64_t)work->device_diff; + for (i = 31; intdiff; i++, intdiff >>= 1); + op_hash_data.search_difficulty = i; + op_hash_data.group = 0; + if ((sequence = info->hash_sequence_head + 1) >= info->num_sequence) + sequence = 0; + ret = hfa_send_frame(hashfast, OP_HASH, sequence, (uint8_t *)&op_hash_data, sizeof(op_hash_data)); + if (unlikely(!ret)) { + free_work(work); + if (base_work) + free_work(base_work); + hfa_running_shutdown(hashfast, info); + return -1; + } + + mutex_lock(&info->lock); + info->hash_sequence_head = sequence; + info->works[info->hash_sequence_head] = work; + mutex_unlock(&info->lock); + + applog(LOG_DEBUG, "%s %s: OP_HASH sequence %d search_difficulty %d work_difficulty %g", + hashfast->drv->name, hashfast->unique_id, info->hash_sequence_head, + op_hash_data.search_difficulty, work->work_difficulty); + } + + if (base_work) + free_work(base_work); + + /* Only count 2/3 of the hashes to smooth out the hashrate for cycles + * that have no hashes added. */ + mutex_lock(&info->lock); + hashes = info->hash_count / 3 * 2; + info->calc_hashes += hashes; + info->hash_count -= hashes; + mutex_unlock(&info->lock); + + return hashes; +} + +static struct api_data *hfa_api_stats(struct cgpu_info *cgpu) +{ + struct hashfast_info *info; + struct hf_long_usb_stats1 *s1; + struct api_data *root = NULL; + struct hf_usb_init_base *db; + int varint, i; + char buf[64]; + + info = cgpu->device_data; + if (!info) + return NULL; + + root = api_add_int(root, "asic count", &info->asic_count, false); + root = api_add_int(root, "core count", &info->core_count, false); + + root = api_add_double(root, "firmware rev", &info->firmware_version, false); + root = api_add_double(root, "hardware rev", &info->hardware_version, false); + db = &info->usb_init_base; + root = api_add_hex32(root, "serial number", &db->serial_number, true); + varint = db->hash_clockrate; + root = api_add_int(root, "base clockrate", &varint, true); + varint = db->inflight_target; + root = api_add_int(root, "inflight target", &varint, true); + varint = db->sequence_modulus; + root = api_add_int(root, "sequence modulus", &varint, true); + root = api_add_int(root, "fan percent", &info->fanspeed, false); + if (info->op_name[0] != '\0') + root = api_add_string(root, "op name", info->op_name, false); + + s1 = &info->stats1; + root = api_add_uint64(root, "rx preambles", &s1->usb_rx_preambles, false); + root = api_add_uint64(root, "rx rcv byte err", &s1->usb_rx_receive_byte_errors, false); + root = api_add_uint64(root, "rx bad hcrc", &s1->usb_rx_bad_hcrc, false); + root = api_add_uint64(root, "tx attempts", &s1->usb_tx_attempts, false); + root = api_add_uint64(root, "tx packets", &s1->usb_tx_packets, false); + root = api_add_uint64(root, "tx incompletes", &s1->usb_tx_incompletes, false); + root = api_add_uint64(root, "tx ep stalled", &s1->usb_tx_endpointstalled, false); + root = api_add_uint64(root, "tx disconnect", &s1->usb_tx_disconnected, false); + root = api_add_uint64(root, "tx suspend", &s1->usb_tx_suspended, false); + varint = s1->max_tx_buffers; + root = api_add_int(root, "max tx buf", &varint, true); + varint = s1->max_rx_buffers; + root = api_add_int(root, "max rx buf", &varint, true); + + for (i = 0; i < info->asic_count; i++) { + struct hf_long_statistics *l; + struct hf_g1_die_data *d; + char which[16]; + double val; + int j; + + if (!info->die_statistics || !info->die_status) + continue; + l = &info->die_statistics[i]; + if (!l) + continue; + d = &info->die_status[i]; + if (!d) + continue; + snprintf(which, sizeof(which), "Asic%d", i); + + snprintf(buf, sizeof(buf), "%s hash clockrate", which); + root = api_add_int(root, buf, &(info->die_data[i].hash_clock), false); + snprintf(buf, sizeof(buf), "%s die temperature", which); + val = GN_DIE_TEMPERATURE(d->die.die_temperature); + root = api_add_double(root, buf, &val, true); + snprintf(buf, sizeof(buf), "%s board temperature", which); + val = board_temperature(d->temperature); + root = api_add_double(root, buf, &val, true); + for (j = 0; j < 6; j++) { + snprintf(buf, sizeof(buf), "%s voltage %d", which, j); + val = GN_CORE_VOLTAGE(d->die.core_voltage[j]); + root = api_add_utility(root, buf, &val, true); + } + snprintf(buf, sizeof(buf), "%s rx header crc", which); + root = api_add_uint64(root, buf, &l->rx_header_crc, false); + snprintf(buf, sizeof(buf), "%s rx body crc", which); + root = api_add_uint64(root, buf, &l->rx_body_crc, false); + snprintf(buf, sizeof(buf), "%s rx header to", which); + root = api_add_uint64(root, buf, &l->rx_header_timeouts, false); + snprintf(buf, sizeof(buf), "%s rx body to", which); + root = api_add_uint64(root, buf, &l->rx_body_timeouts, false); + snprintf(buf, sizeof(buf), "%s cn fifo full", which); + root = api_add_uint64(root, buf, &l->core_nonce_fifo_full, false); + snprintf(buf, sizeof(buf), "%s an fifo full", which); + root = api_add_uint64(root, buf, &l->array_nonce_fifo_full, false); + snprintf(buf, sizeof(buf), "%s stats overrun", which); + root = api_add_uint64(root, buf, &l->stats_overrun, false); + } + + root = api_add_uint64(root, "raw hashcount", &info->raw_hashes, false); + root = api_add_uint64(root, "calc hashcount", &info->calc_hashes, false); + root = api_add_int(root, "no matching work", &info->no_matching_work, false); + root = api_add_uint16(root, "shed count", &info->shed_count, false); + root = api_add_int(root, "resets", &info->resets, false); + + return root; +} + +static void hfa_statline_before(char *buf, size_t bufsiz, struct cgpu_info *hashfast) +{ + struct hashfast_info *info; + struct hf_g1_die_data *d; + double max_volt; + int i; + + if (!hashfast->device_data) + return; + info = hashfast->device_data; + /* Can happen during init sequence */ + if (!info->die_status) + return; + max_volt = 0.0; + + for (i = 0; i < info->asic_count; i++) { + int j; + + d = &info->die_status[i]; + for (j = 0; j < 6; j++) { + double volt = GN_CORE_VOLTAGE(d->die.core_voltage[j]); + + if (volt > max_volt) + max_volt = volt; + } + } + + tailsprintf(buf, bufsiz, "%3dMHz %3.0fC %3d%% %3.2fV", info->base_clock, + hashfast->temp, info->fanspeed, max_volt); +} + +/* We cannot re-initialise so just shut down the device for it to hotplug + * again. */ +static void hfa_reinit(struct cgpu_info *hashfast) +{ + if (hashfast && hashfast->device_data) + hfa_running_shutdown(hashfast, hashfast->device_data); +} + +static void hfa_free_all_work(struct hashfast_info *info) +{ + while (info->device_sequence_tail != info->hash_sequence_head) { + struct work *work; + + if (++info->hash_sequence_tail >= info->num_sequence) + info->hash_sequence_tail = 0; + if (unlikely(!(work = info->works[info->hash_sequence_tail]))) + break; + free_work(work); + info->works[info->hash_sequence_tail] = NULL; + } +} + +static void hfa_shutdown(struct thr_info *thr) +{ + struct cgpu_info *hashfast = thr->cgpu; + struct hashfast_info *info = hashfast->device_data; + + hfa_send_shutdown(hashfast); + pthread_join(info->read_thr, NULL); + hfa_free_all_work(info); + hfa_clear_readbuf(hashfast); + free(info->works); + free(info->die_statistics); + free(info->die_status); + free(info->die_data); + /* Keep the device data intact to allow new instances to match old + * ones. */ +} + +struct device_drv hashfast_drv = { + .drv_id = DRIVER_hashfast, + .dname = "Hashfast", + .name = "HFA", + .max_diff = 32.0, // Limit max diff to get some nonces back regardless + .drv_detect = hfa_detect, + .thread_init = hfa_init, + .hash_work = &hash_driver_work, + .scanwork = hfa_scanwork, + .get_api_stats = hfa_api_stats, + .get_statline_before = hfa_statline_before, + .reinit_device = hfa_reinit, + .thread_shutdown = hfa_shutdown, +}; diff --git a/driver-hashfast.h b/driver-hashfast.h new file mode 100644 index 0000000000..331bbd6256 --- /dev/null +++ b/driver-hashfast.h @@ -0,0 +1,163 @@ +/* + * Copyright 2013-2014 Con Kolivas + * Copyright 2013 Hashfast + * + * 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 HASHFAST_H +#define HASHFAST_H + +#ifdef USE_HASHFAST +#include "miner.h" +#include "elist.h" +#include "hf_protocol.h" + +int opt_hfa_ntime_roll; +int opt_hfa_hash_clock; +int opt_hfa_overheat; +int opt_hfa_target; +bool opt_hfa_pll_bypass; +bool opt_hfa_dfu_boot; +int opt_hfa_fan_default; +int opt_hfa_fan_max; +int opt_hfa_fan_min; +int opt_hfa_fail_drop; +bool opt_hfa_noshed; + +char *set_hfa_fan(char *arg); +char *opt_hfa_name; +char *opt_hfa_options; + +#define HASHFAST_MINER_THREADS 1 +#define HFA_CLOCK_DEFAULT 550 +#define HFA_CLOCK_MIN 125 +#define HFA_CLOCK_MAX 1000 +#define HFA_CLOCK_MAXDIFF 100 +#define HFA_TEMP_OVERHEAT 95 +#define HFA_TEMP_TARGET 88 +#define HFA_TEMP_HYSTERESIS 3 +#define HFA_FAN_DEFAULT 33 +#define HFA_FAN_MAX 85 +#define HFA_FAN_MIN 5 +#define HFA_VOLTAGE_MAX 1000 +#define HFA_VOLTAGE_MIN 500 +#define HFA_MAGIC_SETTINGS_VALUE 0x42AA + +// # Factory Operation Codes +#define OP_SETTINGS 55 // Read or write settings +#define OP_POWER 57 + +// Matching fields for hf_statistics, but large #s for local accumulation, per-die +struct hf_long_statistics { + uint64_t rx_header_crc; // Header CRCs + uint64_t rx_body_crc; // Data CRCs + uint64_t rx_header_timeouts; // Header timeouts + uint64_t rx_body_timeouts; // Data timeouts + uint64_t core_nonce_fifo_full; // Core nonce Q overrun events + uint64_t array_nonce_fifo_full; // System nonce Q overrun events + uint64_t stats_overrun; // Overrun in statistics reporting +}; + +// Matching fields for hf_usb_stats1, but large #s for local accumulation, per device +struct hf_long_usb_stats1 { + // USB incoming + uint64_t usb_rx_preambles; + uint64_t usb_rx_receive_byte_errors; + uint64_t usb_rx_bad_hcrc; + + // USB outgoing + uint64_t usb_tx_attempts; + uint64_t usb_tx_packets; + uint64_t usb_tx_timeouts; + uint64_t usb_tx_incompletes; + uint64_t usb_tx_endpointstalled; + uint64_t usb_tx_disconnected; + uint64_t usb_tx_suspended; +#if 0 + /* We don't care about UART stats */ + // UART transmit + uint64_t uart_tx_queue_dma; + uint64_t uart_tx_interrupts; + + // UART receive + uint64_t uart_rx_preamble_ints; + uint64_t uart_rx_missed_preamble_ints; + uint64_t uart_rx_header_done; + uint64_t uart_rx_data_done; + uint64_t uart_rx_bad_hcrc; + uint64_t uart_rx_bad_dma; + uint64_t uart_rx_short_dma; + uint64_t uart_rx_buffers_full; +#endif + + uint8_t max_tx_buffers; + uint8_t max_rx_buffers; +}; + +/* Private per die data for dynamic clocking */ +struct hf_die_data { + int hash_clock; + double temp; + double board_temp; + time_t last_restart; +}; + +struct hashfast_info { + struct cgpu_info *cgpu; // Points back to parent structure + struct cgpu_info *old_cgpu ; // Points to old structure if hotplugged same device + int asic_count; // # of chips in the chain + int core_count; // # of cores per chip + int device_type; // What sort of device this is + int num_sequence; // A power of 2. What the sequence number range is. + int ref_frequency; // Reference clock rate + struct hf_g1_die_data *die_status; // Array of per-die voltage, current, temperature sensor data + struct hf_long_statistics *die_statistics; // Array of per-die error counters + struct hf_long_usb_stats1 stats1; + struct hf_die_data *die_data; + double firmware_version; + double hardware_version; + int hash_clock_rate; // Hash clock rate to use, in Mhz + int base_clock; // Clock rate we actually got + struct hf_usb_init_base usb_init_base; // USB Base information from USB_INIT + struct hf_config_data config_data; // Configuration data used from USB_INIT + int core_bitmap_size; // in bytes + uint32_t *core_bitmap; // Core OK bitmap test results, run with PLL Bypassed + int group_ntime_roll; // Total ntime roll amount per group + int core_ntime_roll; // Total core ntime roll amount + uint32_t serial_number; // db->serial_number if it exists + char op_name[36]; + bool has_opname; + bool opname_valid; + + pthread_mutex_t lock; + pthread_mutex_t rlock; + struct work **works; + uint16_t hash_sequence_head; // HOST: The next hash sequence # to be sent + uint16_t hash_sequence_tail; // HOST: Follows device_sequence_tail around to free work + uint16_t device_sequence_head; // DEVICE: The most recent sequence number the device dispatched + uint16_t device_sequence_tail; // DEVICE: The most recently completed job in the device + int64_t hash_count; + uint64_t raw_hashes; + uint64_t calc_hashes; + uint16_t shed_count; // Dynamic copy of #cores device has shed for thermal control + int no_matching_work; + int resets; + int overheat; + int last_max_temp; + int temp_updates; + int fanspeed; // Fanspeed in percent + int last_die_adjusted; + int clock_offset; + int hash_voltage; // Hash voltage to use, in mV + + pthread_t read_thr; + time_t last_restart; + time_t last_send; +}; + +#endif /* USE_HASHFAST */ +#endif /* HASHFAST_H */ diff --git a/driver-hashratio.c b/driver-hashratio.c new file mode 100644 index 0000000000..a95f902946 --- /dev/null +++ b/driver-hashratio.c @@ -0,0 +1,876 @@ +/* + * Copyright 2013-2014 Con Kolivas + * Copyright 2012-2014 Xiangfu + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the Free + * Software Foundation; either version 3 of the License, or (at your option) + * any later version. See COPYING for more details. + */ + +#include "config.h" + +#include +#include +#include +#include +#include +#include +#include +#ifndef WIN32 + #include + #include + #include + #ifndef O_CLOEXEC + #define O_CLOEXEC 0 + #endif +#else + #include + #include +#endif + +#include "elist.h" +#include "miner.h" +#include "driver-hashratio.h" +#include "crc.h" +#include "usbutils.h" + +static int opt_hashratio_fan_min = HRTO_DEFAULT_FAN_MIN; +static int opt_hashratio_fan_max = HRTO_DEFAULT_FAN_MAX; + +static int hashratio_freq = HRTO_DEFAULT_FREQUENCY; + +//static int get_fan_pwm(int temp) { +// int pwm; +// uint8_t fan_pwm_arr[] = {30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, +// 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, +// 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, +// 30, 37, 49, 61, 73, 85, 88, 91, 94, 97, 100, 100, 100, 100, 100, 100, +// 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, +// 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, +// 100, 100, 100, 100, 100, 100, 100}; +// if (temp < 0 || temp >= sizeof(fan_pwm_arr)/sizeof(fan_pwm_arr[0]) || +// fan_pwm_arr[temp] > opt_hashratio_fan_max) { +// return opt_hashratio_fan_max; +// } +// pwm = HRTO_PWM_MAX - fan_pwm_arr[temp] * HRTO_PWM_MAX / 100; +// +// if (pwm < opt_hashratio_fan_min) { +// return opt_hashratio_fan_min; +// } +// if (pwm > opt_hashratio_fan_max) { +// return opt_hashratio_fan_max; +// } +// return pwm; +//} + +char *set_hashratio_freq(char *arg) +{ + int val, ret; + + ret = sscanf(arg, "%d", &val); + if (ret != 1) + return "No values passed to hashratio-freq"; + + if (val < HRTO_DEFAULT_FREQUENCY_MIN || val > HRTO_DEFAULT_FREQUENCY_MAX) + return "Invalid value passed to hashratio-freq"; + + hashratio_freq = val; + + return NULL; +} + +static inline uint8_t rev8(uint8_t d) +{ + int i; + uint8_t out = 0; + + /* (from left to right) */ + for (i = 0; i < 8; i++) + if (d & (1 << i)) + out |= (1 << (7 - i)); + + return out; +} + +char *set_hashratio_fan(char *arg) +{ + int val1, val2, ret; + + ret = sscanf(arg, "%d-%d", &val1, &val2); + if (ret < 1) + return "No values passed to hashratio-fan"; + if (ret == 1) + val2 = val1; + + if (val1 < 0 || val1 > 100 || val2 < 0 || val2 > 100 || val2 < val1) + return "Invalid value passed to hashratio-fan"; + + opt_hashratio_fan_min = val1 * HRTO_PWM_MAX / 100; + opt_hashratio_fan_max = val2 * HRTO_PWM_MAX / 100; + + return NULL; +} + +static int hashratio_init_pkg(struct hashratio_pkg *pkg, uint8_t type, + uint8_t idx, uint8_t cnt) +{ + unsigned short crc; + + pkg->head[0] = HRTO_H1; + pkg->head[1] = HRTO_H2; + + pkg->type = type; + pkg->idx = idx; + pkg->cnt = cnt; + + crc = crc16(pkg->data, HRTO_P_DATA_LEN); + + pkg->crc[0] = (crc & 0xff00) >> 8; + pkg->crc[1] = crc & 0x00ff; + 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((const 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, "Hashratio: job_id not match! [%04x:%04x (%s)]", + crc, crc_expect, pool_job_id); + + return 1; +} + +static int decode_pkg(struct thr_info *thr, struct hashratio_ret *ar, uint8_t *pkg) +{ + struct cgpu_info *hashratio = thr->cgpu; + struct hashratio_info *info = hashratio->device_data; + struct pool *pool, *real_pool, *pool_stratum = &info->pool; + + unsigned int expected_crc; + unsigned int actual_crc; + uint32_t nonce, nonce2, miner; + int pool_no; + uint8_t job_id[4]; + int tmp; + + int type = HRTO_GETS_ERROR; + + memcpy((uint8_t *)ar, pkg, HRTO_READ_SIZE); + +// applog(LOG_DEBUG, "pkg.type, hex: %02x, dec: %d", ar->type, ar->type); + + if (ar->head[0] == HRTO_H1 && ar->head[1] == HRTO_H2) { + expected_crc = crc16(ar->data, HRTO_P_DATA_LEN); + actual_crc = (ar->crc[0] & 0xff) | + ((ar->crc[1] & 0xff) << 8); + + type = ar->type; + applog(LOG_DEBUG, "hashratio: %d: expected crc(%04x), actual_crc(%04x)", type, expected_crc, actual_crc); + if (expected_crc != actual_crc) + goto out; + + switch(type) { + case HRTO_P_NONCE: + applog(LOG_DEBUG, "Hashratio: HRTO_P_NONCE"); + memcpy(&miner, ar->data + 0, 4); + memcpy(&pool_no, ar->data + 4, 4); + memcpy(&nonce2, ar->data + 8, 4); + /* Calc time ar->data + 12 */ + memcpy(&nonce, ar->data + 12, 4); + memcpy(job_id, ar->data + 16, 4); + + miner = be32toh(miner); + pool_no = be32toh(pool_no); + if (miner >= HRTO_DEFAULT_MINERS || pool_no >= total_pools || pool_no < 0) { + applog(LOG_DEBUG, "hashratio: Wrong miner/pool/id no %d,%d", miner, pool_no); + break; + } else + info->matching_work[miner]++; + nonce2 = be32toh(nonce2); + nonce = be32toh(nonce); + + applog(LOG_DEBUG, "hashratio: Found! [%s] %d:(%08x) (%08x)", + job_id, pool_no, nonce2, nonce); + + real_pool = pool = pools[pool_no]; + if (job_idcmp(job_id, pool->swork.job_id)) { + if (!job_idcmp(job_id, pool_stratum->swork.job_id)) { + applog(LOG_DEBUG, "Hashratio: Match to previous stratum! (%s)", pool_stratum->swork.job_id); + pool = pool_stratum; + } else { + applog(LOG_DEBUG, "Hashratio Cannot match to any stratum! (%s)", pool->swork.job_id); + break; + } + } + submit_nonce2_nonce(thr, pool, real_pool, nonce2, nonce, 0); + break; + case HRTO_P_STATUS: + applog(LOG_DEBUG, "Hashratio: HRTO_P_STATUS"); + memcpy(&tmp, ar->data, 4); + tmp = be32toh(tmp); + info->temp = (tmp & 0x00f0) >> 8; + if (info->temp_max < info->temp) { + info->temp_max = info->temp; + } +// info->temp[1] = tmp & 0xffff; + + memcpy(&tmp, ar->data + 4, 4); + tmp = be32toh(tmp); + info->fan[0] = tmp >> 16; + info->fan[1] = tmp & 0xffff; + + // local_work + memcpy(&tmp, ar->data + 8, 4); + tmp = be32toh(tmp); + info->local_work = tmp; + info->local_works += tmp; + + // hw_work + memcpy(&tmp, ar->data + 12, 4); + tmp = be32toh(tmp); + info->hw_works += tmp; + + hashratio->temp = info->temp; + break; + case HRTO_P_ACKDETECT: + applog(LOG_DEBUG, "Hashratio: HRTO_P_ACKDETECT"); + break; + case HRTO_P_ACK: + applog(LOG_DEBUG, "Hashratio: HRTO_P_ACK"); + break; + case HRTO_P_NAK: + applog(LOG_DEBUG, "Hashratio: HRTO_P_NAK"); + break; + default: + applog(LOG_DEBUG, "Hashratio: HRTO_GETS_ERROR"); + type = HRTO_GETS_ERROR; + break; + } + } + +out: + return type; +} + +static inline int hashratio_gets(struct cgpu_info *hashratio, uint8_t *buf) +{ + int i; + int read_amount = HRTO_READ_SIZE; + uint8_t buf_tmp[HRTO_READ_SIZE]; + uint8_t buf_copy[2 * HRTO_READ_SIZE]; + uint8_t *buf_back = buf; + int ret = 0; + + while (true) { + int err; + + do { + memset(buf, 0, read_amount); + err = usb_read(hashratio, (char *)buf, read_amount, &ret, C_HRO_READ); + if (unlikely(err < 0 || ret != read_amount)) { + applog(LOG_ERR, "hashratio: Error on read in hashratio_gets got %d", ret); + return HRTO_GETS_ERROR; + } + if (likely(ret >= read_amount)) { + for (i = 1; i < read_amount; i++) { + if (buf_back[i - 1] == HRTO_H1 && buf_back[i] == HRTO_H2) + break; + } + i -= 1; + if (i) { + err = usb_read(hashratio, (char *)buf, read_amount, &ret, C_HRO_READ); + if (unlikely(err < 0 || ret != read_amount)) { + applog(LOG_ERR, "hashratio: Error on 2nd read in hashratio_gets got %d", ret); + return HRTO_GETS_ERROR; + } + memcpy(buf_copy, buf_back + i, HRTO_READ_SIZE - i); + memcpy(buf_copy + HRTO_READ_SIZE - i, buf_tmp, i); + memcpy(buf_back, buf_copy, HRTO_READ_SIZE); + } + return HRTO_GETS_OK; + } + buf += ret; + read_amount -= ret; + continue; + } while (ret > 0); + + return HRTO_GETS_TIMEOUT; + } +} + +static int hashratio_send_pkg(struct cgpu_info *hashratio, const struct hashratio_pkg *pkg) +{ + int err, amount; + uint8_t buf[HRTO_WRITE_SIZE]; + int nr_len = HRTO_WRITE_SIZE; + + memcpy(buf, pkg, HRTO_WRITE_SIZE); +// if (opt_debug) { +// applog(LOG_DEBUG, "hashratio: Sent(%d):", nr_len); +// hexdump((uint8_t *)buf, nr_len); +// } + + if (unlikely(hashratio->usbinfo.nodev)) + return HRTO_SEND_ERROR; + + err = usb_write(hashratio, (char *)buf, nr_len, &amount, C_HRO_WRITE); + if (err || amount != nr_len) { + applog(LOG_DEBUG, "hashratio: Send(%d)!", amount); + return HRTO_SEND_ERROR; + } + + return HRTO_SEND_OK; +} + +static int hashratio_send_pkgs(struct cgpu_info *hashratio, const struct hashratio_pkg *pkg) +{ + int ret; + + do { + if (unlikely(hashratio->usbinfo.nodev)) + return -1; + ret = hashratio_send_pkg(hashratio, pkg); + } while (ret != HRTO_SEND_OK); + return 0; +} + +static void hashratio_stratum_pkgs(struct cgpu_info *hashratio, struct pool *pool) +{ + const int merkle_offset = 36; + struct hashratio_pkg pkg; + int i, a, b, tmp; + unsigned char target[32]; + int job_id_len; + unsigned short crc; + + /* Send out the first stratum message STATIC */ + applog(LOG_DEBUG, "hashratio: Pool stratum message STATIC: %d, %d, %d, %d, %d, %d", + pool->coinbase_len, + pool->nonce2_offset, + pool->n2size, + merkle_offset, + pool->merkles, + pool->pool_no); + memset(pkg.data, 0, HRTO_P_DATA_LEN); + tmp = be32toh(pool->coinbase_len); + memcpy(pkg.data, &tmp, 4); + + tmp = be32toh(pool->nonce2_offset); + memcpy(pkg.data + 4, &tmp, 4); + + tmp = be32toh(pool->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); + + tmp = be32toh((int)pool->sdiff); + memcpy(pkg.data + 20, &tmp, 4); + + tmp = be32toh((int)pool->pool_no); + memcpy(pkg.data + 24, &tmp, 4); + + hashratio_init_pkg(&pkg, HRTO_P_STATIC, 1, 1); + if (hashratio_send_pkgs(hashratio, &pkg)) + return; + + set_target(target, pool->sdiff); + memcpy(pkg.data, target, 32); + if (opt_debug) { + char *target_str; + target_str = bin2hex(target, 32); + applog(LOG_DEBUG, "hashratio: Pool stratum target: %s", target_str); + free(target_str); + } + hashratio_init_pkg(&pkg, HRTO_P_TARGET, 1, 1); + if (hashratio_send_pkgs(hashratio, &pkg)) + return; + + applog(LOG_DEBUG, "hashratio: Pool stratum message JOBS_ID: %s", + pool->swork.job_id); + memset(pkg.data, 0, HRTO_P_DATA_LEN); + + job_id_len = strlen(pool->swork.job_id); + crc = crc16((const unsigned char *)pool->swork.job_id, job_id_len); + pkg.data[0] = (crc & 0xff00) >> 8; + pkg.data[1] = crc & 0x00ff; + hashratio_init_pkg(&pkg, HRTO_P_JOB_ID, 1, 1); + if (hashratio_send_pkgs(hashratio, &pkg)) + return; + + a = pool->coinbase_len / HRTO_P_DATA_LEN; + b = pool->coinbase_len % HRTO_P_DATA_LEN; + applog(LOG_DEBUG, "pool->coinbase_len: %d", pool->coinbase_len); + applog(LOG_DEBUG, "hashratio: Pool stratum message COINBASE: %d %d", a, b); + for (i = 0; i < a; i++) { + memcpy(pkg.data, pool->coinbase + i * 32, 32); + hashratio_init_pkg(&pkg, HRTO_P_COINBASE, i + 1, a + (b ? 1 : 0)); + if (hashratio_send_pkgs(hashratio, &pkg)) + return; + if (i % 25 == 0) { + cgsleep_ms(2); + } + } + if (b) { + memset(pkg.data, 0, HRTO_P_DATA_LEN); + memcpy(pkg.data, pool->coinbase + i * 32, b); + hashratio_init_pkg(&pkg, HRTO_P_COINBASE, i + 1, i + 1); + if (hashratio_send_pkgs(hashratio, &pkg)) + return; + } + + b = pool->merkles; + applog(LOG_DEBUG, "hashratio: Pool stratum message MERKLES: %d", b); + for (i = 0; i < b; i++) { + memset(pkg.data, 0, HRTO_P_DATA_LEN); + memcpy(pkg.data, pool->swork.merkle_bin[i], 32); + hashratio_init_pkg(&pkg, HRTO_P_MERKLES, i + 1, b); + if (hashratio_send_pkgs(hashratio, &pkg)) + return; + } + + applog(LOG_DEBUG, "hashratio: Pool stratum message HEADER: 4"); + for (i = 0; i < 4; i++) { + memset(pkg.data, 0, HRTO_P_HEADER); + memcpy(pkg.data, pool->header_bin + i * 32, 32); + hashratio_init_pkg(&pkg, HRTO_P_HEADER, i + 1, 4); + if (hashratio_send_pkgs(hashratio, &pkg)) + return; + + } +} + +static int hashratio_get_result(struct thr_info *thr, struct hashratio_ret *ar) +{ + struct cgpu_info *hashratio = thr->cgpu; + uint8_t result[HRTO_READ_SIZE]; + int ret; + + memset(result, 0, HRTO_READ_SIZE); + + ret = hashratio_gets(hashratio, result); + if (ret != HRTO_GETS_OK) + return ret; + +// if (opt_debug) { +// applog(LOG_DEBUG, "hashratio: Get(ret = %d):", ret); +// hexdump((uint8_t *)result, HRTO_READ_SIZE); +// } + + return decode_pkg(thr, ar, result); +} + +#define HASHRATIO_LATENCY 5 + +static void hashratio_initialise(struct cgpu_info *hashratio) +{ + int err, interface; + + if (hashratio->usbinfo.nodev) + return; + + interface = usb_interface(hashratio); + // Reset + err = usb_transfer(hashratio, FTDI_TYPE_OUT, FTDI_REQUEST_RESET, + FTDI_VALUE_RESET, interface, C_RESET); + + applog(LOG_DEBUG, "%s%i: reset got err %d", + hashratio->drv->name, hashratio->device_id, err); + + if (hashratio->usbinfo.nodev) + return; + + // Set latency + err = usb_transfer(hashratio, FTDI_TYPE_OUT, FTDI_REQUEST_LATENCY, + HASHRATIO_LATENCY, interface, C_LATENCY); + + applog(LOG_DEBUG, "%s%i: latency got err %d", + hashratio->drv->name, hashratio->device_id, err); + + if (hashratio->usbinfo.nodev) + return; + + // Set data + err = usb_transfer(hashratio, FTDI_TYPE_OUT, FTDI_REQUEST_DATA, + FTDI_VALUE_DATA_AVA, interface, C_SETDATA); + + applog(LOG_DEBUG, "%s%i: data got err %d", + hashratio->drv->name, hashratio->device_id, err); + + if (hashratio->usbinfo.nodev) + return; + + // Set the baud + err = usb_transfer(hashratio, FTDI_TYPE_OUT, FTDI_REQUEST_BAUD, FTDI_VALUE_BAUD_AVA, + (FTDI_INDEX_BAUD_AVA & 0xff00) | interface, + C_SETBAUD); + + applog(LOG_DEBUG, "%s%i: setbaud got err %d", + hashratio->drv->name, hashratio->device_id, err); + + if (hashratio->usbinfo.nodev) + return; + + // Set Modem Control + err = usb_transfer(hashratio, FTDI_TYPE_OUT, FTDI_REQUEST_MODEM, + FTDI_VALUE_MODEM, interface, C_SETMODEM); + + applog(LOG_DEBUG, "%s%i: setmodemctrl got err %d", + hashratio->drv->name, hashratio->device_id, err); + + if (hashratio->usbinfo.nodev) + return; + + // Set Flow Control + err = usb_transfer(hashratio, FTDI_TYPE_OUT, FTDI_REQUEST_FLOW, + FTDI_VALUE_FLOW, interface, C_SETFLOW); + + applog(LOG_DEBUG, "%s%i: setflowctrl got err %d", + hashratio->drv->name, hashratio->device_id, err); + + if (hashratio->usbinfo.nodev) + return; + + /* hashratio repeats the following */ + // Set Modem Control + err = usb_transfer(hashratio, FTDI_TYPE_OUT, FTDI_REQUEST_MODEM, + FTDI_VALUE_MODEM, interface, C_SETMODEM); + + applog(LOG_DEBUG, "%s%i: setmodemctrl 2 got err %d", + hashratio->drv->name, hashratio->device_id, err); + + if (hashratio->usbinfo.nodev) + return; + + // Set Flow Control + err = usb_transfer(hashratio, FTDI_TYPE_OUT, FTDI_REQUEST_FLOW, + FTDI_VALUE_FLOW, interface, C_SETFLOW); + + applog(LOG_DEBUG, "%s%i: setflowctrl 2 got err %d", + hashratio->drv->name, hashratio->device_id, err); +} + +static struct cgpu_info *hashratio_detect_one(struct libusb_device *dev, struct usb_find_devices *found) +{ + struct hashratio_info *info; + int err, amount; + int ackdetect; + char mm_version[16]; + + struct cgpu_info *hashratio = usb_alloc_cgpu(&hashratio_drv, 1); + struct hashratio_pkg detect_pkg; + struct hashratio_ret ret_pkg; + + if (!usb_init(hashratio, dev, found)) { + applog(LOG_ERR, "Hashratio failed usb_init"); + hashratio = usb_free_cgpu(hashratio); + return NULL; + } + + hashratio_initialise(hashratio); + + strcpy(mm_version, "NONE"); + /* Send out detect pkg */ + memset(detect_pkg.data, 0, HRTO_P_DATA_LEN); + + hashratio_init_pkg(&detect_pkg, HRTO_P_DETECT, 1, 1); + hashratio_send_pkg(hashratio, &detect_pkg); + err = usb_read(hashratio, (char *)&ret_pkg, HRTO_READ_SIZE, &amount, C_HRO_READ); + if (err || amount != HRTO_READ_SIZE) { + applog(LOG_ERR, "%s %d: Hashratio failed usb_read with err %d amount %d", + hashratio->drv->name, hashratio->device_id, err, amount); + usb_uninit(hashratio); + usb_free_cgpu(hashratio); + return NULL; + } + + ackdetect = ret_pkg.type; + applog(LOG_DEBUG, "hashratio Detect ID: %d", ackdetect); + + if (ackdetect != HRTO_P_ACKDETECT) { + applog(LOG_DEBUG, "Not a hashratio device"); + usb_uninit(hashratio); + usb_free_cgpu(hashratio); + return NULL; + } + + memcpy(mm_version, ret_pkg.data, 15); + mm_version[15] = '\0'; + + /* We have a real Hashratio! */ + hashratio->threads = HRTO_MINER_THREADS; + add_cgpu(hashratio); + + update_usb_stats(hashratio); + + applog(LOG_INFO, "%s%d: Found at %s", hashratio->drv->name, hashratio->device_id, + hashratio->device_path); + + hashratio->device_data = calloc(sizeof(struct hashratio_info), 1); + if (unlikely(!(hashratio->device_data))) + quit(1, "Failed to malloc hashratio_info"); + + info = hashratio->device_data; + + strcpy(info->mm_version, mm_version); + + info->fan_pwm = HRTO_DEFAULT_FAN / 100 * HRTO_PWM_MAX; + info->temp_max = 0; + info->temp_history_index = 0; + info->temp_sum = 0; + info->temp_old = 0; + info->default_freq = hashratio_freq; + + return hashratio; +} + +static inline void hashratio_detect(bool __maybe_unused hotplug) +{ + usb_detect(&hashratio_drv, hashratio_detect_one); +} + +static bool hashratio_prepare(struct thr_info *thr) +{ + struct cgpu_info *hashratio = thr->cgpu; + struct hashratio_info *info = hashratio->device_data; + + cglock_init(&info->pool.data_lock); + + return true; +} + +static void copy_pool_stratum(struct hashratio_info *info, struct pool *pool) +{ + int i; + int merkles = pool->merkles; + size_t coinbase_len = pool->coinbase_len; + struct pool *pool_stratum = &info->pool; + + if (!job_idcmp((uint8_t *)pool->swork.job_id, pool_stratum->swork.job_id)) + return; + + cg_wlock(&(pool_stratum->data_lock)); + free(pool_stratum->swork.job_id); + free(pool_stratum->nonce1); + free(pool_stratum->coinbase); + + align_len(&coinbase_len); + pool_stratum->coinbase = calloc(coinbase_len, 1); + if (unlikely(!pool_stratum->coinbase)) + quit(1, "Failed to calloc pool_stratum coinbase in hashratio"); + 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 = realloc(pool_stratum->swork.merkle_bin, + sizeof(char *) * merkles + 1); + for (i = 0; i < merkles; i++) { + pool_stratum->swork.merkle_bin[i] = malloc(32); + if (unlikely(!pool_stratum->swork.merkle_bin[i])) + quit(1, "Failed to malloc pool_stratum swork merkle_bin"); + 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 hashratio_update_work(struct cgpu_info *hashratio) +{ + struct hashratio_info *info = hashratio->device_data; + struct thr_info *thr = hashratio->thr[0]; + struct hashratio_pkg send_pkg; + uint32_t tmp, range, start; + struct work *work; + struct pool *pool; + + applog(LOG_DEBUG, "hashratio: New stratum: restart: %d, update: %d", + thr->work_restart, thr->work_update); + thr->work_update = false; + thr->work_restart = false; + + work = get_work(thr, thr->id); /* Make sure pool is ready */ + discard_work(work); /* Don't leak memory */ + + pool = current_pool(); + if (!pool->has_stratum) + quit(1, "hashratio: Miner Manager have to use stratum pool"); + if (pool->coinbase_len > HRTO_P_COINBASE_SIZE) + quit(1, "hashratio: Miner Manager pool coinbase length have to less then %d", HRTO_P_COINBASE_SIZE); + if (pool->merkles > HRTO_P_MERKLES_COUNT) + quit(1, "hashratio: Miner Manager merkles have to less then %d", HRTO_P_MERKLES_COUNT); + + info->pool_no = pool->pool_no; + + cgtime(&info->last_stratum); + cg_rlock(&pool->data_lock); + info->pool_no = pool->pool_no; + copy_pool_stratum(info, pool); + hashratio_stratum_pkgs(hashratio, pool); + cg_runlock(&pool->data_lock); + + /* Configure the parameter from outside */ + memset(send_pkg.data, 0, HRTO_P_DATA_LEN); + + // fan. We're not measuring temperature so set a safe but not max value + info->fan_pwm = HRTO_PWM_MAX * 2 / 3; + tmp = be32toh(info->fan_pwm); + memcpy(send_pkg.data, &tmp, 4); + + // freq + tmp = be32toh(info->default_freq); + memcpy(send_pkg.data + 4, &tmp, 4); + applog(LOG_DEBUG, "set freq: %d", info->default_freq); + + /* Configure the nonce2 offset and range */ + range = 0xffffffff / (total_devices + 1); + start = range * (hashratio->device_id + 1); + + tmp = be32toh(start); + memcpy(send_pkg.data + 8, &tmp, 4); + + tmp = be32toh(range); + memcpy(send_pkg.data + 12, &tmp, 4); + + /* Package the data */ + hashratio_init_pkg(&send_pkg, HRTO_P_SET, 1, 1); + hashratio_send_pkgs(hashratio, &send_pkg); +} + +static int64_t hashratio_scanhash(struct thr_info *thr) +{ + struct cgpu_info *hashratio = thr->cgpu; + struct hashratio_info *info = hashratio->device_data; + struct hashratio_pkg send_pkg; + struct hashratio_ret ar; + + memset(send_pkg.data, 0, HRTO_P_DATA_LEN); + hashratio_init_pkg(&send_pkg, HRTO_P_POLLING, 1, 1); + + if (unlikely(hashratio->usbinfo.nodev || hashratio_send_pkgs(hashratio, &send_pkg))) { + applog(LOG_ERR, "%s%d: Device disappeared, shutting down thread", + hashratio->drv->name, hashratio->device_id); + return -1; + } + hashratio_get_result(thr, &ar); + + return (int64_t)info->local_work * 64 * 0xffffffff; +} + +static struct api_data *hashratio_api_stats(struct cgpu_info *cgpu) +{ + struct api_data *root = NULL; + struct hashratio_info *info = cgpu->device_data; + char buf[24]; + char buf2[256]; + double hwp; + int i; + + // mm version + sprintf(buf, "MM Version"); + root = api_add_string(root, buf, info->mm_version, false); + + // asic freq + sprintf(buf, "Asic Freq (MHz)"); + root = api_add_int(root, buf, &(info->default_freq), false); + + // match work count + for (i = 0; i < HRTO_DEFAULT_MODULARS; i++) { + sprintf(buf, "Match work Modular %02d", i + 1); + memset(buf2, 0, sizeof(buf2)); + snprintf(buf2, sizeof(buf2), + "%02d:%08d %02d:%08d %02d:%08d %02d:%08d " + "%02d:%08d %02d:%08d %02d:%08d %02d:%08d " + "%02d:%08d %02d:%08d %02d:%08d %02d:%08d " + "%02d:%08d %02d:%08d %02d:%08d %02d:%08d", + i*16 + 1, info->matching_work[i*16 + 0], + i*16 + 2, info->matching_work[i*16 + 1], + i*16 + 3, info->matching_work[i*16 + 2], + i*16 + 4, info->matching_work[i*16 + 3], + i*16 + 5, info->matching_work[i*16 + 4], + i*16 + 6, info->matching_work[i*16 + 5], + i*16 + 7, info->matching_work[i*16 + 6], + i*16 + 8, info->matching_work[i*16 + 7], + i*16 + 9, info->matching_work[i*16 + 8], + i*16 + 10, info->matching_work[i*16 + 9], + i*16 + 11, info->matching_work[i*16 + 10], + i*16 + 12, info->matching_work[i*16 + 11], + i*16 + 13, info->matching_work[i*16 + 12], + i*16 + 14, info->matching_work[i*16 + 13], + i*16 + 15, info->matching_work[i*16 + 14], + i*16 + 16, info->matching_work[i*16 + 15]); + root = api_add_string(root, buf, buf2, true); + } + + // local works + sprintf(buf, "Local works"); + root = api_add_int(root, buf, &(info->local_works), false); + + // hardware error works + sprintf(buf, "Hardware error works"); + root = api_add_int(root, buf, &(info->hw_works), false); + + // device hardware error % + hwp = info->local_works ? ((double)info->hw_works / (double)info->local_works) : 0; + sprintf(buf, "Device hardware error%%"); + root = api_add_percent(root, buf, &hwp, true); + + // Temperature + sprintf(buf, "Temperature"); + root = api_add_int(root, buf, &(info->temp), false); + + // Fan + for (i = 0; i < HRTO_FAN_COUNT; i++) { + sprintf(buf, "Fan%d", i+1); + root = api_add_int(root, buf, &(info->fan[i]), false); + } + + return root; +} + +static void hashratio_shutdown(struct thr_info __maybe_unused *thr) +{ +} + +struct device_drv hashratio_drv = { + .drv_id = DRIVER_hashratio, + .dname = "hashratio", + .name = "HRO", + .get_api_stats = hashratio_api_stats, + .drv_detect = hashratio_detect, + .thread_prepare = hashratio_prepare, + .hash_work = hash_driver_work, + .scanwork = hashratio_scanhash, + .flush_work = hashratio_update_work, + .update_work = hashratio_update_work, + .thread_shutdown = hashratio_shutdown, +}; diff --git a/driver-hashratio.h b/driver-hashratio.h new file mode 100644 index 0000000000..e155b5aea8 --- /dev/null +++ b/driver-hashratio.h @@ -0,0 +1,131 @@ +/* + * Copyright 2013-2014 Con Kolivas + * Copyright 2012-2014 Xiangfu + * + * 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 _HASHRATIO_H_ +#define _HASHRATIO_H_ + +#include "miner.h" +#include "util.h" + +#ifdef USE_HASHRATIO +char *opt_hashratio_freq; + +#define HRTO_MINER_THREADS 1 + +#define HRTO_RESET_FAULT_DECISECONDS 10 +#define HRTO_IO_SPEED 115200 + +#define HRTO_DEFAULT_MODULARS 5 +#define HRTO_DEFAULT_MINERS_PER_MODULAR 16 +/* total chips number */ +#define HRTO_DEFAULT_MINERS (HRTO_DEFAULT_MODULARS * 16) + +#define HRTO_PWM_MAX 0x3FF +#define HRTO_DEFAULT_FAN 20 /* N% */ +#define HRTO_DEFAULT_FAN_MIN 50 /* N% */ +#define HRTO_DEFAULT_FAN_MAX 100 /* N% */ + +#define HRTO_DEFAULT_FREQUENCY 280 /* MHz */ +#define HRTO_DEFAULT_FREQUENCY_MIN 100 +#define HRTO_DEFAULT_FREQUENCY_MAX 750 + +#define HRTO_FAN_COUNT 2 +//#define HRTO_TEMP_COUNT 1 + +/* Hashratio protocol package type */ +#define HRTO_H1 'H' +#define HRTO_H2 'R' + +#define HRTO_P_COINBASE_SIZE (6 * 1024) +#define HRTO_P_MERKLES_COUNT 20 + +#define HRTO_P_COUNT 39 +#define HRTO_P_DATA_LEN (HRTO_P_COUNT - 7) + +#define HRTO_P_DETECT 10 // 0x0a +#define HRTO_P_STATIC 11 // 0x0b +#define HRTO_P_JOB_ID 12 // 0x0c +#define HRTO_P_COINBASE 13 // 0x0d +#define HRTO_P_MERKLES 14 // 0x0e +#define HRTO_P_HEADER 15 // 0x0f +#define HRTO_P_POLLING 16 // 0x10 +#define HRTO_P_TARGET 17 // 0x11 +#define HRTO_P_REQUIRE 18 // 0x12 +#define HRTO_P_SET 19 // 0x13 +#define HRTO_P_TEST 20 // 0x14 + +#define HRTO_P_ACK 51 // 0x33 +#define HRTO_P_NAK 52 // 0x34 +#define HRTO_P_NONCE 53 // 0x35 +#define HRTO_P_STATUS 54 // 0x36 +#define HRTO_P_ACKDETECT 55 // 0x37 +#define HRTO_P_TEST_RET 56 // 0x38 +/* Hashratio protocol package type */ + +struct hashratio_pkg { + uint8_t head[2]; + uint8_t type; + uint8_t idx; + uint8_t cnt; + uint8_t data[32]; + uint8_t crc[2]; +}; +#define hashratio_ret hashratio_pkg + +struct hashratio_info { + int default_freq; + + int fan_pwm; + + int temp; + int fan[HRTO_FAN_COUNT]; +// uint8_t freq[HRTO_DEFAULT_MINERS]; + uint8_t target_freq[HRTO_DEFAULT_MINERS]; + + int temp_max; + int temp_history_count; + int temp_history_index; + int temp_sum; + int temp_old; + + struct timeval last_stratum; + struct pool pool; + int pool_no; + + int local_works; + int hw_works; + int matching_work[HRTO_DEFAULT_MINERS]; + int local_work; + int hw_work; + +// uint32_t get_result_counter; + + char mm_version[16]; +}; + +#define HRTO_WRITE_SIZE (sizeof(struct hashratio_pkg)) +#define HRTO_READ_SIZE HRTO_WRITE_SIZE + +#define HRTO_GETS_OK 0 +#define HRTO_GETS_TIMEOUT -1 +#define HRTO_GETS_RESTART -2 +#define HRTO_GETS_ERROR -3 + +#define HRTO_SEND_OK 0 +#define HRTO_SEND_ERROR -1 + +#define hashratio_open(devpath, baud, purge) serial_open(devpath, baud, HRTO_RESET_FAULT_DECISECONDS, purge) +#define hashratio_close(fd) close(fd) + +extern char *set_hashratio_fan(char *arg); +extern char *set_hashratio_freq(char *arg); + +#endif /* USE_HASHRATIO */ +#endif /* _HASHRATIO_H_ */ diff --git a/driver-icarus.c b/driver-icarus.c index 647c9d14c6..2971121786 100644 --- a/driver-icarus.c +++ b/driver-icarus.c @@ -1,7 +1,7 @@ /* * Copyright 2012-2013 Andrew Smith * Copyright 2012 Xiangfu - * Copyright 2013 Con Kolivas + * Copyright 2013-2014 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 @@ -38,6 +38,7 @@ #include #include #include +#include #include "config.h" @@ -52,12 +53,11 @@ // The serial I/O speed - Linux uses a define 'B115200' in bits/termios.h #define ICARUS_IO_SPEED 115200 +#define ICARUS_BUF_SIZE 8 // The size of a successful nonce read +#define ANT_READ_SIZE 5 #define ICARUS_READ_SIZE 4 - -#define AMU_PREF_PACKET 256 -#define BLT_PREF_PACKET 512 -#define ICA_PREF_PACKET 256 +#define ROCK_READ_SIZE 8 // Ensure the sizes are correct for the Serial read #if (ICARUS_READ_SIZE != 4) @@ -73,7 +73,12 @@ ASSERT1(sizeof(uint32_t) == 4); #define ICARUS_READ_TIME(baud) (0.001) // USB ms timeout to wait - user specified timeouts are multiples of this -#define ICARUS_WAIT_TIMEOUT 100 +#define ICA_WAIT_TIMEOUT 100 +#define ANT_WAIT_TIMEOUT 10 +#define AU3_WAIT_TIMEOUT 1 +#define ICARUS_WAIT_TIMEOUT (info->u3 ? AU3_WAIT_TIMEOUT : (info->ant ? ANT_WAIT_TIMEOUT : ICA_WAIT_TIMEOUT)) + +#define ICARUS_CMR2_TIMEOUT 1 // Defined in multiples of ICARUS_WAIT_TIMEOUT // Must of course be greater than ICARUS_READ_COUNT_TIMING/ICARUS_WAIT_TIMEOUT @@ -86,6 +91,13 @@ ASSERT1(sizeof(uint32_t) == 4); // In timing mode: Default starting value until an estimate can be obtained // 5000 ms allows for up to a ~840MH/s device #define ICARUS_READ_COUNT_TIMING 5000 + +// Antminer USB is > 1GH/s so use a shorter limit +// 1000 ms allows for up to ~4GH/s device +#define ANTUSB_READ_COUNT_TIMING 1000 + +#define ANTU3_READ_COUNT_TIMING 100 + #define ICARUS_READ_COUNT_MIN ICARUS_WAIT_TIMEOUT #define SECTOMS(s) ((int)((s) * 1000)) // How many ms below the expected completion time to abort work @@ -102,8 +114,16 @@ ASSERT1(sizeof(uint32_t) == 4); #define LANCELOT_HASH_TIME 0.0000000025000 #define ASICMINERUSB_HASH_TIME 0.0000000029761 // TODO: What is it? -#define CAIRNSMORE1_HASH_TIME 0.0000000026316 +#define CAIRNSMORE1_HASH_TIME 0.0000000027000 +// Per FPGA +#define CAIRNSMORE2_HASH_TIME 0.0000000066600 #define NANOSEC 1000000000.0 +#define ANTMINERUSB_HASH_MHZ 0.000000125 +#define ANTMINERUSB_HASH_TIME (ANTMINERUSB_HASH_MHZ / (double)(opt_anu_freq)) +#define ANTU3_HASH_MHZ 0.0000000032 +#define ANTU3_HASH_TIME (ANTU3_HASH_MHZ / (double)(opt_au3_freq)) + +#define CAIRNSMORE2_INTS 4 // Icarus Rev3 doesn't send a completion message when it finishes // the full nonce range, so to avoid being idle we must abort the @@ -174,7 +194,86 @@ static const char *MODE_LONG_STREQ = "long="; static const char *MODE_VALUE_STR = "value"; static const char *MODE_UNKNOWN_STR = "unknown"; +#define MAX_DEVICE_NUM 100 +#define MAX_WORK_BUFFER_SIZE 2 +#define MAX_CHIP_NUM 24 +// Set it to 3, 5 or 9 +#define NONCE_CORRECTION_TIMES 5 +#define MAX_TRIES 4 +#define RM_CMD_MASK 0x0F +#define RM_STATUS_MASK 0xF0 +#define RM_CHIP_MASK 0x3F +#define RM_PRODUCT_MASK 0xC0 +#define RM_PRODUCT_RBOX 0x00 +#define RM_PRODUCT_T1 0x40 +#define RM_PRODUCT_T2 0x80 +#define RM_PRODUCT_TEST 0xC0 + +#if (NONCE_CORRECTION_TIMES == 5) +static int32_t rbox_corr_values[] = {0, 1, -1, -2, -4}; +#endif +#if (NONCE_CORRECTION_TIMES == 9) +static int32_t rbox_corr_values[] = {0, 1, -1, 2, -2, 3, -3, 4, -4}; +#endif +#if (NONCE_CORRECTION_TIMES == 3) +static int32_t rbox_corr_values[] = {0, 1, -1}; +#endif + +#define ANT_QUEUE_NUM 36 + +typedef enum { + NONCE_DATA1_OFFSET = 0, + NONCE_DATA2_OFFSET, + NONCE_DATA3_OFFSET, + NONCE_DATA4_OFFSET, + NONCE_TASK_CMD_OFFSET, + NONCE_CHIP_NO_OFFSET, + NONCE_TASK_NO_OFFSET, + NONCE_COMMAND_OFFSET, + NONCE_MAX_OFFSET +} NONCE_OFFSET; + +typedef enum { + NONCE_DATA_CMD = 0, + NONCE_TASK_COMPLETE_CMD, + NONCE_GET_TASK_CMD, +} NONCE_COMMAND; + +typedef struct nonce_data { + int chip_no; + unsigned int task_no ; + unsigned char work_state; + int cmd_value; +} NONCE_DATA; + +typedef enum { + ROCKMINER_RBOX = 0, + ROCKMINER_T1, + ROCKMINER_T2, + ROCKMINER_MAX +} ROCKMINER_PRODUCT_T; + +typedef struct rockminer_chip_info { + unsigned char freq; + int error_cnt; + time_t last_received_task_complete_time; +} ROCKMINER_CHIP_INFO; + +typedef struct rockminer_device_info { + unsigned char detect_chip_no; + unsigned char chip_max; + unsigned char product_id; + float min_frq; + float def_frq; + float max_frq; + ROCKMINER_CHIP_INFO chip[MAX_CHIP_NUM]; + time_t dev_detect_time; +} ROCKMINER_DEVICE_INFO; + struct ICARUS_INFO { + enum sub_ident ident; + int intinfo; + // time to calculate the golden_ob uint64_t golden_hashes; struct timeval golden_tv; @@ -182,12 +281,16 @@ struct ICARUS_INFO { struct ICARUS_HISTORY history[INFO_HISTORY+1]; uint32_t min_data_count; + int timeout; + // seconds per Hash double Hs; // ms til we abort int read_time; // ms limit for (short=/long=) read_time int read_time_limit; + // How long without hashes is considered a failed device + int fail_time; enum timing_mode timing_mode; bool do_icarus_timing; @@ -208,6 +311,95 @@ struct ICARUS_INFO { int work_division; int fpga_count; uint32_t nonce_mask; + + uint8_t cmr2_speed; + bool speed_next_work; + bool flash_next_work; + + int nonce_size; + + bool failing; + + pthread_mutex_t lock; + + ROCKMINER_DEVICE_INFO rmdev; + struct work *base_work; // For when we roll work + struct work *g_work[MAX_CHIP_NUM][MAX_WORK_BUFFER_SIZE]; + uint32_t last_nonce[MAX_CHIP_NUM][MAX_WORK_BUFFER_SIZE]; + char rock_init[64]; + uint64_t nonces_checked; + uint64_t nonces_correction_times; + uint64_t nonces_correction_tests; + uint64_t nonces_fail; + uint64_t nonces_correction[NONCE_CORRECTION_TIMES]; + + struct work **antworks; + int nonces; + int workid; + bool ant; + bool u3; +}; + +#define ICARUS_MIDSTATE_SIZE 32 +#define ICARUS_UNUSED_SIZE 16 +#define ICARUS_WORK_SIZE 12 + +#define ICARUS_WORK_DATA_OFFSET 64 + +#define ICARUS_CMR2_SPEED_FACTOR 2.5 +#define ICARUS_CMR2_SPEED_MIN_INT 100 +#define ICARUS_CMR2_SPEED_DEF_INT 180 +#define ICARUS_CMR2_SPEED_MAX_INT 220 +#define CMR2_INT_TO_SPEED(_speed) ((uint8_t)((float)_speed / ICARUS_CMR2_SPEED_FACTOR)) +#define ICARUS_CMR2_SPEED_MIN CMR2_INT_TO_SPEED(ICARUS_CMR2_SPEED_MIN_INT) +#define ICARUS_CMR2_SPEED_DEF CMR2_INT_TO_SPEED(ICARUS_CMR2_SPEED_DEF_INT) +#define ICARUS_CMR2_SPEED_MAX CMR2_INT_TO_SPEED(ICARUS_CMR2_SPEED_MAX_INT) +#define ICARUS_CMR2_SPEED_INC 1 +#define ICARUS_CMR2_SPEED_DEC -1 +#define ICARUS_CMR2_SPEED_FAIL -10 + +#define ICARUS_CMR2_PREFIX ((uint8_t)0xB7) +#define ICARUS_CMR2_CMD_SPEED ((uint8_t)0) +#define ICARUS_CMR2_CMD_FLASH ((uint8_t)1) +#define ICARUS_CMR2_DATA_FLASH_OFF ((uint8_t)0) +#define ICARUS_CMR2_DATA_FLASH_ON ((uint8_t)1) +#define ICARUS_CMR2_CHECK ((uint8_t)0x6D) + +#define ANT_UNUSED_SIZE 15 + +struct ICARUS_WORK { + uint8_t midstate[ICARUS_MIDSTATE_SIZE]; + // These 4 bytes are for CMR2 bitstreams that handle MHz adjustment + uint8_t check; + uint8_t data; + uint8_t cmd; + uint8_t prefix; + uint8_t unused[ANT_UNUSED_SIZE]; + uint8_t id; // Used only by ANT, otherwise unused by other icarus + uint8_t work[ICARUS_WORK_SIZE]; +}; + +#define ANT_U1_DEFFREQ 200 +#define ANT_U3_DEFFREQ 225 +#define ANT_U3_MAXFREQ 250 +struct { + float freq; + uint16_t hex; +} u3freqtable[] = { + { 100, 0x0783 }, + { 125, 0x0983 }, + { 150, 0x0b83 }, + { 175, 0x0d83 }, + { 193.75, 0x0f03 }, + { 196.88, 0x1f07 }, + { 200, 0x0782 }, + { 206.25, 0x1006 }, + { 212.5, 0x1086 }, + { 218.75, 0x1106 }, + { 225, 0x0882 }, + { 237.5, 0x1286 }, + { 243.75, 0x1306 }, + { 250, 0x0982 }, }; #define END_CONDITION 0x0000ffff @@ -229,8 +421,6 @@ struct ICARUS_INFO { // static int option_offset = -1; -struct device_drv icarus_drv; - /* #define ICA_BUFSIZ (0x200) @@ -262,6 +452,7 @@ static void _transfer(struct cgpu_info *icarus, uint8_t request_type, uint8_t bR static void icarus_initialise(struct cgpu_info *icarus, int baud) { + struct ICARUS_INFO *info = (struct ICARUS_INFO *)(icarus->device_data); uint16_t wValue, wIndex; enum sub_ident ident; int interface; @@ -269,10 +460,7 @@ static void icarus_initialise(struct cgpu_info *icarus, int baud) if (icarus->usbinfo.nodev) return; - usb_set_cps(icarus, baud / 10); - usb_enable_cps(icarus); - - interface = usb_interface(icarus); + interface = _usb_interface(icarus, info->intinfo); ident = usb_ident(icarus); switch (ident) { @@ -280,8 +468,6 @@ static void icarus_initialise(struct cgpu_info *icarus, int baud) case IDENT_LLT: case IDENT_CMR1: case IDENT_CMR2: - usb_set_pps(icarus, BLT_PREF_PACKET); - // Reset transfer(icarus, FTDI_TYPE_OUT, FTDI_REQUEST_RESET, FTDI_VALUE_RESET, interface, C_RESET); @@ -290,7 +476,7 @@ static void icarus_initialise(struct cgpu_info *icarus, int baud) return; // Latency - usb_ftdi_set_latency(icarus); + _usb_ftdi_set_latency(icarus, info->intinfo); if (icarus->usbinfo.nodev) return; @@ -355,8 +541,6 @@ static void icarus_initialise(struct cgpu_info *icarus, int baud) interface, C_PURGERX); break; case IDENT_ICA: - usb_set_pps(icarus, ICA_PREF_PACKET); - // Set Data Control transfer(icarus, PL2303_CTRL_OUT, PL2303_REQUEST_CTRL, PL2303_VALUE_CTRL, interface, C_SETDATA); @@ -377,8 +561,9 @@ static void icarus_initialise(struct cgpu_info *icarus, int baud) interface, C_VENDOR); break; case IDENT_AMU: - usb_set_pps(icarus, AMU_PREF_PACKET); - + case IDENT_ANU: + case IDENT_AU3: + case IDENT_LIN: // Enable the UART transfer(icarus, CP210X_TYPE_OUT, CP210X_REQUEST_IFC_ENABLE, CP210X_VALUE_UART_ENABLE, @@ -399,6 +584,8 @@ static void icarus_initialise(struct cgpu_info *icarus, int baud) _transfer(icarus, CP210X_TYPE_OUT, CP210X_REQUEST_BAUD, 0, interface, &data, sizeof(data), C_SETBAUD); break; + case IDENT_AVA: + break; default: quit(1, "icarus_intialise() called with invalid %s cgid %i ident=%d", icarus->drv->name, icarus->cgminer_id, ident); @@ -422,60 +609,45 @@ static void rev(unsigned char *s, size_t l) #define ICA_NONCE_RESTART 1 #define ICA_NONCE_TIMEOUT 2 -static int icarus_get_nonce(struct cgpu_info *icarus, unsigned char *buf, struct timeval *tv_start, struct timeval *tv_finish, struct thr_info *thr, int read_time) +static int icarus_get_nonce(struct cgpu_info *icarus, unsigned char *buf, struct timeval *tv_start, + struct timeval *tv_finish, struct thr_info *thr, int read_time) { - struct timeval read_start, read_finish; - int err, amt; - int rc = 0; - int read_amount = ICARUS_READ_SIZE; - bool first = true; - - cgtime(tv_start); - while (true) { - if (icarus->usbinfo.nodev) - return ICA_NONCE_ERROR; - - cgtime(&read_start); - err = usb_read_timeout(icarus, (char *)buf, read_amount, &amt, ICARUS_WAIT_TIMEOUT, C_GETRESULTS); - cgtime(&read_finish); - if (err < 0 && err != LIBUSB_ERROR_TIMEOUT) { - applog(LOG_ERR, "%s%i: Comms error (rerr=%d amt=%d)", - icarus->drv->name, icarus->device_id, err, amt); - dev_error(icarus, REASON_DEV_COMMS_ERROR); - return ICA_NONCE_ERROR; - } - - if (first) - copy_time(tv_finish, &read_finish); + struct ICARUS_INFO *info = (struct ICARUS_INFO *)(icarus->device_data); + int err, amt, rc; - if (amt >= read_amount) - return ICA_NONCE_OK; + if (icarus->usbinfo.nodev) + return ICA_NONCE_ERROR; - rc = SECTOMS(tdiff(&read_finish, tv_start)); - if (rc >= read_time) { - if (amt > 0) - applog(LOG_DEBUG, "Icarus Read: Timeout reading for %d ms", rc); - else - applog(LOG_DEBUG, "Icarus Read: No data for %d ms", rc); - return ICA_NONCE_TIMEOUT; - } + cgtime(tv_start); + err = usb_read_ii_timeout_cancellable(icarus, info->intinfo, (char *)buf, + info->nonce_size, &amt, read_time, + C_GETRESULTS); + cgtime(tv_finish); + + if (err < 0 && err != LIBUSB_ERROR_TIMEOUT) { + applog(LOG_ERR, "%s %i: Comms error (rerr=%d amt=%d)", icarus->drv->name, + icarus->device_id, err, amt); + dev_error(icarus, REASON_DEV_COMMS_ERROR); + return ICA_NONCE_ERROR; + } - if (thr && thr->work_restart) { - if (opt_debug) { - applog(LOG_DEBUG, - "Icarus Read: Work restart at %d ms", rc); - } - return ICA_NONCE_RESTART; - } + if (amt >= info->nonce_size) + return ICA_NONCE_OK; - if (amt > 0) { - buf += amt; - read_amount -= amt; - first = false; - } + rc = SECTOMS(tdiff(tv_finish, tv_start)); + if (thr && thr->work_restart) { + applog(LOG_DEBUG, "Icarus Read: Work restart at %d ms", rc); + return ICA_NONCE_RESTART; } + + if (amt > 0) + applog(LOG_DEBUG, "Icarus Read: Timeout reading for %d ms", rc); + else + applog(LOG_DEBUG, "Icarus Read: No data for %d ms", rc); + return ICA_NONCE_TIMEOUT; } + static const char *timing_mode_str(enum timing_mode timing_mode) { switch(timing_mode) { @@ -495,8 +667,9 @@ static const char *timing_mode_str(enum timing_mode timing_mode) static void set_timing_mode(int this_option_offset, struct cgpu_info *icarus) { struct ICARUS_INFO *info = (struct ICARUS_INFO *)(icarus->device_data); + int read_count_timing = 0; enum sub_ident ident; - double Hs; + double Hs, fail_time; char buf[BUFSIZ+1]; char *ptr, *comma, *eq; size_t max; @@ -528,19 +701,34 @@ static void set_timing_mode(int this_option_offset, struct cgpu_info *icarus) ident = usb_ident(icarus); switch (ident) { case IDENT_ICA: + case IDENT_AVA: info->Hs = ICARUS_REV3_HASH_TIME; + read_count_timing = ICARUS_READ_COUNT_TIMING; break; case IDENT_BLT: case IDENT_LLT: info->Hs = LANCELOT_HASH_TIME; + read_count_timing = ICARUS_READ_COUNT_TIMING; break; case IDENT_AMU: info->Hs = ASICMINERUSB_HASH_TIME; + read_count_timing = ICARUS_READ_COUNT_TIMING; break; - // TODO: ? case IDENT_CMR1: - case IDENT_CMR2: info->Hs = CAIRNSMORE1_HASH_TIME; + read_count_timing = ICARUS_READ_COUNT_TIMING; + break; + case IDENT_CMR2: + info->Hs = CAIRNSMORE2_HASH_TIME; + read_count_timing = ICARUS_READ_COUNT_TIMING; + break; + case IDENT_ANU: + info->Hs = ANTMINERUSB_HASH_TIME; + read_count_timing = ANTUSB_READ_COUNT_TIMING; + break; + case IDENT_AU3: + info->Hs = ANTU3_HASH_TIME; + read_count_timing = ANTU3_READ_COUNT_TIMING; break; default: quit(1, "Icarus get_options() called with invalid %s ident=%d", @@ -552,13 +740,13 @@ static void set_timing_mode(int this_option_offset, struct cgpu_info *icarus) if (strcasecmp(buf, MODE_SHORT_STR) == 0) { // short - info->read_time = ICARUS_READ_COUNT_TIMING; + info->read_time = read_count_timing; info->timing_mode = MODE_SHORT; info->do_icarus_timing = true; } else if (strncasecmp(buf, MODE_SHORT_STREQ, strlen(MODE_SHORT_STREQ)) == 0) { // short=limit - info->read_time = ICARUS_READ_COUNT_TIMING; + info->read_time = read_count_timing; info->timing_mode = MODE_SHORT; info->do_icarus_timing = true; @@ -570,13 +758,13 @@ static void set_timing_mode(int this_option_offset, struct cgpu_info *icarus) info->read_time_limit = ICARUS_READ_TIME_LIMIT_MAX; } else if (strcasecmp(buf, MODE_LONG_STR) == 0) { // long - info->read_time = ICARUS_READ_COUNT_TIMING; + info->read_time = read_count_timing; info->timing_mode = MODE_LONG; info->do_icarus_timing = true; } else if (strncasecmp(buf, MODE_LONG_STREQ, strlen(MODE_LONG_STREQ)) == 0) { // long=limit - info->read_time = ICARUS_READ_COUNT_TIMING; + info->read_time = read_count_timing; info->timing_mode = MODE_LONG; info->do_icarus_timing = true; @@ -629,6 +817,11 @@ static void set_timing_mode(int this_option_offset, struct cgpu_info *icarus) icarus->drv->name, icarus->cgminer_id, timing_mode_str(info->timing_mode), info->read_time, info->read_time_limit, info->Hs); + + /* Set the time to detect a dead device to 30 full nonce ranges. */ + fail_time = info->Hs * 0xffffffffull * 30.0; + /* Integer accuracy is definitely enough. */ + info->fail_time = fail_time; } static uint32_t mask(int work_division) @@ -692,22 +885,28 @@ static void get_options(int this_option_offset, struct cgpu_info *icarus, int *b case IDENT_ICA: case IDENT_BLT: case IDENT_LLT: + case IDENT_AVA: *baud = ICARUS_IO_SPEED; *work_division = 2; *fpga_count = 2; break; case IDENT_AMU: + case IDENT_ANU: + case IDENT_AU3: *baud = ICARUS_IO_SPEED; *work_division = 1; *fpga_count = 1; break; - // TODO: ? case IDENT_CMR1: - case IDENT_CMR2: *baud = ICARUS_IO_SPEED; *work_division = 2; *fpga_count = 2; break; + case IDENT_CMR2: + *baud = ICARUS_IO_SPEED; + *work_division = 1; + *fpga_count = 1; + break; default: quit(1, "Icarus get_options() called with invalid %s ident=%d", icarus->drv->name, ident); @@ -759,7 +958,221 @@ static void get_options(int this_option_offset, struct cgpu_info *icarus, int *b } } -static bool icarus_detect_one(struct libusb_device *dev, struct usb_find_devices *found) +unsigned char crc5(unsigned char *ptr, unsigned char len) +{ + unsigned char i, j, k; + unsigned char crc = 0x1f; + + unsigned char crcin[5] = {1, 1, 1, 1, 1}; + unsigned char crcout[5] = {1, 1, 1, 1, 1}; + unsigned char din = 0; + + j = 0x80; + k = 0; + for (i = 0; i < len; i++) { + if (*ptr & j) + din = 1; + else + din = 0; + crcout[0] = crcin[4] ^ din; + crcout[1] = crcin[0]; + crcout[2] = crcin[1] ^ crcin[4] ^ din; + crcout[3] = crcin[2]; + crcout[4] = crcin[3]; + + j = j >> 1; + k++; + if (k == 8) { + j = 0x80; + k = 0; + ptr++; + } + memcpy(crcin, crcout, 5); + } + crc = 0; + if (crcin[4]) + crc |= 0x10; + if (crcin[3]) + crc |= 0x08; + if (crcin[2]) + crc |= 0x04; + if (crcin[1]) + crc |= 0x02; + if (crcin[0]) + crc |= 0x01; + return crc; +} + +static uint16_t anu_find_freqhex(void) +{ + float fout, best_fout = opt_anu_freq; + int od, nf, nr, no, n, m, bs; + uint16_t anu_freq_hex = 0; + float best_diff = 1000; + + if (!best_fout) + best_fout = ANT_U1_DEFFREQ; + + for (od = 0; od < 4; od++) { + no = 1 << od; + for (n = 0; n < 16; n++) { + nr = n + 1; + for (m = 0; m < 64; m++) { + nf = m + 1; + fout = 25 * (float)nf /((float)(nr) * (float)(no)); + if (fabsf(fout - opt_anu_freq) > best_diff) + continue; + if (500 <= (fout * no) && (fout * no) <= 1000) + bs = 1; + else + bs = 0; + best_diff = fabsf(fout - opt_anu_freq); + best_fout = fout; + anu_freq_hex = (bs << 14) | (m << 7) | (n << 2) | od; + if (fout == opt_anu_freq) { + applog(LOG_DEBUG, "ANU found exact frequency %.1f with hex %04x", + opt_anu_freq, anu_freq_hex); + goto out; + } + } + } + } + applog(LOG_NOTICE, "ANU found nearest frequency %.1f with hex %04x", best_fout, + anu_freq_hex); +out: + return anu_freq_hex; +} + +static uint16_t anu3_find_freqhex(void) +{ + int i = 0, freq = opt_au3_freq, u3freq; + uint16_t anu_freq_hex = 0x0882; + + if (!freq) + freq = ANT_U3_DEFFREQ; + + do { + u3freq = u3freqtable[i].freq; + if (u3freq <= freq) + anu_freq_hex = u3freqtable[i].hex; + i++; + } while (u3freq < ANT_U3_MAXFREQ); + + return anu_freq_hex; +} + +static bool set_anu_freq(struct cgpu_info *icarus, struct ICARUS_INFO *info, uint16_t anu_freq_hex) +{ + unsigned char cmd_buf[4], rdreg_buf[4]; + int amount, err; + char buf[512]; + + if (!anu_freq_hex) + anu_freq_hex = anu_find_freqhex(); + memset(cmd_buf, 0, 4); + memset(rdreg_buf, 0, 4); + cmd_buf[0] = 2 | 0x80; + cmd_buf[1] = (anu_freq_hex & 0xff00u) >> 8; + cmd_buf[2] = (anu_freq_hex & 0x00ffu); + cmd_buf[3] = crc5(cmd_buf, 27); + + rdreg_buf[0] = 4 | 0x80; + rdreg_buf[1] = 0; //16-23 + rdreg_buf[2] = 0x04; //8-15 + rdreg_buf[3] = crc5(rdreg_buf, 27); + + applog(LOG_DEBUG, "%s %i: Send frequency %02x%02x%02x%02x", icarus->drv->name, icarus->device_id, + cmd_buf[0], cmd_buf[1], cmd_buf[2], cmd_buf[3]); + err = usb_write_ii(icarus, info->intinfo, (char *)cmd_buf, 4, &amount, C_ANU_SEND_CMD); + if (err != LIBUSB_SUCCESS || amount != 4) { + applog(LOG_ERR, "%s %i: Write freq Comms error (werr=%d amount=%d)", + icarus->drv->name, icarus->device_id, err, amount); + return false; + } + err = usb_read_ii_timeout(icarus, info->intinfo, buf, 512, &amount, 100, C_GETRESULTS); + if (err < 0 && err != LIBUSB_ERROR_TIMEOUT) { + applog(LOG_ERR, "%s %i: Read freq Comms error (rerr=%d amount=%d)", + icarus->drv->name, icarus->device_id, err, amount); + return false; + } + + applog(LOG_DEBUG, "%s %i: Send freq getstatus %02x%02x%02x%02x", icarus->drv->name, icarus->device_id, + rdreg_buf[0], rdreg_buf[1], rdreg_buf[2], rdreg_buf[3]); + err = usb_write_ii(icarus, info->intinfo, (char *)cmd_buf, 4, &amount, C_ANU_SEND_RDREG); + if (err != LIBUSB_SUCCESS || amount != 4) { + applog(LOG_ERR, "%s %i: Write freq Comms error (werr=%d amount=%d)", + icarus->drv->name, icarus->device_id, err, amount); + return false; + } + err = usb_read_ii_timeout(icarus, info->intinfo, buf, 512, &amount, 100, C_GETRESULTS); + if (err < 0 && err != LIBUSB_ERROR_TIMEOUT) { + applog(LOG_ERR, "%s %i: Read freq Comms error (rerr=%d amount=%d)", + icarus->drv->name, icarus->device_id, err, amount); + return false; + } + + return true; +} + +static void set_anu_volt(struct cgpu_info *icarus) +{ + unsigned char voltage_data[2], cmd_buf[4]; + char volt_buf[8]; + int err, amount; + + /* Allow a zero setting to imply not to try and set voltage */ + if (!opt_au3_volt) + return; + if (opt_au3_volt < 725 || opt_au3_volt > 850) { + applog(LOG_WARNING, "Invalid ANU voltage %d specified, must be 725-850", opt_au3_volt); + return; + } + sprintf(volt_buf, "%04d", opt_au3_volt); + hex2bin(voltage_data, volt_buf, 2); + cmd_buf[0] = 0xaa; + cmd_buf[1] = voltage_data[0]; + cmd_buf[1] &=0x0f; + cmd_buf[1] |=0xb0; + cmd_buf[2] = voltage_data[1]; + cmd_buf[3] = 0x00; //0-7 + cmd_buf[3] = crc5(cmd_buf, 4*8 - 5); + cmd_buf[3] |= 0xc0; + applog(LOG_INFO, "Send ANU voltage %02x%02x%02x%02x", cmd_buf[0], cmd_buf[1], cmd_buf[2], cmd_buf[3]); + cgsleep_ms(500); + err = usb_write(icarus, (char * )cmd_buf, 4, &amount, C_ANU_SEND_VOLT); + if (err != LIBUSB_SUCCESS || amount != 4) + applog(LOG_ERR, "Write voltage Comms error (werr=%d amount=%d)", err, amount); +} + +static void rock_init_last_received_task_complete_time(struct ICARUS_INFO *info) +{ + int i; + + if (opt_rock_freq < info->rmdev.min_frq || + opt_rock_freq > info->rmdev.max_frq) + opt_rock_freq = info->rmdev.def_frq; + + for (i = 0; i < MAX_CHIP_NUM; ++i) { + info->rmdev.chip[i].last_received_task_complete_time = time(NULL); + info->rmdev.chip[i].freq = opt_rock_freq/10 - 1; + info->rmdev.chip[i].error_cnt = 0; + } + + info->rmdev.dev_detect_time = time(NULL); +} + + +static void icarus_clear(struct cgpu_info *icarus, struct ICARUS_INFO *info) +{ + char buf[512]; + int amt; + + do { + usb_read_ii_timeout(icarus, info->intinfo, buf, 512, &amt, 100, C_GETRESULTS); + } while (amt > 0); +} + +static struct cgpu_info *icarus_detect_one(struct libusb_device *dev, struct usb_find_devices *found) { int this_option_offset = ++option_offset; struct ICARUS_INFO *info; @@ -777,44 +1190,134 @@ static bool icarus_detect_one(struct libusb_device *dev, struct usb_find_devices const char golden_nonce[] = "000187a2"; const uint32_t golden_nonce_val = 0x000187a2; - unsigned char ob_bin[64], nonce_bin[ICARUS_READ_SIZE]; + unsigned char nonce_bin[ICARUS_READ_SIZE]; + struct ICARUS_WORK workdata; char *nonce_hex; int baud, uninitialised_var(work_division), uninitialised_var(fpga_count); + bool anu_freqset = false; struct cgpu_info *icarus; - int ret, err, amount, tries; + int ret, err, amount, tries, i; bool ok; + bool cmr2_ok[CAIRNSMORE2_INTS]; + int cmr2_count; + + if ((sizeof(workdata) << 1) != (sizeof(golden_ob) - 1)) + quithere(1, "Data and golden_ob sizes don't match"); icarus = usb_alloc_cgpu(&icarus_drv, 1); if (!usb_init(icarus, dev, found)) goto shin; - usb_buffer_enable(icarus); - get_options(this_option_offset, icarus, &baud, &work_division, &fpga_count); - hex2bin(ob_bin, golden_ob, sizeof(ob_bin)); + hex2bin((void *)(&workdata), golden_ob, sizeof(workdata)); + + info = (struct ICARUS_INFO *)calloc(1, sizeof(struct ICARUS_INFO)); + if (unlikely(!info)) + quit(1, "Failed to malloc ICARUS_INFO"); + icarus->device_data = (void *)info; + + info->ident = usb_ident(icarus); + switch (info->ident) { + case IDENT_ICA: + case IDENT_AVA: + case IDENT_BLT: + case IDENT_LLT: + case IDENT_AMU: + case IDENT_CMR1: + info->timeout = ICARUS_WAIT_TIMEOUT; + break; + case IDENT_ANU: + case IDENT_AU3: + info->timeout = ANT_WAIT_TIMEOUT; + break; + case IDENT_CMR2: + if (found->intinfo_count != CAIRNSMORE2_INTS) { + quithere(1, "CMR2 Interface count (%d) isn't expected: %d", + found->intinfo_count, + CAIRNSMORE2_INTS); + } + info->timeout = ICARUS_CMR2_TIMEOUT; + cmr2_count = 0; + for (i = 0; i < CAIRNSMORE2_INTS; i++) + cmr2_ok[i] = false; + break; + default: + quit(1, "%s icarus_detect_one() invalid %s ident=%d", + icarus->drv->dname, icarus->drv->dname, info->ident); + } + + info->nonce_size = ICARUS_READ_SIZE; +// For CMR2 test each USB Interface + +retry: tries = 2; ok = false; while (!ok && tries-- > 0) { + icarus_clear(icarus, info); icarus_initialise(icarus, baud); - err = usb_write(icarus, (char *)ob_bin, sizeof(ob_bin), &amount, C_SENDTESTWORK); + if (info->u3) { + uint16_t anu_freq_hex = anu3_find_freqhex(); + + set_anu_volt(icarus); + if (!set_anu_freq(icarus, info, anu_freq_hex)) { + applog(LOG_WARNING, "%s %i: Failed to set frequency, too much overclock?", + icarus->drv->name, icarus->device_id); + continue; + } + icarus->usbdev->ident = info->ident = IDENT_AU3; + info->Hs = ANTU3_HASH_TIME; + icarus->drv->name = "AU3"; + applog(LOG_DEBUG, "%s %i: Detected Antminer U3", icarus->drv->name, + icarus->device_id); + } else if (info->ident == IDENT_ANU && !info->u3) { + if (!set_anu_freq(icarus, info, 0)) { + applog(LOG_WARNING, "%s %i: Failed to set frequency, too much overclock?", + icarus->drv->name, icarus->device_id); + continue; + } + } + + err = usb_write_ii(icarus, info->intinfo, + (char *)(&workdata), sizeof(workdata), &amount, C_SENDWORK); - if (err != LIBUSB_SUCCESS || amount != sizeof(ob_bin)) + if (err != LIBUSB_SUCCESS || amount != sizeof(workdata)) continue; memset(nonce_bin, 0, sizeof(nonce_bin)); - ret = icarus_get_nonce(icarus, nonce_bin, &tv_start, &tv_finish, NULL, 100); + ret = icarus_get_nonce(icarus, nonce_bin, &tv_start, &tv_finish, NULL, 300); if (ret != ICA_NONCE_OK) continue; + if (info->nonce_size == ICARUS_READ_SIZE && usb_buffer_size(icarus) == 4) { + applog(LOG_DEBUG, "%s %i: Detected Rockminer, deferring detection", + icarus->drv->name, icarus->device_id); + usb_buffer_clear(icarus); + break; + + } + if (info->nonce_size == ICARUS_READ_SIZE && usb_buffer_size(icarus) == 1) { + info->ant = true; + usb_buffer_clear(icarus); + icarus->usbdev->ident = info->ident = IDENT_ANU; + info->nonce_size = ANT_READ_SIZE; + info->Hs = ANTMINERUSB_HASH_TIME; + icarus->drv->name = "ANU"; + applog(LOG_DEBUG, "%s %i: Detected Antminer U1/2/3, changing nonce size to %d", + icarus->drv->name, icarus->device_id, ANT_READ_SIZE); + } + nonce_hex = bin2hex(nonce_bin, sizeof(nonce_bin)); - if (strncmp(nonce_hex, golden_nonce, 8) == 0) - ok = true; - else { - if (tries < 0) { + if (strncmp(nonce_hex, golden_nonce, 8) == 0) { + if (info->ant && !anu_freqset) + anu_freqset = true; + else + ok = true; + } else { + if (tries < 0 && info->ident != IDENT_CMR2) { applog(LOG_ERR, "Icarus Detect: " "Test failed at %s: get %s, should: %s", @@ -824,13 +1327,54 @@ static bool icarus_detect_one(struct libusb_device *dev, struct usb_find_devices free(nonce_hex); } - if (!ok) - goto unshin; + if (!ok) { + if (info->ident != IDENT_CMR2) { + if (info->u3) + goto unshin; + info->u3 = true; + goto retry; + } + + if (info->intinfo < CAIRNSMORE2_INTS-1) { + info->intinfo++; + goto retry; + } + } else { + if (info->ident == IDENT_CMR2) { + applog(LOG_DEBUG, + "Icarus Detect: " + "Test succeeded at %s i%d: got %s", + icarus->device_path, info->intinfo, golden_nonce); + + cmr2_ok[info->intinfo] = true; + cmr2_count++; + if (info->intinfo < CAIRNSMORE2_INTS-1) { + info->intinfo++; + goto retry; + } + } + } + + if (info->ident == IDENT_CMR2) { + if (cmr2_count == 0) { + applog(LOG_ERR, + "Icarus Detect: Test failed at %s: for all %d CMR2 Interfaces", + icarus->device_path, CAIRNSMORE2_INTS); + goto unshin; + } - applog(LOG_DEBUG, - "Icarus Detect: " - "Test succeeded at %s: got %s", - icarus->device_path, golden_nonce); + // set the interface to the first one that succeeded + for (i = 0; i < CAIRNSMORE2_INTS; i++) + if (cmr2_ok[i]) { + info->intinfo = i; + break; + } + } else { + applog(LOG_DEBUG, + "Icarus Detect: " + "Test succeeded at %s: got %s", + icarus->device_path, golden_nonce); + } /* We have a real Icarus! */ if (!add_cgpu(icarus)) @@ -838,20 +1382,23 @@ static bool icarus_detect_one(struct libusb_device *dev, struct usb_find_devices update_usb_stats(icarus); - applog(LOG_INFO, "%s%d: Found at %s", + applog(LOG_INFO, "%s %d: Found at %s", icarus->drv->name, icarus->device_id, icarus->device_path); - applog(LOG_DEBUG, "%s%d: Init baud=%d work_division=%d fpga_count=%d", - icarus->drv->name, icarus->device_id, baud, work_division, fpga_count); - - info = (struct ICARUS_INFO *)malloc(sizeof(struct ICARUS_INFO)); - if (unlikely(!info)) - quit(1, "Failed to malloc ICARUS_INFO"); + if (info->ident == IDENT_CMR2) { + applog(LOG_INFO, "%s %d: with %d Interface%s", + icarus->drv->name, icarus->device_id, + cmr2_count, cmr2_count > 1 ? "s" : ""); - icarus->device_data = (void *)info; + // Assume 1 or 2 are running FPGA pairs + if (cmr2_count < 3) { + work_division = fpga_count = 2; + info->Hs /= 2; + } + } - // Initialise everything to zero for a new device - memset(info, 0, sizeof(struct ICARUS_INFO)); + applog(LOG_DEBUG, "%s %d: Init baud=%d work_division=%d fpga_count=%d", + icarus->drv->name, icarus->device_id, baud, work_division, fpga_count); info->baud = baud; info->work_division = work_division; @@ -862,56 +1409,588 @@ static bool icarus_detect_one(struct libusb_device *dev, struct usb_find_devices timersub(&tv_finish, &tv_start, &(info->golden_tv)); set_timing_mode(this_option_offset, icarus); + + if (info->ident == IDENT_CMR2) { + int i; + for (i = info->intinfo + 1; i < icarus->usbdev->found->intinfo_count; i++) { + struct cgpu_info *cgtmp; + struct ICARUS_INFO *intmp; + + if (!cmr2_ok[i]) + continue; + + cgtmp = usb_copy_cgpu(icarus); + if (!cgtmp) { + applog(LOG_ERR, "%s %d: Init failed initinfo %d", + icarus->drv->name, icarus->device_id, i); + continue; + } - return true; + cgtmp->usbinfo.usbstat = USB_NOSTAT; + + intmp = (struct ICARUS_INFO *)malloc(sizeof(struct ICARUS_INFO)); + if (unlikely(!intmp)) + quit(1, "Failed2 to malloc ICARUS_INFO"); + + cgtmp->device_data = (void *)intmp; + + // Initialise everything to match + memcpy(intmp, info, sizeof(struct ICARUS_INFO)); + + intmp->intinfo = i; + + icarus_initialise(cgtmp, baud); + + if (!add_cgpu(cgtmp)) { + usb_uninit(cgtmp); + free(intmp); + continue; + } + + update_usb_stats(cgtmp); + } + } + + return icarus; + +unshin: + + usb_uninit(icarus); + free(info); + icarus->device_data = NULL; + +shin: + + icarus = usb_free_cgpu(icarus); + + return NULL; +} + +static int64_t rock_scanwork(struct thr_info *thr); + +static void rock_statline_before(char *buf, size_t bufsiz, struct cgpu_info *cgpu) +{ + if (cgpu->temp) + tailsprintf(buf, bufsiz, "%3.0fMHz %3.0fC", opt_rock_freq, cgpu->temp); + else + tailsprintf(buf, bufsiz, "%.0fMHz", opt_rock_freq); +} + +/* The only thing to do on flush_work is to remove the base work to prevent us + * rolling what is now stale work */ +static void rock_flush(struct cgpu_info *icarus) +{ + struct ICARUS_INFO *info = icarus->device_data; + struct work *work; + + mutex_lock(&info->lock); + work = info->base_work; + info->base_work = NULL; + mutex_unlock(&info->lock); + + if (work) + free_work(work); +} + +static struct cgpu_info *rock_detect_one(struct libusb_device *dev, struct usb_find_devices *found) +{ + struct ICARUS_INFO *info; + struct timeval tv_start, tv_finish; + char *ob_hex = NULL; + + // Block 171874 nonce = (0xa2870100) = 0x000187a2 + // N.B. golden_ob MUST take less time to calculate + // than the timeout set in icarus_open() + // This one takes ~0.53ms on Rev3 Icarus + const char golden_ob[] = + "4679ba4ec99876bf4bfe086082b40025" + "4df6c356451471139a3afa71e48f544a" + "00000000000000000000000000000000" + "aa1ff05587320b1a1426674f2fa722ce"; + + const char golden_nonce[] = "000187a2"; + const uint32_t golden_nonce_val = 0x000187a2; + unsigned char nonce_bin[ROCK_READ_SIZE]; + struct ICARUS_WORK workdata; + char *nonce_hex; + struct cgpu_info *icarus; + int ret, err, amount, tries; + bool ok; + int correction_times = 0; + NONCE_DATA nonce_data; + uint32_t nonce; + char *newname = NULL; + + if ((sizeof(workdata) << 1) != (sizeof(golden_ob) - 1)) + quithere(1, "Data and golden_ob sizes don't match"); + + icarus = usb_alloc_cgpu(&icarus_drv, 1); + + if (!usb_init(icarus, dev, found)) + goto shin; + + hex2bin((void *)(&workdata), golden_ob, sizeof(workdata)); + rev((void *)(&(workdata.midstate)), ICARUS_MIDSTATE_SIZE); + rev((void *)(&(workdata.work)), ICARUS_WORK_SIZE); + if (opt_debug) { + ob_hex = bin2hex((void *)(&workdata), sizeof(workdata)); + applog(LOG_WARNING, "%s %d: send_gold_nonce %s", + icarus->drv->name, icarus->device_id, ob_hex); + free(ob_hex); + } + + info = (struct ICARUS_INFO *)calloc(1, sizeof(struct ICARUS_INFO)); + if (unlikely(!info)) + quit(1, "Failed to malloc ICARUS_INFO"); + (void)memset(info, 0, sizeof(struct ICARUS_INFO)); + icarus->device_data = (void *)info; + icarus->usbdev->ident = info->ident = IDENT_LIN; + info->nonce_size = ROCK_READ_SIZE; + info->fail_time = 10; + info->nonce_mask = 0xffffffff; + update_usb_stats(icarus); + + tries = MAX_TRIES; + ok = false; + while (!ok && tries-- > 0) { + icarus_initialise(icarus, info->baud); + + applog(LOG_DEBUG, "tries: %d", tries); + workdata.unused[ICARUS_UNUSED_SIZE - 3] = opt_rock_freq/10 - 1; + workdata.unused[ICARUS_UNUSED_SIZE - 2] = (MAX_TRIES-1-tries); + info->rmdev.detect_chip_no++; + if (info->rmdev.detect_chip_no >= MAX_TRIES) + info->rmdev.detect_chip_no = 0; + //g_detect_chip_no = (g_detect_chip_no + 1) & MAX_CHIP_NUM; + + usb_buffer_clear(icarus); + err = usb_write_ii(icarus, info->intinfo, + (char *)(&workdata), sizeof(workdata), &amount, C_SENDWORK); + if (err != LIBUSB_SUCCESS || amount != sizeof(workdata)) + continue; + + memset(nonce_bin, 0, sizeof(nonce_bin)); + ret = icarus_get_nonce(icarus, nonce_bin, &tv_start, &tv_finish, NULL, 100); + + applog(LOG_DEBUG, "Rockminer nonce_bin: %02x %02x %02x %02x %02x %02x %02x %02x", + nonce_bin[0], nonce_bin[1], nonce_bin[2], nonce_bin[3], + nonce_bin[4], nonce_bin[5], nonce_bin[6], nonce_bin[7]); + if (ret != ICA_NONCE_OK) { + applog(LOG_DEBUG, "detect_one get_gold_nonce error, tries = %d", tries); + continue; + } + if (usb_buffer_size(icarus) == 1) { + applog(LOG_INFO, "Rock detect found an ANU, skipping"); + usb_buffer_clear(icarus); + break; + } + + newname = NULL; + switch (nonce_bin[NONCE_CHIP_NO_OFFSET] & RM_PRODUCT_MASK) { + case RM_PRODUCT_T1: + newname = "LIR"; // Rocketbox + info->rmdev.product_id = ROCKMINER_T1; + info->rmdev.chip_max = 12; + info->rmdev.min_frq = 200; + info->rmdev.def_frq = 330; + info->rmdev.max_frq = 400; + break; + case RM_PRODUCT_T2: // what's this? + newname = "LIX"; + info->rmdev.product_id = ROCKMINER_T2; + info->rmdev.chip_max = 16; + info->rmdev.min_frq = 200; + info->rmdev.def_frq = 300; + info->rmdev.max_frq = 400; + break; + case RM_PRODUCT_RBOX: + newname = "LIN"; // R-Box + info->rmdev.product_id = ROCKMINER_RBOX; + info->rmdev.chip_max = 4; + info->rmdev.min_frq = 200; + info->rmdev.def_frq = 270; + info->rmdev.max_frq = 400; + break; + default: + continue; + } + + snprintf(info->rock_init, sizeof(info->rock_init), "%02x %02x %02x %02x", + nonce_bin[4], nonce_bin[5], nonce_bin[6], nonce_bin[7]); + + nonce_data.chip_no = nonce_bin[NONCE_CHIP_NO_OFFSET] & RM_CHIP_MASK; + if (nonce_data.chip_no >= info->rmdev.chip_max) + nonce_data.chip_no = 0; + + nonce_data.cmd_value = nonce_bin[NONCE_TASK_CMD_OFFSET] & RM_CMD_MASK; + if (nonce_data.cmd_value == NONCE_TASK_COMPLETE_CMD) { + applog(LOG_DEBUG, "complete g_detect_chip_no: %d", info->rmdev.detect_chip_no); + workdata.unused[ICARUS_UNUSED_SIZE - 3] = opt_rock_freq/10 - 1; + workdata.unused[ICARUS_UNUSED_SIZE - 2] = info->rmdev.detect_chip_no; + info->rmdev.detect_chip_no++; + if (info->rmdev.detect_chip_no >= MAX_TRIES) + info->rmdev.detect_chip_no = 0; + + err = usb_write_ii(icarus, info->intinfo, + (char *)(&workdata), sizeof(workdata), &amount, C_SENDWORK); + if (err != LIBUSB_SUCCESS || amount != sizeof(workdata)) + continue; + applog(LOG_DEBUG, "send_gold_nonce usb_write_ii"); + continue; + } + + memcpy((char *)&nonce, nonce_bin, ICARUS_READ_SIZE); + nonce = htobe32(nonce); + applog(LOG_DEBUG, "Rockminer nonce: %08X", nonce); + correction_times = 0; + while (correction_times < NONCE_CORRECTION_TIMES) { + nonce_hex = bin2hex(nonce_bin, 4); + if (golden_nonce_val == nonce + rbox_corr_values[correction_times]) { + memset(&(info->g_work[0]), 0, sizeof(info->g_work)); + rock_init_last_received_task_complete_time(info); + + ok = true; + break; + } else { + applog(LOG_DEBUG, "detect_one gold_nonce compare error times = %d", + correction_times); + if (tries < 0 && info->ident != IDENT_CMR2) { + applog(LOG_WARNING, + "Icarus Detect: " + "Test failed at %s: get %s, should: %s", + icarus->device_path, nonce_hex, golden_nonce); + } + + if (nonce == 0) + break; + } + free(nonce_hex); + correction_times++; + } + } + + if (!ok) + goto unshin; + + if (newname) { + if (!icarus->drv->copy) + icarus->drv = copy_drv(icarus->drv); + icarus->drv->name = newname; + } + + applog(LOG_DEBUG, "Icarus Detect: Test succeeded at %s: got %s", + icarus->device_path, golden_nonce); + + /* We have a real Rockminer! */ + if (!add_cgpu(icarus)) + goto unshin; + + icarus->drv->scanwork = rock_scanwork; + icarus->drv->dname = "Rockminer"; + icarus->drv->get_statline_before = &rock_statline_before; + icarus->drv->flush_work = &rock_flush; + mutex_init(&info->lock); + + applog(LOG_INFO, "%s %d: Found at %s", + icarus->drv->name, icarus->device_id, + icarus->device_path); + + timersub(&tv_finish, &tv_start, &(info->golden_tv)); + + return icarus; unshin: usb_uninit(icarus); + free(info); + icarus->device_data = NULL; shin: icarus = usb_free_cgpu(icarus); - return false; + return NULL; } -static void icarus_detect() +static void icarus_detect(bool __maybe_unused hotplug) { + usb_detect(&icarus_drv, rock_detect_one); usb_detect(&icarus_drv, icarus_detect_one); } -static bool icarus_prepare(__maybe_unused struct thr_info *thr) +static bool icarus_prepare(struct thr_info *thr) { -// struct cgpu_info *icarus = thr->cgpu; + struct cgpu_info *icarus = thr->cgpu; + struct ICARUS_INFO *info = (struct ICARUS_INFO *)(icarus->device_data); + if (info->ant) + info->antworks = calloc(sizeof(struct work *), ANT_QUEUE_NUM); return true; } -static int64_t icarus_scanhash(struct thr_info *thr, struct work *work, - __maybe_unused int64_t max_nonce) +static void cmr2_command(struct cgpu_info *icarus, uint8_t cmd, uint8_t data) +{ + struct ICARUS_INFO *info = (struct ICARUS_INFO *)(icarus->device_data); + struct ICARUS_WORK workdata; + int amount; + + memset((void *)(&workdata), 0, sizeof(workdata)); + + workdata.prefix = ICARUS_CMR2_PREFIX; + workdata.cmd = cmd; + workdata.data = data; + workdata.check = workdata.data ^ workdata.cmd ^ workdata.prefix ^ ICARUS_CMR2_CHECK; + + usb_write_ii(icarus, info->intinfo, (char *)(&workdata), sizeof(workdata), &amount, C_SENDWORK); +} + +static void cmr2_commands(struct cgpu_info *icarus) +{ + struct ICARUS_INFO *info = (struct ICARUS_INFO *)(icarus->device_data); + + if (info->speed_next_work) { + info->speed_next_work = false; + cmr2_command(icarus, ICARUS_CMR2_CMD_SPEED, info->cmr2_speed); + return; + } + + if (info->flash_next_work) { + info->flash_next_work = false; + cmr2_command(icarus, ICARUS_CMR2_CMD_FLASH, ICARUS_CMR2_DATA_FLASH_ON); + cgsleep_ms(250); + cmr2_command(icarus, ICARUS_CMR2_CMD_FLASH, ICARUS_CMR2_DATA_FLASH_OFF); + cgsleep_ms(250); + cmr2_command(icarus, ICARUS_CMR2_CMD_FLASH, ICARUS_CMR2_DATA_FLASH_ON); + cgsleep_ms(250); + cmr2_command(icarus, ICARUS_CMR2_CMD_FLASH, ICARUS_CMR2_DATA_FLASH_OFF); + return; + } +} + +void rock_send_task(unsigned char chip_no, unsigned int current_task_id, struct thr_info *thr) { struct cgpu_info *icarus = thr->cgpu; struct ICARUS_INFO *info = (struct ICARUS_INFO *)(icarus->device_data); - int ret, err, amount; - unsigned char ob_bin[64], nonce_bin[ICARUS_READ_SIZE]; + int err, amount; + struct ICARUS_WORK workdata; char *ob_hex; - uint32_t nonce; - int64_t hash_count; - struct timeval tv_start, tv_finish, elapsed; - struct timeval tv_history_start, tv_history_finish; - double Ti, Xi; - int curr_hw_errors, i; - bool was_hw_error; + struct work *work = NULL; + + /* Only base_work needs locking since it can be asynchronously deleted + * by flush work */ + if (info->g_work[chip_no][current_task_id] == NULL) { + mutex_lock(&info->lock); + if (!info->base_work) + info->base_work = get_work(thr, thr->id); + if (info->base_work->drv_rolllimit > 0) { + info->base_work->drv_rolllimit--; + roll_work(info->base_work); + work = make_clone(info->base_work); + } else { + work = info->base_work; + info->base_work = NULL; + } + mutex_unlock(&info->lock); + + info->g_work[chip_no][current_task_id] = work; + } else { + work = info->g_work[chip_no][current_task_id]; + applog(LOG_DEBUG, "::resend work"); + } + + memset((void *)(&workdata), 0, sizeof(workdata)); + memcpy(&(workdata.midstate), work->midstate, ICARUS_MIDSTATE_SIZE); + memcpy(&(workdata.work), work->data + ICARUS_WORK_DATA_OFFSET, ICARUS_WORK_SIZE); + workdata.unused[ICARUS_UNUSED_SIZE - 4] = 0xaa; + if (info->rmdev.chip[chip_no].freq > (info->rmdev.max_frq/10 - 1) || + info->rmdev.chip[chip_no].freq < (info->rmdev.min_frq/10 - 1)) + rock_init_last_received_task_complete_time(info); + + workdata.unused[ICARUS_UNUSED_SIZE - 3] = info->rmdev.chip[chip_no].freq; //icarus->freq/10 - 1; ; + workdata.unused[ICARUS_UNUSED_SIZE - 2] = chip_no ; + workdata.id = 0x55; + + if (opt_debug) { + ob_hex = bin2hex((void *)(work->data), 128); + applog(LOG_WARNING, "%s %d: work->data %s", + icarus->drv->name, icarus->device_id, ob_hex); + free(ob_hex); + } + + // We only want results for the work we are about to send + usb_buffer_clear(icarus); + + err = usb_write_ii(icarus, info->intinfo, (char *)(&workdata), sizeof(workdata), &amount, C_SENDWORK); + + if (err < 0 || amount != sizeof(workdata)) { + applog(LOG_ERR, "%s %i: Comms error (werr=%d amt=%d)", + icarus->drv->name, icarus->device_id, err, amount); + dev_error(icarus, REASON_DEV_COMMS_ERROR); + icarus_initialise(icarus, info->baud); + + if (info->g_work[chip_no][current_task_id]) + { + free_work(info->g_work[chip_no][current_task_id]); + info->g_work[chip_no][current_task_id] = NULL; + } + + return; + } + return; +} + +static void process_history(struct cgpu_info *icarus, struct ICARUS_INFO *info, uint32_t nonce, + uint64_t hash_count, struct timeval *elapsed, struct timeval *tv_start) +{ struct ICARUS_HISTORY *history0, *history; + struct timeval tv_history_start, tv_history_finish; int count; double Hs, W, fullnonce; - int read_time; + int read_time, i; bool limited; - int64_t estimate_hashes; uint32_t values; int64_t hash_count_range; + double Ti, Xi; + + // Ignore possible end condition values ... + // TODO: set limitations on calculated values depending on the device + // to avoid crap values caused by CPU/Task Switching/Swapping/etc + if ((nonce & info->nonce_mask) <= END_CONDITION || + (nonce & info->nonce_mask) >= (info->nonce_mask & ~END_CONDITION)) + return; + + cgtime(&tv_history_start); + + history0 = &(info->history[0]); + + if (history0->values == 0) + timeradd(tv_start, &history_sec, &(history0->finish)); + + Ti = (double)(elapsed->tv_sec) + + ((double)(elapsed->tv_usec))/((double)1000000) + - ((double)ICARUS_READ_TIME(info->baud)); + Xi = (double)hash_count; + history0->sumXiTi += Xi * Ti; + history0->sumXi += Xi; + history0->sumTi += Ti; + history0->sumXi2 += Xi * Xi; + + history0->values++; + + if (history0->hash_count_max < hash_count) + history0->hash_count_max = hash_count; + if (history0->hash_count_min > hash_count || history0->hash_count_min == 0) + history0->hash_count_min = hash_count; + + if (history0->values >= info->min_data_count + && timercmp(tv_start, &(history0->finish), >)) { + for (i = INFO_HISTORY; i > 0; i--) + memcpy(&(info->history[i]), + &(info->history[i-1]), + sizeof(struct ICARUS_HISTORY)); + + // Initialise history0 to zero for summary calculation + memset(history0, 0, sizeof(struct ICARUS_HISTORY)); + + // We just completed a history data set + // So now recalc read_time based on the whole history thus we will + // initially get more accurate until it completes INFO_HISTORY + // total data sets + count = 0; + for (i = 1 ; i <= INFO_HISTORY; i++) { + history = &(info->history[i]); + if (history->values >= MIN_DATA_COUNT) { + count++; + + history0->sumXiTi += history->sumXiTi; + history0->sumXi += history->sumXi; + history0->sumTi += history->sumTi; + history0->sumXi2 += history->sumXi2; + history0->values += history->values; + + if (history0->hash_count_max < history->hash_count_max) + history0->hash_count_max = history->hash_count_max; + if (history0->hash_count_min > history->hash_count_min || history0->hash_count_min == 0) + history0->hash_count_min = history->hash_count_min; + } + } + + // All history data + Hs = (history0->values*history0->sumXiTi - history0->sumXi*history0->sumTi) + / (history0->values*history0->sumXi2 - history0->sumXi*history0->sumXi); + W = history0->sumTi/history0->values - Hs*history0->sumXi/history0->values; + hash_count_range = history0->hash_count_max - history0->hash_count_min; + values = history0->values; + + // Initialise history0 to zero for next data set + memset(history0, 0, sizeof(struct ICARUS_HISTORY)); + + fullnonce = W + Hs * (((double)0xffffffff) + 1); + read_time = SECTOMS(fullnonce) - ICARUS_READ_REDUCE; + if (info->read_time_limit > 0 && read_time > info->read_time_limit) { + read_time = info->read_time_limit; + limited = true; + } else + limited = false; + + info->Hs = Hs; + info->read_time = read_time; + + info->fullnonce = fullnonce; + info->count = count; + info->W = W; + info->values = values; + info->hash_count_range = hash_count_range; + + if (info->min_data_count < MAX_MIN_DATA_COUNT) + info->min_data_count *= 2; + else if (info->timing_mode == MODE_SHORT) + info->do_icarus_timing = false; + + applog(LOG_WARNING, "%s %d Re-estimate: Hs=%e W=%e read_time=%dms%s fullnonce=%.3fs", + icarus->drv->name, icarus->device_id, Hs, W, read_time, + limited ? " (limited)" : "", fullnonce); + } + info->history_count++; + cgtime(&tv_history_finish); + + timersub(&tv_history_finish, &tv_history_start, &tv_history_finish); + timeradd(&tv_history_finish, &(info->history_time), &(info->history_time)); +} + +static int64_t icarus_scanwork(struct thr_info *thr) +{ + struct cgpu_info *icarus = thr->cgpu; + struct ICARUS_INFO *info = (struct ICARUS_INFO *)(icarus->device_data); + int ret, err, amount; + unsigned char nonce_bin[ICARUS_BUF_SIZE]; + struct ICARUS_WORK workdata; + char *ob_hex; + uint32_t nonce; + int64_t hash_count = 0; + struct timeval tv_start, tv_finish, elapsed; + int curr_hw_errors; + bool was_hw_error; + struct work *work; + int64_t estimate_hashes; + uint8_t workid = 0; + + if (unlikely(share_work_tdiff(icarus) > info->fail_time)) { + if (info->failing) { + if (share_work_tdiff(icarus) > info->fail_time + 60) { + applog(LOG_ERR, "%s %d: Device failed to respond to restart", + icarus->drv->name, icarus->device_id); + usb_nodev(icarus); + return -1; + } + } else { + applog(LOG_WARNING, "%s %d: No valid hashes for over %d secs, attempting to reset", + icarus->drv->name, icarus->device_id, info->fail_time); + usb_reset(icarus); + info->failing = true; + } + } // Device is gone if (icarus->usbinfo.nodev) @@ -919,41 +1998,57 @@ static int64_t icarus_scanhash(struct thr_info *thr, struct work *work, elapsed.tv_sec = elapsed.tv_usec = 0; - memset(ob_bin, 0, sizeof(ob_bin)); - memcpy(ob_bin, work->midstate, 32); - memcpy(ob_bin + 52, work->data + 64, 12); - rev(ob_bin, 32); - rev(ob_bin + 52, 12); + work = get_work(thr, thr->id); + memset((void *)(&workdata), 0, sizeof(workdata)); + memcpy(&(workdata.midstate), work->midstate, ICARUS_MIDSTATE_SIZE); + memcpy(&(workdata.work), work->data + ICARUS_WORK_DATA_OFFSET, ICARUS_WORK_SIZE); + rev((void *)(&(workdata.midstate)), ICARUS_MIDSTATE_SIZE); + rev((void *)(&(workdata.work)), ICARUS_WORK_SIZE); + if (info->ant) { + workid = info->workid; + if (++info->workid >= 0x1F) + info->workid = 0; + if (info->antworks[workid]) + free_work(info->antworks[workid]); + info->antworks[workid] = work; + workdata.id = workid; + } + + if (info->speed_next_work || info->flash_next_work) + cmr2_commands(icarus); // We only want results for the work we are about to send usb_buffer_clear(icarus); - err = usb_write(icarus, (char *)ob_bin, sizeof(ob_bin), &amount, C_SENDWORK); - if (err < 0 || amount != sizeof(ob_bin)) { - applog(LOG_ERR, "%s%i: Comms error (werr=%d amt=%d)", + err = usb_write_ii(icarus, info->intinfo, (char *)(&workdata), sizeof(workdata), &amount, C_SENDWORK); + if (err < 0 || amount != sizeof(workdata)) { + applog(LOG_ERR, "%s %i: Comms error (werr=%d amt=%d)", icarus->drv->name, icarus->device_id, err, amount); dev_error(icarus, REASON_DEV_COMMS_ERROR); icarus_initialise(icarus, info->baud); - return 0; + goto out; } if (opt_debug) { - ob_hex = bin2hex(ob_bin, sizeof(ob_bin)); - applog(LOG_DEBUG, "%s%d: sent %s", + ob_hex = bin2hex((void *)(&workdata), sizeof(workdata)); + applog(LOG_DEBUG, "%s %d: sent %s", icarus->drv->name, icarus->device_id, ob_hex); free(ob_hex); } - - /* Icarus will return 4 bytes (ICARUS_READ_SIZE) nonces or nothing */ +more_nonces: + /* Icarus will return nonces or nothing. If we know we have enough data + * for a response in the buffer already, there will be no usb read + * performed. */ memset(nonce_bin, 0, sizeof(nonce_bin)); ret = icarus_get_nonce(icarus, nonce_bin, &tv_start, &tv_finish, thr, info->read_time); if (ret == ICA_NONCE_ERROR) - return 0; - - work->blk.nonce = 0xffffffff; + goto out; // aborted before becoming idle, get new work if (ret == ICA_NONCE_TIMEOUT || ret == ICA_NONCE_RESTART) { + if (info->ant) + goto out; + timersub(&tv_finish, &tv_start, &elapsed); // ONLY up to just when it aborted @@ -966,32 +2061,48 @@ static int64_t icarus_scanhash(struct thr_info *thr, struct work *work, if (unlikely(estimate_hashes > 0xffffffff)) estimate_hashes = 0xffffffff; - if (opt_debug) { - applog(LOG_DEBUG, "%s%d: no nonce = 0x%08lX hashes (%ld.%06lds)", - icarus->drv->name, icarus->device_id, - (long unsigned int)estimate_hashes, - elapsed.tv_sec, elapsed.tv_usec); - } + applog(LOG_DEBUG, "%s %d: no nonce = 0x%08lX hashes (%ld.%06lds)", + icarus->drv->name, icarus->device_id, + (long unsigned int)estimate_hashes, + (long)elapsed.tv_sec, (long)elapsed.tv_usec); - return estimate_hashes; + hash_count = estimate_hashes; + goto out; } - memcpy((char *)&nonce, nonce_bin, sizeof(nonce_bin)); + if (info->ant) { + workid = nonce_bin[4] & 0x1F; + if (info->antworks[workid]) + work = info->antworks[workid]; + else + goto out; + } + + memcpy((char *)&nonce, nonce_bin, ICARUS_READ_SIZE); nonce = htobe32(nonce); curr_hw_errors = icarus->hw_errors; - submit_nonce(thr, work, nonce); - was_hw_error = (curr_hw_errors > icarus->hw_errors); - - hash_count = (nonce & info->nonce_mask); - hash_count++; - hash_count *= info->fpga_count; + if (submit_nonce(thr, work, nonce)) + info->failing = false; + was_hw_error = (curr_hw_errors < icarus->hw_errors); + + /* U3s return shares fast enough to use just that for hashrate + * calculation, otherwise the result is inaccurate instead. */ + if (info->ant) { + info->nonces++; + if (usb_buffer_size(icarus) >= ANT_READ_SIZE) + goto more_nonces; + } else { + hash_count = (nonce & info->nonce_mask); + hash_count++; + hash_count *= info->fpga_count; + } #if 0 // This appears to only return zero nonce values if (usb_buffer_size(icarus) > 3) { memcpy((char *)&nonce, icarus->usbdev->buffer, sizeof(nonce_bin)); nonce = htobe32(nonce); - applog(LOG_WARNING, "%s%d: attempting to submit 2nd nonce = 0x%08lX", + applog(LOG_WARNING, "%s %d: attempting to submit 2nd nonce = 0x%08lX", icarus->drv->name, icarus->device_id, (long unsigned int)nonce); curr_hw_errors = icarus->hw_errors; @@ -1003,119 +2114,202 @@ static int64_t icarus_scanhash(struct thr_info *thr, struct work *work, if (opt_debug || info->do_icarus_timing) timersub(&tv_finish, &tv_start, &elapsed); - if (opt_debug) { - applog(LOG_DEBUG, "%s%d: nonce = 0x%08x = 0x%08lX hashes (%ld.%06lds)", - icarus->drv->name, icarus->device_id, - nonce, (long unsigned int)hash_count, - elapsed.tv_sec, elapsed.tv_usec); + applog(LOG_DEBUG, "%s %d: nonce = 0x%08x = 0x%08lX hashes (%ld.%06lds)", + icarus->drv->name, icarus->device_id, + nonce, (long unsigned int)hash_count, + (long)elapsed.tv_sec, (long)elapsed.tv_usec); + + if (info->do_icarus_timing && !was_hw_error) + process_history(icarus, info, nonce, hash_count, &elapsed, &tv_start); +out: + if (!info->ant) + free_work(work); + else { + /* Ant USBs free the work themselves. Return only one full + * nonce worth on each pass to smooth out displayed hashrate */ + if (info->nonces) { + hash_count = 0xffffffff; + info->nonces--; + } } - // Ignore possible end condition values ... and hw errors - // TODO: set limitations on calculated values depending on the device - // to avoid crap values caused by CPU/Task Switching/Swapping/etc - if (info->do_icarus_timing - && !was_hw_error - && ((nonce & info->nonce_mask) > END_CONDITION) - && ((nonce & info->nonce_mask) < (info->nonce_mask & ~END_CONDITION))) { - cgtime(&tv_history_start); - - history0 = &(info->history[0]); - - if (history0->values == 0) - timeradd(&tv_start, &history_sec, &(history0->finish)); - - Ti = (double)(elapsed.tv_sec) - + ((double)(elapsed.tv_usec))/((double)1000000) - - ((double)ICARUS_READ_TIME(info->baud)); - Xi = (double)hash_count; - history0->sumXiTi += Xi * Ti; - history0->sumXi += Xi; - history0->sumTi += Ti; - history0->sumXi2 += Xi * Xi; - - history0->values++; - - if (history0->hash_count_max < hash_count) - history0->hash_count_max = hash_count; - if (history0->hash_count_min > hash_count || history0->hash_count_min == 0) - history0->hash_count_min = hash_count; - - if (history0->values >= info->min_data_count - && timercmp(&tv_start, &(history0->finish), >)) { - for (i = INFO_HISTORY; i > 0; i--) - memcpy(&(info->history[i]), - &(info->history[i-1]), - sizeof(struct ICARUS_HISTORY)); - - // Initialise history0 to zero for summary calculation - memset(history0, 0, sizeof(struct ICARUS_HISTORY)); - - // We just completed a history data set - // So now recalc read_time based on the whole history thus we will - // initially get more accurate until it completes INFO_HISTORY - // total data sets - count = 0; - for (i = 1 ; i <= INFO_HISTORY; i++) { - history = &(info->history[i]); - if (history->values >= MIN_DATA_COUNT) { - count++; - - history0->sumXiTi += history->sumXiTi; - history0->sumXi += history->sumXi; - history0->sumTi += history->sumTi; - history0->sumXi2 += history->sumXi2; - history0->values += history->values; - - if (history0->hash_count_max < history->hash_count_max) - history0->hash_count_max = history->hash_count_max; - if (history0->hash_count_min > history->hash_count_min || history0->hash_count_min == 0) - history0->hash_count_min = history->hash_count_min; - } + return hash_count; +} + +static int64_t rock_scanwork(struct thr_info *thr) +{ + struct cgpu_info *icarus = thr->cgpu; + struct ICARUS_INFO *info = (struct ICARUS_INFO *)(icarus->device_data); + int ret; + unsigned char nonce_bin[ICARUS_BUF_SIZE]; + uint32_t nonce; + int64_t hash_count = 0; + struct timeval tv_start, tv_finish, elapsed; + struct work *work = NULL; + int64_t estimate_hashes; + int correction_times = 0; + NONCE_DATA nonce_data; + double temp; + + int chip_no = 0; + time_t recv_time = 0; + + if (unlikely(share_work_tdiff(icarus) > info->fail_time)) { + if (info->failing) { + if (share_work_tdiff(icarus) > info->fail_time + 60) { + applog(LOG_ERR, "%s %d: Device failed to respond to restart", + icarus->drv->name, icarus->device_id); + usb_nodev(icarus); + return -1; } + } else { + applog(LOG_WARNING, "%s %d: No valid hashes for over %d secs, attempting to reset", + icarus->drv->name, icarus->device_id, info->fail_time); + usb_reset(icarus); + info->failing = true; + } + } + + // Device is gone + if (icarus->usbinfo.nodev) + return -1; - // All history data - Hs = (history0->values*history0->sumXiTi - history0->sumXi*history0->sumTi) - / (history0->values*history0->sumXi2 - history0->sumXi*history0->sumXi); - W = history0->sumTi/history0->values - Hs*history0->sumXi/history0->values; - hash_count_range = history0->hash_count_max - history0->hash_count_min; - values = history0->values; - - // Initialise history0 to zero for next data set - memset(history0, 0, sizeof(struct ICARUS_HISTORY)); - - fullnonce = W + Hs * (((double)0xffffffff) + 1); - read_time = SECTOMS(fullnonce) - ICARUS_READ_REDUCE; - if (info->read_time_limit > 0 && read_time > info->read_time_limit) { - read_time = info->read_time_limit; - limited = true; - } else - limited = false; - - info->Hs = Hs; - info->read_time = read_time; - - info->fullnonce = fullnonce; - info->count = count; - info->W = W; - info->values = values; - info->hash_count_range = hash_count_range; - - if (info->min_data_count < MAX_MIN_DATA_COUNT) - info->min_data_count *= 2; - else if (info->timing_mode == MODE_SHORT) - info->do_icarus_timing = false; - - applog(LOG_WARNING, "%s%d Re-estimate: Hs=%e W=%e read_time=%dms%s fullnonce=%.3fs", - icarus->drv->name, icarus->device_id, Hs, W, read_time, - limited ? " (limited)" : "", fullnonce); + elapsed.tv_sec = elapsed.tv_usec = 0; + + for (chip_no = 0; chip_no < info->rmdev.chip_max; chip_no++) { + recv_time = time(NULL); + if (recv_time > info->rmdev.chip[chip_no].last_received_task_complete_time + 1) { + info->rmdev.chip[chip_no].last_received_task_complete_time = recv_time; + rock_send_task(chip_no, 0,thr); + break; } - info->history_count++; - cgtime(&tv_history_finish); + } + + memset(nonce_bin, 0, sizeof(nonce_bin)); + ret = icarus_get_nonce(icarus, nonce_bin, &tv_start, &tv_finish, thr, 3000);//info->read_time); + + nonce_data.chip_no = nonce_bin[NONCE_CHIP_NO_OFFSET] & RM_CHIP_MASK; + if (nonce_data.chip_no >= info->rmdev.chip_max) + nonce_data.chip_no = 0; + nonce_data.task_no = nonce_bin[NONCE_TASK_NO_OFFSET] & 0x1; + nonce_data.cmd_value = nonce_bin[NONCE_TASK_CMD_OFFSET] & RM_CMD_MASK; + nonce_data.work_state = nonce_bin[NONCE_TASK_CMD_OFFSET] & RM_STATUS_MASK; + + temp = (double)nonce_bin[NONCE_COMMAND_OFFSET]; + if (temp != 128) + icarus->temp = temp; + + if (nonce_data.cmd_value == NONCE_TASK_COMPLETE_CMD) { + info->rmdev.chip[nonce_data.chip_no].last_received_task_complete_time = time(NULL); + if (info->g_work[nonce_data.chip_no][nonce_data.task_no]) { + free_work(info->g_work[nonce_data.chip_no][nonce_data.task_no]); + info->g_work[nonce_data.chip_no][nonce_data.task_no] = NULL; + } + goto out; + } - timersub(&tv_history_finish, &tv_history_start, &tv_history_finish); - timeradd(&tv_history_finish, &(info->history_time), &(info->history_time)); + if (nonce_data.cmd_value == NONCE_GET_TASK_CMD) { + rock_send_task(nonce_data.chip_no, nonce_data.task_no, thr); + goto out; } + if (ret == ICA_NONCE_TIMEOUT) + rock_send_task(nonce_data.chip_no, nonce_data.task_no, thr); + + work = info->g_work[nonce_data.chip_no][nonce_data.task_no]; + if (work == NULL) + goto out; + + if (ret == ICA_NONCE_ERROR) + goto out; + + // aborted before becoming idle, get new work + if (ret == ICA_NONCE_TIMEOUT || ret == ICA_NONCE_RESTART) { + timersub(&tv_finish, &tv_start, &elapsed); + + // ONLY up to just when it aborted + // We didn't read a reply so we don't subtract ICARUS_READ_TIME + estimate_hashes = ((double)(elapsed.tv_sec) + + ((double)(elapsed.tv_usec))/((double)1000000)) / info->Hs; + + // If some Serial-USB delay allowed the full nonce range to + // complete it can't have done more than a full nonce + if (unlikely(estimate_hashes > 0xffffffff)) + estimate_hashes = 0xffffffff; + + applog(LOG_DEBUG, "%s %d: no nonce = 0x%08lX hashes (%ld.%06lds)", + icarus->drv->name, icarus->device_id, + (long unsigned int)estimate_hashes, + (long)elapsed.tv_sec, (long)elapsed.tv_usec); + + goto out; + } + + memcpy((char *)&nonce, nonce_bin, ICARUS_READ_SIZE); + nonce = htobe32(nonce); + recv_time = time(NULL); + if ((recv_time-info->rmdev.dev_detect_time) >= 60) { + unsigned char i; + info->rmdev.dev_detect_time = recv_time; + for (i = 0; i < info->rmdev.chip_max; i ++) { + if (info->rmdev.chip[i].error_cnt >= 12) { + if (info->rmdev.chip[i].freq > info->rmdev.min_frq) + info->rmdev.chip[i].freq--; + } else if (info->rmdev.chip[i].error_cnt <= 1) { + if (info->rmdev.chip[i].freq < (info->rmdev.def_frq / 10 - 1)) + info->rmdev.chip[i].freq++; + } + info->rmdev.chip[i].error_cnt = 0; + } + } + + correction_times = 0; + info->nonces_checked++; + while (correction_times < NONCE_CORRECTION_TIMES) { + uint32_t new_nonce; + + if (correction_times > 0) { + info->nonces_correction_tests++; + if (correction_times == 1) + info->nonces_correction_times++; + } + new_nonce = nonce + rbox_corr_values[correction_times]; + /* Basic dupe testing */ + if (new_nonce == info->last_nonce[nonce_data.chip_no][nonce_data.task_no]) + break; + if (test_nonce(work, new_nonce)) { + nonce = new_nonce; + submit_tested_work(thr, work); + info->last_nonce[nonce_data.chip_no][nonce_data.task_no] = nonce; + info->nonces_correction[correction_times]++; + hash_count++; + info->failing = false; + applog(LOG_DEBUG, "Rockminer nonce :::OK:::"); + break; + } else { + applog(LOG_DEBUG, "Rockminer nonce error times = %d", correction_times); + if (new_nonce == 0) + break; + } + correction_times++; + } + if (correction_times >= NONCE_CORRECTION_TIMES) { + inc_hw_errors(thr); + info->nonces_fail++; + } + + hash_count = (hash_count * info->nonce_mask); + + if (opt_debug || info->do_icarus_timing) + timersub(&tv_finish, &tv_start, &elapsed); + + applog(LOG_DEBUG, "%s %d: nonce = 0x%08x = 0x%08lX hashes (%ld.%06lds)", + icarus->drv->name, icarus->device_id, + nonce, (long unsigned int)hash_count, + (long)elapsed.tv_sec, (long)elapsed.tv_usec); + +out: + return hash_count; } @@ -1123,6 +2317,10 @@ static struct api_data *icarus_api_stats(struct cgpu_info *cgpu) { struct api_data *root = NULL; struct ICARUS_INFO *info = (struct ICARUS_INFO *)(cgpu->device_data); + char data[4096]; + int i, off; + size_t len; + float avg; // Warning, access to these is not locked - but we don't really // care since hashing performance is way more important than @@ -1146,21 +2344,119 @@ static struct api_data *icarus_api_stats(struct cgpu_info *cgpu) root = api_add_int(root, "work_division", &(info->work_division), false); root = api_add_int(root, "fpga_count", &(info->fpga_count), false); + if (info->ident == IDENT_LIN) { + root = api_add_string(root, "rock_init", info->rock_init, false); + root = api_add_uint8(root, "rock_chips", &(info->rmdev.detect_chip_no), false); + root = api_add_uint8(root, "rock_chip_max", &(info->rmdev.chip_max), false); + root = api_add_uint8(root, "rock_prod_id", &(info->rmdev.product_id), false); + root = api_add_avg(root, "rock_min_freq", &(info->rmdev.min_frq), false); + root = api_add_avg(root, "rock_max_freq", &(info->rmdev.max_frq), false); + root = api_add_uint64(root, "rock_check", &(info->nonces_checked), false); + root = api_add_uint64(root, "rock_corr", &(info->nonces_correction_times), false); + root = api_add_uint64(root, "rock_corr_tests", &(info->nonces_correction_tests), false); + root = api_add_uint64(root, "rock_corr_fail", &(info->nonces_fail), false); + if (info->nonces_checked <= 0) + avg = 0; + else + avg = (float)(info->nonces_correction_tests) / (float)(info->nonces_checked); + root = api_add_avg(root, "rock_corr_avg", &avg, true); + data[0] = '\0'; + off = 0; + for (i = 0; i < NONCE_CORRECTION_TIMES; i++) { + len = snprintf(data+off, sizeof(data)-off, + "%s%"PRIu64, + i > 0 ? "/" : "", + info->nonces_correction[i]); + if (len >= (sizeof(data)-off)) + off = sizeof(data)-1; + else { + if (len > 0) + off += len; + } + } + root = api_add_string(root, "rock_corr_finds", data, true); + } + return root; } +static void icarus_statline_before(char *buf, size_t bufsiz, struct cgpu_info *cgpu) +{ + struct ICARUS_INFO *info = (struct ICARUS_INFO *)(cgpu->device_data); + + if (info->ant) { + if (info->u3) + tailsprintf(buf, bufsiz, "%3.0fMHz %3dmV", opt_au3_freq, opt_au3_volt); + else + tailsprintf(buf, bufsiz, "%3.0fMHz", opt_anu_freq); + } else if (info->ident == IDENT_CMR2 && info->cmr2_speed > 0) + tailsprintf(buf, bufsiz, "%5.1fMhz", (float)(info->cmr2_speed) * ICARUS_CMR2_SPEED_FACTOR); +} + static void icarus_shutdown(__maybe_unused struct thr_info *thr) { // TODO: ? } +static void icarus_identify(struct cgpu_info *cgpu) +{ + struct ICARUS_INFO *info = (struct ICARUS_INFO *)(cgpu->device_data); + + if (info->ident == IDENT_CMR2) + info->flash_next_work = true; +} + +static char *icarus_set(struct cgpu_info *cgpu, char *option, char *setting, char *replybuf) +{ + struct ICARUS_INFO *info = (struct ICARUS_INFO *)(cgpu->device_data); + int val; + + if (info->ident != IDENT_CMR2) { + strcpy(replybuf, "no set options available"); + return replybuf; + } + + if (strcasecmp(option, "help") == 0) { + sprintf(replybuf, "clock: range %d-%d", + ICARUS_CMR2_SPEED_MIN_INT, ICARUS_CMR2_SPEED_MAX_INT); + return replybuf; + } + + if (strcasecmp(option, "clock") == 0) { + if (!setting || !*setting) { + sprintf(replybuf, "missing clock setting"); + return replybuf; + } + + val = atoi(setting); + if (val < ICARUS_CMR2_SPEED_MIN_INT || val > ICARUS_CMR2_SPEED_MAX_INT) { + sprintf(replybuf, "invalid clock: '%s' valid range %d-%d", + setting, + ICARUS_CMR2_SPEED_MIN_INT, + ICARUS_CMR2_SPEED_MAX_INT); + } + + info->cmr2_speed = CMR2_INT_TO_SPEED(val); + info->speed_next_work = true; + + return NULL; + } + + sprintf(replybuf, "Unknown option: %s", option); + return replybuf; +} + struct device_drv icarus_drv = { - .drv_id = DRIVER_ICARUS, + .drv_id = DRIVER_icarus, .dname = "Icarus", .name = "ICA", .drv_detect = icarus_detect, + .hash_work = &hash_driver_work, .get_api_stats = icarus_api_stats, + .get_statline_before = icarus_statline_before, + .set_device = icarus_set, + .identify_device = icarus_identify, .thread_prepare = icarus_prepare, - .scanhash = icarus_scanhash, + .scanwork = icarus_scanwork, .thread_shutdown = icarus_shutdown, }; diff --git a/driver-klondike.c b/driver-klondike.c new file mode 100644 index 0000000000..1429041ec3 --- /dev/null +++ b/driver-klondike.c @@ -0,0 +1,1555 @@ +/* + * Copyright 2013 Andrew Smith + * Copyright 2013 Con Kolivas + * Copyright 2013 Chris Savery + * + * 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 +#include +#include +#include +#include +#include +#include +#include + +#include "config.h" + +#ifdef WIN32 +#include +#endif + +#include "compat.h" +#include "miner.h" +#include "usbutils.h" + +#define K1 "K1" +#define K16 "K16" +#define K64 "K64" + +static const char *msg_detect_send = "DSend"; +static const char *msg_detect_reply = "DReply"; +static const char *msg_send = "Send"; +static const char *msg_reply = "Reply"; + +#define KLN_CMD_ABORT 'A' +#define KLN_CMD_CONFIG 'C' +#define KLN_CMD_ENABLE 'E' +#define KLN_CMD_IDENT 'I' +#define KLN_CMD_NONCE '=' +#define KLN_CMD_STATUS 'S' +#define KLN_CMD_WORK 'W' + +#define KLN_CMD_ENABLE_OFF '0' +#define KLN_CMD_ENABLE_ON '1' + +#define MIDSTATE_BYTES 32 +#define MERKLE_OFFSET 64 +#define MERKLE_BYTES 12 + +#define REPLY_SIZE 15 // adequate for all types of replies +#define MAX_KLINES 1024 // unhandled reply limit +#define REPLY_WAIT_TIME 100 // poll interval for a cmd waiting it's reply +#define CMD_REPLY_RETRIES 8 // how many retries for cmds +#define MAX_WORK_COUNT 4 // for now, must be binary multiple and match firmware +#define TACH_FACTOR 87890 // fan rpm divisor + +#define KLN_KILLWORK_TEMP 53.5 +#define KLN_COOLED_DOWN 45.5 + +/* + * Work older than 5s will already be completed + * FYI it must not be possible to complete 256 work + * items this quickly on a single device - + * thus limited to 219.9GH/s per device + */ +#define OLD_WORK_MS ((int)(5 * 1000)) + +/* + * How many incorrect slave counts to ignore in a row + * 2 means it allows random grabage returned twice + * Until slaves are implemented, this should never occur + * so allowing 2 in a row should ignore random errros + */ +#define KLN_ISS_IGNORE 2 + +/* + * If the queue status hasn't been updated for this long then do it now + * 5GH/s = 859ms per full nonce range + */ +#define LATE_UPDATE_MS ((int)(2.5 * 1000)) + +// If 5 late updates in a row, try to reset the device +#define LATE_UPDATE_LIMIT 5 + +// If the reset fails sleep for 1s +#define LATE_UPDATE_SLEEP_MS 1000 + +// However give up after 8s +#define LATE_UPDATE_NODEV_MS ((int)(8.0 * 1000)) + +struct device_drv klondike_drv; + +typedef struct klondike_header { + uint8_t cmd; + uint8_t dev; + uint8_t buf[REPLY_SIZE-2]; +} HEADER; + +#define K_2(_bytes) ((int)(_bytes[0]) + \ + ((int)(_bytes[1]) << 8)) + +#define K_4(_bytes) ((uint64_t)(_bytes[0]) + \ + ((uint64_t)(_bytes[1]) << 8) + \ + ((uint64_t)(_bytes[2]) << 16) + \ + ((uint64_t)(_bytes[3]) << 24)) + +#define K_SERIAL(_serial) K_4(_serial) +#define K_HASHCOUNT(_hashcount) K_2(_hashcount) +#define K_MAXCOUNT(_maxcount) K_2(_maxcount) +#define K_NONCE(_nonce) K_4(_nonce) +#define K_HASHCLOCK(_hashclock) K_2(_hashclock) + +#define SET_HASHCLOCK(_hashclock, _value) do { \ + (_hashclock)[0] = (uint8_t)((_value) & 0xff); \ + (_hashclock)[1] = (uint8_t)(((_value) >> 8) & 0xff); \ + } while(0) + +#define KSENDHD(_add) (sizeof(uint8_t) + sizeof(uint8_t) + _add) + +typedef struct klondike_id { + uint8_t cmd; + uint8_t dev; + uint8_t version; + uint8_t product[7]; + uint8_t serial[4]; +} IDENTITY; + +typedef struct klondike_status { + uint8_t cmd; + uint8_t dev; + uint8_t state; + uint8_t chipcount; + uint8_t slavecount; + uint8_t workqc; + uint8_t workid; + uint8_t temp; + uint8_t fanspeed; + uint8_t errorcount; + uint8_t hashcount[2]; + uint8_t maxcount[2]; + uint8_t noise; +} WORKSTATUS; + +typedef struct _worktask { + uint8_t cmd; + uint8_t dev; + uint8_t workid; + uint8_t midstate[32]; + uint8_t merkle[12]; +} WORKTASK; + +typedef struct _workresult { + uint8_t cmd; + uint8_t dev; + uint8_t workid; + uint8_t nonce[4]; +} WORKRESULT; + +typedef struct klondike_cfg { + uint8_t cmd; + uint8_t dev; + uint8_t hashclock[2]; + uint8_t temptarget; + uint8_t tempcritical; + uint8_t fantarget; + uint8_t pad2; +} WORKCFG; + +typedef struct kline { + union { + HEADER hd; + IDENTITY id; + WORKSTATUS ws; + WORKTASK wt; + WORKRESULT wr; + WORKCFG cfg; + }; +} KLINE; + +#define zero_kline(_kline) memset((void *)(_kline), 0, sizeof(KLINE)); + +typedef struct device_info { + uint32_t noncecount; + uint32_t nextworkid; + uint16_t lasthashcount; + uint64_t totalhashcount; + uint32_t rangesize; + uint32_t *chipstats; +} DEVINFO; + +typedef struct klist { + struct klist *prev; + struct klist *next; + KLINE kline; + struct timeval tv_when; + int block_seq; + bool ready; + bool working; +} KLIST; + +typedef struct jobque { + int workqc; + struct timeval last_update; + bool overheat; + bool flushed; + int late_update_count; + int late_update_sequential; +} JOBQUE; + +struct klondike_info { + pthread_rwlock_t stat_lock; + struct thr_info replies_thr; + cglock_t klist_lock; + KLIST *used; + KLIST *free; + int kline_count; + int used_count; + int block_seq; + KLIST *status; + DEVINFO *devinfo; + KLIST *cfg; + JOBQUE *jobque; + int noncecount; + uint64_t hashcount; + uint64_t errorcount; + uint64_t noisecount; + int incorrect_slave_sequential; + + // us Delay from USB reply to being processed + double delay_count; + double delay_total; + double delay_min; + double delay_max; + + struct timeval tv_last_nonce_received; + + // Time from recieving one nonce to the next + double nonce_count; + double nonce_total; + double nonce_min; + double nonce_max; + + int wque_size; + int wque_cleared; + + bool initialised; +}; + +static KLIST *new_klist_set(struct cgpu_info *klncgpu) +{ + struct klondike_info *klninfo = (struct klondike_info *)(klncgpu->device_data); + KLIST *klist = NULL; + int i; + + klist = calloc(MAX_KLINES, sizeof(*klist)); + if (!klist) + quit(1, "Failed to calloc klist - when old count=%d", klninfo->kline_count); + + klninfo->kline_count += MAX_KLINES; + + klist[0].prev = NULL; + klist[0].next = &(klist[1]); + for (i = 1; i < MAX_KLINES-1; i++) { + klist[i].prev = &klist[i-1]; + klist[i].next = &klist[i+1]; + } + klist[MAX_KLINES-1].prev = &(klist[MAX_KLINES-2]); + klist[MAX_KLINES-1].next = NULL; + + return klist; +} + +static KLIST *allocate_kitem(struct cgpu_info *klncgpu) +{ + struct klondike_info *klninfo = (struct klondike_info *)(klncgpu->device_data); + KLIST *kitem = NULL; + int ran_out = 0; + char errbuf[1024]; + + cg_wlock(&klninfo->klist_lock); + + if (klninfo->free == NULL) { + ran_out = klninfo->kline_count; + klninfo->free = new_klist_set(klncgpu); + snprintf(errbuf, sizeof(errbuf), + "%s%i: KLINE count exceeded %d, now %d", + klncgpu->drv->name, klncgpu->device_id, + ran_out, klninfo->kline_count); + } + + kitem = klninfo->free; + + klninfo->free = klninfo->free->next; + if (klninfo->free) + klninfo->free->prev = NULL; + + kitem->next = klninfo->used; + kitem->prev = NULL; + if (kitem->next) + kitem->next->prev = kitem; + klninfo->used = kitem; + + kitem->ready = false; + kitem->working = false; + + memset((void *)&(kitem->kline), 0, sizeof(kitem->kline)); + + klninfo->used_count++; + + cg_wunlock(&klninfo->klist_lock); + + if (ran_out > 0) + applog(LOG_WARNING, "%s", errbuf); + + return kitem; +} + +static KLIST *release_kitem(struct cgpu_info *klncgpu, KLIST *kitem) +{ + struct klondike_info *klninfo = (struct klondike_info *)(klncgpu->device_data); + + cg_wlock(&klninfo->klist_lock); + + if (kitem == klninfo->used) + klninfo->used = kitem->next; + + if (kitem->next) + kitem->next->prev = kitem->prev; + if (kitem->prev) + kitem->prev->next = kitem->next; + + kitem->next = klninfo->free; + if (klninfo->free) + klninfo->free->prev = kitem; + + kitem->prev = NULL; + + klninfo->free = kitem; + + klninfo->used_count--; + + cg_wunlock(&klninfo->klist_lock); + + return NULL; +} + +static double cvtKlnToC(uint8_t temp) +{ + double Rt, stein, celsius; + + if (temp == 0) + return 0.0; + + Rt = 1000.0 * 255.0 / (double)temp - 1000.0; + + stein = log(Rt / 2200.0) / 3987.0; + + stein += 1.0 / (double)(25.0 + 273.15); + + celsius = (1.0 / stein) - 273.15; + + // For display of bad data + if (celsius < 0.0) + celsius = 0.0; + if (celsius > 200.0) + celsius = 200.0; + + return celsius; +} + +static int cvtCToKln(double deg) +{ + double Rt, stein, temp; + + if (deg < 0.0) + deg = 0.0; + + stein = 1.0 / (deg + 273.15); + + stein -= 1.0 / (double)(25.0 + 273.15); + + Rt = exp(stein * 3987.0) * 2200.0; + + if (Rt == -1000.0) + Rt++; + + temp = 1000.0 * 256.0 / (Rt + 1000.0); + + if (temp > 255) + temp = 255; + if (temp < 0) + temp = 0; + + return (int)temp; +} + +// Change this to LOG_WARNING if you wish to always see the replies +#define READ_DEBUG LOG_DEBUG + +static void display_kline(struct cgpu_info *klncgpu, KLINE *kline, const char *msg) +{ + char *hexdata; + + switch (kline->hd.cmd) { + case KLN_CMD_NONCE: + applog(READ_DEBUG, + "%s%i:%d %s work [%c] dev=%d workid=%d" + " nonce=0x%08x", + klncgpu->drv->name, klncgpu->device_id, + (int)(kline->wr.dev), msg, kline->wr.cmd, + (int)(kline->wr.dev), + (int)(kline->wr.workid), + (unsigned int)K_NONCE(kline->wr.nonce) - 0xC0); + break; + case KLN_CMD_STATUS: + case KLN_CMD_WORK: + case KLN_CMD_ENABLE: + case KLN_CMD_ABORT: + applog(READ_DEBUG, + "%s%i:%d %s status [%c] dev=%d chips=%d" + " slaves=%d workcq=%d workid=%d temp=%d fan=%d" + " errors=%d hashes=%d max=%d noise=%d", + klncgpu->drv->name, klncgpu->device_id, + (int)(kline->ws.dev), msg, kline->ws.cmd, + (int)(kline->ws.dev), + (int)(kline->ws.chipcount), + (int)(kline->ws.slavecount), + (int)(kline->ws.workqc), + (int)(kline->ws.workid), + (int)(kline->ws.temp), + (int)(kline->ws.fanspeed), + (int)(kline->ws.errorcount), + K_HASHCOUNT(kline->ws.hashcount), + K_MAXCOUNT(kline->ws.maxcount), + (int)(kline->ws.noise)); + break; + case KLN_CMD_CONFIG: + applog(READ_DEBUG, + "%s%i:%d %s config [%c] dev=%d clock=%d" + " temptarget=%d tempcrit=%d fan=%d", + klncgpu->drv->name, klncgpu->device_id, + (int)(kline->cfg.dev), msg, kline->cfg.cmd, + (int)(kline->cfg.dev), + K_HASHCLOCK(kline->cfg.hashclock), + (int)(kline->cfg.temptarget), + (int)(kline->cfg.tempcritical), + (int)(kline->cfg.fantarget)); + break; + case KLN_CMD_IDENT: + applog(READ_DEBUG, + "%s%i:%d %s info [%c] version=0x%02x prod=%.7s" + " serial=0x%08x", + klncgpu->drv->name, klncgpu->device_id, + (int)(kline->hd.dev), msg, kline->hd.cmd, + (int)(kline->id.version), + kline->id.product, + (unsigned int)K_SERIAL(kline->id.serial)); + break; + default: + hexdata = bin2hex((unsigned char *)&(kline->hd.dev), REPLY_SIZE - 1); + applog(LOG_ERR, + "%s%i:%d %s [%c:%s] unknown and ignored", + klncgpu->drv->name, klncgpu->device_id, + (int)(kline->hd.dev), msg, kline->hd.cmd, + hexdata); + free(hexdata); + break; + } +} + +static void display_send_kline(struct cgpu_info *klncgpu, KLINE *kline, const char *msg) +{ + char *hexdata; + + switch (kline->hd.cmd) { + case KLN_CMD_WORK: + applog(READ_DEBUG, + "%s%i:%d %s work [%c] dev=%d workid=0x%02x ...", + klncgpu->drv->name, klncgpu->device_id, + (int)(kline->wt.dev), msg, kline->ws.cmd, + (int)(kline->wt.dev), + (int)(kline->wt.workid)); + break; + case KLN_CMD_CONFIG: + applog(READ_DEBUG, + "%s%i:%d %s config [%c] dev=%d clock=%d" + " temptarget=%d tempcrit=%d fan=%d", + klncgpu->drv->name, klncgpu->device_id, + (int)(kline->cfg.dev), msg, kline->cfg.cmd, + (int)(kline->cfg.dev), + K_HASHCLOCK(kline->cfg.hashclock), + (int)(kline->cfg.temptarget), + (int)(kline->cfg.tempcritical), + (int)(kline->cfg.fantarget)); + break; + case KLN_CMD_IDENT: + case KLN_CMD_STATUS: + case KLN_CMD_ABORT: + applog(READ_DEBUG, + "%s%i:%d %s cmd [%c]", + klncgpu->drv->name, klncgpu->device_id, + (int)(kline->hd.dev), msg, kline->hd.cmd); + break; + case KLN_CMD_ENABLE: + applog(READ_DEBUG, + "%s%i:%d %s enable [%c] enable=%c", + klncgpu->drv->name, klncgpu->device_id, + (int)(kline->hd.dev), msg, kline->hd.cmd, + (char)(kline->hd.buf[0])); + break; + case KLN_CMD_NONCE: + default: + hexdata = bin2hex((unsigned char *)&(kline->hd.dev), REPLY_SIZE - 1); + applog(LOG_ERR, + "%s%i:%d %s [%c:%s] unknown/unexpected and ignored", + klncgpu->drv->name, klncgpu->device_id, + (int)(kline->hd.dev), msg, kline->hd.cmd, + hexdata); + free(hexdata); + break; + } +} + +static bool SendCmd(struct cgpu_info *klncgpu, KLINE *kline, int datalen) +{ + int err, amt, writ; + + if (klncgpu->usbinfo.nodev) + return false; + + display_send_kline(klncgpu, kline, msg_send); + writ = KSENDHD(datalen); + err = usb_write(klncgpu, (char *)kline, writ, &amt, C_REQUESTRESULTS); + if (err < 0 || amt != writ) { + applog(LOG_ERR, "%s%i:%d Cmd:%c Dev:%d, write failed (%d:%d:%d)", + klncgpu->drv->name, klncgpu->device_id, + (int)(kline->hd.dev), + kline->hd.cmd, (int)(kline->hd.dev), + writ, amt, err); + return false; + } + + return true; +} + +static KLIST *GetReply(struct cgpu_info *klncgpu, uint8_t cmd, uint8_t dev) +{ + struct klondike_info *klninfo = (struct klondike_info *)(klncgpu->device_data); + KLIST *kitem; + int retries = CMD_REPLY_RETRIES; + + while (retries-- > 0 && klncgpu->shutdown == false) { + cgsleep_ms(REPLY_WAIT_TIME); + cg_rlock(&klninfo->klist_lock); + kitem = klninfo->used; + while (kitem) { + if (kitem->kline.hd.cmd == cmd && + kitem->kline.hd.dev == dev && + kitem->ready == true && kitem->working == false) { + kitem->working = true; + cg_runlock(&klninfo->klist_lock); + return kitem; + } + kitem = kitem->next; + } + cg_runlock(&klninfo->klist_lock); + } + return NULL; +} + +static KLIST *SendCmdGetReply(struct cgpu_info *klncgpu, KLINE *kline, int datalen) +{ + if (!SendCmd(klncgpu, kline, datalen)) + return NULL; + + return GetReply(klncgpu, kline->hd.cmd, kline->hd.dev); +} + +static bool klondike_get_stats(struct cgpu_info *klncgpu) +{ + struct klondike_info *klninfo = (struct klondike_info *)(klncgpu->device_data); + KLIST *kitem; + KLINE kline; + int slaves, dev; + + if (klncgpu->usbinfo.nodev || klninfo->status == NULL) + return false; + + applog(LOG_DEBUG, "%s%i: getting status", + klncgpu->drv->name, klncgpu->device_id); + + rd_lock(&(klninfo->stat_lock)); + slaves = klninfo->status[0].kline.ws.slavecount; + rd_unlock(&(klninfo->stat_lock)); + + // loop thru devices and get status for each + for (dev = 0; dev <= slaves; dev++) { + zero_kline(&kline); + kline.hd.cmd = KLN_CMD_STATUS; + kline.hd.dev = dev; + kitem = SendCmdGetReply(klncgpu, &kline, 0); + if (kitem != NULL) { + wr_lock(&(klninfo->stat_lock)); + memcpy((void *)(&(klninfo->status[dev])), + (void *)kitem, + sizeof(klninfo->status[dev])); + wr_unlock(&(klninfo->stat_lock)); + kitem = release_kitem(klncgpu, kitem); + } else { + applog(LOG_ERR, "%s%i:%d failed to update stats", + klncgpu->drv->name, klncgpu->device_id, dev); + } + } + return true; +} + +// TODO: this only enables the master (no slaves) +static bool kln_enable(struct cgpu_info *klncgpu) +{ + KLIST *kitem; + KLINE kline; + int tries = 2; + bool ok = false; + + zero_kline(&kline); + kline.hd.cmd = KLN_CMD_ENABLE; + kline.hd.dev = 0; + kline.hd.buf[0] = KLN_CMD_ENABLE_ON; + + while (tries-- > 0) { + kitem = SendCmdGetReply(klncgpu, &kline, 1); + if (kitem) { + kitem = release_kitem(klncgpu, kitem); + ok = true; + break; + } + cgsleep_ms(50); + } + + if (ok) + cgsleep_ms(50); + + return ok; +} + +static void kln_disable(struct cgpu_info *klncgpu, int dev, bool all) +{ + KLINE kline; + int i; + + zero_kline(&kline); + kline.hd.cmd = KLN_CMD_ENABLE; + kline.hd.buf[0] = KLN_CMD_ENABLE_OFF; + for (i = (all ? 0 : dev); i <= dev; i++) { + kline.hd.dev = i; + SendCmd(klncgpu, &kline, KSENDHD(1)); + } +} + +static bool klondike_init(struct cgpu_info *klncgpu) +{ + struct klondike_info *klninfo = (struct klondike_info *)(klncgpu->device_data); + KLIST *kitem; + KLINE kline; + int slaves, dev; + + klninfo->initialised = false; + + zero_kline(&kline); + kline.hd.cmd = KLN_CMD_STATUS; + kline.hd.dev = 0; + kitem = SendCmdGetReply(klncgpu, &kline, 0); + if (kitem == NULL) + return false; + + slaves = kitem->kline.ws.slavecount; + if (klninfo->status == NULL) { + applog(LOG_DEBUG, "%s%i: initializing data", + klncgpu->drv->name, klncgpu->device_id); + + // alloc space for status, devinfo, cfg and jobque for master and slaves + klninfo->status = calloc(slaves+1, sizeof(*(klninfo->status))); + if (unlikely(!klninfo->status)) + quit(1, "Failed to calloc status array in klondke_get_stats"); + klninfo->devinfo = calloc(slaves+1, sizeof(*(klninfo->devinfo))); + if (unlikely(!klninfo->devinfo)) + quit(1, "Failed to calloc devinfo array in klondke_get_stats"); + klninfo->cfg = calloc(slaves+1, sizeof(*(klninfo->cfg))); + if (unlikely(!klninfo->cfg)) + quit(1, "Failed to calloc cfg array in klondke_get_stats"); + klninfo->jobque = calloc(slaves+1, sizeof(*(klninfo->jobque))); + if (unlikely(!klninfo->jobque)) + quit(1, "Failed to calloc jobque array in klondke_get_stats"); + } + + memcpy((void *)(&(klninfo->status[0])), (void *)kitem, sizeof(klninfo->status[0])); + kitem = release_kitem(klncgpu, kitem); + + // zero init triggers read back only + zero_kline(&kline); + kline.cfg.cmd = KLN_CMD_CONFIG; + + int size = 2; + + // boundaries are checked by device, with valid values returned + if (opt_klondike_options != NULL) { + int hashclock; + double temptarget; + + sscanf(opt_klondike_options, "%d:%lf", &hashclock, &temptarget); + SET_HASHCLOCK(kline.cfg.hashclock, hashclock); + kline.cfg.temptarget = cvtCToKln(temptarget); + kline.cfg.tempcritical = 0; // hard code for old firmware + kline.cfg.fantarget = 0xff; // hard code for old firmware + size = sizeof(kline.cfg) - 2; + } + + for (dev = 0; dev <= slaves; dev++) { + kline.cfg.dev = dev; + kitem = SendCmdGetReply(klncgpu, &kline, size); + if (kitem != NULL) { + memcpy((void *)&(klninfo->cfg[dev]), kitem, sizeof(klninfo->cfg[dev])); + applog(LOG_WARNING, "%s%i:%d config (%d: Clk: %d, T:%.0lf, C:%.0lf, F:%d)", + klncgpu->drv->name, klncgpu->device_id, dev, + dev, K_HASHCLOCK(klninfo->cfg[dev].kline.cfg.hashclock), + cvtKlnToC(klninfo->cfg[dev].kline.cfg.temptarget), + cvtKlnToC(klninfo->cfg[dev].kline.cfg.tempcritical), + (int)100*klninfo->cfg[dev].kline.cfg.fantarget/256); + kitem = release_kitem(klncgpu, kitem); + } + } + klondike_get_stats(klncgpu); + klninfo->initialised = true; + for (dev = 0; dev <= slaves; dev++) { + klninfo->devinfo[dev].rangesize = ((uint64_t)1<<32) / klninfo->status[dev].kline.ws.chipcount; + klninfo->devinfo[dev].chipstats = calloc(klninfo->status[dev].kline.ws.chipcount*2 , sizeof(uint32_t)); + } + + bool ok = kln_enable(klncgpu); + + if (!ok) + applog(LOG_ERR, "%s%i: failed to enable", klncgpu->drv->name, klncgpu->device_id); + + return ok; +} + +static void control_init(struct cgpu_info *klncgpu) +{ + int err, interface; + + if (klncgpu->usbinfo.nodev) + return; + + interface = usb_interface(klncgpu); + + err = usb_transfer(klncgpu, 0, 9, 1, interface, C_RESET); + + applog(LOG_DEBUG, "%s%i: reset got err %d", + klncgpu->drv->name, klncgpu->device_id, err); +} + +static struct cgpu_info *klondike_detect_one(struct libusb_device *dev, struct usb_find_devices *found) +{ + struct cgpu_info *klncgpu = usb_alloc_cgpu(&klondike_drv, 1); + struct klondike_info *klninfo = NULL; + KLINE kline; + + if (unlikely(!klncgpu)) + quit(1, "Failed to calloc klncgpu in klondike_detect_one"); + + klninfo = calloc(1, sizeof(*klninfo)); + if (unlikely(!klninfo)) + quit(1, "Failed to calloc klninfo in klondke_detect_one"); + klncgpu->device_data = (void *)klninfo; + + klninfo->free = new_klist_set(klncgpu); + + if (usb_init(klncgpu, dev, found)) { + int sent, recd, err; + KLIST kitem; + int attempts = 0; + + control_init(klncgpu); + + while (attempts++ < 3) { + kline.hd.cmd = KLN_CMD_IDENT; + kline.hd.dev = 0; + display_send_kline(klncgpu, &kline, msg_detect_send); + err = usb_write(klncgpu, (char *)&(kline.hd), 2, &sent, C_REQUESTRESULTS); + if (err < 0 || sent != 2) { + applog(LOG_ERR, "%s (%s) detect write failed (%d:%d)", + klncgpu->drv->dname, + klncgpu->device_path, + sent, err); + } + cgsleep_ms(REPLY_WAIT_TIME*10); + err = usb_read(klncgpu, (char *)&(kitem.kline), REPLY_SIZE, &recd, C_GETRESULTS); + if (err < 0) { + applog(LOG_ERR, "%s (%s) detect read failed (%d:%d)", + klncgpu->drv->dname, + klncgpu->device_path, + recd, err); + } else if (recd < 1) { + applog(LOG_ERR, "%s (%s) detect empty reply (%d)", + klncgpu->drv->dname, + klncgpu->device_path, + recd); + } else if (kitem.kline.hd.cmd == KLN_CMD_IDENT && kitem.kline.hd.dev == 0) { + display_kline(klncgpu, &kitem.kline, msg_detect_reply); + applog(LOG_DEBUG, "%s (%s) detect successful (%d attempt%s)", + klncgpu->drv->dname, + klncgpu->device_path, + attempts, attempts == 1 ? "" : "s"); + if (!add_cgpu(klncgpu)) + break; + update_usb_stats(klncgpu); + applog(LOG_DEBUG, "Klondike cgpu added"); + rwlock_init(&klninfo->stat_lock); + cglock_init(&klninfo->klist_lock); + return klncgpu; + } + } + usb_uninit(klncgpu); + } + free(klninfo->free); + free(klninfo); + free(klncgpu); + return NULL; +} + +static void klondike_detect(bool __maybe_unused hotplug) +{ + usb_detect(&klondike_drv, klondike_detect_one); +} + +static void klondike_identify(__maybe_unused struct cgpu_info *klncgpu) +{ +/* + KLINE kline; + + zero_kline(&kline); + kline.hd.cmd = KLN_CMD_IDENT; + kline.hd.dev = 0; + SendCmdGetReply(klncgpu, &kline, KSENDHD(0)); +*/ +} + +static void klondike_check_nonce(struct cgpu_info *klncgpu, KLIST *kitem) +{ + struct klondike_info *klninfo = (struct klondike_info *)(klncgpu->device_data); + struct work *work, *look, *tmp; + KLINE *kline = &(kitem->kline); + struct timeval tv_now; + double us_diff; + uint32_t nonce = K_NONCE(kline->wr.nonce) - 0xC0; + + applog(LOG_DEBUG, "%s%i:%d FOUND NONCE (%02x:%08x)", + klncgpu->drv->name, klncgpu->device_id, (int)(kline->wr.dev), + kline->wr.workid, (unsigned int)nonce); + + work = NULL; + cgtime(&tv_now); + rd_lock(&(klncgpu->qlock)); + HASH_ITER(hh, klncgpu->queued_work, look, tmp) { + if (ms_tdiff(&tv_now, &(look->tv_stamp)) < OLD_WORK_MS && + (look->subid == (kline->wr.dev*256 + kline->wr.workid))) { + work = look; + break; + } + } + rd_unlock(&(klncgpu->qlock)); + + if (work) { + wr_lock(&(klninfo->stat_lock)); + klninfo->devinfo[kline->wr.dev].noncecount++; + klninfo->noncecount++; + wr_unlock(&(klninfo->stat_lock)); + + applog(LOG_DEBUG, "%s%i:%d SUBMIT NONCE (%02x:%08x)", + klncgpu->drv->name, klncgpu->device_id, (int)(kline->wr.dev), + kline->wr.workid, (unsigned int)nonce); + + cgtime(&tv_now); + bool ok = submit_nonce(klncgpu->thr[0], work, nonce); + + applog(LOG_DEBUG, "%s%i:%d chip stats %d, %08x, %d, %d", + klncgpu->drv->name, klncgpu->device_id, (int)(kline->wr.dev), + kline->wr.dev, (unsigned int)nonce, + klninfo->devinfo[kline->wr.dev].rangesize, + klninfo->status[kline->wr.dev].kline.ws.chipcount); + + klninfo->devinfo[kline->wr.dev].chipstats[(nonce / klninfo->devinfo[kline->wr.dev].rangesize) + (ok ? 0 : klninfo->status[kline->wr.dev].kline.ws.chipcount)]++; + + us_diff = us_tdiff(&tv_now, &(kitem->tv_when)); + if (klninfo->delay_count == 0) { + klninfo->delay_min = us_diff; + klninfo->delay_max = us_diff; + } else { + if (klninfo->delay_min > us_diff) + klninfo->delay_min = us_diff; + if (klninfo->delay_max < us_diff) + klninfo->delay_max = us_diff; + } + klninfo->delay_count++; + klninfo->delay_total += us_diff; + + if (klninfo->nonce_count > 0) { + us_diff = us_tdiff(&(kitem->tv_when), &(klninfo->tv_last_nonce_received)); + if (klninfo->nonce_count == 1) { + klninfo->nonce_min = us_diff; + klninfo->nonce_max = us_diff; + } else { + if (klninfo->nonce_min > us_diff) + klninfo->nonce_min = us_diff; + if (klninfo->nonce_max < us_diff) + klninfo->nonce_max = us_diff; + } + klninfo->nonce_total += us_diff; + } + klninfo->nonce_count++; + + memcpy(&(klninfo->tv_last_nonce_received), &(kitem->tv_when), + sizeof(klninfo->tv_last_nonce_received)); + + return; + } + + applog(LOG_ERR, "%s%i:%d unknown work (%02x:%08x) - ignored", + klncgpu->drv->name, klncgpu->device_id, (int)(kline->wr.dev), + kline->wr.workid, (unsigned int)nonce); + + //inc_hw_errors(klncgpu->thr[0]); +} + +// thread to keep looking for replies +static void *klondike_get_replies(void *userdata) +{ + struct cgpu_info *klncgpu = (struct cgpu_info *)userdata; + struct klondike_info *klninfo = (struct klondike_info *)(klncgpu->device_data); + KLIST *kitem = NULL; + char *hexdata; + int err, recd, slaves, dev, isc; + bool overheat, sent; + + applog(LOG_DEBUG, "%s%i: listening for replies", + klncgpu->drv->name, klncgpu->device_id); + + while (klncgpu->shutdown == false) { + if (klncgpu->usbinfo.nodev) + return NULL; + + if (kitem == NULL) + kitem = allocate_kitem(klncgpu); + else + memset((void *)&(kitem->kline), 0, sizeof(kitem->kline)); + + err = usb_read(klncgpu, (char *)&(kitem->kline), REPLY_SIZE, &recd, C_GETRESULTS); + if (err || recd != REPLY_SIZE) { + if (err != -7) + applog(LOG_ERR, "%s%i: reply err=%d amt=%d", + klncgpu->drv->name, klncgpu->device_id, + err, recd); + } + if (!err && recd == REPLY_SIZE) { + cgtime(&(kitem->tv_when)); + rd_lock(&(klninfo->stat_lock)); + kitem->block_seq = klninfo->block_seq; + rd_unlock(&(klninfo->stat_lock)); + if (opt_log_level <= READ_DEBUG) { + hexdata = bin2hex((unsigned char *)&(kitem->kline.hd.dev), recd-1); + applog(READ_DEBUG, "%s%i:%d reply [%c:%s]", + klncgpu->drv->name, klncgpu->device_id, + (int)(kitem->kline.hd.dev), + kitem->kline.hd.cmd, hexdata); + free(hexdata); + } + + // We can't check this until it's initialised + if (klninfo->initialised) { + rd_lock(&(klninfo->stat_lock)); + slaves = klninfo->status[0].kline.ws.slavecount; + rd_unlock(&(klninfo->stat_lock)); + + if (kitem->kline.hd.dev > slaves) { + applog(LOG_ERR, "%s%i: reply [%c] has invalid dev=%d (max=%d) using 0", + klncgpu->drv->name, klncgpu->device_id, + (char)(kitem->kline.hd.cmd), + (int)(kitem->kline.hd.dev), + slaves); + /* TODO: this is rather problematic if there are slaves + * however without slaves - it should always be zero */ + kitem->kline.hd.dev = 0; + } else { + wr_lock(&(klninfo->stat_lock)); + klninfo->jobque[kitem->kline.hd.dev].late_update_sequential = 0; + wr_unlock(&(klninfo->stat_lock)); + } + } + + switch (kitem->kline.hd.cmd) { + case KLN_CMD_NONCE: + klondike_check_nonce(klncgpu, kitem); + display_kline(klncgpu, &kitem->kline, msg_reply); + break; + case KLN_CMD_WORK: + // We can't do/check this until it's initialised + if (klninfo->initialised) { + dev = kitem->kline.ws.dev; + if (kitem->kline.ws.workqc == 0) { + bool idle = false; + rd_lock(&(klninfo->stat_lock)); + if (klninfo->jobque[dev].flushed == false) + idle = true; + slaves = klninfo->status[0].kline.ws.slavecount; + rd_unlock(&(klninfo->stat_lock)); + if (idle) + applog(LOG_WARNING, "%s%i:%d went idle before work was sent", + klncgpu->drv->name, + klncgpu->device_id, + dev); + } + wr_lock(&(klninfo->stat_lock)); + klninfo->jobque[dev].flushed = false; + wr_unlock(&(klninfo->stat_lock)); + } + case KLN_CMD_STATUS: + case KLN_CMD_ABORT: + // We can't do/check this until it's initialised + if (klninfo->initialised) { + isc = 0; + dev = kitem->kline.ws.dev; + wr_lock(&(klninfo->stat_lock)); + klninfo->jobque[dev].workqc = (int)(kitem->kline.ws.workqc); + cgtime(&(klninfo->jobque[dev].last_update)); + slaves = klninfo->status[0].kline.ws.slavecount; + overheat = klninfo->jobque[dev].overheat; + if (dev == 0) { + if (kitem->kline.ws.slavecount != slaves) + isc = ++klninfo->incorrect_slave_sequential; + else + isc = klninfo->incorrect_slave_sequential = 0; + } + wr_unlock(&(klninfo->stat_lock)); + + if (isc) { + applog(LOG_ERR, "%s%i:%d reply [%c] has a diff" + " # of slaves=%d (curr=%d)%s", + klncgpu->drv->name, + klncgpu->device_id, + dev, + (char)(kitem->kline.ws.cmd), + (int)(kitem->kline.ws.slavecount), + slaves, + isc <= KLN_ISS_IGNORE ? "" : + " disabling device"); + if (isc > KLN_ISS_IGNORE) + usb_nodev(klncgpu); + break; + } + + if (!overheat) { + double temp = cvtKlnToC(kitem->kline.ws.temp); + if (temp >= KLN_KILLWORK_TEMP) { + KLINE kline; + + wr_lock(&(klninfo->stat_lock)); + klninfo->jobque[dev].overheat = true; + wr_unlock(&(klninfo->stat_lock)); + + applog(LOG_WARNING, "%s%i:%d Critical overheat (%.0fC)", + klncgpu->drv->name, + klncgpu->device_id, + dev, temp); + + zero_kline(&kline); + kline.hd.cmd = KLN_CMD_ABORT; + kline.hd.dev = dev; + sent = SendCmd(klncgpu, &kline, KSENDHD(0)); + kln_disable(klncgpu, dev, false); + if (!sent) { + applog(LOG_ERR, "%s%i:%d overheat failed to" + " abort work - disabling device", + klncgpu->drv->name, + klncgpu->device_id, + dev); + usb_nodev(klncgpu); + } + } + } + } + case KLN_CMD_ENABLE: + wr_lock(&(klninfo->stat_lock)); + klninfo->errorcount += kitem->kline.ws.errorcount; + klninfo->noisecount += kitem->kline.ws.noise; + wr_unlock(&(klninfo->stat_lock)); + display_kline(klncgpu, &kitem->kline, msg_reply); + kitem->ready = true; + kitem = NULL; + break; + case KLN_CMD_CONFIG: + display_kline(klncgpu, &kitem->kline, msg_reply); + kitem->ready = true; + kitem = NULL; + break; + case KLN_CMD_IDENT: + display_kline(klncgpu, &kitem->kline, msg_reply); + kitem->ready = true; + kitem = NULL; + break; + default: + display_kline(klncgpu, &kitem->kline, msg_reply); + break; + } + } + } + return NULL; +} + +static void klondike_flush_work(struct cgpu_info *klncgpu) +{ + struct klondike_info *klninfo = (struct klondike_info *)(klncgpu->device_data); + KLIST *kitem; + KLINE kline; + int slaves, dev; + + if (klninfo->initialised) { + wr_lock(&(klninfo->stat_lock)); + klninfo->block_seq++; + slaves = klninfo->status[0].kline.ws.slavecount; + wr_unlock(&(klninfo->stat_lock)); + + applog(LOG_DEBUG, "%s%i: flushing work", + klncgpu->drv->name, klncgpu->device_id); + zero_kline(&kline); + kline.hd.cmd = KLN_CMD_ABORT; + for (dev = 0; dev <= slaves; dev++) { + kline.hd.dev = dev; + kitem = SendCmdGetReply(klncgpu, &kline, KSENDHD(0)); + if (kitem != NULL) { + wr_lock(&(klninfo->stat_lock)); + memcpy((void *)&(klninfo->status[dev]), + kitem, + sizeof(klninfo->status[dev])); + klninfo->jobque[dev].flushed = true; + wr_unlock(&(klninfo->stat_lock)); + kitem = release_kitem(klncgpu, kitem); + } + } + } +} + +static bool klondike_thread_prepare(struct thr_info *thr) +{ + struct cgpu_info *klncgpu = thr->cgpu; + struct klondike_info *klninfo = (struct klondike_info *)(klncgpu->device_data); + + if (thr_info_create(&(klninfo->replies_thr), NULL, klondike_get_replies, (void *)klncgpu)) { + applog(LOG_ERR, "%s%i: thread create failed", klncgpu->drv->name, klncgpu->device_id); + return false; + } + pthread_detach(klninfo->replies_thr.pth); + + // let the listening get started + cgsleep_ms(100); + + return klondike_init(klncgpu); +} + +static bool klondike_thread_init(struct thr_info *thr) +{ + struct cgpu_info *klncgpu = thr->cgpu; + + if (klncgpu->usbinfo.nodev) + return false; + + klondike_flush_work(klncgpu); + + return true; +} + +static void klondike_shutdown(struct thr_info *thr) +{ + struct cgpu_info *klncgpu = thr->cgpu; + struct klondike_info *klninfo = (struct klondike_info *)(klncgpu->device_data); + + applog(LOG_DEBUG, "%s%i: shutting down work", + klncgpu->drv->name, klncgpu->device_id); + + kln_disable(klncgpu, klninfo->status[0].kline.ws.slavecount, true); + + klncgpu->shutdown = true; +} + +static void klondike_thread_enable(struct thr_info *thr) +{ + struct cgpu_info *klncgpu = thr->cgpu; + + if (klncgpu->usbinfo.nodev) + return; + +/* + KLINE kline; + + zero_kline(&kline); + kline.hd.cmd = KLN_CMD_ENABLE; + kline.hd.dev = dev; + kline.hd.buf[0] = KLN_CMD_ENABLE_OFF; + kitem = SendCmdGetReply(klncgpu, &kline, KSENDHD(1)); +*/ + +} + +static bool klondike_send_work(struct cgpu_info *klncgpu, int dev, struct work *work) +{ + struct klondike_info *klninfo = (struct klondike_info *)(klncgpu->device_data); + struct work *look, *tmp; + KLINE kline; + struct timeval tv_old; + int wque_size, wque_cleared; + + if (klncgpu->usbinfo.nodev) + return false; + + zero_kline(&kline); + kline.wt.cmd = KLN_CMD_WORK; + kline.wt.dev = dev; + memcpy(kline.wt.midstate, work->midstate, MIDSTATE_BYTES); + memcpy(kline.wt.merkle, work->data + MERKLE_OFFSET, MERKLE_BYTES); + kline.wt.workid = (uint8_t)(klninfo->devinfo[dev].nextworkid++ & 0xFF); + work->subid = dev*256 + kline.wt.workid; + cgtime(&work->tv_stamp); + + if (opt_log_level <= LOG_DEBUG) { + char *hexdata = bin2hex((void *)&kline.wt, sizeof(kline.wt)); + applog(LOG_DEBUG, "WORKDATA: %s", hexdata); + free(hexdata); + } + + applog(LOG_DEBUG, "%s%i:%d sending work (%d:%02x)", + klncgpu->drv->name, klncgpu->device_id, dev, + dev, kline.wt.workid); + KLIST *kitem = SendCmdGetReply(klncgpu, &kline, sizeof(kline.wt)); + if (kitem != NULL) { + wr_lock(&(klninfo->stat_lock)); + memcpy((void *)&(klninfo->status[dev]), kitem, sizeof(klninfo->status[dev])); + wr_unlock(&(klninfo->stat_lock)); + kitem = release_kitem(klncgpu, kitem); + + // remove old work + wque_size = 0; + wque_cleared = 0; + cgtime(&tv_old); + wr_lock(&klncgpu->qlock); + HASH_ITER(hh, klncgpu->queued_work, look, tmp) { + if (ms_tdiff(&tv_old, &(look->tv_stamp)) > OLD_WORK_MS) { + __work_completed(klncgpu, look); + free_work(look); + wque_cleared++; + } else + wque_size++; + } + wr_unlock(&klncgpu->qlock); + + wr_lock(&(klninfo->stat_lock)); + klninfo->wque_size = wque_size; + klninfo->wque_cleared = wque_cleared; + wr_unlock(&(klninfo->stat_lock)); + return true; + } + return false; +} + +static bool klondike_queue_full(struct cgpu_info *klncgpu) +{ + struct klondike_info *klninfo = (struct klondike_info *)(klncgpu->device_data); + struct work *work = NULL; + int dev, queued, slaves, seq, howlong; + struct timeval now; + bool nowork; + + if (klncgpu->shutdown == true) + return true; + + cgtime(&now); + rd_lock(&(klninfo->stat_lock)); + slaves = klninfo->status[0].kline.ws.slavecount; + for (dev = 0; dev <= slaves; dev++) + if (ms_tdiff(&now, &(klninfo->jobque[dev].last_update)) > LATE_UPDATE_MS) { + klninfo->jobque[dev].late_update_count++; + seq = ++klninfo->jobque[dev].late_update_sequential; + rd_unlock(&(klninfo->stat_lock)); + if (seq < LATE_UPDATE_LIMIT) { + applog(LOG_DEBUG, "%s%i:%d late update", + klncgpu->drv->name, klncgpu->device_id, dev); + klondike_get_stats(klncgpu); + goto que; + } else { + applog(LOG_WARNING, "%s%i:%d late update (%d) reached - attempting reset", + klncgpu->drv->name, klncgpu->device_id, + dev, LATE_UPDATE_LIMIT); + control_init(klncgpu); + kln_enable(klncgpu); + klondike_get_stats(klncgpu); + rd_lock(&(klninfo->stat_lock)); + howlong = ms_tdiff(&now, &(klninfo->jobque[dev].last_update)); + if (howlong > LATE_UPDATE_MS) { + rd_unlock(&(klninfo->stat_lock)); + if (howlong > LATE_UPDATE_NODEV_MS) { + applog(LOG_ERR, "%s%i:%d reset failed - dropping device", + klncgpu->drv->name, klncgpu->device_id, dev); + usb_nodev(klncgpu); + } else + cgsleep_ms(LATE_UPDATE_SLEEP_MS); + + return true; + } + break; + } + } + rd_unlock(&(klninfo->stat_lock)); + +que: + + nowork = true; + for (queued = 0; queued < MAX_WORK_COUNT-1; queued++) + for (dev = 0; dev <= slaves; dev++) { +tryagain: + rd_lock(&(klninfo->stat_lock)); + if (klninfo->jobque[dev].overheat) { + double temp = cvtKlnToC(klninfo->status[0].kline.ws.temp); + if ((queued == MAX_WORK_COUNT-2) && + ms_tdiff(&now, &(klninfo->jobque[dev].last_update)) > (LATE_UPDATE_MS/2)) { + rd_unlock(&(klninfo->stat_lock)); + klondike_get_stats(klncgpu); + goto tryagain; + } + if (temp <= KLN_COOLED_DOWN) { + klninfo->jobque[dev].overheat = false; + rd_unlock(&(klninfo->stat_lock)); + applog(LOG_WARNING, "%s%i:%d Overheat recovered (%.0fC)", + klncgpu->drv->name, klncgpu->device_id, + dev, temp); + kln_enable(klncgpu); + goto tryagain; + } else { + rd_unlock(&(klninfo->stat_lock)); + continue; + } + } + + if (klninfo->jobque[dev].workqc <= queued) { + rd_unlock(&(klninfo->stat_lock)); + if (!work) + work = get_queued(klncgpu); + if (unlikely(!work)) + return false; + nowork = false; + if (klondike_send_work(klncgpu, dev, work)) + return false; + } else + rd_unlock(&(klninfo->stat_lock)); + } + + if (nowork) + cgsleep_ms(10); // avoid a hard loop in case we have nothing to do + + return true; +} + +static int64_t klondike_scanwork(struct thr_info *thr) +{ + struct cgpu_info *klncgpu = thr->cgpu; + struct klondike_info *klninfo = (struct klondike_info *)(klncgpu->device_data); + int64_t newhashcount = 0; + int dev, slaves; + + if (klncgpu->usbinfo.nodev) + return -1; + + restart_wait(thr, 200); + if (klninfo->status != NULL) { + rd_lock(&(klninfo->stat_lock)); + slaves = klninfo->status[0].kline.ws.slavecount; + for (dev = 0; dev <= slaves; dev++) { + uint64_t newhashdev = 0, hashcount; + int maxcount; + + hashcount = K_HASHCOUNT(klninfo->status[dev].kline.ws.hashcount); + maxcount = K_MAXCOUNT(klninfo->status[dev].kline.ws.maxcount); + // todo: chg this to check workid for wrapped instead + if (klninfo->devinfo[dev].lasthashcount > hashcount) + newhashdev += maxcount; // hash counter wrapped + newhashdev += hashcount - klninfo->devinfo[dev].lasthashcount; + klninfo->devinfo[dev].lasthashcount = hashcount; + if (maxcount != 0) + klninfo->hashcount += (newhashdev << 32) / maxcount; + } + newhashcount += 0xffffffffull * (uint64_t)klninfo->noncecount; + klninfo->noncecount = 0; + rd_unlock(&(klninfo->stat_lock)); + } + + return newhashcount; +} + + +static void get_klondike_statline_before(char *buf, size_t siz, struct cgpu_info *klncgpu) +{ + struct klondike_info *klninfo = (struct klondike_info *)(klncgpu->device_data); + uint8_t temp = 0xFF; + uint16_t fan = 0; + uint16_t clock = 0; + int dev, slaves; + + if (klninfo->status == NULL) { + blank_get_statline_before(buf, siz, klncgpu); + return; + } + + rd_lock(&(klninfo->stat_lock)); + slaves = klninfo->status[0].kline.ws.slavecount; + for (dev = 0; dev <= slaves; dev++) { + if (klninfo->status[dev].kline.ws.temp < temp) + temp = klninfo->status[dev].kline.ws.temp; + fan += klninfo->cfg[dev].kline.cfg.fantarget; + clock += (uint16_t)K_HASHCLOCK(klninfo->cfg[dev].kline.cfg.hashclock); + } + rd_unlock(&(klninfo->stat_lock)); + fan /= slaves + 1; + //fan *= 100/255; // <-- You can't do this because int 100 / int 255 == 0 + fan = 100 * fan / 255; + if (fan > 100) + fan = 100; + clock /= slaves + 1; + if (clock > 999) // error - so truncate it + clock = 999; + + tailsprintf(buf, siz, "%3dMHz %3d%% %.1fC", (int)clock, (int)fan, cvtKlnToC(temp)); +} + +static struct api_data *klondike_api_stats(struct cgpu_info *klncgpu) +{ + struct klondike_info *klninfo = (struct klondike_info *)(klncgpu->device_data); + struct api_data *root = NULL; + char buf[32]; + int dev, slaves; + + if (klninfo->status == NULL) + return NULL; + + rd_lock(&(klninfo->stat_lock)); + slaves = klninfo->status[0].kline.ws.slavecount; + for (dev = 0; dev <= slaves; dev++) { + + float fTemp = cvtKlnToC(klninfo->status[dev].kline.ws.temp); + sprintf(buf, "Temp %d", dev); + root = api_add_temp(root, buf, &fTemp, true); + + double dClk = (double)K_HASHCLOCK(klninfo->cfg[dev].kline.cfg.hashclock); + sprintf(buf, "Clock %d", dev); + root = api_add_freq(root, buf, &dClk, true); + + unsigned int iFan = (unsigned int)100 * klninfo->cfg[dev].kline.cfg.fantarget / 255; + sprintf(buf, "Fan Percent %d", dev); + root = api_add_int(root, buf, (int *)(&iFan), true); + + iFan = 0; + if (klninfo->status[dev].kline.ws.fanspeed > 0) + iFan = (unsigned int)TACH_FACTOR / klninfo->status[dev].kline.ws.fanspeed; + sprintf(buf, "Fan RPM %d", dev); + root = api_add_int(root, buf, (int *)(&iFan), true); + + if (klninfo->devinfo[dev].chipstats != NULL) { + char data[2048]; + char one[32]; + int n; + + sprintf(buf, "Nonces / Chip %d", dev); + data[0] = '\0'; + for (n = 0; n < klninfo->status[dev].kline.ws.chipcount; n++) { + snprintf(one, sizeof(one), "%07d ", klninfo->devinfo[dev].chipstats[n]); + strcat(data, one); + } + root = api_add_string(root, buf, data, true); + + sprintf(buf, "Errors / Chip %d", dev); + data[0] = '\0'; + for (n = 0; n < klninfo->status[dev].kline.ws.chipcount; n++) { + snprintf(one, sizeof(one), "%07d ", klninfo->devinfo[dev].chipstats[n + klninfo->status[dev].kline.ws.chipcount]); + strcat(data, one); + } + root = api_add_string(root, buf, data, true); + } + } + + root = api_add_uint64(root, "Hash Count", &(klninfo->hashcount), true); + root = api_add_uint64(root, "Error Count", &(klninfo->errorcount), true); + root = api_add_uint64(root, "Noise Count", &(klninfo->noisecount), true); + + root = api_add_int(root, "KLine Limit", &(klninfo->kline_count), true); + root = api_add_int(root, "KLine Used", &(klninfo->used_count), true); + + root = api_add_elapsed(root, "KQue Delay Count", &(klninfo->delay_count), true); + root = api_add_elapsed(root, "KQue Delay Total", &(klninfo->delay_total), true); + root = api_add_elapsed(root, "KQue Delay Min", &(klninfo->delay_min), true); + root = api_add_elapsed(root, "KQue Delay Max", &(klninfo->delay_max), true); + double avg; + if (klninfo->delay_count == 0) + avg = 0; + else + avg = klninfo->delay_total / klninfo->delay_count; + root = api_add_diff(root, "KQue Delay Avg", &avg, true); + + root = api_add_elapsed(root, "KQue Nonce Count", &(klninfo->nonce_count), true); + root = api_add_elapsed(root, "KQue Nonce Total", &(klninfo->nonce_total), true); + root = api_add_elapsed(root, "KQue Nonce Min", &(klninfo->nonce_min), true); + root = api_add_elapsed(root, "KQue Nonce Max", &(klninfo->nonce_max), true); + if (klninfo->nonce_count == 0) + avg = 0; + else + avg = klninfo->nonce_total / klninfo->nonce_count; + root = api_add_diff(root, "KQue Nonce Avg", &avg, true); + + root = api_add_int(root, "WQue Size", &(klninfo->wque_size), true); + root = api_add_int(root, "WQue Cleared", &(klninfo->wque_cleared), true); + + rd_unlock(&(klninfo->stat_lock)); + + return root; +} + +struct device_drv klondike_drv = { + .drv_id = DRIVER_klondike, + .dname = "Klondike", + .name = "KLN", + .drv_detect = klondike_detect, + .get_api_stats = klondike_api_stats, + .get_statline_before = get_klondike_statline_before, + .get_stats = klondike_get_stats, + .identify_device = klondike_identify, + .thread_prepare = klondike_thread_prepare, + .thread_init = klondike_thread_init, + .hash_work = hash_queued_work, + .scanwork = klondike_scanwork, + .queue_full = klondike_queue_full, + .flush_work = klondike_flush_work, + .thread_shutdown = klondike_shutdown, + .thread_enable = klondike_thread_enable +}; diff --git a/driver-knc.c b/driver-knc.c new file mode 100644 index 0000000000..564d27dccb --- /dev/null +++ b/driver-knc.c @@ -0,0 +1,865 @@ +/* + * cgminer driver for KnCminer devices + * + * Copyright 2014 KnCminer + * + * 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 +#include +#include +#include +#include +#include +#include +#include + +#include + +#include "logging.h" +#include "miner.h" +#include "knc-transport.h" +#include "knc-asic.h" + +#define MAX_ASICS 6 +#define DIES_PER_ASIC 4 +#define MAX_CORES_PER_DIE 360 +#define WORKS_PER_CORE 3 + +#define CORE_ERROR_LIMIT 30 +#define CORE_ERROR_INTERVAL 30 +#define CORE_ERROR_DISABLE_TIME 5*60 +#define CORE_SUBMIT_MIN_TIME 2 +#define CORE_TIMEOUT 20 +#define SCAN_ADJUST_RANGE 32 + +static struct timeval now; +static const struct timeval core_check_interval = { + CORE_ERROR_INTERVAL, 0 +}; +static const struct timeval core_disable_interval = { + CORE_ERROR_DISABLE_TIME, 0 +}; +static const struct timeval core_submit_interval = { + CORE_SUBMIT_MIN_TIME, 0 +}; +static const struct timeval core_timeout_interval = { + CORE_TIMEOUT, 0 +}; + +struct knc_die; + +struct knc_core_state { + int generation; + int core; + int coreid; + struct knc_die *die; + struct { + int slot; + struct work *work; + } workslot[WORKS_PER_CORE]; /* active, next */ + int transfer_stamp; + struct knc_report report; + struct { + int slot; + uint32_t nonce; + } last_nonce; + uint32_t works; + uint32_t shares; + uint32_t errors; + uint32_t completed; + int last_slot; + uint32_t errors_now; + struct timeval disabled_until; + struct timeval hold_work_until; + struct timeval timeout; + bool inuse; +}; + +struct knc_state; + +struct knc_die { + int channel; + int die; + int version; + int cores; + struct knc_state *knc; + struct knc_core_state *core; +}; + +#define MAX_SPI_SIZE (4096) +#define MAX_SPI_RESPONSES (MAX_SPI_SIZE / (2 + 4 + 1 + 1 + 1 + 4)) +#define MAX_SPI_MESSAGE (128) +#define KNC_SPI_BUFFERS (3) + +struct knc_state { + struct cgpu_info *cgpu; + void *ctx; + int generation; /* work/block generation, incremented on each flush invalidating older works */ + int dies; + struct knc_die die[MAX_ASICS*DIES_PER_ASIC]; + int cores; + int scan_adjust; + int startup; + /* Statistics */ + uint64_t shares; /* diff1 shares reported by hardware */ + uint64_t works; /* Work units submitted */ + uint64_t completed; /* Work units completed */ + uint64_t errors; /* Hardware & communication errors */ + struct timeval next_error_interval; + /* End of statistics */ + /* SPI communications thread */ + pthread_mutex_t spi_qlock; /* SPI queue status lock */ + struct thr_info spi_thr; /* SPI I/O thread */ + pthread_cond_t spi_qcond; /* SPI queue change wakeup */ + struct knc_spi_buffer { + enum { + KNC_SPI_IDLE=0, + KNC_SPI_PENDING, + KNC_SPI_DONE + } state; + int size; + uint8_t txbuf[MAX_SPI_SIZE]; + uint8_t rxbuf[MAX_SPI_SIZE]; + int responses; + struct knc_spi_response { + int request_length; + int response_length; + enum { + KNC_UNKNOWN = 0, + KNC_NO_RESPONSE, + KNC_SETWORK, + KNC_REPORT, + KNC_INFO + } type; + struct knc_core_state *core; + uint32_t data; + int offset; + } response_info[MAX_SPI_RESPONSES]; + } spi_buffer[KNC_SPI_BUFFERS]; + int send_buffer; + int read_buffer; + int send_buffer_count; + int read_buffer_count; + /* end SPI thread */ + + /* Do not add anything below here!! core[] must be last */ + struct knc_core_state core[]; +}; + +int opt_knc_device_idx = 0; +int opt_knc_device_bus = -1; +char *knc_log_file = NULL; + +static void *knc_spi(void *thr_data) +{ + struct cgpu_info *cgpu = thr_data; + struct knc_state *knc = cgpu->device_data; + int buffer = 0; + + pthread_mutex_lock(&knc->spi_qlock); + while (!cgpu->shutdown) { + int this_buffer = buffer; + while (knc->spi_buffer[buffer].state != KNC_SPI_PENDING && !cgpu->shutdown) + pthread_cond_wait(&knc->spi_qcond, &knc->spi_qlock); + pthread_mutex_unlock(&knc->spi_qlock); + if (cgpu->shutdown) + return NULL; + + knc_trnsp_transfer(knc->ctx, knc->spi_buffer[buffer].txbuf, knc->spi_buffer[buffer].rxbuf, knc->spi_buffer[buffer].size); + + buffer += 1; + if (buffer >= KNC_SPI_BUFFERS) + buffer = 0; + + pthread_mutex_lock(&knc->spi_qlock); + knc->spi_buffer[this_buffer].state = KNC_SPI_DONE; + pthread_cond_signal(&knc->spi_qcond); + } + pthread_mutex_unlock(&knc->spi_qlock); + return NULL; +} + +static void knc_process_responses(struct thr_info *thr); + +static void knc_flush(struct thr_info *thr) +{ + struct cgpu_info *cgpu = thr->cgpu; + struct knc_state *knc = cgpu->device_data; + struct knc_spi_buffer *buffer = &knc->spi_buffer[knc->send_buffer]; + if (buffer->state == KNC_SPI_IDLE && buffer->size > 0) { + pthread_mutex_lock(&knc->spi_qlock); + buffer->state = KNC_SPI_PENDING; + pthread_cond_signal(&knc->spi_qcond); + knc->send_buffer += 1; + knc->send_buffer_count += 1; + if (knc->send_buffer >= KNC_SPI_BUFFERS) + knc->send_buffer = 0; + buffer = &knc->spi_buffer[knc->send_buffer]; + /* Block for SPI to finish a transfer if all buffers are busy */ + while (buffer->state == KNC_SPI_PENDING) { + applog(LOG_DEBUG, "KnC: SPI buffer full (%d), waiting for SPI thread", buffer->responses); + pthread_cond_wait(&knc->spi_qcond, &knc->spi_qlock); + } + pthread_mutex_unlock(&knc->spi_qlock); + } + knc_process_responses(thr); +} + +static void knc_sync(struct thr_info *thr) +{ + struct cgpu_info *cgpu = thr->cgpu; + struct knc_state *knc = cgpu->device_data; + struct knc_spi_buffer *buffer = &knc->spi_buffer[knc->send_buffer]; + int sent = 0; + pthread_mutex_lock(&knc->spi_qlock); + if (buffer->state == KNC_SPI_IDLE && buffer->size > 0) { + buffer->state = KNC_SPI_PENDING; + pthread_cond_signal(&knc->spi_qcond); + knc->send_buffer += 1; + knc->send_buffer_count += 1; + if (knc->send_buffer >= KNC_SPI_BUFFERS) + knc->send_buffer = 0; + sent = 1; + } + int prev_buffer = knc->send_buffer - 1; + if (prev_buffer < 0) + prev_buffer = KNC_SPI_BUFFERS - 1; + buffer = &knc->spi_buffer[prev_buffer]; + while (buffer->state == KNC_SPI_PENDING) + pthread_cond_wait(&knc->spi_qcond, &knc->spi_qlock); + pthread_mutex_unlock(&knc->spi_qlock); + + int pending = knc->send_buffer - knc->read_buffer; + if (pending <= 0) + pending += KNC_SPI_BUFFERS; + pending -= 1 - sent; + applog(LOG_INFO, "KnC: sync %d pending buffers", pending); + knc_process_responses(thr); +} + +static void knc_transfer(struct thr_info *thr, struct knc_core_state *core, int request_length, uint8_t *request, int response_length, int response_type, uint32_t data) +{ + struct cgpu_info *cgpu = thr->cgpu; + struct knc_state *knc = cgpu->device_data; + struct knc_spi_buffer *buffer = &knc->spi_buffer[knc->send_buffer]; + /* FPGA control, request header, request body/response, CRC(4), ACK(1), EXTRA(3) */ + int msglen = 2 + MAX(request_length, 4 + response_length ) + 4 + 1 + 3; + if (buffer->size + msglen > MAX_SPI_SIZE || buffer->responses >= MAX_SPI_RESPONSES) { + applog(LOG_INFO, "KnC: SPI buffer sent, %d messages %d bytes", buffer->responses, buffer->size); + knc_flush(thr); + buffer = &knc->spi_buffer[knc->send_buffer]; + } + struct knc_spi_response *response_info = &buffer->response_info[buffer->responses]; + buffer->responses++; + response_info->offset = buffer->size; + response_info->type = response_type; + response_info->request_length = request_length; + response_info->response_length = response_length; + response_info->core = core; + response_info->data = data; + buffer->size = knc_prepare_transfer(buffer->txbuf, buffer->size, MAX_SPI_SIZE, core->die->channel, request_length, request, response_length); +} + +static int knc_transfer_stamp(struct knc_state *knc) +{ + return knc->send_buffer_count; +} + +static int knc_transfer_completed(struct knc_state *knc, int stamp) +{ + /* signed delta math, counter wrap OK */ + return (int)(knc->read_buffer_count - stamp) >= 1; +} + +static bool knc_detect_one(void *ctx) +{ + /* Scan device for ASICs */ + int channel, die, cores = 0, core; + struct cgpu_info *cgpu; + struct knc_state *knc; + struct knc_die_info die_info[MAX_ASICS][DIES_PER_ASIC]; + + memset(die_info, 0, sizeof(die_info)); + + /* Send GETINFO to each die to detect if it is usable */ + for (channel = 0; channel < MAX_ASICS; channel++) { + if (!knc_trnsp_asic_detect(ctx, channel)) + continue; + for (die = 0; die < DIES_PER_ASIC; die++) { + if (knc_detect_die(ctx, channel, die, &die_info[channel][die]) == 0) + cores += die_info[channel][die].cores; + } + } + + if (!cores) { + applog(LOG_NOTICE, "no KnCminer cores found"); + return false; + } + + applog(LOG_ERR, "Found a KnC miner with %d cores", cores); + + cgpu = calloc(1, sizeof(*cgpu)); + knc = calloc(1, sizeof(*knc) + cores * sizeof(struct knc_core_state)); + if (!cgpu || !knc) { + applog(LOG_ERR, "KnC miner detected, but failed to allocate memory"); + return false; + } + + knc->cgpu = cgpu; + knc->ctx = ctx; + knc->generation = 1; + + /* Index all cores */ + int dies = 0; + cores = 0; + struct knc_core_state *pcore = knc->core; + for (channel = 0; channel < MAX_ASICS; channel++) { + for (die = 0; die < DIES_PER_ASIC; die++) { + if (die_info[channel][die].cores) { + knc->die[dies].channel = channel; + knc->die[dies].die = die; + knc->die[dies].version = die_info[channel][die].version; + knc->die[dies].cores = die_info[channel][die].cores; + knc->die[dies].core = pcore; + knc->die[dies].knc = knc; + for (core = 0; core < knc->die[dies].cores; core++) { + knc->die[dies].core[core].die = &knc->die[dies]; + knc->die[dies].core[core].core = core; + } + cores += knc->die[dies].cores; + pcore += knc->die[dies].cores; + dies++; + } + } + } + for (core = 0; core < cores; core++) + knc->core[core].coreid = core; + knc->dies = dies; + knc->cores = cores; + knc->startup = 2; + + cgpu->drv = &knc_drv; + cgpu->name = "KnCminer"; + cgpu->threads = 1; + + cgpu->device_data = knc; + + pthread_mutex_init(&knc->spi_qlock, NULL); + pthread_cond_init(&knc->spi_qcond, NULL); + if (thr_info_create(&knc->spi_thr, NULL, knc_spi, (void *)cgpu)) { + applog(LOG_ERR, "%s%i: SPI thread create failed", + cgpu->drv->name, cgpu->device_id); + free(cgpu); + free(knc); + return false; + } + + add_cgpu(cgpu); + + return true; +} + +/* Probe devices and register with add_cgpu */ +void knc_detect(bool __maybe_unused hotplug) +{ + void *ctx = knc_trnsp_new(opt_knc_device_idx); + + if (ctx != NULL) { + if (!knc_detect_one(ctx)) + knc_trnsp_free(ctx); + } +} + +/* Core helper functions */ +static int knc_core_hold_work(struct knc_core_state *core) +{ + return timercmp(&core->hold_work_until, &now, >); +} + +static int knc_core_has_work(struct knc_core_state *core) +{ + int i; + for (i = 0; i < WORKS_PER_CORE; i++) { + if (core->workslot[i].slot > 0) + return true; + } + return false; +} + +static int knc_core_need_work(struct knc_core_state *core) +{ + return !knc_core_hold_work(core) && !core->workslot[1].work && !core->workslot[2].work; +} + +static int knc_core_disabled(struct knc_core_state *core) +{ + return timercmp(&core->disabled_until, &now, >); +} + +static int _knc_core_next_slot(struct knc_core_state *core) +{ + /* Avoid slot #0 and #15. #0 is "no work assigned" and #15 is seen on bad cores */ + int slot = core->last_slot + 1; + if (slot >= 15) + slot = 1; + core->last_slot = slot; + return slot; +} + +static bool knc_core_slot_busy(struct knc_core_state *core, int slot) +{ + if (slot == core->report.active_slot) + return true; + if (slot == core->report.next_slot) + return true; + int i; + for (i = 0; i < WORKS_PER_CORE; i++) { + if (slot == core->workslot[i].slot) + return true; + } + return false; +} + +static int knc_core_next_slot(struct knc_core_state *core) +{ + int slot; + do slot = _knc_core_next_slot(core); + while (knc_core_slot_busy(core, slot)); + return slot; +} + +static void knc_core_failure(struct knc_core_state *core) +{ + core->errors++; + core->errors_now++; + core->die->knc->errors++; + if (knc_core_disabled(core)) + return; + if (core->errors_now > CORE_ERROR_LIMIT) { + applog(LOG_ERR, "KnC: %d.%d.%d disabled for %d seconds due to repeated hardware errors", + core->die->channel, core->die->die, core->core, core_disable_interval.tv_sec); + timeradd(&now, &core_disable_interval, &core->disabled_until); + } +} + +static int knc_core_handle_nonce(struct thr_info *thr, struct knc_core_state *core, int slot, uint32_t nonce) +{ + int i; + if (!slot) + return; + core->last_nonce.slot = slot; + core->last_nonce.nonce = nonce; + if (core->die->knc->startup) + return; + for (i = 0; i < WORKS_PER_CORE; i++) { + if (slot == core->workslot[i].slot && core->workslot[i].work) { + applog(LOG_INFO, "KnC: %d.%d.%d found nonce %08x", core->die->channel, core->die->die, core->core, nonce); + if (submit_nonce(thr, core->workslot[i].work, nonce)) { + /* Good share */ + core->shares++; + core->die->knc->shares++; + /* This core is useful. Ignore any errors */ + core->errors_now = 0; + } else { + applog(LOG_INFO, "KnC: %d.%d.%d hwerror nonce %08x", core->die->channel, core->die->die, core->core, nonce); + /* Bad share */ + knc_core_failure(core); + } + } + } +} + +static int knc_core_process_report(struct thr_info *thr, struct knc_core_state *core, uint8_t *response) +{ + struct knc_report *report = &core->report; + knc_decode_report(response, report, core->die->version); + bool had_event = false; + + applog(LOG_DEBUG, "KnC %d.%d.%d: Process report %d %d(%d) / %d %d %d", core->die->channel, core->die->die, core->core, report->active_slot, report->next_slot, report->next_state, core->workslot[0].slot, core->workslot[1].slot, core->workslot[2].slot); + int n; + for (n = 0; n < KNC_NONCES_PER_REPORT; n++) { + if (report->nonce[n].slot < 0) + break; + if (core->last_nonce.slot == report->nonce[n].slot && core->last_nonce.nonce == report->nonce[n].nonce) + break; + } + while(n-- > 0) { + knc_core_handle_nonce(thr, core, report->nonce[n].slot, report->nonce[n].nonce); + } + + if (report->active_slot && core->workslot[0].slot != report->active_slot) { + had_event = true; + applog(LOG_INFO, "KnC: New work on %d.%d.%d, %d %d / %d %d %d", core->die->channel, core->die->die, core->core, report->active_slot, report->next_slot, core->workslot[0].slot, core->workslot[1].slot, core->workslot[2].slot); + /* Core switched to next work */ + if (core->workslot[0].work) { + core->die->knc->completed++; + core->completed++; + applog(LOG_INFO, "KnC: Work completed on core %d.%d.%d!", core->die->channel, core->die->die, core->core); + free_work(core->workslot[0].work); + } + core->workslot[0] = core->workslot[1]; + core->workslot[1].work = NULL; + core->workslot[1].slot = -1; + + /* or did it switch directly to pending work? */ + if (report->active_slot == core->workslot[2].slot) { + applog(LOG_INFO, "KnC: New work on %d.%d.%d, %d %d %d %d (pending)", core->die->channel, core->die->die, core->core, report->active_slot, core->workslot[0].slot, core->workslot[1].slot, core->workslot[2].slot); + if (core->workslot[0].work) + free_work(core->workslot[0].work); + core->workslot[0] = core->workslot[2]; + core->workslot[2].work = NULL; + core->workslot[2].slot = -1; + } + } + + if (report->next_state && core->workslot[2].slot > 0 && (core->workslot[2].slot == report->next_slot || report->next_slot == -1)) { + had_event = true; + applog(LOG_INFO, "KnC: Accepted work on %d.%d.%d, %d %d %d %d (pending)", core->die->channel, core->die->die, core->core, report->active_slot, core->workslot[0].slot, core->workslot[1].slot, core->workslot[2].slot); + /* core accepted next work */ + if (core->workslot[1].work) + free_work(core->workslot[1].work); + core->workslot[1] = core->workslot[2]; + core->workslot[2].work = NULL; + core->workslot[2].slot = -1; + } + + if (core->workslot[2].work && knc_transfer_completed(core->die->knc, core->transfer_stamp)) { + had_event = true; + applog(LOG_INFO, "KnC: Setwork failed on core %d.%d.%d?", core->die->channel, core->die->die, core->core); + free_work(core->workslot[2].work); + core->workslot[2].slot = -1; + } + + if (had_event) + applog(LOG_INFO, "KnC: Exit report on %d.%d.%d, %d %d / %d %d %d", core->die->channel, core->die->die, core->core, report->active_slot, report->next_slot, core->workslot[0].slot, core->workslot[1].slot, core->workslot[2].slot); + + return 0; +} + +static void knc_process_responses(struct thr_info *thr) +{ + struct cgpu_info *cgpu = thr->cgpu; + struct knc_state *knc = cgpu->device_data; + struct knc_spi_buffer *buffer = &knc->spi_buffer[knc->read_buffer]; + while (buffer->state == KNC_SPI_DONE) { + int i; + for (i = 0; i < buffer->responses; i++) { + struct knc_spi_response *response_info = &buffer->response_info[i]; + uint8_t *rxbuf = &buffer->rxbuf[response_info->offset]; + struct knc_core_state *core = response_info->core; + int status = knc_decode_response(rxbuf, response_info->request_length, &rxbuf, response_info->response_length); + /* Invert KNC_ACCEPTED to simplify logics below */ + if (response_info->type == KNC_SETWORK && !KNC_IS_ERROR(status)) + status ^= KNC_ACCEPTED; + if (core->die->version != KNC_VERSION_JUPITER && status != 0) { + applog(LOG_ERR, "KnC %d.%d.%d: Communication error (%x / %d)", core->die->channel, core->die->die, core->core, status, i); + if (status == KNC_ACCEPTED) { + /* Core refused our work vector. Likely out of sync. Reset it */ + core->inuse = false; + } + knc_core_failure(core); + } + switch(response_info->type) { + case KNC_REPORT: + case KNC_SETWORK: + /* Should we care about failed SETWORK explicit? Or simply handle it by next state not loaded indication in reports? */ + knc_core_process_report(thr, core, rxbuf); + break; + } + } + + buffer->state = KNC_SPI_IDLE; + buffer->responses = 0; + buffer->size = 0; + knc->read_buffer += 1; + knc->read_buffer_count += 1; + if (knc->read_buffer >= KNC_SPI_BUFFERS) + knc->read_buffer = 0; + buffer = &knc->spi_buffer[knc->read_buffer]; + } +} + +static int knc_core_send_work(struct thr_info *thr, struct knc_core_state *core, struct work *work, bool clean) +{ + struct knc_state *knc = core->die->knc; + struct cgpu_info *cgpu = knc->cgpu; + int request_length = 4 + 1 + 6*4 + 3*4 + 8*4; + uint8_t request[request_length]; + int response_length = 1 + 1 + (1 + 4) * 5; + uint8_t response[response_length]; + + int slot = knc_core_next_slot(core); + if (slot < 0) + goto error; + + applog(LOG_INFO, "KnC setwork%s %d.%d.%d = %d, %d %d / %d %d %d", clean ? " CLEAN" : "", core->die->channel, core->die->die, core->core, slot, core->report.active_slot, core->report.next_slot, core->workslot[0].slot, core->workslot[1].slot, core->workslot[2].slot); + if (!clean && !knc_core_need_work(core)) + goto error; + + switch(core->die->version) { + case KNC_VERSION_JUPITER: + if (clean) { + /* Double halt to get rid of any previous queued work */ + request_length = knc_prepare_jupiter_halt(request, core->die->die, core->core); + knc_transfer(thr, core, request_length, request, 0, KNC_NO_RESPONSE, 0); + knc_transfer(thr, core, request_length, request, 0, KNC_NO_RESPONSE, 0); + } + request_length = knc_prepare_jupiter_setwork(request, core->die->die, core->core, slot, work); + knc_transfer(thr, core, request_length, request, 0, KNC_NO_RESPONSE, 0); + break; + case KNC_VERSION_NEPTUNE: + request_length = knc_prepare_neptune_setwork(request, core->die->die, core->core, slot, work, clean); + knc_transfer(thr, core, request_length, request, response_length, KNC_SETWORK, slot); + break; + default: + goto error; + } + + core->workslot[2].work = work; + core->workslot[2].slot = slot; + core->works++; + core->die->knc->works++; + core->transfer_stamp = knc_transfer_stamp(knc); + core->inuse = true; + + timeradd(&now, &core_submit_interval, &core->hold_work_until); + timeradd(&now, &core_timeout_interval, &core->timeout); + + return 0; + +error: + applog(LOG_INFO, "KnC: %d.%d.%d Failed to setwork (%d)", + core->die->channel, core->die->die, core->core, core->errors_now); + knc_core_failure(core); + free_work(work); + return -1; +} + +static int knc_core_request_report(struct thr_info *thr, struct knc_core_state *core) +{ + struct knc_state *knc = core->die->knc; + struct cgpu_info *cgpu = knc->cgpu; + int request_length = 4; + uint8_t request[request_length]; + int response_length = 1 + 1 + (1 + 4) * 5; + uint8_t response[response_length]; + + applog(LOG_DEBUG, "KnC: %d.%d.%d Request report", core->die->channel, core->die->die, core->core); + + request_length = knc_prepare_report(request, core->die->die, core->core); + + switch(core->die->version) { + case KNC_VERSION_JUPITER: + response_length = 1 + 1 + (1 + 4); + knc_transfer(thr, core, request_length, request, response_length, KNC_REPORT, 0); + return 0; + case KNC_VERSION_NEPTUNE: + knc_transfer(thr, core, request_length, request, response_length, KNC_REPORT, 0); + return 0; + } + +error: + applog(LOG_INFO, "KnC: Failed to scan work report"); + knc_core_failure(core); + return -1; +} + +/* return value is number of nonces that have been checked since + * previous call + */ +static int64_t knc_scanwork(struct thr_info *thr) +{ +#define KNC_COUNT_UNIT shares + struct cgpu_info *cgpu = thr->cgpu; + struct knc_state *knc = cgpu->device_data; + int64_t ret = 0; + uint32_t last_count = knc->KNC_COUNT_UNIT; + + applog(LOG_DEBUG, "KnC running scanwork"); + + gettimeofday(&now, NULL); + + knc_trnsp_periodic_check(knc->ctx); + + int i; + + knc_process_responses(thr); + + if (timercmp(&knc->next_error_interval, &now, >)) { + /* Reset hw error limiter every check interval */ + timeradd(&now, &core_check_interval, &knc->next_error_interval); + for (i = 0; i < knc->cores; i++) { + struct knc_core_state *core = &knc->core[i]; + core->errors_now = 0; + } + } + + for (i = 0; i < knc->cores; i++) { + struct knc_core_state *core = &knc->core[i]; + bool clean = !core->inuse; + if (knc_core_disabled(core)) + continue; + if (core->generation != knc->generation) { + applog(LOG_INFO, "KnC %d.%d.%d flush gen=%d/%d", core->die->channel, core->die->die, core->core, core->generation, knc->generation); + /* clean set state, forget everything */ + int slot; + for (slot = 0; slot < WORKS_PER_CORE; slot ++) { + if (core->workslot[slot].work) + free_work(core->workslot[slot].work); + core->workslot[slot].slot = -1; + } + core->hold_work_until = now; + core->generation = knc->generation; + } else if (timercmp(&core->timeout, &now, <=) && (core->workslot[0].slot > 0 || core->workslot[1].slot > 0 || core->workslot[2].slot > 0)) { + applog(LOG_ERR, "KnC %d.%d.%d timeout", core->die->channel, core->die->die, core->core, core->generation, knc->generation); + clean = true; + } + if (!knc_core_has_work(core)) + clean = true; + if (core->workslot[0].slot < 0 && core->workslot[1].slot < 0 && core->workslot[2].slot < 0) + clean = true; + if (i % SCAN_ADJUST_RANGE == knc->scan_adjust) + clean = true; + if ((knc_core_need_work(core) || clean) && !knc->startup) { + struct work *work = get_work(thr, thr->id); + knc_core_send_work(thr, core, work, clean); + } else { + knc_core_request_report(thr, core); + } + } + /* knc->startup delays initial work submission until we have had chance to query all cores on their current status, to avoid slot number collisions with earlier run */ + if (knc->startup) + knc->startup--; + else if (knc->scan_adjust < SCAN_ADJUST_RANGE) + knc->scan_adjust++; + + knc_flush(thr); + + return (int64_t)(knc->KNC_COUNT_UNIT - last_count) * 0x100000000UL; +} + +static void knc_flush_work(struct cgpu_info *cgpu) +{ + struct knc_state *knc = cgpu->device_data; + + applog(LOG_INFO, "KnC running flushwork"); + + knc->generation++; + knc->scan_adjust=0; + if (!knc->generation) + knc->generation++; +} + +static void knc_zero_stats(struct cgpu_info *cgpu) +{ + int core; + struct knc_state *knc = cgpu->device_data; + for (core = 0; core < knc->cores; core++) { + knc->shares = 0; + knc->completed = 0; + knc->works = 0; + knc->errors = 0; + knc->core[core].works = 0; + knc->core[core].errors = 0; + knc->core[core].shares = 0; + knc->core[core].completed = 0; + } +} + +static struct api_data *knc_api_stats(struct cgpu_info *cgpu) +{ + struct knc_state *knc = cgpu->device_data; + struct api_data *root = NULL; + unsigned int cursize; + int asic, core, n; + char label[256]; + + root = api_add_int(root, "dies", &knc->dies, 1); + root = api_add_int(root, "cores", &knc->cores, 1); + root = api_add_uint64(root, "shares", &knc->shares, 1); + root = api_add_uint64(root, "works", &knc->works, 1); + root = api_add_uint64(root, "completed", &knc->completed, 1); + root = api_add_uint64(root, "errors", &knc->errors, 1); + + /* Active cores */ + int active = knc->cores; + for (core = 0; core < knc->cores; core++) { + if (knc_core_disabled(&knc->core[core])) + active -= 1; + } + root = api_add_int(root, "active", &active, 1); + + /* Per ASIC/die data */ + for (n = 0; n < knc->dies; n++) { + struct knc_die *die = &knc->die[n]; + +#define knc_api_die_string(name, value) do { \ + snprintf(label, sizeof(label), "%d.%d.%s", die->channel, die->die, name); \ + root = api_add_string(root, label, value, 1); \ + } while(0) +#define knc_api_die_int(name, value) do { \ + snprintf(label, sizeof(label), "%d.%d.%s", die->channel, die->die, name); \ + uint64_t v = value; \ + root = api_add_uint64(root, label, &v, 1); \ + } while(0) + + /* Model */ + { + char *model = "?"; + switch(die->version) { + case KNC_VERSION_JUPITER: + model = "Jupiter"; + break; + case KNC_VERSION_NEPTUNE: + model = "Neptune"; + break; + } + knc_api_die_string("model", model); + knc_api_die_int("cores", die->cores); + } + + /* Core based stats */ + { + int active = 0; + uint64_t errors = 0; + uint64_t shares = 0; + uint64_t works = 0; + uint64_t completed = 0; + char coremap[die->cores+1]; + + for (core = 0; core < die->cores; core++) { + coremap[core] = knc_core_disabled(&die->core[core]) ? '0' : '1'; + works += die->core[core].works; + shares += die->core[core].shares; + errors += die->core[core].errors; + completed += die->core[core].completed; + } + coremap[die->cores] = '\0'; + knc_api_die_int("errors", errors); + knc_api_die_int("shares", shares); + knc_api_die_int("works", works); + knc_api_die_int("completed", completed); + knc_api_die_string("coremap", coremap); + } + } + + return root; +} + +struct device_drv knc_drv = { + .drv_id = DRIVER_knc, + .dname = "KnCminer Neptune", + .name = "KnC", + .drv_detect = knc_detect, + .hash_work = hash_driver_work, + .flush_work = knc_flush_work, + .scanwork = knc_scanwork, + .zero_stats = knc_zero_stats, + .get_api_stats = knc_api_stats, +}; diff --git a/driver-minion.c b/driver-minion.c new file mode 100644 index 0000000000..52e3442223 --- /dev/null +++ b/driver-minion.c @@ -0,0 +1,5380 @@ +/* + * Copyright 2013-2014 Andrew Smith - BlackArrow Ltd + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the Free + * Software Foundation; either version 3 of the License, or (at your option) + * any later version. See COPYING for more details. + */ + +#include "config.h" +#include "compat.h" +#include "miner.h" +#include "klist.h" +#include +#include + +#ifndef LINUX +static void minion_detect(__maybe_unused bool hotplug) +{ +} +#else + +#include +#include +#include +#include +#include +#include +#include + +// Define this to 1 to enable interrupt code and enable no_nonce +#define ENABLE_INT_NONO 0 + +// Define this to 1 if compiling on RockChip and not on RPi +#define MINION_ROCKCHIP 0 + +// The code is always in - this just decides if it does it +static bool minreread = false; + +#if MINION_ROCKCHIP == 1 +#define MINION_POWERCYCLE_GPIO 173 +#define MINION_CHIP_OFF "1" +#define MINION_CHIP_ON "0" +#define MINION_CHIP_DELAY 100 +#endif + +// Power cycle if the xff_list is full and the tail is less than +// this long ago +#define MINION_POWER_TIME 60 + +/* + * Use pins for board selection + * If disabled, it will test chips just as 'pin 0' + * but never do any gpio - the equivalent of the previous 'no pins' code + */ +static bool usepins = false; + +#define MINION_PAGE_SIZE 4096 + +#define BCM2835_BASE 0x20000000 +#define BCM2835_GPIO_BASE (BCM2835_BASE + 0x200000) + +#define BCM2835_GPIO_SET0 0x001c // GPIO Pin Output Set 0 +#define BCM2835_GPIO_CLR0 0x0028 // GPIO Pin Output Clear 0 + +#define BCM2835_GPIO_FSEL0 0x0000 + +#define BCM2835_GPIO_FSEL_INPUT 0b000 +#define BCM2835_GPIO_FSEL_OUTPUT 0b001 +#define BCM2835_GPIO_FSEL_MASK 0b111 + +#define BCM2835_PIN_HIGH 0x1 +#define BCM2835_PIN_LOW 0x0 + +static const char *minion_memory = "/dev/mem"; +static int minion_memory_addr = BCM2835_GPIO_BASE; + +#define MINION_SPI_BUS 0 +#define MINION_SPI_CHIP 0 + +#if MINION_ROCKCHIP == 0 +#define MINION_SPI_SPEED 8000000 +#else +#define MINION_SPI_SPEED 500000 +#endif +#define MINION_SPI_BUFSIZ 1024 + +static struct minion_select_pins { + int pin; + int wpi; + char *name; + int bcm; // this is what we use +} minionPins[] = { + { 24, 10, "CE0", 8, }, + { 26, 11, "CE1", 7, }, + { 16, 4, "GPIO4", 23, }, + { 22, 6, "GPIO6", 25, }, + { 12, 1, "GPIO1", 18, }, + { 18, 5, "GPIO5", 24, }, + { 11, 0, "GPIO0", 17, }, + { 13, 2, "GPIO2", 27, }, + { 15, 3, "GPIO3", 22, }, + { 7, 7, "GPIO7", 4, } + +/* The rest on the RPi + { 3, 8, "SDA", 2, } + { 5, 9, "SCL", 3, } + { 19, 12, "MOSI", 10, } + { 21, 13, "MISO", 9, } + { 23, 14, "SCLK", 11, } + { 8, 15, "TxD", 14, } + { 10, 16, "RxD", 15, } +*/ +}; + +/* + * uS delays for GPIO pin access + */ +#define MINION_PIN_BEFORE cgsleep_us(33) +#define MINION_PIN_SLEEP cgsleep_us(133) +#define MINION_PIN_AFTER + +#define MINION_PIN_COUNT (sizeof(minionPins)/ \ + sizeof(struct minion_select_pins)) + +#define CHIP_PIN(_chip) (minioninfo->chip_pin[_chip]) + +#define MINION_MIN_CHIP 0 +#define MINION_MAX_CHIP 11 + +#define MINION_CHIP_PER_PIN (1 + MINION_MAX_CHIP - MINION_MIN_CHIP) + +#define MINION_CHIPS (MINION_PIN_COUNT * MINION_CHIP_PER_PIN) +#define MINION_CORES 99 +#define FAKE_CORE MINION_CORES + +/* + * TODO: These will need adjusting for final hardware + * Look them up and calculate them? + */ +#define MINION_QUE_MAX 64 +#define MINION_QUE_HIGH 48 +#define MINION_QUE_SEND 16 +#define MINION_QUE_LOW 8 + +#define MINION_FFL " - from %s %s() line %d" +#define MINION_FFL_HERE __FILE__, __func__, __LINE__ +#define MINION_FFL_PASS file, func, line +#define MINION_FFL_ARGS __maybe_unused const char *file, \ + __maybe_unused const char *func, \ + __maybe_unused const int line + +#define minion_txrx(_task) _minion_txrx(minioncgpu, minioninfo, _task, MINION_FFL_HERE) + +#define MINION_SYS_REGS 0x00 +#define MINION_CORE_REGS 0x10 +#define MINION_RES_BUF 0x20 +#define MINION_CMD_QUE 0x30 +#define MINION_NONCE_RANGES 0x70 + +#define DATA_SIZ (sizeof(uint32_t)) + +// All SYS data sizes are DATA_SIZ +#define MINION_SYS_CHIP_SIG 0x00 +#define MINION_SYS_CHIP_STA 0x01 +#define MINION_SYS_SPI_LED 0x02 +#define MINION_SYS_TEMP_CTL 0x03 +#define MINION_SYS_FREQ_CTL 0x04 +#define MINION_SYS_NONCE_LED 0x05 +#define MINION_SYS_MISC_CTL 0x06 +#define MINION_SYS_RSTN_CTL 0x07 +#define MINION_SYS_INT_ENA 0x08 +#define MINION_SYS_INT_CLR 0x09 +#define MINION_SYS_INT_STA 0x0a +#define MINION_SYS_FIFO_STA 0x0b +#define MINION_SYS_QUE_TRIG 0x0c +#define MINION_SYS_BUF_TRIG 0x0d +#define MINION_SYS_IDLE_CNT 0x0e + +// How many 32 bit reports make up all the cores - 99 cores = 4 reps +#define MINION_CORE_REPS (int)((((MINION_CORES-1) >> 5) & 0xff) + 1) + +// All SYS data sizes are DATA_SIZ +#define MINION_SYS_SIZ DATA_SIZ + +// Header Pin 18 = GPIO5 = BCM 24 +#define MINION_GPIO_RESULT_INT_PIN 24 +// RockChip is pin 172 ... + +#define MINION_GPIO_SYS "/sys/class/gpio" +#define MINION_GPIO_ENA "/export" +#define MINION_GPIO_ENA_VAL "%d" +#define MINION_GPIO_DIS "/unexport" +#define MINION_GPIO_PIN "/gpio%d" +#define MINION_GPIO_DIR "/direction" +#define MINION_GPIO_DIR_READ "in" +#define MINION_GPIO_DIR_WRITE "out" +#define MINION_GPIO_EDGE "/edge" +#define MINION_GPIO_EDGE_NONE "none" +#define MINION_GPIO_EDGE_RISING "rising" +#define MINION_GPIO_EDGE_FALLING "falling" +#define MINION_GPIO_EDGE_BOTH "both" +#define MINION_GPIO_ACT "/active_low" +#define MINION_GPIO_ACT_LO "1" +#define MINION_GPIO_ACT_HI "0" +#define MINION_GPIO_VALUE "/value" + +#define MINION_RESULT_INT 0x01 +#define MINION_RESULT_FULL_INT 0x02 +#define MINION_CMD_INT 0x04 +#define MINION_CMD_FULL_INT 0x08 +#define MINION_TEMP_LOW_INT 0x10 +#define MINION_TEMP_HI_INT 0x20 +#define MINION_ALL_INT MINION_RESULT_INT | \ + MINION_RESULT_FULL_INT | \ + MINION_CMD_INT | \ + MINION_CMD_FULL_INT | \ + MINION_TEMP_LOW_INT | \ + MINION_TEMP_HI_INT + +#define RSTN_CTL_RESET_CORES 0x01 +#define RSTN_CTL_FLUSH_RESULTS 0x02 +#define RSTN_CTL_FLUSH_CMD_QUEUE 0x04 +#define RSTN_CTL_SPI_SW_RSTN 0x08 +#define RSTN_CTL_SHA_MGR_RESET 0x10 + +// Init +#define SYS_RSTN_CTL_INIT (RSTN_CTL_RESET_CORES | \ + RSTN_CTL_FLUSH_RESULTS | \ + RSTN_CTL_FLUSH_CMD_QUEUE | \ + RSTN_CTL_SPI_SW_RSTN | \ + RSTN_CTL_SHA_MGR_RESET) + +// Block change +#define SYS_RSTN_CTL_FLUSH (RSTN_CTL_RESET_CORES | \ + RSTN_CTL_SPI_SW_RSTN | \ + RSTN_CTL_FLUSH_CMD_QUEUE) + +#if ENABLE_INT_NONO +// enable 'no nonce' report +#define SYS_MISC_CTL_DEFAULT 0x04 +#else +#define SYS_MISC_CTL_DEFAULT 0x00 +#endif + +// Temperature returned by MINION_SYS_CHIP_STA 0x01 STA_TEMP() +#define MINION_TEMP_40 0 +#define MINION_TEMP_60 1 +#define MINION_TEMP_80 3 +#define MINION_TEMP_100 7 +#define MINION_TEMP_OVER 15 + +static const char *min_temp_40 = "<40"; +static const char *min_temp_60 = "40-60"; +static const char *min_temp_80 = "60-80"; +static const char *min_temp_100 = "80-100"; +static const char *min_temp_over = ">100"; +static const char *min_temp_invalid = "?"; + +/* + * Temperature for MINION_SYS_TEMP_CTL 0x03 temp_thres [0:3] + * i.e. it starts at 120 and goes up in steps of 5 to 160 + */ +#define MINION_TEMP_CTL_MIN 1 +#define MINION_TEMP_CTL_MAX 9 +#define MINION_TEMP_CTL_BITS 0x0f +#define MINION_TEMP_CTL_DEF 135 +#define MINION_TEMP_CTL_STEP 5 +#define MINION_TEMP_CTL_MIN_VALUE 120 +#define MINION_TEMP_CTL_MAX_VALUE (MINION_TEMP_CTL_MIN_VALUE + \ + (MINION_TEMP_CTL_STEP * \ + (MINION_TEMP_CTL_MAX - MINION_TEMP_CTL_MIN))) +#define MINION_TEMP_DISABLE "disable" +#define MINION_TEMP_CTL_DISABLE -1 +#define MINION_TEMP_CTL_DISABLE_VALUE 0x20 + +// CORE data size is DATA_SIZ +#define MINION_CORE_ENA0_31 0x10 +#define MINION_CORE_ENA32_63 0x11 +#define MINION_CORE_ENA64_95 0x12 +#define MINION_CORE_ENA96_98 0x13 +#define MINION_CORE_ACT0_31 0x14 +#define MINION_CORE_ACT32_63 0x15 +#define MINION_CORE_ACT64_95 0x16 +#define MINION_CORE_ACT96_98 0x17 + +// All CORE data sizes are DATA_SIZ +#define MINION_CORE_SIZ DATA_SIZ + +#define MINION_CORE_ALL "all" + +// RES data size is minion_result +#define MINION_RES_DATA 0x20 +#define MINION_RES_PEEK 0x21 + +// QUE data size is minion_que +#define MINION_QUE_0 0x30 +#define MINION_QUE_R 0x31 + +// RANGE data sizes are DATA_SIZ +#define MINION_NONCE_START 0x70 +#define MINION_NONCE_RANGE 0x71 + +// This must be >= max txsiz + max rxsiz +#define MINION_BUFSIZ 1024 + +#define u8tou32(_c, _off) (((uint8_t *)(_c))[(_off)+0] + \ + ((uint8_t *)(_c))[(_off)+1] * 0x100 + \ + ((uint8_t *)(_c))[(_off)+2] * 0x10000 + \ + ((uint8_t *)(_c))[(_off)+3] * 0x1000000 ) + +#define MINION_ADDR_WRITE 0x7f +#define MINION_ADDR_READ 0x80 + +#define READ_ADDR(_reg) ((_reg) | MINION_ADDR_READ) +#define WRITE_ADDR(_reg) ((_reg) & MINION_ADDR_WRITE) + +#define IS_ADDR_READ(_reg) (((_reg) & MINION_ADDR_READ) == MINION_ADDR_READ) +#define IS_ADDR_WRITE(_reg) (((_reg) & MINION_ADDR_READ) == 0) + +#define SET_HEAD_WRITE(_h, _reg) ((_h)->reg) = WRITE_ADDR(_reg) +#define SET_HEAD_READ(_h, _reg) ((_h)->reg) = READ_ADDR(_reg) +#define SET_HEAD_SIZ(_h, _siz) \ + do { \ + ((_h)->siz)[0] = (uint8_t)((_siz) & 0xff); \ + ((_h)->siz)[1] = (uint8_t)(((_siz) & 0xff00) >> 8); \ + } while (0) + +struct minion_header { + uint8_t chipid; + uint8_t reg; + uint8_t siz[2]; + uint8_t data[4]; // placeholder +}; + +#define HSIZE() (sizeof(struct minion_header) - 4) + +#define MINION_NOCHIP_SIG 0x00000000 +#define MINION_NOCHIP_SIG2 0xffffffff +#define MINION_CHIP_SIG 0xb1ac8a44 + +/* + * Number of times to try and get the SIG with each chip, + * if the chip returns neither of the above values + * TODO: maybe need some reset between tries, to handle a shift value? + */ +#define MINION_SIG_TRIES 3 + +/* + * TODO: Finding these means the chip is there - but how to fix it? + * The extra &'s are to ensure there is no sign bit issue since + * the sign bit carry in a C bit-shift is compiler dependent + */ +#define MINION_CHIP_SIG_SHIFT1 (((MINION_CHIP_SIG & 0x0000ffff) << 16) & 0xffff0000) +#define MINION_CHIP_SIG_SHIFT2 (((MINION_CHIP_SIG & 0x00ffffff) << 8) & 0xffffff00) +#define MINION_CHIP_SIG_SHIFT3 (((MINION_CHIP_SIG & 0xffffff00) >> 8) & 0x00ffffff) +#define MINION_CHIP_SIG_SHIFT4 (((MINION_CHIP_SIG & 0xffff0000) >> 16) & 0x0000ffff) + +#define MINION_SPI_LED_ON 0xa5a5 +#define MINION_SPI_LED_OFF 0x0 + +// Time since first nonce/last reset before turning on the LED +#define MINION_LED_TEST_TIME 600 + +#define MINION_FREQ_MIN 100 +#define MINION_FREQ_DEF 1200 +#define MINION_FREQ_MAX 1400 +#define MINION_FREQ_FACTOR 100 +#define MINION_FREQ_RESET_STEP MINION_FREQ_FACTOR +#define MINION_FREQ_FACTOR_MIN 1 +#define MINION_FREQ_FACTOR_MAX 14 + +static uint32_t minion_freq[] = { + 0x0, + 0x205032, // 1 = 100Mhz + 0x203042, // 2 = 200Mhz + 0x20204B, // 3 = 300Mhz + 0x201042, // 4 = 400Mhz + 0x201053, // 5 = 500Mhz + 0x200032, // 6 = 600Mhz + 0x20003A, // 7 = 700Mhz + 0x200042, // 8 = 800Mhz + 0x20004B, // 9 = 900Mhz + 0x200053, // 10 = 1000Mhz + 0x21005B, // 11 = 1100Mhz + 0x210064, // 12 = 1200Mhz + 0x21006C, // 13 = 1300Mhz + 0x210074 // 14 = 1400Mhz +}; + +// When hash rate falls below this in the history hash rate, reset it +#define MINION_RESET_PERCENT 75.0 +// When hash rate falls below this after the longer test time +#define MINION_RESET2_PERCENT 85.0 + +// After the above resets, delay sending work for: +#define MINION_RESET_DELAY_s 0.088 + +#define STA_TEMP(_sta) ((uint16_t)((_sta)[3] & 0x1f)) +#define STA_CORES(_sta) ((uint16_t)((_sta)[2])) +#define STA_FREQ(_sta) ((uint32_t)((_sta)[1]) * 0x100 + (uint32_t)((_sta)[0])) + +// Randomly between 1s and 2s per chip +#define MINION_STATS_UPDATE_TIME_mS 1000 +#define MINION_STATS_UPDATE_RAND_mS 1000 + +// Don't report it more than once every ... 5s +#define MINION_IDLE_MESSAGE_ms 5000 + +struct minion_status { + uint16_t temp; + uint16_t cores; + uint32_t freq; + uint32_t quework; + uint32_t chipwork; + uint32_t realwork; // FIFO_STA + struct timeval last; + bool overheat; + bool islow; + bool tohigh; + int lowcount; + uint32_t overheats; + struct timeval lastoverheat; + struct timeval lastrecover; + double overheattime; + uint32_t tempsent; + uint32_t idle; + uint32_t last_rpt_idle; + struct timeval idle_rpt; + struct timeval first_nonce; + uint64_t from_first_good; +}; + +#define ENABLE_CORE(_core, _n) ((_core[_n >> 3]) |= (1 << (_n % 8))) +#define CORE_IDLE(_core, _n) ((_core[_n >> 3]) & (1 << (_n % 8))) + +#define FIFO_RES(_fifo, _off) ((_fifo)[(_off) + 0]) +#define FIFO_CMD(_fifo, _off) ((_fifo)[(_off) + 1]) + +#define RES_GOLD(_res) ((((_res)->status[3]) & 0x80) == 0) +#define RES_CHIPID(_res) (((_res)->status[3]) & 0x1f) +#define RES_CORE(_res) ((_res)->status[2]) +#define RES_TASK(_res) ((int)((_res)->status[1]) * 0x100 + (int)((_res)->status[0])) +#define RES_NONCE(_res) u8tou32((_res)->nonce, 0) + +/* + * This is only valid since we avoid using task_id 0 for work + * However, it isn't really necessary since we only request + * the number of results the result buffer says it has + * However, it is a simple failsafe + */ +#define IS_RESULT(_res) ((_res)->status[1] || (_res)->status[0]) + +struct minion_result { + uint8_t status[DATA_SIZ]; + uint8_t nonce[DATA_SIZ]; +}; + +#define MINION_RES_DATA_SIZ sizeof(struct minion_result) + +/* + * (MINION_SPI_BUFSIZ - HSIZE()) / MINION_RES_DATA_SIZ + * less a little bit to round it out + */ +#define MINION_MAX_RES 120 + +#define MIDSTATE_BYTES 32 +#define MERKLE7_OFFSET 64 +#define MERKLE_BYTES 12 + +#define MINION_MAX_TASK_ID 0xffff + +struct minion_que { + uint8_t task_id[2]; + uint8_t reserved[2]; + uint8_t midstate[MIDSTATE_BYTES]; + uint8_t merkle7[DATA_SIZ]; + uint8_t ntime[DATA_SIZ]; + uint8_t bits[DATA_SIZ]; +}; + +/* + * Max time to wait before checking the task list + * Required, since only urgent tasks trigger an immediate check + * TODO: ? for 2TH/s + */ +#define MINION_TASK_mS 8 + +/* + * Max time to wait before checking the result list for nonces + * This can be long since it's only a failsafe + * cgsem_post is always sent if there are nonces ready to check + */ +#define MINION_NONCE_mS 888 + +// Number of results to make a GPIO interrupt +//#define MINION_RESULT_INT_SIZE 1 +#define MINION_RESULT_INT_SIZE 2 + +/* + * Max time to wait before checking for results + * The interrupt doesn't occur until MINION_RESULT_INT_SIZE results are found + * See comment in minion_spi_reply() at poll() + */ +#define MINION_REPLY_mS 88 + +/* + * Max time to wait before returning the amount of work done + * A result interrupt will send a trigger for this also + * See comment in minion_scanwork() + * This avoids the cgminer master work loop spinning doing nothing + */ +#define MINION_SCAN_mS 88 + +// *** Work lists: generated, queued for a chip, sent to chip +typedef struct work_item { + struct work *work; + uint32_t task_id; + struct timeval sent; + int nonces; + bool urgent; + bool stale; // if stale, don't decrement que/chipwork when discarded + bool rolled; + int errors; // uncertain since the error could mean task_id is wrong + struct timeval created; // when work was generated + uint64_t ioseq; +} WORK_ITEM; + +#define ALLOC_WORK_ITEMS 4096 +#define LIMIT_WORK_ITEMS 0 + +// *** Task queue ready to be sent +typedef struct task_item { + uint64_t tid; + uint8_t chip; + bool write; + uint8_t address; + uint32_t task_id; + uint32_t wsiz; + uint32_t osiz; + uint32_t rsiz; + uint8_t wbuf[MINION_BUFSIZ]; + uint8_t obuf[MINION_BUFSIZ]; + uint8_t rbuf[MINION_BUFSIZ]; + int reply; + bool urgent; + uint8_t work_state; + struct work *work; + K_ITEM *witem; + uint64_t ioseq; +} TASK_ITEM; + +#define ALLOC_TASK_ITEMS 256 +#define LIMIT_TASK_ITEMS 0 + +// *** Results queue ready to be checked +typedef struct res_item { + int chip; + int core; + uint32_t task_id; + uint32_t nonce; + struct timeval when; + /* + * Only once per task_id if no nonces were found + * Sent with core = 0 + * However, currently it always sends it at the end of every task + * TODO: code assumes it doesn't - change later when we + * see what the final hardware does (minor code performance gain) + */ + bool no_nonce; + // If we requested the result twice: + bool another; + uint32_t task_id2; + uint32_t nonce2; +} RES_ITEM; + +#define ALLOC_RES_ITEMS 256 +#define LIMIT_RES_ITEMS 0 + +// *** Per chip nonce history +typedef struct hist_item { + struct timeval when; +} HIST_ITEM; + +#define ALLOC_HIST_ITEMS 4096 +#define LIMIT_HIST_ITEMS 0 + +// How much history to keep (5min) +#define MINION_HISTORY_s 300 +// History required to decide a reset at MINION_FREQ_DEF Mhz +#define MINION_RESET_s 10 +// How many times to reset before changing Freq +// This doesn't include the secondary higher % check +#define MINION_RESET_COUNT 6 + +// To enable the 2nd check +static bool second_check = true; +// Longer time lapse to expect the higher % +// This intercepts a slow GHs drop earlier +#define MINION_RESET2_s 60 + +#if (MINION_RESET_s > MINION_HISTORY_s) +#error "MINION_RESET_s can't be greater than MINION_HISTORY_s" +#endif + +#define FREQ_DELAY(freq) ((float)(MINION_RESET_s * MINION_FREQ_DEF) / (freq)) + +#if (MINION_RESET2_s > MINION_HISTORY_s) +#error "MINION_RESET2_s can't be greater than MINION_HISTORY_s" +#endif + +// FREQ2_DELAY(MINION_FREQ_MIN) = FREQ2_FACTOR * MINION_RESET2_s +#define FREQ2_FACTOR 1.5 + +#define FREQ2_DELAY(freq) ((1.0 + (float)((freq - MINION_FREQ_DEF) * (1 - FREQ2_FACTOR)) / \ + (float)(MINION_FREQ_DEF - MINION_FREQ_MIN)) * MINION_RESET2_s) + +#if (MINION_RESET2_s <= MINION_RESET_s) +#error "MINION_RESET2_s must be greater than MINION_RESET_s" +#endif + +/* If there was no reset for this long, clear the reset history + * (except the last one) since this means the current clock is ok + * with rare resets */ +#define MINION_CLR_s 300 + +#if (MINION_CLR_s <= MINION_RESET2_s) +#error "MINION_CLR_s must be greater than MINION_RESET2_s" +#endif + +// History must be always generated for the reset check +#define MINION_MAX_RESET_CHECK 2 + +/* Floating point reset settings required for the code to work properly + * Basically: RESET2 must be after RESET and CLR must be after RESET2 */ +static void define_test() +{ + float test; + + if (MINION_RESET2_PERCENT <= MINION_RESET_PERCENT) { + quithere(1, "MINION_RESET2_PERCENT=%f must be " + "> MINION_RESET_PERCENT=%f", + MINION_RESET2_PERCENT, MINION_RESET_PERCENT); + } + + test = FREQ_DELAY(MINION_FREQ_MIN); + if (test >= MINION_HISTORY_s) { + quithere(1, "FREQ_DELAY(MINION_FREQ_MIN)=%f must be " + "< MINION_HISTORY_s=%d", + test, MINION_HISTORY_s); + } + + if (MINION_CLR_s <= test) { + quithere(1, "MINION_CLR_s=%d must be > " + "FREQ_DELAY(MINION_FREQ_MIN)=%f", + MINION_CLR_s, test); + } + + if (FREQ2_FACTOR <= 1.0) + quithere(1, "FREQ2_FACTOR=%f must be > 1.0", FREQ2_FACTOR); + + + test = FREQ2_DELAY(MINION_FREQ_MIN); + if (test >= MINION_HISTORY_s) { + quithere(1, "FREQ2_DELAY(MINION_FREQ_MIN)=%f must be " + "< MINION_HISTORY_s=%d", + test, MINION_HISTORY_s); + } + + if (MINION_CLR_s <= test) { + quithere(1, "MINION_CLR_s=%d must be > " + "FREQ2_DELAY(MINION_FREQ_MIN)=%f", + MINION_CLR_s, test); + } +} + +// *** Chip freq/MHs performance history +typedef struct perf_item { + double elapsed; + uint64_t nonces; + uint32_t freq; + double ghs; + struct timeval when; +} PERF_ITEM; + +#define ALLOC_PERF_ITEMS 128 +#define LIMIT_PERF_ITEMS 0 + +// *** 0xff error history +typedef struct xff_item { + time_t when; +} XFF_ITEM; + +#define ALLOC_XFF_ITEMS 100 +#define LIMIT_XFF_ITEMS 100 + +#define DATA_WORK(_item) ((WORK_ITEM *)(_item->data)) +#define DATA_TASK(_item) ((TASK_ITEM *)(_item->data)) +#define DATA_RES(_item) ((RES_ITEM *)(_item->data)) +#define DATA_HIST(_item) ((HIST_ITEM *)(_item->data)) +#define DATA_PERF(_item) ((PERF_ITEM *)(_item->data)) +#define DATA_XFF(_item) ((XFF_ITEM *)(_item->data)) + +// Set this to 1 to enable iostats processing +// N.B. it slows down mining +#define DO_IO_STATS 0 + +#if DO_IO_STATS +#define IO_STAT_NOW(_tv) cgtime(_tv) +#define IO_STAT_STORE(_sta, _fin, _lsta, _lfin, _tsd, _buf, _siz, _reply, _ioc) \ + do { \ + double _diff, _ldiff, _lwdiff, _1time; \ + int _off; \ + _diff = us_tdiff(_fin, _sta); \ + _ldiff = us_tdiff(_lfin, _lsta); \ + _lwdiff = us_tdiff(_sta, _lsta); \ + _1time = us_tdiff(_tsd, _lfin); \ + _off = (int)(_buf[1]) + (_reply >= 0 ? 0 : 0x100); \ + minioninfo->summary.count++; \ + minioninfo->summary.tsd += _1time; \ + minioninfo->iostats[_off].count++; \ + minioninfo->iostats[_off].tsd += _1time; \ + if (_diff <= 0) { \ + minioninfo->summary.zero_delay++; \ + minioninfo->iostats[_off].zero_delay++; \ + } else { \ + minioninfo->summary.total_delay += _diff; \ + if (minioninfo->summary.max_delay < _diff) \ + minioninfo->summary.max_delay = _diff; \ + if (minioninfo->summary.min_delay == 0 || \ + minioninfo->summary.min_delay > _diff) \ + minioninfo->summary.min_delay = _diff; \ + minioninfo->iostats[_off].total_delay += _diff; \ + if (minioninfo->iostats[_off].max_delay < _diff) \ + minioninfo->iostats[_off].max_delay = _diff; \ + if (minioninfo->iostats[_off].min_delay == 0 || \ + minioninfo->iostats[_off].min_delay > _diff) \ + minioninfo->iostats[_off].min_delay = _diff; \ + } \ + if (_ldiff <= 0) { \ + minioninfo->summary.zero_dlock++; \ + minioninfo->iostats[_off].zero_dlock++; \ + } else { \ + minioninfo->summary.total_dlock += _ldiff; \ + if (minioninfo->summary.max_dlock < _ldiff) \ + minioninfo->summary.max_dlock = _ldiff; \ + if (minioninfo->summary.min_dlock == 0 || \ + minioninfo->summary.min_dlock > _ldiff) \ + minioninfo->summary.min_dlock = _ldiff; \ + minioninfo->iostats[_off].total_dlock += _ldiff; \ + if (minioninfo->iostats[_off].max_dlock < _ldiff) \ + minioninfo->iostats[_off].max_dlock = _ldiff; \ + if (minioninfo->iostats[_off].min_dlock == 0 || \ + minioninfo->iostats[_off].min_dlock > _ldiff) \ + minioninfo->iostats[_off].min_dlock = _ldiff; \ + } \ + minioninfo->summary.total_dlwait += _lwdiff; \ + minioninfo->iostats[_off].total_dlwait += _lwdiff; \ + if (_siz == 0) { \ + minioninfo->summary.zero_bytes++; \ + minioninfo->iostats[_off].zero_bytes++; \ + } else { \ + minioninfo->summary.total_bytes += _siz; \ + if (minioninfo->summary.max_bytes < _siz) \ + minioninfo->summary.max_bytes = _siz; \ + if (minioninfo->summary.min_bytes == 0 || \ + minioninfo->summary.min_bytes > _siz) \ + minioninfo->summary.min_bytes = _siz; \ + minioninfo->iostats[_off].total_bytes += _siz; \ + if (minioninfo->iostats[_off].max_bytes < _siz) \ + minioninfo->iostats[_off].max_bytes = _siz; \ + if (minioninfo->iostats[_off].min_bytes == 0 || \ + minioninfo->iostats[_off].min_bytes > _siz) \ + minioninfo->iostats[_off].min_bytes = _siz; \ + } \ + } while (0); + +typedef struct iostat { + uint64_t count; // total ioctl() + + double total_delay; // total elapsed ioctl() + double min_delay; + double max_delay; + uint64_t zero_delay; // how many had <= 0 delay + + // Above but including locking + double total_dlock; + double min_dlock; + double max_dlock; + uint64_t zero_dlock; + + // Total time waiting to get lock + double total_dlwait; + + // these 3 fields are ignored for now since all are '1' + uint64_t total_ioc; // SPI_IOC_MESSAGE(x) + uint64_t min_ioc; + uint64_t max_ioc; + + uint64_t total_bytes; // ioctl() bytes + uint64_t min_bytes; + uint64_t max_bytes; + uint64_t zero_bytes; // how many had siz == 0 + + double tsd; // total doing one extra cgtime() each time +} IOSTAT; +#else +#define IO_STAT_NOW(_tv) +#define IO_STAT_STORE(_sta, _fin, _lsta, _lfin, _tsd, _buf, _siz, _reply, _ioc) +#endif + +static double time_bands[] = { 0.1, 0.5, 1.0, 2.0, 4.0, 8.0, 16.0, 32.0 }; +#define TIME_BANDS ((int)(sizeof(time_bands)/sizeof(double))) + +struct minion_info { + struct thr_info *thr; + struct thr_info spiw_thr; + struct thr_info spir_thr; + struct thr_info res_thr; + + pthread_mutex_t spi_lock; + pthread_mutex_t sta_lock; + + cgsem_t task_ready; + cgsem_t nonce_ready; + cgsem_t scan_work; + + volatile unsigned *gpio; + + int spifd; + char gpiointvalue[64]; + int gpiointfd; + + // I/O or seconds + bool spi_reset_io; + int spi_reset_count; + time_t last_spi_reset; + uint64_t spi_resets; + + // TODO: need to track disabled chips - done? + int chips; + bool has_chip[MINION_CHIPS]; + int init_temp[MINION_CHIPS]; + uint8_t init_cores[MINION_CHIPS][DATA_SIZ*MINION_CORE_REPS]; + + uint8_t chipid[MINION_CHIPS]; // Chip Number + int chip_pin[MINION_CHIPS]; + + uint64_t ioseq; + uint32_t next_task_id; + + // Stats + uint64_t chip_nonces[MINION_CHIPS]; + uint64_t chip_nononces[MINION_CHIPS]; + uint64_t chip_good[MINION_CHIPS]; + uint64_t chip_bad[MINION_CHIPS]; + uint64_t chip_err[MINION_CHIPS]; + uint64_t chip_dup[MINION_CHIPS]; + uint64_t core_good[MINION_CHIPS][MINION_CORES+1]; + uint64_t core_bad[MINION_CHIPS][MINION_CORES+1]; + + uint32_t chip_core_ena[MINION_CORE_REPS][MINION_CHIPS]; + uint32_t chip_core_act[MINION_CORE_REPS][MINION_CHIPS]; + + struct minion_status chip_status[MINION_CHIPS]; + + uint64_t interrupts; + uint64_t result_interrupts; + uint64_t command_interrupts; + char last_interrupt[64]; + + pthread_mutex_t nonce_lock; + uint64_t new_nonces; + + uint64_t ok_nonces; + uint64_t untested_nonces; + uint64_t tested_nonces; + + uint64_t work_unrolled; + uint64_t work_rolled; + + uint64_t spi_errors; + uint64_t fifo_spi_errors[MINION_CHIPS]; + uint64_t res_spi_errors[MINION_CHIPS]; + uint64_t use_res2[MINION_CHIPS]; + + uint64_t tasks_failed[MINION_CHIPS]; + uint64_t tasks_recovered[MINION_CHIPS]; + uint64_t nonces_failed[MINION_CHIPS]; + uint64_t nonces_recovered[MINION_CHIPS]; + struct timeval last_reset[MINION_CHIPS]; + double do_reset[MINION_CHIPS]; + bool flag_reset[MINION_CHIPS]; + + // Work items + K_LIST *wfree_list; + K_STORE *wwork_list; + K_STORE *wstale_list; + K_STORE *wque_list[MINION_CHIPS]; + K_STORE *wchip_list[MINION_CHIPS]; + uint64_t wwork_flushed; + uint64_t wque_flushed; + uint64_t wchip_staled; + + // Task list + K_LIST *tfree_list; + K_STORE *task_list; + K_STORE *treply_list; + + uint64_t next_tid; + + // Nonce replies + K_LIST *rfree_list; + K_STORE *rnonce_list; + + struct timeval last_did; + + // Nonce history + K_LIST *hfree_list; + K_STORE *hchip_list[MINION_CHIPS]; + + int history_gen; + struct timeval chip_chk; + struct timeval chip_rpt; + double history_ghs[MINION_CHIPS]; + // Point in history for MINION_RESET_s + int reset_time[MINION_CHIPS]; + K_ITEM *reset_mark[MINION_CHIPS]; + int reset_count[MINION_CHIPS]; + // Point in history for MINION_RESET2_s + int reset2_time[MINION_CHIPS]; + K_ITEM *reset2_mark[MINION_CHIPS]; + int reset2_count[MINION_CHIPS]; + + // Performance history + K_LIST *pfree_list; + K_STORE *p_list[MINION_CHIPS]; + + // 0xff history + K_LIST *xfree_list; + K_STORE *xff_list; + time_t last_power_cycle; + uint64_t power_cycles; + time_t last_xff; + uint64_t xffs; + uint64_t last_displayed_xff; + + // Gets reset to zero each time it is used in reporting + int res_err_count[MINION_CHIPS]; + +#if DO_IO_STATS + // Total + IOSTAT summary; + + // Two for each command plus wasted extras i.e. direct/fast lookup + // No error uses 0x0 to 0xff, error uses 0x100 to 0x1ff + IOSTAT iostats[0x200]; +#endif + + // Stats on how long work is waiting to move from wwork_list to wque_list + uint64_t que_work; + double que_time; + double que_min; + double que_max; + uint64_t que_bands[TIME_BANDS+1]; + + // From wwork_list to txrx + uint64_t wt_work; + double wt_time; + double wt_min; + double wt_max; + uint64_t wt_bands[TIME_BANDS+1]; + + bool lednow[MINION_CHIPS]; + bool setled[MINION_CHIPS]; + + // When changing the frequency don't modify 'anything' + bool changing[MINION_CHIPS]; + int init_freq[MINION_CHIPS]; + int want_freq[MINION_CHIPS]; + uint32_t freqsent[MINION_CHIPS]; + struct timeval lastfreq[MINION_CHIPS]; + int freqms[MINION_CHIPS]; + + bool initialised; +}; + +#if MINION_ROCKCHIP == 1 +static bool minion_toggle_gpio(struct cgpu_info *minioncgpu, int gpionum) +{ + struct minion_info *minioninfo = (struct minion_info *)(minioncgpu->device_data); + char pindir[64], ena[64], pin[8], dir[64]; + char gpiointvalue[64]; + struct stat st; + int file, err, chip; + ssize_t ret; + + snprintf(pindir, sizeof(pindir), MINION_GPIO_SYS MINION_GPIO_PIN, gpionum); + memset(&st, 0, sizeof(st)); + + if (stat(pindir, &st) == 0) { // already exists + if (!S_ISDIR(st.st_mode)) { + applog(LOG_ERR, "%s: failed1 to enable GPIO pin %d" + " - not a directory", + minioncgpu->drv->dname, gpionum); + return false; + } + } else { + snprintf(ena, sizeof(ena), MINION_GPIO_SYS MINION_GPIO_ENA); + file = open(ena, O_WRONLY | O_SYNC); + if (file == -1) { + applog(LOG_ERR, "%s: failed2 to export GPIO pin %d (%d)" + " - you need to be root?", + minioncgpu->drv->dname, + gpionum, errno); + return false; + } + snprintf(pin, sizeof(pin), MINION_GPIO_ENA_VAL, gpionum); + ret = write(file, pin, (size_t)strlen(pin)); + if (ret != (ssize_t)strlen(pin)) { + if (ret < 0) + err = errno; + else + err = (int)ret; + close(file); + applog(LOG_ERR, "%s: failed3 to export GPIO pin %d (%d:%d)", + minioncgpu->drv->dname, + gpionum, err, (int)strlen(pin)); + return false; + } + close(file); + + // Check again if it exists + memset(&st, 0, sizeof(st)); + if (stat(pindir, &st) != 0) { + applog(LOG_ERR, "%s: failed4 to export GPIO pin %d (%d)", + minioncgpu->drv->dname, + gpionum, errno); + return false; + } + } + + // Set the pin attributes + // Direction + snprintf(dir, sizeof(dir), MINION_GPIO_SYS MINION_GPIO_PIN MINION_GPIO_DIR, gpionum); + file = open(dir, O_WRONLY | O_SYNC); + if (file == -1) { + applog(LOG_ERR, "%s: failed5 to configure GPIO pin %d (%d)" + " - you need to be root?", + minioncgpu->drv->dname, + gpionum, errno); + return false; + } + ret = write(file, MINION_GPIO_DIR_WRITE, sizeof(MINION_GPIO_DIR_WRITE)-1); + if (ret != sizeof(MINION_GPIO_DIR_WRITE)-1) { + if (ret < 0) + err = errno; + else + err = (int)ret; + close(file); + applog(LOG_ERR, "%s: failed6 to configure GPIO pin %d (%d:%d)", + minioncgpu->drv->dname, gpionum, + err, (int)sizeof(MINION_GPIO_DIR_WRITE)-1); + return false; + } + close(file); + + // Open it + snprintf(gpiointvalue, sizeof(gpiointvalue), + MINION_GPIO_SYS MINION_GPIO_PIN MINION_GPIO_VALUE, + gpionum); + int fd = open(gpiointvalue, O_WRONLY); + if (fd == -1) { + applog(LOG_ERR, "%s: failed7 to access GPIO pin %d (%d)", + minioncgpu->drv->dname, + gpionum, errno); + return false; + } + + ret = write(fd, MINION_CHIP_OFF, sizeof(MINION_CHIP_OFF)-1); + if (ret != sizeof(MINION_CHIP_OFF)-1) { + close(fd); + applog(LOG_ERR, "%s: failed8 to toggle off GPIO pin %d (%d:%d)", + minioncgpu->drv->dname, + gpionum, (int)ret, errno); + return false; + } + + cgsleep_ms(MINION_CHIP_DELAY); + + ret = write(fd, MINION_CHIP_ON, sizeof(MINION_CHIP_ON)-1); + if (ret != sizeof(MINION_CHIP_OFF)-1) { + close(fd); + applog(LOG_ERR, "%s: failed9 to toggle on GPIO pin %d (%d:%d)", + minioncgpu->drv->dname, + gpionum, (int)ret, errno); + return false; + } + + close(fd); + minioninfo->last_power_cycle = time(NULL); + minioninfo->power_cycles++; + // Reset all chip led counters + for (chip = 0; chip < (int)MINION_CHIPS; chip++) { + if (minioninfo->has_chip[chip]) + minioninfo->chip_status[chip].first_nonce.tv_sec = 0L; + } + return true; +} +#endif + +static void ready_work(struct cgpu_info *minioncgpu, struct work *work, bool rolled) +{ + struct minion_info *minioninfo = (struct minion_info *)(minioncgpu->device_data); + K_ITEM *item = NULL; + + K_WLOCK(minioninfo->wfree_list); + + item = k_unlink_head(minioninfo->wfree_list); + + DATA_WORK(item)->work = work; + DATA_WORK(item)->task_id = 0; + memset(&(DATA_WORK(item)->sent), 0, sizeof(DATA_WORK(item)->sent)); + DATA_WORK(item)->nonces = 0; + DATA_WORK(item)->urgent = false; + DATA_WORK(item)->rolled = rolled; + DATA_WORK(item)->errors = 0; + cgtime(&(DATA_WORK(item)->created)); + + k_add_head(minioninfo->wwork_list, item); + + K_WUNLOCK(minioninfo->wfree_list); +} + +static bool oldest_nonce(struct cgpu_info *minioncgpu, int *chip, int *core, uint32_t *task_id, + uint32_t *nonce, bool *no_nonce, struct timeval *when, + bool *another, uint32_t *task_id2, uint32_t *nonce2) +{ + struct minion_info *minioninfo = (struct minion_info *)(minioncgpu->device_data); + K_ITEM *item = NULL; + bool found = false; + + K_WLOCK(minioninfo->rnonce_list); + + item = k_unlink_tail(minioninfo->rnonce_list); + if (item) { + found = true; + *chip = DATA_RES(item)->chip; + *core = DATA_RES(item)->core; + *task_id = DATA_RES(item)->task_id; + *nonce = DATA_RES(item)->nonce; + *no_nonce = DATA_RES(item)->no_nonce; + memcpy(when, &(DATA_RES(item)->when), sizeof(*when)); + *another = DATA_RES(item)->another; + *task_id2 = DATA_RES(item)->task_id2; + *nonce2 = DATA_RES(item)->nonce2; + + k_free_head(minioninfo->rfree_list, item); + } + + K_WUNLOCK(minioninfo->rnonce_list); + + return found; +} + +static const char *addr2txt(uint8_t addr) +{ + switch (addr) { + case READ_ADDR(MINION_SYS_CHIP_SIG): + return "RChipSig"; + case READ_ADDR(MINION_SYS_CHIP_STA): + return "RChipSta"; + case WRITE_ADDR(MINION_SYS_SPI_LED): + return "WLed"; + case WRITE_ADDR(MINION_SYS_MISC_CTL): + return "WMiscCtrl"; + case WRITE_ADDR(MINION_SYS_RSTN_CTL): + return "WResetCtrl"; + case READ_ADDR(MINION_SYS_FIFO_STA): + return "RFifoSta"; + case READ_ADDR(MINION_CORE_ENA0_31): + return "RCoreEna0-31"; + case WRITE_ADDR(MINION_CORE_ENA0_31): + return "WCoreEna0-31"; + case READ_ADDR(MINION_CORE_ENA32_63): + return "RCoreEna32-63"; + case WRITE_ADDR(MINION_CORE_ENA32_63): + return "WCoreEna32-63"; + case READ_ADDR(MINION_CORE_ENA64_95): + return "RCoreEna64-95"; + case WRITE_ADDR(MINION_CORE_ENA64_95): + return "WCoreEna64-95"; + case READ_ADDR(MINION_CORE_ENA96_98): + return "RCoreEna96-98"; + case WRITE_ADDR(MINION_CORE_ENA96_98): + return "WCoreEna96-98"; + case READ_ADDR(MINION_CORE_ACT0_31): + return "RCoreAct0-31"; + case READ_ADDR(MINION_CORE_ACT32_63): + return "RCoreAct32-63"; + case READ_ADDR(MINION_CORE_ACT64_95): + return "RCoreAct64-95"; + case READ_ADDR(MINION_CORE_ACT96_98): + return "RCoreAct96-98"; + case READ_ADDR(MINION_RES_DATA): + return "RResData"; + case READ_ADDR(MINION_RES_PEEK): + return "RResPeek"; + case WRITE_ADDR(MINION_QUE_0): + return "WQueWork"; + case READ_ADDR(MINION_NONCE_START): + return "RNonceStart"; + case WRITE_ADDR(MINION_NONCE_START): + return "WNonceStart"; + case READ_ADDR(MINION_NONCE_RANGE): + return "RNonceRange"; + case WRITE_ADDR(MINION_NONCE_RANGE): + return "WNonceRange"; + case READ_ADDR(MINION_SYS_INT_STA): + return "RIntSta"; + case WRITE_ADDR(MINION_SYS_INT_ENA): + return "WIntEna"; + case WRITE_ADDR(MINION_SYS_INT_CLR): + return "WIntClear"; + case WRITE_ADDR(MINION_SYS_BUF_TRIG): + return "WResTrigger"; + case WRITE_ADDR(MINION_SYS_QUE_TRIG): + return "WCmdTrigger"; + case READ_ADDR(MINION_SYS_TEMP_CTL): + return "RTempCtrl"; + case WRITE_ADDR(MINION_SYS_TEMP_CTL): + return "WTempCtrl"; + case READ_ADDR(MINION_SYS_FREQ_CTL): + return "RFreqCtrl"; + case WRITE_ADDR(MINION_SYS_FREQ_CTL): + return "WFreqCtrl"; + case READ_ADDR(MINION_SYS_IDLE_CNT): + return "RIdleCnt"; + } + + // gcc warning if this is in default: + if (IS_ADDR_READ(addr)) + return "RUnhandled"; + else + return "WUnhandled"; +} + +// For display_ioctl() +#define IOCTRL_LOG LOG_WARNING + +// For all other debug so it can easily be switched always on +#define MINION_LOG LOG_DEBUG + +// For task corruption logging +#define MINTASK_LOG LOG_DEBUG + +// Set to 1 for debug +#define MINION_SHOW_IO 0 + +#define DATA_ALL 2048 +#define DATA_OFF 512 + +#if MINION_SHOW_IO +static void display_ioctl(int reply, uint32_t osiz, uint8_t *obuf, uint32_t rsiz, uint8_t *rbuf) +{ + struct minion_result *res; + const char *name, *dir, *ex; + char buf[4096]; + int i, rescount; + + name = addr2txt(obuf[1]); + + if (IS_ADDR_READ(obuf[1])) + dir = "from"; + else + dir = "to"; + + buf[0] = '\0'; + ex = ""; + + switch (obuf[1]) { + case READ_ADDR(MINION_SYS_CHIP_SIG): + case READ_ADDR(MINION_SYS_CHIP_STA): + break; + case WRITE_ADDR(MINION_SYS_SPI_LED): + case WRITE_ADDR(MINION_SYS_MISC_CTL): + case WRITE_ADDR(MINION_SYS_RSTN_CTL): + if (osiz > HSIZE()) { + ex = " wrote "; + __bin2hex(buf, obuf + HSIZE(), osiz - HSIZE()); + } else + ex = " wrote nothing"; + break; + default: + if (IS_ADDR_WRITE(obuf[1])) { + if (osiz > HSIZE()) { + ex = " wrote "; + __bin2hex(buf, obuf + HSIZE(), osiz - HSIZE()); + } else + ex = " wrote nothing"; + } + break; + } + + if (reply < 0) { + applog(IOCTRL_LOG, "%s %s chipid %d osiz %d%s%s", + name, dir, (int)obuf[0], (int)osiz, ex, buf); + applog(IOCTRL_LOG, " reply was error %d", reply); + } else { + if (IS_ADDR_WRITE(obuf[1])) { + applog(IOCTRL_LOG, "%s %s chipid %d osiz %d%s%s", + name, dir, (int)obuf[0], (int)osiz, ex, buf); + applog(IOCTRL_LOG, " write ret was %d", reply); + } else { + switch (obuf[1]) { + case READ_ADDR(MINION_RES_DATA): + rescount = (int)((float)rsiz / (float)MINION_RES_DATA_SIZ); + applog(IOCTRL_LOG, "%s %s chipid %d osiz %d%s%s", + name, dir, (int)obuf[0], (int)osiz, ex, buf); + for (i = 0; i < rescount; i++) { + res = (struct minion_result *)(rbuf + osiz - rsiz + (i * MINION_RES_DATA_SIZ)); + if (!IS_RESULT(res)) { + applog(IOCTRL_LOG, " %s reply %d of %d - none", name, i+1, rescount); + } else { + __bin2hex(buf, res->nonce, DATA_SIZ); + applog(IOCTRL_LOG, " %s reply %d of %d %d(%d) was task 0x%04x" + " chipid %d core %d gold %s nonce 0x%s", + name, i+1, rescount, reply, rsiz, + RES_TASK(res), + (int)RES_CHIPID(res), + (int)RES_CORE(res), + (int)RES_GOLD(res) ? "Y" : "N", + buf); + } + } + break; + case READ_ADDR(MINION_SYS_CHIP_SIG): + case READ_ADDR(MINION_SYS_CHIP_STA): + default: + applog(IOCTRL_LOG, "%s %s chipid %d osiz %d%s%s", + name, dir, (int)obuf[0], (int)osiz, ex, buf); + __bin2hex(buf, rbuf + osiz - rsiz, rsiz); + applog(IOCTRL_LOG, " %s reply %d(%d) was %s", name, reply, rsiz, buf); + break; + } + } + } +} +#endif + +#define MINION_UNEXPECTED_TASK -999 +#define MINION_OVERSIZE_TASK -998 + +static void set_pin(struct minion_info *minioninfo, int pin, bool on) +{ + volatile uint32_t *paddr; + uint32_t value; + int bcm; + + bcm = minionPins[pin].bcm; + + paddr = minioninfo->gpio + ((on ? BCM2835_GPIO_SET0 : BCM2835_GPIO_CLR0) / 4) + (bcm / 10); + + value = 1 << (bcm % 32); + + *paddr = value; + *paddr = value; +} + +static void init_pins(struct minion_info *minioninfo) +{ + int pin; + + // Initialise all pins high as required + MINION_PIN_BEFORE; + for (pin = 0; pin < (int)MINION_PIN_COUNT; pin++) { + set_pin(minioninfo, pin, true); + MINION_PIN_SLEEP; + } +} + +#define EXTRA_LOG_IO 0 + +static bool minion_init_spi(struct cgpu_info *minioncgpu, struct minion_info *minioninfo, int bus, int chip, bool reset); + +static int __do_ioctl(struct cgpu_info *minioncgpu, struct minion_info *minioninfo, + int pin, uint8_t *obuf, uint32_t osiz, uint8_t *rbuf, + uint32_t rsiz, uint64_t *ioseq, MINION_FFL_ARGS) +{ + struct spi_ioc_transfer tran; + bool fail = false, powercycle = false, show = false; + double lastshow, total; + K_ITEM *xitem; + time_t now; + int ret; +#if MINION_SHOW_IO + char dataw[DATA_ALL], datar[DATA_ALL]; +#endif + +#if DO_IO_STATS + struct timeval sta, fin, lsta, lfin, tsd; +#endif + + if ((int)osiz > MINION_BUFSIZ) + quitfrom(1, file, func, line, "%s() invalid osiz %u > %d (chip=%d reg=0x%02x)", + __func__, osiz, MINION_BUFSIZ, (int)(obuf[0]), obuf[1]); + + if (rsiz >= osiz) + quitfrom(1, file, func, line, "%s() invalid rsiz %u >= osiz %u (chip=%u reg=0x%02x)", + __func__, rsiz, osiz, (int)(obuf[0]), obuf[1]); + + memset(&obuf[0] + osiz - rsiz, 0xff, rsiz); + +#if MINION_SHOW_IO + // if the a5/5a outside the data change, it means data overrun or corruption + memset(dataw, 0xa5, sizeof(dataw)); + memset(datar, 0x5a, sizeof(datar)); + memcpy(&dataw[DATA_OFF], &obuf[0], osiz); + + char *buf = bin2hex((unsigned char *)&(dataw[DATA_OFF]), osiz); + applog(IOCTRL_LOG, "*** %s() pin %d cid %d sending %02x %02x %s %02x %02x", + __func__, pin, (int)(dataw[DATA_OFF]), + dataw[0], dataw[DATA_OFF-1], buf, + dataw[DATA_OFF+osiz], dataw[DATA_ALL-1]); + free(buf); +#endif + + memset((char *)rbuf, 0x00, osiz); + +// cgsleep_ms(5); // TODO: a delay ... based on the last command? But subtract elapsed + // i.e. do any commands need a delay after the I/O has completed before the next I/O? + + memset(&tran, 0, sizeof(tran)); + if (osiz < MINION_SPI_BUFSIZ) + tran.len = osiz; + else + return MINION_OVERSIZE_TASK; + + tran.delay_usecs = opt_minion_spiusec; + tran.speed_hz = MINION_SPI_SPEED; + +#if MINION_SHOW_IO + tran.tx_buf = (uintptr_t)&(dataw[DATA_OFF]); + tran.rx_buf = (uintptr_t)&(datar[DATA_OFF]); +#else + tran.tx_buf = (uintptr_t)obuf; + tran.rx_buf = (uintptr_t)rbuf; +#endif + + IO_STAT_NOW(&lsta); + mutex_lock(&(minioninfo->spi_lock)); + if (usepins) { + // Pin low for I/O + MINION_PIN_BEFORE; + set_pin(minioninfo, pin, false); + MINION_PIN_SLEEP; + } + IO_STAT_NOW(&sta); + ret = ioctl(minioninfo->spifd, SPI_IOC_MESSAGE(1), (void *)&tran); + *ioseq = minioninfo->ioseq++; + IO_STAT_NOW(&fin); + if (usepins) { + MINION_PIN_AFTER; + // Pin back high after I/O + set_pin(minioninfo, pin, true); + } + now = time(NULL); + if (ret >= 0 && rbuf[0] == 0xff && rbuf[ret-1] == 0xff && + (obuf[1] == READ_ADDR(MINION_RES_DATA) || obuf[1] == READ_ADDR(MINION_SYS_FIFO_STA))) { + int i; + fail = true; + for (i = 1; i < ret-2; i++) { + if (rbuf[i] != 0xff) { + fail = false; + break; + } + } + if (fail) { + powercycle = show = false; + minioninfo->xffs++; + minioninfo->last_xff = now; + + if (minioninfo->xfree_list->count > 0) + xitem = k_unlink_head(minioninfo->xfree_list); + else + xitem = k_unlink_tail(minioninfo->xff_list); + DATA_XFF(xitem)->when = now; + if (!minioninfo->xff_list->head) + show = true; + else { + // if !changing and xff_list is full + if (!minioninfo->changing[obuf[0]] && + minioninfo->xfree_list->count == 0) { + total = DATA_XFF(xitem)->when - + DATA_XFF(minioninfo->xff_list->tail)->when; + if (total <= MINION_POWER_TIME) { + powercycle = true; + // Discard the history + k_list_transfer_to_head(minioninfo->xff_list, + minioninfo->xfree_list); + k_add_head(minioninfo->xfree_list, xitem); + xitem = NULL; + } + } + + if (!powercycle) { + lastshow = DATA_XFF(xitem)->when - + DATA_XFF(minioninfo->xff_list->head)->when; + show = (lastshow >= 5); + } + } + if (xitem) + k_add_head(minioninfo->xff_list, xitem); + +#if MINION_ROCKCHIP == 1 + if (powercycle) + minion_toggle_gpio(minioncgpu, MINION_POWERCYCLE_GPIO); +#endif + minion_init_spi(minioncgpu, minioninfo, 0, 0, true); + } + } else if (minioninfo->spi_reset_count) { + if (minioninfo->spi_reset_io) { + if (*ioseq > 0 && (*ioseq % minioninfo->spi_reset_count) == 0) + minion_init_spi(minioncgpu, minioninfo, 0, 0, true); + } else { + if (minioninfo->last_spi_reset == 0) + minioninfo->last_spi_reset = now; + else { + if ((now - minioninfo->last_spi_reset) >= minioninfo->spi_reset_count) + minion_init_spi(minioncgpu, minioninfo, 0, 0, true); + minioninfo->last_spi_reset = now; + } + } + } + if (opt_minion_spidelay) + cgsleep_ms(opt_minion_spidelay); + mutex_unlock(&(minioninfo->spi_lock)); + IO_STAT_NOW(&lfin); + IO_STAT_NOW(&tsd); + + IO_STAT_STORE(&sta, &fin, &lsta, &lfin, &tsd, obuf, osiz, ret, 1); + + if (fail) { + if (powercycle) { + applog(LOG_ERR, "%s%d: power cycle ioctl %"PRIu64" (%"PRIu64")", + minioncgpu->drv->name, minioncgpu->device_id, *ioseq, + minioninfo->xffs - minioninfo->last_displayed_xff); + minioninfo->last_displayed_xff = minioninfo->xffs; + } else if (show) { + char *what = "unk"; + switch (obuf[1]) { + case READ_ADDR(MINION_RES_DATA): + what = "nonce"; + break; + case READ_ADDR(MINION_SYS_FIFO_STA): + what = "fifo"; + break; + } + applog(LOG_ERR, "%s%d: reset ioctl %"PRIu64" %s all 0xff (%"PRIu64")", + minioncgpu->drv->name, minioncgpu->device_id, + *ioseq, what, minioninfo->xffs - minioninfo->last_displayed_xff); + minioninfo->last_displayed_xff = minioninfo->xffs; + } + } + +#if MINION_SHOW_IO + if (ret > 0) { + buf = bin2hex((unsigned char *)&(datar[DATA_OFF]), ret); + applog(IOCTRL_LOG, "*** %s() reply %d = pin %d cid %d %02x %02x %s %02x %02x", + __func__, ret, pin, (int)(dataw[DATA_OFF]), + datar[0], datar[DATA_OFF-1], buf, + datar[DATA_OFF+osiz], datar[DATA_ALL-1]); + free(buf); + } else + applog(LOG_ERR, "*** %s() reply = %d", __func__, ret); + + memcpy(&rbuf[0], &datar[DATA_OFF], osiz); + + display_ioctl(ret, osiz, (uint8_t *)(&dataw[DATA_OFF]), rsiz, (uint8_t *)(&datar[DATA_OFF])); +#endif +#if EXTRA_LOG_IO + if (obuf[1] == READ_ADDR(MINION_RES_PEEK) || + obuf[1] == READ_ADDR(MINION_RES_DATA) || + obuf[1] == READ_ADDR(MINION_SYS_FIFO_STA)) { + char *uf1, *uf2, c; + uf1 = bin2hex(obuf, DATA_SIZ); + uf2 = bin2hex(rbuf, (size_t)ret); + switch (obuf[1]) { + case READ_ADDR(MINION_RES_PEEK): + c = 'P'; + break; + case READ_ADDR(MINION_RES_DATA): + c = 'D'; + break; + case READ_ADDR(MINION_SYS_FIFO_STA): + c = 'F'; + break; + } + applog(LOG_WARNING, "*** ioseq %"PRIu64" cmd %c %s rep %.8s %s", + *ioseq, c, uf1, uf2, uf2+8); + free(uf2); + free(uf1); + } + if (obuf[1] == WRITE_ADDR(MINION_QUE_0)) { + char *uf; + uf = bin2hex(obuf, osiz); + applog(LOG_WARNING, "*** ioseq %"PRIu64" work %s", + *ioseq, uf); + free(uf); + } +#endif + return ret; +} + +#if 1 +#define do_ioctl(_pin, _obuf, _osiz, _rbuf, _rsiz, _ioseq) \ + __do_ioctl(minioncgpu, minioninfo, _pin, _obuf, _osiz, _rbuf, \ + _rsiz, _ioseq, MINION_FFL_HERE) +#else +#define do_ioctl(_pin, _obuf, _osiz, _rbuf, _rsiz, _ioseq) \ + _do_ioctl(minioninfo, _pin, _obuf, _osiz, _rbuf, \ + _rsiz, _ioseq, MINION_FFL_HERE) +// This sends an expected to work, SPI command before each SPI command +static int _do_ioctl(struct minion_info *minioninfo, int pin, uint8_t *obuf, uint32_t osiz, uint8_t *rbuf, uint32_t rsiz, uint64_t *ioseq, MINION_FFL_ARGS) +{ + struct minion_header *head; + uint8_t buf1[MINION_BUFSIZ]; + uint8_t buf2[MINION_BUFSIZ]; + uint32_t siz; + + head = (struct minion_header *)buf1; + head->chipid = 1; // Needs to be set to a valid chip + head->reg = READ_ADDR(MINION_SYS_FIFO_STA); + SET_HEAD_SIZ(head, DATA_SIZ); + siz = HSIZE() + DATA_SIZ; + __do_ioctl(minioncgpu, minioninfo, pin, buf1, siz, buf2, MINION_CORE_SIZ, ioseq, MINION_FFL_PASS); + + return __do_ioctl(minioncgpu, minioninfo, pin, obuf, osiz, rbuf, rsiz, ioseq, MINION_FFL_PASS); +} +#endif + +static bool _minion_txrx(struct cgpu_info *minioncgpu, struct minion_info *minioninfo, TASK_ITEM *task, MINION_FFL_ARGS) +{ + struct minion_header *head; + + head = (struct minion_header *)(task->obuf); + head->chipid = minioninfo->chipid[task->chip]; + if (task->write) + SET_HEAD_WRITE(head, task->address); + else + SET_HEAD_READ(head, task->address); + + SET_HEAD_SIZ(head, task->wsiz + task->rsiz); + + if (task->wsiz) + memcpy(&(head->data[0]), task->wbuf, task->wsiz); + task->osiz = HSIZE() + task->wsiz + task->rsiz; + + task->reply = do_ioctl(CHIP_PIN(task->chip), task->obuf, task->osiz, task->rbuf, task->rsiz, + &(task->ioseq)); + if (task->reply < 0) { + applog(LOG_ERR, "%s%d: chip=%d ioctl failed reply=%d err=%d" MINION_FFL, + minioncgpu->drv->name, minioncgpu->device_id, + task->chip, task->reply, errno, MINION_FFL_PASS); + } else if (task->reply < (int)(task->osiz)) { + applog(LOG_ERR, "%s%d: chip=%d ioctl failed to write %d only wrote %d (err=%d)" MINION_FFL, + minioncgpu->drv->name, minioncgpu->device_id, + task->chip, (int)(task->osiz), task->reply, errno, MINION_FFL_PASS); + } + + return (task->reply >= (int)(task->osiz)); +} + +// Only for DATA_SIZ commands +static int build_cmd(struct cgpu_info *minioncgpu, struct minion_info *minioninfo, int chip, uint8_t reg, uint8_t *rbuf, uint32_t rsiz, uint8_t *data) +{ + struct minion_header *head; + uint8_t wbuf[MINION_BUFSIZ]; + uint32_t wsiz; + uint64_t ioseq; + int reply; + + head = (struct minion_header *)wbuf; + head->chipid = minioninfo->chipid[chip]; + head->reg = reg; + SET_HEAD_SIZ(head, DATA_SIZ); + + head->data[0] = data[0]; + head->data[1] = data[1]; + head->data[2] = data[2]; + head->data[3] = data[3]; + + wsiz = HSIZE() + DATA_SIZ; + reply = do_ioctl(CHIP_PIN(chip), wbuf, wsiz, rbuf, rsiz, &ioseq); + + if (reply != (int)wsiz) { + applog(LOG_ERR, "%s: chip %d %s returned %d (should be %d)", + minioncgpu->drv->dname, chip, + addr2txt(head->reg), + reply, (int)wsiz); + } + + return reply; +} + +static void set_freq(struct cgpu_info *minioncgpu, struct minion_info *minioninfo, int chip, int freq) +{ + uint8_t rbuf[MINION_BUFSIZ]; + uint8_t data[4]; + uint32_t value; + __maybe_unused int reply; + + freq /= MINION_FREQ_FACTOR; + if (freq < MINION_FREQ_FACTOR_MIN) + freq = MINION_FREQ_FACTOR_MIN; + if (freq > MINION_FREQ_FACTOR_MAX) + freq = MINION_FREQ_FACTOR_MAX; + value = minion_freq[freq]; + data[0] = (uint8_t)(value & 0xff); + data[1] = (uint8_t)(((value & 0xff00) >> 8) & 0xff); + data[2] = (uint8_t)(((value & 0xff0000) >> 16) & 0xff); + data[3] = (uint8_t)(((value & 0xff000000) >> 24) & 0xff); + + minioninfo->freqsent[chip] = value; + + reply = build_cmd(minioncgpu, minioninfo, + chip, WRITE_ADDR(MINION_SYS_FREQ_CTL), + rbuf, 0, data); + + cgtime(&(minioninfo->lastfreq[chip])); + applog(LOG_DEBUG, "%s%i: chip %d freq %d sec %d usec %d", + minioncgpu->drv->name, minioncgpu->device_id, + chip, freq, + (int)(minioninfo->lastfreq[chip].tv_sec) % 10, + (int)(minioninfo->lastfreq[chip].tv_usec)); + + // Reset all this info on chip reset or freq change + minioninfo->reset_time[chip] = (int)FREQ_DELAY(minioninfo->init_freq[chip]); + if (second_check) + minioninfo->reset2_time[chip] = (int)FREQ2_DELAY(minioninfo->init_freq[chip]); + + minioninfo->chip_status[chip].first_nonce.tv_sec = 0L; + + // Discard chip history (if there is any) + if (minioninfo->hfree_list) { + K_WLOCK(minioninfo->hfree_list); + k_list_transfer_to_head(minioninfo->hchip_list[chip], minioninfo->hfree_list); + minioninfo->reset_mark[chip] = NULL; + minioninfo->reset_count[chip] = 0; + K_WUNLOCK(minioninfo->hfree_list); + } +} + +static void init_chip(struct cgpu_info *minioncgpu, struct minion_info *minioninfo, int chip) +{ + uint8_t rbuf[MINION_BUFSIZ]; + uint8_t data[4]; + __maybe_unused int reply; + int choice; + + // Complete chip reset + data[0] = 0x00; + data[1] = 0x00; + data[2] = 0xa5; + data[3] = 0xf5; + + reply = build_cmd(minioncgpu, minioninfo, + chip, WRITE_ADDR(MINION_SYS_RSTN_CTL), + rbuf, 0, data); + + // Default reset + data[0] = SYS_RSTN_CTL_INIT; + data[1] = 0x00; + data[2] = 0x00; + data[3] = 0x00; + + reply = build_cmd(minioncgpu, minioninfo, + chip, WRITE_ADDR(MINION_SYS_RSTN_CTL), + rbuf, 0, data); + + // Default initialisation + data[0] = SYS_MISC_CTL_DEFAULT; + data[1] = 0x00; + data[2] = 0x00; + data[3] = 0x00; + + reply = build_cmd(minioncgpu, minioninfo, + chip, WRITE_ADDR(MINION_SYS_MISC_CTL), + rbuf, 0, data); + + // Set chip frequency + choice = minioninfo->init_freq[chip]; + if (choice < MINION_FREQ_MIN) + choice = MINION_FREQ_MIN; + if (choice > MINION_FREQ_MAX) + choice = MINION_FREQ_MAX; + minioninfo->init_freq[chip] = choice; + set_freq(minioncgpu, minioninfo, chip, choice); + + // Set temp threshold + choice = minioninfo->init_temp[chip]; + if (choice == MINION_TEMP_CTL_DISABLE) + choice = MINION_TEMP_CTL_DISABLE_VALUE; + else { + if (choice < MINION_TEMP_CTL_MIN_VALUE || choice > MINION_TEMP_CTL_MAX_VALUE) + choice = MINION_TEMP_CTL_DEF; + choice -= MINION_TEMP_CTL_MIN_VALUE; + choice /= MINION_TEMP_CTL_STEP; + choice += MINION_TEMP_CTL_MIN; + if (choice < MINION_TEMP_CTL_MIN) + choice = MINION_TEMP_CTL_MIN; + if (choice > MINION_TEMP_CTL_MAX) + choice = MINION_TEMP_CTL_MAX; + } + data[0] = (uint8_t)choice; + data[1] = 0; + data[2] = 0; + data[3] = 0; + + minioninfo->chip_status[chip].tempsent = choice; + + reply = build_cmd(minioncgpu, minioninfo, + chip, WRITE_ADDR(MINION_SYS_TEMP_CTL), + rbuf, 0, data); +} + +static void enable_chip_cores(struct cgpu_info *minioncgpu, struct minion_info *minioninfo, int chip) +{ + uint8_t rbuf[MINION_BUFSIZ]; + uint8_t data[4]; + __maybe_unused int reply; + int rep, i; + + for (i = 0; i < 4; i++) + data[i] = minioninfo->init_cores[chip][i]; + + reply = build_cmd(minioncgpu, minioninfo, + chip, WRITE_ADDR(MINION_CORE_ENA0_31), + rbuf, 0, data); + + for (i = 0; i < 4; i++) + data[i] = minioninfo->init_cores[chip][i+4]; + + reply = build_cmd(minioncgpu, minioninfo, + chip, WRITE_ADDR(MINION_CORE_ENA32_63), + rbuf, 0, data); + + for (i = 0; i < 4; i++) + data[i] = minioninfo->init_cores[chip][i+8]; + + reply = build_cmd(minioncgpu, minioninfo, + chip, WRITE_ADDR(MINION_CORE_ENA64_95), + rbuf, 0, data); + + for (i = 0; i < 4; i++) + data[i] = minioninfo->init_cores[chip][i+12]; + + reply = build_cmd(minioncgpu, minioninfo, + chip, WRITE_ADDR(MINION_CORE_ENA96_98), + rbuf, 0, data); + +/* Below is for testing - disabled/use default + // 1/3 range for each of the 3 cores +// data[0] = 0x55; +// data[1] = 0x55; +// data[2] = 0x55; +// data[3] = 0x55; + + // quicker replies +// data[0] = 0x05; +// data[1] = 0x05; +// data[2] = 0x05; +// data[3] = 0x05; + + // 0x00000100 at 20MH/s per core = 336TH/s if 1 nonce per work item + // 0x00001000 = 21.0TH/s - so well above 2TH/s + // 0x00002000 = 10.5TH/s - above 2TH/s + // speed test + data[0] = 0x00; + data[1] = 0x01; + data[2] = 0x00; + data[3] = 0x00; +// data[3] = 0x20; // slow it down for other testing + + // 2 cores +// data[0] = 0xff; +// data[1] = 0xff; +// data[2] = 0xff; +// data[3] = 0x7f; + + reply = build_cmd(minioncgpu, minioninfo, + chip, WRITE_ADDR(MINION_NONCE_RANGE), + rbuf, 0, data); + + // find lots more nonces in a short time on my test data + // i.e. emulate a MUCH higher hash rate on SPI and work + // generation/testing + // Current test data (same repeated 10 times) has nonce 0x05e0ed6d + data[0] = 0x00; + data[1] = 0xed; + data[2] = 0xe0; + data[3] = 0x05; + + reply = build_cmd(minioncgpu, minioninfo, + chip, WRITE_ADDR(MINION_NONCE_START), + rbuf, 0, data); +*/ + + // store the core ena state + for (rep = 0; rep < MINION_CORE_REPS; rep++) { + data[0] = 0x0; + data[1] = 0x0; + data[2] = 0x0; + data[3] = 0x0; + + reply = build_cmd(minioncgpu, minioninfo, + chip, READ_ADDR(MINION_CORE_ENA0_31 + rep), + rbuf, MINION_CORE_SIZ, data); + + minioninfo->chip_core_ena[rep][chip] = *((uint32_t *)&(rbuf[HSIZE()])); + } + + // store the core active state + for (rep = 0; rep < MINION_CORE_REPS; rep++) { + data[0] = 0x0; + data[1] = 0x0; + data[2] = 0x0; + data[3] = 0x0; + + reply = build_cmd(minioncgpu, minioninfo, + chip, READ_ADDR(MINION_CORE_ACT0_31 + rep), + rbuf, MINION_CORE_SIZ, data); + + minioninfo->chip_core_act[rep][chip] = *((uint32_t *)&(rbuf[HSIZE()])); + } +} + +#if ENABLE_INT_NONO +static void enable_interrupt(struct cgpu_info *minioncgpu, struct minion_info *minioninfo, int chip) +{ + uint8_t rbuf[MINION_BUFSIZ]; + uint8_t data[4]; + __maybe_unused int reply; + + data[0] = MINION_RESULT_INT_SIZE; + data[1] = 0x00; + data[2] = 0x00; + data[3] = 0x00; + + reply = build_cmd(minioncgpu, minioninfo, + chip, WRITE_ADDR(MINION_SYS_BUF_TRIG), + rbuf, 0, data); + +// data[0] = MINION_QUE_MAX; // spaces available ... i.e. empty +// data[0] = MINION_QUE_LOW; // spaces in use + data[0] = MINION_QUE_MAX - MINION_QUE_LOW; // spaces available + data[1] = 0x00; + data[2] = 0x00; + data[3] = 0x00; + + reply = build_cmd(minioncgpu, minioninfo, + chip, WRITE_ADDR(MINION_SYS_QUE_TRIG), + rbuf, 0, data); + +// data[0] = MINION_RESULT_INT; + data[0] = MINION_RESULT_INT | MINION_CMD_INT; + data[1] = 0x00; + data[2] = 0x00; + data[3] = 0x00; + + reply = build_cmd(minioncgpu, minioninfo, + chip, WRITE_ADDR(MINION_SYS_INT_ENA), + rbuf, 0, data); +} +#endif + +static void minion_detect_one(struct cgpu_info *minioncgpu, struct minion_info *minioninfo, int pin, int chipid) +{ + struct minion_header *head; + uint8_t wbuf[MINION_BUFSIZ]; + uint8_t rbuf[MINION_BUFSIZ]; + uint32_t wsiz, rsiz; + int reply, tries, newchip; + uint64_t ioseq; + bool ok; + + head = (struct minion_header *)wbuf; + head->chipid = chipid; + rsiz = MINION_SYS_SIZ; + SET_HEAD_READ(head, MINION_SYS_CHIP_SIG); + SET_HEAD_SIZ(head, rsiz); + wsiz = HSIZE() + rsiz; + + tries = 0; + ok = false; + do { + reply = do_ioctl(pin, wbuf, wsiz, rbuf, rsiz, &ioseq); + + if (reply == (int)(wsiz)) { + uint32_t sig = u8tou32(rbuf, wsiz - rsiz); + + if (sig == MINION_CHIP_SIG) { + newchip = (minioninfo->chips)++; + minioninfo->has_chip[newchip] = true; + minioninfo->chipid[newchip] = chipid; + minioninfo->chip_pin[newchip] = pin; + ok = true; + } else { + if (sig == MINION_CHIP_SIG_SHIFT1 || + sig == MINION_CHIP_SIG_SHIFT2 || + sig == MINION_CHIP_SIG_SHIFT3 || + sig == MINION_CHIP_SIG_SHIFT4) { + applog(LOG_WARNING, "%s: pin %d chipid %d detect offset got" + " 0x%08x wanted 0x%08x", + minioncgpu->drv->dname, pin, chipid, + sig, MINION_CHIP_SIG); + } else { + if (sig == MINION_NOCHIP_SIG || + sig == MINION_NOCHIP_SIG2) // Assume no chip + ok = true; + else { + applog(LOG_ERR, "%s: pin %d chipid %d detect failed" + " got 0x%08x wanted 0x%08x", + minioncgpu->drv->dname, pin, + chipid, sig, MINION_CHIP_SIG); + } + } + } + } else { + applog(LOG_ERR, "%s: pin %d chipid %d reply %d ignored should be %d", + minioncgpu->drv->dname, pin, chipid, reply, (int)(wsiz)); + } + } while (!ok && ++tries <= MINION_SIG_TRIES); + + if (!ok) { + applog(LOG_ERR, "%s: pin %d chipid %d - detect failure status", + minioncgpu->drv->dname, pin, chipid); + } +} + +// Simple detect - just check each chip for the signature +static void minion_detect_chips(struct cgpu_info *minioncgpu, struct minion_info *minioninfo) +{ + int pin, chipid, chip; + int pinend, start_freq, want_freq, freqms; + +#if MINION_ROCKCHIP == 1 + minion_toggle_gpio(minioncgpu, MINION_POWERCYCLE_GPIO); + cgsleep_ms(100); +#endif + + if (usepins) { + init_pins(minioninfo); + pinend = (int)MINION_PIN_COUNT; + } else + pinend = 1; + + for (pin = 0; pin < pinend; pin++) { + for (chipid = MINION_MIN_CHIP; chipid <= MINION_MAX_CHIP; chipid++) { + minion_detect_one(minioncgpu, minioninfo, pin, chipid); + } + } + + if (minioninfo->chips) { + for (chip = 0; chip < (int)MINION_CHIPS; chip++) { + if (minioninfo->has_chip[chip]) { + want_freq = minioninfo->init_freq[chip]; + start_freq = want_freq * opt_minion_freqpercent / 100; + start_freq -= (start_freq % MINION_FREQ_FACTOR); + if (start_freq < MINION_FREQ_MIN) + start_freq = MINION_FREQ_MIN; + minioninfo->want_freq[chip] = want_freq; + minioninfo->init_freq[chip] = start_freq; + if (start_freq != want_freq) { + freqms = opt_minion_freqchange; + freqms /= ((want_freq - start_freq) / MINION_FREQ_FACTOR); + if (freqms < 0) + freqms = -freqms; + minioninfo->freqms[chip] = freqms; + minioninfo->changing[chip] = true; + } + init_chip(minioncgpu, minioninfo, chip); + enable_chip_cores(minioncgpu, minioninfo, chip); + } + } + +#if ENABLE_INT_NONO + // After everything is ready + for (chip = 0; chip < MINION_CHIPS; chip++) + if (minioninfo->has_chip[chip]) + enable_interrupt(minioncgpu, minioninfo, chip); +#endif + } +} + +static const char *minion_modules[] = { +#if MINION_ROCKCHIP == 0 + "i2c-dev", + "i2c-bcm2708", + "spidev", + "spi-bcm2708", +#endif + NULL +}; + +static struct { + int request; + int value; +} minion_ioc[] = { + { SPI_IOC_RD_MODE, 0 }, + { SPI_IOC_WR_MODE, 0 }, + { SPI_IOC_RD_BITS_PER_WORD, 8 }, + { SPI_IOC_WR_BITS_PER_WORD, 8 }, + { SPI_IOC_RD_MAX_SPEED_HZ, MINION_SPI_SPEED }, + { SPI_IOC_WR_MAX_SPEED_HZ, MINION_SPI_SPEED }, + { -1, -1 } +}; + +static bool minion_init_spi(struct cgpu_info *minioncgpu, struct minion_info *minioninfo, int bus, int chip, bool reset) +{ + int i, err, data; + char buf[64]; + + if (reset) { + // TODO: maybe slow it down? + close(minioninfo->spifd); + if (opt_minion_spisleep) + cgsleep_ms(opt_minion_spisleep); + minioninfo->spifd = open(minioncgpu->device_path, O_RDWR); + if (minioninfo->spifd < 0) + goto bad_out; + minioninfo->spi_resets++; +// minioninfo->chip_status[chip].first_nonce.tv_sec = 0L; + } else { + for (i = 0; minion_modules[i]; i++) { + snprintf(buf, sizeof(buf), "modprobe %s", minion_modules[i]); + err = system(buf); + if (err) { + applog(LOG_ERR, "%s: failed to modprobe %s (%d) - you need to be root?", + minioncgpu->drv->dname, + minion_modules[i], err); + goto bad_out; + } + } + + snprintf(buf, sizeof(buf), "/dev/spidev%d.%d", bus, chip); + minioninfo->spifd = open(buf, O_RDWR); + if (minioninfo->spifd < 0) { + applog(LOG_ERR, "%s: failed to open spidev (%d)", + minioncgpu->drv->dname, + errno); + goto bad_out; + } + + minioncgpu->device_path = strdup(buf); + } + + for (i = 0; minion_ioc[i].value != -1; i++) { + data = minion_ioc[i].value; + err = ioctl(minioninfo->spifd, minion_ioc[i].request, (void *)&data); + if (err < 0) { + applog(LOG_ERR, "%s: failed ioctl configuration (%d) (%d)", + minioncgpu->drv->dname, + i, errno); + goto close_out; + } + } + + return true; + +close_out: + close(minioninfo->spifd); + minioninfo->spifd = 0; + free(minioncgpu->device_path); + minioncgpu->device_path = NULL; + +bad_out: + return false; +} + +static bool minion_setup_chip_select(struct cgpu_info *minioncgpu, struct minion_info *minioninfo) +{ + volatile uint32_t *paddr; + uint32_t mask, value, mem; + int count, memfd, pin, bcm; + + memfd = open(minion_memory, O_RDWR | O_SYNC); + if (memfd < 0) { + applog(LOG_ERR, "%s: failed open %s (%d)", + minioncgpu->drv->dname, + minion_memory, errno); + return false; + } + + minioninfo->gpio = (volatile unsigned *)mmap(NULL, MINION_PAGE_SIZE, + PROT_READ | PROT_WRITE, + MAP_SHARED, memfd, + minion_memory_addr); + if (minioninfo->gpio == MAP_FAILED) { + close(memfd); + applog(LOG_ERR, "%s: failed mmap gpio (%d)", + minioncgpu->drv->dname, + errno); + return false; + } + + close(memfd); + + for (pin = 0; pin < (int)MINION_PIN_COUNT; pin++) { + bcm = minionPins[pin].bcm; + + paddr = minioninfo->gpio + (BCM2835_GPIO_FSEL0 / 4) + (bcm / 10); + + // Set each pin to be an output pin + mask = BCM2835_GPIO_FSEL_MASK << ((bcm % 10) * 3); + value = BCM2835_GPIO_FSEL_OUTPUT << ((bcm % 10) * 3); + + // Read settings + mem = *paddr; + *paddr; + + mem = (mem & ~mask) | (value & mask); + + // Write appended setting + *paddr = mem; + *paddr = mem; + + count++; + } + + if (count == 0) + return false; + else + return true; +} + +#if ENABLE_INT_NONO +static bool minion_init_gpio_interrupt(struct cgpu_info *minioncgpu, struct minion_info *minioninfo) +{ + char pindir[64], ena[64], pin[8], dir[64], edge[64], act[64]; + struct stat st; + int file, err; + ssize_t ret; + + snprintf(pindir, sizeof(pindir), MINION_GPIO_SYS MINION_GPIO_PIN, + MINION_GPIO_RESULT_INT_PIN); + memset(&st, 0, sizeof(st)); + + if (stat(pindir, &st) == 0) { // already exists + if (!S_ISDIR(st.st_mode)) { + applog(LOG_ERR, "%s: failed1 to enable GPIO pin %d interrupt" + " - not a directory", + minioncgpu->drv->dname, + MINION_GPIO_RESULT_INT_PIN); + return false; + } + } else { + snprintf(ena, sizeof(ena), MINION_GPIO_SYS MINION_GPIO_ENA); + file = open(ena, O_WRONLY | O_SYNC); + if (file == -1) { + applog(LOG_ERR, "%s: failed2 to enable GPIO pin %d interrupt (%d)" + " - you need to be root?", + minioncgpu->drv->dname, + MINION_GPIO_RESULT_INT_PIN, + errno); + return false; + } + snprintf(pin, sizeof(pin), MINION_GPIO_ENA_VAL, MINION_GPIO_RESULT_INT_PIN); + ret = write(file, pin, (size_t)strlen(pin)); + if (ret != (ssize_t)strlen(pin)) { + if (ret < 0) + err = errno; + else + err = (int)ret; + close(file); + applog(LOG_ERR, "%s: failed3 to enable GPIO pin %d interrupt (%d:%d)", + minioncgpu->drv->dname, + MINION_GPIO_RESULT_INT_PIN, + err, (int)strlen(pin)); + return false; + } + close(file); + + // Check again if it exists + memset(&st, 0, sizeof(st)); + if (stat(pindir, &st) != 0) { + applog(LOG_ERR, "%s: failed4 to enable GPIO pin %d interrupt (%d)", + minioncgpu->drv->dname, + MINION_GPIO_RESULT_INT_PIN, + errno); + return false; + } + } + + // Set the pin attributes + // Direction + snprintf(dir, sizeof(dir), MINION_GPIO_SYS MINION_GPIO_PIN MINION_GPIO_DIR, + MINION_GPIO_RESULT_INT_PIN); + file = open(dir, O_WRONLY | O_SYNC); + if (file == -1) { + applog(LOG_ERR, "%s: failed5 to enable GPIO pin %d interrupt (%d)" + " - you need to be root?", + minioncgpu->drv->dname, + MINION_GPIO_RESULT_INT_PIN, + errno); + return false; + } + ret = write(file, MINION_GPIO_DIR_READ, (size_t)strlen(MINION_GPIO_DIR_READ)); + if (ret != (ssize_t)strlen(MINION_GPIO_DIR_READ)) { + if (ret < 0) + err = errno; + else + err = (int)ret; + close(file); + applog(LOG_ERR, "%s: failed6 to enable GPIO pin %d interrupt (%d:%d)", + minioncgpu->drv->dname, + MINION_GPIO_RESULT_INT_PIN, + err, (int)strlen(MINION_GPIO_DIR_READ)); + return false; + } + close(file); + + // Edge + snprintf(edge, sizeof(edge), MINION_GPIO_SYS MINION_GPIO_PIN MINION_GPIO_EDGE, + MINION_GPIO_RESULT_INT_PIN); + file = open(edge, O_WRONLY | O_SYNC); + if (file == -1) { + applog(LOG_ERR, "%s: failed7 to enable GPIO pin %d interrupt (%d)", + minioncgpu->drv->dname, + MINION_GPIO_RESULT_INT_PIN, + errno); + return false; + } + ret = write(file, MINION_GPIO_EDGE_RISING, (size_t)strlen(MINION_GPIO_EDGE_RISING)); + if (ret != (ssize_t)strlen(MINION_GPIO_EDGE_RISING)) { + if (ret < 0) + err = errno; + else + err = (int)ret; + close(file); + applog(LOG_ERR, "%s: failed8 to enable GPIO pin %d interrupt (%d:%d)", + minioncgpu->drv->dname, + MINION_GPIO_RESULT_INT_PIN, + err, (int)strlen(MINION_GPIO_EDGE_RISING)); + return false; + } + close(file); + + // Active + snprintf(act, sizeof(act), MINION_GPIO_SYS MINION_GPIO_PIN MINION_GPIO_ACT, + MINION_GPIO_RESULT_INT_PIN); + file = open(act, O_WRONLY | O_SYNC); + if (file == -1) { + applog(LOG_ERR, "%s: failed9 to enable GPIO pin %d interrupt (%d)", + minioncgpu->drv->dname, + MINION_GPIO_RESULT_INT_PIN, + errno); + return false; + } + ret = write(file, MINION_GPIO_ACT_HI, (size_t)strlen(MINION_GPIO_ACT_HI)); + if (ret != (ssize_t)strlen(MINION_GPIO_ACT_HI)) { + if (ret < 0) + err = errno; + else + err = (int)ret; + close(file); + applog(LOG_ERR, "%s: failed10 to enable GPIO pin %d interrupt (%d:%d)", + minioncgpu->drv->dname, + MINION_GPIO_RESULT_INT_PIN, + err, (int)strlen(MINION_GPIO_ACT_HI)); + return false; + } + close(file); + + // Setup fd access to Value + snprintf(minioninfo->gpiointvalue, sizeof(minioninfo->gpiointvalue), + MINION_GPIO_SYS MINION_GPIO_PIN MINION_GPIO_VALUE, + MINION_GPIO_RESULT_INT_PIN); + minioninfo->gpiointfd = open(minioninfo->gpiointvalue, O_RDONLY); + if (minioninfo->gpiointfd == -1) { + applog(LOG_ERR, "%s: failed11 to enable GPIO pin %d interrupt (%d)", + minioncgpu->drv->dname, + MINION_GPIO_RESULT_INT_PIN, + errno); + return false; + } + + return true; +} +#endif + +// Default meaning all cores +static void default_all_cores(uint8_t *cores) +{ + int i; + + // clear all bits + for (i = 0; i < (int)(DATA_SIZ * MINION_CORE_REPS); i++) + cores[i] = 0x00; + + // enable (only) all cores + for (i = 0; i < MINION_CORES; i++) + ENABLE_CORE(cores, i); +} + +static void minion_process_options(struct minion_info *minioninfo) +{ + int last_freq, last_temp; + char *freq, *temp, *core, *comma, *buf, *plus, *minus; + uint8_t last_cores[DATA_SIZ*MINION_CORE_REPS]; + int i, core1, core2; + bool cleared; + + if (opt_minion_spireset && *opt_minion_spireset) { + bool is_io = true; + int val; + + switch (tolower(*opt_minion_spireset)) { + case 'i': + is_io = true; + break; + case 's': + is_io = false; + break; + default: + applog(LOG_WARNING, "ERR: Invalid SPI reset '%s'", + opt_minion_spireset); + goto skip; + } + val = atoi(opt_minion_spireset+1); + if (val < 0 || val > 9999) { + applog(LOG_WARNING, "ERR: Invalid SPI reset '%s'", + opt_minion_spireset); + } else { + minioninfo->spi_reset_io = is_io; + minioninfo->spi_reset_count = val; + minioninfo->last_spi_reset = time(NULL); + } + } +skip: + last_freq = MINION_FREQ_DEF; + if (opt_minion_freq && *opt_minion_freq) { + buf = freq = strdup(opt_minion_freq); + comma = strchr(freq, ','); + if (comma) + *(comma++) = '\0'; + + for (i = 0; i < (int)MINION_CHIPS; i++) { + if (freq && isdigit(*freq)) { + last_freq = (int)(round((double)atoi(freq) / (double)MINION_FREQ_FACTOR)) * MINION_FREQ_FACTOR; + if (last_freq < MINION_FREQ_MIN) + last_freq = MINION_FREQ_MIN; + if (last_freq > MINION_FREQ_MAX) + last_freq = MINION_FREQ_MAX; + + freq = comma; + if (comma) { + comma = strchr(freq, ','); + if (comma) + *(comma++) = '\0'; + } + } + minioninfo->init_freq[i] = last_freq; + } + free(buf); + } + + last_temp = MINION_TEMP_CTL_DEF; + if (opt_minion_temp && *opt_minion_temp) { + buf = temp = strdup(opt_minion_temp); + comma = strchr(temp, ','); + if (comma) + *(comma++) = '\0'; + + for (i = 0; i < (int)MINION_CHIPS; i++) { + if (temp) { + if (isdigit(*temp)) { + last_temp = atoi(temp); + last_temp -= (last_temp % MINION_TEMP_CTL_STEP); + if (last_temp < MINION_TEMP_CTL_MIN_VALUE) + last_temp = MINION_TEMP_CTL_MIN_VALUE; + if (last_temp > MINION_TEMP_CTL_MAX_VALUE) + last_temp = MINION_TEMP_CTL_MAX_VALUE; + } else { + if (strcasecmp(temp, MINION_TEMP_DISABLE) == 0) + last_temp = MINION_TEMP_CTL_DISABLE; + } + + temp = comma; + if (comma) { + comma = strchr(temp, ','); + if (comma) + *(comma++) = '\0'; + } + } + minioninfo->init_temp[i] = last_temp; + } + free(buf); + } + + default_all_cores(&(last_cores[0])); + // default to all cores until we find valid data + cleared = false; + if (opt_minion_cores && *opt_minion_cores) { + buf = core = strdup(opt_minion_cores); + comma = strchr(core, ','); + if (comma) + *(comma++) = '\0'; + + for (i = 0; i < (int)MINION_CHIPS; i++) { + // default to previous until we find valid data + cleared = false; + if (core) { + plus = strchr(core, '+'); + if (plus) + *(plus++) = '\0'; + while (core) { + minus = strchr(core, '-'); + if (minus) + *(minus++) = '\0'; + if (isdigit(*core)) { + core1 = atoi(core); + if (core1 >= 0 && core1 < MINION_CORES) { + if (!minus) { + if (!cleared) { + memset(last_cores, 0, sizeof(last_cores)); + cleared = true; + } + ENABLE_CORE(last_cores, core1); + } else { + core2 = atoi(minus); + if (core2 >= core1) { + if (core2 >= MINION_CORES) + core2 = MINION_CORES - 1; + while (core1 <= core2) { + if (!cleared) { + memset(last_cores, 0, + sizeof(last_cores)); + cleared = true; + } + ENABLE_CORE(last_cores, core1); + core1++; + } + } + } + } + } else { + if (strcasecmp(core, MINION_CORE_ALL) == 0) + default_all_cores(&(last_cores[0])); + } + core = plus; + if (plus) { + plus = strchr(core, '+'); + if (plus) + *(plus++) = '\0'; + } + } + core = comma; + if (comma) { + comma = strchr(core, ','); + if (comma) + *(comma++) = '\0'; + } + } + memcpy(&(minioninfo->init_cores[i][0]), &(last_cores[0]), sizeof(last_cores)); + } + free(buf); + } +} + +static void minion_detect(bool hotplug) +{ + struct cgpu_info *minioncgpu = NULL; + struct minion_info *minioninfo = NULL; + char buf[512]; + size_t off; + int i; + + if (hotplug) + return; + + define_test(); + + minioncgpu = calloc(1, sizeof(*minioncgpu)); + if (unlikely(!minioncgpu)) + quithere(1, "Failed to calloc minioncgpu"); + + minioncgpu->drv = &minion_drv; + minioncgpu->deven = DEV_ENABLED; + minioncgpu->threads = 1; + + minioninfo = calloc(1, sizeof(*minioninfo)); // everything '0' + if (unlikely(!minioninfo)) + quithere(1, "Failed to calloc minioninfo"); + minioncgpu->device_data = (void *)minioninfo; + + if (!minion_init_spi(minioncgpu, minioninfo, MINION_SPI_BUS, MINION_SPI_CHIP, false)) + goto unalloc; + +#if ENABLE_INT_NONO + if (!minion_init_gpio_interrupt(minioncgpu, minioninfo)) + goto unalloc; +#endif + + + if (usepins) { + if (!minion_setup_chip_select(minioncgpu, minioninfo)) + goto unalloc; + } + + mutex_init(&(minioninfo->spi_lock)); + mutex_init(&(minioninfo->sta_lock)); + + for (i = 0; i < (int)MINION_CHIPS; i++) { + minioninfo->init_freq[i] = MINION_FREQ_DEF; + minioninfo->init_temp[i] = MINION_TEMP_CTL_DEF; + default_all_cores(&(minioninfo->init_cores[i][0])); + } + + minion_process_options(minioninfo); + + applog(LOG_WARNING, "%s: checking for chips ...", minioncgpu->drv->dname); + + minion_detect_chips(minioncgpu, minioninfo); + + buf[0] = '\0'; + for (i = 0; i < (int)MINION_CHIPS; i++) { + if (minioninfo->has_chip[i]) { + off = strlen(buf); + snprintf(buf + off, sizeof(buf) - off, " %d:%d/%d", + i, minioninfo->chip_pin[i], (int)(minioninfo->chipid[i])); + } + } + + applog(LOG_WARNING, "%s: found %d chip%s:%s", + minioncgpu->drv->dname, minioninfo->chips, + (minioninfo->chips == 1) ? "" : "s", buf); + + if (minioninfo->chips == 0) + goto cleanup; + + if (!add_cgpu(minioncgpu)) + goto cleanup; + + mutex_init(&(minioninfo->nonce_lock)); + + minioninfo->wfree_list = k_new_list("Work", sizeof(WORK_ITEM), + ALLOC_WORK_ITEMS, LIMIT_WORK_ITEMS, true); + minioninfo->wwork_list = k_new_store(minioninfo->wfree_list); + minioninfo->wstale_list = k_new_store(minioninfo->wfree_list); + // Initialise them all in case we later decide to enable chips + for (i = 0; i < (int)MINION_CHIPS; i++) { + minioninfo->wque_list[i] = k_new_store(minioninfo->wfree_list); + minioninfo->wchip_list[i] = k_new_store(minioninfo->wfree_list); + } + + minioninfo->tfree_list = k_new_list("Task", sizeof(TASK_ITEM), + ALLOC_TASK_ITEMS, LIMIT_TASK_ITEMS, true); + minioninfo->task_list = k_new_store(minioninfo->tfree_list); + minioninfo->treply_list = k_new_store(minioninfo->tfree_list); + + minioninfo->rfree_list = k_new_list("Reply", sizeof(RES_ITEM), + ALLOC_RES_ITEMS, LIMIT_RES_ITEMS, true); + minioninfo->rnonce_list = k_new_store(minioninfo->rfree_list); + + minioninfo->history_gen = MINION_MAX_RESET_CHECK; + minioninfo->hfree_list = k_new_list("History", sizeof(HIST_ITEM), + ALLOC_HIST_ITEMS, LIMIT_HIST_ITEMS, true); + for (i = 0; i < (int)MINION_CHIPS; i++) + minioninfo->hchip_list[i] = k_new_store(minioninfo->hfree_list); + + minioninfo->pfree_list = k_new_list("Performance", sizeof(PERF_ITEM), + ALLOC_PERF_ITEMS, LIMIT_PERF_ITEMS, true); + for (i = 0; i < (int)MINION_CHIPS; i++) + minioninfo->p_list[i] = k_new_store(minioninfo->pfree_list); + + minioninfo->xfree_list = k_new_list("0xff", sizeof(XFF_ITEM), + ALLOC_XFF_ITEMS, LIMIT_XFF_ITEMS, true); + minioninfo->xff_list = k_new_store(minioninfo->xfree_list); + + cgsem_init(&(minioninfo->task_ready)); + cgsem_init(&(minioninfo->nonce_ready)); + cgsem_init(&(minioninfo->scan_work)); + + minioninfo->initialised = true; + + dupalloc(minioncgpu, 10); + + return; + +cleanup: + close(minioninfo->gpiointfd); + close(minioninfo->spifd); + mutex_destroy(&(minioninfo->sta_lock)); + mutex_destroy(&(minioninfo->spi_lock)); +unalloc: + free(minioninfo); + free(minioncgpu); +} + +static char *minion_api_set(struct cgpu_info *minioncgpu, char *option, char *setting, char *replybuf) +{ + struct minion_info *minioninfo = (struct minion_info *)(minioncgpu->device_data); + int chip, val; + char *colon; + + if (strcasecmp(option, "help") == 0) { + sprintf(replybuf, "reset: chip 0-%d freq: 0-%d:%d-%d " + "ledcount: 0-100 ledlimit: 0-200 " + "spidelay: 0-9999 spireset i|s0-9999 " + "spisleep: 0-9999", + minioninfo->chips - 1, + minioninfo->chips - 1, + MINION_FREQ_MIN, MINION_FREQ_MAX); + return replybuf; + } + + if (strcasecmp(option, "reset") == 0) { + if (!setting || !*setting) { + sprintf(replybuf, "missing chip to reset"); + return replybuf; + } + + chip = atoi(setting); + if (chip < 0 || chip >= minioninfo->chips) { + sprintf(replybuf, "invalid reset: chip '%s' valid range 0-%d", + setting, + minioninfo->chips); + return replybuf; + } + + if (!minioninfo->has_chip[chip]) { + sprintf(replybuf, "unable to reset chip %d - chip disabled", + chip); + return replybuf; + } + minioninfo->flag_reset[chip] = true; + return NULL; + } + + // This sets up a freq step up/down to the given freq without a reset + if (strcasecmp(option, "freq") == 0) { + if (!setting || !*setting) { + sprintf(replybuf, "missing chip:freq"); + return replybuf; + } + + colon = strchr(setting, ':'); + if (!colon) { + sprintf(replybuf, "missing ':' for chip:freq"); + return replybuf; + } + + *(colon++) = '\0'; + if (!*colon) { + sprintf(replybuf, "missing freq in chip:freq"); + return replybuf; + } + + chip = atoi(setting); + if (chip < 0 || chip >= minioninfo->chips) { + sprintf(replybuf, "invalid freq: chip '%s' valid range 0-%d", + setting, + minioninfo->chips); + return replybuf; + } + + if (!minioninfo->has_chip[chip]) { + sprintf(replybuf, "unable to modify chip %d - chip not enabled", + chip); + return replybuf; + } + + val = atoi(colon); + if (val < MINION_FREQ_MIN || val > MINION_FREQ_MAX) { + sprintf(replybuf, "invalid freq: '%s' valid range %d-%d", + setting, + MINION_FREQ_MIN, MINION_FREQ_MAX); + return replybuf; + } + + int want_freq = val - (val % MINION_FREQ_FACTOR); + int start_freq = minioninfo->init_freq[chip]; + int freqms; + + if (want_freq != start_freq) { + minioninfo->changing[chip] = false; + freqms = opt_minion_freqchange; + freqms /= ((want_freq - start_freq) / MINION_FREQ_FACTOR); + if (freqms < 0) + freqms = -freqms; + minioninfo->freqms[chip] = freqms; + minioninfo->want_freq[chip] = want_freq; + cgtime(&(minioninfo->lastfreq[chip])); + minioninfo->changing[chip] = true; + } + + return NULL; + } + + if (strcasecmp(option, "ledcount") == 0) { + if (!setting || !*setting) { + sprintf(replybuf, "missing ledcount value"); + return replybuf; + } + + val = atoi(setting); + if (val < 0 || val > 100) { + sprintf(replybuf, "invalid ledcount: '%s' valid range 0-100", + setting); + return replybuf; + } + + opt_minion_ledcount = val; + return NULL; + } + + if (strcasecmp(option, "ledlimit") == 0) { + if (!setting || !*setting) { + sprintf(replybuf, "missing ledlimit value"); + return replybuf; + } + + val = atoi(setting); + if (val < 0 || val > 200) { + sprintf(replybuf, "invalid ledlimit: GHs '%s' valid range 0-200", + setting); + return replybuf; + } + + opt_minion_ledlimit = val; + return NULL; + } + + if (strcasecmp(option, "spidelay") == 0) { + if (!setting || !*setting) { + sprintf(replybuf, "missing spidelay value"); + return replybuf; + } + + val = atoi(setting); + if (val < 0 || val > 9999) { + sprintf(replybuf, "invalid spidelay: ms '%s' valid range 0-9999", + setting); + return replybuf; + } + + opt_minion_spidelay = val; + return NULL; + } + + if (strcasecmp(option, "spireset") == 0) { + bool is_io = true; + + if (!setting || !*setting) { + sprintf(replybuf, "missing spireset value"); + return replybuf; + } + + switch (tolower(*setting)) { + case 'i': + is_io = true; + break; + case 's': + is_io = false; + break; + default: + sprintf(replybuf, "invalid spireset: '%s' must start with i or s", + setting); + return replybuf; + } + val = atoi(setting+1); + if (val < 0 || val > 9999) { + sprintf(replybuf, "invalid spireset: %c '%s' valid range 0-9999", + *setting, setting+1); + return replybuf; + } + + minioninfo->spi_reset_io = is_io; + minioninfo->spi_reset_count = val; + minioninfo->last_spi_reset = time(NULL); + + return NULL; + } + + if (strcasecmp(option, "spisleep") == 0) { + if (!setting || !*setting) { + sprintf(replybuf, "missing spisleep value"); + return replybuf; + } + + val = atoi(setting); + if (val < 0 || val > 9999) { + sprintf(replybuf, "invalid spisleep: ms '%s' valid range 0-9999", + setting); + return replybuf; + } + + opt_minion_spisleep = val; + return NULL; + } + + if (strcasecmp(option, "spiusec") == 0) { + if (!setting || !*setting) { + sprintf(replybuf, "missing spiusec value"); + return replybuf; + } + + val = atoi(setting); + if (val < 0 || val > 9999) { + sprintf(replybuf, "invalid spiusec: '%s' valid range 0-9999", + setting); + return replybuf; + } + + opt_minion_spiusec = val; + return NULL; + } + + sprintf(replybuf, "Unknown option: %s", option); + return replybuf; +} + +static void minion_identify(__maybe_unused struct cgpu_info *minioncgpu) +{ + // flash a led +} + +/* + * SPI/ioctl write thread + * Non urgent work is to keep the queue full + * Urgent work is when an LP occurs (or the queue is empty/low) + */ +static void *minion_spi_write(void *userdata) +{ + struct cgpu_info *minioncgpu = (struct cgpu_info *)userdata; + struct minion_info *minioninfo = (struct minion_info *)(minioncgpu->device_data); + K_ITEM *item, *tail, *task, *work; + TASK_ITEM *titem; + + applog(MINION_LOG, "%s%i: SPI writing...", + minioncgpu->drv->name, minioncgpu->device_id); + + // Wait until we're ready + while (minioncgpu->shutdown == false) { + if (minioninfo->initialised) { + break; + } + cgsleep_ms(1); // asap to start mining + } + + // TODO: combine all urgent into a single I/O? + // Then combine all state 1 for the same chip into a single I/O ? + // (then again for state 2?) + while (minioncgpu->shutdown == false) { + item = NULL; + K_WLOCK(minioninfo->task_list); + tail = minioninfo->task_list->tail; + if (tail) { + // Find first urgent item + item = tail; + while (item && !(DATA_TASK(item)->urgent)) + item = item->prev; + + // No urgent items, just do the tail + if (!item) + item = tail; + + k_unlink_item(minioninfo->task_list, item); + } + K_WUNLOCK(minioninfo->task_list); + + if (item) { + bool do_txrx = true; + bool store_reply = true; + struct timeval now; + double howlong; + int i; + + titem = DATA_TASK(item); + + switch (titem->address) { + // TODO: case MINION_SYS_TEMP_CTL: + // TODO: case MINION_SYS_FREQ_CTL: + case READ_ADDR(MINION_SYS_CHIP_STA): + case WRITE_ADDR(MINION_SYS_SPI_LED): + case WRITE_ADDR(MINION_SYS_RSTN_CTL): + case WRITE_ADDR(MINION_SYS_INT_CLR): + case READ_ADDR(MINION_SYS_IDLE_CNT): + case READ_ADDR(MINION_CORE_ENA0_31): + case READ_ADDR(MINION_CORE_ENA32_63): + case READ_ADDR(MINION_CORE_ENA64_95): + case READ_ADDR(MINION_CORE_ENA96_98): + case READ_ADDR(MINION_CORE_ACT0_31): + case READ_ADDR(MINION_CORE_ACT32_63): + case READ_ADDR(MINION_CORE_ACT64_95): + case READ_ADDR(MINION_CORE_ACT96_98): + store_reply = false; + break; + case WRITE_ADDR(MINION_QUE_0): +//applog(LOG_ERR, "%s%i: ZZZ send task_id 0x%04x - chip %d", minioncgpu->drv->name, minioncgpu->device_id, titem->task_id, titem->chip); + store_reply = false; + break; + default: + do_txrx = false; + titem->reply = MINION_UNEXPECTED_TASK; + applog(LOG_ERR, "%s%i: Unexpected task address 0x%02x (%s)", + minioncgpu->drv->name, minioncgpu->device_id, + (unsigned int)(titem->address), + addr2txt(titem->address)); + + break; + } + + if (do_txrx) { + if (titem->witem) { + cgtime(&now); + howlong = tdiff(&now, &(DATA_WORK(titem->witem)->created)); + minioninfo->wt_work++; + minioninfo->wt_time += howlong; + if (minioninfo->wt_min == 0 || minioninfo->wt_min > howlong) + minioninfo->wt_min = howlong; + else if (minioninfo->wt_max < howlong) + minioninfo->wt_max = howlong; + for (i = 0; i < TIME_BANDS; i++) { + if (howlong < time_bands[i]) { + minioninfo->wt_bands[i]++; + break; + } + } + if (i >= TIME_BANDS) + minioninfo->wt_bands[TIME_BANDS]++; + } + + minion_txrx(titem); + + int chip = titem->chip; + switch (titem->address) { + case READ_ADDR(MINION_SYS_CHIP_STA): + if (titem->reply >= (int)(titem->osiz)) { + uint8_t *rep = &(titem->rbuf[titem->osiz - titem->rsiz]); + mutex_lock(&(minioninfo->sta_lock)); + minioninfo->chip_status[chip].temp = STA_TEMP(rep); + minioninfo->chip_status[chip].cores = STA_CORES(rep); + minioninfo->chip_status[chip].freq = STA_FREQ(rep); + mutex_unlock(&(minioninfo->sta_lock)); + + if (minioninfo->chip_status[chip].overheat) { + switch (STA_TEMP(rep)) { + case MINION_TEMP_40: + case MINION_TEMP_60: + case MINION_TEMP_80: + cgtime(&(minioninfo->chip_status[chip].lastrecover)); + minioninfo->chip_status[chip].overheat = false; + applog(LOG_WARNING, "%s%d: chip %d cooled, restarting", + minioncgpu->drv->name, + minioncgpu->device_id, + chip); + cgtime(&(minioninfo->chip_status[chip].lastrecover)); + minioninfo->chip_status[chip].overheattime += + tdiff(&(minioninfo->chip_status[chip].lastrecover), + &(minioninfo->chip_status[chip].lastoverheat)); + break; + default: + break; + } + } else { + if (opt_minion_overheat && STA_TEMP(rep) == MINION_TEMP_OVER) { + cgtime(&(minioninfo->chip_status[chip].lastoverheat)); + minioninfo->chip_status[chip].overheat = true; + applog(LOG_WARNING, "%s%d: chip %d overheated! idling", + minioncgpu->drv->name, + minioncgpu->device_id, + chip); + K_WLOCK(minioninfo->tfree_list); + task = k_unlink_head(minioninfo->tfree_list); + DATA_TASK(task)->tid = ++(minioninfo->next_tid); + DATA_TASK(task)->chip = chip; + DATA_TASK(task)->write = true; + DATA_TASK(task)->address = MINION_SYS_RSTN_CTL; + DATA_TASK(task)->task_id = 0; // ignored + DATA_TASK(task)->wsiz = MINION_SYS_SIZ; + DATA_TASK(task)->rsiz = 0; + DATA_TASK(task)->wbuf[0] = SYS_RSTN_CTL_FLUSH; + DATA_TASK(task)->wbuf[1] = 0; + DATA_TASK(task)->wbuf[2] = 0; + DATA_TASK(task)->wbuf[3] = 0; + DATA_TASK(task)->urgent = true; + k_add_head(minioninfo->task_list, task); + K_WUNLOCK(minioninfo->tfree_list); + minioninfo->chip_status[chip].overheats++; + } + } + } + break; + case READ_ADDR(MINION_SYS_IDLE_CNT): + { + uint32_t *cnt = (uint32_t *)&(titem->rbuf[titem->osiz - titem->rsiz]); + minioninfo->chip_status[chip].idle = *cnt; + } + break; + case WRITE_ADDR(MINION_SYS_RSTN_CTL): + // Do this here after it has actually been flushed + if ((titem->wbuf[0] & SYS_RSTN_CTL_FLUSH) == SYS_RSTN_CTL_FLUSH) { + int cnt = 0; + K_WLOCK(minioninfo->wwork_list); + work = minioninfo->wchip_list[chip]->head; + while (work) { + cnt++; + DATA_WORK(work)->stale = true; + work = work->next; + } + minioninfo->chip_status[chip].chipwork = 0; + minioninfo->chip_status[chip].realwork = 0; + minioninfo->wchip_staled += cnt; +#if MINION_SHOW_IO + applog(IOCTRL_LOG, "RSTN chip %d (cnt=%d) cw0=%u rw0=%u qw=%u", + chip, cnt, + minioninfo->chip_status[chip].chipwork, + minioninfo->chip_status[chip].realwork, + minioninfo->chip_status[chip].quework); +#endif + K_WUNLOCK(minioninfo->wwork_list); + } + break; + case WRITE_ADDR(MINION_QUE_0): + K_WLOCK(minioninfo->wchip_list[chip]); + k_unlink_item(minioninfo->wque_list[chip], titem->witem); + k_add_head(minioninfo->wchip_list[chip], titem->witem); + DATA_WORK(titem->witem)->ioseq = titem->ioseq; + minioninfo->chip_status[chip].quework--; + minioninfo->chip_status[chip].chipwork++; +#if MINION_SHOW_IO + applog(IOCTRL_LOG, "QUE_0 chip %d cw+1=%u rw=%u qw-1=%u", + chip, + minioninfo->chip_status[chip].chipwork, + minioninfo->chip_status[chip].realwork, + minioninfo->chip_status[chip].quework); +#endif + K_WUNLOCK(minioninfo->wchip_list[chip]); + applog(LOG_DEBUG, "%s%d: task 0x%04x sent to chip %d", + minioncgpu->drv->name, minioncgpu->device_id, + titem->task_id, chip); + break; + case READ_ADDR(MINION_CORE_ENA0_31): + case READ_ADDR(MINION_CORE_ENA32_63): + case READ_ADDR(MINION_CORE_ENA64_95): + case READ_ADDR(MINION_CORE_ENA96_98): + { + uint32_t *rep = (uint32_t *)&(titem->rbuf[titem->osiz - titem->rsiz]); + int off = titem->address - READ_ADDR(MINION_CORE_ENA0_31); + minioninfo->chip_core_ena[off][chip] = *rep; + } + break; + case READ_ADDR(MINION_CORE_ACT0_31): + case READ_ADDR(MINION_CORE_ACT32_63): + case READ_ADDR(MINION_CORE_ACT64_95): + case READ_ADDR(MINION_CORE_ACT96_98): + { + uint32_t *rep = (uint32_t *)&(titem->rbuf[titem->osiz - titem->rsiz]); + int off = titem->address - READ_ADDR(MINION_CORE_ACT0_31); + minioninfo->chip_core_act[off][chip] = *rep; + } + break; + case WRITE_ADDR(MINION_SYS_INT_CLR): + case WRITE_ADDR(MINION_SYS_SPI_LED): + break; + default: + break; + } + } + + K_WLOCK(minioninfo->treply_list); + if (store_reply) + k_add_head(minioninfo->treply_list, item); + else + k_free_head(minioninfo->tfree_list, item); + K_WUNLOCK(minioninfo->treply_list); + + /* + * Always check for the next task immediately if we just did one + * i.e. empty the task queue + */ + continue; + } + cgsem_mswait(&(minioninfo->task_ready), MINION_TASK_mS); + } + return NULL; +} + +/* + * SPI/ioctl reply thread + * ioctl done every interrupt or MINION_REPLY_mS checking for results + */ +static void *minion_spi_reply(void *userdata) +{ + struct cgpu_info *minioncgpu = (struct cgpu_info *)userdata; + struct minion_info *minioninfo = (struct minion_info *)(minioncgpu->device_data); + struct minion_result *result1, *result2, *use1, *use2; + K_ITEM *item; + TASK_ITEM fifo_task, res1_task, res2_task; + int chip, resoff; + bool somelow; + struct timeval now; + +#if ENABLE_INT_NONO + uint64_t ioseq; + TASK_ITEM clr_task; + struct pollfd pfd; + struct minion_header *head; + uint8_t rbuf[MINION_BUFSIZ]; + uint8_t wbuf[MINION_BUFSIZ]; + uint32_t wsiz, rsiz; + int ret, reply; + bool gotreplies = false; +#endif + + applog(MINION_LOG, "%s%i: SPI replying...", + minioncgpu->drv->name, minioncgpu->device_id); + + // Wait until we're ready + while (minioncgpu->shutdown == false) { + if (minioninfo->initialised) { + break; + } + cgsleep_ms(2); + } + + fifo_task.chip = 0; + fifo_task.write = false; + fifo_task.address = MINION_SYS_FIFO_STA; + fifo_task.wsiz = 0; + fifo_task.rsiz = MINION_SYS_SIZ; + + res1_task.chip = 0; + res1_task.write = false; + if (minreread) + res1_task.address = MINION_RES_PEEK; + else + res1_task.address = MINION_RES_DATA; + res1_task.wsiz = 0; + res1_task.rsiz = MINION_RES_DATA_SIZ; + + res2_task.chip = 0; + res2_task.write = false; + res2_task.address = MINION_RES_DATA; + res2_task.wsiz = 0; + res2_task.rsiz = MINION_RES_DATA_SIZ; + +#if ENABLE_INT_NONO + // Clear RESULT_INT after reading all results + clr_task.chip = 0; + clr_task.write = true; + clr_task.address = MINION_SYS_INT_CLR; + clr_task.wsiz = MINION_SYS_SIZ; + clr_task.rsiz = 0; + clr_task.wbuf[0] = MINION_RESULT_INT; + clr_task.wbuf[1] = 0; + clr_task.wbuf[2] = 0; + clr_task.wbuf[3] = 0; + + memset(&pfd, 0, sizeof(pfd)); + pfd.fd = minioninfo->gpiointfd; + pfd.events = POLLPRI; + + head = (struct minion_header *)wbuf; + SET_HEAD_SIZ(head, MINION_SYS_SIZ); + wsiz = HSIZE() + MINION_SYS_SIZ; + rsiz = MINION_SYS_SIZ; // for READ, use 0 for WRITE +#endif + + somelow = false; + while (minioncgpu->shutdown == false) { + for (chip = 0; chip < (int)MINION_CHIPS; chip++) { + if (minioninfo->has_chip[chip]) { + int tries = 0; + uint8_t res, cmd; + + if (minioninfo->changing[chip] && + ms_tdiff(&now, &minioninfo->lastfreq[chip]) > + minioninfo->freqms[chip]) { + int want_freq = minioninfo->want_freq[chip]; + int init_freq = minioninfo->init_freq[chip]; + + if (want_freq > init_freq) { + minioninfo->init_freq[chip] += MINION_FREQ_FACTOR; + init_freq += MINION_FREQ_FACTOR; + + set_freq(minioncgpu, minioninfo, chip, init_freq); + } else if (want_freq < init_freq) { + minioninfo->init_freq[chip] -= MINION_FREQ_FACTOR; + init_freq -= MINION_FREQ_FACTOR; + + set_freq(minioncgpu, minioninfo, chip, init_freq); + } + + if (init_freq == want_freq) + minioninfo->changing[chip] = false; + } + + while (++tries < 4) { + res = cmd = 0; + fifo_task.chip = chip; + fifo_task.reply = 0; + minion_txrx(&fifo_task); + if (fifo_task.reply <= 0) { + minioninfo->spi_errors++; + minioninfo->fifo_spi_errors[chip]++; + minioninfo->res_err_count[chip]++; + break; + } else { + if (fifo_task.reply < (int)(fifo_task.osiz)) { + char *buf = bin2hex((unsigned char *)(&(fifo_task.rbuf[fifo_task.osiz - fifo_task.rsiz])), + (int)(fifo_task.rsiz)); + applog(LOG_DEBUG, "%s%i: Chip %d Bad fifo reply (%s) size %d, should be %d", + minioncgpu->drv->name, minioncgpu->device_id, + chip, buf, + fifo_task.reply, (int)(fifo_task.osiz)); + free(buf); + minioninfo->spi_errors++; + minioninfo->fifo_spi_errors[chip]++; + minioninfo->res_err_count[chip]++; + } else { + if (fifo_task.reply > (int)(fifo_task.osiz)) { + applog(LOG_DEBUG, "%s%i: Chip %d Unexpected fifo reply size %d, " + "expected only %d", + minioncgpu->drv->name, minioncgpu->device_id, + chip, fifo_task.reply, (int)(fifo_task.osiz)); + } + res = FIFO_RES(fifo_task.rbuf, fifo_task.osiz - fifo_task.rsiz); + cmd = FIFO_CMD(fifo_task.rbuf, fifo_task.osiz - fifo_task.rsiz); + // valid reply? + if (res <= MINION_QUE_MAX && cmd <= MINION_QUE_MAX) + break; + + applog(LOG_DEBUG, "%s%i: Chip %d Bad fifo reply res %d (max is %d) " + "cmd %d (max is %d)", + minioncgpu->drv->name, minioncgpu->device_id, + chip, (int)res, MINION_QUE_MAX, + (int)cmd, MINION_QUE_MAX); + minioninfo->spi_errors++; + minioninfo->fifo_spi_errors[chip]++; + minioninfo->res_err_count[chip]++; + } + } + } + + // Give up on this chip this round + if (tries >= 4) + continue; + + K_WLOCK(minioninfo->wwork_list); + // have to just assume it's always correct since we can't verify it + minioninfo->chip_status[chip].realwork = (uint32_t)cmd; +#if MINION_SHOW_IO + applog(IOCTRL_LOG, "SetReal chip %d cw=%u rw==%u qw=%u", + chip, + minioninfo->chip_status[chip].chipwork, + minioninfo->chip_status[chip].realwork, + minioninfo->chip_status[chip].quework); +#endif + K_WUNLOCK(minioninfo->wwork_list); + + if (cmd < MINION_QUE_LOW) { + somelow = true; + // Flag it in case the count is wrong + K_WLOCK(minioninfo->wwork_list); + minioninfo->chip_status[chip].islow = true; + minioninfo->chip_status[chip].lowcount = (int)cmd; + K_WUNLOCK(minioninfo->wwork_list); + } + + /* + * Chip has results? + * You can't request results unless it says it has some. + * We don't ever directly flush the output queue while processing + * (except at startup) so the answer is always valid + * i.e. there could be more, but never less ... unless the reply was corrupt + */ + if (res > MINION_MAX_RES) { + applog(LOG_ERR, "%s%i: Large work reply chip %d res %d", + minioncgpu->drv->name, minioncgpu->device_id, chip, res); + minioninfo->spi_errors++; + minioninfo->fifo_spi_errors[chip]++; + minioninfo->res_err_count[chip]++; + res = 1; // Just read one result + } +//else +//applog(LOG_ERR, "%s%i: work reply res %d", minioncgpu->drv->name, minioncgpu->device_id, res); + uint8_t left = res; + int peeks = 0; + while (left > 0) { + res = left; + if (res > MINION_MAX_RES) + res = MINION_MAX_RES; + left -= res; +repeek: + res1_task.chip = chip; + res1_task.reply = 0; + res1_task.rsiz = res * MINION_RES_DATA_SIZ; + minion_txrx(&res1_task); + if (res1_task.reply <= 0) + break; + else { + cgtime(&now); + if (res1_task.reply < (int)MINION_RES_DATA_SIZ) { + char *buf = bin2hex((unsigned char *)(&(res1_task.rbuf[res1_task.osiz - res1_task.rsiz])), (int)(res1_task.rsiz)); + applog(LOG_ERR, "%s%i: Chip %d Bad work reply (%s) size %d, should be at least %d", + minioncgpu->drv->name, minioncgpu->device_id, + chip, buf, + res1_task.reply, (int)MINION_RES_DATA_SIZ); + free(buf); + minioninfo->spi_errors++; + minioninfo->res_spi_errors[chip]++; + minioninfo->res_err_count[chip]++; + } else { + if (res1_task.reply != (int)(res1_task.osiz)) { + applog(LOG_ERR, "%s%i: Chip %d Unexpected work reply size %d, expected %d", + minioncgpu->drv->name, minioncgpu->device_id, + chip, res1_task.reply, (int)(res1_task.osiz)); + minioninfo->spi_errors++; + minioninfo->res_spi_errors[chip]++; + minioninfo->res_err_count[chip]++; + // Can retry a PEEK without losing data + if (minreread) { + if (++peeks < 4) + goto repeek; + break; + } + } + + if (minreread) { + res2_task.chip = chip; + res2_task.reply = 0; + res2_task.rsiz = res * MINION_RES_DATA_SIZ; + minion_txrx(&res2_task); + if (res2_task.reply <= 0) { + minioninfo->spi_errors++; + minioninfo->res_spi_errors[chip]++; + minioninfo->res_err_count[chip]++; + } + } + + for (resoff = res1_task.osiz - res1_task.rsiz; resoff < (int)res1_task.osiz; resoff += MINION_RES_DATA_SIZ) { + result1 = (struct minion_result *)&(res1_task.rbuf[resoff]); + if (minreread && resoff < (int)res2_task.osiz) + result2 = (struct minion_result *)&(res2_task.rbuf[resoff]); + else + result2 = NULL; + + if (IS_RESULT(result1) || (minreread && result2 && IS_RESULT(result2))) { + K_WLOCK(minioninfo->rfree_list); + item = k_unlink_head(minioninfo->rfree_list); + K_WUNLOCK(minioninfo->rfree_list); + + if (IS_RESULT(result1)) { + use1 = result1; + if (minreread && result2 && IS_RESULT(result2)) + use2 = result2; + else + use2 = NULL; + } else { + use1 = result2; + use2 = NULL; + minioninfo->use_res2[chip]++; + } + + //DATA_RES(item)->chip = RES_CHIPID(use1); + // We can avoid any SPI transmission error of the chip number + DATA_RES(item)->chip = (uint8_t)chip; + if (minioninfo->chipid[chip] != RES_CHIPID(use1)) { + minioninfo->spi_errors++; + minioninfo->res_spi_errors[chip]++; + minioninfo->res_err_count[chip]++; + } + if (use2 && minioninfo->chipid[chip] != RES_CHIPID(use2)) { + minioninfo->spi_errors++; + minioninfo->res_spi_errors[chip]++; + minioninfo->res_err_count[chip]++; + } + DATA_RES(item)->core = RES_CORE(use1); + DATA_RES(item)->task_id = RES_TASK(use1); + DATA_RES(item)->nonce = RES_NONCE(use1); + DATA_RES(item)->no_nonce = !RES_GOLD(use1); + memcpy(&(DATA_RES(item)->when), &now, sizeof(now)); + applog(LOG_DEBUG, "%s%i: reply task_id 0x%04x" + " - chip %d - gold %d", + minioncgpu->drv->name, + minioncgpu->device_id, + RES_TASK(use1), + (int)RES_CHIPID(use1), + (int)RES_GOLD(use1)); + + if (!use2) + DATA_RES(item)->another = false; + else { + DATA_RES(item)->another = true; + DATA_RES(item)->task_id2 = RES_TASK(use2); + DATA_RES(item)->nonce2 = RES_NONCE(use2); + } +//if (RES_GOLD(use1)) +//applog(MINTASK_LOG, "%s%i: found a result chip %d core %d task 0x%04x nonce 0x%08x gold=%d", minioncgpu->drv->name, minioncgpu->device_id, DATA_RES(item)->chip, DATA_RES(item)->core, DATA_RES(item)->task_id, DATA_RES(item)->nonce, (int)RES_GOLD(use1)); + + K_WLOCK(minioninfo->rnonce_list); + k_add_head(minioninfo->rnonce_list, item); + K_WUNLOCK(minioninfo->rnonce_list); + + if (!(minioninfo->chip_status[chip].first_nonce.tv_sec)) { + cgtime(&(minioninfo->chip_status[chip].first_nonce)); + minioninfo->chip_status[chip].from_first_good = 0; + } + + cgsem_post(&(minioninfo->nonce_ready)); + } else { + minioninfo->res_err_count[chip]++; + applog(MINTASK_LOG, "%s%i: Invalid res0 task_id 0x%04x - chip %d", + minioncgpu->drv->name, minioncgpu->device_id, + RES_TASK(result1), chip); + if (minreread && result2) { + applog(MINTASK_LOG, "%s%i: Invalid res1 task_id 0x%04x - chip %d", + minioncgpu->drv->name, minioncgpu->device_id, + RES_TASK(result2), chip); + } + } + } + } + } + } + } + } + + if (somelow) + cgsem_post(&(minioninfo->scan_work)); + +#if ENABLE_INT_NONO + if (gotreplies) + minion_txrx(&clr_task); +#endif + +#if !ENABLE_INT_NONO + cgsleep_ms(MINION_REPLY_mS); +#else + // TODO: this is going to require a bit of tuning with 2TH/s mining: + // The interrupt size MINION_RESULT_INT_SIZE should be high enough to expect + // most chips to have some results but low enough to cause negligible latency + // If all chips don't have some results when an interrupt occurs, then it is a waste + // since we have to check all chips for results anyway since we don't know which one + // caused the interrupt + // MINION_REPLY_mS needs to be low enough in the case of bad luck where no chip + // finds MINION_RESULT_INT_SIZE results in a short amount of time, so we go check + // them all anyway - to avoid high latency when there are only a few results due to low luck + ret = poll(&pfd, 1, MINION_REPLY_mS); + if (ret > 0) { + bool gotres; + int c; + + minioninfo->interrupts++; + + read(minioninfo->gpiointfd, &c, 1); + +// applog(LOG_ERR, "%s%i: Interrupt2", +// minioncgpu->drv->name, +// minioncgpu->device_id); + + gotres = false; + for (chip = 0; chip < (int)MINION_CHIPS; chip++) { + if (minioninfo->has_chip[chip]) { + SET_HEAD_READ(head, MINION_SYS_INT_STA); + head->chipid = minioninfo->chipid[chip]; + reply = do_ioctl(CHIP_PIN(chip), wbuf, wsiz, rbuf, rsiz, &ioseq); + if (reply != (int)wsiz) { + applog(LOG_ERR, "%s: chip %d int status returned %d" + " (should be %d)", + minioncgpu->drv->dname, + chip, reply, (int)wsiz); + } + + snprintf(minioninfo->last_interrupt, + sizeof(minioninfo->last_interrupt), + "%d %d 0x%02x%02x%02x%02x%02x%02x%02x%02x %d %d 0x%02x %d %d", + (int)(minioninfo->interrupts), chip, + rbuf[0], rbuf[1], rbuf[2], rbuf[3], + rbuf[4], rbuf[5], rbuf[6], rbuf[7], + (int)wsiz, (int)rsiz, rbuf[wsiz - rsiz], + rbuf[wsiz - rsiz] & MINION_RESULT_INT, + rbuf[wsiz - rsiz] & MINION_CMD_INT); + + if ((rbuf[wsiz - rsiz] & MINION_RESULT_INT) != 0) { + gotres = true; + (minioninfo->result_interrupts)++; +// applog(LOG_ERR, "%s%i: chip %d got RES interrupt", +// minioncgpu->drv->name, +// minioncgpu->device_id, +// chip); + } + + if ((rbuf[wsiz - rsiz] & MINION_CMD_INT) != 0) { + // Work queue is empty + (minioninfo->command_interrupts)++; +// applog(LOG_ERR, "%s%i: chip %d got CMD interrupt", +// minioncgpu->drv->name, +// minioncgpu->device_id, +// chip); + } + +// char *tmp; +// tmp = bin2hex(rbuf, wsiz); +// applog(LOG_ERR, "%s%i: chip %d interrupt: %s", +// minioncgpu->drv->name, +// minioncgpu->device_id, +// chip, tmp); +// free(tmp); + + // Don't clear either interrupt until after send/recv + } + } + + // Doing this last means we can't miss an interrupt + if (gotres) + cgsem_post(&(minioninfo->scan_work)); + } +#endif + } + + return NULL; +} + +/* + * Find the matching work item for this chip + * Discard any older work items for this chip + */ + +enum nonce_state { + NONCE_GOOD_NONCE, + NONCE_NO_NONCE, + NONCE_DUP_NONCE, + NONCE_BAD_NONCE, + NONCE_BAD_WORK, + NONCE_NO_WORK, + NONCE_SPI_ERR +}; + +static void cleanup_older(struct cgpu_info *minioncgpu, int chip, K_ITEM *item, bool no_nonce) +{ + struct minion_info *minioninfo = (struct minion_info *)(minioncgpu->device_data); + K_ITEM *tail; +// bool errs; + + /* remove older ioseq work items + no_nonce means this 'item' has finished also */ + tail = minioninfo->wchip_list[chip]->tail; + while (tail && (DATA_WORK(tail)->ioseq < DATA_WORK(item)->ioseq)) { + k_unlink_item(minioninfo->wchip_list[chip], tail); + if (!(DATA_WORK(tail)->stale)) { + minioninfo->chip_status[chip].chipwork--; +#if MINION_SHOW_IO + applog(IOCTRL_LOG, "COld chip %d cw-1=%u rw=%u qw=%u", + chip, + minioninfo->chip_status[chip].chipwork, + minioninfo->chip_status[chip].realwork, + minioninfo->chip_status[chip].quework); +#endif +/* + // If it had no valid work (only errors) then it won't have been cleaned up + errs = (DATA_WORK(tail)->errors > 0); + applog(errs ? LOG_DEBUG : LOG_ERR, + applog(LOG_ERR, + "%s%i: discarded old task 0x%04x chip %d no reply errs=%d", + minioncgpu->drv->name, minioncgpu->device_id, + DATA_WORK(tail)->task_id, chip, DATA_WORK(tail)->errors); +*/ + } + applog(MINION_LOG, "%s%i: marking complete - old task 0x%04x chip %d", + minioncgpu->drv->name, minioncgpu->device_id, + DATA_WORK(tail)->task_id, chip); + if (DATA_WORK(tail)->rolled) + free_work(DATA_WORK(tail)->work); + else + work_completed(minioncgpu, DATA_WORK(tail)->work); + k_free_head(minioninfo->wfree_list, tail); + tail = minioninfo->wchip_list[chip]->tail; + } + if (no_nonce) { + if (!(DATA_WORK(item)->stale)) { + minioninfo->chip_status[chip].chipwork--; +#if MINION_SHOW_IO + applog(IOCTRL_LOG, "CONoN chip %d cw-1=%u rw=%u qw=%u", + chip, + minioninfo->chip_status[chip].chipwork, + minioninfo->chip_status[chip].realwork, + minioninfo->chip_status[chip].quework); +#endif + } + applog(MINION_LOG, "%s%i: marking complete - no_nonce task 0x%04x chip %d", + minioncgpu->drv->name, minioncgpu->device_id, + DATA_WORK(item)->task_id, chip); + if (DATA_WORK(item)->rolled) + free_work(DATA_WORK(item)->work); + else + work_completed(minioncgpu, DATA_WORK(item)->work); + } +} + +// Need to put it back in the list where it was - according to ioseq +static void restorework(struct minion_info *minioninfo, int chip, K_ITEM *item) +{ + K_ITEM *look; + + look = minioninfo->wchip_list[chip]->tail; + while (look && DATA_WORK(look)->ioseq < DATA_WORK(item)->ioseq) + look = look->prev; + if (!look) + k_add_head(minioninfo->wchip_list[chip], item); + else + k_insert_after(minioninfo->wchip_list[chip], item, look); +} + +static enum nonce_state oknonce(struct thr_info *thr, struct cgpu_info *minioncgpu, int chip, int core, + uint32_t task_id, uint32_t nonce, bool no_nonce, struct timeval *when, + bool another, uint32_t task_id2, uint32_t nonce2) +{ + struct minion_info *minioninfo = (struct minion_info *)(minioncgpu->device_data); + struct timeval now; + K_ITEM *item, *tail; + uint32_t min_task_id, max_task_id; +// uint64_t chip_good; + bool redo; + + // if the chip has been disabled - but we don't do that - so not possible (yet) + if (!(minioninfo->has_chip[chip])) { + minioninfo->spi_errors++; + applog(MINTASK_LOG, "%s%i: nonce error chip %d not present", + minioncgpu->drv->name, minioncgpu->device_id, chip); + return NONCE_NO_WORK; + } + + if (core < 0 || core >= MINION_CORES) { + minioninfo->spi_errors++; + minioninfo->res_spi_errors[chip]++; + minioninfo->res_err_count[chip]++; + applog(MINTASK_LOG, "%s%i: SPI nonce error invalid core %d (chip %d)", + minioncgpu->drv->name, minioncgpu->device_id, core, chip); + + // use the fake core number so we don't discard the result + core = FAKE_CORE; + } + + if (no_nonce) + minioninfo->chip_nononces[chip]++; + else + minioninfo->chip_nonces[chip]++; + + redo = false; +retry: + K_WLOCK(minioninfo->wchip_list[chip]); + item = minioninfo->wchip_list[chip]->tail; + + if (!item) { + K_WUNLOCK(minioninfo->wchip_list[chip]); + minioninfo->spi_errors++; + minioninfo->res_spi_errors[chip]++; + minioninfo->res_err_count[chip]++; + applog(MINTASK_LOG, "%s%i: chip %d has no tasks (core %d task 0x%04x)", + minioncgpu->drv->name, minioncgpu->device_id, + chip, core, (int)task_id); + if (!no_nonce) { + minioninfo->untested_nonces++; + minioninfo->chip_err[chip]++; + } + return NONCE_NO_WORK; + } + + min_task_id = DATA_WORK(item)->task_id; + while (item) { + if (DATA_WORK(item)->task_id == task_id) + break; + + item = item->prev; + } + max_task_id = DATA_WORK(minioninfo->wchip_list[chip]->head)->task_id; + + if (!item) { + K_WUNLOCK(minioninfo->wchip_list[chip]); + if (another && task_id != task_id2) { + minioninfo->tasks_failed[chip]++; + task_id = task_id2; + redo = true; + goto retry; + } + + minioninfo->spi_errors++; + minioninfo->res_spi_errors[chip]++; + minioninfo->res_err_count[chip]++; + applog(MINTASK_LOG, "%s%i: chip %d core %d unknown task 0x%04x " + "(min=0x%04x max=0x%04x no_nonce=%d)", + minioncgpu->drv->name, minioncgpu->device_id, + chip, core, (int)task_id, (int)min_task_id, + (int)max_task_id, no_nonce); + if (!no_nonce) { + minioninfo->untested_nonces++; + minioninfo->chip_err[chip]++; + } + return NONCE_BAD_WORK; + } + if (redo) + minioninfo->tasks_recovered[chip]++; + + k_unlink_item(minioninfo->wchip_list[chip], item); + if (no_nonce) { + cleanup_older(minioncgpu, chip, item, no_nonce); + k_free_head(minioninfo->wfree_list, item); + K_WUNLOCK(minioninfo->wchip_list[chip]); + return NONCE_NO_NONCE; + } + K_WUNLOCK(minioninfo->wchip_list[chip]); + + minioninfo->tested_nonces++; + + redo = false; +retest: + if (test_nonce(DATA_WORK(item)->work, nonce)) { +/* + if (isdupnonce(minioncgpu, DATA_WORK(item)->work, nonce)) { + minioninfo->chip_dup[chip]++; + applog(LOG_WARNING, " ... nonce %02x%02x%02x%02x chip %d core %d task 0x%04x", + (nonce & 0xff), ((nonce >> 8) & 0xff), + ((nonce >> 16) & 0xff), ((nonce >> 24) & 0xff), + chip, core, task_id); + K_WLOCK(minioninfo->wchip_list[chip]); + restorework(minioninfo, chip, item); + K_WUNLOCK(minioninfo->wchip_list[chip]); + return NONCE_DUP_NONCE; + } +*/ +//applog(MINTASK_LOG, "%s%i: Valid Nonce chip %d core %d task 0x%04x nonce 0x%08x", minioncgpu->drv->name, minioncgpu->device_id, chip, core, task_id, nonce); +// + submit_tested_work(thr, DATA_WORK(item)->work); + + if (redo) + minioninfo->nonces_recovered[chip]++; + + /* chip_good = */ ++(minioninfo->chip_good[chip]); + minioninfo->chip_status[chip].from_first_good++; + minioninfo->core_good[chip][core]++; + DATA_WORK(item)->nonces++; + + mutex_lock(&(minioninfo->nonce_lock)); + minioninfo->new_nonces++; + mutex_unlock(&(minioninfo->nonce_lock)); + minioninfo->ok_nonces++; + + K_WLOCK(minioninfo->wchip_list[chip]); + cleanup_older(minioncgpu, chip, item, no_nonce); + restorework(minioninfo, chip, item); + K_WUNLOCK(minioninfo->wchip_list[chip]); + + // add to history and remove old history and keep track of the 2 reset marks + int chip_tmp; + cgtime(&now); + K_WLOCK(minioninfo->hfree_list); + item = k_unlink_head(minioninfo->hfree_list); + memcpy(&(DATA_HIST(item)->when), when, sizeof(*when)); + k_add_head(minioninfo->hchip_list[chip], item); + if (minioninfo->reset_mark[chip]) + minioninfo->reset_count[chip]++; + if (second_check && minioninfo->reset2_mark[chip]) + minioninfo->reset2_count[chip]++; + + // N.B. this also corrects each reset_mark/reset_count within each hchip_list + for (chip_tmp = 0; chip_tmp < (int)MINION_CHIPS; chip_tmp++) { + tail = minioninfo->hchip_list[chip_tmp]->tail; + while (tail && tdiff(&(DATA_HIST(tail)->when), &now) > MINION_HISTORY_s) { + if (minioninfo->reset_mark[chip] == tail) { + minioninfo->reset_mark[chip] = tail->prev; + minioninfo->reset_count[chip]--; + } + if (second_check && minioninfo->reset2_mark[chip] == tail) { + minioninfo->reset2_mark[chip] = tail->prev; + minioninfo->reset2_count[chip]--; + } + tail = k_unlink_tail(minioninfo->hchip_list[chip_tmp]); + k_add_head(minioninfo->hfree_list, item); + tail = minioninfo->hchip_list[chip_tmp]->tail; + } + if (!(minioninfo->reset_mark[chip])) { + minioninfo->reset_mark[chip] = minioninfo->hchip_list[chip]->tail; + minioninfo->reset_count[chip] = minioninfo->hchip_list[chip]->count; + } + if (second_check && !(minioninfo->reset2_mark[chip])) { + minioninfo->reset2_mark[chip] = minioninfo->hchip_list[chip]->tail; + minioninfo->reset2_count[chip] = minioninfo->hchip_list[chip]->count; + } + tail = minioninfo->reset_mark[chip]; + while (tail && tdiff(&(DATA_HIST(tail)->when), &now) > minioninfo->reset_time[chip]) { + tail = minioninfo->reset_mark[chip] = tail->prev; + minioninfo->reset_count[chip]--; + } + if (second_check) { + tail = minioninfo->reset2_mark[chip]; + while (tail && tdiff(&(DATA_HIST(tail)->when), &now) > minioninfo->reset2_time[chip]) { + tail = minioninfo->reset2_mark[chip] = tail->prev; + minioninfo->reset2_count[chip]--; + } + } + } + K_WUNLOCK(minioninfo->hfree_list); + +/* + // Reset the chip after 8 nonces found + if (chip_good == 8) { + memcpy(&(minioninfo->last_reset[chip]), &now, sizeof(now)); + init_chip(minioncgpu, minioninfo, chip); + } +*/ + + return NONCE_GOOD_NONCE; + } + + if (another && nonce != nonce2) { + minioninfo->nonces_failed[chip]++; + nonce = nonce2; + redo = true; + goto retest; + } + + DATA_WORK(item)->errors++; + K_WLOCK(minioninfo->wchip_list[chip]); + restorework(minioninfo, chip, item); + K_WUNLOCK(minioninfo->wchip_list[chip]); + + minioninfo->chip_bad[chip]++; + minioninfo->core_bad[chip][core]++; + inc_hw_errors(thr); +//applog(MINTASK_LOG, "%s%i: HW ERROR chip %d core %d task 0x%04x nonce 0x%08x", minioncgpu->drv->name, minioncgpu->device_id, chip, core, task_id, nonce); + + return NONCE_BAD_NONCE; +} + +/* Check each chip how long since the last nonce + * Should normally be a fraction of a second + * so (MINION_RESET_s * 1.5) will certainly be long enough, + * but also will avoid lots of resets if there is trouble getting work + * Should be longer than MINION_RESET_s to avoid interfering with normal resets */ +static void check_last_nonce(struct cgpu_info *minioncgpu) +{ + struct minion_info *minioninfo = (struct minion_info *)(minioncgpu->device_data); + struct timeval now; + K_ITEM *head; + double howlong; + int chip; + + cgtime(&now); + K_RLOCK(minioninfo->hfree_list); + for (chip = 0; chip < (int)MINION_CHIPS; chip++) { + if (minioninfo->has_chip[chip] && !(minioninfo->changing[chip])) { + head = minioninfo->hchip_list[chip]->head; + if (head) { + howlong = tdiff(&now, &(DATA_HIST(head)->when)); + if (howlong > ((double)MINION_RESET_s * 1.5)) { + // Setup a reset + minioninfo->flag_reset[chip] = true; + minioninfo->do_reset[chip] = 0.0; + } + } + } + } + K_RUNLOCK(minioninfo->hfree_list); +} + +// Results checking thread +static void *minion_results(void *userdata) +{ + struct cgpu_info *minioncgpu = (struct cgpu_info *)userdata; + struct minion_info *minioninfo = (struct minion_info *)(minioncgpu->device_data); + struct thr_info *thr; + int chip = 0, core = 0; + uint32_t task_id = 0; + uint32_t nonce = 0; + bool no_nonce = false; + struct timeval when; + bool another; + uint32_t task_id2 = 0; + uint32_t nonce2 = 0; + int last_check; + + applog(MINION_LOG, "%s%i: Results...", + minioncgpu->drv->name, minioncgpu->device_id); + + // Wait until we're ready + while (minioncgpu->shutdown == false) { + if (minioninfo->initialised) { + break; + } + cgsleep_ms(3); + } + + thr = minioninfo->thr; + + last_check = 0; + while (minioncgpu->shutdown == false) { + if (!oldest_nonce(minioncgpu, &chip, &core, &task_id, &nonce, + &no_nonce, &when, &another, &task_id2, &nonce2)) { + check_last_nonce(minioncgpu); + last_check = 0; + cgsem_mswait(&(minioninfo->nonce_ready), MINION_NONCE_mS); + continue; + } + + oknonce(thr, minioncgpu, chip, core, task_id, nonce, no_nonce, &when, + another, task_id2, nonce2); + + // Interrupt nonce checking if low CPU and oldest_nonce() is always true + if (++last_check > 100) { + check_last_nonce(minioncgpu); + last_check = 0; + } + } + + return NULL; +} + +static void minion_flush_work(struct cgpu_info *minioncgpu) +{ + struct minion_info *minioninfo = (struct minion_info *)(minioncgpu->device_data); + K_ITEM *prev_unused, *task, *prev_task, *witem; + int i; + + if (minioninfo->initialised == false) + return; + + applog(MINION_LOG, "%s%i: flushing work", + minioncgpu->drv->name, minioncgpu->device_id); + + // TODO: N.B. scanwork also gets work locks - which master thread calls flush? + K_WLOCK(minioninfo->wwork_list); + + // Simply remove the whole unused wwork_list + k_list_transfer_to_head(minioninfo->wwork_list, minioninfo->wstale_list); + minioninfo->wwork_flushed += minioninfo->wstale_list->count; + + // TODO: flush/work tasks should have a block sequence number so this task removal code + // might be better implemented in minion_spi_write where each work task would + // update the block sequence number and any work tasks with an old block sequence + // number would be discarded rather than sent - minion_spi_write will also need to + // prioritise flush urgent tasks above work urgent tasks - have 3 urgent states? + // They should however be 2 seperate variables in minioninfo to reduce locking + // - flush will increment one and put it in the flush task, (and work will use that) + // minion_spi_write will check/update the other and thus not need a lock + + // No deadlock since this is the only code to get 2 locks + K_WLOCK(minioninfo->tfree_list); + task = minioninfo->task_list->tail; + while (task) { + prev_task = task->prev; + if (DATA_TASK(task)->address == WRITE_ADDR(MINION_QUE_0)) { + minioninfo->chip_status[DATA_TASK(task)->chip].quework--; +#if MINION_SHOW_IO + applog(IOCTRL_LOG, "QueFlush chip %d cw=%u rw=%u qw-1=%u", + (int)DATA_TASK(task)->chip, + minioninfo->chip_status[DATA_TASK(task)->chip].chipwork, + minioninfo->chip_status[DATA_TASK(task)->chip].realwork, + minioninfo->chip_status[DATA_TASK(task)->chip].quework); +#endif + witem = DATA_TASK(task)->witem; + k_unlink_item(minioninfo->wque_list[DATA_TASK(task)->chip], witem); + minioninfo->wque_flushed++; + if (DATA_WORK(witem)->rolled) + free_work(DATA_WORK(witem)->work); + else + work_completed(minioncgpu, DATA_WORK(witem)->work); + k_free_head(minioninfo->wfree_list, witem); + k_unlink_item(minioninfo->task_list, task); + k_free_head(minioninfo->tfree_list, task); + } + task = prev_task; + } + for (i = 0; i < (int)MINION_CHIPS; i++) { + if (minioninfo->has_chip[i]) { + // TODO: consider sending it now rather than adding to the task list? + task = k_unlink_head(minioninfo->tfree_list); + DATA_TASK(task)->tid = ++(minioninfo->next_tid); + DATA_TASK(task)->chip = i; + DATA_TASK(task)->write = true; + DATA_TASK(task)->address = MINION_SYS_RSTN_CTL; + DATA_TASK(task)->task_id = 0; // ignored + DATA_TASK(task)->wsiz = MINION_SYS_SIZ; + DATA_TASK(task)->rsiz = 0; + DATA_TASK(task)->wbuf[0] = SYS_RSTN_CTL_FLUSH; + DATA_TASK(task)->wbuf[1] = 0; + DATA_TASK(task)->wbuf[2] = 0; + DATA_TASK(task)->wbuf[3] = 0; + DATA_TASK(task)->urgent = true; + k_add_head(minioninfo->task_list, task); + } + } + K_WUNLOCK(minioninfo->tfree_list); + + K_WUNLOCK(minioninfo->wwork_list); + + // TODO: send a signal to force getting and sending new work - needs cgsem_wait in the sending thread + + // TODO: should we use this thread to do the following work? + if (minioninfo->wstale_list->count) { + // mark complete all stale unused work (oldest first) + prev_unused = minioninfo->wstale_list->tail; + while (prev_unused) { + if (DATA_WORK(prev_unused)->rolled) + free_work(DATA_WORK(prev_unused)->work); + else + work_completed(minioncgpu, DATA_WORK(prev_unused)->work); + prev_unused = prev_unused->prev; + } + + // put them back in the wfree_list + K_WLOCK(minioninfo->wfree_list); + k_list_transfer_to_head(minioninfo->wstale_list, minioninfo->wfree_list); + K_WUNLOCK(minioninfo->wfree_list); + } +} + +static void sys_chip_sta(struct cgpu_info *minioncgpu, int chip) +{ + struct minion_info *minioninfo = (struct minion_info *)(minioncgpu->device_data); + struct timeval now; + K_ITEM *item; + int limit, rep; + + cgtime(&now); + // No lock required since 'last' is only accessed here + if (minioninfo->chip_status[chip].last.tv_sec == 0) { + memcpy(&(minioninfo->chip_status[chip].last), &now, sizeof(now)); + } else { + limit = MINION_STATS_UPDATE_TIME_mS + + (int)(random() % MINION_STATS_UPDATE_RAND_mS); + if (ms_tdiff(&now, &(minioninfo->chip_status[chip].last)) > limit) { + memcpy(&(minioninfo->chip_status[chip].last), &now, sizeof(now)); + + K_WLOCK(minioninfo->tfree_list); + item = k_unlink_head(minioninfo->tfree_list); + DATA_TASK(item)->tid = ++(minioninfo->next_tid); + K_WUNLOCK(minioninfo->tfree_list); + + DATA_TASK(item)->chip = chip; + DATA_TASK(item)->write = false; + DATA_TASK(item)->address = READ_ADDR(MINION_SYS_CHIP_STA); + DATA_TASK(item)->task_id = 0; + DATA_TASK(item)->wsiz = 0; + DATA_TASK(item)->rsiz = MINION_SYS_SIZ; + DATA_TASK(item)->urgent = false; + + K_WLOCK(minioninfo->task_list); + k_add_head(minioninfo->task_list, item); + item = k_unlink_head(minioninfo->tfree_list); + DATA_TASK(item)->tid = ++(minioninfo->next_tid); + K_WUNLOCK(minioninfo->task_list); + + DATA_TASK(item)->chip = chip; + DATA_TASK(item)->write = false; + DATA_TASK(item)->address = READ_ADDR(MINION_SYS_IDLE_CNT); + DATA_TASK(item)->task_id = 0; + DATA_TASK(item)->wsiz = 0; + DATA_TASK(item)->rsiz = MINION_SYS_SIZ; + DATA_TASK(item)->urgent = false; + + K_WLOCK(minioninfo->task_list); + k_add_head(minioninfo->task_list, item); + K_WUNLOCK(minioninfo->task_list); + + // Get the core ena and act state + for (rep = 0; rep < MINION_CORE_REPS; rep++) { + // Ena + K_WLOCK(minioninfo->tfree_list); + item = k_unlink_head(minioninfo->tfree_list); + DATA_TASK(item)->tid = ++(minioninfo->next_tid); + K_WUNLOCK(minioninfo->tfree_list); + + DATA_TASK(item)->chip = chip; + DATA_TASK(item)->write = false; + DATA_TASK(item)->address = READ_ADDR(MINION_CORE_ENA0_31 + rep); + DATA_TASK(item)->task_id = 0; + DATA_TASK(item)->wsiz = 0; + DATA_TASK(item)->rsiz = MINION_SYS_SIZ; + DATA_TASK(item)->urgent = false; + + K_WLOCK(minioninfo->task_list); + k_add_head(minioninfo->task_list, item); + // Act + item = k_unlink_head(minioninfo->tfree_list); + DATA_TASK(item)->tid = ++(minioninfo->next_tid); + K_WUNLOCK(minioninfo->task_list); + + DATA_TASK(item)->chip = chip; + DATA_TASK(item)->write = false; + DATA_TASK(item)->address = READ_ADDR(MINION_CORE_ACT0_31 + rep); + DATA_TASK(item)->task_id = 0; + DATA_TASK(item)->wsiz = 0; + DATA_TASK(item)->rsiz = MINION_SYS_SIZ; + DATA_TASK(item)->urgent = false; + + K_WLOCK(minioninfo->task_list); + k_add_head(minioninfo->task_list, item); + K_WUNLOCK(minioninfo->task_list); + } + + if (minioninfo->lednow[chip] != minioninfo->setled[chip]) { + uint32_t led; + + minioninfo->lednow[chip] = minioninfo->setled[chip]; + if (minioninfo->lednow[chip]) + led = MINION_SPI_LED_ON; + else + led = MINION_SPI_LED_OFF; + + K_WLOCK(minioninfo->tfree_list); + item = k_unlink_head(minioninfo->tfree_list); + DATA_TASK(item)->tid = ++(minioninfo->next_tid); + K_WUNLOCK(minioninfo->tfree_list); + + DATA_TASK(item)->chip = chip; + DATA_TASK(item)->write = true; + DATA_TASK(item)->address = MINION_SYS_SPI_LED; + DATA_TASK(item)->task_id = 0; + DATA_TASK(item)->wsiz = MINION_SYS_SIZ; + DATA_TASK(item)->rsiz = 0; + DATA_TASK(item)->wbuf[0] = led & 0xff; + DATA_TASK(item)->wbuf[1] = (led >> 8) & 0xff; + DATA_TASK(item)->wbuf[2] = (led >> 16) & 0xff; + DATA_TASK(item)->wbuf[3] = (led >> 24) & 0xff; + DATA_TASK(item)->urgent = false; + + K_WLOCK(minioninfo->task_list); + k_add_head(minioninfo->task_list, item); + K_WUNLOCK(minioninfo->task_list); + } + } + } +} + +static void new_work_task(struct cgpu_info *minioncgpu, K_ITEM *witem, int chip, bool urgent, uint8_t state) +{ + struct minion_info *minioninfo = (struct minion_info *)(minioncgpu->device_data); + struct minion_que *que; + K_ITEM *item; + + K_WLOCK(minioninfo->tfree_list); + item = k_unlink_head(minioninfo->tfree_list); + DATA_TASK(item)->tid = ++(minioninfo->next_tid); + K_WUNLOCK(minioninfo->tfree_list); + + DATA_TASK(item)->chip = chip; + DATA_TASK(item)->write = true; + DATA_TASK(item)->address = MINION_QUE_0; + + // if threaded access to new_work_task() is added, this will need locking + // Don't use task_id 0 so that we can ignore all '0' work replies + // ... and report them as errors + if (minioninfo->next_task_id == 0) + minioninfo->next_task_id = 1; + DATA_TASK(item)->task_id = minioninfo->next_task_id; + DATA_WORK(witem)->task_id = minioninfo->next_task_id; + minioninfo->next_task_id = (minioninfo->next_task_id + 1) & MINION_MAX_TASK_ID; + + DATA_TASK(item)->urgent = urgent; + DATA_TASK(item)->work_state = state; + DATA_TASK(item)->work = DATA_WORK(witem)->work; + DATA_TASK(item)->witem = witem; + + que = (struct minion_que *)&(DATA_TASK(item)->wbuf[0]); + que->task_id[0] = DATA_TASK(item)->task_id & 0xff; + que->task_id[1] = (DATA_TASK(item)->task_id & 0xff00) >> 8; + + memcpy(&(que->midstate[0]), &(DATA_WORK(witem)->work->midstate[0]), MIDSTATE_BYTES); + memcpy(&(que->merkle7[0]), &(DATA_WORK(witem)->work->data[MERKLE7_OFFSET]), MERKLE_BYTES); + + DATA_TASK(item)->wsiz = (int)sizeof(*que); + DATA_TASK(item)->rsiz = 0; + + K_WLOCK(minioninfo->wque_list[chip]); + k_add_head(minioninfo->wque_list[chip], witem); + minioninfo->chip_status[chip].quework++; +#if MINION_SHOW_IO + applog(IOCTRL_LOG, "Que chip %d cw=%u rw=%u qw+1=%u", + chip, + minioninfo->chip_status[chip].chipwork, + minioninfo->chip_status[chip].realwork, + minioninfo->chip_status[chip].quework); +#endif + K_WUNLOCK(minioninfo->wque_list[chip]); + + K_WLOCK(minioninfo->task_list); + k_add_head(minioninfo->task_list, item); + K_WUNLOCK(minioninfo->task_list); + + if (urgent) + cgsem_post(&(minioninfo->task_ready)); + + // N.B. this will only update often enough if a chip is > ~2GH/s + if (!urgent) + sys_chip_sta(minioncgpu, chip); +} + +// TODO: stale work ... +static K_ITEM *next_work(struct minion_info *minioninfo) +{ + K_ITEM *item; + struct timeval now; + double howlong; + int i; + + K_WLOCK(minioninfo->wwork_list); + item = k_unlink_tail(minioninfo->wwork_list); + K_WUNLOCK(minioninfo->wwork_list); + if (item) { + cgtime(&now); + howlong = tdiff(&now, &(DATA_WORK(item)->created)); + minioninfo->que_work++; + minioninfo->que_time += howlong; + if (minioninfo->que_min == 0 || minioninfo->que_min > howlong) + minioninfo->que_min = howlong; + else if (minioninfo->que_max < howlong) + minioninfo->que_max = howlong; + for (i = 0; i < TIME_BANDS; i++) { + if (howlong < time_bands[i]) { + minioninfo->que_bands[i]++; + break; + } + } + if (i >= TIME_BANDS) + minioninfo->que_bands[TIME_BANDS]++; + } + + return item; +} + +static void minion_do_work(struct cgpu_info *minioncgpu) +{ + struct minion_info *minioninfo = (struct minion_info *)(minioncgpu->device_data); + int count, chip, j, lowcount; + TASK_ITEM fifo_task; + uint8_t state, cmd; + K_ITEM *item; +#if ENABLE_INT_NONO + K_ITEM *task; +#endif + bool islow, sentwork; + + fifo_task.chip = 0; + fifo_task.write = false; + fifo_task.address = MINION_SYS_FIFO_STA; + fifo_task.wsiz = 0; + fifo_task.rsiz = MINION_SYS_SIZ; + + // TODO: (remove this) Fake starved of work to test CMD Interrupt +// if (total_secs > 120) { +// cgsleep_ms(888); +// return; +// } + + /* + * Fill the queues as follows: + * 1) put at least 1 in each queue or if islow then add 1 + * 2) push each queue up to LOW or if count is high but islow, then add LOW-1 + * 3) push each LOW queue up to HIGH + */ + + sentwork = false; + for (state = 0; state < 3; state++) { +#define CHP 0 +//applog(LOG_ERR, "%s%i: chip %d presta %d: quew %d chw %d", minioncgpu->drv->name, minioncgpu->device_id, CHP, state, minioninfo->chip_status[CHP].quework, minioninfo->chip_status[CHP].chipwork); + for (chip = 0; chip < (int)MINION_CHIPS; chip++) + minioninfo->chip_status[chip].tohigh = false; + + for (chip = 0; chip < (int)MINION_CHIPS; chip++) { + if (minioninfo->has_chip[chip] && !minioninfo->chip_status[chip].overheat) { + struct timeval now; + double howlong; + cgtime(&now); + howlong = tdiff(&now, &(minioninfo->last_reset[chip])); + if (howlong < MINION_RESET_DELAY_s) + continue; + + int tries = 0; + while (tries++ < 4) { + cmd = 0; + fifo_task.chip = chip; + fifo_task.reply = 0; + minion_txrx(&fifo_task); + if (fifo_task.reply <= 0) { + if (fifo_task.reply < (int)(fifo_task.osiz)) { + char *buf = bin2hex((unsigned char *)(&(fifo_task.rbuf[fifo_task.osiz - fifo_task.rsiz])), + (int)(fifo_task.rsiz)); + applog(LOG_ERR, "%s%i: Chip %d Bad fifo reply (%s) size %d, should be %d", + minioncgpu->drv->name, minioncgpu->device_id, + chip, buf, + fifo_task.reply, (int)(fifo_task.osiz)); + free(buf); + minioninfo->spi_errors++; + minioninfo->fifo_spi_errors[chip]++; + minioninfo->res_err_count[chip]++; + } else { + if (fifo_task.reply > (int)(fifo_task.osiz)) { + applog(LOG_ERR, "%s%i: Chip %d Unexpected fifo reply size %d, expected only %d", + minioncgpu->drv->name, minioncgpu->device_id, + chip, fifo_task.reply, (int)(fifo_task.osiz)); + } + cmd = FIFO_CMD(fifo_task.rbuf, fifo_task.osiz - fifo_task.rsiz); + // valid reply? + if (cmd < MINION_QUE_MAX) { + K_WLOCK(minioninfo->wchip_list[chip]); + minioninfo->chip_status[chip].realwork = cmd; + K_WUNLOCK(minioninfo->wchip_list[chip]); + if (cmd <= MINION_QUE_LOW || cmd >= MINION_QUE_HIGH) { + applog(LOG_DEBUG, "%s%i: Chip %d fifo cmd %d", + minioncgpu->drv->name, + minioncgpu->device_id, + chip, (int)cmd); + } + break; + } + + applog(LOG_ERR, "%s%i: Chip %d Bad fifo reply cmd %d (max is %d)", + minioncgpu->drv->name, minioncgpu->device_id, + chip, (int)cmd, MINION_QUE_MAX); + minioninfo->spi_errors++; + minioninfo->fifo_spi_errors[chip]++; + minioninfo->res_err_count[chip]++; + } + } + } + + K_WLOCK(minioninfo->wchip_list[chip]); + count = minioninfo->chip_status[chip].quework + + minioninfo->chip_status[chip].realwork; + islow = minioninfo->chip_status[chip].islow; + minioninfo->chip_status[chip].islow = false; + lowcount = minioninfo->chip_status[chip].lowcount; + K_WUNLOCK(minioninfo->wchip_list[chip]); + + switch (state) { + case 0: + if (count == 0 || islow) { + item = next_work(minioninfo); + if (item) { + new_work_task(minioncgpu, item, chip, true, state); + sentwork = true; + applog(MINION_LOG, "%s%i: 0 task 0x%04x in chip %d list", + minioncgpu->drv->name, + minioncgpu->device_id, + DATA_WORK(item)->task_id, chip); + } else { + applog(LOG_ERR, "%s%i: chip %d urgent empty work list", + minioncgpu->drv->name, + minioncgpu->device_id, + chip); + } + } + break; + case 1: + if (count < MINION_QUE_LOW || islow) { + // do case 2: after we've done other chips + minioninfo->chip_status[chip].tohigh = true; + j = count; + if (count >= MINION_QUE_LOW) { + // islow means run a full case 1 + j = 1; + applog(LOG_ERR, "%s%i: chip %d low que (%d) with high count %d", + minioncgpu->drv->name, + minioncgpu->device_id, + chip, lowcount, count); + } + for (; j < MINION_QUE_LOW; j++) { + item = next_work(minioninfo); + if (item) { + new_work_task(minioncgpu, item, chip, false, state); + sentwork = true; + applog(MINION_LOG, "%s%i: 1 task 0x%04x in chip %d list", + minioncgpu->drv->name, + minioncgpu->device_id, + DATA_WORK(item)->task_id, chip); + } else { + applog(LOG_ERR, "%s%i: chip %d non-urgent lo " + "empty work list (count=%d)", + minioncgpu->drv->name, + minioncgpu->device_id, + chip, j); + } + } + } + break; + case 2: + if (count <= MINION_QUE_LOW || minioninfo->chip_status[chip].tohigh) { + for (j = count; j < MINION_QUE_HIGH; j++) { + item = next_work(minioninfo); + if (item) { + new_work_task(minioncgpu, item, chip, false, state); + sentwork = true; + applog(MINION_LOG, "%s%i: 2 task 0x%04x in chip %d list", + minioncgpu->drv->name, + minioncgpu->device_id, + DATA_WORK(item)->task_id, chip); + } else { + applog(LOG_DEBUG, "%s%i: chip %d non-urgent hi " + "empty work list (count=%d)", + minioncgpu->drv->name, + minioncgpu->device_id, + chip, j); + } + } + } + break; + } + } else + if (minioninfo->has_chip[chip] && minioninfo->chip_status[chip].overheat && state == 2) + sys_chip_sta(minioncgpu, chip); + } + } + + sentwork = sentwork; +#if ENABLE_INT_NONO + if (sentwork) { + // Clear CMD interrupt since we've now sent more + K_WLOCK(minioninfo->tfree_list); + task = k_unlink_head(minioninfo->tfree_list); + DATA_TASK(task)->tid = ++(minioninfo->next_tid); + DATA_TASK(task)->chip = 0; // ignored + DATA_TASK(task)->write = true; + DATA_TASK(task)->address = MINION_SYS_INT_CLR; + DATA_TASK(task)->task_id = 0; // ignored + DATA_TASK(task)->wsiz = MINION_SYS_SIZ; + DATA_TASK(task)->rsiz = 0; + DATA_TASK(task)->wbuf[0] = MINION_CMD_INT; + DATA_TASK(task)->wbuf[1] = 0; + DATA_TASK(task)->wbuf[2] = 0; + DATA_TASK(task)->wbuf[3] = 0; + DATA_TASK(task)->urgent = false; + k_add_head(minioninfo->task_list, task); + K_WUNLOCK(minioninfo->tfree_list); + } +#endif + +//applog(LOG_ERR, "%s%i: chip %d fin: quew %d chw %d", minioncgpu->drv->name, minioncgpu->device_id, CHP, minioninfo->chip_status[CHP].quework, minioninfo->chip_status[CHP].chipwork); +} + +static bool minion_thread_prepare(struct thr_info *thr) +{ + struct cgpu_info *minioncgpu = thr->cgpu; + struct minion_info *minioninfo = (struct minion_info *)(minioncgpu->device_data); + + minioninfo->thr = thr; + /* + * SPI/ioctl write thread + */ + if (thr_info_create(&(minioninfo->spiw_thr), NULL, minion_spi_write, (void *)minioncgpu)) { + applog(LOG_ERR, "%s%i: SPI write thread create failed", + minioncgpu->drv->name, minioncgpu->device_id); + return false; + } + pthread_detach(minioninfo->spiw_thr.pth); + + /* + * SPI/ioctl results thread + */ + if (thr_info_create(&(minioninfo->spir_thr), NULL, minion_spi_reply, (void *)minioncgpu)) { + applog(LOG_ERR, "%s%i: SPI reply thread create failed", + minioncgpu->drv->name, minioncgpu->device_id); + return false; + } + pthread_detach(minioninfo->spir_thr.pth); + + /* + * Seperate results checking thread so ioctl timing can ignore the results checking + */ + if (thr_info_create(&(minioninfo->res_thr), NULL, minion_results, (void *)minioncgpu)) { + applog(LOG_ERR, "%s%i: Results thread create failed", + minioncgpu->drv->name, minioncgpu->device_id); + return false; + } + pthread_detach(minioninfo->res_thr.pth); + + return true; +} + +static void minion_shutdown(struct thr_info *thr) +{ + struct cgpu_info *minioncgpu = thr->cgpu; + struct minion_info *minioninfo = (struct minion_info *)(minioncgpu->device_data); + int i; + + applog(MINION_LOG, "%s%i: shutting down", + minioncgpu->drv->name, minioncgpu->device_id); + + for (i = 0; i < (int)MINION_CHIPS; i++) + if (minioninfo->has_chip[i]) +// TODO: minion_shutdown(minioncgpu, minioninfo, i); + i = i; + + minioncgpu->shutdown = true; +} + +static bool minion_queue_full(struct cgpu_info *minioncgpu) +{ + struct minion_info *minioninfo = (struct minion_info *)(minioncgpu->device_data); + struct work *work, *usework; + int count, totneed, need, roll, roll_limit, chip; + bool ret, rolled; + + if (minioninfo->initialised == false) { + cgsleep_us(42); + return true; + } + + K_RLOCK(minioninfo->wwork_list); + count = minioninfo->wwork_list->count; + totneed = 0; + for (chip = 0; chip < (int)MINION_CHIPS; chip++) { + if (minioninfo->has_chip[chip] && + !minioninfo->chip_status[chip].overheat) { + totneed += MINION_QUE_HIGH; + totneed -= minioninfo->chip_status[chip].quework; + totneed -= minioninfo->chip_status[chip].realwork; + // One for the pot :) + totneed++; + } + } + K_RUNLOCK(minioninfo->wwork_list); + + if (count >= totneed) + ret = true; + else { + need = totneed - count; + /* Ensure we do enough rolling to reduce CPU + but dont roll too much to have them end up stale */ + if (need < 16) + need = 16; + work = get_queued(minioncgpu); + if (work) { + roll_limit = work->drv_rolllimit; + roll = 0; + do { + if (roll == 0) { + usework = work; + minioninfo->work_unrolled++; + rolled = false; + } else { + usework = copy_work_noffset(work, roll); + minioninfo->work_rolled++; + rolled = true; + } + ready_work(minioncgpu, usework, rolled); + } while (--need > 0 && ++roll <= roll_limit); + } else { + // Avoid a hard loop when we can't get work fast enough + cgsleep_us(42); + } + + if (need > 0) + ret = false; + else + ret = true; + } + + return ret; +} + +static void idle_report(struct cgpu_info *minioncgpu) +{ + struct minion_info *minioninfo = (struct minion_info *)(minioncgpu->device_data); + struct timeval now; + uint32_t idle; + int msdiff; + int chip; + + for (chip = 0; chip < (int)MINION_CHIPS; chip++) { + if (minioninfo->has_chip[chip]) { + idle = minioninfo->chip_status[chip].idle; + if (idle != minioninfo->chip_status[chip].last_rpt_idle) { + cgtime(&now); + msdiff = ms_tdiff(&now, &(minioninfo->chip_status[chip].idle_rpt)); + if (msdiff >= MINION_IDLE_MESSAGE_ms) { + memcpy(&(minioninfo->chip_status[chip].idle_rpt), &now, sizeof(now)); + applog(LOG_WARNING, + "%s%d: chip %d internal idle changed %08x", + minioncgpu->drv->name, minioncgpu->device_id, + chip, idle); + minioninfo->chip_status[chip].last_rpt_idle = idle; + } + } + } + } +} + +static void chip_report(struct cgpu_info *minioncgpu) +{ + struct minion_info *minioninfo = (struct minion_info *)(minioncgpu->device_data); + struct timeval now; + char buf[512]; + char res_err_msg[2]; + size_t len; + double elapsed, ghs, ghs2, expect, howlong; + char ghs2_display[64]; + K_ITEM *pitem; + int msdiff, chip; + int res_err_count; + + cgtime(&now); + if (!(minioninfo->chip_chk.tv_sec)) { + memcpy(&(minioninfo->chip_chk), &now, sizeof(now)); + memcpy(&(minioninfo->chip_rpt), &now, sizeof(now)); + return; + } + + // Always run the calculations to check chip GHs for the LED + buf[0] = '\0'; + res_err_msg[0] = '\0'; + res_err_msg[1] = '\0'; + K_RLOCK(minioninfo->hfree_list); + for (chip = 0; chip < (int)MINION_CHIPS; chip++) { + if (minioninfo->has_chip[chip]) { + len = strlen(buf); + if (minioninfo->hchip_list[chip]->count < 2) + ghs = 0.0; + else { + ghs = 0xffffffffull * (minioninfo->hchip_list[chip]->count - 1); + ghs /= 1000000000.0; + ghs /= tdiff(&now, &(DATA_HIST(minioninfo->hchip_list[chip]->tail)->when)); + } + if (minioninfo->chip_status[chip].first_nonce.tv_sec == 0L || + tdiff(&now, &minioninfo->chip_status[chip].first_nonce) < MINION_LED_TEST_TIME) { + ghs2_display[0] = '\0'; + minioninfo->setled[chip] = false; + } else { + ghs2 = 0xffffffffull * (minioninfo->chip_status[chip].from_first_good - 1); + ghs2 /= 1000000000.0; + ghs2 /= tdiff(&now, &minioninfo->chip_status[chip].first_nonce); + minioninfo->setled[chip] = (ghs2 >= opt_minion_ledlimit); + snprintf(ghs2_display, sizeof(ghs2_display), "[%.2f]", ghs2); + } + + res_err_count = minioninfo->res_err_count[chip]; + minioninfo->res_err_count[chip] = 0; + if (res_err_count > 100) + res_err_msg[0] = '!'; + else if (res_err_count > 50) + res_err_msg[0] = '*'; + else if (res_err_count > 0) + res_err_msg[0] = '\''; + else + res_err_msg[0] = '\0'; + snprintf(buf + len, sizeof(buf) - len, + " %d=%s%.2f%s", chip, res_err_msg, ghs, ghs2_display); + minioninfo->history_ghs[chip] = ghs; + } + } + K_RUNLOCK(minioninfo->hfree_list); + + // But only display it if required + if (opt_minion_chipreport > 0) { + msdiff = ms_tdiff(&now, &(minioninfo->chip_rpt)); + if (msdiff >= (opt_minion_chipreport * 1000)) { + memcpy(&(minioninfo->chip_chk), &now, sizeof(now)); + applogsiz(LOG_WARNING, 512, + "%s%d: Chip GHs%s", + minioncgpu->drv->name, minioncgpu->device_id, buf); + memcpy(&(minioninfo->chip_rpt), &now, sizeof(now)); + } + } + + msdiff = ms_tdiff(&now, &(minioninfo->chip_chk)); + if (total_secs >= MINION_RESET_s && msdiff >= (minioninfo->history_gen * 1000)) { + K_RLOCK(minioninfo->hfree_list); + for (chip = 0; chip < (int)MINION_CHIPS; chip++) { + if (minioninfo->has_chip[chip]) { + // Don't reset the chip while 'changing' + if (minioninfo->changing[chip]) + continue; + + if (!minioninfo->reset_mark[chip] || + minioninfo->reset_count[chip] < 2) { + elapsed = 0.0; + ghs = 0.0; + } else { + // 'now' includes that it may have stopped getting nonces + elapsed = tdiff(&now, &(DATA_HIST(minioninfo->reset_mark[chip])->when)); + ghs = 0xffffffffull * (minioninfo->reset_count[chip] - 1); + ghs /= 1000000000.0; + ghs /= elapsed; + } + expect = (double)(minioninfo->init_freq[chip]) * + MINION_RESET_PERCENT / 1000.0; + howlong = tdiff(&now, &(minioninfo->last_reset[chip])); + if (ghs <= expect && howlong >= minioninfo->reset_time[chip]) { + minioninfo->do_reset[chip] = expect; + + // For now - no lock required since no other code accesses it + pitem = k_unlink_head(minioninfo->pfree_list); + DATA_PERF(pitem)->elapsed = elapsed; + DATA_PERF(pitem)->nonces = minioninfo->reset_count[chip] - 1; + DATA_PERF(pitem)->freq = minioninfo->init_freq[chip]; + DATA_PERF(pitem)->ghs = ghs; + memcpy(&(DATA_PERF(pitem)->when), &now, sizeof(now)); + k_add_head(minioninfo->p_list[chip], pitem); + } else if (second_check) { + expect = (double)(minioninfo->init_freq[chip]) * + MINION_RESET2_PERCENT / 1000.0; + if (ghs < expect && howlong >= minioninfo->reset2_time[chip]) { + /* Only do a reset, don't record it, since the ghs + is still above MINION_RESET_PERCENT */ + minioninfo->do_reset[chip] = expect; + } + } + minioninfo->history_ghs[chip] = ghs; + // Expire old perf items to stop clockdown + if (minioninfo->do_reset[chip] <= 1.0 && howlong > MINION_CLR_s) { + // Always remember the last reset + while (minioninfo->p_list[chip]->count > 1) { + pitem = k_unlink_tail(minioninfo->p_list[chip]); + k_add_head(minioninfo->pfree_list, pitem); + } + } + } + } + K_RUNLOCK(minioninfo->hfree_list); + + memcpy(&(minioninfo->chip_chk), &now, sizeof(now)); + } + + for (chip = 0; chip < (int)MINION_CHIPS; chip++) { + if (minioninfo->has_chip[chip]) { + // Don't reset the chip while 'changing' + if (minioninfo->changing[chip]) + continue; + + if (minioninfo->do_reset[chip] > 1.0 || + minioninfo->flag_reset[chip]) { + bool std_reset = true; + int curr_freq = minioninfo->init_freq[chip]; + int new_freq = 0.0; + int count; + + // Adjust frequency down? + if (!opt_minion_noautofreq && + minioninfo->p_list[chip]->count >= MINION_RESET_COUNT) { + pitem = minioninfo->p_list[chip]->head; + count = 1; + while (pitem && pitem->next && count++ < MINION_RESET_COUNT) { + if (DATA_PERF(pitem)->freq != DATA_PERF(pitem->next)->freq) + break; + if (count >= MINION_RESET_COUNT) { + new_freq = minioninfo->init_freq[chip] - + MINION_FREQ_RESET_STEP; + if (new_freq < MINION_FREQ_MIN) + new_freq = MINION_FREQ_MIN; + if (minioninfo->init_freq[chip] != new_freq) { + minioninfo->init_freq[chip] = new_freq; + std_reset = false; + } + break; + } else + pitem = pitem->next; + } + } + + if (std_reset) { + if (minioninfo->do_reset[chip] > 1.0) { + applog(LOG_WARNING, "%s%d: Chip %d %dMHz threshold " + "%.2fGHs - resetting", + minioncgpu->drv->name, + minioncgpu->device_id, + chip, curr_freq, + minioninfo->do_reset[chip]); + } else { + applog(LOG_WARNING, "%s%d: Chip %d %dMhz flagged - " + "resetting", + minioncgpu->drv->name, + minioncgpu->device_id, + chip, curr_freq); + } + } else { + if (minioninfo->do_reset[chip] > 1.0) { + applog(LOG_WARNING, "%s%d: Chip %d %dMHz threshold " + "%.2fGHs - resetting to %dMhz", + minioncgpu->drv->name, + minioncgpu->device_id, + chip, curr_freq, + minioninfo->do_reset[chip], + new_freq); + } else { + applog(LOG_WARNING, "%s%d: Chip %d %dMhz flagged - " + "resetting to %dMHz", + minioncgpu->drv->name, + minioncgpu->device_id, + chip, curr_freq, new_freq); + } + } + minioninfo->do_reset[chip] = 0.0; + memcpy(&(minioninfo->last_reset[chip]), &now, sizeof(now)); + init_chip(minioncgpu, minioninfo, chip); + minioninfo->flag_reset[chip] = false; + } + } + } +} + +static int64_t minion_scanwork(__maybe_unused struct thr_info *thr) +{ + struct cgpu_info *minioncgpu = thr->cgpu; + struct minion_info *minioninfo = (struct minion_info *)(minioncgpu->device_data); + int64_t hashcount = 0; + + if (minioninfo->initialised == false) + return hashcount; + + minion_do_work(minioncgpu); + + mutex_lock(&(minioninfo->nonce_lock)); + if (minioninfo->new_nonces) { + hashcount += 0xffffffffull * minioninfo->new_nonces; + minioninfo->new_nonces = 0; + } + mutex_unlock(&(minioninfo->nonce_lock)); + + if (opt_minion_idlecount) + idle_report(minioncgpu); + + // Must always generate data to check/allow for chip reset + chip_report(minioncgpu); + + /* + * To avoid wasting CPU, wait until we get an interrupt + * before returning back to the main cgminer work loop + * i.e. we then know we'll need more work + */ + cgsem_mswait(&(minioninfo->scan_work), MINION_SCAN_mS); + + return hashcount; +} + +static const char *temp_str(uint16_t temp) +{ + switch (temp) { + case MINION_TEMP_40: + return min_temp_40; + case MINION_TEMP_60: + return min_temp_60; + case MINION_TEMP_80: + return min_temp_80; + case MINION_TEMP_100: + return min_temp_100; + case MINION_TEMP_OVER: + return min_temp_over; + } + return min_temp_invalid; +} + +static void minion_get_statline_before(char *buf, size_t bufsiz, struct cgpu_info *minioncgpu) +{ + struct minion_info *minioninfo = (struct minion_info *)(minioncgpu->device_data); + uint16_t max_temp, cores; + int chip, core; + + max_temp = 0; + cores = 0; + mutex_lock(&(minioninfo->sta_lock)); + for (chip = 0; chip < (int)MINION_CHIPS; chip++) { + if (minioninfo->has_chip[chip]) { + if (max_temp < minioninfo->chip_status[chip].temp) + max_temp = minioninfo->chip_status[chip].temp; + for (core = 0; core < MINION_CORES; core++) { + if (minioninfo->chip_core_ena[core >> 5][chip] & (0x1 << (core % 32))) + cores++; + } + } + } + mutex_unlock(&(minioninfo->sta_lock)); + + tailsprintf(buf, bufsiz, "max%sC Ch:%d Co:%d", + temp_str(max_temp), minioninfo->chips, (int)cores); +} + +#define CHIPS_PER_STAT 5 + +static struct api_data *minion_api_stats(struct cgpu_info *minioncgpu) +{ + struct minion_info *minioninfo = (struct minion_info *)(minioncgpu->device_data); + struct api_data *root = NULL; + char cores[MINION_CORES+1]; + char data[2048]; + char buf[32]; + int i, to, j; + size_t datalen, nlen; + int chip, max_chip, que_work, chip_work, temp; + + if (minioninfo->initialised == false) + return NULL; + + root = api_add_uint64(root, "OK Nonces", &(minioninfo->ok_nonces), true); + root = api_add_uint64(root, "New Nonces", &(minioninfo->new_nonces), true); + root = api_add_uint64(root, "Tested Nonces", &(minioninfo->tested_nonces), true); + root = api_add_uint64(root, "Untested Nonces", &(minioninfo->untested_nonces), true); + + root = api_add_int(root, "Chips", &(minioninfo->chips), true); + i = MINION_PIN_COUNT; + root = api_add_int(root, "GPIO Pins", &i, true); + + max_chip = 0; + for (chip = 0; chip < (int)MINION_CHIPS; chip++) + if (minioninfo->has_chip[chip]) { + max_chip = chip; + + snprintf(buf, sizeof(buf), "Chip %d Pin", chip); + root = api_add_int(root, buf, &(minioninfo->chip_pin[chip]), true); + snprintf(buf, sizeof(buf), "Chip %d ChipID", chip); + i = (int)(minioninfo->chipid[chip]); + root = api_add_int(root, buf, &i, true); + snprintf(buf, sizeof(buf), "Chip %d Temperature", chip); + root = api_add_const(root, buf, temp_str(minioninfo->chip_status[chip].temp), false); + snprintf(buf, sizeof(buf), "Chip %d Cores", chip); + root = api_add_uint16(root, buf, &(minioninfo->chip_status[chip].cores), true); + snprintf(buf, sizeof(buf), "Chip %d Frequency", chip); + root = api_add_uint32(root, buf, &(minioninfo->chip_status[chip].freq), true); + snprintf(buf, sizeof(buf), "Chip %d InitFreq", chip); + root = api_add_int(root, buf, &(minioninfo->init_freq[chip]), true); + snprintf(buf, sizeof(buf), "Chip %d FreqSent", chip); + root = api_add_hex32(root, buf, &(minioninfo->freqsent[chip]), true); + snprintf(buf, sizeof(buf), "Chip %d InitTemp", chip); + temp = minioninfo->init_temp[chip]; + if (temp == MINION_TEMP_CTL_DISABLE) + root = api_add_string(root, buf, MINION_TEMP_DISABLE, true); + else { + snprintf(data, sizeof(data), "%d", temp); + root = api_add_string(root, buf, data, true); + } + snprintf(buf, sizeof(buf), "Chip %d TempSent", chip); + root = api_add_hex32(root, buf, &(minioninfo->chip_status[chip].tempsent), true); + __bin2hex(data, (unsigned char *)(&(minioninfo->init_cores[chip][0])), + sizeof(minioninfo->init_cores[chip])); + snprintf(buf, sizeof(buf), "Chip %d InitCores", chip); + root = api_add_string(root, buf, data, true); + snprintf(buf, sizeof(buf), "Chip %d IdleCount", chip); + root = api_add_hex32(root, buf, &(minioninfo->chip_status[chip].idle), true); + snprintf(buf, sizeof(buf), "Chip %d QueWork", chip); + root = api_add_uint32(root, buf, &(minioninfo->chip_status[chip].quework), true); + snprintf(buf, sizeof(buf), "Chip %d ChipWork", chip); + root = api_add_uint32(root, buf, &(minioninfo->chip_status[chip].chipwork), true); + snprintf(buf, sizeof(buf), "Chip %d RealWork", chip); + root = api_add_uint32(root, buf, &(minioninfo->chip_status[chip].realwork), true); + snprintf(buf, sizeof(buf), "Chip %d QueListCount", chip); + root = api_add_int(root, buf, &(minioninfo->wque_list[chip]->count), true); + snprintf(buf, sizeof(buf), "Chip %d WorkListCount", chip); + root = api_add_int(root, buf, &(minioninfo->wchip_list[chip]->count), true); + snprintf(buf, sizeof(buf), "Chip %d Overheat", chip); + root = api_add_bool(root, buf, &(minioninfo->chip_status[chip].overheat), true); + snprintf(buf, sizeof(buf), "Chip %d Overheats", chip); + root = api_add_uint32(root, buf, &(minioninfo->chip_status[chip].overheats), true); + snprintf(buf, sizeof(buf), "Chip %d LastOverheat", chip); + root = api_add_timeval(root, buf, &(minioninfo->chip_status[chip].lastoverheat), true); + snprintf(buf, sizeof(buf), "Chip %d LastRecover", chip); + root = api_add_timeval(root, buf, &(minioninfo->chip_status[chip].lastrecover), true); + snprintf(buf, sizeof(buf), "Chip %d OverheatIdle", chip); + root = api_add_double(root, buf, &(minioninfo->chip_status[chip].overheattime), true); + for (i = 0; i < MINION_CORES; i++) { + if (minioninfo->chip_core_ena[i >> 5][chip] & (0x1 << (i % 32))) + cores[i] = 'o'; + else + cores[i] = 'x'; + } + cores[MINION_CORES] = '\0'; + snprintf(buf, sizeof(buf), "Chip %d CoresEna", chip); + root = api_add_string(root, buf, cores, true); + for (i = 0; i < MINION_CORES; i++) { + if (minioninfo->chip_core_act[i >> 5][chip] & (0x1 << (i % 32))) + cores[i] = '-'; + else + cores[i] = 'o'; + } + cores[MINION_CORES] = '\0'; + snprintf(buf, sizeof(buf), "Chip %d CoresAct", chip); + root = api_add_string(root, buf, cores, true); + + if (opt_minion_extra) { + data[0] = '\0'; + datalen = 0; + for (i = 0; i < MINION_CORES; i++) { + if (datalen < sizeof(data)) { + nlen = snprintf(data+datalen, sizeof(data)-datalen, + "%s%"PRIu64"-%s%"PRIu64, + i == 0 ? "" : "/", + minioninfo->core_good[chip][i], + minioninfo->core_bad[chip][i] ? "'" : "", + minioninfo->core_bad[chip][i]); + if (nlen < 1) + break; + datalen += nlen; + } + } + snprintf(buf, sizeof(buf), "Chip %d Cores Good-Bad", chip); + root = api_add_string(root, buf, data, true); + } + + snprintf(buf, sizeof(buf), "Chip %d History GHs", chip); + root = api_add_mhs(root, buf, &(minioninfo->history_ghs[chip]), true); + } + + double his = MINION_HISTORY_s; + root = api_add_double(root, "History length", &his, true); + his = MINION_RESET_s; + root = api_add_double(root, "Default reset length", &his, true); + his = MINION_RESET2_s; + root = api_add_double(root, "Default reset2 length", &his, true); + root = api_add_bool(root, "Reset2 enabled", &second_check, true); + + for (i = 0; i <= max_chip; i += CHIPS_PER_STAT) { + to = i + CHIPS_PER_STAT - 1; + if (to > max_chip) + to = max_chip; + + data[0] = '\0'; + for (j = i; j <= to; j++) { + snprintf(buf, sizeof(buf), + "%s%d", + j == i ? "" : " ", + minioninfo->has_chip[j] ? 1 : 0); + strcat(data, buf); + } + snprintf(buf, sizeof(buf), "Detected %02d - %02d", i, to); + root = api_add_string(root, buf, data, true); + + data[0] = '\0'; + for (j = i; j <= to; j++) { + snprintf(buf, sizeof(buf), + "%s%8"PRIu64, + j == i ? "" : " ", + minioninfo->chip_nonces[j]); + strcat(data, buf); + } + snprintf(buf, sizeof(buf), "Nonces %02d - %02d", i, to); + root = api_add_string(root, buf, data, true); + + data[0] = '\0'; + for (j = i; j <= to; j++) { + snprintf(buf, sizeof(buf), + "%s%8"PRIu64, + j == i ? "" : " ", + minioninfo->chip_nononces[j]); + strcat(data, buf); + } + snprintf(buf, sizeof(buf), "NoNonces %02d - %02d", i, to); + root = api_add_string(root, buf, data, true); + + data[0] = '\0'; + for (j = i; j <= to; j++) { + snprintf(buf, sizeof(buf), + "%s%8"PRIu64, + j == i ? "" : " ", + minioninfo->chip_good[j]); + strcat(data, buf); + } + snprintf(buf, sizeof(buf), "Good %02d - %02d", i, to); + root = api_add_string(root, buf, data, true); + + data[0] = '\0'; + for (j = i; j <= to; j++) { + snprintf(buf, sizeof(buf), + "%s%8"PRIu64, + j == i ? "" : " ", + minioninfo->chip_bad[j]); + strcat(data, buf); + } + snprintf(buf, sizeof(buf), "Bad %02d - %02d", i, to); + root = api_add_string(root, buf, data, true); + + data[0] = '\0'; + for (j = i; j <= to; j++) { + snprintf(buf, sizeof(buf), + "%s%8"PRIu64, + j == i ? "" : " ", + minioninfo->chip_err[j]); + strcat(data, buf); + } + snprintf(buf, sizeof(buf), "Err %02d - %02d", i, to); + root = api_add_string(root, buf, data, true); + + data[0] = '\0'; + for (j = i; j <= to; j++) { + snprintf(buf, sizeof(buf), + "%s%8"PRIu64, + j == i ? "" : " ", + minioninfo->fifo_spi_errors[j]); + strcat(data, buf); + } + snprintf(buf, sizeof(buf), "FifoSpiErr %02d - %02d", i, to); + root = api_add_string(root, buf, data, true); + + data[0] = '\0'; + for (j = i; j <= to; j++) { + snprintf(buf, sizeof(buf), + "%s%8"PRIu64, + j == i ? "" : " ", + minioninfo->res_spi_errors[j]); + strcat(data, buf); + } + snprintf(buf, sizeof(buf), "ResSpiErr %02d - %02d", i, to); + root = api_add_string(root, buf, data, true); + + data[0] = '\0'; + for (j = i; j <= to; j++) { + snprintf(buf, sizeof(buf), + "%s%"PRIu64"/%"PRIu64"/%"PRIu64"/%"PRIu64"/%"PRIu64, + j == i ? "" : " ", + minioninfo->use_res2[j], + minioninfo->tasks_failed[j], + minioninfo->tasks_recovered[j], + minioninfo->nonces_failed[j], + minioninfo->nonces_recovered[j]); + strcat(data, buf); + } + snprintf(buf, sizeof(buf), "Redo %02d - %02d", i, to); + root = api_add_string(root, buf, data, true); + } + + que_work = chip_work = 0; + for (chip = 0; chip <= max_chip; chip++) { + if (minioninfo->has_chip[chip]) { + que_work += minioninfo->wque_list[chip]->count; + chip_work += minioninfo->wchip_list[chip]->count; + } + } + + root = api_add_int(root, "WFree Total", &(minioninfo->wfree_list->total), true); + root = api_add_int(root, "WFree Count", &(minioninfo->wfree_list->count), true); + root = api_add_int(root, "WWork Count", &(minioninfo->wwork_list->count), true); + root = api_add_uint64(root, "WWork Flushed", &(minioninfo->wwork_flushed), true); + root = api_add_int(root, "WQue Count", &que_work, true); + root = api_add_uint64(root, "WQue Flushed", &(minioninfo->wque_flushed), true); + root = api_add_int(root, "WChip Count", &chip_work, true); + root = api_add_uint64(root, "WChip Stale", &(minioninfo->wchip_staled), true); + + root = api_add_int(root, "TFree Total", &(minioninfo->tfree_list->total), true); + root = api_add_int(root, "TFree Count", &(minioninfo->tfree_list->count), true); + root = api_add_int(root, "Task Count", &(minioninfo->task_list->count), true); + root = api_add_int(root, "Reply Count", &(minioninfo->treply_list->count), true); + + root = api_add_int(root, "RFree Total", &(minioninfo->rfree_list->total), true); + root = api_add_int(root, "RFree Count", &(minioninfo->rfree_list->count), true); + root = api_add_int(root, "RNonce Count", &(minioninfo->rnonce_list->count), true); + + root = api_add_int(root, "XFree Count", &(minioninfo->xfree_list->count), true); + root = api_add_int(root, "XFF Count", &(minioninfo->xff_list->count), true); + root = api_add_uint64(root, "XFFs", &(minioninfo->xffs), true); + root = api_add_uint64(root, "SPI Resets", &(minioninfo->spi_resets), true); + root = api_add_uint64(root, "Power Cycles", &(minioninfo->power_cycles), true); + + root = api_add_int(root, "Chip Report", &opt_minion_chipreport, true); + root = api_add_int(root, "LED Count", &opt_minion_ledcount, true); + root = api_add_int(root, "LED Limit", &opt_minion_ledlimit, true); + bool b = !opt_minion_noautofreq; + root = api_add_bool(root, "Auto Freq", &b, true); + root = api_add_int(root, "SPI Delay", &opt_minion_spidelay, true); + root = api_add_bool(root, "SPI Reset I/O", &(minioninfo->spi_reset_io), true); + root = api_add_int(root, "SPI Reset", &(minioninfo->spi_reset_count), true); + root = api_add_int(root, "SPI Reset Sleep", &opt_minion_spisleep, true); + +#if DO_IO_STATS +#define sta_api(_name, _iostat) \ + do { \ + if ((_iostat).count) { \ + float _davg = (float)((_iostat).total_delay) / (float)((_iostat).count); \ + float _dlavg = (float)((_iostat).total_dlock) / (float)((_iostat).count); \ + float _dlwavg = (float)((_iostat).total_dlwait) / (float)((_iostat).count); \ + float _bavg = (float)((_iostat).total_bytes) / (float)((_iostat).count); \ + float _tavg = (float)((_iostat).tsd) / (float)((_iostat).count); \ + snprintf(data, sizeof(data), "%s Count=%"PRIu64 \ + " Delay=%.0fus DAvg=%.3f" \ + " DMin=%.0f DMax=%.0f DZ=%"PRIu64 \ + " DLock=%.0fus DLAvg=%.3f" \ + " DLMin=%.0f DLMax=%.0f DZ=%"PRIu64 \ + " DLWait=%.0fus DLWAvg=%.3f" \ + " Bytes=%"PRIu64" BAvg=%.3f" \ + " BMin=%"PRIu64" BMax=%"PRIu64" BZ=%"PRIu64 \ + " TSD=%.0fus TAvg=%.03f", \ + _name, (_iostat).count, \ + (_iostat).total_delay, _davg, (_iostat).min_delay, \ + (_iostat).max_delay, (_iostat).zero_delay, \ + (_iostat).total_dlock, _dlavg, (_iostat).min_dlock, \ + (_iostat).max_dlock, (_iostat).zero_dlock, \ + (_iostat).total_dlwait, _dlwavg, \ + (_iostat).total_bytes, _bavg, (_iostat).min_bytes, \ + (_iostat).max_bytes, (_iostat).zero_bytes, \ + (_iostat).tsd, _tavg); \ + root = api_add_string(root, buf, data, true); \ + } \ + } while(0); + + for (i = 0; i < 0x200; i++) { + snprintf(buf, sizeof(buf), "Stat-0x%02x", i); + sta_api(addr2txt((uint8_t)(i & 0xff)), minioninfo->iostats[i]); + } + + // Test to avoid showing applog + if (minioninfo->summary.count) { + snprintf(buf, sizeof(buf), "Stat-S"); + sta_api("Summary", minioninfo->summary); + applog(LOG_WARNING, "%s %d: (%.0f) %s - %s", + minioncgpu->drv->name, minioncgpu->device_id, + total_secs, buf, data); + } +#endif + + root = api_add_uint64(root, "Total SPI Errors", &(minioninfo->spi_errors), true); + root = api_add_uint64(root, "Work Unrolled", &(minioninfo->work_unrolled), true); + root = api_add_uint64(root, "Work Rolled", &(minioninfo->work_rolled), true); + root = api_add_uint64(root, "Ints", &(minioninfo->interrupts), true); + root = api_add_uint64(root, "Res Ints", &(minioninfo->result_interrupts), true); + root = api_add_uint64(root, "Cmd Ints", &(minioninfo->command_interrupts), true); + root = api_add_string(root, "Last Int", minioninfo->last_interrupt, true); + root = api_add_hex32(root, "Next TaskID", &(minioninfo->next_task_id), true); + + double avg; + root = api_add_uint64(root, "ToQue", &(minioninfo->que_work), true); + if (minioninfo->que_work) + avg = minioninfo->que_time / (double)(minioninfo->que_work); + else + avg = 0; + root = api_add_double(root, "Que Avg", &avg, true); + root = api_add_double(root, "Que Min", &(minioninfo->que_min), true); + root = api_add_double(root, "Que Max", &(minioninfo->que_max), true); + data[0] = '\0'; + for (i = 0; i <= TIME_BANDS; i++) { + snprintf(buf, sizeof(buf), + "%s%"PRIu64, + i == 0 ? "" : "/", + minioninfo->que_bands[i]); + strcat(data, buf); + } + root = api_add_string(root, "Que Bands", data, true); + + root = api_add_uint64(root, "ToTxRx", &(minioninfo->wt_work), true); + if (minioninfo->wt_work) + avg = minioninfo->wt_time / (double)(minioninfo->wt_work); + else + avg = 0; + root = api_add_double(root, "TxRx Avg", &avg, true); + root = api_add_double(root, "TxRx Min", &(minioninfo->wt_min), true); + root = api_add_double(root, "TxRx Max", &(minioninfo->wt_max), true); + data[0] = '\0'; + for (i = 0; i <= TIME_BANDS; i++) { + snprintf(buf, sizeof(buf), + "%s%"PRIu64, + i == 0 ? "" : "/", + minioninfo->wt_bands[i]); + strcat(data, buf); + } + root = api_add_string(root, "TxRx Bands", data, true); + + uint64_t checked, dups; + dupcounters(minioncgpu, &checked, &dups); + root = api_add_uint64(root, "Dups", &dups, true); + + return root; +} +#endif + +struct device_drv minion_drv = { + .drv_id = DRIVER_minion, + .dname = "Minion BlackArrow", + .name = "MBA", + .drv_detect = minion_detect, +#ifdef LINUX + .get_api_stats = minion_api_stats, + .get_statline_before = minion_get_statline_before, + .set_device = minion_api_set, + .identify_device = minion_identify, + .thread_prepare = minion_thread_prepare, + .hash_work = hash_queued_work, + .scanwork = minion_scanwork, + .queue_full = minion_queue_full, + .flush_work = minion_flush_work, + .thread_shutdown = minion_shutdown +#endif +}; diff --git a/driver-modminer.c b/driver-modminer.c index 8f85855ad4..a7fb856ff5 100644 --- a/driver-modminer.c +++ b/driver-modminer.c @@ -87,8 +87,6 @@ // Limit when reducing shares_to_good #define MODMINER_MIN_BACK 12 -struct device_drv modminer_drv; - // 45 noops sent when detecting, in case the device was left in "start job" reading static const char NOOP[] = MODMINER_PING "\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff"; @@ -111,7 +109,7 @@ static void do_ping(struct cgpu_info *modminer) modminer->drv->name, modminer->fpgaid, amount, err); } -static bool modminer_detect_one(struct libusb_device *dev, struct usb_find_devices *found) +static struct cgpu_info *modminer_detect_one(struct libusb_device *dev, struct usb_find_devices *found) { char buf[0x100+1]; char *devname = NULL; @@ -128,9 +126,6 @@ static bool modminer_detect_one(struct libusb_device *dev, struct usb_find_devic if (!usb_init(modminer, dev, found)) goto shin; - usb_set_cps(modminer, 11520); - usb_enable_cps(modminer); - do_ping(modminer); if ((err = usb_write(modminer, MODMINER_GET_VERSION, 1, &amount, C_REQUESTVERSION)) < 0 || amount != 1) { @@ -208,7 +203,7 @@ static bool modminer_detect_one(struct libusb_device *dev, struct usb_find_devic tmp->modminer_mutex = modminer->modminer_mutex; if (!add_cgpu(tmp)) { - tmp = usb_free_cgpu_devlock(tmp, !added); + tmp = usb_free_cgpu(tmp); goto unshin; } @@ -217,9 +212,9 @@ static bool modminer_detect_one(struct libusb_device *dev, struct usb_find_devic added = true; } - modminer = usb_free_cgpu_devlock(modminer, !added); + modminer = usb_free_cgpu(modminer); - return true; + return modminer; unshin: if (!added) @@ -231,15 +226,15 @@ static bool modminer_detect_one(struct libusb_device *dev, struct usb_find_devic modminer->modminer_mutex = NULL; } - modminer = usb_free_cgpu_devlock(modminer, !added); + modminer = usb_free_cgpu(modminer); if (added) - return true; + return modminer; else - return false; + return NULL; } -static void modminer_detect() +static void modminer_detect(bool __maybe_unused hotplug) { usb_detect(&modminer_drv, modminer_detect_one); } @@ -739,7 +734,7 @@ static bool modminer_fpga_init(struct thr_info *thr) static void get_modminer_statline_before(char *buf, size_t bufsiz, struct cgpu_info *modminer) { - tailsprintf(buf, bufsiz, " %s%.1fC %3uMHz | ", + tailsprintf(buf, bufsiz, "%s%.1fC %3uMHz", (modminer->temp < 10) ? " " : "", modminer->temp, (unsigned int)(modminer->clock)); @@ -1031,7 +1026,7 @@ static uint64_t modminer_process_results(struct thr_info *thr, struct work *work if (hashes > 0xffffffff) hashes = 0xffffffff; - work->blk.nonce = 0xffffffff; + work->nonce = 0xffffffff; return hashes; } @@ -1132,7 +1127,7 @@ static char *modminer_set_device(struct cgpu_info *modminer, char *option, char } struct device_drv modminer_drv = { - .drv_id = DRIVER_MODMINER, + .drv_id = DRIVER_modminer, .dname = "ModMiner", .name = "MMQ", .drv_detect = modminer_detect, diff --git a/driver-opencl.c b/driver-opencl.c deleted file mode 100644 index 57df2dcc96..0000000000 --- a/driver-opencl.c +++ /dev/null @@ -1,1593 +0,0 @@ -/* - * Copyright 2011-2012 Con Kolivas - * Copyright 2011-2012 Luke Dashjr - * Copyright 2010 Jeff Garzik - * - * This program is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License as published by the Free - * Software Foundation; either version 3 of the License, or (at your option) - * any later version. See COPYING for more details. - */ - -#include "config.h" - -#ifdef HAVE_CURSES -#include -#endif - -#include -#include -#include - -#include - -#ifndef WIN32 -#include -#endif -#include - -#include "compat.h" -#include "miner.h" -#include "driver-opencl.h" -#include "findnonce.h" -#include "ocl.h" -#include "adl.h" -#include "util.h" - -/* TODO: cleanup externals ********************/ - -#ifdef HAVE_CURSES -extern WINDOW *mainwin, *statuswin, *logwin; -extern void enable_curses(void); -#endif - -extern int mining_threads; -extern double total_secs; -extern int opt_g_threads; -extern bool opt_loginput; -extern char *opt_kernel_path; -extern int gpur_thr_id; -extern bool opt_noadl; -extern bool have_opencl; - -extern void *miner_thread(void *userdata); -extern int dev_from_id(int thr_id); -extern void decay_time(double *f, double fadd); - -/**********************************************/ - -#ifdef HAVE_OPENCL -struct device_drv opencl_drv; -#endif - -#ifdef HAVE_ADL -extern float gpu_temp(int gpu); -extern int gpu_fanspeed(int gpu); -extern int gpu_fanpercent(int gpu); -#endif - -#ifdef HAVE_OPENCL -char *set_vector(char *arg) -{ - int i, val = 0, device = 0; - char *nextptr; - - nextptr = strtok(arg, ","); - if (nextptr == NULL) - return "Invalid parameters for set vector"; - val = atoi(nextptr); - if (val != 1 && val != 2 && val != 4) - return "Invalid value passed to set_vector"; - - gpus[device++].vwidth = val; - - while ((nextptr = strtok(NULL, ",")) != NULL) { - val = atoi(nextptr); - if (val != 1 && val != 2 && val != 4) - return "Invalid value passed to set_vector"; - - gpus[device++].vwidth = val; - } - if (device == 1) { - for (i = device; i < MAX_GPUDEVICES; i++) - gpus[i].vwidth = gpus[0].vwidth; - } - - return NULL; -} - -char *set_worksize(char *arg) -{ - int i, val = 0, device = 0; - char *nextptr; - - nextptr = strtok(arg, ","); - if (nextptr == NULL) - return "Invalid parameters for set work size"; - val = atoi(nextptr); - if (val < 1 || val > 9999) - return "Invalid value passed to set_worksize"; - - gpus[device++].work_size = val; - - while ((nextptr = strtok(NULL, ",")) != NULL) { - val = atoi(nextptr); - if (val < 1 || val > 9999) - return "Invalid value passed to set_worksize"; - - gpus[device++].work_size = val; - } - if (device == 1) { - for (i = device; i < MAX_GPUDEVICES; i++) - gpus[i].work_size = gpus[0].work_size; - } - - return NULL; -} - -#ifdef USE_SCRYPT -char *set_shaders(char *arg) -{ - int i, val = 0, device = 0; - char *nextptr; - - nextptr = strtok(arg, ","); - if (nextptr == NULL) - return "Invalid parameters for set lookup gap"; - val = atoi(nextptr); - - gpus[device++].shaders = val; - - while ((nextptr = strtok(NULL, ",")) != NULL) { - val = atoi(nextptr); - - gpus[device++].shaders = val; - } - if (device == 1) { - for (i = device; i < MAX_GPUDEVICES; i++) - gpus[i].shaders = gpus[0].shaders; - } - - return NULL; -} - -char *set_lookup_gap(char *arg) -{ - int i, val = 0, device = 0; - char *nextptr; - - nextptr = strtok(arg, ","); - if (nextptr == NULL) - return "Invalid parameters for set lookup gap"; - val = atoi(nextptr); - - gpus[device++].opt_lg = val; - - while ((nextptr = strtok(NULL, ",")) != NULL) { - val = atoi(nextptr); - - gpus[device++].opt_lg = val; - } - if (device == 1) { - for (i = device; i < MAX_GPUDEVICES; i++) - gpus[i].opt_lg = gpus[0].opt_lg; - } - - return NULL; -} - -char *set_thread_concurrency(char *arg) -{ - int i, val = 0, device = 0; - char *nextptr; - - nextptr = strtok(arg, ","); - if (nextptr == NULL) - return "Invalid parameters for set thread concurrency"; - val = atoi(nextptr); - - gpus[device++].opt_tc = val; - - while ((nextptr = strtok(NULL, ",")) != NULL) { - val = atoi(nextptr); - - gpus[device++].opt_tc = val; - } - if (device == 1) { - for (i = device; i < MAX_GPUDEVICES; i++) - gpus[i].opt_tc = gpus[0].opt_tc; - } - - return NULL; -} -#endif - -static enum cl_kernels select_kernel(char *arg) -{ - if (!strcmp(arg, "diablo")) - return KL_DIABLO; - if (!strcmp(arg, "diakgcn")) - return KL_DIAKGCN; - if (!strcmp(arg, "poclbm")) - return KL_POCLBM; - if (!strcmp(arg, "phatk")) - return KL_PHATK; -#ifdef USE_SCRYPT - if (!strcmp(arg, "scrypt")) - return KL_SCRYPT; -#endif - return KL_NONE; -} - -char *set_kernel(char *arg) -{ - enum cl_kernels kern; - int i, device = 0; - char *nextptr; - - if (opt_scrypt) - return "Cannot specify a kernel with scrypt"; - nextptr = strtok(arg, ","); - if (nextptr == NULL) - return "Invalid parameters for set kernel"; - kern = select_kernel(nextptr); - if (kern == KL_NONE) - return "Invalid parameter to set_kernel"; - gpus[device++].kernel = kern; - - while ((nextptr = strtok(NULL, ",")) != NULL) { - kern = select_kernel(nextptr); - if (kern == KL_NONE) - return "Invalid parameter to set_kernel"; - - gpus[device++].kernel = kern; - } - if (device == 1) { - for (i = device; i < MAX_GPUDEVICES; i++) - gpus[i].kernel = gpus[0].kernel; - } - - return NULL; -} -#endif - -#ifdef HAVE_ADL -/* This function allows us to map an adl device to an opencl device for when - * simple enumeration has failed to match them. */ -char *set_gpu_map(char *arg) -{ - int val1 = 0, val2 = 0; - char *nextptr; - - nextptr = strtok(arg, ","); - if (nextptr == NULL) - return "Invalid parameters for set gpu map"; - if (sscanf(arg, "%d:%d", &val1, &val2) != 2) - return "Invalid description for map pair"; - if (val1 < 0 || val1 > MAX_GPUDEVICES || val2 < 0 || val2 > MAX_GPUDEVICES) - return "Invalid value passed to set_gpu_map"; - - gpus[val1].virtual_adl = val2; - gpus[val1].mapped = true; - - while ((nextptr = strtok(NULL, ",")) != NULL) { - if (sscanf(nextptr, "%d:%d", &val1, &val2) != 2) - return "Invalid description for map pair"; - if (val1 < 0 || val1 > MAX_GPUDEVICES || val2 < 0 || val2 > MAX_GPUDEVICES) - return "Invalid value passed to set_gpu_map"; - gpus[val1].virtual_adl = val2; - gpus[val1].mapped = true; - } - - return NULL; -} - -char *set_gpu_engine(char *arg) -{ - int i, val1 = 0, val2 = 0, device = 0; - char *nextptr; - - nextptr = strtok(arg, ","); - if (nextptr == NULL) - return "Invalid parameters for set gpu engine"; - get_intrange(nextptr, &val1, &val2); - if (val1 < 0 || val1 > 9999 || val2 < 0 || val2 > 9999) - return "Invalid value passed to set_gpu_engine"; - - gpus[device].min_engine = val1; - gpus[device].gpu_engine = val2; - device++; - - while ((nextptr = strtok(NULL, ",")) != NULL) { - get_intrange(nextptr, &val1, &val2); - if (val1 < 0 || val1 > 9999 || val2 < 0 || val2 > 9999) - return "Invalid value passed to set_gpu_engine"; - gpus[device].min_engine = val1; - gpus[device].gpu_engine = val2; - device++; - } - - if (device == 1) { - for (i = 1; i < MAX_GPUDEVICES; i++) { - gpus[i].min_engine = gpus[0].min_engine; - gpus[i].gpu_engine = gpus[0].gpu_engine; - } - } - - return NULL; -} - -char *set_gpu_fan(char *arg) -{ - int i, val1 = 0, val2 = 0, device = 0; - char *nextptr; - - nextptr = strtok(arg, ","); - if (nextptr == NULL) - return "Invalid parameters for set gpu fan"; - get_intrange(nextptr, &val1, &val2); - if (val1 < 0 || val1 > 100 || val2 < 0 || val2 > 100) - return "Invalid value passed to set_gpu_fan"; - - gpus[device].min_fan = val1; - gpus[device].gpu_fan = val2; - device++; - - while ((nextptr = strtok(NULL, ",")) != NULL) { - get_intrange(nextptr, &val1, &val2); - if (val1 < 0 || val1 > 100 || val2 < 0 || val2 > 100) - return "Invalid value passed to set_gpu_fan"; - - gpus[device].min_fan = val1; - gpus[device].gpu_fan = val2; - device++; - } - - if (device == 1) { - for (i = 1; i < MAX_GPUDEVICES; i++) { - gpus[i].min_fan = gpus[0].min_fan; - gpus[i].gpu_fan = gpus[0].gpu_fan; - } - } - - return NULL; -} - -char *set_gpu_memclock(char *arg) -{ - int i, val = 0, device = 0; - char *nextptr; - - nextptr = strtok(arg, ","); - if (nextptr == NULL) - return "Invalid parameters for set gpu memclock"; - val = atoi(nextptr); - if (val < 0 || val >= 9999) - return "Invalid value passed to set_gpu_memclock"; - - gpus[device++].gpu_memclock = val; - - while ((nextptr = strtok(NULL, ",")) != NULL) { - val = atoi(nextptr); - if (val < 0 || val >= 9999) - return "Invalid value passed to set_gpu_memclock"; - - gpus[device++].gpu_memclock = val; - } - if (device == 1) { - for (i = device; i < MAX_GPUDEVICES; i++) - gpus[i].gpu_memclock = gpus[0].gpu_memclock; - } - - return NULL; -} - -char *set_gpu_memdiff(char *arg) -{ - int i, val = 0, device = 0; - char *nextptr; - - nextptr = strtok(arg, ","); - if (nextptr == NULL) - return "Invalid parameters for set gpu memdiff"; - val = atoi(nextptr); - if (val < -9999 || val > 9999) - return "Invalid value passed to set_gpu_memdiff"; - - gpus[device++].gpu_memdiff = val; - - while ((nextptr = strtok(NULL, ",")) != NULL) { - val = atoi(nextptr); - if (val < -9999 || val > 9999) - return "Invalid value passed to set_gpu_memdiff"; - - gpus[device++].gpu_memdiff = val; - } - if (device == 1) { - for (i = device; i < MAX_GPUDEVICES; i++) - gpus[i].gpu_memdiff = gpus[0].gpu_memdiff; - } - - return NULL; -} - -char *set_gpu_powertune(char *arg) -{ - int i, val = 0, device = 0; - char *nextptr; - - nextptr = strtok(arg, ","); - if (nextptr == NULL) - return "Invalid parameters for set gpu powertune"; - val = atoi(nextptr); - if (val < -99 || val > 99) - return "Invalid value passed to set_gpu_powertune"; - - gpus[device++].gpu_powertune = val; - - while ((nextptr = strtok(NULL, ",")) != NULL) { - val = atoi(nextptr); - if (val < -99 || val > 99) - return "Invalid value passed to set_gpu_powertune"; - - gpus[device++].gpu_powertune = val; - } - if (device == 1) { - for (i = device; i < MAX_GPUDEVICES; i++) - gpus[i].gpu_powertune = gpus[0].gpu_powertune; - } - - return NULL; -} - -char *set_gpu_vddc(char *arg) -{ - int i, device = 0; - float val = 0; - char *nextptr; - - nextptr = strtok(arg, ","); - if (nextptr == NULL) - return "Invalid parameters for set gpu vddc"; - val = atof(nextptr); - if (val < 0 || val >= 9999) - return "Invalid value passed to set_gpu_vddc"; - - gpus[device++].gpu_vddc = val; - - while ((nextptr = strtok(NULL, ",")) != NULL) { - val = atof(nextptr); - if (val < 0 || val >= 9999) - return "Invalid value passed to set_gpu_vddc"; - - gpus[device++].gpu_vddc = val; - } - if (device == 1) { - for (i = device; i < MAX_GPUDEVICES; i++) - gpus[i].gpu_vddc = gpus[0].gpu_vddc; - } - - return NULL; -} - -char *set_temp_overheat(char *arg) -{ - int i, val = 0, device = 0, *to; - char *nextptr; - - nextptr = strtok(arg, ","); - if (nextptr == NULL) - return "Invalid parameters for set temp overheat"; - val = atoi(nextptr); - if (val < 0 || val > 200) - return "Invalid value passed to set temp overheat"; - - to = &gpus[device++].adl.overtemp; - *to = val; - - while ((nextptr = strtok(NULL, ",")) != NULL) { - val = atoi(nextptr); - if (val < 0 || val > 200) - return "Invalid value passed to set temp overheat"; - - to = &gpus[device++].adl.overtemp; - *to = val; - } - if (device == 1) { - for (i = device; i < MAX_GPUDEVICES; i++) { - to = &gpus[i].adl.overtemp; - *to = val; - } - } - - return NULL; -} - -char *set_temp_target(char *arg) -{ - int i, val = 0, device = 0, *tt; - char *nextptr; - - nextptr = strtok(arg, ","); - if (nextptr == NULL) - return "Invalid parameters for set temp target"; - val = atoi(nextptr); - if (val < 0 || val > 200) - return "Invalid value passed to set temp target"; - - tt = &gpus[device++].adl.targettemp; - *tt = val; - - while ((nextptr = strtok(NULL, ",")) != NULL) { - val = atoi(nextptr); - if (val < 0 || val > 200) - return "Invalid value passed to set temp target"; - - tt = &gpus[device++].adl.targettemp; - *tt = val; - } - if (device == 1) { - for (i = device; i < MAX_GPUDEVICES; i++) { - tt = &gpus[i].adl.targettemp; - *tt = val; - } - } - - return NULL; -} -#endif -#ifdef HAVE_OPENCL -char *set_intensity(char *arg) -{ - int i, device = 0, *tt; - char *nextptr, val = 0; - - nextptr = strtok(arg, ","); - if (nextptr == NULL) - return "Invalid parameters for set intensity"; - if (!strncasecmp(nextptr, "d", 1)) - gpus[device].dynamic = true; - else { - gpus[device].dynamic = false; - val = atoi(nextptr); - if (val < MIN_INTENSITY || val > MAX_GPU_INTENSITY) - return "Invalid value passed to set intensity"; - tt = &gpus[device].intensity; - *tt = val; - } - - device++; - - while ((nextptr = strtok(NULL, ",")) != NULL) { - if (!strncasecmp(nextptr, "d", 1)) - gpus[device].dynamic = true; - else { - gpus[device].dynamic = false; - val = atoi(nextptr); - if (val < MIN_INTENSITY || val > MAX_GPU_INTENSITY) - return "Invalid value passed to set intensity"; - - tt = &gpus[device].intensity; - *tt = val; - } - device++; - } - if (device == 1) { - for (i = device; i < MAX_GPUDEVICES; i++) { - gpus[i].dynamic = gpus[0].dynamic; - gpus[i].intensity = gpus[0].intensity; - } - } - - return NULL; -} - -void print_ndevs(int *ndevs) -{ - opt_log_output = true; - opencl_drv.drv_detect(); - clear_adl(*ndevs); - applog(LOG_INFO, "%i GPU devices max detected", *ndevs); -} -#endif - -struct cgpu_info gpus[MAX_GPUDEVICES]; /* Maximum number apparently possible */ -struct cgpu_info *cpus; - -#ifdef HAVE_OPENCL - -/* In dynamic mode, only the first thread of each device will be in use. - * This potentially could start a thread that was stopped with the start-stop - * options if one were to disable dynamic from the menu on a paused GPU */ -void pause_dynamic_threads(int gpu) -{ - struct cgpu_info *cgpu = &gpus[gpu]; - int i; - - for (i = 1; i < cgpu->threads; i++) { - struct thr_info *thr; - - thr = get_thread(i); - if (!thr->pause && cgpu->dynamic) { - applog(LOG_WARNING, "Disabling extra threads due to dynamic mode."); - applog(LOG_WARNING, "Tune dynamic intensity with --gpu-dyninterval"); - } - - thr->pause = cgpu->dynamic; - if (!cgpu->dynamic && cgpu->deven != DEV_DISABLED) - cgsem_post(&thr->sem); - } -} - -#endif /* HAVE_OPENCL */ - -#if defined(HAVE_OPENCL) && defined(HAVE_CURSES) -void manage_gpu(void) -{ - struct thr_info *thr; - int selected, gpu, i; - char checkin[40]; - char input; - - if (!opt_g_threads) - return; - - opt_loginput = true; - immedok(logwin, true); - clear_logwin(); -retry: - - for (gpu = 0; gpu < nDevs; gpu++) { - struct cgpu_info *cgpu = &gpus[gpu]; - double displayed_rolling, displayed_total; - bool mhash_base = true; - - displayed_rolling = cgpu->rolling; - displayed_total = cgpu->total_mhashes / total_secs; - if (displayed_rolling < 1) { - displayed_rolling *= 1000; - displayed_total *= 1000; - mhash_base = false; - } - - wlog("GPU %d: %.1f / %.1f %sh/s | A:%d R:%d HW:%d U:%.2f/m I:%d\n", - gpu, displayed_rolling, displayed_total, mhash_base ? "M" : "K", - cgpu->accepted, cgpu->rejected, cgpu->hw_errors, - cgpu->utility, cgpu->intensity); -#ifdef HAVE_ADL - if (gpus[gpu].has_adl) { - int engineclock = 0, memclock = 0, activity = 0, fanspeed = 0, fanpercent = 0, powertune = 0; - float temp = 0, vddc = 0; - - if (gpu_stats(gpu, &temp, &engineclock, &memclock, &vddc, &activity, &fanspeed, &fanpercent, &powertune)) { - char logline[255]; - - strcpy(logline, ""); // In case it has no data - if (temp != -1) - sprintf(logline, "%.1f C ", temp); - if (fanspeed != -1 || fanpercent != -1) { - tailsprintf(logline, sizeof(logline), "F: "); - if (fanpercent != -1) - tailsprintf(logline, sizeof(logline), "%d%% ", fanpercent); - if (fanspeed != -1) - tailsprintf(logline, sizeof(logline), "(%d RPM) ", fanspeed); - tailsprintf(logline, sizeof(logline), " "); - } - if (engineclock != -1) - tailsprintf(logline, sizeof(logline), "E: %d MHz ", engineclock); - if (memclock != -1) - tailsprintf(logline, sizeof(logline), "M: %d Mhz ", memclock); - if (vddc != -1) - tailsprintf(logline, sizeof(logline), "V: %.3fV ", vddc); - if (activity != -1) - tailsprintf(logline, sizeof(logline), "A: %d%% ", activity); - if (powertune != -1) - tailsprintf(logline, sizeof(logline), "P: %d%%", powertune); - tailsprintf(logline, sizeof(logline), "\n"); - _wlog(logline); - } - } -#endif - wlog("Last initialised: %s\n", cgpu->init); - wlog("Intensity: "); - if (gpus[gpu].dynamic) - wlog("Dynamic (only one thread in use)\n"); - else - wlog("%d\n", gpus[gpu].intensity); - for (i = 0; i < mining_threads; i++) { - thr = get_thread(i); - if (thr->cgpu != cgpu) - continue; - get_datestamp(checkin, sizeof(checkin), &thr->last); - displayed_rolling = thr->rolling; - if (!mhash_base) - displayed_rolling *= 1000; - wlog("Thread %d: %.1f %sh/s %s ", i, displayed_rolling, mhash_base ? "M" : "K" , cgpu->deven != DEV_DISABLED ? "Enabled" : "Disabled"); - switch (cgpu->status) { - default: - case LIFE_WELL: - wlog("ALIVE"); - break; - case LIFE_SICK: - wlog("SICK reported in %s", checkin); - break; - case LIFE_DEAD: - wlog("DEAD reported in %s", checkin); - break; - case LIFE_INIT: - case LIFE_NOSTART: - wlog("Never started"); - break; - } - if (thr->pause) - wlog(" paused"); - wlog("\n"); - } - wlog("\n"); - } - - wlogprint("[E]nable [D]isable [I]ntensity [R]estart GPU %s\n",adl_active ? "[C]hange settings" : ""); - - wlogprint("Or press any other key to continue\n"); - logwin_update(); - input = getch(); - - if (nDevs == 1) - selected = 0; - else - selected = -1; - if (!strncasecmp(&input, "e", 1)) { - struct cgpu_info *cgpu; - - if (selected) - selected = curses_int("Select GPU to enable"); - if (selected < 0 || selected >= nDevs) { - wlogprint("Invalid selection\n"); - goto retry; - } - if (gpus[selected].deven != DEV_DISABLED) { - wlogprint("Device already enabled\n"); - goto retry; - } - gpus[selected].deven = DEV_ENABLED; - for (i = 0; i < mining_threads; ++i) { - thr = get_thread(i); - cgpu = thr->cgpu; - if (cgpu->drv->drv_id != DRIVER_OPENCL) - continue; - if (dev_from_id(i) != selected) - continue; - if (cgpu->status != LIFE_WELL) { - wlogprint("Must restart device before enabling it"); - goto retry; - } - applog(LOG_DEBUG, "Pushing sem post to thread %d", thr->id); - - cgsem_post(&thr->sem); - } - goto retry; - } if (!strncasecmp(&input, "d", 1)) { - if (selected) - selected = curses_int("Select GPU to disable"); - if (selected < 0 || selected >= nDevs) { - wlogprint("Invalid selection\n"); - goto retry; - } - if (gpus[selected].deven == DEV_DISABLED) { - wlogprint("Device already disabled\n"); - goto retry; - } - gpus[selected].deven = DEV_DISABLED; - goto retry; - } else if (!strncasecmp(&input, "i", 1)) { - int intensity; - char *intvar; - - if (selected) - selected = curses_int("Select GPU to change intensity on"); - if (selected < 0 || selected >= nDevs) { - wlogprint("Invalid selection\n"); - goto retry; - } - if (opt_scrypt) { - intvar = curses_input("Set GPU scan intensity (d or " - MIN_SCRYPT_INTENSITY_STR " -> " - MAX_SCRYPT_INTENSITY_STR ")"); - } else { - intvar = curses_input("Set GPU scan intensity (d or " - MIN_SHA_INTENSITY_STR " -> " - MAX_SHA_INTENSITY_STR ")"); - } - if (!intvar) { - wlogprint("Invalid input\n"); - goto retry; - } - if (!strncasecmp(intvar, "d", 1)) { - wlogprint("Dynamic mode enabled on gpu %d\n", selected); - gpus[selected].dynamic = true; - pause_dynamic_threads(selected); - free(intvar); - goto retry; - } - intensity = atoi(intvar); - free(intvar); - if (intensity < MIN_INTENSITY || intensity > MAX_INTENSITY) { - wlogprint("Invalid selection\n"); - goto retry; - } - gpus[selected].dynamic = false; - gpus[selected].intensity = intensity; - wlogprint("Intensity on gpu %d set to %d\n", selected, intensity); - pause_dynamic_threads(selected); - goto retry; - } else if (!strncasecmp(&input, "r", 1)) { - if (selected) - selected = curses_int("Select GPU to attempt to restart"); - if (selected < 0 || selected >= nDevs) { - wlogprint("Invalid selection\n"); - goto retry; - } - wlogprint("Attempting to restart threads of GPU %d\n", selected); - reinit_device(&gpus[selected]); - goto retry; - } else if (adl_active && (!strncasecmp(&input, "c", 1))) { - if (selected) - selected = curses_int("Select GPU to change settings on"); - if (selected < 0 || selected >= nDevs) { - wlogprint("Invalid selection\n"); - goto retry; - } - change_gpusettings(selected); - goto retry; - } else - clear_logwin(); - - immedok(logwin, false); - opt_loginput = false; -} -#else -void manage_gpu(void) -{ -} -#endif - - -#ifdef HAVE_OPENCL -static _clState *clStates[MAX_GPUDEVICES]; - -#define CL_SET_BLKARG(blkvar) status |= clSetKernelArg(*kernel, num++, sizeof(uint), (void *)&blk->blkvar) -#define CL_SET_ARG(var) status |= clSetKernelArg(*kernel, num++, sizeof(var), (void *)&var) -#define CL_SET_VARG(args, var) status |= clSetKernelArg(*kernel, num++, args * sizeof(uint), (void *)var) - -static cl_int queue_poclbm_kernel(_clState *clState, dev_blk_ctx *blk, cl_uint threads) -{ - cl_kernel *kernel = &clState->kernel; - unsigned int num = 0; - cl_int status = 0; - - CL_SET_BLKARG(ctx_a); - CL_SET_BLKARG(ctx_b); - CL_SET_BLKARG(ctx_c); - CL_SET_BLKARG(ctx_d); - CL_SET_BLKARG(ctx_e); - CL_SET_BLKARG(ctx_f); - CL_SET_BLKARG(ctx_g); - CL_SET_BLKARG(ctx_h); - - CL_SET_BLKARG(cty_b); - CL_SET_BLKARG(cty_c); - - - CL_SET_BLKARG(cty_f); - CL_SET_BLKARG(cty_g); - CL_SET_BLKARG(cty_h); - - if (!clState->goffset) { - cl_uint vwidth = clState->vwidth; - uint *nonces = alloca(sizeof(uint) * vwidth); - unsigned int i; - - for (i = 0; i < vwidth; i++) - nonces[i] = blk->nonce + (i * threads); - CL_SET_VARG(vwidth, nonces); - } - - CL_SET_BLKARG(fW0); - CL_SET_BLKARG(fW1); - CL_SET_BLKARG(fW2); - CL_SET_BLKARG(fW3); - CL_SET_BLKARG(fW15); - CL_SET_BLKARG(fW01r); - - CL_SET_BLKARG(D1A); - CL_SET_BLKARG(C1addK5); - CL_SET_BLKARG(B1addK6); - CL_SET_BLKARG(W16addK16); - CL_SET_BLKARG(W17addK17); - CL_SET_BLKARG(PreVal4addT1); - CL_SET_BLKARG(PreVal0); - - CL_SET_ARG(clState->outputBuffer); - - return status; -} - -static cl_int queue_phatk_kernel(_clState *clState, dev_blk_ctx *blk, - __maybe_unused cl_uint threads) -{ - cl_kernel *kernel = &clState->kernel; - cl_uint vwidth = clState->vwidth; - unsigned int i, num = 0; - cl_int status = 0; - uint *nonces; - - CL_SET_BLKARG(ctx_a); - CL_SET_BLKARG(ctx_b); - CL_SET_BLKARG(ctx_c); - CL_SET_BLKARG(ctx_d); - CL_SET_BLKARG(ctx_e); - CL_SET_BLKARG(ctx_f); - CL_SET_BLKARG(ctx_g); - CL_SET_BLKARG(ctx_h); - - CL_SET_BLKARG(cty_b); - CL_SET_BLKARG(cty_c); - CL_SET_BLKARG(cty_d); - CL_SET_BLKARG(cty_f); - CL_SET_BLKARG(cty_g); - CL_SET_BLKARG(cty_h); - - nonces = alloca(sizeof(uint) * vwidth); - for (i = 0; i < vwidth; i++) - nonces[i] = blk->nonce + i; - CL_SET_VARG(vwidth, nonces); - - CL_SET_BLKARG(W16); - CL_SET_BLKARG(W17); - CL_SET_BLKARG(PreVal4_2); - CL_SET_BLKARG(PreVal0); - CL_SET_BLKARG(PreW18); - CL_SET_BLKARG(PreW19); - CL_SET_BLKARG(PreW31); - CL_SET_BLKARG(PreW32); - - CL_SET_ARG(clState->outputBuffer); - - return status; -} - -static cl_int queue_diakgcn_kernel(_clState *clState, dev_blk_ctx *blk, - __maybe_unused cl_uint threads) -{ - cl_kernel *kernel = &clState->kernel; - unsigned int num = 0; - cl_int status = 0; - - if (!clState->goffset) { - cl_uint vwidth = clState->vwidth; - uint *nonces = alloca(sizeof(uint) * vwidth); - unsigned int i; - for (i = 0; i < vwidth; i++) - nonces[i] = blk->nonce + i; - CL_SET_VARG(vwidth, nonces); - } - - CL_SET_BLKARG(PreVal0); - CL_SET_BLKARG(PreVal4_2); - CL_SET_BLKARG(cty_h); - CL_SET_BLKARG(D1A); - CL_SET_BLKARG(cty_b); - CL_SET_BLKARG(cty_c); - CL_SET_BLKARG(cty_f); - CL_SET_BLKARG(cty_g); - CL_SET_BLKARG(C1addK5); - CL_SET_BLKARG(B1addK6); - CL_SET_BLKARG(PreVal0addK7); - CL_SET_BLKARG(W16addK16); - CL_SET_BLKARG(W17addK17); - CL_SET_BLKARG(PreW18); - CL_SET_BLKARG(PreW19); - CL_SET_BLKARG(W16); - CL_SET_BLKARG(W17); - CL_SET_BLKARG(PreW31); - CL_SET_BLKARG(PreW32); - - CL_SET_BLKARG(ctx_a); - CL_SET_BLKARG(ctx_b); - CL_SET_BLKARG(ctx_c); - CL_SET_BLKARG(ctx_d); - CL_SET_BLKARG(ctx_e); - CL_SET_BLKARG(ctx_f); - CL_SET_BLKARG(ctx_g); - CL_SET_BLKARG(ctx_h); - - CL_SET_BLKARG(zeroA); - CL_SET_BLKARG(zeroB); - - CL_SET_BLKARG(oneA); - CL_SET_BLKARG(twoA); - CL_SET_BLKARG(threeA); - CL_SET_BLKARG(fourA); - CL_SET_BLKARG(fiveA); - CL_SET_BLKARG(sixA); - CL_SET_BLKARG(sevenA); - - CL_SET_ARG(clState->outputBuffer); - - return status; -} - -static cl_int queue_diablo_kernel(_clState *clState, dev_blk_ctx *blk, cl_uint threads) -{ - cl_kernel *kernel = &clState->kernel; - unsigned int num = 0; - cl_int status = 0; - - if (!clState->goffset) { - cl_uint vwidth = clState->vwidth; - uint *nonces = alloca(sizeof(uint) * vwidth); - unsigned int i; - - for (i = 0; i < vwidth; i++) - nonces[i] = blk->nonce + (i * threads); - CL_SET_VARG(vwidth, nonces); - } - - - CL_SET_BLKARG(PreVal0); - CL_SET_BLKARG(PreVal0addK7); - CL_SET_BLKARG(PreVal4addT1); - CL_SET_BLKARG(PreW18); - CL_SET_BLKARG(PreW19); - CL_SET_BLKARG(W16); - CL_SET_BLKARG(W17); - CL_SET_BLKARG(W16addK16); - CL_SET_BLKARG(W17addK17); - CL_SET_BLKARG(PreW31); - CL_SET_BLKARG(PreW32); - - CL_SET_BLKARG(D1A); - CL_SET_BLKARG(cty_b); - CL_SET_BLKARG(cty_c); - CL_SET_BLKARG(cty_h); - CL_SET_BLKARG(cty_f); - CL_SET_BLKARG(cty_g); - - CL_SET_BLKARG(C1addK5); - CL_SET_BLKARG(B1addK6); - - CL_SET_BLKARG(ctx_a); - CL_SET_BLKARG(ctx_b); - CL_SET_BLKARG(ctx_c); - CL_SET_BLKARG(ctx_d); - CL_SET_BLKARG(ctx_e); - CL_SET_BLKARG(ctx_f); - CL_SET_BLKARG(ctx_g); - CL_SET_BLKARG(ctx_h); - - CL_SET_ARG(clState->outputBuffer); - - return status; -} - -#ifdef USE_SCRYPT -static cl_int queue_scrypt_kernel(_clState *clState, dev_blk_ctx *blk, __maybe_unused cl_uint threads) -{ - unsigned char *midstate = blk->work->midstate; - cl_kernel *kernel = &clState->kernel; - unsigned int num = 0; - cl_uint le_target; - cl_int status = 0; - - le_target = *(cl_uint *)(blk->work->device_target + 28); - clState->cldata = blk->work->data; - status = clEnqueueWriteBuffer(clState->commandQueue, clState->CLbuffer0, true, 0, 80, clState->cldata, 0, NULL,NULL); - - CL_SET_ARG(clState->CLbuffer0); - CL_SET_ARG(clState->outputBuffer); - CL_SET_ARG(clState->padbuffer8); - CL_SET_VARG(4, &midstate[0]); - CL_SET_VARG(4, &midstate[16]); - CL_SET_ARG(le_target); - - return status; -} -#endif - -static void set_threads_hashes(unsigned int vectors,int64_t *hashes, size_t *globalThreads, - unsigned int minthreads, __maybe_unused int *intensity) -{ - unsigned int threads = 0; - - while (threads < minthreads) { - threads = 1 << ((opt_scrypt ? 0 : 15) + *intensity); - if (threads < minthreads) { - if (likely(*intensity < MAX_INTENSITY)) - (*intensity)++; - else - threads = minthreads; - } - } - - *globalThreads = threads; - *hashes = threads * vectors; -} -#endif /* HAVE_OPENCL */ - - -#ifdef HAVE_OPENCL -/* We have only one thread that ever re-initialises GPUs, thus if any GPU - * init command fails due to a completely wedged GPU, the thread will never - * return, unable to harm other GPUs. If it does return, it means we only had - * a soft failure and then the reinit_gpu thread is ready to tackle another - * GPU */ -void *reinit_gpu(void *userdata) -{ - struct thr_info *mythr = userdata; - struct cgpu_info *cgpu; - struct thr_info *thr; - struct timeval now; - char name[256]; - int thr_id; - int gpu; - - pthread_detach(pthread_self()); - -select_cgpu: - cgpu = tq_pop(mythr->q, NULL); - if (!cgpu) - goto out; - - if (clDevicesNum() != nDevs) { - applog(LOG_WARNING, "Hardware not reporting same number of active devices, will not attempt to restart GPU"); - goto out; - } - - gpu = cgpu->device_id; - - for (thr_id = 0; thr_id < mining_threads; ++thr_id) { - thr = get_thread(thr_id); - cgpu = thr->cgpu; - if (cgpu->drv->drv_id != DRIVER_OPENCL) - continue; - if (dev_from_id(thr_id) != gpu) - continue; - - thr = get_thread(thr_id); - if (!thr) { - applog(LOG_WARNING, "No reference to thread %d exists", thr_id); - continue; - } - - thr->rolling = thr->cgpu->rolling = 0; - /* Reports the last time we tried to revive a sick GPU */ - cgtime(&thr->sick); - if (!pthread_cancel(thr->pth)) { - applog(LOG_WARNING, "Thread %d still exists, killing it off", thr_id); - } else - applog(LOG_WARNING, "Thread %d no longer exists", thr_id); - } - - for (thr_id = 0; thr_id < mining_threads; ++thr_id) { - int virtual_gpu; - - thr = get_thread(thr_id); - cgpu = thr->cgpu; - if (cgpu->drv->drv_id != DRIVER_OPENCL) - continue; - if (dev_from_id(thr_id) != gpu) - continue; - - virtual_gpu = cgpu->virtual_gpu; - /* Lose this ram cause we may get stuck here! */ - //tq_freeze(thr->q); - - thr->q = tq_new(); - if (!thr->q) - quit(1, "Failed to tq_new in reinit_gpu"); - - /* Lose this ram cause we may dereference in the dying thread! */ - //free(clState); - - applog(LOG_INFO, "Reinit GPU thread %d", thr_id); - clStates[thr_id] = initCl(virtual_gpu, name, sizeof(name)); - if (!clStates[thr_id]) { - applog(LOG_ERR, "Failed to reinit GPU thread %d", thr_id); - goto select_cgpu; - } - applog(LOG_INFO, "initCl() finished. Found %s", name); - - if (unlikely(thr_info_create(thr, NULL, miner_thread, thr))) { - applog(LOG_ERR, "thread %d create failed", thr_id); - return NULL; - } - applog(LOG_WARNING, "Thread %d restarted", thr_id); - } - - cgtime(&now); - get_datestamp(cgpu->init, sizeof(cgpu->init), &now); - - for (thr_id = 0; thr_id < mining_threads; ++thr_id) { - thr = get_thread(thr_id); - cgpu = thr->cgpu; - if (cgpu->drv->drv_id != DRIVER_OPENCL) - continue; - if (dev_from_id(thr_id) != gpu) - continue; - - cgsem_post(&thr->sem); - } - - goto select_cgpu; -out: - return NULL; -} -#else -void *reinit_gpu(__maybe_unused void *userdata) -{ - return NULL; -} -#endif - - -#ifdef HAVE_OPENCL -static void opencl_detect() -{ - int i; - - nDevs = clDevicesNum(); - if (nDevs < 0) { - applog(LOG_ERR, "clDevicesNum returned error, no GPUs usable"); - nDevs = 0; - } - - if (!nDevs) - return; - - /* If opt_g_threads is not set, use default 1 thread on scrypt and - * 2 for regular mining */ - if (opt_g_threads == -1) { - if (opt_scrypt) - opt_g_threads = 1; - else - opt_g_threads = 2; - } - - if (opt_scrypt) - opencl_drv.max_diff = 65536; - - for (i = 0; i < nDevs; ++i) { - struct cgpu_info *cgpu; - - cgpu = &gpus[i]; - cgpu->deven = DEV_ENABLED; - cgpu->drv = &opencl_drv; - cgpu->device_id = i; - cgpu->threads = opt_g_threads; - cgpu->virtual_gpu = i; - add_cgpu(cgpu); - } - - if (!opt_noadl) - init_adl(nDevs); -} - -static void reinit_opencl_device(struct cgpu_info *gpu) -{ - tq_push(control_thr[gpur_thr_id].q, gpu); -} - -#ifdef HAVE_ADL -static void get_opencl_statline_before(char *buf, size_t bufsiz, struct cgpu_info *gpu) -{ - if (gpu->has_adl) { - int gpuid = gpu->device_id; - float gt = gpu_temp(gpuid); - int gf = gpu_fanspeed(gpuid); - int gp; - - if (gt != -1) - tailsprintf(buf, bufsiz, "%5.1fC ", gt); - else - tailsprintf(buf, bufsiz, " "); - if (gf != -1) - // show invalid as 9999 - tailsprintf(buf, bufsiz, "%4dRPM ", gf > 9999 ? 9999 : gf); - else if ((gp = gpu_fanpercent(gpuid)) != -1) - tailsprintf(buf, bufsiz, "%3d%% ", gp); - else - tailsprintf(buf, bufsiz, " "); - tailsprintf(buf, bufsiz, "| "); - } else - gpu->drv->get_statline_before = &blank_get_statline_before; -} -#endif - -static void get_opencl_statline(char *buf, size_t bufsiz, struct cgpu_info *gpu) -{ - tailsprintf(buf, bufsiz, " I:%2d", gpu->intensity); -} - -struct opencl_thread_data { - cl_int (*queue_kernel_parameters)(_clState *, dev_blk_ctx *, cl_uint); - uint32_t *res; -}; - -static uint32_t *blank_res; - -static bool opencl_thread_prepare(struct thr_info *thr) -{ - char name[256]; - struct timeval now; - struct cgpu_info *cgpu = thr->cgpu; - int gpu = cgpu->device_id; - int virtual_gpu = cgpu->virtual_gpu; - int i = thr->id; - static bool failmessage = false; - int buffersize = opt_scrypt ? SCRYPT_BUFFERSIZE : BUFFERSIZE; - - if (!blank_res) - blank_res = calloc(buffersize, 1); - if (!blank_res) { - applog(LOG_ERR, "Failed to calloc in opencl_thread_init"); - return false; - } - - strcpy(name, ""); - applog(LOG_INFO, "Init GPU thread %i GPU %i virtual GPU %i", i, gpu, virtual_gpu); - clStates[i] = initCl(virtual_gpu, name, sizeof(name)); - if (!clStates[i]) { -#ifdef HAVE_CURSES - if (use_curses) - enable_curses(); -#endif - applog(LOG_ERR, "Failed to init GPU thread %d, disabling device %d", i, gpu); - if (!failmessage) { - applog(LOG_ERR, "Restarting the GPU from the menu will not fix this."); - applog(LOG_ERR, "Try restarting cgminer."); - failmessage = true; -#ifdef HAVE_CURSES - char *buf; - if (use_curses) { - buf = curses_input("Press enter to continue"); - if (buf) - free(buf); - } -#endif - } - cgpu->deven = DEV_DISABLED; - cgpu->status = LIFE_NOSTART; - - dev_error(cgpu, REASON_DEV_NOSTART); - - return false; - } - if (!cgpu->name) - cgpu->name = strdup(name); - if (!cgpu->kname) - { - switch (clStates[i]->chosen_kernel) { - case KL_DIABLO: - cgpu->kname = "diablo"; - break; - case KL_DIAKGCN: - cgpu->kname = "diakgcn"; - break; - case KL_PHATK: - cgpu->kname = "phatk"; - break; -#ifdef USE_SCRYPT - case KL_SCRYPT: - cgpu->kname = "scrypt"; - break; -#endif - case KL_POCLBM: - cgpu->kname = "poclbm"; - break; - default: - break; - } - } - applog(LOG_INFO, "initCl() finished. Found %s", name); - cgtime(&now); - get_datestamp(cgpu->init, sizeof(cgpu->init), &now); - - have_opencl = true; - - return true; -} - -static bool opencl_thread_init(struct thr_info *thr) -{ - const int thr_id = thr->id; - struct cgpu_info *gpu = thr->cgpu; - struct opencl_thread_data *thrdata; - _clState *clState = clStates[thr_id]; - cl_int status = 0; - thrdata = calloc(1, sizeof(*thrdata)); - thr->cgpu_data = thrdata; - int buffersize = opt_scrypt ? SCRYPT_BUFFERSIZE : BUFFERSIZE; - - if (!thrdata) { - applog(LOG_ERR, "Failed to calloc in opencl_thread_init"); - return false; - } - - switch (clState->chosen_kernel) { - case KL_POCLBM: - thrdata->queue_kernel_parameters = &queue_poclbm_kernel; - break; - case KL_PHATK: - thrdata->queue_kernel_parameters = &queue_phatk_kernel; - break; - case KL_DIAKGCN: - thrdata->queue_kernel_parameters = &queue_diakgcn_kernel; - break; -#ifdef USE_SCRYPT - case KL_SCRYPT: - thrdata->queue_kernel_parameters = &queue_scrypt_kernel; - break; -#endif - default: - case KL_DIABLO: - thrdata->queue_kernel_parameters = &queue_diablo_kernel; - break; - } - - thrdata->res = calloc(buffersize, 1); - - if (!thrdata->res) { - free(thrdata); - applog(LOG_ERR, "Failed to calloc in opencl_thread_init"); - return false; - } - - status |= clEnqueueWriteBuffer(clState->commandQueue, clState->outputBuffer, CL_TRUE, 0, - buffersize, blank_res, 0, NULL, NULL); - if (unlikely(status != CL_SUCCESS)) { - applog(LOG_ERR, "Error: clEnqueueWriteBuffer failed."); - return false; - } - - gpu->status = LIFE_WELL; - - gpu->device_last_well = time(NULL); - - return true; -} - - -static bool opencl_prepare_work(struct thr_info __maybe_unused *thr, struct work *work) -{ -#ifdef USE_SCRYPT - if (opt_scrypt) - work->blk.work = work; - else -#endif - precalc_hash(&work->blk, (uint32_t *)(work->midstate), (uint32_t *)(work->data + 64)); - return true; -} - -extern int opt_dynamic_interval; - -static int64_t opencl_scanhash(struct thr_info *thr, struct work *work, - int64_t __maybe_unused max_nonce) -{ - const int thr_id = thr->id; - struct opencl_thread_data *thrdata = thr->cgpu_data; - struct cgpu_info *gpu = thr->cgpu; - _clState *clState = clStates[thr_id]; - const cl_kernel *kernel = &clState->kernel; - const int dynamic_us = opt_dynamic_interval * 1000; - - cl_int status; - size_t globalThreads[1]; - size_t localThreads[1] = { clState->wsize }; - int64_t hashes; - int found = opt_scrypt ? SCRYPT_FOUND : FOUND; - int buffersize = opt_scrypt ? SCRYPT_BUFFERSIZE : BUFFERSIZE; - - /* Windows' timer resolution is only 15ms so oversample 5x */ - if (gpu->dynamic && (++gpu->intervals * dynamic_us) > 70000) { - struct timeval tv_gpuend; - double gpu_us; - - cgtime(&tv_gpuend); - gpu_us = us_tdiff(&tv_gpuend, &gpu->tv_gpustart) / gpu->intervals; - if (gpu_us > dynamic_us) { - if (gpu->intensity > MIN_INTENSITY) - --gpu->intensity; - } else if (gpu_us < dynamic_us / 2) { - if (gpu->intensity < MAX_INTENSITY) - ++gpu->intensity; - } - memcpy(&(gpu->tv_gpustart), &tv_gpuend, sizeof(struct timeval)); - gpu->intervals = 0; - } - - set_threads_hashes(clState->vwidth, &hashes, globalThreads, localThreads[0], &gpu->intensity); - if (hashes > gpu->max_hashes) - gpu->max_hashes = hashes; - - status = thrdata->queue_kernel_parameters(clState, &work->blk, globalThreads[0]); - if (unlikely(status != CL_SUCCESS)) { - applog(LOG_ERR, "Error: clSetKernelArg of all params failed."); - return -1; - } - - if (clState->goffset) { - size_t global_work_offset[1]; - - global_work_offset[0] = work->blk.nonce; - status = clEnqueueNDRangeKernel(clState->commandQueue, *kernel, 1, global_work_offset, - globalThreads, localThreads, 0, NULL, NULL); - } else - status = clEnqueueNDRangeKernel(clState->commandQueue, *kernel, 1, NULL, - globalThreads, localThreads, 0, NULL, NULL); - if (unlikely(status != CL_SUCCESS)) { - applog(LOG_ERR, "Error %d: Enqueueing kernel onto command queue. (clEnqueueNDRangeKernel)", status); - return -1; - } - - status = clEnqueueReadBuffer(clState->commandQueue, clState->outputBuffer, CL_FALSE, 0, - buffersize, thrdata->res, 0, NULL, NULL); - if (unlikely(status != CL_SUCCESS)) { - applog(LOG_ERR, "Error: clEnqueueReadBuffer failed error %d. (clEnqueueReadBuffer)", status); - return -1; - } - - /* The amount of work scanned can fluctuate when intensity changes - * and since we do this one cycle behind, we increment the work more - * than enough to prevent repeating work */ - work->blk.nonce += gpu->max_hashes; - - /* This finish flushes the readbuffer set with CL_FALSE in clEnqueueReadBuffer */ - clFinish(clState->commandQueue); - - /* FOUND entry is used as a counter to say how many nonces exist */ - if (thrdata->res[found]) { - /* Clear the buffer again */ - status = clEnqueueWriteBuffer(clState->commandQueue, clState->outputBuffer, CL_FALSE, 0, - buffersize, blank_res, 0, NULL, NULL); - if (unlikely(status != CL_SUCCESS)) { - applog(LOG_ERR, "Error: clEnqueueWriteBuffer failed."); - return -1; - } - applog(LOG_DEBUG, "GPU %d found something?", gpu->device_id); - postcalc_hash_async(thr, work, thrdata->res); - memset(thrdata->res, 0, buffersize); - /* This finish flushes the writebuffer set with CL_FALSE in clEnqueueWriteBuffer */ - clFinish(clState->commandQueue); - } - - return hashes; -} - -static void opencl_thread_shutdown(struct thr_info *thr) -{ - const int thr_id = thr->id; - _clState *clState = clStates[thr_id]; - - clReleaseKernel(clState->kernel); - clReleaseProgram(clState->program); - clReleaseCommandQueue(clState->commandQueue); - clReleaseContext(clState->context); -} - -struct device_drv opencl_drv = { - .drv_id = DRIVER_OPENCL, - .dname = "opencl", - .name = "GPU", - .drv_detect = opencl_detect, - .reinit_device = reinit_opencl_device, -#ifdef HAVE_ADL - .get_statline_before = get_opencl_statline_before, -#endif - .get_statline = get_opencl_statline, - .thread_prepare = opencl_thread_prepare, - .thread_init = opencl_thread_init, - .prepare_work = opencl_prepare_work, - .scanhash = opencl_scanhash, - .thread_shutdown = opencl_thread_shutdown, -}; -#endif diff --git a/driver-opencl.h b/driver-opencl.h deleted file mode 100644 index 1cb88076af..0000000000 --- a/driver-opencl.h +++ /dev/null @@ -1,35 +0,0 @@ -#ifndef __DEVICE_GPU_H__ -#define __DEVICE_GPU_H__ - -#include "miner.h" - - -extern void print_ndevs(int *ndevs); -extern void *reinit_gpu(void *userdata); -extern char *set_gpu_map(char *arg); -extern char *set_gpu_engine(char *arg); -extern char *set_gpu_fan(char *arg); -extern char *set_gpu_memclock(char *arg); -extern char *set_gpu_memdiff(char *arg); -extern char *set_gpu_powertune(char *arg); -extern char *set_gpu_vddc(char *arg); -extern char *set_temp_overheat(char *arg); -extern char *set_temp_target(char *arg); -extern char *set_intensity(char *arg); -extern char *set_vector(char *arg); -extern char *set_worksize(char *arg); -#ifdef USE_SCRYPT -extern char *set_shaders(char *arg); -extern char *set_lookup_gap(char *arg); -extern char *set_thread_concurrency(char *arg); -#endif -extern char *set_kernel(char *arg); -void manage_gpu(void); -extern void pause_dynamic_threads(int gpu); - -extern bool have_opencl; -extern int opt_platform_id; - -extern struct device_drv opencl_drv; - -#endif /* __DEVICE_GPU_H__ */ diff --git a/driver-spondoolies-sp10-p.c b/driver-spondoolies-sp10-p.c new file mode 100644 index 0000000000..c37e171b1a --- /dev/null +++ b/driver-spondoolies-sp10-p.c @@ -0,0 +1,44 @@ +/* + * Copyright 2014 Con Kolivas + * Copyright 2014 Zvi (Zvisha) Shteingart - Spondoolies-tech.com + * + * 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. + * + * Note that changing this SW will void your miners guaranty + */ + +/* + This file holds functions needed for minergate packet parsing/creation + by Zvisha Shteingart +*/ + +#include "driver-spondoolies-sp10-p.h" +#include "assert.h" +//#include "spond_debug.h" + +minergate_req_packet *allocate_minergate_packet_req(uint8_t requester_id, uint8_t request_id) +{ + minergate_req_packet *p = (minergate_req_packet*)malloc(sizeof(minergate_req_packet)); + p->requester_id = requester_id; + p->req_count = 0; + p->protocol_version = MINERGATE_PROTOCOL_VERSION; + p->request_id = request_id; + p->magic = 0xcaf4; + p->mask |= 0x01; // first packet + return p; +} + +minergate_rsp_packet *allocate_minergate_packet_rsp(uint8_t requester_id, uint8_t request_id) +{ + minergate_rsp_packet *p = (minergate_rsp_packet*)malloc(sizeof(minergate_rsp_packet)); + p->requester_id = requester_id; + p->rsp_count = 0; + p->protocol_version = MINERGATE_PROTOCOL_VERSION; + p->request_id = request_id; + p->magic = 0xcaf4; + p->gh_div_10_rate = 0; + return p; +} diff --git a/driver-spondoolies-sp10-p.h b/driver-spondoolies-sp10-p.h new file mode 100644 index 0000000000..1476bc292b --- /dev/null +++ b/driver-spondoolies-sp10-p.h @@ -0,0 +1,92 @@ +/* + * Copyright 2014 Con Kolivas + * Copyright 2014 Zvi (Zvisha) Shteingart - Spondoolies-tech.com + * + * 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. + * + * Note that changing this SW will void your miners guaranty + */ + +#ifndef ____MINERGATE_LIB_H___ +#define ____MINERGATE_LIB_H___ + +//#include "squid.h" +#include +#include +#include +#include +#include +#include +#include +#include +//#include "queue.h" +//#include "spond_debug.h" + +#ifndef passert +#define passert assert +#endif + +#define MINERGATE_PROTOCOL_VERSION 6 +#define MINERGATE_SOCKET_FILE "/tmp/connection_pipe" + +typedef enum { + //MINERGATE_DATA_ID_CONNECT = 1, + MINERGATE_DATA_ID_DO_JOB_REQ = 2, + MINERGATE_DATA_ID_DO_JOB_RSP = 3, + +} MINERGATE_DATA_ID; + +typedef struct { + uint32_t work_id_in_sw; + uint32_t difficulty; + uint32_t timestamp; + uint32_t mrkle_root; + uint32_t midstate[8]; + uint8_t leading_zeroes; + uint8_t ntime_limit; + uint8_t ntime_offset; + uint8_t resr1; +} minergate_do_job_req; + +#define MAX_REQUESTS 100 +#define MAX_RESPONDS 300 +#define MINERGATE_TOTAL_QUEUE 300 + +typedef struct { + uint32_t work_id_in_sw; + uint32_t mrkle_root; // to validate + uint32_t winner_nonce[2]; + uint8_t ntime_offset; + uint8_t res; // 0 = done, 1 = overflow, 2 = dropped bist + uint8_t resrv1; + uint8_t resrv2; +} minergate_do_job_rsp; + + +typedef struct { + uint8_t requester_id; + uint8_t request_id; + uint8_t protocol_version; + uint8_t mask; // 0x01 = first request, 0x2 = drop old work + uint16_t magic; // 0xcafe + uint16_t req_count; + minergate_do_job_req req[MAX_REQUESTS]; // array of requests +} minergate_req_packet; + +typedef struct { + uint8_t requester_id; + uint8_t request_id; + uint8_t protocol_version; + uint8_t gh_div_10_rate; // == + uint16_t magic; // 0xcafe + uint16_t rsp_count; + minergate_do_job_rsp rsp[MAX_RESPONDS]; // array of responce +} minergate_rsp_packet; + +minergate_req_packet *allocate_minergate_packet_req(uint8_t requester_id, uint8_t request_id); +minergate_rsp_packet *allocate_minergate_packet_rsp(uint8_t requester_id, uint8_t request_id); + +#endif diff --git a/driver-spondoolies-sp10.c b/driver-spondoolies-sp10.c new file mode 100644 index 0000000000..3ffab8d87c --- /dev/null +++ b/driver-spondoolies-sp10.c @@ -0,0 +1,446 @@ +/* + * Copyright 2014 Con Kolivas + * Copyright 2014 Zvi (Zvisha) Shteingart - Spondoolies-tech.com + * + * 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. + */ + +/* + This driver communicates the job requests via Unix socket to the minergate + process, that is responsible for controlling the Spondoolies Dawson SP10 miner. + + The jobs sent each with unique ID and returned asynchronously in one of the next + transactions. REQUEST_PERIOD and REQUEST_SIZE define the communication rate with minergate. +*/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "config.h" + +#ifdef WIN32 +#include +#endif + +#include "compat.h" +#include "miner.h" +#include "driver-spondoolies-sp10-p.h" +#include "driver-spondoolies-sp10.h" + +#ifdef WORDS_BIGENDIAN +# define swap32tobe(out, in, sz) ((out == in) ? (void)0 : memmove(out, in, sz)) +# define LOCAL_swap32be(type, var, sz) ; +# define swap32tole(out, in, sz) swap32yes(out, in, sz) +# define LOCAL_swap32le(type, var, sz) LOCAL_swap32(type, var, sz) +#else +# define swap32tobe(out, in, sz) swap32yes(out, in, sz) +# define LOCAL_swap32be(type, var, sz) LOCAL_swap32(type, var, sz) +# define swap32tole(out, in, sz) ((out == in) ? (void)0 : memmove(out, in, sz)) +# define LOCAL_swap32le(type, var, sz) ; +#endif + +static inline void swap32yes(void *out, const void *in, size_t sz) +{ + size_t swapcounter; + + for (swapcounter = 0; swapcounter < sz; ++swapcounter) + (((uint32_t*)out)[swapcounter]) = swab32(((uint32_t*)in)[swapcounter]); +} + +static void send_minergate_pkt(const minergate_req_packet* mp_req, minergate_rsp_packet* mp_rsp, + int socket_fd) +{ + int nbytes, nwrote, nread; + + nbytes = sizeof(minergate_req_packet); + nwrote = write(socket_fd, (const void *)mp_req, nbytes); + if (unlikely(nwrote != nbytes)) + _quit(-1); + nbytes = sizeof(minergate_rsp_packet); + nread = read(socket_fd, (void *)mp_rsp, nbytes); + if (unlikely(nread != nbytes)) + _quit(-1); + passert(mp_rsp->magic == 0xcaf4); +} + +static bool spondoolies_prepare(struct thr_info *thr) +{ + struct cgpu_info *spondoolies = thr->cgpu; + struct timeval now; + + assert(spondoolies); + cgtime(&now); + /* FIXME: Vladik */ +#if NEED_FIX + get_datestamp(spondoolies->init, &now); +#endif + return true; +} + +static int init_socket(void) +{ + int socket_fd = socket(PF_UNIX, SOCK_STREAM, 0); + struct sockaddr_un address; + + if (socket_fd < 0) { + printf("socket() failed\n"); + perror("Err:"); + return 0; + } + + /* start with a clean address structure */ + memset(&address, 0, sizeof(struct sockaddr_un)); + + address.sun_family = AF_UNIX; + sprintf(address.sun_path, MINERGATE_SOCKET_FILE); + + if(connect(socket_fd, (struct sockaddr *) &address, sizeof(struct sockaddr_un))) { + printf("connect() failed\n"); + perror("Err:"); + return 0; + } + + return socket_fd; +} + +static bool spondoolies_flush_queue(struct spond_adapter* a, bool flush_queue) +{ + if (!a->parse_resp) { + static int i = 0; + + if (i++ % 10 == 0 && a->works_in_minergate_and_pending_tx + a->works_pending_tx != a->works_in_driver) + printf("%d + %d != %d\n", a->works_in_minergate_and_pending_tx, a->works_pending_tx,a->works_in_driver); + assert(a->works_in_minergate_and_pending_tx + a->works_pending_tx == a->works_in_driver); + send_minergate_pkt(a->mp_next_req, a->mp_last_rsp, a->socket_fd); + if (flush_queue) + a->mp_next_req->mask |= 0x02; + else + a->mp_next_req->mask &= ~0x02; + + a->mp_next_req->req_count = 0; + a->parse_resp = 1; + a->works_in_minergate_and_pending_tx += a->works_pending_tx; + a->works_pending_tx = 0; + } + return true; +} + +static void spondoolies_detect(__maybe_unused bool hotplug) +{ + struct cgpu_info *cgpu = calloc(1, sizeof(*cgpu)); + struct device_drv *drv = &sp10_drv; + struct spond_adapter *a; + +#if NEED_FIX + nDevs = 1; +#endif + + assert(cgpu); + cgpu->drv = drv; + cgpu->deven = DEV_ENABLED; + cgpu->threads = 1; + cgpu->device_data = calloc(sizeof(struct spond_adapter), 1); + if (unlikely(!(cgpu->device_data))) + quit(1, "Failed to calloc cgpu_info data"); + a = cgpu->device_data; + a->cgpu = (void *)cgpu; + a->adapter_state = ADAPTER_STATE_OPERATIONAL; + a->mp_next_req = allocate_minergate_packet_req(0xca, 0xfe); + a->mp_last_rsp = allocate_minergate_packet_rsp(0xca, 0xfe); + + pthread_mutex_init(&a->lock, NULL); + a->socket_fd = init_socket(); + if (a->socket_fd < 1) { + printf("Error connecting to minergate server!"); + _quit(-1); + } + + assert(add_cgpu(cgpu)); + // Clean MG socket + spondoolies_flush_queue(a, true); + spondoolies_flush_queue(a, true); + spondoolies_flush_queue(a, true); + applog(LOG_DEBUG, "SPOND spondoolies_detect done"); +} + +static struct api_data *spondoolies_api_stats(struct cgpu_info *cgpu) +{ + struct spond_adapter *a = cgpu->device_data; + struct api_data *root = NULL; + + root = api_add_int(root, "ASICs total rate", &a->temp_rate, false); + root = api_add_int(root, "Temperature front", &a->front_temp, false); + root = api_add_int(root, "Temperature rear top", &a->rear_temp_top, false); + root = api_add_int(root, "Temperature rear bot", &a->rear_temp_bot, false); + + return root; +} + +#if 0 +static unsigned char get_leading_zeroes(const unsigned char *target) +{ + unsigned char leading = 0; + int first_non_zero_chr; + uint8_t m; + + for (first_non_zero_chr = 31; first_non_zero_chr >= 0; first_non_zero_chr--) { + if (target[first_non_zero_chr] == 0) + leading += 8; + else + break; + } + + // j = first non-zero + m = target[first_non_zero_chr]; + while ((m & 0x80) == 0) { + leading++; + m = m << 1; + } + return leading; +} +#endif + +static void spondoolies_shutdown(__maybe_unused struct thr_info *thr) +{ +} + +static void fill_minergate_request(minergate_do_job_req* work, struct work *cg_work, + int ntime_offset) +{ + uint32_t x[64/4]; + uint64_t wd; + + memset(work, 0, sizeof(minergate_do_job_req)); + //work-> + LOCAL_swap32le(unsigned char, cg_work->midstate, 32/4) + LOCAL_swap32le(unsigned char, cg_work->data+64, 64/4) + swap32yes(x, cg_work->data + 64, 64/4); + memcpy(work->midstate, cg_work->midstate, 32); + work->mrkle_root = ntohl(x[0]); + work->timestamp = ntohl(x[1]); + work->difficulty = ntohl(x[2]); + //work->leading_zeroes = get_leading_zeroes(cg_work->target); + // Is there no better way to get leading zeroes? + work->leading_zeroes = 30; + wd = round(cg_work->work_difficulty); + while (wd) { + work->leading_zeroes++; + wd = wd >> 1; + } + //printf("%d %d\n",work->leading_zeroes, (int)round(cg_work->work_difficulty)); + work->work_id_in_sw = cg_work->subid; + work->ntime_limit = 0; + work->ntime_offset = ntime_offset; +} + +// returns true if queue full. +static struct timeval last_force_queue = {0}; + +static bool spondoolies_queue_full(struct cgpu_info *cgpu) +{ + // Only once every 1/10 second do work. + struct spond_adapter* a = cgpu->device_data; + int next_job_id, ntime_clones, i; + struct timeval tv; + struct work *work; + unsigned int usec; + bool ret = false; + + mutex_lock(&a->lock); + passert(a->works_pending_tx <= REQUEST_SIZE); + + gettimeofday(&tv, NULL); + + usec = (tv.tv_sec-last_force_queue.tv_sec) * 1000000; + usec += (tv.tv_usec-last_force_queue.tv_usec); + + if ((usec >= REQUEST_PERIOD) || (a->reset_mg_queue == 2) || + ((a->reset_mg_queue == 1) && (a->works_pending_tx == REQUEST_SIZE))) { + spondoolies_flush_queue(a, (a->reset_mg_queue == 2)); + if (a->reset_mg_queue) + a->reset_mg_queue--; + last_force_queue = tv; + } + + // see if we have enough jobs + if (a->works_pending_tx == REQUEST_SIZE) { + ret = true; + goto return_unlock; + } + + // see if can take 1 more job. + next_job_id = (a->current_job_id + 1) % MAX_JOBS_IN_MINERGATE; + if (a->my_jobs[next_job_id].cgminer_work) { + ret = true; + goto return_unlock; + } + work = get_queued(cgpu); + if (!work) { + cgsleep_ms(10); + goto return_unlock; + } + + work->thr = cgpu->thr[0]; + work->thr_id = cgpu->thr[0]->id; + assert(work->thr); + + // Create 5 works using ntime increment + a->current_job_id = next_job_id; + work->subid = a->current_job_id; + // Get pointer for the request + a->my_jobs[a->current_job_id].cgminer_work = work; + a->my_jobs[a->current_job_id].state = SPONDWORK_STATE_IN_BUSY; + a->my_jobs[a->current_job_id].ntime_clones = 0; + + ntime_clones = (work->drv_rolllimit < MAX_NROLES) ? work->drv_rolllimit : MAX_NROLES; + for (i = 0 ; (i < ntime_clones) && (a->works_pending_tx < REQUEST_SIZE) ; i++) { + minergate_do_job_req* pkt_job = &a->mp_next_req->req[a->works_pending_tx]; + fill_minergate_request(pkt_job, work, i); + a->works_in_driver++; + a->works_pending_tx++; + a->mp_next_req->req_count++; + a->my_jobs[a->current_job_id].merkle_root = pkt_job->mrkle_root; + a->my_jobs[a->current_job_id].ntime_clones++; + } + +return_unlock: + mutex_unlock(&a->lock); + + return ret; +} + +static void spond_poll_stats(struct cgpu_info *spond, struct spond_adapter *a) +{ + FILE *fp = fopen("/var/run/mg_rate_temp", "r"); + + if (!fp) { + applog(LOG_DEBUG, "SPOND unable to open mg_rate_temp"); + a->temp_rate = a->front_temp = a->rear_temp_top = a->rear_temp_bot = 0; + } else { + int ret = fscanf(fp, "%d %d %d %d", &a->temp_rate, &a->front_temp , &a->rear_temp_top , &a->rear_temp_bot); + + if (ret != 4) + a->temp_rate = a->front_temp = a->rear_temp_top = a->rear_temp_bot = 0; + fclose(fp); + } + applog(LOG_DEBUG, "SPOND poll_stats rate: %d front: %d rear(T/B): %d/%d", + a->temp_rate, a->front_temp , a->rear_temp_top, a->rear_temp_bot); + /* Use the rear temperature as the dev temperature for now */ + spond->temp = (a->rear_temp_top + a->rear_temp_bot)/2; +} + +// Return completed work to submit_nonce() and work_completed() +// struct timeval last_force_queue = {0}; +static int64_t spond_scanhash(struct thr_info *thr) +{ + struct cgpu_info *cgpu = thr->cgpu; + struct spond_adapter *a = cgpu->device_data; + int64_t ghashes = 0; + cgtimer_t cgt; + time_t now_t; + + cgsleep_prepare_r(&cgt); + now_t = time(NULL); + /* Poll stats only once per second */ + if (now_t != a->last_stats) { + a->last_stats = now_t; + spond_poll_stats(cgpu, a); + } + + if (a->parse_resp) { + int array_size, i, j; + + mutex_lock(&a->lock); + ghashes = (a->mp_last_rsp->gh_div_10_rate); + ghashes = ghashes * 10000 * REQUEST_PERIOD; + array_size = a->mp_last_rsp->rsp_count; + for (i = 0; i < array_size; i++) { // walk the jobs + int job_id; + + minergate_do_job_rsp* work = a->mp_last_rsp->rsp + i; + job_id = work->work_id_in_sw; + if ((a->my_jobs[job_id].cgminer_work)) { + if (a->my_jobs[job_id].merkle_root == work->mrkle_root) { + assert(a->my_jobs[job_id].state == SPONDWORK_STATE_IN_BUSY); + a->works_in_minergate_and_pending_tx--; + a->works_in_driver--; + for (j = 0; j < 2; j++) { + if (work->winner_nonce[j]) { + bool __maybe_unused ok; + struct work *cg_work = a->my_jobs[job_id].cgminer_work; +#ifndef SP_NTIME + ok = submit_nonce(cg_work->thr, cg_work, work->winner_nonce[j]); +#else + ok = submit_noffset_nonce(cg_work->thr, cg_work, work->winner_nonce[j], work->ntime_offset); +#endif + //printf("OK on %d:%d = %d\n",work->work_id_in_sw,j, ok); + a->wins++; + } + } + //printf("%d ntime_clones = %d\n",job_id,a->my_jobs[job_id].ntime_clones); + if ((--a->my_jobs[job_id].ntime_clones) == 0) { + //printf("Done with %d\n", job_id); + work_completed(a->cgpu, a->my_jobs[job_id].cgminer_work); + a->good++; + a->my_jobs[job_id].cgminer_work = NULL; + a->my_jobs[job_id].state = SPONDWORK_STATE_EMPTY; + } + } else { + a->bad++; + printf("Dropping minergate old job id=%d mrkl=%x my-mrkl=%x\n", + job_id, a->my_jobs[job_id].merkle_root, work->mrkle_root); + } + } else { + a->empty++; + printf("No cgminer job (id:%d res:%d)!\n",job_id, work->res); + } + } + mutex_unlock(&a->lock); + + a->parse_resp = 0; + } + cgsleep_ms_r(&cgt, 40); + + return ghashes; +} + +// Remove all work from queue +static void spond_flush_work(struct cgpu_info *cgpu) +{ + struct spond_adapter *a = cgpu->device_data; + + mutex_lock(&a->lock); + a->reset_mg_queue = 2; + mutex_unlock(&a->lock); +} + +struct device_drv sp10_drv = { + .drv_id = DRIVER_sp10, + .dname = "Spondoolies", + .name = "SPN", + .max_diff = 64.0, // Limit max diff to get some nonces back regardless + .drv_detect = spondoolies_detect, + .get_api_stats = spondoolies_api_stats, + .thread_prepare = spondoolies_prepare, + .thread_shutdown = spondoolies_shutdown, + .hash_work = hash_queued_work, + .queue_full = spondoolies_queue_full, + .scanwork = spond_scanhash, + .flush_work = spond_flush_work, +}; diff --git a/driver-spondoolies-sp10.h b/driver-spondoolies-sp10.h new file mode 100644 index 0000000000..b99fe21c79 --- /dev/null +++ b/driver-spondoolies-sp10.h @@ -0,0 +1,84 @@ +/* + * Copyright 2014 Con Kolivas + * Copyright 2014 Zvi Shteingart - Spondoolies-tech.com + * + * 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 SPONDA_HFILE +#define SPONDA_HFILE + +#include "miner.h" +#include "driver-spondoolies-sp10-p.h" + + +#define SP_NTIME + +typedef enum adapter_state { + ADAPTER_STATE_INIT, + ADAPTER_STATE_OPERATIONAL, +} ADAPTER_STATE; + +typedef enum spond_work_state { + SPONDWORK_STATE_EMPTY, + SPONDWORK_STATE_IN_BUSY, +} SPONDWORK_STATE; + +#define MAX_JOBS_IN_MINERGATE MINERGATE_TOTAL_QUEUE // 1.5 sec worth of jobs +#define MAX_NROLES 50 + +typedef struct { + struct work *cgminer_work; + SPONDWORK_STATE state; + uint32_t merkle_root; + time_t start_time; + int job_id[MAX_NROLES]; + int ntime_clones; +} spond_driver_work; + +struct spond_adapter { + pthread_mutex_t lock; + ADAPTER_STATE adapter_state; + void *cgpu; + + // Statistics + int wins; + int good; + int empty; + int bad; + int overflow; + // state + int works_in_driver; + int works_in_minergate_and_pending_tx; + int works_pending_tx; + int socket_fd; + int reset_mg_queue; // 2=reset, 1=fast send, 0=nada + int current_job_id; + int parse_resp; + minergate_req_packet* mp_next_req; + minergate_rsp_packet* mp_last_rsp; + spond_driver_work my_jobs[MAX_JOBS_IN_MINERGATE]; + + // Temperature statistics + int temp_rate; + int front_temp; + int rear_temp_top; + int rear_temp_bot; + + // Last second we polled stats + time_t last_stats; +}; + +// returns non-zero if needs to change ASICs. +int spond_one_sec_timer_scaling(struct spond_adapter *a, int t); +int spond_do_scaling(struct spond_adapter *a); + +extern void one_sec_spondoolies_watchdog(int uptime); + +#define REQUEST_PERIOD (100000) // times per second - in usec +#define REQUEST_SIZE 100 // jobs per request + +#endif diff --git a/driver-spondoolies-sp30-p.c b/driver-spondoolies-sp30-p.c new file mode 100644 index 0000000000..ecc89162a8 --- /dev/null +++ b/driver-spondoolies-sp30-p.c @@ -0,0 +1,47 @@ +/* + * Copyright 2014 Zvi (Zvisha) Shteingart - Spondoolies-tech.com + * + * 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. + */ + +/* + This file holds functions needed for minergate packet parsing/creation +*/ + +#include "driver-spondoolies-sp30-p.h" +#include "assert.h" +//#include "spond_debug.h" + +#ifndef passert +#define passert assert +#endif + +minergate_req_packet_sp30 *allocate_minergate_packet_req_sp30(uint8_t requester_id, + uint8_t request_id) { + minergate_req_packet_sp30 *p = + (minergate_req_packet_sp30 *)malloc(sizeof(minergate_req_packet_sp30)); + p->requester_id = requester_id; + p->req_count = 0; + p->protocol_version = MINERGATE_PROTOCOL_VERSION_SP30; + p->request_id = request_id; + p->magic = 0xcaf4; + p->mask = 0; + return p; +} + +minergate_rsp_packet_sp30 *allocate_minergate_packet_rsp_sp30(uint8_t requester_id, + uint8_t request_id) { + + minergate_rsp_packet_sp30 *p = + (minergate_rsp_packet_sp30 *)malloc(sizeof(minergate_rsp_packet_sp30)); + p->requester_id = requester_id; + p->rsp_count = 0; + p->protocol_version = MINERGATE_PROTOCOL_VERSION_SP30; + p->request_id = request_id; + p->magic = 0xcaf4; + p->gh_div_50_rate= 0; + return p; +} diff --git a/driver-spondoolies-sp30-p.h b/driver-spondoolies-sp30-p.h new file mode 100644 index 0000000000..27f755bb69 --- /dev/null +++ b/driver-spondoolies-sp30-p.h @@ -0,0 +1,83 @@ +/* + * Copyright 2014 Zvi (Zvisha) Shteingart - Spondoolies-tech.com + * + * 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. + * + * Note that changing this SW will void your miners guaranty + */ + + +#ifndef ____MINERGATE_LIB30_H___ +#define ____MINERGATE_LIB30_H___ + +#include +#include +#include +#include +#include +#include +#include +#include + + +#define MINERGATE_PROTOCOL_VERSION_SP30 30 +#define MINERGATE_SOCKET_FILE_SP30 "/tmp/connection_pipe_sp30" + +typedef enum { + MINERGATE_DATA_ID_DO_JOB_REQ_SP30 = 5, + MINERGATE_DATA_ID_DO_JOB_RSP_SP30 = 6, +} MINERGATE_DATA_ID_SP30; + +typedef struct { + uint32_t work_id_in_sw; + uint32_t difficulty; + uint32_t timestamp; + uint32_t mrkle_root; + uint32_t midstate[8]; + uint8_t leading_zeroes; + uint8_t ntime_limit; // max ntime - should be 60 + uint8_t resr2; + uint8_t resr1; +} minergate_do_job_req_sp30; + +#define MAX_REQUESTS_SP30 30 +#define MAX_RESPONDS_SP30 60 +#define MINERGATE_ADAPTER_QUEUE_SP30 40 + +typedef struct { + uint32_t work_id_in_sw; + uint32_t mrkle_root; // to validate + uint32_t winner_nonce; + uint8_t ntime_offset; + uint8_t res; // 0 = done, 1 = overflow, 2 = dropped bist + uint8_t job_complete; + uint8_t resrv2; +} minergate_do_job_rsp_sp30; + +typedef struct { + uint8_t requester_id; + uint8_t request_id; + uint8_t protocol_version; + uint8_t mask; // 0x01 = first request, 0x2 = drop old work + uint16_t magic; // 0xcaf4 + uint16_t req_count; + minergate_do_job_req_sp30 req[MAX_REQUESTS_SP30]; // array of requests +} minergate_req_packet_sp30; + +typedef struct { + uint8_t requester_id; + uint8_t request_id; + uint8_t protocol_version; + uint8_t gh_div_50_rate; + uint16_t magic; // 0xcaf4 + uint16_t rsp_count; + minergate_do_job_rsp_sp30 rsp[MAX_RESPONDS_SP30]; // array of responces +} minergate_rsp_packet_sp30; + +minergate_req_packet_sp30* allocate_minergate_packet_req_sp30(uint8_t requester_id,uint8_t request_id); +minergate_rsp_packet_sp30* allocate_minergate_packet_rsp_sp30(uint8_t requester_id,uint8_t request_id); + +#endif diff --git a/driver-spondoolies-sp30.c b/driver-spondoolies-sp30.c new file mode 100644 index 0000000000..f7664d3c86 --- /dev/null +++ b/driver-spondoolies-sp30.c @@ -0,0 +1,489 @@ +/* + * Copyright 2014 Con Kolivas + * Copyright 2014 Zvi (Zvisha) Shteingart - Spondoolies-tech.com + * + * 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. + */ + +/* + This driver communicates the job requests via Unix socket to the minergate + process, that is responsible for controlling the Spondoolies Dawson SP10 miner. + + The jobs sent each with unique ID and returned asynchronously in one of the next + transactions. REQUEST_PERIOD and REQUEST_SIZE define the communication rate with minergate. +*/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "config.h" + +#ifdef WIN32 +#include +#endif + +#include "compat.h" +#include "miner.h" +#include "driver-spondoolies-sp30-p.h" +#include "driver-spondoolies-sp30.h" + +#ifdef WORDS_BIGENDIAN +# define swap32tobe(out, in, sz) ((out == in) ? (void)0 : memmove(out, in, sz)) +# define LOCAL_swap32be(type, var, sz) ; +# define swap32tole(out, in, sz) swap32yes(out, in, sz) +# define LOCAL_swap32le(type, var, sz) LOCAL_swap32(type, var, sz) +#else +# define swap32tobe(out, in, sz) swap32yes(out, in, sz) +# define LOCAL_swap32be(type, var, sz) LOCAL_swap32(type, var, sz) +# define swap32tole(out, in, sz) ((out == in) ? (void)0 : memmove(out, in, sz)) +# define LOCAL_swap32le(type, var, sz) ; +#endif + +static inline void swap32yes(void *out, const void *in, size_t sz) +{ + size_t swapcounter; + + for (swapcounter = 0; swapcounter < sz; ++swapcounter) + (((uint32_t*)out)[swapcounter]) = swab32(((uint32_t*)in)[swapcounter]); +} + +static void send_minergate_pkt(const minergate_req_packet_sp30* mp_req, minergate_rsp_packet_sp30* mp_rsp, + int socket_fd) +{ + int nbytes, nwrote, nread; + + nbytes = sizeof(minergate_req_packet_sp30); + nwrote = write(socket_fd, (const void *)mp_req, nbytes); + if (unlikely(nwrote != nbytes)) + _quit(-1); + nbytes = sizeof(minergate_rsp_packet_sp30); + nread = read(socket_fd, (void *)mp_rsp, nbytes); + if (unlikely(nread != nbytes)) + _quit(-1); + assert(mp_rsp->magic == 0xcaf4); +} + +static bool spondoolies_prepare_sp30(struct thr_info *thr) +{ + struct cgpu_info *spondoolies_sp30 = thr->cgpu; + struct timeval now; + + assert(spondoolies_sp30); + cgtime(&now); + /* FIXME: Vladik */ +#if NEED_FIX + get_datestamp(spondoolies_sp30->init, &now); +#endif + return true; +} + +static int init_socket(void) +{ + int socket_fd; + struct sockaddr_un address; + + printf("Init\n"); + socket_fd = socket(PF_UNIX, SOCK_STREAM, 0); + if (socket_fd < 0) { + printf("socket() failed\n"); + perror("Err:"); + return 0; + } + + /* start with a clean address structure */ + memset(&address, 0, sizeof(struct sockaddr_un)); + + address.sun_family = AF_UNIX; + sprintf(address.sun_path, MINERGATE_SOCKET_FILE_SP30); + + if (connect(socket_fd, (struct sockaddr *) &address, sizeof(struct sockaddr_un))) { + printf("connect() failed\n"); + perror("Err:"); + return 0; + } + + return socket_fd; +} + +static bool spondoolies_flush_queue(struct spond_adapter* a, bool flush_queue) +{ + if (!a->parse_resp) { + static int i = 0; + + if (i++ % 10 == 0 && a->works_in_minergate_and_pending_tx + a->works_pending_tx != a->works_in_driver) + printf("%d + %d != %d\n", a->works_in_minergate_and_pending_tx, a->works_pending_tx,a->works_in_driver); + assert(a->works_in_minergate_and_pending_tx + a->works_pending_tx == a->works_in_driver); + send_minergate_pkt(a->mp_next_req, a->mp_last_rsp, a->socket_fd); + if (flush_queue) { + printf("FLUSH!\n"); + a->mp_next_req->mask |= 0x02; + } else { + a->mp_next_req->mask &= ~0x02; + } + + a->mp_next_req->req_count = 0; + a->parse_resp = 1; + a->works_in_minergate_and_pending_tx += a->works_pending_tx; + a->works_pending_tx = 0; + } + return true; +} + +static void spondoolies_detect_sp30(__maybe_unused bool hotplug) +{ + struct cgpu_info *cgpu = calloc(1, sizeof(*cgpu)); + struct device_drv *drv = &sp30_drv; + struct spond_adapter *a; + +#if NEED_FIX + nDevs = 1; +#endif + + assert(cgpu); + cgpu->drv = drv; + cgpu->deven = DEV_ENABLED; + cgpu->threads = 1; + cgpu->device_data = calloc(sizeof(struct spond_adapter), 1); + if (unlikely(!(cgpu->device_data))) + quit(1, "Failed to calloc cgpu_info data"); + a = cgpu->device_data; + a->cgpu = (void *)cgpu; + a->adapter_state = ADAPTER_STATE_OPERATIONAL; + a->mp_next_req = allocate_minergate_packet_req_sp30(0xca, 0xfe); + a->mp_last_rsp = allocate_minergate_packet_rsp_sp30(0xca, 0xfe); + + pthread_mutex_init(&a->lock, NULL); + a->socket_fd = init_socket(); + if (a->socket_fd < 1) { + printf("Error connecting to minergate server!"); + _quit(-1); + } + + assert(add_cgpu(cgpu)); + // Clean MG socket + spondoolies_flush_queue(a, true); + spondoolies_flush_queue(a, true); + spondoolies_flush_queue(a, true); + applog(LOG_DEBUG, "SPOND spondoolies_detect_sp30 done"); +} + +static struct api_data *spondoolies_api_stats_sp30(struct cgpu_info *cgpu) +{ + struct spond_adapter *a = cgpu->device_data; + struct api_data *root = NULL; + + root = api_add_int(root, "ASICs total rate", &a->temp_rate, false); + root = api_add_int(root, "Temperature front", &a->front_temp, false); + root = api_add_int(root, "Temperature rear top", &a->rear_temp_top, false); + root = api_add_int(root, "Temperature rear bot", &a->rear_temp_bot, false); + + + + return root; +} + +#if 0 +static unsigned char get_leading_zeroes(const unsigned char *target) +{ + unsigned char leading = 0; + int first_non_zero_chr; + uint8_t m; + + for (first_non_zero_chr = 31; first_non_zero_chr >= 0; first_non_zero_chr--) { + if (target[first_non_zero_chr] == 0) + leading += 8; + else + break; + } + + // j = first non-zero + m = target[first_non_zero_chr]; + while ((m & 0x80) == 0) { + leading++; + m = m << 1; + } + return leading; +} +#endif + +static void spondoolies_shutdown_sp30(__maybe_unused struct thr_info *thr) +{ +} + +static void fill_minergate_request(minergate_do_job_req_sp30* work, struct work *cg_work, int max_offset) +{ + uint32_t x[64 / 4]; + uint64_t wd; + + memset(work, 0, sizeof(minergate_do_job_req_sp30)); + //work-> + LOCAL_swap32le(unsigned char, cg_work->midstate, 32 / 4) + LOCAL_swap32le(unsigned char, cg_work->data + 64, 64 / 4) + swap32yes(x, cg_work->data + 64, 64 / 4); + memcpy(work->midstate, cg_work->midstate, 32); + work->mrkle_root = ntohl(x[0]); + work->timestamp = ntohl(x[1]); + work->difficulty = ntohl(x[2]); + //work->leading_zeroes = get_leading_zeroes(cg_work->target); + // Is there no better way to get leading zeroes? + work->leading_zeroes = 31; + wd = round(cg_work->device_diff); + while (wd) { + work->leading_zeroes++; + wd = wd >> 1; + } + //printf("%d %d\n",work->leading_zeroes, (int)round(cg_work->work_difficulty)); + work->work_id_in_sw = cg_work->subid; + work->ntime_limit = max_offset; + //printf("ID:%d, TS:%x\n",work->work_id_in_sw,work->timestamp); + //work->ntime_offset = ntime_offset; +} + +// returns true if queue full. +static struct timeval last_force_queue; + +unsigned long usec_stamp(void) +{ + static unsigned long long int first_usec = 0; + struct timeval tv; + unsigned long long int curr_usec; + + cgtime(&tv); + curr_usec = tv.tv_sec * 1000000 + tv.tv_usec; + if (first_usec == 0) { + first_usec = curr_usec; + curr_usec = 0; + } else + curr_usec -= first_usec; + return curr_usec; +} + +static bool spondoolies_queue_full_sp30(struct cgpu_info *cgpu) +{ + struct spond_adapter* a = cgpu->device_data; +#if 0 + static int bla = 0; + + if (!((bla++)%500)) { + printf("FAKE TEST FLUSH T:%d!\n",usec_stamp()); + a->reset_mg_queue = 3; + } +#endif + // Only once every 1/10 second do work. + bool ret = false, do_sleep = false; + int next_job_id; + struct timeval tv; + struct work *work; + unsigned int usec; + + mutex_lock(&a->lock); + assert(a->works_pending_tx <= REQUEST_SIZE); + + gettimeofday(&tv, NULL); + + usec = (tv.tv_sec-last_force_queue.tv_sec) * 1000000; + usec += (tv.tv_usec-last_force_queue.tv_usec); + + if ((usec >= REQUEST_PERIOD) || + (a->reset_mg_queue == 3) || // push flush + ((a->reset_mg_queue == 2)) || // Fast pull + ((a->reset_mg_queue == 1) && (a->works_pending_tx == REQUEST_SIZE))) { // Fast push after flush + spondoolies_flush_queue(a, (a->reset_mg_queue == 3)); + if (a->reset_mg_queue) { + //printf("FLUSH(%d) %d T:%d\n",a->reset_mg_queue , a->works_pending_tx, usec_stamp()); + if (a->works_pending_tx || (a->reset_mg_queue == 3)) { + a->reset_mg_queue--; + } + } + last_force_queue = tv; + } + + // see if we have enough jobs + if (a->works_pending_tx == REQUEST_SIZE) { + ret = true; + goto return_unlock; + } + + // see if can take 1 more job. + // Must be smaller to prevent overflow. + assert(MAX_JOBS_PENDING_IN_MINERGATE_SP30 < MINERGATE_ADAPTER_QUEUE_SP30); + next_job_id = (a->current_job_id + 1) % MAX_JOBS_PENDING_IN_MINERGATE_SP30; + if (a->my_jobs[next_job_id].cgminer_work) { + ret = true; + goto return_unlock; + } + work = get_queued(cgpu); + if (unlikely(!work)) { + do_sleep = true; + goto return_unlock; + } + + work->thr = cgpu->thr[0]; + work->thr_id = cgpu->thr[0]->id; + assert(work->thr); + + a->current_job_id = next_job_id; + work->subid = a->current_job_id; + // Get pointer for the request + a->my_jobs[a->current_job_id].cgminer_work = work; + a->my_jobs[a->current_job_id].state = SPONDWORK_STATE_IN_BUSY; + //printf("Push: %d\n", a->current_job_id); + + int max_ntime_roll = (work->drv_rolllimit < MAX_NROLES) ? work->drv_rolllimit : MAX_NROLES; + minergate_do_job_req_sp30* pkt_job = &a->mp_next_req->req[a->works_pending_tx]; + fill_minergate_request(pkt_job, work, max_ntime_roll); + a->works_in_driver++; + a->works_pending_tx++; + a->mp_next_req->req_count++; + a->my_jobs[a->current_job_id].merkle_root = pkt_job->mrkle_root; + +return_unlock: + //printf("D:P.TX:%d inD:%d\n", a->works_pending_tx, a->works_in_driver); + mutex_unlock(&a->lock); + + if (do_sleep) + cgsleep_ms(10); + + return ret; +} + +static void spond_poll_stats(struct cgpu_info *spond, struct spond_adapter *a) +{ + FILE *fp = fopen("/var/run/mg_rate_temp", "r"); + + if (!fp) { + applog(LOG_DEBUG, "SPOND unable to open mg_rate_temp"); + a->temp_rate = a->front_temp = a->rear_temp_top = a->rear_temp_bot = 0; + } else { + int ret = fscanf(fp, "%d %d %d %d", &a->temp_rate, &a->front_temp , &a->rear_temp_top , &a->rear_temp_bot); + + + if (ret != 4) + a->temp_rate = a->front_temp = a->rear_temp_top = a->rear_temp_bot = 0; + fclose(fp); + } + applog(LOG_DEBUG, "SPOND poll_stats rate: %d front: %d rear(T/B): %d/%d", + a->temp_rate, a->front_temp , a->rear_temp_top, a->rear_temp_bot); + /* Use the rear temperature as the dev temperature for now */ + spond->temp = (a->rear_temp_top + a->rear_temp_bot)/2; +} + +// Return completed work to submit_nonce() and work_completed() +// struct timeval last_force_queue = {0}; +static int64_t spond_scanhash_sp30(struct thr_info *thr) +{ + struct cgpu_info *cgpu = thr->cgpu; + struct spond_adapter *a = cgpu->device_data; + int64_t ghashes = 0; + cgtimer_t cgt; + time_t now_t; + + cgsleep_prepare_r(&cgt); + now_t = time(NULL); + /* Poll stats only once per second */ + if (now_t != a->last_stats) { + a->last_stats = now_t; + spond_poll_stats(cgpu, a); + } + + if (a->parse_resp) { + int array_size, i; + + mutex_lock(&a->lock); + //ghashes = (a->mp_last_rsp->gh_div_50_rate); + //ghashes = ghashes * 50000 * REQUEST_PERIOD; + array_size = a->mp_last_rsp->rsp_count; + for (i = 0; i < array_size; i++) { // walk the jobs + int job_id; + + minergate_do_job_rsp_sp30* work = a->mp_last_rsp->rsp + i; + job_id = work->work_id_in_sw; + if ((a->my_jobs[job_id].cgminer_work)) { + if (a->my_jobs[job_id].merkle_root == work->mrkle_root) { + assert(a->my_jobs[job_id].state == SPONDWORK_STATE_IN_BUSY); + + if (work->winner_nonce) { + struct work *cg_work = a->my_jobs[job_id].cgminer_work; + bool ok; + + ok = submit_noffset_nonce(cg_work->thr, cg_work, work->winner_nonce, work->ntime_offset); + if (ok) + ghashes += 0xffffffffull * cg_work->device_diff; + /*printf("WIn on %d (+%d), none=%x = %d\n", + * work->work_id_in_sw, work->ntime_offset, htole32(work->winner_nonce), ok);*/ + a->wins++; + } + + //printf("%d ntime_clones = %d\n",job_id,a->my_jobs[job_id].ntime_clones); + + //printf("Done with %d\n", job_id); + if (work->job_complete) { + //printf("Complete %d\n", job_id); + work_completed(a->cgpu, a->my_jobs[job_id].cgminer_work); + a->good++; + a->my_jobs[job_id].cgminer_work = NULL; + a->my_jobs[job_id].state = SPONDWORK_STATE_EMPTY; + a->works_in_minergate_and_pending_tx--; + a->works_in_driver--; + } + } else { + a->bad++; + printf("Dropping minergate old job id=%d mrkl=%x my-mrkl=%x\n", + job_id, a->my_jobs[job_id].merkle_root, work->mrkle_root); + } + } else { + a->empty++; + printf("No cgminer job (id:%d res:%d)!\n",job_id, work->res); + } + } + mutex_unlock(&a->lock); + + a->parse_resp = 0; + } + cgsleep_ms_r(&cgt, 40); + + return ghashes; +} + +// Remove all work from queue +static void spond_flush_work_sp30(struct cgpu_info *cgpu) +{ + struct spond_adapter *a = cgpu->device_data; + + //printf("GOT FLUSH!%d\n"); + mutex_lock(&a->lock); + a->reset_mg_queue = 3; + mutex_unlock(&a->lock); +} + +struct device_drv sp30_drv = { + .drv_id = DRIVER_sp30, + .dname = "Sp30", + .name = "S30", + .min_diff = 16, + .max_diff = 1024.0, // Limit max diff to get some nonces back regardless + .drv_detect = spondoolies_detect_sp30, + .get_api_stats = spondoolies_api_stats_sp30, + .thread_prepare = spondoolies_prepare_sp30, + .thread_shutdown = spondoolies_shutdown_sp30, + .hash_work = hash_queued_work, + .queue_full = spondoolies_queue_full_sp30, + .scanwork = spond_scanhash_sp30, + .flush_work = spond_flush_work_sp30, +}; diff --git a/driver-spondoolies-sp30.h b/driver-spondoolies-sp30.h new file mode 100644 index 0000000000..ced4eb4087 --- /dev/null +++ b/driver-spondoolies-sp30.h @@ -0,0 +1,85 @@ +/* + * Copyright 2014 Con Kolivas + * Copyright 2014 Zvi Shteingart - Spondoolies-tech.com + * + * 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 SPONDA_HFILE +#define SPONDA_HFILE + +#include "miner.h" +#include "driver-spondoolies-sp30-p.h" + + + +typedef enum adapter_state { + ADAPTER_STATE_INIT, + ADAPTER_STATE_OPERATIONAL, +} ADAPTER_STATE; + +typedef enum spond_work_state { + SPONDWORK_STATE_EMPTY, + SPONDWORK_STATE_IN_BUSY, +} SPONDWORK_STATE; + +#define MAX_JOBS_PENDING_IN_MINERGATE_SP30 30 +#define MAX_NROLES 60 + + +typedef struct { + struct work *cgminer_work; + SPONDWORK_STATE state; + uint32_t merkle_root; + time_t start_time; + int job_id; +} spond_driver_work_sp30; + + + +struct spond_adapter { + pthread_mutex_t lock; + ADAPTER_STATE adapter_state; + void *cgpu; + + // Statistics + int wins; + int good; + int empty; + int bad; + int overflow; + // state + int works_in_driver; + int works_in_minergate_and_pending_tx; + int works_pending_tx; + int socket_fd; + int reset_mg_queue; // 3=reset, 2=fast send 1 job, 1=fast send 10 jobs, 0=nada + int current_job_id; + int parse_resp; + minergate_req_packet_sp30* mp_next_req; + minergate_rsp_packet_sp30* mp_last_rsp; + spond_driver_work_sp30 my_jobs[MAX_JOBS_PENDING_IN_MINERGATE_SP30]; + + // Temperature statistics + int temp_rate; + int front_temp; + int rear_temp_top; + int rear_temp_bot; + + // Last second we polled stats + time_t last_stats; +}; + +// returns non-zero if needs to change ASICs. +int spond_one_sec_timer_scaling(struct spond_adapter *a, int t); +int spond_do_scaling(struct spond_adapter *a); + +extern void one_sec_spondoolies_watchdog(int uptime); + +#define REQUEST_PERIOD (100000) // times per second - in usec +#define REQUEST_SIZE 10 // jobs per request + +#endif diff --git a/driver-ztex.c b/driver-ztex.c deleted file mode 100644 index 25254716af..0000000000 --- a/driver-ztex.c +++ /dev/null @@ -1,425 +0,0 @@ -/** - * ztex.c - cgminer worker for Ztex 1.15x fpga board - * - * Copyright (c) 2012 nelisky.btc@gmail.com - * - * This work is based upon the Java SDK provided by ztex which is - * Copyright (C) 2009-2011 ZTEX GmbH. - * http://www.ztex.de - * - * This work is based upon the icarus.c worker which was - * Copyright 2012 Luke Dashjr - * Copyright 2012 Xiangfu - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, see http://www.gnu.org/licenses/. -**/ -#include "miner.h" -#include -#include -#include "libztex.h" -#include "util.h" - -#define GOLDEN_BACKLOG 5 - -struct device_drv ztex_drv; - -// Forward declarations -static void ztex_disable(struct thr_info* thr); -static bool ztex_prepare(struct thr_info *thr); - -static void ztex_selectFpga(struct libztex_device* ztex) -{ - if (ztex->root->numberOfFpgas > 1) { - if (ztex->root->selectedFpga != ztex->fpgaNum) - mutex_lock(&ztex->root->mutex); - libztex_selectFpga(ztex); - } -} - -static void ztex_releaseFpga(struct libztex_device* ztex) -{ - if (ztex->root->numberOfFpgas > 1) { - ztex->root->selectedFpga = -1; - mutex_unlock(&ztex->root->mutex); - } -} - -static void ztex_detect(void) -{ - int cnt; - int i,j; - int fpgacount; - struct libztex_dev_list **ztex_devices; - struct libztex_device *ztex_slave; - struct cgpu_info *ztex; - - cnt = libztex_scanDevices(&ztex_devices); - if (cnt > 0) - applog(LOG_WARNING, "Found %d ztex board%s", cnt, cnt > 1 ? "s" : ""); - - for (i = 0; i < cnt; i++) { - ztex = calloc(1, sizeof(struct cgpu_info)); - ztex->drv = &ztex_drv; - ztex->device_ztex = ztex_devices[i]->dev; - ztex->threads = 1; - ztex->device_ztex->fpgaNum = 0; - ztex->device_ztex->root = ztex->device_ztex; - add_cgpu(ztex); - - fpgacount = libztex_numberOfFpgas(ztex->device_ztex); - - if (fpgacount > 1) - pthread_mutex_init(&ztex->device_ztex->mutex, NULL); - - for (j = 1; j < fpgacount; j++) { - ztex = calloc(1, sizeof(struct cgpu_info)); - ztex->drv = &ztex_drv; - ztex_slave = calloc(1, sizeof(struct libztex_device)); - memcpy(ztex_slave, ztex_devices[i]->dev, sizeof(struct libztex_device)); - ztex->device_ztex = ztex_slave; - ztex->threads = 1; - ztex_slave->fpgaNum = j; - ztex_slave->root = ztex_devices[i]->dev; - ztex_slave->repr[strlen(ztex_slave->repr) - 1] = ('1' + j); - add_cgpu(ztex); - } - - applog(LOG_WARNING,"%s: Found Ztex (fpga count = %d) , mark as %d", ztex->device_ztex->repr, fpgacount, ztex->device_id); - } - - if (cnt > 0) - libztex_freeDevList(ztex_devices); -} - -static bool ztex_updateFreq(struct libztex_device* ztex) -{ - int i, maxM, bestM; - double bestR, r; - - for (i = 0; i < ztex->freqMaxM; i++) - if (ztex->maxErrorRate[i + 1] * i < ztex->maxErrorRate[i] * (i + 20)) - ztex->maxErrorRate[i + 1] = ztex->maxErrorRate[i] * (1.0 + 20.0 / i); - - maxM = 0; - while (maxM < ztex->freqMDefault && ztex->maxErrorRate[maxM + 1] < LIBZTEX_MAXMAXERRORRATE) - maxM++; - while (maxM < ztex->freqMaxM && ztex->errorWeight[maxM] > 150 && ztex->maxErrorRate[maxM + 1] < LIBZTEX_MAXMAXERRORRATE) - maxM++; - - bestM = 0; - bestR = 0; - for (i = 0; i <= maxM; i++) { - r = (i + 1 + (i == ztex->freqM? LIBZTEX_ERRORHYSTERESIS: 0)) * (1 - ztex->maxErrorRate[i]); - if (r > bestR) { - bestM = i; - bestR = r; - } - } - - if (bestM != ztex->freqM) { - ztex_selectFpga(ztex); - libztex_setFreq(ztex, bestM); - ztex_releaseFpga(ztex); - } - - maxM = ztex->freqMDefault; - while (maxM < ztex->freqMaxM && ztex->errorWeight[maxM + 1] > 100) - maxM++; - if ((bestM < (1.0 - LIBZTEX_OVERHEATTHRESHOLD) * maxM) && bestM < maxM - 1) { - ztex_selectFpga(ztex); - libztex_resetFpga(ztex); - ztex_releaseFpga(ztex); - applog(LOG_ERR, "%s: frequency drop of %.1f%% detect. This may be caused by overheating. FPGA is shut down to prevent damage.", - ztex->repr, (1.0 - 1.0 * bestM / maxM) * 100); - return false; - } - return true; -} - - -static uint32_t ztex_checkNonce(struct work *work, uint32_t nonce) -{ - uint32_t *data32 = (uint32_t *)(work->data); - unsigned char swap[80]; - uint32_t *swap32 = (uint32_t *)swap; - unsigned char hash1[32]; - unsigned char hash2[32]; - uint32_t *hash2_32 = (uint32_t *)hash2; - int i; - - swap32[76/4] = htonl(nonce); - - for (i = 0; i < 76 / 4; i++) - swap32[i] = swab32(data32[i]); - - sha256(swap, 80, hash1); - sha256(hash1, 32, hash2); - - return htonl(hash2_32[7]); -} - -static int64_t ztex_scanhash(struct thr_info *thr, struct work *work, - __maybe_unused int64_t max_nonce) -{ - struct libztex_device *ztex; - unsigned char sendbuf[44]; - int i, j, k; - uint32_t *backlog; - int backlog_p = 0, backlog_max; - uint32_t *lastnonce; - uint32_t nonce, noncecnt = 0; - bool overflow, found; - struct libztex_hash_data hdata[GOLDEN_BACKLOG]; - - if (thr->cgpu->deven == DEV_DISABLED) - return -1; - - ztex = thr->cgpu->device_ztex; - - memcpy(sendbuf, work->data + 64, 12); - memcpy(sendbuf + 12, work->midstate, 32); - - ztex_selectFpga(ztex); - i = libztex_sendHashData(ztex, sendbuf); - if (i < 0) { - // Something wrong happened in send - applog(LOG_ERR, "%s: Failed to send hash data with err %d, retrying", ztex->repr, i); - cgsleep_ms(500); - i = libztex_sendHashData(ztex, sendbuf); - if (i < 0) { - // And there's nothing we can do about it - ztex_disable(thr); - applog(LOG_ERR, "%s: Failed to send hash data with err %d, giving up", ztex->repr, i); - ztex_releaseFpga(ztex); - return -1; - } - } - ztex_releaseFpga(ztex); - - applog(LOG_DEBUG, "%s: sent hashdata", ztex->repr); - - lastnonce = calloc(1, sizeof(uint32_t)*ztex->numNonces); - if (lastnonce == NULL) { - applog(LOG_ERR, "%s: failed to allocate lastnonce[%d]", ztex->repr, ztex->numNonces); - return -1; - } - - /* Add an extra slot for detecting dupes that lie around */ - backlog_max = ztex->numNonces * (2 + ztex->extraSolutions); - backlog = calloc(1, sizeof(uint32_t) * backlog_max); - if (backlog == NULL) { - applog(LOG_ERR, "%s: failed to allocate backlog[%d]", ztex->repr, backlog_max); - return -1; - } - - overflow = false; - int count = 0; - int validNonces = 0; - double errorCount = 0; - - applog(LOG_DEBUG, "%s: entering poll loop", ztex->repr); - while (!(overflow || thr->work_restart)) { - count++; - - int sleepcount = 0; - while (thr->work_restart == 0 && sleepcount < 25) { - cgsleep_ms(10); - sleepcount += 1; - } - - if (thr->work_restart) { - applog(LOG_DEBUG, "%s: New work detected", ztex->repr); - break; - } - - ztex_selectFpga(ztex); - i = libztex_readHashData(ztex, &hdata[0]); - if (i < 0) { - // Something wrong happened in read - applog(LOG_ERR, "%s: Failed to read hash data with err %d, retrying", ztex->repr, i); - cgsleep_ms(500); - i = libztex_readHashData(ztex, &hdata[0]); - if (i < 0) { - // And there's nothing we can do about it - ztex_disable(thr); - applog(LOG_ERR, "%s: Failed to read hash data with err %d, giving up", ztex->repr, i); - free(lastnonce); - free(backlog); - ztex_releaseFpga(ztex); - return -1; - } - } - ztex_releaseFpga(ztex); - - if (thr->work_restart) { - applog(LOG_DEBUG, "%s: New work detected", ztex->repr); - break; - } - - ztex->errorCount[ztex->freqM] *= 0.995; - ztex->errorWeight[ztex->freqM] = ztex->errorWeight[ztex->freqM] * 0.995 + 1.0; - - for (i = 0; i < ztex->numNonces; i++) { - nonce = hdata[i].nonce; - if (nonce > noncecnt) - noncecnt = nonce; - if (((0xffffffff - nonce) < (nonce - lastnonce[i])) || nonce < lastnonce[i]) { - applog(LOG_DEBUG, "%s: overflow nonce=%08x lastnonce=%08x", ztex->repr, nonce, lastnonce[i]); - overflow = true; - } else - lastnonce[i] = nonce; - - if (ztex_checkNonce(work, nonce) != (hdata->hash7 + 0x5be0cd19)) { - applog(LOG_DEBUG, "%s: checkNonce failed for %08X", ztex->repr, nonce); - - // do not count errors in the first 500ms after sendHashData (2x250 wait time) - if (count > 2) { - thr->cgpu->hw_errors++; - errorCount += (1.0 / ztex->numNonces); - } - } - else - validNonces++; - - - for (j=0; j<=ztex->extraSolutions; j++) { - nonce = hdata[i].goldenNonce[j]; - - if (nonce == ztex->offsNonces) { - continue; - } - - // precheck the extraSolutions since they often fail - if (j > 0 && ztex_checkNonce(work, nonce) != 0) { - continue; - } - - found = false; - for (k = 0; k < backlog_max; k++) { - if (backlog[k] == nonce) { - found = true; - break; - } - } - if (!found) { - applog(LOG_DEBUG, "%s: Share found N%dE%d", ztex->repr, i, j); - backlog[backlog_p++] = nonce; - - if (backlog_p >= backlog_max) - backlog_p = 0; - - work->blk.nonce = 0xffffffff; - submit_nonce(thr, work, nonce); - applog(LOG_DEBUG, "%s: submitted %08x", ztex->repr, nonce); - } - } - } - } - - // only add the errorCount if we had at least some valid nonces or - // had no valid nonces in the last round - if (errorCount > 0.0) { - if (ztex->nonceCheckValid > 0 && validNonces == 0) { - applog(LOG_ERR, "%s: resetting %.1f errors", ztex->repr, errorCount); - } - else { - ztex->errorCount[ztex->freqM] += errorCount; - } - } - - // remember the number of valid nonces for the check in the next round - ztex->nonceCheckValid = validNonces; - - ztex->errorRate[ztex->freqM] = ztex->errorCount[ztex->freqM] / ztex->errorWeight[ztex->freqM] * (ztex->errorWeight[ztex->freqM] < 100? ztex->errorWeight[ztex->freqM] * 0.01: 1.0); - if (ztex->errorRate[ztex->freqM] > ztex->maxErrorRate[ztex->freqM]) - ztex->maxErrorRate[ztex->freqM] = ztex->errorRate[ztex->freqM]; - - if (!ztex_updateFreq(ztex)) { - // Something really serious happened, so mark this thread as dead! - free(lastnonce); - free(backlog); - - return -1; - } - - applog(LOG_DEBUG, "%s: exit %1.8X", ztex->repr, noncecnt); - - work->blk.nonce = 0xffffffff; - - free(lastnonce); - free(backlog); - - return noncecnt; -} - -static void ztex_statline_before(char *buf, size_t bufsiz, struct cgpu_info *cgpu) -{ - if (cgpu->deven == DEV_ENABLED) { - tailsprintf(buf, bufsiz, "%s-%d | ", cgpu->device_ztex->snString, cgpu->device_ztex->fpgaNum+1); - tailsprintf(buf, bufsiz, "%0.1fMHz | ", cgpu->device_ztex->freqM1 * (cgpu->device_ztex->freqM + 1)); - } -} - -static bool ztex_prepare(struct thr_info *thr) -{ - struct cgpu_info *cgpu = thr->cgpu; - struct libztex_device *ztex = cgpu->device_ztex; - - ztex_selectFpga(ztex); - if (libztex_configureFpga(ztex) != 0) { - libztex_resetFpga(ztex); - ztex_releaseFpga(ztex); - applog(LOG_ERR, "%s: Disabling!", thr->cgpu->device_ztex->repr); - thr->cgpu->deven = DEV_DISABLED; - return true; - } - ztex->freqM = ztex->freqMaxM+1;; - //ztex_updateFreq(ztex); - libztex_setFreq(ztex, ztex->freqMDefault); - ztex_releaseFpga(ztex); - applog(LOG_DEBUG, "%s: prepare", ztex->repr); - return true; -} - -static void ztex_shutdown(struct thr_info *thr) -{ - if (thr->cgpu->device_ztex != NULL) { - if (thr->cgpu->device_ztex->fpgaNum == 0) - pthread_mutex_destroy(&thr->cgpu->device_ztex->mutex); - applog(LOG_DEBUG, "%s: shutdown", thr->cgpu->device_ztex->repr); - libztex_destroy_device(thr->cgpu->device_ztex); - thr->cgpu->device_ztex = NULL; - } -} - -static void ztex_disable(struct thr_info *thr) -{ - struct cgpu_info *cgpu; - - applog(LOG_ERR, "%s: Disabling!", thr->cgpu->device_ztex->repr); - cgpu = get_devices(thr->cgpu->device_id); - cgpu->deven = DEV_DISABLED; - ztex_shutdown(thr); -} - -struct device_drv ztex_drv = { - .drv_id = DRIVER_ZTEX, - .dname = "ztex", - .name = "ZTX", - .drv_detect = ztex_detect, - .get_statline_before = ztex_statline_before, - .thread_prepare = ztex_prepare, - .scanhash = ztex_scanhash, - .thread_shutdown = ztex_shutdown, -}; - diff --git a/example.conf b/example.conf index ae3366e632..2448ed508b 100644 --- a/example.conf +++ b/example.conf @@ -17,24 +17,9 @@ } ], -"intensity" : "d,9,9,9", -"gpu-engine" : "0-985,0-950,0-960,0-1000", -"gpu-fan" : "0-85,0-85,0-85,0-85", -"gpu-memclock" : "860,825,835,875", -"gpu-powertune" : "20,20,20,20", -"temp-cutoff" : "95,95,95,95", -"temp-overheat" : "85,85,85,85", -"temp-target" : "75,75,75,75", - -"auto-fan" : true, -"auto-gpu" : true, -"expiry" : "120", "failover-only" : true, -"gpu-threads" : "2", -"log" : "5", -"queue" : "1", -"scan-time" : "60", -"temp-hysteresis" : "3", - -"kernel-path" : "/usr/local/bin" +"no-submit-stale" : true, +"api-listen" : true, +"api-port" : "4028", +"api-allow" : "W:192.168.1.0/24,W:127.0.0.1" } diff --git a/findnonce.c b/findnonce.c deleted file mode 100644 index baa652f19c..0000000000 --- a/findnonce.c +++ /dev/null @@ -1,234 +0,0 @@ -/* - * Copyright 2011-2013 Con Kolivas - * Copyright 2011 Nils Schneider - * - * This program is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License as published by the Free - * Software Foundation; either version 3 of the License, or (at your option) - * any later version. See COPYING for more details. - */ - -#include "config.h" -#ifdef HAVE_OPENCL - -#include -#include -#include -#include - -#include "findnonce.h" -#include "scrypt.h" - -const uint32_t SHA256_K[64] = { - 0x428a2f98, 0x71374491, 0xb5c0fbcf, 0xe9b5dba5, - 0x3956c25b, 0x59f111f1, 0x923f82a4, 0xab1c5ed5, - 0xd807aa98, 0x12835b01, 0x243185be, 0x550c7dc3, - 0x72be5d74, 0x80deb1fe, 0x9bdc06a7, 0xc19bf174, - 0xe49b69c1, 0xefbe4786, 0x0fc19dc6, 0x240ca1cc, - 0x2de92c6f, 0x4a7484aa, 0x5cb0a9dc, 0x76f988da, - 0x983e5152, 0xa831c66d, 0xb00327c8, 0xbf597fc7, - 0xc6e00bf3, 0xd5a79147, 0x06ca6351, 0x14292967, - 0x27b70a85, 0x2e1b2138, 0x4d2c6dfc, 0x53380d13, - 0x650a7354, 0x766a0abb, 0x81c2c92e, 0x92722c85, - 0xa2bfe8a1, 0xa81a664b, 0xc24b8b70, 0xc76c51a3, - 0xd192e819, 0xd6990624, 0xf40e3585, 0x106aa070, - 0x19a4c116, 0x1e376c08, 0x2748774c, 0x34b0bcb5, - 0x391c0cb3, 0x4ed8aa4a, 0x5b9cca4f, 0x682e6ff3, - 0x748f82ee, 0x78a5636f, 0x84c87814, 0x8cc70208, - 0x90befffa, 0xa4506ceb, 0xbef9a3f7, 0xc67178f2 -}; - -#define rotate(x,y) ((x<>(sizeof(x)*8-y))) -#define rotr(x,y) ((x>>y) | (x<<(sizeof(x)*8-y))) - -#define R(a, b, c, d, e, f, g, h, w, k) \ - h = h + (rotate(e, 26) ^ rotate(e, 21) ^ rotate(e, 7)) + (g ^ (e & (f ^ g))) + k + w; \ - d = d + h; \ - h = h + (rotate(a, 30) ^ rotate(a, 19) ^ rotate(a, 10)) + ((a & b) | (c & (a | b))) - -void precalc_hash(dev_blk_ctx *blk, uint32_t *state, uint32_t *data) -{ - cl_uint A, B, C, D, E, F, G, H; - - A = state[0]; - B = state[1]; - C = state[2]; - D = state[3]; - E = state[4]; - F = state[5]; - G = state[6]; - H = state[7]; - - R(A, B, C, D, E, F, G, H, data[0], SHA256_K[0]); - R(H, A, B, C, D, E, F, G, data[1], SHA256_K[1]); - R(G, H, A, B, C, D, E, F, data[2], SHA256_K[2]); - - blk->cty_a = A; - blk->cty_b = B; - blk->cty_c = C; - blk->cty_d = D; - - blk->D1A = D + 0xb956c25b; - - blk->cty_e = E; - blk->cty_f = F; - blk->cty_g = G; - blk->cty_h = H; - - blk->ctx_a = state[0]; - blk->ctx_b = state[1]; - blk->ctx_c = state[2]; - blk->ctx_d = state[3]; - blk->ctx_e = state[4]; - blk->ctx_f = state[5]; - blk->ctx_g = state[6]; - blk->ctx_h = state[7]; - - blk->merkle = data[0]; - blk->ntime = data[1]; - blk->nbits = data[2]; - - blk->W16 = blk->fW0 = data[0] + (rotr(data[1], 7) ^ rotr(data[1], 18) ^ (data[1] >> 3)); - blk->W17 = blk->fW1 = data[1] + (rotr(data[2], 7) ^ rotr(data[2], 18) ^ (data[2] >> 3)) + 0x01100000; - blk->PreVal4 = blk->fcty_e = blk->ctx_e + (rotr(B, 6) ^ rotr(B, 11) ^ rotr(B, 25)) + (D ^ (B & (C ^ D))) + 0xe9b5dba5; - blk->T1 = blk->fcty_e2 = (rotr(F, 2) ^ rotr(F, 13) ^ rotr(F, 22)) + ((F & G) | (H & (F | G))); - blk->PreVal4_2 = blk->PreVal4 + blk->T1; - blk->PreVal0 = blk->PreVal4 + blk->ctx_a; - blk->PreW31 = 0x00000280 + (rotr(blk->W16, 7) ^ rotr(blk->W16, 18) ^ (blk->W16 >> 3)); - blk->PreW32 = blk->W16 + (rotr(blk->W17, 7) ^ rotr(blk->W17, 18) ^ (blk->W17 >> 3)); - blk->PreW18 = data[2] + (rotr(blk->W16, 17) ^ rotr(blk->W16, 19) ^ (blk->W16 >> 10)); - blk->PreW19 = 0x11002000 + (rotr(blk->W17, 17) ^ rotr(blk->W17, 19) ^ (blk->W17 >> 10)); - - - blk->W2 = data[2]; - - blk->W2A = blk->W2 + (rotr(blk->W16, 19) ^ rotr(blk->W16, 17) ^ (blk->W16 >> 10)); - blk->W17_2 = 0x11002000 + (rotr(blk->W17, 19) ^ rotr(blk->W17, 17) ^ (blk->W17 >> 10)); - - blk->fW2 = data[2] + (rotr(blk->fW0, 17) ^ rotr(blk->fW0, 19) ^ (blk->fW0 >> 10)); - blk->fW3 = 0x11002000 + (rotr(blk->fW1, 17) ^ rotr(blk->fW1, 19) ^ (blk->fW1 >> 10)); - blk->fW15 = 0x00000280 + (rotr(blk->fW0, 7) ^ rotr(blk->fW0, 18) ^ (blk->fW0 >> 3)); - blk->fW01r = blk->fW0 + (rotr(blk->fW1, 7) ^ rotr(blk->fW1, 18) ^ (blk->fW1 >> 3)); - - - blk->PreVal4addT1 = blk->PreVal4 + blk->T1; - blk->T1substate0 = blk->ctx_a - blk->T1; - - blk->C1addK5 = blk->cty_c + SHA256_K[5]; - blk->B1addK6 = blk->cty_b + SHA256_K[6]; - blk->PreVal0addK7 = blk->PreVal0 + SHA256_K[7]; - blk->W16addK16 = blk->W16 + SHA256_K[16]; - blk->W17addK17 = blk->W17 + SHA256_K[17]; - - blk->zeroA = blk->ctx_a + 0x98c7e2a2; - blk->zeroB = blk->ctx_a + 0xfc08884d; - blk->oneA = blk->ctx_b + 0x90bb1e3c; - blk->twoA = blk->ctx_c + 0x50c6645b; - blk->threeA = blk->ctx_d + 0x3ac42e24; - blk->fourA = blk->ctx_e + SHA256_K[4]; - blk->fiveA = blk->ctx_f + SHA256_K[5]; - blk->sixA = blk->ctx_g + SHA256_K[6]; - blk->sevenA = blk->ctx_h + SHA256_K[7]; -} - -#if 0 // not used any more - -#define P(t) (W[(t)&0xF] = W[(t-16)&0xF] + (rotate(W[(t-15)&0xF], 25) ^ rotate(W[(t-15)&0xF], 14) ^ (W[(t-15)&0xF] >> 3)) + W[(t-7)&0xF] + (rotate(W[(t-2)&0xF], 15) ^ rotate(W[(t-2)&0xF], 13) ^ (W[(t-2)&0xF] >> 10))) - -#define IR(u) \ - R(A, B, C, D, E, F, G, H, W[u+0], SHA256_K[u+0]); \ - R(H, A, B, C, D, E, F, G, W[u+1], SHA256_K[u+1]); \ - R(G, H, A, B, C, D, E, F, W[u+2], SHA256_K[u+2]); \ - R(F, G, H, A, B, C, D, E, W[u+3], SHA256_K[u+3]); \ - R(E, F, G, H, A, B, C, D, W[u+4], SHA256_K[u+4]); \ - R(D, E, F, G, H, A, B, C, W[u+5], SHA256_K[u+5]); \ - R(C, D, E, F, G, H, A, B, W[u+6], SHA256_K[u+6]); \ - R(B, C, D, E, F, G, H, A, W[u+7], SHA256_K[u+7]) -#define FR(u) \ - R(A, B, C, D, E, F, G, H, P(u+0), SHA256_K[u+0]); \ - R(H, A, B, C, D, E, F, G, P(u+1), SHA256_K[u+1]); \ - R(G, H, A, B, C, D, E, F, P(u+2), SHA256_K[u+2]); \ - R(F, G, H, A, B, C, D, E, P(u+3), SHA256_K[u+3]); \ - R(E, F, G, H, A, B, C, D, P(u+4), SHA256_K[u+4]); \ - R(D, E, F, G, H, A, B, C, P(u+5), SHA256_K[u+5]); \ - R(C, D, E, F, G, H, A, B, P(u+6), SHA256_K[u+6]); \ - R(B, C, D, E, F, G, H, A, P(u+7), SHA256_K[u+7]) - -#define PIR(u) \ - R(F, G, H, A, B, C, D, E, W[u+3], SHA256_K[u+3]); \ - R(E, F, G, H, A, B, C, D, W[u+4], SHA256_K[u+4]); \ - R(D, E, F, G, H, A, B, C, W[u+5], SHA256_K[u+5]); \ - R(C, D, E, F, G, H, A, B, W[u+6], SHA256_K[u+6]); \ - R(B, C, D, E, F, G, H, A, W[u+7], SHA256_K[u+7]) - -#define PFR(u) \ - R(A, B, C, D, E, F, G, H, P(u+0), SHA256_K[u+0]); \ - R(H, A, B, C, D, E, F, G, P(u+1), SHA256_K[u+1]); \ - R(G, H, A, B, C, D, E, F, P(u+2), SHA256_K[u+2]); \ - R(F, G, H, A, B, C, D, E, P(u+3), SHA256_K[u+3]); \ - R(E, F, G, H, A, B, C, D, P(u+4), SHA256_K[u+4]); \ - R(D, E, F, G, H, A, B, C, P(u+5), SHA256_K[u+5]) - -#endif - -struct pc_data { - struct thr_info *thr; - struct work *work; - uint32_t res[SCRYPT_MAXBUFFERS]; - pthread_t pth; - int found; -}; - -static void *postcalc_hash(void *userdata) -{ - struct pc_data *pcd = (struct pc_data *)userdata; - struct thr_info *thr = pcd->thr; - unsigned int entry = 0; - int found = opt_scrypt ? SCRYPT_FOUND : FOUND; - - pthread_detach(pthread_self()); - - /* To prevent corrupt values in FOUND from trying to read beyond the - * end of the res[] array */ - if (unlikely(pcd->res[found] & ~found)) { - applog(LOG_WARNING, "%s%d: invalid nonce count - HW error", - thr->cgpu->drv->name, thr->cgpu->device_id); - hw_errors++; - thr->cgpu->hw_errors++; - pcd->res[found] &= found; - } - - for (entry = 0; entry < pcd->res[found]; entry++) { - uint32_t nonce = pcd->res[entry]; - - applog(LOG_DEBUG, "OCL NONCE %u found in slot %d", nonce, entry); - submit_nonce(thr, pcd->work, nonce); - } - - discard_work(pcd->work); - free(pcd); - - return NULL; -} - -void postcalc_hash_async(struct thr_info *thr, struct work *work, uint32_t *res) -{ - struct pc_data *pcd = malloc(sizeof(struct pc_data)); - int buffersize; - - if (unlikely(!pcd)) { - applog(LOG_ERR, "Failed to malloc pc_data in postcalc_hash_async"); - return; - } - - pcd->thr = thr; - pcd->work = copy_work(work); - buffersize = opt_scrypt ? SCRYPT_BUFFERSIZE : BUFFERSIZE; - memcpy(&pcd->res, res, buffersize); - - if (pthread_create(&pcd->pth, NULL, postcalc_hash, (void *)pcd)) { - applog(LOG_ERR, "Failed to create postcalc_hash thread"); - return; - } -} -#endif /* HAVE_OPENCL */ diff --git a/findnonce.h b/findnonce.h deleted file mode 100644 index fc5a157f90..0000000000 --- a/findnonce.h +++ /dev/null @@ -1,19 +0,0 @@ -#ifndef __FINDNONCE_H__ -#define __FINDNONCE_H__ -#include "miner.h" -#include "config.h" - -#define MAXTHREADS (0xFFFFFFFEULL) -#define MAXBUFFERS (0x10) -#define BUFFERSIZE (sizeof(uint32_t) * MAXBUFFERS) -#define FOUND (0x0F) - -#define SCRYPT_MAXBUFFERS (0x100) -#define SCRYPT_BUFFERSIZE (sizeof(uint32_t) * SCRYPT_MAXBUFFERS) -#define SCRYPT_FOUND (0xFF) - -#ifdef HAVE_OPENCL -extern void precalc_hash(dev_blk_ctx *blk, uint32_t *state, uint32_t *data); -extern void postcalc_hash_async(struct thr_info *thr, struct work *work, uint32_t *res); -#endif /* HAVE_OPENCL */ -#endif /*__FINDNONCE_H__*/ diff --git a/hexdump.c b/hexdump.c index 8951dde879..80b8c2db04 100644 --- a/hexdump.c +++ b/hexdump.c @@ -27,10 +27,10 @@ static char nibble[] = { #define BYTES_PER_LINE 0x10 -void hexdump(const uint8_t *p, unsigned int len) +static void hexdump(const uint8_t *p, unsigned int len) { unsigned int i, addr; - unsigned int wordlen = sizeof(void*); + unsigned int wordlen = sizeof(unsigned int); unsigned char v, line[BYTES_PER_LINE * 5]; for (addr = 0; addr < len; addr += BYTES_PER_LINE) { diff --git a/hf_protocol.h b/hf_protocol.h new file mode 100644 index 0000000000..7a1a0c2122 --- /dev/null +++ b/hf_protocol.h @@ -0,0 +1,407 @@ +// +// Copyright 2013, 2014 HashFast Technologies LLC +// +// 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. +// +// Useful data structures and values for interfacing with HashFast products +// +// Version 1.1 +// + +#ifndef _HF_PROTOCOL_H_ +#define _HF_PROTOCOL_H_ + +#define HF_PROTOCOL_VERSION ((0<<8)|1) + +#define HF_PREAMBLE (uint8_t) 0xaa +#define HF_BROADCAST_ADDRESS (uint8_t) 0xff +#define HF_GWQ_ADDRESS (uint8_t) 254 + +// Serial protocol operation codes (Second header byte) +#define OP_NULL 0 +#define OP_ROOT 1 +#define OP_RESET 2 +#define OP_PLL_CONFIG 3 +#define OP_ADDRESS 4 +#define OP_READDRESS 5 +#define OP_HIGHEST 6 +#define OP_BAUD 7 +#define OP_UNROOT 8 + +#define OP_HASH 9 +#define OP_NONCE 10 +#define OP_ABORT 11 +#define OP_STATUS 12 +#define OP_GPIO 13 +#define OP_CONFIG 14 +#define OP_STATISTICS 15 +#define OP_GROUP 16 +#define OP_CLOCKGATE 17 + +// Conversions for the ADC readings from GN on-chip sensors +#define GN_CORE_VOLTAGE(a) ((float)(a)/256*1.2) +#define GN_DIE_TEMPERATURE(a) ((((float)(a)*240)/4096.0)-61.5) + +// What to use in an OP_CONFIG hdata field to set thermal overload point to a given temp in degrees C +#define GN_THERMAL_CUTOFF(temp) ((uint16_t)(((temp)+61.5)*4096/240)) + +// The sequence distance between a sent and received sequence number. +#define HF_SEQUENCE_DISTANCE(tx,rx) ((tx)>=(rx)?((tx)-(rx)):(info->num_sequence+(tx)-(rx))) + +// Values the protocol field in the above structure may take +#define PROTOCOL_USB_MAPPED_SERIAL 0 +#define PROTOCOL_GLOBAL_WORK_QUEUE 1 + +// Conversions for the board/module level sensors +#define M_VOLTAGE(a) ((float)(a)*19.0734e-6) +#define M_PHASE_CURRENT(a) ((float)(a)*0.794728597e-3) + +// Values info->device_type can take +#define HFD_G1 1 // HashFast G-1 GN ASIC +#define HFD_VC709 128 +#define HFD_ExpressAGX 129 + +// USB interface specific operation codes +#define OP_USB_INIT 128 // Initialize USB interface details +#define OP_GET_TRACE 129 // Send back the trace buffer if present +#define OP_LOOPBACK_USB 130 +#define OP_LOOPBACK_UART 131 +#define OP_DFU 132 // Jump into the boot loader +#define OP_USB_SHUTDOWN 133 // Initialize USB interface details +#define OP_DIE_STATUS 134 // Die status. There are 4 die per ASIC +#define OP_GWQ_STATUS 135 // Global Work Queue protocol status +#define OP_WORK_RESTART 136 // Stratum work restart regime +#define OP_USB_STATS1 137 // Statistics class 1 +#define OP_USB_GWQSTATS 138 // GWQ protocol statistics +#define OP_USB_NOTICE 139 // Asynchronous notification event +#define OP_PING 140 // Echo +#define OP_CORE_MAP 141 // Return core map +#define OP_VERSION 142 // Version information +#define OP_FAN 143 // Set Fan Speed +#define OP_NAME 144 // System name write/read +#define OP_USB_DEBUG 255 + +// HashFast vendor and product ID's +#define HF_USB_VENDOR_ID 0x297c +#define HF_USB_PRODUCT_ID_G1 0x0001 + +// If this bit is set, search forward for other nonce(s) +#define HF_NTIME_MASK 0xfff // Mask for for ntime +#define HF_NONCE_SEARCH 0x1000 // Search bit in candidate_nonce -> ntime + +// +// Fault codes that can be returned in struct hf_usb_init_base.operation_status +// +#define E_RESET_TIMEOUT 1 +#define E_ADDRESS_TIMEOUT 2 +#define E_CLOCKGATE_TIMEOUT 3 +#define E_CONFIG_TIMEOUT 4 +#define E_EXCESS_CORE_FAILURES 5 +#define E_TOTAL_CORE_FAILURES 6 +#define E_TOO_MANY_GROUPS 7 +#define E_NO_SLAVES 8 +#define E_SLAVE_COMM 9 +#define E_MAIN_POWER_BAD 10 +#define E_SECONDARY_POWER_BAD 11 +#define E_BOARD_1 12 +#define E_BOARD_2 13 +#define E_BOARD_3 14 +#define E_BOARD_4 15 +#define E_BOARD_5 16 +#define E_CORE_POWER_FAULT 17 +#define E_BAUD_TIMEOUT 18 +#define E_ADDRESS_FAILURE 19 +#define E_IR_PROG_FAILURE 20 +#define E_MIXED_MISMATCH 21 +#define E_MIXED_TIMEOUT 22 + +#define U32SIZE(x) (sizeof(x)/sizeof(uint32_t)) + +// Baud rate vs. code for gpi[7:5] coming out of reset +#define BAUD_RATE_PWRUP_0 115200 +#define BAUD_RATE_PWRUP_1 9600 +#define BAUD_RATE_PWRUP_2 38400 +#define BAUD_RATE_PWRUP_3 57600 +#define BAUD_RATE_PWRUP_4 230400 +#define BAUD_RATE_PWRUP_5 576000 +#define BAUD_RATE_PWRUP_6 921600 +#define BAUD_RATE_PWRUP_7 1152000 + +// OP_WORK_RESTART hash clock change methods. +// +// May be issued *infrequently* by the host to adjust hash clock rate for thermal control +// The "hdata" field, if non zero, contains adjustment instructions. Bits 15:12 of "hdata" +// contain the adjustment type according to the following code, and bits 11:0 contain the +// associated value. Examples: +// hdata = (1<<12)|550 = Set hash clock rate to 550 Mhz +// hdata = (4<<12)|1 = Increase hash clock rate by 1% +// hdata = (6<<12) = Go back to whatever the "original" OP_USB_INIT settings were +// +// Finally, if 4 bytes of "data" follows the OP_WORK_RESTART header, then that data is taken +// as a little endian bitmap, bit set = enable clock change to that die, bit clear = don't +// change clock on that die, i.e. considered as a uint32_t, then 0x1 = die 0, 0x2 = die 1 etc. + +#define WR_NO_CHANGE 0 +#define WR_CLOCK_VALUE 1 +#define WR_MHZ_INCREASE 2 +#define WR_MHZ_DECREASE 3 +#define WR_PERCENT_INCREASE 4 +#define WR_PERCENT_DECREASE 5 +#define WR_REVERT 6 + +#define WR_COMMAND_SHIFT 12 + +// Structure definitions, LE platforms + +#if __BYTE_ORDER == __BIG_ENDIAN && !defined(WIN32) +#include "hf_protocol_be.h" +#else +// Generic header +struct hf_header { + uint8_t preamble; // Always 0xaa + uint8_t operation_code; + uint8_t chip_address; + uint8_t core_address; + uint16_t hdata; // Header specific data + uint8_t data_length; // .. of data frame to follow, in 4 byte blocks, 0=no data + uint8_t crc8; // Computed across bytes 1-6 inclusive +} __attribute__((packed,aligned(4))); // 8 bytes total + +// Header specific to OP_PLL_CONFIG +struct hf_pll_config { + uint8_t preamble; + uint8_t operation_code; + uint8_t chip_address; + + uint8_t pll_divr:6; + uint8_t pll_bypass:1; + uint8_t pll_reset:1; + + uint8_t pll_divf; + + uint8_t spare1:1; // Must always be 0 + uint8_t pll_divq:3; + uint8_t pll_range:3; + uint8_t pll_fse:1; // Must always be 1 + + uint8_t data_length; // Always 0 + uint8_t crc8; // Computed across bytes 1-6 inclusive +} __attribute__((packed,aligned(4))); // 8 bytes total + +// OP_HASH serial data +struct hf_hash_serial { + uint8_t midstate[32]; // Computed from first half of block header + uint8_t merkle_residual[4]; // From block header + uint32_t timestamp; // From block header + uint32_t bits; // Actual difficulty target for block header + uint32_t starting_nonce; // Usually set to 0 + uint32_t nonce_loops; // How many nonces to search, or 0 for 2^32 + uint16_t ntime_loops; // How many times to roll timestamp, or 0 + uint8_t search_difficulty; // Search difficulty to use, # of '0' digits required + uint8_t option; + uint8_t group; + uint8_t spare3[3]; +} __attribute__((packed,aligned(4))); + +// OP_HASH usb data - header+data = 64 bytes +struct hf_hash_usb { + uint8_t midstate[32]; // Computed from first half of block header + uint8_t merkle_residual[4]; // From block header + uint32_t timestamp; // From block header + uint32_t bits; // Actual difficulty target for block header + uint32_t starting_nonce; // Usually set to 0 + uint32_t nonce_loops; // How many nonces to search, or 0 for 2^32 + uint16_t ntime_loops; // How many times to roll timestamp, or 0 + uint8_t search_difficulty; // Search difficulty to use, # of '0' digits required + uint8_t group; // Non-zero for valid group +} __attribute__((packed,aligned(4))); + +// OP_NONCE data +struct hf_candidate_nonce { + uint32_t nonce; // Candidate nonce + uint16_t sequence; // Sequence number from corresponding OP_HASH + uint16_t ntime; // ntime offset, if ntime roll occurred, in LS 12 bits + // If b12 set, search forward next 128 nonces to find solution(s) +} __attribute__((packed,aligned(4))); + +// OP_CONFIG data +struct hf_config_data { + uint16_t status_period:11; // Periodic status time, msec + uint16_t enable_periodic_status:1; // Send periodic status + uint16_t send_status_on_core_idle:1; // Schedule status whenever core goes idle + uint16_t send_status_on_pending_empty:1; // Schedule status whenever core pending goes idle + uint16_t pwm_active_level:1; // Active level of PWM outputs, if used + uint16_t forward_all_privileged_packets:1; // Forward priv pkts -- diagnostic + uint8_t status_batch_delay; // Batching delay, time to wait before sending status + uint8_t watchdog:7; // Watchdog timeout, seconds + uint8_t disable_sensors:1; // Diagnostic + + uint8_t rx_header_timeout:7; // Header timeout in char times + uint8_t rx_ignore_header_crc:1; // Ignore rx header crc's (diagnostic) + uint8_t rx_data_timeout:7; // Data timeout in char times / 16 + uint8_t rx_ignore_data_crc:1; // Ignore rx data crc's (diagnostic) + uint8_t stats_interval:7; // Minimum interval to report statistics (seconds) + uint8_t stat_diagnostic:1; // Never set this + uint8_t measure_interval; // Die temperature measurement interval (msec) + + uint32_t one_usec:12; // How many LF clocks per usec. + uint32_t max_nonces_per_frame:4; // Maximum # of nonces to combine in a single frame + uint32_t voltage_sample_points:8; // Bit mask for sample points (up to 5 bits set) + uint32_t pwm_phases:2; // phases - 1 + uint32_t trim:4; // Trim value for temperature measurements + uint32_t clock_diagnostic:1; // Never set this + uint32_t forward_all_packets:1; // Forward everything - diagnostic. + + uint16_t pwm_period; // Period of PWM outputs, in reference clock cycles + uint16_t pwm_pulse_period; // Initial count, phase 0 +} __attribute__((packed,aligned(4))); + +// OP_GROUP data +struct hf_group_data { + uint16_t nonce_msoffset; // This value << 16 added to starting nonce + uint16_t ntime_offset; // This value added to timestamp +} __attribute__((packed,aligned(4))); + +// Structure of the monitor fields for G-1, returned in OP_STATUS, core bitmap follows this +struct hf_g1_monitor { + uint16_t die_temperature; // Die temperature ADC count + uint8_t core_voltage[6]; // Core voltage + // [0] = main sensor + // [1]-[5] = other positions +} __attribute__((packed,aligned(4))); + +// What comes back in the body of an OP_STATISTICS frame (On die statistics) +struct hf_statistics { + uint8_t rx_header_crc; // Header CRC error's + uint8_t rx_body_crc; // Data CRC error's + uint8_t rx_header_timeouts; // Header timeouts + uint8_t rx_body_timeouts; // Data timeouts + uint8_t core_nonce_fifo_full; // Core nonce Q overrun events + uint8_t array_nonce_fifo_full; // System nonce Q overrun events + uint8_t stats_overrun; // Overrun in statistics reporting + uint8_t spare; +} __attribute__((packed,aligned(4))); + + +//////////////////////////////////////////////////////////////////////////////// +// USB protocol data structures +//////////////////////////////////////////////////////////////////////////////// + +// Convenience header specific to OP_USB_INIT +struct hf_usb_init_header { + uint8_t preamble; // Always 0xaa + uint8_t operation_code; + uint8_t spare1; + + uint8_t protocol:3; // Which protocol to use + uint8_t user_configuration:1; // Use the following configuration data + uint8_t pll_bypass:1; // Force PLL bypass, hash clock = ref clock + uint8_t no_asic_initialization:1; // Do not perform automatic ASIC initialization + uint8_t do_atspeed_core_tests:1; // Do core tests at speed, return second bitmap + uint8_t shed_supported:1; // Host supports gwq status shed_count + + uint16_t hash_clock; // Requested hash clock frequency + + uint8_t data_length; // .. of data frame to follow, in 4 byte blocks + uint8_t crc8; // Computed across bytes 1-6 inclusive +} __attribute__((packed,aligned(4))); // 8 bytes total + +// Options (only if present) that may be appended to the above header +// Each option involving a numerical value will only be in effect if the value is non-zero +// This allows the user to select only those options desired for modification. Do not +// use this facility unless you are an expert - loading inconsistent settings will not work. +struct hf_usb_init_options { + uint16_t group_ntime_roll; // Total ntime roll amount per group + uint16_t core_ntime_roll; // Total core ntime roll amount + uint8_t low_operating_temp_limit; // Lowest normal operating limit + uint8_t high_operating_temp_limit; // Highest normal operating limit + uint16_t spare; +} __attribute__((packed,aligned(4))); + +// Base item returned from device for OP_USB_INIT +struct hf_usb_init_base { + uint16_t firmware_rev; // Firmware revision # + uint16_t hardware_rev; // Hardware revision # + uint32_t serial_number; // Board serial number + uint8_t operation_status; // Reply status for OP_USB_INIT (0 = success) + uint8_t extra_status_1; // Extra reply status information, code specific + uint16_t sequence_modulus; // Sequence numbers are to be modulo this + uint16_t hash_clockrate; // Actual hash clock rate used (nearest Mhz) + uint16_t inflight_target; // Target inflight amount for GWQ protocol +} __attribute__((packed,aligned(4))); + +// The above base item (16 bytes) is followed by the struct hf_config_data (16 bytes) actually +// used internally (so users may modify non-critical fields by doing subsequent +// OP_CONFIG operations). This is followed by a device specific "core good" bitmap (unless the +// user disabled initialization), and optionally by an at-speed "core good" bitmap. + + +// Information in an OP_DIE_STATUS frame. This is for one die - there are four per ASIC. +// Board level phase current and voltage sensors are likely to disappear in later production models. +struct hf_g1_die_data { + struct hf_g1_monitor die; // Die sensors - 8 bytes + uint16_t phase_currents[4]; // Phase currents (0 if unavailable) + uint16_t voltage; // Voltage at device boundary (0 if unavailable) + uint16_t temperature; // Regulator temp sensor + uint16_t tacho; // See documentation + uint16_t spare; +} __attribute__((packed,aligned(4))); // 24 bytes total + + +// Information for an OP_GWQ_STATUS frame +// If sequence_head == sequence_tail, then there is no active work and sequence_head is invalid +struct hf_gwq_data { + uint64_t hash_count; // Add this to host's cumulative hash count + uint16_t sequence_head; // The latest, internal, active sequence # + uint16_t sequence_tail; // The latest, internal, inactive sequence # + uint16_t shed_count; // # of cores have been shedded for thermal control + uint16_t spare; +} __attribute__((packed,aligned(4))); + + +// Information for an OP_USB_STATS1 frame - Communication statistics +struct hf_usb_stats1 { + // USB incoming + uint16_t usb_rx_preambles; + uint16_t usb_rx_receive_byte_errors; + uint16_t usb_rx_bad_hcrc; + + // USB outgoing + uint16_t usb_tx_attempts; + uint16_t usb_tx_packets; + uint16_t usb_tx_timeouts; + uint16_t usb_tx_incompletes; + uint16_t usb_tx_endpointstalled; + uint16_t usb_tx_disconnected; + uint16_t usb_tx_suspended; + + // Internal UART transmit + uint16_t uart_tx_queue_dma; + uint16_t uart_tx_interrupts; + + // Internal UART receive + uint16_t uart_rx_preamble_ints; + uint16_t uart_rx_missed_preamble_ints; + uint16_t uart_rx_header_done; + uint16_t uart_rx_data_done; + uint16_t uart_rx_bad_hcrc; + //uint16_t uart_rx_bad_crc32; + uint16_t uart_rx_bad_dma; + uint16_t uart_rx_short_dma; + uint16_t uart_rx_buffers_full; + + uint8_t max_tx_buffers; // Maximum # of send buffers ever used + uint8_t max_rx_buffers; // Maximum # of receive buffers ever used +} __attribute__((packed,aligned(4))); + +// Information for an OP_USB_NOTICE frame +struct hf_usb_notice_data { + uint32_t extra_data; // Depends on notification code + char message[]; // NULL terminated, little endian byte order +}; +#endif + +#endif diff --git a/hf_protocol_be.h b/hf_protocol_be.h new file mode 100644 index 0000000000..5920d501cb --- /dev/null +++ b/hf_protocol_be.h @@ -0,0 +1,267 @@ +// +// Copyright 2013, 2014 HashFast Technologies LLC +// +// 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. +// +// Big endian versions of packed structures +// +// Version 1.0 +// + +#ifndef _HF_PROTOCOL_BE_H_ +#define _HF_PROTOCOL_BE_H_ + +// Generic header +struct hf_header { + uint8_t preamble; // Always 0xaa + uint8_t operation_code; + uint8_t chip_address; + uint8_t core_address; + uint16_t hdata; // Header specific data + uint8_t data_length; // .. of data frame to follow, in 4 byte blocks, 0=no data + uint8_t crc8; // Computed across bytes 1-6 inclusive +} __attribute__((packed,aligned(4))); // 8 bytes total + +// Header specific to OP_PLL_CONFIG +struct hf_pll_config { + uint8_t preamble; + uint8_t operation_code; + uint8_t chip_address; + + uint8_t pll_reset:1; + uint8_t pll_bypass:1; + uint8_t pll_divr:6; + + uint8_t pll_divf; + + uint8_t pll_fse:1; // Must always be 1 + uint8_t pll_range:3; + uint8_t pll_divq:3; + uint8_t spare1:1; // Must always be 0 + + uint8_t data_length; // Always 0 + uint8_t crc8; // Computed across bytes 1-6 inclusive +} __attribute__((packed,aligned(4))); // 8 bytes total + +// OP_HASH serial data +struct hf_hash_serial { + uint8_t midstate[32]; // Computed from first half of block header + uint8_t merkle_residual[4]; // From block header + uint32_t timestamp; // From block header + uint32_t bits; // Actual difficulty target for block header + uint32_t starting_nonce; // Usually set to 0 + uint32_t nonce_loops; // How many nonces to search, or 0 for 2^32 + uint16_t ntime_loops; // How many times to roll timestamp, or 0 + uint8_t search_difficulty; // Search difficulty to use, # of '0' digits required + uint8_t option; + uint8_t group; + uint8_t spare3[3]; +} __attribute__((packed,aligned(4))); + +// OP_HASH usb data - header+data = 64 bytes +struct hf_hash_usb { + uint8_t midstate[32]; // Computed from first half of block header + uint8_t merkle_residual[4]; // From block header + uint32_t timestamp; // From block header + uint32_t bits; // Actual difficulty target for block header + uint32_t starting_nonce; // Usually set to 0 + uint32_t nonce_loops; // How many nonces to search, or 0 for 2^32 + uint16_t ntime_loops; // How many times to roll timestamp, or 0 + uint8_t search_difficulty; // Search difficulty to use, # of '0' digits required + uint8_t group; // Non-zero for valid group +} __attribute__((packed,aligned(4))); + +// OP_NONCE data +struct hf_candidate_nonce { + uint32_t nonce; // Candidate nonce + uint16_t sequence; // Sequence number from corresponding OP_HASH + uint16_t ntime; // ntime offset, if ntime roll occurred, in LS 12 bits + // If b12 set, search forward next 128 nonces to find solution(s) +} __attribute__((packed,aligned(4))); + +// OP_CONFIG data +// This is usually internal data only, for serial drivers only +// Users shouldn't normally need to interpret this, but in the event a Big Endian +// user requires access to this data, the following structure will get all +// the fields in the right place, but byte swaps will be required for the +// uint16_t's and the uint32_t. +struct hf_config_data { + uint16_t forward_all_privileged_packets:1; // Forward priv pkts -- diagnostic + uint16_t pwm_active_level:1; // Active level of PWM outputs, if used + uint16_t send_status_on_pending_empty:1; // Schedule status whenever core pending goes idle + uint16_t send_status_on_core_idle:1; // Schedule status whenever core goes idle + uint16_t enable_periodic_status:1; // Send periodic status + uint16_t status_period:11; // Periodic status time, msec + + uint8_t status_batch_delay; // Batching delay, time to wait before sending status + uint8_t disable_sensors:1; // Diagnostic + uint8_t watchdog:7; // Watchdog timeout, seconds + + uint8_t rx_ignore_header_crc:1; // Ignore rx header crc's (diagnostic) + uint8_t rx_header_timeout:7; // Header timeout in char times + uint8_t rx_ignore_data_crc:1; // Ignore rx data crc's (diagnostic) + uint8_t rx_data_timeout:7; // Data timeout in char times / 16 + uint8_t stat_diagnostic:1; // Never set this + uint8_t stats_interval:7; // Minimum interval to report statistics (seconds) + uint8_t measure_interval; // Die temperature measurement interval (msec) + + uint32_t forward_all_packets:1; // Forward everything - diagnostic. + uint32_t clock_diagnostic:1; // Never set this + uint32_t trim:4; // Trim value for temperature measurements + uint32_t pwm_phases:2; // phases - 1 + uint32_t voltage_sample_points:8; // Bit mask for sample points (up to 5 bits set) + uint32_t max_nonces_per_frame:4; // Maximum # of nonces to combine in a single frame + uint32_t one_usec:12; // How many LF clocks per usec. + + uint16_t pwm_period; // Period of PWM outputs, in reference clock cycles + uint16_t pwm_pulse_period; // Initial count, phase 0 +} __attribute__((packed,aligned(4))); + +// OP_GROUP data +struct hf_group_data { + uint16_t nonce_msoffset; // This value << 16 added to starting nonce + uint16_t ntime_offset; // This value added to timestamp +} __attribute__((packed,aligned(4))); + +// Structure of the monitor fields for G-1, returned in OP_STATUS, core bitmap follows this +struct hf_g1_monitor { + uint16_t die_temperature; // Die temperature ADC count + uint8_t core_voltage[6]; // Core voltage + // [0] = main sensor + // [1]-[5] = other positions +} __attribute__((packed,aligned(4))); + +// What comes back in the body of an OP_STATISTICS frame (On die statistics) +struct hf_statistics { + uint8_t rx_header_crc; // Header CRC error's + uint8_t rx_body_crc; // Data CRC error's + uint8_t rx_header_timeouts; // Header timeouts + uint8_t rx_body_timeouts; // Data timeouts + uint8_t core_nonce_fifo_full; // Core nonce Q overrun events + uint8_t array_nonce_fifo_full; // System nonce Q overrun events + uint8_t stats_overrun; // Overrun in statistics reporting + uint8_t spare; +} __attribute__((packed,aligned(4))); + + +//////////////////////////////////////////////////////////////////////////////// +// USB protocol data structures +//////////////////////////////////////////////////////////////////////////////// + +// Convenience header specific to OP_USB_INIT +struct hf_usb_init_header { + uint8_t preamble; // Always 0xaa + uint8_t operation_code; + uint8_t spare1; + + uint8_t shed_supported:1; // Host supports gwq status shed_count + uint8_t do_atspeed_core_tests:1; // Do core tests at speed, return second bitmap + uint8_t no_asic_initialization:1; // Do not perform automatic ASIC initialization + uint8_t pll_bypass:1; // Force PLL bypass, hash clock = ref clock + uint8_t user_configuration:1; // Use the following configuration data + uint8_t protocol:3; // Which protocol to use + + uint16_t hash_clock; // Requested hash clock frequency + + uint8_t data_length; // .. of data frame to follow, in 4 byte blocks + uint8_t crc8; // Computed across bytes 1-6 inclusive +} __attribute__((packed,aligned(4))); // 8 bytes total + +// Options (only if present) that may be appended to the above header +// Each option involving a numerical value will only be in effect if the value is non-zero +// This allows the user to select only those options desired for modification. Do not +// use this facility unless you are an expert - loading inconsistent settings will not work. +struct hf_usb_init_options { + uint16_t group_ntime_roll; // Total ntime roll amount per group + uint16_t core_ntime_roll; // Total core ntime roll amount + uint8_t low_operating_temp_limit; // Lowest normal operating limit + uint8_t high_operating_temp_limit; // Highest normal operating limit + uint16_t spare; +} __attribute__((packed,aligned(4))); + +// Base item returned from device for OP_USB_INIT +struct hf_usb_init_base { + uint16_t firmware_rev; // Firmware revision # + uint16_t hardware_rev; // Hardware revision # + uint32_t serial_number; // Board serial number + uint8_t operation_status; // Reply status for OP_USB_INIT (0 = success) + uint8_t extra_status_1; // Extra reply status information, code specific + uint16_t sequence_modulus; // Sequence numbers are to be modulo this + uint16_t hash_clockrate; // Actual hash clock rate used (nearest Mhz) + uint16_t inflight_target; // Target inflight amount for GWQ protocol +} __attribute__((packed,aligned(4))); + +// The above base item (16 bytes) is followed by the struct hf_config_data (16 bytes) actually +// used internally (so users may modify non-critical fields by doing subsequent +// OP_CONFIG operations). This is followed by a device specific "core good" bitmap (unless the +// user disabled initialization), and optionally by an at-speed "core good" bitmap. + + +// Information in an OP_DIE_STATUS frame. This is for one die - there are four per ASIC. +// Board level phase current and voltage sensors are likely to disappear in later production models. +struct hf_g1_die_data { + struct hf_g1_monitor die; // Die sensors - 8 bytes + uint16_t phase_currents[4]; // Phase currents (0 if unavailable) + uint16_t voltage; // Voltage at device boundary (0 if unavailable) + uint16_t temperature; // Regulator temp sensor + uint16_t tacho; // See documentation + uint16_t spare; +} __attribute__((packed,aligned(4))); // 24 bytes total + +// Information for an OP_GWQ_STATUS frame +// If sequence_head == sequence_tail, then there is no active work and sequence_head is invalid +struct hf_gwq_data { + uint64_t hash_count; // Add this to host's cumulative hash count + uint16_t sequence_head; // The latest, internal, active sequence # + uint16_t sequence_tail; // The latest, internal, inactive sequence # + uint16_t shed_count; // # of cores have been shedded for thermal control + uint16_t spare; +} __attribute__((packed,aligned(4))); + + +// Information for an OP_USB_STATS1 frame - Communication statistics +struct hf_usb_stats1 { + // USB incoming + uint16_t usb_rx_preambles; + uint16_t usb_rx_receive_byte_errors; + uint16_t usb_rx_bad_hcrc; + + // USB outgoing + uint16_t usb_tx_attempts; + uint16_t usb_tx_packets; + uint16_t usb_tx_timeouts; + uint16_t usb_tx_incompletes; + uint16_t usb_tx_endpointstalled; + uint16_t usb_tx_disconnected; + uint16_t usb_tx_suspended; + + // Internal UART transmit + uint16_t uart_tx_queue_dma; + uint16_t uart_tx_interrupts; + + // Internal UART receive + uint16_t uart_rx_preamble_ints; + uint16_t uart_rx_missed_preamble_ints; + uint16_t uart_rx_header_done; + uint16_t uart_rx_data_done; + uint16_t uart_rx_bad_hcrc; + //uint16_t uart_rx_bad_crc32; + uint16_t uart_rx_bad_dma; + uint16_t uart_rx_short_dma; + uint16_t uart_rx_buffers_full; + + uint8_t max_tx_buffers; // Maximum # of send buffers ever used + uint8_t max_rx_buffers; // Maximum # of receive buffers ever used +} __attribute__((packed,aligned(4))); + +// Information for an OP_USB_NOTICE frame +struct hf_usb_notice_data { + uint32_t extra_data; // Depends on notification code + char message[]; // NULL terminated, little endian byte order +}; + + +#endif diff --git a/i2c-context.c b/i2c-context.c new file mode 100644 index 0000000000..b3fbe189a3 --- /dev/null +++ b/i2c-context.c @@ -0,0 +1,102 @@ +/* + * generic I2C slave access interface + * + * Copyright 2014 Zefir Kurtisi + * + * 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 +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include "miner.h" +#include "i2c-context.h" + + +static bool i2c_slave_write(struct i2c_ctx *ctx, uint8_t reg, uint8_t val) +{ + union i2c_smbus_data data; + data.byte = val; + + struct i2c_smbus_ioctl_data args; + + args.read_write = I2C_SMBUS_WRITE; + args.command = reg; + args.size = I2C_SMBUS_BYTE_DATA; + args.data = &data; + + if (ioctl(ctx->file, I2C_SMBUS, &args) == -1) { + applog(LOG_INFO, "i2c 0x%02x: failed to write to fdesc %d: %s", + ctx->addr, ctx->file, strerror(errno)); + return false; + } + applog(LOG_DEBUG, "I2C-W(0x%02x/0x%02x)=0x%02x", ctx->addr, reg, val); + return true; +} + +static bool i2c_slave_read(struct i2c_ctx *ctx, uint8_t reg, uint8_t *val) +{ + union i2c_smbus_data data; + struct i2c_smbus_ioctl_data args; + + args.read_write = I2C_SMBUS_READ; + args.command = reg; + args.size = I2C_SMBUS_BYTE_DATA; + args.data = &data; + + if (ioctl(ctx->file, I2C_SMBUS, &args) == -1) { + applog(LOG_INFO, "i2c 0x%02x: failed to read from fdesc %d: %s", + ctx->addr, ctx->file, strerror(errno)); + return false; + } + *val = data.byte; + applog(LOG_DEBUG, "I2C-R(0x%02x/0x%02x)=0x%02x", ctx->addr, reg, *val); + return true; +} + +static void i2c_slave_exit(struct i2c_ctx *ctx) +{ + if (ctx->file == -1) + return; + close(ctx->file); + free(ctx); +} + +extern struct i2c_ctx *i2c_slave_open(char *i2c_bus, uint8_t slave_addr) +{ + int file = open(i2c_bus, O_RDWR); + if (file < 0) { + applog(LOG_INFO, "Failed to open i2c-1: %s", strerror(errno)); + return NULL; + } + + if (ioctl(file, I2C_SLAVE, slave_addr) < 0) { + close(file); + return NULL; + } + struct i2c_ctx *ctx = malloc(sizeof(*ctx)); + assert(ctx != NULL); + + ctx->addr = slave_addr; + ctx->file = file; + ctx->exit = i2c_slave_exit; + ctx->read = i2c_slave_read; + ctx->write = i2c_slave_write; + return ctx; +} + diff --git a/i2c-context.h b/i2c-context.h new file mode 100644 index 0000000000..e39d5daffa --- /dev/null +++ b/i2c-context.h @@ -0,0 +1,26 @@ +#ifndef I2C_CONTEXT_H +#define I2C_CONTEXT_H + +#include +#include + +/* common i2c context */ +struct i2c_ctx { + /* destructor */ + void (*exit)(struct i2c_ctx *ctx); + /* write one byte to given register */ + bool (*write)(struct i2c_ctx *ctx, uint8_t reg, uint8_t val); + /* read one byte from given register */ + bool (*read)(struct i2c_ctx *ctx, uint8_t reg, uint8_t *val); + + /* common data */ + uint8_t addr; + int file; +}; + +/* the default I2C bus on RPi */ +#define I2C_BUS "/dev/i2c-1" + +extern struct i2c_ctx *i2c_slave_open(char *i2c_bus, uint8_t slave_addr); + +#endif /* I2C_CONTEXT_H */ diff --git a/klist.c b/klist.c new file mode 100644 index 0000000000..a552017f0a --- /dev/null +++ b/klist.c @@ -0,0 +1,430 @@ +/* + * Copyright 2013-2014 Andrew Smith - BlackArrow Ltd + * + * 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 + +static void k_alloc_items(K_LIST *list, KLIST_FFL_ARGS) +{ + K_ITEM *item; + int allocate, i; + + if (list->is_store) { + quithere(1, "List %s store can't %s()" KLIST_FFL, + list->name, __func__, KLIST_FFL_PASS); + } + + if (list->limit > 0 && list->total >= list->limit) + return; + + allocate = list->allocate; + if (list->limit > 0 && (list->total + allocate) > list->limit) + allocate = list->limit - list->total; + + list->item_mem_count++; + if (!(list->item_memory = realloc(list->item_memory, + list->item_mem_count * sizeof(*(list->item_memory))))) { + quithere(1, "List %s item_memory failed to realloc count=%d", + list->name, list->item_mem_count); + } + item = calloc(allocate, sizeof(*item)); + if (!item) { + quithere(1, "List %s failed to calloc %d new items - total was %d, limit was %d", + list->name, allocate, list->total, list->limit); + } + list->item_memory[list->item_mem_count - 1] = (void *)item; + + list->total += allocate; + list->count = allocate; + list->count_up = allocate; + + item[0].name = list->name; + item[0].prev = NULL; + item[0].next = &(item[1]); + for (i = 1; i < allocate-1; i++) { + item[i].name = list->name; + item[i].prev = &item[i-1]; + item[i].next = &item[i+1]; + } + item[allocate-1].name = list->name; + item[allocate-1].prev = &(item[allocate-2]); + item[allocate-1].next = NULL; + + list->head = item; + if (list->do_tail) + list->tail = &(item[allocate-1]); + + item = list->head; + while (item) { + list->data_mem_count++; + if (!(list->data_memory = realloc(list->data_memory, + list->data_mem_count * + sizeof(*(list->data_memory))))) { + quithere(1, "List %s data_memory failed to realloc count=%d", + list->name, list->data_mem_count); + } + item->data = calloc(1, list->siz); + if (!(item->data)) + quithere(1, "List %s failed to calloc item data", list->name); + list->data_memory[list->data_mem_count - 1] = (void *)(item->data); + item = item->next; + } +} + +K_STORE *k_new_store(K_LIST *list) +{ + K_STORE *store; + + store = calloc(1, sizeof(*store)); + if (!store) + quithere(1, "Failed to calloc store for %s", list->name); + + store->is_store = true; + store->lock = list->lock; + store->name = list->name; + store->do_tail = list->do_tail; + + return store; +} + +K_LIST *_k_new_list(const char *name, size_t siz, int allocate, int limit, bool do_tail, KLIST_FFL_ARGS) +{ + K_LIST *list; + + if (allocate < 1) + quithere(1, "Invalid new list %s with allocate %d must be > 0", name, allocate); + + if (limit < 0) + quithere(1, "Invalid new list %s with limit %d must be >= 0", name, limit); + + list = calloc(1, sizeof(*list)); + if (!list) + quithere(1, "Failed to calloc list %s", name); + + list->is_store = false; + + list->lock = calloc(1, sizeof(*(list->lock))); + if (!(list->lock)) + quithere(1, "Failed to calloc lock for list %s", name); + + cglock_init(list->lock); + + list->name = name; + list->siz = siz; + list->allocate = allocate; + list->limit = limit; + list->do_tail = do_tail; + + k_alloc_items(list, KLIST_FFL_PASS); + + return list; +} + +/* + * Unlink and return the head of the list + * If the list is empty: + * 1) If it's a store - return NULL + * 2) alloc a new list and return the head - + * which is NULL if the list limit has been reached + */ +K_ITEM *_k_unlink_head(K_LIST *list, KLIST_FFL_ARGS) +{ + K_ITEM *item; + + if (!(list->head) && !(list->is_store)) + k_alloc_items(list, KLIST_FFL_PASS); + + if (!(list->head)) + return NULL; + + item = list->head; + list->head = item->next; + if (list->head) + list->head->prev = NULL; + else { + if (list->do_tail) + list->tail = NULL; + } + + item->prev = item->next = NULL; + + list->count--; + + return item; +} + +// Zeros the head returned +K_ITEM *_k_unlink_head_zero(K_LIST *list, KLIST_FFL_ARGS) +{ + K_ITEM *item; + + item = _k_unlink_head(list, KLIST_FFL_PASS); + + if (item) + memset(item->data, 0, list->siz); + + return item; +} + +// Returns NULL if empty +K_ITEM *_k_unlink_tail(K_LIST *list, KLIST_FFL_ARGS) +{ + K_ITEM *item; + + if (!(list->do_tail)) { + quithere(1, "List %s can't %s() - do_tail is false" KLIST_FFL, + list->name, __func__, KLIST_FFL_PASS); + } + + if (!(list->tail)) + return NULL; + + item = list->tail; + list->tail = item->prev; + if (list->tail) + list->tail->next = NULL; + else + list->head = NULL; + + item->prev = item->next = NULL; + + list->count--; + + return item; +} + +void _k_add_head(K_LIST *list, K_ITEM *item, KLIST_FFL_ARGS) +{ + if (item->name != list->name) { + quithere(1, "List %s can't %s() a %s item" KLIST_FFL, + list->name, __func__, item->name, KLIST_FFL_PASS); + } + + item->prev = NULL; + item->next = list->head; + if (list->head) + list->head->prev = item; + + list->head = item; + + if (list->do_tail) { + if (!(list->tail)) + list->tail = item; + } + + list->count++; + list->count_up++; +} + +/* slows it down (of course) - only for debugging +void _k_free_head(K_LIST *list, K_ITEM *item, KLIST_FFL_ARGS) +{ + memset(item->data, 0xff, list->siz); + _k_add_head(list, item, KLIST_FFL_PASS); +} +*/ + +void _k_add_tail(K_LIST *list, K_ITEM *item, KLIST_FFL_ARGS) +{ + if (item->name != list->name) { + quithere(1, "List %s can't %s() a %s item" KLIST_FFL, + list->name, __func__, item->name, KLIST_FFL_PASS); + } + + if (!(list->do_tail)) { + quithere(1, "List %s can't %s() - do_tail is false" KLIST_FFL, + list->name, __func__, KLIST_FFL_PASS); + } + + item->prev = list->tail; + item->next = NULL; + if (list->tail) + list->tail->next = item; + + list->tail = item; + + if (!(list->head)) + list->head = item; + + list->count++; + list->count_up++; +} + +void _k_insert_before(K_LIST *list, K_ITEM *item, K_ITEM *before, KLIST_FFL_ARGS) +{ + if (item->name != list->name) { + quithere(1, "List %s can't %s() a %s item" KLIST_FFL, + list->name, __func__, item->name, KLIST_FFL_PASS); + } + + if (!before) { + quithere(1, "%s() (%s) can't before a null item" KLIST_FFL, + __func__, list->name, KLIST_FFL_PASS); + } + + item->next = before; + item->prev = before->prev; + if (before->prev) + before->prev->next = item; + else + list->head = item; + before->prev = item; + + list->count++; + list->count_up++; +} + +void _k_insert_after(K_LIST *list, K_ITEM *item, K_ITEM *after, KLIST_FFL_ARGS) +{ + if (item->name != list->name) { + quithere(1, "List %s can't %s() a %s item" KLIST_FFL, + list->name, __func__, item->name, KLIST_FFL_PASS); + } + + if (!after) { + quithere(1, "%s() (%s) can't after a null item" KLIST_FFL, + __func__, list->name, KLIST_FFL_PASS); + } + + item->prev = after; + item->next = after->next; + if (after->next) + after->next->prev = item; + else { + if (list->do_tail) + list->tail = item; + } + after->next = item; + + list->count++; + list->count_up++; +} + +void _k_unlink_item(K_LIST *list, K_ITEM *item, KLIST_FFL_ARGS) +{ + if (item->name != list->name) { + quithere(1, "List %s can't %s() a %s item" KLIST_FFL, + list->name, __func__, item->name, KLIST_FFL_PASS); + } + + if (item->prev) + item->prev->next = item->next; + + if (item->next) + item->next->prev = item->prev; + + if (list->head == item) + list->head = item->next; + + if (list->do_tail) { + if (list->tail == item) + list->tail = item->prev; + } + + item->prev = item->next = NULL; + + list->count--; +} + +void _k_list_transfer_to_head(K_LIST *from, K_LIST *to, KLIST_FFL_ARGS) +{ + if (from->name != to->name) { + quithere(1, "List %s can't %s() to a %s list" KLIST_FFL, + from->name, __func__, to->name, KLIST_FFL_PASS); + } + + if (!(from->do_tail)) { + quithere(1, "List %s can't %s() - do_tail is false" KLIST_FFL, + from->name, __func__, KLIST_FFL_PASS); + } + + if (!(from->head)) + return; + + if (to->head) + to->head->prev = from->tail; + else + to->tail = from->tail; + + from->tail->next = to->head; + to->head = from->head; + + from->head = from->tail = NULL; + to->count += from->count; + from->count = 0; + to->count_up += from->count_up; + from->count_up = 0; +} + +void _k_list_transfer_to_tail(K_LIST *from, K_LIST *to, KLIST_FFL_ARGS) +{ + if (from->name != to->name) { + quithere(1, "List %s can't %s() to a %s list" KLIST_FFL, + from->name, __func__, to->name, KLIST_FFL_PASS); + } + + if (!(from->do_tail)) { + quithere(1, "List %s can't %s() - do_tail is false" KLIST_FFL, + from->name, __func__, KLIST_FFL_PASS); + } + + if (!(from->head)) + return; + + if (to->tail) + to->tail->next = from->head; + else + to->head = from->head; + + from->head->prev = to->tail; + to->tail = from->tail; + + from->head = from->tail = NULL; + to->count += from->count; + from->count = 0; + to->count_up += from->count_up; + from->count_up = 0; +} + +K_LIST *_k_free_list(K_LIST *list, KLIST_FFL_ARGS) +{ + int i; + + if (list->is_store) { + quithere(1, "List %s can't %s() a store" KLIST_FFL, + list->name, __func__, KLIST_FFL_PASS); + } + + for (i = 0; i < list->item_mem_count; i++) + free(list->item_memory[i]); + free(list->item_memory); + + for (i = 0; i < list->data_mem_count; i++) + free(list->data_memory[i]); + free(list->data_memory); + + cglock_destroy(list->lock); + + free(list->lock); + + free(list); + + return NULL; +} + +K_STORE *_k_free_store(K_STORE *store, KLIST_FFL_ARGS) +{ + if (!(store->is_store)) { + quithere(1, "Store %s can't %s() the list" KLIST_FFL, + store->name, __func__, KLIST_FFL_PASS); + } + + free(store); + + return NULL; +} diff --git a/klist.h b/klist.h new file mode 100644 index 0000000000..a79a4edfa8 --- /dev/null +++ b/klist.h @@ -0,0 +1,94 @@ +/* + * Copyright 2013-2014 Andrew Smith - BlackArrow Ltd + * + * 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 KLIST_H +#define KLIST_H + +#include + +#define KLIST_FFL " - from %s %s() line %d" +#define KLIST_FFL_HERE __FILE__, __func__, __LINE__ +#define KLIST_FFL_PASS file, func, line +#define KLIST_FFL_ARGS __maybe_unused const char *file, \ + __maybe_unused const char *func, \ + __maybe_unused const int line + +typedef struct k_item { + const char *name; + struct k_item *prev; + struct k_item *next; + void *data; +} K_ITEM; + +typedef struct k_list { + const char *name; + bool is_store; + cglock_t *lock; + struct k_item *head; + struct k_item *tail; + size_t siz; // item data size + int total; // total allocated + int count; // in this list + int count_up; // incremented every time one is added + int allocate; // number to intially allocate and each time we run out + int limit; // total limit - 0 means unlimited + bool do_tail; // track the tail? + int item_mem_count; // how many item memory buffers have been allocated + void **item_memory; // allocated item memory buffers + int data_mem_count; // how many item data memory buffers have been allocated + void **data_memory; // allocated item data memory buffers +} K_LIST; + +/* + * K_STORE is for a list of items taken from a K_LIST + * The restriction is, a K_STORE must not allocate new items, + * only the K_LIST should do that + * i.e. all K_STORE items came from a K_LIST + */ +#define K_STORE K_LIST + +/* + * N.B. all locking is done in the code using the K_*LOCK macros + */ +#define K_WLOCK(_list) cg_wlock(_list->lock) +#define K_WUNLOCK(_list) cg_wunlock(_list->lock) +#define K_RLOCK(_list) cg_rlock(_list->lock) +#define K_RUNLOCK(_list) cg_runlock(_list->lock) + +extern K_STORE *k_new_store(K_LIST *list); +extern K_LIST *_k_new_list(const char *name, size_t siz, int allocate, int limit, bool do_tail, KLIST_FFL_ARGS); +#define k_new_list(_name, _siz, _allocate, _limit, _do_tail) _k_new_list(_name, _siz, _allocate, _limit, _do_tail, KLIST_FFL_HERE) +extern K_ITEM *_k_unlink_head(K_LIST *list, KLIST_FFL_ARGS); +#define k_unlink_head(_list) _k_unlink_head(_list, KLIST_FFL_HERE) +extern K_ITEM *_k_unlink_head_zero(K_LIST *list, KLIST_FFL_ARGS); +#define k_unlink_head_zero(_list) _k_unlink_head_zero(_list, KLIST_FFL_HERE) +extern K_ITEM *_k_unlink_tail(K_LIST *list, KLIST_FFL_ARGS); +#define k_unlink_tail(_list) _k_unlink_tail(_list, KLIST_FFL_HERE) +extern void _k_add_head(K_LIST *list, K_ITEM *item, KLIST_FFL_ARGS); +#define k_add_head(_list, _item) _k_add_head(_list, _item, KLIST_FFL_HERE) +// extern void k_free_head(K_LIST *list, K_ITEM *item, KLIST_FFL_ARGS); +#define k_free_head(__list, __item) _k_add_head(__list, __item, KLIST_FFL_HERE) +extern void _k_add_tail(K_LIST *list, K_ITEM *item, KLIST_FFL_ARGS); +#define k_add_tail(_list, _item) _k_add_tail(_list, _item, KLIST_FFL_HERE) +extern void _k_insert_before(K_LIST *list, K_ITEM *item, K_ITEM *before, KLIST_FFL_ARGS); +#define k_insert_before(_list, _item, _before) _k_insert_before(_list, _item, _before, KLIST_FFL_HERE) +extern void _k_insert_after(K_LIST *list, K_ITEM *item, K_ITEM *after, KLIST_FFL_ARGS); +#define k_insert_after(_list, _item, _after) _k_insert_after(_list, _item, _after, KLIST_FFL_HERE) +extern void _k_unlink_item(K_LIST *list, K_ITEM *item, KLIST_FFL_ARGS); +#define k_unlink_item(_list, _item) _k_unlink_item(_list, _item, KLIST_FFL_HERE) +void _k_list_transfer_to_head(K_LIST *from, K_LIST *to, KLIST_FFL_ARGS); +#define k_list_transfer_to_head(_from, _to) _k_list_transfer_to_head(_from, _to, KLIST_FFL_HERE) +void _k_list_transfer_to_tail(K_LIST *from, K_LIST *to, KLIST_FFL_ARGS); +#define k_list_transfer_to_tail(_from, _to) _k_list_transfer_to_tail(_from, _to, KLIST_FFL_HERE) +extern K_LIST *_k_free_list(K_LIST *list, KLIST_FFL_ARGS); +#define k_free_list(_list) _k_free_list(_list, KLIST_FFL_HERE) +extern K_STORE *_k_free_store(K_STORE *store, KLIST_FFL_ARGS); +#define k_free_store(_store) _k_free_store(_store, KLIST_FFL_HERE) + +#endif diff --git a/knc-asic.c b/knc-asic.c new file mode 100644 index 0000000000..7c164f9a61 --- /dev/null +++ b/knc-asic.c @@ -0,0 +1,551 @@ +/* + * library for KnCminer devices + * + * Copyright 2014 KnCminer + * + * 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 +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include "miner.h" +#include "logging.h" + +#include "knc-transport.h" + +#include "knc-asic.h" + +/* Control Commands + * + * SPI command on channel. 1- + * 1'b1 3'channel 12'msglen_in_bits SPI message data + * Sends the supplied message on selected SPI bus + * + * Communication test + * 16'h1 16'x + * Simple test of SPI communication + * + * LED control + * 4'h1 4'red 4'green 4'blue + * Sets led colour + * + * Clock frequency + * 4'h2 12'msglen_in_bits 4'channel 4'die 16'MHz 512'x + * Configures the hashing clock rate + */ + +/* ASIC Command structure + * command 8 bits + * chip 8 bits + * core 16 bits + * data [command dependent] + * CRC32 32 bits (Neptune) + * + * ASIC response starts immediately after core address bits. + * + * response data + * CRC32 32 bits (Neptune) + * STATUS 8 bits 1 0 ~CRC_OK 0 0 ACCEPTED_WORK 0 1 (Neptune) + * + * Requests + * + * SETWORK (Jupiter) + * midstate 256 bits + * data 96 bits + * + * SETWORK/SETWORK_CLEAN (Neptune) + * slot | 0xf0 8 bits + * precalc_midstate 192 bits + * precalc_data 96 bits + * midstate 256 bits + * + * Returns REPORT response on Neptune + * + * Responses + * + * GETINFO + * + * (core field unused) + * + * cores 16 bits + * version 16 bits + * reserved 60 bits (Neptune) + * die_status 4 bits (Neptune) + * 1' pll_locked + * 1' hash_reset_n 1 if cores have been reset since last report + * 1' pll_reset_n 1 if PLL have been reset since last report + * 1' pll_power_down + * core_status cores * 2 bits (Neptune) rounded up to bytes + * 1' want_work + * 1' has_report (unreliable) + * + * REPORT + * + * reserved 2 bits + * next_state 1 bit next work state loaded + * state 1 bit hashing (0 on Jupiter) + * next_slot 4 bit slot id of next work state (0 on Jupiter) + * progress 8 bits upper 8 bits of nonce counter + * active_slot 4 bits slot id of current work state + * nonce_slot 4 bits slot id of found nonce + * nonce 32 bits + * + * reserved 4 bits + * nonce_slot 4 bits + * nonce 32 bits + * + * repeat for 5 nonce entries in total on Neptune + * Jupiter only has first nonce entry + */ + +// Precalculate first 3 rounds of SHA256 - as much as possible +// Macro routines copied from sha2.c +static void knc_prepare_neptune_work(unsigned char *out, struct work *work) { + const uint8_t *midstate = work->midstate; + const uint8_t *data = work->data + 16*4; + +#ifndef GET_ULONG_BE +#define GET_ULONG_BE(b,i) \ + (( (uint32_t) (b)[(i) ] << 24 ) \ + | ( (uint32_t) (b)[(i) + 1] << 16 ) \ + | ( (uint32_t) (b)[(i) + 2] << 8 ) \ + | ( (uint32_t) (b)[(i) + 3] )) +#endif + +#ifndef GET_ULONG_LE +#define GET_ULONG_LE(b,i) \ + (( (uint32_t) (b)[(i) + 3] << 24 ) \ + | ( (uint32_t) (b)[(i) + 2] << 16 ) \ + | ( (uint32_t) (b)[(i) + 1] << 8 ) \ + | ( (uint32_t) (b)[(i) + 0] )) +#endif + +#ifndef PUT_ULONG_BE +#define PUT_ULONG_BE(n,b,i) \ + { \ + (b)[(i) ] = (unsigned char) ( (n) >> 24 ); \ + (b)[(i) + 1] = (unsigned char) ( (n) >> 16 ); \ + (b)[(i) + 2] = (unsigned char) ( (n) >> 8 ); \ + (b)[(i) + 3] = (unsigned char) ( (n) ); \ + } +#endif + +#ifndef PUT_ULONG_LE +#define PUT_ULONG_LE(n,b,i) \ + { \ + (b)[(i) + 3] = (unsigned char) ( (n) >> 24 ); \ + (b)[(i) + 2] = (unsigned char) ( (n) >> 16 ); \ + (b)[(i) + 1] = (unsigned char) ( (n) >> 8 ); \ + (b)[(i) + 0] = (unsigned char) ( (n) ); \ + } +#endif + +#define SHR(x,n) ((x & 0xFFFFFFFF) >> n) +#define ROTR(x,n) (SHR(x,n) | (x << (32 - n))) + +#define S0(x) (ROTR(x, 7) ^ ROTR(x,18) ^ SHR(x, 3)) +#define S1(x) (ROTR(x,17) ^ ROTR(x,19) ^ SHR(x,10)) + +#define S2(x) (ROTR(x, 2) ^ ROTR(x,13) ^ ROTR(x,22)) +#define S3(x) (ROTR(x, 6) ^ ROTR(x,11) ^ ROTR(x,25)) + +#define F0(x,y,z) ((x & y) | (z & (x | y))) +#define F1(x,y,z) (z ^ (x & (y ^ z))) + +#define R(t) \ +( \ + W[t] = S1(W[t - 2]) + W[t - 7] + \ + S0(W[t - 15]) + W[t - 16] \ +) + +#define P(a,b,c,d,e,f,g,h,x,K) \ + { \ + temp1 = h + S3(e) + F1(e,f,g) + K + x; \ + temp2 = S2(a) + F0(a,b,c); \ + d += temp1; h = temp1 + temp2; \ + } + + uint32_t temp1, temp2, W[16+3]; + uint32_t A, B, C, D, E, F, G, H; + + W[0] = GET_ULONG_LE(data, 0*4 ); + W[1] = GET_ULONG_LE(data, 1*4 ); + W[2] = GET_ULONG_LE(data, 2*4 ); + W[3] = 0; // since S0(0)==0, this must be 0. S0(nonce) is added in hardware. + W[4] = 0x80000000; + W[5] = 0; + W[6] = 0; + W[7] = 0; + W[8] = 0; + W[9] = 0; + W[10] = 0; + W[11] = 0; + W[12] = 0; + W[13] = 0; + W[14] = 0; + W[15] = 0x00000280; + R(16); // Expand W 14, 9, 1, 0 + R(17); // 15, 10, 2, 1 + R(18); // 16, 11, 3, 2 + + A = GET_ULONG_LE(midstate, 0*4 ); + B = GET_ULONG_LE(midstate, 1*4 ); + C = GET_ULONG_LE(midstate, 2*4 ); + D = GET_ULONG_LE(midstate, 3*4 ); + E = GET_ULONG_LE(midstate, 4*4 ); + F = GET_ULONG_LE(midstate, 5*4 ); + G = GET_ULONG_LE(midstate, 6*4 ); + H = GET_ULONG_LE(midstate, 7*4 ); + + uint32_t D_ = D, H_ = H; + P( A, B, C, D_, E, F, G, H_, W[ 0], 0x428A2F98 ); + uint32_t C_ = C, G_ = G; + P( H_, A, B, C_, D_, E, F, G_, W[ 1], 0x71374491 ); + uint32_t B_ = B, F_ = F; + P( G_, H_, A, B_, C_, D_, E, F_, W[ 2], 0xB5C0FBCF ); + + PUT_ULONG_BE( D_, out, 0*4 ); + PUT_ULONG_BE( C_, out, 1*4 ); + PUT_ULONG_BE( B_, out, 2*4 ); + PUT_ULONG_BE( H_, out, 3*4 ); + PUT_ULONG_BE( G_, out, 4*4 ); + PUT_ULONG_BE( F_, out, 5*4 ); + PUT_ULONG_BE( W[18], out, 6*4 ); // This is partial S0(nonce) added by hardware + PUT_ULONG_BE( W[17], out, 7*4 ); + PUT_ULONG_BE( W[16], out, 8*4 ); + PUT_ULONG_BE( H, out, 9*4 ); + PUT_ULONG_BE( G, out, 10*4 ); + PUT_ULONG_BE( F, out, 11*4 ); + PUT_ULONG_BE( E, out, 12*4 ); + PUT_ULONG_BE( D, out, 13*4 ); + PUT_ULONG_BE( C, out, 14*4 ); + PUT_ULONG_BE( B, out, 15*4 ); + PUT_ULONG_BE( A, out, 16*4 ); +} + +static void knc_prepare_jupiter_work(unsigned char *out, struct work *work) { + int i; + for (i = 0; i < 8 * 4; i++) + out[i] = work->midstate[8 * 4 - i - 1]; + for (i = 0; i < 3 * 4; i++) + out[8 * 4 + i] = work->data[16 * 4 + 3 * 4 - i - 1]; +} + +static void knc_prepare_core_command(uint8_t *request, int command, int die, int core) +{ + request[0] = command; + request[1] = die; + request[2] = core >> 8; + request[3] = core & 0xff; +} + +int knc_prepare_report(uint8_t *request, int die, int core) +{ + knc_prepare_core_command(request, KNC_ASIC_CMD_REPORT, die, core); + return 4; +} + +int knc_prepare_info(uint8_t *request, int die, struct knc_die_info *die_info, int *response_size) +{ + request[0] = KNC_ASIC_CMD_GETINFO; + request[1] = die; + request[2] = 0; + request[3] = 0; + switch (die_info->version) { + case KNC_VERSION_JUPITER: + *response_size = 4; + break; + default: + *response_size = 12 + (KNC_MAX_CORES_PER_DIE*2 + 7) / 8; + break; + case KNC_VERSION_NEPTUNE: + *response_size = 12 + (die_info->cores*2 + 7) / 8; + break; + } + return 4; +} + +int knc_prepare_neptune_setwork(uint8_t *request, int die, int core, int slot, struct work *work, int clean) +{ + if (!clean) + knc_prepare_core_command(request, KNC_ASIC_CMD_SETWORK, die, core); + else + knc_prepare_core_command(request, KNC_ASIC_CMD_SETWORK_CLEAN, die, core); + request[4] = slot | 0xf0; + if (work) + knc_prepare_neptune_work(request + 4 + 1, work); + else + memset(request + 4 + 1, 0, 6*4 + 3*4 + 8*4); + return 4 + 1 + 6*4 + 3*4 + 8*4; +} + +int knc_prepare_jupiter_setwork(uint8_t *request, int die, int core, int slot, struct work *work) +{ + knc_prepare_core_command(request, KNC_ASIC_CMD_SETWORK, die, core); + request[4] = slot | 0xf0; + if (work) + knc_prepare_jupiter_work(request + 4 + 1, work); + else + memset(request + 4 + 1, 0, 8*4 + 3*4); + return 4 + 1 + 8*4 + 3*4; +} + +int knc_prepare_jupiter_halt(uint8_t *request, int die, int core) +{ + knc_prepare_core_command(request, KNC_ASIC_CMD_HALT, die, core); + return 4; +} + +int knc_prepare_neptune_halt(uint8_t *request, int die, int core) +{ + knc_prepare_core_command(request, KNC_ASIC_CMD_HALT, die, core); + request[4] = 0 | 0xf0; + memset(request + 4 + 1, 0, 6*4 + 3*4 + 8*4); + return 4 + 1 + 6*4 + 3*4 + 8*4; +} + +void knc_prepare_neptune_message(int request_length, const uint8_t *request, uint8_t *buffer) +{ + uint32_t crc; + memcpy(buffer, request, request_length); + buffer += request_length; + crc = crc32(0, Z_NULL, 0); + crc = crc32(crc, request, request_length); + PUT_ULONG_BE(crc, buffer, 0); +} + +int knc_transfer_length(int request_length, int response_length) +{ + /* FPGA control, request header, request body/response, CRC(4), ACK(1), EXTRA(3) */ + return 2 + MAX(request_length, 4 + response_length ) + 4 + 1 + 3; +} + +int knc_prepare_transfer(uint8_t *txbuf, int offset, int size, int channel, int request_length, const uint8_t *request, int response_length) +{ + /* FPGA control, request header, request body/response, CRC(4), ACK(1), EXTRA(3) */ + int msglen = MAX(request_length, 4 + response_length ) + 4 + 1 + 3; + int len = 2 + msglen; + txbuf += offset; + + if (len + offset > size) { + applog(LOG_DEBUG, "KnC SPI buffer full"); + return -1; + } + txbuf[0] = 1 << 7 | (channel+1) << 4 | (msglen * 8) >> 8; + txbuf[1] = (msglen * 8); + knc_prepare_neptune_message(request_length, request, txbuf+2); + + return offset + len; +} + +/* red, green, blue valid range 0 - 15 */ +int knc_prepare_led(uint8_t *txbuf, int offset, int size, int red, int green, int blue) +{ + /* 4'h1 4'red 4'green 4'blue */ + int len = 2; + txbuf += offset; + + if (len + offset > size) { + applog(LOG_DEBUG, "KnC SPI buffer full"); + return -1; + } + txbuf[0] = 1 << 4 | red; + txbuf[1] = green << 4 | blue; + + return offset + len; + +} + +/* reset controller */ +int knc_prepare_reset(uint8_t *txbuf, int offset, int size) +{ + /* 16'h0002 16'unused */ + int len = 4; + txbuf += offset; + + if (len + offset > size) { + applog(LOG_DEBUG, "KnC SPI buffer full"); + return -1; + } + txbuf[0] = (0x0002) >> 8; + txbuf[1] = (0x0002) & 0xff; + txbuf[2] = 0; + txbuf[3] = 0; + + return offset + len; +} + +/* request_length = 0 disables communication checks, i.e. Jupiter protocol */ +int knc_decode_response(uint8_t *rxbuf, int request_length, uint8_t **response, int response_length) +{ + int ret = 0; + int len = knc_transfer_length(request_length, response_length); + if (request_length > 0 && response_length > 0) { + uint32_t crc, recv_crc; + crc = crc32(0, Z_NULL, 0); + crc = crc32(crc, rxbuf + 2 + 4, response_length); + recv_crc = GET_ULONG_BE(rxbuf + 2 + 4, response_length); + if (crc != recv_crc) + ret |= KNC_ERR_CRC; + } + + if (response) { + if (response_length > 0) { + *response = rxbuf + 2 + 4; + } else { + *response = NULL; + } + } + + if (response_length == 0) + return 0; + + uint8_t ack = rxbuf[len - 4]; + + if ((ack & KNC_ASIC_ACK_MASK) != KNC_ASIC_ACK_MATCH) + ret |= KNC_ERR_ACK; + if ((ack & KNC_ASIC_ACK_CRC)) + ret |= KNC_ERR_CRCACK; + if ((ack & KNC_ASIC_ACK_ACCEPT)) + ret |= KNC_ACCEPTED; + if (ret && memcmp(&rxbuf[len-4], "\377\377\377\377", 4) == 0) + ret = KNC_ERR_UNAVAIL; + return ret; +} + +int knc_syncronous_transfer(void *ctx, int channel, int request_length, const uint8_t *request, int response_length, uint8_t *response) +{ + int len = knc_transfer_length(request_length, response_length); + uint8_t txbuf[len]; + uint8_t rxbuf[len]; + memset(txbuf, 0, len); + knc_prepare_transfer(txbuf, 0, len, channel, request_length, request, response_length); + knc_trnsp_transfer(ctx, txbuf, rxbuf, len); + + uint8_t *response_buf; + int rc = knc_decode_response(rxbuf, request_length, &response_buf, response_length); + if (response) + memcpy(response, response_buf, response_length); + return rc; +} + +int knc_decode_info(uint8_t *response, struct knc_die_info *die_info) +{ + int cores_in_die = response[0]<<8 | response[1]; + int version = response[2]<<8 | response[3]; + if (version == KNC_ASIC_VERSION_JUPITER && cores_in_die <= 48) { + die_info->version = KNC_VERSION_JUPITER; + die_info->cores = cores_in_die; + memset(die_info->want_work, -1, cores_in_die); + die_info->pll_power_down = -1; + die_info->pll_reset_n = -1; + die_info->hash_reset_n = -1; + die_info->pll_locked = -1; + return 0; + } else if (version == KNC_ASIC_VERSION_NEPTUNE && cores_in_die <= KNC_MAX_CORES_PER_DIE) { + die_info->version = KNC_VERSION_NEPTUNE; + die_info->cores = cores_in_die; + int core; + for (core = 0; core < cores_in_die; core++) + die_info->want_work[core] = ((response[12 + core/4] >> ((3-(core % 4)) * 2)) >> 1) & 1; + int die_status = response[11] & 0xf; + die_info->pll_power_down = (die_status >> 0) & 1; + die_info->pll_reset_n = (die_status >> 1) & 1; + die_info->hash_reset_n = (die_status >> 2) & 1; + die_info->pll_locked = (die_status >> 3) & 1; + return 0; + } else { + return -1; + } +} + +int knc_decode_report(uint8_t *response, struct knc_report *report, int version) +{ +/* + * reserved 2 bits + * next_state 1 bit next work state loaded + * state 1 bit hashing (0 on Jupiter) + * next_slot 4 bit slot id of next work state (0 on Jupiter) + * progress 8 bits upper 8 bits of nonce counter + * active_slot 4 bits slot id of current work state + * nonce_slot 4 bits slot id of found nonce + * nonce 32 bits + * + * reserved 4 bits + * nonce_slot 4 bits + * nonce 32 bits + */ + report->next_state = (response[0] >> 5) & 1; + if (version != KNC_VERSION_JUPITER) { + report->state = (response[0] >> 4) & 1; + report->next_slot = response[0] & ((1<<4)-1); + } else { + report->state = -1; + report->next_slot = -1; + } + report->progress = (uint32_t)response[1] << 24; + report->active_slot = (response[2] >> 4) & ((1<<4)-1); + int n; + int n_nonces = version == KNC_VERSION_JUPITER ? 1 : 5; + for (n = 0; n < n_nonces; n++) { + report->nonce[n].slot = response[2+n*5] & ((1<<4)-1); + report->nonce[n].nonce = + (uint32_t)response[3+n*5] << 24 | + (uint32_t)response[4+n*5] << 16 | + (uint32_t)response[5+n*5] << 8 | + (uint32_t)response[6+n*5] << 0 | + 0; + } + for (; n < KNC_NONCES_PER_REPORT; n++) { + report->nonce[n].slot = -1; + report->nonce[n].nonce = 0; + } + return 0; +} + +int knc_detect_die(void *ctx, int channel, int die, struct knc_die_info *die_info) +{ + uint8_t request[4]; + int response_len = 2 + 2 + 4 + 4 + (KNC_MAX_CORES_PER_DIE*2 + 7) / 8; + uint8_t response[response_len]; + + int request_len = knc_prepare_info(request, die, die_info, &response_len); + int status = knc_syncronous_transfer(ctx, channel, request_len, request, response_len, response); + + /* Workaround for pre-ASIC version */ + int cores_in_die = response[0]<<8 | response[1]; + int version = response[2]<<8 | response[3]; + if (version == KNC_ASIC_VERSION_NEPTUNE && cores_in_die < KNC_MAX_CORES_PER_DIE) { + applog(LOG_DEBUG, "KnC %d-%d: Looks like a NEPTUNE die with %d cores", channel, die, cores_in_die); + /* Try again with right response size */ + response_len = 2 + 2 + 4 + 4 + (cores_in_die*2 + 7) / 8; + status = knc_syncronous_transfer(ctx, channel, request_len, request, response_len, response); + } + int rc = -1; + if (version == KNC_ASIC_VERSION_JUPITER || status == 0) + rc = knc_decode_info(response, die_info); + if (rc == 0) + applog(LOG_INFO, "KnC %d-%d: Found %s die with %d cores", channel, die, + die_info->version == KNC_VERSION_NEPTUNE ? "NEPTUNE" : + die_info->version == KNC_VERSION_JUPITER ? "JUPITER" : + "UNKNOWN", + cores_in_die); + else + applog(LOG_DEBUG, "KnC %d-%d: No KnC chip found", channel, die); + return rc; +} + diff --git a/knc-asic.h b/knc-asic.h new file mode 100644 index 0000000000..7c48317702 --- /dev/null +++ b/knc-asic.h @@ -0,0 +1,88 @@ +#ifndef _CGMINER_NEPTUNE_H +#define _CGMINER_NEPTUNE_H +#include +#include "miner.h" + +/* ASIC Command codes */ +#define KNC_ASIC_CMD_GETINFO 0x80 +#define KNC_ASIC_CMD_SETWORK 0x81 +#define KNC_ASIC_CMD_SETWORK_CLEAN 0x83 /* Neptune */ +#define KNC_ASIC_CMD_HALT 0x83 /* Jupiter */ +#define KNC_ASIC_CMD_REPORT 0x82 + +/* Status byte */ +#define KNC_ASIC_ACK_CRC (1<<5) +#define KNC_ASIC_ACK_ACCEPT (1<<2) +#define KNC_ASIC_ACK_MASK (~(KNC_ASIC_ACK_CRC|KNC_ASIC_ACK_ACCEPT)) +#define KNC_ASIC_ACK_MATCH ((1<<7)|(1<<0)) + +/* Version word */ +#define KNC_ASIC_VERSION_JUPITER 0xa001 +#define KNC_ASIC_VERSION_NEPTUNE 0xa002 + +/* Limits of current chips & I/O board */ +#define KNC_MAX_CORES_PER_DIE 360 +#define KNC_MAX_ASICS 6 + +struct knc_die_info { + enum { + KNC_VERSION_UNKNOWN = 0, + KNC_VERSION_JUPITER, + KNC_VERSION_NEPTUNE + } version; + char want_work[KNC_MAX_CORES_PER_DIE]; + int cores; + int pll_locked; + int hash_reset_n; + int pll_reset_n; + int pll_power_down; +}; + +#define KNC_NONCES_PER_REPORT 5 + +struct knc_report { + int next_state; + int state; + int next_slot; + int active_slot; + uint32_t progress; + struct { + int slot; + uint32_t nonce; + } nonce[KNC_NONCES_PER_REPORT]; +}; + +int knc_prepare_info(uint8_t *request, int die, struct knc_die_info *die_info, int *response_size); +int knc_prepare_report(uint8_t *request, int die, int core); +int knc_prepare_neptune_setwork(uint8_t *request, int die, int core, int slot, struct work *work, int clean); +int knc_prepare_jupiter_setwork(uint8_t *request, int die, int core, int slot, struct work *work); +int knc_prepare_jupiter_halt(uint8_t *request, int die, int core); +int knc_prepare_neptune_halt(uint8_t *request, int die, int core); + +int knc_decode_info(uint8_t *response, struct knc_die_info *die_info); +int knc_decode_report(uint8_t *response, struct knc_report *report, int version); + +void knc_prepare_neptune_message(int request_length, const uint8_t *request, uint8_t *buffer); + +#define KNC_ACCEPTED (1<<0) +#define KNC_ERR_CRC (1<<1) +#define KNC_ERR_ACK (1<<2) +#define KNC_ERR_CRCACK (1<<3) +#define KNC_ERR_UNAVAIL (1<<4) +#define KNC_ERR_MASK (~(KNC_ACCEPTED)) +#define KNC_IS_ERROR(x) (((x) & KNC_ERR_MASK) != 0) + +int knc_prepare_transfer(uint8_t *txbuf, int offset, int size, int channel, int request_length, const uint8_t *request, int response_length); +int knc_decode_response(uint8_t *rxbuf, int request_length, uint8_t **response, int response_length); +int knc_syncronous_transfer(void *ctx, int channel, int request_length, const uint8_t *request, int response_length, uint8_t *response); + +/* Detect ASIC DIE version */ +int knc_detect_die(void *ctx, int channel, int die, struct knc_die_info *die_info); + +/* red, green, blue valid range 0 - 15. No response or checksum from controller */ +int knc_prepare_led(uint8_t *txbuf, int offset, int size, int red, int green, int blue); + +/* Reset controller */ +int knc_prepare_reset(uint8_t *txbuf, int offset, int size); + +#endif diff --git a/knc-transport-spi.c b/knc-transport-spi.c new file mode 100644 index 0000000000..5cefdf6944 --- /dev/null +++ b/knc-transport-spi.c @@ -0,0 +1,148 @@ +/* + * Direct SPI transport layer for KnCminer Jupiters + * + * Copyright 2014 KnCminer + * + * 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 +#include +#include + +#include "logging.h" +#include "miner.h" +#include "hexdump.c" +#include "knc-transport.h" + +#define SPI_DEVICE_TEMPLATE "/dev/spidev%d.%d" +#define SPI_MODE (SPI_CPHA | SPI_CPOL | SPI_CS_HIGH) +#define SPI_BITS_PER_WORD 8 +#define SPI_MAX_SPEED 3000000 +#define SPI_DELAY_USECS 0 + +struct spidev_context { + int fd; + uint32_t speed; + uint16_t delay; + uint8_t mode; + uint8_t bits; +}; + +/* Init SPI transport */ +void *knc_trnsp_new(int dev_idx) +{ + struct spidev_context *ctx; + char dev_name[PATH_MAX]; + + if (NULL == (ctx = malloc(sizeof(struct spidev_context)))) { + applog(LOG_ERR, "KnC transport: Out of memory"); + goto l_exit_error; + } + ctx->mode = SPI_MODE; + ctx->bits = SPI_BITS_PER_WORD; + ctx->speed = SPI_MAX_SPEED; + ctx->delay = SPI_DELAY_USECS; + + ctx->fd = -1; + sprintf(dev_name, SPI_DEVICE_TEMPLATE, + dev_idx + 1, /* bus */ + 0 /* chipselect */ + ); + if (0 > (ctx->fd = open(dev_name, O_RDWR))) { + applog(LOG_ERR, "KnC transport: Can not open SPI device %s: %m", + dev_name); + goto l_free_exit_error; + } + + /* + * spi mode + */ + if (0 > ioctl(ctx->fd, SPI_IOC_WR_MODE, &ctx->mode)) + goto l_ioctl_error; + if (0 > ioctl(ctx->fd, SPI_IOC_RD_MODE, &ctx->mode)) + goto l_ioctl_error; + + /* + * bits per word + */ + if (0 > ioctl(ctx->fd, SPI_IOC_WR_BITS_PER_WORD, &ctx->bits)) + goto l_ioctl_error; + if (0 > ioctl(ctx->fd, SPI_IOC_RD_BITS_PER_WORD, &ctx->bits)) + goto l_ioctl_error; + + /* + * max speed hz + */ + if (0 > ioctl(ctx->fd, SPI_IOC_WR_MAX_SPEED_HZ, &ctx->speed)) + goto l_ioctl_error; + if (0 > ioctl(ctx->fd, SPI_IOC_RD_MAX_SPEED_HZ, &ctx->speed)) + goto l_ioctl_error; + + applog(LOG_INFO, "KnC transport: SPI device %s uses mode %hhu, bits %hhu, speed %u", + dev_name, ctx->mode, ctx->bits, ctx->speed); + + return ctx; + +l_ioctl_error: + applog(LOG_ERR, "KnC transport: ioctl error on SPI device %s: %m", dev_name); + close(ctx->fd); +l_free_exit_error: + free(ctx); +l_exit_error: + return NULL; +} + +void knc_trnsp_free(void *opaque_ctx) +{ + struct spidev_context *ctx = opaque_ctx; + + if (NULL == ctx) + return; + + close(ctx->fd); + free(ctx); +} + +int knc_trnsp_transfer(void *opaque_ctx, uint8_t *txbuf, uint8_t *rxbuf, int len) +{ + struct spidev_context *ctx = opaque_ctx; + struct spi_ioc_transfer xfr; + int ret; + + memset(rxbuf, 0xff, len); + + ret = len; + + xfr.tx_buf = (unsigned long)txbuf; + xfr.rx_buf = (unsigned long)rxbuf; + xfr.len = len; + xfr.speed_hz = ctx->speed; + xfr.delay_usecs = ctx->delay; + xfr.bits_per_word = ctx->bits; + xfr.cs_change = 0; + xfr.pad = 0; + + applog(LOG_DEBUG, "KnC spi:"); + hexdump(txbuf, len); + if (1 > (ret = ioctl(ctx->fd, SPI_IOC_MESSAGE(1), &xfr))) + applog(LOG_ERR, "KnC spi xfer: ioctl error on SPI device: %m"); + hexdump(rxbuf, len); + + return ret; +} + +bool knc_trnsp_asic_detect(void *opaque_ctx, int chip_id) +{ + return true; +} + +void knc_trnsp_periodic_check(void *opaque_ctx) +{ + return; +} + diff --git a/knc-transport.h b/knc-transport.h new file mode 100644 index 0000000000..3db91101aa --- /dev/null +++ b/knc-transport.h @@ -0,0 +1,23 @@ +/* + * Transport layer interface for KnCminer devices + * + * Copyright 2014 KnCminer + * + * 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. + */ + +#define MAX_ASICS 6 +#define NUM_DIES_IN_ASIC 4 +#define CORES_IN_DIE 48 +#define CORES_PER_ASIC (NUM_DIES_IN_ASIC * CORES_IN_DIE) + +#define MAX_BYTES_IN_SPI_XSFER 4096 + +void *knc_trnsp_new(int dev_idx); +void knc_trnsp_free(void *opaque_ctx); +int knc_trnsp_transfer(void *opaque_ctx, uint8_t *txbuf, uint8_t *rxbuf, int len); +bool knc_trnsp_asic_detect(void *opaque_ctx, int chip_id); +void knc_trnsp_periodic_check(void *opaque_ctx); diff --git a/libbitfury.c b/libbitfury.c new file mode 100644 index 0000000000..78f982cf61 --- /dev/null +++ b/libbitfury.c @@ -0,0 +1,387 @@ +/* + * Copyright 2014 Con Kolivas + * Copyright 2013 Andrew Smith + * Copyright 2013 bitfury + * + * 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 "miner.h" +#include "driver-bitfury.h" +#include "libbitfury.h" +#include "sha2.h" + +void ms3steps(uint32_t *p) +{ + uint32_t a, b, c, d, e, f, g, h, new_e, new_a; + int i; + + a = p[0]; + b = p[1]; + c = p[2]; + d = p[3]; + e = p[4]; + f = p[5]; + g = p[6]; + h = p[7]; + for (i = 0; i < 3; i++) { + new_e = p[i+16] + sha256_k[i] + h + CH(e,f,g) + SHA256_F2(e) + d; + new_a = p[i+16] + sha256_k[i] + h + CH(e,f,g) + SHA256_F2(e) + + SHA256_F1(a) + MAJ(a,b,c); + d = c; + c = b; + b = a; + a = new_a; + h = g; + g = f; + f = e; + e = new_e; + } + p[15] = a; + p[14] = b; + p[13] = c; + p[12] = d; + p[11] = e; + p[10] = f; + p[9] = g; + p[8] = h; +} + +uint32_t decnonce(uint32_t in) +{ + uint32_t out; + + /* First part load */ + out = (in & 0xFF) << 24; + in >>= 8; + + /* Byte reversal */ + in = (((in & 0xaaaaaaaa) >> 1) | ((in & 0x55555555) << 1)); + in = (((in & 0xcccccccc) >> 2) | ((in & 0x33333333) << 2)); + in = (((in & 0xf0f0f0f0) >> 4) | ((in & 0x0f0f0f0f) << 4)); + + out |= (in >> 2) & 0x3FFFFF; + + /* Extraction */ + if (in & 1) + out |= (1 << 23); + if (in & 2) + out |= (1 << 22); + + out -= 0x800004; + return out; +} + +/* Test vectors to calculate (using address-translated loads) */ +static unsigned int atrvec[] = { + 0xb0e72d8e, 0x1dc5b862, 0xe9e7c4a6, 0x3050f1f5, 0x8a1a6b7e, 0x7ec384e8, 0x42c1c3fc, 0x8ed158a1, /* MIDSTATE */ + 0,0,0,0,0,0,0,0, + 0x8a0bb7b7, 0x33af304f, 0x0b290c1a, 0xf0c4e61f, /* WDATA: hashMerleRoot[7], nTime, nBits, nNonce */ +}; +static bool atrvec_set; + +void bitfury_work_to_payload(struct bitfury_payload *p, struct work *work) +{ + memcpy(p->midstate, work->midstate, 32); + p->m7 = *(unsigned int *)(work->data + 64); + p->ntime = *(unsigned int *)(work->data + 68); + p->nbits = *(unsigned int *)(work->data + 72); + applog(LOG_INFO, "INFO nonc: %08x bitfury_scanHash MS0: %08x, ", p->nnonce, + ((unsigned int *)work->midstate)[0]); + applog(LOG_INFO, "INFO merkle[7]: %08x, ntime: %08x, nbits: %08x", p->m7, + p->ntime, p->nbits); +} + +/* Configuration registers - control oscillators and such stuff. PROGRAMMED when + * magic number matches, UNPROGRAMMED (default) otherwise */ +void spi_config_reg(struct bitfury_info *info, int cfgreg, int ena) +{ + static const uint8_t enaconf[4] = { 0xc1, 0x6a, 0x59, 0xe3 }; + static const uint8_t disconf[4] = { 0, 0, 0, 0 }; + + if (ena) + spi_add_data(info, 0x7000 + cfgreg * 32, enaconf, 4); + else + spi_add_data(info, 0x7000 + cfgreg * 32, disconf, 4); +} + +void spi_set_freq(struct bitfury_info *info) +{ + uint64_t freq; + const uint8_t *osc6 = (unsigned char *)&freq; + + freq = (1ULL << info->osc6_bits) - 1ULL; + spi_add_data(info, 0x6000, osc6, 8); /* Program internal on-die slow oscillator frequency */ +} + +#define FIRST_BASE 61 +#define SECOND_BASE 4 + +void spi_send_conf(struct bitfury_info *info) +{ + const int8_t nfu_counters[16] = { 64, 64, SECOND_BASE, SECOND_BASE+4, SECOND_BASE+2, + SECOND_BASE+2+16, SECOND_BASE, SECOND_BASE+1, (FIRST_BASE)%65, (FIRST_BASE+1)%65, + (FIRST_BASE+3)%65, (FIRST_BASE+3+16)%65, (FIRST_BASE+4)%65, (FIRST_BASE+4+4)%65, + (FIRST_BASE+3+3)%65, (FIRST_BASE+3+1+3)%65 }; + int i; + + for (i = 7; i <= 11; i++) + spi_config_reg(info, i, 0); + spi_config_reg(info, 6, 1); /* disable OUTSLK */ + spi_config_reg(info, 4, 1); /* Enable slow oscillator */ + for (i = 1; i <= 3; ++i) + spi_config_reg(info, i, 0); + /* Program counters correctly for rounds processing, here it should + * start consuming power */ + spi_add_data(info, 0x0100, nfu_counters, 16); +} + +void spi_send_init(struct bitfury_info *info) +{ + /* Prepare internal buffers */ + /* PREPARE BUFFERS (INITIAL PROGRAMMING) */ + unsigned int w[16]; + + if (!atrvec_set) { + atrvec_set = true; + ms3steps(atrvec); + } + memset(w, 0, sizeof(w)); + w[3] = 0xffffffff; + w[4] = 0x80000000; + w[15] = 0x00000280; + spi_add_data(info, 0x1000, w, 16 * 4); + spi_add_data(info, 0x1400, w, 8 * 4); + memset(w, 0, sizeof(w)); + w[0] = 0x80000000; + w[7] = 0x100; + spi_add_data(info, 0x1900, w, 8 * 4); /* Prepare MS and W buffers! */ + spi_add_data(info, 0x3000, atrvec, 19 * 4); +} +void spi_clear_buf(struct bitfury_info *info) +{ + info->spibufsz = 0; +} + +void spi_add_buf(struct bitfury_info *info, const void *buf, const int sz) +{ + if (unlikely(info->spibufsz + sz > SPIBUF_SIZE)) { + applog(LOG_WARNING, "SPI bufsize overflow!"); + return; + } + memcpy(&info->spibuf[info->spibufsz], buf, sz); + info->spibufsz += sz; +} + +void spi_add_break(struct bitfury_info *info) +{ + spi_add_buf(info, "\x4", 1); +} + +void spi_add_fasync(struct bitfury_info *info, int n) +{ + int i; + + for (i = 0; i < n; i++) + spi_add_buf(info, "\x5", 1); +} + +static void spi_add_buf_reverse(struct bitfury_info *info, const char *buf, const int sz) +{ + int i; + + for (i = 0; i < sz; i++) { // Reverse bit order in each byte! + unsigned char p = buf[i]; + + p = ((p & 0xaa) >> 1) | ((p & 0x55) << 1); + p = ((p & 0xcc) >> 2) | ((p & 0x33) << 2); + p = ((p & 0xf0) >> 4) | ((p & 0x0f) << 4); + info->spibuf[info->spibufsz + i] = p; + } + info->spibufsz += sz; +} + +void spi_add_data(struct bitfury_info *info, uint16_t addr, const void *buf, int len) +{ + unsigned char otmp[3]; + + if (len < 4 || len > 128) { + applog(LOG_WARNING, "Can't add SPI data size %d", len); + return; + } + len /= 4; /* Strip */ + otmp[0] = (len - 1) | 0xE0; + otmp[1] = (addr >> 8) & 0xFF; + otmp[2] = addr & 0xFF; + spi_add_buf(info, otmp, 3); + len *= 4; + spi_add_buf_reverse(info, buf, len); +} + +// Bit-banging reset... Each 3 reset cycles reset first chip in chain +bool spi_reset(struct cgpu_info *bitfury, struct bitfury_info *info) +{ + struct mcp_settings *mcp = &info->mcp; + int r; + + // SCK_OVRRIDE + mcp->value.pin[NFU_PIN_SCK_OVR] = MCP2210_GPIO_PIN_HIGH; + mcp->direction.pin[NFU_PIN_SCK_OVR] = MCP2210_GPIO_OUTPUT; + mcp->designation.pin[NFU_PIN_SCK_OVR] = MCP2210_PIN_GPIO; + if (!mcp2210_set_gpio_settings(bitfury, mcp)) + return false; + + for (r = 0; r < 16; ++r) { + char buf[1] = {0x81}; // will send this waveform: - _ _ _ _ _ _ - + unsigned int length = 1; + + if (!mcp2210_spi_transfer(bitfury, &info->mcp, buf, &length)) + return false; + } + + // Deactivate override + mcp->direction.pin[NFU_PIN_SCK_OVR] = MCP2210_GPIO_INPUT; + if (!mcp2210_set_gpio_settings(bitfury, mcp)) + return false; + + return true; +} + +bool mcp_spi_txrx(struct cgpu_info *bitfury, struct bitfury_info *info) +{ + unsigned int length, sendrcv; + int offset = 0; + + length = info->spibufsz; + applog(LOG_DEBUG, "%s %d: SPI sending %u bytes total", bitfury->drv->name, + bitfury->device_id, length); + while (length > MCP2210_TRANSFER_MAX) { + sendrcv = MCP2210_TRANSFER_MAX; + if (!mcp2210_spi_transfer(bitfury, &info->mcp, info->spibuf + offset, &sendrcv)) + return false; + if (sendrcv != MCP2210_TRANSFER_MAX) { + applog(LOG_DEBUG, "%s %d: Send/Receive size mismatch sent %d received %d", + bitfury->drv->name, bitfury->device_id, MCP2210_TRANSFER_MAX, sendrcv); + } + length -= MCP2210_TRANSFER_MAX; + offset += MCP2210_TRANSFER_MAX; + } + sendrcv = length; + if (!mcp2210_spi_transfer(bitfury, &info->mcp, info->spibuf + offset, &sendrcv)) + return false; + if (sendrcv != length) { + applog(LOG_WARNING, "%s %d: Send/Receive size mismatch sent %d received %d", + bitfury->drv->name, bitfury->device_id, length, sendrcv); + return false; + } + return true; +} + +#define READ_WRITE_BYTES_SPI0 0x31 + +bool ftdi_spi_txrx(struct cgpu_info *bitfury, struct bitfury_info *info) +{ + int err, amount, len; + uint16_t length; + char buf[1024]; + + len = info->spibufsz; + length = info->spibufsz - 1; //FTDI length is shifted by one 0x0000 = one byte + buf[0] = READ_WRITE_BYTES_SPI0; + buf[1] = length & 0x00FF; + buf[2] = (length & 0xFF00) >> 8; + memcpy(&buf[3], info->spibuf, info->spibufsz); + info->spibufsz += 3; + err = usb_write(bitfury, buf, info->spibufsz, &amount, C_BXM_SPITX); + if (err || amount != (int)info->spibufsz) { + applog(LOG_ERR, "%s %d: SPI TX error %d, sent %d of %d", bitfury->drv->name, + bitfury->device_id, err, amount, info->spibufsz); + return false; + } + info->spibufsz = len; + /* We shouldn't even get a timeout error on reads in spi mode */ + err = usb_read(bitfury, info->spibuf, len, &amount, C_BXM_SPIRX); + if (err || amount != len) { + applog(LOG_ERR, "%s %d: SPI RX error %d, read %d of %d", bitfury->drv->name, + bitfury->device_id, err, amount, info->spibufsz); + return false; + } + amount = usb_buffer_size(bitfury); + if (amount) { + applog(LOG_ERR, "%s %d: SPI RX Extra read buffer size %d", bitfury->drv->name, + bitfury->device_id, amount); + usb_buffer_clear(bitfury); + return false; + } + return true; +} + +#define BT_OFFSETS 3 + +bool bitfury_checkresults(struct thr_info *thr, struct work *work, uint32_t nonce) +{ + const uint32_t bf_offsets[] = {-0x800000, 0, -0x400000}; + int i; + + for (i = 0; i < BT_OFFSETS; i++) { + uint32_t noffset = nonce + bf_offsets[i]; + + if (test_nonce(work, noffset)) { + submit_tested_work(thr, work); + return true; + } + } + return false; +} + +/* Currently really only supports 2 chips, so chip_n can only be 0 or 1 */ +bool libbitfury_sendHashData(struct thr_info *thr, struct cgpu_info *bitfury, + struct bitfury_info *info, int chip_n) +{ + unsigned newbuf[17]; + unsigned *oldbuf = &info->oldbuf[17 * chip_n]; + struct bitfury_payload *p = &info->payload[chip_n]; + unsigned int localvec[20]; + + /* Programming next value */ + memcpy(localvec, p, 20 * 4); + ms3steps(localvec); + + spi_clear_buf(info); + spi_add_break(info); + spi_add_fasync(info, chip_n); + spi_add_data(info, 0x3000, (void*)localvec, 19 * 4); + if (!info->spi_txrx(bitfury, info)) + return false; + + memcpy(newbuf, info->spibuf + 4 + chip_n, 17 * 4); + + info->job_switched[chip_n] = newbuf[16] != oldbuf[16]; + + if (likely(info->second_run[chip_n])) { + if (info->job_switched[chip_n]) { + int i; + + for (i = 0; i < 16; i++) { + if (oldbuf[i] != newbuf[i] && info->owork[chip_n]) { + uint32_t nonce; //possible nonce + + nonce = decnonce(newbuf[i]); + if (bitfury_checkresults(thr, info->owork[chip_n], nonce)) { + info->submits[chip_n]++; + info->nonces++; + } + } + } + memcpy(oldbuf, newbuf, 17 * 4); + } + } else + info->second_run[chip_n] = true; + + cgsleep_ms(BITFURY_REFRESH_DELAY); + + return true; +} diff --git a/libbitfury.h b/libbitfury.h new file mode 100644 index 0000000000..ad896320d6 --- /dev/null +++ b/libbitfury.h @@ -0,0 +1,34 @@ +/* + * Copyright 2014 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 LIBBITFURY_H +#define LIBBITFURY_H +#include "miner.h" +#include "driver-bitfury.h" + +void ms3steps(uint32_t *p); +uint32_t decnonce(uint32_t in); +void bitfury_work_to_payload(struct bitfury_payload *p, struct work *work); +void spi_config_reg(struct bitfury_info *info, int cfgreg, int ena); +void spi_set_freq(struct bitfury_info *info); +void spi_send_conf(struct bitfury_info *info); +void spi_send_init(struct bitfury_info *info); +void spi_clear_buf(struct bitfury_info *info); +void spi_add_buf(struct bitfury_info *info, const void *buf, const int sz); +void spi_add_break(struct bitfury_info *info); +void spi_add_fasync(struct bitfury_info *info, int n); +void spi_add_data(struct bitfury_info *info, uint16_t addr, const void *buf, int len); +bool spi_reset(struct cgpu_info *bitfury, struct bitfury_info *info); +bool mcp_spi_txrx(struct cgpu_info *bitfury, struct bitfury_info *info); +bool ftdi_spi_txrx(struct cgpu_info *bitfury, struct bitfury_info *info); +bool bitfury_checkresults(struct thr_info *thr, struct work *work, uint32_t nonce); +bool libbitfury_sendHashData(struct thr_info *thr, struct cgpu_info *bitfury, + struct bitfury_info *info, int chip_n); + +#endif /* LIBBITFURY_H */ diff --git a/libztex.c b/libztex.c deleted file mode 100644 index 860a8423ab..0000000000 --- a/libztex.c +++ /dev/null @@ -1,915 +0,0 @@ -/** - * libztex.c - Ztex 1.15x/1.15y fpga board support library - * - * Copyright (c) 2012 nelisky.btc@gmail.com - * Copyright (c) 2012 Denis Ahrens - * Copyright (c) 2012 Peter Stuge - * - * This work is based upon the Java SDK provided by ztex which is - * Copyright (C) 2009-2011 ZTEX GmbH. - * http://www.ztex.de - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, see http://www.gnu.org/licenses/. -**/ - -#include "config.h" - -#include -#include -#include - -#include "miner.h" -#include "fpgautils.h" -#include "libztex.h" - -//* Capability index for EEPROM support. -#define CAPABILITY_EEPROM 0,0 -//* Capability index for FPGA configuration support. -#define CAPABILITY_FPGA 0,1 -//* Capability index for FLASH memory support. -#define CAPABILITY_FLASH 0,2 -//* Capability index for DEBUG helper support. -#define CAPABILITY_DEBUG 0,3 -//* Capability index for AVR XMEGA support. -#define CAPABILITY_XMEGA 0,4 -//* Capability index for AVR XMEGA support. -#define CAPABILITY_HS_FPGA 0,5 -//* Capability index for AVR XMEGA support. -#define CAPABILITY_MAC_EEPROM 0,6 -//* Capability index for multi FPGA support. -#define CAPABILITY_MULTI_FPGA 0,7 - -static int libztex_get_string_descriptor_ascii(libusb_device_handle *dev, uint8_t desc_index, - unsigned char *data, int length) -{ - int i, cnt; - uint16_t langid; - unsigned char buf[260]; - - /* We open code string descriptor retrieval and ASCII decoding here - * in order to work around that libusb_get_string_descriptor_ascii() - * in the FreeBSD libusb implementation hits a bug in ZTEX firmware, - * where the device returns more bytes than requested, causing babble, - * which makes FreeBSD return an error to us. - * - * Avoid the mess by doing it manually the same way as libusb-1.0. - */ - - cnt = libusb_control_transfer(dev, LIBUSB_ENDPOINT_IN, - LIBUSB_REQUEST_GET_DESCRIPTOR, (LIBUSB_DT_STRING << 8) | 0, - 0x0000, buf, sizeof(buf), 1000); - if (cnt < 0) { - applog(LOG_ERR, "%s: Failed to read LANGIDs: %d", __func__, cnt); - return cnt; - } - - langid = libusb_le16_to_cpu(((uint16_t *)buf)[1]); - - cnt = libusb_control_transfer(dev, LIBUSB_ENDPOINT_IN, - LIBUSB_REQUEST_GET_DESCRIPTOR, (LIBUSB_DT_STRING << 8) | desc_index, - langid, buf, sizeof(buf), 1000); - if (cnt < 0) { - applog(LOG_ERR, "%s: Failed to read string descriptor: %d", __func__, cnt); - return cnt; - } - - /* num chars = (all bytes except bLength and bDescriptorType) / 2 */ - for (i = 0; i <= (cnt - 2) / 2 && i < length-1; i++) - data[i] = buf[2 + i*2]; - - data[i] = 0; - - return LIBUSB_SUCCESS; -} - -enum check_result -{ - CHECK_ERROR, - CHECK_IS_NOT_ZTEX, - CHECK_OK, - CHECK_RESCAN, -}; - -static bool libztex_firmwareReset(struct libusb_device_handle *hndl, bool enable) -{ - uint8_t reset = enable; - int cnt = libusb_control_transfer(hndl, 0x40, 0xA0, 0xE600, 0, &reset, 1, 1000); - if (cnt < 0) - { - applog(LOG_ERR, "Ztex reset %d failed: %d", enable, cnt); - return 1; - } - - return 0; -} - -static enum check_result libztex_checkDevice(struct libusb_device *dev) -{ - FILE *fp = NULL; - libusb_device_handle *hndl = NULL; - struct libusb_device_descriptor desc; - int ret = CHECK_ERROR, err, cnt; - size_t got_bytes, length; - unsigned char buf[64], *fw_buf; - unsigned int i; - - err = libusb_get_device_descriptor(dev, &desc); - if (unlikely(err != 0)) { - applog(LOG_ERR, "Ztex check device: Failed to open read descriptor with error %d", err); - return CHECK_ERROR; - } - - if (desc.idVendor != LIBZTEX_IDVENDOR || desc.idProduct != LIBZTEX_IDPRODUCT) { - applog(LOG_DEBUG, "Not a ZTEX device %04x:%04x", desc.idVendor, desc.idProduct); - return CHECK_IS_NOT_ZTEX; - } - - err = libusb_open(dev, &hndl); - if (err != LIBUSB_SUCCESS) { - applog(LOG_ERR, "%s: Can not open ZTEX device: %d", __func__, err); - goto done; - } - - cnt = libusb_control_transfer(hndl, 0xc0, 0x22, 0, 0, buf, 40, 500); - if (unlikely(cnt < 0)) { - applog(LOG_ERR, "Ztex check device: Failed to read ztex descriptor with err %d", cnt); - goto done; - } - - if (buf[0] != 40 || buf[1] != 1 || buf[2] != 'Z' || buf[3] != 'T' || buf[4] != 'E' || buf[5] != 'X') { - applog(LOG_ERR, "Ztex check device: Error reading ztex descriptor"); - goto done; - } - - if (buf[6] != 10) - { - ret = CHECK_IS_NOT_ZTEX; - goto done; - } - - // 15 = 1.15y 13 = 1.15d or 1.15x - switch(buf[7]) - { - case 13: - applog(LOG_ERR, "Found ztex board 1.15d or 1.15x"); - break; - case 15: - applog(LOG_ERR, "Found ztex board 1.15y"); - break; - default: - applog(LOG_ERR, "Found unknown ztex board"); - ret = CHECK_IS_NOT_ZTEX; - goto done; - } - - // testing for dummy firmware - if (buf[8] != 0) { - ret = CHECK_OK; - goto done; - } - - applog(LOG_ERR, "Found dummy firmware, trying to send mining firmware"); - - char productString[32]; - - cnt = libztex_get_string_descriptor_ascii(hndl, desc.iProduct, (unsigned char*)productString, sizeof(productString)); - if (unlikely(cnt < 0)) { - applog(LOG_ERR, "Ztex check device: Failed to read device productString with err %d", cnt); - return cnt; - } - - applog(LOG_ERR, "productString: %s", productString); - - unsigned char productID2 = buf[7]; - char *firmware = NULL; - - if (strcmp("USB-FPGA Module 1.15d (default)", productString) == 0 && productID2 == 13) - { - firmware = "ztex_ufm1_15d4.bin"; - } - else if (strcmp("USB-FPGA Module 1.15x (default)", productString) == 0 && productID2 == 13) - { - firmware = "ztex_ufm1_15d4.bin"; - } - else if (strcmp("USB-FPGA Module 1.15y (default)", productString) == 0 && productID2 == 15) - { - firmware = "ztex_ufm1_15y1.bin"; - } - - if (firmware == NULL) - { - applog(LOG_ERR, "could not figure out which firmware to use"); - goto done; - } - - applog(LOG_ERR, "Mining firmware filename: %s", firmware); - - fp = open_bitstream("ztex", firmware); - if (!fp) { - applog(LOG_ERR, "failed to open firmware file '%s'", firmware); - goto done; - } - - if (0 != fseek(fp, 0, SEEK_END)) { - applog(LOG_ERR, "Ztex firmware fseek: %s", strerror(errno)); - goto done; - } - - length = ftell(fp); - rewind(fp); - fw_buf = malloc(length); - if (!fw_buf) { - applog(LOG_ERR, "%s: Can not allocate memory: %s", __func__, strerror(errno)); - goto done; - } - - got_bytes = fread(fw_buf, 1, length, fp); - fclose(fp); - fp = NULL; - - if (got_bytes < length) { - applog(LOG_ERR, "%s: Incomplete firmware read: %d/%d", __func__, (int)got_bytes, (int)length); - goto done; - } - - // in buf[] is still the identifier of the dummy firmware - // use it to compare it with the new firmware - char *rv = memmem(fw_buf, got_bytes, buf, 8); - if (rv == NULL) - { - applog(LOG_ERR, "%s: found firmware is not ZTEX", __func__); - goto done; - } - - // check for dummy firmware - if (rv[8] == 0) - { - applog(LOG_ERR, "%s: found a ZTEX dummy firmware", __func__); - goto done; - } - - if (libztex_firmwareReset(hndl, true)) - goto done; - - for (i = 0; i < length; i+= 256) { - // firmware wants data in small chunks like 256 bytes - int numbytes = (length - i) < 256 ? (length - i) : 256; - int k = libusb_control_transfer(hndl, 0x40, 0xA0, i, 0, fw_buf + i, numbytes, 1000); - if (k < numbytes) - { - applog(LOG_ERR, "Ztex device: Failed to write firmware at %d with err: %d", i, k); - goto done; - } - } - - if (libztex_firmwareReset(hndl, false)) - goto done; - - applog(LOG_ERR, "Ztex device: succesfully wrote firmware"); - ret = CHECK_RESCAN; - -done: - if (fp) - fclose(fp); - if (hndl) - libusb_close(hndl); - return ret; -} - -static bool libztex_checkCapability(struct libztex_device *ztex, int i, int j) -{ - if (!((i >= 0) && (i <= 5) && (j >= 0) && (j < 8) && - (((ztex->interfaceCapabilities[i] & 255) & (1 << j)) != 0))) { - applog(LOG_ERR, "%s: capability missing: %d %d", ztex->repr, i, j); - return false; - } - return true; -} - -static char libztex_detectBitstreamBitOrder(const unsigned char *buf, int size) -{ - int i; - - for (i = 0; i < size - 4; i++) { - if (((buf[i] & 255) == 0xaa) && ((buf[i + 1] & 255) == 0x99) && ((buf[i + 2] & 255) == 0x55) && ((buf[i + 3] & 255) == 0x66)) - return 1; - if (((buf[i] & 255) == 0x55) && ((buf[i + 1] & 255) == 0x99) && ((buf[i + 2] & 255) == 0xaa) && ((buf[i + 3] & 255) == 0x66)) - return 0; - } - applog(LOG_WARNING, "Unable to determine bitstream bit order: no signature found"); - return 0; -} - -static void libztex_swapBits(unsigned char *buf, int size) -{ - unsigned char c; - int i; - - for (i = 0; i < size; i++) { - c = buf[i]; - buf[i] = ((c & 128) >> 7) | - ((c & 64) >> 5) | - ((c & 32) >> 3) | - ((c & 16) >> 1) | - ((c & 8) << 1) | - ((c & 4) << 3) | - ((c & 2) << 5) | - ((c & 1) << 7); - } -} - -static int libztex_getFpgaState(struct libztex_device *ztex, struct libztex_fpgastate *state) -{ - unsigned char buf[9]; - int cnt; - - if (!libztex_checkCapability(ztex, CAPABILITY_FPGA)) - return -1; - cnt = libusb_control_transfer(ztex->hndl, 0xc0, 0x30, 0, 0, buf, 9, 1000); - if (unlikely(cnt < 0)) { - applog(LOG_ERR, "%s: Failed getFpgaState with err %d", ztex->repr, cnt); - return cnt; - } - state->fpgaConfigured = (buf[0] == 0); - state->fpgaChecksum = buf[1] & 0xff; - state->fpgaBytes = ((buf[5] & 0xff) << 24) | ((buf[4] & 0xff) << 16) | ((buf[3] & 0xff) << 8) | (buf[2] & 0xff); - state->fpgaInitB = buf[6] & 0xff; - state->fpgaFlashResult = buf[7]; - state->fpgaFlashBitSwap = (buf[8] != 0); - return 0; -} - -static int libztex_configureFpgaHS(struct libztex_device *ztex, const char* firmware, bool force, char bs) -{ - struct libztex_fpgastate state; - const int transactionBytes = 65536; - unsigned char buf[transactionBytes], settings[2]; - int tries, cnt, err; - FILE *fp; - - if (!libztex_checkCapability(ztex, CAPABILITY_HS_FPGA)) - return -1; - libztex_getFpgaState(ztex, &state); - if (!force && state.fpgaConfigured) { - applog(LOG_INFO, "Bitstream already configured"); - return 0; - } - cnt = libusb_control_transfer(ztex->hndl, 0xc0, 0x33, 0, 0, settings, 2, 1000); - if (unlikely(cnt < 0)) { - applog(LOG_ERR, "%s: Failed getHSFpgaSettings with err %d", ztex->repr, cnt); - return cnt; - } - - err = libusb_claim_interface(ztex->hndl, settings[1]); - if (err != LIBUSB_SUCCESS) { - applog(LOG_ERR, "%s: failed to claim interface for hs transfer", ztex->repr); - return -4; - } - - for (tries = 3; tries > 0; tries--) { - fp = open_bitstream("ztex", firmware); - if (!fp) { - applog(LOG_ERR, "%s: failed to read bitstream '%s'", ztex->repr, firmware); - libusb_release_interface(ztex->hndl, settings[1]); - return -2; - } - - libusb_control_transfer(ztex->hndl, 0x40, 0x34, 0, 0, NULL, 0, 1000); - // 0x34 - initHSFPGAConfiguration - - do - { - int length = fread(buf,1,transactionBytes,fp); - - if (bs != 0 && bs != 1) - bs = libztex_detectBitstreamBitOrder(buf, length); - if (bs == 1) - libztex_swapBits(buf, length); - - err = libusb_bulk_transfer(ztex->hndl, settings[0], buf, length, &cnt, 1000); - if (cnt != length) - applog(LOG_ERR, "%s: cnt != length", ztex->repr); - if (err != 0) - applog(LOG_ERR, "%s: Failed send hs fpga data", ztex->repr); - } - while (!feof(fp)); - - libusb_control_transfer(ztex->hndl, 0x40, 0x35, 0, 0, NULL, 0, 1000); - // 0x35 - finishHSFPGAConfiguration - if (cnt >= 0) - tries = 0; - - fclose(fp); - - libztex_getFpgaState(ztex, &state); - if (!state.fpgaConfigured) { - applog(LOG_ERR, "%s: HS FPGA configuration failed: DONE pin does not go high", ztex->repr); - libusb_release_interface(ztex->hndl, settings[1]); - return -3; - } - } - - libusb_release_interface(ztex->hndl, settings[1]); - - cgsleep_ms(200); - applog(LOG_INFO, "%s: HS FPGA configuration done", ztex->repr); - return 0; -} - -static int libztex_configureFpgaLS(struct libztex_device *ztex, const char* firmware, bool force, char bs) -{ - struct libztex_fpgastate state; - const int transactionBytes = 2048; - unsigned char buf[transactionBytes]; - int tries, cnt; - FILE *fp; - - if (!libztex_checkCapability(ztex, CAPABILITY_FPGA)) - return -1; - - libztex_getFpgaState(ztex, &state); - if (!force && state.fpgaConfigured) { - applog(LOG_DEBUG, "Bitstream already configured"); - return 0; - } - - for (tries = 10; tries > 0; tries--) { - fp = open_bitstream("ztex", firmware); - if (!fp) { - applog(LOG_ERR, "%s: failed to read bitstream '%s'", ztex->repr, firmware); - return -2; - } - - //* Reset fpga - cnt = libztex_resetFpga(ztex); - if (unlikely(cnt < 0)) { - applog(LOG_ERR, "%s: Failed reset fpga with err %d", ztex->repr, cnt); - continue; - } - - do - { - int length = fread(buf, 1, transactionBytes, fp); - - if (bs != 0 && bs != 1) - bs = libztex_detectBitstreamBitOrder(buf, length); - if (bs == 1) - libztex_swapBits(buf, length); - cnt = libusb_control_transfer(ztex->hndl, 0x40, 0x32, 0, 0, buf, length, 5000); - if (cnt != length) - { - applog(LOG_ERR, "%s: Failed send ls fpga data", ztex->repr); - break; - } - } - while (!feof(fp)); - - if (cnt > 0) - tries = 0; - - fclose(fp); - } - - libztex_getFpgaState(ztex, &state); - if (!state.fpgaConfigured) { - applog(LOG_ERR, "%s: LS FPGA configuration failed: DONE pin does not go high", ztex->repr); - return -3; - } - - cgsleep_ms(200); - applog(LOG_INFO, "%s: FPGA configuration done", ztex->repr); - return 0; -} - -int libztex_configureFpga(struct libztex_device *ztex) -{ - char buf[256]; - int rv; - - strcpy(buf, ztex->bitFileName); - strcat(buf, ".bit"); - rv = libztex_configureFpgaHS(ztex, buf, true, 2); - if (rv != 0) - rv = libztex_configureFpgaLS(ztex, buf, true, 2); - return rv; -} - -int libztex_numberOfFpgas(struct libztex_device *ztex) -{ - int cnt; - unsigned char buf[3]; - - if (ztex->numberOfFpgas < 0) { - if (libztex_checkCapability(ztex, CAPABILITY_MULTI_FPGA)) { - cnt = libusb_control_transfer(ztex->hndl, 0xc0, 0x50, 0, 0, buf, 3, 1000); - if (unlikely(cnt < 0)) { - applog(LOG_ERR, "%s: Failed getMultiFpgaInfo with err %d", ztex->repr, cnt); - return cnt; - } - ztex->numberOfFpgas = buf[0] + 1; - ztex->selectedFpga = -1;//buf[1]; - ztex->parallelConfigSupport = (buf[2] == 1); - } else { - ztex->numberOfFpgas = 1; - ztex->selectedFpga = -1;//0; - ztex->parallelConfigSupport = false; - } - } - return ztex->numberOfFpgas; -} - -int libztex_selectFpga(struct libztex_device *ztex) -{ - int cnt, fpgacnt = libztex_numberOfFpgas(ztex->root); - int16_t number = ztex->fpgaNum; - - if (number < 0 || number >= fpgacnt) { - applog(LOG_WARNING, "%s: Trying to select wrong fpga (%d in %d)", ztex->repr, number, fpgacnt); - return 1; - } - if (ztex->root->selectedFpga != number && libztex_checkCapability(ztex->root, CAPABILITY_MULTI_FPGA)) { - cnt = libusb_control_transfer(ztex->root->hndl, 0x40, 0x51, (uint16_t)number, 0, NULL, 0, 500); - if (unlikely(cnt < 0)) { - applog(LOG_ERR, "Ztex check device: Failed to set fpga with err %d", cnt); - ztex->root->selectedFpga = -1; - return cnt; - } - ztex->root->selectedFpga = number; - } - return 0; -} - -int libztex_setFreq(struct libztex_device *ztex, uint16_t freq) -{ - int cnt; - uint16_t oldfreq = ztex->freqM; - - if (freq > ztex->freqMaxM) - freq = ztex->freqMaxM; - - cnt = libusb_control_transfer(ztex->hndl, 0x40, 0x83, freq, 0, NULL, 0, 500); - if (unlikely(cnt < 0)) { - applog(LOG_ERR, "Ztex check device: Failed to set frequency with err %d", cnt); - return cnt; - } - ztex->freqM = freq; - if (oldfreq > ztex->freqMaxM) - applog(LOG_WARNING, "%s: Frequency set to %0.1f MHz", - ztex->repr, ztex->freqM1 * (ztex->freqM + 1)); - else - applog(LOG_WARNING, "%s: Frequency change from %0.1f to %0.1f MHz", - ztex->repr, ztex->freqM1 * (oldfreq + 1), ztex->freqM1 * (ztex->freqM + 1)); - - return 0; -} - -int libztex_resetFpga(struct libztex_device *ztex) -{ - return libusb_control_transfer(ztex->hndl, 0x40, 0x31, 0, 0, NULL, 0, 1000); -} - -int libztex_suspend(struct libztex_device *ztex) -{ - if (ztex->suspendSupported) { - return libusb_control_transfer(ztex->hndl, 0x40, 0x84, 0, 0, NULL, 0, 1000); - } else { - return 0; - } -} - -int libztex_prepare_device(struct libusb_device *dev, struct libztex_device** ztex) -{ - struct libztex_device *newdev = *ztex; - int i, cnt, err; - unsigned char buf[64]; - - err = libusb_open(dev, &newdev->hndl); - if (err != LIBUSB_SUCCESS) { - applog(LOG_ERR, "%s: Can not open ZTEX device: %d", __func__, err); - return CHECK_ERROR; - } - - err = libusb_get_device_descriptor(dev, &newdev->descriptor); - if (unlikely(err != 0)) { - applog(LOG_ERR, "Ztex check device: Failed to open read descriptor with error %d", err); - return CHECK_ERROR; - } - - cnt = libztex_get_string_descriptor_ascii(newdev->hndl, newdev->descriptor.iSerialNumber, newdev->snString, sizeof(newdev->snString)); - if (unlikely(cnt < 0)) { - applog(LOG_ERR, "Ztex check device: Failed to read device snString with err %d", cnt); - return cnt; - } - - cnt = libusb_control_transfer(newdev->hndl, 0xc0, 0x22, 0, 0, buf, 40, 500); - if (unlikely(cnt < 0)) { - applog(LOG_ERR, "Ztex check device: Failed to read ztex descriptor with err %d", cnt); - return cnt; - } - - if (buf[0] != 40 || buf[1] != 1 || buf[2] != 'Z' || buf[3] != 'T' || buf[4] != 'E' || buf[5] != 'X') { - applog(LOG_ERR, "Ztex check device: Error reading ztex descriptor"); - return 2; - } - - newdev->productId[0] = buf[6]; - newdev->productId[1] = buf[7]; - newdev->productId[2] = buf[8]; - newdev->productId[3] = buf[9]; - newdev->fwVersion = buf[10]; - newdev->interfaceVersion = buf[11]; - newdev->interfaceCapabilities[0] = buf[12]; - newdev->interfaceCapabilities[1] = buf[13]; - newdev->interfaceCapabilities[2] = buf[14]; - newdev->interfaceCapabilities[3] = buf[15]; - newdev->interfaceCapabilities[4] = buf[16]; - newdev->interfaceCapabilities[5] = buf[17]; - newdev->moduleReserved[0] = buf[18]; - newdev->moduleReserved[1] = buf[19]; - newdev->moduleReserved[2] = buf[20]; - newdev->moduleReserved[3] = buf[21]; - newdev->moduleReserved[4] = buf[22]; - newdev->moduleReserved[5] = buf[23]; - newdev->moduleReserved[6] = buf[24]; - newdev->moduleReserved[7] = buf[25]; - newdev->moduleReserved[8] = buf[26]; - newdev->moduleReserved[9] = buf[27]; - newdev->moduleReserved[10] = buf[28]; - newdev->moduleReserved[11] = buf[29]; - - cnt = libusb_control_transfer(newdev->hndl, 0xc0, 0x82, 0, 0, buf, 64, 500); - if (unlikely(cnt < 0)) { - applog(LOG_ERR, "Ztex check device: Failed to read ztex descriptor with err %d", cnt); - return cnt; - } - - if (unlikely(buf[0] != 5)) { - if (unlikely(buf[0] != 2 && buf[0] != 4)) { - applog(LOG_ERR, "Invalid BTCMiner descriptor version. Firmware must be updated (%d).", buf[0]); - return 3; - } - applog(LOG_WARNING, "Firmware out of date (%d).", buf[0]); - } - - i = buf[0] > 4? 11: (buf[0] > 2? 10: 8); - - while (cnt < 64 && buf[cnt] != 0) - cnt++; - if (cnt < i + 1) { - applog(LOG_ERR, "Invalid bitstream file name ."); - return 4; - } - - newdev->bitFileName = malloc(sizeof(char) * (cnt + 1)); - memcpy(newdev->bitFileName, &buf[i], cnt); - newdev->bitFileName[cnt] = 0; - - newdev->numNonces = buf[1] + 1; - newdev->offsNonces = ((buf[2] & 255) | ((buf[3] & 255) << 8)) - 10000; - newdev->freqM1 = ((buf[4] & 255) | ((buf[5] & 255) << 8) ) * 0.01; - newdev->freqMaxM = (buf[7] & 255); - newdev->freqM = (buf[6] & 255); - newdev->freqMDefault = newdev->freqM; - newdev->suspendSupported = (buf[0] == 5); - newdev->hashesPerClock = buf[0] > 2? (((buf[8] & 255) | ((buf[9] & 255) << 8)) + 1) / 128.0: 1.0; - newdev->extraSolutions = buf[0] > 4? buf[10]: 0; - - applog(LOG_DEBUG, "PID: %d numNonces: %d offsNonces: %d freqM1: %f freqMaxM: %d freqM: %d suspendSupported: %s hashesPerClock: %f extraSolutions: %d", - buf[0], newdev->numNonces, newdev->offsNonces, newdev->freqM1, newdev->freqMaxM, newdev->freqM, newdev->suspendSupported ? "T": "F", - newdev->hashesPerClock, newdev->extraSolutions); - - if (buf[0] < 4) { - if (strncmp(newdev->bitFileName, "ztex_ufm1_15b", 13) != 0) - newdev->hashesPerClock = 0.5; - applog(LOG_WARNING, "HASHES_PER_CLOCK not defined, assuming %0.2f", newdev->hashesPerClock); - } - - for (cnt=0; cnt < 255; cnt++) { - newdev->errorCount[cnt] = 0; - newdev->errorWeight[cnt] = 0; - newdev->errorRate[cnt] = 0; - newdev->maxErrorRate[cnt] = 0; - } - - // fake that the last round found something valid - newdev->nonceCheckValid = 1; - - newdev->usbbus = libusb_get_bus_number(dev); - newdev->usbaddress = libusb_get_device_address(dev); - sprintf(newdev->repr, "ZTEX %s-1", newdev->snString); - return 0; -} - -void libztex_destroy_device(struct libztex_device* ztex) -{ - if (ztex->hndl != NULL) { - libusb_close(ztex->hndl); - ztex->hndl = NULL; - } - if (ztex->bitFileName != NULL) { - free(ztex->bitFileName); - ztex->bitFileName = NULL; - } - free(ztex); -} - -int libztex_scanDevices(struct libztex_dev_list*** devs_p) -{ - int usbdevices[LIBZTEX_MAX_DESCRIPTORS]; - struct libztex_dev_list **devs = NULL; - struct libztex_device *ztex = NULL; - int found, max_found = 0, pos = 0, err, rescan, ret = 0; - libusb_device **list = NULL; - ssize_t cnt, i; - - do { - cnt = libusb_get_device_list(NULL, &list); - if (unlikely(cnt < 0)) { - applog(LOG_ERR, "Ztex scan devices: Failed to list usb devices with err %d", (int)cnt); - goto done; - } - - for (found = rescan = i = 0; i < cnt; i++) { - err = libztex_checkDevice(list[i]); - switch (err) { - case CHECK_ERROR: - applog(LOG_ERR, "Ztex: Can not check device: %d", err); - continue; - case CHECK_IS_NOT_ZTEX: - continue; - case CHECK_OK: - // Got one! - usbdevices[found++] = i; - break; - case CHECK_RESCAN: - rescan = 1; - found++; - break; - } - } - - if (found < max_found) - rescan = 1; - else if (found > max_found) - max_found = found; - - if (rescan) - libusb_free_device_list(list, 1); - } while (rescan); - - if (0 == found) - goto done; - - devs = malloc(sizeof(struct libztex_dev_list *) * found); - if (devs == NULL) { - applog(LOG_ERR, "Ztex scan devices: Failed to allocate memory"); - goto done; - } - - for (i = 0; i < found; i++) { - if (!ztex) { - ztex = malloc(sizeof(*ztex)); - if (!ztex) { - applog(LOG_ERR, "%s: Can not allocate memory for device struct: %s", __func__, strerror(errno)); - goto done; - } - } - - ztex->bitFileName = NULL; - ztex->numberOfFpgas = -1; - - err = libztex_prepare_device(list[usbdevices[i]], &ztex); - if (unlikely(err != 0)) { - applog(LOG_ERR, "prepare device: %d", err); - libztex_destroy_device(ztex); - ztex = NULL; - continue; - } - - devs[pos] = malloc(sizeof(struct libztex_dev_list)); - if (NULL == devs[pos]) { - applog(LOG_ERR, "%s: Can not allocate memory for device: %s", __func__, strerror(errno)); - libztex_destroy_device(ztex); - ztex = NULL; - continue; - } - - devs[pos]->dev = ztex; - ztex = NULL; - devs[pos]->next = NULL; - if (pos > 0) - devs[pos - 1]->next = devs[pos]; - pos++; - } - - ret = pos; - -done: - if (ret > 0) - *devs_p = devs; - else if (devs) - free(devs); - if (list) - libusb_free_device_list(list, 1); - return ret; -} - -int libztex_sendHashData(struct libztex_device *ztex, unsigned char *sendbuf) -{ - int cnt = 0, ret, len; - - if (ztex == NULL || ztex->hndl == NULL) - return 0; - ret = 44; len = 0; - while (ret > 0) { - cnt = libusb_control_transfer(ztex->hndl, 0x40, 0x80, 0, 0, sendbuf + len, ret, 1000); - if (cnt >= 0) { - ret -= cnt; - len += cnt; - } else - break; - } - if (unlikely(cnt < 0)) - applog(LOG_ERR, "%s: Failed sendHashData with err %d", ztex->repr, cnt); - - return cnt; -} - -int libztex_readHashData(struct libztex_device *ztex, struct libztex_hash_data nonces[]) -{ - int bufsize = 12 + ztex->extraSolutions * 4; - int cnt = 0, i, j, ret, len; - unsigned char *rbuf; - - if (ztex->hndl == NULL) - return 0; - - rbuf = malloc(sizeof(unsigned char) * (ztex->numNonces * bufsize)); - if (rbuf == NULL) { - applog(LOG_ERR, "%s: Failed to allocate memory for reading nonces", ztex->repr); - return 0; - } - ret = bufsize * ztex->numNonces; len = 0; - while (ret > 0) { - cnt = libusb_control_transfer(ztex->hndl, 0xc0, 0x81, 0, 0, rbuf + len, ret, 1000); - if (cnt >= 0) { - ret -= cnt; - len += cnt; - } else - break; - } - - if (unlikely(cnt < 0)) { - applog(LOG_ERR, "%s: Failed readHashData with err %d", ztex->repr, cnt); - free(rbuf); - return cnt; - } - - for (i=0; inumNonces; i++) { - memcpy((char*)&nonces[i].goldenNonce[0], &rbuf[i*bufsize], 4); - nonces[i].goldenNonce[0] -= ztex->offsNonces; - //applog(LOG_DEBUG, "W %d:0 %0.8x", i, nonces[i].goldenNonce[0]); - - memcpy((char*)&nonces[i].nonce, &rbuf[(i*bufsize)+4], 4); - memcpy((char*)&nonces[i].hash7, &rbuf[(i*bufsize)+8], 4); - - nonces[i].nonce = htole32(nonces[i].nonce); - nonces[i].hash7 = htole32(nonces[i].hash7); - - nonces[i].nonce -= ztex->offsNonces; - - for (j=0; jextraSolutions; j++) { - memcpy((char*)&nonces[i].goldenNonce[j+1], &rbuf[(i*bufsize)+12+(j*4)], 4); - nonces[i].goldenNonce[j+1] = htole32(nonces[i].goldenNonce[j+1]); - nonces[i].goldenNonce[j+1] -= ztex->offsNonces; - //applog(LOG_DEBUG, "W %d:%d %0.8x", i, j+1, nonces[i].goldenNonce[j+1]); - } - } - - free(rbuf); - return cnt; -} - -void libztex_freeDevList(struct libztex_dev_list **devs) -{ - bool done = false; - ssize_t cnt = 0; - - while (!done) { - if (devs[cnt]->next == NULL) - done = true; - free(devs[cnt++]); - } - free(devs); -} - diff --git a/libztex.h b/libztex.h deleted file mode 100644 index c1d09b6010..0000000000 --- a/libztex.h +++ /dev/null @@ -1,108 +0,0 @@ -/** - * libztex.h - headers for Ztex 1.15x fpga board support library - * - * Copyright (c) 2012 nelisky.btc@gmail.com - * - * This work is based upon the Java SDK provided by ztex which is - * Copyright (C) 2009-2011 ZTEX GmbH. - * http://www.ztex.de - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, see http://www.gnu.org/licenses/. -**/ -#ifndef __LIBZTEX_H__ -#define __LIBZTEX_H__ - -#include - -#define LIBZTEX_MAX_DESCRIPTORS 512 -#define LIBZTEX_SNSTRING_LEN 10 - -#define LIBZTEX_IDVENDOR 0x221A -#define LIBZTEX_IDPRODUCT 0x0100 - -#define LIBZTEX_MAXMAXERRORRATE 0.05 -#define LIBZTEX_ERRORHYSTERESIS 0.1 -#define LIBZTEX_OVERHEATTHRESHOLD 0.4 - -struct libztex_fpgastate { - bool fpgaConfigured; - unsigned char fpgaChecksum; - uint16_t fpgaBytes; - unsigned char fpgaInitB; - unsigned char fpgaFlashResult; - bool fpgaFlashBitSwap; -}; - -struct libztex_device { - pthread_mutex_t mutex; - struct libztex_device *root; - int16_t fpgaNum; - struct libusb_device_descriptor descriptor; - libusb_device_handle *hndl; - unsigned char usbbus; - unsigned char usbaddress; - unsigned char snString[LIBZTEX_SNSTRING_LEN+1]; - unsigned char productId[4]; - unsigned char fwVersion; - unsigned char interfaceVersion; - unsigned char interfaceCapabilities[6]; - unsigned char moduleReserved[12]; - uint8_t numNonces; - uint16_t offsNonces; - double freqM1; - uint8_t freqM; - uint8_t freqMaxM; - uint8_t freqMDefault; - char* bitFileName; - bool suspendSupported; - double hashesPerClock; - uint8_t extraSolutions; - - double errorCount[256]; - double errorWeight[256]; - double errorRate[256]; - double maxErrorRate[256]; - - int16_t nonceCheckValid; - - int16_t numberOfFpgas; - int selectedFpga; - bool parallelConfigSupport; - - char repr[20]; -}; - -struct libztex_dev_list { - struct libztex_device *dev; - struct libztex_dev_list *next; -}; - -struct libztex_hash_data { - uint32_t goldenNonce[2]; - uint32_t nonce; - uint32_t hash7; -}; - -extern int libztex_scanDevices (struct libztex_dev_list ***devs); -extern void libztex_freeDevList (struct libztex_dev_list **devs); -extern int libztex_prepare_device (struct libusb_device *dev, struct libztex_device** ztex); -extern void libztex_destroy_device (struct libztex_device* ztex); -extern int libztex_configureFpga (struct libztex_device *dev); -extern int libztex_setFreq (struct libztex_device *ztex, uint16_t freq); -extern int libztex_sendHashData (struct libztex_device *ztex, unsigned char *sendbuf); -extern int libztex_readHashData (struct libztex_device *ztex, struct libztex_hash_data nonces[]); -extern int libztex_resetFpga (struct libztex_device *ztex); -extern int libztex_selectFpga(struct libztex_device *ztex); -extern int libztex_numberOfFpgas(struct libztex_device *ztex); - -#endif /* __LIBZTEX_H__ */ diff --git a/logging.c b/logging.c index d00600f8e6..c54cbefbff 100644 --- a/logging.c +++ b/logging.c @@ -20,12 +20,23 @@ bool opt_log_output = false; /* per default priorities higher than LOG_NOTICE are logged */ int opt_log_level = LOG_NOTICE; +FILE * g_log_file = NULL; -static void my_log_curses(int prio, const char *datetime, const char *str) +bool g_logfile_enable = false; +char g_logfile_path[256] = {0}; +char g_logfile_openflag[32] = {0}; + +static void my_log_curses(int prio, const char *datetime, const char *str, bool force) { if (opt_quiet && prio != LOG_ERR) return; + /* Mutex could be locked by dead thread on shutdown so forcelog will + * invalidate any console lock status. */ + if (force) { + mutex_trylock(&console_lock); + mutex_unlock(&console_lock); + } #ifdef HAVE_CURSES extern bool use_curses; if (use_curses && log_curses_only(prio, datetime, str)) @@ -44,11 +55,11 @@ static void my_log_curses(int prio, const char *datetime, const char *str) /* * log function */ -void _applog(int prio, const char *str) +void _applog(int prio, const char *str, bool force) { #ifdef HAVE_SYSLOG_H if (use_syslog) { - syslog(prio, "%s", str); + syslog(LOG_LOCAL0 | prio, "%s", str); } #else if (0) {} @@ -76,7 +87,37 @@ void _applog(int prio, const char *str) fprintf(stderr, "%s%s\n", datetime, str); /* atomic write to stderr */ fflush(stderr); } + if(g_logfile_enable) { + if(!g_log_file) { + g_log_file = fopen(g_logfile_path, g_logfile_openflag); + } + if(g_log_file) { + fwrite(datetime, strlen(datetime), 1, g_log_file); + fwrite(str, strlen(str), 1, g_log_file); + fwrite("\n", 1, 1, g_log_file); + fflush(g_log_file); + } + } + my_log_curses(prio, datetime, str, force); + } +} + +void _simplelog(int prio, const char *str, bool force) +{ +#ifdef HAVE_SYSLOG_H + if (use_syslog) { + syslog(LOG_LOCAL0 | prio, "%s", str); + } +#else + if (0) {} +#endif + else { + /* Only output to stderr if it's not going to the screen as well */ + if (!isatty(fileno((FILE *)stderr))) { + fprintf(stderr, "%s\n", str); /* atomic write to stderr */ + fflush(stderr); + } - my_log_curses(prio, datetime, str); + my_log_curses(prio, "", str, force); } } diff --git a/logging.h b/logging.h index 2956452fa3..bd053d4690 100644 --- a/logging.h +++ b/logging.h @@ -28,7 +28,8 @@ extern int opt_log_level; #define LOGBUFSIZ 256 -extern void _applog(int prio, const char *str); +extern void _applog(int prio, const char *str, bool force); +extern void _simplelog(int prio, const char *str, bool force); #define IN_FMT_FFL " in %s %s():%d" @@ -37,7 +38,17 @@ extern void _applog(int prio, const char *str); if (use_syslog || opt_log_output || prio <= opt_log_level) { \ char tmp42[LOGBUFSIZ]; \ snprintf(tmp42, sizeof(tmp42), fmt, ##__VA_ARGS__); \ - _applog(prio, tmp42); \ + _applog(prio, tmp42, false); \ + } \ + } \ +} while (0) + +#define simplelog(prio, fmt, ...) do { \ + if (opt_debug || prio != LOG_DEBUG) { \ + if (use_syslog || opt_log_output || prio <= opt_log_level) { \ + char tmp42[LOGBUFSIZ]; \ + snprintf(tmp42, sizeof(tmp42), fmt, ##__VA_ARGS__); \ + _simplelog(prio, tmp42, false); \ } \ } \ } while (0) @@ -47,7 +58,17 @@ extern void _applog(int prio, const char *str); if (use_syslog || opt_log_output || prio <= opt_log_level) { \ char tmp42[_SIZ]; \ snprintf(tmp42, sizeof(tmp42), fmt, ##__VA_ARGS__); \ - _applog(prio, tmp42); \ + _applog(prio, tmp42, false); \ + } \ + } \ +} while (0) + +#define forcelog(prio, fmt, ...) do { \ + if (opt_debug || prio != LOG_DEBUG) { \ + if (use_syslog || opt_log_output || prio <= opt_log_level) { \ + char tmp42[LOGBUFSIZ]; \ + snprintf(tmp42, sizeof(tmp42), fmt, ##__VA_ARGS__); \ + _applog(prio, tmp42, true); \ } \ } \ } while (0) @@ -56,17 +77,26 @@ extern void _applog(int prio, const char *str); if (fmt) { \ char tmp42[LOGBUFSIZ]; \ snprintf(tmp42, sizeof(tmp42), fmt, ##__VA_ARGS__); \ - _applog(LOG_ERR, tmp42); \ + _applog(LOG_ERR, tmp42, true); \ } \ _quit(status); \ } while (0) +#define early_quit(status, fmt, ...) do { \ + if (fmt) { \ + char tmp42[LOGBUFSIZ]; \ + snprintf(tmp42, sizeof(tmp42), fmt, ##__VA_ARGS__); \ + _applog(LOG_ERR, tmp42, true); \ + } \ + __quit(status, false); \ +} while (0) + #define quithere(status, fmt, ...) do { \ if (fmt) { \ char tmp42[LOGBUFSIZ]; \ snprintf(tmp42, sizeof(tmp42), fmt IN_FMT_FFL, \ ##__VA_ARGS__, __FILE__, __func__, __LINE__); \ - _applog(LOG_ERR, tmp42); \ + _applog(LOG_ERR, tmp42, true); \ } \ _quit(status); \ } while (0) @@ -76,7 +106,7 @@ extern void _applog(int prio, const char *str); char tmp42[LOGBUFSIZ]; \ snprintf(tmp42, sizeof(tmp42), fmt IN_FMT_FFL, \ ##__VA_ARGS__, _file, _func, _line); \ - _applog(LOG_ERR, tmp42); \ + _applog(LOG_ERR, tmp42, true); \ } \ _quit(status); \ } while (0) diff --git a/mcp2210.c b/mcp2210.c new file mode 100644 index 0000000000..83ff95ffb5 --- /dev/null +++ b/mcp2210.c @@ -0,0 +1,366 @@ +/* + * Copyright 2014 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 "miner.h" +#include "usbutils.h" +#include "mcp2210.h" + +static bool mcp2210_send(struct cgpu_info *cgpu, char *buf, enum usb_cmds cmd) +{ + int amount, err; + + if (unlikely(cgpu->usbinfo.nodev)) + return false; + + err = usb_write(cgpu, buf, MCP2210_BUFFER_LENGTH, &amount, cmd); + if (err || amount != MCP2210_BUFFER_LENGTH) { + applog(LOG_WARNING, "%s %d: Error %d sending %s sent %d of %d", + cgpu->drv->name, cgpu->device_id, err, usb_cmdname(cmd), + amount, MCP2210_BUFFER_LENGTH); + return false; + } + return true; +} + +static bool mcp2210_recv(struct cgpu_info *cgpu, char *buf, enum usb_cmds cmd) +{ + int amount, err; + + if (unlikely(cgpu->usbinfo.nodev)) + return false; + + err = usb_read(cgpu, buf, MCP2210_BUFFER_LENGTH, &amount, cmd); + if (err || amount != MCP2210_BUFFER_LENGTH) { + applog(LOG_WARNING, "%s %d: Error %d receiving %s received %d of %d", + cgpu->drv->name, cgpu->device_id, err, usb_cmdname(cmd), + amount, MCP2210_BUFFER_LENGTH); + return false; + } + return true; +} + +bool mcp2210_send_recv(struct cgpu_info *cgpu, char *buf, enum usb_cmds cmd) +{ + uint8_t mcp_cmd = buf[0]; + + if (!mcp2210_send(cgpu, buf, cmd)) + return false; + + if (!mcp2210_recv(cgpu, buf, cmd)) + return false; + + /* Return code should always echo original command */ + if (buf[0] != mcp_cmd) { + applog(LOG_WARNING, "%s %d: Response code mismatch, asked for %u got %u", + cgpu->drv->name, cgpu->device_id, mcp_cmd, buf[0]); + return false; + } + return true; +} + +bool mcp2210_get_gpio_settings(struct cgpu_info *cgpu, struct mcp_settings *mcp) +{ + char buf[MCP2210_BUFFER_LENGTH]; + int i; + + memset(buf, 0, MCP2210_BUFFER_LENGTH); + buf[0] = MCP2210_GET_GPIO_SETTING; + if (!mcp2210_send_recv(cgpu, buf, C_MCP_GETGPIOSETTING)) + return false; + + for (i = 0; i < 8; i++) { + mcp->designation.pin[i] = buf[4 + i]; + mcp->value.pin[i] = !!(buf[13] & (0x01u << i)); + mcp->direction.pin[i] = !!(buf[15] & (0x01u << i)); + } + mcp->designation.pin[8] = buf[12]; + mcp->value.pin[8] = buf[14] & 0x01u; + mcp->direction.pin[8] = buf[16] & 0x01u; + + return true; +} + +bool mcp2210_set_gpio_settings(struct cgpu_info *cgpu, struct mcp_settings *mcp) +{ + char buf[MCP2210_BUFFER_LENGTH]; + uint8_t buf17; + int i; + + memset(buf, 0, MCP2210_BUFFER_LENGTH); + buf[0] = MCP2210_GET_GPIO_SETTING; + if (!mcp2210_send_recv(cgpu, buf, C_MCP_GETGPIOSETTING)) + return false; + buf17 = buf[17]; + + memset(buf, 0, MCP2210_BUFFER_LENGTH); + buf[0] = MCP2210_SET_GPIO_SETTING; + buf[17] = buf17; + for (i = 0; i < 8; i++) { + buf[4 + i] = mcp->designation.pin[i]; + buf[13] |= mcp->value.pin[i] << i; + buf[15] |= mcp->direction.pin[i] << i; + } + buf[12] = mcp->designation.pin[8]; + buf[14] = mcp->value.pin[8]; + buf[16] = mcp->direction.pin[8]; + return mcp2210_send_recv(cgpu, buf, C_MCP_SETGPIOSETTING); +} + +/* Get all the pin designations and store them in a gpio_pin struct */ +bool mcp2210_get_gpio_pindes(struct cgpu_info *cgpu, struct gpio_pin *gp) +{ + char buf[MCP2210_BUFFER_LENGTH]; + int i; + + memset(buf, 0, MCP2210_BUFFER_LENGTH); + buf[0] = MCP2210_GET_GPIO_SETTING; + if (!mcp2210_send_recv(cgpu, buf, C_MCP_GETGPIOSETTING)) + return false; + + for (i = 0; i < 9; i++) + gp->pin[i] = buf[4 + i]; + return true; +} + + +/* Get all the pin vals and store them in a gpio_pin struct */ +bool mcp2210_get_gpio_pinvals(struct cgpu_info *cgpu, struct gpio_pin *gp) +{ + char buf[MCP2210_BUFFER_LENGTH]; + int i; + + memset(buf, 0, MCP2210_BUFFER_LENGTH); + buf[0] = MCP2210_GET_GPIO_PIN_VAL; + if (!mcp2210_send_recv(cgpu, buf, C_MCP_GETGPIOPINVAL)) + return false; + + for (i = 0; i < 8; i++) + gp->pin[i] = !!(buf[4] & (0x01u << i)); + gp->pin[8] = buf[5] & 0x01u; + + return true; +} + +/* Get all the pindirs */ +bool mcp2210_get_gpio_pindirs(struct cgpu_info *cgpu, struct gpio_pin *gp) +{ + char buf[MCP2210_BUFFER_LENGTH]; + int i; + + memset(buf, 0, MCP2210_BUFFER_LENGTH); + buf[0] = MCP2210_GET_GPIO_PIN_DIR; + if (!mcp2210_send_recv(cgpu, buf, C_MCP_GETGPIOPINDIR)) + return false; + + for (i = 0; i < 8; i++) + gp->pin[i] = !!(buf[4] & (0x01u << i)); + gp->pin[8] = buf[5] & 0x01u; + + return true; +} + +/* Get the designation of one pin */ +bool mcp2210_get_gpio_pin(struct cgpu_info *cgpu, int pin, int *des) +{ + struct gpio_pin gp; + + if (!mcp2210_get_gpio_pindes(cgpu, &gp)) + return false; + + *des = gp.pin[pin]; + return true; +} + +/* Get one pinval */ +bool mcp2210_get_gpio_pinval(struct cgpu_info *cgpu, int pin, int *val) +{ + char buf[MCP2210_BUFFER_LENGTH]; + + memset(buf, 0, MCP2210_BUFFER_LENGTH); + buf[0] = MCP2210_GET_GPIO_PIN_VAL; + if (!mcp2210_send_recv(cgpu, buf, C_MCP_GETGPIOPINVAL)) + return false; + + buf[0] = MCP2210_GET_GPIO_PIN_VAL; + + if (pin < 8) + *val = !!(buf[4] & (0x01u << pin)); + else + *val = !!(buf[5] & 0x01u); + + return true; +} + +/* Get one pindir */ +bool mcp2210_get_gpio_pindir(struct cgpu_info *cgpu, int pin, int *dir) +{ + char buf[MCP2210_BUFFER_LENGTH]; + + memset(buf, 0, MCP2210_BUFFER_LENGTH); + buf[0] = MCP2210_GET_GPIO_PIN_DIR; + if (!mcp2210_send_recv(cgpu, buf, C_MCP_GETGPIOPINDIR)) + return false; + + buf[0] = MCP2210_GET_GPIO_PIN_DIR; + + if (pin < 8) + *dir = !!(buf[4] & (0x01u << pin)); + else + *dir = !!(buf[5] & 0x01u); + + return true; +} + +bool mcp2210_spi_cancel(struct cgpu_info *cgpu) +{ + char buf[MCP2210_BUFFER_LENGTH]; + + memset(buf, 0, MCP2210_BUFFER_LENGTH); + buf[0] = MCP2210_SPI_CANCEL; + return mcp2210_send_recv(cgpu, buf, C_MCP_SPICANCEL); +} + +/* Abbreviations correspond to: + * IdleChipSelectValue, ActiveChipSelectValue, CSToDataDelay, LastDataByteToCSDelay, + * SubsequentDataByteDelay, BytesPerSPITransfer + */ +bool +mcp2210_get_spi_transfer_settings(struct cgpu_info *cgpu, unsigned int *bitrate, unsigned int *icsv, + unsigned int *acsv, unsigned int *cstdd, unsigned int *ldbtcsd, + unsigned int *sdbd, unsigned int *bpst, unsigned int *spimode) +{ + char buf[MCP2210_BUFFER_LENGTH]; + + memset(buf, 0, MCP2210_BUFFER_LENGTH); + buf[0] = MCP2210_GET_SPI_SETTING; + + if (!mcp2210_send_recv(cgpu, buf, C_MCP_GETSPISETTING)) + return false; + *bitrate = buf[7] << 24 | buf[6] << 16 | buf[5] << 8 | buf[4]; + *icsv = (buf[9] & 0x1) << 8 | buf[8]; + *acsv = (buf[11] & 0x1) << 8 | buf[10]; + *cstdd = buf[13] << 8 | buf[12]; + *ldbtcsd = buf[15] << 8 | buf[14]; + *sdbd = buf[17] << 8 | buf[16]; + *bpst = buf[19] << 8 | buf[18]; + *spimode = buf[20]; + return true; +} + +bool +mcp2210_set_spi_transfer_settings(struct cgpu_info *cgpu, unsigned int bitrate, unsigned int icsv, + unsigned int acsv, unsigned int cstdd, unsigned int ldbtcsd, + unsigned int sdbd, unsigned int bpst, unsigned int spimode) +{ + char buf[MCP2210_BUFFER_LENGTH]; + bool ret; + + memset(buf, 0, MCP2210_BUFFER_LENGTH); + buf[0] = MCP2210_SET_SPI_SETTING; + + buf[4] = bitrate & 0xfful; + buf[5] = (bitrate & 0xff00ul) >> 8; + buf[6] = (bitrate & 0xff0000ul) >> 16; + buf[7] = (bitrate & 0xff000000ul) >> 24; + + buf[8] = icsv & 0xff; + buf[9] = (icsv & 0x100) >> 8; + + buf[10] = acsv & 0xff; + buf[11] = (acsv & 0x100) >> 8; + + buf[12] = cstdd & 0xff; + buf[13] = (cstdd & 0xff00) >> 8; + + buf[14] = ldbtcsd & 0xff; + buf[15] = (ldbtcsd & 0xff00) >> 8; + + buf[16] = sdbd & 0xff; + buf[17] = (sdbd & 0xff00) >> 8; + + buf[18] = bpst & 0xff; + buf[19] = (bpst & 0xff00) >> 8; + + buf[20] = spimode; + ret = mcp2210_send_recv(cgpu, buf, C_MCP_SETSPISETTING); + if (!ret) + return ret; + if (buf[1] != 0) { + applog(LOG_DEBUG, "Failed to set spi settings"); + return false; + } + return true; +} + +/* Perform an spi transfer of *length bytes and return the amount of data + * returned in the same buffer in *length */ +bool mcp2210_spi_transfer(struct cgpu_info *cgpu, struct mcp_settings *mcp, + char *data, unsigned int *length) +{ + uint8_t res, status, orig_len, offset = 0; + char buf[MCP2210_BUFFER_LENGTH]; + + if (unlikely(*length > MCP2210_TRANSFER_MAX || !*length)) { + applog(LOG_ERR, "%s %d: Unable to spi transfer %u bytes", cgpu->drv->name, + cgpu->device_id, *length); + return false; + } + if (mcp->bpst != *length) { + /* Set the transfer setting only when it changes. */ + mcp->bpst = *length; + if (!mcp2210_set_spi_transfer_settings(cgpu, mcp->bitrate, mcp->icsv, + mcp->acsv, mcp->cstdd, mcp->ldbtcsd, mcp->sdbd, mcp->bpst, mcp->spimode)) + return false; + } + orig_len = *length; +retry: + applog(LOG_DEBUG, "%s %d: SPI sending %u bytes", cgpu->drv->name, cgpu->device_id, + *length); + memset(buf, 0, MCP2210_BUFFER_LENGTH); + buf[0] = MCP2210_SPI_TRANSFER; + buf[1] = *length; + + if (*length) + memcpy(buf + 4, data + offset, *length); + if (!mcp2210_send_recv(cgpu, buf, C_MCP_SPITRANSFER)) + return false; + + res = (uint8_t)buf[1]; + switch(res) { + case MCP2210_SPI_TRANSFER_SUCCESS: + *length = buf[2]; + status = buf[3]; + applog(LOG_DEBUG, "%s %d: SPI transfer success, received %u bytes status 0x%x", + cgpu->drv->name, cgpu->device_id, *length, status); + if (*length) { + memcpy(data + offset, buf + 4, *length); + offset += *length; + } + if (status == 0x30) { + /* This shouldn't happen */ + applog(LOG_DEBUG, "%s %d: SPI expecting more data inappropriately", + cgpu->drv->name, cgpu->device_id); + return false; + } + if (offset < orig_len) { + *length = 0; + goto retry; + } + *length = orig_len; + return true; + case MCP2210_SPI_TRANSFER_ERROR_IP: + applog(LOG_DEBUG, "%s %d: SPI transfer error in progress", + cgpu->drv->name, cgpu->device_id); + goto retry; + case MCP2210_SPI_TRANSFER_ERROR_NA: + applog(LOG_WARNING, "%s %d: External owner error on mcp2210 spi transfer", + cgpu->drv->name, cgpu->device_id); + default: + return false; + } +} diff --git a/mcp2210.h b/mcp2210.h new file mode 100644 index 0000000000..cb7544355e --- /dev/null +++ b/mcp2210.h @@ -0,0 +1,73 @@ +/* + * Copyright 2014 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 MCP2210_H +#define MCP2210_H + +#define MCP2210_BUFFER_LENGTH 64 +#define MCP2210_TRANSFER_MAX 60 + +#define MCP2210_PIN_GPIO 0x0 +#define MCP2210_PIN_CS 0x1 +#define MCP2210_PIN_DEDICATED 0x2 + +#define MCP2210_GPIO_PIN_LOW 0 +#define MCP2210_GPIO_PIN_HIGH 1 + +#define MCP2210_GPIO_OUTPUT 0 +#define MCP2210_GPIO_INPUT 1 + +#define MCP2210_SPI_CANCEL 0x11 +#define MCP2210_GET_GPIO_SETTING 0x20 +#define MCP2210_SET_GPIO_SETTING 0x21 +#define MCP2210_SET_GPIO_PIN_VAL 0x30 +#define MCP2210_GET_GPIO_PIN_VAL 0x31 +#define MCP2210_SET_GPIO_PIN_DIR 0x32 +#define MCP2210_GET_GPIO_PIN_DIR 0x33 +#define MCP2210_SET_SPI_SETTING 0X40 +#define MCP2210_GET_SPI_SETTING 0X41 +#define MCP2210_SPI_TRANSFER 0x42 + +#define MCP2210_SPI_TRANSFER_SUCCESS 0x00 +#define MCP2210_SPI_TRANSFER_ERROR_NA 0xF7 // SPI not available due to external owner +#define MCP2210_SPI_TRANSFER_ERROR_IP 0xF8 // SPI not available due to transfer in progress + +struct gpio_pin { + uint8_t pin[9]; +}; + +struct mcp_settings { + struct gpio_pin designation; + struct gpio_pin value; + struct gpio_pin direction; + unsigned int bitrate, icsv, acsv, cstdd, ldbtcsd, sdbd, bpst, spimode; +}; + +bool mcp2210_send_recv(struct cgpu_info *cgpu, char *buf, enum usb_cmds cmd); +bool mcp2210_get_gpio_settings(struct cgpu_info *cgpu, struct mcp_settings *mcp); +bool mcp2210_set_gpio_settings(struct cgpu_info *cgpu, struct mcp_settings *mcp); +bool mcp2210_get_gpio_pindes(struct cgpu_info *cgpu, struct gpio_pin *gp); +bool mcp2210_get_gpio_pinvals(struct cgpu_info *cgpu, struct gpio_pin *gp); +bool mcp2210_get_gpio_pindirs(struct cgpu_info *cgpu, struct gpio_pin *gp); +bool mcp2210_get_gpio_pin(struct cgpu_info *cgpu, int pin, int *des); +bool mcp2210_get_gpio_pinval(struct cgpu_info *cgpu, int pin, int *val); +bool mcp2210_get_gpio_pindir(struct cgpu_info *cgpu, int pin, int *dir); +bool mcp2210_spi_cancel(struct cgpu_info *cgpu); +bool +mcp2210_get_spi_transfer_settings(struct cgpu_info *cgpu, unsigned int *bitrate, unsigned int *icsv, + unsigned int *acsv, unsigned int *cstdd, unsigned int *ldbtcsd, + unsigned int *sdbd, unsigned int *bpst, unsigned int *spimode); +bool +mcp2210_set_spi_transfer_settings(struct cgpu_info *cgpu, unsigned int bitrate, unsigned int icsv, + unsigned int acsv, unsigned int cstdd, unsigned int ldbtcsd, + unsigned int sdbd, unsigned int bpst, unsigned int spimode); +bool mcp2210_spi_transfer(struct cgpu_info *cgpu, struct mcp_settings *mcp, + char *data, unsigned int *length); + +#endif /* MCP2210_H */ diff --git a/miner.h b/miner.h index 2300a27394..7f96f56d86 100644 --- a/miner.h +++ b/miner.h @@ -8,7 +8,17 @@ #include #include #include +#ifdef HAVE_LIBCURL #include +#else +typedef char CURL; +extern char *curly; +#define curl_easy_init(curl) (curly) +#define curl_easy_cleanup(curl) {} +#define curl_global_cleanup() {} +#define CURL_GLOBAL_ALL 0 +#define curl_global_init(X) (0) +#endif #include #include "elist.h" @@ -25,14 +35,6 @@ #include #endif -#ifdef HAVE_OPENCL -#ifdef __APPLE_CC__ -#include -#else -#include -#endif -#endif /* HAVE_OPENCL */ - #ifdef STDC_HEADERS # include # include @@ -113,18 +115,10 @@ static inline int fsync (int fd) #endif -#ifdef HAVE_ADL - #include "ADL_SDK/adl_sdk.h" -#endif - #ifdef USE_USBUTILS #include #endif -#ifdef USE_ZTEX - #include "libztex.h" -#endif - #ifdef USE_USBUTILS #include "usbutils.h" #endif @@ -132,7 +126,8 @@ static inline int fsync (int fd) #if (!defined(WIN32) && ((__GNUC__ > 4) || (__GNUC__ == 4 && __GNUC_MINOR__ >= 3))) \ || (defined(WIN32) && ((__GNUC__ > 4) || (__GNUC__ == 4 && __GNUC_MINOR__ >= 7))) #ifndef bswap_16 - #define bswap_16 __builtin_bswap16 + #define bswap_16(value) \ + ((((value) & 0xff) << 8) | ((value) >> 8)) #define bswap_32 __builtin_bswap32 #define bswap_64 __builtin_bswap64 #endif @@ -161,28 +156,54 @@ static inline int fsync (int fd) #endif #endif /* !defined(__GLXBYTEORDER_H__) */ +#ifndef bswap_8 +extern unsigned char bit_swap_table[256]; +#define bswap_8(x) (bit_swap_table[x]) +#endif + /* This assumes htobe32 is a macro in endian.h, and if it doesn't exist, then * htobe64 also won't exist */ #ifndef htobe32 # if __BYTE_ORDER == __LITTLE_ENDIAN +# define htole8(x) (x) # define htole16(x) (x) +# define le16toh(x) (x) # define htole32(x) (x) +# define htole64(x) (x) # define le32toh(x) (x) +# define le64toh(x) (x) # define be32toh(x) bswap_32(x) # define be64toh(x) bswap_64(x) +# define htobe16(x) bswap_16(x) # define htobe32(x) bswap_32(x) # define htobe64(x) bswap_64(x) # elif __BYTE_ORDER == __BIG_ENDIAN +# define htole8(x) bswap_8(x) # define htole16(x) bswap_16(x) +# define le16toh(x) bswap_16(x) # define htole32(x) bswap_32(x) # define le32toh(x) bswap_32(x) +# define le64toh(x) bswap_64(x) +# define htole64(x) bswap_64(x) # define be32toh(x) (x) # define be64toh(x) (x) +# define htobe16(x) (x) # define htobe32(x) (x) # define htobe64(x) (x) #else #error UNKNOWN BYTE ORDER #endif + +#else + +# if __BYTE_ORDER == __LITTLE_ENDIAN +# define htole8(x) (x) +# elif __BYTE_ORDER == __BIG_ENDIAN +# define htole8(x) bswap_8(x) +#else +#error UNKNOWN BYTE ORDER +#endif + #endif #undef unlikely @@ -206,31 +227,64 @@ static inline int fsync (int fd) #define ARRAY_SIZE(arr) (sizeof(arr) / sizeof((arr)[0])) #endif -#ifdef MIPSEB -#ifndef roundl -#define roundl(x) (long double)((long long)((x==0)?0.0:((x)+((x)>0)?0.5:-0.5))) -#endif -#endif - /* No semtimedop on apple so ignore timeout till we implement one */ #ifdef __APPLE__ #define semtimedop(SEM, SOPS, VAL, TIMEOUT) semop(SEM, SOPS, VAL) #endif +#ifndef MIN #define MIN(x, y) ((x) > (y) ? (y) : (x)) +#endif +#ifndef MAX #define MAX(x, y) ((x) > (y) ? (x) : (y)) +#endif +/* Put avalon last to make it the last device it tries to detect to prevent it + * trying to claim same chip but different devices. Adding a device here will + * update all macros in the code that use the *_PARSE_COMMANDS macros for each + * listed driver. */ +#define FPGA_PARSE_COMMANDS(DRIVER_ADD_COMMAND) \ + DRIVER_ADD_COMMAND(bitforce) \ + DRIVER_ADD_COMMAND(modminer) + +#define ASIC_PARSE_COMMANDS(DRIVER_ADD_COMMAND) \ + DRIVER_ADD_COMMAND(bitmain) \ + DRIVER_ADD_COMMAND(bmsc) \ + DRIVER_ADD_COMMAND(avalon) \ + DRIVER_ADD_COMMAND(avalon2) \ + DRIVER_ADD_COMMAND(avalon4) \ + DRIVER_ADD_COMMAND(bflsc) \ + DRIVER_ADD_COMMAND(bitfury) \ + DRIVER_ADD_COMMAND(blockerupter) \ + DRIVER_ADD_COMMAND(cointerra) \ + DRIVER_ADD_COMMAND(hashfast) \ + DRIVER_ADD_COMMAND(hashratio) \ + DRIVER_ADD_COMMAND(icarus) \ + DRIVER_ADD_COMMAND(klondike) \ + DRIVER_ADD_COMMAND(knc) \ + DRIVER_ADD_COMMAND(bitmineA1) \ + DRIVER_ADD_COMMAND(drillbit) \ + DRIVER_ADD_COMMAND(bab) \ + DRIVER_ADD_COMMAND(minion) \ + DRIVER_ADD_COMMAND(sp10) \ + DRIVER_ADD_COMMAND(sp30) + +#define DRIVER_PARSE_COMMANDS(DRIVER_ADD_COMMAND) \ + FPGA_PARSE_COMMANDS(DRIVER_ADD_COMMAND) \ + ASIC_PARSE_COMMANDS(DRIVER_ADD_COMMAND) + +#define DRIVER_ENUM(X) DRIVER_##X, +#define DRIVER_PROTOTYPE(X) struct device_drv X##_drv; + +/* Create drv_driver enum from DRIVER_PARSE_COMMANDS macro */ enum drv_driver { - DRIVER_OPENCL = 0, - DRIVER_ICARUS, - DRIVER_BITFORCE, - DRIVER_MODMINER, - DRIVER_ZTEX, - DRIVER_BFLSC, - DRIVER_AVALON, + DRIVER_PARSE_COMMANDS(DRIVER_ENUM) DRIVER_MAX }; +/* Use DRIVER_PARSE_COMMANDS to generate extern device_drv prototypes */ +DRIVER_PARSE_COMMANDS(DRIVER_PROTOTYPE) + enum alive { LIFE_WELL, LIFE_SICK, @@ -256,44 +310,6 @@ struct strategies { struct cgpu_info; -#ifdef HAVE_ADL -struct gpu_adl { - ADLTemperature lpTemperature; - int iAdapterIndex; - int lpAdapterID; - int iBusNumber; - char strAdapterName[256]; - - ADLPMActivity lpActivity; - ADLODParameters lpOdParameters; - ADLODPerformanceLevels *DefPerfLev; - ADLFanSpeedInfo lpFanSpeedInfo; - ADLFanSpeedValue lpFanSpeedValue; - ADLFanSpeedValue DefFanSpeedValue; - - int iEngineClock; - int iMemoryClock; - int iVddc; - int iPercentage; - - bool autofan; - bool autoengine; - bool managed; /* Were the values ever changed on this card */ - - int lastengine; - int lasttemp; - int targetfan; - int targettemp; - int overtemp; - int minspeed; - int maxspeed; - - int gpu; - bool has_fanspeed; - struct gpu_adl *twin; -}; -#endif - extern void blank_get_statline_before(char *buf, size_t bufsiz, struct cgpu_info __maybe_unused *cgpu); struct api_data; @@ -307,7 +323,7 @@ struct device_drv { char *name; // DRV-global functions - void (*drv_detect)(); + void (*drv_detect)(bool); // Device-specific functions void (*reinit_device)(struct cgpu_info *); @@ -336,18 +352,26 @@ struct device_drv { * the main loop that it should not add any further work to the table. */ bool (*queue_full)(struct cgpu_info *); + /* Tell the driver of a block change */ void (*flush_work)(struct cgpu_info *); + /* Tell the driver of an updated work template for eg. stratum */ + void (*update_work)(struct cgpu_info *); void (*hw_error)(struct thr_info *); void (*thread_shutdown)(struct thr_info *); void (*thread_enable)(struct thr_info *); + /* What should be zeroed in this device when global zero stats is sent */ + void (*zero_stats)(struct cgpu_info *); + // Does it need to be free()d? bool copy; /* Highest target diff the device supports */ double max_diff; - double working_diff; + + /* Lowest diff the controller can safely run at */ + double min_diff; }; extern struct device_drv *copy_drv(struct device_drv*); @@ -358,15 +382,6 @@ enum dev_enable { DEV_RECOVER, }; -enum cl_kernels { - KL_NONE, - KL_POCLBM, - KL_PHATK, - KL_DIAKGCN, - KL_DIABLO, - KL_SCRYPT, -}; - enum dev_reason { REASON_THREAD_FAIL_INIT, REASON_THREAD_ZERO_HASH, @@ -434,22 +449,26 @@ struct cgpu_info { char *name; char *device_path; void *device_data; - union { -#ifdef USE_ZTEX - struct libztex_device *device_ztex; -#endif + void *dup_data; + char *unique_id; #ifdef USE_USBUTILS - struct cg_usb_device *usbdev; + struct cg_usb_device *usbdev; + struct cg_usb_info usbinfo; + bool blacklisted; + bool nozlp; // Device prefers no zero length packet #endif - }; -#ifdef USE_AVALON +#if defined(USE_AVALON) || defined(USE_AVALON2) struct work **works; int work_array; int queued; int results; #endif -#ifdef USE_USBUTILS - struct cg_usb_info usbinfo; +#ifdef USE_BITMAIN + int device_fd; + struct work **works; + int work_array; + int queued; + int results; #endif #ifdef USE_MODMINER char fpgaid; @@ -475,6 +494,9 @@ struct cgpu_info { int rejected; int hw_errors; double rolling; + double rolling1; + double rolling5; + double rolling15; double total_mhashes; double utility; enum alive status; @@ -487,52 +509,20 @@ struct cgpu_info { int64_t max_hashes; const char *kname; -#ifdef HAVE_OPENCL - bool mapped; - int virtual_gpu; - int virtual_adl; - int intensity; - bool dynamic; - - cl_uint vwidth; - size_t work_size; - enum cl_kernels kernel; - cl_ulong max_alloc; - -#ifdef USE_SCRYPT - int opt_lg, lookup_gap; - size_t opt_tc, thread_concurrency; - size_t shaders; -#endif - struct timeval tv_gpustart; - int intervals; -#endif bool new_work; - float temp; + double temp; int cutofftemp; -#ifdef HAVE_ADL - bool has_adl; - struct gpu_adl adl; - - int gpu_engine; - int min_engine; - int gpu_fan; - int min_fan; - int gpu_memclock; - int gpu_memdiff; - int gpu_powertune; - float gpu_vddc; -#endif - int diff1; + int64_t diff1; double diff_accepted; double diff_rejected; int last_share_pool; time_t last_share_pool_time; double last_share_diff; time_t last_device_valid_work; + uint32_t last_nonce; time_t device_last_well; time_t device_last_not_well; @@ -552,11 +542,17 @@ struct cgpu_info { pthread_rwlock_t qlock; struct work *queued_work; + struct work *unqueued_work; unsigned int queued_count; bool shutdown; struct timeval dev_start_tv; + + /* For benchmarking only */ + int hidiff; + int lodiff; + int direction; }; extern bool add_cgpu(struct cgpu_info*); @@ -585,9 +581,9 @@ struct thr_info { bool pause; bool getwork; - double rolling; bool work_restart; + bool work_update; }; struct string_elist { @@ -650,6 +646,16 @@ static inline void swab256(void *dest_p, const void *src_p) dest[7] = swab32(src[0]); } +static inline void flip12(void *dest_p, const void *src_p) +{ + uint32_t *dest = dest_p; + const uint32_t *src = src_p; + int i; + + for (i = 0; i < 3; i++) + dest[i] = swab32(src[i]); +} + static inline void flip32(void *dest_p, const void *src_p) { uint32_t *dest = dest_p; @@ -713,172 +719,303 @@ endian_flip128(void __maybe_unused *dest_p, const void __maybe_unused *src_p) } #endif +extern double cgpu_runtime(struct cgpu_info *cgpu); +extern double tsince_restart(void); +extern double tsince_update(void); +extern void __quit(int status, bool clean); extern void _quit(int status); -#define mutex_lock(_lock) _mutex_lock(_lock, __FILE__, __func__, __LINE__) -#define mutex_unlock_noyield(_lock) _mutex_unlock_noyield(_lock, __FILE__, __func__, __LINE__) -#define wr_lock(_lock) _wr_lock(_lock, __FILE__, __func__, __LINE__) -#define rd_lock(_lock) _rd_lock(_lock, __FILE__, __func__, __LINE__) -#define rw_unlock(_lock) _rw_unlock(_lock, __FILE__, __func__, __LINE__) -#define mutex_init(_lock) _mutex_init(_lock, __FILE__, __func__, __LINE__) -#define rwlock_init(_lock) _rwlock_init(_lock, __FILE__, __func__, __LINE__) +/* + * Set this to non-zero to enable lock tracking + * Use the API lockstats command to see the locking status on stderr + * i.e. in your log file if you 2> log.log - but not on the screen + * API lockstats is privilidged but will always exist and will return + * success if LOCK_TRACKING is enabled and warning if disabled + * In production code, this should never be enabled since it will slow down all locking + * So, e.g. use it to track down a deadlock - after a reproducable deadlock occurs + * ... Of course if the API code itself deadlocks, it wont help :) + */ +#define LOCK_TRACKING 0 + +#if LOCK_TRACKING +enum cglock_typ { + CGLOCK_MUTEX, + CGLOCK_RW, + CGLOCK_UNKNOWN +}; + +extern uint64_t api_getlock(void *lock, const char *file, const char *func, const int line); +extern void api_gotlock(uint64_t id, void *lock, const char *file, const char *func, const int line); +extern uint64_t api_trylock(void *lock, const char *file, const char *func, const int line); +extern void api_didlock(uint64_t id, int ret, void *lock, const char *file, const char *func, const int line); +extern void api_gunlock(void *lock, const char *file, const char *func, const int line); +extern void api_initlock(void *lock, enum cglock_typ typ, const char *file, const char *func, const int line); + +#define GETLOCK(_lock, _file, _func, _line) uint64_t _id1 = api_getlock((void *)(_lock), _file, _func, _line) +#define GOTLOCK(_lock, _file, _func, _line) api_gotlock(_id1, (void *)(_lock), _file, _func, _line) +#define TRYLOCK(_lock, _file, _func, _line) uint64_t _id2 = api_trylock((void *)(_lock), _file, _func, _line) +#define DIDLOCK(_ret, _lock, _file, _func, _line) api_didlock(_id2, _ret, (void *)(_lock), _file, _func, _line) +#define GUNLOCK(_lock, _file, _func, _line) api_gunlock((void *)(_lock), _file, _func, _line) +#define INITLOCK(_lock, _typ, _file, _func, _line) api_initlock((void *)(_lock), _typ, _file, _func, _line) +#else +#define GETLOCK(_lock, _file, _func, _line) +#define GOTLOCK(_lock, _file, _func, _line) +#define TRYLOCK(_lock, _file, _func, _line) +#define DIDLOCK(_ret, _lock, _file, _func, _line) +#define GUNLOCK(_lock, _file, _func, _line) +#define INITLOCK(_typ, _lock, _file, _func, _line) +#endif + +#define mutex_lock(_lock) _mutex_lock(_lock, __FILE__, __func__, __LINE__) +#define mutex_unlock_noyield(_lock) _mutex_unlock_noyield(_lock, __FILE__, __func__, __LINE__) +#define mutex_unlock(_lock) _mutex_unlock(_lock, __FILE__, __func__, __LINE__) +#define mutex_trylock(_lock) _mutex_trylock(_lock, __FILE__, __func__, __LINE__) +#define wr_lock(_lock) _wr_lock(_lock, __FILE__, __func__, __LINE__) +#define wr_trylock(_lock) _wr_trylock(_lock, __FILE__, __func__, __LINE__) +#define rd_lock(_lock) _rd_lock(_lock, __FILE__, __func__, __LINE__) +#define rw_unlock(_lock) _rw_unlock(_lock, __FILE__, __func__, __LINE__) +#define rd_unlock_noyield(_lock) _rd_unlock_noyield(_lock, __FILE__, __func__, __LINE__) +#define wr_unlock_noyield(_lock) _wr_unlock_noyield(_lock, __FILE__, __func__, __LINE__) +#define rd_unlock(_lock) _rd_unlock(_lock, __FILE__, __func__, __LINE__) +#define wr_unlock(_lock) _wr_unlock(_lock, __FILE__, __func__, __LINE__) +#define mutex_init(_lock) _mutex_init(_lock, __FILE__, __func__, __LINE__) +#define rwlock_init(_lock) _rwlock_init(_lock, __FILE__, __func__, __LINE__) +#define cglock_init(_lock) _cglock_init(_lock, __FILE__, __func__, __LINE__) +#define cg_rlock(_lock) _cg_rlock(_lock, __FILE__, __func__, __LINE__) +#define cg_ilock(_lock) _cg_ilock(_lock, __FILE__, __func__, __LINE__) +#define cg_uilock(_lock) _cg_uilock(_lock, __FILE__, __func__, __LINE__) +#define cg_ulock(_lock) _cg_ulock(_lock, __FILE__, __func__, __LINE__) +#define cg_wlock(_lock) _cg_wlock(_lock, __FILE__, __func__, __LINE__) +#define cg_dwlock(_lock) _cg_dwlock(_lock, __FILE__, __func__, __LINE__) +#define cg_dwilock(_lock) _cg_dwilock(_lock, __FILE__, __func__, __LINE__) +#define cg_dlock(_lock) _cg_dlock(_lock, __FILE__, __func__, __LINE__) +#define cg_runlock(_lock) _cg_runlock(_lock, __FILE__, __func__, __LINE__) +#define cg_ruwlock(_lock) _cg_ruwlock(_lock, __FILE__, __func__, __LINE__) +#define cg_wunlock(_lock) _cg_wunlock(_lock, __FILE__, __func__, __LINE__) static inline void _mutex_lock(pthread_mutex_t *lock, const char *file, const char *func, const int line) { + GETLOCK(lock, file, func, line); if (unlikely(pthread_mutex_lock(lock))) quitfrom(1, file, func, line, "WTF MUTEX ERROR ON LOCK! errno=%d", errno); + GOTLOCK(lock, file, func, line); } static inline void _mutex_unlock_noyield(pthread_mutex_t *lock, const char *file, const char *func, const int line) { if (unlikely(pthread_mutex_unlock(lock))) quitfrom(1, file, func, line, "WTF MUTEX ERROR ON UNLOCK! errno=%d", errno); + GUNLOCK(lock, file, func, line); } -static inline void mutex_unlock(pthread_mutex_t *lock) +static inline void _mutex_unlock(pthread_mutex_t *lock, const char *file, const char *func, const int line) { - mutex_unlock_noyield(lock); - sched_yield(); + _mutex_unlock_noyield(lock, file, func, line); + selective_yield(); } -static inline int mutex_trylock(pthread_mutex_t *lock) +static inline int _mutex_trylock(pthread_mutex_t *lock, __maybe_unused const char *file, __maybe_unused const char *func, __maybe_unused const int line) { - return pthread_mutex_trylock(lock); + TRYLOCK(lock, file, func, line); + int ret = pthread_mutex_trylock(lock); + DIDLOCK(ret, lock, file, func, line); + return ret; } static inline void _wr_lock(pthread_rwlock_t *lock, const char *file, const char *func, const int line) { + GETLOCK(lock, file, func, line); if (unlikely(pthread_rwlock_wrlock(lock))) quitfrom(1, file, func, line, "WTF WRLOCK ERROR ON LOCK! errno=%d", errno); + GOTLOCK(lock, file, func, line); +} + +static inline int _wr_trylock(pthread_rwlock_t *lock, __maybe_unused const char *file, __maybe_unused const char *func, __maybe_unused const int line) +{ + TRYLOCK(lock, file, func, line); + int ret = pthread_rwlock_trywrlock(lock); + DIDLOCK(ret, lock, file, func, line); + return ret; } static inline void _rd_lock(pthread_rwlock_t *lock, const char *file, const char *func, const int line) { + GETLOCK(lock, file, func, line); if (unlikely(pthread_rwlock_rdlock(lock))) quitfrom(1, file, func, line, "WTF RDLOCK ERROR ON LOCK! errno=%d", errno); + GOTLOCK(lock, file, func, line); } static inline void _rw_unlock(pthread_rwlock_t *lock, const char *file, const char *func, const int line) { if (unlikely(pthread_rwlock_unlock(lock))) quitfrom(1, file, func, line, "WTF RWLOCK ERROR ON UNLOCK! errno=%d", errno); + GUNLOCK(lock, file, func, line); } -static inline void rd_unlock_noyield(pthread_rwlock_t *lock) +static inline void _rd_unlock_noyield(pthread_rwlock_t *lock, const char *file, const char *func, const int line) { - rw_unlock(lock); + _rw_unlock(lock, file, func, line); } -static inline void wr_unlock_noyield(pthread_rwlock_t *lock) +static inline void _wr_unlock_noyield(pthread_rwlock_t *lock, const char *file, const char *func, const int line) { - rw_unlock(lock); + _rw_unlock(lock, file, func, line); } -static inline void rd_unlock(pthread_rwlock_t *lock) +static inline void _rd_unlock(pthread_rwlock_t *lock, const char *file, const char *func, const int line) { - rw_unlock(lock); - sched_yield(); + _rw_unlock(lock, file, func, line); + selective_yield(); } -static inline void wr_unlock(pthread_rwlock_t *lock) +static inline void _wr_unlock(pthread_rwlock_t *lock, const char *file, const char *func, const int line) { - rw_unlock(lock); - sched_yield(); + _rw_unlock(lock, file, func, line); + selective_yield(); } static inline void _mutex_init(pthread_mutex_t *lock, const char *file, const char *func, const int line) { if (unlikely(pthread_mutex_init(lock, NULL))) quitfrom(1, file, func, line, "Failed to pthread_mutex_init errno=%d", errno); + INITLOCK(lock, CGLOCK_MUTEX, file, func, line); +} + +static inline void mutex_destroy(pthread_mutex_t *lock) +{ + /* Ignore return code. This only invalidates the mutex on linux but + * releases resources on windows. */ + pthread_mutex_destroy(lock); } static inline void _rwlock_init(pthread_rwlock_t *lock, const char *file, const char *func, const int line) { if (unlikely(pthread_rwlock_init(lock, NULL))) quitfrom(1, file, func, line, "Failed to pthread_rwlock_init errno=%d", errno); + INITLOCK(lock, CGLOCK_RW, file, func, line); } -/* cgminer locks, a write biased variant of rwlocks */ -struct cglock { - pthread_mutex_t mutex; - pthread_rwlock_t rwlock; -}; +static inline void rwlock_destroy(pthread_rwlock_t *lock) +{ + pthread_rwlock_destroy(lock); +} -typedef struct cglock cglock_t; +static inline void _cglock_init(cglock_t *lock, const char *file, const char *func, const int line) +{ + _mutex_init(&lock->mutex, file, func, line); + _rwlock_init(&lock->rwlock, file, func, line); +} + +static inline void cglock_destroy(cglock_t *lock) +{ + rwlock_destroy(&lock->rwlock); + mutex_destroy(&lock->mutex); +} -static inline void cglock_init(cglock_t *lock) +/* Read lock variant of cglock. Cannot be promoted. */ +static inline void _cg_rlock(cglock_t *lock, const char *file, const char *func, const int line) { - mutex_init(&lock->mutex); - rwlock_init(&lock->rwlock); + _mutex_lock(&lock->mutex, file, func, line); + _rd_lock(&lock->rwlock, file, func, line); + _mutex_unlock_noyield(&lock->mutex, file, func, line); } -/* Read lock variant of cglock */ -static inline void cg_rlock(cglock_t *lock) +/* Intermediate variant of cglock - behaves as a read lock but can be promoted + * to a write lock or demoted to read lock. */ +static inline void _cg_ilock(cglock_t *lock, const char *file, const char *func, const int line) { - mutex_lock(&lock->mutex); - rd_lock(&lock->rwlock); - mutex_unlock_noyield(&lock->mutex); + _mutex_lock(&lock->mutex, file, func, line); } -/* Intermediate variant of cglock */ -static inline void cg_ilock(cglock_t *lock) +/* Unlock intermediate variant without changing to read or write version */ +static inline void _cg_uilock(cglock_t *lock, const char *file, const char *func, const int line) { - mutex_lock(&lock->mutex); + _mutex_unlock(&lock->mutex, file, func, line); } /* Upgrade intermediate variant to a write lock */ -static inline void cg_ulock(cglock_t *lock) +static inline void _cg_ulock(cglock_t *lock, const char *file, const char *func, const int line) { - wr_lock(&lock->rwlock); + _wr_lock(&lock->rwlock, file, func, line); } /* Write lock variant of cglock */ -static inline void cg_wlock(cglock_t *lock) +static inline void _cg_wlock(cglock_t *lock, const char *file, const char *func, const int line) { - mutex_lock(&lock->mutex); - wr_lock(&lock->rwlock); + _mutex_lock(&lock->mutex, file, func, line); + _wr_lock(&lock->rwlock, file, func, line); } /* Downgrade write variant to a read lock */ -static inline void cg_dwlock(cglock_t *lock) +static inline void _cg_dwlock(cglock_t *lock, const char *file, const char *func, const int line) +{ + _wr_unlock_noyield(&lock->rwlock, file, func, line); + _rd_lock(&lock->rwlock, file, func, line); + _mutex_unlock_noyield(&lock->mutex, file, func, line); +} + +/* Demote a write variant to an intermediate variant */ +static inline void _cg_dwilock(cglock_t *lock, const char *file, const char *func, const int line) { - wr_unlock_noyield(&lock->rwlock); - rd_lock(&lock->rwlock); - mutex_unlock_noyield(&lock->mutex); + _wr_unlock(&lock->rwlock, file, func, line); } /* Downgrade intermediate variant to a read lock */ -static inline void cg_dlock(cglock_t *lock) +static inline void _cg_dlock(cglock_t *lock, const char *file, const char *func, const int line) { - rd_lock(&lock->rwlock); - mutex_unlock_noyield(&lock->mutex); + _rd_lock(&lock->rwlock, file, func, line); + _mutex_unlock_noyield(&lock->mutex, file, func, line); } -static inline void cg_runlock(cglock_t *lock) +static inline void _cg_runlock(cglock_t *lock, const char *file, const char *func, const int line) { - rd_unlock(&lock->rwlock); + _rd_unlock(&lock->rwlock, file, func, line); } -static inline void cg_wunlock(cglock_t *lock) +/* This drops the read lock and grabs a write lock. It does NOT protect data + * between the two locks! */ +static inline void _cg_ruwlock(cglock_t *lock, const char *file, const char *func, const int line) { - wr_unlock(&lock->rwlock); - mutex_unlock(&lock->mutex); + _rd_unlock_noyield(&lock->rwlock, file, func, line); + _cg_wlock(lock, file, func, line); +} + +static inline void _cg_wunlock(cglock_t *lock, const char *file, const char *func, const int line) +{ + _wr_unlock_noyield(&lock->rwlock, file, func, line); + _mutex_unlock(&lock->mutex, file, func, line); } struct pool; +#define API_LISTEN_ADDR "0.0.0.0" #define API_MCAST_CODE "FTW" #define API_MCAST_ADDR "224.0.0.75" +extern bool g_logfile_enable; +extern char g_logfile_path[256]; +extern char g_logfile_openflag[32]; +extern FILE * g_logwork_file; +extern FILE * g_logwork_files[65]; +extern FILE * g_logwork_diffs[65]; +extern int g_logwork_asicnum; + +extern bool opt_work_update; extern bool opt_protocol; extern bool have_longpoll; extern char *opt_kernel_path; extern char *opt_socks_proxy; +extern int opt_suggest_diff; extern char *cgminer_path; extern bool opt_fail_only; +extern bool opt_lowmem; extern bool opt_autofan; extern bool opt_autoengine; extern bool use_curses; +extern char *opt_logwork_path; +extern char *opt_logwork_asicnum; +extern bool opt_logwork_diff; extern char *opt_api_allow; extern bool opt_api_mcast; extern char *opt_api_mcast_addr; @@ -888,15 +1025,77 @@ extern int opt_api_mcast_port; extern char *opt_api_groups; extern char *opt_api_description; extern int opt_api_port; +extern char *opt_api_host; extern bool opt_api_listen; extern bool opt_api_network; extern bool opt_delaynet; +extern time_t last_getwork; extern bool opt_restart; +#ifdef USE_ICARUS extern char *opt_icarus_options; extern char *opt_icarus_timing; +extern float opt_anu_freq; +extern float opt_au3_freq; +extern int opt_au3_volt; +extern float opt_rock_freq; +#endif extern bool opt_worktime; #ifdef USE_AVALON extern char *opt_avalon_options; +extern char *opt_bitburner_fury_options; +#endif +#ifdef USE_KLONDIKE +extern char *opt_klondike_options; +#endif +#ifdef USE_DRILLBIT +extern char *opt_drillbit_options; +extern char *opt_drillbit_auto; +#endif +#ifdef USE_BAB +extern char *opt_bab_options; +#endif +#ifdef USE_BITMINE_A1 +extern char *opt_bitmine_a1_options; +#endif +#ifdef USE_BITMAIN +extern char *opt_bitmain_options; +extern bool opt_bitmain_hwerror; +extern bool opt_bitmain_checkall; +extern char *opt_bitmain_freq; +extern char *opt_bitmain_voltage; +extern bool opt_bitmain_checkn2diff; +extern bool opt_bitmain_nobeeper; +extern bool opt_bitmain_notempoverctrl; +extern bool opt_bitmain_homemode; +#endif +#ifdef USE_BMSC +extern char *opt_bmsc_options; +extern char *opt_bmsc_timing; +extern bool opt_bmsc_gray; +extern char *opt_bmsc_bandops; +extern char *opt_bmsc_voltage; +extern bool opt_bmsc_bootstart; +extern char *opt_bmsc_freq; +extern char *opt_bmsc_rdreg; +extern bool opt_bmsc_rdworktest; +#endif +#ifdef USE_MINION +extern int opt_minion_chipreport; +extern char *opt_minion_cores; +extern bool opt_minion_extra; +extern char *opt_minion_freq; +extern int opt_minion_freqchange; +extern int opt_minion_freqpercent; +extern bool opt_minion_idlecount; +extern int opt_minion_ledcount; +extern int opt_minion_ledlimit; +extern bool opt_minion_noautofreq; +extern bool opt_minion_overheat; +extern int opt_minion_spidelay; +extern char *opt_minion_spireset; +extern int opt_minion_spisleep; +extern int opt_minion_spiusec; +extern char *opt_minion_temp; #endif #ifdef USE_USBUTILS extern char *opt_usb_select; @@ -909,14 +1108,22 @@ extern bool opt_bfl_noncerange; #endif extern int swork_id; +#if LOCK_TRACKING +extern pthread_mutex_t lockstat_lock; +#endif + extern pthread_rwlock_t netacc_lock; extern const uint32_t sha256_init_state[]; +#ifdef HAVE_LIBCURL +extern json_t *json_web_config(const char *url); extern json_t *json_rpc_call(CURL *curl, const char *url, const char *userpass, const char *rpc_req, bool, bool, int *, struct pool *pool, bool); -extern const char *proxytype(curl_proxytype proxytype); +#endif +extern const char *proxytype(proxytypes_t proxytype); extern char *get_proxy(char *url, struct pool *pool); +extern void __bin2hex(char *s, const unsigned char *p, size_t len); extern char *bin2hex(const unsigned char *p, size_t len); extern bool hex2bin(unsigned char *p, const char *hexstr, size_t len); @@ -934,12 +1141,6 @@ extern int opt_queue; extern int opt_scantime; extern int opt_expiry; -#ifdef USE_USBUTILS -extern pthread_mutex_t cgusb_lock; -extern pthread_mutex_t cgusbres_lock; -extern cglock_t cgusb_fd_lock; -#endif - extern cglock_t control_lock; extern pthread_mutex_t hash_lock; extern pthread_mutex_t console_lock; @@ -951,21 +1152,18 @@ extern pthread_mutex_t restart_lock; 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_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); +#endif extern int restart_wait(struct thr_info *thr, unsigned int mstime); extern void kill_work(void); extern void reinit_device(struct cgpu_info *cgpu); -#ifdef HAVE_ADL -extern bool gpu_stats(int gpu, float *temp, int *engineclock, int *memclock, float *vddc, int *activity, int *fanspeed, int *fanpercent, int *powertune); -extern int set_fanspeed(int gpu, int iFanSpeed); -extern int set_vddc(int gpu, float fVddc); -extern int set_engineclock(int gpu, int iEngineClock); -extern int set_memoryclock(int gpu, int iMemoryClock); -#endif - extern void api(int thr_id); extern struct pool *current_pool(void); @@ -973,34 +1171,15 @@ extern int enabled_pools; extern void get_intrange(char *arg, int *val1, int *val2); extern bool detect_stratum(struct pool *pool, char *url); extern void print_summary(void); +extern void adjust_quota_gcd(void); extern struct pool *add_pool(void); extern bool add_pool_details(struct pool *pool, bool live, char *url, char *user, char *pass); -#define MAX_GPUDEVICES 16 #define MAX_DEVICES 4096 -#define MIN_SHA_INTENSITY -10 -#define MIN_SHA_INTENSITY_STR "-10" -#define MAX_SHA_INTENSITY 14 -#define MAX_SHA_INTENSITY_STR "14" -#define MIN_SCRYPT_INTENSITY 8 -#define MIN_SCRYPT_INTENSITY_STR "8" -#define MAX_SCRYPT_INTENSITY 20 -#define MAX_SCRYPT_INTENSITY_STR "20" -#ifdef USE_SCRYPT -#define MIN_INTENSITY (opt_scrypt ? MIN_SCRYPT_INTENSITY : MIN_SHA_INTENSITY) -#define MIN_INTENSITY_STR (opt_scrypt ? MIN_SCRYPT_INTENSITY_STR : MIN_SHA_INTENSITY_STR) -#define MAX_INTENSITY (opt_scrypt ? MAX_SCRYPT_INTENSITY : MAX_SHA_INTENSITY) -#define MAX_INTENSITY_STR (opt_scrypt ? MAX_SCRYPT_INTENSITY_STR : MAX_SHA_INTENSITY_STR) -#define MAX_GPU_INTENSITY MAX_SCRYPT_INTENSITY -#else -#define MIN_INTENSITY MIN_SHA_INTENSITY -#define MIN_INTENSITY_STR MIN_SHA_INTENSITY_STR -#define MAX_INTENSITY MAX_SHA_INTENSITY -#define MAX_INTENSITY_STR MAX_SHA_INTENSITY_STR -#define MAX_GPU_INTENSITY MAX_SHA_INTENSITY -#endif - +extern char g_miner_version[256]; +extern char g_miner_compiletime[256]; +extern char g_miner_type[256]; extern bool hotplug_mode; extern int hotplug_time; extern struct list_head scan_devices; @@ -1011,13 +1190,6 @@ extern bool use_syslog; extern bool opt_quiet; extern struct thr_info *control_thr; extern struct thr_info **mining_thr; -extern struct cgpu_info gpus[MAX_GPUDEVICES]; -extern int gpu_threads; -#ifdef USE_SCRYPT -extern bool opt_scrypt; -#else -#define opt_scrypt (0) -#endif extern double total_secs; extern int mining_threads; extern int total_devices; @@ -1028,57 +1200,27 @@ extern struct pool **pools; extern struct strategies strategies[]; extern enum pool_strategy pool_strategy; extern int opt_rotate_period; +extern double rolling1, rolling5, rolling15; +extern double total_rolling; extern double total_mhashes_done; +extern double g_displayed_rolling; extern unsigned int new_blocks; extern unsigned int found_blocks; -extern int total_accepted, total_rejected, total_diff1;; -extern int total_getworks, total_stale, total_discarded; +extern int g_max_fan, g_max_temp; +extern int64_t total_accepted, total_rejected, total_diff1; +extern int64_t total_getworks, total_stale, total_discarded; extern double total_diff_accepted, total_diff_rejected, total_diff_stale; extern unsigned int local_work; extern unsigned int total_go, total_ro; extern const int opt_cutofftemp; extern int opt_log_interval; extern unsigned long long global_hashrate; -extern char *current_fullhash; +extern char current_hash[68]; extern double current_diff; extern uint64_t best_diff; extern struct timeval block_timeval; extern char *workpadding; -#ifdef HAVE_OPENCL -typedef struct { - cl_uint ctx_a; cl_uint ctx_b; cl_uint ctx_c; cl_uint ctx_d; - cl_uint ctx_e; cl_uint ctx_f; cl_uint ctx_g; cl_uint ctx_h; - cl_uint cty_a; cl_uint cty_b; cl_uint cty_c; cl_uint cty_d; - cl_uint cty_e; cl_uint cty_f; cl_uint cty_g; cl_uint cty_h; - cl_uint merkle; cl_uint ntime; cl_uint nbits; cl_uint nonce; - cl_uint fW0; cl_uint fW1; cl_uint fW2; cl_uint fW3; cl_uint fW15; - cl_uint fW01r; cl_uint fcty_e; cl_uint fcty_e2; - cl_uint W16; cl_uint W17; cl_uint W2; - cl_uint PreVal4; cl_uint T1; - cl_uint C1addK5; cl_uint D1A; cl_uint W2A; cl_uint W17_2; - cl_uint PreVal4addT1; cl_uint T1substate0; - cl_uint PreVal4_2; - cl_uint PreVal0; - cl_uint PreW18; - cl_uint PreW19; - cl_uint PreW31; - cl_uint PreW32; - - /* For diakgcn */ - cl_uint B1addK6, PreVal0addK7, W16addK16, W17addK17; - cl_uint zeroA, zeroB; - cl_uint oneA, twoA, threeA, fourA, fiveA, sixA, sevenA; -#ifdef USE_SCRYPT - struct work *work; -#endif -} dev_blk_ctx; -#else -typedef struct { - uint32_t nonce; -} dev_blk_ctx; -#endif - struct curl_ent { CURL *curl; struct list_head node; @@ -1095,16 +1237,9 @@ enum pool_enable { struct stratum_work { char *job_id; - char *prev_hash; unsigned char **merkle_bin; - char *bbversion; - char *nbit; - char *ntime; bool clean; - size_t cb_len; - size_t header_len; - int merkles; double diff; }; @@ -1114,12 +1249,16 @@ struct stratum_work { struct pool { int pool_no; int prio; - int accepted, rejected; + int64_t accepted, rejected; int seq_rejects; int seq_getfails; int solved; - int diff1; + int64_t diff1; char diff[8]; + int quota; + int quota_gcd; + int quota_used; + int works; double diff_accepted; double diff_rejected; @@ -1133,6 +1272,7 @@ struct pool { bool submit_old; bool removed; bool lp_started; + bool blocking; char *hdr_path; char *lp_url; @@ -1151,7 +1291,7 @@ struct pool { char *rpc_url; char *rpc_userpass; char *rpc_user, *rpc_pass; - curl_proxytype rpc_proxytype; + proxytypes_t rpc_proxytype; char *rpc_proxy; pthread_mutex_t pool_lock; @@ -1175,18 +1315,23 @@ struct pool { struct cgminer_stats cgminer_stats; struct cgminer_pool_stats cgminer_pool_stats; + /* The last block this particular pool knows about */ + char prev_block[32]; + /* Stratum variables */ char *stratum_url; + bool extranonce_subscribe; char *stratum_port; - struct addrinfo stratum_hints; SOCKETTYPE sock; char *sockbuf; size_t sockbuf_size; char *sockaddr_url; /* stripped url used for sockaddr */ + char *sockaddr_proxy_url; + char *sockaddr_proxy_port; + char *nonce1; unsigned char *nonce1bin; - size_t n1_len; - uint32_t nonce2; + uint64_t nonce2; int n2size; char *sessionid; bool has_stratum; @@ -1214,13 +1359,30 @@ struct pool { uint32_t gbt_bits; unsigned char *txn_hashes; int gbt_txns; - int coinbase_len; + int height; + + bool gbt_solo; + unsigned char merklebin[16 * 32]; + int transactions; + char *txn_data; + unsigned char scriptsig_base[100]; + unsigned char script_pubkey[25 + 3]; + int nValue; + CURL *gbt_curl; + bool gbt_curl_inuse; /* Shared by both stratum & GBT */ + size_t n1_len; unsigned char *coinbase; + int coinbase_len; int nonce2_offset; unsigned char header_bin[128]; - int merkle_offset; + int merkles; + char prev_hash[68]; + char bbversion[12]; + char nbit[12]; + char ntime[12]; + double sdiff; struct timeval tv_lastwork; }; @@ -1231,6 +1393,7 @@ struct pool { #define GETWORK_MODE_BENCHMARK 'B' #define GETWORK_MODE_STRATUM 'S' #define GETWORK_MODE_GBT 'G' +#define GETWORK_MODE_SOLO 'C' struct work { unsigned char data[128]; @@ -1238,15 +1401,14 @@ struct work { unsigned char target[32]; unsigned char hash[32]; -#ifdef USE_SCRYPT - unsigned char device_target[32]; -#endif + /* This is the diff the device is currently aiming for and must be + * the minimum of work_difficulty & drv->max_diff */ double device_diff; uint64_t share_diff; int rolls; - - dev_blk_ctx blk; + int drv_rolllimit; /* How much the driver can roll ntime */ + uint32_t nonce; /* For devices that hash sole work */ struct thr_info *thr; int thr_id; @@ -1261,11 +1423,10 @@ struct work { bool stale; bool mandatory; bool block; - bool queued; bool stratum; char *job_id; - uint32_t nonce2; + uint64_t nonce2; size_t nonce2_len; char *ntime; double sdiff; @@ -1276,15 +1437,19 @@ struct work { int gbt_txns; unsigned int work_block; - int id; + uint32_t id; UT_hash_handle hh; + /* This is the diff work we're aiming to submit and should match the + * work->target binary */ double work_difficulty; // Allow devices to identify work if multiple sub-devices int subid; // Allow devices to flag work for their own purposes bool devflag; + // Allow devices to timestamp work for their own purposes + struct timeval tv_stamp; struct timeval tv_getwork; struct timeval tv_getwork_reply; @@ -1334,15 +1499,40 @@ struct modminer_fpga_state { strcat(buf, tmp13); \ } while (0) +extern uint64_t share_ndiff(const struct work *work); extern void get_datestamp(char *, size_t, struct timeval *); extern void inc_hw_errors(struct thr_info *thr); +extern void inc_dev_status(int max_fan, int max_temp); +extern void inc_work_stats(struct thr_info *thr, struct pool *pool, int diff1); +extern bool test_nonce(struct work *work, uint32_t nonce); +extern bool test_nonce_diff(struct work *work, uint32_t nonce, double diff); +extern bool submit_tested_work(struct thr_info *thr, struct work *work); extern bool submit_nonce(struct thr_info *thr, struct work *work, uint32_t nonce); +extern bool submit_noffset_nonce(struct thr_info *thr, struct work *work, uint32_t nonce, + int noffset); +extern int share_work_tdiff(struct cgpu_info *cgpu); +extern bool submit_nonce_1(struct thr_info *thr, struct work *work, uint32_t nonce, int * nofull); +extern void submit_nonce_2(struct work *work); +extern bool submit_nonce_direct(struct thr_info *thr, struct work *work, uint32_t nonce); +extern bool submit_noffset_nonce(struct thr_info *thr, struct work *work, uint32_t nonce, int noffset); +extern struct work *get_work(struct thr_info *thr, const int thr_id); +extern void __add_queued(struct cgpu_info *cgpu, struct work *work); extern struct work *get_queued(struct cgpu_info *cgpu); +extern struct work *__get_queued(struct cgpu_info *cgpu); +extern void add_queued(struct cgpu_info *cgpu, struct work *work); +extern struct work *get_queue_work(struct thr_info *thr, struct cgpu_info *cgpu, int thr_id); extern struct work *__find_work_bymidstate(struct work *que, char *midstate, size_t midstatelen, char *data, int offset, size_t datalen); extern struct work *find_queued_work_bymidstate(struct cgpu_info *cgpu, char *midstate, size_t midstatelen, char *data, int offset, size_t datalen); extern struct work *clone_queued_work_bymidstate(struct cgpu_info *cgpu, char *midstate, size_t midstatelen, char *data, int offset, size_t datalen); +extern struct work *__find_work_byid(struct work *que, uint32_t id); +extern struct work *find_queued_work_byid(struct cgpu_info *cgpu, uint32_t id); +extern struct work *clone_queued_work_byid(struct cgpu_info *cgpu, uint32_t id); +extern void __work_completed(struct cgpu_info *cgpu, struct work *work); +extern int age_queued_work(struct cgpu_info *cgpu, double secs); extern void work_completed(struct cgpu_info *cgpu, struct work *work); extern struct work *take_queued_work_bymidstate(struct cgpu_info *cgpu, char *midstate, size_t midstatelen, char *data, int offset, size_t datalen); +extern void flush_queue(struct cgpu_info *cgpu); +extern void hash_driver_work(struct thr_info *mythr); extern void hash_queued_work(struct thr_info *mythr); extern void _wlog(const char *str); extern void _wlogprint(const char *str); @@ -1350,7 +1540,11 @@ extern int curses_int(const char *query); extern char *curses_input(const char *query); extern void kill_work(void); extern void switch_pools(struct pool *selected); -extern void discard_work(struct work *work); +extern void _discard_work(struct work *work); +#define discard_work(WORK) do { \ + _discard_work(WORK); \ + WORK = NULL; \ +} while (0) extern void remove_pool(struct pool *pool); extern void write_config(FILE *fcfg); extern void zero_bestshare(void); @@ -1360,6 +1554,8 @@ extern bool log_curses_only(int prio, const char *datetime, const char *str); extern void clear_logwin(void); extern void logwin_update(void); extern bool pool_tclear(struct pool *pool, bool *var); +extern void stratum_resumed(struct pool *pool); +extern void pool_died(struct pool *pool); extern struct thread_q *tq_new(void); extern void tq_free(struct thread_q *tq); extern bool tq_push(struct thread_q *tq, void *data); @@ -1369,10 +1565,18 @@ extern void tq_thaw(struct thread_q *tq); extern bool successful_connect; extern void adl(void); extern void app_restart(void); +extern void roll_work(struct work *work); +extern struct work *make_clone(struct work *work); extern void clean_work(struct work *work); -extern void free_work(struct work *work); -extern void __copy_work(struct work *work, struct work *base_work); -extern struct work *copy_work(struct work *base_work); +extern void _free_work(struct work *work); +#define free_work(WORK) do { \ + _free_work(WORK); \ + WORK = NULL; \ +} while (0) +extern void set_work_ntime(struct work *work, int ntime); +extern struct work *copy_work_noffset(struct work *base_work, int noffset); +#define copy_work(work_in) copy_work_noffset(work_in, 0) +extern uint64_t share_diff(const struct work *work); extern struct thr_info *get_thread(int thr_id); extern struct cgpu_info *get_devices(int id); @@ -1380,10 +1584,15 @@ enum api_data_type { API_ESCAPE, API_STRING, API_CONST, + API_UINT8, + API_INT16, + API_UINT16, API_INT, API_UINT, API_UINT32, + API_HEX32, API_UINT64, + API_INT64, API_DOUBLE, API_ELAPSED, API_BOOL, @@ -1397,7 +1606,8 @@ enum api_data_type { API_VOLTS, API_HS, API_DIFF, - API_PERCENT + API_PERCENT, + API_AVG }; struct api_data { @@ -1412,9 +1622,13 @@ struct api_data { extern struct api_data *api_add_escape(struct api_data *root, char *name, char *data, bool copy_data); extern struct api_data *api_add_string(struct api_data *root, char *name, char *data, bool copy_data); extern struct api_data *api_add_const(struct api_data *root, char *name, const char *data, bool copy_data); +extern struct api_data *api_add_uint8(struct api_data *root, char *name, uint8_t *data, bool copy_data); +extern struct api_data *api_add_int16(struct api_data *root, char *name, uint16_t *data, bool copy_data); +extern struct api_data *api_add_uint16(struct api_data *root, char *name, uint16_t *data, bool copy_data); extern struct api_data *api_add_int(struct api_data *root, char *name, int *data, bool copy_data); extern struct api_data *api_add_uint(struct api_data *root, char *name, unsigned int *data, bool copy_data); extern struct api_data *api_add_uint32(struct api_data *root, char *name, uint32_t *data, bool copy_data); +extern struct api_data *api_add_hex32(struct api_data *root, char *name, uint32_t *data, bool copy_data); extern struct api_data *api_add_uint64(struct api_data *root, char *name, uint64_t *data, bool copy_data); extern struct api_data *api_add_double(struct api_data *root, char *name, double *data, bool copy_data); extern struct api_data *api_add_elapsed(struct api_data *root, char *name, double *data, bool copy_data); @@ -1430,5 +1644,14 @@ extern struct api_data *api_add_volts(struct api_data *root, char *name, float * extern struct api_data *api_add_hs(struct api_data *root, char *name, double *data, bool copy_data); extern struct api_data *api_add_diff(struct api_data *root, char *name, double *data, bool copy_data); extern struct api_data *api_add_percent(struct api_data *root, char *name, double *data, bool copy_data); +extern struct api_data *api_add_avg(struct api_data *root, char *name, float *data, bool copy_data); + +extern void dupalloc(struct cgpu_info *cgpu, int timelimit); +extern void dupcounters(struct cgpu_info *cgpu, uint64_t *checked, uint64_t *dups); +extern bool isdupnonce(struct cgpu_info *cgpu, struct work *work, uint32_t nonce); +#if defined(USE_BITMAIN) || defined(USE_BMSC) +extern void rev(unsigned char *s, size_t l); +extern int check_asicnum(int asic_num, unsigned char nonce); +#endif #endif /* __MINER_H__ */ diff --git a/miner.php b/miner.php index fdb9e52f0a..6d4d210431 100644 --- a/miner.php +++ b/miner.php @@ -1,16 +1,17 @@ \n"; @@ -44,10 +45,20 @@ # N.B. also if $readonly is true, it will not display the fields $poolinputs = false; # +# Default port to use if any $rigs entries don't specify the port number +$rigport = 4028; +# # Set $rigs to an array of your cgminer rigs that are running -# format: 'IP:Port' or 'Host:Port' or 'Host:Port:Name' +# format: 'IP' or 'Host' or 'IP:Port' or 'Host:Port' or 'Host:Port:Name' $rigs = array('127.0.0.1:4028'); # +# Set $rignames to false, or one of 'ip' or 'ipx' +# this says what to use if $rigs doesn't have a 'name' +$rignames = false; +# +# Set $rigbuttons to false to display a link rather than a button +$rigbuttons = true; +# # Set $mcast to true to look for your rigs and ignore $rigs $mcast = false; # @@ -70,6 +81,9 @@ # to wait for replies to the Multicast message $mcasttimeout = 1.5; # +# Set $mcastretries to the number of times to retry the multicast +$mcastretries = 0; +# # Set $allowgen to true to allow customsummarypages to use 'gen' # false means ignore any 'gen' options $allowgen = false; @@ -112,71 +126,289 @@ $mobilepage = array( 'DATE' => null, 'RIGS' => null, - 'SUMMARY' => array('Elapsed', 'MHS av', 'Found Blocks=Blks', 'Accepted', 'Rejected=Rej', 'Utility'), - 'DEVS+NOTIFY' => array('DEVS.Name=Name', 'DEVS.ID=ID', 'DEVS.Status=Status', 'DEVS.Temperature=Temp', - 'DEVS.MHS av=MHS av', 'DEVS.Accepted=Accept', 'DEVS.Rejected=Rej', - 'DEVS.Utility=Utility', 'NOTIFY.Last Not Well=Not Well'), - 'POOL' => array('POOL', 'Status', 'Accepted', 'Rejected=Rej', 'Last Share Time')); + 'SUMMARY' => array('Elapsed', 'MHS av', 'MHS 5m', 'Found Blocks=Blks', + 'Difficulty Accepted=DiffA', + 'Difficulty Rejected=DiffR', + 'Hardware Errors=HW', + 'Work Utility=WU'), + 'DEVS+NOTIFY' => array('DEVS.Name=Name', 'DEVS.ID=ID', 'DEVS.Status=Status', + 'DEVS.Temperature=Temp', 'DEVS.MHS av=MHS av', + 'DEVS.MHS 5m=MHS 5m', 'DEVS.Difficulty Accepted=DiffA', + 'DEVS.Difficulty Rejected=DiffR', + 'DEVS.Work Utility=WU', + 'NOTIFY.Last Not Well=Not Well'), + 'POOL' => array('POOL', 'Status', 'Difficulty Accepted=DiffA', + 'Difficulty Rejected=DiffR', 'Last Share Time=LST')); $mobilesum = array( - 'SUMMARY' => array('MHS av', 'Found Blocks', 'Accepted', 'Rejected', 'Utility'), - 'DEVS+NOTIFY' => array('DEVS.MHS av', 'DEVS.Accepted', 'DEVS.Rejected', 'DEVS.Utility'), - 'POOL' => array('Accepted', 'Rejected')); + 'SUMMARY' => array('MHS av', 'MHS 5m', 'Found Blocks', 'Difficulty Accepted', + 'Difficulty Rejected', 'Hardware Errors', + 'Work Utility'), + 'DEVS+NOTIFY' => array('DEVS.MHS av', 'DEVS.Difficulty Accepted', + 'DEVS.Difficulty Rejected'), + 'POOL' => array('Difficulty Accepted', 'Difficulty Rejected')); # $statspage = array( 'DATE' => null, 'RIGS' => null, - 'SUMMARY' => array('Elapsed', 'MHS av', 'Found Blocks=Blks', - 'Accepted', 'Rejected=Rej', 'Utility', - 'Hardware Errors=HW Errs', 'Network Blocks=Net Blks', - 'Work Utility'), + 'SUMMARY' => array('Elapsed', 'MHS av', 'MHS 5m', 'Found Blocks=Blks', + 'Difficulty Accepted=DiffA', + 'Difficulty Rejected=DiffR', + 'Work Utility=WU', 'Hardware Errors=HW Errs', + 'Network Blocks=Net Blks'), 'COIN' => array('*'), 'STATS' => array('*')); # $statssum = array( - 'SUMMARY' => array('MHS av', 'Found Blocks', 'Accepted', - 'Rejected', 'Utility', 'Hardware Errors', - 'Work Utility')); + 'SUMMARY' => array('MHS av', 'MHS 5m', 'Found Blocks', + 'Difficulty Accepted', 'Difficulty Rejected', + 'Work Utility', 'Hardware Errors')); # $poolspage = array( 'DATE' => null, 'RIGS' => null, - 'SUMMARY' => array('Elapsed', 'MHS av', 'Found Blocks=Blks', 'Accepted', 'Rejected=Rej', - 'Utility', 'Hardware Errors=HW Errs', 'Network Blocks=Net Blks', - 'Work Utility'), - 'POOL+STATS' => array('STATS.ID=ID', 'POOL.URL=URL', 'POOL.Difficulty Accepted=Diff Acc', - 'POOL.Difficulty Rejected=Diff Rej', - 'POOL.Has Stratum=Stratum', 'POOL.Stratum Active=StrAct', + 'SUMMARY' => array('Elapsed', 'MHS av', 'MHS 5m', 'Found Blocks=Blks', + 'Difficulty Accepted=DiffA', + 'Difficulty Rejected=DiffR', + 'Work Utility', 'Hardware Errors=HW', + 'Network Blocks=Net Blks', 'Best Share'), + 'POOL+STATS' => array('STATS.ID=ID', 'POOL.URL=URL', + 'POOL.Difficulty Accepted=DiffA', + 'POOL.Difficulty Rejected=DiffR', + 'POOL.Has Stratum=Stratum', + 'POOL.Stratum Active=StrAct', 'POOL.Has GBT=GBT', 'STATS.Times Sent=TSent', 'STATS.Bytes Sent=BSent', 'STATS.Net Bytes Sent=NSent', 'STATS.Times Recv=TRecv', 'STATS.Bytes Recv=BRecv', 'STATS.Net Bytes Recv=NRecv', 'GEN.AvShr=AvShr')); # $poolssum = array( - 'SUMMARY' => array('MHS av', 'Found Blocks', 'Accepted', - 'Rejected', 'Utility', 'Hardware Errors', - 'Work Utility'), + 'SUMMARY' => array('MHS av', 'MHS 5m', 'Found Blocks', + 'Difficulty Accepted', 'Difficulty Rejected', + 'Work Utility', 'Hardware Errors'), 'POOL+STATS' => array('POOL.Difficulty Accepted', 'POOL.Difficulty Rejected', - 'STATS.Times Sent', 'STATS.Bytes Sent', 'STATS.Net Bytes Sent', - 'STATS.Times Recv', 'STATS.Bytes Recv', 'STATS.Net Bytes Recv')); + 'STATS.Times Sent', 'STATS.Bytes Sent', + 'STATS.Net Bytes Sent', 'STATS.Times Recv', + 'STATS.Bytes Recv', 'STATS.Net Bytes Recv')); # $poolsext = array( 'POOL+STATS' => array( 'where' => null, - 'group' => array('POOL.URL', 'POOL.Has Stratum', 'POOL.Stratum Active', 'POOL.Has GBT'), - 'calc' => array('POOL.Difficulty Accepted' => 'sum', 'POOL.Difficulty Rejected' => 'sum', - 'STATS.Times Sent' => 'sum', 'STATS.Bytes Sent' => 'sum', - 'STATS.Net Bytes Sent' => 'sum', 'STATS.Times Recv' => 'sum', - 'STATS.Bytes Recv' => 'sum', 'STATS.Net Bytes Recv' => 'sum', + 'group' => array('POOL.URL', 'POOL.Has Stratum', + 'POOL.Stratum Active', 'POOL.Has GBT'), + 'calc' => array('POOL.Difficulty Accepted' => 'sum', + 'POOL.Difficulty Rejected' => 'sum', + 'STATS.Times Sent' => 'sum', + 'STATS.Bytes Sent' => 'sum', + 'STATS.Net Bytes Sent' => 'sum', + 'STATS.Times Recv' => 'sum', + 'STATS.Bytes Recv' => 'sum', + 'STATS.Net Bytes Recv' => 'sum', 'POOL.Accepted' => 'sum'), - 'gen' => array('AvShr' => 'round(POOL.Difficulty Accepted/max(POOL.Accepted,1)*100)/100'), + 'gen' => array('AvShr' => + 'round(POOL.Difficulty Accepted/'. + 'max(POOL.Accepted,1)*100)/100'), 'having' => array(array('STATS.Bytes Recv', '>', 0))) ); - # -# customsummarypages is an array of these Custom Summary Pages -$customsummarypages = array('Mobile' => array($mobilepage, $mobilesum), - 'Stats' => array($statspage, $statssum), - 'Pools' => array($poolspage, $poolssum, $poolsext)); +$devnotpage = array( + 'DATE' => null, + 'RIGS' => null, + 'DEVS+NOTIFY' => array('DEVS.Name=Name', 'DEVS.ID=ID', + 'DEVS.Temperature=Temp', 'DEVS.MHS av=MHS av', + 'DEVS.Difficulty Accepted=DiffA', + 'DEVS.Difficulty Rejected=DiffR', + 'NOTIFY.Last Not Well=Last Not Well')); +$devnotsum = array( + 'DEVS+NOTIFY' => array('DEVS.MHS av', 'DEVS.Difficulty Accepted', + 'DEVS.Difficulty Rejected')); +# +$devdetpage = array( + 'DATE' => null, + 'RIGS' => null, + 'DEVS+DEVDETAILS' => array('DEVS.Name=Name', 'DEVS.ID=ID', + 'DEVS.Temperature=Temp', + 'DEVS.MHS av=MHS av', + 'DEVS.Difficulty Accepted=DiffA', + 'DEVS.Difficulty Rejected=DiffR', + 'DEVDETAILS.Device Path=Device')); +$devdetsum = array( + 'DEVS+DEVDETAILS' => array('DEVS.MHS av', 'DEVS.Difficulty Accepted', + 'DEVS.Difficulty Rejected')); +# +$protopage = array( + 'DATE' => null, + 'RIGS' => null, + 'CONFIG' => array('ASC Count=ASCs', 'PGA Count=PGAs', 'Pool Count=Pools', + 'Strategy', 'Device Code', 'OS', 'Failover-Only'), + 'SUMMARY' => array('Elapsed', 'MHS av', 'Found Blocks=Blks', + 'Difficulty Accepted=Diff Acc', + 'Difficulty Rejected=Diff Rej', + 'Hardware Errors=HW Errs', + 'Network Blocks=Net Blks', 'Utility', 'Work Utility'), + 'POOL+STATS' => array('STATS.ID=ID', 'POOL.URL=URL', 'POOL.Accepted=Acc', + 'POOL.Difficulty Accepted=DiffA', + 'POOL.Difficulty Rejected=DiffR', 'POOL.Has GBT=GBT', + 'STATS.Max Diff=Max Work Diff', + 'STATS.Times Sent=#Sent', 'STATS.Bytes Sent=Byte Sent', + 'STATS.Net Bytes Sent=Net Sent', + 'STATS.Times Recv=#Recv', + 'STATS.Bytes Recv=Byte Recv', + 'STATS.Net Bytes Recv=Net Recv')); +$protosum = array( + 'SUMMARY' => array('MHS av', 'Found Blocks', 'Difficulty Accepted', + 'Difficulty Rejected', 'Hardware Errors', + 'Utility', 'Work Utility'), + 'POOL+STATS' => array('POOL.Accepted', 'POOL.Difficulty Accepted', + 'POOL.Difficulty Rejected', + 'STATS.Times Sent', 'STATS.Bytes Sent', + 'STATS.Net Bytes Sent', 'STATS.Times Recv', + 'STATS.Bytes Recv', 'STATS.Net Bytes Recv')); +$protoext = array( + 'POOL+STATS' => array( + 'where' => null, + 'group' => array('POOL.URL', 'POOL.Has GBT'), + 'calc' => array('POOL.Accepted' => 'sum', + 'POOL.Difficulty Accepted' => 'sum', + 'POOL.Difficulty Rejected' => 'sum', + 'STATS.Max Diff' => 'max', + 'STATS.Times Sent' => 'sum', + 'STATS.Bytes Sent' => 'sum', + 'STATS.Net Bytes Sent' => 'sum', + 'STATS.Times Recv' => 'sum', + 'STATS.Bytes Recv' => 'sum', + 'STATS.Net Bytes Recv' => 'sum'), + 'having' => array(array('STATS.Bytes Recv', '>', 0))) +); +# +# If 'gen' isn't enabled, the 'GEN' fields won't show but +# where present, will be replaced with the ||SUMMARY fields +$kanogenpage = array( + 'DATE' => null, + 'RIGS' => null, + 'SUMMARY+COIN' => array('SUMMARY.Elapsed=Elapsed', + 'GEN.Mined=Block%', 'GEN.GHS Acc=GH/s Acc', + 'GEN.GHS av=GH/s av||SUMMARY.MHS av=MHS av', + 'GEN.GHS 5m=GH/s 5m||SUMMARY.MHS 5m=MHS 5m', + 'GEN.GHS WU=GH/s WU||SUMMARY.Work Utility=WU', + 'SUMMARY.Found Blocks=Blks', + 'SUMMARY.Difficulty Accepted=DiffA', + 'SUMMARY.Difficulty Rejected=DiffR', + 'SUMMARY.Hardware Errors=HW', + 'SUMMARY.Difficulty Stale=DiffS', + 'SUMMARY.Best Share=Best Share', + 'SUMMARY.Device Hardware%=Dev HW%', + 'SUMMARY.Device Rejected%=Dev Rej%', + 'SUMMARY.Pool Rejected%=Pool Rej%', + 'SUMMARY.Pool Stale%=Pool Stale%'), + 'POOL' => array('URL', 'Diff1 Shares=Diff Work', + 'Difficulty Accepted=DiffA', + 'Difficulty Rejected=DiffR', + 'Difficulty Stale=DiffS', + 'Best Share', 'GEN.Acc=Pool Acc%', 'GEN.Rej=Pool Rej%') +); +# sum should list all fields seperately including GEN/BGEN || replacements +$kanogensum = array( + 'SUMMARY+COIN' => array('GEN.Mined', 'GEN.GHS Acc', 'GEN.GHS av', + 'GEN.GHS 5m', 'GEN.GHS WU', + 'SUMMARY.MHS av', 'SUMMARY.MHS 5m', + 'SUMMARY.Work Utility', + 'SUMMARY.Found Blocks', + 'SUMMARY.Difficulty Accepted', + 'SUMMARY.Difficulty Rejected', + 'SUMMARY.Hardware Errors', + 'SUMMARY.Difficulty Stale'), + 'POOL' => array('Diff1 Shares', 'Difficulty Accepted', + 'Difficulty Rejected', 'Difficulty Stale') +); +# 'where', 'calc' and 'having' should list GEN/BGEN || replacements seperately +# 'group' must use the 'name1||name2' format for GEN/BGEN fields +$kanogenext = array( + 'SUMMARY+COIN' => array( + 'gen' => array('GHS Acc' => + 'round(pow(2,32) * SUMMARY.Difficulty Accepted / '. + 'SUMMARY.Elapsed / 10000000) / 100', + 'Mined' => + 'SUMMARY.Elapsed * SUMMARY.Work Utility / 60 / '. + 'COIN.Network Difficulty', + 'GHS av' => + 'SUMMARY.MHS av / 1000.0', + 'GHS 5m' => + 'SUMMARY.MHS 5m / 1000.0', + 'GHS WU' => + 'round(pow(2,32) * SUMMARY.Work Utility / 60 / '. + '10000000 ) / 100')), + 'POOL' => array( + 'group' => array('URL'), + 'calc' => array('Diff1 Shares' => 'sum', 'Difficulty Accepted' => 'sum', + 'Difficulty Rejected' => 'sum', + 'Difficulty Stale' => 'sum', 'Best Share' => 'max'), + 'gen' => array('Rej' => 'Difficulty Rejected / '. + 'max(1,Difficulty Accepted+Difficulty Rejected)', + 'Acc' => 'Difficulty Accepted / '. + 'max(1,Difficulty Accepted+Difficulty Rejected)')) +); +# +$syspage = array( + 'DATE' => null, + 'RIGS' => null, + 'SUMMARY' => array('#', 'Elapsed', 'MHS av', 'MHS 5m', 'Found Blocks=Blks', + 'Difficulty Accepted=DiffA', + 'Difficulty Rejected=DiffR', + 'Difficulty Stale=DiffS', 'Hardware Errors=HW', + 'Work Utility', 'Network Blocks=Net Blks', 'Total MH', + 'Best Share', 'Device Hardware%=Dev HW%', + 'Device Rejected%=Dev Rej%', + 'Pool Rejected%=Pool Rej%', 'Pool Stale%', + 'Last getwork'), + 'DEVS' => array('#', 'ID', 'Name', 'ASC', 'Device Elapsed', 'Enabled', + 'Status', 'No Device', 'Temperature=Temp', + 'MHS av', 'MHS 5s', 'MHS 5m', 'Diff1 Work', + 'Difficulty Accepted=DiffA', + 'Difficulty Rejected=DiffR', + 'Hardware Errors=HW', 'Work Utility', + 'Last Valid Work', 'Last Share Pool', + 'Last Share Time', 'Total MH', + 'Device Hardware%=Dev HW%', + 'Device Rejected%=Dev Rej%'), + 'POOL' => array('POOL', 'URL', 'Status', 'Priority', 'Quota', + 'Getworks', 'Diff1 Shares', + 'Difficulty Accepted=DiffA', + 'Difficulty Rejected=DiffR', + 'Difficulty Stale=DiffS', + 'Last Share Difficulty', + 'Last Share Time', + 'Best Share', 'Pool Rejected%=Pool Rej%', + 'Pool Stale%') +); +$syssum = array( + 'SUMMARY' => array('MHS av', 'MHS 5m', 'Found Blocks', + 'Difficulty Accepted', 'Difficulty Rejected', + 'Difficulty Stale', 'Hardware Errors', + 'Work Utility', 'Total MH'), + 'DEVS' => array('MHS av', 'MHS 5s', 'MHS 5m', 'Diff1 Work', + 'Difficulty Accepted', 'Difficulty Rejected', + 'Hardware Errors', 'Total MH'), + 'POOL' => array('Getworks', 'Diff1 Shares', 'Difficulty Accepted', + 'Difficulty Rejected', 'Difficulty Stale') +); +# +# $customsummarypages is an array of these Custom Summary Pages +# that you can override in myminer.php +# It can be 'Name' => 1 with 'Name' in any of $user_pages or $sys_pages +# and it can be a fully defined 'Name' => array(...) like in $sys_pages below +$customsummarypages = array( + 'Kano' => 1, + 'Mobile' => 1, + 'Stats' => 1, + 'Pools' => 1 +); +# +# $user_pages are the myminer.php definable version of $sys_pages +# It should contain a set of 'Name' => array(...) like in $sys_pages +# that $customsummarypages can refer to by 'Name' +# If a 'Name' is in both $user_pages and $sys_pages, then the one +# in $user_pages will override the one in $sys_pages +$user_pages = array(); # $here = $_SERVER['PHP_SELF']; # @@ -194,6 +426,12 @@ $bad_font_family = '"Times New Roman", Times, serif'; $bad_font_size = '18pt'; # +# List of css names to add to the css style object +# e.g. array('td.cool' => false); +# true/false to not include the default $miner_font +# The css name/value pairs must be defined in $colouroverride below +$add_css_names = array(); +# # Edit this or redefine it in myminer.php to change the colour scheme # See $colourtable below for the list of names $colouroverride = array(); @@ -240,18 +478,31 @@ 'td.lo background' => '#deffff' ); # +# A list of system default summary pages (defined further above) +# that you can use by 'Name' in $customsummarypages +global $sys_pages; +$sys_pages = array( + 'Mobile' => array($mobilepage, $mobilesum), + 'Stats' => array($statspage, $statssum), + 'Pools' => array($poolspage, $poolssum, $poolsext), + 'DevNot' => array($devnotpage, $devnotsum), + 'DevDet' => array($devdetpage, $devdetsum), + 'Proto' => array($protopage, $protosum, $protoext), + 'Kano' => array($kanogenpage, $kanogensum, $kanogenext), + 'Summary' => array($syspage, $syssum) +); +# # Don't touch these 2 $miner = null; $port = null; # +global $rigips; +$rigips = array(); +# # Ensure it is only ever shown once global $showndate; $showndate = false; # -# For summary page to stop retrying failed rigs -global $rigerror; -$rigerror = array(); -# global $rownum; $rownum = 0; # @@ -259,6 +510,91 @@ global $ses; $ses = 'rutroh'; # +function getcsp($name, $systempage = false) +{ + global $customsummarypages, $user_pages, $sys_pages; + + if ($systempage === false) + { + if (!isset($customsummarypages[$name])) + return false; + + $csp = $customsummarypages[$name]; + if (is_array($csp)) + { + if (count($csp) < 2 || count($csp) > 3) + return false; + else + return $csp; + } + } + + if (isset($user_pages[$name])) + { + $csp = $user_pages[$name]; + if (!is_array($csp) || count($csp) < 2 || count($csp) > 3) + return false; + else + return $csp; + } + + if (isset($sys_pages[$name])) + { + $csp = $sys_pages[$name]; + if (!is_array($csp) || count($csp) < 2 || count($csp) > 3) + return false; + else + return $csp; + } + + return false; +} +# +function degenfields(&$sec, $name, $fields) +{ + global $allowgen; + + if (!is_array($fields)) + return; + + foreach ($fields as $num => $fld) + if (substr($fld, 0, 5) == 'BGEN.' || substr($fld, 0, 4) == 'GEN.') + { + $opts = explode('||', $fld, 2); + if ($allowgen) + { + if (count($opts) > 1) + $sec[$name][$num] = $opts[0]; + } + else + { + if (count($opts) > 1) + $sec[$name][$num] = $opts[1]; + else + unset($sec[$name][$num]); + } + } +} +# +# Allow BGEN/GEN fields to have a '||' replacement when gen is disabled +# N.B. if gen is disabled and all page fields are GBEN/GEN without '||' then +# the table will disappear +# Replacements can be in the page fields and then also the ext group fields +# All other $csp sections should list both separately +function degen(&$csp) +{ + $page = 0; + if (isset($csp[$page]) && is_array($csp[$page])) + foreach ($csp[$page] as $sec => $fields) + degenfields($csp[$page], $sec, $fields); + + $ext = 2; + if (isset($csp[$ext]) && is_array($csp[$ext])) + foreach ($csp[$ext] as $sec => $types) + if (is_array($types) && isset($types['group'])) + degenfields($types, 'group', $types['group']); +} +# function getcss($cssname, $dom = false) { global $colourtable, $colouroverride; @@ -286,10 +622,18 @@ function getdom($domname) return getcss($domname, true); } # +# N.B. don't call this before calling htmlhead() +function php_pr($cmd) +{ + global $here, $autorefresh; + + return "$here?ref=$autorefresh$cmd"; +} +# function htmlhead($mcerr, $checkapi, $rig, $pg = null, $noscript = false) { global $doctype, $title, $miner_font_family, $miner_font_size; - global $bad_font_family, $bad_font_size; + global $bad_font_family, $bad_font_size, $add_css_names; global $error, $readonly, $poolinputs, $here; global $ignorerefresh, $autorefresh; @@ -333,8 +677,16 @@ function htmlhead($mcerr, $checkapi, $rig, $pg = null, $noscript = false) td.tot { $miner_font ".getcss('td.tot')."} td.lst { $miner_font ".getcss('td.lst')."} td.hi { $miner_font ".getcss('td.hi')."} -td.lo { $miner_font ".getcss('td.lo')."} - +td.lo { $miner_font ".getcss('td.lo')."}\n"; + if (isset($add_css_names)) + foreach ($add_css_names as $css_name => $no_miner_font) + { + echo "$css_name { "; + if ($no_miner_font !== true) + echo "$miner_font "; + echo getcss("$css_name")."}\n"; + } + echo " \n"; if ($noscript === false) { @@ -373,10 +725,10 @@ function minhead($mcerr = '') $haderror = false; $error = null; # -function getrigs() +function mcastrigs() { - global $rigs, $mcastaddr, $mcastport, $mcastcode; - global $mcastlistport, $mcasttimeout, $error; + global $rigs, $mcastexpect, $mcastaddr, $mcastport, $mcastcode; + global $mcastlistport, $mcasttimeout, $mcastretries, $error; $listname = "0.0.0.0"; @@ -414,61 +766,86 @@ function getrigs() return; } - $mcast_soc = socket_create(AF_INET, SOCK_DGRAM, SOL_UDP); - if ($mcast_soc === false || $mcast_soc == null) + $retries = $mcastretries; + $doretry = ($retries > 0); + do { - $msg = "ERR: mcast send socket create(UDP) failed"; - if ($rigipsecurity === false) + $mcast_soc = socket_create(AF_INET, SOCK_DGRAM, SOL_UDP); + if ($mcast_soc === false || $mcast_soc == null) { - $error = socket_strerror(socket_last_error()); - $error = "$msg '$error'\n"; - } - else - $error = "$msg\n"; + $msg = "ERR: mcast send socket create(UDP) failed"; + if ($rigipsecurity === false) + { + $error = socket_strerror(socket_last_error()); + $error = "$msg '$error'\n"; + } + else + $error = "$msg\n"; - socket_close($rep_soc); - return; - } + socket_close($rep_soc); + return; + } - $buf = "cgminer-$mcastcode-$mcastlistport"; - socket_sendto($mcast_soc, $buf, strlen($buf), 0, $mcastaddr, $mcastport); - socket_close($mcast_soc); + $buf = "cgminer-$mcastcode-$mcastlistport"; + socket_sendto($mcast_soc, $buf, strlen($buf), 0, $mcastaddr, $mcastport); + socket_close($mcast_soc); - $stt = microtime(true); - while (true) - { - $got = @socket_recvfrom($rep_soc, $buf, 32, MSG_DONTWAIT, $ip, $p); - if ($got !== false && $got > 0) + $stt = microtime(true); + while (true) { - $ans = explode('-', $buf, 4); - if (count($ans) >= 3 && $ans[0] == 'cgm' && $ans[1] == 'FTW') + $got = @socket_recvfrom($rep_soc, $buf, 32, MSG_DONTWAIT, $ip, $p); + if ($got !== false && $got > 0) { - $rp = intval($ans[2]); + $ans = explode('-', $buf, 4); + if (count($ans) >= 3 && $ans[0] == 'cgm' && $ans[1] == 'FTW') + { + $rp = intval($ans[2]); - if (count($ans) > 3) - $mdes = str_replace("\0", '', $ans[3]); - else - $mdes = ''; + if (count($ans) > 3) + $mdes = str_replace("\0", '', $ans[3]); + else + $mdes = ''; - if (strlen($mdes) > 0) - $rigs[] = "$ip:$rp:$mdes"; - else - $rigs[] = "$ip:$rp"; + if (strlen($mdes) > 0) + $rig = "$ip:$rp:$mdes"; + else + $rig = "$ip:$rp"; + + if (!in_array($rig, $rigs)) + $rigs[] = $rig; + } } + if ((microtime(true) - $stt) >= $mcasttimeout) + break; + + usleep(100000); } - if ((microtime(true) - $stt) >= $mcasttimeout) - break; - usleep(100000); - } + if ($mcastexpect > 0 && count($rigs) >= $mcastexpect) + $doretry = false; + + } while ($doretry && --$retries > 0); + socket_close($rep_soc); } # +function getrigs() +{ + global $rigs; + + mcastrigs(); + + sort($rigs); +} +# function getsock($rig, $addr, $port) { - global $rigipsecurity; + global $rigport, $rigips, $rignames, $rigipsecurity; global $haderror, $error, $socksndtimeoutsec, $sockrcvtimeoutsec; + $port = trim($port); + if (strlen($port) == 0) + $port = $rigport; $error = null; $socket = null; $socket = socket_create(AF_INET, SOCK_STREAM, SOL_TCP); @@ -509,6 +886,9 @@ function getsock($rig, $addr, $port) socket_close($socket); return null; } + if ($rignames !== false && !isset($rigips[$addr])) + if (socket_getpeername($socket, $ip) == true) + $rigips[$addr] = $ip; return $socket; } # @@ -743,77 +1123,105 @@ function endzero($num) return $rep; } # -function fmt($section, $name, $value, $when, $alldata) +function fmt($section, $name, $value, $when, $alldata, $cf = NULL) { global $dfmt, $rownum; if ($alldata == null) $alldata = array(); - $errorclass = ' class=err'; - $warnclass = ' class=warn'; - $lstclass = ' class=lst'; - $hiclass = ' class=hi'; - $loclass = ' class=lo'; - $c2class = ' class=two'; - $totclass = ' class=tot'; + $errorclass = 'err'; + $warnclass = 'warn'; + $lstclass = 'lst'; + $hiclass = 'hi'; + $loclass = 'lo'; + $c2class = 'two'; + $totclass = 'tot'; $b = ' '; - $ret = $value; $class = ''; $nams = explode('.', $name); if (count($nams) > 1) $name = $nams[count($nams)-1]; + $done = false; if ($value === null) + { $ret = $b; + $done = true; + } else - switch ($section.'.'.$name) + if ($cf != NULL and function_exists($cf)) { - case 'GPU.Last Share Time': - case 'PGA.Last Share Time': - case 'ASC.Last Share Time': - case 'DEVS.Last Share Time': - if ($value == 0 - || (isset($alldata['Last Share Pool']) && $alldata['Last Share Pool'] == -1)) + list($ret, $class) = $cf($section, $name, $value, $when, $alldata, + $warnclass, $errorclass, $hiclass, $loclass, $totclass); + if ($ret !== '') + $done = true; + } + + if ($done === false) + { + $ret = $value; + /* + * To speed up the PHP, the case statement is just $name + * It used to be $section.'.'.$name + * If any names clash, the case code will need to check the value of + * $section to resolve the clash - as with 'Last Share Time' below + * If the code picks up a field that you wish to format differently, + * then you'll need a customsummarypage 'fmt' extension + */ + switch ($name) + { + case '0': + break; + case 'Last Share Time': + if ($section == 'total') + break; + if ($section == 'POOL') { - $ret = 'Never'; - $class = $warnclass; + if ($value == 0) + $ret = 'Never'; + else + $ret = date('H:i:s d-M', $value); } else { - $ret = date('H:i:s', $value); - $class = classlastshare($when, $alldata, $warnclass, $errorclass); + if ($value == 0 + || (isset($alldata['Last Share Pool']) && $alldata['Last Share Pool'] == -1)) + { + $ret = 'Never'; + $class = $warnclass; + } + else + { + $ret = date('H:i:s', $value); + $class = classlastshare($when, $alldata, $warnclass, $errorclass); + } } break; - case 'GPU.Last Valid Work': - case 'PGA.Last Valid Work': - case 'ASC.Last Valid Work': - case 'DEVS.Last Valid Work': + case 'Last getwork': + case 'Last Valid Work': + if ($section == 'total') + break; if ($value == 0) $ret = 'Never'; else $ret = ($value - $when) . 's'; break; - case 'POOL.Last Share Time': - if ($value == 0) - $ret = 'Never'; - else - $ret = date('H:i:s d-M', $value); - break; - case 'GPU.Last Share Pool': - case 'PGA.Last Share Pool': - case 'ASC.Last Share Pool': - case 'DEVS.Last Share Pool': + case 'Last Share Pool': + if ($section == 'total') + break; if ($value == -1) { $ret = 'None'; $class = $warnclass; } break; - case 'SUMMARY.Elapsed': - case 'STATS.Elapsed': + case 'Elapsed': + case 'Device Elapsed': + if ($section == 'total') + break; $s = $value % 60; $value -= $s; $value /= 60; @@ -845,7 +1253,9 @@ function fmt($section, $name, $value, $when, $alldata) } } break; - case 'NOTIFY.Last Well': + case 'Last Well': + if ($section == 'total') + break; if ($value == '0') { $ret = 'Never'; @@ -854,7 +1264,9 @@ function fmt($section, $name, $value, $when, $alldata) else $ret = date('H:i:s', $value); break; - case 'NOTIFY.Last Not Well': + case 'Last Not Well': + if ($section == 'total') + break; if ($value == '0') $ret = 'Never'; else @@ -863,17 +1275,14 @@ function fmt($section, $name, $value, $when, $alldata) $class = $errorclass; } break; - case 'NOTIFY.Reason Not Well': + case 'Reason Not Well': + if ($section == 'total') + break; if ($value != 'None') $class = $errorclass; break; - case 'GPU.Utility': - case 'PGA.Utility': - case 'ASC.Utility': - case 'DEVS.Utility': - case 'SUMMARY.Utility': - case 'total.Utility': - $ret = $value.'/m'; + case 'Utility': + $ret = number_format($value, 2).'/m'; if ($value == 0) $class = $errorclass; else @@ -899,14 +1308,12 @@ function fmt($section, $name, $value, $when, $alldata) $class = $hiclass; } break; - case 'SUMMARY.Work Utility': - case 'total.Work Utility': - $ret = $value.'/m'; + case 'Work Utility': + $ret = number_format($value, 2).'/m'; break; - case 'GPU.Temperature': - case 'PGA.Temperature': - case 'ASC.Temperature': - case 'DEVS.Temperature': + case 'Temperature': + if ($section == 'total') + break; $ret = $value.'°C'; if (!isset($alldata['GPU'])) { @@ -914,19 +1321,18 @@ function fmt($section, $name, $value, $when, $alldata) $ret = ' '; break; } - case 'GPU.GPU Clock': - case 'DEVS.GPU Clock': - case 'GPU.Memory Clock': - case 'DEVS.Memory Clock': - case 'GPU.GPU Voltage': - case 'DEVS.GPU Voltage': - case 'GPU.GPU Activity': - case 'DEVS.GPU Activity': + case 'GPU Clock': + case 'Memory Clock': + case 'GPU Voltage': + case 'GPU Activity': + if ($section == 'total') + break; if ($value == 0) $class = $warnclass; break; - case 'GPU.Fan Percent': - case 'DEVS.Fan Percent': + case 'Fan Percent': + if ($section == 'total') + break; if ($value == 0) $class = $warnclass; else @@ -938,8 +1344,9 @@ function fmt($section, $name, $value, $when, $alldata) $class = $warnclass; } break; - case 'GPU.Fan Speed': - case 'DEVS.Fan Speed': + case 'Fan Speed': + if ($section == 'total') + break; if ($value == 0) $class = $warnclass; else @@ -953,12 +1360,11 @@ function fmt($section, $name, $value, $when, $alldata) $class = $warnclass; } break; - case 'GPU.MHS av': - case 'PGA.MHS av': - case 'ASC.MHS av': - case 'DEVS.MHS av': - case 'SUMMARY.MHS av': - case 'total.MHS av': + case 'MHS av': + case 'MHS 5s': + case 'MHS 1m': + case 'MHS 5m': + case 'MHS 15m': $parts = explode('.', $value, 2); if (count($parts) == 1) $dec = ''; @@ -991,52 +1397,21 @@ function fmt($section, $name, $value, $when, $alldata) $class = $loclass; } break; - case 'GPU.Total MH': - case 'PGA.Total MH': - case 'ASC.Total MH': - case 'DEVS.Total MH': - case 'SUMMARY.Total MH': - case 'total.Total MH': - case 'SUMMARY.Getworks': - case 'POOL.Getworks': - case 'total.Getworks': - case 'GPU.Accepted': - case 'PGA.Accepted': - case 'ASC.Accepted': - case 'DEVS.Accepted': - case 'SUMMARY.Accepted': - case 'POOL.Accepted': - case 'total.Accepted': - case 'GPU.Rejected': - case 'PGA.Rejected': - case 'ASC.Rejected': - case 'DEVS.Rejected': - case 'SUMMARY.Rejected': - case 'POOL.Rejected': - case 'total.Rejected': - case 'SUMMARY.Local Work': - case 'total.Local Work': - case 'SUMMARY.Discarded': - case 'POOL.Discarded': - case 'total.Discarded': - case 'POOL.Diff1 Shares': - case 'total.Diff1 Shares': - case 'GPU.Diff1 Work': - case 'PGA.Diff1 Work': - case 'ASC.Diff1 Work': - case 'total.Diff1 Work': - case 'STATS.Times Sent': - case 'STATS.Bytes Sent': - case 'STATS.Net Bytes Sent': - case 'STATS.Times Recv': - case 'STATS.Bytes Recv': - case 'STATS.Net Bytes Recv': - case 'total.Times Sent': - case 'total.Bytes Sent': - case 'total.Net Bytes Sent': - case 'total.Times Recv': - case 'total.Bytes Recv': - case 'total.Net Bytes Recv': + case 'Total MH': + case 'Getworks': + case 'Works': + case 'Accepted': + case 'Rejected': + case 'Local Work': + case 'Discarded': + case 'Diff1 Shares': + case 'Diff1 Work': + case 'Times Sent': + case 'Bytes Sent': + case 'Net Bytes Sent': + case 'Times Recv': + case 'Bytes Recv': + case 'Net Bytes Recv': $parts = explode('.', $value, 2); if (count($parts) == 1) $dec = ''; @@ -1044,16 +1419,16 @@ function fmt($section, $name, $value, $when, $alldata) $dec = '.'.$parts[1]; $ret = number_format((float)$parts[0]).$dec; break; - case 'STATS.Hs': - case 'STATS.W': - case 'STATS.history_time': - case 'STATS.Pool Wait': - case 'STATS.Pool Max': - case 'STATS.Pool Min': - case 'STATS.Pool Av': - case 'STATS.Min Diff': - case 'STATS.Max Diff': - case 'STATS.Work Diff': + case 'Hs': + case 'W': + case 'history_time': + case 'Pool Wait': + case 'Pool Max': + case 'Pool Min': + case 'Pool Av': + case 'Min Diff': + case 'Max Diff': + case 'Work Diff': $parts = explode('.', $value, 2); if (count($parts) == 1) $dec = ''; @@ -1061,78 +1436,87 @@ function fmt($section, $name, $value, $when, $alldata) $dec = '.'.endzero($parts[1]); $ret = number_format((float)$parts[0]).$dec; break; - case 'GPU.Status': - case 'PGA.Status': - case 'ASC.Status': - case 'DEVS.Status': - case 'POOL.Status': + case 'Status': + if ($section == 'total') + break; if ($value != 'Alive') $class = $errorclass; break; - case 'GPU.Enabled': - case 'PGA.Enabled': - case 'ASC.Enabled': - case 'ASC.Enabled': - case 'DEVS.Enabled': + case 'Enabled': + if ($section == 'total') + break; if ($value != 'Y') $class = $warnclass; break; - case 'STATUS.When': - case 'COIN.Current Block Time': - $ret = date($dfmt, $value); + case 'No Device': + if ($section == 'total') + break; + if ($value != 'false') + $class = $errorclass; break; - case 'BUTTON.Rig': - case 'BUTTON.Pool': - case 'BUTTON.GPU': - $ret = $value; + case 'When': + case 'Current Block Time': + if ($section == 'total') + break; + $ret = date($dfmt, $value); break; - case 'SUMMARY.Difficulty Accepted': - case 'GPU.Difficulty Accepted': - case 'PGA.Difficulty Accepted': - case 'ASC.Difficulty Accepted': - case 'DEVS.Difficulty Accepted': - case 'POOL.Difficulty Accepted': - case 'total.Difficulty Accepted': - case 'SUMMARY.Difficulty Rejected': - case 'GPU.Difficulty Rejected': - case 'PGA.Difficulty Rejected': - case 'ASC.Difficulty Rejected': - case 'DEVS.Difficulty Rejected': - case 'POOL.Difficulty Rejected': - case 'total.Difficulty Rejected': - case 'SUMMARY.Difficulty Stale': - case 'POOL.Difficulty Stale': - case 'total.Difficulty Stale': - case 'GPU.Last Share Difficulty': - case 'PGA.Last Share Difficulty': - case 'ASC.Last Share Difficulty': - case 'DEVS.Last Share Difficulty': - case 'POOL.Last Share Difficulty': + case 'Last Share Difficulty': + if ($section == 'total') + break; + case 'Difficulty Accepted': + case 'Difficulty Rejected': + case 'Difficulty Stale': if ($value != '') $ret = number_format((float)$value, 2); break; - case 'DEVS.Device Hardware%': - case 'DEVS.Device Rejected%': - case 'ASC.Device Hardware%': - case 'ASC.Device Rejected%': - case 'PGA.Device Hardware%': - case 'PGA.Device Rejected%': - case 'GPU.Device Hardware%': - case 'GPU.Device Rejected%': - case 'POOL.Pool Rejected%': - case 'POOL.Pool Stale%': - case 'SUMMARY.Device Hardware%': - case 'SUMMARY.Device Rejected%': - case 'SUMMARY.Pool Rejected%': - case 'SUMMARY.Pool Stale%': + case 'Device Hardware%': + case 'Device Rejected%': + case 'Pool Rejected%': + case 'Pool Stale%': + if ($section == 'total') + break; if ($value != '') $ret = number_format((float)$value, 2) . '%'; break; - case 'SUMMARY.Best Share': + case 'Best Share': + if ($section == 'total') + break; + case 'Hardware Errors': if ($value != '') $ret = number_format((float)$value); break; + // BUTTON. + case 'Rig': + case 'Pool': + case 'GPU': + break; + // Sample GEN fields + case 'Mined': + if ($value != '') + $ret = number_format((float)$value * 100.0, 3) . '%'; + break; + case 'Acc': + case 'Rej': + if ($value != '') + $ret = number_format((float)$value * 100.0, 2) . '%'; + break; + case 'GHS av': + case 'GHS 5m': + case 'GHS WU': + case 'GHS Acc': + if ($value != '') + $ret = number_format((float)$value, 2); + break; + case 'AvShr': + if ($section == 'total') + break; + if ($value != '') + $ret = number_format((float)$value, 2); + if ($value == 0) + $class = $warnclass; + break; } + } if ($section == 'NOTIFY' && substr($name, 0, 1) == '*' && $value != '0') $class = $errorclass; @@ -1149,6 +1533,9 @@ function fmt($section, $name, $value, $when, $alldata) if ($ret === '') $ret = $b; + if ($class !== '') + $class = " class=$class"; + return array($ret, $class); } # @@ -1187,14 +1574,16 @@ function showdatetime() # global $singlerigsum; $singlerigsum = array( - 'devs' => array('MHS av' => 1, 'MHS 5s' => 1, 'Accepted' => 1, 'Rejected' => 1, + 'devs' => array('MHS av' => 1, 'MHS 5s' => 1, 'MHS 1m' => 1, 'MHS 5m' => 1, + 'MHS 15m' => 1, 'Accepted' => 1, 'Rejected' => 1, 'Hardware Errors' => 1, 'Utility' => 1, 'Total MH' => 1, - 'Diff1 Shares' => 1, 'Diff1 Work' => 1, 'Difficulty Accepted' => 1, - 'Difficulty Rejected' => 1), + 'Diff1 Shares' => 1, 'Diff1 Work' => 1, + 'Difficulty Accepted' => 1, 'Difficulty Rejected' => 1), 'pools' => array('Getworks' => 1, 'Accepted' => 1, 'Rejected' => 1, 'Discarded' => 1, 'Stale' => 1, 'Get Failures' => 1, 'Remote Failures' => 1, - 'Diff1 Shares' => 1, 'Diff1 Work' => 1, 'Difficulty Accepted' => 1, - 'Difficulty Rejected' => 1, 'Difficulty Stale' => 1), + 'Diff1 Shares' => 1, 'Diff1 Work' => 1, + 'Difficulty Accepted' => 1, 'Difficulty Rejected' => 1, + 'Difficulty Stale' => 1), 'notify' => array('*' => 1)); # function showtotal($total, $when, $oldvalues) @@ -1511,39 +1900,67 @@ function process($cmds, $rig) # function rigname($rig, $rigname) { - global $rigs; + global $rigs, $rignames, $rigips; if (isset($rigs[$rig])) { $parts = explode(':', $rigs[$rig], 3); if (count($parts) == 3) $rigname = $parts[2]; + else + if ($rignames !== false) + { + switch ($rignames) + { + case 'ip': + if (isset($parts[0]) && isset($rigips[$parts[0]])) + { + $ip = explode('.', $rigips[$parts[0]]); + if (count($ip) == 4) + $rigname = intval($ip[3]); + } + break; + case 'ipx': + if (isset($parts[0]) && isset($rigips[$parts[0]])) + { + $ip = explode('.', $rigips[$parts[0]]); + if (count($ip) == 4) + $rigname = intval($ip[3], 16); + } + break; + } + } } return $rigname; } # -function riginput($rig, $rigname) +function riginput($rig, $rigname, $usebuttons) { $rigname = rigname($rig, $rigname); - return ""; + if ($usebuttons === true) + return ""; + else + return "$rigname"; } # -function rigbutton($rig, $rigname, $when, $row) +function rigbutton($rig, $rigname, $when, $row, $usebuttons) { list($value, $class) = fmt('BUTTON', 'Rig', '', $when, $row); if ($rig === '') $ri = ' '; else - $ri = riginput($rig, $rigname); + $ri = riginput($rig, $rigname, $usebuttons); return "$ri"; } # function showrigs($anss, $headname, $rigname) { + global $rigbuttons; + $dthead = array($headname => 1, 'STATUS' => 1, 'Description' => 1, 'When' => 1, 'API' => 1, 'CGMiner' => 1); showhead('', $dthead); @@ -1566,7 +1983,7 @@ function showrigs($anss, $headname, $rigname) foreach ($dthead as $name => $x) { if ($item == 'STATUS' && $name == $headname) - echo rigbutton($rig, $rigname.$rig, $when, null); + echo rigbutton($rig, $rigname.$rig, $when, null, $rigbuttons); else { if (isset($row[$name])) @@ -1581,165 +1998,6 @@ function showrigs($anss, $headname, $rigname) } } # -# $head is a hack but this is just a demo anyway :) -function doforeach($cmd, $des, $sum, $head, $datetime) -{ - global $miner, $port; - global $error, $readonly, $notify, $rigs; - global $warnfont, $warnoff, $dfmt; - global $rigerror; - - $when = 0; - - $header = $head; - $anss = array(); - - $count = 0; - $preverr = count($rigerror); - foreach ($rigs as $num => $rig) - { - $anss[$num] = null; - - if (isset($rigerror[$rig])) - continue; - - $parts = explode(':', $rig, 3); - if (count($parts) >= 2) - { - $miner = $parts[0]; - $port = $parts[1]; - - if (count($parts) > 2) - $name = $parts[2]; - else - $name = $num; - - $ans = api($name, $cmd); - - if ($error != null) - { - $rw = "Error on rig $name getting "; - $rw .= "$des: $warnfont$error$warnoff"; - otherrow($rw); - $rigerror[$rig] = $error; - $error = null; - } - else - { - $anss[$num] = $ans; - $count++; - } - } - } - - if ($count == 0) - { - $rw = 'Failed to access any rigs successfully'; - if ($preverr > 0) - $rw .= ' (or rigs had previous errors)'; - $rw .= ''; - otherrow($rw); - return; - } - - if ($datetime) - { - showdatetime(); - endtable(); - newtable(); - showrigs($anss, '', 'Rig '); - endtable(); - otherrow('

'); - newtable(); - - return; - } - - $total = array(); - - foreach ($anss as $rig => $ans) - { - if ($ans == null) - continue; - - foreach ($ans as $item => $row) - { - if ($item == 'STATUS') - continue; - - if (count($row) > count($header)) - { - $header = $head; - foreach ($row as $name => $value) - if (!isset($header[$name])) - $header[$name] = ''; - } - - if ($sum != null) - foreach ($sum as $name) - { - if (isset($row[$name])) - { - if (isset($total[$name])) - $total[$name] += $row[$name]; - else - $total[$name] = $row[$name]; - } - } - } - } - - if ($sum != null) - $anss['total']['total'] = $total; - - showhead('', $header); - - foreach ($anss as $rig => $ans) - { - if ($ans == null) - continue; - - $when = 0; - if (isset($ans['STATUS']['When'])) - $when = $ans['STATUS']['When']; - - foreach ($ans as $item => $row) - { - if ($item == 'STATUS') - continue; - - newrow(); - - $section = preg_replace('/\d/', '', $item); - - foreach ($header as $name => $x) - { - if ($name == '') - { - if ($rig === 'total') - { - list($ignore, $class) = fmt($rig, '', '', $when, $row); - echo "Total:"; - } - else - echo rigbutton($rig, "Rig $rig", $when, $row); - } - else - { - if (isset($row[$name])) - $value = $row[$name]; - else - $value = null; - - list($showvalue, $class) = fmt($section, $name, $value, $when, $row); - echo "$showvalue"; - } - } - endrow(); - } - } -} -# function refreshbuttons() { global $ignorerefresh, $changerefresh, $autorefresh; @@ -1755,7 +2013,7 @@ function refreshbuttons() # function pagebuttons($rig, $pg) { - global $readonly, $rigs, $userlist, $ses; + global $readonly, $rigs, $rigbuttons, $userlist, $ses; global $allowcustompages, $customsummarypages; if ($rig === null) @@ -1794,12 +2052,14 @@ function pagebuttons($rig, $pg) if ($userlist === null || isset($_SESSION[$ses])) { if ($prev !== null) - echo riginput($prev, 'Prev').' '; + echo riginput($prev, 'Prev', true).' '; + echo " "; + if ($next !== null) - echo riginput($next, 'Next').' '; + echo riginput($next, 'Next', true).' '; echo ' '; - if (count($rigs) > 1) + if (count($rigs) > 1 and getcsp('Summary', true) !== false) echo " "; } @@ -1816,7 +2076,8 @@ function pagebuttons($rig, $pg) } foreach ($list as $pagename => $data) - echo " "; + if (getcsp($pagename) !== false) + echo " "; } echo ' '; @@ -1877,12 +2138,14 @@ function doOne($rig, $preprocess) 'SUMMARY' => 'summary', 'POOL' => 'pools', 'DEVS' => 'devs', + 'EDEVS' => 'edevs', 'GPU' => 'devs', // You would normally use DEVS 'PGA' => 'devs', // You would normally use DEVS 'ASC' => 'devs', // You would normally use DEVS 'NOTIFY' => 'notify', 'DEVDETAILS' => 'devdetails', 'STATS' => 'stats', + 'ESTATS' => 'estats', 'CONFIG' => 'config', 'COIN' => 'coin', 'USBSTATS' => 'usbstats'); @@ -2090,6 +2353,7 @@ function joinsections($sections, $results, $errors) { case 'POOL': case 'DEVS': + case 'EDEVS': case 'CONFIG': case 'COIN': $sectionmap[$section] = $section; @@ -2101,6 +2365,7 @@ function joinsections($sections, $results, $errors) } break; case 'DEVS': + case 'EDEVS': switch($both[1]) { case 'NOTIFY': @@ -2111,6 +2376,7 @@ function joinsections($sections, $results, $errors) $results[$section] = joinfields($both[0], $both[1], $join, $results); break; case 'STATS': + case 'ESTATS': $join = array('L' => array('Name','ID'), 'R' => array('ID')); $sectionmap[$section] = $section; $results[$section] = joinlr($both[0], $both[1], $join, $results); @@ -2150,15 +2416,18 @@ function secmatch($section, $field) if ($section == $field) return true; - if ($section == 'DEVS' + if (($section == 'DEVS' || $section == 'EDEVS') && ($field == 'GPU' || $field == 'PGA' || $field == 'ASC')) return true; return false; } # -function customset($showfields, $sum, $section, $rig, $isbutton, $result, $total) +function customset($showfields, $sum, $section, $rig, $isbutton, $result, $total, $cf = NULL) { + global $rigbuttons; + + $rn = 0; foreach ($result as $sec => $row) { $secname = preg_replace('/\d/', '', $sec); @@ -2175,16 +2444,25 @@ function customset($showfields, $sum, $section, $rig, $isbutton, $result, $total if ($isbutton) - echo rigbutton($rig, $rig, $when, $row); + echo rigbutton($rig, $rig, $when, $row, $rigbuttons); else { - list($ignore, $class) = fmt('total', '', '', $when, $row); + list($ignore, $class) = fmt('total', '', '', $when, $row, $cf); echo "$rig"; } foreach ($showfields as $name => $one) { - if (isset($row[$name])) + if ($name === '#' and $sec != 'total') + { + $rn++; + $value = $rn; + if (isset($total[$name])) + $total[$name]++; + else + $total[$name] = 1; + } + elseif (isset($row[$name])) { $value = $row[$name]; @@ -2205,11 +2483,14 @@ function customset($showfields, $sum, $section, $rig, $isbutton, $result, $total } if (strpos($secname, '+') === false) - list($showvalue, $class) = fmt($secname, $name, $value, $when, $row); + list($showvalue, $class) = fmt($secname, $name, $value, $when, $row, $cf); else { - $parts = explode('.', $name, 2); - list($showvalue, $class) = fmt($parts[0], $parts[1], $value, $when, $row); + if ($name != '#') + $parts = explode('.', $name, 2); + else + $parts[0] = $parts[1] = '#'; + list($showvalue, $class) = fmt($parts[0], $parts[1], $value, $when, $row, $cf); } echo "$showvalue"; @@ -2360,10 +2641,17 @@ function ss($a, $b) $la = strlen($a); $lb = strlen($b); if ($la != $lb) - return $la - $lb; + return $lb - $la; return strcmp($a, $b); } # +# If you are developing a customsummarypage that uses BGEN or GEN, +# you may want to remove the '@' in front of '@eval()' to help with debugging +# The '@' removes php comments from the web log about missing fields +# Since there are many forks of cgminer that break the API or do not +# keep their fork up to date with current cgminer, the addition of +# '@' solves the problem of generating unnecessary and excessive web logs +# about the eval() function genfld($row, $calc) { uksort($row, "ss"); @@ -2372,17 +2660,20 @@ function genfld($row, $calc) if (strstr($calc, $name) !== FALSE) $calc = str_replace($name, $value, $calc); - eval("\$val = $calc;"); + @eval("\$val = $calc;"); - return $val; + if (!isset($val)) + return ''; + else + return $val; } # -function dogen($ext, $section, &$res, &$fields) +function dogen($ext, $wg, $gname, $section, &$res, &$fields) { - $gen = $ext[$section]['gen']; + $gen = $ext[$section][$wg]; foreach ($gen as $fld => $calc) - $fields[] = "GEN.$fld"; + $fields[] = "$gname.$fld"; foreach ($res as $rig => $result) foreach ($result as $sec => $row) @@ -2391,7 +2682,7 @@ function dogen($ext, $section, &$res, &$fields) if (secmatch($section, $secname)) foreach ($gen as $fld => $calc) { - $name = "GEN.$fld"; + $name = "$gname.$fld"; $val = genfld($row, $calc); @@ -2406,6 +2697,10 @@ function processext($ext, $section, $res, &$fields) $res = processcompare('where', $ext, $section, $res); + // Generated fields (functions of other fields before grouping) + if ($allowgen === true && isset($ext[$section]['bgen'])) + dogen($ext, 'bgen', 'BGEN', $section, $res, $fields); + if (isset($ext[$section]['group'])) { $grp = $ext[$section]['group']; @@ -2450,9 +2745,12 @@ function processext($ext, $section, $res, &$fields) if ($calc !== null) foreach ($calc as $field => $func) { - if (!isset($interim[$grpkey]['cal'][$field])) - $interim[$grpkey]['cal'][$field] = array(); - $interim[$grpkey]['cal'][$field][] = $row[$field]; + if (isset($row[$field])) + { + if (!isset($interim[$grpkey]['cal'][$field])) + $interim[$grpkey]['cal'][$field] = array(); + $interim[$grpkey]['cal'][$field][] = $row[$field]; + } } } } @@ -2471,9 +2769,9 @@ function processext($ext, $section, $res, &$fields) } } - // Generated fields (functions of other fields) + // Generated fields (functions of other fields after grouping) if ($allowgen === true && isset($ext[$section]['gen'])) - dogen($ext, $section, $res, $fields); + dogen($ext, 'gen', 'GEN', $section, $res, $fields); return processcompare('having', $ext, $section, $res); } @@ -2510,10 +2808,13 @@ function processcustompage($pagename, $sections, $sum, $ext, $namemap) foreach ($rigs as $num => $rig) { $parts = explode(':', $rig, 3); - if (count($parts) >= 2) + if (count($parts) >= 1) { $miner = $parts[0]; - $port = $parts[1]; + if (count($parts) >= 2) + $port = $parts[1]; + else + $port = ''; if (count($parts) > 2) $name = $parts[2]; @@ -2537,6 +2838,14 @@ function processcustompage($pagename, $sections, $sum, $ext, $namemap) otherrow('Bad "$rigs" array'); } + // Show API errors at the top + if (count($errors) > 0) + { + foreach ($errors as $err) + otherrow("$err"); + $errors = array(); + } + $shownsomething = false; if (count($results) > 0) { @@ -2571,6 +2880,11 @@ function processcustompage($pagename, $sections, $sum, $ext, $namemap) if (isset($results[$sectionmap[$section]])) { + if (isset($ext[$section]['fmt'])) + $cf = $ext[$section]['fmt']; + else + $cf = NULL; + $rigresults = processext($ext, $section, $results[$sectionmap[$section]], $fields); $showfields = array(); @@ -2594,6 +2908,11 @@ function processcustompage($pagename, $sections, $sum, $ext, $namemap) $showhead[$f] = 1; } } + elseif ($field === '#') + { + $showfields[$field] = 1; + $showhead[$field] = 1; + } elseif (isset($row[$field])) { $showfields[$field] = 1; @@ -2622,10 +2941,10 @@ function processcustompage($pagename, $sections, $sum, $ext, $namemap) $add = array('total' => array()); foreach ($rigresults as $num => $result) - $total = customset($showfields, $sum, $section, $num, true, $result, $total); + $total = customset($showfields, $sum, $section, $num, true, $result, $total, $cf); if (count($total) > 0) - customset($showfields, $sum, $section, 'Σ', false, $add, $total); + customset($showfields, $sum, $section, 'Σ', false, $add, $total, $cf); $first = false; @@ -2646,7 +2965,7 @@ function processcustompage($pagename, $sections, $sum, $ext, $namemap) } } # -function showcustompage($pagename) +function showcustompage($pagename, $systempage = false) { global $customsummarypages; global $placebuttons; @@ -2654,22 +2973,22 @@ function showcustompage($pagename) if ($placebuttons == 'top' || $placebuttons == 'both') pagebuttons(null, $pagename); - if (!isset($customsummarypages[$pagename])) + if ($systempage === false && !isset($customsummarypages[$pagename])) { otherrow("Unknown custom summary page '$pagename'"); return; } - $c = count($customsummarypages[$pagename]); - if ($c < 2 || $c > 3) + $csp = getcsp($pagename, $systempage); + if ($csp === false) { - $rw = "Invalid custom summary page '$pagename' ("; - $rw .= count($customsummarypages[$pagename]).')'; - otherrow($rw); + otherrow("Invalid custom summary page '$pagename'"); return; } - $page = $customsummarypages[$pagename][0]; + degen($csp); + + $page = $csp[0]; $namemap = array(); foreach ($page as $name => $fields) { @@ -2690,10 +3009,10 @@ function showcustompage($pagename) } $ext = null; - if (isset($customsummarypages[$pagename][2])) - $ext = $customsummarypages[$pagename][2]; + if (isset($csp[2])) + $ext = $csp[2]; - $sum = $customsummarypages[$pagename][1]; + $sum = $csp[1]; if ($sum === null) $sum = array(); @@ -2753,7 +3072,7 @@ function onlylogin() # function checklogin() { - global $allowcustompages, $customsummarypages; + global $allowcustompages; global $readonly, $userlist, $ses; $out = trim(getparam('logout', true)); @@ -2811,7 +3130,7 @@ function checklogin() { // Ensure at least one exists foreach ($userlist['def'] as $pg) - if (isset($customsummarypages[$pg])) + if (getcsp($pg) !== false) return true; } @@ -2825,7 +3144,7 @@ function display() global $mcast, $mcastexpect; global $readonly, $notify, $rigs; global $ignorerefresh, $autorefresh; - global $allowcustompages, $customsummarypages; + global $allowcustompages; global $placebuttons; global $userlist, $ses; @@ -2871,10 +3190,13 @@ function display() if ($rig != null and $rig != '' and $rig >= 0 and $rig < count($rigs)) { $parts = explode(':', $rigs[$rig], 3); - if (count($parts) >= 2) + if (count($parts) >= 1) { $miner = $parts[0]; - $port = $parts[1]; + if (count($parts) >= 2) + $port = $parts[1]; + else + $port = ''; if ($readonly !== true) $preprocess = $arg; @@ -2885,7 +3207,7 @@ function display() if ($allowcustompages === true) { - $pg = trim(getparam('pg', true)); + $pg = urlencode(trim(getparam('pg', true))); if ($pagesonly === true) { if ($pg !== null && $pg !== '') @@ -2898,7 +3220,7 @@ function display() { if ($userlist !== null && isset($userlist['def'])) foreach ($userlist['def'] as $pglook) - if (isset($customsummarypages[$pglook])) + if (getcsp($pglook) !== false) { $pg = $pglook; break; @@ -2909,7 +3231,7 @@ function display() if ($pg !== null && $pg !== '') { htmlhead($mcerr, false, null, $pg); - showcustompage($pg, $mcerr); + showcustompage($pg); return; } } @@ -2923,10 +3245,13 @@ function display() if (count($rigs) == 1) { $parts = explode(':', $rigs[0], 3); - if (count($parts) >= 2) + if (count($parts) >= 1) { $miner = $parts[0]; - $port = $parts[1]; + if (count($parts) >= 2) + $port = $parts[1]; + else + $port = ''; htmlhead($mcerr, true, 0); doOne(0, $preprocess); @@ -2943,10 +3268,13 @@ function display() if ($rig != null and $rig != '' and $rig >= 0 and $rig < count($rigs)) { $parts = explode(':', $rigs[$rig], 3); - if (count($parts) >= 2) + if (count($parts) >= 1) { $miner = $parts[0]; - $port = $parts[1]; + if (count($parts) >= 2) + $port = $parts[1]; + else + $port = ''; htmlhead($mcerr, true, 0); doOne($rig, $preprocess); @@ -2962,28 +3290,11 @@ function display() htmlhead($mcerr, false, null); - if ($placebuttons == 'top' || $placebuttons == 'both') - pagebuttons(null, null); - if ($preprocess != null) process(array($preprocess => $preprocess), $rig); - newtable(); - doforeach('version', 'rig summary', array(), array(), true); - $sum = array('MHS av', 'Getworks', 'Found Blocks', 'Accepted', 'Rejected', 'Discarded', 'Stale', 'Utility', 'Local Work', 'Total MH', 'Work Utility', 'Diff1 Shares', 'Diff1 Work', 'Difficulty Accepted', 'Difficulty Rejected', 'Difficulty Stale'); - doforeach('summary', 'summary information', $sum, array(), false); - endtable(); - otherrow('

'); - newtable(); - doforeach('devs', 'device list', $sum, array(''=>'','ID'=>'','Name'=>''), false); - endtable(); - otherrow('

'); - newtable(); - doforeach('pools', 'pool list', $sum, array(''=>''), false); - endtable(); - - if ($placebuttons == 'bot' || $placebuttons == 'both') - pagebuttons(null, null); + if (getcsp('Summary', true) !== false) + showcustompage('Summary', true); } # if ($mcast === true) diff --git a/noncedup.c b/noncedup.c new file mode 100644 index 0000000000..6b94fd60a4 --- /dev/null +++ b/noncedup.c @@ -0,0 +1,99 @@ +/* + * Copyright 2014 Andrew Smith + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the Free + * Software Foundation; either version 3 of the License, or (at your option) + * any later version. See COPYING for more details. + */ + +#include "miner.h" +#include "klist.h" + +// Nonce +typedef struct nitem { + uint32_t work_id; + uint32_t nonce; + struct timeval when; +} NITEM; + +#define DATAN(_item) ((NITEM *)(_item->data)) + +struct dupdata { + int timelimit; + K_LIST *nfree_list; + K_STORE *nonce_list; + uint64_t checked; + uint64_t dups; +}; + +void dupalloc(struct cgpu_info *cgpu, int timelimit) +{ + struct dupdata *dup; + + dup = calloc(1, sizeof(*dup)); + if (unlikely(!dup)) + quithere(1, "Failed to calloc dupdata"); + + dup->timelimit = timelimit; + dup->nfree_list = k_new_list("Nonces", sizeof(NITEM), 1024, 0, true); + dup->nonce_list = k_new_store(dup->nfree_list); + + cgpu->dup_data = dup; +} + +void dupcounters(struct cgpu_info *cgpu, uint64_t *checked, uint64_t *dups) +{ + struct dupdata *dup = (struct dupdata *)(cgpu->dup_data); + + if (!dup) { + *checked = 0; + *dups = 0; + } else { + *checked = dup->checked; + *dups = dup->dups; + } +} + +bool isdupnonce(struct cgpu_info *cgpu, struct work *work, uint32_t nonce) +{ + struct dupdata *dup = (struct dupdata *)(cgpu->dup_data); + struct timeval now; + bool unique = true; + K_ITEM *item; + + if (!dup) + return false; + + cgtime(&now); + dup->checked++; + K_WLOCK(dup->nfree_list); + item = dup->nonce_list->tail; + while (unique && item) { + if (DATAN(item)->work_id == work->id && DATAN(item)->nonce == nonce) { + unique = false; + applog(LOG_WARNING, "%s%d: Duplicate nonce %08x", + cgpu->drv->name, cgpu->device_id, nonce); + } else + item = item->prev; + } + if (unique) { + item = k_unlink_head(dup->nfree_list); + DATAN(item)->work_id = work->id; + DATAN(item)->nonce = nonce; + memcpy(&(DATAN(item)->when), &now, sizeof(now)); + k_add_head(dup->nonce_list, item); + } + item = dup->nonce_list->tail; + while (item && tdiff(&(DATAN(item)->when), &now) > dup->timelimit) { + item = k_unlink_tail(dup->nonce_list); + k_add_head(dup->nfree_list, item); + item = dup->nonce_list->tail; + } + K_WUNLOCK(dup->nfree_list); + + if (!unique) + dup->dups++; + + return !unique; +} diff --git a/ocl.c b/ocl.c deleted file mode 100644 index 27ef1e9d57..0000000000 --- a/ocl.c +++ /dev/null @@ -1,841 +0,0 @@ -/* - * Copyright 2011-2012 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 "config.h" -#ifdef HAVE_OPENCL - -#include -#include -#include -#include -#include - -#ifdef WIN32 - #include -#else - #include - #include - #include -#endif - -#include -#include -#include -#include -#include - -#include "findnonce.h" -#include "ocl.h" - -int opt_platform_id = -1; - -char *file_contents(const char *filename, int *length) -{ - char *fullpath = alloca(PATH_MAX); - void *buffer; - FILE *f; - - strcpy(fullpath, opt_kernel_path); - strcat(fullpath, filename); - - /* Try in the optional kernel path or installed prefix first */ - f = fopen(fullpath, "rb"); - if (!f) { - /* Then try from the path cgminer was called */ - strcpy(fullpath, cgminer_path); - strcat(fullpath, filename); - f = fopen(fullpath, "rb"); - } - /* Finally try opening it directly */ - if (!f) - f = fopen(filename, "rb"); - - if (!f) { - applog(LOG_ERR, "Unable to open %s or %s for reading", filename, fullpath); - return NULL; - } - - fseek(f, 0, SEEK_END); - *length = ftell(f); - fseek(f, 0, SEEK_SET); - - buffer = malloc(*length+1); - *length = fread(buffer, 1, *length, f); - fclose(f); - ((char*)buffer)[*length] = '\0'; - - return (char*)buffer; -} - -int clDevicesNum(void) { - cl_int status; - char pbuff[256]; - cl_uint numDevices; - cl_uint numPlatforms; - int most_devices = -1; - cl_platform_id *platforms; - cl_platform_id platform = NULL; - unsigned int i, mdplatform = 0; - - status = clGetPlatformIDs(0, NULL, &numPlatforms); - /* If this fails, assume no GPUs. */ - if (status != CL_SUCCESS) { - applog(LOG_ERR, "Error %d: clGetPlatformsIDs failed (no OpenCL SDK installed?)", status); - return -1; - } - - if (numPlatforms == 0) { - applog(LOG_ERR, "clGetPlatformsIDs returned no platforms (no OpenCL SDK installed?)"); - return -1; - } - - platforms = (cl_platform_id *)alloca(numPlatforms*sizeof(cl_platform_id)); - status = clGetPlatformIDs(numPlatforms, platforms, NULL); - if (status != CL_SUCCESS) { - applog(LOG_ERR, "Error %d: Getting Platform Ids. (clGetPlatformsIDs)", status); - return -1; - } - - for (i = 0; i < numPlatforms; i++) { - if (opt_platform_id >= 0 && (int)i != opt_platform_id) - continue; - - status = clGetPlatformInfo( platforms[i], CL_PLATFORM_VENDOR, sizeof(pbuff), pbuff, NULL); - if (status != CL_SUCCESS) { - applog(LOG_ERR, "Error %d: Getting Platform Info. (clGetPlatformInfo)", status); - return -1; - } - platform = platforms[i]; - applog(LOG_INFO, "CL Platform %d vendor: %s", i, pbuff); - status = clGetPlatformInfo(platform, CL_PLATFORM_NAME, sizeof(pbuff), pbuff, NULL); - if (status == CL_SUCCESS) - applog(LOG_INFO, "CL Platform %d name: %s", i, pbuff); - status = clGetPlatformInfo(platform, CL_PLATFORM_VERSION, sizeof(pbuff), pbuff, NULL); - if (status == CL_SUCCESS) - applog(LOG_INFO, "CL Platform %d version: %s", i, pbuff); - status = clGetDeviceIDs(platform, CL_DEVICE_TYPE_GPU, 0, NULL, &numDevices); - if (status != CL_SUCCESS) { - applog(LOG_INFO, "Error %d: Getting Device IDs (num)", status); - continue; - } - applog(LOG_INFO, "Platform %d devices: %d", i, numDevices); - if ((int)numDevices > most_devices) { - most_devices = numDevices; - mdplatform = i; - } - if (numDevices) { - unsigned int j; - cl_device_id *devices = (cl_device_id *)malloc(numDevices*sizeof(cl_device_id)); - - clGetDeviceIDs(platform, CL_DEVICE_TYPE_GPU, numDevices, devices, NULL); - for (j = 0; j < numDevices; j++) { - clGetDeviceInfo(devices[j], CL_DEVICE_NAME, sizeof(pbuff), pbuff, NULL); - applog(LOG_INFO, "\t%i\t%s", j, pbuff); - } - free(devices); - } - } - - if (opt_platform_id < 0) - opt_platform_id = mdplatform;; - - return most_devices; -} - -static int advance(char **area, unsigned *remaining, const char *marker) -{ - char *find = memmem(*area, *remaining, marker, strlen(marker)); - - if (!find) { - applog(LOG_DEBUG, "Marker \"%s\" not found", marker); - return 0; - } - *remaining -= find - *area; - *area = find; - return 1; -} - -#define OP3_INST_BFE_UINT 4ULL -#define OP3_INST_BFE_INT 5ULL -#define OP3_INST_BFI_INT 6ULL -#define OP3_INST_BIT_ALIGN_INT 12ULL -#define OP3_INST_BYTE_ALIGN_INT 13ULL - -void patch_opcodes(char *w, unsigned remaining) -{ - uint64_t *opcode = (uint64_t *)w; - int patched = 0; - int count_bfe_int = 0; - int count_bfe_uint = 0; - int count_byte_align = 0; - while (42) { - int clamp = (*opcode >> (32 + 31)) & 0x1; - int dest_rel = (*opcode >> (32 + 28)) & 0x1; - int alu_inst = (*opcode >> (32 + 13)) & 0x1f; - int s2_neg = (*opcode >> (32 + 12)) & 0x1; - int s2_rel = (*opcode >> (32 + 9)) & 0x1; - int pred_sel = (*opcode >> 29) & 0x3; - if (!clamp && !dest_rel && !s2_neg && !s2_rel && !pred_sel) { - if (alu_inst == OP3_INST_BFE_INT) { - count_bfe_int++; - } else if (alu_inst == OP3_INST_BFE_UINT) { - count_bfe_uint++; - } else if (alu_inst == OP3_INST_BYTE_ALIGN_INT) { - count_byte_align++; - // patch this instruction to BFI_INT - *opcode &= 0xfffc1fffffffffffULL; - *opcode |= OP3_INST_BFI_INT << (32 + 13); - patched++; - } - } - if (remaining <= 8) - break; - opcode++; - remaining -= 8; - } - applog(LOG_DEBUG, "Potential OP3 instructions identified: " - "%i BFE_INT, %i BFE_UINT, %i BYTE_ALIGN", - count_bfe_int, count_bfe_uint, count_byte_align); - applog(LOG_DEBUG, "Patched a total of %i BFI_INT instructions", patched); -} - -_clState *initCl(unsigned int gpu, char *name, size_t nameSize) -{ - _clState *clState = calloc(1, sizeof(_clState)); - bool patchbfi = false, prog_built = false; - struct cgpu_info *cgpu = &gpus[gpu]; - cl_platform_id platform = NULL; - char pbuff[256], vbuff[255]; - cl_platform_id* platforms; - cl_uint preferred_vwidth; - cl_device_id *devices; - cl_uint numPlatforms; - cl_uint numDevices; - cl_int status; - - status = clGetPlatformIDs(0, NULL, &numPlatforms); - if (status != CL_SUCCESS) { - applog(LOG_ERR, "Error %d: Getting Platforms. (clGetPlatformsIDs)", status); - return NULL; - } - - platforms = (cl_platform_id *)alloca(numPlatforms*sizeof(cl_platform_id)); - status = clGetPlatformIDs(numPlatforms, platforms, NULL); - if (status != CL_SUCCESS) { - applog(LOG_ERR, "Error %d: Getting Platform Ids. (clGetPlatformsIDs)", status); - return NULL; - } - - if (opt_platform_id >= (int)numPlatforms) { - applog(LOG_ERR, "Specified platform that does not exist"); - return NULL; - } - - status = clGetPlatformInfo(platforms[opt_platform_id], CL_PLATFORM_VENDOR, sizeof(pbuff), pbuff, NULL); - if (status != CL_SUCCESS) { - applog(LOG_ERR, "Error %d: Getting Platform Info. (clGetPlatformInfo)", status); - return NULL; - } - platform = platforms[opt_platform_id]; - - if (platform == NULL) { - perror("NULL platform found!\n"); - return NULL; - } - - applog(LOG_INFO, "CL Platform vendor: %s", pbuff); - status = clGetPlatformInfo(platform, CL_PLATFORM_NAME, sizeof(pbuff), pbuff, NULL); - if (status == CL_SUCCESS) - applog(LOG_INFO, "CL Platform name: %s", pbuff); - status = clGetPlatformInfo(platform, CL_PLATFORM_VERSION, sizeof(vbuff), vbuff, NULL); - if (status == CL_SUCCESS) - applog(LOG_INFO, "CL Platform version: %s", vbuff); - - status = clGetDeviceIDs(platform, CL_DEVICE_TYPE_GPU, 0, NULL, &numDevices); - if (status != CL_SUCCESS) { - applog(LOG_ERR, "Error %d: Getting Device IDs (num)", status); - return NULL; - } - - if (numDevices > 0 ) { - devices = (cl_device_id *)malloc(numDevices*sizeof(cl_device_id)); - - /* Now, get the device list data */ - - status = clGetDeviceIDs(platform, CL_DEVICE_TYPE_GPU, numDevices, devices, NULL); - if (status != CL_SUCCESS) { - applog(LOG_ERR, "Error %d: Getting Device IDs (list)", status); - return NULL; - } - - applog(LOG_INFO, "List of devices:"); - - unsigned int i; - for (i = 0; i < numDevices; i++) { - status = clGetDeviceInfo(devices[i], CL_DEVICE_NAME, sizeof(pbuff), pbuff, NULL); - if (status != CL_SUCCESS) { - applog(LOG_ERR, "Error %d: Getting Device Info", status); - return NULL; - } - - applog(LOG_INFO, "\t%i\t%s", i, pbuff); - } - - if (gpu < numDevices) { - status = clGetDeviceInfo(devices[gpu], CL_DEVICE_NAME, sizeof(pbuff), pbuff, NULL); - if (status != CL_SUCCESS) { - applog(LOG_ERR, "Error %d: Getting Device Info", status); - return NULL; - } - - applog(LOG_INFO, "Selected %i: %s", gpu, pbuff); - strncpy(name, pbuff, nameSize); - } else { - applog(LOG_ERR, "Invalid GPU %i", gpu); - return NULL; - } - - } else return NULL; - - cl_context_properties cps[3] = { CL_CONTEXT_PLATFORM, (cl_context_properties)platform, 0 }; - - clState->context = clCreateContextFromType(cps, CL_DEVICE_TYPE_GPU, NULL, NULL, &status); - if (status != CL_SUCCESS) { - applog(LOG_ERR, "Error %d: Creating Context. (clCreateContextFromType)", status); - return NULL; - } - - ///////////////////////////////////////////////////////////////// - // Create an OpenCL command queue - ///////////////////////////////////////////////////////////////// - clState->commandQueue = clCreateCommandQueue(clState->context, devices[gpu], - CL_QUEUE_OUT_OF_ORDER_EXEC_MODE_ENABLE, &status); - if (status != CL_SUCCESS) /* Try again without OOE enable */ - clState->commandQueue = clCreateCommandQueue(clState->context, devices[gpu], 0 , &status); - if (status != CL_SUCCESS) { - applog(LOG_ERR, "Error %d: Creating Command Queue. (clCreateCommandQueue)", status); - return NULL; - } - - /* Check for BFI INT support. Hopefully people don't mix devices with - * and without it! */ - char * extensions = malloc(1024); - const char * camo = "cl_amd_media_ops"; - char *find; - - status = clGetDeviceInfo(devices[gpu], CL_DEVICE_EXTENSIONS, 1024, (void *)extensions, NULL); - if (status != CL_SUCCESS) { - applog(LOG_ERR, "Error %d: Failed to clGetDeviceInfo when trying to get CL_DEVICE_EXTENSIONS", status); - return NULL; - } - find = strstr(extensions, camo); - if (find) - clState->hasBitAlign = true; - - /* Check for OpenCL >= 1.0 support, needed for global offset parameter usage. */ - char * devoclver = malloc(1024); - const char * ocl10 = "OpenCL 1.0"; - - status = clGetDeviceInfo(devices[gpu], CL_DEVICE_VERSION, 1024, (void *)devoclver, NULL); - if (status != CL_SUCCESS) { - applog(LOG_ERR, "Error %d: Failed to clGetDeviceInfo when trying to get CL_DEVICE_VERSION", status); - return NULL; - } - find = strstr(devoclver, ocl10); - if (!find) - clState->hasOpenCL11plus = true; - - status = clGetDeviceInfo(devices[gpu], CL_DEVICE_PREFERRED_VECTOR_WIDTH_INT, sizeof(cl_uint), (void *)&preferred_vwidth, NULL); - if (status != CL_SUCCESS) { - applog(LOG_ERR, "Error %d: Failed to clGetDeviceInfo when trying to get CL_DEVICE_PREFERRED_VECTOR_WIDTH_INT", status); - return NULL; - } - applog(LOG_DEBUG, "Preferred vector width reported %d", preferred_vwidth); - - status = clGetDeviceInfo(devices[gpu], CL_DEVICE_MAX_WORK_GROUP_SIZE, sizeof(size_t), (void *)&clState->max_work_size, NULL); - if (status != CL_SUCCESS) { - applog(LOG_ERR, "Error %d: Failed to clGetDeviceInfo when trying to get CL_DEVICE_MAX_WORK_GROUP_SIZE", status); - return NULL; - } - applog(LOG_DEBUG, "Max work group size reported %d", (int)(clState->max_work_size)); - - status = clGetDeviceInfo(devices[gpu], CL_DEVICE_MAX_MEM_ALLOC_SIZE , sizeof(cl_ulong), (void *)&cgpu->max_alloc, NULL); - if (status != CL_SUCCESS) { - applog(LOG_ERR, "Error %d: Failed to clGetDeviceInfo when trying to get CL_DEVICE_MAX_MEM_ALLOC_SIZE", status); - return NULL; - } - applog(LOG_DEBUG, "Max mem alloc size is %lu", (long unsigned int)(cgpu->max_alloc)); - - /* Create binary filename based on parameters passed to opencl - * compiler to ensure we only load a binary that matches what would - * have otherwise created. The filename is: - * name + kernelname +/- g(offset) + v + vectors + w + work_size + l + sizeof(long) + .bin - * For scrypt the filename is: - * name + kernelname + g + lg + lookup_gap + tc + thread_concurrency + w + work_size + l + sizeof(long) + .bin - */ - char binaryfilename[255]; - char filename[255]; - char numbuf[16]; - - if (cgpu->kernel == KL_NONE) { - if (opt_scrypt) { - applog(LOG_INFO, "Selecting scrypt kernel"); - clState->chosen_kernel = KL_SCRYPT; - } else if (!strstr(name, "Tahiti") && - /* Detect all 2.6 SDKs not with Tahiti and use diablo kernel */ - (strstr(vbuff, "844.4") || // Linux 64 bit ATI 2.6 SDK - strstr(vbuff, "851.4") || // Windows 64 bit "" - strstr(vbuff, "831.4") || - strstr(vbuff, "898.1") || // 12.2 driver SDK - strstr(vbuff, "923.1") || // 12.4 - strstr(vbuff, "938.2") || // SDK 2.7 - strstr(vbuff, "1113.2"))) {// SDK 2.8 - applog(LOG_INFO, "Selecting diablo kernel"); - clState->chosen_kernel = KL_DIABLO; - /* Detect all 7970s, older ATI and NVIDIA and use poclbm */ - } else if (strstr(name, "Tahiti") || !clState->hasBitAlign) { - applog(LOG_INFO, "Selecting poclbm kernel"); - clState->chosen_kernel = KL_POCLBM; - /* Use phatk for the rest R5xxx R6xxx */ - } else { - applog(LOG_INFO, "Selecting phatk kernel"); - clState->chosen_kernel = KL_PHATK; - } - cgpu->kernel = clState->chosen_kernel; - } else { - clState->chosen_kernel = cgpu->kernel; - if (clState->chosen_kernel == KL_PHATK && - (strstr(vbuff, "844.4") || strstr(vbuff, "851.4") || - strstr(vbuff, "831.4") || strstr(vbuff, "898.1") || - strstr(vbuff, "923.1") || strstr(vbuff, "938.2") || - strstr(vbuff, "1113.2"))) { - applog(LOG_WARNING, "WARNING: You have selected the phatk kernel."); - applog(LOG_WARNING, "You are running SDK 2.6+ which performs poorly with this kernel."); - applog(LOG_WARNING, "Downgrade your SDK and delete any .bin files before starting again."); - applog(LOG_WARNING, "Or allow cgminer to automatically choose a more suitable kernel."); - } - } - - /* For some reason 2 vectors is still better even if the card says - * otherwise, and many cards lie about their max so use 256 as max - * unless explicitly set on the command line. Tahiti prefers 1 */ - if (strstr(name, "Tahiti")) - preferred_vwidth = 1; - else if (preferred_vwidth > 2) - preferred_vwidth = 2; - - switch (clState->chosen_kernel) { - case KL_POCLBM: - strcpy(filename, POCLBM_KERNNAME".cl"); - strcpy(binaryfilename, POCLBM_KERNNAME); - break; - case KL_PHATK: - strcpy(filename, PHATK_KERNNAME".cl"); - strcpy(binaryfilename, PHATK_KERNNAME); - break; - case KL_DIAKGCN: - strcpy(filename, DIAKGCN_KERNNAME".cl"); - strcpy(binaryfilename, DIAKGCN_KERNNAME); - break; - case KL_SCRYPT: - strcpy(filename, SCRYPT_KERNNAME".cl"); - strcpy(binaryfilename, SCRYPT_KERNNAME); - /* Scrypt only supports vector 1 */ - cgpu->vwidth = 1; - break; - case KL_NONE: /* Shouldn't happen */ - case KL_DIABLO: - strcpy(filename, DIABLO_KERNNAME".cl"); - strcpy(binaryfilename, DIABLO_KERNNAME); - break; - } - - if (cgpu->vwidth) - clState->vwidth = cgpu->vwidth; - else { - clState->vwidth = preferred_vwidth; - cgpu->vwidth = preferred_vwidth; - } - - if (((clState->chosen_kernel == KL_POCLBM || clState->chosen_kernel == KL_DIABLO || clState->chosen_kernel == KL_DIAKGCN) && - clState->vwidth == 1 && clState->hasOpenCL11plus) || opt_scrypt) - clState->goffset = true; - - if (cgpu->work_size && cgpu->work_size <= clState->max_work_size) - clState->wsize = cgpu->work_size; - else if (opt_scrypt) - clState->wsize = 256; - else if (strstr(name, "Tahiti")) - clState->wsize = 64; - else - clState->wsize = (clState->max_work_size <= 256 ? clState->max_work_size : 256) / clState->vwidth; - cgpu->work_size = clState->wsize; - -#ifdef USE_SCRYPT - if (opt_scrypt) { - if (!cgpu->opt_lg) { - applog(LOG_DEBUG, "GPU %d: selecting lookup gap of 2", gpu); - cgpu->lookup_gap = 2; - } else - cgpu->lookup_gap = cgpu->opt_lg; - - if (!cgpu->opt_tc) { - unsigned int sixtyfours; - - sixtyfours = cgpu->max_alloc / 131072 / 64 - 1; - cgpu->thread_concurrency = sixtyfours * 64; - if (cgpu->shaders && cgpu->thread_concurrency > cgpu->shaders) { - cgpu->thread_concurrency -= cgpu->thread_concurrency % cgpu->shaders; - if (cgpu->thread_concurrency > cgpu->shaders * 5) - cgpu->thread_concurrency = cgpu->shaders * 5; - } - applog(LOG_DEBUG, "GPU %d: selecting thread concurrency of %d", gpu, (int)(cgpu->thread_concurrency)); - } else - cgpu->thread_concurrency = cgpu->opt_tc; - } -#endif - - FILE *binaryfile; - size_t *binary_sizes; - char **binaries; - int pl; - char *source = file_contents(filename, &pl); - size_t sourceSize[] = {(size_t)pl}; - cl_uint slot, cpnd; - - slot = cpnd = 0; - - if (!source) - return NULL; - - binary_sizes = calloc(sizeof(size_t) * MAX_GPUDEVICES * 4, 1); - if (unlikely(!binary_sizes)) { - applog(LOG_ERR, "Unable to calloc binary_sizes"); - return NULL; - } - binaries = calloc(sizeof(char *) * MAX_GPUDEVICES * 4, 1); - if (unlikely(!binaries)) { - applog(LOG_ERR, "Unable to calloc binaries"); - return NULL; - } - - strcat(binaryfilename, name); - if (clState->goffset) - strcat(binaryfilename, "g"); - if (opt_scrypt) { -#ifdef USE_SCRYPT - sprintf(numbuf, "lg%utc%u", cgpu->lookup_gap, (unsigned int)cgpu->thread_concurrency); - strcat(binaryfilename, numbuf); -#endif - } else { - sprintf(numbuf, "v%d", clState->vwidth); - strcat(binaryfilename, numbuf); - } - sprintf(numbuf, "w%d", (int)clState->wsize); - strcat(binaryfilename, numbuf); - sprintf(numbuf, "l%d", (int)sizeof(long)); - strcat(binaryfilename, numbuf); - strcat(binaryfilename, ".bin"); - - binaryfile = fopen(binaryfilename, "rb"); - if (!binaryfile) { - applog(LOG_DEBUG, "No binary found, generating from source"); - } else { - struct stat binary_stat; - - if (unlikely(stat(binaryfilename, &binary_stat))) { - applog(LOG_DEBUG, "Unable to stat binary, generating from source"); - fclose(binaryfile); - goto build; - } - if (!binary_stat.st_size) - goto build; - - binary_sizes[slot] = binary_stat.st_size; - binaries[slot] = (char *)calloc(binary_sizes[slot], 1); - if (unlikely(!binaries[slot])) { - applog(LOG_ERR, "Unable to calloc binaries"); - fclose(binaryfile); - return NULL; - } - - if (fread(binaries[slot], 1, binary_sizes[slot], binaryfile) != binary_sizes[slot]) { - applog(LOG_ERR, "Unable to fread binaries"); - fclose(binaryfile); - free(binaries[slot]); - goto build; - } - - clState->program = clCreateProgramWithBinary(clState->context, 1, &devices[gpu], &binary_sizes[slot], (const unsigned char **)binaries, &status, NULL); - if (status != CL_SUCCESS) { - applog(LOG_ERR, "Error %d: Loading Binary into cl_program (clCreateProgramWithBinary)", status); - fclose(binaryfile); - free(binaries[slot]); - goto build; - } - - fclose(binaryfile); - applog(LOG_DEBUG, "Loaded binary image %s", binaryfilename); - - goto built; - } - - ///////////////////////////////////////////////////////////////// - // Load CL file, build CL program object, create CL kernel object - ///////////////////////////////////////////////////////////////// - -build: - clState->program = clCreateProgramWithSource(clState->context, 1, (const char **)&source, sourceSize, &status); - if (status != CL_SUCCESS) { - applog(LOG_ERR, "Error %d: Loading Binary into cl_program (clCreateProgramWithSource)", status); - return NULL; - } - - /* create a cl program executable for all the devices specified */ - char *CompilerOptions = calloc(1, 256); - -#ifdef USE_SCRYPT - if (opt_scrypt) - sprintf(CompilerOptions, "-D LOOKUP_GAP=%d -D CONCURRENT_THREADS=%d -D WORKSIZE=%d", - cgpu->lookup_gap, (unsigned int)cgpu->thread_concurrency, (int)clState->wsize); - else -#endif - { - sprintf(CompilerOptions, "-D WORKSIZE=%d -D VECTORS%d -D WORKVEC=%d", - (int)clState->wsize, clState->vwidth, (int)clState->wsize * clState->vwidth); - } - applog(LOG_DEBUG, "Setting worksize to %d", (int)(clState->wsize)); - if (clState->vwidth > 1) - applog(LOG_DEBUG, "Patched source to suit %d vectors", clState->vwidth); - - if (clState->hasBitAlign) { - strcat(CompilerOptions, " -D BITALIGN"); - applog(LOG_DEBUG, "cl_amd_media_ops found, setting BITALIGN"); - if (strstr(name, "Cedar") || - strstr(name, "Redwood") || - strstr(name, "Juniper") || - strstr(name, "Cypress" ) || - strstr(name, "Hemlock" ) || - strstr(name, "Caicos" ) || - strstr(name, "Turks" ) || - strstr(name, "Barts" ) || - strstr(name, "Cayman" ) || - strstr(name, "Antilles" ) || - strstr(name, "Wrestler" ) || - strstr(name, "Zacate" ) || - strstr(name, "WinterPark" )) - patchbfi = true; - } else - applog(LOG_DEBUG, "cl_amd_media_ops not found, will not set BITALIGN"); - - if (patchbfi) { - strcat(CompilerOptions, " -D BFI_INT"); - applog(LOG_DEBUG, "BFI_INT patch requiring device found, patched source with BFI_INT"); - } else - applog(LOG_DEBUG, "BFI_INT patch requiring device not found, will not BFI_INT patch"); - - if (clState->goffset) - strcat(CompilerOptions, " -D GOFFSET"); - - if (!clState->hasOpenCL11plus) - strcat(CompilerOptions, " -D OCL1"); - - applog(LOG_DEBUG, "CompilerOptions: %s", CompilerOptions); - status = clBuildProgram(clState->program, 1, &devices[gpu], CompilerOptions , NULL, NULL); - free(CompilerOptions); - - if (status != CL_SUCCESS) { - applog(LOG_ERR, "Error %d: Building Program (clBuildProgram)", status); - size_t logSize; - status = clGetProgramBuildInfo(clState->program, devices[gpu], CL_PROGRAM_BUILD_LOG, 0, NULL, &logSize); - - char *log = malloc(logSize); - status = clGetProgramBuildInfo(clState->program, devices[gpu], CL_PROGRAM_BUILD_LOG, logSize, log, NULL); - applog(LOG_ERR, "%s", log); - return NULL; - } - - prog_built = true; - -#ifdef __APPLE__ - /* OSX OpenCL breaks reading off binaries with >1 GPU so always build - * from source. */ - goto built; -#endif - - status = clGetProgramInfo(clState->program, CL_PROGRAM_NUM_DEVICES, sizeof(cl_uint), &cpnd, NULL); - if (unlikely(status != CL_SUCCESS)) { - applog(LOG_ERR, "Error %d: Getting program info CL_PROGRAM_NUM_DEVICES. (clGetProgramInfo)", status); - return NULL; - } - - status = clGetProgramInfo(clState->program, CL_PROGRAM_BINARY_SIZES, sizeof(size_t)*cpnd, binary_sizes, NULL); - if (unlikely(status != CL_SUCCESS)) { - applog(LOG_ERR, "Error %d: Getting program info CL_PROGRAM_BINARY_SIZES. (clGetProgramInfo)", status); - return NULL; - } - - /* The actual compiled binary ends up in a RANDOM slot! Grr, so we have - * to iterate over all the binary slots and find where the real program - * is. What the heck is this!? */ - for (slot = 0; slot < cpnd; slot++) - if (binary_sizes[slot]) - break; - - /* copy over all of the generated binaries. */ - applog(LOG_DEBUG, "Binary size for gpu %d found in binary slot %d: %d", gpu, slot, (int)(binary_sizes[slot])); - if (!binary_sizes[slot]) { - applog(LOG_ERR, "OpenCL compiler generated a zero sized binary, FAIL!"); - return NULL; - } - binaries[slot] = calloc(sizeof(char) * binary_sizes[slot], 1); - status = clGetProgramInfo(clState->program, CL_PROGRAM_BINARIES, sizeof(char *) * cpnd, binaries, NULL ); - if (unlikely(status != CL_SUCCESS)) { - applog(LOG_ERR, "Error %d: Getting program info. CL_PROGRAM_BINARIES (clGetProgramInfo)", status); - return NULL; - } - - /* Patch the kernel if the hardware supports BFI_INT but it needs to - * be hacked in */ - if (patchbfi) { - unsigned remaining = binary_sizes[slot]; - char *w = binaries[slot]; - unsigned int start, length; - - /* Find 2nd incidence of .text, and copy the program's - * position and length at a fixed offset from that. Then go - * back and find the 2nd incidence of \x7ELF (rewind by one - * from ELF) and then patch the opcocdes */ - if (!advance(&w, &remaining, ".text")) - goto build; - w++; remaining--; - if (!advance(&w, &remaining, ".text")) { - /* 32 bit builds only one ELF */ - w--; remaining++; - } - memcpy(&start, w + 285, 4); - memcpy(&length, w + 289, 4); - w = binaries[slot]; remaining = binary_sizes[slot]; - if (!advance(&w, &remaining, "ELF")) - goto build; - w++; remaining--; - if (!advance(&w, &remaining, "ELF")) { - /* 32 bit builds only one ELF */ - w--; remaining++; - } - w--; remaining++; - w += start; remaining -= start; - applog(LOG_DEBUG, "At %p (%u rem. bytes), to begin patching", - w, remaining); - patch_opcodes(w, length); - - status = clReleaseProgram(clState->program); - if (status != CL_SUCCESS) { - applog(LOG_ERR, "Error %d: Releasing program. (clReleaseProgram)", status); - return NULL; - } - - clState->program = clCreateProgramWithBinary(clState->context, 1, &devices[gpu], &binary_sizes[slot], (const unsigned char **)&binaries[slot], &status, NULL); - if (status != CL_SUCCESS) { - applog(LOG_ERR, "Error %d: Loading Binary into cl_program (clCreateProgramWithBinary)", status); - return NULL; - } - - /* Program needs to be rebuilt */ - prog_built = false; - } - - free(source); - - /* Save the binary to be loaded next time */ - binaryfile = fopen(binaryfilename, "wb"); - if (!binaryfile) { - /* Not a fatal problem, just means we build it again next time */ - applog(LOG_DEBUG, "Unable to create file %s", binaryfilename); - } else { - if (unlikely(fwrite(binaries[slot], 1, binary_sizes[slot], binaryfile) != binary_sizes[slot])) { - applog(LOG_ERR, "Unable to fwrite to binaryfile"); - return NULL; - } - fclose(binaryfile); - } -built: - if (binaries[slot]) - free(binaries[slot]); - free(binaries); - free(binary_sizes); - - applog(LOG_INFO, "Initialising kernel %s with%s bitalign, %d vectors and worksize %d", - filename, clState->hasBitAlign ? "" : "out", clState->vwidth, (int)(clState->wsize)); - - if (!prog_built) { - /* create a cl program executable for all the devices specified */ - status = clBuildProgram(clState->program, 1, &devices[gpu], NULL, NULL, NULL); - if (status != CL_SUCCESS) { - applog(LOG_ERR, "Error %d: Building Program (clBuildProgram)", status); - size_t logSize; - status = clGetProgramBuildInfo(clState->program, devices[gpu], CL_PROGRAM_BUILD_LOG, 0, NULL, &logSize); - - char *log = malloc(logSize); - status = clGetProgramBuildInfo(clState->program, devices[gpu], CL_PROGRAM_BUILD_LOG, logSize, log, NULL); - applog(LOG_ERR, "%s", log); - return NULL; - } - } - - /* get a kernel object handle for a kernel with the given name */ - clState->kernel = clCreateKernel(clState->program, "search", &status); - if (status != CL_SUCCESS) { - applog(LOG_ERR, "Error %d: Creating Kernel from program. (clCreateKernel)", status); - return NULL; - } - -#ifdef USE_SCRYPT - if (opt_scrypt) { - size_t ipt = (1024 / cgpu->lookup_gap + (1024 % cgpu->lookup_gap > 0)); - size_t bufsize = 128 * ipt * cgpu->thread_concurrency; - - /* Use the max alloc value which has been rounded to a power of - * 2 greater >= required amount earlier */ - if (bufsize > cgpu->max_alloc) { - applog(LOG_WARNING, "Maximum buffer memory device %d supports says %lu", - gpu, (long unsigned int)(cgpu->max_alloc)); - applog(LOG_WARNING, "Your scrypt settings come to %d", (int)bufsize); - } - applog(LOG_DEBUG, "Creating scrypt buffer sized %d", (int)bufsize); - clState->padbufsize = bufsize; - - /* This buffer is weird and might work to some degree even if - * the create buffer call has apparently failed, so check if we - * get anything back before we call it a failure. */ - clState->padbuffer8 = NULL; - clState->padbuffer8 = clCreateBuffer(clState->context, CL_MEM_READ_WRITE, bufsize, NULL, &status); - if (status != CL_SUCCESS && !clState->padbuffer8) { - applog(LOG_ERR, "Error %d: clCreateBuffer (padbuffer8), decrease TC or increase LG", status); - return NULL; - } - - clState->CLbuffer0 = clCreateBuffer(clState->context, CL_MEM_READ_ONLY, 128, NULL, &status); - if (status != CL_SUCCESS) { - applog(LOG_ERR, "Error %d: clCreateBuffer (CLbuffer0)", status); - return NULL; - } - clState->outputBuffer = clCreateBuffer(clState->context, CL_MEM_WRITE_ONLY, SCRYPT_BUFFERSIZE, NULL, &status); - } else -#endif - clState->outputBuffer = clCreateBuffer(clState->context, CL_MEM_WRITE_ONLY, BUFFERSIZE, NULL, &status); - if (status != CL_SUCCESS) { - applog(LOG_ERR, "Error %d: clCreateBuffer (outputBuffer)", status); - return NULL; - } - - return clState; -} -#endif /* HAVE_OPENCL */ - diff --git a/ocl.h b/ocl.h deleted file mode 100644 index 984e7d62b0..0000000000 --- a/ocl.h +++ /dev/null @@ -1,41 +0,0 @@ -#ifndef __OCL_H__ -#define __OCL_H__ - -#include "config.h" - -#include -#ifdef HAVE_OPENCL -#ifdef __APPLE_CC__ -#include -#else -#include -#endif - -#include "miner.h" - -typedef struct { - cl_context context; - cl_kernel kernel; - cl_command_queue commandQueue; - cl_program program; - cl_mem outputBuffer; -#ifdef USE_SCRYPT - cl_mem CLbuffer0; - cl_mem padbuffer8; - size_t padbufsize; - void * cldata; -#endif - bool hasBitAlign; - bool hasOpenCL11plus; - bool goffset; - cl_uint vwidth; - size_t max_work_size; - size_t wsize; - enum cl_kernels chosen_kernel; -} _clState; - -extern char *file_contents(const char *filename, int *length); -extern int clDevicesNum(void); -extern _clState *initCl(unsigned int gpu, char *name, size_t nameSize); -#endif /* HAVE_OPENCL */ -#endif /* __OCL_H__ */ diff --git a/phatk121016.cl b/phatk121016.cl deleted file mode 100644 index 60f2870385..0000000000 --- a/phatk121016.cl +++ /dev/null @@ -1,417 +0,0 @@ -// This file is taken and modified from the public-domain poclbm project, and -// I have therefore decided to keep it public-domain. -// Modified version copyright 2011-2012 Con Kolivas - -#ifdef VECTORS4 - typedef uint4 u; -#elif defined VECTORS2 - typedef uint2 u; -#else - typedef uint u; -#endif - -__constant uint K[64] = { - 0x428a2f98, 0x71374491, 0xb5c0fbcf, 0xe9b5dba5, 0x3956c25b, 0x59f111f1, 0x923f82a4, 0xab1c5ed5, - 0xd807aa98, 0x12835b01, 0x243185be, 0x550c7dc3, 0x72be5d74, 0x80deb1fe, 0x9bdc06a7, 0xc19bf174, - 0xe49b69c1, 0xefbe4786, 0x0fc19dc6, 0x240ca1cc, 0x2de92c6f, 0x4a7484aa, 0x5cb0a9dc, 0x76f988da, - 0x983e5152, 0xa831c66d, 0xb00327c8, 0xbf597fc7, 0xc6e00bf3, 0xd5a79147, 0x06ca6351, 0x14292967, - 0x27b70a85, 0x2e1b2138, 0x4d2c6dfc, 0x53380d13, 0x650a7354, 0x766a0abb, 0x81c2c92e, 0x92722c85, - 0xa2bfe8a1, 0xa81a664b, 0xc24b8b70, 0xc76c51a3, 0xd192e819, 0xd6990624, 0xf40e3585, 0x106aa070, - 0x19a4c116, 0x1e376c08, 0x2748774c, 0x34b0bcb5, 0x391c0cb3, 0x4ed8aa4a, 0x5b9cca4f, 0x682e6ff3, - 0x748f82ee, 0x78a5636f, 0x84c87814, 0x8cc70208, 0x90befffa, 0xa4506ceb, 0xbef9a3f7, 0xc67178f2 -}; - -__constant uint ConstW[128] = { -0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x80000000U, 0x00000000, 0x00000000, 0x00000000, -0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000280U, -0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, -0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, -0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, -0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, -0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, -0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, - -0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, -0x80000000U, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000100U, -0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, -0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, -0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, -0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, -0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, -0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000 -}; - -__constant uint H[8] = { - 0x6a09e667, 0xbb67ae85, 0x3c6ef372, 0xa54ff53a, 0x510e527f, 0x9b05688c, 0x1f83d9ab, 0x5be0cd19 -}; - - -#ifdef BITALIGN - #pragma OPENCL EXTENSION cl_amd_media_ops : enable - #define rot(x, y) amd_bitalign(x, x, (uint)(32 - y)) - -// This part is not from the stock poclbm kernel. It's part of an optimization -// added in the Phoenix Miner. - -// Some AMD devices have Vals[0] BFI_INT opcode, which behaves exactly like the -// SHA-256 Ch function, but provides it in exactly one instruction. If -// detected, use it for Ch. Otherwise, construct Ch out of simpler logical -// primitives. - - #ifdef BFI_INT - // Well, slight problem... It turns out BFI_INT isn't actually exposed to - // OpenCL (or CAL IL for that matter) in any way. However, there is - // a similar instruction, BYTE_ALIGN_INT, which is exposed to OpenCL via - // amd_bytealign, takes the same inputs, and provides the same output. - // We can use that as a placeholder for BFI_INT and have the application - // patch it after compilation. - - // This is the BFI_INT function - #define Ch(x, y, z) amd_bytealign(x,y,z) - // Ma can also be implemented in terms of BFI_INT... - #define Ma(z, x, y) amd_bytealign(z^x,y,x) - #else // BFI_INT - // Later SDKs optimise this to BFI INT without patching and GCN - // actually fails if manually patched with BFI_INT - - #define Ch(x, y, z) bitselect((u)z, (u)y, (u)x) - #define Ma(x, y, z) bitselect((u)x, (u)y, (u)z ^ (u)x) - #define rotr(x, y) amd_bitalign((u)x, (u)x, (u)y) - #endif -#else // BITALIGN - #define Ch(x, y, z) (z ^ (x & (y ^ z))) - #define Ma(x, y, z) ((x & z) | (y & (x | z))) - #define rot(x, y) rotate((u)x, (u)y) - #define rotr(x, y) rotate((u)x, (u)(32-y)) -#endif - - - -//Various intermediate calculations for each SHA round -#define s0(n) (S0(Vals[(0 + 128 - (n)) % 8])) -#define S0(n) (rot(n, 30u)^rot(n, 19u)^rot(n,10u)) - -#define s1(n) (S1(Vals[(4 + 128 - (n)) % 8])) -#define S1(n) (rot(n, 26u)^rot(n, 21u)^rot(n, 7u)) - -#define ch(n) Ch(Vals[(4 + 128 - (n)) % 8],Vals[(5 + 128 - (n)) % 8],Vals[(6 + 128 - (n)) % 8]) -#define maj(n) Ma(Vals[(1 + 128 - (n)) % 8],Vals[(2 + 128 - (n)) % 8],Vals[(0 + 128 - (n)) % 8]) - -//t1 calc when W is already calculated -#define t1(n) K[(n) % 64] + Vals[(7 + 128 - (n)) % 8] + W[(n)] + s1(n) + ch(n) - -//t1 calc which calculates W -#define t1W(n) K[(n) % 64] + Vals[(7 + 128 - (n)) % 8] + W(n) + s1(n) + ch(n) - -//Used for constant W Values (the compiler optimizes out zeros) -#define t1C(n) (K[(n) % 64]+ ConstW[(n)]) + Vals[(7 + 128 - (n)) % 8] + s1(n) + ch(n) - -//t2 Calc -#define t2(n) maj(n) + s0(n) - -#define rotC(x,n) (x<> (32-n)) - -//W calculation used for SHA round -#define W(n) (W[n] = P4(n) + P3(n) + P2(n) + P1(n)) - - - -//Partial W calculations (used for the begining where only some values are nonzero) -#define P1(n) ((rot(W[(n)-2],15u)^rot(W[(n)-2],13u)^((W[(n)-2])>>10U))) -#define P2(n) ((rot(W[(n)-15],25u)^rot(W[(n)-15],14u)^((W[(n)-15])>>3U))) - - -#define p1(x) ((rot(x,15u)^rot(x,13u)^((x)>>10U))) -#define p2(x) ((rot(x,25u)^rot(x,14u)^((x)>>3U))) - - -#define P3(n) W[n-7] -#define P4(n) W[n-16] - - -//Partial Calcs for constant W values -#define P1C(n) ((rotC(ConstW[(n)-2],15)^rotC(ConstW[(n)-2],13)^((ConstW[(n)-2])>>10U))) -#define P2C(n) ((rotC(ConstW[(n)-15],25)^rotC(ConstW[(n)-15],14)^((ConstW[(n)-15])>>3U))) -#define P3C(x) ConstW[x-7] -#define P4C(x) ConstW[x-16] - -//SHA round with built in W calc -#define sharoundW(n) Barrier1(n); Vals[(3 + 128 - (n)) % 8] += t1W(n); Vals[(7 + 128 - (n)) % 8] = t1W(n) + t2(n); - -//SHA round without W calc -#define sharound(n) Barrier2(n); Vals[(3 + 128 - (n)) % 8] += t1(n); Vals[(7 + 128 - (n)) % 8] = t1(n) + t2(n); - -//SHA round for constant W values -#define sharoundC(n) Barrier3(n); Vals[(3 + 128 - (n)) % 8] += t1C(n); Vals[(7 + 128 - (n)) % 8] = t1C(n) + t2(n); - -//The compiler is stupid... I put this in there only to stop the compiler from (de)optimizing the order -#define Barrier1(n) t1 = t1C((n+1)) -#define Barrier2(n) t1 = t1C((n)) -#define Barrier3(n) t1 = t1C((n)) - -//#define WORKSIZE 256 -#define MAXBUFFERS (4095) - -__kernel - __attribute__((reqd_work_group_size(WORKSIZE, 1, 1))) -void search( const uint state0, const uint state1, const uint state2, const uint state3, - const uint state4, const uint state5, const uint state6, const uint state7, - const uint B1, const uint C1, const uint D1, - const uint F1, const uint G1, const uint H1, - const u base, - const uint W16, const uint W17, - const uint PreVal4, const uint PreVal0, - const uint PreW18, const uint PreW19, - const uint PreW31, const uint PreW32, - - volatile __global uint * output) -{ - - - u W[124]; - u Vals[8]; - -//Dummy Variable to prevent compiler from reordering between rounds - u t1; - - //Vals[0]=state0; - Vals[1]=B1; - Vals[2]=C1; - Vals[3]=D1; - //Vals[4]=PreVal4; - Vals[5]=F1; - Vals[6]=G1; - Vals[7]=H1; - - W[16] = W16; - W[17] = W17; - -#ifdef VECTORS4 - //Less dependencies to get both the local id and group id and then add them - W[3] = base + (uint)(get_local_id(0)) * 4u + (uint)(get_group_id(0)) * (WORKSIZE * 4u); - uint r = rot(W[3].x,25u)^rot(W[3].x,14u)^((W[3].x)>>3U); - //Since only the 2 LSB is opposite between the nonces, we can save an instruction by flipping the 4 bits in W18 rather than the 1 bit in W3 - W[18] = PreW18 + (u){r, r ^ 0x2004000U, r ^ 0x4008000U, r ^ 0x600C000U}; -#elif defined VECTORS2 - W[3] = base + (uint)(get_local_id(0)) * 2u + (uint)(get_group_id(0)) * (WORKSIZE * 2u); - uint r = rot(W[3].x,25u)^rot(W[3].x,14u)^((W[3].x)>>3U); - W[18] = PreW18 + (u){r, r ^ 0x2004000U}; -#else - W[3] = base + get_local_id(0) + get_group_id(0) * (WORKSIZE); - u r = rot(W[3],25u)^rot(W[3],14u)^((W[3])>>3U); - W[18] = PreW18 + r; -#endif - //the order of the W calcs and Rounds is like this because the compiler needs help finding how to order the instructions - - - - Vals[4] = PreVal4 + W[3]; - Vals[0] = PreVal0 + W[3]; - - sharoundC(4); - W[19] = PreW19 + W[3]; - sharoundC(5); - W[20] = P4C(20) + P1(20); - sharoundC(6); - W[21] = P1(21); - sharoundC(7); - W[22] = P3C(22) + P1(22); - sharoundC(8); - W[23] = W[16] + P1(23); - sharoundC(9); - W[24] = W[17] + P1(24); - sharoundC(10); - W[25] = P1(25) + P3(25); - W[26] = P1(26) + P3(26); - sharoundC(11); - W[27] = P1(27) + P3(27); - W[28] = P1(28) + P3(28); - sharoundC(12); - W[29] = P1(29) + P3(29); - sharoundC(13); - W[30] = P1(30) + P2C(30) + P3(30); - W[31] = PreW31 + (P1(31) + P3(31)); - sharoundC(14); - W[32] = PreW32 + (P1(32) + P3(32)); - sharoundC(15); - sharound(16); - sharound(17); - sharound(18); - sharound(19); - sharound(20); - sharound(21); - sharound(22); - sharound(23); - sharound(24); - sharound(25); - sharound(26); - sharound(27); - sharound(28); - sharound(29); - sharound(30); - sharound(31); - sharound(32); - sharoundW(33); - sharoundW(34); - sharoundW(35); - sharoundW(36); - sharoundW(37); - sharoundW(38); - sharoundW(39); - sharoundW(40); - sharoundW(41); - sharoundW(42); - sharoundW(43); - sharoundW(44); - sharoundW(45); - sharoundW(46); - sharoundW(47); - sharoundW(48); - sharoundW(49); - sharoundW(50); - sharoundW(51); - sharoundW(52); - sharoundW(53); - sharoundW(54); - sharoundW(55); - sharoundW(56); - sharoundW(57); - sharoundW(58); - sharoundW(59); - sharoundW(60); - sharoundW(61); - sharoundW(62); - sharoundW(63); - - W[64]=state0+Vals[0]; - W[65]=state1+Vals[1]; - W[66]=state2+Vals[2]; - W[67]=state3+Vals[3]; - W[68]=state4+Vals[4]; - W[69]=state5+Vals[5]; - W[70]=state6+Vals[6]; - W[71]=state7+Vals[7]; - - Vals[0]=H[0]; - Vals[1]=H[1]; - Vals[2]=H[2]; - Vals[3]=H[3]; - Vals[4]=H[4]; - Vals[5]=H[5]; - Vals[6]=H[6]; - Vals[7]=H[7]; - - //sharound(64 + 0); - const u Temp = (0xb0edbdd0U + K[0]) + W[64]; - Vals[7] = Temp + 0x08909ae5U; - Vals[3] = 0xa54ff53aU + Temp; - -#define P124(n) P2(n) + P1(n) + P4(n) - - - W[64 + 16] = + P2(64 + 16) + P4(64 + 16); - sharound(64 + 1); - W[64 + 17] = P1C(64 + 17) + P2(64 + 17) + P4(64 + 17); - sharound(64 + 2); - W[64 + 18] = P124(64 + 18); - sharound(64 + 3); - W[64 + 19] = P124(64 + 19); - sharound(64 + 4); - W[64 + 20] = P124(64 + 20); - sharound(64 + 5); - W[64 + 21] = P124(64 + 21); - sharound(64 + 6); - W[64 + 22] = P4(64 + 22) + P3C(64 + 22) + P2(64 + 22) + P1(64 + 22); - sharound(64 + 7); - W[64 + 23] = P4(64 + 23) + P3(64 + 23) + P2C(64 + 23) + P1(64 + 23); - sharoundC(64 + 8); - W[64 + 24] = P1(64 + 24) + P4C(64 + 24) + P3(64 + 24); - sharoundC(64 + 9); - W[64 + 25] = P3(64 + 25) + P1(64 + 25); - sharoundC(64 + 10); - W[64 + 26] = P3(64 + 26) + P1(64 + 26); - sharoundC(64 + 11); - W[64 + 27] = P3(64 + 27) + P1(64 + 27); - sharoundC(64 + 12); - W[64 + 28] = P3(64 + 28) + P1(64 + 28); - sharoundC(64 + 13); - W[64 + 29] = P1(64 + 29) + P3(64 + 29); - W[64 + 30] = P3(64 + 30) + P2C(64 + 30) + P1(64 + 30); - sharoundC(64 + 14); - W[64 + 31] = P4C(64 + 31) + P3(64 + 31) + P2(64 + 31) + P1(64 + 31); - sharoundC(64 + 15); - sharound(64 + 16); - sharound(64 + 17); - sharound(64 + 18); - sharound(64 + 19); - sharound(64 + 20); - sharound(64 + 21); - sharound(64 + 22); - sharound(64 + 23); - sharound(64 + 24); - sharound(64 + 25); - sharound(64 + 26); - sharound(64 + 27); - sharound(64 + 28); - sharound(64 + 29); - sharound(64 + 30); - sharound(64 + 31); - sharoundW(64 + 32); - sharoundW(64 + 33); - sharoundW(64 + 34); - sharoundW(64 + 35); - sharoundW(64 + 36); - sharoundW(64 + 37); - sharoundW(64 + 38); - sharoundW(64 + 39); - sharoundW(64 + 40); - sharoundW(64 + 41); - sharoundW(64 + 42); - sharoundW(64 + 43); - sharoundW(64 + 44); - sharoundW(64 + 45); - sharoundW(64 + 46); - sharoundW(64 + 47); - sharoundW(64 + 48); - sharoundW(64 + 49); - sharoundW(64 + 50); - sharoundW(64 + 51); - sharoundW(64 + 52); - sharoundW(64 + 53); - sharoundW(64 + 54); - sharoundW(64 + 55); - sharoundW(64 + 56); - sharoundW(64 + 57); - sharoundW(64 + 58); - - W[117] += W[108] + Vals[3] + Vals[7] + P2(124) + P1(124) + Ch((Vals[0] + Vals[4]) + (K[59] + W(59+64)) + s1(64+59)+ ch(59+64),Vals[1],Vals[2]) - - (-(K[60] + H[7]) - S1((Vals[0] + Vals[4]) + (K[59] + W(59+64)) + s1(64+59)+ ch(59+64))); - -#define FOUND (0x0F) -#define SETFOUND(Xnonce) output[output[FOUND]++] = Xnonce - -#ifdef VECTORS4 - bool result = W[117].x & W[117].y & W[117].z & W[117].w; - if (!result) { - if (!W[117].x) - SETFOUND(W[3].x); - if (!W[117].y) - SETFOUND(W[3].y); - if (!W[117].z) - SETFOUND(W[3].z); - if (!W[117].w) - SETFOUND(W[3].w); - } -#elif defined VECTORS2 - bool result = W[117].x & W[117].y; - if (!result) { - if (!W[117].x) - SETFOUND(W[3].x); - if (!W[117].y) - SETFOUND(W[3].y); - } -#else - if (!W[117]) - SETFOUND(W[3]); -#endif -} diff --git a/poclbm130302.cl b/poclbm130302.cl deleted file mode 100644 index 6f5fd014f0..0000000000 --- a/poclbm130302.cl +++ /dev/null @@ -1,1388 +0,0 @@ -// -ck modified kernel taken from Phoenix taken from poclbm, with aspects of -// phatk and others. -// Modified version copyright 2011-2012 Con Kolivas - -// This file is taken and modified from the public-domain poclbm project, and -// we have therefore decided to keep it public-domain in Phoenix. - -#ifdef VECTORS4 - typedef uint4 u; -#elif defined VECTORS2 - typedef uint2 u; -#else - typedef uint u; -#endif - -__constant uint K[87] = { - 0x428a2f98, 0x71374491, 0xb5c0fbcf, 0xe9b5dba5, 0x3956c25b, 0x59f111f1, 0x923f82a4, 0xab1c5ed5, - 0xd807aa98, 0x12835b01, 0x243185be, 0x550c7dc3, 0x72be5d74, 0x80deb1fe, 0x9bdc06a7, 0xc19bf174, - 0xe49b69c1, 0xefbe4786, 0x0fc19dc6, 0x240ca1cc, 0x2de92c6f, 0x4a7484aa, 0x5cb0a9dc, 0x76f988da, - 0x983e5152, 0xa831c66d, 0xb00327c8, 0xbf597fc7, 0xc6e00bf3, 0xd5a79147, 0x06ca6351, 0x14292967, - 0x27b70a85, 0x2e1b2138, 0x4d2c6dfc, 0x53380d13, 0x650a7354, 0x766a0abb, 0x81c2c92e, 0x92722c85, - 0xa2bfe8a1, 0xa81a664b, 0xc24b8b70, 0xc76c51a3, 0xd192e819, 0xd6990624, 0xf40e3585, 0x106aa070, - 0x19a4c116, 0x1e376c08, 0x2748774c, 0x34b0bcb5, 0x391c0cb3, 0x4ed8aa4a, 0x5b9cca4f, 0x682e6ff3, - 0x748f82ee, 0x78a5636f, 0x84c87814, 0x8cc70208, 0x90befffa, 0xa4506ceb, 0xbef9a3f7, 0xc67178f2, - - 0xc19bf3f4U, - 0x80000000U, - 0x00000280U, - 0x00a00055U, - 0xf377ed68U, - 0xa54ff53aU, - 0x08909ae5U, - 0x90bb1e3cU, - 0x9b05688cU, - 0xca0b3af3U, - 0x3c6ef372U, - 0xbb67ae85U, - 0x6a09e667U, - 0x50c6645bU, - 0x510e527fU, - 0x3ac42e24U, - 0x5807aa98U, - 0xc19bf274U, - 0x00a00000U, - 0x00000100U, - 0x11002000U, - 0x00400022U, - 0x136032edU -}; - -#define xc19bf3f4U K[64] -#define x80000000U K[65] -#define x00000280U K[66] -#define x00a00055U K[67] -#define xf377ed68U K[68] -#define xa54ff53aU K[69] -#define x08909ae5U K[70] -#define x90bb1e3cU K[71] -#define x9b05688cU K[72] -#define xca0b3af3U K[73] -#define x3c6ef372U K[74] -#define xbb67ae85U K[75] -#define x6a09e667U K[76] -#define x50c6645bU K[77] -#define x510e527fU K[78] -#define x3ac42e24U K[79] -#define x5807aa98U K[80] -#define xc19bf274U K[81] -#define x00a00000U K[82] -#define x00000100U K[83] -#define x11002000U K[84] -#define x00400022U K[85] -#define x136032edU K[86] - -// This part is not from the stock poclbm kernel. It's part of an optimization -// added in the Phoenix Miner. - -// Some AMD devices have a BFI_INT opcode, which behaves exactly like the -// SHA-256 ch function, but provides it in exactly one instruction. If -// detected, use it for ch. Otherwise, construct ch out of simpler logical -// primitives. - -#ifdef BITALIGN - #pragma OPENCL EXTENSION cl_amd_media_ops : enable - #define rotr(x, y) amd_bitalign((u)x, (u)x, (u)y) -#else - #define rotr(x, y) rotate((u)x, (u)(32 - y)) -#endif -#ifdef BFI_INT - // Well, slight problem... It turns out BFI_INT isn't actually exposed to - // OpenCL (or CAL IL for that matter) in any way. However, there is - // a similar instruction, BYTE_ALIGN_INT, which is exposed to OpenCL via - // amd_bytealign, takes the same inputs, and provides the same output. - // We can use that as a placeholder for BFI_INT and have the application - // patch it after compilation. - - // This is the BFI_INT function - #define ch(x, y, z) amd_bytealign(x, y, z) - - // Ma can also be implemented in terms of BFI_INT... - #define Ma(x, y, z) amd_bytealign( (z^x), (y), (x) ) - - // AMD's KernelAnalyzer throws errors compiling the kernel if we use - // amd_bytealign on constants with vectors enabled, so we use this to avoid - // problems. (this is used 4 times, and likely optimized out by the compiler.) - #define Ma2(x, y, z) bitselect((u)x, (u)y, (u)z ^ (u)x) -#else // BFI_INT - //GCN actually fails if manually patched with BFI_INT - - #define ch(x, y, z) bitselect((u)z, (u)y, (u)x) - #define Ma(x, y, z) bitselect((u)x, (u)y, (u)z ^ (u)x) - #define Ma2(x, y, z) Ma(x, y, z) -#endif - - -__kernel -__attribute__((vec_type_hint(u))) -__attribute__((reqd_work_group_size(WORKSIZE, 1, 1))) -void search(const uint state0, const uint state1, const uint state2, const uint state3, - const uint state4, const uint state5, const uint state6, const uint state7, - const uint b1, const uint c1, - const uint f1, const uint g1, const uint h1, -#ifndef GOFFSET - const u base, -#endif - const uint fw0, const uint fw1, const uint fw2, const uint fw3, const uint fw15, const uint fw01r, - const uint D1A, const uint C1addK5, const uint B1addK6, - const uint W16addK16, const uint W17addK17, - const uint PreVal4addT1, const uint Preval0, - volatile __global uint * output) -{ - u Vals[24]; - u *W = &Vals[8]; - -#ifdef GOFFSET - const u nonce = (uint)(get_global_id(0)); -#else - const u nonce = base + (uint)(get_global_id(0)); -#endif - -Vals[5]=Preval0; -Vals[5]+=nonce; - -Vals[0]=(rotr(Vals[5],6)^rotr(Vals[5],11)^rotr(Vals[5],25)); -Vals[0]+=ch(Vals[5],b1,c1); -Vals[0]+=D1A; - -Vals[2]=Vals[0]; -Vals[2]+=h1; - -Vals[1]=PreVal4addT1; -Vals[1]+=nonce; -Vals[0]+=(rotr(Vals[1],2)^rotr(Vals[1],13)^rotr(Vals[1],22)); - -Vals[6]=C1addK5; -Vals[6]+=(rotr(Vals[2],6)^rotr(Vals[2],11)^rotr(Vals[2],25)); -Vals[6]+=ch(Vals[2],Vals[5],b1); - -Vals[3]=Vals[6]; -Vals[3]+=g1; -Vals[0]+=Ma2(g1,Vals[1],f1); -Vals[6]+=(rotr(Vals[0],2)^rotr(Vals[0],13)^rotr(Vals[0],22)); -Vals[6]+=Ma2(f1,Vals[0],Vals[1]); - -Vals[7]=B1addK6; -Vals[7]+=(rotr(Vals[3],6)^rotr(Vals[3],11)^rotr(Vals[3],25)); -Vals[7]+=ch(Vals[3],Vals[2],Vals[5]); - -Vals[4]=Vals[7]; -Vals[4]+=f1; - -Vals[7]+=(rotr(Vals[6],2)^rotr(Vals[6],13)^rotr(Vals[6],22)); -Vals[7]+=Ma(Vals[1],Vals[6],Vals[0]); - -Vals[5]+=(rotr(Vals[4],6)^rotr(Vals[4],11)^rotr(Vals[4],25)); -Vals[5]+=ch(Vals[4],Vals[3],Vals[2]); -Vals[5]+=K[7]; -Vals[1]+=Vals[5]; -Vals[5]+=(rotr(Vals[7],2)^rotr(Vals[7],13)^rotr(Vals[7],22)); -Vals[5]+=Ma(Vals[0],Vals[7],Vals[6]); - -Vals[2]+=(rotr(Vals[1],6)^rotr(Vals[1],11)^rotr(Vals[1],25)); -Vals[2]+=ch(Vals[1],Vals[4],Vals[3]); -Vals[2]+=K[8]; -Vals[0]+=Vals[2]; -Vals[2]+=(rotr(Vals[5],2)^rotr(Vals[5],13)^rotr(Vals[5],22)); -Vals[2]+=Ma(Vals[6],Vals[5],Vals[7]); - -Vals[3]+=(rotr(Vals[0],6)^rotr(Vals[0],11)^rotr(Vals[0],25)); -Vals[3]+=ch(Vals[0],Vals[1],Vals[4]); -Vals[3]+=K[9]; -Vals[6]+=Vals[3]; -Vals[3]+=(rotr(Vals[2],2)^rotr(Vals[2],13)^rotr(Vals[2],22)); -Vals[3]+=Ma(Vals[7],Vals[2],Vals[5]); - -Vals[4]+=(rotr(Vals[6],6)^rotr(Vals[6],11)^rotr(Vals[6],25)); -Vals[4]+=ch(Vals[6],Vals[0],Vals[1]); -Vals[4]+=K[10]; -Vals[7]+=Vals[4]; -Vals[4]+=(rotr(Vals[3],2)^rotr(Vals[3],13)^rotr(Vals[3],22)); -Vals[4]+=Ma(Vals[5],Vals[3],Vals[2]); - -Vals[1]+=(rotr(Vals[7],6)^rotr(Vals[7],11)^rotr(Vals[7],25)); -Vals[1]+=ch(Vals[7],Vals[6],Vals[0]); -Vals[1]+=K[11]; -Vals[5]+=Vals[1]; -Vals[1]+=(rotr(Vals[4],2)^rotr(Vals[4],13)^rotr(Vals[4],22)); -Vals[1]+=Ma(Vals[2],Vals[4],Vals[3]); - -Vals[0]+=(rotr(Vals[5],6)^rotr(Vals[5],11)^rotr(Vals[5],25)); -Vals[0]+=ch(Vals[5],Vals[7],Vals[6]); -Vals[0]+=K[12]; -Vals[2]+=Vals[0]; -Vals[0]+=(rotr(Vals[1],2)^rotr(Vals[1],13)^rotr(Vals[1],22)); -Vals[0]+=Ma(Vals[3],Vals[1],Vals[4]); - -Vals[6]+=(rotr(Vals[2],6)^rotr(Vals[2],11)^rotr(Vals[2],25)); -Vals[6]+=ch(Vals[2],Vals[5],Vals[7]); -Vals[6]+=K[13]; -Vals[3]+=Vals[6]; -Vals[6]+=(rotr(Vals[0],2)^rotr(Vals[0],13)^rotr(Vals[0],22)); -Vals[6]+=Ma(Vals[4],Vals[0],Vals[1]); - -Vals[7]+=(rotr(Vals[3],6)^rotr(Vals[3],11)^rotr(Vals[3],25)); -Vals[7]+=ch(Vals[3],Vals[2],Vals[5]); -Vals[7]+=K[14]; -Vals[4]+=Vals[7]; -Vals[7]+=(rotr(Vals[6],2)^rotr(Vals[6],13)^rotr(Vals[6],22)); -Vals[7]+=Ma(Vals[1],Vals[6],Vals[0]); - -Vals[5]+=(rotr(Vals[4],6)^rotr(Vals[4],11)^rotr(Vals[4],25)); -Vals[5]+=ch(Vals[4],Vals[3],Vals[2]); -Vals[5]+=xc19bf3f4U; -Vals[1]+=Vals[5]; -Vals[5]+=(rotr(Vals[7],2)^rotr(Vals[7],13)^rotr(Vals[7],22)); -Vals[5]+=Ma(Vals[0],Vals[7],Vals[6]); - -Vals[2]+=(rotr(Vals[1],6)^rotr(Vals[1],11)^rotr(Vals[1],25)); -Vals[2]+=ch(Vals[1],Vals[4],Vals[3]); -Vals[2]+=W16addK16; -Vals[0]+=Vals[2]; -Vals[2]+=(rotr(Vals[5],2)^rotr(Vals[5],13)^rotr(Vals[5],22)); -Vals[2]+=Ma(Vals[6],Vals[5],Vals[7]); - -Vals[3]+=(rotr(Vals[0],6)^rotr(Vals[0],11)^rotr(Vals[0],25)); -Vals[3]+=ch(Vals[0],Vals[1],Vals[4]); -Vals[3]+=W17addK17; -Vals[6]+=Vals[3]; -Vals[3]+=(rotr(Vals[2],2)^rotr(Vals[2],13)^rotr(Vals[2],22)); -Vals[3]+=Ma(Vals[7],Vals[2],Vals[5]); - -W[2]=(rotr(nonce,7)^rotr(nonce,18)^(nonce>>3U)); -W[2]+=fw2; -Vals[4]+=W[2]; -Vals[4]+=(rotr(Vals[6],6)^rotr(Vals[6],11)^rotr(Vals[6],25)); -Vals[4]+=ch(Vals[6],Vals[0],Vals[1]); -Vals[4]+=K[18]; -Vals[7]+=Vals[4]; -Vals[4]+=(rotr(Vals[3],2)^rotr(Vals[3],13)^rotr(Vals[3],22)); -Vals[4]+=Ma(Vals[5],Vals[3],Vals[2]); - -W[3]=nonce; -W[3]+=fw3; -Vals[1]+=W[3]; -Vals[1]+=(rotr(Vals[7],6)^rotr(Vals[7],11)^rotr(Vals[7],25)); -Vals[1]+=ch(Vals[7],Vals[6],Vals[0]); -Vals[1]+=K[19]; -Vals[5]+=Vals[1]; -Vals[1]+=(rotr(Vals[4],2)^rotr(Vals[4],13)^rotr(Vals[4],22)); -Vals[1]+=Ma(Vals[2],Vals[4],Vals[3]); - -W[4]=(rotr(W[2],17)^rotr(W[2],19)^(W[2]>>10U)); -W[4]+=x80000000U; -Vals[0]+=W[4]; -Vals[0]+=(rotr(Vals[5],6)^rotr(Vals[5],11)^rotr(Vals[5],25)); -Vals[0]+=ch(Vals[5],Vals[7],Vals[6]); -Vals[0]+=K[20]; -Vals[2]+=Vals[0]; -Vals[0]+=(rotr(Vals[1],2)^rotr(Vals[1],13)^rotr(Vals[1],22)); -Vals[0]+=Ma(Vals[3],Vals[1],Vals[4]); - -W[5]=(rotr(W[3],17)^rotr(W[3],19)^(W[3]>>10U)); -Vals[6]+=W[5]; -Vals[6]+=(rotr(Vals[2],6)^rotr(Vals[2],11)^rotr(Vals[2],25)); -Vals[6]+=ch(Vals[2],Vals[5],Vals[7]); -Vals[6]+=K[21]; -Vals[3]+=Vals[6]; -Vals[6]+=(rotr(Vals[0],2)^rotr(Vals[0],13)^rotr(Vals[0],22)); -Vals[6]+=Ma(Vals[4],Vals[0],Vals[1]); - -W[6]=(rotr(W[4],17)^rotr(W[4],19)^(W[4]>>10U)); -W[6]+=x00000280U; -Vals[7]+=W[6]; -Vals[7]+=(rotr(Vals[3],6)^rotr(Vals[3],11)^rotr(Vals[3],25)); -Vals[7]+=ch(Vals[3],Vals[2],Vals[5]); -Vals[7]+=K[22]; -Vals[4]+=Vals[7]; -Vals[7]+=(rotr(Vals[6],2)^rotr(Vals[6],13)^rotr(Vals[6],22)); -Vals[7]+=Ma(Vals[1],Vals[6],Vals[0]); - -W[7]=(rotr(W[5],17)^rotr(W[5],19)^(W[5]>>10U)); -W[7]+=fw0; -Vals[5]+=W[7]; -Vals[5]+=(rotr(Vals[4],6)^rotr(Vals[4],11)^rotr(Vals[4],25)); -Vals[5]+=ch(Vals[4],Vals[3],Vals[2]); -Vals[5]+=K[23]; -Vals[1]+=Vals[5]; -Vals[5]+=(rotr(Vals[7],2)^rotr(Vals[7],13)^rotr(Vals[7],22)); -Vals[5]+=Ma(Vals[0],Vals[7],Vals[6]); - -W[8]=(rotr(W[6],17)^rotr(W[6],19)^(W[6]>>10U)); -W[8]+=fw1; -Vals[2]+=W[8]; -Vals[2]+=(rotr(Vals[1],6)^rotr(Vals[1],11)^rotr(Vals[1],25)); -Vals[2]+=ch(Vals[1],Vals[4],Vals[3]); -Vals[2]+=K[24]; -Vals[0]+=Vals[2]; -Vals[2]+=(rotr(Vals[5],2)^rotr(Vals[5],13)^rotr(Vals[5],22)); -Vals[2]+=Ma(Vals[6],Vals[5],Vals[7]); - -W[9]=W[2]; -W[9]+=(rotr(W[7],17)^rotr(W[7],19)^(W[7]>>10U)); -Vals[3]+=W[9]; -Vals[3]+=(rotr(Vals[0],6)^rotr(Vals[0],11)^rotr(Vals[0],25)); -Vals[3]+=ch(Vals[0],Vals[1],Vals[4]); -Vals[3]+=K[25]; -Vals[6]+=Vals[3]; -Vals[3]+=(rotr(Vals[2],2)^rotr(Vals[2],13)^rotr(Vals[2],22)); -Vals[3]+=Ma(Vals[7],Vals[2],Vals[5]); - -W[10]=W[3]; -W[10]+=(rotr(W[8],17)^rotr(W[8],19)^(W[8]>>10U)); -Vals[4]+=W[10]; -Vals[4]+=(rotr(Vals[6],6)^rotr(Vals[6],11)^rotr(Vals[6],25)); -Vals[4]+=ch(Vals[6],Vals[0],Vals[1]); -Vals[4]+=K[26]; -Vals[7]+=Vals[4]; -Vals[4]+=(rotr(Vals[3],2)^rotr(Vals[3],13)^rotr(Vals[3],22)); -Vals[4]+=Ma(Vals[5],Vals[3],Vals[2]); - -W[11]=W[4]; -W[11]+=(rotr(W[9],17)^rotr(W[9],19)^(W[9]>>10U)); -Vals[1]+=W[11]; -Vals[1]+=(rotr(Vals[7],6)^rotr(Vals[7],11)^rotr(Vals[7],25)); -Vals[1]+=ch(Vals[7],Vals[6],Vals[0]); -Vals[1]+=K[27]; -Vals[5]+=Vals[1]; -Vals[1]+=(rotr(Vals[4],2)^rotr(Vals[4],13)^rotr(Vals[4],22)); -Vals[1]+=Ma(Vals[2],Vals[4],Vals[3]); - -W[12]=W[5]; -W[12]+=(rotr(W[10],17)^rotr(W[10],19)^(W[10]>>10U)); -Vals[0]+=W[12]; -Vals[0]+=(rotr(Vals[5],6)^rotr(Vals[5],11)^rotr(Vals[5],25)); -Vals[0]+=ch(Vals[5],Vals[7],Vals[6]); -Vals[0]+=K[28]; -Vals[2]+=Vals[0]; -Vals[0]+=(rotr(Vals[1],2)^rotr(Vals[1],13)^rotr(Vals[1],22)); -Vals[0]+=Ma(Vals[3],Vals[1],Vals[4]); - -W[13]=W[6]; -W[13]+=(rotr(W[11],17)^rotr(W[11],19)^(W[11]>>10U)); -Vals[6]+=W[13]; -Vals[6]+=(rotr(Vals[2],6)^rotr(Vals[2],11)^rotr(Vals[2],25)); -Vals[6]+=ch(Vals[2],Vals[5],Vals[7]); -Vals[6]+=K[29]; -Vals[3]+=Vals[6]; -Vals[6]+=(rotr(Vals[0],2)^rotr(Vals[0],13)^rotr(Vals[0],22)); -Vals[6]+=Ma(Vals[4],Vals[0],Vals[1]); - -W[14]=x00a00055U; -W[14]+=W[7]; -W[14]+=(rotr(W[12],17)^rotr(W[12],19)^(W[12]>>10U)); -Vals[7]+=W[14]; -Vals[7]+=(rotr(Vals[3],6)^rotr(Vals[3],11)^rotr(Vals[3],25)); -Vals[7]+=ch(Vals[3],Vals[2],Vals[5]); -Vals[7]+=K[30]; -Vals[4]+=Vals[7]; -Vals[7]+=(rotr(Vals[6],2)^rotr(Vals[6],13)^rotr(Vals[6],22)); -Vals[7]+=Ma(Vals[1],Vals[6],Vals[0]); - -W[15]=fw15; -W[15]+=W[8]; -W[15]+=(rotr(W[13],17)^rotr(W[13],19)^(W[13]>>10U)); -Vals[5]+=W[15]; -Vals[5]+=(rotr(Vals[4],6)^rotr(Vals[4],11)^rotr(Vals[4],25)); -Vals[5]+=ch(Vals[4],Vals[3],Vals[2]); -Vals[5]+=K[31]; -Vals[1]+=Vals[5]; -Vals[5]+=(rotr(Vals[7],2)^rotr(Vals[7],13)^rotr(Vals[7],22)); -Vals[5]+=Ma(Vals[0],Vals[7],Vals[6]); - -W[0]=fw01r; -W[0]+=W[9]; -W[0]+=(rotr(W[14],17)^rotr(W[14],19)^(W[14]>>10U)); -Vals[2]+=W[0]; -Vals[2]+=(rotr(Vals[1],6)^rotr(Vals[1],11)^rotr(Vals[1],25)); -Vals[2]+=ch(Vals[1],Vals[4],Vals[3]); -Vals[2]+=K[32]; -Vals[0]+=Vals[2]; -Vals[2]+=(rotr(Vals[5],2)^rotr(Vals[5],13)^rotr(Vals[5],22)); -Vals[2]+=Ma(Vals[6],Vals[5],Vals[7]); - -W[1]=fw1; -W[1]+=(rotr(W[2],7)^rotr(W[2],18)^(W[2]>>3U)); -W[1]+=W[10]; -W[1]+=(rotr(W[15],17)^rotr(W[15],19)^(W[15]>>10U)); -Vals[3]+=W[1]; -Vals[3]+=(rotr(Vals[0],6)^rotr(Vals[0],11)^rotr(Vals[0],25)); -Vals[3]+=ch(Vals[0],Vals[1],Vals[4]); -Vals[3]+=K[33]; -Vals[6]+=Vals[3]; -Vals[3]+=(rotr(Vals[2],2)^rotr(Vals[2],13)^rotr(Vals[2],22)); -Vals[3]+=Ma(Vals[7],Vals[2],Vals[5]); - -W[2]+=(rotr(W[3],7)^rotr(W[3],18)^(W[3]>>3U)); -W[2]+=W[11]; -W[2]+=(rotr(W[0],17)^rotr(W[0],19)^(W[0]>>10U)); -Vals[4]+=W[2]; -Vals[4]+=(rotr(Vals[6],6)^rotr(Vals[6],11)^rotr(Vals[6],25)); -Vals[4]+=ch(Vals[6],Vals[0],Vals[1]); -Vals[4]+=K[34]; -Vals[7]+=Vals[4]; -Vals[4]+=(rotr(Vals[3],2)^rotr(Vals[3],13)^rotr(Vals[3],22)); -Vals[4]+=Ma(Vals[5],Vals[3],Vals[2]); - -W[3]+=(rotr(W[4],7)^rotr(W[4],18)^(W[4]>>3U)); -W[3]+=W[12]; -W[3]+=(rotr(W[1],17)^rotr(W[1],19)^(W[1]>>10U)); -Vals[1]+=W[3]; -Vals[1]+=(rotr(Vals[7],6)^rotr(Vals[7],11)^rotr(Vals[7],25)); -Vals[1]+=ch(Vals[7],Vals[6],Vals[0]); -Vals[1]+=K[35]; -Vals[5]+=Vals[1]; -Vals[1]+=(rotr(Vals[4],2)^rotr(Vals[4],13)^rotr(Vals[4],22)); -Vals[1]+=Ma(Vals[2],Vals[4],Vals[3]); - -W[4]+=(rotr(W[5],7)^rotr(W[5],18)^(W[5]>>3U)); -W[4]+=W[13]; -W[4]+=(rotr(W[2],17)^rotr(W[2],19)^(W[2]>>10U)); -Vals[0]+=W[4]; -Vals[0]+=(rotr(Vals[5],6)^rotr(Vals[5],11)^rotr(Vals[5],25)); -Vals[0]+=ch(Vals[5],Vals[7],Vals[6]); -Vals[0]+=K[36]; -Vals[2]+=Vals[0]; -Vals[0]+=(rotr(Vals[1],2)^rotr(Vals[1],13)^rotr(Vals[1],22)); -Vals[0]+=Ma(Vals[3],Vals[1],Vals[4]); - -W[5]+=(rotr(W[6],7)^rotr(W[6],18)^(W[6]>>3U)); -W[5]+=W[14]; -W[5]+=(rotr(W[3],17)^rotr(W[3],19)^(W[3]>>10U)); -Vals[6]+=W[5]; -Vals[6]+=(rotr(Vals[2],6)^rotr(Vals[2],11)^rotr(Vals[2],25)); -Vals[6]+=ch(Vals[2],Vals[5],Vals[7]); -Vals[6]+=K[37]; -Vals[3]+=Vals[6]; -Vals[6]+=(rotr(Vals[0],2)^rotr(Vals[0],13)^rotr(Vals[0],22)); -Vals[6]+=Ma(Vals[4],Vals[0],Vals[1]); - -W[6]+=(rotr(W[7],7)^rotr(W[7],18)^(W[7]>>3U)); -W[6]+=W[15]; -W[6]+=(rotr(W[4],17)^rotr(W[4],19)^(W[4]>>10U)); -Vals[7]+=W[6]; -Vals[7]+=(rotr(Vals[3],6)^rotr(Vals[3],11)^rotr(Vals[3],25)); -Vals[7]+=ch(Vals[3],Vals[2],Vals[5]); -Vals[7]+=K[38]; -Vals[4]+=Vals[7]; -Vals[7]+=(rotr(Vals[6],2)^rotr(Vals[6],13)^rotr(Vals[6],22)); -Vals[7]+=Ma(Vals[1],Vals[6],Vals[0]); - -W[7]+=(rotr(W[8],7)^rotr(W[8],18)^(W[8]>>3U)); -W[7]+=W[0]; -W[7]+=(rotr(W[5],17)^rotr(W[5],19)^(W[5]>>10U)); -Vals[5]+=W[7]; -Vals[5]+=(rotr(Vals[4],6)^rotr(Vals[4],11)^rotr(Vals[4],25)); -Vals[5]+=ch(Vals[4],Vals[3],Vals[2]); -Vals[5]+=K[39]; -Vals[1]+=Vals[5]; -Vals[5]+=(rotr(Vals[7],2)^rotr(Vals[7],13)^rotr(Vals[7],22)); -Vals[5]+=Ma(Vals[0],Vals[7],Vals[6]); - -W[8]+=(rotr(W[9],7)^rotr(W[9],18)^(W[9]>>3U)); -W[8]+=W[1]; -W[8]+=(rotr(W[6],17)^rotr(W[6],19)^(W[6]>>10U)); -Vals[2]+=W[8]; -Vals[2]+=(rotr(Vals[1],6)^rotr(Vals[1],11)^rotr(Vals[1],25)); -Vals[2]+=ch(Vals[1],Vals[4],Vals[3]); -Vals[2]+=K[40]; -Vals[0]+=Vals[2]; -Vals[2]+=(rotr(Vals[5],2)^rotr(Vals[5],13)^rotr(Vals[5],22)); -Vals[2]+=Ma(Vals[6],Vals[5],Vals[7]); - -W[9]+=(rotr(W[10],7)^rotr(W[10],18)^(W[10]>>3U)); -W[9]+=W[2]; -W[9]+=(rotr(W[7],17)^rotr(W[7],19)^(W[7]>>10U)); -Vals[3]+=W[9]; -Vals[3]+=(rotr(Vals[0],6)^rotr(Vals[0],11)^rotr(Vals[0],25)); -Vals[3]+=ch(Vals[0],Vals[1],Vals[4]); -Vals[3]+=K[41]; -Vals[6]+=Vals[3]; -Vals[3]+=(rotr(Vals[2],2)^rotr(Vals[2],13)^rotr(Vals[2],22)); -Vals[3]+=Ma(Vals[7],Vals[2],Vals[5]); - -W[10]+=(rotr(W[11],7)^rotr(W[11],18)^(W[11]>>3U)); -W[10]+=W[3]; -W[10]+=(rotr(W[8],17)^rotr(W[8],19)^(W[8]>>10U)); -Vals[4]+=W[10]; -Vals[4]+=(rotr(Vals[6],6)^rotr(Vals[6],11)^rotr(Vals[6],25)); -Vals[4]+=ch(Vals[6],Vals[0],Vals[1]); -Vals[4]+=K[42]; -Vals[7]+=Vals[4]; -Vals[4]+=(rotr(Vals[3],2)^rotr(Vals[3],13)^rotr(Vals[3],22)); -Vals[4]+=Ma(Vals[5],Vals[3],Vals[2]); - -W[11]+=(rotr(W[12],7)^rotr(W[12],18)^(W[12]>>3U)); -W[11]+=W[4]; -W[11]+=(rotr(W[9],17)^rotr(W[9],19)^(W[9]>>10U)); -Vals[1]+=W[11]; -Vals[1]+=(rotr(Vals[7],6)^rotr(Vals[7],11)^rotr(Vals[7],25)); -Vals[1]+=ch(Vals[7],Vals[6],Vals[0]); -Vals[1]+=K[43]; -Vals[5]+=Vals[1]; -Vals[1]+=(rotr(Vals[4],2)^rotr(Vals[4],13)^rotr(Vals[4],22)); -Vals[1]+=Ma(Vals[2],Vals[4],Vals[3]); - -W[12]+=(rotr(W[13],7)^rotr(W[13],18)^(W[13]>>3U)); -W[12]+=W[5]; -W[12]+=(rotr(W[10],17)^rotr(W[10],19)^(W[10]>>10U)); -Vals[0]+=W[12]; -Vals[0]+=(rotr(Vals[5],6)^rotr(Vals[5],11)^rotr(Vals[5],25)); -Vals[0]+=ch(Vals[5],Vals[7],Vals[6]); -Vals[0]+=K[44]; -Vals[2]+=Vals[0]; -Vals[0]+=(rotr(Vals[1],2)^rotr(Vals[1],13)^rotr(Vals[1],22)); -Vals[0]+=Ma(Vals[3],Vals[1],Vals[4]); - -W[13]+=(rotr(W[14],7)^rotr(W[14],18)^(W[14]>>3U)); -W[13]+=W[6]; -W[13]+=(rotr(W[11],17)^rotr(W[11],19)^(W[11]>>10U)); -Vals[6]+=W[13]; -Vals[6]+=(rotr(Vals[2],6)^rotr(Vals[2],11)^rotr(Vals[2],25)); -Vals[6]+=ch(Vals[2],Vals[5],Vals[7]); -Vals[6]+=K[45]; -Vals[3]+=Vals[6]; -Vals[6]+=(rotr(Vals[0],2)^rotr(Vals[0],13)^rotr(Vals[0],22)); -Vals[6]+=Ma(Vals[4],Vals[0],Vals[1]); - -W[14]+=(rotr(W[15],7)^rotr(W[15],18)^(W[15]>>3U)); -W[14]+=W[7]; -W[14]+=(rotr(W[12],17)^rotr(W[12],19)^(W[12]>>10U)); -Vals[7]+=W[14]; -Vals[7]+=(rotr(Vals[3],6)^rotr(Vals[3],11)^rotr(Vals[3],25)); -Vals[7]+=ch(Vals[3],Vals[2],Vals[5]); -Vals[7]+=K[46]; -Vals[4]+=Vals[7]; -Vals[7]+=(rotr(Vals[6],2)^rotr(Vals[6],13)^rotr(Vals[6],22)); -Vals[7]+=Ma(Vals[1],Vals[6],Vals[0]); - -W[15]+=(rotr(W[0],7)^rotr(W[0],18)^(W[0]>>3U)); -W[15]+=W[8]; -W[15]+=(rotr(W[13],17)^rotr(W[13],19)^(W[13]>>10U)); -Vals[5]+=W[15]; -Vals[5]+=(rotr(Vals[4],6)^rotr(Vals[4],11)^rotr(Vals[4],25)); -Vals[5]+=ch(Vals[4],Vals[3],Vals[2]); -Vals[5]+=K[47]; -Vals[1]+=Vals[5]; -Vals[5]+=(rotr(Vals[7],2)^rotr(Vals[7],13)^rotr(Vals[7],22)); -Vals[5]+=Ma(Vals[0],Vals[7],Vals[6]); - -W[0]+=(rotr(W[1],7)^rotr(W[1],18)^(W[1]>>3U)); -W[0]+=W[9]; -W[0]+=(rotr(W[14],17)^rotr(W[14],19)^(W[14]>>10U)); -Vals[2]+=W[0]; -Vals[2]+=(rotr(Vals[1],6)^rotr(Vals[1],11)^rotr(Vals[1],25)); -Vals[2]+=ch(Vals[1],Vals[4],Vals[3]); -Vals[2]+=K[48]; -Vals[0]+=Vals[2]; -Vals[2]+=(rotr(Vals[5],2)^rotr(Vals[5],13)^rotr(Vals[5],22)); -Vals[2]+=Ma(Vals[6],Vals[5],Vals[7]); - -W[1]+=(rotr(W[2],7)^rotr(W[2],18)^(W[2]>>3U)); -W[1]+=W[10]; -W[1]+=(rotr(W[15],17)^rotr(W[15],19)^(W[15]>>10U)); -Vals[3]+=W[1]; -Vals[3]+=(rotr(Vals[0],6)^rotr(Vals[0],11)^rotr(Vals[0],25)); -Vals[3]+=ch(Vals[0],Vals[1],Vals[4]); -Vals[3]+=K[49]; -Vals[6]+=Vals[3]; -Vals[3]+=(rotr(Vals[2],2)^rotr(Vals[2],13)^rotr(Vals[2],22)); -Vals[3]+=Ma(Vals[7],Vals[2],Vals[5]); - -W[2]+=(rotr(W[3],7)^rotr(W[3],18)^(W[3]>>3U)); -W[2]+=W[11]; -W[2]+=(rotr(W[0],17)^rotr(W[0],19)^(W[0]>>10U)); -Vals[4]+=W[2]; -Vals[4]+=(rotr(Vals[6],6)^rotr(Vals[6],11)^rotr(Vals[6],25)); -Vals[4]+=ch(Vals[6],Vals[0],Vals[1]); -Vals[4]+=K[50]; -Vals[7]+=Vals[4]; -Vals[4]+=(rotr(Vals[3],2)^rotr(Vals[3],13)^rotr(Vals[3],22)); -Vals[4]+=Ma(Vals[5],Vals[3],Vals[2]); - -W[3]+=(rotr(W[4],7)^rotr(W[4],18)^(W[4]>>3U)); -W[3]+=W[12]; -W[3]+=(rotr(W[1],17)^rotr(W[1],19)^(W[1]>>10U)); -Vals[1]+=W[3]; -Vals[1]+=(rotr(Vals[7],6)^rotr(Vals[7],11)^rotr(Vals[7],25)); -Vals[1]+=ch(Vals[7],Vals[6],Vals[0]); -Vals[1]+=K[51]; -Vals[5]+=Vals[1]; -Vals[1]+=(rotr(Vals[4],2)^rotr(Vals[4],13)^rotr(Vals[4],22)); -Vals[1]+=Ma(Vals[2],Vals[4],Vals[3]); - -W[4]+=(rotr(W[5],7)^rotr(W[5],18)^(W[5]>>3U)); -W[4]+=W[13]; -W[4]+=(rotr(W[2],17)^rotr(W[2],19)^(W[2]>>10U)); -Vals[0]+=W[4]; -Vals[0]+=(rotr(Vals[5],6)^rotr(Vals[5],11)^rotr(Vals[5],25)); -Vals[0]+=ch(Vals[5],Vals[7],Vals[6]); -Vals[0]+=K[52]; -Vals[2]+=Vals[0]; -Vals[0]+=(rotr(Vals[1],2)^rotr(Vals[1],13)^rotr(Vals[1],22)); -Vals[0]+=Ma(Vals[3],Vals[1],Vals[4]); - -W[5]+=(rotr(W[6],7)^rotr(W[6],18)^(W[6]>>3U)); -W[5]+=W[14]; -W[5]+=(rotr(W[3],17)^rotr(W[3],19)^(W[3]>>10U)); -Vals[6]+=W[5]; -Vals[6]+=(rotr(Vals[2],6)^rotr(Vals[2],11)^rotr(Vals[2],25)); -Vals[6]+=ch(Vals[2],Vals[5],Vals[7]); -Vals[6]+=K[53]; -Vals[3]+=Vals[6]; -Vals[6]+=(rotr(Vals[0],2)^rotr(Vals[0],13)^rotr(Vals[0],22)); -Vals[6]+=Ma(Vals[4],Vals[0],Vals[1]); - -W[6]+=(rotr(W[7],7)^rotr(W[7],18)^(W[7]>>3U)); -W[6]+=W[15]; -W[6]+=(rotr(W[4],17)^rotr(W[4],19)^(W[4]>>10U)); -Vals[7]+=W[6]; -Vals[7]+=(rotr(Vals[3],6)^rotr(Vals[3],11)^rotr(Vals[3],25)); -Vals[7]+=ch(Vals[3],Vals[2],Vals[5]); -Vals[7]+=K[54]; -Vals[4]+=Vals[7]; -Vals[7]+=(rotr(Vals[6],2)^rotr(Vals[6],13)^rotr(Vals[6],22)); -Vals[7]+=Ma(Vals[1],Vals[6],Vals[0]); - -W[7]+=(rotr(W[8],7)^rotr(W[8],18)^(W[8]>>3U)); -W[7]+=W[0]; -W[7]+=(rotr(W[5],17)^rotr(W[5],19)^(W[5]>>10U)); -Vals[5]+=W[7]; -Vals[5]+=(rotr(Vals[4],6)^rotr(Vals[4],11)^rotr(Vals[4],25)); -Vals[5]+=ch(Vals[4],Vals[3],Vals[2]); -Vals[5]+=K[55]; -Vals[1]+=Vals[5]; -Vals[5]+=(rotr(Vals[7],2)^rotr(Vals[7],13)^rotr(Vals[7],22)); -Vals[5]+=Ma(Vals[0],Vals[7],Vals[6]); - -W[8]+=(rotr(W[9],7)^rotr(W[9],18)^(W[9]>>3U)); -W[8]+=W[1]; -W[8]+=(rotr(W[6],17)^rotr(W[6],19)^(W[6]>>10U)); -Vals[2]+=W[8]; -Vals[2]+=(rotr(Vals[1],6)^rotr(Vals[1],11)^rotr(Vals[1],25)); -Vals[2]+=ch(Vals[1],Vals[4],Vals[3]); -Vals[2]+=K[56]; -Vals[0]+=Vals[2]; -Vals[2]+=(rotr(Vals[5],2)^rotr(Vals[5],13)^rotr(Vals[5],22)); -Vals[2]+=Ma(Vals[6],Vals[5],Vals[7]); - -W[9]+=(rotr(W[10],7)^rotr(W[10],18)^(W[10]>>3U)); -W[9]+=W[2]; -W[9]+=(rotr(W[7],17)^rotr(W[7],19)^(W[7]>>10U)); -Vals[3]+=W[9]; -Vals[3]+=(rotr(Vals[0],6)^rotr(Vals[0],11)^rotr(Vals[0],25)); -Vals[3]+=ch(Vals[0],Vals[1],Vals[4]); -Vals[3]+=K[57]; -Vals[6]+=Vals[3]; -Vals[3]+=(rotr(Vals[2],2)^rotr(Vals[2],13)^rotr(Vals[2],22)); -Vals[3]+=Ma(Vals[7],Vals[2],Vals[5]); - -W[10]+=(rotr(W[11],7)^rotr(W[11],18)^(W[11]>>3U)); -W[10]+=W[3]; -W[10]+=(rotr(W[8],17)^rotr(W[8],19)^(W[8]>>10U)); -Vals[4]+=W[10]; -Vals[4]+=(rotr(Vals[6],6)^rotr(Vals[6],11)^rotr(Vals[6],25)); -Vals[4]+=ch(Vals[6],Vals[0],Vals[1]); -Vals[4]+=K[58]; -Vals[7]+=Vals[4]; -Vals[4]+=(rotr(Vals[3],2)^rotr(Vals[3],13)^rotr(Vals[3],22)); -Vals[4]+=Ma(Vals[5],Vals[3],Vals[2]); - -W[11]+=(rotr(W[12],7)^rotr(W[12],18)^(W[12]>>3U)); -W[11]+=W[4]; -W[11]+=(rotr(W[9],17)^rotr(W[9],19)^(W[9]>>10U)); -Vals[1]+=W[11]; -Vals[1]+=(rotr(Vals[7],6)^rotr(Vals[7],11)^rotr(Vals[7],25)); -Vals[1]+=ch(Vals[7],Vals[6],Vals[0]); -Vals[1]+=K[59]; -Vals[5]+=Vals[1]; -Vals[1]+=(rotr(Vals[4],2)^rotr(Vals[4],13)^rotr(Vals[4],22)); -Vals[1]+=Ma(Vals[2],Vals[4],Vals[3]); - -W[12]+=(rotr(W[13],7)^rotr(W[13],18)^(W[13]>>3U)); -W[12]+=W[5]; -W[12]+=(rotr(W[10],17)^rotr(W[10],19)^(W[10]>>10U)); -Vals[0]+=W[12]; -Vals[0]+=(rotr(Vals[5],6)^rotr(Vals[5],11)^rotr(Vals[5],25)); -Vals[0]+=ch(Vals[5],Vals[7],Vals[6]); -Vals[0]+=K[60]; -Vals[2]+=Vals[0]; -Vals[0]+=(rotr(Vals[1],2)^rotr(Vals[1],13)^rotr(Vals[1],22)); -Vals[0]+=Ma(Vals[3],Vals[1],Vals[4]); - -W[13]+=(rotr(W[14],7)^rotr(W[14],18)^(W[14]>>3U)); -W[13]+=W[6]; -W[13]+=(rotr(W[11],17)^rotr(W[11],19)^(W[11]>>10U)); -Vals[6]+=W[13]; -Vals[6]+=(rotr(Vals[2],6)^rotr(Vals[2],11)^rotr(Vals[2],25)); -Vals[6]+=ch(Vals[2],Vals[5],Vals[7]); -Vals[6]+=K[61]; -Vals[3]+=Vals[6]; -Vals[6]+=(rotr(Vals[0],2)^rotr(Vals[0],13)^rotr(Vals[0],22)); -Vals[6]+=Ma(Vals[4],Vals[0],Vals[1]); - -Vals[7]+=W[14]; -Vals[7]+=(rotr(W[15],7)^rotr(W[15],18)^(W[15]>>3U)); -Vals[7]+=W[7]; -Vals[7]+=(rotr(W[12],17)^rotr(W[12],19)^(W[12]>>10U)); -Vals[7]+=(rotr(Vals[3],6)^rotr(Vals[3],11)^rotr(Vals[3],25)); -Vals[7]+=ch(Vals[3],Vals[2],Vals[5]); -Vals[7]+=K[62]; -Vals[4]+=Vals[7]; -Vals[7]+=(rotr(Vals[6],2)^rotr(Vals[6],13)^rotr(Vals[6],22)); -Vals[7]+=Ma(Vals[1],Vals[6],Vals[0]); - -Vals[5]+=W[15]; -Vals[5]+=(rotr(W[0],7)^rotr(W[0],18)^(W[0]>>3U)); -Vals[5]+=W[8]; -Vals[5]+=(rotr(W[13],17)^rotr(W[13],19)^(W[13]>>10U)); -Vals[5]+=(rotr(Vals[4],6)^rotr(Vals[4],11)^rotr(Vals[4],25)); -Vals[5]+=ch(Vals[4],Vals[3],Vals[2]); -Vals[5]+=K[63]; -Vals[1]+=Vals[5]; -Vals[5]+=(rotr(Vals[7],2)^rotr(Vals[7],13)^rotr(Vals[7],22)); -Vals[5]+=Ma(Vals[0],Vals[7],Vals[6]); - -Vals[5]+=state0; - -W[7]=state7; -W[7]+=Vals[2]; - -Vals[2]=xf377ed68U; -Vals[2]+=Vals[5]; -W[0]=Vals[5]; -Vals[5]=x6a09e667U; - -W[3]=state3; -W[3]+=Vals[0]; - -Vals[0]=xa54ff53aU; -Vals[0]+=Vals[2]; -Vals[2]+=x08909ae5U; - -W[6]=state6; -W[6]+=Vals[3]; - -Vals[3]=x90bb1e3cU; -Vals[3]+=(rotr(Vals[0],6)^rotr(Vals[0],11)^rotr(Vals[0],25)); -Vals[3]+=(x9b05688cU^(Vals[0]&xca0b3af3U)); - -Vals[7]+=state1; -Vals[3]+=Vals[7]; -W[1]=Vals[7]; -Vals[7]=xbb67ae85U; - -W[2]=state2; -W[2]+=Vals[6]; - -Vals[6]=x3c6ef372U; -Vals[6]+=Vals[3]; -Vals[3]+=(rotr(Vals[2],2)^rotr(Vals[2],13)^rotr(Vals[2],22)); -Vals[3]+=Ma2(Vals[7],Vals[2],Vals[5]); - -W[5]=state5; -W[5]+=Vals[4]; - -Vals[4]=x50c6645bU; -Vals[4]+=(rotr(Vals[6],6)^rotr(Vals[6],11)^rotr(Vals[6],25)); -Vals[4]+=ch(Vals[6],Vals[0],x510e527fU); -Vals[4]+=W[2]; - -Vals[7]+=Vals[4]; -Vals[4]+=(rotr(Vals[3],2)^rotr(Vals[3],13)^rotr(Vals[3],22)); -Vals[4]+=Ma2(Vals[5],Vals[3],Vals[2]); - -W[4]=state4; -W[4]+=Vals[1]; - -Vals[1]=x3ac42e24U; -Vals[1]+=(rotr(Vals[7],6)^rotr(Vals[7],11)^rotr(Vals[7],25)); -Vals[1]+=ch(Vals[7],Vals[6],Vals[0]); -Vals[1]+=W[3]; -Vals[5]+=Vals[1]; -Vals[1]+=(rotr(Vals[4],2)^rotr(Vals[4],13)^rotr(Vals[4],22)); -Vals[1]+=Ma(Vals[2],Vals[4],Vals[3]); - -Vals[0]+=(rotr(Vals[5],6)^rotr(Vals[5],11)^rotr(Vals[5],25)); -Vals[0]+=ch(Vals[5],Vals[7],Vals[6]); -Vals[0]+=K[4]; -Vals[0]+=W[4]; -Vals[2]+=Vals[0]; -Vals[0]+=(rotr(Vals[1],2)^rotr(Vals[1],13)^rotr(Vals[1],22)); -Vals[0]+=Ma(Vals[3],Vals[1],Vals[4]); - -Vals[6]+=(rotr(Vals[2],6)^rotr(Vals[2],11)^rotr(Vals[2],25)); -Vals[6]+=ch(Vals[2],Vals[5],Vals[7]); -Vals[6]+=K[5]; -Vals[6]+=W[5]; -Vals[3]+=Vals[6]; -Vals[6]+=(rotr(Vals[0],2)^rotr(Vals[0],13)^rotr(Vals[0],22)); -Vals[6]+=Ma(Vals[4],Vals[0],Vals[1]); - -Vals[7]+=(rotr(Vals[3],6)^rotr(Vals[3],11)^rotr(Vals[3],25)); -Vals[7]+=ch(Vals[3],Vals[2],Vals[5]); -Vals[7]+=K[6]; -Vals[7]+=W[6]; -Vals[4]+=Vals[7]; -Vals[7]+=(rotr(Vals[6],2)^rotr(Vals[6],13)^rotr(Vals[6],22)); -Vals[7]+=Ma(Vals[1],Vals[6],Vals[0]); - -Vals[5]+=(rotr(Vals[4],6)^rotr(Vals[4],11)^rotr(Vals[4],25)); -Vals[5]+=ch(Vals[4],Vals[3],Vals[2]); -Vals[5]+=K[7]; -Vals[5]+=W[7]; -Vals[1]+=Vals[5]; -Vals[5]+=(rotr(Vals[7],2)^rotr(Vals[7],13)^rotr(Vals[7],22)); -Vals[5]+=Ma(Vals[0],Vals[7],Vals[6]); - -Vals[2]+=(rotr(Vals[1],6)^rotr(Vals[1],11)^rotr(Vals[1],25)); -Vals[2]+=ch(Vals[1],Vals[4],Vals[3]); -Vals[2]+=x5807aa98U; -Vals[0]+=Vals[2]; -Vals[2]+=(rotr(Vals[5],2)^rotr(Vals[5],13)^rotr(Vals[5],22)); -Vals[2]+=Ma(Vals[6],Vals[5],Vals[7]); - -Vals[3]+=(rotr(Vals[0],6)^rotr(Vals[0],11)^rotr(Vals[0],25)); -Vals[3]+=ch(Vals[0],Vals[1],Vals[4]); -Vals[3]+=K[9]; -Vals[6]+=Vals[3]; -Vals[3]+=(rotr(Vals[2],2)^rotr(Vals[2],13)^rotr(Vals[2],22)); -Vals[3]+=Ma(Vals[7],Vals[2],Vals[5]); - -Vals[4]+=(rotr(Vals[6],6)^rotr(Vals[6],11)^rotr(Vals[6],25)); -Vals[4]+=ch(Vals[6],Vals[0],Vals[1]); -Vals[4]+=K[10]; -Vals[7]+=Vals[4]; -Vals[4]+=(rotr(Vals[3],2)^rotr(Vals[3],13)^rotr(Vals[3],22)); -Vals[4]+=Ma(Vals[5],Vals[3],Vals[2]); - -Vals[1]+=(rotr(Vals[7],6)^rotr(Vals[7],11)^rotr(Vals[7],25)); -Vals[1]+=ch(Vals[7],Vals[6],Vals[0]); -Vals[1]+=K[11]; -Vals[5]+=Vals[1]; -Vals[1]+=(rotr(Vals[4],2)^rotr(Vals[4],13)^rotr(Vals[4],22)); -Vals[1]+=Ma(Vals[2],Vals[4],Vals[3]); - -Vals[0]+=(rotr(Vals[5],6)^rotr(Vals[5],11)^rotr(Vals[5],25)); -Vals[0]+=ch(Vals[5],Vals[7],Vals[6]); -Vals[0]+=K[12]; -Vals[2]+=Vals[0]; -Vals[0]+=(rotr(Vals[1],2)^rotr(Vals[1],13)^rotr(Vals[1],22)); -Vals[0]+=Ma(Vals[3],Vals[1],Vals[4]); - -Vals[6]+=(rotr(Vals[2],6)^rotr(Vals[2],11)^rotr(Vals[2],25)); -Vals[6]+=ch(Vals[2],Vals[5],Vals[7]); -Vals[6]+=K[13]; -Vals[3]+=Vals[6]; -Vals[6]+=(rotr(Vals[0],2)^rotr(Vals[0],13)^rotr(Vals[0],22)); -Vals[6]+=Ma(Vals[4],Vals[0],Vals[1]); - -Vals[7]+=(rotr(Vals[3],6)^rotr(Vals[3],11)^rotr(Vals[3],25)); -Vals[7]+=ch(Vals[3],Vals[2],Vals[5]); -Vals[7]+=K[14]; -Vals[4]+=Vals[7]; -Vals[7]+=(rotr(Vals[6],2)^rotr(Vals[6],13)^rotr(Vals[6],22)); -Vals[7]+=Ma(Vals[1],Vals[6],Vals[0]); - -Vals[5]+=(rotr(Vals[4],6)^rotr(Vals[4],11)^rotr(Vals[4],25)); -Vals[5]+=ch(Vals[4],Vals[3],Vals[2]); -Vals[5]+=xc19bf274U; -Vals[1]+=Vals[5]; -Vals[5]+=(rotr(Vals[7],2)^rotr(Vals[7],13)^rotr(Vals[7],22)); -Vals[5]+=Ma(Vals[0],Vals[7],Vals[6]); - -W[0]+=(rotr(W[1],7)^rotr(W[1],18)^(W[1]>>3U)); -Vals[2]+=W[0]; -Vals[2]+=(rotr(Vals[1],6)^rotr(Vals[1],11)^rotr(Vals[1],25)); -Vals[2]+=ch(Vals[1],Vals[4],Vals[3]); -Vals[2]+=K[16]; -Vals[0]+=Vals[2]; -Vals[2]+=(rotr(Vals[5],2)^rotr(Vals[5],13)^rotr(Vals[5],22)); -Vals[2]+=Ma(Vals[6],Vals[5],Vals[7]); - -W[1]+=(rotr(W[2],7)^rotr(W[2],18)^(W[2]>>3U)); -W[1]+=x00a00000U; -Vals[3]+=W[1]; -Vals[3]+=(rotr(Vals[0],6)^rotr(Vals[0],11)^rotr(Vals[0],25)); -Vals[3]+=ch(Vals[0],Vals[1],Vals[4]); -Vals[3]+=K[17]; -Vals[6]+=Vals[3]; -Vals[3]+=(rotr(Vals[2],2)^rotr(Vals[2],13)^rotr(Vals[2],22)); -Vals[3]+=Ma(Vals[7],Vals[2],Vals[5]); - -W[2]+=(rotr(W[3],7)^rotr(W[3],18)^(W[3]>>3U)); -W[2]+=(rotr(W[0],17)^rotr(W[0],19)^(W[0]>>10U)); -Vals[4]+=W[2]; -Vals[4]+=(rotr(Vals[6],6)^rotr(Vals[6],11)^rotr(Vals[6],25)); -Vals[4]+=ch(Vals[6],Vals[0],Vals[1]); -Vals[4]+=K[18]; -Vals[7]+=Vals[4]; -Vals[4]+=(rotr(Vals[3],2)^rotr(Vals[3],13)^rotr(Vals[3],22)); -Vals[4]+=Ma(Vals[5],Vals[3],Vals[2]); - -W[3]+=(rotr(W[4],7)^rotr(W[4],18)^(W[4]>>3U)); -W[3]+=(rotr(W[1],17)^rotr(W[1],19)^(W[1]>>10U)); -Vals[1]+=W[3]; -Vals[1]+=(rotr(Vals[7],6)^rotr(Vals[7],11)^rotr(Vals[7],25)); -Vals[1]+=ch(Vals[7],Vals[6],Vals[0]); -Vals[1]+=K[19]; -Vals[5]+=Vals[1]; -Vals[1]+=(rotr(Vals[4],2)^rotr(Vals[4],13)^rotr(Vals[4],22)); -Vals[1]+=Ma(Vals[2],Vals[4],Vals[3]); - -W[4]+=(rotr(W[5],7)^rotr(W[5],18)^(W[5]>>3U)); -W[4]+=(rotr(W[2],17)^rotr(W[2],19)^(W[2]>>10U)); -Vals[0]+=W[4]; -Vals[0]+=(rotr(Vals[5],6)^rotr(Vals[5],11)^rotr(Vals[5],25)); -Vals[0]+=ch(Vals[5],Vals[7],Vals[6]); -Vals[0]+=K[20]; -Vals[2]+=Vals[0]; -Vals[0]+=(rotr(Vals[1],2)^rotr(Vals[1],13)^rotr(Vals[1],22)); -Vals[0]+=Ma(Vals[3],Vals[1],Vals[4]); - -W[5]+=(rotr(W[6],7)^rotr(W[6],18)^(W[6]>>3U)); -W[5]+=(rotr(W[3],17)^rotr(W[3],19)^(W[3]>>10U)); -Vals[6]+=W[5]; -Vals[6]+=(rotr(Vals[2],6)^rotr(Vals[2],11)^rotr(Vals[2],25)); -Vals[6]+=ch(Vals[2],Vals[5],Vals[7]); -Vals[6]+=K[21]; -Vals[3]+=Vals[6]; -Vals[6]+=(rotr(Vals[0],2)^rotr(Vals[0],13)^rotr(Vals[0],22)); -Vals[6]+=Ma(Vals[4],Vals[0],Vals[1]); - -W[6]+=(rotr(W[7],7)^rotr(W[7],18)^(W[7]>>3U)); -W[6]+=x00000100U; -W[6]+=(rotr(W[4],17)^rotr(W[4],19)^(W[4]>>10U)); -Vals[7]+=W[6]; -Vals[7]+=(rotr(Vals[3],6)^rotr(Vals[3],11)^rotr(Vals[3],25)); -Vals[7]+=ch(Vals[3],Vals[2],Vals[5]); -Vals[7]+=K[22]; -Vals[4]+=Vals[7]; -Vals[7]+=(rotr(Vals[6],2)^rotr(Vals[6],13)^rotr(Vals[6],22)); -Vals[7]+=Ma(Vals[1],Vals[6],Vals[0]); - -W[7]+=x11002000U; -W[7]+=W[0]; -W[7]+=(rotr(W[5],17)^rotr(W[5],19)^(W[5]>>10U)); -Vals[5]+=W[7]; -Vals[5]+=(rotr(Vals[4],6)^rotr(Vals[4],11)^rotr(Vals[4],25)); -Vals[5]+=ch(Vals[4],Vals[3],Vals[2]); -Vals[5]+=K[23]; -Vals[1]+=Vals[5]; -Vals[5]+=(rotr(Vals[7],2)^rotr(Vals[7],13)^rotr(Vals[7],22)); -Vals[5]+=Ma(Vals[0],Vals[7],Vals[6]); - -W[8]=x80000000U; -W[8]+=W[1]; -W[8]+=(rotr(W[6],17)^rotr(W[6],19)^(W[6]>>10U)); -Vals[2]+=W[8]; -Vals[2]+=(rotr(Vals[1],6)^rotr(Vals[1],11)^rotr(Vals[1],25)); -Vals[2]+=ch(Vals[1],Vals[4],Vals[3]); -Vals[2]+=K[24]; -Vals[0]+=Vals[2]; -Vals[2]+=(rotr(Vals[5],2)^rotr(Vals[5],13)^rotr(Vals[5],22)); -Vals[2]+=Ma(Vals[6],Vals[5],Vals[7]); - -W[9]=W[2]; -W[9]+=(rotr(W[7],17)^rotr(W[7],19)^(W[7]>>10U)); -Vals[3]+=W[9]; -Vals[3]+=(rotr(Vals[0],6)^rotr(Vals[0],11)^rotr(Vals[0],25)); -Vals[3]+=ch(Vals[0],Vals[1],Vals[4]); -Vals[3]+=K[25]; -Vals[6]+=Vals[3]; -Vals[3]+=(rotr(Vals[2],2)^rotr(Vals[2],13)^rotr(Vals[2],22)); -Vals[3]+=Ma(Vals[7],Vals[2],Vals[5]); - -W[10]=W[3]; -W[10]+=(rotr(W[8],17)^rotr(W[8],19)^(W[8]>>10U)); -Vals[4]+=W[10]; -Vals[4]+=(rotr(Vals[6],6)^rotr(Vals[6],11)^rotr(Vals[6],25)); -Vals[4]+=ch(Vals[6],Vals[0],Vals[1]); -Vals[4]+=K[26]; -Vals[7]+=Vals[4]; -Vals[4]+=(rotr(Vals[3],2)^rotr(Vals[3],13)^rotr(Vals[3],22)); -Vals[4]+=Ma(Vals[5],Vals[3],Vals[2]); - -W[11]=W[4]; -W[11]+=(rotr(W[9],17)^rotr(W[9],19)^(W[9]>>10U)); -Vals[1]+=W[11]; -Vals[1]+=(rotr(Vals[7],6)^rotr(Vals[7],11)^rotr(Vals[7],25)); -Vals[1]+=ch(Vals[7],Vals[6],Vals[0]); -Vals[1]+=K[27]; -Vals[5]+=Vals[1]; -Vals[1]+=(rotr(Vals[4],2)^rotr(Vals[4],13)^rotr(Vals[4],22)); -Vals[1]+=Ma(Vals[2],Vals[4],Vals[3]); - -W[12]=W[5]; -W[12]+=(rotr(W[10],17)^rotr(W[10],19)^(W[10]>>10U)); -Vals[0]+=W[12]; -Vals[0]+=(rotr(Vals[5],6)^rotr(Vals[5],11)^rotr(Vals[5],25)); -Vals[0]+=ch(Vals[5],Vals[7],Vals[6]); -Vals[0]+=K[28]; -Vals[2]+=Vals[0]; -Vals[0]+=(rotr(Vals[1],2)^rotr(Vals[1],13)^rotr(Vals[1],22)); -Vals[0]+=Ma(Vals[3],Vals[1],Vals[4]); - -W[13]=W[6]; -W[13]+=(rotr(W[11],17)^rotr(W[11],19)^(W[11]>>10U)); -Vals[6]+=W[13]; -Vals[6]+=(rotr(Vals[2],6)^rotr(Vals[2],11)^rotr(Vals[2],25)); -Vals[6]+=ch(Vals[2],Vals[5],Vals[7]); -Vals[6]+=K[29]; -Vals[3]+=Vals[6]; -Vals[6]+=(rotr(Vals[0],2)^rotr(Vals[0],13)^rotr(Vals[0],22)); -Vals[6]+=Ma(Vals[4],Vals[0],Vals[1]); - -W[14]=x00400022U; -W[14]+=W[7]; -W[14]+=(rotr(W[12],17)^rotr(W[12],19)^(W[12]>>10U)); -Vals[7]+=W[14]; -Vals[7]+=(rotr(Vals[3],6)^rotr(Vals[3],11)^rotr(Vals[3],25)); -Vals[7]+=ch(Vals[3],Vals[2],Vals[5]); -Vals[7]+=K[30]; -Vals[4]+=Vals[7]; -Vals[7]+=(rotr(Vals[6],2)^rotr(Vals[6],13)^rotr(Vals[6],22)); -Vals[7]+=Ma(Vals[1],Vals[6],Vals[0]); - -W[15]=x00000100U; -W[15]+=(rotr(W[0],7)^rotr(W[0],18)^(W[0]>>3U)); -W[15]+=W[8]; -W[15]+=(rotr(W[13],17)^rotr(W[13],19)^(W[13]>>10U)); -Vals[5]+=W[15]; -Vals[5]+=(rotr(Vals[4],6)^rotr(Vals[4],11)^rotr(Vals[4],25)); -Vals[5]+=ch(Vals[4],Vals[3],Vals[2]); -Vals[5]+=K[31]; -Vals[1]+=Vals[5]; -Vals[5]+=(rotr(Vals[7],2)^rotr(Vals[7],13)^rotr(Vals[7],22)); -Vals[5]+=Ma(Vals[0],Vals[7],Vals[6]); - -W[0]+=(rotr(W[1],7)^rotr(W[1],18)^(W[1]>>3U)); -W[0]+=W[9]; -W[0]+=(rotr(W[14],17)^rotr(W[14],19)^(W[14]>>10U)); -Vals[2]+=W[0]; -Vals[2]+=(rotr(Vals[1],6)^rotr(Vals[1],11)^rotr(Vals[1],25)); -Vals[2]+=ch(Vals[1],Vals[4],Vals[3]); -Vals[2]+=K[32]; -Vals[0]+=Vals[2]; -Vals[2]+=(rotr(Vals[5],2)^rotr(Vals[5],13)^rotr(Vals[5],22)); -Vals[2]+=Ma(Vals[6],Vals[5],Vals[7]); - -W[1]+=(rotr(W[2],7)^rotr(W[2],18)^(W[2]>>3U)); -W[1]+=W[10]; -W[1]+=(rotr(W[15],17)^rotr(W[15],19)^(W[15]>>10U)); -Vals[3]+=W[1]; -Vals[3]+=(rotr(Vals[0],6)^rotr(Vals[0],11)^rotr(Vals[0],25)); -Vals[3]+=ch(Vals[0],Vals[1],Vals[4]); -Vals[3]+=K[33]; -Vals[6]+=Vals[3]; -Vals[3]+=(rotr(Vals[2],2)^rotr(Vals[2],13)^rotr(Vals[2],22)); -Vals[3]+=Ma(Vals[7],Vals[2],Vals[5]); - -W[2]+=(rotr(W[3],7)^rotr(W[3],18)^(W[3]>>3U)); -W[2]+=W[11]; -W[2]+=(rotr(W[0],17)^rotr(W[0],19)^(W[0]>>10U)); -Vals[4]+=W[2]; -Vals[4]+=(rotr(Vals[6],6)^rotr(Vals[6],11)^rotr(Vals[6],25)); -Vals[4]+=ch(Vals[6],Vals[0],Vals[1]); -Vals[4]+=K[34]; -Vals[7]+=Vals[4]; -Vals[4]+=(rotr(Vals[3],2)^rotr(Vals[3],13)^rotr(Vals[3],22)); -Vals[4]+=Ma(Vals[5],Vals[3],Vals[2]); - -W[3]+=(rotr(W[4],7)^rotr(W[4],18)^(W[4]>>3U)); -W[3]+=W[12]; -W[3]+=(rotr(W[1],17)^rotr(W[1],19)^(W[1]>>10U)); -Vals[1]+=W[3]; -Vals[1]+=(rotr(Vals[7],6)^rotr(Vals[7],11)^rotr(Vals[7],25)); -Vals[1]+=ch(Vals[7],Vals[6],Vals[0]); -Vals[1]+=K[35]; -Vals[5]+=Vals[1]; -Vals[1]+=(rotr(Vals[4],2)^rotr(Vals[4],13)^rotr(Vals[4],22)); -Vals[1]+=Ma(Vals[2],Vals[4],Vals[3]); - -W[4]+=(rotr(W[5],7)^rotr(W[5],18)^(W[5]>>3U)); -W[4]+=W[13]; -W[4]+=(rotr(W[2],17)^rotr(W[2],19)^(W[2]>>10U)); -Vals[0]+=W[4]; -Vals[0]+=(rotr(Vals[5],6)^rotr(Vals[5],11)^rotr(Vals[5],25)); -Vals[0]+=ch(Vals[5],Vals[7],Vals[6]); -Vals[0]+=K[36]; -Vals[2]+=Vals[0]; -Vals[0]+=(rotr(Vals[1],2)^rotr(Vals[1],13)^rotr(Vals[1],22)); -Vals[0]+=Ma(Vals[3],Vals[1],Vals[4]); - -W[5]+=(rotr(W[6],7)^rotr(W[6],18)^(W[6]>>3U)); -W[5]+=W[14]; -W[5]+=(rotr(W[3],17)^rotr(W[3],19)^(W[3]>>10U)); -Vals[6]+=W[5]; -Vals[6]+=(rotr(Vals[2],6)^rotr(Vals[2],11)^rotr(Vals[2],25)); -Vals[6]+=ch(Vals[2],Vals[5],Vals[7]); -Vals[6]+=K[37]; -Vals[3]+=Vals[6]; -Vals[6]+=(rotr(Vals[0],2)^rotr(Vals[0],13)^rotr(Vals[0],22)); -Vals[6]+=Ma(Vals[4],Vals[0],Vals[1]); - -W[6]+=(rotr(W[7],7)^rotr(W[7],18)^(W[7]>>3U)); -W[6]+=W[15]; -W[6]+=(rotr(W[4],17)^rotr(W[4],19)^(W[4]>>10U)); -Vals[7]+=W[6]; -Vals[7]+=(rotr(Vals[3],6)^rotr(Vals[3],11)^rotr(Vals[3],25)); -Vals[7]+=ch(Vals[3],Vals[2],Vals[5]); -Vals[7]+=K[38]; -Vals[4]+=Vals[7]; -Vals[7]+=(rotr(Vals[6],2)^rotr(Vals[6],13)^rotr(Vals[6],22)); -Vals[7]+=Ma(Vals[1],Vals[6],Vals[0]); - -W[7]+=(rotr(W[8],7)^rotr(W[8],18)^(W[8]>>3U)); -W[7]+=W[0]; -W[7]+=(rotr(W[5],17)^rotr(W[5],19)^(W[5]>>10U)); -Vals[5]+=W[7]; -Vals[5]+=(rotr(Vals[4],6)^rotr(Vals[4],11)^rotr(Vals[4],25)); -Vals[5]+=ch(Vals[4],Vals[3],Vals[2]); -Vals[5]+=K[39]; -Vals[1]+=Vals[5]; -Vals[5]+=(rotr(Vals[7],2)^rotr(Vals[7],13)^rotr(Vals[7],22)); -Vals[5]+=Ma(Vals[0],Vals[7],Vals[6]); - -W[8]+=(rotr(W[9],7)^rotr(W[9],18)^(W[9]>>3U)); -W[8]+=W[1]; -W[8]+=(rotr(W[6],17)^rotr(W[6],19)^(W[6]>>10U)); -Vals[2]+=W[8]; -Vals[2]+=(rotr(Vals[1],6)^rotr(Vals[1],11)^rotr(Vals[1],25)); -Vals[2]+=ch(Vals[1],Vals[4],Vals[3]); -Vals[2]+=K[40]; -Vals[0]+=Vals[2]; -Vals[2]+=(rotr(Vals[5],2)^rotr(Vals[5],13)^rotr(Vals[5],22)); -Vals[2]+=Ma(Vals[6],Vals[5],Vals[7]); - -W[9]+=(rotr(W[10],7)^rotr(W[10],18)^(W[10]>>3U)); -W[9]+=W[2]; -W[9]+=(rotr(W[7],17)^rotr(W[7],19)^(W[7]>>10U)); -Vals[3]+=W[9]; -Vals[3]+=(rotr(Vals[0],6)^rotr(Vals[0],11)^rotr(Vals[0],25)); -Vals[3]+=ch(Vals[0],Vals[1],Vals[4]); -Vals[3]+=K[41]; -Vals[6]+=Vals[3]; -Vals[3]+=(rotr(Vals[2],2)^rotr(Vals[2],13)^rotr(Vals[2],22)); -Vals[3]+=Ma(Vals[7],Vals[2],Vals[5]); - -W[10]+=(rotr(W[11],7)^rotr(W[11],18)^(W[11]>>3U)); -W[10]+=W[3]; -W[10]+=(rotr(W[8],17)^rotr(W[8],19)^(W[8]>>10U)); -Vals[4]+=W[10]; -Vals[4]+=(rotr(Vals[6],6)^rotr(Vals[6],11)^rotr(Vals[6],25)); -Vals[4]+=ch(Vals[6],Vals[0],Vals[1]); -Vals[4]+=K[42]; -Vals[7]+=Vals[4]; -Vals[4]+=(rotr(Vals[3],2)^rotr(Vals[3],13)^rotr(Vals[3],22)); -Vals[4]+=Ma(Vals[5],Vals[3],Vals[2]); - -W[11]+=(rotr(W[12],7)^rotr(W[12],18)^(W[12]>>3U)); -W[11]+=W[4]; -W[11]+=(rotr(W[9],17)^rotr(W[9],19)^(W[9]>>10U)); -Vals[1]+=W[11]; -Vals[1]+=(rotr(Vals[7],6)^rotr(Vals[7],11)^rotr(Vals[7],25)); -Vals[1]+=ch(Vals[7],Vals[6],Vals[0]); -Vals[1]+=K[43]; -Vals[5]+=Vals[1]; -Vals[1]+=(rotr(Vals[4],2)^rotr(Vals[4],13)^rotr(Vals[4],22)); -Vals[1]+=Ma(Vals[2],Vals[4],Vals[3]); - -W[12]+=(rotr(W[13],7)^rotr(W[13],18)^(W[13]>>3U)); -W[12]+=W[5]; -W[12]+=(rotr(W[10],17)^rotr(W[10],19)^(W[10]>>10U)); -Vals[0]+=W[12]; -Vals[0]+=(rotr(Vals[5],6)^rotr(Vals[5],11)^rotr(Vals[5],25)); -Vals[0]+=ch(Vals[5],Vals[7],Vals[6]); -Vals[0]+=K[44]; -Vals[2]+=Vals[0]; -Vals[0]+=(rotr(Vals[1],2)^rotr(Vals[1],13)^rotr(Vals[1],22)); -Vals[0]+=Ma(Vals[3],Vals[1],Vals[4]); - -W[13]+=(rotr(W[14],7)^rotr(W[14],18)^(W[14]>>3U)); -W[13]+=W[6]; -W[13]+=(rotr(W[11],17)^rotr(W[11],19)^(W[11]>>10U)); -Vals[6]+=W[13]; -Vals[6]+=(rotr(Vals[2],6)^rotr(Vals[2],11)^rotr(Vals[2],25)); -Vals[6]+=ch(Vals[2],Vals[5],Vals[7]); -Vals[6]+=K[45]; -Vals[3]+=Vals[6]; -Vals[6]+=(rotr(Vals[0],2)^rotr(Vals[0],13)^rotr(Vals[0],22)); -Vals[6]+=Ma(Vals[4],Vals[0],Vals[1]); - -W[14]+=(rotr(W[15],7)^rotr(W[15],18)^(W[15]>>3U)); -W[14]+=W[7]; -W[14]+=(rotr(W[12],17)^rotr(W[12],19)^(W[12]>>10U)); -Vals[7]+=W[14]; -Vals[7]+=(rotr(Vals[3],6)^rotr(Vals[3],11)^rotr(Vals[3],25)); -Vals[7]+=ch(Vals[3],Vals[2],Vals[5]); -Vals[7]+=K[46]; -Vals[4]+=Vals[7]; -Vals[7]+=(rotr(Vals[6],2)^rotr(Vals[6],13)^rotr(Vals[6],22)); -Vals[7]+=Ma(Vals[1],Vals[6],Vals[0]); - -W[15]+=(rotr(W[0],7)^rotr(W[0],18)^(W[0]>>3U)); -W[15]+=W[8]; -W[15]+=(rotr(W[13],17)^rotr(W[13],19)^(W[13]>>10U)); -Vals[5]+=W[15]; -Vals[5]+=(rotr(Vals[4],6)^rotr(Vals[4],11)^rotr(Vals[4],25)); -Vals[5]+=ch(Vals[4],Vals[3],Vals[2]); -Vals[5]+=K[47]; -Vals[1]+=Vals[5]; -Vals[5]+=(rotr(Vals[7],2)^rotr(Vals[7],13)^rotr(Vals[7],22)); -Vals[5]+=Ma(Vals[0],Vals[7],Vals[6]); - -W[0]+=(rotr(W[1],7)^rotr(W[1],18)^(W[1]>>3U)); -W[0]+=W[9]; -W[0]+=(rotr(W[14],17)^rotr(W[14],19)^(W[14]>>10U)); -Vals[2]+=W[0]; -Vals[2]+=(rotr(Vals[1],6)^rotr(Vals[1],11)^rotr(Vals[1],25)); -Vals[2]+=ch(Vals[1],Vals[4],Vals[3]); -Vals[2]+=K[48]; -Vals[0]+=Vals[2]; -Vals[2]+=(rotr(Vals[5],2)^rotr(Vals[5],13)^rotr(Vals[5],22)); -Vals[2]+=Ma(Vals[6],Vals[5],Vals[7]); - -W[1]+=(rotr(W[2],7)^rotr(W[2],18)^(W[2]>>3U)); -W[1]+=W[10]; -W[1]+=(rotr(W[15],17)^rotr(W[15],19)^(W[15]>>10U)); -Vals[3]+=W[1]; -Vals[3]+=(rotr(Vals[0],6)^rotr(Vals[0],11)^rotr(Vals[0],25)); -Vals[3]+=ch(Vals[0],Vals[1],Vals[4]); -Vals[3]+=K[49]; -Vals[6]+=Vals[3]; -Vals[3]+=(rotr(Vals[2],2)^rotr(Vals[2],13)^rotr(Vals[2],22)); -Vals[3]+=Ma(Vals[7],Vals[2],Vals[5]); - -W[2]+=(rotr(W[3],7)^rotr(W[3],18)^(W[3]>>3U)); -W[2]+=W[11]; -W[2]+=(rotr(W[0],17)^rotr(W[0],19)^(W[0]>>10U)); -Vals[4]+=W[2]; -Vals[4]+=(rotr(Vals[6],6)^rotr(Vals[6],11)^rotr(Vals[6],25)); -Vals[4]+=ch(Vals[6],Vals[0],Vals[1]); -Vals[4]+=K[50]; -Vals[7]+=Vals[4]; -Vals[4]+=(rotr(Vals[3],2)^rotr(Vals[3],13)^rotr(Vals[3],22)); -Vals[4]+=Ma(Vals[5],Vals[3],Vals[2]); - -W[3]+=(rotr(W[4],7)^rotr(W[4],18)^(W[4]>>3U)); -W[3]+=W[12]; -W[3]+=(rotr(W[1],17)^rotr(W[1],19)^(W[1]>>10U)); -Vals[1]+=W[3]; -Vals[1]+=(rotr(Vals[7],6)^rotr(Vals[7],11)^rotr(Vals[7],25)); -Vals[1]+=ch(Vals[7],Vals[6],Vals[0]); -Vals[1]+=K[51]; -Vals[5]+=Vals[1]; -Vals[1]+=(rotr(Vals[4],2)^rotr(Vals[4],13)^rotr(Vals[4],22)); -Vals[1]+=Ma(Vals[2],Vals[4],Vals[3]); - -W[4]+=(rotr(W[5],7)^rotr(W[5],18)^(W[5]>>3U)); -W[4]+=W[13]; -W[4]+=(rotr(W[2],17)^rotr(W[2],19)^(W[2]>>10U)); -Vals[0]+=W[4]; -Vals[0]+=(rotr(Vals[5],6)^rotr(Vals[5],11)^rotr(Vals[5],25)); -Vals[0]+=ch(Vals[5],Vals[7],Vals[6]); -Vals[0]+=K[52]; -Vals[2]+=Vals[0]; -Vals[0]+=(rotr(Vals[1],2)^rotr(Vals[1],13)^rotr(Vals[1],22)); -Vals[0]+=Ma(Vals[3],Vals[1],Vals[4]); - -W[5]+=(rotr(W[6],7)^rotr(W[6],18)^(W[6]>>3U)); -W[5]+=W[14]; -W[5]+=(rotr(W[3],17)^rotr(W[3],19)^(W[3]>>10U)); -Vals[6]+=W[5]; -Vals[6]+=(rotr(Vals[2],6)^rotr(Vals[2],11)^rotr(Vals[2],25)); -Vals[6]+=ch(Vals[2],Vals[5],Vals[7]); -Vals[6]+=K[53]; -Vals[3]+=Vals[6]; -Vals[6]+=(rotr(Vals[0],2)^rotr(Vals[0],13)^rotr(Vals[0],22)); -Vals[6]+=Ma(Vals[4],Vals[0],Vals[1]); - -W[6]+=(rotr(W[7],7)^rotr(W[7],18)^(W[7]>>3U)); -W[6]+=W[15]; -W[6]+=(rotr(W[4],17)^rotr(W[4],19)^(W[4]>>10U)); -Vals[7]+=W[6]; -Vals[7]+=(rotr(Vals[3],6)^rotr(Vals[3],11)^rotr(Vals[3],25)); -Vals[7]+=ch(Vals[3],Vals[2],Vals[5]); -Vals[7]+=K[54]; -Vals[4]+=Vals[7]; -Vals[7]+=(rotr(Vals[6],2)^rotr(Vals[6],13)^rotr(Vals[6],22)); -Vals[7]+=Ma(Vals[1],Vals[6],Vals[0]); - -W[7]+=(rotr(W[8],7)^rotr(W[8],18)^(W[8]>>3U)); -W[7]+=W[0]; -W[7]+=(rotr(W[5],17)^rotr(W[5],19)^(W[5]>>10U)); -Vals[5]+=W[7]; -Vals[5]+=(rotr(Vals[4],6)^rotr(Vals[4],11)^rotr(Vals[4],25)); -Vals[5]+=ch(Vals[4],Vals[3],Vals[2]); -Vals[5]+=K[55]; -Vals[1]+=Vals[5]; -Vals[5]+=(rotr(Vals[7],2)^rotr(Vals[7],13)^rotr(Vals[7],22)); -Vals[5]+=Ma(Vals[0],Vals[7],Vals[6]); - -W[8]+=(rotr(W[9],7)^rotr(W[9],18)^(W[9]>>3U)); -W[8]+=W[1]; -W[8]+=(rotr(W[6],17)^rotr(W[6],19)^(W[6]>>10U)); -Vals[2]+=W[8]; -Vals[2]+=(rotr(Vals[1],6)^rotr(Vals[1],11)^rotr(Vals[1],25)); -Vals[2]+=ch(Vals[1],Vals[4],Vals[3]); -Vals[2]+=K[56]; -Vals[0]+=Vals[2]; - -W[9]+=(rotr(W[10],7)^rotr(W[10],18)^(W[10]>>3U)); -W[9]+=W[2]; -W[9]+=(rotr(W[7],17)^rotr(W[7],19)^(W[7]>>10U)); -Vals[3]+=W[9]; -Vals[3]+=(rotr(Vals[0],6)^rotr(Vals[0],11)^rotr(Vals[0],25)); -Vals[3]+=ch(Vals[0],Vals[1],Vals[4]); -Vals[3]+=K[57]; -Vals[3]+=Vals[6]; - -W[10]+=(rotr(W[11],7)^rotr(W[11],18)^(W[11]>>3U)); -W[10]+=W[3]; -W[10]+=(rotr(W[8],17)^rotr(W[8],19)^(W[8]>>10U)); -Vals[4]+=W[10]; -Vals[4]+=(rotr(Vals[3],6)^rotr(Vals[3],11)^rotr(Vals[3],25)); -Vals[4]+=ch(Vals[3],Vals[0],Vals[1]); -Vals[4]+=K[58]; -Vals[4]+=Vals[7]; -Vals[1]+=(rotr(Vals[4],6)^rotr(Vals[4],11)^rotr(Vals[4],25)); -Vals[1]+=ch(Vals[4],Vals[3],Vals[0]); -Vals[1]+=W[11]; -Vals[1]+=(rotr(W[12],7)^rotr(W[12],18)^(W[12]>>3U)); -Vals[1]+=W[4]; -Vals[1]+=(rotr(W[9],17)^rotr(W[9],19)^(W[9]>>10U)); -Vals[1]+=K[59]; -Vals[1]+=Vals[5]; - -Vals[2]+=Ma(Vals[6],Vals[5],Vals[7]); -Vals[2]+=(rotr(Vals[5],2)^rotr(Vals[5],13)^rotr(Vals[5],22)); -Vals[2]+=W[12]; -Vals[2]+=(rotr(W[13],7)^rotr(W[13],18)^(W[13]>>3U)); -Vals[2]+=W[5]; -Vals[2]+=(rotr(W[10],17)^rotr(W[10],19)^(W[10]>>10U)); -Vals[2]+=Vals[0]; -Vals[2]+=(rotr(Vals[1],6)^rotr(Vals[1],11)^rotr(Vals[1],25)); -Vals[2]+=ch(Vals[1],Vals[4],Vals[3]); - -#define FOUND (0x0F) -#define SETFOUND(Xnonce) output[output[FOUND]++] = Xnonce - -#if defined(VECTORS2) || defined(VECTORS4) - if (any(Vals[2] == x136032edU)) { - if (Vals[2].x == x136032edU) - SETFOUND(nonce.x); - if (Vals[2].y == x136032edU) - SETFOUND(nonce.y); -#if defined(VECTORS4) - if (Vals[2].z == x136032edU) - SETFOUND(nonce.z); - if (Vals[2].w == x136032edU) - SETFOUND(nonce.w); -#endif - } -#else - if (Vals[2] == x136032edU) - SETFOUND(nonce); -#endif -} diff --git a/scrypt.c b/scrypt.c deleted file mode 100644 index e0c75f4463..0000000000 --- a/scrypt.c +++ /dev/null @@ -1,491 +0,0 @@ -/*- - * Copyright 2009 Colin Percival, 2011 ArtForz - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS - * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) - * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT - * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY - * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF - * SUCH DAMAGE. - * - * This file was originally written by Colin Percival as part of the Tarsnap - * online backup system. - */ - -#include "config.h" -#include "miner.h" - -#include -#include -#include - -typedef struct SHA256Context { - uint32_t state[8]; - uint32_t buf[16]; -} SHA256_CTX; - -/* - * Encode a length len/4 vector of (uint32_t) into a length len vector of - * (unsigned char) in big-endian form. Assumes len is a multiple of 4. - */ -static inline void -be32enc_vect(uint32_t *dst, const uint32_t *src, uint32_t len) -{ - uint32_t i; - - for (i = 0; i < len; i++) - dst[i] = htobe32(src[i]); -} - -/* Elementary functions used by SHA256 */ -#define Ch(x, y, z) ((x & (y ^ z)) ^ z) -#define Maj(x, y, z) ((x & (y | z)) | (y & z)) -#define SHR(x, n) (x >> n) -#define ROTR(x, n) ((x >> n) | (x << (32 - n))) -#define S0(x) (ROTR(x, 2) ^ ROTR(x, 13) ^ ROTR(x, 22)) -#define S1(x) (ROTR(x, 6) ^ ROTR(x, 11) ^ ROTR(x, 25)) -#define s0(x) (ROTR(x, 7) ^ ROTR(x, 18) ^ SHR(x, 3)) -#define s1(x) (ROTR(x, 17) ^ ROTR(x, 19) ^ SHR(x, 10)) - -/* SHA256 round function */ -#define RND(a, b, c, d, e, f, g, h, k) \ - t0 = h + S1(e) + Ch(e, f, g) + k; \ - t1 = S0(a) + Maj(a, b, c); \ - d += t0; \ - h = t0 + t1; - -/* Adjusted round function for rotating state */ -#define RNDr(S, W, i, k) \ - RND(S[(64 - i) % 8], S[(65 - i) % 8], \ - S[(66 - i) % 8], S[(67 - i) % 8], \ - S[(68 - i) % 8], S[(69 - i) % 8], \ - S[(70 - i) % 8], S[(71 - i) % 8], \ - W[i] + k) - -/* - * SHA256 block compression function. The 256-bit state is transformed via - * the 512-bit input block to produce a new state. - */ -static void -SHA256_Transform(uint32_t * state, const uint32_t block[16], int swap) -{ - uint32_t W[64]; - uint32_t S[8]; - uint32_t t0, t1; - int i; - - /* 1. Prepare message schedule W. */ - if(swap) - for (i = 0; i < 16; i++) - W[i] = htobe32(block[i]); - else - memcpy(W, block, 64); - for (i = 16; i < 64; i += 2) { - W[i] = s1(W[i - 2]) + W[i - 7] + s0(W[i - 15]) + W[i - 16]; - W[i+1] = s1(W[i - 1]) + W[i - 6] + s0(W[i - 14]) + W[i - 15]; - } - - /* 2. Initialize working variables. */ - memcpy(S, state, 32); - - /* 3. Mix. */ - RNDr(S, W, 0, 0x428a2f98); - RNDr(S, W, 1, 0x71374491); - RNDr(S, W, 2, 0xb5c0fbcf); - RNDr(S, W, 3, 0xe9b5dba5); - RNDr(S, W, 4, 0x3956c25b); - RNDr(S, W, 5, 0x59f111f1); - RNDr(S, W, 6, 0x923f82a4); - RNDr(S, W, 7, 0xab1c5ed5); - RNDr(S, W, 8, 0xd807aa98); - RNDr(S, W, 9, 0x12835b01); - RNDr(S, W, 10, 0x243185be); - RNDr(S, W, 11, 0x550c7dc3); - RNDr(S, W, 12, 0x72be5d74); - RNDr(S, W, 13, 0x80deb1fe); - RNDr(S, W, 14, 0x9bdc06a7); - RNDr(S, W, 15, 0xc19bf174); - RNDr(S, W, 16, 0xe49b69c1); - RNDr(S, W, 17, 0xefbe4786); - RNDr(S, W, 18, 0x0fc19dc6); - RNDr(S, W, 19, 0x240ca1cc); - RNDr(S, W, 20, 0x2de92c6f); - RNDr(S, W, 21, 0x4a7484aa); - RNDr(S, W, 22, 0x5cb0a9dc); - RNDr(S, W, 23, 0x76f988da); - RNDr(S, W, 24, 0x983e5152); - RNDr(S, W, 25, 0xa831c66d); - RNDr(S, W, 26, 0xb00327c8); - RNDr(S, W, 27, 0xbf597fc7); - RNDr(S, W, 28, 0xc6e00bf3); - RNDr(S, W, 29, 0xd5a79147); - RNDr(S, W, 30, 0x06ca6351); - RNDr(S, W, 31, 0x14292967); - RNDr(S, W, 32, 0x27b70a85); - RNDr(S, W, 33, 0x2e1b2138); - RNDr(S, W, 34, 0x4d2c6dfc); - RNDr(S, W, 35, 0x53380d13); - RNDr(S, W, 36, 0x650a7354); - RNDr(S, W, 37, 0x766a0abb); - RNDr(S, W, 38, 0x81c2c92e); - RNDr(S, W, 39, 0x92722c85); - RNDr(S, W, 40, 0xa2bfe8a1); - RNDr(S, W, 41, 0xa81a664b); - RNDr(S, W, 42, 0xc24b8b70); - RNDr(S, W, 43, 0xc76c51a3); - RNDr(S, W, 44, 0xd192e819); - RNDr(S, W, 45, 0xd6990624); - RNDr(S, W, 46, 0xf40e3585); - RNDr(S, W, 47, 0x106aa070); - RNDr(S, W, 48, 0x19a4c116); - RNDr(S, W, 49, 0x1e376c08); - RNDr(S, W, 50, 0x2748774c); - RNDr(S, W, 51, 0x34b0bcb5); - RNDr(S, W, 52, 0x391c0cb3); - RNDr(S, W, 53, 0x4ed8aa4a); - RNDr(S, W, 54, 0x5b9cca4f); - RNDr(S, W, 55, 0x682e6ff3); - RNDr(S, W, 56, 0x748f82ee); - RNDr(S, W, 57, 0x78a5636f); - RNDr(S, W, 58, 0x84c87814); - RNDr(S, W, 59, 0x8cc70208); - RNDr(S, W, 60, 0x90befffa); - RNDr(S, W, 61, 0xa4506ceb); - RNDr(S, W, 62, 0xbef9a3f7); - RNDr(S, W, 63, 0xc67178f2); - - /* 4. Mix local working variables into global state */ - for (i = 0; i < 8; i++) - state[i] += S[i]; -} - -static inline void -SHA256_InitState(uint32_t * state) -{ - /* Magic initialization constants */ - state[0] = 0x6A09E667; - state[1] = 0xBB67AE85; - state[2] = 0x3C6EF372; - state[3] = 0xA54FF53A; - state[4] = 0x510E527F; - state[5] = 0x9B05688C; - state[6] = 0x1F83D9AB; - state[7] = 0x5BE0CD19; -} - -static const uint32_t passwdpad[12] = {0x00000080, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x80020000}; -static const uint32_t outerpad[8] = {0x80000000, 0, 0, 0, 0, 0, 0, 0x00000300}; - -/** - * PBKDF2_SHA256(passwd, passwdlen, salt, saltlen, c, buf, dkLen): - * Compute PBKDF2(passwd, salt, c, dkLen) using HMAC-SHA256 as the PRF, and - * write the output to buf. The value dkLen must be at most 32 * (2^32 - 1). - */ -static inline void -PBKDF2_SHA256_80_128(const uint32_t * passwd, uint32_t * buf) -{ - SHA256_CTX PShictx, PShoctx; - uint32_t tstate[8]; - uint32_t ihash[8]; - uint32_t i; - uint32_t pad[16]; - - static const uint32_t innerpad[11] = {0x00000080, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0xa0040000}; - - /* If Klen > 64, the key is really SHA256(K). */ - SHA256_InitState(tstate); - SHA256_Transform(tstate, passwd, 1); - memcpy(pad, passwd+16, 16); - memcpy(pad+4, passwdpad, 48); - SHA256_Transform(tstate, pad, 1); - memcpy(ihash, tstate, 32); - - SHA256_InitState(PShictx.state); - for (i = 0; i < 8; i++) - pad[i] = ihash[i] ^ 0x36363636; - for (; i < 16; i++) - pad[i] = 0x36363636; - SHA256_Transform(PShictx.state, pad, 0); - SHA256_Transform(PShictx.state, passwd, 1); - be32enc_vect(PShictx.buf, passwd+16, 4); - be32enc_vect(PShictx.buf+5, innerpad, 11); - - SHA256_InitState(PShoctx.state); - for (i = 0; i < 8; i++) - pad[i] = ihash[i] ^ 0x5c5c5c5c; - for (; i < 16; i++) - pad[i] = 0x5c5c5c5c; - SHA256_Transform(PShoctx.state, pad, 0); - memcpy(PShoctx.buf+8, outerpad, 32); - - /* Iterate through the blocks. */ - for (i = 0; i < 4; i++) { - uint32_t istate[8]; - uint32_t ostate[8]; - - memcpy(istate, PShictx.state, 32); - PShictx.buf[4] = i + 1; - SHA256_Transform(istate, PShictx.buf, 0); - memcpy(PShoctx.buf, istate, 32); - - memcpy(ostate, PShoctx.state, 32); - SHA256_Transform(ostate, PShoctx.buf, 0); - be32enc_vect(buf+i*8, ostate, 8); - } -} - - -static inline void -PBKDF2_SHA256_80_128_32(const uint32_t * passwd, const uint32_t * salt, uint32_t *ostate) -{ - uint32_t tstate[8]; - uint32_t ihash[8]; - uint32_t i; - - /* Compute HMAC state after processing P and S. */ - uint32_t pad[16]; - - static const uint32_t ihash_finalblk[16] = {0x00000001,0x80000000,0,0, 0,0,0,0, 0,0,0,0, 0,0,0,0x00000620}; - - /* If Klen > 64, the key is really SHA256(K). */ - SHA256_InitState(tstate); - SHA256_Transform(tstate, passwd, 1); - memcpy(pad, passwd+16, 16); - memcpy(pad+4, passwdpad, 48); - SHA256_Transform(tstate, pad, 1); - memcpy(ihash, tstate, 32); - - SHA256_InitState(ostate); - for (i = 0; i < 8; i++) - pad[i] = ihash[i] ^ 0x5c5c5c5c; - for (; i < 16; i++) - pad[i] = 0x5c5c5c5c; - SHA256_Transform(ostate, pad, 0); - - SHA256_InitState(tstate); - for (i = 0; i < 8; i++) - pad[i] = ihash[i] ^ 0x36363636; - for (; i < 16; i++) - pad[i] = 0x36363636; - SHA256_Transform(tstate, pad, 0); - SHA256_Transform(tstate, salt, 1); - SHA256_Transform(tstate, salt+16, 1); - SHA256_Transform(tstate, ihash_finalblk, 0); - memcpy(pad, tstate, 32); - memcpy(pad+8, outerpad, 32); - - /* Feed the inner hash to the outer SHA256 operation. */ - SHA256_Transform(ostate, pad, 0); -} - - -/** - * salsa20_8(B): - * Apply the salsa20/8 core to the provided block. - */ -static inline void -salsa20_8(uint32_t B[16], const uint32_t Bx[16]) -{ - uint32_t x00,x01,x02,x03,x04,x05,x06,x07,x08,x09,x10,x11,x12,x13,x14,x15; - size_t i; - - x00 = (B[ 0] ^= Bx[ 0]); - x01 = (B[ 1] ^= Bx[ 1]); - x02 = (B[ 2] ^= Bx[ 2]); - x03 = (B[ 3] ^= Bx[ 3]); - x04 = (B[ 4] ^= Bx[ 4]); - x05 = (B[ 5] ^= Bx[ 5]); - x06 = (B[ 6] ^= Bx[ 6]); - x07 = (B[ 7] ^= Bx[ 7]); - x08 = (B[ 8] ^= Bx[ 8]); - x09 = (B[ 9] ^= Bx[ 9]); - x10 = (B[10] ^= Bx[10]); - x11 = (B[11] ^= Bx[11]); - x12 = (B[12] ^= Bx[12]); - x13 = (B[13] ^= Bx[13]); - x14 = (B[14] ^= Bx[14]); - x15 = (B[15] ^= Bx[15]); - for (i = 0; i < 8; i += 2) { -#define R(a,b) (((a) << (b)) | ((a) >> (32 - (b)))) - /* Operate on columns. */ - x04 ^= R(x00+x12, 7); x09 ^= R(x05+x01, 7); x14 ^= R(x10+x06, 7); x03 ^= R(x15+x11, 7); - x08 ^= R(x04+x00, 9); x13 ^= R(x09+x05, 9); x02 ^= R(x14+x10, 9); x07 ^= R(x03+x15, 9); - x12 ^= R(x08+x04,13); x01 ^= R(x13+x09,13); x06 ^= R(x02+x14,13); x11 ^= R(x07+x03,13); - x00 ^= R(x12+x08,18); x05 ^= R(x01+x13,18); x10 ^= R(x06+x02,18); x15 ^= R(x11+x07,18); - - /* Operate on rows. */ - x01 ^= R(x00+x03, 7); x06 ^= R(x05+x04, 7); x11 ^= R(x10+x09, 7); x12 ^= R(x15+x14, 7); - x02 ^= R(x01+x00, 9); x07 ^= R(x06+x05, 9); x08 ^= R(x11+x10, 9); x13 ^= R(x12+x15, 9); - x03 ^= R(x02+x01,13); x04 ^= R(x07+x06,13); x09 ^= R(x08+x11,13); x14 ^= R(x13+x12,13); - x00 ^= R(x03+x02,18); x05 ^= R(x04+x07,18); x10 ^= R(x09+x08,18); x15 ^= R(x14+x13,18); -#undef R - } - B[ 0] += x00; - B[ 1] += x01; - B[ 2] += x02; - B[ 3] += x03; - B[ 4] += x04; - B[ 5] += x05; - B[ 6] += x06; - B[ 7] += x07; - B[ 8] += x08; - B[ 9] += x09; - B[10] += x10; - B[11] += x11; - B[12] += x12; - B[13] += x13; - B[14] += x14; - B[15] += x15; -} - -/* cpu and memory intensive function to transform a 80 byte buffer into a 32 byte output - scratchpad size needs to be at least 63 + (128 * r * p) + (256 * r + 64) + (128 * r * N) bytes - */ -static void scrypt_1024_1_1_256_sp(const uint32_t* input, char* scratchpad, uint32_t *ostate) -{ - uint32_t * V; - uint32_t X[32]; - uint32_t i; - uint32_t j; - uint32_t k; - uint64_t *p1, *p2; - - p1 = (uint64_t *)X; - V = (uint32_t *)(((uintptr_t)(scratchpad) + 63) & ~ (uintptr_t)(63)); - - PBKDF2_SHA256_80_128(input, X); - - for (i = 0; i < 1024; i += 2) { - memcpy(&V[i * 32], X, 128); - - salsa20_8(&X[0], &X[16]); - salsa20_8(&X[16], &X[0]); - - memcpy(&V[(i + 1) * 32], X, 128); - - salsa20_8(&X[0], &X[16]); - salsa20_8(&X[16], &X[0]); - } - for (i = 0; i < 1024; i += 2) { - j = X[16] & 1023; - p2 = (uint64_t *)(&V[j * 32]); - for(k = 0; k < 16; k++) - p1[k] ^= p2[k]; - - salsa20_8(&X[0], &X[16]); - salsa20_8(&X[16], &X[0]); - - j = X[16] & 1023; - p2 = (uint64_t *)(&V[j * 32]); - for(k = 0; k < 16; k++) - p1[k] ^= p2[k]; - - salsa20_8(&X[0], &X[16]); - salsa20_8(&X[16], &X[0]); - } - - PBKDF2_SHA256_80_128_32(input, X, ostate); -} - -/* 131583 rounded up to 4 byte alignment */ -#define SCRATCHBUF_SIZE (131584) - -void scrypt_regenhash(struct work *work) -{ - uint32_t data[20]; - char *scratchbuf; - uint32_t *nonce = (uint32_t *)(work->data + 76); - uint32_t *ohash = (uint32_t *)(work->hash); - - be32enc_vect(data, (const uint32_t *)work->data, 19); - data[19] = htobe32(*nonce); - scratchbuf = alloca(SCRATCHBUF_SIZE); - scrypt_1024_1_1_256_sp(data, scratchbuf, ohash); - flip32(ohash, ohash); -} - -static const uint32_t diff1targ = 0x0000ffff; - -/* Used externally as confirmation of correct OCL code */ -int scrypt_test(unsigned char *pdata, const unsigned char *ptarget, uint32_t nonce) -{ - uint32_t tmp_hash7, Htarg = le32toh(((const uint32_t *)ptarget)[7]); - uint32_t data[20], ohash[8]; - char *scratchbuf; - - be32enc_vect(data, (const uint32_t *)pdata, 19); - data[19] = htobe32(nonce); - scratchbuf = alloca(SCRATCHBUF_SIZE); - scrypt_1024_1_1_256_sp(data, scratchbuf, ohash); - tmp_hash7 = be32toh(ohash[7]); - - applog(LOG_DEBUG, "htarget %08lx diff1 %08lx hash %08lx", - (long unsigned int)Htarg, - (long unsigned int)diff1targ, - (long unsigned int)tmp_hash7); - if (tmp_hash7 > diff1targ) - return -1; - if (tmp_hash7 > Htarg) - return 0; - return 1; -} - -bool scanhash_scrypt(struct thr_info *thr, const unsigned char __maybe_unused *pmidstate, - unsigned char *pdata, unsigned char __maybe_unused *phash1, - unsigned char __maybe_unused *phash, const unsigned char *ptarget, - uint32_t max_nonce, uint32_t *last_nonce, uint32_t n) -{ - uint32_t *nonce = (uint32_t *)(pdata + 76); - char *scratchbuf; - uint32_t data[20]; - uint32_t tmp_hash7; - uint32_t Htarg = le32toh(((const uint32_t *)ptarget)[7]); - bool ret = false; - - be32enc_vect(data, (const uint32_t *)pdata, 19); - - scratchbuf = malloc(SCRATCHBUF_SIZE); - if (unlikely(!scratchbuf)) { - applog(LOG_ERR, "Failed to malloc scratchbuf in scanhash_scrypt"); - return ret; - } - - while(1) { - uint32_t ostate[8]; - - *nonce = ++n; - data[19] = htobe32(n); - scrypt_1024_1_1_256_sp(data, scratchbuf, ostate); - tmp_hash7 = be32toh(ostate[7]); - - if (unlikely(tmp_hash7 <= Htarg)) { - ((uint32_t *)pdata)[19] = htobe32(n); - *last_nonce = n; - ret = true; - break; - } - - if (unlikely((n >= max_nonce) || thr->work_restart)) { - *last_nonce = n; - break; - } - } - - free(scratchbuf);; - return ret; -} diff --git a/scrypt.h b/scrypt.h deleted file mode 100644 index 8f3d2190e5..0000000000 --- a/scrypt.h +++ /dev/null @@ -1,24 +0,0 @@ -#ifndef SCRYPT_H -#define SCRYPT_H - -#include "miner.h" - -#ifdef USE_SCRYPT -extern int scrypt_test(unsigned char *pdata, const unsigned char *ptarget, - uint32_t nonce); -extern void scrypt_regenhash(struct work *work); - -#else /* USE_SCRYPT */ -static inline int scrypt_test(__maybe_unused unsigned char *pdata, - __maybe_unused const unsigned char *ptarget, - __maybe_unused uint32_t nonce) -{ - return 0; -} - -static inline void scrypt_regenhash(__maybe_unused struct work *work) -{ -} -#endif /* USE_SCRYPT */ - -#endif /* SCRYPT_H */ diff --git a/scrypt130511.cl b/scrypt130511.cl deleted file mode 100644 index 5ae0304dbd..0000000000 --- a/scrypt130511.cl +++ /dev/null @@ -1,853 +0,0 @@ -/*- - * Copyright 2009 Colin Percival, 2011 ArtForz, 2011 pooler, 2012 mtrlt, - * 2012-2013 Con Kolivas. - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS - * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) - * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT - * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY - * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF - * SUCH DAMAGE. - * - * This file was originally written by Colin Percival as part of the Tarsnap - * online backup system. - */ - -__constant uint ES[2] = { 0x00FF00FF, 0xFF00FF00 }; -__constant uint K[] = { - 0x428a2f98U, - 0x71374491U, - 0xb5c0fbcfU, - 0xe9b5dba5U, - 0x3956c25bU, - 0x59f111f1U, - 0x923f82a4U, - 0xab1c5ed5U, - 0xd807aa98U, - 0x12835b01U, - 0x243185beU, // 10 - 0x550c7dc3U, - 0x72be5d74U, - 0x80deb1feU, - 0x9bdc06a7U, - 0xe49b69c1U, - 0xefbe4786U, - 0x0fc19dc6U, - 0x240ca1ccU, - 0x2de92c6fU, - 0x4a7484aaU, // 20 - 0x5cb0a9dcU, - 0x76f988daU, - 0x983e5152U, - 0xa831c66dU, - 0xb00327c8U, - 0xbf597fc7U, - 0xc6e00bf3U, - 0xd5a79147U, - 0x06ca6351U, - 0x14292967U, // 30 - 0x27b70a85U, - 0x2e1b2138U, - 0x4d2c6dfcU, - 0x53380d13U, - 0x650a7354U, - 0x766a0abbU, - 0x81c2c92eU, - 0x92722c85U, - 0xa2bfe8a1U, - 0xa81a664bU, // 40 - 0xc24b8b70U, - 0xc76c51a3U, - 0xd192e819U, - 0xd6990624U, - 0xf40e3585U, - 0x106aa070U, - 0x19a4c116U, - 0x1e376c08U, - 0x2748774cU, - 0x34b0bcb5U, // 50 - 0x391c0cb3U, - 0x4ed8aa4aU, - 0x5b9cca4fU, - 0x682e6ff3U, - 0x748f82eeU, - 0x78a5636fU, - 0x84c87814U, - 0x8cc70208U, - 0x90befffaU, - 0xa4506cebU, // 60 - 0xbef9a3f7U, - 0xc67178f2U, - 0x98c7e2a2U, - 0xfc08884dU, - 0xcd2a11aeU, - 0x510e527fU, - 0x9b05688cU, - 0xC3910C8EU, - 0xfb6feee7U, - 0x2a01a605U, // 70 - 0x0c2e12e0U, - 0x4498517BU, - 0x6a09e667U, - 0xa4ce148bU, - 0x95F61999U, - 0xc19bf174U, - 0xBB67AE85U, - 0x3C6EF372U, - 0xA54FF53AU, - 0x1F83D9ABU, // 80 - 0x5BE0CD19U, - 0x5C5C5C5CU, - 0x36363636U, - 0x80000000U, - 0x000003FFU, - 0x00000280U, - 0x000004a0U, - 0x00000300U -}; - -#define rotl(x,y) rotate(x,y) -#define Ch(x,y,z) bitselect(z,y,x) -#define Maj(x,y,z) Ch((x^z),y,z) - -#define EndianSwap(n) (rotl(n & ES[0], 24U)|rotl(n & ES[1], 8U)) - -#define Tr2(x) (rotl(x, 30U) ^ rotl(x, 19U) ^ rotl(x, 10U)) -#define Tr1(x) (rotl(x, 26U) ^ rotl(x, 21U) ^ rotl(x, 7U)) -#define Wr2(x) (rotl(x, 25U) ^ rotl(x, 14U) ^ (x>>3U)) -#define Wr1(x) (rotl(x, 15U) ^ rotl(x, 13U) ^ (x>>10U)) - -#define RND(a, b, c, d, e, f, g, h, k) \ - h += Tr1(e); \ - h += Ch(e, f, g); \ - h += k; \ - d += h; \ - h += Tr2(a); \ - h += Maj(a, b, c); - -void SHA256(uint4*restrict state0,uint4*restrict state1, const uint4 block0, const uint4 block1, const uint4 block2, const uint4 block3) -{ - uint4 S0 = *state0; - uint4 S1 = *state1; - -#define A S0.x -#define B S0.y -#define C S0.z -#define D S0.w -#define E S1.x -#define F S1.y -#define G S1.z -#define H S1.w - - uint4 W[4]; - - W[ 0].x = block0.x; - RND(A,B,C,D,E,F,G,H, W[0].x+ K[0]); - W[ 0].y = block0.y; - RND(H,A,B,C,D,E,F,G, W[0].y+ K[1]); - W[ 0].z = block0.z; - RND(G,H,A,B,C,D,E,F, W[0].z+ K[2]); - W[ 0].w = block0.w; - RND(F,G,H,A,B,C,D,E, W[0].w+ K[3]); - - W[ 1].x = block1.x; - RND(E,F,G,H,A,B,C,D, W[1].x+ K[4]); - W[ 1].y = block1.y; - RND(D,E,F,G,H,A,B,C, W[1].y+ K[5]); - W[ 1].z = block1.z; - RND(C,D,E,F,G,H,A,B, W[1].z+ K[6]); - W[ 1].w = block1.w; - RND(B,C,D,E,F,G,H,A, W[1].w+ K[7]); - - W[ 2].x = block2.x; - RND(A,B,C,D,E,F,G,H, W[2].x+ K[8]); - W[ 2].y = block2.y; - RND(H,A,B,C,D,E,F,G, W[2].y+ K[9]); - W[ 2].z = block2.z; - RND(G,H,A,B,C,D,E,F, W[2].z+ K[10]); - W[ 2].w = block2.w; - RND(F,G,H,A,B,C,D,E, W[2].w+ K[11]); - - W[ 3].x = block3.x; - RND(E,F,G,H,A,B,C,D, W[3].x+ K[12]); - W[ 3].y = block3.y; - RND(D,E,F,G,H,A,B,C, W[3].y+ K[13]); - W[ 3].z = block3.z; - RND(C,D,E,F,G,H,A,B, W[3].z+ K[14]); - W[ 3].w = block3.w; - RND(B,C,D,E,F,G,H,A, W[3].w+ K[76]); - - W[ 0].x += Wr1(W[ 3].z) + W[ 2].y + Wr2(W[ 0].y); - RND(A,B,C,D,E,F,G,H, W[0].x+ K[15]); - - W[ 0].y += Wr1(W[ 3].w) + W[ 2].z + Wr2(W[ 0].z); - RND(H,A,B,C,D,E,F,G, W[0].y+ K[16]); - - W[ 0].z += Wr1(W[ 0].x) + W[ 2].w + Wr2(W[ 0].w); - RND(G,H,A,B,C,D,E,F, W[0].z+ K[17]); - - W[ 0].w += Wr1(W[ 0].y) + W[ 3].x + Wr2(W[ 1].x); - RND(F,G,H,A,B,C,D,E, W[0].w+ K[18]); - - W[ 1].x += Wr1(W[ 0].z) + W[ 3].y + Wr2(W[ 1].y); - RND(E,F,G,H,A,B,C,D, W[1].x+ K[19]); - - W[ 1].y += Wr1(W[ 0].w) + W[ 3].z + Wr2(W[ 1].z); - RND(D,E,F,G,H,A,B,C, W[1].y+ K[20]); - - W[ 1].z += Wr1(W[ 1].x) + W[ 3].w + Wr2(W[ 1].w); - RND(C,D,E,F,G,H,A,B, W[1].z+ K[21]); - - W[ 1].w += Wr1(W[ 1].y) + W[ 0].x + Wr2(W[ 2].x); - RND(B,C,D,E,F,G,H,A, W[1].w+ K[22]); - - W[ 2].x += Wr1(W[ 1].z) + W[ 0].y + Wr2(W[ 2].y); - RND(A,B,C,D,E,F,G,H, W[2].x+ K[23]); - - W[ 2].y += Wr1(W[ 1].w) + W[ 0].z + Wr2(W[ 2].z); - RND(H,A,B,C,D,E,F,G, W[2].y+ K[24]); - - W[ 2].z += Wr1(W[ 2].x) + W[ 0].w + Wr2(W[ 2].w); - RND(G,H,A,B,C,D,E,F, W[2].z+ K[25]); - - W[ 2].w += Wr1(W[ 2].y) + W[ 1].x + Wr2(W[ 3].x); - RND(F,G,H,A,B,C,D,E, W[2].w+ K[26]); - - W[ 3].x += Wr1(W[ 2].z) + W[ 1].y + Wr2(W[ 3].y); - RND(E,F,G,H,A,B,C,D, W[3].x+ K[27]); - - W[ 3].y += Wr1(W[ 2].w) + W[ 1].z + Wr2(W[ 3].z); - RND(D,E,F,G,H,A,B,C, W[3].y+ K[28]); - - W[ 3].z += Wr1(W[ 3].x) + W[ 1].w + Wr2(W[ 3].w); - RND(C,D,E,F,G,H,A,B, W[3].z+ K[29]); - - W[ 3].w += Wr1(W[ 3].y) + W[ 2].x + Wr2(W[ 0].x); - RND(B,C,D,E,F,G,H,A, W[3].w+ K[30]); - - W[ 0].x += Wr1(W[ 3].z) + W[ 2].y + Wr2(W[ 0].y); - RND(A,B,C,D,E,F,G,H, W[0].x+ K[31]); - - W[ 0].y += Wr1(W[ 3].w) + W[ 2].z + Wr2(W[ 0].z); - RND(H,A,B,C,D,E,F,G, W[0].y+ K[32]); - - W[ 0].z += Wr1(W[ 0].x) + W[ 2].w + Wr2(W[ 0].w); - RND(G,H,A,B,C,D,E,F, W[0].z+ K[33]); - - W[ 0].w += Wr1(W[ 0].y) + W[ 3].x + Wr2(W[ 1].x); - RND(F,G,H,A,B,C,D,E, W[0].w+ K[34]); - - W[ 1].x += Wr1(W[ 0].z) + W[ 3].y + Wr2(W[ 1].y); - RND(E,F,G,H,A,B,C,D, W[1].x+ K[35]); - - W[ 1].y += Wr1(W[ 0].w) + W[ 3].z + Wr2(W[ 1].z); - RND(D,E,F,G,H,A,B,C, W[1].y+ K[36]); - - W[ 1].z += Wr1(W[ 1].x) + W[ 3].w + Wr2(W[ 1].w); - RND(C,D,E,F,G,H,A,B, W[1].z+ K[37]); - - W[ 1].w += Wr1(W[ 1].y) + W[ 0].x + Wr2(W[ 2].x); - RND(B,C,D,E,F,G,H,A, W[1].w+ K[38]); - - W[ 2].x += Wr1(W[ 1].z) + W[ 0].y + Wr2(W[ 2].y); - RND(A,B,C,D,E,F,G,H, W[2].x+ K[39]); - - W[ 2].y += Wr1(W[ 1].w) + W[ 0].z + Wr2(W[ 2].z); - RND(H,A,B,C,D,E,F,G, W[2].y+ K[40]); - - W[ 2].z += Wr1(W[ 2].x) + W[ 0].w + Wr2(W[ 2].w); - RND(G,H,A,B,C,D,E,F, W[2].z+ K[41]); - - W[ 2].w += Wr1(W[ 2].y) + W[ 1].x + Wr2(W[ 3].x); - RND(F,G,H,A,B,C,D,E, W[2].w+ K[42]); - - W[ 3].x += Wr1(W[ 2].z) + W[ 1].y + Wr2(W[ 3].y); - RND(E,F,G,H,A,B,C,D, W[3].x+ K[43]); - - W[ 3].y += Wr1(W[ 2].w) + W[ 1].z + Wr2(W[ 3].z); - RND(D,E,F,G,H,A,B,C, W[3].y+ K[44]); - - W[ 3].z += Wr1(W[ 3].x) + W[ 1].w + Wr2(W[ 3].w); - RND(C,D,E,F,G,H,A,B, W[3].z+ K[45]); - - W[ 3].w += Wr1(W[ 3].y) + W[ 2].x + Wr2(W[ 0].x); - RND(B,C,D,E,F,G,H,A, W[3].w+ K[46]); - - W[ 0].x += Wr1(W[ 3].z) + W[ 2].y + Wr2(W[ 0].y); - RND(A,B,C,D,E,F,G,H, W[0].x+ K[47]); - - W[ 0].y += Wr1(W[ 3].w) + W[ 2].z + Wr2(W[ 0].z); - RND(H,A,B,C,D,E,F,G, W[0].y+ K[48]); - - W[ 0].z += Wr1(W[ 0].x) + W[ 2].w + Wr2(W[ 0].w); - RND(G,H,A,B,C,D,E,F, W[0].z+ K[49]); - - W[ 0].w += Wr1(W[ 0].y) + W[ 3].x + Wr2(W[ 1].x); - RND(F,G,H,A,B,C,D,E, W[0].w+ K[50]); - - W[ 1].x += Wr1(W[ 0].z) + W[ 3].y + Wr2(W[ 1].y); - RND(E,F,G,H,A,B,C,D, W[1].x+ K[51]); - - W[ 1].y += Wr1(W[ 0].w) + W[ 3].z + Wr2(W[ 1].z); - RND(D,E,F,G,H,A,B,C, W[1].y+ K[52]); - - W[ 1].z += Wr1(W[ 1].x) + W[ 3].w + Wr2(W[ 1].w); - RND(C,D,E,F,G,H,A,B, W[1].z+ K[53]); - - W[ 1].w += Wr1(W[ 1].y) + W[ 0].x + Wr2(W[ 2].x); - RND(B,C,D,E,F,G,H,A, W[1].w+ K[54]); - - W[ 2].x += Wr1(W[ 1].z) + W[ 0].y + Wr2(W[ 2].y); - RND(A,B,C,D,E,F,G,H, W[2].x+ K[55]); - - W[ 2].y += Wr1(W[ 1].w) + W[ 0].z + Wr2(W[ 2].z); - RND(H,A,B,C,D,E,F,G, W[2].y+ K[56]); - - W[ 2].z += Wr1(W[ 2].x) + W[ 0].w + Wr2(W[ 2].w); - RND(G,H,A,B,C,D,E,F, W[2].z+ K[57]); - - W[ 2].w += Wr1(W[ 2].y) + W[ 1].x + Wr2(W[ 3].x); - RND(F,G,H,A,B,C,D,E, W[2].w+ K[58]); - - W[ 3].x += Wr1(W[ 2].z) + W[ 1].y + Wr2(W[ 3].y); - RND(E,F,G,H,A,B,C,D, W[3].x+ K[59]); - - W[ 3].y += Wr1(W[ 2].w) + W[ 1].z + Wr2(W[ 3].z); - RND(D,E,F,G,H,A,B,C, W[3].y+ K[60]); - - W[ 3].z += Wr1(W[ 3].x) + W[ 1].w + Wr2(W[ 3].w); - RND(C,D,E,F,G,H,A,B, W[3].z+ K[61]); - - W[ 3].w += Wr1(W[ 3].y) + W[ 2].x + Wr2(W[ 0].x); - RND(B,C,D,E,F,G,H,A, W[3].w+ K[62]); - -#undef A -#undef B -#undef C -#undef D -#undef E -#undef F -#undef G -#undef H - - *state0 += S0; - *state1 += S1; -} - -void SHA256_fresh(uint4*restrict state0,uint4*restrict state1, const uint4 block0, const uint4 block1, const uint4 block2, const uint4 block3) -{ -#define A (*state0).x -#define B (*state0).y -#define C (*state0).z -#define D (*state0).w -#define E (*state1).x -#define F (*state1).y -#define G (*state1).z -#define H (*state1).w - - uint4 W[4]; - - W[0].x = block0.x; - D= K[63] +W[0].x; - H= K[64] +W[0].x; - - W[0].y = block0.y; - C= K[65] +Tr1(D)+Ch(D, K[66], K[67])+W[0].y; - G= K[68] +C+Tr2(H)+Ch(H, K[69] ,K[70]); - - W[0].z = block0.z; - B= K[71] +Tr1(C)+Ch(C,D,K[66])+W[0].z; - F= K[72] +B+Tr2(G)+Maj(G,H, K[73]); - - W[0].w = block0.w; - A= K[74] +Tr1(B)+Ch(B,C,D)+W[0].w; - E= K[75] +A+Tr2(F)+Maj(F,G,H); - - W[1].x = block1.x; - RND(E,F,G,H,A,B,C,D, W[1].x+ K[4]); - W[1].y = block1.y; - RND(D,E,F,G,H,A,B,C, W[1].y+ K[5]); - W[1].z = block1.z; - RND(C,D,E,F,G,H,A,B, W[1].z+ K[6]); - W[1].w = block1.w; - RND(B,C,D,E,F,G,H,A, W[1].w+ K[7]); - - W[2].x = block2.x; - RND(A,B,C,D,E,F,G,H, W[2].x+ K[8]); - W[2].y = block2.y; - RND(H,A,B,C,D,E,F,G, W[2].y+ K[9]); - W[2].z = block2.z; - RND(G,H,A,B,C,D,E,F, W[2].z+ K[10]); - W[2].w = block2.w; - RND(F,G,H,A,B,C,D,E, W[2].w+ K[11]); - - W[3].x = block3.x; - RND(E,F,G,H,A,B,C,D, W[3].x+ K[12]); - W[3].y = block3.y; - RND(D,E,F,G,H,A,B,C, W[3].y+ K[13]); - W[3].z = block3.z; - RND(C,D,E,F,G,H,A,B, W[3].z+ K[14]); - W[3].w = block3.w; - RND(B,C,D,E,F,G,H,A, W[3].w+ K[76]); - - W[0].x += Wr1(W[3].z) + W[2].y + Wr2(W[0].y); - RND(A,B,C,D,E,F,G,H, W[0].x+ K[15]); - - W[0].y += Wr1(W[3].w) + W[2].z + Wr2(W[0].z); - RND(H,A,B,C,D,E,F,G, W[0].y+ K[16]); - - W[0].z += Wr1(W[0].x) + W[2].w + Wr2(W[0].w); - RND(G,H,A,B,C,D,E,F, W[0].z+ K[17]); - - W[0].w += Wr1(W[0].y) + W[3].x + Wr2(W[1].x); - RND(F,G,H,A,B,C,D,E, W[0].w+ K[18]); - - W[1].x += Wr1(W[0].z) + W[3].y + Wr2(W[1].y); - RND(E,F,G,H,A,B,C,D, W[1].x+ K[19]); - - W[1].y += Wr1(W[0].w) + W[3].z + Wr2(W[1].z); - RND(D,E,F,G,H,A,B,C, W[1].y+ K[20]); - - W[1].z += Wr1(W[1].x) + W[3].w + Wr2(W[1].w); - RND(C,D,E,F,G,H,A,B, W[1].z+ K[21]); - - W[1].w += Wr1(W[1].y) + W[0].x + Wr2(W[2].x); - RND(B,C,D,E,F,G,H,A, W[1].w+ K[22]); - - W[2].x += Wr1(W[1].z) + W[0].y + Wr2(W[2].y); - RND(A,B,C,D,E,F,G,H, W[2].x+ K[23]); - - W[2].y += Wr1(W[1].w) + W[0].z + Wr2(W[2].z); - RND(H,A,B,C,D,E,F,G, W[2].y+ K[24]); - - W[2].z += Wr1(W[2].x) + W[0].w + Wr2(W[2].w); - RND(G,H,A,B,C,D,E,F, W[2].z+ K[25]); - - W[2].w += Wr1(W[2].y) + W[1].x + Wr2(W[3].x); - RND(F,G,H,A,B,C,D,E, W[2].w+ K[26]); - - W[3].x += Wr1(W[2].z) + W[1].y + Wr2(W[3].y); - RND(E,F,G,H,A,B,C,D, W[3].x+ K[27]); - - W[3].y += Wr1(W[2].w) + W[1].z + Wr2(W[3].z); - RND(D,E,F,G,H,A,B,C, W[3].y+ K[28]); - - W[3].z += Wr1(W[3].x) + W[1].w + Wr2(W[3].w); - RND(C,D,E,F,G,H,A,B, W[3].z+ K[29]); - - W[3].w += Wr1(W[3].y) + W[2].x + Wr2(W[0].x); - RND(B,C,D,E,F,G,H,A, W[3].w+ K[30]); - - W[0].x += Wr1(W[3].z) + W[2].y + Wr2(W[0].y); - RND(A,B,C,D,E,F,G,H, W[0].x+ K[31]); - - W[0].y += Wr1(W[3].w) + W[2].z + Wr2(W[0].z); - RND(H,A,B,C,D,E,F,G, W[0].y+ K[32]); - - W[0].z += Wr1(W[0].x) + W[2].w + Wr2(W[0].w); - RND(G,H,A,B,C,D,E,F, W[0].z+ K[33]); - - W[0].w += Wr1(W[0].y) + W[3].x + Wr2(W[1].x); - RND(F,G,H,A,B,C,D,E, W[0].w+ K[34]); - - W[1].x += Wr1(W[0].z) + W[3].y + Wr2(W[1].y); - RND(E,F,G,H,A,B,C,D, W[1].x+ K[35]); - - W[1].y += Wr1(W[0].w) + W[3].z + Wr2(W[1].z); - RND(D,E,F,G,H,A,B,C, W[1].y+ K[36]); - - W[1].z += Wr1(W[1].x) + W[3].w + Wr2(W[1].w); - RND(C,D,E,F,G,H,A,B, W[1].z+ K[37]); - - W[1].w += Wr1(W[1].y) + W[0].x + Wr2(W[2].x); - RND(B,C,D,E,F,G,H,A, W[1].w+ K[38]); - - W[2].x += Wr1(W[1].z) + W[0].y + Wr2(W[2].y); - RND(A,B,C,D,E,F,G,H, W[2].x+ K[39]); - - W[2].y += Wr1(W[1].w) + W[0].z + Wr2(W[2].z); - RND(H,A,B,C,D,E,F,G, W[2].y+ K[40]); - - W[2].z += Wr1(W[2].x) + W[0].w + Wr2(W[2].w); - RND(G,H,A,B,C,D,E,F, W[2].z+ K[41]); - - W[2].w += Wr1(W[2].y) + W[1].x + Wr2(W[3].x); - RND(F,G,H,A,B,C,D,E, W[2].w+ K[42]); - - W[3].x += Wr1(W[2].z) + W[1].y + Wr2(W[3].y); - RND(E,F,G,H,A,B,C,D, W[3].x+ K[43]); - - W[3].y += Wr1(W[2].w) + W[1].z + Wr2(W[3].z); - RND(D,E,F,G,H,A,B,C, W[3].y+ K[44]); - - W[3].z += Wr1(W[3].x) + W[1].w + Wr2(W[3].w); - RND(C,D,E,F,G,H,A,B, W[3].z+ K[45]); - - W[3].w += Wr1(W[3].y) + W[2].x + Wr2(W[0].x); - RND(B,C,D,E,F,G,H,A, W[3].w+ K[46]); - - W[0].x += Wr1(W[3].z) + W[2].y + Wr2(W[0].y); - RND(A,B,C,D,E,F,G,H, W[0].x+ K[47]); - - W[0].y += Wr1(W[3].w) + W[2].z + Wr2(W[0].z); - RND(H,A,B,C,D,E,F,G, W[0].y+ K[48]); - - W[0].z += Wr1(W[0].x) + W[2].w + Wr2(W[0].w); - RND(G,H,A,B,C,D,E,F, W[0].z+ K[49]); - - W[0].w += Wr1(W[0].y) + W[3].x + Wr2(W[1].x); - RND(F,G,H,A,B,C,D,E, W[0].w+ K[50]); - - W[1].x += Wr1(W[0].z) + W[3].y + Wr2(W[1].y); - RND(E,F,G,H,A,B,C,D, W[1].x+ K[51]); - - W[1].y += Wr1(W[0].w) + W[3].z + Wr2(W[1].z); - RND(D,E,F,G,H,A,B,C, W[1].y+ K[52]); - - W[1].z += Wr1(W[1].x) + W[3].w + Wr2(W[1].w); - RND(C,D,E,F,G,H,A,B, W[1].z+ K[53]); - - W[1].w += Wr1(W[1].y) + W[0].x + Wr2(W[2].x); - RND(B,C,D,E,F,G,H,A, W[1].w+ K[54]); - - W[2].x += Wr1(W[1].z) + W[0].y + Wr2(W[2].y); - RND(A,B,C,D,E,F,G,H, W[2].x+ K[55]); - - W[2].y += Wr1(W[1].w) + W[0].z + Wr2(W[2].z); - RND(H,A,B,C,D,E,F,G, W[2].y+ K[56]); - - W[2].z += Wr1(W[2].x) + W[0].w + Wr2(W[2].w); - RND(G,H,A,B,C,D,E,F, W[2].z+ K[57]); - - W[2].w += Wr1(W[2].y) + W[1].x + Wr2(W[3].x); - RND(F,G,H,A,B,C,D,E, W[2].w+ K[58]); - - W[3].x += Wr1(W[2].z) + W[1].y + Wr2(W[3].y); - RND(E,F,G,H,A,B,C,D, W[3].x+ K[59]); - - W[3].y += Wr1(W[2].w) + W[1].z + Wr2(W[3].z); - RND(D,E,F,G,H,A,B,C, W[3].y+ K[60]); - - W[3].z += Wr1(W[3].x) + W[1].w + Wr2(W[3].w); - RND(C,D,E,F,G,H,A,B, W[3].z+ K[61]); - - W[3].w += Wr1(W[3].y) + W[2].x + Wr2(W[0].x); - RND(B,C,D,E,F,G,H,A, W[3].w+ K[62]); - -#undef A -#undef B -#undef C -#undef D -#undef E -#undef F -#undef G -#undef H - - *state0 += (uint4)(K[73], K[77], K[78], K[79]); - *state1 += (uint4)(K[66], K[67], K[80], K[81]); -} - -__constant uint fixedW[64] = -{ - 0x428a2f99,0xf1374491,0xb5c0fbcf,0xe9b5dba5,0x3956c25b,0x59f111f1,0x923f82a4,0xab1c5ed5, - 0xd807aa98,0x12835b01,0x243185be,0x550c7dc3,0x72be5d74,0x80deb1fe,0x9bdc06a7,0xc19bf794, - 0xf59b89c2,0x73924787,0x23c6886e,0xa42ca65c,0x15ed3627,0x4d6edcbf,0xe28217fc,0xef02488f, - 0xb707775c,0x0468c23f,0xe7e72b4c,0x49e1f1a2,0x4b99c816,0x926d1570,0xaa0fc072,0xadb36e2c, - 0xad87a3ea,0xbcb1d3a3,0x7b993186,0x562b9420,0xbff3ca0c,0xda4b0c23,0x6cd8711a,0x8f337caa, - 0xc91b1417,0xc359dce1,0xa83253a7,0x3b13c12d,0x9d3d725d,0xd9031a84,0xb1a03340,0x16f58012, - 0xe64fb6a2,0xe84d923a,0xe93a5730,0x09837686,0x078ff753,0x29833341,0xd5de0b7e,0x6948ccf4, - 0xe0a1adbe,0x7c728e11,0x511c78e4,0x315b45bd,0xfca71413,0xea28f96a,0x79703128,0x4e1ef848, -}; - -void SHA256_fixed(uint4*restrict state0,uint4*restrict state1) -{ - uint4 S0 = *state0; - uint4 S1 = *state1; - -#define A S0.x -#define B S0.y -#define C S0.z -#define D S0.w -#define E S1.x -#define F S1.y -#define G S1.z -#define H S1.w - - RND(A,B,C,D,E,F,G,H, fixedW[0]); - RND(H,A,B,C,D,E,F,G, fixedW[1]); - RND(G,H,A,B,C,D,E,F, fixedW[2]); - RND(F,G,H,A,B,C,D,E, fixedW[3]); - RND(E,F,G,H,A,B,C,D, fixedW[4]); - RND(D,E,F,G,H,A,B,C, fixedW[5]); - RND(C,D,E,F,G,H,A,B, fixedW[6]); - RND(B,C,D,E,F,G,H,A, fixedW[7]); - RND(A,B,C,D,E,F,G,H, fixedW[8]); - RND(H,A,B,C,D,E,F,G, fixedW[9]); - RND(G,H,A,B,C,D,E,F, fixedW[10]); - RND(F,G,H,A,B,C,D,E, fixedW[11]); - RND(E,F,G,H,A,B,C,D, fixedW[12]); - RND(D,E,F,G,H,A,B,C, fixedW[13]); - RND(C,D,E,F,G,H,A,B, fixedW[14]); - RND(B,C,D,E,F,G,H,A, fixedW[15]); - RND(A,B,C,D,E,F,G,H, fixedW[16]); - RND(H,A,B,C,D,E,F,G, fixedW[17]); - RND(G,H,A,B,C,D,E,F, fixedW[18]); - RND(F,G,H,A,B,C,D,E, fixedW[19]); - RND(E,F,G,H,A,B,C,D, fixedW[20]); - RND(D,E,F,G,H,A,B,C, fixedW[21]); - RND(C,D,E,F,G,H,A,B, fixedW[22]); - RND(B,C,D,E,F,G,H,A, fixedW[23]); - RND(A,B,C,D,E,F,G,H, fixedW[24]); - RND(H,A,B,C,D,E,F,G, fixedW[25]); - RND(G,H,A,B,C,D,E,F, fixedW[26]); - RND(F,G,H,A,B,C,D,E, fixedW[27]); - RND(E,F,G,H,A,B,C,D, fixedW[28]); - RND(D,E,F,G,H,A,B,C, fixedW[29]); - RND(C,D,E,F,G,H,A,B, fixedW[30]); - RND(B,C,D,E,F,G,H,A, fixedW[31]); - RND(A,B,C,D,E,F,G,H, fixedW[32]); - RND(H,A,B,C,D,E,F,G, fixedW[33]); - RND(G,H,A,B,C,D,E,F, fixedW[34]); - RND(F,G,H,A,B,C,D,E, fixedW[35]); - RND(E,F,G,H,A,B,C,D, fixedW[36]); - RND(D,E,F,G,H,A,B,C, fixedW[37]); - RND(C,D,E,F,G,H,A,B, fixedW[38]); - RND(B,C,D,E,F,G,H,A, fixedW[39]); - RND(A,B,C,D,E,F,G,H, fixedW[40]); - RND(H,A,B,C,D,E,F,G, fixedW[41]); - RND(G,H,A,B,C,D,E,F, fixedW[42]); - RND(F,G,H,A,B,C,D,E, fixedW[43]); - RND(E,F,G,H,A,B,C,D, fixedW[44]); - RND(D,E,F,G,H,A,B,C, fixedW[45]); - RND(C,D,E,F,G,H,A,B, fixedW[46]); - RND(B,C,D,E,F,G,H,A, fixedW[47]); - RND(A,B,C,D,E,F,G,H, fixedW[48]); - RND(H,A,B,C,D,E,F,G, fixedW[49]); - RND(G,H,A,B,C,D,E,F, fixedW[50]); - RND(F,G,H,A,B,C,D,E, fixedW[51]); - RND(E,F,G,H,A,B,C,D, fixedW[52]); - RND(D,E,F,G,H,A,B,C, fixedW[53]); - RND(C,D,E,F,G,H,A,B, fixedW[54]); - RND(B,C,D,E,F,G,H,A, fixedW[55]); - RND(A,B,C,D,E,F,G,H, fixedW[56]); - RND(H,A,B,C,D,E,F,G, fixedW[57]); - RND(G,H,A,B,C,D,E,F, fixedW[58]); - RND(F,G,H,A,B,C,D,E, fixedW[59]); - RND(E,F,G,H,A,B,C,D, fixedW[60]); - RND(D,E,F,G,H,A,B,C, fixedW[61]); - RND(C,D,E,F,G,H,A,B, fixedW[62]); - RND(B,C,D,E,F,G,H,A, fixedW[63]); - -#undef A -#undef B -#undef C -#undef D -#undef E -#undef F -#undef G -#undef H - *state0 += S0; - *state1 += S1; -} - -void shittify(uint4 B[8]) -{ - uint4 tmp[4]; - tmp[0] = (uint4)(B[1].x,B[2].y,B[3].z,B[0].w); - tmp[1] = (uint4)(B[2].x,B[3].y,B[0].z,B[1].w); - tmp[2] = (uint4)(B[3].x,B[0].y,B[1].z,B[2].w); - tmp[3] = (uint4)(B[0].x,B[1].y,B[2].z,B[3].w); - -#pragma unroll - for(uint i=0; i<4; ++i) - B[i] = EndianSwap(tmp[i]); - - tmp[0] = (uint4)(B[5].x,B[6].y,B[7].z,B[4].w); - tmp[1] = (uint4)(B[6].x,B[7].y,B[4].z,B[5].w); - tmp[2] = (uint4)(B[7].x,B[4].y,B[5].z,B[6].w); - tmp[3] = (uint4)(B[4].x,B[5].y,B[6].z,B[7].w); - -#pragma unroll - for(uint i=0; i<4; ++i) - B[i+4] = EndianSwap(tmp[i]); -} - -void unshittify(uint4 B[8]) -{ - uint4 tmp[4]; - tmp[0] = (uint4)(B[3].x,B[2].y,B[1].z,B[0].w); - tmp[1] = (uint4)(B[0].x,B[3].y,B[2].z,B[1].w); - tmp[2] = (uint4)(B[1].x,B[0].y,B[3].z,B[2].w); - tmp[3] = (uint4)(B[2].x,B[1].y,B[0].z,B[3].w); - -#pragma unroll - for(uint i=0; i<4; ++i) - B[i] = EndianSwap(tmp[i]); - - tmp[0] = (uint4)(B[7].x,B[6].y,B[5].z,B[4].w); - tmp[1] = (uint4)(B[4].x,B[7].y,B[6].z,B[5].w); - tmp[2] = (uint4)(B[5].x,B[4].y,B[7].z,B[6].w); - tmp[3] = (uint4)(B[6].x,B[5].y,B[4].z,B[7].w); - -#pragma unroll - for(uint i=0; i<4; ++i) - B[i+4] = EndianSwap(tmp[i]); -} - -void salsa(uint4 B[8]) -{ - uint4 w[4]; - -#pragma unroll - for(uint i=0; i<4; ++i) - w[i] = (B[i]^=B[i+4]); - -#pragma unroll - for(uint i=0; i<4; ++i) - { - w[0] ^= rotl(w[3] +w[2] , 7U); - w[1] ^= rotl(w[0] +w[3] , 9U); - w[2] ^= rotl(w[1] +w[0] ,13U); - w[3] ^= rotl(w[2] +w[1] ,18U); - w[2] ^= rotl(w[3].wxyz+w[0].zwxy, 7U); - w[1] ^= rotl(w[2].wxyz+w[3].zwxy, 9U); - w[0] ^= rotl(w[1].wxyz+w[2].zwxy,13U); - w[3] ^= rotl(w[0].wxyz+w[1].zwxy,18U); - } - -#pragma unroll - for(uint i=0; i<4; ++i) - w[i] = (B[i+4]^=(B[i]+=w[i])); - -#pragma unroll - for(uint i=0; i<4; ++i) - { - w[0] ^= rotl(w[3] +w[2] , 7U); - w[1] ^= rotl(w[0] +w[3] , 9U); - w[2] ^= rotl(w[1] +w[0] ,13U); - w[3] ^= rotl(w[2] +w[1] ,18U); - w[2] ^= rotl(w[3].wxyz+w[0].zwxy, 7U); - w[1] ^= rotl(w[2].wxyz+w[3].zwxy, 9U); - w[0] ^= rotl(w[1].wxyz+w[2].zwxy,13U); - w[3] ^= rotl(w[0].wxyz+w[1].zwxy,18U); - } - -#pragma unroll - for(uint i=0; i<4; ++i) - B[i+4] += w[i]; -} - -#define Coord(x,y,z) x+y*(x ## SIZE)+z*(y ## SIZE)*(x ## SIZE) -#define CO Coord(z,x,y) - -void scrypt_core(uint4 X[8], __global uint4*restrict lookup) -{ - shittify(X); - const uint zSIZE = 8; - const uint ySIZE = (1024/LOOKUP_GAP+(1024%LOOKUP_GAP>0)); - const uint xSIZE = CONCURRENT_THREADS; - uint x = get_global_id(0)%xSIZE; - - for(uint y=0; y<1024/LOOKUP_GAP; ++y) - { -#pragma unroll - for(uint z=0; z> n) -#define ROTR(x, n) ((x >> n) | (x << ((sizeof(x) << 3) - n))) -#define CH(x, y, z) ((x & y) ^ (~x & z)) -#define MAJ(x, y, z) ((x & y) ^ (x & z) ^ (y & z)) - -#define SHA256_F1(x) (ROTR(x, 2) ^ ROTR(x, 13) ^ ROTR(x, 22)) -#define SHA256_F2(x) (ROTR(x, 6) ^ ROTR(x, 11) ^ ROTR(x, 25)) -#define SHA256_F3(x) (ROTR(x, 7) ^ ROTR(x, 18) ^ SHFR(x, 3)) -#define SHA256_F4(x) (ROTR(x, 17) ^ ROTR(x, 19) ^ SHFR(x, 10)) - #define UNPACK32(x, str) \ { \ *((str) + 3) = (uint8_t) ((x) ); \ diff --git a/sha2.h b/sha2.h index faa4f61c42..71d4404cbd 100644 --- a/sha2.h +++ b/sha2.h @@ -41,6 +41,16 @@ #define SHA256_DIGEST_SIZE ( 256 / 8) #define SHA256_BLOCK_SIZE ( 512 / 8) +#define SHFR(x, n) (x >> n) +#define ROTR(x, n) ((x >> n) | (x << ((sizeof(x) << 3) - n))) +#define CH(x, y, z) ((x & y) ^ (~x & z)) +#define MAJ(x, y, z) ((x & y) ^ (x & z) ^ (y & z)) + +#define SHA256_F1(x) (ROTR(x, 2) ^ ROTR(x, 13) ^ ROTR(x, 22)) +#define SHA256_F2(x) (ROTR(x, 6) ^ ROTR(x, 11) ^ ROTR(x, 25)) +#define SHA256_F3(x) (ROTR(x, 7) ^ ROTR(x, 18) ^ SHFR(x, 3)) +#define SHA256_F4(x) (ROTR(x, 17) ^ ROTR(x, 19) ^ SHFR(x, 10)) + typedef struct { unsigned int tot_len; unsigned int len; @@ -48,6 +58,8 @@ typedef struct { uint32_t h[8]; } sha256_ctx; +extern uint32_t sha256_k[64]; + void sha256_init(sha256_ctx * ctx); void sha256_update(sha256_ctx *ctx, const unsigned char *message, unsigned int len); diff --git a/spi-context.c b/spi-context.c new file mode 100644 index 0000000000..e0ec139580 --- /dev/null +++ b/spi-context.c @@ -0,0 +1,94 @@ +/* + * generic SPI functions + * + * Copyright 2013, 2014 Zefir Kurtisi + * + * 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 "spi-context.h" + +#include "logging.h" +#include "miner.h" + +#include +#include +#include +#include + +struct spi_ctx *spi_init(struct spi_config *config) +{ + char dev_fname[PATH_MAX]; + struct spi_ctx *ctx; + + if (config == NULL) + return NULL; + + sprintf(dev_fname, SPI_DEVICE_TEMPLATE, config->bus, config->cs_line); + + int fd = open(dev_fname, O_RDWR); + if (fd < 0) { + applog(LOG_ERR, "SPI: Can not open SPI device %s", dev_fname); + return NULL; + } + + if ((ioctl(fd, SPI_IOC_WR_MODE, &config->mode) < 0) || + (ioctl(fd, SPI_IOC_RD_MODE, &config->mode) < 0) || + (ioctl(fd, SPI_IOC_WR_BITS_PER_WORD, &config->bits) < 0) || + (ioctl(fd, SPI_IOC_RD_BITS_PER_WORD, &config->bits) < 0) || + (ioctl(fd, SPI_IOC_WR_MAX_SPEED_HZ, &config->speed) < 0) || + (ioctl(fd, SPI_IOC_RD_MAX_SPEED_HZ, &config->speed) < 0)) { + applog(LOG_ERR, "SPI: ioctl error on SPI device %s", dev_fname); + close(fd); + return NULL; + } + + ctx = malloc(sizeof(*ctx)); + assert(ctx != NULL); + + ctx->fd = fd; + ctx->config = *config; + applog(LOG_WARNING, "SPI '%s': mode=%hhu, bits=%hhu, speed=%u", + dev_fname, ctx->config.mode, ctx->config.bits, + ctx->config.speed); + return ctx; +} + +extern void spi_exit(struct spi_ctx *ctx) +{ + if (NULL == ctx) + return; + + close(ctx->fd); + free(ctx); +} + +extern bool spi_transfer(struct spi_ctx *ctx, uint8_t *txbuf, + uint8_t *rxbuf, int len) +{ + struct spi_ioc_transfer xfr; + int ret; + + if (rxbuf != NULL) + memset(rxbuf, 0xff, len); + + ret = len; + + xfr.tx_buf = (unsigned long)txbuf; + xfr.rx_buf = (unsigned long)rxbuf; + xfr.len = len; + xfr.speed_hz = ctx->config.speed; + xfr.delay_usecs = ctx->config.delay; + xfr.bits_per_word = ctx->config.bits; + xfr.cs_change = 0; + xfr.pad = 0; + + ret = ioctl(ctx->fd, SPI_IOC_MESSAGE(1), &xfr); + if (ret < 1) + applog(LOG_ERR, "SPI: ioctl error on SPI device: %d", ret); + + return ret > 0; +} diff --git a/spi-context.h b/spi-context.h new file mode 100644 index 0000000000..a341e69ada --- /dev/null +++ b/spi-context.h @@ -0,0 +1,48 @@ +#ifndef SPI_CONTEXT_H +#define SPI_CONTEXT_H + +#include +#include +#include +#include + +#define SPI_DEVICE_TEMPLATE "/dev/spidev%d.%d" +#define DEFAULT_SPI_BUS 0 +#define DEFAULT_SPI_CS_LINE 0 +#define DEFAULT_SPI_MODE SPI_MODE_0 +#define DEFAULT_SPI_BITS_PER_WORD 8 +#define DEFAULT_SPI_SPEED 1500000 +#define DEFAULT_SPI_DELAY_USECS 0 + +struct spi_config { + int bus; + int cs_line; + uint8_t mode; + uint32_t speed; + uint8_t bits; + uint16_t delay; +}; + +static const struct spi_config default_spi_config = { + .bus = DEFAULT_SPI_BUS, + .cs_line = DEFAULT_SPI_CS_LINE, + .mode = DEFAULT_SPI_MODE, + .speed = DEFAULT_SPI_SPEED, + .bits = DEFAULT_SPI_BITS_PER_WORD, + .delay = DEFAULT_SPI_DELAY_USECS, +}; + +struct spi_ctx { + int fd; + struct spi_config config; +}; + +/* create SPI context with given configuration, returns NULL on failure */ +extern struct spi_ctx *spi_init(struct spi_config *config); +/* close descriptor and free resources */ +extern void spi_exit(struct spi_ctx *ctx); +/* process RX/TX transfer, ensure buffers are long enough */ +extern bool spi_transfer(struct spi_ctx *ctx, uint8_t *txbuf, + uint8_t *rxbuf, int len); + +#endif /* SPI_CONTEXT_H */ diff --git a/todo_ztex.txt b/todo_ztex.txt deleted file mode 100644 index 4014b04fb0..0000000000 --- a/todo_ztex.txt +++ /dev/null @@ -1,5 +0,0 @@ -- verify setting cgpu.status=DEAD does in fact stop the thread -- allow configuring bitstream directory -- HS fpga config -- allow configuring LIBZTEX_OVERHEATTHRESHOLD -- hotplug support? diff --git a/usbutils.c b/usbutils.c index 2f9166aae4..8c06882e36 100644 --- a/usbutils.c +++ b/usbutils.c @@ -1,6 +1,6 @@ /* * Copyright 2012-2013 Andrew Smith - * Copyright 2013 Con Kolivas + * Copyright 2013-2014 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 @@ -18,344 +18,960 @@ #include "miner.h" #include "usbutils.h" -#define NODEV(err) ((err) == LIBUSB_ERROR_NO_DEVICE || \ - (err) == LIBUSB_ERROR_PIPE || \ - (err) == LIBUSB_ERROR_OTHER) +static pthread_mutex_t cgusb_lock; +static pthread_mutex_t cgusbres_lock; +static cglock_t cgusb_fd_lock; +static cgtimer_t usb11_cgt; -#define NOCONTROLDEV(err) ((err) == LIBUSB_ERROR_NO_DEVICE || \ - (err) == LIBUSB_ERROR_OTHER) +#define NODEV(err) ((err) != LIBUSB_SUCCESS && (err) != LIBUSB_ERROR_TIMEOUT) - -#ifdef USE_BFLSC -#define DRV_BFLSC 1 -#endif - -#ifdef USE_BITFORCE -#define DRV_BITFORCE 2 -#endif +#define NOCONTROLDEV(err) ((err) < 0 && NODEV(err)) -#ifdef USE_MODMINER -#define DRV_MODMINER 3 -#endif - -#ifdef USE_ZTEX -#define DRV_ZTEX 4 -#endif +/* + * WARNING - these assume DEVLOCK(cgpu, pstate) is called first and + * DEVUNLOCK(cgpu, pstate) in called in the same function with the same pstate + * given to DEVLOCK. + * You must call DEVUNLOCK(cgpu, pstate) before exiting the function or it will leave + * the thread Cancelability unrestored + */ +#define DEVWLOCK(cgpu, _pth_state) do { \ + pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, &_pth_state); \ + cg_wlock(&cgpu->usbinfo.devlock); \ + } while (0) -#ifdef USE_ICARUS -#define DRV_ICARUS 5 -#endif +#define DEVWUNLOCK(cgpu, _pth_state) do { \ + cg_wunlock(&cgpu->usbinfo.devlock); \ + pthread_setcancelstate(_pth_state, NULL); \ + } while (0) -#ifdef USE_AVALON -#define DRV_AVALON 6 -#endif +#define DEVRLOCK(cgpu, _pth_state) do { \ + pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, &_pth_state); \ + cg_rlock(&cgpu->usbinfo.devlock); \ + } while (0) -#define DRV_LAST -1 +#define DEVRUNLOCK(cgpu, _pth_state) do { \ + cg_runlock(&cgpu->usbinfo.devlock); \ + pthread_setcancelstate(_pth_state, NULL); \ + } while (0) #define USB_CONFIG 1 +#define BITFURY_TIMEOUT_MS 999 +#define DRILLBIT_TIMEOUT_MS 999 +#define ICARUS_TIMEOUT_MS 999 +#define BMSC_TIMEOUT_MS 999 + #ifdef WIN32 #define BFLSC_TIMEOUT_MS 999 #define BITFORCE_TIMEOUT_MS 999 #define MODMINER_TIMEOUT_MS 999 #define AVALON_TIMEOUT_MS 999 -#define ICARUS_TIMEOUT_MS 999 +#define AVALON4_TIMEOUT_MS 999 +#define BITMAIN_TIMEOUT_MS 999 +#define KLONDIKE_TIMEOUT_MS 999 +#define COINTERRA_TIMEOUT_MS 999 +#define HASHFAST_TIMEOUT_MS 999 +#define HASHRATIO_TIMEOUT_MS 999 +#define BLOCKERUPTER_TIMEOUT_MS 999 + +/* The safety timeout we use, cancelling async transfers on windows that fail + * to timeout on their own. */ +#define WIN_CALLBACK_EXTRA 40 +#define WIN_WRITE_CBEXTRA 5000 #else #define BFLSC_TIMEOUT_MS 300 #define BITFORCE_TIMEOUT_MS 200 #define MODMINER_TIMEOUT_MS 100 #define AVALON_TIMEOUT_MS 200 -#define ICARUS_TIMEOUT_MS 200 +#define AVALON4_TIMEOUT_MS 50 +#define BITMAIN_TIMEOUT_MS 200 +#define KLONDIKE_TIMEOUT_MS 200 +#define COINTERRA_TIMEOUT_MS 200 +#define HASHFAST_TIMEOUT_MS 500 +#define HASHRATIO_TIMEOUT_MS 200 +#define BLOCKERUPTER_TIMEOUT_MS 300 #endif -#define USB_READ_MINPOLL 40 +#define USB_EPS(_intx, _epinfosx) { \ + .interface = _intx, \ + .ctrl_transfer = _intx, \ + .epinfo_count = ARRAY_SIZE(_epinfosx), \ + .epinfos = _epinfosx \ + } + +#define USB_EPS_CTRL(_inty, _ctrlinty, _epinfosy) { \ + .interface = _inty, \ + .ctrl_transfer = _ctrlinty, \ + .epinfo_count = ARRAY_SIZE(_epinfosy), \ + .epinfos = _epinfosy \ + } + +/* Linked list of all async transfers in progress. Protected by cgusb_fd_lock. + * This allows us to not stop the usb polling thread till all are complete, and + * to find cancellable transfers. */ +static struct list_head ut_list; #ifdef USE_BFLSC -// N.B. transfer size is 512 with USB2.0, but only 64 with USB1.1 -static struct usb_endpoints bas_eps[] = { - { LIBUSB_TRANSFER_TYPE_BULK, 64, EPI(1), 0 }, - { LIBUSB_TRANSFER_TYPE_BULK, 64, EPO(2), 0 } +static struct usb_epinfo bflsc_epinfos[] = { + { LIBUSB_TRANSFER_TYPE_BULK, 512, EPI(1), 0, 0 }, + { LIBUSB_TRANSFER_TYPE_BULK, 512, EPO(2), 0, 0 } +}; + +static struct usb_intinfo bflsc_ints[] = { + USB_EPS(0, bflsc_epinfos) }; #endif #ifdef USE_BITFORCE // N.B. transfer size is 512 with USB2.0, but only 64 with USB1.1 -static struct usb_endpoints bfl_eps[] = { - { LIBUSB_TRANSFER_TYPE_BULK, 64, EPI(1), 0 }, - { LIBUSB_TRANSFER_TYPE_BULK, 64, EPO(2), 0 } +static struct usb_epinfo bfl_epinfos[] = { + { LIBUSB_TRANSFER_TYPE_BULK, 64, EPI(1), 0, 0 }, + { LIBUSB_TRANSFER_TYPE_BULK, 64, EPO(2), 0, 0 } +}; + +static struct usb_intinfo bfl_ints[] = { + USB_EPS(0, bfl_epinfos) +}; +#endif + +#ifdef USE_BITFURY +static struct usb_epinfo bfu0_epinfos[] = { + { LIBUSB_TRANSFER_TYPE_INTERRUPT, 8, EPI(2), 0, 0 } +}; + +static struct usb_epinfo bfu1_epinfos[] = { + { LIBUSB_TRANSFER_TYPE_BULK, 16, EPI(3), 0, 0 }, + { LIBUSB_TRANSFER_TYPE_BULK, 16, EPO(4), 0, 0 } +}; + +/* Default to interface 1 */ +static struct usb_intinfo bfu_ints[] = { + USB_EPS(1, bfu1_epinfos), + USB_EPS(0, bfu0_epinfos) +}; + +static struct usb_epinfo bxf0_epinfos[] = { + { LIBUSB_TRANSFER_TYPE_INTERRUPT, 8, EPI(1), 0, 0 } +}; + +static struct usb_epinfo bxf1_epinfos[] = { + { LIBUSB_TRANSFER_TYPE_BULK, 64, EPI(2), 0, 0 }, + { LIBUSB_TRANSFER_TYPE_BULK, 64, EPO(2), 0, 0 } +}; + +static struct usb_intinfo bxf_ints[] = { + USB_EPS(1, bxf1_epinfos), + USB_EPS(0, bxf0_epinfos) +}; + +static struct usb_epinfo nfu_epinfos[] = { + { LIBUSB_TRANSFER_TYPE_INTERRUPT, 64, EPI(1), 0, 0 }, + { LIBUSB_TRANSFER_TYPE_INTERRUPT, 64, EPO(1), 0, 0 }, +}; + +static struct usb_intinfo nfu_ints[] = { + USB_EPS(0, nfu_epinfos) +}; + +static struct usb_epinfo bxm_epinfos[] = { + { LIBUSB_TRANSFER_TYPE_BULK, 512, EPI(1), 0, 0 }, + { LIBUSB_TRANSFER_TYPE_BULK, 512, EPO(2), 0, 0 } +}; + +static struct usb_intinfo bxm_ints[] = { + USB_EPS(0, bxm_epinfos) +}; +#endif + +#ifdef USE_BLOCKERUPTER +// BlockErupter Device +static struct usb_epinfo bet_epinfos[] = { + { LIBUSB_TRANSFER_TYPE_BULK, 64, EPI(1), 0, 0 }, + { LIBUSB_TRANSFER_TYPE_BULK, 64, EPO(1), 0, 0 } +}; + +static struct usb_intinfo bet_ints[] = { + USB_EPS(0, bet_epinfos) +}; +#endif + +#ifdef USE_DRILLBIT +// Drillbit Bitfury devices +static struct usb_epinfo drillbit_int_epinfos[] = { + { LIBUSB_TRANSFER_TYPE_INTERRUPT, 8, EPI(3), 0, 0 } +}; + +static struct usb_epinfo drillbit_bulk_epinfos[] = { + { LIBUSB_TRANSFER_TYPE_BULK, 16, EPI(1), 0, 0 }, + { LIBUSB_TRANSFER_TYPE_BULK, 16, EPO(2), 0, 0 }, +}; + +/* Default to interface 1 */ +static struct usb_intinfo drillbit_ints[] = { + USB_EPS(1, drillbit_bulk_epinfos), + USB_EPS(0, drillbit_int_epinfos) +}; +#endif + +#ifdef USE_HASHFAST +#include "driver-hashfast.h" + +static struct usb_epinfo hfa0_epinfos[] = { + { LIBUSB_TRANSFER_TYPE_INTERRUPT, 8, EPI(3), 0, 0 } +}; + +static struct usb_epinfo hfa1_epinfos[] = { + { LIBUSB_TRANSFER_TYPE_BULK, 64, EPI(1), 0, 0 }, + { LIBUSB_TRANSFER_TYPE_BULK, 64, EPO(2), 0, 0 } +}; + +/* Default to interface 1 */ +static struct usb_intinfo hfa_ints[] = { + USB_EPS(1, hfa1_epinfos), + USB_EPS(0, hfa0_epinfos) +}; +#endif + +#ifdef USE_HASHRATIO +static struct usb_epinfo hro_epinfos[] = { + { LIBUSB_TRANSFER_TYPE_BULK, 64, EPI(1), 0, 0 }, + { LIBUSB_TRANSFER_TYPE_BULK, 64, EPO(2), 0, 0 } +}; + +static struct usb_intinfo hro_ints[] = { + USB_EPS(0, hro_epinfos) }; #endif #ifdef USE_MODMINER -static struct usb_endpoints mmq_eps[] = { - { LIBUSB_TRANSFER_TYPE_BULK, 64, EPI(3), 0 }, - { LIBUSB_TRANSFER_TYPE_BULK, 64, EPO(3), 0 } +static struct usb_epinfo mmq_epinfos[] = { + { LIBUSB_TRANSFER_TYPE_BULK, 64, EPI(3), 0, 0 }, + { LIBUSB_TRANSFER_TYPE_BULK, 64, EPO(3), 0, 0 } +}; + +static struct usb_intinfo mmq_ints[] = { + USB_EPS(1, mmq_epinfos) }; #endif #ifdef USE_AVALON -static struct usb_endpoints ava_eps[] = { - { LIBUSB_TRANSFER_TYPE_BULK, 64, EPI(1), 0 }, - { LIBUSB_TRANSFER_TYPE_BULK, 64, EPO(2), 0 } +static struct usb_epinfo ava_epinfos[] = { + { LIBUSB_TRANSFER_TYPE_BULK, 64, EPI(1), 0, 0 }, + { LIBUSB_TRANSFER_TYPE_BULK, 64, EPO(2), 0, 0 } +}; + +static struct usb_intinfo ava_ints[] = { + USB_EPS(0, ava_epinfos) +}; +#endif + +#ifdef USE_AVALON2 +static struct usb_epinfo ava2_epinfos[] = { + { LIBUSB_TRANSFER_TYPE_BULK, 64, EPI(3), 0, 0 }, + { LIBUSB_TRANSFER_TYPE_BULK, 64, EPO(2), 0, 0 } +}; + +static struct usb_intinfo ava2_ints[] = { + USB_EPS(0, ava2_epinfos) +}; +#endif + +#ifdef USE_AVALON4 +static struct usb_epinfo ava4_epinfos[] = { + { LIBUSB_TRANSFER_TYPE_BULK, 64, EPI(1), 0, 0 }, + { LIBUSB_TRANSFER_TYPE_BULK, 64, EPO(1), 0, 0 } +}; + +static struct usb_intinfo ava4_ints[] = { + USB_EPS(1, ava4_epinfos) +}; +#endif + +#ifdef USE_KLONDIKE +static struct usb_epinfo kln_epinfos[] = { + { LIBUSB_TRANSFER_TYPE_BULK, 64, EPI(1), 0, 0 }, + { LIBUSB_TRANSFER_TYPE_BULK, 64, EPO(1), 0, 0 } +}; + +static struct usb_intinfo kln_ints[] = { + USB_EPS(0, kln_epinfos) +}; + +static struct usb_epinfo kli0_epinfos[] = { + { LIBUSB_TRANSFER_TYPE_INTERRUPT, 8, EPI(1), 0, 0 } +}; + +static struct usb_epinfo kli1_epinfos[] = { + { LIBUSB_TRANSFER_TYPE_BULK, 64, EPI(2), 0, 0 }, + { LIBUSB_TRANSFER_TYPE_BULK, 64, EPO(2), 0, 0 } +}; + +static struct usb_intinfo kli_ints[] = { + USB_EPS(1, kli1_epinfos), + USB_EPS(0, kli0_epinfos) }; #endif #ifdef USE_ICARUS -static struct usb_endpoints ica_eps[] = { - { LIBUSB_TRANSFER_TYPE_BULK, 64, EPI(3), 0 }, - { LIBUSB_TRANSFER_TYPE_BULK, 64, EPO(2), 0 } +static struct usb_epinfo ica_epinfos[] = { + { LIBUSB_TRANSFER_TYPE_BULK, 64, EPI(3), 0, 0 }, + { LIBUSB_TRANSFER_TYPE_BULK, 64, EPO(2), 0, 0 } }; -static struct usb_endpoints amu_eps[] = { - { LIBUSB_TRANSFER_TYPE_BULK, 64, EPI(1), 0 }, - { LIBUSB_TRANSFER_TYPE_BULK, 64, EPO(1), 0 } + +static struct usb_intinfo ica_ints[] = { + USB_EPS(0, ica_epinfos) }; -static struct usb_endpoints llt_eps[] = { - { LIBUSB_TRANSFER_TYPE_BULK, 64, EPI(1), 0 }, - { LIBUSB_TRANSFER_TYPE_BULK, 64, EPO(2), 0 } + +static struct usb_epinfo ica1_epinfos0[] = { + { LIBUSB_TRANSFER_TYPE_INTERRUPT, 16, EPI(0x82), 0, 0 } +}; + +static struct usb_epinfo ica1_epinfos1[] = { + { LIBUSB_TRANSFER_TYPE_BULK, 64, EPI(0x81), 0, 0 }, + { LIBUSB_TRANSFER_TYPE_BULK, 64, EPO(0x01), 0, 0 } +}; + +static struct usb_intinfo ica1_ints[] = { + USB_EPS(1, ica1_epinfos1), + USB_EPS(0, ica1_epinfos0) +}; + +static struct usb_epinfo amu_epinfos[] = { + { LIBUSB_TRANSFER_TYPE_BULK, 64, EPI(1), 0, 0 }, + { LIBUSB_TRANSFER_TYPE_BULK, 64, EPO(1), 0, 0 } +}; + +static struct usb_intinfo amu_ints[] = { + USB_EPS(0, amu_epinfos) +}; + +static struct usb_epinfo llt_epinfos[] = { + { LIBUSB_TRANSFER_TYPE_BULK, 64, EPI(1), 0, 0 }, + { LIBUSB_TRANSFER_TYPE_BULK, 64, EPO(2), 0, 0 } +}; + +static struct usb_intinfo llt_ints[] = { + USB_EPS(0, llt_epinfos) }; -static struct usb_endpoints cmr1_eps[] = { - { LIBUSB_TRANSFER_TYPE_BULK, 64, EPI(1), 0 }, - { LIBUSB_TRANSFER_TYPE_BULK, 64, EPO(2), 0 } -/* - Interface 1 - { LIBUSB_TRANSFER_TYPE_BULK, 64, EPI(3), 0 }, - { LIBUSB_TRANSFER_TYPE_BULK, 64, EPO(4), 0 }, - Interface 2 - { LIBUSB_TRANSFER_TYPE_BULK, 64, EPI(5), 0 }, - { LIBUSB_TRANSFER_TYPE_BULK, 64, EPO(6), 0 }, +static struct usb_epinfo cmr1_epinfos[] = { + { LIBUSB_TRANSFER_TYPE_BULK, 64, EPI(1), 0, 0 }, + { LIBUSB_TRANSFER_TYPE_BULK, 64, EPO(2), 0, 0 } +}; + +static struct usb_intinfo cmr1_ints[] = { + USB_EPS(0, cmr1_epinfos) +}; + +static struct usb_epinfo cmr2_epinfos0[] = { + { LIBUSB_TRANSFER_TYPE_BULK, 64, EPI(1), 0, 0 }, + { LIBUSB_TRANSFER_TYPE_BULK, 64, EPO(2), 0, 0 } +}; +static struct usb_epinfo cmr2_epinfos1[] = { + { LIBUSB_TRANSFER_TYPE_BULK, 64, EPI(3), 0, 0 }, + { LIBUSB_TRANSFER_TYPE_BULK, 64, EPO(4), 0, 0 }, +}; +static struct usb_epinfo cmr2_epinfos2[] = { + { LIBUSB_TRANSFER_TYPE_BULK, 64, EPI(5), 0, 0 }, + { LIBUSB_TRANSFER_TYPE_BULK, 64, EPO(6), 0, 0 }, +}; +static struct usb_epinfo cmr2_epinfos3[] = { + { LIBUSB_TRANSFER_TYPE_BULK, 64, EPI(7), 0, 0 }, + { LIBUSB_TRANSFER_TYPE_BULK, 64, EPO(8), 0, 0 } +}; + +static struct usb_intinfo cmr2_ints[] = { + USB_EPS_CTRL(0, 1, cmr2_epinfos0), + USB_EPS_CTRL(1, 2, cmr2_epinfos1), + USB_EPS_CTRL(2, 3, cmr2_epinfos2), + USB_EPS_CTRL(3, 4, cmr2_epinfos3) +}; +#endif + +#ifdef USE_COINTERRA +static struct usb_epinfo cointerra_epinfos[] = { + { LIBUSB_TRANSFER_TYPE_BULK, 64, EPI(1), 0, 0 }, + { LIBUSB_TRANSFER_TYPE_BULK, 64, EPO(1), 0, 0 } +}; + +static struct usb_intinfo cointerra_ints[] = { + USB_EPS(0, cointerra_epinfos) +}; +#endif + +#ifdef USE_BMSC +static struct usb_epinfo ica_epinfos[] = { + { LIBUSB_TRANSFER_TYPE_BULK, 64, EPI(3), 0, 0 }, + { LIBUSB_TRANSFER_TYPE_BULK, 64, EPO(2), 0, 0 } +}; + +static struct usb_intinfo ica_ints[] = { + USB_EPS(0, ica_epinfos) +}; + +static struct usb_epinfo amu_epinfos[] = { + { LIBUSB_TRANSFER_TYPE_BULK, 64, EPI(1), 0, 0 }, + { LIBUSB_TRANSFER_TYPE_BULK, 64, EPO(1), 0, 0 } +}; + +static struct usb_intinfo amu_ints[] = { + USB_EPS(0, amu_epinfos) +}; + +static struct usb_epinfo llt_epinfos[] = { + { LIBUSB_TRANSFER_TYPE_BULK, 64, EPI(1), 0, 0 }, + { LIBUSB_TRANSFER_TYPE_BULK, 64, EPO(2), 0, 0 } +}; + +static struct usb_intinfo llt_ints[] = { + USB_EPS(0, llt_epinfos) +}; + +static struct usb_epinfo cmr1_epinfos[] = { + { LIBUSB_TRANSFER_TYPE_BULK, 64, EPI(1), 0, 0 }, + { LIBUSB_TRANSFER_TYPE_BULK, 64, EPO(2), 0, 0 } +}; + +static struct usb_intinfo cmr1_ints[] = { + USB_EPS(0, cmr1_epinfos) +}; - Interface 3 - { LIBUSB_TRANSFER_TYPE_BULK, 64, EPI(7), 0 }, - { LIBUSB_TRANSFER_TYPE_BULK, 64, EPO(8), 0 } -*/ +static struct usb_epinfo cmr2_epinfos0[] = { + { LIBUSB_TRANSFER_TYPE_BULK, 64, EPI(1), 0, 0 }, + { LIBUSB_TRANSFER_TYPE_BULK, 64, EPO(2), 0, 0 } +}; +static struct usb_epinfo cmr2_epinfos1[] = { + { LIBUSB_TRANSFER_TYPE_BULK, 64, EPI(3), 0, 0 }, + { LIBUSB_TRANSFER_TYPE_BULK, 64, EPO(4), 0, 0 }, +}; +static struct usb_epinfo cmr2_epinfos2[] = { + { LIBUSB_TRANSFER_TYPE_BULK, 64, EPI(5), 0, 0 }, + { LIBUSB_TRANSFER_TYPE_BULK, 64, EPO(6), 0, 0 }, +}; +static struct usb_epinfo cmr2_epinfos3[] = { + { LIBUSB_TRANSFER_TYPE_BULK, 64, EPI(7), 0, 0 }, + { LIBUSB_TRANSFER_TYPE_BULK, 64, EPO(8), 0, 0 } +}; + +static struct usb_intinfo cmr2_ints[] = { + USB_EPS_CTRL(0, 1, cmr2_epinfos0), + USB_EPS_CTRL(1, 2, cmr2_epinfos1), + USB_EPS_CTRL(2, 3, cmr2_epinfos2), + USB_EPS_CTRL(3, 4, cmr2_epinfos3) +}; +#endif + +#ifdef USE_BITMAIN +static struct usb_epinfo btm_epinfos[] = { + { LIBUSB_TRANSFER_TYPE_BULK, 64, EPI(1), 0, 0 }, +#ifdef WIN32 + { LIBUSB_TRANSFER_TYPE_BULK, 64, EPO(2), 0, 0 } +#else + { LIBUSB_TRANSFER_TYPE_BULK, 64, EPO(1), 0, 0 } +#endif }; -static struct usb_endpoints cmr2_eps[] = { - { LIBUSB_TRANSFER_TYPE_BULK, 64, EPI(1), 0 }, - { LIBUSB_TRANSFER_TYPE_BULK, 64, EPO(2), 0 } + +static struct usb_intinfo btm_ints[] = { + USB_EPS(0, btm_epinfos) }; #endif #define IDVENDOR_FTDI 0x0403 +#define INTINFO(_ints) \ + .intinfo_count = ARRAY_SIZE(_ints), \ + .intinfos = _ints + +#define USBEP(_usbdev, _intinfo, _epinfo) (_usbdev->found->intinfos[_intinfo].epinfos[_epinfo].ep) +#define THISIF(_found, _this) (_found->intinfos[_this].interface) +#define USBIF(_usbdev, _this) THISIF(_usbdev->found, _this) + // TODO: Add support for (at least) Isochronous endpoints static struct usb_find_devices find_dev[] = { #ifdef USE_BFLSC { - .drv = DRV_BFLSC, + .drv = DRIVER_bflsc, .name = "BAS", .ident = IDENT_BAS, .idVendor = IDVENDOR_FTDI, .idProduct = 0x6014, //.iManufacturer = "Butterfly Labs", .iProduct = "BitFORCE SHA256 SC", - .kernel = 0, .config = 1, - .interface = 0, - .timeout = BFLSC_TIMEOUT_MS, + .timeout = BFLSC_TIMEOUT_MS, + .latency = LATENCY_STD, + INTINFO(bflsc_ints) }, + { + .drv = DRIVER_bflsc, + .name = "BMA", + .ident = IDENT_BMA, + .idVendor = IDVENDOR_FTDI, + .idProduct = 0x6014, + //.iManufacturer = "BUTTERFLY LABS" + .iProduct = "BitFORCE SC-28nm", + .config = 1, + .timeout = BFLSC_TIMEOUT_MS, + .latency = LATENCY_STD, + INTINFO(bflsc_ints) }, + { + .drv = DRIVER_bflsc, + .name = "BMA", + .ident = IDENT_BMA, + .idVendor = IDVENDOR_FTDI, + .idProduct = 0x6014, + .iManufacturer = "BUTTERFLY LABS", + .iProduct = "BitFORCE SHA256", + .config = 1, + .timeout = BFLSC_TIMEOUT_MS, + .latency = LATENCY_STD, + INTINFO(bflsc_ints) }, +#endif +#ifdef USE_BITFORCE + { + .drv = DRIVER_bitforce, + .name = "BFL", + .ident = IDENT_BFL, + .idVendor = IDVENDOR_FTDI, + .idProduct = 0x6014, + .iManufacturer = "Butterfly Labs Inc.", + .iProduct = "BitFORCE SHA256", + .config = 1, + .timeout = BITFORCE_TIMEOUT_MS, + .latency = LATENCY_STD, + INTINFO(bfl_ints) }, +#endif +#ifdef USE_BITFURY + { + .drv = DRIVER_bitfury, + .name = "BF1", + .ident = IDENT_BF1, + .idVendor = 0x03eb, + .idProduct = 0x204b, + .config = 1, + .timeout = BITFURY_TIMEOUT_MS, + .latency = LATENCY_UNUSED, + //.iManufacturer = "BPMC", + .iProduct = "Bitfury BF1", + INTINFO(bfu_ints) + }, + { + .drv = DRIVER_bitfury, + .name = "BXF", + .ident = IDENT_BXF, + .idVendor = 0x198c, + .idProduct = 0xb1f1, + .config = 1, + .timeout = BITFURY_TIMEOUT_MS, + .latency = LATENCY_UNUSED, + .iManufacturer = "c-scape", + .iProduct = "bi?fury", + INTINFO(bxf_ints) + }, + { + .drv = DRIVER_bitfury, + .name = "OSM", + .ident = IDENT_OSM, + .idVendor = 0x198c, + .idProduct = 0xb1f1, + .config = 1, + .timeout = BITFURY_TIMEOUT_MS, + .latency = LATENCY_UNUSED, + .iManufacturer = "c-scape", + .iProduct = "OneString", + INTINFO(bxf_ints) + }, + { + .drv = DRIVER_bitfury, + .name = "NFU", + .ident = IDENT_NFU, + .idVendor = 0x04d8, + .idProduct = 0x00de, + .config = 1, + .timeout = BITFURY_TIMEOUT_MS, + .latency = LATENCY_UNUSED, + INTINFO(nfu_ints) + }, + { + .drv = DRIVER_bitfury, + .name = "BXM", + .ident = IDENT_BXM, + .idVendor = 0x0403, + .idProduct = 0x6014, + .config = 1, + .timeout = BITFURY_TIMEOUT_MS, + .latency = LATENCY_UNUSED, + INTINFO(bxm_ints) + }, +#endif +#ifdef USE_BLOCKERUPTER + { + .drv = DRIVER_blockerupter, + .name = "BET", + .ident = IDENT_BET, + .idVendor = 0x10c4, + .idProduct = 0xea60, + .config = 1, + .timeout = BLOCKERUPTER_TIMEOUT_MS, + .latency = LATENCY_UNUSED, + INTINFO(bet_ints) }, + +#endif +#ifdef USE_DRILLBIT + { + .drv = DRIVER_drillbit, + .name = "DRB", + .ident = IDENT_DRB, + .idVendor = 0x03eb, + .idProduct = 0x2404, + .config = 1, + .timeout = DRILLBIT_TIMEOUT_MS, + .latency = LATENCY_UNUSED, + .iManufacturer = "Drillbit Systems", + .iProduct = NULL, /* Can be Thumb or Eight, same driver */ + INTINFO(drillbit_ints) + }, +#endif +#ifdef USE_MODMINER + { + .drv = DRIVER_modminer, + .name = "MMQ", + .ident = IDENT_MMQ, + .idVendor = 0x1fc9, + .idProduct = 0x0003, + .config = 1, + .timeout = MODMINER_TIMEOUT_MS, + .latency = LATENCY_UNUSED, + INTINFO(mmq_ints) }, +#endif +#ifdef USE_AVALON + { + .drv = DRIVER_avalon, + .name = "BTB", + .ident = IDENT_BTB, + .idVendor = IDVENDOR_FTDI, + .idProduct = 0x6001, + .iManufacturer = "Burnin Electronics", + .iProduct = "BitBurner", + .config = 1, + .timeout = AVALON_TIMEOUT_MS, + .latency = 10, + INTINFO(ava_ints) }, + { + .drv = DRIVER_avalon, + .name = "BBF", + .ident = IDENT_BBF, + .idVendor = IDVENDOR_FTDI, + .idProduct = 0x6001, + .iManufacturer = "Burnin Electronics", + .iProduct = "BitBurner Fury", + .config = 1, + .timeout = AVALON_TIMEOUT_MS, + .latency = 10, + INTINFO(ava_ints) }, + { + .drv = DRIVER_avalon, + .name = "AVA", + .ident = IDENT_AVA, + .idVendor = IDVENDOR_FTDI, + .idProduct = 0x6001, + .config = 1, + .timeout = AVALON_TIMEOUT_MS, + .latency = 10, + INTINFO(ava_ints) }, +#endif +#ifdef USE_AVALON2 + { + .drv = DRIVER_avalon2, + .name = "AV2", + .ident = IDENT_AV2, + .idVendor = 0x067b, + .idProduct = 0x2303, + .config = 1, + .timeout = AVALON_TIMEOUT_MS, + .latency = LATENCY_UNUSED, + INTINFO(ava2_ints) }, +#endif +#ifdef USE_AVALON4 + { + .drv = DRIVER_avalon4, + .name = "AV4", + .ident = IDENT_AV4, + .idVendor = 0x29f1, + .idProduct = 0x33f2, + .iManufacturer = "CANAAN", + .iProduct = "USB2IIC Converter", + .config = 1, + .timeout = AVALON4_TIMEOUT_MS, + .latency = LATENCY_UNUSED, + INTINFO(ava4_ints) }, +#endif +#ifdef USE_HASHFAST + { + .drv = DRIVER_hashfast, + .name = "HFA", + .ident = IDENT_HFA, + .idVendor = HF_USB_VENDOR_ID, + .idProduct = HF_USB_PRODUCT_ID_G1, + .iManufacturer = "HashFast LLC", + .iProduct = "M1 Module", + .config = 1, + .timeout = HASHFAST_TIMEOUT_MS, + .latency = LATENCY_UNUSED, + INTINFO(hfa_ints) }, +#endif +#ifdef USE_HASHRATIO + { + .drv = DRIVER_hashratio, + .name = "HRO", + .ident = IDENT_HRO, + .idVendor = IDVENDOR_FTDI, + .idProduct = 0x6001, + .config = 1, + .timeout = HASHRATIO_TIMEOUT_MS, + .latency = LATENCY_UNUSED, + INTINFO(hro_ints) }, +#endif +#ifdef USE_KLONDIKE + { + .drv = DRIVER_klondike, + .name = "KLN", + .ident = IDENT_KLN, + .idVendor = 0x04D8, + .idProduct = 0xF60A, + .config = 1, + .timeout = KLONDIKE_TIMEOUT_MS, + .latency = 10, + INTINFO(kln_ints) }, + { + .drv = DRIVER_klondike, + .name = "KLI", + .ident = IDENT_KLN, + .idVendor = 0x04D8, + .idProduct = 0xF60A, + .config = 1, + .timeout = KLONDIKE_TIMEOUT_MS, + .latency = 10, + INTINFO(kli_ints) }, +#endif +#ifdef USE_ICARUS + { + .drv = DRIVER_icarus, + .name = "ICA", + .ident = IDENT_ICA, + .idVendor = 0x067b, + .idProduct = 0x2303, + .config = 1, + .timeout = ICARUS_TIMEOUT_MS, + .latency = LATENCY_UNUSED, + INTINFO(ica_ints) }, + { + .drv = DRIVER_icarus, + .name = "ICA", + .ident = IDENT_AVA, + .idVendor = 0x1fc9, + .idProduct = 0x0083, + .config = 1, + .timeout = ICARUS_TIMEOUT_MS, + .latency = LATENCY_UNUSED, + INTINFO(ica1_ints) }, + { + .drv = DRIVER_icarus, + .name = "AMU", + .ident = IDENT_AMU, + .idVendor = 0x10c4, + .idProduct = 0xea60, + .config = 1, + .timeout = ICARUS_TIMEOUT_MS, + .latency = LATENCY_UNUSED, + INTINFO(amu_ints) }, + { + .drv = DRIVER_icarus, + .name = "LIN", + .ident = IDENT_LIN, + .idVendor = 0x10c4, + .idProduct = 0xea60, + .config = 1, + .timeout = ICARUS_TIMEOUT_MS, + .latency = LATENCY_UNUSED, + INTINFO(amu_ints) }, + { + .drv = DRIVER_icarus, + .name = "ANU", + .ident = IDENT_ANU, + .idVendor = 0x10c4, + .idProduct = 0xea60, + .config = 1, + .timeout = ICARUS_TIMEOUT_MS, + .latency = LATENCY_UNUSED, + INTINFO(amu_ints) }, + { + .drv = DRIVER_icarus, + .name = "BLT", + .ident = IDENT_BLT, + .idVendor = IDVENDOR_FTDI, + .idProduct = 0x6001, + .iProduct = "FT232R USB UART", + .config = 1, + .timeout = ICARUS_TIMEOUT_MS, + .latency = LATENCY_STD, + INTINFO(llt_ints) }, + // For any that don't match the above "BLT" + { + .drv = DRIVER_icarus, + .name = "LLT", + .ident = IDENT_LLT, + .idVendor = IDVENDOR_FTDI, + .idProduct = 0x6001, + .config = 1, + .timeout = ICARUS_TIMEOUT_MS, .latency = LATENCY_STD, - .epcount = ARRAY_SIZE(bas_eps), - .eps = bas_eps }, -#endif -#ifdef USE_BITFORCE + INTINFO(llt_ints) }, { - .drv = DRV_BITFORCE, - .name = "BFL", - .ident = IDENT_BFL, + .drv = DRIVER_icarus, + .name = "CMR", + .ident = IDENT_CMR1, .idVendor = IDVENDOR_FTDI, .idProduct = 0x6014, - .iManufacturer = "Butterfly Labs Inc.", - .iProduct = "BitFORCE SHA256", - .kernel = 0, + .iProduct = "Cairnsmore1", .config = 1, - .interface = 0, - .timeout = BITFORCE_TIMEOUT_MS, + .timeout = ICARUS_TIMEOUT_MS, .latency = LATENCY_STD, - .epcount = ARRAY_SIZE(bfl_eps), - .eps = bfl_eps }, -#endif -#ifdef USE_MODMINER + INTINFO(cmr1_ints) }, { - .drv = DRV_MODMINER, - .name = "MMQ", - .ident = IDENT_MMQ, - .idVendor = 0x1fc9, - .idProduct = 0x0003, - .kernel = 0, - .config = 1, - .interface = 1, - .timeout = MODMINER_TIMEOUT_MS, - .latency = LATENCY_UNUSED, - .epcount = ARRAY_SIZE(mmq_eps), - .eps = mmq_eps }, -#endif -#ifdef USE_AVALON - { - .drv = DRV_AVALON, - .name = "BTB", - .ident = IDENT_BTB, + .drv = DRIVER_icarus, + .name = "CMR", + .ident = IDENT_CMR2, .idVendor = IDVENDOR_FTDI, - .idProduct = 0x6001, - .iManufacturer = "Burnin Electronics", - .iProduct = "BitBurner", - .kernel = 0, + .idProduct = 0x8350, + .iProduct = "Cairnsmore1", .config = 1, - .interface = 0, - .timeout = AVALON_TIMEOUT_MS, - .latency = 10, - .epcount = ARRAY_SIZE(ava_eps), - .eps = ava_eps }, + .timeout = ICARUS_TIMEOUT_MS, + .latency = LATENCY_STD, + INTINFO(cmr2_ints) }, +#endif +#ifdef USE_COINTERRA { - .drv = DRV_AVALON, - .name = "AVA", - .ident = IDENT_AVA, - .idVendor = IDVENDOR_FTDI, - .idProduct = 0x6001, - .kernel = 0, + .drv = DRIVER_cointerra, + .name = "CTA", + .ident = IDENT_CTA, + .idVendor = 0x1cbe, + .idProduct = 0x0003, .config = 1, - .interface = 0, - .timeout = AVALON_TIMEOUT_MS, - .latency = 10, - .epcount = ARRAY_SIZE(ava_eps), - .eps = ava_eps }, + .timeout = COINTERRA_TIMEOUT_MS, + .latency = LATENCY_STD, + INTINFO(cointerra_ints) }, #endif -#ifdef USE_ICARUS +#ifdef USE_BMSC { - .drv = DRV_ICARUS, + .drv = DRIVER_bmsc, .name = "ICA", .ident = IDENT_ICA, .idVendor = 0x067b, .idProduct = 0x2303, - .kernel = 0, .config = 1, - .interface = 0, .timeout = ICARUS_TIMEOUT_MS, .latency = LATENCY_UNUSED, - .epcount = ARRAY_SIZE(ica_eps), - .eps = ica_eps }, + INTINFO(ica_ints) }, { - .drv = DRV_ICARUS, + .drv = DRIVER_bmsc, .name = "AMU", .ident = IDENT_AMU, .idVendor = 0x10c4, .idProduct = 0xea60, - .kernel = 0, .config = 1, - .interface = 0, .timeout = ICARUS_TIMEOUT_MS, .latency = LATENCY_UNUSED, - .epcount = ARRAY_SIZE(amu_eps), - .eps = amu_eps }, + INTINFO(amu_ints) }, + { + .drv = DRIVER_bmsc, + .name = "ANU", + .ident = IDENT_ANU, + .idVendor = 0x10c4, + .idProduct = 0xea60, + .config = 1, + .timeout = ICARUS_TIMEOUT_MS, + .latency = LATENCY_UNUSED, + INTINFO(amu_ints) }, { - .drv = DRV_ICARUS, + .drv = DRIVER_bmsc, .name = "BLT", .ident = IDENT_BLT, .idVendor = IDVENDOR_FTDI, .idProduct = 0x6001, .iProduct = "FT232R USB UART", - .kernel = 0, .config = 1, - .interface = 0, .timeout = ICARUS_TIMEOUT_MS, .latency = LATENCY_STD, - .epcount = ARRAY_SIZE(llt_eps), - .eps = llt_eps }, + INTINFO(llt_ints) }, // For any that don't match the above "BLT" { - .drv = DRV_ICARUS, + .drv = DRIVER_bmsc, .name = "LLT", .ident = IDENT_LLT, .idVendor = IDVENDOR_FTDI, .idProduct = 0x6001, - .kernel = 0, .config = 1, - .interface = 0, .timeout = ICARUS_TIMEOUT_MS, .latency = LATENCY_STD, - .epcount = ARRAY_SIZE(llt_eps), - .eps = llt_eps }, + INTINFO(llt_ints) }, { - .drv = DRV_ICARUS, + .drv = DRIVER_bmsc, .name = "CMR", .ident = IDENT_CMR1, .idVendor = IDVENDOR_FTDI, - .idProduct = 0x8350, + .idProduct = 0x6014, .iProduct = "Cairnsmore1", - .kernel = 0, .config = 1, - .interface = 0, .timeout = ICARUS_TIMEOUT_MS, .latency = LATENCY_STD, - .epcount = ARRAY_SIZE(cmr1_eps), - .eps = cmr1_eps }, + INTINFO(cmr1_ints) }, { - .drv = DRV_ICARUS, + .drv = DRIVER_bmsc, .name = "CMR", .ident = IDENT_CMR2, .idVendor = IDVENDOR_FTDI, - .idProduct = 0x6014, + .idProduct = 0x8350, .iProduct = "Cairnsmore1", - .kernel = 0, .config = 1, - .interface = 0, .timeout = ICARUS_TIMEOUT_MS, .latency = LATENCY_STD, - .epcount = ARRAY_SIZE(cmr2_eps), - .eps = cmr2_eps }, + INTINFO(cmr2_ints) }, +#endif +#ifdef USE_BITMAIN + { + .drv = DRIVER_bitmain, + .name = "BMM", + .ident = IDENT_BMM, +#ifdef WIN32 + .idVendor = IDVENDOR_FTDI, + .idProduct = 0x6001, +#else + .idVendor = 0x4254, + .idProduct = 0x4153, #endif -#ifdef USE_ZTEX -// This is here so cgminer -n shows them -// the ztex driver (as at 201303) doesn't use usbutils + .config = 1, + .timeout = BITMAIN_TIMEOUT_MS, + .latency = 10, + INTINFO(btm_ints) }, { - .drv = DRV_ZTEX, - .name = "ZTX", - .ident = IDENT_ZTX, - .idVendor = 0x221a, - .idProduct = 0x0100, - .kernel = 0, + .drv = DRIVER_bitmain, + .name = "BMS", + .ident = IDENT_BMS, + .idVendor = IDVENDOR_FTDI, + .idProduct = 0x6602, .config = 1, - .interface = 1, - .timeout = 100, - .latency = LATENCY_UNUSED, - .epcount = 0, - .eps = NULL }, + .timeout = BITMAIN_TIMEOUT_MS, + .latency = 10, + INTINFO(btm_ints) }, #endif - { DRV_LAST, NULL, 0, 0, 0, NULL, NULL, 0, 0, 0, 0, 0, 0, 0, NULL } + { DRIVER_MAX, NULL, 0, 0, 0, NULL, NULL, 0, 0, 0, 0, NULL } }; -#ifdef USE_BFLSC -extern struct device_drv bflsc_drv; -#endif - -#ifdef USE_BITFORCE -extern struct device_drv bitforce_drv; -#endif - -#ifdef USE_MODMINER -extern struct device_drv modminer_drv; -#endif - -#ifdef USE_ICARUS -extern struct device_drv icarus_drv; -#endif - -#ifdef USE_AVALON -extern struct device_drv avalon_drv; -#endif - #define STRBUFLEN 256 static const char *BLANK = ""; static const char *space = " "; static const char *nodatareturned = "no data returned "; -#define IOERR_CHECK(cgpu, err) \ - if (err == LIBUSB_ERROR_IO) { \ - cgpu->usbinfo.ioerr_count++; \ - cgpu->usbinfo.continuous_ioerr_count++; \ - } else { \ - cgpu->usbinfo.continuous_ioerr_count = 0; \ - } - #if 0 // enable USBDEBUG - only during development testing static const char *debug_true_str = "true"; static const char *debug_false_str = "false"; @@ -369,16 +985,20 @@ static const char *nodatareturned = "no data returned "; // For device limits by driver static struct driver_count { - uint32_t count; - uint32_t limit; + int count; + int limit; } drv_count[DRIVER_MAX]; // For device limits by list of bus/dev static struct usb_busdev { int bus_number; int device_address; +#ifdef WIN32 void *resource1; void *resource2; +#else + int fd; +#endif } *busdev; static int busdev_count = 0; @@ -395,6 +1015,7 @@ struct usb_in_use_list { // List of in use devices static struct usb_in_use_list *in_use_head = NULL; +static struct usb_in_use_list *blacklist_head = NULL; struct resource_work { bool lock; @@ -487,71 +1108,11 @@ static int next_stat = USB_NOSTAT; #endif // DO_USB_STATS -static const char **usb_commands; - -static const char *C_REJECTED_S = "RejectedNoDevice"; -static const char *C_PING_S = "Ping"; -static const char *C_CLEAR_S = "Clear"; -static const char *C_REQUESTVERSION_S = "RequestVersion"; -static const char *C_GETVERSION_S = "GetVersion"; -static const char *C_REQUESTFPGACOUNT_S = "RequestFPGACount"; -static const char *C_GETFPGACOUNT_S = "GetFPGACount"; -static const char *C_STARTPROGRAM_S = "StartProgram"; -static const char *C_STARTPROGRAMSTATUS_S = "StartProgramStatus"; -static const char *C_PROGRAM_S = "Program"; -static const char *C_PROGRAMSTATUS_S = "ProgramStatus"; -static const char *C_PROGRAMSTATUS2_S = "ProgramStatus2"; -static const char *C_FINALPROGRAMSTATUS_S = "FinalProgramStatus"; -static const char *C_SETCLOCK_S = "SetClock"; -static const char *C_REPLYSETCLOCK_S = "ReplySetClock"; -static const char *C_REQUESTUSERCODE_S = "RequestUserCode"; -static const char *C_GETUSERCODE_S = "GetUserCode"; -static const char *C_REQUESTTEMPERATURE_S = "RequestTemperature"; -static const char *C_GETTEMPERATURE_S = "GetTemperature"; -static const char *C_SENDWORK_S = "SendWork"; -static const char *C_SENDWORKSTATUS_S = "SendWorkStatus"; -static const char *C_REQUESTWORKSTATUS_S = "RequestWorkStatus"; -static const char *C_GETWORKSTATUS_S = "GetWorkStatus"; -static const char *C_REQUESTIDENTIFY_S = "RequestIdentify"; -static const char *C_GETIDENTIFY_S = "GetIdentify"; -static const char *C_REQUESTFLASH_S = "RequestFlash"; -static const char *C_REQUESTSENDWORK_S = "RequestSendWork"; -static const char *C_REQUESTSENDWORKSTATUS_S = "RequestSendWorkStatus"; -static const char *C_RESET_S = "Reset"; -static const char *C_SETBAUD_S = "SetBaud"; -static const char *C_SETDATA_S = "SetDataCtrl"; -static const char *C_SETFLOW_S = "SetFlowCtrl"; -static const char *C_SETMODEM_S = "SetModemCtrl"; -static const char *C_PURGERX_S = "PurgeRx"; -static const char *C_PURGETX_S = "PurgeTx"; -static const char *C_FLASHREPLY_S = "FlashReply"; -static const char *C_REQUESTDETAILS_S = "RequestDetails"; -static const char *C_GETDETAILS_S = "GetDetails"; -static const char *C_REQUESTRESULTS_S = "RequestResults"; -static const char *C_GETRESULTS_S = "GetResults"; -static const char *C_REQUESTQUEJOB_S = "RequestQueJob"; -static const char *C_REQUESTQUEJOBSTATUS_S = "RequestQueJobStatus"; -static const char *C_QUEJOB_S = "QueJob"; -static const char *C_QUEJOBSTATUS_S = "QueJobStatus"; -static const char *C_QUEFLUSH_S = "QueFlush"; -static const char *C_QUEFLUSHREPLY_S = "QueFlushReply"; -static const char *C_REQUESTVOLTS_S = "RequestVolts"; -static const char *C_GETVOLTS_S = "GetVolts"; -static const char *C_SENDTESTWORK_S = "SendTestWork"; -static const char *C_LATENCY_S = "SetLatency"; -static const char *C_SETLINE_S = "SetLine"; -static const char *C_VENDOR_S = "Vendor"; -static const char *C_SETFAN_S = "SetFan"; -static const char *C_FANREPLY_S = "GetFan"; -static const char *C_AVALON_TASK_S = "AvalonTask"; -static const char *C_AVALON_READ_S = "AvalonRead"; -static const char *C_GET_AVALON_READY_S = "AvalonReady"; -static const char *C_AVALON_RESET_S = "AvalonReset"; -static const char *C_GET_AVALON_RESET_S = "GetAvalonReset"; -static const char *C_FTDI_STATUS_S = "FTDIStatus"; -static const char *C_ENABLE_UART_S = "EnableUART"; -static const char *C_BB_SET_VOLTAGE_S = "SetCoreVoltage"; -static const char *C_BB_GET_VOLTAGE_S = "GetCoreVoltage"; +/* Create usb_commands array from USB_PARSE_COMMANDS macro in usbutils.h */ +char *usb_commands[] = { + USB_PARSE_COMMANDS(JUMPTABLE) + "Null" +}; #ifdef EOL #undef EOL @@ -594,29 +1155,6 @@ static const char *BULK = "Bulk"; static const char *INTERRUPT = "Interrupt"; static const char *UNKNOWN = "Unknown"; -static const char *err_io_str = " IO Error"; -static const char *err_access_str = " Access Denied-a"; -static const char *err_timeout_str = " Reply Timeout"; -static const char *err_pipe_str = " Access denied-p"; -static const char *err_other_str = " Access denied-o"; - -static const char *usberrstr(int err) -{ - switch (err) { - case LIBUSB_ERROR_IO: - return err_io_str; - case LIBUSB_ERROR_ACCESS: - return err_access_str; - case LIBUSB_ERROR_TIMEOUT: - return err_timeout_str; - case LIBUSB_ERROR_PIPE: - return err_pipe_str; - case LIBUSB_ERROR_OTHER: - return err_other_str; - } - return BLANK; -} - static const char *destype(uint8_t bDescriptorType) { switch (bDescriptorType) { @@ -782,7 +1320,7 @@ static void usb_full(ssize_t *count, libusb_device *dev, char **buf, size_t *off if (!opt_usb_list_all) { bool known = false; - for (i = 0; find_dev[i].drv != DRV_LAST; i++) + for (i = 0; find_dev[i].drv != DRIVER_MAX; i++) if ((find_dev[i].idVendor == desc.idVendor) && (find_dev[i].idProduct == desc.idProduct)) { known = true; @@ -823,11 +1361,11 @@ static void usb_full(ssize_t *count, libusb_device *dev, char **buf, size_t *off err = libusb_get_string_descriptor_ascii(handle, desc.iManufacturer, man, STRBUFLEN); if (err < 0) - snprintf((char *)man, sizeof(man), "** err(%d)%s", err, usberrstr(err)); + snprintf((char *)man, sizeof(man), "** err:(%d) %s", err, libusb_error_name(err)); err = libusb_get_string_descriptor_ascii(handle, desc.iProduct, prod, STRBUFLEN); if (err < 0) - snprintf((char *)prod, sizeof(prod), "** err(%d)%s", err, usberrstr(err)); + snprintf((char *)prod, sizeof(prod), "** err:(%d) %s", err, libusb_error_name(err)); if (level == 0) { libusb_close(handle); @@ -903,7 +1441,7 @@ static void usb_full(ssize_t *count, libusb_device *dev, char **buf, size_t *off err = libusb_get_string_descriptor_ascii(handle, desc.iSerialNumber, ser, STRBUFLEN); if (err < 0) - snprintf((char *)ser, sizeof(ser), "** err(%d)%s", err, usberrstr(err)); + snprintf((char *)ser, sizeof(ser), "** err:(%d) %s", err, libusb_error_name(err)); snprintf(tmp, sizeof(tmp), EOL " dev %d: More Info:" EOL "\tManufacturer: '%s'" EOL "\tProduct: '%s'" EOL "\tSerial '%s'", @@ -923,7 +1461,7 @@ void usb_all(int level) count = libusb_get_device_list(NULL, &list); if (count < 0) { - applog(LOG_ERR, "USB all: failed, err %d%s", (int)count, usberrstr((int)count)); + applog(LOG_ERR, "USB all: failed, err:(%d) %s", (int)count, libusb_error_name((int)count)); return; } @@ -946,7 +1484,7 @@ void usb_all(int level) for (i = 0; i < count; i++) usb_full(&j, list[i], &buf, &off, &len, level); - _applog(LOG_WARNING, buf); + _applog(LOG_WARNING, buf, false); free(buf); @@ -971,78 +1509,6 @@ static void cgusb_check_init() libusb_set_debug(NULL, opt_usbdump); usb_all(opt_usbdump); } - - usb_commands = malloc(sizeof(*usb_commands) * C_MAX); - if (unlikely(!usb_commands)) - quit(1, "USB failed to malloc usb_commands"); - - // use constants so the stat generation is very quick - // and the association between number and name can't - // be missalined easily - usb_commands[C_REJECTED] = C_REJECTED_S; - usb_commands[C_PING] = C_PING_S; - usb_commands[C_CLEAR] = C_CLEAR_S; - usb_commands[C_REQUESTVERSION] = C_REQUESTVERSION_S; - usb_commands[C_GETVERSION] = C_GETVERSION_S; - usb_commands[C_REQUESTFPGACOUNT] = C_REQUESTFPGACOUNT_S; - usb_commands[C_GETFPGACOUNT] = C_GETFPGACOUNT_S; - usb_commands[C_STARTPROGRAM] = C_STARTPROGRAM_S; - usb_commands[C_STARTPROGRAMSTATUS] = C_STARTPROGRAMSTATUS_S; - usb_commands[C_PROGRAM] = C_PROGRAM_S; - usb_commands[C_PROGRAMSTATUS] = C_PROGRAMSTATUS_S; - usb_commands[C_PROGRAMSTATUS2] = C_PROGRAMSTATUS2_S; - usb_commands[C_FINALPROGRAMSTATUS] = C_FINALPROGRAMSTATUS_S; - usb_commands[C_SETCLOCK] = C_SETCLOCK_S; - usb_commands[C_REPLYSETCLOCK] = C_REPLYSETCLOCK_S; - usb_commands[C_REQUESTUSERCODE] = C_REQUESTUSERCODE_S; - usb_commands[C_GETUSERCODE] = C_GETUSERCODE_S; - usb_commands[C_REQUESTTEMPERATURE] = C_REQUESTTEMPERATURE_S; - usb_commands[C_GETTEMPERATURE] = C_GETTEMPERATURE_S; - usb_commands[C_SENDWORK] = C_SENDWORK_S; - usb_commands[C_SENDWORKSTATUS] = C_SENDWORKSTATUS_S; - usb_commands[C_REQUESTWORKSTATUS] = C_REQUESTWORKSTATUS_S; - usb_commands[C_GETWORKSTATUS] = C_GETWORKSTATUS_S; - usb_commands[C_REQUESTIDENTIFY] = C_REQUESTIDENTIFY_S; - usb_commands[C_GETIDENTIFY] = C_GETIDENTIFY_S; - usb_commands[C_REQUESTFLASH] = C_REQUESTFLASH_S; - usb_commands[C_REQUESTSENDWORK] = C_REQUESTSENDWORK_S; - usb_commands[C_REQUESTSENDWORKSTATUS] = C_REQUESTSENDWORKSTATUS_S; - usb_commands[C_RESET] = C_RESET_S; - usb_commands[C_SETBAUD] = C_SETBAUD_S; - usb_commands[C_SETDATA] = C_SETDATA_S; - usb_commands[C_SETFLOW] = C_SETFLOW_S; - usb_commands[C_SETMODEM] = C_SETMODEM_S; - usb_commands[C_PURGERX] = C_PURGERX_S; - usb_commands[C_PURGETX] = C_PURGETX_S; - usb_commands[C_FLASHREPLY] = C_FLASHREPLY_S; - usb_commands[C_REQUESTDETAILS] = C_REQUESTDETAILS_S; - usb_commands[C_GETDETAILS] = C_GETDETAILS_S; - usb_commands[C_REQUESTRESULTS] = C_REQUESTRESULTS_S; - usb_commands[C_GETRESULTS] = C_GETRESULTS_S; - usb_commands[C_REQUESTQUEJOB] = C_REQUESTQUEJOB_S; - usb_commands[C_REQUESTQUEJOBSTATUS] = C_REQUESTQUEJOBSTATUS_S; - usb_commands[C_QUEJOB] = C_QUEJOB_S; - usb_commands[C_QUEJOBSTATUS] = C_QUEJOBSTATUS_S; - usb_commands[C_QUEFLUSH] = C_QUEFLUSH_S; - usb_commands[C_QUEFLUSHREPLY] = C_QUEFLUSHREPLY_S; - usb_commands[C_REQUESTVOLTS] = C_REQUESTVOLTS_S; - usb_commands[C_GETVOLTS] = C_GETVOLTS_S; - usb_commands[C_SENDTESTWORK] = C_SENDTESTWORK_S; - usb_commands[C_LATENCY] = C_LATENCY_S; - usb_commands[C_SETLINE] = C_SETLINE_S; - usb_commands[C_VENDOR] = C_VENDOR_S; - usb_commands[C_SETFAN] = C_SETFAN_S; - usb_commands[C_FANREPLY] = C_FANREPLY_S; - usb_commands[C_AVALON_TASK] = C_AVALON_TASK_S; - usb_commands[C_AVALON_READ] = C_AVALON_READ_S; - usb_commands[C_GET_AVALON_READY] = C_GET_AVALON_READY_S; - usb_commands[C_AVALON_RESET] = C_AVALON_RESET_S; - usb_commands[C_GET_AVALON_RESET] = C_GET_AVALON_RESET_S; - usb_commands[C_FTDI_STATUS] = C_FTDI_STATUS_S; - usb_commands[C_ENABLE_UART] = C_ENABLE_UART_S; - usb_commands[C_BB_SET_VOLTAGE] = C_BB_SET_VOLTAGE_S; - usb_commands[C_BB_GET_VOLTAGE] = C_BB_GET_VOLTAGE_S; - stats_initialised = true; } @@ -1071,6 +1537,7 @@ void usb_applog(struct cgpu_info *cgpu, enum usb_cmds cmd, char *msg, int amount err, amount); } +#ifdef WIN32 static void in_use_store_ress(uint8_t bus_number, uint8_t device_address, void *resource1, void *resource2) { struct usb_in_use_list *in_use_tmp; @@ -1142,25 +1609,88 @@ static void in_use_get_ress(uint8_t bus_number, uint8_t device_address, void **r applog(LOG_ERR, "FAIL: USB get_lock empty (%d:%d)", (int)bus_number, (int)device_address); } +#else -static bool __is_in_use(uint8_t bus_number, uint8_t device_address) +static void in_use_store_fd(uint8_t bus_number, uint8_t device_address, int fd) { struct usb_in_use_list *in_use_tmp; - bool ret = false; + bool found = false; + + mutex_lock(&cgusb_lock); + in_use_tmp = in_use_head; + while (in_use_tmp) { + if (in_use_tmp->in_use.bus_number == (int)bus_number && + in_use_tmp->in_use.device_address == (int)device_address) { + found = true; + in_use_tmp->in_use.fd = fd; + break; + } + in_use_tmp = in_use_tmp->next; + } + mutex_unlock(&cgusb_lock); + + if (found == false) { + applog(LOG_ERR, "FAIL: USB store_fd not found (%d:%d)", + (int)bus_number, (int)device_address); + } +} + +static int in_use_get_fd(uint8_t bus_number, uint8_t device_address) +{ + struct usb_in_use_list *in_use_tmp; + bool found = false; + int fd = -1; + mutex_lock(&cgusb_lock); in_use_tmp = in_use_head; while (in_use_tmp) { if (in_use_tmp->in_use.bus_number == (int)bus_number && in_use_tmp->in_use.device_address == (int)device_address) { - ret = true; + found = true; + fd = in_use_tmp->in_use.fd; break; } in_use_tmp = in_use_tmp->next; } + mutex_unlock(&cgusb_lock); + + if (found == false) { + applog(LOG_ERR, "FAIL: USB get_lock not found (%d:%d)", + (int)bus_number, (int)device_address); + } + return fd; +} +#endif +static bool _in_use(struct usb_in_use_list *head, uint8_t bus_number, + uint8_t device_address) +{ + struct usb_in_use_list *in_use_tmp; + bool ret = false; + + in_use_tmp = head; + while (in_use_tmp) { + if (in_use_tmp->in_use.bus_number == (int)bus_number && + in_use_tmp->in_use.device_address == (int)device_address) { + ret = true; + break; + } + in_use_tmp = in_use_tmp->next; + if (in_use_tmp == head) + break; + } return ret; } +static bool __is_in_use(uint8_t bus_number, uint8_t device_address) +{ + if (_in_use(in_use_head, bus_number, device_address)) + return true; + if (_in_use(blacklist_head, bus_number, device_address)) + return true; + return false; +} + static bool is_in_use_bd(uint8_t bus_number, uint8_t device_address) { bool ret; @@ -1176,16 +1706,94 @@ static bool is_in_use(libusb_device *dev) return is_in_use_bd(libusb_get_bus_number(dev), libusb_get_device_address(dev)); } -static void add_in_use(uint8_t bus_number, uint8_t device_address) +static bool how_in_use(uint8_t bus_number, uint8_t device_address, bool *blacklisted) { - struct usb_in_use_list *in_use_tmp; + bool ret; + mutex_lock(&cgusb_lock); + ret = _in_use(in_use_head, bus_number, device_address); + if (!ret) { + if (_in_use(blacklist_head, bus_number, device_address)) + *blacklisted = true; + } + mutex_unlock(&cgusb_lock); + + return ret; +} + +void usb_list(void) +{ + struct libusb_device_descriptor desc; + struct libusb_device_handle *handle; + uint8_t bus_number; + uint8_t device_address; + libusb_device **list; + ssize_t count, i, j; + int err, total = 0; + + count = libusb_get_device_list(NULL, &list); + if (count < 0) { + applog(LOG_ERR, "USB list: failed, err:(%d) %s", (int)count, libusb_error_name((int)count)); + return; + } + if (count == 0) { + applog(LOG_WARNING, "USB list: found no devices"); + return; + } + for (i = 0; i < count; i++) { + bool known = false, blacklisted = false, active; + unsigned char manuf[256], prod[256]; + libusb_device *dev = list[i]; + + err = libusb_get_device_descriptor(dev, &desc); + if (err) { + applog(LOG_WARNING, "USB list: Failed to get descriptor %d", (int)i); + break; + } + + bus_number = libusb_get_bus_number(dev); + device_address = libusb_get_device_address(dev); + + for (j = 0; find_dev[j].drv != DRIVER_MAX; j++) { + if ((find_dev[j].idVendor == desc.idVendor) && + (find_dev[j].idProduct == desc.idProduct)) { + known = true; + break; + } + } + if (!known) + continue; + + err = libusb_open(dev, &handle); + if (err) { + applog(LOG_WARNING, "USB list: Failed to open %d", (int)i); + break; + } + libusb_get_string_descriptor_ascii(handle, desc.iManufacturer, manuf, 255); + libusb_get_string_descriptor_ascii(handle, desc.iProduct, prod, 255); + total++; + active = how_in_use(bus_number, device_address, &blacklisted); + simplelog(LOG_WARNING, "Bus %u Device %u ID: %04x:%04x %s %s %sactive %s", + bus_number, device_address, desc.idVendor, desc.idProduct, + manuf, prod, active ? "" : "in", blacklisted ? "blacklisted" : ""); + } + libusb_free_device_list(list, 1); + simplelog(LOG_WARNING, "%d total known USB device%s", total, total > 1 ? "s": ""); +} + +static void add_in_use(uint8_t bus_number, uint8_t device_address, bool blacklist) +{ + struct usb_in_use_list *in_use_tmp, **head; bool found = false; mutex_lock(&cgusb_lock); - if (unlikely(__is_in_use(bus_number, device_address))) { + if (unlikely(!blacklist && __is_in_use(bus_number, device_address))) { found = true; goto nofway; } + if (blacklist) + head = &blacklist_head; + else + head = &in_use_head; in_use_tmp = calloc(1, sizeof(*in_use_tmp)); if (unlikely(!in_use_tmp)) @@ -1193,9 +1801,9 @@ static void add_in_use(uint8_t bus_number, uint8_t device_address) in_use_tmp->in_use.bus_number = (int)bus_number; in_use_tmp->in_use.device_address = (int)device_address; in_use_tmp->next = in_use_head; - if (in_use_head) - in_use_head->prev = in_use_tmp; - in_use_head = in_use_tmp; + if (*head) + (*head)->prev = in_use_tmp; + *head = in_use_tmp; nofway: mutex_unlock(&cgusb_lock); @@ -1204,22 +1812,26 @@ static void add_in_use(uint8_t bus_number, uint8_t device_address) (int)bus_number, (int)device_address); } -static void remove_in_use(uint8_t bus_number, uint8_t device_address) +static void __remove_in_use(uint8_t bus_number, uint8_t device_address, bool blacklist) { - struct usb_in_use_list *in_use_tmp; + struct usb_in_use_list *in_use_tmp, **head; bool found = false; mutex_lock(&cgusb_lock); + if (blacklist) + head = &blacklist_head; + else + head = &in_use_head; - in_use_tmp = in_use_head; + in_use_tmp = *head; while (in_use_tmp) { if (in_use_tmp->in_use.bus_number == (int)bus_number && in_use_tmp->in_use.device_address == (int)device_address) { found = true; - if (in_use_tmp == in_use_head) { - in_use_head = in_use_head->next; - if (in_use_head) - in_use_head->prev = NULL; + if (in_use_tmp == *head) { + *head = (*head)->next; + if (*head) + (*head)->prev = NULL; } else { in_use_tmp->prev->next = in_use_tmp->next; if (in_use_tmp->next) @@ -1229,13 +1841,21 @@ static void remove_in_use(uint8_t bus_number, uint8_t device_address) break; } in_use_tmp = in_use_tmp->next; + if (in_use_tmp == *head) + break; } mutex_unlock(&cgusb_lock); - if (!found) + if (!found) { applog(LOG_ERR, "FAIL: USB remove not already in use (%d:%d)", (int)bus_number, (int)device_address); + } +} + +static void remove_in_use(uint8_t bus_number, uint8_t device_address) +{ + __remove_in_use(bus_number, device_address, false); } static bool cgminer_usb_lock_bd(struct device_drv *drv, uint8_t bus_number, uint8_t device_address) @@ -1340,86 +1960,156 @@ static struct cg_usb_device *free_cgusb(struct cg_usb_device *cgusb) if (cgusb->prod_string && cgusb->prod_string != BLANK) free(cgusb->prod_string); - free(cgusb->descriptor); + if (cgusb->descriptor) + free(cgusb->descriptor); free(cgusb->found); - if (cgusb->buffer) - free(cgusb->buffer); - free(cgusb); return NULL; } -void usb_uninit(struct cgpu_info *cgpu) +static void _usb_uninit(struct cgpu_info *cgpu) { - applog(LOG_DEBUG, "USB uninit %s%i", - cgpu->drv->name, cgpu->device_id); + int ifinfo; // May have happened already during a failed initialisation // if release_cgpu() was called due to a USB NODEV(err) if (!cgpu->usbdev) return; - if (!libusb_release_interface(cgpu->usbdev->handle, cgpu->usbdev->found->interface)) { + + applog(LOG_DEBUG, "USB uninit %s%i", + cgpu->drv->name, cgpu->device_id); + + if (cgpu->usbdev->handle) { + for (ifinfo = cgpu->usbdev->found->intinfo_count - 1; ifinfo >= 0; ifinfo--) { + libusb_release_interface(cgpu->usbdev->handle, + THISIF(cgpu->usbdev->found, ifinfo)); + } +#ifdef LINUX + libusb_attach_kernel_driver(cgpu->usbdev->handle, THISIF(cgpu->usbdev->found, ifinfo)); +#endif cg_wlock(&cgusb_fd_lock); libusb_close(cgpu->usbdev->handle); + cgpu->usbdev->handle = NULL; cg_wunlock(&cgusb_fd_lock); } cgpu->usbdev = free_cgusb(cgpu->usbdev); } -/* - * N.B. this is always called inside - * DEVLOCK(cgpu, pstate); - */ -static void release_cgpu(struct cgpu_info *cgpu) +void usb_uninit(struct cgpu_info *cgpu) +{ + int pstate; + + DEVWLOCK(cgpu, pstate); + + _usb_uninit(cgpu); + + DEVWUNLOCK(cgpu, pstate); +} + +/* We have dropped the read devlock before entering this function but we pick + * up the write lock to prevent any attempts to work on dereferenced code once + * the nodev flag has been set. */ +static bool __release_cgpu(struct cgpu_info *cgpu) { struct cg_usb_device *cgusb = cgpu->usbdev; + bool initted = cgpu->usbinfo.initialised; struct cgpu_info *lookcgpu; int i; - applog(LOG_DEBUG, "USB release %s%i", - cgpu->drv->name, cgpu->device_id); - // It has already been done if (cgpu->usbinfo.nodev) - return; + return false; + + applog(LOG_DEBUG, "USB release %s%i", + cgpu->drv->name, cgpu->device_id); - zombie_devs++; - total_count--; - drv_count[cgpu->drv->drv_id].count--; + if (initted) { + zombie_devs++; + total_count--; + drv_count[cgpu->drv->drv_id].count--; + } cgpu->usbinfo.nodev = true; cgpu->usbinfo.nodev_count++; cgtime(&cgpu->usbinfo.last_nodev); // Any devices sharing the same USB device should be marked also - // Currently only MMQ shares a USB device for (i = 0; i < total_devices; i++) { lookcgpu = get_devices(i); if (lookcgpu != cgpu && lookcgpu->usbdev == cgusb) { - total_count--; - drv_count[lookcgpu->drv->drv_id].count--; + if (initted) { + total_count--; + drv_count[lookcgpu->drv->drv_id].count--; + } lookcgpu->usbinfo.nodev = true; lookcgpu->usbinfo.nodev_count++; - memcpy(&(lookcgpu->usbinfo.last_nodev), + cg_memcpy(&(lookcgpu->usbinfo.last_nodev), &(cgpu->usbinfo.last_nodev), sizeof(struct timeval)); lookcgpu->usbdev = NULL; } } - usb_uninit(cgpu); + _usb_uninit(cgpu); + return true; +} + +static void release_cgpu(struct cgpu_info *cgpu) +{ + if (__release_cgpu(cgpu)) + cgminer_usb_unlock_bd(cgpu->drv, cgpu->usbinfo.bus_number, cgpu->usbinfo.device_address); +} + +void blacklist_cgpu(struct cgpu_info *cgpu) +{ + if (cgpu->blacklisted) { + applog(LOG_WARNING, "Device already blacklisted"); + return; + } + cgpu->blacklisted = true; + add_in_use(cgpu->usbinfo.bus_number, cgpu->usbinfo.device_address, true); + if (__release_cgpu(cgpu)) + cgminer_usb_unlock_bd(cgpu->drv, cgpu->usbinfo.bus_number, cgpu->usbinfo.device_address); +} + +void whitelist_cgpu(struct cgpu_info *cgpu) +{ + if (!cgpu->blacklisted) { + applog(LOG_WARNING, "Device not blacklisted"); + return; + } + __remove_in_use(cgpu->usbinfo.bus_number, cgpu->usbinfo.device_address, true); + cgpu->blacklisted = false; +} + +/* + * Force a NODEV on a device so it goes back to hotplug + */ +void usb_nodev(struct cgpu_info *cgpu) +{ + int pstate; + + DEVWLOCK(cgpu, pstate); - cgminer_usb_unlock_bd(cgpu->drv, cgpu->usbinfo.bus_number, cgpu->usbinfo.device_address); + release_cgpu(cgpu); + + DEVWUNLOCK(cgpu, pstate); } -// Currently only used by MMQ +/* + * Use the same usbdev thus locking is across all related devices + */ struct cgpu_info *usb_copy_cgpu(struct cgpu_info *orig) { - struct cgpu_info *copy = calloc(1, sizeof(*copy)); + struct cgpu_info *copy; + int pstate; + + DEVWLOCK(orig, pstate); + copy = calloc(1, sizeof(*copy)); if (unlikely(!copy)) quit(1, "Failed to calloc cgpu for %s in usb_copy_cgpu", orig->drv->dname); @@ -1430,11 +2120,11 @@ struct cgpu_info *usb_copy_cgpu(struct cgpu_info *orig) copy->usbdev = orig->usbdev; - memcpy(&(copy->usbinfo), &(orig->usbinfo), sizeof(copy->usbinfo)); + cg_memcpy(&(copy->usbinfo), &(orig->usbinfo), sizeof(copy->usbinfo)); copy->usbinfo.nodev = (copy->usbdev == NULL); - copy->usbinfo.devlock = orig->usbinfo.devlock; + DEVWUNLOCK(orig, pstate); return copy; } @@ -1452,25 +2142,18 @@ struct cgpu_info *usb_alloc_cgpu(struct device_drv *drv, int threads) cgpu->usbinfo.nodev = true; - cgpu->usbinfo.devlock = calloc(1, sizeof(*(cgpu->usbinfo.devlock))); - if (unlikely(!cgpu->usbinfo.devlock)) - quit(1, "Failed to calloc devlock for %s in usb_alloc_cgpu", drv->dname); - - rwlock_init(cgpu->usbinfo.devlock); + cglock_init(&cgpu->usbinfo.devlock); return cgpu; } -struct cgpu_info *usb_free_cgpu_devlock(struct cgpu_info *cgpu, bool free_devlock) +struct cgpu_info *usb_free_cgpu(struct cgpu_info *cgpu) { if (cgpu->drv->copy) free(cgpu->drv); free(cgpu->device_path); - if (free_devlock) - free(cgpu->usbinfo.devlock); - free(cgpu); return NULL; @@ -1480,25 +2163,9 @@ struct cgpu_info *usb_free_cgpu_devlock(struct cgpu_info *cgpu, bool free_devloc #define USB_INIT_OK 1 #define USB_INIT_IGNORE 2 -/* - * WARNING - these assume DEVLOCK(cgpu, pstate) is called first and - * DEVUNLOCK(cgpu, pstate) in called in the same function with the same pstate - * given to DEVLOCK. - * You must call DEVUNLOCK(cgpu, pstate) before exiting the function or it will leave - * the thread Cancelability unrestored - */ -#define DEVLOCK(cgpu, _pth_state) do { \ - pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, &_pth_state); \ - wr_lock(cgpu->usbinfo.devlock); \ - } while (0) - -#define DEVUNLOCK(cgpu, _pth_state) do { \ - wr_unlock(cgpu->usbinfo.devlock); \ - pthread_setcancelstate(_pth_state, NULL); \ - } while (0) - static int _usb_init(struct cgpu_info *cgpu, struct libusb_device *dev, struct usb_find_devices *found) { + unsigned char man[STRBUFLEN+1], prod[STRBUFLEN+1]; struct cg_usb_device *cgusb = NULL; struct libusb_config_descriptor *config = NULL; const struct libusb_interface_descriptor *idesc; @@ -1506,18 +2173,25 @@ static int _usb_init(struct cgpu_info *cgpu, struct libusb_device *dev, struct u unsigned char strbuf[STRBUFLEN+1]; char devpath[32]; char devstr[STRBUFLEN+1]; - int err, i, j, k, pstate; + int err, ifinfo, epinfo, alt, epnum, pstate; int bad = USB_INIT_FAIL; - int cfg; + int cfg, claimed = 0, i; - DEVLOCK(cgpu, pstate); + DEVWLOCK(cgpu, pstate); cgpu->usbinfo.bus_number = libusb_get_bus_number(dev); cgpu->usbinfo.device_address = libusb_get_device_address(dev); - snprintf(devpath, sizeof(devpath), "%d:%d", - (int)(cgpu->usbinfo.bus_number), - (int)(cgpu->usbinfo.device_address)); + if (found->intinfo_count > 1) { + snprintf(devpath, sizeof(devpath), "%d:%d-i%d", + (int)(cgpu->usbinfo.bus_number), + (int)(cgpu->usbinfo.device_address), + THISIF(found, 0)); + } else { + snprintf(devpath, sizeof(devpath), "%d:%d", + (int)(cgpu->usbinfo.bus_number), + (int)(cgpu->usbinfo.device_address)); + } cgpu->device_path = strdup(devpath); @@ -1552,17 +2226,18 @@ static int _usb_init(struct cgpu_info *cgpu, struct libusb_device *dev, struct u switch (err) { case LIBUSB_ERROR_ACCESS: applog(LOG_ERR, - "USB init open device failed, err %d, " - "you dont have priviledge to access %s", + "USB init, open device failed, err %d, " + "you don't have privilege to access %s", err, devstr); + applog(LOG_ERR, "See README file included for help"); break; #ifdef WIN32 // Windows specific message case LIBUSB_ERROR_NOT_SUPPORTED: - applog(LOG_ERR, - "USB init, open device failed, err %d, " - "you need to install a WinUSB driver for %s", - err, devstr); + applog(LOG_ERR, "USB init, open device failed, err %d, ", err); + applog(LOG_ERR, "You need to install a WinUSB driver for %s", devstr); + applog(LOG_ERR, "And associate %s with WinUSB using zadig", devstr); + applog(LOG_ERR, "See README.txt file included for help"); break; #endif default: @@ -1573,26 +2248,27 @@ static int _usb_init(struct cgpu_info *cgpu, struct libusb_device *dev, struct u goto dame; } -#ifndef WIN32 - if (libusb_kernel_driver_active(cgusb->handle, found->kernel) == 1) { - applog(LOG_DEBUG, "USB init, kernel attached ... %s", devstr); - err = libusb_detach_kernel_driver(cgusb->handle, found->kernel); - if (err == 0) { - applog(LOG_DEBUG, - "USB init, kernel detached successfully %s", - devstr); - } else { - applog(LOG_WARNING, - "USB init, kernel detach failed, err %d in use? %s", - err, devstr); - goto cldame; +#ifdef LINUX + for (ifinfo = 0; ifinfo < found->intinfo_count; ifinfo++) { + if (libusb_kernel_driver_active(cgusb->handle, THISIF(found, ifinfo)) == 1) { + applog(LOG_DEBUG, "USB init, kernel attached ... %s", devstr); + err = libusb_detach_kernel_driver(cgusb->handle, THISIF(found, ifinfo)); + if (err == 0) { + applog(LOG_DEBUG, + "USB init, kernel detached ifinfo %d interface %d" + " successfully %s", + ifinfo, THISIF(found, ifinfo), devstr); + } else { + applog(LOG_WARNING, + "USB init, kernel detach ifinfo %d interface %d failed," + " err %d in use? %s", + ifinfo, THISIF(found, ifinfo), err, devstr); + goto nokernel; + } } } #endif - if (found->iManufacturer) { - unsigned char man[STRBUFLEN+1]; - err = libusb_get_string_descriptor_ascii(cgusb->handle, cgusb->descriptor->iManufacturer, man, STRBUFLEN); @@ -1602,17 +2278,32 @@ static int _usb_init(struct cgpu_info *cgpu, struct libusb_device *dev, struct u err, devstr); goto cldame; } - if (strcmp((char *)man, found->iManufacturer)) { + if (found->iManufacturer) { + if (strcasecmp((char *)man, found->iManufacturer)) { applog(LOG_DEBUG, "USB init, iManufacturer mismatch %s", devstr); + applog(LOG_DEBUG, "Found %s vs %s", man, found->iManufacturer); bad = USB_INIT_IGNORE; goto cldame; } + } else { + for (i = 0; find_dev[i].drv != DRIVER_MAX; i++) { + const char *iManufacturer = find_dev[i].iManufacturer; + /* If other drivers has an iManufacturer set that match, + * don't try to claim this device. */ + + if (!iManufacturer) + continue; + if (!strcasecmp((char *)man, iManufacturer)) { + applog(LOG_DEBUG, "USB init, alternative iManufacturer match %s", + devstr); + applog(LOG_DEBUG, "Found %s", iManufacturer); + bad = USB_INIT_IGNORE; + goto cldame; + } + } } - if (found->iProduct) { - unsigned char prod[STRBUFLEN+1]; - err = libusb_get_string_descriptor_ascii(cgusb->handle, cgusb->descriptor->iProduct, prod, STRBUFLEN); @@ -1622,12 +2313,29 @@ static int _usb_init(struct cgpu_info *cgpu, struct libusb_device *dev, struct u err, devstr); goto cldame; } - if (strcmp((char *)prod, found->iProduct)) { + if (found->iProduct) { + if (strcasecmp((char *)prod, found->iProduct)) { applog(LOG_DEBUG, "USB init, iProduct mismatch %s", devstr); + applog(LOG_DEBUG, "Found %s vs %s", prod, found->iProduct); bad = USB_INIT_IGNORE; goto cldame; } + } else { + for (i = 0; find_dev[i].drv != DRIVER_MAX; i++) { + const char *iProduct = find_dev[i].iProduct; + /* Do same for iProduct as iManufacturer above */ + + if (!iProduct) + continue; + if (!strcasecmp((char *)prod, iProduct)) { + applog(LOG_DEBUG, "USB init, alternative iProduct match %s", + devstr); + applog(LOG_DEBUG, "Found %s", iProduct); + bad = USB_INIT_IGNORE; + goto cldame; + } + } } cfg = -1; @@ -1662,55 +2370,71 @@ static int _usb_init(struct cgpu_info *cgpu, struct libusb_device *dev, struct u goto cldame; } - if ((int)(config->bNumInterfaces) <= found->interface) { - applog(LOG_DEBUG, "USB init bNumInterfaces <= interface %s", - devstr); + int imax = -1; + for (ifinfo = 0; ifinfo < found->intinfo_count; ifinfo++) + if (found->intinfos[ifinfo].interface > imax) + imax = found->intinfos[ifinfo].interface; + + if ((int)(config->bNumInterfaces) <= imax) { + applog(LOG_DEBUG, "USB init bNumInterfaces %d <= interface max %d for %s", + (int)(config->bNumInterfaces), imax, devstr); goto cldame; } - for (i = 0; i < found->epcount; i++) - found->eps[i].found = false; - - for (i = 0; i < config->interface[found->interface].num_altsetting; i++) { - idesc = &(config->interface[found->interface].altsetting[i]); - for (j = 0; j < (int)(idesc->bNumEndpoints); j++) { - epdesc = &(idesc->endpoint[j]); - for (k = 0; k < found->epcount; k++) { - if (!found->eps[k].found) { - if (epdesc->bmAttributes == found->eps[k].att - && epdesc->wMaxPacketSize >= found->eps[k].size - && epdesc->bEndpointAddress == found->eps[k].ep) { - found->eps[k].found = true; - found->wMaxPacketSize = epdesc->wMaxPacketSize; - break; + for (ifinfo = 0; ifinfo < found->intinfo_count; ifinfo++) + for (epinfo = 0; epinfo < found->intinfos[ifinfo].epinfo_count; epinfo++) + found->intinfos[ifinfo].epinfos[epinfo].found = false; + + for (ifinfo = 0; ifinfo < found->intinfo_count; ifinfo++) { + int interface = found->intinfos[ifinfo].interface; + for (alt = 0; alt < config->interface[interface].num_altsetting; alt++) { + idesc = &(config->interface[interface].altsetting[alt]); + for (epnum = 0; epnum < (int)(idesc->bNumEndpoints); epnum++) { + struct usb_epinfo *epinfos = found->intinfos[ifinfo].epinfos; + epdesc = &(idesc->endpoint[epnum]); + for (epinfo = 0; epinfo < found->intinfos[ifinfo].epinfo_count; epinfo++) { + if (!epinfos[epinfo].found) { + if (epdesc->bmAttributes == epinfos[epinfo].att + && epdesc->wMaxPacketSize >= epinfos[epinfo].size + && epdesc->bEndpointAddress == epinfos[epinfo].ep) { + epinfos[epinfo].found = true; + epinfos[epinfo].wMaxPacketSize = epdesc->wMaxPacketSize; + break; + } } } } } } - for (i = 0; i < found->epcount; i++) { - if (found->eps[i].found == false) { - applog(LOG_DEBUG, "USB init found == false %s", - devstr); - goto cldame; - } - } + for (ifinfo = 0; ifinfo < found->intinfo_count; ifinfo++) + for (epinfo = 0; epinfo < found->intinfos[ifinfo].epinfo_count; epinfo++) + if (found->intinfos[ifinfo].epinfos[epinfo].found == false) { + applog(LOG_DEBUG, "USB init found (%d,%d) == false %s", + ifinfo, epinfo, devstr); + goto cldame; + } - err = libusb_claim_interface(cgusb->handle, found->interface); - if (err) { - switch(err) { - case LIBUSB_ERROR_BUSY: - applog(LOG_WARNING, - "USB init, claim interface %d in use %s", - found->interface, devstr); - break; - default: - applog(LOG_DEBUG, - "USB init, claim interface %d failed, err %d %s", - found->interface, err, devstr); + claimed = 0; + for (ifinfo = 0; ifinfo < found->intinfo_count; ifinfo++) { + err = libusb_claim_interface(cgusb->handle, THISIF(found, ifinfo)); + if (err == 0) + claimed++; + else { + switch(err) { + case LIBUSB_ERROR_BUSY: + applog(LOG_WARNING, + "USB init, claim ifinfo %d interface %d in use %s", + ifinfo, THISIF(found, ifinfo), devstr); + break; + default: + applog(LOG_DEBUG, + "USB init, claim ifinfo %d interface %d failed," + " err %d %s", + ifinfo, THISIF(found, ifinfo), err, devstr); + } + goto reldame; } - goto cldame; } cfg = -1; @@ -1725,6 +2449,10 @@ static int _usb_init(struct cgpu_info *cgpu, struct libusb_device *dev, struct u } cgusb->usbver = cgusb->descriptor->bcdUSB; + if (cgusb->usbver < 0x0200) { + cgusb->usb11 = true; + cgusb->tt = true; + } // TODO: allow this with the right version of the libusb include and running library // cgusb->speed = libusb_get_device_speed(dev); @@ -1766,7 +2494,7 @@ static int _usb_init(struct cgpu_info *cgpu, struct libusb_device *dev, struct u // Allow a name change based on the idVendor+idProduct // N.B. must be done before calling add_cgpu() - if (strcmp(cgpu->drv->name, found->name)) { + if (strcasecmp(cgpu->drv->name, found->name)) { if (!cgpu->drv->copy) cgpu->drv = copy_drv(cgpu->drv); cgpu->drv->name = (char *)(found->name); @@ -1777,12 +2505,19 @@ static int _usb_init(struct cgpu_info *cgpu, struct libusb_device *dev, struct u reldame: - libusb_release_interface(cgusb->handle, found->interface); + ifinfo = claimed; + while (ifinfo-- > 0) + libusb_release_interface(cgusb->handle, THISIF(found, ifinfo)); cldame: +#ifdef LINUX + libusb_attach_kernel_driver(cgusb->handle, THISIF(found, ifinfo)); +nokernel: +#endif cg_wlock(&cgusb_fd_lock); libusb_close(cgusb->handle); + cgusb->handle = NULL; cg_wunlock(&cgusb_fd_lock); dame: @@ -1793,7 +2528,7 @@ static int _usb_init(struct cgpu_info *cgpu, struct libusb_device *dev, struct u cgusb = free_cgusb(cgusb); out_unlock: - DEVUNLOCK(cgpu, pstate); + DEVWUNLOCK(cgpu, pstate); return bad; } @@ -1804,14 +2539,14 @@ bool usb_init(struct cgpu_info *cgpu, struct libusb_device *dev, struct usb_find int uninitialised_var(ret); int i; - for (i = 0; find_dev[i].drv != DRV_LAST; i++) { + for (i = 0; find_dev[i].drv != DRIVER_MAX; i++) { if (find_dev[i].drv == found_match->drv && find_dev[i].idVendor == found_match->idVendor && find_dev[i].idProduct == found_match->idProduct) { found_use = malloc(sizeof(*found_use)); if (unlikely(!found_use)) quit(1, "USB failed to malloc found_use"); - memcpy(found_use, &(find_dev[i]), sizeof(*found_use)); + cg_memcpy(found_use, &(find_dev[i]), sizeof(*found_use)); ret = _usb_init(cgpu, dev, found_use); @@ -1881,13 +2616,13 @@ static struct usb_find_devices *usb_check_each(int drvnum, struct device_drv *dr struct usb_find_devices *found; int i; - for (i = 0; find_dev[i].drv != DRV_LAST; i++) + for (i = 0; find_dev[i].drv != DRIVER_MAX; i++) if (find_dev[i].drv == drvnum) { if (usb_check_device(drv, dev, &(find_dev[i]))) { found = malloc(sizeof(*found)); if (unlikely(!found)) quit(1, "USB failed to malloc found"); - memcpy(found, &(find_dev[i]), sizeof(*found)); + cg_memcpy(found, &(find_dev[i]), sizeof(*found)); return found; } } @@ -1895,6 +2630,9 @@ static struct usb_find_devices *usb_check_each(int drvnum, struct device_drv *dr return NULL; } +#define DRIVER_USB_CHECK_EACH(X) if (drv->drv_id == DRIVER_##X) \ + return usb_check_each(DRIVER_##X, drv, dev); + static struct usb_find_devices *usb_check(__maybe_unused struct device_drv *drv, __maybe_unused struct libusb_device *dev) { if (drv_count[drv->drv_id].count >= drv_count[drv->drv_id].limit) { @@ -1904,39 +2642,18 @@ static struct usb_find_devices *usb_check(__maybe_unused struct device_drv *drv, return NULL; } -#ifdef USE_BFLSC - if (drv->drv_id == DRIVER_BFLSC) - return usb_check_each(DRV_BFLSC, drv, dev); -#endif - -#ifdef USE_BITFORCE - if (drv->drv_id == DRIVER_BITFORCE) - return usb_check_each(DRV_BITFORCE, drv, dev); -#endif - -#ifdef USE_MODMINER - if (drv->drv_id == DRIVER_MODMINER) - return usb_check_each(DRV_MODMINER, drv, dev); -#endif - -#ifdef USE_ICARUS - if (drv->drv_id == DRIVER_ICARUS) - return usb_check_each(DRV_ICARUS, drv, dev); -#endif - -#ifdef USE_AVALON - if (drv->drv_id == DRIVER_AVALON) - return usb_check_each(DRV_AVALON, drv, dev); -#endif + DRIVER_PARSE_COMMANDS(DRIVER_USB_CHECK_EACH) return NULL; } -void usb_detect(struct device_drv *drv, bool (*device_detect)(struct libusb_device *, struct usb_find_devices *)) +void __usb_detect(struct device_drv *drv, struct cgpu_info *(*device_detect)(struct libusb_device *, struct usb_find_devices *), + bool single) { libusb_device **list; ssize_t count, i; struct usb_find_devices *found; + struct cgpu_info *cgpu; applog(LOG_DEBUG, "USB scan devices: checking for %s devices", drv->name); @@ -1978,17 +2695,24 @@ void usb_detect(struct device_drv *drv, bool (*device_detect)(struct libusb_devi found = usb_check(drv, list[i]); if (found != NULL) { + bool new_dev = false; + if (is_in_use(list[i]) || cgminer_usb_lock(drv, list[i]) == false) free(found); else { - if (!device_detect(list[i], found)) + cgpu = device_detect(list[i], found); + if (!cgpu) cgminer_usb_unlock(drv, list[i]); else { + new_dev = true; + cgpu->usbinfo.initialised = true; total_count++; drv_count[drv->drv_id].count++; } free(found); } + if (single && new_dev) + break; } } @@ -2132,7 +2856,7 @@ static void newstats(struct cgpu_info *cgpu) usb_stats[next_stat].name = cgpu->drv->name; usb_stats[next_stat].device_id = -1; - usb_stats[next_stat].details = calloc(1, sizeof(struct cg_usb_stats_details) * C_MAX * 2); + usb_stats[next_stat].details = calloc(2, sizeof(struct cg_usb_stats_details) * (C_MAX + 1)); if (unlikely(!usb_stats[next_stat].details)) quit(1, "USB failed to calloc details for %d", next_stat+1); @@ -2175,7 +2899,7 @@ static void stats(struct cgpu_info *cgpu, struct timeval *tv_start, struct timev int offset = 0; if (extrams >= USB_TMO_2) { - applog(LOG_ERR, "%s%i: TIMEOUT %s took %dms but was %dms", + applog(LOG_INFO, "%s%i: TIMEOUT %s took %dms but was %dms", cgpu->drv->name, cgpu->device_id, usb_cmdname(cmd), totms, timeout) ; offset = 2; @@ -2215,7 +2939,7 @@ static void stats(struct cgpu_info *cgpu, struct timeval *tv_start, struct timev if (details->item[item].count == 0) { details->item[item].min_delay = diff; - memcpy(&(details->item[item].first), tv_start, sizeof(*tv_start)); + cg_memcpy(&(details->item[item].first), tv_start, sizeof(*tv_start)); } else if (diff < details->item[item].min_delay) details->item[item].min_delay = diff; @@ -2223,7 +2947,7 @@ static void stats(struct cgpu_info *cgpu, struct timeval *tv_start, struct timev details->item[item].max_delay = diff; details->item[item].total_delay += diff; - memcpy(&(details->item[item].last), tv_start, sizeof(*tv_start)); + cg_memcpy(&(details->item[item].last), tv_start, sizeof(*tv_start)); details->item[item].count++; } @@ -2241,150 +2965,330 @@ static void rejected_inc(struct cgpu_info *cgpu, uint32_t mode) } #endif -static char *find_end(unsigned char *buf, unsigned char *ptr, int ptrlen, int tot, char *end, int endlen, bool first) +#define USB_RETRY_MAX 5 + +struct usb_transfer { + cgsem_t cgsem; + struct libusb_transfer *transfer; + bool cancellable; + struct list_head list; +}; + +bool async_usb_transfers(void) { - unsigned char *search; + bool ret; - if (endlen > tot) - return NULL; + cg_rlock(&cgusb_fd_lock); + ret = !list_empty(&ut_list); + cg_runlock(&cgusb_fd_lock); - // If end is only 1 char - do a faster search - if (endlen == 1) { - if (first) - search = buf; - else - search = ptr; + return ret; +} - return strchr((char *)search, *end); - } else { - if (first) - search = buf; - else { - // must allow end to have been chopped in 2 - if ((tot - ptrlen) >= (endlen - 1)) - search = ptr - (endlen - 1); - else - search = ptr - (tot - ptrlen); +/* Cancellable transfers should only be labelled as such if it is safe for them + * to effectively mimic timing out early. This flag is usually used to signify + * a read is waiting on a non-critical response that takes a long time and the + * driver wishes it be aborted if work restart message has been sent. */ +void cancel_usb_transfers(void) +{ + struct usb_transfer *ut; + int cancellations = 0; + + cg_wlock(&cgusb_fd_lock); + list_for_each_entry(ut, &ut_list, list) { + if (ut->cancellable) { + ut->cancellable = false; + libusb_cancel_transfer(ut->transfer); + cancellations++; } + } + cg_wunlock(&cgusb_fd_lock); + + if (cancellations) + applog(LOG_DEBUG, "Cancelled %d USB transfers", cancellations); +} + +static void init_usb_transfer(struct usb_transfer *ut) +{ + cgsem_init(&ut->cgsem); + ut->transfer = libusb_alloc_transfer(0); + if (unlikely(!ut->transfer)) + quit(1, "Failed to libusb_alloc_transfer"); + ut->transfer->user_data = ut; + ut->cancellable = false; +} + +static void complete_usb_transfer(struct usb_transfer *ut) +{ + cg_wlock(&cgusb_fd_lock); + list_del(&ut->list); + cg_wunlock(&cgusb_fd_lock); + + cgsem_destroy(&ut->cgsem); + libusb_free_transfer(ut->transfer); +} + +static void LIBUSB_CALL transfer_callback(struct libusb_transfer *transfer) +{ + struct usb_transfer *ut = transfer->user_data; + + ut->cancellable = false; + cgsem_post(&ut->cgsem); +} + +static int usb_transfer_toerr(int ret) +{ + if (ret <= 0) + return ret; - return strstr((char *)search, end); + switch (ret) { + default: + case LIBUSB_TRANSFER_COMPLETED: + ret = LIBUSB_SUCCESS; + break; + case LIBUSB_TRANSFER_ERROR: + ret = LIBUSB_ERROR_IO; + break; + case LIBUSB_TRANSFER_TIMED_OUT: + case LIBUSB_TRANSFER_CANCELLED: + ret = LIBUSB_ERROR_TIMEOUT; + break; + case LIBUSB_TRANSFER_STALL: + ret = LIBUSB_ERROR_PIPE; + break; + case LIBUSB_TRANSFER_NO_DEVICE: + ret = LIBUSB_ERROR_NO_DEVICE; + break; + case LIBUSB_TRANSFER_OVERFLOW: + ret = LIBUSB_ERROR_OVERFLOW; + break; } + return ret; } -#define USB_MAX_READ 8192 -#define USB_RETRY_MAX 5 +/* Wait for callback function to tell us it has finished the USB transfer, but + * use our own timer to cancel the request if we go beyond the timeout. */ +static int callback_wait(struct usb_transfer *ut, int *transferred, unsigned int timeout) +{ + struct libusb_transfer *transfer= ut->transfer; + int ret; + + ret = cgsem_mswait(&ut->cgsem, timeout); + if (ret == ETIMEDOUT) { + /* We are emulating a timeout ourself here */ + libusb_cancel_transfer(transfer); + + /* Now wait for the callback function to be invoked. */ + cgsem_wait(&ut->cgsem); + } + ret = transfer->status; + ret = usb_transfer_toerr(ret); + + /* No need to sort out mutexes here since they won't be reused */ + *transferred = transfer->actual_length; + + return ret; +} + +static int usb_submit_transfer(struct usb_transfer *ut, struct libusb_transfer *transfer, + bool cancellable, bool tt) +{ + int err; + + INIT_LIST_HEAD(&ut->list); + + cg_wlock(&cgusb_fd_lock); + /* Imitate a transaction translator for writes to usb1.1 devices */ + if (tt) + cgsleep_ms_r(&usb11_cgt, 1); + err = libusb_submit_transfer(transfer); + if (likely(!err)) + ut->cancellable = cancellable; + list_add(&ut->list, &ut_list); + if (tt) + cgtimer_time(&usb11_cgt); + cg_wunlock(&cgusb_fd_lock); + + return err; +} static int -usb_bulk_transfer(struct libusb_device_handle *dev_handle, - unsigned char endpoint, unsigned char *data, int length, - int *transferred, unsigned int timeout, - struct cgpu_info *cgpu, __maybe_unused int mode, - enum usb_cmds cmd, __maybe_unused int seq) -{ - uint16_t MaxPacketSize; - int err, errn, tries = 0; +usb_perform_transfer(struct cgpu_info *cgpu, struct cg_usb_device *usbdev, int intinfo, + int epinfo, unsigned char *data, int length, int *transferred, + unsigned int timeout, __maybe_unused int mode, enum usb_cmds cmd, + __maybe_unused int seq, bool cancellable, bool tt) +{ + int bulk_timeout, callback_timeout = timeout, err_retries = 0; + struct libusb_device_handle *dev_handle = usbdev->handle; + struct usb_epinfo *usb_epinfo; + struct usb_transfer ut; + unsigned char endpoint; + bool interrupt; + int err, errn; #if DO_USB_STATS struct timeval tv_start, tv_finish; #endif - unsigned char *buf; + unsigned char buf[512]; +#ifdef WIN32 + /* On windows the callback_timeout is a safety mechanism only. */ + bulk_timeout = timeout; + callback_timeout += WIN_CALLBACK_EXTRA; +#else + /* We give the transfer no timeout since we manage timeouts ourself on + * non windows. */ + bulk_timeout = 0; +#endif - /* Limit length of transfer to the largest this descriptor supports - * and leave the higher level functions to transfer more if needed. */ - if (cgpu->usbdev->PrefPacketSize) - MaxPacketSize = cgpu->usbdev->PrefPacketSize; - else - MaxPacketSize = cgpu->usbdev->found->wMaxPacketSize; - if (length > MaxPacketSize) - length = MaxPacketSize; - buf = alloca(MaxPacketSize); - if (endpoint == LIBUSB_ENDPOINT_OUT) - memcpy(buf, data, length); + usb_epinfo = &(usbdev->found->intinfos[intinfo].epinfos[epinfo]); + interrupt = usb_epinfo->att == LIBUSB_TRANSFER_TYPE_INTERRUPT; + endpoint = usb_epinfo->ep; + + if (unlikely(!data)) { + applog(LOG_ERR, "USB error: usb_perform_transfer sent NULL data (%s,intinfo=%d,epinfo=%d,length=%d,timeout=%u,mode=%d,cmd=%s,seq=%d) endpoint=%d", + cgpu->drv->name, intinfo, epinfo, length, timeout, mode, usb_cmdname(cmd), seq, (int)endpoint); + err = LIBUSB_ERROR_IO; + goto out_fail; + } + /* Avoid any async transfers during shutdown to allow the polling + * thread to be shut down after all existing transfers are complete */ + if (opt_lowmem || cgpu->shutdown) + return libusb_bulk_transfer(dev_handle, endpoint, data, length, transferred, timeout); +err_retry: + init_usb_transfer(&ut); + + if ((endpoint & LIBUSB_ENDPOINT_DIR_MASK) == LIBUSB_ENDPOINT_OUT) { + cg_memcpy(buf, data, length); +#ifndef HAVE_LIBUSB + /* Older versions may not have this feature so only enable it + * when we know we're compiling with included static libusb. We + * only do this for bulk transfer, not interrupt. */ + if (!cgpu->nozlp && !interrupt) + ut.transfer->flags |= LIBUSB_TRANSFER_ADD_ZERO_PACKET; +#endif +#ifdef WIN32 + /* Writes on windows really don't like to be cancelled, but + * are prone to timeouts under heavy USB traffic, so make this + * a last resort cancellation delayed long after the write + * would have timed out on its own. */ + callback_timeout += WIN_WRITE_CBEXTRA; +#endif + } + + USBDEBUG("USB debug: @usb_perform_transfer(%s (nodev=%s),intinfo=%d,epinfo=%d,data=%p,length=%d,timeout=%u,mode=%d,cmd=%s,seq=%d) endpoint=%d", cgpu->drv->name, bool_str(cgpu->usbinfo.nodev), intinfo, epinfo, data, length, timeout, mode, usb_cmdname(cmd), seq, (int)endpoint); + if (interrupt) { + libusb_fill_interrupt_transfer(ut.transfer, dev_handle, endpoint, + buf, length, transfer_callback, &ut, + bulk_timeout); + } else { + libusb_fill_bulk_transfer(ut.transfer, dev_handle, endpoint, buf, + length, transfer_callback, &ut, bulk_timeout); + } STATS_TIMEVAL(&tv_start); - cg_rlock(&cgusb_fd_lock); - err = libusb_bulk_transfer(dev_handle, endpoint, data, length, - transferred, timeout); + err = usb_submit_transfer(&ut, ut.transfer, cancellable, tt); errn = errno; - cg_runlock(&cgusb_fd_lock); + if (!err) + err = callback_wait(&ut, transferred, callback_timeout); + else + err = usb_transfer_toerr(err); + complete_usb_transfer(&ut); + STATS_TIMEVAL(&tv_finish); USB_STATS(cgpu, &tv_start, &tv_finish, err, mode, cmd, seq, timeout); - if (err < 0) + if (err < 0) { applog(LOG_DEBUG, "%s%i: %s (amt=%d err=%d ern=%d)", cgpu->drv->name, cgpu->device_id, usb_cmdname(cmd), *transferred, err, errn); + } if (err == LIBUSB_ERROR_PIPE) { - cgpu->usbinfo.last_pipe = time(NULL); - cgpu->usbinfo.pipe_count++; - applog(LOG_INFO, "%s%i: libusb pipe error, trying to clear", - cgpu->drv->name, cgpu->device_id); - do { - err = libusb_clear_halt(dev_handle, endpoint); - if (unlikely(err == LIBUSB_ERROR_NOT_FOUND || - err == LIBUSB_ERROR_NO_DEVICE)) { - cgpu->usbinfo.clear_err_count++; - break; - } + int pipeerr, retries = 0; - STATS_TIMEVAL(&tv_start); - cg_rlock(&cgusb_fd_lock); - err = libusb_bulk_transfer(dev_handle, endpoint, data, - length, transferred, timeout); - errn = errno; - cg_runlock(&cgusb_fd_lock); - STATS_TIMEVAL(&tv_finish); - USB_STATS(cgpu, &tv_start, &tv_finish, err, mode, cmd, seq, timeout); - - if (err < 0) - applog(LOG_DEBUG, "%s%i: %s (amt=%d err=%d ern=%d)", - cgpu->drv->name, cgpu->device_id, - usb_cmdname(cmd), *transferred, err, errn); - - if (err) - cgpu->usbinfo.retry_err_count++; - } while (err == LIBUSB_ERROR_PIPE && tries++ < USB_RETRY_MAX); - applog(LOG_DEBUG, "%s%i: libusb pipe error%scleared", - cgpu->drv->name, cgpu->device_id, err ? " not " : " "); - - if (err) - cgpu->usbinfo.clear_fail_count++; + do { + cgpu->usbinfo.last_pipe = time(NULL); + cgpu->usbinfo.pipe_count++; + applog(LOG_INFO, "%s%i: libusb pipe error, trying to clear", + cgpu->drv->name, cgpu->device_id); + pipeerr = libusb_clear_halt(dev_handle, endpoint); + applog(LOG_DEBUG, "%s%i: libusb pipe error%scleared", + cgpu->drv->name, cgpu->device_id, err ? " not " : " "); + + if (pipeerr) + cgpu->usbinfo.clear_fail_count++; + } while (pipeerr && ++retries < USB_RETRY_MAX); + if (!pipeerr && ++err_retries < USB_RETRY_MAX) + goto err_retry; } - if (endpoint == LIBUSB_ENDPOINT_OUT) - memcpy(data, buf, length); + if (err == LIBUSB_ERROR_IO && ++err_retries < USB_RETRY_MAX) + goto err_retry; +out_fail: + if (NODEV(err)) + *transferred = 0; + else if ((endpoint & LIBUSB_ENDPOINT_DIR_MASK) == LIBUSB_ENDPOINT_IN && *transferred) + cg_memcpy(data, buf, *transferred); return err; } -int _usb_read(struct cgpu_info *cgpu, int ep, char *buf, size_t bufsiz, int *processed, unsigned int timeout, const char *end, enum usb_cmds cmd, bool readonce) +void usb_reset(struct cgpu_info *cgpu) { - struct cg_usb_device *usbdev; - bool ftdi; + int pstate, err = 0; + + DEVRLOCK(cgpu, pstate); + if (!cgpu->usbinfo.nodev) { + err = libusb_reset_device(cgpu->usbdev->handle); + applog(LOG_WARNING, "%s %i attempted reset got err:(%d) %s", + cgpu->drv->name, cgpu->device_id, err, libusb_error_name(err)); + } + if (NODEV(err)) { + cg_ruwlock(&cgpu->usbinfo.devlock); + release_cgpu(cgpu); + DEVWUNLOCK(cgpu, pstate); + } else + DEVRUNLOCK(cgpu, pstate); +} + +int _usb_read(struct cgpu_info *cgpu, int intinfo, int epinfo, char *buf, size_t bufsiz, + int *processed, int timeout, const char *end, enum usb_cmds cmd, bool readonce, bool cancellable) +{ + unsigned char *ptr, usbbuf[USB_READ_BUFSIZE]; struct timeval read_start, tv_finish; + int bufleft, err, got, tot, pstate, tried_reset; + struct cg_usb_device *usbdev; unsigned int initial_timeout; - double max, done; - int bufleft, err, got, tot, pstate; bool first = true; - char *search; - int endlen; - unsigned char *ptr, *usbbuf = cgpu->usbinfo.bulkbuf; size_t usbbufread; + int endlen = 0; + char *eom = NULL; + double done; + bool ftdi; + + memset(usbbuf, 0, USB_READ_BUFSIZE); + memset(buf, 0, bufsiz); - DEVLOCK(cgpu, pstate); + if (end) + endlen = strlen(end); + DEVRLOCK(cgpu, pstate); if (cgpu->usbinfo.nodev) { - *buf = '\0'; *processed = 0; USB_REJECT(cgpu, MODE_BULK_READ); err = LIBUSB_ERROR_NO_DEVICE; - goto out_unlock; + goto out_noerrmsg; } usbdev = cgpu->usbdev; + /* Interrupt transfers are guaranteed to be of an expected size (we hope) */ + if (usbdev->found->intinfos[intinfo].epinfos[epinfo].att == LIBUSB_TRANSFER_TYPE_INTERRUPT) + usbbufread = bufsiz; + else + usbbufread = 512; + ftdi = (usbdev->usb_type == USB_TYPE_FTDI); - USBDEBUG("USB debug: _usb_read(%s (nodev=%s),ep=%d,buf=%p,bufsiz=%zu,proc=%p,timeout=%u,end=%s,cmd=%s,ftdi=%s,readonce=%s)", cgpu->drv->name, bool_str(cgpu->usbinfo.nodev), ep, buf, bufsiz, processed, timeout, end ? (char *)str_text((char *)end) : "NULL", usb_cmdname(cmd), bool_str(ftdi), bool_str(readonce)); + USBDEBUG("USB debug: _usb_read(%s (nodev=%s),intinfo=%d,epinfo=%d,buf=%p,bufsiz=%d,proc=%p,timeout=%u,end=%s,cmd=%s,ftdi=%s,readonce=%s)", cgpu->drv->name, bool_str(cgpu->usbinfo.nodev), intinfo, epinfo, buf, (int)bufsiz, processed, timeout, end ? (char *)str_text((char *)end) : "NULL", usb_cmdname(cmd), bool_str(ftdi), bool_str(readonce)); if (bufsiz > USB_MAX_READ) quit(1, "%s USB read request %d too large (max=%d)", cgpu->drv->name, (int)bufsiz, USB_MAX_READ); @@ -2392,165 +3296,28 @@ int _usb_read(struct cgpu_info *cgpu, int ep, char *buf, size_t bufsiz, int *pro if (timeout == DEVTIMEOUT) timeout = usbdev->found->timeout; - if (end == NULL) { - if (usbdev->buffer && usbdev->bufamt) { - tot = usbdev->bufamt; - bufleft = bufsiz - tot; - memcpy(usbbuf, usbdev->buffer, tot); - ptr = usbbuf + tot; - usbdev->bufamt = 0; - } else { - tot = 0; - bufleft = bufsiz; - ptr = usbbuf; - } - - err = LIBUSB_SUCCESS; - initial_timeout = timeout; - max = ((double)timeout) / 1000.0; - cgtime(&read_start); - while (bufleft > 0) { - // TODO: use (USB_MAX_READ - tot) always? - if (usbdev->buffer) - usbbufread = USB_MAX_READ - tot; - else { - if (ftdi) - usbbufread = bufleft + 2; - else - usbbufread = bufleft; - } - got = 0; - - if (first && usbdev->usecps && usbdev->last_write_siz) { - cgtimer_t now, already_done; - double sleep_estimate; - double write_time = (double)(usbdev->last_write_siz) / - (double)(usbdev->cps); - - cgtimer_time(&now); - cgtimer_sub(&now, &usbdev->cgt_last_write, &already_done); - sleep_estimate = write_time - cgtimer_to_ms(&already_done); - - if (sleep_estimate > 0.0) { - cgsleep_ms_r(&usbdev->cgt_last_write, write_time * 1000.0); - cgpu->usbinfo.read_delay_count++; - cgpu->usbinfo.total_read_delay += sleep_estimate; - } - } - err = usb_bulk_transfer(usbdev->handle, - usbdev->found->eps[ep].ep, - ptr, usbbufread, &got, timeout, - cgpu, MODE_BULK_READ, cmd, first ? SEQ0 : SEQ1); - cgtime(&tv_finish); - ptr[got] = '\0'; - - USBDEBUG("USB debug: @_usb_read(%s (nodev=%s)) first=%s err=%d%s got=%d ptr='%s' usbbufread=%zu", cgpu->drv->name, bool_str(cgpu->usbinfo.nodev), bool_str(first), err, isnodev(err), got, (char *)str_text((char *)ptr), usbbufread); - - IOERR_CHECK(cgpu, err); - - if (ftdi) { - // first 2 bytes returned are an FTDI status - if (got > 2) { - got -= 2; - memmove(ptr, ptr+2, got+1); - } else { - got = 0; - *ptr = '\0'; - } - } - - tot += got; - - if (err || readonce) - break; - - ptr += got; - bufleft -= got; - - first = false; - - done = tdiff(&tv_finish, &read_start); - // N.B. this is: return LIBUSB_SUCCESS with whatever size has already been read - if (unlikely(done >= max)) - break; - timeout = initial_timeout - (done * 1000); - if (!timeout) - break; - } - - // N.B. usbdev->buffer was emptied before the while() loop - if (usbdev->buffer && tot > (int)bufsiz) { - usbdev->bufamt = tot - bufsiz; - memcpy(usbdev->buffer, ptr + bufsiz, usbdev->bufamt); - tot -= usbdev->bufamt; - usbbuf[tot] = '\0'; - applog(LOG_ERR, "USB: %s%i read1 buffering %d extra bytes", - cgpu->drv->name, cgpu->device_id, usbdev->bufamt); - } - - *processed = tot; - memcpy((char *)buf, (const char *)usbbuf, (tot < (int)bufsiz) ? tot + 1 : (int)bufsiz); + tot = usbdev->bufamt; + bufleft = bufsiz - tot; + if (tot) + cg_memcpy(usbbuf, usbdev->buffer, tot); + ptr = usbbuf + tot; + usbdev->bufamt = 0; - if (NODEV(err)) - release_cgpu(cgpu); - - goto out_unlock; - } - - if (usbdev->buffer && usbdev->bufamt) { - tot = usbdev->bufamt; - bufleft = bufsiz - tot; - memcpy(usbbuf, usbdev->buffer, tot); - ptr = usbbuf + tot; - usbdev->bufamt = 0; - } else { - tot = 0; - bufleft = bufsiz; - ptr = usbbuf; - } - - endlen = strlen(end); err = LIBUSB_SUCCESS; + if (end != NULL) + eom = strstr((const char *)usbbuf, end); + initial_timeout = timeout; - max = ((double)timeout) / 1000.0; cgtime(&read_start); - while (bufleft > 0) { - // TODO: use (USB_MAX_READ - tot) always? - if (usbdev->buffer) - usbbufread = USB_MAX_READ - tot; - else { - if (ftdi) - usbbufread = bufleft + 2; - else - usbbufread = bufleft; - } - got = 0; - if (first && usbdev->usecps && usbdev->last_write_siz) { - cgtimer_t now, already_done; - double sleep_estimate; - double write_time = (double)(usbdev->last_write_siz) / - (double)(usbdev->cps); - - cgtimer_time(&now); - cgtimer_sub(&now, &usbdev->cgt_last_write, &already_done); - sleep_estimate = write_time - cgtimer_to_ms(&already_done); - - if (sleep_estimate > 0.0) { - cgsleep_ms_r(&usbdev->cgt_last_write, write_time * 1000.0); - cgpu->usbinfo.read_delay_count++; - cgpu->usbinfo.total_read_delay += sleep_estimate; - } - } - err = usb_bulk_transfer(usbdev->handle, - usbdev->found->eps[ep].ep, ptr, - usbbufread, &got, timeout, - cgpu, MODE_BULK_READ, cmd, first ? SEQ0 : SEQ1); + tried_reset = 0; + while (bufleft > 0 && !eom) { + err = usb_perform_transfer(cgpu, usbdev, intinfo, epinfo, ptr, usbbufread, + &got, timeout, MODE_BULK_READ, cmd, + first ? SEQ0 : SEQ1, cancellable, false); cgtime(&tv_finish); ptr[got] = '\0'; - USBDEBUG("USB debug: @_usb_read(%s (nodev=%s)) first=%s err=%d%s got=%d ptr='%s' usbbufread=%zu", cgpu->drv->name, bool_str(cgpu->usbinfo.nodev), bool_str(first), err, isnodev(err), got, (char *)str_text((char *)ptr), usbbufread); - - IOERR_CHECK(cgpu, err); + USBDEBUG("USB debug: @_usb_read(%s (nodev=%s)) first=%s err=%d%s got=%d ptr='%s' usbbufread=%d", cgpu->drv->name, bool_str(cgpu->usbinfo.nodev), bool_str(first), err, isnodev(err), got, (char *)str_text((char *)ptr), (int)usbbufread); if (ftdi) { // first 2 bytes returned are an FTDI status @@ -2564,83 +3331,89 @@ int _usb_read(struct cgpu_info *cgpu, int ep, char *buf, size_t bufsiz, int *pro } tot += got; + if (end != NULL) + eom = strstr((const char *)usbbuf, end); + + /* Attempt a usb reset for an error that will otherwise cause + * this device to drop out provided we know the device still + * might exist. */ + if (err && err != LIBUSB_ERROR_TIMEOUT) { + applog(LOG_WARNING, "%s %i %s usb read err:(%d) %s", cgpu->drv->name, + cgpu->device_id, usb_cmdname(cmd), err, libusb_error_name(err)); + if (err != LIBUSB_ERROR_NO_DEVICE && !tried_reset) { + err = libusb_reset_device(usbdev->handle); + tried_reset = 1; // don't call reset twice in a row + applog(LOG_WARNING, "%s %i attempted reset got err:(%d) %s", + cgpu->drv->name, cgpu->device_id, err, libusb_error_name(err)); + } + } else { + tried_reset = 0; + } + ptr += got; + bufleft -= got; + if (bufleft < 1) + err = LIBUSB_SUCCESS; if (err || readonce) break; - if (find_end(usbbuf, ptr, got, tot, (char *)end, endlen, first)) - break; - - ptr += got; - bufleft -= got; first = false; done = tdiff(&tv_finish, &read_start); - // N.B. this is: return LIBUSB_SUCCESS with whatever size has already been read - if (unlikely(done >= max)) - break; + // N.B. this is: return last err with whatever size has already been read timeout = initial_timeout - (done * 1000); - if (!timeout) - break; - } - - if (usbdev->buffer) { - bool dobuffer = false; - - if ((search = find_end(usbbuf, usbbuf, tot, tot, (char *)end, endlen, true))) { - // end finishes after bufsiz - if ((search + endlen - (char *)usbbuf) > (int)bufsiz) { - usbdev->bufamt = tot - bufsiz; - dobuffer = true; - } else { - // extra data after end - if (*(search + endlen)) { - usbdev->bufamt = tot - (search + endlen - (char *)usbbuf); - dobuffer = true; - } - } - } else { - // no end, but still bigger than bufsiz - if (tot > (int)bufsiz) { - usbdev->bufamt = tot - bufsiz; - dobuffer = true; - } - } + if (timeout <= 0) + break; + } - if (dobuffer) { - tot -= usbdev->bufamt; - memcpy(usbdev->buffer, usbbuf + tot, usbdev->bufamt); - usbbuf[tot] = '\0'; - applog(LOG_ERR, "USB: %s%i read2 buffering %d extra bytes", - cgpu->drv->name, cgpu->device_id, usbdev->bufamt); + /* If we found the end of message marker, just use that data and + * return success. */ + if (eom) { + size_t eomlen = (void *)eom - (void *)usbbuf + endlen; + + if (eomlen < bufsiz) { + bufsiz = eomlen; + err = LIBUSB_SUCCESS; } } + // N.B. usbdev->buffer was emptied before the while() loop + if (tot > (int)bufsiz) { + usbdev->bufamt = tot - bufsiz; + cg_memcpy(usbdev->buffer, usbbuf + bufsiz, usbdev->bufamt); + tot -= usbdev->bufamt; + usbbuf[tot] = '\0'; + applog(LOG_DEBUG, "USB: %s%i read1 buffering %d extra bytes", + cgpu->drv->name, cgpu->device_id, usbdev->bufamt); + } + *processed = tot; - memcpy((char *)buf, (const char *)usbbuf, (tot < (int)bufsiz) ? tot + 1 : (int)bufsiz); + cg_memcpy((char *)buf, (const char *)usbbuf, (tot < (int)bufsiz) ? tot + 1 : (int)bufsiz); - if (NODEV(err)) +out_noerrmsg: + if (NODEV(err)) { + cg_ruwlock(&cgpu->usbinfo.devlock); release_cgpu(cgpu); - -out_unlock: - DEVUNLOCK(cgpu, pstate); + DEVWUNLOCK(cgpu, pstate); + } else + DEVRUNLOCK(cgpu, pstate); return err; } -int _usb_write(struct cgpu_info *cgpu, int ep, char *buf, size_t bufsiz, int *processed, unsigned int timeout, enum usb_cmds cmd) +int _usb_write(struct cgpu_info *cgpu, int intinfo, int epinfo, char *buf, size_t bufsiz, int *processed, int timeout, enum usb_cmds cmd) { + struct timeval write_start, tv_finish; struct cg_usb_device *usbdev; - struct timeval read_start, tv_finish; unsigned int initial_timeout; - double max, done; - __maybe_unused bool first = true; - int err, sent, tot, pstate; + int err, sent, tot, pstate, tried_reset; + bool first = true; + double done; - DEVLOCK(cgpu, pstate); + DEVRLOCK(cgpu, pstate); - USBDEBUG("USB debug: _usb_write(%s (nodev=%s),ep=%d,buf='%s',bufsiz=%zu,proc=%p,timeout=%u,cmd=%s)", cgpu->drv->name, bool_str(cgpu->usbinfo.nodev), ep, (char *)str_text(buf), bufsiz, processed, timeout, usb_cmdname(cmd)); + USBDEBUG("USB debug: _usb_write(%s (nodev=%s),intinfo=%d,epinfo=%d,buf='%s',bufsiz=%d,proc=%p,timeout=%u,cmd=%s)", cgpu->drv->name, bool_str(cgpu->usbinfo.nodev), intinfo, epinfo, (char *)str_text(buf), (int)bufsiz, processed, timeout, usb_cmdname(cmd)); *processed = 0; @@ -2648,7 +3421,7 @@ int _usb_write(struct cgpu_info *cgpu, int ep, char *buf, size_t bufsiz, int *pr USB_REJECT(cgpu, MODE_BULK_WRITE); err = LIBUSB_ERROR_NO_DEVICE; - goto out_unlock; + goto out_noerrmsg; } usbdev = cgpu->usbdev; @@ -2658,42 +3431,46 @@ int _usb_write(struct cgpu_info *cgpu, int ep, char *buf, size_t bufsiz, int *pr tot = 0; err = LIBUSB_SUCCESS; initial_timeout = timeout; - max = ((double)timeout) / 1000.0; - cgtime(&read_start); + cgtime(&write_start); + tried_reset = 0; while (bufsiz > 0) { - sent = 0; - if (usbdev->usecps) { - if (usbdev->last_write_siz) { - cgtimer_t now, already_done; - double sleep_estimate; - double write_time = (double)(usbdev->last_write_siz) / - (double)(usbdev->cps); - - cgtimer_time(&now); - cgtimer_sub(&now, &usbdev->cgt_last_write, &already_done); - sleep_estimate = write_time - cgtimer_to_ms(&already_done); - - if (sleep_estimate > 0.0) { - cgsleep_ms_r(&usbdev->cgt_last_write, write_time * 1000.0); - cgpu->usbinfo.write_delay_count++; - cgpu->usbinfo.total_write_delay += sleep_estimate; - } + int tosend = bufsiz; + + /* USB 1.1 devices don't handle zero packets well so split them + * up to not have the final transfer equal to the wMaxPacketSize + * or they will stall waiting for more data. */ + if (usbdev->usb11) { + struct usb_epinfo *ue = &usbdev->found->intinfos[intinfo].epinfos[epinfo]; + + if (tosend == ue->wMaxPacketSize) { + tosend >>= 1; + if (unlikely(!tosend)) + tosend = 1; } - cgsleep_prepare_r(&usbdev->cgt_last_write); - usbdev->last_write_siz = bufsiz; } - err = usb_bulk_transfer(usbdev->handle, - usbdev->found->eps[ep].ep, - (unsigned char *)buf, bufsiz, &sent, - timeout, cgpu, MODE_BULK_WRITE, cmd, first ? SEQ0 : SEQ1); + err = usb_perform_transfer(cgpu, usbdev, intinfo, epinfo, (unsigned char *)buf, + tosend, &sent, timeout, MODE_BULK_WRITE, + cmd, first ? SEQ0 : SEQ1, false, usbdev->tt); cgtime(&tv_finish); USBDEBUG("USB debug: @_usb_write(%s (nodev=%s)) err=%d%s sent=%d", cgpu->drv->name, bool_str(cgpu->usbinfo.nodev), err, isnodev(err), sent); - IOERR_CHECK(cgpu, err); - tot += sent; + /* Unlike reads, even a timeout error is unrecoverable on + * writes. */ + if (err) { + applog(LOG_WARNING, "%s %i %s usb write err:(%d) %s", cgpu->drv->name, + cgpu->device_id, usb_cmdname(cmd), err, libusb_error_name(err)); + if (err != LIBUSB_ERROR_NO_DEVICE && !tried_reset) { + err = libusb_reset_device(usbdev->handle); + tried_reset = 1; // don't try reset twice in a row + applog(LOG_WARNING, "%s %i attempted reset got err:(%d) %s", + cgpu->drv->name, cgpu->device_id, err, libusb_error_name(err)); + } + } else { + tried_reset = 0; + } if (err) break; @@ -2702,23 +3479,65 @@ int _usb_write(struct cgpu_info *cgpu, int ep, char *buf, size_t bufsiz, int *pr first = false; - done = tdiff(&tv_finish, &read_start); - // N.B. this is: return LIBUSB_SUCCESS with whatever size was written - if (unlikely(done >= max)) - break; + done = tdiff(&tv_finish, &write_start); + // N.B. this is: return last err with whatever size was written timeout = initial_timeout - (done * 1000); - if (!timeout) + if (timeout <= 0) break; } *processed = tot; - if (NODEV(err)) +out_noerrmsg: + if (NODEV(err)) { + cg_ruwlock(&cgpu->usbinfo.devlock); release_cgpu(cgpu); + DEVWUNLOCK(cgpu, pstate); + } else + DEVRUNLOCK(cgpu, pstate); -out_unlock: - DEVUNLOCK(cgpu, pstate); + return err; +} +/* As we do for bulk reads, emulate a sync function for control transfers using + * our own timeouts that takes the same parameters as libusb_control_transfer. + */ +static int usb_control_transfer(struct cgpu_info *cgpu, libusb_device_handle *dev_handle, uint8_t bmRequestType, + uint8_t bRequest, uint16_t wValue, uint16_t wIndex, + unsigned char *buffer, uint16_t wLength, unsigned int timeout) +{ + struct usb_transfer ut; + unsigned char buf[70]; + int err, transferred; + bool tt = false; + + if (unlikely(cgpu->shutdown)) + return libusb_control_transfer(dev_handle, bmRequestType, bRequest, wValue, wIndex, buffer, wLength, timeout); + + init_usb_transfer(&ut); + libusb_fill_control_setup(buf, bmRequestType, bRequest, wValue, + wIndex, wLength); + if ((bmRequestType & LIBUSB_ENDPOINT_DIR_MASK) == LIBUSB_ENDPOINT_OUT) { + if (wLength) + cg_memcpy(buf + LIBUSB_CONTROL_SETUP_SIZE, buffer, wLength); + if (cgpu->usbdev->descriptor->bcdUSB < 0x0200) + tt = true; + } + libusb_fill_control_transfer(ut.transfer, dev_handle, buf, transfer_callback, + &ut, 0); + err = usb_submit_transfer(&ut, ut.transfer, false, tt); + if (!err) + err = callback_wait(&ut, &transferred, timeout); + if (err == LIBUSB_SUCCESS && transferred) { + if ((bmRequestType & LIBUSB_ENDPOINT_DIR_MASK) == LIBUSB_ENDPOINT_IN) + cg_memcpy(buffer, libusb_control_transfer_get_data(ut.transfer), + transferred); + err = transferred; + goto out; + } + err = usb_transfer_toerr(err); +out: + complete_usb_transfer(&ut); return err; } @@ -2756,54 +3575,38 @@ int __usb_transfer(struct cgpu_info *cgpu, uint8_t request_type, uint8_t bReques USBDEBUG("USB debug: @_usb_transfer() buf=%s", bin2hex(buf, (size_t)siz)); - if (usbdev->usecps) { - if (usbdev->last_write_siz) { - cgtimer_t now, already_done; - double sleep_estimate; - double write_time = (double)(usbdev->last_write_siz) / - (double)(usbdev->cps); - - cgtimer_time(&now); - cgtimer_sub(&now, &usbdev->cgt_last_write, &already_done); - sleep_estimate = write_time - cgtimer_to_ms(&already_done); - - if (sleep_estimate > 0.0) { - cgsleep_ms_r(&usbdev->cgt_last_write, write_time * 1000.0); - cgpu->usbinfo.write_delay_count++; - cgpu->usbinfo.total_write_delay += sleep_estimate; - } - } - cgsleep_prepare_r(&usbdev->cgt_last_write); - usbdev->last_write_siz = siz; - } STATS_TIMEVAL(&tv_start); - cg_rlock(&cgusb_fd_lock); - err = libusb_control_transfer(usbdev->handle, request_type, - bRequest, wValue, wIndex, buf, (uint16_t)siz, timeout); - cg_runlock(&cgusb_fd_lock); + err = usb_control_transfer(cgpu, usbdev->handle, request_type, bRequest, + wValue, wIndex, buf, (uint16_t)siz, timeout); STATS_TIMEVAL(&tv_finish); USB_STATS(cgpu, &tv_start, &tv_finish, err, MODE_CTRL_WRITE, cmd, SEQ0, timeout); USBDEBUG("USB debug: @_usb_transfer(%s (nodev=%s)) err=%d%s", cgpu->drv->name, bool_str(cgpu->usbinfo.nodev), err, isnodev(err)); - IOERR_CHECK(cgpu, err); - - if (NOCONTROLDEV(err)) - release_cgpu(cgpu); - + if (err < 0 && err != LIBUSB_ERROR_TIMEOUT) { + applog(LOG_WARNING, "%s %i usb transfer err:(%d) %s", cgpu->drv->name, cgpu->device_id, + err, libusb_error_name(err)); + } out_: return err; } +/* We use the write devlock for control transfers since some control transfers + * are rare but may be changing settings within the device causing problems + * if concurrent transfers are happening. Using the write lock serialises + * any transfers. */ int _usb_transfer(struct cgpu_info *cgpu, uint8_t request_type, uint8_t bRequest, uint16_t wValue, uint16_t wIndex, uint32_t *data, int siz, unsigned int timeout, enum usb_cmds cmd) { int pstate, err; - DEVLOCK(cgpu, pstate); + DEVWLOCK(cgpu, pstate); err = __usb_transfer(cgpu, request_type, bRequest, wValue, wIndex, data, siz, timeout, cmd); - DEVUNLOCK(cgpu, pstate); + if (NOCONTROLDEV(err)) + release_cgpu(cgpu); + + DEVWUNLOCK(cgpu, pstate); return err; } @@ -2817,7 +3620,7 @@ int _usb_transfer_read(struct cgpu_info *cgpu, uint8_t request_type, uint8_t bRe unsigned char tbuf[64]; int err, pstate; - DEVLOCK(cgpu, pstate); + DEVWLOCK(cgpu, pstate); USBDEBUG("USB debug: _usb_transfer_read(%s (nodev=%s),type=%"PRIu8",req=%"PRIu8",value=%"PRIu16",index=%"PRIu16",bufsiz=%d,timeout=%u,cmd=%s)", cgpu->drv->name, bool_str(cgpu->usbinfo.nodev), request_type, bRequest, wValue, wIndex, bufsiz, timeout, usb_cmdname(cmd)); @@ -2825,7 +3628,7 @@ int _usb_transfer_read(struct cgpu_info *cgpu, uint8_t request_type, uint8_t bRe USB_REJECT(cgpu, MODE_CTRL_READ); err = LIBUSB_ERROR_NO_DEVICE; - goto out_unlock; + goto out_noerrmsg; } usbdev = cgpu->usbdev; if (timeout == DEVTIMEOUT) @@ -2833,45 +3636,29 @@ int _usb_transfer_read(struct cgpu_info *cgpu, uint8_t request_type, uint8_t bRe *amount = 0; - if (usbdev->usecps && usbdev->last_write_siz) { - cgtimer_t now, already_done; - double sleep_estimate; - double write_time = (double)(usbdev->last_write_siz) / - (double)(usbdev->cps); - - cgtimer_time(&now); - cgtimer_sub(&now, &usbdev->cgt_last_write, &already_done); - sleep_estimate = write_time - cgtimer_to_ms(&already_done); - - if (sleep_estimate > 0.0) { - cgsleep_ms_r(&usbdev->cgt_last_write, write_time * 1000.0); - cgpu->usbinfo.read_delay_count++; - cgpu->usbinfo.total_read_delay += sleep_estimate; - } - } memset(tbuf, 0, 64); STATS_TIMEVAL(&tv_start); - cg_rlock(&cgusb_fd_lock); - err = libusb_control_transfer(usbdev->handle, request_type, - bRequest, wValue, wIndex, - tbuf, (uint16_t)bufsiz, timeout); - cg_runlock(&cgusb_fd_lock); + err = usb_control_transfer(cgpu, usbdev->handle, request_type, bRequest, + wValue, wIndex, tbuf, (uint16_t)bufsiz, timeout); STATS_TIMEVAL(&tv_finish); USB_STATS(cgpu, &tv_start, &tv_finish, err, MODE_CTRL_READ, cmd, SEQ0, timeout); - memcpy(buf, tbuf, bufsiz); + cg_memcpy(buf, tbuf, bufsiz); USBDEBUG("USB debug: @_usb_transfer_read(%s (nodev=%s)) amt/err=%d%s%s%s", cgpu->drv->name, bool_str(cgpu->usbinfo.nodev), err, isnodev(err), err > 0 ? " = " : BLANK, err > 0 ? bin2hex((unsigned char *)buf, (size_t)err) : BLANK); - IOERR_CHECK(cgpu, err); - if (err > 0) { *amount = err; err = 0; - } else if (NOCONTROLDEV(err)) + } + if (err < 0 && err != LIBUSB_ERROR_TIMEOUT) { + applog(LOG_WARNING, "%s %i usb transfer read err:(%d) %s", cgpu->drv->name, cgpu->device_id, + err, libusb_error_name(err)); + } +out_noerrmsg: + if (NOCONTROLDEV(err)) release_cgpu(cgpu); -out_unlock: - DEVUNLOCK(cgpu, pstate); + DEVWUNLOCK(cgpu, pstate); return err; } @@ -2900,12 +3687,12 @@ int usb_ftdi_cts(struct cgpu_info *cgpu) return (ret & FTDI_RS0_CTS); } -int usb_ftdi_set_latency(struct cgpu_info *cgpu) +int _usb_ftdi_set_latency(struct cgpu_info *cgpu, int intinfo) { int err = 0; int pstate; - DEVLOCK(cgpu, pstate); + DEVWLOCK(cgpu, pstate); if (cgpu->usbdev) { if (cgpu->usbdev->usb_type != USB_TYPE_FTDI) { @@ -2921,11 +3708,11 @@ int usb_ftdi_set_latency(struct cgpu_info *cgpu) if (!err) err = __usb_transfer(cgpu, FTDI_TYPE_OUT, FTDI_REQUEST_LATENCY, cgpu->usbdev->found->latency, - cgpu->usbdev->found->interface, + USBIF(cgpu->usbdev, intinfo), NULL, 0, DEVTIMEOUT, C_LATENCY); } - DEVUNLOCK(cgpu, pstate); + DEVWUNLOCK(cgpu, pstate); applog(LOG_DEBUG, "%s: cgid %d %s got err %d", cgpu->drv->name, cgpu->cgminer_id, @@ -2934,54 +3721,16 @@ int usb_ftdi_set_latency(struct cgpu_info *cgpu) return err; } -void usb_buffer_enable(struct cgpu_info *cgpu) -{ - struct cg_usb_device *cgusb; - int pstate; - - DEVLOCK(cgpu, pstate); - - cgusb = cgpu->usbdev; - if (cgusb && !cgusb->buffer) { - cgusb->bufamt = 0; - cgusb->buffer = malloc(USB_MAX_READ+1); - if (!cgusb->buffer) - quit(1, "Failed to malloc buffer for USB %s%i", - cgpu->drv->name, cgpu->device_id); - cgusb->bufsiz = USB_MAX_READ; - } - - DEVUNLOCK(cgpu, pstate); -} - -void usb_buffer_disable(struct cgpu_info *cgpu) -{ - struct cg_usb_device *cgusb; - int pstate; - - DEVLOCK(cgpu, pstate); - - cgusb = cgpu->usbdev; - if (cgusb && cgusb->buffer) { - cgusb->bufamt = 0; - cgusb->bufsiz = 0; - free(cgusb->buffer); - cgusb->buffer = NULL; - } - - DEVUNLOCK(cgpu, pstate); -} - void usb_buffer_clear(struct cgpu_info *cgpu) { int pstate; - DEVLOCK(cgpu, pstate); + DEVWLOCK(cgpu, pstate); if (cgpu->usbdev) cgpu->usbdev->bufamt = 0; - DEVUNLOCK(cgpu, pstate); + DEVWUNLOCK(cgpu, pstate); } uint32_t usb_buffer_size(struct cgpu_info *cgpu) @@ -2989,68 +3738,34 @@ uint32_t usb_buffer_size(struct cgpu_info *cgpu) uint32_t ret = 0; int pstate; - DEVLOCK(cgpu, pstate); + DEVRLOCK(cgpu, pstate); if (cgpu->usbdev) ret = cgpu->usbdev->bufamt; - DEVUNLOCK(cgpu, pstate); + DEVRUNLOCK(cgpu, pstate); return ret; } -void usb_set_cps(struct cgpu_info *cgpu, int cps) -{ - int pstate; - - DEVLOCK(cgpu, pstate); - - if (cgpu->usbdev) - cgpu->usbdev->cps = cps; - - DEVUNLOCK(cgpu, pstate); -} - -void usb_enable_cps(struct cgpu_info *cgpu) -{ - int pstate; - - DEVLOCK(cgpu, pstate); - - if (cgpu->usbdev) - cgpu->usbdev->usecps = true; - - DEVUNLOCK(cgpu, pstate); -} - -void usb_disable_cps(struct cgpu_info *cgpu) -{ - int pstate; - - DEVLOCK(cgpu, pstate); - - if (cgpu->usbdev) - cgpu->usbdev->usecps = false; - - DEVUNLOCK(cgpu, pstate); -} - /* * The value returned (0) when usbdev is NULL * doesn't matter since it also means the next call to * any usbutils function will fail with a nodev + * N.B. this is to get the interface number to use in a control_transfer + * which for some devices isn't actually the interface number */ -int usb_interface(struct cgpu_info *cgpu) +int _usb_interface(struct cgpu_info *cgpu, int intinfo) { int interface = 0; int pstate; - DEVLOCK(cgpu, pstate); + DEVRLOCK(cgpu, pstate); if (cgpu->usbdev) - interface = cgpu->usbdev->found->interface; + interface = cgpu->usbdev->found->intinfos[intinfo].ctrl_transfer; - DEVUNLOCK(cgpu, pstate); + DEVRUNLOCK(cgpu, pstate); return interface; } @@ -3060,28 +3775,16 @@ enum sub_ident usb_ident(struct cgpu_info *cgpu) enum sub_ident ident = IDENT_UNK; int pstate; - DEVLOCK(cgpu, pstate); + DEVRLOCK(cgpu, pstate); if (cgpu->usbdev) ident = cgpu->usbdev->ident; - DEVUNLOCK(cgpu, pstate); + DEVRUNLOCK(cgpu, pstate); return ident; } -void usb_set_pps(struct cgpu_info *cgpu, uint16_t PrefPacketSize) -{ - int pstate; - - DEVLOCK(cgpu, pstate); - - if (cgpu->usbdev) - cgpu->usbdev->PrefPacketSize = PrefPacketSize; - - DEVUNLOCK(cgpu, pstate); -} - // Need to set all devices with matching usbdev void usb_set_dev_start(struct cgpu_info *cgpu) { @@ -3090,7 +3793,7 @@ void usb_set_dev_start(struct cgpu_info *cgpu) struct timeval now; int pstate; - DEVLOCK(cgpu, pstate); + DEVWLOCK(cgpu, pstate); cgusb = cgpu->usbdev; @@ -3107,13 +3810,13 @@ void usb_set_dev_start(struct cgpu_info *cgpu) } } - DEVUNLOCK(cgpu, pstate); + DEVWUNLOCK(cgpu, pstate); } -void usb_cleanup() +void usb_cleanup(void) { struct cgpu_info *cgpu; - int count; + int count, pstate; int i; hotplug_time = 0; @@ -3124,14 +3827,23 @@ void usb_cleanup() for (i = 0; i < total_devices; i++) { cgpu = devices[i]; switch (cgpu->drv->drv_id) { - case DRIVER_BFLSC: - case DRIVER_BITFORCE: - case DRIVER_MODMINER: - case DRIVER_ICARUS: - case DRIVER_AVALON: - wr_lock(cgpu->usbinfo.devlock); + case DRIVER_bflsc: + case DRIVER_bitforce: + case DRIVER_bitfury: + case DRIVER_cointerra: + case DRIVER_drillbit: + case DRIVER_modminer: + case DRIVER_icarus: + case DRIVER_avalon: + case DRIVER_avalon2: + case DRIVER_avalon4: + case DRIVER_bitmain: + case DRIVER_bmsc: + case DRIVER_klondike: + case DRIVER_hashfast: + DEVWLOCK(cgpu, pstate); release_cgpu(cgpu); - wr_unlock(cgpu->usbinfo.devlock); + DEVWUNLOCK(cgpu, pstate); count++; break; default: @@ -3170,12 +3882,18 @@ void usb_cleanup() cgsem_destroy(&usb_resource_sem); } -void usb_initialise() +#define DRIVER_COUNT_FOUND(X) if (X##_drv.name && strcasecmp(ptr, X##_drv.name) == 0) { \ + drv_count[X##_drv.drv_id].limit = lim; \ + found = true; \ + } +void usb_initialise(void) { char *fre, *ptr, *comma, *colon; int bus, dev, lim, i; bool found; + INIT_LIST_HEAD(&ut_list); + for (i = 0; i < DRIVER_MAX; i++) { drv_count[i].count = 0; drv_count[i].limit = 999999; @@ -3253,36 +3971,9 @@ void usb_initialise() quit(1, "Invalid --usb DRV:limit - limit must be >= 0"); found = false; -#ifdef USE_BFLSC - if (strcasecmp(ptr, bflsc_drv.name) == 0) { - drv_count[bflsc_drv.drv_id].limit = lim; - found = true; - } -#endif -#ifdef USE_BITFORCE - if (!found && strcasecmp(ptr, bitforce_drv.name) == 0) { - drv_count[bitforce_drv.drv_id].limit = lim; - found = true; - } -#endif -#ifdef USE_MODMINER - if (!found && strcasecmp(ptr, modminer_drv.name) == 0) { - drv_count[modminer_drv.drv_id].limit = lim; - found = true; - } -#endif -#ifdef USE_ICARUS - if (!found && strcasecmp(ptr, icarus_drv.name) == 0) { - drv_count[icarus_drv.drv_id].limit = lim; - found = true; - } -#endif -#ifdef USE_AVALON - if (!found && strcasecmp(ptr, avalon_drv.name) == 0) { - drv_count[avalon_drv.drv_id].limit = lim; - found = true; - } -#endif + /* Use the DRIVER_PARSE_COMMANDS macro to iterate + * over all the drivers. */ + DRIVER_PARSE_COMMANDS(DRIVER_COUNT_FOUND) if (!found) quit(1, "Invalid --usb DRV:limit - unknown DRV='%s'", ptr); @@ -3296,10 +3987,8 @@ void usb_initialise() #ifndef WIN32 #include #include -#include -#include -#include #include +#include #include #ifndef __APPLE__ @@ -3385,7 +4074,6 @@ static LPSECURITY_ATTRIBUTES mksec(const char *dname, uint8_t bus_number, uint8_ static bool resource_lock(const char *dname, uint8_t bus_number, uint8_t device_address) { applog(LOG_DEBUG, "USB res lock %s %d-%d", dname, (int)bus_number, (int)device_address); - #ifdef WIN32 struct cgpu_info *cgpu; LPSECURITY_ATTRIBUTES sec; @@ -3454,7 +4142,7 @@ static bool resource_lock(const char *dname, uint8_t bus_number, uint8_t device_ goto fail; } - add_in_use(bus_number, device_address); + add_in_use(bus_number, device_address, false); in_use_store_ress(bus_number, device_address, (void *)usbMutex, (void *)sec); return true; @@ -3463,12 +4151,8 @@ static bool resource_lock(const char *dname, uint8_t bus_number, uint8_t device_ sec = unsec(sec); return false; #else - struct semid_ds seminfo; - union semun opt; char name[64]; - key_t *key; - int *sem; - int fd, count; + int fd; if (is_in_use_bd(bus_number, device_address)) return false; @@ -3476,89 +4160,20 @@ static bool resource_lock(const char *dname, uint8_t bus_number, uint8_t device_ snprintf(name, sizeof(name), "/tmp/cgminer-usb-%d-%d", (int)bus_number, (int)device_address); fd = open(name, O_CREAT|O_RDONLY, S_IRUSR|S_IWUSR|S_IRGRP|S_IROTH); if (fd == -1) { - applog(LOG_ERR, - "SEM: %s USB open failed '%s' err (%d) %s", - dname, name, errno, strerror(errno)); - goto _out; - } - close(fd); - - key = malloc(sizeof(*key)); - if (unlikely(!key)) - quit(1, "SEM: Failed to malloc key"); - - sem = malloc(sizeof(*sem)); - if (unlikely(!sem)) - quit(1, "SEM: Failed to malloc sem"); - - *key = ftok(name, 'K'); - *sem = semget(*key, 1, IPC_CREAT | IPC_EXCL | 438); - if (*sem < 0) { - if (errno != EEXIST) { - applog(LOG_ERR, - "SEM: %s USB failed to get '%s' err (%d) %s", - dname, name, errno, strerror(errno)); - goto free_out; - } - - *sem = semget(*key, 1, 0); - if (*sem < 0) { - applog(LOG_ERR, - "SEM: %s USB failed to access '%s' err (%d) %s", - dname, name, errno, strerror(errno)); - goto free_out; - } - - opt.buf = &seminfo; - count = 0; - while (++count) { - // Should NEVER take 100ms - if (count > 99) { - applog(LOG_ERR, - "SEM: %s USB timeout waiting for (%d) '%s'", - dname, *sem, name); - goto free_out; - } - if (semctl(*sem, 0, IPC_STAT, opt) == -1) { - applog(LOG_ERR, - "SEM: %s USB failed to wait for (%d) '%s' count %d err (%d) %s", - dname, *sem, name, count, errno, strerror(errno)); - goto free_out; - } - if (opt.buf->sem_otime != 0) - break; - cgsleep_ms(1); - } + applog(LOG_ERR, "%s USB open failed '%s' err (%d) %s", + dname, name, errno, strerror(errno)); + return false; } - - struct sembuf sops[] = { - { 0, 0, IPC_NOWAIT | SEM_UNDO }, - { 0, 1, IPC_NOWAIT | SEM_UNDO } - }; - - if (semop(*sem, sops, 2)) { - if (errno == EAGAIN) { - if (!hotplug_mode) - applog(LOG_WARNING, - "SEM: %s USB failed to get (%d) '%s' - device in use", - dname, *sem, name); - } else { - applog(LOG_DEBUG, - "SEM: %s USB failed to get (%d) '%s' err (%d) %s", - dname, *sem, name, errno, strerror(errno)); - } - goto free_out; + if (flock(fd, LOCK_EX | LOCK_NB)) { + applog(LOG_INFO, "%s USB failed to get '%s' - device in use", + dname, name); + close(fd); + return false; } - add_in_use(bus_number, device_address); - in_use_store_ress(bus_number, device_address, (void *)key, (void *)sem); + add_in_use(bus_number, device_address, false); + in_use_store_fd(bus_number, device_address, fd); return true; - -free_out: - free(sem); - free(key); -_out: - return false; #endif } @@ -3595,41 +4210,16 @@ static void resource_unlock(const char *dname, uint8_t bus_number, uint8_t devic return; #else char name[64]; - key_t *key = NULL; - int *sem = NULL; + int fd; snprintf(name, sizeof(name), "/tmp/cgminer-usb-%d-%d", (int)bus_number, (int)device_address); - in_use_get_ress(bus_number, device_address, (void **)(&key), (void **)(&sem)); - - if (!key || !sem) - goto fila; - - struct sembuf sops[] = { - { 0, -1, SEM_UNDO } - }; - - // Allow a 10ms timeout - // exceeding this timeout means it would probably never succeed anyway - struct timespec timeout = { 0, 10000000 }; - - if (semtimedop(*sem, sops, 1, &timeout)) { - applog(LOG_ERR, - "SEM: %s USB failed to release '%s' err (%d) %s", - dname, name, errno, strerror(errno)); - } - - if (semctl(*sem, 0, IPC_RMID)) { - applog(LOG_WARNING, - "SEM: %s USB failed to remove SEM '%s' err (%d) %s", - dname, name, errno, strerror(errno)); - } - -fila: - - free(sem); - free(key); + fd = in_use_get_fd(bus_number, device_address); + if (fd < 0) + return; remove_in_use(bus_number, device_address); + close(fd); + unlink(name); return; #endif } @@ -3682,7 +4272,7 @@ void *usb_resource_thread(void __maybe_unused *userdata) { pthread_setcanceltype(PTHREAD_CANCEL_ASYNCHRONOUS, NULL); - RenameThread("usbresource"); + RenameThread("USBResource"); applog(LOG_DEBUG, "RES: thread starting"); @@ -3698,3 +4288,169 @@ void *usb_resource_thread(void __maybe_unused *userdata) return NULL; } + +void initialise_usblocks(void) +{ + mutex_init(&cgusb_lock); + mutex_init(&cgusbres_lock); + cglock_init(&cgusb_fd_lock); +} + +#ifdef USE_BITMAIN + +struct cgpu_info *btm_alloc_cgpu(struct device_drv *drv, int threads) +{ + struct cgpu_info *cgpu = calloc(1, sizeof(*cgpu)); + + if (unlikely(!cgpu)) + quit(1, "Failed to calloc cgpu for %s in usb_alloc_cgpu", drv->dname); + + cgpu->drv = drv; + cgpu->deven = DEV_ENABLED; + cgpu->threads = threads; + + cgpu->usbinfo.nodev = true; + cgpu->device_fd = -1; + + cglock_init(&cgpu->usbinfo.devlock); + + return cgpu; +} + +struct cgpu_info *btm_free_cgpu(struct cgpu_info *cgpu) +{ + if (cgpu->drv->copy) + free(cgpu->drv); + + if(cgpu->device_path) { + free(cgpu->device_path); + } + + free(cgpu); + + return NULL; +} + +bool btm_init(struct cgpu_info *cgpu, const char * devpath) +{ +#ifdef WIN32 + int fd = -1; + signed short timeout = 1; + unsigned long baud = 115200; + bool purge = true; + HANDLE hSerial = NULL; + applog(LOG_DEBUG, "btm_init cgpu->device_fd=%d", cgpu->device_fd); + if(cgpu->device_fd >= 0) { + return false; + } + hSerial = CreateFile(devpath, GENERIC_READ | GENERIC_WRITE, 0, NULL, OPEN_EXISTING, 0, NULL); + if (unlikely(hSerial == INVALID_HANDLE_VALUE)) + { + DWORD e = GetLastError(); + switch (e) { + case ERROR_ACCESS_DENIED: + applog(LOG_DEBUG, "Do not have user privileges required to open %s", devpath); + break; + case ERROR_SHARING_VIOLATION: + applog(LOG_DEBUG, "%s is already in use by another process", devpath); + break; + default: + applog(LOG_DEBUG, "Open %s failed, GetLastError:%d", devpath, (int)e); + break; + } + } else { + // thanks to af_newbie for pointers about this + COMMCONFIG comCfg = {0}; + comCfg.dwSize = sizeof(COMMCONFIG); + comCfg.wVersion = 1; + comCfg.dcb.DCBlength = sizeof(DCB); + comCfg.dcb.BaudRate = baud; + comCfg.dcb.fBinary = 1; + comCfg.dcb.fDtrControl = DTR_CONTROL_ENABLE; + comCfg.dcb.fRtsControl = RTS_CONTROL_ENABLE; + comCfg.dcb.ByteSize = 8; + + SetCommConfig(hSerial, &comCfg, sizeof(comCfg)); + + // Code must specify a valid timeout value (0 means don't timeout) + const DWORD ctoms = (timeout * 100); + COMMTIMEOUTS cto = {ctoms, 0, ctoms, 0, ctoms}; + SetCommTimeouts(hSerial, &cto); + + if (purge) { + PurgeComm(hSerial, PURGE_RXABORT); + PurgeComm(hSerial, PURGE_TXABORT); + PurgeComm(hSerial, PURGE_RXCLEAR); + PurgeComm(hSerial, PURGE_TXCLEAR); + } + fd = _open_osfhandle((intptr_t)hSerial, 0); + } +#else + int fd = -1; + if(cgpu->device_fd >= 0) { + return false; + } + fd = open(devpath, O_RDWR|O_EXCL|O_NONBLOCK); +#endif + if(fd == -1) { + applog(LOG_DEBUG, "%s open %s error %d", + cgpu->drv->dname, devpath, errno); + return false; + } + cgpu->device_path = strdup(devpath); + cgpu->device_fd = fd; + cgpu->usbinfo.nodev = false; + applog(LOG_DEBUG, "btm_init open device fd = %d", cgpu->device_fd); + return true; +} + +void btm_uninit(struct cgpu_info *cgpu) +{ + applog(LOG_DEBUG, "BTM uninit %s%i", cgpu->drv->name, cgpu->device_fd); + + // May have happened already during a failed initialisation + // if release_cgpu() was called due to a USB NODEV(err) + close(cgpu->device_fd); + if(cgpu->device_path) { + free(cgpu->device_path); + cgpu->device_path = NULL; + } +} + +void btm_detect(struct device_drv *drv, bool (*device_detect)(const char*)) +{ + ssize_t count, i; + + applog(LOG_DEBUG, "BTM scan devices: checking for %s devices", drv->name); + + if (total_count >= total_limit) { + applog(LOG_DEBUG, "BTM scan devices: total limit %d reached", total_limit); + return; + } + + if (drv_count[drv->drv_id].count >= drv_count[drv->drv_id].limit) { + applog(LOG_DEBUG, + "BTM scan devices: %s limit %d reached", + drv->dname, drv_count[drv->drv_id].limit); + return; + } + device_detect("asic"); +} + +int btm_read(struct cgpu_info *cgpu, char *buf, size_t bufsize) +{ + int err = 0; + //applog(LOG_DEBUG, "btm_read ----- %d -----", bufsize); + err = read(cgpu->device_fd, buf, bufsize); + return err; +} + +int btm_write(struct cgpu_info *cgpu, char *buf, size_t bufsize) +{ + int err = 0; + //applog(LOG_DEBUG, "btm_write ----- %d -----", bufsize); + err = write(cgpu->device_fd, buf, bufsize); + return err; +} + +#endif diff --git a/usbutils.h b/usbutils.h index 4894a9da43..b1bd64f1de 100644 --- a/usbutils.h +++ b/usbutils.h @@ -1,6 +1,6 @@ /* * Copyright 2012-2013 Andrew Smith - * Copyright 2013 Con Kolivas + * Copyright 2013-2014 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 @@ -50,6 +50,12 @@ #define FTDI_VALUE_DATA_AVA 8 +// Bitmain +#define FTDI_VALUE_BAUD_BTM 0x001A +#define FTDI_INDEX_BAUD_BTM 0x0000 + +#define FTDI_VALUE_DATA_BTM 8 + // BitBurner #define BITBURNER_REQUEST ((uint8_t)0x42) #define BITBURNER_VALUE 0x4242 @@ -85,6 +91,12 @@ #define CP210X_VALUE_DATA 0x0303 #define CP210X_DATA_BAUD 0x0001c200 +#define CP210X_SET_LINE_CTL 0x03 +#define CP210X_BITS_DATA_MASK 0x0f00 +#define CP210X_BITS_DATA_8 0x0800 +#define CP210X_BITS_PARITY_MARK 0x0030 +#define CP210X_BITS_PARITY_SPACE 0x0040 + // For 0x067b:0x2303 Prolific PL2303 - ICA #define PL2303_CTRL_DTR 0x01 @@ -109,35 +121,65 @@ // Use the device defined timeout #define DEVTIMEOUT 0 -// For endpoints defined in usb_find_devices.eps, -// the first two must be the default IN and OUT +// The default intinfo structure used is the first one +#define DEFAULT_INTINFO 0 + +// For endpoints defined in usb_find_devices.intinfos.epinfos, +// the first two must be the default IN and OUT and both must always exist #define DEFAULT_EP_IN 0 #define DEFAULT_EP_OUT 1 -struct usb_endpoints { +struct usb_epinfo { uint8_t att; uint16_t size; unsigned char ep; + uint16_t wMaxPacketSize; bool found; }; +struct usb_intinfo { + int interface; + int ctrl_transfer; + int epinfo_count; + struct usb_epinfo *epinfos; +}; + enum sub_ident { IDENT_UNK = 0, + IDENT_AMU, + IDENT_ANU, + IDENT_BMM, + IDENT_BMS, + IDENT_AU3, + IDENT_AVA, + IDENT_AV2, + IDENT_AV4, IDENT_BAJ, IDENT_BAL, - IDENT_BAS, IDENT_BAM, + IDENT_BAS, + IDENT_BBF, + IDENT_BET, + IDENT_BF1, IDENT_BFL, - IDENT_MMQ, - IDENT_AVA, - IDENT_BTB, - IDENT_ICA, - IDENT_AMU, IDENT_BLT, - IDENT_LLT, + IDENT_BMA, + IDENT_BTB, + IDENT_BXF, + IDENT_BXM, IDENT_CMR1, IDENT_CMR2, - IDENT_ZTX + IDENT_CTA, + IDENT_DRB, + IDENT_HFA, + IDENT_HRO, + IDENT_ICA, + IDENT_KLN, + IDENT_LIN, + IDENT_LLT, + IDENT_MMQ, + IDENT_NFU, + IDENT_OSM }; struct usb_find_devices { @@ -148,14 +190,11 @@ struct usb_find_devices { uint16_t idProduct; char *iManufacturer; char *iProduct; - int kernel; int config; - int interface; unsigned int timeout; - uint16_t wMaxPacketSize; uint16_t latency; - int epcount; - struct usb_endpoints *eps; + int intinfo_count; + struct usb_intinfo *intinfos; }; /* Latency is set to 32ms to prevent a transfer ever being more than 512 bytes @@ -169,6 +208,14 @@ enum usb_types { USB_TYPE_FTDI }; +#define USB_MAX_READ 8192 +/* + * We add 4: 1 for null, 2 for FTDI status and 1 to round to 4 bytes + * If a single device ever has multiple end points then it will need + * multiple of these + */ +#define USB_READ_BUFSIZE (USB_MAX_READ + 4) + struct cg_usb_device { struct usb_find_devices *found; libusb_device_handle *handle; @@ -177,25 +224,20 @@ struct cg_usb_device { enum usb_types usb_type; enum sub_ident ident; uint16_t usbver; - int cps; - bool usecps; char *prod_string; char *manuf_string; char *serial_string; unsigned char fwVersion; // ?? unsigned char interfaceVersion; // ?? - char *buffer; + char buffer[USB_MAX_READ]; uint32_t bufsiz; uint32_t bufamt; - uint16_t PrefPacketSize; - cgtimer_t cgt_last_write; - size_t last_write_siz; + bool usb11; // USB 1.1 flag for convenience + bool tt; // Enable the transaction translator }; #define USB_NOSTAT 0 -#define USB_MAX_READ 8192 - #define USB_TMO_0 50 #define USB_TMO_1 100 #define USB_TMO_2 500 @@ -214,6 +256,7 @@ struct cg_usb_info { uint8_t device_address; int usbstat; bool nodev; + bool initialised; int nodev_count; struct timeval last_nodev; uint32_t ioerr_count; @@ -228,7 +271,7 @@ struct cg_usb_info { * that uses the lock - however, all usbutils code MUST use it * to avoid devices disappearing while in use by multiple threads */ - pthread_rwlock_t *devlock; + cglock_t devlock; time_t last_pipe; uint64_t pipe_count; @@ -241,161 +284,319 @@ struct cg_usb_info { uint64_t write_delay_count; double total_write_delay; - /* - * We add 4: 1 for null, 2 for FTDI status and 1 to round to 4 bytes - * If a single device ever has multiple end points then it will need - * multiple of these - */ - unsigned char bulkbuf[USB_MAX_READ+4]; - uint64_t tmo_count; struct cg_usb_tmo usb_tmo[USB_TMOS]; }; +#define ENUMERATION(a,b) a, +#define JUMPTABLE(a,b) b, + +#define USB_PARSE_COMMANDS(USB_ADD_COMMAND) \ + USB_ADD_COMMAND(C_REJECTED, "RejectedNoDevice") \ + USB_ADD_COMMAND(C_PING, "Ping") \ + USB_ADD_COMMAND(C_CLEAR, "Clear") \ + USB_ADD_COMMAND(C_REQUESTVERSION, "RequestVersion") \ + USB_ADD_COMMAND(C_GETVERSION, "GetVersion") \ + USB_ADD_COMMAND(C_REQUESTFPGACOUNT, "RequestFPGACount") \ + USB_ADD_COMMAND(C_GETFPGACOUNT, "GetFPGACount") \ + USB_ADD_COMMAND(C_STARTPROGRAM, "StartProgram") \ + USB_ADD_COMMAND(C_STARTPROGRAMSTATUS, "StartProgramStatus") \ + USB_ADD_COMMAND(C_PROGRAM, "Program") \ + USB_ADD_COMMAND(C_PROGRAMSTATUS, "ProgramStatus") \ + USB_ADD_COMMAND(C_PROGRAMSTATUS2, "ProgramStatus2") \ + USB_ADD_COMMAND(C_FINALPROGRAMSTATUS, "FinalProgramStatus") \ + USB_ADD_COMMAND(C_SETCLOCK, "SetClock") \ + USB_ADD_COMMAND(C_SETPARITY, "SetParity") \ + USB_ADD_COMMAND(C_REPLYSETCLOCK, "ReplySetClock") \ + USB_ADD_COMMAND(C_SETVOLT, "SetVolt") \ + USB_ADD_COMMAND(C_REPLYSETVOLT, "ReplySetVolt") \ + USB_ADD_COMMAND(C_REQUESTUSERCODE, "RequestUserCode") \ + USB_ADD_COMMAND(C_GETUSERCODE, "GetUserCode") \ + USB_ADD_COMMAND(C_REQUESTTEMPERATURE, "RequestTemperature") \ + USB_ADD_COMMAND(C_GETTEMPERATURE, "GetTemperature") \ + USB_ADD_COMMAND(C_SENDWORK, "SendWork") \ + USB_ADD_COMMAND(C_SENDWORKSTATUS, "SendWorkStatus") \ + USB_ADD_COMMAND(C_REQUESTWORKSTATUS, "RequestWorkStatus") \ + USB_ADD_COMMAND(C_GETWORKSTATUS, "GetWorkStatus") \ + USB_ADD_COMMAND(C_REQUESTIDENTIFY, "RequestIdentify") \ + USB_ADD_COMMAND(C_GETIDENTIFY, "GetIdentify") \ + USB_ADD_COMMAND(C_REQUESTFLASH, "RequestFlash") \ + USB_ADD_COMMAND(C_REQUESTSENDWORK, "RequestSendWork") \ + USB_ADD_COMMAND(C_REQUESTSENDWORKSTATUS, "RequestSendWorkStatus") \ + USB_ADD_COMMAND(C_RESET, "Reset") \ + USB_ADD_COMMAND(C_SETBAUD, "SetBaud") \ + USB_ADD_COMMAND(C_SETDATA, "SetDataCtrl") \ + USB_ADD_COMMAND(C_SETFLOW, "SetFlowCtrl") \ + USB_ADD_COMMAND(C_SETMODEM, "SetModemCtrl") \ + USB_ADD_COMMAND(C_PURGERX, "PurgeRx") \ + USB_ADD_COMMAND(C_PURGETX, "PurgeTx") \ + USB_ADD_COMMAND(C_FLASHREPLY, "FlashReply") \ + USB_ADD_COMMAND(C_REQUESTDETAILS, "RequestDetails") \ + USB_ADD_COMMAND(C_GETDETAILS, "GetDetails") \ + USB_ADD_COMMAND(C_REQUESTRESULTS, "RequestResults") \ + USB_ADD_COMMAND(C_GETRESULTS, "GetResults") \ + USB_ADD_COMMAND(C_REQUESTQUEJOB, "RequestQueJob") \ + USB_ADD_COMMAND(C_REQUESTQUEJOBSTATUS, "RequestQueJobStatus") \ + USB_ADD_COMMAND(C_QUEJOB, "QueJob") \ + USB_ADD_COMMAND(C_QUEJOBSTATUS, "QueJobStatus") \ + USB_ADD_COMMAND(C_QUEFLUSH, "QueFlush") \ + USB_ADD_COMMAND(C_QUEFLUSHREPLY, "QueFlushReply") \ + USB_ADD_COMMAND(C_REQUESTVOLTS, "RequestVolts") \ + USB_ADD_COMMAND(C_GETVOLTS, "GetVolts") \ + USB_ADD_COMMAND(C_SENDTESTWORK, "SendTestWork") \ + USB_ADD_COMMAND(C_LATENCY, "SetLatency") \ + USB_ADD_COMMAND(C_SETLINE, "SetLine") \ + USB_ADD_COMMAND(C_VENDOR, "Vendor") \ + USB_ADD_COMMAND(C_SETFAN, "SetFan") \ + USB_ADD_COMMAND(C_FANREPLY, "GetFan") \ + USB_ADD_COMMAND(C_AVALON_TASK, "AvalonTask") \ + USB_ADD_COMMAND(C_AVALON_READ, "AvalonRead") \ + USB_ADD_COMMAND(C_GET_AVALON_READY, "AvalonReady") \ + USB_ADD_COMMAND(C_AVALON_RESET, "AvalonReset") \ + USB_ADD_COMMAND(C_GET_AVALON_RESET, "GetAvalonReset") \ + USB_ADD_COMMAND(C_FTDI_STATUS, "FTDIStatus") \ + USB_ADD_COMMAND(C_ENABLE_UART, "EnableUART") \ + USB_ADD_COMMAND(C_ANU_SEND_CMD, "ANUSendcmd") \ + USB_ADD_COMMAND(C_ANU_SEND_RDREG, "ANUSendrdreg") \ + USB_ADD_COMMAND(C_ANU_SEND_VOLT, "ANUSendvolt") \ + USB_ADD_COMMAND(C_BB_SET_VOLTAGE, "SetCoreVoltage") \ + USB_ADD_COMMAND(C_BB_GET_VOLTAGE, "GetCoreVoltage") \ + USB_ADD_COMMAND(C_BF_RESET, "BFReset") \ + USB_ADD_COMMAND(C_BF_OPEN, "BFOpen") \ + USB_ADD_COMMAND(C_BF_INIT, "BFInit") \ + USB_ADD_COMMAND(C_BF_CLOSE, "BFClose") \ + USB_ADD_COMMAND(C_BF_REQINFO, "BFRequestInfo") \ + USB_ADD_COMMAND(C_BF_GETINFO, "BFGetInfo") \ + USB_ADD_COMMAND(C_BF_REQRESET, "BFRequestReset") \ + USB_ADD_COMMAND(C_BF_GETRESET, "BFGetReset") \ + USB_ADD_COMMAND(C_BF_REQWORK, "BFRequestWork") \ + USB_ADD_COMMAND(C_BF_GETWORK, "BFGetWork") \ + USB_ADD_COMMAND(C_BF_GETRES, "BFGetResults") \ + USB_ADD_COMMAND(C_BF_FLUSH, "BFFlush") \ + USB_ADD_COMMAND(C_BF_IFLUSH, "BFInterruptFlush") \ + USB_ADD_COMMAND(C_BF_IDENTIFY, "BFIdentify") \ + USB_ADD_COMMAND(C_BF_DETECTCHIPS, "BFDetectChips") \ + USB_ADD_COMMAND(C_BF_CONFIG, "BFConfig") \ + USB_ADD_COMMAND(C_BF_GETTEMP, "BFGetTemp") \ + USB_ADD_COMMAND(C_BF_AUTOTUNE, "BFAutoTune") \ + USB_ADD_COMMAND(C_ATMEL_RESET, "AtmelReset") \ + USB_ADD_COMMAND(C_ATMEL_OPEN, "AtmelOpen") \ + USB_ADD_COMMAND(C_ATMEL_INIT, "AtmelInit") \ + USB_ADD_COMMAND(C_ATMEL_CLOSE, "AtmelClose") \ + USB_ADD_COMMAND(C_AVA2_READ, "Ava2Read") \ + USB_ADD_COMMAND(C_AVA2_WRITE, "Ava2Write") \ + USB_ADD_COMMAND(C_AVA4_READ, "Ava4Read") \ + USB_ADD_COMMAND(C_AVA4_WRITE, "Ava4Write") \ + USB_ADD_COMMAND(C_BET_WRITE, "BlockErupterWrite") \ + USB_ADD_COMMAND(C_BET_READ, "BlockErupterRead") \ + USB_ADD_COMMAND(C_BF1_REQINFO, "BF1RequestInfo") \ + USB_ADD_COMMAND(C_BF1_GETINFO, "BF1GetInfo") \ + USB_ADD_COMMAND(C_BF1_REQRESET, "BF1RequestReset") \ + USB_ADD_COMMAND(C_BF1_GETRESET, "BF1GetReset") \ + USB_ADD_COMMAND(C_BF1_REQWORK, "BF1RequestWork") \ + USB_ADD_COMMAND(C_BF1_GETWORK, "BF1GetWork") \ + USB_ADD_COMMAND(C_BF1_GETRES, "BF1GetResults") \ + USB_ADD_COMMAND(C_BF1_FLUSH, "BF1Flush") \ + USB_ADD_COMMAND(C_BF1_IFLUSH, "BF1InterruptFlush") \ + USB_ADD_COMMAND(C_BF1_IDENTIFY, "BF1Identify") \ + USB_ADD_COMMAND(C_BXF_READ, "BXFRead") \ + USB_ADD_COMMAND(C_BXF_WORK, "BXFWork") \ + USB_ADD_COMMAND(C_BXF_TARGET, "BXFTarget") \ + USB_ADD_COMMAND(C_BXF_VERSION, "BXFVersion") \ + USB_ADD_COMMAND(C_BXF_MAXROLL, "BXFMaxRoll") \ + USB_ADD_COMMAND(C_BXF_FLUSH, "BXFFlush") \ + USB_ADD_COMMAND(C_BXF_CLOCK, "BXFClock") \ + USB_ADD_COMMAND(C_BXF_LEDMODE, "BXFLedMode") \ + USB_ADD_COMMAND(C_BXF_DEBUGMODE, "BXFDebugMode") \ + USB_ADD_COMMAND(C_BXM_FLUSH, "BXMFlush") \ + USB_ADD_COMMAND(C_BXM_SRESET, "BXMSReset") \ + USB_ADD_COMMAND(C_BXM_SETLATENCY, "BXMSetLatency") \ + USB_ADD_COMMAND(C_BXM_SECR, "BXMSetEventCharRequest") \ + USB_ADD_COMMAND(C_BXM_SETBITMODE, "BXMSetBitmodeRequest") \ + USB_ADD_COMMAND(C_BXM_CLOCK, "BXMClock") \ + USB_ADD_COMMAND(C_BXM_CLOCKDIV, "BXMClockDiv") \ + USB_ADD_COMMAND(C_BXM_LOOP, "BXMLoop") \ + USB_ADD_COMMAND(C_BXM_ADBUS, "BXMADBus") \ + USB_ADD_COMMAND(C_BXM_ACBUS, "BXMACBus") \ + USB_ADD_COMMAND(C_BXM_PURGERX, "BXMPurgeRX") \ + USB_ADD_COMMAND(C_BXM_PURGETX, "BXMPurgeTX") \ + USB_ADD_COMMAND(C_BXM_CSLOW, "BXMCSLow") \ + USB_ADD_COMMAND(C_BXM_CSHIGH, "BXMCSHigh") \ + USB_ADD_COMMAND(C_BXM_RESET, "BXMReset") \ + USB_ADD_COMMAND(C_BXM_SPITX, "BXMSPITX") \ + USB_ADD_COMMAND(C_BXM_SPIRX, "BXMSPIRX") \ + USB_ADD_COMMAND(C_HF_RESET, "HFReset") \ + USB_ADD_COMMAND(C_HF_PLL_CONFIG, "HFPLLConfig") \ + USB_ADD_COMMAND(C_HF_ADDRESS, "HFAddress") \ + USB_ADD_COMMAND(C_HF_BAUD, "HFBaud") \ + USB_ADD_COMMAND(C_HF_HASH, "HFHash") \ + USB_ADD_COMMAND(C_HF_NONCE, "HFNonce") \ + USB_ADD_COMMAND(C_HF_ABORT, "HFAbort") \ + USB_ADD_COMMAND(C_HF_STATUS, "HFStatus") \ + USB_ADD_COMMAND(C_HF_CONFIG, "HFConfig") \ + USB_ADD_COMMAND(C_HF_STATISTICS, "HFStatistics") \ + USB_ADD_COMMAND(C_HF_CLOCKGATE, "HFClockGate") \ + USB_ADD_COMMAND(C_HF_USB_INIT, "HFUSBInit") \ + USB_ADD_COMMAND(C_HF_DFU, "HFDFU") \ + USB_ADD_COMMAND(C_HF_DIE_STATUS, "HFDieStatus") \ + USB_ADD_COMMAND(C_HF_GWQ_STATUS, "HFGWQStatus") \ + USB_ADD_COMMAND(C_HF_WORK_RESTART, "HFWorkRestart") \ + USB_ADD_COMMAND(C_HF_GWQSTATS, "HFGWQStats") \ + USB_ADD_COMMAND(C_HF_NOTICE, "HFNotice") \ + USB_ADD_COMMAND(C_HF_PING, "HFPing") \ + USB_ADD_COMMAND(C_HF_FAN, "HFFan") \ + USB_ADD_COMMAND(C_HRO_WRITE, "HROWrite") \ + USB_ADD_COMMAND(C_HRO_READ, "HRORead") \ + USB_ADD_COMMAND(C_OP_NAME, "HFName") \ + USB_ADD_COMMAND(C_HF_GETHEADER, "HFGetHeader") \ + USB_ADD_COMMAND(C_HF_GETDATA, "HFGetData") \ + USB_ADD_COMMAND(C_HF_CLEAR_READ, "HFClearRead") \ + USB_ADD_COMMAND(C_CTA_READ, "CTARead") \ + USB_ADD_COMMAND(C_CTA_WRITE, "CTAWrite") \ + USB_ADD_COMMAND(C_MCP_GETGPIOSETTING, "MCPGetGPIOSetting") \ + USB_ADD_COMMAND(C_MCP_SETGPIOSETTING, "MCPSetGPIOSetting") \ + USB_ADD_COMMAND(C_MCP_GETGPIOPINVAL, "MCPGetGPIOPinVal") \ + USB_ADD_COMMAND(C_MCP_SETGPIOPINVAL, "MCPSetGPIOPinVal") \ + USB_ADD_COMMAND(C_MCP_GETGPIOPINDIR, "MCPGetGPIOPinDir") \ + USB_ADD_COMMAND(C_MCP_SETGPIOPINDIR, "MCPSetGPIOPinDir") \ + USB_ADD_COMMAND(C_MCP_SETSPISETTING, "MCPSetSPISetting") \ + USB_ADD_COMMAND(C_MCP_GETSPISETTING, "MCPGetSPISetting") \ + USB_ADD_COMMAND(C_MCP_SPITRANSFER, "MCPSPITransfer") \ + USB_ADD_COMMAND(C_MCP_SPICANCEL, "MCPSPICancel") \ + USB_ADD_COMMAND(C_BITMAIN_SEND, "BitmainSend") \ + USB_ADD_COMMAND(C_BITMAIN_READ, "BitmainRead") \ + USB_ADD_COMMAND(C_BITMAIN_TOKEN_TXCONFIG, "BitmainTokenTxConfig") \ + USB_ADD_COMMAND(C_BITMAIN_TOKEN_TXTASK, "BitmainTokenTxTask") \ + USB_ADD_COMMAND(C_BITMAIN_TOKEN_RXSTATUS, "BitmainTokenRxStatus") \ + USB_ADD_COMMAND(C_BITMAIN_DATA_RXSTATUS, "BitmainDataRxStatus") \ + USB_ADD_COMMAND(C_BITMAIN_DATA_RXNONCE, "BitmainDataRxNonce") + +/* Create usb_cmds enum from USB_PARSE_COMMANDS macro */ enum usb_cmds { - C_REJECTED = 0, - C_PING, - C_CLEAR, - C_REQUESTVERSION, - C_GETVERSION, - C_REQUESTFPGACOUNT, - C_GETFPGACOUNT, - C_STARTPROGRAM, - C_STARTPROGRAMSTATUS, - C_PROGRAM, - C_PROGRAMSTATUS, - C_PROGRAMSTATUS2, - C_FINALPROGRAMSTATUS, - C_SETCLOCK, - C_REPLYSETCLOCK, - C_REQUESTUSERCODE, - C_GETUSERCODE, - C_REQUESTTEMPERATURE, - C_GETTEMPERATURE, - C_SENDWORK, - C_SENDWORKSTATUS, - C_REQUESTWORKSTATUS, - C_GETWORKSTATUS, - C_REQUESTIDENTIFY, - C_GETIDENTIFY, - C_REQUESTFLASH, - C_REQUESTSENDWORK, - C_REQUESTSENDWORKSTATUS, - C_RESET, - C_SETBAUD, - C_SETDATA, - C_SETFLOW, - C_SETMODEM, - C_PURGERX, - C_PURGETX, - C_FLASHREPLY, - C_REQUESTDETAILS, - C_GETDETAILS, - C_REQUESTRESULTS, - C_GETRESULTS, - C_REQUESTQUEJOB, - C_REQUESTQUEJOBSTATUS, - C_QUEJOB, - C_QUEJOBSTATUS, - C_QUEFLUSH, - C_QUEFLUSHREPLY, - C_REQUESTVOLTS, - C_GETVOLTS, - C_SENDTESTWORK, - C_LATENCY, - C_SETLINE, - C_VENDOR, - C_SETFAN, - C_FANREPLY, - C_AVALON_TASK, - C_AVALON_READ, - C_GET_AVALON_READY, - C_AVALON_RESET, - C_GET_AVALON_RESET, - C_FTDI_STATUS, - C_ENABLE_UART, - C_BB_SET_VOLTAGE, - C_BB_GET_VOLTAGE, + USB_PARSE_COMMANDS(ENUMERATION) C_MAX }; struct device_drv; struct cgpu_info; +#ifdef USE_BITMAIN +struct cgpu_info *btm_alloc_cgpu(struct device_drv *drv, int threads); +struct cgpu_info *btm_free_cgpu(struct cgpu_info *cgpu); +void btm_uninit(struct cgpu_info *cgpu); +bool btm_init(struct cgpu_info *cgpu, const char * devpath); +void btm_detect(struct device_drv *drv, bool (*device_detect)(const char*)); +int btm_read(struct cgpu_info *cgpu, char *buf, size_t bufsize); +int btm_write(struct cgpu_info *cgpu, char *buf, size_t bufsize); +#endif + +bool async_usb_transfers(void); +void cancel_usb_transfers(void); void usb_all(int level); +void usb_list(void); const char *usb_cmdname(enum usb_cmds cmd); -void usb_applog(struct cgpu_info *bflsc, enum usb_cmds cmd, char *msg, int amount, int err); +void usb_applog(struct cgpu_info *cgpu, enum usb_cmds cmd, char *msg, int amount, int err); +void blacklist_cgpu(struct cgpu_info *cgpu); +void whitelist_cgpu(struct cgpu_info *cgpu); +void usb_nodev(struct cgpu_info *cgpu); struct cgpu_info *usb_copy_cgpu(struct cgpu_info *orig); struct cgpu_info *usb_alloc_cgpu(struct device_drv *drv, int threads); -struct cgpu_info *usb_free_cgpu_devlock(struct cgpu_info *cgpu, bool free_devlock); -#define usb_free_cgpu(cgpu) usb_free_cgpu_devlock(cgpu, true) +struct cgpu_info *usb_free_cgpu(struct cgpu_info *cgpu); void usb_uninit(struct cgpu_info *cgpu); bool usb_init(struct cgpu_info *cgpu, struct libusb_device *dev, struct usb_find_devices *found); -void usb_detect(struct device_drv *drv, bool (*device_detect)(struct libusb_device *, struct usb_find_devices *)); +void __usb_detect(struct device_drv *drv, struct cgpu_info *(*device_detect)(struct libusb_device *, struct usb_find_devices *), + bool single); +#define usb_detect(drv, cgpu) __usb_detect(drv, cgpu, false) +#define usb_detect_one(drv, cgpu) __usb_detect(drv, cgpu, true) struct api_data *api_usb_stats(int *count); void update_usb_stats(struct cgpu_info *cgpu); -int _usb_read(struct cgpu_info *cgpu, int ep, char *buf, size_t bufsiz, int *processed, unsigned int timeout, const char *end, enum usb_cmds cmd, bool readonce); -int _usb_write(struct cgpu_info *cgpu, int ep, char *buf, size_t bufsiz, int *processed, unsigned int timeout, enum usb_cmds); +void usb_reset(struct cgpu_info *cgpu); +int _usb_read(struct cgpu_info *cgpu, int intinfo, int epinfo, char *buf, size_t bufsiz, int *processed, int timeout, const char *end, enum usb_cmds cmd, bool readonce, bool cancellable); +int _usb_write(struct cgpu_info *cgpu, int intinfo, int epinfo, char *buf, size_t bufsiz, int *processed, int timeout, enum usb_cmds); int _usb_transfer(struct cgpu_info *cgpu, uint8_t request_type, uint8_t bRequest, uint16_t wValue, uint16_t wIndex, uint32_t *data, int siz, unsigned int timeout, enum usb_cmds cmd); int _usb_transfer_read(struct cgpu_info *cgpu, uint8_t request_type, uint8_t bRequest, uint16_t wValue, uint16_t wIndex, char *buf, int bufsiz, int *amount, unsigned int timeout, enum usb_cmds cmd); int usb_ftdi_cts(struct cgpu_info *cgpu); -int usb_ftdi_set_latency(struct cgpu_info *cgpu); -void usb_buffer_enable(struct cgpu_info *cgpu); -void usb_buffer_disable(struct cgpu_info *cgpu); +int _usb_ftdi_set_latency(struct cgpu_info *cgpu, int intinfo); +#define usb_ftdi_set_latency(_cgpu) _usb_ftdi_set_latency(_cgpu, DEFAULT_INTINFO) void usb_buffer_clear(struct cgpu_info *cgpu); uint32_t usb_buffer_size(struct cgpu_info *cgpu); -void usb_set_cps(struct cgpu_info *cgpu, int cps); -void usb_enable_cps(struct cgpu_info *cgpu); -void usb_disable_cps(struct cgpu_info *cgpu); -int usb_interface(struct cgpu_info *cgpu); +int _usb_interface(struct cgpu_info *cgpu, int intinfo); +#define usb_interface(_cgpu) _usb_interface(_cgpu, DEFAULT_INTINFO) enum sub_ident usb_ident(struct cgpu_info *cgpu); -void usb_set_pps(struct cgpu_info *cgpu, uint16_t PrefPacketSize); void usb_set_dev_start(struct cgpu_info *cgpu); void usb_cleanup(); void usb_initialise(); void *usb_resource_thread(void *userdata); +void initialise_usblocks(void); #define usb_read(cgpu, buf, bufsiz, read, cmd) \ - _usb_read(cgpu, DEFAULT_EP_IN, buf, bufsiz, read, DEVTIMEOUT, NULL, cmd, false) + _usb_read(cgpu, DEFAULT_INTINFO, DEFAULT_EP_IN, buf, bufsiz, read, DEVTIMEOUT, NULL, cmd, false, false) + +#define usb_read_cancellable(cgpu, buf, bufsiz, read, cmd) \ + _usb_read(cgpu, DEFAULT_INTINFO, DEFAULT_EP_IN, buf, bufsiz, read, DEVTIMEOUT, NULL, cmd, false, true) + +#define usb_read_ii(cgpu, intinfo, buf, bufsiz, read, cmd) \ + _usb_read(cgpu, intinfo, DEFAULT_EP_IN, buf, bufsiz, read, DEVTIMEOUT, NULL, cmd, false, false) #define usb_read_once(cgpu, buf, bufsiz, read, cmd) \ - _usb_read(cgpu, DEFAULT_EP_IN, buf, bufsiz, read, DEVTIMEOUT, NULL, cmd, true) + _usb_read(cgpu, DEFAULT_INTINFO, DEFAULT_EP_IN, buf, bufsiz, read, DEVTIMEOUT, NULL, cmd, true, false) + +#define usb_read_ii_once(cgpu, intinfo, buf, bufsiz, read, cmd) \ + _usb_read(cgpu, intinfo, DEFAULT_EP_IN, buf, bufsiz, read, DEVTIMEOUT, NULL, cmd, true, false) #define usb_read_once_timeout(cgpu, buf, bufsiz, read, timeout, cmd) \ - _usb_read(cgpu, DEFAULT_EP_IN, buf, bufsiz, read, timeout, NULL, cmd, true) + _usb_read(cgpu, DEFAULT_INTINFO, DEFAULT_EP_IN, buf, bufsiz, read, timeout, NULL, cmd, true, false) + +#define usb_read_once_timeout_cancellable(cgpu, buf, bufsiz, read, timeout, cmd) \ + _usb_read(cgpu, DEFAULT_INTINFO, DEFAULT_EP_IN, buf, bufsiz, read, timeout, NULL, cmd, true, true) + +#define usb_read_ii_once_timeout(cgpu, intinfo, buf, bufsiz, read, timeout, cmd) \ + _usb_read(cgpu, intinfo, DEFAULT_EP_IN, buf, bufsiz, read, timeout, NULL, cmd, true, false) #define usb_read_nl(cgpu, buf, bufsiz, read, cmd) \ - _usb_read(cgpu, DEFAULT_EP_IN, buf, bufsiz, read, DEVTIMEOUT, "\n", cmd, false) + _usb_read(cgpu, DEFAULT_INTINFO, DEFAULT_EP_IN, buf, bufsiz, read, DEVTIMEOUT, "\n", cmd, false, false) #define usb_read_nl_timeout(cgpu, buf, bufsiz, read, timeout, cmd) \ - _usb_read(cgpu, DEFAULT_EP_IN, buf, bufsiz, read, timeout, "\n", cmd, false) + _usb_read(cgpu, DEFAULT_INTINFO, DEFAULT_EP_IN, buf, bufsiz, read, timeout, "\n", cmd, false, false) #define usb_read_ok(cgpu, buf, bufsiz, read, cmd) \ - _usb_read(cgpu, DEFAULT_EP_IN, buf, bufsiz, read, DEVTIMEOUT, "OK\n", cmd, false) + _usb_read(cgpu, DEFAULT_INTINFO, DEFAULT_EP_IN, buf, bufsiz, read, DEVTIMEOUT, "OK\n", cmd, false, false) #define usb_read_ok_timeout(cgpu, buf, bufsiz, read, timeout, cmd) \ - _usb_read(cgpu, DEFAULT_EP_IN, buf, bufsiz, read, timeout, "OK\n", cmd, false) + _usb_read(cgpu, DEFAULT_INTINFO, DEFAULT_EP_IN, buf, bufsiz, read, timeout, "OK\n", cmd, false, false) #define usb_read_ep(cgpu, ep, buf, bufsiz, read, cmd) \ - _usb_read(cgpu, ep, buf, bufsiz, read, DEVTIMEOUT, NULL, cmd, false) + _usb_read(cgpu, DEFAULT_INTINFO, ep, buf, bufsiz, read, DEVTIMEOUT, NULL, cmd, false, false) #define usb_read_timeout(cgpu, buf, bufsiz, read, timeout, cmd) \ - _usb_read(cgpu, DEFAULT_EP_IN, buf, bufsiz, read, timeout, NULL, cmd, false) + _usb_read(cgpu, DEFAULT_INTINFO, DEFAULT_EP_IN, buf, bufsiz, read, timeout, NULL, cmd, false, false) + +#define usb_read_timeout_cancellable(cgpu, buf, bufsiz, read, timeout, cmd) \ + _usb_read(cgpu, DEFAULT_INTINFO, DEFAULT_EP_IN, buf, bufsiz, read, timeout, NULL, cmd, false, true) + +#define usb_read_ii_timeout(cgpu, intinfo, buf, bufsiz, read, timeout, cmd) \ + _usb_read(cgpu, intinfo, DEFAULT_EP_IN, buf, bufsiz, read, timeout, NULL, cmd, false, false) + +#define usb_read_ii_timeout_cancellable(cgpu, intinfo, buf, bufsiz, read, timeout, cmd) \ + _usb_read(cgpu, intinfo, DEFAULT_EP_IN, buf, bufsiz, read, timeout, NULL, cmd, false, true) #define usb_read_ep_timeout(cgpu, ep, buf, bufsiz, read, timeout, cmd) \ - _usb_read(cgpu, ep, buf, bufsiz, read, timeout, NULL, cmd, false) + _usb_read(cgpu, DEFAULT_INTINFO, ep, buf, bufsiz, read, timeout, NULL, cmd, false, false) #define usb_write(cgpu, buf, bufsiz, wrote, cmd) \ - _usb_write(cgpu, DEFAULT_EP_OUT, buf, bufsiz, wrote, DEVTIMEOUT, cmd) + _usb_write(cgpu, DEFAULT_INTINFO, DEFAULT_EP_OUT, buf, bufsiz, wrote, DEVTIMEOUT, cmd) + +#define usb_write_ii(cgpu, intinfo, buf, bufsiz, wrote, cmd) \ + _usb_write(cgpu, intinfo, DEFAULT_EP_OUT, buf, bufsiz, wrote, DEVTIMEOUT, cmd) #define usb_write_ep(cgpu, ep, buf, bufsiz, wrote, cmd) \ - _usb_write(cgpu, ep, buf, bufsiz, wrote, DEVTIMEOUT, cmd) + _usb_write(cgpu, DEFAULT_INTINFO, ep, buf, bufsiz, wrote, DEVTIMEOUT, cmd) #define usb_write_timeout(cgpu, buf, bufsiz, wrote, timeout, cmd) \ - _usb_write(cgpu, DEFAULT_EP_OUT, buf, bufsiz, wrote, timeout, cmd) + _usb_write(cgpu, DEFAULT_INTINFO, DEFAULT_EP_OUT, buf, bufsiz, wrote, timeout, cmd) #define usb_write_ep_timeout(cgpu, ep, buf, bufsiz, wrote, timeout, cmd) \ - _usb_write(cgpu, ep, buf, bufsiz, wrote, timeout, cmd) + _usb_write(cgpu, DEFAULT_INTINFO, ep, buf, bufsiz, wrote, timeout, cmd) #define usb_transfer(cgpu, typ, req, val, idx, cmd) \ _usb_transfer(cgpu, typ, req, val, idx, NULL, 0, DEVTIMEOUT, cmd) diff --git a/uthash.h b/uthash.h index 4cedb9c954..72acf117e7 100644 --- a/uthash.h +++ b/uthash.h @@ -1,5 +1,5 @@ /* -Copyright (c) 2003-2011, Troy D. Hanson http://uthash.sourceforge.net +Copyright (c) 2003-2013, Troy D. Hanson http://troydhanson.github.com/uthash/ All rights reserved. Redistribution and use in source and binary forms, with or without @@ -22,7 +22,7 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #ifndef UTHASH_H -#define UTHASH_H +#define UTHASH_H #include /* memcmp,strlen */ #include /* ptrdiff_t */ @@ -49,7 +49,7 @@ do { char **_da_dst = (char**)(&(dst)); \ *_da_dst = (char*)(src); \ } while(0) -#else +#else #define DECLTYPE_ASSIGN(dst,src) \ do { \ (dst) = DECLTYPE(dst)(src); \ @@ -64,14 +64,24 @@ typedef unsigned char uint8_t; #include /* uint32_t */ #endif -#define UTHASH_VERSION 1.9.4 +#define UTHASH_VERSION 1.9.8 +#ifndef uthash_fatal #define uthash_fatal(msg) exit(-1) /* fatal error (out of memory,etc) */ +#endif +#ifndef uthash_malloc #define uthash_malloc(sz) malloc(sz) /* malloc fcn */ +#endif +#ifndef uthash_free #define uthash_free(ptr,sz) free(ptr) /* free fcn */ +#endif +#ifndef uthash_noexpand_fyi #define uthash_noexpand_fyi(tbl) /* can be defined to log noexpand */ +#endif +#ifndef uthash_expand_fyi #define uthash_expand_fyi(tbl) /* can be defined to log expands */ +#endif /* initial number of buckets */ #define HASH_INITIAL_NUM_BUCKETS 32 /* initial number of buckets */ @@ -104,12 +114,12 @@ do { if (!((tbl)->bloom_bv)) { uthash_fatal( "out of memory"); } \ memset((tbl)->bloom_bv, 0, HASH_BLOOM_BYTELEN); \ (tbl)->bloom_sig = HASH_BLOOM_SIGNATURE; \ -} while (0); +} while (0) #define HASH_BLOOM_FREE(tbl) \ do { \ uthash_free((tbl)->bloom_bv, HASH_BLOOM_BYTELEN); \ -} while (0); +} while (0) #define HASH_BLOOM_BITSET(bv,idx) (bv[(idx)/8] |= (1U << ((idx)%8))) #define HASH_BLOOM_BITTEST(bv,idx) (bv[(idx)/8] & (1U << ((idx)%8))) @@ -121,10 +131,11 @@ do { HASH_BLOOM_BITTEST((tbl)->bloom_bv, (hashv & (uint32_t)((1ULL << (tbl)->bloom_nbits) - 1))) #else -#define HASH_BLOOM_MAKE(tbl) -#define HASH_BLOOM_FREE(tbl) -#define HASH_BLOOM_ADD(tbl,hashv) +#define HASH_BLOOM_MAKE(tbl) +#define HASH_BLOOM_FREE(tbl) +#define HASH_BLOOM_ADD(tbl,hashv) #define HASH_BLOOM_TEST(tbl,hashv) (1) +#define HASH_BLOOM_BYTELEN 0 #endif #define HASH_MAKE_TABLE(hh,head) \ @@ -147,14 +158,24 @@ do { } while(0) #define HASH_ADD(hh,head,fieldname,keylen_in,add) \ - HASH_ADD_KEYPTR(hh,head,&add->fieldname,keylen_in,add) + HASH_ADD_KEYPTR(hh,head,&((add)->fieldname),keylen_in,add) +#define HASH_REPLACE(hh,head,fieldname,keylen_in,add,replaced) \ +do { \ + replaced=NULL; \ + HASH_FIND(hh,head,&((add)->fieldname),keylen_in,replaced); \ + if (replaced!=NULL) { \ + HASH_DELETE(hh,head,replaced); \ + }; \ + HASH_ADD(hh,head,fieldname,keylen_in,add); \ +} while(0) + #define HASH_ADD_KEYPTR(hh,head,keyptr,keylen_in,add) \ do { \ unsigned _ha_bkt; \ (add)->hh.next = NULL; \ - (add)->hh.key = (char*)keyptr; \ - (add)->hh.keylen = keylen_in; \ + (add)->hh.key = (char*)(keyptr); \ + (add)->hh.keylen = (unsigned)(keylen_in); \ if (!(head)) { \ head = (add); \ (head)->hh.prev = NULL; \ @@ -205,17 +226,17 @@ do { _hd_hh_del = &((delptr)->hh); \ if ((delptr) == ELMT_FROM_HH((head)->hh.tbl,(head)->hh.tbl->tail)) { \ (head)->hh.tbl->tail = \ - (UT_hash_handle*)((char*)((delptr)->hh.prev) + \ + (UT_hash_handle*)((ptrdiff_t)((delptr)->hh.prev) + \ (head)->hh.tbl->hho); \ } \ if ((delptr)->hh.prev) { \ - ((UT_hash_handle*)((char*)((delptr)->hh.prev) + \ + ((UT_hash_handle*)((ptrdiff_t)((delptr)->hh.prev) + \ (head)->hh.tbl->hho))->next = (delptr)->hh.next; \ } else { \ DECLTYPE_ASSIGN(head,(delptr)->hh.next); \ } \ if (_hd_hh_del->next) { \ - ((UT_hash_handle*)((char*)_hd_hh_del->next + \ + ((UT_hash_handle*)((ptrdiff_t)_hd_hh_del->next + \ (head)->hh.tbl->hho))->prev = \ _hd_hh_del->prev; \ } \ @@ -232,14 +253,20 @@ do { HASH_FIND(hh,head,findstr,strlen(findstr),out) #define HASH_ADD_STR(head,strfield,add) \ HASH_ADD(hh,head,strfield,strlen(add->strfield),add) +#define HASH_REPLACE_STR(head,strfield,add,replaced) \ + HASH_REPLACE(hh,head,strfield,strlen(add->strfield),add,replaced) #define HASH_FIND_INT(head,findint,out) \ HASH_FIND(hh,head,findint,sizeof(int),out) #define HASH_ADD_INT(head,intfield,add) \ HASH_ADD(hh,head,intfield,sizeof(int),add) +#define HASH_REPLACE_INT(head,intfield,add,replaced) \ + HASH_REPLACE(hh,head,intfield,sizeof(int),add,replaced) #define HASH_FIND_PTR(head,findptr,out) \ HASH_FIND(hh,head,findptr,sizeof(void *),out) #define HASH_ADD_PTR(head,ptrfield,add) \ HASH_ADD(hh,head,ptrfield,sizeof(void *),add) +#define HASH_REPLACE_PTR(head,ptrfield,add) \ + HASH_REPLACE(hh,head,ptrfield,sizeof(void *),add,replaced) #define HASH_DEL(head,delptr) \ HASH_DELETE(hh,head,delptr) @@ -300,10 +327,10 @@ do { } \ } while (0) #else -#define HASH_FSCK(hh,head) +#define HASH_FSCK(hh,head) #endif -/* When compiled with -DHASH_EMIT_KEYS, length-prefixed keys are emitted to +/* When compiled with -DHASH_EMIT_KEYS, length-prefixed keys are emitted to * the descriptor to which this macro is defined for tuning the hash function. * The app can #include to get the prototype for write(2). */ #ifdef HASH_EMIT_KEYS @@ -313,12 +340,12 @@ do { write(HASH_EMIT_KEYS, &_klen, sizeof(_klen)); \ write(HASH_EMIT_KEYS, keyptr, fieldlen); \ } while (0) -#else -#define HASH_EMIT_KEY(hh,head,keyptr,fieldlen) +#else +#define HASH_EMIT_KEY(hh,head,keyptr,fieldlen) #endif /* default to Jenkin's hash unless overridden e.g. DHASH_FUNCTION=HASH_SAX */ -#ifdef HASH_FUNCTION +#ifdef HASH_FUNCTION #define HASH_FCN HASH_FUNCTION #else #define HASH_FCN HASH_JEN @@ -335,7 +362,7 @@ do { } while (0) -/* SAX/FNV/OAT/JEN hash functions are macro variants of those listed at +/* SAX/FNV/OAT/JEN hash functions are macro variants of those listed at * http://eternallyconfuzzled.com/tuts/algorithms/jsw_tut_hashing.aspx */ #define HASH_SAX(key,keylen,num_bkts,hashv,bkt) \ do { \ @@ -355,8 +382,8 @@ do { for(_fn_i=0; _fn_i < keylen; _fn_i++) \ hashv = (hashv * 16777619) ^ _hf_key[_fn_i]; \ bkt = hashv & (num_bkts-1); \ -} while(0); - +} while(0) + #define HASH_OAT(key,keylen,num_bkts,hashv,bkt) \ do { \ unsigned _ho_i; \ @@ -389,10 +416,10 @@ do { #define HASH_JEN(key,keylen,num_bkts,hashv,bkt) \ do { \ unsigned _hj_i,_hj_j,_hj_k; \ - char *_hj_key=(char*)(key); \ + unsigned char *_hj_key=(unsigned char*)(key); \ hashv = 0xfeedbeef; \ _hj_i = _hj_j = 0x9e3779b9; \ - _hj_k = keylen; \ + _hj_k = (unsigned)(keylen); \ while (_hj_k >= 12) { \ _hj_i += (_hj_key[0] + ( (unsigned)_hj_key[1] << 8 ) \ + ( (unsigned)_hj_key[2] << 16 ) \ @@ -440,7 +467,7 @@ do { #endif #define HASH_SFH(key,keylen,num_bkts,hashv,bkt) \ do { \ - char *_sfh_key=(char*)(key); \ + unsigned char *_sfh_key=(unsigned char*)(key); \ uint32_t _sfh_tmp, _sfh_len = keylen; \ \ int _sfh_rem = _sfh_len & 3; \ @@ -450,7 +477,7 @@ do { /* Main loop */ \ for (;_sfh_len > 0; _sfh_len--) { \ hashv += get16bits (_sfh_key); \ - _sfh_tmp = (get16bits (_sfh_key+2) << 11) ^ hashv; \ + _sfh_tmp = (uint32_t)(get16bits (_sfh_key+2)) << 11 ^ hashv; \ hashv = (hashv << 16) ^ _sfh_tmp; \ _sfh_key += 2*sizeof (uint16_t); \ hashv += hashv >> 11; \ @@ -460,7 +487,7 @@ do { switch (_sfh_rem) { \ case 3: hashv += get16bits (_sfh_key); \ hashv ^= hashv << 16; \ - hashv ^= _sfh_key[sizeof (uint16_t)] << 18; \ + hashv ^= (uint32_t)(_sfh_key[sizeof (uint16_t)] << 18); \ hashv += hashv >> 11; \ break; \ case 2: hashv += get16bits (_sfh_key); \ @@ -480,19 +507,19 @@ do { hashv ^= hashv << 25; \ hashv += hashv >> 6; \ bkt = hashv & (num_bkts-1); \ -} while(0); +} while(0) #ifdef HASH_USING_NO_STRICT_ALIASING /* The MurmurHash exploits some CPU's (x86,x86_64) tolerance for unaligned reads. * For other types of CPU's (e.g. Sparc) an unaligned read causes a bus error. - * MurmurHash uses the faster approach only on CPU's where we know it's safe. + * MurmurHash uses the faster approach only on CPU's where we know it's safe. * * Note the preprocessor built-in defines can be emitted using: * * gcc -m64 -dM -E - < /dev/null (on gcc) * cc -## a.c (where a.c is a simple test file) (Sun Studio) */ -#if (defined(__i386__) || defined(__x86_64__)) +#if (defined(__i386__) || defined(__x86_64__) || defined(_M_IX86)) #define MUR_GETBLOCK(p,i) p[i] #else /* non intel */ #define MUR_PLUS0_ALIGNED(p) (((unsigned long)p & 0x3) == 0) @@ -531,10 +558,12 @@ do { \ uint32_t _mur_h1 = 0xf88D5353; \ uint32_t _mur_c1 = 0xcc9e2d51; \ uint32_t _mur_c2 = 0x1b873593; \ + uint32_t _mur_k1 = 0; \ + const uint8_t *_mur_tail; \ const uint32_t *_mur_blocks = (const uint32_t*)(_mur_data+_mur_nblocks*4); \ int _mur_i; \ for(_mur_i = -_mur_nblocks; _mur_i; _mur_i++) { \ - uint32_t _mur_k1 = MUR_GETBLOCK(_mur_blocks,_mur_i); \ + _mur_k1 = MUR_GETBLOCK(_mur_blocks,_mur_i); \ _mur_k1 *= _mur_c1; \ _mur_k1 = MUR_ROTL32(_mur_k1,15); \ _mur_k1 *= _mur_c2; \ @@ -543,8 +572,8 @@ do { \ _mur_h1 = MUR_ROTL32(_mur_h1,13); \ _mur_h1 = _mur_h1*5+0xe6546b64; \ } \ - const uint8_t *_mur_tail = (const uint8_t*)(_mur_data + _mur_nblocks*4); \ - uint32_t _mur_k1=0; \ + _mur_tail = (const uint8_t*)(_mur_data + _mur_nblocks*4); \ + _mur_k1=0; \ switch((keylen) & 3) { \ case 3: _mur_k1 ^= _mur_tail[2] << 16; \ case 2: _mur_k1 ^= _mur_tail[1] << 8; \ @@ -562,7 +591,7 @@ do { \ #endif /* HASH_USING_NO_STRICT_ALIASING */ /* key comparison function; return 0 if keys equal */ -#define HASH_KEYCMP(a,b,len) memcmp(a,b,len) +#define HASH_KEYCMP(a,b,len) memcmp(a,b,len) /* iterate over items in a known bucket to find desired item */ #define HASH_FIND_IN_BKT(tbl,hh,head,keyptr,keylen_in,out) \ @@ -570,10 +599,10 @@ do { if (head.hh_head) DECLTYPE_ASSIGN(out,ELMT_FROM_HH(tbl,head.hh_head)); \ else out=NULL; \ while (out) { \ - if (out->hh.keylen == keylen_in) { \ - if ((HASH_KEYCMP(out->hh.key,keyptr,keylen_in)) == 0) break; \ + if ((out)->hh.keylen == keylen_in) { \ + if ((HASH_KEYCMP((out)->hh.key,keyptr,keylen_in)) == 0) break; \ } \ - if (out->hh.hh_next) DECLTYPE_ASSIGN(out,ELMT_FROM_HH(tbl,out->hh.hh_next)); \ + if ((out)->hh.hh_next) DECLTYPE_ASSIGN(out,ELMT_FROM_HH(tbl,(out)->hh.hh_next)); \ else out = NULL; \ } \ } while(0) @@ -603,36 +632,36 @@ do { } \ if (hh_del->hh_next) { \ hh_del->hh_next->hh_prev = hh_del->hh_prev; \ - } + } /* Bucket expansion has the effect of doubling the number of buckets * and redistributing the items into the new buckets. Ideally the * items will distribute more or less evenly into the new buckets * (the extent to which this is true is a measure of the quality of - * the hash function as it applies to the key domain). - * + * the hash function as it applies to the key domain). + * * With the items distributed into more buckets, the chain length * (item count) in each bucket is reduced. Thus by expanding buckets - * the hash keeps a bound on the chain length. This bounded chain + * the hash keeps a bound on the chain length. This bounded chain * length is the essence of how a hash provides constant time lookup. - * + * * The calculation of tbl->ideal_chain_maxlen below deserves some * explanation. First, keep in mind that we're calculating the ideal * maximum chain length based on the *new* (doubled) bucket count. * In fractions this is just n/b (n=number of items,b=new num buckets). - * Since the ideal chain length is an integer, we want to calculate + * Since the ideal chain length is an integer, we want to calculate * ceil(n/b). We don't depend on floating point arithmetic in this * hash, so to calculate ceil(n/b) with integers we could write - * + * * ceil(n/b) = (n/b) + ((n%b)?1:0) - * + * * and in fact a previous version of this hash did just that. * But now we have improved things a bit by recognizing that b is * always a power of two. We keep its base 2 log handy (call it lb), * so now we can write this with a bit shift and logical AND: - * + * * ceil(n/b) = (n>>lb) + ( (n & (b-1)) ? 1:0) - * + * */ #define HASH_EXPAND_BUCKETS(tbl) \ do { \ @@ -684,7 +713,7 @@ do { /* This is an adaptation of Simon Tatham's O(n log(n)) mergesort */ -/* Note that HASH_SORT assumes the hash handle name to be hh. +/* Note that HASH_SORT assumes the hash handle name to be hh. * HASH_SRT was added to allow the hash handle name to be passed in. */ #define HASH_SORT(head,cmpfcn) HASH_SRT(hh,head,cmpfcn) #define HASH_SRT(hh,head,cmpfcn) \ @@ -722,18 +751,22 @@ do { _hs_qsize--; \ } else if ( (_hs_qsize == 0) || !(_hs_q) ) { \ _hs_e = _hs_p; \ - _hs_p = (UT_hash_handle*)((_hs_p->next) ? \ - ((void*)((char*)(_hs_p->next) + \ - (head)->hh.tbl->hho)) : NULL); \ + if (_hs_p){ \ + _hs_p = (UT_hash_handle*)((_hs_p->next) ? \ + ((void*)((char*)(_hs_p->next) + \ + (head)->hh.tbl->hho)) : NULL); \ + } \ _hs_psize--; \ } else if (( \ cmpfcn(DECLTYPE(head)(ELMT_FROM_HH((head)->hh.tbl,_hs_p)), \ DECLTYPE(head)(ELMT_FROM_HH((head)->hh.tbl,_hs_q))) \ ) <= 0) { \ _hs_e = _hs_p; \ - _hs_p = (UT_hash_handle*)((_hs_p->next) ? \ - ((void*)((char*)(_hs_p->next) + \ - (head)->hh.tbl->hho)) : NULL); \ + if (_hs_p){ \ + _hs_p = (UT_hash_handle*)((_hs_p->next) ? \ + ((void*)((char*)(_hs_p->next) + \ + (head)->hh.tbl->hho)) : NULL); \ + } \ _hs_psize--; \ } else { \ _hs_e = _hs_q; \ @@ -748,13 +781,17 @@ do { } else { \ _hs_list = _hs_e; \ } \ + if (_hs_e) { \ _hs_e->prev = ((_hs_tail) ? \ ELMT_FROM_HH((head)->hh.tbl,_hs_tail) : NULL); \ + } \ _hs_tail = _hs_e; \ } \ _hs_p = _hs_q; \ } \ - _hs_tail->next = NULL; \ + if (_hs_tail){ \ + _hs_tail->next = NULL; \ + } \ if ( _hs_nmerges <= 1 ) { \ _hs_looping=0; \ (head)->hh.tbl->tail = _hs_tail; \ @@ -766,10 +803,10 @@ do { } \ } while (0) -/* This function selects items from one hash into another hash. - * The end result is that the selected items have dual presence - * in both hashes. There is no copy of the items made; rather - * they are added into the new hash through a secondary hash +/* This function selects items from one hash into another hash. + * The end result is that the selected items have dual presence + * in both hashes. There is no copy of the items made; rather + * they are added into the new hash through a secondary hash * hash handle that must be present in the structure. */ #define HASH_SELECT(hh_dst, dst, hh_src, src, cond) \ do { \ @@ -814,15 +851,22 @@ do { if (head) { \ uthash_free((head)->hh.tbl->buckets, \ (head)->hh.tbl->num_buckets*sizeof(struct UT_hash_bucket)); \ + HASH_BLOOM_FREE((head)->hh.tbl); \ uthash_free((head)->hh.tbl, sizeof(UT_hash_table)); \ (head)=NULL; \ } \ } while(0) +#define HASH_OVERHEAD(hh,head) \ + (size_t)((((head)->hh.tbl->num_items * sizeof(UT_hash_handle)) + \ + ((head)->hh.tbl->num_buckets * sizeof(UT_hash_bucket)) + \ + (sizeof(UT_hash_table)) + \ + (HASH_BLOOM_BYTELEN))) + #ifdef NO_DECLTYPE #define HASH_ITER(hh,head,el,tmp) \ for((el)=(head), (*(char**)(&(tmp)))=(char*)((head)?(head)->hh.next:NULL); \ - el; (el)=(tmp),(*(char**)(&(tmp)))=(char*)((tmp)?(tmp)->hh.next:NULL)) + el; (el)=(tmp),(*(char**)(&(tmp)))=(char*)((tmp)?(tmp)->hh.next:NULL)) #else #define HASH_ITER(hh,head,el,tmp) \ for((el)=(head),(tmp)=DECLTYPE(el)((head)?(head)->hh.next:NULL); \ @@ -830,7 +874,7 @@ for((el)=(head),(tmp)=DECLTYPE(el)((head)?(head)->hh.next:NULL); #endif /* obtain a count of items in the hash */ -#define HASH_COUNT(head) HASH_CNT(hh,head) +#define HASH_COUNT(head) HASH_CNT(hh,head) #define HASH_CNT(hh,head) ((head)?((head)->hh.tbl->num_items):0) typedef struct UT_hash_bucket { @@ -839,7 +883,7 @@ typedef struct UT_hash_bucket { /* expand_mult is normally set to 0. In this situation, the max chain length * threshold is enforced at its default value, HASH_BKT_CAPACITY_THRESH. (If - * the bucket's chain exceeds this length, bucket expansion is triggered). + * the bucket's chain exceeds this length, bucket expansion is triggered). * However, setting expand_mult to a non-zero value delays bucket expansion * (that would be triggered by additions to this particular bucket) * until its chain length reaches a *multiple* of HASH_BKT_CAPACITY_THRESH. @@ -847,7 +891,7 @@ typedef struct UT_hash_bucket { * multiplier is to reduce bucket expansions, since they are expensive, in * situations where we know that a particular bucket tends to be overused. * It is better to let its chain length grow to a longer yet-still-bounded - * value, than to do an O(n) bucket expansion too often. + * value, than to do an O(n) bucket expansion too often. */ unsigned expand_mult; @@ -873,7 +917,7 @@ typedef struct UT_hash_table { * hash distribution; reaching them in a chain traversal takes >ideal steps */ unsigned nonideal_items; - /* ineffective expands occur when a bucket doubling was performed, but + /* ineffective expands occur when a bucket doubling was performed, but * afterward, more than half the items in the hash had nonideal chain * positions. If this happens on two consecutive expansions we inhibit any * further expansion, as it's not helping; this happens when the hash diff --git a/util.c b/util.c index 5060720ec9..f0e8e230aa 100644 --- a/util.c +++ b/util.c @@ -1,5 +1,5 @@ /* - * Copyright 2011-2013 Con Kolivas + * Copyright 2011-2014 Con Kolivas * Copyright 2010 Jeff Garzik * * This program is free software; you can redistribute it and/or modify it @@ -16,7 +16,9 @@ #include #include #include +#ifdef HAVE_LIBCURL #include +#endif #include #include #include @@ -31,11 +33,11 @@ # include # include #else -# include # include # include # include #endif +#include #include "miner.h" #include "elist.h" @@ -45,6 +47,254 @@ #define DEFAULT_SOCKWAIT 60 bool successful_connect = false; + +int no_yield(void) +{ + return 0; +} + +int (*selective_yield)(void) = &no_yield; + +unsigned char bit_swap_table[256] = +{ + 0x00, 0x80, 0x40, 0xc0, 0x20, 0xa0, 0x60, 0xe0, + 0x10, 0x90, 0x50, 0xd0, 0x30, 0xb0, 0x70, 0xf0, + 0x08, 0x88, 0x48, 0xc8, 0x28, 0xa8, 0x68, 0xe8, + 0x18, 0x98, 0x58, 0xd8, 0x38, 0xb8, 0x78, 0xf8, + 0x04, 0x84, 0x44, 0xc4, 0x24, 0xa4, 0x64, 0xe4, + 0x14, 0x94, 0x54, 0xd4, 0x34, 0xb4, 0x74, 0xf4, + 0x0c, 0x8c, 0x4c, 0xcc, 0x2c, 0xac, 0x6c, 0xec, + 0x1c, 0x9c, 0x5c, 0xdc, 0x3c, 0xbc, 0x7c, 0xfc, + 0x02, 0x82, 0x42, 0xc2, 0x22, 0xa2, 0x62, 0xe2, + 0x12, 0x92, 0x52, 0xd2, 0x32, 0xb2, 0x72, 0xf2, + 0x0a, 0x8a, 0x4a, 0xca, 0x2a, 0xaa, 0x6a, 0xea, + 0x1a, 0x9a, 0x5a, 0xda, 0x3a, 0xba, 0x7a, 0xfa, + 0x06, 0x86, 0x46, 0xc6, 0x26, 0xa6, 0x66, 0xe6, + 0x16, 0x96, 0x56, 0xd6, 0x36, 0xb6, 0x76, 0xf6, + 0x0e, 0x8e, 0x4e, 0xce, 0x2e, 0xae, 0x6e, 0xee, + 0x1e, 0x9e, 0x5e, 0xde, 0x3e, 0xbe, 0x7e, 0xfe, + 0x01, 0x81, 0x41, 0xc1, 0x21, 0xa1, 0x61, 0xe1, + 0x11, 0x91, 0x51, 0xd1, 0x31, 0xb1, 0x71, 0xf1, + 0x09, 0x89, 0x49, 0xc9, 0x29, 0xa9, 0x69, 0xe9, + 0x19, 0x99, 0x59, 0xd9, 0x39, 0xb9, 0x79, 0xf9, + 0x05, 0x85, 0x45, 0xc5, 0x25, 0xa5, 0x65, 0xe5, + 0x15, 0x95, 0x55, 0xd5, 0x35, 0xb5, 0x75, 0xf5, + 0x0d, 0x8d, 0x4d, 0xcd, 0x2d, 0xad, 0x6d, 0xed, + 0x1d, 0x9d, 0x5d, 0xdd, 0x3d, 0xbd, 0x7d, 0xfd, + 0x03, 0x83, 0x43, 0xc3, 0x23, 0xa3, 0x63, 0xe3, + 0x13, 0x93, 0x53, 0xd3, 0x33, 0xb3, 0x73, 0xf3, + 0x0b, 0x8b, 0x4b, 0xcb, 0x2b, 0xab, 0x6b, 0xeb, + 0x1b, 0x9b, 0x5b, 0xdb, 0x3b, 0xbb, 0x7b, 0xfb, + 0x07, 0x87, 0x47, 0xc7, 0x27, 0xa7, 0x67, 0xe7, + 0x17, 0x97, 0x57, 0xd7, 0x37, 0xb7, 0x77, 0xf7, + 0x0f, 0x8f, 0x4f, 0xcf, 0x2f, 0xaf, 0x6f, 0xef, + 0x1f, 0x9f, 0x5f, 0xdf, 0x3f, 0xbf, 0x7f, 0xff, +}; + +static void keep_sockalive(SOCKETTYPE fd) +{ + const int tcp_one = 1; +#ifndef WIN32 + const int tcp_keepidle = 45; + const int tcp_keepintvl = 30; + int flags = fcntl(fd, F_GETFL, 0); + + fcntl(fd, F_SETFL, O_NONBLOCK | flags); +#else + u_long flags = 1; + + ioctlsocket(fd, FIONBIO, &flags); +#endif + + setsockopt(fd, SOL_SOCKET, SO_KEEPALIVE, (const void *)&tcp_one, sizeof(tcp_one)); + if (!opt_delaynet) +#ifndef __linux + setsockopt(fd, IPPROTO_TCP, TCP_NODELAY, (const void *)&tcp_one, sizeof(tcp_one)); +#else /* __linux */ + fcntl(fd, F_SETFD, FD_CLOEXEC); + setsockopt(fd, SOL_TCP, TCP_NODELAY, (const void *)&tcp_one, sizeof(tcp_one)); + setsockopt(fd, SOL_TCP, TCP_KEEPCNT, &tcp_one, sizeof(tcp_one)); + setsockopt(fd, SOL_TCP, TCP_KEEPIDLE, &tcp_keepidle, sizeof(tcp_keepidle)); + setsockopt(fd, SOL_TCP, TCP_KEEPINTVL, &tcp_keepintvl, sizeof(tcp_keepintvl)); +#endif /* __linux */ + +#ifdef __APPLE_CC__ + setsockopt(fd, IPPROTO_TCP, TCP_KEEPALIVE, &tcp_keepintvl, sizeof(tcp_keepintvl)); +#endif /* __APPLE_CC__ */ + +} + +#ifdef WIN32 +/* Generic versions of inet_pton for windows, using different names in case + * it is implemented in ming in the future. */ +#define W32NS_INADDRSZ 4 +#define W32NS_IN6ADDRSZ 16 +#define W32NS_INT16SZ 2 + +static int Inet_Pton4(const char *src, char *dst) +{ + uint8_t tmp[W32NS_INADDRSZ], *tp; + + int saw_digit = 0; + int octets = 0; + *(tp = tmp) = 0; + + int ch; + while ((ch = *src++) != '\0') + { + if (ch >= '0' && ch <= '9') + { + uint32_t n = *tp * 10 + (ch - '0'); + + if (saw_digit && *tp == 0) + return 0; + + if (n > 255) + return 0; + + *tp = n; + if (!saw_digit) + { + if (++octets > 4) + return 0; + saw_digit = 1; + } + } + else if (ch == '.' && saw_digit) + { + if (octets == 4) + return 0; + *++tp = 0; + saw_digit = 0; + } + else + return 0; + } + if (octets < 4) + return 0; + + memcpy(dst, tmp, W32NS_INADDRSZ); + + return 1; +} + +static int Inet_Pton6(const char *src, char *dst) +{ + static const char xdigits[] = "0123456789abcdef"; + uint8_t tmp[W32NS_IN6ADDRSZ]; + + uint8_t *tp = (uint8_t*) memset(tmp, '\0', W32NS_IN6ADDRSZ); + uint8_t *endp = tp + W32NS_IN6ADDRSZ; + uint8_t *colonp = NULL; + + /* Leading :: requires some special handling. */ + if (*src == ':') + { + if (*++src != ':') + return 0; + } + + const char *curtok = src; + int saw_xdigit = 0; + uint32_t val = 0; + int ch; + while ((ch = tolower(*src++)) != '\0') + { + const char *pch = strchr(xdigits, ch); + if (pch != NULL) + { + val <<= 4; + val |= (pch - xdigits); + if (val > 0xffff) + return 0; + saw_xdigit = 1; + continue; + } + if (ch == ':') + { + curtok = src; + if (!saw_xdigit) + { + if (colonp) + return 0; + colonp = tp; + continue; + } + else if (*src == '\0') + { + return 0; + } + if (tp + W32NS_INT16SZ > endp) + return 0; + *tp++ = (uint8_t) (val >> 8) & 0xff; + *tp++ = (uint8_t) val & 0xff; + saw_xdigit = 0; + val = 0; + continue; + } + if (ch == '.' && ((tp + W32NS_INADDRSZ) <= endp) && + Inet_Pton4(curtok, (char*) tp) > 0) + { + tp += W32NS_INADDRSZ; + saw_xdigit = 0; + break; /* '\0' was seen by inet_pton4(). */ + } + return 0; + } + if (saw_xdigit) + { + if (tp + W32NS_INT16SZ > endp) + return 0; + *tp++ = (uint8_t) (val >> 8) & 0xff; + *tp++ = (uint8_t) val & 0xff; + } + if (colonp != NULL) + { + int i; + /* + * Since some memmove()'s erroneously fail to handle + * overlapping regions, we'll do the shift by hand. + */ + const int n = tp - colonp; + + if (tp == endp) + return 0; + + for (i = 1; i <= n; i++) + { + endp[-i] = colonp[n - i]; + colonp[n - i] = 0; + } + tp = endp; + } + if (tp != endp) + return 0; + + memcpy(dst, tmp, W32NS_IN6ADDRSZ); + + return 1; +} + +int Inet_Pton(int af, const char *src, void *dst) +{ + switch (af) + { + case AF_INET: + return Inet_Pton4(src, dst); + case AF_INET6: + return Inet_Pton6(src, dst); + default: + return -1; + } +} +#endif + +struct tq_ent { + void *data; + struct list_head q_node; +}; + +#ifdef HAVE_LIBCURL struct timeval nettime; struct data_buffer { @@ -67,11 +317,6 @@ struct header_info { bool hadexpire; }; -struct tq_ent { - void *data; - struct list_head q_node; -}; - static void databuf_free(struct data_buffer *db) { if (!db) @@ -202,36 +447,19 @@ static size_t resp_hdr_cb(void *ptr, size_t size, size_t nmemb, void *user_data) return ptrlen; } -static void keep_sockalive(SOCKETTYPE fd) +static void last_nettime(struct timeval *last) { - const int tcp_one = 1; -#ifndef WIN32 - const int tcp_keepidle = 45; - const int tcp_keepintvl = 30; - int flags = fcntl(fd, F_GETFL, 0); - - fcntl(fd, F_SETFL, O_NONBLOCK | flags); -#else - u_long flags = 1; - - ioctlsocket(fd, FIONBIO, &flags); -#endif - - setsockopt(fd, SOL_SOCKET, SO_KEEPALIVE, (const void *)&tcp_one, sizeof(tcp_one)); - if (!opt_delaynet) -#ifndef __linux - setsockopt(fd, IPPROTO_TCP, TCP_NODELAY, (const void *)&tcp_one, sizeof(tcp_one)); -#else /* __linux */ - setsockopt(fd, SOL_TCP, TCP_NODELAY, (const void *)&tcp_one, sizeof(tcp_one)); - setsockopt(fd, SOL_TCP, TCP_KEEPCNT, &tcp_one, sizeof(tcp_one)); - setsockopt(fd, SOL_TCP, TCP_KEEPIDLE, &tcp_keepidle, sizeof(tcp_keepidle)); - setsockopt(fd, SOL_TCP, TCP_KEEPINTVL, &tcp_keepintvl, sizeof(tcp_keepintvl)); -#endif /* __linux */ - -#ifdef __APPLE_CC__ - setsockopt(fd, IPPROTO_TCP, TCP_KEEPALIVE, &tcp_keepintvl, sizeof(tcp_keepintvl)); -#endif /* __APPLE_CC__ */ + rd_lock(&netacc_lock); + last->tv_sec = nettime.tv_sec; + last->tv_usec = nettime.tv_usec; + rd_unlock(&netacc_lock); +} +static void set_nettime(void) +{ + wr_lock(&netacc_lock); + cgtime(&nettime); + wr_unlock(&netacc_lock); } #if CURL_HAS_KEEPALIVE @@ -255,21 +483,6 @@ static void keep_curlalive(CURL *curl) } #endif -static void last_nettime(struct timeval *last) -{ - rd_lock(&netacc_lock); - last->tv_sec = nettime.tv_sec; - last->tv_usec = nettime.tv_usec; - rd_unlock(&netacc_lock); -} - -static void set_nettime(void) -{ - wr_lock(&netacc_lock); - cgtime(&nettime); - wr_unlock(&netacc_lock); -} - static int curl_debug_cb(__maybe_unused CURL *handle, curl_infotype type, __maybe_unused char *data, size_t size, void *userdata) { @@ -293,6 +506,59 @@ static int curl_debug_cb(__maybe_unused CURL *handle, curl_infotype type, return 0; } +json_t *json_web_config(const char *url) +{ + struct data_buffer all_data = {NULL, 0}; + char curl_err_str[CURL_ERROR_SIZE]; + long timeout = 60; + json_error_t err; + json_t *val; + CURL *curl; + int rc; + + memset(&err, 0, sizeof(err)); + + curl = curl_easy_init(); + if (unlikely(!curl)) + quithere(1, "CURL initialisation failed"); + + curl_easy_setopt(curl, CURLOPT_TIMEOUT, timeout); + + curl_easy_setopt(curl, CURLOPT_NOSIGNAL, 1); + curl_easy_setopt(curl, CURLOPT_URL, url); + curl_easy_setopt(curl, CURLOPT_ENCODING, ""); + curl_easy_setopt(curl, CURLOPT_FAILONERROR, 1); + + curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, all_data_cb); + curl_easy_setopt(curl, CURLOPT_WRITEDATA, &all_data); + curl_easy_setopt(curl, CURLOPT_ERRORBUFFER, curl_err_str); + curl_easy_setopt(curl, CURLOPT_FOLLOWLOCATION, 1); + curl_easy_setopt(curl, CURLOPT_USE_SSL, CURLUSESSL_TRY); + + val = NULL; + rc = curl_easy_perform(curl); + curl_easy_cleanup(curl); + if (rc) { + applog(LOG_ERR, "HTTP config request of '%s' failed: %s", url, curl_err_str); + goto c_out; + } + + if (!all_data.buf) { + applog(LOG_ERR, "Empty config data received from '%s'", url); + goto c_out; + } + + val = JSON_LOADS(all_data.buf, &err); + if (!val) { + applog(LOG_ERR, "JSON config decode of '%s' failed(%d): %s", url, + err.line, err.text); + } + databuf_free(&all_data); + +c_out: + return val; +} + json_t *json_rpc_call(CURL *curl, const char *url, const char *userpass, const char *rpc_req, bool probe, bool longpoll, int *rolltime, @@ -513,29 +779,35 @@ json_t *json_rpc_call(CURL *curl, const char *url, curl_easy_setopt(curl, CURLOPT_FRESH_CONNECT, 1); return NULL; } +#define PROXY_HTTP CURLPROXY_HTTP +#define PROXY_HTTP_1_0 CURLPROXY_HTTP_1_0 +#define PROXY_SOCKS4 CURLPROXY_SOCKS4 +#define PROXY_SOCKS5 CURLPROXY_SOCKS5 +#define PROXY_SOCKS4A CURLPROXY_SOCKS4A +#define PROXY_SOCKS5H CURLPROXY_SOCKS5_HOSTNAME +#else /* HAVE_LIBCURL */ +#define PROXY_HTTP 0 +#define PROXY_HTTP_1_0 1 +#define PROXY_SOCKS4 2 +#define PROXY_SOCKS5 3 +#define PROXY_SOCKS4A 4 +#define PROXY_SOCKS5H 5 +#endif /* HAVE_LIBCURL */ -#if (LIBCURL_VERSION_MAJOR == 7 && LIBCURL_VERSION_MINOR >= 10) || (LIBCURL_VERSION_MAJOR > 7) static struct { const char *name; - curl_proxytype proxytype; + proxytypes_t proxytype; } proxynames[] = { - { "http:", CURLPROXY_HTTP }, -#if (LIBCURL_VERSION_MAJOR > 7) || (LIBCURL_VERSION_MINOR > 19) || (LIBCURL_VERSION_MINOR == 19 && LIBCURL_VERSION_PATCH >= 4) - { "http0:", CURLPROXY_HTTP_1_0 }, -#endif -#if (LIBCURL_VERSION_MAJOR > 7) || (LIBCURL_VERSION_MINOR > 15) || (LIBCURL_VERSION_MINOR == 15 && LIBCURL_VERSION_PATCH >= 2) - { "socks4:", CURLPROXY_SOCKS4 }, -#endif - { "socks5:", CURLPROXY_SOCKS5 }, -#if (LIBCURL_VERSION_MAJOR > 7) || (LIBCURL_VERSION_MINOR >= 18) - { "socks4a:", CURLPROXY_SOCKS4A }, - { "socks5h:", CURLPROXY_SOCKS5_HOSTNAME }, -#endif + { "http:", PROXY_HTTP }, + { "http0:", PROXY_HTTP_1_0 }, + { "socks4:", PROXY_SOCKS4 }, + { "socks5:", PROXY_SOCKS5 }, + { "socks4a:", PROXY_SOCKS4A }, + { "socks5h:", PROXY_SOCKS5H }, { NULL, 0 } }; -#endif -const char *proxytype(curl_proxytype proxytype) +const char *proxytype(proxytypes_t proxytype) { int i; @@ -550,7 +822,6 @@ char *get_proxy(char *url, struct pool *pool) { pool->rpc_proxy = NULL; -#if (LIBCURL_VERSION_MAJOR == 7 && LIBCURL_VERSION_MINOR >= 10) || (LIBCURL_VERSION_MAJOR > 7) char *split; int plen, len, i; @@ -567,15 +838,28 @@ char *get_proxy(char *url, struct pool *pool) quithere(1, "Failed to malloc rpc_proxy"); strcpy(pool->rpc_proxy, url + plen); + extract_sockaddr(pool->rpc_proxy, &pool->sockaddr_proxy_url, &pool->sockaddr_proxy_port); pool->rpc_proxytype = proxynames[i].proxytype; url = split + 1; break; } } -#endif return url; } +/* Adequate size s==len*2 + 1 must be alloced to use this variant */ +void __bin2hex(char *s, const unsigned char *p, size_t len) +{ + int i; + static const char hex[16] = {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f'}; + + for (i = 0; i < (int)len; i++) { + *s++ = hex[p[i] >> 4]; + *s++ = hex[p[i] & 0xF]; + } + *s++ = '\0'; +} + /* Returns a malloced array string of a binary value of arbitrary length. The * array is rounded up to a 4 byte size to appease architectures that need * aligned array sizes */ @@ -583,7 +867,6 @@ char *bin2hex(const unsigned char *p, size_t len) { ssize_t slen; char *s; - int i; slen = len * 2 + 1; if (slen % 4) @@ -592,40 +875,55 @@ char *bin2hex(const unsigned char *p, size_t len) if (unlikely(!s)) quithere(1, "Failed to calloc"); - for (i = 0; i < (int)len; i++) - sprintf(s + (i * 2), "%02x", (unsigned int)p[i]); + __bin2hex(s, p, len); return s; } +static const int hex2bin_tbl[256] = { + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, -1, -1, -1, -1, -1, -1, + -1, 10, 11, 12, 13, 14, 15, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, 10, 11, 12, 13, 14, 15, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, +}; + /* Does the reverse of bin2hex but does not allocate any ram */ bool hex2bin(unsigned char *p, const char *hexstr, size_t len) { + int nibble1, nibble2; + unsigned char idx; bool ret = false; while (*hexstr && len) { - char hex_byte[4]; - unsigned int v; - if (unlikely(!hexstr[1])) { applog(LOG_ERR, "hex2bin str truncated"); return ret; } - memset(hex_byte, 0, 4); - hex_byte[0] = hexstr[0]; - hex_byte[1] = hexstr[1]; + idx = *hexstr++; + nibble1 = hex2bin_tbl[idx]; + idx = *hexstr++; + nibble2 = hex2bin_tbl[idx]; - if (unlikely(sscanf(hex_byte, "%x", &v) != 1)) { - applog(LOG_INFO, "hex2bin sscanf '%s' failed", hex_byte); + if (unlikely((nibble1 < 0) || (nibble2 < 0))) { + applog(LOG_ERR, "hex2bin scan failed"); return ret; } - *p = (unsigned char) v; - - p++; - hexstr += 2; - len--; + *p++ = (((unsigned char)nibble1) << 4) | ((unsigned char)nibble2); + --len; } if (likely(len == 0 && *hexstr == 0)) @@ -633,23 +931,170 @@ bool hex2bin(unsigned char *p, const char *hexstr, size_t len) return ret; } +static bool _valid_hex(char *s, const char *file, const char *func, const int line) +{ + bool ret = false; + int i, len; + + if (unlikely(!s)) { + applog(LOG_ERR, "Null string passed to valid_hex from"IN_FMT_FFL, file, func, line); + return ret; + } + len = strlen(s); + for (i = 0; i < len; i++) { + unsigned char idx = s[i]; + + if (unlikely(hex2bin_tbl[idx] < 0)) { + applog(LOG_ERR, "Invalid char 0x%x passed to valid_hex from"IN_FMT_FFL, idx, file, func, line); + return ret; + } + } + ret = true; + return ret; +} + +#define valid_hex(s) _valid_hex(s, __FILE__, __func__, __LINE__) + +static bool _valid_ascii(char *s, const char *file, const char *func, const int line) +{ + bool ret = false; + int i, len; + + if (unlikely(!s)) { + applog(LOG_ERR, "Null string passed to valid_ascii from"IN_FMT_FFL, file, func, line); + return ret; + } + len = strlen(s); + if (unlikely(!len)) { + applog(LOG_ERR, "Zero length string passed to valid_ascii from"IN_FMT_FFL, file, func, line); + return ret; + } + for (i = 0; i < len; i++) { + unsigned char idx = s[i]; + + if (unlikely(idx < 32 || idx > 126)) { + applog(LOG_ERR, "Invalid char 0x%x passed to valid_ascii from"IN_FMT_FFL, idx, file, func, line); + return ret; + } + } + ret = true; + return ret; +} + +#define valid_ascii(s) _valid_ascii(s, __FILE__, __func__, __LINE__) + +static const int b58tobin_tbl[] = { + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, 0, 1, 2, 3, 4, 5, 6, 7, 8, -1, -1, -1, -1, -1, -1, + -1, 9, 10, 11, 12, 13, 14, 15, 16, -1, 17, 18, 19, 20, 21, -1, + 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, -1, -1, -1, -1, -1, + -1, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, -1, 44, 45, 46, + 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57 +}; + +/* b58bin should always be at least 25 bytes long and already checked to be + * valid. */ +void b58tobin(unsigned char *b58bin, const char *b58) +{ + uint32_t c, bin32[7]; + int len, i, j; + uint64_t t; + + memset(bin32, 0, 7 * sizeof(uint32_t)); + len = strlen(b58); + for (i = 0; i < len; i++) { + c = b58[i]; + c = b58tobin_tbl[c]; + for (j = 6; j >= 0; j--) { + t = ((uint64_t)bin32[j]) * 58 + c; + c = (t & 0x3f00000000ull) >> 32; + bin32[j] = t & 0xffffffffull; + } + } + *(b58bin++) = bin32[0] & 0xff; + for (i = 1; i < 7; i++) { + *((uint32_t *)b58bin) = htobe32(bin32[i]); + b58bin += sizeof(uint32_t); + } +} + +void address_to_pubkeyhash(unsigned char *pkh, const char *addr) +{ + unsigned char b58bin[25]; + + memset(b58bin, 0, 25); + b58tobin(b58bin, addr); + pkh[0] = 0x76; + pkh[1] = 0xa9; + pkh[2] = 0x14; + memcpy(&pkh[3], &b58bin[1], 20); + pkh[23] = 0x88; + pkh[24] = 0xac; +} + +/* For encoding nHeight into coinbase, return how many bytes were used */ +int ser_number(unsigned char *s, int32_t val) +{ + int32_t *i32 = (int32_t *)&s[1]; + int len; + + if (val < 128) + len = 1; + else if (val < 16512) + len = 2; + else if (val < 2113664) + len = 3; + else + len = 4; + *i32 = htole32(val); + s[0] = len++; + return len; +} + +/* For encoding variable length strings */ +unsigned char *ser_string(char *s, int *slen) +{ + size_t len = strlen(s); + unsigned char *ret; + + ret = malloc(1 + len + 8); // Leave room for largest size + if (unlikely(!ret)) + quit(1, "Failed to malloc ret in ser_string"); + if (len < 253) { + ret[0] = len; + memcpy(ret + 1, s, len); + *slen = len + 1; + } else if (len < 0x10000) { + uint16_t *u16 = (uint16_t *)&ret[1]; + + ret[0] = 253; + *u16 = htobe16(len); + memcpy(ret + 3, s, len); + *slen = len + 3; + } else { + /* size_t is only 32 bit on many platforms anyway */ + uint32_t *u32 = (uint32_t *)&ret[1]; + + ret[0] = 254; + *u32 = htobe32(len); + memcpy(ret + 5, s, len); + *slen = len + 5; + } + return ret; +} + bool fulltest(const unsigned char *hash, const unsigned char *target) { - unsigned char hash_swap[32], target_swap[32]; - uint32_t *hash32 = (uint32_t *) hash_swap; - uint32_t *target32 = (uint32_t *) target_swap; - char *hash_str, *target_str; + uint32_t *hash32 = (uint32_t *)hash; + uint32_t *target32 = (uint32_t *)target; bool rc = true; int i; - swap256(hash_swap, hash); - swap256(target_swap, target); - - for (i = 0; i < 32/4; i++) { - uint32_t h32tmp = htobe32(hash32[i]); - uint32_t t32tmp = htole32(target32[i]); - - target32[i] = swab32(target32[i]); /* for printing */ + for (i = 28 / 4; i >= 0; i--) { + uint32_t h32tmp = le32toh(hash32[i]); + uint32_t t32tmp = le32toh(target32[i]); if (h32tmp > t32tmp) { rc = false; @@ -662,6 +1107,11 @@ bool fulltest(const unsigned char *hash, const unsigned char *target) } if (opt_debug) { + unsigned char hash_swap[32], target_swap[32]; + char *hash_str, *target_str; + + swab256(hash_swap, hash); + swab256(target_swap, target); hash_str = bin2hex(hash_swap, 32); target_str = bin2hex(target_swap, 32); @@ -865,30 +1315,36 @@ void ms_to_timespec(struct timespec *spec, int64_t ms) spec->tv_nsec = tvdiv.rem * 1000000; } -void timeraddspec(struct timespec *a, const struct timespec *b) +void ms_to_timeval(struct timeval *val, int64_t ms) { - a->tv_sec += b->tv_sec; - a->tv_nsec += b->tv_nsec; - if (a->tv_nsec >= 1000000000) { - a->tv_nsec -= 1000000000; - a->tv_sec++; + lldiv_t tvdiv = lldiv(ms, 1000); + + val->tv_sec = tvdiv.quot; + val->tv_usec = tvdiv.rem * 1000; +} + +static void spec_nscheck(struct timespec *ts) +{ + while (ts->tv_nsec >= 1000000000) { + ts->tv_nsec -= 1000000000; + ts->tv_sec++; + } + while (ts->tv_nsec < 0) { + ts->tv_nsec += 1000000000; + ts->tv_sec--; } } -static int timespec_to_ms(struct timespec *ts) +void timeraddspec(struct timespec *a, const struct timespec *b) { - return ts->tv_sec * 1000 + ts->tv_nsec / 1000000; + a->tv_sec += b->tv_sec; + a->tv_nsec += b->tv_nsec; + spec_nscheck(a); } -/* Subtracts b from a and stores it in res. */ -void cgtimer_sub(cgtimer_t *a, cgtimer_t *b, cgtimer_t *res) +static int __maybe_unused timespec_to_ms(struct timespec *ts) { - res->tv_sec = a->tv_sec - b->tv_sec; - res->tv_nsec = a->tv_nsec - b->tv_nsec; - if (res->tv_nsec < 0) { - res->tv_nsec += 1000000000; - res->tv_sec--; - } + return ts->tv_sec * 1000 + ts->tv_nsec / 1000000; } /* Subtract b from a */ @@ -896,35 +1352,59 @@ static void __maybe_unused timersubspec(struct timespec *a, const struct timespe { a->tv_sec -= b->tv_sec; a->tv_nsec -= b->tv_nsec; - if (a->tv_nsec < 0) { - a->tv_nsec += 1000000000; - a->tv_sec--; - } + spec_nscheck(a); } -static void __maybe_unused cgsleep_spec(struct timespec *ts_diff, const struct timespec *ts_start) +char *Strcasestr(char *haystack, const char *needle) { - struct timespec now; + char *lowhay, *lowneedle, *ret; + int hlen, nlen, i, ofs; - timeraddspec(ts_diff, ts_start); - cgtimer_time(&now); - timersubspec(ts_diff, &now); - if (unlikely(ts_diff->tv_sec < 0)) - return; - nanosleep(ts_diff, NULL); + if (unlikely(!haystack || !needle)) + return NULL; + hlen = strlen(haystack); + nlen = strlen(needle); + if (!hlen || !nlen) + return NULL; + lowhay = alloca(hlen); + lowneedle = alloca(nlen); + for (i = 0; i < hlen; i++) + lowhay[i] = tolower(haystack[i]); + for (i = 0; i < nlen; i++) + lowneedle[i] = tolower(needle[i]); + ret = strstr(lowhay, lowneedle); + if (!ret) + return ret; + ofs = ret - lowhay; + return haystack + ofs; } -int cgtimer_to_ms(cgtimer_t *cgt) +char *Strsep(char **stringp, const char *delim) { - return timespec_to_ms(cgt); + char *ret = *stringp; + char *p; + + p = (ret != NULL) ? strpbrk(ret, delim) : NULL; + + if (p == NULL) + *stringp = NULL; + else { + *p = '\0'; + *stringp = p + 1; + } + + return ret; } -/* These are cgminer specific sleep functions that use an absolute nanosecond - * resolution timer to avoid poor usleep accuracy and overruns. */ #ifdef WIN32 +/* Mingw32 has no strsep so create our own custom one */ + /* Windows start time is since 1601 LOL so convert it to unix epoch 1970. */ #define EPOCHFILETIME (116444736000000000LL) +/* These are cgminer specific sleep functions that use an absolute nanosecond + * resolution timer to avoid poor usleep accuracy and overruns. */ + /* Return the system time as an lldiv_t in decimicroseconds. */ static void decius_time(lldiv_t *lidiv) { @@ -952,19 +1432,27 @@ void cgtime(struct timeval *tv) tv->tv_usec = lidiv.rem / 10; } -void cgtimer_time(cgtimer_t *ts_start) -{ - lldiv_t lidiv;; - - decius_time(&lidiv); - ts_start->tv_sec = lidiv.quot; - ts_start->tv_nsec = lidiv.quot * 100; -} #else /* WIN32 */ void cgtime(struct timeval *tv) { gettimeofday(tv, NULL); } + +int cgtimer_to_ms(cgtimer_t *cgt) +{ + return timespec_to_ms(cgt); +} + +/* Subtracts b from a and stores it in res. */ +void cgtimer_sub(cgtimer_t *a, cgtimer_t *b, cgtimer_t *res) +{ + res->tv_sec = a->tv_sec - b->tv_sec; + res->tv_nsec = a->tv_nsec - b->tv_nsec; + if (res->tv_nsec < 0) { + res->tv_nsec += 1000000000; + res->tv_sec--; + } +} #endif /* WIN32 */ #ifdef CLOCK_MONOTONIC /* Essentially just linux */ @@ -1027,6 +1515,88 @@ void cgtimer_time(cgtimer_t *ts_start) ts_start->tv_nsec = tv->tv_usec * 1000; } #endif /* __MACH__ */ + +#ifdef WIN32 +/* For windows we use the SystemTime stored as a LARGE_INTEGER as the cgtimer_t + * typedef, allowing us to have sub-microsecond resolution for times, do simple + * arithmetic for timer calculations, and use windows' own hTimers to get + * accurate absolute timeouts. */ +int cgtimer_to_ms(cgtimer_t *cgt) +{ + return (int)(cgt->QuadPart / 10000LL); +} + +/* Subtracts b from a and stores it in res. */ +void cgtimer_sub(cgtimer_t *a, cgtimer_t *b, cgtimer_t *res) +{ + res->QuadPart = a->QuadPart - b->QuadPart; +} + +/* Note that cgtimer time is NOT offset by the unix epoch since we use absolute + * timeouts with hTimers. */ +void cgtimer_time(cgtimer_t *ts_start) +{ + FILETIME ft; + + GetSystemTimeAsFileTime(&ft); + ts_start->LowPart = ft.dwLowDateTime; + ts_start->HighPart = ft.dwHighDateTime; +} + +static void liSleep(LARGE_INTEGER *li, int timeout) +{ + HANDLE hTimer; + DWORD ret; + + if (unlikely(timeout <= 0)) + return; + + hTimer = CreateWaitableTimer(NULL, TRUE, NULL); + if (unlikely(!hTimer)) + quit(1, "Failed to create hTimer in liSleep"); + ret = SetWaitableTimer(hTimer, li, 0, NULL, NULL, 0); + if (unlikely(!ret)) + quit(1, "Failed to SetWaitableTimer in liSleep"); + /* We still use a timeout as a sanity check in case the system time + * is changed while we're running */ + ret = WaitForSingleObject(hTimer, timeout); + if (unlikely(ret != WAIT_OBJECT_0 && ret != WAIT_TIMEOUT)) + quit(1, "Failed to WaitForSingleObject in liSleep"); + CloseHandle(hTimer); +} + +void cgsleep_ms_r(cgtimer_t *ts_start, int ms) +{ + LARGE_INTEGER li; + + li.QuadPart = ts_start->QuadPart + (int64_t)ms * 10000LL; + liSleep(&li, ms); +} + +void cgsleep_us_r(cgtimer_t *ts_start, int64_t us) +{ + LARGE_INTEGER li; + int ms; + + li.QuadPart = ts_start->QuadPart + us * 10LL; + ms = us / 1000; + if (!ms) + ms = 1; + liSleep(&li, ms); +} +#else /* WIN32 */ +static void cgsleep_spec(struct timespec *ts_diff, const struct timespec *ts_start) +{ + struct timespec now; + + timeraddspec(ts_diff, ts_start); + cgtimer_time(&now); + timersubspec(ts_diff, &now); + if (unlikely(ts_diff->tv_sec < 0)) + return; + nanosleep(ts_diff, NULL); +} + void cgsleep_ms_r(cgtimer_t *ts_start, int ms) { struct timespec ts_diff; @@ -1042,6 +1612,7 @@ void cgsleep_us_r(cgtimer_t *ts_start, int64_t us) us_to_timespec(&ts_diff, us); cgsleep_spec(&ts_diff, ts_start); } +#endif /* WIN32 */ #endif /* CLOCK_MONOTONIC */ void cgsleep_ms(int ms) @@ -1063,7 +1634,20 @@ void cgsleep_us(int64_t us) /* Returns the microseconds difference between end and start times as a double */ double us_tdiff(struct timeval *end, struct timeval *start) { - return end->tv_sec * 1000000 + end->tv_usec - start->tv_sec * 1000000 - start->tv_usec; + /* Sanity check. We should only be using this for small differences so + * limit the max to 60 seconds. */ + if (unlikely(end->tv_sec - start->tv_sec > 60)) + return 60000000; + return (end->tv_sec - start->tv_sec) * 1000000 + (end->tv_usec - start->tv_usec); +} + +/* Returns the milliseconds difference between end and start times */ +int ms_tdiff(struct timeval *end, struct timeval *start) +{ + /* Like us_tdiff, limit to 1 hour. */ + if (unlikely(end->tv_sec - start->tv_sec > 3600)) + return 3600000; + return (end->tv_sec - start->tv_sec) * 1000 + (end->tv_usec - start->tv_usec) / 1000; } /* Returns the seconds difference between end and start times as a double */ @@ -1072,13 +1656,31 @@ double tdiff(struct timeval *end, struct timeval *start) return end->tv_sec - start->tv_sec + (end->tv_usec - start->tv_usec) / 1000000.0; } -bool extract_sockaddr(struct pool *pool, char *url) +void check_extranonce_option(struct pool *pool, char * url) +{ + char extra_op[16],*extra_op_loc; + extra_op_loc = strstr(url,"#"); + if(extra_op_loc && !pool->extranonce_subscribe) + { + strcpy(extra_op, extra_op_loc); + *extra_op_loc = '\0'; + if(!strcmp(extra_op,"#xnsub")) + { + pool->extranonce_subscribe = true; + applog(LOG_DEBUG, "Pool %d extranonce subscribing enabled.",pool->pool_no); + return; + } + } + return; +} + +bool extract_sockaddr(char *url, char **sockaddr_url, char **sockaddr_port) { char *url_begin, *url_end, *ipv6_begin, *ipv6_end, *port_start = NULL; char url_address[256], port[6]; int url_len, port_len = 0; - pool->sockaddr_url = url; + *sockaddr_url = url; url_begin = strstr(url, "//"); if (!url_begin) url_begin = url; @@ -1104,15 +1706,26 @@ bool extract_sockaddr(struct pool *pool, char *url) if (url_len < 1) return false; - sprintf(url_address, "%.*s", url_len, url_begin); + /* Get rid of the [] */ + if (ipv6_begin && ipv6_end && ipv6_end > ipv6_begin) { + url_len -= 2; + url_begin++; + } + + snprintf(url_address, 254, "%.*s", url_len, url_begin); + + if (port_len) { + char *slash; - if (port_len) snprintf(port, 6, "%.*s", port_len, port_start); - else + slash = strchr(port, '/'); + if (slash) + *slash = '\0'; + } else strcpy(port, "80"); - pool->stratum_port = strdup(port); - pool->sockaddr_url = strdup(url_address); + *sockaddr_port = strdup(port); + *sockaddr_url = strdup(url_address); return true; } @@ -1138,11 +1751,14 @@ static enum send_ret __stratum_send(struct pool *pool, char *s, ssize_t len) struct timeval timeout = {1, 0}; ssize_t sent; fd_set wd; - +retry: FD_ZERO(&wd); FD_SET(sock, &wd); - if (select(sock + 1, NULL, &wd, NULL, &timeout) < 1) + if (select(sock + 1, NULL, &wd, NULL, &timeout) < 1) { + if (interrupted()) + goto retry; return SEND_SELECTFAIL; + } #ifdef __APPLE__ sent = send(pool->sock, s + ssent, len, SO_NOSIGPIPE); #elif WIN32 @@ -1225,7 +1841,8 @@ bool sock_full(struct pool *pool) static void clear_sockbuf(struct pool *pool) { - strcpy(pool->sockbuf, ""); + if (likely(pool->sockbuf)) + strcpy(pool->sockbuf, ""); } static void clear_sock(struct pool *pool) @@ -1244,6 +1861,18 @@ static void clear_sock(struct pool *pool) clear_sockbuf(pool); } +/* Realloc memory to new size and zero any extra memory added */ +void _recalloc(void **ptr, size_t old, size_t new, const char *file, const char *func, const int line) +{ + if (new == old) + return; + *ptr = realloc(*ptr, new); + if (unlikely(!*ptr)) + quitfrom(1, file, func, line, "Failed to realloc"); + if (new > old) + memset(*ptr + old, 0, new - old); +} + /* Make sure the pool sockbuf is large enough to cope with any coinbase size * by reallocing it to a large enough size rounded up to a multiple of RBUFSIZE * and zeroing the new memory */ @@ -1367,14 +1996,14 @@ static char *json_array_string(json_t *val, unsigned int entry) return NULL; } -static char *blank_merkel = "0000000000000000000000000000000000000000000000000000000000000000"; +static char *blank_merkle = "0000000000000000000000000000000000000000000000000000000000000000"; static bool parse_notify(struct pool *pool, json_t *val) { char *job_id, *prev_hash, *coinbase1, *coinbase2, *bbversion, *nbit, - *ntime, *header; + *ntime, header[228]; + unsigned char *cb1 = NULL, *cb2 = NULL; size_t cb1_len, cb2_len, alloc_len; - unsigned char *cb1, *cb2; bool clean, ret = false; int merkles, i; json_t *arr; @@ -1386,51 +2015,38 @@ static bool parse_notify(struct pool *pool, json_t *val) merkles = json_array_size(arr); job_id = json_array_string(val, 0); - prev_hash = json_array_string(val, 1); + prev_hash = __json_array_string(val, 1); coinbase1 = json_array_string(val, 2); coinbase2 = json_array_string(val, 3); - bbversion = json_array_string(val, 5); - nbit = json_array_string(val, 6); - ntime = json_array_string(val, 7); + bbversion = __json_array_string(val, 5); + nbit = __json_array_string(val, 6); + ntime = __json_array_string(val, 7); clean = json_is_true(json_array_get(val, 8)); - if (!job_id || !prev_hash || !coinbase1 || !coinbase2 || !bbversion || !nbit || !ntime) { + if (!valid_ascii(job_id) || !valid_hex(prev_hash) || !valid_hex(coinbase1) || + !valid_hex(coinbase2) || !valid_hex(bbversion) || !valid_hex(nbit) || + !valid_hex(ntime)) { /* Annoying but we must not leak memory */ - if (job_id) - free(job_id); - if (prev_hash) - free(prev_hash); - if (coinbase1) - free(coinbase1); - if (coinbase2) - free(coinbase2); - if (bbversion) - free(bbversion); - if (nbit) - free(nbit); - if (ntime) - free(ntime); + free(job_id); + free(coinbase1); + free(coinbase2); goto out; } cg_wlock(&pool->data_lock); free(pool->swork.job_id); - free(pool->swork.prev_hash); - free(pool->swork.bbversion); - free(pool->swork.nbit); - free(pool->swork.ntime); pool->swork.job_id = job_id; - pool->swork.prev_hash = prev_hash; + snprintf(pool->prev_hash, 65, "%s", prev_hash); cb1_len = strlen(coinbase1) / 2; cb2_len = strlen(coinbase2) / 2; - pool->swork.bbversion = bbversion; - pool->swork.nbit = nbit; - pool->swork.ntime = ntime; + snprintf(pool->bbversion, 9, "%s", bbversion); + snprintf(pool->nbit, 9, "%s", nbit); + snprintf(pool->ntime, 9, "%s", ntime); pool->swork.clean = clean; - alloc_len = pool->swork.cb_len = cb1_len + pool->n1_len + pool->n2size + cb2_len; + alloc_len = pool->coinbase_len = cb1_len + pool->n1_len + pool->n2size + cb2_len; pool->nonce2_offset = cb1_len + pool->n1_len; - for (i = 0; i < pool->swork.merkles; i++) + for (i = 0; i < pool->merkles; i++) free(pool->swork.merkle_bin[i]); if (merkles) { pool->swork.merkle_bin = realloc(pool->swork.merkle_bin, @@ -1441,45 +2057,55 @@ static bool parse_notify(struct pool *pool, json_t *val) pool->swork.merkle_bin[i] = malloc(32); if (unlikely(!pool->swork.merkle_bin[i])) quit(1, "Failed to malloc pool swork merkle_bin"); - hex2bin(pool->swork.merkle_bin[i], merkle, 32); + if (opt_protocol) + applog(LOG_DEBUG, "merkle %d: %s", i, merkle); + ret = hex2bin(pool->swork.merkle_bin[i], merkle, 32); free(merkle); + if (unlikely(!ret)) { + applog(LOG_ERR, "Failed to convert merkle to merkle_bin in parse_notify"); + goto out_unlock; + } } } - pool->swork.merkles = merkles; + pool->merkles = merkles; if (clean) pool->nonce2 = 0; - pool->merkle_offset = strlen(pool->swork.bbversion) + - strlen(pool->swork.prev_hash); - pool->swork.header_len = pool->merkle_offset + +#if 0 + header_len = strlen(pool->bbversion) + + strlen(pool->prev_hash); /* merkle_hash */ 32 + - strlen(pool->swork.ntime) + - strlen(pool->swork.nbit) + + strlen(pool->ntime) + + strlen(pool->nbit) + /* nonce */ 8 + /* workpadding */ 96; - pool->merkle_offset /= 2; - pool->swork.header_len = pool->swork.header_len * 2 + 1; - align_len(&pool->swork.header_len); - header = alloca(pool->swork.header_len); - snprintf(header, pool->swork.header_len, +#endif + snprintf(header, 225, "%s%s%s%s%s%s%s", - pool->swork.bbversion, - pool->swork.prev_hash, - blank_merkel, - pool->swork.ntime, - pool->swork.nbit, + pool->bbversion, + pool->prev_hash, + blank_merkle, + pool->ntime, + pool->nbit, "00000000", /* nonce */ workpadding); - if (unlikely(!hex2bin(pool->header_bin, header, 128))) - quit(1, "Failed to convert header to header_bin in parse_notify"); - - cb1 = calloc(cb1_len, 1); - if (unlikely(!cb1)) - quithere(1, "Failed to calloc cb1 in parse_notify"); - hex2bin(cb1, coinbase1, cb1_len); - cb2 = calloc(cb2_len, 1); - if (unlikely(!cb2)) - quithere(1, "Failed to calloc cb2 in parse_notify"); - hex2bin(cb2, coinbase2, cb2_len); + ret = hex2bin(pool->header_bin, header, 112); + if (unlikely(!ret)) { + applog(LOG_ERR, "Failed to convert header to header_bin in parse_notify"); + goto out_unlock; + } + + cb1 = alloca(cb1_len); + ret = hex2bin(cb1, coinbase1, cb1_len); + if (unlikely(!ret)) { + applog(LOG_ERR, "Failed to convert cb1 to cb1_bin in parse_notify"); + goto out_unlock; + } + cb2 = alloca(cb2_len); + ret = hex2bin(cb2, coinbase2, cb2_len); + if (unlikely(!ret)) { + applog(LOG_ERR, "Failed to convert cb2 to cb2_bin in parse_notify"); + goto out_unlock; + } free(pool->coinbase); align_len(&alloc_len); pool->coinbase = calloc(alloc_len, 1); @@ -1488,6 +2114,13 @@ static bool parse_notify(struct pool *pool, json_t *val) memcpy(pool->coinbase, cb1, cb1_len); memcpy(pool->coinbase + cb1_len, pool->nonce1bin, pool->n1_len); memcpy(pool->coinbase + cb1_len + pool->n1_len + pool->n2size, cb2, cb2_len); + if (opt_debug) { + char *cb = bin2hex(pool->coinbase, pool->coinbase_len); + + applog(LOG_DEBUG, "Pool %d coinbase %s", pool->pool_no, cb); + free(cb); + } +out_unlock: cg_wunlock(&pool->data_lock); if (opt_protocol) { @@ -1502,13 +2135,12 @@ static bool parse_notify(struct pool *pool, json_t *val) } free(coinbase1); free(coinbase2); - free(cb1); - free(cb2); /* A notify message is the closest stratum gets to a getwork */ pool->getwork_requested++; total_getworks++; - ret = true; + if (pool == current_pool()) + opt_work_update = true; out: return ret; } @@ -1522,8 +2154,8 @@ static bool parse_diff(struct pool *pool, json_t *val) return false; cg_wlock(&pool->data_lock); - old_diff = pool->swork.diff; - pool->swork.diff = diff; + old_diff = pool->sdiff; + pool->sdiff = diff; cg_wunlock(&pool->data_lock); if (old_diff != diff) { @@ -1533,7 +2165,7 @@ static bool parse_diff(struct pool *pool, json_t *val) applog(LOG_NOTICE, "Pool %d difficulty changed to %d", pool->pool_no, idiff); else - applog(LOG_NOTICE, "Pool %d difficulty changed to %f", + applog(LOG_NOTICE, "Pool %d difficulty changed to %.1f", pool->pool_no, diff); } else applog(LOG_DEBUG, "Pool %d difficulty set to %f", pool->pool_no, @@ -1542,41 +2174,115 @@ static bool parse_diff(struct pool *pool, json_t *val) return true; } +static bool parse_extranonce(struct pool *pool, json_t *val) +{ + int n2size; + char* nonce1; + + nonce1 = json_array_string(val, 0); + if (!valid_hex(nonce1)) { + applog(LOG_INFO, "Failed to get valid nonce1 in parse_extranonce"); + goto out; + } + n2size = json_integer_value(json_array_get(val, 1)); + if (n2size < 2 || n2size > 16) { + applog(LOG_INFO, "Failed to get valid n2size in parse_extranonce"); + free(nonce1); + goto out; + } + + cg_wlock(&pool->data_lock); + pool->nonce1 = nonce1; + pool->n1_len = strlen(nonce1) / 2; + free(pool->nonce1bin); + pool->nonce1bin = calloc(pool->n1_len, 1); + if (unlikely(!pool->nonce1bin)) + quithere(1, "Failed to calloc pool->nonce1bin"); + hex2bin(pool->nonce1bin, pool->nonce1, pool->n1_len); + pool->n2size = n2size; + applog(LOG_NOTICE, "Pool %d confirmed mining.extranonce.subscribe with extranonce1 %s extran2size %d", + pool->pool_no, pool->nonce1, pool->n2size); + cg_wunlock(&pool->data_lock); + return true; +out: + return false; +} + +static void __suspend_stratum(struct pool *pool) +{ + clear_sockbuf(pool); + pool->stratum_active = pool->stratum_notify = false; + if (pool->sock) + CLOSESOCKET(pool->sock); + pool->sock = 0; +} + static bool parse_reconnect(struct pool *pool, json_t *val) { + char *sockaddr_url, *stratum_port, *tmp; char *url, *port, address[256]; memset(address, 0, 255); url = (char *)json_string_value(json_array_get(val, 0)); if (!url) url = pool->sockaddr_url; + else { + char *dot_pool, *dot_reconnect; + dot_pool = strchr(pool->sockaddr_url, '.'); + if (!dot_pool) { + applog(LOG_ERR, "Denied stratum reconnect request for pool without domain '%s'", + pool->sockaddr_url); + return false; + } + dot_reconnect = strchr(url, '.'); + if (!dot_reconnect) { + applog(LOG_ERR, "Denied stratum reconnect request to url without domain '%s'", + url); + return false; + } + if (strcmp(dot_pool, dot_reconnect)) { + applog(LOG_ERR, "Denied stratum reconnect request to non-matching domain url '%s'", + pool->sockaddr_url); + return false; + } + } port = (char *)json_string_value(json_array_get(val, 1)); if (!port) port = pool->stratum_port; - sprintf(address, "%s:%s", url, port); + snprintf(address, 254, "%s:%s", url, port); - if (!extract_sockaddr(pool, address)) + if (!extract_sockaddr(address, &sockaddr_url, &stratum_port)) return false; - pool->stratum_url = pool->sockaddr_url; + applog(LOG_WARNING, "Stratum reconnect requested from pool %d to %s", pool->pool_no, address); - applog(LOG_NOTICE, "Reconnect requested from pool %d to %s", pool->pool_no, address); + clear_pool_work(pool); - if (!restart_stratum(pool)) - return false; + mutex_lock(&pool->stratum_lock); + __suspend_stratum(pool); + tmp = pool->sockaddr_url; + pool->sockaddr_url = sockaddr_url; + pool->stratum_url = pool->sockaddr_url; + free(tmp); + tmp = pool->stratum_port; + pool->stratum_port = stratum_port; + free(tmp); + mutex_unlock(&pool->stratum_lock); - return true; + return restart_stratum(pool); } static bool send_version(struct pool *pool, json_t *val) { + json_t *id_val = json_object_get(val, "id"); char s[RBUFSIZE]; - int id = json_integer_value(json_object_get(val, "id")); + int id; - if (!id) + if (!id_val) return false; + id = json_integer_value(json_object_get(val, "id")); sprintf(s, "{\"id\": %d, \"result\": \""PACKAGE"/"VERSION"\", \"error\": null}", id); if (!stratum_send(pool, s, strlen(s))) @@ -1585,6 +2291,23 @@ static bool send_version(struct pool *pool, json_t *val) return true; } +static bool send_pong(struct pool *pool, json_t *val) +{ + json_t *id_val = json_object_get(val, "id"); + char s[RBUFSIZE]; + int id; + + if (!id_val) + return false; + id = json_integer_value(json_object_get(val, "id")); + + sprintf(s, "{\"id\": %d, \"result\": \"pong\", \"error\": null}", id); + if (!stratum_send(pool, s, strlen(s))) + return false; + + return true; +} + static bool show_message(struct pool *pool, json_t *val) { char *msg; @@ -1606,17 +2329,17 @@ bool parse_method(struct pool *pool, char *s) char *buf; if (!s) - return ret; + goto out; val = JSON_LOADS(s, &err); if (!val) { applog(LOG_INFO, "JSON decode failed(%d): %s", err.line, err.text); - return ret; + goto out; } method = json_object_get(val, "method"); if (!method) - return ret; + goto out_decref; err_val = json_object_get(val, "error"); params = json_object_get(val, "params"); @@ -1629,43 +2352,56 @@ bool parse_method(struct pool *pool, char *s) ss = strdup("(unknown reason)"); applog(LOG_INFO, "JSON-RPC method decode failed: %s", ss); - free(ss); - - return ret; + goto out_decref; } buf = (char *)json_string_value(method); if (!buf) - return ret; + goto out_decref; if (!strncasecmp(buf, "mining.notify", 13)) { if (parse_notify(pool, params)) pool->stratum_notify = ret = true; else pool->stratum_notify = ret = false; - return ret; + goto out_decref; } - if (!strncasecmp(buf, "mining.set_difficulty", 21) && parse_diff(pool, params)) { - ret = true; - return ret; + + if(!strncasecmp(buf, "mining.set_extranonce", 21)) { + ret = parse_extranonce(pool, params); + goto out_decref; } - if (!strncasecmp(buf, "client.reconnect", 16) && parse_reconnect(pool, params)) { - ret = true; - return ret; + if (!strncasecmp(buf, "mining.set_difficulty", 21)) { + ret = parse_diff(pool, params); + goto out_decref; } - if (!strncasecmp(buf, "client.get_version", 18) && send_version(pool, val)) { - ret = true; - return ret; + if (!strncasecmp(buf, "client.reconnect", 16)) { + ret = parse_reconnect(pool, params); + goto out_decref; } - if (!strncasecmp(buf, "client.show_message", 19) && show_message(pool, params)) { - ret = true; - return ret; + if (!strncasecmp(buf, "client.get_version", 18)) { + ret = send_version(pool, val); + goto out_decref; } + + if (!strncasecmp(buf, "client.show_message", 19)) { + ret = show_message(pool, params); + goto out_decref; + } + + if (!strncasecmp(buf, "mining.ping", 11)) { + applog(LOG_INFO, "Pool %d ping", pool->pool_no); + ret = send_pong(pool, val); + goto out_decref; + } +out_decref: + json_decref(val); +out: return ret; } @@ -1705,22 +2441,269 @@ bool auth_stratum(struct pool *pool) ss = json_dumps(err_val, JSON_INDENT(3)); else ss = strdup("(unknown reason)"); - applog(LOG_WARNING, "pool %d JSON stratum auth failed: %s", pool->pool_no, ss); + applog(LOG_INFO, "pool %d JSON stratum auth failed: %s", pool->pool_no, ss); free(ss); - return ret; + suspend_stratum(pool); + + goto out; } ret = true; applog(LOG_INFO, "Stratum authorisation success for pool %d", pool->pool_no); pool->probed = true; successful_connect = true; + + if (opt_suggest_diff) { + sprintf(s, "{\"id\": %d, \"method\": \"mining.suggest_difficulty\", \"params\": [%d]}", + swork_id++, opt_suggest_diff); + stratum_send(pool, s, strlen(s)); + } +out: + json_decref(val); return ret; } +static int recv_byte(int sockd) +{ + char c; + + if (recv(sockd, &c, 1, 0) != -1) + return c; + + return -1; +} + +static bool http_negotiate(struct pool *pool, int sockd, bool http0) +{ + char buf[1024]; + int i, len; + + if (http0) { + snprintf(buf, 1024, "CONNECT %s:%s HTTP/1.0\r\n\r\n", + pool->sockaddr_url, pool->stratum_port); + } else { + snprintf(buf, 1024, "CONNECT %s:%s HTTP/1.1\r\nHost: %s:%s\r\n\r\n", + pool->sockaddr_url, pool->stratum_port, pool->sockaddr_url, + pool->stratum_port); + } + applog(LOG_DEBUG, "Sending proxy %s:%s - %s", + pool->sockaddr_proxy_url, pool->sockaddr_proxy_port, buf); + send(sockd, buf, strlen(buf), 0); + len = recv(sockd, buf, 12, 0); + if (len <= 0) { + applog(LOG_WARNING, "Couldn't read from proxy %s:%s after sending CONNECT", + pool->sockaddr_proxy_url, pool->sockaddr_proxy_port); + return false; + } + buf[len] = '\0'; + applog(LOG_DEBUG, "Received from proxy %s:%s - %s", + pool->sockaddr_proxy_url, pool->sockaddr_proxy_port, buf); + if (strcmp(buf, "HTTP/1.1 200") && strcmp(buf, "HTTP/1.0 200")) { + applog(LOG_WARNING, "HTTP Error from proxy %s:%s - %s", + pool->sockaddr_proxy_url, pool->sockaddr_proxy_port, buf); + return false; + } + + /* Ignore unwanted headers till we get desired response */ + for (i = 0; i < 4; i++) { + buf[i] = recv_byte(sockd); + if (buf[i] == (char)-1) { + applog(LOG_WARNING, "Couldn't read HTTP byte from proxy %s:%s", + pool->sockaddr_proxy_url, pool->sockaddr_proxy_port); + return false; + } + } + while (strncmp(buf, "\r\n\r\n", 4)) { + for (i = 0; i < 3; i++) + buf[i] = buf[i + 1]; + buf[3] = recv_byte(sockd); + if (buf[3] == (char)-1) { + applog(LOG_WARNING, "Couldn't read HTTP byte from proxy %s:%s", + pool->sockaddr_proxy_url, pool->sockaddr_proxy_port); + return false; + } + } + + applog(LOG_DEBUG, "Success negotiating with %s:%s HTTP proxy", + pool->sockaddr_proxy_url, pool->sockaddr_proxy_port); + return true; +} + +static bool socks5_negotiate(struct pool *pool, int sockd) +{ + unsigned char atyp, uclen; + unsigned short port; + char buf[515]; + int i, len; + + buf[0] = 0x05; + buf[1] = 0x01; + buf[2] = 0x00; + applog(LOG_DEBUG, "Attempting to negotiate with %s:%s SOCKS5 proxy", + pool->sockaddr_proxy_url, pool->sockaddr_proxy_port ); + send(sockd, buf, 3, 0); + if (recv_byte(sockd) != 0x05 || recv_byte(sockd) != buf[2]) { + applog(LOG_WARNING, "Bad response from %s:%s SOCKS5 server", + pool->sockaddr_proxy_url, pool->sockaddr_proxy_port ); + return false; + } + + buf[0] = 0x05; + buf[1] = 0x01; + buf[2] = 0x00; + buf[3] = 0x03; + len = (strlen(pool->sockaddr_url)); + if (len > 255) + len = 255; + uclen = len; + buf[4] = (uclen & 0xff); + memcpy(buf + 5, pool->sockaddr_url, len); + port = atoi(pool->stratum_port); + buf[5 + len] = (port >> 8); + buf[6 + len] = (port & 0xff); + send(sockd, buf, (7 + len), 0); + if (recv_byte(sockd) != 0x05 || recv_byte(sockd) != 0x00) { + applog(LOG_WARNING, "Bad response from %s:%s SOCKS5 server", + pool->sockaddr_proxy_url, pool->sockaddr_proxy_port ); + return false; + } + + recv_byte(sockd); + atyp = recv_byte(sockd); + if (atyp == 0x01) { + for (i = 0; i < 4; i++) + recv_byte(sockd); + } else if (atyp == 0x03) { + len = recv_byte(sockd); + for (i = 0; i < len; i++) + recv_byte(sockd); + } else { + applog(LOG_WARNING, "Bad response from %s:%s SOCKS5 server", + pool->sockaddr_proxy_url, pool->sockaddr_proxy_port ); + return false; + } + for (i = 0; i < 2; i++) + recv_byte(sockd); + + applog(LOG_DEBUG, "Success negotiating with %s:%s SOCKS5 proxy", + pool->sockaddr_proxy_url, pool->sockaddr_proxy_port); + return true; +} + +static bool socks4_negotiate(struct pool *pool, int sockd, bool socks4a) +{ + unsigned short port; + in_addr_t inp; + char buf[515]; + int i, len; + + buf[0] = 0x04; + buf[1] = 0x01; + port = atoi(pool->stratum_port); + buf[2] = port >> 8; + buf[3] = port & 0xff; + sprintf(&buf[8], "CGMINER"); + + /* See if we've been given an IP address directly to avoid needing to + * resolve it. */ + inp = inet_addr(pool->sockaddr_url); + inp = ntohl(inp); + if ((int)inp != -1) + socks4a = false; + else { + /* Try to extract the IP address ourselves first */ + struct addrinfo servinfobase, *servinfo, hints; + + servinfo = &servinfobase; + memset(&hints, 0, sizeof(struct addrinfo)); + hints.ai_family = AF_INET; /* IPV4 only */ + if (!getaddrinfo(pool->sockaddr_url, NULL, &hints, &servinfo)) { + struct sockaddr_in *saddr_in = (struct sockaddr_in *)servinfo->ai_addr; + + inp = ntohl(saddr_in->sin_addr.s_addr); + socks4a = false; + freeaddrinfo(servinfo); + } + } + + if (!socks4a) { + if ((int)inp == -1) { + applog(LOG_WARNING, "Invalid IP address specified for socks4 proxy: %s", + pool->sockaddr_url); + return false; + } + buf[4] = (inp >> 24) & 0xFF; + buf[5] = (inp >> 16) & 0xFF; + buf[6] = (inp >> 8) & 0xFF; + buf[7] = (inp >> 0) & 0xFF; + send(sockd, buf, 16, 0); + } else { + /* This appears to not be working but hopefully most will be + * able to resolve IP addresses themselves. */ + buf[4] = 0; + buf[5] = 0; + buf[6] = 0; + buf[7] = 1; + len = strlen(pool->sockaddr_url); + if (len > 255) + len = 255; + memcpy(&buf[16], pool->sockaddr_url, len); + len += 16; + buf[len++] = '\0'; + send(sockd, buf, len, 0); + } + + if (recv_byte(sockd) != 0x00 || recv_byte(sockd) != 0x5a) { + applog(LOG_WARNING, "Bad response from %s:%s SOCKS4 server", + pool->sockaddr_proxy_url, pool->sockaddr_proxy_port); + return false; + } + + for (i = 0; i < 6; i++) + recv_byte(sockd); + + return true; +} + +static void noblock_socket(SOCKETTYPE fd) +{ +#ifndef WIN32 + int flags = fcntl(fd, F_GETFL, 0); + + fcntl(fd, F_SETFL, O_NONBLOCK | flags); +#else + u_long flags = 1; + + ioctlsocket(fd, FIONBIO, &flags); +#endif +} + +static void block_socket(SOCKETTYPE fd) +{ +#ifndef WIN32 + int flags = fcntl(fd, F_GETFL, 0); + + fcntl(fd, F_SETFL, flags & ~O_NONBLOCK); +#else + u_long flags = 0; + + ioctlsocket(fd, FIONBIO, &flags); +#endif +} + +static bool sock_connecting(void) +{ +#ifndef WIN32 + return errno == EINPROGRESS; +#else + return WSAGetLastError() == WSAEWOULDBLOCK; +#endif +} static bool setup_stratum_socket(struct pool *pool) { - struct addrinfo servinfobase, *servinfo, *hints, pbase, *p; + struct addrinfo *servinfo, hints, *p; + char *sockaddr_url, *sockaddr_port; int sockd; mutex_lock(&pool->stratum_lock); @@ -1730,20 +2713,31 @@ static bool setup_stratum_socket(struct pool *pool) pool->sock = 0; mutex_unlock(&pool->stratum_lock); - hints = &pool->stratum_hints; - memset(hints, 0, sizeof(struct addrinfo)); - hints->ai_family = AF_UNSPEC; - hints->ai_socktype = SOCK_STREAM; - servinfo = &servinfobase; - p = &pbase; - if (getaddrinfo(pool->sockaddr_url, pool->stratum_port, hints, &servinfo) != 0) { + memset(&hints, 0, sizeof(struct addrinfo)); + hints.ai_family = AF_UNSPEC; + hints.ai_socktype = SOCK_STREAM; + + if (!pool->rpc_proxy && opt_socks_proxy) { + pool->rpc_proxy = opt_socks_proxy; + extract_sockaddr(pool->rpc_proxy, &pool->sockaddr_proxy_url, &pool->sockaddr_proxy_port); + pool->rpc_proxytype = PROXY_SOCKS5; + } + + if (pool->rpc_proxy) { + sockaddr_url = pool->sockaddr_proxy_url; + sockaddr_port = pool->sockaddr_proxy_port; + } else { + sockaddr_url = pool->sockaddr_url; + sockaddr_port = pool->stratum_port; + } + if (getaddrinfo(sockaddr_url, sockaddr_port, &hints, &servinfo) != 0) { if (!pool->probed) { applog(LOG_WARNING, "Failed to resolve (?wrong URL) %s:%s", - pool->sockaddr_url, pool->stratum_port); + sockaddr_url, sockaddr_port); pool->probed = true; } else { applog(LOG_INFO, "Failed to getaddrinfo for %s:%s", - pool->sockaddr_url, pool->stratum_port); + sockaddr_url, sockaddr_port); } return false; } @@ -1755,22 +2749,86 @@ static bool setup_stratum_socket(struct pool *pool) continue; } + /* Iterate non blocking over entries returned by getaddrinfo + * to cope with round robin DNS entries, finding the first one + * we can connect to quickly. */ + noblock_socket(sockd); if (connect(sockd, p->ai_addr, p->ai_addrlen) == -1) { + struct timeval tv_timeout = {1, 0}; + int selret; + fd_set rw; + + if (!sock_connecting()) { + CLOSESOCKET(sockd); + applog(LOG_DEBUG, "Failed sock connect"); + continue; + } +retry: + FD_ZERO(&rw); + FD_SET(sockd, &rw); + selret = select(sockd + 1, NULL, &rw, NULL, &tv_timeout); + if (selret > 0 && FD_ISSET(sockd, &rw)) { + socklen_t len; + int err, n; + + len = sizeof(err); + n = getsockopt(sockd, SOL_SOCKET, SO_ERROR, (void *)&err, &len); + if (!n && !err) { + applog(LOG_DEBUG, "Succeeded delayed connect"); + block_socket(sockd); + break; + } + } + if (selret < 0 && interrupted()) + goto retry; CLOSESOCKET(sockd); - applog(LOG_DEBUG, "Failed connect"); + applog(LOG_DEBUG, "Select timeout/failed connect"); continue; } + applog(LOG_WARNING, "Succeeded immediate connect"); + block_socket(sockd); break; } if (p == NULL) { - applog(LOG_INFO, "Failed to find a stratum servinfo on %s:%s", - pool->sockaddr_url, pool->stratum_port); + applog(LOG_INFO, "Failed to connect to stratum on %s:%s", + sockaddr_url, sockaddr_port); freeaddrinfo(servinfo); return false; } freeaddrinfo(servinfo); + if (pool->rpc_proxy) { + switch (pool->rpc_proxytype) { + case PROXY_HTTP_1_0: + if (!http_negotiate(pool, sockd, true)) + return false; + break; + case PROXY_HTTP: + if (!http_negotiate(pool, sockd, false)) + return false; + break; + case PROXY_SOCKS5: + case PROXY_SOCKS5H: + if (!socks5_negotiate(pool, sockd)) + return false; + break; + case PROXY_SOCKS4: + if (!socks4_negotiate(pool, sockd, false)) + return false; + break; + case PROXY_SOCKS4A: + if (!socks4_negotiate(pool, sockd, true)) + return false; + break; + default: + applog(LOG_WARNING, "Unsupported proxy type for %s:%s", + pool->sockaddr_proxy_url, pool->sockaddr_proxy_port); + return false; + break; + } + } + if (!pool->sockbuf) { pool->sockbuf = calloc(RBUFSIZE, 1); if (!pool->sockbuf) @@ -1813,17 +2871,25 @@ static char *get_sessionid(json_t *val) void suspend_stratum(struct pool *pool) { - clear_sockbuf(pool); applog(LOG_INFO, "Closing socket for stratum pool %d", pool->pool_no); mutex_lock(&pool->stratum_lock); - pool->stratum_active = pool->stratum_notify = false; - if (pool->sock) - CLOSESOCKET(pool->sock); - pool->sock = 0; + __suspend_stratum(pool); mutex_unlock(&pool->stratum_lock); } +void extranonce_subscribe_stratum(struct pool *pool) +{ + if(pool->extranonce_subscribe) + { + char s[RBUFSIZE]; + sprintf(s,"{\"id\": %d, \"method\": \"mining.extranonce.subscribe\", \"params\": []}", swork_id++); + applog(LOG_INFO, "Send extranonce.subscribe for stratum pool %d", pool->pool_no); + stratum_send(pool, s, strlen(s)); + } +} + + bool initiate_stratum(struct pool *pool) { bool ret = false, recvd = false, noresume = false, sockd = false; @@ -1897,14 +2963,14 @@ bool initiate_stratum(struct pool *pool) if (!sessionid) applog(LOG_DEBUG, "Failed to get sessionid in initiate_stratum"); nonce1 = json_array_string(res_val, 1); - if (!nonce1) { - applog(LOG_INFO, "Failed to get nonce1 in initiate_stratum"); + if (!valid_hex(nonce1)) { + applog(LOG_INFO, "Failed to get valid nonce1 in initiate_stratum"); free(sessionid); goto out; } n2size = json_integer_value(json_array_get(res_val, 2)); - if (!n2size) { - applog(LOG_INFO, "Failed to get n2size in initiate_stratum"); + if (n2size < 2 || n2size > 16) { + applog(LOG_INFO, "Failed to get valid n2size in initiate_stratum"); free(sessionid); free(nonce1); goto out; @@ -1931,11 +2997,16 @@ bool initiate_stratum(struct pool *pool) if (!pool->stratum_url) pool->stratum_url = pool->sockaddr_url; pool->stratum_active = true; - pool->swork.diff = 1; + pool->sdiff = 1; if (opt_protocol) { applog(LOG_DEBUG, "Pool %d confirmed mining.subscribe with extranonce1 %s extran2size %d", pool->pool_no, pool->nonce1, pool->n2size); } + if(pool->extranonce_subscribe) + { + sprintf(s,"{\"id\": %d, \"method\": \"mining.extranonce.subscribe\", \"params\": []}", swork_id++); + stratum_send(pool, s, strlen(s)); + } } else { if (recvd && !noresume) { /* Reset the sessionid used for stratum resuming in case the pool @@ -1949,6 +3020,7 @@ bool initiate_stratum(struct pool *pool) applog(LOG_DEBUG, "Failed to resume stratum, trying afresh"); noresume = true; + json_decref(val); goto resend; } applog(LOG_DEBUG, "Initiate stratum failed"); @@ -1956,18 +3028,28 @@ bool initiate_stratum(struct pool *pool) suspend_stratum(pool); } + json_decref(val); return ret; } bool restart_stratum(struct pool *pool) { + bool ret = false; + if (pool->stratum_active) suspend_stratum(pool); if (!initiate_stratum(pool)) - return false; + goto out; if (!auth_stratum(pool)) - return false; - return true; + goto out; + extranonce_subscribe_stratum(pool); + ret = true; +out: + if (!ret) + pool_died(pool); + else + stratum_resumed(pool); + return ret; } void dev_error(struct cgpu_info *dev, enum dev_reason reason) @@ -2012,11 +3094,13 @@ void dev_error(struct cgpu_info *dev, enum dev_reason reason) /* Realloc an existing string to fit an extra string s, appending s to it. */ void *realloc_strcat(char *ptr, char *s) { - size_t old = strlen(ptr), len = strlen(s); + size_t old = 0, len = strlen(s); char *ret; if (!len) return ptr; + if (ptr) + old = strlen(ptr); len += old + 1; align_len(&len); @@ -2025,8 +3109,11 @@ void *realloc_strcat(char *ptr, char *s) if (unlikely(!ret)) quithere(1, "Failed to malloc"); - sprintf(ret, "%s%s", ptr, s); - free(ptr); + if (ptr) { + sprintf(ret, "%s%s", ptr, s); + free(ptr); + } else + sprintf(ret, "%s", s); return ret; } @@ -2066,16 +3153,19 @@ void *str_text(char *ptr) void RenameThread(const char* name) { + char buf[16]; + + snprintf(buf, sizeof(buf), "cg@%s", name); #if defined(PR_SET_NAME) // Only the first 15 characters are used (16 - NUL terminator) - prctl(PR_SET_NAME, name, 0, 0, 0); + prctl(PR_SET_NAME, buf, 0, 0, 0); #elif (defined(__FreeBSD__) || defined(__OpenBSD__)) - pthread_set_name_np(pthread_self(), name); + pthread_set_name_np(pthread_self(), buf); #elif defined(MAC_OSX) - pthread_setname_np(name); + pthread_setname_np(buf); #else - // Prevent warnings for unused parameters... - (void)name; + // Prevent warnings + (void)buf; #endif } @@ -2106,26 +3196,80 @@ void _cgsem_post(cgsem_t *cgsem, const char *file, const char *func, const int l const char buf = 1; int ret; +retry: ret = write(cgsem->pipefd[1], &buf, 1); if (unlikely(ret == 0)) applog(LOG_WARNING, "Failed to write errno=%d" IN_FMT_FFL, errno, file, func, line); + else if (unlikely(ret < 0 && interrupted)) + goto retry; } void _cgsem_wait(cgsem_t *cgsem, const char *file, const char *func, const int line) { char buf; int ret; - +retry: ret = read(cgsem->pipefd[0], &buf, 1); if (unlikely(ret == 0)) applog(LOG_WARNING, "Failed to read errno=%d" IN_FMT_FFL, errno, file, func, line); + else if (unlikely(ret < 0 && interrupted)) + goto retry; } -void _cgsem_destroy(cgsem_t *cgsem) +void cgsem_destroy(cgsem_t *cgsem) { close(cgsem->pipefd[1]); close(cgsem->pipefd[0]); } + +/* This is similar to sem_timedwait but takes a millisecond value */ +int _cgsem_mswait(cgsem_t *cgsem, int ms, const char *file, const char *func, const int line) +{ + struct timeval timeout; + int ret, fd; + fd_set rd; + char buf; + +retry: + fd = cgsem->pipefd[0]; + FD_ZERO(&rd); + FD_SET(fd, &rd); + ms_to_timeval(&timeout, ms); + ret = select(fd + 1, &rd, NULL, NULL, &timeout); + + if (ret > 0) { + ret = read(fd, &buf, 1); + return 0; + } + if (likely(!ret)) + return ETIMEDOUT; + if (interrupted()) + goto retry; + quitfrom(1, file, func, line, "Failed to sem_timedwait errno=%d cgsem=0x%p", errno, cgsem); + /* We don't reach here */ + return 0; +} + +/* Reset semaphore count back to zero */ +void cgsem_reset(cgsem_t *cgsem) +{ + int ret, fd; + fd_set rd; + char buf; + + fd = cgsem->pipefd[0]; + FD_ZERO(&rd); + FD_SET(fd, &rd); + do { + struct timeval timeout = {0, 0}; + + ret = select(fd + 1, &rd, NULL, NULL, &timeout); + if (ret > 0) + ret = read(fd, &buf, 1); + else if (unlikely(ret < 0 && interrupted())) + ret = 1; + } while (ret > 0); +} #else void _cgsem_init(cgsem_t *cgsem, const char *file, const char *func, const int line) { @@ -2142,12 +3286,392 @@ void _cgsem_post(cgsem_t *cgsem, const char *file, const char *func, const int l void _cgsem_wait(cgsem_t *cgsem, const char *file, const char *func, const int line) { - if (unlikely(sem_wait(cgsem))) +retry: + if (unlikely(sem_wait(cgsem))) { + if (interrupted()) + goto retry; quitfrom(1, file, func, line, "Failed to sem_wait errno=%d cgsem=0x%p", errno, cgsem); + } } -void _cgsem_destroy(cgsem_t *cgsem) +int _cgsem_mswait(cgsem_t *cgsem, int ms, const char *file, const char *func, const int line) +{ + struct timespec abs_timeout, ts_now; + struct timeval tv_now; + int ret; + + cgtime(&tv_now); + timeval_to_spec(&ts_now, &tv_now); + ms_to_timespec(&abs_timeout, ms); +retry: + timeraddspec(&abs_timeout, &ts_now); + ret = sem_timedwait(cgsem, &abs_timeout); + + if (ret) { + if (likely(sock_timeout())) + return ETIMEDOUT; + if (interrupted()) + goto retry; + quitfrom(1, file, func, line, "Failed to sem_timedwait errno=%d cgsem=0x%p", errno, cgsem); + } + return 0; +} + +void cgsem_reset(cgsem_t *cgsem) +{ + int ret; + + do { + ret = sem_trywait(cgsem); + if (unlikely(ret < 0 && interrupted())) + ret = 0; + } while (!ret); +} + +void cgsem_destroy(cgsem_t *cgsem) { sem_destroy(cgsem); } #endif + +/* Provide a completion_timeout helper function for unreliable functions that + * may die due to driver issues etc that time out if the function fails and + * can then reliably return. */ +struct cg_completion { + cgsem_t cgsem; + void (*fn)(void *fnarg); + void *fnarg; +}; + +void *completion_thread(void *arg) +{ + struct cg_completion *cgc = (struct cg_completion *)arg; + + pthread_setcanceltype(PTHREAD_CANCEL_ASYNCHRONOUS, NULL); + cgc->fn(cgc->fnarg); + cgsem_post(&cgc->cgsem); + + return NULL; +} + +bool cg_completion_timeout(void *fn, void *fnarg, int timeout) +{ + struct cg_completion *cgc; + pthread_t pthread; + bool ret = false; + + cgc = malloc(sizeof(struct cg_completion)); + if (unlikely(!cgc)) + return ret; + cgsem_init(&cgc->cgsem); + cgc->fn = fn; + cgc->fnarg = fnarg; + + pthread_create(&pthread, NULL, completion_thread, (void *)cgc); + + ret = cgsem_mswait(&cgc->cgsem, timeout); + if (!ret) { + pthread_join(pthread, NULL); + free(cgc); + } else + pthread_cancel(pthread); + return !ret; +} + +void _cg_memcpy(void *dest, const void *src, unsigned int n, const char *file, const char *func, const int line) +{ + if (unlikely(n < 1 || n > (1ul << 31))) { + applog(LOG_ERR, "ERR: Asked to memcpy %u bytes from %s %s():%d", + n, file, func, line); + return; + } + memcpy(dest, src, n); +} + +int cg_timeval_subtract(struct timeval* result, struct timeval* x, struct timeval* y) +{ + int nsec = 0; + if(x->tv_sec > y->tv_sec) + return -1; + + if((x->tv_sec == y->tv_sec) && (x->tv_usec > y->tv_usec)) + return -1; + + result->tv_sec = (y->tv_sec - x->tv_sec); + result->tv_usec = (y->tv_usec - x->tv_usec); + + if(result->tv_usec < 0) + { + result->tv_sec--; + result->tv_usec += 1000000; + } + return 0; +} + +void rev(unsigned char *s, size_t l) +{ + size_t i, j; + unsigned char t; + + for (i = 0, j = l - 1; i < j; i++, j--) { + t = s[i]; + s[i] = s[j]; + s[j] = t; + } +} + +int check_asicnum(int asic_num, unsigned char nonce) +{ + switch(asic_num) + { + case 1: + return 1; + case 2: + switch(nonce & 0x80) + { + case 0x80: return 2; + default: return 1; + } + case 4: + switch(nonce & 0xC0) + { + case 0xC0: return 4; + case 0x80: return 3; + case 0x40: return 2; + default: return 1; + } + case 8: + switch(nonce & 0xE0) + { + case 0xE0: return 8; + case 0xC0: return 7; + case 0xA0: return 6; + case 0x80: return 5; + case 0x60: return 4; + case 0x40: return 3; + case 0x20: return 2; + default : return 1; + } + case 16: + switch(nonce & 0xF0) + { + case 0xF0: return 16; + case 0xE0: return 15; + case 0xD0: return 14; + case 0xC0: return 13; + case 0xB0: return 12; + case 0xA0: return 11; + case 0x90: return 10; + case 0x80: return 9; + case 0x70: return 8; + case 0x60: return 7; + case 0x50: return 6; + case 0x40: return 5; + case 0x30: return 4; + case 0x20: return 3; + case 0x10: return 2; + default : return 1; + } + case 32: + switch(nonce & 0xF8) + { + case 0xF8: return 32; + case 0xF0: return 31; + case 0xE8: return 30; + case 0xE0: return 29; + case 0xD8: return 28; + case 0xD0: return 27; + case 0xC8: return 26; + case 0xC0: return 25; + case 0xB8: return 24; + case 0xB0: return 23; + case 0xA8: return 22; + case 0xA0: return 21; + case 0x98: return 20; + case 0x90: return 19; + case 0x88: return 18; + case 0x80: return 17; + case 0x78: return 16; + case 0x70: return 15; + case 0x68: return 14; + case 0x60: return 13; + case 0x58: return 12; + case 0x50: return 11; + case 0x48: return 10; + case 0x40: return 9; + case 0x38: return 8; + case 0x30: return 7; + case 0x28: return 6; + case 0x20: return 5; + case 0x18: return 4; + case 0x10: return 3; + case 0x08: return 2; + default : return 1; + } + case 64: + switch(nonce & 0xFC) + { + case 0xFC: return 64; + case 0xF8: return 63; + case 0xF4: return 62; + case 0xF0: return 61; + case 0xEC: return 60; + case 0xE8: return 59; + case 0xE4: return 58; + case 0xE0: return 57; + case 0xDC: return 56; + case 0xD8: return 55; + case 0xD4: return 54; + case 0xD0: return 53; + case 0xCC: return 52; + case 0xC8: return 51; + case 0xC4: return 50; + case 0xC0: return 49; + case 0xBC: return 48; + case 0xB8: return 47; + case 0xB4: return 46; + case 0xB0: return 45; + case 0xAC: return 44; + case 0xA8: return 43; + case 0xA4: return 42; + case 0xA0: return 41; + case 0x9C: return 40; + case 0x98: return 39; + case 0x94: return 38; + case 0x90: return 37; + case 0x8C: return 36; + case 0x88: return 35; + case 0x84: return 34; + case 0x80: return 33; + case 0x7C: return 32; + case 0x78: return 31; + case 0x74: return 30; + case 0x70: return 29; + case 0x6C: return 28; + case 0x68: return 27; + case 0x64: return 26; + case 0x60: return 25; + case 0x5C: return 24; + case 0x58: return 23; + case 0x54: return 22; + case 0x50: return 21; + case 0x4C: return 20; + case 0x48: return 19; + case 0x44: return 18; + case 0x40: return 17; + case 0x3C: return 16; + case 0x38: return 15; + case 0x34: return 14; + case 0x30: return 13; + case 0x2C: return 12; + case 0x28: return 11; + case 0x24: return 10; + case 0x20: return 9; + case 0x1C: return 8; + case 0x18: return 7; + case 0x14: return 6; + case 0x10: return 5; + case 0x0C: return 4; + case 0x08: return 3; + case 0x04: return 2; + default : return 1; + } + default: + return 0; + } +} + +void cg_logwork(struct work *work, unsigned char *nonce_bin, bool ok) +{ + if(opt_logwork_path) { + char szmsg[1024] = {0}; + unsigned char midstate_tmp[32] = {0}; + unsigned char data_tmp[32] = {0}; + unsigned char hash_tmp[32] = {0}; + char * szworkdata = NULL; + char * szmidstate = NULL; + char * szdata = NULL; + char * sznonce4 = NULL; + char * sznonce5 = NULL; + char * szhash = NULL; + int asicnum = 0; + uint64_t worksharediff = 0; + memcpy(midstate_tmp, work->midstate, 32); + memcpy(data_tmp, work->data+64, 12); + memcpy(hash_tmp, work->hash, 32); + rev((void *)midstate_tmp, 32); + rev((void *)data_tmp, 12); + rev((void *)hash_tmp, 32); + szworkdata = bin2hex((void *)work->data, 128); + szmidstate = bin2hex((void *)midstate_tmp, 32); + szdata = bin2hex((void *)data_tmp, 12); + sznonce4 = bin2hex((void *)nonce_bin, 4); + sznonce5 = bin2hex((void *)nonce_bin, 5); + szhash = bin2hex((void *)hash_tmp, 32); + worksharediff = share_ndiff(work); + sprintf(szmsg, "%s %08x midstate %s data %s nonce %s hash %s diff %I64d", ok?"o":"x", work->id, szmidstate, szdata, sznonce5, szhash, worksharediff); + if(strcmp(opt_logwork_path, "screen") == 0) { + applog(LOG_ERR, szmsg); + } else { + applog(LOG_ERR, szmsg); + if(g_logwork_file) { + sprintf(szmsg, "%s %08x work %s midstate %s data %s nonce %s hash %s diff %I64d", ok?"o":"x", work->id, szworkdata, szmidstate, szdata, sznonce5, szhash, worksharediff); + + fwrite(szmsg, strlen(szmsg), 1, g_logwork_file); + fwrite("\n", 1, 1, g_logwork_file); + fflush(g_logwork_file); + + if(ok) { + if(g_logwork_asicnum == 1) { + sprintf(szmsg, "midstate %s data %s nonce %s hash %s", szmidstate, szdata, sznonce4, szhash); + fwrite(szmsg, strlen(szmsg), 1, g_logwork_files[0]); + fwrite("\n", 1, 1, g_logwork_files[0]); + fflush(g_logwork_files[0]); + } else if(g_logwork_asicnum == 32 || g_logwork_asicnum == 64) { + sprintf(szmsg, "midstate %s data %s nonce %s hash %s", szmidstate, szdata, sznonce4, szhash); + asicnum = check_asicnum(g_logwork_asicnum, nonce_bin[0]); + fwrite(szmsg, strlen(szmsg), 1, g_logwork_files[asicnum]); + fwrite("\n", 1, 1, g_logwork_files[asicnum]); + fflush(g_logwork_files[asicnum]); + } + + if(opt_logwork_diff) { + int diffnum = 0; + uint64_t difftmp = worksharediff; + while(1) { + difftmp = difftmp >> 1; + if(difftmp > 0) { + diffnum++; + if(diffnum >= 64) { + break; + } + } else { + break; + } + } + applog(LOG_DEBUG, "work diff %I64d diffnum %d", worksharediff, diffnum); + sprintf(szmsg, "midstate %s data %s nonce %s hash %s", szmidstate, szdata, sznonce4, szhash); + fwrite(szmsg, strlen(szmsg), 1, g_logwork_diffs[diffnum]); + fwrite("\n", 1, 1, g_logwork_diffs[diffnum]); + fflush(g_logwork_diffs[diffnum]); + } + } + } + } + if(szworkdata) free(szworkdata); + if(szmidstate) free(szmidstate); + if(szdata) free(szdata); + if(sznonce4) free(sznonce4); + if(sznonce5) free(sznonce5); + if(szhash) free(szhash); + } +} + +void cg_logwork_uint32(struct work *work, uint32_t nonce, bool ok) +{ + if(opt_logwork_path) { + unsigned char nonce_bin[5] = {0}; + memcpy(nonce_bin, &nonce, 4); + cg_logwork(work, nonce_bin, ok); + } +} diff --git a/util.h b/util.h index f083f20bde..14336ac975 100644 --- a/util.h +++ b/util.h @@ -14,15 +14,24 @@ #define INVSOCK -1 #define INVINETADDR -1 #define CLOSESOCKET close + #define INET_PTON inet_pton #define SOCKERRMSG strerror(errno) static inline bool sock_blocks(void) { return (errno == EAGAIN || errno == EWOULDBLOCK); } + static inline bool sock_timeout(void) + { + return (errno == ETIMEDOUT); + } + static inline bool interrupted(void) + { + return (errno == EINTR); + } #elif defined WIN32 - #include #include + #include #define SOCKETTYPE SOCKET #define SOCKETFAIL(a) ((int)(a) == SOCKET_ERROR) @@ -30,12 +39,25 @@ #define INVINETADDR INADDR_NONE #define CLOSESOCKET closesocket + int Inet_Pton(int af, const char *src, void *dst); + #define INET_PTON Inet_Pton + extern char *WSAErrorMsg(void); #define SOCKERRMSG WSAErrorMsg() + /* Check for windows variants of the errors as well as when ming + * decides to wrap the error into the errno equivalent. */ static inline bool sock_blocks(void) { - return (WSAGetLastError() == WSAEWOULDBLOCK); + return (WSAGetLastError() == WSAEWOULDBLOCK || errno == EAGAIN); + } + static inline bool sock_timeout(void) + { + return (WSAGetLastError() == WSAETIMEDOUT || errno == ETIMEDOUT); + } + static inline bool interrupted(void) + { + return (WSAGetLastError() == WSAEINTR || errno == EINTR); } #ifndef SHUT_RDWR #define SHUT_RDWR SD_BOTH @@ -46,11 +68,22 @@ #endif #endif -#if JANSSON_MAJOR_VERSION >= 2 #define JSON_LOADS(str, err_ptr) json_loads((str), 0, (err_ptr)) + +#ifdef HAVE_LIBCURL +#include +typedef curl_proxytype proxytypes_t; #else -#define JSON_LOADS(str, err_ptr) json_loads((str), (err_ptr)) -#endif +typedef int proxytypes_t; +#endif /* HAVE_LIBCURL */ + +/* cgminer locks, a write biased variant of rwlocks */ +struct cglock { + pthread_mutex_t mutex; + pthread_rwlock_t rwlock; +}; + +typedef struct cglock cglock_t; /* cgminer specific unnamed semaphore implementations to cope with osx not * implementing them. */ @@ -63,12 +96,22 @@ typedef struct cgsem cgsem_t; #else typedef sem_t cgsem_t; #endif +#ifdef WIN32 +typedef LARGE_INTEGER cgtimer_t; +#else typedef struct timespec cgtimer_t; +#endif +int no_yield(void); +int (*selective_yield)(void); struct thr_info; struct pool; enum dev_reason; struct cgpu_info; +void b58tobin(unsigned char *b58bin, const char *b58); +void address_to_pubkeyhash(unsigned char *pkh, const char *addr); +int ser_number(unsigned char *s, int32_t val); +unsigned char *ser_string(char *s, int *slen); int thr_info_create(struct thr_info *thr, pthread_attr_t *attr, void *(*start) (void *), void *arg); void thr_info_cancel(struct thr_info *thr); void cgtime(struct timeval *tv); @@ -83,6 +126,8 @@ void us_to_timeval(struct timeval *val, int64_t us); void us_to_timespec(struct timespec *spec, int64_t us); void ms_to_timespec(struct timespec *spec, int64_t ms); void timeraddspec(struct timespec *a, const struct timespec *b); +char *Strcasestr(char *haystack, const char *needle); +char *Strsep(char **stringp, const char *delim); void cgsleep_ms(int ms); void cgsleep_us(int64_t us); void cgtimer_time(cgtimer_t *ts_start); @@ -92,12 +137,17 @@ void cgsleep_us_r(cgtimer_t *ts_start, int64_t us); int cgtimer_to_ms(cgtimer_t *cgt); void cgtimer_sub(cgtimer_t *a, cgtimer_t *b, cgtimer_t *res); double us_tdiff(struct timeval *end, struct timeval *start); +int ms_tdiff(struct timeval *end, struct timeval *start); double tdiff(struct timeval *end, struct timeval *start); bool stratum_send(struct pool *pool, char *s, ssize_t len); bool sock_full(struct pool *pool); +void _recalloc(void **ptr, size_t old, size_t new, const char *file, const char *func, const int line); +#define recalloc(ptr, old, new) _recalloc((void *)&(ptr), old, new, __FILE__, __func__, __LINE__) char *recv_line(struct pool *pool); bool parse_method(struct pool *pool, char *s); -bool extract_sockaddr(struct pool *pool, char *url); +void check_extranonce_option(struct pool *pool, char * url); +bool extract_sockaddr(char *url, char **sockaddr_url, char **sockaddr_port); +void extranonce_subscribe_stratum(struct pool *pool); bool auth_stratum(struct pool *pool); bool initiate_stratum(struct pool *pool); bool restart_stratum(struct pool *pool); @@ -109,12 +159,17 @@ void RenameThread(const char* name); void _cgsem_init(cgsem_t *cgsem, const char *file, const char *func, const int line); void _cgsem_post(cgsem_t *cgsem, const char *file, const char *func, const int line); void _cgsem_wait(cgsem_t *cgsem, const char *file, const char *func, const int line); -void _cgsem_destroy(cgsem_t *cgsem); +int _cgsem_mswait(cgsem_t *cgsem, int ms, const char *file, const char *func, const int line); +void cgsem_reset(cgsem_t *cgsem); +void cgsem_destroy(cgsem_t *cgsem); +bool cg_completion_timeout(void *fn, void *fnarg, int timeout); +void _cg_memcpy(void *dest, const void *src, unsigned int n, const char *file, const char *func, const int line); #define cgsem_init(_sem) _cgsem_init(_sem, __FILE__, __func__, __LINE__) #define cgsem_post(_sem) _cgsem_post(_sem, __FILE__, __func__, __LINE__) #define cgsem_wait(_sem) _cgsem_wait(_sem, __FILE__, __func__, __LINE__) -#define cgsem_destroy(_sem) _cgsem_destroy(_sem) +#define cgsem_mswait(_sem, _timeout) _cgsem_mswait(_sem, _timeout, __FILE__, __func__, __LINE__) +#define cg_memcpy(dest, src, n) _cg_memcpy(dest, src, n, __FILE__, __func__, __LINE__) /* Align a size_t to 4 byte boundaries for fussy arches */ static inline void align_len(size_t *len) diff --git a/windows-build.txt b/windows-build.txt index 2da205de67..d2699de9fd 100644 --- a/windows-build.txt +++ b/windows-build.txt @@ -4,6 +4,8 @@ # # ###################################################################################### +(See bottom of file for steps to cross-build for Win32 from Linux.) + ************************************************************************************** * Introduction * ************************************************************************************** @@ -245,14 +247,102 @@ You will have to copy "libusb-1.0.dll" to your working cgminer binary directory. --disable-adl Override detection and disable building with adl --enable-bitforce Compile support for BitForce FPGAs(default disabled) --enable-icarus Compile support for Icarus Board(default disabled) +--enable-bmsc Compile support for BitMain Single Chain(default disabled) +--enable-bitmain Compile support for BitMain Multi Chain(default disabled) --enable-modminer Compile support for ModMiner FPGAs(default disabled) --enable-ztex Compile support for Ztex Board(default disabled) --enable-scrypt Compile support for scrypt litecoin mining (default disabled) --without-curses Compile support for curses TUI (default enabled) --without-libudev Autodetect FPGAs using libudev (default enabled) + + ###################################################################################### # # -# Native WIN32 setup and build instructions (on mingw32/Windows) complete # +# Cross-compiling for Windows from Linux # # # ###################################################################################### + +It is possible to cross-compile Windows binaries from Linux. The +process is a bit different to the native steps shown above (it is also +possible to use wine and the native steps, but this is more messing +around, very slow, and not advisable.) + +** Install mingw cross compiler + +On Ubuntu/Debian: + +sudo apt-get install mingw32 + +** create a directory to hold our cross-library dependencies + +We'll create a directory outside the source tree to hold non-system +libraries we depend on. We could put these in +/usr/i586-mingw32msvc/lib or anywhere else, instead (though keeping it +outside /usr means we can set it up without root privileges.) + +IMPORTANT: If you put this directory inside your cgminer directory, +remember 'make distclean' may delete it! + +mkdir -p ../cgminer-win32-deps/lib +cd ../cgminer-win32-deps +mkdir include +mkdir bin + +NB: All following steps assume you are in the "cgminer-win32-deps" directory. Adjust as necessary. + +** pdcurses + +wget http://internode.dl.sourceforge.net/project/pdcurses/pdcurses/3.4/pdc34dllw.zip +unzip /home/gus/Downloads/pdc34dllw.zip +mv *.h include/ +mv pdcurses.lib lib/ +mv pdcurses.dll bin/ + +** pthreads-w32 + +(NB: I found pthreads-w32 2.9.1 doesn't seem to work properly, transfers time out early due to sem_timedwait exiting immediately(?)) + +wget -O lib/libpthread.a ftp://sourceware.org/pub/pthreads-win32/prebuilt-dll-2-8-0-release/lib/libpthreadGC2.a +wget -O include/pthread.h ftp://sourceware.org/pub/pthreads-win32/prebuilt-dll-2-8-0-release/include/pthread.h +wget -O include/sched.h ftp://sourceware.org/pub/pthreads-win32/prebuilt-dll-2-8-0-release/include/sched.h +wget -O include/semaphore.h ftp://sourceware.org/pub/pthreads-win32/prebuilt-dll-2-8-0-release/include/semaphore.h +wget -O lib/libpthread.a ftp://sourceware.org/pub/pthreads-win32/prebuilt-dll-2-8-0-release/lib/libpthreadGC2.a +wget -O bin/pthreadGC2.dll ftp://sourceware.org/pub/pthreads-win32/prebuilt-dll-2-8-0-release/lib/pthreadGC2.dll + +** libcurl + +wget http://curl.haxx.se/gknw.net/7.33.0/dist-w32/curl-7.33.0-devel-mingw32.zip +unzip curl-7.33.0-devel-mingw32.zip +mv curl-7.33.0-devel-mingw32/include/* include/ +mv curl-7.33.0-devel-mingw32/lib/* lib/ +mv curl-7.33.0-devel-mingw32/bin/* bin/ +rm -rf curl-7.33.0-devel-mingw32 + + +** clean up + +rm *.zip + + +** Building cgminer + +Below assumes you're building in a "build-win32" or similar directory +inside the cgminer directory. Fix up the -I and -L paths appropriately +if you're building in-tree or someplace else. + +Configure command: + +CPPFLAGS="-I`pwd`/../../cgminer-win32-deps/include" LDFLAGS="-L`pwd`/../../cgminer-win32-deps/lib -lcurldll" ../autogen.sh --prefix=/usr/local/i586-mingw32 --host=i586-mingw32msvc --build=i686-linux + +^^^ Plus whatever configure arguments you want to add. Note the paths + to cgminer-win32-deps that you may need to change. + +And make: + +make + +After cgminer builds, the next steps are the same as for native +building as given under "Copy files to a build directory/folder" +(DLLs can all be found in the cgminer-win32-deps/bin directory.) +