xref: /openbmc/phosphor-logging/extensions/openpower-pels/device_callouts.cpp (revision 44c0a643bcf351abd72fe5ca28386e75333117d4)
118c42b0fSMatt Spinler /**
218c42b0fSMatt Spinler  * Copyright © 2020 IBM Corporation
318c42b0fSMatt Spinler  *
418c42b0fSMatt Spinler  * Licensed under the Apache License, Version 2.0 (the "License");
518c42b0fSMatt Spinler  * you may not use this file except in compliance with the License.
618c42b0fSMatt Spinler  * You may obtain a copy of the License at
718c42b0fSMatt Spinler  *
818c42b0fSMatt Spinler  *     http://www.apache.org/licenses/LICENSE-2.0
918c42b0fSMatt Spinler  *
1018c42b0fSMatt Spinler  * Unless required by applicable law or agreed to in writing, software
1118c42b0fSMatt Spinler  * distributed under the License is distributed on an "AS IS" BASIS,
1218c42b0fSMatt Spinler  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
1318c42b0fSMatt Spinler  * See the License for the specific language governing permissions and
1418c42b0fSMatt Spinler  * limitations under the License.
1518c42b0fSMatt Spinler  */
1618c42b0fSMatt Spinler #include "device_callouts.hpp"
1718c42b0fSMatt Spinler 
1818c42b0fSMatt Spinler #include "paths.hpp"
1918c42b0fSMatt Spinler 
2018c42b0fSMatt Spinler #include <fstream>
2118c42b0fSMatt Spinler #include <phosphor-logging/log.hpp>
2218c42b0fSMatt Spinler #include <regex>
2318c42b0fSMatt Spinler 
2418c42b0fSMatt Spinler namespace openpower::pels::device_callouts
2518c42b0fSMatt Spinler {
2618c42b0fSMatt Spinler 
2718c42b0fSMatt Spinler constexpr auto debugFilePath = "/etc/phosphor-logging/";
2818c42b0fSMatt Spinler constexpr auto calloutFileSuffix = "_dev_callouts.json";
2918c42b0fSMatt Spinler 
3018c42b0fSMatt Spinler namespace fs = std::filesystem;
3118c42b0fSMatt Spinler using namespace phosphor::logging;
3218c42b0fSMatt Spinler 
3318c42b0fSMatt Spinler namespace util
3418c42b0fSMatt Spinler {
3518c42b0fSMatt Spinler 
3618c42b0fSMatt Spinler fs::path getJSONFilename(const std::vector<std::string>& compatibleList)
3718c42b0fSMatt Spinler {
3818c42b0fSMatt Spinler     auto basePath = getPELReadOnlyDataPath();
3918c42b0fSMatt Spinler     fs::path fullPath;
4018c42b0fSMatt Spinler 
4118c42b0fSMatt Spinler     // Find an entry in the list of compatible system names that
4218c42b0fSMatt Spinler     // matches a filename we have.
4318c42b0fSMatt Spinler 
4418c42b0fSMatt Spinler     for (const auto& name : compatibleList)
4518c42b0fSMatt Spinler     {
4618c42b0fSMatt Spinler         fs::path filename = name + calloutFileSuffix;
4718c42b0fSMatt Spinler 
4818c42b0fSMatt Spinler         // Check the debug path first
4918c42b0fSMatt Spinler         fs::path path{fs::path{debugFilePath} / filename};
5018c42b0fSMatt Spinler 
5118c42b0fSMatt Spinler         if (fs::exists(path))
5218c42b0fSMatt Spinler         {
5318c42b0fSMatt Spinler             log<level::INFO>("Found device callout debug file");
5418c42b0fSMatt Spinler             fullPath = path;
5518c42b0fSMatt Spinler             break;
5618c42b0fSMatt Spinler         }
5718c42b0fSMatt Spinler 
5818c42b0fSMatt Spinler         path = basePath / filename;
5918c42b0fSMatt Spinler 
6018c42b0fSMatt Spinler         if (fs::exists(path))
6118c42b0fSMatt Spinler         {
6218c42b0fSMatt Spinler             fullPath = path;
6318c42b0fSMatt Spinler             break;
6418c42b0fSMatt Spinler         }
6518c42b0fSMatt Spinler     }
6618c42b0fSMatt Spinler 
6718c42b0fSMatt Spinler     if (fullPath.empty())
6818c42b0fSMatt Spinler     {
6918c42b0fSMatt Spinler         throw std::invalid_argument(
7018c42b0fSMatt Spinler             "No JSON dev path callout file for this system");
7118c42b0fSMatt Spinler     }
7218c42b0fSMatt Spinler 
7318c42b0fSMatt Spinler     return fullPath;
7418c42b0fSMatt Spinler }
7518c42b0fSMatt Spinler 
7618c42b0fSMatt Spinler /**
7718c42b0fSMatt Spinler  * @brief Reads the callout JSON into an object based on the
7818c42b0fSMatt Spinler  *        compatible system names list.
7918c42b0fSMatt Spinler  *
8018c42b0fSMatt Spinler  * @param[in] compatibleList - The list of compatible names for this
8118c42b0fSMatt Spinler  *                             system.
8218c42b0fSMatt Spinler  *
8318c42b0fSMatt Spinler  * @return nlohmann::json - The JSON object
8418c42b0fSMatt Spinler  */
8518c42b0fSMatt Spinler nlohmann::json loadJSON(const std::vector<std::string>& compatibleList)
8618c42b0fSMatt Spinler {
8718c42b0fSMatt Spinler     auto filename = getJSONFilename(compatibleList);
8818c42b0fSMatt Spinler     std::ifstream file{filename};
8918c42b0fSMatt Spinler     return nlohmann::json::parse(file);
9018c42b0fSMatt Spinler }
9118c42b0fSMatt Spinler 
92*44c0a643SMatt Spinler std::tuple<size_t, uint8_t> getI2CSearchKeys(const std::string& devPath)
93*44c0a643SMatt Spinler {
94*44c0a643SMatt Spinler     std::smatch match;
95*44c0a643SMatt Spinler 
96*44c0a643SMatt Spinler     // Look for i2c-A/A-00BB
97*44c0a643SMatt Spinler     // where A = bus number and BB = address
98*44c0a643SMatt Spinler     std::regex regex{"i2c-[0-9]+/([0-9]+)-00([0-9a-f]{2})"};
99*44c0a643SMatt Spinler 
100*44c0a643SMatt Spinler     regex_search(devPath, match, regex);
101*44c0a643SMatt Spinler 
102*44c0a643SMatt Spinler     if (match.size() != 3)
103*44c0a643SMatt Spinler     {
104*44c0a643SMatt Spinler         std::string msg = "Could not get I2C bus and address from " + devPath;
105*44c0a643SMatt Spinler         throw std::invalid_argument{msg.c_str()};
106*44c0a643SMatt Spinler     }
107*44c0a643SMatt Spinler 
108*44c0a643SMatt Spinler     size_t bus = std::stoul(match[1].str(), nullptr, 0);
109*44c0a643SMatt Spinler 
110*44c0a643SMatt Spinler     // An I2C bus on a CFAM has everything greater than the 10s digit
111*44c0a643SMatt Spinler     // as the CFAM number, so strip it off.  Like:
112*44c0a643SMatt Spinler     //    112 = cfam1 bus 12
113*44c0a643SMatt Spinler     //    1001 = cfam10 bus 1
114*44c0a643SMatt Spinler     bus = bus % 100;
115*44c0a643SMatt Spinler 
116*44c0a643SMatt Spinler     uint8_t address = std::stoul(match[2].str(), nullptr, 16);
117*44c0a643SMatt Spinler 
118*44c0a643SMatt Spinler     return {bus, address};
119*44c0a643SMatt Spinler }
120*44c0a643SMatt Spinler 
121*44c0a643SMatt Spinler std::string getFSISearchKeys(const std::string& devPath)
122*44c0a643SMatt Spinler {
123*44c0a643SMatt Spinler     std::string links;
124*44c0a643SMatt Spinler     std::smatch match;
125*44c0a643SMatt Spinler     auto search = devPath;
126*44c0a643SMatt Spinler 
127*44c0a643SMatt Spinler     // Look for slave@XX:
128*44c0a643SMatt Spinler     // where XX = link number in hex
129*44c0a643SMatt Spinler     std::regex regex{"slave@([0-9a-f]{2}):"};
130*44c0a643SMatt Spinler 
131*44c0a643SMatt Spinler     // Find all links in the path and separate them with hyphens.
132*44c0a643SMatt Spinler     while (regex_search(search, match, regex))
133*44c0a643SMatt Spinler     {
134*44c0a643SMatt Spinler         // Convert to an int first to handle a hex number like "0a"
135*44c0a643SMatt Spinler         // though in reality there won't be more than links 0 - 9.
136*44c0a643SMatt Spinler         auto linkNum = std::stoul(match[1].str(), nullptr, 16);
137*44c0a643SMatt Spinler         links += std::to_string(linkNum) + '-';
138*44c0a643SMatt Spinler 
139*44c0a643SMatt Spinler         search = match.suffix();
140*44c0a643SMatt Spinler     }
141*44c0a643SMatt Spinler 
142*44c0a643SMatt Spinler     if (links.empty())
143*44c0a643SMatt Spinler     {
144*44c0a643SMatt Spinler         std::string msg = "Could not get FSI links from " + devPath;
145*44c0a643SMatt Spinler         throw std::invalid_argument{msg.c_str()};
146*44c0a643SMatt Spinler     }
147*44c0a643SMatt Spinler 
148*44c0a643SMatt Spinler     // Remove the trailing '-'
149*44c0a643SMatt Spinler     links.pop_back();
150*44c0a643SMatt Spinler 
151*44c0a643SMatt Spinler     return links;
152*44c0a643SMatt Spinler }
153*44c0a643SMatt Spinler 
154*44c0a643SMatt Spinler std::tuple<std::string, std::tuple<size_t, uint8_t>>
155*44c0a643SMatt Spinler     getFSII2CSearchKeys(const std::string& devPath)
156*44c0a643SMatt Spinler {
157*44c0a643SMatt Spinler     // This combines the FSI and i2C search keys
158*44c0a643SMatt Spinler 
159*44c0a643SMatt Spinler     auto links = getFSISearchKeys(devPath);
160*44c0a643SMatt Spinler     auto busAndAddr = getI2CSearchKeys(devPath);
161*44c0a643SMatt Spinler 
162*44c0a643SMatt Spinler     return {std::move(links), std::move(busAndAddr)};
163*44c0a643SMatt Spinler }
164*44c0a643SMatt Spinler 
165*44c0a643SMatt Spinler size_t getSPISearchKeys(const std::string& devPath)
166*44c0a643SMatt Spinler {
167*44c0a643SMatt Spinler     std::smatch match;
168*44c0a643SMatt Spinler 
169*44c0a643SMatt Spinler     // Look for spi_master/spiX/ where X is the SPI bus/port number
170*44c0a643SMatt Spinler     // Note: This doesn't distinguish between multiple chips on
171*44c0a643SMatt Spinler     // the same port as no need for it yet.
172*44c0a643SMatt Spinler     std::regex regex{"spi_master/spi(\\d+)/"};
173*44c0a643SMatt Spinler 
174*44c0a643SMatt Spinler     regex_search(devPath, match, regex);
175*44c0a643SMatt Spinler 
176*44c0a643SMatt Spinler     if (match.size() != 2)
177*44c0a643SMatt Spinler     {
178*44c0a643SMatt Spinler         std::string msg = "Could not get SPI bus from " + devPath;
179*44c0a643SMatt Spinler         throw std::invalid_argument{msg.c_str()};
180*44c0a643SMatt Spinler     }
181*44c0a643SMatt Spinler 
182*44c0a643SMatt Spinler     size_t port = std::stoul(match[1].str());
183*44c0a643SMatt Spinler 
184*44c0a643SMatt Spinler     return port;
185*44c0a643SMatt Spinler }
186*44c0a643SMatt Spinler 
187*44c0a643SMatt Spinler std::tuple<std::string, size_t> getFSISPISearchKeys(const std::string& devPath)
188*44c0a643SMatt Spinler {
189*44c0a643SMatt Spinler 
190*44c0a643SMatt Spinler     // Combine the FSI and SPI search keys.
191*44c0a643SMatt Spinler     auto links = getFSISearchKeys(devPath);
192*44c0a643SMatt Spinler     auto bus = getSPISearchKeys(devPath);
193*44c0a643SMatt Spinler 
194*44c0a643SMatt Spinler     return {std::move(links), std::move(bus)};
195*44c0a643SMatt Spinler }
196*44c0a643SMatt Spinler 
19718c42b0fSMatt Spinler std::vector<device_callouts::Callout>
19818c42b0fSMatt Spinler     calloutI2C(size_t i2cBus, uint8_t i2cAddress,
19918c42b0fSMatt Spinler                const nlohmann::json& calloutJSON)
20018c42b0fSMatt Spinler {
20118c42b0fSMatt Spinler     // TODO
20218c42b0fSMatt Spinler     return {};
20318c42b0fSMatt Spinler }
20418c42b0fSMatt Spinler 
20518c42b0fSMatt Spinler std::vector<device_callouts::Callout> findCallouts(const std::string& devPath,
20618c42b0fSMatt Spinler                                                    const nlohmann::json& json)
20718c42b0fSMatt Spinler {
20818c42b0fSMatt Spinler     std::vector<Callout> callouts;
209a307089cSMatt Spinler     fs::path path;
21018c42b0fSMatt Spinler 
211a307089cSMatt Spinler     // Gives the /sys/devices/platform/ path
212a307089cSMatt Spinler     try
213a307089cSMatt Spinler     {
214a307089cSMatt Spinler         path = fs::canonical(devPath);
215a307089cSMatt Spinler     }
216a307089cSMatt Spinler     catch (const fs::filesystem_error& e)
217a307089cSMatt Spinler     {
218a307089cSMatt Spinler         // Path not there, still try to do the callout
219a307089cSMatt Spinler         path = devPath;
220a307089cSMatt Spinler     }
221a307089cSMatt Spinler 
222a307089cSMatt Spinler     switch (util::getCalloutType(path))
223a307089cSMatt Spinler     {
224a307089cSMatt Spinler         case util::CalloutType::i2c:
225a307089cSMatt Spinler             // callouts = calloutI2CUsingPath(errnoValue, path, json);
226a307089cSMatt Spinler             break;
227a307089cSMatt Spinler         case util::CalloutType::fsi:
228a307089cSMatt Spinler             // callouts = calloutFSI(errnoValue, path, json);
229a307089cSMatt Spinler             break;
230a307089cSMatt Spinler         case util::CalloutType::fsii2c:
231a307089cSMatt Spinler             // callouts = calloutFSII2C(errnoValue, path, json);
232a307089cSMatt Spinler             break;
233a307089cSMatt Spinler         case util::CalloutType::fsispi:
234a307089cSMatt Spinler             // callouts = calloutFSISPI(errnoValue, path, json);
235a307089cSMatt Spinler             break;
236a307089cSMatt Spinler         default:
237a307089cSMatt Spinler             std::string msg =
238a307089cSMatt Spinler                 "Could not get callout type from device path: " + path.string();
239a307089cSMatt Spinler             throw std::invalid_argument{msg.c_str()};
240a307089cSMatt Spinler             break;
241a307089cSMatt Spinler     }
24218c42b0fSMatt Spinler 
24318c42b0fSMatt Spinler     return callouts;
24418c42b0fSMatt Spinler }
24518c42b0fSMatt Spinler 
246a307089cSMatt Spinler CalloutType getCalloutType(const std::string& devPath)
247a307089cSMatt Spinler {
248a307089cSMatt Spinler     if ((devPath.find("fsi-master") != std::string::npos) &&
249a307089cSMatt Spinler         (devPath.find("i2c-") != std::string::npos))
250a307089cSMatt Spinler     {
251a307089cSMatt Spinler         return CalloutType::fsii2c;
252a307089cSMatt Spinler     }
253a307089cSMatt Spinler 
254a307089cSMatt Spinler     if ((devPath.find("fsi-master") != std::string::npos) &&
255a307089cSMatt Spinler         (devPath.find("spi") != std::string::npos))
256a307089cSMatt Spinler     {
257a307089cSMatt Spinler         return CalloutType::fsispi;
258a307089cSMatt Spinler     }
259a307089cSMatt Spinler 
260a307089cSMatt Spinler     // Treat anything else FSI related as plain FSI
261a307089cSMatt Spinler     if (devPath.find("fsi-master") != std::string::npos)
262a307089cSMatt Spinler     {
263a307089cSMatt Spinler         return CalloutType::fsi;
264a307089cSMatt Spinler     }
265a307089cSMatt Spinler 
266a307089cSMatt Spinler     if (devPath.find("i2c-bus/i2c-") != std::string::npos)
267a307089cSMatt Spinler     {
268a307089cSMatt Spinler         return CalloutType::i2c;
269a307089cSMatt Spinler     }
270a307089cSMatt Spinler 
271a307089cSMatt Spinler     return CalloutType::unknown;
272a307089cSMatt Spinler }
273a307089cSMatt Spinler 
27418c42b0fSMatt Spinler } // namespace util
27518c42b0fSMatt Spinler 
27618c42b0fSMatt Spinler std::vector<Callout> getCallouts(const std::string& devPath,
27718c42b0fSMatt Spinler                                  const std::vector<std::string>& compatibleList)
27818c42b0fSMatt Spinler {
27918c42b0fSMatt Spinler     auto json = util::loadJSON(compatibleList);
28018c42b0fSMatt Spinler     return util::findCallouts(devPath, json);
28118c42b0fSMatt Spinler }
28218c42b0fSMatt Spinler 
28318c42b0fSMatt Spinler std::vector<Callout>
28418c42b0fSMatt Spinler     getI2CCallouts(size_t i2cBus, uint8_t i2cAddress,
28518c42b0fSMatt Spinler                    const std::vector<std::string>& compatibleList)
28618c42b0fSMatt Spinler {
28718c42b0fSMatt Spinler     auto json = util::loadJSON(compatibleList);
28818c42b0fSMatt Spinler     return util::calloutI2C(i2cBus, i2cAddress, json);
28918c42b0fSMatt Spinler }
29018c42b0fSMatt Spinler 
29118c42b0fSMatt Spinler } // namespace openpower::pels::device_callouts
292