blob: 83ebdf3b4fe9b097a612dc0be91f71b71f61c188 [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 }
James Feist9f6565d2019-10-09 13:15:13 -070098
99 if (!isPowerOn())
100 {
101 std::cerr << "Can't change blink state when power is off\n";
102 throw std::runtime_error(
103 "Can't change blink state when power is off");
104 }
James Feist09dd2312019-10-09 09:29:03 -0700105 BlinkPattern pattern =
106 req ? BlinkPattern::error : BlinkPattern::terminate;
107 if (!self->set(pattern))
108 {
James Feist9f6565d2019-10-09 13:15:13 -0700109 std::cerr << "Can't change blink pattern\n";
James Feist09dd2312019-10-09 09:29:03 -0700110 throw std::runtime_error("Cannot set blink pattern");
111 }
112 val = req;
113 return 1;
114 });
115 ledInterface->initialize();
116 }
117
118 virtual ~Led()
119 {
120 objServer.remove_interface(ledInterface);
121 }
122
123 bool set(BlinkPattern pattern)
124 {
125 int ret = i2c_smbus_write_byte_data(file, address,
126 static_cast<uint8_t>(pattern));
127 return ret >= 0;
128 }
129
130 uint8_t address;
131 int file;
132 std::shared_ptr<sdbusplus::asio::dbus_interface> ledInterface;
133};
134
James Feist45772222019-09-27 10:38:08 -0700135struct Drive
136{
James Feist244f3232019-09-27 15:15:14 -0700137 Drive(size_t driveIndex, bool isPresent, bool isOperational, bool nvme,
138 bool rebuilding) :
James Feist45772222019-09-27 10:38:08 -0700139 isNvme(nvme)
140 {
141 constexpr const char* basePath =
142 "/xyz/openbmc_project/inventory/item/drive/Drive_";
143 itemIface = objServer.add_interface(
144 basePath + std::to_string(driveIndex), inventory::interface);
145 itemIface->register_property("Present", isPresent);
146 itemIface->register_property("PrettyName",
147 "Drive " + std::to_string(driveIndex));
148 itemIface->initialize();
149 operationalIface = objServer.add_interface(
James Feist244f3232019-09-27 15:15:14 -0700150 itemIface->get_object_path(),
James Feist45772222019-09-27 10:38:08 -0700151 "xyz.openbmc_project.State.Decorator.OperationalStatus");
152 operationalIface->register_property("Functional", isOperational);
153 operationalIface->initialize();
James Feist244f3232019-09-27 15:15:14 -0700154 rebuildingIface = objServer.add_interface(
155 itemIface->get_object_path(), "xyz.openbmc_project.State.Drive");
156 rebuildingIface->register_property("Rebuilding", rebuilding);
157 rebuildingIface->initialize();
James Feist45772222019-09-27 10:38:08 -0700158 }
James Feist09dd2312019-10-09 09:29:03 -0700159 virtual ~Drive()
James Feist45772222019-09-27 10:38:08 -0700160 {
161 objServer.remove_interface(itemIface);
162 objServer.remove_interface(operationalIface);
James Feist244f3232019-09-27 15:15:14 -0700163 objServer.remove_interface(rebuildingIface);
James Feist0b236ab2019-10-02 09:09:16 -0700164 objServer.remove_interface(associationIface);
James Feistdb2e0e72019-10-07 16:34:06 -0700165 objServer.remove_interface(driveIface);
James Feist0b236ab2019-10-02 09:09:16 -0700166 }
167
168 void createAssociation(const std::string& path)
169 {
170 if (associationIface != nullptr)
171 {
172 return;
173 }
174 associationIface = objServer.add_interface(
175 itemIface->get_object_path(),
176 "xyz.openbmc_project.Association.Definitions");
177 std::vector<Association> associations;
178 associations.emplace_back("inventory", "drive", path);
179 associationIface->register_property("Associations", associations);
180 associationIface->initialize();
James Feist45772222019-09-27 10:38:08 -0700181 }
182
James Feistdb2e0e72019-10-07 16:34:06 -0700183 void removeDriveIface()
184 {
185 objServer.remove_interface(driveIface);
186 }
187
188 void createDriveIface()
189 {
190 if (associationIface != nullptr || driveIface != nullptr)
191 {
192 // this shouldn't be used if we found another provider of the drive
193 // interface, or if we already created one
194 return;
195 }
196 driveIface =
197 objServer.add_interface(itemIface->get_object_path(),
198 "xyz.openbmc_project.Inventory.Item.Drive");
199 driveIface->initialize();
200 createAssociation(itemIface->get_object_path());
201 }
202
James Feist45772222019-09-27 10:38:08 -0700203 std::shared_ptr<sdbusplus::asio::dbus_interface> itemIface;
204 std::shared_ptr<sdbusplus::asio::dbus_interface> operationalIface;
James Feist244f3232019-09-27 15:15:14 -0700205 std::shared_ptr<sdbusplus::asio::dbus_interface> rebuildingIface;
James Feist0b236ab2019-10-02 09:09:16 -0700206 std::shared_ptr<sdbusplus::asio::dbus_interface> associationIface;
James Feistdb2e0e72019-10-07 16:34:06 -0700207
208 // if something else doesn't expose a driveInterface for this, we need to
209 // export it ourselves
210 std::shared_ptr<sdbusplus::asio::dbus_interface> driveIface;
211
James Feist45772222019-09-27 10:38:08 -0700212 bool isNvme;
213};
214
Feist, Jamesc95cf672019-08-29 16:10:35 -0700215struct Backplane
216{
217
James Feist45772222019-09-27 10:38:08 -0700218 Backplane(size_t busIn, size_t addressIn, size_t backplaneIndexIn,
219 const std::string& nameIn) :
220 bus(busIn),
221 address(addressIn), backplaneIndex(backplaneIndexIn - 1), name(nameIn),
James Feist0b236ab2019-10-02 09:09:16 -0700222 timer(std::make_shared<boost::asio::steady_timer>(io)),
223 muxes(std::make_shared<std::vector<Mux>>())
Feist, Jamesc95cf672019-08-29 16:10:35 -0700224 {
225 }
Feist, Jamesc95cf672019-08-29 16:10:35 -0700226 void run()
227 {
James Feist09dd2312019-10-09 09:29:03 -0700228 file = open(("/dev/i2c-" + std::to_string(bus)).c_str(),
229 O_RDWR | O_CLOEXEC);
Feist, Jamesc95cf672019-08-29 16:10:35 -0700230 if (file < 0)
231 {
232 std::cerr << "unable to open bus " << bus << "\n";
233 return;
234 }
235
236 if (ioctl(file, I2C_SLAVE_FORCE, address) < 0)
237 {
238 std::cerr << "unable to set address to " << address << "\n";
239 return;
240 }
241
James Feist45772222019-09-27 10:38:08 -0700242 if (!getPresent())
243 {
244 std::cerr << "Cannot detect CPLD\n";
245 return;
246 }
247
248 getBootVer(bootVer);
249 getFPGAVer(fpgaVer);
250 getSecurityRev(securityRev);
251 std::string dbusName = boost::replace_all_copy(name, " ", "_");
Feist, Jamesc95cf672019-08-29 16:10:35 -0700252 hsbpItemIface = objServer.add_interface(
James Feist45772222019-09-27 10:38:08 -0700253 "/xyz/openbmc_project/inventory/item/hsbp/" + dbusName,
Feist, Jamesc95cf672019-08-29 16:10:35 -0700254 inventory::interface);
James Feist45772222019-09-27 10:38:08 -0700255 hsbpItemIface->register_property("Present", true);
Feist, Jamesc95cf672019-08-29 16:10:35 -0700256 hsbpItemIface->register_property("PrettyName", name);
257 hsbpItemIface->initialize();
258
James Feist45772222019-09-27 10:38:08 -0700259 versionIface =
260 objServer.add_interface(hsbpItemIface->get_object_path(),
261 "xyz.openbmc_project.Software.Version");
262 versionIface->register_property("Version", zeroPad(bootVer) + "." +
263 zeroPad(fpgaVer) + "." +
264 zeroPad(securityRev));
265 versionIface->register_property(
266 "Purpose",
267 std::string(
268 "xyz.openbmc_project.Software.Version.VersionPurpose.HSBP"));
269 versionIface->initialize();
270 getPresence(presence);
271 getIFDET(ifdet);
272
273 createDrives();
274
275 runTimer();
276 }
277
278 void runTimer()
279 {
280 timer->expires_after(std::chrono::seconds(scanRateSeconds));
281 timer->async_wait([this](boost::system::error_code ec) {
282 if (ec == boost::asio::error::operation_aborted)
283 {
284 // we're being destroyed
285 return;
286 }
287 else if (ec)
288 {
289 std::cerr << "timer error " << ec.message() << "\n";
290 return;
291 }
James Feist9f6565d2019-10-09 13:15:13 -0700292
293 if (!isPowerOn())
294 {
295 // can't access hsbp when power is off
296 runTimer();
297 return;
298 }
James Feist45772222019-09-27 10:38:08 -0700299 uint8_t curPresence = 0;
300 uint8_t curIFDET = 0;
301 uint8_t curFailed = 0;
James Feist244f3232019-09-27 15:15:14 -0700302 uint8_t curRebuild = 0;
James Feist45772222019-09-27 10:38:08 -0700303
304 getPresence(curPresence);
305 getIFDET(curIFDET);
306 getFailed(curFailed);
James Feist244f3232019-09-27 15:15:14 -0700307 getRebuild(curRebuild);
James Feist45772222019-09-27 10:38:08 -0700308
309 if (curPresence != presence || curIFDET != ifdet ||
James Feist244f3232019-09-27 15:15:14 -0700310 curFailed != failed || curRebuild != rebuilding)
James Feist45772222019-09-27 10:38:08 -0700311 {
312 presence = curPresence;
313 ifdet = curIFDET;
314 failed = curFailed;
James Feist244f3232019-09-27 15:15:14 -0700315 rebuilding = curRebuild;
James Feist45772222019-09-27 10:38:08 -0700316 updateDrives();
317 }
318 runTimer();
319 });
320 }
321
322 void createDrives()
323 {
324 uint8_t nvme = ifdet ^ presence;
325 for (size_t ii = 0; ii < maxDrives; ii++)
Feist, Jamesc95cf672019-08-29 16:10:35 -0700326 {
James Feist45772222019-09-27 10:38:08 -0700327 bool isNvme = nvme & (1 << ii);
328 bool isPresent = isNvme || (presence & (1 << ii));
329 bool isFailed = !isPresent || failed & (1 << ii);
James Feist244f3232019-09-27 15:15:14 -0700330 bool isRebuilding = !isPresent && (rebuilding & (1 << ii));
James Feist45772222019-09-27 10:38:08 -0700331
332 // +1 to convert from 0 based to 1 based
333 size_t driveIndex = (backplaneIndex * maxDrives) + ii + 1;
James Feist09dd2312019-10-09 09:29:03 -0700334 Drive& drive = drives.emplace_back(driveIndex, isPresent, !isFailed,
335 isNvme, isRebuilding);
336 std::shared_ptr<Led> led = leds.emplace_back(std::make_shared<Led>(
337 drive.itemIface->get_object_path(), ii, file));
338 led->createInterface();
Feist, Jamesc95cf672019-08-29 16:10:35 -0700339 }
340 }
341
James Feist45772222019-09-27 10:38:08 -0700342 void updateDrives()
Feist, Jamesc95cf672019-08-29 16:10:35 -0700343 {
James Feist45772222019-09-27 10:38:08 -0700344
345 uint8_t nvme = ifdet ^ presence;
346 for (size_t ii = 0; ii < maxDrives; ii++)
Feist, Jamesc95cf672019-08-29 16:10:35 -0700347 {
James Feist45772222019-09-27 10:38:08 -0700348 bool isNvme = nvme & (1 << ii);
349 bool isPresent = isNvme || (presence & (1 << ii));
James Feist244f3232019-09-27 15:15:14 -0700350 bool isFailed = !isPresent || (failed & (1 << ii));
351 bool isRebuilding = isPresent && (rebuilding & (1 << ii));
James Feist45772222019-09-27 10:38:08 -0700352
353 Drive& drive = drives[ii];
354 drive.isNvme = isNvme;
355 drive.itemIface->set_property("Present", isPresent);
356 drive.operationalIface->set_property("Functional", !isFailed);
James Feist244f3232019-09-27 15:15:14 -0700357 drive.rebuildingIface->set_property("Rebuilding", isRebuilding);
Feist, Jamesc95cf672019-08-29 16:10:35 -0700358 }
James Feist45772222019-09-27 10:38:08 -0700359 }
360
361 bool getPresent()
362 {
363 present = i2c_smbus_read_byte(file) >= 0;
Feist, Jamesc95cf672019-08-29 16:10:35 -0700364 return present;
365 }
James Feist45772222019-09-27 10:38:08 -0700366
367 bool getTypeID(uint8_t& val)
368 {
369 constexpr uint8_t addr = 2;
370 int ret = i2c_smbus_read_byte_data(file, addr);
371 if (ret < 0)
372 {
373 std::cerr << "Error " << __FUNCTION__ << "\n";
374 return false;
375 }
376 val = static_cast<uint8_t>(ret);
377 return true;
378 }
379
380 bool getBootVer(uint8_t& val)
381 {
382 constexpr uint8_t addr = 3;
383 int ret = i2c_smbus_read_byte_data(file, addr);
384 if (ret < 0)
385 {
386 std::cerr << "Error " << __FUNCTION__ << "\n";
387 return false;
388 }
389 val = static_cast<uint8_t>(ret);
390 return true;
391 }
392
393 bool getFPGAVer(uint8_t& val)
394 {
395 constexpr uint8_t addr = 4;
396 int ret = i2c_smbus_read_byte_data(file, addr);
397 if (ret < 0)
398 {
399 std::cerr << "Error " << __FUNCTION__ << "\n";
400 return false;
401 }
402 val = static_cast<uint8_t>(ret);
403 return true;
404 }
405
406 bool getSecurityRev(uint8_t& val)
407 {
408 constexpr uint8_t addr = 5;
409 int ret = i2c_smbus_read_byte_data(file, addr);
410 if (ret < 0)
411 {
412 std::cerr << "Error " << __FUNCTION__ << "\n";
413 return false;
414 }
415 val = static_cast<uint8_t>(ret);
416 return true;
417 }
418
419 bool getPresence(uint8_t& val)
420 {
421 // NVMe drives do not assert PRSNTn, and as such do not get reported as
422 // PRESENT in this register
423
424 constexpr uint8_t addr = 8;
425
426 int ret = i2c_smbus_read_byte_data(file, addr);
427 if (ret < 0)
428 {
429 std::cerr << "Error " << __FUNCTION__ << "\n";
430 return false;
431 }
432 // presence is inverted
433 val = static_cast<uint8_t>(~ret);
434 return true;
435 }
436
437 bool getIFDET(uint8_t& val)
438 {
439 // This register is a bitmap of parallel GPIO pins connected to the
440 // IFDETn pin of a drive slot. SATA, SAS, and NVMe drives all assert
441 // IFDETn low when they are inserted into the HSBP.This register, in
442 // combination with the PRESENCE register, are used by the BMC to detect
443 // the presence of NVMe drives.
444
445 constexpr uint8_t addr = 9;
446
447 int ret = i2c_smbus_read_byte_data(file, addr);
448 if (ret < 0)
449 {
450 std::cerr << "Error " << __FUNCTION__ << "\n";
451 return false;
452 }
453 // ifdet is inverted
454 val = static_cast<uint8_t>(~ret);
455 return true;
456 }
457
458 bool getFailed(uint8_t& val)
459 {
460 constexpr uint8_t addr = 0xC;
461 int ret = i2c_smbus_read_byte_data(file, addr);
462 if (ret < 0)
463 {
464 std::cerr << "Error " << __FUNCTION__ << "\n";
465 return false;
466 }
467 val = static_cast<uint8_t>(ret);
468 return true;
469 }
470
471 bool getRebuild(uint8_t& val)
472 {
473 constexpr uint8_t addr = 0xD;
474 int ret = i2c_smbus_read_byte_data(file, addr);
475 if (ret < 0)
476 {
477 std::cerr << "Error " << __FUNCTION__ << "\n";
478 return false;
479 }
480 val = static_cast<uint8_t>(ret);
481 return true;
482 }
483
James Feist09dd2312019-10-09 09:29:03 -0700484 virtual ~Backplane()
Feist, Jamesc95cf672019-08-29 16:10:35 -0700485 {
486 objServer.remove_interface(hsbpItemIface);
James Feist45772222019-09-27 10:38:08 -0700487 objServer.remove_interface(versionIface);
Feist, Jamesc95cf672019-08-29 16:10:35 -0700488 if (file >= 0)
489 {
490 close(file);
491 }
492 }
493
494 size_t bus;
495 size_t address;
James Feist45772222019-09-27 10:38:08 -0700496 size_t backplaneIndex;
Feist, Jamesc95cf672019-08-29 16:10:35 -0700497 std::string name;
James Feist45772222019-09-27 10:38:08 -0700498 std::shared_ptr<boost::asio::steady_timer> timer;
499 bool present = false;
500 uint8_t typeId = 0;
501 uint8_t bootVer = 0;
502 uint8_t fpgaVer = 0;
503 uint8_t securityRev = 0;
504 uint8_t funSupported = 0;
505 uint8_t presence = 0;
506 uint8_t ifdet = 0;
507 uint8_t failed = 0;
James Feist244f3232019-09-27 15:15:14 -0700508 uint8_t rebuilding = 0;
James Feist45772222019-09-27 10:38:08 -0700509
510 int file = -1;
511
Feist, Jamesc95cf672019-08-29 16:10:35 -0700512 std::string type;
513
514 std::shared_ptr<sdbusplus::asio::dbus_interface> hsbpItemIface;
James Feist45772222019-09-27 10:38:08 -0700515 std::shared_ptr<sdbusplus::asio::dbus_interface> versionIface;
516
517 std::vector<Drive> drives;
James Feist09dd2312019-10-09 09:29:03 -0700518 std::vector<std::shared_ptr<Led>> leds;
James Feist0b236ab2019-10-02 09:09:16 -0700519 std::shared_ptr<std::vector<Mux>> muxes;
Feist, Jamesc95cf672019-08-29 16:10:35 -0700520};
521
522std::unordered_map<std::string, Backplane> backplanes;
523
James Feistdb2e0e72019-10-07 16:34:06 -0700524void createDriveInterfaces(size_t referenceCount)
525{
526 if (referenceCount != 1)
527 {
528 return;
529 }
530 for (auto& [name, backplane] : backplanes)
531 {
532 for (Drive& drive : backplane.drives)
533 {
534 drive.createDriveIface();
535 }
536 }
537}
538
James Feist0b236ab2019-10-02 09:09:16 -0700539void updateAssociations()
540{
541 constexpr const char* driveType =
542 "xyz.openbmc_project.Inventory.Item.Drive";
543
544 conn->async_method_call(
545 [](const boost::system::error_code ec, const GetSubTreeType& subtree) {
546 if (ec)
547 {
548 std::cerr << "Error contacting mapper " << ec.message() << "\n";
549 return;
550 }
551 for (const auto& [path, objDict] : subtree)
552 {
553 if (objDict.empty())
554 {
555 continue;
556 }
557
558 const std::string& owner = objDict.begin()->first;
James Feistdb2e0e72019-10-07 16:34:06 -0700559 // we export this interface too
560 if (owner == busName)
561 {
562 continue;
563 }
564 std::shared_ptr<bool> referenceCount = std::make_shared<bool>();
James Feist0b236ab2019-10-02 09:09:16 -0700565 conn->async_method_call(
James Feistdb2e0e72019-10-07 16:34:06 -0700566 [path, referenceCount](
567 const boost::system::error_code ec2,
568 const boost::container::flat_map<
569 std::string, std::variant<uint64_t>>& values) {
James Feist0b236ab2019-10-02 09:09:16 -0700570 if (ec2)
571 {
572 std::cerr << "Error Getting Config "
573 << ec2.message() << " " << __FUNCTION__
574 << "\n";
James Feistdb2e0e72019-10-07 16:34:06 -0700575 createDriveInterfaces(referenceCount.use_count());
James Feist0b236ab2019-10-02 09:09:16 -0700576 return;
577 }
578 auto findBus = values.find("Bus");
579 auto findIndex = values.find("Index");
580
581 if (findBus == values.end() ||
582 findIndex == values.end())
583 {
584 std::cerr << "Illegal interface at " << path
585 << "\n";
James Feistdb2e0e72019-10-07 16:34:06 -0700586 createDriveInterfaces(referenceCount.use_count());
James Feist0b236ab2019-10-02 09:09:16 -0700587 return;
588 }
589
590 size_t muxBus = static_cast<size_t>(
591 std::get<uint64_t>(findBus->second));
592 size_t driveIndex = static_cast<size_t>(
593 std::get<uint64_t>(findIndex->second));
594 std::filesystem::path muxPath =
595 "/sys/bus/i2c/devices/i2c-" +
596 std::to_string(muxBus) + "/mux_device";
597 if (!std::filesystem::is_symlink(muxPath))
598 {
599 std::cerr << path << " mux does not exist\n";
James Feistdb2e0e72019-10-07 16:34:06 -0700600 createDriveInterfaces(referenceCount.use_count());
James Feist0b236ab2019-10-02 09:09:16 -0700601 return;
602 }
603
604 // we should be getting something of the form 7-0052 for
605 // bus 7 addr 52
606 std::string fname =
607 std::filesystem::read_symlink(muxPath).filename();
608 auto findDash = fname.find('-');
609
610 if (findDash == std::string::npos ||
611 findDash + 1 >= fname.size())
612 {
613 std::cerr << path << " mux path invalid\n";
James Feistdb2e0e72019-10-07 16:34:06 -0700614 createDriveInterfaces(referenceCount.use_count());
James Feist0b236ab2019-10-02 09:09:16 -0700615 return;
616 }
617
618 std::string busStr = fname.substr(0, findDash);
619 std::string muxStr = fname.substr(findDash + 1);
620
621 size_t bus = static_cast<size_t>(std::stoi(busStr));
622 size_t addr =
623 static_cast<size_t>(std::stoi(muxStr, nullptr, 16));
624 Backplane* parent = nullptr;
625 for (auto& [name, backplane] : backplanes)
626 {
627 for (const Mux& mux : *(backplane.muxes))
628 {
629 if (bus == mux.bus && addr == mux.address)
630 {
631 parent = &backplane;
632 break;
633 }
634 }
635 }
636 if (parent == nullptr)
637 {
638 std::cerr << "Failed to find mux at bus " << bus
639 << ", addr " << addr << "\n";
James Feistdb2e0e72019-10-07 16:34:06 -0700640 createDriveInterfaces(referenceCount.use_count());
James Feist0b236ab2019-10-02 09:09:16 -0700641 return;
642 }
643 if (parent->drives.size() <= driveIndex)
644 {
645
646 std::cerr << "Illegal drive index at " << path
647 << " " << driveIndex << "\n";
James Feistdb2e0e72019-10-07 16:34:06 -0700648 createDriveInterfaces(referenceCount.use_count());
James Feist0b236ab2019-10-02 09:09:16 -0700649 return;
650 }
651 Drive& drive = parent->drives[driveIndex];
James Feistdb2e0e72019-10-07 16:34:06 -0700652 drive.removeDriveIface();
James Feist0b236ab2019-10-02 09:09:16 -0700653 drive.createAssociation(path);
James Feistdb2e0e72019-10-07 16:34:06 -0700654 createDriveInterfaces(referenceCount.use_count());
James Feist0b236ab2019-10-02 09:09:16 -0700655 },
656 owner, path, "org.freedesktop.DBus.Properties", "GetAll",
657 "xyz.openbmc_project.Inventory.Item.Drive");
658 }
659 },
660 mapper::busName, mapper::path, mapper::interface, mapper::subtree, "/",
661 0, std::array<const char*, 1>{driveType});
662}
663
664void populateMuxes(std::shared_ptr<std::vector<Mux>> muxes,
665 std::string& rootPath)
666{
667 const static std::array<const std::string, 4> muxTypes = {
668 "xyz.openbmc_project.Configuration.PCA9543Mux",
669 "xyz.openbmc_project.Configuration.PCA9544Mux",
670 "xyz.openbmc_project.Configuration.PCA9545Mux",
671 "xyz.openbmc_project.Configuration.PCA9546Mux"};
672 conn->async_method_call(
673 [muxes](const boost::system::error_code ec,
674 const GetSubTreeType& subtree) {
675 if (ec)
676 {
677 std::cerr << "Error contacting mapper " << ec.message() << "\n";
678 return;
679 }
680 std::shared_ptr<std::function<void()>> callback =
681 std::make_shared<std::function<void()>>(
682 []() { updateAssociations(); });
683 for (const auto& [path, objDict] : subtree)
684 {
685 if (objDict.empty() || objDict.begin()->second.empty())
686 {
687 continue;
688 }
689
690 const std::string& owner = objDict.begin()->first;
691 const std::vector<std::string>& interfaces =
692 objDict.begin()->second;
693
694 const std::string* interface = nullptr;
695 for (const std::string& iface : interfaces)
696 {
697 if (std::find(muxTypes.begin(), muxTypes.end(), iface) !=
698 muxTypes.end())
699 {
700 interface = &iface;
701 break;
702 }
703 }
704 if (interface == nullptr)
705 {
706 std::cerr << "Cannot get mux type\n";
707 continue;
708 }
709
710 conn->async_method_call(
711 [path, muxes, callback](
712 const boost::system::error_code ec2,
713 const boost::container::flat_map<
714 std::string, std::variant<uint64_t>>& values) {
715 if (ec2)
716 {
717 std::cerr << "Error Getting Config "
718 << ec2.message() << " " << __FUNCTION__
719 << "\n";
720 return;
721 }
722 auto findBus = values.find("Bus");
723 auto findAddress = values.find("Address");
724 if (findBus == values.end() ||
725 findAddress == values.end())
726 {
727 std::cerr << "Illegal configuration at " << path
728 << "\n";
729 return;
730 }
731 size_t bus = static_cast<size_t>(
732 std::get<uint64_t>(findBus->second));
733 size_t address = static_cast<size_t>(
734 std::get<uint64_t>(findAddress->second));
735 muxes->emplace_back(bus, address);
736 if (callback.use_count() == 1)
737 {
738 (*callback)();
739 }
740 },
741 owner, path, "org.freedesktop.DBus.Properties", "GetAll",
742 *interface);
743 }
744 },
745 mapper::busName, mapper::path, mapper::interface, mapper::subtree,
746 rootPath, 1, muxTypes);
747}
748
Feist, Jamesc95cf672019-08-29 16:10:35 -0700749void populate()
750{
751 conn->async_method_call(
752 [](const boost::system::error_code ec, const GetSubTreeType& subtree) {
753 if (ec)
754 {
755 std::cerr << "Error contacting mapper " << ec.message() << "\n";
756 return;
757 }
758 for (const auto& [path, objDict] : subtree)
759 {
760 if (objDict.empty())
761 {
762 continue;
763 }
764
765 const std::string& owner = objDict.begin()->first;
766 conn->async_method_call(
767 [path](const boost::system::error_code ec2,
768 const boost::container::flat_map<
769 std::string, BasicVariantType>& resp) {
770 if (ec2)
771 {
772 std::cerr << "Error Getting Config "
773 << ec2.message() << "\n";
774 return;
775 }
776 backplanes.clear();
777 std::optional<size_t> bus;
778 std::optional<size_t> address;
James Feist45772222019-09-27 10:38:08 -0700779 std::optional<size_t> backplaneIndex;
Feist, Jamesc95cf672019-08-29 16:10:35 -0700780 std::optional<std::string> name;
781 for (const auto& [key, value] : resp)
782 {
783 if (key == "Bus")
784 {
785 bus = std::get<uint64_t>(value);
786 }
787 else if (key == "Address")
788 {
789 address = std::get<uint64_t>(value);
790 }
James Feist45772222019-09-27 10:38:08 -0700791 else if (key == "Index")
792 {
793 backplaneIndex = std::get<uint64_t>(value);
794 }
Feist, Jamesc95cf672019-08-29 16:10:35 -0700795 else if (key == "Name")
796 {
797 name = std::get<std::string>(value);
798 }
799 }
James Feist45772222019-09-27 10:38:08 -0700800 if (!bus || !address || !name || !backplaneIndex)
Feist, Jamesc95cf672019-08-29 16:10:35 -0700801 {
802 std::cerr << "Illegal configuration at " << path
803 << "\n";
804 return;
805 }
James Feist0b236ab2019-10-02 09:09:16 -0700806 std::string parentPath =
807 std::filesystem::path(path).parent_path();
Feist, Jamesc95cf672019-08-29 16:10:35 -0700808 const auto& [backplane, status] = backplanes.emplace(
James Feist45772222019-09-27 10:38:08 -0700809 *name,
810 Backplane(*bus, *address, *backplaneIndex, *name));
Feist, Jamesc95cf672019-08-29 16:10:35 -0700811 backplane->second.run();
James Feist0b236ab2019-10-02 09:09:16 -0700812 populateMuxes(backplane->second.muxes, parentPath);
Feist, Jamesc95cf672019-08-29 16:10:35 -0700813 },
814 owner, path, "org.freedesktop.DBus.Properties", "GetAll",
815 configType);
816 }
817 },
818 mapper::busName, mapper::path, mapper::interface, mapper::subtree, "/",
819 0, std::array<const char*, 1>{configType});
820}
821
822int main()
823{
824 boost::asio::steady_timer callbackTimer(io);
825
James Feistdb2e0e72019-10-07 16:34:06 -0700826 conn->request_name(busName);
Feist, Jamesc95cf672019-08-29 16:10:35 -0700827
828 sdbusplus::bus::match::match match(
829 *conn,
830 "type='signal',member='PropertiesChanged',arg0='" +
831 std::string(configType) + "'",
832 [&callbackTimer](sdbusplus::message::message&) {
833 callbackTimer.expires_after(std::chrono::seconds(2));
834 callbackTimer.async_wait([](const boost::system::error_code ec) {
835 if (ec == boost::asio::error::operation_aborted)
836 {
837 // timer was restarted
838 return;
839 }
840 else if (ec)
841 {
842 std::cerr << "Timer error" << ec.message() << "\n";
843 return;
844 }
845 populate();
846 });
847 });
848
James Feist0b236ab2019-10-02 09:09:16 -0700849 sdbusplus::bus::match::match drive(
850 *conn,
851 "type='signal',member='PropertiesChanged',arg0='xyz.openbmc_project."
852 "Inventory.Item.Drive'",
James Feistdb2e0e72019-10-07 16:34:06 -0700853 [&callbackTimer](sdbusplus::message::message& message) {
James Feist0b236ab2019-10-02 09:09:16 -0700854 callbackTimer.expires_after(std::chrono::seconds(2));
James Feistdb2e0e72019-10-07 16:34:06 -0700855 if (message.get_sender() == conn->get_unique_name())
856 {
857 return;
858 }
James Feist0b236ab2019-10-02 09:09:16 -0700859 callbackTimer.async_wait([](const boost::system::error_code ec) {
860 if (ec == boost::asio::error::operation_aborted)
861 {
862 // timer was restarted
863 return;
864 }
865 else if (ec)
866 {
867 std::cerr << "Timer error" << ec.message() << "\n";
868 return;
869 }
870 populate();
871 });
872 });
873
Feist, Jamesc95cf672019-08-29 16:10:35 -0700874 io.post([]() { populate(); });
James Feist9f6565d2019-10-09 13:15:13 -0700875 setupPowerMatch(conn);
Feist, Jamesc95cf672019-08-29 16:10:35 -0700876 io.run();
877}