cpusensor: detect if cpu is available

Add CPU inventory item detection via gpio.

Tested: inventory item is exposed

Change-Id: Ia26a4bed5534edec6a7f5c7cf0965f84fa0fc23a
Signed-off-by: James Feist <james.feist@linux.intel.com>
diff --git a/CMakeLists.txt b/CMakeLists.txt
index 1d57691..8279694 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -126,6 +126,7 @@
 add_executable (cpusensor src/CPUSensorMain.cpp ${CPU_SRC_FILES})
 add_dependencies (cpusensor sdbusplus-project)
 target_link_libraries (cpusensor ${SENSOR_LINK_LIBS})
+target_link_libraries (cpusensor gpiodcxx)
 
 add_executable (exitairtempsensor src/ExitAirTempSensor.cpp
                 ${EXIT_AIR_SRC_FILES})
diff --git a/include/CPUSensor.hpp b/include/CPUSensor.hpp
index f66e655..1b25518 100644
--- a/include/CPUSensor.hpp
+++ b/include/CPUSensor.hpp
@@ -1,6 +1,12 @@
 #pragma once
 
+#include "Utils.hpp"
+
 #include <Thresholds.hpp>
+#include <boost/container/flat_map.hpp>
+#include <filesystem>
+#include <fstream>
+#include <gpiod.hpp>
 #include <sdbusplus/asio/object_server.hpp>
 #include <sensor.hpp>
 
@@ -37,3 +43,77 @@
 
 extern boost::container::flat_map<std::string, std::unique_ptr<CPUSensor>>
     gCpuSensors;
+
+// this is added to cpusensor.hpp to avoid having every sensor have to link
+// against libgpiod, if another sensor needs it we may move it to utils
+inline bool hostIsPresent(size_t gpioNum)
+{
+    static boost::container::flat_map<size_t, bool> cpuPresence;
+
+    auto findIndex = cpuPresence.find(gpioNum);
+    if (findIndex != cpuPresence.end())
+    {
+        return findIndex->second;
+    }
+
+    constexpr size_t sgpioBase = 232;
+
+    // check if sysfs has device
+    bool sysfs = std::filesystem::exists(gpioPath + std::string("gpio") +
+                                         std::to_string(gpioNum));
+
+    // todo: delete this when we remove all sysfs code
+    if (sysfs)
+    {
+        // close it, we'll reopen it at the end
+        std::ofstream unexport(gpioPath + std::string("unexport"));
+        if (unexport.good())
+        {
+            unexport << gpioNum;
+        }
+        else
+        {
+            std::cerr << "Error cleaning up sysfs device\n";
+        }
+    }
+
+    size_t chipNum = (gpioNum - sgpioBase) / 8;
+    size_t index = (gpioNum - sgpioBase) % 8;
+    gpiod::chip chip("gpiochip" + std::to_string(chipNum));
+    auto line = chip.get_line(index);
+
+    if (!line)
+    {
+        std::cerr << "Error requesting gpio\n";
+        return true;
+    }
+
+    bool resp = true;
+    try
+    {
+        line.request({"adcsensor", gpiod::line_request::DIRECTION_INPUT});
+        resp = !line.get_value();
+    }
+    catch (std::system_error&)
+    {
+        std::cerr << "Error reading gpio\n";
+        return true;
+    }
+
+    // todo: delete this when we remove all sysfs code
+    if (sysfs)
+    {
+        // reopen it
+        std::ofstream populate(gpioPath + std::string("export"));
+        if (populate.good())
+        {
+            populate << gpioNum;
+        }
+        else
+        {
+            std::cerr << "Error cleaning up sysfs device\n";
+        }
+    }
+    cpuPresence[gpioNum] = resp;
+    return resp;
+}
\ No newline at end of file
diff --git a/include/TachSensor.hpp b/include/TachSensor.hpp
index b160a60..2a489dc 100644
--- a/include/TachSensor.hpp
+++ b/include/TachSensor.hpp
@@ -8,7 +8,6 @@
 #include <sdbusplus/asio/object_server.hpp>
 #include <sensor.hpp>
 
-constexpr const char* gpioPath = "/sys/class/gpio/";
 class PresenceSensor
 {
 
diff --git a/include/Utils.hpp b/include/Utils.hpp
index d65fb83..f3abc7f 100644
--- a/include/Utils.hpp
+++ b/include/Utils.hpp
@@ -9,9 +9,13 @@
 #include <sdbusplus/asio/object_server.hpp>
 #include <sdbusplus/message/types.hpp>
 
+constexpr const char* gpioPath = "/sys/class/gpio/";
 const constexpr char* jsonStore = "/var/configuration/flattened.json";
 const constexpr char* inventoryPath = "/xyz/openbmc_project/inventory";
 const constexpr char* entityManagerName = "xyz.openbmc_project.EntityManager";
+
+constexpr const char* cpuInventoryPath =
+    "/xyz/openbmc_project/inventory/system/chassis/motherboard";
 const std::regex illegalDbusRegex("[^A-Za-z0-9_]");
 
 using BasicVariantType =
diff --git a/src/CPUSensorMain.cpp b/src/CPUSensorMain.cpp
index d16cf8a..44c5178 100644
--- a/src/CPUSensorMain.cpp
+++ b/src/CPUSensorMain.cpp
@@ -515,9 +515,14 @@
     });
 }
 
-bool getCpuConfig(const std::shared_ptr<sdbusplus::asio::connection>& systemBus,
-                  boost::container::flat_set<CPUConfig>& cpuConfigs,
-                  ManagedObjectType& sensorConfigs)
+bool getCpuConfig(
+    const std::shared_ptr<sdbusplus::asio::connection>& systemBus,
+    boost::container::flat_set<CPUConfig>& cpuConfigs,
+    ManagedObjectType& sensorConfigs,
+    sdbusplus::asio::object_server& objectServer,
+    boost::container::flat_map<
+        std::string, std::shared_ptr<sdbusplus::asio::dbus_interface>>&
+        inventoryIfaces)
 {
     bool useCache = false;
     sensorConfigs.clear();
@@ -559,6 +564,28 @@
                 std::string name =
                     std::regex_replace(nameRaw, illegalDbusRegex, "_");
 
+                auto findCpuGpio = config.second.find("PresenceGpio");
+                if (findCpuGpio != config.second.end())
+                {
+                    size_t gpio = std::visit(VariantToUnsignedIntVisitor(),
+                                             findCpuGpio->second);
+                    bool present = hostIsPresent(gpio);
+                    if (inventoryIfaces.find(name) == inventoryIfaces.end())
+                    {
+                        auto iface = objectServer.add_interface(
+                            cpuInventoryPath + std::string("/") + name,
+                            "xyz.openbmc_project.Inventory.Item");
+                        iface->register_property("PrettyName", name);
+                        iface->register_property("Present", present);
+                        iface->initialize();
+                        inventoryIfaces[name] = std::move(iface);
+                    }
+                    if (!present)
+                    {
+                        continue; // no reason to look for non present cpu
+                    }
+                }
+
                 auto findBus = config.second.find("Bus");
                 if (findBus == config.second.end())
                 {
@@ -614,6 +641,9 @@
     boost::asio::deadline_timer creationTimer(io);
     boost::asio::deadline_timer filterTimer(io);
     ManagedObjectType sensorConfigs;
+    boost::container::flat_map<std::string,
+                               std::shared_ptr<sdbusplus::asio::dbus_interface>>
+        inventoryIfaces;
 
     filterTimer.expires_from_now(boost::posix_time::seconds(1));
     filterTimer.async_wait([&](const boost::system::error_code& ec) {
@@ -622,7 +652,8 @@
             return; // we're being canceled
         }
 
-        if (getCpuConfig(systemBus, cpuConfigs, sensorConfigs))
+        if (getCpuConfig(systemBus, cpuConfigs, sensorConfigs, objectServer,
+                         inventoryIfaces))
         {
             detectCpuAsync(pingTimer, creationTimer, io, objectServer,
                            systemBus, cpuConfigs, sensorConfigs);
@@ -650,7 +681,8 @@
                     return; // we're being canceled
                 }
 
-                if (getCpuConfig(systemBus, cpuConfigs, sensorConfigs))
+                if (getCpuConfig(systemBus, cpuConfigs, sensorConfigs,
+                                 objectServer, inventoryIfaces))
                 {
                     detectCpuAsync(pingTimer, creationTimer, io, objectServer,
                                    systemBus, cpuConfigs, sensorConfigs);
diff --git a/src/TachSensor.cpp b/src/TachSensor.cpp
index 295c7e6..bbd382f 100644
--- a/src/TachSensor.cpp
+++ b/src/TachSensor.cpp
@@ -14,10 +14,12 @@
 // limitations under the License.
 */
 
+#include "TachSensor.hpp"
+
+#include "Utils.hpp"
+
 #include <unistd.h>
 
-#include <TachSensor.hpp>
-#include <Utils.hpp>
 #include <boost/algorithm/string/predicate.hpp>
 #include <boost/algorithm/string/replace.hpp>
 #include <boost/date_time/posix_time/posix_time.hpp>
diff --git a/src/Utils.cpp b/src/Utils.cpp
index 88a8390..d90813d 100644
--- a/src/Utils.cpp
+++ b/src/Utils.cpp
@@ -31,6 +31,7 @@
 
 static bool powerStatusOn = false;
 static bool biosHasPost = false;
+
 static std::unique_ptr<sdbusplus::bus::match::match> powerMatch = nullptr;
 
 bool getSensorConfiguration(