blob: 8b1ce70118c0266145b2cccad8ea625b6a91f2e5 [file] [log] [blame]
/** @file
Serial driver for PCI or SIO UARTS.
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
};
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 gSerialDevTemplate
//
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 ((DEBUG_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 ((DEBUG_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 ((DEBUG_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;
}
}