Browse Source

Merge tag 'bfgminer-3.1.4' into bigpic

BFGMiner version 3.1.4
Luke Dashjr 12 years ago
parent
commit
caaf00ea5a
33 changed files with 1602 additions and 537 deletions
  1. 143 0
      NEWS
  2. 37 34
      README
  3. 9 1
      README.RPC
  4. 2 2
      adl.c
  5. 4 4
      api.c
  6. 4 1
      compat.h
  7. 39 8
      configure.ac
  8. 16 0
      debian/changelog
  9. 1 1
      debian/control
  10. 119 13
      deviceapi.c
  11. 21 0
      deviceapi.h
  12. 3 5
      driver-avalon.c
  13. 69 6
      driver-bitforce.c
  14. 3 3
      driver-cpu.c
  15. 21 2
      driver-erupter.c
  16. 111 26
      driver-icarus.c
  17. 76 22
      driver-modminer.c
  18. 5 9
      driver-opencl.c
  19. 107 29
      driver-x6500.c
  20. 3 5
      driver-ztex.c
  21. 1 1
      findnonce.c
  22. 54 130
      fpgautils.c
  23. 3 19
      fpgautils.h
  24. 1 1
      ft232r.c
  25. 3 0
      icarus-common.h
  26. 37 44
      logging.c
  27. 434 127
      miner.c
  28. 32 6
      miner.h
  29. 7 1
      miner.php
  30. 1 1
      ocl.c
  31. 1 1
      openwrt/bfgminer/Makefile
  32. 160 27
      util.c
  33. 75 8
      util.h

+ 143 - 0
NEWS

@@ -1,3 +1,146 @@
+BFGMiner Version 3.1.4 - August 2, 2013
+- Windows: Rebuild pdcurses with UTF-8 and wide character support
+- Bugfix: Avoid using wide curses symbols/macros when USE_UNICODE is not defined
+- Unicode: Use line drawing in TUI Help
+- Use bfg_waddstr even with Unicode disabled, since it's needed for red
+highlight
+- Colour bad conditions in red
+- Unicode: Cross-tee intersecting lines
+- Unicode: Use WACS_VLINE for vertical lines
+- Unicode: If degrees symbol is available, add it to temperatures
+- Unicode: bfg_waddstr wrapper to handle non-ASCII characters, currently used
+only by logging and statlines
+- Unicode: Use WACS_HLINE for horizontal lines
+- Add framework for using Unicode in TUI (can be disabled with --no-unicode)
+- Avoid using potentially locale-dependent ctype functions in locale-independent
+contexts
+- Refactor temperature in TUI statlines to share code nicer
+- Bugfix: avalon: Fix applog formatting
+- Bugfix: Align totals columns in per-processor view
+- Bugfix: Fix curses-less build
+- configure: Workaround buggy autoconf versions
+- Bugfix: erupter: Include headers in order necessary for Windows
+- Bugfix: Reimplement get_intrange using strtol instead of sscanf (which is
+broken on Windows)
+- Bugfix: get_intrange: Check for extra garbage at the end, only after we know
+we have an end-position
+- Bugfix: Fix Enter key in TUI on Windows
+- erupter: Split identify-handling logic into handle_identify function
+- Bugfix: erupter: Ensure identify is handled during no-once or firstrun
+- erupter: After identify, check if a work restart is needed immediately
+- erupter: Implement identify function by pausing hashing for 3 seconds
+- Bugfix: icarus: Remember firstrun state in case it gets changed for the next
+run
+- icarus: Move actual dynclock updates to icarus_job_start
+- icarus: Split out icarus_job_prepare, and rename icarus_job_start
+- Bugfix: ZeroStats: Reset column widths to 1
+- miner.php: Include max temperature in device totals line
+- Bugfix: Stratum Fix debug logging of initial mining.subscribe command
+- Bugfix: Call pool_set_opaque from work_decode, so block content hiding/
+providing messages work for getwork/GBT
+- Split block contents hiding/providing notices out from stratum code
+- Add test suite for get_intrange
+- Bugfix: Check for error conditions in get_intrange to not have weird --device
+behaviour when bad values are provided
+- Bugfix: erupter: Take advantage of detectone_meta_info to handle Emerald
+autodetection
+- TUI Help describing the various status fields (contributed by midnightmagic)
+- Bugfix: ManageTUI: Allow 'I' key to be used by devices not supporting identify
+- Bugfix: Prefer Sapphire over Emerald for -S erupter:*
+- Bugfix: Clear total_bad_nonces when zeroing statistics
+- Bugfix: modminer: Since we are not searching iManuf string for needles, only
+look for "ModMiner"
+- Bugfix: sysfs autodetect: Recurse into tty/ subdirectory (necessary for
+CDC/ACM ttys)
+- sysfs autodetect: Split tty* directory search into new _sysfs_find_tty
+function
+- modminer: Reduce default clock to 190 MHz
+- README: Update driver info to include Erupter driver
+- README: FAQ about scrypt and difficulty
+- Include count of working devices/processors in totals statline
+- Format totals statline the same way as individual device/processor statlines
+- Rearrange TUI a bit, including menu at the top (+1 log line) and hashrate
+total closer to device summaries
+- Bugfix: setup_stratum_curl: Need to release stratum lock on connection failure
+too
+- Bugfix: Avoid unnecessary locks inside curses_print_status, which is called
+with the console lock held
+- Bugfix: setup_stratum_curl: Hold stratum lock until connection completes, to
+avoid potential races
+- Bugfix: stratum_works: If stratum is already active, it works (avoid trying to
+initialise it again)
+- Replace hashrate_to_bufstr/ti_hashrate_bufstr with format_unit/
+multi_format_unit_array
+- New multi_format_unit_array to fill multiple buffers instead of building a
+delimited string
+- multi_format_unit: Skip recounting length of fixed-length strings
+- Shrink status line to fit in 80 columns
+- Add network bandwidth rate to TUI
+- New multi_format_unit variadic macro to handle formatting multiple numbers at
+once
+- format_unit: Option to choose 3-digit integer display vs 5-character floating-
+point display
+- Optimization: format_unit: Handle number first, to avoid having to restore
+suffix later
+- Generalise hashrate_pick_unit/hashrate_to_bufstr into pick_unit/format_unit
+- Extend hashrate_pick_unit/hashrate_to_bufstr to handle sub-kilo units
+- Split total_bytes_xfer to total_bytes_rcvd and total_bytes_sent
+- Bugfix: _decode_udev_enc_dup: Allocate enough space for full string
+- Bugfix: Never use waddstr for logwin, since it would bypass special newline
+handling
+- Bugfix: bitforce: Set kname on chip processors
+- bitforce: Include voltages in Manage device TUI
+- Defer newlines going to curses logwin, to avoid a useless blank line at the
+bottom of the window
+- Ensure printing to logwin always goes through _wlog
+- Remove blank line above log window
+- bitforce: Identify parallel queue protocol distinctly from mere bulk queue
+- ManageTUI: Include kernel name, when available
+- Stratum: Roll ntime as we generate work
+- Stratum: Make swork.ntime native-endian
+- Stratum: Treat ntime as uint32_t (as it should be), still always big endian
+- Debuglog ManageTUI actions/responses
+- ManageTUI: Add generic Identify support
+- Bugfix: Move serial_detect* and open_bitstream to DevAPI code so CPU/OpenCL
+can build properly without fpgautils
+- Short-circuit logging sooner in quiet mode
+- Write to both stderr and console within same console lock "session"
+- Bugfix: Also hold the console lock when writing to stderr
+- Use common console locking function for stdout in logging.c
+- Move console lock and unlock functions (which also handle thread cancelstate)
+to miner.h
+- Bugfix: bitforce: Only try to clear queues of SC devices, since FPGA MR boards
+interpret ZQX/ZOX differently
+- Timer-based gettimeofday substitute for systems with poor time-of-day clocks
+(Windows)
+- Use clock_gettime(CLOCK_MONOTONIC) for timers when available
+- Use QueryPerformanceCounter for timers on Windows
+- Generic refactoring for timer_set_now
+- Replace all remaining uses of gettimeofday for timers, with timer_set_now (aka
+cgtime)
+- Don't mix timers with timestamps (visual only)
+- Always use struct timeval for timers, and don't mix timers with timestamps
+(functional only)
+- get_datestamp: Change timeval parameter to time_t, and implement
+get_now_datestamp for common "current time" use case
+- Use get_datestamp for (non-microsecond) log timestamps
+- Bugfix: ztex: Allocate final processor names on the heap, so they survive when
+the stack for ztex_prepare is gone
+- Bugfix: ztex: Copy serial number to device "name" before cloning it for other
+processors
+- Bugfix: x6500: Use cgpu->temp directly since there is only one sensor per
+processor
+- Bugfix: Actually show the highest temperature, not just calculate it
+- x6500: Allow changing clock speed from TUI Manage device
+- x6500: Port pgaset clock from modminer driver at
+66d2a3ab072fcdbc3c7ed41a97f265afa917bbee
+- modminer: Allow changing clock speed from TUI Manage device
+- bitforce: Flush job and result queues at startup to avoid unnecessary warnings
+- x6500: Reduce default clock to 190 MHz
+- Bugfix: fpgautils: Close libusb handle after copying USB strings
+- use BSD sed syntax to generate iospeed_local.h
+
+
 BFGMiner Version 3.1.3 - July 11, 2013
 - Bugfix: Reset staged_full flag when discarding (stale) popped work, or
 increasing the queue minimum

+ 37 - 34
README

@@ -327,7 +327,7 @@ WHILE RUNNING:
 
 The following options are available while running with a single keypress:
 
-[M]anage devices [P]ool management [S]ettings [D]isplay options [Q]uit
+[M]anage devices [P]ool management [S]ettings [D]isplay options  [H]elp [Q]uit
 
 M gives you something like:
 
@@ -392,49 +392,44 @@ dedicated to this program,
 	https://bitcointalk.org/?topic=78192
 	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/.81%
+The block display shows:
+Block: ...1b89f8d3 #217364  Diff:7.67M (54.93Th/s)  Started: [17:17:22]
 
-Each column is as follows:
-5s:  A 5 second exponentially decaying average hash rate
-avg: An all time average hash rate
-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, and percentage invalid of nonces returned
+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.
 
 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
+ ST:1  F:0  NB:1  AS:0  BW:[ 75/241 B/s]  E:2.42  U:22.53/m  BS:2.71k
 
 ST is STaged work items (ready to use).
-GF is Getwork Fail Occasions (server slow to provide work)
+F  is network Failure occasions (server down or slow to provide work)
 NB is New Blocks detected on the network
 AS is Active Submissions (shares in the process of submitting)
-RF is Remote Fail occasions (server slow to accept work)
+BW is BandWidth usage on the network
 E  is Efficiency defined as number of shares accepted (multiplied by their
           difficulty) per 2 KB of bandwidth
 U  is Utility defined as the number of shares / minute
 BS is the all time Best Share difficulty you've found
 
-The block display shows:
-Block: ...1b89f8d3 #217364  Diff:7.67M (54.93Th/s)  Started: [17:17:22]
+The totals line shows the following:
+ 6/32   75.0C | 171.3/170.8/171.2Gh/s | A:729 R:8+0(.01%) HW:0/.81%
 
-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 column is as follows:
+  The number of devices and processors currently mining
+  Hottest temperature reported by any processor
+  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 stale shares discarded (never submitted),
+      and the percentage these are of total found.
+  The number of HardWare errors, and percentage invalid of nonces returned
 
 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
+Columns are the same as in the totals line.
 
 
 ---
@@ -694,13 +689,15 @@ performance per unit power due to being dedicated to only one purpose.
 Q: How do I get my BFL/Icarus/Lancelot/Cairnsmore device to auto-recognise?
 A: On Linux, if the /dev/ttyUSB* devices don't automatically appear, the only
 thing that needs to be done is to load the driver for them:
-BFL: sudo modprobe ftdi_sio vendor=0x0403 product=0x6014
-Icarus: sudo modprobe pl2303 vendor=0x067b product=0x230
-Lancelot: sudo modprobe ftdi_sio vendor=0x0403 product=0x6001
-Cairnsmore: sudo modprobe ftdi_sio product=0x8350 vendor=0x0403
-On windows you must install the pl2303 or ftdi driver required for the device
-pl2303: http://prolificusa.com/pl-2303hx-drivers/
-ftdi: http://www.ftdichip.com/Drivers/VCP.htm
+  BitForce:   sudo modprobe ftdi_sio vendor=0x0403 product=0x6014
+  Erupter:    sudo modprobe cp210x   vendor=0x10c4 product=0xea60
+  Icarus:     sudo modprobe pl2303   vendor=0x067b product=0x0230
+  Lancelot:   sudo modprobe ftdi_sio vendor=0x0403 product=0x6001
+  Cairnsmore: sudo modprobe ftdi_sio vendor=0x0403 product=0x8350
+On Windows, you must install the driver required for the device:
+  FTDI:       http://www.ftdichip.com/Drivers/VCP.htm
+  Erupter:    http://www.silabs.com/products/mcu/pages/usbtouartbridgevcpdrivers.aspx
+  Icarus:     http://prolificusa.com/pl-2303hx-drivers/
 
 Q: On Linux I can see the /dev/ttyUSB* devices for my ICA/BFL/MMQ FPGA, but
 BFGMiner can't mine on them
@@ -717,6 +714,12 @@ Then logout and back in again
 Q: Can I mine scrypt with FPGAs or ASICs?
 A: No.
 
+Q: Why does BFGMiner show difficulty 0 when mining scrypt?
+A: BFGMiner consistently uses pdiff measurement for difficulty everywhere,
+rather than other measurements that may exist. For scrypt, pdiff 1 is very
+difficult, and higher get exponentially harder. It is unlikely you will want to
+use pdiff 1+ with scrypt until you have FPGAs and/or ASICs for it.
+
 Q: What is stratum and how do I use it?
 A: Stratum is a protocol designed to reduce resources for mining pools at the
 cost of keeping the miner in the dark and blindly transferring his mining

+ 9 - 1
README.RPC

@@ -351,7 +351,8 @@ The list of requests - a (*) means it requires privileged access - and replies a
                               help message about the options available
 
                               The current options are:
-                               MMQ opt=clock val=2 to 230 (and a multiple of 2)
+                               MMQ opt=clock val=2 to 250 (and a multiple of 2)
+                               XBS opt=clock val=2 to 250 (and a multiple of 2)
 
  zero|Which,true/false (*)
                none           There is no reply section just the STATUS section
@@ -414,6 +415,13 @@ api-example.py - a Python script to access the API
 Feature Changelog for external applications using the API:
 
 
+API V1.25.2
+
+Modified API commands:
+ 'pgaset' - added: XBS opt=clock val=2 to 250 (and a multiple of 2)
+
+----------
+
 API V1.25.1 (BFGMiner v3.1.2)
 
 Added API commands:

+ 2 - 2
adl.c

@@ -1,6 +1,6 @@
 /*
- * Copyright 2011-2012 Con Kolivas
- * Copyright 2012 Luke Dashjr
+ * Copyright 2011-2013 Con Kolivas
+ * Copyright 2012-2013 Luke Dashjr
  *
  * This program is free software; you can redistribute it and/or modify it
  * under the terms of the GNU General Public License as published by the Free

+ 4 - 4
api.c

@@ -3471,7 +3471,7 @@ static void setup_ipaccess()
 
 		group = NOPRIVGROUP;
 
-		if (isalpha(*ptr) && *(ptr+1) == ':') {
+		if (VALIDGROUP(*ptr) && *(ptr+1) == ':') {
 			if (DEFINEDGROUP(*ptr))
 				group = GROUP(*ptr);
 
@@ -3568,7 +3568,7 @@ void api(int api_thr_id)
 	int n, bound;
 	char *connectaddr;
 	const char *binderror;
-	time_t bindstart;
+	struct timeval bindstart;
 	short int port = opt_api_port;
 	struct sockaddr_in serv;
 	struct sockaddr_in cli;
@@ -3653,11 +3653,11 @@ void api(int api_thr_id)
 
 	// try for more than 1 minute ... in case the old one hasn't completely gone yet
 	bound = 0;
-	bindstart = time(NULL);
+	cgtime(&bindstart);
 	while (bound == 0) {
 		if (SOCKETFAIL(bind(*apisock, (struct sockaddr *)(&serv), sizeof(serv)))) {
 			binderror = SOCKERRMSG;
-			if ((time(NULL) - bindstart) > 61)
+			if (timer_elapsed(&bindstart, NULL) > 61)
 				break;
 			else {
 				applog(LOG_WARNING, "API bind to port %d failed - trying again in 30sec", port);

+ 4 - 1
compat.h

@@ -85,7 +85,8 @@ struct tm *localtime_convert(time_t t)
 #endif
 
 #ifndef HAVE_NANOSLEEP
-extern void cgtime(struct timeval *);
+extern void (*timer_set_now)(struct timeval *);
+#define cgtime(tvp)  timer_set_now(tvp)
 
 static inline int nanosleep(const struct timespec *req, struct timespec *rem)
 {
@@ -118,6 +119,8 @@ static inline int nanosleep(const struct timespec *req, struct timespec *rem)
 	}
 	return 0;
 }
+
+#undef cgtime
 #endif
 
 #ifdef WIN32

+ 39 - 8
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], [3])
+m4_define([v_mic], [4])
 ##--##--##--##--##--##--##--##--##--##--##--##--##--##--##--##--##
 m4_define([v_ver], [v_maj.v_min.v_mic])
 m4_define([lt_rev], m4_eval(v_maj + v_min))
@@ -145,6 +145,7 @@ m4_define([BFG_PREPROC_IFELSE],
 
 AC_CHECK_DECL([HASH_ITER],[
 	AC_CHECK_DECL([DL_FOREACH_SAFE],[
+		true
 	],[
 		AC_MSG_ERROR([Could not find DL_FOREACH_SAFE - install uthash-dev 1.9.2+])
 	],[
@@ -204,7 +205,7 @@ m4_define([BFG_PTHREAD_FLAG_CHECK],
 				fi
 				$2
 				break 2
-			],[])
+			])
 		done
 	done
 	if test "x${found_pthread}" = "xfalse"; then
@@ -357,7 +358,7 @@ PKG_CHECK_MODULES([LIBUSB], [libusb-1.0],[
 		AC_CHECK_LIB($usb_lib, libusb_init, [
 			libusb=yes
 			break
-		], [])
+		])
 	done
 	if test "x$libusb" = xyes; then
 			AC_CHECK_DECL([libusb_init],[
@@ -426,7 +427,7 @@ if test "x$libusb" = xyes; then
 	AC_DEFINE([HAVE_LIBUSB], [1], [Define if you have libusb-1.0])
 	save_CFLAGS="$CFLAGS"
 	CFLAGS="$LIBUSB_CFLAGS $CFLAGS"
-	AC_CHECK_DECLS([libusb_error_name],[],[],[#include <libusb.h>])
+	AC_CHECK_DECLS([libusb_error_name],[true],[true],[#include <libusb.h>])
 	CFLAGS="$save_CFLAGS"
 fi
 
@@ -443,15 +444,16 @@ fi
 
 
 need_fpgautils=no
-if test x$avalon$icarus$bitforce$modminer$opencl$x6500$ztex != xnonononononono; then
+if test x$avalon$icarus$bitforce$modminer$x6500$ztex != xnononononono; then
 	need_fpgautils=yes
+	AC_DEFINE([HAVE_FPGAUTILS], [1], [Defined to 1 if fpgautils is being used])
 	
 	if $have_win32; then
 		echo '#include <iospeeds.h>' >iospeeds_local.h
 	else
 		AC_PROG_CPP
 		AC_MSG_CHECKING([what baud rates your system supports])
-		echo '#include <termios.h>' | ${CPP} -dM - 2>/dev/null | sed 's/.* B\([0-9]\+\) .*/IOSPEED(\1)/;t;d' >iospeeds_local.h
+		echo '#include <termios.h>' | ${CPP} -dM - 2>/dev/null | sed 's/.*[ 	]B\([0-9][0-9]*\)[ 	].*/IOSPEED(\1)/' | grep IOSPEED >iospeeds_local.h
 		if grep -q IOSPEED iospeeds_local.h; then
 			AC_MSG_RESULT([done])
 		else
@@ -536,7 +538,7 @@ else
 fi
 
 
-AC_ARG_WITH([system-libblkmaker], [AC_HELP_STRING([--with-system-libblkmaker], [Use system libblkmaker rather than bundled one (default disabled)])],[],[with_system_libblkmaker=no])
+AC_ARG_WITH([system-libblkmaker], [AC_HELP_STRING([--with-system-libblkmaker], [Use system libblkmaker rather than bundled one (default disabled)])],[true],[with_system_libblkmaker=no])
 if test "x$with_system_libblkmaker" = "xyes"; then
 	PKG_CHECK_MODULES([libblkmaker],[libblkmaker_jansson-0.1],[
 		true
@@ -783,7 +785,7 @@ for sym in bswap_ __builtin_bswap __bswap_ __swap swap OSSwapInt; do
 				AC_DEFINE_UNQUOTED(AS_TR_CPP([HAVE_$headerfile]), 1)
 			fi
 			break 2
-		],[])
+		])
 	done
 	AC_MSG_RESULT([no])
 done
@@ -855,6 +857,35 @@ AC_TRY_COMPILE([
 ])
 
 
+AC_MSG_CHECKING([for clock_gettime(CLOCK_MONOTONIC)])
+AC_TRY_COMPILE([
+	#define _GNU_SOURCE
+	#include <time.h>
+],[
+	struct timespec ts;
+	clock_gettime(CLOCK_MONOTONIC, &ts);
+],[
+	AC_MSG_RESULT([yes])
+	AC_DEFINE([HAVE_CLOCK_GETTIME_MONOTONIC], [1], [Defined to 1 if clock_gettime(CLOCK_MONOTONIC) is defined])
+	AC_SEARCH_LIBS([clock_gettime],[rt posix4])
+	AC_MSG_CHECKING([for clock_gettime(CLOCK_MONOTONIC_RAW)])
+	AC_TRY_COMPILE([
+		#define _GNU_SOURCE
+		#include <time.h>
+	],[
+		struct timespec ts;
+		clock_gettime(CLOCK_MONOTONIC_RAW, &ts);
+	],[
+		AC_MSG_RESULT([yes])
+		AC_DEFINE([HAVE_CLOCK_GETTIME_MONOTONIC_RAW], [1], [Defined to 1 if clock_gettime(CLOCK_MONOTONIC_RAW) is defined])
+	],[
+		AC_MSG_RESULT([no])
+	])
+],[
+	AC_MSG_RESULT([no])
+])
+
+
 if test "x$prefix" = xNONE; then
 	prefix=/usr/local
 fi

+ 16 - 0
debian/changelog

@@ -1,3 +1,19 @@
+bfgminer (3.1.4-0precise1) precise; urgency=low
+
+  * Enable triggering "identify" (flash LED) function from Manage device TUI for devices that support it.
+  * modminer & x6500: Enable changing clock frequency from Manage devices TUI interface.
+  * Device ailments (SICK, ERR, etc) are now displayed in red/pink.
+  * erupter: Add support for "identify" (flash LED) function. After some delay (up to 13 seconds), the LED will light for 3 seconds (hashing stops during this time).
+  * bitforce: Display voltages in Manage device TUI.
+  * Reorganised TUI display, adding network transfer rate, and totals summary line including count of total devices/processors hashing as well as hottest temperature across all sensors.
+  * Improved timer functionality under the hood, taking advantage of clock_gettime. This should fix issues (eg, erupters reporting insane hashrates) related to system clock changes (such as NTP drift adjustment).
+  * Removed blank/wasted lines above/below log window in TUI mode.
+  * bitforce: Clear queues at startup to avoid spurious warnings.
+  * Stratum: Add support for rolling ntime header.
+
+ -- Luke Dashjr <luke+bfgminer@dashjr.org>  Fri, 02 Aug 2013 02:28:17 -0000
+
+
 bfgminer (3.1.3-0precise1) precise; urgency=low
 
   * Fix 100% CPU usage hang with GBT/getwork pools.

+ 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.3
+Standards-Version: 3.1.4
 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

+ 119 - 13
deviceapi.c

@@ -37,8 +37,7 @@ bool hashes_done(struct thr_info *thr, int64_t hashes, struct timeval *tvp_hashe
 	const long cycle = opt_log_interval / 5 ? : 1;
 	
 	if (unlikely(hashes == -1)) {
-		time_t now = time(NULL);
-		if (difftime(now, cgpu->device_last_not_well) > 1.)
+		if (timer_elapsed(&cgpu->tv_device_last_not_well, NULL) > 0)
 			dev_error(cgpu, REASON_THREAD_ZERO_HASH);
 		
 		if (thr->scanhash_working && opt_restart) {
@@ -103,7 +102,7 @@ int restart_wait(struct thr_info *thr, unsigned int mstime)
 		return (thr->work_restart ? 0 : ETIMEDOUT);
 	}
 	
-	gettimeofday(&tv_now, NULL);
+	timer_set_now(&tv_now);
 	timer_set_delay(&tv_timer, &tv_now, mstime * 1000);
 	while (true)
 	{
@@ -119,7 +118,7 @@ int restart_wait(struct thr_info *thr, unsigned int mstime)
 				return 0;
 			notifier_read(thr->work_restart_notifier);
 		}
-		gettimeofday(&tv_now, NULL);
+		timer_set_now(&tv_now);
 	}
 }
 
@@ -164,16 +163,16 @@ void minerloop_scanhash(struct thr_info *mythr)
 		work = get_and_prepare_work(mythr);
 		if (!work)
 			break;
-		gettimeofday(&(work->tv_work_start), NULL);
+		timer_set_now(&work->tv_work_start);
 		
 		do {
 			thread_reportin(mythr);
 			/* Only allow the mining thread to be cancelled when
 			* it is not in the driver code. */
 			pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, NULL);
-			gettimeofday(&tv_start, NULL);
+			timer_set_now(&tv_start);
 			hashes = api->scanhash(mythr, work, work->blk.nonce + max_nonce);
-			gettimeofday(&tv_end, NULL);
+			timer_set_now(&tv_end);
 			pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, NULL);
 			pthread_testcancel();
 			thread_reportin(mythr);
@@ -277,7 +276,7 @@ void job_results_fetched(struct thr_info *mythr)
 	{
 		struct timeval tv_now;
 		
-		gettimeofday(&tv_now, NULL);
+		timer_set_now(&tv_now);
 		
 		do_process_results(mythr, &tv_now, mythr->prev_work, true);
 	}
@@ -296,7 +295,7 @@ void mt_job_transition(struct thr_info *mythr)
 {
 	struct timeval tv_now;
 	
-	gettimeofday(&tv_now, NULL);
+	timer_set_now(&tv_now);
 	
 	if (mythr->starting_next_work)
 	{
@@ -315,7 +314,7 @@ void job_start_complete(struct thr_info *mythr)
 {
 	struct timeval tv_now;
 	
-	gettimeofday(&tv_now, NULL);
+	timer_set_now(&tv_now);
 	
 	do_process_results(mythr, &tv_now, mythr->prev_work, false);
 }
@@ -359,7 +358,7 @@ void do_notifier_select(struct thr_info *thr, struct timeval *tvp_timeout)
 	int maxfd;
 	fd_set rfds;
 	
-	gettimeofday(&tv_now, NULL);
+	timer_set_now(&tv_now);
 	FD_ZERO(&rfds);
 	FD_SET(thr->notifier[0], &rfds);
 	maxfd = thr->notifier[0];
@@ -404,7 +403,7 @@ void minerloop_async(struct thr_info *mythr)
 	
 	while (likely(!cgpu->shutdown)) {
 		tv_timeout.tv_sec = -1;
-		gettimeofday(&tv_now, NULL);
+		timer_set_now(&tv_now);
 		for (proc = cgpu; proc; proc = proc->next_proc)
 		{
 			mythr = proc->thr[0];
@@ -489,7 +488,7 @@ void minerloop_queue(struct thr_info *thr)
 	
 	while (likely(!cgpu->shutdown)) {
 		tv_timeout.tv_sec = -1;
-		gettimeofday(&tv_now, NULL);
+		timer_set_now(&tv_now);
 		for (proc = cgpu; proc; proc = proc->next_proc)
 		{
 			mythr = proc->thr[0];
@@ -618,9 +617,11 @@ 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);
 	
+#ifdef HAVE_FPGAUTILS
 	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);
+#endif
 	
 	devices_new = realloc(devices_new, sizeof(struct cgpu_info *) * (total_devices_new + lpcount + 1));
 	devices_new[total_devices_new++] = cgpu;
@@ -670,3 +671,108 @@ bool add_cgpu(struct cgpu_info *cgpu)
 	
 	return true;
 }
+
+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 = flags & 4;
+	char found = 0;
+	bool forceauto = flags & 1;
+	bool hasname;
+	size_t namel = strlen(api->name);
+	size_t dnamel = strlen(api->dname);
+
+#ifdef HAVE_FPGAUTILS
+	clear_detectone_meta_info();
+#endif
+	DL_FOREACH_SAFE(scan_devices, iter, tmp) {
+		dev = iter->string;
+		if ((colon = strchr(dev, ':')) && colon[1] != '\0') {
+			size_t idlen = colon - dev;
+
+			// allow either name:device or dname:device
+			if ((idlen != namel || strncasecmp(dev, api->name, idlen))
+			&&  (idlen != dnamel || strncasecmp(dev, api->dname, idlen)))
+				continue;
+
+			dev = colon + 1;
+			hasname = true;
+		}
+		else
+			hasname = false;
+		if (!strcmp(dev, "auto"))
+			forceauto = true;
+		else if (!strcmp(dev, "noauto"))
+			inhibitauto = true;
+		else
+		if ((flags & 2) && !hasname)
+			continue;
+		else
+		if (!detectone)
+		{}  // do nothing
+#ifdef HAVE_FPGAUTILS
+		else
+		if (serial_claim(dev, NULL))
+		{
+			applog(LOG_DEBUG, "%s is already claimed... skipping probes", dev);
+			string_elist_del(&scan_devices, iter);
+		}
+#endif
+		else if (detectone(dev)) {
+			string_elist_del(&scan_devices, iter);
+			inhibitauto = true;
+			++found;
+		}
+	}
+
+	if ((forceauto || !inhibitauto) && autoscan)
+		found += autoscan();
+
+	return found;
+}
+
+static
+FILE *_open_bitstream(const char *path, const char *subdir, const char *sub2, const char *filename)
+{
+	char fullpath[PATH_MAX];
+	strcpy(fullpath, path);
+	strcat(fullpath, "/");
+	if (subdir) {
+		strcat(fullpath, subdir);
+		strcat(fullpath, "/");
+	}
+	if (sub2) {
+		strcat(fullpath, sub2);
+		strcat(fullpath, "/");
+	}
+	strcat(fullpath, filename);
+	return fopen(fullpath, "rb");
+}
+#define _open_bitstream(path, subdir, sub2)  do {  \
+	f = _open_bitstream(path, subdir, sub2, filename);  \
+	if (f)  \
+		return f;  \
+} while(0)
+
+#define _open_bitstream2(path, path3)  do {  \
+	_open_bitstream(path, NULL, path3);  \
+	_open_bitstream(path, "../share/" PACKAGE, path3);  \
+} while(0)
+
+#define _open_bitstream3(path)  do {  \
+	_open_bitstream2(path, dname);  \
+	_open_bitstream2(path, "bitstreams");  \
+	_open_bitstream2(path, NULL);  \
+} while(0)
+
+FILE *open_bitstream(const char *dname, const char *filename)
+{
+	FILE *f;
+
+	_open_bitstream3(opt_kernel_path);
+	_open_bitstream3(cgminer_path);
+	_open_bitstream3(".");
+
+	return NULL;
+}

+ 21 - 0
deviceapi.h

@@ -32,4 +32,25 @@ extern void minerloop_queue(struct thr_info *);
 
 extern void *miner_thread(void *);
 
+typedef bool(*detectone_func_t)(const char*);
+typedef int(*autoscan_func_t)();
+
+extern int _serial_detect(struct device_drv *api, detectone_func_t, autoscan_func_t, int flags);
+#define serial_detect_fauto(api, detectone, autoscan)  \
+	_serial_detect(api, detectone, autoscan, 1)
+#define serial_detect_auto(api, detectone, autoscan)  \
+	_serial_detect(api, detectone, autoscan, 0)
+#define serial_detect_auto_byname(api, detectone, autoscan)  \
+	_serial_detect(api, detectone, autoscan, 2)
+#define serial_detect(api, detectone)  \
+	_serial_detect(api, detectone,     NULL, 0)
+#define serial_detect_byname(api, detectone)  \
+	_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 FILE *open_bitstream(const char *dname, const char *filename);
+
 #endif

+ 3 - 5
driver-avalon.c

@@ -662,7 +662,6 @@ static bool avalon_prepare(struct thr_info *thr)
 {
 	struct cgpu_info *avalon = thr->cgpu;
 	struct avalon_info *info = avalon->device_data;
-	struct timeval now;
 
 	free(avalon->works);
 	avalon->works = calloc(info->miner_count * sizeof(struct work *),
@@ -674,8 +673,7 @@ static bool avalon_prepare(struct thr_info *thr)
 	else
 		__avalon_init(avalon);
 
-	cgtime(&now);
-	get_datestamp(avalon->init, &now);
+	get_now_datestamp(avalon->init);
 	avalon->status = LIFE_INIT2;
 	return true;
 }
@@ -925,9 +923,9 @@ static int64_t avalon_scanhash(struct thr_info *thr)
 		if (opt_debug) {
 			timersub(&tv_finish, &tv_start, &elapsed);
 			applog(LOG_DEBUG,
-			       "Avalon: nonce = 0x%08x = 0x%08"PRIx64" hashes "
+			       "Avalon: nonce = 0x%08"PRIx32" = 0x%08"PRIx64" hashes "
 			       "(%ld.%06lds)", nonce, (uint64_t)hash_count,
-			       elapsed.tv_sec, elapsed.tv_usec);
+			       (long)elapsed.tv_sec, (long)elapsed.tv_usec);
 		}
 	}
 	if (hash_count && avalon->results < AVALON_ARRAY_SIZE)

+ 69 - 6
driver-bitforce.c

@@ -10,6 +10,7 @@
 
 #include "config.h"
 
+#include <ctype.h>
 #include <limits.h>
 #include <pthread.h>
 #include <stdint.h>
@@ -48,6 +49,7 @@ enum bitforce_proto {
 	BFP_RANGE,
 	BFP_QUEUE,
 	BFP_BQUEUE,
+	BFP_PQUEUE,
 };
 
 static const char *protonames[] = {
@@ -55,6 +57,7 @@ static const char *protonames[] = {
 	"nonce range",
 	"work queue",
 	"bulk queue",
+	"parallel queue",
 };
 
 struct device_drv bitforce_drv;
@@ -321,6 +324,7 @@ struct bitforce_data {
 	unsigned sleep_ms_default;
 	struct timeval tv_hashmeter_start;
 	float temp[2];
+	char *voltinfo;
 };
 
 struct bitforce_proc_data {
@@ -356,7 +360,6 @@ static bool bitforce_thread_prepare(struct thr_info *thr)
 {
 	struct cgpu_info *bitforce = thr->cgpu;
 	int fdDev = BFopen(bitforce->device_path);
-	struct timeval now;
 
 	if (unlikely(fdDev == -1)) {
 		applog(LOG_ERR, "%s: Failed to open %s", bitforce->dev_repr, bitforce->device_path);
@@ -366,8 +369,7 @@ static bool bitforce_thread_prepare(struct thr_info *thr)
 	bitforce->device_fd = fdDev;
 
 	applog(LOG_INFO, "%s: Opened %s", bitforce->dev_repr, bitforce->device_path);
-	cgtime(&now);
-	get_datestamp(bitforce->init, &now);
+	get_now_datestamp(bitforce->init);
 
 	return true;
 }
@@ -546,7 +548,8 @@ static bool bitforce_get_temp(struct cgpu_info *bitforce)
 	struct bitforce_data *data = bitforce->device_data;
 	pthread_mutex_t *mutexp = &bitforce->device->device_mutex;
 	int fdDev = bitforce->device->device_fd;
-	char pdevbuf[0x100];
+	char pdevbuf[0x40];
+	char voltbuf[0x40];
 	char *s;
 	struct cgpu_info *chip_cgpu;
 
@@ -569,9 +572,51 @@ static bool bitforce_get_temp(struct cgpu_info *bitforce)
 	if (mutex_trylock(mutexp))
 		return false;
 
+	if (data->sc)
+		bitforce_cmd1(fdDev, data->xlink_id, voltbuf, sizeof(voltbuf), "ZTX");
 	bitforce_cmd1(fdDev, data->xlink_id, pdevbuf, sizeof(pdevbuf), "ZLX");
 	mutex_unlock(mutexp);
 	
+	if (data->sc && likely(voltbuf[0]))
+	{
+		// Process voltage info
+		// "NNNxxx,NNNxxx,NNNxxx" -> "NNN.xxx / NNN.xxx / NNN.xxx"
+		size_t sz = strlen(voltbuf) * 4;
+		char *saveptr, *v, *outbuf = malloc(sz);
+		char *out = outbuf;
+		if (!out)
+			goto skipvolts;
+		for (v = strtok_r(voltbuf, ",", &saveptr); v; v = strtok_r(NULL, ",", &saveptr))
+		{
+			while (isCspace(v[0]))
+				++v;
+			sz = strlen(v);
+			while (isCspace(v[sz - 1]))
+				--sz;
+			if (sz < 4)
+			{
+				memcpy(out, "0.00? / ", 8);
+				memcpy(&out[5 - sz], v, sz);
+				out += 8;
+			}
+			else
+			{
+				memcpy(out, v, sz - 3);
+				out += sz - 3;
+				out[0] = '.';
+				memcpy(&out[1], &v[sz - 3], 3);
+				memcpy(&out[4], " / ", 3);
+				out += 7;
+			}
+		}
+		out[-3] = '\0';
+		assert(out[-2]=='/');
+		saveptr = data->voltinfo;
+		data->voltinfo = outbuf;
+		free(saveptr);
+	}
+	
+skipvolts:
 	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);
@@ -652,6 +697,8 @@ bool bitforce_job_prepare(struct thr_info *thr, struct work *work, __maybe_unuse
 	{
 		case BFP_BQUEUE:
 			quit(1, "%"PRIpreprv": Impossible BFP_BQUEUE in bitforce_job_prepare", bitforce->proc_repr);
+		case BFP_PQUEUE:
+			quit(1, "%"PRIpreprv": Impossible BFP_PQUEUE in bitforce_job_prepare", bitforce->proc_repr);
 		case BFP_RANGE:
 		{
 			uint32_t *ob_nonce = (uint32_t*)&(ob_dt[32]);
@@ -1232,6 +1279,8 @@ static bool bitforce_thread_init(struct thr_info *thr)
 	bool sc = initdata->sc;
 	int xlink_id = 0, boardno = 0;
 	struct bitforce_proc_data *first_on_this_board;
+	char buf[100];
+	int fd = bitforce->device_fd;
 	
 	for ( ; bitforce; bitforce = bitforce->next_proc)
 	{
@@ -1269,7 +1318,7 @@ static bool bitforce_thread_init(struct thr_info *thr)
 			
 			if (bitforce->drv == &bitforce_queue_api)
 			{
-				bitforce_change_mode(bitforce, BFP_BQUEUE);
+				bitforce_change_mode(bitforce, data->parallel_protocol ? BFP_PQUEUE : BFP_BQUEUE);
 				bitforce->sleep_ms = data->sleep_ms_default = 100;
 				timer_set_delay_from_now(&thr->tv_poll, 0);
 				data->queued_max = data->parallel * 2;
@@ -1280,6 +1329,9 @@ static bool bitforce_thread_init(struct thr_info *thr)
 			}
 			else
 				bitforce_change_mode(bitforce, BFP_QUEUE);
+			
+			// Clear job queue to start fresh; ignore response
+			bitforce_cmd1(fd, data->xlink_id, buf, sizeof(buf), "ZQX");
 		}
 		else
 		{
@@ -1308,6 +1360,7 @@ static bool bitforce_thread_init(struct thr_info *thr)
 			procdata->cgpu = bitforce;
 			bitforce->device_data = data;
 			bitforce->status = LIFE_INIT2;
+			bitforce->kname = first_on_this_board->cgpu->kname;
 		}
 		applog(LOG_DEBUG, "%s: Board %d: %"PRIpreprv"-%"PRIpreprv, bitforce->dev_repr, boardno, first_on_this_board->cgpu->proc_repr, bitforce->proc_repr);
 		
@@ -1316,6 +1369,7 @@ static bool bitforce_thread_init(struct thr_info *thr)
 		{}
 	}
 	
+	// NOTE: This doesn't restore the first processor, but it does get us the last one; this is sufficient for the delay debug and start of the next loop below
 	bitforce = thr->cgpu;
 
 	free(initdata->parallels);
@@ -1327,6 +1381,13 @@ static bool bitforce_thread_init(struct thr_info *thr)
 	applog(LOG_DEBUG, "%s: Delaying start by %dms", bitforce->dev_repr, wait / 1000);
 	nmsleep(wait);
 
+	if (sc)
+	{
+		// Clear results queue last, to start fresh; ignore response
+		for (bitforce = bitforce->device; bitforce; bitforce = bitforce->next_proc)
+			bitforce_zox(thr, "ZOX");
+	}
+	
 	return true;
 }
 
@@ -1383,6 +1444,8 @@ 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]);
+	if (data->voltinfo)
+		wlogprint("Voltages: %s\n", data->voltinfo);
 }
 #endif
 
@@ -1396,7 +1459,7 @@ static struct api_data *bitforce_drv_stats(struct cgpu_info *cgpu)
 	// locking access to displaying API debug 'stats'
 	// If locking becomes an issue for any of them, use copy_data=true also
 	root = api_add_uint(root, "Sleep Time", &(cgpu->sleep_ms), false);
-	if (data->proto != BFP_BQUEUE)
+	if (data->proto != BFP_BQUEUE && data->proto != BFP_PQUEUE)
 		root = api_add_uint(root, "Avg Wait", &(cgpu->avg_wait_d), false);
 	if (data->temp[0] > 0 && data->temp[1] > 0)
 	{

+ 3 - 3
driver-cpu.c

@@ -30,7 +30,7 @@
 #include <libgen.h>
 
 #include "compat.h"
-#include "fpgautils.h"
+#include "deviceapi.h"
 #include "miner.h"
 #include "bench_block.h"
 #include "util.h"
@@ -246,7 +246,7 @@ double bench_algo_stage3(
 
 	memcpy(&hash1[0], &hash1_init[0], sizeof(hash1));
 
-	gettimeofday(&start, 0);
+	timer_set_now(&start);
 			{
 				sha256_func func = sha256_funcs[algo];
 				(*func)(
@@ -261,7 +261,7 @@ double bench_algo_stage3(
 					work.blk.nonce
 				);
 			}
-	gettimeofday(&end, 0);
+	timer_set_now(&end);
 
 	uint64_t usec_end = ((uint64_t)end.tv_sec)*1000*1000 + end.tv_usec;
 	uint64_t usec_start = ((uint64_t)start.tv_sec)*1000*1000 + start.tv_usec;

+ 21 - 2
driver-erupter.c

@@ -7,6 +7,9 @@
  * any later version.  See COPYING for more details.
  */
 
+#include "config.h"
+
+#include "miner.h"
 #include "fpgautils.h"
 #include "icarus-common.h"
 
@@ -37,12 +40,19 @@ static bool _erupter_detect_one(const char *devpath, struct device_drv *drv)
 
 static bool erupter_emerald_detect_one(const char *devpath)
 {
+	// For detection via BEE:*
 	return _erupter_detect_one(devpath, &erupter_drv_emerald);
 }
 
 static bool erupter_detect_one(const char *devpath)
 {
-	return _erupter_detect_one(devpath, &erupter_drv);
+	struct device_drv *drv = &erupter_drv;
+	
+	// For autodetection
+	if (unlikely(detectone_meta_info.product && strstr(detectone_meta_info.product, "Emerald")))
+		drv = &erupter_drv_emerald;
+	
+	return _erupter_detect_one(devpath, drv);
 }
 
 static int erupter_emerald_detect_auto(void)
@@ -61,8 +71,16 @@ 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);
+	serial_detect_auto_byname(&erupter_drv_emerald, erupter_emerald_detect_one, erupter_emerald_detect_auto);
+}
+
+static bool erupter_identify(struct cgpu_info *erupter)
+{
+	struct thr_info *thr = erupter->thr[0];
+	struct icarus_state *state = thr->cgpu_data;
+	state->identify = true;
+	return true;
 }
 
 static void erupter_drv_init()
@@ -71,6 +89,7 @@ static void erupter_drv_init()
 	erupter_drv.dname = "erupter";
 	erupter_drv.name = "BES";
 	erupter_drv.drv_detect = erupter_detect;
+	erupter_drv.identify_device = erupter_identify;
 	
 	erupter_drv_emerald = erupter_drv;
 	erupter_drv_emerald.name = "BEE";

+ 111 - 26
driver-icarus.c

@@ -645,8 +645,6 @@ static bool icarus_prepare(struct thr_info *thr)
 	struct cgpu_info *icarus = thr->cgpu;
 	struct ICARUS_INFO *info = icarus->device_data;
 
-	struct timeval now;
-
 	icarus->device_fd = -1;
 
 	int fd = icarus_open2(icarus->device_path, info->baud, true);
@@ -659,8 +657,7 @@ static bool icarus_prepare(struct thr_info *thr)
 	icarus->device_fd = fd;
 
 	applog(LOG_INFO, "Opened Icarus on %s", icarus->device_path);
-	cgtime(&now);
-	get_datestamp(icarus->init, &now);
+	get_now_datestamp(icarus->init);
 
 	struct icarus_state *state;
 	thr->cgpu_data = state = calloc(1, sizeof(*state));
@@ -750,13 +747,44 @@ static bool icarus_reopen(struct cgpu_info *icarus, struct icarus_state *state,
 	return true;
 }
 
-static bool icarus_start_work(struct thr_info *thr, const unsigned char *ob_bin)
+static
+bool icarus_job_prepare(struct thr_info *thr, struct work *work, __maybe_unused uint64_t max_nonce)
+{
+	struct cgpu_info * const icarus = thr->cgpu;
+	struct icarus_state * const state = thr->cgpu_data;
+	uint8_t * const ob_bin = state->ob_bin;
+	
+	memcpy(ob_bin, work->midstate, 32);
+	memcpy(ob_bin + 52, work->data + 64, 12);
+	if (!(memcmp(&ob_bin[56], "\xff\xff\xff\xff", 4)
+	   || memcmp(&ob_bin, "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0", 32))) {
+		// This sequence is used on cairnsmore bitstreams for commands, NEVER send it otherwise
+		applog(LOG_WARNING, "%"PRIpreprv": Received job attempting to send a command, corrupting it!",
+		       icarus->proc_repr);
+		ob_bin[56] = 0;
+	}
+	rev(ob_bin, 32);
+	rev(ob_bin + 52, 12);
+	
+	return true;
+}
+
+static bool icarus_job_start(struct thr_info *thr)
 {
 	struct cgpu_info *icarus = thr->cgpu;
+	struct ICARUS_INFO *info = icarus->device_data;
 	struct icarus_state *state = thr->cgpu_data;
+	const uint8_t * const ob_bin = state->ob_bin;
 	int fd = icarus->device_fd;
 	int ret;
 
+	// Handle dynamic clocking for "subclass" devices
+	// This needs to run before sending next job, since it hashes the command too
+	if (info->dclk.freqM && likely(!state->firstrun)) {
+		dclk_preUpdate(&info->dclk);
+		dclk_updateFreq(&info->dclk, info->dclk_change_clock_func, thr);
+	}
+	
 	cgtime(&state->tv_workstart);
 
 	ret = icarus_write(fd, ob_bin, 64);
@@ -778,6 +806,63 @@ static bool icarus_start_work(struct thr_info *thr, const unsigned char *ob_bin)
 	return true;
 }
 
+static
+void handle_identify(struct thr_info * const thr, int ret, const bool was_first_run)
+{
+	const struct cgpu_info * const icarus = thr->cgpu;
+	const struct ICARUS_INFO * const info = icarus->device_data;
+	struct icarus_state * const state = thr->cgpu_data;
+	int fd = icarus->device_fd;
+	struct timeval tv_now;
+	double delapsed;
+	uint32_t nonce;
+	
+	// If identify is requested (block erupters):
+	// 1. Don't start the next job right away (above)
+	// 2. Wait for the current job to complete 100%
+	
+	if (!was_first_run)
+	{
+		applog(LOG_DEBUG, "%"PRIpreprv": Identify: Waiting for current job to finish", icarus->proc_repr);
+		while (true)
+		{
+			cgtime(&tv_now);
+			delapsed = tdiff(&tv_now, &state->tv_workstart);
+			if (delapsed + 0.1 > info->fullnonce)
+				break;
+			
+			// Try to get more nonces (ignoring work restart)
+			ret = icarus_gets((void *)&nonce, fd, &tv_now, NULL, (info->fullnonce - delapsed) * 10);
+			if (ret == ICA_GETS_OK)
+			{
+				nonce = be32toh(nonce);
+				submit_nonce(thr, &state->last_work, nonce);
+			}
+		}
+	}
+	else
+		applog(LOG_DEBUG, "%"PRIpreprv": Identify: Current job should already be finished", icarus->proc_repr);
+	
+	// 3. Delay 3 more seconds
+	applog(LOG_DEBUG, "%"PRIpreprv": Identify: Leaving idle for 3 seconds", icarus->proc_repr);
+	nmsleep(3000);
+	
+	// Check for work restart in the meantime
+	if (thr->work_restart)
+	{
+		applog(LOG_DEBUG, "%"PRIpreprv": Identify: Work restart requested during delay", icarus->proc_repr);
+		goto no_job_start;
+	}
+	
+	// 4. Start next job
+	applog(LOG_DEBUG, "%"PRIpreprv": Identify: Starting next job", icarus->proc_repr);
+	if (!icarus_job_start(thr))
+no_job_start:
+		state->firstrun = true;
+	
+	state->identify = false;
+}
+
 static int64_t icarus_scanhash(struct thr_info *thr, struct work *work,
 				__maybe_unused int64_t max_nonce)
 {
@@ -787,7 +872,7 @@ static int64_t icarus_scanhash(struct thr_info *thr, struct work *work,
 
 	struct ICARUS_INFO *info;
 
-	unsigned char ob_bin[64] = {0}, nonce_bin[ICARUS_READ_SIZE] = {0};
+	unsigned char nonce_bin[ICARUS_READ_SIZE] = {0};
 	uint32_t nonce;
 	int64_t hash_count;
 	struct timeval tv_start, elapsed;
@@ -795,6 +880,7 @@ static int64_t icarus_scanhash(struct thr_info *thr, struct work *work,
 	double Ti, Xi;
 	int curr_hw_errors, i;
 	bool was_hw_error;
+	bool was_first_run;
 
 	struct ICARUS_HISTORY *history0, *history;
 	int count;
@@ -808,19 +894,9 @@ static int64_t icarus_scanhash(struct thr_info *thr, struct work *work,
 
 	icarus = thr->cgpu;
 	struct icarus_state *state = thr->cgpu_data;
+	was_first_run = state->firstrun;
 
-	// Prepare the next work immediately
-	memcpy(ob_bin, work->midstate, 32);
-	memcpy(ob_bin + 52, work->data + 64, 12);
-	if (!(memcmp(&ob_bin[56], "\xff\xff\xff\xff", 4)
-	   || memcmp(&ob_bin, "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0", 32))) {
-		// This sequence is used on cairnsmore bitstreams for commands, NEVER send it otherwise
-		applog(LOG_WARNING, "%"PRIpreprv": Received job attempting to send a command, corrupting it!",
-		       icarus->proc_repr);
-		ob_bin[56] = 0;
-	}
-	rev(ob_bin, 32);
-	rev(ob_bin + 52, 12);
+	icarus_job_prepare(thr, work, max_nonce);
 
 	// Wait for the previous run's result
 	fd = icarus->device_fd;
@@ -874,17 +950,20 @@ static int64_t icarus_scanhash(struct thr_info *thr, struct work *work,
 
 	// Handle dynamic clocking for "subclass" devices
 	// This needs to run before sending next job, since it hashes the command too
-	if (info->dclk.freqM && likely(!state->firstrun)) {
+	if (info->dclk.freqM && likely(!was_first_run)) {
 		int qsec = ((4 * elapsed.tv_sec) + (elapsed.tv_usec / 250000)) ?: 1;
 		for (int n = qsec; n; --n)
 			dclk_gotNonces(&info->dclk);
 		if (nonce && !test_nonce(&state->last_work, nonce, false))
 			dclk_errorCount(&info->dclk, qsec);
-		dclk_preUpdate(&info->dclk);
-		dclk_updateFreq(&info->dclk, info->dclk_change_clock_func, thr);
 	}
 
-	if (!icarus_start_work(thr, ob_bin))
+	if (unlikely(state->identify))
+	{
+		// Delay job start until later...
+	}
+	else
+	if (!icarus_job_start(thr))
 		/* This should never happen */
 		state->firstrun = true;
 
@@ -893,10 +972,11 @@ static int64_t icarus_scanhash(struct thr_info *thr, struct work *work,
 
 	work->blk.nonce = 0xffffffff;
 
-	if (state->firstrun) {
+	if (was_first_run) {
 		state->firstrun = false;
 		__copy_work(&state->last_work, work);
-		return 0;
+		hash_count = 0;
+		goto out;
 	}
 
 	// OK, done starting Icarus's next job... now process the last run's result!
@@ -921,7 +1001,8 @@ static int64_t icarus_scanhash(struct thr_info *thr, struct work *work,
 					(int64_t)elapsed.tv_sec, (unsigned long)elapsed.tv_usec);
 		}
 
-		return estimate_hashes;
+		hash_count = estimate_hashes;
+		goto out;
 	}
 
 	curr_hw_errors = icarus->hw_errors;
@@ -935,7 +1016,7 @@ static int64_t icarus_scanhash(struct thr_info *thr, struct work *work,
 			if (!icarus_reopen(icarus, state, &fd))
 				state->firstrun = true;
 			// Some devices (Cairnsmore1, for example) abort hashing when reopened, so send the job again
-			if (!icarus_start_work(thr, ob_bin))
+			if (!icarus_job_start(thr))
 				state->firstrun = true;
 		}
 
@@ -1082,6 +1163,10 @@ static int64_t icarus_scanhash(struct thr_info *thr, struct work *work,
 		timeradd(&tv_history_finish, &(info->history_time), &(info->history_time));
 	}
 
+out:
+	if (unlikely(state->identify))
+		handle_identify(thr, ret, was_first_run);
+	
 	return hash_count;
 }
 

+ 76 - 22
driver-modminer.c

@@ -26,7 +26,7 @@
 #define BISTREAM_USER_ID "\2\4$B"
 
 #define MODMINER_MAX_CLOCK 250
-#define MODMINER_DEF_CLOCK 210
+#define MODMINER_DEF_CLOCK 190
 #define MODMINER_MIN_CLOCK   2
 
 // Commands
@@ -64,7 +64,7 @@ struct modminer_fpga_state {
 	// Number of nonces did meet pdiff 1, ever
 	int good_share_counter;
 	// Time the clock was last reduced due to temperature
-	time_t last_cutoff_reduced;
+	struct timeval tv_last_cutoff_reduced;
 
 	unsigned char temp;
 
@@ -157,7 +157,7 @@ modminer_detect_one(const char *devpath)
 static int
 modminer_detect_auto()
 {
-	return serial_autodetect(modminer_detect_one, "BTCFPGA", "ModMiner");
+	return serial_autodetect(modminer_detect_one, "ModMiner");
 }
 
 static void
@@ -276,9 +276,7 @@ modminer_device_prepare(struct cgpu_info *modminer)
 	modminer->device->device_fd = fd;
 	applog(LOG_INFO, "%s: Opened %s", modminer->dev_repr, modminer->device_path);
 
-	struct timeval now;
-	gettimeofday(&now, NULL);
-	get_datestamp(modminer->init, &now);
+	get_now_datestamp(modminer->init);
 
 	return true;
 }
@@ -486,9 +484,10 @@ static void modminer_get_temperature(struct cgpu_info *modminer, struct thr_info
 		state->temp = temperature;
 		if (temperature > modminer->targettemp + opt_hysteresis) {
 			{
-				time_t now = time(NULL);
-				if (state->last_cutoff_reduced != now) {
-					state->last_cutoff_reduced = now;
+				struct timeval now;
+				cgtime(&now);
+				if (timer_elapsed(&state->tv_last_cutoff_reduced, &now)) {
+					state->tv_last_cutoff_reduced = now;
 					int oldFreq = state->dclk.freqM;
 					if (modminer_reduce_clock(thr, false))
 						applog(LOG_NOTICE, "%s: Frequency %s from %u to %u MHz (temp: %d)",
@@ -593,7 +592,7 @@ fd_set fds;
 
 	if (46 != write(fd, state->next_work_cmd, 46))
 		bailout2(LOG_ERR, "%s: Error writing (start work)", modminer->proc_repr);
-	gettimeofday(&state->tv_workstart, NULL);
+	timer_set_now(&state->tv_workstart);
 	state->hashes = 0;
 	status_read("start work");
 	mutex_unlock(mutexp);
@@ -674,7 +673,7 @@ modminer_process_results(struct thr_info*thr)
 	}
 
 	struct timeval tv_workend, elapsed;
-	gettimeofday(&tv_workend, NULL);
+	timer_set_now(&tv_workend);
 	timersub(&tv_workend, &state->tv_workstart, &elapsed);
 
 	uint64_t hashes = (uint64_t)state->dclk.freqM * 2 * (((uint64_t)elapsed.tv_sec * 1000000) + elapsed.tv_usec);
@@ -744,6 +743,21 @@ modminer_fpga_shutdown(struct thr_info *thr)
 	thr->cgpu_data = NULL;
 }
 
+static
+bool modminer_user_set_clock(struct cgpu_info *cgpu, const int val)
+{
+	struct thr_info * const thr = cgpu->thr[0];
+	struct modminer_fpga_state * const state = thr->cgpu_data;
+	const int multiplier = val / 2;
+	const uint8_t oldFreqM = state->dclk.freqM;
+	const signed char delta = (multiplier - oldFreqM) * 2;
+	state->dclk.freqMDefault = multiplier;
+	const bool rv = modminer_change_clock(thr, true, delta);
+	if (likely(rv))
+		dclk_msg_freqchange(cgpu->proc_repr, oldFreqM * 2, state->dclk.freqM * 2, " on user request");
+	return rv;
+}
+
 static char *modminer_set_device(struct cgpu_info *modminer, char *option, char *setting, char *replybuf)
 {
 	int val;
@@ -755,8 +769,6 @@ static char *modminer_set_device(struct cgpu_info *modminer, char *option, char
 	}
 
 	if (strcasecmp(option, "clock") == 0) {
-		int multiplier;
-
 		if (!setting || !*setting) {
 			sprintf(replybuf, "missing clock setting");
 			return replybuf;
@@ -769,20 +781,13 @@ static char *modminer_set_device(struct cgpu_info *modminer, char *option, char
 			return replybuf;
 		}
 
-		multiplier = val / 2;
-		struct thr_info *thr = modminer->thr[0];
-		struct modminer_fpga_state *state = thr->cgpu_data;
-		uint8_t oldFreqM = state->dclk.freqM;
-		signed char delta = (multiplier - oldFreqM) * 2;
-		state->dclk.freqMDefault = multiplier;
-		if (unlikely(!modminer_change_clock(thr, true, delta))) {
+		if (unlikely(!modminer_user_set_clock(modminer, val)))
+		{
 			sprintf(replybuf, "Set clock failed: %s",
 			        modminer->proc_repr);
 			return replybuf;
 		}
 
-		dclk_msg_freqchange(modminer->proc_repr, oldFreqM * 2, state->dclk.freqM * 2, " on user request");
-
 		return NULL;
 	}
 
@@ -790,6 +795,50 @@ static char *modminer_set_device(struct cgpu_info *modminer, char *option, char
 	return replybuf;
 }
 
+#ifdef HAVE_CURSES
+static
+void modminer_tui_wlogprint_choices(struct cgpu_info *cgpu)
+{
+	wlogprint("[C]lock speed ");
+}
+
+static
+const char *modminer_tui_handle_choice(struct cgpu_info *cgpu, int input)
+{
+	static char buf[0x100];  // Static for replies
+	
+	switch (input)
+	{
+		case 'c': case 'C':
+		{
+			int val;
+			char *intvar;
+			
+			sprintf(buf, "Set clock speed (range %d-%d, multiple of 2)", MODMINER_MIN_CLOCK, MODMINER_MAX_CLOCK);
+			intvar = curses_input(buf);
+			if (!intvar)
+				return "Invalid clock speed\n";
+			val = atoi(intvar);
+			free(intvar);
+			if (val < MODMINER_MIN_CLOCK || val > MODMINER_MAX_CLOCK || (val & 1) != 0)
+				return "Invalid clock speed\n";
+			
+			if (unlikely(!modminer_user_set_clock(cgpu, val)))
+				return "Set clock failed\n";
+			return "Clock speed changed\n";
+		}
+	}
+	return NULL;
+}
+
+static
+void modminer_wlogprint_status(struct cgpu_info *cgpu)
+{
+	struct modminer_fpga_state *state = cgpu->thr[0]->cgpu_data;
+	wlogprint("Clock speed: %d\n", (int)(state->dclk.freqM * 2));
+}
+#endif
+
 struct device_drv modminer_drv = {
 	.dname = "modminer",
 	.name = "MMQ",
@@ -798,6 +847,11 @@ struct device_drv modminer_drv = {
 	.get_stats = modminer_get_stats,
 	.get_api_extra_device_status = get_modminer_drv_extra_device_status,
 	.set_device = modminer_set_device,
+#ifdef HAVE_CURSES
+	.proc_wlogprint_status = modminer_wlogprint_status,
+	.proc_tui_wlogprint_choices = modminer_tui_wlogprint_choices,
+	.proc_tui_handle_choice = modminer_tui_handle_choice,
+#endif
 	.thread_prepare = modminer_fpga_prepare,
 	.thread_init = modminer_fpga_init,
 	.scanhash = modminer_scanhash,

+ 5 - 9
driver-opencl.c

@@ -39,8 +39,8 @@
 #define OMIT_OPENCL_API
 
 #include "compat.h"
-#include "fpgautils.h"
 #include "miner.h"
+#include "deviceapi.h"
 #include "driver-opencl.h"
 #include "findnonce.h"
 #include "ocl.h"
@@ -888,7 +888,7 @@ void opencl_wlogprint_status(struct cgpu_info *cgpu)
 		if (thr->cgpu != cgpu)
 			continue;
 		
-		get_datestamp(checkin, &thr->last);
+		get_datestamp(checkin, time(NULL) - timer_elapsed(&thr->last, NULL));
 		displayed_rolling = thr->rolling;
 		if (!mhash_base)
 			displayed_rolling *= 1000;
@@ -963,7 +963,7 @@ const char *opencl_tui_handle_choice(struct cgpu_info *cgpu, int input)
 			clear_logwin();
 			get_statline3(logline, cgpu, true, true);
 			wattron(logwin, A_BOLD);
-			waddstr(logwin, logline);
+			wlogprint("%s", logline);
 			wattroff(logwin, A_BOLD);
 			wlogprint("\n");
 			
@@ -1251,7 +1251,6 @@ void *reinit_gpu(void *userdata)
 	struct thr_info *mythr = userdata;
 	struct cgpu_info *cgpu, *sel_cgpu;
 	struct thr_info *thr;
-	struct timeval now;
 	char name[256];
 	int thr_id;
 	int i;
@@ -1317,8 +1316,7 @@ select_cgpu:
 		applog(LOG_WARNING, "Thread %d restarted", thr_id);
 	}
 
-	cgtime(&now);
-	get_datestamp(sel_cgpu->init, &now);
+	get_now_datestamp(sel_cgpu->init);
 
 	proc_enable(cgpu);
 
@@ -1500,7 +1498,6 @@ static uint32_t *blank_res;
 static bool opencl_thread_prepare(struct thr_info *thr)
 {
 	char name[256];
-	struct timeval now;
 	struct cgpu_info *cgpu = thr->cgpu;
 	int gpu = cgpu->device_id;
 	int virtual_gpu = cgpu->virtual_gpu;
@@ -1571,8 +1568,7 @@ static bool opencl_thread_prepare(struct thr_info *thr)
 		}
 	}
 	applog(LOG_INFO, "initCl() finished. Found %s", name);
-	cgtime(&now);
-	get_datestamp(cgpu->init, &now);
+	get_now_datestamp(cgpu->init);
 
 	have_opencl = true;
 

+ 107 - 29
driver-x6500.c

@@ -1,5 +1,6 @@
 /*
  * Copyright 2012-2013 Luke Dashjr
+ * Copyright 2012 Andrew Smith
  *
  * This program is free software; you can redistribute it and/or modify it
  * under the terms of the GNU General Public License as published by the Free
@@ -13,6 +14,7 @@
 #include <winsock2.h>
 #endif
 
+#include <limits.h>
 #include <math.h>
 #include <sys/time.h>
 
@@ -32,7 +34,7 @@
 // NOTE: X6500_BITSTREAM_USERID is bitflipped
 #define X6500_BITSTREAM_USERID "\x40\x20\x24\x42"
 #define X6500_MINIMUM_CLOCK    2
-#define X6500_DEFAULT_CLOCK  200
+#define X6500_DEFAULT_CLOCK  190
 #define X6500_MAXIMUM_CLOCK  250
 
 struct device_drv x6500_api;
@@ -200,10 +202,8 @@ struct x6500_fpga_data {
 	uint8_t freqMaxMaxM;
 
 	// Time the clock was last reduced due to temperature
-	time_t last_cutoff_reduced;
+	struct timeval tv_last_cutoff_reduced;
 
-	float temp;
-	
 	uint32_t prepwork_last_register;
 };
 
@@ -488,25 +488,27 @@ void x6500_get_temperature(struct cgpu_info *x6500)
 		if (!fpga) continue;
 
 		if (code[i] == 0xffff || !code[i]) {
-			fpga->temp = 0;
+			x6500->temp = 0;
 			continue;
 		}
 		if ((code[i] >> 15) & 1)
 			code[i] -= 0x10000;
-		fpga->temp = (float)(code[i] >> 2) * 0.03125f;
-		applog(LOG_DEBUG,"x6500_get_temperature: fpga[%d]->temp=%.1fC",i,fpga->temp);
+		x6500->temp = (float)(code[i] >> 2) * 0.03125f;
+		applog(LOG_DEBUG,"x6500_get_temperature: fpga[%d]->temp=%.1fC",
+		       i, x6500->temp);
 
-		int temperature = round(fpga->temp);
+		int temperature = round(x6500->temp);
 		if (temperature > x6500->targettemp + opt_hysteresis) {
-			time_t now = time(NULL);
-			if (fpga->last_cutoff_reduced != now) {
-				fpga->last_cutoff_reduced = now;
+			struct timeval now;
+			cgtime(&now);
+			if (timer_elapsed(&fpga->tv_last_cutoff_reduced, &now)) {
+				fpga->tv_last_cutoff_reduced = now;
 				int oldFreq = fpga->dclk.freqM;
 				if (x6500_change_clock(thr, oldFreq - 1))
 					applog(LOG_NOTICE, "%"PRIprepr": Frequency dropped from %u to %u MHz (temp: %.1fC)",
 					       x6500->proc_repr,
 					       oldFreq * 2, fpga->dclk.freqM * 2,
-					       fpga->temp
+					       x6500->temp
 					);
 				fpga->dclk.freqMaxM = fpga->dclk.freqM;
 			}
@@ -534,7 +536,6 @@ bool x6500_all_idle(struct cgpu_info *any_proc)
 
 static bool x6500_get_stats(struct cgpu_info *x6500)
 {
-	float hottest = 0;
 	if (x6500_all_idle(x6500)) {
 		struct cgpu_info *cgpu = x6500->device;
 		// Getting temperature more efficiently while running
@@ -547,18 +548,6 @@ static bool x6500_get_stats(struct cgpu_info *x6500)
 		mutex_unlock(mutexp);
 	}
 
-	for (int i = x6500->threads; i--; ) {
-		struct thr_info *thr = x6500->thr[i];
-		struct x6500_fpga_data *fpga = thr->cgpu_data;
-		if (!fpga)
-			continue;
-		float temp = fpga->temp;
-		if (temp > hottest)
-			hottest = temp;
-	}
-
-	x6500->temp = hottest;
-
 	return true;
 }
 
@@ -581,8 +570,6 @@ get_x6500_api_extra_device_status(struct cgpu_info *x6500)
 	struct x6500_fpga_data *fpga = thr->cgpu_data;
 	double d;
 
-	if (fpga->temp)
-		root = api_add_temp(root, "Temperature", &fpga->temp, true);
 	d = (double)fpga->dclk.freqM * 2;
 	root = api_add_freq(root, "Frequency", &d, true);
 	d = (double)fpga->dclk.freqMaxM * 2;
@@ -637,7 +624,7 @@ void x6500_job_start(struct thr_info *thr)
 
 	ft232r_flush(jp->a->ftdi);
 
-	gettimeofday(&tv_now, NULL);
+	timer_set_now(&tv_now);
 	if (!thr->prev_work)
 		fpga->tv_hashstart = tv_now;
 	else
@@ -693,7 +680,7 @@ int64_t x6500_process_results(struct thr_info *thr, struct work *work)
 	bool bad;
 
 	while (1) {
-		gettimeofday(&tv_now, NULL);
+		timer_set_now(&tv_now);
 		nonce = x6500_get_register(jtag, 0xE);
 		if (nonce != 0xffffffff) {
 			bad = !(work && test_nonce(work, nonce, false));
@@ -743,6 +730,91 @@ void x6500_fpga_poll(struct thr_info *thr)
 		timer_set_delay_from_now(&thr->tv_poll, 10000);
 }
 
+static
+void x6500_user_set_clock(struct cgpu_info *cgpu, const int val)
+{
+	struct thr_info * const thr = cgpu->thr[0];
+	struct x6500_fpga_data *fpga = thr->cgpu_data;
+	const int multiplier = val / 2;
+	fpga->dclk.freqMDefault = multiplier;
+}
+
+static
+char *x6500_set_device(struct cgpu_info *cgpu, char *option, char *setting, char *replybuf)
+{
+	int val;
+	
+	if (strcasecmp(option, "help") == 0) {
+		sprintf(replybuf, "clock: range %d-%d and a multiple of 2",
+		        X6500_MINIMUM_CLOCK, X6500_MAXIMUM_CLOCK);
+		return replybuf;
+	}
+	
+	if (strcasecmp(option, "clock") == 0) {
+		if (!setting || !*setting) {
+			sprintf(replybuf, "missing clock setting");
+			return replybuf;
+		}
+		
+		val = atoi(setting);
+		if (val < X6500_MINIMUM_CLOCK || val > X6500_MAXIMUM_CLOCK || (val & 1) != 0) {
+			sprintf(replybuf, "invalid clock: '%s' valid range %d-%d and a multiple of 2",
+			        setting, X6500_MINIMUM_CLOCK, X6500_MAXIMUM_CLOCK);
+			return replybuf;
+		}
+		
+		x6500_user_set_clock(cgpu, val);
+		
+		return NULL;
+	}
+
+	sprintf(replybuf, "Unknown option: %s", option);
+	return replybuf;
+}
+
+#ifdef HAVE_CURSES
+static
+void x6500_tui_wlogprint_choices(struct cgpu_info *cgpu)
+{
+	wlogprint("[C]lock speed ");
+}
+
+static
+const char *x6500_tui_handle_choice(struct cgpu_info *cgpu, int input)
+{
+	static char buf[0x100];  // Static for replies
+	
+	switch (input)
+	{
+		case 'c': case 'C':
+		{
+			int val;
+			char *intvar;
+			
+			sprintf(buf, "Set clock speed (range %d-%d, multiple of 2)", X6500_MINIMUM_CLOCK, X6500_MAXIMUM_CLOCK);
+			intvar = curses_input(buf);
+			if (!intvar)
+				return "Invalid clock speed\n";
+			val = atoi(intvar);
+			free(intvar);
+			if (val < X6500_MINIMUM_CLOCK || val > X6500_MAXIMUM_CLOCK || (val & 1) != 0)
+				return "Invalid clock speed\n";
+			
+			x6500_user_set_clock(cgpu, val);
+			return "Clock speed changed\n";
+		}
+	}
+	return NULL;
+}
+
+static
+void x6500_wlogprint_status(struct cgpu_info *cgpu)
+{
+	struct x6500_fpga_data *fpga = cgpu->thr[0]->cgpu_data;
+	wlogprint("Clock speed: %d\n", (int)(fpga->dclk.freqM * 2));
+}
+#endif
+
 struct device_drv x6500_api = {
 	.dname = "x6500",
 	.name = "XBS",
@@ -752,6 +824,12 @@ struct device_drv x6500_api = {
 	.get_stats = x6500_get_stats,
 	.override_statline_temp = get_x6500_upload_percent,
 	.get_api_extra_device_status = get_x6500_api_extra_device_status,
+	.set_device = x6500_set_device,
+#ifdef HAVE_CURSES
+	.proc_wlogprint_status = x6500_wlogprint_status,
+	.proc_tui_wlogprint_choices = x6500_tui_wlogprint_choices,
+	.proc_tui_handle_choice = x6500_tui_handle_choice,
+#endif
 	.poll = x6500_fpga_poll,
 	.minerloop = minerloop_async,
 	.job_prepare = x6500_job_prepare,

+ 3 - 5
driver-ztex.c

@@ -64,9 +64,9 @@ static struct cgpu_info *ztex_setup(struct libztex_device *dev, int fpgacount)
 	ztex->dev_manufacturer = dev->dev_manufacturer;
 	ztex->dev_product = dev->dev_product;
 	ztex->dev_serial = (char*)&dev->snString[0];
+	ztex->name = fpganame;
 	add_cgpu(ztex);
 	strcpy(ztex->device_ztex->repr, ztex->dev_repr);
-	ztex->name = fpganame;
 	applog(LOG_INFO, "%"PRIpreprv": Found Ztex (ZTEX %s)", ztex->dev_repr, fpganame);
 
 	return ztex;
@@ -339,15 +339,13 @@ get_ztex_drv_extra_device_status(struct cgpu_info *ztex)
 
 static bool ztex_prepare(struct thr_info *thr)
 {
-	struct timeval now;
 	struct cgpu_info *cgpu = thr->cgpu;
 	struct libztex_device *ztex = cgpu->device_ztex;
 
-	cgtime(&now);
-	get_datestamp(cgpu->init, &now);
+	get_now_datestamp(cgpu->init);
 	
 	{
-		char fpganame[LIBZTEX_SNSTRING_LEN+3+1];
+		char *fpganame = malloc(LIBZTEX_SNSTRING_LEN+3+1);
 		sprintf(fpganame, "%s-%u", ztex->snString, cgpu->proc_id+1);
 		cgpu->name = fpganame;
 	}

+ 1 - 1
findnonce.c

@@ -1,5 +1,5 @@
 /*
- * Copyright 2011-2012 Con Kolivas
+ * Copyright 2011-2013 Con Kolivas
  * Copyright 2012-2013 Luke Dashjr
  * Copyright 2011 Nils Schneider
  *

+ 54 - 130
fpgautils.c

@@ -119,7 +119,6 @@ int _detectone_wrap(const detectone_func_t detectone, const char * const param,
 
 struct detectone_meta_info_t detectone_meta_info;
 
-static
 void clear_detectone_meta_info(void)
 {
 	detectone_meta_info = (struct detectone_meta_info_t){
@@ -150,7 +149,7 @@ char *_decode_udev_enc_dup(const char *s)
 	if (!s)
 		return NULL;
 	
-	char *o = malloc(strlen(s));
+	char *o = malloc(strlen(s) + 1);
 	if (!o)
 	{
 		applog(LOG_ERR, "Failed to malloc in _decode_udev_enc_dup");
@@ -259,7 +258,7 @@ char *_sysfs_do_read(char *buf, size_t bufsz, const char *devpath, char *devfile
 		if (fgets(buf, bufsz, F))
 		{
 			size_t L = strlen(buf);
-			while (isspace(buf[--L]))
+			while (isCspace(buf[--L]))
 				buf[L] = '\0';
 		}
 		else
@@ -272,15 +271,59 @@ char *_sysfs_do_read(char *buf, size_t bufsz, const char *devpath, char *devfile
 	return buf[0] ? buf : NULL;
 }
 
+static
+void _sysfs_find_tty(detectone_func_t detectone, char *devpath, char *devfile, const char *prod, char *pfound)
+{
+	DIR *DT;
+	struct dirent *de;
+	char ttybuf[0x10] = "/dev/";
+	char manuf[0x40], serial[0x40];
+	char *mydevfile = strdup(devfile);
+	
+	DT = opendir(devpath);
+	if (!DT)
+		goto out;
+	
+	while ( (de = readdir(DT)) )
+	{
+		if (strncmp(de->d_name, "tty", 3))
+			continue;
+		if (!de->d_name[3])
+		{
+			// "tty" directory: recurse (needed for ttyACM)
+			sprintf(devfile, "%s/tty", mydevfile);
+			_sysfs_find_tty(detectone, devpath, devfile, prod, pfound);
+			continue;
+		}
+		if (strncmp(&de->d_name[3], "USB", 3) && strncmp(&de->d_name[3], "ACM", 3))
+			continue;
+		
+		
+		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))
+			++*pfound;
+	}
+	closedir(DT);
+	
+out:
+	free(mydevfile);
+}
+
 static
 int _serial_autodetect_sysfs(detectone_func_t detectone, va_list needles)
 {
-	DIR *D, *DS, *DT;
+	DIR *D, *DS;
 	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 ttybuf[0x10], manuf[0x40], prod[0x40], serial[0x40];
+	char prod[0x40];
 	char *devfile, *upfile;
 	char found = 0;
 	size_t len, len2;
@@ -310,8 +353,6 @@ int _serial_autodetect_sysfs(detectone_func_t detectone, va_list needles)
 		devfile[0] = '/';
 		++devfile;
 		
-		memcpy(ttybuf, "/dev/", 5);
-		
 		while ( (de = readdir(DS)) )
 		{
 			if (strncmp(de->d_name, upfile, len))
@@ -320,29 +361,7 @@ int _serial_autodetect_sysfs(detectone_func_t detectone, va_list needles)
 			len2 = strlen(de->d_name);
 			memcpy(devfile, de->d_name, len2 + 1);
 			
-			DT = opendir(devpath);
-			if (!DT)
-				continue;
-			
-			while ( (de = readdir(DT)) )
-			{
-				if (strncmp(de->d_name, "tty", 3))
-					continue;
-				if (strncmp(&de->d_name[3], "USB", 3) && strncmp(&de->d_name[3], "ACM", 3))
-					continue;
-				
-				
-				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);
+			_sysfs_find_tty(detectone, devpath, devfile, prod, &found);
 		}
 		closedir(DS);
 	}
@@ -475,62 +494,6 @@ int _serial_autodetect(detectone_func_t detectone, ...)
 	return rv;
 }
 
-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 = 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') {
-			size_t idlen = colon - dev;
-
-			// allow either name:device or dname:device
-			if ((idlen != namel || strncasecmp(dev, api->name, idlen))
-			&&  (idlen != dnamel || strncasecmp(dev, api->dname, idlen)))
-				continue;
-
-			dev = colon + 1;
-			hasname = true;
-		}
-		else
-			hasname = false;
-		if (!strcmp(dev, "auto"))
-			forceauto = true;
-		else if (!strcmp(dev, "noauto"))
-			inhibitauto = true;
-		else
-		if ((flags & 2) && !hasname)
-			continue;
-		else
-		if (!detectone)
-		{}  // do nothing
-		else
-		if (serial_claim(dev, NULL))
-		{
-			applog(LOG_DEBUG, "%s is already claimed... skipping probes", dev);
-			string_elist_del(&scan_devices, iter);
-		}
-		else if (detectone(dev)) {
-			string_elist_del(&scan_devices, iter);
-			inhibitauto = true;
-			++found;
-		}
-	}
-
-	if ((forceauto || !inhibitauto) && autoscan)
-		found += autoscan();
-
-	return found;
-}
-
 enum bfg_device_bus {
 	BDB_SERIAL,
 	BDB_USB,
@@ -643,7 +606,10 @@ void cgpu_copy_libusb_strings(struct cgpu_info *cgpu, libusb_device *usb)
 	if (LIBUSB_SUCCESS != libusb_open(usb, &h))
 		return;
 	if (libusb_get_device_descriptor(usb, &desc))
+	{
+		libusb_close(h);
 		return;
+	}
 	
 	if ((!cgpu->dev_manufacturer) && libusb_get_string_descriptor_ascii(h, desc.iManufacturer, buf, sizeof(buf)) >= 0)
 		cgpu->dev_manufacturer = strdup((void *)buf);
@@ -651,6 +617,8 @@ void cgpu_copy_libusb_strings(struct cgpu_info *cgpu, libusb_device *usb)
 		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);
+	
+	libusb_close(h);
 }
 #endif
 
@@ -928,50 +896,6 @@ ssize_t _serial_read(int fd, char *buf, size_t bufsiz, char *eol)
 	return tlen;
 }
 
-static FILE *_open_bitstream(const char *path, const char *subdir, const char *sub2, const char *filename)
-{
-	char fullpath[PATH_MAX];
-	strcpy(fullpath, path);
-	strcat(fullpath, "/");
-	if (subdir) {
-		strcat(fullpath, subdir);
-		strcat(fullpath, "/");
-	}
-	if (sub2) {
-		strcat(fullpath, sub2);
-		strcat(fullpath, "/");
-	}
-	strcat(fullpath, filename);
-	return fopen(fullpath, "rb");
-}
-#define _open_bitstream(path, subdir, sub2)  do {  \
-	f = _open_bitstream(path, subdir, sub2, filename);  \
-	if (f)  \
-		return f;  \
-} while(0)
-
-#define _open_bitstream2(path, path3)  do {  \
-	_open_bitstream(path, NULL, path3);  \
-	_open_bitstream(path, "../share/" PACKAGE, path3);  \
-} while(0)
-
-#define _open_bitstream3(path)  do {  \
-	_open_bitstream2(path, dname);  \
-	_open_bitstream2(path, "bitstreams");  \
-	_open_bitstream2(path, NULL);  \
-} while(0)
-
-FILE *open_bitstream(const char *dname, const char *filename)
-{
-	FILE *f;
-
-	_open_bitstream3(opt_kernel_path);
-	_open_bitstream3(cgminer_path);
-	_open_bitstream3(".");
-
-	return NULL;
-}
-
 #define bailout(...)  do {  \
 	applog(__VA_ARGS__);  \
 	return NULL;  \

+ 3 - 19
fpgautils.h

@@ -10,6 +10,8 @@
 #include <libusb.h>
 #endif
 
+#include "deviceapi.h"
+
 struct device_drv;
 struct cgpu_info;
 
@@ -21,25 +23,8 @@ struct detectone_meta_info_t {
 
 // NOTE: Should detectone become run multithreaded, this will become a threadsafe #define
 extern struct detectone_meta_info_t detectone_meta_info;
+extern void clear_detectone_meta_info(void);
 
-typedef bool(*detectone_func_t)(const char*);
-typedef int(*autoscan_func_t)();
-
-extern int _serial_detect(struct device_drv *api, detectone_func_t, autoscan_func_t, int flags);
-#define serial_detect_fauto(api, detectone, autoscan)  \
-	_serial_detect(api, detectone, autoscan, 1)
-#define serial_detect_auto(api, detectone, autoscan)  \
-	_serial_detect(api, detectone, autoscan, 0)
-#define serial_detect_auto_byname(api, detectone, autoscan)  \
-	_serial_detect(api, detectone, autoscan, 2)
-#define serial_detect(api, detectone)  \
-	_serial_detect(api, detectone,     NULL, 0)
-#define serial_detect_byname(api, detectone)  \
-	_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)
 
@@ -61,7 +46,6 @@ extern ssize_t _serial_read(int fd, char *buf, size_t buflen, char *eol);
 	_serial_read(fd, buf, bufsiz, &eol)
 #define serial_close(fd)  close(fd)
 
-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);

+ 1 - 1
ft232r.c

@@ -1,5 +1,5 @@
 /*
- * Copyright 2012 Luke Dashjr
+ * Copyright 2012-2013 Luke Dashjr
  *
  * This program is free software; you can redistribute it and/or modify it
  * under the terms of the GNU General Public License as published by the Free

+ 3 - 0
icarus-common.h

@@ -94,6 +94,9 @@ struct icarus_state {
 	struct timeval tv_workfinish;
 	struct work last_work;
 	bool changework;
+	bool identify;
+	
+	uint8_t ob_bin[64];
 };
 
 bool icarus_detect_custom(const char *devpath, struct device_drv *, struct ICARUS_INFO *);

+ 37 - 44
logging.c

@@ -25,27 +25,15 @@ bool opt_log_microseconds;
 /* per default priorities higher than LOG_NOTICE are logged */
 int opt_log_level = LOG_NOTICE;
 
-static void my_log_curses(int prio, const char *datetime, const char *str)
+static void _my_log_curses(int prio, const char *datetime, const char *str)
 {
-	if (opt_quiet && prio != LOG_ERR)
-		return;
-
 #ifdef HAVE_CURSES
 	extern bool use_curses;
-	if (use_curses && log_curses_only(prio, datetime, str))
+	if (use_curses && _log_curses_only(prio, datetime, str))
 		;
 	else
 #endif
-	{
-		int cancelstate;
-		bool scs;
-		scs = !pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, &cancelstate);
-		mutex_lock(&console_lock);
-		printf("%s%s%s", datetime, str, "                    \n");
-		mutex_unlock(&console_lock);
-		if (scs)
-			pthread_setcancelstate(cancelstate, &cancelstate);
-	}
+		printf(" %s %s%s", datetime, str, "                    \n");
 }
 
 /* high-level logging function, based on global opt_log_level */
@@ -63,45 +51,50 @@ void _applog(int prio, const char *str)
 	if (0) {}
 #endif
 	else {
-		bool writetocon = opt_debug_console || (opt_log_output && prio != LOG_DEBUG) || prio <= LOG_NOTICE;
+		bool writetocon =
+			(opt_debug_console || (opt_log_output && prio != LOG_DEBUG) || prio <= LOG_NOTICE)
+		 && !(opt_quiet && prio != LOG_ERR);
 		bool writetofile = !isatty(fileno((FILE *)stderr));
 		if (!(writetocon || writetofile))
 			return;
 
 		char datetime[64];
-		struct timeval tv = {0, 0};
-		struct tm _tm;
-		struct tm *tm = &_tm;
-
-		cgtime(&tv);
-
-		localtime_r(&tv.tv_sec, tm);
 
 		if (opt_log_microseconds)
-			sprintf(datetime, " [%d-%02d-%02d %02d:%02d:%02d.%06ld] ",
-				tm->tm_year + 1900,
-				tm->tm_mon + 1,
-				tm->tm_mday,
-				tm->tm_hour,
-				tm->tm_min,
-				tm->tm_sec,
+		{
+			struct timeval tv;
+			struct tm tm;
+			
+			bfg_init_time();
+			bfg_gettimeofday(&tv);
+			localtime_r(&tv.tv_sec, &tm);
+			
+			sprintf(datetime, "[%d-%02d-%02d %02d:%02d:%02d.%06ld]",
+				tm.tm_year + 1900,
+				tm.tm_mon + 1,
+				tm.tm_mday,
+				tm.tm_hour,
+				tm.tm_min,
+				tm.tm_sec,
 				(long)tv.tv_usec);
+		}
 		else
-			sprintf(datetime, " [%d-%02d-%02d %02d:%02d:%02d] ",
-				tm->tm_year + 1900,
-				tm->tm_mon + 1,
-				tm->tm_mday,
-				tm->tm_hour,
-				tm->tm_min,
-				tm->tm_sec);
+			get_now_datestamp(datetime);
 
-		/* Only output to stderr if it's not going to the screen as well */
-		if (writetofile) {
-			fprintf(stderr, "%s%s\n", datetime, str);	/* atomic write to stderr */
-			fflush(stderr);
-		}
+		if (writetofile || writetocon)
+		{
+			bfg_console_lock();
+			
+			/* Only output to stderr if it's not going to the screen as well */
+			if (writetofile) {
+				fprintf(stderr, " %s %s\n", datetime, str);	/* atomic write to stderr */
+				fflush(stderr);
+			}
 
-		if (writetocon)
-			my_log_curses(prio, datetime, str);
+			if (writetocon)
+				_my_log_curses(prio, datetime, str);
+			
+			bfg_console_unlock();
+		}
 	}
 }

File diff suppressed because it is too large
+ 434 - 127
miner.c


+ 32 - 6
miner.h

@@ -543,6 +543,7 @@ struct cgpu_info {
 
 	time_t device_last_well;
 	time_t device_last_not_well;
+	struct timeval tv_device_last_not_well;
 	enum dev_reason device_not_well_reason;
 	float reinit_backoff;
 	int thread_fail_init_count;
@@ -904,6 +905,26 @@ extern cglock_t ch_lock;
 extern pthread_rwlock_t mining_thr_lock;
 extern pthread_rwlock_t devices_lock;
 
+
+extern bool _bfg_console_cancel_disabled;
+extern int _bfg_console_prev_cancelstate;
+
+static inline
+void bfg_console_lock(void)
+{
+	_bfg_console_cancel_disabled = !pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, &_bfg_console_prev_cancelstate);
+	mutex_lock(&console_lock);
+}
+
+static inline
+void bfg_console_unlock(void)
+{
+	mutex_unlock(&console_lock);
+	if (_bfg_console_cancel_disabled)
+		pthread_setcancelstate(_bfg_console_prev_cancelstate, &_bfg_console_prev_cancelstate);
+}
+
+
 extern void thread_reportin(struct thr_info *thr);
 extern void thread_reportout(struct thr_info *);
 extern void clear_stratum_shares(struct pool *pool);
@@ -932,7 +953,7 @@ extern void api(int thr_id);
 
 extern struct pool *current_pool(void);
 extern int enabled_pools;
-extern void get_intrange(char *arg, int *val1, int *val2);
+extern bool get_intrange(const char *arg, int *val1, int *val2);
 extern bool detect_stratum(struct pool *pool, char *url);
 extern void print_summary(void);
 extern struct pool *add_pool(void);
@@ -987,7 +1008,8 @@ extern unsigned int new_blocks;
 extern unsigned int found_blocks;
 extern int total_accepted, total_rejected, total_diff1;;
 extern int total_getworks, total_stale, total_discarded;
-extern uint64_t total_bytes_xfer;
+extern uint64_t total_bytes_rcvd, total_bytes_sent;
+#define total_bytes_xfer (total_bytes_rcvd + total_bytes_sent)
 extern double total_diff_accepted, total_diff_rejected, total_diff_stale;
 extern unsigned int local_work;
 extern unsigned int total_go, total_ro;
@@ -1067,12 +1089,13 @@ struct stratum_work {
 	
 	uint8_t header1[36];
 	uint8_t diffbits[4];
-	uint8_t ntime[4];
+	uint32_t ntime;
+	struct timeval tv_received;
 
 	double diff;
 
 	bool transparency_probed;
-	time_t transparency_time;
+	struct timeval tv_transparency;
 	bool opaque;
 };
 
@@ -1144,6 +1167,7 @@ struct pool {
 	struct submit_work_state *sws_waiting_on_curl;
 
 	time_t last_work_time;
+	struct timeval tv_last_work_time;
 	time_t last_share_time;
 	double last_share_diff;
 	uint64_t best_diff;
@@ -1237,6 +1261,7 @@ struct work {
 	bool		do_foreign_submit;
 
 	struct timeval	tv_getwork;
+	time_t		ts_getwork;
 	struct timeval	tv_getwork_reply;
 	struct timeval	tv_cloned;
 	struct timeval	tv_work_start;
@@ -1248,7 +1273,8 @@ struct work {
 	struct work *next;
 };
 
-extern void get_datestamp(char *, struct timeval *);
+extern void get_datestamp(char *, time_t);
+#define get_now_datestamp(buf)  get_datestamp(buf, INVALID_TIMESTAMP)
 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 {
@@ -1284,7 +1310,7 @@ extern void write_config(FILE *fcfg);
 extern void zero_bestshare(void);
 extern void zero_stats(void);
 extern void default_save_file(char *filename);
-extern bool log_curses_only(int prio, const char *datetime, const char *str);
+extern bool _log_curses_only(int prio, const char *datetime, const char *str);
 extern void clear_logwin(void);
 extern void logwin_update(void);
 extern bool pool_tclear(struct pool *pool, bool *var);

+ 7 - 1
miner.php

@@ -994,6 +994,7 @@ function showdatetime()
 global $singlerigsum;
 $singlerigsum = array(
  'devs' => array('MHS av' => 1, 'MHS 5s' => 1, 'Accepted' => 1, 'Rejected' => 1,
+                 'Temperature' => 2,
 			'Hardware Errors' => 1, 'Utility' => 1, 'Total MH' => 1),
  'pools' => array('Getworks' => 1, 'Accepted' => 1, 'Rejected' => 1, 'Discarded' => 1,
 			'Stale' => 1, 'Get Failures' => 1, 'Remote Failures' => 1),
@@ -1151,7 +1152,12 @@ function details($cmd, $list, $rig)
 		||  (isset($dototal['*']) and substr($name, 0, 1) == '*'))
 		{
 			if (isset($total[$name]))
-				$total[$name] += $value;
+			{
+				if ($dototal[$name] == 2)
+					$total[$name] = max($total[$name], $value);
+				else
+					$total[$name] += $value;
+			}
 			else
 				$total[$name] = $value;
 		}

+ 1 - 1
ocl.c

@@ -33,8 +33,8 @@
 
 #define OMIT_OPENCL_API
 
+#include "deviceapi.h"
 #include "findnonce.h"
-#include "fpgautils.h"
 #include "ocl.h"
 
 /* Platform API */

+ 1 - 1
openwrt/bfgminer/Makefile

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

+ 160 - 27
util.c

@@ -117,7 +117,7 @@ static size_t all_data_cb(const void *ptr, size_t size, size_t nmemb,
 	if (db->idlemarker) {
 		const unsigned char *cptr = ptr;
 		for (size_t i = 0; i < len; ++i)
-			if (!(isspace(cptr[i]) || cptr[i] == '{')) {
+			if (!(isCspace(cptr[i]) || cptr[i] == '{')) {
 				*db->idlemarker = CURL_SOCKET_BAD;
 				db->idlemarker = NULL;
 				break;
@@ -182,14 +182,14 @@ static size_t resp_hdr_cb(void *ptr, size_t size, size_t nmemb, void *user_data)
 
 	rem = ptr + slen + 1;		/* trim value's leading whitespace */
 	remlen = ptrlen - slen - 1;
-	while ((remlen > 0) && (isspace(*rem))) {
+	while ((remlen > 0) && (isCspace(*rem))) {
 		remlen--;
 		rem++;
 	}
 
 	memcpy(val, rem, remlen);	/* store value, trim trailing ws */
 	val[remlen] = 0;
-	while ((*val) && (isspace(val[strlen(val) - 1])))
+	while ((*val) && (isCspace(val[strlen(val) - 1])))
 		val[strlen(val) - 1] = 0;
 
 	if (!*val)			/* skip blank value */
@@ -339,14 +339,14 @@ static int curl_debug_cb(__maybe_unused CURL *handle, curl_infotype type,
 		case CURLINFO_DATA_IN:
 		case CURLINFO_SSL_DATA_IN:
 			pool->cgminer_pool_stats.bytes_received += size;
-			total_bytes_xfer += size;
+			total_bytes_rcvd += size;
 			pool->cgminer_pool_stats.net_bytes_received += size;
 			break;
 		case CURLINFO_HEADER_OUT:
 		case CURLINFO_DATA_OUT:
 		case CURLINFO_SSL_DATA_OUT:
 			pool->cgminer_pool_stats.bytes_sent += size;
-			total_bytes_xfer += size;
+			total_bytes_sent += size;
 			pool->cgminer_pool_stats.net_bytes_sent += size;
 			break;
 		case CURLINFO_TEXT:
@@ -356,7 +356,7 @@ static int curl_debug_cb(__maybe_unused CURL *handle, curl_infotype type,
 			// data is not null-terminated, so we need to copy and terminate it for applog
 			char datacp[size + 1];
 			memcpy(datacp, data, size);
-			while (likely(size) && unlikely(isspace(datacp[size-1])))
+			while (likely(size) && unlikely(isCspace(datacp[size-1])))
 				--size;
 			if (unlikely(!size))
 				break;
@@ -1098,12 +1098,11 @@ void nusleep(unsigned int usecs)
 #endif
 }
 
-/* This is a cgminer gettimeofday wrapper. Since we always call gettimeofday
- * with tz set to NULL, and windows' default resolution is only 15ms, this
- * gives us higher resolution times on windows. */
-void cgtime(struct timeval *tv)
+static
+void _now_gettimeofday(struct timeval *tv)
 {
 #ifdef WIN32
+	// Windows' default resolution is only 15ms. This requests 1ms.
 	timeBeginPeriod(1);
 #endif
 	gettimeofday(tv, NULL);
@@ -1112,6 +1111,134 @@ void cgtime(struct timeval *tv)
 #endif
 }
 
+#ifdef HAVE_POOR_GETTIMEOFDAY
+static struct timeval tv_timeofday_offset;
+static struct timeval _tv_timeofday_lastchecked;
+static pthread_mutex_t _tv_timeofday_mutex = PTHREAD_MUTEX_INITIALIZER;
+
+static
+void bfg_calibrate_timeofday(struct timeval *expected, char *buf)
+{
+	struct timeval actual, delta;
+	timeradd(expected, &tv_timeofday_offset, expected);
+	_now_gettimeofday(&actual);
+	if (expected->tv_sec >= actual.tv_sec - 1 && expected->tv_sec <= actual.tv_sec + 1)
+		// Within reason - no change necessary
+		return;
+	
+	timersub(&actual, expected, &delta);
+	timeradd(&tv_timeofday_offset, &delta, &tv_timeofday_offset);
+	sprintf(buf, "Recalibrating timeofday offset (delta %ld.%06lds)", (long)delta.tv_sec, (long)delta.tv_usec);
+	*expected = actual;
+}
+
+void bfg_gettimeofday(struct timeval *out)
+{
+	char buf[64] = "";
+	timer_set_now(out);
+	mutex_lock(&_tv_timeofday_mutex);
+	if (_tv_timeofday_lastchecked.tv_sec < out->tv_sec - 21)
+		bfg_calibrate_timeofday(out, buf);
+	else
+		timeradd(out, &tv_timeofday_offset, out);
+	mutex_unlock(&_tv_timeofday_mutex);
+	if (unlikely(buf[0]))
+		applog(LOG_WARNING, "%s", buf);
+}
+#endif
+
+#ifdef WIN32
+static LARGE_INTEGER _perffreq;
+
+static
+void _now_queryperformancecounter(struct timeval *tv)
+{
+	LARGE_INTEGER now;
+	if (unlikely(!QueryPerformanceCounter(&now)))
+		quit(1, "QueryPerformanceCounter failed");
+	
+	*tv = (struct timeval){
+		.tv_sec = now.QuadPart / _perffreq.QuadPart,
+		.tv_usec = (now.QuadPart % _perffreq.QuadPart) * 1000000 / _perffreq.QuadPart,
+	};
+}
+#endif
+
+static
+void _now_is_not_set(__maybe_unused struct timeval *tv)
+{
+	// Might be unclean to swap algorithms after getting a timer
+	quit(1, "timer_set_now called before bfg_init_time");
+}
+
+void (*timer_set_now)(struct timeval *tv) = _now_is_not_set;
+
+#ifdef HAVE_CLOCK_GETTIME_MONOTONIC
+static clockid_t bfg_timer_clk;
+
+static
+void _now_clock_gettime(struct timeval *tv)
+{
+	struct timespec ts;
+	if (unlikely(clock_gettime(bfg_timer_clk, &ts)))
+		quit(1, "clock_gettime failed");
+	
+	*tv = (struct timeval){
+		.tv_sec = ts.tv_sec,
+		.tv_usec = ts.tv_nsec / 1000,
+	};
+}
+
+static
+bool _bfg_try_clock_gettime(clockid_t clk)
+{
+	struct timespec ts;
+	if (clock_gettime(clk, &ts))
+		return false;
+	
+	bfg_timer_clk = clk;
+	timer_set_now = _now_clock_gettime;
+	return true;
+}
+#endif
+
+void bfg_init_time()
+{
+	if (timer_set_now != _now_is_not_set)
+		return;
+	
+#ifdef HAVE_CLOCK_GETTIME_MONOTONIC
+#ifdef HAVE_CLOCK_GETTIME_MONOTONIC_RAW
+	if (_bfg_try_clock_gettime(CLOCK_MONOTONIC_RAW))
+		applog(LOG_DEBUG, "Timers: Using clock_gettime(CLOCK_MONOTONIC_RAW)");
+	else
+#endif
+	if (_bfg_try_clock_gettime(CLOCK_MONOTONIC))
+		applog(LOG_DEBUG, "Timers: Using clock_gettime(CLOCK_MONOTONIC)");
+	else
+#endif
+#ifdef WIN32
+	if (QueryPerformanceFrequency(&_perffreq) && _perffreq.QuadPart)
+	{
+		timer_set_now = _now_queryperformancecounter;
+		applog(LOG_DEBUG, "Timers: Using QueryPerformanceCounter");
+	}
+	else
+#endif
+	{
+		timer_set_now = _now_gettimeofday;
+		applog(LOG_DEBUG, "Timers: Using gettimeofday");
+	}
+	
+#ifdef HAVE_POOR_GETTIMEOFDAY
+	char buf[64] = "";
+	struct timeval tv;
+	timer_set_now(&tv);
+	bfg_calibrate_timeofday(&tv, buf);
+	applog(LOG_DEBUG, "%s", buf);
+#endif
+}
+
 void subtime(struct timeval *a, struct timeval *b)
 {
 	timersub(a, b, b);
@@ -1239,12 +1366,12 @@ static enum send_ret __stratum_send(struct pool *pool, char *s, ssize_t len)
 
 	pool->cgminer_pool_stats.times_sent++;
 	pool->cgminer_pool_stats.bytes_sent += ssent;
-	total_bytes_xfer += ssent;
+	total_bytes_sent += ssent;
 	pool->cgminer_pool_stats.net_bytes_sent += ssent;
 	return SEND_OK;
 }
 
-bool stratum_send(struct pool *pool, char *s, ssize_t len)
+bool _stratum_send(struct pool *pool, char *s, ssize_t len, bool force)
 {
 	enum send_ret ret = SEND_INACTIVE;
 
@@ -1252,7 +1379,7 @@ bool stratum_send(struct pool *pool, char *s, ssize_t len)
 		applog(LOG_DEBUG, "Pool %u: SEND: %s", pool->pool_no, s);
 
 	mutex_lock(&pool->stratum_lock);
-	if (pool->stratum_active)
+	if (pool->stratum_active || force)
 		ret = __stratum_send(pool, s, len);
 	mutex_unlock(&pool->stratum_lock);
 
@@ -1409,7 +1536,7 @@ char *recv_line(struct pool *pool)
 
 	pool->cgminer_pool_stats.times_received++;
 	pool->cgminer_pool_stats.bytes_received += len;
-	total_bytes_xfer += len;
+	total_bytes_rcvd += len;
 	pool->cgminer_pool_stats.net_bytes_received += len;
 
 out:
@@ -1451,7 +1578,7 @@ char *json_dumps_ANY(json_t *json, size_t flags)
 	if (!s)
 		return NULL;
 	for (i = 0; s[i] != '['; ++i)
-		if (unlikely(!(s[i] && isspace(s[i]))))
+		if (unlikely(!(s[i] && isCspace(s[i]))))
 			quit(1, "json_dumps_ANY failed to find opening bracket in array dump");
 	len = strlen(&s[++i]) - 1;
 	if (unlikely(s[i+len] != ']'))
@@ -1503,8 +1630,8 @@ void stratum_probe_transparency(struct pool *pool)
 	        pool->swork.job_id,
 	        pool->swork.job_id);
 	stratum_send(pool, s, sLen);
-	if ((!pool->swork.opaque) && pool->swork.transparency_time == (time_t)-1)
-		pool->swork.transparency_time = time(NULL);
+	if ((!pool->swork.opaque) && !timer_isset(&pool->swork.tv_transparency))
+		cgtime(&pool->swork.tv_transparency);
 	pool->swork.transparency_probed = true;
 }
 
@@ -1541,6 +1668,7 @@ static bool parse_notify(struct pool *pool, json_t *val)
 		goto out;
 
 	cg_wlock(&pool->data_lock);
+	cgtime(&pool->swork.tv_received);
 	free(pool->swork.job_id);
 	pool->swork.job_id = job_id;
 	pool->submit_old = !clean;
@@ -1548,7 +1676,8 @@ static bool parse_notify(struct pool *pool, json_t *val)
 	
 	hex2bin(&pool->swork.header1[0], bbversion,  4);
 	hex2bin(&pool->swork.header1[4], prev_hash, 32);
-	hex2bin(&pool->swork.ntime[0], ntime, 4);
+	hex2bin((void*)&pool->swork.ntime, ntime, 4);
+	pool->swork.ntime = be32toh(pool->swork.ntime);
 	hex2bin(&pool->swork.diffbits[0], nbit, 4);
 	
 	cb1_len = strlen(coinbase1) / 2;
@@ -1590,7 +1719,7 @@ static bool parse_notify(struct pool *pool, json_t *val)
 	pool->getwork_requested++;
 	total_getworks++;
 
-	if ((merkles && (!pool->swork.transparency_probed || rand() <= RAND_MAX / (opt_skip_checks + 1))) || pool->swork.transparency_time != (time_t)-1)
+	if ((merkles && (!pool->swork.transparency_probed || rand() <= RAND_MAX / (opt_skip_checks + 1))) || timer_isset(&pool->swork.tv_transparency))
 		if (pool->stratum_init)
 			stratum_probe_transparency(pool);
 
@@ -1836,10 +1965,11 @@ static bool setup_stratum_curl(struct pool *pool)
 	char curl_err_str[CURL_ERROR_SIZE];
 	CURL *curl = NULL;
 	char s[RBUFSIZE];
+	bool ret = false;
 
 	applog(LOG_DEBUG, "initiate_stratum with sockbuf=%p", pool->sockbuf);
 	mutex_lock(&pool->stratum_lock);
-	pool->swork.transparency_time = (time_t)-1;
+	timer_unset(&pool->swork.tv_transparency);
 	pool->stratum_active = false;
 	pool->stratum_notify = false;
 	pool->swork.transparency_probed = false;
@@ -1850,7 +1980,6 @@ static bool setup_stratum_curl(struct pool *pool)
 		quit(1, "Failed to curl_easy_init in initiate_stratum");
 	if (pool->sockbuf)
 		pool->sockbuf[0] = '\0';
-	mutex_unlock(&pool->stratum_lock);
 
 	curl = pool->stratum_curl;
 
@@ -1893,23 +2022,26 @@ static bool setup_stratum_curl(struct pool *pool)
 	pool->sock = INVSOCK;
 	if (curl_easy_perform(curl)) {
 		applog(LOG_INFO, "Stratum connect failed to pool %d: %s", pool->pool_no, curl_err_str);
+errout:
 		curl_easy_cleanup(curl);
 		pool->stratum_curl = NULL;
-		return false;
+		goto out;
 	}
 	if (pool->sock == INVSOCK)
 	{
-		pool->stratum_curl = NULL;
-		curl_easy_cleanup(curl);
 		applog(LOG_ERR, "Stratum connect succeeded, but technical problem extracting socket (pool %u)", pool->pool_no);
-		return false;
+		goto errout;
 	}
 	keep_sockalive(pool->sock);
 
 	pool->cgminer_pool_stats.times_sent++;
 	pool->cgminer_pool_stats.times_received++;
+	ret = true;
 
-	return true;
+out:
+	mutex_unlock(&pool->stratum_lock);
+	
+	return ret;
 }
 
 static char *get_sessionid(json_t *val)
@@ -1982,7 +2114,7 @@ resend:
 			sprintf(s, "{\"id\": %d, \"method\": \"mining.subscribe\", \"params\": [\""PACKAGE"/"VERSION"\"]}", swork_id++);
 	}
 
-	if (__stratum_send(pool, s, strlen(s)) != SEND_OK) {
+	if (!_stratum_send(pool, s, strlen(s), true)) {
 		applog(LOG_DEBUG, "Failed to send s in initiate_stratum");
 		goto out;
 	}
@@ -2099,6 +2231,7 @@ bool restart_stratum(struct pool *pool)
 void dev_error(struct cgpu_info *dev, enum dev_reason reason)
 {
 	dev->device_last_not_well = time(NULL);
+	cgtime(&dev->tv_device_last_not_well);
 	dev->device_not_well_reason = reason;
 
 	switch (reason) {

+ 75 - 8
util.h

@@ -13,11 +13,16 @@
 #ifndef __UTIL_H__
 #define __UTIL_H__
 
+#include <stdbool.h>
+#include <sys/time.h>
+
 #include <curl/curl.h>
 #include <jansson.h>
 
 #include "compat.h"
 
+#define INVALID_TIMESTAMP ((time_t)-1)
+
 #if defined(unix) || defined(__APPLE__)
 	#include <errno.h>
 	#include <sys/socket.h>
@@ -69,6 +74,18 @@
 #endif
 extern char *json_dumps_ANY(json_t *, size_t flags);
 
+static inline
+bool isCspace(int c)
+{
+	switch (c)
+	{
+		case ' ': case '\f': case '\n': case '\r': case '\t': case '\v':
+			return true;
+		default:
+			return false;
+	}
+}
+
 struct thr_info;
 struct pool;
 enum dev_reason;
@@ -90,7 +107,6 @@ void thr_info_freeze(struct thr_info *thr);
 void thr_info_cancel(struct thr_info *thr);
 void nmsleep(unsigned int msecs);
 void nusleep(unsigned int usecs);
-void cgtime(struct timeval *tv);
 void subtime(struct timeval *a, struct timeval *b);
 void addtime(struct timeval *a, struct timeval *b);
 bool time_more(struct timeval *a, struct timeval *b);
@@ -98,7 +114,8 @@ bool time_less(struct timeval *a, struct timeval *b);
 void copy_time(struct timeval *dest, const struct timeval *src);
 double us_tdiff(struct timeval *end, struct timeval *start);
 double tdiff(struct timeval *end, struct timeval *start);
-bool stratum_send(struct pool *pool, char *s, ssize_t len);
+bool _stratum_send(struct pool *pool, char *s, ssize_t len, bool force);
+#define stratum_send(pool, s, len)  _stratum_send(pool, s, len, false)
 bool sock_full(struct pool *pool);
 char *recv_line(struct pool *pool);
 bool parse_method(struct pool *pool, char *s);
@@ -209,6 +226,22 @@ void set_maxfd(int *p_maxfd, int fd)
 }
 
 
+static inline
+void timer_unset(struct timeval *tvp)
+{
+	tvp->tv_sec = -1;
+}
+
+static inline
+bool timer_isset(const struct timeval *tvp)
+{
+	return tvp->tv_sec != -1;
+}
+
+extern void (*timer_set_now)(struct timeval *);
+extern void bfg_init_time();
+#define cgtime(tvp)  timer_set_now(tvp)
+
 #define TIMEVAL_USECS(usecs)  (  \
 	(struct timeval){  \
 		.tv_sec = (usecs) / 1000000,  \
@@ -223,29 +256,63 @@ void set_maxfd(int *p_maxfd, int fd)
 
 #define timer_set_delay_from_now(tvp_timer, usecs)  do {  \
 	struct timeval tv_now;  \
-	gettimeofday(&tv_now, NULL);  \
+	timer_set_now(&tv_now);  \
 	timer_set_delay(tvp_timer, &tv_now, usecs);  \
 } while(0)
 
 static inline
-bool timer_passed(struct timeval *tvp_timer, struct timeval *tvp_now)
+const struct timeval *_bfg_nullisnow(const struct timeval *tvp, struct timeval *tvp_buf)
+{
+	if (tvp)
+		return tvp;
+	cgtime(tvp_buf);
+	return tvp_buf;
+}
+
+static inline
+int timer_elapsed(const struct timeval *tvp_timer, const struct timeval *tvp_now)
+{
+	struct timeval tv;
+	const struct timeval *_tvp_now = _bfg_nullisnow(tvp_now, &tv);
+	timersub(_tvp_now, tvp_timer, &tv);
+	return tv.tv_sec;
+}
+
+static inline
+bool timer_passed(const struct timeval *tvp_timer, const struct timeval *tvp_now)
 {
-	return (tvp_timer->tv_sec != -1 && timercmp(tvp_timer, tvp_now, <));
+	if (!timer_isset(tvp_timer))
+		return false;
+	
+	struct timeval tv;
+	const struct timeval *_tvp_now = _bfg_nullisnow(tvp_now, &tv);
+	
+	return timercmp(tvp_timer, _tvp_now, <);
 }
 
+#if defined(WIN32) && !defined(HAVE_POOR_GETTIMEOFDAY)
+#define HAVE_POOR_GETTIMEOFDAY
+#endif
+
+#ifdef HAVE_POOR_GETTIMEOFDAY
+extern void bfg_gettimeofday(struct timeval *);
+#else
+#define bfg_gettimeofday(out)  gettimeofday(out, NULL)
+#endif
+
 static inline
 void reduce_timeout_to(struct timeval *tvp_timeout, struct timeval *tvp_time)
 {
-	if (tvp_time->tv_sec == -1)
+	if (!timer_isset(tvp_time))
 		return;
-	if (tvp_timeout->tv_sec == -1 /* no timeout */ || timercmp(tvp_time, tvp_timeout, <))
+	if ((!timer_isset(tvp_timeout)) || timercmp(tvp_time, tvp_timeout, <))
 		*tvp_timeout = *tvp_time;
 }
 
 static inline
 struct timeval *select_timeout(struct timeval *tvp_timeout, struct timeval *tvp_now)
 {
-	if (tvp_timeout->tv_sec == -1)
+	if (!timer_isset(tvp_timeout))
 		return NULL;
 	
 	if (timercmp(tvp_timeout, tvp_now, <))

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