Read GPIO config from JSON instead of D-Bus
Read the GPIO information for the buttons from
/etc/default/obmc/gpio/gpio_defs.json instead of
from D-Bus, as the D-Bus location for these were a
legacy thing and are going away.
This requires that the GPIO pin name, like 'J1', is converted
to the GPIO number as used in /sys/class/gpio.
The libgpioplus utility will handle most of this for us, with the
exception of needing to look up the base offset in the
/sys/class/gpiochip*/base file, where the specific gpiochip*
directory is determined as following:
1) Find the 'label' file in a /sys/class/gpiochipX/ directory
and check for it to match GPIO_BASE_LABEL_NAME, which is
configurable at build time and defaults to the ASPEED BMC
value.
2) Use the value in the 'base' file in that directory as the
base offset.
If a platform doesn't need to use this base offset, it can be
turned off with the LOOKUP_GPIO_BASE dial.
Change-Id: I88d29529ffa8a6d13132ac9059b47f86344837ea
Signed-off-by: Matt Spinler <spinler@us.ibm.com>
diff --git a/CMakeLists.txt b/CMakeLists.txt
index 723b300..5115501 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -13,10 +13,12 @@
set(POWER_DBUS_OBJECT_NAME "xyz/openbmc_project/Chassis/Buttons/Power")
set(RESET_DBUS_OBJECT_NAME "xyz/openbmc_project/Chassis/Buttons/Reset")
set(ID_DBUS_OBJECT_NAME "xyz/openbmc_project/Chassis/Buttons/ID")
+set(GPIO_BASE_LABEL_NAME "1e780000.gpio")
add_definitions(-DPOWER_DBUS_OBJECT_NAME="/${POWER_DBUS_OBJECT_NAME}0")
add_definitions(-DRESET_DBUS_OBJECT_NAME="/${RESET_DBUS_OBJECT_NAME}0")
add_definitions(-DID_DBUS_OBJECT_NAME="/${ID_DBUS_OBJECT_NAME}0")
+add_definitions(-DGPIO_BASE_LABEL_NAME="${GPIO_BASE_LABEL_NAME}")
set(SRC_FILES src/power_button.cpp
src/reset_button.cpp
src/id_button.cpp
@@ -24,6 +26,13 @@
src/gpio.cpp
)
+option (
+ LOOKUP_GPIO_BASE
+ "Look up the GPIO base value in /sys/class/gpio. Otherwise use a base of 0." ON
+)
+
+configure_file (settings.hpp.in ${CMAKE_BINARY_DIR}/inc/settings.hpp)
+
# import sdbusplus
find_package(PkgConfig REQUIRED)
pkg_check_modules(SDBUSPLUSPLUS sdbusplus REQUIRED)
diff --git a/settings.hpp.in b/settings.hpp.in
new file mode 100644
index 0000000..e89f312
--- /dev/null
+++ b/settings.hpp.in
@@ -0,0 +1,3 @@
+#pragma once
+
+#cmakedefine LOOKUP_GPIO_BASE
diff --git a/src/gpio.cpp b/src/gpio.cpp
index 27fc74d..a398d04 100644
--- a/src/gpio.cpp
+++ b/src/gpio.cpp
@@ -16,23 +16,24 @@
#include "gpio.hpp"
+#include "settings.hpp"
+
#include <fcntl.h>
#include <unistd.h>
#include <experimental/filesystem>
#include <fstream>
+#include <gpioplus/utility/aspeed.hpp>
#include <nlohmann/json.hpp>
-#include <phosphor-logging/elog-errors.hpp>
+#include <optional>
#include <phosphor-logging/log.hpp>
-#include <xyz/openbmc_project/Common/error.hpp>
+#include <tuple>
-const static constexpr char* SYSMGR_SERVICE = "org.openbmc.managers.System";
-const static constexpr char* SYSMGR_OBJ_PATH = "/org/openbmc/managers/System";
-const static constexpr char* SYSMGR_INTERFACE = "org.openbmc.managers.System";
-
+const std::string gpioDev = "/sys/class/gpio";
static constexpr auto gpioDefs = "/etc/default/obmc/gpio/gpio_defs.json";
using namespace phosphor::logging;
+namespace fs = std::experimental::filesystem;
void closeGpio(int fd)
{
@@ -42,6 +43,49 @@
}
}
+uint32_t getGpioBase()
+{
+ // Look for a /sys/class/gpio/gpiochip*/label file
+ // with a value of GPIO_BASE_LABEL_NAME. Then read
+ // the base value from the 'base' file in that directory.
+#ifdef LOOKUP_GPIO_BASE
+ for (auto& f : fs::directory_iterator(gpioDev))
+ {
+ std::string path{f.path()};
+ if (path.find("gpiochip") == std::string::npos)
+ {
+ continue;
+ }
+
+ std::ifstream labelStream{path + "/label"};
+ std::string label;
+ labelStream >> label;
+
+ if (label == GPIO_BASE_LABEL_NAME)
+ {
+ uint32_t base;
+ std::ifstream baseStream{path + "/base"};
+ baseStream >> base;
+ return base;
+ }
+ }
+
+ log<level::ERR>("Could not find GPIO base");
+ throw std::runtime_error("Could not find GPIO base!");
+#else
+ return 0;
+#endif
+}
+
+uint32_t getGpioNum(const std::string& gpioPin)
+{
+ // gpioplus promises that they will figure out how to easily
+ // support multiple BMC vendors when the time comes.
+ auto offset = gpioplus::utility::aspeed::nameToOffset(gpioPin);
+
+ return getGpioBase() + offset;
+}
+
bool gpioDefined(const std::string& gpioName)
{
try
@@ -68,45 +112,66 @@
return false;
}
+std::optional<std::tuple<int, std::string>>
+ getGpioConfig(const std::string& gpioName)
+{
+
+ try
+ {
+ std::ifstream gpios{gpioDefs};
+ auto json = nlohmann::json::parse(gpios, nullptr, true);
+ auto defs = json["gpio_definitions"];
+
+ auto gpio =
+ std::find_if(defs.begin(), defs.end(), [&gpioName](const auto g) {
+ return gpioName == g["name"];
+ });
+
+ if (gpio != defs.end())
+ {
+ return std::make_tuple(getGpioNum((*gpio)["pin"]),
+ (*gpio)["direction"]);
+ }
+ else
+ {
+ log<level::ERR>("Unable to find GPIO in the definitions",
+ entry("GPIO_NAME=%s", gpioName.c_str()));
+ }
+ }
+ catch (std::exception& e)
+ {
+ log<level::ERR>("Error parsing GPIO JSON", entry("ERROR=%s", e.what()),
+ entry("GPIO_NAME=%s", gpioName.c_str()));
+ }
+ return {};
+}
+
int configGpio(const char* gpioName, int* fd, sdbusplus::bus::bus& bus)
{
- auto method = bus.new_method_call(SYSMGR_SERVICE, SYSMGR_OBJ_PATH,
- SYSMGR_INTERFACE, "gpioInit");
-
- method.append(gpioName);
-
- auto result = bus.call(method);
-
- if (result.is_method_error())
+ auto config = getGpioConfig(gpioName);
+ if (!config)
{
- log<level::ERR>("bus call error!");
return -1;
}
- int32_t gpioNum;
- std::string gpioDev;
- std::string gpioDirection;
+ auto [gpioNum, gpioDirection] = *config;
- result.read(gpioDev, gpioNum, gpioDirection);
-
- std::string devPath;
+ std::string devPath{gpioDev};
std::fstream stream;
stream.exceptions(std::ifstream::failbit | std::ifstream::badbit);
- devPath.clear();
- devPath = gpioDev + "/gpio" + std::to_string(gpioNum) + "/value";
+ devPath += "/gpio" + std::to_string(gpioNum) + "/value";
- std::experimental::filesystem::path fullPath(devPath);
+ fs::path fullPath(devPath);
- if (std::experimental::filesystem::exists(fullPath))
+ if (fs::exists(fullPath))
{
log<level::INFO>("GPIO exported", entry("PATH=%s", devPath.c_str()));
}
else
{
- devPath.clear();
devPath = gpioDev + "/export";
stream.open(devPath, std::fstream::out);
@@ -127,7 +192,6 @@
if (gpioDirection == "out")
{
- devPath.clear();
devPath = gpioDev + "/gpio" + std::to_string(gpioNum) + "/value";
uint32_t currentValue;
@@ -166,7 +230,6 @@
}
else if (gpioDirection == "in")
{
- devPath.clear();
devPath = gpioDev + "/gpio" + std::to_string(gpioNum) + "/direction";
stream.open(devPath, std::fstream::out);
@@ -187,7 +250,6 @@
// For gpio configured as ‘both’, it is an interrupt pin and trigged on
// both rising and falling signals
- devPath.clear();
devPath = gpioDev + "/gpio" + std::to_string(gpioNum) + "/edge";
stream.open(devPath, std::fstream::out);
@@ -204,7 +266,6 @@
}
}
- devPath.clear();
devPath = gpioDev + "/gpio" + std::to_string(gpioNum) + "/value";
*fd = ::open(devPath.c_str(), O_RDWR | O_NONBLOCK);