Support multi power buttons with multi behaviors
For supporting more-than-one power buttons behaviors,
add new matches and instances by following json config.
This change is for multiple slots integrated on one chassis,
and each slot has button to control power status.
For example:
/xyz/openbmc_project/Chassis/Buttons/Power01 for button on slot1
/xyz/openbmc_project/Chassis/Buttons/Power02 for button on slot2
Moreover, support multi-level power control by json config,
chassis now can do action by corresponding pressing duration.
Tested:
Press buttons and check corresponding behaviors.
Change-Id: I7789f0367d5e846dd9e68f966ba0755fc916217a
Signed-off-by: Rush Chen <rush.chen.wiwynn@gmail.com>
diff --git a/README.md b/README.md
index d3da7e5..47f3c45 100644
--- a/README.md
+++ b/README.md
@@ -297,3 +297,54 @@
}
}
```
+
+## Multiple power gpio config example
+
+This config is used for configs with multiple power buttons on each slots and
+whole sled, or for which needs multi-level chassis power control behavior.
+
+name - this is name of the specific gpio line, "POWER_BUTTON" + the index of
+chassis instance
+
+- pin - this represents the pin number from linux dts file.
+- multi-action - default no action, set the corresponding behavior with
+ following format:
+- duration - activate when pulling gpio over this value (unit: millisecond)
+- action - chassis power control behavior
+
+```json
+{
+ "gpio_definitions": [
+ {
+ "name": "POWER_BUTTON0",
+ "pin": "I6",
+ "direction": "both",
+ "multi-action": [
+ {
+ "duration": 4000,
+ "action": "chassis cycle"
+ }
+ ]
+ },
+ {
+ "name": "POWER_BUTTON1",
+ "pin": "V0",
+ "direction": "both",
+ "multi-action": [
+ {
+ "duration": 0,
+ "action": "chassis on"
+ },
+ {
+ "duration": 4000,
+ "action": "chassis cycle"
+ },
+ {
+ "duration": 8000,
+ "action": "chassis off"
+ }
+ ]
+ }
+ ]
+}
+```
diff --git a/inc/button_factory.hpp b/inc/button_factory.hpp
index 83cf530..2b7c9b9 100644
--- a/inc/button_factory.hpp
+++ b/inc/button_factory.hpp
@@ -35,13 +35,27 @@
template <typename T>
void addToRegistry()
{
- buttonIfaceRegistry[std::string(T::getFormFactorName())] =
+ buttonIfaceRegistry[T::getFormFactorName()] =
[](sdbusplus::bus_t& bus, EventPtr& event,
ButtonConfig& buttonCfg) {
- return std::make_unique<T>(bus, T::getDbusObjectPath(), event,
- buttonCfg);
+ return std::make_unique<T>(bus, T::getDbusObjectPath().c_str(),
+ event, buttonCfg);
};
}
+
+ template <typename T>
+ void addToRegistry(size_t index)
+ {
+ auto indexStr = std::to_string(index);
+ buttonIfaceRegistry[T::getFormFactorName() + indexStr] =
+ [=](sdbusplus::bus_t& bus, EventPtr& event,
+ ButtonConfig& buttonCfg) {
+ return std::make_unique<T>(
+ bus, (T::getDbusObjectPath() + indexStr).c_str(), event,
+ buttonCfg);
+ };
+ }
+
/**
* @brief this method returns the button interface object
* corresponding to the button formfactor name provided
@@ -76,4 +90,15 @@
// register the class factory function
ButtonFactory::instance().addToRegistry<T>();
}
+
+ explicit ButtonIFRegister(size_t count)
+ {
+ // register the class factory function
+ // The index, 'countIter', starts at 1 and increments,
+ // representing slot_1 through slot_N.
+ for (size_t countIter = 1; countIter <= count; countIter++)
+ {
+ ButtonFactory::instance().addToRegistry<T>(countIter);
+ }
+ }
};
diff --git a/inc/button_handler.hpp b/inc/button_handler.hpp
index 1332a0f..ff8850b 100644
--- a/inc/button_handler.hpp
+++ b/inc/button_handler.hpp
@@ -1,10 +1,16 @@
#pragma once
+#include "config.hpp"
#include "power_button_profile.hpp"
#include <sdbusplus/bus.hpp>
#include <sdbusplus/bus/match.hpp>
+#include <algorithm>
+#include <numeric>
+#include <sstream>
+#include <string>
+
namespace phosphor
{
namespace button
@@ -16,6 +22,19 @@
powerReleased,
resetReleased
};
+
+enum class PwrCtl
+{
+ chassisOn,
+ chassisOff,
+ chassisCycle,
+};
+
+inline static size_t numberOfChassis()
+{
+ return instances.size();
+}
+
/**
* @class Handler
*
@@ -129,6 +148,7 @@
* @return void
*/
void handlePowerEvent(PowerEvent powerEventType,
+ const std::string& objectPath,
std::chrono::microseconds duration);
/**
@@ -147,6 +167,12 @@
std::unique_ptr<sdbusplus::bus::match_t> powerButtonLongPressed;
/**
+ * @brief Matches on the multi power button released signal
+ */
+ std::vector<std::unique_ptr<sdbusplus::bus::match_t>>
+ multiPowerButtonReleased;
+
+ /**
* @brief Matches on the ID button released signal
*/
std::unique_ptr<sdbusplus::bus::match_t> idButtonReleased;
@@ -165,6 +191,17 @@
* @brief The custom power handler profile object.
*/
std::unique_ptr<PowerButtonProfile> powerButtonProfile;
+
+ /**
+ * @brief Flag to indicate multi power button mode(false) or host select
+ * button mode(true)
+ */
+ bool hostSelectButtonMode = false;
+
+ /**
+ * @brief Flag to indicate if the button supports multi action
+ */
+ bool isButtonMultiActionSupport = true;
};
} // namespace button
diff --git a/inc/debugHostSelector_button.hpp b/inc/debugHostSelector_button.hpp
index bbae539..17431d4 100644
--- a/inc/debugHostSelector_button.hpp
+++ b/inc/debugHostSelector_button.hpp
@@ -13,8 +13,7 @@
#include <phosphor-logging/elog-errors.hpp>
#include <phosphor-logging/lg2.hpp>
-static constexpr std::string_view DEBUG_SELECTOR_BUTTON =
- "DEBUG_SELECTOR_BUTTON";
+static constexpr auto DEBUG_SELECTOR_BUTTON = "DEBUG_SELECTOR_BUTTON";
class DebugHostSelector final :
public sdbusplus::server::object_t<
@@ -44,12 +43,12 @@
void simLongPress() override;
void handleEvent(sd_event_source* es, int fd, uint32_t revents) override;
- static constexpr std::string_view getFormFactorName()
+ static constexpr std::string getFormFactorName()
{
return DEBUG_SELECTOR_BUTTON;
}
- static const char* getDbusObjectPath()
+ static constexpr std::string getDbusObjectPath()
{
return DBG_HS_DBUS_OBJECT_NAME;
}
diff --git a/inc/hostSelector_switch.hpp b/inc/hostSelector_switch.hpp
index 70e31e0..04f80bb 100644
--- a/inc/hostSelector_switch.hpp
+++ b/inc/hostSelector_switch.hpp
@@ -15,7 +15,7 @@
#include <fstream>
#include <iostream>
-static constexpr std::string_view HOST_SELECTOR = "HOST_SELECTOR";
+static constexpr auto HOST_SELECTOR = "HOST_SELECTOR";
static constexpr auto INVALID_INDEX = std::numeric_limits<size_t>::max();
@@ -51,12 +51,12 @@
deInit();
}
- static constexpr std::string_view getFormFactorName()
+ static constexpr std::string getFormFactorName()
{
return HOST_SELECTOR;
}
- static const char* getDbusObjectPath()
+ static constexpr std::string getDbusObjectPath()
{
return HS_DBUS_OBJECT_NAME;
}
diff --git a/inc/host_then_chassis_poweroff.hpp b/inc/host_then_chassis_poweroff.hpp
index 96d7aee..cf29a4a 100644
--- a/inc/host_then_chassis_poweroff.hpp
+++ b/inc/host_then_chassis_poweroff.hpp
@@ -61,7 +61,7 @@
* @brief Returns the name that matches the value in
* meson_options.txt.
*/
- static constexpr std::string_view getName()
+ static constexpr std::string getName()
{
return "host_then_chassis_poweroff";
}
diff --git a/inc/id_button.hpp b/inc/id_button.hpp
index 5239d77..7329bae 100644
--- a/inc/id_button.hpp
+++ b/inc/id_button.hpp
@@ -27,7 +27,7 @@
#include <phosphor-logging/elog-errors.hpp>
-static constexpr std::string_view ID_BUTTON = "ID_BTN";
+static constexpr auto ID_BUTTON = "ID_BTN";
class IDButton :
public sdbusplus::server::object_t<
@@ -52,11 +52,11 @@
void simPress() override;
- static constexpr std::string_view getFormFactorName()
+ static constexpr std::string getFormFactorName()
{
return ID_BUTTON;
}
- static constexpr const char* getDbusObjectPath()
+ static constexpr std::string getDbusObjectPath()
{
return ID_DBUS_OBJECT_NAME;
}
diff --git a/inc/power_button.hpp b/inc/power_button.hpp
index 2cf28e4..f1e8ca2 100644
--- a/inc/power_button.hpp
+++ b/inc/power_button.hpp
@@ -29,7 +29,7 @@
#include <chrono>
-static constexpr std::string_view POWER_BUTTON = "POWER_BUTTON";
+static constexpr auto POWER_BUTTON = "POWER_BUTTON";
class PowerButton :
public sdbusplus::server::object_t<
@@ -55,11 +55,11 @@
void simPress() override;
void simLongPress() override;
- static constexpr std::string_view getFormFactorName()
+ static constexpr std::string getFormFactorName()
{
return POWER_BUTTON;
}
- static constexpr const char* getDbusObjectPath()
+ static constexpr std::string getDbusObjectPath()
{
return POWER_DBUS_OBJECT_NAME;
}
diff --git a/inc/power_button_profile_factory.hpp b/inc/power_button_profile_factory.hpp
index 3f05c77..ea8ca2e 100644
--- a/inc/power_button_profile_factory.hpp
+++ b/inc/power_button_profile_factory.hpp
@@ -33,7 +33,7 @@
template <typename T>
void addToRegistry()
{
- profileRegistry[std::string(T::getName())] = [](sdbusplus::bus_t& bus) {
+ profileRegistry[T::getName()] = [](sdbusplus::bus_t& bus) {
return std::make_unique<T>(bus);
};
}
diff --git a/inc/reset_button.hpp b/inc/reset_button.hpp
index 79a6096..f370ed4 100644
--- a/inc/reset_button.hpp
+++ b/inc/reset_button.hpp
@@ -27,7 +27,7 @@
#include <phosphor-logging/elog-errors.hpp>
-static constexpr std::string_view RESET_BUTTON = "RESET_BUTTON";
+static constexpr auto RESET_BUTTON = "RESET_BUTTON";
class ResetButton :
public sdbusplus::server::object_t<
@@ -52,12 +52,12 @@
void simPress() override;
- static constexpr std::string_view getFormFactorName()
+ static constexpr std::string getFormFactorName()
{
return RESET_BUTTON;
}
- static constexpr const char* getDbusObjectPath()
+ static constexpr std::string getDbusObjectPath()
{
return RESET_DBUS_OBJECT_NAME;
}
diff --git a/inc/serial_uart_mux.hpp b/inc/serial_uart_mux.hpp
index a280cb4..1e954da 100644
--- a/inc/serial_uart_mux.hpp
+++ b/inc/serial_uart_mux.hpp
@@ -14,9 +14,8 @@
#include <phosphor-logging/elog-errors.hpp>
#include <sdbusplus/bus.hpp>
#include <sdbusplus/bus/match.hpp>
-static constexpr std::string_view DEBUG_CARD_PRESENT_GPIO =
- "debug_card_present";
-static constexpr std::string_view SERIAL_CONSOLE_SWITCH = "SERIAL_UART_MUX";
+static constexpr auto DEBUG_CARD_PRESENT_GPIO = "debug_card_present";
+static constexpr auto SERIAL_CONSOLE_SWITCH = "SERIAL_UART_MUX";
class SerialUartMux final : public ButtonIface
{
@@ -58,11 +57,11 @@
deInit();
}
void init() override;
- static const std::string_view getFormFactorName()
+ static constexpr std::string getFormFactorName()
{
return SERIAL_CONSOLE_SWITCH;
}
- static const char* getDbusObjectPath()
+ static constexpr std::string getDbusObjectPath()
{
return "NO_DBUS_OBJECT";
}
diff --git a/meson.build b/meson.build
index 25d803a..1dee2ab 100644
--- a/meson.build
+++ b/meson.build
@@ -9,7 +9,11 @@
]
)
+host_instances = get_option('host-instances')
+formatted_instances = host_instances.replace(' ', ', ')
+
conf_data = configuration_data()
+conf_data.set('INSTANCES',formatted_instances)
conf_data.set_quoted('ID_LED_GROUP', get_option('id-led-group'))
conf_data.set_quoted('POWER_BUTTON_PROFILE', get_option('power-button-profile'))
conf_data.set('LONG_PRESS_TIME_MS', get_option('long-press-time-ms'))
diff --git a/meson.options b/meson.options
index c41ed4b..0f394aa 100644
--- a/meson.options
+++ b/meson.options
@@ -33,3 +33,10 @@
value : 'disabled',
description : 'Enable warm reboot on the reset button'
)
+
+option(
+ 'host-instances',
+ type: 'string',
+ value: '0',
+ description: 'Host Instances that can be defined by project, for example: 1 2 3 4'
+)
diff --git a/meson_config.hpp.in b/meson_config.hpp.in
index 6610a44..ffc82a6 100644
--- a/meson_config.hpp.in
+++ b/meson_config.hpp.in
@@ -1,5 +1,6 @@
#pragma once
+#include <array>
#include <chrono>
constexpr inline auto POWER_DBUS_OBJECT_NAME =
@@ -23,9 +24,12 @@
"/xyz/openbmc_project/state/host";
constexpr inline auto GPIO_BASE_LABEL_NAME = "1e780000.gpio";
+constexpr inline auto gpioDefFile = "/etc/default/obmc/gpio/gpio_defs.json";
#define LOOKUP_GPIO_BASE @LOOKUP_GPIO_BASE@
constexpr inline auto POWER_BUTTON_PROFILE = @POWER_BUTTON_PROFILE@;
constexpr inline auto ID_LED_GROUP = @ID_LED_GROUP@;
constexpr inline const auto LONG_PRESS_TIME_MS =
std::chrono::milliseconds(@LONG_PRESS_TIME_MS@);
+
+constexpr inline static auto instances = std::to_array({ @INSTANCES@ });
diff --git a/src/button_handler.cpp b/src/button_handler.cpp
index aec5367..99ce739 100644
--- a/src/button_handler.cpp
+++ b/src/button_handler.cpp
@@ -1,11 +1,18 @@
#include "button_handler.hpp"
#include "config.hpp"
+#include "gpio.hpp"
#include "power_button_profile_factory.hpp"
#include <phosphor-logging/lg2.hpp>
+#include <xyz/openbmc_project/Chassis/Buttons/Power/server.hpp>
#include <xyz/openbmc_project/State/Chassis/server.hpp>
#include <xyz/openbmc_project/State/Host/server.hpp>
+
+#include <fstream>
+#include <iostream>
+#include <string>
+
namespace phosphor
{
namespace button
@@ -13,6 +20,12 @@
namespace sdbusRule = sdbusplus::bus::match::rules;
using namespace sdbusplus::xyz::openbmc_project::State::server;
+using namespace sdbusplus::xyz::openbmc_project::Chassis::Buttons::server;
+
+const std::map<std::string, Chassis::Transition> chassisPwrCtls = {
+ {"chassis-on", Chassis::Transition::On},
+ {"chassis-off", Chassis::Transition::Off},
+ {"chassis-cycle", Chassis::Transition::PowerCycle}};
constexpr auto chassisIface = "xyz.openbmc_project.State.Chassis";
constexpr auto hostIface = "xyz.openbmc_project.State.Host";
@@ -33,8 +46,58 @@
constexpr auto mapperService = "xyz.openbmc_project.ObjectMapper";
constexpr auto BMC_POSITION = 0;
+std::vector<std::map<uint16_t, Chassis::Transition>> multiPwrBtnActConf;
+
Handler::Handler(sdbusplus::bus_t& bus) : bus(bus)
{
+ /* So far, there are two modes for multi-host power control
+ - host select button mode, e.g.: Yosemite V2
+ only one power button with host select switch,
+ which's interface for handling target host,
+ in the case, hostSelectButtonMode = true
+ - multi power button mode, e.g.: Greatlakes
+ each slot/sled has its own power button,
+ in the case, hostSelectButtonMode = false */
+ hostSelectButtonMode =
+ !getService(HS_DBUS_OBJECT_NAME, hostSelectorIface).empty();
+ size_t powerButtonCount = 1;
+ if (!hostSelectButtonMode)
+ {
+ powerButtonCount = phosphor::button::numberOfChassis();
+ }
+
+ std::ifstream gpios{gpioDefFile};
+ auto configDefJson = nlohmann::json::parse(gpios, nullptr, true);
+ nlohmann::json gpioDefs = configDefJson["gpio_definitions"];
+
+ for (const auto& gpioConfig : gpioDefs)
+ {
+ if (gpioConfig.contains("multi-action"))
+ {
+ std::map<uint16_t, Chassis::Transition> mapEntry;
+ const auto& multiActCfg = gpioConfig["multi-action"];
+ for (const auto& ActCfg : multiActCfg)
+ {
+ auto chassisPwrCtl = chassisPwrCtls.find(ActCfg["action"]);
+ if (chassisPwrCtl != chassisPwrCtls.end())
+ {
+ auto duration = ActCfg["duration"].get<uint16_t>();
+ mapEntry[duration] = chassisPwrCtl->second;
+ }
+ else
+ {
+ lg2::error("unknown power button action");
+ }
+ }
+ multiPwrBtnActConf.emplace_back(mapEntry);
+ }
+ else
+ {
+ isButtonMultiActionSupport = false;
+ break;
+ }
+ }
+
try
{
if (!getService(POWER_DBUS_OBJECT_NAME, powerButtonIface).empty())
@@ -56,10 +119,34 @@
std::placeholders::_1));
}
}
+
+ if (!hostSelectButtonMode && isButtonMultiActionSupport)
+ {
+ lg2::info("Starting multi power button handler");
+ // The index, 'countIter', starts at 1 and increments,
+ // representing slot_1 through slot_N.
+ for (size_t countIter = 1; countIter <= powerButtonCount;
+ countIter++)
+ {
+ std::unique_ptr<sdbusplus::bus::match_t>
+ multiPowerReleaseMatch =
+ std::make_unique<sdbusplus::bus::match_t>(
+ bus,
+ sdbusRule::type::signal() +
+ sdbusRule::member("Released") +
+ sdbusRule::path(POWER_DBUS_OBJECT_NAME +
+ std::to_string(countIter)) +
+ sdbusRule::interface(powerButtonIface),
+ std::bind(std::mem_fn(&Handler::powerReleased),
+ this, std::placeholders::_1));
+ multiPowerButtonReleased.emplace_back(
+ std::move(multiPowerReleaseMatch));
+ }
+ }
}
catch (const sdbusplus::exception_t& e)
{
- // The button wasn't implemented
+ lg2::error("Error creating power button handler: {ERROR}", "ERROR", e);
}
try
@@ -121,8 +208,14 @@
}
bool Handler::isMultiHost()
{
- // return true in case host selector object is available
- return (!getService(HS_DBUS_OBJECT_NAME, hostSelectorIface).empty());
+ if (numberOfChassis() != 1)
+ {
+ return true;
+ }
+ else
+ {
+ return (hostSelectButtonMode);
+ }
}
std::string Handler::getService(const std::string& path,
const std::string& interface) const
@@ -188,6 +281,7 @@
}
void Handler::handlePowerEvent(PowerEvent powerEventType,
+ const std::string& objectPath,
std::chrono::microseconds duration)
{
std::string objPathName;
@@ -196,30 +290,45 @@
std::variant<Host::Transition, Chassis::Transition> transition;
size_t hostNumber = 0;
+ std::string hostNumStr =
+ objectPath.substr(std::string(POWER_DBUS_OBJECT_NAME).length());
auto isMultiHostSystem = isMultiHost();
- if (isMultiHostSystem)
+
+ if (hostSelectButtonMode)
{
hostNumber = getHostSelectorValue();
lg2::info("Multi-host system detected : {POSITION}", "POSITION",
hostNumber);
- }
- std::string hostNumStr = std::to_string(hostNumber);
+ hostNumStr = std::to_string(hostNumber);
- // ignore power and reset button events if BMC is selected.
- if (isMultiHostSystem && (hostNumber == BMC_POSITION) &&
- (powerEventType != PowerEvent::powerReleased) &&
- (duration <= LONG_PRESS_TIME_MS))
- {
- lg2::info(
- "handlePowerEvent : BMC selected on multi-host system. ignoring power and reset button events...");
- return;
+ // ignore power and reset button events if BMC is selected.
+ if (isMultiHostSystem && (hostNumber == BMC_POSITION) &&
+ (powerEventType != PowerEvent::powerReleased) &&
+ (duration <= LONG_PRESS_TIME_MS))
+ {
+ lg2::info(
+ "handlePowerEvent : BMC selected on multi-host system. ignoring power and reset button events...");
+ return;
+ }
}
switch (powerEventType)
{
case PowerEvent::powerReleased:
{
+ for (const auto& iter : multiPwrBtnActConf[stoi(hostNumStr) - 1])
+ {
+ if (duration > std::chrono::milliseconds(iter.first))
+ {
+ dbusIfaceName = chassisIface;
+ transitionName = "RequestedPowerTransition";
+ objPathName = CHASSIS_STATE_OBJECT_NAME + hostNumStr;
+ transition = iter.second;
+ }
+ }
+ break;
+
if (duration <= LONG_PRESS_TIME_MS)
{
objPathName = HOST_STATE_OBJECT_NAME + hostNumStr;
@@ -233,7 +342,6 @@
transition = Host::Transition::Off;
}
lg2::info("handlePowerEvent : Handle power button press ");
-
break;
}
else
@@ -302,6 +410,7 @@
method.append(dbusIfaceName, transitionName, transition);
bus.call(method);
}
+
void Handler::powerReleased(sdbusplus::message_t& msg)
{
try
@@ -309,7 +418,7 @@
uint64_t time;
msg.read(time);
- handlePowerEvent(PowerEvent::powerReleased,
+ handlePowerEvent(PowerEvent::powerReleased, msg.get_path(),
std::chrono::microseconds(time));
}
catch (const sdbusplus::exception_t& e)
@@ -319,12 +428,12 @@
}
}
-void Handler::resetReleased(sdbusplus::message_t& /* msg */)
+void Handler::resetReleased(sdbusplus::message_t& msg)
{
try
{
// No need to calculate duration, set to 0.
- handlePowerEvent(PowerEvent::resetReleased,
+ handlePowerEvent(PowerEvent::resetReleased, msg.get_path(),
std::chrono::microseconds(0));
}
catch (const sdbusplus::exception_t& e)
diff --git a/src/main.cpp b/src/main.cpp
index fc02acb..06e1c80 100644
--- a/src/main.cpp
+++ b/src/main.cpp
@@ -22,7 +22,6 @@
#include <phosphor-logging/lg2.hpp>
#include <fstream>
-static constexpr auto gpioDefFile = "/etc/default/obmc/gpio/gpio_defs.json";
int main(void)
{
diff --git a/src/power_button.cpp b/src/power_button.cpp
index 80334a8..879c659 100644
--- a/src/power_button.cpp
+++ b/src/power_button.cpp
@@ -16,8 +16,11 @@
#include "power_button.hpp"
+#include "button_handler.hpp"
+
// add the button iface class to registry
-static ButtonIFRegister<PowerButton> buttonRegister;
+static ButtonIFRegister<PowerButton>
+ multiButtonRegister(phosphor::button::numberOfChassis());
void PowerButton::simPress()
{
diff --git a/src/serial_uart_mux.cpp b/src/serial_uart_mux.cpp
index c36e82a..efae3f4 100644
--- a/src/serial_uart_mux.cpp
+++ b/src/serial_uart_mux.cpp
@@ -13,7 +13,7 @@
using HostSelectorClientObj =
sdbusplus::common::xyz::openbmc_project::chassis::buttons::HostSelector;
-constexpr std::string_view SERIAL_UART_RX_GPIO = "serial_uart_rx";
+static constexpr auto SERIAL_UART_RX_GPIO = "serial_uart_rx";
void SerialUartMux::init()
{
try