From 7f05fa00f73038b425002566d3afe6c3ade2ccdb Mon Sep 17 00:00:00 2001 From: Guo Mang Date: Thu, 22 Dec 2016 15:55:38 +0800 Subject: MdeModulePkg: Move to new location Contributed-under: TianoCore Contribution Agreement 1.0 Signed-off-by: Guo Mang --- Core/MdeModulePkg/Bus/Pci/PciSioSerialDxe/Serial.c | 1248 ++++++++++++++++++++ 1 file changed, 1248 insertions(+) create mode 100644 Core/MdeModulePkg/Bus/Pci/PciSioSerialDxe/Serial.c (limited to 'Core/MdeModulePkg/Bus/Pci/PciSioSerialDxe/Serial.c') diff --git a/Core/MdeModulePkg/Bus/Pci/PciSioSerialDxe/Serial.c b/Core/MdeModulePkg/Bus/Pci/PciSioSerialDxe/Serial.c new file mode 100644 index 0000000000..aeafee247c --- /dev/null +++ b/Core/MdeModulePkg/Bus/Pci/PciSioSerialDxe/Serial.c @@ -0,0 +1,1248 @@ +/** @file + Serial driver for PCI or SIO UARTS. + +Copyright (c) 2006 - 2015, Intel Corporation. All rights reserved.
+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; + } +} -- cgit v1.2.3