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/configurations/1Ux16 Riser.json b/configurations/1Ux16 Riser.json
index 08aa342..9d985e1 100644
--- a/configurations/1Ux16 Riser.json
+++ b/configurations/1Ux16 Riser.json
@@ -36,8 +36,14 @@
{
"Address": "0x72",
"Bus": "$bus",
+ "ChannelNames": [
+ "PcieSlot1",
+ "PcieSlot2",
+ "PcieSlot3",
+ ""
+ ],
"Name": "Riser 1 Mux",
- "Type": "PCA9543Mux"
+ "Type": "PCA9545Mux"
},
{
"Address": "$address",
@@ -92,8 +98,14 @@
{
"Address": "0x73",
"Bus": "$bus",
+ "ChannelNames": [
+ "PcieSlot1",
+ "PcieSlot2",
+ "PcieSlot3",
+ ""
+ ],
"Name": "Riser 2 Mux",
- "Type": "PCA9543Mux"
+ "Type": "PCA9545Mux"
},
{
"Address": "$address",
diff --git a/configurations/2Ux8 Riser.json b/configurations/2Ux8 Riser.json
index 49b7a6d..842d26c 100644
--- a/configurations/2Ux8 Riser.json
+++ b/configurations/2Ux8 Riser.json
@@ -36,6 +36,12 @@
{
"Address": "0x72",
"Bus": "$bus",
+ "ChannelNames": [
+ "PcieSlot1",
+ "PcieSlot2",
+ "PcieSlot3",
+ ""
+ ],
"Name": "Riser 1 Mux",
"Type": "PCA9545Mux"
},
@@ -92,6 +98,12 @@
{
"Address": "0x73",
"Bus": "$bus",
+ "ChannelNames": [
+ "PcieSlot1",
+ "PcieSlot2",
+ "PcieSlot3",
+ ""
+ ],
"Name": "Riser 2 Mux",
"Type": "PCA9545Mux"
},
diff --git a/configurations/A2UL16RISER.json b/configurations/A2UL16RISER.json
index f02cec8..d3850c9 100644
--- a/configurations/A2UL16RISER.json
+++ b/configurations/A2UL16RISER.json
@@ -36,6 +36,12 @@
{
"Address": "0x72",
"Bus": "$bus",
+ "ChannelNames": [
+ "PcieSlot1",
+ "PcieSlot2",
+ "PcieSlot3",
+ ""
+ ],
"Name": "Riser 2 Mux",
"Type": "PCA9544Mux"
},
@@ -92,6 +98,12 @@
{
"Address": "0x73",
"Bus": "$bus",
+ "ChannelNames": [
+ "PcieSlot1",
+ "PcieSlot2",
+ "PcieSlot3",
+ ""
+ ],
"Name": "Riser 2 Mux",
"Type": "PCA9544Mux"
},
diff --git a/include/devices.hpp b/include/devices.hpp
index 2756731..24cba32 100644
--- a/include/devices.hpp
+++ b/include/devices.hpp
@@ -50,6 +50,9 @@
{"PCA9545Mux",
ExportTemplate("pca9545 $Address",
"/sys/bus/i2c/devices/i2c-$Bus/new_device")},
+ {"PCA9546Mux",
+ ExportTemplate("pca9546 $Address",
+ "/sys/bus/i2c/devices/i2c-$Bus/new_device")},
{"pmbus", ExportTemplate("pmbus $Address",
"/sys/bus/i2c/devices/i2c-$Bus/new_device")},
{"TMP75", ExportTemplate("tmp75 $Address",
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);
}
}
}