Muxes: Add symlink to /dev/i2c* support
It is difficult to tell what mux is what i2c channel
when looking at sysfs or d-bus alone. This creates
/dev/mux/<muxname>/<channelname> symlinks to aid other
applications.
Tested-by:
ls /dev/i2c-mux/Riser_*
/dev/mux/Riser_1_Mux:
PcieSlot1 PcieSlot2 PcieSlot3
/dev/i2c-mux/Riser_2_Mux:
PcieSlot1 PcieSlot2 PcieSlot3
Change-Id: I3485d87ad2546d4bc27092bac91d6add59250736
Signed-off-by: James Feist <james.feist@linux.intel.com>
diff --git a/src/Overlay.cpp b/src/Overlay.cpp
index 5596ac3..07b5e60 100644
--- a/src/Overlay.cpp
+++ b/src/Overlay.cpp
@@ -17,6 +17,7 @@
#include <string>
#include <iostream>
#include <regex>
+#include <iomanip>
#include <boost/process/child.hpp>
#include <boost/algorithm/string/predicate.hpp>
#include "filesystem.hpp"
@@ -34,15 +35,13 @@
constexpr const char *TEMPLATE_CHAR = "$";
constexpr const char *HEX_FORMAT_STR = "0x";
constexpr const char *PLATFORM = "aspeed,ast2500";
-constexpr const char *I2C_DEVS_DIR = "/sys/class/i2c-dev";
+constexpr const char *I2C_DEVS_DIR = "/sys/bus/i2c/devices";
+constexpr const char *MUX_SYMLINK_DIR = "/dev/i2c-mux";
// some drivers need to be unbind / bind to load device tree changes
static const boost::container::flat_map<std::string, std::string> FORCE_PROBES =
{{"IntelFanConnector", "/sys/bus/platform/drivers/aspeed_pwm_tacho"}};
-static const boost::container::flat_set<std::string> MUX_TYPES = {
- {"PCA9543Mux"}, {"PCA9545Mux"}};
-
std::regex ILLEGAL_NAME_REGEX("[^A-Za-z0-9_]");
void createOverlay(const std::string &templatePath,
@@ -106,40 +105,80 @@
return in.dump();
}
-// when muxes create new i2c devices, the symbols are not there to map the new
-// i2c devices to the mux address. this looks up the device tree path and adds
-// the new symbols so the new devices can be referenced via the phandle
-void fixupSymbols(const std::vector<std::filesystem::path> &i2cDevsBefore)
+void linkMux(const std::string &muxName, size_t bus, size_t address,
+ const nlohmann::json::array_t &channelNames)
{
- std::vector<std::filesystem::path> i2cDevsAfter;
- findFiles(std::filesystem::path(I2C_DEVS_DIR),
- R"(i2c-\d+)", i2cDevsAfter);
-
- for (const auto &dev : i2cDevsAfter)
+ // create directory first time
+ static bool createDir = false;
+ std::error_code ec;
+ if (!createDir)
{
- if (std::find(i2cDevsBefore.begin(), i2cDevsBefore.end(), dev) !=
- i2cDevsBefore.end())
+ std::filesystem::create_directory(MUX_SYMLINK_DIR, ec);
+ createDir = true;
+ }
+ std::ostringstream hex;
+ hex << std::hex << std::setfill('0') << std::setw(4) << address;
+ const std::string &addressHex = hex.str();
+
+ std::filesystem::path devDir(I2C_DEVS_DIR);
+ devDir /= std::to_string(bus) + "-" + addressHex;
+
+ size_t channel = 0;
+ std::string channelName;
+ std::filesystem::path channelPath = devDir / "channel-0";
+ while (is_symlink(channelPath))
+ {
+ if (channel > channelNames.size())
+ {
+ channelName = std::to_string(channel);
+ }
+ else
+ {
+ const std::string *ptr =
+ channelNames[channel].get_ptr<const std::string *>();
+ if (ptr == nullptr)
+ {
+ channelName = channelNames[channel].dump();
+ }
+ else
+ {
+ channelName = *ptr;
+ }
+ }
+ // if configuration has name empty, don't put it in dev
+ if (channelName.empty())
{
continue;
}
- // removes everything before and including the '-' in /path/i2c-##
- std::string bus =
- std::regex_replace(dev.string(), std::regex("^.*-"), "");
- std::string devtreeRef = dev.string() + "/device/of_node";
- auto devtreePath = std::filesystem::path(devtreeRef);
- std::string symbolPath = std::filesystem::canonical(devtreePath);
- symbolPath =
- symbolPath.substr(sizeof("/sys/firmware/devicetree/base") - 1);
- nlohmann::json configuration = {{"Path", symbolPath},
- {"Type", "Symbol"},
- {"Bus", std::stoul(bus)},
- {"Name", "i2c" + bus}};
- createOverlay(TEMPLATE_DIR + std::string("/Symbol.template"),
- configuration);
+
+ std::filesystem::path bus = std::filesystem::read_symlink(channelPath);
+ const std::string &busName = bus.filename();
+
+ std::string linkDir = MUX_SYMLINK_DIR + std::string("/") + muxName;
+ if (channel == 0)
+ {
+ std::filesystem::create_directory(linkDir, ec);
+ }
+ std::filesystem::create_symlink(
+ "/dev/" + busName, linkDir + std::string("/") + channelName, ec);
+
+ if (ec)
+ {
+ std::cerr << "Failure creating symlink for " << busName << "\n";
+ return;
+ }
+
+ channel++;
+ channelPath = devDir / ("channel-" + std::to_string(channel));
+ }
+ if (channel == 0)
+ {
+ std::cerr << "Mux missing channels " << devDir << "\n";
}
}
-void exportDevice(const devices::ExportTemplate &exportTemplate,
+void exportDevice(const std::string &type,
+ const devices::ExportTemplate &exportTemplate,
const nlohmann::json &configuration)
{
@@ -148,6 +187,7 @@
std::string name = "unknown";
const uint64_t *bus = nullptr;
const uint64_t *address = nullptr;
+ const nlohmann::json::array_t *channels = nullptr;
for (auto keyPair = configuration.begin(); keyPair != configuration.end();
keyPair++)
@@ -174,6 +214,11 @@
{
address = keyPair.value().get_ptr<const uint64_t *>();
}
+ else if (keyPair.key() == "ChannelNames")
+ {
+ channels =
+ keyPair.value().get_ptr<const nlohmann::json::array_t *>();
+ }
boost::replace_all(parameters, TEMPLATE_CHAR + keyPair.key(),
subsituteString);
boost::replace_all(device, TEMPLATE_CHAR + keyPair.key(),
@@ -214,6 +259,10 @@
}
deviceFile << parameters;
deviceFile.close();
+ if (boost::ends_with(type, "Mux") && bus && address && channels)
+ {
+ linkMux(name, *bus, *address, *channels);
+ }
}
// this is now deprecated
@@ -369,7 +418,7 @@
auto device = devices::exportTemplates.find(type.c_str());
if (device != devices::exportTemplates.end())
{
- exportDevice(device->second, configuration);
+ exportDevice(type, device->second, configuration);
}
}
}