Professional Documents
Culture Documents
#include "config.h"
#include "miner.h"
#include "driver-drillbit.h"
#include "sha2.h"
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;
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);
// 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)
#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;
#define SZ_SERIALISED_IDENTITY 16
static void deserialise_identity(Identity *identity, const char *buf);
/* 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;
/* 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);
/* 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
do {
usb_read_timeout(drillbit, buf, sizeof(buf), &amount, EMPTY_TIMEOUT,
C_BF_FLUSH);
} while (amount);
}
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);
drillbit_empty_buffer(drillbit);
return true;
}
drillbit_empty_buffer(drillbit);
return res;
}
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;
}
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);
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 (!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);
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;
}
if (!drillbit_parse_options(drillbit))
goto out;
drillbit_open(drillbit);
drillbit_identify(drillbit);
drillbit_empty_buffer(drillbit);
cgtime(&info->tv_lastchipinfo);
if (!add_cgpu(drillbit))
goto out_close;
update_usb_stats(drillbit);
return drillbit;
out_close:
drillbit_close(drillbit);
usb_uninit(drillbit);
out:
drillbit = usb_free_cgpu(drillbit);
return drillbit;
}
/* Byte reversal */
in = (((in & 0xaaaaaaaa) >> 1) | ((in & 0x55555555) << 1));
in = (((in & 0xcccccccc) >> 2) | ((in & 0x33333333) << 2));
in = (((in & 0xf0f0f0f0) >> 4) | ((in & 0x0f0f0f0f) << 4));
/* 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};
/*
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;
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;
if (result_count == 0) {
// Short circuit reading any work results
return 0;
}
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!
cleanup:
if (responses)
free(responses);
return successful_results;
}
if (unlikely(thr->work_restart)) {
work_completed(drillbit, work);
return;
}
chip->work_sent_count++;
}
/* 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;
}
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);
}
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;
}
drillbit_close(drillbit);
}
/* 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)