blob: e33f747da075f338df2705d3fb8703261a8af4b1 [file] [log] [blame]
Matt Spinlere9d33b62021-11-09 13:27:26 -06001/**
2 * Copyright © 2022 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
17#include "pcie_card_metadata.hpp"
18
19#include "json_config.hpp"
20
21#include <iostream>
22
23static constexpr auto cardFileName = "pcie_cards.json";
24
25namespace phosphor::fan::control::json
26{
27
28namespace fs = std::filesystem;
29using namespace phosphor::fan;
30
31PCIeCardMetadata::PCIeCardMetadata(const std::vector<std::string>& systemNames)
32{
33 loadCards(systemNames);
34}
35
36void PCIeCardMetadata::loadCards(const std::vector<std::string>& systemNames)
37{
38 const auto defaultPath = fs::path{"control"} / cardFileName;
39
40 // Look in the override location first
41 auto confFile = fs::path{confOverridePath} / defaultPath;
42
43 if (!fs::exists(confFile))
44 {
45 confFile = fs::path{confBasePath} / defaultPath;
46 }
47
48 if (fs::exists(confFile))
49 {
50 log<level::DEBUG>(
51 fmt::format("Loading PCIe card file {}", confFile.native())
52 .c_str());
53 auto json = JsonConfig::load(confFile);
54 load(json);
55 }
56
57 // Go from least specific to most specific in the system names so files in
58 // the latter category can override ones in the former.
59 for (auto nameIt = systemNames.rbegin(); nameIt != systemNames.rend();
60 ++nameIt)
61 {
62 const auto basePath = fs::path{"control"} / *nameIt / cardFileName;
63
64 // Look in the override location first
65 auto confFile = fs::path{confOverridePath} / basePath;
66
67 if (!fs::exists(confFile))
68 {
69 confFile = fs::path{confBasePath} / basePath;
70 }
71
72 if (fs::exists(confFile))
73 {
74 log<level::DEBUG>(
75 fmt::format("Loading PCIe card file {}", confFile.native())
76 .c_str());
77
78 auto json = JsonConfig::load(confFile);
79 load(json);
80 }
81 }
82
83 if (_cards.empty())
84 {
85 throw std::runtime_error{
86 "No valid PCIe card entries found in any JSON"};
87 }
88}
89
90void PCIeCardMetadata::load(const nlohmann::json& json)
91{
92 if (!json.contains("cards") || !json.at("cards").is_array())
93 {
94 throw std::runtime_error{
95 fmt::format("Missing 'cards' array in PCIe card JSON")};
96 }
97
98 for (const auto& card : json.at("cards"))
99 {
100 if (!card.contains("vendor_id") || !card.contains("device_id") ||
101 !card.contains("subsystem_vendor_id") ||
102 !card.contains("subsystem_id") ||
103 !(card.contains("has_temp_sensor") || card.contains("floor_index")))
104 {
105 throw std::runtime_error{"Invalid PCIe card json"};
106 }
107
108 Metadata data;
109 data.vendorID =
110 std::stoul(card.at("vendor_id").get<std::string>(), nullptr, 16);
111 data.deviceID =
112 std::stoul(card.at("device_id").get<std::string>(), nullptr, 16);
113 data.subsystemVendorID = std::stoul(
114 card.at("subsystem_vendor_id").get<std::string>(), nullptr, 16);
115 data.subsystemID =
116 std::stoul(card.at("subsystem_id").get<std::string>(), nullptr, 16);
117
118 data.hasTempSensor = card.value("has_temp_sensor", false);
119 data.floorIndex = card.value("floor_index", -1);
120
121 auto iter = std::find(_cards.begin(), _cards.end(), data);
122 if (iter != _cards.end())
123 {
124 iter->vendorID = data.vendorID;
125 iter->deviceID = data.deviceID;
126 iter->subsystemVendorID = data.subsystemVendorID;
127 iter->subsystemID = data.subsystemID;
128 iter->floorIndex = data.floorIndex;
129 iter->hasTempSensor = data.hasTempSensor;
130 }
131 else
132 {
133 _cards.push_back(std::move(data));
134 }
135 }
136}
137
138void PCIeCardMetadata::dump() const
139{
140 for (const auto& entry : _cards)
141 {
142 std::cerr << "--------------------------------------------------"
143 << "\n";
144 std::cerr << "vendorID: " << std::hex << entry.vendorID << "\n";
145 std::cerr << "deviceID: " << entry.deviceID << "\n";
146 std::cerr << "subsysVendorID: " << entry.subsystemVendorID << "\n";
147 std::cerr << "subsystemID: " << entry.subsystemID << "\n";
148 std::cerr << "hasTempSensor: " << std::dec << entry.hasTempSensor
149 << "\n";
150 std::cerr << "floorIndex: " << entry.floorIndex << "\n";
151 }
152}
153
154std::optional<std::variant<int32_t, bool>>
155 PCIeCardMetadata::lookup(uint16_t deviceID, uint16_t vendorID,
156 uint16_t subsystemID,
157 uint16_t subsystemVendorID) const
158{
159 log<level::DEBUG>(fmt::format("Lookup {:#x} ${:#x} {:#x} {:#x}", deviceID,
160 vendorID, subsystemID, subsystemVendorID)
161 .c_str());
162 auto card =
163 std::find_if(_cards.begin(), _cards.end(),
164 [&deviceID, &vendorID, &subsystemID,
165 &subsystemVendorID](const auto& card) {
166 return (deviceID == card.deviceID) &&
167 (vendorID == card.vendorID) &&
168 (subsystemID == card.subsystemID) &&
169 (subsystemVendorID == card.subsystemVendorID);
170 });
171
172 if (card != _cards.end())
173 {
174 if (card->hasTempSensor)
175 {
176 return true;
177 }
178 return card->floorIndex;
179 }
180 return std::nullopt;
181}
182
183} // namespace phosphor::fan::control::json