Add Host selector button interface
This change includes new button interface
for the host selector switch.
The button handler code is adapted to support
both single host and multiple host power control
dbus events.
design : https://gerrit.openbmc-project.xyz/c/openbmc/docs/+/45544
Signed-off-by: Naveen Moses <naveen.mosess@hcl.com>
Change-Id: Icbfb22baaee057fd255c3ab0cba129693b913a9d
diff --git a/src/button_handler.cpp b/src/button_handler.cpp
index 6362f82..55f5db5 100644
--- a/src/button_handler.cpp
+++ b/src/button_handler.cpp
@@ -5,7 +5,6 @@
#include <phosphor-logging/log.hpp>
#include <xyz/openbmc_project/State/Chassis/server.hpp>
#include <xyz/openbmc_project/State/Host/server.hpp>
-
namespace phosphor
{
namespace button
@@ -15,18 +14,24 @@
using namespace sdbusplus::xyz::openbmc_project::State::server;
using namespace phosphor::logging;
-constexpr auto propertyIface = "org.freedesktop.DBus.Properties";
constexpr auto chassisIface = "xyz.openbmc_project.State.Chassis";
constexpr auto hostIface = "xyz.openbmc_project.State.Host";
constexpr auto powerButtonIface = "xyz.openbmc_project.Chassis.Buttons.Power";
constexpr auto idButtonIface = "xyz.openbmc_project.Chassis.Buttons.ID";
constexpr auto resetButtonIface = "xyz.openbmc_project.Chassis.Buttons.Reset";
-constexpr auto mapperIface = "xyz.openbmc_project.ObjectMapper";
constexpr auto ledGroupIface = "xyz.openbmc_project.Led.Group";
+constexpr auto ledGroupBasePath = "/xyz/openbmc_project/led/groups/";
+constexpr auto hostSelectorIface =
+ "xyz.openbmc_project.Chassis.Buttons.HostSelector";
+constexpr auto debugHostSelectorIface =
+ "xyz.openbmc_project.Chassis.Buttons.DebugHostSelector";
+
+constexpr auto propertyIface = "org.freedesktop.DBus.Properties";
+constexpr auto mapperIface = "xyz.openbmc_project.ObjectMapper";
constexpr auto mapperObjPath = "/xyz/openbmc_project/object_mapper";
constexpr auto mapperService = "xyz.openbmc_project.ObjectMapper";
-constexpr auto ledGroupBasePath = "/xyz/openbmc_project/led/groups/";
+constexpr auto BMC_POSITION = 0;
Handler::Handler(sdbusplus::bus::bus& bus) : bus(bus)
{
@@ -97,7 +102,11 @@
// The button wasn't implemented
}
}
-
+bool Handler::isMultiHost()
+{
+ // return true in case host selector object is available
+ return (!getService(HS_DBUS_OBJECT_NAME, hostSelectorIface).empty());
+}
std::string Handler::getService(const std::string& path,
const std::string& interface) const
{
@@ -111,12 +120,43 @@
return objectData.begin()->first;
}
-
-bool Handler::poweredOn() const
+size_t Handler::getHostSelectorValue()
{
- auto service = getService(CHASSIS_STATE_OBJECT_NAME, chassisIface);
+ auto HSService = getService(HS_DBUS_OBJECT_NAME, hostSelectorIface);
+
+ if (HSService.empty())
+ {
+ log<level::INFO>("Host Selector dbus object not available");
+ throw std::invalid_argument("Host selector dbus object not available");
+ }
+
+ try
+ {
+ auto method = bus.new_method_call(
+ HSService.c_str(), HS_DBUS_OBJECT_NAME, propertyIface, "Get");
+ method.append(hostSelectorIface, "Position");
+ auto result = bus.call(method);
+
+ std::variant<size_t> HSPositionVariant;
+ result.read(HSPositionVariant);
+
+ auto position = std::get<size_t>(HSPositionVariant);
+ return position;
+ }
+ catch (const sdbusplus::exception::exception& e)
+ {
+ log<level::ERR>("Error reading Host selector Position",
+ entry("ERROR=%s", e.what()));
+ throw;
+ }
+}
+bool Handler::poweredOn(size_t hostNumber) const
+{
+ auto chassisObjectName =
+ CHASSIS_STATE_OBJECT_NAME + std::to_string(hostNumber);
+ auto service = getService(chassisObjectName.c_str(), chassisIface);
auto method = bus.new_method_call(
- service.c_str(), CHASSIS_STATE_OBJECT_NAME, propertyIface, "Get");
+ service.c_str(), chassisObjectName.c_str(), propertyIface, "Get");
method.append(chassisIface, "CurrentPowerState");
auto result = bus.call(method);
@@ -127,27 +167,118 @@
Chassis::convertPowerStateFromString(std::get<std::string>(state));
}
-void Handler::powerPressed(sdbusplus::message::message& msg)
+void Handler::handlePowerEvent(PowerEvent powerEventType)
{
- auto transition = Host::Transition::On;
+ std::string objPathName;
+ std::string dbusIfaceName;
+ std::string transitionName;
+ std::variant<Host::Transition, Chassis::Transition> transition;
- try
+ size_t hostNumber = 0;
+ auto isMultiHostSystem = isMultiHost();
+ if (isMultiHostSystem)
{
- if (poweredOn())
- {
- transition = Host::Transition::Off;
+ hostNumber = getHostSelectorValue();
+ log<level::INFO>("Multi host system detected : ",
+ entry("POSITION=%d", hostNumber));
+ }
+
+ std::string hostNumStr = std::to_string(hostNumber);
+
+ // ignore power and reset button events if BMC is selected.
+ if (isMultiHostSystem && (hostNumber == BMC_POSITION) &&
+ (powerEventType != PowerEvent::longPowerPressed))
+ {
+ log<level::INFO>("handlePowerEvent : BMC selected on multihost system."
+ "ignoring power and reset button events...");
+ return;
+ }
+
+ switch (powerEventType)
+ {
+ case PowerEvent::powerPressed: {
+ objPathName = HOST_STATE_OBJECT_NAME + hostNumStr;
+ dbusIfaceName = hostIface;
+ transitionName = "RequestedHostTransition";
+
+ transition = Host::Transition::On;
+
+ if (poweredOn(hostNumber))
+ {
+ transition = Host::Transition::Off;
+ }
+ log<level::INFO>("handlePowerEvent : handle power button press ");
+
+ break;
+ }
+ case PowerEvent::longPowerPressed: {
+ dbusIfaceName = chassisIface;
+ transitionName = "RequestedPowerTransition";
+ objPathName = CHASSIS_STATE_OBJECT_NAME + hostNumStr;
+ transition = Chassis::Transition::Off;
+
+ /* multi host system :
+ hosts (1 to N) - host shutdown
+ bmc (0) - sled cycle
+ single host system :
+ host(0) - host shutdown
+ */
+ if (isMultiHostSystem && (hostNumber == BMC_POSITION))
+ {
+#if CHASSIS_SYSTEM_RESET_ENABLED
+ objPathName = CHASSISSYSTEM_STATE_OBJECT_NAME + hostNumStr;
+ transition = Chassis::Transition::PowerCycle;
+#else
+ return;
+#endif
+ }
+ else if (!poweredOn(hostNumber))
+ {
+ log<level::INFO>(
+ "Power is off so ignoring long power button press");
+ return;
+ }
+ log<level::INFO>(
+ "handlePowerEvent : handle long power button press");
+
+ break;
}
- log<level::INFO>("Handling power button press");
+ case PowerEvent::resetPressed: {
- std::variant<std::string> state = convertForMessage(transition);
+ objPathName = HOST_STATE_OBJECT_NAME + hostNumStr;
+ dbusIfaceName = hostIface;
+ transitionName = "RequestedHostTransition";
- auto service = getService(HOST_STATE_OBJECT_NAME, hostIface);
- auto method = bus.new_method_call(
- service.c_str(), HOST_STATE_OBJECT_NAME, propertyIface, "Set");
- method.append(hostIface, "RequestedHostTransition", state);
+ if (!poweredOn(hostNumber))
+ {
+ log<level::INFO>("Power is off so ignoring reset button press");
+ return;
+ }
- bus.call(method);
+ log<level::INFO>("Handling reset button press");
+ transition = Host::Transition::Reboot;
+ break;
+ }
+ default: {
+ log<level::ERR>(
+ "Invalid power event. skipping...",
+ entry("EVENT=%d", static_cast<int>(powerEventType)));
+
+ return;
+ }
+ }
+ auto service = getService(objPathName.c_str(), dbusIfaceName);
+ auto method = bus.new_method_call(service.c_str(), objPathName.c_str(),
+ propertyIface, "Set");
+ method.append(dbusIfaceName, transitionName, transition);
+ bus.call(method);
+}
+void Handler::powerPressed(sdbusplus::message::message& msg)
+{
+ try
+ {
+ handlePowerEvent(PowerEvent::powerPressed);
}
catch (const sdbusplus::exception::exception& e)
{
@@ -155,29 +286,11 @@
entry("ERROR=%s", e.what()));
}
}
-
void Handler::longPowerPressed(sdbusplus::message::message& msg)
{
try
{
- if (!poweredOn())
- {
- log<level::INFO>(
- "Power is off so ignoring long power button press");
- return;
- }
-
- log<level::INFO>("Handling long power button press");
-
- std::variant<std::string> state =
- convertForMessage(Chassis::Transition::Off);
-
- auto service = getService(CHASSIS_STATE_OBJECT_NAME, chassisIface);
- auto method = bus.new_method_call(
- service.c_str(), CHASSIS_STATE_OBJECT_NAME, propertyIface, "Set");
- method.append(chassisIface, "RequestedPowerTransition", state);
-
- bus.call(method);
+ handlePowerEvent(PowerEvent::longPowerPressed);
}
catch (const sdbusplus::exception::exception& e)
{
@@ -190,24 +303,7 @@
{
try
{
- if (!poweredOn())
- {
- log<level::INFO>("Power is off so ignoring reset button press");
- return;
- }
-
- log<level::INFO>("Handling reset button press");
-
- std::variant<std::string> state =
- convertForMessage(Host::Transition::Reboot);
-
- auto service = getService(HOST_STATE_OBJECT_NAME, hostIface);
- auto method = bus.new_method_call(
- service.c_str(), HOST_STATE_OBJECT_NAME, propertyIface, "Set");
-
- method.append(hostIface, "RequestedHostTransition", state);
-
- bus.call(method);
+ handlePowerEvent(PowerEvent::resetPressed);
}
catch (const sdbusplus::exception::exception& e)
{
diff --git a/src/hostSelector_switch.cpp b/src/hostSelector_switch.cpp
new file mode 100644
index 0000000..c3ad83f
--- /dev/null
+++ b/src/hostSelector_switch.cpp
@@ -0,0 +1,133 @@
+
+#include "hostSelector_switch.hpp"
+
+// add the button iface class to registry
+static ButtonIFRegister<HostSelector> buttonRegister;
+using namespace phosphor::logging;
+size_t HostSelector::getMappedHSConfig(size_t hsPosition)
+{
+ size_t adjustedPosition = INVALID_INDEX; // set bmc as default value
+ std::string hsPosStr;
+ hsPosStr = std::to_string(hsPosition);
+
+ if (hsPosMap.find(hsPosStr) != hsPosMap.end())
+ {
+ adjustedPosition = hsPosMap[hsPosStr];
+ }
+ else
+ {
+ log<level::DEBUG>(
+ "getMappedHSConfig : no valid value in map.",
+ entry("FORM_FACTOR_TYPE=%s", (getFormFactorType()).c_str()));
+ }
+ return adjustedPosition;
+}
+
+size_t HostSelector::getGpioIndex(int fd)
+{
+ for (size_t index = 0; index < gpioLineCount; index++)
+ {
+ if (config.gpios[index].fd == fd)
+ {
+ return index;
+ }
+ }
+ return INVALID_INDEX;
+}
+void HostSelector::setInitialHostSelectorValue()
+{
+ char buf;
+ for (int index = 0; index < gpioLineCount; index++)
+ {
+ auto result = ::lseek(config.gpios[index].fd, 0, SEEK_SET);
+
+ if (result < 0)
+ {
+ log<level::ERR>(
+ "gpio fd lseek error!",
+ entry("FORM_FACTOR_TYPE=%s", (getFormFactorType()).c_str()));
+ throw sdbusplus::xyz::openbmc_project::Chassis::Common::Error::
+ IOError();
+ }
+
+ result = ::read(config.gpios[index].fd, &buf, sizeof(buf));
+ if (result < 0)
+ {
+ log<level::ERR>(
+ "gpio fd read error!",
+ entry("FORM_FACTOR_TYPE=%s", (getFormFactorType()).c_str()));
+ throw sdbusplus::xyz::openbmc_project::Chassis::Common::Error::
+ IOError();
+ }
+ GpioState gpioState =
+ (buf == '0') ? (GpioState::low) : (GpioState::high);
+ setHostSelectorValue(config.gpios[index].fd, gpioState);
+ size_t hsPosMapped = getMappedHSConfig(hostSelectorPosition);
+ if (hsPosMapped != INVALID_INDEX)
+ {
+ position(hsPosMapped, true);
+ }
+ }
+}
+
+void HostSelector::setHostSelectorValue(int fd, GpioState state)
+{
+ size_t pos = getGpioIndex(fd);
+
+ if (pos == INVALID_INDEX)
+ {
+ return;
+ }
+ auto set_bit = [](size_t& val, size_t n) { val |= 0xff & (1 << n); };
+
+ auto clr_bit = [](size_t& val, size_t n) { val &= ~(0xff & (1 << n)); };
+
+ auto bit_op = (state == GpioState::low) ? set_bit : clr_bit;
+
+ bit_op(hostSelectorPosition, pos);
+ return;
+}
+/**
+ * @brief This method is called from sd-event provided callback function
+ * callbackHandler if platform specific event handling is needed then a
+ * derived class instance with its specific event handling logic along with
+ * init() function can be created to override the default event handling
+ */
+
+void HostSelector::handleEvent(sd_event_source* es, int fd, uint32_t revents)
+{
+ int n = -1;
+ char buf = '0';
+
+ n = ::lseek(fd, 0, SEEK_SET);
+
+ if (n < 0)
+ {
+ log<level::ERR>(
+ "gpio fd lseek error!",
+ entry("FORM_FACTOR_TYPE=%s", (getFormFactorType()).c_str()));
+ return;
+ }
+
+ n = ::read(fd, &buf, sizeof(buf));
+ if (n < 0)
+ {
+ log<level::ERR>(
+ "gpio fd read error!",
+ entry("FORM_FACTOR_TYPE=%s", (getFormFactorType()).c_str()));
+ throw sdbusplus::xyz::openbmc_project::Chassis::Common::Error::
+ IOError();
+ }
+
+ // read the gpio state for the io event received
+ GpioState gpioState = (buf == '0') ? (GpioState::low) : (GpioState::high);
+
+ setHostSelectorValue(fd, gpioState);
+
+ size_t hsPosMapped = getMappedHSConfig(hostSelectorPosition);
+
+ if (hsPosMapped != INVALID_INDEX)
+ {
+ position(hsPosMapped);
+ }
+}
\ No newline at end of file
diff --git a/src/main.cpp b/src/main.cpp
index cfa224e..e1efd3e 100644
--- a/src/main.cpp
+++ b/src/main.cpp
@@ -22,6 +22,9 @@
#include <phosphor-logging/elog-errors.hpp>
static constexpr auto gpioDefFile = "/etc/default/obmc/gpio/gpio_defs.json";
+using namespace phosphor::logging;
+nlohmann::json gpioDefs;
+
int main(int argc, char* argv[])
{
int ret = 0;
@@ -49,8 +52,8 @@
std::vector<std::unique_ptr<ButtonIface>> buttonInterfaces;
std::ifstream gpios{gpioDefFile};
- auto json = nlohmann::json::parse(gpios, nullptr, true);
- auto gpioDefs = json["gpio_definitions"];
+ auto gpioDefJson = nlohmann::json::parse(gpios, nullptr, true);
+ gpioDefs = gpioDefJson["gpio_definitions"];
// load gpio config from gpio defs json file and create button interface
// objects based on the button form factor type
@@ -60,6 +63,7 @@
std::string formFactorName = gpioConfig["name"];
buttonConfig buttonCfg;
buttonCfg.formFactorName = formFactorName;
+ buttonCfg.extraJsonInfo = gpioConfig;
/* The folloing code checks if the gpio config read
from json file is single gpio config or group gpio config,