xref: /openbmc/phosphor-power/phosphor-power-supply/util.hpp (revision f8e8bc19fa720b6b348a3dddefd8eca7650b3e26)
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