Files
NecroHash/src/cgminer-gc3355/driver-gridseed.c
2026-01-21 13:26:02 +00:00

740 lines
20 KiB
C
Raw Blame History

This file contains invisible Unicode characters
This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
/*
* Copyright 2013 Faster <develop@gridseed.com>
* Copyright 2012-2013 Andrew Smith
* Copyright 2012 Luke Dashjr
* Copyright 2012 Con Kolivas <kernel@kolivas.org>
*
* 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 <limits.h>
#include <pthread.h>
#include <stdint.h>
#include <stdio.h>
#include <strings.h>
#include <sys/time.h>
#include <unistd.h>
#include "miner.h"
#include "usbutils.h"
#include "util.h"
#ifdef WIN32
#include "compat.h"
#include <windows.h>
#include <winsock2.h>
#include <io.h>
#else
#include <sys/select.h>
#include <termios.h>
#include <sys/stat.h>
#include <fcntl.h>
#endif /* WIN32 */
#include "elist.h"
#include "miner.h"
#include "usbutils.h"
#include "driver-gridseed.h"
#include "hexdump.c"
#include "util.h"
static const char *gridseed_version = "v3.8.5.20140210.02";
typedef struct s_gridseed_info {
enum sub_ident ident;
uint32_t fw_version;
struct timeval scanhash_time;
int nonce_count[GRIDSEED_MAX_CHIPS]; // per chip
int error_count[GRIDSEED_MAX_CHIPS]; // per chip
// options
int baud;
int freq;
unsigned char freq_cmd[8];
int chips; //chips per module
int voltage;
int per_chip_stats;
} GRIDSEED_INFO;
static const char *str_reset[] = {
"55AAC000808080800000000001000000", // Chip reset
NULL
};
static const char *str_init[] = {
"55AAC000C0C0C0C00500000001000000",
"55AAEF020000000000000000000000000000000000000000",
"55AAEF3020000000",
NULL
};
static const char *str_ltc_reset[] = {
"55AA1F2816000000",
"55AA1F2817000000",
NULL
};
static const char *str_btc_reset[] = {
"55AA1F2800000000",
"55AA1F2800000000",
NULL
};
#ifdef WIN32
static void set_text_color(WORD color)
{
SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE), color);
}
#endif
/*---------------------------------------------------------------------------------------*/
static void _transfer(struct cgpu_info *gridseed, 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(gridseed, request_type, bRequest, wValue, wIndex, data, siz, cmd);
applog(LOG_DEBUG, "%s: cgid %d %s got err %d",
gridseed->drv->name, gridseed->cgminer_id,
usb_cmdname(cmd), err);
}
static int gc3355_write_data(struct cgpu_info *gridseed, unsigned char *data, int size)
{
int err, wrote;
#if 1
if (!opt_quiet && opt_debug) {
int i;
#ifndef WIN32
printf(" >>> %d : ", size);
#else
set_text_color(FOREGROUND_RED|FOREGROUND_GREEN);
printf(" >>> %d : ", size);
set_text_color(FOREGROUND_RED|FOREGROUND_GREEN|FOREGROUND_BLUE);
#endif
for(i=0; i<size; i++) {
printf("%02x", data[i]);
if (i==3)
printf(" ");
}
printf("\n");
}
#endif
err = usb_write(gridseed, data, size, &wrote, C_SENDWORK);
if (err != LIBUSB_SUCCESS || wrote != size)
return -1;
return 0;
}
static int gc3355_get_data(struct cgpu_info *gridseed, unsigned char *buf, int size)
{
unsigned char *p;
int readcount;
int err = 0, amount;
readcount = size;
p = buf;
while(readcount > 0) {
err = usb_read_once(gridseed, p, readcount, &amount, C_GETRESULTS);
if (err) {
if (readcount != size)
applog(LOG_ERR, "Timed out after receiving partial data from %i",
gridseed->cgminer_id);
break;
}
readcount -= amount;
p += amount;
}
#if 1
if (!opt_quiet && opt_debug && (size-readcount) > 0) {
int i;
#ifndef WIN32
printf(" <<< %d : ", size);
#else
set_text_color(FOREGROUND_RED);
printf(" <<< %d : ", (size-readcount));
set_text_color(FOREGROUND_RED|FOREGROUND_GREEN|FOREGROUND_BLUE);
#endif
for(i=0; i<(size-readcount); i++) {
printf("%02x", buf[i]);
if ((i+1) % 4 == 0)
printf(" ");
}
printf("\n");
}
#endif
return err;
}
static void gc3355_send_cmds(struct cgpu_info *gridseed, const char *cmds[])
{
unsigned char ob[512];
int i;
for(i=0; ; i++) {
if (cmds[i] == NULL)
break;
hex2bin(ob, cmds[i], sizeof(ob));
gc3355_write_data(gridseed, ob, strlen(cmds[i])/2);
cgsleep_ms(GRIDSEED_COMMAND_DELAY);
}
}
static bool gc3355_read_register(struct cgpu_info *gridseed, uint32_t reg_addr,
uint32_t *reg_value) {
GRIDSEED_INFO *info = (GRIDSEED_INFO*)(gridseed->device_data);
char cmd[16] = "\x55\xaa\xc0\x01";
uint32_t reg_len = 4;
unsigned char buf[4];
if (info->fw_version != 0x01140113) {
applog(LOG_ERR, "Can't read registers; incompatible firmware %08X on %i",
info->fw_version, gridseed->device_id);
return false;
}
*(uint32_t *)(cmd + 4) = htole32(reg_addr);
*(uint32_t *)(cmd + 8) = htole32(reg_len);
*(uint32_t *)(cmd + 12) = htole32(reg_len);
if (gc3355_write_data(gridseed, cmd, sizeof(cmd)) != 0) {
applog(LOG_DEBUG, "Failed to write data to %i", gridseed->device_id);
return false;
}
cgsleep_ms(GRIDSEED_COMMAND_DELAY);
if (gc3355_get_data(gridseed, buf, 4)) {
applog(LOG_DEBUG, "No response from %i", gridseed->device_id);
return false;
}
*reg_value = le32toh(*(uint32_t *)buf);
return true;
}
static bool gc3355_write_register(struct cgpu_info *gridseed, uint32_t reg_addr,
uint32_t reg_value) {
GRIDSEED_INFO *info = (GRIDSEED_INFO*)(gridseed->device_data);
char cmd[16] = "\x55\xaa\xc0\x02";
uint32_t reg_len = 4;
unsigned char buf[4];
if (info->fw_version != 0x01140113) {
applog(LOG_ERR, "Can't write registers; incompatible firmware %08X on %i",
info->fw_version, gridseed->device_id);
return false;
}
*(uint32_t *)(cmd + 4) = htole32(reg_addr);
*(uint32_t *)(cmd + 8) = htole32(reg_value);
*(uint32_t *)(cmd + 12) = htole32(reg_len);
if (gc3355_write_data(gridseed, cmd, sizeof(cmd)) != 0) {
applog(LOG_DEBUG, "Failed to write data to %i", gridseed->device_id);
return false;
}
cgsleep_ms(GRIDSEED_COMMAND_DELAY);
if (gc3355_get_data(gridseed, buf, 4)) {
applog(LOG_DEBUG, "No response from %i", gridseed->device_id);
return false;
}
return true;
}
static void gc3355_set_core_freq(struct cgpu_info *gridseed)
{
GRIDSEED_INFO *info = (GRIDSEED_INFO*)(gridseed->device_data);
gc3355_write_data(gridseed, info->freq_cmd, sizeof(info->freq_cmd));
cgsleep_ms(GRIDSEED_COMMAND_DELAY);
applog(LOG_NOTICE, "Set GC3355 core frequency to %d MHz", info->freq);
}
static void gc3355_switch_voltage(struct cgpu_info *gridseed) {
uint32_t reg_value;
// Put GPIOA pin 5 into general function, 50 MHz output.
if (!gc3355_read_register(gridseed, GRIDSEED_GPIOA_BASE + GRIDSEED_CRL_OFFSET, &reg_value)) {
applog(LOG_DEBUG, "Failed to read GPIOA CRL register from %i", gridseed->device_id);
return;
}
reg_value = (reg_value & 0xff0fffff) | 0x00300000;
if (!gc3355_write_register(gridseed, GRIDSEED_GPIOA_BASE + GRIDSEED_CRL_OFFSET, reg_value)) {
applog(LOG_DEBUG, "Failed to write GPIOA CRL register from %i", gridseed->device_id);
return;
}
// Set GPIOA pin 5 high.
if (!gc3355_read_register(gridseed, GRIDSEED_GPIOA_BASE + GRIDSEED_ODR_OFFSET, &reg_value)) {
applog(LOG_DEBUG, "Failed to read GPIOA ODR register from %i", gridseed->device_id);
return;
}
reg_value |= 0x00000020;
if (!gc3355_write_register(gridseed, GRIDSEED_GPIOA_BASE + GRIDSEED_ODR_OFFSET, reg_value)) {
applog(LOG_DEBUG, "Failed to write GPIOA ODR register from %i", gridseed->device_id);
return;
}
applog(LOG_NOTICE, "Switched GC3355 voltage to alternate voltage");
}
static void gc3355_init(struct cgpu_info *gridseed, GRIDSEED_INFO *info)
{
unsigned char buf[512];
int amount;
applog(LOG_NOTICE, "DEBUG: gc3355_init called. opt_scrypt is: %d", opt_scrypt);
applog(LOG_NOTICE, "System reseting");
gc3355_send_cmds(gridseed, str_reset);
cgsleep_ms(200);
usb_buffer_clear(gridseed);
usb_read_timeout(gridseed, buf, sizeof(buf), &amount, 10, C_GETRESULTS);
applog(LOG_NOTICE, "DEBUG: Sending str_init");
gc3355_send_cmds(gridseed, str_init);
if (opt_scrypt) {
applog(LOG_NOTICE, "DEBUG: Sending str_ltc_reset (Scrypt Mode)");
gc3355_send_cmds(gridseed, str_ltc_reset);
} else {
applog(LOG_NOTICE, "DEBUG: Sending str_btc_reset (SHA256 Mode)");
gc3355_send_cmds(gridseed, str_btc_reset);
}
gc3355_set_core_freq(gridseed);
if (info->voltage)
gc3355_switch_voltage(gridseed);
}
static bool get_options(GRIDSEED_INFO *info, char *options)
{
char *ss, *p, *end, *comma, *colon;
int tmp, pll_r = 0, pll_f = 0, pll_od = 0;
if (options == NULL)
return false;
applog(LOG_NOTICE, "GridSeed options: '%s'", options);
ss = strdup(options);
p = ss;
end = p + strlen(p);
another:
comma = strchr(p, ',');
if (comma != NULL)
*comma = '\0';
colon = strchr(p, '=');
if (colon == NULL)
goto next;
*colon = '\0';
tmp = atoi(colon+1);
if (strcasecmp(p, "baud")==0) {
info->baud = (tmp != 0) ? tmp : info->baud;
}
else if (strcasecmp(p, "freq")==0) {
info->freq = tmp;
}
else if (strcasecmp(p, "pll_r")==0) {
pll_r = (tmp != 0) ? tmp : pll_r;
pll_r = MAX(0, MIN(31, pll_r));
}
else if (strcasecmp(p, "pll_f")==0) {
pll_f = (tmp != 0) ? tmp : pll_f;
pll_f = MAX(0, MIN(127, pll_f));
}
else if (strcasecmp(p, "pll_od")==0) {
pll_od = (tmp != 0) ? tmp : pll_od;
pll_od = MAX(0, MIN(4, pll_od));
}
else if (strcasecmp(p, "chips")==0) {
info->chips = (tmp != 0) ? tmp : info->chips;
info->chips = MAX(0, MIN(GRIDSEED_MAX_CHIPS, info->chips));
}
else if (strcasecmp(p, "voltage")==0) {
info->voltage = (tmp != 0) ? tmp : info->voltage;
}
else if (strcasecmp(p, "per_chip_stats")==0) {
info->per_chip_stats = (tmp != 0) ? tmp : info->per_chip_stats;
}
next:
if (comma != NULL) {
p = comma + 1;
if (p < end)
goto another;
}
free(ss);
if (pll_r == 0 && pll_f == 0 && pll_od == 0) {
// Support frequency increments of 25.
pll_f = info->freq / GRIDSEED_F_IN - 1;
pll_f = MAX(0, MIN(127, pll_f));
}
int f_ref = GRIDSEED_F_IN / (pll_r + 1);
int f_vco = f_ref * (pll_f + 1);
int f_out = f_vco / (1 << pll_od);
int pll_bs = (f_out >= 500) ? 1 : 0;
int cfg_pm = 1, pll_clk_gate = 1;
uint32_t cmd = (cfg_pm << 0) | (pll_clk_gate << 2) | (pll_r << 16) |
(pll_f << 21) | (pll_od << 28) | (pll_bs << 31);
info->freq = f_out;
memcpy(info->freq_cmd, "\x55\xaa\xef\x00", 4);
*(uint32_t *)(info->freq_cmd + 4) = htole32(cmd);
return true;
}
static int gridseed_cp210x_init(struct cgpu_info *gridseed, int interface)
{
// Enable the UART
transfer(gridseed, CP210X_TYPE_OUT, CP210X_REQUEST_IFC_ENABLE, CP210X_VALUE_UART_ENABLE,
interface, C_ENABLE_UART);
if (gridseed->usbinfo.nodev)
return -1;
// Set data control
transfer(gridseed, CP210X_TYPE_OUT, CP210X_REQUEST_DATA, CP210X_VALUE_DATA,
interface, C_SETDATA);
if (gridseed->usbinfo.nodev)
return -1;
// Set the baud
uint32_t data = CP210X_DATA_BAUD;
_transfer(gridseed, CP210X_TYPE_OUT, CP210X_REQUEST_BAUD, 0,
interface, &data, sizeof(data), C_SETBAUD);
return 0;
}
static int gridseed_ftdi_init(struct cgpu_info *gridseed, int interface)
{
int err;
// Reset
err = usb_transfer(gridseed, FTDI_TYPE_OUT, FTDI_REQUEST_RESET,
FTDI_VALUE_RESET, interface, C_RESET);
applog(LOG_DEBUG, "%s%i: reset got err %d",
gridseed->drv->name, gridseed->device_id, err);
if (gridseed->usbinfo.nodev)
return -1;
// Set latency
err = usb_transfer(gridseed, FTDI_TYPE_OUT, FTDI_REQUEST_LATENCY,
GRIDSEED_LATENCY, interface, C_LATENCY);
applog(LOG_DEBUG, "%s%i: latency got err %d",
gridseed->drv->name, gridseed->device_id, err);
if (gridseed->usbinfo.nodev)
return -1;
// Set data
err = usb_transfer(gridseed, FTDI_TYPE_OUT, FTDI_REQUEST_DATA,
FTDI_VALUE_DATA_AVA, interface, C_SETDATA);
applog(LOG_DEBUG, "%s%i: data got err %d",
gridseed->drv->name, gridseed->device_id, err);
if (gridseed->usbinfo.nodev)
return -1;
// Set the baud
err = usb_transfer(gridseed, 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",
gridseed->drv->name, gridseed->device_id, err);
if (gridseed->usbinfo.nodev)
return -1;
// Set Modem Control
err = usb_transfer(gridseed, FTDI_TYPE_OUT, FTDI_REQUEST_MODEM,
FTDI_VALUE_MODEM, interface, C_SETMODEM);
applog(LOG_DEBUG, "%s%i: setmodemctrl got err %d",
gridseed->drv->name, gridseed->device_id, err);
if (gridseed->usbinfo.nodev)
return -1;
// Set Flow Control
err = usb_transfer(gridseed, FTDI_TYPE_OUT, FTDI_REQUEST_FLOW,
FTDI_VALUE_FLOW, interface, C_SETFLOW);
applog(LOG_DEBUG, "%s%i: setflowctrl got err %d",
gridseed->drv->name, gridseed->device_id, err);
if (gridseed->usbinfo.nodev)
return -1;
/* Avalon repeats the following */
// Set Modem Control
err = usb_transfer(gridseed, FTDI_TYPE_OUT, FTDI_REQUEST_MODEM,
FTDI_VALUE_MODEM, interface, C_SETMODEM);
applog(LOG_DEBUG, "%s%i: setmodemctrl 2 got err %d",
gridseed->drv->name, gridseed->device_id, err);
if (gridseed->usbinfo.nodev)
return -1;
// Set Flow Control
err = usb_transfer(gridseed, FTDI_TYPE_OUT, FTDI_REQUEST_FLOW,
FTDI_VALUE_FLOW, interface, C_SETFLOW);
applog(LOG_DEBUG, "%s%i: setflowctrl 2 got err %d",
gridseed->drv->name, gridseed->device_id, err);
if (gridseed->usbinfo.nodev)
return -1;
return 0;
}
static int gridseed_pl2303_init(struct cgpu_info *gridseed, int interface)
{
// Set Data Control
transfer(gridseed, PL2303_CTRL_OUT, PL2303_REQUEST_CTRL, PL2303_VALUE_CTRL,
interface, C_SETDATA);
if (gridseed->usbinfo.nodev)
return -1;
// Set Line Control
uint32_t ica_data[2] = { PL2303_VALUE_LINE0, PL2303_VALUE_LINE1 };
_transfer(gridseed, PL2303_CTRL_OUT, PL2303_REQUEST_LINE, PL2303_VALUE_LINE,
interface, &ica_data[0], PL2303_VALUE_LINE_SIZE, C_SETLINE);
if (gridseed->usbinfo.nodev)
return -1;
// Vendor
transfer(gridseed, PL2303_VENDOR_OUT, PL2303_REQUEST_VENDOR, PL2303_VALUE_VENDOR,
interface, C_VENDOR);
return 0;
}
static void gridseed_initialise(struct cgpu_info *gridseed, GRIDSEED_INFO *info)
{
int err, interface;
enum sub_ident ident;
if (gridseed->usbinfo.nodev)
return;
interface = usb_interface(gridseed);
ident = usb_ident(gridseed);
switch(ident) {
case IDENT_GSD:
err = 0;
break;
case IDENT_GSD1:
err = gridseed_cp210x_init(gridseed, interface);
break;
case IDENT_GSD2:
err = gridseed_ftdi_init(gridseed, interface);
break;
case IDENT_GSD3:
err = gridseed_pl2303_init(gridseed, interface);
break;
default:
quit(1, "gridseed_intialise() called with invalid %s cgid %i ident=%d",
gridseed->drv->name, gridseed->cgminer_id, ident);
}
if (err)
return;
}
static bool gridseed_detect_one(libusb_device *dev, struct usb_find_devices *found)
{
struct cgpu_info *gridseed;
GRIDSEED_INFO *info;
int err, wrote;
unsigned char rbuf[GRIDSEED_READ_SIZE];
#if 0
const char detect_cmd[] =
"55aa0f01"
"4a548fe471fa3a9a1371144556c3f64d"
"2500b4826008fe4bbf7698c94eba7946"
"ce22a72f4f6726141a0b3287eeeeeeee";
unsigned char detect_data[52];
#else
const char detect_cmd[] = "55aac000909090900000000001000000";
unsigned char detect_data[16];
#endif
gridseed = usb_alloc_cgpu(&gridseed_drv, GRIDSEED_MINER_THREADS);
if (!usb_init(gridseed, dev, found))
goto shin;
libusb_reset_device(gridseed->usbdev->handle);
info = (GRIDSEED_INFO*)calloc(sizeof(GRIDSEED_INFO), 1);
if (unlikely(!info))
quit(1, "Failed to calloc gridseed_info data");
gridseed->device_data = (void *)info;
info->baud = GRIDSEED_DEFAULT_BAUD;
info->freq = GRIDSEED_DEFAULT_FREQUENCY;
info->chips = GRIDSEED_DEFAULT_CHIPS;
info->voltage = 0;
info->per_chip_stats = 0;
memset(info->nonce_count, 0, sizeof(info->nonce_count));
memset(info->error_count, 0, sizeof(info->error_count));
get_options(info, opt_gridseed_options);
update_usb_stats(gridseed);
gridseed->usbdev->usb_type = USB_TYPE_STD;
gridseed_initialise(gridseed, info);
/* get MCU firmware version */
hex2bin(detect_data, detect_cmd, sizeof(detect_data));
if (gc3355_write_data(gridseed, detect_data, sizeof(detect_data))) {
applog(LOG_DEBUG, "Failed to write work data to %i, err %d",
gridseed->device_id, err);
goto unshin;
}
/* waiting for return */
if (gc3355_get_data(gridseed, rbuf, GRIDSEED_READ_SIZE)) {
applog(LOG_DEBUG, "No response from %i", gridseed->device_id);
goto unshin;
}
if (memcmp(rbuf, "\x55\xaa\xc0\x00\x90\x90\x90\x90", GRIDSEED_READ_SIZE-4) != 0) {
applog(LOG_DEBUG, "Bad response from %i",
gridseed->device_id);
goto unshin;
}
info->fw_version = le32toh(*(uint32_t *)(rbuf+8));
applog(LOG_NOTICE, "Device found, firmware version %08X, driver version %s",
info->fw_version, gridseed_version);
gc3355_init(gridseed, info);
if (!add_cgpu(gridseed))
goto unshin;
return true;
unshin:
usb_uninit(gridseed);
free(gridseed->device_data);
gridseed->device_data = NULL;
shin:
gridseed = usb_free_cgpu(gridseed);
return false;
}
static bool gridseed_send_task(struct cgpu_info *gridseed, struct work *work)
{
unsigned char cmd[156];
memcpy(cmd, "\x55\xaa\x1f\x00", 4);
memcpy(cmd+4, work->target, 32);
memcpy(cmd+36, work->midstate, 32);
memcpy(cmd+68, work->data, 80);
memcpy(cmd+148, "\xff\xff\xff\xff", 4); // nonce_max
memcpy(cmd+152, "\x12\x34\x56\x78", 4); // taskid
return (gc3355_write_data(gridseed, cmd, sizeof(cmd)) == 0);
}
/*========== functions for struct device_drv ===========*/
static void gridseed_detect(bool __maybe_unused hotplug)
{
usb_detect(&gridseed_drv, gridseed_detect_one);
}
static void gridseed_get_statline(char *buf, size_t siz, struct cgpu_info *gridseed) {
GRIDSEED_INFO *info = gridseed->device_data;
if (info->per_chip_stats) {
int i;
tailsprintf(buf, siz, " N:");
for (i = 0; i < info->chips; ++i) {
tailsprintf(buf, siz, " %d", info->nonce_count[i]);
if (info->error_count[i])
tailsprintf(buf, siz, "[%d]", info->error_count[i]);
}
}
}
static void gridseed_get_statline_before(char *buf, size_t siz, struct cgpu_info *gridseed) {
GRIDSEED_INFO *info = gridseed->device_data;
tailsprintf(buf, siz, "%s %4d MHz | ", gridseed->usbdev->serial_string, info->freq);
}
static bool gridseed_prepare_work(struct thr_info __maybe_unused *thr, struct work *work) {
struct cgpu_info *gridseed = thr->cgpu;
GRIDSEED_INFO *info = gridseed->device_data;
cgtime(&info->scanhash_time);
if (opt_scrypt)
gc3355_send_cmds(gridseed, str_ltc_reset);
else
gc3355_send_cmds(gridseed, str_btc_reset);
usb_buffer_clear(gridseed);
return gridseed_send_task(gridseed, work);
}
static int64_t gridseed_scanhash(struct thr_info *thr, struct work *work, int64_t __maybe_unused max_nonce)
{
struct cgpu_info *gridseed = thr->cgpu;
GRIDSEED_INFO *info = gridseed->device_data;
unsigned char buf[GRIDSEED_READ_SIZE];
int ret = 0;
struct timeval old_scanhash_time = info->scanhash_time;
int elapsed_ms;
while (!thr->work_restart && (ret = gc3355_get_data(gridseed, buf, GRIDSEED_READ_SIZE)) == 0) {
if (buf[0] == 0x55 || buf[1] == 0x20) {
uint32_t nonce = le32toh(*(uint32_t *)(buf+4));
uint32_t chip = nonce / ((uint32_t)0xffffffff / info->chips);
info->nonce_count[chip]++;
if (!submit_nonce(thr, work, nonce))
info->error_count[chip]++;
} else {
applog(LOG_ERR, "Unrecognized response from %i", gridseed->device_id);
return -1;
}
}
if (ret != 0 && ret != LIBUSB_ERROR_TIMEOUT) {
applog(LOG_ERR, "No response from %i", gridseed->device_id);
return -1;
}
cgtime(&info->scanhash_time);
elapsed_ms = ms_tdiff(&info->scanhash_time, &old_scanhash_time);
return GRIDSEED_HASH_SPEED * (double)elapsed_ms * (double)(info->freq * info->chips);
}
/* driver functions */
struct device_drv gridseed_drv = {
.drv_id = DRIVER_gridseed,
.dname = "gridseed",
.name = "GSD",
.drv_detect = gridseed_detect,
.get_statline = gridseed_get_statline,
.get_statline_before = gridseed_get_statline_before,
.prepare_work = gridseed_prepare_work,
.scanhash = gridseed_scanhash,
};