From 5dfc8b694f95159857aaabff60922c8755b9c08b Mon Sep 17 00:00:00 2001 From: Luke Dashjr Date: Sun, 8 Jan 2012 19:56:15 -0500 Subject: [PATCH] BitForce FPGA support cgminer will scan for and mine BitForce FPGAs on USB ports by providing the new --scan-serial option, or autodetect them by searching /dev/serial/by-id for *BitFORCE_SHA256* --- Makefile.am | 4 + bitforce.c | 219 +++++++++++++++++++++++++++++++++++++++++++++++++++ configure.ac | 16 +++- main.c | 29 ++++++- miner.h | 31 ++++++++ 5 files changed, 295 insertions(+), 4 deletions(-) create mode 100644 bitforce.c diff --git a/Makefile.am b/Makefile.am index da4a3651..85ff715c 100644 --- a/Makefile.am +++ b/Makefile.am @@ -60,3 +60,7 @@ AM_CFLAGS = -DHAS_YASM endif endif endif + +if USE_BITFORCE +cgminer_SOURCES += bitforce.c +endif diff --git a/bitforce.c b/bitforce.c new file mode 100644 index 00000000..f548b072 --- /dev/null +++ b/bitforce.c @@ -0,0 +1,219 @@ +/* + * Copyright 2012 Luke Dashjr + * + * 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. See COPYING for more details. + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "elist.h" +#include "miner.h" + + +struct device_api bitforce_api; + +static bool bitforce_detect_one(const char *devpath) +{ + char pdevbuf[0x100]; + int i = 0; + + if (total_devices == MAX_DEVICES) + return false; + + FILE *fileDev = fopen(devpath, "r+b"); + if (unlikely(!fileDev)) + { + applog(LOG_DEBUG, "BitForce Detect: Failed to open %s", devpath); + return false; + } + setbuf(fileDev, NULL); + fprintf(fileDev, "ZGX"); + if (!fgets(pdevbuf, sizeof(pdevbuf), fileDev)) + { + applog(LOG_ERR, "Error reading from BitForce (ZGX)"); + return 0; + } + fclose(fileDev); + if (unlikely(!strstr(pdevbuf, "SHA256"))) + { + applog(LOG_DEBUG, "BitForce Detect: Didn't recognize BitForce on %s", devpath); + return false; + } + + // We have a real BitForce! + struct cgpu_info *bitforce; + bitforce = calloc(1, sizeof(*bitforce)); + devices[total_devices++] = bitforce; + bitforce->api = &bitforce_api; + bitforce->device_id = i++; + bitforce->device_path = strdup(devpath); + bitforce->enabled = true; + bitforce->threads = 1; + + return true; +} + +static void bitforce_detect_auto() +{ + DIR *D; + struct dirent *de; + const char udevdir[] = "/dev/serial/by-id"; + char devpath[sizeof(udevdir) + 1 + NAME_MAX]; + char *devfile = devpath + sizeof(udevdir); + + D = opendir(udevdir); + if (!D) + return; + memcpy(devpath, udevdir, sizeof(udevdir) - 1); + devpath[sizeof(udevdir) - 1] = '/'; + while ( (de = readdir(D)) ) { + if (!strstr(de->d_name, "BitFORCE_SHA256")) + continue; + strcpy(devfile, de->d_name); + bitforce_detect_one(devpath); + } + closedir(D); +} + +static void bitforce_detect() +{ + struct string_elist *iter, *tmp; + + list_for_each_entry_safe(iter, tmp, &scan_devices, list) { + if (bitforce_detect_one(iter->string)) + string_elist_del(iter); + } + + bitforce_detect_auto(); +} + +static bool bitforce_thread_prepare(struct thr_info *thr) +{ + struct cgpu_info *bitforce = thr->cgpu; + + struct timeval now; + + FILE *fileDev = fopen(bitforce->device_path, "r+b"); + if (unlikely(!fileDev)) + { + applog(LOG_ERR, "Failed to open BitForce on %s", bitforce->device_path); + return false; + } + + { + int nDevFD = fileno(fileDev); + struct termios pattr; + tcgetattr(nDevFD, &pattr); + pattr.c_iflag &= ~(IGNBRK | BRKINT | PARMRK | ISTRIP | INLCR | IGNCR | ICRNL | IXON); + pattr.c_oflag &= ~OPOST; + pattr.c_lflag &= ~(ECHO | ECHONL | ICANON | ISIG | IEXTEN); + pattr.c_cflag &= ~(CSIZE | PARENB); + pattr.c_cflag |= CS8; + tcsetattr(nDevFD, TCSANOW, &pattr); + } + setbuf(fileDev, NULL); + bitforce->device_file = fileDev; + + applog(LOG_INFO, "Opened BitForce on %s", bitforce->device_path); + gettimeofday(&now, NULL); + get_datestamp(bitforce->init, &now); + + return true; +} + +static uint64_t bitforce_scanhash(struct thr_info *thr, struct work *work, uint64_t max_nonce) +{ + struct cgpu_info *bitforce = thr->cgpu; + FILE *fileDev = bitforce->device_file; + + char pdevbuf[0x100]; + unsigned char ob[61] = ">>>>>>>>12345678901234567890123456789012123456789012>>>>>>>>"; + int i; + char *pnoncebuf; + uint32_t nonce; + + fprintf(fileDev, "ZDX"); + if (!fgets(pdevbuf, sizeof(pdevbuf), fileDev)) { + applog(LOG_ERR, "Error reading from BitForce (ZDX)"); + return 0; + } + if (unlikely(pdevbuf[0] != 'O' || pdevbuf[1] != 'K')) + { + applog(LOG_ERR, "BitForce ZDX reports: %s", pdevbuf); + return 0; + } + + memcpy(ob + 8, work->midstate, 32); + memcpy(ob + 8 + 32, work->data + 64, 12); + fwrite(ob, 60, 1, fileDev); + applog(LOG_DEBUG, "BitForce block data: %s", bin2hex(ob + 8, 44)); + + if (!fgets(pdevbuf, sizeof(pdevbuf), fileDev)) + { + applog(LOG_ERR, "Error reading from BitForce (block data)"); + return 0; + } + if (unlikely(pdevbuf[0] != 'O' || pdevbuf[1] != 'K')) + { + applog(LOG_ERR, "BitForce block data reports: %s", pdevbuf); + return 0; + } + + usleep(4500000); + i = 4500; + while (1) { + fprintf(fileDev, "ZFX"); + if (!fgets(pdevbuf, sizeof(pdevbuf), fileDev)) + { + applog(LOG_ERR, "Error reading from BitForce (ZFX)"); + return 0; + } + if (pdevbuf[0] != 'B') + break; + usleep(10000); + i += 10; + } + applog(LOG_DEBUG, "BitForce waited %dms until %s\n", i, pdevbuf); + work->blk.nonce = 0xffffffff; + if (pdevbuf[2] == '-') + return 0xffffffff; + else + if (strncasecmp(pdevbuf, "NONCE-FOUND", 11)) { + applog(LOG_ERR, "BitForce result reports: %s", pdevbuf); + return 0; + } + + pnoncebuf = &pdevbuf[12]; + + while (1) { + hex2bin((void*)&nonce, pnoncebuf, 4); +#ifndef __BIG_ENDIAN__ + nonce = swab32(nonce); +#endif + + submit_nonce(thr, work, nonce); + if (pnoncebuf[8] != ',') + break; + pnoncebuf += 9; + } + + return 0xffffffff; +} + +struct device_api bitforce_api = { + .name = "BFL", + .api_detect = bitforce_detect, + // .reinit_device = TODO + .thread_prepare = bitforce_thread_prepare, + .scanhash = bitforce_scanhash, +}; diff --git a/configure.ac b/configure.ac index f5077c2e..d3b55a4a 100644 --- a/configure.ac +++ b/configure.ac @@ -181,6 +181,15 @@ else DLOPEN_FLAGS="" fi +bitforce=yes +AC_ARG_ENABLE([bitforce], + [AC_HELP_STRING([--disable-bitforce],[Don't compile support for BitForce FPGAs])], + [bitforce=$enableval] +) +if test "x$bitforce" != xno; then + AC_DEFINE([USE_BITFORCE], [1], [Defined to 1 if BitForce support is wanted.]) +fi + AC_SEARCH_LIBS(addstr, ncurses pdcurses, , AC_MSG_ERROR([Could not find curses library - please install libncurses-dev or pdcurses-dev])) @@ -190,6 +199,7 @@ AC_CHECK_LIB(pdcurses, addstr, PDCURSES_LIBS=-lpdcurses) AM_CONDITIONAL([WANT_JANSSON], [test x$request_jansson = xtrue]) AM_CONDITIONAL([HAVE_WINDOWS], [test x$have_win32 = xtrue]) AM_CONDITIONAL([HAVE_x86_64], [test x$have_x86_64 = xtrue]) +AM_CONDITIONAL([USE_BITFORCE], [test x$bitforce != xno]) if test x$request_jansson = xtrue then @@ -312,18 +322,20 @@ echo echo "Configuration Options Summary:" echo +echo " BitForce.FPGAs.......: $bitforce" + if test "x$opencl" != xno; then if test $found_opencl = 1; then echo " OpenCL...............: FOUND. GPU mining support enabled" else echo " OpenCL...............: NOT FOUND. GPU mining support DISABLED" - if test "x$cpumining" != xyes; then + if test "x$cpumining$bitforce" = xnono; then AC_MSG_ERROR([No mining configured in]) fi fi else echo " OpenCL...............: Detection overrided. GPU mining support DISABLED" - if test "x$cpumining" != xyes; then + if test "x$cpumining$bitforce" = xnono; then AC_MSG_ERROR([No mining configured in]) fi fi diff --git a/main.c b/main.c index 5571ec13..1f5a8c44 100644 --- a/main.c +++ b/main.c @@ -216,6 +216,7 @@ static bool opt_restart = true; static bool opt_nogpu; #endif +struct list_head scan_devices; int nDevs; static int opt_g_threads = 2; static signed int devices_enabled = 0; @@ -1003,6 +1004,12 @@ static char *set_float_0_to_99(const char *arg, float *f) return NULL; } +static char *add_serial(char *arg) +{ + string_elist_add(arg, &scan_devices); + return NULL; +} + static char *set_devices(char *arg) { int i = strtol(arg, &arg, 0); @@ -1670,6 +1677,11 @@ static struct opt_table opt_config_table[] = { OPT_WITHOUT_ARG("--round-robin", set_rr, &pool_strategy, "Change multipool strategy from failover to round robin on failure"), +#ifdef USE_BITFORCE + OPT_WITH_ARG("--scan-serial|-S", + add_serial, NULL, NULL, + "Serial port to probe for BitForce 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"), @@ -4340,6 +4352,7 @@ static inline bool abandon_work(int thr_id, struct work *work, struct timeval *w { if (wdiff->tv_sec > opt_scantime || work->blk.nonce >= MAXTHREADS - hashes || + hashes >= 0xfffffffe || stale_work(work, false)) return true; return false; @@ -4432,7 +4445,7 @@ static void *miner_thread(void *userdata) } } - if (sdiff.tv_sec < cycle) { + if (unlikely(sdiff.tv_sec < cycle)) { if (likely(!api->can_limit_work || max_nonce == 0xffffffff)) continue; @@ -5386,7 +5399,7 @@ static void cpu_detect() #endif /* !WIN32 */ if (opt_n_threads < 0 || !forced_n_threads) { - if (nDevs && !opt_usecpu) + if (total_devices && !opt_usecpu) opt_n_threads = 0; else opt_n_threads = num_processors; @@ -5803,6 +5816,12 @@ struct device_api opencl_api = { }; #endif + +#ifdef USE_BITFORCE +extern struct device_api bitforce_api; +#endif + + static int cgminer_id_count = 0; void enable_device(struct cgpu_info *cgpu) @@ -5877,6 +5896,8 @@ int main (int argc, char *argv[]) HASH_ADD_STR(blocks, hash, block); strcpy(current_block, block->hash); + INIT_LIST_HEAD(&scan_devices); + memset(gpus, 0, sizeof(gpus)); for (i = 0; i < MAX_GPUDEVICES; i++) gpus[i].dynamic = true; @@ -5946,6 +5967,10 @@ int main (int argc, char *argv[]) opencl_api.api_detect(); #endif +#ifdef USE_BITFORCE + bitforce_api.api_detect(); +#endif + #ifdef WANT_CPUMINE cpu_api.api_detect(); #endif diff --git a/miner.h b/miner.h index 86352a38..eb29058f 100644 --- a/miner.h +++ b/miner.h @@ -236,6 +236,9 @@ struct cgpu_info { int cgminer_id; struct device_api *api; int device_id; + char *device_path; + FILE *device_file; + bool enabled; int accepted; int rejected; @@ -297,6 +300,32 @@ struct thr_info { extern int thr_info_create(struct thr_info *thr, pthread_attr_t *attr, void *(*start) (void *), void *arg); extern void thr_info_cancel(struct thr_info *thr); + +struct string_elist { + char *string; + bool free_me; + + struct list_head list; +}; + +static inline void string_elist_add(const char *s, struct list_head *head) +{ + struct string_elist *n; + + n = calloc(1, sizeof(*n)); + n->string = strdup(s); + n->free_me = true; + list_add_tail(&n->list, head); +} + +static inline void string_elist_del(struct string_elist *item) +{ + if (item->free_me) + free(item->string); + list_del(&item->list); +} + + static inline uint32_t swab32(uint32_t v) { return bswap_32(v); @@ -468,6 +497,7 @@ extern void api(void); #define MAX_DEVICES 32 #define MAX_POOLS (32) +extern struct list_head scan_devices; extern int nDevs; extern int opt_n_threads; extern int num_processors; @@ -581,6 +611,7 @@ enum cl_kernel { KL_PHATK, }; +extern void get_datestamp(char *, struct timeval *); bool submit_nonce(struct thr_info *thr, struct work *work, uint32_t nonce); extern void wlogprint(const char *f, ...); extern int curses_int(const char *query);