Browse Source

Merge branch 'multiproc' into bfgminer

Conflicts:
	miner.c
	util.h
Luke Dashjr 13 years ago
parent
commit
c5f6b52050
24 changed files with 2193 additions and 1022 deletions
  1. 124 0
      HACKING
  2. 1 0
      Makefile.am
  3. 6 5
      README
  4. 25 8
      README.RPC
  5. 28 46
      api.c
  6. 451 0
      deviceapi.c
  7. 32 0
      deviceapi.h
  8. 526 190
      driver-bitforce.c
  9. 6 8
      driver-cairnsmore.c
  10. 1 2
      driver-cpu.c
  11. 34 36
      driver-icarus.c
  12. 148 129
      driver-modminer.c
  13. 19 50
      driver-opencl.c
  14. 218 126
      driver-x6500.c
  15. 27 18
      driver-ztex.c
  16. 3 3
      dynclock.c
  17. 2 2
      findnonce.c
  18. 28 28
      fpgautils.c
  19. 1 1
      fpgautils.h
  20. 358 346
      miner.c
  21. 60 5
      miner.h
  22. 25 19
      miner.php
  23. 14 0
      util.c
  24. 56 0
      util.h

+ 124 - 0
HACKING

@@ -0,0 +1,124 @@
+Driver API
+==========
+
+NOTE: This API is subject to change. It is recommended that you submit your
+driver, even if obscure, to the mainline BFGMiner codebase so that it will be
+updated when the API changes.
+
+BFGMiner defines 3 different units that drivers can use:
+- "Device" is a logical unit used for mining. It is represented by its first
+  processor's `struct cgpu_info`. Example: ButterFly Labs MiniRig SC.
+- "Processor" is a logical work processing unit. It is represented by a `struct
+  cgpu_info` and one or more `struct thr_info`. Example: a single board within
+  ButterFly Labs MiniRig SC.
+- "Thread" is a sequence of instructions and stack that manages hashing on one
+  or more Processors within a single Device. It is represented by a `struct
+  thr_info`.
+
+It should be noted that while every Processor has a `struct thr_info`, this may
+not represent the same Thread which is managing hashing on the Processor.
+Instead, this `struct thr_info` is only used to store status information needed
+for the Processor, and is maintained by the managing Thread in addition to its
+own `struct thr_info`. New drivers are encouraged to use an asynchronous model
+to manage as many Processors as possible within a single Thread.
+
+struct device_api basics
+------------------------
+
+Every driver defines a `struct device_api`. The `dname` field contains a
+short name of the driver. This should be the same name used in the source file:
+driver-foobar.c defines `dname` "foobar". The `name` field contains a three-
+letter abbreviation for the device, used in the representation of devices. For
+example, `dname` "FOO" would result in devices represented as "FOO 0", "FOO 1",
+etc and processors represented as "FOO 0a", "FOO 0b", etc.
+
+Drivers must define a function `api_detect`, which is run at startup to detect
+devices. For each device (note: NOT each processor), it should allocate a
+`struct cgpu_info`, set some basic parameters on it, and call the `add_cgpu`
+function with it as an argument. Various values you can initialize are:
+	.api         This MUST be set to your driver's `struct driver_api`!
+	.deven       Should be set to DEV_ENABLED
+	.procs       Number of Processors for this device
+	.threads     Number of threads your device needs - should be either a
+	             multiple of .procs (threads will be allocated to each
+	             Processor), or one (a single thread will be allocated only to
+	             the Device, to manage all Processors)
+	.name        Null-terminated name of the device itself
+`api_detect` should return the total number of devices created. It should leave
+the device in an unused state, as the user may opt to delete it outright.
+
+Threads
+-------
+
+The first interaction BFGMiner will have with a device is by calling the
+driver's `thread_prepare` function for each Thread. This occurs while BFGMiner
+is still in a single-threaded state, before any Threads have actually started
+running independently. It should do only the minimal initialization necessary
+to proceed, and return true iff successful.
+
+Once all the Threads are setup, BFGMiner starts them off by calling the
+`thread_init` function. This should do all initialization that can occur in
+parallel with other Threads.
+
+The driver should specify a `minerloop` to use. For the purposes of this
+document, it is assumed you will be using `minerloop_async`. Please note that
+the default is currently `minerloop_scanhash`, and much of the documentation
+here will NOT work with this `minerloop`.
+
+Processors
+----------
+
+Processors work with `struct work` objects, which each represent a block header
+to find a solution for. Before your driver sees a `struct work`, it will be
+passed to the function `prepare_work` with pointers to the Processor `struct
+thr_info` and the `struct work` as arguments. Most drivers do not need to do
+anything at this stage, so feel free to omit the `prepare_work` function.
+
+For each job, the `job_prepare` function is called in advance, with three
+arguments: Processor `struct thr_info *`, `struct work *`, and a `uint64_t`
+limiting how many nonces to check (starting from `work->blk.nonce`). Unless you
+implement a `can_limit_work` function, you will always receive a full nonce
+range from 0 to 0xffffffff. `job_prepare` increments `work->blk.nonce` to the
+last nonce the processor will be attempting and returns true when successful.
+Please note this will be called while the previous job is still executing.
+
+When it is time to actually start the new job, the `job_start` function will be
+called. This is given the Processor `struct thr_info *` as its only argument,
+and should start the job most recently prepared with `job_prepare`. Note that
+it is possible for `job_prepare` to be called for a job that never starts
+(another `job_prepare` may be executed to override the previous one instead).
+`job_start` must call `mt_job_transition` as soon as the actual switchover to
+the new job takes place, and must call `job_start_complete` when successful;
+in case of a failure, it should call `job_start_abort` instead. `job_start`
+must set `thr->tv_morework` to the time the device expects to need its next
+work item. It is generally advisable to set this a bit early to ensure any
+delays do not make it late. `job_start` is expected to always succeed and does
+not have a return value.
+
+Immediately before `job_start` is called to change from one job to the next,
+`job_get_results` will be called to fetch any volatile results from the
+previous job. It is provided the Processor's `struct thr_info *` and the
+currently executing job's `struct work *`. It should ONLY fetch the raw data
+for the results, and not spend any time processing or submitting it. If
+`job_get_results` is defined for your driver, it must (directly or indirectly)
+ensure `job_results_fetched` is called when complete (including the case of
+failure). After the new job has been started, your driver's
+`job_process_results` function will be called to complete the submission of
+these results with the same arguments, plus a bool to tell you whether the
+processor is being stopped. If it is, your driver must call `mt_disable_start`
+when it has successfully stopped hashing.
+
+Drivers may define a `poll` function. If this is defined, `thr->tv_poll` must
+always be set to a valid time to next execute it, for each Processor.
+
+Whenever a solution is found (at any point), the function `submit_nonce` should
+be called, passing the Processor `struct thr_info *`, `struct work *`, and
+nonce as arguments. If the solution is invalid (any of the final 32 bits of the
+hash are nonzero), it will be recorded as a hardware error and your driver's
+`hw_error` function (if one is defined) will be called.
+
+As often as results are processed, your driver should call the `hashes_done`
+function with a number of arguments: Processor `struct thr_info *`, count of
+hashes completed (including calls to `submit_nonce`), a `struct timeval *`
+that tells how long it took to find these hashes (usually time since the last
+call to `hashes_done`, and a `uint32_t *` which should usually be NULL.

+ 1 - 0
Makefile.am

@@ -27,6 +27,7 @@ bfgminer_CPPFLAGS += $(NCURSES_CPPFLAGS)
 bfgminer_SOURCES := miner.c
 
 bfgminer_SOURCES	+= elist.h miner.h compat.h bench_block.h	\
+	deviceapi.c deviceapi.h \
 		   util.c util.h uthash.h logging.h		\
 		   sha2.c sha2.h api.c
 bfgminer_DEPENDENCIES =

+ 6 - 5
README

@@ -171,6 +171,7 @@ Options for both config file and command line:
 --scrypt            Use the scrypt algorithm for mining (non-bitcoin)
 --sharelog <arg>    Append share log to file
 --shares <arg>      Quit after mining N shares (default: unlimited)
+--show-processors   Show per processor statistics in summary
 --skip-security-checks <arg> Skip security checks sometimes to save bandwidth; only check 1/<arg>th of the time (default: never skip)
 --socks-proxy <arg> Set socks4 proxy (host:port) for all pools without a proxy specified
 --submit-threads    Minimum number of concurrent share submissions (default: 64)
@@ -401,12 +402,12 @@ 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
+ [2013-02-13 00:26:30] Accepted 1758e8df BFL 0  pool 0 Diff 10/1
+ [2013-02-13 00:26:32] Accepted 1d9a2199 MMQ 0a pool 0 Diff 8/1
+ [2013-02-13 00:26:33] Accepted b1304924 ZTX 0  pool 0 Diff 1/1
+ [2013-02-13 00:26:33] Accepted c3ad22f4 XBS 0b pool 0 Diff 1/1
 
-The 8 byte hex value are the 2nd 8 bytes of the share being submitted to the
+The 8 byte hex value are the 2nd set of 32 bits from the share 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.
 

+ 25 - 8
README.RPC

@@ -244,7 +244,7 @@ The list of requests - a (*) means it requires privileged access - and replies a
                               reply before BFGMiner quits
 
  notify        NOTIFY         The last status and history count of each devices problem
-                              e.g. NOTIFY=0,Name=GPU,ID=0,Last Well=1332432290,...|
+                              e.g. NOTIFY=0,Name=PGA,ID=0,ProcID=0,Last Well=1332432290,...|
 
  privileged (*)
                none           There is no reply section just the STATUS section
@@ -282,7 +282,7 @@ The list of requests - a (*) means it requires privileged access - and replies a
  devdetails    DEVDETAILS     Each device with a list of their static details
                               This lists all devices including those not supported
                               by the 'devs' command
-                              e.g. DEVDETAILS=0,Name=GPU,ID=0,Driver=opencl,...|
+                              e.g. DEVDETAILS=0,Name=PGA,ID=0,ProcID=0,Driver=bitforce,...|
 
  restart (*)   none           There is no status section but just a single "RESTART"
                               reply before BFGMiner restarts
@@ -408,6 +408,22 @@ miner.php - an example web page to access the API
 Feature Changelog for external applications using the API:
 
 
+API V1.24b
+
+Modified API commands:
+ 'cpustatus' - Added 'ProcID'
+ 'gpustatus' - Added 'ProcID'
+ 'pgastatus' - Added 'ProcID'
+ 'devstatus' - Added 'ProcID'
+ 'notify' - Added 'ProcID'
+ 'devdetails' - Added 'ProcID'
+ 'devdetail' - Added 'Name', 'ID', and 'ProcID'
+
+Pretty much updated every method returning 'Name' and 'ID' to also return
+'ProcID'. This is a number starting with 0 for 'a', 1 for 'b', etc.
+
+----------
+
 API V1.24 (BFGMiner v2.10.3)
 
 Added API commands:
@@ -1239,7 +1255,8 @@ Looking at the Mobile example:
   'RIGS' => null,
   'SUMMARY' => array('Elapsed', 'MHS av', 'Found Blocks=Blks', 
 			Accepted', 'Rejected=Rej', 'Utility'),
-  'DEVS+NOTIFY' => array('DEVS.Name=Name', 'DEVS.ID=ID', 'DEVS.Status=Status',
+  'DEVS+NOTIFY' => array('DEVS.Name=Name', 'DEVS.ID=ID', 'DEVS.ProcID=Proc',
+			'DEVS.Status=Status',
 			'DEVS.Temperature=Temp', 'DEVS.MHS av=MHS av',
 			'DEVS.Accepted=Accept', 'DEVS.Rejected=Rej',
 			'DEVS.Utility=Utility', 'NOTIFY.Last Not Well=Not Well'),
@@ -1280,11 +1297,11 @@ Each table will have the chosen details for all the rigs specified in $rigs
 	since each field name in the join between DEVS and NOTIFY is actually
 	section.fieldname, not just fieldname
 
-	The join code automatically adds 2 fields to each GPU device: 'Name' and 'ID'
-	They don't exist in the API 'devs' output but I can correctly calculate
-	them from the GPU device data
-	These two fields are used to join DEVS to NOTIFY i.e. find the NOTIFY
-	record that has the same Name and ID as the DEVS record and join them
+	The join code automatically adds 2 fields to each GPU device: 'Name', 'ID',
+	and 'ProcID'. They don't exist in the API 'devs' output but we can correctly
+	calculate them from the GPU device data. These two fields are used to join
+	DEVS to NOTIFY: i.e. find the NOTIFY record that has the same Name/ID/ProcID
+	as the DEVS record and join them.
 
  POOL
 

+ 28 - 46
api.c

@@ -573,9 +573,6 @@ struct CODES {
 
 static int my_thr_id = 0;
 static bool bye;
-#if defined(HAVE_OPENCL) || defined(HAVE_AN_FPGA)
-static bool ping = true;
-#endif
 
 // Used to control quit restart access to shutdown variables
 static pthread_mutex_t quit_restart_lock;
@@ -1460,6 +1457,15 @@ static const char *status2str(enum alive status)
 	}
 }
 
+static
+struct api_data *api_add_device_identifier(struct api_data *root, struct cgpu_info *cgpu)
+{
+	root = api_add_string(root, "Name", cgpu->api->name, false);
+	root = api_add_int(root, "ID", &(cgpu->device_id), false);
+	root = api_add_int(root, "ProcID", &(cgpu->proc_id), false);
+	return root;
+}
+
 static void devdetail_an(struct io_data *io_data, struct cgpu_info *cgpu, bool isjson, bool precom)
 {
 	struct api_data *root = NULL;
@@ -1476,6 +1482,7 @@ static void devdetail_an(struct io_data *io_data, struct cgpu_info *cgpu, bool i
 	}
 
 	root = api_add_int(root, (char*)cgpu->devtype, &n, true);
+	root = api_add_device_identifier(root, cgpu);
 	root = api_add_string(root, "Driver", cgpu->api->dname, false);
 	if (cgpu->kname)
 		root = api_add_string(root, "Kernel", cgpu->kname, false);
@@ -1507,8 +1514,7 @@ static void devstatus_an(struct io_data *io_data, struct cgpu_info *cgpu, bool i
 	}
 
 	root = api_add_int(root, (char*)cgpu->devtype, &n, true);
-	root = api_add_string(root, "Name", cgpu->api->name, false);
-	root = api_add_int(root, "ID", &(cgpu->device_id), false);
+	root = api_add_device_identifier(root, cgpu);
 	root = api_add_string(root, "Enabled", bool2str(cgpu->deven != DEV_DISABLED), false);
 	root = api_add_string(root, "Status", status2str(cgpu->status), false);
 	if (cgpu->temp)
@@ -1672,10 +1678,7 @@ static void pgadev(struct io_data *io_data, __maybe_unused SOCKETTYPE c, char *p
 static void pgaenable(struct io_data *io_data, __maybe_unused SOCKETTYPE c, char *param, bool isjson, __maybe_unused char group)
 {
 	int numpga = numpgas();
-	struct thr_info *thr;
-	int pga;
 	int id;
-	int i;
 
 	if (numpga == 0) {
 		message(io_data, MSG_PGANON, 0, NULL, isjson);
@@ -1701,8 +1704,8 @@ static void pgaenable(struct io_data *io_data, __maybe_unused SOCKETTYPE c, char
 
 	struct cgpu_info *cgpu = devices[dev];
 
-	applog(LOG_DEBUG, "API: request to pgaenable pgaid %d device %d %s%u",
-			id, dev, cgpu->api->name, cgpu->device_id);
+	applog(LOG_DEBUG, "API: request to pgaenable pgaid %d device %d %s",
+			id, dev, cgpu->proc_repr_ns);
 
 	if (cgpu->deven != DEV_DISABLED) {
 		message(io_data, MSG_PGALRENA, id, NULL, isjson);
@@ -1716,15 +1719,7 @@ static void pgaenable(struct io_data *io_data, __maybe_unused SOCKETTYPE c, char
 	}
 #endif
 
-	for (i = 0; i < mining_threads; i++) {
-		pga = thr_info[i].cgpu->cgminer_id;
-		if (pga == dev) {
-			thr = &thr_info[i];
-			cgpu->deven = DEV_ENABLED;
-			applog(LOG_DEBUG, "API: pushing ping (%d) to thread %d", ping, thr->id);
-			tq_push(thr->q, &ping);
-		}
-	}
+	proc_enable(cgpu);
 
 	message(io_data, MSG_PGAENA, id, NULL, isjson);
 }
@@ -1758,8 +1753,8 @@ static void pgadisable(struct io_data *io_data, __maybe_unused SOCKETTYPE c, cha
 
 	struct cgpu_info *cgpu = devices[dev];
 
-	applog(LOG_DEBUG, "API: request to pgadisable pgaid %d device %d %s%u",
-			id, dev, cgpu->api->name, cgpu->device_id);
+	applog(LOG_DEBUG, "API: request to pgadisable pgaid %d device %d %s",
+			id, dev, cgpu->proc_repr_ns);
 
 	if (cgpu->deven == DEV_DISABLED) {
 		message(io_data, MSG_PGALRDIS, id, NULL, isjson);
@@ -1988,10 +1983,7 @@ static void summary(struct io_data *io_data, __maybe_unused SOCKETTYPE c, __mayb
 #ifdef HAVE_OPENCL
 static void gpuenable(struct io_data *io_data, __maybe_unused SOCKETTYPE c, char *param, bool isjson, __maybe_unused char group)
 {
-	struct thr_info *thr;
-	int gpu;
 	int id;
-	int i;
 
 	if (gpu_threads == 0) {
 		message(io_data, MSG_GPUNON, 0, NULL, isjson);
@@ -2009,27 +2001,20 @@ static void gpuenable(struct io_data *io_data, __maybe_unused SOCKETTYPE c, char
 		return;
 	}
 
-	applog(LOG_DEBUG, "API: request to gpuenable gpuid %d %s%u",
-			id, gpus[id].api->name, gpus[id].device_id);
+	applog(LOG_DEBUG, "API: request to gpuenable gpuid %d %s",
+			id, gpus[id].proc_repr_ns);
 
 	if (gpus[id].deven != DEV_DISABLED) {
 		message(io_data, MSG_ALRENA, id, NULL, isjson);
 		return;
 	}
 
-	for (i = 0; i < gpu_threads; i++) {
-		gpu = thr_info[i].cgpu->device_id;
-		if (gpu == id) {
-			thr = &thr_info[i];
-			if (thr->cgpu->status != LIFE_WELL) {
-				message(io_data, MSG_GPUMRE, id, NULL, isjson);
-				return;
-			}
-			gpus[id].deven = DEV_ENABLED;
-			applog(LOG_DEBUG, "API: pushing ping (%d) to thread %d", ping, thr->id);
-			tq_push(thr->q, &ping);
-		}
+	if (gpus[id].status != LIFE_WELL)
+	{
+		message(io_data, MSG_GPUMRE, id, NULL, isjson);
+		return;
 	}
+	proc_enable(&gpus[id]);
 
 	message(io_data, MSG_GPUREN, id, NULL, isjson);
 }
@@ -2054,8 +2039,8 @@ static void gpudisable(struct io_data *io_data, __maybe_unused SOCKETTYPE c, cha
 		return;
 	}
 
-	applog(LOG_DEBUG, "API: request to gpudisable gpuid %d %s%u",
-			id, gpus[id].api->name, gpus[id].device_id);
+	applog(LOG_DEBUG, "API: request to gpudisable gpuid %d %s",
+			id, gpus[id].proc_repr_ns);
 
 	if (gpus[id].deven == DEV_DISABLED) {
 		message(io_data, MSG_ALRDIS, id, NULL, isjson);
@@ -2641,8 +2626,7 @@ void notifystatus(struct io_data *io_data, int device, struct cgpu_info *cgpu, b
 	// ALL counters (and only counters) must start the name with a '*'
 	// Simplifies future external support for identifying new counters
 	root = api_add_int(root, "NOTIFY", &device, false);
-	root = api_add_string(root, "Name", cgpu->api->name, false);
-	root = api_add_int(root, "ID", &(cgpu->device_id), false);
+	root = api_add_device_identifier(root, cgpu);
 	root = api_add_time(root, "Last Well", &(cgpu->device_last_well), false);
 	root = api_add_time(root, "Last Not Well", &(cgpu->device_last_not_well), false);
 	root = api_add_string(root, "Reason Not Well", reason, false);
@@ -2705,8 +2689,7 @@ static void devdetails(struct io_data *io_data, __maybe_unused SOCKETTYPE c, __m
 		cgpu = devices[i];
 
 		root = api_add_int(root, "DEVDETAILS", &i, false);
-		root = api_add_string(root, "Name", cgpu->api->name, false);
-		root = api_add_int(root, "ID", &(cgpu->device_id), false);
+		root = api_add_device_identifier(root, cgpu);
 		root = api_add_string(root, "Driver", cgpu->api->dname, false);
 		root = api_add_const(root, "Kernel", cgpu->kname ? : BLANK, false);
 		root = api_add_const(root, "Model", cgpu->name ? : BLANK, false);
@@ -2819,8 +2802,7 @@ static void minerstats(struct io_data *io_data, __maybe_unused SOCKETTYPE c, __m
 			else
 				extra = NULL;
 
-			sprintf(id, "%s%d", cgpu->api->name, cgpu->device_id);
-			i = itemstats(io_data, i, id, &(cgpu->cgminer_stats), NULL, extra, isjson);
+			i = itemstats(io_data, i, cgpu->proc_repr_ns, &(cgpu->cgminer_stats), NULL, extra, isjson);
 		}
 	}
 

+ 451 - 0
deviceapi.c

@@ -0,0 +1,451 @@
+/*
+ * Copyright 2011-2013 Luke Dashjr
+ * Copyright 2011-2012 Con Kolivas
+ * Copyright 2012-2013 Andrew Smith
+ * Copyright 2010 Jeff Garzik
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 3 of the License, or (at your option)
+ * any later version.  See COPYING for more details.
+ */
+
+#include "config.h"
+
+#ifdef WIN32
+#include <winsock2.h>
+#else
+#include <sys/select.h>
+#endif
+#include <stdbool.h>
+#include <stdint.h>
+#include <sys/time.h>
+#include <sys/types.h>
+#include <time.h>
+#include <unistd.h>
+
+#include "compat.h"
+#include "deviceapi.h"
+#include "logging.h"
+#include "miner.h"
+#include "util.h"
+
+bool hashes_done(struct thr_info *thr, int64_t hashes, struct timeval *tvp_hashes, uint32_t *max_nonce)
+{
+	struct cgpu_info *cgpu = thr->cgpu;
+	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.)
+			dev_error(cgpu, REASON_THREAD_ZERO_HASH);
+		
+		if (thr->scanhash_working && opt_restart) {
+			applog(LOG_ERR, "%"PRIpreprv" failure, attempting to reinitialize", cgpu->proc_repr);
+			thr->scanhash_working = false;
+			cgpu->reinit_backoff = 5.2734375;
+			hashes = 0;
+		} else {
+			applog(LOG_ERR, "%"PRIpreprv" failure, disabling!", cgpu->proc_repr);
+			cgpu->deven = DEV_RECOVER_ERR;
+			return false;
+		}
+	}
+	else
+		thr->scanhash_working = true;
+	
+	thr->hashes_done += hashes;
+	if (hashes > cgpu->max_hashes)
+		cgpu->max_hashes = hashes;
+	
+	timeradd(&thr->tv_hashes_done, tvp_hashes, &thr->tv_hashes_done);
+	
+	// max_nonce management (optional)
+	if (unlikely((long)thr->tv_hashes_done.tv_sec < cycle)) {
+		int mult;
+		
+		if (likely(!max_nonce || *max_nonce == 0xffffffff))
+			return true;
+		
+		mult = 1000000 / ((thr->tv_hashes_done.tv_usec + 0x400) / 0x400) + 0x10;
+		mult *= cycle;
+		if (*max_nonce > (0xffffffff * 0x400) / mult)
+			*max_nonce = 0xffffffff;
+		else
+			*max_nonce = (*max_nonce * mult) / 0x400;
+	} else if (unlikely(thr->tv_hashes_done.tv_sec > cycle) && max_nonce)
+		*max_nonce = *max_nonce * cycle / thr->tv_hashes_done.tv_sec;
+	else if (unlikely(thr->tv_hashes_done.tv_usec > 100000) && max_nonce)
+		*max_nonce = *max_nonce * 0x400 / (((cycle * 1000000) + thr->tv_hashes_done.tv_usec) / (cycle * 1000000 / 0x400));
+	
+	hashmeter2(thr);
+	
+	return true;
+}
+
+// Miner loop to manage a single processor (with possibly multiple threads per processor)
+void minerloop_scanhash(struct thr_info *mythr)
+{
+	const int thr_id = mythr->id;
+	struct cgpu_info *cgpu = mythr->cgpu;
+	const struct device_api *api = cgpu->api;
+	struct timeval tv_start, tv_end;
+	struct timeval tv_hashes, tv_worktime;
+	uint32_t max_nonce = api->can_limit_work ? api->can_limit_work(mythr) : 0xffffffff;
+	int64_t hashes;
+	struct work *work;
+	const bool primary = (!mythr->device_thread) || mythr->primary_thread;
+	
+	while (1) {
+		mythr->work_restart = false;
+		request_work(mythr);
+		work = get_work(mythr);
+		if (api->prepare_work && !api->prepare_work(mythr, work)) {
+			applog(LOG_ERR, "work prepare failed, exiting "
+				"mining thread %d", thr_id);
+			break;
+		}
+		gettimeofday(&(work->tv_work_start), NULL);
+		
+		do {
+			thread_reportin(mythr);
+			gettimeofday(&tv_start, NULL);
+			hashes = api->scanhash(mythr, work, work->blk.nonce + max_nonce);
+			gettimeofday(&tv_end, NULL);
+			thread_reportin(mythr);
+			
+			timersub(&tv_end, &tv_start, &tv_hashes);
+			if (!hashes_done(mythr, hashes, &tv_hashes, api->can_limit_work ? &max_nonce : NULL))
+				goto disabled;
+			
+			if (unlikely(mythr->work_restart)) {
+				/* Apart from device_thread 0, we stagger the
+				 * starting of every next thread to try and get
+				 * all devices busy before worrying about
+				 * getting work for their extra threads */
+				if (!primary) {
+					struct timespec rgtp;
+
+					rgtp.tv_sec = 0;
+					rgtp.tv_nsec = 250 * mythr->device_thread * 1000000;
+					nanosleep(&rgtp, NULL);
+				}
+				break;
+			}
+			
+			if (unlikely(mythr->pause || cgpu->deven != DEV_ENABLED))
+disabled:
+				mt_disable(mythr);
+			
+			timersub(&tv_end, &work->tv_work_start, &tv_worktime);
+		} while (!abandon_work(work, &tv_worktime, cgpu->max_hashes));
+		free_work(work);
+	}
+}
+
+bool do_job_prepare(struct thr_info *mythr, struct timeval *tvp_now)
+{
+	struct cgpu_info *proc = mythr->cgpu;
+	const struct device_api *api = proc->api;
+	struct timeval tv_worktime;
+	
+	mythr->tv_morework.tv_sec = -1;
+	mythr->_job_transition_in_progress = true;
+	if (mythr->work)
+		timersub(tvp_now, &mythr->work->tv_work_start, &tv_worktime);
+	if ((!mythr->work) || abandon_work(mythr->work, &tv_worktime, proc->max_hashes))
+	{
+		mythr->work_restart = false;
+		request_work(mythr);
+		// FIXME: Allow get_work to return NULL to retry on notification
+		mythr->next_work = get_work(mythr);
+		if (api->prepare_work && !api->prepare_work(mythr, mythr->next_work)) {
+			applog(LOG_ERR, "%"PRIpreprv": Work prepare failed, disabling!", proc->proc_repr);
+			proc->deven = DEV_RECOVER_ERR;
+			return false;
+		}
+		mythr->starting_next_work = true;
+		api->job_prepare(mythr, mythr->next_work, mythr->_max_nonce);
+	}
+	else
+	{
+		mythr->starting_next_work = false;
+		api->job_prepare(mythr, mythr->work, mythr->_max_nonce);
+	}
+	job_prepare_complete(mythr);
+	return true;
+}
+
+void job_prepare_complete(struct thr_info *mythr)
+{
+	if (mythr->work)
+	{
+		if (true /* TODO: job is near complete */ || unlikely(mythr->work_restart))
+			do_get_results(mythr, true);
+		else
+		{}  // TODO: Set a timer to call do_get_results when job is near complete
+	}
+	else  // no job currently running
+		do_job_start(mythr);
+}
+
+void do_get_results(struct thr_info *mythr, bool proceed_with_new_job)
+{
+	struct cgpu_info *proc = mythr->cgpu;
+	const struct device_api *api = proc->api;
+	struct work *work = mythr->work;
+	
+	mythr->_job_transition_in_progress = true;
+	mythr->tv_results_jobstart = mythr->tv_jobstart;
+	mythr->_proceed_with_new_job = proceed_with_new_job;
+	if (api->job_get_results)
+		api->job_get_results(mythr, work);
+	else
+		job_results_fetched(mythr);
+}
+
+void job_results_fetched(struct thr_info *mythr)
+{
+	if (mythr->_proceed_with_new_job)
+		do_job_start(mythr);
+	else
+	{
+		struct timeval tv_now;
+		
+		gettimeofday(&tv_now, NULL);
+		
+		do_process_results(mythr, &tv_now, mythr->prev_work, true);
+	}
+}
+
+void do_job_start(struct thr_info *mythr)
+{
+	struct cgpu_info *proc = mythr->cgpu;
+	const struct device_api *api = proc->api;
+	
+	thread_reportin(mythr);
+	api->job_start(mythr);
+}
+
+void mt_job_transition(struct thr_info *mythr)
+{
+	struct timeval tv_now;
+	
+	gettimeofday(&tv_now, NULL);
+	
+	if (mythr->starting_next_work)
+	{
+		mythr->next_work->tv_work_start = tv_now;
+		if (mythr->prev_work)
+			free_work(mythr->prev_work);
+		mythr->prev_work = mythr->work;
+		mythr->work = mythr->next_work;
+		mythr->next_work = NULL;
+	}
+	mythr->tv_jobstart = tv_now;
+	mythr->_job_transition_in_progress = false;
+}
+
+void job_start_complete(struct thr_info *mythr)
+{
+	struct timeval tv_now;
+	
+	gettimeofday(&tv_now, NULL);
+	
+	do_process_results(mythr, &tv_now, mythr->prev_work, false);
+}
+
+void job_start_abort(struct thr_info *mythr, bool failure)
+{
+	struct cgpu_info *proc = mythr->cgpu;
+	
+	if (failure)
+		proc->deven = DEV_RECOVER_ERR;
+	mythr->work = NULL;
+	mythr->_job_transition_in_progress = false;
+}
+
+bool do_process_results(struct thr_info *mythr, struct timeval *tvp_now, struct work *work, bool stopping)
+{
+	struct cgpu_info *proc = mythr->cgpu;
+	const struct device_api *api = proc->api;
+	struct timeval tv_hashes;
+	int64_t hashes = 0;
+	
+	if (api->job_process_results)
+		hashes = api->job_process_results(mythr, work, stopping);
+	thread_reportin(mythr);
+	
+	if (hashes)
+	{
+		timersub(tvp_now, &mythr->tv_results_jobstart, &tv_hashes);
+		if (!hashes_done(mythr, hashes, &tv_hashes, api->can_limit_work ? &mythr->_max_nonce : NULL))
+			return false;
+	}
+	
+	return true;
+}
+
+void minerloop_async(struct thr_info *mythr)
+{
+	struct cgpu_info *cgpu = mythr->cgpu;
+	const struct device_api *api = cgpu->api;
+	struct timeval tv_now;
+	struct timeval tv_timeout;
+	struct cgpu_info *proc;
+	int maxfd;
+	fd_set rfds;
+	bool is_running, should_be_running;
+	
+	if (mythr->work_restart_notifier[1] == -1)
+		notifier_init(mythr->work_restart_notifier);
+	
+	while (1) {
+		tv_timeout.tv_sec = -1;
+		gettimeofday(&tv_now, NULL);
+		for (proc = cgpu; proc; proc = proc->next_proc)
+		{
+			mythr = proc->thr[0];
+			is_running = mythr->work;
+			should_be_running = (proc->deven == DEV_ENABLED && !mythr->pause);
+			
+			if (should_be_running)
+			{
+				if (unlikely(!(is_running || mythr->_job_transition_in_progress)))
+				{
+					mt_disable_finish(mythr);
+					goto djp;
+				}
+				if (unlikely(mythr->work_restart))
+					goto djp;
+			}
+			else  // ! should_be_running
+			{
+				if (unlikely(is_running && !mythr->_job_transition_in_progress))
+				{
+disabled: ;
+					mythr->tv_morework.tv_sec = -1;
+					do_get_results(mythr, false);
+				}
+			}
+			
+			if (timer_passed(&mythr->tv_morework, &tv_now))
+			{
+djp: ;
+				if (!do_job_prepare(mythr, &tv_now))
+					goto disabled;
+			}
+			
+			if (timer_passed(&mythr->tv_poll, &tv_now))
+				api->poll(mythr);
+			
+			reduce_timeout_to(&tv_timeout, &mythr->tv_morework);
+			reduce_timeout_to(&tv_timeout, &mythr->tv_poll);
+		}
+		
+		gettimeofday(&tv_now, NULL);
+		FD_ZERO(&rfds);
+		FD_SET(mythr->notifier[0], &rfds);
+		maxfd = mythr->notifier[0];
+		FD_SET(mythr->work_restart_notifier[0], &rfds);
+		set_maxfd(&maxfd, mythr->work_restart_notifier[0]);
+		if (select(maxfd + 1, &rfds, NULL, NULL, select_timeout(&tv_timeout, &tv_now)) < 0)
+			continue;
+		if (FD_ISSET(mythr->notifier[0], &rfds)) {
+			notifier_read(mythr->notifier);
+		}
+		if (FD_ISSET(mythr->work_restart_notifier[0], &rfds))
+			notifier_read(mythr->work_restart_notifier);
+	}
+}
+
+void *miner_thread(void *userdata)
+{
+	struct thr_info *mythr = userdata;
+	const int thr_id = mythr->id;
+	struct cgpu_info *cgpu = mythr->cgpu;
+	const struct device_api *api = cgpu->api;
+
+	pthread_setcanceltype(PTHREAD_CANCEL_ASYNCHRONOUS, NULL);
+
+	char threadname[20];
+	snprintf(threadname, 20, "miner_%s", cgpu->proc_repr_ns);
+	RenameThread(threadname);
+
+	if (api->thread_init && !api->thread_init(mythr)) {
+		dev_error(cgpu, REASON_THREAD_FAIL_INIT);
+		for (struct cgpu_info *slave = cgpu->next_proc; slave && !slave->threads; slave = slave->next_proc)
+			dev_error(slave, REASON_THREAD_FAIL_INIT);
+		goto out;
+	}
+
+	thread_reportout(mythr);
+	applog(LOG_DEBUG, "Popping ping in miner thread");
+	notifier_read(mythr->notifier);  // Wait for a notification to start
+
+	if (api->minerloop)
+		api->minerloop(mythr);
+	else
+		minerloop_scanhash(mythr);
+
+out:
+	if (api->thread_shutdown)
+		api->thread_shutdown(mythr);
+
+	thread_reportin(mythr);
+	applog(LOG_ERR, "Thread %d failure, exiting", thr_id);
+	notifier_destroy(mythr->notifier);
+
+	return NULL;
+}
+
+bool add_cgpu(struct cgpu_info*cgpu)
+{
+	int lpcount;
+	
+	renumber_cgpu(cgpu);
+	if (!cgpu->procs)
+		cgpu->procs = 1;
+	lpcount = cgpu->procs;
+	cgpu->device = cgpu;
+	
+	cgpu->dev_repr = malloc(6);
+	sprintf(cgpu->dev_repr, "%s%2u", cgpu->api->name, cgpu->device_id % 100);
+	cgpu->dev_repr_ns = malloc(6);
+	sprintf(cgpu->dev_repr_ns, "%s%u", cgpu->api->name, cgpu->device_id % 100);
+	strcpy(cgpu->proc_repr, cgpu->dev_repr);
+	sprintf(cgpu->proc_repr_ns, "%s%u", cgpu->api->name, cgpu->device_id);
+	
+	devices = realloc(devices, sizeof(struct cgpu_info *) * (total_devices + lpcount + 1));
+	devices[total_devices++] = cgpu;
+	
+	if (lpcount > 1)
+	{
+		int ns;
+		int tpp = cgpu->threads / lpcount;
+		struct cgpu_info **nlp_p, *slave;
+		
+		// Note, strcpy instead of assigning a byte to get the \0 too
+		strcpy(&cgpu->proc_repr[5], "a");
+		ns = strlen(cgpu->proc_repr_ns);
+		strcpy(&cgpu->proc_repr_ns[ns], "a");
+		
+		nlp_p = &cgpu->next_proc;
+		for (int i = 1; i < lpcount; ++i)
+		{
+			slave = malloc(sizeof(*slave));
+			*slave = *cgpu;
+			slave->proc_id = i;
+			slave->proc_repr[5] += i;
+			slave->proc_repr_ns[ns] += i;
+			slave->threads = tpp;
+			devices[total_devices++] = slave;
+			*nlp_p = slave;
+			nlp_p = &slave->next_proc;
+		}
+		*nlp_p = NULL;
+		cgpu->proc_id = 0;
+		cgpu->threads -= (tpp * (lpcount - 1));
+	}
+	return true;
+}

+ 32 - 0
deviceapi.h

@@ -0,0 +1,32 @@
+#ifndef __DEVICEAPI_H__
+#define __DEVICEAPI_H__
+
+#include <stdbool.h>
+#include <stdint.h>
+#include <sys/time.h>
+
+#include "miner.h"
+
+extern void request_work(struct thr_info *);
+extern struct work *get_work(struct thr_info *);
+extern bool hashes_done(struct thr_info *, int64_t hashes, struct timeval *tvp_hashes, uint32_t *max_nonce);
+extern void mt_disable_start(struct thr_info *);
+extern void mt_disable_finish(struct thr_info *);
+extern void mt_disable(struct thr_info *);  // blocks until reenabled
+
+extern void minerloop_scanhash(struct thr_info *);
+
+extern bool do_job_prepare(struct thr_info *, struct timeval *tvp_now);
+extern void job_prepare_complete(struct thr_info *);
+extern void do_get_results(struct thr_info *, bool proceed_with_new_job);
+extern void job_results_fetched(struct thr_info *);
+extern void do_job_start(struct thr_info *);
+extern void mt_job_transition(struct thr_info *);
+extern void job_start_complete(struct thr_info *);
+extern void job_start_abort(struct thr_info *, bool failure);
+extern bool do_process_results(struct thr_info *, struct timeval *tvp_now, struct work *, bool stopping);
+extern void minerloop_async(struct thr_info *);
+
+extern void *miner_thread(void *);
+
+#endif

File diff suppressed because it is too large
+ 526 - 190
driver-bitforce.c


+ 6 - 8
driver-cairnsmore.c

@@ -86,7 +86,7 @@ bool cairnsmore_supports_dynclock(int fd)
 		struct timeval tv_finish;
 		struct thr_info dummy = {
 			.work_restart = false,
-			.work_restart_fd = -1,
+			.work_restart_notifier = {-1, -1},
 		};
 		icarus_gets((unsigned char*)&nonce, fd, &tv_finish, &dummy, 1);
 	}
@@ -117,9 +117,7 @@ static bool cairnsmore_change_clock_func(struct thr_info *thr, int bestM)
 	// Adjust Hs expectations for frequency change
 	info->Hs = info->Hs * (double)bestM / (double)info->dclk.freqM;
 
-	char repr[0x10];
-	sprintf(repr, "%s %u", cm1->api->name, cm1->device_id);
-	dclk_msg_freqchange(repr, 2.5 * (double)info->dclk.freqM, 2.5 * (double)bestM, NULL);
+	dclk_msg_freqchange(cm1->proc_repr, 2.5 * (double)info->dclk.freqM, 2.5 * (double)bestM, NULL);
 	info->dclk.freqM = bestM;
 
 	return true;
@@ -139,8 +137,8 @@ static bool cairnsmore_init(struct thr_info *thr)
 		info->dclk.freqM =
 		info->dclk.freqMDefault = CAIRNSMORE1_DEFAULT_CLOCK / 2.5;
 		cairnsmore_send_cmd(cm1->device_fd, 0, info->dclk.freqM);
-		applog(LOG_WARNING, "%s %u: Frequency set to %u MHz (range: %u-%u)",
-		       cm1->api->name, cm1->device_id,
+		applog(LOG_WARNING, "%"PRIpreprv": Frequency set to %u MHz (range: %u-%u)",
+		       cm1->proc_repr,
 		       CAIRNSMORE1_DEFAULT_CLOCK, CAIRNSMORE1_MINIMUM_CLOCK, CAIRNSMORE1_MAXIMUM_CLOCK
 		);
 		// The dynamic-clocking firmware connects each FPGA as its own device
@@ -150,8 +148,8 @@ static bool cairnsmore_init(struct thr_info *thr)
 				info->fpga_count = 1;
 		}
 	} else {
-		applog(LOG_WARNING, "%s %u: Frequency scaling not supported",
-			cm1->api->name, cm1->device_id
+		applog(LOG_WARNING, "%"PRIpreprv": Frequency scaling not supported",
+			cm1->proc_repr
 		);
 	}
 	// Commands corrupt the hash state, so next scanhash is a firstrun

+ 1 - 2
driver-cpu.c

@@ -809,7 +809,6 @@ static bool cpu_thread_init(struct thr_info *thr)
 
 static int64_t cpu_scanhash(struct thr_info *thr, struct work *work, int64_t max_nonce)
 {
-	const int thr_id = thr->id;
 	unsigned char hash1[64];
 	uint32_t first_nonce = work->blk.nonce;
 	uint32_t last_nonce;
@@ -838,7 +837,7 @@ CPUSearch:
 
 	/* if nonce found, submit work */
 	if (unlikely(rc)) {
-		applog(LOG_DEBUG, "CPU %d found something?", dev_from_id(thr_id));
+		applog(LOG_DEBUG, "%"PRIpreprv" found something?", thr->cgpu->proc_repr);
 		submit_work_async(work, NULL);
 		work->blk.nonce = last_nonce + 1;
 		goto CPUSearch;

+ 34 - 36
driver-icarus.c

@@ -198,7 +198,7 @@ int icarus_gets(unsigned char *buf, int fd, struct timeval *tv_finish, struct th
 	};
 	struct epoll_event evr[2];
 	int epoll_timeout = ICARUS_READ_FAULT_DECISECONDS * 100;
-	if (thr && thr->work_restart_fd != -1) {
+	if (thr && thr->work_restart_notifier[1] != -1) {
 	epollfd = epoll_create(2);
 	if (epollfd != -1) {
 		if (-1 == epoll_ctl(epollfd, EPOLL_CTL_ADD, fd, &ev)) {
@@ -206,8 +206,8 @@ int icarus_gets(unsigned char *buf, int fd, struct timeval *tv_finish, struct th
 			epollfd = -1;
 		}
 		{
-			ev.data.fd = thr->work_restart_fd;
-			if (-1 == epoll_ctl(epollfd, EPOLL_CTL_ADD, thr->work_restart_fd, &ev))
+			ev.data.fd = thr->work_restart_notifier[0];
+			if (-1 == epoll_ctl(epollfd, EPOLL_CTL_ADD, thr->work_restart_notifier[0], &ev))
 				applog(LOG_ERR, "Icarus: Error adding work restart fd to epoll");
 			else
 			{
@@ -231,8 +231,7 @@ int icarus_gets(unsigned char *buf, int fd, struct timeval *tv_finish, struct th
 			else
 			{
 				if (ret)
-					// work restart trigger
-					(void)read(thr->work_restart_fd, buf, read_amount);
+					notifier_read(thr->work_restart_notifier);
 				ret = 0;
 			}
 		}
@@ -404,9 +403,9 @@ static void set_timing_mode(int this_option_offset, struct cgpu_info *icarus)
 
 	info->min_data_count = MIN_DATA_COUNT;
 
-	applog(LOG_DEBUG, "%s %u: Init: mode=%s read_count=%d Hs=%e",
-		icarus->api->name,
-		icarus->device_id, timing_mode_str(info->timing_mode), info->read_count, info->Hs);
+	applog(LOG_DEBUG, "%"PRIpreprv": Init: mode=%s read_count=%d Hs=%e",
+		icarus->proc_repr,
+		timing_mode_str(info->timing_mode), info->read_count, info->Hs);
 }
 
 static uint32_t mask(int work_division)
@@ -620,13 +619,13 @@ bool icarus_detect_custom(const char *devpath, struct device_api *api, struct IC
 	icarus->threads = 1;
 	add_cgpu(icarus);
 
-	applog(LOG_INFO, "Found %s %u at %s",
-		icarus->api->name, icarus->device_id,
+	applog(LOG_INFO, "Found %"PRIpreprv" at %s",
+		icarus->proc_repr,
 		devpath);
 
-	applog(LOG_DEBUG, "%s %u: Init: baud=%d work_division=%d fpga_count=%d",
-		icarus->api->name,
-		icarus->device_id, baud, work_division, fpga_count);
+	applog(LOG_DEBUG, "%"PRIpreprv": Init: baud=%d work_division=%d fpga_count=%d",
+		icarus->proc_repr,
+		baud, work_division, fpga_count);
 
 	icarus->cgpu_data = info;
 
@@ -696,7 +695,7 @@ static bool icarus_prepare(struct thr_info *thr)
 	if (epollfd != -1)
 	{
 		close(epollfd);
-		thr->work_restart_fd = 0;
+		notifier_init(thr->work_restart_notifier);
 	}
 #endif
 
@@ -711,7 +710,7 @@ static bool icarus_reopen(struct cgpu_info *icarus, struct icarus_state *state,
 	icarus_close(icarus->device_fd);
 	*fdp = icarus->device_fd = icarus_open(icarus->device_path, info->baud);
 	if (unlikely(-1 == *fdp)) {
-		applog(LOG_ERR, "%s %u: Failed to reopen on %s", icarus->api->name, icarus->device_id, icarus->device_path);
+		applog(LOG_ERR, "%"PRIpreprv": Failed to reopen on %s", icarus->proc_repr, icarus->device_path);
 		dev_error(icarus, REASON_DEV_COMMS_ERROR);
 		state->firstrun = true;
 		return false;
@@ -739,9 +738,9 @@ static bool icarus_start_work(struct thr_info *thr, const unsigned char *ob_bin)
 
 	if (opt_debug) {
 		ob_hex = bin2hex(ob_bin, 64);
-		applog(LOG_DEBUG, "%s %u sent: %s",
-			icarus->api->name,
-			icarus->device_id, ob_hex);
+		applog(LOG_DEBUG, "%"PRIpreprv" sent: %s",
+			icarus->proc_repr,
+			ob_hex);
 		free(ob_hex);
 	}
 
@@ -785,8 +784,8 @@ static int64_t icarus_scanhash(struct thr_info *thr, struct work *work,
 	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, "%s %u: Received job attempting to send a command, corrupting it!",
-		       icarus->api->name, icarus->device_id);
+		applog(LOG_WARNING, "%"PRIpreprv": Received job attempting to send a command, corrupting it!",
+		       icarus->proc_repr);
 		ob_bin[56] = 0;
 	}
 	rev(ob_bin, 32);
@@ -885,9 +884,8 @@ static int64_t icarus_scanhash(struct thr_info *thr, struct work *work,
 			estimate_hashes = 0xffffffff;
 
 		if (opt_debug) {
-			applog(LOG_DEBUG, "%s %u no nonce = 0x%08"PRIx64" hashes (%"PRId64".%06lus)",
-					icarus->api->name,
-					icarus->device_id,
+			applog(LOG_DEBUG, "%"PRIpreprv" no nonce = 0x%08"PRIx64" hashes (%"PRId64".%06lus)",
+					icarus->proc_repr,
 					(uint64_t)estimate_hashes,
 					(int64_t)elapsed.tv_sec, (unsigned long)elapsed.tv_usec);
 		}
@@ -915,9 +913,8 @@ static int64_t icarus_scanhash(struct thr_info *thr, struct work *work,
 	hash_count *= info->fpga_count;
 
 	if (opt_debug) {
-		applog(LOG_DEBUG, "%s %u nonce = 0x%08x = 0x%08" PRIx64 " hashes (%"PRId64".%06lus)",
-				icarus->api->name,
-				icarus->device_id,
+		applog(LOG_DEBUG, "%"PRIpreprv" nonce = 0x%08x = 0x%08" PRIx64 " hashes (%"PRId64".%06lus)",
+				icarus->proc_repr,
 				nonce,
 				(uint64_t)hash_count,
 				(int64_t)elapsed.tv_sec, (unsigned long)elapsed.tv_usec);
@@ -926,10 +923,10 @@ static int64_t icarus_scanhash(struct thr_info *thr, struct work *work,
 	if (info->do_default_detection && elapsed.tv_sec >= DEFAULT_DETECT_THRESHOLD) {
 		int MHs = (double)hash_count / ((double)elapsed.tv_sec * 1e6 + (double)elapsed.tv_usec);
 		--info->do_default_detection;
-		applog(LOG_DEBUG, "%s %u: Autodetect device speed: %d MH/s", icarus->api->name, icarus->device_id, MHs);
+		applog(LOG_DEBUG, "%"PRIpreprv": Autodetect device speed: %d MH/s", icarus->proc_repr, MHs);
 		if (MHs <= 370 || MHs > 420) {
 			// Not a real Icarus: enable short timing
-			applog(LOG_WARNING, "%s %u: Seems too %s to be an Icarus; calibrating with short timing", icarus->api->name, icarus->device_id, MHs>380?"fast":"slow");
+			applog(LOG_WARNING, "%"PRIpreprv": Seems too %s to be an Icarus; calibrating with short timing", icarus->proc_repr, MHs>380?"fast":"slow");
 			info->timing_mode = MODE_SHORT;
 			info->do_icarus_timing = true;
 			info->do_default_detection = 0;
@@ -938,18 +935,19 @@ static int64_t icarus_scanhash(struct thr_info *thr, struct work *work,
 		if (MHs <= 380) {
 			// Real Icarus?
 			if (!info->do_default_detection) {
-				applog(LOG_DEBUG, "%s %u: Seems to be a real Icarus", icarus->api->name, icarus->device_id);
+				applog(LOG_DEBUG, "%"PRIpreprv": Seems to be a real Icarus", icarus->proc_repr);
 				info->read_count = (int)(info->fullnonce * TIME_FACTOR) - 1;
 			}
 		}
 		else
 		if (MHs <= 420) {
 			// Enterpoint Cairnsmore1
-			const char *old_name = icarus->api->name;
-			int old_devid = icarus->device_id;
+			size_t old_repr_len = strlen(icarus->proc_repr);
+			char old_repr[old_repr_len + 1];
+			strcpy(old_repr, icarus->proc_repr);
 			convert_icarus_to_cairnsmore(icarus);
 			info->do_default_detection = 0;
-			applog(LOG_WARNING, "%s %u: Detected Cairnsmore1 device, upgrading driver to %s %u", old_name, old_devid, icarus->api->name, icarus->device_id);
+			applog(LOG_WARNING, "%"PRIpreprv": Detected Cairnsmore1 device, upgrading driver to %"PRIpreprv, old_repr, icarus->proc_repr);
 		}
 	}
 
@@ -1041,10 +1039,10 @@ static int64_t icarus_scanhash(struct thr_info *thr, struct work *work,
 			else if (info->timing_mode == MODE_SHORT)
 				info->do_icarus_timing = false;
 
-//			applog(LOG_DEBUG, "%s %u Re-estimate: read_count=%d fullnonce=%fs history count=%d Hs=%e W=%e values=%d hash range=0x%08lx min data count=%u", icarus->api->name, icarus->device_id, read_count, fullnonce, count, Hs, W, values, hash_count_range, info->min_data_count);
-			applog(LOG_DEBUG, "%s %u Re-estimate: Hs=%e W=%e read_count=%d fullnonce=%.3fs",
-					icarus->api->name,
-					icarus->device_id, Hs, W, read_count, fullnonce);
+//			applog(LOG_DEBUG, "%"PRIpreprv" Re-estimate: read_count=%d fullnonce=%fs history count=%d Hs=%e W=%e values=%d hash range=0x%08lx min data count=%u", icarus->proc_repr, read_count, fullnonce, count, Hs, W, values, hash_count_range, info->min_data_count);
+			applog(LOG_DEBUG, "%"PRIpreprv" Re-estimate: Hs=%e W=%e read_count=%d fullnonce=%.3fs",
+					icarus->proc_repr,
+					Hs, W, read_count, fullnonce);
 		}
 		info->history_count++;
 		gettimeofday(&tv_history_finish, NULL);

+ 148 - 129
driver-modminer.c

@@ -81,8 +81,9 @@ _bailout(int fd, struct cgpu_info*modminer, int prio, const char *fmt, ...)
 	if (fd != -1)
 		serial_close(fd);
 	if (modminer) {
-		modminer->device_fd = -1;
-		mutex_unlock(&modminer->device_mutex);
+		pthread_mutex_t *mutexp = &modminer->device->device_mutex;
+		modminer->device->device_fd = -1;
+		mutex_unlock(mutexp);
 	}
 
 	va_list ap;
@@ -139,6 +140,7 @@ modminer_detect_one(const char *devpath)
 	modminer->device_path = strdup(devpath);
 	modminer->device_fd = -1;
 	modminer->deven = DEV_ENABLED;
+	modminer->procs = buf[0];
 	modminer->threads = buf[0];
 	modminer->name = devname;
 	modminer->cutofftemp = 85;
@@ -167,20 +169,20 @@ modminer_detect()
 static bool
 modminer_reopen(struct cgpu_info*modminer)
 {
-	close(modminer->device_fd);
+	close(modminer->device->device_fd);
 	int fd = serial_open(modminer->device_path, 0, 10, true);
 	if (unlikely(-1 == fd)) {
-		applog(LOG_ERR, "%s %u: Failed to reopen %s", modminer->api->name, modminer->device_id, modminer->device_path);
+		applog(LOG_ERR, "%s: Failed to reopen %s", modminer->dev_repr, modminer->device_path);
 		return false;
 	}
-	modminer->device_fd = fd;
+	modminer->device->device_fd = fd;
 	return true;
 }
 #define safebailout() do {  \
 	bool _safebailoutrv;  \
 	state->work_running = false;  \
 	_safebailoutrv = modminer_reopen(modminer);  \
-	mutex_unlock(&modminer->device_mutex);  \
+	mutex_unlock(mutexp);  \
 	return _safebailoutrv ? 0 : -1;  \
 } while(0)
 
@@ -207,9 +209,9 @@ FD_ZERO(&fds); \
 FD_SET(fd, &fds);  \
 select(fd+1, &fds, NULL, NULL, NULL);  \
 	if (1 != read(fd, buf, 1))  \
-		bailout2(LOG_ERR, "%s %u: Error programming %s (" eng ")", modminer->api->name, modminer->device_id, modminer->device_path);  \
+		bailout2(LOG_ERR, "%s: Error programming %s (" eng ")", modminer->dev_repr, modminer->device_path);  \
 	if (buf[0] != 1)  \
-		bailout2(LOG_ERR, "%s %u: Wrong " eng " programming %s", modminer->api->name, modminer->device_id, modminer->device_path);  \
+		bailout2(LOG_ERR, "%s: Wrong " eng " programming %s", modminer->dev_repr, modminer->device_path);  \
 } while(0)
 
 static bool
@@ -220,14 +222,14 @@ modminer_fpga_upload_bitstream(struct cgpu_info*modminer)
 	char buf[0x100];
 	unsigned long len, flen;
 	char fpgaid = FPGAID_ALL;
-	FILE *f = open_xilinx_bitstream(modminer, BITSTREAM_FILENAME, &len);
+	FILE *f = open_xilinx_bitstream(modminer->api->dname, modminer->dev_repr, BITSTREAM_FILENAME, &len);
 	if (!f)
 		return false;
 
 	flen = len;
-	int fd = modminer->device_fd;
+	int fd = modminer->device->device_fd;
 
-	applog(LOG_WARNING, "%s %u: Programming %s... DO NOT EXIT UNTIL COMPLETE", modminer->api->name, modminer->device_id, modminer->device_path);
+	applog(LOG_WARNING, "%s: Programming %s... DO NOT EXIT UNTIL COMPLETE", modminer->dev_repr, modminer->device_path);
 	buf[0] = MODMINER_PROGRAM;
 	buf[1] = fpgaid;
 	buf[2] = (len >>  0) & 0xff;
@@ -235,27 +237,27 @@ modminer_fpga_upload_bitstream(struct cgpu_info*modminer)
 	buf[4] = (len >> 16) & 0xff;
 	buf[5] = (len >> 24) & 0xff;
 	if (6 != write(fd, buf, 6))
-		bailout2(LOG_ERR, "%s %u: Error programming %s (cmd)", modminer->api->name, modminer->device_id, modminer->device_path);
+		bailout2(LOG_ERR, "%s: Error programming %s (cmd)", modminer->dev_repr, modminer->device_path);
 	status_read("cmd reply");
 	ssize_t buflen;
 	char nextstatus = 10;
 	while (len) {
 		buflen = len < 32 ? len : 32;
 		if (fread(buf, buflen, 1, f) != 1)
-			bailout2(LOG_ERR, "%s %u: File underrun programming %s (%d bytes left)", modminer->api->name, modminer->device_id, modminer->device_path, len);
+			bailout2(LOG_ERR, "%s: File underrun programming %s (%d bytes left)", modminer->dev_repr, modminer->device_path, len);
 		if (write(fd, buf, buflen) != buflen)
-			bailout2(LOG_ERR, "%s %u: Error programming %s (data)", modminer->api->name, modminer->device_id,  modminer->device_path);
+			bailout2(LOG_ERR, "%s: Error programming %s (data)", modminer->dev_repr,  modminer->device_path);
 		state->pdone = 100 - ((len * 100) / flen);
 		if (state->pdone >= nextstatus)
 		{
 			nextstatus += 10;
-			applog(LOG_WARNING, "%s %u: Programming %s... %d%% complete...", modminer->api->name, modminer->device_id, modminer->device_path, state->pdone);
+			applog(LOG_WARNING, "%s: Programming %s... %d%% complete...", modminer->dev_repr, modminer->device_path, state->pdone);
 		}
 		status_read("status");
 		len -= buflen;
 	}
 	status_read("final status");
-	applog(LOG_WARNING, "%s %u: Done programming %s", modminer->api->name, modminer->device_id, modminer->device_path);
+	applog(LOG_WARNING, "%s: Done programming %s", modminer->dev_repr, modminer->device_path);
 
 	return true;
 }
@@ -265,10 +267,10 @@ modminer_device_prepare(struct cgpu_info *modminer)
 {
 	int fd = serial_open(modminer->device_path, 0, 10, true);
 	if (unlikely(-1 == fd))
-		bailout(LOG_ERR, "%s %u: Failed to open %s", modminer->api->name, modminer->device_id, modminer->device_path);
+		bailout(LOG_ERR, "%s: Failed to open %s", modminer->dev_repr, modminer->device_path);
 
-	modminer->device_fd = fd;
-	applog(LOG_INFO, "%s %u: Opened %s", modminer->api->name, modminer->device_id, modminer->device_path);
+	modminer->device->device_fd = fd;
+	applog(LOG_INFO, "%s: Opened %s", modminer->dev_repr, modminer->device_path);
 
 	struct timeval now;
 	gettimeofday(&now, NULL);
@@ -282,17 +284,18 @@ modminer_device_prepare(struct cgpu_info *modminer)
 static bool
 modminer_fpga_prepare(struct thr_info *thr)
 {
-	struct cgpu_info *modminer = thr->cgpu;
+	struct cgpu_info *proc = thr->cgpu;
+	struct cgpu_info *modminer = proc->device;
 
 	// Don't need to lock the mutex here, since prepare runs from the main thread before the miner threads start
-	if (modminer->device_fd == -1 && !modminer_device_prepare(modminer))
+	if (modminer->device->device_fd == -1 && !modminer_device_prepare(modminer))
 		return false;
 
 	struct modminer_fpga_state *state;
 	state = thr->cgpu_data = calloc(1, sizeof(struct modminer_fpga_state));
 	dclk_prepare(&state->dclk);
 	state->next_work_cmd[0] = MODMINER_SEND_WORK;
-	state->next_work_cmd[1] = thr->device_thread;  // FPGA id
+	state->next_work_cmd[1] = proc->proc_id;  // FPGA id
 
 	return true;
 }
@@ -302,7 +305,8 @@ modminer_change_clock(struct thr_info*thr, bool needlock, signed char delta)
 {
 	struct cgpu_info*modminer = thr->cgpu;
 	struct modminer_fpga_state *state = thr->cgpu_data;
-	char fpgaid = thr->device_thread;
+	char fpgaid = modminer->proc_id;
+	pthread_mutex_t *mutexp = &modminer->device->device_mutex;
 	int fd;
 	unsigned char cmd[6], buf[1];
 	unsigned char clk;
@@ -315,14 +319,14 @@ modminer_change_clock(struct thr_info*thr, bool needlock, signed char delta)
 	cmd[3] = cmd[4] = cmd[5] = '\0';
 
 	if (needlock)
-		mutex_lock(&modminer->device_mutex);
-	fd = modminer->device_fd;
+		mutex_lock(mutexp);
+	fd = modminer->device->device_fd;
 	if (6 != write(fd, cmd, 6))
-		bailout2(LOG_ERR, "%s %u.%u: Error writing (set frequency)", modminer->api->name, modminer->device_id, fpgaid);
+		bailout2(LOG_ERR, "%s: Error writing (set frequency)", modminer->proc_repr);
 	if (serial_read(fd, &buf, 1) != 1)
-		bailout2(LOG_ERR, "%s %u.%u: Error reading (set frequency)", modminer->api->name, modminer->device_id, fpgaid);
+		bailout2(LOG_ERR, "%s: Error reading (set frequency)", modminer->proc_repr);
 	if (needlock)
-		mutex_unlock(&modminer->device_mutex);
+		mutex_unlock(mutexp);
 
 	if (buf[0])
 		state->dclk.freqM = clk / 2;
@@ -335,16 +339,13 @@ modminer_change_clock(struct thr_info*thr, bool needlock, signed char delta)
 static bool modminer_dclk_change_clock(struct thr_info*thr, int multiplier)
 {
 	struct cgpu_info *modminer = thr->cgpu;
-	char fpgaid = thr->device_thread;
 	struct modminer_fpga_state *state = thr->cgpu_data;
 	uint8_t oldFreq = state->dclk.freqM;
 	signed char delta = (multiplier - oldFreq) * 2;
 	if (unlikely(!modminer_change_clock(thr, true, delta)))
 		return false;
 
-	char repr[0x10];
-	sprintf(repr, "%s %u.%u", modminer->api->name, modminer->device_id, fpgaid);
-	dclk_msg_freqchange(repr, oldFreq * 2, state->dclk.freqM * 2, NULL);
+	dclk_msg_freqchange(modminer->proc_repr, oldFreq * 2, state->dclk.freqM * 2, NULL);
 	return true;
 }
 
@@ -361,15 +362,15 @@ modminer_reduce_clock(struct thr_info*thr, bool needlock)
 
 static bool _modminer_get_nonce(struct cgpu_info*modminer, char fpgaid, uint32_t*nonce)
 {
-	int fd = modminer->device_fd;
+	int fd = modminer->device->device_fd;
 	char cmd[2] = {MODMINER_CHECK_WORK, fpgaid};
 	
 	if (write(fd, cmd, 2) != 2) {
-		applog(LOG_ERR, "%s %u: Error writing (get nonce %u)", modminer->api->name, modminer->device_id, fpgaid);
+		applog(LOG_ERR, "%s: Error writing (get nonce)", modminer->proc_repr);
 		return false;
 	}
 	if (4 != serial_read(fd, nonce, 4)) {
-		applog(LOG_ERR, "%s %u: Short read (get nonce %u)", modminer->api->name, modminer->device_id, fpgaid);
+		applog(LOG_ERR, "%s: Short read (get nonce)", modminer->proc_repr);
 		return false;
 	}
 	
@@ -382,50 +383,51 @@ modminer_fpga_init(struct thr_info *thr)
 	struct cgpu_info *modminer = thr->cgpu;
 	struct modminer_fpga_state *state = thr->cgpu_data;
 	int fd;
-	char fpgaid = thr->device_thread;
+	char fpgaid = modminer->proc_id;
+	pthread_mutex_t *mutexp = &modminer->device->device_mutex;
 	uint32_t nonce;
 
 	unsigned char cmd[2], buf[4];
 
-	mutex_lock(&modminer->device_mutex);
-	fd = modminer->device_fd;
+	mutex_lock(mutexp);
+	fd = modminer->device->device_fd;
 	if (fd == -1) {
 		// Died in another thread...
-		mutex_unlock(&modminer->device_mutex);
+		mutex_unlock(mutexp);
 		return false;
 	}
 
 	cmd[0] = MODMINER_GET_USERCODE;
 	cmd[1] = fpgaid;
 	if (write(fd, cmd, 2) != 2)
-		bailout2(LOG_ERR, "%s %u.%u: Error writing (read USER code)", modminer->api->name, modminer->device_id, fpgaid);
+		bailout2(LOG_ERR, "%s: Error writing (read USER code)", modminer->proc_repr);
 	if (serial_read(fd, buf, 4) != 4)
-		bailout2(LOG_ERR, "%s %u.%u: Error reading (read USER code)", modminer->api->name, modminer->device_id, fpgaid);
+		bailout2(LOG_ERR, "%s: Error reading (read USER code)", modminer->proc_repr);
 
 	if (memcmp(buf, BISTREAM_USER_ID, 4)) {
-		applog(LOG_ERR, "%s %u.%u: FPGA not programmed", modminer->api->name, modminer->device_id, fpgaid);
+		applog(LOG_ERR, "%s: FPGA not programmed", modminer->proc_repr);
 		if (!modminer_fpga_upload_bitstream(modminer))
 			return false;
 	} else if (opt_force_dev_init && modminer->status == LIFE_INIT) {
-		applog(LOG_DEBUG, "%s %u.%u: FPGA is already programmed, but --force-dev-init is set",
-		       modminer->api->name, modminer->device_id, fpgaid);
+		applog(LOG_DEBUG, "%s: FPGA is already programmed, but --force-dev-init is set",
+		       modminer->proc_repr);
 		if (!modminer_fpga_upload_bitstream(modminer))
 			return false;
 	}
 	else
-		applog(LOG_DEBUG, "%s %u.%u: FPGA is already programmed :)", modminer->api->name, modminer->device_id, fpgaid);
+		applog(LOG_DEBUG, "%s: FPGA is already programmed :)", modminer->proc_repr);
 	state->pdone = 101;
 
 	state->dclk.freqM = MODMINER_MAX_CLOCK / 2 + 1;  // Will be reduced immediately
 	while (1) {
 		if (state->dclk.freqM <= MODMINER_MIN_CLOCK / 2)
-			bailout2(LOG_ERR, "%s %u.%u: Hit minimum trying to find acceptable frequencies", modminer->api->name, modminer->device_id, fpgaid);
+			bailout2(LOG_ERR, "%s: Hit minimum trying to find acceptable frequencies", modminer->proc_repr);
 		--state->dclk.freqM;
 		if (!modminer_change_clock(thr, false, 0))
 			// MCU rejected assignment
 			continue;
 		if (!_modminer_get_nonce(modminer, fpgaid, &nonce))
-			bailout2(LOG_ERR, "%s %u.%u: Error detecting acceptable frequencies", modminer->api->name, modminer->device_id, fpgaid);
+			bailout2(LOG_ERR, "%s: Error detecting acceptable frequencies", modminer->proc_repr);
 		if (!memcmp(&nonce, "\x00\xff\xff\xff", 4))
 			// MCU took assignment, but disabled FPGA
 			continue;
@@ -435,39 +437,66 @@ modminer_fpga_init(struct thr_info *thr)
 	state->dclk.freqMaxM = state->dclk.freqM;
 	if (MODMINER_DEF_CLOCK / 2 < state->dclk.freqM) {
 		if (!modminer_change_clock(thr, false, -(state->dclk.freqM * 2 - MODMINER_DEF_CLOCK)))
-			applog(LOG_WARNING, "%s %u.%u: Failed to set desired initial frequency of %u", modminer->api->name, modminer->device_id, fpgaid, MODMINER_DEF_CLOCK);
+			applog(LOG_WARNING, "%s: Failed to set desired initial frequency of %u", modminer->proc_repr, MODMINER_DEF_CLOCK);
 	}
 	state->dclk.freqMDefault = state->dclk.freqM;
-	applog(LOG_WARNING, "%s %u.%u: Frequency set to %u MHz (range: %u-%u)", modminer->api->name, modminer->device_id, fpgaid, state->dclk.freqM * 2, MODMINER_MIN_CLOCK, state->dclk.freqMaxM * 2);
+	applog(LOG_WARNING, "%s: Frequency set to %u MHz (range: %u-%u)", modminer->proc_repr, state->dclk.freqM * 2, MODMINER_MIN_CLOCK, state->dclk.freqMaxM * 2);
 
-	mutex_unlock(&modminer->device_mutex);
+	mutex_unlock(mutexp);
 
 	thr->primary_thread = true;
 
 	return true;
 }
 
-static void
-get_modminer_statline_before(char *buf, struct cgpu_info *modminer)
+static
+bool get_modminer_upload_percent(char *buf, struct cgpu_info *modminer)
 {
 	char info[18] = "               | ";
-	int tc = modminer->threads;
-	bool havetemp = false;
-	int i;
 
-	char pdone = ((struct modminer_fpga_state*)(modminer->thr[0]->cgpu_data))->pdone;
+	char pdone = ((struct modminer_fpga_state*)(modminer->device->thr[0]->cgpu_data))->pdone;
 	if (pdone != 101) {
 		sprintf(&info[1], "%3d%%", pdone);
 		info[5] = ' ';
 		strcat(buf, info);
-		return;
+		return true;
 	}
+	return false;
+}
+
+static
+void get_modminer_statline_before(char *buf, struct cgpu_info *modminer)
+{
+	if (get_modminer_upload_percent(buf, modminer))
+		return;
+
+	struct thr_info*thr = modminer->thr[0];
+	struct modminer_fpga_state *state = thr->cgpu_data;
+	float gt = state->temp;
+	
+	if (gt > 0)
+		tailsprintf(buf, "%5.1fC ", gt);
+	else
+		tailsprintf(buf, "       ", gt);
+	tailsprintf(buf, "        | ");
+}
+
+static
+void get_modminer_dev_statline_before(char *buf, struct cgpu_info *modminer)
+{
+	if (get_modminer_upload_percent(buf, modminer))
+		return;
+
+	char info[18] = "               | ";
+	int tc = modminer->procs;
+	bool havetemp = false;
+	int i;
 
 	if (tc > 4)
 		tc = 4;
 
-	for (i = tc - 1; i >= 0; --i) {
-		struct thr_info*thr = modminer->thr[i];
+	for (i = 0; i < tc; ++i, modminer = modminer->next_proc) {
+		struct thr_info*thr = modminer->thr[0];
 		struct modminer_fpga_state *state = thr->cgpu_data;
 		unsigned char temp = state->temp;
 
@@ -498,8 +527,8 @@ static void modminer_get_temperature(struct cgpu_info *modminer, struct thr_info
 		return;
 #endif
 
-	int fd = modminer->device_fd;
-	int fpgaid = thr->device_thread;
+	int fd = modminer->device->device_fd;
+	int fpgaid = modminer->proc_id;
 	char cmd[2] = {MODMINER_TEMP1, fpgaid};
 	char temperature;
 
@@ -513,8 +542,8 @@ static void modminer_get_temperature(struct cgpu_info *modminer, struct thr_info
 					state->last_cutoff_reduced = now;
 					int oldFreq = state->dclk.freqM;
 					if (modminer_reduce_clock(thr, false))
-						applog(LOG_NOTICE, "%s %u.%u: Frequency %s from %u to %u MHz (temp: %d)",
-						       modminer->api->name, modminer->device_id, fpgaid,
+						applog(LOG_NOTICE, "%s: Frequency %s from %u to %u MHz (temp: %d)",
+						       modminer->proc_repr,
 						       (oldFreq > state->dclk.freqM ? "dropped" : "raised "),
 						       oldFreq * 2, state->dclk.freqM * 2,
 						       temperature
@@ -536,15 +565,19 @@ static void modminer_get_temperature(struct cgpu_info *modminer, struct thr_info
 
 static bool modminer_get_stats(struct cgpu_info *modminer)
 {
+	pthread_mutex_t *mutexp = &modminer->device->device_mutex;
 	int hottest = 0;
 	bool get_temp = (modminer->deven != DEV_ENABLED);
 	// Getting temperature more efficiently while enabled
-	// NOTE: Don't need to mess with mutex here, since the device is disabled
 	for (int i = modminer->threads; i--; ) {
 		struct thr_info*thr = modminer->thr[i];
 		struct modminer_fpga_state *state = thr->cgpu_data;
 		if (get_temp)
+		{
+			mutex_lock(mutexp);
 			modminer_get_temperature(modminer, thr);
+			mutex_unlock(mutexp);
+		}
 		int temp = state->temp;
 		if (temp > hottest)
 			hottest = temp;
@@ -559,25 +592,24 @@ static struct api_data*
 get_modminer_api_extra_device_status(struct cgpu_info*modminer)
 {
 	struct api_data*root = NULL;
-	static char *k[4] = {"Board0", "Board1", "Board2", "Board3"};
-	int i;
+	struct thr_info*thr = modminer->thr[0];
+	struct modminer_fpga_state *state = thr->cgpu_data;
+	float f;
+	double d;
 
-	for (i = modminer->threads - 1; i >= 0; --i) {
-		struct thr_info*thr = modminer->thr[i];
-		struct modminer_fpga_state *state = thr->cgpu_data;
-		json_t *o = json_object();
-
-		if (state->temp)
-			json_object_set_new(o, "Temperature", json_integer(state->temp));
-		json_object_set_new(o, "Frequency", json_real((double)state->dclk.freqM * 2 * 1000000.));
-		json_object_set_new(o, "Cool Max Frequency", json_real((double)state->dclk.freqMaxM * 2 * 1000000.));
-		json_object_set_new(o, "Max Frequency", json_real((double)state->freqMaxMaxM * 2 * 1000000.));
-		json_object_set_new(o, "Hardware Errors", json_integer(state->bad_share_counter));
-		json_object_set_new(o, "Valid Nonces", json_integer(state->good_share_counter));
-
-		root = api_add_json(root, k[i], o, false);
-		json_decref(o);
+	if (state->temp)
+	{
+		f = state->temp;
+		root = api_add_temp(root, "Temperature", &f, true);
 	}
+	d = (double)state->dclk.freqM * 2;
+	root = api_add_freq(root, "Frequency", &d, true);
+	d = (double)state->dclk.freqMaxM * 2;
+	root = api_add_freq(root, "Cool Max Frequency", &d, true);
+	d = (double)state->freqMaxMaxM * 2;
+	root = api_add_freq(root, "Max Frequency", &d, true);
+	root = api_add_int(root, "Hardware Errors", &state->bad_share_counter, true);
+	root = api_add_int(root, "Valid Nonces", &state->good_share_counter, true);
 
 	return root;
 }
@@ -600,32 +632,32 @@ modminer_start_work(struct thr_info*thr)
 fd_set fds;
 	struct cgpu_info*modminer = thr->cgpu;
 	struct modminer_fpga_state *state = thr->cgpu_data;
-	char fpgaid = thr->device_thread;
+	pthread_mutex_t *mutexp = &modminer->device->device_mutex;
 	int fd;
 
 	char buf[1];
 
-	mutex_lock(&modminer->device_mutex);
-	fd = modminer->device_fd;
+	mutex_lock(mutexp);
+	fd = modminer->device->device_fd;
 
 	if (unlikely(fd == -1)) {
 		if (!modminer_reopen(modminer)) {
-			mutex_unlock(&modminer->device_mutex);
+			mutex_unlock(mutexp);
 			return false;
 		}
-		fd = modminer->device_fd;
+		fd = modminer->device->device_fd;
 	}
 
 	if (46 != write(fd, state->next_work_cmd, 46))
-		bailout2(LOG_ERR, "%s %u.%u: Error writing (start work)", modminer->api->name, modminer->device_id, fpgaid);
+		bailout2(LOG_ERR, "%s: Error writing (start work)", modminer->proc_repr);
 	gettimeofday(&state->tv_workstart, NULL);
 	state->hashes = 0;
 	status_read("start work");
-	mutex_unlock(&modminer->device_mutex);
+	mutex_unlock(mutexp);
 	if (opt_debug) {
 		char *xdata = bin2hex(state->running_work.data, 80);
-		applog(LOG_DEBUG, "%s %u.%u: Started work: %s",
-		       modminer->api->name, modminer->device_id, fpgaid, xdata);
+		applog(LOG_DEBUG, "%s: Started work: %s",
+		       modminer->proc_repr, xdata);
 		free(xdata);
 	}
 
@@ -645,7 +677,8 @@ modminer_process_results(struct thr_info*thr)
 {
 	struct cgpu_info*modminer = thr->cgpu;
 	struct modminer_fpga_state *state = thr->cgpu_data;
-	char fpgaid = thr->device_thread;
+	char fpgaid = modminer->proc_id;
+	pthread_mutex_t *mutexp = &modminer->device->device_mutex;
 	struct work *work = &state->running_work;
 
 	uint32_t nonce;
@@ -653,27 +686,27 @@ modminer_process_results(struct thr_info*thr)
 	int immediate_bad_nonces = 0, immediate_nonces = 0;
 	bool bad;
 
-	mutex_lock(&modminer->device_mutex);
+	mutex_lock(mutexp);
 	modminer_get_temperature(modminer, thr);
 
 	iter = 200;
 	while (1) {
 		if (!_modminer_get_nonce(modminer, fpgaid, &nonce))
 			safebailout();
-		mutex_unlock(&modminer->device_mutex);
+		mutex_unlock(mutexp);
 		if (memcmp(&nonce, "\xff\xff\xff\xff", 4)) {
 			nonce = le32toh(nonce);
 			bad = !test_nonce(work, nonce, false);
 			++immediate_nonces;
 			if (!bad)
-				applog(LOG_DEBUG, "%s %u.%u: Nonce for current  work: %02x%02x%02x%02x",
-				       modminer->api->name, modminer->device_id, fpgaid,
+				applog(LOG_DEBUG, "%s: Nonce for current  work: %02x%02x%02x%02x",
+				       modminer->proc_repr,
 				       NONCE_CHARS(nonce));
 			else
 			if (test_nonce(&state->last_work, nonce, false))
 			{
-				applog(LOG_DEBUG, "%s %u.%u: Nonce for previous work: %02x%02x%02x%02x",
-				       modminer->api->name, modminer->device_id, fpgaid,
+				applog(LOG_DEBUG, "%s: Nonce for previous work: %02x%02x%02x%02x",
+				       modminer->proc_repr,
 				       NONCE_CHARS(nonce));
 				work = &state->last_work;
 				bad = false;
@@ -684,8 +717,8 @@ modminer_process_results(struct thr_info*thr)
 				submit_nonce(thr, work, nonce);
 			}
 			else {
-				applog(LOG_DEBUG, "%s %u.%u: Nonce with H not zero  : %02x%02x%02x%02x",
-				       modminer->api->name, modminer->device_id, fpgaid,
+				applog(LOG_DEBUG, "%s: Nonce with H not zero  : %02x%02x%02x%02x",
+				       modminer->proc_repr,
 				       NONCE_CHARS(nonce));
 				++hw_errors;
 				++modminer->hw_errors;
@@ -698,7 +731,7 @@ modminer_process_results(struct thr_info*thr)
 		nmsleep(1);
 		if (work_restart(thr))
 			break;
-		mutex_lock(&modminer->device_mutex);
+		mutex_lock(mutexp);
 	}
 
 	struct timeval tv_workend, elapsed;
@@ -708,7 +741,7 @@ modminer_process_results(struct thr_info*thr)
 	uint64_t hashes = (uint64_t)state->dclk.freqM * 2 * (((uint64_t)elapsed.tv_sec * 1000000) + elapsed.tv_usec);
 	if (hashes > 0xffffffff)
 	{
-		applog(LOG_WARNING, "%s %u.%u: Finished work before new one sent", modminer->api->name, modminer->device_id, fpgaid);
+		applog(LOG_WARNING, "%s: Finished work before new one sent", modminer->proc_repr);
 		hashes = 0xffffffff;
 	}
 	if (hashes <= state->hashes)
@@ -779,20 +812,8 @@ static char *modminer_set_device(struct cgpu_info *modminer, char *option, char
 		return replybuf;
 	}
 
-	if (!strncasecmp(option, "clock", 5)) {
-		char repr[0x10];
-		int fpgaid, fpgaid_end, multiplier;
-
-		if (option[5])
-			fpgaid = fpgaid_end = abs(atoi(&option[5]));
-		else {
-			fpgaid = 0;
-			fpgaid_end = modminer->threads - 1;
-		}
-		if (fpgaid >= modminer->threads) {
-			sprintf(replybuf, "invalid fpga: '%s' valid range 0-%d", &option[5], modminer->threads - 1);
-			return replybuf;
-		}
+	if (strcasecmp(option, "clock") == 0) {
+		int multiplier;
 
 		if (!setting || !*setting) {
 			sprintf(replybuf, "missing clock setting");
@@ -807,22 +828,19 @@ static char *modminer_set_device(struct cgpu_info *modminer, char *option, char
 		}
 
 		multiplier = val / 2;
-		for ( ; fpgaid <= fpgaid_end; ++fpgaid) {
-			struct thr_info *thr = modminer->thr[fpgaid];
-			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))) {
-				sprintf(replybuf, "Set clock failed: %s %u.%u",
-				        modminer->api->name, modminer->device_id, fpgaid);
-				return replybuf;
-			}
-
-			sprintf(repr, "%s %u.%u", modminer->api->name, modminer->device_id, fpgaid);
-			dclk_msg_freqchange(repr, oldFreqM * 2, state->dclk.freqM * 2, " on user request");
+		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))) {
+			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;
 	}
 
@@ -834,6 +852,7 @@ struct device_api modminer_api = {
 	.dname = "modminer",
 	.name = "MMQ",
 	.api_detect = modminer_detect,
+	.get_dev_statline_before = get_modminer_dev_statline_before,
 	.get_statline_before = get_modminer_statline_before,
 	.get_stats = modminer_get_stats,
 	.get_api_extra_device_status = get_modminer_api_extra_device_status,

+ 19 - 50
driver-opencl.c

@@ -824,7 +824,7 @@ void pause_dynamic_threads(int gpu)
 
 		thr->pause = cgpu->dynamic;
 		if (!cgpu->dynamic && cgpu->deven != DEV_DISABLED)
-			tq_push(thr->q, &ping);
+			mt_enable(thr);
 	}
 }
 
@@ -862,8 +862,9 @@ retry:
 			mhash_base = false;
 		}
 
-		wlog("GPU %d: %.1f / %.1f %sh/s | A:%d  R:%d  HW:%d  U:%.2f/m  I:%d\n",
-			gpu, displayed_rolling, displayed_total, mhash_base ? "M" : "K",
+		wlog("%"PRIpreprv": %.1f / %.1f %sh/s | A:%d  R:%d  HW:%d  U:%.2f/m  I:%d\n",
+			cgpu->proc_repr,
+			displayed_rolling, displayed_total, mhash_base ? "M" : "K",
 			cgpu->accepted, cgpu->rejected, cgpu->hw_errors,
 			cgpu->utility, cgpu->intensity);
 #ifdef HAVE_ADL
@@ -956,26 +957,17 @@ retry:
 			wlogprint("Invalid selection\n");
 			goto retry;
 		}
+		cgpu = &gpus[selected];
 		if (gpus[selected].deven != DEV_DISABLED) {
 			wlogprint("Device already enabled\n");
 			goto retry;
 		}
 		gpus[selected].deven = DEV_ENABLED;
-		for (i = 0; i < mining_threads; ++i) {
-			thr = &thr_info[i];
-			cgpu = thr->cgpu;
-			if (cgpu->api != &opencl_api)
-				continue;
-			if (dev_from_id(i) != selected)
-				continue;
-			if (cgpu->status != LIFE_WELL) {
-				wlogprint("Must restart device before enabling it");
-				goto retry;
-			}
-			applog(LOG_DEBUG, "Pushing ping to thread %d", thr->id);
-
-			tq_push(thr->q, &ping);
+		if (cgpu->status != LIFE_WELL) {
+			wlogprint("Must restart device before enabling it");
+			goto retry;
 		}
+		proc_enable(cgpu);
 		goto retry;
 	} if (!strncasecmp(&input, "d", 1)) {
 		if (selected)
@@ -1332,7 +1324,7 @@ void *reinit_gpu(void *userdata)
 	struct timeval now;
 	char name[256];
 	int thr_id;
-	int gpu;
+	int i;
 
 	pthread_detach(pthread_self());
 	RenameThread("reinit_gpu");
@@ -1348,21 +1340,10 @@ select_cgpu:
 		goto out;
 	}
 
-	gpu = cgpu->device_id;
-
-	for (thr_id = 0; thr_id < mining_threads; ++thr_id) {
-		thr = &thr_info[thr_id];
-		cgpu = thr->cgpu;
-		if (cgpu->api != &opencl_api)
-			continue;
-		if (dev_from_id(thr_id) != gpu)
-			continue;
-
-		thr = &thr_info[thr_id];
-		if (!thr) {
-			applog(LOG_WARNING, "No reference to thread %d exists", thr_id);
-			continue;
-		}
+	for (i = 0; i < cgpu->threads; ++i)
+	{
+		thr = cgpu->thr[i];
+		thr_id = thr->id;
 
 		thr->rolling = thr->cgpu->rolling = 0;
 		/* Reports the last time we tried to revive a sick GPU */
@@ -1373,15 +1354,12 @@ select_cgpu:
 			applog(LOG_WARNING, "Thread %d no longer exists", thr_id);
 	}
 
-	for (thr_id = 0; thr_id < mining_threads; ++thr_id) {
+	for (i = 0; i < cgpu->threads; ++i)
+	{
 		int virtual_gpu;
 
-		thr = &thr_info[thr_id];
-		cgpu = thr->cgpu;
-		if (cgpu->api != &opencl_api)
-			continue;
-		if (dev_from_id(thr_id) != gpu)
-			continue;
+		thr = cgpu->thr[i];
+		thr_id = thr->id;
 
 		virtual_gpu = cgpu->virtual_gpu;
 		/* Lose this ram cause we may get stuck here! */
@@ -1412,16 +1390,7 @@ select_cgpu:
 	gettimeofday(&now, NULL);
 	get_datestamp(sel_cgpu->init, &now);
 
-	for (thr_id = 0; thr_id < mining_threads; ++thr_id) {
-		thr = &thr_info[thr_id];
-		cgpu = thr->cgpu;
-		if (cgpu->api != &opencl_api)
-			continue;
-		if (dev_from_id(thr_id) != gpu)
-			continue;
-
-		tq_push(thr->q, &ping);
-	}
+	proc_enable(cgpu);
 
 	goto select_cgpu;
 out:

+ 218 - 126
driver-x6500.c

@@ -19,6 +19,7 @@
 #include <libusb.h>
 
 #include "compat.h"
+#include "deviceapi.h"
 #include "dynclock.h"
 #include "jtag.h"
 #include "logging.h"
@@ -129,7 +130,8 @@ static bool x6500_foundusb(libusb_device *dev, const char *product, const char *
 	mutex_init(&x6500->device_mutex);
 	x6500->device_path = strdup(serial);
 	x6500->deven = DEV_ENABLED;
-	x6500->threads = 2;
+	x6500->threads = 1;
+	x6500->procs = 2;
 	x6500->name = strdup(product);
 	x6500->cutofftemp = 85;
 	x6500->cgpu_data = dev;
@@ -154,11 +156,11 @@ static void x6500_detect()
 
 static bool x6500_prepare(struct thr_info *thr)
 {
-	if (thr->device_thread)
+	struct cgpu_info *x6500 = thr->cgpu;
+	
+	if (x6500->proc_id)
 		return true;
 	
-	struct cgpu_info *x6500 = thr->cgpu;
-	mutex_init(&x6500->device_mutex);
 	struct ft232r_device_handle *ftdi = ft232r_open(x6500->cgpu_data);
 	x6500->device_ft232r = NULL;
 	if (!ftdi)
@@ -176,13 +178,19 @@ static bool x6500_prepare(struct thr_info *thr)
 	jtag_a->ftdi = ftdi;
 	x6500->cgpu_data = jtag_a;
 	
+	for (struct cgpu_info *slave = x6500->next_proc; slave; slave = slave->next_proc)
+	{
+		slave->device_ft232r = x6500->device_ft232r;
+		slave->cgpu_data = x6500->cgpu_data;
+	}
+	
 	return true;
 }
 
 struct x6500_fpga_data {
 	struct jtag_port jtag;
-	struct work prevwork;
-	struct timeval tv_workstart;
+	struct timeval tv_hashstart;
+	int64_t hashes_left;
 
 	struct dclk_data dclk;
 	uint8_t freqMaxMaxM;
@@ -191,6 +199,8 @@ struct x6500_fpga_data {
 	time_t last_cutoff_reduced;
 
 	float temp;
+	
+	uint32_t prepwork_last_register;
 };
 
 #define bailout2(...) do {  \
@@ -206,14 +216,14 @@ x6500_fpga_upload_bitstream(struct cgpu_info *x6500, struct jtag_port *jp1)
 	unsigned char *pdone = (unsigned char*)x6500->cgpu_data - 1;
 	struct ft232r_device_handle *ftdi = jp1->a->ftdi;
 
-	FILE *f = open_xilinx_bitstream(x6500, X6500_BITSTREAM_FILENAME, &len);
+	FILE *f = open_xilinx_bitstream(x6500->api->dname, x6500->dev_repr, X6500_BITSTREAM_FILENAME, &len);
 	if (!f)
 		return false;
 
 	flen = len;
 
-	applog(LOG_WARNING, "%s %u: Programming %s...",
-	       x6500->api->name, x6500->device_id, x6500->device_path);
+	applog(LOG_WARNING, "%s: Programming %s...",
+	       x6500->dev_repr, x6500->device_path);
 	x6500->status = LIFE_INIT;
 	
 	// "Magic" jtag_port configured to access both FPGAs concurrently
@@ -235,8 +245,8 @@ x6500_fpga_upload_bitstream(struct cgpu_info *x6500, struct jtag_port *jp1)
 			i = 0xd0;  // Re-set JPROGRAM while reading status
 			jtag_read(jp, JTAG_REG_IR, &i, 6);
 		} while (i & 8);
-		applog(LOG_DEBUG, "%s %u.%u: JPROGRAM ready",
-		       x6500->api->name, x6500->device_id, j);
+		applog(LOG_DEBUG, "%s%c: JPROGRAM ready",
+		       x6500->dev_repr, 'a' + j);
 	}
 	x6500_jtag_set(jp, 0x11);
 	jtag_write(jp, JTAG_REG_IR, "\xa0", 6);  // CFG_IN
@@ -244,7 +254,7 @@ x6500_fpga_upload_bitstream(struct cgpu_info *x6500, struct jtag_port *jp1)
 	sleep(1);
 	
 	if (fread(buf, 32, 1, f) != 1)
-		bailout2(LOG_ERR, "%s %u: File underrun programming %s (%lu bytes left)", x6500->api->name, x6500->device_id, x6500->device_path, len);
+		bailout2(LOG_ERR, "%s: File underrun programming %s (%lu bytes left)", x6500->dev_repr, x6500->device_path, len);
 	jtag_swrite(jp, JTAG_REG_DR, buf, 256);
 	len -= 32;
 	
@@ -262,13 +272,13 @@ x6500_fpga_upload_bitstream(struct cgpu_info *x6500, struct jtag_port *jp1)
 	while (len) {
 		buflen = len < 32 ? len : 32;
 		if (fread(buf, buflen, 1, f) != 1)
-			bailout2(LOG_ERR, "%s %u: File underrun programming %s (%lu bytes left)", x6500->api->name, x6500->device_id, x6500->device_path, len);
+			bailout2(LOG_ERR, "%s: File underrun programming %s (%lu bytes left)", x6500->dev_repr, x6500->device_path, len);
 		jtag_swrite_more(jp, buf, buflen * 8, len == (unsigned long)buflen);
 		*pdone = 100 - ((len * 100) / flen);
 		if (*pdone >= nextstatus)
 		{
 			nextstatus += 25;
-			applog(LOG_WARNING, "%s %u: Programming %s... %d%% complete...", x6500->api->name, x6500->device_id, x6500->device_path, *pdone);
+			applog(LOG_WARNING, "%s: Programming %s... %d%% complete...", x6500->dev_repr, x6500->device_path, *pdone);
 		}
 		len -= buflen;
 	}
@@ -290,7 +300,7 @@ x6500_fpga_upload_bitstream(struct cgpu_info *x6500, struct jtag_port *jp1)
 	if (!(i & 4))
 		return false;
 	
-	applog(LOG_WARNING, "%s %u: Done programming %s", x6500->api->name, x6500->device_id, x6500->device_path);
+	applog(LOG_WARNING, "%s: Done programming %s", x6500->dev_repr, x6500->device_path);
 	*pdone = 101;
 
 	return true;
@@ -311,30 +321,29 @@ static bool x6500_change_clock(struct thr_info *thr, int multiplier)
 static bool x6500_dclk_change_clock(struct thr_info *thr, int multiplier)
 {
 	struct cgpu_info *x6500 = thr->cgpu;
-	char fpgaid = thr->device_thread;
 	struct x6500_fpga_data *fpga = thr->cgpu_data;
 	uint8_t oldFreq = fpga->dclk.freqM;
 
-	mutex_lock(&x6500->device_mutex);
 	if (!x6500_change_clock(thr, multiplier)) {
-		mutex_unlock(&x6500->device_mutex);
 		return false;
 	}
-	mutex_unlock(&x6500->device_mutex);
 
-	char repr[0x10];
-	sprintf(repr, "%s %u.%u", x6500->api->name, x6500->device_id, fpgaid);
-	dclk_msg_freqchange(repr, oldFreq * 2, fpga->dclk.freqM * 2, NULL);
+	dclk_msg_freqchange(x6500->proc_repr, oldFreq * 2, fpga->dclk.freqM * 2, NULL);
 	return true;
 }
 
-static bool x6500_fpga_init(struct thr_info *thr)
+static bool x6500_thread_init(struct thr_info *thr)
 {
 	struct cgpu_info *x6500 = thr->cgpu;
 	struct ft232r_device_handle *ftdi = x6500->device_ft232r;
+
+	for ( ; x6500; x6500 = x6500->next_proc)
+	{
+		thr = x6500->thr[0];
+
 	struct x6500_fpga_data *fpga;
 	struct jtag_port *jp;
-	int fpgaid = thr->device_thread;
+	int fpgaid = x6500->proc_id;
 	uint8_t pinoffset = fpgaid ? 0x10 : 1;
 	unsigned char buf[4] = {0};
 	int i;
@@ -348,19 +357,16 @@ static bool x6500_fpga_init(struct thr_info *thr)
 	x6500_jtag_set(jp, pinoffset);
 	thr->cgpu_data = fpga;
 	
-	mutex_lock(&x6500->device_mutex);
 	if (!jtag_reset(jp)) {
-		mutex_unlock(&x6500->device_mutex);
-		applog(LOG_ERR, "%s %u: JTAG reset failed",
-		       x6500->api->name, x6500->device_id);
+		applog(LOG_ERR, "%s: JTAG reset failed",
+		       x6500->dev_repr);
 		return false;
 	}
 	
 	i = jtag_detect(jp);
 	if (i != 1) {
-		mutex_unlock(&x6500->device_mutex);
-		applog(LOG_ERR, "%s %u: JTAG detect returned %d",
-		       x6500->api->name, x6500->device_id, i);
+		applog(LOG_ERR, "%s: JTAG detect returned %d",
+		       x6500->dev_repr, i);
 		return false;
 	}
 	
@@ -369,53 +375,52 @@ static bool x6500_fpga_init(struct thr_info *thr)
 	 && jtag_read (jp, JTAG_REG_DR, buf, 32)
 	 && jtag_reset(jp)
 	)) {
-		mutex_unlock(&x6500->device_mutex);
-		applog(LOG_ERR, "%s %u: JTAG error reading user code",
-		       x6500->api->name, x6500->device_id);
+		applog(LOG_ERR, "%s: JTAG error reading user code",
+		       x6500->dev_repr);
 		return false;
 	}
 	
 	if (memcmp(buf, X6500_BITSTREAM_USERID, 4)) {
-		applog(LOG_ERR, "%s %u.%u: FPGA not programmed",
-		       x6500->api->name, x6500->device_id, fpgaid);
+		applog(LOG_ERR, "%"PRIprepr": FPGA not programmed",
+		       x6500->proc_repr);
 		if (!x6500_fpga_upload_bitstream(x6500, jp))
 			return false;
 	} else if (opt_force_dev_init && x6500->status == LIFE_INIT) {
-		applog(LOG_DEBUG, "%s %u.%u: FPGA is already programmed, but --force-dev-init is set",
-		       x6500->api->name, x6500->device_id, fpgaid);
+		applog(LOG_DEBUG, "%"PRIprepr": FPGA is already programmed, but --force-dev-init is set",
+		       x6500->proc_repr);
 		if (!x6500_fpga_upload_bitstream(x6500, jp))
 			return false;
 	} else
-		applog(LOG_DEBUG, "%s %u.%u: FPGA is already programmed :)",
-		       x6500->api->name, x6500->device_id, fpgaid);
+		applog(LOG_DEBUG, "%s"PRIprepr": FPGA is already programmed :)",
+		       x6500->proc_repr);
 	
 	dclk_prepare(&fpga->dclk);
 	x6500_change_clock(thr, X6500_DEFAULT_CLOCK / 2);
 	for (i = 0; 0xffffffff != x6500_get_register(jp, 0xE); ++i)
 	{}
-	mutex_unlock(&x6500->device_mutex);
 
 	if (i)
-		applog(LOG_WARNING, "%s %u.%u: Flushed %d nonces from buffer at init",
-		       x6500->api->name, x6500->device_id, fpgaid, i);
+		applog(LOG_WARNING, "%"PRIprepr": Flushed %d nonces from buffer at init",
+		       x6500->proc_repr, i);
 
 	fpga->dclk.minGoodSamples = 3;
 	fpga->freqMaxMaxM =
 	fpga->dclk.freqMaxM = X6500_MAXIMUM_CLOCK / 2;
 	fpga->dclk.freqMDefault = fpga->dclk.freqM;
-	applog(LOG_WARNING, "%s %u.%u: Frequency set to %u MHz (range: %u-%u)",
-	       x6500->api->name, x6500->device_id, fpgaid,
+	applog(LOG_WARNING, "%"PRIprepr": Frequency set to %u MHz (range: %u-%u)",
+	       x6500->proc_repr,
 	       fpga->dclk.freqM * 2,
 	       X6500_MINIMUM_CLOCK,
 	       fpga->dclk.freqMaxM * 2);
 
+	}
+
 	return true;
 }
 
 static 
 void x6500_get_temperature(struct cgpu_info *x6500)
 {
-
 	struct x6500_fpga_data *fpga = x6500->thr[0]->cgpu_data;
 	struct jtag_port *jp = &fpga->jtag;
 	struct ft232r_device_handle *ftdi = jp->a->ftdi;
@@ -465,8 +470,9 @@ void x6500_get_temperature(struct cgpu_info *x6500)
 	ft232r_purge_buffers(jp->a->ftdi, FTDI_PURGE_BOTH);
 	jp->a->bufread = 0;
 
-	for (i = 0; i < 2; ++i) {
-		struct thr_info *thr = x6500->thr[i];
+	x6500 = x6500->device;
+	for (i = 0; i < 2; ++i, x6500 = x6500->next_proc) {
+		struct thr_info *thr = x6500->thr[0];
 		fpga = thr->cgpu_data;
 
 		if (!fpga) continue;
@@ -487,8 +493,8 @@ void x6500_get_temperature(struct cgpu_info *x6500)
 				fpga->last_cutoff_reduced = now;
 				int oldFreq = fpga->dclk.freqM;
 				if (x6500_change_clock(thr, oldFreq - 1))
-					applog(LOG_NOTICE, "%s %u.%u: Frequency dropped from %u to %u MHz (temp: %.1fC)",
-					       x6500->api->name, x6500->device_id, i,
+					applog(LOG_NOTICE, "%"PRIprepr": Frequency dropped from %u to %u MHz (temp: %.1fC)",
+					       x6500->proc_repr,
 					       oldFreq * 2, fpga->dclk.freqM * 2,
 					       fpga->temp
 					);
@@ -507,13 +513,25 @@ void x6500_get_temperature(struct cgpu_info *x6500)
 
 }
 
+static
+bool x6500_all_idle(struct cgpu_info *any_proc)
+{
+	for (struct cgpu_info *proc = any_proc->device; proc; proc = proc->next_proc)
+		if (proc->thr[0]->tv_poll.tv_sec != -1)
+			return false;
+	return true;
+}
+
 static bool x6500_get_stats(struct cgpu_info *x6500)
 {
 	float hottest = 0;
-	if (x6500->deven != DEV_ENABLED) {
-		// Getting temperature more efficiently while enabled
-		// NOTE: Don't need to mess with mutex here, since the device is disabled
-		x6500_get_temperature(x6500);
+	if (x6500_all_idle(x6500)) {
+		// Getting temperature more efficiently while running
+		pthread_mutex_t *mutexp = &x6500->device->device_mutex;
+		mutex_lock(mutexp);
+		if (x6500_all_idle(x6500))
+			x6500_get_temperature(x6500);
+		mutex_unlock(mutexp);
 	}
 
 	for (int i = x6500->threads; i--; ) {
@@ -531,20 +549,49 @@ static bool x6500_get_stats(struct cgpu_info *x6500)
 	return true;
 }
 
-static void
-get_x6500_statline_before(char *buf, struct cgpu_info *x6500)
+static
+bool get_x6500_upload_percent(char *buf, struct cgpu_info *x6500)
 {
 	char info[18] = "               | ";
-	struct x6500_fpga_data *fpga0 = x6500->thr[0]->cgpu_data;
-	struct x6500_fpga_data *fpga1 = x6500->thr[1]->cgpu_data;
 
 	unsigned char pdone = *((unsigned char*)x6500->cgpu_data - 1);
 	if (pdone != 101) {
 		sprintf(&info[1], "%3d%%", pdone);
 		info[5] = ' ';
 		strcat(buf, info);
+		return true;
+	}
+	return false;
+}
+
+static
+void get_x6500_statline_before(char *buf, struct cgpu_info *x6500)
+{
+	if (get_x6500_upload_percent(buf, x6500))
+		return;
+
+	char info[18] = "               | ";
+	struct x6500_fpga_data *fpga = x6500->thr[0]->cgpu_data;
+
+	if (fpga->temp) {
+		sprintf(&info[1], "%.1fC", fpga->temp);
+		info[strlen(info)] = ' ';
+		strcat(buf, info);
 		return;
 	}
+	strcat(buf, "               | ");
+}
+
+static
+void get_x6500_dev_statline_before(char *buf, struct cgpu_info *x6500)
+{
+	if (get_x6500_upload_percent(buf, x6500))
+		return;
+
+	char info[18] = "               | ";
+	struct x6500_fpga_data *fpga0 = x6500->thr[0]->cgpu_data;
+	struct x6500_fpga_data *fpga1 = x6500->next_proc->thr[0]->cgpu_data;
+
 	if (x6500->temp) {
 		sprintf(&info[1], "%.1fC/%.1fC", fpga0->temp, fpga1->temp);
 		info[strlen(info)] = ' ';
@@ -558,69 +605,119 @@ static struct api_data*
 get_x6500_api_extra_device_status(struct cgpu_info *x6500)
 {
 	struct api_data *root = NULL;
-	static char *k[2] = {"FPGA0", "FPGA1"};
-	int i;
-
-	for (i = 0; i < 2; ++i) {
-		struct thr_info *thr = x6500->thr[i];
-		struct x6500_fpga_data *fpga = thr->cgpu_data;
-		json_t *o = json_object();
-
-		if (fpga->temp)
-			json_object_set_new(o, "Temperature", json_real(fpga->temp));
-		json_object_set_new(o, "Frequency", json_real((double)fpga->dclk.freqM * 2 * 1000000.));
-		json_object_set_new(o, "Cool Max Frequency", json_real((double)fpga->dclk.freqMaxM * 2 * 1000000.));
-		json_object_set_new(o, "Max Frequency", json_real((double)fpga->freqMaxMaxM * 2 * 1000000.));
+	struct thr_info *thr = x6500->thr[0];
+	struct x6500_fpga_data *fpga = thr->cgpu_data;
+	double d;
 
-		root = api_add_json(root, k[i], o, false);
-		json_decref(o);
-	}
+	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;
+	root = api_add_freq(root, "Cool Max Frequency", &d, true);
+	d = (double)fpga->freqMaxMaxM * 2;
+	root = api_add_freq(root, "Max Frequency", &d, true);
 
 	return root;
 }
 
 static
-bool x6500_start_work(struct thr_info *thr, struct work *work)
+bool x6500_job_prepare(struct thr_info *thr, struct work *work, __maybe_unused uint64_t max_nonce)
 {
 	struct cgpu_info *x6500 = thr->cgpu;
 	struct x6500_fpga_data *fpga = thr->cgpu_data;
 	struct jtag_port *jp = &fpga->jtag;
-	char fpgaid = thr->device_thread;
-
-	mutex_lock(&x6500->device_mutex);
 
+	if (x6500_all_idle(x6500))
+	{
+		// Neither FPGA is running, so make sure we're not doing a temperature check
+		pthread_mutex_t *mutexp = &x6500->device->device_mutex;
+		mutex_lock(mutexp);
+	}
+	
 	for (int i = 1, j = 0; i < 9; ++i, j += 4)
 		x6500_set_register(jp, i, fromlebytes(work->midstate, j));
 
-	for (int i = 9, j = 64; i < 12; ++i, j += 4)
+	for (int i = 9, j = 64; i < 11; ++i, j += 4)
 		x6500_set_register(jp, i, fromlebytes(work->data, j));
 
+	x6500_get_temperature(x6500);
+	
 	ft232r_flush(jp->a->ftdi);
+	
+	fpga->prepwork_last_register = fromlebytes(work->data, 72);
+	
+	work->blk.nonce = 0xffffffff;
+	
+	return true;
+}
 
-	gettimeofday(&fpga->tv_workstart, NULL);
-	x6500_get_temperature(x6500);
-	mutex_unlock(&x6500->device_mutex);
+static int64_t calc_hashes(struct thr_info *, struct timeval *);
+
+static
+void x6500_job_start(struct thr_info *thr)
+{
+	struct cgpu_info *x6500 = thr->cgpu;
+	struct x6500_fpga_data *fpga = thr->cgpu_data;
+	struct jtag_port *jp = &fpga->jtag;
+	struct timeval tv_now;
 
+	if (thr->prev_work)
+	{
+		dclk_preUpdate(&fpga->dclk);
+		dclk_updateFreq(&fpga->dclk, x6500_dclk_change_clock, thr);
+	}
+
+	x6500_set_register(jp, 11, fpga->prepwork_last_register);
+
+	ft232r_flush(jp->a->ftdi);
+
+	gettimeofday(&tv_now, NULL);
+	if (!thr->prev_work)
+		fpga->tv_hashstart = tv_now;
+	else
+	if (thr->prev_work != thr->work)
+		calc_hashes(thr, &tv_now);
+	fpga->hashes_left = 0x100000000;
+	mt_job_transition(thr);
+
+	if (x6500_all_idle(x6500))
+	{
+		pthread_mutex_t *mutexp = &x6500->device->device_mutex;
+		mutex_unlock(mutexp);
+	}
+	
 	if (opt_debug) {
-		char *xdata = bin2hex(work->data, 80);
-		applog(LOG_DEBUG, "%s %u.%u: Started work: %s",
-		       x6500->api->name, x6500->device_id, fpgaid, xdata);
+		char *xdata = bin2hex(thr->work->data, 80);
+		applog(LOG_DEBUG, "%"PRIprepr": Started work: %s",
+		       x6500->proc_repr, xdata);
 		free(xdata);
 	}
 
-	return true;
+	uint32_t usecs = 0x80000000 / fpga->dclk.freqM;
+	usecs -= 1000000;
+	timer_set_delay(&thr->tv_morework, &tv_now, usecs);
+
+	timer_set_delay(&thr->tv_poll, &tv_now, 10000);
+	
+	job_start_complete(thr);
 }
 
 static
-int64_t calc_hashes(struct x6500_fpga_data *fpga, struct timeval *tv_now)
+int64_t calc_hashes(struct thr_info *thr, struct timeval *tv_now)
 {
+	struct x6500_fpga_data *fpga = thr->cgpu_data;
 	struct timeval tv_delta;
-	int64_t hashes;
+	int64_t hashes, hashes_left;
 
-	timersub(tv_now, &fpga->tv_workstart, &tv_delta);
+	timersub(tv_now, &fpga->tv_hashstart, &tv_delta);
 	hashes = (((int64_t)tv_delta.tv_sec * 1000000) + tv_delta.tv_usec) * fpga->dclk.freqM * 2;
-	if (unlikely(hashes > 0x100000000))
-		hashes = 0x100000000;
+	hashes_left = fpga->hashes_left;
+	if (unlikely(hashes > hashes_left))
+		hashes = hashes_left;
+	fpga->hashes_left -= hashes;
+	hashes_done(thr, hashes, &tv_delta, NULL);
+	fpga->tv_hashstart = *tv_now;
 	return hashes;
 }
 
@@ -630,7 +727,6 @@ int64_t x6500_process_results(struct thr_info *thr, struct work *work)
 	struct cgpu_info *x6500 = thr->cgpu;
 	struct x6500_fpga_data *fpga = thr->cgpu_data;
 	struct jtag_port *jtag = &fpga->jtag;
-	char fpgaid = thr->device_thread;
 
 	struct timeval tv_now;
 	int64_t hashes;
@@ -638,27 +734,25 @@ int64_t x6500_process_results(struct thr_info *thr, struct work *work)
 	bool bad;
 
 	while (1) {
-		mutex_lock(&x6500->device_mutex);
 		gettimeofday(&tv_now, NULL);
 		nonce = x6500_get_register(jtag, 0xE);
-		mutex_unlock(&x6500->device_mutex);
 		if (nonce != 0xffffffff) {
-			bad = !test_nonce(work, nonce, false);
+			bad = !(work && test_nonce(work, nonce, false));
 			if (!bad) {
 				submit_nonce(thr, work, nonce);
-				applog(LOG_DEBUG, "%s %u.%u: Nonce for current  work: %08lx",
-				       x6500->api->name, x6500->device_id, fpgaid,
+				applog(LOG_DEBUG, "%"PRIprepr": Nonce for current  work: %08lx",
+				       x6500->proc_repr,
 				       (unsigned long)nonce);
 
 				dclk_gotNonces(&fpga->dclk);
-			} else if (test_nonce(&fpga->prevwork, nonce, false)) {
-				submit_nonce(thr, &fpga->prevwork, nonce);
-				applog(LOG_DEBUG, "%s %u.%u: Nonce for PREVIOUS work: %08lx",
-				       x6500->api->name, x6500->device_id, fpgaid,
+			} else if (likely(thr->prev_work) && test_nonce(thr->prev_work, nonce, false)) {
+				submit_nonce(thr, thr->prev_work, nonce);
+				applog(LOG_DEBUG, "%"PRIprepr": Nonce for PREVIOUS work: %08lx",
+				       x6500->proc_repr,
 				       (unsigned long)nonce);
 			} else {
-				applog(LOG_DEBUG, "%s %u.%u: Nonce with H not zero  : %08lx",
-				       x6500->api->name, x6500->device_id, fpgaid,
+				applog(LOG_DEBUG, "%"PRIprepr": Nonce with H not zero  : %08lx",
+				       x6500->proc_repr,
 				       (unsigned long)nonce);
 				++hw_errors;
 				++x6500->hw_errors;
@@ -671,44 +765,42 @@ int64_t x6500_process_results(struct thr_info *thr, struct work *work)
 			continue;
 		}
 
-		hashes = calc_hashes(fpga, &tv_now);
-		if (thr->work_restart || hashes >= 0xf0000000)
-			break;
-		usleep(10000);
-		hashes = calc_hashes(fpga, &tv_now);
-		if (thr->work_restart || hashes >= 0xf0000000)
-			break;
+		hashes = calc_hashes(thr, &tv_now);
+		
+		break;
 	}
 
-	dclk_preUpdate(&fpga->dclk);
-	dclk_updateFreq(&fpga->dclk, x6500_dclk_change_clock, thr);
-
-	__copy_work(&fpga->prevwork, work);
-
 	return hashes;
 }
 
-static int64_t
-x6500_scanhash(struct thr_info *thr, struct work *work, int64_t __maybe_unused max_nonce)
+static
+void x6500_fpga_poll(struct thr_info *thr)
 {
-	if (!x6500_start_work(thr, work))
-		return -1;
-
-	int64_t hashes = x6500_process_results(thr, work);
-	if (hashes > 0)
-		work->blk.nonce += hashes;
-	return hashes;
+	struct x6500_fpga_data *fpga = thr->cgpu_data;
+	
+	x6500_process_results(thr, thr->work);
+	if (unlikely(!fpga->hashes_left))
+	{
+		mt_disable_start(thr);
+		thr->tv_poll.tv_sec = -1;
+	}
+	else
+		timer_set_delay_from_now(&thr->tv_poll, 10000);
 }
 
 struct device_api x6500_api = {
 	.dname = "x6500",
 	.name = "XBS",
 	.api_detect = x6500_detect,
+	.get_dev_statline_before = get_x6500_dev_statline_before,
 	.thread_prepare = x6500_prepare,
-	.thread_init = x6500_fpga_init,
+	.thread_init = x6500_thread_init,
 	.get_stats = x6500_get_stats,
 	.get_statline_before = get_x6500_statline_before,
 	.get_api_extra_device_status = get_x6500_api_extra_device_status,
-	.scanhash = x6500_scanhash,
+	.poll = x6500_fpga_poll,
+	.minerloop = minerloop_async,
+	.job_prepare = x6500_job_prepare,
+	.job_start = x6500_job_start,
 // 	.thread_shutdown = x6500_fpga_shutdown,
 };

+ 27 - 18
driver-ztex.c

@@ -58,21 +58,21 @@ static void ztex_releaseFpga(struct libztex_device* ztex)
 	}
 }
 
-static struct cgpu_info *ztex_setup(struct libztex_device *dev, int j)
+static struct cgpu_info *ztex_setup(struct libztex_device *dev, int j, int fpgacount)
 {
 	struct cgpu_info *ztex;
-	char fpganame[LIBZTEX_SNSTRING_LEN+3+1];
+	char *fpganame = (char*)dev->snString;
 
 	ztex = calloc(1, sizeof(struct cgpu_info));
 	ztex->api = &ztex_api;
 	ztex->device_ztex = dev;
-	ztex->threads = 1;
+	ztex->procs = fpgacount;
+	ztex->threads = fpgacount;
 	dev->fpgaNum = j;
 	add_cgpu(ztex);
-	sprintf(ztex->device_ztex->repr, "%s %u", ztex->api->name, ztex->device_id);
-	sprintf(fpganame, "%s-%u", ztex->device_ztex->snString, j+1);
-	ztex->name = strdup(fpganame);
-	applog(LOG_INFO, "%s %u: Found Ztex (ZTEX %s)", ztex->api->name, ztex->device_id, fpganame);
+	strcpy(ztex->device_ztex->repr, ztex->proc_repr);
+	ztex->name = fpganame;
+	applog(LOG_INFO, "%"PRIpreprv": Found Ztex (ZTEX %s)", ztex->dev_repr, fpganame);
 
 	return ztex;
 }
@@ -80,12 +80,11 @@ static struct cgpu_info *ztex_setup(struct libztex_device *dev, int j)
 static int ztex_autodetect(void)
 {
 	int cnt;
-	int i,j;
+	int i;
 	int fpgacount;
 	int totaldevs = 0;
 	struct libztex_dev_list **ztex_devices;
 	struct libztex_device *ztex_master;
-	struct libztex_device *ztex_slave;
 	struct cgpu_info *ztex;
 
 	cnt = libztex_scanDevices(&ztex_devices);
@@ -95,20 +94,13 @@ static int ztex_autodetect(void)
 	for (i = 0; i < cnt; i++) {
 		ztex_master = ztex_devices[i]->dev;
 		ztex_master->root = ztex_master;
-		ztex = ztex_setup(ztex_master, 0);
+		fpgacount = libztex_numberOfFpgas(ztex_master);
+		ztex = ztex_setup(ztex_master, 0, fpgacount);
 
-		fpgacount = libztex_numberOfFpgas(ztex->device_ztex);
 		totaldevs += fpgacount;
 
 		if (fpgacount > 1)
 			pthread_mutex_init(&ztex->device_ztex->mutex, NULL);
-
-		for (j = 1; j < fpgacount; j++) {
-			ztex_slave = calloc(1, sizeof(struct libztex_device));
-			memcpy(ztex_slave, ztex_master, sizeof(struct libztex_device));
-			ztex_slave->root = ztex_master;
-			ztex_setup(ztex_slave, j);
-		}
 	}
 
 	if (cnt > 0)
@@ -367,6 +359,23 @@ static bool ztex_prepare(struct thr_info *thr)
 	gettimeofday(&now, NULL);
 	get_datestamp(cgpu->init, &now);
 
+	if (cgpu->proc_id)
+	{
+		struct libztex_device *ztex_master = cgpu->device->device_ztex;
+		ztex = malloc(sizeof(struct libztex_device));
+		memcpy(ztex, ztex_master, sizeof(*ztex));
+		cgpu->device_ztex = ztex;
+		ztex->root = ztex_master;
+		ztex->fpgaNum = cgpu->proc_id;
+		strcpy(ztex->repr, cgpu->proc_repr);
+	}
+	
+	{
+		char fpganame[LIBZTEX_SNSTRING_LEN+3+1];
+		sprintf(fpganame, "%s-%u", ztex->snString, cgpu->proc_id+1);
+		cgpu->name = fpganame;
+	}
+
 	ztex_selectFpga(ztex);
 	if (libztex_configureFpga(ztex) != 0) {
 		libztex_resetFpga(ztex);

+ 3 - 3
dynclock.c

@@ -22,7 +22,7 @@ void dclk_prepare(struct dclk_data *data)
 
 void dclk_msg_freqchange(const char *repr, int oldFreq, int newFreq, const char *tail)
 {
-	applog(LOG_NOTICE, "%s: Frequency %s from %u to %u MHz%s",
+	applog(LOG_NOTICE, "%"PRIpreprv": Frequency %s from %u to %u MHz%s",
 	       repr,
 	       (oldFreq > newFreq ? "dropped" : "raised "),
 	       oldFreq, newFreq,
@@ -70,8 +70,8 @@ bool dclk_updateFreq(struct dclk_data *data, dclk_change_clock_func_t changecloc
 	while (maxM < data->freqMaxM && data->errorWeight[maxM + 1] > 100)
 		maxM++;
 	if ((bestM < (1.0 - DCLK_OVERHEATTHRESHOLD) * maxM) && bestM < maxM - 1) {
-		applog(LOG_ERR, "%s %u: frequency drop of %.1f%% detect. This may be caused by overheating. FPGA is shut down to prevent damage.",
-		       cgpu->api->name, cgpu->device_id,
+		applog(LOG_ERR, "%"PRIpreprv": frequency drop of %.1f%% detect. This may be caused by overheating. FPGA is shut down to prevent damage.",
+		       cgpu->proc_repr,
 		       (1.0 - 1.0 * bestM / maxM) * 100);
 		return false;
 	}

+ 2 - 2
findnonce.c

@@ -153,8 +153,8 @@ static void *postcalc_hash(void *userdata)
 	/* To prevent corrupt values in FOUND from trying to read beyond the
 	 * end of the res[] array */
 	if (unlikely(pcd->res[FOUND] & ~FOUND)) {
-		applog(LOG_WARNING, "%s%d: invalid nonce count - HW error",
-				thr->cgpu->api->name, thr->cgpu->device_id);
+		applog(LOG_WARNING, "%"PRIpreprv": invalid nonce count - HW error",
+				thr->cgpu->proc_repr);
 		hw_errors++;
 		thr->cgpu->hw_errors++;
 		pcd->res[FOUND] &= FOUND;

+ 28 - 28
fpgautils.c

@@ -806,51 +806,51 @@ FILE *open_bitstream(const char *dname, const char *filename)
 
 #define check_magic(L)  do {  \
 	if (1 != fread(buf, 1, 1, f))  \
-		bailout(LOG_ERR, "%s %u: Error reading firmware ('%c')",  \
-		        cgpu->api->name, cgpu->device_id, L);  \
+		bailout(LOG_ERR, "%s: Error reading firmware ('%c')",  \
+		        repr, L);  \
 	if (buf[0] != L)  \
-		bailout(LOG_ERR, "%s %u: Firmware has wrong magic ('%c')",  \
-		        cgpu->api->name, cgpu->device_id, L);  \
+		bailout(LOG_ERR, "%s: Firmware has wrong magic ('%c')",  \
+		        repr, L);  \
 } while(0)
 
 #define read_str(eng)  do {  \
 	if (1 != fread(buf, 2, 1, f))  \
-		bailout(LOG_ERR, "%s %u: Error reading firmware (" eng " len)",  \
-		        cgpu->api->name, cgpu->device_id);  \
+		bailout(LOG_ERR, "%s: Error reading firmware (" eng " len)",  \
+		        repr);  \
 	len = (ubuf[0] << 8) | ubuf[1];  \
 	if (len >= sizeof(buf))  \
-		bailout(LOG_ERR, "%s %u: Firmware " eng " too long",  \
-		        cgpu->api->name, cgpu->device_id);  \
+		bailout(LOG_ERR, "%s: Firmware " eng " too long",  \
+		        repr);  \
 	if (1 != fread(buf, len, 1, f))  \
-		bailout(LOG_ERR, "%s %u: Error reading firmware (" eng ")",  \
-		        cgpu->api->name, cgpu->device_id);  \
+		bailout(LOG_ERR, "%s: Error reading firmware (" eng ")",  \
+		        repr);  \
 	buf[len] = '\0';  \
 } while(0)
 
-FILE *open_xilinx_bitstream(struct cgpu_info *cgpu, const char *fwfile, unsigned long *out_len)
+FILE *open_xilinx_bitstream(const char *dname, const char *repr, const char *fwfile, unsigned long *out_len)
 {
 	char buf[0x100];
 	unsigned char *ubuf = (unsigned char*)buf;
 	unsigned long len;
 	char *p;
 
-	FILE *f = open_bitstream(cgpu->api->dname, fwfile);
+	FILE *f = open_bitstream(dname, fwfile);
 	if (!f)
-		bailout(LOG_ERR, "%s %u: Error opening firmware file %s",
-		        cgpu->api->name, cgpu->device_id, fwfile);
+		bailout(LOG_ERR, "%s: Error opening firmware file %s",
+		        repr, fwfile);
 	if (1 != fread(buf, 2, 1, f))
-		bailout(LOG_ERR, "%s %u: Error reading firmware (magic)",
-		        cgpu->api->name, cgpu->device_id);
+		bailout(LOG_ERR, "%s: Error reading firmware (magic)",
+		        repr);
 	if (buf[0] || buf[1] != 9)
-		bailout(LOG_ERR, "%s %u: Firmware has wrong magic (9)",
-		        cgpu->api->name, cgpu->device_id);
+		bailout(LOG_ERR, "%s: Firmware has wrong magic (9)",
+		        repr);
 	if (-1 == fseek(f, 11, SEEK_CUR))
-		bailout(LOG_ERR, "%s %u: Firmware seek failed",
-		        cgpu->api->name, cgpu->device_id);
+		bailout(LOG_ERR, "%s: Firmware seek failed",
+		        repr);
 	check_magic('a');
 	read_str("design name");
-	applog(LOG_DEBUG, "%s %u: Firmware file %s info:",
-	       cgpu->api->name, cgpu->device_id, fwfile);
+	applog(LOG_DEBUG, "%s: Firmware file %s info:",
+	       repr, fwfile);
 	applog(LOG_DEBUG, "  Design name: %s", buf);
 	p = strrchr(buf, ';') ?: buf;
 	p = strrchr(buf, '=') ?: p;
@@ -858,11 +858,11 @@ FILE *open_xilinx_bitstream(struct cgpu_info *cgpu, const char *fwfile, unsigned
 		++p;
 	unsigned long fwusercode = (unsigned long)strtoll(p, &p, 16);
 	if (p[0] != '\0')
-		bailout(LOG_ERR, "%s %u: Bad usercode in firmware file",
-		        cgpu->api->name, cgpu->device_id);
+		bailout(LOG_ERR, "%s: Bad usercode in firmware file",
+		        repr);
 	if (fwusercode == 0xffffffff)
-		bailout(LOG_ERR, "%s %u: Firmware doesn't support user code",
-		        cgpu->api->name, cgpu->device_id);
+		bailout(LOG_ERR, "%s: Firmware doesn't support user code",
+		        repr);
 	applog(LOG_DEBUG, "  Version: %u, build %u", (unsigned)((fwusercode >> 8) & 0xff), (unsigned)(fwusercode & 0xff));
 	check_magic('b');
 	read_str("part number");
@@ -875,8 +875,8 @@ FILE *open_xilinx_bitstream(struct cgpu_info *cgpu, const char *fwfile, unsigned
 	applog(LOG_DEBUG, "  Build time: %s", buf);
 	check_magic('e');
 	if (1 != fread(buf, 4, 1, f))
-		bailout(LOG_ERR, "%s %u: Error reading firmware (data len)",
-		        cgpu->api->name, cgpu->device_id);
+		bailout(LOG_ERR, "%s: Error reading firmware (data len)",
+		        repr);
 	len = ((unsigned long)ubuf[0] << 24) | ((unsigned long)ubuf[1] << 16) | (ubuf[2] << 8) | ubuf[3];
 	applog(LOG_DEBUG, "  Bitstream size: %lu", len);
 

+ 1 - 1
fpgautils.h

@@ -46,6 +46,6 @@ extern ssize_t _serial_read(int fd, char *buf, size_t buflen, char *eol);
 #define serial_close(fd)  close(fd)
 
 extern FILE *open_bitstream(const char *dname, const char *filename);
-extern FILE *open_xilinx_bitstream(struct cgpu_info *cgpu, const char *fwfile, unsigned long *out_len);
+extern FILE *open_xilinx_bitstream(const char *dname, const char *repr, const char *fwfile, unsigned long *out_len);
 
 #endif

File diff suppressed because it is too large
+ 358 - 346
miner.c


+ 60 - 5
miner.h

@@ -271,6 +271,10 @@ struct device_api {
 	void (*api_detect)();
 
 	// Device-specific functions
+	void (*get_dev_statline_before)(char *, struct cgpu_info *);
+	void (*get_dev_statline_after)(char *, struct cgpu_info *);
+
+	// Processor-specific functions
 	void (*reinit_device)(struct cgpu_info *);
 	void (*get_statline_before)(char *, struct cgpu_info *);
 	void (*get_statline)(char *, struct cgpu_info *);
@@ -283,6 +287,7 @@ struct device_api {
 
 	// Thread-specific functions
 	bool (*thread_prepare)(struct thr_info *);
+	void (*minerloop)(struct thr_info *);
 	uint64_t (*can_limit_work)(struct thr_info *);
 	bool (*thread_init)(struct thr_info *);
 	bool (*prepare_work)(struct thr_info *, struct work *);
@@ -290,6 +295,17 @@ struct device_api {
 	void (*hw_error)(struct thr_info *);
 	void (*thread_shutdown)(struct thr_info *);
 	void (*thread_enable)(struct thr_info *);
+
+	// === Implemented by minerloop_async ===
+
+	// Can be used per-thread or per-processor
+	void (*poll)(struct thr_info *);
+
+	// Job-specific functions (only with minerloop_async!)
+	bool (*job_prepare)(struct thr_info*, struct work*, uint64_t);
+	void (*job_start)(struct thr_info*);
+	void (*job_get_results)(struct thr_info*, struct work*);
+	int64_t (*job_process_results)(struct thr_info*, struct work*, bool stopping);
 };
 
 enum dev_enable {
@@ -349,6 +365,8 @@ struct cgminer_stats {
 	struct timeval getwork_wait;
 	struct timeval getwork_wait_max;
 	struct timeval getwork_wait_min;
+
+	struct timeval _get_start;
 };
 
 // Just the actual network getworks to the pool
@@ -376,12 +394,26 @@ struct cgminer_pool_stats {
 	uint64_t net_bytes_received;
 };
 
+#define PRIprepr "-6s"
+#define PRIpreprv "s"
+
 struct cgpu_info {
 	int cgminer_id;
+	int device_line_id;
 	const struct device_api *api;
 	const char *devtype;
 	int device_id;
+	char *dev_repr;
+	char *dev_repr_ns;
 	const char *name;
+	
+	int procs;
+	int proc_id;
+	char proc_repr[8];
+	char proc_repr_ns[8];
+	struct cgpu_info *device;
+	struct cgpu_info *next_proc;
+	
 	const char *device_path;
 	FILE *device_file;
 	union {
@@ -400,7 +432,6 @@ struct cgpu_info {
 	double avg_wait_f;
 	unsigned int avg_wait_d;
 	uint32_t nonces;
-	bool nonce_range;
 	bool polling;
 #endif
 #if defined(USE_BITFORCE) || defined(USE_ICARUS)
@@ -448,8 +479,6 @@ struct cgpu_info {
 	int intervals;
 #endif
 
-	bool new_work;
-
 	float temp;
 	int cutofftemp;
 	int targettemp;
@@ -516,13 +545,32 @@ struct thr_info {
 	struct timeval last;
 	struct timeval sick;
 
+	bool	scanhash_working;
+	uint64_t hashes_done;
+	struct timeval tv_hashes_done;
+	struct timeval tv_lastupdate;
+
 	bool	pause;
 	time_t	getwork;
 	double	rolling;
 
+	// Used by minerloop_async
+	struct work *prev_work;
+	struct work *work;
+	struct work *next_work;
+	struct timeval tv_morework;
+	struct work *results_work;
+	bool _job_transition_in_progress;
+	bool _proceed_with_new_job;
+	struct timeval tv_results_jobstart;
+	struct timeval tv_jobstart;
+	struct timeval tv_poll;
+	notifier_t notifier;
+	bool starting_next_work;
+	uint32_t _max_nonce;
+
 	bool	work_restart;
-	int		work_restart_fd;
-	int		_work_restart_fd_w;
+	notifier_t work_restart_notifier;
 };
 
 extern int thr_info_create(struct thr_info *thr, pthread_attr_t *attr, void *(*start) (void *), void *arg);
@@ -739,12 +787,18 @@ extern pthread_mutex_t restart_lock;
 extern pthread_cond_t restart_cond;
 
 extern void thread_reportin(struct thr_info *thr);
+extern void thread_reportout(struct thr_info *);
+extern void hashmeter2(struct thr_info *);
 extern int restart_wait(unsigned int mstime);
+extern bool stale_work(struct work *, bool share);
+extern bool stale_work_future(struct work *, bool share, unsigned long ustime);
 extern int stale_wait(unsigned int mstime, struct work*, bool checkend);
 
 extern void kill_work(void);
 extern void app_restart(void);
 
+extern void mt_enable(struct thr_info *thr);
+extern void proc_enable(struct cgpu_info *);
 extern void reinit_device(struct cgpu_info *cgpu);
 
 #ifdef HAVE_ADL
@@ -1066,6 +1120,7 @@ extern enum test_nonce2_result _test_nonce2(struct work *, uint32_t nonce, bool
 #define test_nonce(work, nonce, checktarget)  (_test_nonce2(work, nonce, checktarget) == TNR_GOOD)
 #define test_nonce2(work, nonce)  (_test_nonce2(work, nonce, true))
 extern void submit_nonce(struct thr_info *thr, struct work *work, uint32_t nonce);
+extern bool abandon_work(struct work *, struct timeval *work_runtime, uint64_t hashes);
 extern void tailsprintf(char *f, const char *fmt, ...);
 extern void wlogprint(const char *f, ...);
 extern int curses_int(const char *query);

+ 25 - 19
miner.php

@@ -83,7 +83,8 @@ $mobilepage = array(
  'DATE' => null,
  'RIGS' => null,
  'SUMMARY' => array('Elapsed', 'MHS av', 'Found Blocks=Blks', 'Accepted', 'Rejected=Rej', 'Utility'),
- 'DEVS+NOTIFY' => array('DEVS.Name=Name', 'DEVS.ID=ID', 'DEVS.Status=Status', 'DEVS.Temperature=Temp',
+ 'DEVS+NOTIFY' => array('DEVS.Name=Name', 'DEVS.ID=ID', 'DEVS.ProcID=Proc',
+			'DEVS.Status=Status', 'DEVS.Temperature=Temp',
 			'DEVS.MHS av=MHS av', 'DEVS.Accepted=Accept', 'DEVS.Rejected=Rej',
 			'DEVS.Utility=Utility', 'NOTIFY.Last Not Well=Not Well'),
  'POOL' => array('POOL', 'Status', 'Accepted', 'Rejected=Rej', 'Last Share Time'));
@@ -1074,9 +1075,12 @@ function details($cmd, $list, $rig)
 	if (isset($values['ID']))
 	{
 		$repr = $values['Name'].$values['ID'];
+		if (isset($values['ProcID']))
+			$repr .= join_get_field('ProcID', $values);
 		$list[$item] = $values = array('Device' => $repr) + array_slice($values, 1);
 		unset($values['Name']);
 		unset($values['ID']);
+		unset($values['ProcID']);
 	}
 	$namesByIndex = array_keys($values);
 	$nameCount = count($namesByIndex);
@@ -1781,6 +1785,19 @@ function joinfields($section1, $section2, $join, $results)
  return $newres;
 }
 #
+function join_get_field($field, $fields)
+{
+	// : means a string constant otherwise it's a field name
+	// ProcID field name is converted to a lowercase letter
+	if (substr($field, 0, 1) == ':')
+		return substr($field, 1);
+	else
+	if ($field == 'ProcID')
+		return chr(97 + $fields[$field]);
+	else
+		return $fields[$field];
+}
+#
 function joinlr($section1, $section2, $join, $results)
 {
  global $sectionmap;
@@ -1805,15 +1822,9 @@ function joinlr($section1, $section2, $join, $results)
 		}
 
 		// Build L string to be matched
-		// : means a string constant otherwise it's a field name
 		$Lval = '';
 		foreach ($join['L'] as $field)
-		{
-			if (substr($field, 0, 1) == ':')
-				$Lval .= substr($field, 1);
-			else
-				$Lval .= $fields1b[$field];
-		}
+			$Lval .= join_get_field($field, $fields1b);
 
 		// foreach answer section in the rig api call (for the other api command)
 		foreach ($results[$name2][$rig] as $name2b => $fields2b)
@@ -1822,15 +1833,9 @@ function joinlr($section1, $section2, $join, $results)
 				continue;
 
 			// Build R string and compare
-			// : means a string constant otherwise it's a field name
 			$Rval = '';
 			foreach ($join['R'] as $field)
-			{
-				if (substr($field, 0, 1) == ':')
-					$Rval .= substr($field, 1);
-				else
-					$Rval .= $fields2b[$field];
-			}
+				$Rval .= join_get_field($field, $fields2b);
 
 			if ($Lval === $Rval)
 			{
@@ -1898,7 +1903,7 @@ function joinsections($sections, $results, $errors)
 {
  global $sectionmap;
 
- // GPU's don't have Name,ID fields - so create them
+ // GPU's don't have Name,ID,ProcID fields - so create them
  foreach ($results as $section => $res)
 	foreach ($res as $rig => $result)
 		foreach ($result as $name => $fields)
@@ -1908,6 +1913,7 @@ function joinsections($sections, $results, $errors)
 			{
 				$results[$section][$rig][$name]['Name'] = 'GPU';
 				$results[$section][$rig][$name]['ID'] = $result[$name]['GPU'];
+				$results[$section][$rig][$name]['ProcID'] = 0;
 			}
 		}
 
@@ -1940,12 +1946,12 @@ function joinsections($sections, $results, $errors)
 				case 'NOTIFY':
 				case 'DEVDETAILS':
 				case 'USBSTATS':
-					$join = array('Name', 'ID');
+					$join = array('Name', 'ID', 'ProcID');
 					$sectionmap[$section] = $section;
 					$results[$section] = joinfields($both[0], $both[1], $join, $results);
 					break;
 				case 'STATS':
-					$join = array('L' => array('Name','ID'), 'R' => array('ID'));
+					$join = array('L' => array('Name','ID','ProcID'), 'R' => array('ID'));
 					$sectionmap[$section] = $section;
 					$results[$section] = joinlr($both[0], $both[1], $join, $results);
 					break;
@@ -2732,7 +2738,7 @@ function display()
  endtable();
  otherrow('<td><br><br></td>');
  newtable();
- doforeach('devs', 'device list', $sum, array(''=>'','ID'=>'','Name'=>''), false);
+ doforeach('devs', 'device list', $sum, array(''=>'','ProcID'=>'','ID'=>'','Name'=>''), false);
  endtable();
  otherrow('<td><br><br></td>');
  newtable();

+ 14 - 0
util.c

@@ -1829,6 +1829,8 @@ void notifier_init(notifier_t pipefd)
 
 void notifier_wake(notifier_t fd)
 {
+	if (fd[1] == INVSOCK)
+		return;
 #ifdef WIN32
 	(void)send(fd[1], "\0", 1, 0);
 #else
@@ -1845,3 +1847,15 @@ void notifier_read(notifier_t fd)
 	(void)read(fd[0], buf, sizeof(buf));
 #endif
 }
+
+void notifier_destroy(notifier_t fd)
+{
+#ifdef WIN32
+	closesocket(fd[0]);
+	closesocket(fd[1]);
+#else
+	close(fd[0]);
+	close(fd[1]);
+#endif
+	fd[0] = fd[1] = INVSOCK;
+}

+ 56 - 0
util.h

@@ -4,6 +4,8 @@
 #include <curl/curl.h>
 #include <jansson.h>
 
+#include "compat.h"
+
 #if defined(unix) || defined(__APPLE__)
 	#include <errno.h>
 	#include <sys/socket.h>
@@ -74,6 +76,7 @@ typedef SOCKETTYPE notifier_t[2];
 extern void notifier_init(notifier_t);
 extern void notifier_wake(notifier_t);
 extern void notifier_read(notifier_t);
+extern void notifier_destroy(notifier_t);
 
 /* Align a size_t to 4 byte boundaries for fussy arches */
 static inline void align_len(size_t *len)
@@ -82,4 +85,57 @@ static inline void align_len(size_t *len)
 		*len += 4 - (*len % 4);
 }
 
+
+static inline
+void set_maxfd(int *p_maxfd, int fd)
+{
+	if (fd > *p_maxfd)
+		*p_maxfd = fd;
+}
+
+
+#define timer_set_delay(tvp_timer, tvp_now, usecs)  do {  \
+	struct timeval tv_add = {  \
+		.tv_sec = usecs / 1000000,  \
+		.tv_usec = usecs % 1000000,  \
+	};  \
+	timeradd(&tv_add, tvp_now, tvp_timer);  \
+} while(0)
+
+#define timer_set_delay_from_now(tvp_timer, usecs)  do {  \
+	struct timeval tv_now;  \
+	gettimeofday(&tv_now, NULL);  \
+	timer_set_delay(tvp_timer, &tv_now, usecs);  \
+} while(0)
+
+static inline
+bool timer_passed(struct timeval *tvp_timer, struct timeval *tvp_now)
+{
+	return (tvp_timer->tv_sec != -1 && timercmp(tvp_timer, tvp_now, <));
+}
+
+static inline
+void reduce_timeout_to(struct timeval *tvp_timeout, struct timeval *tvp_time)
+{
+	if (tvp_time->tv_sec == -1)
+		return;
+	if (tvp_timeout->tv_sec == -1 /* no 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)
+		return NULL;
+	
+	if (timercmp(tvp_timeout, tvp_now, <))
+		timerclear(tvp_timeout);
+	else
+		timersub(tvp_timeout, tvp_now, tvp_timeout);
+	
+	return tvp_timeout;
+}
+
+
 #endif /* __UTIL_H__ */

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