1 /** 2 * Copyright © 2017 IBM Corporation 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 #include "gpio.hpp" 17 18 #include "logging.hpp" 19 #include "rpolicy.hpp" 20 #include "sdbusplus.hpp" 21 22 #include <phosphor-logging/elog-errors.hpp> 23 #include <phosphor-logging/elog.hpp> 24 #include <sdeventplus/event.hpp> 25 #include <xyz/openbmc_project/Common/Callout/error.hpp> 26 #include <xyz/openbmc_project/Logging/Entry/server.hpp> 27 28 #include <functional> 29 #include <tuple> 30 31 namespace phosphor 32 { 33 namespace fan 34 { 35 namespace presence 36 { 37 38 const auto loggingService = "xyz.openbmc_project.Logging"; 39 const auto loggingPath = "/xyz/openbmc_project/logging"; 40 const auto loggingCreateIface = "xyz.openbmc_project.Logging.Create"; 41 42 Gpio::Gpio(const std::string& physDevice, const std::string& device, 43 unsigned int physPin) : 44 currentState(false), 45 evdevfd(open(device.c_str(), O_RDONLY | O_NONBLOCK)), 46 evdev(evdevpp::evdev::newFromFD(evdevfd())), phys(physDevice), pin(physPin) 47 {} 48 49 bool Gpio::start() 50 { 51 source.emplace(sdeventplus::Event::get_default(), evdevfd(), EPOLLIN, 52 std::bind(&Gpio::ioCallback, this)); 53 currentState = present(); 54 return currentState; 55 } 56 57 void Gpio::stop() 58 { 59 source.reset(); 60 } 61 62 bool Gpio::present() 63 { 64 return evdev.fetch(EV_KEY, pin) != 0; 65 } 66 67 void Gpio::fail() 68 { 69 using namespace sdbusplus::xyz::openbmc_project::Common::Callout::Error; 70 using namespace phosphor::logging; 71 using namespace xyz::openbmc_project::Common::Callout; 72 73 report<sdbusplus::xyz::openbmc_project::Common::Callout::Error::GPIO>( 74 GPIO::CALLOUT_GPIO_NUM(pin), GPIO::CALLOUT_ERRNO(0), 75 GPIO::CALLOUT_DEVICE_PATH(phys.c_str())); 76 } 77 78 void Gpio::ioCallback() 79 { 80 unsigned int type, code, value; 81 82 std::tie(type, code, value) = evdev.next(); 83 if (type != EV_KEY || code != pin) 84 { 85 return; 86 } 87 88 bool newState = value != 0; 89 90 if (currentState != newState) 91 { 92 getPolicy().stateChanged(newState, *this); 93 currentState = newState; 94 } 95 } 96 97 void Gpio::logConflict(const std::string& fanInventoryPath) const 98 { 99 using namespace sdbusplus::xyz::openbmc_project::Logging::server; 100 std::map<std::string, std::string> ad; 101 Entry::Level severity = Entry::Level::Informational; 102 103 static constexpr auto errorName = 104 "xyz.openbmc_project.Fan.Presence.Error.Detection"; 105 106 ad.emplace("_PID", std::to_string(getpid())); 107 ad.emplace("CALLOUT_INVENTORY_PATH", fanInventoryPath); 108 ad.emplace("GPIO_NUM", std::to_string(pin)); 109 ad.emplace("GPIO_DEVICE_PATH", (phys.c_str())); 110 111 getLogger().log( 112 fmt::format("GPIO presence detect for fan {} said not present but " 113 "other methods indicated present", 114 fanInventoryPath)); 115 try 116 { 117 util::SDBusPlus::callMethod(loggingService, loggingPath, 118 loggingCreateIface, "Create", errorName, 119 severity, ad); 120 } 121 catch (const util::DBusError& e) 122 { 123 getLogger().log( 124 fmt::format("Call to create a {} error for fan {} failed: {}", 125 errorName, fanInventoryPath, e.what()), 126 Logger::error); 127 } 128 } 129 130 } // namespace presence 131 } // namespace fan 132 } // namespace phosphor 133