Add Support to do device bind and unbind based on OCC status

Fixes openbmc/openbmc#1698

Change-Id: Iaa33c3065b0358b62e3ec7e39d4b57c6b9d181e1
Signed-off-by: Vishwanatha Subbanna <vishwa@linux.vnet.ibm.com>
diff --git a/Makefile.am b/Makefile.am
index a15ea12..0ab5856 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -7,6 +7,7 @@
 openpower_occ_control_SOURCES = \
 	occ_pass_through.cpp \
 	occ_status.cpp \
+	occ_device.cpp \
 	app.cpp \
 	org/open_power/OCC/PassThrough/error.cpp
 
diff --git a/configure.ac b/configure.ac
index 94673da..693c864 100644
--- a/configure.ac
+++ b/configure.ac
@@ -59,6 +59,10 @@
 AS_IF([test "x$OCC_NAME" == "x"], [OCC_NAME="occ"])
 AC_DEFINE_UNQUOTED([OCC_NAME], ["$OCC_NAME"], [The OCC object name])
 
+AC_ARG_VAR(OCC_HWMON_PATH, [The OCC hwmon path])
+AS_IF([test "x$OCC_HWMON_PATH" == "x"], [OCC_HWMON_PATH="/sys/bus/platform/drivers/occ-hwmon/"])
+AC_DEFINE_UNQUOTED([OCC_HWMON_PATH], ["$OCC_HWMON_PATH"], [The OCC hwmon path])
+
 AC_CONFIG_HEADERS([config.h])
 AC_CONFIG_FILES([Makefile])
 AC_OUTPUT
diff --git a/occ_device.cpp b/occ_device.cpp
new file mode 100644
index 0000000..865aef2
--- /dev/null
+++ b/occ_device.cpp
@@ -0,0 +1,12 @@
+#include "occ_device.hpp"
+
+namespace open_power
+{
+namespace occ
+{
+
+fs::path Device::bindPath = fs::path(OCC_HWMON_PATH) / "bind";
+fs::path Device::unBindPath = fs::path(OCC_HWMON_PATH) / "unbind";
+
+} // namespace occ
+} // namespace open_power
diff --git a/occ_device.hpp b/occ_device.hpp
new file mode 100644
index 0000000..dc057be
--- /dev/null
+++ b/occ_device.hpp
@@ -0,0 +1,79 @@
+#pragma once
+
+#include <fstream>
+#include <experimental/filesystem>
+#include "config.h"
+namespace open_power
+{
+namespace occ
+{
+
+namespace fs = std::experimental::filesystem;
+
+/** @class Device
+ *  @brief Binds and unbinds the OCC driver upon request
+ */
+class Device
+{
+    public:
+        Device() = delete;
+        ~Device() = default;
+        Device(const Device&) = delete;
+        Device& operator=(const Device&) = delete;
+        Device(Device&&) = default;
+        Device& operator=(Device&&) = default;
+
+        /** @brief Constructs the Device object
+         *
+         *  @param[in] name - OCC instance name
+         */
+        Device(const std::string& name) :
+            config(name + '-' + "dev0")
+        {
+            // Nothing to do here
+        }
+
+        /** @brief Binds device to the OCC driver */
+        inline void bind()
+        {
+            return write(bindPath, config);
+        }
+
+        /** @brief Un-binds device from the OCC driver */
+        inline void unBind()
+        {
+            return write(unBindPath, config);
+        }
+
+    private:
+        /** @brief Config value to be used to do bind and unbind */
+        const std::string config;
+
+        /**  @brief To bind the device to the OCC driver, do:
+         *    Write occ<#>-dev0 to: /sys/bus/platform/drivers/occ-hwmon/bind
+         */
+        static fs::path bindPath;
+
+        /**  @brief To un-bind the device from the OCC driver, do:
+         *    Write occ<#>-dev0 to: /sys/bus/platform/drivers/occ-hwmon/unbind
+         */
+        static fs::path unBindPath;
+
+        /** @brief file writer to achieve bind and unbind
+         *
+         *  @param[in] filename - Name of file to be written
+         *  @param[in] data     - Data to be written to
+         *  @return             - None
+         */
+        void write(const fs::path& fileName, const std::string& data)
+        {
+            // If there is an error, move the exception all the way up
+            std::ofstream file(fileName, std::ios::out);
+            file << data;
+            file.close();
+            return;
+        }
+};
+
+} // namespace occ
+} // namespace open_power
diff --git a/occ_status.cpp b/occ_status.cpp
index a78ea47..ca8a1f9 100644
--- a/occ_status.cpp
+++ b/occ_status.cpp
@@ -7,6 +7,19 @@
 // Handles updates to occActive property
 bool Status::occActive(bool value)
 {
+    if (value != this->occActive())
+    {
+        if (value)
+        {
+            // Bind the device
+            device.bind();
+        }
+        else
+        {
+            // Do the unbind
+            device.unBind();
+        }
+    }
     return Base::Status::occActive(value);
 }
 
diff --git a/occ_status.hpp b/occ_status.hpp
index 068f83b..5d092b7 100644
--- a/occ_status.hpp
+++ b/occ_status.hpp
@@ -3,6 +3,7 @@
 #include <sdbusplus/bus.hpp>
 #include <sdbusplus/server/object.hpp>
 #include <org/open_power/OCC/Status/server.hpp>
+#include "occ_device.hpp"
 namespace open_power
 {
 namespace occ
@@ -30,11 +31,19 @@
          *  @param[in] path - DBus object path
          */
         Status(sdbusplus::bus::bus& bus, const char* path)
-            : Interface(bus, path)
+            : Interface(bus, path),
+              path(path),
+              device(name + std::to_string((this->path.back() - '0') + 1))
         {
             // Nothing to do here
         }
 
+        /** @brief Since we are overriding the setter-occActive but not the
+         *         getter-occActive, we need to have this using in order to
+         *         allow passthrough usage of the getter-occActive
+         */
+        using Base::Status::occActive;
+
         /** @brief SET OccActive to True or False
          *
          *  @param[in] value - Intended value
@@ -42,6 +51,16 @@
          *  @return          - Updated value of the property
          */
         bool occActive(bool value) override;
+
+    private:
+        /** @brief OCC dbus object path */
+        std::string path;
+
+        /** @brief occ name prefix */
+        std::string name = OCC_NAME;
+
+        /** @brief OCC device object to do bind and unbind */
+        Device device;
 };
 
 } // namespace occ