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 <filesystem> 22 #include <iostream> 23 #include <sdbusplus/asio/connection.hpp> 24 #include <sdbusplus/asio/object_server.hpp> 25 #include <sdbusplus/bus/match.hpp> 26 #include <string> 27 #include <utility> 28 29 extern "C" { 30 #include <i2c/smbus.h> 31 #include <linux/i2c-dev.h> 32 } 33 34 constexpr const char* configType = 35 "xyz.openbmc_project.Configuration.Intel_HSBP_CPLD"; 36 constexpr const char* busName = "xyz.openbmc_project.HsbpManager"; 37 38 constexpr size_t scanRateSeconds = 5; 39 constexpr size_t maxDrives = 8; // only 1 byte alloted 40 41 boost::asio::io_context io; 42 auto conn = std::make_shared<sdbusplus::asio::connection>(io); 43 sdbusplus::asio::object_server objServer(conn); 44 45 static 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 52 struct 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 }; 60 61 enum class BlinkPattern : uint8_t 62 { 63 off = 0x0, 64 error = 0x2, 65 terminate = 0x3 66 }; 67 68 struct 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 127 struct Drive 128 { 129 Drive(size_t driveIndex, bool isPresent, bool isOperational, bool nvme, 130 bool rebuilding) : 131 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( 142 itemIface->get_object_path(), 143 "xyz.openbmc_project.State.Decorator.OperationalStatus"); 144 operationalIface->register_property("Functional", isOperational); 145 operationalIface->initialize(); 146 rebuildingIface = objServer.add_interface( 147 itemIface->get_object_path(), "xyz.openbmc_project.State.Drive"); 148 rebuildingIface->register_property("Rebuilding", rebuilding); 149 rebuildingIface->initialize(); 150 } 151 virtual ~Drive() 152 { 153 objServer.remove_interface(itemIface); 154 objServer.remove_interface(operationalIface); 155 objServer.remove_interface(rebuildingIface); 156 objServer.remove_interface(associationIface); 157 objServer.remove_interface(driveIface); 158 } 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(); 173 } 174 175 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 195 std::shared_ptr<sdbusplus::asio::dbus_interface> itemIface; 196 std::shared_ptr<sdbusplus::asio::dbus_interface> operationalIface; 197 std::shared_ptr<sdbusplus::asio::dbus_interface> rebuildingIface; 198 std::shared_ptr<sdbusplus::asio::dbus_interface> associationIface; 199 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 204 bool isNvme; 205 }; 206 207 struct Backplane 208 { 209 210 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), 214 timer(std::make_shared<boost::asio::steady_timer>(io)), 215 muxes(std::make_shared<std::vector<Mux>>()) 216 { 217 } 218 void run() 219 { 220 file = open(("/dev/i2c-" + std::to_string(bus)).c_str(), 221 O_RDWR | O_CLOEXEC); 222 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 234 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, " ", "_"); 244 hsbpItemIface = objServer.add_interface( 245 "/xyz/openbmc_project/inventory/item/hsbp/" + dbusName, 246 inventory::interface); 247 hsbpItemIface->register_property("Present", true); 248 hsbpItemIface->register_property("PrettyName", name); 249 hsbpItemIface->initialize(); 250 251 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; 287 uint8_t curRebuild = 0; 288 289 getPresence(curPresence); 290 getIFDET(curIFDET); 291 getFailed(curFailed); 292 getRebuild(curRebuild); 293 294 if (curPresence != presence || curIFDET != ifdet || 295 curFailed != failed || curRebuild != rebuilding) 296 { 297 presence = curPresence; 298 ifdet = curIFDET; 299 failed = curFailed; 300 rebuilding = curRebuild; 301 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++) 311 { 312 bool isNvme = nvme & (1 << ii); 313 bool isPresent = isNvme || (presence & (1 << ii)); 314 bool isFailed = !isPresent || failed & (1 << ii); 315 bool isRebuilding = !isPresent && (rebuilding & (1 << ii)); 316 317 // +1 to convert from 0 based to 1 based 318 size_t driveIndex = (backplaneIndex * maxDrives) + ii + 1; 319 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(); 324 } 325 } 326 327 void updateDrives() 328 { 329 330 uint8_t nvme = ifdet ^ presence; 331 for (size_t ii = 0; ii < maxDrives; ii++) 332 { 333 bool isNvme = nvme & (1 << ii); 334 bool isPresent = isNvme || (presence & (1 << ii)); 335 bool isFailed = !isPresent || (failed & (1 << ii)); 336 bool isRebuilding = isPresent && (rebuilding & (1 << ii)); 337 338 Drive& drive = drives[ii]; 339 drive.isNvme = isNvme; 340 drive.itemIface->set_property("Present", isPresent); 341 drive.operationalIface->set_property("Functional", !isFailed); 342 drive.rebuildingIface->set_property("Rebuilding", isRebuilding); 343 } 344 } 345 346 bool getPresent() 347 { 348 present = i2c_smbus_read_byte(file) >= 0; 349 return present; 350 } 351 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 469 virtual ~Backplane() 470 { 471 objServer.remove_interface(hsbpItemIface); 472 objServer.remove_interface(versionIface); 473 if (file >= 0) 474 { 475 close(file); 476 } 477 } 478 479 size_t bus; 480 size_t address; 481 size_t backplaneIndex; 482 std::string name; 483 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; 493 uint8_t rebuilding = 0; 494 495 int file = -1; 496 497 std::string type; 498 499 std::shared_ptr<sdbusplus::asio::dbus_interface> hsbpItemIface; 500 std::shared_ptr<sdbusplus::asio::dbus_interface> versionIface; 501 502 std::vector<Drive> drives; 503 std::vector<std::shared_ptr<Led>> leds; 504 std::shared_ptr<std::vector<Mux>> muxes; 505 }; 506 507 std::unordered_map<std::string, Backplane> backplanes; 508 509 void 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 524 void 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; 544 // we export this interface too 545 if (owner == busName) 546 { 547 continue; 548 } 549 std::shared_ptr<bool> referenceCount = std::make_shared<bool>(); 550 conn->async_method_call( 551 [path, referenceCount]( 552 const boost::system::error_code ec2, 553 const boost::container::flat_map< 554 std::string, std::variant<uint64_t>>& values) { 555 if (ec2) 556 { 557 std::cerr << "Error Getting Config " 558 << ec2.message() << " " << __FUNCTION__ 559 << "\n"; 560 createDriveInterfaces(referenceCount.use_count()); 561 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"; 571 createDriveInterfaces(referenceCount.use_count()); 572 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"; 585 createDriveInterfaces(referenceCount.use_count()); 586 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"; 599 createDriveInterfaces(referenceCount.use_count()); 600 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"; 625 createDriveInterfaces(referenceCount.use_count()); 626 return; 627 } 628 if (parent->drives.size() <= driveIndex) 629 { 630 631 std::cerr << "Illegal drive index at " << path 632 << " " << driveIndex << "\n"; 633 createDriveInterfaces(referenceCount.use_count()); 634 return; 635 } 636 Drive& drive = parent->drives[driveIndex]; 637 drive.removeDriveIface(); 638 drive.createAssociation(path); 639 createDriveInterfaces(referenceCount.use_count()); 640 }, 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 649 void 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 734 void 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; 764 std::optional<size_t> backplaneIndex; 765 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 } 776 else if (key == "Index") 777 { 778 backplaneIndex = std::get<uint64_t>(value); 779 } 780 else if (key == "Name") 781 { 782 name = std::get<std::string>(value); 783 } 784 } 785 if (!bus || !address || !name || !backplaneIndex) 786 { 787 std::cerr << "Illegal configuration at " << path 788 << "\n"; 789 return; 790 } 791 std::string parentPath = 792 std::filesystem::path(path).parent_path(); 793 const auto& [backplane, status] = backplanes.emplace( 794 *name, 795 Backplane(*bus, *address, *backplaneIndex, *name)); 796 backplane->second.run(); 797 populateMuxes(backplane->second.muxes, parentPath); 798 }, 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 807 int main() 808 { 809 boost::asio::steady_timer callbackTimer(io); 810 811 conn->request_name(busName); 812 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 834 sdbusplus::bus::match::match drive( 835 *conn, 836 "type='signal',member='PropertiesChanged',arg0='xyz.openbmc_project." 837 "Inventory.Item.Drive'", 838 [&callbackTimer](sdbusplus::message::message& message) { 839 callbackTimer.expires_after(std::chrono::seconds(2)); 840 if (message.get_sender() == conn->get_unique_name()) 841 { 842 return; 843 } 844 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 859 io.post([]() { populate(); }); 860 io.run(); 861 } 862