/** @file Serial driver for PCI or SIO UARTS. Copyright (c) 2006 - 2018, Intel Corporation. All rights reserved.
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; } }