#include "gpio_presence.hpp" #include "xyz/openbmc_project/Common/error.hpp" #include <fcntl.h> #include <libevdev/libevdev.h> #include <phosphor-logging/elog-errors.hpp> #include <phosphor-logging/elog.hpp> #include <phosphor-logging/lg2.hpp> #include <fstream> namespace phosphor { namespace gpio { namespace presence { using namespace phosphor::logging; using namespace sdbusplus::xyz::openbmc_project::Common::Error; constexpr auto INVENTORY_PATH = "/xyz/openbmc_project/inventory"; constexpr auto INVENTORY_INTF = "xyz.openbmc_project.Inventory.Manager"; constexpr auto MAPPER_BUSNAME = "xyz.openbmc_project.ObjectMapper"; constexpr auto MAPPER_PATH = "/xyz/openbmc_project/object_mapper"; constexpr auto MAPPER_INTERFACE = "xyz.openbmc_project.ObjectMapper"; std::string getService(const std::string& path, const std::string& interface, sdbusplus::bus_t& bus) { auto mapperCall = bus.new_method_call(MAPPER_BUSNAME, MAPPER_PATH, MAPPER_INTERFACE, "GetObject"); mapperCall.append(path); mapperCall.append(std::vector<std::string>({interface})); std::map<std::string, std::vector<std::string>> mapperResponse; try { auto mapperResponseMsg = bus.call(mapperCall); mapperResponseMsg.read(mapperResponse); } catch (const sdbusplus::exception_t& e) { lg2::error( "Error in mapper call to get service name, path: {PATH}, interface: {INTERFACE}, error: {ERROR}", "PATH", path, "INTERFACE", interface, "ERROR", e); elog<InternalFailure>(); } return mapperResponse.begin()->first; } void Presence::determinePresence() { auto present = false; auto value = static_cast<int>(0); auto fetch_rc = libevdev_fetch_event_value(devicePtr.get(), EV_KEY, key, &value); if (0 == fetch_rc) { lg2::error("Device does not support event type, key: {KEYCODE}", "KEYCODE", key); elog<InternalFailure>(); return; } if (value > 0) { present = true; } updateInventory(present); } // Callback handler when there is an activity on the FD int Presence::processEvents(sd_event_source*, int, uint32_t, void* userData) { auto presence = static_cast<Presence*>(userData); presence->analyzeEvent(); return 0; } // Analyzes the GPIO event void Presence::analyzeEvent() { // Data returned struct input_event ev {}; int rc = 0; // While testing, observed that not having a loop here was leading // into events being missed. while (rc >= 0) { // Wait until no more events are available on the device. rc = libevdev_next_event(devicePtr.get(), LIBEVDEV_READ_FLAG_NORMAL, &ev); if (rc < 0) { // There was an error waiting for events, mostly that there are no // events to be read.. So continue waiting... return; } if (rc == LIBEVDEV_READ_STATUS_SUCCESS) { if (ev.type == EV_SYN && ev.code == SYN_REPORT) { continue; } else if (ev.code == key) { auto present = false; if (ev.value > 0) { present = true; std::this_thread::sleep_for( std::chrono::milliseconds(delay)); bindOrUnbindDrivers(present); updateInventory(present); } else { updateInventory(present); bindOrUnbindDrivers(present); } } } } return; } Presence::ObjectMap Presence::getObjectMap(bool present) { ObjectMap invObj; InterfaceMap invIntf; PropertyMap invProp; invProp.emplace("Present", present); invProp.emplace("PrettyName", name); invIntf.emplace("xyz.openbmc_project.Inventory.Item", std::move(invProp)); // Add any extra interfaces we want to associate with the inventory item for (auto& iface : ifaces) { invIntf.emplace(iface, PropertyMap()); } invObj.emplace(std::move(inventory), std::move(invIntf)); return invObj; } void Presence::updateInventory(bool present) { ObjectMap invObj = getObjectMap(present); lg2::info( "Updating inventory present property value to {PRESENT}, path: {PATH}", "PRESENT", present, "PATH", inventory); auto invService = getService(INVENTORY_PATH, INVENTORY_INTF, bus); // Update inventory auto invMsg = bus.new_method_call(invService.c_str(), INVENTORY_PATH, INVENTORY_INTF, "Notify"); invMsg.append(std::move(invObj)); try { auto invMgrResponseMsg = bus.call(invMsg); } catch (const sdbusplus::exception_t& e) { lg2::error( "Error in inventory manager call to update inventory: {ERROR}", "ERROR", e); elog<InternalFailure>(); } } void Presence::bindOrUnbindDrivers(bool present) { auto action = (present) ? "bind" : "unbind"; for (auto& driver : drivers) { auto path = std::get<pathField>(driver) / action; auto device = std::get<deviceField>(driver); if (present) { lg2::info("Binding a {DEVICE} driver: {PATH}", "DEVICE", device, "PATH", path); } else { lg2::info("Unbinding a {DEVICE} driver: {PATH}", "DEVICE", device, "PATH", path); } std::ofstream file; file.exceptions(std::ofstream::failbit | std::ofstream::badbit | std::ofstream::eofbit); try { file.open(path); file << device; file.close(); } catch (const std::exception& e) { lg2::error( "Failed binding or unbinding a {DEVICE} after a card was removed or added, path: {PATH}, error: {ERROR}", "DEVICE", device, "PATH", path, "ERROR", e); } } } } // namespace presence } // namespace gpio } // namespace phosphor