diff --git a/Makefile.am b/Makefile.am index 32364248c6..c1d13d7d2d 100644 --- a/Makefile.am +++ b/Makefile.am @@ -226,6 +226,7 @@ if HAS_ICARUS bfgminer_SOURCES += driver-icarus.c icarus-common.h bfgminer_SOURCES += driver-cairnsmore.c bfgminer_SOURCES += driver-erupter.c +bfgminer_SOURCES += driver-antminer.c endif if HAS_AVALON diff --git a/configure.ac b/configure.ac index d281c80d51..869760d3b3 100644 --- a/configure.ac +++ b/configure.ac @@ -378,7 +378,7 @@ if test "x$bitforce" = xyes; then fi AM_CONDITIONAL([HAS_BITFORCE], [test x$bitforce = xyes]) -driverlist="$driverlist icarus cairnsmore/icarus erupter/icarus" +driverlist="$driverlist icarus cairnsmore/icarus erupter/icarus antminer/icarus" AC_ARG_ENABLE([icarus], [AC_HELP_STRING([--disable-icarus],[Compile support for Icarus (default enabled)])], [icarus=$enableval], diff --git a/driver-antminer.c b/driver-antminer.c new file mode 100644 index 0000000000..cfeaa72248 --- /dev/null +++ b/driver-antminer.c @@ -0,0 +1,188 @@ +/* + * Copyright 2013 Luke Dashjr + * Copyright 2013 Nate Woolls + * 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. + */ + +#include "config.h" + +#include +#include +#include +#include +#include +#include + +#include "miner.h" +#include "icarus-common.h" +#include "lowlevel.h" +#include "lowl-vcom.h" +#include "deviceapi.h" +#include "logging.h" +#include "util.h" + +#define ANTMINER_IO_SPEED 115200 +#define ANTMINER_HASH_TIME 0.0000000004761 + +#define ANTMINER_STATUS_LEN 5 + +BFG_REGISTER_DRIVER(antminer_drv) + +static +bool antminer_detect_one(const char *devpath) +{ + struct device_drv *drv = &antminer_drv; + + struct ICARUS_INFO *info = calloc(1, sizeof(struct ICARUS_INFO)); + if (unlikely(!info)) + quit(1, "Failed to malloc ICARUS_INFO"); + + *info = (struct ICARUS_INFO){ + .baud = ANTMINER_IO_SPEED, + .Hs = ANTMINER_HASH_TIME, + .timing_mode = MODE_DEFAULT, + .read_size = 5, + }; + + if (!icarus_detect_custom(devpath, drv, info)) + { + free(info); + return false; + } + + info->read_count = 15; + + return true; +} + +static +bool antminer_lowl_probe(const struct lowlevel_device_info * const info) +{ + return vcom_lowl_probe_wrapper(info, antminer_detect_one); +} + +static +char *antminer_get_clock(struct cgpu_info *cgpu, char *replybuf) +{ + uint8_t rdreg_buf[4] = {0}; + unsigned char rebuf[ANTMINER_STATUS_LEN] = {0}; + + struct timeval tv_now; + + rdreg_buf[0] = 4; + rdreg_buf[0] |= 0x80; + rdreg_buf[1] = 0; //16-23 + rdreg_buf[2] = 0x04; // 8-15 + rdreg_buf[3] = crc5usb(rdreg_buf, 27); + + applog(LOG_DEBUG, "%"PRIpreprv": Get clock: %02x%02x%02x%02x", cgpu->proc_repr, rdreg_buf[0], rdreg_buf[1], rdreg_buf[2], rdreg_buf[3]); + + timer_set_now(&tv_now); + int err = icarus_write(cgpu->device_fd, rdreg_buf, sizeof(rdreg_buf)); + + if (err != 0) + { + sprintf(replybuf, "invalid send get clock: comms error (err=%d)", err); + return replybuf; + } + + applog(LOG_DEBUG, "%"PRIpreprv": Get clock: OK", cgpu->proc_repr); + + memset(rebuf, 0, sizeof(rebuf)); + err = icarus_gets(rebuf, cgpu->device_fd, &tv_now, NULL, 10, ANTMINER_STATUS_LEN); + + // Timeout is ok - checking specifically for an error here + if (err == ICA_GETS_ERROR) + { + sprintf(replybuf, "invalid recv get clock: comms error (err=%d)", err); + return replybuf; + } + + applog(LOG_DEBUG, "%"PRIpreprv": Get clock: %02x%02x%02x%02x%02x", cgpu->proc_repr, rebuf[0], rebuf[1], rebuf[2], rebuf[3], rebuf[4]); + + return NULL; +} + +static +char *antminer_set_clock(struct cgpu_info *cgpu, char *setting, char *replybuf) +{ + if (!setting || !*setting) + return "missing clock setting"; + + // For now we only allow hex values that use BITMAINtech's lookup table + // This means values should be prefixed with an x so that later we can + // accept and distinguish decimal values + if (setting[0] != 'x') + { + sprintf(replybuf, "invalid clock: '%s' data must be prefixed with an x", setting); + return replybuf; + } + + //remove leading character + char *hex_setting = setting + 1; + + uint8_t reg_data[4] = {0}; + + if (!hex2bin(reg_data, hex_setting, strlen(hex_setting) / 2)) + { + sprintf(replybuf, "invalid clock: '%s' data must be a hexidecimal value", hex_setting); + return replybuf; + } + + uint8_t cmd_buf[4] = {0}; + + 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] = crc5usb(cmd_buf, 27); + + applog(LOG_DEBUG, "%"PRIpreprv": Set clock: %02x%02x%02x%02x", cgpu->proc_repr, cmd_buf[0], cmd_buf[1], cmd_buf[2], cmd_buf[3]); + + int err = icarus_write(cgpu->device_fd, cmd_buf, sizeof(cmd_buf)); + + if (err != 0) + { + sprintf(replybuf, "invalid send clock: '%s' comms error (err=%d)", setting, err); + return replybuf; + } + + applog(LOG_DEBUG, "%"PRIpreprv": Set clock: OK", cgpu->proc_repr); + + // This is confirmed required in order for the clock change to "take" + cgsleep_ms(500); + + return antminer_get_clock(cgpu, replybuf); +} + +static +char *antminer_set_device(struct cgpu_info *cgpu, char *option, char *setting, char *replybuf) +{ + if (strcasecmp(option, "clock") == 0) + { + return antminer_set_clock(cgpu, setting, replybuf); + } + + sprintf(replybuf, "Unknown option: %s", option); + return replybuf; +} + +static +void antminer_drv_init() +{ + antminer_drv = icarus_drv; + antminer_drv.dname = "antminer"; + antminer_drv.name = "AMU"; + antminer_drv.lowl_probe = antminer_lowl_probe; + antminer_drv.set_device = antminer_set_device, + ++antminer_drv.probe_priority; +} + +struct device_drv antminer_drv = { + .drv_init = antminer_drv_init, +}; diff --git a/driver-cairnsmore.c b/driver-cairnsmore.c index 38c2a86073..4df51555ec 100644 --- a/driver-cairnsmore.c +++ b/driver-cairnsmore.c @@ -92,7 +92,7 @@ bool cairnsmore_supports_dynclock(int fd) .work_restart = false, .work_restart_notifier = {-1, -1}, }; - icarus_gets((unsigned char*)&nonce, fd, &tv_finish, &dummy, 1); + icarus_gets((unsigned char*)&nonce, fd, &tv_finish, &dummy, 1, ICARUS_DEFAULT_READ_SIZE); } applog(LOG_DEBUG, "Cairnsmore dynclock detection... Got %08x", nonce); switch (nonce) { diff --git a/driver-icarus.c b/driver-icarus.c index c2062ebbdf..63c75fad82 100644 --- a/driver-icarus.c +++ b/driver-icarus.c @@ -65,17 +65,15 @@ // The serial I/O speed - Linux uses a define 'B115200' in bits/termios.h #define ICARUS_IO_SPEED 115200 -// The size of a successful nonce read -#define ICARUS_READ_SIZE 4 +// The number of bytes in a nonce (always 4) +// This is NOT the read-size for the Icarus driver +// That is defined in ICARUS_INFO->read_size +#define ICARUS_NONCE_SIZE 4 -// Ensure the sizes are correct for the Serial read -#if (ICARUS_READ_SIZE != 4) -#error ICARUS_READ_SIZE must be 4 -#endif #define ASSERT1(condition) __maybe_unused static char sizeof_uint32_t_must_be_4[(condition)?1:-1] ASSERT1(sizeof(uint32_t) == 4); -#define ICARUS_READ_TIME(baud) ((double)ICARUS_READ_SIZE * (double)8.0 / (double)(baud)) +#define ICARUS_READ_TIME(baud, read_size) ((double)read_size * (double)8.0 / (double)(baud)) // Defined in deciseconds // There's no need to have this bigger, since the overhead/latency of extra work @@ -184,18 +182,13 @@ static void rev(unsigned char *s, size_t l) #define icarus_open2(devpath, baud, purge) serial_open(devpath, baud, ICARUS_READ_FAULT_DECISECONDS, purge) #define icarus_open(devpath, baud) icarus_open2(devpath, baud, false) -#define ICA_GETS_ERROR -1 -#define ICA_GETS_OK 0 -#define ICA_GETS_RESTART 1 -#define ICA_GETS_TIMEOUT 2 - -int icarus_gets(unsigned char *buf, int fd, struct timeval *tv_finish, struct thr_info *thr, int read_count) +int icarus_gets(unsigned char *buf, int fd, struct timeval *tv_finish, struct thr_info *thr, int read_count, int read_size) { ssize_t ret = 0; int rc = 0; int epollfd = -1; int epoll_timeout = ICARUS_READ_FAULT_DECISECONDS * 100; - int read_amount = ICARUS_READ_SIZE; + int read_amount = read_size; bool first = true; #ifdef HAVE_EPOLL @@ -282,7 +275,7 @@ int icarus_gets(unsigned char *buf, int fd, struct timeval *tv_finish, struct th } } -static int icarus_write(int fd, const void *buf, size_t bufLen) +int icarus_write(int fd, const void *buf, size_t bufLen) { size_t ret; @@ -557,6 +550,27 @@ static void get_options(int this_option_offset, struct ICARUS_INFO *info) } } +// Number of bytes remaining after reading a nonce from Icarus +int icarus_excess_nonce_size(int fd, struct ICARUS_INFO *info) +{ + // How big a buffer? + int excess_size = info->read_size - ICARUS_NONCE_SIZE; + + // Try to read one more to ensure the device doesn't return + // more than we want for this driver + excess_size++; + + unsigned char excess_bin[excess_size]; + // Read excess_size from Icarus + struct timeval tv_now; + timer_set_now(&tv_now); + //icarus_gets(excess_bin, fd, &tv_now, NULL, 1, excess_size); + int bytes_read = read(fd, excess_bin, excess_size); + // Number of bytes that were still available + + return bytes_read; +} + bool icarus_detect_custom(const char *devpath, struct device_drv *api, struct ICARUS_INFO *info) { int this_option_offset = ++option_offset; @@ -582,7 +596,7 @@ bool icarus_detect_custom(const char *devpath, struct device_drv *api, struct IC const char golden_nonce[] = "000187a2"; - unsigned char ob_bin[64], nonce_bin[ICARUS_READ_SIZE]; + unsigned char ob_bin[64], nonce_bin[ICARUS_NONCE_SIZE]; char nonce_hex[(sizeof(nonce_bin) * 2) + 1]; get_options(this_option_offset, info); @@ -598,14 +612,25 @@ bool icarus_detect_custom(const char *devpath, struct device_drv *api, struct IC applog(LOG_DEBUG, "Icarus Detect: Failed to open %s", devpath); return false; } + + // Set a default so that individual drivers need not specify + // e.g. Cairnsmore + if (info->read_size == 0) + info->read_size = ICARUS_DEFAULT_READ_SIZE; hex2bin(ob_bin, golden_ob, sizeof(ob_bin)); icarus_write(fd, ob_bin, sizeof(ob_bin)); cgtime(&tv_start); memset(nonce_bin, 0, sizeof(nonce_bin)); - icarus_gets(nonce_bin, fd, &tv_finish, NULL, 1); - + // Do not use info->read_size here, instead read exactly ICARUS_NONCE_SIZE + // We will then compare the bytes left in fd with info->read_size to determine + // if this is a valid device + icarus_gets(nonce_bin, fd, &tv_finish, NULL, 1, ICARUS_NONCE_SIZE); + + // How many bytes were left after reading the above nonce + int bytes_left = icarus_excess_nonce_size(fd, info); + icarus_close(fd); bin2hex(nonce_hex, nonce_bin, sizeof(nonce_bin)); @@ -616,6 +641,16 @@ bool icarus_detect_custom(const char *devpath, struct device_drv *api, struct IC devpath, nonce_hex, golden_nonce); return false; } + + if (info->read_size - ICARUS_NONCE_SIZE != bytes_left) + { + applog(LOG_DEBUG, + "Icarus Detect: " + "Test failed at %s: expected %d bytes, got %d", + devpath, info->read_size, ICARUS_NONCE_SIZE + bytes_left); + return false; + } + applog(LOG_DEBUG, "Icarus Detect: " "Test succeeded at %s: got %s", @@ -663,6 +698,7 @@ static bool icarus_detect_one(const char *devpath) info->quirk_reopen = 1; info->Hs = ICARUS_REV3_HASH_TIME; info->timing_mode = MODE_DEFAULT; + info->read_size = ICARUS_DEFAULT_READ_SIZE; if (!icarus_detect_custom(devpath, &icarus_drv, info)) { free(info); @@ -722,6 +758,10 @@ static bool icarus_init(struct thr_info *thr) if (!info->work_division) { struct timeval tv_finish; + + // For reading the nonce from Icarus + unsigned char res_bin[info->read_size]; + // For storing the the 32-bit nonce uint32_t res; applog(LOG_DEBUG, "%"PRIpreprv": Work division not specified - autodetecting", icarus->proc_repr); @@ -734,8 +774,12 @@ static bool icarus_init(struct thr_info *thr) "BFG\0\x64\x61\x01\x1a\xc9\x06\xa9\x51\xfb\x9b\x3c\x73"; icarus_write(fd, pkt, sizeof(pkt)); - if (ICA_GETS_OK == icarus_gets((unsigned char*)&res, fd, &tv_finish, NULL, info->read_count)) + memset(res_bin, 0, sizeof(res_bin)); + if (ICA_GETS_OK == icarus_gets(res_bin, fd, &tv_finish, NULL, info->read_count, info->read_size)) + { + memcpy(&res, res_bin, sizeof(res)); res = be32toh(res); + } else res = 0; @@ -862,6 +906,10 @@ void handle_identify(struct thr_info * const thr, int ret, const bool was_first_ int fd = icarus->device_fd; struct timeval tv_now; double delapsed; + + // For reading the nonce from Icarus + unsigned char nonce_bin[info->read_size]; + // For storing the the 32-bit nonce uint32_t nonce; if (fd == -1) @@ -882,9 +930,11 @@ void handle_identify(struct thr_info * const thr, int ret, const bool was_first_ break; // Try to get more nonces (ignoring work restart) - ret = icarus_gets((void *)&nonce, fd, &tv_now, NULL, (info->fullnonce - delapsed) * 10); + memset(nonce_bin, 0, sizeof(nonce_bin)); + ret = icarus_gets(nonce_bin, fd, &tv_now, NULL, (info->fullnonce - delapsed) * 10, info->read_size); if (ret == ICA_GETS_OK) { + memcpy(&nonce, nonce_bin, sizeof(nonce)); nonce = be32toh(nonce); submit_nonce(thr, state->last_work, nonce); } @@ -934,7 +984,6 @@ static int64_t icarus_scanhash(struct thr_info *thr, struct work *work, struct ICARUS_INFO *info; - uint32_t nonce; struct work *nonce_work; int64_t hash_count; struct timeval tv_start = {.tv_sec=0}, elapsed; @@ -964,6 +1013,11 @@ static int64_t icarus_scanhash(struct thr_info *thr, struct work *work, // Wait for the previous run's result fd = icarus->device_fd; info = icarus->device_data; + + // For reading the nonce from Icarus + unsigned char nonce_bin[info->read_size]; + // For storing the the 32-bit nonce + uint32_t nonce; if (unlikely(fd == -1) && !icarus_reopen(icarus, state, &fd)) return -1; @@ -978,8 +1032,9 @@ static int64_t icarus_scanhash(struct thr_info *thr, struct work *work, { read_count = info->read_count; keepwaiting: - /* Icarus will return 4 bytes (ICARUS_READ_SIZE) nonces or nothing */ - ret = icarus_gets((void*)&nonce, fd, &state->tv_workfinish, thr, read_count); + /* Icarus will return info->read_size bytes nonces or nothing */ + memset(nonce_bin, 0, sizeof(nonce_bin)); + ret = icarus_gets(nonce_bin, fd, &state->tv_workfinish, thr, read_count, info->read_size); switch (ret) { case ICA_GETS_RESTART: // The prepared work is invalid, and the current work is abandoned @@ -1020,6 +1075,7 @@ static int64_t icarus_scanhash(struct thr_info *thr, struct work *work, if (ret == ICA_GETS_OK) { + memcpy(&nonce, nonce_bin, sizeof(nonce)); nonce_work = icarus_process_worknonce(state, &nonce); if (likely(nonce_work)) { @@ -1168,7 +1224,7 @@ static int64_t icarus_scanhash(struct thr_info *thr, struct work *work, Ti = (double)(elapsed.tv_sec) + ((double)(elapsed.tv_usec))/((double)1000000) - - ((double)ICARUS_READ_TIME(info->baud)); + - ((double)ICARUS_READ_TIME(info->baud, info->read_size)); Xi = (double)hash_count; history0->sumXiTi += Xi * Ti; history0->sumXi += Xi; diff --git a/icarus-common.h b/icarus-common.h index 00bbd36c4a..ad16ef1395 100644 --- a/icarus-common.h +++ b/icarus-common.h @@ -28,6 +28,13 @@ #define NANOSEC 1000000000.0 +// Default value for ICARUS_INFO->read_size +#define ICARUS_DEFAULT_READ_SIZE 4 + +#define ICA_GETS_ERROR -1 +#define ICA_GETS_OK 0 +#define ICA_GETS_RESTART 1 +#define ICA_GETS_TIMEOUT 2 // Store the last INFO_HISTORY data sets // [0] = current data, not yet ready to be included as an estimate @@ -89,6 +96,9 @@ struct ICARUS_INFO { dclk_change_clock_func_t dclk_change_clock_func; struct dclk_data dclk; + + // Bytes to read from Icarus for nonce + int read_size; }; struct icarus_state { @@ -104,6 +114,7 @@ struct icarus_state { }; bool icarus_detect_custom(const char *devpath, struct device_drv *, struct ICARUS_INFO *); -extern int icarus_gets(unsigned char *, int fd, struct timeval *tv_finish, struct thr_info *, int read_count); +extern int icarus_gets(unsigned char *, int fd, struct timeval *tv_finish, struct thr_info *, int read_count, int read_size); +extern int icarus_write(int fd, const void *buf, size_t bufLen); #endif diff --git a/util.c b/util.c index 133f09b8b2..6837ed986f 100644 --- a/util.c +++ b/util.c @@ -2911,3 +2911,60 @@ void run_cmd(const char *cmd) pthread_t pth; pthread_create(&pth, NULL, cmd_thread, (void*)cmd); } + +uint8_t crc5usb(unsigned char *ptr, uint8_t len) +{ + uint8_t i, j, k; + uint8_t crc = 0x1f; + + uint8_t crcin[5] = {1, 1, 1, 1, 1}; + uint8_t crcout[5] = {1, 1, 1, 1, 1}; + uint8_t 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; +} + + diff --git a/util.h b/util.h index 79fa77582b..5283698c44 100644 --- a/util.h +++ b/util.h @@ -500,4 +500,7 @@ void maybe_strdup_if_null(const char **p, const char *s) extern void run_cmd(const char *cmd); +extern uint8_t crc5usb(unsigned char *ptr, uint8_t len); + + #endif /* __UTIL_H__ */