Browse Source

Merge branch 'master' of git@github.com:pshep/cgminer.git

Conflicts:
	driver-bitforce.c
nfactor-troky
Paul Sheppard 13 years ago
parent
commit
4889d7ecb2
  1. 459
      API-README
  2. 0
      API.class
  3. 56
      FPGA-README
  4. 2
      Makefile.am
  5. 28
      NEWS
  6. 278
      README
  7. 6
      adl.c
  8. 34
      api.c
  9. 116
      cgminer.c
  10. 2
      configure.ac
  11. 30
      debian/changelog
  12. 1
      debian/patches/series
  13. 64422
      debian/patches/v2.4.2
  14. 178
      driver-bitforce.c
  15. 514
      driver-icarus.c
  16. 7
      miner.h
  17. 337
      miner.php

459
API-README

@ -0,0 +1,459 @@
This README contains details about the cgminer RPC API
If you start cgminer with the "--api-listen" option, it will listen on a
simple TCP/IP socket for single string API requests from the same machine
running cgminer and reply with a string and then close the socket each time
If you add the "--api-network" option, it will accept API requests from any
network attached computer.
You can only access the comands that reply with data in this mode.
By default, you cannot access any privileged command that affects the miner -
you will receive an access denied status message see --api-allow below.
You can specify IP addresses/prefixes that are only allowed to access the API
with the "--api-allow" option e.g. --api-allow W:192.168.0.1,10.0.0/24
will allow 192.168.0.1 or any address matching 10.0.0.*, but nothing else
IP addresses are automatically padded with extra '.0's as needed
Without a /prefix is the same as specifying /32
0/0 means all IP addresses.
The 'W:' on the front gives that address/subnet privileged access to commands
that modify cgminer.
Without it those commands return an access denied status.
Privileged access is checked in the order the IP addresses were supplied to
"--api-allow"
The first match determines the privilege level.
Using the "--api-allow" option overides the "--api-network" option if they
are both specified
With "--api-allow", 127.0.0.1 is not by default given access unless specified
The RPC API request can be either simple text or JSON.
If the request is JSON (starts with '{'), it will reply with a JSON formatted
response, otherwise it replies with text formatted as described further below.
The JSON request format required is '{"command":"CMD","parameter":"PARAM"}'
(though of course parameter is not required for all requests)
where "CMD" is from the "Request" column below and "PARAM" would be e.g.
the CPU/GPU number if required.
An example request in both formats to set GPU 0 fan to 80%:
gpufan|0,80
{"command":"gpufan","parameter":"0,80"}
The format of each reply (unless stated otherwise) is a STATUS section
followed by an optional detail section
From API version 1.7 onwards, reply strings in JSON and Text have the
necessary escaping as required to avoid ambiguity - they didn't before 1.7
For JSON the 2 characters '"' and '\' are escaped with a '\' before them
For Text the 4 characters '|' ',' '=' and '\' are escaped the same way
Only user entered information will contain characters that require being
escaped, such as Pool URL, User and Password or the Config save filename,
when they are returned in messages or as their values by the API
For API version 1.4 and later:
The STATUS section is:
STATUS=X,When=NNN,Code=N,Msg=string,Description=string|
STATUS=X Where X is one of:
W - Warning
I - Informational
S - Success
E - Error
F - Fatal (code bug)
When=NNN
Standard long time of request in seconds
Code=N
Each unique reply has a unigue Code (See api.c - #define MSG_NNNNNN)
Msg=string
Message matching the Code value N
Description=string
This defaults to the cgminer version but is the value of --api-description
if it was specified at runtime.
For API version 1.9:
The list of requests - a (*) means it requires privileged access - and replies are:
Request Reply Section Details
------- ------------- -------
version VERSION CGMiner=cgminer version
API=API version
config CONFIG Some miner configuration information:
GPU Count=N, <- the number of GPUs
PGA Count=N, <- the number of PGAs
CPU Count=N, <- the number of CPUs
Pool Count=N, <- the number of Pools
ADL=X, <- Y or N if ADL is compiled in the code
ADL in use=X, <- Y or N if any GPU has ADL
Strategy=Name, <- the current pool strategy
Log Interval=N, <- log interval (--log N)
Device Code=GPU ICA | <- spaced list of compiled devices
summary SUMMARY The status summary of the miner
e.g. Elapsed=NNN,Found Blocks=N,Getworks=N,...|
pools POOLS The status of each pool
e.g. Pool=0,URL=http://pool.com:6311,Status=Alive,...|
devs DEVS Each available GPU, PGA and CPU with their details
e.g. GPU=0,Accepted=NN,MHS av=NNN,...,Intensity=D|
Last Share Time=NNN, <- standand long time in seconds
(or 0 if none) of last accepted share
Last Share Pool=N, <- pool number (or -1 if none)
Will not report PGAs if PGA mining is disabled
Will not report CPUs if CPU mining is disabled
gpu|N GPU The details of a single GPU number N in the same
format and details as for DEVS
pga|N PGA The details of a single PGA number N in the same
format and details as for DEVS
This is only available if PGA mining is enabled
Use 'pgacount' or 'config' first to see if there are any
cpu|N CPU The details of a single CPU number N in the same
format and details as for DEVS
This is only available if CPU mining is enabled
Use 'cpucount' or 'config' first to see if there are any
gpucount GPUS Count=N| <- the number of GPUs
pgacount PGAS Count=N| <- the number of PGAs
Always returns 0 if PGA mining is disabled
cpucount CPUS Count=N| <- the number of CPUs
Always returns 0 if CPU mining is disabled
switchpool|N (*)
none There is no reply section just the STATUS section
stating the results of switching pool N to the
highest priority (the pool is also enabled)
The Msg includes the pool URL
enablepool|N (*)
none There is no reply section just the STATUS section
stating the results of enabling pool N
The Msg includes the pool URL
addpool|URL,USR,PASS (*)
none There is no reply section just the STATUS section
stating the results of attempting to add pool N
The Msg includes the pool URL
Use '\\' to get a '\' and '\,' to include a comma
inside URL, USR or PASS
disablepool|N (*)
none There is no reply section just the STATUS section
stating the results of disabling pool N
The Msg includes the pool URL
removepool|N (*)
none There is no reply section just the STATUS section
stating the results of removing pool N
The Msg includes the pool URL
N.B. all details for the pool will be lost
gpuenable|N (*)
none There is no reply section just the STATUS section
stating the results of the enable request
gpudisable|N (*)
none There is no reply section just the STATUS section
stating the results of the disable request
gpurestart|N (*)
none There is no reply section just the STATUS section
stating the results of the restart request
gpuintensity|N,I (*)
none There is no reply section just the STATUS section
stating the results of setting GPU N intensity to I
gpumem|N,V (*)
none There is no reply section just the STATUS section
stating the results of setting GPU N memoryclock to V MHz
gpuengine|N,V (*)
none There is no reply section just the STATUS section
stating the results of setting GPU N clock to V MHz
gpufan|N,V (*)
none There is no reply section just the STATUS section
stating the results of setting GPU N fan speed to V%
gpuvddc|N,V (*)
none There is no reply section just the STATUS section
stating the results of setting GPU N vddc to V
save|filename (*)
none There is no reply section just the STATUS section
stating success or failure saving the cgminer config
to filename
The filename is optional and will use the cgminer
default if not specified
quit (*) none There is no status section but just a single "BYE"
reply before cgminer quits
notify NOTIFY The last status and history count of each devices problem
This lists all devices including those not supported
by the 'devs' command
e.g. NOTIFY=0,Name=GPU,ID=0,Last Well=1332432290,...|
privileged (*)
none There is no reply section just the STATUS section
stating an error if you do not have privileged access
to the API and success if you do have privilege
The command doesn't change anything in cgminer
pgaenable|N (*)
none There is no reply section just the STATUS section
stating the results of the enable request
You cannot enable a PGA if it's status is not WELL
This is only available if PGA mining is enabled
pgadisable|N (*)
none There is no reply section just the STATUS section
stating the results of the disable request
This is only available if PGA mining is enabled
devdetails DEVDETAILS Each device with a list of their static details
This lists all devices including those not supported
by the 'devs' command
e.g. DEVDETAILS=0,Name=GPU,ID=0,Driver=opencl,...|
restart (*) none There is no status section but just a single "RESTART"
reply before cgminer restarts
stats STATS Each device or pool that has 1 or more getworks
with a list of stats regarding getwork times
The values returned by stats may change in future
versions thus would not normally be displayed
Device drivers are also able to add stats to the
end of the details returned
When you enable, disable or restart a GPU or PGA, you will also get Thread messages
in the cgminer status window
When you switch to a different pool to the current one, you will get a
'Switching to URL' message in the cgminer status windows
Obviously, the JSON format is simply just the names as given before the '='
with the values after the '='
If you enable cgminer debug (-D or --debug) you will also get messages showing
details of the requests received and the replies
There are included 4 program examples for accessing the API:
api-example.php - a php script to access the API
usAge: php api-example.php command
by default it sends a 'summary' request to the miner at 127.0.0.1:4028
If you specify a command it will send that request instead
You must modify the line "$socket = getsock('127.0.0.1', 4028);" at the
beginning of "function request($cmd)" to change where it looks for cgminer
API.java/API.class
a java program to access the API (with source code)
usAge is: java API command address port
Any missing or blank parameters are replaced as if you entered:
java API summary 127.0.0.1 4028
api-example.c - a 'C' program to access the API (with source code)
usAge: api-example [command [ip/host [port]]]
again, as above, missing or blank parameters are replaced as if you entered:
api-example summary 127.0.0.1 4028
miner.php - an example web page to access the API
This includes buttons and inputs to attempt access to the privileged commands
Read the top of the file (miner.php) for details of how to tune the display
and also to use the option to display a multi-rig summary
----------
Feature Changelog for external applications using the API:
API V1.11
Modified API commands:
'save' no longer requires a filename (use default if not specified)
'save' incorrectly returned status E (error) on success before.
It now correctly returns S (success)
----------
API V1.10 (cgminer v2.4.1)
Added API commands:
'stats'
N.B. the 'stats' command can change at any time so any specific content
present should not be relied upon.
The data content is mainly used for debugging purposes or hidden options
in cgminer and can change as development work requires
Modified API commands:
'pools' added "Last Share Time"
----------
API V1.9 (cgminer v2.4.0)
Added API commands:
'restart'
Modified API commands:
'notify' corrected invalid JSON
----------
API V1.8 (cgminer v2.3.5)
Added API commands:
'devdetails'
Support for the ZTex FPGA was added
----------
API V1.7 (cgminer v2.3.4)
Added API commands:
'removepool'
Modified API commands:
'pools' added "User"
From API version 1.7 onwards, reply strings in JSON and Text have the
necessary escaping as required to avoid ambiguity
For JSON the 2 characters '"' and '\' are escaped with a '\' before them
For Text the 4 characters '|' ',' '=' and '\' are escaped the same way
----------
API V1.6 (cgminer v2.3.2)
Added API commands:
'pga'
'pgaenable'
'pgadisable'
'pgacount'
Modified API commands:
'devs' now includes Icarus and Bitforce FPGA devices
'notify' added "*" to the front of the name of all numeric error fields
'config' correct "Log Interval" to use numeric (not text) type for JSON
Support for Icarus and Bitforce FPGAs was added
----------
API V1.5 was not released
----------
API V1.4 (Kano's interim release of cgminer v2.3.1)
Added API commands:
'notify'
Modified API commands:
'config' added "Device Code" and "OS"
Added "When" to the STATUS reply section of all commands
----------
API V1.3 (cgminer v2.3.1-2)
Added API commands:
'addpool'
Modified API commands:
'devs'/'gpu' added "Total MH" for each device
'summary' added "Total MH"
----------
API V1.2 (cgminer v2.3.0)
Added API commands:
'enablepool'
'disablepool'
'privileged'
Modified API commands:
'config' added "Log Interval"
Starting with API V1.2, any attempt to access a command that requires
privileged security, from an IP address that does not have privileged
security, will return an "Access denied" Error Status
----------
API V1.1 (cgminer v2.2.4)
There were no changes to the API commands in cgminer v2.2.4,
however support was added to cgminer for IP address restrictions
with the --api-allow option
----------
API V1.1 (cgminer v2.2.2)
Prior to V1.1, devs/gpu incorrectly reported GPU0 Intensity for all GPUs
Modified API commands:
'devs'/'gpu' added "Last Share Pool" and "Last Share Time" for each device
----------
API V1.0 (cgminer v2.2.0)
Remove default CPU support
Added API commands:
'config'
'gpucount'
'cpucount'
'switchpool'
'gpuintensity'
'gpumem'
'gpuengine'
'gpufan'
'gpuvddc'
'save'
----------
API V0.7 (cgminer v2.1.0)
Initial release of the API in the main cgminer git
Commands:
'version'
'devs'
'pools'
'summary'
'gpuenable'
'gpudisable'
'gpurestart'
'gpu'
'cpu'
'gpucount'
'cpucount'
'quit'

56
FPGA-README

@ -0,0 +1,56 @@
This README contains extended details about FPGA mining with cgminer
Icarus
There is a hidden option in cgminer when Icarus support is compiled in:
--icarus-timing <arg> Set how the Icarus timing is calculated - one setting/value for all or comma separated
default[=N] Use the default Icarus hash time (2.6316ns)
short Calculate the hash time and stop adjusting it at ~315 difficulty 1 shares (~1hr)
long Re-calculate the hash time continuously
value[=N] Specify the hash time in nanoseconds (e.g. 2.6316) and abort time (e.g. 2.6316=80)
Icarus timing is required for devices that do not exactly match a default Icarus Rev3 in
processing speed
If you have an Icarus Rev3 you should not normally need to use --icarus-timing since the
default values will maximise the MH/s and display it correctly
Icarus timing is used to determine the number of hashes that have been checked when it aborts
a nonce range (including on a LongPoll)
It is also used to determine the elapsed time when it should abort a nonce range to avoid
letting the Icarus go idle, but also to safely maximise that time
'short' or 'long' mode should only be used on a computer that has enough CPU available to run
cgminer without any CPU delays (an active desktop or swapping computer would not be stable enough)
Any CPU delays while calculating the hash time will affect the result
'short' mode only requires the computer to be stable until it has completed ~315 difficulty 1 shares
'long' mode requires it to always be stable to ensure accuracy, however, over time it continually
corrects itself
When in 'short' or 'long' mode, it will report the hash time value each time it is re-calculated
In 'short' or 'long' mode, the scan abort time starts at 5 seconds and uses the default 2.6316ns
scan hash time, for the first 5 nonce's or one minute (whichever is longer)
In 'default' or 'value' mode the 'constants' are calculated once at the start, based on the default
value or the value specified
The optional additional =N specifies to set the default abort at N 1/10ths of a second, not the
calculated value, which is 112 for 2.6316ns
To determine the hash time value for a non Icarus Rev3 device or an Icarus Rev3 with a different
bitstream to the default one, use 'long' mode and give it at least a few hundred shares, or use
'short' mode and take note of the final hash time value (Hs) calculated
You can also use the RPC API 'stats' command to see the current hash time (Hs) at any time
The Icarus code currently only works with a dual FPGA device that supports the same commands as
Icarus Rev3 requires and also is less than ~840MH/s and greater than 2MH/s
If a dual FPGA device does hash faster than ~840MH/s it should work correctly if you supply the
correct hash time nanoseconds value
The timing code itself will affect the Icarus performance since it increases the delay after
work is completed or aborted until it starts again
The increase is, however, extremely small and the actual increase is reported with the
RPC API 'stats' command (a very slow CPU will make it more noticeable)
Using the 'short' mode will remove this delay after 'short' mode completes
The delay doesn't affect the calculation of the correct hash time

2
Makefile.am

@ -10,7 +10,7 @@ endif
EXTRA_DIST = example.conf m4/gnulib-cache.m4 linux-usb-cgminer \ EXTRA_DIST = example.conf m4/gnulib-cache.m4 linux-usb-cgminer \
ADL_SDK/readme.txt api-example.php miner.php \ ADL_SDK/readme.txt api-example.php miner.php \
API.class API.java api-example.c windows-build.txt \ API.class API.java api-example.c windows-build.txt \
bitstreams/* bitstreams/* API-README FPGA-README
SUBDIRS = lib compat ccan SUBDIRS = lib compat ccan

28
NEWS

@ -1,3 +1,31 @@
Version 2.4.2 - June 2, 2012
- API.class compiled with Java SE 6.0_03 - works with Win7x64
- miner.php highlight devs too slow finding shares (possibly failing)
- API update version to V1.11 and document changes
- API save default config file if none specified
- api.c save success incorrectly returns error
- api.c replace BUFSIZ (linux/windows have different values)
- Move RPC API content out of README to API-README
- Open a longpoll connection if a pool is in the REJECTING state as it's the
only way to re-enable it automatically.
- Use only one longpoll as much as possible by using a pthread conditional
broadcast that each longpoll thread waits on and checks if it's the current pool
before
- If shares are known stale, don't use them to decide to disable a pool for
sequential rejects.
- Restarting cgminer from within after ADL has been corrupted only leads to a
crash. Display a warning only and disable fanspeed monitoring.
- Icarus: fix abort calculation/allow user specified abort
- Icarus: make --icarus-timing hidden and document it in FPGA-README
- Icarus: high accuracy timing and other bitstream speed support
- add-MIPSEB-to-icarus-for-BIG_ENDIAN
- work_decode only needs swab32 on midstate under BIG ENDIAN
- add compile command to api-example.c
- save config bugfix: writing an extra ',' when no gpus
- Add dpkg-source commits
Version 2.4.1 - May 6, 2012 Version 2.4.1 - May 6, 2012
- In the unlikely event of finding a block, display the block solved count with - In the unlikely event of finding a block, display the block solved count with

278
README

@ -209,6 +209,8 @@ FPGA mining boards(BitForce, Icarus, Ztex) only options:
On windows <arg> is usually of the format \\.\COMn On windows <arg> is usually of the format \\.\COMn
(where n = the correct device number for the FPGA device) (where n = the correct device number for the FPGA device)
For other FPGA details see the FPGA-README
CPU only options (deprecated, not included in binaries!): CPU only options (deprecated, not included in binaries!):
@ -569,281 +571,7 @@ cgminer shuts down because of this.
RPC API RPC API
If you start cgminer with the "--api-listen" option, it will listen on a For RPC API details see the API-README file
simple TCP/IP socket for single string API requests from the same machine
running cgminer and reply with a string and then close the socket each time
If you add the "--api-network" option, it will accept API requests from any
network attached computer.
You can only access the comands that reply with data in this mode.
By default, you cannot access any privileged command that affects the miner -
you will receive an access denied status message see --api-allow below.
You can specify IP addresses/prefixes that are only allowed to access the API
with the "--api-allow" option e.g. --api-allow W:192.168.0.1,10.0.0/24
will allow 192.168.0.1 or any address matching 10.0.0.*, but nothing else
IP addresses are automatically padded with extra '.0's as needed
Without a /prefix is the same as specifying /32
0/0 means all IP addresses.
The 'W:' on the front gives that address/subnet privileged access to commands
that modify cgminer.
Without it those commands return an access denied status.
Privileged access is checked in the order the IP addresses were supplied to
"--api-allow"
The first match determines the privilege level.
Using the "--api-allow" option overides the "--api-network" option if they
are both specified
With "--api-allow", 127.0.0.1 is not by default given access unless specified
The RPC API request can be either simple text or JSON.
If the request is JSON (starts with '{'), it will reply with a JSON formatted
response, otherwise it replies with text formatted as described further below.
The JSON request format required is '{"command":"CMD","parameter":"PARAM"}'
(though of course parameter is not required for all requests)
where "CMD" is from the "Request" column below and "PARAM" would be e.g.
the CPU/GPU number if required.
An example request in both formats to set GPU 0 fan to 80%:
gpufan|0,80
{"command":"gpufan","parameter":"0,80"}
The format of each reply (unless stated otherwise) is a STATUS section
followed by an optional detail section
From API version 1.7 onwards, reply strings in JSON and Text have the
necessary escaping as required to avoid ambiguity - they didn't before 1.7
For JSON the 2 characters '"' and '\' are escaped with a '\' before them
For Text the 4 characters '|' ',' '=' and '\' are escaped the same way
Only user entered information will contain characters that require being
escaped, such as Pool URL, User and Password or the Config save filename,
when they are returned in messages or as their values by the API
For API version 1.4 and later:
The STATUS section is:
STATUS=X,When=NNN,Code=N,Msg=string,Description=string|
STATUS=X Where X is one of:
W - Warning
I - Informational
S - Success
E - Error
F - Fatal (code bug)
When=NNN
Standard long time of request in seconds
Code=N
Each unique reply has a unigue Code (See api.c - #define MSG_NNNNNN)
Msg=string
Message matching the Code value N
Description=string
This defaults to the cgminer version but is the value of --api-description
if it was specified at runtime.
For API version 1.9:
The list of requests - a (*) means it requires privileged access - and replies are:
Request Reply Section Details
------- ------------- -------
version VERSION CGMiner=cgminer version
API=API version
config CONFIG Some miner configuration information:
GPU Count=N, <- the number of GPUs
PGA Count=N, <- the number of PGAs
CPU Count=N, <- the number of CPUs
Pool Count=N, <- the number of Pools
ADL=X, <- Y or N if ADL is compiled in the code
ADL in use=X, <- Y or N if any GPU has ADL
Strategy=Name, <- the current pool strategy
Log Interval=N, <- log interval (--log N)
Device Code=GPU ICA | <- spaced list of compiled devices
summary SUMMARY The status summary of the miner
e.g. Elapsed=NNN,Found Blocks=N,Getworks=N,...|
pools POOLS The status of each pool
e.g. Pool=0,URL=http://pool.com:6311,Status=Alive,...|
devs DEVS Each available GPU, PGA and CPU with their details
e.g. GPU=0,Accepted=NN,MHS av=NNN,...,Intensity=D|
Last Share Time=NNN, <- standand long time in seconds
(or 0 if none) of last accepted share
Last Share Pool=N, <- pool number (or -1 if none)
Will not report PGAs if PGA mining is disabled
Will not report CPUs if CPU mining is disabled
gpu|N GPU The details of a single GPU number N in the same
format and details as for DEVS
pga|N PGA The details of a single PGA number N in the same
format and details as for DEVS
This is only available if PGA mining is enabled
Use 'pgacount' or 'config' first to see if there are any
cpu|N CPU The details of a single CPU number N in the same
format and details as for DEVS
This is only available if CPU mining is enabled
Use 'cpucount' or 'config' first to see if there are any
gpucount GPUS Count=N| <- the number of GPUs
pgacount PGAS Count=N| <- the number of PGAs
Always returns 0 if PGA mining is disabled
cpucount CPUS Count=N| <- the number of CPUs
Always returns 0 if CPU mining is disabled
switchpool|N (*)
none There is no reply section just the STATUS section
stating the results of switching pool N to the
highest priority (the pool is also enabled)
The Msg includes the pool URL
enablepool|N (*)
none There is no reply section just the STATUS section
stating the results of enabling pool N
The Msg includes the pool URL
addpool|URL,USR,PASS (*)
none There is no reply section just the STATUS section
stating the results of attempting to add pool N
The Msg includes the pool URL
Use '\\' to get a '\' and '\,' to include a comma
inside URL, USR or PASS
disablepool|N (*)
none There is no reply section just the STATUS section
stating the results of disabling pool N
The Msg includes the pool URL
removepool|N (*)
none There is no reply section just the STATUS section
stating the results of removing pool N
The Msg includes the pool URL
N.B. all details for the pool will be lost
gpuenable|N (*)
none There is no reply section just the STATUS section
stating the results of the enable request
gpudisable|N (*)
none There is no reply section just the STATUS section
stating the results of the disable request
gpurestart|N (*)
none There is no reply section just the STATUS section
stating the results of the restart request
gpuintensity|N,I (*)
none There is no reply section just the STATUS section
stating the results of setting GPU N intensity to I
gpumem|N,V (*)
none There is no reply section just the STATUS section
stating the results of setting GPU N memoryclock to V MHz
gpuengine|N,V (*)
none There is no reply section just the STATUS section
stating the results of setting GPU N clock to V MHz
gpufan|N,V (*)
none There is no reply section just the STATUS section
stating the results of setting GPU N fan speed to V%
gpuvddc|N,V (*)
none There is no reply section just the STATUS section
stating the results of setting GPU N vddc to V
save|filename (*)
none There is no reply section just the STATUS section
stating success or failure saving the cgminer config
to filename
quit (*) none There is no status section but just a single "BYE"
reply before cgminer quits
notify NOTIFY The last status and history count of each devices problem
This lists all devices including those not supported
by the 'devs' command
e.g. NOTIFY=0,Name=GPU,ID=0,Last Well=1332432290,...|
privileged (*)
none There is no reply section just the STATUS section
stating an error if you do not have privileged access
to the API and success if you do have privilege
The command doesn't change anything in cgminer
pgaenable|N (*)
none There is no reply section just the STATUS section
stating the results of the enable request
You cannot enable a PGA if it's status is not WELL
This is only available if PGA mining is enabled
pgadisable|N (*)
none There is no reply section just the STATUS section
stating the results of the disable request
This is only available if PGA mining is enabled
devdetails DEVDETAILS Each device with a list of their static details
This lists all devices including those not supported
by the 'devs' command
e.g. DEVDETAILS=0,Name=GPU,ID=0,Driver=opencl,...|
restart (*) none There is no status section but just a single "RESTART"
reply before cgminer restarts
stats STATS Each device or pool that has 1 or more getworks
with a list of stats regarding getwork times
The values returned by stats may change in future
versions thus would not normally be displayed
Device drivers are also able to add stats to the
end of the details returned
When you enable, disable or restart a GPU or PGA, you will also get Thread messages
in the cgminer status window
When you switch to a different pool to the current one, you will get a
'Switching to URL' message in the cgminer status windows
Obviously, the JSON format is simply just the names as given before the '='
with the values after the '='
If you enable cgminer debug (-D or --debug) you will also get messages showing
details of the requests received and the replies
There are included 4 program examples for accessing the API:
api-example.php - a php script to access the API
usAge: php api-example.php command
by default it sends a 'summary' request to the miner at 127.0.0.1:4028
If you specify a command it will send that request instead
You must modify the line "$socket = getsock('127.0.0.1', 4028);" at the
beginning of "function request($cmd)" to change where it looks for cgminer
API.java/API.class
a java program to access the API (with source code)
usAge is: java API command address port
Any missing or blank parameters are replaced as if you entered:
java API summary 127.0.0.1 4028
api-example.c - a 'C' program to access the API (with source code)
usAge: api-example [command [ip/host [port]]]
again, as above, missing or blank parameters are replaced as if you entered:
api-example summary 127.0.0.1 4028
miner.php - an example web page to access the API
This includes buttons and inputs to attempt access to the privileged commands
Read the top of the file (miner.php) for details of how to tune the display
and also to use the option to display a multi-rig summary
--- ---

6
adl.c

@ -692,11 +692,7 @@ int gpu_fanpercent(int gpu)
unlock_adl(); unlock_adl();
if (unlikely(ga->has_fanspeed && ret == -1)) { if (unlikely(ga->has_fanspeed && ret == -1)) {
applog(LOG_WARNING, "GPU %d stopped reporting fanspeed due to driver corruption", gpu); applog(LOG_WARNING, "GPU %d stopped reporting fanspeed due to driver corruption", gpu);
if (opt_restart) { applog(LOG_WARNING, "You will need to start cgminer from scratch to correct this");
applog(LOG_WARNING, "Restart enabled, will restart cgminer");
applog(LOG_WARNING, "You can disable this with the --no-restart option");
app_restart();
}
applog(LOG_WARNING, "Disabling fanspeed monitoring on this device"); applog(LOG_WARNING, "Disabling fanspeed monitoring on this device");
ga->has_fanspeed = false; ga->has_fanspeed = false;
} }

34
api.c

@ -142,6 +142,9 @@
// Current code assumes it can socket send this size also // Current code assumes it can socket send this size also
#define MYBUFSIZ 65432 // TODO: intercept before it's exceeded #define MYBUFSIZ 65432 // TODO: intercept before it's exceeded
// BUFSIZ varies on Windows and Linux
#define TMPBUFSIZ 8192
// Number of requests to queue - normally would be small // Number of requests to queue - normally would be small
// However lots of PGA's may mean more // However lots of PGA's may mean more
#define QUEUE 100 #define QUEUE 100
@ -763,7 +766,7 @@ static void apiversion(__maybe_unused SOCKETTYPE c, __maybe_unused char *param,
static void minerconfig(__maybe_unused SOCKETTYPE c, __maybe_unused char *param, bool isjson) static void minerconfig(__maybe_unused SOCKETTYPE c, __maybe_unused char *param, bool isjson)
{ {
char buf[BUFSIZ]; char buf[TMPBUFSIZ];
int pgacount = 0; int pgacount = 0;
int cpucount = 0; int cpucount = 0;
char *adlinuse = (char *)NO; char *adlinuse = (char *)NO;
@ -804,7 +807,7 @@ static void minerconfig(__maybe_unused SOCKETTYPE c, __maybe_unused char *param,
static void gpustatus(int gpu, bool isjson) static void gpustatus(int gpu, bool isjson)
{ {
char intensity[20]; char intensity[20];
char buf[BUFSIZ]; char buf[TMPBUFSIZ];
char *enabled; char *enabled;
char *status; char *status;
float gt, gv; float gt, gv;
@ -856,7 +859,7 @@ static void gpustatus(int gpu, bool isjson)
#if defined(USE_BITFORCE) || defined(USE_ICARUS) || defined(USE_ZTEX) #if defined(USE_BITFORCE) || defined(USE_ICARUS) || defined(USE_ZTEX)
static void pgastatus(int pga, bool isjson) static void pgastatus(int pga, bool isjson)
{ {
char buf[BUFSIZ]; char buf[TMPBUFSIZ];
char *enabled; char *enabled;
char *status; char *status;
int numpga = numpgas(); int numpga = numpgas();
@ -908,7 +911,7 @@ static void pgastatus(int pga, bool isjson)
#ifdef WANT_CPUMINE #ifdef WANT_CPUMINE
static void cpustatus(int cpu, bool isjson) static void cpustatus(int cpu, bool isjson)
{ {
char buf[BUFSIZ]; char buf[TMPBUFSIZ];
if (opt_n_threads > 0 && cpu >= 0 && cpu < num_processors) { if (opt_n_threads > 0 && cpu >= 0 && cpu < num_processors) {
struct cgpu_info *cgpu = &cpus[cpu]; struct cgpu_info *cgpu = &cpus[cpu];
@ -1188,7 +1191,7 @@ static void cpudev(__maybe_unused SOCKETTYPE c, char *param, bool isjson)
static void poolstatus(__maybe_unused SOCKETTYPE c, __maybe_unused char *param, bool isjson) static void poolstatus(__maybe_unused SOCKETTYPE c, __maybe_unused char *param, bool isjson)
{ {
char buf[BUFSIZ]; char buf[TMPBUFSIZ];
char *status, *lp; char *status, *lp;
char *rpc_url; char *rpc_url;
char *rpc_user; char *rpc_user;
@ -1400,7 +1403,7 @@ static void gpurestart(__maybe_unused SOCKETTYPE c, char *param, bool isjson)
static void gpucount(__maybe_unused SOCKETTYPE c, __maybe_unused char *param, bool isjson) static void gpucount(__maybe_unused SOCKETTYPE c, __maybe_unused char *param, bool isjson)
{ {
char buf[BUFSIZ]; char buf[TMPBUFSIZ];
strcpy(io_buffer, message(MSG_NUMGPU, 0, NULL, isjson)); strcpy(io_buffer, message(MSG_NUMGPU, 0, NULL, isjson));
@ -1414,7 +1417,7 @@ static void gpucount(__maybe_unused SOCKETTYPE c, __maybe_unused char *param, bo
static void pgacount(__maybe_unused SOCKETTYPE c, __maybe_unused char *param, bool isjson) static void pgacount(__maybe_unused SOCKETTYPE c, __maybe_unused char *param, bool isjson)
{ {
char buf[BUFSIZ]; char buf[TMPBUFSIZ];
int count = 0; int count = 0;
#if defined(USE_BITFORCE) || defined(USE_ICARUS) || defined(USE_ZTEX) #if defined(USE_BITFORCE) || defined(USE_ICARUS) || defined(USE_ZTEX)
@ -1433,7 +1436,7 @@ static void pgacount(__maybe_unused SOCKETTYPE c, __maybe_unused char *param, bo
static void cpucount(__maybe_unused SOCKETTYPE c, __maybe_unused char *param, bool isjson) static void cpucount(__maybe_unused SOCKETTYPE c, __maybe_unused char *param, bool isjson)
{ {
char buf[BUFSIZ]; char buf[TMPBUFSIZ];
int count = 0; int count = 0;
#ifdef WANT_CPUMINE #ifdef WANT_CPUMINE
@ -1863,7 +1866,7 @@ void privileged(__maybe_unused SOCKETTYPE c, __maybe_unused char *param, bool is
void notifystatus(int device, struct cgpu_info *cgpu, bool isjson) void notifystatus(int device, struct cgpu_info *cgpu, bool isjson)
{ {
char buf[BUFSIZ]; char buf[TMPBUFSIZ];
char *reason; char *reason;
if (cgpu->device_last_not_well == 0) if (cgpu->device_last_not_well == 0)
@ -1940,7 +1943,7 @@ static void notify(__maybe_unused SOCKETTYPE c, __maybe_unused char *param, bool
static void devdetails(__maybe_unused SOCKETTYPE c, __maybe_unused char *param, bool isjson) static void devdetails(__maybe_unused SOCKETTYPE c, __maybe_unused char *param, bool isjson)
{ {
char buf[BUFSIZ]; char buf[TMPBUFSIZ];
struct cgpu_info *cgpu; struct cgpu_info *cgpu;
int i; int i;
@ -2006,7 +2009,7 @@ void dosave(__maybe_unused SOCKETTYPE c, char *param, bool isjson)
static int itemstats(int i, char *id, struct cgminer_stats *stats, char *extra, bool isjson) static int itemstats(int i, char *id, struct cgminer_stats *stats, char *extra, bool isjson)
{ {
char buf[BUFSIZ]; char buf[TMPBUFSIZ];
if (stats->getwork_calls || (extra != NULL && *extra)) if (stats->getwork_calls || (extra != NULL && *extra))
{ {
@ -2032,7 +2035,7 @@ static int itemstats(int i, char *id, struct cgminer_stats *stats, char *extra,
} }
static void minerstats(__maybe_unused SOCKETTYPE c, __maybe_unused char *param, bool isjson) static void minerstats(__maybe_unused SOCKETTYPE c, __maybe_unused char *param, bool isjson)
{ {
char extra[BUFSIZ]; char extra[TMPBUFSIZ];
char id[20]; char id[20];
int i, j; int i, j;
@ -2300,8 +2303,8 @@ static void *restart_thread(__maybe_unused void *userdata)
void api(int api_thr_id) void api(int api_thr_id)
{ {
struct thr_info bye_thr; struct thr_info bye_thr;
char buf[BUFSIZ]; char buf[TMPBUFSIZ];
char param_buf[BUFSIZ]; char param_buf[TMPBUFSIZ];
const char *localaddr = "127.0.0.1"; const char *localaddr = "127.0.0.1";
SOCKETTYPE c; SOCKETTYPE c;
int n, bound; int n, bound;
@ -2434,7 +2437,7 @@ void api(int api_thr_id)
applog(LOG_DEBUG, "API: connection from %s - %s", connectaddr, addrok ? "Accepted" : "Ignored"); applog(LOG_DEBUG, "API: connection from %s - %s", connectaddr, addrok ? "Accepted" : "Ignored");
if (addrok) { if (addrok) {
n = recv(c, &buf[0], BUFSIZ-1, 0); n = recv(c, &buf[0], TMPBUFSIZ-1, 0);
if (SOCKETFAIL(n)) if (SOCKETFAIL(n))
buf[0] = '\0'; buf[0] = '\0';
else else
@ -2563,3 +2566,4 @@ die:
mutex_unlock(&quit_restart_lock); mutex_unlock(&quit_restart_lock);
} }

116
cgminer.c

@ -134,6 +134,7 @@ bool opt_api_listen;
bool opt_api_network; bool opt_api_network;
bool opt_delaynet; bool opt_delaynet;
bool opt_disable_pool = true; bool opt_disable_pool = true;
char *opt_icarus_timing = NULL;
char *opt_kernel_path; char *opt_kernel_path;
char *cgminer_path; char *cgminer_path;
@ -165,6 +166,9 @@ static pthread_rwlock_t blk_lock;
pthread_rwlock_t netacc_lock; pthread_rwlock_t netacc_lock;
static pthread_mutex_t lp_lock;
static pthread_cond_t lp_cond;
double total_mhashes_done; double total_mhashes_done;
static struct timeval total_tv_start, total_tv_end; static struct timeval total_tv_start, total_tv_end;
@ -675,6 +679,15 @@ static char *set_api_description(const char *arg)
return NULL; return NULL;
} }
#ifdef USE_ICARUS
static char *set_icarus_timing(const char *arg)
{
opt_set_charp(arg, &opt_icarus_timing);
return NULL;
}
#endif
/* These options are available from config file or commandline */ /* These options are available from config file or commandline */
static struct opt_table opt_config_table[] = { static struct opt_table opt_config_table[] = {
#ifdef WANT_CPUMINE #ifdef WANT_CPUMINE
@ -811,6 +824,11 @@ static struct opt_table opt_config_table[] = {
OPT_WITH_ARG("--kernel|-k", OPT_WITH_ARG("--kernel|-k",
set_kernel, NULL, NULL, set_kernel, NULL, NULL,
"Override kernel to use (diablo, poclbm, phatk or diakgcn) - one value or comma separated"), "Override kernel to use (diablo, poclbm, phatk or diakgcn) - one value or comma separated"),
#endif
#ifdef USE_ICARUS
OPT_WITH_ARG("--icarus-timing",
set_icarus_timing, NULL, NULL,
opt_hidden),
#endif #endif
OPT_WITHOUT_ARG("--load-balance", OPT_WITHOUT_ARG("--load-balance",
set_loadbalance, &pool_strategy, set_loadbalance, &pool_strategy,
@ -1718,8 +1736,10 @@ static bool submit_upstream_work(const struct work *work, CURL *curl)
/* Once we have more than a nominal amount of sequential rejects, /* Once we have more than a nominal amount of sequential rejects,
* at least 10 and more than 3 mins at the current utility, * at least 10 and more than 3 mins at the current utility,
* disable the pool because some pool error is likely to have * disable the pool because some pool error is likely to have
* ensued. */ * ensued. Do not do this if we know the share just happened to
if (pool->seq_rejects > 10 && opt_disable_pool && total_pools > 1) { * be stale due to networking delays.
*/
if (pool->seq_rejects > 10 && !work->stale && opt_disable_pool && total_pools > 1) {
double utility = total_accepted / ( total_secs ? total_secs : 1 ) * 60; double utility = total_accepted / ( total_secs ? total_secs : 1 ) * 60;
if (pool->seq_rejects > utility * 3) { if (pool->seq_rejects > utility * 3) {
@ -2154,6 +2174,7 @@ static void *submit_work_thread(void *userdata)
pool->stale_shares++; pool->stale_shares++;
goto out; goto out;
} }
work->stale = true;
} }
ce = pop_curl_entry(pool); ce = pop_curl_entry(pool);
@ -2280,6 +2301,10 @@ void switch_pools(struct pool *selected)
mutex_lock(&qd_lock); mutex_lock(&qd_lock);
total_queued = 0; total_queued = 0;
mutex_unlock(&qd_lock); mutex_unlock(&qd_lock);
mutex_lock(&lp_lock);
pthread_cond_broadcast(&lp_cond);
mutex_unlock(&lp_lock);
} }
static void discard_work(struct work *work) static void discard_work(struct work *work)
@ -2618,22 +2643,15 @@ void remove_pool(struct pool *pool)
void write_config(FILE *fcfg) void write_config(FILE *fcfg)
{ {
int i = 0; int i;
int j = 0;
/* Write pool values in priority order */ /* Write pool values */
fputs("{\n\"pools\" : [", fcfg); fputs("{\n\"pools\" : [", fcfg);
while((j < total_pools) && (i < total_pools)) { for(i = 0; i < total_pools; i++) {
if(pools[i]->prio == j) { fprintf(fcfg, "%s\n\t{\n\t\t\"url\" : \"%s\",", i > 0 ? "," : "", pools[i]->rpc_url);
fprintf(fcfg, "%s\n\t{\n\t\t\"url\" : \"%s\",", i > 0 ? "," : "", pools[i]->rpc_url); fprintf(fcfg, "\n\t\t\"user\" : \"%s\",", pools[i]->rpc_user);
fprintf(fcfg, "\n\t\t\"user\" : \"%s\",", pools[i]->rpc_user); fprintf(fcfg, "\n\t\t\"pass\" : \"%s\"\n\t}", pools[i]->rpc_pass);
fprintf(fcfg, "\n\t\t\"pass\" : \"%s\"\n\t}", pools[i]->rpc_pass); }
j++;
i=0;
}
else
i++;
}
fputs("\n]\n", fcfg); fputs("\n]\n", fcfg);
if (nDevs) { if (nDevs) {
@ -2766,6 +2784,8 @@ void write_config(FILE *fcfg)
fprintf(fcfg, ",\n\"api-allow\" : \"%s\"", opt_api_allow); fprintf(fcfg, ",\n\"api-allow\" : \"%s\"", opt_api_allow);
if (strcmp(opt_api_description, PACKAGE_STRING) != 0) if (strcmp(opt_api_description, PACKAGE_STRING) != 0)
fprintf(fcfg, ",\n\"api-description\" : \"%s\"", opt_api_description); fprintf(fcfg, ",\n\"api-description\" : \"%s\"", opt_api_description);
if (opt_icarus_timing)
fprintf(fcfg, ",\n\"icarus-timing\" : \"%s\"", opt_icarus_timing);
fputs("\n}", fcfg); fputs("\n}", fcfg);
} }
@ -2986,6 +3006,23 @@ retry:
} }
#endif #endif
void default_save_file(char *filename)
{
#if defined(unix)
if (getenv("HOME") && *getenv("HOME")) {
strcpy(filename, getenv("HOME"));
strcat(filename, "/");
}
else
strcpy(filename, "");
strcat(filename, ".cgminer/");
mkdir(filename, 0777);
#else
strcpy(filename, "");
#endif
strcat(filename, def_conf);
}
#ifdef HAVE_CURSES #ifdef HAVE_CURSES
static void set_options(void) static void set_options(void)
{ {
@ -3046,19 +3083,7 @@ retry:
FILE *fcfg; FILE *fcfg;
char *str, filename[PATH_MAX], prompt[PATH_MAX + 50]; char *str, filename[PATH_MAX], prompt[PATH_MAX + 50];
#if defined(unix) default_save_file(filename);
if (getenv("HOME") && *getenv("HOME")) {
strcpy(filename, getenv("HOME"));
strcat(filename, "/");
}
else
strcpy(filename, "");
strcat(filename, ".cgminer/");
mkdir(filename, 0777);
#else
strcpy(filename, "");
#endif
strcat(filename, def_conf);
sprintf(prompt, "Config filename to write (Enter for default) [%s]", filename); sprintf(prompt, "Config filename to write (Enter for default) [%s]", filename);
str = curses_input(prompt); str = curses_input(prompt);
if (strcmp(str, "-1")) { if (strcmp(str, "-1")) {
@ -3997,6 +4022,21 @@ static struct pool *select_longpoll_pool(struct pool *cp)
return NULL; return NULL;
} }
/* This will make the longpoll thread wait till it's the current pool, or it
* has been flagged as rejecting, before attempting to open any connections.
*/
static void wait_lpcurrent(struct pool *pool)
{
if (pool->enabled == POOL_REJECTING)
return;
while (pool != current_pool()) {
mutex_lock(&lp_lock);
pthread_cond_wait(&lp_cond, &lp_lock);
mutex_unlock(&lp_lock);
}
}
static void *longpoll_thread(void *userdata) static void *longpoll_thread(void *userdata)
{ {
struct pool *cp = (struct pool *)userdata; struct pool *cp = (struct pool *)userdata;
@ -4027,6 +4067,8 @@ retry_pool:
/* Any longpoll from any pool is enough for this to be true */ /* Any longpoll from any pool is enough for this to be true */
have_longpoll = true; have_longpoll = true;
wait_lpcurrent(cp);
if (cp == pool) if (cp == pool)
applog(LOG_WARNING, "Long-polling activated for %s", pool->lp_url); applog(LOG_WARNING, "Long-polling activated for %s", pool->lp_url);
else else
@ -4035,6 +4077,8 @@ retry_pool:
while (42) { while (42) {
json_t *val, *soval; json_t *val, *soval;
wait_lpcurrent(cp);
gettimeofday(&start, NULL); gettimeofday(&start, NULL);
/* Longpoll connections can be persistent for a very long time /* Longpoll connections can be persistent for a very long time
@ -4230,13 +4274,17 @@ static void *watchdog_thread(void __maybe_unused *userdata)
} }
} }
#ifdef HAVE_OPENCL
for (i = 0; i < total_devices; ++i) { for (i = 0; i < total_devices; ++i) {
struct cgpu_info *cgpu = devices[i]; struct cgpu_info *cgpu = devices[i];
struct thr_info *thr = cgpu->thread; struct thr_info *thr = cgpu->thread;
enum dev_enable *denable; enum dev_enable *denable;
int gpu; int gpu;
if (cgpu->api->get_stats) {
cgpu->api->get_stats(cgpu);
}
#ifdef HAVE_OPENCL
if (cgpu->api != &opencl_api) if (cgpu->api != &opencl_api)
continue; continue;
/* Use only one thread per device to determine if the GPU is healthy */ /* Use only one thread per device to determine if the GPU is healthy */
@ -4256,6 +4304,7 @@ static void *watchdog_thread(void __maybe_unused *userdata)
temp, fanpercent, fanspeed, engineclock, memclock, vddc, activity, powertune); temp, fanpercent, fanspeed, engineclock, memclock, vddc, activity, powertune);
} }
#endif #endif
/* Thread is waiting on getwork or disabled */ /* Thread is waiting on getwork or disabled */
if (thr->getwork || *denable == DEV_DISABLED) if (thr->getwork || *denable == DEV_DISABLED)
continue; continue;
@ -4303,8 +4352,9 @@ static void *watchdog_thread(void __maybe_unused *userdata)
if (opt_restart) if (opt_restart)
reinit_device(thr->cgpu); reinit_device(thr->cgpu);
} }
#endif /* HAVE_OPENCL */
} }
#endif
} }
return NULL; return NULL;
@ -4730,6 +4780,10 @@ int main(int argc, char *argv[])
rwlock_init(&blk_lock); rwlock_init(&blk_lock);
rwlock_init(&netacc_lock); rwlock_init(&netacc_lock);
mutex_init(&lp_lock);
if (unlikely(pthread_cond_init(&lp_cond, NULL)))
quit(1, "Failed to pthread_cond_init lp_cond");
sprintf(packagename, "%s %s", PACKAGE, VERSION); sprintf(packagename, "%s %s", PACKAGE, VERSION);
#ifdef WANT_CPUMINE #ifdef WANT_CPUMINE

2
configure.ac

@ -2,7 +2,7 @@
##--##--##--##--##--##--##--##--##--##--##--##--##--##--##--##--## ##--##--##--##--##--##--##--##--##--##--##--##--##--##--##--##--##
m4_define([v_maj], [2]) m4_define([v_maj], [2])
m4_define([v_min], [4]) m4_define([v_min], [4])
m4_define([v_mic], [1]) m4_define([v_mic], [2])
##--##--##--##--##--##--##--##--##--##--##--##--##--##--##--##--## ##--##--##--##--##--##--##--##--##--##--##--##--##--##--##--##--##
m4_define([v_ver], [v_maj.v_min.v_mic]) m4_define([v_ver], [v_maj.v_min.v_mic])
m4_define([lt_rev], m4_eval(v_maj + v_min)) m4_define([lt_rev], m4_eval(v_maj + v_min))

30
debian/changelog vendored

@ -1,3 +1,33 @@
cgminer (2.4.2-1) stable; urgency=medium
Version 2.4.2 - June 2, 2012
- API.class compiled with Java SE 6.0_03 - works with Win7x64
- miner.php highlight devs too slow finding shares (possibly failing)
- API update version to V1.11 and document changes
- API save default config file if none specified
- api.c save success incorrectly returns error
- api.c replace BUFSIZ (linux/windows have different values)
- Move RPC API content out of README to API-README
- Open a longpoll connection if a pool is in the REJECTING state as it's the
only way to re-enable it automatically.
- Use only one longpoll as much as possible by using a pthread conditional
broadcast that each longpoll thread waits on and checks if it's the current pool
before
- If shares are known stale, don't use them to decide to disable a pool for
sequential rejects.
- Restarting cgminer from within after ADL has been corrupted only leads to a
crash. Display a warning only and disable fanspeed monitoring.
- Icarus: fix abort calculation/allow user specified abort
- Icarus: make --icarus-timing hidden and document it in FPGA-README
- Icarus: high accuracy timing and other bitstream speed support
- add-MIPSEB-to-icarus-for-BIG_ENDIAN
- work_decode only needs swab32 on midstate under BIG ENDIAN
- add compile command to api-example.c
- save config bugfix: writing an extra ',' when no gpus
- Add dpkg-source commits
-- nushor <nushor11@gmail.com> Sun, 03 Jun 2012 22:02:03 -0500
cgminer (2.4.1-1) stable; urgency=low cgminer (2.4.1-1) stable; urgency=low
Version 2.4.1-1 - May 6, 2012 Version 2.4.1-1 - May 6, 2012
- In the unlikely event of finding a block, display the block solved count with - In the unlikely event of finding a block, display the block solved count with

1
debian/patches/series vendored

@ -1 +0,0 @@
v2.4.1

64422
debian/patches/v2.4.2 vendored

File diff suppressed because it is too large Load Diff

178
driver-bitforce.c

@ -37,6 +37,9 @@
#include "elist.h" #include "elist.h"
#include "miner.h" #include "miner.h"
#define BITFORCE_SLEEP_US 4500000
#define BITFORCE_SLEEP_MS (BITFORCE_SLEEP_US/1000)
#define BITFORCE_TIMEOUT_MS 30000
struct device_api bitforce_api; struct device_api bitforce_api;
@ -103,7 +106,7 @@ static bool bitforce_detect_one(const char *devpath)
int fdDev = BFopen(devpath); int fdDev = BFopen(devpath);
if (unlikely(fdDev == -1)) { if (unlikely(fdDev == -1)) {
applog(LOG_DEBUG, "BitForce Detect: Failed to open %s", devpath); applog(LOG_ERR, "BitForce Detect: Failed to open %s", devpath);
return false; return false;
} }
BFwrite(fdDev, "ZGX", 3); BFwrite(fdDev, "ZGX", 3);
@ -114,7 +117,7 @@ static bool bitforce_detect_one(const char *devpath)
} }
BFclose(fdDev); BFclose(fdDev);
if (unlikely(!strstr(pdevbuf, "SHA256"))) { if (unlikely(!strstr(pdevbuf, "SHA256"))) {
applog(LOG_DEBUG, "BitForce Detect: Didn't recognise BitForce on %s", devpath); applog(LOG_ERR, "BitForce Detect: Didn't recognise BitForce on %s", devpath);
return false; return false;
} }
@ -131,6 +134,8 @@ static bool bitforce_detect_one(const char *devpath)
bitforce->name = strdup(pdevbuf + 7); bitforce->name = strdup(pdevbuf + 7);
} }
mutex_init(&bitforce->dev_lock);
return add_cgpu(bitforce); return add_cgpu(bitforce);
} }
@ -264,12 +269,58 @@ static bool bitforce_thread_prepare(struct thr_info *thr)
return true; return true;
} }
<<<<<<< HEAD
static uint64_t bitforce_get_temp(struct cgpu_info *bitforce) static uint64_t bitforce_get_temp(struct cgpu_info *bitforce)
=======
static bool bitforce_init(struct cgpu_info *bitforce)
{ {
int fdDev = bitforce->device_fd; int fdDev = bitforce->device_fd;
char *devpath = bitforce->device_path;
char pdevbuf[0x100]; char pdevbuf[0x100];
char *s; char *s;
BFclose(fdDev);
fdDev = BFopen(devpath);
if (unlikely(fdDev == -1)) {
applog(LOG_ERR, "BitForce init: Failed to open %s", devpath);
return false;
}
bitforce->device_fd = fdDev;
mutex_lock(&bitforce->dev_lock);
BFwrite(fdDev, "ZGX", 3);
BFgets(pdevbuf, sizeof(pdevbuf), fdDev);
mutex_unlock(&bitforce->dev_lock);
if (unlikely(!pdevbuf[0])) {
applog(LOG_ERR, "Error reading from BitForce (ZGX)");
return false;
}
if (unlikely(!strstr(pdevbuf, "SHA256"))) {
applog(LOG_ERR, "BitForce init: Didn't recognise BitForce on %s", devpath);
return false;
}
if (likely((!memcmp(pdevbuf, ">>>ID: ", 7)) && (s = strstr(pdevbuf + 3, ">>>"))))
{
s[0] = '\0';
bitforce->name = strdup(pdevbuf + 7);
}
return true;
}
static bool bitforce_get_temp(struct cgpu_info *bitforce)
>>>>>>> origin/master
{
int fdDev = bitforce->device_fd;
char pdevbuf[0x100];
char *s;
<<<<<<< HEAD
BFwrite(fdDev, "ZLX", 3); BFwrite(fdDev, "ZLX", 3);
BFgets(pdevbuf, sizeof(pdevbuf), fdDev); BFgets(pdevbuf, sizeof(pdevbuf), fdDev);
if (unlikely(!pdevbuf[0])) { if (unlikely(!pdevbuf[0])) {
@ -284,53 +335,89 @@ static uint64_t bitforce_get_temp(struct cgpu_info *bitforce)
applog(LOG_WARNING, "Hit thermal cutoff limit on %s %d, disabling!", bitforce->api->name, bitforce->device_id); applog(LOG_WARNING, "Hit thermal cutoff limit on %s %d, disabling!", bitforce->api->name, bitforce->device_id);
bitforce->deven = DEV_RECOVER; bitforce->deven = DEV_RECOVER;
=======
mutex_lock(&bitforce->dev_lock);
BFwrite(fdDev, "ZLX", 3);
BFgets(pdevbuf, sizeof(pdevbuf), fdDev);
mutex_unlock(&bitforce->dev_lock);
if (unlikely(!pdevbuf[0])) {
applog(LOG_ERR, "Error reading temp from BitForce (ZLX)");
return false;
}
if ((!strncasecmp(pdevbuf, "TEMP", 4)) && (s = strchr(pdevbuf + 4, ':'))) {
float temp = strtof(s + 1, NULL);
if (temp > 0) {
bitforce->temp = temp;
if (temp > bitforce->cutofftemp) {
applog(LOG_WARNING, "Hit thermal cutoff limit on %s %d, disabling!", bitforce->api->name, bitforce->device_id);
bitforce->deven = DEV_RECOVER;
>>>>>>> origin/master
bitforce->device_last_not_well = time(NULL); bitforce->device_last_not_well = time(NULL);
bitforce->device_not_well_reason = REASON_DEV_THERMAL_CUTOFF; bitforce->device_not_well_reason = REASON_DEV_THERMAL_CUTOFF;
bitforce->dev_thermal_cutoff_count++; bitforce->dev_thermal_cutoff_count++;
} }
} }
} }
<<<<<<< HEAD
return 1; return 1;
} }
static uint64_t bitforce_send_work(struct cgpu_info *bitforce, struct work *work) static uint64_t bitforce_send_work(struct cgpu_info *bitforce, struct work *work)
{ {
=======
return true;
}
static bool bitforce_send_work(struct thr_info *thr, struct work *work)
{
struct cgpu_info *bitforce = thr->cgpu;
>>>>>>> origin/master
int fdDev = bitforce->device_fd; int fdDev = bitforce->device_fd;
char pdevbuf[0x100]; char pdevbuf[0x100];
unsigned char ob[61] = ">>>>>>>>12345678901234567890123456789012123456789012>>>>>>>>"; unsigned char ob[61] = ">>>>>>>>12345678901234567890123456789012123456789012>>>>>>>>";
char *s; char *s;
mutex_lock(&bitforce->dev_lock);
BFwrite(fdDev, "ZDX", 3); BFwrite(fdDev, "ZDX", 3);
BFgets(pdevbuf, sizeof(pdevbuf), fdDev); BFgets(pdevbuf, sizeof(pdevbuf), fdDev);
if (unlikely(!pdevbuf[0])) { if (unlikely(!pdevbuf[0])) {
applog(LOG_ERR, "Error reading from BitForce (ZDX)"); applog(LOG_ERR, "Error reading from BitForce (ZDX)");
return 0; mutex_unlock(&bitforce->dev_lock);
return false;
} }
if (unlikely(pdevbuf[0] != 'O' || pdevbuf[1] != 'K')) { if (unlikely(pdevbuf[0] != 'O' || pdevbuf[1] != 'K')) {
applog(LOG_ERR, "BitForce ZDX reports: %s", pdevbuf); applog(LOG_ERR, "BitForce ZDX reports: %s", pdevbuf);
return 0; mutex_unlock(&bitforce->dev_lock);
return false;
} }
memcpy(ob + 8, work->midstate, 32); memcpy(ob + 8, work->midstate, 32);
memcpy(ob + 8 + 32, work->data + 64, 12); memcpy(ob + 8 + 32, work->data + 64, 12);
BFwrite(fdDev, ob, 60); BFwrite(fdDev, ob, 60);
if (opt_debug) { if (opt_debug) {
s = bin2hex(ob + 8, 44); s = bin2hex(ob + 8, 44);
applog(LOG_DEBUG, "BitForce block data: %s", s); applog(LOG_DEBUG, "BitForce block data: %s", s);
free(s); free(s);
} }
BFgets(pdevbuf, sizeof(pdevbuf), fdDev); BFgets(pdevbuf, sizeof(pdevbuf), fdDev);
mutex_unlock(&bitforce->dev_lock);
if (unlikely(!pdevbuf[0])) { if (unlikely(!pdevbuf[0])) {
applog(LOG_ERR, "Error reading from BitForce (block data)"); applog(LOG_ERR, "Error reading from BitForce (block data)");
return 0; return false;
} }
if (unlikely(pdevbuf[0] != 'O' || pdevbuf[1] != 'K')) { if (unlikely(pdevbuf[0] != 'O' || pdevbuf[1] != 'K')) {
applog(LOG_ERR, "BitForce block data reports: %s", pdevbuf); applog(LOG_ERR, "BitForce block data reports: %s", pdevbuf);
return 0; return false;
} }
<<<<<<< HEAD
return 1; return 1;
=======
return true;
>>>>>>> origin/master
} }
static uint64_t bitforce_get_result(struct thr_info *thr, struct work *work) static uint64_t bitforce_get_result(struct thr_info *thr, struct work *work)
@ -339,38 +426,65 @@ static uint64_t bitforce_get_result(struct thr_info *thr, struct work *work)
int fdDev = bitforce->device_fd; int fdDev = bitforce->device_fd;
char pdevbuf[0x100]; char pdevbuf[0x100];
<<<<<<< HEAD
int i; int i;
char *pnoncebuf; char *pnoncebuf;
uint32_t nonce; uint32_t nonce;
i = 5000; i = 5000;
while (i < 10000) { while (i < 10000) {
=======
char *pnoncebuf;
uint32_t nonce;
int i;
i = BITFORCE_SLEEP_MS;
while (i < BITFORCE_TIMEOUT_MS) {
mutex_lock(&bitforce->dev_lock);
>>>>>>> origin/master
BFwrite(fdDev, "ZFX", 3); BFwrite(fdDev, "ZFX", 3);
BFgets(pdevbuf, sizeof(pdevbuf), fdDev); BFgets(pdevbuf, sizeof(pdevbuf), fdDev);
mutex_unlock(&bitforce->dev_lock);
if (unlikely(!pdevbuf[0])) { if (unlikely(!pdevbuf[0])) {
applog(LOG_ERR, "Error reading from BitForce (ZFX)"); applog(LOG_ERR, "Error reading from BitForce (ZFX)");
mutex_unlock(&bitforce->dev_lock);
return 0; return 0;
} }
if (pdevbuf[0] != 'B') if (pdevbuf[0] != 'B')
break; break;
usleep(10000); usleep(10000);
i += 10; i += 10;
} }
<<<<<<< HEAD
if (i >= 10000) { if (i >= 10000) {
applog(LOG_DEBUG, "BitForce took longer than 10s"); applog(LOG_DEBUG, "BitForce took longer than 10s");
return 0; return 0;
} }
=======
if (i >= BITFORCE_TIMEOUT_MS) {
applog(LOG_ERR, "BitForce took longer than 30s");
bitforce->device_last_not_well = time(NULL);
bitforce->device_not_well_reason = REASON_THREAD_ZERO_HASH;
bitforce->thread_zero_hash_count++;
return 1;
}
>>>>>>> origin/master
applog(LOG_DEBUG, "BitForce waited %dms until %s\n", i, pdevbuf); applog(LOG_DEBUG, "BitForce waited %dms until %s\n", i, pdevbuf);
work->blk.nonce = 0xffffffff; work->blk.nonce = 0xffffffff;
if (pdevbuf[2] == '-') if (pdevbuf[2] == '-')
return 0xffffffff; /* No valid nonce found */ return 0xffffffff; /* No valid nonce found */
<<<<<<< HEAD
else if (pdevbuf[0] == 'I') else if (pdevbuf[0] == 'I')
return 0x1; /* Device idle */ return 0x1; /* Device idle */
=======
else if (pdevbuf[0] == 'I')
return 1; /* Device idle */
>>>>>>> origin/master
else if (strncasecmp(pdevbuf, "NONCE-FOUND", 11)) { else if (strncasecmp(pdevbuf, "NONCE-FOUND", 11)) {
applog(LOG_ERR, "BitForce result reports: %s", pdevbuf); applog(LOG_WARNING, "BitForce result reports: %s", pdevbuf);
return 0; return 1;
} }
pnoncebuf = &pdevbuf[12]; pnoncebuf = &pdevbuf[12];
@ -413,11 +527,53 @@ static uint64_t bitforce_scanhash(struct thr_info *thr, struct work *work, uint6
} }
static void bitforce_shutdown(struct thr_info *thr)
{
struct cgpu_info *bitforce = thr->cgpu;
int fdDev = bitforce->device_fd;
BFclose(fdDev);
}
static uint64_t bitforce_scanhash(struct thr_info *thr, struct work *work, uint64_t __maybe_unused max_nonce)
{
struct cgpu_info *bitforce = thr->cgpu;
bool dev_enabled = (bitforce->deven == DEV_ENABLED);
static enum dev_enable last_dev_state = DEV_ENABLED;
if (bitforce->deven == DEV_DISABLED) {
bitforce_shutdown(thr);
return 1;
}
// if device has just gone from disabled to enabled, re-initialise it
if (last_dev_state == DEV_DISABLED && dev_enabled)
bitforce_init(bitforce);
if (dev_enabled)
if (!bitforce_send_work(thr, work))
return 0;
usleep(BITFORCE_SLEEP_US);
if (dev_enabled)
return bitforce_get_result(thr, work);
else
return 1;
}
static bool bitforce_get_stats(struct cgpu_info *bitforce)
{
return bitforce_get_temp(bitforce);
}
struct device_api bitforce_api = { struct device_api bitforce_api = {
.dname = "bitforce", .dname = "bitforce",
.name = "BFL", .name = "BFL",
.api_detect = bitforce_detect, .api_detect = bitforce_detect,
.get_statline_before = get_bitforce_statline_before, .get_statline_before = get_bitforce_statline_before,
.get_stats = bitforce_get_stats,
.thread_prepare = bitforce_thread_prepare, .thread_prepare = bitforce_thread_prepare,
.scanhash = bitforce_scanhash, .scanhash = bitforce_scanhash,
.thread_shutdown = bitforce_shutdown
}; };

514
driver-icarus.c

@ -51,17 +51,136 @@
#include "elist.h" #include "elist.h"
#include "miner.h" #include "miner.h"
// This is valid for a standard Icarus Rev 3 // The serial I/O speed - Linux uses a define 'B115200' in bits/termios.h
// Assuming each hash pair takes 5.26ns then a whole nonce range would take 11.3s #define ICARUS_IO_SPEED 115200
// Giving a little leaway 11.1s would be best
//#define ICARUS_READ_COUNT_DEFAULT 111
#define ICARUS_READ_COUNT_DEFAULT 80
// 2 x 11.1 / (5.26 x 10^-9) // The size of a successful nonce read
//#define ESTIMATE_HASHES 0xFB90365E #define ICARUS_READ_SIZE 4
// This is the 8s value // Ensure the sizes are correct for the Serial read
#define ESTIMATE_HASHES 0xB54E9147 #if (ICARUS_READ_SIZE != 4)
#error ICARUS_READ_SIZE must be 4
#endif
#define ASSERT1(condition) __maybe_unused static char sizeof_uint32_t_must_be_4[(condition)?1:-1]
ASSERT1(sizeof(uint32_t) == 4);
#define ICARUS_READ_TIME ((double)ICARUS_READ_SIZE * (double)8.0 / (double)ICARUS_IO_SPEED)
// Fraction of a second, USB timeout is measured in
// i.e. 10 means 1/10 of a second
#define TIME_FACTOR 10
// In Linux it's 10 per second, thus value = 10/TIME_FACTOR =
#define LINUX_TIMEOUT_VALUE 1
// In Windows it's 1000 per second, thus value = 1000/TIME_FACTOR =
#define WINDOWS_TIMEOUT_VALUE 100
// In timing mode: Default starting value until an estimate can be obtained
// 5 seconds allows for up to a ~840MH/s device
#define ICARUS_READ_COUNT_TIMING (5 * TIME_FACTOR)
// For a standard Icarus REV3 (to 5 places)
// Since this rounds up a the last digit - it is a slight overestimate
// Thus the hash rate will be a VERY slight underestimate
// (by a lot less than the displayed accuracy)
#define ICARUS_REV3_HASH_TIME 0.0000000026316
#define NANOSEC 1000000000.0
// Icarus Rev3 doesn't send a completion message when it finishes
// the full nonce range, so to avoid being idle we must abort the
// work (by starting a new work) shortly before it finishes
//
// Thus we need to estimate 2 things:
// 1) How many hashes were done if the work was aborted
// 2) How high can the timeout be before the Icarus is idle,
// to minimise the number of work started
// We set 2) to 'the calculated estimate' - 1
// to ensure the estimate ends before idle
//
// The simple calculation used is:
// Tn = Total time in seconds to calculate n hashes
// Hs = seconds per hash
// Xn = number of hashes
// W = code overhead per work
//
// Rough but reasonable estimate:
// Tn = Hs * Xn + W (of the form y = mx + b)
//
// Thus:
// Line of best fit (using least squares)
//
// Hs = (n*Sum(XiTi)-Sum(Xi)*Sum(Ti))/(n*Sum(Xi^2)-Sum(Xi)^2)
// W = Sum(Ti)/n - (Hs*Sum(Xi))/n
//
// N.B. W is less when aborting work since we aren't waiting for the reply
// to be transferred back (ICARUS_READ_TIME)
// Calculating the hashes aborted at n seconds is thus just n/Hs
// (though this is still a slight overestimate due to code delays)
//
// Both below must be exceeded to complete a set of data
// Minimum how long after the first, the last data point must be
#define HISTORY_SEC 60
// Minimum how many points a single ICARUS_HISTORY should have
#define MIN_DATA_COUNT 5
// The value above used is doubled each history until it exceeds:
#define MAX_MIN_DATA_COUNT 100
static struct timeval history_sec = { HISTORY_SEC, 0 };
// Store the last INFO_HISTORY data sets
// [0] = current data, not yet ready to be included as an estimate
// Each new data set throws the last old set off the end thus
// keeping a ongoing average of recent data
#define INFO_HISTORY 10
struct ICARUS_HISTORY {
struct timeval finish;
double sumXiTi;
double sumXi;
double sumTi;
double sumXi2;
uint32_t values;
uint32_t hash_count_min;
uint32_t hash_count_max;
};
enum timing_mode { MODE_DEFAULT, MODE_SHORT, MODE_LONG, MODE_VALUE };
static const char *MODE_DEFAULT_STR = "default";
static const char *MODE_SHORT_STR = "short";
static const char *MODE_LONG_STR = "long";
static const char *MODE_VALUE_STR = "value";
static const char *MODE_UNKNOWN_STR = "unknown";
struct ICARUS_INFO {
// time to calculate the golden_ob
uint64_t golden_hashes;
struct timeval golden_tv;
struct ICARUS_HISTORY history[INFO_HISTORY+1];
uint32_t min_data_count;
// seconds per Hash
double Hs;
int read_count;
enum timing_mode timing_mode;
bool do_icarus_timing;
double fullnonce;
int count;
double W;
uint32_t values;
uint64_t hash_count_range;
// Determine the cost of history processing
// (which will only affect W)
uint64_t history_count;
struct timeval history_time;
};
// One for each possible device
static struct ICARUS_INFO *icarus_info[MAX_DEVICES];
struct device_api icarus_api; struct device_api icarus_api;
@ -98,7 +217,7 @@ static int icarus_open(const char *devpath)
ISTRIP | INLCR | IGNCR | ICRNL | IXON); ISTRIP | INLCR | IGNCR | ICRNL | IXON);
my_termios.c_oflag &= ~OPOST; my_termios.c_oflag &= ~OPOST;
my_termios.c_lflag &= ~(ECHO | ECHONL | ICANON | ISIG | IEXTEN); my_termios.c_lflag &= ~(ECHO | ECHONL | ICANON | ISIG | IEXTEN);
my_termios.c_cc[VTIME] = 1; /* block 0.1 second */ my_termios.c_cc[VTIME] = LINUX_TIMEOUT_VALUE; /* how long to block */
my_termios.c_cc[VMIN] = 0; my_termios.c_cc[VMIN] = 0;
tcsetattr(serialfd, TCSANOW, &my_termios); tcsetattr(serialfd, TCSANOW, &my_termios);
@ -119,7 +238,7 @@ static int icarus_open(const char *devpath)
comCfg.dwSize = sizeof(COMMCONFIG); comCfg.dwSize = sizeof(COMMCONFIG);
comCfg.wVersion = 1; comCfg.wVersion = 1;
comCfg.dcb.DCBlength = sizeof(DCB); comCfg.dcb.DCBlength = sizeof(DCB);
comCfg.dcb.BaudRate = 115200; comCfg.dcb.BaudRate = ICARUS_IO_SPEED;
comCfg.dcb.fBinary = 1; comCfg.dcb.fBinary = 1;
comCfg.dcb.fDtrControl = DTR_CONTROL_ENABLE; comCfg.dcb.fDtrControl = DTR_CONTROL_ENABLE;
comCfg.dcb.fRtsControl = RTS_CONTROL_ENABLE; comCfg.dcb.fRtsControl = RTS_CONTROL_ENABLE;
@ -127,42 +246,57 @@ static int icarus_open(const char *devpath)
SetCommConfig(hSerial, &comCfg, sizeof(comCfg)); SetCommConfig(hSerial, &comCfg, sizeof(comCfg));
// block 0.1 second // How long to block
COMMTIMEOUTS cto = {100, 0, 100, 0, 100}; COMMTIMEOUTS cto = {WINDOWS_TIMEOUT_VALUE, 0, WINDOWS_TIMEOUT_VALUE, 0, WINDOWS_TIMEOUT_VALUE};
SetCommTimeouts(hSerial, &cto); SetCommTimeouts(hSerial, &cto);
return _open_osfhandle((LONG)hSerial, 0); return _open_osfhandle((LONG)hSerial, 0);
#endif #endif
} }
static int icarus_gets(unsigned char *buf, size_t bufLen, int fd, int thr_id, int read_count) static int icarus_gets(unsigned char *buf, int fd, struct timeval *tv_finish, int thr_id, int read_count)
{ {
ssize_t ret = 0; ssize_t ret = 0;
int rc = 0; int rc = 0;
int read_amount = ICARUS_READ_SIZE;
bool first = true;
while (bufLen) { // Read reply 1 byte at a time to get earliest tv_finish
while (true) {
ret = read(fd, buf, 1); ret = read(fd, buf, 1);
if (ret == 1) {
bufLen--; if (first)
buf++; gettimeofday(tv_finish, NULL);
if (ret >= read_amount)
return 0;
if (ret > 0) {
buf += ret;
read_amount -= ret;
first = false;
continue; continue;
} }
rc++; rc++;
if (rc >= read_count) { if (rc >= read_count) {
applog(LOG_DEBUG, if (opt_debug) {
"Icarus Read: No data in %.2f seconds", (float)(rc/10.0f)); applog(LOG_DEBUG,
"Icarus Read: No data in %.2f seconds",
(float)rc/(float)TIME_FACTOR);
}
return 1; return 1;
} }
if (thr_id >= 0 && work_restart[thr_id].restart) { if (thr_id >= 0 && work_restart[thr_id].restart) {
applog(LOG_DEBUG, if (opt_debug) {
"Icarus Read: Work restart at %.2f seconds", (float)(rc/10.0f)); applog(LOG_DEBUG,
"Icarus Read: Work restart at %.2f seconds",
(float)(rc)/(float)TIME_FACTOR);
}
return 1; return 1;
} }
} }
return 0;
} }
static int icarus_write(int fd, const void *buf, size_t bufLen) static int icarus_write(int fd, const void *buf, size_t bufLen)
@ -178,8 +312,110 @@ static int icarus_write(int fd, const void *buf, size_t bufLen)
#define icarus_close(fd) close(fd) #define icarus_close(fd) close(fd)
static const char *timing_mode_str(enum timing_mode timing_mode)
{
switch(timing_mode) {
case MODE_DEFAULT:
return MODE_DEFAULT_STR;
case MODE_SHORT:
return MODE_SHORT_STR;
case MODE_LONG:
return MODE_LONG_STR;
case MODE_VALUE:
return MODE_VALUE_STR;
default:
return MODE_UNKNOWN_STR;
}
}
static void set_timing_mode(struct cgpu_info *icarus)
{
struct ICARUS_INFO *info = icarus_info[icarus->device_id];
double Hs;
char buf[BUFSIZ+1];
char *ptr, *comma, *eq;
size_t max;
int i;
if (opt_icarus_timing == NULL)
buf[0] = '\0';
else {
ptr = opt_icarus_timing;
for (i = 0; i < icarus->device_id; i++) {
comma = strchr(ptr, ',');
if (comma == NULL)
break;
ptr = comma + 1;
}
comma = strchr(ptr, ',');
if (comma == NULL)
max = strlen(ptr);
else
max = comma - ptr;
if (max > BUFSIZ)
max = BUFSIZ;
strncpy(buf, ptr, max);
buf[max] = '\0';
}
info->Hs = 0;
info->read_count = 0;
if (strcasecmp(buf, MODE_SHORT_STR) == 0) {
info->Hs = ICARUS_REV3_HASH_TIME;
info->read_count = ICARUS_READ_COUNT_TIMING;
info->timing_mode = MODE_SHORT;
info->do_icarus_timing = true;
} else if (strcasecmp(buf, MODE_LONG_STR) == 0) {
info->Hs = ICARUS_REV3_HASH_TIME;
info->read_count = ICARUS_READ_COUNT_TIMING;
info->timing_mode = MODE_LONG;
info->do_icarus_timing = true;
} else if ((Hs = atof(buf)) != 0) {
info->Hs = Hs / NANOSEC;
info->fullnonce = info->Hs * (((double)0xffffffff) + 1);
if ((eq = strchr(buf, '=')) != NULL)
info->read_count = atoi(eq+1);
if (info->read_count < 1)
info->read_count = (int)(info->fullnonce * TIME_FACTOR) - 1;
if (unlikely(info->read_count < 1))
info->read_count = 1;
info->timing_mode = MODE_VALUE;
info->do_icarus_timing = false;
} else {
// Anything else in buf just uses DEFAULT mode
info->Hs = ICARUS_REV3_HASH_TIME;
info->fullnonce = info->Hs * (((double)0xffffffff) + 1);
if ((eq = strchr(buf, '=')) != NULL)
info->read_count = atoi(eq+1);
if (info->read_count < 1)
info->read_count = (int)(info->fullnonce * TIME_FACTOR) - 1;
info->timing_mode = MODE_DEFAULT;
info->do_icarus_timing = false;
}
info->min_data_count = MIN_DATA_COUNT;
applog(LOG_DEBUG, "Icarus: Init: %d mode=%s read_count=%d Hs=%e",
icarus->device_id, timing_mode_str(info->timing_mode), info->read_count, info->Hs);
}
static bool icarus_detect_one(const char *devpath) static bool icarus_detect_one(const char *devpath)
{ {
struct ICARUS_INFO *info;
struct timeval tv_start, tv_finish; struct timeval tv_start, tv_finish;
int fd; int fd;
@ -194,8 +430,9 @@ static bool icarus_detect_one(const char *devpath)
"0000000087320b1a1426674f2fa722ce"; "0000000087320b1a1426674f2fa722ce";
const char golden_nonce[] = "000187a2"; const char golden_nonce[] = "000187a2";
const uint32_t golden_nonce_val = 0x000187a2;
unsigned char ob_bin[64], nonce_bin[4]; unsigned char ob_bin[64], nonce_bin[ICARUS_READ_SIZE];
char *nonce_hex; char *nonce_hex;
if (total_devices == MAX_DEVICES) if (total_devices == MAX_DEVICES)
@ -212,8 +449,7 @@ static bool icarus_detect_one(const char *devpath)
gettimeofday(&tv_start, NULL); gettimeofday(&tv_start, NULL);
memset(nonce_bin, 0, sizeof(nonce_bin)); memset(nonce_bin, 0, sizeof(nonce_bin));
icarus_gets(nonce_bin, sizeof(nonce_bin), fd, -1, 1); icarus_gets(nonce_bin, fd, &tv_finish, -1, 1);
gettimeofday(&tv_finish, NULL);
icarus_close(fd); icarus_close(fd);
@ -221,16 +457,16 @@ static bool icarus_detect_one(const char *devpath)
if (nonce_hex) { if (nonce_hex) {
if (strncmp(nonce_hex, golden_nonce, 8)) { if (strncmp(nonce_hex, golden_nonce, 8)) {
applog(LOG_ERR, applog(LOG_ERR,
"Icarus Detect: " "Icarus Detect: "
"Test failed at %s: get %s, should: %s", "Test failed at %s: get %s, should: %s",
devpath, nonce_hex, golden_nonce); devpath, nonce_hex, golden_nonce);
free(nonce_hex); free(nonce_hex);
return false; return false;
} }
applog(LOG_DEBUG, applog(LOG_DEBUG,
"Icarus Detect: " "Icarus Detect: "
"Test succeeded at %s: got %s", "Test succeeded at %s: got %s",
devpath, nonce_hex); devpath, nonce_hex);
free(nonce_hex); free(nonce_hex);
} else } else
return false; return false;
@ -244,7 +480,23 @@ static bool icarus_detect_one(const char *devpath)
add_cgpu(icarus); add_cgpu(icarus);
applog(LOG_INFO, "Found Icarus at %s, mark as %d", applog(LOG_INFO, "Found Icarus at %s, mark as %d",
devpath, icarus->device_id); devpath, icarus->device_id);
if (icarus_info[icarus->device_id] == NULL) {
icarus_info[icarus->device_id] = (struct ICARUS_INFO *)malloc(sizeof(struct ICARUS_INFO));
if (unlikely(!(icarus_info[icarus->device_id])))
quit(1, "Failed to malloc ICARUS_INFO");
}
info = icarus_info[icarus->device_id];
// Initialise everything to zero for a new device
memset(info, 0, sizeof(struct ICARUS_INFO));
info->golden_hashes = (golden_nonce_val & 0x7fffffff) << 1;
timersub(&tv_finish, &tv_start, &(info->golden_tv));
set_timing_mode(icarus);
return true; return true;
} }
@ -295,11 +547,24 @@ static uint64_t icarus_scanhash(struct thr_info *thr, struct work *work,
int fd; int fd;
int ret; int ret;
unsigned char ob_bin[64], nonce_bin[4]; struct ICARUS_INFO *info;
char *ob_hex, *nonce_hex;
unsigned char ob_bin[64], nonce_bin[ICARUS_READ_SIZE];
char *ob_hex;
uint32_t nonce; uint32_t nonce;
uint32_t hash_count; uint64_t hash_count;
struct timeval tv_start, tv_finish, elapsed; struct timeval tv_start, tv_finish, elapsed;
struct timeval tv_history_start, tv_history_finish;
double Ti, Xi;
int i;
struct ICARUS_HISTORY *history0, *history;
int count;
double Hs, W, fullnonce;
int read_count;
uint64_t estimate_hashes;
uint32_t values;
uint64_t hash_count_range;
elapsed.tv_sec = elapsed.tv_usec = 0; elapsed.tv_sec = elapsed.tv_usec = 0;
@ -315,39 +580,49 @@ static uint64_t icarus_scanhash(struct thr_info *thr, struct work *work,
tcflush(fd, TCOFLUSH); tcflush(fd, TCOFLUSH);
#endif #endif
ret = icarus_write(fd, ob_bin, sizeof(ob_bin)); ret = icarus_write(fd, ob_bin, sizeof(ob_bin));
if (opt_debug)
gettimeofday(&tv_start, NULL);
if (ret) if (ret)
return 0; /* This should never happen */ return 0; /* This should never happen */
gettimeofday(&tv_start, NULL);
if (opt_debug) { if (opt_debug) {
ob_hex = bin2hex(ob_bin, sizeof(ob_bin)); ob_hex = bin2hex(ob_bin, sizeof(ob_bin));
if (ob_hex) { if (ob_hex) {
applog(LOG_DEBUG, "Icarus %d sent: %s", applog(LOG_DEBUG, "Icarus %d sent: %s",
icarus->device_id, ob_hex); icarus->device_id, ob_hex);
free(ob_hex); free(ob_hex);
} }
} }
/* Icarus will return 4 bytes nonces or nothing */ /* Icarus will return 4 bytes (ICARUS_READ_SIZE) nonces or nothing */
memset(nonce_bin, 0, sizeof(nonce_bin)); memset(nonce_bin, 0, sizeof(nonce_bin));
ret = icarus_gets(nonce_bin, sizeof(nonce_bin), fd, thr_id, info = icarus_info[icarus->device_id];
ICARUS_READ_COUNT_DEFAULT); ret = icarus_gets(nonce_bin, fd, &tv_finish, thr_id, info->read_count);
if (opt_debug)
gettimeofday(&tv_finish, NULL);
work->blk.nonce = 0xffffffff; work->blk.nonce = 0xffffffff;
memcpy((char *)&nonce, nonce_bin, sizeof(nonce_bin)); memcpy((char *)&nonce, nonce_bin, sizeof(nonce_bin));
// aborted before becoming idle, get new work // aborted before becoming idle, get new work
if (nonce == 0 && ret) { if (nonce == 0 && ret) {
timersub(&tv_finish, &tv_start, &elapsed);
// ONLY up to just when it aborted
// We didn't read a reply so we don't subtract ICARUS_READ_TIME
estimate_hashes = ((double)(elapsed.tv_sec)
+ ((double)(elapsed.tv_usec))/((double)1000000)) / info->Hs;
// If some Serial-USB delay allowed the full nonce range to
// complete it can't have done more than a full nonce
if (unlikely(estimate_hashes > 0xffffffff))
estimate_hashes = 0xffffffff;
if (opt_debug) { if (opt_debug) {
timersub(&tv_finish, &tv_start, &elapsed); applog(LOG_DEBUG, "Icarus %d no nonce = 0x%08llx hashes (%ld.%06lds)",
applog(LOG_DEBUG, "Icarus %d no nonce = 0x%08x hashes (%ld.%06lds)", icarus->device_id, estimate_hashes,
icarus->device_id, ESTIMATE_HASHES, elapsed.tv_sec, elapsed.tv_usec); elapsed.tv_sec, elapsed.tv_usec);
} }
return ESTIMATE_HASHES;
return estimate_hashes;
} }
#if !defined (__BIG_ENDIAN__) && !defined(MIPSEB) #if !defined (__BIG_ENDIAN__) && !defined(MIPSEB)
@ -356,28 +631,136 @@ static uint64_t icarus_scanhash(struct thr_info *thr, struct work *work,
submit_nonce(thr, work, nonce); submit_nonce(thr, work, nonce);
if (opt_debug) {
timersub(&tv_finish, &tv_start, &elapsed);
nonce_hex = bin2hex(nonce_bin, sizeof(nonce_bin));
if (nonce_hex) {
applog(LOG_DEBUG, "Icarus %d returned (elapsed %ld.%06ld seconds): %s",
icarus->device_id, elapsed.tv_sec, elapsed.tv_usec, nonce_hex);
free(nonce_hex);
}
}
hash_count = (nonce & 0x7fffffff); hash_count = (nonce & 0x7fffffff);
if (hash_count++ == 0x7fffffff) if (hash_count++ == 0x7fffffff)
hash_count = 0xffffffff; hash_count = 0xffffffff;
else else
hash_count <<= 1; hash_count <<= 1;
if (opt_debug) if (opt_debug || info->do_icarus_timing)
applog(LOG_DEBUG, "Icarus %d nonce = 0x%08x = 0x%08x hashes (%ld.%06lds)", timersub(&tv_finish, &tv_start, &elapsed);
if (opt_debug) {
applog(LOG_DEBUG, "Icarus %d nonce = 0x%08x = 0x%08llx hashes (%ld.%06lds)",
icarus->device_id, nonce, hash_count, elapsed.tv_sec, elapsed.tv_usec); icarus->device_id, nonce, hash_count, elapsed.tv_sec, elapsed.tv_usec);
}
return hash_count; // ignore possible end condition values
if (info->do_icarus_timing && (nonce & 0x7fffffff) > 0x000fffff && (nonce & 0x7fffffff) < 0x7ff00000) {
gettimeofday(&tv_history_start, NULL);
history0 = &(info->history[0]);
if (history0->values == 0)
timeradd(&tv_start, &history_sec, &(history0->finish));
Ti = (double)(elapsed.tv_sec)
+ ((double)(elapsed.tv_usec))/((double)1000000)
- ICARUS_READ_TIME;
Xi = (double)hash_count;
history0->sumXiTi += Xi * Ti;
history0->sumXi += Xi;
history0->sumTi += Ti;
history0->sumXi2 += Xi * Xi;
history0->values++;
if (history0->hash_count_max < hash_count)
history0->hash_count_max = hash_count;
if (history0->hash_count_min > hash_count || history0->hash_count_min == 0)
history0->hash_count_min = hash_count;
if (history0->values >= info->min_data_count
&& timercmp(&tv_start, &(history0->finish), >)) {
for (i = INFO_HISTORY; i > 0; i--)
memcpy(&(info->history[i]),
&(info->history[i-1]),
sizeof(struct ICARUS_HISTORY));
// Initialise history0 to zero for summary calculation
memset(history0, 0, sizeof(struct ICARUS_HISTORY));
// We just completed a history data set
// So now recalc read_count based on the whole history thus we will
// initially get more accurate until it completes INFO_HISTORY
// total data sets
count = 0;
for (i = 1 ; i <= INFO_HISTORY; i++) {
history = &(info->history[i]);
if (history->values >= MIN_DATA_COUNT) {
count++;
history0->sumXiTi += history->sumXiTi;
history0->sumXi += history->sumXi;
history0->sumTi += history->sumTi;
history0->sumXi2 += history->sumXi2;
history0->values += history->values;
if (history0->hash_count_max < history->hash_count_max)
history0->hash_count_max = history->hash_count_max;
if (history0->hash_count_min > history->hash_count_min || history0->hash_count_min == 0)
history0->hash_count_min = history->hash_count_min;
}
}
// All history data
Hs = (history0->values*history0->sumXiTi - history0->sumXi*history0->sumTi)
/ (history0->values*history0->sumXi2 - history0->sumXi*history0->sumXi);
W = history0->sumTi/history0->values - Hs*history0->sumXi/history0->values;
hash_count_range = history0->hash_count_max - history0->hash_count_min;
values = history0->values;
// Initialise history0 to zero for next data set
memset(history0, 0, sizeof(struct ICARUS_HISTORY));
fullnonce = W + Hs * (((double)0xffffffff) + 1);
read_count = (int)(fullnonce * TIME_FACTOR) - 1;
info->Hs = Hs;
info->read_count = read_count;
info->fullnonce = fullnonce;
info->count = count;
info->W = W;
info->values = values;
info->hash_count_range = hash_count_range;
if (info->min_data_count < MAX_MIN_DATA_COUNT)
info->min_data_count *= 2;
else if (info->timing_mode == MODE_SHORT)
info->do_icarus_timing = false;
// applog(LOG_WARNING, "Icarus %d 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->device_id, read_count, fullnonce, count, Hs, W, values, hash_count_range, info->min_data_count);
applog(LOG_WARNING, "Icarus %d Re-estimate: Hs=%e W=%e read_count=%d fullnonce=%.3fs",
icarus->device_id, Hs, W, read_count, fullnonce);
}
info->history_count++;
gettimeofday(&tv_history_finish, NULL);
timersub(&tv_history_finish, &tv_history_start, &tv_history_finish);
timeradd(&tv_history_finish, &(info->history_time), &(info->history_time));
}
return hash_count;
}
static void icarus_api_stats(char *buf, struct cgpu_info *cgpu, bool isjson)
{
struct ICARUS_INFO *info = icarus_info[cgpu->device_id];
// Warning, access to these is not locked - but we don't really
// care since hashing performance is way more important than
// locking access to displaying API debug 'stats'
sprintf(buf, isjson
? "\"read_count\":%d,\"fullnonce\":%f,\"count\":%d,\"Hs\":%.15f,\"W\":%f,\"total_values\":%u,\"range\":%"PRIu64",\"history_count\":%"PRIu64",\"history_time\":%f,\"min_data_count\":%u,\"timing_values\":%u"
: "read_count=%d,fullnonce=%f,count=%d,Hs=%.15f,W=%f,total_values=%u,range=%"PRIu64",history_count=%"PRIu64",history_time=%f,min_data_count=%u,timing_values=%u",
info->read_count, info->fullnonce,
info->count, info->Hs, info->W,
info->values, info->hash_count_range,
info->history_count,
(double)(info->history_time.tv_sec)
+ ((double)(info->history_time.tv_usec))/((double)1000000),
info->min_data_count, info->history[0].values);
} }
static void icarus_shutdown(struct thr_info *thr) static void icarus_shutdown(struct thr_info *thr)
@ -390,6 +773,7 @@ struct device_api icarus_api = {
.dname = "icarus", .dname = "icarus",
.name = "ICA", .name = "ICA",
.api_detect = icarus_detect, .api_detect = icarus_detect,
.get_api_stats = icarus_api_stats,
.thread_prepare = icarus_prepare, .thread_prepare = icarus_prepare,
.scanhash = icarus_scanhash, .scanhash = icarus_scanhash,
.thread_shutdown = icarus_shutdown, .thread_shutdown = icarus_shutdown,

7
miner.h

@ -235,6 +235,7 @@ struct device_api {
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*);
void (*get_api_stats)(char*, struct cgpu_info*, bool); void (*get_api_stats)(char*, struct cgpu_info*, bool);
bool (*get_stats)(struct cgpu_info*);
// Thread-specific functions // Thread-specific functions
bool (*thread_prepare)(struct thr_info*); bool (*thread_prepare)(struct thr_info*);
@ -366,6 +367,8 @@ struct cgpu_info {
int dev_thermal_cutoff_count; int dev_thermal_cutoff_count;
struct cgminer_stats cgminer_stats; struct cgminer_stats cgminer_stats;
pthread_mutex_t dev_lock;
}; };
extern bool add_cgpu(struct cgpu_info*); extern bool add_cgpu(struct cgpu_info*);
@ -515,6 +518,7 @@ extern bool opt_api_listen;
extern bool opt_api_network; extern bool opt_api_network;
extern bool opt_delaynet; extern bool opt_delaynet;
extern bool opt_restart; extern bool opt_restart;
extern char *opt_icarus_timing;
extern pthread_rwlock_t netacc_lock; extern pthread_rwlock_t netacc_lock;
@ -717,11 +721,13 @@ struct work {
int thr_id; int thr_id;
struct pool *pool; struct pool *pool;
struct timeval tv_staged; struct timeval tv_staged;
bool mined; bool mined;
bool clone; bool clone;
bool cloned; bool cloned;
bool rolltime; bool rolltime;
bool longpoll; bool longpoll;
bool stale;
unsigned int work_block; unsigned int work_block;
int id; int id;
@ -740,6 +746,7 @@ extern void kill_work(void);
extern void switch_pools(struct pool *selected); extern void switch_pools(struct pool *selected);
extern void remove_pool(struct pool *pool); extern void remove_pool(struct pool *pool);
extern void write_config(FILE *fcfg); extern void write_config(FILE *fcfg);
extern void default_save_file(char *filename);
extern void log_curses(int prio, const char *f, va_list ap); extern void log_curses(int prio, const char *f, va_list ap);
extern void clear_logwin(void); extern void clear_logwin(void);
extern bool pool_tclear(struct pool *pool, bool *var); extern bool pool_tclear(struct pool *pool, bool *var);

337
miner.php

@ -2,6 +2,7 @@
session_start(); session_start();
# #
global $miner, $port, $readonly, $notify, $rigs, $socktimeoutsec; global $miner, $port, $readonly, $notify, $rigs, $socktimeoutsec;
global $checklastshare;
# #
# Don't touch these 2 - see $rigs below # Don't touch these 2 - see $rigs below
$miner = null; $miner = null;
@ -17,6 +18,13 @@ $readonly = false;
# coz it doesn't have notify - it just shows the error status table # coz it doesn't have notify - it just shows the error status table
$notify = true; $notify = true;
# #
# set $checklastshare to true to do the following checks:
# If a device's last share is 12x expected ago then display as an error
# If a device's last share is 8x expected ago then display as a warning
# If either of the above is true, also display the whole line highlighted
# This assumes shares are 1 difficulty shares
$checklastshare = true;
#
# Set $rigs to an array of your cgminer rigs that are running # Set $rigs to an array of your cgminer rigs that are running
# format: 'IP:Port' or 'Host:Port' # format: 'IP:Port' or 'Host:Port'
# If you only have one rig, it will just show the detail of that rig # If you only have one rig, it will just show the detail of that rig
@ -70,6 +78,7 @@ td.err { color:black; font-family:verdana,arial,sans; font-size:13pt; background
td.warn { color:black; font-family:verdana,arial,sans; font-size:13pt; background:#ffb050 } td.warn { color:black; font-family:verdana,arial,sans; font-size:13pt; background:#ffb050 }
td.sta { color:green; font-family:verdana,arial,sans; font-size:13pt; } td.sta { color:green; font-family:verdana,arial,sans; font-size:13pt; }
td.tot { color:blue; font-family:verdana,arial,sans; font-size:13pt; background:#fff8f2 } td.tot { color:blue; font-family:verdana,arial,sans; font-size:13pt; background:#fff8f2 }
td.lst { color:blue; font-family:verdana,arial,sans; font-size:13pt; background:#ffffdd }
</style> </style>
</head><body bgcolor=#ecffff> </head><body bgcolor=#ecffff>
<script type='text/javascript'> <script type='text/javascript'>
@ -222,140 +231,230 @@ function getparam($name, $both = false)
return substr($a, 0, 1024); return substr($a, 0, 1024);
} }
# #
function fmt($section, $name, $value) function classlastshare($when, $alldata, $warnclass, $errorclass)
{
global $checklastshare;
if ($checklastshare === false)
return '';
if ($when == 0)
return '';
if (!isset($alldata['MHS av']))
return '';
if (!isset($alldata['Last Share Time']))
return '';
$expected = pow(2, 32) / ($alldata['MHS av'] * pow(10, 6));
$howlong = $when - $alldata['Last Share Time'];
if ($howlong < 1)
$howlong = 1;
if ($howlong > ($expected * 12))
return $errorclass;
if ($howlong > ($expected * 8))
return $warnclass;
return '';
}
#
function fmt($section, $name, $value, $when, $alldata)
{ {
global $dfmt; global $dfmt;
if ($alldata == null)
$alldata = array();
$errorclass = ' class=err'; $errorclass = ' class=err';
$warnclass = ' class=warn'; $warnclass = ' class=warn';
$lstclass = ' class=lst';
$b = '&nbsp;'; $b = '&nbsp;';
$ret = $value; $ret = $value;
$class = ''; $class = '';
switch ($section.'.'.$name) if ($value === null)
{ $ret = $b;
case 'GPU.Last Share Time': else
case 'PGA.Last Share Time': switch ($section.'.'.$name)
$ret = date('H:i:s', $value);
break;
case 'SUMMARY.Elapsed':
$s = $value % 60;
$value -= $s;
$value /= 60;
if ($value == 0)
$ret = $s.'s';
else
{ {
$m = $value % 60; case 'GPU.Last Share Time':
$value -= $m; case 'PGA.Last Share Time':
if ($value == 0
|| (isset($alldata['Last Share Pool']) && $alldata['Last Share Pool'] == -1))
{
$ret = 'Never';
$class = $warnclass;
}
else
{
$ret = date('H:i:s', $value);
$class = classlastshare($when, $alldata, $warnclass, $errorclass);
}
break;
case 'POOL.Last Share Time':
if ($value == 0)
$ret = 'Never';
else
$ret = date('H:i:s d-M', $value);
break;
case 'GPU.Last Share Pool':
case 'PGA.Last Share Pool':
if ($value == -1)
{
$ret = 'None';
$class = $warnclass;
}
break;
case 'SUMMARY.Elapsed':
$s = $value % 60;
$value -= $s;
$value /= 60; $value /= 60;
if ($value == 0) if ($value == 0)
$ret = sprintf("%dm$b%02ds", $m, $s); $ret = $s.'s';
else else
{ {
$h = $value % 24; $m = $value % 60;
$value -= $h; $value -= $m;
$value /= 24; $value /= 60;
if ($value == 0) if ($value == 0)
$ret = sprintf("%dh$b%02dm$b%02ds", $h, $m, $s); $ret = sprintf("%dm$b%02ds", $m, $s);
else else
{ {
if ($value == 1) $h = $value % 24;
$days = ''; $value -= $h;
$value /= 24;
if ($value == 0)
$ret = sprintf("%dh$b%02dm$b%02ds", $h, $m, $s);
else else
$days = 's'; {
if ($value == 1)
$days = '';
else
$days = 's';
$ret = sprintf("%dday$days$b%02dh$b%02dm$b%02ds", $value, $h, $m, $s); $ret = sprintf("%dday$days$b%02dh$b%02dm$b%02ds", $value, $h, $m, $s);
}
} }
} }
break;
case 'NOTIFY.Last Well':
if ($value == '0')
{
$ret = 'Never';
$class = $warnclass;
}
else
$ret = date('H:i:s', $value);
break;
case 'NOTIFY.Last Not Well':
if ($value == '0')
$ret = 'Never';
else
{
$ret = date('H:i:s', $value);
$class = $errorclass;
}
break;
case 'NOTIFY.Reason Not Well':
if ($value != 'None')
$class = $errorclass;
break;
case 'GPU.Utility':
case 'PGA.Utility':
case 'SUMMARY.Utility':
$ret = $value.'/m';
if ($value == 0)
$class = $warnclass;
break;
case 'PGA.Temperature':
$ret = $value.'&deg;C';
break;
case 'GPU.Temperature':
$ret = $value.'&deg;C';
case 'GPU.GPU Clock':
case 'GPU.Memory Clock':
case 'GPU.GPU Voltage':
case 'GPU.GPU Activity':
if ($value == 0)
$class = $warnclass;
break;
case 'GPU.Fan Percent':
if ($value == 0)
$class = $warnclass;
else
{
if ($value == 100)
$class = $errorclass;
else
if ($value > 85)
$class = $warnclass;
}
break;
case 'GPU.Fan Speed':
if ($value == 0)
$class = $warnclass;
else
if (isset($alldata['Fan Percent']))
{
$test = $alldata['Fan Percent'];
if ($test == 100)
$class = $errorclass;
else
if ($test > 85)
$class = $warnclass;
}
break;
case 'GPU.MHS av':
case 'PGA.MHS av':
case 'SUMMARY.MHS av':
case 'GPU.Total MH':
case 'PGA.Total MH':
case 'SUMMARY.Total MH':
case 'SUMMARY.Getworks':
case 'GPU.Accepted':
case 'PGA.Accepted':
case 'SUMMARY.Accepted':
case 'GPU.Rejected':
case 'PGA.Rejected':
case 'SUMMARY.Rejected':
case 'SUMMARY.Local Work':
case 'POOL.Getworks':
case 'POOL.Accepted':
case 'POOL.Rejected':
case 'POOL.Discarded':
$parts = explode('.', $value, 2);
if (count($parts) == 1)
$dec = '';
else
$dec = '.'.$parts[1];
$ret = number_format($parts[0]).$dec;
break;
case 'GPU.Status':
case 'PGA.Status':
case 'POOL.Status':
if ($value != 'Alive')
$class = $errorclass;
break;
case 'GPU.Enabled':
case 'PGA.Enabled':
if ($value != 'Y')
$class = $warnclass;
break;
case 'STATUS.When':
$ret = date($dfmt, $value);
break;
} }
break;
case 'NOTIFY.Last Well':
if ($value == '0')
{
$ret = 'Never';
$class = $warnclass;
}
else
$ret = date('H:i:s', $value);
break;
case 'NOTIFY.Last Not Well':
if ($value == '0')
$ret = 'Never';
else
{
$ret = date('H:i:s', $value);
$class = $errorclass;
}
break;
case 'NOTIFY.Reason Not Well':
if ($value != 'None')
$class = $errorclass;
break;
case 'GPU.Utility':
case 'PGA.Utility':
case 'SUMMARY.Utility':
$ret = $value.'/m';
break;
case 'PGA.Temperature':
$ret = $value.'&deg;C';
break;
case 'GPU.Temperature':
$ret = $value.'&deg;C';
case 'GPU.Fan Speed':
case 'GPU.Fan Percent':
case 'GPU.GPU Clock':
case 'GPU.Memory Clock':
case 'GPU.GPU Voltage':
case 'GPU.GPU Activity':
if ($value == 0)
$class = $warnclass;
break;
case 'GPU.MHS av':
case 'PGA.MHS av':
case 'SUMMARY.MHS av':
case 'GPU.Total MH':
case 'PGA.Total MH':
case 'SUMMARY.Total MH':
case 'SUMMARY.Getworks':
case 'GPU.Accepted':
case 'PGA.Accepted':
case 'SUMMARY.Accepted':
case 'GPU.Rejected':
case 'PGA.Rejected':
case 'SUMMARY.Rejected':
case 'SUMMARY.Local Work':
case 'POOL.Getworks':
case 'POOL.Accepted':
case 'POOL.Rejected':
case 'POOL.Discarded':
$parts = explode('.', $value, 2);
if (count($parts) == 1)
$dec = '';
else
$dec = '.'.$parts[1];
$ret = number_format($parts[0]).$dec;
break;
case 'GPU.Status':
case 'PGA.Status':
case 'POOL.Status':
if ($value != 'Alive')
$class = $errorclass;
break;
case 'GPU.Enabled':
case 'PGA.Enabled':
if ($value != 'Y')
$class = $warnclass;
break;
case 'STATUS.When':
$ret = date($dfmt, $value);
break;
}
if ($section == 'NOTIFY' && substr($name, 0, 1) == '*' && $value != '0') if ($section == 'NOTIFY' && substr($name, 0, 1) == '*' && $value != '0')
$class = $errorclass; $class = $errorclass;
if ($class == '' && $section != 'POOL')
$class = classlastshare($when, $alldata, $lstclass, $lstclass);
return array($ret, $class); return array($ret, $class);
} }
# #
@ -390,6 +489,8 @@ function details($cmd, $list, $rig)
global $poolcmd, $readonly; global $poolcmd, $readonly;
global $showndate; global $showndate;
$when = 0;
$stas = array('S' => 'Success', 'W' => 'Warning', 'I' => 'Informational', 'E' => 'Error', 'F' => 'Fatal'); $stas = array('S' => 'Success', 'W' => 'Warning', 'I' => 'Informational', 'E' => 'Error', 'F' => 'Fatal');
echo $tablebegin; echo $tablebegin;
@ -408,7 +509,10 @@ function details($cmd, $list, $rig)
echo '<tr>'; echo '<tr>';
echo '<td>Computer: '.$list['STATUS']['Description'].'</td>'; echo '<td>Computer: '.$list['STATUS']['Description'].'</td>';
if (isset($list['STATUS']['When'])) if (isset($list['STATUS']['When']))
{
echo '<td>When: '.date($dfmt, $list['STATUS']['When']).'</td>'; echo '<td>When: '.date($dfmt, $list['STATUS']['When']).'</td>';
$when = $list['STATUS']['When'];
}
$sta = $list['STATUS']['STATUS']; $sta = $list['STATUS']['STATUS'];
echo '<td>Status: '.$stas[$sta].'</td>'; echo '<td>Status: '.$stas[$sta].'</td>';
echo '<td>Message: '.$list['STATUS']['Msg'].'</td>'; echo '<td>Message: '.$list['STATUS']['Msg'].'</td>';
@ -436,7 +540,7 @@ function details($cmd, $list, $rig)
foreach ($values as $name => $value) foreach ($values as $name => $value)
{ {
list($showvalue, $class) = fmt($section, $name, $value); list($showvalue, $class) = fmt($section, $name, $value, $when, $values);
echo "<td$class>$showvalue</td>"; echo "<td$class>$showvalue</td>";
} }
@ -587,6 +691,8 @@ function doforeach($cmd, $des, $sum, $head, $datetime)
global $tablebegin, $tableend, $warnfont, $warnoff, $dfmt; global $tablebegin, $tableend, $warnfont, $warnoff, $dfmt;
global $rigerror; global $rigerror;
$when = 0;
$header = $head; $header = $head;
$anss = array(); $anss = array();
@ -652,7 +758,7 @@ function doforeach($cmd, $des, $sum, $head, $datetime)
else else
{ {
if (isset($row[$name])) if (isset($row[$name]))
list($showvalue, $class) = fmt('STATUS', $name, $row[$name]); list($showvalue, $class) = fmt('STATUS', $name, $row[$name], $when, null);
else else
{ {
$class = ''; $class = '';
@ -710,6 +816,10 @@ function doforeach($cmd, $des, $sum, $head, $datetime)
foreach ($anss as $rig => $ans) foreach ($anss as $rig => $ans)
{ {
$when = 0;
if (isset($ans['STATUS']['When']))
$when = $ans['STATUS']['When'];
foreach ($ans as $item => $row) foreach ($ans as $item => $row)
{ {
if ($item == 'STATUS') if ($item == 'STATUS')
@ -733,12 +843,11 @@ function doforeach($cmd, $des, $sum, $head, $datetime)
else else
{ {
if (isset($row[$name])) if (isset($row[$name]))
list($showvalue, $class) = fmt($section, $name, $row[$name]); $value = $row[$name];
else else
{ $value = null;
$class = '';
$showvalue = '&nbsp;'; list($showvalue, $class) = fmt($section, $name, $value, $when, $row);
}
if ($rig === 'total' and $class == '') if ($rig === 'total' and $class == '')
$class = ' class=tot'; $class = ' class=tot';

Loading…
Cancel
Save