Utility functions for download firmware

Added set of utility functions to be used within the classes for
managing the PSU firmware updates. Here is a breakdown of the key
functions:

- getDevicePath():
  Construct the PSU device path using I2C bus and address.

- getClassInstance():
  Determines the appropriate updater class to use based on PSU model
  number.

- getFWFilenamePath():
  Searches a directory for a firmware file matching a specified prefix
  and file extension (.bin or .hex).

- calculateCRC8():
  Computes the CRC-8 checksum for transferred data.

- delay():
  Pauses execution for a specified number of milliseconds.

- bigEndianToLittleEndian():
  Converts a 32-bit value from big-endian to little-endian.

- validateFWFile():
  Checks if a firmware file exists and is non-empty.

- openFirmwareFile():
  Opens a firmware file in binary mode, returning a file stream if
  successful.

- readFirmwareBytes():
  Reads specified number of data bytes from a firmware file into a
  buffer. Return data read or null to the caller.

- usePsuJsonFile():
  Wrapper to check the existence of the PSU JSON file.

- Class accessors to private data:
  getPsuInventoryPath(): Accessor for PSU inventory path.
  getDevPath(): Accessor for device path.
  getDevName(): Accessor for device name.
  getImageDir(): Accessor for image directory.
  getI2C(): I2C interface accessor.

Tested every function manually:

- getDevicePath() (using busctl):
  - Validate I2C bus and address values through psuInventoryPath
    validate expected result
  - Modified psuInventoryPath to invalid path
    validate returned invalid path.

- getClassInstance():
  - Validate with matching model number the function instantiate
    appropriate class.
  - Validate the default class instantiated.

- getFWFilenamePath():
  - Validate return of the correct file name in the PSU FW directory
  - Validate null returns when FW files don't exist

- calculaterCRC8():
  - Validate single byte 0x0 result 0x0, single byte 0x01 result 0x07

- delay():
  - Verified the task suspend execution.

- bigEndianToLittleEndian():
  - Verified input 0x12345678 resulted in 0x78563412

- validateFWFile():
  - Validate the existence of the file otherwise an error is logged
  - Validate the file size is greater than 0 otherwise an error is
    logged.

- openFirmwareFile():
  - Validate ifstream object is returned and was able to read from.
  - Validate error logged if the file name is null
  - validate error logged when unable to open the file

- readFirmwareBytes():
  - Validate data read from FW file
  - Validate number of bytes read.

- usePsuJsonFile():
  - Added JSON file to simulator and validated true return.

Change-Id: I0b8b24ae7d37724dab608d2c4977c1b42d4e1632
Signed-off-by: Faisal Awada <faisal@us.ibm.com>
diff --git a/tools/power-utils/updater.hpp b/tools/power-utils/updater.hpp
index 458f651..1732f86 100644
--- a/tools/power-utils/updater.hpp
+++ b/tools/power-utils/updater.hpp
@@ -16,11 +16,15 @@
 #pragma once
 
 #include "i2c_interface.hpp"
+#include "version.hpp"
 
 #include <sdbusplus/bus.hpp>
 
+#include <chrono>
 #include <filesystem>
+#include <memory>
 #include <string>
+#include <thread>
 
 class TestUpdater;
 
@@ -32,12 +36,14 @@
 /**
  * Update PSU firmware
  *
+ * @param[in] bus - The sdbusplus DBus bus connection
  * @param[in] psuInventoryPath - The inventory path of the PSU
  * @param[in] imageDir - The directory containing the PSU image
  *
  * @return true if successful, otherwise false
  */
-bool update(const std::string& psuInventoryPath, const std::string& imageDir);
+bool update(sdbusplus::bus_t& bus, const std::string& psuInventoryPath,
+            const std::string& imageDir);
 
 class Updater
 {
@@ -60,7 +66,7 @@
             const std::string& imageDir);
 
     /** @brief Destructor */
-    ~Updater() = default;
+    virtual ~Updater() = default;
 
     /** @brief Bind or unbind the driver
      *
@@ -84,7 +90,7 @@
      *
      * @return 0 if success, otherwise non-zero
      */
-    int doUpdate();
+    virtual int doUpdate();
 
     /** @brief Create I2C device
      *
@@ -93,6 +99,37 @@
      */
     void createI2CDevice();
 
+  protected:
+    /** @brief Accessor for PSU inventory path */
+    const std::string& getPsuInventoryPath() const
+    {
+        return psuInventoryPath;
+    }
+
+    /** @brief Accessor for device path */
+    const std::string& getDevPath() const
+    {
+        return devPath;
+    }
+
+    /** @brief Accessor for device name */
+    const std::string& getDevName() const
+    {
+        return devName;
+    }
+
+    /** @brief Accessor for image directory */
+    const std::string& getImageDir() const
+    {
+        return imageDir;
+    }
+
+    /** @brief I2C interface accessor */
+    i2c::I2CInterface* getI2C()
+    {
+        return i2c.get();
+    }
+
   private:
     /** @brief The sdbusplus DBus bus connection */
     sdbusplus::bus_t bus;
@@ -128,4 +165,122 @@
     std::unique_ptr<i2c::I2CInterface> i2c;
 };
 
+namespace internal
+{
+
+/**
+ * @brief Get the device name from the device path
+ *
+ * @param[in] devPath - PSU path
+ *
+ * @return device name e.g. 3-0068
+ */
+std::string getDeviceName(std::string devPath);
+
+/**
+ * @brief Function to get device path using DBus bus and PSU
+ * inventory Path
+ *
+ * @param[in] bus - The sdbusplus DBus bus connection
+ * @param[in] psuInventoryPath - PSU inventory path
+ *
+ * @return device path e.g. /sys/bus/i2c/devices/3-0068
+ */
+std::string getDevicePath(sdbusplus::bus_t& bus,
+                          const std::string& psuInventoryPath);
+
+/**
+ * @brief Parse the device name to obtain bus and device address
+ *
+ * @param[in] devName - Device name
+ *
+ * @return bus and device address
+ */
+std::pair<uint8_t, uint8_t> parseDeviceName(const std::string& devName);
+
+/**
+ * @brief Factory function to create an Updater instance based on PSU model
+ * number
+ *
+ * @param[in] model - PSU model number
+ * @param[in] psuInventoryPath - PSU inventory path
+ * @param[in] devPath - Device path
+ * @param[in] imageDir - Image directory
+ *
+ * return pointer class based on the device PSU model.
+ */
+std::unique_ptr<updater::Updater> getClassInstance(
+    const std::string& model, const std::string& psuInventoryPath,
+    const std::string& devPath, const std::string& imageDir);
+
+/**
+ * @brief Retrieve the firmware filename path in the specified directory
+ *
+ * @param[in] directory - Path to FS directory
+ *
+ * @retun filename or null
+ */
+const std::string getFWFilenamePath(const std::string& directory);
+
+/**
+ * @brief Calculate CRC-8 for a data vector
+ *
+ * @param[in] data - Firmware data block
+ *
+ * @return CRC8
+ */
+uint8_t calculateCRC8(const std::vector<uint8_t>& data);
+
+/**
+ * @brief Delay execution in milliseconds
+ *
+ * @param[in] milliseconds - Time in milliseconds
+ */
+void delay(const int& milliseconds);
+
+/**
+ * @brief Convert a big-endian value to little-endian
+ *
+ * @param[in] bigEndianValue - Uint 32 bit value
+ *
+ * @return vector of little endians.
+ */
+std::vector<uint8_t> bigEndianToLittleEndian(const uint32_t bigEndianValue);
+
+/**
+ * @brief Validate the existence and size of a firmware file.
+ *
+ * @param[in] fileName - Firmware file name
+ *
+ * @return true for success or false
+ */
+bool validateFWFile(const std::string& fileName);
+
+/**
+ * @brief Open a firmware file for reading in binary mode.
+ *
+ * @param[in] fileName - Firmware file name
+ *
+ * @return pointer to firmware file stream
+ */
+std::unique_ptr<std::ifstream> openFirmwareFile(const std::string& fileName);
+
+/**
+ * @brief Read firmware bytes from file.
+ *
+ * @param[in] inputFile - Input file stream
+ * @param[in] numberOfBytesToRead - Number of bytes to read from firmware file.
+ *
+ * @return vector of data read
+ */
+std::vector<uint8_t> readFirmwareBytes(std::ifstream& inputFile,
+                                       const size_t numberOfBytesToRead);
+
+/**
+ * @brief Wrapper to check existence of PSU JSON file
+ *
+ * @return true or false (true if using JSON file)
+ */
+bool usePsuJsonFile();
+} // namespace internal
 } // namespace updater