audk/MdeModulePkg/Bus/Pci/PciSioSerialDxe/Serial.c

1257 lines
43 KiB
C

/** @file
Serial driver for PCI or SIO UARTS.
Copyright (c) 2006 - 2016, Intel Corporation. All rights reserved.<BR>
This program and the accompanying materials
are licensed and made available under the terms and conditions of the BSD License
which accompanies this distribution. The full text of the license may be found at
http://opensource.org/licenses/bsd-license.php
THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
**/
#include "Serial.h"
//
// ISA Serial Driver Global Variables
//
EFI_DRIVER_BINDING_PROTOCOL gSerialControllerDriver = {
SerialControllerDriverSupported,
SerialControllerDriverStart,
SerialControllerDriverStop,
0xa,
NULL,
NULL
};
CONTROLLER_DEVICE_PATH mControllerDevicePathTemplate = {
{
HARDWARE_DEVICE_PATH,
HW_CONTROLLER_DP,
{
(UINT8) (sizeof (CONTROLLER_DEVICE_PATH)),
(UINT8) ((sizeof (CONTROLLER_DEVICE_PATH)) >> 8)
}
},
0
};
SERIAL_DEV gSerialDevTemplate = {
SERIAL_DEV_SIGNATURE,
NULL,
{
SERIAL_IO_INTERFACE_REVISION,
SerialReset,
SerialSetAttributes,
SerialSetControl,
SerialGetControl,
SerialWrite,
SerialRead,
NULL
}, // SerialIo
{
SERIAL_PORT_SUPPORT_CONTROL_MASK,
SERIAL_PORT_DEFAULT_TIMEOUT,
0,
16,
0,
0,
0
}, // SerialMode
NULL, // DevicePath
NULL, // ParentDevicePath
{
{
MESSAGING_DEVICE_PATH,
MSG_UART_DP,
{
(UINT8) (sizeof (UART_DEVICE_PATH)),
(UINT8) ((sizeof (UART_DEVICE_PATH)) >> 8)
}
},
0, 0, 0, 0, 0
}, // UartDevicePath
0, // BaseAddress
FALSE, // MmioAccess
1, // RegisterStride
0, // ClockRate
16, // ReceiveFifoDepth
{ 0, 0 }, // Receive;
16, // TransmitFifoDepth
{ 0, 0 }, // Transmit;
FALSE, // SoftwareLoopbackEnable;
FALSE, // HardwareFlowControl;
NULL, // *ControllerNameTable;
FALSE, // ContainsControllerNode;
0, // Instance;
NULL // *PciDeviceInfo;
};
/**
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
IsUartFlowControlDevicePathNode (
IN UART_FLOW_CONTROL_DEVICE_PATH *FlowControl
)
{
return (BOOLEAN) (
(DevicePathType (FlowControl) == MESSAGING_DEVICE_PATH) &&
(DevicePathSubType (FlowControl) == MSG_VENDOR_DP) &&
(CompareGuid (&FlowControl->Guid, &gEfiUartDevicePathGuid))
);
}
/**
The user Entry Point for module PciSioSerial. 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
InitializePciSioSerial (
IN EFI_HANDLE ImageHandle,
IN EFI_SYSTEM_TABLE *SystemTable
)
{
EFI_STATUS Status;
//
// Install driver model protocol(s).
//
Status = EfiLibInstallDriverBindingComponentName2 (
ImageHandle,
SystemTable,
&gSerialControllerDriver,
ImageHandle,
&gPciSioSerialComponentName,
&gPciSioSerialComponentName2
);
ASSERT_EFI_ERROR (Status);
//
// Initialize UART default setting in gSerialDevTempate
//
gSerialDevTemplate.SerialMode.BaudRate = PcdGet64 (PcdUartDefaultBaudRate);
gSerialDevTemplate.SerialMode.DataBits = PcdGet8 (PcdUartDefaultDataBits);
gSerialDevTemplate.SerialMode.Parity = PcdGet8 (PcdUartDefaultParity);
gSerialDevTemplate.SerialMode.StopBits = PcdGet8 (PcdUartDefaultStopBits);
gSerialDevTemplate.UartDevicePath.BaudRate = PcdGet64 (PcdUartDefaultBaudRate);
gSerialDevTemplate.UartDevicePath.DataBits = PcdGet8 (PcdUartDefaultDataBits);
gSerialDevTemplate.UartDevicePath.Parity = PcdGet8 (PcdUartDefaultParity);
gSerialDevTemplate.UartDevicePath.StopBits = PcdGet8 (PcdUartDefaultStopBits);
gSerialDevTemplate.ClockRate = PcdGet32 (PcdSerialClockRate);
return Status;
}
/**
Return whether the controller is a SIO serial controller.
@param Controller The controller handle.
@retval EFI_SUCCESS The controller is a SIO serial controller.
@retval others The controller is not a SIO serial controller.
**/
EFI_STATUS
IsSioSerialController (
EFI_HANDLE Controller
)
{
EFI_STATUS Status;
EFI_SIO_PROTOCOL *Sio;
EFI_DEVICE_PATH_PROTOCOL *DevicePath;
ACPI_HID_DEVICE_PATH *Acpi;
//
// Open the IO Abstraction(s) needed to perform the supported test
//
Status = gBS->OpenProtocol (
Controller,
&gEfiSioProtocolGuid,
(VOID **) &Sio,
gSerialControllerDriver.DriverBindingHandle,
Controller,
EFI_OPEN_PROTOCOL_BY_DRIVER
);
if (Status == EFI_ALREADY_STARTED) {
return EFI_SUCCESS;
}
if (!EFI_ERROR (Status)) {
//
// Close the I/O Abstraction(s) used to perform the supported test
//
gBS->CloseProtocol (
Controller,
&gEfiSioProtocolGuid,
gSerialControllerDriver.DriverBindingHandle,
Controller
);
Status = gBS->OpenProtocol (
Controller,
&gEfiDevicePathProtocolGuid,
(VOID **) &DevicePath,
gSerialControllerDriver.DriverBindingHandle,
Controller,
EFI_OPEN_PROTOCOL_BY_DRIVER
);
ASSERT (Status != EFI_ALREADY_STARTED);
if (!EFI_ERROR (Status)) {
do {
Acpi = (ACPI_HID_DEVICE_PATH *) DevicePath;
DevicePath = NextDevicePathNode (DevicePath);
} while (!IsDevicePathEnd (DevicePath));
if (DevicePathType (Acpi) != ACPI_DEVICE_PATH ||
(DevicePathSubType (Acpi) != ACPI_DP && DevicePathSubType (Acpi) != ACPI_EXTENDED_DP) ||
Acpi->HID != EISA_PNP_ID (0x501)
) {
Status = EFI_UNSUPPORTED;
}
}
//
// Close protocol, don't use device path protocol in the Support() function
//
gBS->CloseProtocol (
Controller,
&gEfiDevicePathProtocolGuid,
gSerialControllerDriver.DriverBindingHandle,
Controller
);
}
return Status;
}
/**
Return whether the controller is a PCI serial controller.
@param Controller The controller handle.
@retval EFI_SUCCESS The controller is a PCI serial controller.
@retval others The controller is not a PCI serial controller.
**/
EFI_STATUS
IsPciSerialController (
EFI_HANDLE Controller
)
{
EFI_STATUS Status;
EFI_PCI_IO_PROTOCOL *PciIo;
EFI_DEVICE_PATH_PROTOCOL *DevicePath;
PCI_TYPE00 Pci;
PCI_SERIAL_PARAMETER *PciSerialParameter;
//
// Open the IO Abstraction(s) needed to perform the supported test
//
Status = gBS->OpenProtocol (
Controller,
&gEfiPciIoProtocolGuid,
(VOID **) &PciIo,
gSerialControllerDriver.DriverBindingHandle,
Controller,
EFI_OPEN_PROTOCOL_BY_DRIVER
);
if (Status == EFI_ALREADY_STARTED) {
return EFI_SUCCESS;
}
if (!EFI_ERROR (Status)) {
Status = PciIo->Pci.Read (PciIo, EfiPciIoWidthUint8, 0, sizeof (Pci), &Pci);
if (!EFI_ERROR (Status)) {
if (!IS_PCI_16550_SERIAL (&Pci)) {
for (PciSerialParameter = (PCI_SERIAL_PARAMETER *) PcdGetPtr (PcdPciSerialParameters)
; PciSerialParameter->VendorId != 0xFFFF
; PciSerialParameter++
) {
if ((Pci.Hdr.VendorId == PciSerialParameter->VendorId) &&
(Pci.Hdr.DeviceId == PciSerialParameter->DeviceId)
) {
break;
}
}
if (PciSerialParameter->VendorId == 0xFFFF) {
Status = EFI_UNSUPPORTED;
} else {
Status = EFI_SUCCESS;
}
}
}
//
// Close the I/O Abstraction(s) used to perform the supported test
//
gBS->CloseProtocol (
Controller,
&gEfiPciIoProtocolGuid,
gSerialControllerDriver.DriverBindingHandle,
Controller
);
}
if (EFI_ERROR (Status)) {
return Status;
}
//
// Open the EFI Device Path protocol needed to perform the supported test
//
Status = gBS->OpenProtocol (
Controller,
&gEfiDevicePathProtocolGuid,
(VOID **) &DevicePath,
gSerialControllerDriver.DriverBindingHandle,
Controller,
EFI_OPEN_PROTOCOL_BY_DRIVER
);
ASSERT (Status != EFI_ALREADY_STARTED);
//
// Close protocol, don't use device path protocol in the Support() function
//
gBS->CloseProtocol (
Controller,
&gEfiDevicePathProtocolGuid,
gSerialControllerDriver.DriverBindingHandle,
Controller
);
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;
UART_DEVICE_PATH *Uart;
UART_FLOW_CONTROL_DEVICE_PATH *FlowControl;
//
// Test RemainingDevicePath
//
if ((RemainingDevicePath != NULL) && !IsDevicePathEnd (RemainingDevicePath)) {
Status = EFI_UNSUPPORTED;
Uart = SkipControllerDevicePathNode (RemainingDevicePath, NULL, NULL);
if (DevicePathType (Uart) != MESSAGING_DEVICE_PATH ||
DevicePathSubType (Uart) != MSG_UART_DP ||
DevicePathNodeLength (Uart) != sizeof (UART_DEVICE_PATH)
) {
return EFI_UNSUPPORTED;
}
//
// Do a rough check because Clock Rate is unknown until DriverBindingStart()
//
if (!VerifyUartParameters (0, Uart->BaudRate, Uart->DataBits, Uart->Parity, Uart->StopBits, NULL, NULL)) {
return EFI_UNSUPPORTED;
}
FlowControl = (UART_FLOW_CONTROL_DEVICE_PATH *) NextDevicePathNode (Uart);
if (IsUartFlowControlDevicePathNode (FlowControl)) {
//
// If the second node is Flow Control Node,
// return error when it request other than hardware flow control.
//
if ((ReadUnaligned32 (&FlowControl->FlowControlMap) & ~UART_FLOW_CONTROL_HARDWARE) != 0) {
return EFI_UNSUPPORTED;
}
}
}
Status = IsSioSerialController (Controller);
if (EFI_ERROR (Status)) {
Status = IsPciSerialController (Controller);
}
return Status;
}
/**
Create the child serial device instance.
@param Controller The parent controller handle.
@param Uart Pointer to the UART device path node in RemainingDevicePath,
or NULL if RemainingDevicePath is NULL.
@param ParentDevicePath Pointer to the parent device path.
@param CreateControllerNode TRUE to create the controller node.
@param Instance Instance number of the serial device.
The value will be set to the controller node
if CreateControllerNode is TRUE.
@param ParentIo A union type pointer to either Sio or PciIo.
@param PciSerialParameter The PCI serial parameter to be used by current serial device.
NULL for SIO serial device.
@param PciDeviceInfo The PCI device info for the current serial device.
NULL for SIO serial device.
@retval EFI_SUCCESS The serial device was created successfully.
@retval others The serial device wasn't created.
**/
EFI_STATUS
CreateSerialDevice (
IN EFI_HANDLE Controller,
IN UART_DEVICE_PATH *Uart,
IN EFI_DEVICE_PATH_PROTOCOL *ParentDevicePath,
IN BOOLEAN CreateControllerNode,
IN UINT32 Instance,
IN PARENT_IO_PROTOCOL_PTR ParentIo,
IN PCI_SERIAL_PARAMETER *PciSerialParameter, OPTIONAL
IN PCI_DEVICE_INFO *PciDeviceInfo OPTIONAL
)
{
EFI_STATUS Status;
SERIAL_DEV *SerialDevice;
UINT8 BarIndex;
UINT64 Offset;
UART_FLOW_CONTROL_DEVICE_PATH *FlowControl;
UINT32 FlowControlMap;
ACPI_RESOURCE_HEADER_PTR Resources;
EFI_ACPI_IO_PORT_DESCRIPTOR *Io;
EFI_ACPI_FIXED_LOCATION_IO_PORT_DESCRIPTOR *FixedIo;
EFI_ACPI_ADDRESS_SPACE_DESCRIPTOR *AddressSpace;
EFI_DEVICE_PATH_PROTOCOL *TempDevicePath;
BarIndex = 0;
Offset = 0;
FlowControl = NULL;
FlowControlMap = 0;
//
// Initialize the serial device instance
//
SerialDevice = AllocateCopyPool (sizeof (SERIAL_DEV), &gSerialDevTemplate);
ASSERT (SerialDevice != NULL);
SerialDevice->SerialIo.Mode = &(SerialDevice->SerialMode);
SerialDevice->ParentDevicePath = ParentDevicePath;
SerialDevice->PciDeviceInfo = PciDeviceInfo;
SerialDevice->Instance = Instance;
if (Uart != NULL) {
CopyMem (&SerialDevice->UartDevicePath, Uart, sizeof (UART_DEVICE_PATH));
FlowControl = (UART_FLOW_CONTROL_DEVICE_PATH *) NextDevicePathNode (Uart);
if (IsUartFlowControlDevicePathNode (FlowControl)) {
FlowControlMap = ReadUnaligned32 (&FlowControl->FlowControlMap);
} else {
FlowControl = NULL;
}
}
//
// For PCI serial device, use the information from PCD
//
if (PciSerialParameter != NULL) {
BarIndex = (PciSerialParameter->BarIndex == MAX_UINT8) ? 0 : PciSerialParameter->BarIndex;
Offset = PciSerialParameter->Offset;
if (PciSerialParameter->RegisterStride != 0) {
SerialDevice->RegisterStride = PciSerialParameter->RegisterStride;
}
if (PciSerialParameter->ClockRate != 0) {
SerialDevice->ClockRate = PciSerialParameter->ClockRate;
}
if (PciSerialParameter->ReceiveFifoDepth != 0) {
SerialDevice->ReceiveFifoDepth = PciSerialParameter->ReceiveFifoDepth;
}
if (PciSerialParameter->TransmitFifoDepth != 0) {
SerialDevice->TransmitFifoDepth = PciSerialParameter->TransmitFifoDepth;
}
}
//
// Pass NULL ActualBaudRate to VerifyUartParameters to disallow baudrate degrade.
// DriverBindingStart() shouldn't create a handle with different UART device path.
//
if (!VerifyUartParameters (SerialDevice->ClockRate, SerialDevice->UartDevicePath.BaudRate, SerialDevice->UartDevicePath.DataBits,
SerialDevice->UartDevicePath.Parity, SerialDevice->UartDevicePath.StopBits, NULL, NULL
)) {
Status = EFI_INVALID_PARAMETER;
goto CreateError;
}
if (PciSerialParameter == NULL) {
Status = ParentIo.Sio->GetResources (ParentIo.Sio, &Resources);
} else {
Status = ParentIo.PciIo->GetBarAttributes (ParentIo.PciIo, BarIndex, NULL, (VOID **) &Resources);
}
if (!EFI_ERROR (Status)) {
//
// Get the base address information from ACPI resource descriptor.
// ACPI_IO_PORT_DESCRIPTOR and ACPI_FIXED_LOCATION_IO_PORT_DESCRIPTOR are returned from Sio;
// ACPI_ADDRESS_SPACE_DESCRIPTOR is returned from PciIo.
//
while ((Resources.SmallHeader->Byte != ACPI_END_TAG_DESCRIPTOR) && (SerialDevice->BaseAddress == 0)) {
switch (Resources.SmallHeader->Byte) {
case ACPI_IO_PORT_DESCRIPTOR:
Io = (EFI_ACPI_IO_PORT_DESCRIPTOR *) Resources.SmallHeader;
if (Io->Length != 0) {
SerialDevice->BaseAddress = Io->BaseAddressMin;
}
break;
case ACPI_FIXED_LOCATION_IO_PORT_DESCRIPTOR:
FixedIo = (EFI_ACPI_FIXED_LOCATION_IO_PORT_DESCRIPTOR *) Resources.SmallHeader;
if (FixedIo->Length != 0) {
SerialDevice->BaseAddress = FixedIo->BaseAddress;
}
break;
case ACPI_ADDRESS_SPACE_DESCRIPTOR:
AddressSpace = (EFI_ACPI_ADDRESS_SPACE_DESCRIPTOR *) Resources.SmallHeader;
if (AddressSpace->AddrLen != 0) {
if (AddressSpace->ResType == ACPI_ADDRESS_SPACE_TYPE_MEM) {
SerialDevice->MmioAccess = TRUE;
}
SerialDevice->BaseAddress = AddressSpace->AddrRangeMin + Offset;
}
break;
}
if (Resources.SmallHeader->Bits.Type == 0) {
Resources.SmallHeader = (ACPI_SMALL_RESOURCE_HEADER *) ((UINT8 *) Resources.SmallHeader
+ Resources.SmallHeader->Bits.Length
+ sizeof (*Resources.SmallHeader));
} else {
Resources.LargeHeader = (ACPI_LARGE_RESOURCE_HEADER *) ((UINT8 *) Resources.LargeHeader
+ Resources.LargeHeader->Length
+ sizeof (*Resources.LargeHeader));
}
}
}
if (SerialDevice->BaseAddress == 0) {
Status = EFI_INVALID_PARAMETER;
goto CreateError;
}
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,
SerialDevice->ParentDevicePath
);
if (!SerialPresent (SerialDevice)) {
Status = EFI_DEVICE_ERROR;
REPORT_STATUS_CODE_WITH_DEVICE_PATH (
EFI_ERROR_CODE,
EFI_P_EC_NOT_DETECTED | EFI_PERIPHERAL_SERIAL_PORT,
SerialDevice->ParentDevicePath
);
goto CreateError;
}
//
// 1. Append Controller device path node.
//
if (CreateControllerNode) {
mControllerDevicePathTemplate.ControllerNumber = SerialDevice->Instance;
SerialDevice->DevicePath = AppendDevicePathNode (
SerialDevice->ParentDevicePath,
(EFI_DEVICE_PATH_PROTOCOL *) &mControllerDevicePathTemplate
);
SerialDevice->ContainsControllerNode = TRUE;
}
//
// 2. Append UART device path node.
// The Uart setings are zero here.
// SetAttribute() will update them to match the default setings.
//
TempDevicePath = SerialDevice->DevicePath;
if (TempDevicePath != NULL) {
SerialDevice->DevicePath = AppendDevicePathNode (
TempDevicePath,
(EFI_DEVICE_PATH_PROTOCOL *) &SerialDevice->UartDevicePath
);
FreePool (TempDevicePath);
} else {
SerialDevice->DevicePath = AppendDevicePathNode (
SerialDevice->ParentDevicePath,
(EFI_DEVICE_PATH_PROTOCOL *) &SerialDevice->UartDevicePath
);
}
//
// 3. Append the Flow Control device path node.
// 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);
}
}
ASSERT (SerialDevice->DevicePath != NULL);
//
// 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,
SerialDevice->DevicePath
);
goto CreateError;
}
AddName (SerialDevice, Instance);
//
// Install protocol interfaces for the serial device.
//
Status = gBS->InstallMultipleProtocolInterfaces (
&SerialDevice->Handle,
&gEfiDevicePathProtocolGuid, SerialDevice->DevicePath,
&gEfiSerialIoProtocolGuid, &SerialDevice->SerialIo,
NULL
);
if (EFI_ERROR (Status)) {
goto CreateError;
}
//
// Open For Child Device
//
Status = gBS->OpenProtocol (
Controller,
PciSerialParameter != NULL ? &gEfiPciIoProtocolGuid : &gEfiSioProtocolGuid,
(VOID **) &ParentIo,
gSerialControllerDriver.DriverBindingHandle,
SerialDevice->Handle,
EFI_OPEN_PROTOCOL_BY_CHILD_CONTROLLER
);
if (EFI_ERROR (Status)) {
gBS->UninstallMultipleProtocolInterfaces (
&SerialDevice->Handle,
&gEfiDevicePathProtocolGuid, SerialDevice->DevicePath,
&gEfiSerialIoProtocolGuid, &SerialDevice->SerialIo,
NULL
);
}
CreateError:
if (EFI_ERROR (Status)) {
if (SerialDevice->DevicePath != NULL) {
FreePool (SerialDevice->DevicePath);
}
if (SerialDevice->ControllerNameTable != NULL) {
FreeUnicodeStringTable (SerialDevice->ControllerNameTable);
}
FreePool (SerialDevice);
}
return Status;
}
/**
Returns an array of pointers containing all the child serial device pointers.
@param Controller The parent controller handle.
@param IoProtocolGuid The protocol GUID, either equals to gEfiSioProtocolGuid
or equals to gEfiPciIoProtocolGuid.
@param Count Count of the serial devices.
@return An array of pointers containing all the child serial device pointers.
**/
SERIAL_DEV **
GetChildSerialDevices (
IN EFI_HANDLE Controller,
IN EFI_GUID *IoProtocolGuid,
OUT UINTN *Count
)
{
EFI_STATUS Status;
UINTN Index;
EFI_OPEN_PROTOCOL_INFORMATION_ENTRY *OpenInfoBuffer;
UINTN EntryCount;
SERIAL_DEV **SerialDevices;
EFI_SERIAL_IO_PROTOCOL *SerialIo;
BOOLEAN OpenByDriver;
*Count = 0;
//
// If the SerialIo instance specified by RemainingDevicePath is already created,
// update the attributes/control.
//
Status = gBS->OpenProtocolInformation (
Controller,
IoProtocolGuid,
&OpenInfoBuffer,
&EntryCount
);
if (EFI_ERROR (Status)) {
return NULL;
}
SerialDevices = AllocatePool (EntryCount * sizeof (SERIAL_DEV *));
ASSERT (SerialDevices != NULL);
*Count = 0;
OpenByDriver = FALSE;
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,
gSerialControllerDriver.DriverBindingHandle,
Controller,
EFI_OPEN_PROTOCOL_GET_PROTOCOL
);
if (!EFI_ERROR (Status)) {
SerialDevices[(*Count)++] = SERIAL_DEV_FROM_THIS (SerialIo);
}
}
if ((OpenInfoBuffer[Index].Attributes & EFI_OPEN_PROTOCOL_BY_DRIVER) != 0) {
ASSERT (OpenInfoBuffer[Index].AgentHandle == gSerialControllerDriver.DriverBindingHandle);
OpenByDriver = TRUE;
}
}
if (OpenInfoBuffer != NULL) {
FreePool (OpenInfoBuffer);
}
ASSERT ((*Count == 0) || (OpenByDriver));
return SerialDevices;
}
/**
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;
UINTN Index;
EFI_DEVICE_PATH_PROTOCOL *ParentDevicePath;
EFI_DEVICE_PATH_PROTOCOL *Node;
EFI_SERIAL_IO_PROTOCOL *SerialIo;
UINT32 ControllerNumber;
UART_DEVICE_PATH *Uart;
UART_FLOW_CONTROL_DEVICE_PATH *FlowControl;
UINT32 Control;
PARENT_IO_PROTOCOL_PTR ParentIo;
ACPI_HID_DEVICE_PATH *Acpi;
EFI_GUID *IoProtocolGuid;
PCI_SERIAL_PARAMETER *PciSerialParameter;
PCI_SERIAL_PARAMETER DefaultPciSerialParameter;
PCI_TYPE00 Pci;
UINT32 PciSerialCount;
SERIAL_DEV **SerialDevices;
UINTN SerialDeviceCount;
PCI_DEVICE_INFO *PciDeviceInfo;
UINT64 Supports;
BOOLEAN ContainsControllerNode;
//
// 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
//
IoProtocolGuid = &gEfiSioProtocolGuid;
Status = gBS->OpenProtocol (
Controller,
IoProtocolGuid,
(VOID **) &ParentIo,
This->DriverBindingHandle,
Controller,
EFI_OPEN_PROTOCOL_BY_DRIVER
);
if (EFI_ERROR (Status) && Status != EFI_ALREADY_STARTED) {
IoProtocolGuid = &gEfiPciIoProtocolGuid;
Status = gBS->OpenProtocol (
Controller,
IoProtocolGuid,
(VOID **) &ParentIo,
This->DriverBindingHandle,
Controller,
EFI_OPEN_PROTOCOL_BY_DRIVER
);
}
ASSERT (!EFI_ERROR (Status) || Status == EFI_ALREADY_STARTED);
//
// Do nothing for END device path node
//
if ((RemainingDevicePath != NULL) && IsDevicePathEnd (RemainingDevicePath)) {
return EFI_SUCCESS;
}
ControllerNumber = 0;
ContainsControllerNode = FALSE;
SerialDevices = GetChildSerialDevices (Controller, IoProtocolGuid, &SerialDeviceCount);
if (SerialDeviceCount != 0) {
if (RemainingDevicePath == NULL) {
//
// If the SerialIo instance is already created, NULL as RemainingDevicePath is treated
// as to create the same SerialIo instance.
//
return EFI_SUCCESS;
} else {
//
// Update the attributes/control of the SerialIo instance specified by RemainingDevicePath.
//
Uart = (UART_DEVICE_PATH *) SkipControllerDevicePathNode (RemainingDevicePath, &ContainsControllerNode, &ControllerNumber);
for (Index = 0; Index < SerialDeviceCount; Index++) {
ASSERT ((SerialDevices != NULL) && (SerialDevices[Index] != NULL));
if ((!SerialDevices[Index]->ContainsControllerNode && !ContainsControllerNode) ||
(SerialDevices[Index]->ContainsControllerNode && ContainsControllerNode && SerialDevices[Index]->Instance == ControllerNumber)
) {
SerialIo = &SerialDevices[Index]->SerialIo;
Status = EFI_INVALID_PARAMETER;
//
// Pass NULL ActualBaudRate to VerifyUartParameters to disallow baudrate degrade.
// DriverBindingStart() shouldn't create a handle with different UART device path.
//
if (VerifyUartParameters (SerialDevices[Index]->ClockRate, Uart->BaudRate, Uart->DataBits,
(EFI_PARITY_TYPE) Uart->Parity, (EFI_STOP_BITS_TYPE) Uart->StopBits, NULL, NULL)) {
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) && IsUartFlowControlDevicePathNode (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;
}
}
if (Index != SerialDeviceCount) {
//
// Directly return if the SerialIo instance specified by RemainingDevicePath is found and updated.
// Otherwise continue to create the instance specified by RemainingDevicePath.
//
if (SerialDevices != NULL) {
FreePool (SerialDevices);
}
return Status;
}
}
}
if (RemainingDevicePath != NULL) {
Uart = (UART_DEVICE_PATH *) SkipControllerDevicePathNode (RemainingDevicePath, &ContainsControllerNode, &ControllerNumber);
} else {
Uart = NULL;
}
PciDeviceInfo = NULL;
if (IoProtocolGuid == &gEfiSioProtocolGuid) {
Status = EFI_NOT_FOUND;
if (RemainingDevicePath == NULL || !ContainsControllerNode) {
Node = ParentDevicePath;
do {
Acpi = (ACPI_HID_DEVICE_PATH *) Node;
Node = NextDevicePathNode (Node);
} while (!IsDevicePathEnd (Node));
Status = CreateSerialDevice (Controller, Uart, ParentDevicePath, FALSE, Acpi->UID, ParentIo, NULL, NULL);
DEBUG ((EFI_D_INFO, "PciSioSerial: Create SIO child serial device - %r\n", Status));
}
} else {
Status = ParentIo.PciIo->Pci.Read (ParentIo.PciIo, EfiPciIoWidthUint8, 0, sizeof (Pci), &Pci);
if (!EFI_ERROR (Status)) {
//
// PcdPciSerialParameters takes the higher priority.
//
PciSerialCount = 0;
for (PciSerialParameter = PcdGetPtr (PcdPciSerialParameters); PciSerialParameter->VendorId != 0xFFFF; PciSerialParameter++) {
if ((PciSerialParameter->VendorId == Pci.Hdr.VendorId) &&
(PciSerialParameter->DeviceId == Pci.Hdr.DeviceId)
) {
PciSerialCount++;
}
}
if (SerialDeviceCount == 0) {
//
// Enable the IO & MEM decoding when creating the first child.
// Restore the PCI attributes when all children is destroyed (PciDeviceInfo->ChildCount == 0).
//
PciDeviceInfo = AllocatePool (sizeof (PCI_DEVICE_INFO));
ASSERT (PciDeviceInfo != NULL);
PciDeviceInfo->ChildCount = 0;
PciDeviceInfo->PciIo = ParentIo.PciIo;
Status = ParentIo.PciIo->Attributes (
ParentIo.PciIo,
EfiPciIoAttributeOperationGet,
0,
&PciDeviceInfo->PciAttributes
);
if (!EFI_ERROR (Status)) {
Status = ParentIo.PciIo->Attributes (
ParentIo.PciIo,
EfiPciIoAttributeOperationSupported,
0,
&Supports
);
if (!EFI_ERROR (Status)) {
Supports &= (UINT64)(EFI_PCI_IO_ATTRIBUTE_IO | EFI_PCI_IO_ATTRIBUTE_MEMORY);
Status = ParentIo.PciIo->Attributes (
ParentIo.PciIo,
EfiPciIoAttributeOperationEnable,
Supports,
NULL
);
}
}
} else {
//
// Re-use the PciDeviceInfo stored in existing children.
//
ASSERT ((SerialDevices != NULL) && (SerialDevices[0] != NULL));
PciDeviceInfo = SerialDevices[0]->PciDeviceInfo;
ASSERT (PciDeviceInfo != NULL);
}
Status = EFI_NOT_FOUND;
if (PciSerialCount <= 1) {
//
// PCI serial device contains only one UART
//
if (RemainingDevicePath == NULL || !ContainsControllerNode) {
//
// This PCI serial device is matched by class code in Supported()
//
if (PciSerialCount == 0) {
DefaultPciSerialParameter.VendorId = Pci.Hdr.VendorId;
DefaultPciSerialParameter.DeviceId = Pci.Hdr.DeviceId;
DefaultPciSerialParameter.BarIndex = 0;
DefaultPciSerialParameter.Offset = 0;
DefaultPciSerialParameter.RegisterStride = 0;
DefaultPciSerialParameter.ClockRate = 0;
PciSerialParameter = &DefaultPciSerialParameter;
} else if (PciSerialCount == 1) {
PciSerialParameter = PcdGetPtr (PcdPciSerialParameters);
}
Status = CreateSerialDevice (Controller, Uart, ParentDevicePath, FALSE, 0, ParentIo, PciSerialParameter, PciDeviceInfo);
DEBUG ((EFI_D_INFO, "PciSioSerial: Create PCI child serial device (single) - %r\n", Status));
if (!EFI_ERROR (Status)) {
PciDeviceInfo->ChildCount++;
}
}
} else {
//
// PCI serial device contains multiple UARTs
//
if (RemainingDevicePath == NULL || ContainsControllerNode) {
PciSerialCount = 0;
for (PciSerialParameter = PcdGetPtr (PcdPciSerialParameters); PciSerialParameter->VendorId != 0xFFFF; PciSerialParameter++) {
if ((PciSerialParameter->VendorId == Pci.Hdr.VendorId) &&
(PciSerialParameter->DeviceId == Pci.Hdr.DeviceId) &&
((RemainingDevicePath == NULL) || (ControllerNumber == PciSerialCount))
) {
//
// Create controller node when PCI serial device contains multiple UARTs
//
Status = CreateSerialDevice (Controller, Uart, ParentDevicePath, TRUE, PciSerialCount, ParentIo, PciSerialParameter, PciDeviceInfo);
PciSerialCount++;
DEBUG ((EFI_D_INFO, "PciSioSerial: Create PCI child serial device (multiple) - %r\n", Status));
if (!EFI_ERROR (Status)) {
PciDeviceInfo->ChildCount++;
}
}
}
}
}
}
}
if (SerialDevices != NULL) {
FreePool (SerialDevices);
}
//
// For multiple PCI serial devices, set Status to SUCCESS if one child is created successfully
//
if ((PciDeviceInfo != NULL) && (PciDeviceInfo->ChildCount != 0)) {
Status = EFI_SUCCESS;
}
if (EFI_ERROR (Status) && (SerialDeviceCount == 0)) {
if (PciDeviceInfo != NULL) {
Status = ParentIo.PciIo->Attributes (
ParentIo.PciIo,
EfiPciIoAttributeOperationSet,
PciDeviceInfo->PciAttributes,
NULL
);
ASSERT_EFI_ERROR (Status);
FreePool (PciDeviceInfo);
}
gBS->CloseProtocol (
Controller,
&gEfiDevicePathProtocolGuid,
This->DriverBindingHandle,
Controller
);
gBS->CloseProtocol (
Controller,
IoProtocolGuid,
This->DriverBindingHandle,
Controller
);
}
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;
VOID *IoProtocol;
EFI_DEVICE_PATH_PROTOCOL *DevicePath;
PCI_DEVICE_INFO *PciDeviceInfo;
PciDeviceInfo = NULL;
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
);
if (NumberOfChildren == 0) {
//
// Close the bus driver
//
Status = gBS->OpenProtocol (
Controller,
&gEfiPciIoProtocolGuid,
&IoProtocol,
This->DriverBindingHandle,
Controller,
EFI_OPEN_PROTOCOL_TEST_PROTOCOL
);
gBS->CloseProtocol (
Controller,
!EFI_ERROR (Status) ? &gEfiPciIoProtocolGuid : &gEfiSioProtocolGuid,
This->DriverBindingHandle,
Controller
);
gBS->CloseProtocol (
Controller,
&gEfiDevicePathProtocolGuid,
This->DriverBindingHandle,
Controller
);
return EFI_SUCCESS;
}
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);
ASSERT ((PciDeviceInfo == NULL) || (PciDeviceInfo == SerialDevice->PciDeviceInfo));
PciDeviceInfo = SerialDevice->PciDeviceInfo;
Status = gBS->CloseProtocol (
Controller,
PciDeviceInfo != NULL ? &gEfiPciIoProtocolGuid : &gEfiSioProtocolGuid,
This->DriverBindingHandle,
ChildHandleBuffer[Index]
);
Status = gBS->UninstallMultipleProtocolInterfaces (
ChildHandleBuffer[Index],
&gEfiDevicePathProtocolGuid, SerialDevice->DevicePath,
&gEfiSerialIoProtocolGuid, &SerialDevice->SerialIo,
NULL
);
if (EFI_ERROR (Status)) {
gBS->OpenProtocol (
Controller,
PciDeviceInfo != NULL ? &gEfiPciIoProtocolGuid : &gEfiSioProtocolGuid,
&IoProtocol,
This->DriverBindingHandle,
ChildHandleBuffer[Index],
EFI_OPEN_PROTOCOL_BY_CHILD_CONTROLLER
);
} else {
FreePool (SerialDevice->DevicePath);
FreeUnicodeStringTable (SerialDevice->ControllerNameTable);
FreePool (SerialDevice);
if (PciDeviceInfo != NULL) {
ASSERT (PciDeviceInfo->ChildCount != 0);
PciDeviceInfo->ChildCount--;
}
}
}
if (EFI_ERROR (Status)) {
AllChildrenStopped = FALSE;
}
}
if (!AllChildrenStopped) {
return EFI_DEVICE_ERROR;
} else {
//
// If all children are destroyed, restore the PCI attributes.
//
if ((PciDeviceInfo != NULL) && (PciDeviceInfo->ChildCount == 0)) {
ASSERT (PciDeviceInfo->PciIo != NULL);
Status = PciDeviceInfo->PciIo->Attributes (
PciDeviceInfo->PciIo,
EfiPciIoAttributeOperationSet,
PciDeviceInfo->PciAttributes,
NULL
);
ASSERT_EFI_ERROR (Status);
FreePool (PciDeviceInfo);
}
return EFI_SUCCESS;
}
}