/** @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; | |
} | |
} |