#include "ipmi-oem.hpp"
#include <algorithm>
#include <cstdio>
#include <fcntl.h>
#include <linux/i2c.h>
#include <phosphor-logging/lg2.hpp>
#include <sys/ioctl.h>
#include <unistd.h>
#include <vector>
#include <fstream>
#include <unordered_map>
#include <boost/container/flat_map.hpp>
#include <filesystem>
#include <iostream>
#include <ctime>
#include <iomanip>
#include <nlohmann/json.hpp>
#include <iostream>
#include <string>
#include <cstdint>
#include <dirent.h>
#include <cstring>
#include <cerrno>

#include <xyz/openbmc_project/Software/Activation/server.hpp>
#include <xyz/openbmc_project/Software/Version/server.hpp>
#include <xyz/openbmc_project/State/BMC/server.hpp>

extern "C"
{
#include <i2c/smbus.h>
#include <linux/i2c-dev.h>
}

#define PSU0_ADDR 0x58
#define PSU1_ADDR 0x59
#define PSU_READ_ADC_CMD 0x8B
#define PSU_WRITE_ADC_CMD 0x21
#define PSU_ADC_VAL_MIN 0x1730
#define PSU_ADC_VAL_MAX 0x199E

constexpr uint8_t COVER_CPLD_I2C_BUS = 7;
constexpr uint8_t COVER_TMP468_I2C_SLAVE_ADDR = 0x90;
constexpr uint8_t COVER_CPLD_I2C_SLAVE_ADDR = 0xf8;
constexpr uint8_t COVER_OPEN_STATUS_REG = 0x0037;
constexpr uint8_t COVER_OPEN_CONTROL_REG = 0x008e;
constexpr uint8_t COVER_COMMAND_OPENED_STATUS = 0x01;
constexpr uint8_t COVER_COMMAND_CLOSE_STATUS = 0x02;
constexpr uint8_t COVER_COMMAND_UNCERTAIN_STATUS = 0x03;
constexpr uint8_t COVER_REG_OPENED = 0x04;
constexpr uint8_t COVER_REG_CLOSED = 0x05;
constexpr uint8_t COVER_REG_UNCERTAIN_STATUS = 0x07;
constexpr uint8_t MAX_CPU_ID_NUM = 2;
constexpr uint8_t MAX_DIMM_RANK_NUM = 12;
constexpr uint8_t GET_DIMM_TEMP_BUFF_SIZE = MAX_DIMM_RANK_NUM * 2;
constexpr uint8_t maxRetries = 5;

//define for thermal oem command
#define DIMM_SETPOINT_PATH  "/tmp/dimm_setpoint"
#define COVER_SETPOINT_PATH  "/tmp/cover_setpoint"
#define COVER_STATUS_PATH  "/tmp/cover_status"
#define FSC_CONTROL_MODE  "/tmp/fsc_control_mode"
#define FSC_ENABLE_STATUS_PATH  "/tmp/fsc_enable"
#define MAIN_BMC_BOOT_SOURCE   "/sys/devices/platform/ahb/1e620000.spi/access_primary"
#define BACKUP_BMC_BOOT_SOURCE "/sys/devices/platform/ahb/1e620000.spi/access_backup"

using namespace phosphor::logging;
using Version = sdbusplus::xyz::openbmc_project::Software::server::Version;
using Activation =
    sdbusplus::xyz::openbmc_project::Software::server::Activation;
using BMC = sdbusplus::xyz::openbmc_project::State::server::BMC;

constexpr static const char* softwareServiceName =
    "xyz.openbmc_project.Software.BMC.Updater";
constexpr static const char* biosActiveObjectName =
    "/xyz/openbmc_project/software/bios_active";
constexpr static const char* softwareVersionInterface =
    "xyz.openbmc_project.Software.Version";
constexpr static const char* versionProperty = "Version";
#define BIOSVERSIONLEN 32

constexpr auto DIMMTempSensorBusname = "xyz.openbmc_project.DIMMTempSensor";
constexpr auto dimmLEDInterface = "xyz.openbmc_project.Sensor.dimmLED";
constexpr auto dbusProperties = "org.freedesktop.DBus.Properties";
constexpr auto dimmSpdReaderService = "xyz.openbmc_project.DimmSpdReader";
constexpr auto tempInterface = "xyz.openbmc_project.Dimm.Temperature";
constexpr auto voltInterface = "xyz.openbmc_project.Dimm.Voltage";
constexpr auto curInterface = "xyz.openbmc_project.Dimm.Current";
constexpr auto powInterface = "xyz.openbmc_project.Dimm.Power";
constexpr auto dimmInfoPath = "/var/log/dimmInfo.json";
constexpr auto HostMiscDbusName = "xyz.openbmc_project.Host.Misc.Manager";
constexpr auto platformStatePath = "/xyz/openbmc_project/misc/platform_state";
constexpr auto platformStateInterface = "xyz.openbmc_project.State.Host.Misc";

static const std::unordered_map<uint8_t, std::string> dimm_map = {
        { 0x90, "DIMM_CPU0_A_Temp" },
        { 0x92, "DIMM_CPU0_B_Temp" },
        { 0x94, "DIMM_CPU0_C_Temp" },
        { 0x96, "DIMM_CPU0_D_Temp" },
        { 0x98, "DIMM_CPU0_E_Temp" },
        { 0x9A, "DIMM_CPU0_F_Temp" },
        { 0x9C, "DIMM_CPU0_G_Temp" },
        { 0x9E, "DIMM_CPU0_H_Temp" },
        { 0xA0, "DIMM_CPU0_I_Temp" },
        { 0xA2, "DIMM_CPU0_J_Temp" },
        { 0xA4, "DIMM_CPU0_K_Temp" },
        { 0xA6, "DIMM_CPU0_L_Temp" },
        { 0xB0, "DIMM_CPU1_A_Temp" },
        { 0xB2, "DIMM_CPU1_B_Temp" },
        { 0xB4, "DIMM_CPU1_C_Temp" },
        { 0xB6, "DIMM_CPU1_D_Temp" },
        { 0xB8, "DIMM_CPU1_E_Temp" },
        { 0xBA, "DIMM_CPU1_F_Temp" },
        { 0xBC, "DIMM_CPU1_G_Temp" },
        { 0xBE, "DIMM_CPU1_H_Temp" },
        { 0xC0, "DIMM_CPU1_I_Temp" },
        { 0xC2, "DIMM_CPU1_J_Temp" },
        { 0xC4, "DIMM_CPU1_K_Temp" },
        { 0xC6, "DIMM_CPU1_L_Temp" }
};

static const std::unordered_map<uint8_t, std::string> dimm_state_map = {
        { 0x0, "ON" },
        { 0x1, "FAULT" },
        { 0x2, "NA" }
};

enum class DeviceType : uint8_t {
  MLB_BMC = 0,
  MLB_BIOS,
  MLB_CPLD,
  PSU,
  FPGA,
  ALL_BP_CPLD
};

enum class PSUInfoType : uint8_t {
  MFG_ID = 1,
  MFG_MODEL,
  MFG_REVISION,
  MFG_DATE,
  MFG_LOCATION,
  MFG_SERIAL,
  PART_NUM,
  SERIAL_NUM,
  FW_REVISION,
  MAX_POUT,
  INPUT_LOST,
  STATUS,
  POWER_UNIT_GROUP,
  LOCATION,
  HOT_REPLACEABLE,
  POWER_SUPPLY_TYPE,
  HEALTH,
  INPUT_VOLTAGE_RANGE_SWITCH
};

constexpr uint8_t administrator = 0x01;
constexpr uint8_t user =  0x02;
std::vector<uint8_t> adminPasswd = {0xff,administrator,0};  //Encrypted Password Length = 0
std::vector<uint8_t> userPasswd  = {0xff,user,0};

void register_ipmi_oem_functions() __attribute__((constructor));

ipmi::RspType<std::vector<uint8_t>> GetDeviceFWVersion(ipmi::Context::ptr ctx,
                                                       uint8_t deviceType) {
  uint8_t deviceCount = 1;
  std::string firstVersion{}, secondVersion{}, thirdVersion{};
  ipmi::ObjectTree objectTree;

  std::vector<uint8_t> response{};
  auto purposeNeed = Version::VersionPurpose::BMC;

  switch (static_cast<DeviceType>(deviceType)) {
  case DeviceType::MLB_BMC:
    break;
  case DeviceType::MLB_BIOS:
    purposeNeed = Version::VersionPurpose::Host;
    break;
  case DeviceType::MLB_CPLD:
    purposeNeed = Version::VersionPurpose::CPLD;
    break;
  case DeviceType::PSU:
    break;
  case DeviceType::FPGA:
    break;
  case DeviceType::ALL_BP_CPLD:
    purposeNeed = Version::VersionPurpose::FBCPLD;
    break;
  default:
    return ipmi::responseParmOutOfRange();
  }

  try {
    objectTree =
        ipmi::getAllDbusObjects(*ctx->bus, "/xyz/openbmc_project/software",
                                "xyz.openbmc_project.Software.ExtendedVersion");
  } catch (const sdbusplus::exception_t &e) {
    lg2::error("Failed to fetch redundancy object from dbus");
  }

  for (auto &softObject : objectTree) {
    auto service = ipmi::getService(
        *ctx->bus, "xyz.openbmc_project.Software.ExtendedVersion",
        softObject.first);
    auto objValueTree = ipmi::getManagedObjects(
        *ctx->bus, service, "/xyz/openbmc_project/software");

    for (const auto &objIter : objValueTree) {
      try {
        auto &intfMap = objIter.second;
        auto &versionProps = intfMap.at("xyz.openbmc_project.Software.Version");
        auto &activationProps =
            intfMap.at("xyz.openbmc_project.Software.Activation");
        auto purpose = std::get<std::string>(versionProps.at("Purpose"));
        auto activation =
            std::get<std::string>(activationProps.at("Activation"));
        auto version = std::get<std::string>(versionProps.at("Version"));
        if ((Version::convertVersionPurposeFromString(purpose) ==
             purposeNeed) &&
            (Activation::convertActivationsFromString(activation) ==
             Activation::Activations::Active)) {
          firstVersion = std::move(version);
          break; // Example to find primary version
        }
      } catch (const std::exception &e) {
        lg2::error(e.what());
      }
    }
  }
  firstVersion.resize(16);
  secondVersion.resize(16);
  thirdVersion.resize(16);
  response.push_back(deviceCount);
  std::copy(firstVersion.begin(), firstVersion.end(),
            std::back_inserter(response));
  std::copy(secondVersion.begin(), secondVersion.end(),
            std::back_inserter(response));
  std::copy(thirdVersion.begin(), thirdVersion.end(),
            std::back_inserter(response));

  return ipmi::responseSuccess(response);
}

ipmi::RspType<> SetFanSpeedControlMode(std::vector<uint8_t> data) {
    std::vector<uint8_t> newArray(30);
    size_t i = 0;

    if(data.size() < 1)
    {
        lg2::error("invalid request size");
        return ipmi::responseReqDataLenInvalid();
    }

    if(data[0] > 2)
    {
        lg2::error("invalid fan control mode");
        return ipmi::responseInvalidFieldRequest();
    }
    else if(data[0] == FSC_FAN_TABLE_MANUAL)
    {
        /*request is manual mode */
        if(data.size() < 2)
        {
            lg2::error("invalid request size for manual mode");
            return ipmi::responseReqDataLenInvalid();
        }

        if(data[1] != 0)
        {
            if(data[1] > 100)
            {
                return ipmi::responseInvalidFieldRequest();
            }
            /*set all pwm to request manual pwm */
            for (i = 1; i < newArray.size(); ++i)
            {
                newArray[i] = data[1];
            }
        }
        else
        {
            if(data.size() != 31)
            {
                lg2::error("invalid request size for manual mode");
                return ipmi::responseReqDataLenInvalid();
            }
            for (i = 1; i < data.size(); ++i)
            {
                if (data[i] > 100)
                {
                    return ipmi::responseInvalidFieldRequest();
                }
            }
            size_t startIndex = 2;
            for (i = 0; i < MAX_FSC_PWM_CHANNEL; ++i)
            {
                if (startIndex + i < data.size())
                {
                    newArray[i + 1] = data[startIndex + i];
                }
                else
                {
                    newArray[i + 1] = 0;
                }
            }
        }
    }
    else
    {
        /*request is auto mode */
        for (i = 0; i < MAX_FSC_PWM_CHANNEL; ++i)
        {
            newArray[i + 1] = 0;
        }
    }

    newArray[0] = data[0];
    std::ofstream outFile(FSC_CONTROL_MODE, std::ios::binary);
    if (!outFile) {
        lg2::error("Error opening file /tmp/fsc_control_mode");
        return ipmi::responseBusy();
    }

    outFile.write(reinterpret_cast<const char*>(newArray.data()), newArray.size());
    if (!outFile) {
        lg2::error("Error write file /tmp/fsc_control_mode");
        outFile.close();
        return ipmi::responseBusy();
    }

    outFile.close();
    return ipmi::responseSuccess();
}

ipmi::RspType<std::vector<uint8_t>> GetFanSpeedControlMode() {
    std::vector<uint8_t> response(30);

    if(!std::filesystem::exists(FSC_CONTROL_MODE))
    {
        lg2::error("Error write file /tmp/fsc_control_mode");
        response[0] = FSC_FAN_TABLE_AUTO;
        response.resize(1);
        return ipmi::responseSuccess(response);
    }

    std::ifstream inputFile(FSC_CONTROL_MODE, std::ios::binary);

    if (!inputFile)
    {
        return ipmi::responseBusy();
    }

    inputFile.read(reinterpret_cast<char*>(response.data()), 30);
    if(inputFile.gcount() < 30)
    {
        return ipmi::responseUnspecifiedError();
    }
    inputFile.close();

    return ipmi::responseSuccess(response);
}

ipmi::RspType<> SetBIOSPasswordConfig(std::vector<uint8_t> data) {

    if(!(data[0] == 0x00 || data[0] == 0x01 || data[0] == 0xff))
    {
      return ipmi::responseParmOutOfRange();
    }

    // Clear Passwd
    if(data[1] == administrator)
    {
      adminPasswd[0] = data[0];
    }
    else if(data[1] == user)
    {
      userPasswd[0] = data[0];
    }

    if (data[0] == 0x01)
    {
      //todo set passwd
    }

    return ipmi::responseSuccess();

}

ipmi::RspType<std::vector<uint8_t>> GetBIOSPasswordConfig(std::vector<uint8_t> data) {
    std::vector<uint8_t> response(2);

    if(!(data[0] == 0x01 || data[0] == 0x02))
    {
      return ipmi::responseParmOutOfRange();
    }

    if (data[0] == administrator)
    {
        response.at(0) = adminPasswd[0];
        response.at(1) = adminPasswd[1];
    }
    else if (data[0] == user)
    {
        response.at(0) = userPasswd[0];
        response.at(1) = userPasswd[1];
    }

    return ipmi::responseSuccess(response);
}

ipmi::RspType<std::vector<uint8_t>> GetPSUInfo(uint8_t psuIndex, uint8_t parameter) {
  uint8_t success = 0;
  psuIndex = psuIndex-1;
  std::string psuIndexStr = std::to_string(psuIndex);
  std::vector<uint8_t> response (34,0);
  constexpr auto PSU_INTERFACE = "xyz.openbmc_project.Configuration.Status";
  std::string PSU_PATH = "/xyz/openbmc_project/inventory/system/board/Lux_Baseboard/PSU"
                          + psuIndexStr + "_Supply";
  std::string PSU_PROP = "Vendor";
  std::string type = "string";
  std::string propertyValueTmp;
  double dpropertyValueTmp = 0;
  std::string psuInfo;
  PSUInfoType parmType = static_cast<PSUInfoType>(parameter);

  switch (parmType) {
    case PSUInfoType::MFG_ID:
      PSU_PROP = "MFG_ID";
      break;
    case PSUInfoType::MFG_MODEL:
      PSU_PROP = "MFG_MODEL";
      break;
    case PSUInfoType::MFG_REVISION:
      PSU_PROP = "MFG_REVISION";
      break;
    case PSUInfoType::MFG_DATE:
      PSU_PROP = "MFG_DATE";
      break;
    case PSUInfoType::MFG_LOCATION:
      PSU_PROP = "MFG_LOCATION";
      break;
    case PSUInfoType::MFG_SERIAL:
      PSU_PROP = "MFG_SERIAL";
      break;
    case PSUInfoType::PART_NUM:
      PSU_PROP = "PART_NUM";
      break;
    case PSUInfoType::SERIAL_NUM:
      PSU_PROP = "SERIAL_NUM";
      break;
    case PSUInfoType::FW_REVISION:
      PSU_PROP = "FW_REVISION";
      break;
    case PSUInfoType::MAX_POUT:
      PSU_PROP = "MAX_POUT";
      break;
    case PSUInfoType::INPUT_LOST:
      PSU_PROP = "INPUT_LOST";
      break;
    case PSUInfoType::STATUS:
      PSU_PROP = "Status";
      type = "double";
      break;
    case PSUInfoType::POWER_UNIT_GROUP:
      PSU_PROP = "POWER_UNIT_GROUP";
      type = "double";
      break;
    case PSUInfoType::LOCATION:
      PSU_PROP = "Name";
      break;
    case PSUInfoType::HOT_REPLACEABLE:
      PSU_PROP = "HOT_REPLACEABLE";
      type = "double";
      break;
    case PSUInfoType::POWER_SUPPLY_TYPE:
      PSU_PROP = "POWER_SUPPLY_TYPE";
      break;
    case PSUInfoType::HEALTH:
      type = "null";
      break;
    case PSUInfoType::INPUT_VOLTAGE_RANGE_SWITCH:
      type = "null";
      break;
    default:
      return ipmi::responseParmOutOfRange();
  }

  //Get dbus property value
  sdbusplus::bus_t bus{ipmid_get_sd_bus_connection()};
  auto settingService = ipmi::getService(bus, PSU_INTERFACE, PSU_PATH.c_str());
  auto method = bus.new_method_call(settingService.c_str(), PSU_PATH.c_str(),
                                    "org.freedesktop.DBus.Properties", "Get");
  method.append(PSU_INTERFACE, PSU_PROP);

  if(type == "string"){
    std::variant<std::string> propertyValue{};
    try
    {
        auto reply = bus.call(method);
        reply.read(propertyValue);
        propertyValueTmp = std::get<std::string>(propertyValue);
        std::cerr << "PSU propertyValueTmp " << propertyValueTmp << "\n";
    }
    catch (const std::exception& e)
    {
        lg2::error("Error get PSU Info ");
        return ipmi::responseUnspecifiedError();
    }
  }
  else if(type == "double")
  {
    std::variant<double> propertyValue{};
    try
    {
        auto reply = bus.call(method);
        reply.read(propertyValue);
        dpropertyValueTmp = std::get<double>(propertyValue);
        std::cerr << "PSU dpropertyValueTmp " << static_cast<double>(dpropertyValueTmp) << "\n";
    }
    catch (const std::exception& e)
    {
        lg2::error("Error get PSU Info ");
        return ipmi::responseUnspecifiedError();
    }
  }

  //Return PSU Info
  if(parmType == PSUInfoType::INPUT_LOST)
  {
    //INPUT_LOST：BIOS  0 = AC Input OK, 1 = AC Input Lost
    psuInfo = (propertyValueTmp == "1") ? "0" : "1";
    uint8_t acStatus = std::stoi(psuInfo, 0, 16);
    response.at(0) = success;
    response.at(1) = sizeof(uint16_t);
    response.at(2) = acStatus;
    return ipmi::responseSuccess(response);
  }
  else if(parmType == PSUInfoType::STATUS)
  {
    uint8_t status = 0;
    if(dpropertyValueTmp != 0 && dpropertyValueTmp != 32768 && dpropertyValueTmp != 32770) //psu status 0x8000 = 32768
    {
      status = 1;
    }
    response.at(0) = success;
    response.at(1) = sizeof(uint16_t);
    response.at(2) = status;
    return ipmi::responseSuccess(response);
  }
  else if(parmType == PSUInfoType::MAX_POUT)
  {
    if(propertyValueTmp == "INVALID")
    {
      response.at(0) = success;
      response.at(1) = 2;
      response.at(2) = 0xFF;        // 低字节 0xe8
      response.at(3) = 0xFF; // 高字节 0x0b
    }
    else
    {
      uint16_t maxPout = std::stoi(propertyValueTmp, 0, 10); // 0x0be8
      response.at(0) = success;
      response.at(1) = sizeof(maxPout);
      response.at(2) = static_cast<uint8_t>(maxPout & 0xFF);        // 低字节 0xe8
      response.at(3) = static_cast<uint8_t>((maxPout >> 8) & 0xFF); // 高字节 0x0b
    }

    return ipmi::responseSuccess(response);
  }
  else if(parmType == PSUInfoType::POWER_UNIT_GROUP || parmType == PSUInfoType::HOT_REPLACEABLE)
  {
    int valueTemp = static_cast<uint8_t>(dpropertyValueTmp);
    int8_t value = static_cast<int8_t>(valueTemp);
    response.at(0) = success;
    response.at(1) = sizeof(uint16_t);  //bios request uint16_t
    response.at(2) = value;
    return ipmi::responseSuccess(response);
  }
  else if(parmType == PSUInfoType::POWER_SUPPLY_TYPE)
  {
    int8_t value = 1;
    if(propertyValueTmp == "switching")
    {
      value = 4;
    }
    response.at(0) = success;
    response.at(1) = sizeof(uint16_t);  //bios request
    response.at(2) = value;
    return ipmi::responseSuccess(response);
  }
  else if(parmType == PSUInfoType::HEALTH)
  {
    uint8_t health = 3; // 3 = OK
    response.at(0) = success;
    response.at(1) = sizeof(uint16_t);
    response.at(2) = health;
    return ipmi::responseSuccess(response);
  }
  else if(parmType == PSUInfoType::INPUT_VOLTAGE_RANGE_SWITCH)
  {
    uint8_t status = 4; // 4 = Auto-switch
    response.at(0) = success;
    response.at(1) = sizeof(uint16_t);
    response.at(2) = status;
    return ipmi::responseSuccess(response);
  }
  else
  {
    psuInfo = std::move(propertyValueTmp);
    if(parmType == PSUInfoType::LOCATION)
    {
      psuInfo = psuInfo.substr(0,4); // PSU0
    }
    else if(parmType == PSUInfoType::FW_REVISION)
    {
      psuInfo = psuInfo.substr(0,8); // 00.01.02
    }
    response.at(0) = success;
    response.at(1) = psuInfo.size();
    std::copy_n(psuInfo.c_str(), psuInfo.size(),
            response.begin() + 2);
    return ipmi::responseSuccess(response);
  }
}

ipmi::RspType<int32_t> Get12VPSUADCValue(uint8_t psuNum)
{
  std::string i2cDev = "/dev/i2c-7";
  int fd = -1;
  uint8_t addr;
  fd = open(i2cDev.c_str(), O_RDWR | O_CLOEXEC);
  if (fd < 0)
  {
      close(fd);
      throw std::runtime_error("Unable to open i2c device.");
      return ipmi::responseUnspecifiedError();
  }

  if(psuNum == 0) addr = PSU0_ADDR;
  else if(psuNum == 1) addr = PSU1_ADDR;
  else {
      close(fd);
      throw std::runtime_error("Invalid PSU Number, range 0 ~ 1.");
      return ipmi::responseParmOutOfRange();
  }

  if (ioctl(fd, I2C_SLAVE_FORCE, addr) < 0)
  {
      close(fd);
      fd = -1;
      throw std::runtime_error("Unable to set i2c slave address.");
      return ipmi::responseUnspecifiedError();
  }

  int32_t val = i2c_smbus_read_word_data(fd, PSU_READ_ADC_CMD);
  if (val < 0)
  {
      close(fd);
      fd = -1;
      throw std::runtime_error("i2c read failed");
      return ipmi::responseUnspecifiedError();
  }

  return ipmi::responseSuccess(val);
}

ipmi::RspType<> Set12VPSUADCValue(uint8_t psuNum, uint16_t val)
{
  std::string i2cDev = "/dev/i2c-7";
  int fd = -1;
  uint8_t addr;
  fd = open(i2cDev.c_str(), O_RDWR | O_CLOEXEC);
  if (fd < 0)
  {
      close(fd);
      throw std::runtime_error("Unable to open i2c device.");
      return ipmi::responseUnspecifiedError();
  }

  if(psuNum == 0) addr = PSU0_ADDR;
  else if(psuNum == 1) addr = PSU1_ADDR;
  else {
      close(fd);
      throw std::runtime_error("PSU Number is out of range: 0 ~ 1.");
      return ipmi::responseParmOutOfRange();
  }

  if(val < PSU_ADC_VAL_MIN || val > PSU_ADC_VAL_MAX)
  {
      close(fd);
      throw std::runtime_error("ADC Value is out of range: 0x1730 ~ 0x199E.");
      return ipmi::responseParmOutOfRange();
  }

  if (ioctl(fd, I2C_SLAVE_FORCE, addr) < 0)
  {
      close(fd);
      fd = -1;
      throw std::runtime_error("Unable to set i2c slave address.");
      return ipmi::responseUnspecifiedError();
  }

  int32_t ret = i2c_smbus_write_word_data(fd, PSU_WRITE_ADC_CMD, val);
  if (ret < 0)
  {
      close(fd);
      fd = -1;
      throw std::runtime_error("i2c write failed");
      return ipmi::responseUnspecifiedError();
  }

  return ipmi::responseSuccess();
}


double combineToDouble(uint8_t tempInteger, uint8_t tempDecimal) {
    int exponent = 0;
    int decimal = tempDecimal;
    while (decimal > 0) {
        decimal /= 10;
        exponent++;
    }
    double doubleValue = tempInteger + static_cast<double>(tempDecimal) / pow(10, exponent);
    return doubleValue;
}

ipmi::RspType<uint8_t> GetDIMMTemperatureValue(std::vector<uint8_t> Data) {
    uint8_t response = 0;
    constexpr auto service = "xyz.openbmc_project.DIMMTempSensor";
    constexpr auto dbusProperties = "org.freedesktop.DBus.Properties";
    constexpr auto statusInterface = "xyz.openbmc_project.Sensor.Value";
    char sensorPath[128];

    //BIOS sends 128 byte of data ,Remove the first eight useless bytes
    if(Data.size() != 128)
    {
        std::cout << "BIOS raw data size :" << Data.size() << "\n";
        return ipmi::responseReqDataLenInvalid();
    }

    //struct array
    Data.erase(Data.begin(), Data.begin() + 8);
    const size_t structCount = Data.size() / sizeof(memory_info_t);
    if (Data.size() != structCount * sizeof(memory_info_t)) {
        std::cout << "Data size :" << Data.size() << "\n";
        return ipmi::responseReqDataLenInvalid();
    }
    memory_info_t memoryArray[structCount];
    std::memcpy(memoryArray, Data.data(), structCount * sizeof(memory_info_t));

    //Map path and temperature
    std::map<std::string, int> DIMMTempMap;
    double DIMMTemp;
    int dimmNum = 0;
    for (int socketId = 0; socketId < 2; socketId++) {
        for (int channelId = 0; channelId < 12; channelId++) {
              int num = 65 + channelId;
              char channel = static_cast<char>(num);
              snprintf(sensorPath, sizeof(sensorPath),
                      "/xyz/openbmc_project/sensors/temperature/DIMM_CPU%d_%c_Temp", socketId,channel);
              DIMMTemp = combineToDouble(memoryArray[dimmNum].temp_integer,
                                         memoryArray[dimmNum].temp_decimal);
              dimmNum++;
              DIMMTempMap[sensorPath] = DIMMTemp;
              printf("Formatted string: %s ;", sensorPath);
              printf("Socket:%d,Channel:%d,temperature%f°C\n",socketId,channelId, DIMMTemp);

        }
    }

    //Set DIMM temperature to dbus
    for (const auto& iter : DIMMTempMap) {
      double DIMMTempValue = static_cast<double>(iter.second);
      std::variant<double> value{(double)DIMMTempValue};
      auto bus = sdbusplus::bus::new_default();
      std::string path = iter.first;
      auto method = bus.new_method_call(service, path.c_str(),dbusProperties, "Set");
      method.append(statusInterface, "Value", value);
      try
      {
          bus.call_noreply(method);
      }
      catch (const sdbusplus::exception::exception& e)
      {
          lg2::info("Error setting DIMM sensor Temp, ERROR = {ERROR}",
                    "ERROR", e);
      }

    }

    return ipmi::responseSuccess(response);
}

bool i2cReadWriteNative(uint8_t bus, uint8_t slaveAddr,
                        std::vector<uint8_t> writeData,
                        std::vector<uint8_t> &readData, uint8_t readCount) {
  if (writeData.empty() && readCount == 0) {
    return false;
  }
  std::string busPath = "/dev/i2c-" + std::to_string(bus);
  int fd = open(busPath.c_str(), O_RDWR);
  if (fd < 0) {
    return false;
  }
  struct i2c_msg messages[2];
  struct i2c_rdwr_ioctl_data ioctl_data;
  if (!writeData.empty()) {
    messages[0].addr = slaveAddr;
    messages[0].flags = 0;
    messages[0].len = writeData.size();
    messages[0].buf = writeData.data();
  }
  if (readCount > 0) {
    messages[1].addr = slaveAddr;
    messages[1].flags = I2C_M_RD;
    messages[1].len = readCount;
    messages[1].buf = new uint8_t[readCount];
  }
  ioctl_data.msgs = messages;
  ioctl_data.nmsgs = (writeData.empty() ? 0 : 1) + (readCount > 0 ? 1 : 0);
  if (ioctl(fd, I2C_RDWR, &ioctl_data) < 0) {
    if (readCount > 0)
      delete[] messages[1].buf;
    close(fd);
    return false;
  }
  if (readCount > 0) {
    readData.assign(static_cast<uint8_t *>(messages[1].buf),
                    static_cast<uint8_t *>(messages[1].buf) + readCount);
    delete[] messages[1].buf;
  }
  close(fd);
  return true;
}

ipmi::RspType<std::vector<uint8_t>>
ipmiOemI2cReadWrite(uint8_t bus, uint8_t address, uint8_t readCount,
                    std::vector<uint8_t> writeData) {
  std::vector<uint8_t> response(readCount);
  if (i2cReadWriteNative(bus, address >> 1, writeData, response, readCount))
    return ipmi::responseSuccess(std::move(response));
  return ipmi::responseDestinationUnavailable();
}

ipmi::RspType<uint8_t> ipmiOemSetDIMMSetPoint(uint8_t dimmSetPoint)
{
    uint8_t RspStatus = 0x00;
    if(dimmSetPoint > 120)
    {
      return ipmi::responseParmOutOfRange();
    }

    if(dimmSetPoint <= 40)
    {
        dimmSetPoint = 40;
    }
    else
    {
        dimmSetPoint = 74;
    }

    std::ofstream outFile(DIMM_SETPOINT_PATH, std::ios::out | std::ios::trunc);
    if (!outFile)
    {
        return ipmi::responseBusy();
    }

    outFile << static_cast<int>(dimmSetPoint);
    outFile.close();

    return ipmi::responseSuccess(RspStatus);
}

ipmi::RspType<uint8_t> ipmiOemGetDIMMSetPoint()
{
    std::ifstream inFile(DIMM_SETPOINT_PATH);
    if (!inFile)
    {
        return ipmi::responseUnspecifiedError();
    }

    std::string value;
    inFile >> value;

    if (value.empty() || !std::all_of(value.begin(), value.end(), ::isdigit))
    {
        return ipmi::responseUnspecifiedError();
    }

    uint8_t dimmSetPoint = static_cast<uint8_t>(std::stoi(value));
    inFile.close();

    return ipmi::responseSuccess(dimmSetPoint);
}

ipmi::RspType<uint8_t> ipmiOemSetCoverStatus(uint8_t coverStatus)
{
    uint8_t readCount = 1;
    std::vector<uint8_t> response(readCount);
    std::vector<uint8_t> writeData(3);
    uint8_t RspStatus = 0x00;

    if((coverStatus != COVER_COMMAND_OPENED_STATUS) && (coverStatus != COVER_COMMAND_CLOSE_STATUS))
    {
        return ipmi::responseInvalidFieldRequest();
    }

    writeData[0] = (COVER_OPEN_CONTROL_REG & 0x00ff);
    writeData[1] = (COVER_OPEN_CONTROL_REG & 0xff00) >>8;
    if(COVER_COMMAND_OPENED_STATUS == coverStatus)
    {
        writeData[2] = 0x02;
    }
    else
    {
        writeData[2] = 0x01;
    }
    if(i2cReadWriteNative(COVER_CPLD_I2C_BUS, COVER_CPLD_I2C_SLAVE_ADDR >> 1, writeData, response, readCount))
    {
        writeData[2] = 0x00;
        /*After open or close cover, need write reg to 0x00 */
        i2cReadWriteNative(COVER_CPLD_I2C_BUS, COVER_CPLD_I2C_SLAVE_ADDR >> 1, writeData, response, readCount);
        return ipmi::responseSuccess(RspStatus);
    }
    else
        return ipmi::responseBusy();
}

ipmi::RspType<uint8_t> ipmiOemGetCoverStatus()
{
    uint8_t readCount = 1;
    std::vector<uint8_t> response(readCount);
    std::vector<uint8_t> writeData(2);
    uint8_t coverStatus;

    writeData[0] = (COVER_OPEN_STATUS_REG & 0x00ff);
    writeData[1] = (COVER_OPEN_STATUS_REG & 0xff00) >>8;
    if(i2cReadWriteNative(COVER_CPLD_I2C_BUS, COVER_CPLD_I2C_SLAVE_ADDR >> 1, writeData, response, readCount))
    {
        coverStatus = response[0] & 0x07;
        if(COVER_REG_OPENED == coverStatus)
        {
            return ipmi::responseSuccess(COVER_COMMAND_OPENED_STATUS);
        }
        else if(COVER_REG_CLOSED == coverStatus)
        {
            return ipmi::responseSuccess(COVER_COMMAND_CLOSE_STATUS);
        }
        else
        {
            return ipmi::responseSuccess(COVER_COMMAND_UNCERTAIN_STATUS);
        }
    }
    else
        return ipmi::responseBusy();
}

ipmi::RspType<uint8_t> ipmiOemSetCoverHeatTemp(uint8_t coverTemp)
{
    uint8_t RspStatus = 0x00;
    if(coverTemp > 80)
    {
      return ipmi::responseParmOutOfRange();
    }

    std::ofstream outFile(COVER_SETPOINT_PATH, std::ios::out | std::ios::trunc);
    if (!outFile)
    {
        return ipmi::responseBusy();
    }

    outFile << static_cast<int>(coverTemp);
    outFile.close();

    return ipmi::responseSuccess(RspStatus);
}

ipmi::RspType<uint8_t> ipmiOemGetCoverHeatTemp()
{
    std::ifstream inFile(COVER_SETPOINT_PATH);
    if (!inFile)
    {
        return ipmi::responseBusy();
    }

    std::string value;
    inFile >> value;

    if (value.empty() || !std::all_of(value.begin(), value.end(), ::isdigit))
    {
        return ipmi::responseUnspecifiedError();
    }

    uint8_t coverTemp = static_cast<uint8_t>(std::stoi(value));
    inFile.close();

    return ipmi::responseSuccess(coverTemp);
}

ipmi::RspType<uint8_t> ipmiOemGetCurrentCoverTemp()
{
    std::vector<uint8_t> writeData(1);
    std::vector<uint8_t> readData(2);
    uint8_t readtemp;
    uint8_t maxtemp;
    uint8_t readCount = 2;
    uint8_t sensorindex = 0;

    for(sensorindex = 0; sensorindex < 4; sensorindex++)
    {
        writeData[0] = sensorindex + 1;  //TMP468 sensor channel is from 1 to 5
        if(i2cReadWriteNative(COVER_CPLD_I2C_BUS, COVER_TMP468_I2C_SLAVE_ADDR >> 1, writeData, readData, readCount))
        {
            if((readData[0] & 0x80) == 0)
            {
                readtemp = (uint8_t)((((uint16_t)((readData[0] << 8) +  readData[1])) >> 3) * 0.0625);
                if(maxtemp < readtemp)
                {
                    maxtemp = readtemp;
                }
            }
        }
        else
            return ipmi::responseBusy();
    }

    return ipmi::responseSuccess(maxtemp);
}

ipmi::RspType<uint8_t> ipmiOemSetCoverFSCEnableStatus(uint8_t fscStatus)
{
    uint8_t RspStatus = 0x00;
    if(fscStatus > 1)
    {
      return ipmi::responseParmOutOfRange();
    }

    std::ofstream outFile(FSC_ENABLE_STATUS_PATH, std::ios::out | std::ios::trunc);
    if (!outFile)
    {
        return ipmi::responseBusy();
    }

    outFile << static_cast<int>(fscStatus);
    outFile.close();

    return ipmi::responseSuccess(RspStatus);
}

ipmi::RspType<uint8_t> ipmiOemGetCoverFSCEnableStatus()
{
    uint8_t fscStatus;
    if(!std::filesystem::exists(FSC_ENABLE_STATUS_PATH))
    {
        fscStatus = 0;
        return ipmi::responseSuccess(fscStatus);
    }

    std::ifstream inFile(FSC_ENABLE_STATUS_PATH);
    if (!inFile)
    {
        return ipmi::responseBusy();
    }

    std::string value;
    inFile >> value;

    if (value.empty() || !std::all_of(value.begin(), value.end(), ::isdigit))
    {
        return ipmi::responseUnspecifiedError();
    }

    fscStatus = static_cast<uint8_t>(std::stoi(value));
    inFile.close();

    return ipmi::responseSuccess(fscStatus);
}

ipmi::RspType<std::vector<uint8_t>>
    ipmiSetGetBiosVersion(const ipmi::Context::ptr& ctx, uint8_t operation,
                          std::vector<uint8_t> dataToWrite)
{
    std::vector<uint8_t> biosVerData;
    boost::system::error_code ec;

    if ((operation != 0) && (operation != 1))
    {
        return ipmi::responseInvalidFieldRequest();
    }

    if (operation == 1) // set bios version
    {
        auto len = dataToWrite.size();
        if ((BIOSVERSIONLEN < len) || (0 == len))
        {
            return ipmi::responseReqDataLenExceeded();
        }

        std::string writeString;
        writeString.assign(dataToWrite.begin(), dataToWrite.end());

        ec = ipmi::setDbusProperty(
            ctx, softwareServiceName, biosActiveObjectName,
            softwareVersionInterface, versionProperty, writeString);

        if (ec)
        {
            lg2::error("Failed in setting bios version property: {EC}", "EC",
                       ec.message());
            return ipmi::responseResponseError();
        }

        return ipmi::responseSuccess();
    }
    // get bios version
    std::string biosVer;
    ec = ipmi::getDbusProperty(ctx, softwareServiceName, biosActiveObjectName,
                               softwareVersionInterface, versionProperty,
                               biosVer);

    if (ec)
    {
        lg2::error("Failed in getting bios version property: {EC}", "EC",
                   ec.message());
        return ipmi::responseResponseError();
    }
    biosVerData.assign(biosVer.begin(), biosVer.end());

    return ipmi::responseSuccess(biosVerData);
}

bool factoryReset(const ipmi::Context::ptr& ctx)
{
    constexpr auto softwareServiceName =
        "xyz.openbmc_project.Software.BMC.Updater";
    constexpr auto softwareRoot = "/xyz/openbmc_project/software";
    constexpr auto factoryResetInterface =
        "xyz.openbmc_project.Common.FactoryReset";
    constexpr auto method = "Reset";

    boost::system::error_code ec;

    ctx->bus->yield_method_call(ctx->yield, ec, softwareServiceName,
                                softwareRoot, factoryResetInterface, method);

    if (ec)
    {
        lg2::error("Error resetting BMC: {ERROR}", "ERROR", ec.message());
        return false;
    }

    return true;
}

static bool resetBmc()
{
    /* systemd busname*/
    auto systemdBusname = "org.freedesktop.systemd1";
    /* systemd path*/
    auto systemdPath = "/org/freedesktop/systemd1";
    /* systemd interface*/
    auto systemdInterface = "org.freedesktop.systemd1.Manager";
    auto bus = getSdBus();
    auto method = bus->new_method_call(systemdBusname, systemdPath,
                                       systemdInterface, "StartUnit");
    method.append("obmc-reboot-delay.service", "replace");

    try
    {
        bus->call_noreply(method);
    }
    catch (const std::exception& e)
    {
        lg2::error("Error reboot failed {ERROR}", "ERROR", e);
        return false;
    }

    return true;
}

ipmi::RspType<> ipmiOemRestoreFactoryDefault(const ipmi::Context::ptr& ctx)
{
    if (!factoryReset(ctx))
    {
        lg2::error("Restore factory default error.");
        return ipmi::responseUnspecifiedError();
    }

    if (!resetBmc())
    {
        lg2::error("Restore factory default reset bmc fail.");
        return ipmi::responseUnspecifiedError();
    }

    lg2::info("Restore factory default success.");
    return ipmi::responseSuccess();
}

static void setDimmLEDState(const std::string& sensorPath,const std::string& ledState)
{
    auto bus = sdbusplus::bus::new_default();
    auto method = bus.new_method_call(DIMMTempSensorBusname, sensorPath.c_str(),
                                      dbusProperties, "Set");

    std::variant<std::string> value{ledState};
    method.append(dimmLEDInterface, "LEDState", value);
    try
    {
        bus.call_noreply(method);
    }
    catch (const sdbusplus::exception::exception& e)
    {
        lg2::info("Error setting Dimm LED State, ERROR = {ERROR}",
                  "ERROR", e);
    }

    return;
}

ipmi::RspType<uint8_t> ipmiBiosSetDimmState(std::vector<uint8_t> data) {

    //BIOS sends 128 byte of data ,Remove the first eight useless bytes
    if(data.size() != 2)
    {
        std::cerr << "invalid data size :" << (int)data.size() << "\n";
        return ipmi::responseReqDataLenInvalid();
    }

    if(dimm_map.find(data[0]) == dimm_map.end())
    {
        std::cerr << "invalid sensor number :" << (int)data[0] << "\n";
        return ipmi::responseReqDataLenInvalid();
    }

    if(data[1] > 2)
    {
        std::cerr << "invalid dimm sensor " << (int)data[0] << " with state " << (int)data[1] << ".\n";
        return ipmi::responseReqDataLenInvalid();
    }

    std::string ledStatePath = "/xyz/openbmc_project/sensors/temperature/" + dimm_map.at(data[0]);
    std::string dimmState = dimm_state_map.at(data[1]);

    setDimmLEDState(ledStatePath, dimmState);

    return ipmi::responseSuccess();
}

ipmi::RspType<uint8_t>
    ipmiOemSetSOLBitrate(const ipmi::Context::ptr& ctx, uint8_t bitrate)
{
    uint64_t baudRate = 115200;
    boost::system::error_code ec;

    if ((bitrate != 0) && (bitrate != 1))
    {
        return ipmi::responseInvalidFieldRequest();
    }

    if (bitrate == 0)
    {
        baudRate = 115200;
    }
    else if(bitrate == 1)
    {
        baudRate = 921600;
    }

    ec = ipmi::setDbusProperty(ctx,
        "xyz.openbmc_project.Console.default",
        "/xyz/openbmc_project/console/default",
        "xyz.openbmc_project.Console.UART", "NonVbitrate", baudRate);

    if (ec)
    {
        lg2::error("Failed in setting oem sol bitrate property: {EC}", "EC",
                    ec.message());
        return ipmi::responseResponseError();
    }

    return ipmi::responseSuccess();
}

using BasicVariantType =
    std::variant<std::vector<std::string>, std::vector<uint64_t>, std::string,
                 int64_t, uint64_t, double, int32_t, uint32_t, int16_t,
                 uint16_t, uint8_t, bool>;
using PropertyMapType =
    boost::container::flat_map<std::string, BasicVariantType>;
int setDimmParaToDbus(ipmi::Context::ptr ctx, const std::string& dimmName, const std::string& type, const std::string& propertyName, double wrValue)
{
    boost::system::error_code ec;
    std::string path = "/xyz/openbmc_project/dimm/" + dimmName;
    std::string interface = "xyz.openbmc_project.Dimm." + type;

    ctx->bus->yield_method_call<void>(
        ctx->yield, ec, dimmSpdReaderService, path, dbusProperties, "Set",
        interface, propertyName, std::variant<double>(wrValue));
    if (ec)
    {
        std::cerr << "Setting DIMM " << dimmName << " " << type << " " << propertyName << " failed. \n";
        return -1;
    }
    return 0;
}

inline double dimmTempTodouble(int8_t intPart, uint8_t fracPart) {
    double temp;
    if (intPart & 0x80) {
      temp = -1.0 * (std::fabs(intPart) + fracPart / 100.0);
    } else {
      temp = intPart + fracPart / 100.0;
    }
    return temp;
}

ipmi::RspType<uint8_t> ipmiBiosSetDimmPara(ipmi::Context::ptr ctx, std::vector<uint8_t> data) {

    size_t size = sizeof(oem_dimm_info_t);
    if(data.size() != size)
    {
        std::cerr << "invalid data size :" << (int)data.size() << "\n";
        return ipmi::responseReqDataLenInvalid();
    }

    std::vector<std::string> tempName = {"TS0", "TS1", "SPD_TS"};
    std::vector<std::string> railName = {"SWA", "SWB", "SWC", "SWD", "SWE", "SWF"};
    oem_dimm_info_t dimmInfo;
    std::memcpy(&dimmInfo, data.data(), size);
    int ret = -1;

    //dimmId format: ((Socket << 6) | (Channel << 2) | Dimm)
    int channel = (dimmInfo.dimmId >> 2) & 0x0f;
    int cpuid = (dimmInfo.dimmId >> 6) & 0x03;

    if(cpuid > 1 || channel > 11)
    {
        std::cerr << "invalid dimmid :" << (int)dimmInfo.dimmId << "\n";
        return ipmi::responseParmOutOfRange();
    }

    std::string dimmName = "DIMM_CPU" + std::string(1, '0' + cpuid) + "_" + std::string(1, 'A' + channel);

    //Set temperature
    double temp[3];
    temp[0] = dimmTempTodouble(dimmInfo.dimmTemp.TS0_int, dimmInfo.dimmTemp.TS0_frac);
    temp[1] = dimmTempTodouble(dimmInfo.dimmTemp.TS1_int, dimmInfo.dimmTemp.TS1_frac);
    temp[2] = dimmTempTodouble(dimmInfo.dimmTemp.SPD_int, dimmInfo.dimmTemp.SPD_frac);
    for(int i=0; i<3; i++) {
      ret = setDimmParaToDbus(ctx, dimmName, "Temperature", tempName[i], temp[i]);
      if(ret < 0)
        return ipmi::responseUnspecifiedError();
    }

    //Set voltage
    double railData[6];
    railData[0] = static_cast<double>(dimmInfo.dimmVolt.SWA) / 1000.0;
    railData[1] = static_cast<double>(dimmInfo.dimmVolt.SWB) / 1000.0;
    railData[2] = static_cast<double>(dimmInfo.dimmVolt.SWC) / 1000.0;
    railData[3] = static_cast<double>(dimmInfo.dimmVolt.SWD) / 1000.0;
    railData[4] = static_cast<double>(dimmInfo.dimmVolt.SWE) / 1000.0;
    railData[5] = static_cast<double>(dimmInfo.dimmVolt.SWF) / 1000.0;
    for(int i=0; i<6; i++) {
      ret = setDimmParaToDbus(ctx, dimmName, "Voltage", railName[i], railData[i]);
      if(ret < 0)
        return ipmi::responseUnspecifiedError();
    }

    //Set Current
    railData[0] = static_cast<double>(dimmInfo.dimmCur.SWA) / 1000.0;
    railData[1] = static_cast<double>(dimmInfo.dimmCur.SWB) / 1000.0;
    railData[2] = static_cast<double>(dimmInfo.dimmCur.SWC) / 1000.0;
    railData[3] = static_cast<double>(dimmInfo.dimmCur.SWD) / 1000.0;
    railData[4] = static_cast<double>(dimmInfo.dimmCur.SWE) / 1000.0;
    railData[5] = static_cast<double>(dimmInfo.dimmCur.SWF) / 1000.0;
    for(int i=0; i<6; i++) { //Jim debug check i<6 or i<4
      ret = setDimmParaToDbus(ctx, dimmName, "Current", railName[i], railData[i]);
      if(ret < 0)
        return ipmi::responseUnspecifiedError();
    }

    //Set power
    railData[0] = static_cast<double>(dimmInfo.dimmPow.SWA) / 1000.0;
    railData[1] = static_cast<double>(dimmInfo.dimmPow.SWB) / 1000.0;
    railData[2] = static_cast<double>(dimmInfo.dimmPow.SWC) / 1000.0;
    railData[3] = static_cast<double>(dimmInfo.dimmPow.SWD) / 1000.0;
    railData[4] = static_cast<double>(dimmInfo.dimmPow.SWE) / 1000.0;
    railData[5] = static_cast<double>(dimmInfo.dimmPow.SWF) / 1000.0;
    for(int i=0; i<6; i++) { //Jim debug check i<6 or i<4
      ret = setDimmParaToDbus(ctx, dimmName, "Power", railName[i], railData[i]);
      if(ret < 0)
        return ipmi::responseUnspecifiedError();
    }

    return ipmi::responseSuccess();
}

inline int8_t getTempIntPart (double temp) {
    int16_t int_part = static_cast<int16_t>(temp);
    if(int_part > INT8_MAX) int_part = INT8_MAX;
    if(int_part < INT8_MIN) int_part = INT8_MIN;
    return static_cast<uint8_t>(int_part);
}

inline uint8_t getTempFracPart (double temp) {
    uint8_t frac_part = 0;
    int16_t int_part = static_cast<int16_t>(temp);
    if(int_part & 0x80) {
      frac_part = static_cast<uint8_t>((int_part - temp) * 100);
    } else {
      frac_part = static_cast<uint8_t>((temp - int_part) * 100);
    }
    return frac_part;
}

inline uint16_t doubleToU16 (double data) {
    uint32_t temp = static_cast<uint32_t>(std::floor(data * 1000.0));
    if(temp > UINT16_MAX) temp = UINT16_MAX;

    return static_cast<uint16_t>(temp);
}

ipmi::RspType<std::vector<uint8_t>> ipmiBiosGetDimmPara(ipmi::Context::ptr ctx, uint8_t dimmId) {

    oem_dimm_info_t dimmInfo;
    memset((void *)&dimmInfo, 0xFF, sizeof(oem_dimm_info_t));
    boost::system::error_code ec;
    size_t size = sizeof(oem_dimm_info_t);
    std::vector<uint8_t> byte_vector(size);

    int channel = (dimmId >> 2) & 0x0f;
    int cpuid = (dimmId >> 6) & 0x03;
    if(cpuid > 1 || channel > 11)
    {
        std::cerr << "invalid dimmId :" << (int)dimmId << "\n";
        return ipmi::responseParmOutOfRange();
    }

    dimmInfo.dimmId = dimmId;
    std::string dimmName = "DIMM_CPU" + std::string(1, '0' + cpuid) + "_" + std::string(1, 'A' + channel);
    std::string path = "/xyz/openbmc_project/dimm/" + dimmName;

    //Get temperature
    PropertyMapType propTemp =
            ctx->bus->yield_method_call<PropertyMapType>(
                  ctx->yield, ec, dimmSpdReaderService, path,
                  dbusProperties, "GetAll", tempInterface);
    if (ec)
    {
        std::cerr << "Getting DIMM " << dimmName << " Temperature failed." << "\n";
        return ipmi::responseUnspecifiedError();
    }
    auto ts0_temp = std::get_if<double>(&propTemp["TS0"]);
    auto ts1_temp = std::get_if<double>(&propTemp["TS1"]);
    auto spd_temp = std::get_if<double>(&propTemp["SPD_TS"]);

    if(!std::isnan(*ts0_temp)) {
      dimmInfo.dimmTemp.TS0_int = getTempIntPart(*ts0_temp);
      dimmInfo.dimmTemp.TS0_frac = getTempFracPart(*ts0_temp);
    }
    if(!std::isnan(*ts1_temp)) {
      dimmInfo.dimmTemp.TS1_int = getTempIntPart(*ts1_temp);
      dimmInfo.dimmTemp.TS1_frac = getTempFracPart(*ts1_temp);
    }
    if(!std::isnan(*spd_temp)) {
      dimmInfo.dimmTemp.SPD_int = getTempIntPart(*spd_temp);
      dimmInfo.dimmTemp.SPD_frac = getTempFracPart(*spd_temp);
    }

    //Get voltage
    ec.clear();
    PropertyMapType propVolt =
            ctx->bus->yield_method_call<PropertyMapType>(
                  ctx->yield, ec, dimmSpdReaderService, path,
                  dbusProperties, "GetAll", voltInterface);
    if (ec)
    {
        std::cerr << "Getting DIMM " << dimmName << " Voltage failed." << "\n";
        return ipmi::responseUnspecifiedError();
    }
    auto swa = std::get_if<double>(&propVolt["SWA"]);
    auto swb = std::get_if<double>(&propVolt["SWB"]);
    auto swc = std::get_if<double>(&propVolt["SWC"]);
    auto swd = std::get_if<double>(&propVolt["SWD"]);
    auto swe = std::get_if<double>(&propVolt["SWE"]);
    auto swf = std::get_if<double>(&propVolt["SWF"]);
    if(!std::isnan(*swa))
      dimmInfo.dimmVolt.SWA = doubleToU16(*swa);
    if(!std::isnan(*swb))
      dimmInfo.dimmVolt.SWB = doubleToU16(*swb);
    if(!std::isnan(*swc))
      dimmInfo.dimmVolt.SWC = doubleToU16(*swc);
    if(!std::isnan(*swd))
      dimmInfo.dimmVolt.SWD = doubleToU16(*swd);
    if(!std::isnan(*swe))
      dimmInfo.dimmVolt.SWE = doubleToU16(*swe);
    if(!std::isnan(*swf))
      dimmInfo.dimmVolt.SWF = doubleToU16(*swf);

    //Get Current
    ec.clear();
    PropertyMapType propCur =
            ctx->bus->yield_method_call<PropertyMapType>(
                  ctx->yield, ec, dimmSpdReaderService, path,
                  dbusProperties, "GetAll", curInterface);
    if (ec)
    {
        std::cerr << "Getting DIMM " << dimmName << " Current failed." << "\n";
        return ipmi::responseUnspecifiedError();
    }
    swa = std::get_if<double>(&propCur["SWA"]);
    swb = std::get_if<double>(&propCur["SWB"]);
    swc = std::get_if<double>(&propCur["SWC"]);
    swd = std::get_if<double>(&propCur["SWD"]);
    swe = std::get_if<double>(&propCur["SWE"]);
    swf = std::get_if<double>(&propCur["SWF"]);
    if(!std::isnan(*swa))
      dimmInfo.dimmCur.SWA = doubleToU16(*swa);
    if(!std::isnan(*swb))
      dimmInfo.dimmCur.SWB = doubleToU16(*swb);
    if(!std::isnan(*swc))
      dimmInfo.dimmCur.SWC = doubleToU16(*swc);
    if(!std::isnan(*swd))
      dimmInfo.dimmCur.SWD = doubleToU16(*swd);
    if(!std::isnan(*swe))
      dimmInfo.dimmCur.SWE = doubleToU16(*swe);
    if(!std::isnan(*swf))
      dimmInfo.dimmCur.SWF = doubleToU16(*swf);

    //Set power
    ec.clear();
    PropertyMapType propPow =
            ctx->bus->yield_method_call<PropertyMapType>(
                  ctx->yield, ec, dimmSpdReaderService, path,
                  dbusProperties, "GetAll", powInterface);
    if (ec)
    {
        std::cerr << "Getting DIMM " << dimmName << " Power failed." << "\n";
        return ipmi::responseUnspecifiedError();
    }
    swa = std::get_if<double>(&propPow["SWA"]);
    swb = std::get_if<double>(&propPow["SWB"]);
    swc = std::get_if<double>(&propPow["SWC"]);
    swd = std::get_if<double>(&propPow["SWD"]);
    swe = std::get_if<double>(&propPow["SWE"]);
    swf = std::get_if<double>(&propPow["SWF"]);
    if(!std::isnan(*swa))
      dimmInfo.dimmPow.SWA = doubleToU16(*swa);
    if(!std::isnan(*swb))
      dimmInfo.dimmPow.SWB = doubleToU16(*swb);
    if(!std::isnan(*swc))
      dimmInfo.dimmPow.SWC = doubleToU16(*swc);
    if(!std::isnan(*swd))
      dimmInfo.dimmPow.SWD = doubleToU16(*swd);
    if(!std::isnan(*swe))
      dimmInfo.dimmPow.SWE = doubleToU16(*swe);
    if(!std::isnan(*swf))
      dimmInfo.dimmPow.SWF = doubleToU16(*swf);

    std::memcpy(byte_vector.data(), &dimmInfo, size);

    return ipmi::responseSuccess(byte_vector);
}

bool getPlatformState(ipmi::Context::ptr ctx)
{
    bool* espiPltRst = nullptr;
    bool* i3cSwitch = nullptr;
    boost::system::error_code ec;
    //Set power
    ec.clear();
    PropertyMapType propPow =
            ctx->bus->yield_method_call<PropertyMapType>(
                  ctx->yield, ec, HostMiscDbusName, platformStatePath,
                  dbusProperties, "GetAll", platformStateInterface);
    if (ec)
    {
        std::cerr << "Failed to read platform State" << "\n";
        return false;
    }

    espiPltRst = std::get_if<bool>(&propPow["ESpiPlatformReset"]);
    i3cSwitch = std::get_if<bool>(&propPow["dimmI3cSwitch"]);

    if(espiPltRst == nullptr || i3cSwitch == nullptr)
      return false;
    else {
      return *espiPltRst && *i3cSwitch;
    }
}

static std::vector<uint8_t> convertDoubleToTwoBytes(std::optional<double> value)
{
    if(!value.has_value()) {
        return std::vector<uint8_t>{0xFF, 0xFF};
    }

    double val = value.value();

    if (val < 0.0 || val > 255.994) {
        return std::vector<uint8_t>{0xFF, 0xFF};
    }

    long long scaled = std::llround(val * 100.0);

    if (scaled < 0 || scaled > 25599) {
        return std::vector<uint8_t>{0xFF, 0xFF};
    }

    uint8_t integerPart = static_cast<uint8_t>(scaled / 100);
    uint8_t fractionalRounded = static_cast<uint8_t>(scaled % 100);

    return std::vector<uint8_t>{integerPart, fractionalRounded};
}

template<typename T>
static std::optional<T> getDIMMData(const nlohmann::json& js, const std::string& dimmName,
                             DataType dataType, auto indexId)
{

    static_assert(std::is_arithmetic_v<T>, "The return type must be an arithmetic type");

    if (!js.contains(dimmName)) {
        return std::nullopt;
    }

    const nlohmann::json& dimmData = js[dimmName];

    try {
        if (dataType == DataType::TEMPERATURE) {
            auto sensor = static_cast<TempSensor>(indexId);

            switch (sensor) {
                case TempSensor::TS0: {
                    auto value = dimmData["Temperature_DegreesC"]["TS0"];
                    if (std::isnan(value.get<double>())) return std::nullopt;
                    return static_cast<T>(value.get<double>());
                }
                case TempSensor::TS1: {
                    auto value = dimmData["Temperature_DegreesC"]["TS1"];
                    if (std::isnan(value.get<double>())) return std::nullopt;
                    return static_cast<T>(value.get<double>());
                }
                case TempSensor::SPD_TS: {
                    auto value = dimmData["Temperature_DegreesC"]["SPD_TS"];
                    if (std::isnan(value.get<double>())) return std::nullopt;
                    return static_cast<T>(value.get<double>());
                }
                case TempSensor::MAX_TS01: {
                    auto ts0 = dimmData["Temperature_DegreesC"]["TS0"];
                    auto ts1 = dimmData["Temperature_DegreesC"]["TS1"];

                    bool ts0_valid = !std::isnan(ts0.get<double>());
                    bool ts1_valid = !std::isnan(ts1.get<double>());

                    if (ts0_valid && ts1_valid) {
                        return static_cast<T>(std::max(
                            ts0.get<double>(),
                            ts1.get<double>()
                        ));
                    } else if (ts0_valid) {
                        return static_cast<T>(ts0.get<double>());
                    } else if (ts1_valid) {
                        return static_cast<T>(ts1.get<double>());
                    } else {
                        return std::nullopt;
                    }
                }
                default:
                    return std::nullopt;
            }
        }
        else if (dataType == DataType::CURRENT) {
            auto channel = static_cast<RailChannel>(indexId);
            std::string channelStr;

            switch (channel) {
                case RailChannel::SWA: channelStr = "SWA"; break;
                case RailChannel::SWB: channelStr = "SWB"; break;
                case RailChannel::SWC: channelStr = "SWC"; break;
                case RailChannel::SWD: channelStr = "SWD"; break;
                case RailChannel::SWE: channelStr = "SWE"; break;
                case RailChannel::SWF: channelStr = "SWF"; break;
                default: return std::nullopt;
            }

            auto value = dimmData["Current_Ampere"][channelStr];
            if (std::isnan(value.get<double>())) return std::nullopt;
            return static_cast<T>(value.get<double>());
        }
        else if (dataType == DataType::POWER) {
            auto channel = static_cast<RailChannel>(indexId);
            std::string channelStr;

            switch (channel) {
                case RailChannel::SWA: channelStr = "SWA"; break;
                case RailChannel::SWB: channelStr = "SWB"; break;
                case RailChannel::SWC: channelStr = "SWC"; break;
                case RailChannel::SWD: channelStr = "SWD"; break;
                case RailChannel::SWE: channelStr = "SWE"; break;
                case RailChannel::SWF: channelStr = "SWF"; break;
                default: return std::nullopt;
            }

            auto value = dimmData["Power_Watt"][channelStr];
            if (std::isnan(value.get<double>())) return std::nullopt;
            return static_cast<T>(value.get<double>());
        }
        else if (dataType == DataType::VOLTAGE) {
            auto channel = static_cast<RailChannel>(indexId);
            std::string channelStr;

            switch (channel) {
                case RailChannel::SWA: channelStr = "SWA"; break;
                case RailChannel::SWB: channelStr = "SWB"; break;
                case RailChannel::SWC: channelStr = "SWC"; break;
                case RailChannel::SWD: channelStr = "SWD"; break;
                case RailChannel::SWE: channelStr = "SWE"; break;
                case RailChannel::SWF: channelStr = "SWF"; break;
                default: return std::nullopt;
            }

            auto value = dimmData["Voltage_Volt"][channelStr];
            if (std::isnan(value.get<double>())) return std::nullopt;
            return static_cast<T>(value.get<double>());
        }
        else if (dataType == DataType::STATUS) {
            auto reg = static_cast<StatusReg>(indexId);
            std::string regStr;

            char hexStr[5];
            snprintf(hexStr, sizeof(hexStr), "0x%02x", static_cast<int>(reg));
            regStr = hexStr;

            if (!dimmData["Status_Reg"].contains(regStr)) {
                return std::nullopt;
            }

            auto value = dimmData["Status_Reg"][regStr];
            return static_cast<T>(value.get<int>());
        }
        else if (dataType == DataType::ERROR) {
            auto err = static_cast<ErrCode>(indexId);
            std::string errStr;

            char hexStr[5];
            snprintf(hexStr, sizeof(hexStr), "0x%02x", static_cast<int>(err));
            errStr = hexStr;

            if (!dimmData["Error_Code"].contains(errStr)) {
                return std::nullopt;
            }

            auto value = dimmData["Error_Code"][errStr];
            return static_cast<T>(value.get<int>());
        }	
    } catch (std::exception& e) {
        return std::nullopt;
    }

    return std::nullopt;
}

/****************************************************************************
 reqId:  0:TS0 1:TS1 2:SPD_TEMP 3: Max Temp 4:Voltage 5:Current 6: Status Reg 7: Error Code
*****************************************************************************/
ipmi::RspType<std::vector<uint8_t>> ipmiGetDimmTemp(uint8_t reqId)
{
    bool retry;
    uint8_t attempts = 0;
    const nlohmann::json empty{};
    nlohmann::json js;

    if(reqId > 7) {
        std::cout << "invalid reqId :" << (int)reqId << std::endl;
        return ipmi::responseParmOutOfRange();
    }

    std::vector<uint8_t> byte_vector;

    do
    {
      retry = false;
      const std::filesystem::path path(dimmInfoPath);

      std::ifstream jsonInputFile(path);
      if (jsonInputFile.is_open()) {
          try
          {
              js = nlohmann::json::parse(jsonInputFile);
          }
          catch (const std::exception& e)
          {
              ++attempts;
              retry = true;
              jsonInputFile.close();
              std::this_thread::sleep_for(std::chrono::milliseconds(100));
          }
      }
      else {
        ++attempts;
        retry = true;
        std::this_thread::sleep_for(std::chrono::milliseconds(100));
      }
    } while ((attempts < maxRetries) && retry);

    if(attempts == maxRetries) {
      std::cout << "Failed to parse json file " << std::endl;
      return ipmi::responseUnspecifiedError();
    }

    for(int cpuid = 0; cpuid < MAX_CPU_ID_NUM; cpuid++)
    {
      for(int i = 0; i < MAX_DIMM_RANK_NUM; i++)
      {
        dimm_temp_sensor_t tempSensor;
        memset(&tempSensor, 0xFF, sizeof(dimm_temp_sensor_t));

        std::string dimmName = "DIMM_CPU" + std::string(1, '0' + cpuid)
              + "_" + std::string(1, 'A' + i);
        std::optional<double> temp, swa, swb, swc, swd, swe, swf;
        std::optional<uint8_t> r4, r5, r6, r7, r8, r9, ra, rb;
        std::optional<uint8_t> err_r32;
		
        switch(static_cast<ReqId>(reqId))
        {
          case ReqId::TS0:
              temp = getDIMMData<double>(js, dimmName, DataType::TEMPERATURE, TempSensor::TS0);
              break;
          case ReqId::TS1:
              temp = getDIMMData<double>(js, dimmName, DataType::TEMPERATURE, TempSensor::TS1);
              break;
          case ReqId::SPD_TEMP:
              temp = getDIMMData<double>(js, dimmName, DataType::TEMPERATURE, TempSensor::SPD_TS);
              break;
          case ReqId::MAX_TEMP:
              temp = getDIMMData<double>(js, dimmName, DataType::TEMPERATURE, TempSensor::MAX_TS01);
              break;
          case ReqId::VOLTAGE:
              swa = getDIMMData<double>(js, dimmName, DataType::VOLTAGE, RailChannel::SWA);
              swb = getDIMMData<double>(js, dimmName, DataType::VOLTAGE, RailChannel::SWB);
              swc = getDIMMData<double>(js, dimmName, DataType::VOLTAGE, RailChannel::SWC);
              swd = getDIMMData<double>(js, dimmName, DataType::VOLTAGE, RailChannel::SWD);
              swe = getDIMMData<double>(js, dimmName, DataType::VOLTAGE, RailChannel::SWE);
              swf = getDIMMData<double>(js, dimmName, DataType::VOLTAGE, RailChannel::SWF);
              break;
          case ReqId::CURRENT:
              swa = getDIMMData<double>(js, dimmName, DataType::CURRENT, RailChannel::SWA);
              swb = getDIMMData<double>(js, dimmName, DataType::CURRENT, RailChannel::SWB);
              swc = getDIMMData<double>(js, dimmName, DataType::CURRENT, RailChannel::SWC);
              swd = getDIMMData<double>(js, dimmName, DataType::CURRENT, RailChannel::SWD);
              swe = getDIMMData<double>(js, dimmName, DataType::CURRENT, RailChannel::SWE);
              swf = getDIMMData<double>(js, dimmName, DataType::CURRENT, RailChannel::SWF);
              break;
          case ReqId::STATUS_REG:
              r4 = getDIMMData<uint8_t>(js, dimmName, DataType::STATUS, StatusReg::REG_04);
              r5 = getDIMMData<uint8_t>(js, dimmName, DataType::STATUS, StatusReg::REG_05);
              r6 = getDIMMData<uint8_t>(js, dimmName, DataType::STATUS, StatusReg::REG_06);
              r7 = getDIMMData<uint8_t>(js, dimmName, DataType::STATUS, StatusReg::REG_07);
              r8 = getDIMMData<uint8_t>(js, dimmName, DataType::STATUS, StatusReg::REG_08);
              r9 = getDIMMData<uint8_t>(js, dimmName, DataType::STATUS, StatusReg::REG_09);
              ra = getDIMMData<uint8_t>(js, dimmName, DataType::STATUS, StatusReg::REG_0A);
              rb = getDIMMData<uint8_t>(js, dimmName, DataType::STATUS, StatusReg::REG_0B);
              break;

          case ReqId::ERROR_CODE:
              err_r32 = getDIMMData<uint8_t>(js, dimmName, DataType::ERROR, ErrCode::errCode_REG_32);
              break;

          default:
              std::cout << "invalid reqId :" << (int)reqId << std::endl;
              return ipmi::responseParmOutOfRange();
        }

        if(reqId <= 3) {
          if(temp) {
            tempSensor.int_part = getTempIntPart(*temp);
            tempSensor.frac_part = getTempFracPart(*temp);
          }
          byte_vector.push_back(static_cast<uint8_t>(tempSensor.int_part));
          byte_vector.push_back(tempSensor.frac_part);
        }
        else if(reqId == 6) {
          for (const auto& opt : {r4, r5, r6, r7, r8, r9, ra, rb}) {
            byte_vector.push_back(opt.value_or(0xFF));
          }
        }
        else if(reqId == 7) {
          for (const auto& opt : {err_r32}) {
            byte_vector.push_back(opt.value_or(0xFF));
          }
        }	
        else {
          for (const auto& rail : {swa, swb, swc, swd, swe, swf}) {
            auto bytes = convertDoubleToTwoBytes(rail);
            byte_vector.insert(byte_vector.end(), bytes.begin(), bytes.end());
          }
        }
      }
    }

    return ipmi::responseSuccess(byte_vector);
}

ipmi::RspType<uint8_t> ipmiSwitchBMCFw(uint8_t bootsource)
{
    char cmd[256] = {0};
    if(bootsource !=1 && bootsource !=2)
    {
        return ipmi::responseParmOutOfRange();
    }

    if(bootsource == 1)//switch bmc to main flash
    {
        if(std::filesystem::exists(MAIN_BMC_BOOT_SOURCE))
        {
            memset(cmd, 0 , sizeof(cmd));
            snprintf(cmd, sizeof(cmd), "echo 1 > %s", MAIN_BMC_BOOT_SOURCE);
            if(system(cmd))
            {
                lg2::error("Set BMC boot from main flash failed.");
                return ipmi::responseUnspecifiedError();
            }
        }
        else
        {
            return ipmi::responseCommandNotAvailable();
        }
    }
    else if(bootsource == 2)//switch bmc to backup flash
    {
        if(std::filesystem::exists(BACKUP_BMC_BOOT_SOURCE))
        {
            memset(cmd, 0 , sizeof(cmd));
            snprintf(cmd, sizeof(cmd), "echo 1 > %s", BACKUP_BMC_BOOT_SOURCE);
            if(system(cmd))
            {
                lg2::error("Set BMC boot from backup flash failed.");
                return ipmi::responseUnspecifiedError();
            }
        }
        else
        {
            return ipmi::responseCommandNotAvailable();
        }
    }

    return ipmi::responseSuccess();
}


void register_ipmi_oem_functions() {
  // <Get Device Firmware Version>
  ipmi::registerOemHandler(ipmi::prioOem, ipmi::oemIANA,
                           ipmi::ipmiOem::cmdGetDeviceFWVersion,
                           ipmi::Privilege::Admin, GetDeviceFWVersion);

  // <Set Fan speed control mode>
  ipmi::registerOemHandler(ipmi::prioOem, ipmi::oemIANA,
                           ipmi::ipmiOem::cmdSetFanSpeedControlMode,
                           ipmi::Privilege::Admin, SetFanSpeedControlMode);

  // <Get Fan speed control mode>
  ipmi::registerOemHandler(ipmi::prioOem, ipmi::oemIANA,
                           ipmi::ipmiOem::cmdGetFanSpeedControlMode,
                           ipmi::Privilege::Admin, GetFanSpeedControlMode);

  // <Set BIOS Password Config>
  ipmi::registerOemHandler(ipmi::prioOem, ipmi::oemIANA,
                           ipmi::ipmiOem::cmdSetBIOSPasswordConfig,
                           ipmi::Privilege::Admin, SetBIOSPasswordConfig);

  // <Get BIOS Password Config>
  ipmi::registerOemHandler(ipmi::prioOem, ipmi::oemIANA,
                           ipmi::ipmiOem::cmdGetBIOSPasswordConfig,
                           ipmi::Privilege::Admin, GetBIOSPasswordConfig);

  // <Get PSU Info>
  ipmi::registerOemHandler(ipmi::prioOem, ipmi::oemIANA,
                           ipmi::ipmiOem::cmdGetPSUInfo,
                           ipmi::Privilege::Admin, GetPSUInfo);

  // <Get 12V PSU ADC Value>
  ipmi::registerOemHandler(ipmi::prioOem, ipmi::oemIANA,
                           ipmi::ipmiOem::cmdGet12VPSUADCValue,
                           ipmi::Privilege::Admin, Get12VPSUADCValue);

  // <Set 12V PSU ADC Value>
  ipmi::registerOemHandler(ipmi::prioOem, ipmi::oemIANA,
                           ipmi::ipmiOem::cmdSet12VPSUADCValue,
                           ipmi::Privilege::Admin, Set12VPSUADCValue);

  // <Get Memory Info>
  ipmi::registerOemHandler(ipmi::prioOem, ipmi::oemIANA,
                           ipmi::ipmiOem::cmdDIMMTemperatureValue,
                           ipmi::Privilege::Admin, GetDIMMTemperatureValue);

  // <OEM I2C Read Write>
  ipmi::registerOemHandler(ipmi::prioOem, ipmi::oemIANA,
                           ipmi::ipmiOem::cmdOemI2CReadWrite,
                           ipmi::Privilege::Admin, ipmiOemI2cReadWrite);

  // <OEM Set DIMM Setpoint>
  ipmi::registerOemHandler(ipmi::prioOem, ipmi::oemIANA,
                           ipmi::ipmiOem::cmdSetDIMMSetPoint,
                           ipmi::Privilege::Admin, ipmiOemSetDIMMSetPoint);

  // <OEM Get DIMM Setpoint>
  ipmi::registerOemHandler(ipmi::prioOem, ipmi::oemIANA,
                           ipmi::ipmiOem::cmdGetDIMMSetPoint,
                           ipmi::Privilege::Admin, ipmiOemGetDIMMSetPoint);

  // <OEM Set heat cover status>
  ipmi::registerOemHandler(ipmi::prioOem, ipmi::oemIANA,
                           ipmi::ipmiOem::cmdSetCoverStatus,
                           ipmi::Privilege::Admin, ipmiOemSetCoverStatus);

  // <OEM Get heat cover status>
  ipmi::registerOemHandler(ipmi::prioOem, ipmi::oemIANA,
                           ipmi::ipmiOem::cmdGetCoverStatus,
                           ipmi::Privilege::Admin, ipmiOemGetCoverStatus);

  // <OEM Set heat cover temperature>
  ipmi::registerOemHandler(ipmi::prioOem, ipmi::oemIANA,
                           ipmi::ipmiOem::cmdSetCoverPreHeatTemperature,
                           ipmi::Privilege::Admin, ipmiOemSetCoverHeatTemp);

  // <OEM heat cover temperature>
  ipmi::registerOemHandler(ipmi::prioOem, ipmi::oemIANA,
                           ipmi::ipmiOem::cmdGetCoverPreHeatTemperature,
                           ipmi::Privilege::Admin, ipmiOemGetCoverHeatTemp);

  // <OEM Set/Get BIOS Version>
  ipmi::registerOemHandler(ipmi::prioOem, ipmi::oemIANA,
                          ipmi::ipmiOem::cmdGetSetBiosVer,
                          ipmi::Privilege::Admin, ipmiSetGetBiosVersion);

  // <OEM restore factory default>
  ipmi::registerOemHandler(ipmi::prioOem, ipmi::oemIANA,
                          ipmi::ipmiOem::cmdRestoreFactoryDefault,
                          ipmi::Privilege::Admin, ipmiOemRestoreFactoryDefault);

  // <BIOS set Dimm State to BMC>
  ipmi::registerOemHandler(ipmi::prioOem, ipmi::oemIANA,
                           ipmi::ipmiOem::cmdBiosSetDimmState,
                           ipmi::Privilege::Admin, ipmiBiosSetDimmState);

  // <OEM set sol bitrate>
  ipmi::registerOemHandler(ipmi::prioOem, ipmi::oemIANA,
                           ipmi::ipmiOem::cmdSetSOLBitrate,
                           ipmi::Privilege::Admin, ipmiOemSetSOLBitrate);

  // <BIOS set Dimm Parameters to BMC>
  ipmi::registerOemHandler(ipmi::prioOem, ipmi::oemIANA,
                           ipmi::ipmiOem::cmdBiosSetDimmPara,
                           ipmi::Privilege::Admin, ipmiBiosSetDimmPara);

  // <BIOS get Dimm Parameters to BMC>
  ipmi::registerOemHandler(ipmi::prioOem, ipmi::oemIANA,
                           ipmi::ipmiOem::cmdBiosGetDimmPara,
                           ipmi::Privilege::Admin, ipmiBiosGetDimmPara);

  ipmi::registerOemHandler(ipmi::prioOem, ipmi::oemIANA,
                           ipmi::ipmiOem::cmdGetDimmTemp,
                           ipmi::Privilege::Admin, ipmiGetDimmTemp);

  ipmi::registerOemHandler(ipmi::prioOem, ipmi::oemIANA,
                           ipmi::ipmiOem::cmdGetCoverCurrentTemp,
                           ipmi::Privilege::Admin, ipmiOemGetCurrentCoverTemp);

  ipmi::registerOemHandler(ipmi::prioOem, ipmi::oemIANA,
                           ipmi::ipmiOem::cmdSetCoverFSCEnableStatus,
                           ipmi::Privilege::Admin, ipmiOemSetCoverFSCEnableStatus);

  ipmi::registerOemHandler(ipmi::prioOem, ipmi::oemIANA,
                           ipmi::ipmiOem::cmdGetCoverFSCEnableStatus,
                           ipmi::Privilege::Admin, ipmiOemGetCoverFSCEnableStatus);

  // <OEM Switch BMC Main Backup Flash>
  ipmi::registerOemHandler(ipmi::prioOem, ipmi::oemIANA,
                           ipmi::ipmiOem::cmdSwitchBMCFw,
                           ipmi::Privilege::Admin, ipmiSwitchBMCFw);
}