/** @file
|
KCS Transport Hook.
|
|
Copyright (c) 2018, Intel Corporation. All rights reserved.<BR>
|
SPDX-License-Identifier: BSD-2-Clause-Patent
|
|
**/
|
|
#include "KcsBmc.h"
|
#include <Library/DebugLib.h>
|
|
EFI_STATUS
|
KcsErrorExit (
|
UINT64 KcsTimeoutPeriod,
|
UINT16 KcsPort
|
)
|
/*++
|
|
Routine Description:
|
|
Check the KCS error status
|
|
Arguments:
|
|
KcsPort - The base port of KCS
|
|
Returns:
|
|
EFI_DEVICE_ERROR - The device error happened
|
EFI_SUCCESS - Successfully check the KCS error status
|
|
--*/
|
{
|
EFI_STATUS Status;
|
UINT8 KcsData;
|
EFI_KCS_STATUS KcsStatus;
|
UINT8 BmcStatus;
|
UINT8 RetryCount;
|
UINT64 TimeOut;
|
|
TimeOut = 0;
|
RetryCount = 0;
|
while (RetryCount < KCS_ABORT_RETRY_COUNT) {
|
|
do {
|
MicroSecondDelay(KCS_DELAY_UNIT);
|
KcsStatus.RawData = IoRead8 (KcsPort + 1);
|
if (KcsStatus.RawData == 0xFF || (TimeOut >= KcsTimeoutPeriod)) {
|
RetryCount = KCS_ABORT_RETRY_COUNT;
|
break;
|
}
|
TimeOut++;
|
} while (KcsStatus.Status.Ibf);
|
|
if (RetryCount >= KCS_ABORT_RETRY_COUNT) {
|
break;
|
}
|
|
KcsData = KCS_ABORT;
|
IoWrite8 ((KcsPort + 1), KcsData);
|
|
TimeOut = 0;
|
do {
|
MicroSecondDelay(KCS_DELAY_UNIT);
|
KcsStatus.RawData = IoRead8 (KcsPort + 1);
|
if (KcsStatus.RawData == 0xFF || (TimeOut >= KcsTimeoutPeriod)) {
|
Status = EFI_DEVICE_ERROR;
|
goto LabelError;
|
}
|
TimeOut++;
|
} while (KcsStatus.Status.Ibf);
|
|
KcsData = IoRead8 (KcsPort);
|
|
KcsData = 0x0;
|
IoWrite8 (KcsPort, KcsData);
|
|
TimeOut = 0;
|
do {
|
MicroSecondDelay(KCS_DELAY_UNIT);
|
KcsStatus.RawData = IoRead8 (KcsPort + 1);
|
if (KcsStatus.RawData == 0xFF || (TimeOut >= KcsTimeoutPeriod)) {
|
Status = EFI_DEVICE_ERROR;
|
goto LabelError;
|
}
|
TimeOut++;
|
} while (KcsStatus.Status.Ibf);
|
|
if (KcsStatus.Status.State == KcsReadState) {
|
TimeOut = 0;
|
do {
|
MicroSecondDelay(KCS_DELAY_UNIT);
|
KcsStatus.RawData = IoRead8 (KcsPort + 1);
|
if (KcsStatus.RawData == 0xFF || (TimeOut >= KcsTimeoutPeriod)) {
|
Status = EFI_DEVICE_ERROR;
|
goto LabelError;
|
}
|
TimeOut++;
|
} while (!KcsStatus.Status.Obf);
|
|
BmcStatus = IoRead8 (KcsPort);
|
|
KcsData = KCS_READ;
|
IoWrite8 (KcsPort, KcsData);
|
|
TimeOut = 0;
|
do {
|
MicroSecondDelay(KCS_DELAY_UNIT);
|
KcsStatus.RawData = IoRead8 (KcsPort + 1);
|
if (KcsStatus.RawData == 0xFF || (TimeOut >= KcsTimeoutPeriod)) {
|
Status = EFI_DEVICE_ERROR;
|
goto LabelError;
|
}
|
TimeOut++;
|
} while (KcsStatus.Status.Ibf);
|
|
if (KcsStatus.Status.State == KcsIdleState) {
|
TimeOut = 0;
|
do {
|
MicroSecondDelay(KCS_DELAY_UNIT);
|
KcsStatus.RawData = IoRead8 (KcsPort + 1);
|
if (KcsStatus.RawData == 0xFF || (TimeOut >= KcsTimeoutPeriod)) {
|
Status = EFI_DEVICE_ERROR;
|
goto LabelError;
|
}
|
TimeOut++;
|
} while (!KcsStatus.Status.Obf);
|
|
KcsData = IoRead8 (KcsPort);
|
break;
|
|
} else {
|
RetryCount++;
|
continue;
|
}
|
|
} else {
|
RetryCount++;
|
continue;
|
}
|
}
|
|
if (RetryCount >= KCS_ABORT_RETRY_COUNT) {
|
Status = EFI_DEVICE_ERROR;
|
goto LabelError;
|
}
|
|
return EFI_SUCCESS;
|
|
LabelError:
|
|
return Status;
|
}
|
|
EFI_STATUS
|
KcsCheckStatus (
|
UINT64 KcsTimeoutPeriod,
|
UINT16 KcsPort,
|
KCS_STATE KcsState,
|
BOOLEAN *Idle
|
)
|
/*++
|
|
Routine Description:
|
|
Ckeck KCS status
|
|
Arguments:
|
|
KcsPort - The base port of KCS
|
KcsState - The state of KCS to be checked
|
Idle - If the KCS is idle
|
|
Returns:
|
|
EFI_SUCCESS - Checked the KCS status successfully
|
|
--*/
|
{
|
EFI_STATUS Status = 0;
|
EFI_KCS_STATUS KcsStatus = { 0 };
|
UINT8 KcsData = 0;
|
UINT64 TimeOut = 0;
|
|
if(Idle == NULL ){
|
return EFI_INVALID_PARAMETER;
|
}
|
|
*Idle = FALSE;
|
|
do {
|
MicroSecondDelay(KCS_DELAY_UNIT);
|
KcsStatus.RawData = IoRead8 (KcsPort + 1);
|
if (KcsStatus.RawData == 0xFF || (TimeOut >= KcsTimeoutPeriod)) {
|
Status = EFI_DEVICE_ERROR;
|
goto LabelError;
|
}
|
TimeOut++;
|
} while (KcsStatus.Status.Ibf);
|
|
if (KcsState == KcsWriteState) {
|
KcsData = IoRead8 (KcsPort);
|
}
|
|
if (KcsStatus.Status.State != KcsState) {
|
if ((KcsStatus.Status.State == KcsIdleState) && (KcsState == KcsReadState)) {
|
*Idle = TRUE;
|
} else {
|
Status = KcsErrorExit (KcsTimeoutPeriod, KcsPort);
|
goto LabelError;
|
}
|
}
|
|
if (KcsState == KcsReadState) {
|
TimeOut = 0;
|
do {
|
MicroSecondDelay(KCS_DELAY_UNIT);
|
KcsStatus.RawData = IoRead8 (KcsPort + 1);
|
if (KcsStatus.RawData == 0xFF || (TimeOut >= KcsTimeoutPeriod)) {
|
Status = EFI_DEVICE_ERROR;
|
goto LabelError;
|
}
|
TimeOut++;
|
} while (!KcsStatus.Status.Obf);
|
}
|
|
if (KcsState == KcsWriteState || Idle) {
|
KcsData = IoRead8 (KcsPort);
|
}
|
|
return EFI_SUCCESS;
|
|
LabelError:
|
|
return Status;
|
}
|
|
EFI_STATUS
|
SendDataToBmc (
|
UINT64 KcsTimeoutPeriod,
|
UINT16 KcsPort,
|
UINT8 *Data,
|
UINT8 DataSize
|
)
|
/*++
|
|
Routine Description:
|
|
Send data to BMC
|
|
Arguments:
|
|
Data - The data pointer to be sent
|
DataSize - The data size
|
|
Returns:
|
|
EFI_SUCCESS - Send out the data successfully
|
|
--*/
|
{
|
EFI_KCS_STATUS KcsStatus;
|
UINT8 KcsData;
|
UINT16 KcsIoBase;
|
EFI_STATUS Status;
|
UINT8 i;
|
BOOLEAN Idle;
|
UINT64 TimeOut = 0;
|
|
DEBUG ((DEBUG_INFO, "SendDataToBmc (%ld, 0x%x) - ", KcsTimeoutPeriod, KcsPort));
|
for (i = 0; i < DataSize; i++) {
|
DEBUG ((DEBUG_INFO, "%02x ", Data[i]));
|
}
|
DEBUG ((DEBUG_INFO, "\n"));
|
|
KcsIoBase = KcsPort;
|
|
do {
|
MicroSecondDelay(KCS_DELAY_UNIT);
|
KcsStatus.RawData = IoRead8 (KcsIoBase + 1);
|
if ((KcsStatus.RawData == 0xFF) || (TimeOut >= KcsTimeoutPeriod))
|
{
|
if ((Status = KcsErrorExit (KcsTimeoutPeriod, KcsIoBase)) != EFI_SUCCESS)
|
{
|
DEBUG ((DEBUG_INFO, "KcsErrorExit - %r\n", Status));
|
return Status;
|
}
|
}
|
TimeOut++;
|
} while (KcsStatus.Status.Ibf);
|
|
KcsData = KCS_WRITE_START;
|
IoWrite8 ((KcsIoBase + 1), KcsData);
|
if ((Status = KcsCheckStatus (KcsTimeoutPeriod, KcsIoBase, KcsWriteState, &Idle)) != EFI_SUCCESS) {
|
DEBUG ((DEBUG_INFO, "KcsCheckStatus 1 - %r\n", Status));
|
return Status;
|
}
|
|
for (i = 0; i < DataSize; i++) {
|
if (i == (DataSize - 1)) {
|
if ((Status = KcsCheckStatus (KcsTimeoutPeriod, KcsIoBase, KcsWriteState, &Idle)) != EFI_SUCCESS) {
|
DEBUG ((DEBUG_INFO, "KcsCheckStatus 2 - %r\n", Status));
|
return Status;
|
}
|
|
KcsData = KCS_WRITE_END;
|
IoWrite8 ((KcsIoBase + 1), KcsData);
|
}
|
|
Status = KcsCheckStatus (KcsTimeoutPeriod, KcsIoBase, KcsWriteState, &Idle);
|
if (EFI_ERROR (Status)) {
|
DEBUG ((DEBUG_INFO, "KcsCheckStatus 3 - %r\n", Status));
|
return Status;
|
}
|
|
IoWrite8 (KcsIoBase, Data[i]);
|
}
|
|
return EFI_SUCCESS;
|
}
|
|
EFI_STATUS
|
ReceiveBmcData (
|
UINT64 KcsTimeoutPeriod,
|
UINT16 KcsPort,
|
UINT8 *Data,
|
UINT8 *DataSize
|
)
|
/*++
|
|
Routine Description:
|
|
Routine Description:
|
|
Receive data from BMC
|
|
Arguments:
|
|
Data - The buffer pointer
|
DataSize - The buffer size
|
|
Returns:
|
|
EFI_SUCCESS - Received data successfully
|
|
--*/
|
{
|
UINT8 KcsData;
|
UINT16 KcsIoBase;
|
EFI_STATUS Status;
|
BOOLEAN Idle;
|
UINT8 Count;
|
|
Count = 0;
|
KcsIoBase = KcsPort;
|
|
DEBUG ((DEBUG_INFO, "ReceiveBmcData (%ld, 0x%x)...\n", KcsTimeoutPeriod, KcsPort));
|
|
while (TRUE) {
|
|
if ((Status = KcsCheckStatus (KcsTimeoutPeriod, KcsIoBase, KcsReadState, &Idle)) != EFI_SUCCESS) {
|
DEBUG ((DEBUG_INFO, "KcsCheckStatus - %r\n", Status));
|
return Status;
|
}
|
|
if (Idle) {
|
DEBUG ((DEBUG_INFO, "DataSize - 0x%x\n", Count));
|
*DataSize = Count;
|
break;
|
}
|
|
if (Count > *DataSize) {
|
DEBUG ((DEBUG_INFO, "ERROR: Count(0x%x) > *DataSize(0x%x)\n", Count, *DataSize));
|
return EFI_DEVICE_ERROR;
|
}
|
|
Data[Count] = IoRead8 (KcsIoBase);
|
|
Count++;
|
|
KcsData = KCS_READ;
|
IoWrite8 (KcsIoBase, KcsData);
|
}
|
|
DEBUG ((DEBUG_INFO, "ReceiveBmcData (%ld, 0x%x) - ", KcsTimeoutPeriod, KcsPort));
|
for (Count = 0; Count < *DataSize; Count++) {
|
DEBUG ((DEBUG_INFO, "%02x ", Data[Count]));
|
}
|
DEBUG ((DEBUG_INFO, "\n"));
|
|
return EFI_SUCCESS;
|
}
|
|
EFI_STATUS
|
ReceiveBmcDataFromPort (
|
UINT64 KcsTimeoutPeriod,
|
UINT16 KcsPort,
|
UINT8 *Data,
|
UINT8 *DataSize
|
)
|
/*++
|
|
Routine Description:
|
|
Receive data from BMC
|
|
Arguments:
|
|
Data - The buffer pointer to receive data
|
DataSize - The buffer size
|
|
Returns:
|
|
EFI_SUCCESS - Received the data successfully
|
|
--*/
|
{
|
EFI_STATUS Status;
|
UINT16 KcsIoBase;
|
UINT8 i;
|
UINT8 MyDataSize;
|
|
MyDataSize = *DataSize;
|
|
KcsIoBase = KcsPort;
|
|
for (i = 0; i < KCS_ABORT_RETRY_COUNT; i++) {
|
Status = ReceiveBmcData (KcsTimeoutPeriod, KcsIoBase, Data, DataSize);
|
if (EFI_ERROR (Status)) {
|
if ((Status = KcsErrorExit (KcsTimeoutPeriod, KcsIoBase)) != EFI_SUCCESS) {
|
return Status;
|
}
|
|
*DataSize = MyDataSize;
|
} else {
|
return Status;
|
}
|
}
|
|
return EFI_DEVICE_ERROR;
|
}
|
|
EFI_STATUS
|
SendDataToBmcPort (
|
UINT64 KcsTimeoutPeriod,
|
UINT16 KcsPort,
|
UINT8 *Data,
|
UINT8 DataSize
|
)
|
/*++
|
|
Routine Description:
|
|
Send data to BMC
|
|
Arguments:
|
|
Data - The data pointer to be sent
|
DataSize - The data size
|
|
Returns:
|
|
EFI_SUCCESS - Send out the data successfully
|
|
--*/
|
{
|
EFI_STATUS Status;
|
UINT16 KcsIoBase;
|
UINT8 i;
|
|
KcsIoBase = KcsPort;
|
|
for (i = 0; i < KCS_ABORT_RETRY_COUNT; i++) {
|
Status = SendDataToBmc (KcsTimeoutPeriod, KcsIoBase, Data, DataSize);
|
if (EFI_ERROR (Status)) {
|
if ((Status = KcsErrorExit (KcsTimeoutPeriod, KcsIoBase)) != EFI_SUCCESS) {
|
return Status;
|
}
|
} else {
|
return Status;
|
}
|
}
|
|
return EFI_DEVICE_ERROR;
|
}
|