blob: 5b34872213bdbd87c0c595bcc3548dd238b66e67 [file] [log] [blame]
Feist, Jamesc95cf672019-08-29 16:10:35 -07001/*
2// Copyright (c) 2019 Intel 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 "utils.hpp"
18
19#include <boost/algorithm/string/replace.hpp>
20#include <boost/asio/steady_timer.hpp>
James Feist0b236ab2019-10-02 09:09:16 -070021#include <filesystem>
Feist, Jamesc95cf672019-08-29 16:10:35 -070022#include <iostream>
23#include <sdbusplus/asio/connection.hpp>
24#include <sdbusplus/asio/object_server.hpp>
25#include <sdbusplus/bus/match.hpp>
26#include <string>
James Feist45772222019-09-27 10:38:08 -070027#include <utility>
Feist, Jamesc95cf672019-08-29 16:10:35 -070028
29extern "C" {
30#include <i2c/smbus.h>
31#include <linux/i2c-dev.h>
32}
33
34constexpr const char* configType =
35 "xyz.openbmc_project.Configuration.Intel_HSBP_CPLD";
James Feistdb2e0e72019-10-07 16:34:06 -070036constexpr const char* busName = "xyz.openbmc_project.HsbpManager";
Feist, Jamesc95cf672019-08-29 16:10:35 -070037
James Feist45772222019-09-27 10:38:08 -070038constexpr size_t scanRateSeconds = 5;
39constexpr size_t maxDrives = 8; // only 1 byte alloted
40
Feist, Jamesc95cf672019-08-29 16:10:35 -070041boost::asio::io_context io;
42auto conn = std::make_shared<sdbusplus::asio::connection>(io);
43sdbusplus::asio::object_server objServer(conn);
44
James Feist45772222019-09-27 10:38:08 -070045static std::string zeroPad(const uint8_t val)
46{
47 std::ostringstream version;
48 version << std::setw(2) << std::setfill('0') << static_cast<size_t>(val);
49 return version.str();
50}
51
James Feist0b236ab2019-10-02 09:09:16 -070052struct Mux
53{
54 Mux(size_t busIn, size_t addressIn) : bus(busIn), address(addressIn)
55 {
56 }
57 size_t bus;
58 size_t address;
59};
James Feist45772222019-09-27 10:38:08 -070060struct Drive
61{
James Feist244f3232019-09-27 15:15:14 -070062 Drive(size_t driveIndex, bool isPresent, bool isOperational, bool nvme,
63 bool rebuilding) :
James Feist45772222019-09-27 10:38:08 -070064 isNvme(nvme)
65 {
66 constexpr const char* basePath =
67 "/xyz/openbmc_project/inventory/item/drive/Drive_";
68 itemIface = objServer.add_interface(
69 basePath + std::to_string(driveIndex), inventory::interface);
70 itemIface->register_property("Present", isPresent);
71 itemIface->register_property("PrettyName",
72 "Drive " + std::to_string(driveIndex));
73 itemIface->initialize();
74 operationalIface = objServer.add_interface(
James Feist244f3232019-09-27 15:15:14 -070075 itemIface->get_object_path(),
James Feist45772222019-09-27 10:38:08 -070076 "xyz.openbmc_project.State.Decorator.OperationalStatus");
77 operationalIface->register_property("Functional", isOperational);
78 operationalIface->initialize();
James Feist244f3232019-09-27 15:15:14 -070079 rebuildingIface = objServer.add_interface(
80 itemIface->get_object_path(), "xyz.openbmc_project.State.Drive");
81 rebuildingIface->register_property("Rebuilding", rebuilding);
82 rebuildingIface->initialize();
James Feist45772222019-09-27 10:38:08 -070083 }
84 ~Drive()
85 {
86 objServer.remove_interface(itemIface);
87 objServer.remove_interface(operationalIface);
James Feist244f3232019-09-27 15:15:14 -070088 objServer.remove_interface(rebuildingIface);
James Feist0b236ab2019-10-02 09:09:16 -070089 objServer.remove_interface(associationIface);
James Feistdb2e0e72019-10-07 16:34:06 -070090 objServer.remove_interface(driveIface);
James Feist0b236ab2019-10-02 09:09:16 -070091 }
92
93 void createAssociation(const std::string& path)
94 {
95 if (associationIface != nullptr)
96 {
97 return;
98 }
99 associationIface = objServer.add_interface(
100 itemIface->get_object_path(),
101 "xyz.openbmc_project.Association.Definitions");
102 std::vector<Association> associations;
103 associations.emplace_back("inventory", "drive", path);
104 associationIface->register_property("Associations", associations);
105 associationIface->initialize();
James Feist45772222019-09-27 10:38:08 -0700106 }
107
James Feistdb2e0e72019-10-07 16:34:06 -0700108 void removeDriveIface()
109 {
110 objServer.remove_interface(driveIface);
111 }
112
113 void createDriveIface()
114 {
115 if (associationIface != nullptr || driveIface != nullptr)
116 {
117 // this shouldn't be used if we found another provider of the drive
118 // interface, or if we already created one
119 return;
120 }
121 driveIface =
122 objServer.add_interface(itemIface->get_object_path(),
123 "xyz.openbmc_project.Inventory.Item.Drive");
124 driveIface->initialize();
125 createAssociation(itemIface->get_object_path());
126 }
127
James Feist45772222019-09-27 10:38:08 -0700128 std::shared_ptr<sdbusplus::asio::dbus_interface> itemIface;
129 std::shared_ptr<sdbusplus::asio::dbus_interface> operationalIface;
James Feist244f3232019-09-27 15:15:14 -0700130 std::shared_ptr<sdbusplus::asio::dbus_interface> rebuildingIface;
James Feist0b236ab2019-10-02 09:09:16 -0700131 std::shared_ptr<sdbusplus::asio::dbus_interface> associationIface;
James Feistdb2e0e72019-10-07 16:34:06 -0700132
133 // if something else doesn't expose a driveInterface for this, we need to
134 // export it ourselves
135 std::shared_ptr<sdbusplus::asio::dbus_interface> driveIface;
136
James Feist45772222019-09-27 10:38:08 -0700137 bool isNvme;
138};
139
Feist, Jamesc95cf672019-08-29 16:10:35 -0700140struct Backplane
141{
142
James Feist45772222019-09-27 10:38:08 -0700143 Backplane(size_t busIn, size_t addressIn, size_t backplaneIndexIn,
144 const std::string& nameIn) :
145 bus(busIn),
146 address(addressIn), backplaneIndex(backplaneIndexIn - 1), name(nameIn),
James Feist0b236ab2019-10-02 09:09:16 -0700147 timer(std::make_shared<boost::asio::steady_timer>(io)),
148 muxes(std::make_shared<std::vector<Mux>>())
Feist, Jamesc95cf672019-08-29 16:10:35 -0700149 {
150 }
Feist, Jamesc95cf672019-08-29 16:10:35 -0700151 void run()
152 {
153 file = open(("/dev/i2c-" + std::to_string(bus)).c_str(), O_RDWR);
154 if (file < 0)
155 {
156 std::cerr << "unable to open bus " << bus << "\n";
157 return;
158 }
159
160 if (ioctl(file, I2C_SLAVE_FORCE, address) < 0)
161 {
162 std::cerr << "unable to set address to " << address << "\n";
163 return;
164 }
165
James Feist45772222019-09-27 10:38:08 -0700166 if (!getPresent())
167 {
168 std::cerr << "Cannot detect CPLD\n";
169 return;
170 }
171
172 getBootVer(bootVer);
173 getFPGAVer(fpgaVer);
174 getSecurityRev(securityRev);
175 std::string dbusName = boost::replace_all_copy(name, " ", "_");
Feist, Jamesc95cf672019-08-29 16:10:35 -0700176 hsbpItemIface = objServer.add_interface(
James Feist45772222019-09-27 10:38:08 -0700177 "/xyz/openbmc_project/inventory/item/hsbp/" + dbusName,
Feist, Jamesc95cf672019-08-29 16:10:35 -0700178 inventory::interface);
James Feist45772222019-09-27 10:38:08 -0700179 hsbpItemIface->register_property("Present", true);
Feist, Jamesc95cf672019-08-29 16:10:35 -0700180 hsbpItemIface->register_property("PrettyName", name);
181 hsbpItemIface->initialize();
182
James Feist45772222019-09-27 10:38:08 -0700183 versionIface =
184 objServer.add_interface(hsbpItemIface->get_object_path(),
185 "xyz.openbmc_project.Software.Version");
186 versionIface->register_property("Version", zeroPad(bootVer) + "." +
187 zeroPad(fpgaVer) + "." +
188 zeroPad(securityRev));
189 versionIface->register_property(
190 "Purpose",
191 std::string(
192 "xyz.openbmc_project.Software.Version.VersionPurpose.HSBP"));
193 versionIface->initialize();
194 getPresence(presence);
195 getIFDET(ifdet);
196
197 createDrives();
198
199 runTimer();
200 }
201
202 void runTimer()
203 {
204 timer->expires_after(std::chrono::seconds(scanRateSeconds));
205 timer->async_wait([this](boost::system::error_code ec) {
206 if (ec == boost::asio::error::operation_aborted)
207 {
208 // we're being destroyed
209 return;
210 }
211 else if (ec)
212 {
213 std::cerr << "timer error " << ec.message() << "\n";
214 return;
215 }
216 uint8_t curPresence = 0;
217 uint8_t curIFDET = 0;
218 uint8_t curFailed = 0;
James Feist244f3232019-09-27 15:15:14 -0700219 uint8_t curRebuild = 0;
James Feist45772222019-09-27 10:38:08 -0700220
221 getPresence(curPresence);
222 getIFDET(curIFDET);
223 getFailed(curFailed);
James Feist244f3232019-09-27 15:15:14 -0700224 getRebuild(curRebuild);
James Feist45772222019-09-27 10:38:08 -0700225
226 if (curPresence != presence || curIFDET != ifdet ||
James Feist244f3232019-09-27 15:15:14 -0700227 curFailed != failed || curRebuild != rebuilding)
James Feist45772222019-09-27 10:38:08 -0700228 {
229 presence = curPresence;
230 ifdet = curIFDET;
231 failed = curFailed;
James Feist244f3232019-09-27 15:15:14 -0700232 rebuilding = curRebuild;
James Feist45772222019-09-27 10:38:08 -0700233 updateDrives();
234 }
235 runTimer();
236 });
237 }
238
239 void createDrives()
240 {
241 uint8_t nvme = ifdet ^ presence;
242 for (size_t ii = 0; ii < maxDrives; ii++)
Feist, Jamesc95cf672019-08-29 16:10:35 -0700243 {
James Feist45772222019-09-27 10:38:08 -0700244 bool isNvme = nvme & (1 << ii);
245 bool isPresent = isNvme || (presence & (1 << ii));
246 bool isFailed = !isPresent || failed & (1 << ii);
James Feist244f3232019-09-27 15:15:14 -0700247 bool isRebuilding = !isPresent && (rebuilding & (1 << ii));
James Feist45772222019-09-27 10:38:08 -0700248
249 // +1 to convert from 0 based to 1 based
250 size_t driveIndex = (backplaneIndex * maxDrives) + ii + 1;
James Feist244f3232019-09-27 15:15:14 -0700251 drives.emplace_back(driveIndex, isPresent, !isFailed, isNvme,
252 isRebuilding);
Feist, Jamesc95cf672019-08-29 16:10:35 -0700253 }
254 }
255
James Feist45772222019-09-27 10:38:08 -0700256 void updateDrives()
Feist, Jamesc95cf672019-08-29 16:10:35 -0700257 {
James Feist45772222019-09-27 10:38:08 -0700258
259 uint8_t nvme = ifdet ^ presence;
260 for (size_t ii = 0; ii < maxDrives; ii++)
Feist, Jamesc95cf672019-08-29 16:10:35 -0700261 {
James Feist45772222019-09-27 10:38:08 -0700262 bool isNvme = nvme & (1 << ii);
263 bool isPresent = isNvme || (presence & (1 << ii));
James Feist244f3232019-09-27 15:15:14 -0700264 bool isFailed = !isPresent || (failed & (1 << ii));
265 bool isRebuilding = isPresent && (rebuilding & (1 << ii));
James Feist45772222019-09-27 10:38:08 -0700266
267 Drive& drive = drives[ii];
268 drive.isNvme = isNvme;
269 drive.itemIface->set_property("Present", isPresent);
270 drive.operationalIface->set_property("Functional", !isFailed);
James Feist244f3232019-09-27 15:15:14 -0700271 drive.rebuildingIface->set_property("Rebuilding", isRebuilding);
Feist, Jamesc95cf672019-08-29 16:10:35 -0700272 }
James Feist45772222019-09-27 10:38:08 -0700273 }
274
275 bool getPresent()
276 {
277 present = i2c_smbus_read_byte(file) >= 0;
Feist, Jamesc95cf672019-08-29 16:10:35 -0700278 return present;
279 }
James Feist45772222019-09-27 10:38:08 -0700280
281 bool getTypeID(uint8_t& val)
282 {
283 constexpr uint8_t addr = 2;
284 int ret = i2c_smbus_read_byte_data(file, addr);
285 if (ret < 0)
286 {
287 std::cerr << "Error " << __FUNCTION__ << "\n";
288 return false;
289 }
290 val = static_cast<uint8_t>(ret);
291 return true;
292 }
293
294 bool getBootVer(uint8_t& val)
295 {
296 constexpr uint8_t addr = 3;
297 int ret = i2c_smbus_read_byte_data(file, addr);
298 if (ret < 0)
299 {
300 std::cerr << "Error " << __FUNCTION__ << "\n";
301 return false;
302 }
303 val = static_cast<uint8_t>(ret);
304 return true;
305 }
306
307 bool getFPGAVer(uint8_t& val)
308 {
309 constexpr uint8_t addr = 4;
310 int ret = i2c_smbus_read_byte_data(file, addr);
311 if (ret < 0)
312 {
313 std::cerr << "Error " << __FUNCTION__ << "\n";
314 return false;
315 }
316 val = static_cast<uint8_t>(ret);
317 return true;
318 }
319
320 bool getSecurityRev(uint8_t& val)
321 {
322 constexpr uint8_t addr = 5;
323 int ret = i2c_smbus_read_byte_data(file, addr);
324 if (ret < 0)
325 {
326 std::cerr << "Error " << __FUNCTION__ << "\n";
327 return false;
328 }
329 val = static_cast<uint8_t>(ret);
330 return true;
331 }
332
333 bool getPresence(uint8_t& val)
334 {
335 // NVMe drives do not assert PRSNTn, and as such do not get reported as
336 // PRESENT in this register
337
338 constexpr uint8_t addr = 8;
339
340 int ret = i2c_smbus_read_byte_data(file, addr);
341 if (ret < 0)
342 {
343 std::cerr << "Error " << __FUNCTION__ << "\n";
344 return false;
345 }
346 // presence is inverted
347 val = static_cast<uint8_t>(~ret);
348 return true;
349 }
350
351 bool getIFDET(uint8_t& val)
352 {
353 // This register is a bitmap of parallel GPIO pins connected to the
354 // IFDETn pin of a drive slot. SATA, SAS, and NVMe drives all assert
355 // IFDETn low when they are inserted into the HSBP.This register, in
356 // combination with the PRESENCE register, are used by the BMC to detect
357 // the presence of NVMe drives.
358
359 constexpr uint8_t addr = 9;
360
361 int ret = i2c_smbus_read_byte_data(file, addr);
362 if (ret < 0)
363 {
364 std::cerr << "Error " << __FUNCTION__ << "\n";
365 return false;
366 }
367 // ifdet is inverted
368 val = static_cast<uint8_t>(~ret);
369 return true;
370 }
371
372 bool getFailed(uint8_t& val)
373 {
374 constexpr uint8_t addr = 0xC;
375 int ret = i2c_smbus_read_byte_data(file, addr);
376 if (ret < 0)
377 {
378 std::cerr << "Error " << __FUNCTION__ << "\n";
379 return false;
380 }
381 val = static_cast<uint8_t>(ret);
382 return true;
383 }
384
385 bool getRebuild(uint8_t& val)
386 {
387 constexpr uint8_t addr = 0xD;
388 int ret = i2c_smbus_read_byte_data(file, addr);
389 if (ret < 0)
390 {
391 std::cerr << "Error " << __FUNCTION__ << "\n";
392 return false;
393 }
394 val = static_cast<uint8_t>(ret);
395 return true;
396 }
397
Feist, Jamesc95cf672019-08-29 16:10:35 -0700398 ~Backplane()
399 {
400 objServer.remove_interface(hsbpItemIface);
James Feist45772222019-09-27 10:38:08 -0700401 objServer.remove_interface(versionIface);
Feist, Jamesc95cf672019-08-29 16:10:35 -0700402 if (file >= 0)
403 {
404 close(file);
405 }
406 }
407
408 size_t bus;
409 size_t address;
James Feist45772222019-09-27 10:38:08 -0700410 size_t backplaneIndex;
Feist, Jamesc95cf672019-08-29 16:10:35 -0700411 std::string name;
James Feist45772222019-09-27 10:38:08 -0700412 std::shared_ptr<boost::asio::steady_timer> timer;
413 bool present = false;
414 uint8_t typeId = 0;
415 uint8_t bootVer = 0;
416 uint8_t fpgaVer = 0;
417 uint8_t securityRev = 0;
418 uint8_t funSupported = 0;
419 uint8_t presence = 0;
420 uint8_t ifdet = 0;
421 uint8_t failed = 0;
James Feist244f3232019-09-27 15:15:14 -0700422 uint8_t rebuilding = 0;
James Feist45772222019-09-27 10:38:08 -0700423
424 int file = -1;
425
Feist, Jamesc95cf672019-08-29 16:10:35 -0700426 std::string type;
427
428 std::shared_ptr<sdbusplus::asio::dbus_interface> hsbpItemIface;
James Feist45772222019-09-27 10:38:08 -0700429 std::shared_ptr<sdbusplus::asio::dbus_interface> versionIface;
430
431 std::vector<Drive> drives;
James Feist0b236ab2019-10-02 09:09:16 -0700432 std::shared_ptr<std::vector<Mux>> muxes;
Feist, Jamesc95cf672019-08-29 16:10:35 -0700433};
434
435std::unordered_map<std::string, Backplane> backplanes;
436
James Feistdb2e0e72019-10-07 16:34:06 -0700437void createDriveInterfaces(size_t referenceCount)
438{
439 if (referenceCount != 1)
440 {
441 return;
442 }
443 for (auto& [name, backplane] : backplanes)
444 {
445 for (Drive& drive : backplane.drives)
446 {
447 drive.createDriveIface();
448 }
449 }
450}
451
James Feist0b236ab2019-10-02 09:09:16 -0700452void updateAssociations()
453{
454 constexpr const char* driveType =
455 "xyz.openbmc_project.Inventory.Item.Drive";
456
457 conn->async_method_call(
458 [](const boost::system::error_code ec, const GetSubTreeType& subtree) {
459 if (ec)
460 {
461 std::cerr << "Error contacting mapper " << ec.message() << "\n";
462 return;
463 }
464 for (const auto& [path, objDict] : subtree)
465 {
466 if (objDict.empty())
467 {
468 continue;
469 }
470
471 const std::string& owner = objDict.begin()->first;
James Feistdb2e0e72019-10-07 16:34:06 -0700472 // we export this interface too
473 if (owner == busName)
474 {
475 continue;
476 }
477 std::shared_ptr<bool> referenceCount = std::make_shared<bool>();
James Feist0b236ab2019-10-02 09:09:16 -0700478 conn->async_method_call(
James Feistdb2e0e72019-10-07 16:34:06 -0700479 [path, referenceCount](
480 const boost::system::error_code ec2,
481 const boost::container::flat_map<
482 std::string, std::variant<uint64_t>>& values) {
James Feist0b236ab2019-10-02 09:09:16 -0700483 if (ec2)
484 {
485 std::cerr << "Error Getting Config "
486 << ec2.message() << " " << __FUNCTION__
487 << "\n";
James Feistdb2e0e72019-10-07 16:34:06 -0700488 createDriveInterfaces(referenceCount.use_count());
James Feist0b236ab2019-10-02 09:09:16 -0700489 return;
490 }
491 auto findBus = values.find("Bus");
492 auto findIndex = values.find("Index");
493
494 if (findBus == values.end() ||
495 findIndex == values.end())
496 {
497 std::cerr << "Illegal interface at " << path
498 << "\n";
James Feistdb2e0e72019-10-07 16:34:06 -0700499 createDriveInterfaces(referenceCount.use_count());
James Feist0b236ab2019-10-02 09:09:16 -0700500 return;
501 }
502
503 size_t muxBus = static_cast<size_t>(
504 std::get<uint64_t>(findBus->second));
505 size_t driveIndex = static_cast<size_t>(
506 std::get<uint64_t>(findIndex->second));
507 std::filesystem::path muxPath =
508 "/sys/bus/i2c/devices/i2c-" +
509 std::to_string(muxBus) + "/mux_device";
510 if (!std::filesystem::is_symlink(muxPath))
511 {
512 std::cerr << path << " mux does not exist\n";
James Feistdb2e0e72019-10-07 16:34:06 -0700513 createDriveInterfaces(referenceCount.use_count());
James Feist0b236ab2019-10-02 09:09:16 -0700514 return;
515 }
516
517 // we should be getting something of the form 7-0052 for
518 // bus 7 addr 52
519 std::string fname =
520 std::filesystem::read_symlink(muxPath).filename();
521 auto findDash = fname.find('-');
522
523 if (findDash == std::string::npos ||
524 findDash + 1 >= fname.size())
525 {
526 std::cerr << path << " mux path invalid\n";
James Feistdb2e0e72019-10-07 16:34:06 -0700527 createDriveInterfaces(referenceCount.use_count());
James Feist0b236ab2019-10-02 09:09:16 -0700528 return;
529 }
530
531 std::string busStr = fname.substr(0, findDash);
532 std::string muxStr = fname.substr(findDash + 1);
533
534 size_t bus = static_cast<size_t>(std::stoi(busStr));
535 size_t addr =
536 static_cast<size_t>(std::stoi(muxStr, nullptr, 16));
537 Backplane* parent = nullptr;
538 for (auto& [name, backplane] : backplanes)
539 {
540 for (const Mux& mux : *(backplane.muxes))
541 {
542 if (bus == mux.bus && addr == mux.address)
543 {
544 parent = &backplane;
545 break;
546 }
547 }
548 }
549 if (parent == nullptr)
550 {
551 std::cerr << "Failed to find mux at bus " << bus
552 << ", addr " << addr << "\n";
James Feistdb2e0e72019-10-07 16:34:06 -0700553 createDriveInterfaces(referenceCount.use_count());
James Feist0b236ab2019-10-02 09:09:16 -0700554 return;
555 }
556 if (parent->drives.size() <= driveIndex)
557 {
558
559 std::cerr << "Illegal drive index at " << path
560 << " " << driveIndex << "\n";
James Feistdb2e0e72019-10-07 16:34:06 -0700561 createDriveInterfaces(referenceCount.use_count());
James Feist0b236ab2019-10-02 09:09:16 -0700562 return;
563 }
564 Drive& drive = parent->drives[driveIndex];
James Feistdb2e0e72019-10-07 16:34:06 -0700565 drive.removeDriveIface();
James Feist0b236ab2019-10-02 09:09:16 -0700566 drive.createAssociation(path);
James Feistdb2e0e72019-10-07 16:34:06 -0700567 createDriveInterfaces(referenceCount.use_count());
James Feist0b236ab2019-10-02 09:09:16 -0700568 },
569 owner, path, "org.freedesktop.DBus.Properties", "GetAll",
570 "xyz.openbmc_project.Inventory.Item.Drive");
571 }
572 },
573 mapper::busName, mapper::path, mapper::interface, mapper::subtree, "/",
574 0, std::array<const char*, 1>{driveType});
575}
576
577void populateMuxes(std::shared_ptr<std::vector<Mux>> muxes,
578 std::string& rootPath)
579{
580 const static std::array<const std::string, 4> muxTypes = {
581 "xyz.openbmc_project.Configuration.PCA9543Mux",
582 "xyz.openbmc_project.Configuration.PCA9544Mux",
583 "xyz.openbmc_project.Configuration.PCA9545Mux",
584 "xyz.openbmc_project.Configuration.PCA9546Mux"};
585 conn->async_method_call(
586 [muxes](const boost::system::error_code ec,
587 const GetSubTreeType& subtree) {
588 if (ec)
589 {
590 std::cerr << "Error contacting mapper " << ec.message() << "\n";
591 return;
592 }
593 std::shared_ptr<std::function<void()>> callback =
594 std::make_shared<std::function<void()>>(
595 []() { updateAssociations(); });
596 for (const auto& [path, objDict] : subtree)
597 {
598 if (objDict.empty() || objDict.begin()->second.empty())
599 {
600 continue;
601 }
602
603 const std::string& owner = objDict.begin()->first;
604 const std::vector<std::string>& interfaces =
605 objDict.begin()->second;
606
607 const std::string* interface = nullptr;
608 for (const std::string& iface : interfaces)
609 {
610 if (std::find(muxTypes.begin(), muxTypes.end(), iface) !=
611 muxTypes.end())
612 {
613 interface = &iface;
614 break;
615 }
616 }
617 if (interface == nullptr)
618 {
619 std::cerr << "Cannot get mux type\n";
620 continue;
621 }
622
623 conn->async_method_call(
624 [path, muxes, callback](
625 const boost::system::error_code ec2,
626 const boost::container::flat_map<
627 std::string, std::variant<uint64_t>>& values) {
628 if (ec2)
629 {
630 std::cerr << "Error Getting Config "
631 << ec2.message() << " " << __FUNCTION__
632 << "\n";
633 return;
634 }
635 auto findBus = values.find("Bus");
636 auto findAddress = values.find("Address");
637 if (findBus == values.end() ||
638 findAddress == values.end())
639 {
640 std::cerr << "Illegal configuration at " << path
641 << "\n";
642 return;
643 }
644 size_t bus = static_cast<size_t>(
645 std::get<uint64_t>(findBus->second));
646 size_t address = static_cast<size_t>(
647 std::get<uint64_t>(findAddress->second));
648 muxes->emplace_back(bus, address);
649 if (callback.use_count() == 1)
650 {
651 (*callback)();
652 }
653 },
654 owner, path, "org.freedesktop.DBus.Properties", "GetAll",
655 *interface);
656 }
657 },
658 mapper::busName, mapper::path, mapper::interface, mapper::subtree,
659 rootPath, 1, muxTypes);
660}
661
Feist, Jamesc95cf672019-08-29 16:10:35 -0700662void populate()
663{
664 conn->async_method_call(
665 [](const boost::system::error_code ec, const GetSubTreeType& subtree) {
666 if (ec)
667 {
668 std::cerr << "Error contacting mapper " << ec.message() << "\n";
669 return;
670 }
671 for (const auto& [path, objDict] : subtree)
672 {
673 if (objDict.empty())
674 {
675 continue;
676 }
677
678 const std::string& owner = objDict.begin()->first;
679 conn->async_method_call(
680 [path](const boost::system::error_code ec2,
681 const boost::container::flat_map<
682 std::string, BasicVariantType>& resp) {
683 if (ec2)
684 {
685 std::cerr << "Error Getting Config "
686 << ec2.message() << "\n";
687 return;
688 }
689 backplanes.clear();
690 std::optional<size_t> bus;
691 std::optional<size_t> address;
James Feist45772222019-09-27 10:38:08 -0700692 std::optional<size_t> backplaneIndex;
Feist, Jamesc95cf672019-08-29 16:10:35 -0700693 std::optional<std::string> name;
694 for (const auto& [key, value] : resp)
695 {
696 if (key == "Bus")
697 {
698 bus = std::get<uint64_t>(value);
699 }
700 else if (key == "Address")
701 {
702 address = std::get<uint64_t>(value);
703 }
James Feist45772222019-09-27 10:38:08 -0700704 else if (key == "Index")
705 {
706 backplaneIndex = std::get<uint64_t>(value);
707 }
Feist, Jamesc95cf672019-08-29 16:10:35 -0700708 else if (key == "Name")
709 {
710 name = std::get<std::string>(value);
711 }
712 }
James Feist45772222019-09-27 10:38:08 -0700713 if (!bus || !address || !name || !backplaneIndex)
Feist, Jamesc95cf672019-08-29 16:10:35 -0700714 {
715 std::cerr << "Illegal configuration at " << path
716 << "\n";
717 return;
718 }
James Feist0b236ab2019-10-02 09:09:16 -0700719 std::string parentPath =
720 std::filesystem::path(path).parent_path();
Feist, Jamesc95cf672019-08-29 16:10:35 -0700721 const auto& [backplane, status] = backplanes.emplace(
James Feist45772222019-09-27 10:38:08 -0700722 *name,
723 Backplane(*bus, *address, *backplaneIndex, *name));
Feist, Jamesc95cf672019-08-29 16:10:35 -0700724 backplane->second.run();
James Feist0b236ab2019-10-02 09:09:16 -0700725 populateMuxes(backplane->second.muxes, parentPath);
Feist, Jamesc95cf672019-08-29 16:10:35 -0700726 },
727 owner, path, "org.freedesktop.DBus.Properties", "GetAll",
728 configType);
729 }
730 },
731 mapper::busName, mapper::path, mapper::interface, mapper::subtree, "/",
732 0, std::array<const char*, 1>{configType});
733}
734
735int main()
736{
737 boost::asio::steady_timer callbackTimer(io);
738
James Feistdb2e0e72019-10-07 16:34:06 -0700739 conn->request_name(busName);
Feist, Jamesc95cf672019-08-29 16:10:35 -0700740
741 sdbusplus::bus::match::match match(
742 *conn,
743 "type='signal',member='PropertiesChanged',arg0='" +
744 std::string(configType) + "'",
745 [&callbackTimer](sdbusplus::message::message&) {
746 callbackTimer.expires_after(std::chrono::seconds(2));
747 callbackTimer.async_wait([](const boost::system::error_code ec) {
748 if (ec == boost::asio::error::operation_aborted)
749 {
750 // timer was restarted
751 return;
752 }
753 else if (ec)
754 {
755 std::cerr << "Timer error" << ec.message() << "\n";
756 return;
757 }
758 populate();
759 });
760 });
761
James Feist0b236ab2019-10-02 09:09:16 -0700762 sdbusplus::bus::match::match drive(
763 *conn,
764 "type='signal',member='PropertiesChanged',arg0='xyz.openbmc_project."
765 "Inventory.Item.Drive'",
James Feistdb2e0e72019-10-07 16:34:06 -0700766 [&callbackTimer](sdbusplus::message::message& message) {
James Feist0b236ab2019-10-02 09:09:16 -0700767 callbackTimer.expires_after(std::chrono::seconds(2));
James Feistdb2e0e72019-10-07 16:34:06 -0700768 if (message.get_sender() == conn->get_unique_name())
769 {
770 return;
771 }
James Feist0b236ab2019-10-02 09:09:16 -0700772 callbackTimer.async_wait([](const boost::system::error_code ec) {
773 if (ec == boost::asio::error::operation_aborted)
774 {
775 // timer was restarted
776 return;
777 }
778 else if (ec)
779 {
780 std::cerr << "Timer error" << ec.message() << "\n";
781 return;
782 }
783 populate();
784 });
785 });
786
Feist, Jamesc95cf672019-08-29 16:10:35 -0700787 io.post([]() { populate(); });
788 io.run();
789}