Browse Source

Merge branch 'rpc_fulldev' into bfgminer

Luke Dashjr 12 years ago
parent
commit
ef69ec68fa
4 changed files with 270 additions and 125 deletions
  1. 55 9
      README.RPC
  2. 203 116
      api.c
  3. 1 0
      miner.h
  4. 11 0
      miner.php

+ 55 - 9
README.RPC

@@ -151,13 +151,13 @@ The list of requests - a (*) means it requires privileged access - and replies:
                               Will not report PGAs if PGA mining is disabled
                               Will not report PGAs if PGA mining is disabled
                               Will not report CPUs if CPU mining is disabled
                               Will not report CPUs if CPU mining is disabled
 
 
+ procs         DEVS           The details of each processor in the same format
+                              and details as for DEVS
+
  devscan|info  DEVS           Probes for a device specified by info, which is
  devscan|info  DEVS           Probes for a device specified by info, which is
                               the same format as the --scan-serial command line
                               the same format as the --scan-serial command line
                               option
                               option
 
 
- devdetail     DEVS           Each available device with their fixed details
-                              e.g. GPU=0,Driver=opencl,Kernel=diablo,Model=...|
-
  gpu|N         GPU            The details of a single GPU number N in the same
  gpu|N         GPU            The details of a single GPU number N in the same
                               format and details as for DEVS
                               format and details as for DEVS
 
 
@@ -167,6 +167,9 @@ The list of requests - a (*) means it requires privileged access - and replies:
                               Use 'pgacount' or 'config' first to see if there
                               Use 'pgacount' or 'config' first to see if there
                               are any
                               are any
 
 
+ proc|N        PGA            The details of a single processor number N in the
+                              same format and details as for DEVS
+
  cpu|N         CPU            The details of a single CPU number N in the same
  cpu|N         CPU            The details of a single CPU number N in the same
                               format and details as for DEVS
                               format and details as for DEVS
                               This is only available if CPU mining is enabled
                               This is only available if CPU mining is enabled
@@ -177,6 +180,8 @@ The list of requests - a (*) means it requires privileged access - and replies:
  pgacount      PGAS           Count=N| <- the number of PGAs
  pgacount      PGAS           Count=N| <- the number of PGAs
                               Always returns 0 if PGA mining is disabled
                               Always returns 0 if PGA mining is disabled
 
 
+ proccount     PGAS           Count=N| <- the number of processors
+
  cpucount      CPUS           Count=N| <- the number of CPUs
  cpucount      CPUS           Count=N| <- the number of CPUs
                               Always returns 0 if CPU mining is disabled
                               Always returns 0 if CPU mining is disabled
 
 
@@ -295,9 +300,21 @@ The list of requests - a (*) means it requires privileged access - and replies:
                               This is only available if PGA mining is enabled
                               This is only available if PGA mining is enabled
 
 
  pgaidentify|N (*)
  pgaidentify|N (*)
+               none           This is equivalent to PROCIDENTIFY on the first
+                              processor of any given device
+                              This is only available if PGA mining is enabled
+
+ procenable|N (*)
+               none           There is no reply section just the STATUS section
+                              stating the results of the enable request
+
+ procdisable|N (*)
+               none           There is no reply section just the STATUS section
+                              stating the results of the disable request
+
+ procidentify|N (*)
                none           There is no reply section just the STATUS section
                none           There is no reply section just the STATUS section
                               stating the results of the identify request
                               stating the results of the identify request
-                              This is only available if PGA mining is enabled
                               On most supported devices, it will flash the led
                               On most supported devices, it will flash the led
                               for approximately 4s
                               for approximately 4s
                               All unsupported devices, it will return a warning
                               All unsupported devices, it will return a warning
@@ -313,7 +330,7 @@ The list of requests - a (*) means it requires privileged access - and replies:
  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
                               This lists all devices including those not
                               supported by the 'devs' command
                               supported by the 'devs' command
-                              e.g. DEVDETAILS=0,Name=PGA,ID=0,ProcID=0,Driver=bitforce,...|
+                              e.g. DEVDETAILS=0,Name=BFL,ID=0,ProcID=0,Driver=bitforce,...|
 
 
  restart (*)   none           There is no status section but just a single
  restart (*)   none           There is no status section but just a single
                               "RESTART" reply before BFGMiner restarts
                               "RESTART" reply before BFGMiner restarts
@@ -366,13 +383,17 @@ The list of requests - a (*) means it requires privileged access - and replies:
                               coinbase-sig (string)
                               coinbase-sig (string)
 
 
  pgaset|N,opt[,val] (*)
  pgaset|N,opt[,val] (*)
+               none           This is equivalent to PROCSET on the first
+                              processor of any given device
+                              This is only available if PGA mining is enabled
+
+ procset|N,opt[,val] (*)
                none           There is no reply section just the STATUS section
                none           There is no reply section just the STATUS section
-                              stating the results of setting PGA N with
+                              stating the results of setting processor N with
                               opt[,val]
                               opt[,val]
-                              This is only available if PGA mining is enabled
 
 
-                              If the PGA does not support any set options, it
-                              will always return a WARN stating pgaset isn't
+                              If the processor does not support any set options,
+                              it will always return a WARN stating pgaset isn't
                               supported
                               supported
 
 
                               If opt=help it will return an INFO status with a
                               If opt=help it will return an INFO status with a
@@ -445,6 +466,31 @@ api-example.py - a Python script to access the API
 Feature Changelog for external applications using the API:
 Feature Changelog for external applications using the API:
 
 
 
 
+API V2.0
+
+Removed API commands:
+ 'devdetail' - Use newer 'devdetails' for same information.
+
+Modified API commands:
+ 'devs' - display status of each full device only (not processors)
+ 'pga' - lookup and display device by device (not processor) number
+ 'pgacount' - count only full devices (not processors)
+ 'pgaenable' - enable all processors for a numbered full device
+ 'pgadisable' - disable all processors for a numbered full device
+ 'pgaidentify' - choose first processor of numbered full device
+ 'pgaset' - choose first processor of numbered full device
+
+Added API commands:
+ 'procs'
+ 'proc'
+ 'proccount'
+ 'procenable'
+ 'procdisable'
+ 'procidentify'
+ 'procset'
+
+----------
+
 API V1.25.3 (BFGMiner v3.2.0)
 API V1.25.3 (BFGMiner v3.2.0)
 
 
 Modified API commands:
 Modified API commands:

+ 203 - 116
api.c

@@ -532,6 +532,7 @@ static bool do_a_quit;
 static bool do_a_restart;
 static bool do_a_restart;
 
 
 static time_t when = 0;	// when the request occurred
 static time_t when = 0;	// when the request occurred
+static bool per_proc;
 
 
 struct IP4ACCESS {
 struct IP4ACCESS {
 	in_addr_t ip;
 	in_addr_t ip;
@@ -1113,6 +1114,8 @@ static int numpgas()
 		if (devices[i]->drv == &cpu_drv)
 		if (devices[i]->drv == &cpu_drv)
 			continue;
 			continue;
 #endif
 #endif
+		if (devices[i]->device != devices[i] && !per_proc)
+			continue;
 		++count;
 		++count;
 	}
 	}
 	rd_unlock(&devices_lock);
 	rd_unlock(&devices_lock);
@@ -1134,6 +1137,8 @@ static int pgadevice(int pgaid)
 		if (devices[i]->drv == &cpu_drv)
 		if (devices[i]->drv == &cpu_drv)
 			continue;
 			continue;
 #endif
 #endif
+		if (devices[i]->device != devices[i] && !per_proc)
+			continue;
 		++count;
 		++count;
 		if (count == (pgaid + 1))
 		if (count == (pgaid + 1))
 			goto foundit;
 			goto foundit;
@@ -1401,6 +1406,8 @@ static const char *status2str(enum alive status)
 			return INIT;
 			return INIT;
 		case LIFE_WAIT:
 		case LIFE_WAIT:
 			return WAIT;
 			return WAIT;
+		case LIFE_MIXED:
+			return "Mixed";
 		default:
 		default:
 			return UNKNOWN;
 			return UNKNOWN;
 	}
 	}
@@ -1411,28 +1418,41 @@ struct api_data *api_add_device_identifier(struct api_data *root, struct cgpu_in
 {
 {
 	root = api_add_string(root, "Name", cgpu->drv->name, false);
 	root = api_add_string(root, "Name", cgpu->drv->name, false);
 	root = api_add_int(root, "ID", &(cgpu->device_id), false);
 	root = api_add_int(root, "ID", &(cgpu->device_id), false);
-	root = api_add_int(root, "ProcID", &(cgpu->proc_id), false);
+	if (per_proc)
+		root = api_add_int(root, "ProcID", &(cgpu->proc_id), false);
 	return root;
 	return root;
 }
 }
 
 
-static void devdetail_an(struct io_data *io_data, struct cgpu_info *cgpu, bool isjson, bool precom)
+static
+int find_index_by_cgpu(struct cgpu_info *cgpu)
 {
 {
-	struct api_data *root = NULL;
-	char buf[TMPBUFSIZ];
 	int n = 0, i;
 	int n = 0, i;
-
-	cgpu_utility(cgpu);
-
+	
 	rd_lock(&devices_lock);
 	rd_lock(&devices_lock);
-	for (i = 0; i < total_devices; ++i) {
+	for (i = 0; i < total_devices; ++i)
+	{
 		if (devices[i] == cgpu)
 		if (devices[i] == cgpu)
 			break;
 			break;
+		if (devices[i]->device != devices[i] && !per_proc)
+			continue;
 		if (cgpu->devtype == devices[i]->devtype)
 		if (cgpu->devtype == devices[i]->devtype)
 			++n;
 			++n;
 	}
 	}
 	rd_unlock(&devices_lock);
 	rd_unlock(&devices_lock);
+	return n;
+}
 
 
-	root = api_add_int(root, (char*)cgpu->devtype, &n, true);
+static void devdetail_an(struct io_data *io_data, struct cgpu_info *cgpu, bool isjson, bool precom)
+{
+	struct api_data *root = NULL;
+	char buf[TMPBUFSIZ];
+	int n;
+
+	cgpu_utility(cgpu);
+
+	n = find_index_by_cgpu(cgpu);
+
+	root = api_add_int(root, "DEVDETAILS", &n, true);
 	root = api_add_device_identifier(root, cgpu);
 	root = api_add_device_identifier(root, cgpu);
 	root = api_add_string(root, "Driver", cgpu->drv->dname, false);
 	root = api_add_string(root, "Driver", cgpu->drv->dname, false);
 	if (cgpu->kname)
 	if (cgpu->kname)
@@ -1449,56 +1469,93 @@ static void devdetail_an(struct io_data *io_data, struct cgpu_info *cgpu, bool i
 	io_add(io_data, buf);
 	io_add(io_data, buf);
 }
 }
 
 
-static void devstatus_an(struct io_data *io_data, struct cgpu_info *cgpu, bool isjson, bool precom)
+static
+void devstatus_an(struct io_data *io_data, struct cgpu_info *cgpu, bool isjson, bool precom)
 {
 {
+	struct cgpu_info *proc;
 	struct api_data *root = NULL;
 	struct api_data *root = NULL;
 	char buf[TMPBUFSIZ];
 	char buf[TMPBUFSIZ];
-	int n = 0, i;
-
-	cgpu_utility(cgpu);
+	int n;
 
 
-	rd_lock(&devices_lock);
-	for (i = 0; i < total_devices; ++i) {
-		if (devices[i] == cgpu)
+	n = find_index_by_cgpu(cgpu);
+
+	bool enabled = false;
+	double total_mhashes = 0, rolling = 0, utility = 0;
+	enum alive status = cgpu->status;
+	float temp = -1;
+	int accepted = 0, rejected = 0, hw_errors = 0;
+	int diff1 = 0, bad_nonces = 0;
+	double diff_accepted = 0, diff_rejected = 0;
+	int last_share_pool = -1;
+	time_t last_share_pool_time = -1, last_device_valid_work = -1;
+	double last_share_diff = -1;
+	for (proc = cgpu; proc; proc = proc->next_proc)
+	{
+		cgpu_utility(proc);
+		if (proc->deven != DEV_DISABLED)
+			enabled = true;
+		total_mhashes += proc->total_mhashes;
+		rolling += proc->rolling;
+		utility += proc->utility;
+		accepted += proc->accepted;
+		rejected += proc->rejected;
+		hw_errors += proc->hw_errors;
+		diff1 += proc->diff1;
+		diff_accepted += proc->diff_accepted;
+		diff_rejected += proc->diff_rejected;
+		bad_nonces += proc->bad_nonces;
+		if (status != proc->status)
+			status = LIFE_MIXED;
+		if (proc->temp > temp)
+			temp = proc->temp;
+		if (proc->last_share_pool_time > last_share_pool_time)
+		{
+			last_share_pool_time = proc->last_share_pool_time;
+			last_share_pool = proc->last_share_pool;
+			last_share_diff = proc->last_share_diff;
+		}
+		if (proc->last_device_valid_work > last_device_valid_work)
+			last_device_valid_work = proc->last_device_valid_work;
+		if (per_proc)
 			break;
 			break;
-		if (cgpu->devtype == devices[i]->devtype)
-			++n;
 	}
 	}
-	rd_unlock(&devices_lock);
 
 
 	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_device_identifier(root, cgpu);
-	root = api_add_string(root, "Enabled", bool2str(cgpu->deven != DEV_DISABLED), false);
-	root = api_add_string(root, "Status", status2str(cgpu->status), false);
-	if (cgpu->temp)
-		root = api_add_temp(root, "Temperature", &cgpu->temp, false);
-	double mhs = cgpu->total_mhashes / cgpu_runtime(cgpu);
+	root = api_add_string(root, "Enabled", bool2str(enabled), false);
+	root = api_add_string(root, "Status", status2str(status), false);
+	if (temp > 0)
+		root = api_add_temp(root, "Temperature", &temp, false);
+	double mhs = total_mhashes / cgpu_runtime(cgpu);
 	root = api_add_mhs(root, "MHS av", &mhs, false);
 	root = api_add_mhs(root, "MHS av", &mhs, false);
 	char mhsname[27];
 	char mhsname[27];
 	sprintf(mhsname, "MHS %ds", opt_log_interval);
 	sprintf(mhsname, "MHS %ds", opt_log_interval);
-	root = api_add_mhs(root, mhsname, &(cgpu->rolling), false);
-	root = api_add_int(root, "Accepted", &(cgpu->accepted), false);
-	root = api_add_int(root, "Rejected", &(cgpu->rejected), false);
-	root = api_add_int(root, "Hardware Errors", &(cgpu->hw_errors), false);
-	root = api_add_utility(root, "Utility", &(cgpu->utility), false);
-	int last_share_pool = cgpu->last_share_pool_time > 0 ?
-				cgpu->last_share_pool : -1;
-	root = api_add_int(root, "Last Share Pool", &last_share_pool, false);
-	root = api_add_time(root, "Last Share Time", &(cgpu->last_share_pool_time), false);
-	root = api_add_mhtotal(root, "Total MH", &(cgpu->total_mhashes), false);
-	root = api_add_int(root, "Diff1 Work", &(cgpu->diff1), false);
-	root = api_add_diff(root, "Difficulty Accepted", &(cgpu->diff_accepted), false);
-	root = api_add_diff(root, "Difficulty Rejected", &(cgpu->diff_rejected), false);
-	root = api_add_diff(root, "Last Share Difficulty", &(cgpu->last_share_diff), false);
-	root = api_add_time(root, "Last Valid Work", &(cgpu->last_device_valid_work), false);
-	double hwp = (cgpu->bad_nonces + cgpu->diff1) ?
-			(double)(cgpu->bad_nonces) / (double)(cgpu->bad_nonces + cgpu->diff1) : 0;
+	root = api_add_mhs(root, mhsname, &rolling, false);
+	root = api_add_int(root, "Accepted", &accepted, false);
+	root = api_add_int(root, "Rejected", &rejected, false);
+	root = api_add_int(root, "Hardware Errors", &hw_errors, false);
+	root = api_add_utility(root, "Utility", &utility, false);
+	if (last_share_pool != -1)
+	{
+		root = api_add_int(root, "Last Share Pool", &last_share_pool, false);
+		root = api_add_time(root, "Last Share Time", &last_share_pool_time, false);
+	}
+	root = api_add_mhtotal(root, "Total MH", &total_mhashes, false);
+	root = api_add_int(root, "Diff1 Work", &diff1, false);
+	root = api_add_diff(root, "Difficulty Accepted", &diff_accepted, false);
+	root = api_add_diff(root, "Difficulty Rejected", &diff_rejected, false);
+	if (last_share_diff > 0)
+		root = api_add_diff(root, "Last Share Difficulty", &last_share_diff, false);
+	if (last_device_valid_work != -1)
+		root = api_add_time(root, "Last Valid Work", &last_device_valid_work, false);
+	double hwp = (bad_nonces + diff1) ?
+			(double)(bad_nonces) / (double)(bad_nonces + diff1) : 0;
 	root = api_add_percent(root, "Device Hardware%", &hwp, false);
 	root = api_add_percent(root, "Device Hardware%", &hwp, false);
-	double rejp = cgpu->diff1 ?
-			(double)(cgpu->diff_rejected) / (double)(cgpu->diff1) : 0;
+	double rejp = diff1 ?
+			(double)(diff_rejected) / (double)(diff1) : 0;
 	root = api_add_percent(root, "Device Rejected%", &rejp, false);
 	root = api_add_percent(root, "Device Rejected%", &rejp, false);
 
 
-	if (cgpu->drv->get_api_extra_device_status)
+	if (per_proc && cgpu->drv->get_api_extra_device_status)
 		root = api_add_extra(root, cgpu->drv->get_api_extra_device_status(cgpu));
 		root = api_add_extra(root, cgpu->drv->get_api_extra_device_status(cgpu));
 
 
 	root = print_data(root, buf, isjson, precom);
 	root = print_data(root, buf, isjson, precom);
@@ -1534,8 +1591,9 @@ static void cpustatus(struct io_data *io_data, int cpu, bool isjson, bool precom
 #endif
 #endif
 
 
 static void
 static void
-devinfo_internal(void (*func)(struct io_data *, struct cgpu_info*, bool, bool), struct io_data *io_data, __maybe_unused SOCKETTYPE c, __maybe_unused char *param, bool isjson, __maybe_unused char group)
+devinfo_internal(void (*func)(struct io_data *, struct cgpu_info*, bool, bool), int msg, struct io_data *io_data, __maybe_unused SOCKETTYPE c, __maybe_unused char *param, bool isjson, __maybe_unused char group)
 {
 {
+	struct cgpu_info *cgpu;
 	bool io_open = false;
 	bool io_open = false;
 	int i;
 	int i;
 
 
@@ -1545,12 +1603,14 @@ devinfo_internal(void (*func)(struct io_data *, struct cgpu_info*, bool, bool),
 	}
 	}
 
 
 
 
-	message(io_data, MSG_DEVS, 0, NULL, isjson);
+	message(io_data, msg, 0, NULL, isjson);
 	if (isjson)
 	if (isjson)
 		io_open = io_add(io_data, COMSTR JSON_DEVS);
 		io_open = io_add(io_data, COMSTR JSON_DEVS);
 
 
 	for (i = 0; i < total_devices; ++i) {
 	for (i = 0; i < total_devices; ++i) {
-		func(io_data, get_devices(i), isjson, isjson && i > 0);
+		cgpu = get_devices(i);
+		if (per_proc || cgpu->device == cgpu)
+			func(io_data, cgpu, isjson, isjson && i > 0);
 	}
 	}
 
 
 	if (isjson && io_open)
 	if (isjson && io_open)
@@ -1559,12 +1619,12 @@ devinfo_internal(void (*func)(struct io_data *, struct cgpu_info*, bool, bool),
 
 
 static void devdetail(struct io_data *io_data, SOCKETTYPE c, char *param, bool isjson, __maybe_unused char group)
 static void devdetail(struct io_data *io_data, SOCKETTYPE c, char *param, bool isjson, __maybe_unused char group)
 {
 {
-	return devinfo_internal(devdetail_an, io_data, c, param, isjson, group);
+	return devinfo_internal(devdetail_an, MSG_DEVDETAILS, io_data, c, param, isjson, group);
 }
 }
 
 
 static void devstatus(struct io_data *io_data, __maybe_unused SOCKETTYPE c, __maybe_unused char *param, bool isjson, __maybe_unused char group)
 static void devstatus(struct io_data *io_data, __maybe_unused SOCKETTYPE c, __maybe_unused char *param, bool isjson, __maybe_unused char group)
 {
 {
-	return devinfo_internal(devstatus_an, io_data, c, param, isjson, group);
+	return devinfo_internal(devstatus_an, MSG_DEVS, io_data, c, param, isjson, group);
 }
 }
 
 
 #ifdef HAVE_OPENCL
 #ifdef HAVE_OPENCL
@@ -1662,9 +1722,10 @@ 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)
 {
 {
-	struct cgpu_info *cgpu;
+	struct cgpu_info *cgpu, *proc;
 	int numpga = numpgas();
 	int numpga = numpgas();
 	int id;
 	int id;
+	bool already;
 
 
 	if (numpga == 0) {
 	if (numpga == 0) {
 		message(io_data, MSG_PGANON, 0, NULL, isjson);
 		message(io_data, MSG_PGANON, 0, NULL, isjson);
@@ -1690,10 +1751,21 @@ static void pgaenable(struct io_data *io_data, __maybe_unused SOCKETTYPE c, char
 
 
 	cgpu = get_devices(dev);
 	cgpu = get_devices(dev);
 
 
-	applog(LOG_DEBUG, "API: request to pgaenable pgaid %d device %d %s",
-			id, dev, cgpu->proc_repr_ns);
+	applog(LOG_DEBUG, "API: request to pgaenable %s id %d device %d %s",
+			per_proc ? "proc" : "dev", id, dev, cgpu->proc_repr_ns);
 
 
-	if (cgpu->deven != DEV_DISABLED) {
+	already = true;
+	for (proc = cgpu; proc; proc = proc->next_proc)
+	{
+		if (proc->deven == DEV_DISABLED)
+		{
+			proc_enable(proc);
+			already = false;
+		}
+	}
+	
+	if (already)
+	{
 		message(io_data, MSG_PGALRENA, id, NULL, isjson);
 		message(io_data, MSG_PGALRENA, id, NULL, isjson);
 		return;
 		return;
 	}
 	}
@@ -1705,16 +1777,15 @@ static void pgaenable(struct io_data *io_data, __maybe_unused SOCKETTYPE c, char
 	}
 	}
 #endif
 #endif
 
 
-	proc_enable(cgpu);
-
 	message(io_data, MSG_PGAENA, id, NULL, isjson);
 	message(io_data, MSG_PGAENA, id, NULL, isjson);
 }
 }
 
 
 static void pgadisable(struct io_data *io_data, __maybe_unused SOCKETTYPE c, char *param, bool isjson, __maybe_unused char group)
 static void pgadisable(struct io_data *io_data, __maybe_unused SOCKETTYPE c, char *param, bool isjson, __maybe_unused char group)
 {
 {
-	struct cgpu_info *cgpu;
+	struct cgpu_info *cgpu, *proc;
 	int numpga = numpgas();
 	int numpga = numpgas();
 	int id;
 	int id;
+	bool already;
 
 
 	if (numpga == 0) {
 	if (numpga == 0) {
 		message(io_data, MSG_PGANON, 0, NULL, isjson);
 		message(io_data, MSG_PGANON, 0, NULL, isjson);
@@ -1740,16 +1811,25 @@ static void pgadisable(struct io_data *io_data, __maybe_unused SOCKETTYPE c, cha
 
 
 	cgpu = get_devices(dev);
 	cgpu = get_devices(dev);
 
 
-	applog(LOG_DEBUG, "API: request to pgadisable pgaid %d device %d %s",
-			id, dev, cgpu->proc_repr_ns);
+	applog(LOG_DEBUG, "API: request to pgadisable %s id %d device %d %s",
+			per_proc ? "proc" : "dev", id, dev, cgpu->proc_repr_ns);
 
 
-	if (cgpu->deven == DEV_DISABLED) {
+	already = true;
+	for (proc = cgpu; proc; proc = proc->next_proc)
+	{
+		if (proc->deven != DEV_DISABLED)
+		{
+			cgpu->deven = DEV_DISABLED;
+			already = false;
+		}
+	}
+	
+	if (already)
+	{
 		message(io_data, MSG_PGALRDIS, id, NULL, isjson);
 		message(io_data, MSG_PGALRDIS, id, NULL, isjson);
 		return;
 		return;
 	}
 	}
 
 
-	cgpu->deven = DEV_DISABLED;
-
 	message(io_data, MSG_PGADIS, id, NULL, isjson);
 	message(io_data, MSG_PGADIS, id, NULL, isjson);
 }
 }
 
 
@@ -2693,14 +2773,43 @@ void privileged(struct io_data *io_data, __maybe_unused SOCKETTYPE c, __maybe_un
 
 
 void notifystatus(struct io_data *io_data, int device, struct cgpu_info *cgpu, bool isjson, __maybe_unused char group)
 void notifystatus(struct io_data *io_data, int device, struct cgpu_info *cgpu, bool isjson, __maybe_unused char group)
 {
 {
+	struct cgpu_info *proc;
 	struct api_data *root = NULL;
 	struct api_data *root = NULL;
 	char buf[TMPBUFSIZ];
 	char buf[TMPBUFSIZ];
 	char *reason;
 	char *reason;
+	
+	time_t last_not_well = 0;
+	enum dev_reason uninitialised_var(enum_reason);
+	int thread_fail_init_count = 0, thread_zero_hash_count = 0, thread_fail_queue_count = 0;
+	int dev_sick_idle_60_count = 0, dev_dead_idle_600_count = 0;
+	int dev_nostart_count = 0, dev_over_heat_count = 0, dev_thermal_cutoff_count = 0, dev_comms_error_count = 0, dev_throttle_count = 0;
 
 
-	if (cgpu->device_last_not_well == 0)
+	for (proc = cgpu; proc; proc = proc->next_proc)
+	{
+		if (proc->device_last_not_well > last_not_well)
+		{
+			last_not_well = proc->device_last_not_well;
+			enum_reason = proc->device_not_well_reason;
+			thread_fail_init_count   += proc->thread_fail_init_count;
+			thread_zero_hash_count   += proc->thread_zero_hash_count;
+			thread_fail_queue_count  += proc->thread_fail_queue_count;
+			dev_sick_idle_60_count   += proc->dev_sick_idle_60_count;
+			dev_dead_idle_600_count  += proc->dev_dead_idle_600_count;
+			dev_nostart_count        += proc->dev_nostart_count;
+			dev_over_heat_count      += proc->dev_over_heat_count;
+			dev_thermal_cutoff_count += proc->dev_thermal_cutoff_count;
+			dev_comms_error_count    += proc->dev_comms_error_count;
+			dev_throttle_count       += proc->dev_throttle_count;
+		}
+		if (per_proc)
+			break;
+	}
+	
+	if (last_not_well == 0)
 		reason = REASON_NONE;
 		reason = REASON_NONE;
 	else
 	else
-		switch(cgpu->device_not_well_reason) {
+		switch (enum_reason)
+		{
 			case REASON_THREAD_FAIL_INIT:
 			case REASON_THREAD_FAIL_INIT:
 				reason = REASON_THREAD_FAIL_INIT_STR;
 				reason = REASON_THREAD_FAIL_INIT_STR;
 				break;
 				break;
@@ -2737,29 +2846,31 @@ void notifystatus(struct io_data *io_data, int device, struct cgpu_info *cgpu, b
 	// 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_device_identifier(root, cgpu);
 	root = api_add_device_identifier(root, cgpu);
-	root = api_add_time(root, "Last Well", &(cgpu->device_last_well), false);
-	root = api_add_time(root, "Last Not Well", &(cgpu->device_last_not_well), false);
+	if (per_proc)
+		root = api_add_time(root, "Last Well", &(cgpu->device_last_well), false);
+	root = api_add_time(root, "Last Not Well", &last_not_well, false);
 	root = api_add_string(root, "Reason Not Well", reason, false);
 	root = api_add_string(root, "Reason Not Well", reason, false);
-	root = api_add_int(root, "*Thread Fail Init", &(cgpu->thread_fail_init_count), false);
-	root = api_add_int(root, "*Thread Zero Hash", &(cgpu->thread_zero_hash_count), false);
-	root = api_add_int(root, "*Thread Fail Queue", &(cgpu->thread_fail_queue_count), false);
-	root = api_add_int(root, "*Dev Sick Idle 60s", &(cgpu->dev_sick_idle_60_count), false);
-	root = api_add_int(root, "*Dev Dead Idle 600s", &(cgpu->dev_dead_idle_600_count), false);
-	root = api_add_int(root, "*Dev Nostart", &(cgpu->dev_nostart_count), false);
-	root = api_add_int(root, "*Dev Over Heat", &(cgpu->dev_over_heat_count), false);
-	root = api_add_int(root, "*Dev Thermal Cutoff", &(cgpu->dev_thermal_cutoff_count), false);
-	root = api_add_int(root, "*Dev Comms Error", &(cgpu->dev_comms_error_count), false);
-	root = api_add_int(root, "*Dev Throttle", &(cgpu->dev_throttle_count), false);
+	root = api_add_int(root, "*Thread Fail Init", &thread_fail_init_count, false);
+	root = api_add_int(root, "*Thread Zero Hash", &thread_zero_hash_count, false);
+	root = api_add_int(root, "*Thread Fail Queue", &thread_fail_queue_count, false);
+	root = api_add_int(root, "*Dev Sick Idle 60s", &dev_sick_idle_60_count, false);
+	root = api_add_int(root, "*Dev Dead Idle 600s", &dev_dead_idle_600_count, false);
+	root = api_add_int(root, "*Dev Nostart", &dev_nostart_count, false);
+	root = api_add_int(root, "*Dev Over Heat", &dev_over_heat_count, false);
+	root = api_add_int(root, "*Dev Thermal Cutoff", &dev_thermal_cutoff_count, false);
+	root = api_add_int(root, "*Dev Comms Error", &dev_comms_error_count, false);
+	root = api_add_int(root, "*Dev Throttle", &dev_throttle_count, false);
 
 
 	root = print_data(root, buf, isjson, isjson && (device > 0));
 	root = print_data(root, buf, isjson, isjson && (device > 0));
 	io_add(io_data, buf);
 	io_add(io_data, buf);
 }
 }
 
 
-static void notify(struct io_data *io_data, __maybe_unused SOCKETTYPE c, __maybe_unused char *param, bool isjson, char group)
+static
+void notify(struct io_data *io_data, __maybe_unused SOCKETTYPE c, __maybe_unused char *param, bool isjson, char group)
 {
 {
 	struct cgpu_info *cgpu;
 	struct cgpu_info *cgpu;
 	bool io_open = false;
 	bool io_open = false;
-	int i;
+	int i, n = 0;
 
 
 	if (total_devices == 0) {
 	if (total_devices == 0) {
 		message(io_data, MSG_NODEVS, 0, NULL, isjson);
 		message(io_data, MSG_NODEVS, 0, NULL, isjson);
@@ -2773,43 +2884,8 @@ static void notify(struct io_data *io_data, __maybe_unused SOCKETTYPE c, __maybe
 
 
 	for (i = 0; i < total_devices; i++) {
 	for (i = 0; i < total_devices; i++) {
 		cgpu = get_devices(i);
 		cgpu = get_devices(i);
-		notifystatus(io_data, i, cgpu, isjson, group);
-	}
-
-	if (isjson && io_open)
-		io_close(io_data);
-}
-
-static void devdetails(struct io_data *io_data, __maybe_unused SOCKETTYPE c, __maybe_unused char *param, bool isjson, __maybe_unused char group)
-{
-	struct api_data *root = NULL;
-	char buf[TMPBUFSIZ];
-	bool io_open = false;
-	struct cgpu_info *cgpu;
-	int i;
-
-	if (total_devices == 0) {
-		message(io_data, MSG_NODEVS, 0, NULL, isjson);
-		return;
-	}
-
-	message(io_data, MSG_DEVDETAILS, 0, NULL, isjson);
-
-	if (isjson)
-		io_open = io_add(io_data, COMSTR JSON_DEVDETAILS);
-
-	for (i = 0; i < total_devices; i++) {
-		cgpu = get_devices(i);
-
-		root = api_add_int(root, "DEVDETAILS", &i, false);
-		root = api_add_device_identifier(root, cgpu);
-		root = api_add_string(root, "Driver", cgpu->drv->dname, 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, "Device Path", cgpu->device_path ? : BLANK, false);
-
-		root = print_data(root, buf, isjson, isjson && (i > 0));
-		io_add(io_data, buf);
+		if (cgpu->device == cgpu || per_proc)
+			notifystatus(io_data, n++, cgpu, isjson, group);
 	}
 	}
 
 
 	if (isjson && io_open)
 	if (isjson && io_open)
@@ -3253,7 +3329,7 @@ struct CMDS {
 	{ "config",		minerconfig,	false },
 	{ "config",		minerconfig,	false },
 	{ "devscan",		devscan,	false },
 	{ "devscan",		devscan,	false },
 	{ "devs",		devstatus,	false },
 	{ "devs",		devstatus,	false },
-	{ "devdetail",	devdetail,	false },
+	{ "procs",		devstatus,	false },
 	{ "pools",		poolstatus,	false },
 	{ "pools",		poolstatus,	false },
 	{ "summary",		summary,	false },
 	{ "summary",		summary,	false },
 #ifdef HAVE_OPENCL
 #ifdef HAVE_OPENCL
@@ -3267,6 +3343,10 @@ struct CMDS {
 	{ "pgaenable",		pgaenable,	true },
 	{ "pgaenable",		pgaenable,	true },
 	{ "pgadisable",		pgadisable,	true },
 	{ "pgadisable",		pgadisable,	true },
 	{ "pgaidentify",	pgaidentify,	true },
 	{ "pgaidentify",	pgaidentify,	true },
+	{ "proc",		pgadev,		false },
+	{ "procenable",		pgaenable,	true },
+	{ "procdisable",		pgadisable,	true },
+	{ "procidentify",	pgaidentify,	true },
 #endif
 #endif
 #ifdef WANT_CPUMINE
 #ifdef WANT_CPUMINE
 	{ "cpuenable",		cpuenable,	true },
 	{ "cpuenable",		cpuenable,	true },
@@ -3276,6 +3356,7 @@ struct CMDS {
 #endif
 #endif
 	{ "gpucount",		gpucount,	false },
 	{ "gpucount",		gpucount,	false },
 	{ "pgacount",		pgacount,	false },
 	{ "pgacount",		pgacount,	false },
+	{ "proccount",		pgacount,	false },
 	{ "cpucount",		cpucount,	false },
 	{ "cpucount",		cpucount,	false },
 	{ "switchpool",		switchpool,	true },
 	{ "switchpool",		switchpool,	true },
 	{ "addpool",		addpool,	true },
 	{ "addpool",		addpool,	true },
@@ -3294,7 +3375,9 @@ struct CMDS {
 	{ "quit",		doquit,		true },
 	{ "quit",		doquit,		true },
 	{ "privileged",		privileged,	true },
 	{ "privileged",		privileged,	true },
 	{ "notify",		notify,		false },
 	{ "notify",		notify,		false },
-	{ "devdetails",		devdetails,	false },
+	{ "procnotify",		notify,		false },
+	{ "devdetails",		devdetail,	false },
+	{ "procdetails",		devdetail,	false },
 	{ "restart",		dorestart,	true },
 	{ "restart",		dorestart,	true },
 	{ "stats",		minerstats,	false },
 	{ "stats",		minerstats,	false },
 	{ "check",		checkcommand,	false },
 	{ "check",		checkcommand,	false },
@@ -3304,6 +3387,7 @@ struct CMDS {
 	{ "setconfig",		setconfig,	true },
 	{ "setconfig",		setconfig,	true },
 #ifdef HAVE_AN_FPGA
 #ifdef HAVE_AN_FPGA
 	{ "pgaset",		pgaset,		true },
 	{ "pgaset",		pgaset,		true },
+	{ "procset",		pgaset,		true },
 #endif
 #endif
 	{ "zero",		dozero,		true },
 	{ "zero",		dozero,		true },
 	{ NULL,			NULL,		false }
 	{ NULL,			NULL,		false }
@@ -4083,7 +4167,10 @@ void api(int api_thr_id)
 						if (strcmp(cmd, cmds[i].name) == 0) {
 						if (strcmp(cmd, cmds[i].name) == 0) {
 							sprintf(cmdbuf, "|%s|", cmd);
 							sprintf(cmdbuf, "|%s|", cmd);
 							if (ISPRIVGROUP(group) || strstr(COMMANDS(group), cmdbuf))
 							if (ISPRIVGROUP(group) || strstr(COMMANDS(group), cmdbuf))
+							{
+								per_proc = !strncmp(cmds[i].name, "proc", 4);
 								(cmds[i].func)(io_data, c, param, isjson, group);
 								(cmds[i].func)(io_data, c, param, isjson, group);
+							}
 							else {
 							else {
 								message(io_data, MSG_ACCDENY, 0, cmds[i].name, isjson);
 								message(io_data, MSG_ACCDENY, 0, cmds[i].name, isjson);
 								applog(LOG_DEBUG, "API: access denied to '%s' for '%s' command", connectaddr, cmds[i].name);
 								applog(LOG_DEBUG, "API: access denied to '%s' for '%s' command", connectaddr, cmds[i].name);

+ 1 - 0
miner.h

@@ -226,6 +226,7 @@ enum alive {
 	LIFE_WAIT,
 	LIFE_WAIT,
 	LIFE_INIT2,  // Still initializing, but safe to call functions
 	LIFE_INIT2,  // Still initializing, but safe to call functions
 	LIFE_DEAD2,  // Totally dead, NOT safe to call functions
 	LIFE_DEAD2,  // Totally dead, NOT safe to call functions
+	LIFE_MIXED,  // Only valid in display variables, NOT on devices
 };
 };
 
 
 
 

+ 11 - 0
miner.php

@@ -12,6 +12,7 @@ global $ignorerefresh, $changerefresh, $autorefresh;
 global $allowcustompages, $customsummarypages;
 global $allowcustompages, $customsummarypages;
 global $miner_font_family, $miner_font_size;
 global $miner_font_family, $miner_font_size;
 global $colouroverride, $placebuttons, $userlist;
 global $colouroverride, $placebuttons, $userlist;
+global $per_proc;
 #
 #
 # See README.RPC for more details of these variables and how
 # See README.RPC for more details of these variables and how
 # to configure miner.php
 # to configure miner.php
@@ -26,6 +27,9 @@ $readonly = false;
 # Set $userlist to null to allow anyone access or read README.RPC
 # Set $userlist to null to allow anyone access or read README.RPC
 $userlist = null;
 $userlist = null;
 #
 #
+# Set $per_proc to false to display only full device summaries
+$per_proc = true;
+#
 # Set $notify to false to NOT attempt to display the notify command
 # Set $notify to false to NOT attempt to display the notify command
 # Set $notify to true to attempt to display the notify command
 # Set $notify to true to attempt to display the notify command
 $notify = true;
 $notify = true;
@@ -531,6 +535,13 @@ function api($rig, $cmd)
 {
 {
  global $haderror, $error;
  global $haderror, $error;
  global $miner, $port, $hidefields;
  global $miner, $port, $hidefields;
+ global $per_proc;
+
+ if ($per_proc)
+ {
+	$cmd = preg_replace('/^devs\b/', 'procs', $cmd);
+	$cmd = preg_replace('/^pga/', 'proc', $cmd);
+ }
 
 
  $socket = getsock($rig, $miner, $port);
  $socket = getsock($rig, $miner, $port);
  if ($socket != null)
  if ($socket != null)