Browse Source

Merge branch 'cairnsmore' into bfgminer

Conflicts:
	fpgautils.c
	fpgautils.h
Luke Dashjr 13 years ago
parent
commit
e6b6d8816d
11 changed files with 479 additions and 222 deletions
  1. 7 3
      FPGA-README
  2. 2 1
      Makefile.am
  3. 5 0
      api.c
  4. 1 104
      driver-bitforce.c
  5. 72 0
      driver-cairnsmore.c
  6. 127 105
      driver-icarus.c
  7. 167 3
      fpgautils.c
  8. 10 5
      fpgautils.h
  9. 76 0
      icarus-common.h
  10. 10 1
      miner.c
  11. 2 0
      miner.h

+ 7 - 3
FPGA-README

@@ -42,7 +42,7 @@ Icarus
 There are two hidden options in cgminer when Icarus support is compiled in:
 
 --icarus-options <arg> Set specific FPGA board configurations - one set of values for all or comma separated
-           baud:work_division:fpga_count
+           baud:work_division:fpga_count:quirks
 
            baud           The Serial/USB baud rate - 115200 or 57600 only - default 115200
            work_division  The fraction of work divided up for each FPGA chip - 1, 2, 4 or 8
@@ -51,13 +51,17 @@ There are two hidden options in cgminer when Icarus support is compiled in:
                           as work_division - range is from 1 up to 'work_division'
                           It defaults to the value of work_division - or 2 if you don't specify
                           work_division
+           quirks         List of quirks to enable and disable (after a minus sign):
+                            r  Reopen device regularly to workaround buggy Icarus USB chipset
+                               (enabled by default)
 
 If you define fewer comma seperated values than Icarus devices, the last values will be used
 for all extra devices
 
-An example would be: --icarus-options 57600:2:1
+An example would be: --icarus-options 57600:2:1:-r
 This would mean: use 57600 baud, the FPGA board divides the work in half however
-only 1 FPGA actually runs on the board (e.g. like an early CM1 Icarus copy bitstream)
+only 1 FPGA actually runs on the board, and don't reopen the device (e.g. like
+an early CM1 Icarus copy bitstream)
 
 --icarus-timing <arg> Set how the Icarus timing is calculated - one setting/value for all or comma separated
            default[=N]   Use the default Icarus hash time (2.6316ns)

+ 2 - 1
Makefile.am

@@ -88,7 +88,8 @@ endif
 endif
 
 if HAS_ICARUS
-bfgminer_SOURCES += driver-icarus.c
+bfgminer_SOURCES += driver-icarus.c icarus-common.h
+bfgminer_SOURCES += driver-cairnsmore.c
 endif
 
 if HAS_MODMINER

+ 5 - 0
api.c

@@ -606,6 +606,7 @@ extern struct device_api bitforce_api;
 #endif
 
 #ifdef USE_ICARUS
+extern struct device_api cairnsmore_api;
 extern struct device_api icarus_api;
 #endif
 
@@ -1037,6 +1038,8 @@ static int numpgas()
 			count++;
 #endif
 #ifdef USE_ICARUS
+		if (devices[i]->api == &cairnsmore_api)
+			count++;
 		if (devices[i]->api == &icarus_api)
 			count++;
 #endif
@@ -1063,6 +1066,8 @@ static int pgadevice(int pgaid)
 			count++;
 #endif
 #ifdef USE_ICARUS
+		if (devices[i]->api == &cairnsmore_api)
+			count++;
 		if (devices[i]->api == &icarus_api)
 			count++;
 #endif

+ 1 - 104
driver-bitforce.c

@@ -18,28 +18,6 @@
 
 #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,
-};
-
-#endif /* WIN32 */
-
 #include "compat.h"
 #include "fpgautils.h"
 #include "miner.h"
@@ -137,92 +115,11 @@ static bool bitforce_detect_one(const char *devpath)
 	return add_cgpu(bitforce);
 }
 
-#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)
-
-#ifdef WIN32
-static int bitforce_autodetect_ftdi(void)
-{
-	char devpath[] = "\\\\.\\COMnnnnn";
-	char *devpathnum = &devpath[7];
-	char **bufptrs;
-	char *buf;
-	int found = 0;
-	int 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;
-	}
-
-out:
-	dlclose(dll);
-	return found;
-}
-#else
-static int bitforce_autodetect_ftdi(void)
-{
-	return 0;
-}
-#endif
-
 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() ?:
+		serial_autodetect_ftdi(bitforce_detect_one, "BitFORCE", "SHA256") ?:
 		0);
 }
 

+ 72 - 0
driver-cairnsmore.c

@@ -0,0 +1,72 @@
+/*
+ * Copyright 2012 Luke Dashjr
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 3 of the License, or (at your option)
+ * any later version.  See COPYING for more details.
+ */
+
+#include "fpgautils.h"
+#include "icarus-common.h"
+#include "miner.h"
+
+#define CAIRNSMORE1_IO_SPEED 115200
+#define CAIRNSMORE1_HASH_TIME 0.0000000024484
+
+struct device_api cairnsmore_api;
+
+static bool cairnsmore_detect_one(const char *devpath)
+{
+	struct ICARUS_INFO *info = calloc(1, sizeof(struct ICARUS_INFO));
+	if (unlikely(!info))
+		quit(1, "Failed to malloc ICARUS_INFO");
+
+	info->baud = CAIRNSMORE1_IO_SPEED;
+	info->work_division = 2;
+	info->fpga_count = 2;
+	info->quirk_reopen = false;
+	info->Hs = CAIRNSMORE1_HASH_TIME;
+
+	if (!icarus_detect_custom(devpath, &cairnsmore_api, info)) {
+		free(info);
+		return false;
+	}
+	return true;
+}
+
+static int cairnsmore_detect_auto(void)
+{
+	return
+	serial_autodetect_udev     (cairnsmore_detect_one, "*Cairnsmore1*") ?:
+	serial_autodetect_devserial(cairnsmore_detect_one, "Cairnsmore1") ?:
+	serial_autodetect_ftdi     (cairnsmore_detect_one, "Cairnsmore1", NULL) ?:
+	0;
+}
+
+static void cairnsmore_detect()
+{
+	// Actual serial detection is handled by Icarus driver
+	serial_detect_auto_byname(cairnsmore_api.dname, cairnsmore_detect_one, cairnsmore_detect_auto);
+}
+
+void convert_icarus_to_cairnsmore(struct cgpu_info *cm1)
+{
+	struct ICARUS_INFO *info = cm1->cgpu_data;
+	info->Hs = CAIRNSMORE1_HASH_TIME;
+	info->fullnonce = info->Hs * (((double)0xffffffff) + 1);
+	info->read_count = (int)(info->fullnonce * TIME_FACTOR) - 1;
+	cm1->api = &cairnsmore_api;
+	renumber_cgpu(cm1);
+}
+
+extern struct device_api icarus_api;
+
+__attribute__((constructor(1000)))
+static void cairnsmore_api_init()
+{
+	cairnsmore_api = icarus_api;
+	cairnsmore_api.dname = "cairnsmore";
+	cairnsmore_api.name = "ECM";
+	cairnsmore_api.api_detect = cairnsmore_detect;
+}

+ 127 - 105
driver-icarus.c

@@ -56,6 +56,7 @@
 
 #include "elist.h"
 #include "fpgautils.h"
+#include "icarus-common.h"
 #include "miner.h"
 
 // The serial I/O speed - Linux uses a define 'B115200' in bits/termios.h
@@ -73,20 +74,12 @@ ASSERT1(sizeof(uint32_t) == 4);
 
 #define ICARUS_READ_TIME(baud) ((double)ICARUS_READ_SIZE * (double)8.0 / (double)(baud))
 
-// Fraction of a second, USB timeout is measured in
-// i.e. 10 means 1/10 of a second
-// Right now, it MUST be 10 due to other assumptions.
-#define TIME_FACTOR 10
-// It's 10 per second, thus value = 10/TIME_FACTOR =
-#define ICARUS_READ_FAULT_DECISECONDS 1
-
 // In timing mode: Default starting value until an estimate can be obtained
 // 5 seconds allows for up to a ~840MH/s device
 #define ICARUS_READ_COUNT_TIMING	(5 * TIME_FACTOR)
 
 // For a standard Icarus REV3
 #define ICARUS_REV3_HASH_TIME 0.00000000264083
-#define NANOSEC 1000000000.0
 
 // Icarus Rev3 doesn't send a completion message when it finishes
 // the full nonce range, so to avoid being idle we must abort the
@@ -134,68 +127,14 @@ ASSERT1(sizeof(uint32_t) == 4);
 
 static struct timeval history_sec = { HISTORY_SEC, 0 };
 
-// Store the last INFO_HISTORY data sets
-// [0] = current data, not yet ready to be included as an estimate
-// Each new data set throws the last old set off the end thus
-// keeping a ongoing average of recent data
-#define INFO_HISTORY 10
-
-struct ICARUS_HISTORY {
-	struct timeval finish;
-	double sumXiTi;
-	double sumXi;
-	double sumTi;
-	double sumXi2;
-	uint32_t values;
-	uint32_t hash_count_min;
-	uint32_t hash_count_max;
-};
-
-enum timing_mode { MODE_DEFAULT, MODE_SHORT, MODE_LONG, MODE_VALUE };
-
 static const char *MODE_DEFAULT_STR = "default";
 static const char *MODE_SHORT_STR = "short";
 static const char *MODE_LONG_STR = "long";
 static const char *MODE_VALUE_STR = "value";
 static const char *MODE_UNKNOWN_STR = "unknown";
 
-struct ICARUS_INFO {
-	// time to calculate the golden_ob
-	uint64_t golden_hashes;
-	struct timeval golden_tv;
-
-	struct ICARUS_HISTORY history[INFO_HISTORY+1];
-	uint32_t min_data_count;
-
-	// seconds per Hash
-	double Hs;
-	int read_count;
-
-	enum timing_mode timing_mode;
-	bool do_icarus_timing;
-
-	double fullnonce;
-	int count;
-	double W;
-	uint32_t values;
-	uint64_t hash_count_range;
-
-	// Determine the cost of history processing
-	// (which will only affect W)
-	uint64_t history_count;
-	struct timeval history_time;
-
-	// icarus-options
-	int baud;
-	int work_division;
-	int fpga_count;
-	uint32_t nonce_mask;
-};
-
 #define END_CONDITION 0x0000ffff
-
-// One for each possible device
-static struct ICARUS_INFO **icarus_info;
+#define DEFAULT_DETECT_THRESHOLD 1
 
 // Looking for options in --icarus-timing and --icarus-options:
 //
@@ -216,6 +155,8 @@ static int option_offset = -1;
 
 struct device_api icarus_api;
 
+extern void convert_icarus_to_cairnsmore(struct cgpu_info *);
+
 static void rev(unsigned char *s, size_t l)
 {
 	size_t i, j;
@@ -351,7 +292,7 @@ static const char *timing_mode_str(enum timing_mode timing_mode)
 
 static void set_timing_mode(int this_option_offset, struct cgpu_info *icarus)
 {
-	struct ICARUS_INFO *info = icarus_info[icarus->device_id];
+	struct ICARUS_INFO *info = icarus->cgpu_data;
 	double Hs;
 	char buf[BUFSIZ+1];
 	char *ptr, *comma, *eq;
@@ -381,17 +322,14 @@ static void set_timing_mode(int this_option_offset, struct cgpu_info *icarus)
 		buf[max] = '\0';
 	}
 
-	info->Hs = 0;
 	info->read_count = 0;
 
 	if (strcasecmp(buf, MODE_SHORT_STR) == 0) {
-		info->Hs = ICARUS_REV3_HASH_TIME;
 		info->read_count = ICARUS_READ_COUNT_TIMING;
 
 		info->timing_mode = MODE_SHORT;
 		info->do_icarus_timing = true;
 	} else if (strcasecmp(buf, MODE_LONG_STR) == 0) {
-		info->Hs = ICARUS_REV3_HASH_TIME;
 		info->read_count = ICARUS_READ_COUNT_TIMING;
 
 		info->timing_mode = MODE_LONG;
@@ -414,14 +352,19 @@ static void set_timing_mode(int this_option_offset, struct cgpu_info *icarus)
 	} else {
 		// Anything else in buf just uses DEFAULT mode
 
-		info->Hs = ICARUS_REV3_HASH_TIME;
 		info->fullnonce = info->Hs * (((double)0xffffffff) + 1);
 
 		if ((eq = strchr(buf, '=')) != NULL)
 			info->read_count = atoi(eq+1);
 
-		if (info->read_count < 1)
-			info->read_count = (int)(info->fullnonce * TIME_FACTOR) - 1;
+		if (icarus->api == &icarus_api) {
+			info->do_default_detection = 0x10;
+			if (info->read_count < 1)
+				info->read_count = ICARUS_READ_COUNT_TIMING;
+		} else {
+			if (info->read_count < 1)
+				info->read_count = (int)(info->fullnonce * TIME_FACTOR) - 1;
+		}
 
 		info->timing_mode = MODE_DEFAULT;
 		info->do_icarus_timing = false;
@@ -460,8 +403,12 @@ static uint32_t mask(int work_division)
 	return nonce_mask;
 }
 
-static void get_options(int this_option_offset, int *baud, int *work_division, int *fpga_count)
+static void get_options(int this_option_offset, struct ICARUS_INFO *info)
 {
+	int *baud = &info->baud;
+	int *work_division = &info->work_division;
+	int *fpga_count = &info->fpga_count;
+
 	char err_buf[BUFSIZ+1];
 	char buf[BUFSIZ+1];
 	char *ptr, *comma, *colon, *colon2;
@@ -491,10 +438,6 @@ static void get_options(int this_option_offset, int *baud, int *work_division, i
 		buf[max] = '\0';
 	}
 
-	*baud = ICARUS_IO_SPEED;
-	*work_division = 2;
-	*fpga_count = 2;
-
 	if (*buf) {
 		colon = strchr(buf, ':');
 		if (colon)
@@ -532,6 +475,11 @@ static void get_options(int this_option_offset, int *baud, int *work_division, i
 			}
 
 			if (colon2 && *colon2) {
+			  colon = strchr(colon2, ':');
+			  if (colon)
+					*(colon++) = '\0';
+
+			  if (*colon2) {
 				tmp = atoi(colon2);
 				if (tmp > 0 && tmp <= *work_division)
 					*fpga_count = tmp;
@@ -539,16 +487,26 @@ static void get_options(int this_option_offset, int *baud, int *work_division, i
 					sprintf(err_buf, "Invalid icarus-options for fpga_count (%s) must be >0 and <=work_division (%d)", colon2, *work_division);
 					quit(1, err_buf);
 				}
+			  }
+
+			  if (colon && *colon) {
+					colon2 = strchr(colon, '-') ?: "";
+					if (*colon2)
+						*(colon2++) = '\0';
+					if (strchr(colon, 'r'))
+						info->quirk_reopen = 2;
+					if (strchr(colon2, 'r'))
+						info->quirk_reopen = 0;
+			  }
 			}
 		}
 	}
 }
 
-static bool icarus_detect_one(const char *devpath)
+bool icarus_detect_custom(const char *devpath, struct device_api *api, struct ICARUS_INFO *info)
 {
 	int this_option_offset = ++option_offset;
 
-	struct ICARUS_INFO *info;
 	struct timeval tv_start, tv_finish;
 	int fd;
 
@@ -574,9 +532,11 @@ static bool icarus_detect_one(const char *devpath)
 	unsigned char ob_bin[64], nonce_bin[ICARUS_READ_SIZE];
 	char *nonce_hex;
 
-	int baud, work_division, fpga_count;
+	get_options(this_option_offset, info);
 
-	get_options(this_option_offset, &baud, &work_division, &fpga_count);
+	int baud = info->baud;
+	int work_division = info->work_division;
+	int fpga_count = info->fpga_count;
 
 	applog(LOG_DEBUG, "Icarus Detect: Attempting to open %s", devpath);
 
@@ -617,14 +577,19 @@ static bool icarus_detect_one(const char *devpath)
 	} else
 		return false;
 
+	if (serial_claim(devpath, api)) {
+		const char *claimedby = serial_claim(devpath, api)->dname;
+		applog(LOG_DEBUG, "Icarus device %s already claimed by other driver: %s", devpath, claimedby);
+		return false;
+	}
+
 	/* We have a real Icarus! */
 	struct cgpu_info *icarus;
 	icarus = calloc(1, sizeof(struct cgpu_info));
-	icarus->api = &icarus_api;
+	icarus->api = api;
 	icarus->device_path = strdup(devpath);
 	icarus->threads = 1;
 	add_cgpu(icarus);
-	icarus_info = realloc(icarus_info, sizeof(struct ICARUS_INFO *) * (total_devices + 1));
 
 	applog(LOG_INFO, "Found Icarus at %s, mark as %d",
 		devpath, icarus->device_id);
@@ -632,19 +597,8 @@ static bool icarus_detect_one(const char *devpath)
 	applog(LOG_DEBUG, "Icarus: Init: %d baud=%d work_division=%d fpga_count=%d",
 		icarus->device_id, baud, work_division, fpga_count);
 
-	// Since we are adding a new device on the end it needs to always be allocated
-	icarus_info[icarus->device_id] = (struct ICARUS_INFO *)malloc(sizeof(struct ICARUS_INFO));
-	if (unlikely(!(icarus_info[icarus->device_id])))
-		quit(1, "Failed to malloc ICARUS_INFO");
-
-	info = icarus_info[icarus->device_id];
-
-	// Initialise everything to zero for a new device
-	memset(info, 0, sizeof(struct ICARUS_INFO));
+	icarus->cgpu_data = info;
 
-	info->baud = baud;
-	info->work_division = work_division;
-	info->fpga_count = fpga_count;
 	info->nonce_mask = mask(work_division);
 
 	info->golden_hashes = (golden_nonce_val & info->nonce_mask) * fpga_count;
@@ -655,6 +609,25 @@ static bool icarus_detect_one(const char *devpath)
 	return true;
 }
 
+static bool icarus_detect_one(const char *devpath)
+{
+	struct ICARUS_INFO *info = calloc(1, sizeof(struct ICARUS_INFO));
+	if (unlikely(!info))
+		quit(1, "Failed to malloc ICARUS_INFO");
+
+	info->baud = ICARUS_IO_SPEED;
+	info->work_division = 2;
+	info->fpga_count = 2;
+	info->quirk_reopen = 1;
+	info->Hs = ICARUS_REV3_HASH_TIME;
+
+	if (!icarus_detect_custom(devpath, &icarus_api, info)) {
+		free(info);
+		return false;
+	}
+	return true;
+}
+
 static void icarus_detect()
 {
 	serial_detect(icarus_api.dname, icarus_detect_one);
@@ -671,10 +644,11 @@ struct icarus_state {
 static bool icarus_prepare(struct thr_info *thr)
 {
 	struct cgpu_info *icarus = thr->cgpu;
+	struct ICARUS_INFO *info = icarus->cgpu_data;
 
 	struct timeval now;
 
-	int fd = icarus_open2(icarus->device_path, icarus_info[icarus->device_id]->baud, true);
+	int fd = icarus_open2(icarus->device_path, info->baud, true);
 	if (unlikely(-1 == fd)) {
 		applog(LOG_ERR, "Failed to open Icarus on %s",
 		       icarus->device_path);
@@ -703,6 +677,21 @@ static bool icarus_prepare(struct thr_info *thr)
 	return true;
 }
 
+static bool icarus_reopen(struct cgpu_info *icarus, struct icarus_state *state, int *fdp)
+{
+	struct ICARUS_INFO *info = icarus->cgpu_data;
+
+	// Reopen the serial port to workaround a USB-host-chipset-specific issue with the Icarus's buggy USB-UART
+	icarus_close(icarus->device_fd);
+	*fdp = icarus->device_fd = icarus_open(icarus->device_path, info->baud);
+	if (unlikely(-1 == *fdp)) {
+		applog(LOG_ERR, "%s %u: Failed to reopen on %s", icarus->api->name, icarus->device_id, icarus->device_path);
+		state->firstrun = true;
+		return false;
+	}
+	return true;
+}
+
 static int64_t icarus_scanhash(struct thr_info *thr, struct work *work,
 				__maybe_unused int64_t max_nonce)
 {
@@ -742,7 +731,7 @@ static int64_t icarus_scanhash(struct thr_info *thr, struct work *work,
 
 	// Wait for the previous run's result
 	fd = icarus->device_fd;
-	info = icarus_info[icarus->device_id];
+	info = icarus->cgpu_data;
 
 	if (!state->firstrun) {
 		if (state->changework)
@@ -754,18 +743,28 @@ static int64_t icarus_scanhash(struct thr_info *thr, struct work *work,
 		{
 			/* Icarus will return 4 bytes (ICARUS_READ_SIZE) nonces or nothing */
 			lret = icarus_gets(nonce_bin, fd, &state->tv_workfinish, thr, info->read_count);
-			if (lret && thr->work_restart) {
+			if (lret) {
+				if (thr->work_restart) {
+
 				// The prepared work is invalid, and the current work is abandoned
 				// Go back to the main loop to get the next work, and stuff
 				// Returning to the main loop will clear work_restart, so use a flag...
 				state->changework = true;
 				return 0;
+
+				}
+				if (info->quirk_reopen == 1 && !icarus_reopen(icarus, state, &fd))
+					return 0;
 			}
+			
 		}
 
 		tv_start = state->tv_workstart;
 		timersub(&state->tv_workfinish, &tv_start, &elapsed);
 	}
+	else
+	if (fd == -1 && !icarus_reopen(icarus, state, &fd))
+		return 0;
 
 #ifndef WIN32
 	tcflush(fd, TCOFLUSH);
@@ -788,15 +787,8 @@ static int64_t icarus_scanhash(struct thr_info *thr, struct work *work,
 		}
 	}
 
-	// Reopen the serial port to workaround a USB-host-chipset-specific issue with the Icarus's buggy USB-UART
-	icarus_close(fd);
-	fd = icarus_open(icarus->device_path, icarus_info[icarus->device_id]->baud);
-	if (unlikely(-1 == fd)) {
-		applog(LOG_ERR, "Failed to reopen Icarus on %s",
-		       icarus->device_path);
+	if (info->quirk_reopen == 2 && !icarus_reopen(icarus, state, &fd))
 		return 0;
-	}
-	icarus->device_fd = fd;
 
 	work->blk.nonce = 0xffffffff;
 
@@ -845,6 +837,36 @@ static int64_t icarus_scanhash(struct thr_info *thr, struct work *work,
 				icarus->device_id, nonce, hash_count, elapsed.tv_sec, elapsed.tv_usec);
 	}
 
+	if (info->do_default_detection && elapsed.tv_sec >= DEFAULT_DETECT_THRESHOLD) {
+		int MHs = (double)hash_count / ((double)elapsed.tv_sec * 1e6 + (double)elapsed.tv_usec);
+		--info->do_default_detection;
+		applog(LOG_DEBUG, "%s %u: Autodetect device speed: %d MH/s", icarus->api->name, icarus->device_id, MHs);
+		if (MHs <= 370 || MHs > 420) {
+			// Not a real Icarus: enable short timing
+			applog(LOG_WARNING, "%s %u: Seems too %s to be an Icarus; calibrating with short timing", icarus->api->name, icarus->device_id, MHs>380?"fast":"slow");
+			info->timing_mode = MODE_SHORT;
+			info->do_icarus_timing = true;
+			info->do_default_detection = 0;
+		}
+		else
+		if (MHs <= 380) {
+			// Real Icarus?
+			if (!info->do_default_detection) {
+				applog(LOG_DEBUG, "%s %u: Seems to be a real Icarus", icarus->api->name, icarus->device_id);
+				info->read_count = (int)(info->fullnonce * TIME_FACTOR) - 1;
+			}
+		}
+		else
+		if (MHs <= 420) {
+			// Enterpoint Cairnsmore1
+			const char *old_name = icarus->api->name;
+			int old_devid = icarus->device_id;
+			convert_icarus_to_cairnsmore(icarus);
+			info->do_default_detection = 0;
+			applog(LOG_WARNING, "%s %u: Detected Cairnsmore1 device, upgrading driver to %s %u", old_name, old_devid, icarus->api->name, icarus->device_id);
+		}
+	}
+
 	// ignore possible end condition values
 	if (info->do_icarus_timing
 	&&  ((nonce & info->nonce_mask) > END_CONDITION)
@@ -949,7 +971,7 @@ static int64_t icarus_scanhash(struct thr_info *thr, struct work *work,
 static struct api_data *icarus_api_stats(struct cgpu_info *cgpu)
 {
 	struct api_data *root = NULL;
-	struct ICARUS_INFO *info = icarus_info[cgpu->device_id];
+	struct ICARUS_INFO *info = cgpu->cgpu_data;
 
 	// Warning, access to these is not locked - but we don't really
 	// care since hashing performance is way more important than

+ 167 - 3
fpgautils.c

@@ -11,6 +11,7 @@
 #include "config.h"
 
 #include <stdint.h>
+#include <stdlib.h>
 #include <sys/types.h>
 #include <dirent.h>
 #include <string.h>
@@ -24,10 +25,26 @@
 #ifndef O_CLOEXEC
 #define O_CLOEXEC 0
 #endif
-#else
+#else  /* WIN32 */
 #include <windows.h>
 #include <io.h>
-#endif
+
+#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,
+};
+#endif  /* WIN32 */
 
 #ifdef HAVE_LIBUDEV
 #include <libudev.h>
@@ -108,13 +125,98 @@ serial_autodetect_devserial(detectone_func_t detectone, const char*prodname)
 #endif
 }
 
+#ifdef WIN32
+#define LOAD_SYM(sym)  do { \
+	if (!(sym = dlsym(dll, #sym))) {  \
+		applog(LOG_DEBUG, "Failed to load " #sym ", not using FTDI autodetect");  \
+		goto out;  \
+	}  \
+} while(0)
+
+int serial_autodetect_ftdi(detectone_func_t detectone, const char *needle, const char *needle2)
+{
+	char devpath[] = "\\\\.\\COMnnnnn";
+	char *devpathnum = &devpath[7];
+	char **bufptrs;
+	char *buf;
+	int found = 0;
+	int i;
+
+	FT_STATUS ftStatus;
+	DWORD numDevs;
+	HMODULE dll = LoadLibrary("FTD2XX.DLL");
+	if (!dll) {
+		applog(LOG_DEBUG, "FTD2XX.DLL failed to load, not using FTDI 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 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 autodetect");
+		goto out;
+	}
+	
+	for (i = numDevs; i > 0; ) {
+		--i;
+		bufptrs[i][64] = '\0';
+		
+		if (!(strstr(bufptrs[i], needle) && (!needle2 || strstr(bufptrs[i], needle2))))
+			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 (detectone(devpath))
+			++found;
+	}
+
+out:
+	dlclose(dll);
+	return found;
+}
+#else
+int serial_autodetect_ftdi(__maybe_unused detectone_func_t detectone, __maybe_unused const char *needle, __maybe_unused const char *needle2)
+{
+	return 0;
+}
+#endif
+
+struct device_api *serial_claim(const char *devpath, struct device_api *api);
+
 int
-_serial_detect(const char*dname, detectone_func_t detectone, autoscan_func_t autoscan, bool forceauto)
+_serial_detect(const char*dname, detectone_func_t detectone, autoscan_func_t autoscan, int flags)
 {
 	struct string_elist *iter, *tmp;
 	const char*s, *p;
 	bool inhibitauto = false;
 	char found = 0;
+	bool forceauto = flags & 1;
+	bool hasname;
 	size_t dnamel = strlen(dname);
 
 	list_for_each_entry_safe(iter, tmp, &scan_devices, list) {
@@ -124,16 +226,28 @@ _serial_detect(const char*dname, detectone_func_t detectone, autoscan_func_t aut
 			if (plen != dnamel || strncasecmp(s, dname, plen))
 				continue;
 			s = p + 1;
+			hasname = true;
 		}
+		else
+			hasname = false;
 		if (!strcmp(s, "auto"))
 			forceauto = true;
 		else
 		if (!strcmp(s, "noauto"))
 			inhibitauto = true;
 		else
+		if ((flags & 2) && !hasname)
+			continue;
+		else
 		if (!detectone)
 		{}  // do nothing
 		else
+		if (serial_claim(s, NULL))
+		{
+			applog(LOG_DEBUG, "%s is already claimed... skipping probes", s);
+			string_elist_del(iter);
+		}
+		else
 		if (detectone(s)) {
 			string_elist_del(iter);
 			inhibitauto = true;
@@ -147,6 +261,56 @@ _serial_detect(const char*dname, detectone_func_t detectone, autoscan_func_t aut
 	return found;
 }
 
+#ifndef WIN32
+typedef dev_t my_dev_t;
+#else
+typedef int my_dev_t;
+#endif
+
+struct _device_claim {
+	struct device_api *api;
+	my_dev_t dev;
+	UT_hash_handle hh;
+};
+
+struct device_api *serial_claim(const char *devpath, struct device_api *api)
+{
+	static struct _device_claim *claims = NULL;
+	struct _device_claim *c;
+	my_dev_t dev;
+
+#ifndef WIN32
+	{
+		struct stat my_stat;
+		if (stat(devpath, &my_stat))
+			return NULL;
+		dev = my_stat.st_rdev;
+	}
+#else
+	{
+		char *p = strstr(devpath, "COM"), *p2;
+		if (!p)
+			return NULL;
+		dev = strtol(&p[3], &p2, 10);
+		if (p2 == p)
+			return NULL;
+	}
+#endif
+
+	HASH_FIND(hh, claims, &dev, sizeof(dev), c);
+	if (c)
+		return c->api;
+
+	if (!api)
+		return NULL;
+
+	c = malloc(sizeof(*c));
+	c->dev = dev;
+	c->api = api;
+	HASH_ADD(hh, claims, dev, sizeof(dev), c);
+	return NULL;
+}
+
 // This code is purely for debugging but is very useful for that
 // It also took quite a bit of effort so I left it in
 // #define TERMIOS_DEBUG 1

+ 10 - 5
fpgautils.h

@@ -17,17 +17,22 @@
 typedef bool(*detectone_func_t)(const char*);
 typedef int(*autoscan_func_t)();
 
-extern int _serial_detect(const char*dname, detectone_func_t, autoscan_func_t, bool force_autoscan);
+extern int _serial_detect(const char*dname, detectone_func_t, autoscan_func_t, int flags);
 #define serial_detect_fauto(dname, detectone, autoscan)  \
-	_serial_detect(dname, detectone, autoscan, true)
+	_serial_detect(dname, detectone, autoscan, 1)
 #define serial_detect_auto(dname, detectone, autoscan)  \
-	_serial_detect(dname, detectone, autoscan, false)
+	_serial_detect(dname, detectone, autoscan, 0)
+#define serial_detect_auto_byname(dname, detectone, autoscan)  \
+	_serial_detect(dname, detectone, autoscan, 2)
 #define serial_detect(dname, detectone)  \
-	_serial_detect(dname, detectone,     NULL, false)
+	_serial_detect(dname, detectone,     NULL, 0)
 #define noserial_detect(dname, autoscan)  \
-	_serial_detect(dname, NULL     , autoscan, false)
+	_serial_detect(dname, NULL     , autoscan, 0)
 extern int serial_autodetect_devserial(detectone_func_t, const char*prodname);
 extern int serial_autodetect_udev     (detectone_func_t, const char*prodname);
+extern int serial_autodetect_ftdi     (detectone_func_t, const char*needle, const char *needle2);
+
+extern struct device_api *serial_claim(const char *devpath, struct device_api *);
 
 extern int serial_open(const char*devpath, unsigned long baud, uint8_t timeout, bool purge);
 extern ssize_t _serial_read(int fd, char *buf, size_t buflen, char*eol);

+ 76 - 0
icarus-common.h

@@ -0,0 +1,76 @@
+#ifndef ICARUS_COMMON_H
+#define ICARUS_COMMON_H
+
+#include <stdbool.h>
+#include <stdint.h>
+#include <sys/time.h>
+
+#include "miner.h"
+
+// Fraction of a second, USB timeout is measured in
+// i.e. 10 means 1/10 of a second
+// Right now, it MUST be 10 due to other assumptions.
+#define TIME_FACTOR 10
+// It's 10 per second, thus value = 10/TIME_FACTOR =
+#define ICARUS_READ_FAULT_DECISECONDS 1
+
+#define NANOSEC 1000000000.0
+
+
+// Store the last INFO_HISTORY data sets
+// [0] = current data, not yet ready to be included as an estimate
+// Each new data set throws the last old set off the end thus
+// keeping a ongoing average of recent data
+#define INFO_HISTORY 10
+
+struct ICARUS_HISTORY {
+	struct timeval finish;
+	double sumXiTi;
+	double sumXi;
+	double sumTi;
+	double sumXi2;
+	uint32_t values;
+	uint32_t hash_count_min;
+	uint32_t hash_count_max;
+};
+
+enum timing_mode { MODE_DEFAULT, MODE_SHORT, MODE_LONG, MODE_VALUE };
+
+struct ICARUS_INFO {
+	// time to calculate the golden_ob
+	uint64_t golden_hashes;
+	struct timeval golden_tv;
+
+	struct ICARUS_HISTORY history[INFO_HISTORY+1];
+	uint32_t min_data_count;
+
+	// seconds per Hash
+	double Hs;
+	int read_count;
+
+	enum timing_mode timing_mode;
+	bool do_icarus_timing;
+	int do_default_detection;
+
+	double fullnonce;
+	int count;
+	double W;
+	uint32_t values;
+	uint64_t hash_count_range;
+
+	// Determine the cost of history processing
+	// (which will only affect W)
+	uint64_t history_count;
+	struct timeval history_time;
+
+	// icarus-options
+	int baud;
+	int work_division;
+	int fpga_count;
+	uint32_t nonce_mask;
+	bool quirk_reopen;
+};
+
+bool icarus_detect_custom(const char *devpath, struct device_api *, struct ICARUS_INFO *);
+
+#endif

+ 10 - 1
miner.c

@@ -5768,6 +5768,7 @@ extern struct device_api bitforce_api;
 #endif
 
 #ifdef USE_ICARUS
+extern struct device_api cairnsmore_api;
 extern struct device_api icarus_api;
 #endif
 
@@ -5803,7 +5804,7 @@ struct _cgpu_devid_counter {
 	UT_hash_handle hh;
 };
 
-bool add_cgpu(struct cgpu_info*cgpu)
+void renumber_cgpu(struct cgpu_info *cgpu)
 {
 	static struct _cgpu_devid_counter *devids = NULL;
 	struct _cgpu_devid_counter *d;
@@ -5817,6 +5818,11 @@ bool add_cgpu(struct cgpu_info*cgpu)
 		cgpu->device_id = d->lastid = 0;
 		HASH_ADD_STR(devids, name, d);
 	}
+}
+
+bool add_cgpu(struct cgpu_info*cgpu)
+{
+	renumber_cgpu(cgpu);
 	devices = realloc(devices, sizeof(struct cgpu_info *) * (total_devices + 2));
 	devices[total_devices++] = cgpu;
 	return true;
@@ -6033,7 +6039,10 @@ int main(int argc, char *argv[])
 
 #ifdef USE_ICARUS
 	if (!opt_scrypt)
+	{
+		cairnsmore_api.api_detect();
 		icarus_api.api_detect();
+	}
 #endif
 
 #ifdef USE_BITFORCE

+ 2 - 0
miner.h

@@ -372,6 +372,7 @@ struct cgpu_info {
 	bool nonce_range;
 	bool polling;
 #endif
+	void *cgpu_data;
 	pthread_mutex_t		device_mutex;
 
 	enum dev_enable deven;
@@ -453,6 +454,7 @@ struct cgpu_info {
 	struct cgminer_stats cgminer_stats;
 };
 
+extern void renumber_cgpu(struct cgpu_info *);
 extern bool add_cgpu(struct cgpu_info*);
 
 struct thread_q {