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 := miner.c
 
 
 bfgminer_SOURCES	+= elist.h miner.h compat.h bench_block.h	\
 bfgminer_SOURCES	+= elist.h miner.h compat.h bench_block.h	\
+	deviceapi.c deviceapi.h \
 		   util.c util.h uthash.h logging.h		\
 		   util.c util.h uthash.h logging.h		\
 		   sha2.c sha2.h api.c
 		   sha2.c sha2.h api.c
 bfgminer_DEPENDENCIES =
 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)
 --scrypt            Use the scrypt algorithm for mining (non-bitcoin)
 --sharelog <arg>    Append share log to file
 --sharelog <arg>    Append share log to file
 --shares <arg>      Quit after mining N shares (default: unlimited)
 --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)
 --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
 --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)
 --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:
 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
 pool. The 2 diff values are the actual difficulty target that share reached
 followed by the difficulty target the pool is currently asking for.
 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
                               reply before BFGMiner quits
 
 
  notify        NOTIFY         The last status and history count of each devices problem
  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 (*)
  privileged (*)
                none           There is no reply section just the STATUS section
                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
  devdetails    DEVDETAILS     Each device with a list of their static details
                               This lists all devices including those not supported
                               This lists all devices including those not supported
                               by the 'devs' command
                               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"
  restart (*)   none           There is no status section but just a single "RESTART"
                               reply before BFGMiner restarts
                               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:
 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)
 API V1.24 (BFGMiner v2.10.3)
 
 
 Added API commands:
 Added API commands:
@@ -1239,7 +1255,8 @@ Looking at the Mobile example:
   'RIGS' => null,
   'RIGS' => null,
   'SUMMARY' => array('Elapsed', 'MHS av', 'Found Blocks=Blks', 
   'SUMMARY' => array('Elapsed', 'MHS av', 'Found Blocks=Blks', 
 			Accepted', 'Rejected=Rej', 'Utility'),
 			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.Temperature=Temp', 'DEVS.MHS av=MHS av',
 			'DEVS.Accepted=Accept', 'DEVS.Rejected=Rej',
 			'DEVS.Accepted=Accept', 'DEVS.Rejected=Rej',
 			'DEVS.Utility=Utility', 'NOTIFY.Last Not Well=Not Well'),
 			'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
 	since each field name in the join between DEVS and NOTIFY is actually
 	section.fieldname, not just fieldname
 	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
  POOL
 
 

+ 28 - 46
api.c

@@ -573,9 +573,6 @@ struct CODES {
 
 
 static int my_thr_id = 0;
 static int my_thr_id = 0;
 static bool bye;
 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
 // Used to control quit restart access to shutdown variables
 static pthread_mutex_t quit_restart_lock;
 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)
 static void devdetail_an(struct io_data *io_data, struct cgpu_info *cgpu, bool isjson, bool precom)
 {
 {
 	struct api_data *root = NULL;
 	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_int(root, (char*)cgpu->devtype, &n, true);
+	root = api_add_device_identifier(root, cgpu);
 	root = api_add_string(root, "Driver", cgpu->api->dname, false);
 	root = api_add_string(root, "Driver", cgpu->api->dname, false);
 	if (cgpu->kname)
 	if (cgpu->kname)
 		root = api_add_string(root, "Kernel", cgpu->kname, false);
 		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_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, "Enabled", bool2str(cgpu->deven != DEV_DISABLED), false);
 	root = api_add_string(root, "Status", status2str(cgpu->status), false);
 	root = api_add_string(root, "Status", status2str(cgpu->status), false);
 	if (cgpu->temp)
 	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)
 static void pgaenable(struct io_data *io_data, __maybe_unused SOCKETTYPE c, char *param, bool isjson, __maybe_unused char group)
 {
 {
 	int numpga = numpgas();
 	int numpga = numpgas();
-	struct thr_info *thr;
-	int pga;
 	int id;
 	int id;
-	int i;
 
 
 	if (numpga == 0) {
 	if (numpga == 0) {
 		message(io_data, MSG_PGANON, 0, NULL, isjson);
 		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];
 	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) {
 	if (cgpu->deven != DEV_DISABLED) {
 		message(io_data, MSG_PGALRENA, id, NULL, isjson);
 		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
 #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);
 	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];
 	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) {
 	if (cgpu->deven == DEV_DISABLED) {
 		message(io_data, MSG_PGALRDIS, id, NULL, isjson);
 		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
 #ifdef HAVE_OPENCL
 static void gpuenable(struct io_data *io_data, __maybe_unused SOCKETTYPE c, char *param, bool isjson, __maybe_unused char group)
 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 id;
-	int i;
 
 
 	if (gpu_threads == 0) {
 	if (gpu_threads == 0) {
 		message(io_data, MSG_GPUNON, 0, NULL, isjson);
 		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;
 		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) {
 	if (gpus[id].deven != DEV_DISABLED) {
 		message(io_data, MSG_ALRENA, id, NULL, isjson);
 		message(io_data, MSG_ALRENA, id, NULL, isjson);
 		return;
 		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);
 	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;
 		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) {
 	if (gpus[id].deven == DEV_DISABLED) {
 		message(io_data, MSG_ALRDIS, id, NULL, isjson);
 		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 '*'
 	// ALL counters (and only counters) must start the name with a '*'
 	// Simplifies future external support for identifying new counters
 	// Simplifies future external support for identifying new counters
 	root = api_add_int(root, "NOTIFY", &device, false);
 	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 Well", &(cgpu->device_last_well), false);
 	root = api_add_time(root, "Last Not Well", &(cgpu->device_last_not_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);
 	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];
 		cgpu = devices[i];
 
 
 		root = api_add_int(root, "DEVDETAILS", &i, false);
 		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_string(root, "Driver", cgpu->api->dname, false);
 		root = api_add_const(root, "Kernel", cgpu->kname ? : BLANK, false);
 		root = api_add_const(root, "Kernel", cgpu->kname ? : BLANK, false);
 		root = api_add_const(root, "Model", cgpu->name ? : 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
 			else
 				extra = NULL;
 				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 timeval tv_finish;
 		struct thr_info dummy = {
 		struct thr_info dummy = {
 			.work_restart = false,
 			.work_restart = false,
-			.work_restart_fd = -1,
+			.work_restart_notifier = {-1, -1},
 		};
 		};
 		icarus_gets((unsigned char*)&nonce, fd, &tv_finish, &dummy, 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
 	// Adjust Hs expectations for frequency change
 	info->Hs = info->Hs * (double)bestM / (double)info->dclk.freqM;
 	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;
 	info->dclk.freqM = bestM;
 
 
 	return true;
 	return true;
@@ -139,8 +137,8 @@ static bool cairnsmore_init(struct thr_info *thr)
 		info->dclk.freqM =
 		info->dclk.freqM =
 		info->dclk.freqMDefault = CAIRNSMORE1_DEFAULT_CLOCK / 2.5;
 		info->dclk.freqMDefault = CAIRNSMORE1_DEFAULT_CLOCK / 2.5;
 		cairnsmore_send_cmd(cm1->device_fd, 0, info->dclk.freqM);
 		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
 		       CAIRNSMORE1_DEFAULT_CLOCK, CAIRNSMORE1_MINIMUM_CLOCK, CAIRNSMORE1_MAXIMUM_CLOCK
 		);
 		);
 		// The dynamic-clocking firmware connects each FPGA as its own device
 		// 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;
 				info->fpga_count = 1;
 		}
 		}
 	} else {
 	} 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
 	// 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)
 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];
 	unsigned char hash1[64];
 	uint32_t first_nonce = work->blk.nonce;
 	uint32_t first_nonce = work->blk.nonce;
 	uint32_t last_nonce;
 	uint32_t last_nonce;
@@ -838,7 +837,7 @@ CPUSearch:
 
 
 	/* if nonce found, submit work */
 	/* if nonce found, submit work */
 	if (unlikely(rc)) {
 	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);
 		submit_work_async(work, NULL);
 		work->blk.nonce = last_nonce + 1;
 		work->blk.nonce = last_nonce + 1;
 		goto CPUSearch;
 		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];
 	struct epoll_event evr[2];
 	int epoll_timeout = ICARUS_READ_FAULT_DECISECONDS * 100;
 	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);
 	epollfd = epoll_create(2);
 	if (epollfd != -1) {
 	if (epollfd != -1) {
 		if (-1 == epoll_ctl(epollfd, EPOLL_CTL_ADD, fd, &ev)) {
 		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;
 			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");
 				applog(LOG_ERR, "Icarus: Error adding work restart fd to epoll");
 			else
 			else
 			{
 			{
@@ -231,8 +231,7 @@ int icarus_gets(unsigned char *buf, int fd, struct timeval *tv_finish, struct th
 			else
 			else
 			{
 			{
 				if (ret)
 				if (ret)
-					// work restart trigger
-					(void)read(thr->work_restart_fd, buf, read_amount);
+					notifier_read(thr->work_restart_notifier);
 				ret = 0;
 				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;
 	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)
 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;
 	icarus->threads = 1;
 	add_cgpu(icarus);
 	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);
 		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;
 	icarus->cgpu_data = info;
 
 
@@ -696,7 +695,7 @@ static bool icarus_prepare(struct thr_info *thr)
 	if (epollfd != -1)
 	if (epollfd != -1)
 	{
 	{
 		close(epollfd);
 		close(epollfd);
-		thr->work_restart_fd = 0;
+		notifier_init(thr->work_restart_notifier);
 	}
 	}
 #endif
 #endif
 
 
@@ -711,7 +710,7 @@ static bool icarus_reopen(struct cgpu_info *icarus, struct icarus_state *state,
 	icarus_close(icarus->device_fd);
 	icarus_close(icarus->device_fd);
 	*fdp = icarus->device_fd = icarus_open(icarus->device_path, info->baud);
 	*fdp = icarus->device_fd = icarus_open(icarus->device_path, info->baud);
 	if (unlikely(-1 == *fdp)) {
 	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);
 		dev_error(icarus, REASON_DEV_COMMS_ERROR);
 		state->firstrun = true;
 		state->firstrun = true;
 		return false;
 		return false;
@@ -739,9 +738,9 @@ static bool icarus_start_work(struct thr_info *thr, const unsigned char *ob_bin)
 
 
 	if (opt_debug) {
 	if (opt_debug) {
 		ob_hex = bin2hex(ob_bin, 64);
 		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);
 		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)
 	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))) {
 	   || 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
 		// 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;
 		ob_bin[56] = 0;
 	}
 	}
 	rev(ob_bin, 32);
 	rev(ob_bin, 32);
@@ -885,9 +884,8 @@ static int64_t icarus_scanhash(struct thr_info *thr, struct work *work,
 			estimate_hashes = 0xffffffff;
 			estimate_hashes = 0xffffffff;
 
 
 		if (opt_debug) {
 		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,
 					(uint64_t)estimate_hashes,
 					(int64_t)elapsed.tv_sec, (unsigned long)elapsed.tv_usec);
 					(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;
 	hash_count *= info->fpga_count;
 
 
 	if (opt_debug) {
 	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,
 				nonce,
 				(uint64_t)hash_count,
 				(uint64_t)hash_count,
 				(int64_t)elapsed.tv_sec, (unsigned long)elapsed.tv_usec);
 				(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) {
 	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);
 		int MHs = (double)hash_count / ((double)elapsed.tv_sec * 1e6 + (double)elapsed.tv_usec);
 		--info->do_default_detection;
 		--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) {
 		if (MHs <= 370 || MHs > 420) {
 			// Not a real Icarus: enable short timing
 			// 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->timing_mode = MODE_SHORT;
 			info->do_icarus_timing = true;
 			info->do_icarus_timing = true;
 			info->do_default_detection = 0;
 			info->do_default_detection = 0;
@@ -938,18 +935,19 @@ static int64_t icarus_scanhash(struct thr_info *thr, struct work *work,
 		if (MHs <= 380) {
 		if (MHs <= 380) {
 			// Real Icarus?
 			// Real Icarus?
 			if (!info->do_default_detection) {
 			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;
 				info->read_count = (int)(info->fullnonce * TIME_FACTOR) - 1;
 			}
 			}
 		}
 		}
 		else
 		else
 		if (MHs <= 420) {
 		if (MHs <= 420) {
 			// Enterpoint Cairnsmore1
 			// 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);
 			convert_icarus_to_cairnsmore(icarus);
 			info->do_default_detection = 0;
 			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)
 			else if (info->timing_mode == MODE_SHORT)
 				info->do_icarus_timing = false;
 				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++;
 		info->history_count++;
 		gettimeofday(&tv_history_finish, NULL);
 		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)
 	if (fd != -1)
 		serial_close(fd);
 		serial_close(fd);
 	if (modminer) {
 	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;
 	va_list ap;
@@ -139,6 +140,7 @@ modminer_detect_one(const char *devpath)
 	modminer->device_path = strdup(devpath);
 	modminer->device_path = strdup(devpath);
 	modminer->device_fd = -1;
 	modminer->device_fd = -1;
 	modminer->deven = DEV_ENABLED;
 	modminer->deven = DEV_ENABLED;
+	modminer->procs = buf[0];
 	modminer->threads = buf[0];
 	modminer->threads = buf[0];
 	modminer->name = devname;
 	modminer->name = devname;
 	modminer->cutofftemp = 85;
 	modminer->cutofftemp = 85;
@@ -167,20 +169,20 @@ modminer_detect()
 static bool
 static bool
 modminer_reopen(struct cgpu_info*modminer)
 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);
 	int fd = serial_open(modminer->device_path, 0, 10, true);
 	if (unlikely(-1 == fd)) {
 	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;
 		return false;
 	}
 	}
-	modminer->device_fd = fd;
+	modminer->device->device_fd = fd;
 	return true;
 	return true;
 }
 }
 #define safebailout() do {  \
 #define safebailout() do {  \
 	bool _safebailoutrv;  \
 	bool _safebailoutrv;  \
 	state->work_running = false;  \
 	state->work_running = false;  \
 	_safebailoutrv = modminer_reopen(modminer);  \
 	_safebailoutrv = modminer_reopen(modminer);  \
-	mutex_unlock(&modminer->device_mutex);  \
+	mutex_unlock(mutexp);  \
 	return _safebailoutrv ? 0 : -1;  \
 	return _safebailoutrv ? 0 : -1;  \
 } while(0)
 } while(0)
 
 
@@ -207,9 +209,9 @@ FD_ZERO(&fds); \
 FD_SET(fd, &fds);  \
 FD_SET(fd, &fds);  \
 select(fd+1, &fds, NULL, NULL, NULL);  \
 select(fd+1, &fds, NULL, NULL, NULL);  \
 	if (1 != read(fd, buf, 1))  \
 	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)  \
 	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)
 } while(0)
 
 
 static bool
 static bool
@@ -220,14 +222,14 @@ modminer_fpga_upload_bitstream(struct cgpu_info*modminer)
 	char buf[0x100];
 	char buf[0x100];
 	unsigned long len, flen;
 	unsigned long len, flen;
 	char fpgaid = FPGAID_ALL;
 	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)
 	if (!f)
 		return false;
 		return false;
 
 
 	flen = len;
 	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[0] = MODMINER_PROGRAM;
 	buf[1] = fpgaid;
 	buf[1] = fpgaid;
 	buf[2] = (len >>  0) & 0xff;
 	buf[2] = (len >>  0) & 0xff;
@@ -235,27 +237,27 @@ modminer_fpga_upload_bitstream(struct cgpu_info*modminer)
 	buf[4] = (len >> 16) & 0xff;
 	buf[4] = (len >> 16) & 0xff;
 	buf[5] = (len >> 24) & 0xff;
 	buf[5] = (len >> 24) & 0xff;
 	if (6 != write(fd, buf, 6))
 	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");
 	status_read("cmd reply");
 	ssize_t buflen;
 	ssize_t buflen;
 	char nextstatus = 10;
 	char nextstatus = 10;
 	while (len) {
 	while (len) {
 		buflen = len < 32 ? len : 32;
 		buflen = len < 32 ? len : 32;
 		if (fread(buf, buflen, 1, f) != 1)
 		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)
 		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);
 		state->pdone = 100 - ((len * 100) / flen);
 		if (state->pdone >= nextstatus)
 		if (state->pdone >= nextstatus)
 		{
 		{
 			nextstatus += 10;
 			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");
 		status_read("status");
 		len -= buflen;
 		len -= buflen;
 	}
 	}
 	status_read("final status");
 	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;
 	return true;
 }
 }
@@ -265,10 +267,10 @@ modminer_device_prepare(struct cgpu_info *modminer)
 {
 {
 	int fd = serial_open(modminer->device_path, 0, 10, true);
 	int fd = serial_open(modminer->device_path, 0, 10, true);
 	if (unlikely(-1 == fd))
 	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;
 	struct timeval now;
 	gettimeofday(&now, NULL);
 	gettimeofday(&now, NULL);
@@ -282,17 +284,18 @@ modminer_device_prepare(struct cgpu_info *modminer)
 static bool
 static bool
 modminer_fpga_prepare(struct thr_info *thr)
 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
 	// 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;
 		return false;
 
 
 	struct modminer_fpga_state *state;
 	struct modminer_fpga_state *state;
 	state = thr->cgpu_data = calloc(1, sizeof(struct modminer_fpga_state));
 	state = thr->cgpu_data = calloc(1, sizeof(struct modminer_fpga_state));
 	dclk_prepare(&state->dclk);
 	dclk_prepare(&state->dclk);
 	state->next_work_cmd[0] = MODMINER_SEND_WORK;
 	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;
 	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 cgpu_info*modminer = thr->cgpu;
 	struct modminer_fpga_state *state = thr->cgpu_data;
 	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;
 	int fd;
 	unsigned char cmd[6], buf[1];
 	unsigned char cmd[6], buf[1];
 	unsigned char clk;
 	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';
 	cmd[3] = cmd[4] = cmd[5] = '\0';
 
 
 	if (needlock)
 	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))
 	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)
 	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)
 	if (needlock)
-		mutex_unlock(&modminer->device_mutex);
+		mutex_unlock(mutexp);
 
 
 	if (buf[0])
 	if (buf[0])
 		state->dclk.freqM = clk / 2;
 		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)
 static bool modminer_dclk_change_clock(struct thr_info*thr, int multiplier)
 {
 {
 	struct cgpu_info *modminer = thr->cgpu;
 	struct cgpu_info *modminer = thr->cgpu;
-	char fpgaid = thr->device_thread;
 	struct modminer_fpga_state *state = thr->cgpu_data;
 	struct modminer_fpga_state *state = thr->cgpu_data;
 	uint8_t oldFreq = state->dclk.freqM;
 	uint8_t oldFreq = state->dclk.freqM;
 	signed char delta = (multiplier - oldFreq) * 2;
 	signed char delta = (multiplier - oldFreq) * 2;
 	if (unlikely(!modminer_change_clock(thr, true, delta)))
 	if (unlikely(!modminer_change_clock(thr, true, delta)))
 		return false;
 		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;
 	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)
 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};
 	char cmd[2] = {MODMINER_CHECK_WORK, fpgaid};
 	
 	
 	if (write(fd, cmd, 2) != 2) {
 	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;
 		return false;
 	}
 	}
 	if (4 != serial_read(fd, nonce, 4)) {
 	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;
 		return false;
 	}
 	}
 	
 	
@@ -382,50 +383,51 @@ modminer_fpga_init(struct thr_info *thr)
 	struct cgpu_info *modminer = thr->cgpu;
 	struct cgpu_info *modminer = thr->cgpu;
 	struct modminer_fpga_state *state = thr->cgpu_data;
 	struct modminer_fpga_state *state = thr->cgpu_data;
 	int fd;
 	int fd;
-	char fpgaid = thr->device_thread;
+	char fpgaid = modminer->proc_id;
+	pthread_mutex_t *mutexp = &modminer->device->device_mutex;
 	uint32_t nonce;
 	uint32_t nonce;
 
 
 	unsigned char cmd[2], buf[4];
 	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) {
 	if (fd == -1) {
 		// Died in another thread...
 		// Died in another thread...
-		mutex_unlock(&modminer->device_mutex);
+		mutex_unlock(mutexp);
 		return false;
 		return false;
 	}
 	}
 
 
 	cmd[0] = MODMINER_GET_USERCODE;
 	cmd[0] = MODMINER_GET_USERCODE;
 	cmd[1] = fpgaid;
 	cmd[1] = fpgaid;
 	if (write(fd, cmd, 2) != 2)
 	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)
 	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)) {
 	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))
 		if (!modminer_fpga_upload_bitstream(modminer))
 			return false;
 			return false;
 	} else if (opt_force_dev_init && modminer->status == LIFE_INIT) {
 	} 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))
 		if (!modminer_fpga_upload_bitstream(modminer))
 			return false;
 			return false;
 	}
 	}
 	else
 	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->pdone = 101;
 
 
 	state->dclk.freqM = MODMINER_MAX_CLOCK / 2 + 1;  // Will be reduced immediately
 	state->dclk.freqM = MODMINER_MAX_CLOCK / 2 + 1;  // Will be reduced immediately
 	while (1) {
 	while (1) {
 		if (state->dclk.freqM <= MODMINER_MIN_CLOCK / 2)
 		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;
 		--state->dclk.freqM;
 		if (!modminer_change_clock(thr, false, 0))
 		if (!modminer_change_clock(thr, false, 0))
 			// MCU rejected assignment
 			// MCU rejected assignment
 			continue;
 			continue;
 		if (!_modminer_get_nonce(modminer, fpgaid, &nonce))
 		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))
 		if (!memcmp(&nonce, "\x00\xff\xff\xff", 4))
 			// MCU took assignment, but disabled FPGA
 			// MCU took assignment, but disabled FPGA
 			continue;
 			continue;
@@ -435,39 +437,66 @@ modminer_fpga_init(struct thr_info *thr)
 	state->dclk.freqMaxM = state->dclk.freqM;
 	state->dclk.freqMaxM = state->dclk.freqM;
 	if (MODMINER_DEF_CLOCK / 2 < state->dclk.freqM) {
 	if (MODMINER_DEF_CLOCK / 2 < state->dclk.freqM) {
 		if (!modminer_change_clock(thr, false, -(state->dclk.freqM * 2 - MODMINER_DEF_CLOCK)))
 		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;
 	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;
 	thr->primary_thread = true;
 
 
 	return 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] = "               | ";
 	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) {
 	if (pdone != 101) {
 		sprintf(&info[1], "%3d%%", pdone);
 		sprintf(&info[1], "%3d%%", pdone);
 		info[5] = ' ';
 		info[5] = ' ';
 		strcat(buf, info);
 		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)
 	if (tc > 4)
 		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;
 		struct modminer_fpga_state *state = thr->cgpu_data;
 		unsigned char temp = state->temp;
 		unsigned char temp = state->temp;
 
 
@@ -498,8 +527,8 @@ static void modminer_get_temperature(struct cgpu_info *modminer, struct thr_info
 		return;
 		return;
 #endif
 #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 cmd[2] = {MODMINER_TEMP1, fpgaid};
 	char temperature;
 	char temperature;
 
 
@@ -513,8 +542,8 @@ static void modminer_get_temperature(struct cgpu_info *modminer, struct thr_info
 					state->last_cutoff_reduced = now;
 					state->last_cutoff_reduced = now;
 					int oldFreq = state->dclk.freqM;
 					int oldFreq = state->dclk.freqM;
 					if (modminer_reduce_clock(thr, false))
 					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 > state->dclk.freqM ? "dropped" : "raised "),
 						       oldFreq * 2, state->dclk.freqM * 2,
 						       oldFreq * 2, state->dclk.freqM * 2,
 						       temperature
 						       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)
 static bool modminer_get_stats(struct cgpu_info *modminer)
 {
 {
+	pthread_mutex_t *mutexp = &modminer->device->device_mutex;
 	int hottest = 0;
 	int hottest = 0;
 	bool get_temp = (modminer->deven != DEV_ENABLED);
 	bool get_temp = (modminer->deven != DEV_ENABLED);
 	// Getting temperature more efficiently while 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--; ) {
 	for (int i = modminer->threads; i--; ) {
 		struct thr_info*thr = modminer->thr[i];
 		struct thr_info*thr = modminer->thr[i];
 		struct modminer_fpga_state *state = thr->cgpu_data;
 		struct modminer_fpga_state *state = thr->cgpu_data;
 		if (get_temp)
 		if (get_temp)
+		{
+			mutex_lock(mutexp);
 			modminer_get_temperature(modminer, thr);
 			modminer_get_temperature(modminer, thr);
+			mutex_unlock(mutexp);
+		}
 		int temp = state->temp;
 		int temp = state->temp;
 		if (temp > hottest)
 		if (temp > hottest)
 			hottest = temp;
 			hottest = temp;
@@ -559,25 +592,24 @@ static struct api_data*
 get_modminer_api_extra_device_status(struct cgpu_info*modminer)
 get_modminer_api_extra_device_status(struct cgpu_info*modminer)
 {
 {
 	struct api_data*root = NULL;
 	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;
 	return root;
 }
 }
@@ -600,32 +632,32 @@ modminer_start_work(struct thr_info*thr)
 fd_set fds;
 fd_set fds;
 	struct cgpu_info*modminer = thr->cgpu;
 	struct cgpu_info*modminer = thr->cgpu;
 	struct modminer_fpga_state *state = thr->cgpu_data;
 	struct modminer_fpga_state *state = thr->cgpu_data;
-	char fpgaid = thr->device_thread;
+	pthread_mutex_t *mutexp = &modminer->device->device_mutex;
 	int fd;
 	int fd;
 
 
 	char buf[1];
 	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 (unlikely(fd == -1)) {
 		if (!modminer_reopen(modminer)) {
 		if (!modminer_reopen(modminer)) {
-			mutex_unlock(&modminer->device_mutex);
+			mutex_unlock(mutexp);
 			return false;
 			return false;
 		}
 		}
-		fd = modminer->device_fd;
+		fd = modminer->device->device_fd;
 	}
 	}
 
 
 	if (46 != write(fd, state->next_work_cmd, 46))
 	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);
 	gettimeofday(&state->tv_workstart, NULL);
 	state->hashes = 0;
 	state->hashes = 0;
 	status_read("start work");
 	status_read("start work");
-	mutex_unlock(&modminer->device_mutex);
+	mutex_unlock(mutexp);
 	if (opt_debug) {
 	if (opt_debug) {
 		char *xdata = bin2hex(state->running_work.data, 80);
 		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);
 		free(xdata);
 	}
 	}
 
 
@@ -645,7 +677,8 @@ modminer_process_results(struct thr_info*thr)
 {
 {
 	struct cgpu_info*modminer = thr->cgpu;
 	struct cgpu_info*modminer = thr->cgpu;
 	struct modminer_fpga_state *state = thr->cgpu_data;
 	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;
 	struct work *work = &state->running_work;
 
 
 	uint32_t nonce;
 	uint32_t nonce;
@@ -653,27 +686,27 @@ modminer_process_results(struct thr_info*thr)
 	int immediate_bad_nonces = 0, immediate_nonces = 0;
 	int immediate_bad_nonces = 0, immediate_nonces = 0;
 	bool bad;
 	bool bad;
 
 
-	mutex_lock(&modminer->device_mutex);
+	mutex_lock(mutexp);
 	modminer_get_temperature(modminer, thr);
 	modminer_get_temperature(modminer, thr);
 
 
 	iter = 200;
 	iter = 200;
 	while (1) {
 	while (1) {
 		if (!_modminer_get_nonce(modminer, fpgaid, &nonce))
 		if (!_modminer_get_nonce(modminer, fpgaid, &nonce))
 			safebailout();
 			safebailout();
-		mutex_unlock(&modminer->device_mutex);
+		mutex_unlock(mutexp);
 		if (memcmp(&nonce, "\xff\xff\xff\xff", 4)) {
 		if (memcmp(&nonce, "\xff\xff\xff\xff", 4)) {
 			nonce = le32toh(nonce);
 			nonce = le32toh(nonce);
 			bad = !test_nonce(work, nonce, false);
 			bad = !test_nonce(work, nonce, false);
 			++immediate_nonces;
 			++immediate_nonces;
 			if (!bad)
 			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));
 				       NONCE_CHARS(nonce));
 			else
 			else
 			if (test_nonce(&state->last_work, nonce, false))
 			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));
 				       NONCE_CHARS(nonce));
 				work = &state->last_work;
 				work = &state->last_work;
 				bad = false;
 				bad = false;
@@ -684,8 +717,8 @@ modminer_process_results(struct thr_info*thr)
 				submit_nonce(thr, work, nonce);
 				submit_nonce(thr, work, nonce);
 			}
 			}
 			else {
 			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));
 				       NONCE_CHARS(nonce));
 				++hw_errors;
 				++hw_errors;
 				++modminer->hw_errors;
 				++modminer->hw_errors;
@@ -698,7 +731,7 @@ modminer_process_results(struct thr_info*thr)
 		nmsleep(1);
 		nmsleep(1);
 		if (work_restart(thr))
 		if (work_restart(thr))
 			break;
 			break;
-		mutex_lock(&modminer->device_mutex);
+		mutex_lock(mutexp);
 	}
 	}
 
 
 	struct timeval tv_workend, elapsed;
 	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);
 	uint64_t hashes = (uint64_t)state->dclk.freqM * 2 * (((uint64_t)elapsed.tv_sec * 1000000) + elapsed.tv_usec);
 	if (hashes > 0xffffffff)
 	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;
 		hashes = 0xffffffff;
 	}
 	}
 	if (hashes <= state->hashes)
 	if (hashes <= state->hashes)
@@ -779,20 +812,8 @@ static char *modminer_set_device(struct cgpu_info *modminer, char *option, char
 		return replybuf;
 		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) {
 		if (!setting || !*setting) {
 			sprintf(replybuf, "missing clock 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;
 		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;
 		return NULL;
 	}
 	}
 
 
@@ -834,6 +852,7 @@ struct device_api modminer_api = {
 	.dname = "modminer",
 	.dname = "modminer",
 	.name = "MMQ",
 	.name = "MMQ",
 	.api_detect = modminer_detect,
 	.api_detect = modminer_detect,
+	.get_dev_statline_before = get_modminer_dev_statline_before,
 	.get_statline_before = get_modminer_statline_before,
 	.get_statline_before = get_modminer_statline_before,
 	.get_stats = modminer_get_stats,
 	.get_stats = modminer_get_stats,
 	.get_api_extra_device_status = get_modminer_api_extra_device_status,
 	.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;
 		thr->pause = cgpu->dynamic;
 		if (!cgpu->dynamic && cgpu->deven != DEV_DISABLED)
 		if (!cgpu->dynamic && cgpu->deven != DEV_DISABLED)
-			tq_push(thr->q, &ping);
+			mt_enable(thr);
 	}
 	}
 }
 }
 
 
@@ -862,8 +862,9 @@ retry:
 			mhash_base = false;
 			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->accepted, cgpu->rejected, cgpu->hw_errors,
 			cgpu->utility, cgpu->intensity);
 			cgpu->utility, cgpu->intensity);
 #ifdef HAVE_ADL
 #ifdef HAVE_ADL
@@ -956,26 +957,17 @@ retry:
 			wlogprint("Invalid selection\n");
 			wlogprint("Invalid selection\n");
 			goto retry;
 			goto retry;
 		}
 		}
+		cgpu = &gpus[selected];
 		if (gpus[selected].deven != DEV_DISABLED) {
 		if (gpus[selected].deven != DEV_DISABLED) {
 			wlogprint("Device already enabled\n");
 			wlogprint("Device already enabled\n");
 			goto retry;
 			goto retry;
 		}
 		}
 		gpus[selected].deven = DEV_ENABLED;
 		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;
 		goto retry;
 	} if (!strncasecmp(&input, "d", 1)) {
 	} if (!strncasecmp(&input, "d", 1)) {
 		if (selected)
 		if (selected)
@@ -1332,7 +1324,7 @@ void *reinit_gpu(void *userdata)
 	struct timeval now;
 	struct timeval now;
 	char name[256];
 	char name[256];
 	int thr_id;
 	int thr_id;
-	int gpu;
+	int i;
 
 
 	pthread_detach(pthread_self());
 	pthread_detach(pthread_self());
 	RenameThread("reinit_gpu");
 	RenameThread("reinit_gpu");
@@ -1348,21 +1340,10 @@ select_cgpu:
 		goto out;
 		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;
 		thr->rolling = thr->cgpu->rolling = 0;
 		/* Reports the last time we tried to revive a sick GPU */
 		/* 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);
 			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;
 		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;
 		virtual_gpu = cgpu->virtual_gpu;
 		/* Lose this ram cause we may get stuck here! */
 		/* Lose this ram cause we may get stuck here! */
@@ -1412,16 +1390,7 @@ select_cgpu:
 	gettimeofday(&now, NULL);
 	gettimeofday(&now, NULL);
 	get_datestamp(sel_cgpu->init, &now);
 	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;
 	goto select_cgpu;
 out:
 out:

+ 218 - 126
driver-x6500.c

@@ -19,6 +19,7 @@
 #include <libusb.h>
 #include <libusb.h>
 
 
 #include "compat.h"
 #include "compat.h"
+#include "deviceapi.h"
 #include "dynclock.h"
 #include "dynclock.h"
 #include "jtag.h"
 #include "jtag.h"
 #include "logging.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);
 	mutex_init(&x6500->device_mutex);
 	x6500->device_path = strdup(serial);
 	x6500->device_path = strdup(serial);
 	x6500->deven = DEV_ENABLED;
 	x6500->deven = DEV_ENABLED;
-	x6500->threads = 2;
+	x6500->threads = 1;
+	x6500->procs = 2;
 	x6500->name = strdup(product);
 	x6500->name = strdup(product);
 	x6500->cutofftemp = 85;
 	x6500->cutofftemp = 85;
 	x6500->cgpu_data = dev;
 	x6500->cgpu_data = dev;
@@ -154,11 +156,11 @@ static void x6500_detect()
 
 
 static bool x6500_prepare(struct thr_info *thr)
 static bool x6500_prepare(struct thr_info *thr)
 {
 {
-	if (thr->device_thread)
+	struct cgpu_info *x6500 = thr->cgpu;
+	
+	if (x6500->proc_id)
 		return true;
 		return true;
 	
 	
-	struct cgpu_info *x6500 = thr->cgpu;
-	mutex_init(&x6500->device_mutex);
 	struct ft232r_device_handle *ftdi = ft232r_open(x6500->cgpu_data);
 	struct ft232r_device_handle *ftdi = ft232r_open(x6500->cgpu_data);
 	x6500->device_ft232r = NULL;
 	x6500->device_ft232r = NULL;
 	if (!ftdi)
 	if (!ftdi)
@@ -176,13 +178,19 @@ static bool x6500_prepare(struct thr_info *thr)
 	jtag_a->ftdi = ftdi;
 	jtag_a->ftdi = ftdi;
 	x6500->cgpu_data = jtag_a;
 	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;
 	return true;
 }
 }
 
 
 struct x6500_fpga_data {
 struct x6500_fpga_data {
 	struct jtag_port jtag;
 	struct jtag_port jtag;
-	struct work prevwork;
-	struct timeval tv_workstart;
+	struct timeval tv_hashstart;
+	int64_t hashes_left;
 
 
 	struct dclk_data dclk;
 	struct dclk_data dclk;
 	uint8_t freqMaxMaxM;
 	uint8_t freqMaxMaxM;
@@ -191,6 +199,8 @@ struct x6500_fpga_data {
 	time_t last_cutoff_reduced;
 	time_t last_cutoff_reduced;
 
 
 	float temp;
 	float temp;
+	
+	uint32_t prepwork_last_register;
 };
 };
 
 
 #define bailout2(...) do {  \
 #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;
 	unsigned char *pdone = (unsigned char*)x6500->cgpu_data - 1;
 	struct ft232r_device_handle *ftdi = jp1->a->ftdi;
 	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)
 	if (!f)
 		return false;
 		return false;
 
 
 	flen = len;
 	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;
 	x6500->status = LIFE_INIT;
 	
 	
 	// "Magic" jtag_port configured to access both FPGAs concurrently
 	// "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
 			i = 0xd0;  // Re-set JPROGRAM while reading status
 			jtag_read(jp, JTAG_REG_IR, &i, 6);
 			jtag_read(jp, JTAG_REG_IR, &i, 6);
 		} while (i & 8);
 		} 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);
 	x6500_jtag_set(jp, 0x11);
 	jtag_write(jp, JTAG_REG_IR, "\xa0", 6);  // CFG_IN
 	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);
 	sleep(1);
 	
 	
 	if (fread(buf, 32, 1, f) != 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);
 	jtag_swrite(jp, JTAG_REG_DR, buf, 256);
 	len -= 32;
 	len -= 32;
 	
 	
@@ -262,13 +272,13 @@ x6500_fpga_upload_bitstream(struct cgpu_info *x6500, struct jtag_port *jp1)
 	while (len) {
 	while (len) {
 		buflen = len < 32 ? len : 32;
 		buflen = len < 32 ? len : 32;
 		if (fread(buf, buflen, 1, f) != 1)
 		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);
 		jtag_swrite_more(jp, buf, buflen * 8, len == (unsigned long)buflen);
 		*pdone = 100 - ((len * 100) / flen);
 		*pdone = 100 - ((len * 100) / flen);
 		if (*pdone >= nextstatus)
 		if (*pdone >= nextstatus)
 		{
 		{
 			nextstatus += 25;
 			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;
 		len -= buflen;
 	}
 	}
@@ -290,7 +300,7 @@ x6500_fpga_upload_bitstream(struct cgpu_info *x6500, struct jtag_port *jp1)
 	if (!(i & 4))
 	if (!(i & 4))
 		return false;
 		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;
 	*pdone = 101;
 
 
 	return true;
 	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)
 static bool x6500_dclk_change_clock(struct thr_info *thr, int multiplier)
 {
 {
 	struct cgpu_info *x6500 = thr->cgpu;
 	struct cgpu_info *x6500 = thr->cgpu;
-	char fpgaid = thr->device_thread;
 	struct x6500_fpga_data *fpga = thr->cgpu_data;
 	struct x6500_fpga_data *fpga = thr->cgpu_data;
 	uint8_t oldFreq = fpga->dclk.freqM;
 	uint8_t oldFreq = fpga->dclk.freqM;
 
 
-	mutex_lock(&x6500->device_mutex);
 	if (!x6500_change_clock(thr, multiplier)) {
 	if (!x6500_change_clock(thr, multiplier)) {
-		mutex_unlock(&x6500->device_mutex);
 		return false;
 		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;
 	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 cgpu_info *x6500 = thr->cgpu;
 	struct ft232r_device_handle *ftdi = x6500->device_ft232r;
 	struct ft232r_device_handle *ftdi = x6500->device_ft232r;
+
+	for ( ; x6500; x6500 = x6500->next_proc)
+	{
+		thr = x6500->thr[0];
+
 	struct x6500_fpga_data *fpga;
 	struct x6500_fpga_data *fpga;
 	struct jtag_port *jp;
 	struct jtag_port *jp;
-	int fpgaid = thr->device_thread;
+	int fpgaid = x6500->proc_id;
 	uint8_t pinoffset = fpgaid ? 0x10 : 1;
 	uint8_t pinoffset = fpgaid ? 0x10 : 1;
 	unsigned char buf[4] = {0};
 	unsigned char buf[4] = {0};
 	int i;
 	int i;
@@ -348,19 +357,16 @@ static bool x6500_fpga_init(struct thr_info *thr)
 	x6500_jtag_set(jp, pinoffset);
 	x6500_jtag_set(jp, pinoffset);
 	thr->cgpu_data = fpga;
 	thr->cgpu_data = fpga;
 	
 	
-	mutex_lock(&x6500->device_mutex);
 	if (!jtag_reset(jp)) {
 	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;
 		return false;
 	}
 	}
 	
 	
 	i = jtag_detect(jp);
 	i = jtag_detect(jp);
 	if (i != 1) {
 	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;
 		return false;
 	}
 	}
 	
 	
@@ -369,53 +375,52 @@ static bool x6500_fpga_init(struct thr_info *thr)
 	 && jtag_read (jp, JTAG_REG_DR, buf, 32)
 	 && jtag_read (jp, JTAG_REG_DR, buf, 32)
 	 && jtag_reset(jp)
 	 && 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;
 		return false;
 	}
 	}
 	
 	
 	if (memcmp(buf, X6500_BITSTREAM_USERID, 4)) {
 	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))
 		if (!x6500_fpga_upload_bitstream(x6500, jp))
 			return false;
 			return false;
 	} else if (opt_force_dev_init && x6500->status == LIFE_INIT) {
 	} 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))
 		if (!x6500_fpga_upload_bitstream(x6500, jp))
 			return false;
 			return false;
 	} else
 	} 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);
 	dclk_prepare(&fpga->dclk);
 	x6500_change_clock(thr, X6500_DEFAULT_CLOCK / 2);
 	x6500_change_clock(thr, X6500_DEFAULT_CLOCK / 2);
 	for (i = 0; 0xffffffff != x6500_get_register(jp, 0xE); ++i)
 	for (i = 0; 0xffffffff != x6500_get_register(jp, 0xE); ++i)
 	{}
 	{}
-	mutex_unlock(&x6500->device_mutex);
 
 
 	if (i)
 	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->dclk.minGoodSamples = 3;
 	fpga->freqMaxMaxM =
 	fpga->freqMaxMaxM =
 	fpga->dclk.freqMaxM = X6500_MAXIMUM_CLOCK / 2;
 	fpga->dclk.freqMaxM = X6500_MAXIMUM_CLOCK / 2;
 	fpga->dclk.freqMDefault = fpga->dclk.freqM;
 	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,
 	       fpga->dclk.freqM * 2,
 	       X6500_MINIMUM_CLOCK,
 	       X6500_MINIMUM_CLOCK,
 	       fpga->dclk.freqMaxM * 2);
 	       fpga->dclk.freqMaxM * 2);
 
 
+	}
+
 	return true;
 	return true;
 }
 }
 
 
 static 
 static 
 void x6500_get_temperature(struct cgpu_info *x6500)
 void x6500_get_temperature(struct cgpu_info *x6500)
 {
 {
-
 	struct x6500_fpga_data *fpga = x6500->thr[0]->cgpu_data;
 	struct x6500_fpga_data *fpga = x6500->thr[0]->cgpu_data;
 	struct jtag_port *jp = &fpga->jtag;
 	struct jtag_port *jp = &fpga->jtag;
 	struct ft232r_device_handle *ftdi = jp->a->ftdi;
 	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);
 	ft232r_purge_buffers(jp->a->ftdi, FTDI_PURGE_BOTH);
 	jp->a->bufread = 0;
 	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;
 		fpga = thr->cgpu_data;
 
 
 		if (!fpga) continue;
 		if (!fpga) continue;
@@ -487,8 +493,8 @@ void x6500_get_temperature(struct cgpu_info *x6500)
 				fpga->last_cutoff_reduced = now;
 				fpga->last_cutoff_reduced = now;
 				int oldFreq = fpga->dclk.freqM;
 				int oldFreq = fpga->dclk.freqM;
 				if (x6500_change_clock(thr, oldFreq - 1))
 				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,
 					       oldFreq * 2, fpga->dclk.freqM * 2,
 					       fpga->temp
 					       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)
 static bool x6500_get_stats(struct cgpu_info *x6500)
 {
 {
 	float hottest = 0;
 	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--; ) {
 	for (int i = x6500->threads; i--; ) {
@@ -531,20 +549,49 @@ static bool x6500_get_stats(struct cgpu_info *x6500)
 	return true;
 	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] = "               | ";
 	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);
 	unsigned char pdone = *((unsigned char*)x6500->cgpu_data - 1);
 	if (pdone != 101) {
 	if (pdone != 101) {
 		sprintf(&info[1], "%3d%%", pdone);
 		sprintf(&info[1], "%3d%%", pdone);
 		info[5] = ' ';
 		info[5] = ' ';
 		strcat(buf, info);
 		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;
 		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) {
 	if (x6500->temp) {
 		sprintf(&info[1], "%.1fC/%.1fC", fpga0->temp, fpga1->temp);
 		sprintf(&info[1], "%.1fC/%.1fC", fpga0->temp, fpga1->temp);
 		info[strlen(info)] = ' ';
 		info[strlen(info)] = ' ';
@@ -558,69 +605,119 @@ static struct api_data*
 get_x6500_api_extra_device_status(struct cgpu_info *x6500)
 get_x6500_api_extra_device_status(struct cgpu_info *x6500)
 {
 {
 	struct api_data *root = NULL;
 	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;
 	return root;
 }
 }
 
 
 static
 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 cgpu_info *x6500 = thr->cgpu;
 	struct x6500_fpga_data *fpga = thr->cgpu_data;
 	struct x6500_fpga_data *fpga = thr->cgpu_data;
 	struct jtag_port *jp = &fpga->jtag;
 	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)
 	for (int i = 1, j = 0; i < 9; ++i, j += 4)
 		x6500_set_register(jp, i, fromlebytes(work->midstate, j));
 		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_set_register(jp, i, fromlebytes(work->data, j));
 
 
+	x6500_get_temperature(x6500);
+	
 	ft232r_flush(jp->a->ftdi);
 	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) {
 	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);
 		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
 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;
 	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;
 	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;
 	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 cgpu_info *x6500 = thr->cgpu;
 	struct x6500_fpga_data *fpga = thr->cgpu_data;
 	struct x6500_fpga_data *fpga = thr->cgpu_data;
 	struct jtag_port *jtag = &fpga->jtag;
 	struct jtag_port *jtag = &fpga->jtag;
-	char fpgaid = thr->device_thread;
 
 
 	struct timeval tv_now;
 	struct timeval tv_now;
 	int64_t hashes;
 	int64_t hashes;
@@ -638,27 +734,25 @@ int64_t x6500_process_results(struct thr_info *thr, struct work *work)
 	bool bad;
 	bool bad;
 
 
 	while (1) {
 	while (1) {
-		mutex_lock(&x6500->device_mutex);
 		gettimeofday(&tv_now, NULL);
 		gettimeofday(&tv_now, NULL);
 		nonce = x6500_get_register(jtag, 0xE);
 		nonce = x6500_get_register(jtag, 0xE);
-		mutex_unlock(&x6500->device_mutex);
 		if (nonce != 0xffffffff) {
 		if (nonce != 0xffffffff) {
-			bad = !test_nonce(work, nonce, false);
+			bad = !(work && test_nonce(work, nonce, false));
 			if (!bad) {
 			if (!bad) {
 				submit_nonce(thr, work, nonce);
 				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);
 				       (unsigned long)nonce);
 
 
 				dclk_gotNonces(&fpga->dclk);
 				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);
 				       (unsigned long)nonce);
 			} else {
 			} 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);
 				       (unsigned long)nonce);
 				++hw_errors;
 				++hw_errors;
 				++x6500->hw_errors;
 				++x6500->hw_errors;
@@ -671,44 +765,42 @@ int64_t x6500_process_results(struct thr_info *thr, struct work *work)
 			continue;
 			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;
 	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 = {
 struct device_api x6500_api = {
 	.dname = "x6500",
 	.dname = "x6500",
 	.name = "XBS",
 	.name = "XBS",
 	.api_detect = x6500_detect,
 	.api_detect = x6500_detect,
+	.get_dev_statline_before = get_x6500_dev_statline_before,
 	.thread_prepare = x6500_prepare,
 	.thread_prepare = x6500_prepare,
-	.thread_init = x6500_fpga_init,
+	.thread_init = x6500_thread_init,
 	.get_stats = x6500_get_stats,
 	.get_stats = x6500_get_stats,
 	.get_statline_before = get_x6500_statline_before,
 	.get_statline_before = get_x6500_statline_before,
 	.get_api_extra_device_status = get_x6500_api_extra_device_status,
 	.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,
 // 	.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;
 	struct cgpu_info *ztex;
-	char fpganame[LIBZTEX_SNSTRING_LEN+3+1];
+	char *fpganame = (char*)dev->snString;
 
 
 	ztex = calloc(1, sizeof(struct cgpu_info));
 	ztex = calloc(1, sizeof(struct cgpu_info));
 	ztex->api = &ztex_api;
 	ztex->api = &ztex_api;
 	ztex->device_ztex = dev;
 	ztex->device_ztex = dev;
-	ztex->threads = 1;
+	ztex->procs = fpgacount;
+	ztex->threads = fpgacount;
 	dev->fpgaNum = j;
 	dev->fpgaNum = j;
 	add_cgpu(ztex);
 	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;
 	return ztex;
 }
 }
@@ -80,12 +80,11 @@ static struct cgpu_info *ztex_setup(struct libztex_device *dev, int j)
 static int ztex_autodetect(void)
 static int ztex_autodetect(void)
 {
 {
 	int cnt;
 	int cnt;
-	int i,j;
+	int i;
 	int fpgacount;
 	int fpgacount;
 	int totaldevs = 0;
 	int totaldevs = 0;
 	struct libztex_dev_list **ztex_devices;
 	struct libztex_dev_list **ztex_devices;
 	struct libztex_device *ztex_master;
 	struct libztex_device *ztex_master;
-	struct libztex_device *ztex_slave;
 	struct cgpu_info *ztex;
 	struct cgpu_info *ztex;
 
 
 	cnt = libztex_scanDevices(&ztex_devices);
 	cnt = libztex_scanDevices(&ztex_devices);
@@ -95,20 +94,13 @@ static int ztex_autodetect(void)
 	for (i = 0; i < cnt; i++) {
 	for (i = 0; i < cnt; i++) {
 		ztex_master = ztex_devices[i]->dev;
 		ztex_master = ztex_devices[i]->dev;
 		ztex_master->root = ztex_master;
 		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;
 		totaldevs += fpgacount;
 
 
 		if (fpgacount > 1)
 		if (fpgacount > 1)
 			pthread_mutex_init(&ztex->device_ztex->mutex, NULL);
 			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)
 	if (cnt > 0)
@@ -367,6 +359,23 @@ static bool ztex_prepare(struct thr_info *thr)
 	gettimeofday(&now, NULL);
 	gettimeofday(&now, NULL);
 	get_datestamp(cgpu->init, &now);
 	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);
 	ztex_selectFpga(ztex);
 	if (libztex_configureFpga(ztex) != 0) {
 	if (libztex_configureFpga(ztex) != 0) {
 		libztex_resetFpga(ztex);
 		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)
 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,
 	       repr,
 	       (oldFreq > newFreq ? "dropped" : "raised "),
 	       (oldFreq > newFreq ? "dropped" : "raised "),
 	       oldFreq, newFreq,
 	       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)
 	while (maxM < data->freqMaxM && data->errorWeight[maxM + 1] > 100)
 		maxM++;
 		maxM++;
 	if ((bestM < (1.0 - DCLK_OVERHEATTHRESHOLD) * maxM) && bestM < maxM - 1) {
 	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);
 		       (1.0 - 1.0 * bestM / maxM) * 100);
 		return false;
 		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
 	/* To prevent corrupt values in FOUND from trying to read beyond the
 	 * end of the res[] array */
 	 * end of the res[] array */
 	if (unlikely(pcd->res[FOUND] & ~FOUND)) {
 	if (unlikely(pcd->res[FOUND] & ~FOUND)) {
-		applog(LOG_WARNING, "%s%d: invalid nonce count - HW error",
-				thr->cgpu->api->name, thr->cgpu->device_id);
+		applog(LOG_WARNING, "%"PRIpreprv": invalid nonce count - HW error",
+				thr->cgpu->proc_repr);
 		hw_errors++;
 		hw_errors++;
 		thr->cgpu->hw_errors++;
 		thr->cgpu->hw_errors++;
 		pcd->res[FOUND] &= FOUND;
 		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 {  \
 #define check_magic(L)  do {  \
 	if (1 != fread(buf, 1, 1, f))  \
 	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)  \
 	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)
 } while(0)
 
 
 #define read_str(eng)  do {  \
 #define read_str(eng)  do {  \
 	if (1 != fread(buf, 2, 1, f))  \
 	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];  \
 	len = (ubuf[0] << 8) | ubuf[1];  \
 	if (len >= sizeof(buf))  \
 	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))  \
 	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';  \
 	buf[len] = '\0';  \
 } while(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];
 	char buf[0x100];
 	unsigned char *ubuf = (unsigned char*)buf;
 	unsigned char *ubuf = (unsigned char*)buf;
 	unsigned long len;
 	unsigned long len;
 	char *p;
 	char *p;
 
 
-	FILE *f = open_bitstream(cgpu->api->dname, fwfile);
+	FILE *f = open_bitstream(dname, fwfile);
 	if (!f)
 	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))
 	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)
 	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))
 	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');
 	check_magic('a');
 	read_str("design name");
 	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);
 	applog(LOG_DEBUG, "  Design name: %s", buf);
 	p = strrchr(buf, ';') ?: buf;
 	p = strrchr(buf, ';') ?: buf;
 	p = strrchr(buf, '=') ?: p;
 	p = strrchr(buf, '=') ?: p;
@@ -858,11 +858,11 @@ FILE *open_xilinx_bitstream(struct cgpu_info *cgpu, const char *fwfile, unsigned
 		++p;
 		++p;
 	unsigned long fwusercode = (unsigned long)strtoll(p, &p, 16);
 	unsigned long fwusercode = (unsigned long)strtoll(p, &p, 16);
 	if (p[0] != '\0')
 	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)
 	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));
 	applog(LOG_DEBUG, "  Version: %u, build %u", (unsigned)((fwusercode >> 8) & 0xff), (unsigned)(fwusercode & 0xff));
 	check_magic('b');
 	check_magic('b');
 	read_str("part number");
 	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);
 	applog(LOG_DEBUG, "  Build time: %s", buf);
 	check_magic('e');
 	check_magic('e');
 	if (1 != fread(buf, 4, 1, f))
 	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];
 	len = ((unsigned long)ubuf[0] << 24) | ((unsigned long)ubuf[1] << 16) | (ubuf[2] << 8) | ubuf[3];
 	applog(LOG_DEBUG, "  Bitstream size: %lu", len);
 	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)
 #define serial_close(fd)  close(fd)
 
 
 extern FILE *open_bitstream(const char *dname, const char *filename);
 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
 #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)();
 	void (*api_detect)();
 
 
 	// Device-specific functions
 	// 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 (*reinit_device)(struct cgpu_info *);
 	void (*get_statline_before)(char *, struct cgpu_info *);
 	void (*get_statline_before)(char *, struct cgpu_info *);
 	void (*get_statline)(char *, struct cgpu_info *);
 	void (*get_statline)(char *, struct cgpu_info *);
@@ -283,6 +287,7 @@ struct device_api {
 
 
 	// Thread-specific functions
 	// Thread-specific functions
 	bool (*thread_prepare)(struct thr_info *);
 	bool (*thread_prepare)(struct thr_info *);
+	void (*minerloop)(struct thr_info *);
 	uint64_t (*can_limit_work)(struct thr_info *);
 	uint64_t (*can_limit_work)(struct thr_info *);
 	bool (*thread_init)(struct thr_info *);
 	bool (*thread_init)(struct thr_info *);
 	bool (*prepare_work)(struct thr_info *, struct work *);
 	bool (*prepare_work)(struct thr_info *, struct work *);
@@ -290,6 +295,17 @@ struct device_api {
 	void (*hw_error)(struct thr_info *);
 	void (*hw_error)(struct thr_info *);
 	void (*thread_shutdown)(struct thr_info *);
 	void (*thread_shutdown)(struct thr_info *);
 	void (*thread_enable)(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 {
 enum dev_enable {
@@ -349,6 +365,8 @@ struct cgminer_stats {
 	struct timeval getwork_wait;
 	struct timeval getwork_wait;
 	struct timeval getwork_wait_max;
 	struct timeval getwork_wait_max;
 	struct timeval getwork_wait_min;
 	struct timeval getwork_wait_min;
+
+	struct timeval _get_start;
 };
 };
 
 
 // Just the actual network getworks to the pool
 // Just the actual network getworks to the pool
@@ -376,12 +394,26 @@ struct cgminer_pool_stats {
 	uint64_t net_bytes_received;
 	uint64_t net_bytes_received;
 };
 };
 
 
+#define PRIprepr "-6s"
+#define PRIpreprv "s"
+
 struct cgpu_info {
 struct cgpu_info {
 	int cgminer_id;
 	int cgminer_id;
+	int device_line_id;
 	const struct device_api *api;
 	const struct device_api *api;
 	const char *devtype;
 	const char *devtype;
 	int device_id;
 	int device_id;
+	char *dev_repr;
+	char *dev_repr_ns;
 	const char *name;
 	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;
 	const char *device_path;
 	FILE *device_file;
 	FILE *device_file;
 	union {
 	union {
@@ -400,7 +432,6 @@ struct cgpu_info {
 	double avg_wait_f;
 	double avg_wait_f;
 	unsigned int avg_wait_d;
 	unsigned int avg_wait_d;
 	uint32_t nonces;
 	uint32_t nonces;
-	bool nonce_range;
 	bool polling;
 	bool polling;
 #endif
 #endif
 #if defined(USE_BITFORCE) || defined(USE_ICARUS)
 #if defined(USE_BITFORCE) || defined(USE_ICARUS)
@@ -448,8 +479,6 @@ struct cgpu_info {
 	int intervals;
 	int intervals;
 #endif
 #endif
 
 
-	bool new_work;
-
 	float temp;
 	float temp;
 	int cutofftemp;
 	int cutofftemp;
 	int targettemp;
 	int targettemp;
@@ -516,13 +545,32 @@ struct thr_info {
 	struct timeval last;
 	struct timeval last;
 	struct timeval sick;
 	struct timeval sick;
 
 
+	bool	scanhash_working;
+	uint64_t hashes_done;
+	struct timeval tv_hashes_done;
+	struct timeval tv_lastupdate;
+
 	bool	pause;
 	bool	pause;
 	time_t	getwork;
 	time_t	getwork;
 	double	rolling;
 	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;
 	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);
 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 pthread_cond_t restart_cond;
 
 
 extern void thread_reportin(struct thr_info *thr);
 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 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 int stale_wait(unsigned int mstime, struct work*, bool checkend);
 
 
 extern void kill_work(void);
 extern void kill_work(void);
 extern void app_restart(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);
 extern void reinit_device(struct cgpu_info *cgpu);
 
 
 #ifdef HAVE_ADL
 #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_nonce(work, nonce, checktarget)  (_test_nonce2(work, nonce, checktarget) == TNR_GOOD)
 #define test_nonce2(work, nonce)  (_test_nonce2(work, nonce, true))
 #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 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 tailsprintf(char *f, const char *fmt, ...);
 extern void wlogprint(const char *f, ...);
 extern void wlogprint(const char *f, ...);
 extern int curses_int(const char *query);
 extern int curses_int(const char *query);

+ 25 - 19
miner.php

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

+ 14 - 0
util.c

@@ -1829,6 +1829,8 @@ void notifier_init(notifier_t pipefd)
 
 
 void notifier_wake(notifier_t fd)
 void notifier_wake(notifier_t fd)
 {
 {
+	if (fd[1] == INVSOCK)
+		return;
 #ifdef WIN32
 #ifdef WIN32
 	(void)send(fd[1], "\0", 1, 0);
 	(void)send(fd[1], "\0", 1, 0);
 #else
 #else
@@ -1845,3 +1847,15 @@ void notifier_read(notifier_t fd)
 	(void)read(fd[0], buf, sizeof(buf));
 	(void)read(fd[0], buf, sizeof(buf));
 #endif
 #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 <curl/curl.h>
 #include <jansson.h>
 #include <jansson.h>
 
 
+#include "compat.h"
+
 #if defined(unix) || defined(__APPLE__)
 #if defined(unix) || defined(__APPLE__)
 	#include <errno.h>
 	#include <errno.h>
 	#include <sys/socket.h>
 	#include <sys/socket.h>
@@ -74,6 +76,7 @@ typedef SOCKETTYPE notifier_t[2];
 extern void notifier_init(notifier_t);
 extern void notifier_init(notifier_t);
 extern void notifier_wake(notifier_t);
 extern void notifier_wake(notifier_t);
 extern void notifier_read(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 */
 /* Align a size_t to 4 byte boundaries for fussy arches */
 static inline void align_len(size_t *len)
 static inline void align_len(size_t *len)
@@ -82,4 +85,57 @@ static inline void align_len(size_t *len)
 		*len += 4 - (*len % 4);
 		*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__ */
 #endif /* __UTIL_H__ */

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