text: #include "pldm.hpp"
  
  #include "file.hpp"
  
  #include <libpldm/entity.h>
  #include <libpldm/platform.h>
  #include <libpldm/state_set.h>
  
  #include <phosphor-logging/log.hpp>
  #include <iostream>
  
  namespace pldm
  {
  
  using sdbusplus::exception::SdBusError;
  using namespace phosphor::logging;
  
  void Interface::fetchOCCSensorInfo(const PdrList& pdrs,
                                     SensorToOCCInstance& sensorInstanceMap,
                                     SensorOffset& sensorOffset)
  {
      bool offsetFound = false;
      auto pdr =
          reinterpret_cast<const pldm_state_sensor_pdr*>(pdrs.front().data());
      auto possibleStatesPtr = pdr->possible_states;
      for (auto offset = 0; offset < pdr->composite_sensor_count; offset++)
      {
          auto possibleStates =
              reinterpret_cast<const state_sensor_possible_states*>(
                  possibleStatesPtr);
  
          if (possibleStates->state_set_id ==
              PLDM_STATE_SET_OPERATIONAL_RUNNING_STATUS)
          {
              sensorOffset = offset;
              offsetFound = true;
              break;
          }
          possibleStatesPtr += sizeof(possibleStates->state_set_id) +
                               sizeof(possibleStates->possible_states_size) +
                               possibleStates->possible_states_size;
      }
  
      if (!offsetFound)
      {
          log<level::ERR>("pldm: OCC state sensor PDR with StateSetId "
                          "PLDM_STATE_SET_OPERATIONAL_RUNNING_STATUS not found");
          return;
      }
  
      // To order SensorID based on the EntityInstance
      std::map<EntityInstance, SensorID> entityInstMap{};
      for (auto& pdr : pdrs)
      {
          auto pdrPtr =
              reinterpret_cast<const pldm_state_sensor_pdr*>(pdr.data());
          entityInstMap.emplace(
              static_cast<EntityInstance>(pdrPtr->entity_instance),
              static_cast<SensorID>(pdrPtr->sensor_id));
      }
  
      open_power::occ::instanceID count = start;
      for (auto const& pair : entityInstMap)
      {
          sensorInstanceMap.emplace(pair.second, count);
          count++;
      }
  }
  
  void Interface::sensorEvent(sdbusplus::message::message& msg)
  {
      if (!isOCCSensorCacheValid())
      {
          PdrList pdrs{};
  
          try
          {
              auto method = bus.new_method_call(
                  "xyz.openbmc_project.PLDM", "/xyz/openbmc_project/pldm",
                  "xyz.openbmc_project.PLDM.PDR", "FindStateSensorPDR");
              method.append(tid, (uint16_t)PLDM_ENTITY_PROC_MODULE,
                            (uint16_t)PLDM_STATE_SET_OPERATIONAL_RUNNING_STATUS);
  
              auto responseMsg = bus.call(method);
              responseMsg.read(pdrs);
          }
          catch (const SdBusError& e)
          {
              log<level::ERR>("pldm: Failed to fetch the OCC state sensor PDRs",
                              entry("ERROR=%s", e.what()));
          }
  
          if (!pdrs.size())
          {
              log<level::ERR>("pldm: OCC state sensor PDRs not present");
              return;
          }
  
          fetchOCCSensorInfo(pdrs, sensorToOCCInstance, sensorOffset);
      }
  
      TerminusID tid{};
      SensorID sensorId{};
      SensorOffset msgSensorOffset{};
      EventState eventState{};
      EventState previousEventState{};
  
      msg.read(tid, sensorId, sensorOffset, eventState, previousEventState);
  
      auto sensorEntry = sensorToOCCInstance.find(sensorId);
      if (sensorEntry == sensorToOCCInstance.end() ||
          (msgSensorOffset != sensorOffset))
      {
          // No action for non matching sensorEvents
          return;
      }
  
      bool newState{};
      if (eventState == static_cast<EventState>(
                            PLDM_STATE_SET_OPERATIONAL_RUNNING_STATUS_IN_SERVICE))
      {
  std::cerr << "PLDM OCC ACTIVE" << std::endl << std::flush;
          newState = callBack(sensorEntry->second, true);
      }
      else if (eventState ==
               static_cast<EventState>(
                   PLDM_STATE_SET_OPERATIONAL_RUNNING_STATUS_STOPPED))
      {
  std::cerr << "PLDM OCC INACTIVE" << std::endl << std::flush;
          newState = callBack(sensorEntry->second, false);
      }
      else
      {
          return;
      }
  
      log<level::INFO>("pldm: Updated OCCActive state",
                       entry("STATE=%s", newState ? "true" : "false"));
      return;
  }
  
  void Interface::hostStateEvent(sdbusplus::message::message& msg)
  {
      std::map<std::string, std::variant<std::string>> properties{};
      std::string interface;
      msg.read(interface, properties);
      const auto stateEntry = properties.find("CurrentHostState");
      if (stateEntry != properties.end())
      {
          auto stateEntryValue = stateEntry->second;
          auto propVal = std::get<std::string>(stateEntryValue);
          if (propVal == "xyz.openbmc_project.State.Host.HostState.Off")
          {
              sensorToOCCInstance.clear();
              occInstanceToEffecter.clear();
          }
      }
  }
  
  void Interface::fetchOCCEffecterInfo(
      const PdrList& pdrs, OccInstanceToEffecter& instanceToEffecterMap,
      CompositeEffecterCount& count, uint8_t& bootRestartPos)
  {
      bool offsetFound = false;
      auto pdr =
          reinterpret_cast<const pldm_state_effecter_pdr*>(pdrs.front().data());
      auto possibleStatesPtr = pdr->possible_states;
      for (auto offset = 0; offset < pdr->composite_effecter_count; offset++)
      {
          auto possibleStates =
              reinterpret_cast<const state_effecter_possible_states*>(
                  possibleStatesPtr);
  
          if (possibleStates->state_set_id == PLDM_STATE_SET_BOOT_RESTART_CAUSE)
          {
              bootRestartPos = offset;
              effecterCount = pdr->composite_effecter_count;
              offsetFound = true;
              break;
          }
          possibleStatesPtr += sizeof(possibleStates->state_set_id) +
                               sizeof(possibleStates->possible_states_size) +
                               possibleStates->possible_states_size;
      }
  
      if (!offsetFound)
      {
          return;
      }
  
      std::map<EntityInstance, EffecterID> entityInstMap{};
      for (auto& pdr : pdrs)
      {
          auto pdrPtr =
              reinterpret_cast<const pldm_state_effecter_pdr*>(pdr.data());
          entityInstMap.emplace(
              static_cast<EntityInstance>(pdrPtr->entity_instance),
              static_cast<SensorID>(pdrPtr->effecter_id));
      }
  
      open_power::occ::instanceID position = start;
      for (auto const& pair : entityInstMap)
      {
          occInstanceToEffecter.emplace(position, pair.second);
          position++;
      }
  }
  
  std::vector<uint8_t>
      Interface::prepareSetEffecterReq(uint8_t instanceId, EffecterID effecterId,
                                       CompositeEffecterCount effecterCount,
                                       uint8_t bootRestartPos)
  {
      std::vector<uint8_t> request(
          sizeof(pldm_msg_hdr) + sizeof(effecterId) + sizeof(effecterCount) +
          (effecterCount * sizeof(set_effecter_state_field)));
      auto requestMsg = reinterpret_cast<pldm_msg*>(request.data());
      std::vector<set_effecter_state_field> stateField;
  
      for (uint8_t effecterPos = 0; effecterPos < effecterCount; effecterPos++)
      {
          if (effecterPos == bootRestartPos)
          {
              stateField.emplace_back(set_effecter_state_field{
                  PLDM_REQUEST_SET,
                  PLDM_STATE_SET_BOOT_RESTART_CAUSE_WARM_RESET});
          }
          else
          {
              stateField.emplace_back(
                  set_effecter_state_field{PLDM_NO_CHANGE, 0});
          }
      }
      auto rc = encode_set_state_effecter_states_req(
          instanceId, effecterId, effecterCount, stateField.data(), requestMsg);
      if (rc != PLDM_SUCCESS)
      {
          log<level::ERR>("encode set effecter states request returned error ",
                          entry("RC=%d", rc));
          request.clear();
      }
      return request;
  }
  
  void Interface::resetOCC(open_power::occ::instanceID occInstanceId)
  {
      if (!isPDREffecterCacheValid())
      {
          PdrList pdrs{};
  
          try
          {
              auto method = bus.new_method_call(
                  "xyz.openbmc_project.PLDM", "/xyz/openbmc_project/pldm",
                  "xyz.openbmc_project.PLDM.PDR", "FindStateEffecterPDR");
              method.append(tid, (uint16_t)PLDM_ENTITY_PROC_MODULE,
                            (uint16_t)PLDM_STATE_SET_BOOT_RESTART_CAUSE);
  
              auto responseMsg = bus.call(method);
              responseMsg.read(pdrs);
          }
          catch (const SdBusError& e)
          {
              log<level::ERR>("pldm: Failed to fetch the OCC state effecter PDRs",
                              entry("ERROR=%s", e.what()));
          }
  
          if (!pdrs.size())
          {
              log<level::ERR>("pldm: OCC state effecter PDRs not present");
              return;
          }
  
          fetchOCCEffecterInfo(pdrs, occInstanceToEffecter, effecterCount,
                               bootRestartPosition);
      }
  
      // Find the matching effecter for the OCC instance
      auto effecterEntry = occInstanceToEffecter.find(occInstanceId);
      if (effecterEntry == occInstanceToEffecter.end())
      {
          log<level::ERR>(
              "pldm: Failed to find a matching effecter for OCC instance",
              entry("OCC_INSTANCE_ID=%d", occInstanceId));
  
          return;
      }
  
      uint8_t instanceId{};
  
      try
      {
          auto method = bus.new_method_call(
              "xyz.openbmc_project.PLDM", "/xyz/openbmc_project/pldm",
              "xyz.openbmc_project.PLDM.Requester", "GetInstanceId");
          method.append(mctpEid);
          auto reply = bus.call(method);
          reply.read(instanceId);
      }
      catch (const SdBusError& e)
      {
          log<level::ERR>("pldm: GetInstanceId returned error",
                          entry("ERROR=%s", e.what()));
          return;
      }
  
      // Prepare the SetStateEffecterStates request to reset the OCC
      auto request = prepareSetEffecterReq(instanceId, effecterEntry->second,
                                           effecterCount, bootRestartPosition);
  
      if (request.empty())
      {
          log<level::ERR>("pldm: SetStateEffecterStates request message empty");
          return;
      }
  
      // Connect to MCTP scoket
      int fd = pldm_open();
      if (fd == -1)
      {
          log<level::ERR>("pldm: Failed to connect to MCTP socket");
          return;
      }
      open_power::occ::FileDescriptor fileFd(fd);
  
      // Send the PLDM request message to HBRT
      uint8_t* response = nullptr;
      size_t responseSize{};
      auto rc = pldm_send_recv(mctpEid, fileFd(), request.data(), request.size(),
                               &response, &responseSize);
      std::unique_ptr<uint8_t, decltype(std::free)*> responsePtr{response,
                                                                 std::free};
      if (rc)
      {
          log<level::ERR>("pldm: pldm_send_recv failed for OCC reset",
                          entry("RC=%d", rc));
      }
  
      uint8_t completionCode{};
      auto responseMsg = reinterpret_cast<const pldm_msg*>(responsePtr.get());
      auto rcDecode = decode_set_state_effecter_states_resp(
          responseMsg, responseSize - sizeof(pldm_msg_hdr), &completionCode);
      if (rcDecode || completionCode)
      {
          log<level::ERR>(
              "pldm: decode_set_state_effecter_states_resp returned error",
              entry("RC=%d", rcDecode),
              entry("COMPLETION_CODE=%d", completionCode));
      }
  
      return;
  }
  
  } // namespace pldm
  