You are on page 1of 24

/*

* Copyright 2013-2014 Con Kolivas <kernel@kolivas.org>


* Copyright 2012-2014 Xiangfu <xiangfu@openmobilefree.com>
* Copyright 2014-2015 Mikeqin <Fengling.Qin@gmail.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.
*/
#include <math.h>
#include "config.h"
#include "miner.h"
#include "driver-avalon-miner.h"
#include "crc.h"
#include "sha2.h"
#include "hexdump.c"

#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); \
}

#define PACK32(str, x) \
{ \
*(x) = ((uint32_t) *((str) + 3) ) \
| ((uint32_t) *((str) + 2) << 8) \
| ((uint32_t) *((str) + 1) << 16) \
| ((uint32_t) *((str) + 0) << 24); \
}

#define V_REF 3.3


#define R_REF 10000
#define R0 10000
#define BCOEFFICIENT 3450
#define T0 25

static uint32_t opt_avalonm_freq[3] = {AVAM_DEFAULT_FREQUENCY,


AVAM_DEFAULT_FREQUENCY, AVAM_DEFAULT_FREQUENCY};
uint8_t opt_avalonm_ntime_offset = 0;
int opt_avalonm_voltage = AVAM_DEFAULT_VOLTAGE;
uint32_t opt_avalonm_spispeed = AVAM_DEFAULT_SPISPEED;
bool opt_avalonm_autof;
static uint32_t g_freq_array[][2] = {
{100, 0x1e678447},
{113, 0x22688447},
{125, 0x1c470447},
{138, 0x2a6a8447},
{150, 0x22488447},
{163, 0x326c8447},
{175, 0x1a268447},
{188, 0x1c270447},
{200, 0x1e278447},
{213, 0x20280447},
{225, 0x22288447},
{238, 0x24290447},
{250, 0x26298447},
{263, 0x282a0447},
{275, 0x2a2a8447},
{288, 0x2c2b0447},
{300, 0x2e2b8447},
{313, 0x302c0447},
{325, 0x322c8447},
{338, 0x342d0447},
{350, 0x1a068447},
{363, 0x382e0447},
{375, 0x1c070447},
{388, 0x3c2f0447},
{400, 0x1e078447}
};

static uint16_t encode_voltage(uint32_t v)


{
if (v == 0)
return 0xff;

return (((0x59 - (v - 5000) / 125) & 0xff) << 1 | 1);


}

static uint32_t decode_voltage(uint8_t v)


{
if (v == 0xff)
return 0;

return (0x59 - (v >> 1)) * 125 + 5000;


}

static uint32_t decode_cpm(uint32_t cpm)


{
int i;

for (i = 0; i < sizeof(g_freq_array) / sizeof(g_freq_array[0]); i++) {


if (g_freq_array[i][1] == cpm)
return g_freq_array[i][0];
}

return 0;
}

static int avalonm_init_pkg(struct avalonm_pkg *pkg, uint8_t type, uint8_t idx,


uint8_t cnt)
{
unsigned short crc;

pkg->head[0] = AVAM_H1;
pkg->head[1] = AVAM_H2;

pkg->type = type;
pkg->opt = 0;
pkg->idx = idx;
pkg->cnt = cnt;

crc = crc16(pkg->data, AVAM_P_DATA_LEN);

pkg->crc[0] = (crc & 0xff00) >> 8;


pkg->crc[1] = crc & 0x00ff;
return 0;
}

static float convert_temp(uint32_t adc)


{
float ret, resistance;

if (adc >= 1023)


return -273.15;

resistance = (1023.0 / adc) - 1;


resistance = R_REF / resistance;
ret = resistance / R0;
ret = log(ret);
ret /= BCOEFFICIENT;
ret += 1.0 / (T0 + 273.15);
ret = 1.0 / ret;
ret -= 273.15;

return ret;
}

static float convert_voltage(uint32_t adc, float percent)


{
float voltage;

voltage = adc * V_REF / 1023 / percent;


return voltage;
}

static void process_nonce(struct cgpu_info *avalonm, uint8_t *report)


{
struct avalonm_info *info = avalonm->device_data;
struct work *work;
uint8_t ntime, chip_id;
uint32_t nonce, id;

PACK32(report, &id);
chip_id = report[6];
if (chip_id >= info->asic_cnts) {
applog(LOG_DEBUG, "%s-%d: chip_id >= info->asic_cnts(%d > %d)",
avalonm->drv->name, avalonm->device_id,
chip_id, info->asic_cnts);
return;
}

ntime = report[7];
PACK32(report + 8, &nonce);
nonce -= 0x4000;
info->usbfifo_cnt = report[13];
info->workfifo_cnt = report[14];
info->noncefifo_cnt = report[15];

applog(LOG_DEBUG, "%s-%d: Found! - ID: %08x CID: %02x N:%08x NR:%d",


avalonm->drv->name, avalonm->device_id,
id, chip_id, nonce, ntime);

work = clone_queued_work_byid(avalonm, id);


if (!work)
return;

if(!submit_noffset_nonce(info->thr, work, nonce, ntime)) {


info->hw_work[chip_id]++;
info->hw_work_i[chip_id][info->time_i]++;
}

info->matching_work[chip_id]++;
free_work(work);
info->nonce_cnts++;
}

static int decode_pkg(struct thr_info *thr, struct avalonm_ret *ar)


{
struct cgpu_info *avalonm = thr->cgpu;
struct avalonm_info *info = avalonm->device_data;
uint32_t ret, tmp, i, freq[3];
unsigned int expected_crc;
unsigned int actual_crc;

if (ar->head[0] != AVAM_H1 && ar->head[1] != AVAM_H2) {


applog(LOG_DEBUG, "%s-%d: H1 %02x, H2 %02x",
avalonm->drv->name, avalonm->device_id,
ar->head[0], ar->head[1]);
info->crcerr_cnt++;
return 0;
}

expected_crc = crc16(ar->data, AVAM_P_DATA_LEN);


actual_crc = (ar->crc[1] & 0xff) | ((ar->crc[0] & 0xff) << 8);
if (expected_crc != actual_crc) {
applog(LOG_DEBUG, "%s-%d: %02x: expected crc(%04x), actual_crc(%04x)",
avalonm->drv->name, avalonm->device_id,
ar->type, expected_crc, actual_crc);
return 0;
}

switch(ar->type) {
case AVAM_P_NONCE_M:
ret = ar->type;
applog(LOG_DEBUG, "%s-%d: AVAM_P_NONCE", avalonm->drv->name, avalonm-
>device_id);
hexdump(ar->data, 32);
process_nonce(avalonm, ar->data);
if (ar->data[22] != 0xff) {
process_nonce(avalonm, ar->data + 16);
}
break;
case AVAM_P_STATUS_M:
ret = ar->type;
applog(LOG_DEBUG, "%s-%d: AVAM_P_STATUS_M", avalonm->drv->name,
avalonm->device_id);
hexdump(ar->data, 32);
memcpy(&tmp, ar->data, 4);
if (!strncmp(info->ver, "3U", 2))
info->get_frequency[0][0] = be32toh(tmp);
else
info->spi_speed = be32toh(tmp);
memcpy(&tmp, ar->data + 4, 4);
info->led_status = be32toh(tmp);
memcpy(&tmp, ar->data + 8, 4);
info->fan_pwm = be32toh(tmp);
memcpy(&tmp, ar->data + 12, 4);
if (!strncmp(info->ver, "3U", 2))
info->get_voltage = convert_voltage(be32toh(tmp), 0.5);
else
info->get_voltage = decode_voltage((uint8_t)be32toh(tmp));
memcpy(&tmp, ar->data + 16, 4);
info->adc[0] = be32toh(tmp);
memcpy(&tmp, ar->data + 20, 4);
info->adc[1] = be32toh(tmp);
memcpy(&tmp, ar->data + 24, 4);
info->adc[2] = be32toh(tmp);
memcpy(&tmp, ar->data + 28 , 4);
info->power_good = be32toh(tmp);

/* power off notice */


if (!info->get_voltage) {
usb_buffer_clear(avalonm);
applog(LOG_NOTICE, "%s-%d: AVAM_P_STATUS_M Power off notice",
avalonm->drv->name, avalonm->device_id);
info->power_on = 1;
memset(info->set_frequency, 0, sizeof(uint32_t) * info->asic_cnts
* 3);
for (i = 1; i <= info->asic_cnts; i++)
FLAG_SET(info->freq_set, i);
}
break;
case AVAM_P_STATUS_FREQ:
applog(LOG_DEBUG, "%s-%d: AVAM_P_STATUS_FREQ", avalonm->drv->name,
avalonm->device_id);
memcpy(&tmp, ar->data, 4);
tmp = be32toh(tmp);
freq[0] = decode_cpm(tmp);
memcpy(&tmp, ar->data + 4, 4);
tmp = be32toh(tmp);
freq[1] = decode_cpm(tmp);
memcpy(&tmp, ar->data + 8, 4);
tmp = be32toh(tmp);
freq[2] = decode_cpm(tmp);

if (!ar->opt) {
for (i = 0; i < info->asic_cnts; i++) {
info->get_frequency[i][0] = freq[0];
info->get_frequency[i][1] = freq[1];
info->get_frequency[i][2] = freq[2];
}
}

if (ar->opt) {
info->get_frequency[ar->opt - 1][0] = freq[0];
info->get_frequency[ar->opt - 1][1] = freq[1];
info->get_frequency[ar->opt - 1][2] = freq[2];
}
break;
default:
applog(LOG_DEBUG, "%s-%d: Unknown response (%x)", avalonm->drv->name,
avalonm->device_id,
ar->type);
ret = 0;
break;
}

return ret;
}

static int avalonm_send_pkg(struct cgpu_info *avalonm, const struct avalonm_pkg


*pkg)
{
int err = -1;
int writecnt;

if (unlikely(avalonm->usbinfo.nodev))
return -1;

err = usb_write(avalonm, (char *)pkg, AVAM_P_COUNT, &writecnt, C_AVAM_WRITE);


if (err || writecnt != AVAM_P_COUNT) {
applog(LOG_DEBUG, "%s-%d: avalonm_send_pkg %d, w(%d-%d)!", avalonm-
>drv->name, avalonm->device_id, err, AVAM_P_COUNT, writecnt);
return -1;
}

return writecnt;
}

static int avalonm_receive_pkg(struct cgpu_info *avalonm, struct avalonm_ret *ret)


{
int err = -1;
int readcnt;

if (unlikely(avalonm->usbinfo.nodev))
return -1;

err = usb_read(avalonm, (char*)ret, AVAM_P_COUNT, &readcnt, C_AVAM_READ);


if (err || readcnt != AVAM_P_COUNT) {
applog(LOG_DEBUG, "%s-%d: avalonm_receive_pkg %d, w(%d-%d)!", avalonm-
>drv->name, avalonm->device_id, err, AVAM_P_COUNT, readcnt);
return -1;
}

return readcnt;
}

static int avalonm_xfer_pkg(struct cgpu_info *avalonm, const struct avalonm_pkg


*pkg, struct avalonm_ret *ret)
{
if (sizeof(struct avalonm_pkg) != avalonm_send_pkg(avalonm, pkg))
return AVAM_SEND_ERROR;

if (sizeof(struct avalonm_ret) != avalonm_receive_pkg(avalonm, ret))


return AVAM_SEND_ERROR;

return AVAM_SEND_OK;
}

static int avalonm_get_frequency(struct cgpu_info *avalonm, uint8_t asic_index)


{
struct avalonm_info *info = avalonm->device_data;
struct thr_info *thr = info->thr;
struct avalonm_pkg send_pkg;
struct avalonm_ret ar;
int ret = 0;

memset(send_pkg.data, 0, AVAM_P_DATA_LEN);
avalonm_init_pkg(&send_pkg, AVAM_P_GET_FREQ, 1, 1);
send_pkg.opt = asic_index;
ret = avalonm_xfer_pkg(avalonm, &send_pkg, &ar);
if (ret == AVAM_SEND_OK) {
ret = decode_pkg(thr, &ar);
}

return ret;
}

static void avalonm_set_spispeed(struct cgpu_info *avalonm, uint32_t speed)


{
struct avalonm_pkg send_pkg;
int tmp;

memset(send_pkg.data, 0, AVAM_P_DATA_LEN);
tmp = speed | 0x80000000;
tmp = be32toh(tmp);
memcpy(send_pkg.data, &tmp, 4);
avalonm_init_pkg(&send_pkg, AVAM_P_SETM, 1, 1);
avalonm_send_pkg(avalonm, &send_pkg);
}

static struct cgpu_info *avalonm_detect_one(struct libusb_device *dev, struct


usb_find_devices *found)
{
struct cgpu_info *avalonm = usb_alloc_cgpu(&avalonm_drv, 1);
struct avalonm_info *info;
struct avalonm_pkg send_pkg;
struct avalonm_ret ar;
int ret, i;

if (!usb_init(avalonm, dev, found)) {


applog(LOG_ERR, "Avalonm failed usb_init");
avalonm = usb_free_cgpu(avalonm);
return NULL;
}

usb_buffer_clear(avalonm);
update_usb_stats(avalonm);

/* Cleanup the usb fifo */


while (avalonm_receive_pkg(avalonm, &ar) != -1);

/* We have an Avalonm connected */


avalonm->threads = 1;
memset(send_pkg.data, 0, AVAM_P_DATA_LEN);
avalonm_init_pkg(&send_pkg, AVAM_P_DETECT, 1, 1);
ret = avalonm_xfer_pkg(avalonm, &send_pkg, &ar);
if ((ret != AVAM_SEND_OK) || (ar.type != AVAM_P_ACKDETECT)) {
applog(LOG_DEBUG, "%s-%d: Failed to detect Avalon miner", avalonm->drv-
>name, avalonm->device_id);
return NULL;
}

add_cgpu(avalonm);

applog(LOG_DEBUG, "%s-%d: Found at %s", avalonm->drv->name, avalonm-


>device_id,
avalonm->device_path);

avalonm->device_data = cgcalloc(sizeof(struct avalonm_info), 1);


info = avalonm->device_data;
info->thr = NULL;
memcpy(info->dna, ar.data, AVAM_MM_DNA_LEN);
memcpy(info->ver, ar.data + AVAM_MM_DNA_LEN, AVAM_MM_VER_LEN);
info->ver[AVAM_MM_VER_LEN] = '\0';
memcpy(&info->asic_cnts, ar.data + AVAM_MM_DNA_LEN + AVAM_MM_VER_LEN, 4);
info->asic_cnts = be32toh(info->asic_cnts);
if (opt_avalonm_ntime_offset >= info->asic_cnts)
quit(1, "%s-%d: invalid opt_avalonm_ntime_offset, should 0-%d",
avalonm->drv->name, avalonm->device_id, info->asic_cnts - 1);

memset(info->set_frequency, 0, sizeof(uint32_t) * info->asic_cnts * 3);


memset(info->get_frequency, 0, sizeof(uint32_t) * info->asic_cnts * 3);
for (i = 0; i < info->asic_cnts; i++) {
info->opt_freq[i][0] = opt_avalonm_freq[0];
info->opt_freq[i][1] = opt_avalonm_freq[1];
info->opt_freq[i][2] = opt_avalonm_freq[2];
}
info->set_voltage = 0;
info->opt_voltage = opt_avalonm_voltage;
info->nonce_cnts = 0;
info->usbfifo_cnt = 0;
info->workfifo_cnt = 0;
info->noncefifo_cnt = 0;
info->crcerr_cnt = 0;
info->power_good = 0;
info->spi_speed = 0;
info->led_status = 0;
info->fan_pwm = 0;
info->get_voltage = 0;
info->freq_update = 0;
info->freq_set = 0;
FLAG_SET(info->freq_set, AVAM_ASIC_ALL);
memset(info->hw_work, 0, sizeof(int) * info->asic_cnts);
memset(info->matching_work, 0, sizeof(uint64_t) * info->asic_cnts);
info->adc[0] = info->adc[1] = info->adc[2] = 0;

avalonm_set_spispeed(avalonm, opt_avalonm_spispeed);
cgtime(&info->elapsed);
info->lastadj = info->lasttime = info->elapsed;
info->time_i = 0;
memset(info->hw_work_i, 0, sizeof(int) * info->asic_cnts *
AVAM_DEFAULT_MOV_TIMES);
return avalonm;
}

static uint32_t avalonm_get_cpm(uint32_t freq)


{
int i;

for (i = 0; i < (sizeof(g_freq_array) / sizeof(g_freq_array[0]) - 1); i++) {


if (freq >= g_freq_array[i][0] && freq < g_freq_array[i+1][0])
return g_freq_array[i][1];
}

/* check if the final freq match */


if (freq == g_freq_array[i][0])
return g_freq_array[i][1];

/* return the lowest freq if not found */


return g_freq_array[0][1];
}

static void avalonm_set_freq(struct cgpu_info *avalonm, uint8_t asic_index,


uint32_t freq[])
{
struct avalonm_info *info = avalonm->device_data;
struct avalonm_pkg send_pkg;
uint32_t tmp, i;
uint8_t index, change = 0;
uint32_t max_freq = 0;

if (asic_index == AVAM_ASIC_ALL) {
index = 0;
for (i = 0; i < info->asic_cnts; i++) {
if ((info->set_frequency[i][0] == freq[0]) &&
(info->set_frequency[i][1] == freq[1]) &&
(info->set_frequency[i][2] == freq[2]))
continue;

change = 1;
info->set_frequency[i][0] = freq[0];
info->set_frequency[i][1] = freq[1];
info->set_frequency[i][2] = freq[2];
FLAG_SET(info->freq_update, AVAM_ASIC_ALL);
}
}

if (asic_index != AVAM_ASIC_ALL) {
index = asic_index - 1;
if (!((info->set_frequency[index][0] == freq[0]) &&
(info->set_frequency[index][1] == freq[1]) &&
(info->set_frequency[index][2] == freq[2]))) {
change = 1;
info->set_frequency[index][0] = freq[0];
info->set_frequency[index][1] = freq[1];
info->set_frequency[index][2] = freq[2];
FLAG_SET(info->freq_update, asic_index);
}
}

if (!change)
return;

for (i = 0; i < info->asic_cnts; i++) {


if (max_freq < info->set_frequency[i][0])
max_freq = info->set_frequency[i][0];
if (max_freq < info->set_frequency[i][1])
max_freq = info->set_frequency[i][1];

if (max_freq < info->set_frequency[i][2])


max_freq = info->set_frequency[i][2];
}

info->delay_ms = CAL_DELAY(max_freq);

memset(send_pkg.data, 0, AVAM_P_DATA_LEN);
tmp = avalonm_get_cpm(freq[0]);
tmp = be32toh(tmp);
memcpy(send_pkg.data, &tmp, 4);
tmp = avalonm_get_cpm(freq[1]);
tmp = be32toh(tmp);
memcpy(send_pkg.data + 4, &tmp, 4);
tmp = avalonm_get_cpm(freq[2]);
tmp = be32toh(tmp);
memcpy(send_pkg.data + 8, &tmp, 4);

avalonm_init_pkg(&send_pkg, AVAM_P_SET_FREQ, 1, 1);


send_pkg.opt = asic_index;
avalonm_send_pkg(avalonm, &send_pkg);
applog(LOG_NOTICE, "%s-%d: Avalonm set asic_index %d freq %d,%d,%d",
avalonm->drv->name, avalonm->device_id,
asic_index,
freq[0],
freq[1],
freq[2]);
}

static void avalonm_set_voltage(struct cgpu_info *avalonm)


{
struct avalonm_info *info = avalonm->device_data;
struct avalonm_pkg send_pkg;
uint16_t tmp;

if (!info->power_on && (info->set_voltage == info->opt_voltage))


return;

info->set_voltage = info->opt_voltage;
memset(send_pkg.data, 0, AVAM_P_DATA_LEN);
/* Use shifter to set voltage */
tmp = info->set_voltage;
tmp = encode_voltage(tmp);
tmp = htobe16(tmp);
memcpy(send_pkg.data, &tmp, 2);

/* Package the data */


avalonm_init_pkg(&send_pkg, AVAM_P_SET_VOLT, 1, 1);
avalonm_send_pkg(avalonm, &send_pkg);
applog(LOG_NOTICE, "%s-%d: Avalonm set volt %d",
avalonm->drv->name, avalonm->device_id,
info->set_voltage);

if (info->power_on)
cgsleep_ms(1000);
info->power_on = 0;
}

static inline void avalonm_detect(bool __maybe_unused hotplug)


{
usb_detect(&avalonm_drv, avalonm_detect_one);
}

static int avalonm_get_reports(void *userdata)


{
struct cgpu_info *avalonm = (struct cgpu_info *)userdata;
struct avalonm_info *info = avalonm->device_data;
struct thr_info *thr = info->thr;
struct avalonm_pkg send_pkg;
struct avalonm_ret ar;
int ret = 0;

memset(send_pkg.data, 0, AVAM_P_DATA_LEN);
avalonm_init_pkg(&send_pkg, AVAM_P_POLLING, 1, 1);
ret = avalonm_xfer_pkg(avalonm, &send_pkg, &ar);
if (ret == AVAM_SEND_OK) {
ret = decode_pkg(thr, &ar);
}

return ret;
}

static 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;
}
}

static int64_t avalonm_scanhash(struct thr_info *thr)


{
struct cgpu_info *avalonm = thr->cgpu;
struct avalonm_info *info = avalonm->device_data;
int64_t hash_count, ms_timeout;
struct timeval current;
double device_tdiff;
uint32_t i, j, tmp;

/* Half nonce range */


ms_timeout = 0x80000000ll / 1000;

/* Wait until avalon_send_tasks signals us that it has completed


* 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);

hash_count = info->nonce_cnts++;
info->nonce_cnts = 0;
cgtime(&current);
device_tdiff = tdiff(&current, &(info->lastadj));
if (opt_avalonm_autof && (device_tdiff > AVAM_DEFAULT_ADJ_INTERVAL ||
device_tdiff < 0)) {
copy_time(&info->lastadj, &current);

for (i = 0; i < AVAM_DEFAULT_ASIC_COUNT; i++) {


tmp = 0;
for (j = 0; j < AVAM_DEFAULT_MOV_TIMES; j++)
tmp += info->hw_work_i[i][j];

if (tmp > AVAM_HW_HIGH && (info->opt_freq[i][0] >


opt_avalonm_freq[0] - (uint32_t)(12 * 12.5))) {
if ((info->opt_freq[i][0] == AVAM_DEFAULT_FREQUENCY_MIN) &&
(info->opt_freq[i][1] ==
AVAM_DEFAULT_FREQUENCY_MIN) &&
(info->opt_freq[i][2] ==
AVAM_DEFAULT_FREQUENCY_MIN))
continue;

if (info->opt_freq[i][0] * 10 % 125)
info->opt_freq[i][0] -= 13;
else
info->opt_freq[i][0] -= 12;

if (info->opt_freq[i][1] * 10 % 125)
info->opt_freq[i][1] -= 13;
else
info->opt_freq[i][1] -= 12;

if (info->opt_freq[i][2] * 10 % 125)
info->opt_freq[i][2] -= 13;
else
info->opt_freq[i][2] -= 12;

if (info->opt_freq[i][0] < AVAM_DEFAULT_FREQUENCY_MIN)


info->opt_freq[i][0] = AVAM_DEFAULT_FREQUENCY_MIN;

if (info->opt_freq[i][1] < AVAM_DEFAULT_FREQUENCY_MIN)


info->opt_freq[i][1] = AVAM_DEFAULT_FREQUENCY_MIN;

if (info->opt_freq[i][2] < AVAM_DEFAULT_FREQUENCY_MIN)


info->opt_freq[i][2] = AVAM_DEFAULT_FREQUENCY_MIN;

FLAG_SET(info->freq_set, i + 1);
applog(LOG_NOTICE, "%s-%d: Automatic decrease [%d] freq to
%d,%d,%d",
avalonm->drv->name, avalonm->device_id, i,
info->opt_freq[i][0],
info->opt_freq[i][1],
info->opt_freq[i][2]);
}

if (tmp < AVAM_HW_LOW && (info->opt_freq[i][0] <


opt_avalonm_freq[0] + (uint32_t)(8 * 12.5))) {
if ((info->opt_freq[i][0] == AVAM_DEFAULT_FREQUENCY_MAX) &&
(info->opt_freq[i][1] ==
AVAM_DEFAULT_FREQUENCY_MAX) &&
(info->opt_freq[i][2] ==
AVAM_DEFAULT_FREQUENCY_MAX))
continue;

if (info->opt_freq[i][0] * 10 % 125)
info->opt_freq[i][0] += 12;
else
info->opt_freq[i][0] += 13;

if (info->opt_freq[i][1] * 10 % 125)
info->opt_freq[i][1] += 12;
else
info->opt_freq[i][1] += 13;

if (info->opt_freq[i][2] * 10 % 125)
info->opt_freq[i][2] += 12;
else
info->opt_freq[i][2] += 13;

if (info->opt_freq[i][0] > AVAM_DEFAULT_FREQUENCY_MAX)


info->opt_freq[i][0] = AVAM_DEFAULT_FREQUENCY_MAX;

if (info->opt_freq[i][1] > AVAM_DEFAULT_FREQUENCY_MAX)


info->opt_freq[i][1] = AVAM_DEFAULT_FREQUENCY_MAX;

if (info->opt_freq[i][2] > AVAM_DEFAULT_FREQUENCY_MAX)


info->opt_freq[i][2] = AVAM_DEFAULT_FREQUENCY_MAX;

FLAG_SET(info->freq_set, i + 1);
applog(LOG_NOTICE, "%s-%d: Automatic increase [%d] freq to
%d,%d,%d",
avalonm->drv->name, avalonm->device_id, i,
info->opt_freq[i][0],
info->opt_freq[i][1],
info->opt_freq[i][2]);
}
}
}

return hash_count * 0xffffffffull;


}

static void avalonm_rotate_array(struct cgpu_info *avalonm, struct avalonm_info


*info)
{
mutex_lock(&info->qlock);
avalonm->queued = 0;
if (++avalonm->work_array >= AVAM_DEFAULT_ARRAY_SIZE)
avalonm->work_array = 0;
mutex_unlock(&info->qlock);
}

static void *avalonm_process_tasks(void *userdata)


{
char threadname[16];

struct cgpu_info *avalonm = userdata;


struct avalonm_info *info = avalonm->device_data;
struct work *work;
struct avalonm_pkg send_pkg;
int start_count, end_count, i, j, k, ret;
int avalon_get_work_count = info->asic_cnts;
struct timeval current;
double device_tdiff;

snprintf(threadname, sizeof(threadname), "%d/AvmProc", avalonm->device_id);


RenameThread(threadname);

while (likely(!avalonm->shutdown)) {
if (unlikely(avalonm->usbinfo.nodev)) {
applog(LOG_ERR, "%s-%d: Device disappeared, shutting down
thread",
avalonm->drv->name, avalonm->device_id);
goto out;
}

cgtime(&current);
device_tdiff = tdiff(&current, &(info->lasttime));
if (device_tdiff >= AVAM_DEFAULT_MOV_TIMES || device_tdiff < 0) {
copy_time(&info->lasttime, &current);
if (info->time_i++ >= AVAM_DEFAULT_MOV_TIMES)
info->time_i = 0;

for(i = 0; i < AVAM_DEFAULT_ASIC_COUNT; i++)


info->hw_work_i[i][info->time_i] = 0;
}

/* Give other threads a chance to acquire qlock. */


i = 0;
do {
cgsleep_ms(40);
} while (!avalonm->shutdown && avalonm->queued <
avalon_get_work_count);

mutex_lock(&info->qlock);

start_count = avalonm->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++) {


work = avalonm->works[i];
if (likely(j < avalonm->queued && avalonm->works[i] && j < (info-
>asic_cnts - opt_avalonm_ntime_offset))) {
/* Configuration */
avalonm_set_voltage(avalonm);

if (FLAG_GET(info->freq_set, 0)) {
avalonm_set_freq(avalonm, AVAM_ASIC_ALL, info-
>opt_freq[0]);
FLAG_CLEAR(info->freq_set, 0);
cgsleep_ms(20);
}

for (k = 1; k <= info->asic_cnts; k++) {


if (FLAG_GET(info->freq_set, k)) {
avalonm_set_freq(avalonm, k, info->opt_freq[k -
1]);
FLAG_CLEAR(info->freq_set, k);
cgsleep_ms(20);
}
}

/* P_WORK part 1: midstate */


memcpy(send_pkg.data, work->midstate, AVAM_P_DATA_LEN);
rev((void *)(send_pkg.data), AVAM_P_DATA_LEN);
avalonm_init_pkg(&send_pkg, AVAM_P_WORK, 1, 2);
hexdump(send_pkg.data, 32);
avalonm_send_pkg(avalonm, &send_pkg);
if (info->freq_update)
cgsleep_ms(300);

/* P_WORK part 2:
*
id(6)+reserved(2)+ntime(1)+fan(3)+led(4)+reserved(4)+data(12) */
memset(send_pkg.data, 0, AVAM_P_DATA_LEN);

UNPACK32(work->id, send_pkg.data);

/* always roll work 0 */


if (j == 0)
send_pkg.data[8] = opt_avalonm_ntime_offset;
else
send_pkg.data[8] = 0;

/* TODO led */
UNPACK32(0, send_pkg.data + 12);

memcpy(send_pkg.data + 20, work->data + 64, 12);


rev((void *)(send_pkg.data + 20), 12);
avalonm_init_pkg(&send_pkg, AVAM_P_WORK, 2, 2);
hexdump(send_pkg.data, 32);
avalonm_send_pkg(avalonm, &send_pkg);
if (info->freq_update)
cgsleep_ms(300);
}
}

mutex_unlock(&info->qlock);
avalonm_rotate_array(avalonm, info);

cgsem_post(&info->qsem);

/* little delay, let asics process more job */


if (!strncmp(info->ver, "3U", 2))
cgsleep_ms(400);

/* Get result */
do {
ret = avalonm_get_reports(avalonm);
cgsleep_ms(5);
} while (ret != AVAM_P_STATUS_M);

if (info->freq_update) {
applog(LOG_NOTICE, "%s-%d: avalonm_process_tasks freq change flag
%02x",
avalonm->drv->name, avalonm->device_id,
info->freq_update);
if (FLAG_GET(info->freq_update, 0)) {
avalonm_get_frequency(avalonm, 0);
FLAG_CLEAR(info->freq_update, 0);
}

for (i = 1; i <= info->asic_cnts; i++) {


if (FLAG_GET(info->freq_update, i)) {
avalonm_get_frequency(avalonm, i);
FLAG_CLEAR(info->freq_update, i);
}
}
}

cgsleep_ms(info->delay_ms);
}

out:
return NULL;
}

static bool avalonm_prepare(struct thr_info *thr)


{
struct cgpu_info *avalonm = thr->cgpu;
struct avalonm_info *info = avalonm->device_data;

free(avalonm->works);
avalonm->works = calloc(info->asic_cnts * sizeof(struct work *),
AVAM_DEFAULT_ARRAY_SIZE);
if (!avalonm->works)
quit(1, "Failed to calloc avalon miner works in avalonm_prepare");

info->thr = thr;
info->delay_ms = CAL_DELAY(AVAM_DEFAULT_FREQUENCY);
info->power_on = 1;

mutex_init(&info->lock);
mutex_init(&info->qlock);
cgsem_init(&info->qsem);

if (pthread_create(&info->process_thr, NULL, avalonm_process_tasks, (void


*)avalonm))
quit(1, "Failed to create avalonm process_thr");

return true;
}

static void avalonm_shutdown(struct thr_info *thr)


{
struct cgpu_info *avalonm = thr->cgpu;
struct avalonm_info *info = avalonm->device_data;

pthread_join(info->process_thr, NULL);

cgsem_destroy(&info->qsem);
mutex_destroy(&info->qlock);
mutex_destroy(&info->lock);
free(avalonm->works);
avalonm->works = NULL;
}
char *set_avalonm_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 < AVAM_DEFAULT_FREQUENCY_MIN || val1 >
AVAM_DEFAULT_FREQUENCY_MAX)
return "Invalid value1 passed to set_avalonm_freq";
}

if (colon1 && *colon1) {


colon2 = strchr(colon1, ':');
if (colon2)
*(colon2++) = '\0';

if (*colon1) {
val2 = atoi(colon1);
if (val2 < AVAM_DEFAULT_FREQUENCY_MIN || val2 >
AVAM_DEFAULT_FREQUENCY_MAX)
return "Invalid value2 passed to set_avalonm_freq";
}

if (colon2 && *colon2) {


val3 = atoi(colon2);
if (val3 < AVAM_DEFAULT_FREQUENCY_MIN || val3 >
AVAM_DEFAULT_FREQUENCY_MAX)
return "Invalid value3 passed to set_avalonm_freq";
}
}

if (!val1)
val3 = val2 = val1 = AVAM_DEFAULT_FREQUENCY;

if (!val2)
val3 = val2 = val1;

if (!val3)
val3 = val2;

opt_avalonm_freq[0] = val1;
opt_avalonm_freq[1] = val2;
opt_avalonm_freq[2] = val3;
applog(LOG_NOTICE, "Update all asic frequency to %d",
(opt_avalonm_freq[0] * 4 + opt_avalonm_freq[1] * 4 +
opt_avalonm_freq[2]) / 9);

return NULL;
}
char *set_avalonm_device_freq(struct cgpu_info *avalonm, char *arg)
{
struct avalonm_info *info = avalonm->device_data;
char *colon1, *colon2;
int val1 = 0, val2 = 0, val3 = 0;
int asic_index = AVAM_ASIC_ALL;
uint8_t i;

if (!(*arg))
return NULL;

colon1 = strchr(arg, '-');


if (colon1) {
sscanf(arg, "%d-", &asic_index);
arg = colon1 + 1;
if (asic_index < 0 || asic_index > info->asic_cnts) {
applog(LOG_ERR, "invalid asic index: %d, valid range 0-%d",
asic_index, info->asic_cnts);
return "Invalid asic index to set_avalonm_freq";
}
}

colon1 = strchr(arg, ':');


if (colon1)
*(colon1++) = '\0';

if (*arg) {
val1 = atoi(arg);
if (val1 < AVAM_DEFAULT_FREQUENCY_MIN || val1 >
AVAM_DEFAULT_FREQUENCY_MAX)
return "Invalid value1 passed to set_avalonm_freq";
}

if (colon1 && *colon1) {


colon2 = strchr(colon1, ':');
if (colon2)
*(colon2++) = '\0';

if (*colon1) {
val2 = atoi(colon1);
if (val2 < AVAM_DEFAULT_FREQUENCY_MIN || val2 >
AVAM_DEFAULT_FREQUENCY_MAX)
return "Invalid value2 passed to set_avalonm_freq";
}

if (colon2 && *colon2) {


val3 = atoi(colon2);
if (val3 < AVAM_DEFAULT_FREQUENCY_MIN || val3 >
AVAM_DEFAULT_FREQUENCY_MAX)
return "Invalid value3 passed to set_avalonm_freq";
}
}

if (!val1)
val3 = val2 = val1 = AVAM_DEFAULT_FREQUENCY;

if (!val2)
val3 = val2 = val1;
if (!val3)
val3 = val2;

if (!asic_index) {
for (i = 0; i < info->asic_cnts; i++) {
info->opt_freq[i][0] = val1;
info->opt_freq[i][1] = val2;
info->opt_freq[i][2] = val3;
}
FLAG_SET(info->freq_set, AVAM_ASIC_ALL);
applog(LOG_NOTICE, "Update all asic frequency to %d",
(val1 * 4 + val2 * 4 + val3) / 9);
}

if (asic_index) {
info->opt_freq[asic_index - 1][0] = val1;
info->opt_freq[asic_index - 1][1] = val2;
info->opt_freq[asic_index - 1][2] = val3;

FLAG_SET(info->freq_set, asic_index);
applog(LOG_NOTICE, "Update asic %d frequency to %d",
asic_index - 1,
(val1 * 4 + val2 * 4 + val3) / 9);
}

return NULL;
}

char *set_avalonm_voltage(char *arg)


{
int val, ret;

ret = sscanf(arg, "%d", &val);


if (ret < 1)
return "No values passed to avalonm-voltage";

if (val < AVAM_DEFAULT_VOLTAGE_MIN || val > AVAM_DEFAULT_VOLTAGE_MAX)


return "Invalid value passed to avalonm-voltage";

opt_avalonm_voltage = val;

return NULL;
}

char *set_avalonm_device_voltage(struct cgpu_info *avalonm, char *arg)


{
struct avalonm_info *info = avalonm->device_data;
int val, ret;

ret = sscanf(arg, "%d", &val);


if (ret < 1)
return "No values passed to avalonm-voltage";

if (val < AVAM_DEFAULT_VOLTAGE_MIN || val > AVAM_DEFAULT_VOLTAGE_MAX)


return "Invalid value passed to avalonm-voltage";

info->opt_voltage = val;
applog(LOG_NOTICE, "%s-%d: Update voltage to %d",
avalonm->drv->name, avalonm->device_id, info->opt_voltage);
return NULL;
}

static char *avalonm_set_device(struct cgpu_info *avalonm, char *option, char


*setting, char *replybuf)
{
if (strcasecmp(option, "help") == 0) {
sprintf(replybuf, "frequency|voltage");
return replybuf;
}

if (strcasecmp(option, "frequency") == 0) {
if (!setting || !*setting) {
sprintf(replybuf, "missing frequency value");
return replybuf;
}

if (set_avalonm_device_freq(avalonm, setting)) {
sprintf(replybuf, "invalid frequency value, valid range %d-%d",
AVAM_DEFAULT_FREQUENCY_MIN, AVAM_DEFAULT_FREQUENCY_MAX);
return replybuf;
}

return NULL;
}

if (strcasecmp(option, "voltage") == 0) {
if (!setting || !*setting) {
sprintf(replybuf, "missing voltage value");
return replybuf;
}

if (set_avalonm_device_voltage(avalonm, setting)) {
sprintf(replybuf, "invalid voltage value, valid range %d-%d",
AVAM_DEFAULT_VOLTAGE_MIN, AVAM_DEFAULT_VOLTAGE_MAX);
return replybuf;
}

return NULL;
}

sprintf(replybuf, "Unknown option: %s", option);


return replybuf;
}

#define STATBUFLEN 512


static struct api_data *avalonm_api_stats(struct cgpu_info *cgpu)
{
struct api_data *root = NULL;
struct avalonm_info *info = cgpu->device_data;
char buf[256];
char statbuf[STATBUFLEN];
uint32_t i, j, tmp;
struct timeval now;

memset(statbuf, 0, STATBUFLEN);

sprintf(buf, "VER[%s]", info->ver);


strcat(statbuf, buf);

sprintf(buf, " DNA[%02x%02x%02x%02x%02x%02x%02x%02x]",


info->dna[0],
info->dna[1],
info->dna[2],
info->dna[3],
info->dna[4],
info->dna[5],
info->dna[6],
info->dna[7]);
strcat(statbuf, buf);

cgtime(&now);
sprintf(buf, " Elapsed[%.0f]", tdiff(&now, &(info->elapsed)));
strcat(statbuf, buf);

sprintf(buf, " Chips[%d]", info->asic_cnts);


strcat(statbuf, buf);

sprintf(buf, " Crc[%d]", info->crcerr_cnt);


strcat(statbuf, buf);

sprintf(buf, " Speed[%d]", info->spi_speed);


strcat(statbuf, buf);

if (!strncmp(info->ver, "3U", 2))


sprintf(buf, " Vol[%.2f]", (float)info->get_voltage);
else
sprintf(buf, " Vol[%.4f]", (float)info->get_voltage / 10000);
strcat(statbuf, buf);

if (!strncmp(info->ver, "3U", 2))


sprintf(buf, " V_CORE[%.2f]", convert_voltage(info->adc[0], 1));
else
sprintf(buf, " V12[%.2f]", convert_voltage(info->adc[0], (10 / 110)));
strcat(statbuf, buf);

if (!strncmp(info->ver, "3U", 2))


sprintf(buf, " T[%d]", info->adc[1]);
else
sprintf(buf, " TC[%.2f]", convert_temp(info->adc[1]));
strcat(statbuf, buf);

if (!strncmp(info->ver, "3U", 2)) {


sprintf(buf, " V0_9[%.2f]", convert_voltage(info->adc[2] & 0xffff, 1));
strcat(statbuf, buf);
sprintf(buf, " V1_8[%.2f]", convert_voltage((info->adc[2] >> 16) &
0xffff, 1));
} else
sprintf(buf, " TF[%.2f]", convert_temp(info->adc[2]));
strcat(statbuf, buf);

if (!strncmp(info->ver, "3U", 2)) {


sprintf(buf, " Freq[%d]", info->get_frequency[0][0]);
strcat(statbuf, buf);
} else {
strcat(statbuf, " Freq[");
for (i = 0; i < info->asic_cnts; i++) {
sprintf(buf, "%d %d %d ",
info->get_frequency[i][0],
info->get_frequency[i][1],
info->get_frequency[i][2]);
strcat(statbuf, buf);
}
statbuf[strlen(statbuf) - 1] = ']';
}

strcat(statbuf, " HW[");


for (i = 0; i < info->asic_cnts; i++) {
sprintf(buf, "%d ", info->hw_work[i]);
strcat(statbuf, buf);
}
statbuf[strlen(statbuf) - 1] = ']';

/* simple moving the sum of hardware error */


strcat(statbuf, " SHW[");
for (i = 0; i < info->asic_cnts; i++) {
tmp = 0;
for (j = 0; j < AVAM_DEFAULT_MOV_TIMES; j++)
tmp += info->hw_work_i[i][j];

sprintf(buf, "%d ", tmp);


strcat(statbuf, buf);
}
statbuf[strlen(statbuf) - 1] = ']';

strcat(statbuf, " MW[");


for (i = 0; i < info->asic_cnts; i++) {
sprintf(buf, "%ld ", info->matching_work[i]);
strcat(statbuf, buf);
}
statbuf[strlen(statbuf) - 1] = ']';

sprintf(buf, " Led[%d]", info->led_status);


strcat(statbuf, buf);

sprintf(buf, " PG[%d]", info->power_good);


strcat(statbuf, buf);

root = api_add_string(root, "AVAM Dev", statbuf, true);

sprintf(buf, "%d %d %d",


info->usbfifo_cnt,
info->workfifo_cnt,
info->noncefifo_cnt);

root = api_add_string(root, "AVAM Fifo", buf, true);


root = api_add_uint8(root, "AVAM ntime", &opt_avalonm_ntime_offset, true);
root = api_add_bool(root, "Automatic Frequency", &opt_avalonm_autof, true);
return root;
}

static void avalonm_statline_before(char *buf, size_t bufsiz, struct cgpu_info


*avalonm)
{
struct avalonm_info *info = avalonm->device_data;
int frequency;
if (!strncmp(info->ver, "3U", 2))
tailsprintf(buf, bufsiz, "%4dMhz %.2fV", info->get_frequency[0][0],
(float)info->get_voltage);
else {
frequency = (info->set_frequency[0][0] * 4 + info->set_frequency[0][1]
* 4 + info->set_frequency[0][2]) / 9;
tailsprintf(buf, bufsiz, "%4dMhz %.4fV", frequency, (float)info-
>get_voltage / 10000);
}
}

/* 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 avalonm_fill(struct cgpu_info *avalonm)
{
struct avalonm_info *info = avalonm->device_data;
int subid, slot, ac;
struct work *work;
bool ret = true;

ac = info->asic_cnts;
mutex_lock(&info->qlock);
if (avalonm->queued >= ac)
goto out_unlock;
work = get_queued(avalonm);
if (unlikely(!work)) {
ret = false;
goto out_unlock;
}
subid = avalonm->queued++;
work->subid = subid;
slot = avalonm->work_array * ac + subid;
if (likely(avalonm->works[slot]))
work_completed(avalonm, avalonm->works[slot]);
avalonm->works[slot] = work;
if (avalonm->queued < ac)
ret = false;
out_unlock:
mutex_unlock(&info->qlock);

return ret;
}

static void avalonm_flush_work(struct cgpu_info *avalonm)


{

struct avalonm_info *info = avalonm->device_data;

/* Will overwrite any work queued. Do this unlocked since it's just
* changing a single non-critical value and prevents deadlocks */
avalonm->queued = 0;

/* Signal main loop we need more work */


cgsem_post(&info->qsem);
}

struct device_drv avalonm_drv = {


.drv_id = DRIVER_avalonm,
.dname = "avalonm",
.name = "AVM",
.set_device = avalonm_set_device,
.get_api_stats = avalonm_api_stats,
.get_statline_before = avalonm_statline_before,
.drv_detect = avalonm_detect,
.thread_prepare = avalonm_prepare,

.hash_work = hash_queued_work,
.scanwork = avalonm_scanhash,
.queue_full = avalonm_fill,
.flush_work = avalonm_flush_work,

.thread_shutdown = avalonm_shutdown,
};

You might also like