Browse Source

Merge branch 'stratum' into bfgminer

Conflicts:
	NEWS
	driver-opencl.c
	miner.c
	util.c
Luke Dashjr 13 years ago
parent
commit
e5a326bb89
24 changed files with 1804 additions and 481 deletions
  1. 7 0
      API-README
  2. 74 2
      FPGA-README
  3. 1 2
      Makefile.am
  4. 241 0
      NEWS
  5. 46 4
      README
  6. 89 112
      api.c
  7. 8 19
      configure.ac
  8. 1 6
      diablo121016.cl
  9. 1 6
      diakgcn121016.cl
  10. 14 19
      driver-icarus.c
  11. 2 1
      driver-modminer.c
  12. 20 68
      driver-opencl.c
  13. 11 10
      findnonce.c
  14. 1 1
      fpgautils.c
  15. 2 0
      logging.c
  16. 465 186
      miner.c
  17. 94 8
      miner.h
  18. 1 6
      phatk121016.cl
  19. 1 6
      poclbm121016.cl
  20. 12 0
      scrypt.c
  21. 8 0
      scrypt.h
  22. 1 6
      scrypt121016.cl
  23. 651 19
      util.c
  24. 53 0
      util.h

+ 7 - 0
API-README

@@ -384,6 +384,13 @@ miner.php - an example web page to access the API
 Feature Changelog for external applications using the API:
 
 
+API V1.20
+
+Modified API commands:
+ 'pools' - add 'Has Stratum', 'Stratum Active', 'Stratum URL'
+
+----------
+
 API V1.19b (BFGMiner v2.8.1)
 
 Added API commands:

+ 74 - 2
FPGA-README

@@ -2,7 +2,78 @@
 This README contains extended details about FPGA mining with cgminer
 
 
-Bitforce
+ModMinerQuad (MMQ)
+------------------
+
+The mining bitstream does not survive a power cycle, so cgminer will upload
+it, if it needs to, before it starts mining
+
+-
+
+You must make sure you have an approriate firmware in your MMQ
+Read here for official details of changing the firmware:
+ http://wiki.btcfpga.com/index.php?title=Firmware
+
+The basics of changing the firmware are:
+ You need two short pieces of conductive wire if your MMQ doesn't have
+ buttons on the "RESET" and "ISP" pads on the backplane board
+ Cutting a small (metal) paper-clip in half works well for this
+
+ Join the 2 left pads of the "RESET" pad with wire and the led will dim
+ Without disconnecting the "RESET", join the 2 left pads of the "ISP" pad
+ with a wire and it will stay dim
+ Release "RESET" then release "ISP" and is should still be dim
+ Unplug the USB and when you plug it back in it will show up as a mass
+ storage device
+  Linux: (as one single line):
+   mcopy -i /dev/disk/by-id/usb-NXP_LPC134X_IFLASH_ISP000000000-0:0
+      modminer091012.bin ::/firmware.bin
+  Windows: delete the MSD device file firmware.bin and copy in the new one
+   rename the new file and put it under the same name 'firmware.bin'
+ Disconnect the USB correctly (so writes are flushed first)
+ Join and then disconnect "RESET" and then plug the USB back in and it's done
+
+Best to update to one of the latest 2 listed below if you don't already
+have one of them in your MMQ
+
+The current latest different firmware are:
+
+ Latest for support of normal or TLM bitstream:
+  http://btcfpga.com/files/firmware/modminer092612-TLM.bin
+
+ Latest with only normal bitstream support (Temps/HW Fix):
+  http://btcfpga.com/files/firmware/modminer091012.bin
+
+The code is currently tested on the modminer091012.bin firmware.
+This comment will be updated when others have been tested
+
+-
+
+On many linux distributions there is an app called modem-manager that
+may cause problems when it is enabled, due to opening the MMQ device
+and writing to it
+
+The problem will typically present itself by the flashing led on the
+backplane going out (no longer flashing) and it takes a power cycle to
+re-enable the MMQ firmware - which then can lead to the problem happening
+again
+
+You can either disable/uninstall modem-manager if you don't need it or:
+a (hack) solution to this is to blacklist the MMQ USB device in
+/lib/udev/rules.d/77-mm-usb-device-blacklist.rules
+
+Adding 2 lines like this (just above APC) should help
+# MMQ
+ATTRS{idVendor}=="ifc9", ATTRS{idProduct}=="0003", ENV{ID_MM_DEVICE_IGNORE}="1"
+
+The change will be lost and need to be re-done, next time you update the
+modem-manager software
+
+TODO: check that all MMQ's have the same product ID
+
+
+Bitforce (BFL)
+--------------
 
 --bfl-range         Use nonce range on bitforce devices if supported
 
@@ -37,7 +108,8 @@ the MH/s value reported with the changed firmware - and the MH/s reported
 will be less than the firmware speed since you lose work on every block change.
 
 
-Icarus
+Icarus (ICA)
+------------
 
 There are two hidden options in cgminer when Icarus support is compiled in:
 

+ 1 - 2
Makefile.am

@@ -17,7 +17,6 @@ bin_SCRIPTS	= *.cl
 bfgminer_LDFLAGS	= $(PTHREAD_FLAGS)
 bfgminer_LDADD	= $(DLOPEN_FLAGS) @LIBCURL_LIBS@ @JANSSON_LIBS@ @PTHREAD_LIBS@ \
 		  @NCURSES_LIBS@ @PDCURSES_LIBS@ @WS2_LIBS@ \
-		  @TIMER_LIBS@ \
 		  @UDEV_LIBS@ @USB_LIBS@ \
 		  @MATH_LIBS@ lib/libgnu.a ccan/libccan.a
 bfgminer_CPPFLAGS = -I$(top_builddir)/lib -I$(top_srcdir)/lib
@@ -28,7 +27,7 @@ bfgminer_CPPFLAGS += $(NCURSES_CPPFLAGS)
 bfgminer_SOURCES := miner.c
 
 bfgminer_SOURCES	+= elist.h miner.h compat.h bench_block.h	\
-		   util.c uthash.h logging.h			\
+		   util.c util.h uthash.h logging.h		\
 		   sha2.c sha2.h api.c
 bfgminer_DEPENDENCIES =
 

+ 241 - 0
NEWS

@@ -38,6 +38,247 @@ BFGMiner Version 2.9.0 - Future
 - ft232r: Complete necessary interfaces for X6500
 - x6500: Bare minimum detection-only X6500 support via libusb
 - Minor debian packaging fixes.
+- No longer should hide --no-restart option if OpenCL support is missing
+- Handle crash exceptions by trying to restart cgminer unless the --no-restart
+option is used.
+- Switch queued count when choosing a different pool from a failed stratum pool
+in getwork thread.
+- Put a mandatory 5s wait between reattempting a getwork on failure to avoid
+hammering requests.
+- Make sure to check pool stratum curl exists under lock before attempting any
+recv to not risk dereferencing upon attempting to reinitiate stratum.
+- Avoid redefining macros and align to 4 byte boundaries.
+- API - add Stratum information to pools
+- update FPGA-README for MMQ
+- Time for dynamic is in microseconds, not ms.
+- x86_64 builds of mingw32 are not supported directly and should just configure
+as generic mingw32 builds since they're NOT 64 bit.
+- Use 3 significant digits when suffix string is used and values are >1000.
+- Get rid of unused warning for !scrypt.
+- Use select on stratum send to make sure the socket is writeable.
+- Cope with dval being zero in suffix_string and display a single decimal place
+when significant digits is not specified but the value is greater than 1000.
+- Pad out the suffix string function with zeroes on the right.
+- Failure to calloc in bin2hex is a fatal failure always so just check for that
+failure within the function and abort, simplifying the rest of the code.
+- Provide locking around the change of the stratum curl structures to avoid
+possible races.
+- Bump opencl kernel version numbers.
+- Remove atomic ops from opencl kernels given rarity of more than once nonce on
+the same wavefront and the potential increased ramspeed requirements to use the
+atomics.
+- Clear the pool idle flag in stratum when it comes back to life.
+- Display correct share hash and share difficulty with scrypt mining.
+- Use explicit host to BE functions in scrypt code instead of hard coding
+byteswap everywhere.
+- Show work target diff for scrypt mining.
+- Ease the checking on allocation of padbuffer8 in the hope it works partially
+anyway on an apparently failed call.
+- Watch for buffer overflows on receiving data into the socket buffer.
+- Round target difficulties down to be in keeping with the rounding of detected
+share difficulties.
+- Dramatically simplify the dynamic intensity calculation by oversampling many
+runs through the opencl kernel till we're likely well within the timer
+resolution on windows.
+- String alignment to 4 byte boundaries and optimisations for bin<->hex
+conversions.
+- In opencl_free_work, make sure to still flush results in dynamic mode.
+- Align static arrays to 4 byte boundaries to appease ARM builds for stratum.
+- Update documentation.
+- Left align values that are suffix_string generated.
+- Share_diff should not be converting the work data to hex.
+- Update readme describing difficulty displayed on log lines.
+- Off by one error.
+- Prevent overflows of the port char array in extract_sockaddr.
+- Disable stratum detection with scrypt.
+- Display the actual share diff next to the pool required diff, using a suffix
+creation function to prevent values of >1000 being shown in their entirety.
+- Fix 4 * 0 being 0 that would break dynamic intensity mode.
+- Supplement other 64-bit endian swap macros
+- Bugfix: Fix htobe64 on big endian platforms that don't define it
+- Fix lack of htobe64 on mingw32.
+- Reinstate the history on dynamic intensity mode to damp fluctuations in
+intensity but use an upper limit on how much the value can increase at any time
+to cope with rare overflows.
+- Update to cgminer's newer dynamic intensity algorithm
+- Support for the stratum mining protocol.
+- Simplify target generation code.
+- Add support for client.get_version for stratum.
+- Use a 64 bit unsigned integer on the diff target to generate the hex target.
+- Update reconnect message to show whole address including port.
+- Look for null values and parse correct separate array entries for url and port
+with client reconnect commands for stratum.
+- The command for stratum is client.reconnect, not mining.reconnect.
+- Only copy the stratum url to the rpc url if an rpc url does not exist.
+- Implement rudimentary mining.reconnect support for stratum.
+- Ignore the value of stratum_active on calling initiate_stratum and assume
+we're always trying to reinitiate it, and set the active flag to false in that
+function.
+- stratum auth can be unset if we fail to authorise on subsequent calls to
+auth_stratum which undoes the requirement of setting it in one place so set it
+in pool_active.
+- Format Stratum submission-start debug the same way as other submissions
+- Bugfix: Set work_restart_id in gen_stratum_work for when work is reused to
+avoid thinking it's all stale.
+- Only auto-switch to Stratum internally, but save HTTP URI in case pool stops
+using Stratum; also always shows original pool URI on RPC
+- SHUT_RDWR is now always defined for us, so no need to check ifdef on LP hang
+- Implement --no-stratum option to disable autodetection
+- Show Stratum pools as "Strtm" protocol in "Pool management" TUI
+- Bugfix: BFGMiner doesn't use rpc_proxytype
+- Remove free that could segfault.
+- Use the stratum url as the rpc url advertised if we switch to it.
+- Count an invalid nonce count as a hardware error on opencl.
+- Count each stratum work item as local work.
+- Cope with one stratum pool being the only active pool when it dies by sleeping
+for 5 seconds before retrying to get work from it instead of getting work
+indefinitely.
+- Detect stratum outage based on either select timing out or receiving an empty
+buffer and properly re-establish connection by disabling the stratum_active
+flag, coping with empty buffers in parse_stratum.
+- Fix various modminer warnings on mingw.
+- Fix sign warning on windows build for bitforce.
+- Cast socketfail to integer since SOCKET is an unsigned int on windows.
+- Use the stratum thread to detect when a stratum pool has died based on no
+message for 2 minutes.
+- Only set the stratum auth flag once and once the stratum thread is started,
+use that to set/unset the stratum active flag.
+- Only hand off to stratum from getwork if we succeed in initiating the
+protocol.
+- Target should only be 32 bytes copied.
+- Use a static array for work submission data instead of stack memory.
+- Clear the buffer data before sprinting to it.
+- Clear work stratum strings before setting them and add them to debug output.
+- Drop stratum connect failed message to verbose level only since it's a regular
+probing message.
+- TCP Keepalive in curl is only in very recent versions and not required with
+regular messages on stratum anyway.
+- Move stratum sockets to curl infrastructure with locking around send+recv to
+begin support for proxies and ssl.
+- Make detect stratum fail if a proxy has been set up.
+- Stratum does not currently have any proxy support so do not try to switch to
+stratum if a proxy has been specified.
+- Windows doesn't work with MSG_PEEK on recv so move to a continuously updating
+buffer for incoming messages.
+- Alloca is unreliable on windows so use static arrays in util.c stratum code.
+- Begin support for mingw stratum build.
+- Add space to reject reason.
+- Parse the reject reason where possible from stratum share submission.
+- Pass json error value to share result function to be able to parse reject
+reason in stratum.
+- Don't try to parse unneeded parameters in response to mining.subscribe.
+- Remove the sshare hash entry if we failed to send it.
+- Change notify message to info level to avoid spamming repeatedly when a pool
+is down.
+- Check the stratum pool difference has not changed compared to the work diff
+when testing whether a share meets the target or not and retarget if necessary.
+- Bit error in target calculation for stratum.
+- Offset the current block detection to the prev block hash.
+- We should be testing for id_val, not id in parse stratum response.
+- Make target on stratum scale to any size by clearing sequential bits according
+to diff.
+- Correct target calculation in gen_stratum_work.
+- If a share result has an error code but still has an id, it is likely a
+reject, not an error.
+- Initiate stratum the first time in pool_active only, allowing us to switch to
+it on getting a failed getwork and detecting the presence of stratum on the url
+at that time.
+- Use 5 second timeout on sock full for now as a temporary workaround.
+- If no stratum url is set by the end of the detect stratum routine, copy the
+sockaddr url.
+- Make all buffers slightly larger to prevent overflow.
+- Make the stratum recv buffer larger than the recvsize.
+- Userpass needs to be copied to user and pass earlier to allow stratum
+authorisation to work with it.
+- Store a sockaddr url of the stripped url used in determining sockaddr to not
+confuse it with the stratum url and fix build warnings.
+- Decrease the queued count with stratum work once it's staged as well.
+- Allow the stratum retry to initiate and auth stratum in pool_alive to make
+sure the stratum thread is started.
+- Avoid duplicating pool->rpc_url and setting pool->stratum_url twice to itself.
+- Detect if a getwork based pool has the X-Stratum header on startup, and if so,
+switch to the stratum based pool.
+- Comment update.
+- Minor message change.
+- Create a work item from a "clean" request from stratum allowing the new block
+to be detected and the appropriate block change message to be given.
+- Use statically allocated stratum strings in struct work to cope with the
+inability to safely deallocate dynamically allocated ram.
+- Use the current pool when deciding whether to reuse work from a stratum source
+rather than the work's previous pool.
+- Copy the stratum url to the rpc url to avoid none being set.
+- Provide locking around stratum send operations to avoid races.
+- Submit shares from stratum through the abstracted submit share function
+detecting what message they belong to and showing the data from the associated
+work, and then deleting it from the hash.
+- Use a more robust mechanism to obtain a \n terminated string over a socket.
+- Abstract out share submit as a function to be useable by stratum.
+- Rename parse_stratum to parse_method as it is only for stratum messages that
+contain methods.
+- Display stratum as mechanism in status line when current pool is running it.
+- Count each stratum notify as a getwork equivalent.
+- Correct nonce submitted with share.
+- Extranonce2 should be added before coinbase2.
+- We should be hashing the binary coinbase, not the hex one.
+- Fix endianness of nonce submitted for stratum.
+- Check that stratum is already active in initiate_stratum to avoid
+de-authorising ourselves by subscribing again.
+- Begin implementing a hash database of submissions and attempt sending results.
+- Copy parameters from stratum work required for share submission.
+- Set lagging flag on first adding a pool to prevent pool slow warning at
+startup.
+- Fix work->target being a 32 byte binary in gen_stratum_work.
+- Store and display stripped url in its own variable.
+- Create machinery to divert work requests to stratum.
+- Generate the work target in gen_stratum_work, setting default diff to 1 in
+case it is not yet set.
+- Generate work data, midstate and hash1 in gen_stratum_work.
+- Generate header created from stratum structures in gen_stratum_work.
+- Generate merkle root hash in gen_stratum_work.
+- Generate the coinbase for generation of stratum based work.
+- The number of transactions is variable so make merkle a variable length
+dynamically allocated array and track how many there are for stratum.
+- Rename nonce2 to n2size reflecting that it's a size variable and not the
+actual nonce.
+- Provide rudimentary support for stratum clean work command in the stratum
+thread.
+- Cope with pools being removed in the stratum thread.
+- Use the pool sock value directly in the stratum thread in case it changes
+after reconnecting.
+- Create a stratum thread per pool that has stratum that monitors the socket and
+serves received data.
+- Check return value of stratum_parse.
+- Complete authorisation in stratum.
+- Implement stratum parsing of notify parameters and storing them in the pool
+stratum work structure.
+- Create helper functions for duplicating json strings to avoid keeping json
+references in use.
+- Append \n in the sock_send function instead of adding it when constructing
+json in stratum.
+- Don't keep any json references around with stratum structures.
+- Create parse_stratum function that hands off stratum parameters to other
+functions to manage pool stratum work struct variables. Implement mining
+difficulty setting.
+- Create helper functions for checking when a socket is ready to read on and
+receive a single line at a time. Begin stratum authorisation process.
+- Provide a helper function for reading a single \n terminated string from a
+socket.
+- Create a stratum work structure to store current work variables.
+- Test specifically for stratum being active in pool_active.
+- Detect stratum in common place when adding urls, and use a bool to tell us
+when it's active.
+- Remove unused add_pool_details5
+- Fix warnings.
+- Extract and store various parameters on stratum init confirming successful
+mining notify.
+- Use existing socket macros and close the socket on failure in init stratum.
+- Initiate stratum and grab first json result.
+- Get detailed addressinfo from the parsed URL for future raw socket usage when
+possible. IPV4 only for now.
+- Prepare for getaddrinfo call.
+- Add data structures to pool struct for socket communications.
+- Put all socket definitions in util.h to allow reusing by added socket
+functions to be used in util.c.
 
 
 BFGMiner Version 2.8.3 - October 18, 2012

+ 46 - 4
README

@@ -136,6 +136,7 @@ Options for both config file and command line:
 --benchmark         Run BFGMiner in benchmark mode - produces no shares
 --coinbase-addr <arg> Set coinbase payout address for solo mining
 --coinbase-sig <arg> Set coinbase signature when possible
+--compact           Use compact display without per device statistics
 --debug|-D          Enable debug output
 --debuglog          Enable debug logging
 --expiry|-E <arg>   Upper bound on how many seconds after getting work we consider a share from it stale (default: 120)
@@ -148,6 +149,8 @@ Options for both config file and command line:
 --no-gbt            Disable getblocktemplate support
 --no-longpoll       Disable X-Long-Polling support
 --no-pool-disable   Do not automatically disable pools that continually reject shares
+--no-restart        Do not attempt to restart devices that hang or BFGMiner if it crashes
+--no-stratum        Disable Stratum detection
 --no-submit-stale   Don't submit shares if they are detected as stale
 --pass|-p <arg>     Password for bitcoin JSON-RPC server
 --per-device-stats  Force verbose mode and output per-device statistics
@@ -205,7 +208,6 @@ GPU only options:
 --kernel|-k <arg>   Override kernel to use (diablo, poclbm, phatk or diakgcn) - one value or comma separated
 --ndevs|-n          Enumerate number of detected GPUs and exit
 --no-adl            Disable the ATI display library used for monitoring and setting GPU parameters
---no-restart        Do not attempt to restart GPUs that hang
 --temp-overheat <arg> Overheat temperature when automatically managing fan and GPU speeds (default: 85)
 --vectors|-v <arg>  Override detected optimal vector (1, 2 or 4) - one value or comma separated list
 --worksize|-w <arg> Override detected optimal worksize - one value or comma separated list
@@ -344,6 +346,7 @@ The following options are available while running with a single keypress:
 P gives you:
 
 Current pool management strategy: Failover
+[F]ailover only disabled
 [A]dd pool [R]emove pool [D]isable pool [E]nable pool
 [C]hange management strategy [S]witch pool [I]nformation
 
@@ -355,14 +358,21 @@ S gives you:
 [S]cantime: 60
 [E]xpiry: 120
 [R]etries: -1
-[P]ause: 5
 [W]rite config file
+[B]FGMiner restart
 
 
 D gives you:
 
-Toggle: [D]ebug [N]ormal [S]ilent [V]erbose [R]PC debug
-[L]og interval [C]lear
+[N]ormal [C]lear [S]ilent mode (disable all output)
+[D]ebug:off
+[P]er-device:off
+[Q]uiet:off
+[V]erbose:off
+[R]PC debug:off
+[W]orkTime details:off
+co[M]pact: off
+[L]og interval:5
 
 
 Q quits the application.
@@ -386,6 +396,17 @@ Thread 1: 60.2 Mh/s Enabled ALIVE
 Or press any other key to continue
 
 
+The running log shows output like this:
+
+ [2012-10-12 18:02:20] Accepted f0c05469 Diff 1/1 GPU 0 pool 1
+ [2012-10-12 18:02:22] Accepted 218ac982 Diff 7/1 GPU 1 pool 1
+ [2012-10-12 18:02:23] Accepted d8300795 Diff 1/1 GPU 3 pool 1
+ [2012-10-12 18:02:24] Accepted 122c1ff1 Diff 14/1 GPU 1 pool 1
+
+The 8 byte hex value are the 2nd 8 bytes of the share being submitted to the
+pool. The 2 diff values are the actual difficulty target that share reached
+followed by the difficulty target the pool is currently asking for.
+
 ---
 Also many issues and FAQs are covered in the forum thread
 dedicated to this program,
@@ -435,6 +456,15 @@ diminish return performance even if the hash rate might appear better. A good
 starting baseline intensity to try on dedicated miners is 9. Higher values are
 there to cope with future improvements in hardware.
 
+
+The block display shows:
+Block: 0074c5e482e34a506d2a051a...  Started: [17:17:22]  Best share: 2.71K
+
+This shows a short stretch of the current block, when the new block started,
+and the all time best difficulty share you've submitted since starting cgminer
+this time.
+
+
 ---
 MULTIPOOL
 
@@ -922,6 +952,18 @@ To permanently give your account the 'dialout' group:
  sudo usermod -G dialout -a `whoami`
 Then logout and back in again
 
+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
+authority to the pool. It is a return to the problems of the old centralized
+"getwork" protocol, but capable of scaling to hardware of any speed like the
+standard GBT protocol. If a pool uses stratum instead of GBT, BFGMiner
+will automatically detect it and switch to the support as advertised if it can.
+Stratum uses direct TCP connections to the pool and thus it will NOT currently
+work through a http proxy but will work via a socks proxy if you need to use
+one. If you input the stratum port directly into your configuration, or use the
+special prefix "stratum+tcp://" instead of "http://", BFGMiner will ONLY try to
+use stratum protocol mining.
 
 ---
 

+ 89 - 112
api.c

@@ -25,122 +25,13 @@
 
 #include "compat.h"
 #include "miner.h"
+#include "util.h"
 #include "driver-cpu.h" /* for algo_names[], TODO: re-factor dependency */
 
 #if defined(USE_BITFORCE) || defined(USE_ICARUS) || defined(USE_ZTEX) || defined(USE_MODMINER)
 #define HAVE_AN_FPGA 1
 #endif
 
-#if defined(unix) || defined(__APPLE__)
-	#include <errno.h>
-	#include <sys/socket.h>
-	#include <netinet/in.h>
-	#include <arpa/inet.h>
-
-	#define SOCKETTYPE int
-	#define SOCKETFAIL(a) ((a) < 0)
-	#define INVSOCK -1
-	#define INVINETADDR -1
-	#define CLOSESOCKET close
-
-	#define SOCKERRMSG strerror(errno)
-#endif
-
-#ifdef WIN32
-	#include <ws2tcpip.h>
-	#include <winsock2.h>
-
-	#define SOCKETTYPE SOCKET
-	#define SOCKETFAIL(a) ((a) == SOCKET_ERROR)
-	#define INVSOCK INVALID_SOCKET
-	#define INVINETADDR INADDR_NONE
-	#define CLOSESOCKET closesocket
-
-	static char WSAbuf[1024];
-
-	struct WSAERRORS {
-		int id;
-		char *code;
-	} WSAErrors[] = {
-		{ 0,			"No error" },
-		{ WSAEINTR,		"Interrupted system call" },
-		{ WSAEBADF,		"Bad file number" },
-		{ WSAEACCES,		"Permission denied" },
-		{ WSAEFAULT,		"Bad address" },
-		{ WSAEINVAL,		"Invalid argument" },
-		{ WSAEMFILE,		"Too many open sockets" },
-		{ WSAEWOULDBLOCK,	"Operation would block" },
-		{ WSAEINPROGRESS,	"Operation now in progress" },
-		{ WSAEALREADY,		"Operation already in progress" },
-		{ WSAENOTSOCK,		"Socket operation on non-socket" },
-		{ WSAEDESTADDRREQ,	"Destination address required" },
-		{ WSAEMSGSIZE,		"Message too long" },
-		{ WSAEPROTOTYPE,	"Protocol wrong type for socket" },
-		{ WSAENOPROTOOPT,	"Bad protocol option" },
-		{ WSAEPROTONOSUPPORT,	"Protocol not supported" },
-		{ WSAESOCKTNOSUPPORT,	"Socket type not supported" },
-		{ WSAEOPNOTSUPP,	"Operation not supported on socket" },
-		{ WSAEPFNOSUPPORT,	"Protocol family not supported" },
-		{ WSAEAFNOSUPPORT,	"Address family not supported" },
-		{ WSAEADDRINUSE,	"Address already in use" },
-		{ WSAEADDRNOTAVAIL,	"Can't assign requested address" },
-		{ WSAENETDOWN,		"Network is down" },
-		{ WSAENETUNREACH,	"Network is unreachable" },
-		{ WSAENETRESET,		"Net connection reset" },
-		{ WSAECONNABORTED,	"Software caused connection abort" },
-		{ WSAECONNRESET,	"Connection reset by peer" },
-		{ WSAENOBUFS,		"No buffer space available" },
-		{ WSAEISCONN,		"Socket is already connected" },
-		{ WSAENOTCONN,		"Socket is not connected" },
-		{ WSAESHUTDOWN,		"Can't send after socket shutdown" },
-		{ WSAETOOMANYREFS,	"Too many references, can't splice" },
-		{ WSAETIMEDOUT,		"Connection timed out" },
-		{ WSAECONNREFUSED,	"Connection refused" },
-		{ WSAELOOP,		"Too many levels of symbolic links" },
-		{ WSAENAMETOOLONG,	"File name too long" },
-		{ WSAEHOSTDOWN,		"Host is down" },
-		{ WSAEHOSTUNREACH,	"No route to host" },
-		{ WSAENOTEMPTY,		"Directory not empty" },
-		{ WSAEPROCLIM,		"Too many processes" },
-		{ WSAEUSERS,		"Too many users" },
-		{ WSAEDQUOT,		"Disc quota exceeded" },
-		{ WSAESTALE,		"Stale NFS file handle" },
-		{ WSAEREMOTE,		"Too many levels of remote in path" },
-		{ WSASYSNOTREADY,	"Network system is unavailable" },
-		{ WSAVERNOTSUPPORTED,	"Winsock version out of range" },
-		{ WSANOTINITIALISED,	"WSAStartup not yet called" },
-		{ WSAEDISCON,		"Graceful shutdown in progress" },
-		{ WSAHOST_NOT_FOUND,	"Host not found" },
-		{ WSANO_DATA,		"No host data of that type was found" },
-		{ -1,			"Unknown error code" }
-	};
-
-	static char *WSAErrorMsg()
-	{
-		int i;
-		int id = WSAGetLastError();
-
-		/* Assume none of them are actually -1 */
-		for (i = 0; WSAErrors[i].id != -1; i++)
-			if (WSAErrors[i].id == id)
-				break;
-
-		sprintf(WSAbuf, "Socket Error: (%d) %s", id, WSAErrors[i].code);
-
-		return &(WSAbuf[0]);
-	}
-
-	#define SOCKERRMSG WSAErrorMsg()
-
-	#ifndef SHUT_RDWR
-	#define SHUT_RDWR SD_BOTH
-	#endif
-
-	#ifndef in_addr_t
-	#define in_addr_t uint32_t
-	#endif
-#endif
-
 // Big enough for largest API request
 //  though a PC with 100s of PGAs/CPUs may exceed the size ...
 // Current code assumes it can socket send this size also
@@ -153,6 +44,80 @@
 // However lots of PGA's may mean more
 #define QUEUE	100
 
+#if defined WIN32
+static char WSAbuf[1024];
+
+struct WSAERRORS {
+	int id;
+	char *code;
+} WSAErrors[] = {
+	{ 0,			"No error" },
+	{ WSAEINTR,		"Interrupted system call" },
+	{ WSAEBADF,		"Bad file number" },
+	{ WSAEACCES,		"Permission denied" },
+	{ WSAEFAULT,		"Bad address" },
+	{ WSAEINVAL,		"Invalid argument" },
+	{ WSAEMFILE,		"Too many open sockets" },
+	{ WSAEWOULDBLOCK,	"Operation would block" },
+	{ WSAEINPROGRESS,	"Operation now in progress" },
+	{ WSAEALREADY,		"Operation already in progress" },
+	{ WSAENOTSOCK,		"Socket operation on non-socket" },
+	{ WSAEDESTADDRREQ,	"Destination address required" },
+	{ WSAEMSGSIZE,		"Message too long" },
+	{ WSAEPROTOTYPE,	"Protocol wrong type for socket" },
+	{ WSAENOPROTOOPT,	"Bad protocol option" },
+	{ WSAEPROTONOSUPPORT,	"Protocol not supported" },
+	{ WSAESOCKTNOSUPPORT,	"Socket type not supported" },
+	{ WSAEOPNOTSUPP,	"Operation not supported on socket" },
+	{ WSAEPFNOSUPPORT,	"Protocol family not supported" },
+	{ WSAEAFNOSUPPORT,	"Address family not supported" },
+	{ WSAEADDRINUSE,	"Address already in use" },
+	{ WSAEADDRNOTAVAIL,	"Can't assign requested address" },
+	{ WSAENETDOWN,		"Network is down" },
+	{ WSAENETUNREACH,	"Network is unreachable" },
+	{ WSAENETRESET,		"Net connection reset" },
+	{ WSAECONNABORTED,	"Software caused connection abort" },
+	{ WSAECONNRESET,	"Connection reset by peer" },
+	{ WSAENOBUFS,		"No buffer space available" },
+	{ WSAEISCONN,		"Socket is already connected" },
+	{ WSAENOTCONN,		"Socket is not connected" },
+	{ WSAESHUTDOWN,		"Can't send after socket shutdown" },
+	{ WSAETOOMANYREFS,	"Too many references, can't splice" },
+	{ WSAETIMEDOUT,		"Connection timed out" },
+	{ WSAECONNREFUSED,	"Connection refused" },
+	{ WSAELOOP,		"Too many levels of symbolic links" },
+	{ WSAENAMETOOLONG,	"File name too long" },
+	{ WSAEHOSTDOWN,		"Host is down" },
+	{ WSAEHOSTUNREACH,	"No route to host" },
+	{ WSAENOTEMPTY,		"Directory not empty" },
+	{ WSAEPROCLIM,		"Too many processes" },
+	{ WSAEUSERS,		"Too many users" },
+	{ WSAEDQUOT,		"Disc quota exceeded" },
+	{ WSAESTALE,		"Stale NFS file handle" },
+	{ WSAEREMOTE,		"Too many levels of remote in path" },
+	{ WSASYSNOTREADY,	"Network system is unavailable" },
+	{ WSAVERNOTSUPPORTED,	"Winsock version out of range" },
+	{ WSANOTINITIALISED,	"WSAStartup not yet called" },
+	{ WSAEDISCON,		"Graceful shutdown in progress" },
+	{ WSAHOST_NOT_FOUND,	"Host not found" },
+	{ WSANO_DATA,		"No host data of that type was found" },
+	{ -1,			"Unknown error code" }
+};
+
+char *WSAErrorMsg(void) {
+	int i;
+	int id = WSAGetLastError();
+
+	/* Assume none of them are actually -1 */
+	for (i = 0; WSAErrors[i].id != -1; i++)
+		if (WSAErrors[i].id == id)
+			break;
+
+	sprintf(WSAbuf, "Socket Error: (%d) %s", id, WSAErrors[i].code);
+
+	return &(WSAbuf[0]);
+}
+#endif
 static char *io_buffer = NULL;
 static char *msg_buffer = NULL;
 static SOCKETTYPE sock = INVSOCK;
@@ -166,7 +131,7 @@ static const char SEPARATOR = '|';
 #define SEPSTR "|"
 static const char GPUSEP = ',';
 
-static const char *APIVERSION = "1.19";
+static const char *APIVERSION = "1.20";
 static const char *DEAD = "Dead";
 static const char *SICK = "Sick";
 static const char *NOSTART = "NoStart";
@@ -1765,6 +1730,9 @@ static void poolstatus(__maybe_unused SOCKETTYPE c, __maybe_unused char *param,
 	for (i = 0; i < total_pools; i++) {
 		struct pool *pool = pools[i];
 
+		if (pool->removed)
+			continue;
+
 		switch (pool->enabled) {
 			case POOL_DISABLED:
 				status = (char *)DISABLED;
@@ -1812,6 +1780,12 @@ static void poolstatus(__maybe_unused SOCKETTYPE c, __maybe_unused char *param,
 		root = api_add_diff(root, "Difficulty Rejected", &(pool->diff_rejected), false);
 		root = api_add_diff(root, "Difficulty Stale", &(pool->diff_stale), false);
 		root = api_add_diff(root, "Last Share Difficulty", &(pool->last_share_diff), false);
+		root = api_add_bool(root, "Has Stratum", &(pool->has_stratum), false);
+		root = api_add_bool(root, "Stratum Active", &(pool->stratum_active), false);
+		if (pool->stratum_active)
+			root = api_add_escape(root, "Stratum URL", pool->stratum_url, false);
+		else
+			root = api_add_const(root, "Stratum URL", BLANK, false);
 
 		if (isjson && (i > 0))
 			strcat(io_buffer, COMMA);
@@ -2129,6 +2103,7 @@ exitsama:
 static void addpool(__maybe_unused SOCKETTYPE c, char *param, bool isjson, __maybe_unused char group)
 {
 	char *url, *user, *pass;
+	struct pool *pool;
 	char *ptr;
 
 	if (param == NULL || *param == '\0') {
@@ -2145,7 +2120,9 @@ static void addpool(__maybe_unused SOCKETTYPE c, char *param, bool isjson, __may
 		return;
 	}
 
-	add_pool_details(true, url, user, pass);
+	pool = add_pool();
+	detect_stratum(pool, url);
+	add_pool_details(pool, true, url, user, pass);
 
 	ptr = escape_string(url, isjson);
 	strcpy(io_buffer, message(MSG_ADDPOOL, 0, ptr, isjson));

+ 8 - 19
configure.ac

@@ -69,7 +69,6 @@ USB_LIBS=""
 USB_FLAGS=""
 DLOPEN_FLAGS="-ldl"
 WS2_LIBS=""
-TIMER_LIBS=""
 MATH_LIBS="-lm"
 
 case $target in
@@ -82,21 +81,13 @@ case $target in
 esac
 
 case $target in
-	x86_64-w64-mingw32)
-	have_x86_64=true
-	have_win32=true
-	PTHREAD_FLAGS=""
-	DLOPEN_FLAGS=""
-	WS2_LIBS="-lws2_32"
-	TIMER_LIBS="-lwinmm"
-	;;
   *-*-mingw*)
     have_x86_64=false
     have_win32=true
     PTHREAD_FLAGS=""
     DLOPEN_FLAGS=""
     WS2_LIBS="-lws2_32"
-	TIMER_LIBS="-lwinmm"
+    AC_DEFINE([_WIN32_WINNT], [0x0501], "WinNT version for XP+ support")
     ;;
   *-*-cygwin*)
 	have_cygwin=true
@@ -379,8 +370,7 @@ AM_CONDITIONAL([HAVE_LIBUDEV], [test x$libudev != xno])
 
 PKG_PROG_PKG_CONFIG()
 
-PKG_CHECK_MODULES([LIBCURL], [libcurl >= 7.15.6], [AC_DEFINE([CURL_HAS_SOCKOPT], [1], [Defined if version of curl supports sockopts.])],
-[PKG_CHECK_MODULES([LIBCURL], [libcurl >= 7.10.1], ,[AC_MSG_ERROR([Missing required libcurl dev >= 7.10.1])])])
+PKG_CHECK_MODULES([LIBCURL], [libcurl >= 7.18.2], ,[AC_MSG_ERROR([Missing required libcurl dev >= 7.18.2])])
 AC_SUBST(LIBCURL_LIBS)
 
 dnl CCAN wants to know a lot of vars.
@@ -421,11 +411,11 @@ fi
 
 AC_DEFINE_UNQUOTED([CGMINER_PREFIX], ["$prefix/bin"], [Path to bfgminer install])
 
-AC_DEFINE_UNQUOTED([PHATK_KERNNAME], ["phatk120823"], [Filename for phatk kernel])
-AC_DEFINE_UNQUOTED([POCLBM_KERNNAME], ["poclbm120823"], [Filename for poclbm kernel])
-AC_DEFINE_UNQUOTED([DIAKGCN_KERNNAME], ["diakgcn120823"], [Filename for diakgcn kernel])
-AC_DEFINE_UNQUOTED([DIABLO_KERNNAME], ["diablo120823"], [Filename for diablo kernel])
-AC_DEFINE_UNQUOTED([SCRYPT_KERNNAME], ["scrypt120823"], [Filename for scrypt kernel])
+AC_DEFINE_UNQUOTED([PHATK_KERNNAME], ["phatk121016"], [Filename for phatk kernel])
+AC_DEFINE_UNQUOTED([POCLBM_KERNNAME], ["poclbm121016"], [Filename for poclbm kernel])
+AC_DEFINE_UNQUOTED([DIAKGCN_KERNNAME], ["diakgcn121016"], [Filename for diakgcn kernel])
+AC_DEFINE_UNQUOTED([DIABLO_KERNNAME], ["diablo121016"], [Filename for diablo kernel])
+AC_DEFINE_UNQUOTED([SCRYPT_KERNNAME], ["scrypt121016"], [Filename for scrypt kernel])
 
 
 AC_SUBST(JANSSON_LIBS)
@@ -436,7 +426,6 @@ AC_SUBST(NCURSES_CPPFLAGS)
 AC_SUBST(NCURSES_LIBS)
 AC_SUBST(PDCURSES_LIBS)
 AC_SUBST(WS2_LIBS)
-AC_SUBST(TIMER_LIBS)
 AC_SUBST(MATH_LIBS)
 AC_SUBST(UDEV_LIBS)
 AC_SUBST(USB_LIBS)
@@ -537,7 +526,7 @@ echo "Compilation............: make (or gmake)"
 echo "  CPPFLAGS.............: $CPPFLAGS $NCURSES_CPPFLAGS"
 echo "  CFLAGS...............: $CFLAGS"
 echo "  LDFLAGS..............: $LDFLAGS $PTHREAD_FLAGS $USB_FLAGS"
-echo "  LDADD................: $DLOPEN_FLAGS $LIBCURL_LIBS $JANSSON_LIBS $PTHREAD_LIBS $NCURSES_LIBS $PDCURSES_LIBS $WS2_LIBS $TIMER_LIBS $MATH_LIBS $UDEV_LIBS $USB_LIBS"
+echo "  LDADD................: $DLOPEN_FLAGS $LIBCURL_LIBS $JANSSON_LIBS $PTHREAD_LIBS $NCURSES_LIBS $PDCURSES_LIBS $WS2_LIBS $MATH_LIBS $UDEV_LIBS $USB_LIBS"
 echo
 echo "Installation...........: make install (as root if needed, with 'su' or 'sudo')"
 echo "  prefix...............: $prefix"

+ 1 - 6
diablo120823.cl → diablo121016.cl

@@ -1243,12 +1243,7 @@ void search(
     ZA[924] = (ZCh(ZA[922], ZA[920], ZA[918]) + ZA[923]) + ZR26(ZA[922]);
     
 #define FOUND (0x0F)
-
-#if defined(OCL1)
-	#define SETFOUND(Xnonce) output[output[FOUND]++] = Xnonce
-#else
-	#define SETFOUND(Xnonce) output[atomic_add(&output[FOUND], 1)] = Xnonce
-#endif
+#define SETFOUND(Xnonce) output[output[FOUND]++] = Xnonce
 
 #if defined(VECTORS4)
 	bool result = any(ZA[924] == 0x136032EDU);

+ 1 - 6
diakgcn120823.cl → diakgcn121016.cl

@@ -572,12 +572,7 @@ __kernel
 	V[7] += V[3] + W[12] + ch(V[0], V[1], V[2]) + rotr26(V[0]);
 
 #define FOUND (0x0F)
-
-#if defined(OCL1)
-	#define SETFOUND(Xnonce) output[output[FOUND]++] = Xnonce
-#else
-	#define SETFOUND(Xnonce) output[atomic_add(&output[FOUND], 1)] = Xnonce
-#endif
+#define SETFOUND(Xnonce) output[output[FOUND]++] = Xnonce
 
 #ifdef VECTORS4
 	if ((V[7].x == 0x136032edU) ^ (V[7].y == 0x136032edU) ^ (V[7].z == 0x136032edU) ^ (V[7].w == 0x136032edU)) {

+ 14 - 19
driver-icarus.c

@@ -586,22 +586,19 @@ bool icarus_detect_custom(const char *devpath, struct device_api *api, struct IC
 	icarus_close(fd);
 
 	nonce_hex = bin2hex(nonce_bin, sizeof(nonce_bin));
-	if (nonce_hex) {
-		if (strncmp(nonce_hex, golden_nonce, 8)) {
-			applog(LOG_DEBUG,
-				"Icarus Detect: "
-				"Test failed at %s: get %s, should: %s",
-				devpath, nonce_hex, golden_nonce);
-			free(nonce_hex);
-			return false;
-		}
-		applog(LOG_DEBUG, 
+	if (strncmp(nonce_hex, golden_nonce, 8)) {
+		applog(LOG_DEBUG,
 			"Icarus Detect: "
-			"Test succeeded at %s: got %s",
-				devpath, nonce_hex);
+			"Test failed at %s: get %s, should: %s",
+			devpath, nonce_hex, golden_nonce);
 		free(nonce_hex);
-	} else
 		return false;
+	}
+	applog(LOG_DEBUG,
+		"Icarus Detect: "
+		"Test succeeded at %s: got %s",
+			devpath, nonce_hex);
+	free(nonce_hex);
 
 	if (serial_claim(devpath, api)) {
 		const char *claimedby = serial_claim(devpath, api)->dname;
@@ -741,12 +738,10 @@ static bool icarus_start_work(struct thr_info *thr, const unsigned char *ob_bin)
 
 	if (opt_debug) {
 		ob_hex = bin2hex(ob_bin, 64);
-		if (ob_hex) {
-			applog(LOG_DEBUG, "%s %u sent: %s",
-				icarus->api->name,
-				icarus->device_id, ob_hex);
-			free(ob_hex);
-		}
+		applog(LOG_DEBUG, "%s %u sent: %s",
+			icarus->api->name,
+			icarus->device_id, ob_hex);
+		free(ob_hex);
 	}
 
 	return true;

+ 2 - 1
driver-modminer.c

@@ -17,6 +17,7 @@
 #include "logging.h"
 #include "miner.h"
 #include "fpgautils.h"
+#include "util.h"
 
 #define BITSTREAM_FILENAME "fpgaminer_top_fixed7_197MHz.bit"
 #define BISTREAM_USER_ID "\2\4$B"
@@ -75,7 +76,7 @@ modminer_detect_one(const char *devpath)
 		bailout(LOG_DEBUG, "ModMiner detect: failed to open %s", devpath);
 
 	char buf[0x100];
-	size_t len;
+	ssize_t len;
 
 	// Sending a "ping" first, to workaround bug in new firmware betas (see issue #62)
 	// Sending 45 noops, just in case the device was left in "start job" reading

+ 20 - 68
driver-opencl.c

@@ -800,21 +800,6 @@ struct cgpu_info *cpus;
 
 #ifdef HAVE_OPENCL
 
-#ifdef WIN32
-static UINT timeperiod_set;
-#endif
-
-void opencl_dynamic_cleanup() {
-#ifdef WIN32
-	if (timeperiod_set) {
-		timeEndPeriod(timeperiod_set);
-		timeperiod_set = 0;
-	}
-#endif
-}
-
-extern int opt_dynamic_interval;
-
 /* In dynamic mode, only the first thread of each device will be in use.
  * This potentially could start a thread that was stopped with the start-stop
  * options if one were to disable dynamic from the menu on a paused GPU */
@@ -822,18 +807,10 @@ void pause_dynamic_threads(int gpu)
 {
 	struct cgpu_info *cgpu = &gpus[gpu];
 	int i;
-#ifdef WIN32
-	bool any_dynamic = false;
-#endif
 
 	for (i = 1; i < cgpu->threads; i++) {
 		struct thr_info *thr = &thr_info[i];
 
-#ifdef WIN32
-		if (cgpu->dynamic)
-			any_dynamic = true;
-#endif
-
 		if (!thr->pause && cgpu->dynamic) {
 			applog(LOG_WARNING, "Disabling extra threads due to dynamic mode.");
 			applog(LOG_WARNING, "Tune dynamic intensity with --gpu-dyninterval");
@@ -843,20 +820,6 @@ void pause_dynamic_threads(int gpu)
 		if (!cgpu->dynamic && cgpu->deven != DEV_DISABLED)
 			tq_push(thr->q, &ping);
 	}
-
-#ifdef WIN32
-	if (any_dynamic) {
-		if (!timeperiod_set) {
-			timeperiod_set = opt_dynamic_interval > 3 ? (opt_dynamic_interval / 2) : 1;
-			if (TIMERR_NOERROR != timeBeginPeriod(timeperiod_set))
-				timeperiod_set = 0;
-		}
-	} else {
-		if (timeperiod_set) {
-			opencl_dynamic_cleanup();
-		}
-	}
-#endif
 }
 
 
@@ -1719,10 +1682,8 @@ static void opencl_free_work(struct thr_info *thr, struct work *work)
 	const int thr_id = thr->id;
 	struct opencl_thread_data *thrdata = thr->cgpu_data;
 	_clState *clState = clStates[thr_id];
-	struct cgpu_info *gpu = thr->cgpu;
 
-	if (!gpu->dynamic)
-		clFinish(clState->commandQueue);
+	clFinish(clState->commandQueue);
 
 	if (thrdata->res[FOUND]) {
 		thrdata->last_work = &thrdata->_last_work;
@@ -1759,8 +1720,25 @@ static int64_t opencl_scanhash(struct thr_info *thr, struct work *work,
 	int64_t hashes;
 
 	/* This finish flushes the readbuffer set with CL_FALSE later */
-	if (!gpu->dynamic)
-		clFinish(clState->commandQueue);
+	clFinish(clState->commandQueue);
+
+	/* Windows' timer resolution is only 15ms so oversample 5x */
+	if (gpu->dynamic && (++gpu->intervals * dynamic_us) > 70000) {
+		struct timeval tv_gpuend;
+		double gpu_us;
+
+		gettimeofday(&tv_gpuend, NULL);
+		gpu_us = us_tdiff(&tv_gpuend, &gpu->tv_gpustart) / gpu->intervals;
+		if (gpu_us > dynamic_us) {
+			if (gpu->intensity > MIN_INTENSITY)
+				--gpu->intensity;
+		} else if (gpu_us < dynamic_us / 2) {
+			if (gpu->intensity < MAX_INTENSITY)
+				++gpu->intensity;
+		}
+		memcpy(&(gpu->tv_gpustart), &tv_gpuend, sizeof(struct timeval));
+		gpu->intervals = 0;
+	}
 
 	set_threads_hashes(clState->vwidth, &hashes, globalThreads, localThreads[0], &gpu->intensity);
 	if (hashes > gpu->max_hashes)
@@ -1787,8 +1765,6 @@ static int64_t opencl_scanhash(struct thr_info *thr, struct work *work,
 		clFinish(clState->commandQueue);
 	}
 
-	gettimeofday(&gpu->tv_gpustart, NULL);
-
 	status = thrdata->queue_kernel_parameters(clState, &work->blk, globalThreads[0]);
 	if (unlikely(status != CL_SUCCESS)) {
 		applog(LOG_ERR, "Error: clSetKernelArg of all params failed.");
@@ -1816,30 +1792,6 @@ static int64_t opencl_scanhash(struct thr_info *thr, struct work *work,
 		return -1;
 	}
 
-	if (gpu->dynamic) {
-		struct timeval diff;
-		suseconds_t gpu_us;
-
-		clFinish(clState->commandQueue);
-		gettimeofday(&gpu->tv_gpuend, NULL);
-		timersub(&gpu->tv_gpuend, &gpu->tv_gpustart, &diff);
-		gpu_us = diff.tv_sec * 1000000 + diff.tv_usec;
-		if (likely(gpu_us >= 0)) {
-			gpu->gpu_us_average = (gpu->gpu_us_average + gpu_us * 0.63) / 1.63;
-
-			/* Try to not let the GPU be out for longer than
-			 * opt_dynamic_interval in ms, but increase
-			 * intensity when the system is idle in dynamic mode */
-			if (gpu->gpu_us_average > dynamic_us) {
-				if (gpu->intensity > MIN_INTENSITY)
-					--gpu->intensity;
-			} else if (gpu->gpu_us_average < dynamic_us / 2) {
-				if (gpu->intensity < MAX_INTENSITY)
-					++gpu->intensity;
-			}
-		}
-	}
-
 	/* The amount of work scanned can fluctuate when intensity changes
 	 * and since we do this one cycle behind, we increment the work more
 	 * than enough to prevent repeating work */

+ 11 - 10
findnonce.c

@@ -134,7 +134,7 @@ void precalc_hash(dev_blk_ctx *blk, uint32_t *state, uint32_t *data)
 
 struct pc_data {
 	struct thr_info *thr;
-	struct work *work;
+	struct work work;
 	uint32_t res[MAXBUFFERS];
 	pthread_t pth;
 	int found;
@@ -144,7 +144,6 @@ static void *postcalc_hash(void *userdata)
 {
 	struct pc_data *pcd = (struct pc_data *)userdata;
 	struct thr_info *thr = pcd->thr;
-	struct work *work = pcd->work;
 	unsigned int entry = 0;
 
 	pthread_detach(pthread_self());
@@ -152,12 +151,19 @@ static void *postcalc_hash(void *userdata)
 
 	/* To prevent corrupt values in FOUND from trying to read beyond the
 	 * end of the res[] array */
-	pcd->res[FOUND] &= FOUND;
+	if (unlikely(pcd->res[FOUND] & ~FOUND)) {
+		applog(LOG_WARNING, "%s%d: invalid nonce count - HW error",
+				thr->cgpu->api->name, thr->cgpu->device_id);
+		hw_errors++;
+		thr->cgpu->hw_errors++;
+		pcd->res[FOUND] &= FOUND;
+	}
+
 	for (entry = 0; entry < pcd->res[FOUND]; entry++) {
 		uint32_t nonce = pcd->res[entry];
 
 		applog(LOG_DEBUG, "OCL NONCE %u found in slot %d", nonce, entry);
-		submit_nonce(thr, work, nonce);
+		submit_nonce(thr, &pcd->work, nonce);
 	}
 
 	free(pcd);
@@ -172,14 +178,9 @@ void postcalc_hash_async(struct thr_info *thr, struct work *work, uint32_t *res)
 		applog(LOG_ERR, "Failed to malloc pc_data in postcalc_hash_async");
 		return;
 	}
-	pcd->work = calloc(1, sizeof(struct work));
-	if (unlikely(!pcd->work)) {
-		applog(LOG_ERR, "Failed to malloc work in postcalc_hash_async");
-		return;
-	}
 
 	pcd->thr = thr;
-	memcpy(pcd->work, work, sizeof(struct work));
+	memcpy(&pcd->work, work, sizeof(struct work));
 	memcpy(&pcd->res, res, BUFFERSIZE);
 
 	if (pthread_create(&pcd->pth, NULL, postcalc_hash, (void *)pcd)) {

+ 1 - 1
fpgautils.c

@@ -137,7 +137,7 @@ int serial_autodetect_ftdi(detectone_func_t detectone, const char *needle, const
 	char **bufptrs;
 	char *buf;
 	int found = 0;
-	int i;
+	DWORD i;
 
 	FT_STATUS ftStatus;
 	DWORD numDevs;

+ 2 - 0
logging.c

@@ -7,6 +7,8 @@
  * any later version.  See COPYING for more details.
  */
 
+#include "config.h"
+
 #include <unistd.h>
 
 #include "logging.h"

File diff suppressed because it is too large
+ 465 - 186
miner.c


+ 94 - 8
miner.h

@@ -16,6 +16,7 @@
 #include "elist.h"
 #include "uthash.h"
 #include "logging.h"
+#include "util.h"
 
 #ifdef HAVE_OPENCL
 #include "CL/cl.h"
@@ -51,6 +52,19 @@ void *alloca (size_t);
 # endif
 #endif
 
+#ifdef __MINGW32__
+#include <windows.h>
+#include <io.h>
+static inline int fsync (int fd)
+{
+	return (FlushFileBuffers ((HANDLE) _get_osfhandle (fd))) ? 0 : -1;
+}
+
+#ifndef MSG_DONTWAIT
+# define MSG_DONTWAIT 0x1000000
+#endif
+#endif /* __MINGW32__ */
+
 #if defined (__linux)
  #ifndef LINUX
   #define LINUX
@@ -137,18 +151,27 @@ void *alloca (size_t);
   #include <endian.h>
 #endif
 
-/* This assumes htobe32 is a macro in endian.h */
+/* This assumes htobe32 is a macro in endian.h, and if it doesn't exist, then
+ * the others also won't exist */
 #ifndef htobe32
 # if __BYTE_ORDER == __LITTLE_ENDIAN
 #  define be32toh(x) bswap_32(x)
+#  define be64toh(x) bswap_64(x)
 #  define htobe32(x) bswap_32(x)
+#  define htobe64(x) bswap_64(x)
 #  define le32toh(x) (x)
+#  define le64toh(x) (x)
 #  define htole32(x) (x)
+#  define htole64(x) (x)
 # elif __BYTE_ORDER == __BIG_ENDIAN
 #  define be32toh(x) (x)
+#  define be64toh(x) (x)
 #  define htobe32(x) (x)
+#  define htobe64(x) (x)
 #  define le32toh(x) bswap_32(x)
+#  define le64toh(x) bswap_64(x)
 #  define htole32(x) bswap_32(x)
+#  define htole64(x) bswap_64(x)
 #else
 #error UNKNOWN BYTE ORDER
 #endif
@@ -290,6 +313,7 @@ struct device_api {
 	void (*free_work)(struct thr_info*, struct work*);
 	bool (*prepare_work)(struct thr_info*, struct work*);
 	int64_t (*scanhash)(struct thr_info*, struct work*, int64_t);
+	void (*hw_error)(struct thr_info*);
 	void (*thread_shutdown)(struct thr_info*);
 	void (*thread_enable)(struct thr_info*);
 };
@@ -433,9 +457,8 @@ struct cgpu_info {
 	size_t opt_tc, thread_concurrency;
 	size_t shaders;
 #endif
-	struct timeval tv_gpustart;;
-	struct timeval tv_gpuend;
-	double gpu_us_average;
+	struct timeval tv_gpustart;
+	int intervals;
 #endif
 
 	bool new_work;
@@ -519,6 +542,7 @@ extern int thr_info_create(struct thr_info *thr, pthread_attr_t *attr, void *(*s
 extern void thr_info_cancel(struct thr_info *thr);
 extern void thr_info_freeze(struct thr_info *thr);
 extern void nmsleep(unsigned int msecs);
+extern double us_tdiff(struct timeval *end, struct timeval *start);
 extern void rename_thr(const char* name);
 extern double tdiff(struct timeval *end, struct timeval *start);
 
@@ -582,6 +606,21 @@ static inline void swap32yes(void*out, const void*in, size_t sz) {
 #  define swap32tole(out, in, sz)  (void)0
 #endif
 
+static inline void swab256(void *dest_p, const void *src_p)
+{
+	uint32_t *dest = dest_p;
+	const uint32_t *src = src_p;
+
+	dest[0] = swab32(src[7]);
+	dest[1] = swab32(src[6]);
+	dest[2] = swab32(src[5]);
+	dest[3] = swab32(src[4]);
+	dest[4] = swab32(src[3]);
+	dest[5] = swab32(src[2]);
+	dest[6] = swab32(src[1]);
+	dest[7] = swab32(src[0]);
+}
+
 extern void quit(int status, const char *format, ...);
 
 static inline void mutex_lock(pthread_mutex_t *lock)
@@ -667,6 +706,7 @@ extern bool opt_worktime;
 #ifdef USE_BITFORCE
 extern bool opt_bfl_noncerange;
 #endif
+extern int swork_id;
 
 extern pthread_rwlock_t netacc_lock;
 
@@ -719,8 +759,9 @@ extern void api(int thr_id);
 
 extern struct pool *current_pool(void);
 extern int enabled_pools;
-extern void add_pool_details5(bool live, char *url, char *user, char *pass, char *proxy);
-extern void add_pool_details(bool live, char *url, char *user, char *pass);
+extern bool detect_stratum(struct pool *pool, char *url);
+extern struct pool *add_pool(void);
+extern void add_pool_details(struct pool *pool, bool live, char *url, char *user, char *pass);
 
 #define MAX_GPUDEVICES 16
 
@@ -831,6 +872,24 @@ enum pool_protocol {
 	PLP_GETBLOCKTEMPLATE,
 };
 
+struct stratum_work {
+	char *job_id;
+	char *prev_hash;
+	char *coinbase1;
+	char *coinbase2;
+	char **merkle;
+	char *bbversion;
+	char *nbit;
+	char *ntime;
+	bool clean;
+
+	int merkles;
+	int diff;
+};
+
+#define RECVSIZE 8192
+#define RBUFSIZE (RECVSIZE + 4)
+
 struct pool {
 	int pool_no;
 	int prio;
@@ -899,12 +958,31 @@ struct pool {
 
 	struct cgminer_stats cgminer_stats;
 	struct cgminer_pool_stats cgminer_pool_stats;
+
+	/* Stratum variables */
+	char *stratum_url;
+	char *stratum_port;
+	CURL *stratum_curl;
+	SOCKETTYPE sock;
+	char sockbuf[RBUFSIZE];
+	struct sockaddr_in *server, client;
+	char *sockaddr_url; /* stripped url used for sockaddr */
+	char *nonce1;
+	uint32_t nonce2;
+	int n2size;
+	bool has_stratum;
+	bool stratum_active;
+	bool stratum_auth;
+	struct stratum_work swork;
+	pthread_t stratum_thread;
+	pthread_mutex_t stratum_lock;
 };
 
 #define GETWORK_MODE_TESTPOOL 'T'
 #define GETWORK_MODE_POOL 'P'
 #define GETWORK_MODE_LP 'L'
 #define GETWORK_MODE_BENCHMARK 'B'
+#define GETWORK_MODE_STRATUM 'S'
 
 struct work {
 	unsigned char	data[128];
@@ -913,10 +991,10 @@ struct work {
 	unsigned char	target[32];
 	unsigned char	hash[32];
 
+	uint32_t	outputhash;
+
 	int		rolls;
 
-	uint32_t	output[1];
-	uint32_t	valid;
 	dev_blk_ctx	blk;
 
 	struct thr_info	*thr;
@@ -934,6 +1012,14 @@ struct work {
 	bool		block;
 	bool		queued;
 
+	bool		stratum;
+	/* These are arbitrary lengths as it is too hard to keep track of
+	 * dynamically allocated ram in work structs */
+	char 		job_id[64];
+	char		nonce2[64];
+	char		ntime[16];
+	int		sdiff;
+
 	unsigned char	work_restart_id;
 	int		id;
 	UT_hash_handle hh;

+ 1 - 6
phatk120823.cl → phatk121016.cl

@@ -388,12 +388,7 @@ void search(	const uint state0, const uint state1, const uint state2, const uint
 		(-(K[60] + H[7]) - S1((Vals[0] + Vals[4]) + (K[59] + W(59+64))  + s1(64+59)+ ch(59+64)));
 
 #define FOUND (0x0F)
-
-#if defined(OCL1)
-	#define SETFOUND(Xnonce) output[output[FOUND]++] = Xnonce
-#else
-	#define SETFOUND(Xnonce) output[atomic_add(&output[FOUND], 1)] = Xnonce
-#endif
+#define SETFOUND(Xnonce) output[output[FOUND]++] = Xnonce
 
 #ifdef VECTORS4
 	bool result = W[117].x & W[117].y & W[117].z & W[117].w;

+ 1 - 6
poclbm120823.cl → poclbm121016.cl

@@ -1322,12 +1322,7 @@ Vals[2]+=(rotr(Vals[1],6)^rotr(Vals[1],11)^rotr(Vals[1],25));
 Vals[2]+=ch(Vals[1],Vals[4],Vals[3]);
 
 #define FOUND (0x0F)
-
-#if defined(OCL1)
-	#define SETFOUND(Xnonce) output[output[FOUND]++] = Xnonce
-#else
-	#define SETFOUND(Xnonce) output[atomic_add(&output[FOUND], 1)] = Xnonce
-#endif
+#define SETFOUND(Xnonce) output[output[FOUND]++] = Xnonce
 
 #if defined(VECTORS2) || defined(VECTORS4)
 	if (any(Vals[2] == 0x136032edU)) {

+ 12 - 0
scrypt.c

@@ -405,6 +405,18 @@ static uint32_t scrypt_1024_1_1_256_sp(const uint32_t* input, char* scratchpad)
 	return PBKDF2_SHA256_80_128_32(input, X);
 }
 
+void scrypt_outputhash(struct work *work)
+{
+	uint32_t data[20];
+	char *scratchbuf;
+	uint32_t *nonce = (uint32_t *)(work->data + 76);
+
+	be32enc_vect(data, (const uint32_t *)work->data, 19);
+	data[19] = htobe32(*nonce);
+	scratchbuf = alloca(131584);
+	work->outputhash = scrypt_1024_1_1_256_sp(data, scratchbuf);
+}
+
 /* Used externally as confirmation of correct OCL code */
 bool scrypt_test(unsigned char *pdata, const unsigned char *ptarget, uint32_t nonce)
 {

+ 8 - 0
scrypt.h

@@ -1,9 +1,13 @@
 #ifndef SCRYPT_H
 #define SCRYPT_H
 
+#include "miner.h"
+
 #ifdef USE_SCRYPT
 extern bool scrypt_test(unsigned char *pdata, const unsigned char *ptarget,
 			uint32_t nonce);
+extern void scrypt_outputhash(struct work *work);
+
 #else /* USE_SCRYPT */
 static inline bool scrypt_test(__maybe_unused unsigned char *pdata,
 			       __maybe_unused const unsigned char *ptarget,
@@ -11,6 +15,10 @@ static inline bool scrypt_test(__maybe_unused unsigned char *pdata,
 {
 	return false;
 }
+
+static inline void scrypt_outputhash(__maybe_unused struct work *work)
+{
+}
 #endif /* USE_SCRYPT */
 
 #endif /* SCRYPT_H */

+ 1 - 6
scrypt120823.cl → scrypt121016.cl

@@ -683,12 +683,7 @@ void scrypt_core(uint4 X[8], __global uint4*restrict lookup)
 }
 
 #define FOUND (0x0F)
-
-#if defined(OCL1)
-	#define SETFOUND(Xnonce) output[output[FOUND]++] = Xnonce
-#else
-	#define SETFOUND(Xnonce) output[atomic_add(&output[FOUND], 1)] = Xnonce
-#endif
+#define SETFOUND(Xnonce) output[output[FOUND]++] = Xnonce
 
 __attribute__((reqd_work_group_size(WORKSIZE, 1, 1)))
 __kernel void search(__global const uint4 * restrict input,

+ 651 - 19
util.c

@@ -35,20 +35,17 @@
 # include <sys/socket.h>
 # include <netinet/in.h>
 # include <netinet/tcp.h>
+# include <netdb.h>
 #else
 # include <winsock2.h>
 # include <mstcpip.h>
+# include <ws2tcpip.h>
 #endif
 
 #include "miner.h"
 #include "elist.h"
 #include "compat.h"
-
-#if JANSSON_MAJOR_VERSION >= 2
-#define JSON_LOADS(str, err_ptr) json_loads((str), 0, (err_ptr))
-#else
-#define JSON_LOADS(str, err_ptr) json_loads((str), (err_ptr))
-#endif
+#include "util.h"
 
 bool successful_connect = false;
 struct timeval nettime;
@@ -68,6 +65,7 @@ struct header_info {
 	char		*lp_path;
 	int		rolltime;
 	char		*reason;
+	char		*stratum_url;
 	bool		hadrolltime;
 	bool		canroll;
 	bool		hadexpire;
@@ -207,18 +205,21 @@ static size_t resp_hdr_cb(void *ptr, size_t size, size_t nmemb, void *user_data)
 		val = NULL;
 	}
 
+	if (!strcasecmp("X-Stratum", key)) {
+		hi->stratum_url = val;
+		val = NULL;
+	}
+
 out:
 	free(key);
 	free(val);
 	return ptrlen;
 }
 
-#ifdef CURL_HAS_SOCKOPT
-int json_rpc_call_sockopt_cb(void __maybe_unused *userdata, curl_socket_t fd,
-			     curlsocktype __maybe_unused purpose)
+static int keep_sockalive(SOCKETTYPE fd)
 {
-	int tcp_keepidle = 120;
-	int tcp_keepintvl = 120;
+	int tcp_keepidle = 60;
+	int tcp_keepintvl = 60;
 
 #ifndef WIN32
 	int keepalive = 1;
@@ -261,7 +262,12 @@ int json_rpc_call_sockopt_cb(void __maybe_unused *userdata, curl_socket_t fd,
 
 	return 0;
 }
-#endif
+
+int json_rpc_call_sockopt_cb(void __maybe_unused *userdata, curl_socket_t fd,
+			     curlsocktype __maybe_unused purpose)
+{
+	return keep_sockalive(fd);
+}
 
 static void last_nettime(struct timeval *last)
 {
@@ -285,7 +291,7 @@ json_t *json_rpc_call(CURL *curl, const char *url,
 {
 	long timeout = longpoll ? (60 * 60) : 60;
 	struct data_buffer all_data = {NULL, 0, NULL};
-	struct header_info hi = {NULL, 0, NULL, false, false, false};
+	struct header_info hi = {NULL, 0, NULL, NULL, false, false, false};
 	char len_hdr[64], user_agent_hdr[128];
 	char curl_err_str[CURL_ERROR_SIZE];
 	struct curl_slist *headers = NULL;
@@ -338,10 +344,8 @@ json_t *json_rpc_call(CURL *curl, const char *url,
 		curl_easy_setopt(curl, CURLOPT_USERPWD, userpass);
 		curl_easy_setopt(curl, CURLOPT_HTTPAUTH, CURLAUTH_BASIC);
 	}
-#ifdef CURL_HAS_SOCKOPT
 	if (longpoll)
 		curl_easy_setopt(curl, CURLOPT_SOCKOPTFUNCTION, json_rpc_call_sockopt_cb);
-#endif
 	curl_easy_setopt(curl, CURLOPT_POST, 1);
 
 	if (opt_protocol)
@@ -418,9 +422,19 @@ json_t *json_rpc_call(CURL *curl, const char *url,
 			pool->hdr_path = hi.lp_path;
 		} else
 			pool->hdr_path = NULL;
-	} else if (hi.lp_path) {
-		free(hi.lp_path);
-		hi.lp_path = NULL;
+		if (hi.stratum_url) {
+			pool->stratum_url = hi.stratum_url;
+			hi.stratum_url = NULL;
+		}
+	} else {
+		if (hi.lp_path) {
+			free(hi.lp_path);
+			hi.lp_path = NULL;
+		}
+		if (hi.stratum_url) {
+			free(hi.stratum_url);
+			hi.stratum_url = NULL;
+		}
 	}
 
 	*rolltime = hi.rolltime;
@@ -509,7 +523,7 @@ char *bin2hex(const unsigned char *p, size_t len)
 		slen += 4 - (slen % 4);
 	s = calloc(slen, 1);
 	if (unlikely(!s))
-		return NULL;
+		quit(1, "Failed to calloc in bin2hex");
 
 	for (i = 0; i < len; i++)
 		sprintf(s + (i * 2), "%02x", (unsigned int) p[i]);
@@ -765,6 +779,12 @@ void nmsleep(unsigned int msecs)
 	} while (ret == -1 && errno == EINTR);
 }
 
+/* Returns the microseconds difference between end and start times as a double */
+double us_tdiff(struct timeval *end, struct timeval *start)
+{
+	return end->tv_sec * 1000000 + end->tv_usec - start->tv_sec * 1000000 - start->tv_usec;
+}
+
 void rename_thr(const char* name) {
 #if defined(PR_SET_NAME)
 	// Only the first 15 characters are used (16 - NUL terminator)
@@ -784,3 +804,615 @@ double tdiff(struct timeval *end, struct timeval *start)
 {
 	return end->tv_sec - start->tv_sec + (end->tv_usec - start->tv_usec) / 1000000.0;
 }
+
+bool extract_sockaddr(struct pool *pool, char *url)
+{
+	char *url_begin, *url_end, *port_start = NULL;
+	char url_address[256], port[6];
+	struct addrinfo hints, *res;
+	int url_len, port_len = 0;
+
+	pool->sockaddr_url = url;
+	url_begin = strstr(url, "//");
+	if (!url_begin)
+		url_begin = url;
+	else
+		url_begin += 2;
+	url_end = strstr(url_begin, ":");
+	if (url_end) {
+		url_len = url_end - url_begin;
+		port_len = strlen(url_begin) - url_len - 1;
+		if (port_len < 1)
+			return false;
+		port_start = url_end + 1;
+	} else
+		url_len = strlen(url_begin);
+
+	if (url_len < 1)
+		return false;
+
+	sprintf(url_address, "%.*s", url_len, url_begin);
+
+	if (port_len)
+		snprintf(port, 6, "%.*s", port_len, port_start);
+	else
+		strcpy(port, "80");
+
+	pool->stratum_port = strdup(port);
+
+	memset(&hints, 0, sizeof(struct addrinfo));
+	hints.ai_family = AF_UNSPEC;
+	hints.ai_socktype = SOCK_STREAM;
+	hints.ai_protocol = IPPROTO_TCP;
+
+	if (getaddrinfo(url_address, port, &hints, &res)) {
+		applog(LOG_DEBUG, "Failed to extract sock addr");
+		return false;
+	}
+
+	pool->server = (struct sockaddr_in *)res->ai_addr;
+	pool->sockaddr_url = strdup(url_address);
+
+	return true;
+}
+
+/* Send a single command across a socket, appending \n to it. This should all
+ * be done under stratum lock except when first establishing the socket */
+static bool __stratum_send(struct pool *pool, char *s, ssize_t len)
+{
+	SOCKETTYPE sock = pool->sock;
+	ssize_t ssent = 0;
+
+	if (opt_protocol)
+		applog(LOG_DEBUG, "SEND: %s", s);
+
+	strcat(s, "\n");
+	len++;
+
+	while (len > 0 ) {
+		struct timeval timeout = {0, 0};
+		CURLcode rc = CURLE_SEND_ERROR;
+		size_t sent = 0;
+		fd_set wd;
+
+		FD_ZERO(&wd);
+		FD_SET(sock, &wd);
+		if (select(sock + 1, NULL, &wd, NULL, &timeout) < 1) {
+			applog(LOG_DEBUG, "Write select failed on pool %d sock", pool->pool_no);
+			return false;
+		}
+		if (likely(pool->stratum_curl))
+			rc = curl_easy_send(pool->stratum_curl, s + ssent, len, &sent);
+		if (rc != CURLE_OK) {
+			applog(LOG_DEBUG, "Failed to curl_easy_send in stratum_send");
+			return false;
+		}
+		ssent += sent;
+		len -= ssent;
+	}
+
+	return true;
+}
+
+bool stratum_send(struct pool *pool, char *s, ssize_t len)
+{
+	bool ret = false;
+
+	mutex_lock(&pool->stratum_lock);
+	if (pool->stratum_active)
+		ret = __stratum_send(pool, s, len);
+	else
+		applog(LOG_DEBUG, "Stratum send failed due to no pool stratum_active");
+	mutex_unlock(&pool->stratum_lock);
+
+	return ret;
+}
+
+static void clear_sock(struct pool *pool)
+{
+	size_t n = 0;
+
+	mutex_lock(&pool->stratum_lock);
+	/* Ignore return code of curl_easy_recv since we're just clearing
+	 * anything in the socket if it's still alive */
+	if (likely(pool->stratum_curl))
+		curl_easy_recv(pool->stratum_curl, pool->sockbuf, RECVSIZE, &n);
+	mutex_unlock(&pool->stratum_lock);
+	strcpy(pool->sockbuf, "");
+}
+
+/* Check to see if Santa's been good to you */
+static bool sock_full(struct pool *pool, bool wait)
+{
+	SOCKETTYPE sock = pool->sock;
+	struct timeval timeout;
+	fd_set rd;
+
+	if (strlen(pool->sockbuf))
+		return true;
+
+	FD_ZERO(&rd);
+	FD_SET(sock, &rd);
+	timeout.tv_usec = 0;
+	if (wait)
+		timeout.tv_sec = 60;
+	else
+		timeout.tv_sec = 0;
+	if (select(sock + 1, &rd, NULL, NULL, &timeout) > 0)
+		return true;
+	return false;
+}
+
+/* Peeks at a socket to find the first end of line and then reads just that
+ * from the socket and returns that as a malloced char */
+char *recv_line(struct pool *pool)
+{
+	ssize_t len, buflen;
+	char *tok, *sret = NULL;
+	size_t n = 0;
+
+	if (!strstr(pool->sockbuf, "\n")) {
+		CURLcode rc = CURLE_RECV_ERROR;
+		char s[RBUFSIZE];
+		size_t sspace;
+
+		if (!sock_full(pool, true)) {
+			applog(LOG_DEBUG, "Timed out waiting for data on sock_full");
+			goto out;
+		}
+		memset(s, 0, RBUFSIZE);
+
+		mutex_lock(&pool->stratum_lock);
+		if (likely(pool->stratum_curl))
+			rc = curl_easy_recv(pool->stratum_curl, s, RECVSIZE, &n);
+		mutex_unlock(&pool->stratum_lock);
+
+		if (rc != CURLE_OK) {
+			applog(LOG_DEBUG, "Failed to recv sock in recv_line");
+			goto out;
+		}
+		/* Prevent buffer overflows, but if 8k is still not enough,
+		 * likely we have had some comms issues and the data is all
+		 * useless anyway */
+		sspace = RECVSIZE - strlen(pool->sockbuf);
+		strncat(pool->sockbuf, s, sspace);
+	}
+
+	buflen = strlen(pool->sockbuf);
+	tok = strtok(pool->sockbuf, "\n");
+	if (!tok) {
+		applog(LOG_DEBUG, "Failed to parse a \\n terminated string in recv_line");
+		goto out;
+	}
+	sret = strdup(tok);
+	len = strlen(sret);
+
+	/* Copy what's left in the buffer after the \n, including the
+	 * terminating \0 */
+	if (buflen > len + 1)
+		memmove(pool->sockbuf, pool->sockbuf + len + 1, buflen - len + 1);
+	else
+		strcpy(pool->sockbuf, "");
+out:
+	if (!sret)
+		clear_sock(pool);
+	else if (opt_protocol)
+		applog(LOG_DEBUG, "RECVD: %s", sret);
+	return sret;
+}
+
+/* Extracts a string value from a json array with error checking. To be used
+ * when the value of the string returned is only examined and not to be stored.
+ * See json_array_string below */
+static char *__json_array_string(json_t *val, unsigned int entry)
+{
+	json_t *arr_entry;
+
+	if (json_is_null(val))
+		return NULL;
+	if (!json_is_array(val))
+		return NULL;
+	if (entry > json_array_size(val))
+		return NULL;
+	arr_entry = json_array_get(val, entry);
+	if (!json_is_string(arr_entry))
+		return NULL;
+
+	return (char *)json_string_value(arr_entry);
+}
+
+/* Creates a freshly malloced dup of __json_array_string */
+static char *json_array_string(json_t *val, unsigned int entry)
+{
+	char *buf = __json_array_string(val, entry);
+
+	if (buf)
+		return strdup(buf);
+	return NULL;
+}
+
+static bool parse_notify(struct pool *pool, json_t *val)
+{
+	char *job_id, *prev_hash, *coinbase1, *coinbase2, *bbversion, *nbit, *ntime;
+	int merkles, i;
+	json_t *arr;
+	bool clean;
+
+	arr = json_array_get(val, 4);
+	if (!arr || !json_is_array(arr))
+		return false;
+
+	merkles = json_array_size(arr);
+
+	job_id = json_array_string(val, 0);
+	prev_hash = json_array_string(val, 1);
+	coinbase1 = json_array_string(val, 2);
+	coinbase2 = json_array_string(val, 3);
+	bbversion = json_array_string(val, 5);
+	nbit = json_array_string(val, 6);
+	ntime = json_array_string(val, 7);
+	clean = json_is_true(json_array_get(val, 8));
+
+	if (!job_id || !prev_hash || !coinbase1 || !coinbase2 || !bbversion || !nbit || !ntime) {
+		/* Annoying but we must not leak memory */
+		if (job_id)
+			free(job_id);
+		if (prev_hash)
+			free(prev_hash);
+		if (coinbase1)
+			free(coinbase1);
+		if (coinbase2)
+			free(coinbase2);
+		if (bbversion)
+			free(bbversion);
+		if (nbit)
+			free(nbit);
+		if (ntime)
+			free(ntime);
+		return false;
+	}
+
+	mutex_lock(&pool->pool_lock);
+	pool->swork.job_id = job_id;
+	pool->swork.prev_hash = prev_hash;
+	pool->swork.coinbase1 = coinbase1;
+	pool->swork.coinbase2 = coinbase2;
+	pool->swork.bbversion = bbversion;
+	pool->swork.nbit = nbit;
+	pool->swork.ntime = ntime;
+	pool->swork.clean = clean;
+	for (i = 0; i < pool->swork.merkles; i++)
+		free(pool->swork.merkle[i]);
+	if (merkles) {
+		pool->swork.merkle = realloc(pool->swork.merkle, sizeof(char *) * merkles + 1);
+		for (i = 0; i < merkles; i++)
+			pool->swork.merkle[i] = json_array_string(arr, i);
+	}
+	pool->swork.merkles = merkles;
+	if (clean)
+		pool->nonce2 = 0;
+	mutex_unlock(&pool->pool_lock);
+
+	if (opt_protocol) {
+		applog(LOG_DEBUG, "job_id: %s", job_id);
+		applog(LOG_DEBUG, "prev_hash: %s", prev_hash);
+		applog(LOG_DEBUG, "coinbase1: %s", coinbase1);
+		applog(LOG_DEBUG, "coinbase2: %s", coinbase2);
+		for (i = 0; i < merkles; i++)
+			applog(LOG_DEBUG, "merkle%d: %s", i, pool->swork.merkle[i]);
+		applog(LOG_DEBUG, "bbversion: %s", bbversion);
+		applog(LOG_DEBUG, "nbit: %s", nbit);
+		applog(LOG_DEBUG, "ntime: %s", ntime);
+		applog(LOG_DEBUG, "clean: %s", clean ? "yes" : "no");
+	}
+
+	/* A notify message is the closest stratum gets to a getwork */
+	pool->getwork_requested++;
+	total_getworks++;
+	return true;
+}
+
+static bool parse_diff(struct pool *pool, json_t *val)
+{
+	int diff;
+
+	diff = json_integer_value(json_array_get(val, 0));
+	if (diff < 1)
+		return false;
+
+	mutex_lock(&pool->pool_lock);
+	pool->swork.diff = diff;
+	mutex_unlock(&pool->pool_lock);
+
+	applog(LOG_DEBUG, "Pool %d difficulty set to %d", pool->pool_no, diff);
+
+	return true;
+}
+
+static bool parse_reconnect(struct pool *pool, json_t *val)
+{
+	char *url, *port, address[256];
+
+	memset(address, 0, 255);
+	url = (char *)json_string_value(json_array_get(val, 0));
+	if (!url)
+		url = pool->sockaddr_url;
+
+	port = (char *)json_string_value(json_array_get(val, 1));
+	if (!port)
+		port = pool->stratum_port;
+
+	sprintf(address, "%s:%s", url, port);
+
+	if (!extract_sockaddr(pool, address))
+		return false;
+
+	pool->stratum_url = pool->sockaddr_url;
+
+	applog(LOG_NOTICE, "Reconnect requested from pool %d to %s", pool->pool_no, address);
+
+	if (!initiate_stratum(pool) || !auth_stratum(pool))
+		return false;
+
+	return true;
+}
+
+static bool send_version(struct pool *pool, json_t *val)
+{
+	char s[RBUFSIZE];
+	int id = json_integer_value(json_object_get(val, "id"));
+	
+	if (!id)
+		return false;
+
+	sprintf(s, "{\"id\": %d, \"result\": \""PACKAGE"/"VERSION"\", \"error\": null}", id);
+	if (!stratum_send(pool, s, strlen(s)))
+		return false;
+
+	return true;
+}
+
+bool parse_method(struct pool *pool, char *s)
+{
+	json_t *val = NULL, *method, *err_val, *params;
+	json_error_t err;
+	bool ret = false;
+	char *buf;
+
+	if (!s)
+		goto out;
+
+	val = JSON_LOADS(s, &err);
+	if (!val) {
+		applog(LOG_INFO, "JSON decode failed(%d): %s", err.line, err.text);
+		goto out;
+	}
+
+	method = json_object_get(val, "method");
+	if (!method)
+		goto out;
+	err_val = json_object_get(val, "error");
+	params = json_object_get(val, "params");
+
+	if (err_val && !json_is_null(err_val)) {
+		char *ss;
+
+		if (err_val)
+			ss = json_dumps(err_val, JSON_INDENT(3));
+		else
+			ss = strdup("(unknown reason)");
+
+		applog(LOG_INFO, "JSON-RPC method decode failed: %s", ss);
+
+		free(ss);
+
+		goto out;
+	}
+
+	buf = (char *)json_string_value(method);
+	if (!buf)
+		goto out;
+
+	if (!strncasecmp(buf, "mining.notify", 13) && parse_notify(pool, params)) {
+		ret = true;
+		goto out;
+	}
+
+	if (!strncasecmp(buf, "mining.set_difficulty", 21) && parse_diff(pool, params)) {
+		ret = true;
+		goto out;
+	}
+
+	if (!strncasecmp(buf, "client.reconnect", 16) && parse_reconnect(pool, params)) {
+		ret = true;
+		goto out;
+	}
+
+	if (!strncasecmp(buf, "client.get_version", 18) && send_version(pool, val)) {
+		ret = true;
+		goto out;
+	}
+out:
+	if (val)
+		json_decref(val);
+
+	return ret;
+}
+
+bool auth_stratum(struct pool *pool)
+{
+	json_t *val = NULL, *res_val, *err_val;
+	char s[RBUFSIZE], *sret = NULL;
+	json_error_t err;
+	bool ret = false;
+
+	sprintf(s, "{\"id\": %d, \"method\": \"mining.authorize\", \"params\": [\"%s\", \"%s\"]}",
+		swork_id++, pool->rpc_user, pool->rpc_pass);
+
+	/* Parse all data prior sending auth request */
+	while (sock_full(pool, false)) {
+		sret = recv_line(pool);
+		if (!parse_method(pool, sret)) {
+			clear_sock(pool);
+			applog(LOG_INFO, "Failed to parse stratum buffer");
+			free(sret);
+			return ret;
+		}
+		free(sret);
+	}
+
+	if (!stratum_send(pool, s, strlen(s)))
+		goto out;
+
+	sret = recv_line(pool);
+	if (!sret)
+		goto out;
+	val = JSON_LOADS(sret, &err);
+	free(sret);
+	res_val = json_object_get(val, "result");
+	err_val = json_object_get(val, "error");
+
+	if (!res_val || json_is_false(res_val) || (err_val && !json_is_null(err_val)))  {
+		char *ss;
+
+		if (err_val)
+			ss = json_dumps(err_val, JSON_INDENT(3));
+		else
+			ss = strdup("(unknown reason)");
+		applog(LOG_WARNING, "JSON stratum auth failed: %s", ss);
+		free(ss);
+
+		goto out;
+	}
+	ret = true;
+	applog(LOG_INFO, "Stratum authorisation success for pool %d", pool->pool_no);
+out:
+	if (val)
+		json_decref(val);
+
+	return ret;
+}
+
+bool initiate_stratum(struct pool *pool)
+{
+	json_t *val = NULL, *res_val, *err_val;
+	char curl_err_str[CURL_ERROR_SIZE];
+	char s[RBUFSIZE], *sret = NULL;
+	CURL *curl = NULL;
+	json_error_t err;
+	bool ret = false;
+
+	mutex_lock(&pool->stratum_lock);
+	pool->stratum_active = false;
+
+	if (!pool->stratum_curl) {
+		pool->stratum_curl = curl_easy_init();
+		if (unlikely(!pool->stratum_curl))
+			quit(1, "Failed to curl_easy_init in initiate_stratum");
+	}
+	mutex_unlock(&pool->stratum_lock);
+	curl = pool->stratum_curl;
+
+	/* Create a http url for use with curl */
+	memset(s, 0, RBUFSIZE);
+	sprintf(s, "http://%s:%s", pool->sockaddr_url, pool->stratum_port);
+
+	curl_easy_setopt(curl, CURLOPT_CONNECTTIMEOUT, 60);
+	curl_easy_setopt(curl, CURLOPT_ERRORBUFFER, curl_err_str);
+	curl_easy_setopt(curl, CURLOPT_NOSIGNAL, 1);
+	curl_easy_setopt(curl, CURLOPT_URL, s);
+	curl_easy_setopt(curl, CURLOPT_TCP_NODELAY, 1);
+	curl_easy_setopt(curl, CURLOPT_USE_SSL, CURLUSESSL_TRY);
+	if (pool->rpc_proxy) {
+		curl_easy_setopt(curl, CURLOPT_PROXY, pool->rpc_proxy);
+	} else if (opt_socks_proxy) {
+		curl_easy_setopt(curl, CURLOPT_PROXY, opt_socks_proxy);
+		curl_easy_setopt(curl, CURLOPT_PROXYTYPE, CURLPROXY_SOCKS4);
+	}
+	curl_easy_setopt(curl, CURLOPT_CONNECT_ONLY, 1);
+	if (curl_easy_perform(curl)) {
+		applog(LOG_INFO, "Stratum connect failed to pool %d: %s", pool->pool_no, curl_err_str);
+		goto out;
+	}
+	curl_easy_getinfo(curl, CURLINFO_LASTSOCKET, (long *)&pool->sock);
+	keep_sockalive(pool->sock);
+
+	sprintf(s, "{\"id\": %d, \"method\": \"mining.subscribe\", \"params\": []}", swork_id++);
+
+	if (!__stratum_send(pool, s, strlen(s))) {
+		applog(LOG_DEBUG, "Failed to send s in initiate_stratum");
+		goto out;
+	}
+
+	if (!sock_full(pool, true)) {
+		applog(LOG_DEBUG, "Timed out waiting for response in initiate_stratum");
+		goto out;
+	}
+
+	sret = recv_line(pool);
+	if (!sret)
+		goto out;
+
+	val = JSON_LOADS(sret, &err);
+	free(sret);
+	if (!val) {
+		applog(LOG_INFO, "JSON decode failed(%d): %s", err.line, err.text);
+		goto out;
+	}
+
+	res_val = json_object_get(val, "result");
+	err_val = json_object_get(val, "error");
+
+	if (!res_val || json_is_null(res_val) ||
+	    (err_val && !json_is_null(err_val))) {
+		char *ss;
+
+		if (err_val)
+			ss = json_dumps(err_val, JSON_INDENT(3));
+		else
+			ss = strdup("(unknown reason)");
+
+		applog(LOG_INFO, "JSON-RPC decode failed: %s", ss);
+
+		free(ss);
+
+		goto out;
+	}
+
+	pool->nonce1 = json_array_string(res_val, 1);
+	if (!pool->nonce1) {
+		applog(LOG_INFO, "Failed to get nonce1 in initiate_stratum");
+		goto out;
+	}
+	pool->n2size = json_integer_value(json_array_get(res_val, 2));
+	if (!pool->n2size) {
+		applog(LOG_INFO, "Failed to get n2size in initiate_stratum");
+		goto out;
+	}
+
+	ret = true;
+out:
+	if (val)
+		json_decref(val);
+
+	if (ret) {
+		if (!pool->stratum_url)
+			pool->stratum_url = pool->sockaddr_url;
+		pool->stratum_active = true;
+		pool->swork.diff = 1;
+		if (opt_protocol) {
+			applog(LOG_DEBUG, "Pool %d confirmed mining.subscribe with extranonce1 %s extran2size %d",
+			       pool->pool_no, pool->nonce1, pool->n2size);
+		}
+	} else {
+		applog(LOG_DEBUG, "Initiate stratum failed, disabling stratum_active");
+		mutex_lock(&pool->stratum_lock);
+		if (curl) {
+			curl_easy_cleanup(curl);
+			pool->stratum_curl = NULL;
+		}
+		mutex_unlock(&pool->stratum_lock);
+	}
+
+	return ret;
+}

+ 53 - 0
util.h

@@ -0,0 +1,53 @@
+#ifndef __UTIL_H__
+#define __UTIL_H__
+
+#if defined(unix) || defined(__APPLE__)
+	#include <errno.h>
+	#include <sys/socket.h>
+	#include <netinet/in.h>
+	#include <arpa/inet.h>
+
+	#define SOCKETTYPE long
+	#define SOCKETFAIL(a) ((a) < 0)
+	#define INVSOCK -1
+	#define INVINETADDR -1
+	#define CLOSESOCKET close
+
+	#define SOCKERRMSG strerror(errno)
+#elif defined WIN32
+	#include <ws2tcpip.h>
+	#include <winsock2.h>
+
+	#define SOCKETTYPE SOCKET
+	#define SOCKETFAIL(a) ((int)(a) == SOCKET_ERROR)
+	#define INVSOCK INVALID_SOCKET
+	#define INVINETADDR INADDR_NONE
+	#define CLOSESOCKET closesocket
+
+	extern char *WSAErrorMsg(void);
+	#define SOCKERRMSG WSAErrorMsg()
+
+	#ifndef SHUT_RDWR
+	#define SHUT_RDWR SD_BOTH
+	#endif
+
+	#ifndef in_addr_t
+	#define in_addr_t uint32_t
+	#endif
+#endif
+
+#if JANSSON_MAJOR_VERSION >= 2
+#define JSON_LOADS(str, err_ptr) json_loads((str), 0, (err_ptr))
+#else
+#define JSON_LOADS(str, err_ptr) json_loads((str), (err_ptr))
+#endif
+
+struct pool;
+bool stratum_send(struct pool *pool, char *s, ssize_t len);
+char *recv_line(struct pool *pool);
+bool parse_method(struct pool *pool, char *s);
+bool extract_sockaddr(struct pool *pool, char *url);
+bool auth_stratum(struct pool *pool);
+bool initiate_stratum(struct pool *pool);
+
+#endif /* __UTIL_H__ */

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