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