1 /**
2  * Copyright © 2024 IBM Corporation
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *     http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 #include "config.h"
17 
18 #include "model.hpp"
19 
20 #include "pmbus.hpp"
21 #include "utility.hpp"
22 #include "utils.hpp"
23 
24 #include <nlohmann/json.hpp>
25 #include <phosphor-logging/log.hpp>
26 
27 #include <exception>
28 #include <format>
29 #include <fstream>
30 #include <stdexcept>
31 
32 using json = nlohmann::json;
33 
34 using namespace utils;
35 using namespace phosphor::logging;
36 using namespace phosphor::power::util;
37 
38 namespace model
39 {
40 
41 namespace internal
42 {
43 
44 /**
45  * @brief Get the PSU model from sysfs.
46  *
47  * Obtain PSU information from the PSU JSON file.
48  *
49  * Throws an exception if an error occurs.
50  *
51  * @param[in] psuInventoryPath - PSU D-Bus inventory path
52  *
53  * @return PSU model
54  */
getModelJson(const std::string & psuInventoryPath)55 std::string getModelJson(const std::string& psuInventoryPath)
56 {
57     // Parse PSU JSON file
58     std::ifstream file{PSU_JSON_PATH};
59     json data = json::parse(file);
60 
61     // Get PSU device path from JSON
62     auto it = data.find("psuDevices");
63     if (it == data.end())
64     {
65         throw std::runtime_error{"Unable to find psuDevices"};
66     }
67     auto device = it->find(psuInventoryPath);
68     if (device == it->end())
69     {
70         throw std::runtime_error{std::format(
71             "Unable to find device path for PSU {}", psuInventoryPath)};
72     }
73     std::string devicePath = *device;
74     if (devicePath.empty())
75     {
76         throw std::runtime_error{
77             std::format("Empty device path for PSU {}", psuInventoryPath)};
78     }
79 
80     // Get sysfs filename from JSON for Model information
81     it = data.find("fruConfigs");
82     if (it == data.end())
83     {
84         throw std::runtime_error{"Unable to find fruConfigs"};
85     }
86     std::string fileName;
87     for (const auto& fru : *it)
88     {
89         if (fru.contains("propertyName") && (fru["propertyName"] == "Model") &&
90             fru.contains("fileName"))
91         {
92             fileName = fru["fileName"];
93             break;
94         }
95     }
96     if (fileName.empty())
97     {
98         throw std::runtime_error{"Unable to find file name for Model"};
99     }
100 
101     // Get PMBus access type from JSON
102     phosphor::pmbus::Type type = getPMBusAccessType(data);
103 
104     // Read model from sysfs file
105     phosphor::pmbus::PMBus pmbus(devicePath);
106     std::string model = pmbus.readString(fileName, type);
107     return model;
108 }
109 
110 /**
111  * @brief Get the PSU model from sysfs.
112  *
113  * Obtain PSU information from D-Bus.
114  *
115  * Throws an exception if an error occurs.
116  *
117  * @param[in] bus - D-Bus connection
118  * @param[in] psuInventoryPath - PSU D-Bus inventory path
119  *
120  * @return PSU model
121  */
getModelDbus(sdbusplus::bus_t & bus,const std::string & psuInventoryPath)122 std::string getModelDbus(sdbusplus::bus_t& bus,
123                          const std::string& psuInventoryPath)
124 {
125     // Get PSU I2C bus/address and create PMBus interface
126     const auto [i2cBus, i2cAddr] = getPsuI2c(bus, psuInventoryPath);
127     auto pmbus = getPmbusIntf(i2cBus, i2cAddr);
128 
129     // Read model from sysfs file
130     std::string fileName = "ccin";
131     auto type = phosphor::pmbus::Type::HwmonDeviceDebug;
132     std::string model = pmbus->readString(fileName, type);
133     return model;
134 }
135 
136 } // namespace internal
137 
getModel(sdbusplus::bus_t & bus,const std::string & psuInventoryPath)138 std::string getModel(sdbusplus::bus_t& bus, const std::string& psuInventoryPath)
139 {
140     std::string model;
141     try
142     {
143         if (usePsuJsonFile())
144         {
145             // Obtain PSU information from JSON file
146             model = internal::getModelJson(psuInventoryPath);
147         }
148         else
149         {
150             // Obtain PSU information from D-Bus
151             model = internal::getModelDbus(bus, psuInventoryPath);
152         }
153     }
154     catch (const std::exception& e)
155     {
156         log<level::ERR>(std::format("Error: {}", e.what()).c_str());
157     }
158     return model;
159 }
160 
161 } // namespace model
162