Browse Source

Merge branch 'bfl'

Kano 13 years ago
parent
commit
e3db37a64e
9 changed files with 511 additions and 368 deletions
  1. 14 11
      FPGA-README
  2. 5 1
      Makefile.am
  3. 10 8
      README
  4. 2 2
      api.c
  5. 4 3
      configure.ac
  6. 297 319
      driver-bitforce.c
  7. 7 3
      miner.h
  8. 122 16
      usbutils.c
  9. 50 5
      usbutils.h

+ 14 - 11
FPGA-README

@@ -2,15 +2,8 @@
 This README contains extended details about FPGA mining with cgminer
 
 
-ModMinerQuad (MMQ)
-------------------
-
-The mining bitstream does not survive a power cycle, so cgminer will upload
-it, if it needs to, before it starts mining (approx 7min 40sec)
-
-The red LED also flashes while it is uploading the bitstream
-
--
+For ModMinerQuad (MMQ) and BitForce (BFL)
+-----------------------------------------
 
 When mining on windows, the driver being used will determine if mining will work.
 
@@ -39,7 +32,17 @@ problems:
 
  --usb-dump 0
 
-It will only help if you have a working MMQ device attached to the computer
+It will only help if you have a working MMQ or BFL device attached to the
+computer
+
+
+ModMinerQuad (MMQ)
+------------------
+
+The mining bitstream does not survive a power cycle, so cgminer will upload
+it, if it needs to, before it starts mining (approx 7min 40sec)
+
+The red LED also flashes while it is uploading the bitstream
 
 -
 
@@ -130,7 +133,7 @@ modem-manager software
 TODO: check that all MMQ's have the same product ID
 
 
-Bitforce (BFL)
+BitForce (BFL)
 --------------
 
 --bfl-range         Use nonce range on bitforce devices if supported

+ 5 - 1
Makefile.am

@@ -82,6 +82,10 @@ if NEED_FPGAUTILS
 cgminer_SOURCES += fpgautils.c fpgautils.h
 endif
 
+if NEED_USBUTILS_C
+cgminer_SOURCES += usbutils.c
+endif
+
 if HAS_BITFORCE
 cgminer_SOURCES += driver-bitforce.c
 endif
@@ -91,7 +95,7 @@ cgminer_SOURCES += driver-icarus.c
 endif
 
 if HAS_MODMINER
-cgminer_SOURCES += driver-modminer.c usbutils.c
+cgminer_SOURCES += driver-modminer.c
 bitstreamsdir = $(bindir)/bitstreams
 dist_bitstreams_DATA = bitstreams/*
 endif

+ 10 - 8
README

@@ -220,15 +220,16 @@ SCRYPT only options:
 See SCRYPT-README for more information regarding litecoin mining.
 
 
-FPGA mining boards(BitForce, Icarus, ModMiner, Ztex) only options:
+FPGA mining boards (BitForce, Icarus, ModMiner, Ztex) only options:
 
-cgminer will automatically find your ModMiner or Ztex FPGAs
+cgminer will automatically find your ModMiner, BitForce or Ztex FPGAs
+independent of the --scan-serial options specified below
 
---scan-serial|-S <arg> Serial port to probe for FPGA mining device
+--scan-serial|-S <arg> Serial port to probe for Icarus mining device
 
-This option is only for BitForce and/or Icarus FPGAs
+This option is only for Icarus bitstream FPGAs
 
-By default, cgminer will scan for autodetected FPGAs unless at least one
+By default, cgminer will scan for autodetected Icarus unless at least one
 -S is specified for that driver. If you specify -S and still want cgminer
 to scan, you must also use "-S auto". If you want to prevent cgminer from
 scanning without specifying a device, you can use "-S noauto". Note that
@@ -237,13 +238,14 @@ device depending on the version of udev being used.
 
 On linux <arg> is usually of the format /dev/ttyUSBn
 On windows <arg> is usually of the format \\.\COMn
-(where n = the correct device number for the FPGA device)
+(where n = the correct device number for the Icarus device)
 
 The official supplied binaries are compiled with support for all FPGAs.
 To force the code to only attempt detection with a specific driver,
 prepend the argument with the driver name followed by a colon.
-For example, "icarus:/dev/ttyUSB0" or "bitforce:\\.\COM5"
-or using the short name: "ica:/dev/ttyUSB0" or "bfl:\\.\COM5"
+For example, "icarus:/dev/ttyUSB0" or using the short name: "ica:/dev/ttyUSB0"
+This option not longer matters since Icarus is the only serial-USB
+device that uses it
 
 For other FPGA details see the FPGA-README
 

+ 2 - 2
api.c

@@ -3120,7 +3120,7 @@ static void usbstats(struct io_data *io_data, __maybe_unused SOCKETTYPE c, __may
 {
 	struct api_data *root = NULL;
 
-#ifdef USE_MODMINER
+#if defined(USE_MODMINER) || defined(USE_BITFORCE)
 	char buf[TMPBUFSIZ];
 	bool io_open = false;
 	int count = 0;
@@ -3133,7 +3133,7 @@ static void usbstats(struct io_data *io_data, __maybe_unused SOCKETTYPE c, __may
 		return;
 	}
 
-#ifdef USE_MODMINER
+#if defined(USE_MODMINER) || defined(USE_BITFORCE)
 
 	message(io_data, MSG_USBSTA, 0, NULL, isjson);
 

+ 4 - 3
configure.ac

@@ -273,6 +273,7 @@ fi
 
 
 AM_CONDITIONAL([NEED_FPGAUTILS], [test x$icarus$bitforce$modminer$ztex != xnononono])
+AM_CONDITIONAL([NEED_USBUTILS_C], [test x$bitforce$modminer != xnono])
 AM_CONDITIONAL([HAVE_CURSES], [test x$curses = xyes])
 AM_CONDITIONAL([WANT_JANSSON], [test x$request_jansson = xtrue])
 AM_CONDITIONAL([HAVE_WINDOWS], [test x$have_win32 = xtrue])
@@ -321,7 +322,7 @@ fi
 
 AM_CONDITIONAL([HAS_YASM], [test x$has_yasm = xtrue])
 
-if test "x$bitforce" != xno; then
+if test "x$icarus" != xno; then
 	AC_ARG_WITH([libudev], [AC_HELP_STRING([--without-libudev], [Autodetect FPGAs using libudev (default enabled)])],
 		[libudev=$withval],
 		[libudev=auto]
@@ -343,7 +344,7 @@ AM_CONDITIONAL([HAVE_LIBUDEV], [test x$libudev != xno])
 
 PKG_PROG_PKG_CONFIG()
 
-if test "x$ztex$modminer" != xnono; then
+if test "x$ztex$modminer$bitforce" != xnonono; then
   case $target in
     *-*-freebsd*)
       LIBUSB_LIBS="-lusb"
@@ -508,7 +509,7 @@ else
 	echo "  Ztex.FPGAs...........: Disabled"
 fi
 
-if test "x$bitforce" != xno; then
+if test "x$icarus" != xno; then
 	echo "  libudev.detection....: $libudev"
 fi
 

+ 297 - 319
driver-bitforce.c

@@ -1,4 +1,5 @@
 /*
+ * Copyright 2012 Andrew Smith
  * Copyright 2012 Luke Dashjr
  * Copyright 2012 Con Kolivas
  *
@@ -19,34 +20,25 @@
 #include "config.h"
 
 #ifdef WIN32
-
 #include <windows.h>
-
-#define dlsym (void*)GetProcAddress
-#define dlclose FreeLibrary
-
-typedef unsigned long FT_STATUS;
-typedef PVOID FT_HANDLE;
-__stdcall FT_STATUS (*FT_ListDevices)(PVOID pArg1, PVOID pArg2, DWORD Flags);
-__stdcall FT_STATUS (*FT_Open)(int idx, FT_HANDLE*);
-__stdcall FT_STATUS (*FT_GetComPortNumber)(FT_HANDLE, LPLONG lplComPortNumber);
-__stdcall FT_STATUS (*FT_Close)(FT_HANDLE);
-const uint32_t FT_OPEN_BY_DESCRIPTION =       2;
-const uint32_t FT_LIST_ALL         = 0x20000000;
-const uint32_t FT_LIST_NUMBER_ONLY = 0x80000000;
-enum {
-	FT_OK,
-};
-
-// Code must deal with a timeout. Make it 1 second on windows, 0.1 on linux.
-#define BFopen(devpath)  serial_open(devpath, 0, 10, true)
-#else /* WIN32 */
-#define BFopen(devpath)  serial_open(devpath, 0, 1, true)
 #endif /* WIN32 */
 
 #include "compat.h"
 #include "miner.h"
-#include "fpgautils.h"
+#include "usbutils.h"
+
+#define BITFORCE_IDENTIFY "ZGX"
+#define BITFORCE_IDENTIFY_LEN (sizeof(BITFORCE_IDENTIFY)-1)
+#define BITFORCE_FLASH "ZMX"
+#define BITFORCE_FLASH_LEN (sizeof(BITFORCE_FLASH)-1)
+#define BITFORCE_TEMPERATURE "ZLX"
+#define BITFORCE_TEMPERATURE_LEN (sizeof(BITFORCE_TEMPERATURE)-1)
+#define BITFORCE_SENDRANGE "ZPX"
+#define BITFORCE_SENDRANGE_LEN (sizeof(BITFORCE_SENDRANGE)-1)
+#define BITFORCE_SENDWORK "ZDX"
+#define BITFORCE_SENDWORK_LEN (sizeof(BITFORCE_SENDWORK)-1)
+#define BITFORCE_WORKSTATUS "ZFX"
+#define BITFORCE_WORKSTATUS_LEN (sizeof(BITFORCE_WORKSTATUS)-1)
 
 #define BITFORCE_SLEEP_MS 500
 #define BITFORCE_TIMEOUT_S 7
@@ -62,62 +54,159 @@ enum {
 #define KNAME_WORK  "full work"
 #define KNAME_RANGE "nonce range"
 
+#define BITFORCE_BUFSIZ (0x200)
+
+// If initialisation fails the first time,
+// sleep this amount (ms) and try again
+#define REINIT_TIME_MS 1000
+// But try this many times
+#define REINIT_COUNT 6
+
+static const char *blank = "";
+
 struct device_api bitforce_api;
 
-static void BFgets(char *buf, size_t bufLen, int fd)
+static void bitforce_initialise(struct cgpu_info *bitforce, bool lock)
 {
-	do {
-		buf[0] = '\0';
-		--bufLen;
-	} while (likely(bufLen && read(fd, buf, 1) == 1 && (buf++)[0] != '\n'));
+	int err;
 
-	buf[0] = '\0';
-}
+	if (lock)
+		mutex_lock(&bitforce->device_mutex);
 
-static ssize_t BFwrite(int fd, const void *buf, ssize_t bufLen)
-{
-	if ((bufLen) != write(fd, buf, bufLen))
-		return 0;
-	else
-		return bufLen;
+	// Reset
+	err = usb_transfer(bitforce, FTDI_TYPE_OUT, FTDI_REQUEST_RESET,
+				FTDI_VALUE_RESET, bitforce->usbdev->found->interface, C_RESET);
+	if (opt_debug)
+		applog(LOG_DEBUG, "%s%i: reset got err %d",
+			bitforce->api->name, bitforce->device_id, err);
+
+	// Set data control
+	err = usb_transfer(bitforce, FTDI_TYPE_OUT, FTDI_REQUEST_DATA,
+				FTDI_VALUE_DATA, bitforce->usbdev->found->interface, C_SETDATA);
+	if (opt_debug)
+		applog(LOG_DEBUG, "%s%i: setdata got err %d",
+			bitforce->api->name, bitforce->device_id, err);
+
+	// Set the baud
+	err = usb_transfer(bitforce, FTDI_TYPE_OUT, FTDI_REQUEST_BAUD, FTDI_VALUE_BAUD,
+				(FTDI_INDEX_BAUD & 0xff00) | bitforce->usbdev->found->interface,
+				C_SETBAUD);
+	if (opt_debug)
+		applog(LOG_DEBUG, "%s%i: setbaud got err %d",
+			bitforce->api->name, bitforce->device_id, err);
+
+	// Set Flow Control
+	err = usb_transfer(bitforce, FTDI_TYPE_OUT, FTDI_REQUEST_FLOW,
+				FTDI_VALUE_FLOW, bitforce->usbdev->found->interface, C_SETFLOW);
+	if (opt_debug)
+		applog(LOG_DEBUG, "%s%i: setflowctrl got err %d",
+			bitforce->api->name, bitforce->device_id, err);
+
+	// Set Modem Control
+	err = usb_transfer(bitforce, FTDI_TYPE_OUT, FTDI_REQUEST_MODEM,
+				FTDI_VALUE_MODEM, bitforce->usbdev->found->interface, C_SETMODEM);
+	if (opt_debug)
+		applog(LOG_DEBUG, "%s%i: setmodemctrl got err %d",
+			bitforce->api->name, bitforce->device_id, err);
+
+	// Clear any sent data
+	err = usb_transfer(bitforce, FTDI_TYPE_OUT, FTDI_REQUEST_RESET,
+				FTDI_VALUE_PURGE_TX, bitforce->usbdev->found->interface, C_PURGETX);
+	if (opt_debug)
+		applog(LOG_DEBUG, "%s%i: purgetx got err %d",
+			bitforce->api->name, bitforce->device_id, err);
+
+	// Clear any received data
+	err = usb_transfer(bitforce, FTDI_TYPE_OUT, FTDI_REQUEST_RESET,
+				FTDI_VALUE_PURGE_RX, bitforce->usbdev->found->interface, C_PURGERX);
+	if (opt_debug)
+		applog(LOG_DEBUG, "%s%i: purgerx got err %d",
+			bitforce->api->name, bitforce->device_id, err);
+
+	if (lock)
+		mutex_unlock(&bitforce->device_mutex);
 }
 
-#define BFclose(fd) close(fd)
-
-static bool bitforce_detect_one(const char *devpath)
+static bool bitforce_detect_one(struct libusb_device *dev, struct usb_find_devices *found)
 {
-	int fdDev = BFopen(devpath);
-	struct cgpu_info *bitforce;
-	char pdevbuf[0x100];
+	char buf[BITFORCE_BUFSIZ+1];
+	char devpath[20];
+	int err, amount;
 	char *s;
 
-	applog(LOG_DEBUG, "BFL: Attempting to open %s", devpath);
+	struct cgpu_info *bitforce = NULL;
+	bitforce = calloc(1, sizeof(*bitforce));
+	bitforce->api = &bitforce_api;
+	bitforce->deven = DEV_ENABLED;
+	bitforce->threads = 1;
 
-	if (unlikely(fdDev == -1)) {
-		applog(LOG_ERR, "BFL: Failed to open %s", devpath);
-		return false;
+	if (!usb_init(bitforce, dev, found)) {
+		applog(LOG_ERR, "%s detect (%d:%d) failed to initialise (incorrect device?)",
+			bitforce->api->dname,
+			(int)libusb_get_bus_number(dev),
+			(int)libusb_get_device_address(dev));
+		goto shin;
 	}
 
-	BFwrite(fdDev, "ZGX", 3);
-	pdevbuf[0] = '\0';
-	BFgets(pdevbuf, sizeof(pdevbuf), fdDev);
-	if (unlikely(!pdevbuf[0])) {
-		applog(LOG_ERR, "BFL: Error reading/timeout (ZGX)");
-		return 0;
+	sprintf(devpath, "%d:%d",
+			(int)(bitforce->usbdev->bus_number),
+			(int)(bitforce->usbdev->device_address));
+
+	int init_count = 0;
+reinit:
+
+	bitforce_initialise(bitforce, false);
+
+	if ((err = usb_write(bitforce, BITFORCE_IDENTIFY, BITFORCE_IDENTIFY_LEN, &amount, C_REQUESTIDENTIFY)) < 0 || amount != BITFORCE_IDENTIFY_LEN) {
+		applog(LOG_ERR, "%s detect (%s) send identify request failed (%d:%d)",
+			bitforce->api->dname, devpath, amount, err);
+		goto unshin;
 	}
 
-	BFclose(fdDev);
-	if (unlikely(!strstr(pdevbuf, "SHA256"))) {
-		applog(LOG_ERR, "BFL: Didn't recognise BitForce on %s", devpath);
-		return false;
+	if ((err = usb_ftdi_read_nl(bitforce, buf, sizeof(buf)-1, &amount, C_GETIDENTIFY)) < 0 || amount < 1) {
+		// Maybe it was still processing previous work?
+		if (++init_count <= REINIT_COUNT) {
+			if (init_count < 2) {
+				applog(LOG_WARNING, "%s detect (%s) 1st init failed - retrying (%d:%d)",
+					bitforce->api->dname, devpath, amount, err);
+			}
+			nmsleep(REINIT_TIME_MS);
+			goto reinit;
+		}
+
+		if (init_count > 0)
+			applog(LOG_WARNING, "%s detect (%s) init failed %d times",
+				bitforce->api->dname, devpath, init_count);
+
+		if (err < 0) {
+			applog(LOG_ERR, "%s detect (%s) error identify reply (%d:%d)",
+				bitforce->api->dname, devpath, amount, err);
+		} else {
+			applog(LOG_ERR, "%s detect (%s) empty identify reply (%d)",
+				bitforce->api->dname, devpath, amount);
+		}
+
+		goto unshin;
+	}
+	buf[amount] = '\0';
+
+	if (unlikely(!strstr(buf, "SHA256"))) {
+		applog(LOG_ERR, "%s detect (%s) didn't recognise '%s'",
+			bitforce->api->dname, devpath, buf);
+		goto unshin;
+	}
+
+	if (likely((!memcmp(buf, ">>>ID: ", 7)) && (s = strstr(buf + 3, ">>>")))) {
+		s[0] = '\0';
+		bitforce->name = strdup(buf + 7);
+	} else {
+		bitforce->name = (char *)blank;
 	}
 
 	// We have a real BitForce!
-	bitforce = calloc(1, sizeof(*bitforce));
-	bitforce->api = &bitforce_api;
-	bitforce->device_path = strdup(devpath);
-	bitforce->deven = DEV_ENABLED;
-	bitforce->threads = 1;
+	applog(LOG_DEBUG, "%s (%s) identified as: '%s'",
+		bitforce->api->dname, devpath, bitforce->name);
+
 	/* Initially enable support for nonce range and disable it later if it
 	 * fails */
 	if (opt_bfl_noncerange) {
@@ -129,108 +218,36 @@ static bool bitforce_detect_one(const char *devpath)
 		bitforce->kname = KNAME_WORK;
 	}
 
-	if (likely((!memcmp(pdevbuf, ">>>ID: ", 7)) && (s = strstr(pdevbuf + 3, ">>>")))) {
-		s[0] = '\0';
-		bitforce->name = strdup(pdevbuf + 7);
-	}
+	bitforce->device_path = strdup(devpath);
+
+	if (!add_cgpu(bitforce))
+		goto unshin;
+
+	update_usb_stats(bitforce);
 
 	mutex_init(&bitforce->device_mutex);
 
-	return add_cgpu(bitforce);
-}
+	return true;
 
-#define LOAD_SYM(sym)  do { \
-	if (!(sym = dlsym(dll, #sym))) {  \
-		applog(LOG_DEBUG, "Failed to load " #sym ", not using FTDI bitforce autodetect");  \
-		goto out;  \
-	}  \
-} while(0)
+unshin:
 
-#ifdef WIN32
-static int bitforce_autodetect_ftdi(void)
-{
-	char devpath[] = "\\\\.\\COMnnnnn";
-	char *devpathnum = &devpath[7];
-	char **bufptrs;
-	char *buf;
-	int found = 0;
-	DWORD i;
-
-	FT_STATUS ftStatus;
-	DWORD numDevs;
-	HMODULE dll = LoadLibrary("FTD2XX.DLL");
-	if (!dll) {
-		applog(LOG_DEBUG, "FTD2XX.DLL failed to load, not using FTDI bitforce autodetect");
-		return 0;
-	}
-	LOAD_SYM(FT_ListDevices);
-	LOAD_SYM(FT_Open);
-	LOAD_SYM(FT_GetComPortNumber);
-	LOAD_SYM(FT_Close);
-	
-	ftStatus = FT_ListDevices(&numDevs, NULL, FT_LIST_NUMBER_ONLY);
-	if (ftStatus != FT_OK) {
-		applog(LOG_DEBUG, "FTDI device count failed, not using FTDI bitforce autodetect");
-		goto out;
-	}
-	applog(LOG_DEBUG, "FTDI reports %u devices", (unsigned)numDevs);
-
-	buf = alloca(65 * numDevs);
-	bufptrs = alloca(sizeof(*bufptrs) * (numDevs + 1));
-
-	for (i = 0; i < numDevs; ++i)
-		bufptrs[i] = &buf[i * 65];
-	bufptrs[numDevs] = NULL;
-	ftStatus = FT_ListDevices(bufptrs, &numDevs, FT_LIST_ALL | FT_OPEN_BY_DESCRIPTION);
-	if (ftStatus != FT_OK) {
-		applog(LOG_DEBUG, "FTDI device list failed, not using FTDI bitforce autodetect");
-		goto out;
-	}
-	
-	for (i = numDevs; i > 0; ) {
-		--i;
-		bufptrs[i][64] = '\0';
-		
-		if (!(strstr(bufptrs[i], "BitFORCE") && strstr(bufptrs[i], "SHA256")))
-			continue;
-		
-		FT_HANDLE ftHandle;
-		if (FT_OK != FT_Open(i, &ftHandle))
-			continue;
-		LONG lComPortNumber;
-		ftStatus = FT_GetComPortNumber(ftHandle, &lComPortNumber);
-		FT_Close(ftHandle);
-		if (FT_OK != ftStatus || lComPortNumber < 0)
-			continue;
-		
-		sprintf(devpathnum, "%d", (int)lComPortNumber);
-		
-		if (bitforce_detect_one(devpath))
-			++found;
-	}
+	usb_uninit(bitforce);
 
-out:
-	dlclose(dll);
-	return found;
-}
-#else
-static int bitforce_autodetect_ftdi(void)
-{
-	return 0;
-}
-#endif
+shin:
 
-static int bitforce_detect_auto(void)
-{
-	return (serial_autodetect_udev     (bitforce_detect_one, "BitFORCE*SHA256") ?:
-		serial_autodetect_devserial(bitforce_detect_one, "BitFORCE_SHA256") ?:
-		bitforce_autodetect_ftdi() ?:
-		0);
+	free(bitforce->device_path);
+
+	if (bitforce->name != blank)
+		free(bitforce->name);
+
+	free(bitforce);
+
+	return false;
 }
 
 static void bitforce_detect(void)
 {
-	serial_detect_auto(&bitforce_api, bitforce_detect_one, bitforce_detect_auto);
+	usb_detect(&bitforce_api, bitforce_detect_one);
 }
 
 static void get_bitforce_statline_before(char *buf, struct cgpu_info *bitforce)
@@ -247,105 +264,17 @@ static void get_bitforce_statline_before(char *buf, struct cgpu_info *bitforce)
 static bool bitforce_thread_prepare(struct thr_info *thr)
 {
 	struct cgpu_info *bitforce = thr->cgpu;
-	int fdDev = BFopen(bitforce->device_path);
 	struct timeval now;
 
-	if (unlikely(fdDev == -1)) {
-		applog(LOG_ERR, "BFL%i: Failed to open %s", bitforce->device_id, bitforce->device_path);
-		return false;
-	}
-
-	bitforce->device_fd = fdDev;
-
-	applog(LOG_INFO, "BFL%i: Opened %s", bitforce->device_id, bitforce->device_path);
 	gettimeofday(&now, NULL);
 	get_datestamp(bitforce->init, &now);
 
 	return true;
 }
 
-static void bitforce_clear_buffer(struct cgpu_info *bitforce)
-{
-	int fdDev = bitforce->device_fd;
-	char pdevbuf[0x100];
-	int count = 0;
-
-	if (!fdDev)
-		return;
-
-	applog(LOG_DEBUG, "BFL%i: Clearing read buffer", bitforce->device_id);
-
-	mutex_lock(&bitforce->device_mutex);
-	do {
-		pdevbuf[0] = '\0';
-		BFgets(pdevbuf, sizeof(pdevbuf), fdDev);
-	} while (pdevbuf[0] && (++count < 10));
-	mutex_unlock(&bitforce->device_mutex);
-}
-
-void bitforce_init(struct cgpu_info *bitforce)
-{
-	char *devpath = bitforce->device_path;
-	int fdDev = bitforce->device_fd, retries = 0;
-	char pdevbuf[0x100];
-	char *s;
-
-	applog(LOG_WARNING, "BFL%i: Re-initialising", bitforce->device_id);
-
-	bitforce_clear_buffer(bitforce);
-
-	mutex_lock(&bitforce->device_mutex);
-	if (fdDev) {
-		BFclose(fdDev);
-		sleep(5);
-	}
-	bitforce->device_fd = 0;
-
-	fdDev = BFopen(devpath);
-	if (unlikely(fdDev == -1)) {
-		mutex_unlock(&bitforce->device_mutex);
-		applog(LOG_ERR, "BFL%i: Failed to open %s", bitforce->device_id, devpath);
-		return;
-	}
-
-	do {
-		BFwrite(fdDev, "ZGX", 3);
-		pdevbuf[0] = '\0';
-		BFgets(pdevbuf, sizeof(pdevbuf), fdDev);
-
-		if (unlikely(!pdevbuf[0])) {
-			mutex_unlock(&bitforce->device_mutex);
-			applog(LOG_ERR, "BFL%i: Error reading/timeout (ZGX)", bitforce->device_id);
-			return;
-		}
-
-		if (retries++)
-			nmsleep(10);
-	} while (!strstr(pdevbuf, "BUSY") && (retries * 10 < BITFORCE_TIMEOUT_MS));
-
-	if (unlikely(!strstr(pdevbuf, "SHA256"))) {
-		mutex_unlock(&bitforce->device_mutex);
-		applog(LOG_ERR, "BFL%i: Didn't recognise BitForce on %s returned: %s", bitforce->device_id, devpath, pdevbuf);
-		return;
-	}
-	
-	if (likely((!memcmp(pdevbuf, ">>>ID: ", 7)) && (s = strstr(pdevbuf + 3, ">>>")))) {
-		s[0] = '\0';
-		bitforce->name = strdup(pdevbuf + 7);
-	}
-
-	bitforce->device_fd = fdDev;
-	bitforce->sleep_ms = BITFORCE_SLEEP_MS;
-
-	mutex_unlock(&bitforce->device_mutex);
-}
-
 static void bitforce_flash_led(struct cgpu_info *bitforce)
 {
-	int fdDev = bitforce->device_fd;
-
-	if (!fdDev)
-		return;
+	int err, amount;
 
 	/* Do not try to flash the led if we're polling for a result to
 	 * minimise the chance of interleaved results */
@@ -353,19 +282,22 @@ static void bitforce_flash_led(struct cgpu_info *bitforce)
 		return;
 
 	/* It is not critical flashing the led so don't get stuck if we
-	 * can't grab the mutex here */
+	 * can't grab the mutex now */
 	if (mutex_trylock(&bitforce->device_mutex))
 		return;
 
-	BFwrite(fdDev, "ZMX", 3);
+	if ((err = usb_write(bitforce, BITFORCE_FLASH, BITFORCE_FLASH_LEN, &amount, C_REQUESTFLASH)) < 0 || amount != BITFORCE_FLASH_LEN) {
+		applog(LOG_ERR, "%s%i: flash request failed (%d:%d)",
+			bitforce->api->name, bitforce->device_id, amount, err);
+	} else {
+		/* However, this stops anything else getting a reply
+		 * So best to delay any other access to the BFL */
+		sleep(4);
+	}
 
 	/* Once we've tried - don't do it until told to again */
 	bitforce->flash_led = false;
 
-	/* However, this stops anything else getting a reply
-	 * So best to delay any other access to the BFL */
-	sleep(4);
-
 	mutex_unlock(&bitforce->device_mutex);
 
 	return; // nothing is returned by the BFL
@@ -373,13 +305,10 @@ static void bitforce_flash_led(struct cgpu_info *bitforce)
 
 static bool bitforce_get_temp(struct cgpu_info *bitforce)
 {
-	int fdDev = bitforce->device_fd;
-	char pdevbuf[0x100];
+	char buf[BITFORCE_BUFSIZ+1];
+	int err, amount;
 	char *s;
 
-	if (!fdDev)
-		return false;
-
 	/* Do not try to get the temperature if we're polling for a result to
 	 * minimise the chance of interleaved results */
 	if (bitforce->polling)
@@ -396,18 +325,30 @@ static bool bitforce_get_temp(struct cgpu_info *bitforce)
 	if (mutex_trylock(&bitforce->device_mutex))
 		return false;
 
-	BFwrite(fdDev, "ZLX", 3);
-	pdevbuf[0] = '\0';
-	BFgets(pdevbuf, sizeof(pdevbuf), fdDev);
-	mutex_unlock(&bitforce->device_mutex);
-	
-	if (unlikely(!pdevbuf[0])) {
-		applog(LOG_ERR, "BFL%i: Error: Get temp returned empty string/timed out", bitforce->device_id);
+	if ((err = usb_write(bitforce, BITFORCE_TEMPERATURE, BITFORCE_TEMPERATURE_LEN, &amount, C_REQUESTTEMPERATURE)) < 0 || amount != BITFORCE_TEMPERATURE_LEN) {
+		mutex_unlock(&bitforce->device_mutex);
+		applog(LOG_ERR, "%s%i: Error: Request temp invalid/timed out (%d:%d)",
+				bitforce->api->name, bitforce->device_id, amount, err);
 		bitforce->hw_errors++;
 		return false;
 	}
 
-	if ((!strncasecmp(pdevbuf, "TEMP", 4)) && (s = strchr(pdevbuf + 4, ':'))) {
+	if ((err = usb_ftdi_read_nl(bitforce, buf, sizeof(buf)-1, &amount, C_GETTEMPERATURE)) < 0 || amount < 1) {
+		mutex_unlock(&bitforce->device_mutex);
+		if (err < 0) {
+			applog(LOG_ERR, "%s%i: Error: Get temp return invalid/timed out (%d:%d)",
+					bitforce->api->name, bitforce->device_id, amount, err);
+		} else {
+			applog(LOG_ERR, "%s%i: Error: Get temp returned nothing (%d:%d)",
+					bitforce->api->name, bitforce->device_id, amount, err);
+		}
+		bitforce->hw_errors++;
+		return false;
+	}
+
+	mutex_unlock(&bitforce->device_mutex);
+	
+	if ((!strncasecmp(buf, "TEMP", 4)) && (s = strchr(buf + 4, ':'))) {
 		float temp = strtof(s + 1, NULL);
 
 		/* Cope with older software  that breaks and reads nonsense
@@ -418,7 +359,8 @@ static bool bitforce_get_temp(struct cgpu_info *bitforce)
 		if (temp > 0) {
 			bitforce->temp = temp;
 			if (unlikely(bitforce->cutofftemp > 0 && temp > bitforce->cutofftemp)) {
-				applog(LOG_WARNING, "BFL%i: Hit thermal cutoff limit, disabling!", bitforce->device_id);
+				applog(LOG_WARNING, "%s%i: Hit thermal cutoff limit, disabling!",
+							bitforce->api->name, bitforce->device_id);
 				bitforce->deven = DEV_RECOVER;
 				dev_error(bitforce, REASON_DEV_THERMAL_CUTOFF);
 			}
@@ -427,11 +369,12 @@ static bool bitforce_get_temp(struct cgpu_info *bitforce)
 		/* Use the temperature monitor as a kind of watchdog for when
 		 * our responses are out of sync and flush the buffer to
 		 * hopefully recover */
-		applog(LOG_WARNING, "BFL%i: Garbled response probably throttling, clearing buffer", bitforce->device_id);
+		applog(LOG_WARNING, "%s%i: Garbled response probably throttling, clearing buffer",
+					bitforce->api->name, bitforce->device_id);
 		dev_error(bitforce, REASON_DEV_THROTTLE);
 		/* Count throttling episodes as hardware errors */
 		bitforce->hw_errors++;
-		bitforce_clear_buffer(bitforce);
+		bitforce_initialise(bitforce, true);
 		return false;
 	}
 
@@ -441,35 +384,53 @@ static bool bitforce_get_temp(struct cgpu_info *bitforce)
 static bool bitforce_send_work(struct thr_info *thr, struct work *work)
 {
 	struct cgpu_info *bitforce = thr->cgpu;
-	int fdDev = bitforce->device_fd;
 	unsigned char ob[70];
-	char pdevbuf[0x100];
+	char buf[BITFORCE_BUFSIZ+1];
+	int err, amount;
 	char *s;
+	char *cmd;
+	int len;
 
-	if (!fdDev)
-		return false;
 re_send:
+	if (bitforce->nonce_range) {
+		cmd = BITFORCE_SENDRANGE;
+		len = BITFORCE_SENDRANGE_LEN;
+	} else {
+		cmd = BITFORCE_SENDWORK;
+		len = BITFORCE_SENDWORK_LEN;
+	}
+
 	mutex_lock(&bitforce->device_mutex);
-	if (bitforce->nonce_range)
-		BFwrite(fdDev, "ZPX", 3);
-	else
-		BFwrite(fdDev, "ZDX", 3);
-	pdevbuf[0] = '\0';
-	BFgets(pdevbuf, sizeof(pdevbuf), fdDev);
-	if (!pdevbuf[0] || !strncasecmp(pdevbuf, "B", 1)) {
+	if ((err = usb_write(bitforce, cmd, len, &amount, C_REQUESTSENDWORK)) < 0 || amount != len) {
+		mutex_unlock(&bitforce->device_mutex);
+		applog(LOG_ERR, "%s%i: request send work failed (%d:%d)",
+				bitforce->api->name, bitforce->device_id, amount, err);
+		return false;
+	}
+
+	if ((err = usb_ftdi_read_nl(bitforce, buf, sizeof(buf)-1, &amount, C_REQUESTSENDWORKSTATUS)) < 0) {
+		mutex_unlock(&bitforce->device_mutex);
+		applog(LOG_ERR, "%s%d: read request send work status failed (%d:%d)",
+				bitforce->api->name, bitforce->device_id, amount, err);
+		return false;
+	}
+
+	if (amount == 0 || !buf[0] || !strncasecmp(buf, "B", 1)) {
 		mutex_unlock(&bitforce->device_mutex);
 		nmsleep(WORK_CHECK_INTERVAL_MS);
 		goto re_send;
-	} else if (unlikely(strncasecmp(pdevbuf, "OK", 2))) {
+	} else if (unlikely(strncasecmp(buf, "OK", 2))) {
 		mutex_unlock(&bitforce->device_mutex);
 		if (bitforce->nonce_range) {
-			applog(LOG_WARNING, "BFL%i: Does not support nonce range, disabling", bitforce->device_id);
+			applog(LOG_WARNING, "%s%i: Does not support nonce range, disabling",
+						bitforce->api->name, bitforce->device_id);
 			bitforce->nonce_range = false;
 			bitforce->sleep_ms *= 5;
 			bitforce->kname = KNAME_WORK;
 			goto re_send;
 		}
-		applog(LOG_ERR, "BFL%i: Error: Send work reports: %s", bitforce->device_id, pdevbuf);
+		applog(LOG_ERR, "%s%i: Error: Send work reports: %s",
+				bitforce->api->name, bitforce->device_id, buf);
 		return false;
 	}
 
@@ -479,7 +440,7 @@ re_send:
 	if (!bitforce->nonce_range) {
 		sprintf((char *)ob + 8 + 32 + 12, ">>>>>>>>");
 		work->blk.nonce = bitforce->nonces = 0xffffffff;
-		BFwrite(fdDev, ob, 60);
+		len = 60;
 	} else {
 		uint32_t *nonce;
 
@@ -491,26 +452,41 @@ re_send:
 		*nonce = htobe32(work->blk.nonce + bitforce->nonces);
 		work->blk.nonce += bitforce->nonces + 1;
 		sprintf((char *)ob + 8 + 32 + 12 + 8, ">>>>>>>>");
-		BFwrite(fdDev, ob, 68);
+		len = 68;
+	}
+
+	if ((err = usb_write(bitforce, (char *)ob, len, &amount, C_SENDWORK)) < 0 || amount != len) {
+		mutex_unlock(&bitforce->device_mutex);
+		applog(LOG_ERR, "%s%i: send work failed (%d:%d)",
+				bitforce->api->name, bitforce->device_id, amount, err);
+		return false;
+	}
+
+	if ((err = usb_ftdi_read_nl(bitforce, buf, sizeof(buf)-1, &amount, C_SENDWORKSTATUS)) < 0) {
+		mutex_unlock(&bitforce->device_mutex);
+		applog(LOG_ERR, "%s%d: read send work status failed (%d:%d)",
+				bitforce->api->name, bitforce->device_id, amount, err);
+		return false;
 	}
 
-	pdevbuf[0] = '\0';
-	BFgets(pdevbuf, sizeof(pdevbuf), fdDev);
 	mutex_unlock(&bitforce->device_mutex);
 
 	if (opt_debug) {
 		s = bin2hex(ob + 8, 44);
-		applog(LOG_DEBUG, "BFL%i: block data: %s", bitforce->device_id, s);
+		applog(LOG_DEBUG, "%s%i: block data: %s",
+				bitforce->api->name, bitforce->device_id, s);
 		free(s);
 	}
 
-	if (unlikely(!pdevbuf[0])) {
-		applog(LOG_ERR, "BFL%i: Error: Send block data returned empty string/timed out", bitforce->device_id);
+	if (amount == 0 || !buf[0]) {
+		applog(LOG_ERR, "%s%i: Error: Send block data returned empty string/timed out",
+				bitforce->api->name, bitforce->device_id);
 		return false;
 	}
 
-	if (unlikely(strncasecmp(pdevbuf, "OK", 2))) {
-		applog(LOG_ERR, "BFL%i: Error: Send block data reports: %s", bitforce->device_id, pdevbuf);
+	if (unlikely(strncasecmp(buf, "OK", 2))) {
+		applog(LOG_ERR, "%s%i: Error: Send block data reports: %s",
+				bitforce->api->name, bitforce->device_id, buf);
 		return false;
 	}
 
@@ -521,53 +497,52 @@ re_send:
 static int64_t bitforce_get_result(struct thr_info *thr, struct work *work)
 {
 	struct cgpu_info *bitforce = thr->cgpu;
-	int fdDev = bitforce->device_fd;
 	unsigned int delay_time_ms;
 	struct timeval elapsed;
 	struct timeval now;
-	char pdevbuf[0x100];
+	char buf[BITFORCE_BUFSIZ+1];
+	int amount;
 	char *pnoncebuf;
 	uint32_t nonce;
 
-	if (!fdDev)
-		return -1;
-
 	while (1) {
 		if (unlikely(thr->work_restart))
 			return 0;
 
 		mutex_lock(&bitforce->device_mutex);
-		BFwrite(fdDev, "ZFX", 3);
-		pdevbuf[0] = '\0';
-		BFgets(pdevbuf, sizeof(pdevbuf), fdDev);
+		usb_write(bitforce, BITFORCE_WORKSTATUS, BITFORCE_WORKSTATUS_LEN, &amount, C_REQUESTWORKSTATUS);
+		usb_ftdi_read_nl(bitforce, buf, sizeof(buf)-1, &amount, C_GETWORKSTATUS);
 		mutex_unlock(&bitforce->device_mutex);
 
 		gettimeofday(&now, NULL);
 		timersub(&now, &bitforce->work_start_tv, &elapsed);
 
 		if (elapsed.tv_sec >= BITFORCE_LONG_TIMEOUT_S) {
-			applog(LOG_ERR, "BFL%i: took %dms - longer than %dms", bitforce->device_id,
+			applog(LOG_ERR, "%s%i: took %dms - longer than %dms",
+				bitforce->api->name, bitforce->device_id,
 				tv_to_ms(elapsed), BITFORCE_LONG_TIMEOUT_MS);
 			return 0;
 		}
 
-		if (pdevbuf[0] && strncasecmp(pdevbuf, "B", 1)) /* BFL does not respond during throttling */
+		if (amount > 0 && buf[0] && strncasecmp(buf, "B", 1)) /* BFL does not respond during throttling */
 			break;
 
 		/* if BFL is throttling, no point checking so quickly */
-		delay_time_ms = (pdevbuf[0] ? BITFORCE_CHECK_INTERVAL_MS : 2 * WORK_CHECK_INTERVAL_MS);
+		delay_time_ms = (buf[0] ? BITFORCE_CHECK_INTERVAL_MS : 2 * WORK_CHECK_INTERVAL_MS);
 		nmsleep(delay_time_ms);
 		bitforce->wait_ms += delay_time_ms;
 	}
 
 	if (elapsed.tv_sec > BITFORCE_TIMEOUT_S) {
-		applog(LOG_ERR, "BFL%i: took %dms - longer than %dms", bitforce->device_id,
+		applog(LOG_ERR, "%s%i: took %dms - longer than %dms",
+			bitforce->api->name, bitforce->device_id,
 			tv_to_ms(elapsed), BITFORCE_TIMEOUT_MS);
 		dev_error(bitforce, REASON_DEV_OVER_HEAT);
 
-		if (!pdevbuf[0])	/* Only return if we got nothing after timeout - there still may be results */
+		/* Only return if we got nothing after timeout - there still may be results */
+		if (amount == 0)
 			return 0;
-	} else if (!strncasecmp(pdevbuf, "N", 1)) {/* Hashing complete (NONCE-FOUND or NO-NONCE) */
+	} else if (!strncasecmp(buf, "N", 1)) {/* Hashing complete (NONCE-FOUND or NO-NONCE) */
 		/* Simple timing adjustment. Allow a few polls to cope with
 		 * OS timer delays being variably reliable. wait_ms will
 		 * always equal sleep_ms when we've waited greater than or
@@ -584,26 +559,31 @@ static int64_t bitforce_get_result(struct thr_info *thr, struct work *work)
 		}
 
 		if (delay_time_ms != bitforce->sleep_ms)
-			  applog(LOG_DEBUG, "BFL%i: Wait time changed to: %d, waited %u", bitforce->device_id, bitforce->sleep_ms, bitforce->wait_ms);
+			  applog(LOG_DEBUG, "%s%i: Wait time changed to: %d, waited %u",
+					bitforce->api->name, bitforce->device_id,
+					bitforce->sleep_ms, bitforce->wait_ms);
 
 		/* Work out the average time taken. Float for calculation, uint for display */
 		bitforce->avg_wait_f += (tv_to_ms(elapsed) - bitforce->avg_wait_f) / TIME_AVG_CONSTANT;
 		bitforce->avg_wait_d = (unsigned int) (bitforce->avg_wait_f + 0.5);
 	}
 
-	applog(LOG_DEBUG, "BFL%i: waited %dms until %s", bitforce->device_id, bitforce->wait_ms, pdevbuf);
-	if (!strncasecmp(&pdevbuf[2], "-", 1))
+	applog(LOG_DEBUG, "%s%i: waited %dms until %s",
+			bitforce->api->name, bitforce->device_id,
+			bitforce->wait_ms, buf);
+	if (!strncasecmp(&buf[2], "-", 1))
 		return bitforce->nonces;   /* No valid nonce found */
-	else if (!strncasecmp(pdevbuf, "I", 1))
+	else if (!strncasecmp(buf, "I", 1))
 		return 0;	/* Device idle */
-	else if (strncasecmp(pdevbuf, "NONCE-FOUND", 11)) {
+	else if (strncasecmp(buf, "NONCE-FOUND", 11)) {
 		bitforce->hw_errors++;
-		applog(LOG_WARNING, "BFL%i: Error: Get result reports: %s", bitforce->device_id, pdevbuf);
-		bitforce_clear_buffer(bitforce);
+		applog(LOG_WARNING, "%s%i: Error: Get result reports: %s",
+			bitforce->api->name, bitforce->device_id, buf);
+		bitforce_initialise(bitforce, true);
 		return 0;
 	}
 
-	pnoncebuf = &pdevbuf[12];
+	pnoncebuf = &buf[12];
 
 	while (1) {
 		hex2bin((void*)&nonce, pnoncebuf, 4);
@@ -612,7 +592,8 @@ static int64_t bitforce_get_result(struct thr_info *thr, struct work *work)
 #endif
 		if (unlikely(bitforce->nonce_range && (nonce >= work->blk.nonce ||
 			(work->blk.nonce > 0 && nonce < work->blk.nonce - bitforce->nonces - 1)))) {
-				applog(LOG_WARNING, "BFL%i: Disabling broken nonce range support", bitforce->device_id);
+				applog(LOG_WARNING, "%s%i: Disabling broken nonce range support",
+					bitforce->api->name, bitforce->device_id);
 				bitforce->nonce_range = false;
 				work->blk.nonce = 0xffffffff;
 				bitforce->sleep_ms *= 5;
@@ -628,19 +609,16 @@ static int64_t bitforce_get_result(struct thr_info *thr, struct work *work)
 	return bitforce->nonces;
 }
 
-static void bitforce_shutdown(struct thr_info *thr)
+static void bitforce_shutdown(__maybe_unused struct thr_info *thr)
 {
-	struct cgpu_info *bitforce = thr->cgpu;
-
-	BFclose(bitforce->device_fd);
-	bitforce->device_fd = 0;
+//	struct cgpu_info *bitforce = thr->cgpu;
 }
 
 static void biforce_thread_enable(struct thr_info *thr)
 {
 	struct cgpu_info *bitforce = thr->cgpu;
 
-	bitforce_init(bitforce);
+	bitforce_initialise(bitforce, true);
 }
 
 static int64_t bitforce_scanhash(struct thr_info *thr, struct work *work, int64_t __maybe_unused max_nonce)
@@ -665,11 +643,11 @@ static int64_t bitforce_scanhash(struct thr_info *thr, struct work *work, int64_
 
 	if (ret == -1) {
 		ret = 0;
-		applog(LOG_ERR, "BFL%i: Comms error", bitforce->device_id);
+		applog(LOG_ERR, "%s%i: Comms error", bitforce->api->name, bitforce->device_id);
 		dev_error(bitforce, REASON_DEV_COMMS_ERROR);
 		bitforce->hw_errors++;
 		/* empty read buffer */
-		bitforce_clear_buffer(bitforce);
+		bitforce_initialise(bitforce, true);
 	}
 	return ret;
 }
@@ -692,7 +670,8 @@ static bool bitforce_thread_init(struct thr_info *thr)
 	/* Pause each new thread at least 100ms between initialising
 	 * so the devices aren't making calls all at the same time. */
 	wait = thr->id * MAX_START_DELAY_MS;
-	applog(LOG_DEBUG, "BFL%i: Delaying start by %dms", bitforce->device_id, wait / 1000);
+	applog(LOG_DEBUG, "%s%d: Delaying start by %dms",
+			bitforce->api->name, bitforce->device_id, wait / 1000);
 	nmsleep(wait);
 
 	return true;
@@ -717,7 +696,6 @@ struct device_api bitforce_api = {
 	.name = "BFL",
 	.api_detect = bitforce_detect,
 	.get_api_stats = bitforce_api_stats,
-	.reinit_device = bitforce_init,
 	.get_statline_before = get_bitforce_statline_before,
 	.get_stats = bitforce_get_stats,
 	.identify_device = bitforce_identify,

+ 7 - 3
miner.h

@@ -114,7 +114,7 @@ static inline int fsync (int fd)
   #include "libztex.h"
 #endif
 
-#ifdef USE_MODMINER
+#if defined(USE_MODMINER) || defined(USE_BITFORCE)
   #include "usbutils.h"
 #endif
 
@@ -374,13 +374,17 @@ struct cgpu_info {
 #ifdef USE_ZTEX
 		struct libztex_device *device_ztex;
 #endif
-#ifdef USE_MODMINER
+#if defined(USE_MODMINER) || defined(USE_BITFORCE)
 		struct cg_usb_device *usbdev;
 #endif
+#ifdef USE_ICARUS
 		int device_fd;
+#endif
 	};
-#ifdef USE_MODMINER
+#if defined(USE_MODMINER) || defined(USE_BITFORCE)
 	int usbstat;
+#endif
+#ifdef USE_MODMINER
 	char fpgaid;
 	unsigned char clock;
 	pthread_mutex_t *modminer_mutex;

+ 122 - 16
usbutils.c

@@ -36,11 +36,25 @@
 #define EPO(x) (LIBUSB_ENDPOINT_OUT | (unsigned char)(x))
 
 #ifdef WIN32
+#define BITFORCE_TIMEOUT_MS 500
 #define MODMINER_TIMEOUT_MS 200
 #else
+#define BITFORCE_TIMEOUT_MS 200
 #define MODMINER_TIMEOUT_MS 100
 #endif
 
+#ifdef USE_BITFORCE
+static struct usb_endpoints bfl_eps[] = {
+#ifdef WIN32
+	{ LIBUSB_TRANSFER_TYPE_BULK,	64,	EPI(1), 0 },
+	{ LIBUSB_TRANSFER_TYPE_BULK,	64,	EPO(2), 0 }
+#else
+	{ LIBUSB_TRANSFER_TYPE_BULK,	512,	EPI(1), 0 },
+	{ LIBUSB_TRANSFER_TYPE_BULK,	512,	EPO(2), 0 }
+#endif
+};
+#endif
+
 #ifdef USE_MODMINER
 static struct usb_endpoints mmq_eps[] = {
 	{ LIBUSB_TRANSFER_TYPE_BULK,	64,	EPI(3), 0 },
@@ -48,7 +62,7 @@ static struct usb_endpoints mmq_eps[] = {
 };
 #endif
 
-// TODO: Add support for (at least) Interrupt endpoints
+// TODO: Add support for (at least) Isochronous endpoints
 static struct usb_find_devices find_dev[] = {
 /*
 #ifdef USE_ICARUS
@@ -56,10 +70,19 @@ static struct usb_find_devices find_dev[] = {
 	{ DRV_ICARUS, 	"LOT",	0x0403,	0x6001,	false,	EPI(0),	EPO(0), 1 },
 	{ DRV_ICARUS, 	"CM1",	0x067b,	0x0230,	false,	EPI(0),	EPO(0), 1 },
 #endif
+*/
 #ifdef USE_BITFORCE
-	{ DRV_BITFORCE,	"BFL",	0x0403,	0x6014,	true,	EPI(1),	EPO(2), 1 },
+	{
+		.drv = DRV_BITFORCE,
+		.name = "BFL",
+		.idVendor = 0x0403,
+		.idProduct = 0x6014,
+		.config = 1,
+		.interface = 0,
+		.timeout = BITFORCE_TIMEOUT_MS,
+		.epcount = ARRAY_SIZE(bfl_eps),
+		.eps = bfl_eps },
 #endif
-*/
 #ifdef USE_MODMINER
 	{
 		.drv = DRV_MODMINER,
@@ -160,6 +183,18 @@ static const char *C_SENDWORK_S = "SendWork";
 static const char *C_SENDWORKSTATUS_S = "SendWorkStatus";
 static const char *C_REQUESTWORKSTATUS_S = "RequestWorkStatus";
 static const char *C_GETWORKSTATUS_S = "GetWorkStatus";
+static const char *C_REQUESTIDENTIFY_S = "RequestIdentify";
+static const char *C_GETIDENTIFY_S = "GetIdentify";
+static const char *C_REQUESTFLASH_S = "RequestFlash";
+static const char *C_REQUESTSENDWORK_S = "RequestSendWork";
+static const char *C_REQUESTSENDWORKSTATUS_S = "RequestSendWorkStatus";
+static const char *C_RESET_S = "Reset";
+static const char *C_SETBAUD_S = "SetBaud";
+static const char *C_SETDATA_S = "SetDataCtrl";
+static const char *C_SETFLOW_S = "SetFlowCtrl";
+static const char *C_SETMODEM_S = "SetModemCtrl";
+static const char *C_PURGERX_S = "PurgeRx";
+static const char *C_PURGETX_S = "PurgeTx";
 
 #ifdef EOL
 #undef EOL
@@ -505,6 +540,7 @@ static void cgusb_check_init()
 		list_lock = calloc(1, sizeof(*list_lock));
 		mutex_init(list_lock);
 
+		// N.B. environment LIBUSB_DEBUG also sets libusb_set_debug()
 		if (opt_usbdump >= 0) {
 			libusb_set_debug(NULL, opt_usbdump);
 			usb_all();
@@ -537,6 +573,18 @@ static void cgusb_check_init()
 		usb_commands[C_SENDWORKSTATUS] = C_SENDWORKSTATUS_S;
 		usb_commands[C_REQUESTWORKSTATUS] = C_REQUESTWORKSTATUS_S;
 		usb_commands[C_GETWORKSTATUS] = C_GETWORKSTATUS_S;
+		usb_commands[C_REQUESTIDENTIFY] = C_REQUESTIDENTIFY_S;
+		usb_commands[C_GETIDENTIFY] = C_GETIDENTIFY_S;
+		usb_commands[C_REQUESTFLASH] = C_REQUESTFLASH_S;
+		usb_commands[C_REQUESTSENDWORK] = C_REQUESTSENDWORK_S;
+		usb_commands[C_REQUESTSENDWORKSTATUS] = C_REQUESTSENDWORKSTATUS_S;
+		usb_commands[C_RESET] = C_RESET_S;
+		usb_commands[C_SETBAUD] = C_SETBAUD_S;
+		usb_commands[C_SETDATA] = C_SETDATA_S;
+		usb_commands[C_SETFLOW] = C_SETFLOW_S;
+		usb_commands[C_SETMODEM] = C_SETMODEM_S;
+		usb_commands[C_PURGERX] = C_PURGERX_S;
+		usb_commands[C_PURGETX] = C_PURGETX_S;
 	}
 
 	mutex_unlock(&cgusb_lock);
@@ -745,9 +793,9 @@ bool usb_init(struct cgpu_info *cgpu, struct libusb_device *dev, struct usb_find
 	}
 
 	if (libusb_kernel_driver_active(cgusb->handle, 0) == 1) {
-		applog(LOG_WARNING, "USB init, kernel attached ...");
+		applog(LOG_DEBUG, "USB init, kernel attached ...");
 		if (libusb_detach_kernel_driver(cgusb->handle, 0) == 0)
-			applog(LOG_WARNING, "USB init, kernel detached successfully");
+			applog(LOG_DEBUG, "USB init, kernel detached successfully");
 		else
 			applog(LOG_WARNING, "USB init, kernel detach failed :(");
 	}
@@ -773,7 +821,7 @@ bool usb_init(struct cgpu_info *cgpu, struct libusb_device *dev, struct usb_find
 		goto cldame;
 	}
 
-	if ((int)(config->bNumInterfaces) < found->interface)
+	if ((int)(config->bNumInterfaces) <= found->interface)
 		goto cldame;
 
 	for (i = 0; i < found->epcount; i++)
@@ -1118,26 +1166,42 @@ static void stats(struct cgpu_info *cgpu, struct timeval *tv_start, struct timev
 }
 #endif
 
-int _usb_read(struct cgpu_info *cgpu, int ep, char *buf, size_t bufsiz, int *processed, unsigned int timeout, int eol, enum usb_cmds cmd)
+int _usb_read(struct cgpu_info *cgpu, int ep, char *buf, size_t bufsiz, int *processed, unsigned int timeout, int eol, enum usb_cmds cmd, bool ftdi)
 {
 	struct cg_usb_device *usbdev = cgpu->usbdev;
 #if DO_USB_STATS
-	struct timeval tv_start, tv_finish;
+	struct timeval tv_start;
 #endif
-	int err, got, tot;
+	struct timeval read_start, tv_finish;
+	unsigned int initial_timeout;
+	double max, done;
+	int err, got, tot, i;
 	bool first = true;
 
+	if (timeout == DEVTIMEOUT)
+		timeout = usbdev->found->timeout;
+
 	if (eol == -1) {
 		got = 0;
 		STATS_TIMEVAL(&tv_start);
 		err = libusb_bulk_transfer(usbdev->handle,
 				usbdev->found->eps[ep].ep,
 				(unsigned char *)buf,
-				bufsiz, &got,
-				timeout == DEVTIMEOUT ? usbdev->found->timeout : timeout);
+				bufsiz, &got, timeout);
 		STATS_TIMEVAL(&tv_finish);
 		USB_STATS(cgpu, &tv_start, &tv_finish, err, cmd, SEQ0);
 
+		if (ftdi) {
+			// first 2 bytes returned are an FTDI status
+			if (got > 2) {
+				got -= 2;
+				memmove(buf, buf+2, got+1);
+			} else {
+				got = 0;
+				*buf = '\0';
+			}
+		}
+
 		*processed = got;
 
 		return err;
@@ -1145,31 +1209,55 @@ int _usb_read(struct cgpu_info *cgpu, int ep, char *buf, size_t bufsiz, int *pro
 
 	tot = 0;
 	err = LIBUSB_SUCCESS;
+	initial_timeout = timeout;
+	max = ((double)timeout) / 1000.0;
+	gettimeofday(&read_start, NULL);
 	while (bufsiz) {
 		got = 0;
 		STATS_TIMEVAL(&tv_start);
 		err = libusb_bulk_transfer(usbdev->handle,
 				usbdev->found->eps[ep].ep,
 				(unsigned char *)buf,
-				1, &got,
-				timeout == DEVTIMEOUT ? usbdev->found->timeout : timeout);
-		STATS_TIMEVAL(&tv_finish);
+				bufsiz, &got, timeout);
+		gettimeofday(&tv_finish, NULL);
 		USB_STATS(cgpu, &tv_start, &tv_finish, err, cmd, first ? SEQ0 : SEQ1);
 
+		if (ftdi) {
+			// first 2 bytes returned are an FTDI status
+			if (got > 2) {
+				got -= 2;
+				memmove(buf, buf+2, got+1);
+			} else {
+				got = 0;
+				*buf = '\0';
+			}
+		}
+
 		tot += got;
 
 		if (err)
 			break;
 
-		if (eol == buf[0])
-			break;
+		// WARNING - this will return data past EOL ('if' there is extra data)
+		for (i = 0; i < got; i++)
+			if (buf[i] == eol)
+				goto goteol;
 
 		buf += got;
 		bufsiz -= got;
 
 		first = false;
+
+		done = tdiff(&tv_finish, &read_start);
+		// N.B. this is return LIBUSB_SUCCESS with whatever size has already been read
+		if (unlikely(done >= max))
+			break;
+
+		timeout = initial_timeout - (done * 1000);
 	}
 
+goteol:
+
 	*processed = tot;
 
 	return err;
@@ -1198,6 +1286,24 @@ int _usb_write(struct cgpu_info *cgpu, int ep, char *buf, size_t bufsiz, int *pr
 	return err;
 }
 
+int _usb_transfer(struct cgpu_info *cgpu, uint8_t request_type, uint8_t bRequest, uint16_t wValue, uint16_t wIndex, unsigned int timeout, enum usb_cmds cmd)
+{
+	struct cg_usb_device *usbdev = cgpu->usbdev;
+#if DO_USB_STATS
+	struct timeval tv_start, tv_finish;
+#endif
+	int err;
+
+	STATS_TIMEVAL(&tv_start);
+	err = libusb_control_transfer(usbdev->handle, request_type,
+		bRequest, wValue, wIndex, NULL, 0,
+		timeout == DEVTIMEOUT ? usbdev->found->timeout : timeout);
+	STATS_TIMEVAL(&tv_finish);
+	USB_STATS(cgpu, &tv_start, &tv_finish, err, cmd, SEQ0);
+
+	return err;
+}
+
 void usb_cleanup()
 {
 	// TODO:

+ 50 - 5
usbutils.h

@@ -12,6 +12,29 @@
 
 #include <libusb.h>
 
+// for 0x0403/0x6014 FT232H (and possibly others?)
+#define FTDI_TYPE_OUT (LIBUSB_REQUEST_TYPE_VENDOR | LIBUSB_RECIPIENT_DEVICE | LIBUSB_ENDPOINT_OUT)
+
+#define FTDI_REQUEST_RESET ((uint8_t)0)
+#define FTDI_REQUEST_MODEM ((uint8_t)1)
+#define FTDI_REQUEST_FLOW ((uint8_t)2)
+#define FTDI_REQUEST_BAUD ((uint8_t)3)
+#define FTDI_REQUEST_DATA ((uint8_t)4)
+
+#define FTDI_VALUE_RESET 0
+#define FTDI_VALUE_PURGE_RX 1
+#define FTDI_VALUE_PURGE_TX 2
+
+// baud with a 0 divisor is 120,000,000/10
+//#define FTDI_VALUE_BAUD (0)
+//#define FTDI_INDEX_BAUD (0)
+#define FTDI_VALUE_BAUD 0xc068
+#define FTDI_INDEX_BAUD 0x0200
+
+#define FTDI_VALUE_DATA 0
+#define FTDI_VALUE_FLOW 0
+#define FTDI_VALUE_MODEM 0x0303
+
 // Use the device defined timeout
 #define DEVTIMEOUT 0
 
@@ -78,6 +101,18 @@ enum usb_cmds {
 	C_SENDWORKSTATUS,
 	C_REQUESTWORKSTATUS,
 	C_GETWORKSTATUS,
+	C_REQUESTIDENTIFY,
+	C_GETIDENTIFY,
+	C_REQUESTFLASH,
+	C_REQUESTSENDWORK,
+	C_REQUESTSENDWORKSTATUS,
+	C_RESET,
+	C_SETBAUD,
+	C_SETDATA,
+	C_SETFLOW,
+	C_SETMODEM,
+	C_PURGERX,
+	C_PURGETX,
 	C_MAX
 };
 
@@ -89,21 +124,25 @@ bool usb_init(struct cgpu_info *cgpu, struct libusb_device *dev, struct usb_find
 void usb_detect(struct device_api *api, bool (*device_detect)(struct libusb_device *, struct usb_find_devices *));
 struct api_data *api_usb_stats(int *count);
 void update_usb_stats(struct cgpu_info *cgpu);
-int _usb_read(struct cgpu_info *cgpu, int ep, char *buf, size_t bufsiz, int *processed, unsigned int timeout, int eol, enum usb_cmds);
+int _usb_read(struct cgpu_info *cgpu, int ep, char *buf, size_t bufsiz, int *processed, unsigned int timeout, int eol, enum usb_cmds, bool ftdi);
 int _usb_write(struct cgpu_info *cgpu, int ep, char *buf, size_t bufsiz, int *processed, unsigned int timeout, enum usb_cmds);
+int _usb_transfer(struct cgpu_info *cgpu, uint8_t request_type, uint8_t bRequest, uint16_t wValue, uint16_t wIndex, unsigned int timeout, enum usb_cmds cmd);
 void usb_cleanup();
 
 #define usb_read(cgpu, buf, bufsiz, read, cmd) \
-	_usb_read(cgpu, DEFAULT_EP_IN, buf, bufsiz, read, DEVTIMEOUT, -1, cmd)
+	_usb_read(cgpu, DEFAULT_EP_IN, buf, bufsiz, read, DEVTIMEOUT, -1, cmd, false)
+
+#define usb_read_nl(cgpu, buf, bufsiz, read, cmd) \
+	_usb_read(cgpu, DEFAULT_EP_IN, buf, bufsiz, read, DEVTIMEOUT, '\n', cmd, false)
 
 #define usb_read_ep(cgpu, ep, buf, bufsiz, read, cmd) \
-	_usb_read(cgpu, ep, buf, bufsiz, read, DEVTIMEOUT, -1, cmd)
+	_usb_read(cgpu, ep, buf, bufsiz, read, DEVTIMEOUT, -1, cmd, false)
 
 #define usb_read_timeout(cgpu, buf, bufsiz, read, timeout, cmd) \
-	_usb_read(cgpu, DEFAULT_EP_IN, buf, bufsiz, read, timeout, -1, cmd)
+	_usb_read(cgpu, DEFAULT_EP_IN, buf, bufsiz, read, timeout, -1, cmd, false)
 
 #define usb_read_ep_timeout(cgpu, ep, buf, bufsiz, read, timeout, cmd) \
-	_usb_read(cgpu, ep, buf, bufsiz, read, timeout, -1, cmd)
+	_usb_read(cgpu, ep, buf, bufsiz, read, timeout, -1, cmd, false)
 
 #define usb_write(cgpu, buf, bufsiz, wrote, cmd) \
 	_usb_write(cgpu, DEFAULT_EP_OUT, buf, bufsiz, wrote, DEVTIMEOUT, cmd)
@@ -117,4 +156,10 @@ void usb_cleanup();
 #define usb_write_ep_timeout(cgpu, ep, buf, bufsiz, wrote, timeout, cmd) \
 	_usb_write(cgpu, ep, buf, bufsiz, wrote, timeout, cmd)
 
+#define usb_ftdi_read_nl(cgpu, buf, bufsiz, read, cmd) \
+	_usb_read(cgpu, DEFAULT_EP_IN, buf, bufsiz, read, DEVTIMEOUT, '\n', cmd, true)
+
+#define usb_transfer(cgpu, typ, req, val, idx, cmd) \
+	_usb_transfer(cgpu, typ, req, val, idx, DEVTIMEOUT, cmd)
+
 #endif