Browse Source

Merge tag 'bfgminer-3.1.2' into bigpic

BFGMiner version 3.1.2

Conflicts:
	miner.c
Luke Dashjr 12 years ago
parent
commit
e009320657
39 changed files with 1878 additions and 1130 deletions
  1. 5 0
      Makefile.am
  2. 128 0
      NEWS
  3. 36 37
      README
  4. 11 0
      README.RPC
  5. 7 9
      adl.c
  6. 33 76
      api.c
  7. 3 6
      compat.h
  8. 15 3
      configure.ac
  9. 13 0
      debian/changelog
  10. 1 1
      debian/control
  11. 19 11
      deviceapi.c
  12. 8 33
      driver-avalon.c
  13. 4 27
      driver-bf1.c
  14. 114 71
      driver-bitforce.c
  15. 0 2
      driver-cairnsmore.c
  16. 20 5
      driver-cpu.c
  17. 84 0
      driver-erupter.c
  18. 12 21
      driver-icarus.c
  19. 14 69
      driver-modminer.c
  20. 148 217
      driver-opencl.c
  21. 14 54
      driver-x6500.c
  22. 7 16
      driver-ztex.c
  23. 1 2
      findnonce.c
  24. 289 125
      fpgautils.c
  25. 25 1
      fpgautils.h
  26. 15 5
      ft232r.c
  27. 2 0
      icarus-common.h
  28. 37 0
      iospeeds.h
  29. 2 2
      jtag.c
  30. 35 11
      libztex.c
  31. 2 0
      libztex.h
  32. 6 3
      logging.h
  33. 373 154
      miner.c
  34. 32 23
      miner.h
  35. 9 1
      ocl.c
  36. 1 1
      openwrt/bfgminer/Makefile
  37. 15 15
      usbtest.py
  38. 242 126
      util.c
  39. 96 3
      util.h

+ 5 - 0
Makefile.am

@@ -156,6 +156,10 @@ endif # HAS_CPUMINE
 
 if NEED_FPGAUTILS
 bfgminer_SOURCES += fpgautils.c fpgautils.h
+if HAVE_WINDOWS
+else
+bfgminer_SOURCES += iospeeds.h
+endif
 endif
 
 if NEED_DYNCLOCK
@@ -183,6 +187,7 @@ endif
 if HAS_ICARUS
 bfgminer_SOURCES += driver-icarus.c icarus-common.h
 bfgminer_SOURCES += driver-cairnsmore.c
+bfgminer_SOURCES += driver-erupter.c
 endif
 
 if HAS_AVALON

+ 128 - 0
NEWS

@@ -1,3 +1,131 @@
+BFGMiner Version 3.1.2 - July 8, 2013
+- When not compiling with optimizations, initialize unused nonce2 space to avoid
+warnings from memory checking tools
+- TUI Manage devices: Support PgUp/PgDn keys to skip over processors within the
+same device
+- Bugfix: bitforce: Prefer 2nd temperature if higher than 1st
+- When displaying device summary statlines, use the highest temperature reported
+by any processor
+- Stratum: Fix nonce2 sizes greater than 4 and (on big-endian) smaller than 4
+- bitforce: Manage TUI: Display both temperatures (if two), and enable changing
+fan speed
+- opencl: Add fan speed to Manage device TUI now that it's been removed from
+statline
+- DevAPI: Remove old statline APIs entirely, and add new override_statline_temp
+(used by modminer/x6500 for upload %)
+- README: Update statlines
+- TUI: Replace DevAPI statline_before with a predefined temperature column to
+free up statline space
+- Refactor and simplify bin2hex to speed up and avoid unnecessary heap use
+- stratum: Refactor work generation to do hex2bin conversions once, rather than
+every single header generated
+- Implement bytes_t for generic binary data storage (including smart realloc-
+based resize)
+- Bugfix: fpgautils: Only try to change baud rate when requested
+- x6500: Provide manuf/product/serial to cgpu interface
+- ztex: Provide manuf/product/serial to cgpu interface
+- erupter: Use baud 115200 by default
+- List valid baud rates once in iospeeds.h and standardize conversions
+- TUI: Display device manufacturer/product/serial in Manage device screen, when
+available
+- DevAPI: Store manufacturer/product/serial for each device
+- fpgautils: detectone_meta_info to provide metainformation (manufacturer,
+product, serial) on devices to detectone functions
+- Bugfix: fpgautils: Close product string file from sysfs (autodetect)
+- erupter: New icarus-based driver to handle autodetection of Block Erupter
+devices
+- Add --log-file option which redirects stderr to a file, but valid anywhere in
+the commandline or config file
+- Detect staged work underruns and increase queue to avoid them
+- Rewrite hex2bin to perform much faster (reduces minirig CPU usage by more than
+half!)
+- README: Add condensed list of dependencies
+- Enable "maintainer mode" by default
+- Bugfix: opencl: TUI manage: "Change settings" must not be compiled in with
+no-ADL builds
+- Bugfix: Detect whether the linker accepts -zorigin before attempting to use it
+- opencl: ADL: ADL_Adapter_ID_Get fails with newer drivers, so tolerate its
+failure best we can
+- opencl: Don't try to use BFI_INT patching with APP-SDK newer than 1084
+(Catalyst 13.1), since it doesn't work
+- fpgautils: Elaborate that bitstream open failures are probably due to missing
+bitstream package
+- fpgautils: s/firmware/bitstream/
+- Bugfix: Cleanup handling of complete device/driver failure
+- Deprecate -C (enable CPU) and -G (disable GPU) options, now that -S
+drv:[no]auto can be used for the same purposes
+- Bugfix: Since at least one of unix (or __APPLE__) or WIN32 is required by
+util.h, make sure unix is defined if WIN32 is not
+- Bugfix: Set ELF rpath for bundled libblkmaker to use $ORIGIN so it can be run
+from other directories
+- Bugfix: Cleanup needs to happen before printing the final quit message, or it
+gets lost in TUI mode
+- Bugfix: fpgautils: Initialize my_dev_t instances with null bytes, to ensure
+random unused data cannot influence hash keys
+- opencl: ManageTUI: Clear log cleanly for changing settings
+- Remove "GPU management" TUI entirely
+- opencl: Use new "Manage device" interface to do everything "GPU management"
+used to be used for
+- DevAPI: Add interface for drivers to define custom "Manage device" options
+- DevAPI: New function called to display additional processor information for
+"Manage devices"
+- TUI: Add enable/disable commands to device management
+- TUI: Implement beginnings of generic device management interface
+- Bugfix: avalon: Fix LIFE_INIT2 setting
+- Add LIFE_INIT2 status (safe to call functions, but not mining yet) for devices
+that want to report initialization status in their statline
+- Bugfix: modminer: Only program once for --force-dev-init
+- Bugfix: x6500: Only program once for --force-dev-init
+- fpgautils: Workaround and document Xcode clang bug
+- Bugfix: avalon: Correctly claim serial port
+- Bugfix: -S all: Mac OS X needs to probe /dev/cu.*, not just /dev/cu.usb*
+- cpu & opencl: Refuse to detect more than once
+- cpu & opencl: Respect scan-serial auto/noauto instructions
+- ft232r & libztex: Skip probe of claimed devices
+- fpgautils: Check for devices being claimed before calling detectone from autodetectors
+- x6500 & ztex: Claim USB devices
+- fpgautils: Implement bfg_claim_usb for claiming devices by USB bus number and
+address
+- fpgautils: Replace serial_claim with bfg_claim_serial using a more cleanly
+extensible interface and implementation
+- fpgautils: serial_claim: Include a bus enum in hash key
+- Add serial port claiming logic to avalon, bitforce, and modminer drivers
+- RPC: "devscan" command to probe for new devices
+- New (internal) scan_serial function to probe for new devices at runtime
+- Split out per-cgpu temperature configuration code to load_temp_config_cgpu
+- DevAPI: Modify add_cgpu to use temporary devices_new array, so detection can
+be done without touching live variables
+- Move more cgpu initialization to allocate_cgpu
+- Move devtype default assignment to allocate_cgpu
+- Move cgpu startup routine to new start_cgpu function
+- Move cgpu_info allocation to new allocate_cgpu function
+- Move *.drv_detect calls to a new drv_detect_all function
+- DevAPI: add_cgpu: There is no need to hold mutexes while creating devices
+- Bugfix: cpu: Update device "kernel name" with auto-selected algorithm
+- usbtest: Improve portability to at least 2.7 and 3.2
+- usbtest: Avoid messing up the display by escaping weird bytes via repr()
+- usbtest: Skip last 2 optional parameters, since we use the defaults and they
+are not in older versions of pyserial
+- Bugfix: bitforce: ZOX limits results to 16 results per call, so repeat ZOX
+until there are fewer
+- Bugfix: Initialization for bfgtls needs to be done in each thread
+- Bugfix: stratum: Be patient with stratum lines that come in slower than we can
+process them
+- Use bfg_strerror in locations previously just logging raw error numbers
+- Bugfix: stratum: Log WSAGetLastError() for error number on recv failures on
+Windows
+- Use bfg_strerror where it is already needed (for thread-safety)
+- New thread-safe bfg_strerror function to portably stringify error codes
+- Bugfix: bitforce_queue: Initialize buf2 so errors don't cause the work queue
+to flush
+- TUI: Display percentage invalid of found nonces with hw errors
+- Bugfix: modminer & x6500: Increment *->diff1 for all bad nonces
+- percentf2 that takes t as precalculated total
+- Keep track of bad nonces independently from generic hw errors
+- inc_hw_errors: Resolve cgpu outside of mutex
+- Use inc_hw_errors function at every site which increases hw_errors
+
+
 BFGMiner Version 3.1.1 - June 22, 2013
 - stratum: Deliver exact socket-error within the debug error message
 - Don't install docs for (compile-time) disabled functionality

+ 36 - 37
README

@@ -78,6 +78,10 @@ all pools that don't specify their own proxy setting like above
 ---
 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
+
 Dependencies:
 	autoconf             http://www.gnu.org/software/autoconf/
 	automake             http://www.gnu.org/software/automake/
@@ -178,6 +182,7 @@ Options for both config file and command line:
 --kernel-path|-K <arg> Specify a path to where bitstream and kernel files are (default: "/usr/local/bin")
 --load-balance      Change multipool strategy from failover to efficiency based balance
 --log|-l <arg>      Interval in seconds between log output (default: 5)
+--log-file|-L <arg> Append log file for output messages
 --log-microseconds  Include microseconds in log output
 --monitor|-m <arg>  Use custom pipe cmd for output messages
 --net-delay         Impose small delays in networking to not overload slow routers
@@ -230,7 +235,6 @@ GPU only options:
 
 --auto-fan          Automatically adjust all GPU fan speeds to maintain a target temperature
 --auto-gpu          Automatically adjust all GPU engine clock speeds to maintain a target temperature
---disable-gpu|-G    Disable GPU mining even if suitable devices exist
 --gpu-threads|-g <arg> Number of threads per GPU (1 - 10) (default: 2)
 --gpu-dyninterval <arg> Set the refresh interval in ms for GPUs using dynamic intensity (default: 7)
 --gpu-engine <arg>  GPU engine (over)clock range in MHz - one value, range and/or comma separated list (e.g. 850-900,900,750-850)
@@ -250,6 +254,8 @@ GPU only options:
 --vectors|-v <arg>  Override detected optimal vector (1, 2 or 4) - one value or comma separated list
 --worksize|-w <arg> Override detected optimal worksize - one value or comma separated list
 
+GPU mining can be disabled by specifying the -S opencl:noauto option.
+
 See README.GPU for more information regarding GPU mining.
 
 scrypt only options:
@@ -313,7 +319,6 @@ CPU only options (not included in binaries):
         sse4_64         SSE4.1 64 bit implementation for x86_64 machines
         altivec_4way    Altivec implementation for PowerPC G4 and G5 machines
 --cpu-threads|-t <arg> Number of miner CPU threads (default: 4)
---enable-cpu|-C     Enable CPU mining with other mining (default: no CPU mining if other devices exist)
 
 
 ---
@@ -322,7 +327,18 @@ WHILE RUNNING:
 
 The following options are available while running with a single keypress:
 
-[P]ool management [G]PU management [S]ettings [D]isplay options [Q]uit
+[M]anage devices [P]ool management [S]ettings [D]isplay options [Q]uit
+
+M gives you something like:
+
+Select processor to manage using up/down arrow keys
+ BFL 0a: 78.0C |  3.64/ 3.70/ 2.91Gh/s | A:46 R:0+0(none) HW:  2/none
+  BitFORCE SHA256 SC from Butterfly Labs
+Serial: FTWN6T67
+
+[D]isable
+Or press Enter when done
+
 
 P gives you:
 
@@ -359,24 +375,6 @@ co[M]pact: off
 Q quits the application.
 
 
-G gives you something like:
-
-GPU 0: [124.2 / 191.3 Mh/s] [A:77  R:33+0( 42%)  HW:0]
-Temp: 67.0 C
-Fan Speed: 35% (2500 RPM)
-Engine Clock: 960 MHz
-Memory Clock: 480 MHz
-Vddc: 1.200 V
-Activity: 93%
-Powertune: 0%
-Last initialised: [2011-09-06 12:03:56]
-Thread 0: 62.4 Mh/s Enabled ALIVE
-Thread 1: 60.2 Mh/s Enabled ALIVE
-
-[E]nable [D]isable [R]estart GPU [C]hange settings
-Or press any other key to continue
-
-
 The running log shows output like this:
 
  [2013-02-13 00:26:30] Accepted 1758e8df BFL 0  pool 0 Diff 10/1
@@ -395,7 +393,7 @@ dedicated to this program,
 	https://bitcointalk.org/?topic=168174
 
 The output line shows the following:
- 5s:1713.6 avg:1707.8 u:1710.2 Mh/s | A:729 R:8+0(.01%) HW:0
+ 5s:1713.6 avg:1707.8 u:1710.2 Mh/s | A:729 R:8+0(.01%) HW:0/.81%
 
 Each column is as follows:
 5s:  A 5 second exponentially decaying average hash rate
@@ -404,20 +402,7 @@ u:   An all time average hash rate based on actual accepted shares
 A:   The number of Accepted shares
 R:   The number of Rejected shares, stale shares discarded (never submitted),
      and the percentage these are of total found.
-HW:  The number of HardWare errors
-
- GPU 1: 73.5C 2551RPM | 427.3/443.0/442.1Mh/s | A:8 R:0+0(none) HW:0 U:4.39/m
-
-Each column is as follows:
-Temperature (if supported)
-Fanspeed (if supported)
-A 5 second exponentially decaying average hash rate
-An all time average hash rate
-An all time average hash rate based on actual accepted shares
-The number of accepted shares
-The number of rejected shares (and percentage of total submitted)
-The number of hardware erorrs
-The utility defines as the number of shares / minute
+HW:  The number of HardWare errors, and percentage invalid of nonces returned
 
 The BFGMiner status line shows:
  ST: 1  GF: 1  NB: 1  AS: 0  RF: 1  E: 2.42  U:22.53/m  BS:2.71k
@@ -439,6 +424,18 @@ This shows a short stretch of the current block, the next block's height and
 difficulty (including the network hashrate that difficulty represents), and when
 the search for the new block started.
 
+Each device shows:
+ BFL 2: 74.0C | 51.97/58.90/57.17Gh/s | A:847 R:15+0(.54%) HW:496/.91%
+
+Column are as follows:
+Temperature (if supported)
+5 second exponentially decaying average hash rate
+An all time average hash rate
+An all time average hash rate based on actual accepted shares
+The number of accepted shares
+The number of rejected plus discarded shares (and percentage of total submitted)
+The number of hardware errors and percentage of nonces invalid
+
 
 ---
 MULTIPOOL
@@ -641,7 +638,9 @@ Q: What happened to CPU mining?
 A: Being increasingly irrelevant for most users, and a maintenance issue, it is
 no longer under active development and will not be supported unless someone
 steps up to help maintain it. No binary builds supporting CPU mining will be
-released but CPU mining can be built into BFGMiner when it is compiled.
+released for Windows but CPU mining can be built into BFGMiner when it is
+compiled. For builds which do support CPU mining, it is still disabled by
+default, and must be enabled using the -S cpu:auto option.
 
 Q: GUI version?
 A: No. The RPC interface makes it possible for someone else to write one

+ 11 - 0
README.RPC

@@ -145,6 +145,10 @@ The list of requests - a (*) means it requires privileged access - and replies a
                               Will not report PGAs if PGA mining is disabled
                               Will not report CPUs if CPU mining is disabled
 
+ devscan|info  DEVS           Probes for a device specified by info, which is
+                              the same format as the --scan-serial command line
+                              option
+
  devdetail     DEVS           Each available device with their fixed details
                               e.g. GPU=0,Driver=opencl,Kernel=diablo,Model=...|
 
@@ -410,6 +414,13 @@ api-example.py - a Python script to access the API
 Feature Changelog for external applications using the API:
 
 
+API V1.25.1 (BFGMiner v3.1.2)
+
+Added API commands:
+ 'devscan'
+
+----------
+
 API V1.25 (BFGMiner v3.0.1)
 
 Modified API commands:

+ 7 - 9
adl.c

@@ -251,12 +251,16 @@ void init_adl(int nDevs)
 			applog(LOG_INFO, "Failed to ADL_Adapter_ID_Get. Error %d", result);
 			if (result == -10)
 				applog(LOG_INFO, "This error says the device is not enabled");
-			continue;
 		}
-
+		else
 		/* Each adapter may have multiple entries */
 		if (lpAdapterID == last_adapter)
 			continue;
+		else
+		if (!lpAdapterID)
+			applog(LOG_INFO, "Adapter returns ID 0 meaning not AMD. Card order might be confused");
+		else
+			last_adapter = lpAdapterID;
 
 		applog(LOG_DEBUG, "GPU %d "
 		       "iAdapterIndex %d "
@@ -289,12 +293,6 @@ void init_adl(int nDevs)
 			applog(LOG_ERR, "Use the gpu map feature to reliably map OpenCL to ADL");
 			devs_match = false;
 		}
-		last_adapter = lpAdapterID;
-
-		if (!lpAdapterID) {
-			applog(LOG_INFO, "Adapter returns ID 0 meaning not AMD. Card order might be confused");
-			continue;
-		}
 	}
 
 	if (devices < nDevs) {
@@ -367,7 +365,7 @@ void init_adl(int nDevs)
 		result = ADL_Adapter_ID_Get(iAdapterIndex, &lpAdapterID);
 		if (result != ADL_OK) {
 			applog(LOG_INFO, "Failed to ADL_Adapter_ID_Get. Error %d", result);
-			continue;
+			lpAdapterID = -1;
 		}
 
 		if (gpus[gpu].deven == DEV_DISABLED) {

+ 33 - 76
api.c

@@ -48,81 +48,6 @@
 // However lots of PGA's may mean more
 #define QUEUE	100
 
-#if defined WIN32
-static char WSAbuf[1024];
-
-struct WSAERRORS {
-	int id;
-	char *code;
-} WSAErrors[] = {
-	{ 0,			"No error" },
-	{ WSAEINTR,		"Interrupted system call" },
-	{ WSAEBADF,		"Bad file number" },
-	{ WSAEACCES,		"Permission denied" },
-	{ WSAEFAULT,		"Bad address" },
-	{ WSAEINVAL,		"Invalid argument" },
-	{ WSAEMFILE,		"Too many open sockets" },
-	{ WSAEWOULDBLOCK,	"Operation would block" },
-	{ WSAEINPROGRESS,	"Operation now in progress" },
-	{ WSAEALREADY,		"Operation already in progress" },
-	{ WSAENOTSOCK,		"Socket operation on non-socket" },
-	{ WSAEDESTADDRREQ,	"Destination address required" },
-	{ WSAEMSGSIZE,		"Message too long" },
-	{ WSAEPROTOTYPE,	"Protocol wrong type for socket" },
-	{ WSAENOPROTOOPT,	"Bad protocol option" },
-	{ WSAEPROTONOSUPPORT,	"Protocol not supported" },
-	{ WSAESOCKTNOSUPPORT,	"Socket type not supported" },
-	{ WSAEOPNOTSUPP,	"Operation not supported on socket" },
-	{ WSAEPFNOSUPPORT,	"Protocol family not supported" },
-	{ WSAEAFNOSUPPORT,	"Address family not supported" },
-	{ WSAEADDRINUSE,	"Address already in use" },
-	{ WSAEADDRNOTAVAIL,	"Can't assign requested address" },
-	{ WSAENETDOWN,		"Network is down" },
-	{ WSAENETUNREACH,	"Network is unreachable" },
-	{ WSAENETRESET,		"Net connection reset" },
-	{ WSAECONNABORTED,	"Software caused connection abort" },
-	{ WSAECONNRESET,	"Connection reset by peer" },
-	{ WSAENOBUFS,		"No buffer space available" },
-	{ WSAEISCONN,		"Socket is already connected" },
-	{ WSAENOTCONN,		"Socket is not connected" },
-	{ WSAESHUTDOWN,		"Can't send after socket shutdown" },
-	{ WSAETOOMANYREFS,	"Too many references, can't splice" },
-	{ WSAETIMEDOUT,		"Connection timed out" },
-	{ WSAECONNREFUSED,	"Connection refused" },
-	{ WSAELOOP,		"Too many levels of symbolic links" },
-	{ WSAENAMETOOLONG,	"File name too long" },
-	{ WSAEHOSTDOWN,		"Host is down" },
-	{ WSAEHOSTUNREACH,	"No route to host" },
-	{ WSAENOTEMPTY,		"Directory not empty" },
-	{ WSAEPROCLIM,		"Too many processes" },
-	{ WSAEUSERS,		"Too many users" },
-	{ WSAEDQUOT,		"Disc quota exceeded" },
-	{ WSAESTALE,		"Stale NFS file handle" },
-	{ WSAEREMOTE,		"Too many levels of remote in path" },
-	{ WSASYSNOTREADY,	"Network system is unavailable" },
-	{ WSAVERNOTSUPPORTED,	"Winsock version out of range" },
-	{ WSANOTINITIALISED,	"WSAStartup not yet called" },
-	{ WSAEDISCON,		"Graceful shutdown in progress" },
-	{ WSAHOST_NOT_FOUND,	"Host not found" },
-	{ WSANO_DATA,		"No host data of that type was found" },
-	{ -1,			"Unknown error code" }
-};
-
-char *WSAErrorMsg(void) {
-	int i;
-	int id = WSAGetLastError();
-
-	/* Assume none of them are actually -1 */
-	for (i = 0; WSAErrors[i].id != -1; i++)
-		if (WSAErrors[i].id == id)
-			break;
-
-	sprintf(WSAbuf, "Socket Error: (%d) %s", id, WSAErrors[i].code);
-
-	return &(WSAbuf[0]);
-}
-#endif
-
 static const char *UNAVAILABLE = " - API will not be available";
 
 static const char *BLANK = "";
@@ -398,6 +323,8 @@ static const char *JSON_PARAMETER = "parameter";
 #define MSG_ZERSUM 96
 #define MSG_ZERNOSUM 97
 
+#define MSG_DEVSCAN 0x100
+
 enum code_severity {
 	SEVERITY_ERR,
 	SEVERITY_WARN,
@@ -407,6 +334,7 @@ enum code_severity {
 };
 
 enum code_parameters {
+	PARAM_COUNT,
 	PARAM_GPU,
 	PARAM_PGA,
 	PARAM_CPU,
@@ -572,6 +500,7 @@ struct CODES {
  { SEVERITY_ERR,   MSG_ZERINV,	PARAM_STR,	"Invalid zero parameter '%s'" },
  { SEVERITY_SUCC,  MSG_ZERSUM,	PARAM_STR,	"Zeroed %s stats with summary" },
  { SEVERITY_SUCC,  MSG_ZERNOSUM, PARAM_STR,	"Zeroed %s stats without summary" },
+ { SEVERITY_SUCC,  MSG_DEVSCAN, PARAM_COUNT,	"Added %d new device(s)" },
  { SEVERITY_FAIL, 0, 0, NULL }
 };
 
@@ -1262,6 +1191,7 @@ static void message(struct io_data *io_data, int messageid, int paramid, char *p
 			severity[1] = '\0';
 
 			switch(codes[i].params) {
+				case PARAM_COUNT:
 				case PARAM_GPU:
 				case PARAM_PGA:
 				case PARAM_CPU:
@@ -1464,6 +1394,7 @@ static const char *status2str(enum alive status)
 		case LIFE_NOSTART:
 			return NOSTART;
 		case LIFE_INIT:
+		case LIFE_INIT2:
 			return INIT;
 		case LIFE_WAIT:
 			return WAIT;
@@ -1661,6 +1592,31 @@ static void gpudev(struct io_data *io_data, __maybe_unused SOCKETTYPE c, char *p
 }
 #endif
 
+static void devscan(struct io_data *io_data, __maybe_unused SOCKETTYPE c, __maybe_unused char *param, bool isjson, __maybe_unused char group)
+{
+	int n;
+	bool io_open = false;
+	
+	applog(LOG_DEBUG, "RPC: request to scan %s for devices",
+	       param);
+	
+	if (param && !param[0])
+		param = NULL;
+	
+	n = scan_serial(param);
+	
+	message(io_data, MSG_DEVSCAN, n, NULL, isjson);
+	
+	io_open = io_add(io_data, isjson ? COMSTR JSON_DEVS : _DEVS COMSTR);
+
+	n = total_devices - n;
+	for (int i = n; i < total_devices; ++i)
+		devdetail_an(io_data, get_devices(i), isjson, i > n);
+	
+	if (isjson && io_open)
+		io_close(io_data);
+}
+
 #ifdef HAVE_AN_FPGA
 static void pgadev(struct io_data *io_data, __maybe_unused SOCKETTYPE c, char *param, bool isjson, __maybe_unused char group)
 {
@@ -3150,6 +3106,7 @@ struct CMDS {
 } cmds[] = {
 	{ "version",		apiversion,	false },
 	{ "config",		minerconfig,	false },
+	{ "devscan",		devscan,	false },
 	{ "devs",		devstatus,	false },
 	{ "devdetail",	devdetail,	false },
 	{ "pools",		poolstatus,	false },
@@ -3610,7 +3567,7 @@ void api(int api_thr_id)
 	SOCKETTYPE c;
 	int n, bound;
 	char *connectaddr;
-	char *binderror;
+	const char *binderror;
 	time_t bindstart;
 	short int port = opt_api_port;
 	struct sockaddr_in serv;

+ 3 - 6
compat.h

@@ -18,16 +18,13 @@
 
 #include <stdbool.h>
 
-// NOTE: Nested preprocessor checks since the latter isn't defined at all without the former
-#ifdef HAVE_LIBUSB
-#	if ! HAVE_DECL_LIBUSB_ERROR_NAME
-		static char my_libusb_error_name_buf[0x10];
-#		define libusb_error_name(x) (sprintf(my_libusb_error_name_buf, "%d", x), my_libusb_error_name_buf)
-#	endif
+#if !(defined(WIN32) || defined(unix))
+#define unix
 #endif
 
 #ifdef WIN32
 #include <errno.h>
+#include <fcntl.h>
 #include <time.h>
 #include <pthread.h>
 #include <sys/time.h>

+ 15 - 3
configure.ac

@@ -14,7 +14,7 @@ dnl * any later version.  See COPYING for more details.
 ##--##--##--##--##--##--##--##--##--##--##--##--##--##--##--##--##
 m4_define([v_maj], [3])
 m4_define([v_min], [1])
-m4_define([v_mic], [1])
+m4_define([v_mic], [2])
 ##--##--##--##--##--##--##--##--##--##--##--##--##--##--##--##--##
 m4_define([v_ver], [v_maj.v_min.v_mic])
 m4_define([lt_rev], m4_eval(v_maj + v_min))
@@ -55,7 +55,7 @@ AC_CANONICAL_BUILD
 AC_CANONICAL_HOST
 
 dnl Make sure anyone changing configure.ac/Makefile.am has a clue
-AM_MAINTAINER_MODE
+AM_MAINTAINER_MODE([enable])
 
 dnl Checks for programs
 AC_PROG_CC_C99
@@ -524,8 +524,20 @@ if test "x$with_system_libblkmaker" = "xyes"; then
 		AC_MSG_ERROR([Could not find system libblkmaker])
 	])
 else
+	save_LDFLAGS="$LDFLAGS"
+	LDFLAGS="$LDFLAGS -Wl,-zorigin"
+	origin_LDFLAGS=
+	AC_MSG_CHECKING([whether the linker recognizes the -zorigin option])
+	AC_TRY_LINK([],[],[
+		AC_MSG_RESULT([yes])
+		origin_LDFLAGS=',-zorigin'
+	],[
+		AC_MSG_RESULT([no])
+	])
+	LDFLAGS="$save_LDFLAGS"
+	
 	libblkmaker_CFLAGS='-Ilibblkmaker'
-	libblkmaker_LDFLAGS='-Llibblkmaker/.libs -Wl,-rpath,libblkmaker/.libs'
+	libblkmaker_LDFLAGS='-Llibblkmaker/.libs -Wl,-rpath,\$$ORIGIN/libblkmaker/.libs'"$origin_LDFLAGS"
 	libblkmaker_LIBS='-lblkmaker_jansson-0.1 -lblkmaker-0.1'
 	AC_CONFIG_SUBDIRS([libblkmaker])
 fi

+ 13 - 0
debian/changelog

@@ -1,3 +1,16 @@
+bfgminer (3.1.2-0precise1) precise; urgency=low
+
+  * TUI: The "GPU management" interface has been replaced with a new generic "Manage devices" interface, allowing easy enable and disable of non-GPU devices.
+  * Major CPU usage reduction for faster mining rigs (on my minirig host system, from 35% down to 13%!).
+  * erupter: New icarus-based driver to handle autodetection of (branded) Block Erupter devices.
+  * opencl: Add support for AMD Catalyst 13.2+ drivers.
+  * The device statlines have been condensed by reducing the device-specific space down to a single temperature reading. More detailed information (such as GPU fan speeds) is still available via RPC and the new "Manage devices" interface.
+  * RPC: New "devscan" command to probe for new devices on demand. The effect is the same as starting BFGMiner with -S noauto -S <parameter>.
+  * TUI: Display percentage invalid of found nonces with hw errors.
+  * cpu & opencl: These legacy drivers now respect the --scan-serial auto/noauto directives, and the old -C (enable CPU) and -G (disable GPU) options are now deprecated.
+
+ -- Luke Dashjr <luke+bfgminer@dashjr.org>  Mon, 08 Jul 2013 18:46:53 -0000
+
 bfgminer (3.1.1-0precise1) precise; urgency=low
 
   * bitforce: Support for Little Single boards, and Jalapenos with 1.2 firmware.

+ 1 - 1
debian/control

@@ -2,7 +2,7 @@ Source: bfgminer
 Priority: optional
 Section: misc
 Maintainer: Luke Dashjr <luke_bfgminer@dashjr.org>
-Standards-Version: 3.1.1
+Standards-Version: 3.1.2
 Build-Depends: build-essential, debhelper, autoconf, automake, libtool, libssl-dev, yasm, pkg-config, libudev-dev, libcurl4-openssl-dev, wget, unzip, libjansson-dev, libncurses5-dev, libudev-dev, libusb-1.0-0-dev, git, quilt, uthash-dev, libsensors4-dev
 
 Package: bfgminer

+ 19 - 11
deviceapi.c

@@ -26,6 +26,7 @@
 
 #include "compat.h"
 #include "deviceapi.h"
+#include "fpgautils.h"
 #include "logging.h"
 #include "miner.h"
 #include "util.h"
@@ -580,12 +581,21 @@ void *miner_thread(void *userdata)
 		minerloop_scanhash(mythr);
 	__thr_being_msg(LOG_NOTICE, mythr, "shutting down");
 
-out:
-	cgpu->deven = DEV_DISABLED;
+out: ;
+	struct cgpu_info *proc = cgpu;
+	do
+	{
+		proc->deven = DEV_DISABLED;
+		proc->status = LIFE_DEAD2;
+	}
+	while ( (proc = proc->next_proc) && !proc->threads);
+	mythr->getwork = 0;
+	mythr->has_pth = false;
+	nmsleep(1000);
+	
 	if (drv->thread_shutdown)
 		drv->thread_shutdown(mythr);
 
-	thread_reportin(mythr);
 	notifier_destroy(mythr->notifier);
 
 	return NULL;
@@ -608,10 +618,12 @@ bool add_cgpu(struct cgpu_info *cgpu)
 	strcpy(cgpu->proc_repr, cgpu->dev_repr);
 	sprintf(cgpu->proc_repr_ns, "%s%u", cgpu->drv->name, cgpu->device_id);
 	
-	wr_lock(&devices_lock);
+	maybe_strdup_if_null(&cgpu->dev_manufacturer, detectone_meta_info.manufacturer);
+	maybe_strdup_if_null(&cgpu->dev_product,      detectone_meta_info.product);
+	maybe_strdup_if_null(&cgpu->dev_serial,       detectone_meta_info.serial);
 	
-	devices = realloc(devices, sizeof(struct cgpu_info *) * (total_devices + lpcount + 1));
-	devices[total_devices++] = cgpu;
+	devices_new = realloc(devices_new, sizeof(struct cgpu_info *) * (total_devices_new + lpcount + 1));
+	devices_new[total_devices_new++] = cgpu;
 	
 	if (lpcount > 1)
 	{
@@ -645,7 +657,7 @@ bool add_cgpu(struct cgpu_info *cgpu)
 				slave->proc_repr_ns[ns] += i;
 			}
 			slave->threads = tpp;
-			devices[total_devices++] = slave;
+			devices_new[total_devices_new++] = slave;
 			*nlp_p = slave;
 			nlp_p = &slave->next_proc;
 		}
@@ -653,12 +665,8 @@ bool add_cgpu(struct cgpu_info *cgpu)
 		cgpu->proc_id = 0;
 		cgpu->threads -= (tpp * (lpcount - 1));
 	}
-	
-	wr_unlock(&devices_lock);
 
-	mutex_lock(&stats_lock);
 	cgpu->last_device_valid_work = time(NULL);
-	mutex_unlock(&stats_lock);
 	
 	return true;
 }

+ 8 - 33
driver-avalon.c

@@ -233,7 +233,7 @@ static inline int avalon_gets(int fd, uint8_t *buf, int read_count,
 		ret = read(fd, buf, 1);
 		if (ret < 0)
 		{
-			applog(LOG_ERR, "Avalon: Error %d on read in avalon_gets", errno);
+			applog(LOG_ERR, "Avalon: Error on read in avalon_gets: %s", bfg_strerror(errno, BST_ERRNO));
 			return AVA_GETS_ERROR;
 		}
 
@@ -458,23 +458,8 @@ static void get_options(int this_option_offset, int *baud, int *miner_count,
 		*(colon++) = '\0';
 
 	tmp = atoi(buf);
-	switch (tmp) {
-	case 115200:
-		*baud = 115200;
-		break;
-	case 57600:
-		*baud = 57600;
-		break;
-	case 38400:
-		*baud = 38400;
-		break;
-	case 19200:
-		*baud = 19200;
-		break;
-	default:
-		quit(1, "Invalid avalon-options for baud (%s) "
-			"must be 115200, 57600, 38400 or 19200", buf);
-	}
+	if (!valid_baud(*baud = tmp))
+		quit(1, "Invalid avalon-options for baud (%s)", buf);
 
 	if (colon && *colon) {
 		colon2 = strchr(colon, ':');
@@ -571,6 +556,9 @@ static bool avalon_detect_one(const char *devpath)
 	int baud, miner_count, asic_count, timeout, frequency = 0;
 	struct cgpu_info *avalon;
 
+	if (serial_claim(devpath, &avalon_drv))
+		return false;
+	
 	int this_option_offset = ++option_offset;
 	get_options(this_option_offset, &baud, &miner_count, &asic_count,
 		    &timeout, &frequency);
@@ -688,6 +676,7 @@ static bool avalon_prepare(struct thr_info *thr)
 
 	cgtime(&now);
 	get_datestamp(avalon->init, &now);
+	avalon->status = LIFE_INIT2;
 	return true;
 }
 
@@ -779,20 +768,6 @@ static inline void adjust_fan(struct avalon_info *info)
 	}
 }
 
-static void get_avalon_statline_before(char *buf, struct cgpu_info *avalon)
-{
-	struct avalon_info *info = avalon->device_data;
-	int lowfan = 10000;
-
-	/* Find the lowest fan speed of the ASIC cooling fans. */
-	if (info->fan1 >= 0 && info->fan1 < lowfan)
-		lowfan = info->fan1;
-	if (info->fan2 >= 0 && info->fan2 < lowfan)
-		lowfan = info->fan2;
-
-	tailsprintf(buf, "%2d/%3dC %04dR | ", info->temp0, info->temp2, lowfan);
-}
-
 /* We use a replacement algorithm to only remove references to work done from
  * the buffer when we need the extra space for new work. */
 static bool avalon_fill(struct cgpu_info *avalon)
@@ -976,6 +951,7 @@ static int64_t avalon_scanhash(struct thr_info *thr)
 
 	if (hash_count) {
 		record_temp_fan(info, &ar, &(avalon->temp));
+		avalon->temp = info->temp_max;
 		applog(LOG_INFO,
 		       "Avalon: Fan1: %d/m, Fan2: %d/m, Fan3: %d/m\t"
 		       "Temp1: %dC, Temp2: %dC, Temp3: %dC, TempMAX: %dC",
@@ -1043,7 +1019,6 @@ struct device_drv avalon_drv = {
 	.queue_full = avalon_fill,
 	.scanwork = avalon_scanhash,
 	.get_api_stats = avalon_api_stats,
-	.get_statline_before = get_avalon_statline_before,
 	.reinit_device = avalon_init,
 	.thread_shutdown = avalon_shutdown,
 };

+ 4 - 27
driver-bf1.c

@@ -53,7 +53,7 @@ int bf1_rehash(unsigned char *midstate, unsigned m7, unsigned ntime, unsigned nb
 {
 	uint8_t   in[16];
 	uint32_t *in32 = (uint32_t *)in;
-	char     *hex;
+	char      hex[65];
 	uint32_t *mid32 = (uint32_t *)midstate;
 	uint32_t  out32[8];
 	uint8_t  *out = (uint8_t *) out32;
@@ -76,7 +76,7 @@ int bf1_rehash(unsigned char *midstate, unsigned m7, unsigned ntime, unsigned nb
 
 	if (out32[7] == 0)
 	{
-		hex = bin2hex(out, 32);
+		bin2hex(hex, out, 32);
 		applog(LOG_INFO, "! MS0: %08x, m7: %08x, ntime: %08x, nbits: %08x, nnonce: %08x\n\t\t\t out: %s\n", mid32[0], m7, ntime, nbits, nnonce, hex);
 		return 1;
 	}
@@ -132,12 +132,8 @@ static bool bf1_detect_custom(const char *devpath, struct device_drv *api, struc
 		return false;
 	}
 
-	if(serial_claim(devpath, api))
-	{
-		const char *claimedby = serial_claim(devpath, api)->dname;
-		applog(LOG_DEBUG, "Bitfury BF1 device %s already claimed by other driver: %s", devpath, claimedby);
+	if (serial_claim_v(devpath, api))
 		return false;
-	}
 
 	/* We have a real MiniMiner ONE! */
 	struct cgpu_info *bf1;
@@ -303,10 +299,8 @@ static void bf1_process_results(struct thr_info *thr, struct work *work)
 			nonces--;
 			continue;
 		}
+		inc_hw_errors(thr, work, nonce);
 	}
-
-	for(int i=0; i<nonces; i++)
-		inc_hw_errors(thr);
 }
 
 //------------------------------------------------------------------------------
@@ -391,22 +385,6 @@ static void bf1_poll(struct thr_info *thr)
 	timer_set_delay(&thr->tv_poll, &tv_now, 1000000);
 }
 
-//------------------------------------------------------------------------------
-static void bf1_statline_before(char *buf, struct cgpu_info *cgpu)
-{
-	char before[] = "                    ";
-	if(cgpu->device_data)
-	{
-		struct BF1Info *info = (struct BF1Info *)cgpu->device_data;
-
-		int len = strlen(info->id.product);
-		memcpy(before, info->id.product, len);
-
-		sprintf(before + len + 1, "%08X", info->id.serial);
-	}
-	tailsprintf(buf, "%s | ", &before[0]);
-}
-
 //------------------------------------------------------------------------------
 static void bf1_shutdown(struct thr_info *thr)
 {
@@ -431,7 +409,6 @@ struct device_drv bf1_drv = {
 	.minerloop = hash_queued_work,
 
 	.drv_detect = bf1_detect,
-	.get_statline_before = bf1_statline_before,
 
 	.identify_device = bf1_identify,
 

+ 114 - 71
driver-bitforce.c

@@ -37,7 +37,7 @@
 #define BITFORCE_QRESULT_LINE_LEN 165
 #define BITFORCE_MAX_QUEUED_MAX 40
 #define BITFORCE_MIN_QUEUED_MAX 10
-#define BITFORCE_MAX_QRESULTS 40
+#define BITFORCE_MAX_QRESULTS 16
 #define BITFORCE_GOAL_QRESULTS 5
 #define BITFORCE_MIN_QRESULT_WAIT BITFORCE_CHECK_INTERVAL_MS
 #define BITFORCE_MAX_QRESULT_WAIT 1000
@@ -131,9 +131,9 @@ void bitforce_cmd2(int fd, int procid, void *buf, size_t bufsz, const char *cmd,
 	
 	if (unlikely(opt_dev_protocol))
 	{
-		char *hex = bin2hex(data, datasz);
+		char hex[(datasz * 2) + 1];
+		bin2hex(hex, data, datasz);
 		applog(LOG_DEBUG, "DEVPROTO: CMD2 (fd=%d xlink=%d): %s", fd, procid, hex);
-		free(hex);
 	}
 	
 	bitforce_send(fd, procid, data, datasz);
@@ -190,6 +190,9 @@ static bool bitforce_detect_one(const char *devpath)
 		return false;
 	}
 
+	if (serial_claim_v(devpath, &bitforce_drv))
+		return false;
+	
 	applog(LOG_DEBUG, "Found BitForce device on %s", devpath);
 	initdata = malloc(sizeof(*initdata));
 	*initdata = (struct bitforce_init_data){
@@ -337,8 +340,7 @@ void bitforce_comm_error(struct thr_info *thr)
 	data->noncebuf[0] = '\0';
 	applog(LOG_ERR, "%"PRIpreprv": Comms error", bitforce->proc_repr);
 	dev_error(bitforce, REASON_DEV_COMMS_ERROR);
-	++bitforce->hw_errors;
-	++hw_errors;
+	inc_hw_errors_only(thr);
 	BFclose(*p_fdDev);
 	int fd = *p_fdDev = BFopen(bitforce->device_path);
 	if (fd == -1)
@@ -350,24 +352,6 @@ void bitforce_comm_error(struct thr_info *thr)
 	bitforce_clear_buffer(bitforce);
 }
 
-static void get_bitforce_statline_before(char *buf, struct cgpu_info *bitforce)
-{
-	struct bitforce_data *data = bitforce->device_data;
-	struct bitforce_proc_data *procdata = bitforce->thr[0]->cgpu_data;
-	
-	if (!procdata->handles_board)
-		goto nostats;
-
-	if (data->temp[0] > 0 && data->temp[1] > 0)
-		tailsprintf(buf, "%5.1fC/%4.1fC   | ", data->temp[0], data->temp[1]);
-	else
-	if (bitforce->temp > 0)
-		tailsprintf(buf, "%5.1fC         | ", bitforce->temp);
-	else
-nostats:
-		tailsprintf(buf, "               | ");
-}
-
 static bool bitforce_thread_prepare(struct thr_info *thr)
 {
 	struct cgpu_info *bitforce = thr->cgpu;
@@ -589,9 +573,9 @@ static bool bitforce_get_temp(struct cgpu_info *bitforce)
 	mutex_unlock(mutexp);
 	
 	if (unlikely(!pdevbuf[0])) {
+		struct thr_info *thr = bitforce->thr[0];
 		applog(LOG_ERR, "%"PRIpreprv": Error: Get temp returned empty string/timed out", bitforce->proc_repr);
-		bitforce->hw_errors++;
-		++hw_errors;
+		inc_hw_errors_only(thr);
 		return false;
 	}
 
@@ -606,7 +590,7 @@ static bool bitforce_get_temp(struct cgpu_info *bitforce)
 				float temp2 = my_strtof(s + 1, &s);
 				set_float_if_gt_zero(&data->temp[1], temp2);
 				if (temp2 > temp)
-					temp = temp;
+					temp = temp2;
 			}
 		}
 
@@ -617,14 +601,14 @@ static bool bitforce_get_temp(struct cgpu_info *bitforce)
 				chip_cgpu->temp = temp;
 		}
 	} else {
+		struct thr_info *thr = bitforce->thr[0];
 		/* 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, "%"PRIpreprv": Garbled response probably throttling, clearing buffer", bitforce->proc_repr);
 		dev_error(bitforce, REASON_DEV_THROTTLE);
 		/* Count throttling episodes as hardware errors */
-		bitforce->hw_errors++;
-		++hw_errors;
+		inc_hw_errors_only(thr);
 		bitforce_clear_buffer(bitforce);
 		return false;
 	}
@@ -639,10 +623,9 @@ void dbg_block_data(struct cgpu_info *bitforce)
 		return;
 	
 	struct bitforce_data *data = bitforce->device_data;
-	char *s;
-	s = bin2hex(&data->next_work_ob[8], 44);
+	char s[89];
+	bin2hex(s, &data->next_work_ob[8], 44);
 	applog(LOG_DEBUG, "%"PRIpreprv": block data: %s", bitforce->proc_repr, s);
-	free(s);
 }
 
 static void bitforce_change_mode(struct cgpu_info *, enum bitforce_proto);
@@ -981,14 +964,14 @@ void bitforce_job_get_results(struct thr_info *thr, struct work *work)
 			{
 				// Didn't find the one we're waiting on
 				// Must be extra stuff in the queue results
-				char *xmid = bin2hex(work->midstate, 32);
-				char *xdt = bin2hex(&work->data[64], 12);
+				char xmid[65];
+				char xdt[25];
+				bin2hex(xmid, work->midstate, 32);
+				bin2hex(xdt, &work->data[64], 12);
 				applog(LOG_WARNING, "%"PRIpreprv": Found extra garbage in queue results: %s",
 				       bitforce->proc_repr, pdevbuf);
 				applog(LOG_WARNING, "%"PRIpreprv": ...while waiting on: %s,%s",
 				       bitforce->proc_repr, xmid, xdt);
-				free(xmid);
-				free(xdt);
 				count = 0;
 			}
 			else
@@ -1035,8 +1018,7 @@ noqr:
 		applog(LOG_ERR, "%"PRIpreprv": took %lums - longer than %lums", bitforce->proc_repr,
 			tv_to_ms(elapsed), (unsigned long)BITFORCE_TIMEOUT_MS);
 		dev_error(bitforce, REASON_DEV_OVER_HEAT);
-		++bitforce->hw_errors;
-		++hw_errors;
+		inc_hw_errors_only(thr);
 
 		/* If the device truly throttled, it didn't process the job and there
 		 * are no results. But check first, just in case we're wrong about it
@@ -1092,8 +1074,7 @@ noqr:
 
 	applog(LOG_DEBUG, "%"PRIpreprv": waited %dms until %s", bitforce->proc_repr, bitforce->wait_ms, pdevbuf);
 	if (count < 0 && strncasecmp(pdevbuf, "I", 1)) {
-		bitforce->hw_errors++;
-		++hw_errors;
+		inc_hw_errors_only(thr);
 		applog(LOG_WARNING, "%"PRIpreprv": Error: Get result reports: %s", bitforce->proc_repr, pdevbuf);
 		bitforce_clear_buffer(bitforce);
 	}
@@ -1163,8 +1144,7 @@ void bitforce_process_qresult_line(struct thr_info *thr, char *buf, struct work
 	    || bitforce_process_qresult_line_i(thr, midstate, datatail, buf, thr->next_work) ))
 	{
 		applog(LOG_ERR, "%"PRIpreprv": Failed to find work for queued results", bitforce->proc_repr);
-		++bitforce->hw_errors;
-		++hw_errors;
+		inc_hw_errors_only(thr);
 	}
 }
 
@@ -1312,6 +1292,7 @@ static bool bitforce_thread_init(struct thr_info *thr)
 			if (opt_bfl_noncerange)
 				bitforce_change_mode(bitforce, BFP_RANGE);
 		}
+		bitforce->status = LIFE_INIT2;
 		
 		first_on_this_board = procdata;
 		for (int proc = 1; proc < data->parallel; ++proc)
@@ -1326,6 +1307,7 @@ static bool bitforce_thread_init(struct thr_info *thr)
 			procdata->handles_board = false;
 			procdata->cgpu = bitforce;
 			bitforce->device_data = data;
+			bitforce->status = LIFE_INIT2;
 		}
 		applog(LOG_DEBUG, "%s: Board %d: %"PRIpreprv"-%"PRIpreprv, bitforce->dev_repr, boardno, first_on_this_board->cgpu->proc_repr, bitforce->proc_repr);
 		
@@ -1348,6 +1330,62 @@ static bool bitforce_thread_init(struct thr_info *thr)
 	return true;
 }
 
+#ifdef HAVE_CURSES
+static
+void bitforce_tui_wlogprint_choices(struct cgpu_info *cgpu)
+{
+	struct bitforce_data *data = cgpu->device_data;
+	if (data->sc)
+		wlogprint("[F]an control ");
+}
+
+static
+const char *bitforce_tui_handle_choice(struct cgpu_info *cgpu, int input)
+{
+	struct bitforce_data *data = cgpu->device_data;
+	pthread_mutex_t *mutexp;
+	int fd;
+	static char replybuf[0x100];
+	
+	if (!data->sc)
+		return NULL;
+	switch (input)
+	{
+		case 'f': case 'F':
+		{
+			int fanspeed;
+			char *intvar;
+
+			intvar = curses_input("Set fan speed (range 0-4 for low to fast or 5 for auto)");
+			if (!intvar)
+				return "Invalid fan speed\n";
+			fanspeed = atoi(intvar);
+			free(intvar);
+			if (fanspeed < 0 || fanspeed > 5)
+				return "Invalid fan speed\n";
+			
+			char cmd[4] = "Z0X";
+			cmd[1] += fanspeed;
+			mutexp = &cgpu->device->device_mutex;
+			mutex_lock(mutexp);
+			fd = cgpu->device->device_fd;
+			bitforce_cmd1(fd, data->xlink_id, replybuf, sizeof(replybuf), cmd);
+			mutex_unlock(mutexp);
+			return replybuf;
+		}
+	}
+	return NULL;
+}
+
+static
+void bitforce_wlogprint_status(struct cgpu_info *cgpu)
+{
+	struct bitforce_data *data = cgpu->device_data;
+	if (data->temp[0] > 0 && data->temp[1] > 0)
+		wlogprint("Temperatures: %4.1fC %4.1fC\n", data->temp[0], data->temp[1]);
+}
+#endif
+
 static struct api_data *bitforce_drv_stats(struct cgpu_info *cgpu)
 {
 	struct bitforce_data *data = cgpu->device_data;
@@ -1432,10 +1470,14 @@ struct device_drv bitforce_drv = {
 	.dname = "bitforce",
 	.name = "BFL",
 	.drv_detect = bitforce_detect,
+#ifdef HAVE_CURSES
+	.proc_wlogprint_status = bitforce_wlogprint_status,
+	.proc_tui_wlogprint_choices = bitforce_tui_wlogprint_choices,
+	.proc_tui_handle_choice = bitforce_tui_handle_choice,
+#endif
 	.get_api_stats = bitforce_drv_stats,
 	.minerloop = minerloop_async,
 	.reinit_device = bitforce_reinit,
-	.get_statline_before = get_bitforce_statline_before,
 	.get_stats = bitforce_get_stats,
 	.set_device = bitforce_set_device,
 	.identify_device = bitforce_identify,
@@ -1543,7 +1585,8 @@ bool bitforce_queue_do_results(struct thr_info *thr)
 	struct bitforce_data *data = bitforce->device_data;
 	int fd = bitforce->device->device_fd;
 	int count;
-	char *noncebuf = &data->noncebuf[0], *buf, *end;
+	int fcount;
+	char *noncebuf, *buf, *end;
 	unsigned char midstate[32], datatail[12];
 	struct work *work, *tmpwork, *thiswork;
 	struct timeval tv_now, tv_elapsed;
@@ -1555,23 +1598,22 @@ bool bitforce_queue_do_results(struct thr_info *thr)
 	if (unlikely(!fd))
 		return false;
 	
+again:
+	noncebuf = &data->noncebuf[0];
 	count = bitforce_zox(thr, "ZOX");
 	
-	if (!count)
-	{
-		applog(LOG_DEBUG, "%"PRIpreprv": Received 0 queued results on poll", bitforce->proc_repr);
-		return true;
-	}
-	
 	if (unlikely(count < 0))
 	{
 		applog(LOG_ERR, "%"PRIpreprv": Received unexpected queue result response: %s", bitforce->proc_repr, noncebuf);
-		++bitforce->hw_errors;
-		++hw_errors;
+		inc_hw_errors_only(thr);
 		return false;
 	}
 	
-	count = 0;
+	applog(LOG_DEBUG, "%"PRIpreprv": Received %d queue results on poll (max=%d)", bitforce->proc_repr, count, (int)BITFORCE_MAX_QRESULTS);
+	if (!count)
+		return true;
+	
+	fcount = 0;
 	for (int i = 0; i < data->parallel; ++i)
 		counts[i] = 0;
 	noncebuf = next_line(noncebuf);
@@ -1620,8 +1662,7 @@ bool bitforce_queue_do_results(struct thr_info *thr)
 		if (unlikely(!thiswork))
 		{
 			applog(LOG_ERR, "%"PRIpreprv": Failed to find work for queue results: %s", chip_cgpu->proc_repr, buf);
-			++chip_cgpu->hw_errors;
-			++hw_errors;
+			inc_hw_errors_only(chip_thr);
 			goto next_qline;
 		}
 		
@@ -1639,7 +1680,7 @@ bool bitforce_queue_do_results(struct thr_info *thr)
 			}
 			bitforce_process_result_nonces(chip_thr, work, &end[1]);
 		}
-		++count;
+		++fcount;
 		++counts[chipno];
 		
 finishresult:
@@ -1669,22 +1710,25 @@ next_qline: (void)0;
 	
 	bitforce_set_queue_full(thr);
 	
+	if (count >= BITFORCE_MAX_QRESULTS)
+		goto again;
+	
 	if (data->parallel == 1 && (
-	        (count < BITFORCE_GOAL_QRESULTS && bitforce->sleep_ms < BITFORCE_MAX_QRESULT_WAIT && data->queued > 1)
-	     || (count > BITFORCE_GOAL_QRESULTS && bitforce->sleep_ms > BITFORCE_MIN_QRESULT_WAIT)  ))
+	        (fcount < BITFORCE_GOAL_QRESULTS && bitforce->sleep_ms < BITFORCE_MAX_QRESULT_WAIT && data->queued > 1)
+	     || (fcount > BITFORCE_GOAL_QRESULTS && bitforce->sleep_ms > BITFORCE_MIN_QRESULT_WAIT)  ))
 	{
 		unsigned int old_sleep_ms = bitforce->sleep_ms;
-		bitforce->sleep_ms = (uint32_t)bitforce->sleep_ms * BITFORCE_GOAL_QRESULTS / (count ?: 1);
+		bitforce->sleep_ms = (uint32_t)bitforce->sleep_ms * BITFORCE_GOAL_QRESULTS / (fcount ?: 1);
 		if (bitforce->sleep_ms > BITFORCE_MAX_QRESULT_WAIT)
 			bitforce->sleep_ms = BITFORCE_MAX_QRESULT_WAIT;
 		if (bitforce->sleep_ms < BITFORCE_MIN_QRESULT_WAIT)
 			bitforce->sleep_ms = BITFORCE_MIN_QRESULT_WAIT;
 		applog(LOG_DEBUG, "%"PRIpreprv": Received %d queue results after %ums; Wait time changed to: %ums (queued<=%d)",
-		       bitforce->proc_repr, count, old_sleep_ms, bitforce->sleep_ms, data->queued);
+		       bitforce->proc_repr, fcount, old_sleep_ms, bitforce->sleep_ms, data->queued);
 	}
 	else
 		applog(LOG_DEBUG, "%"PRIpreprv": Received %d queue results after %ums; Wait time unchanged (queued<=%d)",
-		       bitforce->proc_repr, count, bitforce->sleep_ms, data->queued);
+		       bitforce->proc_repr, fcount, bitforce->sleep_ms, data->queued);
 	
 	cgtime(&tv_now);
 	timersub(&tv_now, &data->tv_hashmeter_start, &tv_elapsed);
@@ -1732,8 +1776,7 @@ bool bitforce_queue_append(struct thr_info *thr, struct work *work)
 		{
 			// Problem sending queue, retry again in a few seconds
 			applog(LOG_ERR, "%"PRIpreprv": Failed to send queue", bitforce->proc_repr);
-			++bitforce->hw_errors;
-			++hw_errors;
+			inc_hw_errors_only(thr);
 			data->want_to_send_queue = true;
 		}
 	}
@@ -1756,7 +1799,7 @@ void bitforce_queue_flush(struct thr_info *thr)
 	
 	struct cgpu_info *bitforce = thr->cgpu;
 	struct bitforce_data *data = bitforce->device_data;
-	char *buf = &data->noncebuf[0], *buf2;
+	char *buf = &data->noncebuf[0], *buf2 = NULL;
 	const char *cmd = "ZqX";
 	unsigned flushed;
 	struct _jobinfo *processing = NULL, *item, *this;
@@ -1767,10 +1810,7 @@ void bitforce_queue_flush(struct thr_info *thr)
 	// TODO: Call "ZQX" most of the time: don't need to do sanity checks so often
 	bitforce_zox(thr, cmd);
 	if (!strncasecmp(buf, "OK:FLUSHED", 10))
-	{
 		flushed = atoi(&buf[10]);
-		buf2 = NULL;
-	}
 	else
 	if ((!strncasecmp(buf, "COUNT:", 6)) && (buf2 = strstr(buf, "FLUSHED:")) )
 	{
@@ -1837,9 +1877,9 @@ void bitforce_queue_flush(struct thr_info *thr)
 			HASH_FIND(hh, processing, &key[0], sizeof(key), item);
 			if (unlikely(!item))
 			{
-				char *hex = bin2hex(key, 32+12);
+				char hex[89];
+				bin2hex(hex, key, 32+12);
 				applog(LOG_WARNING, "%"PRIpreprv": Sanity check: Device is missing queued job! %s", bitforce->proc_repr, hex);
-				free(hex);
 				work_list_del(&thr->work_list, work);
 				continue;
 			}
@@ -1879,8 +1919,7 @@ void bitforce_queue_poll(struct thr_info *thr)
 			if (!data->queued)
 			{
 				applog(LOG_ERR, "%"PRIpreprv": Failed to send queue, and queue empty; retrying after 1 second", bitforce->proc_repr);
-				++bitforce->hw_errors;
-				++hw_errors;
+				inc_hw_errors_only(thr);
 				sleep_us = 1000000;
 			}
 	
@@ -1921,7 +1960,11 @@ struct device_drv bitforce_queue_api = {
 	.name = "BFL",
 	.minerloop = minerloop_queue,
 	.reinit_device = bitforce_reinit,
-	.get_statline_before = get_bitforce_statline_before,
+#ifdef HAVE_CURSES
+	.proc_wlogprint_status = bitforce_wlogprint_status,
+	.proc_tui_wlogprint_choices = bitforce_tui_wlogprint_choices,
+	.proc_tui_handle_choice = bitforce_tui_handle_choice,
+#endif
 	.get_api_stats = bitforce_drv_stats,
 	.get_stats = bitforce_get_stats,
 	.set_device = bitforce_set_device,

+ 0 - 2
driver-cairnsmore.c

@@ -196,8 +196,6 @@ static bool cairnsmore_identify(struct cgpu_info *cm1)
 	return true;
 }
 
-extern struct device_drv icarus_drv;
-
 static void cairnsmore_drv_init()
 {
 	cairnsmore_drv = icarus_drv;

+ 20 - 5
driver-cpu.c

@@ -30,8 +30,10 @@
 #include <libgen.h>
 
 #include "compat.h"
+#include "fpgautils.h"
 #include "miner.h"
 #include "bench_block.h"
+#include "util.h"
 #include "driver-cpu.h"
 
 #if defined(unix)
@@ -709,7 +711,7 @@ char *force_nthreads_int(const char *arg, int *i)
 #endif
 
 #ifdef WANT_CPUMINE
-static void cpu_detect()
+static int cpu_autodetect()
 {
 	int i;
 
@@ -745,13 +747,10 @@ static void cpu_detect()
 	#endif /* !WIN32 */
 
 	if (opt_n_threads < 0 || !forced_n_threads) {
-		if (total_devices && !opt_usecpu)
-			opt_n_threads = 0;
-		else
 			opt_n_threads = num_processors;
 	}
 	if (num_processors < 1)
-		return;
+		return 0;
 
 	cpus = calloc(opt_n_threads, sizeof(struct cgpu_info));
 	if (unlikely(!cpus))
@@ -767,6 +766,19 @@ static void cpu_detect()
 		cgpu->kname = algo_names[opt_algo];
 		add_cgpu(cgpu);
 	}
+	return opt_n_threads;
+}
+
+static void cpu_detect()
+{
+	RUNONCE();
+	
+	if ((opt_n_threads < 0 || !forced_n_threads)
+	 && ((total_devices || total_devices_new) && !opt_usecpu))
+		// If there are any other devices, only act if the user has explicitly enabled it
+		noserial_detect_manual(&cpu_drv, cpu_autodetect);
+	else
+		noserial_detect(&cpu_drv, cpu_autodetect);
 }
 
 static pthread_mutex_t cpualgo_lock;
@@ -791,6 +803,7 @@ static uint64_t cpu_can_limit_work(struct thr_info __maybe_unused *thr)
 static bool cpu_thread_init(struct thr_info *thr)
 {
 	const int thr_id = thr->id;
+	struct cgpu_info *cgpu = thr->cgpu;
 
 	mutex_lock(&cpualgo_lock);
 	switch (opt_algo)
@@ -803,6 +816,8 @@ static bool cpu_thread_init(struct thr_info *thr)
 	}
 	mutex_unlock(&cpualgo_lock);
 
+	cgpu->kname = algo_names[opt_algo];
+	
 	/* Set worker threads to nice 19 and then preferentially to SCHED_IDLE
 	 * and if that fails, then SCHED_BATCH. No need for this to be an
 	 * error if it fails */

+ 84 - 0
driver-erupter.c

@@ -0,0 +1,84 @@
+/*
+ * Copyright 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 "fpgautils.h"
+#include "icarus-common.h"
+
+#define ERUPTER_IO_SPEED 115200
+#define ERUPTER_HASH_TIME 0.0000000029761
+
+extern struct device_drv erupter_drv;
+extern struct device_drv erupter_drv_emerald;
+
+static bool _erupter_detect_one(const char *devpath, struct device_drv *drv)
+{
+	struct ICARUS_INFO *info = calloc(1, sizeof(struct ICARUS_INFO));
+	if (unlikely(!info))
+		quit(1, "Failed to malloc ICARUS_INFO");
+
+	*info = (struct ICARUS_INFO){
+		.baud = ERUPTER_IO_SPEED,
+		.Hs = ERUPTER_HASH_TIME,
+		.timing_mode = MODE_DEFAULT,
+	};
+
+	if (!icarus_detect_custom(devpath, drv, info)) {
+		free(info);
+		return false;
+	}
+	return true;
+}
+
+static bool erupter_emerald_detect_one(const char *devpath)
+{
+	return _erupter_detect_one(devpath, &erupter_drv_emerald);
+}
+
+static bool erupter_detect_one(const char *devpath)
+{
+	return _erupter_detect_one(devpath, &erupter_drv);
+}
+
+static int erupter_emerald_detect_auto(void)
+{
+	return serial_autodetect(erupter_emerald_detect_one, "Block", "Erupter", "Emerald");
+}
+
+static int erupter_detect_auto(void)
+{
+	return serial_autodetect(erupter_detect_one, "Block", "Erupter");
+}
+
+static void erupter_drv_init();
+
+static void erupter_detect()
+{
+	erupter_drv_init();
+	// Actual serial detection is handled by Icarus driver
+	serial_detect_auto_byname(&erupter_drv_emerald, erupter_emerald_detect_one, erupter_emerald_detect_auto);
+	serial_detect_auto_byname(&erupter_drv, erupter_detect_one, erupter_detect_auto);
+}
+
+static void erupter_drv_init()
+{
+	erupter_drv = icarus_drv;
+	erupter_drv.dname = "erupter";
+	erupter_drv.name = "BES";
+	erupter_drv.drv_detect = erupter_detect;
+	
+	erupter_drv_emerald = erupter_drv;
+	erupter_drv_emerald.name = "BEE";
+}
+
+struct device_drv erupter_drv = {
+	// Needed to get to erupter_drv_init at all
+	.drv_detect = erupter_detect,
+};
+
+struct device_drv erupter_drv_emerald;

+ 12 - 21
driver-icarus.c

@@ -472,16 +472,8 @@ static void get_options(int this_option_offset, struct ICARUS_INFO *info)
 
 		if (*buf) {
 			tmp = atoi(buf);
-			switch (tmp) {
-			case 115200:
-				*baud = 115200;
-				break;
-			case 57600:
-				*baud = 57600;
-				break;
-			default:
-				quit(1, "Invalid icarus-options for baud (%s) must be 115200 or 57600", buf);
-			}
+			if (!valid_baud(*baud = tmp))
+				quit(1, "Invalid icarus-options for baud (%s)", buf);
 		}
 
 		if (colon && *colon) {
@@ -555,7 +547,7 @@ bool icarus_detect_custom(const char *devpath, struct device_drv *api, struct IC
 	const char golden_nonce[] = "000187a2";
 
 	unsigned char ob_bin[64], nonce_bin[ICARUS_READ_SIZE];
-	char *nonce_hex;
+	char nonce_hex[(sizeof(nonce_bin) * 2) + 1];
 
 	get_options(this_option_offset, info);
 
@@ -580,26 +572,21 @@ bool icarus_detect_custom(const char *devpath, struct device_drv *api, struct IC
 
 	icarus_close(fd);
 
-	nonce_hex = bin2hex(nonce_bin, sizeof(nonce_bin));
+	bin2hex(nonce_hex, nonce_bin, sizeof(nonce_bin));
 	if (strncmp(nonce_hex, golden_nonce, 8)) {
 		applog(LOG_DEBUG,
 			"Icarus Detect: "
 			"Test failed at %s: get %s, should: %s",
 			devpath, nonce_hex, golden_nonce);
-		free(nonce_hex);
 		return false;
 	}
 	applog(LOG_DEBUG,
 		"Icarus Detect: "
 		"Test succeeded at %s: got %s",
 			devpath, nonce_hex);
-	free(nonce_hex);
 
-	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);
+	if (serial_claim_v(devpath, api))
 		return false;
-	}
 
 	/* We have a real Icarus! */
 	struct cgpu_info *icarus;
@@ -633,6 +620,9 @@ static bool icarus_detect_one(const char *devpath)
 	if (unlikely(!info))
 		quit(1, "Failed to malloc ICARUS_INFO");
 
+	// TODO: try some higher speeds with the Icarus and BFL to see
+	// if they support them and if setting them makes any difference
+	// N.B. B3000000 doesn't work on Icarus
 	info->baud = ICARUS_IO_SPEED;
 	info->quirk_reopen = 1;
 	info->Hs = ICARUS_REV3_HASH_TIME;
@@ -685,6 +675,8 @@ static bool icarus_prepare(struct thr_info *thr)
 	}
 #endif
 
+	icarus->status = LIFE_INIT2;
+	
 	return true;
 }
 
@@ -764,7 +756,6 @@ static bool icarus_start_work(struct thr_info *thr, const unsigned char *ob_bin)
 	struct icarus_state *state = thr->cgpu_data;
 	int fd = icarus->device_fd;
 	int ret;
-	char *ob_hex;
 
 	cgtime(&state->tv_workstart);
 
@@ -777,11 +768,11 @@ static bool icarus_start_work(struct thr_info *thr, const unsigned char *ob_bin)
 	}
 
 	if (opt_debug) {
-		ob_hex = bin2hex(ob_bin, 64);
+		char ob_hex[129];
+		bin2hex(ob_hex, ob_bin, 64);
 		applog(LOG_DEBUG, "%"PRIpreprv" sent: %s",
 			icarus->proc_repr,
 			ob_hex);
-		free(ob_hex);
 	}
 
 	return true;

+ 14 - 69
driver-modminer.c

@@ -123,6 +123,9 @@ modminer_detect_one(const char *devpath)
 	char*devname = strdup(buf);
 	applog(LOG_DEBUG, "ModMiner identified as: %s", devname);
 
+	if (serial_claim_v(devpath, &modminer_drv))
+		return false;
+	
 	if (1 != write(fd, MODMINER_FPGA_COUNT, 1))
 		bailout(LOG_DEBUG, "ModMiner detect: write failed on %s (get FPGA count)", devpath);
 	len = read(fd, buf, 1);
@@ -297,6 +300,8 @@ modminer_fpga_prepare(struct thr_info *thr)
 	dclk_prepare(&state->dclk);
 	state->next_work_cmd[0] = MODMINER_SEND_WORK;
 	state->next_work_cmd[1] = proc->proc_id;  // FPGA id
+	
+	proc->status = LIFE_INIT2;
 
 	return true;
 }
@@ -409,7 +414,7 @@ modminer_fpga_init(struct thr_info *thr)
 		applog(LOG_ERR, "%s: FPGA not programmed", modminer->proc_repr);
 		if (!modminer_fpga_upload_bitstream(modminer))
 			return false;
-	} else if (opt_force_dev_init && modminer->status == LIFE_INIT) {
+	} else if (opt_force_dev_init && !((struct modminer_fpga_state *)modminer->device->thr[0]->cgpu_data)->pdone) {
 		applog(LOG_DEBUG, "%s: FPGA is already programmed, but --force-dev-init is set",
 		       modminer->proc_repr);
 		if (!modminer_fpga_upload_bitstream(modminer))
@@ -451,73 +456,16 @@ modminer_fpga_init(struct thr_info *thr)
 }
 
 static
-bool get_modminer_upload_percent(char *buf, struct cgpu_info *modminer)
+bool get_modminer_upload_percent(char *buf, struct cgpu_info *modminer, __maybe_unused bool per_processor)
 {
-	char info[18] = "               | ";
-
 	char pdone = ((struct modminer_fpga_state*)(modminer->device->thr[0]->cgpu_data))->pdone;
 	if (pdone != 101) {
-		sprintf(&info[1], "%3d%%", pdone);
-		info[5] = ' ';
-		strcat(buf, info);
+		tailsprintf(buf, "%3d%% ", pdone);
 		return true;
 	}
 	return false;
 }
 
-static
-void get_modminer_statline_before(char *buf, struct cgpu_info *modminer)
-{
-	if (get_modminer_upload_percent(buf, modminer))
-		return;
-
-	struct thr_info*thr = modminer->thr[0];
-	struct modminer_fpga_state *state = thr->cgpu_data;
-	float gt = state->temp;
-	
-	if (gt > 0)
-		tailsprintf(buf, "%5.1fC ", gt);
-	else
-		tailsprintf(buf, "       ");
-	tailsprintf(buf, "        | ");
-}
-
-static
-void get_modminer_dev_statline_before(char *buf, struct cgpu_info *modminer)
-{
-	if (get_modminer_upload_percent(buf, modminer))
-		return;
-
-	char info[18] = "               | ";
-	int tc = modminer->procs;
-	bool havetemp = false;
-	int i;
-
-	if (tc > 4)
-		tc = 4;
-
-	for (i = 0; i < tc; ++i, modminer = modminer->next_proc) {
-		struct thr_info*thr = modminer->thr[0];
-		struct modminer_fpga_state *state = thr->cgpu_data;
-		unsigned char temp = state->temp;
-
-		info[i*3+2] = '/';
-		if (temp) {
-			havetemp = true;
-			if (temp > 9)
-				info[i*3+0] = 0x30 + (temp / 10);
-			info[i*3+1] = 0x30 + (temp % 10);
-		}
-	}
-	if (havetemp) {
-		info[tc*3-1] = ' ';
-		info[tc*3] = 'C';
-		strcat(buf, info);
-	}
-	else
-		strcat(buf, "               | ");
-}
-
 static void modminer_get_temperature(struct cgpu_info *modminer, struct thr_info *thr)
 {
 	struct modminer_fpga_state *state = thr->cgpu_data;
@@ -650,10 +598,10 @@ fd_set fds;
 	status_read("start work");
 	mutex_unlock(mutexp);
 	if (opt_debug) {
-		char *xdata = bin2hex(state->running_work.data, 80);
+		char xdata[161];
+		bin2hex(xdata, state->running_work.data, 80);
 		applog(LOG_DEBUG, "%s: Started work: %s",
 		       modminer->proc_repr, xdata);
-		free(xdata);
 	}
 
 	return true;
@@ -712,11 +660,7 @@ modminer_process_results(struct thr_info*thr)
 				submit_nonce(thr, work, nonce);
 			}
 			else {
-				applog(LOG_DEBUG, "%s: Nonce with H not zero  : %02x%02x%02x%02x",
-				       modminer->proc_repr,
-				       NONCE_CHARS(nonce));
-				++hw_errors;
-				++modminer->hw_errors;
+				inc_hw_errors(thr, work, nonce);
 				++state->bad_share_counter;
 				++immediate_bad_nonces;
 			}
@@ -794,6 +738,8 @@ modminer_scanhash(struct thr_info*thr, struct work*work, int64_t __maybe_unused
 static void
 modminer_fpga_shutdown(struct thr_info *thr)
 {
+	for (struct cgpu_info *proc = thr->cgpu->device; proc; proc = proc->next_proc)
+		proc->status = LIFE_DEAD2;
 	free(thr->cgpu_data);
 	thr->cgpu_data = NULL;
 }
@@ -848,8 +794,7 @@ struct device_drv modminer_drv = {
 	.dname = "modminer",
 	.name = "MMQ",
 	.drv_detect = modminer_detect,
-	.get_dev_statline_before = get_modminer_dev_statline_before,
-	.get_statline_before = get_modminer_statline_before,
+	.override_statline_temp = get_modminer_upload_percent,
 	.get_stats = modminer_get_stats,
 	.get_api_extra_device_status = get_modminer_drv_extra_device_status,
 	.set_device = modminer_set_device,

+ 148 - 217
driver-opencl.c

@@ -39,6 +39,7 @@
 #define OMIT_OPENCL_API
 
 #include "compat.h"
+#include "fpgautils.h"
 #include "miner.h"
 #include "driver-opencl.h"
 #include "findnonce.h"
@@ -834,217 +835,145 @@ struct device_drv opencl_api;
 #endif /* HAVE_OPENCL */
 
 #if defined(HAVE_OPENCL) && defined(HAVE_CURSES)
-void manage_gpu(void)
+static
+void opencl_wlogprint_status(struct cgpu_info *cgpu)
 {
 	struct thr_info *thr;
-	int selected, gpu, i;
+	int i;
 	char checkin[40];
-	char input;
-
-	if (!opt_g_threads)
-		return;
-
-	opt_loginput = true;
-	immedok(logwin, true);
-	clear_logwin();
-retry:
-
-	for (gpu = 0; gpu < nDevs; gpu++) {
-		struct cgpu_info *cgpu = &gpus[gpu];
-		double displayed_rolling, displayed_total;
-		bool mhash_base = true;
-
-		displayed_rolling = cgpu->rolling;
-		displayed_total = cgpu->total_mhashes / cgpu_runtime(cgpu);
-		if (displayed_rolling < 1) {
-			displayed_rolling *= 1000;
-			displayed_total *= 1000;
-			mhash_base = false;
-		}
-
-		wlog("%"PRIpreprv": %.1f / %.1f %sh/s | A:%d  R:%d  HW:%d  U:%.2f/m  I:%d\n",
-			cgpu->proc_repr,
-			displayed_rolling, displayed_total, mhash_base ? "M" : "K",
-			cgpu->accepted, cgpu->rejected, cgpu->hw_errors,
-			cgpu->utility, cgpu->intensity);
+	double displayed_rolling;
+	bool mhash_base = !(cgpu->rolling < 1);
+	char logline[255];
+	strcpy(logline, ""); // In case it has no data
+	
+	tailsprintf(logline, "I:%s%d  ", (cgpu->dynamic ? "d" : ""), cgpu->intensity);
 #ifdef HAVE_ADL
-		if (gpus[gpu].has_adl) {
-			int engineclock = 0, memclock = 0, activity = 0, fanspeed = 0, fanpercent = 0, powertune = 0;
-			float temp = 0, vddc = 0;
-
-			if (gpu_stats(gpu, &temp, &engineclock, &memclock, &vddc, &activity, &fanspeed, &fanpercent, &powertune)) {
-				char logline[255];
-
-				strcpy(logline, ""); // In case it has no data
-				if (temp != -1)
-					sprintf(logline, "%.1f C  ", temp);
-				if (fanspeed != -1 || fanpercent != -1) {
-					tailsprintf(logline, "F: ");
-					if (fanpercent != -1)
-						tailsprintf(logline, "%d%% ", fanpercent);
+	if (cgpu->has_adl) {
+		int engineclock = 0, memclock = 0, activity = 0, fanspeed = 0, fanpercent = 0, powertune = 0;
+		float temp = 0, vddc = 0;
+
+		if (gpu_stats(cgpu->device_id, &temp, &engineclock, &memclock, &vddc, &activity, &fanspeed, &fanpercent, &powertune)) {
+			if (fanspeed != -1 || fanpercent != -1) {
+				tailsprintf(logline, "F: ");
+				if (fanpercent != -1)
+				{
+					tailsprintf(logline, "%d%% ", fanpercent);
 					if (fanspeed != -1)
 						tailsprintf(logline, "(%d RPM) ", fanspeed);
-					tailsprintf(logline, " ");
 				}
-				if (engineclock != -1)
-					tailsprintf(logline, "E: %d MHz  ", engineclock);
-				if (memclock != -1)
-					tailsprintf(logline, "M: %d MHz  ", memclock);
-				if (vddc != -1)
-					tailsprintf(logline, "V: %.3fV  ", vddc);
-				if (activity != -1)
-					tailsprintf(logline, "A: %d%%  ", activity);
-				if (powertune != -1)
-					tailsprintf(logline, "P: %d%%", powertune);
-				tailsprintf(logline, "\n");
-				_wlog(logline);
+				else
+					tailsprintf(logline, "%d RPM ", fanspeed);
+				tailsprintf(logline, " ");
 			}
+			if (engineclock != -1)
+				tailsprintf(logline, "E: %d MHz  ", engineclock);
+			if (memclock != -1)
+				tailsprintf(logline, "M: %d MHz  ", memclock);
+			if (vddc != -1)
+				tailsprintf(logline, "V: %.3fV  ", vddc);
+			if (activity != -1)
+				tailsprintf(logline, "A: %d%%  ", activity);
+			if (powertune != -1)
+				tailsprintf(logline, "P: %d%%", powertune);
 		}
+	}
 #endif
-		wlog("Last initialised: %s\n", cgpu->init);
-		wlog("Intensity: ");
-		if (gpus[gpu].dynamic)
-			wlog("Dynamic (only one thread in use)\n");
-		else
-			wlog("%d\n", gpus[gpu].intensity);
-		for (i = 0; i < mining_threads; i++) {
-			thr = get_thread(i);
-			if (thr->cgpu != cgpu)
-				continue;
-			get_datestamp(checkin, &thr->last);
-			displayed_rolling = thr->rolling;
-			if (!mhash_base)
-				displayed_rolling *= 1000;
-			wlog("Thread %d: %.1f %sh/s %s ", i, displayed_rolling, mhash_base ? "M" : "K" , cgpu->deven != DEV_DISABLED ? "Enabled" : "Disabled");
-			switch (cgpu->status) {
-				default:
-				case LIFE_WELL:
-					wlog("ALIVE");
-					break;
-				case LIFE_SICK:
-					wlog("SICK reported in %s", checkin);
-					break;
-				case LIFE_DEAD:
-					wlog("DEAD reported in %s", checkin);
-					break;
-				case LIFE_INIT:
-				case LIFE_NOSTART:
-					wlog("Never started");
-					break;
-			}
-			if (thr->pause)
-				wlog(" paused");
-			wlog("\n");
+	
+	wlogprint("%s\n", logline);
+	
+	wlogprint("Last initialised: %s\n", cgpu->init);
+	
+	for (i = 0; i < mining_threads; i++) {
+		thr = get_thread(i);
+		if (thr->cgpu != cgpu)
+			continue;
+		
+		get_datestamp(checkin, &thr->last);
+		displayed_rolling = thr->rolling;
+		if (!mhash_base)
+			displayed_rolling *= 1000;
+		sprintf(logline, "Thread %d: %.1f %sh/s %s ", i, displayed_rolling, mhash_base ? "M" : "K" , cgpu->deven != DEV_DISABLED ? "Enabled" : "Disabled");
+		switch (cgpu->status) {
+			default:
+			case LIFE_WELL:
+				tailsprintf(logline, "ALIVE");
+				break;
+			case LIFE_SICK:
+				tailsprintf(logline, "SICK reported in %s", checkin);
+				break;
+			case LIFE_DEAD:
+				tailsprintf(logline, "DEAD reported in %s", checkin);
+				break;
+			case LIFE_INIT:
+			case LIFE_NOSTART:
+				tailsprintf(logline, "Never started");
+				break;
 		}
-		wlog("\n");
+		if (thr->pause)
+			tailsprintf(logline, " paused");
+		wlogprint("%s\n", logline);
 	}
+}
 
-	wlogprint("[E]nable [D]isable [I]ntensity [R]estart GPU %s\n",adl_active ? "[C]hange settings" : "");
-
-	wlogprint("Or press any other key to continue\n");
-	logwin_update();
-	input = getch();
-
-	if (nDevs == 1)
-		selected = 0;
-	else
-		selected = -1;
-	if (!strncasecmp(&input, "e", 1)) {
-		struct cgpu_info *cgpu;
+static
+void opencl_tui_wlogprint_choices(struct cgpu_info *cgpu)
+{
+	wlogprint("[I]ntensity [R]estart GPU ");
+#ifdef HAVE_ADL
+	if (cgpu->has_adl)
+		wlogprint("[C]hange settings ");
+#endif
+}
 
-		if (selected)
-			selected = curses_int("Select GPU to enable");
-		if (selected < 0 || selected >= nDevs) {
-			wlogprint("Invalid selection\n");
-			goto retry;
-		}
-		cgpu = &gpus[selected];
-		if (gpus[selected].deven != DEV_DISABLED) {
-			wlogprint("Device already enabled\n");
-			goto retry;
-		}
-		gpus[selected].deven = DEV_ENABLED;
-		if (cgpu->status != LIFE_WELL) {
-			wlogprint("Must restart device before enabling it");
-			goto retry;
-		}
-		proc_enable(cgpu);
-		goto retry;
-	} if (!strncasecmp(&input, "d", 1)) {
-		if (selected)
-			selected = curses_int("Select GPU to disable");
-		if (selected < 0 || selected >= nDevs) {
-			wlogprint("Invalid selection\n");
-			goto retry;
-		}
-		if (gpus[selected].deven == DEV_DISABLED) {
-			wlogprint("Device already disabled\n");
-			goto retry;
-		}
-		gpus[selected].deven = DEV_DISABLED;
-		goto retry;
-	} else if (!strncasecmp(&input, "i", 1)) {
-		int intensity;
-		char *intvar;
-
-		if (selected)
-			selected = curses_int("Select GPU to change intensity on");
-		if (selected < 0 || selected >= nDevs) {
-			wlogprint("Invalid selection\n");
-			goto retry;
-		}
-		intvar = curses_input("Set GPU scan intensity (d or " _MIN_INTENSITY_STR " -> " _MAX_INTENSITY_STR ")");
-		if (!intvar) {
-			wlogprint("Invalid input\n");
-			goto retry;
-		}
-		if (!strncasecmp(intvar, "d", 1)) {
-			wlogprint("Dynamic mode enabled on gpu %d\n", selected);
-			gpus[selected].dynamic = true;
-			pause_dynamic_threads(selected);
+static
+const char *opencl_tui_handle_choice(struct cgpu_info *cgpu, int input)
+{
+	switch (input)
+	{
+		case 'i': case 'I':
+		{
+			int intensity;
+			char *intvar;
+
+			intvar = curses_input("Set GPU scan intensity (d or " _MIN_INTENSITY_STR " -> " _MAX_INTENSITY_STR ")");
+			if (!intvar)
+				return "Invalid intensity\n";
+			if (!strncasecmp(intvar, "d", 1)) {
+				cgpu->dynamic = true;
+				pause_dynamic_threads(cgpu->device_id);
+				free(intvar);
+				return "Dynamic mode enabled\n";
+			}
+			intensity = atoi(intvar);
 			free(intvar);
-			goto retry;
+			if (intensity < MIN_INTENSITY || intensity > MAX_INTENSITY)
+				return "Invalid intensity (out of range)\n";
+			cgpu->dynamic = false;
+			cgpu->intensity = intensity;
+			pause_dynamic_threads(cgpu->device_id);
+			return "Intensity changed\n";
 		}
-		intensity = atoi(intvar);
-		free(intvar);
-		if (intensity < MIN_INTENSITY || intensity > MAX_INTENSITY) {
-			wlogprint("Invalid selection\n");
-			goto retry;
-		}
-		gpus[selected].dynamic = false;
-		gpus[selected].intensity = intensity;
-		wlogprint("Intensity on gpu %d set to %d\n", selected, intensity);
-		pause_dynamic_threads(selected);
-		goto retry;
-	} else if (!strncasecmp(&input, "r", 1)) {
-		if (selected)
-			selected = curses_int("Select GPU to attempt to restart");
-		if (selected < 0 || selected >= nDevs) {
-			wlogprint("Invalid selection\n");
-			goto retry;
-		}
-		wlogprint("Attempting to restart threads of GPU %d\n", selected);
-		reinit_device(&gpus[selected]);
-		goto retry;
-	} else if (adl_active && (!strncasecmp(&input, "c", 1))) {
-		if (selected)
-			selected = curses_int("Select GPU to change settings on");
-		if (selected < 0 || selected >= nDevs) {
-			wlogprint("Invalid selection\n");
-			goto retry;
+		case 'r': case 'R':
+			reinit_device(cgpu);
+			return "Attempting to restart\n";
+		case 'c': case 'C':
+		{
+			char logline[256];
+			
+			clear_logwin();
+			get_statline3(logline, cgpu, true, true);
+			wattron(logwin, A_BOLD);
+			waddstr(logwin, logline);
+			wattroff(logwin, A_BOLD);
+			wlogprint("\n");
+			
+			change_gpusettings(cgpu->device_id);
+			return "";  // Force refresh
 		}
-		change_gpusettings(selected);
-		goto retry;
-	} else
-		clear_logwin();
-
-	immedok(logwin, false);
-	opt_loginput = false;
-}
-#else
-void manage_gpu(void)
-{
+	}
+	return NULL;
 }
+
 #endif
 
 
@@ -1408,7 +1337,7 @@ void *reinit_gpu(__maybe_unused void *userdata)
 #ifdef HAVE_OPENCL
 struct device_drv opencl_api;
 
-static void opencl_detect()
+static int opencl_autodetect()
 {
 #ifndef WIN32
 	if (!getenv("DISPLAY")) {
@@ -1419,7 +1348,7 @@ static void opencl_detect()
 
 	if (!load_opencl_symbols()) {
 		nDevs = 0;
-		return;
+		return 0;
 	}
 
 
@@ -1432,7 +1361,7 @@ static void opencl_detect()
 	}
 
 	if (!nDevs)
-		return;
+		return 0;
 
 	/* If opt_g_threads is not set, use default 1 thread on scrypt and
 	 * 2 for regular mining */
@@ -1478,6 +1407,16 @@ static void opencl_detect()
 
 	if (!opt_noadl)
 		init_adl(nDevs);
+	
+	return nDevs;
+}
+
+static void opencl_detect()
+{
+	RUNONCE();
+	
+	// This wrapper ensures users can specify -S opencl:noauto to disable it
+	noserial_detect(&opencl_api, opencl_autodetect);
 }
 
 static void reinit_opencl_device(struct cgpu_info *gpu)
@@ -1485,7 +1424,9 @@ static void reinit_opencl_device(struct cgpu_info *gpu)
 	tq_push(control_thr[gpur_thr_id].q, gpu);
 }
 
-static void get_opencl_statline_before(char *buf, struct cgpu_info *gpu)
+// FIXME: Legacy (called by TUI) for side effects
+static
+bool override_opencl_statline_temp(char *buf, struct cgpu_info *gpu, __maybe_unused bool per_processor)
 {
 #ifdef HAVE_SENSORS
 	struct opencl_device_data *data = gpu->device_data;
@@ -1506,33 +1447,18 @@ static void get_opencl_statline_before(char *buf, struct cgpu_info *gpu)
 				continue;
 			
 			gpu->temp = val;
-			tailsprintf(buf, "%5.1fC         | ", val);
-			return;
+			return false;
 		}
 	}
 #endif
 #ifdef HAVE_ADL
 	if (gpu->has_adl) {
 		int gpuid = gpu->device_id;
-		float gt = gpu_temp(gpuid);
-		int gf = gpu_fanspeed(gpuid);
-		int gp;
-
-		if (gt != -1)
-			tailsprintf(buf, "%5.1fC ", gt);
-		else
-			tailsprintf(buf, "       ");
-		if (gf != -1)
-			tailsprintf(buf, "%4dRPM ", gf);
-		else if ((gp = gpu_fanpercent(gpuid)) != -1)
-			tailsprintf(buf, "%3d%%    ", gp);
-		else
-			tailsprintf(buf, "        ");
-		tailsprintf(buf, "| ");
+		gpu_temp(gpuid);
+		gpu_fanspeed(gpuid);
 	}
-	else
 #endif
-		tailsprintf(buf, "               | ");
+	return false;
 }
 
 static struct api_data*
@@ -1835,7 +1761,12 @@ struct device_drv opencl_api = {
 	.name = "OCL",
 	.drv_detect = opencl_detect,
 	.reinit_device = reinit_opencl_device,
-	.get_statline_before = get_opencl_statline_before,
+	.override_statline_temp = override_opencl_statline_temp,
+#ifdef HAVE_CURSES
+	.proc_wlogprint_status = opencl_wlogprint_status,
+	.proc_tui_wlogprint_choices = opencl_tui_wlogprint_choices,
+	.proc_tui_handle_choice = opencl_tui_handle_choice,
+#endif
 	.get_api_extra_device_status = get_opencl_api_extra_device_status,
 	.thread_prepare = opencl_thread_prepare,
 	.thread_init = opencl_thread_init,

+ 14 - 54
driver-x6500.c

@@ -124,6 +124,9 @@ uint32_t x6500_get_register(struct jtag_port *jp, uint8_t addr)
 
 static bool x6500_foundusb(libusb_device *dev, const char *product, const char *serial)
 {
+	if (bfg_claim_libusb(&x6500_api, true, dev))
+		return false;
+	
 	struct cgpu_info *x6500;
 	x6500 = calloc(1, sizeof(*x6500));
 	x6500->drv = &x6500_api;
@@ -135,6 +138,7 @@ static bool x6500_foundusb(libusb_device *dev, const char *product, const char *
 	x6500->name = strdup(product);
 	x6500->cutofftemp = 85;
 	x6500->device_data = dev;
+	cgpu_copy_libusb_strings(x6500, dev);
 
 	return add_cgpu(x6500);
 }
@@ -224,7 +228,7 @@ x6500_fpga_upload_bitstream(struct cgpu_info *x6500, struct jtag_port *jp1)
 
 	applog(LOG_WARNING, "%s: Programming %s...",
 	       x6500->dev_repr, x6500->device_path);
-	x6500->status = LIFE_INIT;
+	x6500->status = LIFE_INIT2;
 	
 	// "Magic" jtag_port configured to access both FPGAs concurrently
 	struct jtag_port jpt = {
@@ -341,6 +345,7 @@ static bool x6500_thread_init(struct thr_info *thr)
 	notifier_init(thr->mutex_request);
 	pthread_cond_init(&x6500->device_cond, NULL);
 	
+	// This works because x6500_thread_init is only called for the first processor now that they're all using the same thread
 	for ( ; x6500; x6500 = x6500->next_proc)
 	{
 		thr = x6500->thr[0];
@@ -360,6 +365,7 @@ static bool x6500_thread_init(struct thr_info *thr)
 	jp->a = x6500->device_data;
 	x6500_jtag_set(jp, pinoffset);
 	thr->cgpu_data = fpga;
+	x6500->status = LIFE_INIT2;
 	
 	if (!jtag_reset(jp)) {
 		applog(LOG_ERR, "%s: JTAG reset failed",
@@ -389,7 +395,7 @@ static bool x6500_thread_init(struct thr_info *thr)
 		       x6500->proc_repr);
 		if (!x6500_fpga_upload_bitstream(x6500, jp))
 			return false;
-	} else if (opt_force_dev_init && x6500->status == LIFE_INIT) {
+	} else if (opt_force_dev_init && x6500 == x6500->device) {
 		applog(LOG_DEBUG, "%"PRIprepr": FPGA is already programmed, but --force-dev-init is set",
 		       x6500->proc_repr);
 		if (!x6500_fpga_upload_bitstream(x6500, jp))
@@ -557,57 +563,16 @@ static bool x6500_get_stats(struct cgpu_info *x6500)
 }
 
 static
-bool get_x6500_upload_percent(char *buf, struct cgpu_info *x6500)
+bool get_x6500_upload_percent(char *buf, struct cgpu_info *x6500, __maybe_unused bool per_processor)
 {
-	char info[18] = "               | ";
-
 	unsigned char pdone = *((unsigned char*)x6500->device_data - 1);
 	if (pdone != 101) {
-		sprintf(&info[1], "%3d%%", pdone);
-		info[5] = ' ';
-		strcat(buf, info);
+		tailsprintf(buf, "%3d%% ", pdone);
 		return true;
 	}
 	return false;
 }
 
-static
-void get_x6500_statline_before(char *buf, struct cgpu_info *x6500)
-{
-	if (get_x6500_upload_percent(buf, x6500))
-		return;
-
-	char info[18] = "               | ";
-	struct x6500_fpga_data *fpga = x6500->thr[0]->cgpu_data;
-
-	if (fpga->temp) {
-		sprintf(&info[1], "%.1fC", fpga->temp);
-		info[strlen(info)] = ' ';
-		strcat(buf, info);
-		return;
-	}
-	strcat(buf, "               | ");
-}
-
-static
-void get_x6500_dev_statline_before(char *buf, struct cgpu_info *x6500)
-{
-	if (get_x6500_upload_percent(buf, x6500))
-		return;
-
-	char info[18] = "               | ";
-	struct x6500_fpga_data *fpga0 = x6500->thr[0]->cgpu_data;
-	struct x6500_fpga_data *fpga1 = x6500->next_proc->thr[0]->cgpu_data;
-
-	if (x6500->temp) {
-		sprintf(&info[1], "%.1fC/%.1fC", fpga0->temp, fpga1->temp);
-		info[strlen(info)] = ' ';
-		strcat(buf, info);
-		return;
-	}
-	strcat(buf, "               | ");
-}
-
 static struct api_data*
 get_x6500_api_extra_device_status(struct cgpu_info *x6500)
 {
@@ -682,10 +647,10 @@ void x6500_job_start(struct thr_info *thr)
 	mt_job_transition(thr);
 	
 	if (opt_debug) {
-		char *xdata = bin2hex(thr->work->data, 80);
+		char xdata[161];
+		bin2hex(xdata, thr->work->data, 80);
 		applog(LOG_DEBUG, "%"PRIprepr": Started work: %s",
 		       x6500->proc_repr, xdata);
-		free(xdata);
 	}
 
 	uint32_t usecs = 0x80000000 / fpga->dclk.freqM;
@@ -745,11 +710,7 @@ int64_t x6500_process_results(struct thr_info *thr, struct work *work)
 				       x6500->proc_repr,
 				       (unsigned long)nonce);
 			} else {
-				applog(LOG_DEBUG, "%"PRIprepr": Nonce with H not zero  : %08lx",
-				       x6500->proc_repr,
-				       (unsigned long)nonce);
-				++hw_errors;
-				++x6500->hw_errors;
+				inc_hw_errors(thr, work, nonce);
 
 				dclk_gotNonces(&fpga->dclk);
 				dclk_errorCount(&fpga->dclk, 1.);
@@ -786,11 +747,10 @@ struct device_drv x6500_api = {
 	.dname = "x6500",
 	.name = "XBS",
 	.drv_detect = x6500_detect,
-	.get_dev_statline_before = get_x6500_dev_statline_before,
 	.thread_prepare = x6500_prepare,
 	.thread_init = x6500_thread_init,
 	.get_stats = x6500_get_stats,
-	.get_statline_before = get_x6500_statline_before,
+	.override_statline_temp = get_x6500_upload_percent,
 	.get_api_extra_device_status = get_x6500_api_extra_device_status,
 	.poll = x6500_fpga_poll,
 	.minerloop = minerloop_async,

+ 7 - 16
driver-ztex.c

@@ -61,6 +61,9 @@ static struct cgpu_info *ztex_setup(struct libztex_device *dev, int fpgacount)
 	ztex->device_ztex = dev;
 	ztex->procs = fpgacount;
 	ztex->threads = fpgacount;
+	ztex->dev_manufacturer = dev->dev_manufacturer;
+	ztex->dev_product = dev->dev_product;
+	ztex->dev_serial = (char*)&dev->snString[0];
 	add_cgpu(ztex);
 	strcpy(ztex->device_ztex->repr, ztex->dev_repr);
 	ztex->name = fpganame;
@@ -85,6 +88,8 @@ static int ztex_autodetect(void)
 
 	for (i = 0; i < cnt; i++) {
 		ztex_master = ztex_devices[i]->dev;
+		if (bfg_claim_usb(&ztex_drv, true, ztex_master->usbbus, ztex_master->usbaddress))
+			return false;
 		ztex_master->root = ztex_master;
 		fpgacount = libztex_numberOfFpgas(ztex_master);
 		ztex_master->handles = fpgacount;
@@ -266,8 +271,7 @@ static int64_t ztex_scanhash(struct thr_info *thr, struct work *work,
 				if (count > 2)
 					dclk_errorCount(&ztex->dclk, 1.0 / ztex->numNonces);
 
-				thr->cgpu->hw_errors++;
-				++hw_errors;
+				inc_hw_errors_only(thr);
 			}
 
 			for (j=0; j<=ztex->extraSolutions; j++) {
@@ -319,19 +323,6 @@ static int64_t ztex_scanhash(struct thr_info *thr, struct work *work,
 	return noncecnt;
 }
 
-static void ztex_statline_before(char *buf, struct cgpu_info *cgpu)
-{
-	char before[] = "               ";
-	if (cgpu->device_ztex) {
-		const char *snString = (char*)cgpu->device_ztex->snString;
-		size_t snStringLen = strlen(snString);
-		if (snStringLen > 14)
-			snStringLen = 14;
-		memcpy(before, snString, snStringLen);
-	}
-	tailsprintf(buf, "%s| ", &before[0]);
-}
-
 static struct api_data*
 get_ztex_drv_extra_device_status(struct cgpu_info *ztex)
 {
@@ -375,6 +366,7 @@ static bool ztex_prepare(struct thr_info *thr)
 	ztex_releaseFpga(ztex);
 	notifier_init(thr->work_restart_notifier);
 	applog(LOG_DEBUG, "%"PRIpreprv": prepare", cgpu->proc_repr);
+	cgpu->status = LIFE_INIT2;
 	return true;
 }
 
@@ -407,7 +399,6 @@ struct device_drv ztex_drv = {
 	.dname = "ztex",
 	.name = "ZTX",
 	.drv_detect = ztex_detect,
-	.get_statline_before = ztex_statline_before,
 	.get_api_extra_device_status = get_ztex_drv_extra_device_status,
 	.thread_init = ztex_prepare,
 	.scanhash = ztex_scanhash,

+ 1 - 2
findnonce.c

@@ -156,8 +156,7 @@ static void *postcalc_hash(void *userdata)
 	if (unlikely(pcd->res[found] & ~found)) {
 		applog(LOG_WARNING, "%"PRIpreprv": invalid nonce count - HW error",
 				thr->cgpu->proc_repr);
-		hw_errors++;
-		thr->cgpu->hw_errors++;
+		inc_hw_errors_only(thr);
 		pcd->res[found] &= found;
 	}
 

+ 289 - 125
fpgautils.c

@@ -16,6 +16,7 @@
 #include <winsock2.h>
 #endif
 
+#include <ctype.h>
 #include <stdarg.h>
 #include <stdint.h>
 #include <stdlib.h>
@@ -23,6 +24,10 @@
 #include <dirent.h>
 #include <string.h>
 
+#ifdef HAVE_LIBUSB
+#include <libusb.h>
+#endif
+
 #include "miner.h"
 
 #ifndef WIN32
@@ -100,7 +105,62 @@ bool search_needles(const char *haystack, va_list needles)
 
 #define SEARCH_NEEDLES(haystack)  search_needles(haystack, needles)
 
+static
+int _detectone_wrap(const detectone_func_t detectone, const char * const param, const char *fname)
+{
+	if (bfg_claim_serial(NULL, false, param))
+	{
+		applog(LOG_DEBUG, "%s: %s is already claimed, skipping probe", fname, param);
+		return 0;
+	}
+	return detectone(param);
+}
+#define detectone(param)  _detectone_wrap(detectone, param, __func__)
+
+struct detectone_meta_info_t detectone_meta_info;
+
+static
+void clear_detectone_meta_info(void)
+{
+	detectone_meta_info = (struct detectone_meta_info_t){
+		.manufacturer = NULL,
+	};
+}
+
 #ifdef HAVE_LIBUDEV
+static
+void _decode_udev_enc(char *o, const char *s)
+{
+	while(s[0])
+	{
+		if (s[0] == '\\' && s[1] == 'x' && s[2] && s[3])
+		{
+			hex2bin((void*)(o++), &s[2], 1);
+			s += 4;
+		}
+		else
+			(o++)[0] = (s++)[0];
+	}
+	o[0] = '\0';
+}
+
+static
+char *_decode_udev_enc_dup(const char *s)
+{
+	if (!s)
+		return NULL;
+	
+	char *o = malloc(strlen(s));
+	if (!o)
+	{
+		applog(LOG_ERR, "Failed to malloc in _decode_udev_enc_dup");
+		return NULL;
+	}
+	
+	_decode_udev_enc(o, s);
+	return o;
+}
+
 static
 int _serial_autodetect_udev(detectone_func_t detectone, va_list needles)
 {
@@ -126,14 +186,24 @@ int _serial_autodetect_udev(detectone_func_t detectone, va_list needles)
 			continue;
 		}
 
+		detectone_meta_info = (struct detectone_meta_info_t){
+			.manufacturer = _decode_udev_enc_dup(udev_device_get_property_value(device, "ID_VENDOR_ENC")),
+			.product = _decode_udev_enc_dup(udev_device_get_property_value(device, "ID_MODEL_ENC")),
+			.serial = _decode_udev_enc_dup(udev_device_get_property_value(device, "ID_SERIAL_SHORT")),
+		};
+		
 		const char *devpath = udev_device_get_devnode(device);
 		if (devpath && detectone(devpath))
 			++found;
 
+		free((void*)detectone_meta_info.manufacturer);
+		free((void*)detectone_meta_info.product);
+		free((void*)detectone_meta_info.serial);
 		udev_device_unref(device);
 	}
 	udev_enumerate_unref(enumerate);
 	udev_unref(udev);
+	clear_detectone_meta_info();
 
 	return found;
 }
@@ -152,6 +222,9 @@ int _serial_autodetect_devserial(detectone_func_t detectone, va_list needles)
 	char *devfile = devpath + sizeof(udevdir);
 	char found = 0;
 
+	// No way to split this out of the filename reliably
+	clear_detectone_meta_info();
+	
 	D = opendir(udevdir);
 	if (!D)
 		return 0;
@@ -174,16 +247,40 @@ int _serial_autodetect_devserial(detectone_func_t detectone, va_list needles)
 #endif
 
 #ifndef WIN32
+static
+char *_sysfs_do_read(char *buf, size_t bufsz, const char *devpath, char *devfile, const char *append)
+{
+	FILE *F;
+	
+	strcpy(devfile, append);
+	F = fopen(devpath, "r");
+	if (F)
+	{
+		if (fgets(buf, bufsz, F))
+		{
+			size_t L = strlen(buf);
+			while (isspace(buf[--L]))
+				buf[L] = '\0';
+		}
+		else
+			buf[0] = '\0';
+		fclose(F);
+	}
+	else
+		buf[0] = '\0';
+	
+	return buf[0] ? buf : NULL;
+}
+
 static
 int _serial_autodetect_sysfs(detectone_func_t detectone, va_list needles)
 {
 	DIR *D, *DS, *DT;
-	FILE *F;
 	struct dirent *de;
 	const char devroot[] = "/sys/bus/usb/devices";
 	const size_t devrootlen = sizeof(devroot) - 1;
 	char devpath[sizeof(devroot) + (NAME_MAX * 3)];
-	char buf[0x100];
+	char ttybuf[0x10], manuf[0x40], prod[0x40], serial[0x40];
 	char *devfile, *upfile;
 	char found = 0;
 	size_t len, len2;
@@ -199,12 +296,11 @@ int _serial_autodetect_sysfs(detectone_func_t detectone, va_list needles)
 		upfile = &devpath[devrootlen + 1];
 		memcpy(upfile, de->d_name, len);
 		devfile = upfile + len;
-		strcpy(devfile, "/product");
-		F = fopen(devpath, "r");
-		if (!(F && fgets(buf, sizeof(buf), F)))
+		
+		if (!_sysfs_do_read(prod, sizeof(prod), devpath, devfile, "/product"))
 			continue;
 		
-		if (!SEARCH_NEEDLES(buf))
+		if (!SEARCH_NEEDLES(prod))
 			continue;
 		
 		devfile[0] = '\0';
@@ -214,7 +310,7 @@ int _serial_autodetect_sysfs(detectone_func_t detectone, va_list needles)
 		devfile[0] = '/';
 		++devfile;
 		
-		memcpy(buf, "/dev/", 5);
+		memcpy(ttybuf, "/dev/", 5);
 		
 		while ( (de = readdir(DS)) )
 		{
@@ -235,8 +331,15 @@ int _serial_autodetect_sysfs(detectone_func_t detectone, va_list needles)
 				if (strncmp(&de->d_name[3], "USB", 3) && strncmp(&de->d_name[3], "ACM", 3))
 					continue;
 				
-				strcpy(&buf[5], de->d_name);
-				if (detectone(buf))
+				
+				detectone_meta_info = (struct detectone_meta_info_t){
+					.manufacturer = _sysfs_do_read(manuf, sizeof(manuf), devpath, devfile, "/manufacturer"),
+					.product = prod,
+					.serial = _sysfs_do_read(serial, sizeof(serial), devpath, devfile, "/serial"),
+				};
+				
+				strcpy(&ttybuf[5], de->d_name);
+				if (detectone(ttybuf))
 					++found;
 			}
 			closedir(DT);
@@ -244,6 +347,7 @@ int _serial_autodetect_sysfs(detectone_func_t detectone, va_list needles)
 		closedir(DS);
 	}
 	closedir(D);
+	clear_detectone_meta_info();
 	
 	return found;
 }
@@ -259,6 +363,16 @@ int _serial_autodetect_sysfs(detectone_func_t detectone, va_list needles)
 	}  \
 } while(0)
 
+#ifdef UNTESTED_FTDI_DETECTONE_META_INFO
+static
+char *_ftdi_get_string(char *buf, int i, DWORD flags)
+{
+	if (FT_OK != FT_ListDevices((PVOID)i, buf, FT_LIST_BY_INDEX | flags))
+		return NULL;
+	return buf[0] ? buf : NULL;
+}
+#endif
+
 static
 int _serial_autodetect_ftdi(detectone_func_t detectone, va_list needles)
 {
@@ -266,6 +380,9 @@ int _serial_autodetect_ftdi(detectone_func_t detectone, va_list needles)
 	char *devpathnum = &devpath[7];
 	char **bufptrs;
 	char *buf;
+#ifdef UNTESTED_FTDI_DETECTONE_META_INFO
+	char manuf[64], serial[64];
+#endif
 	int found = 0;
 	DWORD i;
 
@@ -300,6 +417,7 @@ int _serial_autodetect_ftdi(detectone_func_t detectone, va_list needles)
 		goto out;
 	}
 	
+	clear_detectone_meta_info();
 	for (i = numDevs; i > 0; ) {
 		--i;
 		bufptrs[i][64] = '\0';
@@ -317,6 +435,13 @@ int _serial_autodetect_ftdi(detectone_func_t detectone, va_list needles)
 			continue;
 		
 		applog(LOG_ERR, "FT_GetComPortNumber(%p (%ld), %ld)", ftHandle, (long)i, (long)lComPortNumber);
+#ifdef UNTESTED_FTDI_DETECTONE_META_INFO
+		detectone_meta_info = (struct detectone_meta_info_t){
+			.product = bufptrs[i],
+			.serial = _ftdi_get_string(serial, i, FT_OPEN_BY_SERIAL_NUMBER),
+		};
+#endif
+		
 		sprintf(devpathnum, "%d", (int)lComPortNumber);
 		
 		if (detectone(devpath))
@@ -324,6 +449,7 @@ int _serial_autodetect_ftdi(detectone_func_t detectone, va_list needles)
 	}
 
 out:
+	clear_detectone_meta_info();
 	dlclose(dll);
 	return found;
 }
@@ -331,6 +457,8 @@ out:
 #	define _serial_autodetect_ftdi(...)  (0)
 #endif
 
+#undef detectone
+
 int _serial_autodetect(detectone_func_t detectone, ...)
 {
 	int rv;
@@ -347,19 +475,18 @@ int _serial_autodetect(detectone_func_t detectone, ...)
 	return rv;
 }
 
-struct device_drv *serial_claim(const char *devpath, struct device_drv *api);
-
 int _serial_detect(struct device_drv *api, detectone_func_t detectone, autoscan_func_t autoscan, int flags)
 {
 	struct string_elist *iter, *tmp;
 	const char *dev, *colon;
-	bool inhibitauto = false;
+	bool inhibitauto = flags & 4;
 	char found = 0;
 	bool forceauto = flags & 1;
 	bool hasname;
 	size_t namel = strlen(api->name);
 	size_t dnamel = strlen(api->dname);
 
+	clear_detectone_meta_info();
 	DL_FOREACH_SAFE(scan_devices, iter, tmp) {
 		dev = iter->string;
 		if ((colon = strchr(dev, ':')) && colon[1] != '\0') {
@@ -404,11 +531,27 @@ int _serial_detect(struct device_drv *api, detectone_func_t detectone, autoscan_
 	return found;
 }
 
+enum bfg_device_bus {
+	BDB_SERIAL,
+	BDB_USB,
+};
+
+// TODO: claim USB side of USB-Serial devices
+typedef
+struct my_dev_t {
+	enum bfg_device_bus bus;
+	union {
+		struct {
+			uint8_t usbbus;
+			uint8_t usbaddr;
+		};
 #ifndef WIN32
-typedef dev_t my_dev_t;
+		dev_t dev;
 #else
-typedef int my_dev_t;
+		int com;
 #endif
+	};
+} my_dev_t;
 
 struct _device_claim {
 	struct device_drv *drv;
@@ -416,43 +559,100 @@ struct _device_claim {
 	UT_hash_handle hh;
 };
 
-struct device_drv *serial_claim(const char *devpath, struct device_drv *api)
+static
+struct device_drv *bfg_claim_any(struct device_drv * const api, const char * const verbose, const my_dev_t * const dev)
 {
 	static struct _device_claim *claims = NULL;
 	struct _device_claim *c;
-	my_dev_t dev;
+	
+	HASH_FIND(hh, claims, dev, sizeof(*dev), c);
+	if (c)
+	{
+		if (verbose)
+			applog(LOG_DEBUG, "%s device %s already claimed by other driver: %s",
+			       api->dname, verbose, c->drv->dname);
+		return c->drv;
+	}
+	
+	if (!api)
+		return NULL;
+	
+	c = malloc(sizeof(*c));
+	c->dev = *dev;
+	c->drv = api;
+	HASH_ADD(hh, claims, dev, sizeof(*dev), c);
+	return NULL;
+}
 
+struct device_drv *bfg_claim_serial(struct device_drv * const api, const bool verbose, const char * const devpath)
+{
+	my_dev_t dev;
+	
+	memset(&dev, 0, sizeof(dev));
+	dev.bus = BDB_SERIAL;
 #ifndef WIN32
 	{
 		struct stat my_stat;
 		if (stat(devpath, &my_stat))
 			return NULL;
-		dev = my_stat.st_rdev;
+		dev.dev = my_stat.st_rdev;
 	}
 #else
 	{
 		char *p = strstr(devpath, "COM"), *p2;
 		if (!p)
 			return NULL;
-		dev = strtol(&p[3], &p2, 10);
+		dev.com = strtol(&p[3], &p2, 10);
 		if (p2 == p)
 			return NULL;
 	}
 #endif
+	
+	return bfg_claim_any(api, (verbose ? devpath : NULL), &dev);
+}
 
-	HASH_FIND(hh, claims, &dev, sizeof(dev), c);
-	if (c)
-		return c->drv;
-
-	if (!api)
-		return NULL;
+struct device_drv *bfg_claim_usb(struct device_drv * const api, const bool verbose, const uint8_t usbbus, const uint8_t usbaddr)
+{
+	my_dev_t dev;
+	char *desc = NULL;
+	
+	// We should be able to just initialize a const my_dev_t for this, but Xcode's clang is broken
+	// Affected: Apple LLVM version 4.2 (clang-425.0.28) (based on LLVM 3.2svn) AKA Xcode 4.6.3
+	// Works with const: GCC 4.6.3, LLVM 3.1
+	memset(&dev, 0, sizeof(dev));
+	dev.bus = BDB_USB;
+	dev.usbbus = usbbus;
+	dev.usbaddr = usbaddr;
+	
+	if (verbose)
+	{
+		desc = alloca(3 + 1 + 3 + 1);
+		sprintf(desc, "%03u:%03u", (unsigned)usbbus, (unsigned)usbaddr);
+	}
+	
+	return bfg_claim_any(api, desc, &dev);
+}
 
-	c = malloc(sizeof(*c));
-	c->dev = dev;
-	c->drv = api;
-	HASH_ADD(hh, claims, dev, sizeof(dev), c);
-	return NULL;
+#ifdef HAVE_LIBUSB
+void cgpu_copy_libusb_strings(struct cgpu_info *cgpu, libusb_device *usb)
+{
+	unsigned char buf[0x20];
+	libusb_device_handle *h;
+	struct libusb_device_descriptor desc;
+	
+	if (LIBUSB_SUCCESS != libusb_open(usb, &h))
+		return;
+	if (libusb_get_device_descriptor(usb, &desc))
+		return;
+	
+	if ((!cgpu->dev_manufacturer) && libusb_get_string_descriptor_ascii(h, desc.iManufacturer, buf, sizeof(buf)) >= 0)
+		cgpu->dev_manufacturer = strdup((void *)buf);
+	if ((!cgpu->dev_product) && libusb_get_string_descriptor_ascii(h, desc.iProduct, buf, sizeof(buf)) >= 0)
+		cgpu->dev_product = strdup((void *)buf);
+	if ((!cgpu->dev_serial) && libusb_get_string_descriptor_ascii(h, desc.iSerialNumber, buf, sizeof(buf)) >= 0)
+		cgpu->dev_serial = strdup((void *)buf);
 }
+#endif
 
 // 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
@@ -468,68 +668,12 @@ struct device_drv *serial_claim(const char *devpath, struct device_drv *api)
 int tiospeed(speed_t speed)
 {
 	switch (speed) {
-	case B0:
-		return 0;
-	case B50:
-		return 50;
-	case B75:
-		return 75;
-	case B110:
-		return 110;
-	case B134:
-		return 134;
-	case B150:
-		return 150;
-	case B200:
-		return 200;
-	case B300:
-		return 300;
-	case B600:
-		return 600;
-	case B1200:
-		return 1200;
-	case B1800:
-		return 1800;
-	case B2400:
-		return 2400;
-	case B4800:
-		return 4800;
-	case B9600:
-		return 9600;
-	case B19200:
-		return 19200;
-	case B38400:
-		return 38400;
-	case B57600:
-		return 57600;
-	case B115200:
-		return 115200;
-	case B230400:
-		return 230400;
-	case B460800:
-		return 460800;
-	case B500000:
-		return 500000;
-	case B576000:
-		return 576000;
-	case B921600:
-		return 921600;
-	case B1000000:
-		return 1000000;
-	case B1152000:
-		return 1152000;
-	case B1500000:
-		return 1500000;
-	case B2000000:
-		return 2000000;
-	case B2500000:
-		return 2500000;
-	case B3000000:
-		return 3000000;
-	case B3500000:
-		return 3500000;
-	case B4000000:
-		return 4000000;
+#define IOSPEED(baud)  \
+		case B ## baud:  \
+			return baud;  \
+// END
+#include "iospeeds.h"
+#undef IOSPEED
 	default:
 		return -1;
 	}
@@ -619,8 +763,37 @@ void termios_debug(const char *devpath, struct termios *my_termios, const char *
 #endif
 			);
 }
-#endif
-#endif
+#endif  /* TERMIOS_DEBUG */
+
+speed_t tiospeed_t(int baud)
+{
+	switch (baud) {
+#define IOSPEED(baud)  \
+		case baud:  \
+			return B ## baud;  \
+// END
+#include "iospeeds.h"
+#undef IOSPEED
+	default:
+		return B0;
+	}
+}
+
+#endif  /* WIN32 */
+
+bool valid_baud(int baud)
+{
+	switch (baud) {
+#define IOSPEED(baud)  \
+		case baud:  \
+			return true;  \
+// END
+#include "iospeeds.h"
+#undef IOSPEED
+		default:
+			return false;
+	}
+}
 
 /* NOTE: Linux only supports uint8_t (decisecond) timeouts; limiting it in
  *       this interface buys us warnings when bad constants are passed in.
@@ -680,7 +853,7 @@ int serial_open(const char *devpath, unsigned long baud, uint8_t timeout, bool p
 		if (errno == EACCES)
 			applog(LOG_ERR, "Do not have user privileges required to open %s", devpath);
 		else
-			applog(LOG_DEBUG, "Open %s failed, errno:%d", devpath, errno);
+			applog(LOG_DEBUG, "Open %s failed: %s", devpath, bfg_strerror(errno, BST_ERRNO));
 
 		return -1;
 	}
@@ -693,30 +866,16 @@ int serial_open(const char *devpath, unsigned long baud, uint8_t timeout, bool p
 	termios_debug(devpath, &my_termios, "before");
 #endif
 
-	switch (baud) {
-	case 0:
-		break;
-	case 19200:
-		cfsetispeed(&my_termios, B19200);
-		cfsetospeed(&my_termios, B19200);
-		break;
-	case 38400:
-		cfsetispeed(&my_termios, B38400);
-		cfsetospeed(&my_termios, B38400);
-		break;
-	case 57600:
-		cfsetispeed(&my_termios, B57600);
-		cfsetospeed(&my_termios, B57600);
-		break;
-	case 115200:
-		cfsetispeed(&my_termios, B115200);
-		cfsetospeed(&my_termios, B115200);
-		break;
-	// TODO: try some higher speeds with the Icarus and BFL to see
-	// if they support them and if setting them makes any difference
-	// N.B. B3000000 doesn't work on Icarus
-	default:
-		applog(LOG_WARNING, "Unrecognized baud rate: %lu", baud);
+	if (baud)
+	{
+		speed_t speed = tiospeed_t(baud);
+		if (speed == B0)
+			applog(LOG_WARNING, "Unrecognized baud rate: %lu", baud);
+		else
+		{
+			cfsetispeed(&my_termios, speed);
+			cfsetospeed(&my_termios, speed);
+		}
 	}
 
 	my_termios.c_cflag &= ~(CSIZE | PARENB);
@@ -820,7 +979,7 @@ FILE *open_bitstream(const char *dname, const char *filename)
 
 #define check_magic(L)  do {  \
 	if (1 != fread(buf, 1, 1, f))  \
-		bailout(LOG_ERR, "%s: Error reading firmware ('%c')",  \
+		bailout(LOG_ERR, "%s: Error reading bitstream ('%c')",  \
 		        repr, L);  \
 	if (buf[0] != L)  \
 		bailout(LOG_ERR, "%s: Firmware has wrong magic ('%c')",  \
@@ -829,14 +988,14 @@ FILE *open_bitstream(const char *dname, const char *filename)
 
 #define read_str(eng)  do {  \
 	if (1 != fread(buf, 2, 1, f))  \
-		bailout(LOG_ERR, "%s: Error reading firmware (" eng " len)",  \
+		bailout(LOG_ERR, "%s: Error reading bitstream (" eng " len)",  \
 		        repr);  \
 	len = (ubuf[0] << 8) | ubuf[1];  \
 	if (len >= sizeof(buf))  \
 		bailout(LOG_ERR, "%s: Firmware " eng " too long",  \
 		        repr);  \
 	if (1 != fread(buf, len, 1, f))  \
-		bailout(LOG_ERR, "%s: Error reading firmware (" eng ")",  \
+		bailout(LOG_ERR, "%s: Error reading bitstream (" eng ")",  \
 		        repr);  \
 	buf[len] = '\0';  \
 } while(0)
@@ -850,10 +1009,15 @@ FILE *open_xilinx_bitstream(const char *dname, const char *repr, const char *fwf
 
 	FILE *f = open_bitstream(dname, fwfile);
 	if (!f)
-		bailout(LOG_ERR, "%s: Error opening firmware file %s",
+	{
+		applog(LOG_ERR, "%s: Error opening bitstream file %s",
 		        repr, fwfile);
+		applog(LOG_ERR, "%s: Did you install the necessary bitstream package?",
+		       repr);
+		return NULL;
+	}
 	if (1 != fread(buf, 2, 1, f))
-		bailout(LOG_ERR, "%s: Error reading firmware (magic)",
+		bailout(LOG_ERR, "%s: Error reading bitstream (magic)",
 		        repr);
 	if (buf[0] || buf[1] != 9)
 		bailout(LOG_ERR, "%s: Firmware has wrong magic (9)",
@@ -872,7 +1036,7 @@ FILE *open_xilinx_bitstream(const char *dname, const char *repr, const char *fwf
 		++p;
 	unsigned long fwusercode = (unsigned long)strtoll(p, &p, 16);
 	if (p[0] != '\0')
-		bailout(LOG_ERR, "%s: Bad usercode in firmware file",
+		bailout(LOG_ERR, "%s: Bad usercode in bitstream file",
 		        repr);
 	if (fwusercode == 0xffffffff)
 		bailout(LOG_ERR, "%s: Firmware doesn't support user code",
@@ -889,7 +1053,7 @@ FILE *open_xilinx_bitstream(const char *dname, const char *repr, const char *fwf
 	applog(LOG_DEBUG, "  Build time: %s", buf);
 	check_magic('e');
 	if (1 != fread(buf, 4, 1, f))
-		bailout(LOG_ERR, "%s: Error reading firmware (data len)",
+		bailout(LOG_ERR, "%s: Error reading bitstream (data len)",
 		        repr);
 	len = ((unsigned long)ubuf[0] << 24) | ((unsigned long)ubuf[1] << 16) | (ubuf[2] << 8) | ubuf[3];
 	applog(LOG_DEBUG, "  Bitstream size: %lu", len);

+ 25 - 1
fpgautils.h

@@ -6,9 +6,22 @@
 #include <stdio.h>
 #include <unistd.h>
 
+#ifdef HAVE_LIBUSB
+#include <libusb.h>
+#endif
+
 struct device_drv;
 struct cgpu_info;
 
+struct detectone_meta_info_t {
+	const char *manufacturer;
+	const char *product;
+	const char *serial;
+};
+
+// NOTE: Should detectone become run multithreaded, this will become a threadsafe #define
+extern struct detectone_meta_info_t detectone_meta_info;
+
 typedef bool(*detectone_func_t)(const char*);
 typedef int(*autoscan_func_t)();
 
@@ -25,10 +38,20 @@ extern int _serial_detect(struct device_drv *api, detectone_func_t, autoscan_fun
 	_serial_detect(api, detectone,     NULL, 2)
 #define noserial_detect(api, autoscan)  \
 	_serial_detect(api, NULL     , autoscan, 0)
+#define noserial_detect_manual(api, autoscan)  \
+	_serial_detect(api, NULL     , autoscan, 4)
 extern int _serial_autodetect(detectone_func_t, ...);
 #define serial_autodetect(...)  _serial_autodetect(__VA_ARGS__, NULL)
 
-extern struct device_drv *serial_claim(const char *devpath, struct device_drv *);
+extern struct device_drv *bfg_claim_serial(struct device_drv * const, const bool verbose, const char * const devpath);
+#define serial_claim(devpath, drv)    bfg_claim_serial(drv, false, devpath)
+#define serial_claim_v(devpath, drv)  bfg_claim_serial(drv, true , devpath)
+extern struct device_drv *bfg_claim_usb(struct device_drv * const, const bool verbose, const uint8_t usbbus, const uint8_t usbaddr);
+#define bfg_claim_libusb(api, verbose, dev)  bfg_claim_usb(api, verbose, libusb_get_bus_number(dev), libusb_get_device_address(dev))
+
+#ifdef HAVE_LIBUSB
+extern void cgpu_copy_libusb_strings(struct cgpu_info *, libusb_device *);
+#endif
 
 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);
@@ -42,5 +65,6 @@ extern FILE *open_bitstream(const char *dname, const char *filename);
 extern FILE *open_xilinx_bitstream(const char *dname, const char *repr, const char *fwfile, unsigned long *out_len);
 
 extern int get_serial_cts(int fd);
+extern bool valid_baud(int baud);
 
 #endif

+ 15 - 5
ft232r.c

@@ -63,12 +63,13 @@ void ft232r_scan()
 	struct ft232r_device_info *info;
 	int err;
 	unsigned char buf[0x100];
+	int skipped = 0;
 
 	ft232r_scan_free();
 
 	count = libusb_get_device_list(NULL, &list);
 	if (unlikely(count < 0)) {
-		applog(LOG_ERR, "ft232r_scan: Error getting USB device list: %s", libusb_error_name(count));
+		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;
 	}
@@ -76,9 +77,15 @@ void ft232r_scan()
 	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]))
+		{
+			++skipped;
+			continue;
+		}
+		
 		err = libusb_get_device_descriptor(list[i], &desc);
 		if (unlikely(err)) {
-			applog(LOG_ERR, "ft232r_scan: Error getting device descriptor: %s", libusb_error_name(err));
+			applog(LOG_ERR, "ft232r_scan: Error getting device descriptor: %s", bfg_strerror(err, BST_LIBUSB));
 			continue;
 		}
 		if (!(desc.idVendor == FT232R_IDVENDOR && desc.idProduct == FT232R_IDPRODUCT)) {
@@ -88,14 +95,14 @@ void ft232r_scan()
 
 		err = libusb_open(list[i], &handle);
 		if (unlikely(err)) {
-			applog(LOG_ERR, "ft232r_scan: Error opening device: %s", libusb_error_name(err));
+			applog(LOG_ERR, "ft232r_scan: Error opening device: %s", bfg_strerror(err, BST_LIBUSB));
 			continue;
 		}
 
 		n = libusb_get_string_descriptor_ascii(handle, desc.iProduct, buf, sizeof(buf)-1);
 		if (unlikely(n < 0)) {
 			libusb_close(handle);
-			applog(LOG_ERR, "ft232r_scan: Error getting iProduct string: %s", libusb_error_name(n));
+			applog(LOG_ERR, "ft232r_scan: Error getting iProduct string: %s", bfg_strerror(n, BST_LIBUSB));
 			continue;
 		}
 		buf[n] = '\0';
@@ -105,7 +112,7 @@ void ft232r_scan()
 		n = libusb_get_string_descriptor_ascii(handle, desc.iSerialNumber, buf, sizeof(buf)-1);
 		libusb_close(handle);
 		if (unlikely(n < 0)) {
-			applog(LOG_ERR, "ft232r_scan: Error getting iSerialNumber string: %s", libusb_error_name(n));
+			applog(LOG_ERR, "ft232r_scan: Error getting iSerialNumber string: %s", bfg_strerror(n, BST_LIBUSB));
 			n = 0;
 		}
 		buf[n] = '\0';
@@ -118,6 +125,9 @@ void ft232r_scan()
 
 	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)

+ 2 - 0
icarus-common.h

@@ -35,6 +35,8 @@
 // keeping a ongoing average of recent data
 #define INFO_HISTORY 10
 
+extern struct device_drv icarus_drv;
+
 struct ICARUS_HISTORY {
 	struct timeval finish;
 	double sumXiTi;

+ 37 - 0
iospeeds.h

@@ -0,0 +1,37 @@
+IOSPEED(0)
+IOSPEED(50)
+IOSPEED(110)
+IOSPEED(134)
+IOSPEED(200)
+
+IOSPEED(75)
+IOSPEED(150)
+IOSPEED(300)
+IOSPEED(600)
+IOSPEED(1200)
+IOSPEED(1800)
+IOSPEED(2400)
+IOSPEED(4800)
+IOSPEED(9600)
+IOSPEED(19200)
+IOSPEED(38400)
+
+IOSPEED(57600)
+IOSPEED(115200)
+IOSPEED(230400)
+IOSPEED(460800)
+IOSPEED(921600)
+
+IOSPEED(576000)
+IOSPEED(1152000)
+
+IOSPEED(1500000)
+IOSPEED(3000000)
+
+IOSPEED(500000)
+IOSPEED(1000000)
+IOSPEED(2000000)
+IOSPEED(4000000)
+
+IOSPEED(2500000)
+IOSPEED(3500000)

+ 2 - 2
jtag.c

@@ -83,9 +83,9 @@ bool jtag_clock(struct jtag_port *jp, bool tms, bool tdi, bool *tdo)
 	if (tdo) {
 		*tdo = (rbuf[rbufsz-1] & jp->tdo);
 #ifdef DEBUG_JTAG_CLOCK
-	char *x = bin2hex(rbuf, rbufsz);
+		char x[(rbufsz * 2) + 1];
+		bin2hex(x, rbuf, rbufsz);
 	applog(LOG_DEBUG, "%p %02x tms=%d tdi=%d tdo=%d (%u:%s)", jp, (unsigned)rbuf[rbufsz-1], (int)tms, (int)tdi, (int)(bool)(rbuf[rbufsz-1] & jp->tdo), (unsigned)rbufsz, x);
-	free(x);
 	} else {
 		applog(LOG_DEBUG, "%p %02x tms=%d tdi=%d tdo=?ignore", jp, (unsigned)buf[2], (int)tms, (int)tdi);
 #endif

+ 35 - 11
libztex.c

@@ -34,6 +34,7 @@
 #include "miner.h"
 #include "fpgautils.h"
 #include "libztex.h"
+#include "util.h"
 
 //* Capability index for EEPROM support.
 #define CAPABILITY_EEPROM 0,0
@@ -72,7 +73,7 @@ static int libztex_get_string_descriptor_ascii(libusb_device_handle *dev, uint8_
 	    LIBUSB_REQUEST_GET_DESCRIPTOR, (LIBUSB_DT_STRING << 8) | 0,
 	    0x0000, buf, sizeof(buf), 1000);
 	if (cnt < 0) {
-		applog(LOG_ERR, "%s: Failed to read LANGIDs: %s", __func__, libusb_error_name(cnt));
+		applog(LOG_ERR, "%s: Failed to read LANGIDs: %s", __func__, bfg_strerror(cnt, BST_LIBUSB));
 		return cnt;
 	}
 
@@ -82,7 +83,7 @@ static int libztex_get_string_descriptor_ascii(libusb_device_handle *dev, uint8_
 	    LIBUSB_REQUEST_GET_DESCRIPTOR, (LIBUSB_DT_STRING << 8) | desc_index,
 	    langid, buf, sizeof(buf), 1000);
 	if (cnt < 0) {
-		applog(LOG_ERR, "%s: Failed to read string descriptor: %s", __func__, libusb_error_name(cnt));
+		applog(LOG_ERR, "%s: Failed to read string descriptor: %s", __func__, bfg_strerror(cnt, BST_LIBUSB));
 		return cnt;
 	}
 
@@ -109,7 +110,7 @@ static bool libztex_firmwareReset(struct libusb_device_handle *hndl, bool enable
 	int cnt = libusb_control_transfer(hndl, 0x40, 0xA0, 0xE600, 0, &reset, 1, 1000);
 	if (cnt < 0)
 	{
-		applog(LOG_ERR, "Ztex reset %d failed: %s", enable, libusb_error_name(cnt));
+		applog(LOG_ERR, "Ztex reset %d failed: %s", enable, bfg_strerror(cnt, BST_LIBUSB));
 		return 1;
 	}
 
@@ -139,7 +140,7 @@ static enum check_result libztex_checkDevice(struct libusb_device *dev)
 
 	err = libusb_open(dev, &hndl);
 	if (err != LIBUSB_SUCCESS) {
-		applog(LOG_ERR, "%s: Can not open ZTEX device: %s", __func__, libusb_error_name(err));
+		applog(LOG_ERR, "%s: Can not open ZTEX device: %s", __func__, bfg_strerror(err, BST_LIBUSB));
 		goto done;
 	}
 
@@ -224,7 +225,7 @@ static enum check_result libztex_checkDevice(struct libusb_device *dev)
 	}
 
 	if (0 != fseek(fp, 0, SEEK_END)) {
-		applog(LOG_ERR, "Ztex firmware fseek: %s", strerror(errno));
+		applog(LOG_ERR, "Ztex firmware fseek: %s", bfg_strerror(errno, BST_ERRNO));
 		goto done;
 	}
 
@@ -232,7 +233,7 @@ static enum check_result libztex_checkDevice(struct libusb_device *dev)
 	rewind(fp);
 	fw_buf = malloc(length);
 	if (!fw_buf) {
-		applog(LOG_ERR, "%s: Can not allocate memory: %s", __func__, strerror(errno));
+		applog(LOG_ERR, "%s: Can not allocate memory: %s", __func__, bfg_strerror(errno, BST_ERRNO));
 		goto done;
 	}
 
@@ -271,7 +272,7 @@ static enum check_result libztex_checkDevice(struct libusb_device *dev)
 		int k = libusb_control_transfer(hndl, 0x40, 0xA0, i, 0, fw_buf + i, numbytes, 1000);
 		if (k < numbytes)
 		{
-			applog(LOG_ERR, "Ztex device: Failed to write firmware at %d with: %s", i, libusb_error_name(k));
+			applog(LOG_ERR, "Ztex device: Failed to write firmware at %d with: %s", i, bfg_strerror(k, BST_LIBUSB));
 			goto done;
 		}
 	}
@@ -611,7 +612,7 @@ int libztex_prepare_device(struct libusb_device *dev, struct libztex_device** zt
 	dclk_prepare(&newdev->dclk);
 	err = libusb_open(dev, &newdev->hndl);
 	if (err != LIBUSB_SUCCESS) {
-		applog(LOG_ERR, "%s: Can not open ZTEX device: %s", __func__, libusb_error_name(err));
+		applog(LOG_ERR, "%s: Can not open ZTEX device: %s", __func__, bfg_strerror(err, BST_LIBUSB));
 		return CHECK_ERROR;
 	}
 
@@ -626,6 +627,18 @@ int libztex_prepare_device(struct libusb_device *dev, struct libztex_device** zt
 		applog(LOG_ERR, "Ztex check device: Failed to read device snString with err %d", cnt);
 		return cnt;
 	}
+	
+	cnt = libztex_get_string_descriptor_ascii(newdev->hndl, newdev->descriptor.iProduct, buf, sizeof(buf));
+	if (unlikely(cnt < 0))
+		applog(LOG_WARNING, "Ztex check device: Failed to read device product with err %d", cnt);
+	else
+		newdev->dev_product = buf[0] ? strdup((void*)buf) : NULL;
+	
+	cnt = libztex_get_string_descriptor_ascii(newdev->hndl, newdev->descriptor.iManufacturer, buf, sizeof(buf));
+	if (unlikely(cnt < 0))
+		applog(LOG_WARNING, "Ztex check device: Failed to read device manufacturer with err %d", cnt);
+	else
+		newdev->dev_manufacturer = buf[0] ? strdup((void*)buf) : NULL;
 
 	cnt = libusb_control_transfer(newdev->hndl, 0xc0, 0x22, 0, 0, buf, 40, 500);
 	if (unlikely(cnt < 0)) {
@@ -737,6 +750,7 @@ int libztex_scanDevices(struct libztex_dev_list*** devs_p)
 	int found, max_found = 0, pos = 0, err, rescan, ret = 0;
 	libusb_device **list = NULL;
 	ssize_t cnt, i;
+	int skipped = 0;
 
 	do {
 		cnt = libusb_get_device_list(NULL, &list);
@@ -746,10 +760,16 @@ int libztex_scanDevices(struct libztex_dev_list*** devs_p)
 		}
 
 		for (found = rescan = i = 0; i < cnt; i++) {
+			if (bfg_claim_libusb(NULL, false, list[i]))
+			{
+				++skipped;
+				continue;
+			}
+			
 			err = libztex_checkDevice(list[i]);
 			switch (err) {
 			case CHECK_ERROR:
-				applog(LOG_ERR, "Ztex: Can not check device: %s", libusb_error_name(err));
+				applog(LOG_ERR, "Ztex: Can not check device: %s", bfg_strerror(err, BST_LIBUSB));
 				continue;
 			case CHECK_IS_NOT_ZTEX:
 				continue;
@@ -786,7 +806,7 @@ int libztex_scanDevices(struct libztex_dev_list*** devs_p)
 		if (!ztex) {
 			ztex = malloc(sizeof(*ztex));
 			if (!ztex) {
-				applog(LOG_ERR, "%s: Can not allocate memory for device struct: %s", __func__, strerror(errno));
+				applog(LOG_ERR, "%s: Can not allocate memory for device struct: %s", __func__, bfg_strerror(errno, BST_ERRNO));
 				goto done;
 			}
 		}
@@ -804,7 +824,7 @@ int libztex_scanDevices(struct libztex_dev_list*** devs_p)
 
 		devs[pos] = malloc(sizeof(struct libztex_dev_list));
 		if (NULL == devs[pos]) {
-			applog(LOG_ERR, "%s: Can not allocate memory for device: %s", __func__, strerror(errno));
+			applog(LOG_ERR, "%s: Can not allocate memory for device: %s", __func__, bfg_strerror(errno, BST_ERRNO));
 			libztex_destroy_device(ztex);
 			ztex = NULL;
 			continue;
@@ -827,6 +847,10 @@ done:
 		free(devs);
 	if (list)
 		libusb_free_device_list(list, 1);
+	
+	if (skipped)
+		applog(LOG_DEBUG, "%s: Skipping probe of %d claimed devices", __func__, skipped);
+	
 	return ret;
 }
 

+ 2 - 0
libztex.h

@@ -27,6 +27,8 @@ struct libztex_device {
 	libusb_device_handle *hndl; 
 	unsigned char usbbus;
 	unsigned char usbaddress;
+	char *dev_manufacturer;
+	char *dev_product;
 	unsigned char snString[LIBZTEX_SNSTRING_LEN+1];
 	unsigned char productId[4];
 	unsigned char fwVersion;

+ 6 - 3
logging.h

@@ -56,12 +56,15 @@ extern void _applog(int prio, const char *str);
 	return rv;  \
 } while (0)
 
+extern void _bfg_clean_up(void);
+
 #define quit(status, fmt, ...) do { \
+	_bfg_clean_up();  \
 	if (fmt) { \
-		char tmp42[LOGBUFSIZ]; \
-		snprintf(tmp42, sizeof(tmp42), fmt, ##__VA_ARGS__); \
-		_applog(LOG_ERR, tmp42); \
+		fprintf(stderr, fmt, ##__VA_ARGS__);  \
 	} \
+	fprintf(stderr, "\n");  \
+	fflush(stderr);  \
 	_quit(status); \
 } while (0)
 

File diff suppressed because it is too large
+ 373 - 154
miner.c


+ 32 - 23
miner.h

@@ -215,6 +215,8 @@ enum alive {
 	LIFE_NOSTART,
 	LIFE_INIT,
 	LIFE_WAIT,
+	LIFE_INIT2,  // Still initializing, but safe to call functions
+	LIFE_DEAD2,  // Totally dead, NOT safe to call functions
 };
 
 
@@ -282,20 +284,18 @@ struct device_drv {
 	// DRV-global functions
 	void (*drv_detect)();
 
-	// Device-specific functions
-	void (*get_dev_statline_before)(char *, struct cgpu_info *);
-	void (*get_dev_statline_after)(char *, struct cgpu_info *);
-
 	// Processor-specific functions
 	void (*reinit_device)(struct cgpu_info *);
-	void (*get_statline_before)(char *, struct cgpu_info *);
-	void (*get_statline)(char *, struct cgpu_info *);
+	bool (*override_statline_temp)(char *buf, struct cgpu_info *, bool per_processor);
 	struct api_data* (*get_api_extra_device_detail)(struct cgpu_info *);
 	struct api_data* (*get_api_extra_device_status)(struct cgpu_info *);
 	struct api_data *(*get_api_stats)(struct cgpu_info *);
 	bool (*get_stats)(struct cgpu_info *);
 	bool (*identify_device)(struct cgpu_info *);  // e.g. to flash a led
 	char *(*set_device)(struct cgpu_info *, char *option, char *setting, char *replybuf);
+	void (*proc_wlogprint_status)(struct cgpu_info *);
+	void (*proc_tui_wlogprint_choices)(struct cgpu_info *);
+	const char *(*proc_tui_handle_choice)(struct cgpu_info *, int input);
 
 	// Thread-specific functions
 	bool (*thread_prepare)(struct thr_info *);
@@ -441,6 +441,9 @@ struct cgpu_info {
 	
 	const char *device_path;
 	void *device_data;
+	const char *dev_manufacturer;
+	const char *dev_product;
+	const char *dev_serial;
 	union {
 #ifdef USE_ZTEX
 		struct libztex_device *device_ztex;
@@ -475,6 +478,7 @@ struct cgpu_info {
 	int accepted;
 	int rejected;
 	int stale;
+	int bad_nonces;
 	int hw_errors;
 	double rolling;
 	double total_mhashes;
@@ -875,7 +879,7 @@ extern json_t *json_rpc_call(CURL *curl, const char *url, const char *userpass,
 			     const char *rpc_req, bool, bool, int *,
 			     struct pool *pool, bool);
 extern bool our_curl_supports_proxy_uris();
-extern char *bin2hex(const unsigned char *p, size_t len);
+extern void bin2hex(char *out, const void *in, size_t len);
 extern bool hex2bin(unsigned char *p, const char *hexstr, size_t len);
 
 typedef bool (*sha256_func)(struct thr_info*, const unsigned char *pmidstate,
@@ -969,6 +973,8 @@ extern int mining_threads;
 extern struct cgpu_info *cpus;
 extern int total_devices;
 extern struct cgpu_info **devices;
+extern int total_devices_new;
+extern struct cgpu_info **devices_new;
 extern int total_pools;
 extern struct pool **pools;
 extern const char *algo_names[];
@@ -1051,21 +1057,18 @@ enum pool_protocol {
 
 struct stratum_work {
 	char *job_id;
-	char *prev_hash;
-	char *coinbase1;
-	char *coinbase2;
-	char **merkle;
-	char *bbversion;
-	char *nbit;
-	char *ntime;
 	bool clean;
-
-	size_t cb1_len;
-	size_t cb2_len;
-	size_t cb_len;
-
-	size_t header_len;
+	
+	bytes_t coinbase;
+	size_t nonce2_offset;
+	
 	int merkles;
+	bytes_t merkle_bin;
+	
+	uint8_t header1[36];
+	uint8_t diffbits[4];
+	uint8_t ntime[4];
+
 	double diff;
 
 	bool transparency_probed;
@@ -1159,6 +1162,10 @@ struct pool {
 	char *nonce1;
 	size_t n1_len;
 	uint32_t nonce2;
+	int nonce2sz;
+#ifdef WORDS_BIGENDIAN
+	int nonce2off;
+#endif
 	int n2size;
 	char *sessionid;
 	bool has_stratum;
@@ -1210,8 +1217,7 @@ struct work {
 
 	bool		stratum;
 	char 		*job_id;
-	char		*nonce2;
-	char		*ntime;
+	bytes_t		nonce2;
 	double		sdiff;
 	char		*nonce1;
 
@@ -1243,7 +1249,8 @@ struct work {
 };
 
 extern void get_datestamp(char *, struct timeval *);
-extern void inc_hw_errors(struct thr_info *thr);
+extern void inc_hw_errors(struct thr_info *, const struct work *, const uint32_t bad_nonce);
+#define inc_hw_errors_only(thr)  inc_hw_errors(thr, NULL, 0)
 enum test_nonce2_result {
 	TNR_GOOD = 1,
 	TNR_HIGH = 0,
@@ -1259,6 +1266,7 @@ extern struct work *find_queued_work_bymidstate(struct cgpu_info *cgpu, char *mi
 extern void work_completed(struct cgpu_info *cgpu, struct work *work);
 extern bool abandon_work(struct work *, struct timeval *work_runtime, uint64_t hashes);
 extern void hash_queued_work(struct thr_info *mythr);
+extern void get_statline3(char *buf, struct cgpu_info *, bool for_curses, bool opt_show_procs);
 extern void tailsprintf(char *f, const char *fmt, ...) FORMAT_SYNTAX_CHECK(printf, 2, 3);
 extern void _wlog(const char *str);
 extern void _wlogprint(const char *str);
@@ -1294,6 +1302,7 @@ extern void __copy_work(struct work *work, const struct work *base_work);
 extern struct work *copy_work(const struct work *base_work);
 extern struct thr_info *get_thread(int thr_id);
 extern struct cgpu_info *get_devices(int id);
+extern int scan_serial(const char *);
 
 enum api_data_type {
 	API_ESCAPE,

+ 9 - 1
ocl.c

@@ -401,6 +401,7 @@ _clState *initCl(unsigned int gpu, char *name, size_t nameSize)
 	struct cgpu_info *cgpu = &gpus[gpu];
 	cl_platform_id platform = NULL;
 	char pbuff[256], vbuff[255];
+	char *s;
 	cl_platform_id* platforms;
 	cl_uint preferred_vwidth;
 	cl_device_id *devices;
@@ -839,7 +840,14 @@ build:
 		    strstr(name, "Wrestler" ) ||
 		    strstr(name, "Zacate" ) ||
 		    strstr(name, "WinterPark" ))
-			patchbfi = true;
+		{
+			// BFI_INT patching only works with AMD-APP up to 1084
+			if (strstr(vbuff, "ATI-Stream"))
+				patchbfi = true;
+			else
+			if ((s = strstr(vbuff, "AMD-APP")) && (s = strchr(s, '(')) && atoi(&s[1]) < 1085)
+				patchbfi = true;
+		}
 	} else
 		applog(LOG_DEBUG, "cl_amd_media_ops not found, will not set BITALIGN");
 

+ 1 - 1
openwrt/bfgminer/Makefile

@@ -11,7 +11,7 @@ include $(TOPDIR)/rules.mk
 
 PKG_NAME:=bfgminer
 PKG_TITLE:=BFGMiner
-PKG_VERSION:=3.1.1
+PKG_VERSION:=3.1.2
 PKG_RELEASE:=1
 
 PKG_SOURCE:=$(PKG_NAME)-$(PKG_VERSION).tbz2

+ 15 - 15
usbtest.py

@@ -1,6 +1,7 @@
-#!/usr/bin/env python2.7
+#!/usr/bin/env python
 # Copyright 2012 Xiangfu
 # Copyright 2012-2013 Andrew Smith
+# Copyright 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
@@ -39,7 +40,7 @@ if len(sys.argv) < 2:
 	sys.exit("Aborting")
 
 # Open with a 10 second timeout - just to be sure
-ser = serial.Serial(sys.argv[1], 115200, serial.EIGHTBITS, serial.PARITY_NONE, serial.STOPBITS_ONE, 10, False, False, 5, False, None)
+ser = serial.Serial(sys.argv[1], 115200, serial.EIGHTBITS, serial.PARITY_NONE, serial.STOPBITS_ONE, 10, False, False, 5)
 
 if sys.argv[2] == "icarus":
 
@@ -48,32 +49,32 @@ if sys.argv[2] == "icarus":
 	block = "0000000120c8222d0497a7ab44a1a2c7bf39de941c9970b1dc7cdc400000079700000000e88aabe1f353238c668d8a4df9318e614c10c474f8cdf8bc5f6397b946c33d7c4e7242c31a098ea500000000000000800000000000000000000000000000000000000000000000000000000000000000000000000000000080020000"
 	midstate = "33c5bf5751ec7f7e056443b5aee3800331432c83f404d9de38b94ecbf907b92d"
 
-	rdata2  = block.decode('hex')[95:63:-1]
-	rmid    = midstate.decode('hex')[::-1]
+	rdata2  = binascii.a2b_hex(block.encode('ascii'))[95:63:-1]
+	rmid    = binascii.a2b_hex(midstate.encode('ascii'))[::-1]
 	payload = rmid + rdata2
 
-	print("Push payload to icarus: " + binascii.hexlify(payload))
+	print("Push payload to icarus: " + binascii.hexlify(payload).decode('ascii'))
 	ser.write(payload)
 
 	b=ser.read(4)
-	print("Result:(should be: 063c5e01): " + binascii.hexlify(b))
+	print("Result:(should be: 063c5e01): " + binascii.hexlify(b).decode('ascii'))
 
 	# Just another test
 	payload2 = "ce92099c5a80bb81c52990d5c0924c625fd25a535640607d5a4bdf8174e2c8d500000000000000000000000080000000000000000b290c1a42313b4f21b5bcb8"
 	print("Push payload to icarus: " + payload2)
-	ser.write(payload2.decode('hex'))
+	ser.write(binascii.a2b_hex(payload2.encode('ascii')))
 
 	b=ser.read(4)
-	print("Result:(should be: 8e0b31c5): " + binascii.hexlify(b))
+	print("Result:(should be: 8e0b31c5): " + binascii.hexlify(b).decode('ascii'))
 else:
-	data = ""
+	data = b""
 	for arg in sys.argv[2::]:
 		if arg[0:2:] == '0x':
-			data += arg[2::].decode('hex')
+			data += binascii.a2b_hex(arg[2::].encode('ascii'))
 		else:
-			data += arg
+			data += arg.encode('latin-1')
 
-	print("Sending: 0x" + binascii.hexlify(data))
+	print("Sending: 0x" + binascii.hexlify(data).decode('ascii'))
 	ser.write(data)
 
 	# If you're expecting more than one linefeed terminated reply,
@@ -81,9 +82,8 @@ else:
 	# AND with no linefeed, this will wait the 10 seconds before returning
 	print("Waiting up to 10 seconds ...")
 	b=ser.readline()
-	print("Result: hex 0x" + binascii.hexlify(b))
+	print("Result: hex 0x" + binascii.hexlify(b).decode('ascii'))
 
-	# This could mess up the display - do it last
-	print("Result: asc '" + b + "'")
+	print("Result: asc %s" % (repr(b),))
 
 ser.close()

+ 242 - 126
util.c

@@ -668,61 +668,56 @@ char *absolute_uri(char *uri, const char *ref)
 	return abs;
 }
 
-/* Returns a malloced array string of a binary value of arbitrary length. The
- * array is rounded up to a 4 byte size to appease architectures that need
- * aligned array  sizes */
-char *bin2hex(const unsigned char *p, size_t len)
-{
-	unsigned int i;
-	ssize_t slen;
-	char *s;
-
-	slen = len * 2 + 1;
-	if (slen % 4)
-		slen += 4 - (slen % 4);
-	s = calloc(slen, 1);
-	if (unlikely(!s))
-		quit(1, "Failed to calloc in bin2hex");
+static const char _hexchars[0x10] = "0123456789abcdef";
 
-	for (i = 0; i < len; i++)
-		sprintf(s + (i * 2), "%02x", (unsigned int) p[i]);
+void bin2hex(char *out, const void *in, size_t len)
+{
+	const unsigned char *p = in;
+	while (len--)
+	{
+		(out++)[0] = _hexchars[p[0] >> 4];
+		(out++)[0] = _hexchars[p[0] & 0xf];
+		++p;
+	}
+	out[0] = '\0';
+}
 
-	return s;
+static inline
+int _hex2bin_char(const char c)
+{
+	if (c >= '0' && c <= '9')
+		return c - '0';
+	if (c >= 'a' && c <= 'f')
+		return (c - 'a') + 10;
+	if (c >= 'A' && c <= 'F')
+		return (c - 'A') + 10;
+	return -1;
 }
 
 /* Does the reverse of bin2hex but does not allocate any ram */
 bool hex2bin(unsigned char *p, const char *hexstr, size_t len)
 {
-	bool ret = false;
-
-	while (*hexstr && len) {
-		char hex_byte[4];
-		unsigned int v;
-
-		if (unlikely(!hexstr[1])) {
-			applog(LOG_ERR, "hex2bin str truncated");
-			return ret;
-		}
-
-		memset(hex_byte, 0, 4);
-		hex_byte[0] = hexstr[0];
-		hex_byte[1] = hexstr[1];
-
-		if (unlikely(sscanf(hex_byte, "%x", &v) != 1)) {
-			applog(LOG_ERR, "hex2bin sscanf '%s' failed", hex_byte);
-			return ret;
+	int n, o;
+	
+	while (len--)
+	{
+		n = _hex2bin_char((hexstr++)[0]);
+		if (unlikely(n == -1))
+		{
+badchar:
+			if (!hexstr[-1])
+				applog(LOG_ERR, "hex2bin: str truncated");
+			else
+				applog(LOG_ERR, "hex2bin: invalid character 0x%02x", (int)hexstr[-1]);
+			return false;
 		}
-
-		*p = (unsigned char) v;
-
-		p++;
-		hexstr += 2;
-		len--;
+		o = _hex2bin_char((hexstr++)[0]);
+		if (unlikely(o == -1))
+			goto badchar;
+		(p++)[0] = (n << 4) | o;
 	}
-
-	if (likely(len == 0 && *hexstr == 0))
-		ret = true;
-	return ret;
+	
+	return likely(!hexstr[0]);
 }
 
 void hash_data(unsigned char *out_hash, const unsigned char *data)
@@ -779,24 +774,22 @@ bool hash_target_check_v(const unsigned char *hash, const unsigned char *target)
 
 	if (opt_debug) {
 		unsigned char hash_swap[32], target_swap[32];
-		char *hash_str, *target_str;
+		char hash_str[65];
+		char target_str[65];
 
 		for (int i = 0; i < 32; ++i) {
 			hash_swap[i] = hash[31-i];
 			target_swap[i] = target[31-i];
 		}
 
-		hash_str = bin2hex(hash_swap, 32);
-		target_str = bin2hex(target_swap, 32);
+		bin2hex(hash_str, hash_swap, 32);
+		bin2hex(target_str, target_swap, 32);
 
 		applog(LOG_DEBUG, " Proof: %s\nTarget: %s\nTrgVal? %s",
 			hash_str,
 			target_str,
 			rc ? "YES (hash <= target)" :
 			     "no (false positive; hash > target)");
-
-		free(hash_str);
-		free(target_str);
 	}
 
 	return rc;
@@ -1383,9 +1376,9 @@ char *recv_line(struct pool *pool)
 			if (n < 0) {
 				//Save errno from being overweitten bei socket_ commands 
 				int socket_recv_errno;
-				socket_recv_errno = errno;
-				if (!sock_blocks() || !socket_full(pool, false)) {
-					applog(LOG_DEBUG, "Failed to recv sock in recv_line: %d", socket_recv_errno);
+				socket_recv_errno = SOCKERR;
+				if (!sock_blocks() || !socket_full(pool, true)) {
+					applog(LOG_DEBUG, "Failed to recv sock in recv_line: %s", bfg_strerror(socket_recv_errno, BST_SOCKET));
 					suspend_stratum(pool);
 					break;
 				}
@@ -1520,6 +1513,7 @@ static bool parse_notify(struct pool *pool, json_t *val)
 	char *job_id, *prev_hash, *coinbase1, *coinbase2, *bbversion, *nbit, *ntime;
 	bool clean, ret = false;
 	int merkles, i;
+	size_t cb1_len, cb2_len;
 	json_t *arr;
 
 	arr = json_array_get(val, 4);
@@ -1527,86 +1521,65 @@ static bool parse_notify(struct pool *pool, json_t *val)
 		goto out;
 
 	merkles = json_array_size(arr);
+	for (i = 0; i < merkles; i++)
+		if (!json_is_string(json_array_get(arr, i)))
+			goto out;
 
-	job_id = json_array_string(val, 0);
-	prev_hash = json_array_string(val, 1);
-	coinbase1 = json_array_string(val, 2);
-	coinbase2 = json_array_string(val, 3);
-	bbversion = json_array_string(val, 5);
-	nbit = json_array_string(val, 6);
-	ntime = json_array_string(val, 7);
+	prev_hash = __json_array_string(val, 1);
+	coinbase1 = __json_array_string(val, 2);
+	coinbase2 = __json_array_string(val, 3);
+	bbversion = __json_array_string(val, 5);
+	nbit = __json_array_string(val, 6);
+	ntime = __json_array_string(val, 7);
 	clean = json_is_true(json_array_get(val, 8));
 
-	if (!job_id || !prev_hash || !coinbase1 || !coinbase2 || !bbversion || !nbit || !ntime) {
-		/* Annoying but we must not leak memory */
-		if (job_id)
-			free(job_id);
-		if (prev_hash)
-			free(prev_hash);
-		if (coinbase1)
-			free(coinbase1);
-		if (coinbase2)
-			free(coinbase2);
-		if (bbversion)
-			free(bbversion);
-		if (nbit)
-			free(nbit);
-		if (ntime)
-			free(ntime);
+	if (!prev_hash || !coinbase1 || !coinbase2 || !bbversion || !nbit || !ntime)
+		goto out;
+	
+	job_id = json_array_string(val, 0);
+	if (!job_id)
 		goto out;
-	}
 
 	cg_wlock(&pool->data_lock);
 	free(pool->swork.job_id);
-	free(pool->swork.prev_hash);
-	free(pool->swork.coinbase1);
-	free(pool->swork.coinbase2);
-	free(pool->swork.bbversion);
-	free(pool->swork.nbit);
-	free(pool->swork.ntime);
 	pool->swork.job_id = job_id;
-	pool->swork.prev_hash = prev_hash;
-	pool->swork.coinbase1 = coinbase1;
-	pool->swork.cb1_len = strlen(coinbase1) / 2;
-	pool->swork.coinbase2 = coinbase2;
-	pool->swork.cb2_len = strlen(coinbase2) / 2;
-	pool->swork.bbversion = bbversion;
-	pool->swork.nbit = nbit;
-	pool->swork.ntime = ntime;
 	pool->submit_old = !clean;
 	pool->swork.clean = true;
-	pool->swork.cb_len = pool->swork.cb1_len + pool->n1_len + pool->n2size + pool->swork.cb2_len;
-
-	for (i = 0; i < pool->swork.merkles; i++)
-		free(pool->swork.merkle[i]);
-	if (merkles) {
-		pool->swork.merkle = realloc(pool->swork.merkle, sizeof(char *) * merkles + 1);
-		for (i = 0; i < merkles; i++)
-			pool->swork.merkle[i] = json_array_string(arr, i);
-	}
+	
+	hex2bin(&pool->swork.header1[0], bbversion,  4);
+	hex2bin(&pool->swork.header1[4], prev_hash, 32);
+	hex2bin(&pool->swork.ntime[0], ntime, 4);
+	hex2bin(&pool->swork.diffbits[0], nbit, 4);
+	
+	cb1_len = strlen(coinbase1) / 2;
+	pool->swork.nonce2_offset = cb1_len + pool->n1_len;
+	cb2_len = strlen(coinbase2) / 2;
+
+	bytes_resize(&pool->swork.coinbase, pool->swork.nonce2_offset + pool->n2size + cb2_len);
+	uint8_t *coinbase = bytes_buf(&pool->swork.coinbase);
+	hex2bin(coinbase, coinbase1, cb1_len);
+	hex2bin(&coinbase[cb1_len], pool->nonce1, pool->n1_len);
+	// NOTE: gap for nonce2, filled at work generation time
+	hex2bin(&coinbase[pool->swork.nonce2_offset + pool->n2size], coinbase2, cb2_len);
+	
+	bytes_resize(&pool->swork.merkle_bin, 32 * merkles);
+	for (i = 0; i < merkles; i++)
+		hex2bin(&bytes_buf(&pool->swork.merkle_bin)[i * 32], json_string_value(json_array_get(arr, i)), 32);
 	pool->swork.merkles = merkles;
 	if (clean)
 		pool->nonce2 = 0;
-	pool->swork.header_len = strlen(pool->swork.bbversion) +
-				 strlen(pool->swork.prev_hash) +
-				 strlen(pool->swork.ntime) +
-				 strlen(pool->swork.nbit) +
-	/* merkle_hash */	 32 +
-	/* nonce */		 8 +
-	/* workpadding */	 96;
-	pool->swork.header_len = pool->swork.header_len * 2 + 1;
-	align_len(&pool->swork.header_len);
 	cg_wunlock(&pool->data_lock);
 
 	applog(LOG_DEBUG, "Received stratum notify from pool %u with job_id=%s",
 	       pool->pool_no, job_id);
-	if (opt_protocol) {
+	if (opt_debug && opt_protocol)
+	{
 		applog(LOG_DEBUG, "job_id: %s", job_id);
 		applog(LOG_DEBUG, "prev_hash: %s", prev_hash);
 		applog(LOG_DEBUG, "coinbase1: %s", coinbase1);
 		applog(LOG_DEBUG, "coinbase2: %s", coinbase2);
 		for (i = 0; i < merkles; i++)
-			applog(LOG_DEBUG, "merkle%d: %s", i, pool->swork.merkle[i]);
+			applog(LOG_DEBUG, "merkle%d: %s", i, json_string_value(json_array_get(arr, i)));
 		applog(LOG_DEBUG, "bbversion: %s", bbversion);
 		applog(LOG_DEBUG, "nbit: %s", nbit);
 		applog(LOG_DEBUG, "ntime: %s", ntime);
@@ -2075,6 +2048,10 @@ resend:
 	pool->nonce1 = nonce1;
 	pool->n1_len = strlen(nonce1) / 2;
 	pool->n2size = n2size;
+	pool->nonce2sz  = (n2size > sizeof(pool->nonce2)) ? sizeof(pool->nonce2) : n2size;
+#ifdef WORDS_BIGENDIAN
+	pool->nonce2off = (n2size < sizeof(pool->nonce2)) ? (sizeof(pool->nonce2) - n2size) : 0;
+#endif
 	cg_wunlock(&pool->data_lock);
 
 	if (sessionid)
@@ -2239,26 +2216,160 @@ void RenameThread(const char* name)
 #endif
 }
 
+static pthread_key_t key_bfgtls;
+struct bfgtls_data {
+	char *bfg_strerror_result;
+	size_t bfg_strerror_resultsz;
 #ifdef WIN32
-static const char *WindowsErrorStr(DWORD dwMessageId)
-{
-	static LPSTR msg = NULL;
-	if (msg)
-		LocalFree(msg);
-	if (FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM, 0, dwMessageId, 0, (LPSTR)&msg, 0, 0))
-		return msg;
-	static const char fmt[] = "Error #%ld";
-	signed long ldMsgId = dwMessageId;
-	int sz = snprintf((char*)&sz, 0, fmt, ldMsgId) + 1;
-	msg = (LPTSTR)LocalAlloc(LMEM_FIXED, sz);
-	sprintf((char*)msg, fmt, ldMsgId);
-	return msg;
+	LPSTR bfg_strerror_socketresult;
+#endif
+};
+
+static
+struct bfgtls_data *get_bfgtls()
+{
+	struct bfgtls_data *bfgtls = pthread_getspecific(key_bfgtls);
+	if (bfgtls)
+		return bfgtls;
+	
+	void *p;
+	
+	bfgtls = malloc(sizeof(*bfgtls));
+	if (!bfgtls)
+		quit(1, "malloc bfgtls failed");
+	p = malloc(64);
+	if (!p)
+		quit(1, "malloc bfg_strerror_result failed");
+	*bfgtls = (struct bfgtls_data){
+		.bfg_strerror_resultsz = 64,
+		.bfg_strerror_result = p,
+	};
+	if (pthread_setspecific(key_bfgtls, bfgtls))
+		quit(1, "pthread_setspecific failed");
+	
+	return bfgtls;
+}
+
+void bfg_init_threadlocal()
+{
+	if (pthread_key_create(&key_bfgtls, NULL))
+		quit(1, "pthread_key_create failed");
+}
+
+static
+bool bfg_grow_buffer(char ** const bufp, size_t * const bufszp, size_t minimum)
+{
+	if (minimum <= *bufszp)
+		return false;
+	
+	while (minimum > *bufszp)
+		*bufszp = 2;
+	*bufp = realloc(*bufp, *bufszp);
+	if (unlikely(!*bufp))
+		quit(1, "realloc failed in bfg_grow_buffer");
+	
+	return true;
 }
+
+static
+const char *bfg_strcpy_growing_buffer(char ** const bufp, size_t * const bufszp, const char *src)
+{
+	if (!src)
+		return NULL;
+	
+	const size_t srcsz = strlen(src) + 1;
+	
+	bfg_grow_buffer(bufp, bufszp, srcsz);
+	memcpy(*bufp, src, srcsz);
+	
+	return *bufp;
+}
+
+// Guaranteed to always return some string (or quit)
+const char *bfg_strerror(int e, enum bfg_strerror_type type)
+{
+	static __maybe_unused pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
+	struct bfgtls_data *bfgtls = get_bfgtls();
+	size_t * const bufszp = &bfgtls->bfg_strerror_resultsz;
+	char ** const bufp = &bfgtls->bfg_strerror_result;
+	const char *have = NULL;
+	
+	switch (type) {
+		case BST_LIBUSB:
+// NOTE: Nested preprocessor checks since the latter isn't defined at all without the former
+#ifdef HAVE_LIBUSB
+#	if HAVE_DECL_LIBUSB_ERROR_NAME
+			// libusb makes no guarantees for thread-safety or persistence
+			mutex_lock(&mutex);
+			have = bfg_strcpy_growing_buffer(bufp, bufszp, libusb_error_name(e));
+			mutex_unlock(&mutex);
+#	endif
 #endif
+			break;
+		case BST_SOCKET:
+		{
+#ifdef WIN32
+			// Windows has a different namespace for socket errors
+			LPSTR *msg = &bfgtls->bfg_strerror_socketresult;
+			if (*msg)
+				LocalFree(*msg);
+			if (FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM, 0, e, 0, (LPSTR)msg, 0, 0))
+				return *msg;
+			*msg = NULL;
+			
+			break;
+#endif
+		}
+			// Fallthru on non-WIN32
+		case BST_ERRNO:
+		{
+#ifdef __STRERROR_S_WORKS
+			// FIXME: Not sure how to get this on MingW64
+retry:
+			if (likely(!strerror_s(*bufp, *bufszp, e)))
+			{
+				if (bfg_grow_buffer(bufp, bufszp, strlen(*bufp) + 2))
+					goto retry;
+				return *bufp;
+			}
+// TODO: XSI strerror_r
+// TODO: GNU strerror_r
+#else
+			mutex_lock(&mutex);
+			have = bfg_strcpy_growing_buffer(bufp, bufszp, strerror(e));
+			mutex_unlock(&mutex);
+#endif
+		}
+	}
+	
+	if (have)
+		return *bufp;
+	
+	// Failback: Stringify the number
+	static const char fmt[] = "%s error #%d", *typestr;
+	switch (type) {
+		case BST_ERRNO:
+			typestr = "System";
+			break;
+		case BST_SOCKET:
+			typestr = "Socket";
+			break;
+		case BST_LIBUSB:
+			typestr = "libusb";
+			break;
+		default:
+			typestr = "Unexpected";
+	}
+	int sz = snprintf((char*)bfgtls, 0, fmt, typestr, e) + 1;
+	bfg_grow_buffer(bufp, bufszp, sz);
+	sprintf(*bufp, fmt, typestr, e);
+	return *bufp;
+}
 
 void notifier_init(notifier_t pipefd)
 {
 #ifdef WIN32
+#define WindowsErrorStr(e)  bfg_strerror(e, true)
 	SOCKET listener, connecter, acceptor;
 	listener = socket(AF_INET, SOCK_STREAM, 0);
 	if (listener == INVALID_SOCKET)
@@ -2332,3 +2443,8 @@ void notifier_destroy(notifier_t fd)
 #endif
 	fd[0] = fd[1] = INVSOCK;
 }
+
+void _bytes_alloc_failure(size_t sz)
+{
+	quit(1, "bytes_resize failed to allocate %lu bytes", (unsigned long)sz);
+}

+ 96 - 3
util.h

@@ -30,7 +30,8 @@
 	#define INVINETADDR -1
 	#define CLOSESOCKET close
 
-	#define SOCKERRMSG strerror(errno)
+	#define SOCKERR (errno)
+	#define SOCKERRMSG bfg_strerror(errno, BST_SOCKET)
 	static inline bool sock_blocks(void)
 	{
 		return (errno == EAGAIN || errno == EWOULDBLOCK);
@@ -45,8 +46,8 @@
 	#define INVINETADDR INADDR_NONE
 	#define CLOSESOCKET closesocket
 
-	extern char *WSAErrorMsg(void);
-	#define SOCKERRMSG WSAErrorMsg()
+	#define SOCKERR (WSAGetLastError())
+	#define SOCKERRMSG bfg_strerror(WSAGetLastError(), BST_SOCKET)
 
 	static inline bool sock_blocks(void)
 	{
@@ -111,6 +112,13 @@ void *realloc_strcat(char *ptr, char *s);
 extern char *sanestr(char *o, char *s);
 void RenameThread(const char* name);
 
+enum bfg_strerror_type {
+	BST_ERRNO,
+	BST_SOCKET,
+	BST_LIBUSB,
+};
+extern const char *bfg_strerror(int, enum bfg_strerror_type);
+
 typedef SOCKETTYPE notifier_t[2];
 extern void notifier_init(notifier_t);
 extern void notifier_wake(notifier_t);
@@ -125,6 +133,69 @@ static inline void align_len(size_t *len)
 }
 
 
+typedef struct bytes_t {
+	uint8_t *buf;
+	size_t sz;
+	size_t allocsz;
+} bytes_t;
+
+// This can't be inline without ugly const/non-const issues
+#define bytes_buf(b)  ((b)->buf)
+
+static inline
+size_t bytes_len(const bytes_t *b)
+{
+	return b->sz;
+}
+
+extern void _bytes_alloc_failure(size_t);
+
+static inline
+void bytes_resize(bytes_t *b, size_t newsz)
+{
+	b->sz = newsz;
+	if (newsz <= b->allocsz)
+		return;
+	
+	if (!b->allocsz)
+		b->allocsz = 0x10;
+	do {
+		b->allocsz *= 2;
+	} while (newsz > b->allocsz);
+	b->buf = realloc(b->buf, b->allocsz);
+	if (!b->buf)
+		_bytes_alloc_failure(b->allocsz);
+}
+
+static inline
+void bytes_cat(bytes_t *b, const bytes_t *cat)
+{
+	size_t origsz = bytes_len(b);
+	size_t addsz = bytes_len(cat);
+	bytes_resize(b, origsz + addsz);
+	memcpy(&bytes_buf(b)[origsz], bytes_buf(cat), addsz);
+}
+
+static inline
+void bytes_cpy(bytes_t *dst, const bytes_t *src)
+{
+	dst->allocsz = src->allocsz;
+	dst->sz = src->sz;
+	size_t half;
+	while (dst->sz <= (half = dst->allocsz / 2))
+		dst->allocsz = half;
+	dst->buf = malloc(dst->allocsz);
+	memcpy(dst->buf, src->buf, dst->sz);
+}
+
+static inline
+void bytes_free(bytes_t *b)
+{
+	free(b->buf);
+	b->sz = b->allocsz = 0;
+}
+
+
 static inline
 void set_maxfd(int *p_maxfd, int fd)
 {
@@ -181,4 +252,26 @@ struct timeval *select_timeout(struct timeval *tvp_timeout, struct timeval *tvp_
 }
 
 
+#define RUNONCE(rv)  do {  \
+	static bool _runonce = false;  \
+	if (_runonce)  \
+		return rv;  \
+	_runonce = true;  \
+} while(0)
+
+
+static inline
+char *maybe_strdup(const char *s)
+{
+	return s ? strdup(s) : NULL;
+}
+
+static inline
+void maybe_strdup_if_null(const char **p, const char *s)
+{
+	if (!*p)
+		*p = maybe_strdup(s);
+}
+
+
 #endif /* __UTIL_H__ */

Some files were not shown because too many files changed in this diff