| /** @file | |
| Serial driver for PCI or SIO UARTS. | |
| Copyright (c) 2006 - 2015, Intel Corporation. All rights reserved.<BR> | |
| This program and the accompanying materials | |
| are licensed and made available under the terms and conditions of the BSD License | |
| which accompanies this distribution. The full text of the license may be found at | |
| http://opensource.org/licenses/bsd-license.php | |
| THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, | |
| WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. | |
| **/ | |
| #include "Serial.h" | |
| // | |
| // ISA Serial Driver Global Variables | |
| // | |
| EFI_DRIVER_BINDING_PROTOCOL gSerialControllerDriver = { | |
| SerialControllerDriverSupported, | |
| SerialControllerDriverStart, | |
| SerialControllerDriverStop, | |
| 0xa, | |
| NULL, | |
| NULL | |
| }; | |
| CONTROLLER_DEVICE_PATH mControllerDevicePathTemplate = { | |
| { | |
| HARDWARE_DEVICE_PATH, | |
| HW_CONTROLLER_DP, | |
| { | |
| (UINT8) (sizeof (CONTROLLER_DEVICE_PATH)), | |
| (UINT8) ((sizeof (CONTROLLER_DEVICE_PATH)) >> 8) | |
| } | |
| }, | |
| 0 | |
| }; | |
| SERIAL_DEV gSerialDevTemplate = { | |
| SERIAL_DEV_SIGNATURE, | |
| NULL, | |
| { | |
| SERIAL_IO_INTERFACE_REVISION, | |
| SerialReset, | |
| SerialSetAttributes, | |
| SerialSetControl, | |
| SerialGetControl, | |
| SerialWrite, | |
| SerialRead, | |
| NULL | |
| }, // SerialIo | |
| { | |
| SERIAL_PORT_SUPPORT_CONTROL_MASK, | |
| SERIAL_PORT_DEFAULT_TIMEOUT, | |
| 0, | |
| 16, | |
| 0, | |
| 0, | |
| 0 | |
| }, // SerialMode | |
| NULL, // DevicePath | |
| NULL, // ParentDevicePath | |
| { | |
| { | |
| MESSAGING_DEVICE_PATH, | |
| MSG_UART_DP, | |
| { | |
| (UINT8) (sizeof (UART_DEVICE_PATH)), | |
| (UINT8) ((sizeof (UART_DEVICE_PATH)) >> 8) | |
| } | |
| }, | |
| 0, 0, 0, 0, 0 | |
| }, // UartDevicePath | |
| 0, // BaseAddress | |
| FALSE, // MmioAccess | |
| 1, // RegisterStride | |
| 0, // ClockRate | |
| 16, // ReceiveFifoDepth | |
| { 0, 0 }, // Receive; | |
| 16, // TransmitFifoDepth | |
| { 0, 0 }, // Transmit; | |
| FALSE, // SoftwareLoopbackEnable; | |
| FALSE, // HardwareFlowControl; | |
| NULL, // *ControllerNameTable; | |
| FALSE, // ContainsControllerNode; | |
| 0, // Instance; | |
| NULL // *PciDeviceInfo; | |
| }; | |
| /** | |
| Check the device path node whether it's the Flow Control node or not. | |
| @param[in] FlowControl The device path node to be checked. | |
| @retval TRUE It's the Flow Control node. | |
| @retval FALSE It's not. | |
| **/ | |
| BOOLEAN | |
| IsUartFlowControlDevicePathNode ( | |
| IN UART_FLOW_CONTROL_DEVICE_PATH *FlowControl | |
| ) | |
| { | |
| return (BOOLEAN) ( | |
| (DevicePathType (FlowControl) == MESSAGING_DEVICE_PATH) && | |
| (DevicePathSubType (FlowControl) == MSG_VENDOR_DP) && | |
| (CompareGuid (&FlowControl->Guid, &gEfiUartDevicePathGuid)) | |
| ); | |
| } | |
| /** | |
| The user Entry Point for module PciSioSerial. The user code starts with this function. | |
| @param[in] ImageHandle The firmware allocated handle for the EFI image. | |
| @param[in] SystemTable A pointer to the EFI System Table. | |
| @retval EFI_SUCCESS The entry point is executed successfully. | |
| @retval other Some error occurs when executing this entry point. | |
| **/ | |
| EFI_STATUS | |
| EFIAPI | |
| InitializePciSioSerial ( | |
| IN EFI_HANDLE ImageHandle, | |
| IN EFI_SYSTEM_TABLE *SystemTable | |
| ) | |
| { | |
| EFI_STATUS Status; | |
| // | |
| // Install driver model protocol(s). | |
| // | |
| Status = EfiLibInstallDriverBindingComponentName2 ( | |
| ImageHandle, | |
| SystemTable, | |
| &gSerialControllerDriver, | |
| ImageHandle, | |
| &gPciSioSerialComponentName, | |
| &gPciSioSerialComponentName2 | |
| ); | |
| ASSERT_EFI_ERROR (Status); | |
| // | |
| // Initialize UART default setting in gSerialDevTempate | |
| // | |
| gSerialDevTemplate.SerialMode.BaudRate = PcdGet64 (PcdUartDefaultBaudRate); | |
| gSerialDevTemplate.SerialMode.DataBits = PcdGet8 (PcdUartDefaultDataBits); | |
| gSerialDevTemplate.SerialMode.Parity = PcdGet8 (PcdUartDefaultParity); | |
| gSerialDevTemplate.SerialMode.StopBits = PcdGet8 (PcdUartDefaultStopBits); | |
| gSerialDevTemplate.UartDevicePath.BaudRate = PcdGet64 (PcdUartDefaultBaudRate); | |
| gSerialDevTemplate.UartDevicePath.DataBits = PcdGet8 (PcdUartDefaultDataBits); | |
| gSerialDevTemplate.UartDevicePath.Parity = PcdGet8 (PcdUartDefaultParity); | |
| gSerialDevTemplate.UartDevicePath.StopBits = PcdGet8 (PcdUartDefaultStopBits); | |
| gSerialDevTemplate.ClockRate = PcdGet32 (PcdSerialClockRate); | |
| return Status; | |
| } | |
| /** | |
| Return whether the controller is a SIO serial controller. | |
| @param Controller The controller handle. | |
| @retval EFI_SUCCESS The controller is a SIO serial controller. | |
| @retval others The controller is not a SIO serial controller. | |
| **/ | |
| EFI_STATUS | |
| IsSioSerialController ( | |
| EFI_HANDLE Controller | |
| ) | |
| { | |
| EFI_STATUS Status; | |
| EFI_SIO_PROTOCOL *Sio; | |
| EFI_DEVICE_PATH_PROTOCOL *DevicePath; | |
| ACPI_HID_DEVICE_PATH *Acpi; | |
| // | |
| // Open the IO Abstraction(s) needed to perform the supported test | |
| // | |
| Status = gBS->OpenProtocol ( | |
| Controller, | |
| &gEfiSioProtocolGuid, | |
| (VOID **) &Sio, | |
| gSerialControllerDriver.DriverBindingHandle, | |
| Controller, | |
| EFI_OPEN_PROTOCOL_BY_DRIVER | |
| ); | |
| if (Status == EFI_ALREADY_STARTED) { | |
| return EFI_SUCCESS; | |
| } | |
| if (!EFI_ERROR (Status)) { | |
| // | |
| // Close the I/O Abstraction(s) used to perform the supported test | |
| // | |
| gBS->CloseProtocol ( | |
| Controller, | |
| &gEfiSioProtocolGuid, | |
| gSerialControllerDriver.DriverBindingHandle, | |
| Controller | |
| ); | |
| Status = gBS->OpenProtocol ( | |
| Controller, | |
| &gEfiDevicePathProtocolGuid, | |
| (VOID **) &DevicePath, | |
| gSerialControllerDriver.DriverBindingHandle, | |
| Controller, | |
| EFI_OPEN_PROTOCOL_BY_DRIVER | |
| ); | |
| ASSERT (Status != EFI_ALREADY_STARTED); | |
| if (!EFI_ERROR (Status)) { | |
| do { | |
| Acpi = (ACPI_HID_DEVICE_PATH *) DevicePath; | |
| DevicePath = NextDevicePathNode (DevicePath); | |
| } while (!IsDevicePathEnd (DevicePath)); | |
| if (DevicePathType (Acpi) != ACPI_DEVICE_PATH || | |
| (DevicePathSubType (Acpi) != ACPI_DP && DevicePathSubType (Acpi) != ACPI_EXTENDED_DP) || | |
| Acpi->HID != EISA_PNP_ID (0x501) | |
| ) { | |
| Status = EFI_UNSUPPORTED; | |
| } | |
| } | |
| // | |
| // Close protocol, don't use device path protocol in the Support() function | |
| // | |
| gBS->CloseProtocol ( | |
| Controller, | |
| &gEfiDevicePathProtocolGuid, | |
| gSerialControllerDriver.DriverBindingHandle, | |
| Controller | |
| ); | |
| } | |
| return Status; | |
| } | |
| /** | |
| Return whether the controller is a PCI serial controller. | |
| @param Controller The controller handle. | |
| @retval EFI_SUCCESS The controller is a PCI serial controller. | |
| @retval others The controller is not a PCI serial controller. | |
| **/ | |
| EFI_STATUS | |
| IsPciSerialController ( | |
| EFI_HANDLE Controller | |
| ) | |
| { | |
| EFI_STATUS Status; | |
| EFI_PCI_IO_PROTOCOL *PciIo; | |
| EFI_DEVICE_PATH_PROTOCOL *DevicePath; | |
| PCI_TYPE00 Pci; | |
| PCI_SERIAL_PARAMETER *PciSerialParameter; | |
| // | |
| // Open the IO Abstraction(s) needed to perform the supported test | |
| // | |
| Status = gBS->OpenProtocol ( | |
| Controller, | |
| &gEfiPciIoProtocolGuid, | |
| (VOID **) &PciIo, | |
| gSerialControllerDriver.DriverBindingHandle, | |
| Controller, | |
| EFI_OPEN_PROTOCOL_BY_DRIVER | |
| ); | |
| if (Status == EFI_ALREADY_STARTED) { | |
| return EFI_SUCCESS; | |
| } | |
| if (!EFI_ERROR (Status)) { | |
| Status = PciIo->Pci.Read (PciIo, EfiPciIoWidthUint8, 0, sizeof (Pci), &Pci); | |
| if (!EFI_ERROR (Status)) { | |
| if (!IS_PCI_16550_SERIAL (&Pci)) { | |
| for (PciSerialParameter = (PCI_SERIAL_PARAMETER *) PcdGetPtr (PcdPciSerialParameters) | |
| ; PciSerialParameter->VendorId != 0xFFFF | |
| ; PciSerialParameter++ | |
| ) { | |
| if ((Pci.Hdr.VendorId == PciSerialParameter->VendorId) && | |
| (Pci.Hdr.DeviceId == PciSerialParameter->DeviceId) | |
| ) { | |
| break; | |
| } | |
| } | |
| if (PciSerialParameter->VendorId == 0xFFFF) { | |
| Status = EFI_UNSUPPORTED; | |
| } else { | |
| Status = EFI_SUCCESS; | |
| } | |
| } | |
| } | |
| // | |
| // Close the I/O Abstraction(s) used to perform the supported test | |
| // | |
| gBS->CloseProtocol ( | |
| Controller, | |
| &gEfiPciIoProtocolGuid, | |
| gSerialControllerDriver.DriverBindingHandle, | |
| Controller | |
| ); | |
| } | |
| if (EFI_ERROR (Status)) { | |
| return Status; | |
| } | |
| // | |
| // Open the EFI Device Path protocol needed to perform the supported test | |
| // | |
| Status = gBS->OpenProtocol ( | |
| Controller, | |
| &gEfiDevicePathProtocolGuid, | |
| (VOID **) &DevicePath, | |
| gSerialControllerDriver.DriverBindingHandle, | |
| Controller, | |
| EFI_OPEN_PROTOCOL_BY_DRIVER | |
| ); | |
| ASSERT (Status != EFI_ALREADY_STARTED); | |
| // | |
| // Close protocol, don't use device path protocol in the Support() function | |
| // | |
| gBS->CloseProtocol ( | |
| Controller, | |
| &gEfiDevicePathProtocolGuid, | |
| gSerialControllerDriver.DriverBindingHandle, | |
| Controller | |
| ); | |
| return Status; | |
| } | |
| /** | |
| Check to see if this driver supports the given controller | |
| @param This A pointer to the EFI_DRIVER_BINDING_PROTOCOL instance. | |
| @param Controller The handle of the controller to test. | |
| @param RemainingDevicePath A pointer to the remaining portion of a device path. | |
| @return EFI_SUCCESS This driver can support the given controller | |
| **/ | |
| EFI_STATUS | |
| EFIAPI | |
| SerialControllerDriverSupported ( | |
| IN EFI_DRIVER_BINDING_PROTOCOL *This, | |
| IN EFI_HANDLE Controller, | |
| IN EFI_DEVICE_PATH_PROTOCOL *RemainingDevicePath | |
| ) | |
| { | |
| EFI_STATUS Status; | |
| UART_DEVICE_PATH *Uart; | |
| UART_FLOW_CONTROL_DEVICE_PATH *FlowControl; | |
| // | |
| // Test RemainingDevicePath | |
| // | |
| if ((RemainingDevicePath != NULL) && !IsDevicePathEnd (RemainingDevicePath)) { | |
| Status = EFI_UNSUPPORTED; | |
| Uart = SkipControllerDevicePathNode (RemainingDevicePath, NULL, NULL); | |
| if (DevicePathType (Uart) != MESSAGING_DEVICE_PATH || | |
| DevicePathSubType (Uart) != MSG_UART_DP || | |
| DevicePathNodeLength (Uart) != sizeof (UART_DEVICE_PATH) | |
| ) { | |
| return EFI_UNSUPPORTED; | |
| } | |
| // | |
| // Do a rough check because Clock Rate is unknown until DriverBindingStart() | |
| // | |
| if (!VerifyUartParameters (0, Uart->BaudRate, Uart->DataBits, Uart->Parity, Uart->StopBits, NULL, NULL)) { | |
| return EFI_UNSUPPORTED; | |
| } | |
| FlowControl = (UART_FLOW_CONTROL_DEVICE_PATH *) NextDevicePathNode (Uart); | |
| if (IsUartFlowControlDevicePathNode (FlowControl)) { | |
| // | |
| // If the second node is Flow Control Node, | |
| // return error when it request other than hardware flow control. | |
| // | |
| if ((ReadUnaligned32 (&FlowControl->FlowControlMap) & ~UART_FLOW_CONTROL_HARDWARE) != 0) { | |
| return EFI_UNSUPPORTED; | |
| } | |
| } | |
| } | |
| Status = IsSioSerialController (Controller); | |
| if (EFI_ERROR (Status)) { | |
| Status = IsPciSerialController (Controller); | |
| } | |
| return Status; | |
| } | |
| /** | |
| Create the child serial device instance. | |
| @param Controller The parent controller handle. | |
| @param Uart Pointer to the UART device path node in RemainingDevicePath, | |
| or NULL if RemainingDevicePath is NULL. | |
| @param ParentDevicePath Pointer to the parent device path. | |
| @param CreateControllerNode TRUE to create the controller node. | |
| @param Instance Instance number of the serial device. | |
| The value will be set to the controller node | |
| if CreateControllerNode is TRUE. | |
| @param ParentIo A union type pointer to either Sio or PciIo. | |
| @param PciSerialParameter The PCI serial parameter to be used by current serial device. | |
| NULL for SIO serial device. | |
| @param PciDeviceInfo The PCI device info for the current serial device. | |
| NULL for SIO serial device. | |
| @retval EFI_SUCCESS The serial device was created successfully. | |
| @retval others The serial device wasn't created. | |
| **/ | |
| EFI_STATUS | |
| CreateSerialDevice ( | |
| IN EFI_HANDLE Controller, | |
| IN UART_DEVICE_PATH *Uart, | |
| IN EFI_DEVICE_PATH_PROTOCOL *ParentDevicePath, | |
| IN BOOLEAN CreateControllerNode, | |
| IN UINT32 Instance, | |
| IN PARENT_IO_PROTOCOL_PTR ParentIo, | |
| IN PCI_SERIAL_PARAMETER *PciSerialParameter, OPTIONAL | |
| IN PCI_DEVICE_INFO *PciDeviceInfo OPTIONAL | |
| ) | |
| { | |
| EFI_STATUS Status; | |
| SERIAL_DEV *SerialDevice; | |
| UINT8 BarIndex; | |
| UINT64 Offset; | |
| UART_FLOW_CONTROL_DEVICE_PATH *FlowControl; | |
| UINT32 FlowControlMap; | |
| ACPI_RESOURCE_HEADER_PTR Resources; | |
| EFI_ACPI_IO_PORT_DESCRIPTOR *Io; | |
| EFI_ACPI_FIXED_LOCATION_IO_PORT_DESCRIPTOR *FixedIo; | |
| EFI_ACPI_ADDRESS_SPACE_DESCRIPTOR *AddressSpace; | |
| EFI_DEVICE_PATH_PROTOCOL *TempDevicePath; | |
| BarIndex = 0; | |
| Offset = 0; | |
| FlowControl = NULL; | |
| FlowControlMap = 0; | |
| // | |
| // Initialize the serial device instance | |
| // | |
| SerialDevice = AllocateCopyPool (sizeof (SERIAL_DEV), &gSerialDevTemplate); | |
| ASSERT (SerialDevice != NULL); | |
| SerialDevice->SerialIo.Mode = &(SerialDevice->SerialMode); | |
| SerialDevice->ParentDevicePath = ParentDevicePath; | |
| SerialDevice->PciDeviceInfo = PciDeviceInfo; | |
| SerialDevice->Instance = Instance; | |
| if (Uart != NULL) { | |
| CopyMem (&SerialDevice->UartDevicePath, Uart, sizeof (UART_DEVICE_PATH)); | |
| FlowControl = (UART_FLOW_CONTROL_DEVICE_PATH *) NextDevicePathNode (Uart); | |
| if (IsUartFlowControlDevicePathNode (FlowControl)) { | |
| FlowControlMap = ReadUnaligned32 (&FlowControl->FlowControlMap); | |
| } else { | |
| FlowControl = NULL; | |
| } | |
| } | |
| // | |
| // For PCI serial device, use the information from PCD | |
| // | |
| if (PciSerialParameter != NULL) { | |
| BarIndex = (PciSerialParameter->BarIndex == PCI_BAR_ALL) ? 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 the SerialIo instance specified by RemainingDevicePath is already created, | |
| // update the attributes/control. | |
| // | |
| if ((SerialDeviceCount != 0) && (RemainingDevicePath != NULL)) { | |
| 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; | |
| } | |
| } |