diff --git a/Makefile.am b/Makefile.am index 20db8a2d..f4e58988 100644 --- a/Makefile.am +++ b/Makefile.am @@ -68,6 +68,10 @@ endif # HAVE_x86_64 endif # HAS_YASM endif # HAS_CPUMINE +if NEED_FPGAUTILS +cgminer_SOURCES += fpgautils.c +endif + if HAS_BITFORCE cgminer_SOURCES += driver-bitforce.c endif @@ -76,6 +80,12 @@ if HAS_ICARUS cgminer_SOURCES += driver-icarus.c endif +if HAS_MODMINER +cgminer_SOURCES += driver-modminer.c +bitstreamsdir = $(bindir)/bitstreams +dist_bitstreams_DATA = bitstreams/* +endif + if HAS_ZTEX cgminer_SOURCES += driver-ztex.c libztex.c libztex.h bitstreamsdir = $(bindir)/bitstreams diff --git a/README b/README index af78af1e..5a58dc48 100644 --- a/README +++ b/README @@ -129,6 +129,7 @@ Options for both config file and command line: --debug|-D Enable debug output --expiry|-E Upper bound on how many seconds after getting work we consider a share from it stale (default: 120) --failover-only Don't leak work to backup pools when primary pool is lagging +--kernel-path|-K Specify a path to where bitstream and kernel files are (default: "/usr/local/bin") --load-balance Change multipool strategy from failover to even load balance --log|-l Interval in seconds between log output (default: 5) --monitor|-m Use custom pipe cmd for output messages @@ -184,7 +185,6 @@ GPU only options: --gpu-vddc Set the GPU voltage in Volts - one value for all or separate by commas for per card. --intensity|-I Intensity of GPU scanning (d or -10 -> 10, default: d to maintain desktop interactivity) --kernel|-k Override kernel to use (diablo, poclbm, phatk or diakgcn) - one value or comma separated ---kernel-path|-K Specify a path to where the kernel .cl files are (default: "/usr/local/bin") --ndevs|-n Enumerate number of detected GPUs and exit --no-restart Do not attempt to restart GPUs that hang --temp-hysteresis Set how much the temperature can fluctuate outside limits when automanaging speeds (default: 3) diff --git a/adl.c b/adl.c index 6ed6a182..a920d95a 100644 --- a/adl.c +++ b/adl.c @@ -1009,8 +1009,10 @@ static int set_powertune(int gpu, int iPercentage) return ret; } -/* Returns whether the fanspeed is optimal already or not */ -static bool fan_autotune(int gpu, int temp, int fanpercent, int lasttemp) +/* Returns whether the fanspeed is optimal already or not. The fan_window bool + * tells us whether the current fanspeed is in the target range for fanspeeds. + */ +static bool fan_autotune(int gpu, int temp, int fanpercent, int lasttemp, bool *fan_window) { struct cgpu_info *cgpu = &gpus[gpu]; struct gpu_adl *ga = &cgpu->adl; @@ -1054,6 +1056,12 @@ static bool fan_autotune(int gpu, int temp, int fanpercent, int lasttemp) newpercent = iMax; else if (newpercent < iMin) newpercent = iMin; + + if (newpercent <= top) + *fan_window = true; + else + *fan_window = false; + if (newpercent != fanpercent) { applog(LOG_INFO, "Setting GPU %d fan percentage to %d", gpu, newpercent); set_fanspeed(gpu, newpercent); @@ -1065,7 +1073,7 @@ static bool fan_autotune(int gpu, int temp, int fanpercent, int lasttemp) void gpu_autotune(int gpu, enum dev_enable *denable) { int temp, fanpercent, engine, newengine, twintemp = 0; - bool fan_optimal = true; + bool fan_optimal = true, fan_window = true; struct cgpu_info *cgpu; struct gpu_adl *ga; @@ -1084,7 +1092,7 @@ void gpu_autotune(int gpu, enum dev_enable *denable) if (temp && fanpercent >= 0 && ga->autofan) { if (!ga->twin) - fan_optimal = fan_autotune(gpu, temp, fanpercent, ga->lasttemp); + fan_optimal = fan_autotune(gpu, temp, fanpercent, ga->lasttemp, &fan_window); else if (ga->autofan && (ga->has_fanspeed || !ga->twin->autofan)) { /* On linked GPUs, we autotune the fan only once, based * on the highest temperature from either GPUs */ @@ -1102,7 +1110,7 @@ void gpu_autotune(int gpu, enum dev_enable *denable) fan_gpu = gpu; else fan_gpu = ga->twin->gpu; - fan_optimal = fan_autotune(fan_gpu, hightemp, fanpercent, lasttemp); + fan_optimal = fan_autotune(fan_gpu, hightemp, fanpercent, lasttemp, &fan_window); } } @@ -1126,7 +1134,7 @@ void gpu_autotune(int gpu, enum dev_enable *denable) applog(LOG_DEBUG, "Temperature %d degrees over target, decreasing clock speed", opt_hysteresis); newengine = engine - ga->lpOdParameters.sEngineClock.iStep; /* Only try to tune engine speed up if this GPU is not disabled */ - } else if (temp < ga->targettemp && engine < ga->maxspeed && *denable == DEV_ENABLED) { + } else if (temp < ga->targettemp && engine < ga->maxspeed && fan_window && *denable == DEV_ENABLED) { applog(LOG_DEBUG, "Temperature below target, increasing clock speed"); if (temp < ga->targettemp - opt_hysteresis) newengine = ga->maxspeed; diff --git a/bitstreams/COPYING_fpgaminer b/bitstreams/COPYING_fpgaminer new file mode 100644 index 00000000..9db2c5fd --- /dev/null +++ b/bitstreams/COPYING_fpgaminer @@ -0,0 +1,23 @@ +All the bitstream files included in this directory that follow the name pattern fpgaminer_*.ncd are: + +---- + +Copyright (c) 2011-2012 fpgaminer@bitcoin-mining.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. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +---- + +You can find the original sources at the Open Source FPGA Bitcoin Miner project GitHub repository: +https://github.com/progranism/Open-Source-FPGA-Bitcoin-Miner/tree/master/projects/X6000_ztex_comm4/hdl diff --git a/bitstreams/LICENSE.txt b/bitstreams/COPYING_ztex similarity index 100% rename from bitstreams/LICENSE.txt rename to bitstreams/COPYING_ztex diff --git a/bitstreams/fpgaminer_top_fixed7_197MHz.ncd b/bitstreams/fpgaminer_top_fixed7_197MHz.ncd new file mode 100644 index 00000000..1df4e1d0 Binary files /dev/null and b/bitstreams/fpgaminer_top_fixed7_197MHz.ncd differ diff --git a/cgminer.c b/cgminer.c index 1092a5d5..d12a009f 100644 --- a/cgminer.c +++ b/cgminer.c @@ -54,6 +54,13 @@ #include #endif +#if defined(USE_BITFORCE) || defined(USE_ICARUS) || defined(USE_MODMINER) +# define USE_FPGA +# define USE_FPGA_SERIAL +#elif defined(USE_ZTEX) +# define USE_FPGA +#endif + enum workio_commands { WC_GET_WORK, WC_SUBMIT_WORK, @@ -468,7 +475,7 @@ static char *set_int_1_to_10(const char *arg, int *i) return set_int_range(arg, i, 1, 10); } -#if defined(USE_BITFORCE) || defined(USE_ICARUS) +#ifdef USE_FPGA_SERIAL static char *add_serial(char *arg) { string_elist_add(arg, &scan_devices); @@ -657,8 +664,12 @@ static void load_temp_cutoffs() devices[device]->cutofftemp = val; } } - else - val = opt_cutofftemp; + else { + for (i = device; i < total_devices; ++i) + if (!devices[i]->cutofftemp) + devices[i]->cutofftemp = opt_cutofftemp; + return; + } if (device <= 1) { for (i = device; i < total_devices; ++i) devices[i]->cutofftemp = val; @@ -768,7 +779,7 @@ static struct opt_table opt_config_table[] = { opt_hidden #endif ), -#if defined(WANT_CPUMINE) && (defined(HAVE_OPENCL) || defined(USE_BITFORCE) || defined(USE_ICARUS)) +#if defined(WANT_CPUMINE) && (defined(HAVE_OPENCL) || defined(USE_FPGA)) OPT_WITHOUT_ARG("--enable-cpu|-C", opt_set_bool, &opt_usecpu, "Enable CPU mining with other mining (default: no CPU mining if other devices exist)"), @@ -818,9 +829,13 @@ static struct opt_table opt_config_table[] = { OPT_WITH_ARG("--intensity|-I", set_intensity, NULL, NULL, "Intensity of GPU scanning (d or " _MIN_INTENSITY_STR " -> " _MAX_INTENSITY_STR ", default: d to maintain desktop interactivity)"), +#endif +#if defined(HAVE_OPENCL) || defined(HAVE_MODMINER) OPT_WITH_ARG("--kernel-path|-K", opt_set_charp, opt_show_charp, &opt_kernel_path, - "Specify a path to where the kernel .cl files are"), + "Specify a path to where bitstream and kernel files are"), +#endif +#ifdef HAVE_OPENCL OPT_WITH_ARG("--kernel|-k", set_kernel, NULL, NULL, "Override kernel to use (diablo, poclbm, phatk or diakgcn) - one value or comma separated"), @@ -899,7 +914,7 @@ 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"), -#if defined(USE_BITFORCE) || defined(USE_ICARUS) +#ifdef USE_FPGA_SERIAL OPT_WITH_ARG("--scan-serial|-S", add_serial, NULL, NULL, "Serial port to probe for FPGA Mining device"), @@ -927,7 +942,7 @@ static struct opt_table opt_config_table[] = { opt_set_bool, &use_syslog, "Use system log for output messages (default: standard error)"), #endif -#if defined(HAVE_ADL) || defined(USE_BITFORCE) +#if defined(HAVE_ADL) || defined(USE_BITFORCE) || defined(USE_MODMINER) OPT_WITH_ARG("--temp-cutoff", set_temp_cutoff, opt_show_intval, &opt_cutofftemp, "Temperature where a device will be automatically disabled, one value or comma separated list"), @@ -1126,6 +1141,9 @@ static char *opt_verusage_and_exit(const char *extra) #ifdef USE_ICARUS "icarus " #endif +#ifdef USE_MODMINER + "modminer " +#endif #ifdef USE_ZTEX "ztex " #endif @@ -3729,15 +3747,21 @@ bool hashtest(const struct work *work) } -bool submit_nonce(struct thr_info *thr, struct work *work, uint32_t nonce) +bool test_nonce(struct work *work, uint32_t nonce) { work->data[64 + 12 + 0] = (nonce >> 0) & 0xff; work->data[64 + 12 + 1] = (nonce >> 8) & 0xff; work->data[64 + 12 + 2] = (nonce >> 16) & 0xff; work->data[64 + 12 + 3] = (nonce >> 24) & 0xff; + return hashtest(work); +} + +bool submit_nonce(struct thr_info *thr, struct work *work, uint32_t nonce) +{ /* Do one last check before attempting to submit the work */ - if (!hashtest(work)) { + /* Side effect: sets work->data for us */ + if (!test_nonce(work, nonce)) { applog(LOG_INFO, "Share below target"); return true; } @@ -4719,6 +4743,10 @@ extern struct device_api bitforce_api; extern struct device_api icarus_api; #endif +#ifdef USE_MODMINER +extern struct device_api modminer_api; +#endif + #ifdef USE_ZTEX extern struct device_api ztex_api; #endif @@ -4961,6 +4989,10 @@ int main(int argc, char *argv[]) bitforce_api.api_detect(); #endif +#ifdef USE_MODMINER + modminer_api.api_detect(); +#endif + #ifdef USE_ZTEX ztex_api.api_detect(); #endif diff --git a/configure.ac b/configure.ac index 685deac7..5cfb9ff9 100644 --- a/configure.ac +++ b/configure.ac @@ -209,6 +209,17 @@ if test "x$icarus" = xyes; then fi AM_CONDITIONAL([HAS_ICARUS], [test x$icarus = xyes]) +modminer="no" + +AC_ARG_ENABLE([modminer], + [AC_HELP_STRING([--enable-modminer],[Compile support for ModMiner FPGAs(default disabled)])], + [modminer=$enableval] + ) +if test "x$modminer" = xyes; then + AC_DEFINE([USE_MODMINER], [1], [Defined to 1 if ModMiner support is wanted]) +fi +AM_CONDITIONAL([HAS_MODMINER], [test x$modminer = xyes]) + ztex="no" AC_ARG_ENABLE([ztex], @@ -245,6 +256,7 @@ else fi +AM_CONDITIONAL([NEED_FPGAUTILS], [test x$icarus$bitforce$modminer$ztex != xnononono]) AM_CONDITIONAL([HAVE_CURSES], [test x$curses = xyes]) AM_CONDITIONAL([WANT_JANSSON], [test x$request_jansson = xtrue]) AM_CONDITIONAL([HAVE_WINDOWS], [test x$have_win32 = xtrue]) @@ -293,7 +305,7 @@ fi AM_CONDITIONAL([HAS_YASM], [test x$has_yasm = xtrue]) -if test "x$bitforce" != xno; then +if test "x$bitforce$modminer" != xnono; then AC_ARG_WITH([libudev], [AC_HELP_STRING([--without-libudev], [Autodetect FPGAs using libudev (default enabled)])], [libudev=$withval], [libudev=auto] @@ -415,13 +427,13 @@ if test "x$opencl" != xno; then echo " OpenCL...............: FOUND. GPU mining support enabled" else echo " OpenCL...............: NOT FOUND. GPU mining support DISABLED" - if test "x$cpumining$bitforce$icarus$ztex" = xnononono; then + if test "x$cpumining$bitforce$icarus$ztex$modminer" = xnonononono; then AC_MSG_ERROR([No mining configured in]) fi fi else echo " OpenCL...............: Detection overrided. GPU mining support DISABLED" - if test "x$cpumining$bitforce$icarus$ztex" = xnononono; then + if test "x$cpumining$bitforce$icarus$ztex$modminer" = xnonononono; then AC_MSG_ERROR([No mining configured in]) fi fi @@ -449,13 +461,19 @@ else echo " Icarus.FPGAs.........: Disabled" fi +if test "x$modminer" = xyes; then + echo " ModMiner.FPGAs.......: Enabled" +else + echo " ModMiner.FPGAs.......: Disabled" +fi + if test "x$ztex" = xyes; then echo " Ztex.FPGAs...........: Enabled" else echo " Ztex.FPGAs...........: Disabled" fi -if test "x$bitforce" != xno; then +if test "x$bitforce$modminer" != xnono; then echo " libudev.detection....: $libudev" fi diff --git a/driver-bitforce.c b/driver-bitforce.c index f90e0119..8067cca1 100644 --- a/driver-bitforce.c +++ b/driver-bitforce.c @@ -13,28 +13,11 @@ #include #include #include -#include -#include -#ifndef WIN32 -#include -#include -#include -#ifndef O_CLOEXEC -#define O_CLOEXEC 0 -#endif -#else -#include -#include -#endif #include #include "config.h" -#ifdef HAVE_LIBUDEV -#include -#endif - -#include "elist.h" +#include "fpgautils.h" #include "miner.h" #define BITFORCE_SLEEP_US 4500000 @@ -43,35 +26,7 @@ struct device_api bitforce_api; -static int BFopen(const char *devpath) -{ -#ifdef WIN32 - HANDLE hSerial = CreateFile(devpath, GENERIC_READ | GENERIC_WRITE, 0, NULL, OPEN_EXISTING, 0, NULL); - if (unlikely(hSerial == INVALID_HANDLE_VALUE)) - return -1; - - COMMTIMEOUTS cto = {30000, 0, 30000, 0, 30000}; - SetCommTimeouts(hSerial, &cto); - - return _open_osfhandle((LONG)hSerial, 0); -#else - int fdDev = open(devpath, O_RDWR | O_CLOEXEC | O_NOCTTY); - if (likely(fdDev != -1)) { - struct termios pattr; - - tcgetattr(fdDev, &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(fdDev, TCSANOW, &pattr); - } - tcflush(fdDev, TCOFLUSH); - tcflush(fdDev, TCIFLUSH); - return fdDev; -#endif -} +#define BFopen(devpath) serial_open(devpath, 0, -1, true) static void BFgets(char *buf, size_t bufLen, int fd) { @@ -101,9 +56,6 @@ static bool bitforce_detect_one(const char *devpath) char *s; char pdevbuf[0x100]; - if (total_devices == MAX_DEVICES) - return false; - int fdDev = BFopen(devpath); if (unlikely(fdDev == -1)) { applog(LOG_ERR, "BitForce Detect: Failed to open %s", devpath); @@ -139,103 +91,17 @@ static bool bitforce_detect_one(const char *devpath) return add_cgpu(bitforce); } -static bool bitforce_detect_auto_udev() +static char bitforce_detect_auto() { -#ifdef HAVE_LIBUDEV - struct udev *udev = udev_new(); - struct udev_enumerate *enumerate = udev_enumerate_new(udev); - struct udev_list_entry *list_entry; - bool foundany = false; - - udev_enumerate_add_match_subsystem(enumerate, "tty"); - udev_enumerate_add_match_property(enumerate, "ID_MODEL", "BitFORCE*SHA256"); - udev_enumerate_scan_devices(enumerate); - udev_list_entry_foreach(list_entry, udev_enumerate_get_list_entry(enumerate)) { - struct udev_device *device = udev_device_new_from_syspath( - udev_enumerate_get_udev(enumerate), - udev_list_entry_get_name(list_entry) - ); - if (!device) - continue; - - const char *devpath = udev_device_get_devnode(device); - if (devpath) { - foundany = true; - bitforce_detect_one(devpath); - } - - udev_device_unref(device); - } - udev_enumerate_unref(enumerate); - udev_unref(udev); - - return foundany; -#else - return false; -#endif -} - -static bool bitforce_detect_auto_devserial() -{ -#ifndef WIN32 - DIR *D; - struct dirent *de; - const char udevdir[] = "/dev/serial/by-id"; - char devpath[sizeof(udevdir) + 1 + NAME_MAX]; - char *devfile = devpath + sizeof(udevdir); - bool foundany = false; - - D = opendir(udevdir); - if (!D) - return false; - memcpy(devpath, udevdir, sizeof(udevdir) - 1); - devpath[sizeof(udevdir) - 1] = '/'; - while ( (de = readdir(D)) ) { - if (!strstr(de->d_name, "BitFORCE_SHA256")) - continue; - foundany = true; - strcpy(devfile, de->d_name); - bitforce_detect_one(devpath); - } - closedir(D); - - return foundany; -#else - return false; -#endif -} - -static void bitforce_detect_auto() -{ - bitforce_detect_auto_udev() ?: - bitforce_detect_auto_devserial() ?: + return + serial_autodetect_udev (bitforce_detect_one, "BitFORCE*SHA256") ?: + serial_autodetect_devserial(bitforce_detect_one, "BitFORCE_SHA256") ?: 0; } static void bitforce_detect() { - struct string_elist *iter, *tmp; - const char*s; - bool found = false; - bool autoscan = false; - - list_for_each_entry_safe(iter, tmp, &scan_devices, list) { - s = iter->string; - if (!strncmp("bitforce:", iter->string, 9)) - s += 9; - if (!strcmp(s, "auto")) - autoscan = true; - else - if (!strcmp(s, "noauto")) - found = true; - else if (bitforce_detect_one(s)) { - string_elist_del(iter); - found = true; - } - } - - if (autoscan || !found) - bitforce_detect_auto(); + serial_detect_auto("bitforce", bitforce_detect_one, bitforce_detect_auto); } static void get_bitforce_statline_before(char *buf, struct cgpu_info *bitforce) diff --git a/driver-icarus.c b/driver-icarus.c index 75ab5e3f..ee9800c4 100644 --- a/driver-icarus.c +++ b/driver-icarus.c @@ -49,6 +49,7 @@ #endif #include "elist.h" +#include "fpgautils.h" #include "miner.h" // The serial I/O speed - Linux uses a define 'B115200' in bits/termios.h @@ -69,10 +70,8 @@ ASSERT1(sizeof(uint32_t) == 4); // Fraction of a second, USB timeout is measured in // i.e. 10 means 1/10 of a second #define TIME_FACTOR 10 -// In Linux it's 10 per second, thus value = 10/TIME_FACTOR = -#define LINUX_TIMEOUT_VALUE 1 -// In Windows it's 1000 per second, thus value = 1000/TIME_FACTOR = -#define WINDOWS_TIMEOUT_VALUE 100 +// It's 10 per second, thus value = 10/TIME_FACTOR = +#define ICARUS_READ_FAULT_DECISECONDS 1 // In timing mode: Default starting value until an estimate can be obtained // 5 seconds allows for up to a ~840MH/s device @@ -196,63 +195,8 @@ static void rev(unsigned char *s, size_t l) } } -static int icarus_open(const char *devpath) -{ -#ifndef WIN32 - struct termios my_termios; - - int serialfd = open(devpath, O_RDWR | O_CLOEXEC | O_NOCTTY); - - if (serialfd == -1) - return -1; - - tcgetattr(serialfd, &my_termios); - my_termios.c_cflag = B115200; - my_termios.c_cflag |= CS8; - my_termios.c_cflag |= CREAD; - my_termios.c_cflag |= CLOCAL; - my_termios.c_cflag &= ~(CSIZE | PARENB); - - my_termios.c_iflag &= ~(IGNBRK | BRKINT | PARMRK | - ISTRIP | INLCR | IGNCR | ICRNL | IXON); - my_termios.c_oflag &= ~OPOST; - my_termios.c_lflag &= ~(ECHO | ECHONL | ICANON | ISIG | IEXTEN); - my_termios.c_cc[VTIME] = LINUX_TIMEOUT_VALUE; /* how long to block */ - my_termios.c_cc[VMIN] = 0; - tcsetattr(serialfd, TCSANOW, &my_termios); - - tcflush(serialfd, TCOFLUSH); - tcflush(serialfd, TCIFLUSH); - - return serialfd; -#else - COMMCONFIG comCfg; - - HANDLE hSerial = CreateFile(devpath, GENERIC_READ | GENERIC_WRITE, 0, - NULL, OPEN_EXISTING, 0, NULL); - if (unlikely(hSerial == INVALID_HANDLE_VALUE)) - return -1; - - // thanks to af_newbie for pointers about this - memset(&comCfg, 0 , sizeof(comCfg)); - comCfg.dwSize = sizeof(COMMCONFIG); - comCfg.wVersion = 1; - comCfg.dcb.DCBlength = sizeof(DCB); - comCfg.dcb.BaudRate = ICARUS_IO_SPEED; - comCfg.dcb.fBinary = 1; - comCfg.dcb.fDtrControl = DTR_CONTROL_ENABLE; - comCfg.dcb.fRtsControl = RTS_CONTROL_ENABLE; - comCfg.dcb.ByteSize = 8; - - SetCommConfig(hSerial, &comCfg, sizeof(comCfg)); - - // How long to block - COMMTIMEOUTS cto = {WINDOWS_TIMEOUT_VALUE, 0, WINDOWS_TIMEOUT_VALUE, 0, WINDOWS_TIMEOUT_VALUE}; - SetCommTimeouts(hSerial, &cto); - - return _open_osfhandle((LONG)hSerial, 0); -#endif -} +#define icarus_open2(devpath, purge) serial_open(devpath, 115200, ICARUS_READ_FAULT_DECISECONDS, purge) +#define icarus_open(devpath) icarus_open2(devpath, false) static int icarus_gets(unsigned char *buf, int fd, struct timeval *tv_finish, int thr_id, int read_count) { @@ -435,10 +379,7 @@ static bool icarus_detect_one(const char *devpath) unsigned char ob_bin[64], nonce_bin[ICARUS_READ_SIZE]; char *nonce_hex; - if (total_devices == MAX_DEVICES) - return false; - - fd = icarus_open(devpath); + fd = icarus_open2(devpath, true); if (unlikely(fd == -1)) { applog(LOG_ERR, "Icarus Detect: Failed to open %s", devpath); return false; @@ -503,18 +444,7 @@ static bool icarus_detect_one(const char *devpath) static void icarus_detect() { - struct string_elist *iter, *tmp; - const char*s; - - list_for_each_entry_safe(iter, tmp, &scan_devices, list) { - s = iter->string; - if (!strncmp("icarus:", iter->string, 7)) - s += 7; - if (!strcmp(s, "auto") || !strcmp(s, "noauto")) - continue; - if (icarus_detect_one(s)) - string_elist_del(iter); - } + serial_detect("icarus", icarus_detect_one); } static bool icarus_prepare(struct thr_info *thr) diff --git a/driver-modminer.c b/driver-modminer.c new file mode 100644 index 00000000..aabf893c --- /dev/null +++ b/driver-modminer.c @@ -0,0 +1,514 @@ +/* + * 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 3 of the License, or (at your option) + * any later version. See COPYING for more details. + */ + +#include "config.h" + +#include +#include +#include + +#include "fpgautils.h" +#include "logging.h" +#include "miner.h" + +#define BITSTREAM_FILENAME "fpgaminer_top_fixed7_197MHz.ncd" +#define BISTREAM_USER_ID "\2\4$B" + +struct device_api modminer_api; + +static inline bool +_bailout(int fd, struct cgpu_info*modminer, int prio, const char *fmt, ...) +{ + if (fd != -1) + serial_close(fd); + if (modminer) { + modminer->device_fd = -1; + mutex_unlock(&modminer->device_mutex); + } + + va_list ap; + va_start(ap, fmt); + vapplog(prio, fmt, ap); + va_end(ap); + return false; +} +#define bailout(...) return _bailout(fd, NULL, __VA_ARGS__); + +static bool +modminer_detect_one(const char *devpath) +{ + int fd = serial_open(devpath, 0, 10, true); + if (unlikely(fd == -1)) + bailout(LOG_DEBUG, "ModMiner detect: failed to open %s", devpath); + + char buf[0x100]; + size_t len; + + // Sending 45 noops, just in case the device was left in "start job" reading + write(fd, "\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff", 45) ?:0; + while (serial_read(fd, buf, sizeof(buf)) > 0) + ; + + if (1 != write(fd, "\x01", 1)) // Get version + bailout(LOG_DEBUG, "ModMiner detect: write failed on %s (get version)", devpath); + len = serial_read(fd, buf, sizeof(buf)-1); + if (len < 1) + bailout(LOG_DEBUG, "ModMiner detect: no response to version request from %s", devpath); + buf[len] = '\0'; + char*devname = strdup(buf); + applog(LOG_DEBUG, "ModMiner identified as: %s", devname); + + if (1 != write(fd, "\x02", 1)) // Get FPGA count + bailout(LOG_DEBUG, "ModMiner detect: write failed on %s (get FPGA count)", devpath); + len = read(fd, buf, 1); + if (len < 1) + bailout(LOG_ERR, "ModMiner detect: timeout waiting for FPGA count from %s", devpath); + if (!buf[0]) + bailout(LOG_ERR, "ModMiner detect: zero FPGAs reported on %s", devpath); + applog(LOG_DEBUG, "ModMiner %s has %u FPGAs", devname, buf[0]); + + serial_close(fd); + + struct cgpu_info *modminer; + modminer = calloc(1, sizeof(*modminer)); + modminer->api = &modminer_api; + mutex_init(&modminer->device_mutex); + modminer->device_path = strdup(devpath); + modminer->device_fd = -1; + modminer->deven = DEV_ENABLED; + modminer->threads = buf[0]; + modminer->name = devname; + modminer->cutofftemp = 85; + + return add_cgpu(modminer); +} + +#undef bailout + +static char +modminer_detect_auto() +{ + return + serial_autodetect_udev (modminer_detect_one, "BTCFPGA*ModMiner") ?: + serial_autodetect_devserial(modminer_detect_one, "BTCFPGA_ModMiner") ?: + 0; +} + +static void +modminer_detect() +{ + serial_detect_auto("modminer", modminer_detect_one, modminer_detect_auto); +} + +static void +get_modminer_statline_before(char *buf, struct cgpu_info *modminer) +{ + float gt = modminer->temp; + if (gt > 0) + tailsprintf(buf, "%5.1fC ", gt); + else + tailsprintf(buf, " ", gt); + tailsprintf(buf, " | "); +} + +#define bailout(...) return _bailout(-1, modminer, __VA_ARGS__); +#define bailout2(...) return _bailout(fd, modminer, __VA_ARGS__); + +#define check_magic(L) do { \ + if (1 != fread(buf, 1, 1, f)) \ + bailout(LOG_ERR, "Error reading ModMiner firmware ('%c')", L); \ + if (buf[0] != L) \ + bailout(LOG_ERR, "ModMiner firmware has wrong magic ('%c')", L); \ +} while(0) + +#define read_str(eng) do { \ + if (1 != fread(buf, 2, 1, f)) \ + bailout(LOG_ERR, "Error reading ModMiner firmware (" eng " len)"); \ + len = (ubuf[0] << 8) | ubuf[1]; \ + if (len >= sizeof(buf)) \ + bailout(LOG_ERR, "ModMiner firmware " eng " too long"); \ + if (1 != fread(buf, len, 1, f)) \ + bailout(LOG_ERR, "Error reading ModMiner firmware (" eng ")"); \ + buf[len] = '\0'; \ +} while(0) + +#define status_read(eng) do { \ +FD_SET(fd, &fds); \ +select(fd+1, &fds, NULL, NULL, NULL); \ + if (1 != read(fd, buf, 1)) \ + bailout2(LOG_ERR, "Error programming ModMiner %s (" eng ")", modminer->device_path); \ + if (buf[0] != 1) \ + bailout2(LOG_ERR, "Wrong " eng " programming ModMiner %s", modminer->device_path); \ +} while(0) + +static bool +modminer_fpga_upload_bitstream(struct cgpu_info*modminer) +{ +fd_set fds; + char buf[0x100]; + unsigned char *ubuf = (unsigned char*)buf; + unsigned long len; + char *p; + const char *fwfile = BITSTREAM_FILENAME; + char fpgaid = 4; // "all FPGAs" + + FILE *f = open_bitstream("modminer", fwfile); + if (!f) + bailout(LOG_ERR, "Error opening ModMiner firmware file %s", fwfile); + if (1 != fread(buf, 2, 1, f)) + bailout(LOG_ERR, "Error reading ModMiner firmware (magic)"); + if (buf[0] || buf[1] != 9) + bailout(LOG_ERR, "ModMiner firmware has wrong magic (9)"); + if (-1 == fseek(f, 11, SEEK_CUR)) + bailout(LOG_ERR, "ModMiner firmware seek failed"); + check_magic('a'); + read_str("design name"); + applog(LOG_DEBUG, "ModMiner firmware file %s info:", fwfile); + applog(LOG_DEBUG, " Design name: %s", buf); + p = strrchr(buf, ';') ?: buf; + p = strrchr(buf, '=') ?: p; + if (p[0] == '=') + ++p; + long fwusercode = strtol(p, &p, 16); + if (p[0] != '\0') + bailout(LOG_ERR, "Bad usercode in ModMiner firmware file"); + if (fwusercode == 0xffffffff) + bailout(LOG_ERR, "ModMiner firmware doesn't support user code"); + applog(LOG_DEBUG, " Version: %u, build %u", (fwusercode >> 8) & 0xff, fwusercode & 0xff); + check_magic('b'); + read_str("part number"); + applog(LOG_DEBUG, " Part number: %s", buf); + check_magic('c'); + read_str("build date"); + applog(LOG_DEBUG, " Build date: %s", buf); + check_magic('d'); + read_str("build time"); + applog(LOG_DEBUG, " Build time: %s", buf); + check_magic('e'); + if (1 != fread(buf, 4, 1, f)) + bailout(LOG_ERR, "Error reading ModMiner firmware (data len)"); + len = ((unsigned long)ubuf[0] << 24) | ((unsigned long)ubuf[1] << 16) | (ubuf[2] << 8) | ubuf[3]; + applog(LOG_DEBUG, " Bitstream size: %lu", len); + + int fd = modminer->device_fd; + + applog(LOG_WARNING, "Programming %s... DO NOT EXIT CGMINER UNTIL COMPLETE", modminer->device_path, fpgaid); + buf[0] = '\x05'; // Program Bitstream + buf[1] = fpgaid; + buf[2] = (len >> 0) & 0xff; + buf[3] = (len >> 8) & 0xff; + buf[4] = (len >> 16) & 0xff; + buf[5] = (len >> 24) & 0xff; + if (6 != write(fd, buf, 6)) + bailout2(LOG_ERR, "Error programming ModMiner %s (cmd)", modminer->device_path); + status_read("cmd reply"); + size_t buflen; + while (len) { + buflen = len < 32 ? len : 32; + if (fread(buf, buflen, 1, f) != 1) + bailout2(LOG_ERR, "File underrun programming ModMiner %s (%d bytes left)", modminer->device_path, len); + if (write(fd, buf, buflen) != buflen) + bailout2(LOG_ERR, "Error programming ModMiner %s (data)"); + status_read("status"); + len -= buflen; + } + status_read("final status"); + applog(LOG_WARNING, "Done programming %s", modminer->device_path); + + return true; +} + +static bool +modminer_device_prepare(struct cgpu_info *modminer) +{ + int fd = serial_open(modminer->device_path, 0, /*FIXME=-1*/3000, true); + if (unlikely(-1 == fd)) + bailout(LOG_ERR, "Failed to open ModMiner on %s", modminer->device_path); + + modminer->device_fd = fd; + applog(LOG_INFO, "Opened ModMiner on %s", modminer->device_path); + + struct timeval now; + gettimeofday(&now, NULL); + get_datestamp(modminer->init, &now); + + return true; +} + +#undef bailout + +struct modminer_fpga_state { + bool work_running; + struct work running_work; + struct timeval tv_workstart; + uint32_t hashes; + + char next_work_cmd[46]; + + unsigned char clock; + int no_nonce_counter; + int good_share_counter; + time_t last_cutoff_reduced; +}; + +static bool +modminer_fpga_prepare(struct thr_info *thr) +{ + struct cgpu_info *modminer = thr->cgpu; + + // Don't need to lock the mutex here, since prepare runs from the main thread before the miner threads start + if (modminer->device_fd == -1 && !modminer_device_prepare(modminer)) + return false; + + struct modminer_fpga_state *state; + state = thr->cgpu_data = calloc(1, sizeof(struct modminer_fpga_state)); + state->next_work_cmd[0] = '\x08'; // Send Job + state->next_work_cmd[1] = thr->device_thread; // FPGA id + + return true; +} + +static bool +modminer_reduce_clock(struct thr_info*thr, bool needlock) +{ + struct cgpu_info*modminer = thr->cgpu; + struct modminer_fpga_state *state = thr->cgpu_data; + char fpgaid = thr->device_thread; + int fd = modminer->device_fd; + unsigned char cmd[6], buf[1]; + + if (state->clock <= 100) + return false; + + cmd[0] = '\x06'; // set clock speed + cmd[1] = fpgaid; + cmd[2] = state->clock -= 2; + cmd[3] = cmd[4] = cmd[5] = '\0'; + + if (needlock) + mutex_lock(&modminer->device_mutex); + if (6 != write(fd, cmd, 6)) + bailout2(LOG_ERR, "Error writing to ModMiner (set clock speed)"); + if (serial_read(fd, &buf, 1) != 1) + bailout2(LOG_ERR, "Error reading from ModMiner (set clock speed)"); + if (needlock) + mutex_unlock(&modminer->device_mutex); + + applog(LOG_WARNING, "ModMiner: Setting clock speed of %s %d (FPGA #%u) to %u", modminer->api->name, modminer->device_id, fpgaid, state->clock); + + return true; +} + +static bool +modminer_fpga_init(struct thr_info *thr) +{ + struct cgpu_info *modminer = thr->cgpu; + struct modminer_fpga_state *state = thr->cgpu_data; + int fd; + char fpgaid = thr->device_thread; + + unsigned char cmd[2], buf[4]; + + mutex_lock(&modminer->device_mutex); + fd = modminer->device_fd; + if (fd == -1) { + // Died in another thread... + mutex_unlock(&modminer->device_mutex); + return false; + } + + cmd[0] = '\x04'; // Read USER code (bitstream id) + cmd[1] = fpgaid; + if (write(fd, cmd, 2) != 2) + bailout2(LOG_ERR, "Error writing to ModMiner (read USER code)"); + if (serial_read(fd, buf, 4) != 4) + bailout2(LOG_ERR, "Error reading from ModMiner (read USER code)"); + + if (memcmp(buf, BISTREAM_USER_ID, 4)) { + applog(LOG_ERR, "FPGA #%d not programmed", fpgaid); + if (!modminer_fpga_upload_bitstream(modminer)) + return false; + } + else + applog(LOG_DEBUG, "FPGA #%d is already programmed :)", fpgaid); + + state->clock = 212; // Will be reduced to 210 by modminer_reduce_clock + modminer_reduce_clock(thr, false); + + mutex_unlock(&modminer->device_mutex); + + return true; +} + +static bool +modminer_prepare_next_work(struct modminer_fpga_state*state, struct work*work) +{ + char *midstate = state->next_work_cmd + 2; + char *taildata = midstate + 32; + if (!(memcmp(midstate, work->midstate, 32) || memcmp(taildata, work->data + 64, 12))) + return false; + memcpy(midstate, work->midstate, 32); + memcpy(taildata, work->data + 64, 12); + return true; +} + +static bool +modminer_start_work(struct thr_info*thr) +{ +fd_set fds; + struct cgpu_info*modminer = thr->cgpu; + struct modminer_fpga_state *state = thr->cgpu_data; + int fd = modminer->device_fd; + + char buf[1]; + + mutex_lock(&modminer->device_mutex); + if (46 != write(fd, state->next_work_cmd, 46)) + bailout2(LOG_ERR, "Error writing to ModMiner (start work)"); + gettimeofday(&state->tv_workstart, NULL); + state->hashes = 0; + status_read("start work"); + mutex_unlock(&modminer->device_mutex); + + return true; +} + +#define work_restart(thr) work_restart[thr->id].restart + +static uint64_t +modminer_process_results(struct thr_info*thr) +{ + struct cgpu_info*modminer = thr->cgpu; + struct modminer_fpga_state *state = thr->cgpu_data; + char fpgaid = thr->device_thread; + int fd = modminer->device_fd; + struct work *work = &state->running_work; + + char cmd[2], temperature; + uint32_t nonce; + long iter; + bool bad; + cmd[0] = '\x0a'; + cmd[1] = fpgaid; + + mutex_lock(&modminer->device_mutex); + if (2 == write(fd, cmd, 2) && read(fd, &temperature, 1) == 1) + { + modminer->temp = (float)temperature; + if (temperature > modminer->cutofftemp - 2) { + if (temperature > modminer->cutofftemp) { + applog(LOG_WARNING, "Hit thermal cutoff limit on %s %d, disabling!", modminer->api->name, modminer->device_id); + modminer->deven = DEV_RECOVER; + + modminer->device_last_not_well = time(NULL); + modminer->device_not_well_reason = REASON_DEV_THERMAL_CUTOFF; + ++modminer->dev_thermal_cutoff_count; + } else { + time_t now = time(NULL); + if (state->last_cutoff_reduced != now) { + state->last_cutoff_reduced = now; + modminer_reduce_clock(thr, false); + } + } + } + } + + cmd[0] = '\x09'; + iter = 200; + while (1) { + if (write(fd, cmd, 2) != 2) + bailout2(LOG_ERR, "Error reading from ModMiner (get nonce)"); + serial_read(fd, &nonce, 4); + mutex_unlock(&modminer->device_mutex); + if (memcmp(&nonce, "\xff\xff\xff\xff", 4)) { + state->no_nonce_counter = 0; + bad = !test_nonce(work, nonce); + if (!bad) + submit_nonce(thr, work, nonce); + else { + ++hw_errors; + if (++modminer->hw_errors * 100 > 1000 + state->good_share_counter) + // Only reduce clocks if hardware errors are more than ~1% of results + modminer_reduce_clock(thr, true); + } + } + else + if (++state->no_nonce_counter > 18000) { + state->no_nonce_counter = 0; + modminer_reduce_clock(thr, true); + } + if (work_restart(thr)) + break; + usleep(10000); + if (work_restart(thr) || !--iter) + break; + mutex_lock(&modminer->device_mutex); + } + + struct timeval tv_workend, elapsed; + gettimeofday(&tv_workend, NULL); + timeval_subtract(&elapsed, &tv_workend, &state->tv_workstart); + + uint64_t hashes = (uint64_t)state->clock * (((uint64_t)elapsed.tv_sec * 1000000) + elapsed.tv_usec); + if (hashes > 0xffffffff) + hashes = 0xffffffff; + else + if (hashes <= state->hashes) + hashes = 1; + else + hashes -= state->hashes; + state->hashes += hashes; + return hashes; +} + +static uint64_t +modminer_scanhash(struct thr_info*thr, struct work*work, uint64_t __maybe_unused max_nonce) +{ + struct modminer_fpga_state *state = thr->cgpu_data; + uint64_t hashes = 1; + bool startwork; + + startwork = modminer_prepare_next_work(state, work); + if (state->work_running) { + hashes = modminer_process_results(thr); + if (work_restart(thr)) { + state->work_running = false; + return 1; + } + } + else + state->work_running = true; + + if (startwork) { + if (!modminer_start_work(thr)) + return 0; + memcpy(&state->running_work, work, sizeof(state->running_work)); + } + + // This is intentionally early + work->blk.nonce += hashes; + return hashes; +} + +static void +modminer_fpga_shutdown(struct thr_info *thr) +{ + free(thr->cgpu_data); +} + +struct device_api modminer_api = { + .dname = "modminer", + .name = "MMQ", + .api_detect = modminer_detect, + .get_statline_before = get_modminer_statline_before, + .thread_prepare = modminer_fpga_prepare, + .thread_init = modminer_fpga_init, + .scanhash = modminer_scanhash, + .thread_shutdown = modminer_fpga_shutdown, +}; diff --git a/fpgautils.c b/fpgautils.c new file mode 100644 index 00000000..39f3abd3 --- /dev/null +++ b/fpgautils.c @@ -0,0 +1,273 @@ +/* + * Copyright 2012 Luke Dashjr + * Copyright 2012 Andrew Smith + * + * 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 + +#ifndef WIN32 +#include +#include +#include +#include +#ifndef O_CLOEXEC +#define O_CLOEXEC 0 +#endif +#else +#include +#include +#endif + +#ifdef HAVE_LIBUDEV +#include +#endif + +#include "elist.h" +#include "fpgautils.h" +#include "logging.h" +#include "miner.h" + +char +serial_autodetect_udev(detectone_func_t detectone, const char*prodname) +{ +#ifdef HAVE_LIBUDEV + if (total_devices == MAX_DEVICES) + return 0; + + struct udev *udev = udev_new(); + struct udev_enumerate *enumerate = udev_enumerate_new(udev); + struct udev_list_entry *list_entry; + char found = 0; + + udev_enumerate_add_match_subsystem(enumerate, "tty"); + udev_enumerate_add_match_property(enumerate, "ID_MODEL", prodname); + udev_enumerate_scan_devices(enumerate); + udev_list_entry_foreach(list_entry, udev_enumerate_get_list_entry(enumerate)) { + struct udev_device *device = udev_device_new_from_syspath( + udev_enumerate_get_udev(enumerate), + udev_list_entry_get_name(list_entry) + ); + if (!device) + continue; + + const char *devpath = udev_device_get_devnode(device); + if (devpath && detectone(devpath)) + ++found; + + udev_device_unref(device); + + if (total_devices == MAX_DEVICES) + break; + } + udev_enumerate_unref(enumerate); + udev_unref(udev); + + return found; +#else + return 0; +#endif +} + +char +serial_autodetect_devserial(detectone_func_t detectone, const char*prodname) +{ +#ifndef WIN32 + if (total_devices == MAX_DEVICES) + return 0; + + DIR *D; + struct dirent *de; + const char udevdir[] = "/dev/serial/by-id"; + char devpath[sizeof(udevdir) + 1 + NAME_MAX]; + char *devfile = devpath + sizeof(udevdir); + char found = 0; + + D = opendir(udevdir); + if (!D) + return 0; + memcpy(devpath, udevdir, sizeof(udevdir) - 1); + devpath[sizeof(udevdir) - 1] = '/'; + while ( (de = readdir(D)) ) { + if (!strstr(de->d_name, prodname)) + continue; + strcpy(devfile, de->d_name); + if (detectone(devpath)) { + ++found; + if (total_devices == MAX_DEVICES) + break; + } + } + closedir(D); + + return found; +#else + return 0; +#endif +} + +char +_serial_detect(const char*dnamec, size_t dnamel, detectone_func_t detectone, autoscan_func_t autoscan, bool force_autoscan) +{ + if (total_devices == MAX_DEVICES) + return 0; + + struct string_elist *iter, *tmp; + const char*s; + bool inhibitauto = false; + bool forceauto = false; + char found = 0; + + list_for_each_entry_safe(iter, tmp, &scan_devices, list) { + s = iter->string; + if (!strncmp(dnamec, iter->string, dnamel)) + s += dnamel; + if (!strcmp(s, "auto")) + forceauto = true; + else + if (!strcmp(s, "noauto")) + inhibitauto = true; + else + if (detectone(s)) { + string_elist_del(iter); + inhibitauto = true; + ++found; + if (total_devices == MAX_DEVICES) + break; + } + } + + if ((forceauto || !inhibitauto) && autoscan && total_devices < MAX_DEVICES) + found += autoscan(); + + return found; +} + +int +serial_open(const char*devpath, unsigned long baud, signed short timeout, bool purge) +{ +#ifdef WIN32 + HANDLE hSerial = CreateFile(devpath, GENERIC_READ | GENERIC_WRITE, 0, NULL, OPEN_EXISTING, 0, NULL); + if (unlikely(hSerial == INVALID_HANDLE_VALUE)) + return -1; + + // thanks to af_newbie for pointers about this + COMMCONFIG comCfg = {0}; + comCfg.dwSize = sizeof(COMMCONFIG); + comCfg.wVersion = 1; + comCfg.dcb.DCBlength = sizeof(DCB); + comCfg.dcb.BaudRate = baud; + comCfg.dcb.fBinary = 1; + comCfg.dcb.fDtrControl = DTR_CONTROL_ENABLE; + comCfg.dcb.fRtsControl = RTS_CONTROL_ENABLE; + comCfg.dcb.ByteSize = 8; + + SetCommConfig(hSerial, &comCfg, sizeof(comCfg)); + + const DWORD ctoms = (timeout == -1) ? 30000 : (timeout * 100); + COMMTIMEOUTS cto = {ctoms, 0, ctoms, 0, ctoms}; + SetCommTimeouts(hSerial, &cto); + + if (purge) { + PurgeComm(hSerial, PURGE_RXABORT); + PurgeComm(hSerial, PURGE_TXABORT); + PurgeComm(hSerial, PURGE_RXCLEAR); + PurgeComm(hSerial, PURGE_TXCLEAR); + } + + return _open_osfhandle((LONG)hSerial, 0); +#else + int fdDev = open(devpath, O_RDWR | O_CLOEXEC | O_NOCTTY); + + if (unlikely(fdDev == -1)) + return -1; + + struct termios pattr; + tcgetattr(fdDev, &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; + + switch (baud) { + case 0: break; + case 115200: pattr.c_cflag = B115200; break; + default: + applog(LOG_WARNING, "Unrecognized baud rate: %lu", baud); + } + pattr.c_cflag |= CREAD | CLOCAL; + + if (timeout >= 0) { + pattr.c_cc[VTIME] = (cc_t)timeout; + pattr.c_cc[VMIN] = 0; + } + + tcsetattr(fdDev, TCSANOW, &pattr); + if (purge) + tcflush(fdDev, TCIOFLUSH); + return fdDev; +#endif +} + +ssize_t +_serial_read(int fd, char *buf, size_t bufsiz, char *eol) +{ + ssize_t len, tlen = 0; + while (bufsiz) { + len = read(fd, buf, eol ? 1 : bufsiz); + if (len < 1) + break; + tlen += len; + if (eol && *eol == buf[0]) + break; + buf += len; + bufsiz -= len; + } + return tlen; +} + +static FILE* +_open_bitstream(const char*path, const char*subdir, const char*filename) +{ + char fullpath[PATH_MAX]; + strcpy(fullpath, path); + strcat(fullpath, "/"); + if (subdir) { + strcat(fullpath, subdir); + strcat(fullpath, "/"); + } + strcat(fullpath, filename); + return fopen(fullpath, "rb"); +} +#define _open_bitstream(path, subdir) do { \ + f = _open_bitstream(path, subdir, filename); \ + if (f) \ + return f; \ +} while(0) + +#define _open_bitstream3(path) do { \ + _open_bitstream(path, dname); \ + _open_bitstream(path, "bitstreams"); \ + _open_bitstream(path, NULL); \ +} while(0) + +FILE* +open_bitstream(const char*dname, const char*filename) +{ + FILE *f; + + _open_bitstream3(opt_kernel_path); + _open_bitstream3(cgminer_path); + _open_bitstream3("."); + + return NULL; +} diff --git a/fpgautils.h b/fpgautils.h new file mode 100644 index 00000000..7b8b3317 --- /dev/null +++ b/fpgautils.h @@ -0,0 +1,39 @@ +/* + * 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 3 of the License, or (at your option) + * any later version. See COPYING for more details. + */ + +#ifndef FPGAUTILS_H +#define FPGAUTILS_H + +#include +#include + +typedef bool(*detectone_func_t)(const char*); +typedef char(*autoscan_func_t)(); + +extern char _serial_detect(const char*dnamec, size_t dnamel, detectone_func_t, autoscan_func_t, bool force_autoscan); +#define serial_detect_fauto(dname, detectone, autoscan) \ + _serial_detect(dname ":", sizeof(dname)+1, detectone, autoscan, true) +#define serial_detect_auto(dname, detectone, autoscan) \ + _serial_detect(dname ":", sizeof(dname)+1, detectone, autoscan, false) +#define serial_detect(dname, detectone) \ + _serial_detect(dname ":", sizeof(dname)+1, detectone, NULL, false) +extern char serial_autodetect_devserial(detectone_func_t, const char*prodname); +extern char serial_autodetect_udev (detectone_func_t, const char*prodname); + +extern int serial_open(const char*devpath, unsigned long baud, signed short timeout, bool purge); +extern ssize_t _serial_read(int fd, char *buf, size_t buflen, char*eol); +#define serial_read(fd, buf, count) \ + _serial_read(fd, (char*)(buf), count, NULL) +#define serial_read_line(fd, buf, bufsiz, eol) \ + _serial_read(fd, buf, count, &eol) +#define serial_close(fd) close(fd) + +extern FILE*open_bitstream(const char*dname, const char*filename); + +#endif diff --git a/libztex.c b/libztex.c index ffc2e67d..636c12c4 100644 --- a/libztex.c +++ b/libztex.c @@ -22,6 +22,8 @@ #include #include + +#include "fpgautils.h" #include "miner.h" #include "libztex.h" @@ -150,7 +152,7 @@ static int libztex_configureFpgaHS(struct libztex_device *ztex, const char* firm libusb_claim_interface(ztex->hndl, settings[1]); for (tries = 3; tries > 0; tries--) { - fp = fopen(firmware, "rb"); + fp = open_bitstream("ztex", firmware); if (!fp) { applog(LOG_ERR, "%s: failed to read firmware '%s'", ztex->repr, firmware); return -2; @@ -245,7 +247,7 @@ static int libztex_configureFpgaLS(struct libztex_device *ztex, const char* firm } for (tries = 10; tries > 0; tries--) { - fp = fopen(firmware, "rb"); + fp = open_bitstream("ztex", firmware); if (!fp) { applog(LOG_ERR, "%s: failed to read firmware '%s'", ztex->repr, firmware); return -2; @@ -316,12 +318,11 @@ static int libztex_configureFpgaLS(struct libztex_device *ztex, const char* firm int libztex_configureFpga(struct libztex_device *ztex) { - char buf[256] = "bitstreams/"; + char buf[256]; int rv; - memset(&buf[11], 0, 245); - strcpy(&buf[11], ztex->bitFileName); - strcpy(&buf[strlen(buf)], ".bit"); + strcpy(buf, ztex->bitFileName); + strcat(buf, ".bit"); rv = libztex_configureFpgaHS(ztex, buf, true, 2); if (rv != 0) rv = libztex_configureFpgaLS(ztex, buf, true, 2); diff --git a/miner.h b/miner.h index 9f4da06e..6e9ea704 100644 --- a/miner.h +++ b/miner.h @@ -315,6 +315,7 @@ struct cgpu_info { #endif int device_fd; }; + pthread_mutex_t device_mutex; enum dev_enable deven; int accepted; @@ -747,6 +748,7 @@ struct work { }; extern void get_datestamp(char *, struct timeval *); +extern bool test_nonce(struct work *work, uint32_t nonce); bool submit_nonce(struct thr_info *thr, struct work *work, uint32_t nonce); extern void tailsprintf(char *f, const char *fmt, ...); extern void wlogprint(const char *f, ...);