sysfs: refactor findCallout

 - Export findCalloutPath so other modules can use it.
 - Change parameter to hwmon instance path to avoid
      fs::canonical outside sysfs.cpp.
 - Check for the iio-hwmon driver instead of relying on
      DT nodes having 'iio-hwmon' in them.
 - For iio devices, provide a /sys/devices path rather
      than a DT path.
 - Use existing application indenting and doxygen style.

Change-Id: I16c2dc7417eb68f7cbef44243f481df8040ec1fd
Signed-off-by: Brad Bishop <bradleyb@fuzziesquirrel.com>
diff --git a/sysfs.cpp b/sysfs.cpp
index 4117b2f..6d52029 100644
--- a/sysfs.cpp
+++ b/sysfs.cpp
@@ -83,31 +83,83 @@
     return emptyString;
 }
 
-/**
- * @brief Return the path to use for a call out.
- *
- * If the path does not contain iio-hwmon, assume passed in path is the call
- * out path.
- *
- * @param[in] ofPath - Open firmware path to search for matching phandle value
- *
- * @return Path to use for call out
- */
-std::string findCalloutPath(const std::string& ofPath)
+std::string findCalloutPath(const std::string& instancePath)
 {
-    static constexpr auto iioHwmonStr = "iio-hwmon";
+    // Follow the hwmon instance (/sys/class/hwmon/hwmon<N>)
+    // /sys/devices symlink.
+    fs::path devPath{instancePath};
+    devPath /= "device";
 
-    if (ofPath.find(iioHwmonStr) != std::string::npos)
+    try
     {
-        auto matchpath = findPhandleMatch(ofPath, ofRoot);
-        auto n = matchpath.rfind('/');
+        devPath = fs::canonical(devPath);
+    }
+    catch (const std::system_error& e)
+    {
+        return emptyString;
+    }
+
+    // See if the device is backed by the iio-hwmon driver.
+    fs::path p{devPath};
+    p /= "driver";
+    p = fs::canonical(p);
+
+    if (p.filename() != "iio_hwmon")
+    {
+        // Not backed by iio-hwmon.  The device pointed to
+        // is the callout device.
+        return devPath;
+    }
+
+    // Find the DT path to the iio-hwmon platform device.
+    fs::path ofDevPath{devPath};
+    ofDevPath /= "of_node";
+
+    try
+    {
+        ofDevPath = fs::canonical(ofDevPath);
+    }
+    catch (const std::system_error& e)
+    {
+        return emptyString;
+    }
+
+    // Search /sys/bus/iio/devices for the phandle in io-channels.
+    // If a match is found, use the corresponding /sys/devices
+    // iio device as the callout device.
+    static constexpr auto iioDevices = "/sys/bus/iio/devices";
+    for (const auto& iioDev: fs::recursive_directory_iterator(iioDevices))
+    {
+        p = iioDev.path();
+        p /= "of_node";
+
+        try
+        {
+            p = fs::canonical(p);
+        }
+        catch (const std::system_error& e)
+        {
+            continue;
+        }
+
+        auto match = findPhandleMatch(ofDevPath, p);
+        auto n = match.rfind('/');
         if (n != std::string::npos)
         {
-            return matchpath.substr(0, n);
+            // This is the iio device referred to by io-channels.
+            // Remove iio:device<N>.
+            try
+            {
+                return fs::canonical(iioDev).parent_path();
+            }
+            catch (const std::system_error& e)
+            {
+                return emptyString;
+            }
         }
     }
 
-    return ofPath;
+    return emptyString;
 }
 
 std::string findHwmon(const std::string& ofNode)
@@ -201,7 +253,7 @@
             exit(0);
         }
         instancePath /= "device";
-        auto callOutPath = findCalloutPath(fs::canonical(instancePath));
+        auto callOutPath = findCalloutPath(instancePath);
         using namespace sdbusplus::xyz::openbmc_project::Sensor::Device::Error;
 
         // this throws a ReadFailure.
@@ -209,8 +261,7 @@
             xyz::openbmc_project::Sensor::Device::
                 ReadFailure::CALLOUT_ERRNO(rc),
             xyz::openbmc_project::Sensor::Device::
-                ReadFailure::CALLOUT_DEVICE_PATH(
-                    fs::canonical(callOutPath).c_str()));
+                ReadFailure::CALLOUT_DEVICE_PATH(callOutPath.c_str()));
     }
 
     return value;
@@ -246,14 +297,13 @@
         // or write system calls that got us here.
         auto rc = errno;
         instancePath /= "device";
-        auto callOutPath = findCalloutPath(fs::canonical(instancePath));
+        auto callOutPath = findCalloutPath(instancePath);
         using namespace sdbusplus::xyz::openbmc_project::Control::Device::Error;
         report<WriteFailure>(
             xyz::openbmc_project::Control::Device::
                 WriteFailure::CALLOUT_ERRNO(rc),
             xyz::openbmc_project::Control::Device::
-                WriteFailure::CALLOUT_DEVICE_PATH(
-                    fs::canonical(callOutPath).c_str()));
+                WriteFailure::CALLOUT_DEVICE_PATH(callOutPath.c_str()));
 
         exit(EXIT_FAILURE);
     }
diff --git a/sysfs.hpp b/sysfs.hpp
index 9a8211d..f0e92db 100644
--- a/sysfs.hpp
+++ b/sysfs.hpp
@@ -64,6 +64,17 @@
  */
 std::string findHwmon(const std::string& ofNode);
 
+/** @brief Return the path to use for a call out.
+ *
+ *  Return an empty string if a callout path cannot be
+ *  found.
+ *
+ *  @param[in] instancePath - /sys/class/hwmon/hwmon<N> path.
+ *
+ *  @return Path to use for call out
+ */
+std::string findCalloutPath(const std::string& instancePath);
+
 /** @brief Read an hwmon sysfs value.
  *
  *  Calls exit(3) with bad status on failure.