1 /* 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> 21 #include <boost/container/flat_set.hpp> 22 #include <filesystem> 23 #include <fstream> 24 #include <iostream> 25 #include <sdbusplus/asio/connection.hpp> 26 #include <sdbusplus/asio/object_server.hpp> 27 #include <sdbusplus/bus/match.hpp> 28 #include <string> 29 #include <utility> 30 31 extern "C" { 32 #include <i2c/smbus.h> 33 #include <linux/i2c-dev.h> 34 } 35 36 constexpr const char* configType = 37 "xyz.openbmc_project.Configuration.Intel_HSBP_CPLD"; 38 constexpr const char* busName = "xyz.openbmc_project.HsbpManager"; 39 40 constexpr size_t scanRateSeconds = 5; 41 constexpr size_t maxDrives = 8; // only 1 byte alloted 42 43 boost::asio::io_context io; 44 auto conn = std::make_shared<sdbusplus::asio::connection>(io); 45 sdbusplus::asio::object_server objServer(conn); 46 47 static std::string zeroPad(const uint8_t val) 48 { 49 std::ostringstream version; 50 version << std::setw(2) << std::setfill('0') << static_cast<size_t>(val); 51 return version.str(); 52 } 53 54 struct Mux 55 { 56 Mux(size_t busIn, size_t addressIn, size_t channelsIn, size_t indexIn) : 57 bus(busIn), address(addressIn), channels(channelsIn), index(indexIn) 58 { 59 } 60 size_t bus; 61 size_t address; 62 size_t channels; 63 size_t index; 64 65 // to sort in the flat set 66 bool operator<(const Mux& rhs) const 67 { 68 return index < rhs.index; 69 } 70 }; 71 72 enum class BlinkPattern : uint8_t 73 { 74 off = 0x0, 75 error = 0x2, 76 terminate = 0x3 77 }; 78 79 struct Led : std::enable_shared_from_this<Led> 80 { 81 // led pattern addresses start at 0x10 82 Led(const std::string& path, size_t index, int fd) : 83 address(static_cast<uint8_t>(index + 0x10)), file(fd), 84 ledInterface(objServer.add_interface(path, ledGroup::interface)) 85 { 86 if (index >= maxDrives) 87 { 88 throw std::runtime_error("Invalid drive index"); 89 } 90 91 if (!set(BlinkPattern::off)) 92 { 93 std::cerr << "Cannot initialize LED " << path << "\n"; 94 } 95 } 96 97 // this has to be called outside the constructor for shared_from_this to 98 // work 99 void createInterface(void) 100 { 101 std::shared_ptr<Led> self = shared_from_this(); 102 103 ledInterface->register_property( 104 ledGroup::asserted, false, [self](const bool req, bool& val) { 105 if (req == val) 106 { 107 return 1; 108 } 109 110 if (!isPowerOn()) 111 { 112 std::cerr << "Can't change blink state when power is off\n"; 113 throw std::runtime_error( 114 "Can't change blink state when power is off"); 115 } 116 BlinkPattern pattern = 117 req ? BlinkPattern::error : BlinkPattern::terminate; 118 if (!self->set(pattern)) 119 { 120 std::cerr << "Can't change blink pattern\n"; 121 throw std::runtime_error("Cannot set blink pattern"); 122 } 123 val = req; 124 return 1; 125 }); 126 ledInterface->initialize(); 127 } 128 129 virtual ~Led() 130 { 131 objServer.remove_interface(ledInterface); 132 } 133 134 bool set(BlinkPattern pattern) 135 { 136 int ret = i2c_smbus_write_byte_data(file, address, 137 static_cast<uint8_t>(pattern)); 138 return ret >= 0; 139 } 140 141 uint8_t address; 142 int file; 143 std::shared_ptr<sdbusplus::asio::dbus_interface> ledInterface; 144 }; 145 146 struct Drive 147 { 148 Drive(size_t driveIndex, bool isPresent, bool isOperational, bool nvme, 149 bool rebuilding) : 150 isNvme(nvme) 151 { 152 constexpr const char* basePath = 153 "/xyz/openbmc_project/inventory/item/drive/Drive_"; 154 itemIface = objServer.add_interface( 155 basePath + std::to_string(driveIndex), inventory::interface); 156 itemIface->register_property("Present", isPresent); 157 itemIface->register_property("PrettyName", 158 "Drive " + std::to_string(driveIndex)); 159 itemIface->initialize(); 160 operationalIface = objServer.add_interface( 161 itemIface->get_object_path(), 162 "xyz.openbmc_project.State.Decorator.OperationalStatus"); 163 operationalIface->register_property("Functional", isOperational); 164 operationalIface->initialize(); 165 rebuildingIface = objServer.add_interface( 166 itemIface->get_object_path(), "xyz.openbmc_project.State.Drive"); 167 rebuildingIface->register_property("Rebuilding", rebuilding); 168 rebuildingIface->initialize(); 169 driveIface = 170 objServer.add_interface(itemIface->get_object_path(), 171 "xyz.openbmc_project.Inventory.Item.Drive"); 172 driveIface->initialize(); 173 } 174 virtual ~Drive() 175 { 176 objServer.remove_interface(itemIface); 177 objServer.remove_interface(operationalIface); 178 objServer.remove_interface(rebuildingIface); 179 objServer.remove_interface(assetIface); 180 objServer.remove_interface(driveIface); 181 } 182 183 void createAsset( 184 const boost::container::flat_map<std::string, std::string>& data) 185 { 186 if (assetIface != nullptr) 187 { 188 return; 189 } 190 assetIface = objServer.add_interface( 191 itemIface->get_object_path(), 192 "xyz.openbmc_project.Inventory.Decorator.Asset"); 193 for (const auto& [key, value] : data) 194 { 195 assetIface->register_property(key, value); 196 } 197 assetIface->initialize(); 198 } 199 200 std::shared_ptr<sdbusplus::asio::dbus_interface> itemIface; 201 std::shared_ptr<sdbusplus::asio::dbus_interface> operationalIface; 202 std::shared_ptr<sdbusplus::asio::dbus_interface> rebuildingIface; 203 std::shared_ptr<sdbusplus::asio::dbus_interface> assetIface; 204 std::shared_ptr<sdbusplus::asio::dbus_interface> driveIface; 205 206 bool isNvme; 207 }; 208 209 struct Backplane 210 { 211 212 Backplane(size_t busIn, size_t addressIn, size_t backplaneIndexIn, 213 const std::string& nameIn) : 214 bus(busIn), 215 address(addressIn), backplaneIndex(backplaneIndexIn - 1), name(nameIn), 216 timer(std::make_shared<boost::asio::steady_timer>(io)), 217 muxes(std::make_shared<boost::container::flat_set<Mux>>()) 218 { 219 } 220 void run() 221 { 222 file = open(("/dev/i2c-" + std::to_string(bus)).c_str(), 223 O_RDWR | O_CLOEXEC); 224 if (file < 0) 225 { 226 std::cerr << "unable to open bus " << bus << "\n"; 227 return; 228 } 229 230 if (ioctl(file, I2C_SLAVE_FORCE, address) < 0) 231 { 232 std::cerr << "unable to set address to " << address << "\n"; 233 return; 234 } 235 236 if (!getPresent()) 237 { 238 std::cerr << "Cannot detect CPLD\n"; 239 return; 240 } 241 242 getBootVer(bootVer); 243 getFPGAVer(fpgaVer); 244 getSecurityRev(securityRev); 245 std::string dbusName = boost::replace_all_copy(name, " ", "_"); 246 hsbpItemIface = objServer.add_interface( 247 "/xyz/openbmc_project/inventory/item/hsbp/" + dbusName, 248 inventory::interface); 249 hsbpItemIface->register_property("Present", true); 250 hsbpItemIface->register_property("PrettyName", name); 251 hsbpItemIface->initialize(); 252 253 versionIface = 254 objServer.add_interface(hsbpItemIface->get_object_path(), 255 "xyz.openbmc_project.Software.Version"); 256 versionIface->register_property("Version", zeroPad(bootVer) + "." + 257 zeroPad(fpgaVer) + "." + 258 zeroPad(securityRev)); 259 versionIface->register_property( 260 "Purpose", 261 std::string( 262 "xyz.openbmc_project.Software.Version.VersionPurpose.HSBP")); 263 versionIface->initialize(); 264 getPresence(presence); 265 getIFDET(ifdet); 266 267 createDrives(); 268 269 runTimer(); 270 } 271 272 void runTimer() 273 { 274 timer->expires_after(std::chrono::seconds(scanRateSeconds)); 275 timer->async_wait([this](boost::system::error_code ec) { 276 if (ec == boost::asio::error::operation_aborted) 277 { 278 // we're being destroyed 279 return; 280 } 281 else if (ec) 282 { 283 std::cerr << "timer error " << ec.message() << "\n"; 284 return; 285 } 286 287 if (!isPowerOn()) 288 { 289 // can't access hsbp when power is off 290 runTimer(); 291 return; 292 } 293 uint8_t curPresence = 0; 294 uint8_t curIFDET = 0; 295 uint8_t curFailed = 0; 296 uint8_t curRebuild = 0; 297 298 getPresence(curPresence); 299 getIFDET(curIFDET); 300 getFailed(curFailed); 301 getRebuild(curRebuild); 302 303 if (curPresence != presence || curIFDET != ifdet || 304 curFailed != failed || curRebuild != rebuilding) 305 { 306 presence = curPresence; 307 ifdet = curIFDET; 308 failed = curFailed; 309 rebuilding = curRebuild; 310 updateDrives(); 311 } 312 runTimer(); 313 }); 314 } 315 316 void createDrives() 317 { 318 uint8_t nvme = ifdet ^ presence; 319 for (size_t ii = 0; ii < maxDrives; ii++) 320 { 321 bool isNvme = nvme & (1 << ii); 322 bool isPresent = isNvme || (presence & (1 << ii)); 323 bool isFailed = !isPresent || failed & (1 << ii); 324 bool isRebuilding = !isPresent && (rebuilding & (1 << ii)); 325 326 // +1 to convert from 0 based to 1 based 327 size_t driveIndex = (backplaneIndex * maxDrives) + ii + 1; 328 Drive& drive = drives.emplace_back(driveIndex, isPresent, !isFailed, 329 isNvme, isRebuilding); 330 std::shared_ptr<Led> led = leds.emplace_back(std::make_shared<Led>( 331 drive.itemIface->get_object_path(), ii, file)); 332 led->createInterface(); 333 } 334 } 335 336 void updateDrives() 337 { 338 339 uint8_t nvme = ifdet ^ presence; 340 for (size_t ii = 0; ii < maxDrives; ii++) 341 { 342 bool isNvme = nvme & (1 << ii); 343 bool isPresent = isNvme || (presence & (1 << ii)); 344 bool isFailed = !isPresent || (failed & (1 << ii)); 345 bool isRebuilding = isPresent && (rebuilding & (1 << ii)); 346 347 Drive& drive = drives[ii]; 348 drive.isNvme = isNvme; 349 drive.itemIface->set_property("Present", isPresent); 350 drive.operationalIface->set_property("Functional", !isFailed); 351 drive.rebuildingIface->set_property("Rebuilding", isRebuilding); 352 } 353 } 354 355 bool getPresent() 356 { 357 present = i2c_smbus_read_byte(file) >= 0; 358 return present; 359 } 360 361 bool getTypeID(uint8_t& val) 362 { 363 constexpr uint8_t addr = 2; 364 int ret = i2c_smbus_read_byte_data(file, addr); 365 if (ret < 0) 366 { 367 std::cerr << "Error " << __FUNCTION__ << "\n"; 368 return false; 369 } 370 val = static_cast<uint8_t>(ret); 371 return true; 372 } 373 374 bool getBootVer(uint8_t& val) 375 { 376 constexpr uint8_t addr = 3; 377 int ret = i2c_smbus_read_byte_data(file, addr); 378 if (ret < 0) 379 { 380 std::cerr << "Error " << __FUNCTION__ << "\n"; 381 return false; 382 } 383 val = static_cast<uint8_t>(ret); 384 return true; 385 } 386 387 bool getFPGAVer(uint8_t& val) 388 { 389 constexpr uint8_t addr = 4; 390 int ret = i2c_smbus_read_byte_data(file, addr); 391 if (ret < 0) 392 { 393 std::cerr << "Error " << __FUNCTION__ << "\n"; 394 return false; 395 } 396 val = static_cast<uint8_t>(ret); 397 return true; 398 } 399 400 bool getSecurityRev(uint8_t& val) 401 { 402 constexpr uint8_t addr = 5; 403 int ret = i2c_smbus_read_byte_data(file, addr); 404 if (ret < 0) 405 { 406 std::cerr << "Error " << __FUNCTION__ << "\n"; 407 return false; 408 } 409 val = static_cast<uint8_t>(ret); 410 return true; 411 } 412 413 bool getPresence(uint8_t& val) 414 { 415 // NVMe drives do not assert PRSNTn, and as such do not get reported as 416 // PRESENT in this register 417 418 constexpr uint8_t addr = 8; 419 420 int ret = i2c_smbus_read_byte_data(file, addr); 421 if (ret < 0) 422 { 423 std::cerr << "Error " << __FUNCTION__ << "\n"; 424 return false; 425 } 426 // presence is inverted 427 val = static_cast<uint8_t>(~ret); 428 return true; 429 } 430 431 bool getIFDET(uint8_t& val) 432 { 433 // This register is a bitmap of parallel GPIO pins connected to the 434 // IFDETn pin of a drive slot. SATA, SAS, and NVMe drives all assert 435 // IFDETn low when they are inserted into the HSBP.This register, in 436 // combination with the PRESENCE register, are used by the BMC to detect 437 // the presence of NVMe drives. 438 439 constexpr uint8_t addr = 9; 440 441 int ret = i2c_smbus_read_byte_data(file, addr); 442 if (ret < 0) 443 { 444 std::cerr << "Error " << __FUNCTION__ << "\n"; 445 return false; 446 } 447 // ifdet is inverted 448 val = static_cast<uint8_t>(~ret); 449 return true; 450 } 451 452 bool getFailed(uint8_t& val) 453 { 454 constexpr uint8_t addr = 0xC; 455 int ret = i2c_smbus_read_byte_data(file, addr); 456 if (ret < 0) 457 { 458 std::cerr << "Error " << __FUNCTION__ << "\n"; 459 return false; 460 } 461 val = static_cast<uint8_t>(ret); 462 return true; 463 } 464 465 bool getRebuild(uint8_t& val) 466 { 467 constexpr uint8_t addr = 0xD; 468 int ret = i2c_smbus_read_byte_data(file, addr); 469 if (ret < 0) 470 { 471 std::cerr << "Error " << __FUNCTION__ << "\n"; 472 return false; 473 } 474 val = static_cast<uint8_t>(ret); 475 return true; 476 } 477 478 virtual ~Backplane() 479 { 480 objServer.remove_interface(hsbpItemIface); 481 objServer.remove_interface(versionIface); 482 if (file >= 0) 483 { 484 close(file); 485 } 486 } 487 488 size_t bus; 489 size_t address; 490 size_t backplaneIndex; 491 std::string name; 492 std::shared_ptr<boost::asio::steady_timer> timer; 493 bool present = false; 494 uint8_t typeId = 0; 495 uint8_t bootVer = 0; 496 uint8_t fpgaVer = 0; 497 uint8_t securityRev = 0; 498 uint8_t funSupported = 0; 499 uint8_t presence = 0; 500 uint8_t ifdet = 0; 501 uint8_t failed = 0; 502 uint8_t rebuilding = 0; 503 504 int file = -1; 505 506 std::string type; 507 508 std::shared_ptr<sdbusplus::asio::dbus_interface> hsbpItemIface; 509 std::shared_ptr<sdbusplus::asio::dbus_interface> versionIface; 510 511 std::vector<Drive> drives; 512 std::vector<std::shared_ptr<Led>> leds; 513 std::shared_ptr<boost::container::flat_set<Mux>> muxes; 514 }; 515 516 std::unordered_map<std::string, Backplane> backplanes; 517 std::vector<Drive> ownerlessDrives; // drives without a backplane 518 519 static size_t getDriveCount() 520 { 521 size_t count = 0; 522 for (const auto& [key, backplane] : backplanes) 523 { 524 count += backplane.drives.size(); 525 } 526 return count + ownerlessDrives.size(); 527 } 528 529 void updateAssets() 530 { 531 static constexpr const char* nvmeType = 532 "xyz.openbmc_project.Inventory.Item.NVMe"; 533 static const std::string assetTag = 534 "xyz.openbmc_project.Inventory.Decorator.Asset"; 535 536 conn->async_method_call( 537 [](const boost::system::error_code ec, const GetSubTreeType& subtree) { 538 if (ec) 539 { 540 std::cerr << "Error contacting mapper " << ec.message() << "\n"; 541 return; 542 } 543 544 // drives may get an owner during this, or we might disover more 545 // drives 546 ownerlessDrives.clear(); 547 for (const auto& [path, objDict] : subtree) 548 { 549 if (objDict.empty()) 550 { 551 continue; 552 } 553 554 const std::string& owner = objDict.begin()->first; 555 // we export this interface too 556 if (owner == busName) 557 { 558 continue; 559 } 560 if (std::find(objDict.begin()->second.begin(), 561 objDict.begin()->second.end(), 562 assetTag) == objDict.begin()->second.end()) 563 { 564 // no asset tag to associate to 565 continue; 566 } 567 568 conn->async_method_call( 569 [path](const boost::system::error_code ec2, 570 const boost::container::flat_map< 571 std::string, 572 std::variant<uint64_t, std::string>>& values) { 573 if (ec2) 574 { 575 std::cerr << "Error Getting Config " 576 << ec2.message() << " " << __FUNCTION__ 577 << "\n"; 578 return; 579 } 580 auto findBus = values.find("Bus"); 581 582 if (findBus == values.end()) 583 { 584 std::cerr << "Illegal interface at " << path 585 << "\n"; 586 return; 587 } 588 589 // find the mux bus and addr 590 size_t muxBus = static_cast<size_t>( 591 std::get<uint64_t>(findBus->second)); 592 std::filesystem::path muxPath = 593 "/sys/bus/i2c/devices/i2c-" + 594 std::to_string(muxBus) + "/mux_device"; 595 if (!std::filesystem::is_symlink(muxPath)) 596 { 597 std::cerr << path << " mux does not exist\n"; 598 return; 599 } 600 601 // we should be getting something of the form 7-0052 602 // for bus 7 addr 52 603 std::string fname = 604 std::filesystem::read_symlink(muxPath).filename(); 605 auto findDash = fname.find('-'); 606 607 if (findDash == std::string::npos || 608 findDash + 1 >= fname.size()) 609 { 610 std::cerr << path << " mux path invalid\n"; 611 return; 612 } 613 614 std::string busStr = fname.substr(0, findDash); 615 std::string muxStr = fname.substr(findDash + 1); 616 617 size_t bus = static_cast<size_t>(std::stoi(busStr)); 618 size_t addr = 619 static_cast<size_t>(std::stoi(muxStr, nullptr, 16)); 620 size_t muxIndex = 0; 621 622 // find the channel of the mux the drive is on 623 std::ifstream nameFile("/sys/bus/i2c/devices/i2c-" + 624 std::to_string(muxBus) + 625 "/name"); 626 if (!nameFile) 627 { 628 std::cerr << "Unable to open name file of bus " 629 << muxBus << "\n"; 630 return; 631 } 632 633 std::string nameStr; 634 std::getline(nameFile, nameStr); 635 636 // file is of the form "i2c-4-mux (chan_id 1)", get chan 637 // assume single digit chan 638 const std::string prefix = "chan_id "; 639 size_t findId = nameStr.find(prefix); 640 if (findId == std::string::npos || 641 findId + 1 >= nameStr.size()) 642 { 643 std::cerr << "Illegal name file on bus " << muxBus 644 << "\n"; 645 } 646 647 std::string indexStr = 648 nameStr.substr(findId + prefix.size(), 1); 649 650 size_t driveIndex = std::stoi(indexStr); 651 652 Backplane* parent = nullptr; 653 for (auto& [name, backplane] : backplanes) 654 { 655 muxIndex = 0; 656 for (const Mux& mux : *(backplane.muxes)) 657 { 658 if (bus == mux.bus && addr == mux.address) 659 { 660 parent = &backplane; 661 break; 662 } 663 muxIndex += mux.channels; 664 } 665 } 666 boost::container::flat_map<std::string, std::string> 667 assetInventory; 668 const std::array<const char*, 4> assetKeys = { 669 "PartNumber", "SerialNumber", "Manufacturer", 670 "Model"}; 671 for (const auto& [key, value] : values) 672 { 673 if (std::find(assetKeys.begin(), assetKeys.end(), 674 key) == assetKeys.end()) 675 { 676 continue; 677 } 678 assetInventory[key] = std::get<std::string>(value); 679 } 680 681 // assume its a M.2 or something without a hsbp 682 if (parent == nullptr) 683 { 684 auto& drive = ownerlessDrives.emplace_back( 685 getDriveCount() + 1, true, true, true, false); 686 drive.createAsset(assetInventory); 687 return; 688 } 689 690 driveIndex += muxIndex; 691 692 if (parent->drives.size() <= driveIndex) 693 { 694 std::cerr << "Illegal drive index at " << path 695 << " " << driveIndex << "\n"; 696 return; 697 } 698 699 Drive& drive = parent->drives[driveIndex]; 700 drive.createAsset(assetInventory); 701 }, 702 owner, path, "org.freedesktop.DBus.Properties", "GetAll", 703 "" /*all interface items*/); 704 } 705 }, 706 mapper::busName, mapper::path, mapper::interface, mapper::subtree, "/", 707 0, std::array<const char*, 1>{nvmeType}); 708 } 709 710 void populateMuxes(std::shared_ptr<boost::container::flat_set<Mux>> muxes, 711 std::string& rootPath) 712 { 713 const static std::array<const std::string, 4> muxTypes = { 714 "xyz.openbmc_project.Configuration.PCA9543Mux", 715 "xyz.openbmc_project.Configuration.PCA9544Mux", 716 "xyz.openbmc_project.Configuration.PCA9545Mux", 717 "xyz.openbmc_project.Configuration.PCA9546Mux"}; 718 conn->async_method_call( 719 [muxes](const boost::system::error_code ec, 720 const GetSubTreeType& subtree) { 721 if (ec) 722 { 723 std::cerr << "Error contacting mapper " << ec.message() << "\n"; 724 return; 725 } 726 std::shared_ptr<std::function<void()>> callback = 727 std::make_shared<std::function<void()>>( 728 []() { updateAssets(); }); 729 size_t index = 0; // as we use a flat map, these are sorted 730 for (const auto& [path, objDict] : subtree) 731 { 732 if (objDict.empty() || objDict.begin()->second.empty()) 733 { 734 continue; 735 } 736 737 const std::string& owner = objDict.begin()->first; 738 const std::vector<std::string>& interfaces = 739 objDict.begin()->second; 740 741 const std::string* interface = nullptr; 742 for (const std::string& iface : interfaces) 743 { 744 if (std::find(muxTypes.begin(), muxTypes.end(), iface) != 745 muxTypes.end()) 746 { 747 interface = &iface; 748 break; 749 } 750 } 751 if (interface == nullptr) 752 { 753 std::cerr << "Cannot get mux type\n"; 754 continue; 755 } 756 757 conn->async_method_call( 758 [path, muxes, callback, index]( 759 const boost::system::error_code ec2, 760 const boost::container::flat_map< 761 std::string, 762 std::variant<uint64_t, std::vector<std::string>>>& 763 values) { 764 if (ec2) 765 { 766 std::cerr << "Error Getting Config " 767 << ec2.message() << " " << __FUNCTION__ 768 << "\n"; 769 return; 770 } 771 auto findBus = values.find("Bus"); 772 auto findAddress = values.find("Address"); 773 auto findChannelNames = values.find("ChannelNames"); 774 if (findBus == values.end() || 775 findAddress == values.end()) 776 { 777 std::cerr << "Illegal configuration at " << path 778 << "\n"; 779 return; 780 } 781 size_t bus = static_cast<size_t>( 782 std::get<uint64_t>(findBus->second)); 783 size_t address = static_cast<size_t>( 784 std::get<uint64_t>(findAddress->second)); 785 std::vector<std::string> channels = 786 std::get<std::vector<std::string>>( 787 findChannelNames->second); 788 muxes->emplace(bus, address, channels.size(), index); 789 if (callback.use_count() == 1) 790 { 791 (*callback)(); 792 } 793 }, 794 owner, path, "org.freedesktop.DBus.Properties", "GetAll", 795 *interface); 796 index++; 797 } 798 }, 799 mapper::busName, mapper::path, mapper::interface, mapper::subtree, 800 rootPath, 1, muxTypes); 801 } 802 803 void populate() 804 { 805 conn->async_method_call( 806 [](const boost::system::error_code ec, const GetSubTreeType& subtree) { 807 if (ec) 808 { 809 std::cerr << "Error contacting mapper " << ec.message() << "\n"; 810 return; 811 } 812 for (const auto& [path, objDict] : subtree) 813 { 814 if (objDict.empty()) 815 { 816 continue; 817 } 818 819 const std::string& owner = objDict.begin()->first; 820 conn->async_method_call( 821 [path](const boost::system::error_code ec2, 822 const boost::container::flat_map< 823 std::string, BasicVariantType>& resp) { 824 if (ec2) 825 { 826 std::cerr << "Error Getting Config " 827 << ec2.message() << "\n"; 828 return; 829 } 830 backplanes.clear(); 831 std::optional<size_t> bus; 832 std::optional<size_t> address; 833 std::optional<size_t> backplaneIndex; 834 std::optional<std::string> name; 835 for (const auto& [key, value] : resp) 836 { 837 if (key == "Bus") 838 { 839 bus = std::get<uint64_t>(value); 840 } 841 else if (key == "Address") 842 { 843 address = std::get<uint64_t>(value); 844 } 845 else if (key == "Index") 846 { 847 backplaneIndex = std::get<uint64_t>(value); 848 } 849 else if (key == "Name") 850 { 851 name = std::get<std::string>(value); 852 } 853 } 854 if (!bus || !address || !name || !backplaneIndex) 855 { 856 std::cerr << "Illegal configuration at " << path 857 << "\n"; 858 return; 859 } 860 std::string parentPath = 861 std::filesystem::path(path).parent_path(); 862 const auto& [backplane, status] = backplanes.emplace( 863 *name, 864 Backplane(*bus, *address, *backplaneIndex, *name)); 865 backplane->second.run(); 866 populateMuxes(backplane->second.muxes, parentPath); 867 }, 868 owner, path, "org.freedesktop.DBus.Properties", "GetAll", 869 configType); 870 } 871 }, 872 mapper::busName, mapper::path, mapper::interface, mapper::subtree, "/", 873 0, std::array<const char*, 1>{configType}); 874 } 875 876 int main() 877 { 878 boost::asio::steady_timer callbackTimer(io); 879 880 conn->request_name(busName); 881 882 sdbusplus::bus::match::match match( 883 *conn, 884 "type='signal',member='PropertiesChanged',arg0='" + 885 std::string(configType) + "'", 886 [&callbackTimer](sdbusplus::message::message&) { 887 callbackTimer.expires_after(std::chrono::seconds(2)); 888 callbackTimer.async_wait([](const boost::system::error_code ec) { 889 if (ec == boost::asio::error::operation_aborted) 890 { 891 // timer was restarted 892 return; 893 } 894 else if (ec) 895 { 896 std::cerr << "Timer error" << ec.message() << "\n"; 897 return; 898 } 899 populate(); 900 }); 901 }); 902 903 sdbusplus::bus::match::match drive( 904 *conn, 905 "type='signal',member='PropertiesChanged',arg0='xyz.openbmc_project." 906 "Inventory.Item.NVMe'", 907 [&callbackTimer](sdbusplus::message::message& message) { 908 callbackTimer.expires_after(std::chrono::seconds(2)); 909 if (message.get_sender() == conn->get_unique_name()) 910 { 911 return; 912 } 913 callbackTimer.async_wait([](const boost::system::error_code ec) { 914 if (ec == boost::asio::error::operation_aborted) 915 { 916 // timer was restarted 917 return; 918 } 919 else if (ec) 920 { 921 std::cerr << "Timer error" << ec.message() << "\n"; 922 return; 923 } 924 populate(); 925 }); 926 }); 927 928 io.post([]() { populate(); }); 929 setupPowerMatch(conn); 930 io.run(); 931 } 932