Squashed import of Dbus-Sensors
commit 722ae749dbc721873dc0e2448bf47e6273d1ec52
Author: James Feist <james.feist@linux.intel.com>
Date: Fri Aug 31 12:05:21 2018 -0700
Update clang-format
Update to new openbmc standard.
Change-Id: I421866b4faf738409f6d603e8a3606009523202d
Signed-off-by: James Feist <james.feist@linux.intel.com>
commit 6d0c7fe1b43c55ffa4a76d41c97ddeaf0be2c438
Author: James Feist <james.feist@linux.intel.com>
Date: Mon Aug 27 17:37:08 2018 -0700
Make Hunter off by default
This accidently got deleted during upgrade.
Change-Id: I58fb4d9caae13bface7d4bcb67f5256cc71a87ec
Signed-off-by: James Feist <james.feist@linux.intel.com>
commit 1ceff4a56cdd4cb1733e4bce0c56c5987d7770a0
Author: James Feist <james.feist@linux.intel.com>
Date: Thu Aug 9 14:48:46 2018 -0700
Stop sensors from segfaulting
catch sdbusplus errors
Change-Id: Ia96b37d375af152a76e59493ff11dfc38d50dc87
Signed-off-by: James Feist <james.feist@linux.intel.com>
commit 1a72218d51dcb5b5a9c210b15394ddb713699545
Author: James Feist <james.feist@linux.intel.com>
Date: Tue Aug 7 15:40:41 2018 -0700
Make out of tree builds work again
This now builds sdbusplus as it must be linked against.
Also cmake-format format the file.
Change-Id: I3e869b7ace0254ac153c723d2e3c87e659e4d5d6
Signed-off-by: James Feist <james.feist@linux.intel.com>
commit 5446b4551064f46d28aaf43a74f3f632da642766
Author: James Feist <james.feist@linux.intel.com>
Date: Mon Aug 6 14:41:54 2018 -0700
Change all properties to Upper Camel Case
Change to match entity manager case fix.
Tested-By: Verfied sensor list output same
Change-Id: Ic1e41e31b28c61d3ef45d0937edbbaf55cfa06af
Signed-off-by: James Feist <james.feist@linux.intel.com>
commit b02f18ff15e64a71a5b28e64f9394e3daee7e071
Author: James Feist <james.feist@linux.intel.com>
Date: Wed Jul 11 10:44:51 2018 -0700
Make isPowerOn async
isPowerOn was killing the response time of fan sensor
when power was off. Make it a match.
Change-Id: If1396cb595ec85979940cef570b87cfd576db555
Signed-off-by: James Feist <james.feist@linux.intel.com>
commit f734aea02ed571910f571a6c56bbfceab4fc49f2
Author: James Feist <james.feist@linux.intel.com>
Date: Thu Jul 19 16:03:41 2018 -0700
Change reading name from entity-manager to Name
Change-Id: Ia0a72dcf04bafd0208a17260187d9ed961c70da8
Signed-off-by: James Feist <james.feist@linux.intel.com>
commit c6fc5b668d222471e6755faa6dc42e1a3822e496
Author: James Feist <james.feist@linux.intel.com>
Date: Fri Jul 6 14:41:54 2018 -0700
Add PWM Sensor
Add pwm sensor for fan control to use.
Change-Id: Ibb237415b245d31a70c69748ee4be6367f5eb219
Signed-off-by: James Feist <james.feist@linux.intel.com>
commit 3fa34f5ae31738ffbfa26a3e25333d756bd323ab
Author: James Feist <james.feist@linux.intel.com>
Date: Fri Jul 6 12:09:40 2018 -0700
Move Sensors path to sensors
Upstream uses lowercase paths, fix it.
Change-Id: I16e3c8ec6d6457ec10376a65bea05a8767f77591
Signed-off-by: James Feist <james.feist@linux.intel.com>
commit 9123255ab3e3047d25df4799bdd5589cdafef70c
Merge: 5338775 be5c41e
Author: Feist, James <james.feist@intel.com>
Date: Mon Jul 2 11:07:26 2018 -0700
Merge "Add setable thresholds to hwmontempsensor"
commit be5c41e37bc5bf8d2e365c5c0a9073cb3da723e2
Author: James Feist <james.feist@linux.intel.com>
Date: Wed Jun 6 11:49:08 2018 -0700
Add setable thresholds to hwmontempsensor
This allows the hwmontempsensor sensor to set and persist
its threshold values via dbus. It uses the properites set
to call the entity manager and update the threshold.
Change-Id: Ida526055bd3d7e9ae709ab22c01f0b9d74dd95d8
Signed-off-by: James Feist <james.feist@linux.intel.com>
commit 5338775ff3a9c17ee47863c826ecf517c52d3466
Author: James Feist <james.feist@linux.intel.com>
Date: Wed Jun 13 14:41:37 2018 -0700
Add setable thresholds to CPU sensors
If thresholds are defined for CPU sensors then allow
them to be set. Don't allow them to be set if they are
pulled from the CPU. Currently tested using DIMMs.
Change-Id: Ic2a957ba0fdf70332ecfb2dfee6ff2bd483dd934
Signed-off-by: James Feist <james.feist@linux.intel.com>
commit 5a3834e50493173b4692a20c417d9d0e6fcbe833
Author: James Feist <james.feist@linux.intel.com>
Date: Wed Jun 13 11:18:58 2018 -0700
Add setable thresholds to tachsensor
This allows the tachsensor sensor to set and persist
its threshold values via dbus. It uses the properites set
to call the entity manager and update the threshold.
Change-Id: I3e3ee8811868136b605e99e6fc24f0764f88d12c
Signed-off-by: James Feist <james.feist@linux.intel.com>
commit d0196639c21b2cca23ffddac9ea37cfd3ae0270f
Author: James Feist <james.feist@linux.intel.com>
Date: Wed Jun 6 11:49:08 2018 -0700
Add setable thresholds to adc sensor
This allows the adc sensor to set and persist its
threshold values via dbus. It uses the properites set
to call the entity manager and update the threshold.
Change-Id: I963eec31a05e2d15985ab339dbeefa07dcfb6b9f
Signed-off-by: James Feist <james.feist@linux.intel.com>
commit ad2e90db555521e1bba4d3350958f0d6dd35132f
Merge: aa3fa4a 52e515e
Author: Feist, James <james.feist@intel.com>
Date: Tue Jun 12 09:45:32 2018 -0700
Merge "Fix bad default scale factor for voltage sensors"
commit 52e515e553f72bac3f35379723c6e29ecf2f5fdb
Author: James Feist <james.feist@linux.intel.com>
Date: Mon Jun 11 16:05:38 2018 -0700
Fix bad default scale factor for voltage sensors
Default should be 1.0, not 0 which does a divide by 0.
Change-Id: Icb658990d22b14986ef5fe20571dc6dcb73181a8
Signed-off-by: James Feist <james.feist@linux.intel.com>
commit aa3fa4a950d3934d80d7f30e43c1f388b3c35587
Author: James Feist <james.feist@linux.intel.com>
Date: Fri Jun 8 09:57:19 2018 -0700
Get oemname file from connector
The connector is a much more reliable source of information,
this gets rid of needing to do the awkward divide by 2
that we use for 1u configurations.
Change-Id: I6264cecc357e98b8601b9b33c27a526288fc7954
Signed-off-by: James Feist <james.feist@linux.intel.com>
commit 4b9ed8f2119cda51bd99c63115037c89d6550746
Author: James Feist <james.feist@linux.intel.com>
Date: Fri Jun 1 13:59:55 2018 -0700
Get managed objects at root and remove boost-dbus
This is to keep in sync with entity manager change.
Also remove all traces of boost-dbus as all sensors
have been updated.
Change-Id: Id53f77ad055cac86fa8dc3f18566ec5f3d189d1a
Signed-off-by: James Feist <james.feist@linux.intel.com>
commit 160f39a4d92ffe26706ad3ff8481f2e762024bb9
Merge: b378761 5fe3c8b
Author: Mauery, Vernon <vernon.mauery@intel.com>
Date: Tue Jun 5 09:30:19 2018 -0700
Merge "Update cpusensor to support PECI v5"
commit b37876130d1e0388f8ade8b940ae2a9764c530e6
Author: James Feist <james.feist@linux.intel.com>
Date: Mon Jun 4 11:16:54 2018 -0700
Link against sdbusplus
Can no longer use sdbusplus header only after exception
changes.
Change-Id: I347ae22a09f1c6a64d5ee307ccff648288971b26
Signed-off-by: James Feist <james.feist@linux.intel.com>
commit 5fe3c8b25010ce9c9770dc5fe112fb5a65d608ca
Author: Yoo, Jae Hyun <jae.hyun.yoo@intel.com>
Date: Thu May 31 10:53:19 2018 -0700
Update cpusensor to support PECI v5
This commit updates cpusensor creation logic to support MFD type
PECI hwmon modules in PECI v5.
Change-Id: I0a76f66aaa1182226c28b95b65626c36de9b5b96
Signed-off-by: Yoo, Jae Hyun <jae.hyun.yoo@intel.com>
commit 59bafd8c4c49048144013f1ba959910bdde84c4f
Merge: d50f94b ced4067
Author: Tanous, Ed <ed.tanous@intel.com>
Date: Wed May 30 16:37:33 2018 -0700
Merge "Update hwmontempsensor"
commit d50f94bd7c6141e4010c75b2df9d9ff0ad2389eb
Author: James Feist <james.feist@linux.intel.com>
Date: Wed May 23 16:14:30 2018 -0700
Update CPUSensor
Update CPU sensor to sdbusplus and make it not exit when
configuration is not yet avaialble. Also modify configuration
from vector to set to allow overwriting of configuration.
Change-Id: Iede83df362ab3e18f4ebd3bc9a3ce24c573bc36a
Signed-off-by: James Feist <james.feist@linux.intel.com>
commit ced40673a0badc11ebd16dbf6aeeb7208236c92c
Author: James Feist <james.feist@linux.intel.com>
Date: Wed May 23 14:31:35 2018 -0700
Update hwmontempsensor
Update to sdbusplus and to only reestablish sensors that
have had changes.
Change-Id: I09b2f4ae571885c3e5cf6d7439c6796dbdd09339
Signed-off-by: James Feist <james.feist@linux.intel.com>
commit 252b29e8f9f13ff1137830366b3a9d7ae1d2423b
Author: James Feist <james.feist@linux.intel.com>
Date: Wed May 23 10:56:53 2018 -0700
Update Tach Sensor
Update to sdbusplus and to only reestablish sensors that
have had changes.
Change-Id: I803894ca05b8bfb13d6be2e226b9e5fef82b864e
Signed-off-by: James Feist <james.feist@linux.intel.com>
commit 4e99a9c3a0932349e5426ae472c5e500a0cd6752
Author: James Feist <james.feist@linux.intel.com>
Date: Tue May 22 16:06:31 2018 -0700
Update ADC sensor
Move ADC sensor over to sdbusplus and allow it to do a
correct hot-reload to load new thresholds from entity
manager. Types that use sdbusplus variants end with S
for now so that one sensor can be moved over at a time.
Change-Id: Id378ce2a64cf4a4f54c69688eafe25a0a847352b
Signed-off-by: James Feist <james.feist@linux.intel.com>
commit 0e11699e8b3605bf89c3dde599756add4203d328
Author: James Feist <james.feist@linux.intel.com>
Date: Mon May 21 14:30:29 2018 -0700
Format CMakeLists.txt
Format CMakeLists.txt using cmake_format
Change-Id: Icf40c96107c876ad9d962feca910715a97fccb71
Signed-off-by: James Feist <james.feist@linux.intel.com>
commit 0d2147adee492d579fbe40e963a4337561fd96e2
Author: Kuiying Wang <kuiying.wang@intel.com>
Date: Wed May 9 14:55:23 2018 +0800
Modify dbus interface for power control.
Switch power control from org to xyz
Change-Id: I794127238bf4c7fb8a7d86e6c11ab5c4c15eb7a8
Signed-off-by: Kuiying Wang <kuiying.wang@intel.com>
commit f465c93e7fdf4e59dbf52855bf7929a9794c9b1e
Author: James Feist <james.feist@linux.intel.com>
Date: Tue Apr 10 13:56:19 2018 -0700
Apply upstream clang-format
Redo clang-format on this repo.
Change-Id: I3df0b6822cd31f94a089c1ba7f3d54d19d4ac5ae
Signed-off-by: James Feist <james.feist@linux.intel.com>
commit bd866ae07dd7d3880a69caf34df7650c1a2c6c73
Author: James Feist <james.feist@linux.intel.com>
Date: Tue Apr 10 12:34:50 2018 -0700
Prep sensors for interface change
Entity manager interfaces are changing to match upstream.
This change makes sure that the sensors will still work
afterwards.
Change-Id: Ibd00884a77446b6f7fb3bb0bc43afe1dd4068783
Signed-off-by: James Feist <james.feist@linux.intel.com>
commit 2c0d91f7818952c8a80f0be62275a8fc57e520d4
Author: Yoo, Jae Hyun <jae.hyun.yoo@intel.com>
Date: Mon Apr 9 10:16:20 2018 -0700
Add DIMM presence checking logic into cpusensor service
This patch adds DIMM presence checking logic into cpusensor service
to support memory test behavior of the host BIOS while the host is
booting up.
Also, adjusted temperature scanning interval of cpusensor from 0.5s
to 1s.
Change-Id: I5778f6c6ed139f4babf604636c6c15ab2ed24065
Signed-off-by: Yoo, Jae Hyun <jae.hyun.yoo@intel.com>
commit 9cdb006765a1f649113497521508af5f51cebf34
Author: Yoo, Jae Hyun <jae.hyun.yoo@intel.com>
Date: Fri Apr 6 16:26:04 2018 -0700
Fix a bug on threshold direction setting logic.
This patch fixes a bug of threshold direction assignment code.
Change-Id: Ifb159729a60de68cbba37c8848d3ca283fdd8ebe
Signed-off-by: Yoo, Jae Hyun <jae.hyun.yoo@intel.com>
commit 6a75f915db5b7e4444af6642d3f6bba2324ad870
Author: Yoo, Jae Hyun <jae.hyun.yoo@intel.com>
Date: Fri Apr 6 14:28:42 2018 -0700
Fix the argument passing way of dbus::connection
This commit fixes the argument passing way of dbus::connection in
dbus-sensors package from by value to by reference to prevent
this error:
process 2657: arguments to dbus_connection_send_with_reply() were incorrect, assertion "connection != NULL" failed in file ../../dbus-1.10.20/dbus/dbus-connection.c line 3410.
This is normally a bug in some application using the D-Bus library.
D-Bus not built with -rdynamic so unable to print a backtrace
This log rarely happens in asynchronous calling a function.
Also, dbus package is getting the shared pointer of connection by
reference according to its definition so this fix would be good.
include/dbus/properties.hpp:28: DbusMethod(const std::string& name, std::shared_ptr<dbus::connection>& conn)
include/dbus/properties.hpp:94: std::shared_ptr<dbus::connection>& conn, Handler h)
include/dbus/properties.hpp:106: std::shared_ptr<dbus::connection>& conn, Handler h)
include/dbus/properties.hpp:157: std::shared_ptr<dbus::connection>& conn)
include/dbus/properties.hpp:185: std::shared_ptr<dbus::connection>& conn)
include/dbus/properties.hpp:422: DbusObjectServer(std::shared_ptr<dbus::connection>& conn) : conn(conn) {
include/dbus/properties.hpp:476: std::shared_ptr<dbus::connection>& get_connection() { return conn; }
Change-Id: I0695f99004ff4110ff06033f79da7d903814fbd1
Signed-off-by: Yoo, Jae Hyun <jae.hyun.yoo@intel.com>
commit 35cd4cb7c37c6a154935caa80e16f6958397fd18
Author: Yoo, Jae Hyun <jae.hyun.yoo@intel.com>
Date: Fri Apr 6 14:22:00 2018 -0700
Update non-yocto build header in dbus-sensor package
This commit update a non-yocto build header in dbus-sensor package
to make it sync with the one in kernel.
Change-Id: I203e924f791e5f85e1f5e0373bc155200e386721
Signed-off-by: Yoo, Jae Hyun <jae.hyun.yoo@intel.com>
commit 5394d4254b545dd936b8fa2e0f832d69960b157a
Author: Yoo, Jae Hyun <jae.hyun.yoo@intel.com>
Date: Wed Apr 4 09:29:18 2018 -0700
Add CPU presence detection logic into cpusensor
This commit adds CPU presence detection logic into cpusensor
service to support dynamic device tree overlay for PECI hwmon
drivers.
Change-Id: I46781b603ad64014f2e706a193cfe243d92925d9
Signed-off-by: Yoo, Jae Hyun <jae.hyun.yoo@intel.com>
commit 78bf357cc5035e61acb23a3cf3ae2aa18248c566
Author: Yoo, Jae Hyun <jae.hyun.yoo@intel.com>
Date: Wed Apr 4 09:42:39 2018 -0700
Remove a deprecated function
This commit removes a deprecated function,
ParseThresholdsFromJson() from Threshold module.
Change-Id: I208e0666764f1fbac33abb2b3286aeae9128e0ba
Signed-off-by: Yoo, Jae Hyun <jae.hyun.yoo@intel.com>
commit f159d0738a3b35e1ce4f7c8c8c241bb75b9ec561
Merge: a99aaa1 3463069
Author: Feist, James <james.feist@intel.com>
Date: Mon Apr 2 10:23:32 2018 -0700
Merge "Move fansensor over to getting data from dbus"
commit a99aaa1a69eebe73b8509bde3e54d842dca9b347
Merge: 45db383 c16633b
Author: Feist, James <james.feist@intel.com>
Date: Mon Apr 2 10:23:27 2018 -0700
Merge "Move adc sensor to get information from dbus"
commit 45db383b6bddc23e9b26e8cab4b4ff3c70553d21
Author: Yoo, Jae Hyun <jae.hyun.yoo@intel.com>
Date: Fri Mar 30 15:31:39 2018 -0700
Move cpusensor over to getting data from dbus
Also add a label argument handling code into the
ParseThresholdsFromConfig().
Change-Id: I74c7446237d867cc538b2f05632f829990b98e6f
Signed-off-by: Yoo, Jae Hyun <jae.hyun.yoo@intel.com>
commit 346306959419f4fb745435559530027be30bfafd
Author: James Feist <james.feist@linux.intel.com>
Date: Fri Mar 30 13:30:08 2018 -0700
Move fansensor over to getting data from dbus
Also a few minor error checking things.
Change-Id: Ib49bdbe62a5291246faa883568e6adbe4fd72f1f
Signed-off-by: James Feist <james.feist@linux.intel.com>
commit c16633b289865729ff8058fbe1e748c64fc301e4
Author: James Feist <james.feist@linux.intel.com>
Date: Thu Mar 29 13:45:08 2018 -0700
Move adc sensor to get information from dbus
Also add a deadline timer to limit the work done scanning
for new sensors on boot.
Change-Id: Id20bde8899747ecfabc4015a4a944526504726e3
Signed-off-by: James Feist <james.feist@linux.intel.com>
commit d3b0650cf021a56877e08657d653d556d06b1063
Author: Yoo, Jae Hyun <jae.hyun.yoo@intel.com>
Date: Fri Mar 30 10:55:18 2018 -0700
Add a deadline timer into CPUsensor to limit the work done scanning.
This patch adds a deadline timer to limit the work done scanning
for new sensors on boot.
Also fixes local variable names based on the camel case naming rule.
Change-Id: I6a71b10db0db9d5a7f7e4b722a7394cef951702b
Signed-off-by: Yoo, Jae Hyun <jae.hyun.yoo@intel.com>
commit 186238f3c7c91ba5e05dd949493182dd646472c2
Author: James Feist <james.feist@linux.intel.com>
Date: Tue Mar 27 16:30:53 2018 -0700
Move hwmontempsensor to get data from Entity Manager
Stop reading json directly, other sensors will be moved
along incrementally. Also move sensor value to the value
interface, as it was noticed we were not matching upstream.
Change-Id: I3c2e72d4acbb8472707924c01fe871cb5e152ec2
Signed-off-by: James Feist <james.feist@linux.intel.com>
commit 24d6f231218c5ade5c4480e33c461e0212c2dac5
Author: Yoo, Jae Hyun <jae.hyun.yoo@intel.com>
Date: Fri Mar 16 14:56:45 2018 -0700
Block sensor reading of CPU sensors when host is powered down
This commit adds a change to block sensor reading of CPU sensors
when host is powered down.
Change-Id: Ie7a915ef811b2126e40a4c750a700041c7790519
Signed-off-by: Yoo, Jae Hyun <jae.hyun.yoo@intel.com>
commit 19b0f57f469a15b413cbafa8f442a009b7ede8ac
Author: James Feist <james.feist@linux.intel.com>
Date: Thu Mar 22 16:39:35 2018 -0700
Allow multiple fan types
The pmbus driver adds fans for PSU, because of that
fan 1 was actually being read as the pmbus fan instead
of the baseboard fan. Check oemname before adding to list.
Change-Id: I1a93aa6fe1b22a52bc2490afb4005a59f1db1785
Signed-off-by: James Feist <james.feist@linux.intel.com>
commit f070207741e719115344c50aed52081b1894e2ca
Author: Yoo, Jae Hyun <jae.hyun.yoo@intel.com>
Date: Fri Mar 16 16:52:32 2018 -0700
Modify threshold parsing logic to support CPUsensor.
This commit modifies ParseThresholdsFromJson() code to support
CPUsensor.
Change-Id: Idfa4641656d5ad8faeec32a1b897c289fe754415
Signed-off-by: Yoo, Jae Hyun <jae.hyun.yoo@intel.com>
commit 10db5ae5467c47ec026fe62603cb144bc8b98e82
Author: Yoo, Jae Hyun <jae.hyun.yoo@intel.com>
Date: Mon Mar 19 17:10:39 2018 -0700
Fix regex string of CPU sensor to improve search speed.
This commit fixes the regex string of CPU sensor to make it doesn't
search unnecessary files such as 'oemname11' or 'oemname12'. It
will search only exactly matched 'oemname1' files with this change.
Change-Id: I1d862ba5213c685dd6577031bdf9536f888c6666
Signed-off-by: Yoo, Jae Hyun <jae.hyun.yoo@intel.com>
commit b9abf5ef64b18c672508c9b3d5a85b71f8cf3827
Author: James Feist <james.feist@linux.intel.com>
Date: Thu Mar 15 16:04:27 2018 -0700
Add fan check for no sensors
This sensor was missing this check
Change-Id: Ic188d682c612e51826e7a8ea62a4c2298fa15c2a
Signed-off-by: James Feist <james.feist@linux.intel.com>
commit 9d1d47080a174b73385fd6c4e7068994d2f8b46d
Author: Yoo, Jae Hyun <jae.hyun.yoo@intel.com>
Date: Tue Mar 13 19:24:19 2018 -0700
Add CPU sensors
This commit adds dbus service for CPU sensors to replace python
scripts in sensor emitter. It uses hwmon sysfs label based dbus
sensor registration.
Change-Id: I49991f99e0e6cdd3f5f288b25ec0720b89c646ae
Signed-off-by: Jae Hyun Yoo <jae.hyun.yoo@intel.com>
commit 6ac0dc7c679aac33c4eaa76fd9ec75bb51a1302c
Author: James Feist <james.feist@linux.intel.com>
Date: Fri Mar 2 14:15:24 2018 -0800
Support HwmonTempSensor adding sensors on the fly
This is to get us to the demo. In the future all data
will be read from dbus, but this rescan works for today.
Change-Id: I196416583cb94517f8e0c79f63f13f66586a0017
Signed-off-by: James Feist <james.feist@linux.intel.com>
commit 783f3c603922f010af3321b1be5428fcc75d9dfa
Author: James Feist <james.feist@linux.intel.com>
Date: Wed Feb 28 14:59:15 2018 -0800
Increment Fan Paths
Found a bug where the fan sensor was only reading sensor 1.
Change-Id: Ie53191d489f7fc2a5e410ec842aba41202354b5f
Signed-off-by: James Feist <james.feist@linux.intel.com>
commit aa276f7eee8941f62bbf2f3d4d06394f854cee4b
Author: James Feist <james.feist@linux.intel.com>
Date: Fri Feb 2 11:25:51 2018 -0800
Fix thresholds after new threshold json format
Quick fix to read 1d-array thresholds. Also fix fans
as we are moving to individual fan objects instead of
lumping fanA and fanB together for dual-rotar fans.
Change-Id: I56c31d4f595bee65253c1021f6fb08f1f53ea8d5
Signed-off-by: James Feist <james.feist@linux.intel.com>
commit 97b83293a7e6bc6728b024be6d1e195da66dca4c
Author: James Feist <james.feist@linux.intel.com>
Date: Fri Dec 15 14:14:36 2017 -0800
Change fan sensors to 0 based.
JSON is being changed to 0 based for ease of understanding.
Change-Id: Ic28d6be2b5da8fea0e93436e9b2c33a9dfb8b113
Signed-off-by: James Feist <james.feist@linux.intel.com>
commit de59f4c898d85d5ba3e89cac7d805e2377abb36e
Author: James Feist <james.feist@linux.intel.com>
Date: Fri Dec 8 11:18:51 2017 -0800
Change json store pointer
This was renamed to avoid some confusion.
Change-Id: I151e06d4063605a6d95f822851d002531d6954d0
commit 637ebb9bb2e361cac023dbbd347ff77e45607735
Author: James Feist <james.feist@linux.intel.com>
Date: Tue Nov 21 16:20:05 2017 -0800
Add power state monitoring to fans.
Don't throw errors when reading with power state off.
Change-Id: I98f71b1f2141fa4db56ad99f647f554fd8f427e6
Signed-off-by: James Feist <james.feist@linux.intel.com>
commit 921056009d6324cbbdaf1f612551791556fa21a7
Author: James Feist <james.feist@linux.intel.com>
Date: Tue Nov 21 11:58:45 2017 -0800
Capitialize threshold
Interface needs to be capitialized.
Change-Id: I7e081fc008dd7191c47fa0087fd886207c783853
Signed-off-by: James Feist <james.feist@linux.intel.com>
commit 893bb223173944809cb39c643521a7f4041ad412
Author: Feist, James <james.feist@linux.intel.com>
Date: Tue Nov 14 16:34:41 2017 -0800
Add ADC sensor and TMP421 internal sensors
Also update boost-dbus sha. Add scale factor to read
correct voltages.
Also const some things.
Change-Id: I0840069874bfe01d0d8d01dc39c9a9369ed6689f
Signed-off-by: Feist, James <james.feist@linux.intel.com>
commit 4608e48777dcdb21396f1022a03a8342b67e6cff
Author: Feist, James <james.feist@linux.intel.com>
Date: Fri Nov 10 13:42:41 2017 -0800
Add well known names to sensors
Change-Id: I8aafe79a8f008a2825b2dd87516dba23782e8953
Signed-off-by: Feist, James <james.feist@linux.intel.com>
commit 89f80e4a0e22cdb272063d38533dda603881d66d
Author: Feist, James <james.feist@linux.intel.com>
Date: Tue Oct 24 14:18:39 2017 -0700
Add Temp Sensor and Cleanup threshold logic
Threshold logic is now pulled out in 'main' files of sensors,
so that sensor objects don't have to have the misfortune of dealing
with json. Completely sperate temp sensor from fan sensor. Add
some tests.
Change-Id: I030a60d75b123ff58a1bfdfe8d9c2a4b4ffb851a
Signed-off-by: Feist, James <james.feist@linux.intel.com>
commit d15dcce50bff52b14af6466f3f827a9c84bf947e
Author: Feist, James <james.feist@linux.intel.com>
Date: Tue Sep 19 16:39:07 2017 -0700
Initial commit
"working" temp sensor (thats really a hacked
fan sensor).
Change-Id: Icab3fe25d3b52bdc99608169aad460a82bc045ad
commit a46ae1cef8f48edd38bbbf9ffd6e8a2487b2c4f7
Author: sys_tfad <ec_sw_sl_git@intel.com>
Date: Tue Oct 17 00:16:20 2017 -0700
Initial empty repository
Change-Id: Id153b36830b8a802bf2ced2e525329eb49e16d64
Signed-off-by: James Feist <james.feist@linux.intel.com>
diff --git a/sensors/.clang-format b/sensors/.clang-format
new file mode 100644
index 0000000..dd27708
--- /dev/null
+++ b/sensors/.clang-format
@@ -0,0 +1,98 @@
+---
+Language: Cpp
+# BasedOnStyle: LLVM
+AccessModifierOffset: -2
+AlignAfterOpenBracket: Align
+AlignConsecutiveAssignments: false
+AlignConsecutiveDeclarations: false
+AlignEscapedNewlinesLeft: false
+AlignOperands: true
+AlignTrailingComments: true
+AllowAllParametersOfDeclarationOnNextLine: true
+AllowShortBlocksOnASingleLine: false
+AllowShortCaseLabelsOnASingleLine: false
+AllowShortFunctionsOnASingleLine: None
+AllowShortIfStatementsOnASingleLine: false
+AllowShortLoopsOnASingleLine: false
+AlwaysBreakAfterDefinitionReturnType: None
+AlwaysBreakAfterReturnType: None
+AlwaysBreakBeforeMultilineStrings: false
+AlwaysBreakTemplateDeclarations: false
+BinPackArguments: true
+BinPackParameters: true
+BraceWrapping:
+ AfterClass: true
+ AfterControlStatement: true
+ AfterEnum: true
+ AfterFunction: true
+ AfterNamespace: true
+ AfterObjCDeclaration: true
+ AfterStruct: true
+ AfterUnion: true
+ BeforeCatch: true
+ BeforeElse: true
+ IndentBraces: false
+BreakBeforeBinaryOperators: None
+BreakBeforeBraces: Custom
+BreakBeforeTernaryOperators: true
+BreakConstructorInitializers: AfterColon
+ColumnLimit: 80
+CommentPragmas: '^ IWYU pragma:'
+ConstructorInitializerAllOnOneLineOrOnePerLine: false
+ConstructorInitializerIndentWidth: 4
+ContinuationIndentWidth: 4
+Cpp11BracedListStyle: true
+DerivePointerAlignment: true
+PointerAlignment: Left
+DisableFormat: false
+ExperimentalAutoDetectBinPacking: false
+FixNamespaceComments: true
+ForEachMacros: [ foreach, Q_FOREACH, BOOST_FOREACH ]
+IncludeBlocks: Regroup
+IncludeCategories:
+ - Regex: '^[<"](gtest|gmock)'
+ Priority: 5
+ - Regex: '^"config.h"'
+ Priority: -1
+ - Regex: '^".*\.hpp"'
+ Priority: 1
+ - Regex: '^<.*\.h>'
+ Priority: 2
+ - Regex: '^<.*'
+ Priority: 3
+ - Regex: '.*'
+ Priority: 4
+IndentCaseLabels: true
+IndentWidth: 4
+IndentWrappedFunctionNames: true
+KeepEmptyLinesAtTheStartOfBlocks: true
+MacroBlockBegin: ''
+MacroBlockEnd: ''
+MaxEmptyLinesToKeep: 1
+NamespaceIndentation: None
+ObjCBlockIndentWidth: 2
+ObjCSpaceAfterProperty: false
+ObjCSpaceBeforeProtocolList: true
+PenaltyBreakBeforeFirstCallParameter: 19
+PenaltyBreakComment: 300
+PenaltyBreakFirstLessLess: 120
+PenaltyBreakString: 1000
+PenaltyExcessCharacter: 1000000
+PenaltyReturnTypeOnItsOwnLine: 60
+PointerAlignment: Right
+ReflowComments: true
+SortIncludes: true
+SpaceAfterCStyleCast: false
+SpaceBeforeAssignmentOperators: true
+SpaceBeforeParens: ControlStatements
+SpaceInEmptyParentheses: false
+SpacesBeforeTrailingComments: 1
+SpacesInAngles: false
+SpacesInContainerLiterals: true
+SpacesInCStyleCastParentheses: false
+SpacesInParentheses: false
+SpacesInSquareBrackets: false
+Standard: Cpp11
+TabWidth: 4
+UseTab: Never
+...
diff --git a/sensors/.gitignore b/sensors/.gitignore
new file mode 100644
index 0000000..4fa6dc2
--- /dev/null
+++ b/sensors/.gitignore
@@ -0,0 +1,3 @@
+build
+.vscode
+toolchain.cmake
diff --git a/sensors/CMakeLists.txt b/sensors/CMakeLists.txt
new file mode 100644
index 0000000..55db096
--- /dev/null
+++ b/sensors/CMakeLists.txt
@@ -0,0 +1,125 @@
+cmake_minimum_required (VERSION 2.8.10 FATAL_ERROR)
+set (BUILD_SHARED_LIBRARIES OFF)
+include (ExternalProject)
+set (CMAKE_CXX_STANDARD 14)
+set (CMAKE_CXX_STANDARD_REQUIRED ON)
+set (CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -lstdc++fs")
+set (CMAKE_MODULE_PATH ${CMAKE_CURRENT_SOURCE_DIR}/cmake ${CMAKE_MODULE_PATH})
+
+option (HUNTER_ENABLED "Enable hunter package pulling" OFF)
+include ("cmake/HunterGate.cmake")
+
+huntergate (URL "https://github.com/ruslo/hunter/archive/v0.18.64.tar.gz" SHA1
+ "baf9c8cc4f65306f0e442b5419967b4c4c04589a")
+
+project (sensors CXX)
+
+set (FAN_SRC_FILES src/TachSensor.cpp src/PwmSensor.cpp src/Utils.cpp
+ src/Thresholds.cpp)
+
+set (HWMON_TEMP_SRC_FILES src/Utils.cpp src/HwmonTempSensor.cpp
+ src/Thresholds.cpp)
+
+set (CPU_SRC_FILES src/Utils.cpp src/CPUSensor.cpp src/Thresholds.cpp)
+
+set (ADC_SRC_FILES src/Utils.cpp src/ADCSensor.cpp src/Thresholds.cpp)
+
+set (EXTERNAL_PACKAGES Boost sdbusplus-project nlohmann-json)
+set (SENSOR_LINK_LIBS -lsystemd stdc++fs sdbusplus)
+
+option (EXTERNAL_PROJECT "Enable Cloning External Projects" OFF)
+if (EXTERNAL_PROJECT)
+ option (ENABLE_TEST "Enable Google Test" OFF)
+ include_directories (${CMAKE_CURRENT_SOURCE_DIR}/include/non-yocto)
+
+ externalproject_add (
+ Boost URL
+ https://dl.bintray.com/boostorg/release/1.66.0/source/boost_1_66_0.tar.gz
+ URL_MD5 d275cd85b00022313c171f602db59fc5 SOURCE_DIR
+ "${CMAKE_BINARY_DIR}/boost-src" BINARY_DIR
+ "${CMAKE_BINARY_DIR}/boost-build" CONFIGURE_COMMAND "" BUILD_COMMAND ""
+ INSTALL_COMMAND ""
+ )
+ include_directories (${CMAKE_BINARY_DIR}/boost-src)
+ set (CMAKE_PREFIX_PATH ${CMAKE_BINARY_DIR}/boost-src ${CMAKE_PREFIX_PATH})
+
+ # requires apt install autoconf-archive and autoconf
+ externalproject_add (sdbusplus-project PREFIX
+ ${CMAKE_BINARY_DIR}/sdbusplus-project GIT_REPOSITORY
+ https://github.com/openbmc/sdbusplus.git SOURCE_DIR
+ ${CMAKE_BINARY_DIR}/sdbusplus-src BINARY_DIR
+ ${CMAKE_BINARY_DIR}/sdbusplus-build CONFIGURE_COMMAND
+ "" BUILD_COMMAND cd ${CMAKE_BINARY_DIR}/sdbusplus-src
+ && ./bootstrap.sh && ./configure --enable-transaction
+ && make -j libsdbusplus.la INSTALL_COMMAND ""
+ LOG_DOWNLOAD ON)
+ include_directories (${CMAKE_BINARY_DIR}/sdbusplus-src)
+ link_directories (${CMAKE_BINARY_DIR}/sdbusplus-src/.libs)
+
+ externalproject_add (nlohmann-json PREFIX
+ ${CMAKE_CURRENT_BINARY_DIR}/nlohmann-json
+ GIT_REPOSITORY https://github.com/nlohmann/json.git
+ SOURCE_DIR ${CMAKE_BINARY_DIR}/nlohmann-json-src
+ BINARY_DIR ${CMAKE_BINARY_DIR}/nlohmann-json-build
+ CONFIGURE_COMMAND "" BUILD_COMMAND "" INSTALL_COMMAND
+ "" LOG_DOWNLOAD ON)
+ include_directories (${CMAKE_BINARY_DIR}/nlohmann-json-src/include)
+ if (ENABLE_TEST)
+ option (HUNTER_ENABLED "Enable hunter package pulling" ON)
+ hunter_add_package (GTest)
+
+ find_package (GTest CONFIG REQUIRED)
+
+ enable_testing ()
+
+ add_executable (runTachTests tests/test_TachSensor.cpp ${FAN_SRC_FILES})
+ add_test (NAME test_fansensor COMMAND runTachTests)
+ target_link_libraries (runTachTests GTest::main GTest::gtest pthread
+ ${DBUS_LIBRARIES} stdc++fs)
+ add_dependencies (runTachTests nlohmann-json)
+
+ add_executable (runHwmonTempTests tests/test_HwmonTempSensor.cpp
+ ${HWMON_TEMP_SRC_FILES})
+ add_test (NAME test_hwmontempsensor COMMAND runHwmonTempTests)
+ target_link_libraries (runHwmonTempTests GTest::main GTest::gtest
+ pthread ${DBUS_LIBRARIES} stdc++fs)
+ add_dependencies (runHwmonTempTests nlohmann-json)
+ endif ()
+
+endif ()
+
+add_definitions (-DBOOST_ERROR_CODE_HEADER_ONLY)
+add_definitions (-DBOOST_SYSTEM_NO_DEPRECATED)
+add_definitions (-DBOOST_ALL_NO_LIB)
+add_definitions (-DBOOST_NO_RTTI)
+add_definitions (-DBOOST_NO_TYPEID)
+add_definitions (-DBOOST_ASIO_DISABLE_THREADS)
+
+link_directories (${EXTERNAL_INSTALL_LOCATION}/lib)
+
+include_directories (${CMAKE_CURRENT_SOURCE_DIR}/include)
+
+add_executable (fansensor src/FanMain.cpp ${FAN_SRC_FILES})
+add_dependencies (fansensor sdbusplus)
+target_link_libraries (fansensor ${SENSOR_LINK_LIBS})
+
+add_executable (hwmontempsensor src/HwmonTempMain.cpp ${HWMON_TEMP_SRC_FILES})
+add_dependencies (hwmontempsensor sdbusplus)
+target_link_libraries (hwmontempsensor ${SENSOR_LINK_LIBS})
+
+add_executable (cpusensor src/CPUSensorMain.cpp ${CPU_SRC_FILES})
+add_dependencies (cpusensor sdbusplus)
+target_link_libraries (cpusensor ${SENSOR_LINK_LIBS})
+
+add_executable (adcsensor src/ADCSensorMain.cpp ${ADC_SRC_FILES})
+add_dependencies (adcsensor sdbusplus)
+target_link_libraries (adcsensor ${SENSOR_LINK_LIBS})
+
+if (EXTERNAL_PROJECT)
+ add_dependencies (fansensor ${EXTERNAL_PACKAGES})
+ add_dependencies (hwmontempsensor ${EXTERNAL_PACKAGES})
+ add_dependencies (adcsensor ${EXTERNAL_PACKAGES})
+ add_dependencies (cpusensor ${EXTERNAL_PACKAGES})
+endif ()
+
+install (TARGETS fansensor hwmontempsensor cpusensor adcsensor DESTINATION bin)
diff --git a/sensors/Jenkinsfile b/sensors/Jenkinsfile
new file mode 100644
index 0000000..c4f6c7a
--- /dev/null
+++ b/sensors/Jenkinsfile
@@ -0,0 +1,26 @@
+#!groovy
+
+
+stage 'Debug Build'
+ sh '''
+ rm -rf build_debug
+ mkdir build_debug
+ cd build_debug
+ cmake .. -DCMAKE_BUILD_TYPE="Debug" -DHUNTER_ENABLED=1 -DEXTERNAL_PROJECT=1
+ cmake --build .'''
+
+stage 'Debug Test'
+ sh '''cd build_debug
+ dbus-launch ctest -V --output-on-failure'''
+
+stage 'Release Build'
+ sh '''
+ rm -rf build_release
+ mkdir build_release
+ cd build_release
+ cmake .. -DCMAKE_BUILD_TYPE="Release" -DHUNTER_ENABLED=1 -DEXTERNAL_PROJECT=1
+ cmake --build .'''
+
+stage 'Release Test'
+ sh '''cd build_release
+ dbus-launch ctest -V --output-on-failure'''
diff --git a/sensors/cmake-format.json b/sensors/cmake-format.json
new file mode 100644
index 0000000..4a68fb7
--- /dev/null
+++ b/sensors/cmake-format.json
@@ -0,0 +1,13 @@
+{
+ "enum_char": ".",
+ "line_ending": "unix",
+ "bullet_char": "*",
+ "max_subargs_per_line": 99,
+ "command_case": "lower",
+ "tab_size": 4,
+ "line_width": 80,
+ "separate_fn_name_with_space": true,
+ "dangle_parens": true,
+ "separate_ctrl_name_with_space": true
+}
+
diff --git a/sensors/cmake/Finddbus.cmake b/sensors/cmake/Finddbus.cmake
new file mode 100644
index 0000000..2d8eea2
--- /dev/null
+++ b/sensors/cmake/Finddbus.cmake
@@ -0,0 +1,61 @@
+# - Try to find DBus
+# Once done, this will define
+#
+# DBUS_FOUND - system has DBus
+# DBUS_INCLUDE_DIRS - the DBus include directories
+# DBUS_LIBRARIES - link these to use DBus
+#
+# Copyright (C) 2012 Raphael Kubo da Costa <rakuco@webkit.org>
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+# 1. Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+# 2. Redistributions in binary form must reproduce the above copyright
+# notice, this list of conditions and the following disclaimer in the
+# documentation and/or other materials provided with the distribution.
+#
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDER AND ITS CONTRIBUTORS ``AS
+# IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+# THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR ITS
+# CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+# EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+# PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+# OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+# WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+# OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+# ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+FIND_PACKAGE(PkgConfig)
+PKG_CHECK_MODULES(PC_DBUS QUIET dbus-1)
+
+FIND_LIBRARY(DBUS_LIBRARIES
+ NAMES dbus-1
+ HINTS ${PC_DBUS_LIBDIR}
+ ${PC_DBUS_LIBRARY_DIRS}
+)
+
+message("DBUS_LIBRARIES = ${DBUS_LIBRARIES}")
+
+FIND_PATH(DBUS_INCLUDE_DIR
+ NAMES dbus/dbus.h
+ HINTS ${PC_DBUS_INCLUDEDIR}
+ ${PC_DBUS_INCLUDE_DIRS}
+)
+
+GET_FILENAME_COMPONENT(_DBUS_LIBRARY_DIR ${DBUS_LIBRARIES} PATH)
+FIND_PATH(DBUS_ARCH_INCLUDE_DIR
+ NAMES dbus/dbus-arch-deps.h
+ HINTS ${PC_DBUS_INCLUDEDIR}
+ ${PC_DBUS_INCLUDE_DIRS}
+ ${_DBUS_LIBRARY_DIR}
+ ${DBUS_INCLUDE_DIR}
+ PATH_SUFFIXES include
+)
+
+SET(DBUS_INCLUDE_DIRS ${DBUS_INCLUDE_DIR} ${DBUS_ARCH_INCLUDE_DIR})
+
+INCLUDE(FindPackageHandleStandardArgs)
+FIND_PACKAGE_HANDLE_STANDARD_ARGS(DBUS REQUIRED_VARS DBUS_INCLUDE_DIRS DBUS_LIBRARIES)
diff --git a/sensors/cmake/HunterGate.cmake b/sensors/cmake/HunterGate.cmake
new file mode 100644
index 0000000..97f69cc
--- /dev/null
+++ b/sensors/cmake/HunterGate.cmake
@@ -0,0 +1,514 @@
+# Copyright (c) 2013-2015, Ruslan Baratov
+# All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions are met:
+#
+# * Redistributions of source code must retain the above copyright notice, this
+# list of conditions and the following disclaimer.
+#
+# * Redistributions in binary form must reproduce the above copyright notice,
+# this list of conditions and the following disclaimer in the documentation
+# and/or other materials provided with the distribution.
+#
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+# DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
+# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+# SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+# OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+# This is a gate file to Hunter package manager.
+# Include this file using `include` command and add package you need, example:
+#
+# cmake_minimum_required(VERSION 3.0)
+#
+# include("cmake/HunterGate.cmake")
+# HunterGate(
+# URL "https://github.com/path/to/hunter/archive.tar.gz"
+# SHA1 "798501e983f14b28b10cda16afa4de69eee1da1d"
+# )
+#
+# project(MyProject)
+#
+# hunter_add_package(Foo)
+# hunter_add_package(Boo COMPONENTS Bar Baz)
+#
+# Projects:
+# * https://github.com/hunter-packages/gate/
+# * https://github.com/ruslo/hunter
+
+option(HUNTER_ENABLED "Enable Hunter package manager support" ON)
+if(HUNTER_ENABLED)
+ if(CMAKE_VERSION VERSION_LESS "3.0")
+ message(FATAL_ERROR "At least CMake version 3.0 required for hunter dependency management."
+ " Update CMake or set HUNTER_ENABLED to OFF.")
+ endif()
+endif()
+
+include(CMakeParseArguments) # cmake_parse_arguments
+
+option(HUNTER_STATUS_PRINT "Print working status" ON)
+option(HUNTER_STATUS_DEBUG "Print a lot info" OFF)
+
+set(HUNTER_WIKI "https://github.com/ruslo/hunter/wiki")
+
+function(hunter_gate_status_print)
+ foreach(print_message ${ARGV})
+ if(HUNTER_STATUS_PRINT OR HUNTER_STATUS_DEBUG)
+ message(STATUS "[hunter] ${print_message}")
+ endif()
+ endforeach()
+endfunction()
+
+function(hunter_gate_status_debug)
+ foreach(print_message ${ARGV})
+ if(HUNTER_STATUS_DEBUG)
+ string(TIMESTAMP timestamp)
+ message(STATUS "[hunter *** DEBUG *** ${timestamp}] ${print_message}")
+ endif()
+ endforeach()
+endfunction()
+
+function(hunter_gate_wiki wiki_page)
+ message("------------------------------ WIKI -------------------------------")
+ message(" ${HUNTER_WIKI}/${wiki_page}")
+ message("-------------------------------------------------------------------")
+ message("")
+ message(FATAL_ERROR "")
+endfunction()
+
+function(hunter_gate_internal_error)
+ message("")
+ foreach(print_message ${ARGV})
+ message("[hunter ** INTERNAL **] ${print_message}")
+ endforeach()
+ message("[hunter ** INTERNAL **] [Directory:${CMAKE_CURRENT_LIST_DIR}]")
+ message("")
+ hunter_gate_wiki("error.internal")
+endfunction()
+
+function(hunter_gate_fatal_error)
+ cmake_parse_arguments(hunter "" "WIKI" "" "${ARGV}")
+ string(COMPARE EQUAL "${hunter_WIKI}" "" have_no_wiki)
+ if(have_no_wiki)
+ hunter_gate_internal_error("Expected wiki")
+ endif()
+ message("")
+ foreach(x ${hunter_UNPARSED_ARGUMENTS})
+ message("[hunter ** FATAL ERROR **] ${x}")
+ endforeach()
+ message("[hunter ** FATAL ERROR **] [Directory:${CMAKE_CURRENT_LIST_DIR}]")
+ message("")
+ hunter_gate_wiki("${hunter_WIKI}")
+endfunction()
+
+function(hunter_gate_user_error)
+ hunter_gate_fatal_error(${ARGV} WIKI "error.incorrect.input.data")
+endfunction()
+
+function(hunter_gate_self root version sha1 result)
+ string(COMPARE EQUAL "${root}" "" is_bad)
+ if(is_bad)
+ hunter_gate_internal_error("root is empty")
+ endif()
+
+ string(COMPARE EQUAL "${version}" "" is_bad)
+ if(is_bad)
+ hunter_gate_internal_error("version is empty")
+ endif()
+
+ string(COMPARE EQUAL "${sha1}" "" is_bad)
+ if(is_bad)
+ hunter_gate_internal_error("sha1 is empty")
+ endif()
+
+ string(SUBSTRING "${sha1}" 0 7 archive_id)
+
+ if(EXISTS "${root}/cmake/Hunter")
+ set(hunter_self "${root}")
+ else()
+ set(
+ hunter_self
+ "${root}/_Base/Download/Hunter/${version}/${archive_id}/Unpacked"
+ )
+ endif()
+
+ set("${result}" "${hunter_self}" PARENT_SCOPE)
+endfunction()
+
+# Set HUNTER_GATE_ROOT cmake variable to suitable value.
+function(hunter_gate_detect_root)
+ # Check CMake variable
+ string(COMPARE NOTEQUAL "${HUNTER_ROOT}" "" not_empty)
+ if(not_empty)
+ set(HUNTER_GATE_ROOT "${HUNTER_ROOT}" PARENT_SCOPE)
+ hunter_gate_status_debug("HUNTER_ROOT detected by cmake variable")
+ return()
+ endif()
+
+ # Check environment variable
+ string(COMPARE NOTEQUAL "$ENV{HUNTER_ROOT}" "" not_empty)
+ if(not_empty)
+ set(HUNTER_GATE_ROOT "$ENV{HUNTER_ROOT}" PARENT_SCOPE)
+ hunter_gate_status_debug("HUNTER_ROOT detected by environment variable")
+ return()
+ endif()
+
+ # Check HOME environment variable
+ string(COMPARE NOTEQUAL "$ENV{HOME}" "" result)
+ if(result)
+ set(HUNTER_GATE_ROOT "$ENV{HOME}/.hunter" PARENT_SCOPE)
+ hunter_gate_status_debug("HUNTER_ROOT set using HOME environment variable")
+ return()
+ endif()
+
+ # Check SYSTEMDRIVE and USERPROFILE environment variable (windows only)
+ if(WIN32)
+ string(COMPARE NOTEQUAL "$ENV{SYSTEMDRIVE}" "" result)
+ if(result)
+ set(HUNTER_GATE_ROOT "$ENV{SYSTEMDRIVE}/.hunter" PARENT_SCOPE)
+ hunter_gate_status_debug(
+ "HUNTER_ROOT set using SYSTEMDRIVE environment variable"
+ )
+ return()
+ endif()
+
+ string(COMPARE NOTEQUAL "$ENV{USERPROFILE}" "" result)
+ if(result)
+ set(HUNTER_GATE_ROOT "$ENV{USERPROFILE}/.hunter" PARENT_SCOPE)
+ hunter_gate_status_debug(
+ "HUNTER_ROOT set using USERPROFILE environment variable"
+ )
+ return()
+ endif()
+ endif()
+
+ hunter_gate_fatal_error(
+ "Can't detect HUNTER_ROOT"
+ WIKI "error.detect.hunter.root"
+ )
+endfunction()
+
+macro(hunter_gate_lock dir)
+ if(NOT HUNTER_SKIP_LOCK)
+ if("${CMAKE_VERSION}" VERSION_LESS "3.2")
+ hunter_gate_fatal_error(
+ "Can't lock, upgrade to CMake 3.2 or use HUNTER_SKIP_LOCK"
+ WIKI "error.can.not.lock"
+ )
+ endif()
+ hunter_gate_status_debug("Locking directory: ${dir}")
+ file(LOCK "${dir}" DIRECTORY GUARD FUNCTION)
+ hunter_gate_status_debug("Lock done")
+ endif()
+endmacro()
+
+function(hunter_gate_download dir)
+ string(
+ COMPARE
+ NOTEQUAL
+ "$ENV{HUNTER_DISABLE_AUTOINSTALL}"
+ ""
+ disable_autoinstall
+ )
+ if(disable_autoinstall AND NOT HUNTER_RUN_INSTALL)
+ hunter_gate_fatal_error(
+ "Hunter not found in '${dir}'"
+ "Set HUNTER_RUN_INSTALL=ON to auto-install it from '${HUNTER_GATE_URL}'"
+ "Settings:"
+ " HUNTER_ROOT: ${HUNTER_GATE_ROOT}"
+ " HUNTER_SHA1: ${HUNTER_GATE_SHA1}"
+ WIKI "error.run.install"
+ )
+ endif()
+ string(COMPARE EQUAL "${dir}" "" is_bad)
+ if(is_bad)
+ hunter_gate_internal_error("Empty 'dir' argument")
+ endif()
+
+ string(COMPARE EQUAL "${HUNTER_GATE_SHA1}" "" is_bad)
+ if(is_bad)
+ hunter_gate_internal_error("HUNTER_GATE_SHA1 empty")
+ endif()
+
+ string(COMPARE EQUAL "${HUNTER_GATE_URL}" "" is_bad)
+ if(is_bad)
+ hunter_gate_internal_error("HUNTER_GATE_URL empty")
+ endif()
+
+ set(done_location "${dir}/DONE")
+ set(sha1_location "${dir}/SHA1")
+
+ set(build_dir "${dir}/Build")
+ set(cmakelists "${dir}/CMakeLists.txt")
+
+ hunter_gate_lock("${dir}")
+ if(EXISTS "${done_location}")
+ # while waiting for lock other instance can do all the job
+ hunter_gate_status_debug("File '${done_location}' found, skip install")
+ return()
+ endif()
+
+ file(REMOVE_RECURSE "${build_dir}")
+ file(REMOVE_RECURSE "${cmakelists}")
+
+ file(MAKE_DIRECTORY "${build_dir}") # check directory permissions
+
+ # Disabling languages speeds up a little bit, reduces noise in the output
+ # and avoids path too long windows error
+ file(
+ WRITE
+ "${cmakelists}"
+ "cmake_minimum_required(VERSION 3.0)\n"
+ "project(HunterDownload LANGUAGES NONE)\n"
+ "include(ExternalProject)\n"
+ "ExternalProject_Add(\n"
+ " Hunter\n"
+ " URL\n"
+ " \"${HUNTER_GATE_URL}\"\n"
+ " URL_HASH\n"
+ " SHA1=${HUNTER_GATE_SHA1}\n"
+ " DOWNLOAD_DIR\n"
+ " \"${dir}\"\n"
+ " SOURCE_DIR\n"
+ " \"${dir}/Unpacked\"\n"
+ " CONFIGURE_COMMAND\n"
+ " \"\"\n"
+ " BUILD_COMMAND\n"
+ " \"\"\n"
+ " INSTALL_COMMAND\n"
+ " \"\"\n"
+ ")\n"
+ )
+
+ if(HUNTER_STATUS_DEBUG)
+ set(logging_params "")
+ else()
+ set(logging_params OUTPUT_QUIET)
+ endif()
+
+ hunter_gate_status_debug("Run generate")
+
+ # Need to add toolchain file too.
+ # Otherwise on Visual Studio + MDD this will fail with error:
+ # "Could not find an appropriate version of the Windows 10 SDK installed on this machine"
+ if(EXISTS "${CMAKE_TOOLCHAIN_FILE}")
+ set(toolchain_arg "-DCMAKE_TOOLCHAIN_FILE=${CMAKE_TOOLCHAIN_FILE}")
+ else()
+ # 'toolchain_arg' can't be empty
+ set(toolchain_arg "-DCMAKE_TOOLCHAIN_FILE=")
+ endif()
+
+ execute_process(
+ COMMAND "${CMAKE_COMMAND}" "-H${dir}" "-B${build_dir}" "-G${CMAKE_GENERATOR}" "${toolchain_arg}"
+ WORKING_DIRECTORY "${dir}"
+ RESULT_VARIABLE download_result
+ ${logging_params}
+ )
+
+ if(NOT download_result EQUAL 0)
+ hunter_gate_internal_error("Configure project failed")
+ endif()
+
+ hunter_gate_status_print(
+ "Initializing Hunter workspace (${HUNTER_GATE_SHA1})"
+ " ${HUNTER_GATE_URL}"
+ " -> ${dir}"
+ )
+ execute_process(
+ COMMAND "${CMAKE_COMMAND}" --build "${build_dir}"
+ WORKING_DIRECTORY "${dir}"
+ RESULT_VARIABLE download_result
+ ${logging_params}
+ )
+
+ if(NOT download_result EQUAL 0)
+ hunter_gate_internal_error("Build project failed")
+ endif()
+
+ file(REMOVE_RECURSE "${build_dir}")
+ file(REMOVE_RECURSE "${cmakelists}")
+
+ file(WRITE "${sha1_location}" "${HUNTER_GATE_SHA1}")
+ file(WRITE "${done_location}" "DONE")
+
+ hunter_gate_status_debug("Finished")
+endfunction()
+
+# Must be a macro so master file 'cmake/Hunter' can
+# apply all variables easily just by 'include' command
+# (otherwise PARENT_SCOPE magic needed)
+macro(HunterGate)
+ if(HUNTER_GATE_DONE)
+ # variable HUNTER_GATE_DONE set explicitly for external project
+ # (see `hunter_download`)
+ set_property(GLOBAL PROPERTY HUNTER_GATE_DONE YES)
+ endif()
+
+ # First HunterGate command will init Hunter, others will be ignored
+ get_property(_hunter_gate_done GLOBAL PROPERTY HUNTER_GATE_DONE SET)
+
+ if(NOT HUNTER_ENABLED)
+ # Empty function to avoid error "unknown function"
+ function(hunter_add_package)
+ endfunction()
+ elseif(_hunter_gate_done)
+ hunter_gate_status_debug("Secondary HunterGate (use old settings)")
+ hunter_gate_self(
+ "${HUNTER_CACHED_ROOT}"
+ "${HUNTER_VERSION}"
+ "${HUNTER_SHA1}"
+ _hunter_self
+ )
+ include("${_hunter_self}/cmake/Hunter")
+ else()
+ set(HUNTER_GATE_LOCATION "${CMAKE_CURRENT_LIST_DIR}")
+
+ string(COMPARE NOTEQUAL "${PROJECT_NAME}" "" _have_project_name)
+ if(_have_project_name)
+ hunter_gate_fatal_error(
+ "Please set HunterGate *before* 'project' command. "
+ "Detected project: ${PROJECT_NAME}"
+ WIKI "error.huntergate.before.project"
+ )
+ endif()
+
+ cmake_parse_arguments(
+ HUNTER_GATE "LOCAL" "URL;SHA1;GLOBAL;FILEPATH" "" ${ARGV}
+ )
+
+ string(COMPARE EQUAL "${HUNTER_GATE_SHA1}" "" _empty_sha1)
+ string(COMPARE EQUAL "${HUNTER_GATE_URL}" "" _empty_url)
+ string(
+ COMPARE
+ NOTEQUAL
+ "${HUNTER_GATE_UNPARSED_ARGUMENTS}"
+ ""
+ _have_unparsed
+ )
+ string(COMPARE NOTEQUAL "${HUNTER_GATE_GLOBAL}" "" _have_global)
+ string(COMPARE NOTEQUAL "${HUNTER_GATE_FILEPATH}" "" _have_filepath)
+
+ if(_have_unparsed)
+ hunter_gate_user_error(
+ "HunterGate unparsed arguments: ${HUNTER_GATE_UNPARSED_ARGUMENTS}"
+ )
+ endif()
+ if(_empty_sha1)
+ hunter_gate_user_error("SHA1 suboption of HunterGate is mandatory")
+ endif()
+ if(_empty_url)
+ hunter_gate_user_error("URL suboption of HunterGate is mandatory")
+ endif()
+ if(_have_global)
+ if(HUNTER_GATE_LOCAL)
+ hunter_gate_user_error("Unexpected LOCAL (already has GLOBAL)")
+ endif()
+ if(_have_filepath)
+ hunter_gate_user_error("Unexpected FILEPATH (already has GLOBAL)")
+ endif()
+ endif()
+ if(HUNTER_GATE_LOCAL)
+ if(_have_global)
+ hunter_gate_user_error("Unexpected GLOBAL (already has LOCAL)")
+ endif()
+ if(_have_filepath)
+ hunter_gate_user_error("Unexpected FILEPATH (already has LOCAL)")
+ endif()
+ endif()
+ if(_have_filepath)
+ if(_have_global)
+ hunter_gate_user_error("Unexpected GLOBAL (already has FILEPATH)")
+ endif()
+ if(HUNTER_GATE_LOCAL)
+ hunter_gate_user_error("Unexpected LOCAL (already has FILEPATH)")
+ endif()
+ endif()
+
+ hunter_gate_detect_root() # set HUNTER_GATE_ROOT
+
+ # Beautify path, fix probable problems with windows path slashes
+ get_filename_component(
+ HUNTER_GATE_ROOT "${HUNTER_GATE_ROOT}" ABSOLUTE
+ )
+ hunter_gate_status_debug("HUNTER_ROOT: ${HUNTER_GATE_ROOT}")
+ if(NOT HUNTER_ALLOW_SPACES_IN_PATH)
+ string(FIND "${HUNTER_GATE_ROOT}" " " _contain_spaces)
+ if(NOT _contain_spaces EQUAL -1)
+ hunter_gate_fatal_error(
+ "HUNTER_ROOT (${HUNTER_GATE_ROOT}) contains spaces."
+ "Set HUNTER_ALLOW_SPACES_IN_PATH=ON to skip this error"
+ "(Use at your own risk!)"
+ WIKI "error.spaces.in.hunter.root"
+ )
+ endif()
+ endif()
+
+ string(
+ REGEX
+ MATCH
+ "[0-9]+\\.[0-9]+\\.[0-9]+[-_a-z0-9]*"
+ HUNTER_GATE_VERSION
+ "${HUNTER_GATE_URL}"
+ )
+ string(COMPARE EQUAL "${HUNTER_GATE_VERSION}" "" _is_empty)
+ if(_is_empty)
+ set(HUNTER_GATE_VERSION "unknown")
+ endif()
+
+ hunter_gate_self(
+ "${HUNTER_GATE_ROOT}"
+ "${HUNTER_GATE_VERSION}"
+ "${HUNTER_GATE_SHA1}"
+ _hunter_self
+ )
+
+ set(_master_location "${_hunter_self}/cmake/Hunter")
+ if(EXISTS "${HUNTER_GATE_ROOT}/cmake/Hunter")
+ # Hunter downloaded manually (e.g. by 'git clone')
+ set(_unused "xxxxxxxxxx")
+ set(HUNTER_GATE_SHA1 "${_unused}")
+ set(HUNTER_GATE_VERSION "${_unused}")
+ else()
+ get_filename_component(_archive_id_location "${_hunter_self}/.." ABSOLUTE)
+ set(_done_location "${_archive_id_location}/DONE")
+ set(_sha1_location "${_archive_id_location}/SHA1")
+
+ # Check Hunter already downloaded by HunterGate
+ if(NOT EXISTS "${_done_location}")
+ hunter_gate_download("${_archive_id_location}")
+ endif()
+
+ if(NOT EXISTS "${_done_location}")
+ hunter_gate_internal_error("hunter_gate_download failed")
+ endif()
+
+ if(NOT EXISTS "${_sha1_location}")
+ hunter_gate_internal_error("${_sha1_location} not found")
+ endif()
+ file(READ "${_sha1_location}" _sha1_value)
+ string(COMPARE EQUAL "${_sha1_value}" "${HUNTER_GATE_SHA1}" _is_equal)
+ if(NOT _is_equal)
+ hunter_gate_internal_error(
+ "Short SHA1 collision:"
+ " ${_sha1_value} (from ${_sha1_location})"
+ " ${HUNTER_GATE_SHA1} (HunterGate)"
+ )
+ endif()
+ if(NOT EXISTS "${_master_location}")
+ hunter_gate_user_error(
+ "Master file not found:"
+ " ${_master_location}"
+ "try to update Hunter/HunterGate"
+ )
+ endif()
+ endif()
+ include("${_master_location}")
+ set_property(GLOBAL PROPERTY HUNTER_GATE_DONE YES)
+ endif()
+endmacro()
\ No newline at end of file
diff --git a/sensors/include/ADCSensor.hpp b/sensors/include/ADCSensor.hpp
new file mode 100644
index 0000000..07c7893
--- /dev/null
+++ b/sensors/include/ADCSensor.hpp
@@ -0,0 +1,45 @@
+#pragma once
+
+#include <Thresholds.hpp>
+#include <sdbusplus/asio/object_server.hpp>
+
+class ADCSensor
+{
+ public:
+ std::string name;
+ std::string configuration;
+ ADCSensor(const std::string &path,
+ sdbusplus::asio::object_server &objectServer,
+ std::shared_ptr<sdbusplus::asio::connection> &conn,
+ boost::asio::io_service &io, const std::string &sensor_name,
+ std::vector<thresholds::Threshold> &&thresholds,
+ const double scale_factor,
+ const std::string &sensorConfiguration);
+ ~ADCSensor();
+
+ private:
+ std::string path;
+ sdbusplus::asio::object_server &objServer;
+ std::vector<thresholds::Threshold> thresholds;
+ std::shared_ptr<sdbusplus::asio::dbus_interface> sensor_interface;
+ std::shared_ptr<sdbusplus::asio::dbus_interface>
+ threshold_interface_warning;
+ std::shared_ptr<sdbusplus::asio::dbus_interface>
+ threshold_interface_critical;
+ boost::asio::posix::stream_descriptor input_dev;
+ boost::asio::deadline_timer wait_timer;
+ boost::asio::streambuf read_buf;
+ double value;
+ int err_count;
+ double max_value;
+ double min_value;
+ double scale_factor;
+ void setup_read(void);
+ void handle_response(const boost::system::error_code &err);
+ void check_thresholds(void);
+ void update_value(const double &new_value);
+ void assert_thresholds(thresholds::Level level,
+ thresholds::Direction direction, bool assert);
+ void set_initial_properties(
+ std::shared_ptr<sdbusplus::asio::connection> &conn);
+};
\ No newline at end of file
diff --git a/sensors/include/CPUSensor.hpp b/sensors/include/CPUSensor.hpp
new file mode 100644
index 0000000..85ae831
--- /dev/null
+++ b/sensors/include/CPUSensor.hpp
@@ -0,0 +1,47 @@
+#pragma once
+
+#include <Thresholds.hpp>
+#include <sdbusplus/asio/object_server.hpp>
+
+class CPUSensor
+{
+ private:
+ std::string path;
+ std::string objectType;
+ sdbusplus::asio::object_server &objServer;
+ std::shared_ptr<sdbusplus::asio::connection> dbusConnection;
+ std::string name;
+ std::vector<thresholds::Threshold> thresholds;
+ std::shared_ptr<sdbusplus::asio::dbus_interface> sensor_interface;
+ std::shared_ptr<sdbusplus::asio::dbus_interface>
+ threshold_interface_warning;
+ std::shared_ptr<sdbusplus::asio::dbus_interface>
+ threshold_interface_critical;
+ boost::asio::posix::stream_descriptor input_dev;
+ boost::asio::deadline_timer wait_timer;
+ boost::asio::streambuf read_buf;
+ double value;
+ int err_count;
+ double max_value;
+ double min_value;
+ void setup_read(void);
+ void handle_response(const boost::system::error_code &err);
+ void check_thresholds(void);
+ void update_value(const double &new_value);
+ void assert_thresholds(thresholds::Level level,
+ thresholds::Direction direction, bool assert);
+ void set_initial_properties(
+ std::shared_ptr<sdbusplus::asio::connection> &conn);
+
+ public:
+ std::string configuration;
+ CPUSensor(const std::string &path, const std::string &objectType,
+ sdbusplus::asio::object_server &object_server,
+ std::shared_ptr<sdbusplus::asio::connection> &conn,
+ boost::asio::io_service &io, const std::string &fan_name,
+ std::vector<thresholds::Threshold> &&thresholds,
+ const std::string &configuration);
+ ~CPUSensor();
+ constexpr static unsigned int SENSOR_SCALE_FACTOR = 1000;
+ constexpr static unsigned int SENSOR_POLL_MS = 1000;
+};
diff --git a/sensors/include/HwmonTempSensor.hpp b/sensors/include/HwmonTempSensor.hpp
new file mode 100644
index 0000000..58eb534
--- /dev/null
+++ b/sensors/include/HwmonTempSensor.hpp
@@ -0,0 +1,44 @@
+#pragma once
+
+#include <Thresholds.hpp>
+#include <sdbusplus/asio/object_server.hpp>
+
+class HwmonTempSensor
+{
+ public:
+ std::string name;
+ std::string configuration;
+ HwmonTempSensor(const std::string &path, const std::string &objectType,
+ sdbusplus::asio::object_server &objectServer,
+ std::shared_ptr<sdbusplus::asio::connection> &conn,
+ boost::asio::io_service &io, const std::string &fan_name,
+ std::vector<thresholds::Threshold> &&thresholds,
+ const std::string &sensorConfiguration);
+ ~HwmonTempSensor();
+
+ private:
+ std::string path;
+ std::string objectType;
+ sdbusplus::asio::object_server &objServer;
+ std::vector<thresholds::Threshold> thresholds;
+ std::shared_ptr<sdbusplus::asio::dbus_interface> sensor_interface;
+ std::shared_ptr<sdbusplus::asio::dbus_interface>
+ threshold_interface_warning;
+ std::shared_ptr<sdbusplus::asio::dbus_interface>
+ threshold_interface_critical;
+ boost::asio::posix::stream_descriptor input_dev;
+ boost::asio::deadline_timer wait_timer;
+ boost::asio::streambuf read_buf;
+ double value;
+ int err_count;
+ double max_value;
+ double min_value;
+ void setup_read(void);
+ void handle_response(const boost::system::error_code &err);
+ void check_thresholds(void);
+ void update_value(const double &new_value);
+ void assert_thresholds(thresholds::Level level,
+ thresholds::Direction direction, bool assert);
+ void set_initial_properties(
+ std::shared_ptr<sdbusplus::asio::connection> &conn);
+};
\ No newline at end of file
diff --git a/sensors/include/PwmSensor.hpp b/sensors/include/PwmSensor.hpp
new file mode 100644
index 0000000..9429099
--- /dev/null
+++ b/sensors/include/PwmSensor.hpp
@@ -0,0 +1,20 @@
+#pragma once
+
+#include <sdbusplus/asio/object_server.hpp>
+
+class PwmSensor
+{
+ public:
+ PwmSensor(const std::string& sysPath,
+ sdbusplus::asio::object_server& objectServer);
+ ~PwmSensor();
+
+ private:
+ std::string sysPath;
+ sdbusplus::asio::object_server& objectServer;
+ std::string name;
+ std::shared_ptr<sdbusplus::asio::dbus_interface> sensorInterface;
+ std::shared_ptr<sdbusplus::asio::dbus_interface> controlInterface;
+ void setValue(uint32_t value);
+ uint32_t getValue(bool errThrow = true);
+};
\ No newline at end of file
diff --git a/sensors/include/TachSensor.hpp b/sensors/include/TachSensor.hpp
new file mode 100644
index 0000000..2c8b535
--- /dev/null
+++ b/sensors/include/TachSensor.hpp
@@ -0,0 +1,44 @@
+#pragma once
+
+#include <Thresholds.hpp>
+#include <sdbusplus/asio/object_server.hpp>
+
+class TachSensor
+{
+ public:
+ std::string name;
+ std::string configuration;
+ TachSensor(const std::string &path,
+ sdbusplus::asio::object_server &objectServer,
+ std::shared_ptr<sdbusplus::asio::connection> &conn,
+ boost::asio::io_service &io, const std::string &fan_name,
+ std::vector<thresholds::Threshold> &&thresholds,
+ const std::string &sensorConfiguration);
+ ~TachSensor();
+
+ private:
+ std::string path;
+ sdbusplus::asio::object_server &objServer;
+ std::shared_ptr<sdbusplus::asio::connection> dbusConnection;
+ std::vector<thresholds::Threshold> thresholds;
+ std::shared_ptr<sdbusplus::asio::dbus_interface> sensor_interface;
+ std::shared_ptr<sdbusplus::asio::dbus_interface>
+ threshold_interface_warning;
+ std::shared_ptr<sdbusplus::asio::dbus_interface>
+ threshold_interface_critical;
+ boost::asio::posix::stream_descriptor input_dev;
+ boost::asio::deadline_timer wait_timer;
+ boost::asio::streambuf read_buf;
+ double value;
+ int err_count;
+ double max_value;
+ double min_value;
+ void setup_read(void);
+ void handle_response(const boost::system::error_code &err);
+ void check_thresholds(void);
+ void update_value(const double &new_value);
+ void assert_thresholds(thresholds::Level level,
+ thresholds::Direction direction, bool assert);
+ void set_initial_properties(
+ std::shared_ptr<sdbusplus::asio::connection> &conn);
+};
diff --git a/sensors/include/Thresholds.hpp b/sensors/include/Thresholds.hpp
new file mode 100644
index 0000000..5b194c6
--- /dev/null
+++ b/sensors/include/Thresholds.hpp
@@ -0,0 +1,47 @@
+#pragma once
+#include <Utils.hpp>
+#include <nlohmann/json.hpp>
+
+namespace thresholds
+{
+enum Level
+{
+ WARNING,
+ CRITICAL
+};
+enum Direction
+{
+ HIGH,
+ LOW
+};
+struct Threshold
+{
+ Threshold(const Level &lev, const Direction &dir, const double &val,
+ bool write = true) :
+ level(lev),
+ direction(dir), value(val), writeable(write)
+ {
+ }
+ Level level;
+ Direction direction;
+ double value;
+ bool writeable;
+};
+
+bool ParseThresholdsFromConfig(
+ const SensorData &sensorData,
+ std::vector<thresholds::Threshold> &thresholdVector,
+ const std::string *matchLabel = nullptr);
+
+bool ParseThresholdsFromAttr(std::vector<thresholds::Threshold> &thresholds,
+ const std::string &input_path,
+ const double scale_factor);
+bool HasCriticalInterface(
+ const std::vector<thresholds::Threshold> &threshold_vector);
+bool HasWarningInterface(
+ const std::vector<thresholds::Threshold> &threshold_vector);
+
+void persistThreshold(const std::string &baseInterface, const std::string &path,
+ const thresholds::Threshold &threshold,
+ std::shared_ptr<sdbusplus::asio::connection> &conn);
+} // namespace thresholds
diff --git a/sensors/include/Utils.hpp b/sensors/include/Utils.hpp
new file mode 100644
index 0000000..0f1a5db
--- /dev/null
+++ b/sensors/include/Utils.hpp
@@ -0,0 +1,34 @@
+#pragma once
+#include <boost/container/flat_map.hpp>
+#include <experimental/filesystem>
+#include <iostream>
+#include <regex>
+#include <sdbusplus/asio/connection.hpp>
+#include <sdbusplus/message/types.hpp>
+
+const constexpr char* JSON_STORE = "/var/configuration/flattened.json";
+const constexpr char* INVENTORY_PATH = "/xyz/openbmc_project/inventory";
+const constexpr char* ENTITY_MANAGER_NAME = "xyz.openbmc_project.EntityManager";
+const std::regex ILLEGAL_DBUS_REGEX("[^A-Za-z0-9_]");
+
+using BasicVariantType =
+ sdbusplus::message::variant<std::string, int64_t, uint64_t, double, int32_t,
+ uint32_t, int16_t, uint16_t, uint8_t, bool>;
+
+using ManagedObjectType = boost::container::flat_map<
+ sdbusplus::message::object_path,
+ boost::container::flat_map<
+ std::string,
+ boost::container::flat_map<std::string, BasicVariantType>>>;
+using SensorData = boost::container::flat_map<
+ std::string, boost::container::flat_map<std::string, BasicVariantType>>;
+
+bool find_files(const std::experimental::filesystem::path dir_path,
+ const std::string& match_string,
+ std::vector<std::experimental::filesystem::path>& found_paths,
+ unsigned int symlink_depth = 1);
+bool isPowerOn(const std::shared_ptr<sdbusplus::asio::connection>& conn);
+bool getSensorConfiguration(
+ const std::string& type,
+ const std::shared_ptr<sdbusplus::asio::connection>& dbusConnection,
+ ManagedObjectType& resp, bool useCache = false);
\ No newline at end of file
diff --git a/sensors/include/VariantVisitors.hpp b/sensors/include/VariantVisitors.hpp
new file mode 100644
index 0000000..8d4b004
--- /dev/null
+++ b/sensors/include/VariantVisitors.hpp
@@ -0,0 +1,75 @@
+/*
+// Copyright (c) 2018 Intel Corporation
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+*/
+
+#pragma once
+#include <boost/variant.hpp>
+#include <string>
+
+struct VariantToFloatVisitor : public boost::static_visitor<float>
+{
+ template <typename T> float operator()(const T &t) const
+ {
+ return static_cast<float>(t);
+ }
+};
+template <>
+inline float VariantToFloatVisitor::
+ operator()<std::string>(const std::string &s) const
+{
+ throw std::invalid_argument("Cannot translate string to float");
+}
+
+struct VariantToIntVisitor : public boost::static_visitor<int>
+{
+ template <typename T> int operator()(const T &t) const
+ {
+ return static_cast<int>(t);
+ }
+};
+template <>
+inline int VariantToIntVisitor::
+ operator()<std::string>(const std::string &s) const
+{
+ throw std::invalid_argument("Cannot translate string to int");
+}
+
+struct VariantToUnsignedIntVisitor : public boost::static_visitor<unsigned int>
+{
+ template <typename T> unsigned int operator()(const T &t) const
+ {
+ return static_cast<int>(t);
+ }
+};
+template <>
+inline unsigned int VariantToUnsignedIntVisitor::
+ operator()<std::string>(const std::string &s) const
+{
+ throw std::invalid_argument("Cannot translate string to unsigned int");
+}
+
+struct VariantToStringVisitor : public boost::static_visitor<std::string>
+{
+ template <typename T> std::string operator()(const T &t) const
+ {
+ return std::to_string(t);
+ }
+};
+template <>
+inline std::string VariantToStringVisitor::
+ operator()<std::string>(const std::string &s) const
+{
+ return s;
+}
\ No newline at end of file
diff --git a/sensors/include/non-yocto/linux/peci-ioctl.h b/sensors/include/non-yocto/linux/peci-ioctl.h
new file mode 100644
index 0000000..9cddb8b
--- /dev/null
+++ b/sensors/include/non-yocto/linux/peci-ioctl.h
@@ -0,0 +1,247 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/* Copyright (c) 2018 Intel Corporation */
+
+#ifndef __PECI_IOCTL_H
+#define __PECI_IOCTL_H
+
+#include <linux/ioctl.h>
+#include <linux/types.h>
+
+/* Base Address of 48d */
+#define PECI_BASE_ADDR 0x30 /* The PECI client's default address of 0x30 */
+#define PECI_OFFSET_MAX 8 /* Max numver of CPU clients */
+
+/* PCI Access */
+#define MAX_PCI_READ_LEN 24 /* Number of bytes of the PCI Space read */
+
+#define PCI_BUS0_CPU0 0x00
+#define PCI_BUS0_CPU1 0x80
+#define PCI_CPUBUSNO_BUS 0x00
+#define PCI_CPUBUSNO_DEV 0x08
+#define PCI_CPUBUSNO_FUNC 0x02
+#define PCI_CPUBUSNO 0xcc
+#define PCI_CPUBUSNO_1 0xd0
+#define PCI_CPUBUSNO_VALID 0xd4
+
+/* Package Identifier Read Parameter Value */
+#define PKG_ID_CPU_ID 0x0000 /* CPUID Info */
+#define PKG_ID_PLATFORM_ID 0x0001 /* Platform ID */
+#define PKG_ID_UNCORE_ID 0x0002 /* Uncore Device ID */
+#define PKG_ID_MAX_THREAD_ID 0x0003 /* Max Thread ID */
+#define PKG_ID_MICROCODE_REV 0x0004 /* CPU Microcode Update Revision */
+#define PKG_ID_MACHINE_CHECK_STATUS 0x0005 /* Machine Check Status */
+
+/* Crashdump Parameters */
+enum crashdump_agent {
+ CRASHDUMP_CORE = 0x00,
+ CRASHDUMP_TOR = 0x01,
+};
+enum crashdump_discovery_sub_opcode {
+ CRASHDUMP_ENABLED = 0x00,
+ CRASHDUMP_NUM_AGENTS = 0x01,
+ CRASHDUMP_AGENT_DATA = 0x02,
+};
+enum crashdump_agent_data_param {
+ CRASHDUMP_AGENT_ID = 0x00,
+ CRASHDUMP_AGENT_PARAM = 0x01,
+};
+enum crashdump_agent_param {
+ CRASHDUMP_PAYLOAD_SIZE = 0x00,
+};
+
+/* RdPkgConfig Index */
+#define MBX_INDEX_CPU_ID 0 /* Package Identifier Read */
+#define MBX_INDEX_VR_DEBUG 1 /* VR Debug */
+#define MBX_INDEX_PKG_TEMP_READ 2 /* Package Temperature Read */
+#define MBX_INDEX_ENERGY_COUNTER 3 /* Energy counter */
+#define MBX_INDEX_ENERGY_STATUS 4 /* DDR Energy Status */
+#define MBX_INDEX_WAKE_MODE_BIT 5 /* "Wake on PECI" Mode bit */
+#define MBX_INDEX_EPI 6 /* Efficient Performance Indication */
+#define MBX_INDEX_PKG_RAPL_PERF 8 /* Pkg RAPL Performance Status Read */
+#define MBX_INDEX_PER_CORE_DTS_TEMP 9 /* Per Core DTS Temperature Read */
+#define MBX_INDEX_DTS_MARGIN 10 /* DTS thermal margin */
+#define MBX_INDEX_SKT_PWR_THRTL_DUR 11 /* Socket Power Throttled Duration */
+#define MBX_INDEX_CFG_TDP_CONTROL 12 /* TDP Config Control */
+#define MBX_INDEX_CFG_TDP_LEVELS 13 /* TDP Config Levels */
+#define MBX_INDEX_DDR_DIMM_TEMP 14 /* DDR DIMM Temperature */
+#define MBX_INDEX_CFG_ICCMAX 15 /* Configurable ICCMAX */
+#define MBX_INDEX_TEMP_TARGET 16 /* Temperature Target Read */
+#define MBX_INDEX_CURR_CFG_LIMIT 17 /* Current Config Limit */
+#define MBX_INDEX_DIMM_TEMP_READ 20 /* Package Thermal Status Read */
+#define MBX_INDEX_DRAM_IMC_TMP_READ 22 /* DRAM IMC Temperature Read */
+#define MBX_INDEX_DDR_CH_THERM_STAT 23 /* DDR Channel Thermal Status */
+#define MBX_INDEX_PKG_POWER_LIMIT1 26 /* Package Power Limit1 */
+#define MBX_INDEX_PKG_POWER_LIMIT2 27 /* Package Power Limit2 */
+#define MBX_INDEX_TDP 28 /* Thermal design power minimum */
+#define MBX_INDEX_TDP_HIGH 29 /* Thermal design power maximum */
+#define MBX_INDEX_TDP_UNITS 30 /* Units for power/energy registers */
+#define MBX_INDEX_RUN_TIME 31 /* Accumulated Run Time */
+#define MBX_INDEX_CONSTRAINED_TIME 32 /* Thermally Constrained Time Read */
+#define MBX_INDEX_TURBO_RATIO 33 /* Turbo Activation Ratio */
+#define MBX_INDEX_DDR_RAPL_PL1 34 /* DDR RAPL PL1 */
+#define MBX_INDEX_DDR_PWR_INFO_HIGH 35 /* DRAM Power Info Read (high) */
+#define MBX_INDEX_DDR_PWR_INFO_LOW 36 /* DRAM Power Info Read (low) */
+#define MBX_INDEX_DDR_RAPL_PL2 37 /* DDR RAPL PL2 */
+#define MBX_INDEX_DDR_RAPL_STATUS 38 /* DDR RAPL Performance Status */
+#define MBX_INDEX_DDR_HOT_ABSOLUTE 43 /* DDR Hottest Dimm Absolute Temp */
+#define MBX_INDEX_DDR_HOT_RELATIVE 44 /* DDR Hottest Dimm Relative Temp */
+#define MBX_INDEX_DDR_THROTTLE_TIME 45 /* DDR Throttle Time */
+#define MBX_INDEX_DDR_THERM_STATUS 46 /* DDR Thermal Status */
+#define MBX_INDEX_TIME_AVG_TEMP 47 /* Package time-averaged temperature */
+#define MBX_INDEX_TURBO_RATIO_LIMIT 49 /* Turbo Ratio Limit Read */
+#define MBX_INDEX_HWP_AUTO_OOB 53 /* HWP Autonomous Out-of-band */
+#define MBX_INDEX_DDR_WARM_BUDGET 55 /* DDR Warm Power Budget */
+#define MBX_INDEX_DDR_HOT_BUDGET 56 /* DDR Hot Power Budget */
+#define MBX_INDEX_PKG_PSYS_PWR_LIM3 57 /* Package/Psys Power Limit3 */
+#define MBX_INDEX_PKG_PSYS_PWR_LIM1 58 /* Package/Psys Power Limit1 */
+#define MBX_INDEX_PKG_PSYS_PWR_LIM2 59 /* Package/Psys Power Limit2 */
+#define MBX_INDEX_PKG_PSYS_PWR_LIM4 60 /* Package/Psys Power Limit4 */
+#define MBX_INDEX_PERF_LIMIT_REASON 65 /* Performance Limit Reasons */
+
+/* WrPkgConfig Index */
+#define MBX_INDEX_DIMM_AMBIENT 19
+#define MBX_INDEX_DIMM_TEMP 24
+
+enum peci_cmd {
+ PECI_CMD_XFER = 0,
+ PECI_CMD_PING,
+ PECI_CMD_GET_DIB,
+ PECI_CMD_GET_TEMP,
+ PECI_CMD_RD_PKG_CFG,
+ PECI_CMD_WR_PKG_CFG,
+ PECI_CMD_RD_IA_MSR,
+ PECI_CMD_WR_IA_MSR,
+ PECI_CMD_RD_PCI_CFG,
+ PECI_CMD_WR_PCI_CFG,
+ PECI_CMD_RD_PCI_CFG_LOCAL,
+ PECI_CMD_WR_PCI_CFG_LOCAL,
+ PECI_CMD_CRASHDUMP_DISC,
+ PECI_CMD_CRASHDUMP_GET_FRAME,
+ PECI_CMD_MAX
+};
+
+struct peci_ping_msg {
+ __u8 addr;
+} __attribute__((__packed__));
+
+struct peci_get_dib_msg {
+ __u8 addr;
+ __u32 dib;
+} __attribute__((__packed__));
+
+struct peci_get_temp_msg {
+ __u8 addr;
+ __s16 temp_raw;
+} __attribute__((__packed__));
+
+struct peci_rd_pkg_cfg_msg {
+ __u8 addr;
+ __u8 index;
+ __u16 param;
+ __u8 rx_len;
+ __u8 pkg_config[4];
+} __attribute__((__packed__));
+
+struct peci_wr_pkg_cfg_msg {
+ __u8 addr;
+ __u8 index;
+ __u16 param;
+ __u8 tx_len;
+ __u32 value;
+} __attribute__((__packed__));
+
+struct peci_rd_ia_msr_msg {
+ __u8 addr;
+ __u8 thread_id;
+ __u16 address;
+ __u64 value;
+} __attribute__((__packed__));
+
+struct peci_rd_pci_cfg_msg {
+ __u8 addr;
+ __u8 bus;
+ __u8 device;
+ __u8 function;
+ __u16 reg;
+ __u8 pci_config[4];
+} __attribute__((__packed__));
+
+struct peci_rd_pci_cfg_local_msg {
+ __u8 addr;
+ __u8 bus;
+ __u8 device;
+ __u8 function;
+ __u16 reg;
+ __u8 rx_len;
+ __u8 pci_config[4];
+} __attribute__((__packed__));
+
+struct peci_wr_pci_cfg_local_msg {
+ __u8 addr;
+ __u8 bus;
+ __u8 device;
+ __u8 function;
+ __u16 reg;
+ __u8 tx_len;
+ __u32 value;
+} __attribute__((__packed__));
+
+struct peci_crashdump_disc_msg {
+ __u8 addr;
+ __u8 subopcode;
+ __u8 param0;
+ __u16 param1;
+ __u8 param2;
+ __u8 rx_len;
+ __u8 data[8];
+} __attribute__((__packed__));
+
+struct peci_crashdump_get_frame_msg {
+ __u8 addr;
+ __u16 param0;
+ __u16 param1;
+ __u16 param2;
+ __u8 rx_len;
+ __u8 data[16];
+} __attribute__((__packed__));
+
+#define PECI_IOC_BASE 0xb6
+
+#define PECI_IOC_PING \
+ _IOWR(PECI_IOC_BASE, PECI_CMD_PING, struct peci_ping_msg)
+
+#define PECI_IOC_GET_DIB \
+ _IOWR(PECI_IOC_BASE, PECI_CMD_GET_DIB, struct peci_get_dib_msg)
+
+#define PECI_IOC_GET_TEMP \
+ _IOWR(PECI_IOC_BASE, PECI_CMD_GET_TEMP, struct peci_get_temp_msg)
+
+#define PECI_IOC_RD_PKG_CFG \
+ _IOWR(PECI_IOC_BASE, PECI_CMD_RD_PKG_CFG, struct peci_rd_pkg_cfg_msg)
+
+#define PECI_IOC_WR_PKG_CFG \
+ _IOWR(PECI_IOC_BASE, PECI_CMD_WR_PKG_CFG, struct peci_wr_pkg_cfg_msg)
+
+#define PECI_IOC_RD_IA_MSR \
+ _IOWR(PECI_IOC_BASE, PECI_CMD_RD_IA_MSR, struct peci_rd_ia_msr_msg)
+
+#define PECI_IOC_RD_PCI_CFG \
+ _IOWR(PECI_IOC_BASE, PECI_CMD_RD_PCI_CFG, struct peci_rd_pci_cfg_msg)
+
+#define PECI_IOC_RD_PCI_CFG_LOCAL \
+ _IOWR(PECI_IOC_BASE, PECI_CMD_RD_PCI_CFG_LOCAL, \
+ struct peci_rd_pci_cfg_local_msg)
+
+#define PECI_IOC_WR_PCI_CFG_LOCAL \
+ _IOWR(PECI_IOC_BASE, PECI_CMD_WR_PCI_CFG_LOCAL, \
+ struct peci_wr_pci_cfg_local_msg)
+
+#define PECI_IOC_CRASHDUMP_DISC \
+ _IOWR(PECI_IOC_BASE, PECI_CMD_CRASHDUMP_DISC, \
+ struct peci_crashdump_disc_msg)
+
+#define PECI_IOC_CRASHDUMP_GET_FRAME \
+ _IOWR(PECI_IOC_BASE, PECI_CMD_CRASHDUMP_GET_FRAME, \
+ struct peci_crashdump_get_frame_msg)
+
+#endif /* __PECI_IOCTL_H */
diff --git a/sensors/src/ADCSensor.cpp b/sensors/src/ADCSensor.cpp
new file mode 100644
index 0000000..1ac0988
--- /dev/null
+++ b/sensors/src/ADCSensor.cpp
@@ -0,0 +1,312 @@
+/*
+// Copyright (c) 2017 Intel Corporation
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+*/
+
+#include <unistd.h>
+
+#include <ADCSensor.hpp>
+#include <boost/algorithm/string/predicate.hpp>
+#include <boost/algorithm/string/replace.hpp>
+#include <boost/date_time/posix_time/posix_time.hpp>
+#include <iostream>
+#include <limits>
+#include <sdbusplus/asio/connection.hpp>
+#include <sdbusplus/asio/object_server.hpp>
+#include <string>
+
+static constexpr unsigned int SENSOR_POLL_MS = 500;
+static constexpr size_t WARN_AFTER_ERROR_COUNT = 10;
+
+// scaling factor from hwmon
+static constexpr unsigned int SENSOR_SCALE_FACTOR = 1000;
+
+ADCSensor::ADCSensor(const std::string &path,
+ sdbusplus::asio::object_server &objectServer,
+ std::shared_ptr<sdbusplus::asio::connection> &conn,
+ boost::asio::io_service &io,
+ const std::string &sensor_name,
+ std::vector<thresholds::Threshold> &&_thresholds,
+ const double scale_factor,
+ const std::string &sensorConfiguration) :
+ path(path),
+ objServer(objectServer), configuration(sensorConfiguration),
+ name(boost::replace_all_copy(sensor_name, " ", "_")),
+ thresholds(std::move(_thresholds)), scale_factor(scale_factor),
+ sensor_interface(objectServer.add_interface(
+ "/xyz/openbmc_project/sensors/voltage/" + name,
+ "xyz.openbmc_project.Sensor.Value")),
+ input_dev(io, open(path.c_str(), O_RDONLY)), wait_timer(io),
+ value(std::numeric_limits<double>::quiet_NaN()), err_count(0),
+ // todo, get these from config
+ max_value(20), min_value(0)
+{
+ if (thresholds::HasWarningInterface(thresholds))
+ {
+ threshold_interface_warning = objectServer.add_interface(
+ "/xyz/openbmc_project/sensors/voltage/" + name,
+ "xyz.openbmc_project.Sensor.Threshold.Warning");
+ }
+ if (thresholds::HasCriticalInterface(thresholds))
+ {
+ threshold_interface_critical = objectServer.add_interface(
+ "/xyz/openbmc_project/sensors/voltage/" + name,
+ "xyz.openbmc_project.Sensor.Threshold.Critical");
+ }
+ set_initial_properties(conn);
+ setup_read();
+}
+
+ADCSensor::~ADCSensor()
+{
+ // close the input dev to cancel async operations
+ input_dev.close();
+ wait_timer.cancel();
+ objServer.remove_interface(threshold_interface_warning);
+ objServer.remove_interface(threshold_interface_critical);
+ objServer.remove_interface(sensor_interface);
+}
+
+void ADCSensor::setup_read(void)
+{
+ boost::asio::async_read_until(
+ input_dev, read_buf, '\n',
+ [&](const boost::system::error_code &ec,
+ std::size_t /*bytes_transfered*/) { handle_response(ec); });
+}
+
+void ADCSensor::handle_response(const boost::system::error_code &err)
+{
+ if (err == boost::system::errc::bad_file_descriptor)
+ {
+ return; // we're being destroyed
+ }
+ std::istream response_stream(&read_buf);
+
+ if (!err)
+ {
+ std::string response;
+ std::getline(response_stream, response);
+
+ // todo read scaling factors from configuration
+ try
+ {
+ float nvalue = std::stof(response);
+
+ nvalue = (nvalue / SENSOR_SCALE_FACTOR) / scale_factor;
+
+ if (nvalue != value)
+ {
+ update_value(nvalue);
+ }
+ err_count = 0;
+ }
+ catch (std::invalid_argument)
+ {
+ err_count++;
+ }
+ }
+ else
+ {
+ std::cerr << "Failure to read sensor " << name << " at " << path
+ << " ec:" << err << "\n";
+
+ err_count++;
+ }
+
+ // only send value update once
+ if (err_count == WARN_AFTER_ERROR_COUNT)
+ {
+ update_value(0);
+ }
+
+ response_stream.clear();
+ input_dev.close();
+ int fd = open(path.c_str(), O_RDONLY);
+ if (fd <= 0)
+ {
+ return; // we're no longer valid
+ }
+ input_dev.assign(fd);
+ wait_timer.expires_from_now(
+ boost::posix_time::milliseconds(SENSOR_POLL_MS));
+ wait_timer.async_wait([&](const boost::system::error_code &ec) {
+ if (ec == boost::asio::error::operation_aborted)
+ {
+ return; // we're being canceled
+ }
+ setup_read();
+ });
+}
+
+void ADCSensor::check_thresholds(void)
+{
+ if (thresholds.empty())
+ return;
+ for (auto threshold : thresholds)
+ {
+ if (threshold.direction == thresholds::Direction::HIGH)
+ {
+ if (value > threshold.value)
+ {
+ assert_thresholds(threshold.level, threshold.direction, true);
+ }
+ else
+ {
+ assert_thresholds(threshold.level, threshold.direction, false);
+ }
+ }
+ else
+ {
+ if (value < threshold.value)
+ {
+ assert_thresholds(threshold.level, threshold.direction, true);
+ }
+ else
+ {
+ assert_thresholds(threshold.level, threshold.direction, false);
+ }
+ }
+ }
+}
+
+void ADCSensor::update_value(const double &new_value)
+{
+ bool ret = sensor_interface->set_property("Value", new_value);
+ value = new_value;
+ check_thresholds();
+}
+
+void ADCSensor::assert_thresholds(thresholds::Level level,
+ thresholds::Direction direction, bool assert)
+{
+ std::string property;
+ std::shared_ptr<sdbusplus::asio::dbus_interface> interface;
+ if (level == thresholds::Level::WARNING &&
+ direction == thresholds::Direction::HIGH)
+ {
+ property = "WarningAlarmHigh";
+ interface = threshold_interface_warning;
+ }
+ else if (level == thresholds::Level::WARNING &&
+ direction == thresholds::Direction::LOW)
+ {
+ property = "WarningAlarmLow";
+ interface = threshold_interface_warning;
+ }
+ else if (level == thresholds::Level::CRITICAL &&
+ direction == thresholds::Direction::HIGH)
+ {
+ property = "CriticalAlarmHigh";
+ interface = threshold_interface_critical;
+ }
+ else if (level == thresholds::Level::CRITICAL &&
+ direction == thresholds::Direction::LOW)
+ {
+ property = "CriticalAlarmLow";
+ interface = threshold_interface_critical;
+ }
+ else
+ {
+ std::cerr << "Unknown threshold, level " << level << "direction "
+ << direction << "\n";
+ return;
+ }
+ if (!interface)
+ {
+ std::cout << "trying to set uninitialized interface\n";
+ return;
+ }
+ interface->set_property(property, assert);
+}
+
+void ADCSensor::set_initial_properties(
+ std::shared_ptr<sdbusplus::asio::connection> &conn)
+{
+ // todo, get max and min from configuration
+ sensor_interface->register_property("MaxValue", max_value);
+ sensor_interface->register_property("MinValue", min_value);
+ sensor_interface->register_property("Value", value);
+
+ for (auto &threshold : thresholds)
+ {
+ std::shared_ptr<sdbusplus::asio::dbus_interface> iface;
+ std::string level;
+ std::string alarm;
+ if (threshold.level == thresholds::Level::CRITICAL)
+ {
+ iface = threshold_interface_critical;
+ if (threshold.direction == thresholds::Direction::HIGH)
+ {
+ level = "CriticalHigh";
+ alarm = "CriticalAlarmHigh";
+ }
+ else
+ {
+ level = "CriticalLow";
+ alarm = "CriticalAlarmLow";
+ }
+ }
+ else if (threshold.level == thresholds::Level::WARNING)
+ {
+ iface = threshold_interface_warning;
+ if (threshold.direction == thresholds::Direction::HIGH)
+ {
+ level = "WarningHigh";
+ alarm = "WarningAlarmHigh";
+ }
+ else
+ {
+ level = "WarningLow";
+ alarm = "WarningAlarmLow";
+ }
+ }
+ else
+ {
+ std::cerr << "Unknown threshold level" << threshold.level << "\n";
+ continue;
+ }
+ if (!iface)
+ {
+ std::cout << "trying to set uninitialized interface\n";
+ continue;
+ }
+ iface->register_property(
+ level, threshold.value,
+ [&](const double &request, double &oldValue) {
+ oldValue = request; // todo, just let the config do this?
+ threshold.value = request;
+ thresholds::persistThreshold(
+ configuration, "xyz.openbmc_project.Configuration.ADC",
+ threshold, conn);
+ return 1;
+ });
+ iface->register_property(alarm, false);
+ }
+ if (!sensor_interface->initialize())
+ {
+ std::cerr << "error initializing value interface\n";
+ }
+ if (threshold_interface_warning &&
+ !threshold_interface_warning->initialize())
+ {
+ std::cerr << "error initializing warning threshold interface\n";
+ }
+
+ if (threshold_interface_critical &&
+ !threshold_interface_critical->initialize())
+ {
+ std::cerr << "error initializing critical threshold interface\n";
+ }
+}
diff --git a/sensors/src/ADCSensorMain.cpp b/sensors/src/ADCSensorMain.cpp
new file mode 100644
index 0000000..640a729
--- /dev/null
+++ b/sensors/src/ADCSensorMain.cpp
@@ -0,0 +1,243 @@
+/*
+// Copyright (c) 2017 Intel Corporation
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+*/
+
+#include <ADCSensor.hpp>
+#include <Utils.hpp>
+#include <VariantVisitors.hpp>
+#include <boost/algorithm/string/predicate.hpp>
+#include <boost/algorithm/string/replace.hpp>
+#include <boost/container/flat_set.hpp>
+#include <experimental/filesystem>
+#include <fstream>
+#include <regex>
+#include <sdbusplus/asio/connection.hpp>
+#include <sdbusplus/asio/object_server.hpp>
+
+static constexpr bool DEBUG = false;
+
+namespace fs = std::experimental::filesystem;
+static constexpr std::array<const char*, 1> SENSOR_TYPES = {
+ "xyz.openbmc_project.Configuration.ADC"};
+static std::regex INPUT_REGEX(R"(in(\d+)_input)");
+
+void createSensors(
+ boost::asio::io_service& io, sdbusplus::asio::object_server& objectServer,
+ boost::container::flat_map<std::string, std::unique_ptr<ADCSensor>>&
+ sensors,
+ std::shared_ptr<sdbusplus::asio::connection>& dbusConnection,
+ const std::unique_ptr<boost::container::flat_set<std::string>>&
+ sensorsChanged)
+{
+ bool firstScan = sensorsChanged == nullptr;
+ // use new data the first time, then refresh
+ ManagedObjectType sensorConfigurations;
+ bool useCache = false;
+ for (const char* type : SENSOR_TYPES)
+ {
+ if (!getSensorConfiguration(type, dbusConnection, sensorConfigurations,
+ useCache))
+ {
+ std::cerr << "error communicating to entity manager\n";
+ return;
+ }
+ useCache = true;
+ }
+ std::vector<fs::path> paths;
+ if (!find_files(fs::path("/sys/class/hwmon"), R"(in\d+_input)", paths))
+ {
+ std::cerr << "No temperature sensors in system\n";
+ return;
+ }
+
+ // iterate through all found adc sensors, and try to match them with
+ // configuration
+ for (auto& path : paths)
+ {
+ std::smatch match;
+ std::string pathStr = path.string();
+
+ std::regex_search(pathStr, match, INPUT_REGEX);
+ std::string indexStr = *(match.begin() + 1);
+
+ auto directory = path.parent_path();
+ // convert to 0 based
+ size_t index = std::stoul(indexStr) - 1;
+ auto oem_name_path =
+ directory.string() + R"(/of_node/oemname)" + std::to_string(index);
+
+ if (DEBUG)
+ std::cout << "Checking path " << oem_name_path << "\n";
+ std::ifstream nameFile(oem_name_path);
+ if (!nameFile.good())
+ {
+ std::cerr << "Failure reading " << oem_name_path << "\n";
+ continue;
+ }
+ std::string oemName;
+ std::getline(nameFile, oemName);
+ nameFile.close();
+ if (!oemName.size())
+ {
+ // shouldn't have an empty name file
+ continue;
+ }
+ oemName.pop_back(); // remove trailing null
+
+ const SensorData* sensorData = nullptr;
+ const std::string* interfacePath = nullptr;
+ for (const std::pair<sdbusplus::message::object_path, SensorData>&
+ sensor : sensorConfigurations)
+ {
+ if (!boost::ends_with(sensor.first.str, oemName))
+ {
+ continue;
+ }
+ sensorData = &(sensor.second);
+ interfacePath = &(sensor.first.str);
+ break;
+ }
+ if (sensorData == nullptr)
+ {
+ std::cerr << "failed to find match for " << oemName << "\n";
+ continue;
+ }
+ const std::pair<std::string, boost::container::flat_map<
+ std::string, BasicVariantType>>*
+ baseConfiguration = nullptr;
+ for (const char* type : SENSOR_TYPES)
+ {
+ auto sensorBase = sensorData->find(type);
+ if (sensorBase != sensorData->end())
+ {
+ baseConfiguration = &(*sensorBase);
+ break;
+ }
+ }
+
+ if (baseConfiguration == nullptr)
+ {
+ std::cerr << "error finding base configuration for" << oemName
+ << "\n";
+ continue;
+ }
+
+ auto findSensorName = baseConfiguration->second.find("Name");
+ if (findSensorName == baseConfiguration->second.end())
+ {
+ std::cerr << "could not determine configuration name for "
+ << oemName << "\n";
+ continue;
+ }
+ std::string sensorName =
+ sdbusplus::message::variant_ns::get<std::string>(
+ findSensorName->second);
+
+ // on rescans, only update sensors we were signaled by
+ auto findSensor = sensors.find(sensorName);
+ if (!firstScan && findSensor != sensors.end())
+ {
+ bool found = false;
+ for (auto it = sensorsChanged->begin(); it != sensorsChanged->end();
+ it++)
+ {
+ if (boost::ends_with(*it, findSensor->second->name))
+ {
+ sensorsChanged->erase(it);
+ findSensor->second = nullptr;
+ found = true;
+ break;
+ }
+ }
+ if (!found)
+ {
+ continue;
+ }
+ }
+ std::vector<thresholds::Threshold> sensorThresholds;
+ if (!ParseThresholdsFromConfig(*sensorData, sensorThresholds))
+ {
+ std::cerr << "error populating thresholds for " << sensorName
+ << "\n";
+ }
+
+ auto findScaleFactor = baseConfiguration->second.find("scale_factor");
+ float scaleFactor = 1.0;
+ if (findScaleFactor != baseConfiguration->second.end())
+ {
+ scaleFactor = mapbox::util::apply_visitor(VariantToFloatVisitor(),
+ findScaleFactor->second);
+ }
+ sensors[sensorName] = std::make_unique<ADCSensor>(
+ path.string(), objectServer, dbusConnection, io, sensorName,
+ std::move(sensorThresholds), scaleFactor, *interfacePath);
+ }
+}
+
+int main(int argc, char** argv)
+{
+ boost::asio::io_service io;
+ auto systemBus = std::make_shared<sdbusplus::asio::connection>(io);
+ systemBus->request_name("xyz.openbmc_project.ADCSensor");
+ sdbusplus::asio::object_server objectServer(systemBus);
+ boost::container::flat_map<std::string, std::unique_ptr<ADCSensor>> sensors;
+ std::vector<std::unique_ptr<sdbusplus::bus::match::match>> matches;
+ std::unique_ptr<boost::container::flat_set<std::string>> sensorsChanged =
+ std::make_unique<boost::container::flat_set<std::string>>();
+
+ io.post([&]() {
+ createSensors(io, objectServer, sensors, systemBus, nullptr);
+ });
+
+ boost::asio::deadline_timer filterTimer(io);
+ std::function<void(sdbusplus::message::message&)> eventHandler =
+ [&](sdbusplus::message::message& message) {
+ if (message.is_method_error())
+ {
+ std::cerr << "callback method error\n";
+ return;
+ }
+ sensorsChanged->insert(message.get_path());
+ // this implicitly cancels the timer
+ filterTimer.expires_from_now(boost::posix_time::seconds(1));
+
+ filterTimer.async_wait([&](const boost::system::error_code& ec) {
+ if (ec == boost::asio::error::operation_aborted)
+ {
+ /* we were canceled*/
+ return;
+ }
+ else if (ec)
+ {
+ std::cerr << "timer error\n";
+ return;
+ }
+ createSensors(io, objectServer, sensors, systemBus,
+ sensorsChanged);
+ });
+ };
+
+ for (const char* type : SENSOR_TYPES)
+ {
+ auto match = std::make_unique<sdbusplus::bus::match::match>(
+ static_cast<sdbusplus::bus::bus&>(*systemBus),
+ "type='signal',member='PropertiesChanged',path_namespace='" +
+ std::string(INVENTORY_PATH) + "',arg0namespace='" + type + "'",
+ eventHandler);
+ matches.emplace_back(std::move(match));
+ }
+
+ io.run();
+}
diff --git a/sensors/src/CPUSensor.cpp b/sensors/src/CPUSensor.cpp
new file mode 100644
index 0000000..66bca1f
--- /dev/null
+++ b/sensors/src/CPUSensor.cpp
@@ -0,0 +1,320 @@
+/*
+// Copyright (c) 2018 Intel Corporation
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+*/
+
+#include <unistd.h>
+
+#include <CPUSensor.hpp>
+#include <Utils.hpp>
+#include <boost/algorithm/string/predicate.hpp>
+#include <boost/algorithm/string/replace.hpp>
+#include <boost/date_time/posix_time/posix_time.hpp>
+#include <iostream>
+#include <limits>
+#include <sdbusplus/asio/connection.hpp>
+#include <sdbusplus/asio/object_server.hpp>
+#include <string>
+
+static constexpr size_t WARN_AFTER_ERROR_COUNT = 10;
+
+CPUSensor::CPUSensor(const std::string &path, const std::string &objectType,
+ sdbusplus::asio::object_server &objectServer,
+ std::shared_ptr<sdbusplus::asio::connection> &conn,
+ boost::asio::io_service &io, const std::string &sensorName,
+ std::vector<thresholds::Threshold> &&_thresholds,
+ const std::string &sensorConfiguration) :
+ path(path),
+ objectType(objectType), objServer(objectServer),
+ name(boost::replace_all_copy(sensorName, " ", "_")), dbusConnection(conn),
+ thresholds(std::move(_thresholds)), configuration(sensorConfiguration),
+ sensor_interface(objectServer.add_interface(
+ "/xyz/openbmc_project/sensors/temperature/" + name,
+ "xyz.openbmc_project.Sensor.Value")),
+ input_dev(io, open(path.c_str(), O_RDONLY)), wait_timer(io),
+ value(std::numeric_limits<double>::quiet_NaN()), err_count(0),
+ // todo, get these from config
+ max_value(127), min_value(-128)
+{
+ if (thresholds::HasWarningInterface(thresholds))
+ {
+ threshold_interface_warning = objectServer.add_interface(
+ "/xyz/openbmc_project/sensors/temperature/" + name,
+ "xyz.openbmc_project.Sensor.Threshold.Warning");
+ }
+ if (thresholds::HasCriticalInterface(thresholds))
+ {
+ threshold_interface_critical = objectServer.add_interface(
+ "/xyz/openbmc_project/sensors/temperature/" + name,
+ "xyz.openbmc_project.Sensor.Threshold.Critical");
+ }
+ set_initial_properties(conn);
+ isPowerOn(dbusConnection); // first call initializes
+ setup_read();
+}
+
+CPUSensor::~CPUSensor()
+{
+ // close the input dev to cancel async operations
+ input_dev.close();
+ wait_timer.cancel();
+ objServer.remove_interface(threshold_interface_warning);
+ objServer.remove_interface(threshold_interface_critical);
+ objServer.remove_interface(sensor_interface);
+}
+
+void CPUSensor::setup_read(void)
+{
+ boost::asio::async_read_until(
+ input_dev, read_buf, '\n',
+ [&](const boost::system::error_code &ec,
+ std::size_t /*bytes_transfered*/) { handle_response(ec); });
+}
+
+void CPUSensor::handle_response(const boost::system::error_code &err)
+{
+ if (err == boost::system::errc::bad_file_descriptor)
+ {
+ return; // we're being destroyed
+ }
+ std::istream response_stream(&read_buf);
+ if (!err)
+ {
+ std::string response;
+ try
+ {
+ std::getline(response_stream, response);
+ float nvalue = std::stof(response);
+ response_stream.clear();
+ nvalue /= CPUSensor::SENSOR_SCALE_FACTOR;
+ if (nvalue != value)
+ {
+ update_value(nvalue);
+ }
+ err_count = 0;
+ }
+ catch (const std::invalid_argument &)
+ {
+ err_count++;
+ }
+ }
+ else
+ {
+ err_count++;
+ }
+
+ // only send value update once
+ if (err_count == WARN_AFTER_ERROR_COUNT)
+ {
+ // only an error if power is on
+ if (isPowerOn(dbusConnection))
+ {
+ std::cerr << "Failure to read sensor " << name << " at " << path
+ << "\n";
+ update_value(0);
+ err_count++;
+ }
+ else
+ {
+ err_count = 0; // check power again in 10 cycles
+ sensor_interface->set_property(
+ "Value", std::numeric_limits<double>::quiet_NaN());
+ }
+ }
+
+ response_stream.clear();
+ input_dev.close();
+ int fd = open(path.c_str(), O_RDONLY);
+ if (fd <= 0)
+ {
+ return; // we're no longer valid
+ }
+ input_dev.assign(fd);
+ wait_timer.expires_from_now(
+ boost::posix_time::milliseconds(CPUSensor::SENSOR_POLL_MS));
+ wait_timer.async_wait([&](const boost::system::error_code &ec) {
+ if (ec == boost::asio::error::operation_aborted)
+ {
+ return; // we're being canceled
+ }
+ setup_read();
+ });
+}
+
+void CPUSensor::check_thresholds(void)
+{
+ if (thresholds.empty())
+ return;
+ for (auto threshold : thresholds)
+ {
+ if (threshold.direction == thresholds::Direction::HIGH)
+ {
+ if (value > threshold.value)
+ {
+ assert_thresholds(threshold.level, threshold.direction, true);
+ }
+ else
+ {
+ assert_thresholds(threshold.level, threshold.direction, false);
+ }
+ }
+ else
+ {
+ if (value < threshold.value)
+ {
+ assert_thresholds(threshold.level, threshold.direction, true);
+ }
+ else
+ {
+ assert_thresholds(threshold.level, threshold.direction, false);
+ }
+ }
+ }
+}
+
+void CPUSensor::update_value(const double &new_value)
+{
+ sensor_interface->set_property("Value", new_value);
+ value = new_value;
+ check_thresholds();
+}
+
+void CPUSensor::assert_thresholds(thresholds::Level level,
+ thresholds::Direction direction, bool assert)
+{
+ std::string property;
+ std::shared_ptr<sdbusplus::asio::dbus_interface> interface;
+ if (level == thresholds::Level::WARNING &&
+ direction == thresholds::Direction::HIGH)
+ {
+ property = "WarningAlarmHigh";
+ interface = threshold_interface_warning;
+ }
+ else if (level == thresholds::Level::WARNING &&
+ direction == thresholds::Direction::LOW)
+ {
+ property = "WarningAlarmLow";
+ interface = threshold_interface_warning;
+ }
+ else if (level == thresholds::Level::CRITICAL &&
+ direction == thresholds::Direction::HIGH)
+ {
+ property = "CriticalAlarmHigh";
+ interface = threshold_interface_critical;
+ }
+ else if (level == thresholds::Level::CRITICAL &&
+ direction == thresholds::Direction::LOW)
+ {
+ property = "CriticalAlarmLow";
+ interface = threshold_interface_critical;
+ }
+ else
+ {
+ std::cerr << "Unknown threshold, level " << level << "direction "
+ << direction << "\n";
+ return;
+ }
+ if (!interface)
+ {
+ std::cout << "trying to set uninitialized interface\n";
+ return;
+ }
+ interface->set_property(property, assert);
+}
+
+void CPUSensor::set_initial_properties(
+ std::shared_ptr<sdbusplus::asio::connection> &conn)
+{
+ // todo, get max and min from configuration
+ sensor_interface->register_property("MaxValue", max_value);
+ sensor_interface->register_property("MinValue", min_value);
+ sensor_interface->register_property("Value", value);
+
+ for (auto &threshold : thresholds)
+ {
+ std::shared_ptr<sdbusplus::asio::dbus_interface> iface;
+ std::string level;
+ std::string alarm;
+ if (threshold.level == thresholds::Level::CRITICAL)
+ {
+ iface = threshold_interface_critical;
+ if (threshold.direction == thresholds::Direction::HIGH)
+ {
+ level = "CriticalHigh";
+ alarm = "CriticalAlarmHigh";
+ }
+ else
+ {
+ level = "CriticalLow";
+ alarm = "CriticalAlarmLow";
+ }
+ }
+ else if (threshold.level == thresholds::Level::WARNING)
+ {
+ iface = threshold_interface_warning;
+ if (threshold.direction == thresholds::Direction::HIGH)
+ {
+ level = "WarningHigh";
+ alarm = "WarningAlarmHigh";
+ }
+ else
+ {
+ level = "WarningLow";
+ alarm = "WarningAlarmLow";
+ }
+ }
+ else
+ {
+ std::cerr << "Unknown threshold level" << threshold.level << "\n";
+ continue;
+ }
+ if (!iface)
+ {
+ std::cout << "trying to set uninitialized interface\n";
+ continue;
+ }
+ if (threshold.writeable)
+ {
+ iface->register_property(
+ level, threshold.value,
+ [&](const double &request, double &oldValue) {
+ oldValue = request; // todo, just let the config do this?
+ threshold.value = request;
+ thresholds::persistThreshold(configuration, objectType,
+ threshold, conn);
+ return 1;
+ });
+ }
+ else
+ {
+ iface->register_property(level, threshold.value);
+ }
+ iface->register_property(alarm, false);
+ }
+ if (!sensor_interface->initialize())
+ {
+ std::cerr << "error initializing value interface\n";
+ }
+ if (threshold_interface_warning &&
+ !threshold_interface_warning->initialize())
+ {
+ std::cerr << "error initializing warning threshold interface\n";
+ }
+
+ if (threshold_interface_critical &&
+ !threshold_interface_critical->initialize())
+ {
+ std::cerr << "error initializing critical threshold interface\n";
+ }
+}
diff --git a/sensors/src/CPUSensorMain.cpp b/sensors/src/CPUSensorMain.cpp
new file mode 100644
index 0000000..d6d68ad
--- /dev/null
+++ b/sensors/src/CPUSensorMain.cpp
@@ -0,0 +1,510 @@
+/*
+// Copyright (c) 2018 Intel Corporation
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+*/
+
+#include <fcntl.h>
+#include <linux/peci-ioctl.h>
+
+#include <CPUSensor.hpp>
+#include <Utils.hpp>
+#include <VariantVisitors.hpp>
+#include <boost/algorithm/string/predicate.hpp>
+#include <boost/algorithm/string/replace.hpp>
+#include <boost/container/flat_set.hpp>
+#include <boost/date_time/posix_time/posix_time.hpp>
+#include <boost/process/child.hpp>
+#include <experimental/filesystem>
+#include <fstream>
+#include <regex>
+#include <sdbusplus/asio/connection.hpp>
+#include <sdbusplus/asio/object_server.hpp>
+
+static constexpr bool DEBUG = false;
+
+enum State
+{
+ OFF, // host powered down
+ ON, // host powered on
+ READY // host powered on and mem test passed - fully ready
+};
+
+struct CPUConfig
+{
+ CPUConfig(const int& address, const std::string& overlayName,
+ const State& st) :
+ addr(address),
+ ovName(overlayName), state(st)
+ {
+ }
+ int addr;
+ std::string ovName;
+ State state;
+
+ bool operator<(const CPUConfig& rhs) const
+ {
+ return (ovName < rhs.ovName);
+ }
+};
+
+static constexpr const char* DT_OVERLAY = "/usr/bin/dtoverlay";
+static constexpr const char* OVERLAY_DIR = "/tmp/overlays";
+static constexpr const char* PECI_DEV = "/dev/peci0";
+static constexpr const unsigned int RANK_NUM_MAX = 8;
+
+namespace fs = std::experimental::filesystem;
+static constexpr const char* CONFIG_PREFIX =
+ "xyz.openbmc_project.Configuration.";
+static constexpr std::array<const char*, 3> SENSOR_TYPES = {
+ "SkylakeCPU", "BroadwellCPU", "HaswellCPU"};
+
+const static std::regex ILLEGAL_NAME_REGEX("[^A-Za-z0-9_]");
+
+void createSensors(
+ boost::asio::io_service& io, sdbusplus::asio::object_server& objectServer,
+ boost::container::flat_map<std::string, std::unique_ptr<CPUSensor>>&
+ sensors,
+ boost::container::flat_set<CPUConfig>& configs,
+ std::shared_ptr<sdbusplus::asio::connection>& dbusConnection)
+{
+ bool available = false;
+ for (CPUConfig cpu : configs)
+ {
+ if (cpu.state != State::OFF)
+ {
+ available = true;
+ break;
+ }
+ }
+ if (!available)
+ {
+ return;
+ }
+
+ // use new data the first time, then refresh
+ ManagedObjectType sensorConfigurations;
+ bool useCache = false;
+ for (const char* type : SENSOR_TYPES)
+ {
+ if (!getSensorConfiguration(CONFIG_PREFIX + std::string(type),
+ dbusConnection, sensorConfigurations,
+ useCache))
+ {
+ std::cerr << "error communicating to entity manager\n";
+ return;
+ }
+ useCache = true;
+ }
+
+ std::vector<fs::path> oemNamePaths;
+ if (!find_files(fs::path(R"(/sys/bus/peci/devices)"),
+ R"(peci\d+/\d+-.+/of_node/oemname1$)", oemNamePaths, 2))
+ {
+ std::cerr << "No CPU sensors in system\n";
+ return;
+ }
+
+ for (fs::path& oemNamePath : oemNamePaths)
+ {
+ std::ifstream nameFile(oemNamePath);
+ if (!nameFile.good())
+ {
+ std::cerr << "Failure reading " << oemNamePath << "\n";
+ continue;
+ }
+ std::string oemName;
+ std::getline(nameFile, oemName);
+ nameFile.close();
+ if (!oemName.size())
+ {
+ // shouldn't have an empty name file
+ continue;
+ }
+ oemName.pop_back(); // remove trailing null
+ if (DEBUG)
+ std::cout << "Checking: " << oemNamePath << ": " << oemName << "\n";
+
+ const SensorData* sensorData = nullptr;
+ const std::string* interfacePath = nullptr;
+ for (const std::pair<sdbusplus::message::object_path, SensorData>&
+ sensor : sensorConfigurations)
+ {
+ if (!boost::ends_with(sensor.first.str, oemName))
+ {
+ continue;
+ }
+ sensorData = &(sensor.second);
+ interfacePath = &(sensor.first.str);
+ break;
+ }
+ if (sensorData == nullptr)
+ {
+ std::cerr << "failed to find match for " << oemName << "\n";
+ continue;
+ }
+ const std::pair<std::string, boost::container::flat_map<
+ std::string, BasicVariantType>>*
+ baseConfiguration = nullptr;
+ std::string sensorObjectType;
+ for (const char* type : SENSOR_TYPES)
+ {
+ sensorObjectType = CONFIG_PREFIX + std::string(type);
+ auto sensorBase = sensorData->find(sensorObjectType);
+ if (sensorBase != sensorData->end())
+ {
+ baseConfiguration = &(*sensorBase);
+ break;
+ }
+ }
+
+ if (baseConfiguration == nullptr)
+ {
+ std::cerr << "error finding base configuration for" << oemName
+ << "\n";
+ continue;
+ }
+
+ auto findCpuId = baseConfiguration->second.find("CpuID");
+ if (findCpuId == baseConfiguration->second.end())
+ {
+ std::cerr << "could not determine CPU ID for " << oemName << "\n";
+ continue;
+ }
+ int cpuId = mapbox::util::apply_visitor(VariantToIntVisitor(),
+ findCpuId->second);
+
+ auto directory = oemNamePath.parent_path().parent_path();
+ std::vector<fs::path> inputPaths;
+ if (!find_files(fs::path(directory),
+ R"(peci-.+/hwmon/hwmon\d+/temp\d+_input$)", inputPaths,
+ 0))
+ {
+ std::cerr << "No temperature sensors in system\n";
+ continue;
+ }
+
+ // iterate through all found temp sensors
+ for (auto& inputPath : inputPaths)
+ {
+ auto inputPathStr = inputPath.string();
+ auto labelPath =
+ boost::replace_all_copy(inputPathStr, "input", "label");
+ std::ifstream labelFile(labelPath);
+ if (!labelFile.good())
+ {
+ std::cerr << "Failure reading " << labelPath << "\n";
+ continue;
+ }
+ std::string label;
+ std::getline(labelFile, label);
+ labelFile.close();
+ std::string sensorName = label + " CPU" + std::to_string(cpuId);
+ std::vector<thresholds::Threshold> sensorThresholds;
+ std::string labelHead = label.substr(0, label.find(" "));
+ if (!ParseThresholdsFromConfig(*sensorData, sensorThresholds,
+ &labelHead))
+ {
+ continue;
+ }
+ if (!sensorThresholds.size())
+ {
+ if (!ParseThresholdsFromAttr(sensorThresholds, inputPathStr,
+ CPUSensor::SENSOR_SCALE_FACTOR))
+ {
+ continue;
+ }
+ }
+ sensors[sensorName] = std::make_unique<CPUSensor>(
+ inputPathStr, sensorObjectType, objectServer, dbusConnection,
+ io, sensorName, std::move(sensorThresholds), *interfacePath);
+ if (DEBUG)
+ std::cout << "Mapped: " << inputPath << " to " << sensorName
+ << "\n";
+ }
+ }
+}
+
+void reloadOverlay(const std::string& overlay)
+{
+ boost::process::child c1(DT_OVERLAY, "-d", OVERLAY_DIR, "-r", overlay);
+ c1.wait();
+ if (c1.exit_code())
+ {
+ if (DEBUG)
+ {
+ std::cout << "DTOverlay unload error with file " << overlay
+ << ". error: " << c1.exit_code() << "\n";
+ }
+
+ /* fall through anyway */
+ }
+
+ boost::process::child c2(DT_OVERLAY, "-d", OVERLAY_DIR, overlay);
+ c2.wait();
+ if (c2.exit_code())
+ {
+ std::cerr << "DTOverlay load error with file " << overlay
+ << ". error: " << c2.exit_code() << "\n";
+ return;
+ }
+}
+
+void detectCpu(boost::asio::deadline_timer& timer, boost::asio::io_service& io,
+ sdbusplus::asio::object_server& objectServer,
+ boost::container::flat_map<std::string,
+ std::unique_ptr<CPUSensor>>& sensors,
+ boost::container::flat_set<CPUConfig>& configs,
+ std::shared_ptr<sdbusplus::asio::connection>& dbusConnection)
+{
+ auto file = open(PECI_DEV, O_RDWR);
+ if (file < 0)
+ {
+ std::cerr << "unable to open " << PECI_DEV << "\n";
+ std::exit(EXIT_FAILURE);
+ }
+
+ size_t rescanDelaySeconds = 0;
+ bool keepPinging = false;
+ for (CPUConfig& config : configs)
+ {
+ State state;
+ struct peci_ping_msg msg;
+ msg.addr = config.addr;
+ if (!ioctl(file, PECI_IOC_PING, &msg))
+ {
+ bool dimmReady = false;
+ for (unsigned int rank = 0; rank < RANK_NUM_MAX; rank++)
+ {
+ struct peci_rd_pkg_cfg_msg msg;
+ msg.addr = config.addr;
+ msg.index = MBX_INDEX_DDR_DIMM_TEMP;
+ msg.param = rank;
+ msg.rx_len = 4;
+ if (!ioctl(file, PECI_IOC_RD_PKG_CFG, &msg))
+ {
+ if (msg.pkg_config[0] || msg.pkg_config[1] ||
+ msg.pkg_config[2])
+ {
+ dimmReady = true;
+ break;
+ }
+ }
+ else
+ {
+ break;
+ }
+ }
+ if (dimmReady)
+ {
+ state = State::READY;
+ }
+ else
+ {
+ state = State::ON;
+ }
+ }
+ else
+ {
+ state = State::OFF;
+ }
+
+ if (config.state != state)
+ {
+ if (config.state == State::OFF)
+ {
+ reloadOverlay(config.ovName);
+ }
+ if (state != State::OFF)
+ {
+ if (state == State::ON)
+ {
+ rescanDelaySeconds = 1;
+ }
+ else
+ {
+ rescanDelaySeconds = 5;
+ }
+ }
+ config.state = state;
+ }
+
+ if (state != State::READY)
+ {
+ keepPinging = true;
+ }
+
+ if (DEBUG)
+ std::cout << config.ovName << ", state: " << state << "\n";
+ }
+
+ close(file);
+
+ if (rescanDelaySeconds)
+ {
+ std::this_thread::sleep_for(std::chrono::seconds(rescanDelaySeconds));
+ createSensors(io, objectServer, sensors, configs, dbusConnection);
+ }
+
+ if (keepPinging)
+ {
+ timer.expires_from_now(boost::posix_time::seconds(1));
+ timer.async_wait([&](const boost::system::error_code& ec) {
+ if (ec == boost::asio::error::operation_aborted)
+ {
+ /* we were canceled*/
+ return;
+ }
+ else if (ec)
+ {
+ std::cerr << "timer error\n";
+ return;
+ }
+ detectCpu(timer, io, objectServer, sensors, configs,
+ dbusConnection);
+ });
+ }
+}
+
+void getCpuConfig(const std::shared_ptr<sdbusplus::asio::connection>& systemBus,
+ boost::container::flat_set<CPUConfig>& configs)
+{
+ ManagedObjectType sensorConfigurations;
+ bool useCache = false;
+ // use new data the first time, then refresh
+ for (const char* type : SENSOR_TYPES)
+ {
+ if (!getSensorConfiguration(CONFIG_PREFIX + std::string(type),
+ systemBus, sensorConfigurations, useCache))
+ {
+ std::cerr
+ << "getCpuConfig: error communicating to entity manager\n";
+ return;
+ }
+ useCache = true;
+ }
+
+ // check PECI client addresses and DT overlay names from CPU configuration
+ // before starting ping operation
+ for (const char* type : SENSOR_TYPES)
+ {
+ for (const std::pair<sdbusplus::message::object_path, SensorData>&
+ sensor : sensorConfigurations)
+ {
+ for (const std::pair<
+ std::string,
+ boost::container::flat_map<std::string, BasicVariantType>>&
+ config : sensor.second)
+ {
+ if ((CONFIG_PREFIX + std::string(type)) != config.first)
+ {
+ continue;
+ }
+
+ auto findAddress = config.second.find("Address");
+ if (findAddress == config.second.end())
+ {
+ continue;
+ }
+ std::string addrStr = mapbox::util::apply_visitor(
+ VariantToStringVisitor(), findAddress->second);
+ int addr = std::stoi(addrStr, 0, 16);
+
+ auto findName = config.second.find("Name");
+ if (findName == config.second.end())
+ {
+ continue;
+ }
+ std::string nameRaw = mapbox::util::apply_visitor(
+ VariantToStringVisitor(), findName->second);
+ std::string name =
+ std::regex_replace(nameRaw, ILLEGAL_NAME_REGEX, "_");
+ std::string overlayName = name + "_" + type;
+
+ if (DEBUG)
+ {
+ std::cout << "addr: " << addr << "\n";
+ std::cout << "name: " << name << "\n";
+ std::cout << "type: " << type << "\n";
+ std::cout << "overlayName: " << overlayName << "\n";
+ }
+
+ configs.emplace(addr, overlayName, State::OFF);
+ }
+ }
+ }
+}
+
+int main(int argc, char** argv)
+{
+ boost::asio::io_service io;
+ auto systemBus = std::make_shared<sdbusplus::asio::connection>(io);
+ boost::container::flat_set<CPUConfig> configs;
+
+ systemBus->request_name("xyz.openbmc_project.CPUSensor");
+ sdbusplus::asio::object_server objectServer(systemBus);
+ boost::container::flat_map<std::string, std::unique_ptr<CPUSensor>> sensors;
+ std::vector<std::unique_ptr<sdbusplus::bus::match::match>> matches;
+ boost::asio::deadline_timer pingTimer(io);
+ getCpuConfig(systemBus, configs);
+ if (configs.size())
+ {
+ detectCpu(pingTimer, io, objectServer, sensors, configs, systemBus);
+ }
+
+ boost::asio::deadline_timer filterTimer(io);
+ std::function<void(sdbusplus::message::message&)> eventHandler =
+ [&](sdbusplus::message::message& message) {
+ if (message.is_method_error())
+ {
+ std::cerr << "callback method error\n";
+ return;
+ }
+ // this implicitly cancels the timer
+ filterTimer.expires_from_now(boost::posix_time::seconds(1));
+
+ filterTimer.async_wait([&](const boost::system::error_code& ec) {
+ if (ec == boost::asio::error::operation_aborted)
+ {
+ /* we were canceled*/
+ return;
+ }
+ else if (ec)
+ {
+ std::cerr << "timer error\n";
+ return;
+ }
+
+ getCpuConfig(systemBus, configs);
+
+ if (configs.size())
+ {
+ detectCpu(pingTimer, io, objectServer, sensors, configs,
+ systemBus);
+ }
+ });
+ };
+
+ for (const char* type : SENSOR_TYPES)
+ {
+ auto match = std::make_unique<sdbusplus::bus::match::match>(
+ static_cast<sdbusplus::bus::bus&>(*systemBus),
+ "type='signal',member='PropertiesChanged',path_namespace='" +
+ std::string(INVENTORY_PATH) + "',arg0namespace='" +
+ CONFIG_PREFIX + type + "'",
+ eventHandler);
+ matches.emplace_back(std::move(match));
+ }
+
+ io.run();
+}
diff --git a/sensors/src/FanMain.cpp b/sensors/src/FanMain.cpp
new file mode 100644
index 0000000..6818692
--- /dev/null
+++ b/sensors/src/FanMain.cpp
@@ -0,0 +1,290 @@
+/*
+// Copyright (c) 2017 Intel Corporation
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+*/
+
+#include <PwmSensor.hpp>
+#include <TachSensor.hpp>
+#include <Utils.hpp>
+#include <VariantVisitors.hpp>
+#include <boost/algorithm/string/predicate.hpp>
+#include <boost/algorithm/string/replace.hpp>
+#include <boost/container/flat_set.hpp>
+#include <boost/lexical_cast.hpp>
+#include <experimental/filesystem>
+#include <fstream>
+#include <regex>
+#include <sdbusplus/asio/connection.hpp>
+#include <sdbusplus/asio/object_server.hpp>
+
+static constexpr bool DEBUG = false;
+
+namespace fs = std::experimental::filesystem;
+static constexpr std::array<const char*, 1> SENSOR_TYPES = {
+ "xyz.openbmc_project.Configuration.AspeedFan"};
+static std::regex INPUT_REGEX(R"(fan(\d+)_input)");
+
+void createSensors(
+ boost::asio::io_service& io, sdbusplus::asio::object_server& objectServer,
+ boost::container::flat_map<std::string, std::unique_ptr<TachSensor>>&
+ tachSensors,
+ boost::container::flat_map<std::string, std::unique_ptr<PwmSensor>>&
+ pwmSensors,
+ std::shared_ptr<sdbusplus::asio::connection>& dbusConnection,
+ const std::unique_ptr<boost::container::flat_set<std::string>>&
+ sensorsChanged)
+{
+ bool firstScan = sensorsChanged == nullptr;
+ // use new data the first time, then refresh
+ ManagedObjectType sensorConfigurations;
+ bool useCache = false;
+ for (const char* type : SENSOR_TYPES)
+ {
+ if (!getSensorConfiguration(type, dbusConnection, sensorConfigurations,
+ useCache))
+ {
+ std::cerr << "error communicating to entity manager\n";
+ return;
+ }
+ useCache = true;
+ }
+ std::vector<fs::path> paths;
+ if (!find_files(fs::path("/sys/class/hwmon"), R"(fan\d+_input)", paths))
+ {
+ std::cerr << "No temperature sensors in system\n";
+ return;
+ }
+
+ // iterate through all found fan sensors, and try to match them with
+ // configuration
+ for (auto& path : paths)
+ {
+ std::smatch match;
+ std::string pathStr = path.string();
+
+ std::regex_search(pathStr, match, INPUT_REGEX);
+ std::string indexStr = *(match.begin() + 1);
+
+ auto directory = path.parent_path();
+ // convert to 0 based
+ size_t index = std::stoul(indexStr) - 1;
+
+ const char* baseType;
+ const SensorData* sensorData = nullptr;
+ const std::string* interfacePath = nullptr;
+ const std::pair<std::string, boost::container::flat_map<
+ std::string, BasicVariantType>>*
+ baseConfiguration = nullptr;
+ for (const std::pair<sdbusplus::message::object_path, SensorData>&
+ sensor : sensorConfigurations)
+ {
+ // find the base of the configuration to see if indexes match
+ for (const char* type : SENSOR_TYPES)
+ {
+ auto sensorBaseFind = sensor.second.find(type);
+ if (sensorBaseFind != sensor.second.end())
+ {
+ baseConfiguration = &(*sensorBaseFind);
+ interfacePath = &(sensor.first.str);
+ baseType = type;
+ break;
+ }
+ }
+ if (baseConfiguration == nullptr)
+ {
+ continue;
+ }
+ auto connector =
+ sensor.second.find(baseType + std::string(".Connector"));
+ if (connector == sensor.second.end())
+ {
+ std::cerr << baseConfiguration->first << " missing connector\n";
+ continue;
+ }
+ auto findPwmIndex = connector->second.find("Pwm");
+ if (findPwmIndex == connector->second.end())
+ {
+ continue;
+ }
+ uint16_t pwmIndex = mapbox::util::apply_visitor(
+ VariantToUnsignedIntVisitor(), findPwmIndex->second);
+ auto oemNamePath = directory.string() + R"(/of_node/oemname)" +
+ std::to_string(pwmIndex);
+
+ if (DEBUG)
+ std::cout << "Checking path " << oemNamePath << "\n";
+ std::ifstream nameFile(oemNamePath);
+ if (!nameFile.good())
+ {
+ continue;
+ }
+ std::string oemName;
+ std::getline(nameFile, oemName);
+ nameFile.close();
+ if (!oemName.size())
+ {
+ // shouldn't have an empty name file
+ continue;
+ }
+ oemName.pop_back(); // remove trailing null
+ auto findIndex = baseConfiguration->second.find("Index");
+ if (findIndex == baseConfiguration->second.end())
+ {
+ std::cerr << baseConfiguration->first << " missing index\n";
+ continue;
+ }
+ unsigned int configIndex = mapbox::util::apply_visitor(
+ VariantToUnsignedIntVisitor(), findIndex->second);
+
+ if (configIndex != index)
+ {
+ continue;
+ }
+ // now that the indexes match, verify the connector
+ auto findConnectorName = connector->second.find("Name");
+ if (findConnectorName == connector->second.end())
+ {
+ continue;
+ }
+ std::string connectorName = mapbox::util::apply_visitor(
+ VariantToStringVisitor(), findConnectorName->second);
+ boost::replace_all(connectorName, " ", "_");
+ if (connectorName == oemName)
+ {
+ sensorData = &(sensor.second);
+ break;
+ }
+ }
+ if (sensorData == nullptr)
+ {
+ std::cerr << "failed to find match for " << path.string() << "\n";
+ continue;
+ }
+
+ auto findSensorName = baseConfiguration->second.find("Name");
+ if (findSensorName == baseConfiguration->second.end())
+ {
+ std::cerr << "could not determine configuration name for "
+ << path.string() << "\n";
+ continue;
+ }
+ std::string sensorName =
+ sdbusplus::message::variant_ns::get<std::string>(
+ findSensorName->second);
+ // on rescans, only update sensors we were signaled by
+ auto findSensor = tachSensors.find(sensorName);
+ if (!firstScan && findSensor != tachSensors.end())
+ {
+ bool found = false;
+ for (auto it = sensorsChanged->begin(); it != sensorsChanged->end();
+ it++)
+ {
+ if (boost::ends_with(*it, findSensor->second->name))
+ {
+ sensorsChanged->erase(it);
+ findSensor->second = nullptr;
+ found = true;
+ break;
+ }
+ }
+ if (!found)
+ {
+ continue;
+ }
+ }
+ std::vector<thresholds::Threshold> sensorThresholds;
+ if (!ParseThresholdsFromConfig(*sensorData, sensorThresholds))
+ {
+ std::cerr << "error populating thresholds for " << sensorName
+ << "\n";
+ }
+
+ tachSensors[sensorName] = std::make_unique<TachSensor>(
+ path.string(), objectServer, dbusConnection, io, sensorName,
+ std::move(sensorThresholds), *interfacePath);
+ }
+ std::vector<fs::path> pwms;
+ if (!find_files(fs::path("/sys/class/hwmon"), R"(pwm\d+)", pwms))
+ {
+ std::cerr << "No pwm in system\n";
+ return;
+ }
+ for (const fs::path& pwm : pwms)
+ {
+ // only add new elements
+ pwmSensors.insert(std::pair<std::string, std::unique_ptr<PwmSensor>>(
+ pwm.string(),
+ std::make_unique<PwmSensor>(pwm.string(), objectServer)));
+ }
+}
+
+int main(int argc, char** argv)
+{
+ boost::asio::io_service io;
+ auto systemBus = std::make_shared<sdbusplus::asio::connection>(io);
+ systemBus->request_name("xyz.openbmc_project.FanSensor");
+ sdbusplus::asio::object_server objectServer(systemBus);
+ boost::container::flat_map<std::string, std::unique_ptr<TachSensor>>
+ tachSensors;
+ boost::container::flat_map<std::string, std::unique_ptr<PwmSensor>>
+ pwmSensors;
+ std::vector<std::unique_ptr<sdbusplus::bus::match::match>> matches;
+ std::unique_ptr<boost::container::flat_set<std::string>> sensorsChanged =
+ std::make_unique<boost::container::flat_set<std::string>>();
+
+ io.post([&]() {
+ createSensors(io, objectServer, tachSensors, pwmSensors, systemBus,
+ nullptr);
+ });
+
+ boost::asio::deadline_timer filterTimer(io);
+ std::function<void(sdbusplus::message::message&)> eventHandler =
+ [&](sdbusplus::message::message& message) {
+ if (message.is_method_error())
+ {
+ std::cerr << "callback method error\n";
+ return;
+ }
+ sensorsChanged->insert(message.get_path());
+ // this implicitly cancels the timer
+ filterTimer.expires_from_now(boost::posix_time::seconds(1));
+
+ filterTimer.async_wait([&](const boost::system::error_code& ec) {
+ if (ec == boost::asio::error::operation_aborted)
+ {
+ /* we were canceled*/
+ return;
+ }
+ else if (ec)
+ {
+ std::cerr << "timer error\n";
+ return;
+ }
+ createSensors(io, objectServer, tachSensors, pwmSensors,
+ systemBus, sensorsChanged);
+ });
+ };
+
+ for (const char* type : SENSOR_TYPES)
+ {
+ auto match = std::make_unique<sdbusplus::bus::match::match>(
+ static_cast<sdbusplus::bus::bus&>(*systemBus),
+ "type='signal',member='PropertiesChanged',path_namespace='" +
+ std::string(INVENTORY_PATH) + "',arg0namespace='" + type + "'",
+ eventHandler);
+ matches.emplace_back(std::move(match));
+ }
+
+ io.run();
+}
diff --git a/sensors/src/HwmonTempMain.cpp b/sensors/src/HwmonTempMain.cpp
new file mode 100644
index 0000000..3c49771
--- /dev/null
+++ b/sensors/src/HwmonTempMain.cpp
@@ -0,0 +1,235 @@
+/*
+// Copyright (c) 2017 Intel Corporation
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+*/
+
+#include <HwmonTempSensor.hpp>
+#include <Utils.hpp>
+#include <boost/algorithm/string/predicate.hpp>
+#include <boost/algorithm/string/replace.hpp>
+#include <boost/container/flat_set.hpp>
+#include <experimental/filesystem>
+#include <fstream>
+#include <regex>
+#include <sdbusplus/asio/connection.hpp>
+#include <sdbusplus/asio/object_server.hpp>
+
+static constexpr bool DEBUG = false;
+
+namespace fs = std::experimental::filesystem;
+static constexpr std::array<const char*, 2> SENSOR_TYPES = {
+ "xyz.openbmc_project.Configuration.TMP75",
+ "xyz.openbmc_project.Configuration.TMP421"};
+static std::regex INPUT_REGEX(R"(temp(\d+)_input)");
+
+void createSensors(
+ boost::asio::io_service& io, sdbusplus::asio::object_server& objectServer,
+ boost::container::flat_map<std::string, std::unique_ptr<HwmonTempSensor>>&
+ sensors,
+ std::shared_ptr<sdbusplus::asio::connection>& dbusConnection,
+ const std::unique_ptr<boost::container::flat_set<std::string>>&
+ sensorsChanged)
+{
+ bool firstScan = sensorsChanged == nullptr;
+ // use new data the first time, then refresh
+ ManagedObjectType sensorConfigurations;
+ bool useCache = false;
+ for (const char* type : SENSOR_TYPES)
+ {
+ if (!getSensorConfiguration(type, dbusConnection, sensorConfigurations,
+ useCache))
+ {
+ std::cerr << "error communicating to entity manager\n";
+ return;
+ }
+ useCache = true;
+ }
+ std::vector<fs::path> paths;
+ if (!find_files(fs::path("/sys/class/hwmon"), R"(temp\d+_input)", paths))
+ {
+ std::cerr << "No temperature sensors in system\n";
+ return;
+ }
+
+ // iterate through all found temp sensors, and try to match them with
+ // configuration
+ for (auto& path : paths)
+ {
+ std::smatch match;
+ std::string pathStr = path.string();
+
+ std::regex_search(pathStr, match, INPUT_REGEX);
+ std::string index = *(match.begin() + 1);
+
+ auto directory = path.parent_path();
+ auto oem_name_path = directory.string() + R"(/of_node/oemname)" + index;
+
+ if (DEBUG)
+ std::cout << "Checking path " << oem_name_path << "\n";
+ std::ifstream nameFile(oem_name_path);
+ if (!nameFile.good())
+ {
+ std::cerr << "Failure reading " << oem_name_path << "\n";
+ continue;
+ }
+ std::string oemName;
+ std::getline(nameFile, oemName);
+ nameFile.close();
+ if (!oemName.size())
+ {
+ // shouldn't have an empty name file
+ continue;
+ }
+ oemName.pop_back(); // remove trailing null
+
+ const SensorData* sensorData = nullptr;
+ const std::string* interfacePath = nullptr;
+ for (const std::pair<sdbusplus::message::object_path, SensorData>&
+ sensor : sensorConfigurations)
+ {
+ if (!boost::ends_with(sensor.first.str, oemName))
+ {
+ continue;
+ }
+ sensorData = &(sensor.second);
+ interfacePath = &(sensor.first.str);
+ break;
+ }
+ if (sensorData == nullptr)
+ {
+ std::cerr << "failed to find match for " << oemName << "\n";
+ continue;
+ }
+ const std::pair<std::string, boost::container::flat_map<
+ std::string, BasicVariantType>>*
+ baseConfiguration = nullptr;
+ const char* sensorType = nullptr;
+ for (const char* type : SENSOR_TYPES)
+ {
+ auto sensorBase = sensorData->find(type);
+ if (sensorBase != sensorData->end())
+ {
+ baseConfiguration = &(*sensorBase);
+ sensorType = type;
+ break;
+ }
+ }
+
+ if (baseConfiguration == nullptr)
+ {
+ std::cerr << "error finding base configuration for" << oemName
+ << "\n";
+ continue;
+ }
+
+ auto findSensorName = baseConfiguration->second.find("Name");
+ if (findSensorName == baseConfiguration->second.end())
+ {
+ std::cerr << "could not determine configuration name for "
+ << oemName << "\n";
+ continue;
+ }
+ std::string sensorName =
+ sdbusplus::message::variant_ns::get<std::string>(
+ findSensorName->second);
+ // on rescans, only update sensors we were signaled by
+ auto findSensor = sensors.find(sensorName);
+ if (!firstScan && findSensor != sensors.end())
+ {
+ bool found = false;
+ for (auto it = sensorsChanged->begin(); it != sensorsChanged->end();
+ it++)
+ {
+ if (boost::ends_with(*it, findSensor->second->name))
+ {
+ sensorsChanged->erase(it);
+ findSensor->second = nullptr;
+ found = true;
+ break;
+ }
+ }
+ if (!found)
+ {
+ continue;
+ }
+ }
+ std::vector<thresholds::Threshold> sensorThresholds;
+ if (!ParseThresholdsFromConfig(*sensorData, sensorThresholds))
+ {
+ std::cerr << "error populating thresholds for " << sensorName
+ << "\n";
+ }
+
+ sensors[sensorName] = std::make_unique<HwmonTempSensor>(
+ path.string(), sensorType, objectServer, dbusConnection, io,
+ sensorName, std::move(sensorThresholds), *interfacePath);
+ }
+}
+
+int main(int argc, char** argv)
+{
+ boost::asio::io_service io;
+ auto systemBus = std::make_shared<sdbusplus::asio::connection>(io);
+ systemBus->request_name("xyz.openbmc_project.HwmonTempSensor");
+ sdbusplus::asio::object_server objectServer(systemBus);
+ boost::container::flat_map<std::string, std::unique_ptr<HwmonTempSensor>>
+ sensors;
+ std::vector<std::unique_ptr<sdbusplus::bus::match::match>> matches;
+ std::unique_ptr<boost::container::flat_set<std::string>> sensorsChanged =
+ std::make_unique<boost::container::flat_set<std::string>>();
+
+ io.post([&]() {
+ createSensors(io, objectServer, sensors, systemBus, nullptr);
+ });
+
+ boost::asio::deadline_timer filterTimer(io);
+ std::function<void(sdbusplus::message::message&)> eventHandler =
+ [&](sdbusplus::message::message& message) {
+ if (message.is_method_error())
+ {
+ std::cerr << "callback method error\n";
+ return;
+ }
+ sensorsChanged->insert(message.get_path());
+ // this implicitly cancels the timer
+ filterTimer.expires_from_now(boost::posix_time::seconds(1));
+
+ filterTimer.async_wait([&](const boost::system::error_code& ec) {
+ if (ec == boost::asio::error::operation_aborted)
+ {
+ /* we were canceled*/
+ return;
+ }
+ else if (ec)
+ {
+ std::cerr << "timer error\n";
+ return;
+ }
+ createSensors(io, objectServer, sensors, systemBus,
+ sensorsChanged);
+ });
+ };
+
+ for (const char* type : SENSOR_TYPES)
+ {
+ auto match = std::make_unique<sdbusplus::bus::match::match>(
+ static_cast<sdbusplus::bus::bus&>(*systemBus),
+ "type='signal',member='PropertiesChanged',path_namespace='" +
+ std::string(INVENTORY_PATH) + "',arg0namespace='" + type + "'",
+ eventHandler);
+ matches.emplace_back(std::move(match));
+ }
+
+ io.run();
+}
diff --git a/sensors/src/HwmonTempSensor.cpp b/sensors/src/HwmonTempSensor.cpp
new file mode 100644
index 0000000..c1db504
--- /dev/null
+++ b/sensors/src/HwmonTempSensor.cpp
@@ -0,0 +1,304 @@
+/*
+// Copyright (c) 2017 Intel Corporation
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+*/
+
+#include <unistd.h>
+
+#include <HwmonTempSensor.hpp>
+#include <boost/algorithm/string/predicate.hpp>
+#include <boost/algorithm/string/replace.hpp>
+#include <boost/date_time/posix_time/posix_time.hpp>
+#include <iostream>
+#include <limits>
+#include <sdbusplus/asio/connection.hpp>
+#include <sdbusplus/asio/object_server.hpp>
+#include <string>
+
+static constexpr unsigned int SENSOR_POLL_MS = 500;
+static constexpr unsigned int SENSOR_SCALE_FACTOR = 1000;
+static constexpr size_t WARN_AFTER_ERROR_COUNT = 10;
+
+HwmonTempSensor::HwmonTempSensor(
+ const std::string &path, const std::string &objectType,
+ sdbusplus::asio::object_server &objectServer,
+ std::shared_ptr<sdbusplus::asio::connection> &conn,
+ boost::asio::io_service &io, const std::string &sensor_name,
+ std::vector<thresholds::Threshold> &&_thresholds,
+ const std::string &sensorConfiguration) :
+ path(path),
+ objectType(objectType), configuration(sensorConfiguration),
+ objServer(objectServer),
+ name(boost::replace_all_copy(sensor_name, " ", "_")),
+ thresholds(std::move(_thresholds)),
+ sensor_interface(objectServer.add_interface(
+ "/xyz/openbmc_project/sensors/temperature/" + name,
+ "xyz.openbmc_project.Sensor.Value")),
+ input_dev(io, open(path.c_str(), O_RDONLY)), wait_timer(io),
+ value(std::numeric_limits<double>::quiet_NaN()), err_count(0),
+ // todo, get these from config
+ max_value(127), min_value(-128)
+{
+ if (thresholds::HasWarningInterface(thresholds))
+ {
+ threshold_interface_warning = objectServer.add_interface(
+ "/xyz/openbmc_project/sensors/temperature/" + name,
+ "xyz.openbmc_project.Sensor.Threshold.Warning");
+ }
+ if (thresholds::HasCriticalInterface(thresholds))
+ {
+ threshold_interface_critical = objectServer.add_interface(
+ "/xyz/openbmc_project/sensors/temperature/" + name,
+ "xyz.openbmc_project.Sensor.Threshold.Critical");
+ }
+ set_initial_properties(conn);
+ setup_read();
+}
+
+HwmonTempSensor::~HwmonTempSensor()
+{
+ // close the input dev to cancel async operations
+ input_dev.close();
+ wait_timer.cancel();
+ objServer.remove_interface(threshold_interface_warning);
+ objServer.remove_interface(threshold_interface_critical);
+ objServer.remove_interface(sensor_interface);
+}
+
+void HwmonTempSensor::setup_read(void)
+{
+ boost::asio::async_read_until(
+ input_dev, read_buf, '\n',
+ [&](const boost::system::error_code &ec,
+ std::size_t /*bytes_transfered*/) { handle_response(ec); });
+}
+
+void HwmonTempSensor::handle_response(const boost::system::error_code &err)
+{
+ if (err == boost::system::errc::bad_file_descriptor)
+ {
+ return; // we're being destroyed
+ }
+ std::istream response_stream(&read_buf);
+ if (!err)
+ {
+ std::string response;
+ std::getline(response_stream, response);
+ try
+ {
+ float nvalue = std::stof(response);
+
+ nvalue /= SENSOR_SCALE_FACTOR;
+ if (nvalue != value)
+ {
+ update_value(nvalue);
+ }
+ err_count = 0;
+ }
+ catch (const std::invalid_argument &)
+ {
+ err_count++;
+ }
+ }
+ else
+ {
+ std::cerr << "Failure to read sensor " << name << " at " << path
+ << "\n";
+ err_count++;
+ }
+ // only send value update once
+ if (err_count == WARN_AFTER_ERROR_COUNT)
+ {
+ update_value(0);
+ }
+ response_stream.clear();
+ input_dev.close();
+ int fd = open(path.c_str(), O_RDONLY);
+ if (fd <= 0)
+ {
+ return; // we're no longer valid
+ }
+ input_dev.assign(fd);
+ wait_timer.expires_from_now(
+ boost::posix_time::milliseconds(SENSOR_POLL_MS));
+ ;
+ wait_timer.async_wait([&](const boost::system::error_code &ec) {
+ if (ec == boost::asio::error::operation_aborted)
+ {
+ return; // we're being canceled
+ }
+ setup_read();
+ });
+}
+
+void HwmonTempSensor::check_thresholds(void)
+{
+ if (thresholds.empty())
+ return;
+ for (auto threshold : thresholds)
+ {
+ if (threshold.direction == thresholds::Direction::HIGH)
+ {
+ if (value > threshold.value)
+ {
+ assert_thresholds(threshold.level, threshold.direction, true);
+ }
+ else
+ {
+ assert_thresholds(threshold.level, threshold.direction, false);
+ }
+ }
+ else
+ {
+ if (value < threshold.value)
+ {
+ assert_thresholds(threshold.level, threshold.direction, true);
+ }
+ else
+ {
+ assert_thresholds(threshold.level, threshold.direction, false);
+ }
+ }
+ }
+}
+
+void HwmonTempSensor::update_value(const double &new_value)
+{
+ sensor_interface->set_property("Value", new_value);
+ value = new_value;
+ check_thresholds();
+}
+
+void HwmonTempSensor::assert_thresholds(thresholds::Level level,
+ thresholds::Direction direction,
+ bool assert)
+{
+ std::string property;
+ std::shared_ptr<sdbusplus::asio::dbus_interface> interface;
+ if (level == thresholds::Level::WARNING &&
+ direction == thresholds::Direction::HIGH)
+ {
+ property = "WarningAlarmHigh";
+ interface = threshold_interface_warning;
+ }
+ else if (level == thresholds::Level::WARNING &&
+ direction == thresholds::Direction::LOW)
+ {
+ property = "WarningAlarmLow";
+ interface = threshold_interface_warning;
+ }
+ else if (level == thresholds::Level::CRITICAL &&
+ direction == thresholds::Direction::HIGH)
+ {
+ property = "CriticalAlarmHigh";
+ interface = threshold_interface_critical;
+ }
+ else if (level == thresholds::Level::CRITICAL &&
+ direction == thresholds::Direction::LOW)
+ {
+ property = "CriticalAlarmLow";
+ interface = threshold_interface_critical;
+ }
+ else
+ {
+ std::cerr << "Unknown threshold, level " << level << "direction "
+ << direction << "\n";
+ return;
+ }
+ if (!interface)
+ {
+ std::cout << "trying to set uninitialized interface\n";
+ return;
+ }
+ interface->set_property(property, assert);
+}
+
+void HwmonTempSensor::set_initial_properties(
+ std::shared_ptr<sdbusplus::asio::connection> &conn)
+{
+ // todo, get max and min from configuration
+ sensor_interface->register_property("MaxValue", max_value);
+ sensor_interface->register_property("MinValue", min_value);
+ sensor_interface->register_property("Value", value);
+
+ for (auto &threshold : thresholds)
+ {
+ std::shared_ptr<sdbusplus::asio::dbus_interface> iface;
+ std::string level;
+ std::string alarm;
+ if (threshold.level == thresholds::Level::CRITICAL)
+ {
+ iface = threshold_interface_critical;
+ if (threshold.direction == thresholds::Direction::HIGH)
+ {
+ level = "CriticalHigh";
+ alarm = "CriticalAlarmHigh";
+ }
+ else
+ {
+ level = "CriticalLow";
+ alarm = "CriticalAlarmLow";
+ }
+ }
+ else if (threshold.level == thresholds::Level::WARNING)
+ {
+ iface = threshold_interface_warning;
+ if (threshold.direction == thresholds::Direction::HIGH)
+ {
+ level = "WarningHigh";
+ alarm = "WarningAlarmHigh";
+ }
+ else
+ {
+ level = "WarningLow";
+ alarm = "WarningAlarmLow";
+ }
+ }
+ else
+ {
+ std::cerr << "Unknown threshold level" << threshold.level << "\n";
+ continue;
+ }
+ if (!iface)
+ {
+ std::cout << "trying to set uninitialized interface\n";
+ continue;
+ }
+ iface->register_property(
+ level, threshold.value,
+ [&](const double &request, double &oldValue) {
+ oldValue = request; // todo, just let the config do this?
+ threshold.value = request;
+ thresholds::persistThreshold(configuration, objectType,
+ threshold, conn);
+ return 1;
+ });
+ iface->register_property(alarm, false);
+ }
+ if (!sensor_interface->initialize())
+ {
+ std::cerr << "error initializing value interface\n";
+ }
+ if (threshold_interface_warning &&
+ !threshold_interface_warning->initialize())
+ {
+ std::cerr << "error initializing warning threshold interface\n";
+ }
+
+ if (threshold_interface_critical &&
+ !threshold_interface_critical->initialize())
+ {
+ std::cerr << "error initializing critical threshold interface\n";
+ }
+}
diff --git a/sensors/src/PwmSensor.cpp b/sensors/src/PwmSensor.cpp
new file mode 100644
index 0000000..11d635f
--- /dev/null
+++ b/sensors/src/PwmSensor.cpp
@@ -0,0 +1,129 @@
+/*
+// Copyright (c) 2018 Intel Corporation
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+*/
+#include <PwmSensor.hpp>
+#include <fstream>
+#include <iostream>
+#include <sdbusplus/asio/object_server.hpp>
+
+constexpr size_t pwmMax = 255;
+constexpr size_t pwmMin = 0;
+
+PwmSensor::PwmSensor(const std::string& sysPath,
+ sdbusplus::asio::object_server& objectServer) :
+ sysPath(sysPath),
+ objectServer(objectServer)
+{
+ // strip off index from path
+ name = "Pwm_" + sysPath.substr(sysPath.find_last_of("pwm") + 1);
+
+ // add interface under sensor and Control.FanPwm as Control is used
+ // in obmc project, also add sensor so it can be viewed as a sensor
+ sensorInterface = objectServer.add_interface(
+ "/xyz/openbmc_project/sensors/fan_pwm/" + name,
+ "xyz.openbmc_project.Sensor.Value");
+ uint32_t pwmValue = getValue(false);
+ double fValue = 100.0 * (static_cast<float>(pwmValue) / pwmMax);
+ sensorInterface->register_property(
+ "Value", fValue,
+ [this](const double& req, double& resp) {
+ if (req > 100 || req < 0)
+ {
+ throw std::runtime_error("Value out of range");
+ return -1;
+ }
+ double value = (req / 100) * pwmMax;
+ setValue(static_cast<int>(value));
+ resp = req;
+ return 1;
+ },
+ [this](double& curVal) {
+ float value = 100.0 * (static_cast<float>(getValue()) / pwmMax);
+ curVal = value;
+ return curVal;
+ });
+ // pwm sensor interface is in percent
+ sensorInterface->register_property("MaxValue", static_cast<int64_t>(100));
+ sensorInterface->register_property("MinValue", static_cast<int64_t>(0));
+
+ controlInterface = objectServer.add_interface(
+ "/xyz/openbmc_project/control/fanpwm/" + name,
+ "xyz.openbmc_project.Control.FanPwm");
+ controlInterface->register_property(
+ "Target", static_cast<uint64_t>(pwmValue),
+ [this](const uint64_t& req, uint64_t& resp) {
+ if (req > pwmMax || req < pwmMin)
+ {
+ throw std::runtime_error("Value out of range");
+ return -1;
+ }
+ setValue(req);
+ resp = req;
+ return 1;
+ },
+ [this](uint64_t& curVal) {
+ curVal = getValue();
+ return curVal;
+ });
+ sensorInterface->initialize();
+ controlInterface->initialize();
+}
+PwmSensor::~PwmSensor()
+{
+ objectServer.remove_interface(sensorInterface);
+ objectServer.remove_interface(controlInterface);
+}
+
+void PwmSensor::setValue(uint32_t value)
+{
+ std::ofstream ref(sysPath);
+ if (!ref.good())
+ {
+ throw std::runtime_error("Bad Write File");
+ return;
+ }
+ ref << value;
+}
+
+// on success returns pwm, on failure throws except on initialization, where it
+// prints an error and returns 0
+uint32_t PwmSensor::getValue(bool errThrow)
+{
+ std::ifstream ref(sysPath);
+ if (!ref.good())
+ {
+ return -1;
+ }
+ std::string line;
+ if (!std::getline(ref, line))
+ {
+ return -1;
+ }
+ try
+ {
+ uint32_t value = std::stoi(line);
+ return value;
+ }
+ catch (std::invalid_argument)
+ {
+ std::cerr << "Error reading pwm at " << sysPath << "\n";
+ // throw if not initial read to be caught by dbus bindings
+ if (errThrow)
+ {
+ throw std::runtime_error("Bad Read");
+ }
+ }
+ return 0;
+}
diff --git a/sensors/src/TachSensor.cpp b/sensors/src/TachSensor.cpp
new file mode 100644
index 0000000..91f0545
--- /dev/null
+++ b/sensors/src/TachSensor.cpp
@@ -0,0 +1,312 @@
+/*
+// Copyright (c) 2018 Intel Corporation
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+*/
+
+#include <unistd.h>
+
+#include <TachSensor.hpp>
+#include <Utils.hpp>
+#include <boost/algorithm/string/predicate.hpp>
+#include <boost/algorithm/string/replace.hpp>
+#include <boost/date_time/posix_time/posix_time.hpp>
+#include <iostream>
+#include <limits>
+#include <sdbusplus/asio/connection.hpp>
+#include <sdbusplus/asio/object_server.hpp>
+#include <string>
+
+static constexpr unsigned int PWM_POLL_MS = 500;
+static constexpr size_t WARN_AFTER_ERROR_COUNT = 10;
+
+TachSensor::TachSensor(const std::string &path,
+ sdbusplus::asio::object_server &objectServer,
+ std::shared_ptr<sdbusplus::asio::connection> &conn,
+ boost::asio::io_service &io, const std::string &fanName,
+ std::vector<thresholds::Threshold> &&_thresholds,
+ const std::string &sensorConfiguration) :
+ path(path),
+ objServer(objectServer), dbusConnection(conn),
+ name(boost::replace_all_copy(fanName, " ", "_")),
+ configuration(sensorConfiguration), thresholds(std::move(_thresholds)),
+ sensor_interface(objectServer.add_interface(
+ "/xyz/openbmc_project/sensors/fan_tach/" + name,
+ "xyz.openbmc_project.Sensor.Value")),
+ input_dev(io, open(path.c_str(), O_RDONLY)), wait_timer(io),
+ value(std::numeric_limits<double>::quiet_NaN()), err_count(0),
+ // todo, get these from config
+ max_value(25000), min_value(0)
+{
+ if (thresholds::HasWarningInterface(thresholds))
+ {
+ threshold_interface_warning = objectServer.add_interface(
+ "/xyz/openbmc_project/sensors/fan_tach/" + name,
+ "xyz.openbmc_project.Sensor.Threshold.Warning");
+ }
+ if (thresholds::HasCriticalInterface(thresholds))
+ {
+ threshold_interface_critical = objectServer.add_interface(
+ "/xyz/openbmc_project/sensors/fan_tach/" + name,
+ "xyz.openbmc_project.Sensor.Threshold.Critical");
+ }
+ set_initial_properties(conn);
+ isPowerOn(dbusConnection); // first call initializes
+ setup_read();
+}
+
+TachSensor::~TachSensor()
+{
+ // close the input dev to cancel async operations
+ input_dev.close();
+ wait_timer.cancel();
+ objServer.remove_interface(threshold_interface_warning);
+ objServer.remove_interface(threshold_interface_critical);
+ objServer.remove_interface(sensor_interface);
+}
+
+void TachSensor::setup_read(void)
+{
+ boost::asio::async_read_until(
+ input_dev, read_buf, '\n',
+ [&](const boost::system::error_code &ec,
+ std::size_t /*bytes_transfered*/) { handle_response(ec); });
+}
+
+void TachSensor::handle_response(const boost::system::error_code &err)
+{
+ if (err == boost::system::errc::bad_file_descriptor)
+ {
+ return; // we're being destroyed
+ }
+ std::istream response_stream(&read_buf);
+ if (!err)
+ {
+ std::string response;
+ try
+ {
+ std::getline(response_stream, response);
+ float nvalue = std::stof(response);
+ response_stream.clear();
+ if (nvalue != value)
+ {
+ update_value(nvalue);
+ }
+ err_count = 0;
+ }
+ catch (const std::invalid_argument &)
+ {
+ err_count++;
+ }
+ }
+ else
+ {
+
+ err_count++;
+ }
+ // only send value update once
+ if (err_count == WARN_AFTER_ERROR_COUNT)
+ {
+ // only an error if power is on
+ if (isPowerOn(dbusConnection))
+ {
+ std::cerr << "Failure to read sensor " << name << " at " << path
+ << "\n";
+ update_value(0);
+ }
+ else
+ {
+ err_count = 0; // check power again in 10 cycles
+ sensor_interface->set_property(
+ "Value", std::numeric_limits<double>::quiet_NaN());
+ }
+ }
+ response_stream.clear();
+ input_dev.close();
+ int fd = open(path.c_str(), O_RDONLY);
+ if (fd <= 0)
+ {
+ return; // we're no longer valid
+ }
+ input_dev.assign(fd);
+ wait_timer.expires_from_now(boost::posix_time::milliseconds(PWM_POLL_MS));
+ wait_timer.async_wait([&](const boost::system::error_code &ec) {
+ if (ec == boost::asio::error::operation_aborted)
+ {
+ return; // we're being canceled
+ }
+ setup_read();
+ });
+}
+
+void TachSensor::check_thresholds(void)
+{
+ if (thresholds.empty())
+ return;
+ for (auto &threshold : thresholds)
+ {
+ if (threshold.direction == thresholds::Direction::HIGH)
+ {
+ if (value > threshold.value)
+ {
+ assert_thresholds(threshold.level, threshold.direction, true);
+ }
+ else
+ {
+ assert_thresholds(threshold.level, threshold.direction, false);
+ }
+ }
+ else
+ {
+ if (value < threshold.value)
+ {
+ assert_thresholds(threshold.level, threshold.direction, true);
+ }
+ else
+ {
+ assert_thresholds(threshold.level, threshold.direction, false);
+ }
+ }
+ }
+}
+
+void TachSensor::update_value(const double &new_value)
+{
+ sensor_interface->set_property("Value", new_value);
+ value = new_value;
+ check_thresholds();
+}
+
+void TachSensor::assert_thresholds(thresholds::Level level,
+ thresholds::Direction direction, bool assert)
+{
+ std::string property;
+ std::shared_ptr<sdbusplus::asio::dbus_interface> interface;
+ if (level == thresholds::Level::WARNING &&
+ direction == thresholds::Direction::HIGH)
+ {
+ property = "WarningAlarmHigh";
+ interface = threshold_interface_warning;
+ }
+ else if (level == thresholds::Level::WARNING &&
+ direction == thresholds::Direction::LOW)
+ {
+ property = "WarningAlarmLow";
+ interface = threshold_interface_warning;
+ }
+ else if (level == thresholds::Level::CRITICAL &&
+ direction == thresholds::Direction::HIGH)
+ {
+ property = "CriticalAlarmHigh";
+ interface = threshold_interface_critical;
+ }
+ else if (level == thresholds::Level::CRITICAL &&
+ direction == thresholds::Direction::LOW)
+ {
+ property = "CriticalAlarmLow";
+ interface = threshold_interface_critical;
+ }
+ else
+ {
+ std::cerr << "Unknown threshold, level " << level << "direction "
+ << direction << "\n";
+ return;
+ }
+ if (!interface)
+ {
+ std::cout << "trying to set uninitialized interface\n";
+ return;
+ }
+ interface->set_property(property, assert);
+}
+
+void TachSensor::set_initial_properties(
+ std::shared_ptr<sdbusplus::asio::connection> &conn)
+{
+ // todo, get max and min from configuration
+ sensor_interface->register_property("MaxValue", max_value);
+ sensor_interface->register_property("MinValue", min_value);
+ sensor_interface->register_property("Value", value);
+
+ for (auto &threshold : thresholds)
+ {
+ std::shared_ptr<sdbusplus::asio::dbus_interface> iface;
+ std::string level;
+ std::string alarm;
+ if (threshold.level == thresholds::Level::CRITICAL)
+ {
+ iface = threshold_interface_critical;
+ if (threshold.direction == thresholds::Direction::HIGH)
+ {
+ level = "CriticalHigh";
+ alarm = "CriticalAlarmHigh";
+ }
+ else
+ {
+ level = "CriticalLow";
+ alarm = "CriticalAlarmLow";
+ }
+ }
+ else if (threshold.level == thresholds::Level::WARNING)
+ {
+ iface = threshold_interface_warning;
+ if (threshold.direction == thresholds::Direction::HIGH)
+ {
+ level = "WarningHigh";
+ alarm = "WarningAlarmHigh";
+ }
+ else
+ {
+ level = "WarningLow";
+ alarm = "WarningAlarmLow";
+ }
+ }
+ else
+ {
+ std::cerr << "Unknown threshold level" << threshold.level << "\n";
+ continue;
+ }
+ if (!iface)
+ {
+ std::cout << "trying to set uninitialized interface\n";
+ continue;
+ }
+ iface->register_property(
+ level, threshold.value,
+ [&](const double &request, double &oldValue) {
+ oldValue = request; // todo, just let the config do this?
+ threshold.value = request;
+ thresholds::persistThreshold(
+ configuration,
+ "xyz.openbmc_project.Configuration.AspeedFan", threshold,
+ conn);
+ return 1;
+ });
+ iface->register_property(alarm, false);
+ }
+ if (!sensor_interface->initialize())
+ {
+ std::cerr << "error initializing value interface\n";
+ }
+ if (threshold_interface_warning &&
+ !threshold_interface_warning->initialize())
+ {
+ std::cerr << "error initializing warning threshold interface\n";
+ }
+
+ if (threshold_interface_critical &&
+ !threshold_interface_critical->initialize())
+ {
+ std::cerr << "error initializing critical threshold interface\n";
+ }
+}
diff --git a/sensors/src/Thresholds.cpp b/sensors/src/Thresholds.cpp
new file mode 100644
index 0000000..d31f487
--- /dev/null
+++ b/sensors/src/Thresholds.cpp
@@ -0,0 +1,224 @@
+#include <Thresholds.hpp>
+#include <VariantVisitors.hpp>
+#include <boost/algorithm/string/replace.hpp>
+#include <boost/lexical_cast.hpp>
+#include <fstream>
+#include <iostream>
+
+static constexpr bool DEBUG = false;
+constexpr size_t MAX_THRESHOLDS = 4;
+
+namespace thresholds
+{
+unsigned int toBusValue(const Level &level)
+{
+ switch (level)
+ {
+ case (Level::WARNING):
+ {
+ return 0;
+ }
+ case (Level::CRITICAL):
+ {
+ return 1;
+ }
+ default:
+ {
+ return -1;
+ }
+ }
+}
+
+std::string toBusValue(const Direction &direction)
+{
+ switch (direction)
+ {
+ case (Direction::LOW):
+ {
+ return "less than";
+ }
+ case (Direction::HIGH):
+ {
+ return "greater than";
+ }
+ default:
+ {
+ return "err";
+ }
+ }
+}
+
+bool ParseThresholdsFromConfig(
+ const SensorData &sensorData,
+ std::vector<thresholds::Threshold> &thresholdVector,
+ const std::string *matchLabel)
+{
+ for (const auto &item : sensorData)
+ {
+ if (item.first.find("Thresholds") == std::string::npos)
+ {
+ continue;
+ }
+ if (matchLabel != nullptr)
+ {
+ auto labelFind = item.second.find("Label");
+ if (labelFind == item.second.end())
+ continue;
+ if (mapbox::util::apply_visitor(VariantToStringVisitor(),
+ labelFind->second) != *matchLabel)
+ continue;
+ }
+ auto directionFind = item.second.find("Direction");
+ auto severityFind = item.second.find("Severity");
+ auto valueFind = item.second.find("Value");
+ if (valueFind == item.second.end() ||
+ severityFind == item.second.end() ||
+ directionFind == item.second.end())
+ {
+ std::cerr << "Malformed threshold in configuration\n";
+ return false;
+ }
+ Level level;
+ Direction direction;
+ if (mapbox::util::apply_visitor(VariantToUnsignedIntVisitor(),
+ severityFind->second) == 0)
+ {
+ level = Level::WARNING;
+ }
+ else
+ {
+ level = Level::CRITICAL;
+ }
+ if (mapbox::util::apply_visitor(VariantToStringVisitor(),
+ directionFind->second) == "less than")
+ {
+ direction = Direction::LOW;
+ }
+ else
+ {
+ direction = Direction::HIGH;
+ }
+ float val = mapbox::util::apply_visitor(VariantToFloatVisitor(),
+ valueFind->second);
+
+ thresholdVector.emplace_back(level, direction, val);
+ }
+ return true;
+}
+
+void persistThreshold(const std::string &path, const std::string &baseInterface,
+ const thresholds::Threshold &threshold,
+ std::shared_ptr<sdbusplus::asio::connection> &conn)
+{
+ for (int ii = 0; ii < MAX_THRESHOLDS; ii++)
+ {
+ std::string thresholdInterface =
+ baseInterface + ".Thresholds" + std::to_string(ii);
+ conn->async_method_call(
+ [&, path, threshold, thresholdInterface](
+ const boost::system::error_code &ec,
+ const boost::container::flat_map<std::string, BasicVariantType>
+ &result) {
+ if (ec)
+ {
+ return; // threshold not supported
+ }
+
+ auto directionFind = result.find("Direction");
+ auto severityFind = result.find("Severity");
+ auto valueFind = result.find("Value");
+ if (valueFind == result.end() || severityFind == result.end() ||
+ directionFind == result.end())
+ {
+ std::cerr << "Malformed threshold in configuration\n";
+ return;
+ }
+ unsigned int level = mapbox::util::apply_visitor(
+ VariantToUnsignedIntVisitor(), severityFind->second);
+
+ std::string dir = mapbox::util::apply_visitor(
+ VariantToStringVisitor(), directionFind->second);
+ if ((toBusValue(threshold.level) != level) ||
+ (toBusValue(threshold.direction) != dir))
+ {
+ return; // not the droid we're looking for
+ }
+
+ sdbusplus::message::variant<double> value(threshold.value);
+ conn->async_method_call(
+ [](const boost::system::error_code &ec) {
+ if (ec)
+ {
+ std::cerr << "Error setting threshold " << ec
+ << "\n";
+ }
+ },
+ ENTITY_MANAGER_NAME, path,
+ "org.freedesktop.DBus.Properties", "Set",
+ thresholdInterface, "Value", value);
+ },
+ ENTITY_MANAGER_NAME, path, "org.freedesktop.DBus.Properties",
+ "GetAll", thresholdInterface);
+ }
+}
+
+static constexpr std::array<const char *, 4> ATTR_TYPES = {"lcrit", "min",
+ "max", "crit"};
+
+bool ParseThresholdsFromAttr(
+ std::vector<thresholds::Threshold> &threshold_vector,
+ const std::string &input_path, const double scale_factor)
+{
+ for (auto &type : ATTR_TYPES)
+ {
+ auto attr_path = boost::replace_all_copy(input_path, "input", type);
+ std::ifstream attr_file(attr_path);
+ if (!attr_file.good())
+ continue;
+ std::string attr;
+ std::getline(attr_file, attr);
+ attr_file.close();
+
+ Level level;
+ Direction direction;
+ double val = std::stod(attr) / scale_factor;
+ if (type == "min" || type == "max")
+ level = Level::WARNING;
+ else
+ level = Level::CRITICAL;
+ if (type == "min" || type == "lcrit")
+ direction = Direction::LOW;
+ else
+ direction = Direction::HIGH;
+
+ if (DEBUG)
+ std::cout << "Threshold: " << attr_path << ": " << val << "\n";
+
+ threshold_vector.emplace_back(level, direction, val);
+ }
+ // no thresholds is allowed, not an error so return true always
+ return true;
+}
+
+bool HasCriticalInterface(
+ const std::vector<thresholds::Threshold> &threshold_vector)
+{
+ for (auto &threshold : threshold_vector)
+ {
+ if (threshold.level == Level::CRITICAL)
+ return true;
+ }
+ return false;
+}
+
+bool HasWarningInterface(
+ const std::vector<thresholds::Threshold> &threshold_vector)
+{
+ for (auto &threshold : threshold_vector)
+ {
+ if (threshold.level == Level::WARNING)
+ return true;
+ }
+ return false;
+}
+} // namespace thresholds
diff --git a/sensors/src/Utils.cpp b/sensors/src/Utils.cpp
new file mode 100644
index 0000000..6ce3f18
--- /dev/null
+++ b/sensors/src/Utils.cpp
@@ -0,0 +1,165 @@
+/*
+// Copyright (c) 2017 Intel Corporation
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+*/
+
+#include <Utils.hpp>
+#include <boost/algorithm/string/predicate.hpp>
+#include <experimental/filesystem>
+#include <fstream>
+#include <regex>
+#include <sdbusplus/asio/connection.hpp>
+#include <sdbusplus/bus/match.hpp>
+
+namespace fs = std::experimental::filesystem;
+const static constexpr char* POWER_INTERFACE_NAME =
+ "xyz.openbmc_project.Chassis.Control.Power";
+const static constexpr char* POWER_OBJECT_NAME =
+ "/xyz/openbmc_project/Chassis/Control/Power0";
+
+bool getSensorConfiguration(
+ const std::string& type,
+ const std::shared_ptr<sdbusplus::asio::connection>& dbusConnection,
+ ManagedObjectType& resp, bool useCache)
+{
+ static ManagedObjectType managedObj;
+
+ if (!useCache)
+ {
+ managedObj.clear();
+ sdbusplus::message::message getManagedObjects =
+ dbusConnection->new_method_call(
+ ENTITY_MANAGER_NAME, "/", "org.freedesktop.DBus.ObjectManager",
+ "GetManagedObjects");
+ bool err = false;
+ try
+ {
+ sdbusplus::message::message reply =
+ dbusConnection->call(getManagedObjects);
+ err = reply.is_method_error();
+ if (!err)
+ {
+ reply.read(managedObj);
+ }
+ }
+ catch (const sdbusplus::exception::exception&)
+ {
+ err = true;
+ }
+
+ if (err)
+ {
+ std::cerr << "Error communicating to entity manager\n";
+ return false;
+ }
+ }
+ for (const auto& pathPair : managedObj)
+ {
+ std::vector<boost::container::flat_map<std::string, BasicVariantType>>
+ sensorData;
+ bool correctType = false;
+ for (const auto& entry : pathPair.second)
+ {
+ if (boost::starts_with(entry.first, type))
+ {
+ correctType = true;
+ break;
+ }
+ }
+ if (correctType)
+ {
+ resp.emplace(pathPair);
+ }
+ }
+ return true;
+}
+
+bool find_files(const fs::path dir_path, const std::string& match_string,
+ std::vector<fs::path>& found_paths, unsigned int symlink_depth)
+{
+ if (!fs::exists(dir_path))
+ return false;
+
+ fs::directory_iterator end_itr;
+ std::regex search(match_string);
+ std::smatch match;
+ for (auto& p : fs::recursive_directory_iterator(dir_path))
+ {
+ std::string path = p.path().string();
+ if (!is_directory(p))
+ {
+ if (std::regex_search(path, match, search))
+ found_paths.emplace_back(p.path());
+ }
+ // since we're using a recursive iterator, these should only be symlink
+ // dirs
+ else if (symlink_depth)
+ {
+ find_files(p.path(), match_string, found_paths, symlink_depth - 1);
+ }
+ }
+ return true;
+}
+
+// initially returns false, then sets up matches and returns status
+// should be called once first to initialize
+bool isPowerOn(const std::shared_ptr<sdbusplus::asio::connection>& conn)
+{
+ static std::unique_ptr<sdbusplus::bus::match::match> powerMatch = nullptr;
+ static bool powerStatusOn = false;
+
+ if (powerMatch != nullptr)
+ {
+ return powerStatusOn;
+ }
+
+ // create a match for powergood changes, first time do a method call to
+ // return the correct value
+ std::function<void(sdbusplus::message::message & message)> eventHandler =
+ [&powerStatusOn](sdbusplus::message::message& message) {
+ std::string objectName;
+ boost::container::flat_map<std::string,
+ sdbusplus::message::variant<int32_t>>
+ values;
+ message.read(objectName, values);
+ auto findPgood = values.find("pgood");
+ if (findPgood != values.end())
+ {
+ powerStatusOn = sdbusplus::message::variant_ns::get<int32_t>(
+ findPgood->second);
+ }
+ };
+
+ powerMatch = std::make_unique<sdbusplus::bus::match::match>(
+ static_cast<sdbusplus::bus::bus&>(*conn),
+ "type='signal',interface='org.freedesktop.DBus.Properties',path_"
+ "namespace='/xyz/openbmc_project/Chassis/Control/"
+ "power0',arg0='xyz.openbmc_project.Chassis.Control.Power'",
+ eventHandler);
+
+ conn->async_method_call(
+ [&powerStatusOn](boost::system::error_code ec,
+ const sdbusplus::message::variant<int32_t>& pgood) {
+ if (ec)
+ {
+ std::cerr << "Error getting initial power status\n";
+ return;
+ }
+ powerStatusOn = sdbusplus::message::variant_ns::get<int32_t>(pgood);
+ },
+ POWER_INTERFACE_NAME, POWER_OBJECT_NAME,
+ "org.freedesktop.DBus.Properties", "Get", "pgood");
+
+ return powerStatusOn;
+}
\ No newline at end of file
diff --git a/sensors/tests/test_HwmonTempSensor.cpp b/sensors/tests/test_HwmonTempSensor.cpp
new file mode 100644
index 0000000..a618720
--- /dev/null
+++ b/sensors/tests/test_HwmonTempSensor.cpp
@@ -0,0 +1,51 @@
+#include <HwmonTempSensor.hpp>
+#include <dbus/connection.hpp>
+#include <nlohmann/json.hpp>
+#include <fstream>
+#include "gtest/gtest.h"
+
+TEST(HwmonTempSensor, TestTMP75)
+{
+ boost::asio::io_service io;
+ auto system_bus =
+ std::make_shared<dbus::connection>(io, dbus::bus::session);
+ dbus::DbusObjectServer object_server(system_bus);
+
+ std::vector<thresholds::Threshold> sensor_thresholds;
+ auto t = thresholds::Threshold(thresholds::Level::CRITICAL,
+ thresholds::Direction::LOW, 80);
+ sensor_thresholds.emplace_back(t);
+
+ std::ofstream test_file("test0.txt");
+ test_file << "28\n";
+ test_file.close();
+ auto filename = std::string("test0.txt");
+ auto tempsensname = std::string("test sensor");
+ HwmonTempSensor test(filename, object_server, io, tempsensname,
+ std::move(sensor_thresholds));
+
+ std::remove("test0.txt");
+}
+
+TEST(HwmonTempSensor, TestTMP421)
+{
+ boost::asio::io_service io;
+ auto system_bus =
+ std::make_shared<dbus::connection>(io, dbus::bus::session);
+ dbus::DbusObjectServer object_server(system_bus);
+
+ std::vector<thresholds::Threshold> sensor_thresholds;
+ auto t = thresholds::Threshold(thresholds::Level::WARNING,
+ thresholds::Direction::HIGH, 80);
+ sensor_thresholds.emplace_back(t);
+
+ std::ofstream test_file("test1.txt");
+ test_file << "28\n";
+ test_file.close();
+ auto filename = std::string("test1.txt");
+ auto tempsensname = std::string("test sensor");
+ HwmonTempSensor test(filename, object_server, io, tempsensname,
+ std::move(sensor_thresholds));
+
+ std::remove("test1.txt");
+}
diff --git a/sensors/tests/test_TachSensor.cpp b/sensors/tests/test_TachSensor.cpp
new file mode 100644
index 0000000..059b18b
--- /dev/null
+++ b/sensors/tests/test_TachSensor.cpp
@@ -0,0 +1,28 @@
+#include <TachSensor.hpp>
+#include <Thresholds.hpp>
+#include <dbus/connection.hpp>
+#include <nlohmann/json.hpp>
+#include <fstream>
+#include "gtest/gtest.h"
+
+TEST(TachSensor, TestTachSensor)
+{
+ boost::asio::io_service io;
+ auto system_bus =
+ std::make_shared<dbus::connection>(io, dbus::bus::session);
+ dbus::DbusObjectServer object_server(system_bus);
+
+ std::vector<thresholds::Threshold> sensor_thresholds;
+ auto t = thresholds::Threshold(thresholds::Level::CRITICAL,
+ thresholds::Direction::LOW, 1000);
+ sensor_thresholds.emplace_back(t);
+
+ std::ofstream test_file("test.txt");
+ test_file << "10000\n";
+ test_file.close();
+ auto filename = std::string("test.txt");
+ auto fanname = std::string("test fan");
+ TachSensor test(filename, object_server, system_bus, io, fanname,
+ std::move(sensor_thresholds));
+ std::remove("test.txt");
+}