/** @file
KCS Transport Hook.
Copyright (c) 2018, Intel Corporation. All rights reserved.
SPDX-License-Identifier: BSD-2-Clause-Patent
**/
#include "KcsBmc.h"
#include
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;
}