Browse Source

Merge branch 'nanofury' into bfgminer

Conflicts:
	deviceapi.c
	miner.c
Luke Dashjr 12 years ago
parent
commit
863d910324
21 changed files with 1279 additions and 88 deletions
  1. 1 0
      AUTHORS
  2. 9 0
      Makefile.am
  3. 5 1
      README
  4. 3 0
      api.c
  5. 49 1
      configure.ac
  6. 3 0
      deviceapi.c
  7. 6 0
      driver-bfsb.c
  8. 48 2
      driver-bitfury.c
  9. 5 0
      driver-bitfury.h
  10. 6 0
      driver-metabank.c
  11. 311 0
      driver-nanofury.c
  12. 16 4
      driver-x6500.c
  13. 36 61
      ft232r.c
  14. 3 6
      ft232r.h
  15. 80 0
      lowlevel.c
  16. 39 0
      lowlevel.h
  17. 1 0
      make-release
  18. 445 0
      mcp2210.c
  19. 28 0
      mcp2210.h
  20. 180 13
      miner.c
  21. 5 0
      miner.h

+ 1 - 0
AUTHORS

@@ -18,6 +18,7 @@ Denis Ahrens <denis@h3q.com>
 blinkier <blinkiest@gmail.com>
 Peter Stuge <peter@stuge.se>
 Paul Sheppard <shepsoft@gmail.com>
+Vladimir Strinski <vstrinski@nanofury.com>
 Jason Snell <abysss@gmail.com>
 Mark Crichton <crichton@gmail.com>
 Zefir Kurtisi <zefir.kurtisi@gmail.com>

+ 9 - 0
Makefile.am

@@ -177,6 +177,10 @@ bfgminer_SOURCES += iospeeds.h iospeeds_posix.h
 endif
 endif
 
+if NEED_BFG_LOWLEVEL
+bfgminer_SOURCES += lowlevel.c lowlevel.h
+endif
+
 if NEED_DYNCLOCK
 bfgminer_SOURCES += dynclock.c dynclock.h
 endif
@@ -243,6 +247,11 @@ endif
 if HAS_LITTLEFURY
 bfgminer_SOURCES += driver-littlefury.c
 endif
+
+if HAS_NANOFURY
+bfgminer_SOURCES += driver-nanofury.c mcp2210.c mcp2210.h
+bfgminer_CPPFLAGS += $(hidapi_CFLAGS)
+endif
 endif
 
 bin_PROGRAMS += bfgminer-rpc

+ 5 - 1
README

@@ -81,7 +81,7 @@ BUILDING BFGMINER
 Everything you probably want, condensed:
 	build-essential autoconf automake libtool pkg-config libcurl4-gnutls-dev
 	libjansson-dev uthash-dev libncursesw5-dev libudev-dev libusb-1.0-0-dev
-	libevent-dev libmicrohttpd-dev
+	libevent-dev libmicrohttpd-dev hidapi
 
 Dependencies:
 	autoconf             http://www.gnu.org/software/autoconf/
@@ -106,6 +106,9 @@ Optional Dependencies:
 	  sysfs              (builtin to most Linux kernels, just mount on /sys)
 	  libudev-dev        http://www.freedesktop.org/software/systemd/libudev/
 	
+	NanoFury USB sticks:
+	  hidapi             https://github.com/signal11/hidapi
+	
 	getwork server for Block Erupter Blades:
 	  libmicrohttpd-dev 0.9.5+  http://www.gnu.org/software/libmicrohttpd/
 	
@@ -244,6 +247,7 @@ Options for both config file and command line:
 --sched-start <arg> Set a time of day in HH:MM to start mining (a once off without a stop time)
 --sched-stop <arg>  Set a time of day in HH:MM to stop mining (will quit without a start time)
 --scrypt            Use the scrypt algorithm for mining (non-bitcoin)
+--set-device <arg>  Set default parameters on devices; eg, NFY:osc6_bits=50
 --setuid <arg>      Username of an unprivileged user to run as
 --sharelog <arg>    Append share log to file
 --shares <arg>      Quit after mining N shares (default: unlimited)

+ 3 - 0
api.c

@@ -111,6 +111,9 @@ static const char *DEVICECODE = ""
 #ifdef USE_METABANK
 			"MBF "
 #endif
+#ifdef USE_NANOFURY
+			"NFY "
+#endif
 #ifdef USE_AVALON
 			"AVA "
 #endif

+ 49 - 1
configure.ac

@@ -111,6 +111,7 @@ optlist=
 
 need_dynclock=no
 need_fpgautils=no
+need_lowlevel=no
 have_cygwin=false
 have_win32=false
 have_macho=false
@@ -527,6 +528,7 @@ if test "x$x6500" = xyes; then
 	AC_DEFINE([USE_X6500], [1], [Defined to 1 if X6500 support is wanted])
 	need_dynclock=yes
 	need_fpgautils=yes
+	need_lowlevel=yes
 fi
 AM_CONDITIONAL([HAS_X6500], [test x$x6500 = xyes])
 
@@ -624,6 +626,47 @@ fi
 AM_CONDITIONAL([HAS_LITTLEFURY], [test x$littlefury = xyes])
 
 
+driverlist="$driverlist nanofury"
+nanofury=auto
+AC_ARG_ENABLE([nanofury],
+	[AC_HELP_STRING([--disable-nanofury],[Compile support for NanoFury (default enabled)])],
+	[nanofury=$enableval]
+	)
+if test "x$nanofury" = "xno"; then
+	true
+elif test "x$bitfury" = "xyes"; then
+	_found_hidapi=false
+	for _hidapi_lib in hidapi hidapi-hidraw hidapi-libusb; do
+		PKG_CHECK_MODULES([hidapi],[$_hidapi_lib],[
+			found_hidapi=true
+			break
+		],[
+			true
+		])
+	done
+	if test x$found_hidapi = xtrue; then
+		nanofury=yes
+	else
+		if test x$nanofury = xauto; then
+			nanofury=no
+			nanofury_enableaction="install hidapi"
+		else
+			AC_MSG_ERROR([Could not find hidapi, required for NanoFury support])
+		fi
+	fi
+elif test "x$nanofury" = "xyes"; then
+	AC_MSG_ERROR([You explicitly disabled Bitfury and explicitly enabled NanoFury])
+else
+	nanofury=no
+fi
+if test "x$nanofury" = "xyes"; then
+	AC_DEFINE([USE_NANOFURY], [1], [Defined to 1 if NanoFury support is wanted])
+	need_fpgautils=yes
+	need_lowlevel=yes
+fi
+AM_CONDITIONAL([HAS_NANOFURY], [test x$nanofury = xyes])
+
+
 driverlist="$driverlist metabank"
 metabank=no
 AC_ARG_ENABLE([metabank],
@@ -682,6 +725,10 @@ if test x$need_fpgautils = xyes; then
 	fi
 fi
 
+if test x$need_lowlevel = xyes; then
+	AC_DEFINE([HAVE_BFG_LOWLEVEL], [1], [Defined to 1 if lowlevel drivers are being used])
+fi
+
 
 curses="auto"
 
@@ -835,6 +882,7 @@ AC_SUBST(libblkmaker_LIBS)
 AM_CONDITIONAL([NEED_LIBBLKMAKER], [test x$with_system_libblkmaker != xyes])
 AM_CONDITIONAL([NEED_DYNCLOCK], [test x$need_dynclock = xyes])
 AM_CONDITIONAL([NEED_FPGAUTILS], [test x$need_fpgautils = xyes])
+AM_CONDITIONAL([NEED_BFG_LOWLEVEL], [test x$need_lowlevel = xyes])
 AM_CONDITIONAL([HAS_SCRYPT], [test x$scrypt = xyes])
 AM_CONDITIONAL([HAVE_CURSES], [test x$curses = xyes])
 AM_CONDITIONAL([HAVE_SENSORS], [test x$with_sensors = xyes])
@@ -1244,7 +1292,7 @@ BFG_PRINT_LIST([Options...],[optlist])
 
 echo
 echo "Compilation............: make (or gmake)"
-echo "  CFLAGS...............: $CPPFLAGS $NCURSES_CPPFLAGS $PTHREAD_FLAGS $CFLAGS $LIBUSB_CFLAGS $JANSSON_CFLAGS $PTHREAD_FLAGS $libblkmaker_CFLAGS"
+echo "  CFLAGS...............: $CPPFLAGS $NCURSES_CPPFLAGS $PTHREAD_FLAGS $CFLAGS $LIBUSB_CFLAGS $JANSSON_CFLAGS $PTHREAD_FLAGS $libblkmaker_CFLAGS $hidapi_CFLAGS"
 echo "  LDFLAGS..............: $LDFLAGS $PTHREAD_FLAGS $libblkmaker_LDFLAGS $PTHREAD_LIBS $LIBS $DLOPEN_FLAGS $LIBCURL_LIBS $JANSSON_LIBS $NCURSES_LIBS $PDCURSES_LIBS $WS2_LIBS $MATH_LIBS $UDEV_LIBS $LIBUSB_LIBS $RT_LIBS $sensors_LIBS $libblkmaker_LIBS"
 echo
 echo "Installation...........: make install$maybe_ldconfig #(as root if needed, with 'su' or 'sudo')"

+ 3 - 0
deviceapi.c

@@ -623,6 +623,9 @@ void *miner_thread(void *userdata)
 		goto out;
 	}
 
+	if (drv_ready(cgpu))
+		cgpu_set_defaults(cgpu);
+	
 	thread_reportout(mythr);
 	applog(LOG_DEBUG, "Popping ping in miner thread");
 	notifier_read(mythr->notifier);  // Wait for a notification to start

+ 6 - 0
driver-bfsb.c

@@ -210,4 +210,10 @@ struct device_drv bfsb_drv = {
 	.thread_disable = bfsb_disable,
 	.thread_enable = bfsb_enable,
 	.thread_shutdown = bfsb_shutdown,
+	
+#ifdef HAVE_CURSES
+	.proc_wlogprint_status = bitfury_wlogprint_status,
+	.proc_tui_wlogprint_choices = bitfury_tui_wlogprint_choices,
+	.proc_tui_handle_choice = bitfury_tui_handle_choice,
+#endif
 };

+ 48 - 2
driver-bitfury.c

@@ -30,6 +30,7 @@
 #include <sha2.h>
 
 #include "deviceapi.h"
+#include "driver-bitfury.h"
 #include "libbitfury.h"
 #include "util.h"
 #include "spidevc.h"
@@ -143,6 +144,8 @@ tryagain:
 			differ = i;
 			applog(LOG_DEBUG, "%"PRIpreprv": %s: Differ at %d",
 			       proc->proc_repr, __func__, i);
+			if (tried > 3)
+				break;
 		}
 	}
 	if (-1 == differ)
@@ -711,7 +714,7 @@ char *bitfury_set_device(struct cgpu_info * const proc, char * const option, cha
 	
 	if (!strcasecmp(option, "help"))
 	{
-		sprintf(replybuf, "baud: SPI baud rate\nosc6_bits: range 1-55 (slow to fast)");
+		sprintf(replybuf, "baud: SPI baud rate\nosc6_bits: range 1-%d (slow to fast)", BITFURY_MAX_OSC6_BITS);
 		return replybuf;
 	}
 	
@@ -726,7 +729,7 @@ char *bitfury_set_device(struct cgpu_info * const proc, char * const option, cha
 	if (!strcasecmp(option, "osc6_bits"))
 	{
 		newval = bitfury->osc6_bits;
-		if (!_bitfury_set_device_parse_setting(&newval, setting, replybuf, 55))
+		if (!_bitfury_set_device_parse_setting(&newval, setting, replybuf, BITFURY_MAX_OSC6_BITS))
 			return replybuf;
 		
 		bitfury->osc6_bits = newval;
@@ -739,6 +742,49 @@ char *bitfury_set_device(struct cgpu_info * const proc, char * const option, cha
 	return replybuf;
 }
 
+#ifdef HAVE_CURSES
+void bitfury_tui_wlogprint_choices(struct cgpu_info *cgpu)
+{
+	wlogprint("[O]scillator bits ");
+}
+
+const char *bitfury_tui_handle_choice(struct cgpu_info *cgpu, int input)
+{
+	struct bitfury_device * const bitfury = cgpu->device_data;
+	char buf[0x100];
+	
+	switch (input)
+	{
+		case 'o': case 'O':
+		{
+			int val;
+			char *intvar;
+			
+			sprintf(buf, "Set oscillator bits (range 1-%d; slow to fast)", BITFURY_MAX_OSC6_BITS);
+			intvar = curses_input(buf);
+			if (!intvar)
+				return "Invalid oscillator bits\n";
+			val = atoi(intvar);
+			free(intvar);
+			if (val < 1 || val > BITFURY_MAX_OSC6_BITS)
+				return "Invalid oscillator bits\n";
+			
+			bitfury->osc6_bits = val;
+			bitfury->force_reinit = true;
+			
+			return "Oscillator bits changing\n";
+		}
+	}
+	return NULL;
+}
+
+void bitfury_wlogprint_status(struct cgpu_info *cgpu)
+{
+	struct bitfury_device * const bitfury = cgpu->device_data;
+	wlogprint("Oscillator bits: %d\n", bitfury->osc6_bits);
+}
+#endif
+
 struct device_drv bitfury_drv = {
 	.dname = "bitfury_gpio",
 	.name = "BFY",

+ 5 - 0
driver-bitfury.h

@@ -6,6 +6,8 @@
 
 #include "miner.h"
 
+#define BITFURY_MAX_OSC6_BITS  55
+
 extern bool bitfury_prepare(struct thr_info *);
 extern bool bitfury_init_chip(struct cgpu_info *);
 
@@ -18,6 +20,9 @@ extern int64_t bitfury_job_process_results(struct thr_info *, struct work *, boo
 extern struct api_data *bitfury_api_device_detail(struct cgpu_info *);
 extern struct api_data *bitfury_api_device_status(struct cgpu_info *);
 extern char *bitfury_set_device(struct cgpu_info *, char *, char *, char *);
+extern void bitfury_tui_wlogprint_choices(struct cgpu_info *);
+extern const char *bitfury_tui_handle_choice(struct cgpu_info *, int input);
+extern void bitfury_wlogprint_status(struct cgpu_info *);
 
 extern void bitfury_shutdown(struct thr_info *);
 

+ 6 - 0
driver-metabank.c

@@ -241,4 +241,10 @@ struct device_drv metabank_drv = {
 	.get_api_extra_device_status = metabank_api_extra_device_status,
 	.get_stats = metabank_get_stats,
 	.set_device = bitfury_set_device,
+	
+#ifdef HAVE_CURSES
+	.proc_wlogprint_status = bitfury_wlogprint_status,
+	.proc_tui_wlogprint_choices = bitfury_tui_wlogprint_choices,
+	.proc_tui_handle_choice = bitfury_tui_handle_choice,
+#endif
 };

+ 311 - 0
driver-nanofury.c

@@ -0,0 +1,311 @@
+/*
+ * Copyright 2013 Luke Dashjr
+ * Copyright 2013 Vladimir Strinski
+ *
+ * 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 <stdbool.h>
+
+#include "deviceapi.h"
+#include "driver-bitfury.h"
+#include "libbitfury.h"
+#include "logging.h"
+#include "lowlevel.h"
+#include "mcp2210.h"
+#include "miner.h"
+
+#define NANOFURY_USB_PRODUCT "NanoFury"
+
+#define NANOFURY_GP_PIN_LED 0
+#define NANOFURY_GP_PIN_SCK_OVR 5
+#define NANOFURY_GP_PIN_PWR_EN 6
+
+#define NANOFURY_MAX_BYTES_PER_SPI_TRANSFER 60			// due to MCP2210 limitation
+
+struct device_drv nanofury_drv;
+
+// Bit-banging reset, to reset more chips in chain - toggle for longer period... Each 3 reset cycles reset first chip in chain
+static
+bool nanofury_spi_reset(struct mcp2210_device * const mcp)
+{
+	int r;
+	char tx[1] = {0x81};  // will send this waveform: - _ _ _  _ _ _ -
+	char buf[1];
+	
+	// SCK_OVRRIDE
+	if (!mcp2210_set_gpio_output(mcp, NANOFURY_GP_PIN_SCK_OVR, MGV_HIGH))
+		return false;
+	
+	for (r = 0; r < 16; ++r)
+		if (!mcp2210_spi_transfer(mcp, tx, buf, 1))
+			return false;
+	
+	if (mcp2210_get_gpio_input(mcp, NANOFURY_GP_PIN_SCK_OVR) == MGV_ERROR)
+		return false;
+	
+	return true;
+}
+
+static
+bool nanofury_spi_txrx(struct spi_port * const port)
+{
+	struct cgpu_info * const cgpu = port->cgpu;
+	struct thr_info * const thr = cgpu->thr[0];
+	struct mcp2210_device * const mcp = thr->cgpu_data;
+	const void *wrbuf = spi_gettxbuf(port);
+	void *rdbuf = spi_getrxbuf(port);
+	size_t bufsz = spi_getbufsz(port);
+	const uint8_t *ptrwrbuf = wrbuf;
+	uint8_t *ptrrdbuf = rdbuf;
+	
+	nanofury_spi_reset(mcp);
+	
+	// start by sending chunks of 60 bytes...
+	while (bufsz >= NANOFURY_MAX_BYTES_PER_SPI_TRANSFER)
+	{
+		if (!mcp2210_spi_transfer(mcp, ptrwrbuf, ptrrdbuf, NANOFURY_MAX_BYTES_PER_SPI_TRANSFER))
+			return false;
+		ptrrdbuf += NANOFURY_MAX_BYTES_PER_SPI_TRANSFER;
+		ptrwrbuf += NANOFURY_MAX_BYTES_PER_SPI_TRANSFER;
+		bufsz -= NANOFURY_MAX_BYTES_PER_SPI_TRANSFER;
+	}
+	
+	// send any remaining bytes...
+	if (bufsz > 0)
+	{
+		if (!mcp2210_spi_transfer(mcp, ptrwrbuf, ptrrdbuf, bufsz))
+			return false;
+	}
+	
+	return true;
+}
+
+static
+void nanofury_device_off(struct mcp2210_device * const mcp)
+{
+	// Try to reset everything back to input
+	for (int i = 0; i < 9; ++i)
+		mcp2210_get_gpio_input(mcp, i);
+}
+
+static
+bool nanofury_checkport(struct mcp2210_device * const mcp)
+{
+	int i;
+	const char tmp = 0;
+	char tmprx;
+	
+	// default: set everything to input
+	for (i = 0; i < 9; ++i)
+		if (MGV_ERROR == mcp2210_get_gpio_input(mcp, i))
+			goto fail;
+	
+	// configure the pins that we need:
+	
+	// LED
+	if (!mcp2210_set_gpio_output(mcp, NANOFURY_GP_PIN_LED, MGV_HIGH))
+		goto fail;
+	
+	// PWR_EN
+	if (!mcp2210_set_gpio_output(mcp, NANOFURY_GP_PIN_PWR_EN, MGV_HIGH))
+		goto fail;
+	
+	// configure SPI
+	// This is the only place where speed, mode and other settings are configured!!!
+	if (!mcp2210_configure_spi(mcp, 200000, 0xffff, 0xffef, 0, 0, 0))
+		goto fail;
+	if (!mcp2210_set_spimode(mcp, 0))
+		goto fail;
+	
+	if (!mcp2210_spi_transfer(mcp, &tmp, &tmprx, 1))
+		goto fail;
+	
+	// after this command SCK_OVRRIDE should read the same as current SCK value (which for mode 0 should be 0)
+	
+	if (mcp2210_get_gpio_input(mcp, NANOFURY_GP_PIN_SCK_OVR) != MGV_LOW)
+		goto fail;
+	
+	// switch SCK to polarity (default SCK=1 in mode 2)
+	if (!mcp2210_set_spimode(mcp, 2))
+		goto fail;
+	if (!mcp2210_spi_transfer(mcp, &tmp, &tmprx, 1))
+		goto fail;
+	
+	// after this command SCK_OVRRIDE should read the same as current SCK value (which for mode 2 should be 1)
+	
+	if (mcp2210_get_gpio_input(mcp, NANOFURY_GP_PIN_SCK_OVR) != MGV_HIGH)
+		goto fail;
+	
+	// switch SCK to polarity (default SCK=0 in mode 0)
+	if (!mcp2210_set_spimode(mcp, 0))
+		goto fail;
+	if (!mcp2210_spi_transfer(mcp, &tmp, &tmprx, 1))
+		goto fail;
+	
+	if (mcp2210_get_gpio_input(mcp, NANOFURY_GP_PIN_SCK_OVR) != MGV_LOW)
+		goto fail;
+	
+	return true;
+
+fail:
+	nanofury_device_off(mcp);
+	return false;
+}
+
+static
+bool nanofury_foundlowl(struct lowlevel_device_info * const info)
+{
+	const char * const product = info->product;
+	const char * const serial = info->serial;
+	struct mcp2210_device *mcp;
+	
+	if (info->lowl != &lowl_mcp2210)
+	{
+		applog(LOG_WARNING, "%s: Matched \"%s\" serial \"%s\", but lowlevel driver is not mcp2210!",
+		       __func__, product, serial);
+		return false;
+	}
+	
+	mcp = mcp2210_open(info);
+	if (!mcp)
+	{
+		applog(LOG_WARNING, "%s: Matched \"%s\" serial \"%s\", but mcp2210 lowlevel driver failed to open it",
+		       __func__, product, serial);
+		return false;
+	}
+	if (!nanofury_checkport(mcp))
+	{
+		applog(LOG_WARNING, "%s: Matched \"%s\" serial \"%s\", but failed to detect nanofury",
+		       __func__, product, serial);
+		// TODO: mcp2210_close(mcp);
+		return false;
+	}
+	nanofury_device_off(mcp);
+	// TODO: mcp2210_close(mcp);
+	
+	// TODO: claim device
+	
+	struct cgpu_info *cgpu;
+	cgpu = malloc(sizeof(*cgpu));
+	*cgpu = (struct cgpu_info){
+		.drv = &nanofury_drv,
+		.device_data = info,
+		.threads = 1,
+		// TODO: .name
+		// TODO: .device_path
+		// TODO: .dev_manufacturer/.dev_product/.dev_serial
+		.deven = DEV_ENABLED,
+		// TODO: .cutofftemp
+	};
+
+	return add_cgpu(cgpu);
+}
+
+static bool nanofury_detect_one(const char *serial)
+{
+	return lowlevel_detect_serial(nanofury_foundlowl, serial);
+}
+
+static int nanofury_detect_auto()
+{
+	return lowlevel_detect(nanofury_foundlowl, NANOFURY_USB_PRODUCT);
+}
+
+static void nanofury_detect()
+{
+	serial_detect_auto(&nanofury_drv, nanofury_detect_one, nanofury_detect_auto);
+}
+
+static
+bool nanofury_init(struct thr_info * const thr)
+{
+	struct cgpu_info * const cgpu = thr->cgpu;
+	struct lowlevel_device_info * const info = cgpu->device_data;
+	struct spi_port *port;
+	struct bitfury_device *bitfury;
+	struct mcp2210_device *mcp;
+	
+	mcp = mcp2210_open(info);
+	lowlevel_devinfo_free(info);
+	if (!mcp)
+	{
+		applog(LOG_ERR, "%"PRIpreprv": Failed to open mcp2210 device", cgpu->proc_repr);
+		return false;
+	}
+	if (!nanofury_checkport(mcp))
+	{
+		applog(LOG_ERR, "%"PRIpreprv": checkport failed", cgpu->proc_repr);
+		// TODO: mcp2210_close(mcp);
+		return false;
+	}
+	
+	port = malloc(sizeof(*port));
+	bitfury = malloc(sizeof(*bitfury));
+	
+	if (!(port && bitfury))
+	{
+		applog(LOG_ERR, "%"PRIpreprv": Failed to allocate spi_port and bitfury_device structures", cgpu->proc_repr);
+		free(port);
+		free(bitfury);
+		// TODO: mcp2210_close(mcp);
+		return false;
+	}
+	
+	thr->cgpu_data = mcp;
+	*port = (struct spi_port){
+		.txrx = nanofury_spi_txrx,
+		.cgpu = cgpu,
+		.repr = cgpu->proc_repr,
+		.logprio = LOG_ERR,
+	};
+	*bitfury = (struct bitfury_device){
+		.spi = port,
+	};
+	cgpu->device_data = bitfury;
+	bitfury->osc6_bits = 50;
+	send_reinit(bitfury->spi, bitfury->slot, bitfury->fasync, bitfury->osc6_bits);
+	bitfury_init_chip(cgpu);
+	
+	timer_set_now(&thr->tv_poll);
+	cgpu->status = LIFE_INIT2;
+	return true;
+}
+
+static
+void nanofury_shutdown(struct thr_info * const thr)
+{
+	struct mcp2210_device * const mcp = thr->cgpu_data;
+	
+	nanofury_device_off(mcp);
+}
+
+struct device_drv nanofury_drv = {
+	.dname = "nanofury",
+	.name = "NFY",
+	.drv_detect = nanofury_detect,
+	
+	.thread_init = nanofury_init,
+	.thread_shutdown = nanofury_shutdown,
+	
+	.minerloop = minerloop_async,
+	.job_prepare = bitfury_job_prepare,
+	.job_start = bitfury_noop_job_start,
+	.poll = bitfury_do_io,
+	.job_process_results = bitfury_job_process_results,
+	
+	.get_api_extra_device_detail = bitfury_api_device_detail,
+	.get_api_extra_device_status = bitfury_api_device_status,
+	.set_device = bitfury_set_device,
+	
+#ifdef HAVE_CURSES
+	.proc_wlogprint_status = bitfury_wlogprint_status,
+	.proc_tui_wlogprint_choices = bitfury_tui_wlogprint_choices,
+	.proc_tui_handle_choice = bitfury_tui_handle_choice,
+#endif
+};

+ 16 - 4
driver-x6500.c

@@ -28,6 +28,7 @@
 #include "miner.h"
 #include "fpgautils.h"
 #include "ft232r.h"
+#include "lowlevel.h"
 
 #define X6500_USB_PRODUCT "X6500 FPGA Miner"
 #define X6500_BITSTREAM_FILENAME "fpgaminer_x6500-overclocker-0402.bit"
@@ -124,8 +125,18 @@ uint32_t x6500_get_register(struct jtag_port *jp, uint8_t addr)
 	return bits2int(buf, 32);
 }
 
-static bool x6500_foundusb(libusb_device *dev, const char *product, const char *serial)
+static bool x6500_foundlowl(struct lowlevel_device_info * const info)
 {
+	const char * const product = info->product;
+	const char * const serial = info->serial;
+	if (info->lowl != &lowl_ft232r)
+	{
+		applog(LOG_WARNING, "%s: Matched \"%s\" serial \"%s\", but lowlevel driver is not ft232r!",
+		       __func__, product, serial);
+		return false;
+	}
+	
+	libusb_device * const dev = info->lowl_data;
 	if (bfg_claim_libusb(&x6500_api, true, dev))
 		return false;
 	
@@ -139,7 +150,7 @@ static bool x6500_foundusb(libusb_device *dev, const char *product, const char *
 	x6500->procs = 2;
 	x6500->name = strdup(product);
 	x6500->cutofftemp = 85;
-	x6500->device_data = dev;
+	x6500->device_data = info;
 	cgpu_copy_libusb_strings(x6500, dev);
 
 	return add_cgpu(x6500);
@@ -147,12 +158,12 @@ static bool x6500_foundusb(libusb_device *dev, const char *product, const char *
 
 static bool x6500_detect_one(const char *serial)
 {
-	return ft232r_detect(X6500_USB_PRODUCT, serial, x6500_foundusb);
+	return lowlevel_detect_serial(x6500_foundlowl, serial);
 }
 
 static int x6500_detect_auto()
 {
-	return ft232r_detect(X6500_USB_PRODUCT, NULL, x6500_foundusb);
+	return lowlevel_detect(x6500_foundlowl, X6500_USB_PRODUCT);
 }
 
 static void x6500_detect()
@@ -168,6 +179,7 @@ static bool x6500_prepare(struct thr_info *thr)
 		return true;
 	
 	struct ft232r_device_handle *ftdi = ft232r_open(x6500->device_data);
+	lowlevel_devinfo_free(x6500->device_data);
 	x6500->device_ft232r = NULL;
 	if (!ftdi)
 		return false;

+ 36 - 61
ft232r.c

@@ -24,58 +24,42 @@
 #include "fpgautils.h"
 #include "ft232r.h"
 #include "logging.h"
+#include "lowlevel.h"
 #include "miner.h"
 
 #define FT232R_IDVENDOR   0x0403
 #define FT232R_IDPRODUCT  0x6001
 
-struct ft232r_device_info {
-	libusb_device *libusb_dev;
-	char *product;
-	char *serial;
-};
-
-static struct ft232r_device_info **ft232r_devinfo_list;
-
-void ft232r_scan_free()
+static
+void ft232r_devinfo_free(struct lowlevel_device_info * const info)
 {
-	if (!ft232r_devinfo_list)
-		return;
-
-	struct ft232r_device_info *info;
-
-	for (struct ft232r_device_info **infop = ft232r_devinfo_list; (info = *infop); ++infop) {
-		libusb_unref_device(info->libusb_dev);
-		free(info->product);
-		free(info->serial);
-		free(info);
-	}
-	free(ft232r_devinfo_list);
-	ft232r_devinfo_list = NULL;
+	libusb_device * const dev = info->lowl_data;
+	if (dev)
+		libusb_unref_device(dev);
 }
 
-void ft232r_scan()
+static
+struct lowlevel_device_info *ft232r_devinfo_scan()
 {
-	ssize_t count, n, i, found = 0;
+	struct lowlevel_device_info *devinfo_list = NULL;
+	ssize_t count, n, i;
 	libusb_device **list;
 	struct libusb_device_descriptor desc;
 	libusb_device_handle *handle;
-	struct ft232r_device_info *info;
+	struct lowlevel_device_info *info;
 	int err;
 	unsigned char buf[0x100];
 	int skipped = 0;
 
-	ft232r_scan_free();
-
+	if (unlikely(!have_libusb))
+		return NULL;
+	
 	count = libusb_get_device_list(NULL, &list);
 	if (unlikely(count < 0)) {
 		applog(LOG_ERR, "ft232r_scan: Error getting USB device list: %s", bfg_strerror(count, BST_LIBUSB));
-		ft232r_devinfo_list = calloc(1, sizeof(struct ft232r_device_info *));
-		return;
+		return NULL;
 	}
 
-	ft232r_devinfo_list = malloc(sizeof(struct ft232r_device_info *) * (count + 1));
-
 	for (i = 0; i < count; ++i) {
 		if (bfg_claim_libusb(NULL, false, list[i]))
 		{
@@ -106,7 +90,10 @@ void ft232r_scan()
 			continue;
 		}
 		buf[n] = '\0';
-		info = malloc(sizeof(struct ft232r_device_info));
+		info = malloc(sizeof(struct lowlevel_device_info));
+		*info = (struct lowlevel_device_info){
+			.lowl = &lowl_ft232r,
+		};
 		info->product = strdup((char*)buf);
 
 		n = libusb_get_string_descriptor_ascii(handle, desc.iSerialNumber, buf, sizeof(buf)-1);
@@ -117,42 +104,19 @@ void ft232r_scan()
 		}
 		buf[n] = '\0';
 		info->serial = strdup((char*)buf);
-		info->libusb_dev = libusb_ref_device(list[i]);
-		ft232r_devinfo_list[found++] = info;
+		info->lowl_data = libusb_ref_device(list[i]);
+		
+		LL_PREPEND(devinfo_list, info);
 
 		applog(LOG_DEBUG, "ft232r_scan: Found \"%s\" serial \"%s\"", info->product, info->serial);
 	}
 
-	ft232r_devinfo_list[found] = NULL;
 	libusb_free_device_list(list, 1);
 	
 	if (skipped)
 		applog(LOG_DEBUG, "%s: Skipping probe of %d claimed devices", __func__, skipped);
-}
-
-int ft232r_detect(const char *product_needle, const char *serial, foundusb_func_t cb)
-{
-	struct ft232r_device_info *info;
-	int found = 0;
-
-	for (struct ft232r_device_info **infop = ft232r_devinfo_list; (info = *infop); ++infop) {
-		if (serial) {
-			// If we are searching for a specific serial, pay no attention to the product id
-			if (strcmp(serial, info->serial))
-				continue;
-		}
-		else
-		if (!strstr(info->product, product_needle))
-			continue;
-		if (!info->libusb_dev)
-			continue;
-		if (!cb(info->libusb_dev, info->product, info->serial))
-			continue;
-		info->libusb_dev = NULL;
-		++found;
-	}
-
-	return found;
+	
+	return devinfo_list;
 }
 
 #define FTDI_REQTYPE  (LIBUSB_REQUEST_TYPE_VENDOR | LIBUSB_RECIPIENT_DEVICE)
@@ -181,8 +145,14 @@ struct ft232r_device_handle {
 	uint16_t obufsz;
 };
 
-struct ft232r_device_handle *ft232r_open(libusb_device *dev)
+struct ft232r_device_handle *ft232r_open(struct lowlevel_device_info *info)
 {
+	libusb_device * const dev = info->lowl_data;
+	info->lowl_data = NULL;
+	
+	if (!dev)
+		return NULL;
+	
 	// FIXME: Cleanup on errors
 	libusb_device_handle *devh;
 	struct ft232r_device_handle *ftdi;
@@ -408,6 +378,11 @@ bool ft232r_get_cbus_bits(struct ft232r_device_handle *dev, bool *out_sio0, bool
 	return true;
 }
 
+struct lowlevel_driver lowl_ft232r = {
+	.devinfo_scan = ft232r_devinfo_scan,
+	.devinfo_free = ft232r_devinfo_free,
+};
+
 #if 0
 int main() {
 	libusb_init(NULL);

+ 3 - 6
ft232r.h

@@ -15,6 +15,8 @@
 
 #include <libusb.h>
 
+#include "lowlevel.h"
+
 enum ft232r_reset_purge {
 	FTDI_PURGE_RX   = 1,
 	FTDI_PURGE_TX   = 2,
@@ -23,12 +25,7 @@ enum ft232r_reset_purge {
 
 struct ft232r_device_handle;
 
-typedef bool(*foundusb_func_t)(libusb_device *, const char *product, const char *serial);
-
-extern void ft232r_scan();
-extern void ft232r_scan_free();
-extern int ft232r_detect(const char *product_needle, const char *serial, foundusb_func_t);
-extern struct ft232r_device_handle *ft232r_open(libusb_device *);
+extern struct ft232r_device_handle *ft232r_open(struct lowlevel_device_info *);
 extern void ft232r_close(struct ft232r_device_handle *);
 extern bool ft232r_purge_buffers(struct ft232r_device_handle *, enum ft232r_reset_purge);
 extern bool ft232r_set_bitmode(struct ft232r_device_handle *, uint8_t mask, uint8_t mode);

+ 80 - 0
lowlevel.c

@@ -0,0 +1,80 @@
+/*
+ * Copyright 2012-2013 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 <stdlib.h>
+#include <string.h>
+
+#include <utlist.h>
+
+#include "lowlevel.h"
+
+static struct lowlevel_device_info *devinfo_list;
+
+void lowlevel_devinfo_free(struct lowlevel_device_info * const info)
+{
+	info->lowl->devinfo_free(info);
+	free(info->product);
+	free(info->serial);
+	free(info);
+}
+
+void lowlevel_scan_free()
+{
+	if (!devinfo_list)
+		return;
+	
+	struct lowlevel_device_info *info, *tmp;
+	
+	LL_FOREACH_SAFE(devinfo_list, info, tmp)
+	{
+		LL_DELETE(devinfo_list, info);
+		lowlevel_devinfo_free(info);
+	}
+}
+
+void lowlevel_scan()
+{
+	struct lowlevel_device_info *devinfo_mid_list;
+	
+	lowlevel_scan_free();
+	
+#ifdef USE_X6500
+	devinfo_mid_list = lowl_ft232r.devinfo_scan();
+	LL_CONCAT(devinfo_list, devinfo_mid_list);
+#endif
+	
+#ifdef USE_NANOFURY
+	devinfo_mid_list = lowl_mcp2210.devinfo_scan();
+	LL_CONCAT(devinfo_list, devinfo_mid_list);
+#endif
+}
+
+int _lowlevel_detect(lowl_found_devinfo_func_t cb, const char *serial, const char **product_needles)
+{
+	struct lowlevel_device_info *info, *tmp;
+	int found = 0, i;
+	
+	LL_FOREACH_SAFE(devinfo_list, info, tmp)
+	{
+		if (serial && strcmp(serial, info->serial))
+			continue;
+		for (i = 0; product_needles[i]; ++i)
+			if (!strstr(info->product, product_needles[i]))
+				goto next;
+		if (!cb(info))
+			continue;
+		LL_DELETE(devinfo_list, info);
+		++found;
+next: ;
+	}
+	
+	return found;
+}

+ 39 - 0
lowlevel.h

@@ -0,0 +1,39 @@
+#ifndef _BFG_LOWLEVEL_H
+#define _BFG_LOWLEVEL_H
+
+#include <stdbool.h>
+
+struct lowlevel_device_info;
+
+typedef bool (*lowl_found_devinfo_func_t)(struct lowlevel_device_info *);
+
+struct lowlevel_driver {
+	struct lowlevel_device_info *(*devinfo_scan)();
+	void (*devinfo_free)(struct lowlevel_device_info *);
+};
+
+struct lowlevel_device_info {
+	char *product;
+	char *serial;
+	
+	struct lowlevel_driver *lowl;
+	void *lowl_data;
+	
+	struct lowlevel_device_info *next;
+};
+
+extern void lowlevel_scan();
+extern int _lowlevel_detect(lowl_found_devinfo_func_t, const char *serial, const char **product_needles);
+#define lowlevel_detect(func, ...)  _lowlevel_detect(func, NULL, (const char *[]){__VA_ARGS__, NULL})
+#define lowlevel_detect_serial(func, serial)  _lowlevel_detect(func, serial, (const char *[]){NULL})
+extern void lowlevel_scan_free();
+extern void lowlevel_devinfo_free(struct lowlevel_device_info *);
+
+#ifdef USE_X6500
+extern struct lowlevel_driver lowl_ft232r;
+#endif
+#ifdef USE_NANOFURY
+extern struct lowlevel_driver lowl_mcp2210;
+#endif
+
+#endif

+ 1 - 0
make-release

@@ -56,6 +56,7 @@ dlls='
 	pdcurses.dll
 	libcurl-4.dll
 	libevent-2-0-5.dll
+	libhidapi-0.dll
 	pthreadGC2.dll
 	libjansson-4.dll
 	libusb-1.0.dll

+ 445 - 0
mcp2210.c

@@ -0,0 +1,445 @@
+/*
+ * Copyright 2012-2013 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"
+
+#ifndef WIN32
+#include <dlfcn.h>
+typedef void *dlh_t;
+#else
+#include <winsock2.h>
+#include <windows.h>
+#define dlopen(lib, flags) LoadLibrary(lib)
+#define dlsym(h, sym)  ((void*)GetProcAddress(h, sym))
+#define dlerror()  "unknown"
+#define dlclose(h)  FreeLibrary(h)
+typedef HMODULE dlh_t;
+#endif
+
+#include <stdbool.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+
+#include <hidapi.h>
+#include <utlist.h>
+
+#include "logging.h"
+#include "lowlevel.h"
+#include "miner.h"
+
+#include "mcp2210.h"
+
+#define MCP2210_IDVENDOR   0x04d8
+#define MCP2210_IDPRODUCT  0x00de
+
+#ifdef WIN32
+#define HID_API_EXPORT __declspec(dllexport)
+#else
+#define HID_API_EXPORT /* */
+#endif
+struct hid_device_info HID_API_EXPORT *(*dlsym_hid_enumerate)(unsigned short, unsigned short);
+void HID_API_EXPORT (*dlsym_hid_free_enumeration)(struct hid_device_info *);
+hid_device * HID_API_EXPORT (*dlsym_hid_open_path)(const char *);
+int HID_API_EXPORT (*dlsym_hid_read)(hid_device *, unsigned char *, size_t);
+int HID_API_EXPORT (*dlsym_hid_write)(hid_device *, const unsigned char *, size_t);
+
+#define LOAD_SYM(sym)  do { \
+	if (!(dlsym_ ## sym = dlsym(dlh, #sym))) {  \
+		applog(LOG_DEBUG, "%s: Failed to load %s in %s", __func__, #sym, dlname);  \
+		goto fail;  \
+	}  \
+} while(0)
+
+static
+bool hidapi_try_lib(const char * const dlname)
+{
+	struct hid_device_info *hid_enum;
+	dlh_t dlh;
+	
+	dlh = dlopen(dlname, RTLD_NOW);
+	if (!dlh)
+	{
+		applog(LOG_DEBUG, "%s: Couldn't load %s: %s", __func__, dlname, dlerror());
+		return false;
+	}
+	
+	LOAD_SYM(hid_enumerate);
+	LOAD_SYM(hid_free_enumeration);
+	
+	hid_enum = dlsym_hid_enumerate(0, 0);
+	if (!hid_enum)
+	{
+		applog(LOG_DEBUG, "%s: Loaded %s, but no devices enumerated; trying other libraries", __func__, dlname);
+		goto fail;
+	}
+	dlsym_hid_free_enumeration(hid_enum);
+	
+	LOAD_SYM(hid_open_path);
+	LOAD_SYM(hid_read);
+	LOAD_SYM(hid_write);
+	
+	applog(LOG_DEBUG, "%s: Successfully loaded %s", __func__, dlname);
+	
+	return true;
+
+fail:
+	dlclose(dlh);
+	return false;
+}
+
+#define hid_enumerate dlsym_hid_enumerate
+#define hid_free_enumeration dlsym_hid_free_enumeration
+#define hid_open_path dlsym_hid_open_path
+#define hid_read dlsym_hid_read
+#define hid_write dlsym_hid_write
+
+static
+bool hidapi_load_library()
+{
+	if (dlsym_hid_write)
+		return true;
+	
+	const char **p;
+	char dlname[23] = "libhidapi";
+	const char *dltry[] = {
+		"",
+		"-0",
+		"-hidraw",
+		"-libusb",
+		NULL
+	};
+	for (p = &dltry[0]; *p; ++p)
+	{
+		sprintf(&dlname[9], "%s.%s", *p,
+#ifdef WIN32
+		        "dll"
+#else
+		        "so"
+#endif
+		);
+		if (hidapi_try_lib(dlname))
+			return true;
+	}
+	
+	return false;
+}
+
+static
+void mcp2210_devinfo_free(struct lowlevel_device_info * const info)
+{
+	free(info->lowl_data);
+}
+
+static
+char *wcs2str_dup(wchar_t *ws)
+{
+	char tmp, *rv;
+	int clen;
+	
+	clen = snprintf(&tmp, 1, "%ls", ws);
+	++clen;
+	rv = malloc(clen);
+	snprintf(rv, clen, "%ls", ws);
+	return rv;
+}
+
+static
+struct lowlevel_device_info *mcp2210_devinfo_scan()
+{
+	if (!hidapi_load_library())
+	{
+		applog(LOG_DEBUG, "%s: Failed to load any hidapi library", __func__);
+		return NULL;
+	}
+	
+	struct hid_device_info *hid_enum, *hid_item;
+	struct lowlevel_device_info *info, *devinfo_list = NULL;
+	
+	hid_enum = hid_enumerate(MCP2210_IDVENDOR, MCP2210_IDPRODUCT);
+	if (!hid_enum)
+	{
+		applog(LOG_DEBUG, "%s: No MCP2210 devices found", __func__);
+		return NULL;
+	}
+	
+	LL_FOREACH(hid_enum, hid_item)
+	{
+		info = malloc(sizeof(struct lowlevel_device_info));
+		*info = (struct lowlevel_device_info){
+			.lowl = &lowl_mcp2210,
+			.lowl_data = strdup(hid_item->path),
+			.product = wcs2str_dup(hid_item->product_string),
+			.serial  = wcs2str_dup(hid_item->serial_number),
+		};
+		LL_PREPEND(devinfo_list, info);
+
+		applog(LOG_DEBUG, "%s: Found \"%s\" serial \"%s\"",
+		       __func__, info->product, info->serial);
+	}
+	
+	hid_free_enumeration(hid_enum);
+	
+	return devinfo_list;
+}
+
+struct mcp2210_device {
+	hid_device *hid;
+	
+	// http://ww1.microchip.com/downloads/en/DeviceDoc/22288A.pdf pg 34
+	uint8_t cfg_spi[0x11];
+	// http://ww1.microchip.com/downloads/en/DeviceDoc/22288A.pdf pg 40
+	uint8_t cfg_gpio[0xf];
+};
+
+static
+bool mcp2210_io(hid_device * const hid, uint8_t * const cmd, uint8_t * const buf)
+{
+	return likely(
+		64 == hid_write(hid, cmd, 64) &&
+		64 == hid_read(hid, buf, 64)
+	);
+}
+
+static
+bool mcp2210_get_configs(struct mcp2210_device * const h)
+{
+	hid_device * const hid = h->hid;
+	uint8_t cmd[0x40] = {0x41}, buf[0x40];
+	
+	if (!mcp2210_io(hid, cmd, buf))
+	{
+		applog(LOG_ERR, "%s: Failed to get current %s config", __func__, "SPI");
+		return false;
+	}
+	memcpy(h->cfg_spi, &buf[4], sizeof(h->cfg_spi));
+	
+	cmd[0] = 0x20;
+	if (!mcp2210_io(hid, cmd, buf))
+	{
+		applog(LOG_ERR, "%s: Failed to get current %s config", __func__, "GPIO");
+		return false;
+	}
+	memcpy(h->cfg_gpio, &buf[4], sizeof(h->cfg_gpio));
+	
+	return true;
+}
+
+struct mcp2210_device *mcp2210_open(struct lowlevel_device_info *info)
+{
+	struct mcp2210_device *h;
+	char * const path = info->lowl_data;
+	hid_device * const hid = hid_open_path(path);
+	
+	if (unlikely(!hid))
+		return NULL;
+	
+	h = malloc(sizeof(*h));
+	h->hid = hid;
+	
+	if (!mcp2210_get_configs(h))
+		goto fail;
+	
+	return h;
+
+fail:
+	free(h);
+	return NULL;
+}
+
+static
+bool mcp2210_set_cfg_spi(struct mcp2210_device * const h)
+{
+	hid_device * const hid = h->hid;
+	uint8_t cmd[0x40] = {0x40}, buf[0x40];
+	memcpy(&cmd[4], h->cfg_spi, sizeof(h->cfg_spi));
+	if (!mcp2210_io(hid, cmd, buf))
+	{
+		applog(LOG_ERR, "%s: Failed to set current %s config", __func__, "SPI");
+		return false;
+	}
+	
+	if (buf[1] != 0)
+	{
+		applog(LOG_ERR, "%s: Error setting current %s config (%d)", __func__, "SPI", buf[1]);
+		return false;
+	}
+	
+	return true;
+}
+
+bool mcp2210_configure_spi(struct mcp2210_device * const h, const uint32_t bitrate, const uint16_t idlechipsel, const uint16_t activechipsel, const uint16_t chipseltodatadelay, const uint16_t lastbytetocsdelay, const uint16_t midbytedelay)
+{
+	uint8_t * const cfg = h->cfg_spi;
+	
+	cfg[0] = (bitrate >> 0x00) & 0xff;
+	cfg[1] = (bitrate >> 0x08) & 0xff;
+	cfg[2] = (bitrate >> 0x10) & 0xff;
+	cfg[3] = (bitrate >> 0x18) & 0xff;
+	
+	cfg[4] = (  idlechipsel >> 0) & 0xff;
+	cfg[5] = (  idlechipsel >> 8) & 0xff;
+	
+	cfg[6] = (activechipsel >> 0) & 0xff;
+	cfg[7] = (activechipsel >> 8) & 0xff;
+	
+	cfg[8] = (chipseltodatadelay >> 0) & 0xff;
+	cfg[9] = (chipseltodatadelay >> 8) & 0xff;
+	
+	cfg[0xa] = (lastbytetocsdelay >> 0) & 0xff;
+	cfg[0xb] = (lastbytetocsdelay >> 8) & 0xff;
+	
+	cfg[0xc] = (midbytedelay >> 0) & 0xff;
+	cfg[0xd] = (midbytedelay >> 8) & 0xff;
+	
+	return mcp2210_set_cfg_spi(h);
+}
+
+bool mcp2210_set_spimode(struct mcp2210_device * const h, const uint8_t spimode)
+{
+	uint8_t * const cfg = h->cfg_spi;
+	cfg[0x10] = spimode;
+	return mcp2210_set_cfg_spi(h);
+}
+
+bool mcp2210_spi_transfer(struct mcp2210_device * const h, const void * const tx, void * const rx, uint8_t sz)
+{
+	hid_device * const hid = h->hid;
+	uint8_t * const cfg = h->cfg_spi;
+	uint8_t cmd[0x40] = {0x42}, buf[0x40];
+	uint8_t *p = rx;
+	
+	if (unlikely(sz > 60))
+	{
+		applog(LOG_ERR, "%s: SPI transfer too long (%d bytes)", __func__, sz);
+		return false;
+	}
+	
+	cfg[0xe] = sz;
+	cfg[0xf] = 0;
+	if (!mcp2210_set_cfg_spi(h))
+		return false;
+	
+	cmd[1] = sz;
+	memcpy(&cmd[4], tx, sz);
+	if (unlikely(!mcp2210_io(hid, cmd, buf)))
+	{
+		applog(LOG_ERR, "%s: Failed to issue SPI transfer", __func__);
+		return false;
+	}
+	
+	while (true)
+	{
+		switch (buf[1])
+		{
+			case 0:     // accepted
+				cmd[1] = 0;
+				break;
+			case 0xf8:  // transfer in progress
+				applog(LOG_DEBUG, "%s: SPI transfer rejected temporarily (%d bytes remaining)", __func__, sz);
+				cgsleep_ms(20);
+				goto retry;
+			default:
+				applog(LOG_ERR, "%s: SPI transfer error (%d) (%d bytes remaining)", __func__, buf[1], sz);
+				return false;
+		}
+		if (buf[2] >= sz)
+		{
+			if (buf[2] > sz)
+				applog(LOG_WARNING, "%s: Received %d extra bytes in SPI transfer", __func__, sz - buf[2]);
+			memcpy(p, &buf[4], sz);
+			return true;
+		}
+		memcpy(p, &buf[4], buf[2]);
+		p += buf[2];
+		sz -= buf[2];
+retry:
+		if (unlikely(!mcp2210_io(hid, cmd, buf)))
+		{
+			applog(LOG_ERR, "%s: Failed to continue SPI transfer (%d bytes remaining)", __func__, sz);
+			return false;
+		}
+	}
+}
+
+static
+bool mcp2210_set_cfg_gpio(struct mcp2210_device * const h)
+{
+	hid_device * const hid = h->hid;
+	uint8_t cmd[0x40] = {0x21}, buf[0x40];
+	
+	// NOTE: NVRAM chip params access control is not set here
+	memcpy(&cmd[4], h->cfg_gpio, 0xe);
+	if (!mcp2210_io(hid, cmd, buf))
+	{
+		applog(LOG_ERR, "%s: Failed to set current %s config", __func__, "GPIO");
+		return false;
+	}
+	
+	if (buf[1] != 0)
+	{
+		applog(LOG_ERR, "%s: Error setting current %s config (%d)", __func__, "GPIO", buf[1]);
+		return false;
+	}
+	
+	return true;
+}
+
+bool mcp2210_set_gpio_output(struct mcp2210_device * const h, const int pin, const enum mcp2210_gpio_value d)
+{
+	const int bit = 1 << (pin % 8);
+	const int byte = (pin / 8);
+	
+	// Set pin to GPIO mode
+	h->cfg_gpio[pin] = 0;
+	
+	// Set GPIO to output mode
+	h->cfg_gpio[byte + 0xb] &= ~bit;
+	
+	// Set value for GPIO output
+	if (d == MGV_HIGH)
+		h->cfg_gpio[byte + 9] |= bit;
+	else
+		h->cfg_gpio[byte + 9] &= ~bit;
+	
+	return mcp2210_set_cfg_gpio(h);
+}
+
+enum mcp2210_gpio_value mcp2210_get_gpio_input(struct mcp2210_device * const h, const int pin)
+{
+	hid_device * const hid = h->hid;
+	uint8_t cmd[0x40] = {0x31}, buf[0x40];
+	const int bit = 1 << (pin % 8);
+	const int byte = (pin / 8);
+	
+	// Set pin to GPIO mode
+	h->cfg_gpio[pin] = 0;
+	
+	// Set GPIO to input mode
+	h->cfg_gpio[byte + 0xb] |= bit;
+	
+	if (!mcp2210_set_cfg_gpio(h))
+		return MGV_ERROR;
+	
+	if (!mcp2210_io(hid, cmd, buf))
+	{
+		applog(LOG_ERR, "%s: Failed to get current GPIO input values", __func__);
+		return MGV_ERROR;
+	}
+	
+	if (buf[byte + 4] & bit)
+		return MGV_HIGH;
+	else
+		return MGV_LOW;
+}
+
+struct lowlevel_driver lowl_mcp2210 = {
+	.devinfo_scan = mcp2210_devinfo_scan,
+	.devinfo_free = mcp2210_devinfo_free,
+};

+ 28 - 0
mcp2210.h

@@ -0,0 +1,28 @@
+#ifndef BFG_MCP2210_H
+#define BFG_MCP2210_H
+
+#include <stdbool.h>
+
+enum mcp2210_gpio_direction {
+	MGD_OUTPUT,
+	MGD_INPUT,
+};
+
+enum mcp2210_gpio_value {
+	MGV_LOW,
+	MGV_HIGH,
+	MGV_ERROR,
+};
+
+struct mcp2210_device;
+
+extern struct mcp2210_device *mcp2210_open(struct lowlevel_device_info *);
+
+extern bool mcp2210_configure_spi(struct mcp2210_device *, uint32_t bitrate, uint16_t idlechipsel, uint16_t activechipsel, uint16_t chipseltodatadelay, uint16_t lastbytetocsdelay, uint16_t midbytedelay);
+extern bool mcp2210_set_spimode(struct mcp2210_device *, uint8_t spimode);
+extern bool mcp2210_spi_transfer(struct mcp2210_device *, const void *tx, void *rx, uint8_t sz);
+
+extern bool mcp2210_set_gpio_output(struct mcp2210_device *, int pin, enum mcp2210_gpio_value);
+extern enum mcp2210_gpio_value mcp2210_get_gpio_input(struct mcp2210_device *, int pin);
+
+#endif

+ 180 - 13
miner.c

@@ -77,8 +77,8 @@
 #include "driver-avalon.h"
 #endif
 
-#ifdef USE_X6500
-#include "ft232r.h"
+#ifdef HAVE_BFG_LOWLEVEL
+#include "lowlevel.h"
 #endif
 
 #if defined(unix) || defined(__APPLE__)
@@ -91,7 +91,7 @@
 #include "scrypt.h"
 #endif
 
-#if defined(USE_AVALON) || defined(USE_BITFORCE) || defined(USE_ICARUS) || defined(USE_MODMINER) || defined(USE_X6500) || defined(USE_ZTEX)
+#if defined(USE_AVALON) || defined(USE_BITFORCE) || defined(USE_ICARUS) || defined(USE_MODMINER) || defined(USE_NANOFURY) || defined(USE_X6500) || defined(USE_ZTEX)
 #	define USE_FPGA
 #endif
 
@@ -174,6 +174,7 @@ int stratumsrv_port = -1;
 #endif
 
 struct string_elist *scan_devices;
+static struct string_elist *opt_set_device_list;
 bool opt_force_dev_init;
 static bool devices_enabled[MAX_DEVICES];
 static int opt_devs_enabled;
@@ -496,6 +497,83 @@ static void applog_and_exit(const char *fmt, ...)
 	exit(1);
 }
 
+static
+bool cgpu_match(const char * const pattern, const struct cgpu_info * const cgpu)
+{
+	// all - matches anything
+	// d0 - matches all processors of device 0
+	// d0a - matches first processor of device 0
+	// 0 - matches processor 0
+	// ___ - matches all processors on all devices using driver/name ___
+	// ___0 - matches all processors of 0th device using driver/name ___
+	// ___0a - matches first processor of 0th device using driver/name ___
+	if (!strcasecmp(pattern, "all"))
+		return true;
+	
+	const char *p = pattern, *p2;
+	size_t L;
+	int n, i, c = -1;
+	struct cgpu_info *device;
+	
+	while (p[0] && !isdigit(p[0]))
+		++p;
+	
+	L = p - pattern;
+	while (L && isspace(pattern[L-1]))
+		--L;
+	n = strtol(p, (void*)&p2, 0);
+	if (L == 0)
+	{
+		if (!p[0])
+			return true;
+		if (p2 && p2[0])
+			goto invsyntax;
+		if (n >= total_devices)
+			return false;
+		return (cgpu == devices[n]);
+	}
+	
+	if (L > 1 || tolower(pattern[0]) != 'd' || !p[0])
+	{
+		const struct device_drv * const drv = cgpu->drv;
+		if ((L == 3 && !strncasecmp(pattern, drv->name, 3)) ||
+			(L == strlen(drv->dname) && !strncasecmp(pattern, drv->dname, L)))
+			{}  // Matched name or dname
+		else
+			return false;
+		if (p[0] && n != cgpu->device_id)
+			return false;
+		if (p2[0] && strcasecmp(p2, &cgpu->proc_repr[5]))
+			return false;
+		return true;
+	}
+	
+	// d#
+	
+	c = -1;
+	for (i = 0; ; ++i)
+	{
+		if (i == total_devices)
+			return false;
+		if (devices[i]->device != devices[i])
+			continue;
+		if (++c == n)
+			break;
+	}
+	for (device = devices[i]; device; device = device->next_proc)
+	{
+		if (p2 && p2[0] && strcasecmp(p2, &cgpu->proc_repr[5]))
+			continue;
+		if (device == cgpu)
+			return true;
+	}
+	return false;
+
+invsyntax:
+	applog(LOG_WARNING, "%s: Invalid syntax: %s", __func__, pattern);
+	return false;
+}
+
 static pthread_mutex_t sharelog_lock;
 static FILE *sharelog_file = NULL;
 
@@ -908,6 +986,13 @@ static char *add_serial(const char *arg)
 	return NULL;
 }
 
+static
+char *opt_string_elist_add(const char *arg, struct string_elist **elist)
+{
+	string_elist_add(arg, elist);
+	return NULL;
+}
+
 bool get_intrange(const char *arg, int *val1, int *val2)
 {
 	// NOTE: This could be done with sscanf, but its %n is broken in strange ways on Windows
@@ -1893,12 +1978,15 @@ static struct opt_table opt_config_table[] = {
 	OPT_WITHOUT_ARG("--scrypt",
 			opt_set_bool, &opt_scrypt,
 			"Use the scrypt algorithm for mining (non-bitcoin)"),
-#ifdef HAVE_OPENCL
+#endif
+	OPT_WITH_ARG("--set-device",
+			opt_string_elist_add, NULL, &opt_set_device_list,
+			"Set default parameters on devices; eg, NFY:osc6_bits=50"),
+#if defined(USE_SCRYPT) && defined(HAVE_OPENCL)
 	OPT_WITH_ARG("--shaders",
 		     set_shaders, NULL, NULL,
 		     "GPU shaders per card for tuning scrypt, comma separated"),
 #endif
-#endif
 #ifdef HAVE_PWD_H
         OPT_WITH_ARG("--setuid",
                      opt_set_charp, NULL, &opt_setuid,
@@ -9211,6 +9299,73 @@ void proc_enable(struct cgpu_info *cgpu)
 
 #define device_recovered(cgpu)  proc_enable(cgpu)
 
+void cgpu_set_defaults(struct cgpu_info * const cgpu)
+{
+	const struct device_drv * const drv = cgpu->drv;
+	struct string_elist *setstr_elist;
+	const char *p, *p2;
+	char replybuf[0x2000];
+	size_t L;
+	DL_FOREACH(opt_set_device_list, setstr_elist)
+	{
+		const char * const setstr = setstr_elist->string;
+		p = strchr(setstr, ':');
+		if (!p)
+			continue;
+		{
+			L = p - setstr;
+			char pattern[L + 1];
+			if (L)
+				memcpy(pattern, setstr, L);
+			pattern[L] = '\0';
+			if (!cgpu_match(pattern, cgpu))
+				continue;
+		}
+		
+		applog(LOG_DEBUG, "%"PRIpreprv": %s: Matched with set default: %s",
+		       cgpu->proc_repr, __func__, setstr);
+		
+		if (!drv->set_device)
+		{
+			applog(LOG_WARNING, "%"PRIpreprv": set_device is not implemented (trying to apply rule: %s)",
+			       cgpu->proc_repr, setstr);
+			continue;
+		}
+		
+		++p;
+		p2 = strchr(p, '=');
+		if (!p2)
+		{
+			L = strlen(p);
+			p2 = "";
+		}
+		else
+		{
+			L = p2 - p;
+			++p2;
+		}
+		char opt[L + 1];
+		if (L)
+			memcpy(opt, p, L);
+		opt[L] = '\0';
+		
+		L = strlen(p2);
+		char setval[L + 1];
+		if (L)
+			memcpy(setval, p2, L);
+		setval[L] = '\0';
+		
+		p = drv->set_device(cgpu, opt, setval, replybuf);
+		if (p)
+			applog(LOG_WARNING, "%"PRIpreprv": Applying rule %s: %s",
+			       cgpu->proc_repr, setstr, p);
+		else
+			applog(LOG_DEBUG, "%"PRIpreprv": Applied rule %s",
+			       cgpu->proc_repr, setstr);
+	}
+	cgpu->already_set_defaults = true;
+}
+
 /* Makes sure the hashmeter keeps going even if mining threads stall, updates
  * the screen at regular intervals, and restarts threads if they appear to have
  * died. */
@@ -9319,8 +9474,13 @@ void bfg_watchdog(struct cgpu_info * const cgpu, struct timeval * const tvp_now)
 			char *dev_str = cgpu->proc_repr;
 			int gpu;
 
-			if (cgpu->drv->get_stats && likely(drv_ready(cgpu)))
-			  cgpu->drv->get_stats(cgpu);
+			if (likely(drv_ready(cgpu)))
+			{
+				if (unlikely(!cgpu->already_set_defaults))
+					cgpu_set_defaults(cgpu);
+				if (cgpu->drv->get_stats)
+					cgpu->drv->get_stats(cgpu);
+			}
 
 			gpu = cgpu->device_id;
 			denable = &cgpu->deven;
@@ -9905,6 +10065,10 @@ extern struct device_drv littlefury_drv;
 extern struct device_drv modminer_drv;
 #endif
 
+#ifdef USE_NANOFURY
+extern struct device_drv nanofury_drv;
+#endif
+
 #ifdef USE_X6500
 extern struct device_drv x6500_api;
 #endif
@@ -9988,9 +10152,8 @@ extern struct sigaction pcwm_orig_term_handler;
 static
 void drv_detect_all()
 {
-#ifdef USE_X6500
-	if (likely(have_libusb))
-		ft232r_scan();
+#ifdef HAVE_BFG_LOWLEVEL
+	lowlevel_scan();
 #endif
 
 #ifdef USE_ICARUS
@@ -10022,6 +10185,11 @@ void drv_detect_all()
 		modminer_drv.drv_detect();
 #endif
 
+#ifdef USE_NANOFURY
+	if (!opt_scrypt)
+		nanofury_drv.drv_detect();
+#endif
+
 #ifdef USE_X6500
 	if (likely(have_libusb) && !opt_scrypt)
 		x6500_api.drv_detect();
@@ -10061,9 +10229,8 @@ void drv_detect_all()
 	cpu_drv.drv_detect();
 #endif
 
-#ifdef USE_X6500
-	if (likely(have_libusb))
-		ft232r_scan_free();
+#ifdef HAVE_BFG_LOWLEVEL
+	lowlevel_scan_free();
 #endif
 
 #ifdef HAVE_OPENCL

+ 5 - 0
miner.h

@@ -485,6 +485,7 @@ struct cgpu_info {
 	pthread_cond_t	device_cond;
 
 	enum dev_enable deven;
+	bool already_set_defaults;
 	int accepted;
 	int rejected;
 	int stale;
@@ -910,6 +911,7 @@ extern bool opt_fail_only;
 extern bool opt_autofan;
 extern bool opt_autoengine;
 extern bool use_curses;
+extern bool have_libusb;
 extern int httpsrv_port;
 extern int stratumsrv_port;
 extern char *opt_api_allow;
@@ -1004,6 +1006,8 @@ extern void mt_enable(struct thr_info *thr);
 extern void proc_enable(struct cgpu_info *);
 extern void reinit_device(struct cgpu_info *cgpu);
 
+extern void cgpu_set_defaults(struct cgpu_info *);
+
 #ifdef HAVE_ADL
 extern bool gpu_stats(int gpu, float *temp, int *engineclock, int *memclock, float *vddc, int *activity, int *fanspeed, int *fanpercent, int *powertune);
 extern int set_fanspeed(int gpu, int iFanSpeed);
@@ -1388,6 +1392,7 @@ extern void _wlog(const char *str);
 extern void _wlogprint(const char *str);
 extern int curses_int(const char *query);
 extern char *curses_input(const char *query);
+extern bool drv_ready(struct cgpu_info *);
 extern double stats_elapsed(struct cgminer_stats *);
 #define cgpu_runtime(cgpu)  stats_elapsed(&((cgpu)->cgminer_stats))
 extern double cgpu_utility(struct cgpu_info *);