Browse Source

Merge branch 'cgminer' into bfgminer

Conflicts:
	configure.ac
	driver-icarus.c
Luke Dashjr 13 years ago
parent
commit
daa928eaaa
15 changed files with 954 additions and 245 deletions
  1. 10 0
      Makefile.am
  2. 1 1
      README
  3. 14 6
      adl.c
  4. 23 0
      bitstreams/COPYING_fpgaminer
  5. 0 0
      bitstreams/COPYING_ztex
  6. BIN
      bitstreams/fpgaminer_top_fixed7_197MHz.ncd
  7. 20 2
      configure.ac
  8. 7 141
      driver-bitforce.c
  9. 3 80
      driver-icarus.c
  10. 514 0
      driver-modminer.c
  11. 273 0
      fpgautils.c
  12. 39 0
      fpgautils.h
  13. 7 6
      libztex.c
  14. 41 9
      miner.c
  15. 2 0
      miner.h

+ 10 - 0
Makefile.am

@@ -68,6 +68,10 @@ endif # HAVE_x86_64
 endif # HAS_YASM
 endif # HAS_YASM
 endif # HAS_CPUMINE
 endif # HAS_CPUMINE
 
 
+if NEED_FPGAUTILS
+bfgminer_SOURCES += fpgautils.c
+endif
+
 if HAS_BITFORCE
 if HAS_BITFORCE
 bfgminer_SOURCES += driver-bitforce.c
 bfgminer_SOURCES += driver-bitforce.c
 endif
 endif
@@ -76,6 +80,12 @@ if HAS_ICARUS
 bfgminer_SOURCES += driver-icarus.c
 bfgminer_SOURCES += driver-icarus.c
 endif
 endif
 
 
+if HAS_MODMINER
+bfgminer_SOURCES += driver-modminer.c
+bitstreamsdir = $(bindir)/bitstreams
+dist_bitstreams_DATA = bitstreams/*
+endif
+
 if HAS_ZTEX
 if HAS_ZTEX
 bfgminer_SOURCES += driver-ztex.c libztex.c libztex.h
 bfgminer_SOURCES += driver-ztex.c libztex.c libztex.h
 bitstreamsdir = $(bindir)/bitstreams
 bitstreamsdir = $(bindir)/bitstreams

+ 1 - 1
README

@@ -129,6 +129,7 @@ Options for both config file and command line:
 --debug|-D          Enable debug output
 --debug|-D          Enable debug output
 --expiry|-E <arg>   Upper bound on how many seconds after getting work we consider a share from it stale (default: 120)
 --expiry|-E <arg>   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
 --failover-only     Don't leak work to backup pools when primary pool is lagging
+--kernel-path|-K <arg> 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
 --load-balance      Change multipool strategy from failover to even load balance
 --log|-l <arg>      Interval in seconds between log output (default: 5)
 --log|-l <arg>      Interval in seconds between log output (default: 5)
 --monitor|-m <arg>  Use custom pipe cmd for output messages
 --monitor|-m <arg>  Use custom pipe cmd for output messages
@@ -185,7 +186,6 @@ GPU only options:
 --gpu-vddc <arg>    Set the GPU voltage in Volts - one value for all or separate by commas for per card.
 --gpu-vddc <arg>    Set the GPU voltage in Volts - one value for all or separate by commas for per card.
 --intensity|-I <arg> Intensity of GPU scanning (d or -10 -> 10, default: d to maintain desktop interactivity)
 --intensity|-I <arg> Intensity of GPU scanning (d or -10 -> 10, default: d to maintain desktop interactivity)
 --kernel|-k <arg>   Override kernel to use (diablo, poclbm, phatk or diakgcn) - one value or comma separated
 --kernel|-k <arg>   Override kernel to use (diablo, poclbm, phatk or diakgcn) - one value or comma separated
---kernel-path|-K <arg> Specify a path to where the kernel .cl files are (default: "/usr/local/bin")
 --ndevs|-n          Enumerate number of detected GPUs and exit
 --ndevs|-n          Enumerate number of detected GPUs and exit
 --no-restart        Do not attempt to restart GPUs that hang
 --no-restart        Do not attempt to restart GPUs that hang
 --temp-hysteresis <arg> Set how much the temperature can fluctuate outside limits when automanaging speeds (default: 3)
 --temp-hysteresis <arg> Set how much the temperature can fluctuate outside limits when automanaging speeds (default: 3)

+ 14 - 6
adl.c

@@ -1009,8 +1009,10 @@ static int set_powertune(int gpu, int iPercentage)
 	return ret;
 	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 cgpu_info *cgpu = &gpus[gpu];
 	struct gpu_adl *ga = &cgpu->adl;
 	struct gpu_adl *ga = &cgpu->adl;
@@ -1054,6 +1056,12 @@ static bool fan_autotune(int gpu, int temp, int fanpercent, int lasttemp)
 		newpercent = iMax;
 		newpercent = iMax;
 	else if (newpercent < iMin)
 	else if (newpercent < iMin)
 		newpercent = iMin;
 		newpercent = iMin;
+
+	if (newpercent <= top)
+		*fan_window = true;
+	else
+		*fan_window = false;
+
 	if (newpercent != fanpercent) {
 	if (newpercent != fanpercent) {
 		applog(LOG_INFO, "Setting GPU %d fan percentage to %d", gpu, newpercent);
 		applog(LOG_INFO, "Setting GPU %d fan percentage to %d", gpu, newpercent);
 		set_fanspeed(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)
 void gpu_autotune(int gpu, enum dev_enable *denable)
 {
 {
 	int temp, fanpercent, engine, newengine, twintemp = 0;
 	int temp, fanpercent, engine, newengine, twintemp = 0;
-	bool fan_optimal = true;
+	bool fan_optimal = true, fan_window = true;
 	struct cgpu_info *cgpu;
 	struct cgpu_info *cgpu;
 	struct gpu_adl *ga;
 	struct gpu_adl *ga;
 
 
@@ -1084,7 +1092,7 @@ void gpu_autotune(int gpu, enum dev_enable *denable)
 
 
 	if (temp && fanpercent >= 0 && ga->autofan) {
 	if (temp && fanpercent >= 0 && ga->autofan) {
 		if (!ga->twin)
 		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)) {
 		else if (ga->autofan && (ga->has_fanspeed || !ga->twin->autofan)) {
 			/* On linked GPUs, we autotune the fan only once, based
 			/* On linked GPUs, we autotune the fan only once, based
 			 * on the highest temperature from either GPUs */
 			 * on the highest temperature from either GPUs */
@@ -1102,7 +1110,7 @@ void gpu_autotune(int gpu, enum dev_enable *denable)
 				fan_gpu = gpu;
 				fan_gpu = gpu;
 			else
 			else
 				fan_gpu = ga->twin->gpu;
 				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);
 			applog(LOG_DEBUG, "Temperature %d degrees over target, decreasing clock speed", opt_hysteresis);
 			newengine = engine - ga->lpOdParameters.sEngineClock.iStep;
 			newengine = engine - ga->lpOdParameters.sEngineClock.iStep;
 			/* Only try to tune engine speed up if this GPU is not disabled */
 			/* 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");
 			applog(LOG_DEBUG, "Temperature below target, increasing clock speed");
 			if (temp < ga->targettemp - opt_hysteresis)
 			if (temp < ga->targettemp - opt_hysteresis)
 				newengine = ga->maxspeed;
 				newengine = ga->maxspeed;

+ 23 - 0
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 <http://www.gnu.org/licenses/>.
+
+----
+
+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

+ 0 - 0
bitstreams/LICENSE.txt → bitstreams/COPYING_ztex


BIN
bitstreams/fpgaminer_top_fixed7_197MHz.ncd


+ 20 - 2
configure.ac

@@ -175,6 +175,17 @@ if test "x$icarus" = xyes; then
 fi
 fi
 AM_CONDITIONAL([HAS_ICARUS], [test x$icarus = xyes])
 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"
 ztex="no"
 
 
 AC_ARG_ENABLE([ztex],
 AC_ARG_ENABLE([ztex],
@@ -211,6 +222,7 @@ else
 fi
 fi
 
 
 
 
+AM_CONDITIONAL([NEED_FPGAUTILS], [test x$icarus$bitforce$modminer$ztex != xnononono])
 AM_CONDITIONAL([HAVE_CURSES], [test x$curses = xyes])
 AM_CONDITIONAL([HAVE_CURSES], [test x$curses = xyes])
 AM_CONDITIONAL([WANT_JANSSON], [test x$request_jansson = xtrue])
 AM_CONDITIONAL([WANT_JANSSON], [test x$request_jansson = xtrue])
 AM_CONDITIONAL([HAVE_WINDOWS], [test x$have_win32 = xtrue])
 AM_CONDITIONAL([HAVE_WINDOWS], [test x$have_win32 = xtrue])
@@ -259,7 +271,7 @@ fi
 
 
 AM_CONDITIONAL([HAS_YASM], [test x$has_yasm = xtrue])
 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)])],
 	AC_ARG_WITH([libudev], [AC_HELP_STRING([--without-libudev], [Autodetect FPGAs using libudev (default enabled)])],
 		[libudev=$withval],
 		[libudev=$withval],
 		[libudev=auto]
 		[libudev=auto]
@@ -404,13 +416,19 @@ else
 	echo "  Icarus.FPGAs.........: Disabled"
 	echo "  Icarus.FPGAs.........: Disabled"
 fi
 fi
 
 
+if test "x$modminer" = xyes; then
+	echo "  ModMiner.FPGAs.......: Enabled"
+else
+	echo "  ModMiner.FPGAs.......: Disabled"
+fi
+
 if test "x$ztex" = xyes; then
 if test "x$ztex" = xyes; then
 	echo "  Ztex.FPGAs...........: Enabled"
 	echo "  Ztex.FPGAs...........: Enabled"
 else
 else
 	echo "  Ztex.FPGAs...........: Disabled"
 	echo "  Ztex.FPGAs...........: Disabled"
 fi
 fi
 
 
-if test "x$bitforce" != xno; then
+if test "x$bitforce$modminer" != xnono; then
 	echo "  libudev.detection....: $libudev"
 	echo "  libudev.detection....: $libudev"
 fi
 fi
 
 

+ 7 - 141
driver-bitforce.c

@@ -13,62 +13,17 @@
 #include <stdio.h>
 #include <stdio.h>
 #include <strings.h>
 #include <strings.h>
 #include <sys/time.h>
 #include <sys/time.h>
-#include <sys/types.h>
-#include <dirent.h>
-#ifndef WIN32
-#include <termios.h>
-#include <sys/stat.h>
-#include <fcntl.h>
-#ifndef O_CLOEXEC
-#define O_CLOEXEC 0
-#endif
-#else
-#include <windows.h>
-#include <io.h>
-#endif
 #include <unistd.h>
 #include <unistd.h>
 
 
 #include "config.h"
 #include "config.h"
 
 
-#ifdef HAVE_LIBUDEV
-#include <libudev.h>
-#endif
-
-#include "elist.h"
+#include "fpgautils.h"
 #include "miner.h"
 #include "miner.h"
 
 
 
 
 struct device_api bitforce_api;
 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)
 static void BFgets(char *buf, size_t bufLen, int fd)
 {
 {
@@ -98,9 +53,6 @@ static bool bitforce_detect_one(const char *devpath)
 	char *s;
 	char *s;
 	char pdevbuf[0x100];
 	char pdevbuf[0x100];
 
 
-	if (total_devices == MAX_DEVICES)
-		return false;
-
 	int fdDev = BFopen(devpath);
 	int fdDev = BFopen(devpath);
 	if (unlikely(fdDev == -1)) {
 	if (unlikely(fdDev == -1)) {
 		applog(LOG_DEBUG, "BitForce Detect: Failed to open %s", devpath);
 		applog(LOG_DEBUG, "BitForce Detect: Failed to open %s", devpath);
@@ -134,103 +86,17 @@ static bool bitforce_detect_one(const char *devpath)
 	return add_cgpu(bitforce);
 	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;
 	0;
 }
 }
 
 
 static void bitforce_detect()
 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)
 static void get_bitforce_statline_before(char *buf, struct cgpu_info *bitforce)

+ 3 - 80
driver-icarus.c

@@ -55,6 +55,7 @@
 #endif
 #endif
 
 
 #include "elist.h"
 #include "elist.h"
+#include "fpgautils.h"
 #include "miner.h"
 #include "miner.h"
 
 
 // The serial I/O speed - Linux uses a define 'B115200' in bits/termios.h
 // The serial I/O speed - Linux uses a define 'B115200' in bits/termios.h
@@ -196,71 +197,7 @@ static void rev(unsigned char *s, size_t l)
 	}
 	}
 }
 }
 
 
-static int icarus_open2(const char *devpath, __maybe_unused bool purge)
-{
-#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] = ICARUS_READ_FAULT_DECISECONDS;
-	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));
-
-	const DWORD ctoms = ICARUS_READ_FAULT_DECISECONDS * 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);
-#endif
-}
-
+#define icarus_open2(devpath, purge)  serial_open(devpath, 115200, ICARUS_READ_FAULT_DECISECONDS, purge)
 #define icarus_open(devpath)  icarus_open2(devpath, false)
 #define icarus_open(devpath)  icarus_open2(devpath, false)
 
 
 static int icarus_gets(unsigned char *buf, int fd, struct timeval *tv_finish, struct thr_info*thr, int read_count)
 static int icarus_gets(unsigned char *buf, int fd, struct timeval *tv_finish, struct thr_info*thr, int read_count)
@@ -485,9 +422,6 @@ static bool icarus_detect_one(const char *devpath)
 	unsigned char ob_bin[64], nonce_bin[ICARUS_READ_SIZE];
 	unsigned char ob_bin[64], nonce_bin[ICARUS_READ_SIZE];
 	char *nonce_hex;
 	char *nonce_hex;
 
 
-	if (total_devices == MAX_DEVICES)
-		return false;
-
 	fd = icarus_open2(devpath, true);
 	fd = icarus_open2(devpath, true);
 	if (unlikely(fd == -1)) {
 	if (unlikely(fd == -1)) {
 		applog(LOG_ERR, "Icarus Detect: Failed to open %s", devpath);
 		applog(LOG_ERR, "Icarus Detect: Failed to open %s", devpath);
@@ -553,18 +487,7 @@ static bool icarus_detect_one(const char *devpath)
 
 
 static void icarus_detect()
 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);
 }
 }
 
 
 struct icarus_state {
 struct icarus_state {

+ 514 - 0
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 <stdarg.h>
+#include <stdio.h>
+#include <unistd.h>
+
+#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)  thr->work_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,
+};

+ 273 - 0
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 <sys/types.h>
+#include <dirent.h>
+#include <string.h>
+
+#ifndef WIN32
+#include <termios.h>
+#include <sys/stat.h>
+#include <unistd.h>
+#include <fcntl.h>
+#ifndef O_CLOEXEC
+#define O_CLOEXEC 0
+#endif
+#else
+#include <windows.h>
+#include <io.h>
+#endif
+
+#ifdef HAVE_LIBUDEV
+#include <libudev.h>
+#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;
+}

+ 39 - 0
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 <stdbool.h>
+#include <stdio.h>
+
+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

+ 7 - 6
libztex.c

@@ -22,6 +22,8 @@
 
 
 #include <stdio.h>
 #include <stdio.h>
 #include <unistd.h>
 #include <unistd.h>
+
+#include "fpgautils.h"
 #include "miner.h"
 #include "miner.h"
 #include "libztex.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]);
 	libusb_claim_interface(ztex->hndl, settings[1]);
 
 
 	for (tries = 3; tries > 0; tries--) {
 	for (tries = 3; tries > 0; tries--) {
-		fp = fopen(firmware, "rb");
+		fp = open_bitstream("ztex", firmware);
 		if (!fp) {
 		if (!fp) {
 			applog(LOG_ERR, "%s: failed to read firmware '%s'", ztex->repr, firmware);
 			applog(LOG_ERR, "%s: failed to read firmware '%s'", ztex->repr, firmware);
 			return -2;
 			return -2;
@@ -245,7 +247,7 @@ static int libztex_configureFpgaLS(struct libztex_device *ztex, const char* firm
 	}
 	}
 
 
 	for (tries = 10; tries > 0; tries--) {
 	for (tries = 10; tries > 0; tries--) {
-		fp = fopen(firmware, "rb");
+		fp = open_bitstream("ztex", firmware);
 		if (!fp) {
 		if (!fp) {
 			applog(LOG_ERR, "%s: failed to read firmware '%s'", ztex->repr, firmware);
 			applog(LOG_ERR, "%s: failed to read firmware '%s'", ztex->repr, firmware);
 			return -2;
 			return -2;
@@ -316,12 +318,11 @@ static int libztex_configureFpgaLS(struct libztex_device *ztex, const char* firm
 
 
 int libztex_configureFpga(struct libztex_device *ztex)
 int libztex_configureFpga(struct libztex_device *ztex)
 {
 {
-	char buf[256] = "bitstreams/";
+	char buf[256];
 	int rv;
 	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); 
 	rv = libztex_configureFpgaHS(ztex, buf, true, 2); 
 	if (rv != 0)
 	if (rv != 0)
 		rv = libztex_configureFpgaLS(ztex, buf, true, 2); 
 		rv = libztex_configureFpgaLS(ztex, buf, true, 2); 

+ 41 - 9
miner.c

@@ -54,6 +54,13 @@
 	#include <sys/wait.h>
 	#include <sys/wait.h>
 #endif
 #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 {
 enum workio_commands {
 	WC_GET_WORK,
 	WC_GET_WORK,
 	WC_SUBMIT_WORK,
 	WC_SUBMIT_WORK,
@@ -467,7 +474,7 @@ static char *set_int_1_to_10(const char *arg, int *i)
 	return set_int_range(arg, i, 1, 10);
 	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)
 static char *add_serial(char *arg)
 {
 {
 	string_elist_add(arg, &scan_devices);
 	string_elist_add(arg, &scan_devices);
@@ -656,8 +663,12 @@ static void load_temp_cutoffs()
 			devices[device]->cutofftemp = val;
 			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) {
 	if (device <= 1) {
 		for (i = device; i < total_devices; ++i)
 		for (i = device; i < total_devices; ++i)
 			devices[i]->cutofftemp = val;
 			devices[i]->cutofftemp = val;
@@ -767,7 +778,7 @@ static struct opt_table opt_config_table[] = {
 			opt_hidden
 			opt_hidden
 #endif
 #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_WITHOUT_ARG("--enable-cpu|-C",
 			opt_set_bool, &opt_usecpu,
 			opt_set_bool, &opt_usecpu,
 			"Enable CPU mining with other mining (default: no CPU mining if other devices exist)"),
 			"Enable CPU mining with other mining (default: no CPU mining if other devices exist)"),
@@ -817,9 +828,13 @@ static struct opt_table opt_config_table[] = {
 	OPT_WITH_ARG("--intensity|-I",
 	OPT_WITH_ARG("--intensity|-I",
 		     set_intensity, NULL, NULL,
 		     set_intensity, NULL, NULL,
 		     "Intensity of GPU scanning (d or " _MIN_INTENSITY_STR " -> " _MAX_INTENSITY_STR ", default: d to maintain desktop interactivity)"),
 		     "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_WITH_ARG("--kernel-path|-K",
 		     opt_set_charp, opt_show_charp, &opt_kernel_path,
 		     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",
 	OPT_WITH_ARG("--kernel|-k",
 		     set_kernel, NULL, NULL,
 		     set_kernel, NULL, NULL,
 		     "Override kernel to use (diablo, poclbm, phatk or diakgcn) - one value or comma separated"),
 		     "Override kernel to use (diablo, poclbm, phatk or diakgcn) - one value or comma separated"),
@@ -901,7 +916,7 @@ static struct opt_table opt_config_table[] = {
 	OPT_WITHOUT_ARG("--round-robin",
 	OPT_WITHOUT_ARG("--round-robin",
 		     set_rr, &pool_strategy,
 		     set_rr, &pool_strategy,
 		     "Change multipool strategy from failover to round robin on failure"),
 		     "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",
 	OPT_WITH_ARG("--scan-serial|-S",
 		     add_serial, NULL, NULL,
 		     add_serial, NULL, NULL,
 		     "Serial port to probe for FPGA Mining device"),
 		     "Serial port to probe for FPGA Mining device"),
@@ -932,7 +947,7 @@ static struct opt_table opt_config_table[] = {
 			opt_set_bool, &use_syslog,
 			opt_set_bool, &use_syslog,
 			"Use system log for output messages (default: standard error)"),
 			"Use system log for output messages (default: standard error)"),
 #endif
 #endif
-#if defined(HAVE_ADL) || defined(USE_BITFORCE)
+#if defined(HAVE_ADL) || defined(USE_BITFORCE) || defined(USE_MODMINER)
 	OPT_WITH_ARG("--temp-cutoff",
 	OPT_WITH_ARG("--temp-cutoff",
 		     set_temp_cutoff, opt_show_intval, &opt_cutofftemp,
 		     set_temp_cutoff, opt_show_intval, &opt_cutofftemp,
 		     "Temperature where a device will be automatically disabled, one value or comma separated list"),
 		     "Temperature where a device will be automatically disabled, one value or comma separated list"),
@@ -1139,6 +1154,9 @@ static char *opt_verusage_and_exit(const char *extra)
 #ifdef USE_ICARUS
 #ifdef USE_ICARUS
 		"icarus "
 		"icarus "
 #endif
 #endif
+#ifdef USE_MODMINER
+		"modminer "
+#endif
 #ifdef USE_ZTEX
 #ifdef USE_ZTEX
 		"ztex "
 		"ztex "
 #endif
 #endif
@@ -3765,15 +3783,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 + 0] = (nonce >> 0) & 0xff;
 	work->data[64 + 12 + 1] = (nonce >> 8) & 0xff;
 	work->data[64 + 12 + 1] = (nonce >> 8) & 0xff;
 	work->data[64 + 12 + 2] = (nonce >> 16) & 0xff;
 	work->data[64 + 12 + 2] = (nonce >> 16) & 0xff;
 	work->data[64 + 12 + 3] = (nonce >> 24) & 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 */
 	/* 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");
 		applog(LOG_INFO, "Share below target");
 		return true;
 		return true;
 	}
 	}
@@ -4785,6 +4809,10 @@ extern struct device_api bitforce_api;
 extern struct device_api icarus_api;
 extern struct device_api icarus_api;
 #endif
 #endif
 
 
+#ifdef USE_MODMINER
+extern struct device_api modminer_api;
+#endif
+
 #ifdef USE_ZTEX
 #ifdef USE_ZTEX
 extern struct device_api ztex_api;
 extern struct device_api ztex_api;
 #endif
 #endif
@@ -5030,6 +5058,10 @@ int main(int argc, char *argv[])
 	bitforce_api.api_detect();
 	bitforce_api.api_detect();
 #endif
 #endif
 
 
+#ifdef USE_MODMINER
+	modminer_api.api_detect();
+#endif
+
 #ifdef USE_ZTEX
 #ifdef USE_ZTEX
 	ztex_api.api_detect();
 	ztex_api.api_detect();
 #endif
 #endif

+ 2 - 0
miner.h

@@ -311,6 +311,7 @@ struct cgpu_info {
 #endif
 #endif
 		int device_fd;
 		int device_fd;
 	};
 	};
+	pthread_mutex_t		device_mutex;
 
 
 	enum dev_enable deven;
 	enum dev_enable deven;
 	int accepted;
 	int accepted;
@@ -739,6 +740,7 @@ struct work {
 };
 };
 
 
 extern void get_datestamp(char *, struct timeval *);
 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);
 bool submit_nonce(struct thr_info *thr, struct work *work, uint32_t nonce);
 extern void tailsprintf(char *f, const char *fmt, ...);
 extern void tailsprintf(char *f, const char *fmt, ...);
 extern void wlogprint(const char *f, ...);
 extern void wlogprint(const char *f, ...);