mirror of
https://github.com/CloverHackyColor/CloverBootloader.git
synced 2024-11-23 11:35:19 +01:00
6b33696c93
Signed-off-by: SergeySlice <sergey.slice@gmail.com>
1251 lines
43 KiB
C
1251 lines
43 KiB
C
/** @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 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;
|
|
}
|
|
}
|