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