hostSelector_switch: add support polling mode

Task Description:
Add support for polling mode to periodically sample the group GPIO
selector inputs and update the HostSelector interface’s Position
property on D-Bus.

Motivation:
-Some platforms expose the host-selector via a CPLD as GPIO inputs
without edge interrupts.
-Polling ensures all position changes are detected reliably.

Design:
- Enabled polling through the 'polling_mode' and 'polling_interval_ms'
  options in JSON configuration.
- Introduced pollTimerHandler to periodically check GPIO states
  and update the internal host selector position.

Tested:
journalctl -xeu xyz.openbmc_project.Chassis.Buttons.service -f
Jul 31 00:06:47 bmc buttons[629]: Start Phosphor buttons service...
Jul 31 00:06:47 bmc buttons[629]: Started polling mode: 2000ms
Jul 31 00:06:49 bmc buttons[629]: Host selector position updated to 0
Jul 31 00:06:53 bmc systemd[1]: Started Phosphor Buttons.

Jul 31 00:13:50 bmc buttons[629]: Host selector position updated to 1
Jul 31 00:13:52 bmc buttons[629]: Host selector position updated to 2
Jul 31 00:13:54 bmc buttons[629]: Host selector position updated to 3
Jul 31 00:14:00 bmc buttons[629]: Host selector position updated to 4
Jul 31 00:14:04 bmc buttons[629]: Host selector position updated to 5
Jul 31 00:14:10 bmc buttons[629]: Host selector position updated to 6
Jul 31 00:14:14 bmc buttons[629]: Host selector position updated to 7
Jul 31 00:14:16 bmc buttons[629]: Host selector position updated to 8
Jul 31 00:14:20 bmc buttons[629]: Host selector position updated to 0

Change-Id: I8b95d773191f1af03b747f9eeb33494da9878bc8
Signed-off-by: Liora Guo <liora.guo.wiwynn@gmail.com>
diff --git a/README.md b/README.md
index 9e2d5b6..8b63513 100644
--- a/README.md
+++ b/README.md
@@ -143,6 +143,19 @@
   the value read from the host selector gpios is mapped to the respective host
   number.
 
+**Optional: Polling Configuration** Some platforms expose the host selector via
+a CPLD as GPIO inputs without edge-triggered interrupt support (e.g., no edge
+detection on GPIOs). In such situations, software-based polling mode can be
+enabled using the following optional fields in the JSON configuration:
+
+- **polling_mode** : Set to `true` to enable polling mode.
+- **polling_interval_ms** (optional): Polling interval in milliseconds. Defaults
+  to `1000` ms if not specified.
+
+### Config Example
+
+#### A.Interrupt example
+
 Example : The value of "7" derived from the 4 host select gpio lines are mapped
 to host position 1.
 
@@ -192,6 +205,54 @@
 }
 ```
 
+#### B.Polling example
+
+```json
+{
+  "name": "HOST_SELECTOR",
+  "polling_mode": true,
+  "polling_interval_ms": 3000,
+  "group_gpio_config": [
+    {
+      "name": "host_select_sel_0",
+      "num": 2340,
+      "direction": "in",
+      "polarity": "active_low"
+    },
+    {
+      "name": "host_select_sel_1",
+      "num": 2341,
+      "direction": "in",
+      "polarity": "active_low"
+    },
+    {
+      "name": "host_select_sel_2",
+      "num": 2342,
+      "direction": "in",
+      "polarity": "active_low"
+    },
+    {
+      "name": "host_select_sel_3",
+      "num": 2343,
+      "direction": "in",
+      "polarity": "active_low"
+    }
+  ],
+  "max_position": 8,
+  "host_selector_map": {
+    "0": 0,
+    "1": 1,
+    "2": 2,
+    "3": 3,
+    "4": 4,
+    "5": 5,
+    "6": 6,
+    "7": 7,
+    "8": 8
+  }
+}
+```
+
 ### Host selector cpld config example
 
 There are also some systems that get the host selector selection from the CPLD,
diff --git a/inc/hostSelector_switch.hpp b/inc/hostSelector_switch.hpp
index 04f80bb..24947ea 100644
--- a/inc/hostSelector_switch.hpp
+++ b/inc/hostSelector_switch.hpp
@@ -11,9 +11,17 @@
 
 #include <nlohmann/json.hpp>
 #include <phosphor-logging/elog-errors.hpp>
+#include <sdeventplus/event.hpp>
+#include <sdeventplus/utility/timer.hpp>
 
+#include <chrono>
 #include <fstream>
 #include <iostream>
+#include <optional>
+
+using sdeventplus::ClockId;
+using sdeventplus::Event;
+using Timer = sdeventplus::utility::Timer<ClockId::Monotonic>;
 
 static constexpr auto HOST_SELECTOR = "HOST_SELECTOR";
 
@@ -66,10 +74,15 @@
     void setInitialHostSelectorValue(void);
     void setHostSelectorValue(int fd, GpioState state);
     char getValueFromFd(int fd);
+    void pollGpioState();
+
+  private:
+    std::optional<Timer> pollTimer;
 
   protected:
     size_t hostSelectorPosition = 0;
     size_t gpioLineCount;
+    size_t previousPos = INVALID_INDEX;
 
     // map of read Host selector switch value and corresponding host number
     // value.
diff --git a/src/hostSelector_switch.cpp b/src/hostSelector_switch.cpp
index c398e35..567f338 100644
--- a/src/hostSelector_switch.cpp
+++ b/src/hostSelector_switch.cpp
@@ -1,6 +1,8 @@
 
 #include "hostSelector_switch.hpp"
 
+#include "gpio.hpp"
+
 #include <error.h>
 
 #include <phosphor-logging/lg2.hpp>
@@ -87,6 +89,19 @@
                    getFormFactorType(), "ERROR", e.what());
     }
 
+    if (config.extraJsonInfo.value("polling_mode", false))
+    {
+        // If polling mode is enabled, set up a timer to poll the GPIO state
+        int intervalMs =
+            config.extraJsonInfo.value("polling_interval_ms", 1000);
+        auto event = Event::get_default();
+        pollTimer.emplace(
+            event, [this](Timer&) { pollGpioState(); },
+            std::chrono::milliseconds(intervalMs));
+        pollTimer->setEnabled(true);
+        lg2::info("Started polling mode: {MS}ms", "MS", intervalMs);
+    }
+
     if (hsPosMapped != INVALID_INDEX)
     {
         position(hsPosMapped, true);
@@ -152,3 +167,23 @@
         position(hsPosMapped);
     }
 }
+
+void HostSelector::pollGpioState()
+{
+    for (const auto& gpioInfo : config.gpios)
+    {
+        GpioState state = getGpioState(gpioInfo.fd, gpioInfo.polarity);
+        setHostSelectorValue(gpioInfo.fd, state);
+        lg2::debug("GPIO {NUM} state is {STATE}", "NUM", gpioInfo.number,
+                   "STATE", state);
+    }
+
+    size_t currentPos = getMappedHSConfig(hostSelectorPosition);
+
+    if (currentPos != INVALID_INDEX && currentPos != previousPos)
+    {
+        position(currentPos);
+        previousPos = currentPos;
+        lg2::info("Host selector position updated to {POS}", "POS", currentPos);
+    }
+}