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