1 #pragma once 2 #include "util_base.hpp" 3 #include "utility.hpp" 4 #include "xyz/openbmc_project/Common/error.hpp" 5 6 #include <gpiod.hpp> 7 #include <phosphor-logging/elog-errors.hpp> 8 #include <phosphor-logging/elog.hpp> 9 #include <phosphor-logging/lg2.hpp> 10 11 #include <bitset> 12 #include <chrono> 13 #include <format> 14 15 namespace phosphor::power::psu 16 { 17 18 using Property = std::string; 19 using Value = std::variant<bool, std::string>; 20 using PropertyMap = std::map<Property, Value>; 21 using Interface = std::string; 22 using InterfaceMap = std::map<Interface, PropertyMap>; 23 using Object = sdbusplus::message::object_path; 24 using ObjectMap = std::map<Object, InterfaceMap>; 25 26 class Util : public UtilBase 27 { 28 public: getPresence(sdbusplus::bus_t & bus,const std::string & invpath) const29 bool getPresence(sdbusplus::bus_t& bus, 30 const std::string& invpath) const override 31 { 32 bool present = false; 33 34 // Use getProperty utility function to get presence status. 35 util::getProperty(INVENTORY_IFACE, PRESENT_PROP, invpath, 36 INVENTORY_MGR_IFACE, bus, present); 37 38 return present; 39 } 40 setPresence(sdbusplus::bus_t & bus,const std::string & invpath,bool present,const std::string & name) const41 void setPresence(sdbusplus::bus_t& bus, const std::string& invpath, 42 bool present, const std::string& name) const override 43 { 44 lg2::info("Updating inventory present property. " 45 "present:{PRESENT} invpath:{INVPATH} name:{NAME}", 46 "PRESENT", present, "INVPATH", invpath, "NAME", name); 47 48 using InternalFailure = 49 sdbusplus::xyz::openbmc_project::Common::Error::InternalFailure; 50 PropertyMap invProp; 51 52 invProp.emplace("Present", present); 53 invProp.emplace("PrettyName", name); 54 55 InterfaceMap invIntf; 56 invIntf.emplace("xyz.openbmc_project.Inventory.Item", 57 std::move(invProp)); 58 59 Interface extraIface = "xyz.openbmc_project.Inventory.Item.PowerSupply"; 60 61 invIntf.emplace(extraIface, PropertyMap()); 62 63 ObjectMap invObj; 64 invObj.emplace(std::move(invpath), std::move(invIntf)); 65 66 try 67 { 68 auto invService = phosphor::power::util::getService( 69 INVENTORY_OBJ_PATH, INVENTORY_MGR_IFACE, bus); 70 71 // Update inventory 72 auto invMsg = 73 bus.new_method_call(invService.c_str(), INVENTORY_OBJ_PATH, 74 INVENTORY_MGR_IFACE, "Notify"); 75 invMsg.append(std::move(invObj)); 76 auto invMgrResponseMsg = bus.call(invMsg); 77 } 78 catch (const std::exception& e) 79 { 80 lg2::error( 81 "Error in inventory manager call to update inventory: {ERROR}", 82 "ERROR", e); 83 phosphor::logging::elog<InternalFailure>(); 84 } 85 } 86 setAvailable(sdbusplus::bus_t & bus,const std::string & invpath,bool available) const87 void setAvailable(sdbusplus::bus_t& bus, const std::string& invpath, 88 bool available) const override 89 { 90 PropertyMap invProp; 91 InterfaceMap invIntf; 92 ObjectMap invObj; 93 94 invProp.emplace(AVAILABLE_PROP, available); 95 invIntf.emplace(AVAILABILITY_IFACE, std::move(invProp)); 96 97 invObj.emplace(std::move(invpath), std::move(invIntf)); 98 99 try 100 { 101 auto invService = phosphor::power::util::getService( 102 INVENTORY_OBJ_PATH, INVENTORY_MGR_IFACE, bus); 103 104 auto invMsg = 105 bus.new_method_call(invService.c_str(), INVENTORY_OBJ_PATH, 106 INVENTORY_MGR_IFACE, "Notify"); 107 invMsg.append(std::move(invObj)); 108 auto invMgrResponseMsg = bus.call(invMsg); 109 } 110 catch (const sdbusplus::exception_t& e) 111 { 112 lg2::error("Error in inventory manager call to update " 113 "availability interface: {ERROR}", 114 "ERROR", e); 115 throw; 116 } 117 } 118 handleChassisHealthRollup(sdbusplus::bus_t & bus,const std::string & invpath,bool addRollup) const119 void handleChassisHealthRollup(sdbusplus::bus_t& bus, 120 const std::string& invpath, 121 bool addRollup) const override 122 { 123 using AssociationTuple = 124 std::tuple<std::string, std::string, std::string>; 125 using AssociationsProperty = std::vector<AssociationTuple>; 126 try 127 { 128 auto chassisPath = getChassis(bus, invpath); 129 130 auto service = phosphor::power::util::getService( 131 invpath, ASSOC_DEF_IFACE, bus); 132 133 AssociationsProperty associations; 134 phosphor::power::util::getProperty<AssociationsProperty>( 135 ASSOC_DEF_IFACE, ASSOC_PROP, invpath, service, bus, 136 associations); 137 138 AssociationTuple critAssociation{"health_rollup", "critical", 139 chassisPath}; 140 141 auto assocIt = std::find(associations.begin(), associations.end(), 142 critAssociation); 143 if (addRollup) 144 { 145 if (assocIt != associations.end()) 146 { 147 // It's already there 148 return; 149 } 150 151 associations.push_back(critAssociation); 152 } 153 else 154 { 155 if (assocIt == associations.end()) 156 { 157 // It's already been removed. 158 return; 159 } 160 161 // If the object still isn't functional, then don't clear 162 // the association. 163 bool functional = false; 164 phosphor::power::util::getProperty<bool>( 165 OPERATIONAL_STATE_IFACE, FUNCTIONAL_PROP, invpath, service, 166 bus, functional); 167 168 if (!functional) 169 { 170 return; 171 } 172 173 associations.erase(assocIt); 174 } 175 176 phosphor::power::util::setProperty(ASSOC_DEF_IFACE, ASSOC_PROP, 177 invpath, service, bus, 178 associations); 179 } 180 catch (const sdbusplus::exception_t& e) 181 { 182 lg2::info("Error trying to handle health rollup " 183 "associations for {INVPATH}: {ERROR}", 184 "INVPATH", invpath, "ERROR", e); 185 } 186 } 187 getChassis(sdbusplus::bus_t & bus,const std::string & invpath) const188 std::string getChassis(sdbusplus::bus_t& bus, 189 const std::string& invpath) const override 190 { 191 sdbusplus::message::object_path assocPath = invpath + "/powering"; 192 sdbusplus::message::object_path basePath{"/"}; 193 std::vector<std::string> interfaces{CHASSIS_IFACE}; 194 195 // Find the object path that implements the chassis interface 196 // and also shows up in the endpoints list of the powering assoc. 197 auto chassisPaths = phosphor::power::util::getAssociatedSubTreePaths( 198 bus, assocPath, basePath, interfaces, 0); 199 200 if (chassisPaths.empty()) 201 { 202 throw std::runtime_error(std::format( 203 "No association to a chassis found for {}", invpath)); 204 } 205 206 return chassisPaths[0]; 207 } 208 }; 209 210 std::unique_ptr<GPIOInterfaceBase> createGPIO(const std::string& namedGpio); 211 212 class GPIOInterface : public GPIOInterfaceBase 213 { 214 public: 215 GPIOInterface() = delete; 216 virtual ~GPIOInterface() = default; 217 GPIOInterface(const GPIOInterface&) = default; 218 GPIOInterface& operator=(const GPIOInterface&) = default; 219 GPIOInterface(GPIOInterface&&) = default; 220 GPIOInterface& operator=(GPIOInterface&&) = default; 221 222 /** 223 * Constructor 224 * 225 * @param[in] namedGpio - The string for the gpio-line-name 226 */ 227 GPIOInterface(const std::string& namedGpio); 228 229 static std::unique_ptr<GPIOInterfaceBase> createGPIO( 230 const std::string& namedGpio); 231 232 /** 233 * @brief Attempts to read the state of the GPIO line. 234 * 235 * Throws an exception if line not found, request line fails, or get_value 236 * from line fails. 237 * 238 * @return 1 for active (low/present), 0 for not active (high/not present). 239 */ 240 int read() override; 241 242 /** 243 * @brief Attempts to set the state of the GPIO line to the specified value. 244 * 245 * Throws an exception if line not found, request line fails, or set_value 246 * to line fails. 247 * 248 * @param[in] value - The value to set the state of the GPIO line, 1 or 0. 249 * @param[in] flags - Additional line request flags as defined in gpiod.hpp. 250 */ 251 void write(int value, std::bitset<32> flags) override; 252 253 /** 254 * @brief Attempts to toggle (write) a GPIO low then high. 255 * 256 * Relies on write, so throws exception if line not found, etc. 257 * 258 * @param[in] delay - Milliseconds to delay betwen low/high toggle. 259 */ 260 void toggleLowHigh(const std::chrono::milliseconds& delay) override; 261 262 /** 263 * @brief Returns the name of the GPIO, if not empty. 264 */ 265 std::string getName() const override; 266 267 private: 268 gpiod::line line; 269 }; 270 271 } // namespace phosphor::power::psu 272