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 <algorithm>
20 #include <bitset>
21 #include <boost/algorithm/string/replace.hpp>
22 #include <boost/asio/posix/stream_descriptor.hpp>
23 #include <boost/asio/steady_timer.hpp>
24 #include <boost/container/flat_set.hpp>
25 #include <filesystem>
26 #include <forward_list>
27 #include <fstream>
28 #include <gpiod.hpp>
29 #include <iostream>
30 #include <list>
31 #include <sdbusplus/asio/connection.hpp>
32 #include <sdbusplus/asio/object_server.hpp>
33 #include <sdbusplus/bus/match.hpp>
34 #include <string>
35 #include <utility>
36 
37 extern "C" {
38 #include <i2c/smbus.h>
39 #include <linux/i2c-dev.h>
40 }
41 
42 /****************************************************************************/
43 /******************** Global Constants/Type Declarations ********************/
44 /****************************************************************************/
45 constexpr const char* hsbpCpldInft =
46     "xyz.openbmc_project.Configuration.Intel_HSBP_CPLD";
47 constexpr const char* hsbpConfigIntf =
48     "xyz.openbmc_project.Configuration.HSBPConfiguration";
49 constexpr const char* nvmeIntf = "xyz.openbmc_project.Inventory.Item.NVMe";
50 constexpr const char* busName = "xyz.openbmc_project.HsbpManager";
51 
52 constexpr size_t scanRateSeconds = 5;
53 constexpr size_t maxDrives = 8; // only 1 byte alloted
54 
55 using NvmeMapping = std::vector<std::string>;
56 /***************************** End of Section *******************************/
57 
58 /****************************************************************************/
59 /**************************** Enums Definitions *****************************/
60 /****************************************************************************/
61 enum class AppState : uint8_t
62 {
63     idle,
64     loadingHsbpConfig,
65     hsbpConfigLoaded,
66     loadingComponents,
67     componentsLoaded,
68     loadingBackplanes,
69     backplanesLoaded,
70     loadingDrives,
71     drivesLoaded
72 };
73 
74 enum class BlinkPattern : uint8_t
75 {
76     off = 0x0,
77     error = 0x2,
78     terminate = 0x3
79 };
80 /***************************** End of Section *******************************/
81 
82 /****************************************************************************/
83 /************ HSBP Configuration related struct/class Definitions ***********/
84 /****************************************************************************/
85 struct HsbpConfig
86 {
87     size_t rootBus;
88     std::vector<std::string> supportedHsbps;
89     std::unordered_map<std::string, NvmeMapping> hsbpNvmeMap;
90     std::vector<std::string> clockBufferTypes;
91     std::vector<std::string> ioExpanderTypes;
92 
93     void clearConfig()
94     {
95         rootBus = -1;
96         supportedHsbps.clear();
97         hsbpNvmeMap.clear();
98         clockBufferTypes.clear();
99         ioExpanderTypes.clear();
100     }
101 };
102 
103 class ClockBuffer
104 {
105     size_t bus;
106     size_t address;
107     std::string modeOfOperation;
108     size_t outCtrlBaseAddr;
109     size_t outCtrlByteCount;
110     std::unordered_map<std::string, std::vector<std::string>> byteMap;
111     std::string name;
112     std::string type;
113 
114     int file = -1;
115     bool initialized = false;
116 
117     void initialize()
118     {
119         /* Execute below operation only when mode of operation is SMBus. By
120          * default the clock buffer is configured to follow OE pin output, so we
121          * need to set the output value to 0 to disable the clock outputs. If
122          * mode of operation is IO, then the IO value will determine the
123          * disable/enable of clock output */
124         if (modeOfOperation == "SMBus")
125         {
126             if (file < 0)
127             {
128                 file = open(("/dev/i2c-" + std::to_string(bus)).c_str(),
129                             O_RDWR | O_CLOEXEC);
130                 if (file < 0)
131                 {
132                     std::cerr << "ClockBuffer : \"" << name
133                               << "\" - Unable to open bus : " << bus << "\n";
134                     return;
135                 }
136             }
137 
138             if (ioctl(file, I2C_SLAVE_FORCE, address) < 0)
139             {
140                 std::cerr << "ClockBuffer : \"" << name
141                           << "\" - Unable to set address to " << address
142                           << "\n";
143                 return;
144             }
145 
146             for (uint8_t i = 0; i < outCtrlByteCount; i++)
147             {
148                 std::string byteName = "Byte" + std::to_string(i);
149 
150                 auto byte = byteMap.find(byteName);
151                 if (byte == byteMap.end())
152                 {
153                     std::cerr << "ClockBuffer : \"" << name
154                               << "\" - Byte map error ! Unable to find "
155                               << byteName << "\n";
156                     return;
157                 }
158 
159                 /* Get current value of output control register */
160                 int read = i2c_smbus_read_byte_data(
161                     file, static_cast<uint8_t>(outCtrlBaseAddr + i));
162                 if (read < 0)
163                 {
164                     std::cerr << "ClockBuffer : \"" << name
165                               << "\" - Error: Unable to read data from clock "
166                                  "buffer register\n";
167                     return;
168                 }
169 
170                 std::bitset<8> currByte(read);
171 
172                 /* Set zero only at bit position that we have a NVMe drive (i.e.
173                  * ignore where byteMap is "-"). We do not want to touch other
174                  * bits */
175                 for (uint8_t bit = 0; bit < 8; bit++)
176                 {
177                     if (byte->second.at(bit) != "-")
178                     {
179                         currByte.reset(bit);
180                     }
181                 }
182 
183                 int ret = i2c_smbus_write_byte_data(
184                     file, static_cast<uint8_t>(outCtrlBaseAddr + i),
185                     static_cast<uint8_t>(currByte.to_ulong()));
186 
187                 if (ret < 0)
188                 {
189                     std::cerr << "ClockBuffer : \"" << name
190                               << "\" - Error: Unable to write data to clock "
191                                  "buffer register\n";
192                     return;
193                 }
194             }
195         }
196         initialized = true;
197         std::cerr << "ClockBuffer : \"" << name << "\" initialized\n";
198     }
199 
200   public:
201     ClockBuffer(
202         size_t busIn, size_t addressIn, std::string& modeOfOperationIn,
203         size_t outCtrlBaseAddrIn, size_t outCtrlByteCountIn,
204         std::unordered_map<std::string, std::vector<std::string>>& byteMapIn,
205         std::string& nameIn, std::string& typeIn) :
206         bus(busIn),
207         address(addressIn), modeOfOperation(std::move(modeOfOperationIn)),
208         outCtrlBaseAddr(outCtrlBaseAddrIn),
209         outCtrlByteCount(outCtrlByteCountIn), byteMap(std::move(byteMapIn)),
210         name(std::move(nameIn)), type(std::move(typeIn))
211     {
212         initialize();
213     }
214 
215     bool isInitialized()
216     {
217         if (!initialized)
218         {
219             /* There was an issue with the initialization of this component. Try
220              * to invoke initialization again */
221             initialize();
222         }
223         return initialized;
224     }
225 
226     std::string getName()
227     {
228         return name;
229     }
230 
231     bool enableDisableClock(std::forward_list<std::string>& nvmeDrivesInserted,
232                             std::forward_list<std::string>& nvmeDrivesRemoved)
233     {
234         if (modeOfOperation != "SMBus")
235         {
236             /* The clock is enabled using IO expander. No action needed from
237              * here */
238             return true;
239         }
240 
241         if (nvmeDrivesInserted.empty() && nvmeDrivesRemoved.empty())
242         {
243             /* There are no drives to update */
244             return true;
245         }
246 
247         for (uint8_t i = 0; i < outCtrlByteCount; i++)
248         {
249             std::string byteName = "Byte" + std::to_string(i);
250 
251             auto byte = byteMap.find(byteName);
252             if (byte == byteMap.end())
253             {
254                 std::cerr << "ClockBuffer : \"" << name
255                           << "\" - Byte map error ! Unable to find " << byteName
256                           << "\n";
257                 return false;
258             }
259 
260             /* Get current value of output control register */
261             int read = i2c_smbus_read_byte_data(
262                 file, static_cast<uint8_t>(outCtrlBaseAddr + i));
263             if (read < 0)
264             {
265                 std::cerr << "ClockBuffer : \"" << name
266                           << "\" - Error: Unable to read data from clock "
267                              "buffer register\n";
268                 return false;
269             }
270 
271             std::bitset<8> currByte(read);
272             bool writeRequired = false;
273 
274             /* Set the bit if the NVMe drive is found in nvmeDrivesInserted, and
275              * reset the bit if found in nvmeDrivesRemoved */
276             for (uint8_t bit = 0; bit < 8; bit++)
277             {
278                 /* The remove function returns number of elements removed from
279                  * list indicating the presence of the drive and also removing
280                  * it form the list */
281                 if (nvmeDrivesInserted.remove(byte->second.at(bit)))
282                 {
283                     writeRequired = true;
284                     currByte.set(bit);
285                     continue;
286                 }
287 
288                 if (nvmeDrivesRemoved.remove(byte->second.at(bit)))
289                 {
290                     writeRequired = true;
291                     currByte.reset(bit);
292                 }
293             }
294 
295             if (!writeRequired)
296             {
297                 /* No Write is required as there are no changes */
298                 continue;
299             }
300 
301             int ret = i2c_smbus_write_byte_data(
302                 file, static_cast<uint8_t>(outCtrlBaseAddr + i),
303                 static_cast<uint8_t>(currByte.to_ulong()));
304             if (ret < 0)
305             {
306                 std::cerr << "ClockBuffer : \"" << name
307                           << "\" - Error: Unable to write data to clock "
308                              "buffer register\n";
309                 return false;
310             }
311         }
312 
313         return true;
314     }
315 
316     ~ClockBuffer()
317     {
318         if (file >= 0)
319         {
320             close(file);
321         }
322     }
323 };
324 
325 class IoExpander
326 {
327     size_t bus;
328     size_t address;
329     size_t confIORegAddr;
330     size_t outCtrlBaseAddr;
331     size_t outCtrlByteCount;
332     std::unordered_map<std::string, std::vector<std::string>> ioMap;
333     std::string name;
334     std::string type;
335 
336     int file = -1;
337     bool initialized = false;
338 
339     void initialize()
340     {
341         /* Initialize the IO expander Control register to configure the IO ports
342          * as outputs and set the output to low by default */
343         if (file < 0)
344         {
345             file = open(("/dev/i2c-" + std::to_string(bus)).c_str(),
346                         O_RDWR | O_CLOEXEC);
347             if (file < 0)
348             {
349                 std::cerr << "IoExpander : " << name
350                           << " - Unable to open bus : " << bus << "\n";
351                 return;
352             }
353         }
354 
355         if (ioctl(file, I2C_SLAVE_FORCE, address) < 0)
356         {
357             std::cerr << "IoExpander : \"" << name
358                       << "\" - Unable to set address to " << address << "\n";
359             return;
360         }
361 
362         for (uint8_t i = 0; i < outCtrlByteCount; i++)
363         {
364             std::string ioName = "IO" + std::to_string(i);
365 
366             auto io = ioMap.find(ioName);
367             if (io == ioMap.end())
368             {
369                 std::cerr << "IoExpander : \"" << name
370                           << "\" - IO map error ! Unable to find " << ioName
371                           << "\n";
372                 return;
373             }
374 
375             /* Get current value of IO configuration register */
376             int read1 = i2c_smbus_read_byte_data(
377                 file, static_cast<uint8_t>(confIORegAddr + i));
378             if (read1 < 0)
379             {
380                 std::cerr << "IoExpander : \"" << name
381                           << "\" - Error: Unable to read data from io expander "
382                              "IO control register\n";
383                 return;
384             }
385 
386             /* Get current value of IO Ouput register */
387             int read2 = i2c_smbus_read_byte_data(
388                 file, static_cast<uint8_t>(confIORegAddr + i));
389             if (read2 < 0)
390             {
391                 std::cerr << "IoExpander : \"" << name
392                           << "\" - Error: Unable to read data from io expander "
393                              "IO output register\n";
394                 return;
395             }
396 
397             std::bitset<8> currCtrlVal(read1);
398             std::bitset<8> currOutVal(read2);
399 
400             /* Set zero only at bit position that we have a NVMe drive (i.e.
401              * ignore where ioMap is "-"). We do not want to touch other
402              * bits */
403             for (uint8_t bit = 0; bit < 8; bit++)
404             {
405                 if (io->second.at(bit) != "-")
406                 {
407                     currCtrlVal.reset(bit);
408                     currOutVal.reset(bit);
409                 }
410             }
411 
412             int ret1 = i2c_smbus_write_byte_data(
413                 file, static_cast<uint8_t>(confIORegAddr + i),
414                 static_cast<uint8_t>(currCtrlVal.to_ulong()));
415             if (ret1 < 0)
416             {
417                 std::cerr << "IoExpander : \"" << name
418                           << "\" - Error: Unable to write data to IO expander "
419                              "IO control register\n";
420                 return;
421             }
422 
423             int ret2 = i2c_smbus_write_byte_data(
424                 file, static_cast<uint8_t>(outCtrlBaseAddr + i),
425                 static_cast<uint8_t>(currOutVal.to_ulong()));
426             if (ret2 < 0)
427             {
428                 std::cerr << "IoExpander : \"" << name
429                           << "\" - Error: Unable to write data to IO expander "
430                              "IO output register\n";
431                 return;
432             }
433         }
434         initialized = true;
435         std::cerr << "IoExpander : \"" << name << "\" initialized\n";
436     }
437 
438   public:
439     IoExpander(
440         size_t busIn, size_t addressIn, size_t confIORegAddrIn,
441         size_t outCtrlBaseAddrIn, size_t outCtrlByteCountIn,
442         std::unordered_map<std::string, std::vector<std::string>>& ioMapIn,
443         std::string& nameIn, std::string& typeIn) :
444         bus(busIn),
445         address(addressIn), confIORegAddr(confIORegAddrIn),
446         outCtrlBaseAddr(outCtrlBaseAddrIn),
447         outCtrlByteCount(outCtrlByteCountIn), ioMap(std::move(ioMapIn)),
448         name(std::move(nameIn)), type(std::move(typeIn))
449     {
450         initialize();
451     }
452 
453     bool isInitialized()
454     {
455         if (!initialized)
456         {
457             /* There was an issue with the initialization of this component. Try
458              * to invoke initialization again */
459             initialize();
460         }
461         return initialized;
462     }
463 
464     std::string getName()
465     {
466         return name;
467     }
468 
469     bool enableDisableOuput(std::forward_list<std::string>& nvmeDrivesInserted,
470                             std::forward_list<std::string>& nvmeDrivesRemoved)
471     {
472         if (nvmeDrivesInserted.empty() && nvmeDrivesRemoved.empty())
473         {
474             /* There are no drives to update */
475             return true;
476         }
477 
478         for (uint8_t i = 0; i < outCtrlByteCount; i++)
479         {
480             std::string ioName = "IO" + std::to_string(i);
481 
482             auto io = ioMap.find(ioName);
483             if (io == ioMap.end())
484             {
485                 std::cerr << "IoExpander : \"" << name
486                           << "\" - IO map error ! Unable to find " << ioName
487                           << "\n";
488                 return false;
489             }
490 
491             /* Get current value of IO output register */
492             int read = i2c_smbus_read_byte_data(
493                 file, static_cast<uint8_t>(outCtrlBaseAddr + i));
494             if (read < 0)
495             {
496                 std::cerr << "IoExpander : \"" << name
497                           << "\" - Error: Unable to read data from io expander "
498                              "register\n";
499                 return false;
500             }
501 
502             std::bitset<8> currVal(read);
503             bool writeRequired = false;
504 
505             /* Set the bit if the NVMe drive is found in nvmeDrivesInserted, and
506              * reset the bit if found in nvmeDrivesRemoved */
507             for (uint8_t bit = 0; bit < 8; bit++)
508             {
509                 /* The remove function returns number of elements removed from
510                  * list indicating the presence of the drive and also removing
511                  * it form the list */
512                 if (nvmeDrivesInserted.remove(io->second.at(bit)))
513                 {
514                     writeRequired = true;
515                     currVal.set(bit);
516                     continue;
517                 }
518 
519                 if (nvmeDrivesRemoved.remove(io->second.at(bit)))
520                 {
521                     writeRequired = true;
522                     currVal.reset(bit);
523                 }
524             }
525 
526             if (!writeRequired)
527             {
528                 /* No Write is required as there are no changes */
529                 continue;
530             }
531 
532             int ret = i2c_smbus_write_byte_data(
533                 file, static_cast<uint8_t>(outCtrlBaseAddr + i),
534                 static_cast<uint8_t>(currVal.to_ulong()));
535             if (ret < 0)
536             {
537                 std::cerr << "IoExpander : \"" << name
538                           << "\" - Error: Unable to write data to IO expander "
539                              "register\n";
540                 return false;
541             }
542         }
543 
544         return true;
545     }
546 
547     ~IoExpander()
548     {
549         if (file >= 0)
550         {
551             close(file);
552         }
553     }
554 };
555 /***************************** End of Section *******************************/
556 
557 /****************************************************************************/
558 /*********************** Global Variables Declarations **********************/
559 /****************************************************************************/
560 /* State os Application */
561 static AppState appState = AppState::idle;
562 
563 /* Configuration and Components */
564 static HsbpConfig hsbpConfig;
565 std::forward_list<ClockBuffer> clockBuffers;
566 std::forward_list<IoExpander> ioExpanders;
567 
568 /* Boost IO context and Dbus variables */
569 boost::asio::io_context io;
570 auto conn = std::make_shared<sdbusplus::asio::connection>(io);
571 sdbusplus::asio::object_server objServer(conn);
572 
573 /* GPIO Lines and GPIO Event Descriptors */
574 static gpiod::line nvmeLvc3AlertLine;
575 static boost::asio::posix::stream_descriptor nvmeLvc3AlertEvent(io);
576 /***************************** End of Section *******************************/
577 
578 /****************************************************************************/
579 /********** HSBP Backplane related struct and Global definitions ************/
580 /****************************************************************************/
581 struct Mux
582 {
583     Mux(size_t busIn, size_t addressIn, size_t channelsIn, size_t indexIn) :
584         bus(busIn), address(addressIn), channels(channelsIn), index(indexIn)
585     {
586     }
587     size_t bus;
588     size_t address;
589     size_t channels;
590     size_t index;
591 
592     // to sort in the flat set
593     bool operator<(const Mux& rhs) const
594     {
595         return index < rhs.index;
596     }
597 };
598 
599 struct Led : std::enable_shared_from_this<Led>
600 {
601     // led pattern addresses start at 0x10
602     Led(const std::string& path, size_t index, int fd) :
603         address(static_cast<uint8_t>(index + 0x10)), file(fd),
604         ledInterface(objServer.add_interface(path, ledGroup::interface))
605     {
606         if (index >= maxDrives)
607         {
608             throw std::runtime_error("Invalid drive index");
609         }
610 
611         if (!set(BlinkPattern::off))
612         {
613             std::cerr << "Cannot initialize LED " << path << "\n";
614         }
615     }
616 
617     // this has to be called outside the constructor for shared_from_this to
618     // work
619     void createInterface(void)
620     {
621         std::shared_ptr<Led> self = shared_from_this();
622 
623         ledInterface->register_property(
624             ledGroup::asserted, false, [self](const bool req, bool& val) {
625                 if (req == val)
626                 {
627                     return 1;
628                 }
629 
630                 if (!isPowerOn())
631                 {
632                     std::cerr << "Can't change blink state when power is off\n";
633                     throw std::runtime_error(
634                         "Can't change blink state when power is off");
635                 }
636                 BlinkPattern pattern =
637                     req ? BlinkPattern::error : BlinkPattern::terminate;
638                 if (!self->set(pattern))
639                 {
640                     std::cerr << "Can't change blink pattern\n";
641                     throw std::runtime_error("Cannot set blink pattern");
642                 }
643                 val = req;
644                 return 1;
645             });
646         ledInterface->initialize();
647     }
648 
649     virtual ~Led()
650     {
651         objServer.remove_interface(ledInterface);
652     }
653 
654     bool set(BlinkPattern pattern)
655     {
656         int ret = i2c_smbus_write_byte_data(file, address,
657                                             static_cast<uint8_t>(pattern));
658         return ret >= 0;
659     }
660 
661     uint8_t address;
662     int file;
663     std::shared_ptr<sdbusplus::asio::dbus_interface> ledInterface;
664 };
665 
666 struct Drive
667 {
668     Drive(std::string driveName, bool present, bool isOperational, bool nvme,
669           bool rebuilding) :
670         isNvme(nvme),
671         isPresent(present), name(driveName)
672     {
673         constexpr const char* basePath =
674             "/xyz/openbmc_project/inventory/item/drive/";
675         itemIface =
676             objServer.add_interface(basePath + driveName, inventory::interface);
677         itemIface->register_property("Present", isPresent);
678         itemIface->register_property("PrettyName", driveName);
679         itemIface->initialize();
680         operationalIface = objServer.add_interface(
681             itemIface->get_object_path(),
682             "xyz.openbmc_project.State.Decorator.OperationalStatus");
683 
684         operationalIface->register_property(
685             "Functional", isOperational,
686             [this](const bool req, bool& property) {
687                 if (!isPresent)
688                 {
689                     return 0;
690                 }
691                 if (property == req)
692                 {
693                     return 1;
694                 }
695                 property = req;
696                 if (req)
697                 {
698                     clearFailed();
699                     return 1;
700                 }
701                 markFailed();
702                 return 1;
703             });
704 
705         operationalIface->initialize();
706         rebuildingIface = objServer.add_interface(
707             itemIface->get_object_path(), "xyz.openbmc_project.State.Drive");
708         rebuildingIface->register_property("Rebuilding", rebuilding);
709         rebuildingIface->initialize();
710         driveIface =
711             objServer.add_interface(itemIface->get_object_path(),
712                                     "xyz.openbmc_project.Inventory.Item.Drive");
713         driveIface->initialize();
714         associations = objServer.add_interface(itemIface->get_object_path(),
715                                                association::interface);
716         associations->register_property("Associations",
717                                         std::vector<Association>{});
718         associations->initialize();
719 
720         if (isPresent && (!isOperational || rebuilding))
721         {
722             markFailed();
723         }
724     }
725     virtual ~Drive()
726     {
727         objServer.remove_interface(itemIface);
728         objServer.remove_interface(operationalIface);
729         objServer.remove_interface(rebuildingIface);
730         objServer.remove_interface(assetIface);
731         objServer.remove_interface(driveIface);
732         objServer.remove_interface(associations);
733     }
734 
735     void removeAsset()
736     {
737         objServer.remove_interface(assetIface);
738         assetIface = nullptr;
739     }
740 
741     void createAsset(
742         const boost::container::flat_map<std::string, std::string>& data)
743     {
744         if (assetIface != nullptr)
745         {
746             return;
747         }
748         assetIface = objServer.add_interface(
749             itemIface->get_object_path(),
750             "xyz.openbmc_project.Inventory.Decorator.Asset");
751         for (const auto& [key, value] : data)
752         {
753             assetIface->register_property(key, value);
754             if (key == "SerialNumber")
755             {
756                 serialNumber = value;
757                 serialNumberInitialized = true;
758             }
759         }
760         assetIface->initialize();
761     }
762 
763     void markFailed(void)
764     {
765         // todo: maybe look this up via mapper
766         constexpr const char* globalInventoryPath =
767             "/xyz/openbmc_project/CallbackManager";
768 
769         if (!isPresent)
770         {
771             return;
772         }
773 
774         operationalIface->set_property("Functional", false);
775         std::vector<Association> warning = {
776             {"", "warning", globalInventoryPath}};
777         associations->set_property("Associations", warning);
778         logDriveError("Drive " + name);
779     }
780 
781     void clearFailed(void)
782     {
783         operationalIface->set_property("Functional", true);
784         associations->set_property("Associations", std::vector<Association>{});
785     }
786 
787     void setPresent(bool set)
788     {
789         // nvme drives get detected by their fru
790         if (set == isPresent)
791         {
792             return;
793         }
794         itemIface->set_property("Present", set);
795         isPresent = set;
796     }
797 
798     void logPresent()
799     {
800         if (isNvme && !serialNumberInitialized)
801         {
802             // wait until NVMe asset is updated to include the serial number
803             // from the NVMe drive
804             return;
805         }
806 
807         if (!isPresent && loggedPresent)
808         {
809             loggedPresent = false;
810             logDeviceRemoved("Drive", name, serialNumber);
811             serialNumber = "N/A";
812             serialNumberInitialized = false;
813             removeAsset();
814         }
815         else if (isPresent && !loggedPresent)
816         {
817             loggedPresent = true;
818             logDeviceAdded("Drive", name, serialNumber);
819         }
820     }
821 
822     std::shared_ptr<sdbusplus::asio::dbus_interface> itemIface;
823     std::shared_ptr<sdbusplus::asio::dbus_interface> operationalIface;
824     std::shared_ptr<sdbusplus::asio::dbus_interface> rebuildingIface;
825     std::shared_ptr<sdbusplus::asio::dbus_interface> assetIface;
826     std::shared_ptr<sdbusplus::asio::dbus_interface> driveIface;
827     std::shared_ptr<sdbusplus::asio::dbus_interface> associations;
828 
829     bool isNvme;
830     bool isPresent;
831     std::string name;
832     std::string serialNumber = "N/A";
833     bool serialNumberInitialized = false;
834     bool loggedPresent = false;
835 };
836 
837 struct Backplane : std::enable_shared_from_this<Backplane>
838 {
839 
840     Backplane(size_t busIn, size_t addressIn, size_t backplaneIndexIn,
841               const std::string& nameIn) :
842         bus(busIn),
843         address(addressIn), backplaneIndex(backplaneIndexIn - 1), name(nameIn),
844         timer(boost::asio::steady_timer(io)),
845         muxes(std::make_shared<boost::container::flat_set<Mux>>())
846     {
847     }
848     void populateAsset(const std::string& rootPath, const std::string& busname)
849     {
850         conn->async_method_call(
851             [assetIface{assetInterface}, hsbpIface{hsbpItemIface}](
852                 const boost::system::error_code ec,
853                 const boost::container::flat_map<
854                     std::string, std::variant<std::string>>& values) mutable {
855                 if (ec)
856                 {
857                     std::cerr
858                         << "Error getting asset tag from HSBP configuration\n";
859 
860                     return;
861                 }
862                 assetIface = objServer.add_interface(
863                     hsbpIface->get_object_path(), assetTag);
864                 for (const auto& [key, value] : values)
865                 {
866                     const std::string* ptr = std::get_if<std::string>(&value);
867                     if (ptr == nullptr)
868                     {
869                         std::cerr << key << " Invalid type!\n";
870                         continue;
871                     }
872                     assetIface->register_property(key, *ptr);
873                 }
874                 assetIface->initialize();
875             },
876             busname, rootPath, "org.freedesktop.DBus.Properties", "GetAll",
877             assetTag);
878     }
879 
880     static std::string zeroPad(const uint8_t val)
881     {
882         std::ostringstream version;
883         version << std::setw(2) << std::setfill('0')
884                 << static_cast<size_t>(val);
885         return version.str();
886     }
887 
888     void run(const std::string& rootPath, const std::string& busname)
889     {
890         file = open(("/dev/i2c-" + std::to_string(bus)).c_str(),
891                     O_RDWR | O_CLOEXEC);
892         if (file < 0)
893         {
894             std::cerr << "unable to open bus " << bus << "\n";
895             return;
896         }
897 
898         if (ioctl(file, I2C_SLAVE_FORCE, address) < 0)
899         {
900             std::cerr << "unable to set address to " << address << "\n";
901             return;
902         }
903 
904         if (!getPresent())
905         {
906             std::cerr << "Cannot detect CPLD\n";
907             return;
908         }
909 
910         getBootVer(bootVer);
911         getFPGAVer(fpgaVer);
912         getSecurityRev(securityRev);
913         std::string dbusName = boost::replace_all_copy(name, " ", "_");
914         hsbpItemIface = objServer.add_interface(
915             "/xyz/openbmc_project/inventory/item/hsbp/" + dbusName,
916             inventory::interface);
917         hsbpItemIface->register_property("Present", true);
918         hsbpItemIface->register_property("PrettyName", name);
919         hsbpItemIface->initialize();
920 
921         storageInterface = objServer.add_interface(
922             hsbpItemIface->get_object_path(),
923             "xyz.openbmc_project.Inventory.Item.StorageController");
924         storageInterface->initialize();
925 
926         versionIface =
927             objServer.add_interface("/xyz/openbmc_project/software/" + dbusName,
928                                     "xyz.openbmc_project.Software.Version");
929         versionIface->register_property("Version", zeroPad(bootVer) + "." +
930                                                        zeroPad(fpgaVer) + "." +
931                                                        zeroPad(securityRev));
932         versionIface->register_property(
933             "Purpose",
934             std::string(
935                 "xyz.openbmc_project.Software.Version.VersionPurpose.HSBP"));
936         versionIface->initialize();
937 
938         auto activationIface =
939             objServer.add_interface("/xyz/openbmc_project/software/" + dbusName,
940                                     "xyz.openbmc_project.Software.Activation");
941 
942         activationIface->register_property(
943             "Activation",
944             std::string(
945                 "xyz.openbmc_project.Software.Activation.Activations.Active"));
946         activationIface->register_property(
947             "RequestedActivation",
948             std::string("xyz.openbmc_project.Software.Activation."
949                         "RequestedActivations.None"));
950 
951         activationIface->initialize();
952 
953         getPresence(presence);
954         getIFDET(ifdet);
955 
956         populateAsset(rootPath, busname);
957 
958         createDrives();
959 
960         runTimer();
961     }
962 
963     void runTimer()
964     {
965         timer.expires_after(std::chrono::seconds(scanRateSeconds));
966         timer.async_wait([weak{std::weak_ptr<Backplane>(shared_from_this())}](
967                              boost::system::error_code ec) {
968             auto self = weak.lock();
969             if (!self)
970             {
971                 return;
972             }
973             if (ec == boost::asio::error::operation_aborted)
974             {
975                 // we're being destroyed
976                 return;
977             }
978             else if (ec)
979             {
980                 std::cerr << "timer error " << ec.message() << "\n";
981                 return;
982             }
983 
984             if (!isPowerOn())
985             {
986                 // can't access hsbp when power is off
987                 self->runTimer();
988                 return;
989             }
990 
991             self->getPresence(self->presence);
992             self->getIFDET(self->ifdet);
993             self->getFailed(self->failed);
994             self->getRebuild(self->rebuilding);
995 
996             self->updateDrives();
997             self->runTimer();
998         });
999     }
1000 
1001     void createDrives()
1002     {
1003         for (size_t ii = 0; ii < maxDrives; ii++)
1004         {
1005             uint8_t driveSlot = (1 << ii);
1006             bool isNvme = ((ifdet & driveSlot) && !(presence & driveSlot));
1007             bool isPresent = isNvme || (presence & driveSlot);
1008             bool isFailed = !isPresent || failed & driveSlot;
1009             bool isRebuilding = !isPresent && (rebuilding & driveSlot);
1010 
1011             // +1 to convert from 0 based to 1 based
1012             std::string driveName = boost::replace_all_copy(name, " ", "_") +
1013                                     "_Drive_" + std::to_string(ii + 1);
1014             Drive& drive = drives.emplace_back(driveName, isPresent, !isFailed,
1015                                                isNvme, isRebuilding);
1016             std::shared_ptr<Led> led = leds.emplace_back(std::make_shared<Led>(
1017                 drive.itemIface->get_object_path(), ii, file));
1018             led->createInterface();
1019         }
1020     }
1021 
1022     void updateDrives()
1023     {
1024         size_t ii = 0;
1025 
1026         for (auto it = drives.begin(); it != drives.end(); it++, ii++)
1027         {
1028             uint8_t driveSlot = (1 << ii);
1029             bool isNvme = ((ifdet & driveSlot) && !(presence & driveSlot));
1030             bool isPresent = isNvme || (presence & driveSlot);
1031             bool isFailed = !isPresent || (failed & driveSlot);
1032             bool isRebuilding = isPresent && (rebuilding & driveSlot);
1033 
1034             it->isNvme = isNvme;
1035             it->setPresent(isPresent);
1036             it->logPresent();
1037 
1038             it->rebuildingIface->set_property("Rebuilding", isRebuilding);
1039             if (isFailed || isRebuilding)
1040             {
1041                 it->markFailed();
1042             }
1043             else
1044             {
1045                 it->clearFailed();
1046             }
1047         }
1048     }
1049 
1050     bool getPresent()
1051     {
1052         present = i2c_smbus_read_byte(file) >= 0;
1053         return present;
1054     }
1055 
1056     bool getTypeID(uint8_t& val)
1057     {
1058         constexpr uint8_t addr = 2;
1059         int ret = i2c_smbus_read_byte_data(file, addr);
1060         if (ret < 0)
1061         {
1062             std::cerr << "Error " << __FUNCTION__ << "\n";
1063             return false;
1064         }
1065         val = static_cast<uint8_t>(ret);
1066         return true;
1067     }
1068 
1069     bool getBootVer(uint8_t& val)
1070     {
1071         constexpr uint8_t addr = 3;
1072         int ret = i2c_smbus_read_byte_data(file, addr);
1073         if (ret < 0)
1074         {
1075             std::cerr << "Error " << __FUNCTION__ << "\n";
1076             return false;
1077         }
1078         val = static_cast<uint8_t>(ret);
1079         return true;
1080     }
1081 
1082     bool getFPGAVer(uint8_t& val)
1083     {
1084         constexpr uint8_t addr = 4;
1085         int ret = i2c_smbus_read_byte_data(file, addr);
1086         if (ret < 0)
1087         {
1088             std::cerr << "Error " << __FUNCTION__ << "\n";
1089             return false;
1090         }
1091         val = static_cast<uint8_t>(ret);
1092         return true;
1093     }
1094 
1095     bool getSecurityRev(uint8_t& val)
1096     {
1097         constexpr uint8_t addr = 5;
1098         int ret = i2c_smbus_read_byte_data(file, addr);
1099         if (ret < 0)
1100         {
1101             std::cerr << "Error " << __FUNCTION__ << "\n";
1102             return false;
1103         }
1104         val = static_cast<uint8_t>(ret);
1105         return true;
1106     }
1107 
1108     bool getPresence(uint8_t& val)
1109     {
1110         // NVMe drives do not assert PRSNTn, and as such do not get reported as
1111         // PRESENT in this register
1112 
1113         constexpr uint8_t addr = 8;
1114 
1115         int ret = i2c_smbus_read_byte_data(file, addr);
1116         if (ret < 0)
1117         {
1118             std::cerr << "Error " << __FUNCTION__ << "\n";
1119             return false;
1120         }
1121         // presence is inverted
1122         val = static_cast<uint8_t>(~ret);
1123         return true;
1124     }
1125 
1126     bool getIFDET(uint8_t& val)
1127     {
1128         // This register is a bitmap of parallel GPIO pins connected to the
1129         // IFDETn pin of a drive slot. SATA, SAS, and NVMe drives all assert
1130         // IFDETn low when they are inserted into the HSBP.This register, in
1131         // combination with the PRESENCE register, are used by the BMC to detect
1132         // the presence of NVMe drives.
1133 
1134         constexpr uint8_t addr = 9;
1135 
1136         int ret = i2c_smbus_read_byte_data(file, addr);
1137         if (ret < 0)
1138         {
1139             std::cerr << "Error " << __FUNCTION__ << "\n";
1140             return false;
1141         }
1142         // ifdet is inverted
1143         val = static_cast<uint8_t>(~ret);
1144         return true;
1145     }
1146 
1147     bool getFailed(uint8_t& val)
1148     {
1149         constexpr uint8_t addr = 0xC;
1150         int ret = i2c_smbus_read_byte_data(file, addr);
1151         if (ret < 0)
1152         {
1153             std::cerr << "Error " << __FUNCTION__ << "\n";
1154             return false;
1155         }
1156         val = static_cast<uint8_t>(ret);
1157         return true;
1158     }
1159 
1160     bool getRebuild(uint8_t& val)
1161     {
1162         constexpr uint8_t addr = 0xD;
1163         int ret = i2c_smbus_read_byte_data(file, addr);
1164         if (ret < 0)
1165         {
1166             std::cerr << "Error " << __FUNCTION__ << " " << strerror(ret)
1167                       << "\n";
1168             return false;
1169         }
1170         val = static_cast<uint8_t>(ret);
1171         return true;
1172     }
1173 
1174     bool getInsertedAndRemovedNvmeDrives(
1175         std::forward_list<std::string>& nvmeDrivesInserted,
1176         std::forward_list<std::string>& nvmeDrivesRemoved)
1177     {
1178         /* Get the current drives status */
1179         std::bitset<8> currDriveStatus;
1180         uint8_t nPresence;
1181         uint8_t nIfdet;
1182 
1183         if (!getPresence(nPresence) || !getIFDET(nIfdet))
1184         {
1185             /* Error getting value. Return */
1186             std::cerr << "Backplane " << name
1187                       << " failed to get drive status\n";
1188             return false;
1189         }
1190 
1191         std::string dbusHsbpName = boost::replace_all_copy(name, " ", "_");
1192         auto nvmeMap = hsbpConfig.hsbpNvmeMap.find(dbusHsbpName);
1193         if (nvmeMap == hsbpConfig.hsbpNvmeMap.end())
1194         {
1195             std::cerr << "Couldn't get the NVMe Map for the backplane : "
1196                       << name << "\n";
1197             return false;
1198         }
1199 
1200         /* NVMe drives do not assert PRSNTn, and as such do not get reported in
1201          * "presence" register, but assert ifdet low. This implies for a NVMe
1202          * drive to be present, corresponding precense bit has to be 0 and idfet
1203          * has to be 1 (as the values of these regosters are negated: check
1204          * getPresence() and getIfdet() functions) */
1205         for (uint8_t bit = 0; bit < 8; bit++)
1206         {
1207             if ((nPresence & (1U << bit)) == 0)
1208             {
1209                 if (nIfdet & (1U << bit))
1210                 {
1211                     currDriveStatus.set(bit);
1212                 }
1213             }
1214         }
1215 
1216         /* Determine Inserted and Removed Drives
1217          * Prev Bit | Curr Bit | Status
1218          *    0     |    0     | No Change
1219          *    0     |    1     | Inserted
1220          *    1     |    0     | Removed
1221          *    1     |    1     | No Change
1222          */
1223         for (uint8_t index = 0; index < 8; index++)
1224         {
1225             /* Inserted */
1226             if (!prevDriveStatus.test(index) && currDriveStatus.test(index))
1227             {
1228                 nvmeDrivesInserted.emplace_front(nvmeMap->second.at(index));
1229                 std::cerr << name << " : " << nvmeDrivesInserted.front()
1230                           << " Inserted !\n";
1231             }
1232 
1233             /* Removed */
1234             else if (prevDriveStatus.test(index) &&
1235                      !currDriveStatus.test(index))
1236             {
1237                 nvmeDrivesRemoved.emplace_front(nvmeMap->second.at(index));
1238                 std::cerr << name << " : " << nvmeDrivesRemoved.front()
1239                           << " Removed !\n";
1240             }
1241         }
1242 
1243         prevDriveStatus = currDriveStatus;
1244         return true;
1245     }
1246 
1247     virtual ~Backplane()
1248     {
1249         timer.cancel();
1250         objServer.remove_interface(hsbpItemIface);
1251         objServer.remove_interface(versionIface);
1252         objServer.remove_interface(storageInterface);
1253         objServer.remove_interface(assetInterface);
1254         if (file >= 0)
1255         {
1256             close(file);
1257         }
1258     }
1259 
1260     size_t bus;
1261     size_t address;
1262     size_t backplaneIndex;
1263     std::string name;
1264     boost::asio::steady_timer timer;
1265     bool present = false;
1266     uint8_t typeId = 0;
1267     uint8_t bootVer = 0;
1268     uint8_t fpgaVer = 0;
1269     uint8_t securityRev = 0;
1270     uint8_t funSupported = 0;
1271     uint8_t presence = 0;
1272     uint8_t ifdet = 0;
1273     uint8_t failed = 0;
1274     uint8_t rebuilding = 0;
1275     std::bitset<8> prevDriveStatus;
1276 
1277     int file = -1;
1278 
1279     std::string type;
1280 
1281     std::shared_ptr<sdbusplus::asio::dbus_interface> hsbpItemIface;
1282     std::shared_ptr<sdbusplus::asio::dbus_interface> versionIface;
1283     std::shared_ptr<sdbusplus::asio::dbus_interface> storageInterface;
1284     std::shared_ptr<sdbusplus::asio::dbus_interface> assetInterface;
1285 
1286     std::list<Drive> drives;
1287     std::vector<std::shared_ptr<Led>> leds;
1288     std::shared_ptr<boost::container::flat_set<Mux>> muxes;
1289 };
1290 
1291 /* Global HSBP backplanes and NVMe drives collection */
1292 std::unordered_map<std::string, std::shared_ptr<Backplane>> backplanes;
1293 std::list<Drive> ownerlessDrives; // drives without a backplane
1294 /***************************** End of Section *******************************/
1295 
1296 /****************************************************************************/
1297 /***************** Miscellaneous Class/Function Definitions *****************/
1298 /****************************************************************************/
1299 /* The purpose of this class is to sync the code flow. Often times there could
1300  * be multiple dbus calls which are async, and upon completely finishing all
1301  * Dbus calls, we need to call next function, or handle the error.
1302  * When an object of this class goes out of scope, the respective handlers
1303  * will be called */
1304 class AsyncCallbackHandler
1305 {
1306     bool errorOccurred = false;
1307     std::function<void()> onSuccess = nullptr;
1308     std::function<void()> onError = nullptr;
1309 
1310   public:
1311     explicit AsyncCallbackHandler(std::function<void()> onSuccessIn,
1312                                   std::function<void()> onErrorIn) :
1313         onSuccess(std::move(onSuccessIn)),
1314         onError(std::move(onErrorIn))
1315     {
1316     }
1317 
1318     void setError()
1319     {
1320         errorOccurred = true;
1321     }
1322 
1323     ~AsyncCallbackHandler()
1324     {
1325         /* If error occurred flag was set, execute the error handler */
1326         if (errorOccurred)
1327         {
1328             /* Check if Error Handler is defined */
1329             if (onError)
1330             {
1331                 onError();
1332             }
1333 
1334             return;
1335         }
1336 
1337         /* If Success Handler is present, execute Success Handler */
1338         if (onSuccess)
1339         {
1340             onSuccess();
1341         }
1342     }
1343 };
1344 
1345 void stopHsbpManager()
1346 {
1347     std::cerr << __FUNCTION__ << ": Stopping hsbp-manager\n";
1348     appState = AppState::idle;
1349     hsbpConfig.clearConfig();
1350     clockBuffers.clear();
1351     ioExpanders.clear();
1352     backplanes.clear();
1353 
1354     io.stop();
1355 }
1356 /***************************** End of Section *******************************/
1357 
1358 /****************************************************************************/
1359 /********* HSBP clock enable/disable related Function Definitions ***********/
1360 /****************************************************************************/
1361 void updateHsbpClocks(std::forward_list<std::string>& nvmeDrivesInserted,
1362                       std::forward_list<std::string>& nvmeDrivesRemoved)
1363 {
1364     if (appState < AppState::backplanesLoaded)
1365     {
1366         std::cerr << "HSBP not initialized ! Cancelling Clock Update ! \n";
1367         return;
1368     }
1369 
1370     std::cerr << "Updating HSBP drive clocks ...\n";
1371 
1372     /* Loop through all clock buffers and try to update the clocks (this will be
1373      * done if the mode of operation of the clock buffer is SMBus) */
1374     for (auto& clockBuffer : clockBuffers)
1375     {
1376         if (!clockBuffer.enableDisableClock(nvmeDrivesInserted,
1377                                             nvmeDrivesRemoved))
1378         {
1379             std::cerr << "Error Occurred while setting the clock in \""
1380                       << clockBuffer.getName() << "\"\n";
1381         }
1382     }
1383 
1384     /* If there are drives yet to be updated, check all the IO Expanders in case
1385      * they are mapped to the drives and enable the respective IO */
1386     if (!nvmeDrivesInserted.empty() || !nvmeDrivesRemoved.empty())
1387     {
1388         for (auto& ioExpander : ioExpanders)
1389         {
1390             if (!ioExpander.enableDisableOuput(nvmeDrivesInserted,
1391                                                nvmeDrivesRemoved))
1392             {
1393                 std::cerr << "Error Occurred while setting the IO in \""
1394                           << ioExpander.getName() << "\"\n";
1395             }
1396         }
1397     }
1398 
1399     /* If there are drives still left, then one or more drives clock
1400      * enable/diable failed. There is a possibility of improper mapping or
1401      * current communication with the device failed */
1402     if (!nvmeDrivesInserted.empty() || !nvmeDrivesRemoved.empty())
1403     {
1404         std::cerr << "Critical Error !!!\nMapping issue detected !\n";
1405 
1406         if (!nvmeDrivesInserted.empty())
1407         {
1408             std::cerr << "The clock enable failed for : ";
1409             for (auto& nvme1 : nvmeDrivesInserted)
1410             {
1411                 std::cerr << nvme1 << ", ";
1412             }
1413             std::cerr << "\n";
1414         }
1415 
1416         if (!nvmeDrivesRemoved.empty())
1417         {
1418             std::cerr << "The clock disable failed for : ";
1419             for (auto& nvme1 : nvmeDrivesRemoved)
1420             {
1421                 std::cerr << nvme1 << ", ";
1422             }
1423             std::cerr << "\n";
1424         }
1425     }
1426 }
1427 
1428 void scanHsbpDrives(bool& hsbpDriveScanInProgress)
1429 {
1430     std::cerr << __FUNCTION__ << ": Scanning HSBP drives status ...\n";
1431 
1432     /* List variables to store the drives Inserted/Removed */
1433     std::forward_list<std::string> nvmeDrivesInserted;
1434     std::forward_list<std::string> nvmeDrivesRemoved;
1435 
1436     /* Loop through each backplane present and get the list of inserted/removed
1437      * drives */
1438     for (auto& [name, backplane] : backplanes)
1439     {
1440         backplane->getInsertedAndRemovedNvmeDrives(nvmeDrivesInserted,
1441                                                    nvmeDrivesRemoved);
1442     }
1443 
1444     if (!nvmeDrivesInserted.empty() || !nvmeDrivesRemoved.empty())
1445     {
1446         updateHsbpClocks(nvmeDrivesInserted, nvmeDrivesRemoved);
1447     }
1448 
1449     std::cerr << __FUNCTION__ << ": Scanning HSBP drives Completed\n";
1450 
1451     hsbpDriveScanInProgress = false;
1452 }
1453 
1454 void checkHsbpDrivesStatus()
1455 {
1456     static bool hsbpDriveScanInProgress = false;
1457     static bool hsbpDriveRescanInQueue = false;
1458 
1459     if (appState < AppState::backplanesLoaded)
1460     {
1461         std::cerr << __FUNCTION__
1462                   << ": HSBP not initialized ! Cancelling scan of HSBP drives "
1463                      "status ! \n";
1464         return;
1465     }
1466 
1467     if (hsbpDriveScanInProgress)
1468     {
1469         /* Scan and Clock Update already in progress. Try again after sometime.
1470          * This event can occur due to the GPIO interrupt */
1471         std::cerr << __FUNCTION__
1472                   << ": HSBP Drives Scan is already in progress\n";
1473         if (hsbpDriveRescanInQueue)
1474         {
1475             /* There is already a Re-Scan in queue. No need to create multiple
1476              * rescans */
1477             return;
1478         }
1479 
1480         hsbpDriveRescanInQueue = true;
1481 
1482         std::cerr << __FUNCTION__ << ": Queuing the Scan \n";
1483 
1484         auto driveScanTimer = std::make_shared<boost::asio::steady_timer>(io);
1485         driveScanTimer->expires_after(std::chrono::seconds(1));
1486         driveScanTimer->async_wait(
1487             [driveScanTimer](const boost::system::error_code ec) {
1488                 if (ec == boost::asio::error::operation_aborted)
1489                 {
1490                     // Timer was Aborted
1491                     return;
1492                 }
1493                 else if (ec)
1494                 {
1495                     std::cerr << "driveScanTimer: Timer error" << ec.message()
1496                               << "\n";
1497                     return;
1498                 }
1499                 hsbpDriveRescanInQueue = false;
1500                 checkHsbpDrivesStatus();
1501             });
1502 
1503         return;
1504     }
1505 
1506     hsbpDriveScanInProgress = true;
1507 
1508     /* Post the scan to IO queue and return from here. This enables capturing
1509      * next GPIO event if any */
1510     boost::asio::post(io, []() { scanHsbpDrives(hsbpDriveScanInProgress); });
1511 }
1512 /***************************** End of Section *******************************/
1513 
1514 /****************************************************************************/
1515 /********** Backplanes and NVMe drives related Function Definitions *********/
1516 /****************************************************************************/
1517 static size_t getDriveCount()
1518 {
1519     size_t count = 0;
1520     for (const auto& [key, backplane] : backplanes)
1521     {
1522         count += backplane->drives.size();
1523     }
1524     return count + ownerlessDrives.size();
1525 }
1526 
1527 void updateAssets()
1528 {
1529     appState = AppState::loadingDrives;
1530 
1531     /* Setup a callback to be called once the assets are populated completely or
1532      * fallback to error handler */
1533     auto drivesLoadedCallback = std::make_shared<AsyncCallbackHandler>(
1534         []() {
1535             appState = AppState::drivesLoaded;
1536             std::cerr << "Drives Updated !\n";
1537         },
1538         []() {
1539             // TODO: Handle this error if needed
1540             appState = AppState::backplanesLoaded;
1541             std::cerr << "An error occured ! Drives load failed \n";
1542         });
1543 
1544     conn->async_method_call(
1545         [drivesLoadedCallback](const boost::system::error_code ec,
1546                                const GetSubTreeType& subtree) {
1547             if (ec)
1548             {
1549                 std::cerr << __FUNCTION__ << ": Error contacting mapper "
1550                           << ec.message() << "\n";
1551                 drivesLoadedCallback->setError();
1552                 return;
1553             }
1554 
1555             // drives may get an owner during this, or we might disover more
1556             // drives
1557             ownerlessDrives.clear();
1558             for (const auto& [path, objDict] : subtree)
1559             {
1560                 if (objDict.empty())
1561                 {
1562                     continue;
1563                 }
1564 
1565                 const std::string& owner = objDict.begin()->first;
1566                 // we export this interface too
1567                 if (owner == busName)
1568                 {
1569                     continue;
1570                 }
1571                 if (std::find(objDict.begin()->second.begin(),
1572                               objDict.begin()->second.end(),
1573                               assetTag) == objDict.begin()->second.end())
1574                 {
1575                     // no asset tag to associate to
1576                     continue;
1577                 }
1578 
1579                 conn->async_method_call(
1580                     [path, drivesLoadedCallback](
1581                         const boost::system::error_code ec2,
1582                         const boost::container::flat_map<
1583                             std::string, std::variant<uint64_t, std::string>>&
1584                             values) {
1585                         if (ec2)
1586                         {
1587                             std::cerr << __FUNCTION__
1588                                       << ": Error Getting Config "
1589                                       << ec2.message() << " "
1590                                       << "\n";
1591                             drivesLoadedCallback->setError();
1592                             return;
1593                         }
1594                         auto findBus = values.find("Bus");
1595 
1596                         if (findBus == values.end())
1597                         {
1598                             std::cerr << __FUNCTION__
1599                                       << ": Illegal interface at " << path
1600                                       << "\n";
1601                             drivesLoadedCallback->setError();
1602                             return;
1603                         }
1604 
1605                         // find the mux bus and addr
1606                         size_t muxBus = static_cast<size_t>(
1607                             std::get<uint64_t>(findBus->second));
1608                         std::filesystem::path muxPath =
1609                             "/sys/bus/i2c/devices/i2c-" +
1610                             std::to_string(muxBus) + "/mux_device";
1611                         if (!std::filesystem::is_symlink(muxPath))
1612                         {
1613                             std::cerr << path << " mux does not exist\n";
1614                             drivesLoadedCallback->setError();
1615                             return;
1616                         }
1617 
1618                         // we should be getting something of the form 7-0052
1619                         // for bus 7 addr 52
1620                         std::string fname =
1621                             std::filesystem::read_symlink(muxPath).filename();
1622                         auto findDash = fname.find('-');
1623 
1624                         if (findDash == std::string::npos ||
1625                             findDash + 1 >= fname.size())
1626                         {
1627                             std::cerr << path << " mux path invalid\n";
1628                             drivesLoadedCallback->setError();
1629                             return;
1630                         }
1631 
1632                         std::string busStr = fname.substr(0, findDash);
1633                         std::string muxStr = fname.substr(findDash + 1);
1634 
1635                         size_t bus = static_cast<size_t>(std::stoi(busStr));
1636                         size_t addr =
1637                             static_cast<size_t>(std::stoi(muxStr, nullptr, 16));
1638                         size_t muxIndex = 0;
1639 
1640                         // find the channel of the mux the drive is on
1641                         std::ifstream nameFile("/sys/bus/i2c/devices/i2c-" +
1642                                                std::to_string(muxBus) +
1643                                                "/name");
1644                         if (!nameFile)
1645                         {
1646                             std::cerr << __FUNCTION__
1647                                       << ": Unable to open name file of bus "
1648                                       << muxBus << "\n";
1649                             drivesLoadedCallback->setError();
1650                             return;
1651                         }
1652 
1653                         std::string nameStr;
1654                         std::getline(nameFile, nameStr);
1655 
1656                         // file is of the form "i2c-4-mux (chan_id 1)", get chan
1657                         // assume single digit chan
1658                         const std::string prefix = "chan_id ";
1659                         size_t findId = nameStr.find(prefix);
1660                         if (findId == std::string::npos ||
1661                             findId + 1 >= nameStr.size())
1662                         {
1663                             std::cerr << __FUNCTION__
1664                                       << ": Illegal name file on bus " << muxBus
1665                                       << "\n";
1666                         }
1667 
1668                         std::string indexStr =
1669                             nameStr.substr(findId + prefix.size(), 1);
1670 
1671                         size_t driveIndex = std::stoi(indexStr);
1672 
1673                         boost::container::flat_map<std::string, std::string>
1674                             assetInventory;
1675                         const std::array<const char*, 4> assetKeys = {
1676                             "PartNumber", "SerialNumber", "Manufacturer",
1677                             "Model"};
1678                         for (const auto& [key, value] : values)
1679                         {
1680                             if (std::find(assetKeys.begin(), assetKeys.end(),
1681                                           key) == assetKeys.end())
1682                             {
1683                                 continue;
1684                             }
1685                             assetInventory[key] = std::get<std::string>(value);
1686                         }
1687 
1688                         Backplane* parent = nullptr;
1689                         for (auto& [name, backplane] : backplanes)
1690                         {
1691                             muxIndex = 0;
1692                             for (const Mux& mux : *(backplane->muxes))
1693                             {
1694                                 if (bus == mux.bus && addr == mux.address)
1695                                 {
1696                                     parent = backplane.get();
1697                                     break;
1698                                 }
1699                                 muxIndex += mux.channels;
1700                             }
1701                             if (parent)
1702                             {
1703                                 /* Found the backplane. No need to proceed
1704                                  * further */
1705                                 break;
1706                             }
1707                         }
1708 
1709                         // assume its a M.2 or something without a hsbp
1710                         if (parent == nullptr)
1711                         {
1712                             std::string driveName =
1713                                 "Drive_" + std::to_string(getDriveCount() + 1);
1714                             auto& drive = ownerlessDrives.emplace_back(
1715                                 driveName, true, true, true, false);
1716                             drive.createAsset(assetInventory);
1717                             return;
1718                         }
1719 
1720                         driveIndex += muxIndex;
1721 
1722                         if (parent->drives.size() <= driveIndex)
1723                         {
1724                             std::cerr << __FUNCTION__
1725                                       << ": Illegal drive index at " << path
1726                                       << " " << driveIndex << "\n";
1727                             drivesLoadedCallback->setError();
1728                             return;
1729                         }
1730                         auto it = parent->drives.begin();
1731                         std::advance(it, driveIndex);
1732 
1733                         it->createAsset(assetInventory);
1734                     },
1735                     owner, path, "org.freedesktop.DBus.Properties", "GetAll",
1736                     "" /*all interface items*/);
1737             }
1738         },
1739         mapper::busName, mapper::path, mapper::interface, mapper::subtree, "/",
1740         0, std::array<const char*, 1>{nvmeIntf});
1741 }
1742 
1743 void populateMuxes(std::shared_ptr<boost::container::flat_set<Mux>> muxes,
1744                    std::string& rootPath)
1745 {
1746     const static std::array<const std::string, 4> muxTypes = {
1747         "xyz.openbmc_project.Configuration.PCA9543Mux",
1748         "xyz.openbmc_project.Configuration.PCA9544Mux",
1749         "xyz.openbmc_project.Configuration.PCA9545Mux",
1750         "xyz.openbmc_project.Configuration.PCA9546Mux"};
1751 
1752     conn->async_method_call(
1753         [muxes](const boost::system::error_code ec,
1754                 const GetSubTreeType& subtree) {
1755             if (ec)
1756             {
1757                 std::cerr << __FUNCTION__ << ": Error contacting mapper "
1758                           << ec.message() << "\n";
1759                 return;
1760             }
1761             size_t index = 0; // as we use a flat map, these are sorted
1762             for (const auto& [path, objDict] : subtree)
1763             {
1764                 if (objDict.empty() || objDict.begin()->second.empty())
1765                 {
1766                     continue;
1767                 }
1768 
1769                 const std::string& owner = objDict.begin()->first;
1770                 const std::vector<std::string>& interfaces =
1771                     objDict.begin()->second;
1772 
1773                 const std::string* interface = nullptr;
1774                 for (const std::string& iface : interfaces)
1775                 {
1776                     if (std::find(muxTypes.begin(), muxTypes.end(), iface) !=
1777                         muxTypes.end())
1778                     {
1779                         interface = &iface;
1780                         break;
1781                     }
1782                 }
1783 
1784                 if (interface == nullptr)
1785                 {
1786                     std::cerr << __FUNCTION__ << ": Cannot get mux type\n";
1787                     continue;
1788                 }
1789 
1790                 conn->async_method_call(
1791                     [path, muxes, index](
1792                         const boost::system::error_code ec2,
1793                         const boost::container::flat_map<
1794                             std::string,
1795                             std::variant<uint64_t, std::vector<std::string>>>&
1796                             values) {
1797                         if (ec2)
1798                         {
1799                             std::cerr << __FUNCTION__
1800                                       << ": Error Getting Config "
1801                                       << ec2.message() << "\n";
1802                             return;
1803                         }
1804                         auto findBus = values.find("Bus");
1805                         auto findAddress = values.find("Address");
1806                         auto findChannelNames = values.find("ChannelNames");
1807                         if (findBus == values.end() ||
1808                             findAddress == values.end())
1809                         {
1810                             std::cerr << __FUNCTION__
1811                                       << ": Illegal configuration at " << path
1812                                       << "\n";
1813                             return;
1814                         }
1815                         size_t bus = static_cast<size_t>(
1816                             std::get<uint64_t>(findBus->second));
1817                         size_t address = static_cast<size_t>(
1818                             std::get<uint64_t>(findAddress->second));
1819                         std::vector<std::string> channels =
1820                             std::get<std::vector<std::string>>(
1821                                 findChannelNames->second);
1822                         muxes->emplace(bus, address, channels.size(), index);
1823                     },
1824                     owner, path, "org.freedesktop.DBus.Properties", "GetAll",
1825                     *interface);
1826                 index++;
1827             }
1828         },
1829         mapper::busName, mapper::path, mapper::interface, mapper::subtree,
1830         rootPath, 1, muxTypes);
1831 }
1832 
1833 void populateHsbpBackplanes(
1834     const std::shared_ptr<AsyncCallbackHandler>& backplanesLoadedCallback)
1835 {
1836     std::cerr << __FUNCTION__ << ": Scanning Backplanes ...\n";
1837     appState = AppState::loadingBackplanes;
1838     backplanes.clear();
1839 
1840     conn->async_method_call(
1841         [backplanesLoadedCallback](const boost::system::error_code ec,
1842                                    const GetSubTreeType& subtree) {
1843             if (ec)
1844             {
1845                 std::cerr << __FUNCTION__ << ": Error contacting mapper "
1846                           << ec.message() << "\n";
1847                 backplanesLoadedCallback->setError();
1848                 return;
1849             }
1850 
1851             if (subtree.empty())
1852             {
1853                 /* There wer no HSBP's detected. set teh state back to
1854                  * componentsLoaded so that on backplane match event, the
1855                  * process can start again */
1856                 appState = AppState::componentsLoaded;
1857                 std::cerr << __FUNCTION__ << ": No HSBPs Detected....\n";
1858                 return;
1859             }
1860 
1861             for (const auto& [path, objDict] : subtree)
1862             {
1863                 if (objDict.empty())
1864                 {
1865                     std::cerr << __FUNCTION__
1866                               << ": Subtree data "
1867                                  "corrupted !\n";
1868                     backplanesLoadedCallback->setError();
1869                     return;
1870                 }
1871 
1872                 const std::string& owner = objDict.begin()->first;
1873                 conn->async_method_call(
1874                     [backplanesLoadedCallback, path,
1875                      owner](const boost::system::error_code ec2,
1876                             const boost::container::flat_map<
1877                                 std::string, BasicVariantType>& resp) {
1878                         if (ec2)
1879                         {
1880                             std::cerr << __FUNCTION__
1881                                       << ": Error Getting Config "
1882                                       << ec2.message() << "\n";
1883                             backplanesLoadedCallback->setError();
1884                             return;
1885                         }
1886                         std::optional<size_t> bus;
1887                         std::optional<size_t> address;
1888                         std::optional<size_t> backplaneIndex;
1889                         std::optional<std::string> name;
1890                         for (const auto& [key, value] : resp)
1891                         {
1892                             if (key == "Bus")
1893                             {
1894                                 bus = std::get<uint64_t>(value);
1895                             }
1896                             else if (key == "Address")
1897                             {
1898                                 address = std::get<uint64_t>(value);
1899                             }
1900                             else if (key == "Index")
1901                             {
1902                                 backplaneIndex = std::get<uint64_t>(value);
1903                             }
1904                             else if (key == "Name")
1905                             {
1906                                 name = std::get<std::string>(value);
1907                             }
1908                         }
1909                         if (!bus || !address || !name || !backplaneIndex)
1910                         {
1911                             std::cerr << __FUNCTION__
1912                                       << ": Illegal configuration at " << path
1913                                       << "\n";
1914                             backplanesLoadedCallback->setError();
1915                             return;
1916                         }
1917                         std::string parentPath =
1918                             std::filesystem::path(path).parent_path();
1919                         const auto& [backplane, status] = backplanes.emplace(
1920                             *name, std::make_shared<Backplane>(
1921                                        *bus, *address, *backplaneIndex, *name));
1922                         backplane->second->run(parentPath, owner);
1923                         populateMuxes(backplane->second->muxes, parentPath);
1924                     },
1925                     owner, path, "org.freedesktop.DBus.Properties", "GetAll",
1926                     hsbpCpldInft);
1927             }
1928         },
1929         mapper::busName, mapper::path, mapper::interface, mapper::subtree, "/",
1930         0, std::array<const char*, 1>{hsbpCpldInft});
1931 }
1932 
1933 void setUpBackplanesAndDrives()
1934 {
1935     static bool backplanesScanInProgress = false;
1936     static bool backplanesRescanInQueue = false;
1937 
1938     if (appState < AppState::componentsLoaded)
1939     {
1940         std::cerr << __FUNCTION__
1941                   << ": Components are not initialized ! Cancelling scan of "
1942                      "Backplanes ! \n";
1943         return;
1944     }
1945 
1946     if (backplanesScanInProgress)
1947     {
1948         std::cerr << __FUNCTION__
1949                   << ": Backplanes Scan is already in progress\n";
1950         if (backplanesRescanInQueue)
1951         {
1952             /* There is already a Re-Scan in queue. No need to create multiple
1953              * rescans */
1954             return;
1955         }
1956 
1957         backplanesRescanInQueue = true;
1958 
1959         std::cerr << __FUNCTION__ << ": Queuing the Backplane Scan \n";
1960 
1961         auto backplaneScanTimer =
1962             std::make_shared<boost::asio::steady_timer>(io);
1963         backplaneScanTimer->expires_after(std::chrono::seconds(1));
1964         backplaneScanTimer->async_wait(
1965             [backplaneScanTimer](const boost::system::error_code ec) {
1966                 if (ec == boost::asio::error::operation_aborted)
1967                 {
1968                     // Timer was Aborted
1969                     return;
1970                 }
1971                 else if (ec)
1972                 {
1973                     std::cerr << "backplaneScanTimer: Timer error"
1974                               << ec.message() << "\n";
1975                     return;
1976                 }
1977                 backplanesRescanInQueue = false;
1978                 setUpBackplanesAndDrives();
1979             });
1980 
1981         return;
1982     }
1983 
1984     backplanesScanInProgress = true;
1985 
1986     /* Set Callback to be called once backplanes are populated to call
1987      * updateAssets() and checkHsbpDrivesStatus() or handle error scnenario */
1988     auto backplanesLoadedCallback = std::make_shared<AsyncCallbackHandler>(
1989         []() {
1990             /* If no HSBP's were detected, the state changes to
1991              * componentsLoaded. Proceed further only if state was
1992              * loadingBackplanes */
1993             if (appState != AppState::loadingBackplanes)
1994             {
1995                 backplanesScanInProgress = false;
1996                 return;
1997             }
1998 
1999             /* If there is a ReScan in the Queue, dont proceed further. Load the
2000              * Backplanes again and then proceed further */
2001             if (backplanesRescanInQueue)
2002             {
2003                 backplanesScanInProgress = false;
2004                 return;
2005             }
2006 
2007             appState = AppState::backplanesLoaded;
2008             std::cerr << __FUNCTION__ << ": Backplanes Loaded...\n";
2009 
2010             checkHsbpDrivesStatus();
2011             updateAssets();
2012             backplanesScanInProgress = false;
2013         },
2014         []() {
2015             /* Loading Backplanes is an important step. If the load failed due
2016              * to an error, stop the app so that restart cant be triggerred */
2017             std::cerr << "Backplanes couldn't be loaded due to an error !...\n";
2018             appState = AppState::idle;
2019             backplanesScanInProgress = false;
2020             stopHsbpManager();
2021         });
2022 
2023     populateHsbpBackplanes(backplanesLoadedCallback);
2024 }
2025 
2026 void setupBackplanesAndDrivesMatch()
2027 {
2028     static auto backplaneMatch = std::make_unique<sdbusplus::bus::match_t>(
2029         *conn,
2030         "sender='xyz.openbmc_project.EntityManager', type='signal', "
2031         "member='PropertiesChanged', "
2032         "interface='org.freedesktop.DBus.Properties', "
2033         "path_namespace='/xyz/openbmc_project/inventory/system/board', arg0='" +
2034             std::string(hsbpCpldInft) + "'",
2035         [](sdbusplus::message_t& msg) {
2036             std::string intfName;
2037             boost::container::flat_map<std::string, BasicVariantType> values;
2038             msg.read(intfName, values);
2039 
2040             /* This match will be triggered for each of the property being set
2041              * under the hsbpCpldInft interface. Call the loader only on one
2042              * property say "name". This will avoid multiple calls to populate
2043              * function
2044              */
2045             for (const auto& [key, value] : values)
2046             {
2047                 if (key == "Name")
2048                 {
2049                     /* This match will be triggered when ever there is a
2050                      * addition/removal of HSBP backplane. At this stage, all
2051                      * the HSBP's need to be populated again and also assets
2052                      * have to be re-discovered. So, setting state to
2053                      * componentsLoaded and calling setUpBackplanesAndDrives()
2054                      * only if configuration and components loading was
2055                      * completed */
2056                     if (appState < AppState::componentsLoaded)
2057                     {
2058                         /* Configuration is not loaded yet. Backplanes will be
2059                          * loaded
2060                          * once configuration and components are loaded. */
2061                         std::cerr << __FUNCTION__
2062                                   << ": Discarding Backplane match\n";
2063                         return;
2064                     }
2065 
2066                     appState = AppState::componentsLoaded;
2067 
2068                     /* We will call the function after a small delay to let all
2069                      * the properties to be intialized */
2070                     auto backplaneTimer =
2071                         std::make_shared<boost::asio::steady_timer>(io);
2072                     backplaneTimer->expires_after(std::chrono::seconds(2));
2073                     backplaneTimer->async_wait(
2074                         [backplaneTimer](const boost::system::error_code ec) {
2075                             if (ec == boost::asio::error::operation_aborted)
2076                             {
2077                                 return;
2078                             }
2079                             else if (ec)
2080                             {
2081                                 std::cerr << "backplaneTimer: Timer error"
2082                                           << ec.message() << "\n";
2083                                 return;
2084                             }
2085                             setUpBackplanesAndDrives();
2086                         });
2087                 }
2088             }
2089         });
2090 
2091     static auto drivesMatch = std::make_unique<sdbusplus::bus::match_t>(
2092         *conn,
2093         "sender='xyz.openbmc_project.EntityManager', type='signal', "
2094         "member='PropertiesChanged', "
2095         "interface='org.freedesktop.DBus.Properties', arg0='" +
2096             std::string(nvmeIntf) + "'",
2097         [](sdbusplus::message_t& msg) {
2098             std::string intfName;
2099             boost::container::flat_map<std::string, BasicVariantType> values;
2100             msg.read(intfName, values);
2101 
2102             /* This match will be triggered for each of the property being set
2103              * under the nvmeIntf interface. Call the loader only on one
2104              * property say "name". This will avoid multiple calls to populate
2105              * function
2106              */
2107             for (const auto& [key, value] : values)
2108             {
2109                 if (key == "Name")
2110                 {
2111                     /* This match will be triggered when ever there is a
2112                      * addition/removal of drives. At this stage only assets
2113                      * have to be re-discovered. So, setting state to
2114                      * backplanesLoaded and calling updateAssets() only if all
2115                      * previous states are completed */
2116                     if (appState < AppState::backplanesLoaded)
2117                     {
2118                         /* Configuration is not loaded yet. Drives will be
2119                          * loaded once
2120                          * configuration, components and backplanes are loaded.
2121                          */
2122                         std::cerr << __FUNCTION__
2123                                   << ": Discarding Drive match\n";
2124                         return;
2125                     }
2126 
2127                     appState = AppState::backplanesLoaded;
2128 
2129                     /* We will call the function after a small delay to let all
2130                      * the properties to be intialized */
2131                     auto driveTimer =
2132                         std::make_shared<boost::asio::steady_timer>(io);
2133                     driveTimer->expires_after(std::chrono::seconds(2));
2134                     driveTimer->async_wait(
2135                         [driveTimer](const boost::system::error_code ec) {
2136                             if (ec == boost::asio::error::operation_aborted)
2137                             {
2138                                 return;
2139                             }
2140                             else if (ec)
2141                             {
2142                                 std::cerr << "driveTimer: Timer error"
2143                                           << ec.message() << "\n";
2144                                 return;
2145                             }
2146                             updateAssets();
2147                         });
2148                 }
2149             }
2150         });
2151 }
2152 /***************************** End of Section *******************************/
2153 
2154 /****************************************************************************/
2155 /******************* Components related Function Definitions ****************/
2156 /****************************************************************************/
2157 bool verifyComponentsLoaded()
2158 {
2159     std::cerr << __FUNCTION__ << ": Verifying all Components...\n";
2160 
2161     /* Loop through all clock buffers */
2162     for (auto& clockBuffer : clockBuffers)
2163     {
2164         if (!clockBuffer.isInitialized())
2165         {
2166             std::cerr << "Critical Error: Initializing \""
2167                       << clockBuffer.getName() << "\" failed\n";
2168             return false;
2169         }
2170     }
2171 
2172     /* Loop through all IO Expanders */
2173     for (auto& ioExpander : ioExpanders)
2174     {
2175         if (!ioExpander.isInitialized())
2176         {
2177             std::cerr << "Critical Error: Initializing \""
2178                       << ioExpander.getName() << "\" failed\n";
2179             return false;
2180         }
2181     }
2182 
2183     std::cerr << __FUNCTION__ << ": Verifying Components Complete\n";
2184 
2185     return true;
2186 }
2187 /***************************** End of Section *******************************/
2188 
2189 /****************************************************************************/
2190 /****************** IO expander related Function Definitions ****************/
2191 /****************************************************************************/
2192 void loadIoExpanderInfo(
2193     const std::shared_ptr<AsyncCallbackHandler>& componentsLoadedCallback)
2194 {
2195     appState = AppState::loadingComponents;
2196 
2197     /* Clear global ioExpanders to start off */
2198     ioExpanders.clear();
2199 
2200     conn->async_method_call(
2201         [componentsLoadedCallback](const boost::system::error_code ec,
2202                                    const GetSubTreeType& subtree) {
2203             if (ec)
2204             {
2205                 std::cerr << __FUNCTION__ << ": Error contacting mapper "
2206                           << ec.message() << "\n";
2207                 componentsLoadedCallback->setError();
2208                 return;
2209             }
2210 
2211             for (auto& [path, objDict] : subtree)
2212             {
2213 
2214                 if (objDict.empty())
2215                 {
2216                     std::cerr << __FUNCTION__ << ": Subtree data corrupted !\n";
2217                     componentsLoadedCallback->setError();
2218                     return;
2219                 }
2220 
2221                 /* Ideally there would be only one element in objDict as only
2222                  * one service exposes it and there would be only one interface
2223                  * so it is safe to directly read them without loop */
2224                 const std::string& service = objDict.begin()->first;
2225                 const std::string& intf = objDict.begin()->second.front();
2226 
2227                 conn->async_method_call(
2228                     [componentsLoadedCallback](
2229                         const boost::system::error_code er,
2230                         const boost::container::flat_map<
2231                             std::string, BasicVariantType>& resp) {
2232                         if (er)
2233                         {
2234                             std::cerr << __FUNCTION__
2235                                       << ": Error Getting "
2236                                          "Config "
2237                                       << er.message() << "\n";
2238                             componentsLoadedCallback->setError();
2239                             return;
2240                         }
2241 
2242                         std::optional<uint64_t> bus;
2243                         std::optional<uint64_t> address;
2244                         std::optional<uint64_t> confIORegAddr;
2245                         std::optional<uint64_t> outCtrlBaseAddr;
2246                         std::optional<uint64_t> outCtrlByteCount;
2247                         std::unordered_map<std::string,
2248                                            std::vector<std::string>>
2249                             ioMap;
2250                         std::optional<std::string> name;
2251                         std::optional<std::string> type;
2252 
2253                         /* Loop through to get all IO Expander properties */
2254                         for (const auto& [key, value] : resp)
2255                         {
2256                             if (key == "Bus")
2257                             {
2258                                 bus = std::get<uint64_t>(value);
2259                             }
2260                             else if (key == "Address")
2261                             {
2262                                 address = std::get<uint64_t>(value);
2263                             }
2264                             else if (key == "ConfIORegAddr")
2265                             {
2266                                 confIORegAddr = std::get<uint64_t>(value);
2267                             }
2268                             else if (key == "OutCtrlBaseAddr")
2269                             {
2270                                 outCtrlBaseAddr = std::get<uint64_t>(value);
2271                             }
2272                             else if (key == "OutCtrlByteCount")
2273                             {
2274                                 outCtrlByteCount = std::get<uint64_t>(value);
2275                             }
2276                             else if (key == "Name")
2277                             {
2278                                 name = std::get<std::string>(value);
2279                             }
2280                             else if (key == "Type")
2281                             {
2282                                 type = std::get<std::string>(value);
2283                             }
2284                             else if (key.starts_with("IO"))
2285                             {
2286                                 std::optional<std::vector<std::string>> outList;
2287                                 outList = std::get<NvmeMapping>(value);
2288                                 if (!outList)
2289                                 {
2290                                     break;
2291                                 }
2292                                 ioMap.try_emplace(key, *outList);
2293                             }
2294                         }
2295 
2296                         /* Verify if all properties were defined */
2297                         if (!bus || !address || !confIORegAddr ||
2298                             !outCtrlBaseAddr || !outCtrlByteCount || !name)
2299                         {
2300                             std::cerr << __FUNCTION__
2301                                       << ": Incomplete "
2302                                          "Clock Buffer definition !! \n";
2303                             componentsLoadedCallback->setError();
2304                             return;
2305                         }
2306 
2307                         /* Check if we were able to get byteMap correctly */
2308                         if ((*outCtrlByteCount) != ioMap.size())
2309                         {
2310                             std::cerr << "loadIoExpanderInfo(): Incomplete "
2311                                          "IO Map !! \n";
2312                             componentsLoadedCallback->setError();
2313                             return;
2314                         }
2315 
2316                         /* Create IO expander object and add it to global
2317                          * ioExpanders vector */
2318                         ioExpanders.emplace_front(
2319                             *bus, *address, *confIORegAddr, *outCtrlBaseAddr,
2320                             *outCtrlByteCount, ioMap, *name, *type);
2321                     },
2322                     service, path, "org.freedesktop.DBus.Properties", "GetAll",
2323                     intf);
2324             }
2325         },
2326         mapper::busName, mapper::path, mapper::interface, mapper::subtree, "/",
2327         0, hsbpConfig.ioExpanderTypes);
2328 }
2329 /***************************** End of Section *******************************/
2330 
2331 /****************************************************************************/
2332 /***************** Clock buffer related Function Definitions ****************/
2333 /****************************************************************************/
2334 void loadClockBufferInfo(
2335     const std::shared_ptr<AsyncCallbackHandler>& componentsLoadedCallback)
2336 {
2337     appState = AppState::loadingComponents;
2338 
2339     /* Clear global clockBuffers to start off */
2340     clockBuffers.clear();
2341 
2342     conn->async_method_call(
2343         [componentsLoadedCallback](const boost::system::error_code ec,
2344                                    const GetSubTreeType& subtree) {
2345             if (ec)
2346             {
2347                 std::cerr << __FUNCTION__ << ": Error contacting mapper "
2348                           << ec.message() << "\n";
2349                 componentsLoadedCallback->setError();
2350                 return;
2351             }
2352 
2353             for (auto& [path, objDict] : subtree)
2354             {
2355 
2356                 if (objDict.empty())
2357                 {
2358                     std::cerr << __FUNCTION__ << ": Subtree data corrupted !\n";
2359                     componentsLoadedCallback->setError();
2360                     return;
2361                 }
2362 
2363                 /* Ideally there would be only one element in objDict as only
2364                  * one service exposes it and there would be only one interface
2365                  * so it is safe to directly read them without loop */
2366                 const std::string& service = objDict.begin()->first;
2367                 const std::string& intf = objDict.begin()->second.front();
2368 
2369                 conn->async_method_call(
2370                     [componentsLoadedCallback](
2371                         const boost::system::error_code er,
2372                         const boost::container::flat_map<
2373                             std::string, BasicVariantType>& resp) {
2374                         if (er)
2375                         {
2376                             std::cerr << __FUNCTION__
2377                                       << ": Error Getting "
2378                                          "Config "
2379                                       << er.message() << "\n";
2380                             componentsLoadedCallback->setError();
2381                             return;
2382                         }
2383 
2384                         std::optional<uint64_t> bus;
2385                         std::optional<uint64_t> address;
2386                         std::optional<std::string> mode;
2387                         std::optional<uint64_t> outCtrlBaseAddr;
2388                         std::optional<uint64_t> outCtrlByteCount;
2389                         std::unordered_map<std::string,
2390                                            std::vector<std::string>>
2391                             byteMap;
2392                         std::optional<std::string> name;
2393                         std::optional<std::string> type;
2394 
2395                         /* Loop through to get all Clock Buffer properties */
2396                         for (const auto& [key, value] : resp)
2397                         {
2398                             if (key == "Bus")
2399                             {
2400                                 bus = std::get<uint64_t>(value);
2401                             }
2402                             else if (key == "Address")
2403                             {
2404                                 address = std::get<uint64_t>(value);
2405                             }
2406                             else if (key == "Mode")
2407                             {
2408                                 mode = std::get<std::string>(value);
2409                             }
2410                             else if (key == "OutCtrlBaseAddr")
2411                             {
2412                                 outCtrlBaseAddr = std::get<uint64_t>(value);
2413                             }
2414                             else if (key == "OutCtrlByteCount")
2415                             {
2416                                 outCtrlByteCount = std::get<uint64_t>(value);
2417                             }
2418                             else if (key == "Name")
2419                             {
2420                                 name = std::get<std::string>(value);
2421                             }
2422                             else if (key == "Type")
2423                             {
2424                                 type = std::get<std::string>(value);
2425                             }
2426                             else if (key.starts_with("Byte"))
2427                             {
2428                                 std::optional<std::vector<std::string>>
2429                                     byteList;
2430                                 byteList = std::get<NvmeMapping>(value);
2431                                 if (!byteList)
2432                                 {
2433                                     break;
2434                                 }
2435                                 byteMap.try_emplace(key, *byteList);
2436                             }
2437                         }
2438 
2439                         /* Verify if all properties were defined */
2440                         if (!bus || !address || !mode || !outCtrlBaseAddr ||
2441                             !outCtrlByteCount || !name)
2442                         {
2443                             std::cerr << __FUNCTION__
2444                                       << ": Incomplete "
2445                                          "Clock Buffer definition !! \n";
2446                             componentsLoadedCallback->setError();
2447                             return;
2448                         }
2449 
2450                         /* Check if we were able to get byteMap correctly */
2451                         if ((*outCtrlByteCount) != byteMap.size())
2452                         {
2453                             std::cerr << __FUNCTION__
2454                                       << ": Incomplete "
2455                                          "Byte Map !! \n";
2456                             componentsLoadedCallback->setError();
2457                             return;
2458                         }
2459 
2460                         /* Create clock buffer object and add it to global
2461                          * clockBuffers vector */
2462                         clockBuffers.emplace_front(
2463                             *bus, *address, *mode, *outCtrlBaseAddr,
2464                             *outCtrlByteCount, byteMap, *name, *type);
2465                     },
2466                     service, path, "org.freedesktop.DBus.Properties", "GetAll",
2467                     intf);
2468             }
2469         },
2470         mapper::busName, mapper::path, mapper::interface, mapper::subtree, "/",
2471         0, hsbpConfig.clockBufferTypes);
2472 }
2473 /***************************** End of Section *******************************/
2474 
2475 /****************************************************************************/
2476 /***************** HSBP Config related Function Definitions *****************/
2477 /****************************************************************************/
2478 void loadHsbpConfig()
2479 {
2480     appState = AppState::loadingHsbpConfig;
2481 
2482     conn->async_method_call(
2483         [](const boost::system::error_code ec, const GetSubTreeType& subtree) {
2484             if (ec)
2485             {
2486                 std::cerr << __FUNCTION__ << ": Error contacting mapper "
2487                           << ec.message() << "\n";
2488                 return;
2489             }
2490 
2491             if (subtree.empty())
2492             {
2493                 /* Entity manager is either still loading the configuration or
2494                  * failed to load. In either way, return from here as the dbus
2495                  * match will take care */
2496                 std::cerr << __FUNCTION__ << ": No configuration detected !!\n";
2497                 return;
2498             }
2499 
2500             /* There should be only one HSBP Configureation exposed */
2501             if (subtree.size() != 1)
2502             {
2503                 std::cerr << __FUNCTION__
2504                           << ": Multiple configurations "
2505                              "detected !!\n";
2506                 /* Critical Error. Stop Application */
2507                 stopHsbpManager();
2508                 return;
2509             }
2510 
2511             auto& path = subtree.begin()->first;
2512             auto& objDict = subtree.begin()->second;
2513 
2514             if (objDict.empty())
2515             {
2516                 /* Critical Error. Stop Application */
2517                 std::cerr << __FUNCTION__ << ": Subtree data corrupted !\n";
2518                 stopHsbpManager();
2519                 return;
2520             }
2521 
2522             const std::string& service = objDict.begin()->first;
2523 
2524             conn->async_method_call(
2525                 [](const boost::system::error_code er,
2526                    const boost::container::flat_map<std::string,
2527                                                     BasicVariantType>& resp) {
2528                     if (er)
2529                     {
2530                         std::cerr << __FUNCTION__ << ": Error Getting Config "
2531                                   << er.message() << "\n";
2532                         /* Critical Error. Stop Application */
2533                         stopHsbpManager();
2534                         return;
2535                     }
2536 
2537                     std::optional<uint64_t> rootI2cBus;
2538                     std::optional<std::vector<std::string>> supportedHsbps;
2539                     std::optional<std::vector<std::string>> clockBufferTypes;
2540                     std::optional<std::vector<std::string>> ioExpanderTypes;
2541 
2542                     /* Loop through to get root i2c bus and list of supported
2543                      * HSBPs */
2544                     for (const auto& [key, value] : resp)
2545                     {
2546                         if (key == "HsbpSupported")
2547                         {
2548                             supportedHsbps =
2549                                 std::get<std::vector<std::string>>(value);
2550                         }
2551                         else if (key == "RootI2cBus")
2552                         {
2553                             rootI2cBus = std::get<uint64_t>(value);
2554                         }
2555                         else if (key == "ClockBuffer")
2556                         {
2557                             clockBufferTypes =
2558                                 std::get<std::vector<std::string>>(value);
2559                         }
2560                         else if (key == "IoExpander")
2561                         {
2562                             ioExpanderTypes =
2563                                 std::get<std::vector<std::string>>(value);
2564                         }
2565                     }
2566 
2567                     /* Verify if i2c bus, supported HSBP's and clock buffers
2568                      * were defined (IO Expanders are optional) */
2569                     if (!rootI2cBus || !supportedHsbps || !clockBufferTypes)
2570                     {
2571                         std::cerr << __FUNCTION__
2572                                   << ": Incomplete HSBP "
2573                                      "configuration !! \n";
2574                         /* Critical Error. Stop Application */
2575                         stopHsbpManager();
2576                         return;
2577                     }
2578 
2579                     /* Clear and Load all details to global hsbp configuration
2580                      * variable */
2581                     hsbpConfig.clearConfig();
2582                     hsbpConfig.rootBus = *rootI2cBus;
2583                     hsbpConfig.supportedHsbps = std::move(*supportedHsbps);
2584 
2585                     for (auto& clkBuffType : *clockBufferTypes)
2586                     {
2587                         hsbpConfig.clockBufferTypes.emplace_back(
2588                             "xyz.openbmc_project.Configuration." + clkBuffType);
2589                     }
2590 
2591                     if (ioExpanderTypes)
2592                     {
2593                         for (auto& ioCntrType : *ioExpanderTypes)
2594                         {
2595                             hsbpConfig.ioExpanderTypes.emplace_back(
2596                                 "xyz.openbmc_project.Configuration." +
2597                                 ioCntrType);
2598                         }
2599                     }
2600 
2601                     /* Loop through to get HSBP-NVME map and Components map
2602                      * details */
2603                     uint8_t hsbpMapCount = 0;
2604                     for (const auto& [key, value] : resp)
2605                     {
2606                         if (std::find(hsbpConfig.supportedHsbps.begin(),
2607                                       hsbpConfig.supportedHsbps.end(),
2608                                       key) != hsbpConfig.supportedHsbps.end())
2609                         {
2610                             std::optional<std::vector<std::string>> hsbpMap;
2611                             hsbpMap = std::get<NvmeMapping>(value);
2612                             if (!hsbpMap)
2613                             {
2614                                 break;
2615                             }
2616                             hsbpConfig.hsbpNvmeMap.try_emplace(key, *hsbpMap);
2617                             hsbpMapCount++;
2618                         }
2619                     }
2620 
2621                     /* Check if we were able to get all the HSBP-NVMe maps */
2622                     if (hsbpConfig.supportedHsbps.size() != hsbpMapCount)
2623                     {
2624                         std::cerr << __FUNCTION__
2625                                   << ": Incomplete HSBP Map "
2626                                      "details !! \n";
2627                         /* Critical Error. Stop Application */
2628                         stopHsbpManager();
2629                         return;
2630                     }
2631 
2632                     /* HSBP configuration is loaded */
2633                     appState = AppState::hsbpConfigLoaded;
2634                     std::cerr << "HSBP Config loaded !\n";
2635 
2636                     /* Get Clock buffers and IO expander details. Create shared
2637                      * object of AsyncCallbackHandler with success and error
2638                      * callback */
2639                     auto componentsLoadedCallback = std::make_shared<
2640                         AsyncCallbackHandler>(
2641                         []() {
2642                             /* Verify if all components were initialized without
2643                              * errors */
2644                             if (!verifyComponentsLoaded())
2645                             {
2646                                 /* The application cannot proceed further as
2647                                  * components initialization failed. App needs
2648                                  * Restart */
2649                                 appState = AppState::idle;
2650                                 std::cerr
2651                                     << "One or more Componenets initialization "
2652                                        "failed !! Restart Required !\n";
2653                                 stopHsbpManager();
2654                             }
2655 
2656                             appState = AppState::componentsLoaded;
2657                             setUpBackplanesAndDrives();
2658                         },
2659                         []() {
2660                             /* The application cannot proceed further as
2661                              * components load failed. App needs Restart */
2662                             appState = AppState::idle;
2663                             std::cerr << "Loading Componenets failed !! "
2664                                          "Restart Required !\n";
2665                             stopHsbpManager();
2666                         });
2667 
2668                     loadClockBufferInfo(componentsLoadedCallback);
2669 
2670                     if (ioExpanderTypes)
2671                     {
2672                         loadIoExpanderInfo(componentsLoadedCallback);
2673                     }
2674                 },
2675                 service, path, "org.freedesktop.DBus.Properties", "GetAll",
2676                 hsbpConfigIntf);
2677         },
2678         mapper::busName, mapper::path, mapper::interface, mapper::subtree, "/",
2679         0, std::array<const char*, 1>{hsbpConfigIntf});
2680 }
2681 
2682 void setupHsbpConfigMatch()
2683 {
2684     static auto hsbpConfigMatch = std::make_unique<sdbusplus::bus::match_t>(
2685         *conn,
2686         "sender='xyz.openbmc_project.EntityManager', type='signal', "
2687         "member='PropertiesChanged', "
2688         "interface='org.freedesktop.DBus.Properties', "
2689         "path_namespace='/xyz/openbmc_project/inventory/system/board', arg0='" +
2690             std::string(hsbpConfigIntf) + "'",
2691         [](sdbusplus::message_t& msg) {
2692             std::string intfName;
2693             boost::container::flat_map<std::string, BasicVariantType> values;
2694             msg.read(intfName, values);
2695 
2696             /* This match will be triggered for each of the property being set
2697              * under the hsbpConfig interface. "HsbpSupported" is one of the
2698              * important property which will enable us to read other properties.
2699              * So, when the match event occurs for "HsbpSupported" property
2700              * being set, we will call "loadHsbpConfig()" If the control has
2701              * come here, its either the first initialization or entity-manager
2702              * reload. So, we will reset the state to uninitialized
2703              */
2704             for (const auto& [key, value] : values)
2705             {
2706                 if (key == "HsbpSupported")
2707                 {
2708                     /* Configuration change detected, change the state to stop
2709                      * other processing */
2710                     appState = AppState::idle;
2711 
2712                     /* We will call the function after a small delay to let all
2713                      * the properties to be intialized */
2714                     auto loadTimer =
2715                         std::make_shared<boost::asio::steady_timer>(io);
2716                     loadTimer->expires_after(std::chrono::seconds(1));
2717                     loadTimer->async_wait(
2718                         [loadTimer](const boost::system::error_code ec) {
2719                             if (ec == boost::asio::error::operation_aborted)
2720                             {
2721                                 return;
2722                             }
2723                             else if (ec)
2724                             {
2725                                 std::cerr << __FUNCTION__ << ": Timer error"
2726                                           << ec.message() << "\n";
2727                                 if (hsbpConfig.supportedHsbps.empty())
2728                                 {
2729                                     /* Critical Error as none of the
2730                                      * configuration was loaded and timer
2731                                      * failed. Stop the application */
2732                                     stopHsbpManager();
2733                                 }
2734                                 return;
2735                             }
2736                             loadHsbpConfig();
2737                         });
2738                 }
2739             }
2740         });
2741 }
2742 /***************************** End of Section *******************************/
2743 
2744 /****************************************************************************/
2745 /***************** GPIO Events related Function Definitions *****************/
2746 /****************************************************************************/
2747 static void nvmeLvc3AlertHandler()
2748 {
2749     /* If the state is not backplanesLoaded, we ignore the GPIO event as we
2750      * cannot communicate to the backplanes yet */
2751     if (appState < AppState::backplanesLoaded)
2752     {
2753         std::cerr << __FUNCTION__
2754                   << ": HSBP not initialized ! Dropping the interrupt ! \n";
2755         return;
2756     }
2757 
2758     /* This GPIO event only indicates the addition or removal of drive to either
2759      * of CPU. The backplanes detected need to be scanned and detect which drive
2760      * has been added/removed and enable/diable clock accordingly */
2761     gpiod::line_event gpioLineEvent = nvmeLvc3AlertLine.event_read();
2762 
2763     if (gpioLineEvent.event_type == gpiod::line_event::FALLING_EDGE)
2764     {
2765         /* Check for HSBP Drives status to determine if any new drive has been
2766          * added/removed and update clocks accordingly */
2767         checkHsbpDrivesStatus();
2768     }
2769 
2770     nvmeLvc3AlertEvent.async_wait(
2771         boost::asio::posix::stream_descriptor::wait_read,
2772         [](const boost::system::error_code ec) {
2773             if (ec)
2774             {
2775                 std::cerr << __FUNCTION__
2776                           << ": nvmealert event error: " << ec.message()
2777                           << "\n";
2778             }
2779             nvmeLvc3AlertHandler();
2780         });
2781 }
2782 
2783 static bool hsbpRequestAlertGpioEvents(
2784     const std::string& name, const std::function<void()>& handler,
2785     gpiod::line& gpioLine,
2786     boost::asio::posix::stream_descriptor& gpioEventDescriptor)
2787 {
2788     // Find the GPIO line
2789     gpioLine = gpiod::find_line(name);
2790     if (!gpioLine)
2791     {
2792         std::cerr << __FUNCTION__ << ": Failed to find the " << name
2793                   << " line\n";
2794         return false;
2795     }
2796 
2797     try
2798     {
2799         gpioLine.request(
2800             {"hsbp-manager", gpiod::line_request::EVENT_BOTH_EDGES, 0});
2801     }
2802     catch (std::exception&)
2803     {
2804         std::cerr << __FUNCTION__ << ": Failed to request events for " << name
2805                   << "\n";
2806         return false;
2807     }
2808 
2809     int gpioLineFd = gpioLine.event_get_fd();
2810     if (gpioLineFd < 0)
2811     {
2812         std::cerr << __FUNCTION__ << ": Failed to get " << name << " fd\n";
2813         return false;
2814     }
2815 
2816     gpioEventDescriptor.assign(gpioLineFd);
2817 
2818     gpioEventDescriptor.async_wait(
2819         boost::asio::posix::stream_descriptor::wait_read,
2820         [&name, handler](const boost::system::error_code ec) {
2821             if (ec)
2822             {
2823                 std::cerr << __FUNCTION__ << ": " << name
2824                           << " fd handler error: " << ec.message() << "\n";
2825                 return;
2826             }
2827             handler();
2828         });
2829     return true;
2830 }
2831 /***************************** End of Section *******************************/
2832 
2833 int main()
2834 {
2835     std::cerr << "******* Starting hsbp-manager *******\n";
2836 
2837     /* Set the Dbus name */
2838     conn->request_name(busName);
2839 
2840     /* Add interface for storage inventory */
2841     objServer.add_interface("/xyz/openbmc_project/inventory/item/storage",
2842                             "xyz.openbmc_project.inventory.item.storage");
2843 
2844     /* HSBP initializtion flow:
2845      * 1. Register GPIO event callback on FM_SMB_BMC_NVME_LVC3_ALERT_N line
2846      * 2. Set up Dbus match for power - determine if host is up and running
2847      *    or powered off
2848      * 3. Set up Dbus match for HSBP backplanes and Drives
2849      * 4. Load HSBP config exposed by entity manager
2850      *    - Also setup a match to capture HSBP configuation in case
2851      *      entity-manager restarts
2852      * 5. Load Clock buffer and IO expander (and other peripherals if any
2853      *    related to HSBP functionality)
2854      *    - Reload the info each time HSBP configuration is changed
2855      * 6. Populate all Backpanes (HSBP's)
2856      * 7. Load all NVMe drive's and associate with HSBP Backpane
2857      */
2858 
2859     /* Register GPIO Events on FM_SMB_BMC_NVME_LVC3_ALERT_N */
2860     if (!hsbpRequestAlertGpioEvents("FM_SMB_BMC_NVME_LVC3_ALERT_N",
2861                                     nvmeLvc3AlertHandler, nvmeLvc3AlertLine,
2862                                     nvmeLvc3AlertEvent))
2863     {
2864         std::cerr << __FUNCTION__
2865                   << ": error: Unable to monitor events on HSBP "
2866                      "Alert line\n";
2867         return -1;
2868     }
2869 
2870     /* Setup Dbus-match for power */
2871     setupPowerMatch(conn);
2872 
2873     /* Setup Dbus-match for HSBP backplanes and Drives */
2874     setupBackplanesAndDrivesMatch();
2875 
2876     /* Setup HSBP Config match and load config
2877      * In the event of entity-manager reboot, the match will help catch new
2878      * configuration.
2879      * In the event of hsbp-manager reboot, loadHsbpConfig will get all
2880      * config details and will take care of remaining config's to be
2881      * loaded
2882      */
2883     setupHsbpConfigMatch();
2884     loadHsbpConfig();
2885 
2886     io.run();
2887     std::cerr << __FUNCTION__ << ": Aborting hsbp-manager !\n";
2888     return -1;
2889 }
2890