audk/IntelFrameworkModulePkg/Bus/Isa/IsaSerialDxe/Serial.c

2033 lines
58 KiB
C

/** @file
Serial driver for standard UARTS on an ISA bus.
Copyright (c) 2006 - 2018, Intel Corporation. All rights reserved.<BR>
SPDX-License-Identifier: BSD-2-Clause-Patent
**/
#include "Serial.h"
//
// ISA Serial Driver Global Variables
//
EFI_DRIVER_BINDING_PROTOCOL gSerialControllerDriver = {
SerialControllerDriverSupported,
SerialControllerDriverStart,
SerialControllerDriverStop,
0xa,
NULL,
NULL
};
SERIAL_DEV gSerialDevTempate = {
SERIAL_DEV_SIGNATURE,
NULL,
{ // SerialIo
SERIAL_IO_INTERFACE_REVISION,
IsaSerialReset,
IsaSerialSetAttributes,
IsaSerialSetControl,
IsaSerialGetControl,
IsaSerialWrite,
IsaSerialRead,
NULL
},
{ // SerialMode
SERIAL_PORT_SUPPORT_CONTROL_MASK,
SERIAL_PORT_DEFAULT_TIMEOUT,
0,
SERIAL_PORT_DEFAULT_RECEIVE_FIFO_DEPTH,
0,
0,
0
},
NULL,
NULL,
{ // UartDevicePath
{
MESSAGING_DEVICE_PATH,
MSG_UART_DP,
{
(UINT8) (sizeof (UART_DEVICE_PATH)),
(UINT8) ((sizeof (UART_DEVICE_PATH)) >> 8)
}
},
0,
0,
0,
0,
0
},
NULL,
0, //BaseAddress
{
0,
0,
SERIAL_MAX_BUFFER_SIZE,
{ 0 }
},
{
0,
0,
SERIAL_MAX_BUFFER_SIZE,
{ 0 }
},
FALSE,
FALSE,
Uart16550A,
NULL
};
/**
Check the device path node whether it's the Flow Control node or not.
@param[in] FlowControl The device path node to be checked.
@retval TRUE It's the Flow Control node.
@retval FALSE It's not.
**/
BOOLEAN
IsUartFlowControlNode (
IN UART_FLOW_CONTROL_DEVICE_PATH *FlowControl
)
{
return (BOOLEAN) (
(DevicePathType (FlowControl) == MESSAGING_DEVICE_PATH) &&
(DevicePathSubType (FlowControl) == MSG_VENDOR_DP) &&
(CompareGuid (&FlowControl->Guid, &gEfiUartDevicePathGuid))
);
}
/**
Check the device path node whether it contains Flow Control node or not.
@param[in] DevicePath The device path to be checked.
@retval TRUE It contains the Flow Control node.
@retval FALSE It doesn't.
**/
BOOLEAN
ContainsFlowControl (
IN EFI_DEVICE_PATH_PROTOCOL *DevicePath
)
{
while (!IsDevicePathEnd (DevicePath)) {
if (IsUartFlowControlNode ((UART_FLOW_CONTROL_DEVICE_PATH *) DevicePath)) {
return TRUE;
}
DevicePath = NextDevicePathNode (DevicePath);
}
return FALSE;
}
/**
The user Entry Point for module IsaSerial. The user code starts with this function.
@param[in] ImageHandle The firmware allocated handle for the EFI image.
@param[in] SystemTable A pointer to the EFI System Table.
@retval EFI_SUCCESS The entry point is executed successfully.
@retval other Some error occurs when executing this entry point.
**/
EFI_STATUS
EFIAPI
InitializeIsaSerial (
IN EFI_HANDLE ImageHandle,
IN EFI_SYSTEM_TABLE *SystemTable
)
{
EFI_STATUS Status;
//
// Install driver model protocol(s).
//
Status = EfiLibInstallDriverBindingComponentName2 (
ImageHandle,
SystemTable,
&gSerialControllerDriver,
ImageHandle,
&gIsaSerialComponentName,
&gIsaSerialComponentName2
);
ASSERT_EFI_ERROR (Status);
//
// Initialize UART default setting in gSerialDevTempate
//
gSerialDevTempate.SerialMode.BaudRate = PcdGet64 (PcdUartDefaultBaudRate);
gSerialDevTempate.SerialMode.DataBits = PcdGet8 (PcdUartDefaultDataBits);
gSerialDevTempate.SerialMode.Parity = PcdGet8 (PcdUartDefaultParity);
gSerialDevTempate.SerialMode.StopBits = PcdGet8 (PcdUartDefaultStopBits);
gSerialDevTempate.UartDevicePath.BaudRate = PcdGet64 (PcdUartDefaultBaudRate);
gSerialDevTempate.UartDevicePath.DataBits = PcdGet8 (PcdUartDefaultDataBits);
gSerialDevTempate.UartDevicePath.Parity = PcdGet8 (PcdUartDefaultParity);
gSerialDevTempate.UartDevicePath.StopBits = PcdGet8 (PcdUartDefaultStopBits);
return Status;
}
/**
Check to see if this driver supports the given controller
@param This A pointer to the EFI_DRIVER_BINDING_PROTOCOL instance.
@param Controller The handle of the controller to test.
@param RemainingDevicePath A pointer to the remaining portion of a device path.
@return EFI_SUCCESS This driver can support the given controller
**/
EFI_STATUS
EFIAPI
SerialControllerDriverSupported (
IN EFI_DRIVER_BINDING_PROTOCOL *This,
IN EFI_HANDLE Controller,
IN EFI_DEVICE_PATH_PROTOCOL *RemainingDevicePath
)
{
EFI_STATUS Status;
EFI_DEVICE_PATH_PROTOCOL *ParentDevicePath;
EFI_ISA_IO_PROTOCOL *IsaIo;
UART_DEVICE_PATH *UartNode;
EFI_DEVICE_PATH_PROTOCOL *DevicePath;
UART_FLOW_CONTROL_DEVICE_PATH *FlowControlNode;
EFI_OPEN_PROTOCOL_INFORMATION_ENTRY *OpenInfoBuffer;
UINTN EntryCount;
UINTN Index;
BOOLEAN HasFlowControl;
//
// Check RemainingDevicePath validation
//
if (RemainingDevicePath != NULL) {
//
// Check if RemainingDevicePath is the End of Device Path Node,
// if yes, go on checking other conditions
//
if (!IsDevicePathEnd (RemainingDevicePath)) {
//
// If RemainingDevicePath isn't the End of Device Path Node,
// check its validation
//
Status = EFI_UNSUPPORTED;
UartNode = (UART_DEVICE_PATH *) RemainingDevicePath;
if (UartNode->Header.Type != MESSAGING_DEVICE_PATH ||
UartNode->Header.SubType != MSG_UART_DP ||
sizeof (UART_DEVICE_PATH) != DevicePathNodeLength ((EFI_DEVICE_PATH_PROTOCOL *) UartNode)
) {
goto Error;
}
if (UartNode->BaudRate > SERIAL_PORT_MAX_BAUD_RATE) {
goto Error;
}
if (UartNode->Parity < NoParity || UartNode->Parity > SpaceParity) {
goto Error;
}
if (UartNode->DataBits < 5 || UartNode->DataBits > 8) {
goto Error;
}
if (UartNode->StopBits < OneStopBit || UartNode->StopBits > TwoStopBits) {
goto Error;
}
if ((UartNode->DataBits == 5) && (UartNode->StopBits == TwoStopBits)) {
goto Error;
}
if ((UartNode->DataBits >= 6) && (UartNode->DataBits <= 8) && (UartNode->StopBits == OneFiveStopBits)) {
goto Error;
}
FlowControlNode = (UART_FLOW_CONTROL_DEVICE_PATH *) NextDevicePathNode (UartNode);
if (IsUartFlowControlNode (FlowControlNode)) {
//
// If the second node is Flow Control Node,
// return error when it request other than hardware flow control.
//
if ((ReadUnaligned32 (&FlowControlNode->FlowControlMap) & ~UART_FLOW_CONTROL_HARDWARE) != 0) {
goto Error;
}
}
}
}
//
// Open the IO Abstraction(s) needed to perform the supported test
//
Status = gBS->OpenProtocol (
Controller,
&gEfiIsaIoProtocolGuid,
(VOID **) &IsaIo,
This->DriverBindingHandle,
Controller,
EFI_OPEN_PROTOCOL_BY_DRIVER
);
if (Status == EFI_ALREADY_STARTED) {
if (RemainingDevicePath == NULL || IsDevicePathEnd (RemainingDevicePath)) {
//
// If RemainingDevicePath is NULL or is the End of Device Path Node
//
return EFI_SUCCESS;
}
//
// When the driver has produced device path with flow control node but RemainingDevicePath only contains UART node,
// return unsupported, and vice versa.
//
Status = gBS->OpenProtocolInformation (
Controller,
&gEfiIsaIoProtocolGuid,
&OpenInfoBuffer,
&EntryCount
);
if (EFI_ERROR (Status)) {
return Status;
}
for (Index = 0; Index < EntryCount; Index++) {
if ((OpenInfoBuffer[Index].Attributes & EFI_OPEN_PROTOCOL_BY_CHILD_CONTROLLER) != 0) {
Status = gBS->OpenProtocol (
OpenInfoBuffer[Index].ControllerHandle,
&gEfiDevicePathProtocolGuid,
(VOID **) &DevicePath,
This->DriverBindingHandle,
Controller,
EFI_OPEN_PROTOCOL_GET_PROTOCOL
);
if (!EFI_ERROR (Status)) {
HasFlowControl = ContainsFlowControl (RemainingDevicePath);
if (HasFlowControl ^ ContainsFlowControl (DevicePath)) {
Status = EFI_UNSUPPORTED;
}
}
break;
}
}
FreePool (OpenInfoBuffer);
return Status;
}
if (EFI_ERROR (Status)) {
return Status;
}
//
// Close the I/O Abstraction(s) used to perform the supported test
//
gBS->CloseProtocol (
Controller,
&gEfiIsaIoProtocolGuid,
This->DriverBindingHandle,
Controller
);
//
// Open the EFI Device Path protocol needed to perform the supported test
//
Status = gBS->OpenProtocol (
Controller,
&gEfiDevicePathProtocolGuid,
(VOID **) &ParentDevicePath,
This->DriverBindingHandle,
Controller,
EFI_OPEN_PROTOCOL_BY_DRIVER
);
if (Status == EFI_ALREADY_STARTED) {
return EFI_SUCCESS;
}
if (EFI_ERROR (Status)) {
return Status;
}
//
// Use the ISA I/O Protocol to see if Controller is standard ISA UART that
// can be managed by this driver.
//
Status = EFI_SUCCESS;
if (IsaIo->ResourceList->Device.HID != EISA_PNP_ID (0x501)) {
Status = EFI_UNSUPPORTED;
goto Error;
}
Error:
//
// Close protocol, don't use device path protocol in the Support() function
//
gBS->CloseProtocol (
Controller,
&gEfiDevicePathProtocolGuid,
This->DriverBindingHandle,
Controller
);
return Status;
}
/**
Start to management the controller passed in
@param This A pointer to the EFI_DRIVER_BINDING_PROTOCOL instance.
@param Controller The handle of the controller to test.
@param RemainingDevicePath A pointer to the remaining portion of a device path.
@return EFI_SUCCESS Driver is started successfully
**/
EFI_STATUS
EFIAPI
SerialControllerDriverStart (
IN EFI_DRIVER_BINDING_PROTOCOL *This,
IN EFI_HANDLE Controller,
IN EFI_DEVICE_PATH_PROTOCOL *RemainingDevicePath
)
{
EFI_STATUS Status;
EFI_ISA_IO_PROTOCOL *IsaIo;
SERIAL_DEV *SerialDevice;
UINTN Index;
EFI_DEVICE_PATH_PROTOCOL *ParentDevicePath;
EFI_OPEN_PROTOCOL_INFORMATION_ENTRY *OpenInfoBuffer;
UINTN EntryCount;
EFI_SERIAL_IO_PROTOCOL *SerialIo;
UART_DEVICE_PATH *Uart;
UINT32 FlowControlMap;
UART_FLOW_CONTROL_DEVICE_PATH *FlowControl;
EFI_DEVICE_PATH_PROTOCOL *TempDevicePath;
UINT32 Control;
SerialDevice = NULL;
//
// Get the Parent Device Path
//
Status = gBS->OpenProtocol (
Controller,
&gEfiDevicePathProtocolGuid,
(VOID **) &ParentDevicePath,
This->DriverBindingHandle,
Controller,
EFI_OPEN_PROTOCOL_BY_DRIVER
);
if (EFI_ERROR (Status) && Status != EFI_ALREADY_STARTED) {
return Status;
}
//
// Report status code enable the serial
//
REPORT_STATUS_CODE_WITH_DEVICE_PATH (
EFI_PROGRESS_CODE,
EFI_P_PC_ENABLE | EFI_PERIPHERAL_SERIAL_PORT,
ParentDevicePath
);
//
// Grab the IO abstraction we need to get any work done
//
Status = gBS->OpenProtocol (
Controller,
&gEfiIsaIoProtocolGuid,
(VOID **) &IsaIo,
This->DriverBindingHandle,
Controller,
EFI_OPEN_PROTOCOL_BY_DRIVER
);
if (EFI_ERROR (Status) && Status != EFI_ALREADY_STARTED) {
goto Error;
}
if (Status == EFI_ALREADY_STARTED) {
if (RemainingDevicePath == NULL || IsDevicePathEnd (RemainingDevicePath)) {
//
// If RemainingDevicePath is NULL or is the End of Device Path Node
//
return EFI_SUCCESS;
}
//
// Make sure a child handle does not already exist. This driver can only
// produce one child per serial port.
//
Status = gBS->OpenProtocolInformation (
Controller,
&gEfiIsaIoProtocolGuid,
&OpenInfoBuffer,
&EntryCount
);
if (EFI_ERROR (Status)) {
return Status;
}
Status = EFI_ALREADY_STARTED;
for (Index = 0; Index < EntryCount; Index++) {
if ((OpenInfoBuffer[Index].Attributes & EFI_OPEN_PROTOCOL_BY_CHILD_CONTROLLER) != 0) {
Status = gBS->OpenProtocol (
OpenInfoBuffer[Index].ControllerHandle,
&gEfiSerialIoProtocolGuid,
(VOID **) &SerialIo,
This->DriverBindingHandle,
Controller,
EFI_OPEN_PROTOCOL_GET_PROTOCOL
);
if (!EFI_ERROR (Status)) {
Uart = (UART_DEVICE_PATH *) RemainingDevicePath;
Status = SerialIo->SetAttributes (
SerialIo,
Uart->BaudRate,
SerialIo->Mode->ReceiveFifoDepth,
SerialIo->Mode->Timeout,
(EFI_PARITY_TYPE) Uart->Parity,
Uart->DataBits,
(EFI_STOP_BITS_TYPE) Uart->StopBits
);
FlowControl = (UART_FLOW_CONTROL_DEVICE_PATH *) NextDevicePathNode (Uart);
if (!EFI_ERROR (Status) && IsUartFlowControlNode (FlowControl)) {
Status = SerialIo->GetControl (SerialIo, &Control);
if (!EFI_ERROR (Status)) {
if (ReadUnaligned32 (&FlowControl->FlowControlMap) == UART_FLOW_CONTROL_HARDWARE) {
Control |= EFI_SERIAL_HARDWARE_FLOW_CONTROL_ENABLE;
} else {
Control &= ~EFI_SERIAL_HARDWARE_FLOW_CONTROL_ENABLE;
}
//
// Clear the bits that are not allowed to pass to SetControl
//
Control &= (EFI_SERIAL_REQUEST_TO_SEND | EFI_SERIAL_DATA_TERMINAL_READY |
EFI_SERIAL_HARDWARE_LOOPBACK_ENABLE | EFI_SERIAL_SOFTWARE_LOOPBACK_ENABLE |
EFI_SERIAL_HARDWARE_FLOW_CONTROL_ENABLE);
Status = SerialIo->SetControl (SerialIo, Control);
}
}
}
break;
}
}
FreePool (OpenInfoBuffer);
return Status;
}
if (RemainingDevicePath != NULL) {
if (IsDevicePathEnd (RemainingDevicePath)) {
//
// If RemainingDevicePath is the End of Device Path Node,
// skip enumerate any device and return EFI_SUCESSS
//
return EFI_SUCCESS;
}
}
//
// Initialize the serial device instance
//
SerialDevice = AllocateCopyPool (sizeof (SERIAL_DEV), &gSerialDevTempate);
if (SerialDevice == NULL) {
Status = EFI_OUT_OF_RESOURCES;
goto Error;
}
SerialDevice->SerialIo.Mode = &(SerialDevice->SerialMode);
SerialDevice->IsaIo = IsaIo;
SerialDevice->ParentDevicePath = ParentDevicePath;
FlowControl = NULL;
FlowControlMap = 0;
//
// Check if RemainingDevicePath is NULL,
// if yes, use the values from the gSerialDevTempate as no remaining device path was
// passed in.
//
if (RemainingDevicePath != NULL) {
//
// If RemainingDevicePath isn't NULL,
// match the configuration of the RemainingDevicePath. IsHandleSupported()
// already checked to make sure the RemainingDevicePath contains settings
// that we can support.
//
CopyMem (&SerialDevice->UartDevicePath, RemainingDevicePath, sizeof (UART_DEVICE_PATH));
FlowControl = (UART_FLOW_CONTROL_DEVICE_PATH *) NextDevicePathNode (RemainingDevicePath);
if (IsUartFlowControlNode (FlowControl)) {
FlowControlMap = ReadUnaligned32 (&FlowControl->FlowControlMap);
} else {
FlowControl = NULL;
}
}
AddName (SerialDevice, IsaIo);
for (Index = 0; SerialDevice->IsaIo->ResourceList->ResourceItem[Index].Type != EfiIsaAcpiResourceEndOfList; Index++) {
if (SerialDevice->IsaIo->ResourceList->ResourceItem[Index].Type == EfiIsaAcpiResourceIo) {
SerialDevice->BaseAddress = (UINT16) SerialDevice->IsaIo->ResourceList->ResourceItem[Index].StartRange;
}
}
SerialDevice->HardwareFlowControl = (BOOLEAN) (FlowControlMap == UART_FLOW_CONTROL_HARDWARE);
//
// Report status code the serial present
//
REPORT_STATUS_CODE_WITH_DEVICE_PATH (
EFI_PROGRESS_CODE,
EFI_P_PC_PRESENCE_DETECT | EFI_PERIPHERAL_SERIAL_PORT,
ParentDevicePath
);
if (!IsaSerialPortPresent (SerialDevice)) {
Status = EFI_DEVICE_ERROR;
REPORT_STATUS_CODE_WITH_DEVICE_PATH (
EFI_ERROR_CODE,
EFI_P_EC_NOT_DETECTED | EFI_PERIPHERAL_SERIAL_PORT,
ParentDevicePath
);
goto Error;
}
//
// Build the device path by appending the UART node to the ParentDevicePath.
// The Uart setings are zero here, since SetAttribute() will update them to match
// the default setings.
//
SerialDevice->DevicePath = AppendDevicePathNode (
ParentDevicePath,
(EFI_DEVICE_PATH_PROTOCOL *) &SerialDevice->UartDevicePath
);
//
// Only produce the Flow Control node when remaining device path has it
//
if (FlowControl != NULL) {
TempDevicePath = SerialDevice->DevicePath;
if (TempDevicePath != NULL) {
SerialDevice->DevicePath = AppendDevicePathNode (
TempDevicePath,
(EFI_DEVICE_PATH_PROTOCOL *) FlowControl
);
FreePool (TempDevicePath);
}
}
if (SerialDevice->DevicePath == NULL) {
Status = EFI_OUT_OF_RESOURCES;
goto Error;
}
//
// Fill in Serial I/O Mode structure based on either the RemainingDevicePath or defaults.
//
SerialDevice->SerialMode.BaudRate = SerialDevice->UartDevicePath.BaudRate;
SerialDevice->SerialMode.DataBits = SerialDevice->UartDevicePath.DataBits;
SerialDevice->SerialMode.Parity = SerialDevice->UartDevicePath.Parity;
SerialDevice->SerialMode.StopBits = SerialDevice->UartDevicePath.StopBits;
//
// Issue a reset to initialize the COM port
//
Status = SerialDevice->SerialIo.Reset (&SerialDevice->SerialIo);
if (EFI_ERROR (Status)) {
REPORT_STATUS_CODE_WITH_DEVICE_PATH (
EFI_ERROR_CODE,
EFI_P_EC_CONTROLLER_ERROR | EFI_PERIPHERAL_SERIAL_PORT,
ParentDevicePath
);
goto Error;
}
//
// Install protocol interfaces for the serial device.
//
Status = gBS->InstallMultipleProtocolInterfaces (
&SerialDevice->Handle,
&gEfiDevicePathProtocolGuid,
SerialDevice->DevicePath,
&gEfiSerialIoProtocolGuid,
&SerialDevice->SerialIo,
NULL
);
if (EFI_ERROR (Status)) {
goto Error;
}
//
// Open For Child Device
//
Status = gBS->OpenProtocol (
Controller,
&gEfiIsaIoProtocolGuid,
(VOID **) &IsaIo,
This->DriverBindingHandle,
SerialDevice->Handle,
EFI_OPEN_PROTOCOL_BY_CHILD_CONTROLLER
);
Error:
if (EFI_ERROR (Status)) {
gBS->CloseProtocol (
Controller,
&gEfiDevicePathProtocolGuid,
This->DriverBindingHandle,
Controller
);
gBS->CloseProtocol (
Controller,
&gEfiIsaIoProtocolGuid,
This->DriverBindingHandle,
Controller
);
if (SerialDevice != NULL) {
if (SerialDevice->DevicePath != NULL) {
gBS->FreePool (SerialDevice->DevicePath);
}
FreeUnicodeStringTable (SerialDevice->ControllerNameTable);
gBS->FreePool (SerialDevice);
}
}
return Status;
}
/**
Disconnect this driver with the controller, uninstall related protocol instance
@param This A pointer to the EFI_DRIVER_BINDING_PROTOCOL instance.
@param Controller The handle of the controller to test.
@param NumberOfChildren Number of child device.
@param ChildHandleBuffer A pointer to the remaining portion of a device path.
@retval EFI_SUCCESS Operation successfully
@retval EFI_DEVICE_ERROR Cannot stop the driver successfully
**/
EFI_STATUS
EFIAPI
SerialControllerDriverStop (
IN EFI_DRIVER_BINDING_PROTOCOL *This,
IN EFI_HANDLE Controller,
IN UINTN NumberOfChildren,
IN EFI_HANDLE *ChildHandleBuffer
)
{
EFI_STATUS Status;
UINTN Index;
BOOLEAN AllChildrenStopped;
EFI_SERIAL_IO_PROTOCOL *SerialIo;
SERIAL_DEV *SerialDevice;
EFI_ISA_IO_PROTOCOL *IsaIo;
EFI_DEVICE_PATH_PROTOCOL *DevicePath;
Status = gBS->HandleProtocol (
Controller,
&gEfiDevicePathProtocolGuid,
(VOID **) &DevicePath
);
//
// Report the status code disable the serial
//
REPORT_STATUS_CODE_WITH_DEVICE_PATH (
EFI_PROGRESS_CODE,
EFI_P_PC_DISABLE | EFI_PERIPHERAL_SERIAL_PORT,
DevicePath
);
//
// Complete all outstanding transactions to Controller.
// Don't allow any new transaction to Controller to be started.
//
if (NumberOfChildren == 0) {
//
// Close the bus driver
//
Status = gBS->CloseProtocol (
Controller,
&gEfiIsaIoProtocolGuid,
This->DriverBindingHandle,
Controller
);
Status = gBS->CloseProtocol (
Controller,
&gEfiDevicePathProtocolGuid,
This->DriverBindingHandle,
Controller
);
return Status;
}
AllChildrenStopped = TRUE;
for (Index = 0; Index < NumberOfChildren; Index++) {
Status = gBS->OpenProtocol (
ChildHandleBuffer[Index],
&gEfiSerialIoProtocolGuid,
(VOID **) &SerialIo,
This->DriverBindingHandle,
Controller,
EFI_OPEN_PROTOCOL_GET_PROTOCOL
);
if (!EFI_ERROR (Status)) {
SerialDevice = SERIAL_DEV_FROM_THIS (SerialIo);
Status = gBS->CloseProtocol (
Controller,
&gEfiIsaIoProtocolGuid,
This->DriverBindingHandle,
ChildHandleBuffer[Index]
);
Status = gBS->UninstallMultipleProtocolInterfaces (
ChildHandleBuffer[Index],
&gEfiDevicePathProtocolGuid,
SerialDevice->DevicePath,
&gEfiSerialIoProtocolGuid,
&SerialDevice->SerialIo,
NULL
);
if (EFI_ERROR (Status)) {
gBS->OpenProtocol (
Controller,
&gEfiIsaIoProtocolGuid,
(VOID **) &IsaIo,
This->DriverBindingHandle,
ChildHandleBuffer[Index],
EFI_OPEN_PROTOCOL_BY_CHILD_CONTROLLER
);
} else {
if (SerialDevice->DevicePath != NULL) {
gBS->FreePool (SerialDevice->DevicePath);
}
FreeUnicodeStringTable (SerialDevice->ControllerNameTable);
gBS->FreePool (SerialDevice);
}
}
if (EFI_ERROR (Status)) {
AllChildrenStopped = FALSE;
}
}
if (!AllChildrenStopped) {
return EFI_DEVICE_ERROR;
}
return EFI_SUCCESS;
}
/**
Detect whether specific FIFO is full or not.
@param Fifo A pointer to the Data Structure SERIAL_DEV_FIFO
@return whether specific FIFO is full or not
**/
BOOLEAN
IsaSerialFifoFull (
IN SERIAL_DEV_FIFO *Fifo
)
{
if (Fifo->Surplus == 0) {
return TRUE;
}
return FALSE;
}
/**
Detect whether specific FIFO is empty or not.
@param Fifo A pointer to the Data Structure SERIAL_DEV_FIFO
@return whether specific FIFO is empty or not
**/
BOOLEAN
IsaSerialFifoEmpty (
IN SERIAL_DEV_FIFO *Fifo
)
{
if (Fifo->Surplus == SERIAL_MAX_BUFFER_SIZE) {
return TRUE;
}
return FALSE;
}
/**
Add data to specific FIFO.
@param Fifo A pointer to the Data Structure SERIAL_DEV_FIFO
@param Data the data added to FIFO
@retval EFI_SUCCESS Add data to specific FIFO successfully
@retval EFI_OUT_OF_RESOURCE Failed to add data because FIFO is already full
**/
EFI_STATUS
IsaSerialFifoAdd (
IN SERIAL_DEV_FIFO *Fifo,
IN UINT8 Data
)
{
//
// if FIFO full can not add data
//
if (IsaSerialFifoFull (Fifo)) {
return EFI_OUT_OF_RESOURCES;
}
//
// FIFO is not full can add data
//
Fifo->Data[Fifo->Last] = Data;
Fifo->Surplus--;
Fifo->Last++;
if (Fifo->Last == SERIAL_MAX_BUFFER_SIZE) {
Fifo->Last = 0;
}
return EFI_SUCCESS;
}
/**
Remove data from specific FIFO.
@param Fifo A pointer to the Data Structure SERIAL_DEV_FIFO
@param Data the data removed from FIFO
@retval EFI_SUCCESS Remove data from specific FIFO successfully
@retval EFI_OUT_OF_RESOURCE Failed to remove data because FIFO is empty
**/
EFI_STATUS
IsaSerialFifoRemove (
IN SERIAL_DEV_FIFO *Fifo,
OUT UINT8 *Data
)
{
//
// if FIFO is empty, no data can remove
//
if (IsaSerialFifoEmpty (Fifo)) {
return EFI_OUT_OF_RESOURCES;
}
//
// FIFO is not empty, can remove data
//
*Data = Fifo->Data[Fifo->First];
Fifo->Surplus++;
Fifo->First++;
if (Fifo->First == SERIAL_MAX_BUFFER_SIZE) {
Fifo->First = 0;
}
return EFI_SUCCESS;
}
/**
Reads and writes all avaliable data.
@param SerialDevice The device to flush
@retval EFI_SUCCESS Data was read/written successfully.
@retval EFI_OUT_OF_RESOURCE Failed because software receive FIFO is full. Note, when
this happens, pending writes are not done.
**/
EFI_STATUS
IsaSerialReceiveTransmit (
IN SERIAL_DEV *SerialDevice
)
{
SERIAL_PORT_LSR Lsr;
UINT8 Data;
BOOLEAN ReceiveFifoFull;
SERIAL_PORT_MSR Msr;
SERIAL_PORT_MCR Mcr;
UINTN TimeOut;
Data = 0;
//
// Begin the read or write
//
if (SerialDevice->SoftwareLoopbackEnable) {
do {
ReceiveFifoFull = IsaSerialFifoFull (&SerialDevice->Receive);
if (!IsaSerialFifoEmpty (&SerialDevice->Transmit)) {
IsaSerialFifoRemove (&SerialDevice->Transmit, &Data);
if (ReceiveFifoFull) {
return EFI_OUT_OF_RESOURCES;
}
IsaSerialFifoAdd (&SerialDevice->Receive, Data);
}
} while (!IsaSerialFifoEmpty (&SerialDevice->Transmit));
} else {
ReceiveFifoFull = IsaSerialFifoFull (&SerialDevice->Receive);
//
// For full handshake flow control, tell the peer to send data
// if receive buffer is available.
//
if (SerialDevice->HardwareFlowControl &&
!FeaturePcdGet(PcdIsaBusSerialUseHalfHandshake)&&
!ReceiveFifoFull
) {
Mcr.Data = READ_MCR (SerialDevice->IsaIo, SerialDevice->BaseAddress);
Mcr.Bits.Rts = 1;
WRITE_MCR (SerialDevice->IsaIo, SerialDevice->BaseAddress, Mcr.Data);
}
do {
Lsr.Data = READ_LSR (SerialDevice->IsaIo, SerialDevice->BaseAddress);
//
// Flush incomming data to prevent a an overrun during a long write
//
if ((Lsr.Bits.Dr == 1) && !ReceiveFifoFull) {
ReceiveFifoFull = IsaSerialFifoFull (&SerialDevice->Receive);
if (!ReceiveFifoFull) {
if (Lsr.Bits.FIFOe == 1 || Lsr.Bits.Oe == 1 || Lsr.Bits.Pe == 1 || Lsr.Bits.Fe == 1 || Lsr.Bits.Bi == 1) {
REPORT_STATUS_CODE_WITH_DEVICE_PATH (
EFI_ERROR_CODE,
EFI_P_EC_INPUT_ERROR | EFI_PERIPHERAL_SERIAL_PORT,
SerialDevice->DevicePath
);
if (Lsr.Bits.FIFOe == 1 || Lsr.Bits.Pe == 1|| Lsr.Bits.Fe == 1 || Lsr.Bits.Bi == 1) {
Data = READ_RBR (SerialDevice->IsaIo, SerialDevice->BaseAddress);
continue;
}
}
Data = READ_RBR (SerialDevice->IsaIo, SerialDevice->BaseAddress);
IsaSerialFifoAdd (&SerialDevice->Receive, Data);
//
// For full handshake flow control, if receive buffer full
// tell the peer to stop sending data.
//
if (SerialDevice->HardwareFlowControl &&
!FeaturePcdGet(PcdIsaBusSerialUseHalfHandshake) &&
IsaSerialFifoFull (&SerialDevice->Receive)
) {
Mcr.Data = READ_MCR (SerialDevice->IsaIo, SerialDevice->BaseAddress);
Mcr.Bits.Rts = 0;
WRITE_MCR (SerialDevice->IsaIo, SerialDevice->BaseAddress, Mcr.Data);
}
continue;
} else {
REPORT_STATUS_CODE_WITH_DEVICE_PATH (
EFI_PROGRESS_CODE,
EFI_P_SERIAL_PORT_PC_CLEAR_BUFFER | EFI_PERIPHERAL_SERIAL_PORT,
SerialDevice->DevicePath
);
}
}
//
// Do the write
//
if (Lsr.Bits.Thre == 1 && !IsaSerialFifoEmpty (&SerialDevice->Transmit)) {
//
// Make sure the transmit data will not be missed
//
if (SerialDevice->HardwareFlowControl) {
//
// For half handshake flow control assert RTS before sending.
//
if (FeaturePcdGet(PcdIsaBusSerialUseHalfHandshake)) {
Mcr.Data = READ_MCR (SerialDevice->IsaIo, SerialDevice->BaseAddress);
Mcr.Bits.Rts= 0;
WRITE_MCR (SerialDevice->IsaIo, SerialDevice->BaseAddress, Mcr.Data);
}
//
// Wait for CTS
//
TimeOut = 0;
Msr.Data = READ_MSR (SerialDevice->IsaIo, SerialDevice->BaseAddress);
while ((Msr.Bits.Dcd == 1) && ((Msr.Bits.Cts == 0) ^ FeaturePcdGet(PcdIsaBusSerialUseHalfHandshake))) {
gBS->Stall (TIMEOUT_STALL_INTERVAL);
TimeOut++;
if (TimeOut > 5) {
break;
}
Msr.Data = READ_MSR (SerialDevice->IsaIo, SerialDevice->BaseAddress);
}
if ((Msr.Bits.Dcd == 0) || ((Msr.Bits.Cts == 1) ^ FeaturePcdGet(PcdIsaBusSerialUseHalfHandshake))) {
IsaSerialFifoRemove (&SerialDevice->Transmit, &Data);
WRITE_THR (SerialDevice->IsaIo, SerialDevice->BaseAddress, Data);
}
//
// For half handshake flow control, tell DCE we are done.
//
if (FeaturePcdGet(PcdIsaBusSerialUseHalfHandshake)) {
Mcr.Data = READ_MCR (SerialDevice->IsaIo, SerialDevice->BaseAddress);
Mcr.Bits.Rts = 1;
WRITE_MCR (SerialDevice->IsaIo, SerialDevice->BaseAddress, Mcr.Data);
}
} else {
IsaSerialFifoRemove (&SerialDevice->Transmit, &Data);
WRITE_THR (SerialDevice->IsaIo, SerialDevice->BaseAddress, Data);
}
}
} while (Lsr.Bits.Thre == 1 && !IsaSerialFifoEmpty (&SerialDevice->Transmit));
}
return EFI_SUCCESS;
}
//
// Interface Functions
//
/**
Reset serial device.
@param This Pointer to EFI_SERIAL_IO_PROTOCOL
@retval EFI_SUCCESS Reset successfully
@retval EFI_DEVICE_ERROR Failed to reset
**/
EFI_STATUS
EFIAPI
IsaSerialReset (
IN EFI_SERIAL_IO_PROTOCOL *This
)
{
EFI_STATUS Status;
SERIAL_DEV *SerialDevice;
SERIAL_PORT_LCR Lcr;
SERIAL_PORT_IER Ier;
SERIAL_PORT_MCR Mcr;
SERIAL_PORT_FCR Fcr;
EFI_TPL Tpl;
UINT32 Control;
SerialDevice = SERIAL_DEV_FROM_THIS (This);
//
// Report the status code reset the serial
//
REPORT_STATUS_CODE_WITH_DEVICE_PATH (
EFI_PROGRESS_CODE,
EFI_P_PC_RESET | EFI_PERIPHERAL_SERIAL_PORT,
SerialDevice->DevicePath
);
Tpl = gBS->RaiseTPL (TPL_NOTIFY);
//
// Make sure DLAB is 0.
//
Lcr.Data = READ_LCR (SerialDevice->IsaIo, SerialDevice->BaseAddress);
Lcr.Bits.DLab = 0;
WRITE_LCR (SerialDevice->IsaIo, SerialDevice->BaseAddress, Lcr.Data);
//
// Turn off all interrupts
//
Ier.Data = READ_IER (SerialDevice->IsaIo, SerialDevice->BaseAddress);
Ier.Bits.Ravie = 0;
Ier.Bits.Theie = 0;
Ier.Bits.Rie = 0;
Ier.Bits.Mie = 0;
WRITE_IER (SerialDevice->IsaIo, SerialDevice->BaseAddress, Ier.Data);
//
// Disable the FIFO.
//
Fcr.Bits.TrFIFOE = 0;
WRITE_FCR (SerialDevice->IsaIo, SerialDevice->BaseAddress, Fcr.Data);
//
// Turn off loopback and disable device interrupt.
//
Mcr.Data = READ_MCR (SerialDevice->IsaIo, SerialDevice->BaseAddress);
Mcr.Bits.Out1 = 0;
Mcr.Bits.Out2 = 0;
Mcr.Bits.Lme = 0;
WRITE_MCR (SerialDevice->IsaIo, SerialDevice->BaseAddress, Mcr.Data);
//
// Clear the scratch pad register
//
WRITE_SCR (SerialDevice->IsaIo, SerialDevice->BaseAddress, 0);
//
// Go set the current attributes
//
Status = This->SetAttributes (
This,
This->Mode->BaudRate,
This->Mode->ReceiveFifoDepth,
This->Mode->Timeout,
(EFI_PARITY_TYPE) This->Mode->Parity,
(UINT8) This->Mode->DataBits,
(EFI_STOP_BITS_TYPE) This->Mode->StopBits
);
if (EFI_ERROR (Status)) {
gBS->RestoreTPL (Tpl);
return EFI_DEVICE_ERROR;
}
//
// Go set the current control bits
//
Control = 0;
if (SerialDevice->HardwareFlowControl) {
Control |= EFI_SERIAL_HARDWARE_FLOW_CONTROL_ENABLE;
}
if (SerialDevice->SoftwareLoopbackEnable) {
Control |= EFI_SERIAL_SOFTWARE_LOOPBACK_ENABLE;
}
Status = This->SetControl (
This,
Control
);
if (EFI_ERROR (Status)) {
gBS->RestoreTPL (Tpl);
return EFI_DEVICE_ERROR;
}
//
// for 16550A enable FIFO, 16550 disable FIFO
//
Fcr.Bits.TrFIFOE = 1;
Fcr.Bits.ResetRF = 1;
Fcr.Bits.ResetTF = 1;
WRITE_FCR (SerialDevice->IsaIo, SerialDevice->BaseAddress, Fcr.Data);
//
// Reset the software FIFO
//
SerialDevice->Receive.First = 0;
SerialDevice->Receive.Last = 0;
SerialDevice->Receive.Surplus = SERIAL_MAX_BUFFER_SIZE;
SerialDevice->Transmit.First = 0;
SerialDevice->Transmit.Last = 0;
SerialDevice->Transmit.Surplus = SERIAL_MAX_BUFFER_SIZE;
gBS->RestoreTPL (Tpl);
//
// Device reset is complete
//
return EFI_SUCCESS;
}
/**
Set new attributes to a serial device.
@param This Pointer to EFI_SERIAL_IO_PROTOCOL
@param BaudRate The baudrate of the serial device
@param ReceiveFifoDepth The depth of receive FIFO buffer
@param Timeout The request timeout for a single char
@param Parity The type of parity used in serial device
@param DataBits Number of databits used in serial device
@param StopBits Number of stopbits used in serial device
@retval EFI_SUCCESS The new attributes were set
@retval EFI_INVALID_PARAMETERS One or more attributes have an unsupported value
@retval EFI_UNSUPPORTED Data Bits can not set to 5 or 6
@retval EFI_DEVICE_ERROR The serial device is not functioning correctly (no return)
**/
EFI_STATUS
EFIAPI
IsaSerialSetAttributes (
IN EFI_SERIAL_IO_PROTOCOL *This,
IN UINT64 BaudRate,
IN UINT32 ReceiveFifoDepth,
IN UINT32 Timeout,
IN EFI_PARITY_TYPE Parity,
IN UINT8 DataBits,
IN EFI_STOP_BITS_TYPE StopBits
)
{
EFI_STATUS Status;
SERIAL_DEV *SerialDevice;
UINT32 Divisor;
UINT32 Remained;
SERIAL_PORT_LCR Lcr;
UART_DEVICE_PATH *Uart;
EFI_TPL Tpl;
SerialDevice = SERIAL_DEV_FROM_THIS (This);
//
// Check for default settings and fill in actual values.
//
if (BaudRate == 0) {
BaudRate = PcdGet64 (PcdUartDefaultBaudRate);
}
if (ReceiveFifoDepth == 0) {
ReceiveFifoDepth = SERIAL_PORT_DEFAULT_RECEIVE_FIFO_DEPTH;
}
if (Timeout == 0) {
Timeout = SERIAL_PORT_DEFAULT_TIMEOUT;
}
if (Parity == DefaultParity) {
Parity = (EFI_PARITY_TYPE)PcdGet8 (PcdUartDefaultParity);
}
if (DataBits == 0) {
DataBits = PcdGet8 (PcdUartDefaultDataBits);
}
if (StopBits == DefaultStopBits) {
StopBits = (EFI_STOP_BITS_TYPE) PcdGet8 (PcdUartDefaultStopBits);
}
//
// 5 and 6 data bits can not be verified on a 16550A UART
// Return EFI_INVALID_PARAMETER if an attempt is made to use these settings.
//
if ((DataBits == 5) || (DataBits == 6)) {
return EFI_INVALID_PARAMETER;
}
//
// Make sure all parameters are valid
//
if ((BaudRate > SERIAL_PORT_MAX_BAUD_RATE) || (BaudRate < SERIAL_PORT_MIN_BAUD_RATE)) {
return EFI_INVALID_PARAMETER;
}
//
// 50,75,110,134,150,300,600,1200,1800,2000,2400,3600,4800,7200,9600,19200,
// 38400,57600,115200
//
if (BaudRate < 75) {
BaudRate = 50;
} else if (BaudRate < 110) {
BaudRate = 75;
} else if (BaudRate < 134) {
BaudRate = 110;
} else if (BaudRate < 150) {
BaudRate = 134;
} else if (BaudRate < 300) {
BaudRate = 150;
} else if (BaudRate < 600) {
BaudRate = 300;
} else if (BaudRate < 1200) {
BaudRate = 600;
} else if (BaudRate < 1800) {
BaudRate = 1200;
} else if (BaudRate < 2000) {
BaudRate = 1800;
} else if (BaudRate < 2400) {
BaudRate = 2000;
} else if (BaudRate < 3600) {
BaudRate = 2400;
} else if (BaudRate < 4800) {
BaudRate = 3600;
} else if (BaudRate < 7200) {
BaudRate = 4800;
} else if (BaudRate < 9600) {
BaudRate = 7200;
} else if (BaudRate < 19200) {
BaudRate = 9600;
} else if (BaudRate < 38400) {
BaudRate = 19200;
} else if (BaudRate < 57600) {
BaudRate = 38400;
} else if (BaudRate < 115200) {
BaudRate = 57600;
} else if (BaudRate <= SERIAL_PORT_MAX_BAUD_RATE) {
BaudRate = 115200;
}
if ((ReceiveFifoDepth < 1) || (ReceiveFifoDepth > SERIAL_PORT_MAX_RECEIVE_FIFO_DEPTH)) {
return EFI_INVALID_PARAMETER;
}
if ((Timeout < SERIAL_PORT_MIN_TIMEOUT) || (Timeout > SERIAL_PORT_MAX_TIMEOUT)) {
return EFI_INVALID_PARAMETER;
}
if ((Parity < NoParity) || (Parity > SpaceParity)) {
return EFI_INVALID_PARAMETER;
}
if ((DataBits < 5) || (DataBits > 8)) {
return EFI_INVALID_PARAMETER;
}
if ((StopBits < OneStopBit) || (StopBits > TwoStopBits)) {
return EFI_INVALID_PARAMETER;
}
//
// for DataBits = 6,7,8, StopBits can not set OneFiveStopBits
//
if ((DataBits >= 6) && (DataBits <= 8) && (StopBits == OneFiveStopBits)) {
return EFI_INVALID_PARAMETER;
}
//
// Compute divisor use to program the baud rate using a round determination
//
Divisor = (UINT32) DivU64x32Remainder (
PcdGet32 (PcdSerialClockRate),
((UINT32) BaudRate * 16),
&Remained
);
if (Remained >= ((UINT32) BaudRate * 8)) {
Divisor += 1;
}
if ((Divisor == 0) || ((Divisor & 0xffff0000) != 0)) {
return EFI_INVALID_PARAMETER;
}
Tpl = gBS->RaiseTPL (TPL_NOTIFY);
//
// Compute the actual baud rate that the serial port will be programmed for.
//
BaudRate = PcdGet32 (PcdSerialClockRate) / Divisor / 16;
//
// Put serial port on Divisor Latch Mode
//
Lcr.Data = READ_LCR (SerialDevice->IsaIo, SerialDevice->BaseAddress);
Lcr.Bits.DLab = 1;
WRITE_LCR (SerialDevice->IsaIo, SerialDevice->BaseAddress, Lcr.Data);
//
// Write the divisor to the serial port
//
WRITE_DLL (SerialDevice->IsaIo, SerialDevice->BaseAddress, (UINT8) (Divisor & 0xff));
WRITE_DLM (SerialDevice->IsaIo, SerialDevice->BaseAddress, (UINT8) ((Divisor >> 8) & 0xff));
//
// Put serial port back in normal mode and set remaining attributes.
//
Lcr.Bits.DLab = 0;
switch (Parity) {
case NoParity:
Lcr.Bits.ParEn = 0;
Lcr.Bits.EvenPar = 0;
Lcr.Bits.SticPar = 0;
break;
case EvenParity:
Lcr.Bits.ParEn = 1;
Lcr.Bits.EvenPar = 1;
Lcr.Bits.SticPar = 0;
break;
case OddParity:
Lcr.Bits.ParEn = 1;
Lcr.Bits.EvenPar = 0;
Lcr.Bits.SticPar = 0;
break;
case SpaceParity:
Lcr.Bits.ParEn = 1;
Lcr.Bits.EvenPar = 1;
Lcr.Bits.SticPar = 1;
break;
case MarkParity:
Lcr.Bits.ParEn = 1;
Lcr.Bits.EvenPar = 0;
Lcr.Bits.SticPar = 1;
break;
default:
break;
}
switch (StopBits) {
case OneStopBit:
Lcr.Bits.StopB = 0;
break;
case OneFiveStopBits:
case TwoStopBits:
Lcr.Bits.StopB = 1;
break;
default:
break;
}
//
// DataBits
//
Lcr.Bits.SerialDB = (UINT8) ((DataBits - 5) & 0x03);
WRITE_LCR (SerialDevice->IsaIo, SerialDevice->BaseAddress, Lcr.Data);
//
// Set the Serial I/O mode
//
This->Mode->BaudRate = BaudRate;
This->Mode->ReceiveFifoDepth = ReceiveFifoDepth;
This->Mode->Timeout = Timeout;
This->Mode->Parity = Parity;
This->Mode->DataBits = DataBits;
This->Mode->StopBits = StopBits;
//
// See if Device Path Node has actually changed
//
if (SerialDevice->UartDevicePath.BaudRate == BaudRate &&
SerialDevice->UartDevicePath.DataBits == DataBits &&
SerialDevice->UartDevicePath.Parity == Parity &&
SerialDevice->UartDevicePath.StopBits == StopBits
) {
gBS->RestoreTPL (Tpl);
return EFI_SUCCESS;
}
//
// Update the device path
//
SerialDevice->UartDevicePath.BaudRate = BaudRate;
SerialDevice->UartDevicePath.DataBits = DataBits;
SerialDevice->UartDevicePath.Parity = (UINT8) Parity;
SerialDevice->UartDevicePath.StopBits = (UINT8) StopBits;
Status = EFI_SUCCESS;
if (SerialDevice->Handle != NULL) {
Uart = (UART_DEVICE_PATH *) (
(UINTN) SerialDevice->DevicePath
+ GetDevicePathSize (SerialDevice->ParentDevicePath)
- END_DEVICE_PATH_LENGTH
);
CopyMem (Uart, &SerialDevice->UartDevicePath, sizeof (UART_DEVICE_PATH));
Status = gBS->ReinstallProtocolInterface (
SerialDevice->Handle,
&gEfiDevicePathProtocolGuid,
SerialDevice->DevicePath,
SerialDevice->DevicePath
);
}
gBS->RestoreTPL (Tpl);
return Status;
}
/**
Set Control Bits.
@param This Pointer to EFI_SERIAL_IO_PROTOCOL
@param Control Control bits that can be settable
@retval EFI_SUCCESS New Control bits were set successfully
@retval EFI_UNSUPPORTED The Control bits wanted to set are not supported
**/
EFI_STATUS
EFIAPI
IsaSerialSetControl (
IN EFI_SERIAL_IO_PROTOCOL *This,
IN UINT32 Control
)
{
SERIAL_DEV *SerialDevice;
SERIAL_PORT_MCR Mcr;
EFI_TPL Tpl;
UART_FLOW_CONTROL_DEVICE_PATH *FlowControl;
EFI_STATUS Status;
//
// The control bits that can be set are :
// EFI_SERIAL_DATA_TERMINAL_READY: 0x0001 // WO
// EFI_SERIAL_REQUEST_TO_SEND: 0x0002 // WO
// EFI_SERIAL_HARDWARE_LOOPBACK_ENABLE: 0x1000 // RW
// EFI_SERIAL_SOFTWARE_LOOPBACK_ENABLE: 0x2000 // RW
// EFI_SERIAL_HARDWARE_FLOW_CONTROL_ENABLE: 0x4000 // RW
//
SerialDevice = SERIAL_DEV_FROM_THIS (This);
//
// first determine the parameter is invalid
//
if ((Control & (~(EFI_SERIAL_REQUEST_TO_SEND | EFI_SERIAL_DATA_TERMINAL_READY |
EFI_SERIAL_HARDWARE_LOOPBACK_ENABLE | EFI_SERIAL_SOFTWARE_LOOPBACK_ENABLE |
EFI_SERIAL_HARDWARE_FLOW_CONTROL_ENABLE))) != 0) {
return EFI_UNSUPPORTED;
}
Tpl = gBS->RaiseTPL (TPL_NOTIFY);
Mcr.Data = READ_MCR (SerialDevice->IsaIo, SerialDevice->BaseAddress);
Mcr.Bits.DtrC = 0;
Mcr.Bits.Rts = 0;
Mcr.Bits.Lme = 0;
SerialDevice->SoftwareLoopbackEnable = FALSE;
SerialDevice->HardwareFlowControl = FALSE;
if ((Control & EFI_SERIAL_DATA_TERMINAL_READY) == EFI_SERIAL_DATA_TERMINAL_READY) {
Mcr.Bits.DtrC = 1;
}
if ((Control & EFI_SERIAL_REQUEST_TO_SEND) == EFI_SERIAL_REQUEST_TO_SEND) {
Mcr.Bits.Rts = 1;
}
if ((Control & EFI_SERIAL_HARDWARE_LOOPBACK_ENABLE) == EFI_SERIAL_HARDWARE_LOOPBACK_ENABLE) {
Mcr.Bits.Lme = 1;
}
if ((Control & EFI_SERIAL_HARDWARE_FLOW_CONTROL_ENABLE) == EFI_SERIAL_HARDWARE_FLOW_CONTROL_ENABLE) {
SerialDevice->HardwareFlowControl = TRUE;
}
WRITE_MCR (SerialDevice->IsaIo, SerialDevice->BaseAddress, Mcr.Data);
if ((Control & EFI_SERIAL_SOFTWARE_LOOPBACK_ENABLE) == EFI_SERIAL_SOFTWARE_LOOPBACK_ENABLE) {
SerialDevice->SoftwareLoopbackEnable = TRUE;
}
Status = EFI_SUCCESS;
if (SerialDevice->Handle != NULL) {
FlowControl = (UART_FLOW_CONTROL_DEVICE_PATH *) (
(UINTN) SerialDevice->DevicePath
+ GetDevicePathSize (SerialDevice->ParentDevicePath)
- END_DEVICE_PATH_LENGTH
+ sizeof (UART_DEVICE_PATH)
);
if (IsUartFlowControlNode (FlowControl) &&
((ReadUnaligned32 (&FlowControl->FlowControlMap) == UART_FLOW_CONTROL_HARDWARE) ^ SerialDevice->HardwareFlowControl)) {
//
// Flow Control setting is changed, need to reinstall device path protocol
//
WriteUnaligned32 (&FlowControl->FlowControlMap, SerialDevice->HardwareFlowControl ? UART_FLOW_CONTROL_HARDWARE : 0);
Status = gBS->ReinstallProtocolInterface (
SerialDevice->Handle,
&gEfiDevicePathProtocolGuid,
SerialDevice->DevicePath,
SerialDevice->DevicePath
);
}
}
gBS->RestoreTPL (Tpl);
return Status;
}
/**
Get ControlBits.
@param This Pointer to EFI_SERIAL_IO_PROTOCOL
@param Control Control signals of the serial device
@retval EFI_SUCCESS Get Control signals successfully
**/
EFI_STATUS
EFIAPI
IsaSerialGetControl (
IN EFI_SERIAL_IO_PROTOCOL *This,
OUT UINT32 *Control
)
{
SERIAL_DEV *SerialDevice;
SERIAL_PORT_MSR Msr;
SERIAL_PORT_MCR Mcr;
EFI_TPL Tpl;
Tpl = gBS->RaiseTPL (TPL_NOTIFY);
SerialDevice = SERIAL_DEV_FROM_THIS (This);
*Control = 0;
//
// Read the Modem Status Register
//
Msr.Data = READ_MSR (SerialDevice->IsaIo, SerialDevice->BaseAddress);
if (Msr.Bits.Cts == 1) {
*Control |= EFI_SERIAL_CLEAR_TO_SEND;
}
if (Msr.Bits.Dsr == 1) {
*Control |= EFI_SERIAL_DATA_SET_READY;
}
if (Msr.Bits.Ri == 1) {
*Control |= EFI_SERIAL_RING_INDICATE;
}
if (Msr.Bits.Dcd == 1) {
*Control |= EFI_SERIAL_CARRIER_DETECT;
}
//
// Read the Modem Control Register
//
Mcr.Data = READ_MCR (SerialDevice->IsaIo, SerialDevice->BaseAddress);
if (Mcr.Bits.DtrC == 1) {
*Control |= EFI_SERIAL_DATA_TERMINAL_READY;
}
if (Mcr.Bits.Rts == 1) {
*Control |= EFI_SERIAL_REQUEST_TO_SEND;
}
if (Mcr.Bits.Lme == 1) {
*Control |= EFI_SERIAL_HARDWARE_LOOPBACK_ENABLE;
}
if (SerialDevice->HardwareFlowControl) {
*Control |= EFI_SERIAL_HARDWARE_FLOW_CONTROL_ENABLE;
}
//
// See if the Transmit FIFO is empty
//
IsaSerialReceiveTransmit (SerialDevice);
if (IsaSerialFifoEmpty (&SerialDevice->Transmit)) {
*Control |= EFI_SERIAL_OUTPUT_BUFFER_EMPTY;
}
//
// See if the Receive FIFO is empty.
//
IsaSerialReceiveTransmit (SerialDevice);
if (IsaSerialFifoEmpty (&SerialDevice->Receive)) {
*Control |= EFI_SERIAL_INPUT_BUFFER_EMPTY;
}
if (SerialDevice->SoftwareLoopbackEnable) {
*Control |= EFI_SERIAL_SOFTWARE_LOOPBACK_ENABLE;
}
gBS->RestoreTPL (Tpl);
return EFI_SUCCESS;
}
/**
Write the specified number of bytes to serial device.
@param This Pointer to EFI_SERIAL_IO_PROTOCOL
@param BufferSize On input the size of Buffer, on output the amount of
data actually written
@param Buffer The buffer of data to write
@retval EFI_SUCCESS The data were written successfully
@retval EFI_DEVICE_ERROR The device reported an error
@retval EFI_TIMEOUT The write operation was stopped due to timeout
**/
EFI_STATUS
EFIAPI
IsaSerialWrite (
IN EFI_SERIAL_IO_PROTOCOL *This,
IN OUT UINTN *BufferSize,
IN VOID *Buffer
)
{
SERIAL_DEV *SerialDevice;
UINT8 *CharBuffer;
UINT32 Index;
UINTN Elapsed;
UINTN ActualWrite;
EFI_TPL Tpl;
UINTN Timeout;
UINTN BitsPerCharacter;
SerialDevice = SERIAL_DEV_FROM_THIS (This);
Elapsed = 0;
ActualWrite = 0;
if (*BufferSize == 0) {
return EFI_SUCCESS;
}
if (Buffer == NULL) {
REPORT_STATUS_CODE_WITH_DEVICE_PATH (
EFI_ERROR_CODE,
EFI_P_EC_OUTPUT_ERROR | EFI_PERIPHERAL_SERIAL_PORT,
SerialDevice->DevicePath
);
return EFI_DEVICE_ERROR;
}
Tpl = gBS->RaiseTPL (TPL_NOTIFY);
CharBuffer = (UINT8 *) Buffer;
//
// Compute the number of bits in a single character. This is a start bit,
// followed by the number of data bits, followed by the number of stop bits.
// The number of stop bits is specified by an enumeration that includes
// support for 1.5 stop bits. Treat 1.5 stop bits as 2 stop bits.
//
BitsPerCharacter =
1 +
This->Mode->DataBits +
((This->Mode->StopBits == TwoStopBits) ? 2 : This->Mode->StopBits);
//
// Compute the timeout in microseconds to wait for a single byte to be
// transmitted. The Mode structure contans a Timeout field that is the
// maximum time to transmit or receive a character. However, many UARTs
// have a FIFO for transmits, so the time required to add one new character
// to the transmit FIFO may be the time required to flush a full FIFO. If
// the Timeout in the Mode structure is smaller than the time required to
// flush a full FIFO at the current baud rate, then use a timeout value that
// is required to flush a full transmit FIFO.
//
Timeout = MAX (
This->Mode->Timeout,
(UINTN)DivU64x64Remainder (
BitsPerCharacter * (SERIAL_PORT_MAX_RECEIVE_FIFO_DEPTH + 1) * 1000000,
This->Mode->BaudRate,
NULL
)
);
for (Index = 0; Index < *BufferSize; Index++) {
IsaSerialFifoAdd (&SerialDevice->Transmit, CharBuffer[Index]);
while (IsaSerialReceiveTransmit (SerialDevice) != EFI_SUCCESS || !IsaSerialFifoEmpty (&SerialDevice->Transmit)) {
//
// Unsuccessful write so check if timeout has expired, if not,
// stall for a bit, increment time elapsed, and try again
//
if (Elapsed >= Timeout) {
*BufferSize = ActualWrite;
gBS->RestoreTPL (Tpl);
return EFI_TIMEOUT;
}
gBS->Stall (TIMEOUT_STALL_INTERVAL);
Elapsed += TIMEOUT_STALL_INTERVAL;
}
ActualWrite++;
//
// Successful write so reset timeout
//
Elapsed = 0;
}
gBS->RestoreTPL (Tpl);
return EFI_SUCCESS;
}
/**
Read the specified number of bytes from serial device.
@param This Pointer to EFI_SERIAL_IO_PROTOCOL
@param BufferSize On input the size of Buffer, on output the amount of
data returned in buffer
@param Buffer The buffer to return the data into
@retval EFI_SUCCESS The data were read successfully
@retval EFI_DEVICE_ERROR The device reported an error
@retval EFI_TIMEOUT The read operation was stopped due to timeout
**/
EFI_STATUS
EFIAPI
IsaSerialRead (
IN EFI_SERIAL_IO_PROTOCOL *This,
IN OUT UINTN *BufferSize,
OUT VOID *Buffer
)
{
SERIAL_DEV *SerialDevice;
UINT32 Index;
UINT8 *CharBuffer;
UINTN Elapsed;
EFI_STATUS Status;
EFI_TPL Tpl;
SerialDevice = SERIAL_DEV_FROM_THIS (This);
Elapsed = 0;
if (*BufferSize == 0) {
return EFI_SUCCESS;
}
if (Buffer == NULL) {
return EFI_DEVICE_ERROR;
}
Tpl = gBS->RaiseTPL (TPL_NOTIFY);
Status = IsaSerialReceiveTransmit (SerialDevice);
if (EFI_ERROR (Status)) {
*BufferSize = 0;
REPORT_STATUS_CODE_WITH_DEVICE_PATH (
EFI_ERROR_CODE,
EFI_P_EC_INPUT_ERROR | EFI_PERIPHERAL_SERIAL_PORT,
SerialDevice->DevicePath
);
gBS->RestoreTPL (Tpl);
return EFI_DEVICE_ERROR;
}
CharBuffer = (UINT8 *) Buffer;
for (Index = 0; Index < *BufferSize; Index++) {
while (IsaSerialFifoRemove (&SerialDevice->Receive, &(CharBuffer[Index])) != EFI_SUCCESS) {
//
// Unsuccessful read so check if timeout has expired, if not,
// stall for a bit, increment time elapsed, and try again
// Need this time out to get conspliter to work.
//
if (Elapsed >= This->Mode->Timeout) {
*BufferSize = Index;
gBS->RestoreTPL (Tpl);
return EFI_TIMEOUT;
}
gBS->Stall (TIMEOUT_STALL_INTERVAL);
Elapsed += TIMEOUT_STALL_INTERVAL;
Status = IsaSerialReceiveTransmit (SerialDevice);
if (Status == EFI_DEVICE_ERROR) {
*BufferSize = Index;
gBS->RestoreTPL (Tpl);
return EFI_DEVICE_ERROR;
}
}
//
// Successful read so reset timeout
//
Elapsed = 0;
}
IsaSerialReceiveTransmit (SerialDevice);
gBS->RestoreTPL (Tpl);
return EFI_SUCCESS;
}
/**
Use scratchpad register to test if this serial port is present.
@param SerialDevice Pointer to serial device structure
@return if this serial port is present
**/
BOOLEAN
IsaSerialPortPresent (
IN SERIAL_DEV *SerialDevice
)
{
UINT8 Temp;
BOOLEAN Status;
Status = TRUE;
//
// Save SCR reg
//
Temp = READ_SCR (SerialDevice->IsaIo, SerialDevice->BaseAddress);
WRITE_SCR (SerialDevice->IsaIo, SerialDevice->BaseAddress, 0xAA);
if (READ_SCR (SerialDevice->IsaIo, SerialDevice->BaseAddress) != 0xAA) {
Status = FALSE;
}
WRITE_SCR (SerialDevice->IsaIo, SerialDevice->BaseAddress, 0x55);
if (READ_SCR (SerialDevice->IsaIo, SerialDevice->BaseAddress) != 0x55) {
Status = FALSE;
}
//
// Restore SCR
//
WRITE_SCR (SerialDevice->IsaIo, SerialDevice->BaseAddress, Temp);
return Status;
}
/**
Use IsaIo protocol to read serial port.
@param IsaIo Pointer to EFI_ISA_IO_PROTOCOL instance
@param BaseAddress Serial port register group base address
@param Offset Offset in register group
@return Data read from serial port
**/
UINT8
IsaSerialReadPort (
IN EFI_ISA_IO_PROTOCOL *IsaIo,
IN UINT16 BaseAddress,
IN UINT32 Offset
)
{
UINT8 Data;
//
// Use IsaIo to access IO
//
IsaIo->Io.Read (
IsaIo,
EfiIsaIoWidthUint8,
BaseAddress + Offset,
1,
&Data
);
return Data;
}
/**
Use IsaIo protocol to write serial port.
@param IsaIo Pointer to EFI_ISA_IO_PROTOCOL instance
@param BaseAddress Serial port register group base address
@param Offset Offset in register group
@param Data data which is to be written to some serial port register
**/
VOID
IsaSerialWritePort (
IN EFI_ISA_IO_PROTOCOL *IsaIo,
IN UINT16 BaseAddress,
IN UINT32 Offset,
IN UINT8 Data
)
{
//
// Use IsaIo to access IO
//
IsaIo->Io.Write (
IsaIo,
EfiIsaIoWidthUint8,
BaseAddress + Offset,
1,
&Data
);
}