/** @file
  IPMI Serial Over Lan Driver.

Copyright (c) 2018 - 2019, Intel Corporation. All rights reserved.<BR>
Copyright (C) 2026 Advanced Micro Devices, Inc. All rights reserved.<BR>
SPDX-License-Identifier: BSD-2-Clause-Patent

**/

#include <Uefi.h>
#include <Library/BaseLib.h>
#include <Library/DebugLib.h>
#include <Library/BaseMemoryLib.h>
#include <Library/MemoryAllocationLib.h>
#include <Library/UefiBootServicesTableLib.h>
#include <Library/UefiRuntimeServicesTableLib.h>
#include <Library/IpmiCommandLib.h>
#include <IndustryStandard/Ipmi.h>

#define SOL_CMD_RETRY_COUNT  10

/**

Routine Description:

    This routine gets the SOL payload status or settings for a specific channel.

Arguments:
    @param[in] Channel  LAN channel number.
    @param[in] ParamSel Configuration parameter selection.
    @param[in, out] Data     Information returned from BMC.
Returns:
    @retval EFI_SUCCESS     SOL configuration parameters are successfully read from BMC.
    @retval Others          SOL configuration parameters could not be read from BMC.

**/
EFI_STATUS
GetSolStatus (
  IN UINT8      Channel,
  IN UINT8      ParamSel,
  IN OUT UINT8  *Data
  )
{
  EFI_STATUS                                      Status;
  IPMI_GET_SOL_CONFIGURATION_PARAMETERS_REQUEST   GetConfigurationParametersRequest;
  IPMI_GET_SOL_CONFIGURATION_PARAMETERS_RESPONSE  GetConfigurationParametersResponse;
  UINT32                                          DataSize;
  UINT8                                           RetryCount;

  Status = EFI_SUCCESS;
  for (RetryCount = 0; RetryCount < SOL_CMD_RETRY_COUNT; RetryCount++) {
    ZeroMem (&GetConfigurationParametersRequest, sizeof (GetConfigurationParametersRequest));
    GetConfigurationParametersRequest.ChannelNumber.Bits.ChannelNumber = Channel;
    GetConfigurationParametersRequest.ParameterSelector                = ParamSel;

    ZeroMem (&GetConfigurationParametersResponse, sizeof (GetConfigurationParametersResponse));

    DataSize = sizeof (GetConfigurationParametersResponse);
    Status   = IpmiGetSolConfigurationParameters (
                 &GetConfigurationParametersRequest,
                 &GetConfigurationParametersResponse,
                 &DataSize
                 );

    if (Status == EFI_SUCCESS) {
      break;
    } else {
      gBS->Stall (100000);
    }
  }

  if (Status == EFI_SUCCESS) {
    *Data = GetConfigurationParametersResponse.ParameterData[0];
  }

  return Status;
}

/**

Routine Description:

    This routine sets the SOL payload configuration parameters for a specific channel.

Arguments:
    @param[in] Channel   LAN channel number.
    @param[in] ParamSel  Configuration parameter selection.
    @param[in] Data      Configuration parameter values.
Returns:
    @retval EFI_SUCCESS     SOL configuration parameters are sent to BMC.
    @retval Others          SOL configuration parameters could not be sent to BMC.

**/
EFI_STATUS
SetSolParams (
  IN UINT8  Channel,
  IN UINT8  ParamSel,
  IN UINT8  Data
  )
{
  EFI_STATUS                                     Status;
  IPMI_SET_SOL_CONFIGURATION_PARAMETERS_REQUEST  SetConfigurationParametersRequest;
  UINT8                                          CompletionCode;
  UINT8                                          RetryCount;

  Status = EFI_SUCCESS;
  for (RetryCount = 0; RetryCount < SOL_CMD_RETRY_COUNT; RetryCount++) {
    ZeroMem (&SetConfigurationParametersRequest, sizeof (SetConfigurationParametersRequest));
    SetConfigurationParametersRequest.ChannelNumber.Bits.ChannelNumber = Channel;
    SetConfigurationParametersRequest.ParameterSelector                = ParamSel;
    SetConfigurationParametersRequest.ParameterData[0]                 = Data;

    CompletionCode = 0;

    Status = IpmiSetSolConfigurationParameters (
               &SetConfigurationParametersRequest,
               sizeof (SetConfigurationParametersRequest),
               &CompletionCode
               );

    if (Status == EFI_SUCCESS) {
      break;
    } else {
      gBS->Stall (100000);
    }
  }

  return Status;
}

/**

  Routine Description:
    This is the standard EFI driver point. This function initializes
    the private data required for creating SOL Status Driver.

  @param[in] ImageHandle - Handle of this driver image
  @param[in] SystemTable - Table containing standard EFI services

  @retval EFI_SUCCESS    - SOL status is initialized successfully.
  @retval Otherwise      - Other errors.

**/
EFI_STATUS
EFIAPI
SolStatusEntryPoint (
  IN EFI_HANDLE        ImageHandle,
  IN EFI_SYSTEM_TABLE  *SystemTable
  )
{
  EFI_STATUS  Status;
  UINT8       Channel;
  BOOLEAN     SolEnabled;

  Status     = EFI_SUCCESS;
  SolEnabled = FALSE;
  for (Channel = 1; Channel <= PcdGet8 (PcdMaxSolChannels); Channel++) {
    Status = GetSolStatus (Channel, IPMI_SOL_CONFIGURATION_PARAMETER_SOL_ENABLE, &SolEnabled);
    if (Status == EFI_SUCCESS) {
      DEBUG ((DEBUG_ERROR, "SOL enabling status for channel %x is %x\n", Channel, SolEnabled));
    } else {
      DEBUG ((DEBUG_ERROR, "Failed to get channel %x SOL status from BMC!, status is %x\n", Channel, Status));
    }
  }

  return Status;
}
