regulators: Implements support for i2c_write_bit

Implements support for parsing the i2c_write_bit JSON elements in the
configuration file parser.

Signed-off-by: Bob King <Bob_King@wistron.com>
Change-Id: I52a16b3391872f2d451ac2bde02f0cb335f4dedd
diff --git a/phosphor-regulators/src/config_file_parser.cpp b/phosphor-regulators/src/config_file_parser.cpp
index 9ac6060..cdf758e 100644
--- a/phosphor-regulators/src/config_file_parser.cpp
+++ b/phosphor-regulators/src/config_file_parser.cpp
@@ -17,6 +17,7 @@
 #include "config_file_parser.hpp"
 
 #include "config_file_parser_error.hpp"
+#include "i2c_interface.hpp"
 #include "pmbus_utils.hpp"
 
 #include <exception>
@@ -102,9 +103,8 @@
     }
     else if (element.contains("i2c_write_bit"))
     {
-        // TODO: Not implemented yet
-        // action = parseI2CWriteBit(element["i2c_write_bit"]);
-        // ++propertyCount;
+        action = parseI2CWriteBit(element["i2c_write_bit"]);
+        ++propertyCount;
     }
     else if (element.contains("i2c_write_byte"))
     {
@@ -194,6 +194,32 @@
     return chassis;
 }
 
+std::unique_ptr<I2CWriteBitAction> parseI2CWriteBit(const json& element)
+{
+    verifyIsObject(element);
+    unsigned int propertyCount{0};
+
+    // Required register property
+    const json& regElement = getRequiredProperty(element, "register");
+    uint8_t reg = parseStringToUint8(regElement);
+    ++propertyCount;
+
+    // Required position property
+    const json& positionElement = getRequiredProperty(element, "position");
+    uint8_t position = parseBitPosition(positionElement);
+    ++propertyCount;
+
+    // Required value property
+    const json& valueElement = getRequiredProperty(element, "value");
+    uint8_t value = parseBitValue(valueElement);
+    ++propertyCount;
+
+    // Verify no invalid properties exist
+    verifyPropertyCount(element, propertyCount);
+
+    return std::make_unique<I2CWriteBitAction>(reg, position, value);
+}
+
 std::unique_ptr<PMBusWriteVoutCommandAction>
     parsePMBusWriteVoutCommand(const json& element)
 {
diff --git a/phosphor-regulators/src/config_file_parser.hpp b/phosphor-regulators/src/config_file_parser.hpp
index 2b5ca0d..f96e6b4 100644
--- a/phosphor-regulators/src/config_file_parser.hpp
+++ b/phosphor-regulators/src/config_file_parser.hpp
@@ -17,6 +17,7 @@
 
 #include "action.hpp"
 #include "chassis.hpp"
+#include "i2c_write_bit_action.hpp"
 #include "pmbus_write_vout_command_action.hpp"
 #include "rule.hpp"
 
@@ -98,6 +99,56 @@
     parseActionArray(const nlohmann::json& element);
 
 /**
+ * Parses a JSON element containing a bit position (from 0-7).
+ *
+ * Returns the corresponding C++ uint8_t value.
+ *
+ * Throws an exception if parsing fails.
+ *
+ * @param element JSON element
+ * @return uint8_t value
+ */
+inline uint8_t parseBitPosition(const nlohmann::json& element)
+{
+    // Verify element contains an integer
+    if (!element.is_number_integer())
+    {
+        throw std::invalid_argument{"Element is not an integer"};
+    }
+    int value = element;
+    if ((value < 0) || (value > 7))
+    {
+        throw std::invalid_argument{"Element is not a bit position"};
+    }
+    return static_cast<uint8_t>(value);
+}
+
+/**
+ * Parses a JSON element containing a bit value (0 or 1).
+ *
+ * Returns the corresponding C++ uint8_t value.
+ *
+ * Throws an exception if parsing fails.
+ *
+ * @param element JSON element
+ * @return uint8_t value
+ */
+inline uint8_t parseBitValue(const nlohmann::json& element)
+{
+    // Verify element contains an integer
+    if (!element.is_number_integer())
+    {
+        throw std::invalid_argument{"Element is not an integer"};
+    }
+    int value = element;
+    if ((value < 0) || (value > 1))
+    {
+        throw std::invalid_argument{"Element is not a bit value"};
+    }
+    return static_cast<uint8_t>(value);
+}
+
+/**
  * Parses a JSON element containing a boolean.
  *
  * Returns the corresponding C++ boolean value.
@@ -151,6 +202,19 @@
 }
 
 /**
+ * Parses a JSON element containing an i2c_write_bit action.
+ *
+ * Returns the corresponding C++ I2CWriteBitAction object.
+ *
+ * Throws an exception if parsing fails.
+ *
+ * @param element JSON element
+ * @return I2CWriteBitAction object
+ */
+std::unique_ptr<I2CWriteBitAction>
+    parseI2CWriteBit(const nlohmann::json& element);
+
+/**
  * Parses a JSON element containing an 8-bit signed integer.
  *
  * Returns the corresponding C++ int8_t value.
@@ -254,6 +318,56 @@
 }
 
 /**
+ * Parses a JSON element containing a hexadecimal string.
+ *
+ * Returns the corresponding C++ uint8_t value.
+ *
+ * Throws an exception if parsing fails.
+ *
+ * @param element JSON element
+ * @return uint8_t value
+ */
+inline uint8_t parseStringToUint8(const nlohmann::json& element)
+{
+    std::string value = parseString(element);
+
+    bool isHex = (value.compare(0, 2, "0x") == 0) && (value.size() > 2) &&
+                 (value.size() < 5) &&
+                 (value.find_first_not_of("0123456789abcdefABCDEF", 2) ==
+                  std::string::npos);
+    if (!isHex)
+    {
+        throw std::invalid_argument{"Element is not hexadecimal string"};
+    }
+    return static_cast<uint8_t>(std::stoul(value, 0, 0));
+}
+
+/**
+ * Parses a JSON element containing an 8-bit unsigned integer.
+ *
+ * Returns the corresponding C++ uint8_t value.
+ *
+ * Throws an exception if parsing fails.
+ *
+ * @param element JSON element
+ * @return uint8_t value
+ */
+inline uint8_t parseUint8(const nlohmann::json& element)
+{
+    // Verify element contains an integer
+    if (!element.is_number_integer())
+    {
+        throw std::invalid_argument{"Element is not an integer"};
+    }
+    int value = element;
+    if ((value < 0) || (value > UINT8_MAX))
+    {
+        throw std::invalid_argument{"Element is not an 8-bit unsigned integer"};
+    }
+    return static_cast<uint8_t>(value);
+}
+
+/**
  * Verifies that the specified JSON element is a JSON array.
  *
  * Throws an invalid_argument exception if the element is not an array.