blob: 08874e9828ee23faa6de7483a2756faeff22d18b [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 Feist09dd2312019-10-09 09:29:03 -070060
61enum class BlinkPattern : uint8_t
62{
63 off = 0x0,
64 error = 0x2,
65 terminate = 0x3
66};
67
68struct Led : std::enable_shared_from_this<Led>
69{
70 // led pattern addresses start at 0x10
71 Led(const std::string& path, size_t index, int fd) :
72 address(static_cast<uint8_t>(index + 0x10)), file(fd),
73 ledInterface(objServer.add_interface(path, ledGroup::interface))
74 {
75 if (index >= maxDrives)
76 {
77 throw std::runtime_error("Invalid drive index");
78 }
79
80 if (!set(BlinkPattern::off))
81 {
82 std::cerr << "Cannot initialize LED " << path << "\n";
83 }
84 }
85
86 // this has to be called outside the constructor for shared_from_this to
87 // work
88 void createInterface(void)
89 {
90 std::shared_ptr<Led> self = shared_from_this();
91
92 ledInterface->register_property(
93 ledGroup::asserted, false, [self](const bool req, bool& val) {
94 if (req == val)
95 {
96 return 1;
97 }
98 BlinkPattern pattern =
99 req ? BlinkPattern::error : BlinkPattern::terminate;
100 if (!self->set(pattern))
101 {
102 throw std::runtime_error("Cannot set blink pattern");
103 }
104 val = req;
105 return 1;
106 });
107 ledInterface->initialize();
108 }
109
110 virtual ~Led()
111 {
112 objServer.remove_interface(ledInterface);
113 }
114
115 bool set(BlinkPattern pattern)
116 {
117 int ret = i2c_smbus_write_byte_data(file, address,
118 static_cast<uint8_t>(pattern));
119 return ret >= 0;
120 }
121
122 uint8_t address;
123 int file;
124 std::shared_ptr<sdbusplus::asio::dbus_interface> ledInterface;
125};
126
James Feist45772222019-09-27 10:38:08 -0700127struct Drive
128{
James Feist244f3232019-09-27 15:15:14 -0700129 Drive(size_t driveIndex, bool isPresent, bool isOperational, bool nvme,
130 bool rebuilding) :
James Feist45772222019-09-27 10:38:08 -0700131 isNvme(nvme)
132 {
133 constexpr const char* basePath =
134 "/xyz/openbmc_project/inventory/item/drive/Drive_";
135 itemIface = objServer.add_interface(
136 basePath + std::to_string(driveIndex), inventory::interface);
137 itemIface->register_property("Present", isPresent);
138 itemIface->register_property("PrettyName",
139 "Drive " + std::to_string(driveIndex));
140 itemIface->initialize();
141 operationalIface = objServer.add_interface(
James Feist244f3232019-09-27 15:15:14 -0700142 itemIface->get_object_path(),
James Feist45772222019-09-27 10:38:08 -0700143 "xyz.openbmc_project.State.Decorator.OperationalStatus");
144 operationalIface->register_property("Functional", isOperational);
145 operationalIface->initialize();
James Feist244f3232019-09-27 15:15:14 -0700146 rebuildingIface = objServer.add_interface(
147 itemIface->get_object_path(), "xyz.openbmc_project.State.Drive");
148 rebuildingIface->register_property("Rebuilding", rebuilding);
149 rebuildingIface->initialize();
James Feist45772222019-09-27 10:38:08 -0700150 }
James Feist09dd2312019-10-09 09:29:03 -0700151 virtual ~Drive()
James Feist45772222019-09-27 10:38:08 -0700152 {
153 objServer.remove_interface(itemIface);
154 objServer.remove_interface(operationalIface);
James Feist244f3232019-09-27 15:15:14 -0700155 objServer.remove_interface(rebuildingIface);
James Feist0b236ab2019-10-02 09:09:16 -0700156 objServer.remove_interface(associationIface);
James Feistdb2e0e72019-10-07 16:34:06 -0700157 objServer.remove_interface(driveIface);
James Feist0b236ab2019-10-02 09:09:16 -0700158 }
159
160 void createAssociation(const std::string& path)
161 {
162 if (associationIface != nullptr)
163 {
164 return;
165 }
166 associationIface = objServer.add_interface(
167 itemIface->get_object_path(),
168 "xyz.openbmc_project.Association.Definitions");
169 std::vector<Association> associations;
170 associations.emplace_back("inventory", "drive", path);
171 associationIface->register_property("Associations", associations);
172 associationIface->initialize();
James Feist45772222019-09-27 10:38:08 -0700173 }
174
James Feistdb2e0e72019-10-07 16:34:06 -0700175 void removeDriveIface()
176 {
177 objServer.remove_interface(driveIface);
178 }
179
180 void createDriveIface()
181 {
182 if (associationIface != nullptr || driveIface != nullptr)
183 {
184 // this shouldn't be used if we found another provider of the drive
185 // interface, or if we already created one
186 return;
187 }
188 driveIface =
189 objServer.add_interface(itemIface->get_object_path(),
190 "xyz.openbmc_project.Inventory.Item.Drive");
191 driveIface->initialize();
192 createAssociation(itemIface->get_object_path());
193 }
194
James Feist45772222019-09-27 10:38:08 -0700195 std::shared_ptr<sdbusplus::asio::dbus_interface> itemIface;
196 std::shared_ptr<sdbusplus::asio::dbus_interface> operationalIface;
James Feist244f3232019-09-27 15:15:14 -0700197 std::shared_ptr<sdbusplus::asio::dbus_interface> rebuildingIface;
James Feist0b236ab2019-10-02 09:09:16 -0700198 std::shared_ptr<sdbusplus::asio::dbus_interface> associationIface;
James Feistdb2e0e72019-10-07 16:34:06 -0700199
200 // if something else doesn't expose a driveInterface for this, we need to
201 // export it ourselves
202 std::shared_ptr<sdbusplus::asio::dbus_interface> driveIface;
203
James Feist45772222019-09-27 10:38:08 -0700204 bool isNvme;
205};
206
Feist, Jamesc95cf672019-08-29 16:10:35 -0700207struct Backplane
208{
209
James Feist45772222019-09-27 10:38:08 -0700210 Backplane(size_t busIn, size_t addressIn, size_t backplaneIndexIn,
211 const std::string& nameIn) :
212 bus(busIn),
213 address(addressIn), backplaneIndex(backplaneIndexIn - 1), name(nameIn),
James Feist0b236ab2019-10-02 09:09:16 -0700214 timer(std::make_shared<boost::asio::steady_timer>(io)),
215 muxes(std::make_shared<std::vector<Mux>>())
Feist, Jamesc95cf672019-08-29 16:10:35 -0700216 {
217 }
Feist, Jamesc95cf672019-08-29 16:10:35 -0700218 void run()
219 {
James Feist09dd2312019-10-09 09:29:03 -0700220 file = open(("/dev/i2c-" + std::to_string(bus)).c_str(),
221 O_RDWR | O_CLOEXEC);
Feist, Jamesc95cf672019-08-29 16:10:35 -0700222 if (file < 0)
223 {
224 std::cerr << "unable to open bus " << bus << "\n";
225 return;
226 }
227
228 if (ioctl(file, I2C_SLAVE_FORCE, address) < 0)
229 {
230 std::cerr << "unable to set address to " << address << "\n";
231 return;
232 }
233
James Feist45772222019-09-27 10:38:08 -0700234 if (!getPresent())
235 {
236 std::cerr << "Cannot detect CPLD\n";
237 return;
238 }
239
240 getBootVer(bootVer);
241 getFPGAVer(fpgaVer);
242 getSecurityRev(securityRev);
243 std::string dbusName = boost::replace_all_copy(name, " ", "_");
Feist, Jamesc95cf672019-08-29 16:10:35 -0700244 hsbpItemIface = objServer.add_interface(
James Feist45772222019-09-27 10:38:08 -0700245 "/xyz/openbmc_project/inventory/item/hsbp/" + dbusName,
Feist, Jamesc95cf672019-08-29 16:10:35 -0700246 inventory::interface);
James Feist45772222019-09-27 10:38:08 -0700247 hsbpItemIface->register_property("Present", true);
Feist, Jamesc95cf672019-08-29 16:10:35 -0700248 hsbpItemIface->register_property("PrettyName", name);
249 hsbpItemIface->initialize();
250
James Feist45772222019-09-27 10:38:08 -0700251 versionIface =
252 objServer.add_interface(hsbpItemIface->get_object_path(),
253 "xyz.openbmc_project.Software.Version");
254 versionIface->register_property("Version", zeroPad(bootVer) + "." +
255 zeroPad(fpgaVer) + "." +
256 zeroPad(securityRev));
257 versionIface->register_property(
258 "Purpose",
259 std::string(
260 "xyz.openbmc_project.Software.Version.VersionPurpose.HSBP"));
261 versionIface->initialize();
262 getPresence(presence);
263 getIFDET(ifdet);
264
265 createDrives();
266
267 runTimer();
268 }
269
270 void runTimer()
271 {
272 timer->expires_after(std::chrono::seconds(scanRateSeconds));
273 timer->async_wait([this](boost::system::error_code ec) {
274 if (ec == boost::asio::error::operation_aborted)
275 {
276 // we're being destroyed
277 return;
278 }
279 else if (ec)
280 {
281 std::cerr << "timer error " << ec.message() << "\n";
282 return;
283 }
284 uint8_t curPresence = 0;
285 uint8_t curIFDET = 0;
286 uint8_t curFailed = 0;
James Feist244f3232019-09-27 15:15:14 -0700287 uint8_t curRebuild = 0;
James Feist45772222019-09-27 10:38:08 -0700288
289 getPresence(curPresence);
290 getIFDET(curIFDET);
291 getFailed(curFailed);
James Feist244f3232019-09-27 15:15:14 -0700292 getRebuild(curRebuild);
James Feist45772222019-09-27 10:38:08 -0700293
294 if (curPresence != presence || curIFDET != ifdet ||
James Feist244f3232019-09-27 15:15:14 -0700295 curFailed != failed || curRebuild != rebuilding)
James Feist45772222019-09-27 10:38:08 -0700296 {
297 presence = curPresence;
298 ifdet = curIFDET;
299 failed = curFailed;
James Feist244f3232019-09-27 15:15:14 -0700300 rebuilding = curRebuild;
James Feist45772222019-09-27 10:38:08 -0700301 updateDrives();
302 }
303 runTimer();
304 });
305 }
306
307 void createDrives()
308 {
309 uint8_t nvme = ifdet ^ presence;
310 for (size_t ii = 0; ii < maxDrives; ii++)
Feist, Jamesc95cf672019-08-29 16:10:35 -0700311 {
James Feist45772222019-09-27 10:38:08 -0700312 bool isNvme = nvme & (1 << ii);
313 bool isPresent = isNvme || (presence & (1 << ii));
314 bool isFailed = !isPresent || failed & (1 << ii);
James Feist244f3232019-09-27 15:15:14 -0700315 bool isRebuilding = !isPresent && (rebuilding & (1 << ii));
James Feist45772222019-09-27 10:38:08 -0700316
317 // +1 to convert from 0 based to 1 based
318 size_t driveIndex = (backplaneIndex * maxDrives) + ii + 1;
James Feist09dd2312019-10-09 09:29:03 -0700319 Drive& drive = drives.emplace_back(driveIndex, isPresent, !isFailed,
320 isNvme, isRebuilding);
321 std::shared_ptr<Led> led = leds.emplace_back(std::make_shared<Led>(
322 drive.itemIface->get_object_path(), ii, file));
323 led->createInterface();
Feist, Jamesc95cf672019-08-29 16:10:35 -0700324 }
325 }
326
James Feist45772222019-09-27 10:38:08 -0700327 void updateDrives()
Feist, Jamesc95cf672019-08-29 16:10:35 -0700328 {
James Feist45772222019-09-27 10:38:08 -0700329
330 uint8_t nvme = ifdet ^ presence;
331 for (size_t ii = 0; ii < maxDrives; ii++)
Feist, Jamesc95cf672019-08-29 16:10:35 -0700332 {
James Feist45772222019-09-27 10:38:08 -0700333 bool isNvme = nvme & (1 << ii);
334 bool isPresent = isNvme || (presence & (1 << ii));
James Feist244f3232019-09-27 15:15:14 -0700335 bool isFailed = !isPresent || (failed & (1 << ii));
336 bool isRebuilding = isPresent && (rebuilding & (1 << ii));
James Feist45772222019-09-27 10:38:08 -0700337
338 Drive& drive = drives[ii];
339 drive.isNvme = isNvme;
340 drive.itemIface->set_property("Present", isPresent);
341 drive.operationalIface->set_property("Functional", !isFailed);
James Feist244f3232019-09-27 15:15:14 -0700342 drive.rebuildingIface->set_property("Rebuilding", isRebuilding);
Feist, Jamesc95cf672019-08-29 16:10:35 -0700343 }
James Feist45772222019-09-27 10:38:08 -0700344 }
345
346 bool getPresent()
347 {
348 present = i2c_smbus_read_byte(file) >= 0;
Feist, Jamesc95cf672019-08-29 16:10:35 -0700349 return present;
350 }
James Feist45772222019-09-27 10:38:08 -0700351
352 bool getTypeID(uint8_t& val)
353 {
354 constexpr uint8_t addr = 2;
355 int ret = i2c_smbus_read_byte_data(file, addr);
356 if (ret < 0)
357 {
358 std::cerr << "Error " << __FUNCTION__ << "\n";
359 return false;
360 }
361 val = static_cast<uint8_t>(ret);
362 return true;
363 }
364
365 bool getBootVer(uint8_t& val)
366 {
367 constexpr uint8_t addr = 3;
368 int ret = i2c_smbus_read_byte_data(file, addr);
369 if (ret < 0)
370 {
371 std::cerr << "Error " << __FUNCTION__ << "\n";
372 return false;
373 }
374 val = static_cast<uint8_t>(ret);
375 return true;
376 }
377
378 bool getFPGAVer(uint8_t& val)
379 {
380 constexpr uint8_t addr = 4;
381 int ret = i2c_smbus_read_byte_data(file, addr);
382 if (ret < 0)
383 {
384 std::cerr << "Error " << __FUNCTION__ << "\n";
385 return false;
386 }
387 val = static_cast<uint8_t>(ret);
388 return true;
389 }
390
391 bool getSecurityRev(uint8_t& val)
392 {
393 constexpr uint8_t addr = 5;
394 int ret = i2c_smbus_read_byte_data(file, addr);
395 if (ret < 0)
396 {
397 std::cerr << "Error " << __FUNCTION__ << "\n";
398 return false;
399 }
400 val = static_cast<uint8_t>(ret);
401 return true;
402 }
403
404 bool getPresence(uint8_t& val)
405 {
406 // NVMe drives do not assert PRSNTn, and as such do not get reported as
407 // PRESENT in this register
408
409 constexpr uint8_t addr = 8;
410
411 int ret = i2c_smbus_read_byte_data(file, addr);
412 if (ret < 0)
413 {
414 std::cerr << "Error " << __FUNCTION__ << "\n";
415 return false;
416 }
417 // presence is inverted
418 val = static_cast<uint8_t>(~ret);
419 return true;
420 }
421
422 bool getIFDET(uint8_t& val)
423 {
424 // This register is a bitmap of parallel GPIO pins connected to the
425 // IFDETn pin of a drive slot. SATA, SAS, and NVMe drives all assert
426 // IFDETn low when they are inserted into the HSBP.This register, in
427 // combination with the PRESENCE register, are used by the BMC to detect
428 // the presence of NVMe drives.
429
430 constexpr uint8_t addr = 9;
431
432 int ret = i2c_smbus_read_byte_data(file, addr);
433 if (ret < 0)
434 {
435 std::cerr << "Error " << __FUNCTION__ << "\n";
436 return false;
437 }
438 // ifdet is inverted
439 val = static_cast<uint8_t>(~ret);
440 return true;
441 }
442
443 bool getFailed(uint8_t& val)
444 {
445 constexpr uint8_t addr = 0xC;
446 int ret = i2c_smbus_read_byte_data(file, addr);
447 if (ret < 0)
448 {
449 std::cerr << "Error " << __FUNCTION__ << "\n";
450 return false;
451 }
452 val = static_cast<uint8_t>(ret);
453 return true;
454 }
455
456 bool getRebuild(uint8_t& val)
457 {
458 constexpr uint8_t addr = 0xD;
459 int ret = i2c_smbus_read_byte_data(file, addr);
460 if (ret < 0)
461 {
462 std::cerr << "Error " << __FUNCTION__ << "\n";
463 return false;
464 }
465 val = static_cast<uint8_t>(ret);
466 return true;
467 }
468
James Feist09dd2312019-10-09 09:29:03 -0700469 virtual ~Backplane()
Feist, Jamesc95cf672019-08-29 16:10:35 -0700470 {
471 objServer.remove_interface(hsbpItemIface);
James Feist45772222019-09-27 10:38:08 -0700472 objServer.remove_interface(versionIface);
Feist, Jamesc95cf672019-08-29 16:10:35 -0700473 if (file >= 0)
474 {
475 close(file);
476 }
477 }
478
479 size_t bus;
480 size_t address;
James Feist45772222019-09-27 10:38:08 -0700481 size_t backplaneIndex;
Feist, Jamesc95cf672019-08-29 16:10:35 -0700482 std::string name;
James Feist45772222019-09-27 10:38:08 -0700483 std::shared_ptr<boost::asio::steady_timer> timer;
484 bool present = false;
485 uint8_t typeId = 0;
486 uint8_t bootVer = 0;
487 uint8_t fpgaVer = 0;
488 uint8_t securityRev = 0;
489 uint8_t funSupported = 0;
490 uint8_t presence = 0;
491 uint8_t ifdet = 0;
492 uint8_t failed = 0;
James Feist244f3232019-09-27 15:15:14 -0700493 uint8_t rebuilding = 0;
James Feist45772222019-09-27 10:38:08 -0700494
495 int file = -1;
496
Feist, Jamesc95cf672019-08-29 16:10:35 -0700497 std::string type;
498
499 std::shared_ptr<sdbusplus::asio::dbus_interface> hsbpItemIface;
James Feist45772222019-09-27 10:38:08 -0700500 std::shared_ptr<sdbusplus::asio::dbus_interface> versionIface;
501
502 std::vector<Drive> drives;
James Feist09dd2312019-10-09 09:29:03 -0700503 std::vector<std::shared_ptr<Led>> leds;
James Feist0b236ab2019-10-02 09:09:16 -0700504 std::shared_ptr<std::vector<Mux>> muxes;
Feist, Jamesc95cf672019-08-29 16:10:35 -0700505};
506
507std::unordered_map<std::string, Backplane> backplanes;
508
James Feistdb2e0e72019-10-07 16:34:06 -0700509void createDriveInterfaces(size_t referenceCount)
510{
511 if (referenceCount != 1)
512 {
513 return;
514 }
515 for (auto& [name, backplane] : backplanes)
516 {
517 for (Drive& drive : backplane.drives)
518 {
519 drive.createDriveIface();
520 }
521 }
522}
523
James Feist0b236ab2019-10-02 09:09:16 -0700524void updateAssociations()
525{
526 constexpr const char* driveType =
527 "xyz.openbmc_project.Inventory.Item.Drive";
528
529 conn->async_method_call(
530 [](const boost::system::error_code ec, const GetSubTreeType& subtree) {
531 if (ec)
532 {
533 std::cerr << "Error contacting mapper " << ec.message() << "\n";
534 return;
535 }
536 for (const auto& [path, objDict] : subtree)
537 {
538 if (objDict.empty())
539 {
540 continue;
541 }
542
543 const std::string& owner = objDict.begin()->first;
James Feistdb2e0e72019-10-07 16:34:06 -0700544 // we export this interface too
545 if (owner == busName)
546 {
547 continue;
548 }
549 std::shared_ptr<bool> referenceCount = std::make_shared<bool>();
James Feist0b236ab2019-10-02 09:09:16 -0700550 conn->async_method_call(
James Feistdb2e0e72019-10-07 16:34:06 -0700551 [path, referenceCount](
552 const boost::system::error_code ec2,
553 const boost::container::flat_map<
554 std::string, std::variant<uint64_t>>& values) {
James Feist0b236ab2019-10-02 09:09:16 -0700555 if (ec2)
556 {
557 std::cerr << "Error Getting Config "
558 << ec2.message() << " " << __FUNCTION__
559 << "\n";
James Feistdb2e0e72019-10-07 16:34:06 -0700560 createDriveInterfaces(referenceCount.use_count());
James Feist0b236ab2019-10-02 09:09:16 -0700561 return;
562 }
563 auto findBus = values.find("Bus");
564 auto findIndex = values.find("Index");
565
566 if (findBus == values.end() ||
567 findIndex == values.end())
568 {
569 std::cerr << "Illegal interface at " << path
570 << "\n";
James Feistdb2e0e72019-10-07 16:34:06 -0700571 createDriveInterfaces(referenceCount.use_count());
James Feist0b236ab2019-10-02 09:09:16 -0700572 return;
573 }
574
575 size_t muxBus = static_cast<size_t>(
576 std::get<uint64_t>(findBus->second));
577 size_t driveIndex = static_cast<size_t>(
578 std::get<uint64_t>(findIndex->second));
579 std::filesystem::path muxPath =
580 "/sys/bus/i2c/devices/i2c-" +
581 std::to_string(muxBus) + "/mux_device";
582 if (!std::filesystem::is_symlink(muxPath))
583 {
584 std::cerr << path << " mux does not exist\n";
James Feistdb2e0e72019-10-07 16:34:06 -0700585 createDriveInterfaces(referenceCount.use_count());
James Feist0b236ab2019-10-02 09:09:16 -0700586 return;
587 }
588
589 // we should be getting something of the form 7-0052 for
590 // bus 7 addr 52
591 std::string fname =
592 std::filesystem::read_symlink(muxPath).filename();
593 auto findDash = fname.find('-');
594
595 if (findDash == std::string::npos ||
596 findDash + 1 >= fname.size())
597 {
598 std::cerr << path << " mux path invalid\n";
James Feistdb2e0e72019-10-07 16:34:06 -0700599 createDriveInterfaces(referenceCount.use_count());
James Feist0b236ab2019-10-02 09:09:16 -0700600 return;
601 }
602
603 std::string busStr = fname.substr(0, findDash);
604 std::string muxStr = fname.substr(findDash + 1);
605
606 size_t bus = static_cast<size_t>(std::stoi(busStr));
607 size_t addr =
608 static_cast<size_t>(std::stoi(muxStr, nullptr, 16));
609 Backplane* parent = nullptr;
610 for (auto& [name, backplane] : backplanes)
611 {
612 for (const Mux& mux : *(backplane.muxes))
613 {
614 if (bus == mux.bus && addr == mux.address)
615 {
616 parent = &backplane;
617 break;
618 }
619 }
620 }
621 if (parent == nullptr)
622 {
623 std::cerr << "Failed to find mux at bus " << bus
624 << ", addr " << addr << "\n";
James Feistdb2e0e72019-10-07 16:34:06 -0700625 createDriveInterfaces(referenceCount.use_count());
James Feist0b236ab2019-10-02 09:09:16 -0700626 return;
627 }
628 if (parent->drives.size() <= driveIndex)
629 {
630
631 std::cerr << "Illegal drive index at " << path
632 << " " << driveIndex << "\n";
James Feistdb2e0e72019-10-07 16:34:06 -0700633 createDriveInterfaces(referenceCount.use_count());
James Feist0b236ab2019-10-02 09:09:16 -0700634 return;
635 }
636 Drive& drive = parent->drives[driveIndex];
James Feistdb2e0e72019-10-07 16:34:06 -0700637 drive.removeDriveIface();
James Feist0b236ab2019-10-02 09:09:16 -0700638 drive.createAssociation(path);
James Feistdb2e0e72019-10-07 16:34:06 -0700639 createDriveInterfaces(referenceCount.use_count());
James Feist0b236ab2019-10-02 09:09:16 -0700640 },
641 owner, path, "org.freedesktop.DBus.Properties", "GetAll",
642 "xyz.openbmc_project.Inventory.Item.Drive");
643 }
644 },
645 mapper::busName, mapper::path, mapper::interface, mapper::subtree, "/",
646 0, std::array<const char*, 1>{driveType});
647}
648
649void populateMuxes(std::shared_ptr<std::vector<Mux>> muxes,
650 std::string& rootPath)
651{
652 const static std::array<const std::string, 4> muxTypes = {
653 "xyz.openbmc_project.Configuration.PCA9543Mux",
654 "xyz.openbmc_project.Configuration.PCA9544Mux",
655 "xyz.openbmc_project.Configuration.PCA9545Mux",
656 "xyz.openbmc_project.Configuration.PCA9546Mux"};
657 conn->async_method_call(
658 [muxes](const boost::system::error_code ec,
659 const GetSubTreeType& subtree) {
660 if (ec)
661 {
662 std::cerr << "Error contacting mapper " << ec.message() << "\n";
663 return;
664 }
665 std::shared_ptr<std::function<void()>> callback =
666 std::make_shared<std::function<void()>>(
667 []() { updateAssociations(); });
668 for (const auto& [path, objDict] : subtree)
669 {
670 if (objDict.empty() || objDict.begin()->second.empty())
671 {
672 continue;
673 }
674
675 const std::string& owner = objDict.begin()->first;
676 const std::vector<std::string>& interfaces =
677 objDict.begin()->second;
678
679 const std::string* interface = nullptr;
680 for (const std::string& iface : interfaces)
681 {
682 if (std::find(muxTypes.begin(), muxTypes.end(), iface) !=
683 muxTypes.end())
684 {
685 interface = &iface;
686 break;
687 }
688 }
689 if (interface == nullptr)
690 {
691 std::cerr << "Cannot get mux type\n";
692 continue;
693 }
694
695 conn->async_method_call(
696 [path, muxes, callback](
697 const boost::system::error_code ec2,
698 const boost::container::flat_map<
699 std::string, std::variant<uint64_t>>& values) {
700 if (ec2)
701 {
702 std::cerr << "Error Getting Config "
703 << ec2.message() << " " << __FUNCTION__
704 << "\n";
705 return;
706 }
707 auto findBus = values.find("Bus");
708 auto findAddress = values.find("Address");
709 if (findBus == values.end() ||
710 findAddress == values.end())
711 {
712 std::cerr << "Illegal configuration at " << path
713 << "\n";
714 return;
715 }
716 size_t bus = static_cast<size_t>(
717 std::get<uint64_t>(findBus->second));
718 size_t address = static_cast<size_t>(
719 std::get<uint64_t>(findAddress->second));
720 muxes->emplace_back(bus, address);
721 if (callback.use_count() == 1)
722 {
723 (*callback)();
724 }
725 },
726 owner, path, "org.freedesktop.DBus.Properties", "GetAll",
727 *interface);
728 }
729 },
730 mapper::busName, mapper::path, mapper::interface, mapper::subtree,
731 rootPath, 1, muxTypes);
732}
733
Feist, Jamesc95cf672019-08-29 16:10:35 -0700734void populate()
735{
736 conn->async_method_call(
737 [](const boost::system::error_code ec, const GetSubTreeType& subtree) {
738 if (ec)
739 {
740 std::cerr << "Error contacting mapper " << ec.message() << "\n";
741 return;
742 }
743 for (const auto& [path, objDict] : subtree)
744 {
745 if (objDict.empty())
746 {
747 continue;
748 }
749
750 const std::string& owner = objDict.begin()->first;
751 conn->async_method_call(
752 [path](const boost::system::error_code ec2,
753 const boost::container::flat_map<
754 std::string, BasicVariantType>& resp) {
755 if (ec2)
756 {
757 std::cerr << "Error Getting Config "
758 << ec2.message() << "\n";
759 return;
760 }
761 backplanes.clear();
762 std::optional<size_t> bus;
763 std::optional<size_t> address;
James Feist45772222019-09-27 10:38:08 -0700764 std::optional<size_t> backplaneIndex;
Feist, Jamesc95cf672019-08-29 16:10:35 -0700765 std::optional<std::string> name;
766 for (const auto& [key, value] : resp)
767 {
768 if (key == "Bus")
769 {
770 bus = std::get<uint64_t>(value);
771 }
772 else if (key == "Address")
773 {
774 address = std::get<uint64_t>(value);
775 }
James Feist45772222019-09-27 10:38:08 -0700776 else if (key == "Index")
777 {
778 backplaneIndex = std::get<uint64_t>(value);
779 }
Feist, Jamesc95cf672019-08-29 16:10:35 -0700780 else if (key == "Name")
781 {
782 name = std::get<std::string>(value);
783 }
784 }
James Feist45772222019-09-27 10:38:08 -0700785 if (!bus || !address || !name || !backplaneIndex)
Feist, Jamesc95cf672019-08-29 16:10:35 -0700786 {
787 std::cerr << "Illegal configuration at " << path
788 << "\n";
789 return;
790 }
James Feist0b236ab2019-10-02 09:09:16 -0700791 std::string parentPath =
792 std::filesystem::path(path).parent_path();
Feist, Jamesc95cf672019-08-29 16:10:35 -0700793 const auto& [backplane, status] = backplanes.emplace(
James Feist45772222019-09-27 10:38:08 -0700794 *name,
795 Backplane(*bus, *address, *backplaneIndex, *name));
Feist, Jamesc95cf672019-08-29 16:10:35 -0700796 backplane->second.run();
James Feist0b236ab2019-10-02 09:09:16 -0700797 populateMuxes(backplane->second.muxes, parentPath);
Feist, Jamesc95cf672019-08-29 16:10:35 -0700798 },
799 owner, path, "org.freedesktop.DBus.Properties", "GetAll",
800 configType);
801 }
802 },
803 mapper::busName, mapper::path, mapper::interface, mapper::subtree, "/",
804 0, std::array<const char*, 1>{configType});
805}
806
807int main()
808{
809 boost::asio::steady_timer callbackTimer(io);
810
James Feistdb2e0e72019-10-07 16:34:06 -0700811 conn->request_name(busName);
Feist, Jamesc95cf672019-08-29 16:10:35 -0700812
813 sdbusplus::bus::match::match match(
814 *conn,
815 "type='signal',member='PropertiesChanged',arg0='" +
816 std::string(configType) + "'",
817 [&callbackTimer](sdbusplus::message::message&) {
818 callbackTimer.expires_after(std::chrono::seconds(2));
819 callbackTimer.async_wait([](const boost::system::error_code ec) {
820 if (ec == boost::asio::error::operation_aborted)
821 {
822 // timer was restarted
823 return;
824 }
825 else if (ec)
826 {
827 std::cerr << "Timer error" << ec.message() << "\n";
828 return;
829 }
830 populate();
831 });
832 });
833
James Feist0b236ab2019-10-02 09:09:16 -0700834 sdbusplus::bus::match::match drive(
835 *conn,
836 "type='signal',member='PropertiesChanged',arg0='xyz.openbmc_project."
837 "Inventory.Item.Drive'",
James Feistdb2e0e72019-10-07 16:34:06 -0700838 [&callbackTimer](sdbusplus::message::message& message) {
James Feist0b236ab2019-10-02 09:09:16 -0700839 callbackTimer.expires_after(std::chrono::seconds(2));
James Feistdb2e0e72019-10-07 16:34:06 -0700840 if (message.get_sender() == conn->get_unique_name())
841 {
842 return;
843 }
James Feist0b236ab2019-10-02 09:09:16 -0700844 callbackTimer.async_wait([](const boost::system::error_code ec) {
845 if (ec == boost::asio::error::operation_aborted)
846 {
847 // timer was restarted
848 return;
849 }
850 else if (ec)
851 {
852 std::cerr << "Timer error" << ec.message() << "\n";
853 return;
854 }
855 populate();
856 });
857 });
858
Feist, Jamesc95cf672019-08-29 16:10:35 -0700859 io.post([]() { populate(); });
860 io.run();
861}