Add Intel-specific IPMI sensor commands
Also includes SDR storage commands that are required to support
the 'ipmitool sensor list' command.
Change-Id: Id1830097d93882114085fce723f0b92367b2db48
Signed-off-by: Jason M. Bills <jason.m.bills@linux.intel.com>
diff --git a/CMakeLists.txt b/CMakeLists.txt
index f8344f8..4362cc7 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -2,7 +2,7 @@
cmake_policy (SET CMP0054 NEW)
-option (YOCTO_DEPENDENCIES "Use YOCTO depedencies system" OFF)
+option (YOCTO "Use YOCTO depedencies system" OFF)
include (ExternalProject)
set (CMAKE_CXX_STANDARD 17)
set (CMAKE_CXX_STANDARD_REQUIRED ON)
@@ -12,13 +12,21 @@
project (intel-ipmi-oem CXX)
+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"
+)
+
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)
-if (NOT ${YOCTO_DEPENDENCIES})
+if (NOT YOCTO)
configure_file (CMakeLists.txt.in 3rdparty/CMakeLists.txt)
execute_process (
COMMAND ${CMAKE_COMMAND} -G "${CMAKE_GENERATOR}" .
@@ -34,26 +42,57 @@
endif ()
# sdbusplus
-if (NOT ${YOCTO_DEPENDENCIES})
+if (NOT YOCTO)
include_directories (${CMAKE_BINARY_DIR}/sdbusplus-src)
link_directories (${CMAKE_BINARY_DIR}/sdbusplus-src/.libs)
endif ()
+# phosphor-host-ipmid
+if (NOT YOCTO)
+ include_directories (${CMAKE_BINARY_DIR})
+ # link_directories (${CMAKE_BINARY_DIR}/sdbusplus-src/.libs)
+endif ()
-if (${YOCTO_DEPENDENCIES})
+if (YOCTO)
find_package (PkgConfig REQUIRED)
pkg_check_modules (LOGGING phosphor-logging REQUIRED)
include_directories (${LOGGING_INCLUDE_DIRS})
link_directories (${LOGGING_LIBRARY_DIRS})
+
endif ()
include_directories (${CMAKE_CURRENT_SOURCE_DIR}/include)
-add_library (oemcmds SHARED src/oemcommands.cpp ${CMAKE_BINARY_DIR}/phosphor-host-ipmid-src/utils.cpp)
+option (ENABLE_TEST "Enable Google Test" OFF)
+if (ENABLE_TEST)
+ set (SENSOR_TEST_SRC tests/test_sensorcommands.cpp src/sensorutils.cpp)
+ hunter_add_package (GTest)
+ find_package (GTest CONFIG REQUIRED)
+
+ enable_testing ()
+
+ add_executable (runSensorTests ${SENSOR_TEST_SRC})
+ add_test (NAME test_sensorcommands COMMAND runSensorTests)
+ target_link_libraries (
+ runSensorTests GTest::main GTest::gtest ${CMAKE_THREAD_LIBS_INIT}
+ )
+endif ()
+
+include_directories (${CMAKE_CURRENT_SOURCE_DIR}/include)
+
+add_library (oemcmds SHARED src/oemcommands.cpp src/utils.cpp)
set_target_properties (oemcmds PROPERTIES VERSION "0.1.0")
set_target_properties (oemcmds PROPERTIES SOVERSION "0")
target_link_libraries (oemcmds sdbusplus)
target_link_libraries (oemcmds ${LOGGING_LIBRARIES})
-install (TARGETS oemcmds DESTINATION lib/ipmid-providers)
+add_library (
+ sensorcommands SHARED src/sensorcommands.cpp src/sensorutils.cpp
+ src/utils.cpp src/storagecommands.cpp
+)
+set_target_properties (sensorcommands PROPERTIES VERSION "0.1.0")
+set_target_properties (sensorcommands PROPERTIES SOVERSION "0")
+target_link_libraries (sensorcommands ${CMAKE_THREAD_LIBS_INIT})
+target_link_libraries (sensorcommands sdbusplus)
+install (TARGETS oemcmds sensorcommands DESTINATION lib/ipmid-providers)
diff --git a/CMakeLists.txt.in b/CMakeLists.txt.in
index a2d0136..7a7f146 100644
--- a/CMakeLists.txt.in
+++ b/CMakeLists.txt.in
@@ -8,119 +8,102 @@
# 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
- GIT_TAG 69425eb7d30816c03f88962c44b12b6de5b3cc71
- 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 clean &&
- ./bootstrap.sh &&
- ./configure --prefix=${CMAKE_BINARY_DIR}/prefix CPPFLAGS=-I${CMAKE_BINARY_DIR}/prefix/include/
- --enable-transaction &&
- make && make install
- INSTALL_COMMAND ""
- LOG_DOWNLOAD ON
+ GIT_REPOSITORY https://github.com/openbmc/sdbusplus.git GIT_TAG
+ 69425eb7d30816c03f88962c44b12b6de5b3cc71 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 clean && ./bootstrap.sh
+ && ./configure --prefix=${CMAKE_BINARY_DIR}/prefix
+ CPPFLAGS=-I${CMAKE_BINARY_DIR}/prefix/include/ --enable-transaction && make
+ && make install INSTALL_COMMAND "" LOG_DOWNLOAD ON
)
externalproject_add (
- dbus-interfaces
- PREFIX ${CMAKE_BINARY_DIR}/phosphor-dbus-interfaces
- DEPENDS sdbusplus-project
- GIT_REPOSITORY https://github.com/openbmc/phosphor-dbus-interfaces
- GIT_TAG 4132f4b6b1de57a993af9bd2bcd039957786a227
- SOURCE_DIR ${CMAKE_BINARY_DIR}/phosphor-dbus-interfaces-src
- BINARY_DIR ${CMAKE_BINARY_DIR}/phosphor-dbus-interfaces-build
- CONFIGURE_COMMAND ""
- BUILD_COMMAND
- cd ${CMAKE_BINARY_DIR}/phosphor-dbus-interfaces-src &&
- echo "#!/bin/bash" > build.sh &&
- echo "export PYTHONPATH=${CMAKE_BINARY_DIR}/prefix/lib/python2.7/site-packages:$ENV{PYTHONPATH}" >> build.sh &&
- echo "export PATH=${CMAKE_BINARY_DIR}/prefix/bin:$ENV{PATH}" >> build.sh &&
- echo "export PKG_CONFIG_PATH=${CMAKE_BINARY_DIR}/prefix/lib/pkgconfig" >> build.sh &&
- echo "./bootstrap.sh " >> build.sh &&
- echo "./configure --prefix=${CMAKE_BINARY_DIR}/prefix CPPFLAGS=-I${CMAKE_BINARY_DIR}/prefix/include/ " >> build.sh &&
- echo "make verbose=1" >> build.sh &&
- echo "make install " >> build.sh &&
- chmod 777 build.sh &&
- ./build.sh
- INSTALL_COMMAND ""
- LOG_DOWNLOAD ON
+ dbus-interfaces PREFIX ${CMAKE_BINARY_DIR}/phosphor-dbus-interfaces DEPENDS
+ sdbusplus-project GIT_REPOSITORY
+ https://github.com/openbmc/phosphor-dbus-interfaces GIT_TAG
+ 4132f4b6b1de57a993af9bd2bcd039957786a227 SOURCE_DIR
+ ${CMAKE_BINARY_DIR}/phosphor-dbus-interfaces-src BINARY_DIR
+ ${CMAKE_BINARY_DIR}/phosphor-dbus-interfaces-build CONFIGURE_COMMAND ""
+ BUILD_COMMAND cd ${CMAKE_BINARY_DIR}/phosphor-dbus-interfaces-src && echo
+ "#!/bin/bash" > build.sh && echo
+ "export PYTHONPATH=${CMAKE_BINARY_DIR}/prefix/lib/python2.7/site-packages:$ENV{PYTHONPATH}"
+ >> build.sh && echo "export PATH=${CMAKE_BINARY_DIR}/prefix/bin:$ENV{PATH}"
+ >> build.sh && echo
+ "export PKG_CONFIG_PATH=${CMAKE_BINARY_DIR}/prefix/lib/pkgconfig" >>
+ build.sh && echo "./bootstrap.sh " >> build.sh && echo
+ "./configure --prefix=${CMAKE_BINARY_DIR}/prefix CPPFLAGS=-I${CMAKE_BINARY_DIR}/prefix/include/ "
+ >> build.sh && echo "make verbose=1" >> build.sh && echo "make install " >>
+ build.sh && chmod 777 build.sh && ./build.sh INSTALL_COMMAND "" LOG_DOWNLOAD
+ ON
)
externalproject_add (
- cereal
- GIT_REPOSITORY https://github.com/USCiLab/cereal
- GIT_TAG 51cbda5f30e56c801c07fe3d3aba5d7fb9e6cca4
- SOURCE_DIR "${CMAKE_BINARY_DIR}/cereal-src"
- BINARY_DIR "${CMAKE_BINARY_DIR}/cereal-build"
- CONFIGURE_COMMAND ""
- BUILD_COMMAND ""
- INSTALL_COMMAND
- mkdir -p "${CMAKE_BINARY_DIR}/prefix/include/cereal" &&
- cp -r "${CMAKE_BINARY_DIR}/cereal-src/include/cereal"
- "${CMAKE_BINARY_DIR}/prefix/include"
-)
-
-
-externalproject_add (
- phosphor-logging
- PREFIX ${CMAKE_BINARY_DIR}/phosphor-logging
- DEPENDS cereal sdbusplus-project dbus-interfaces
- GIT_REPOSITORY https://github.com/openbmc/phosphor-logging
- GIT_TAG 477b731ad0fd8c116ffcaa8265a508c9fb112479
- SOURCE_DIR ${CMAKE_BINARY_DIR}/phosphor-logging-src
- BINARY_DIR ${CMAKE_BINARY_DIR}/phosphor-logging-build
- CONFIGURE_COMMAND ""
- BUILD_COMMAND
- cd ${CMAKE_BINARY_DIR}/phosphor-logging-src &&
- echo "#!/bin/bash" > build.sh &&
- echo "export PYTHONPATH=${CMAKE_BINARY_DIR}/prefix/lib/python2.7/site-packages:$ENV{PYTHONPATH}" >> build.sh &&
- echo "export PATH=${CMAKE_BINARY_DIR}/prefix/bin:$ENV{PATH}" >> build.sh &&
- echo "export PKG_CONFIG_PATH=${CMAKE_BINARY_DIR}/prefix/lib/pkgconfig:$ENV{PKG_CONFIG_PATH}" >> build.sh &&
- echo "./bootstrap.sh clean" >> build.sh &&
- echo "./bootstrap.sh" >> build.sh &&
- echo "./configure --prefix=${CMAKE_BINARY_DIR}/prefix CPPFLAGS=-I${CMAKE_BINARY_DIR}/prefix/include/ \
- YAML_DIR=${CMAKE_BINARY_DIR}/prefix/share/phosphor-dbus-yaml/yaml --enable-metadata-processing" >> build.sh &&
- echo "make verbose=1" >> build.sh &&
- echo "make install " >> build.sh &&
- chmod 777 build.sh &&
- ./build.sh
- INSTALL_COMMAND ""
- LOG_DOWNLOAD ON
+ cereal GIT_REPOSITORY https://github.com/USCiLab/cereal GIT_TAG
+ 51cbda5f30e56c801c07fe3d3aba5d7fb9e6cca4 SOURCE_DIR
+ "${CMAKE_BINARY_DIR}/cereal-src" BINARY_DIR
+ "${CMAKE_BINARY_DIR}/cereal-build" CONFIGURE_COMMAND "" BUILD_COMMAND ""
+ INSTALL_COMMAND mkdir -p "${CMAKE_BINARY_DIR}/prefix/include/cereal" && cp
+ -r "${CMAKE_BINARY_DIR}/cereal-src/include/cereal"
+ "${CMAKE_BINARY_DIR}/prefix/include"
)
externalproject_add (
- nlohmann-json
- GIT_REPOSITORY "https://github.com/nlohmann/json.git"
- GIT_TAG d2dd27dc3b8472dbaa7d66f83619b3ebcd9185fe
- SOURCE_DIR "${CMAKE_BINARY_DIR}/nlohmann-json-src"
- BINARY_DIR "${CMAKE_BINARY_DIR}/nlohmann-json-build"
- CONFIGURE_COMMAND ""
- BUILD_COMMAND ""
- INSTALL_COMMAND
- mkdir -p "${CMAKE_BINARY_DIR}/prefix/include/nlohmann" &&
- cp -r "${CMAKE_BINARY_DIR}/nlohmann-json-src/include/nlohmann"
- "${CMAKE_BINARY_DIR}/prefix/include"
+ phosphor-logging PREFIX ${CMAKE_BINARY_DIR}/phosphor-logging DEPENDS cereal
+ sdbusplus-project dbus-interfaces GIT_REPOSITORY
+ https://github.com/openbmc/phosphor-logging GIT_TAG
+ 477b731ad0fd8c116ffcaa8265a508c9fb112479 SOURCE_DIR
+ ${CMAKE_BINARY_DIR}/phosphor-logging-src BINARY_DIR
+ ${CMAKE_BINARY_DIR}/phosphor-logging-build CONFIGURE_COMMAND ""
+ BUILD_COMMAND cd ${CMAKE_BINARY_DIR}/phosphor-logging-src && echo
+ "#!/bin/bash" > build.sh && echo
+ "export PYTHONPATH=${CMAKE_BINARY_DIR}/prefix/lib/python2.7/site-packages:$ENV{PYTHONPATH}"
+ >> build.sh && echo "export PATH=${CMAKE_BINARY_DIR}/prefix/bin:$ENV{PATH}"
+ >> build.sh && echo
+ "export PKG_CONFIG_PATH=${CMAKE_BINARY_DIR}/prefix/lib/pkgconfig:$ENV{PKG_CONFIG_PATH}"
+ >> build.sh && echo "./bootstrap.sh clean" >> build.sh && echo
+ "./bootstrap.sh" >> build.sh && echo
+ "./configure --prefix=${CMAKE_BINARY_DIR}/prefix CPPFLAGS=-I${CMAKE_BINARY_DIR}/prefix/include/ \
+ YAML_DIR=${CMAKE_BINARY_DIR}/prefix/share/phosphor-dbus-yaml/yaml --enable-metadata-processing"
+ >> build.sh && echo "make verbose=1" >> build.sh && echo "make install " >>
+ build.sh && chmod 777 build.sh && ./build.sh INSTALL_COMMAND "" LOG_DOWNLOAD
+ ON
)
externalproject_add (
- host-ipmid
- PREFIX ${CMAKE_BINARY_DIR}/phosphor-host-ipmid
- DEPENDS sdbusplus-project dbus-interfaces nlohmann-json
- GIT_REPOSITORY https://github.com/openbmc/phosphor-host-ipmid
- SOURCE_DIR ${CMAKE_BINARY_DIR}/phosphor-host-ipmid-src
- BINARY_DIR ${CMAKE_BINARY_DIR}/phosphor-host-ipmid-build
- CONFIGURE_COMMAND ""
- BUILD_COMMAND ""
- INSTALL_COMMAND
- mkdir -p "${CMAKE_BINARY_DIR}/prefix/include/host-ipmid" &&
- cp ${CMAKE_BINARY_DIR}/phosphor-host-ipmid-src/host-ipmid/ipmid-api.h ${CMAKE_BINARY_DIR}/prefix/include/host-ipmid &&
- cp ${CMAKE_BINARY_DIR}/phosphor-host-ipmid-src/elog-errors.hpp ${CMAKE_BINARY_DIR}/prefix/include/host-ipmid &&
- cp ${CMAKE_BINARY_DIR}/phosphor-host-ipmid-src/utils.hpp ${CMAKE_BINARY_DIR}/prefix/include/host-ipmid &&
- cp ${CMAKE_BINARY_DIR}/phosphor-host-ipmid-src/types.hpp ${CMAKE_BINARY_DIR}/prefix/include/host-ipmid
- LOG_DOWNLOAD ON
+ nlohmann-json GIT_REPOSITORY "https://github.com/nlohmann/json.git" GIT_TAG
+ d2dd27dc3b8472dbaa7d66f83619b3ebcd9185fe SOURCE_DIR
+ "${CMAKE_BINARY_DIR}/nlohmann-json-src" BINARY_DIR
+ "${CMAKE_BINARY_DIR}/nlohmann-json-build" CONFIGURE_COMMAND "" BUILD_COMMAND
+ "" INSTALL_COMMAND mkdir -p "${CMAKE_BINARY_DIR}/prefix/include/nlohmann" &&
+ cp -r "${CMAKE_BINARY_DIR}/nlohmann-json-src/include/nlohmann"
+ "${CMAKE_BINARY_DIR}/prefix/include"
)
+externalproject_add (
+ host-ipmid PREFIX ${CMAKE_BINARY_DIR}/phosphor-host-ipmid DEPENDS
+ sdbusplus-project dbus-interfaces nlohmann-json GIT_REPOSITORY
+ https://github.com/openbmc/phosphor-host-ipmid SOURCE_DIR
+ ${CMAKE_BINARY_DIR}/phosphor-ipmi-host BINARY_DIR
+ ${CMAKE_BINARY_DIR}/phosphor-host-ipmid-build CONFIGURE_COMMAND ""
+ BUILD_COMMAND "" INSTALL_COMMAND mkdir -p
+ "${CMAKE_BINARY_DIR}/prefix/include/host-ipmid" && cp
+ ${CMAKE_BINARY_DIR}/phosphor-ipmi-host/host-ipmid/ipmid-api.h
+ ${CMAKE_BINARY_DIR}/prefix/include/host-ipmid && cp
+ ${CMAKE_BINARY_DIR}/phosphor-ipmi-host/elog-errors.hpp
+ ${CMAKE_BINARY_DIR}/prefix/include/host-ipmid && cp
+ ${CMAKE_BINARY_DIR}/phosphor-ipmi-host/utils.hpp
+ ${CMAKE_BINARY_DIR}/prefix/include/host-ipmid && cp
+ ${CMAKE_BINARY_DIR}/phosphor-ipmi-host/types.hpp
+ ${CMAKE_BINARY_DIR}/prefix/include/host-ipmid LOG_DOWNLOAD ON
+)
-
+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 mkdir -p
+ "${CMAKE_BINARY_DIR}/prefix/include/" && cp -R
+ ${CMAKE_BINARY_DIR}/boost-src/boost ${CMAKE_BINARY_DIR}/prefix/include
+)
diff --git a/LICENSE b/LICENSE
index 261eeb9..dd04083 100644
--- a/LICENSE
+++ b/LICENSE
@@ -1,201 +1,13 @@
- Apache License
- Version 2.0, January 2004
- http://www.apache.org/licenses/
+Copyright 2018 Intel Corporation
- TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+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
- 1. Definitions.
+ http://www.apache.org/licenses/LICENSE-2.0
- "License" shall mean the terms and conditions for use, reproduction,
- and distribution as defined by Sections 1 through 9 of this document.
-
- "Licensor" shall mean the copyright owner or entity authorized by
- the copyright owner that is granting the License.
-
- "Legal Entity" shall mean the union of the acting entity and all
- other entities that control, are controlled by, or are under common
- control with that entity. For the purposes of this definition,
- "control" means (i) the power, direct or indirect, to cause the
- direction or management of such entity, whether by contract or
- otherwise, or (ii) ownership of fifty percent (50%) or more of the
- outstanding shares, or (iii) beneficial ownership of such entity.
-
- "You" (or "Your") shall mean an individual or Legal Entity
- exercising permissions granted by this License.
-
- "Source" form shall mean the preferred form for making modifications,
- including but not limited to software source code, documentation
- source, and configuration files.
-
- "Object" form shall mean any form resulting from mechanical
- transformation or translation of a Source form, including but
- not limited to compiled object code, generated documentation,
- and conversions to other media types.
-
- "Work" shall mean the work of authorship, whether in Source or
- Object form, made available under the License, as indicated by a
- copyright notice that is included in or attached to the work
- (an example is provided in the Appendix below).
-
- "Derivative Works" shall mean any work, whether in Source or Object
- form, that is based on (or derived from) the Work and for which the
- editorial revisions, annotations, elaborations, or other modifications
- represent, as a whole, an original work of authorship. For the purposes
- of this License, Derivative Works shall not include works that remain
- separable from, or merely link (or bind by name) to the interfaces of,
- the Work and Derivative Works thereof.
-
- "Contribution" shall mean any work of authorship, including
- the original version of the Work and any modifications or additions
- to that Work or Derivative Works thereof, that is intentionally
- submitted to Licensor for inclusion in the Work by the copyright owner
- or by an individual or Legal Entity authorized to submit on behalf of
- the copyright owner. For the purposes of this definition, "submitted"
- means any form of electronic, verbal, or written communication sent
- to the Licensor or its representatives, including but not limited to
- communication on electronic mailing lists, source code control systems,
- and issue tracking systems that are managed by, or on behalf of, the
- Licensor for the purpose of discussing and improving the Work, but
- excluding communication that is conspicuously marked or otherwise
- designated in writing by the copyright owner as "Not a Contribution."
-
- "Contributor" shall mean Licensor and any individual or Legal Entity
- on behalf of whom a Contribution has been received by Licensor and
- subsequently incorporated within the Work.
-
- 2. Grant of Copyright License. Subject to the terms and conditions of
- this License, each Contributor hereby grants to You a perpetual,
- worldwide, non-exclusive, no-charge, royalty-free, irrevocable
- copyright license to reproduce, prepare Derivative Works of,
- publicly display, publicly perform, sublicense, and distribute the
- Work and such Derivative Works in Source or Object form.
-
- 3. Grant of Patent License. Subject to the terms and conditions of
- this License, each Contributor hereby grants to You a perpetual,
- worldwide, non-exclusive, no-charge, royalty-free, irrevocable
- (except as stated in this section) patent license to make, have made,
- use, offer to sell, sell, import, and otherwise transfer the Work,
- where such license applies only to those patent claims licensable
- by such Contributor that are necessarily infringed by their
- Contribution(s) alone or by combination of their Contribution(s)
- with the Work to which such Contribution(s) was submitted. If You
- institute patent litigation against any entity (including a
- cross-claim or counterclaim in a lawsuit) alleging that the Work
- or a Contribution incorporated within the Work constitutes direct
- or contributory patent infringement, then any patent licenses
- granted to You under this License for that Work shall terminate
- as of the date such litigation is filed.
-
- 4. Redistribution. You may reproduce and distribute copies of the
- Work or Derivative Works thereof in any medium, with or without
- modifications, and in Source or Object form, provided that You
- meet the following conditions:
-
- (a) You must give any other recipients of the Work or
- Derivative Works a copy of this License; and
-
- (b) You must cause any modified files to carry prominent notices
- stating that You changed the files; and
-
- (c) You must retain, in the Source form of any Derivative Works
- that You distribute, all copyright, patent, trademark, and
- attribution notices from the Source form of the Work,
- excluding those notices that do not pertain to any part of
- the Derivative Works; and
-
- (d) If the Work includes a "NOTICE" text file as part of its
- distribution, then any Derivative Works that You distribute must
- include a readable copy of the attribution notices contained
- within such NOTICE file, excluding those notices that do not
- pertain to any part of the Derivative Works, in at least one
- of the following places: within a NOTICE text file distributed
- as part of the Derivative Works; within the Source form or
- documentation, if provided along with the Derivative Works; or,
- within a display generated by the Derivative Works, if and
- wherever such third-party notices normally appear. The contents
- of the NOTICE file are for informational purposes only and
- do not modify the License. You may add Your own attribution
- notices within Derivative Works that You distribute, alongside
- or as an addendum to the NOTICE text from the Work, provided
- that such additional attribution notices cannot be construed
- as modifying the License.
-
- You may add Your own copyright statement to Your modifications and
- may provide additional or different license terms and conditions
- for use, reproduction, or distribution of Your modifications, or
- for any such Derivative Works as a whole, provided Your use,
- reproduction, and distribution of the Work otherwise complies with
- the conditions stated in this License.
-
- 5. Submission of Contributions. Unless You explicitly state otherwise,
- any Contribution intentionally submitted for inclusion in the Work
- by You to the Licensor shall be under the terms and conditions of
- this License, without any additional terms or conditions.
- Notwithstanding the above, nothing herein shall supersede or modify
- the terms of any separate license agreement you may have executed
- with Licensor regarding such Contributions.
-
- 6. Trademarks. This License does not grant permission to use the trade
- names, trademarks, service marks, or product names of the Licensor,
- except as required for reasonable and customary use in describing the
- origin of the Work and reproducing the content of the NOTICE file.
-
- 7. Disclaimer of Warranty. Unless required by applicable law or
- agreed to in writing, Licensor provides the Work (and each
- Contributor provides its Contributions) on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
- implied, including, without limitation, any warranties or conditions
- of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
- PARTICULAR PURPOSE. You are solely responsible for determining the
- appropriateness of using or redistributing the Work and assume any
- risks associated with Your exercise of permissions under this License.
-
- 8. Limitation of Liability. In no event and under no legal theory,
- whether in tort (including negligence), contract, or otherwise,
- unless required by applicable law (such as deliberate and grossly
- negligent acts) or agreed to in writing, shall any Contributor be
- liable to You for damages, including any direct, indirect, special,
- incidental, or consequential damages of any character arising as a
- result of this License or out of the use or inability to use the
- Work (including but not limited to damages for loss of goodwill,
- work stoppage, computer failure or malfunction, or any and all
- other commercial damages or losses), even if such Contributor
- has been advised of the possibility of such damages.
-
- 9. Accepting Warranty or Additional Liability. While redistributing
- the Work or Derivative Works thereof, You may choose to offer,
- and charge a fee for, acceptance of support, warranty, indemnity,
- or other liability obligations and/or rights consistent with this
- License. However, in accepting such obligations, You may act only
- on Your own behalf and on Your sole responsibility, not on behalf
- of any other Contributor, and only if You agree to indemnify,
- defend, and hold each Contributor harmless for any liability
- incurred by, or claims asserted against, such Contributor by reason
- of your accepting any such warranty or additional liability.
-
- END OF TERMS AND CONDITIONS
-
- APPENDIX: How to apply the Apache License to your work.
-
- To apply the Apache License to your work, attach the following
- boilerplate notice, with the fields enclosed by brackets "[]"
- replaced with your own identifying information. (Don't include
- the brackets!) The text should be enclosed in the appropriate
- comment syntax for the file format. We also recommend that a
- file or class name and description of purpose be included on the
- same "printed page" as the copyright notice for easier
- identification within third-party archives.
-
- Copyright [yyyy] [name of copyright owner]
-
- 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.
+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.
\ No newline at end of file
diff --git a/cmake-format.json b/cmake-format.json
new file mode 100755
index 0000000..4a68fb7
--- /dev/null
+++ b/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/cmake/HunterGate.cmake b/cmake/HunterGate.cmake
new file mode 100644
index 0000000..97f69cc
--- /dev/null
+++ b/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/include/commandutils.hpp b/include/commandutils.hpp
new file mode 100644
index 0000000..e296015
--- /dev/null
+++ b/include/commandutils.hpp
@@ -0,0 +1,55 @@
+/*
+// Copyright (c) 2017 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 <iostream>
+#include <sdbusplus/bus.hpp>
+
+static constexpr bool debug = false;
+
+inline static void printRegistration(unsigned int netfn, unsigned int cmd)
+{
+ if constexpr (debug)
+ {
+ std::cout << "Registering NetFn:[0x" << std::hex << std::uppercase
+ << netfn << "], Cmd:[0x" << cmd << "]\n";
+ }
+}
+
+inline static void ipmiPrintAndRegister(ipmi_netfn_t netfn, ipmi_cmd_t cmd,
+ ipmi_context_t context,
+ ipmid_callback_t handler,
+ ipmi_cmd_privilege_t priv)
+{
+ printRegistration(netfn, cmd);
+ ipmi_register_callback(netfn, cmd, context, handler, priv);
+}
+
+inline static void printCommand(unsigned int netfn, unsigned int cmd)
+{
+ if constexpr (debug)
+ {
+ std::cout << "Executing NetFn:[0x" << std::hex << std::uppercase
+ << netfn << "], Cmd:[0x" << cmd << "]\n";
+ }
+}
+
+namespace ipmi
+{
+using DbusVariant =
+ sdbusplus::message::variant<std::string, bool, uint8_t, uint16_t, int16_t,
+ uint32_t, int32_t, uint64_t, int64_t, double>;
+} // namespace ipmi
diff --git a/include/sdrutils.hpp b/include/sdrutils.hpp
new file mode 100644
index 0000000..8b93e9b
--- /dev/null
+++ b/include/sdrutils.hpp
@@ -0,0 +1,165 @@
+/*
+// 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 <boost/algorithm/string.hpp>
+#include <boost/container/flat_map.hpp>
+#include <cstring>
+#include <phosphor-logging/log.hpp>
+#include <storagecommands.hpp>
+
+#pragma once
+
+struct CmpStrVersion
+{
+ bool operator()(std::string a, std::string b) const
+ {
+ return strverscmp(a.c_str(), b.c_str()) < 0;
+ }
+};
+
+using SensorSubTree = boost::container::flat_map<
+ std::string,
+ boost::container::flat_map<std::string, std::vector<std::string>>,
+ CmpStrVersion>;
+
+inline static bool getSensorSubtree(SensorSubTree& subtree)
+{
+ sd_bus* bus = NULL;
+ int ret = sd_bus_default_system(&bus);
+ if (ret < 0)
+ {
+ phosphor::logging::log<phosphor::logging::level::ERR>(
+ "Failed to connect to system bus",
+ phosphor::logging::entry("ERRNO=0x%X", -ret));
+ sd_bus_unref(bus);
+ return false;
+ }
+ sdbusplus::bus::bus dbus(bus);
+ auto mapperCall =
+ dbus.new_method_call("xyz.openbmc_project.ObjectMapper",
+ "/xyz/openbmc_project/object_mapper",
+ "xyz.openbmc_project.ObjectMapper", "GetSubTree");
+ static const auto depth = 2;
+ static constexpr std::array<const char*, 3> interfaces = {
+ "xyz.openbmc_project.Sensor.Value",
+ "xyz.openbmc_project.Sensor.Threshold.Warning",
+ "xyz.openbmc_project.Sensor.Threshold.Critical"};
+ mapperCall.append("/xyz/openbmc_project/sensors", depth, interfaces);
+
+ try
+ {
+ auto mapperReply = dbus.call(mapperCall);
+ subtree.clear();
+ mapperReply.read(subtree);
+ }
+ catch (sdbusplus::exception_t&)
+ {
+ phosphor::logging::log<phosphor::logging::level::ERR>(
+ "getSensorSubtree: Error calling mapper");
+ return false;
+ }
+ return true;
+}
+
+struct CmpStr
+{
+ bool operator()(const char* a, const char* b) const
+ {
+ return std::strcmp(a, b) < 0;
+ }
+};
+
+const static boost::container::flat_map<const char*, SensorTypeCodes, CmpStr>
+ sensorTypes{{{"temperature", SensorTypeCodes::temperature},
+ {"voltage", SensorTypeCodes::voltage},
+ {"current", SensorTypeCodes::current},
+ {"fan_tach", SensorTypeCodes::fan},
+ {"power", SensorTypeCodes::other}}};
+
+inline static std::string getSensorTypeStringFromPath(const std::string& path)
+{
+ // get sensor type string from path, path is defined as
+ // /xyz/openbmc_project/sensors/<type>/label
+ size_t typeEnd = path.rfind("/");
+ size_t typeStart = path.rfind("/", typeEnd - 1);
+ if (typeEnd != std::string::npos && typeStart != std::string::npos)
+ {
+ return path.substr(typeStart + 1, typeEnd - typeStart);
+ }
+ return path;
+}
+
+inline static uint8_t getSensorTypeFromPath(const std::string& path)
+{
+ uint8_t sensorType = 0;
+ std::string type = getSensorTypeStringFromPath(path);
+ auto findSensor = sensorTypes.find(type.c_str());
+ if (findSensor != sensorTypes.end())
+ {
+ sensorType = static_cast<uint8_t>(findSensor->second);
+ } // else default 0x0 RESERVED
+
+ return sensorType;
+}
+
+inline static uint8_t getSensorNumberFromPath(const std::string& path)
+{
+ SensorSubTree sensorTree;
+ if (!getSensorSubtree(sensorTree))
+ return 0xFF;
+ uint8_t sensorNum = 0xFF;
+
+ for (const auto& sensor : sensorTree)
+ {
+ sensorNum++;
+ if (sensor.first == path)
+ {
+ break;
+ }
+ }
+ return sensorNum;
+}
+
+inline static uint8_t getSensorEventTypeFromPath(const std::string& path)
+{
+ // TODO: Add support for additional reading types as needed
+ return 0x1; // reading type = threshold
+}
+
+inline static std::string getPathFromSensorNumber(uint8_t sensorNum)
+{
+ SensorSubTree sensorTree;
+ std::string path;
+ if (!getSensorSubtree(sensorTree))
+ return path;
+
+ if (sensorTree.size() < sensorNum)
+ {
+ return path;
+ }
+
+ uint8_t sensorIndex = sensorNum;
+ for (const auto& sensor : sensorTree)
+ {
+ if (sensorIndex-- == 0)
+ {
+ path = sensor.first;
+ break;
+ }
+ }
+
+ return path;
+}
diff --git a/include/sensorcommands.hpp b/include/sensorcommands.hpp
new file mode 100644
index 0000000..691fb11
--- /dev/null
+++ b/include/sensorcommands.hpp
@@ -0,0 +1,134 @@
+/*
+// Copyright (c) 2017 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 <cstdint>
+
+#pragma pack(push, 1)
+struct SensorReadingResp
+{
+ uint8_t value;
+ uint8_t operation;
+ uint8_t indication[2];
+};
+
+struct SensorThresholdResp
+{
+ uint8_t readable;
+ uint8_t lowernc;
+ uint8_t lowercritical;
+ uint8_t lowernonrecoverable;
+ uint8_t uppernc;
+ uint8_t uppercritical;
+ uint8_t uppernonrecoverable;
+};
+
+struct SensorThresholdReq
+{
+ uint8_t sensorNum;
+ uint8_t mask;
+ uint8_t lowerNonCritical;
+ uint8_t lowerCritical;
+ uint8_t lowerNonRecoverable;
+ uint8_t upperNonCritical;
+ uint8_t upperCritical;
+ uint8_t upperNonRecoverable;
+};
+#pragma pack(pop)
+
+enum class SensorThresholdReqEnable : uint8_t
+{
+ setLowerNonCritical = 0x1,
+ setLowerCritical = 0x2,
+ setLowerNonRecoverable = 0x4,
+ setUpperNonCritical = 0x8,
+ setUpperCritical = 0x10,
+ setUpperNonRecoverable = 0x20
+};
+
+#pragma pack(push, 1)
+struct SensorEventEnableResp
+{
+ uint8_t enabled;
+ uint8_t assertionEnabledLSB;
+ uint8_t assertionEnabledMSB;
+ uint8_t deassertionEnabledLSB;
+ uint8_t deassertionEnabledMSB;
+};
+
+struct SensorEventStatusResp
+{
+ uint8_t enabled;
+ uint8_t assertionsLSB;
+ uint8_t assertionsMSB;
+ // deassertion events currently not supported
+ // uint8_t deassertionsLSB;
+ // uint8_t deassertionsMSB;
+};
+#pragma pack(pop)
+
+enum class IPMIhresholdRespBits
+{
+ lowerNonCritical,
+ lowerCritical,
+ lowerNonRecoverable,
+ upperNonCritical,
+ upperCritical,
+ upperNonRecoverable
+};
+
+enum class IPMISensorReadingByte2 : uint8_t
+{
+ eventMessagesEnable = (1 << 7),
+ sensorScanningEnable = (1 << 6),
+ readingStateUnavailable = (1 << 5),
+};
+
+enum class IPMISensorEventEnableByte2 : uint8_t
+{
+ eventMessagesEnable = (1 << 7),
+ sensorScanningEnable = (1 << 6),
+};
+
+enum class IPMISensorEventEnableThresholds : uint8_t
+{
+ upperNonRecoverableGoingHigh = (1 << 3),
+ upperNonRecoverableGoingLow = (1 << 2),
+ upperCriticalGoingHigh = (1 << 1),
+ upperCriticalGoingLow = (1 << 0),
+ upperNonCriticalGoingHigh = (1 << 7),
+ upperNonCriticalGoingLow = (1 << 6),
+ lowerNonRecoverableGoingHigh = (1 << 5),
+ lowerNonRecoverableGoingLow = (1 << 4),
+ lowerCriticalGoingHigh = (1 << 3),
+ lowerCriticalGoingLow = (1 << 2),
+ lowerNonCriticalGoingHigh = (1 << 1),
+ lowerNonCriticalGoingLow = (1 << 0),
+};
+
+enum class IPMINetfnSensorCmds : ipmi_cmd_t
+{
+ ipmiCmdGetDeviceSDRInfo = 0x20,
+ ipmiCmdGetDeviceSDR = 0x21,
+ ipmiCmdReserveDeviceSDRRepo = 0x22,
+ ipmiCmdGetSensorThreshold = 0x27,
+ ipmiCmdSetSensorThreshold = 0x28,
+ ipmiCmdGetSensorEventEnable = 0x29,
+ ipmiCmdGetSensorEventStatus = 0x2B,
+ ipmiCmdGetSensorReading = 0x2D,
+ ipmiCmdGetSensorType = 0x2F,
+ ipmiCmdSetSensorReadingAndEventStatus = 0x30,
+};
diff --git a/include/sensorutils.hpp b/include/sensorutils.hpp
new file mode 100644
index 0000000..7295a4b
--- /dev/null
+++ b/include/sensorutils.hpp
@@ -0,0 +1,28 @@
+/*
+// Copyright (c) 2017 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 <cstdint>
+
+namespace ipmi
+{
+bool getSensorAttributes(const double maxValue, const double minValue,
+ int16_t &mValue, int8_t &rExp, int16_t &bValue,
+ int8_t &bExp, bool &bSigned);
+uint8_t scaleIPMIValueFromDouble(const double value, const uint16_t mValue,
+ const int8_t rExp, const uint16_t bValue,
+ const int8_t bExp, const bool bSigned);
+} // namespace ipmi
\ No newline at end of file
diff --git a/include/storagecommands.hpp b/include/storagecommands.hpp
new file mode 100644
index 0000000..ef32c74
--- /dev/null
+++ b/include/storagecommands.hpp
@@ -0,0 +1,118 @@
+/*
+// Copyright (c) 2017 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 <cstdint>
+#include <phosphor-ipmi-host/sensorhandler.hpp>
+
+static constexpr uint8_t ipmiSdrVersion = 0x51;
+
+#pragma pack(push, 1)
+struct GetSDRInfoResp
+{
+ uint8_t sdrVersion;
+ uint8_t recordCountLS;
+ uint8_t recordCountMS;
+ uint8_t freeSpace[2];
+ uint32_t mostRecentAddition;
+ uint32_t mostRecentErase;
+ uint8_t operationSupport;
+};
+
+struct GetSDRReq
+{
+ uint16_t reservationID;
+ uint16_t recordID;
+ uint8_t offset;
+ uint8_t bytesToRead;
+};
+#pragma pack(pop)
+
+enum class SdrRepositoryInfoOps : uint8_t
+{
+ allocCommandSupported = 0x1,
+ reserveSDRRepositoryCommandSupported = 0x2,
+ partialAddSDRSupported = 0x4,
+ deleteSDRSupported = 0x8,
+ reserved = 0x10,
+ modalLSB = 0x20,
+ modalMSB = 0x40,
+ overflow = 0x80
+};
+
+#pragma pack(push, 1)
+struct GetAllocInfoResp
+{
+ uint8_t allocUnitsLSB;
+ uint8_t allocUnitsMSB;
+ uint8_t allocUnitSizeLSB;
+ uint8_t allocUnitSizeMSB;
+ uint8_t allocUnitFreeLSB;
+ uint8_t allocUnitFreeMSB;
+ uint8_t allocUnitLargestFreeLSB;
+ uint8_t allocUnitLargestFreeMSB;
+ uint8_t maxRecordSize;
+};
+#pragma pack(pop)
+
+enum class SensorTypeCodes : uint8_t
+{
+ reserved = 0x0,
+ temperature = 0x1,
+ voltage = 0x2,
+ current = 0x3,
+ fan = 0x4,
+ other = 0xB,
+};
+
+enum class SensorUnits : uint8_t
+{
+ unspecified = 0x0,
+ degreesC = 0x1,
+ volts = 0x4,
+ amps = 0x5,
+ watts = 0x6,
+ rpm = 0x12,
+};
+
+enum class IPMINetfnStorageCmds : ipmi_cmd_t
+{
+ ipmiCmdGetFRUInvAreaInfo = 0x10,
+ ipmiCmdReadFRUData = 0x11,
+ ipmiCmdWriteFRUData = 0x12,
+ ipmiCmdGetRepositoryInfo = 0x20,
+ ipmiCmdGetSDRAllocationInfo = 0x21,
+ ipmiCmdReserveSDR = 0x22,
+ ipmiCmdGetSDR = 0x23,
+ ipmiCmdGetSELInfo = 0x40,
+ ipmiCmdReserveSEL = 0x42,
+ ipmiCmdGetSELEntry = 0x43,
+ ipmiCmdAddSEL = 0x44,
+ ipmiCmdDeleteSEL = 0x46,
+ ipmiCmdClearSEL = 0x47,
+ ipmiCmdGetSELTime = 0x48,
+ ipmiCmdSetSELTime = 0x49,
+};
+
+namespace ipmi
+{
+namespace storage
+{
+ipmi_ret_t getFruSdrs(size_t index, get_sdr::SensorDataFruRecord& resp);
+
+ipmi_ret_t getFruSdrCount(size_t& count);
+} // namespace storage
+} // namespace ipmi
diff --git a/src/oemcommands.cpp b/src/oemcommands.cpp
index e6eddcc..6b38579 100644
--- a/src/oemcommands.cpp
+++ b/src/oemcommands.cpp
@@ -17,9 +17,9 @@
#include <host-ipmid/ipmid-api.h>
#include <array>
-#include <host-ipmid/utils.hpp>
#include <iostream>
#include <oemcommands.hpp>
+#include <phosphor-ipmi-host/utils.hpp>
#include <phosphor-logging/log.hpp>
#include <sdbusplus/bus.hpp>
#include <sstream>
diff --git a/src/sensorcommands.cpp b/src/sensorcommands.cpp
new file mode 100644
index 0000000..3c1f050
--- /dev/null
+++ b/src/sensorcommands.cpp
@@ -0,0 +1,1228 @@
+/*
+// Copyright (c) 2017 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 <host-ipmid/ipmid-api.h>
+
+#include <boost/algorithm/string.hpp>
+#include <boost/container/flat_map.hpp>
+#include <chrono>
+#include <cmath>
+#include <commandutils.hpp>
+#include <iostream>
+#include <phosphor-ipmi-host/utils.hpp>
+#include <phosphor-logging/log.hpp>
+#include <sdbusplus/bus.hpp>
+#include <sdrutils.hpp>
+#include <sensorcommands.hpp>
+#include <sensorutils.hpp>
+#include <storagecommands.hpp>
+#include <string>
+
+namespace ipmi
+{
+using ManagedObjectType =
+ std::map<sdbusplus::message::object_path,
+ std::map<std::string, std::map<std::string, DbusVariant>>>;
+
+using SensorMap = std::map<std::string, std::map<std::string, DbusVariant>>;
+
+static constexpr int sensorListUpdatePeriod = 10;
+static constexpr int sensorMapUpdatePeriod = 2;
+
+constexpr size_t maxSDRTotalSize =
+ 76; // Largest SDR Record Size (type 01) + SDR Overheader Size
+constexpr static const uint32_t noTimestamp = 0xFFFFFFFF;
+
+static uint16_t sdrReservationID;
+static uint32_t sdrLastAdd = noTimestamp;
+static uint32_t sdrLastRemove = noTimestamp;
+
+static SensorSubTree sensorTree;
+static boost::container::flat_map<std::string, ManagedObjectType> SensorCache;
+
+const static boost::container::flat_map<const char *, SensorUnits> sensorUnits{
+ {{"temperature", SensorUnits::degreesC},
+ {"voltage", SensorUnits::volts},
+ {"current", SensorUnits::amps},
+ {"fan_tach", SensorUnits::rpm},
+ {"power", SensorUnits::watts}}};
+
+void registerSensorFunctions() __attribute__((constructor));
+static sdbusplus::bus::bus dbus(ipmid_get_sd_bus_connection());
+
+static sdbusplus::bus::match::match sensorAdded(
+ dbus,
+ "type='signal',member='InterfacesAdded',arg0path='/xyz/openbmc_project/"
+ "sensors/'",
+ [](sdbusplus::message::message &m) {
+ sensorTree.clear();
+ sdrLastAdd = std::chrono::duration_cast<std::chrono::seconds>(
+ std::chrono::system_clock::now().time_since_epoch())
+ .count();
+ });
+
+static sdbusplus::bus::match::match sensorRemoved(
+ dbus,
+ "type='signal',member='InterfacesRemoved',arg0path='/xyz/openbmc_project/"
+ "sensors/'",
+ [](sdbusplus::message::message &m) {
+ sensorTree.clear();
+ sdrLastRemove = std::chrono::duration_cast<std::chrono::seconds>(
+ std::chrono::system_clock::now().time_since_epoch())
+ .count();
+ });
+
+static void
+ getSensorMaxMin(const std::map<std::string, DbusVariant> &sensorPropertyMap,
+ double &max, double &min)
+{
+ auto maxMap = sensorPropertyMap.find("MaxValue");
+ auto minMap = sensorPropertyMap.find("MinValue");
+ max = 127;
+ min = -128;
+
+ if (maxMap != sensorPropertyMap.end())
+ {
+ max = apply_visitor(VariantToDoubleVisitor(), maxMap->second);
+ }
+ if (minMap != sensorPropertyMap.end())
+ {
+ min = apply_visitor(VariantToDoubleVisitor(), minMap->second);
+ }
+}
+
+static ipmi_ret_t getSensorConnection(uint8_t sensnum, std::string &connection,
+ std::string &path)
+{
+ if (sensorTree.empty() && !getSensorSubtree(sensorTree))
+ {
+ return IPMI_CC_RESPONSE_ERROR;
+ }
+
+ if (sensorTree.size() < (sensnum + 1))
+ {
+ return IPMI_CC_INVALID_FIELD_REQUEST;
+ }
+
+ uint8_t sensorIndex = sensnum;
+ for (const auto &sensor : sensorTree)
+ {
+ if (sensorIndex-- == 0)
+ {
+ if (!sensor.second.size())
+ {
+ return IPMI_CC_RESPONSE_ERROR;
+ }
+ connection = sensor.second.begin()->first;
+ path = sensor.first;
+ break;
+ }
+ }
+
+ return 0;
+}
+
+static bool getSensorMap(std::string sensorConnection, std::string sensorPath,
+ SensorMap &sensorMap)
+{
+ static boost::container::flat_map<
+ std::string, std::chrono::time_point<std::chrono::steady_clock>>
+ updateTimeMap;
+
+ auto updateFind = updateTimeMap.find(sensorConnection);
+ auto lastUpdate = std::chrono::time_point<std::chrono::steady_clock>();
+ if (updateFind != updateTimeMap.end())
+ {
+ lastUpdate = updateFind->second;
+ }
+
+ auto now = std::chrono::steady_clock::now();
+
+ if (std::chrono::duration_cast<std::chrono::seconds>(now - lastUpdate)
+ .count() > sensorMapUpdatePeriod)
+ {
+ updateTimeMap[sensorConnection] = now;
+
+ auto managedObj = dbus.new_method_call(
+ sensorConnection.c_str(), "/", "org.freedesktop.DBus.ObjectManager",
+ "GetManagedObjects");
+
+ ManagedObjectType managedObjects;
+ try
+ {
+ auto reply = dbus.call(managedObj);
+ reply.read(managedObjects);
+ }
+ catch (sdbusplus::exception_t &)
+ {
+ phosphor::logging::log<phosphor::logging::level::ERR>(
+ "Error getting managed objects from connection",
+ phosphor::logging::entry("CONNECTION=%s",
+ sensorConnection.c_str()));
+ return false;
+ }
+
+ SensorCache[sensorConnection] = managedObjects;
+ }
+ auto connection = SensorCache.find(sensorConnection);
+ if (connection == SensorCache.end())
+ {
+ return false;
+ }
+ auto path = connection->second.find(sensorPath);
+ if (path == connection->second.end())
+ {
+ return false;
+ }
+ sensorMap = path->second;
+
+ return true;
+}
+
+/* sensor commands */
+ipmi_ret_t ipmiSensorWildcardHandler(ipmi_netfn_t netfn, ipmi_cmd_t cmd,
+ ipmi_request_t request,
+ ipmi_response_t response,
+ ipmi_data_len_t dataLen,
+ ipmi_context_t context)
+{
+ *dataLen = 0;
+ printCommand(+netfn, +cmd);
+ return IPMI_CC_INVALID;
+}
+
+ipmi_ret_t ipmiSenGetSensorReading(ipmi_netfn_t netfn, ipmi_cmd_t cmd,
+ ipmi_request_t request,
+ ipmi_response_t response,
+ ipmi_data_len_t dataLen,
+ ipmi_context_t context)
+{
+ if (*dataLen != 1)
+ {
+ *dataLen = 0;
+ return IPMI_CC_REQ_DATA_LEN_INVALID;
+ }
+ *dataLen = 0; // default to 0 in case of an error
+
+ uint8_t sensnum = *(static_cast<uint8_t *>(request));
+
+ std::string connection;
+ std::string path;
+
+ auto status = getSensorConnection(sensnum, connection, path);
+ if (status)
+ {
+ return status;
+ }
+
+ SensorMap sensorMap;
+ if (!getSensorMap(connection, path, sensorMap))
+ {
+ return IPMI_CC_RESPONSE_ERROR;
+ }
+ auto sensorObject = sensorMap.find("xyz.openbmc_project.Sensor.Value");
+
+ if (sensorObject == sensorMap.end() ||
+ sensorObject->second.find("Value") == sensorObject->second.end())
+ {
+ return IPMI_CC_RESPONSE_ERROR;
+ }
+ auto &value = sensorObject->second["Value"];
+ double reading = apply_visitor(VariantToDoubleVisitor(), value);
+
+ double max;
+ double min;
+ getSensorMaxMin(sensorObject->second, max, min);
+
+ int16_t mValue = 0;
+ int16_t bValue = 0;
+ int8_t rExp = 0;
+ int8_t bExp = 0;
+ bool bSigned = false;
+
+ if (!getSensorAttributes(max, min, mValue, rExp, bValue, bExp, bSigned))
+ {
+ return IPMI_CC_RESPONSE_ERROR;
+ }
+
+ SensorReadingResp *msgReply = static_cast<SensorReadingResp *>(response);
+ *dataLen = sizeof(SensorReadingResp);
+
+ msgReply->value =
+ scaleIPMIValueFromDouble(reading, mValue, rExp, bValue, bExp, bSigned);
+ msgReply->operation =
+ static_cast<uint8_t>(IPMISensorReadingByte2::sensorScanningEnable);
+ msgReply->indication[0] = 0; // ignore for non-threshold sensors
+ msgReply->indication[1] = 0;
+
+ return IPMI_CC_OK;
+}
+
+ipmi_ret_t ipmiSenSetSensorThresholds(ipmi_netfn_t netfn, ipmi_cmd_t cmd,
+ ipmi_request_t request,
+ ipmi_response_t response,
+ ipmi_data_len_t dataLen,
+ ipmi_context_t context)
+{
+ if (*dataLen != 8)
+ {
+ *dataLen = 0;
+ return IPMI_CC_REQ_DATA_LEN_INVALID;
+ }
+ *dataLen = 0;
+
+ SensorThresholdReq *req = static_cast<SensorThresholdReq *>(request);
+
+ // upper two bits reserved
+ if (req->mask & 0xC0)
+ {
+ return IPMI_CC_INVALID_FIELD_REQUEST;
+ }
+
+ // lower nc and upper nc not suppported on any sensor
+ if ((req->mask & static_cast<uint8_t>(
+ SensorThresholdReqEnable::setLowerNonRecoverable)) ||
+ (req->mask & static_cast<uint8_t>(
+ SensorThresholdReqEnable::setUpperNonRecoverable)))
+ {
+ return IPMI_CC_INVALID_FIELD_REQUEST;
+ }
+
+ // if no bits are set in the mask, nothing to do
+ if (!(req->mask))
+ {
+ return IPMI_CC_OK;
+ }
+
+ std::string connection;
+ std::string path;
+
+ ipmi_ret_t status = getSensorConnection(req->sensorNum, connection, path);
+ if (status)
+ {
+ return status;
+ }
+ SensorMap sensorMap;
+ if (!getSensorMap(connection, path, sensorMap))
+ {
+ return IPMI_CC_RESPONSE_ERROR;
+ }
+
+ auto sensorObject = sensorMap.find("xyz.openbmc_project.Sensor.Value");
+
+ if (sensorObject == sensorMap.end())
+ {
+ return IPMI_CC_RESPONSE_ERROR;
+ }
+ double max = 0;
+ double min = 0;
+ getSensorMaxMin(sensorObject->second, max, min);
+
+ int16_t mValue = 0;
+ int16_t bValue = 0;
+ int8_t rExp = 0;
+ int8_t bExp = 0;
+ bool bSigned = false;
+
+ if (!getSensorAttributes(max, min, mValue, rExp, bValue, bExp, bSigned))
+ {
+ return IPMI_CC_RESPONSE_ERROR;
+ }
+
+ bool setLowerCritical =
+ req->mask &
+ static_cast<uint8_t>(SensorThresholdReqEnable::setLowerCritical);
+ bool setUpperCritical =
+ req->mask &
+ static_cast<uint8_t>(SensorThresholdReqEnable::setUpperCritical);
+
+ bool setLowerWarning =
+ req->mask &
+ static_cast<uint8_t>(SensorThresholdReqEnable::setLowerNonCritical);
+ bool setUpperWarning =
+ req->mask &
+ static_cast<uint8_t>(SensorThresholdReqEnable::setUpperNonCritical);
+
+ // store a vector of property name, value to set, and interface
+ std::vector<std::tuple<std::string, uint8_t, std::string>> thresholdsToSet;
+
+ // define the indexes of the tuple
+ constexpr uint8_t propertyName = 0;
+ constexpr uint8_t thresholdValue = 1;
+ constexpr uint8_t interface = 2;
+ // verifiy all needed fields are present
+ if (setLowerCritical || setUpperCritical)
+ {
+ auto findThreshold =
+ sensorMap.find("xyz.openbmc_project.Sensor.Threshold.Critical");
+ if (findThreshold == sensorMap.end())
+ {
+ return IPMI_CC_INVALID_FIELD_REQUEST;
+ }
+ if (setLowerCritical)
+ {
+ auto findLower = findThreshold->second.find("CriticalLow");
+ if (findLower == findThreshold->second.end())
+ {
+ return IPMI_CC_INVALID_FIELD_REQUEST;
+ }
+ thresholdsToSet.emplace_back("CriticalLow", req->lowerCritical,
+ findThreshold->first);
+ }
+ if (setUpperCritical)
+ {
+ auto findUpper = findThreshold->second.find("CriticalHigh");
+ if (findUpper == findThreshold->second.end())
+ {
+ return IPMI_CC_INVALID_FIELD_REQUEST;
+ }
+ thresholdsToSet.emplace_back("CriticalHigh", req->upperCritical,
+ findThreshold->first);
+ }
+ }
+ if (setLowerWarning || setUpperWarning)
+ {
+ auto findThreshold =
+ sensorMap.find("xyz.openbmc_project.Sensor.Threshold.Warning");
+ if (findThreshold == sensorMap.end())
+ {
+ return IPMI_CC_INVALID_FIELD_REQUEST;
+ }
+ if (setLowerWarning)
+ {
+ auto findLower = findThreshold->second.find("WarningLow");
+ if (findLower == findThreshold->second.end())
+ {
+ return IPMI_CC_INVALID_FIELD_REQUEST;
+ }
+ thresholdsToSet.emplace_back("WarningLow", req->lowerNonCritical,
+ findThreshold->first);
+ }
+ if (setUpperWarning)
+ {
+ auto findUpper = findThreshold->second.find("WarningHigh");
+ if (findUpper == findThreshold->second.end())
+ {
+ return IPMI_CC_INVALID_FIELD_REQUEST;
+ }
+ thresholdsToSet.emplace_back("WarningHigh", req->upperNonCritical,
+ findThreshold->first);
+ }
+ }
+
+ for (const auto &property : thresholdsToSet)
+ {
+ // from section 36.3 in the IPMI Spec, assume all linear
+ double valueToSet = ((mValue * std::get<thresholdValue>(property)) +
+ (bValue * std::pow(10, bExp))) *
+ std::pow(10, rExp);
+ setDbusProperty(dbus, connection, path, std::get<interface>(property),
+ std::get<propertyName>(property),
+ ipmi::Value(valueToSet));
+ }
+
+ return IPMI_CC_OK;
+}
+
+ipmi_ret_t ipmiSenGetSensorThresholds(ipmi_netfn_t netfn, ipmi_cmd_t cmd,
+ ipmi_request_t request,
+ ipmi_response_t response,
+ ipmi_data_len_t dataLen,
+ ipmi_context_t context)
+{
+ if (*dataLen != 1)
+ {
+ *dataLen = 0;
+ return IPMI_CC_REQ_DATA_LEN_INVALID;
+ }
+ *dataLen = 0; // default to 0 in case of an error
+
+ uint8_t sensnum = *(static_cast<uint8_t *>(request));
+
+ std::string connection;
+ std::string path;
+
+ auto status = getSensorConnection(sensnum, connection, path);
+ if (status)
+ {
+ return status;
+ }
+
+ SensorMap sensorMap;
+ if (!getSensorMap(connection, path, sensorMap))
+ {
+ return IPMI_CC_RESPONSE_ERROR;
+ }
+
+ // zero out response buff
+ auto responseClear = static_cast<uint8_t *>(response);
+ std::fill(responseClear, responseClear + sizeof(SensorThresholdResp), 0);
+
+ auto warningInterface =
+ sensorMap.find("xyz.openbmc_project.Sensor.Threshold.Warning");
+ auto criticalInterface =
+ sensorMap.find("xyz.openbmc_project.Sensor.Threshold.Critical");
+
+ if ((warningInterface != sensorMap.end()) ||
+ (criticalInterface != sensorMap.end()))
+ {
+ auto sensorPair = sensorMap.find("xyz.openbmc_project.Sensor.Value");
+
+ if (sensorPair == sensorMap.end())
+ {
+ // should not have been able to find a sensor not implementing
+ // the sensor object
+ return IPMI_CC_RESPONSE_ERROR;
+ }
+
+ double max;
+ double min;
+ getSensorMaxMin(sensorPair->second, max, min);
+
+ int16_t mValue = 0;
+ int16_t bValue = 0;
+ int8_t rExp = 0;
+ int8_t bExp = 0;
+ bool bSigned = false;
+
+ if (!getSensorAttributes(max, min, mValue, rExp, bValue, bExp, bSigned))
+ {
+ return IPMI_CC_RESPONSE_ERROR;
+ }
+
+ auto msgReply = static_cast<SensorThresholdResp *>(response);
+
+ if (warningInterface != sensorMap.end())
+ {
+ auto &warningMap = warningInterface->second;
+
+ auto warningHigh = warningMap.find("WarningHigh");
+ auto warningLow = warningMap.find("WarningLow");
+
+ if (warningHigh != warningMap.end())
+ {
+ msgReply->readable |=
+ 1 << static_cast<int>(
+ IPMIhresholdRespBits::upperNonCritical);
+ double value = apply_visitor(VariantToDoubleVisitor(),
+ warningHigh->second);
+ msgReply->uppernc = scaleIPMIValueFromDouble(
+ value, mValue, rExp, bValue, bExp, bSigned);
+ }
+ if (warningLow != warningMap.end())
+ {
+ msgReply->readable |=
+ 1 << static_cast<int>(
+ IPMIhresholdRespBits::lowerNonCritical);
+ double value =
+ apply_visitor(VariantToDoubleVisitor(), warningLow->second);
+ msgReply->lowernc = scaleIPMIValueFromDouble(
+ value, mValue, rExp, bValue, bExp, bSigned);
+ }
+ }
+ if (criticalInterface != sensorMap.end())
+ {
+ auto &criticalMap = criticalInterface->second;
+
+ auto criticalHigh = criticalMap.find("CriticalHigh");
+ auto criticalLow = criticalMap.find("CriticalLow");
+
+ if (criticalHigh != criticalMap.end())
+ {
+ msgReply->readable |=
+ 1 << static_cast<int>(IPMIhresholdRespBits::upperCritical);
+ double value = apply_visitor(VariantToDoubleVisitor(),
+ criticalHigh->second);
+ msgReply->uppercritical = scaleIPMIValueFromDouble(
+ value, mValue, rExp, bValue, bExp, bSigned);
+ }
+ if (criticalLow != criticalMap.end())
+ {
+ msgReply->readable |=
+ 1 << static_cast<int>(IPMIhresholdRespBits::lowerCritical);
+ double value = apply_visitor(VariantToDoubleVisitor(),
+ criticalLow->second);
+ msgReply->lowercritical = scaleIPMIValueFromDouble(
+ value, mValue, rExp, bValue, bExp, bSigned);
+ }
+ }
+ }
+
+ *dataLen = sizeof(SensorThresholdResp);
+ return IPMI_CC_OK;
+}
+
+ipmi_ret_t ipmiSenGetSensorEventEnable(ipmi_netfn_t netfn, ipmi_cmd_t cmd,
+ ipmi_request_t request,
+ ipmi_response_t response,
+ ipmi_data_len_t dataLen,
+ ipmi_context_t context)
+{
+ if (*dataLen != 1)
+ {
+ *dataLen = 0;
+ return IPMI_CC_REQ_DATA_LEN_INVALID;
+ }
+ *dataLen = 0; // default to 0 in case of an error
+
+ uint8_t sensnum = *(static_cast<uint8_t *>(request));
+
+ std::string connection;
+ std::string path;
+
+ auto status = getSensorConnection(sensnum, connection, path);
+ if (status)
+ {
+ return status;
+ }
+
+ SensorMap sensorMap;
+ if (!getSensorMap(connection, path, sensorMap))
+ {
+ return IPMI_CC_RESPONSE_ERROR;
+ }
+
+ auto warningInterface =
+ sensorMap.find("xyz.openbmc_project.Sensor.Threshold.Warning");
+ auto criticalInterface =
+ sensorMap.find("xyz.openbmc_project.Sensor.Threshold.Critical");
+
+ if ((warningInterface != sensorMap.end()) ||
+ (criticalInterface != sensorMap.end()))
+ {
+ // zero out response buff
+ auto responseClear = static_cast<uint8_t *>(response);
+ std::fill(responseClear, responseClear + sizeof(SensorEventEnableResp),
+ 0);
+
+ // assume all threshold sensors
+ auto resp = static_cast<SensorEventEnableResp *>(response);
+
+ resp->enabled = static_cast<uint8_t>(
+ IPMISensorEventEnableByte2::sensorScanningEnable);
+ if (warningInterface != sensorMap.end())
+ {
+ auto &warningMap = warningInterface->second;
+
+ auto warningHigh = warningMap.find("WarningHigh");
+ auto warningLow = warningMap.find("WarningLow");
+ if (warningHigh != warningMap.end())
+ {
+ resp->assertionEnabledLSB |= static_cast<uint8_t>(
+ IPMISensorEventEnableThresholds::upperNonCriticalGoingHigh);
+ resp->deassertionEnabledLSB |= static_cast<uint8_t>(
+ IPMISensorEventEnableThresholds::upperNonCriticalGoingLow);
+ }
+ if (warningLow != warningMap.end())
+ {
+ resp->assertionEnabledLSB |= static_cast<uint8_t>(
+ IPMISensorEventEnableThresholds::lowerNonCriticalGoingLow);
+ resp->deassertionEnabledLSB |= static_cast<uint8_t>(
+ IPMISensorEventEnableThresholds::lowerNonCriticalGoingHigh);
+ }
+ }
+ if (criticalInterface != sensorMap.end())
+ {
+ auto &criticalMap = criticalInterface->second;
+
+ auto criticalHigh = criticalMap.find("CriticalHigh");
+ auto criticalLow = criticalMap.find("CriticalLow");
+
+ if (criticalHigh != criticalMap.end())
+ {
+ resp->assertionEnabledMSB |= static_cast<uint8_t>(
+ IPMISensorEventEnableThresholds::upperCriticalGoingHigh);
+ resp->deassertionEnabledMSB |= static_cast<uint8_t>(
+ IPMISensorEventEnableThresholds::upperCriticalGoingLow);
+ }
+ if (criticalLow != criticalMap.end())
+ {
+ resp->assertionEnabledLSB |= static_cast<uint8_t>(
+ IPMISensorEventEnableThresholds::lowerCriticalGoingLow);
+ resp->deassertionEnabledLSB |= static_cast<uint8_t>(
+ IPMISensorEventEnableThresholds::lowerCriticalGoingHigh);
+ }
+ }
+ *dataLen =
+ sizeof(SensorEventEnableResp); // todo only return needed bytes
+ }
+ // no thresholds enabled
+ else
+ {
+ *dataLen = 1;
+ auto resp = static_cast<uint8_t *>(response);
+ *resp = static_cast<uint8_t>(
+ IPMISensorEventEnableByte2::eventMessagesEnable);
+ *resp |= static_cast<uint8_t>(
+ IPMISensorEventEnableByte2::sensorScanningEnable);
+ }
+ return IPMI_CC_OK;
+}
+
+ipmi_ret_t ipmiSenGetSensorEventStatus(ipmi_netfn_t netfn, ipmi_cmd_t cmd,
+ ipmi_request_t request,
+ ipmi_response_t response,
+ ipmi_data_len_t dataLen,
+ ipmi_context_t context)
+{
+ if (*dataLen != 1)
+ {
+ *dataLen = 0;
+ return IPMI_CC_REQ_DATA_LEN_INVALID;
+ }
+ *dataLen = 0; // default to 0 in case of an error
+
+ uint8_t sensnum = *(static_cast<uint8_t *>(request));
+
+ std::string connection;
+ std::string path;
+
+ auto status = getSensorConnection(sensnum, connection, path);
+ if (status)
+ {
+ return status;
+ }
+
+ SensorMap sensorMap;
+ if (!getSensorMap(connection, path, sensorMap))
+ {
+ return IPMI_CC_RESPONSE_ERROR;
+ }
+
+ auto warningInterface =
+ sensorMap.find("xyz.openbmc_project.Sensor.Threshold.Warning");
+ auto criticalInterface =
+ sensorMap.find("xyz.openbmc_project.Sensor.Threshold.Critical");
+
+ // zero out response buff
+ auto responseClear = static_cast<uint8_t *>(response);
+ std::fill(responseClear, responseClear + sizeof(SensorEventStatusResp), 0);
+ auto resp = static_cast<SensorEventStatusResp *>(response);
+ resp->enabled =
+ static_cast<uint8_t>(IPMISensorEventEnableByte2::sensorScanningEnable);
+
+ if ((warningInterface != sensorMap.end()) ||
+ (criticalInterface != sensorMap.end()))
+ {
+ resp->enabled = static_cast<uint8_t>(
+ IPMISensorEventEnableByte2::eventMessagesEnable);
+ if (warningInterface != sensorMap.end())
+ {
+ auto &warningMap = warningInterface->second;
+
+ auto warningHigh = warningMap.find("WarningAlarmHigh");
+ auto warningLow = warningMap.find("WarningAlarmLow");
+ auto warningHighAlarm = false;
+ auto warningLowAlarm = false;
+
+ if (warningHigh != warningMap.end())
+ {
+ warningHighAlarm = warningHigh->second.get<bool>();
+ }
+ if (warningLow != warningMap.end())
+ {
+ warningLowAlarm = warningLow->second.get<bool>();
+ }
+ if (warningHighAlarm)
+ {
+ resp->assertionsLSB |= static_cast<uint8_t>(
+ IPMISensorEventEnableThresholds::upperNonCriticalGoingHigh);
+ }
+ if (warningLowAlarm)
+ {
+ resp->assertionsLSB |= 1; // lower nc going low
+ }
+ }
+ if (criticalInterface != sensorMap.end())
+ {
+ auto &criticalMap = criticalInterface->second;
+
+ auto criticalHigh = criticalMap.find("CriticalAlarmHigh");
+ auto criticalLow = criticalMap.find("CriticalAlarmLow");
+ auto criticalHighAlarm = false;
+ auto criticalLowAlarm = false;
+
+ if (criticalHigh != criticalMap.end())
+ {
+ criticalHighAlarm = criticalHigh->second.get<bool>();
+ }
+ if (criticalLow != criticalMap.end())
+ {
+ criticalLowAlarm = criticalLow->second.get<bool>();
+ }
+ if (criticalHighAlarm)
+ {
+ resp->assertionsMSB |= static_cast<uint8_t>(
+ IPMISensorEventEnableThresholds::upperCriticalGoingHigh);
+ }
+ if (criticalLowAlarm)
+ {
+ resp->assertionsLSB |= static_cast<uint8_t>(
+ IPMISensorEventEnableThresholds::lowerCriticalGoingLow);
+ }
+ }
+ *dataLen = sizeof(SensorEventStatusResp);
+ }
+
+ // no thresholds enabled, don't need assertionMSB
+ else
+ {
+ *dataLen = sizeof(SensorEventStatusResp) - 1;
+ }
+
+ return IPMI_CC_OK;
+}
+
+/* end sensor commands */
+
+/* storage commands */
+
+ipmi_ret_t ipmiStorageGetSDRRepositoryInfo(ipmi_netfn_t netfn, ipmi_cmd_t cmd,
+ ipmi_request_t request,
+ ipmi_response_t response,
+ ipmi_data_len_t dataLen,
+ ipmi_context_t context)
+{
+ printCommand(+netfn, +cmd);
+
+ if (*dataLen)
+ {
+ *dataLen = 0;
+ return IPMI_CC_REQ_DATA_LEN_INVALID;
+ }
+ *dataLen = 0; // default to 0 in case of an error
+
+ if (sensorTree.empty() && !getSensorSubtree(sensorTree))
+ {
+ return IPMI_CC_RESPONSE_ERROR;
+ }
+
+ // zero out response buff
+ auto responseClear = static_cast<uint8_t *>(response);
+ std::fill(responseClear, responseClear + sizeof(GetSDRInfoResp), 0);
+
+ auto resp = static_cast<GetSDRInfoResp *>(response);
+ resp->sdrVersion = ipmiSdrVersion;
+ uint16_t recordCount = sensorTree.size();
+
+ // todo: for now, sdr count is number of sensors
+ resp->recordCountLS = recordCount & 0xFF;
+ resp->recordCountMS = recordCount >> 8;
+
+ // free space unspcified
+ resp->freeSpace[0] = 0xFF;
+ resp->freeSpace[1] = 0xFF;
+
+ resp->mostRecentAddition = sdrLastAdd;
+ resp->mostRecentErase = sdrLastRemove;
+ resp->operationSupport = static_cast<uint8_t>(
+ SdrRepositoryInfoOps::overflow); // write not supported
+ resp->operationSupport |=
+ static_cast<uint8_t>(SdrRepositoryInfoOps::allocCommandSupported);
+ resp->operationSupport |= static_cast<uint8_t>(
+ SdrRepositoryInfoOps::reserveSDRRepositoryCommandSupported);
+ *dataLen = sizeof(GetSDRInfoResp);
+ return IPMI_CC_OK;
+}
+
+ipmi_ret_t ipmiStorageGetSDRAllocationInfo(ipmi_netfn_t netfn, ipmi_cmd_t cmd,
+ ipmi_request_t request,
+ ipmi_response_t response,
+ ipmi_data_len_t dataLen,
+ ipmi_context_t context)
+{
+ if (*dataLen)
+ {
+ *dataLen = 0;
+ return IPMI_CC_REQ_DATA_LEN_INVALID;
+ }
+ *dataLen = 0; // default to 0 in case of an error
+ GetAllocInfoResp *resp = static_cast<GetAllocInfoResp *>(response);
+
+ // 0000h unspecified number of alloc units
+ resp->allocUnitsLSB = 0;
+ resp->allocUnitsMSB = 0;
+
+ // max unit size is size of max record
+ resp->allocUnitSizeLSB = maxSDRTotalSize & 0xFF;
+ resp->allocUnitSizeMSB = maxSDRTotalSize >> 8;
+ // read only sdr, no free alloc blocks
+ resp->allocUnitFreeLSB = 0;
+ resp->allocUnitFreeMSB = 0;
+ resp->allocUnitLargestFreeLSB = 0;
+ resp->allocUnitLargestFreeMSB = 0;
+ // only allow one block at a time
+ resp->maxRecordSize = 1;
+
+ *dataLen = sizeof(GetAllocInfoResp);
+
+ return IPMI_CC_OK;
+}
+
+ipmi_ret_t ipmiStorageReserveSDR(ipmi_netfn_t netfn, ipmi_cmd_t cmd,
+ ipmi_request_t request,
+ ipmi_response_t response,
+ ipmi_data_len_t dataLen,
+ ipmi_context_t context)
+{
+ printCommand(+netfn, +cmd);
+
+ if (*dataLen)
+ {
+ *dataLen = 0;
+ return IPMI_CC_REQ_DATA_LEN_INVALID;
+ }
+ *dataLen = 0; // default to 0 in case of an error
+ sdrReservationID++;
+ *dataLen = 2;
+ auto resp = static_cast<uint8_t *>(response);
+ resp[0] = sdrReservationID & 0xFF;
+ resp[1] = sdrReservationID >> 8;
+
+ return IPMI_CC_OK;
+}
+
+ipmi_ret_t ipmiStorageGetSDR(ipmi_netfn_t netfn, ipmi_cmd_t cmd,
+ ipmi_request_t request, ipmi_response_t response,
+ ipmi_data_len_t dataLen, ipmi_context_t context)
+{
+ printCommand(+netfn, +cmd);
+
+ if (*dataLen != 6)
+ {
+ *dataLen = 0;
+ return IPMI_CC_REQ_DATA_LEN_INVALID;
+ }
+ auto requestedSize = *dataLen;
+ *dataLen = 0; // default to 0 in case of an error
+
+ constexpr uint16_t lastRecordIndex = 0xFFFF;
+ auto req = static_cast<GetSDRReq *>(request);
+
+ // reservation required for partial reads with non zero offset into
+ // record
+ if (req->reservationID != sdrReservationID && req->offset)
+ {
+ return IPMI_CC_INVALID_RESERVATION_ID;
+ }
+
+ if (sensorTree.empty() && !getSensorSubtree(sensorTree))
+ {
+ return IPMI_CC_RESPONSE_ERROR;
+ }
+
+ size_t fruCount = 0;
+ ipmi_ret_t ret = ipmi::storage::getFruSdrCount(fruCount);
+ if (ret != IPMI_CC_OK)
+ {
+ return ret;
+ }
+
+ size_t lastRecord = sensorTree.size() + fruCount - 1;
+ if (req->recordID == lastRecordIndex)
+ {
+ req->recordID = lastRecord;
+ }
+ if (req->recordID > lastRecord)
+ {
+ return IPMI_CC_INVALID_FIELD_REQUEST;
+ }
+
+ uint16_t nextRecord =
+ lastRecord > (req->recordID + 1) ? req->recordID + 1 : 0XFFFF;
+
+ auto responseClear = static_cast<uint8_t *>(response);
+ std::fill(responseClear, responseClear + requestedSize, 0);
+
+ auto resp = static_cast<get_sdr::GetSdrResp *>(response);
+ resp->next_record_id_lsb = nextRecord & 0xFF;
+ resp->next_record_id_msb = nextRecord >> 8;
+
+ if (req->recordID >= sensorTree.size())
+ {
+ size_t fruIndex = req->recordID - sensorTree.size();
+ if (fruIndex >= fruCount)
+ {
+ return IPMI_CC_INVALID_FIELD_REQUEST;
+ }
+ get_sdr::SensorDataFruRecord data;
+ if (req->offset > sizeof(data))
+ {
+ return IPMI_CC_INVALID_FIELD_REQUEST;
+ }
+ ret = ipmi::storage::getFruSdrs(fruIndex, data);
+ if (ret != IPMI_CC_OK)
+ {
+ return ret;
+ }
+ data.header.record_id_msb = req->recordID << 8;
+ data.header.record_id_lsb = req->recordID & 0xFF;
+ if (sizeof(data) < (req->offset + req->bytesToRead))
+ {
+ req->bytesToRead = sizeof(data) - req->offset;
+ }
+ *dataLen = req->bytesToRead + 2; // next record
+ std::memcpy(&resp->record_data, (char *)&data + req->offset,
+ req->bytesToRead);
+ return IPMI_CC_OK;
+ }
+
+ std::string connection;
+ std::string path;
+ uint16_t sensorIndex = req->recordID;
+ for (const auto &sensor : sensorTree)
+ {
+ if (sensorIndex-- == 0)
+ {
+ if (!sensor.second.size())
+ {
+ return IPMI_CC_RESPONSE_ERROR;
+ }
+ connection = sensor.second.begin()->first;
+ path = sensor.first;
+ break;
+ }
+ }
+
+ SensorMap sensorMap;
+ if (!getSensorMap(connection, path, sensorMap))
+ {
+ return IPMI_CC_RESPONSE_ERROR;
+ }
+ uint8_t sensornumber = (req->recordID & 0xFF);
+ get_sdr::SensorDataFullRecord record = {0};
+
+ record.header.record_id_msb = req->recordID << 8;
+ record.header.record_id_lsb = req->recordID & 0xFF;
+ record.header.sdr_version = ipmiSdrVersion;
+ record.header.record_type = get_sdr::SENSOR_DATA_FULL_RECORD;
+ record.header.record_length = sizeof(get_sdr::SensorDataFullRecord) -
+ sizeof(get_sdr::SensorDataRecordHeader);
+ record.key.owner_id = 0x20;
+ record.key.owner_lun = 0x0;
+ record.key.sensor_number = sensornumber;
+
+ record.body.entity_id = 0x0;
+ record.body.entity_instance = 0x01;
+ record.body.sensor_capabilities = 0x60; // auto rearm - todo hysteresis
+ record.body.sensor_type = getSensorTypeFromPath(path);
+ std::string type = getSensorTypeStringFromPath(path);
+ auto typeCstr = type.c_str();
+ auto findUnits = sensorUnits.find(typeCstr);
+ if (findUnits != sensorUnits.end())
+ {
+ record.body.sensor_units_2_base =
+ static_cast<uint8_t>(findUnits->second);
+ } // else default 0x0 unspecified
+
+ record.body.event_reading_type = getSensorEventTypeFromPath(path);
+
+ auto sensorObject = sensorMap.find("xyz.openbmc_project.Sensor.Value");
+ if (sensorObject == sensorMap.end())
+ {
+ return IPMI_CC_RESPONSE_ERROR;
+ }
+
+ auto maxObject = sensorObject->second.find("MaxValue");
+ auto minObject = sensorObject->second.find("MinValue");
+ double max = 128;
+ double min = -127;
+ if (maxObject != sensorObject->second.end())
+ {
+ max = apply_visitor(VariantToDoubleVisitor(), maxObject->second);
+ }
+
+ if (minObject != sensorObject->second.end())
+ {
+ min = apply_visitor(VariantToDoubleVisitor(), minObject->second);
+ }
+
+ int16_t mValue;
+ int8_t rExp;
+ int16_t bValue;
+ int8_t bExp;
+ bool bSigned;
+
+ if (!getSensorAttributes(max, min, mValue, rExp, bValue, bExp, bSigned))
+ {
+ return IPMI_CC_RESPONSE_ERROR;
+ }
+
+ // apply M, B, and exponents, M and B are 10 bit values, exponents are 4
+ record.body.m_lsb = mValue & 0xFF;
+
+ // move the smallest bit of the MSB into place (bit 9)
+ // the MSbs are bits 7:8 in m_msb_and_tolerance
+ uint8_t mMsb = (mValue & (1 << 8)) > 0 ? (1 << 6) : 0;
+
+ // assign the negative
+ if (mValue < 0)
+ {
+ mMsb |= (1 << 7);
+ }
+ record.body.m_msb_and_tolerance = mMsb;
+
+ record.body.b_lsb = bValue & 0xFF;
+
+ // move the smallest bit of the MSB into place
+ // the MSbs are bits 7:8 in b_msb_and_accuracy_lsb
+ uint8_t bMsb = (bValue & (1 << 8)) > 0 ? (1 << 6) : 0;
+
+ // assign the negative
+ if (bValue < 0)
+ {
+ bMsb |= (1 << 7);
+ }
+ record.body.b_msb_and_accuracy_lsb = bMsb;
+
+ record.body.r_b_exponents = bExp & 0x7;
+ if (bExp < 0)
+ {
+ record.body.r_b_exponents |= 1 << 3;
+ }
+ record.body.r_b_exponents = (rExp & 0x7) << 4;
+ if (rExp < 0)
+ {
+ record.body.r_b_exponents |= 1 << 7;
+ }
+
+ // todo fill out rest of units
+ if (bSigned)
+ {
+ record.body.sensor_units_1 = 1 << 7;
+ }
+
+ // populate sensor name from path
+ std::string name;
+ size_t nameStart = path.rfind("/");
+ if (nameStart != std::string::npos)
+ {
+ name = path.substr(nameStart + 1, std::string::npos - nameStart);
+ }
+
+ std::replace(name.begin(), name.end(), '_', ' ');
+ if (name.size() > FULL_RECORD_ID_STR_MAX_LENGTH)
+ {
+ name.resize(FULL_RECORD_ID_STR_MAX_LENGTH);
+ }
+ record.body.id_string_info = name.size();
+ std::strncpy(record.body.id_string, name.c_str(),
+ sizeof(record.body.id_string));
+
+ if (sizeof(get_sdr::SensorDataFullRecord) <
+ (req->offset + req->bytesToRead))
+ {
+ req->bytesToRead = sizeof(get_sdr::SensorDataFullRecord) - req->offset;
+ }
+
+ *dataLen =
+ 2 + req->bytesToRead; // bytesToRead + MSB and LSB of next record id
+
+ std::memcpy(&resp->record_data, (char *)&record + req->offset,
+ req->bytesToRead);
+
+ return IPMI_CC_OK;
+}
+/* end storage commands */
+
+void registerSensorFunctions()
+{
+ // get firmware version information
+ ipmiPrintAndRegister(NETFUN_SENSOR, IPMI_CMD_WILDCARD, nullptr,
+ ipmiSensorWildcardHandler, PRIVILEGE_USER);
+
+ // <Get Sensor Type>
+ ipmiPrintAndRegister(
+ NETFUN_SENSOR,
+ static_cast<ipmi_cmd_t>(IPMINetfnSensorCmds::ipmiCmdGetSensorType),
+ nullptr, ipmiSensorWildcardHandler, PRIVILEGE_USER);
+
+ // <Set Sensor Reading and Event Status>
+ ipmiPrintAndRegister(
+ NETFUN_SENSOR,
+ static_cast<ipmi_cmd_t>(
+ IPMINetfnSensorCmds::ipmiCmdSetSensorReadingAndEventStatus),
+ nullptr, ipmiSensorWildcardHandler, PRIVILEGE_OPERATOR);
+
+ // <Get Sensor Reading>
+ ipmiPrintAndRegister(
+ NETFUN_SENSOR,
+ static_cast<ipmi_cmd_t>(IPMINetfnSensorCmds::ipmiCmdGetSensorReading),
+ nullptr, ipmiSenGetSensorReading, PRIVILEGE_USER);
+
+ // <Get Sensor Threshold>
+ ipmiPrintAndRegister(
+ NETFUN_SENSOR,
+ static_cast<ipmi_cmd_t>(IPMINetfnSensorCmds::ipmiCmdGetSensorThreshold),
+ nullptr, ipmiSenGetSensorThresholds, PRIVILEGE_USER);
+
+ ipmiPrintAndRegister(
+ NETFUN_SENSOR,
+ static_cast<ipmi_cmd_t>(IPMINetfnSensorCmds::ipmiCmdSetSensorThreshold),
+ nullptr, ipmiSenSetSensorThresholds, PRIVILEGE_OPERATOR);
+
+ // <Get Sensor Event Enable>
+ ipmiPrintAndRegister(NETFUN_SENSOR,
+ static_cast<ipmi_cmd_t>(
+ IPMINetfnSensorCmds::ipmiCmdGetSensorEventEnable),
+ nullptr, ipmiSenGetSensorEventEnable, PRIVILEGE_USER);
+
+ // <Get Sensor Event Status>
+ ipmiPrintAndRegister(NETFUN_SENSOR,
+ static_cast<ipmi_cmd_t>(
+ IPMINetfnSensorCmds::ipmiCmdGetSensorEventStatus),
+ nullptr, ipmiSenGetSensorEventStatus, PRIVILEGE_USER);
+
+ // register all storage commands for both Sensor and Storage command
+ // versions
+
+ // <Get SDR Repository Info>
+ ipmiPrintAndRegister(
+ NETFUN_STORAGE,
+ static_cast<ipmi_cmd_t>(IPMINetfnStorageCmds::ipmiCmdGetRepositoryInfo),
+ nullptr, ipmiStorageGetSDRRepositoryInfo, PRIVILEGE_USER);
+
+ // <Get SDR Allocation Info>
+ ipmiPrintAndRegister(NETFUN_STORAGE,
+ static_cast<ipmi_cmd_t>(
+ IPMINetfnStorageCmds::ipmiCmdGetSDRAllocationInfo),
+ nullptr, ipmiStorageGetSDRAllocationInfo,
+ PRIVILEGE_USER);
+
+ // <Reserve SDR Repo>
+ ipmiPrintAndRegister(NETFUN_SENSOR,
+ static_cast<ipmi_cmd_t>(
+ IPMINetfnSensorCmds::ipmiCmdReserveDeviceSDRRepo),
+ nullptr, ipmiStorageReserveSDR, PRIVILEGE_USER);
+
+ ipmiPrintAndRegister(
+ NETFUN_STORAGE,
+ static_cast<ipmi_cmd_t>(IPMINetfnStorageCmds::ipmiCmdReserveSDR),
+ nullptr, ipmiStorageReserveSDR, PRIVILEGE_USER);
+
+ // <Get Sdr>
+ ipmiPrintAndRegister(
+ NETFUN_SENSOR,
+ static_cast<ipmi_cmd_t>(IPMINetfnSensorCmds::ipmiCmdGetDeviceSDR),
+ nullptr, ipmiStorageGetSDR, PRIVILEGE_USER);
+
+ ipmiPrintAndRegister(
+ NETFUN_STORAGE,
+ static_cast<ipmi_cmd_t>(IPMINetfnStorageCmds::ipmiCmdGetSDR), nullptr,
+ ipmiStorageGetSDR, PRIVILEGE_USER);
+ return;
+}
+} // namespace ipmi
diff --git a/src/sensorutils.cpp b/src/sensorutils.cpp
new file mode 100644
index 0000000..62e2cd2
--- /dev/null
+++ b/src/sensorutils.cpp
@@ -0,0 +1,161 @@
+/*
+// Copyright (c) 2017 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 <host-ipmid/ipmid-api.h>
+
+#include <cmath>
+#include <iostream>
+#include <phosphor-logging/log.hpp>
+#include <sensorutils.hpp>
+
+namespace ipmi
+{
+static constexpr int16_t maxInt10 = 0x1FF;
+static constexpr int16_t minInt10 = -(0x200);
+static constexpr int8_t maxInt4 = 7;
+static constexpr int8_t minInt4 = -8;
+
+bool getSensorAttributes(const double max, const double min, int16_t &mValue,
+ int8_t &rExp, int16_t &bValue, int8_t &bExp,
+ bool &bSigned)
+{
+ // computing y = (10^rRexp) * (Mx + (B*(10^Bexp)))
+ // check for 0, assume always positive
+ double mDouble;
+ double bDouble;
+ if (!(max > min))
+ {
+ phosphor::logging::log<phosphor::logging::level::DEBUG>(
+ "getSensorAttributes: Max must be greater than min");
+ return false;
+ }
+ else
+ {
+ mDouble = (max - min) / 0xFF;
+ }
+ if (!mDouble)
+ {
+ mDouble = 1;
+ }
+
+ if (min < 0)
+ {
+ bSigned = true;
+ bDouble = floor(0.5 + ((max + min) / 2));
+ }
+ else
+ {
+ bSigned = false;
+ bDouble = min;
+ }
+
+ rExp = 0;
+
+ // M too big for 10 bit variable
+ while (mDouble > maxInt10)
+ {
+ if (rExp == maxInt4)
+ {
+ phosphor::logging::log<phosphor::logging::level::DEBUG>(
+ "rExp Too big, Max and Min range too far");
+ return false;
+ }
+ mDouble /= 10;
+ rExp += 1;
+ }
+
+ // M too small, loop until we loose less than 1 eight bit count of precision
+ while (((mDouble - floor(mDouble)) / mDouble) > (1.0 / 255))
+ {
+ if (rExp == minInt4)
+ {
+ phosphor::logging::log<phosphor::logging::level::DEBUG>(
+ "rExp Too Small, Max and Min range too close");
+ return false;
+ }
+ // check to see if we reached the limit of where we can adjust back the
+ // B value
+ if (bDouble / std::pow(10, rExp + minInt4 - 1) > bDouble)
+ {
+ if (mDouble < 1.0)
+ {
+ phosphor::logging::log<phosphor::logging::level::DEBUG>(
+ "Could not find mValue and B value with enough "
+ "precision.");
+ return false;
+ }
+ break;
+ }
+ // can't multiply M any more, max precision reached
+ else if (mDouble * 10 > maxInt10)
+ {
+ break;
+ }
+ mDouble *= 10;
+ rExp -= 1;
+ }
+
+ bDouble /= std::pow(10, rExp);
+ bExp = 0;
+
+ // B too big for 10 bit variable
+ while (bDouble > maxInt10 || bDouble < minInt10)
+ {
+ if (bExp == maxInt4)
+ {
+ phosphor::logging::log<phosphor::logging::level::DEBUG>(
+ "bExp Too Big, Max and Min range need to be adjusted");
+ return false;
+ }
+ bDouble /= 10;
+ bExp += 1;
+ }
+
+ while (((fabs(bDouble) - floor(fabs(bDouble))) / fabs(bDouble)) >
+ (1.0 / 255))
+ {
+ if (bExp == minInt4)
+ {
+ phosphor::logging::log<phosphor::logging::level::DEBUG>(
+ "bExp Too Small, Max and Min range need to be adjusted");
+ return false;
+ }
+ bDouble *= 10;
+ bExp -= 1;
+ }
+
+ mValue = static_cast<int16_t>(mDouble + 0.5) & maxInt10;
+ bValue = static_cast<int16_t>(bDouble + 0.5) & maxInt10;
+
+ return true;
+}
+
+uint8_t scaleIPMIValueFromDouble(const double value, const uint16_t mValue,
+ const int8_t rExp, const uint16_t bValue,
+ const int8_t bExp, const bool bSigned)
+{
+ uint32_t scaledValue =
+ (value - (bValue * std::pow(10, bExp) * std::pow(10, rExp))) /
+ (mValue * std::pow(10, rExp));
+ if (bSigned)
+ {
+ return static_cast<int8_t>(scaledValue);
+ }
+ else
+ {
+ return static_cast<uint8_t>(scaledValue);
+ }
+}
+} // namespace ipmi
\ No newline at end of file
diff --git a/src/storagecommands.cpp b/src/storagecommands.cpp
new file mode 100644
index 0000000..e71d47f
--- /dev/null
+++ b/src/storagecommands.cpp
@@ -0,0 +1,315 @@
+/*
+// Copyright (c) 2017 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 <host-ipmid/ipmid-api.h>
+
+#include <boost/container/flat_map.hpp>
+#include <commandutils.hpp>
+#include <iostream>
+#include <phosphor-logging/log.hpp>
+#include <sdbusplus/message/types.hpp>
+#include <sdbusplus/timer.hpp>
+#include <storagecommands.hpp>
+
+namespace ipmi
+{
+
+namespace storage
+{
+
+constexpr static const size_t maxFruSdrNameSize = 16;
+using ManagedObjectType = boost::container::flat_map<
+ sdbusplus::message::object_path,
+ boost::container::flat_map<
+ std::string, boost::container::flat_map<std::string, DbusVariant>>>;
+using ManagedEntry = std::pair<
+ sdbusplus::message::object_path,
+ boost::container::flat_map<
+ std::string, boost::container::flat_map<std::string, DbusVariant>>>;
+
+constexpr static const char* fruDeviceServiceName = "com.intel.FruDevice";
+
+static std::vector<uint8_t> fruCache;
+static uint8_t cacheBus = 0xFF;
+static uint8_t cacheAddr = 0XFF;
+
+std::unique_ptr<phosphor::Timer> cacheTimer = nullptr;
+
+// we unfortunately have to build a map of hashes in case there is a
+// collision to verify our dev-id
+boost::container::flat_map<uint8_t, std::pair<uint8_t, uint8_t>> deviceHashes;
+
+static sdbusplus::bus::bus dbus(ipmid_get_sd_bus_connection());
+
+bool writeFru()
+{
+ sdbusplus::message::message writeFru = dbus.new_method_call(
+ fruDeviceServiceName, "/xyz/openbmc_project/FruDevice",
+ "xyz.openbmc_project.FruDeviceManager", "WriteFru");
+ writeFru.append(cacheBus, cacheAddr, fruCache);
+ try
+ {
+ sdbusplus::message::message writeFruResp = dbus.call(writeFru);
+ }
+ catch (sdbusplus::exception_t&)
+ {
+ // todo: log sel?
+ phosphor::logging::log<phosphor::logging::level::ERR>(
+ "error writing fru");
+ return false;
+ }
+ return true;
+}
+
+ipmi_ret_t replaceCacheFru(uint8_t devId)
+{
+ static uint8_t lastDevId = 0xFF;
+
+ bool timerRunning = (cacheTimer != nullptr) && !cacheTimer->isExpired();
+ if (lastDevId == devId && timerRunning)
+ {
+ return IPMI_CC_OK; // cache already up to date
+ }
+ // if timer is running, stop it and writeFru manually
+ else if (timerRunning)
+ {
+ cacheTimer->stop();
+ writeFru();
+ }
+
+ sdbusplus::message::message getObjects = dbus.new_method_call(
+ fruDeviceServiceName, "/", "org.freedesktop.DBus.ObjectManager",
+ "GetManagedObjects");
+ ManagedObjectType frus;
+ try
+ {
+ sdbusplus::message::message resp = dbus.call(getObjects);
+ resp.read(frus);
+ }
+ catch (sdbusplus::exception_t&)
+ {
+ phosphor::logging::log<phosphor::logging::level::ERR>(
+ "replaceCacheFru: error getting managed objects");
+ return IPMI_CC_RESPONSE_ERROR;
+ }
+
+ deviceHashes.clear();
+
+ // hash the object paths to create unique device id's. increment on
+ // collision
+ std::hash<std::string> hasher;
+ for (const auto& fru : frus)
+ {
+ auto fruIface = fru.second.find("xyz.openbmc_project.FruDevice");
+ if (fruIface == fru.second.end())
+ {
+ continue;
+ }
+
+ auto busFind = fruIface->second.find("BUS");
+ auto addrFind = fruIface->second.find("ADDRESS");
+ if (busFind == fruIface->second.end() ||
+ addrFind == fruIface->second.end())
+ {
+ phosphor::logging::log<phosphor::logging::level::INFO>(
+ "fru device missing Bus or Address",
+ phosphor::logging::entry("FRU=%s", fru.first.str.c_str()));
+ continue;
+ }
+
+ uint8_t fruBus =
+ sdbusplus::message::variant_ns::get<uint32_t>(busFind->second);
+ uint8_t fruAddr =
+ sdbusplus::message::variant_ns::get<uint32_t>(addrFind->second);
+
+ uint8_t fruHash = 0;
+ if (fruBus != 0 || fruAddr != 0)
+ {
+ fruHash = hasher(fru.first.str);
+ // can't be 0xFF based on spec, and 0 is reserved for baseboard
+ if (fruHash == 0 || fruHash == 0xFF)
+ {
+ fruHash = 1;
+ }
+ }
+ std::pair<uint8_t, uint8_t> newDev(fruBus, fruAddr);
+
+ bool emplacePassed = false;
+ while (!emplacePassed)
+ {
+ auto resp = deviceHashes.emplace(fruHash, newDev);
+ emplacePassed = resp.second;
+ if (!emplacePassed)
+ {
+ fruHash++;
+ // can't be 0xFF based on spec, and 0 is reserved for
+ // baseboard
+ if (fruHash == 0XFF)
+ {
+ fruHash = 0x1;
+ }
+ }
+ }
+ }
+ auto deviceFind = deviceHashes.find(devId);
+ if (deviceFind == deviceHashes.end())
+ {
+ return IPMI_CC_SENSOR_INVALID;
+ }
+
+ fruCache.clear();
+ sdbusplus::message::message getRawFru = dbus.new_method_call(
+ fruDeviceServiceName, "/xyz/openbmc_project/FruDevice",
+ "xyz.openbmc_project.FruDeviceManager", "GetRawFru");
+ cacheBus = deviceFind->second.first;
+ cacheAddr = deviceFind->second.second;
+ getRawFru.append(cacheBus, cacheAddr);
+ try
+ {
+ sdbusplus::message::message getRawResp = dbus.call(getRawFru);
+ getRawResp.read(fruCache);
+ }
+ catch (sdbusplus::exception_t&)
+ {
+ lastDevId = 0xFF;
+ cacheBus = 0xFF;
+ cacheAddr = 0xFF;
+ return IPMI_CC_RESPONSE_ERROR;
+ }
+
+ lastDevId = devId;
+ return IPMI_CC_OK;
+}
+
+ipmi_ret_t getFruSdrCount(size_t& count)
+{
+ ipmi_ret_t ret = replaceCacheFru(0);
+ if (ret != IPMI_CC_OK)
+ {
+ return ret;
+ }
+ count = deviceHashes.size();
+ return IPMI_CC_OK;
+}
+
+ipmi_ret_t getFruSdrs(size_t index, get_sdr::SensorDataFruRecord& resp)
+{
+ ipmi_ret_t ret = replaceCacheFru(0); // this will update the hash list
+ if (ret != IPMI_CC_OK)
+ {
+ return ret;
+ }
+ if (deviceHashes.size() < index)
+ {
+ return IPMI_CC_INVALID_FIELD_REQUEST;
+ }
+ auto device = deviceHashes.begin() + index;
+ uint8_t& bus = device->second.first;
+ uint8_t& address = device->second.second;
+
+ ManagedObjectType frus;
+
+ sdbusplus::message::message getObjects = dbus.new_method_call(
+ fruDeviceServiceName, "/", "org.freedesktop.DBus.ObjectManager",
+ "GetManagedObjects");
+ try
+ {
+ sdbusplus::message::message resp = dbus.call(getObjects);
+ resp.read(frus);
+ }
+ catch (sdbusplus::exception_t&)
+ {
+ return IPMI_CC_RESPONSE_ERROR;
+ }
+ boost::container::flat_map<std::string, DbusVariant>* fruData = nullptr;
+ auto fru =
+ std::find_if(frus.begin(), frus.end(),
+ [bus, address, &fruData](ManagedEntry& entry) {
+ auto findFruDevice =
+ entry.second.find("xyz.openbmc_project.FruDevice");
+ if (findFruDevice == entry.second.end())
+ {
+ return false;
+ }
+ fruData = &(findFruDevice->second);
+ auto findBus = findFruDevice->second.find("BUS");
+ auto findAddress =
+ findFruDevice->second.find("ADDRESS");
+ if (findBus == findFruDevice->second.end() ||
+ findAddress == findFruDevice->second.end())
+ {
+ return false;
+ }
+ if (sdbusplus::message::variant_ns::get<uint32_t>(
+ findBus->second) != bus)
+ {
+ return false;
+ }
+ if (sdbusplus::message::variant_ns::get<uint32_t>(
+ findAddress->second) != address)
+ {
+ return false;
+ }
+ return true;
+ });
+ if (fru == frus.end())
+ {
+ return IPMI_CC_RESPONSE_ERROR;
+ }
+ std::string name;
+ auto findProductName = fruData->find("BOARD_PRODUCT_NAME");
+ auto findBoardName = fruData->find("PRODUCT_PRODUCT_NAME");
+ if (findProductName != fruData->end())
+ {
+ name = sdbusplus::message::variant_ns::get<std::string>(
+ findProductName->second);
+ }
+ else if (findBoardName != fruData->end())
+ {
+ name = sdbusplus::message::variant_ns::get<std::string>(
+ findBoardName->second);
+ }
+ else
+ {
+ name = "UNKNOWN";
+ }
+ if (name.size() > maxFruSdrNameSize)
+ {
+ name = name.substr(0, maxFruSdrNameSize);
+ }
+ size_t sizeDiff = maxFruSdrNameSize - name.size();
+
+ resp.header.record_id_lsb = 0x0; // calling code is to implement these
+ resp.header.record_id_msb = 0x0;
+ resp.header.sdr_version = ipmiSdrVersion;
+ resp.header.record_type = 0x11; // FRU Device Locator
+ resp.header.record_length = sizeof(resp.body) + sizeof(resp.key) - sizeDiff;
+ resp.key.deviceAddress = 0x20;
+ resp.key.fruID = device->first;
+ resp.key.accessLun = 0x80; // logical / physical fru device
+ resp.key.channelNumber = 0x0;
+ resp.body.reserved = 0x0;
+ resp.body.deviceType = 0x10;
+ resp.body.entityID = 0x0;
+ resp.body.entityInstance = 0x1;
+ resp.body.oem = 0x0;
+ resp.body.deviceIDLen = name.size();
+ name.copy(resp.body.deviceID, name.size());
+
+ return IPMI_CC_OK;
+}
+} // namespace storage
+} // namespace ipmi
\ No newline at end of file
diff --git a/src/utils.cpp b/src/utils.cpp
new file mode 100644
index 0000000..3851e82
--- /dev/null
+++ b/src/utils.cpp
@@ -0,0 +1,567 @@
+#include "phosphor-ipmi-host/utils.hpp"
+
+#include <arpa/inet.h>
+#include <dirent.h>
+#include <net/if.h>
+
+#include <phosphor-logging/elog-errors.hpp>
+#include <phosphor-logging/log.hpp>
+#include <xyz/openbmc_project/Common/error.hpp>
+
+namespace ipmi
+{
+
+using namespace phosphor::logging;
+using namespace sdbusplus::xyz::openbmc_project::Common::Error;
+
+namespace network
+{
+
+/** @brief checks if the given ip is Link Local Ip or not.
+ * @param[in] ipaddress - IPAddress.
+ */
+bool isLinkLocalIP(const std::string& ipaddress);
+
+} // namespace network
+
+// TODO There may be cases where an interface is implemented by multiple
+// objects,to handle such cases we are interested on that object
+// which are on interested busname.
+// Currently mapper doesn't give the readable busname(gives busid) so we can't
+// use busname to find the object,will do later once the support is there.
+
+DbusObjectInfo getDbusObject(sdbusplus::bus::bus& bus,
+ const std::string& interface,
+ const std::string& serviceRoot,
+ const std::string& match)
+{
+ std::vector<DbusInterface> interfaces;
+ interfaces.emplace_back(interface);
+
+ auto depth = 0;
+
+ auto mapperCall = bus.new_method_call(MAPPER_BUS_NAME, MAPPER_OBJ,
+ MAPPER_INTF, "GetSubTree");
+
+ mapperCall.append(serviceRoot, depth, interfaces);
+
+ ObjectTree objectTree;
+ try
+ {
+ auto mapperReply = bus.call(mapperCall);
+ mapperReply.read(objectTree);
+ }
+ catch (sdbusplus::exception_t&)
+ {
+ log<level::ERR>("Error in mapper call");
+ elog<InternalFailure>();
+ }
+
+ if (objectTree.empty())
+ {
+ log<level::ERR>("No Object has implemented the interface",
+ entry("INTERFACE=%s", interface.c_str()));
+ elog<InternalFailure>();
+ }
+
+ DbusObjectInfo objectInfo;
+
+ // if match is empty then return the first object
+ if (match == "")
+ {
+ objectInfo = std::make_pair(
+ objectTree.begin()->first,
+ std::move(objectTree.begin()->second.begin()->first));
+ return objectInfo;
+ }
+
+ // else search the match string in the object path
+ auto objectFound = false;
+ for (auto& object : objectTree)
+ {
+ if (object.first.find(match) != std::string::npos)
+ {
+ objectFound = true;
+ objectInfo = make_pair(object.first,
+ std::move(object.second.begin()->first));
+ break;
+ }
+ }
+
+ if (!objectFound)
+ {
+ log<level::ERR>("Failed to find object which matches",
+ entry("MATCH=%s", match.c_str()));
+ elog<InternalFailure>();
+ }
+ return objectInfo;
+}
+
+DbusObjectInfo getIPObject(sdbusplus::bus::bus& bus,
+ const std::string& interface,
+ const std::string& serviceRoot,
+ const std::string& match)
+{
+ auto objectTree = getAllDbusObjects(bus, serviceRoot, interface, match);
+
+ if (objectTree.empty())
+ {
+ log<level::ERR>("No Object has implemented the IP interface",
+ entry("INTERFACE=%s", interface.c_str()));
+ elog<InternalFailure>();
+ }
+
+ DbusObjectInfo objectInfo;
+
+ for (auto& object : objectTree)
+ {
+ auto variant = ipmi::getDbusProperty(
+ bus, object.second.begin()->first, object.first,
+ ipmi::network::IP_INTERFACE, "Address");
+
+ objectInfo = std::make_pair(object.first, object.second.begin()->first);
+
+ // if LinkLocalIP found look for Non-LinkLocalIP
+ if (ipmi::network::isLinkLocalIP(variant.get<std::string>()))
+ {
+ continue;
+ }
+ else
+ {
+ break;
+ }
+ }
+ return objectInfo;
+}
+
+Value getDbusProperty(sdbusplus::bus::bus& bus, const std::string& service,
+ const std::string& objPath, const std::string& interface,
+ const std::string& property)
+{
+
+ Value value;
+
+ auto method = bus.new_method_call(service.c_str(), objPath.c_str(),
+ PROP_INTF, METHOD_GET);
+
+ method.append(interface, property);
+
+ try
+ {
+ auto reply = bus.call(method);
+ reply.read(value);
+ }
+ catch (sdbusplus::exception_t&)
+ {
+ log<level::ERR>("Failed to get property",
+ entry("PROPERTY=%s", property.c_str()),
+ entry("PATH=%s", objPath.c_str()),
+ entry("INTERFACE=%s", interface.c_str()));
+ elog<InternalFailure>();
+ }
+
+ return value;
+}
+
+PropertyMap getAllDbusProperties(sdbusplus::bus::bus& bus,
+ const std::string& service,
+ const std::string& objPath,
+ const std::string& interface)
+{
+ PropertyMap properties;
+
+ auto method = bus.new_method_call(service.c_str(), objPath.c_str(),
+ PROP_INTF, METHOD_GET_ALL);
+
+ method.append(interface);
+
+ try
+ {
+ auto reply = bus.call(method);
+ reply.read(properties);
+ }
+ catch (sdbusplus::exception_t&)
+ {
+ log<level::ERR>("Failed to get all properties",
+ entry("PATH=%s", objPath.c_str()),
+ entry("INTERFACE=%s", interface.c_str()));
+ elog<InternalFailure>();
+ }
+
+ return properties;
+}
+
+ObjectValueTree getManagedObjects(sdbusplus::bus::bus& bus,
+ const std::string& service,
+ const std::string& objPath)
+{
+ ipmi::ObjectValueTree interfaces;
+
+ auto method = bus.new_method_call(service.c_str(), objPath.c_str(),
+ "org.freedesktop.DBus.ObjectManager",
+ "GetManagedObjects");
+
+ try
+ {
+ auto reply = bus.call(method);
+ reply.read(interfaces);
+ }
+ catch (sdbusplus::exception_t&)
+ {
+ log<level::ERR>("Failed to get managed objects",
+ entry("PATH=%s", objPath.c_str()));
+ elog<InternalFailure>();
+ }
+
+ return interfaces;
+}
+
+void setDbusProperty(sdbusplus::bus::bus& bus, const std::string& service,
+ const std::string& objPath, const std::string& interface,
+ const std::string& property, const Value& value)
+{
+ auto method = bus.new_method_call(service.c_str(), objPath.c_str(),
+ PROP_INTF, METHOD_SET);
+
+ method.append(interface, property, value);
+
+ try
+ {
+ bus.call(method);
+ }
+ catch (sdbusplus::exception_t&)
+ {
+ log<level::ERR>("Failed to set property",
+ entry("PROPERTY=%s", property.c_str()),
+ entry("PATH=%s", objPath.c_str()),
+ entry("INTERFACE=%s", interface.c_str()));
+ elog<InternalFailure>();
+ }
+}
+
+ServiceCache::ServiceCache(const std::string& intf, const std::string& path) :
+ intf(intf), path(path), cachedService(std::experimental::nullopt),
+ cachedBusName(std::experimental::nullopt)
+{
+}
+
+ServiceCache::ServiceCache(std::string&& intf, std::string&& path) :
+ intf(std::move(intf)), path(std::move(path)),
+ cachedService(std::experimental::nullopt),
+ cachedBusName(std::experimental::nullopt)
+{
+}
+
+const std::string& ServiceCache::getService(sdbusplus::bus::bus& bus)
+{
+ if (!isValid(bus))
+ {
+ cachedBusName = bus.get_unique_name();
+ cachedService = ::ipmi::getService(bus, intf, path);
+ }
+ return cachedService.value();
+}
+
+void ServiceCache::invalidate()
+{
+ cachedBusName = std::experimental::nullopt;
+ cachedService = std::experimental::nullopt;
+}
+
+sdbusplus::message::message
+ ServiceCache::newMethodCall(sdbusplus::bus::bus& bus, const char* intf,
+ const char* method)
+{
+ return bus.new_method_call(getService(bus).c_str(), path.c_str(), intf,
+ method);
+}
+
+bool ServiceCache::isValid(sdbusplus::bus::bus& bus) const
+{
+ return cachedService && cachedBusName == bus.get_unique_name();
+}
+
+std::string getService(sdbusplus::bus::bus& bus, const std::string& intf,
+ const std::string& path)
+{
+ auto mapperCall =
+ bus.new_method_call("xyz.openbmc_project.ObjectMapper",
+ "/xyz/openbmc_project/object_mapper",
+ "xyz.openbmc_project.ObjectMapper", "GetObject");
+
+ mapperCall.append(path);
+ mapperCall.append(std::vector<std::string>({intf}));
+
+ std::map<std::string, std::vector<std::string>> mapperResponse;
+ try
+ {
+ auto mapperResponseMsg = bus.call(mapperCall);
+ mapperResponseMsg.read(mapperResponse);
+ }
+ catch (sdbusplus::exception_t&)
+ {
+ throw std::runtime_error("ERROR in mapper call");
+ }
+
+ if (mapperResponse.begin() == mapperResponse.end())
+ {
+ throw std::runtime_error("ERROR in reading the mapper response");
+ }
+
+ return mapperResponse.begin()->first;
+}
+
+ipmi::ObjectTree getAllDbusObjects(sdbusplus::bus::bus& bus,
+ const std::string& serviceRoot,
+ const std::string& interface,
+ const std::string& match)
+{
+ std::vector<std::string> interfaces;
+ interfaces.emplace_back(interface);
+
+ auto depth = 0;
+
+ auto mapperCall = bus.new_method_call(MAPPER_BUS_NAME, MAPPER_OBJ,
+ MAPPER_INTF, "GetSubTree");
+
+ mapperCall.append(serviceRoot, depth, interfaces);
+
+ ObjectTree objectTree;
+ try
+ {
+ auto mapperReply = bus.call(mapperCall);
+ mapperReply.read(objectTree);
+ }
+ catch (sdbusplus::exception_t&)
+ {
+ log<level::ERR>("Error in mapper call",
+ entry("SERVICEROOT=%s", serviceRoot.c_str()),
+ entry("INTERFACE=%s", interface.c_str()));
+
+ elog<InternalFailure>();
+ }
+
+ for (auto it = objectTree.begin(); it != objectTree.end();)
+ {
+ if (it->first.find(match) == std::string::npos)
+ {
+ it = objectTree.erase(it);
+ }
+ else
+ {
+ ++it;
+ }
+ }
+
+ return objectTree;
+}
+
+void deleteAllDbusObjects(sdbusplus::bus::bus& bus,
+ const std::string& serviceRoot,
+ const std::string& interface,
+ const std::string& match)
+{
+ try
+ {
+ auto objectTree = getAllDbusObjects(bus, serviceRoot, interface, match);
+
+ for (auto& object : objectTree)
+ {
+ method_no_args::callDbusMethod(bus, object.second.begin()->first,
+ object.first, DELETE_INTERFACE,
+ "Delete");
+ }
+ }
+ catch (sdbusplus::exception::exception& e)
+ {
+ log<level::INFO>("sdbusplus exception - Unable to delete the objects",
+ entry("ERROR=%s", e.what()),
+ entry("INTERFACE=%s", interface.c_str()),
+ entry("SERVICE=%s", serviceRoot.c_str()));
+ }
+}
+
+ObjectTree getAllAncestors(sdbusplus::bus::bus& bus, const std::string& path,
+ InterfaceList&& interfaces)
+{
+ auto convertToString = [](InterfaceList& interfaces) -> std::string {
+ std::string intfStr;
+ for (const auto& intf : interfaces)
+ {
+ intfStr += "," + intf;
+ }
+ return intfStr;
+ };
+
+ auto mapperCall = bus.new_method_call(MAPPER_BUS_NAME, MAPPER_OBJ,
+ MAPPER_INTF, "GetAncestors");
+ mapperCall.append(path, interfaces);
+
+ ObjectTree objectTree;
+ try
+ {
+ auto mapperReply = bus.call(mapperCall);
+ mapperReply.read(objectTree);
+ }
+ catch (sdbusplus::exception_t&)
+ {
+ log<level::ERR>(
+ "Error in mapper call", entry("PATH=%s", path.c_str()),
+ entry("INTERFACES=%s", convertToString(interfaces).c_str()));
+
+ elog<InternalFailure>();
+ }
+
+ if (objectTree.empty())
+ {
+ log<level::ERR>(
+ "No Object has implemented the interface",
+ entry("PATH=%s", path.c_str()),
+ entry("INTERFACES=%s", convertToString(interfaces).c_str()));
+ elog<InternalFailure>();
+ }
+
+ return objectTree;
+}
+
+namespace method_no_args
+{
+
+void callDbusMethod(sdbusplus::bus::bus& bus, const std::string& service,
+ const std::string& objPath, const std::string& interface,
+ const std::string& method)
+
+{
+ auto busMethod = bus.new_method_call(service.c_str(), objPath.c_str(),
+ interface.c_str(), method.c_str());
+
+ try
+ {
+ bus.call(busMethod);
+ }
+ catch (sdbusplus::exception_t&)
+ {
+ log<level::ERR>("Failed to execute method",
+ entry("METHOD=%s", method.c_str()),
+ entry("PATH=%s", objPath.c_str()),
+ entry("INTERFACE=%s", interface.c_str()));
+ elog<InternalFailure>();
+ }
+}
+
+} // namespace method_no_args
+
+namespace network
+{
+
+bool isLinkLocalIP(const std::string& address)
+{
+ return address.find(IPV4_PREFIX) == 0 || address.find(IPV6_PREFIX) == 0;
+}
+
+void createIP(sdbusplus::bus::bus& bus, const std::string& service,
+ const std::string& objPath, const std::string& protocolType,
+ const std::string& ipaddress, uint8_t prefix)
+{
+ std::string gateway = "";
+
+ auto busMethod = bus.new_method_call(service.c_str(), objPath.c_str(),
+ IP_CREATE_INTERFACE, "IP");
+
+ busMethod.append(protocolType, ipaddress, prefix, gateway);
+
+ try
+ {
+ bus.call(busMethod);
+ }
+ catch (sdbusplus::exception_t&)
+ {
+ log<level::ERR>("Failed to execute method", entry("METHOD=%s", "IP"),
+ entry("PATH=%s", objPath.c_str()));
+ elog<InternalFailure>();
+ }
+}
+
+void createVLAN(sdbusplus::bus::bus& bus, const std::string& service,
+ const std::string& objPath, const std::string& interfaceName,
+ uint32_t vlanID)
+{
+ auto busMethod = bus.new_method_call(service.c_str(), objPath.c_str(),
+ VLAN_CREATE_INTERFACE, "VLAN");
+
+ busMethod.append(interfaceName, vlanID);
+
+ try
+ {
+ bus.call(busMethod);
+ }
+ catch (sdbusplus::exception_t&)
+ {
+ log<level::ERR>("Failed to execute method", entry("METHOD=%s", "VLAN"),
+ entry("PATH=%s", objPath.c_str()));
+ elog<InternalFailure>();
+ }
+}
+
+uint8_t toPrefix(int addressFamily, const std::string& subnetMask)
+{
+ if (addressFamily == AF_INET6)
+ {
+ return 0;
+ }
+
+ uint32_t buff{};
+
+ auto rc = inet_pton(addressFamily, subnetMask.c_str(), &buff);
+ if (rc <= 0)
+ {
+ log<level::ERR>("inet_pton failed:",
+ entry("SUBNETMASK=%s", subnetMask.c_str()));
+ return 0;
+ }
+
+ buff = be32toh(buff);
+ // total no of bits - total no of leading zero == total no of ones
+ if (((sizeof(buff) * 8) - (__builtin_ctz(buff))) ==
+ __builtin_popcount(buff))
+ {
+ return __builtin_popcount(buff);
+ }
+ else
+ {
+ log<level::ERR>("Invalid Mask",
+ entry("SUBNETMASK=%s", subnetMask.c_str()));
+ return 0;
+ }
+}
+
+uint32_t getVLAN(const std::string& path)
+{
+ // Path would be look like
+ // /xyz/openbmc_project/network/eth0_443/ipv4
+
+ uint32_t vlanID = 0;
+ try
+ {
+ auto intfObjectPath = path.substr(0, path.find(IP_TYPE) - 1);
+
+ auto intfName = intfObjectPath.substr(intfObjectPath.rfind("/") + 1);
+
+ auto index = intfName.find("_");
+ if (index != std::string::npos)
+ {
+ auto str = intfName.substr(index + 1);
+ vlanID = std::stoul(str);
+ }
+ }
+ catch (std::exception& e)
+ {
+ log<level::ERR>("Exception occurred during getVLAN",
+ entry("PATH=%s", path.c_str()),
+ entry("EXCEPTION=%s", e.what()));
+ }
+ return vlanID;
+}
+
+} // namespace network
+} // namespace ipmi
diff --git a/tests/test_sensorcommands.cpp b/tests/test_sensorcommands.cpp
new file mode 100644
index 0000000..fafe0f7
--- /dev/null
+++ b/tests/test_sensorcommands.cpp
@@ -0,0 +1,144 @@
+#include <cmath>
+#include <sensorutils.hpp>
+
+#include "gtest/gtest.h"
+
+TEST(sensorutils, TranslateToIPMI)
+{
+ /*bool getSensorAttributes(double maxValue, double minValue, int16_t
+ &mValue, int8_t &rExp, int16_t &bValue, int8_t &bExp, bool &bSigned); */
+ // normal unsigned sensor
+ double maxValue = 0xFF;
+ double minValue = 0x0;
+ int16_t mValue;
+ int8_t rExp;
+ int16_t bValue;
+ int8_t bExp;
+ bool bSigned;
+ bool result;
+
+ uint8_t scaledVal;
+
+ result = ipmi::getSensorAttributes(maxValue, minValue, mValue, rExp, bValue,
+ bExp, bSigned);
+ EXPECT_EQ(result, true);
+ if (result)
+ {
+ EXPECT_EQ(bSigned, false);
+ EXPECT_EQ(mValue, 1);
+ EXPECT_EQ(rExp, 0);
+ EXPECT_EQ(bValue, 0);
+ EXPECT_EQ(bExp, 0);
+ }
+ double expected = 0x50;
+ scaledVal = ipmi::scaleIPMIValueFromDouble(0x50, mValue, rExp, bValue, bExp,
+ bSigned);
+ EXPECT_NEAR(scaledVal, expected, expected * 0.01);
+
+ // normal signed sensor
+ maxValue = 127;
+ minValue = -128;
+
+ result = ipmi::getSensorAttributes(maxValue, minValue, mValue, rExp, bValue,
+ bExp, bSigned);
+ EXPECT_EQ(result, true);
+
+ if (result)
+ {
+ EXPECT_EQ(bSigned, true);
+ EXPECT_EQ(mValue, 1);
+ EXPECT_EQ(rExp, 0);
+ EXPECT_EQ(bValue, 0);
+ EXPECT_EQ(bExp, 0);
+ }
+
+ // fan example
+ maxValue = 16000;
+ minValue = 0;
+
+ result = ipmi::getSensorAttributes(maxValue, minValue, mValue, rExp, bValue,
+ bExp, bSigned);
+ EXPECT_EQ(result, true);
+ if (result)
+ {
+ EXPECT_EQ(bSigned, false);
+ EXPECT_EQ(mValue, floor(16000.0 / 0xFF + 0.5));
+ EXPECT_EQ(rExp, 0);
+ EXPECT_EQ(bValue, 0);
+ EXPECT_EQ(bExp, 0);
+ }
+
+ // voltage sensor example
+ maxValue = 20;
+ minValue = 0;
+
+ result = ipmi::getSensorAttributes(maxValue, minValue, mValue, rExp, bValue,
+ bExp, bSigned);
+ EXPECT_EQ(result, true);
+ if (result)
+ {
+ EXPECT_EQ(bSigned, false);
+ EXPECT_EQ(mValue, floor(0.5 + ((20.0 / 0xFF) / std::pow(10, rExp))));
+ EXPECT_EQ(rExp, -3);
+ EXPECT_EQ(bValue, 0);
+ EXPECT_EQ(bExp, 0);
+ }
+ scaledVal = ipmi::scaleIPMIValueFromDouble(12.2, mValue, rExp, bValue, bExp,
+ bSigned);
+
+ expected = 12.2 / (mValue * std::pow(10, rExp));
+ EXPECT_NEAR(scaledVal, expected, expected * 0.01);
+
+ // shifted fan example
+ maxValue = 16000;
+ minValue = 8000;
+
+ result = ipmi::getSensorAttributes(maxValue, minValue, mValue, rExp, bValue,
+ bExp, bSigned);
+ EXPECT_EQ(result, true);
+
+ if (result)
+ {
+ EXPECT_EQ(bSigned, false);
+ EXPECT_EQ(mValue, floor(8000.0 / 0xFF + 0.5));
+ EXPECT_EQ(rExp, 0);
+ EXPECT_EQ(bValue, 80);
+ EXPECT_EQ(bExp, 2);
+ }
+
+ // signed voltage sensor example
+ maxValue = 10;
+ minValue = -10;
+
+ result = ipmi::getSensorAttributes(maxValue, minValue, mValue, rExp, bValue,
+ bExp, bSigned);
+ EXPECT_EQ(result, true);
+ if (result)
+ {
+ EXPECT_EQ(bSigned, true);
+ EXPECT_EQ(mValue, floor(0.5 + ((20.0 / 0xFF) / std::pow(10, rExp))));
+ EXPECT_EQ(rExp, -3);
+ EXPECT_EQ(bValue, 0);
+ EXPECT_EQ(bExp, 0);
+ }
+
+ scaledVal =
+ ipmi::scaleIPMIValueFromDouble(5, mValue, rExp, bValue, bExp, bSigned);
+
+ expected = 5 / (mValue * std::pow(10, rExp));
+ EXPECT_NEAR(scaledVal, expected, expected * 0.01);
+
+ // 0, 0 failure
+ maxValue = 0;
+ minValue = 0;
+ result = ipmi::getSensorAttributes(maxValue, minValue, mValue, rExp, bValue,
+ bExp, bSigned);
+ EXPECT_EQ(result, false);
+
+ // too close failure
+ maxValue = 12;
+ minValue = 10;
+ result = ipmi::getSensorAttributes(maxValue, minValue, mValue, rExp, bValue,
+ bExp, bSigned);
+ EXPECT_EQ(result, false);
+}