Extend blacklist function for address.

Currently, blacklist only support for blocking a entire bus from
scanning by FruDevice. This extension makes user can specify certain
addresses on i2c bus.

Example :
{
  "buses": [
    10,
    12,
    {
      "bus": 11,
      "addresses": ["0x40", "0x44"]
    }
  ]
}

User can put in number for blocking i2c buses and addresses on
certain bus.

Tested: Tested and verified systemd journal for function.
Change-Id: Ibdb8dd8c3b9593a0c5e0d60e9e765dbf82938dd7
Signed-off-by: Bonnie Lo <Bonnie_Lo@wiwynn.com>
diff --git a/docs/blacklist_configuration.md b/docs/blacklist_configuration.md
new file mode 100644
index 0000000..5a7eb54
--- /dev/null
+++ b/docs/blacklist_configuration.md
@@ -0,0 +1,54 @@
+# Blacklist Configuration
+
+The blacklist.json in package directory can determine i2c buses and addresses
+that should not be scanned by FruDevice. An integer blocks an entire bus from
+being scanned. A bus/addresses object can block specific addresses on the bus
+while allowing scanning others addresses on the same bus.
+
+## For buses
+
+Put in numbers of buses. For example:
+
+```json
+{
+  "buses": [1, 3, 5]
+}
+```
+
+Note that "buses" should be an array of unsigned integer.
+
+## For addresses
+
+Put in bus and addresses with this format:
+
+```json
+{
+  "buses": [
+    {
+      "bus": 3,
+      "addresses": ["0x30", "0x40"]
+    },
+    {
+      "bus": 5,
+      "addresses": ["0x55"]
+    }
+  ]
+}
+```
+
+Note that "bus" should be an unsigned integer and "addresses" be an array of
+string of hex.
+
+## For both
+
+```json
+{
+  "buses": [
+    1,
+    {
+      "bus": 3,
+      "addresses": ["0x30", "0x40"]
+    }
+  ]
+}
+```
diff --git a/src/fru_device.cpp b/src/fru_device.cpp
index 11f2872..6644d93 100644
--- a/src/fru_device.cpp
+++ b/src/fru_device.cpp
@@ -43,6 +43,7 @@
 #include <iostream>
 #include <limits>
 #include <map>
+#include <optional>
 #include <regex>
 #include <set>
 #include <sstream>
@@ -71,7 +72,8 @@
 
 const static constexpr char* i2CDevLocation = "/dev";
 
-static std::set<size_t> busBlacklist;
+static boost::container::flat_map<size_t, std::optional<std::set<size_t>>>
+    busBlacklist;
 struct FindDevicesWithCallback;
 
 static boost::container::flat_map<
@@ -320,10 +322,10 @@
     return pair.first;
 }
 
-std::set<int> findI2CEeproms(int i2cBus,
-                             const std::shared_ptr<DeviceMap>& devices)
+std::set<size_t> findI2CEeproms(int i2cBus,
+                                const std::shared_ptr<DeviceMap>& devices)
 {
-    std::set<int> foundList;
+    std::set<size_t> foundList;
 
     std::string path = "/sys/bus/i2c/devices/i2c-" + std::to_string(i2cBus);
 
@@ -355,10 +357,11 @@
 
         std::ssub_match subMatch = m[1];
         std::string addressString = subMatch.str();
+        std::string_view addressStringView(addressString);
 
-        std::size_t ignored = 0;
-        const int hexBase = 16;
-        int address = std::stoi(addressString, &ignored, hexBase);
+        size_t address = 0;
+        std::from_chars(addressStringView.begin(), addressStringView.end(),
+                        address, 16);
 
         const std::string eeprom = node + "/eeprom";
 
@@ -403,16 +406,39 @@
         // hexdumps of the eeprom later were successful.
 
         // Scan for i2c eeproms loaded on this bus.
-        std::set<int> skipList = findI2CEeproms(bus, devices);
+        std::set<size_t> skipList = findI2CEeproms(bus, devices);
         std::set<size_t>& failedItems = failedAddresses[bus];
         std::set<size_t>& foundItems = fruAddresses[bus];
         foundItems.clear();
 
+        auto busFind = busBlacklist.find(bus);
+        if (busFind != busBlacklist.end())
+        {
+            if (busFind->second != std::nullopt)
+            {
+                for (const auto& address : *(busFind->second))
+                {
+                    skipList.insert(address);
+                }
+            }
+        }
+
         std::set<size_t>* rootFailures = nullptr;
         int rootBus = getRootBus(bus);
 
         if (rootBus >= 0)
         {
+            auto rootBusFind = busBlacklist.find(rootBus);
+            if (rootBusFind != busBlacklist.end())
+            {
+                if (rootBusFind->second != std::nullopt)
+                {
+                    for (const auto& rootAddress : *(rootBusFind->second))
+                    {
+                        skipList.insert(rootAddress);
+                    }
+                }
+            }
             rootFailures = &(failedAddresses[rootBus]);
             foundItems = fruAddresses[rootBus];
         }
@@ -528,7 +554,7 @@
         std::cerr << "Error reading bus " << bus << "\n";
         if (powerIsOn)
         {
-            busBlacklist.insert(bus);
+            busBlacklist[bus] = std::nullopt;
         }
         close(file);
         return -1;
@@ -581,9 +607,32 @@
         // Catch exception here for type mis-match.
         try
         {
-            for (const auto& bus : buses)
+            for (const auto& busIterator : buses)
             {
-                busBlacklist.insert(bus.get<size_t>());
+                // If bus and addresses field are missing, that's fine.
+                if (busIterator.contains("bus") &&
+                    busIterator.contains("addresses"))
+                {
+                    auto busData = busIterator.at("bus");
+                    auto bus = busData.get<size_t>();
+
+                    auto addressData = busIterator.at("addresses");
+                    auto addresses =
+                        addressData.get<std::set<std::string_view>>();
+
+                    busBlacklist[bus].emplace();
+                    for (const auto& address : addresses)
+                    {
+                        size_t addressInt = 0;
+                        std::from_chars(address.begin() + 2, address.end(),
+                                        addressInt, 16);
+                        busBlacklist[bus]->insert(addressInt);
+                    }
+                }
+                else
+                {
+                    busBlacklist[busIterator.get<size_t>()] = std::nullopt;
+                }
             }
         }
         catch (const nlohmann::detail::type_error& e)
@@ -608,15 +657,22 @@
             std::cerr << "Cannot translate " << i2cBus << " to int\n";
             continue;
         }
-        if (busBlacklist.find(bus) != busBlacklist.end())
+        auto busFind = busBlacklist.find(bus);
+        if (busFind != busBlacklist.end())
         {
-            continue; // skip previously failed busses
+            if (busFind->second == std::nullopt)
+            {
+                continue; // Skip blocked busses.
+            }
         }
-
         int rootBus = getRootBus(bus);
-        if (busBlacklist.find(rootBus) != busBlacklist.end())
+        auto rootBusFind = busBlacklist.find(rootBus);
+        if (rootBusFind != busBlacklist.end())
         {
-            continue;
+            if (rootBusFind->second == std::nullopt)
+            {
+                continue;
+            }
         }
 
         auto file = open(i2cBus.c_str(), O_RDWR);