TachSensor: Support multiple fans on the same GPIO

On IBM SBP1 two fans share the same enclosure, even though they can be
controlled independently as they have two distinct I2C PWM controllers
with separate temperature sensors.
The fan enclosure occupies only one GPIO for presence detection.

Allow the same presence GPIO to be used within multiple TachSensors.

TEST: Used the same GPIO on two I2CFans, both disappear when unplugged.

Change-Id: I946e4579a361f00512eb707d2e5eb3b9ec7f2a55
Signed-off-by: Patrick Rudolph <patrick.rudolph@9elements.com>
diff --git a/src/FanMain.cpp b/src/FanMain.cpp
index 484cfbb..8061d4c 100644
--- a/src/FanMain.cpp
+++ b/src/FanMain.cpp
@@ -274,15 +274,17 @@
         tachSensors,
     boost::container::flat_map<std::string, std::unique_ptr<PwmSensor>>&
         pwmSensors,
+    boost::container::flat_map<std::string, std::weak_ptr<PresenceSensor>>&
+        presenceSensors,
     std::shared_ptr<sdbusplus::asio::connection>& dbusConnection,
     const std::shared_ptr<boost::container::flat_set<std::string>>&
         sensorsChanged,
     size_t retries = 0)
 {
     auto getter = std::make_shared<GetSensorConfiguration>(
-        dbusConnection,
-        [&io, &objectServer, &tachSensors, &pwmSensors, &dbusConnection,
-         sensorsChanged](const ManagedObjectType& sensorConfigurations) {
+        dbusConnection, [&io, &objectServer, &tachSensors, &pwmSensors,
+                         &presenceSensors, &dbusConnection, sensorsChanged](
+                            const ManagedObjectType& sensorConfigurations) {
         bool firstScan = sensorsChanged == nullptr;
         std::vector<fs::path> paths;
         if (!findFiles(fs::path("/sys/class/hwmon"), R"(fan\d+_input)", paths))
@@ -429,7 +431,7 @@
             auto presenceConfig =
                 sensorData->find(cfgIntf + std::string(".Presence"));
 
-            std::unique_ptr<PresenceSensor> presenceSensor(nullptr);
+            std::shared_ptr<PresenceSensor> presenceSensor(nullptr);
 
             // presence sensors are optional
             if (presenceConfig != sensorData->end())
@@ -446,11 +448,27 @@
                 {
                     bool inverted =
                         std::get<std::string>(findPolarity->second) == "Low";
-                    if (const auto* pinName =
-                            std::get_if<std::string>(&findPinName->second))
+                    const auto* pinName =
+                        std::get_if<std::string>(&findPinName->second);
+
+                    if (pinName != nullptr)
                     {
-                        presenceSensor = std::make_unique<PresenceSensor>(
-                            *pinName, inverted, io, sensorName);
+                        auto findPresenceSensor =
+                            presenceSensors.find(*pinName);
+                        if (findPresenceSensor != presenceSensors.end())
+                        {
+                            auto p = findPresenceSensor->second.lock();
+                            if (p)
+                            {
+                                presenceSensor = p;
+                            }
+                        }
+                        if (!presenceSensor)
+                        {
+                            presenceSensor = std::make_shared<PresenceSensor>(
+                                *pinName, inverted, io, sensorName);
+                            presenceSensors[*pinName] = presenceSensor;
+                        }
                     }
                     else
                     {
@@ -556,7 +574,7 @@
             tachSensor = nullptr;
             tachSensor = std::make_shared<TachSensor>(
                 path.string(), baseType, objectServer, dbusConnection,
-                std::move(presenceSensor), redundancy, io, sensorName,
+                presenceSensor, redundancy, io, sensorName,
                 std::move(sensorThresholds), *interfacePath, limits, powerState,
                 led);
             tachSensor->setupRead();
@@ -591,12 +609,14 @@
         tachSensors;
     boost::container::flat_map<std::string, std::unique_ptr<PwmSensor>>
         pwmSensors;
+    boost::container::flat_map<std::string, std::weak_ptr<PresenceSensor>>
+        presenceSensors;
     auto sensorsChanged =
         std::make_shared<boost::container::flat_set<std::string>>();
 
     boost::asio::post(io, [&]() {
-        createSensors(io, objectServer, tachSensors, pwmSensors, systemBus,
-                      nullptr);
+        createSensors(io, objectServer, tachSensors, pwmSensors,
+                      presenceSensors, systemBus, nullptr);
     });
 
     boost::asio::steady_timer filterTimer(io);
@@ -622,8 +642,8 @@
                 std::cerr << "timer error\n";
                 return;
             }
-            createSensors(io, objectServer, tachSensors, pwmSensors, systemBus,
-                          sensorsChanged, 5);
+            createSensors(io, objectServer, tachSensors, pwmSensors,
+                          presenceSensors, systemBus, sensorsChanged, 5);
         });
     };
 
diff --git a/src/TachSensor.cpp b/src/TachSensor.cpp
index 5019fd5..b31c1b7 100644
--- a/src/TachSensor.cpp
+++ b/src/TachSensor.cpp
@@ -47,7 +47,7 @@
 TachSensor::TachSensor(const std::string& path, const std::string& objectType,
                        sdbusplus::asio::object_server& objectServer,
                        std::shared_ptr<sdbusplus::asio::connection>& conn,
-                       std::unique_ptr<PresenceSensor>&& presenceSensor,
+                       std::shared_ptr<PresenceSensor>& presenceSensor,
                        std::optional<RedundancySensor>* redundancy,
                        boost::asio::io_context& io, const std::string& fanName,
                        std::vector<thresholds::Threshold>&& thresholdsIn,
@@ -58,8 +58,7 @@
     Sensor(escapeName(fanName), std::move(thresholdsIn), sensorConfiguration,
            objectType, false, false, limits.second, limits.first, conn,
            powerState),
-    objServer(objectServer), redundancy(redundancy),
-    presence(std::move(presenceSensor)),
+    objServer(objectServer), redundancy(redundancy), presence(presenceSensor),
     inputDev(io, path, boost::asio::random_access_file::read_only),
     waitTimer(io), path(path), led(ledIn)
 {
diff --git a/src/TachSensor.hpp b/src/TachSensor.hpp
index 845fbec..15ed2e4 100644
--- a/src/TachSensor.hpp
+++ b/src/TachSensor.hpp
@@ -68,7 +68,7 @@
     TachSensor(const std::string& path, const std::string& objectType,
                sdbusplus::asio::object_server& objectServer,
                std::shared_ptr<sdbusplus::asio::connection>& conn,
-               std::unique_ptr<PresenceSensor>&& presence,
+               std::shared_ptr<PresenceSensor>& presence,
                std::optional<RedundancySensor>* redundancy,
                boost::asio::io_context& io, const std::string& fanName,
                std::vector<thresholds::Threshold>&& thresholds,
@@ -85,7 +85,7 @@
     std::array<char, 128> readBuf{};
     sdbusplus::asio::object_server& objServer;
     std::optional<RedundancySensor>* redundancy;
-    std::unique_ptr<PresenceSensor> presence;
+    std::shared_ptr<PresenceSensor> presence;
     std::shared_ptr<sdbusplus::asio::dbus_interface> itemIface;
     std::shared_ptr<sdbusplus::asio::dbus_interface> itemAssoc;
     boost::asio::random_access_file inputDev;