/**@file Copyright (c) 2006 - 2010, Intel Corporation. All rights reserved.
SPDX-License-Identifier: BSD-2-Clause-Patent Module Name: WinNtSerialIo.c Abstract: Our DriverBinding member functions operate on the handles created by the NT Bus driver. Handle(1) - WinNtIo - DevicePath(1) If a serial port is added to the system this driver creates a new handle. The new handle is required, since the serial device must add an UART device pathnode. Handle(2) - SerialIo - DevicePath(1)\UART The driver then adds a gEfiWinNtSerialPortGuid as a protocol to Handle(1). The instance data for this protocol is the private data used to create Handle(2). Handle(1) - WinNtIo - DevicePath(1) - WinNtSerialPort If the driver is unloaded Handle(2) is removed from the system and gEfiWinNtSerialPortGuid is removed from Handle(1). Note: Handle(1) is any handle created by the Win NT Bus driver that is passed into the DriverBinding member functions of this driver. This driver requires a Handle(1) to contain a WinNtIo protocol, a DevicePath protocol, and the TypeGuid in the WinNtIo must be gEfiWinNtSerialPortGuid. If Handle(1) contains a gEfiWinNtSerialPortGuid protocol then the driver is loaded on the device. **/ #include "WinNtSerialIo.h" EFI_DRIVER_BINDING_PROTOCOL gWinNtSerialIoDriverBinding = { WinNtSerialIoDriverBindingSupported, WinNtSerialIoDriverBindingStart, WinNtSerialIoDriverBindingStop, 0xa, NULL, NULL }; // // List of supported baud rate // UINT64 mBaudRateCurrentSupport[] = {50, 75, 110, 134, 150, 300, 600, 1200, 1800, 2000, 2400, 3600, 4800, 7200, 9600, 19200, 38400, 57600, 115200, SERIAL_PORT_MAX_BAUD_RATE + 1}; /** 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 IsUartFlowControlNode ( IN UART_FLOW_CONTROL_DEVICE_PATH *FlowControl ) { return (BOOLEAN) ( (DevicePathType (FlowControl) == MESSAGING_DEVICE_PATH) && (DevicePathSubType (FlowControl) == MSG_VENDOR_DP) && (CompareGuid (&FlowControl->Guid, &gEfiUartDevicePathGuid)) ); } /** Check the device path node whether it contains Flow Control node or not. @param[in] DevicePath The device path to be checked. @retval TRUE It contains the Flow Control node. @retval FALSE It doesn't. **/ BOOLEAN ContainsFlowControl ( IN EFI_DEVICE_PATH_PROTOCOL *DevicePath ) { while (!IsDevicePathEnd (DevicePath)) { if (IsUartFlowControlNode ((UART_FLOW_CONTROL_DEVICE_PATH *) DevicePath)) { return TRUE; } DevicePath = NextDevicePathNode (DevicePath); } return FALSE; } /** The user Entry Point for module WinNtSerialIo. 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 InitializeWinNtSerialIo( IN EFI_HANDLE ImageHandle, IN EFI_SYSTEM_TABLE *SystemTable ) { EFI_STATUS Status; // // Install driver model protocol(s). // Status = EfiLibInstallDriverBindingComponentName2 ( ImageHandle, SystemTable, &gWinNtSerialIoDriverBinding, ImageHandle, &gWinNtSerialIoComponentName, &gWinNtSerialIoComponentName2 ); ASSERT_EFI_ERROR (Status); return Status; } EFI_STATUS EFIAPI WinNtSerialIoDriverBindingSupported ( IN EFI_DRIVER_BINDING_PROTOCOL *This, IN EFI_HANDLE Handle, IN EFI_DEVICE_PATH_PROTOCOL *RemainingDevicePath ) /*++ Routine Description: Arguments: Returns: None --*/ // TODO: This - add argument and description to function comment // TODO: Handle - add argument and description to function comment // TODO: RemainingDevicePath - add argument and description to function comment // TODO: EFI_SUCCESS - add return value to function comment // TODO: EFI_SUCCESS - add return value to function comment { EFI_STATUS Status; EFI_DEVICE_PATH_PROTOCOL *ParentDevicePath; EFI_WIN_NT_IO_PROTOCOL *WinNtIo; UART_DEVICE_PATH *UartNode; EFI_DEVICE_PATH_PROTOCOL *DevicePath; UART_FLOW_CONTROL_DEVICE_PATH *FlowControlNode; EFI_OPEN_PROTOCOL_INFORMATION_ENTRY *OpenInfoBuffer; UINTN EntryCount; UINTN Index; BOOLEAN RemainingDevicePathContainsFlowControl; // // Check RemainingDevicePath validation // if (RemainingDevicePath != NULL) { // // Check if RemainingDevicePath is the End of Device Path Node, // if yes, go on checking other conditions // if (!IsDevicePathEnd (RemainingDevicePath)) { // // If RemainingDevicePath isn't the End of Device Path Node, // check its validation // Status = EFI_UNSUPPORTED; UartNode = (UART_DEVICE_PATH *) RemainingDevicePath; if (UartNode->Header.Type != MESSAGING_DEVICE_PATH || UartNode->Header.SubType != MSG_UART_DP || DevicePathNodeLength((EFI_DEVICE_PATH_PROTOCOL *)UartNode) != sizeof(UART_DEVICE_PATH)) { goto Error; } if ( UartNode->BaudRate > SERIAL_PORT_MAX_BAUD_RATE) { goto Error; } if (UartNode->Parity < NoParity || UartNode->Parity > SpaceParity) { goto Error; } if (UartNode->DataBits < 5 || UartNode->DataBits > 8) { goto Error; } if (UartNode->StopBits < OneStopBit || UartNode->StopBits > TwoStopBits) { goto Error; } if ((UartNode->DataBits == 5) && (UartNode->StopBits == TwoStopBits)) { goto Error; } if ((UartNode->DataBits >= 6) && (UartNode->DataBits <= 8) && (UartNode->StopBits == OneFiveStopBits)) { goto Error; } FlowControlNode = (UART_FLOW_CONTROL_DEVICE_PATH *) NextDevicePathNode (UartNode); if (IsUartFlowControlNode (FlowControlNode)) { // // If the second node is Flow Control Node, // return error when it request other than hardware flow control. // if ((FlowControlNode->FlowControlMap & ~UART_FLOW_CONTROL_HARDWARE) != 0) { goto Error; } } } } // // Open the IO Abstraction(s) needed to perform the supported test // Status = gBS->OpenProtocol ( Handle, &gEfiWinNtIoProtocolGuid, (VOID **) &WinNtIo, This->DriverBindingHandle, Handle, EFI_OPEN_PROTOCOL_BY_DRIVER ); if (Status == EFI_ALREADY_STARTED) { if (RemainingDevicePath == NULL || IsDevicePathEnd (RemainingDevicePath)) { // // If RemainingDevicePath is NULL or is the End of Device Path Node // return EFI_SUCCESS; } // // When the driver has produced device path with flow control node but RemainingDevicePath only contains UART node, // return unsupported, and vice versa. // Status = gBS->OpenProtocolInformation ( Handle, &gEfiWinNtIoProtocolGuid, &OpenInfoBuffer, &EntryCount ); if (EFI_ERROR (Status)) { return Status; } // // See if RemainingDevicePath has a Flow Control device path node // RemainingDevicePathContainsFlowControl = ContainsFlowControl (RemainingDevicePath); for (Index = 0; Index < EntryCount; Index++) { if ((OpenInfoBuffer[Index].Attributes & EFI_OPEN_PROTOCOL_BY_CHILD_CONTROLLER) != 0) { Status = gBS->OpenProtocol ( OpenInfoBuffer[Index].ControllerHandle, &gEfiDevicePathProtocolGuid, (VOID **) &DevicePath, This->DriverBindingHandle, Handle, EFI_OPEN_PROTOCOL_GET_PROTOCOL ); if (!EFI_ERROR (Status)) { if (RemainingDevicePathContainsFlowControl ^ ContainsFlowControl (DevicePath)) { Status = EFI_UNSUPPORTED; } } break; } } FreePool (OpenInfoBuffer); return Status; } if (EFI_ERROR (Status)) { return Status; } // // Close the I/O Abstraction(s) used to perform the supported test // gBS->CloseProtocol ( Handle, &gEfiWinNtIoProtocolGuid, This->DriverBindingHandle, Handle ); // // Open the EFI Device Path protocol needed to perform the supported test // Status = gBS->OpenProtocol ( Handle, &gEfiDevicePathProtocolGuid, (VOID **) &ParentDevicePath, This->DriverBindingHandle, Handle, EFI_OPEN_PROTOCOL_BY_DRIVER ); if (Status == EFI_ALREADY_STARTED) { return EFI_SUCCESS; } if (EFI_ERROR (Status)) { return Status; } // // Close protocol, don't use device path protocol in the Support() function // gBS->CloseProtocol ( Handle, &gEfiDevicePathProtocolGuid, This->DriverBindingHandle, Handle ); // // Make sure that the WinNt Thunk Protocol is valid // if (WinNtIo->WinNtThunk->Signature != EFI_WIN_NT_THUNK_PROTOCOL_SIGNATURE) { Status = EFI_UNSUPPORTED; goto Error; } // // Check the GUID to see if this is a handle type the driver supports // if (!CompareGuid (WinNtIo->TypeGuid, &gEfiWinNtSerialPortGuid)) { Status = EFI_UNSUPPORTED; goto Error; } return EFI_SUCCESS; Error: return Status; } EFI_STATUS EFIAPI WinNtSerialIoDriverBindingStart ( IN EFI_DRIVER_BINDING_PROTOCOL *This, IN EFI_HANDLE Handle, IN EFI_DEVICE_PATH_PROTOCOL *RemainingDevicePath ) /*++ Routine Description: Arguments: Returns: None --*/ // TODO: This - add argument and description to function comment // TODO: Handle - add argument and description to function comment // TODO: RemainingDevicePath - add argument and description to function comment // TODO: EFI_SUCCESS - add return value to function comment // TODO: EFI_SUCCESS - add return value to function comment { EFI_STATUS Status; EFI_WIN_NT_IO_PROTOCOL *WinNtIo; WIN_NT_SERIAL_IO_PRIVATE_DATA *Private; HANDLE NtHandle; UART_DEVICE_PATH UartNode; EFI_DEVICE_PATH_PROTOCOL *ParentDevicePath; EFI_OPEN_PROTOCOL_INFORMATION_ENTRY *OpenInfoBuffer; UINTN EntryCount; UINTN Index; EFI_SERIAL_IO_PROTOCOL *SerialIo; UART_DEVICE_PATH *Uart; UINT32 FlowControlMap; UART_FLOW_CONTROL_DEVICE_PATH *FlowControl; EFI_DEVICE_PATH_PROTOCOL *TempDevicePath; UINT32 Control; Private = NULL; NtHandle = INVALID_HANDLE_VALUE; // // Get the Parent Device Path // Status = gBS->OpenProtocol ( Handle, &gEfiDevicePathProtocolGuid, (VOID **) &ParentDevicePath, This->DriverBindingHandle, Handle, EFI_OPEN_PROTOCOL_BY_DRIVER ); if (EFI_ERROR (Status) && Status != EFI_ALREADY_STARTED) { return Status; } // // Grab the IO abstraction we need to get any work done // Status = gBS->OpenProtocol ( Handle, &gEfiWinNtIoProtocolGuid, (VOID **) &WinNtIo, This->DriverBindingHandle, Handle, EFI_OPEN_PROTOCOL_BY_DRIVER ); if (EFI_ERROR (Status) && Status != EFI_ALREADY_STARTED) { gBS->CloseProtocol ( Handle, &gEfiDevicePathProtocolGuid, This->DriverBindingHandle, Handle ); return Status; } if (Status == EFI_ALREADY_STARTED) { if (RemainingDevicePath == NULL || IsDevicePathEnd (RemainingDevicePath)) { // // If RemainingDevicePath is NULL or is the End of Device Path Node // return EFI_SUCCESS; } // // Make sure a child handle does not already exist. This driver can only // produce one child per serial port. // Status = gBS->OpenProtocolInformation ( Handle, &gEfiWinNtIoProtocolGuid, &OpenInfoBuffer, &EntryCount ); if (EFI_ERROR (Status)) { return Status; } Status = EFI_ALREADY_STARTED; 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, This->DriverBindingHandle, Handle, EFI_OPEN_PROTOCOL_GET_PROTOCOL ); if (!EFI_ERROR (Status)) { Uart = (UART_DEVICE_PATH *) RemainingDevicePath; 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) && IsUartFlowControlNode (FlowControl)) { Status = SerialIo->GetControl (SerialIo, &Control); if (!EFI_ERROR (Status)) { if (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; } } FreePool (OpenInfoBuffer); return Status; } FlowControl = NULL; FlowControlMap = 0; if (RemainingDevicePath == NULL) { // // Build the device path by appending the UART node to the ParentDevicePath // from the WinNtIo handle. The Uart setings are zero here, since // SetAttribute() will update them to match the default setings. // ZeroMem (&UartNode, sizeof (UART_DEVICE_PATH)); UartNode.Header.Type = MESSAGING_DEVICE_PATH; UartNode.Header.SubType = MSG_UART_DP; SetDevicePathNodeLength ((EFI_DEVICE_PATH_PROTOCOL *) &UartNode, sizeof (UART_DEVICE_PATH)); } else if (!IsDevicePathEnd (RemainingDevicePath)) { // // If RemainingDevicePath isn't the End of Device Path Node, // only scan the specified device by RemainingDevicePath // // // Match the configuration of the RemainingDevicePath. IsHandleSupported() // already checked to make sure the RemainingDevicePath contains settings // that we can support. // CopyMem (&UartNode, RemainingDevicePath, sizeof (UART_DEVICE_PATH)); FlowControl = (UART_FLOW_CONTROL_DEVICE_PATH *) NextDevicePathNode (RemainingDevicePath); if (IsUartFlowControlNode (FlowControl)) { FlowControlMap = FlowControl->FlowControlMap; } else { FlowControl = NULL; } } else { // // If RemainingDevicePath is the End of Device Path Node, // skip enumerate any device and return EFI_SUCESSS // return EFI_SUCCESS; } // // Check to see if we can access the hardware device. If it's Open in NT we // will not get access. // NtHandle = WinNtIo->WinNtThunk->CreateFile ( WinNtIo->EnvString, GENERIC_READ | GENERIC_WRITE, 0, NULL, OPEN_EXISTING, 0, NULL ); if (NtHandle == INVALID_HANDLE_VALUE) { Status = EFI_DEVICE_ERROR; goto Error; } // // Construct Private data // Private = AllocatePool (sizeof (WIN_NT_SERIAL_IO_PRIVATE_DATA)); if (Private == NULL) { goto Error; } // // This signature must be valid before any member function is called // Private->Signature = WIN_NT_SERIAL_IO_PRIVATE_DATA_SIGNATURE; Private->NtHandle = NtHandle; Private->ControllerHandle = Handle; Private->Handle = NULL; Private->WinNtThunk = WinNtIo->WinNtThunk; Private->ParentDevicePath = ParentDevicePath; Private->ControllerNameTable = NULL; Private->SoftwareLoopbackEnable = FALSE; Private->HardwareLoopbackEnable = FALSE; Private->HardwareFlowControl = (BOOLEAN) (FlowControlMap == UART_FLOW_CONTROL_HARDWARE); Private->Fifo.First = 0; Private->Fifo.Last = 0; Private->Fifo.Surplus = SERIAL_MAX_BUFFER_SIZE; CopyMem (&Private->UartDevicePath, &UartNode, sizeof (UART_DEVICE_PATH)); AddUnicodeString2 ( "eng", gWinNtSerialIoComponentName.SupportedLanguages, &Private->ControllerNameTable, WinNtIo->EnvString, TRUE ); AddUnicodeString2 ( "en", gWinNtSerialIoComponentName2.SupportedLanguages, &Private->ControllerNameTable, WinNtIo->EnvString, FALSE ); Private->SerialIo.Revision = SERIAL_IO_INTERFACE_REVISION; Private->SerialIo.Reset = WinNtSerialIoReset; Private->SerialIo.SetAttributes = WinNtSerialIoSetAttributes; Private->SerialIo.SetControl = WinNtSerialIoSetControl; Private->SerialIo.GetControl = WinNtSerialIoGetControl; Private->SerialIo.Write = WinNtSerialIoWrite; Private->SerialIo.Read = WinNtSerialIoRead; Private->SerialIo.Mode = &Private->SerialIoMode; // // Build the device path by appending the UART node to the ParentDevicePath // from the WinNtIo handle. The Uart setings are zero here, since // SetAttribute() will update them to match the current setings. // Private->DevicePath = AppendDevicePathNode ( ParentDevicePath, (EFI_DEVICE_PATH_PROTOCOL *) &Private->UartDevicePath ); // // Only produce the FlowControl node when remaining device path has it // if (FlowControl != NULL) { TempDevicePath = Private->DevicePath; if (TempDevicePath != NULL) { Private->DevicePath = AppendDevicePathNode ( TempDevicePath, (EFI_DEVICE_PATH_PROTOCOL *) FlowControl ); FreePool (TempDevicePath); } } if (Private->DevicePath == NULL) { Status = EFI_OUT_OF_RESOURCES; goto Error; } // // Fill in Serial I/O Mode structure based on either the RemainingDevicePath or defaults. // Private->SerialIoMode.ControlMask = SERIAL_CONTROL_MASK; Private->SerialIoMode.Timeout = SERIAL_TIMEOUT_DEFAULT; Private->SerialIoMode.BaudRate = Private->UartDevicePath.BaudRate; Private->SerialIoMode.ReceiveFifoDepth = SERIAL_FIFO_DEFAULT; Private->SerialIoMode.DataBits = Private->UartDevicePath.DataBits; Private->SerialIoMode.Parity = Private->UartDevicePath.Parity; Private->SerialIoMode.StopBits = Private->UartDevicePath.StopBits; // // Issue a reset to initialize the COM port // Status = Private->SerialIo.Reset (&Private->SerialIo); if (EFI_ERROR (Status)) { goto Error; } // // Create new child handle // Status = gBS->InstallMultipleProtocolInterfaces ( &Private->Handle, &gEfiSerialIoProtocolGuid, &Private->SerialIo, &gEfiDevicePathProtocolGuid, Private->DevicePath, NULL ); if (EFI_ERROR (Status)) { goto Error; } // // Open For Child Device // Status = gBS->OpenProtocol ( Handle, &gEfiWinNtIoProtocolGuid, (VOID **) &WinNtIo, This->DriverBindingHandle, Private->Handle, EFI_OPEN_PROTOCOL_BY_CHILD_CONTROLLER ); if (EFI_ERROR (Status)) { goto Error; } return EFI_SUCCESS; Error: // // Use the Stop() function to free all resources allocated in Start() // if (Private != NULL) { if (Private->Handle != NULL) { This->Stop (This, Handle, 1, &Private->Handle); } else { if (NtHandle != INVALID_HANDLE_VALUE) { Private->WinNtThunk->CloseHandle (NtHandle); } if (Private->DevicePath != NULL) { FreePool (Private->DevicePath); } FreeUnicodeStringTable (Private->ControllerNameTable); FreePool (Private); } } This->Stop (This, Handle, 0, NULL); return Status; } EFI_STATUS EFIAPI WinNtSerialIoDriverBindingStop ( IN EFI_DRIVER_BINDING_PROTOCOL *This, IN EFI_HANDLE Handle, IN UINTN NumberOfChildren, IN EFI_HANDLE *ChildHandleBuffer ) /*++ Routine Description: TODO: Add function description Arguments: This - TODO: add argument description Handle - TODO: add argument description NumberOfChildren - TODO: add argument description ChildHandleBuffer - TODO: add argument description Returns: EFI_DEVICE_ERROR - TODO: Add description for return value EFI_SUCCESS - TODO: Add description for return value --*/ { EFI_STATUS Status; UINTN Index; BOOLEAN AllChildrenStopped; EFI_SERIAL_IO_PROTOCOL *SerialIo; WIN_NT_SERIAL_IO_PRIVATE_DATA *Private; EFI_WIN_NT_IO_PROTOCOL *WinNtIo; // // Complete all outstanding transactions to Controller. // Don't allow any new transaction to Controller to be started. // if (NumberOfChildren == 0) { // // Close the bus driver // Status = gBS->CloseProtocol ( Handle, &gEfiWinNtIoProtocolGuid, This->DriverBindingHandle, Handle ); Status = gBS->CloseProtocol ( Handle, &gEfiDevicePathProtocolGuid, This->DriverBindingHandle, Handle ); return Status; } AllChildrenStopped = TRUE; for (Index = 0; Index < NumberOfChildren; Index++) { Status = gBS->OpenProtocol ( ChildHandleBuffer[Index], &gEfiSerialIoProtocolGuid, (VOID **) &SerialIo, This->DriverBindingHandle, Handle, EFI_OPEN_PROTOCOL_GET_PROTOCOL ); if (!EFI_ERROR (Status)) { Private = WIN_NT_SERIAL_IO_PRIVATE_DATA_FROM_THIS (SerialIo); ASSERT (Private->Handle == ChildHandleBuffer[Index]); Status = gBS->CloseProtocol ( Handle, &gEfiWinNtIoProtocolGuid, This->DriverBindingHandle, ChildHandleBuffer[Index] ); Status = gBS->UninstallMultipleProtocolInterfaces ( ChildHandleBuffer[Index], &gEfiSerialIoProtocolGuid, &Private->SerialIo, &gEfiDevicePathProtocolGuid, Private->DevicePath, NULL ); if (EFI_ERROR (Status)) { gBS->OpenProtocol ( Handle, &gEfiWinNtIoProtocolGuid, (VOID **) &WinNtIo, This->DriverBindingHandle, ChildHandleBuffer[Index], EFI_OPEN_PROTOCOL_BY_CHILD_CONTROLLER ); } else { Private->WinNtThunk->CloseHandle (Private->NtHandle); FreePool (Private->DevicePath); FreeUnicodeStringTable (Private->ControllerNameTable); FreePool (Private); } } if (EFI_ERROR (Status)) { AllChildrenStopped = FALSE; } } if (!AllChildrenStopped) { return EFI_DEVICE_ERROR; } return EFI_SUCCESS; } // // Serial IO Protocol member functions // EFI_STATUS EFIAPI WinNtSerialIoReset ( IN EFI_SERIAL_IO_PROTOCOL *This ) /*++ Routine Description: TODO: Add function description Arguments: This - TODO: add argument description Returns: TODO: add return values --*/ { WIN_NT_SERIAL_IO_PRIVATE_DATA *Private; EFI_TPL Tpl; Tpl = gBS->RaiseTPL (TPL_NOTIFY); Private = WIN_NT_SERIAL_IO_PRIVATE_DATA_FROM_THIS (This); Private->WinNtThunk->PurgeComm ( Private->NtHandle, PURGE_TXCLEAR | PURGE_RXCLEAR ); gBS->RestoreTPL (Tpl); return This->SetAttributes ( This, This->Mode->BaudRate, This->Mode->ReceiveFifoDepth, This->Mode->Timeout, (EFI_PARITY_TYPE)This->Mode->Parity, (UINT8) This->Mode->DataBits, (EFI_STOP_BITS_TYPE)This->Mode->StopBits ); } EFI_STATUS EFIAPI WinNtSerialIoSetAttributes ( IN EFI_SERIAL_IO_PROTOCOL *This, IN UINT64 BaudRate, IN UINT32 ReceiveFifoDepth, IN UINT32 Timeout, IN EFI_PARITY_TYPE Parity, IN UINT8 DataBits, IN EFI_STOP_BITS_TYPE StopBits ) /*++ Routine Description: This function is used to set the attributes. Arguments: This - A pointer to the EFI_SERIAL_IO_PROTOCOL structrue. BaudRate - The Baud rate of the serial device. ReceiveFifoDepth - The request depth of fifo on receive side. Timeout - the request timeout for a single charact. Parity - The type of parity used in serial device. DataBits - Number of deata bits used in serial device. StopBits - Number of stop bits used in serial device. Returns: Status code None --*/ // TODO: EFI_SUCCESS - add return value to function comment // TODO: EFI_DEVICE_ERROR - add return value to function comment // TODO: EFI_DEVICE_ERROR - add return value to function comment // TODO: EFI_DEVICE_ERROR - add return value to function comment // TODO: EFI_SUCCESS - add return value to function comment // TODO: EFI_DEVICE_ERROR - add return value to function comment // TODO: EFI_SUCCESS - add return value to function comment { EFI_STATUS Status; UINTN Index; WIN_NT_SERIAL_IO_PRIVATE_DATA *Private; COMMTIMEOUTS PortTimeOuts; DWORD ConvertedTime; BOOL Result; UART_DEVICE_PATH *Uart; EFI_TPL Tpl; Private = WIN_NT_SERIAL_IO_PRIVATE_DATA_FROM_THIS (This); // // Some of our arguments have defaults if a null value is passed in, and // we must set the default values if a null argument is passed in. // if (BaudRate == 0) { BaudRate = PcdGet64 (PcdUartDefaultBaudRate); } if (ReceiveFifoDepth == 0) { ReceiveFifoDepth = SERIAL_FIFO_DEFAULT; } if (Timeout == 0) { Timeout = SERIAL_TIMEOUT_DEFAULT; } if (Parity == DefaultParity) { Parity = (EFI_PARITY_TYPE) (PcdGet8 (PcdUartDefaultParity)); } if (DataBits == 0) { DataBits = PcdGet8 (PcdUartDefaultDataBits); } if (StopBits == DefaultStopBits) { StopBits = (EFI_STOP_BITS_TYPE) PcdGet8 (PcdUartDefaultStopBits); } // // Make sure all parameters are valid // if ((BaudRate > SERIAL_PORT_MAX_BAUD_RATE) || (BaudRate < SERIAL_PORT_MIN_BAUD_RATE)) { return EFI_INVALID_PARAMETER; } // //The lower baud rate supported by the serial device will be selected without exceeding the unsupported BaudRate parameter // for (Index = 1; Index < (ARRAY_SIZE (mBaudRateCurrentSupport)); Index++) { if (BaudRate < mBaudRateCurrentSupport[Index]) { BaudRate = mBaudRateCurrentSupport[Index-1]; break; } } if ((ReceiveFifoDepth < 1) || (ReceiveFifoDepth > SERIAL_PORT_MAX_RECEIVE_FIFO_DEPTH)) { return EFI_INVALID_PARAMETER; } if ((Timeout < SERIAL_PORT_MIN_TIMEOUT) || (Timeout > SERIAL_PORT_MAX_TIMEOUT)) { return EFI_INVALID_PARAMETER; } if ((Parity < NoParity) || (Parity > SpaceParity)) { return EFI_INVALID_PARAMETER; } if ((StopBits < OneStopBit) || (StopBits > TwoStopBits)) { return EFI_INVALID_PARAMETER; } // // Now we only support DataBits=7,8. // if ((DataBits < 7) || (DataBits > 8)) { return EFI_INVALID_PARAMETER; } // // Now we only support DataBits=7,8. // for DataBits = 6,7,8, StopBits can not set OneFiveStopBits. // if (StopBits == OneFiveStopBits) { return EFI_INVALID_PARAMETER; } // // See if the new attributes already match the current attributes // if (Private->UartDevicePath.BaudRate == BaudRate && Private->UartDevicePath.DataBits == DataBits && Private->UartDevicePath.Parity == Parity && Private->UartDevicePath.StopBits == StopBits && Private->SerialIoMode.ReceiveFifoDepth == ReceiveFifoDepth && Private->SerialIoMode.Timeout == Timeout ) { return EFI_SUCCESS; } Tpl = gBS->RaiseTPL (TPL_NOTIFY); // // Get current values from NT // ZeroMem (&Private->NtDCB, sizeof (DCB)); Private->NtDCB.DCBlength = sizeof (DCB); if (!Private->WinNtThunk->GetCommState (Private->NtHandle, &Private->NtDCB)) { Private->NtError = Private->WinNtThunk->GetLastError (); DEBUG ((EFI_D_ERROR, "SerialSetAttributes: GetCommState %d\n", Private->NtError)); gBS->RestoreTPL (Tpl); return EFI_DEVICE_ERROR; } // // Map EFI com setting to NT // Private->NtDCB.BaudRate = ConvertBaud2Nt (BaudRate); Private->NtDCB.ByteSize = ConvertData2Nt (DataBits); Private->NtDCB.Parity = ConvertParity2Nt (Parity); Private->NtDCB.StopBits = ConvertStop2Nt (StopBits); Private->NtDCB.fBinary = TRUE; Private->NtDCB.fParity = Private->NtDCB.Parity == NOPARITY ? FALSE : TRUE; Private->NtDCB.fOutxCtsFlow = FALSE; Private->NtDCB.fOutxDsrFlow = FALSE; Private->NtDCB.fDtrControl = DTR_CONTROL_ENABLE; Private->NtDCB.fDsrSensitivity = FALSE; Private->NtDCB.fOutX = FALSE; Private->NtDCB.fInX = FALSE; Private->NtDCB.fRtsControl = RTS_CONTROL_ENABLE; Private->NtDCB.fNull = FALSE; // // Set new values // Result = Private->WinNtThunk->SetCommState (Private->NtHandle, &Private->NtDCB); if (!Result) { Private->NtError = Private->WinNtThunk->GetLastError (); DEBUG ((EFI_D_ERROR, "SerialSetAttributes: SetCommState %d\n", Private->NtError)); gBS->RestoreTPL (Tpl); return EFI_DEVICE_ERROR; } // // Set com port read/write timeout values // ConvertedTime = ConvertTime2Nt (Timeout); PortTimeOuts.ReadIntervalTimeout = MAXDWORD; PortTimeOuts.ReadTotalTimeoutMultiplier = 0; PortTimeOuts.ReadTotalTimeoutConstant = ConvertedTime; PortTimeOuts.WriteTotalTimeoutMultiplier = ConvertedTime == 0 ? 1 : ConvertedTime; PortTimeOuts.WriteTotalTimeoutConstant = 0; if (!Private->WinNtThunk->SetCommTimeouts (Private->NtHandle, &PortTimeOuts)) { Private->NtError = Private->WinNtThunk->GetLastError (); DEBUG ((EFI_D_ERROR, "SerialSetAttributes: SetCommTimeouts %d\n", Private->NtError)); gBS->RestoreTPL (Tpl); return EFI_DEVICE_ERROR; } // // Update mode // Private->SerialIoMode.BaudRate = BaudRate; Private->SerialIoMode.ReceiveFifoDepth = ReceiveFifoDepth; Private->SerialIoMode.Timeout = Timeout; Private->SerialIoMode.Parity = Parity; Private->SerialIoMode.DataBits = DataBits; Private->SerialIoMode.StopBits = StopBits; // // See if Device Path Node has actually changed // if (Private->UartDevicePath.BaudRate == BaudRate && Private->UartDevicePath.DataBits == DataBits && Private->UartDevicePath.Parity == Parity && Private->UartDevicePath.StopBits == StopBits ) { gBS->RestoreTPL(Tpl); return EFI_SUCCESS; } // // Update the device path // Private->UartDevicePath.BaudRate = BaudRate; Private->UartDevicePath.DataBits = DataBits; Private->UartDevicePath.Parity = (UINT8) Parity; Private->UartDevicePath.StopBits = (UINT8) StopBits; Status = EFI_SUCCESS; if (Private->Handle != NULL) { Uart = (UART_DEVICE_PATH *) ( (UINTN) Private->DevicePath + GetDevicePathSize (Private->ParentDevicePath) - END_DEVICE_PATH_LENGTH ); CopyMem (Uart, &Private->UartDevicePath, sizeof (UART_DEVICE_PATH)); Status = gBS->ReinstallProtocolInterface ( Private->Handle, &gEfiDevicePathProtocolGuid, Private->DevicePath, Private->DevicePath ); } gBS->RestoreTPL (Tpl); return Status; } EFI_STATUS EFIAPI WinNtSerialIoSetControl ( IN EFI_SERIAL_IO_PROTOCOL *This, IN UINT32 Control ) /*++ Routine Description: TODO: Add function description Arguments: This - TODO: add argument description Control - TODO: add argument description Returns: EFI_DEVICE_ERROR - TODO: Add description for return value EFI_DEVICE_ERROR - TODO: Add description for return value EFI_SUCCESS - TODO: Add description for return value --*/ { WIN_NT_SERIAL_IO_PRIVATE_DATA *Private; BOOL Result; DCB Dcb; EFI_TPL Tpl; UART_FLOW_CONTROL_DEVICE_PATH *FlowControl; EFI_STATUS Status; // // first determine the parameter is invalid // if (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))) { return EFI_UNSUPPORTED; } Tpl = gBS->RaiseTPL (TPL_NOTIFY); Private = WIN_NT_SERIAL_IO_PRIVATE_DATA_FROM_THIS (This); Result = Private->WinNtThunk->GetCommState (Private->NtHandle, &Dcb); if (!Result) { Private->NtError = Private->WinNtThunk->GetLastError (); DEBUG ((EFI_D_ERROR, "SerialSetControl: GetCommState %d\n", Private->NtError)); gBS->RestoreTPL (Tpl); return EFI_DEVICE_ERROR; } Dcb.fRtsControl = RTS_CONTROL_DISABLE; Dcb.fDtrControl = DTR_CONTROL_DISABLE; Private->HardwareFlowControl = FALSE; Private->SoftwareLoopbackEnable = FALSE; Private->HardwareLoopbackEnable = FALSE; if (Control & EFI_SERIAL_REQUEST_TO_SEND) { Dcb.fRtsControl = RTS_CONTROL_ENABLE; } if (Control & EFI_SERIAL_DATA_TERMINAL_READY) { Dcb.fDtrControl = DTR_CONTROL_ENABLE; } if (Control & EFI_SERIAL_HARDWARE_FLOW_CONTROL_ENABLE) { Private->HardwareFlowControl = TRUE; } if (Control & EFI_SERIAL_SOFTWARE_LOOPBACK_ENABLE) { Private->SoftwareLoopbackEnable = TRUE; } if (Control & EFI_SERIAL_HARDWARE_LOOPBACK_ENABLE) { Private->HardwareLoopbackEnable = TRUE; } Result = Private->WinNtThunk->SetCommState ( Private->NtHandle, &Dcb ); if (!Result) { Private->NtError = Private->WinNtThunk->GetLastError (); DEBUG ((EFI_D_ERROR, "SerialSetControl: SetCommState %d\n", Private->NtError)); gBS->RestoreTPL (Tpl); return EFI_DEVICE_ERROR; } Status = EFI_SUCCESS; if (Private->Handle != NULL) { FlowControl = (UART_FLOW_CONTROL_DEVICE_PATH *) ( (UINTN) Private->DevicePath + GetDevicePathSize (Private->ParentDevicePath) - END_DEVICE_PATH_LENGTH + sizeof (UART_DEVICE_PATH) ); if (IsUartFlowControlNode (FlowControl) && ((FlowControl->FlowControlMap == UART_FLOW_CONTROL_HARDWARE) ^ Private->HardwareFlowControl)) { // // Flow Control setting is changed, need to reinstall device path protocol // FlowControl->FlowControlMap = Private->HardwareFlowControl ? UART_FLOW_CONTROL_HARDWARE : 0; Status = gBS->ReinstallProtocolInterface ( Private->Handle, &gEfiDevicePathProtocolGuid, Private->DevicePath, Private->DevicePath ); } } gBS->RestoreTPL (Tpl); return Status; } EFI_STATUS EFIAPI WinNtSerialIoGetControl ( IN EFI_SERIAL_IO_PROTOCOL *This, OUT UINT32 *Control ) /*++ Routine Description: TODO: Add function description Arguments: This - TODO: add argument description Control - TODO: add argument description Returns: EFI_DEVICE_ERROR - TODO: Add description for return value EFI_DEVICE_ERROR - TODO: Add description for return value EFI_DEVICE_ERROR - TODO: Add description for return value EFI_SUCCESS - TODO: Add description for return value --*/ { WIN_NT_SERIAL_IO_PRIVATE_DATA *Private; DWORD ModemStatus; DWORD Errors; UINT32 Bits; DCB Dcb; EFI_TPL Tpl; Tpl = gBS->RaiseTPL (TPL_NOTIFY); Private = WIN_NT_SERIAL_IO_PRIVATE_DATA_FROM_THIS (This); // // Get modem status // if (!Private->WinNtThunk->GetCommModemStatus (Private->NtHandle, &ModemStatus)) { Private->NtError = Private->WinNtThunk->GetLastError (); gBS->RestoreTPL (Tpl); return EFI_DEVICE_ERROR; } Bits = 0; if (ModemStatus & MS_CTS_ON) { Bits |= EFI_SERIAL_CLEAR_TO_SEND; } if (ModemStatus & MS_DSR_ON) { Bits |= EFI_SERIAL_DATA_SET_READY; } if (ModemStatus & MS_RING_ON) { Bits |= EFI_SERIAL_RING_INDICATE; } if (ModemStatus & MS_RLSD_ON) { Bits |= EFI_SERIAL_CARRIER_DETECT; } // // Get ctrl status // if (!Private->WinNtThunk->GetCommState (Private->NtHandle, &Dcb)) { Private->NtError = Private->WinNtThunk->GetLastError (); DEBUG ((EFI_D_ERROR, "SerialGetControl: GetCommState %d\n", Private->NtError)); gBS->RestoreTPL (Tpl); return EFI_DEVICE_ERROR; } if (Dcb.fDtrControl == DTR_CONTROL_ENABLE) { Bits |= EFI_SERIAL_DATA_TERMINAL_READY; } if (Dcb.fRtsControl == RTS_CONTROL_ENABLE) { Bits |= EFI_SERIAL_REQUEST_TO_SEND; } if (Private->HardwareFlowControl) { Bits |= EFI_SERIAL_HARDWARE_FLOW_CONTROL_ENABLE; } if (Private->SoftwareLoopbackEnable) { Bits |= EFI_SERIAL_SOFTWARE_LOOPBACK_ENABLE; } if (Private->HardwareLoopbackEnable) { Bits |= EFI_SERIAL_HARDWARE_LOOPBACK_ENABLE; } // // Get input buffer status // if (!Private->WinNtThunk->ClearCommError (Private->NtHandle, &Errors, &Private->NtComStatus)) { Private->NtError = Private->WinNtThunk->GetLastError (); DEBUG ((EFI_D_ERROR, "SerialGetControl: ClearCommError %d\n", Private->NtError)); gBS->RestoreTPL (Tpl); return EFI_DEVICE_ERROR; } if (Private->NtComStatus.cbInQue == 0) { Bits |= EFI_SERIAL_INPUT_BUFFER_EMPTY; } *Control = Bits; gBS->RestoreTPL (Tpl); return EFI_SUCCESS; } EFI_STATUS EFIAPI WinNtSerialIoWrite ( IN EFI_SERIAL_IO_PROTOCOL *This, IN OUT UINTN *BufferSize, IN VOID *Buffer ) /*++ Routine Description: TODO: Add function description Arguments: This - TODO: add argument description BufferSize - TODO: add argument description Buffer - TODO: add argument description Returns: EFI_DEVICE_ERROR - TODO: Add description for return value EFI_SUCCESS - TODO: Add description for return value --*/ { WIN_NT_SERIAL_IO_PRIVATE_DATA *Private; UINT8 *ByteBuffer; UINTN TotalBytesWritten; DWORD BytesToGo; DWORD BytesWritten; BOOL Result; UINT32 Index; UINT32 Control; EFI_TPL Tpl; Tpl = gBS->RaiseTPL (TPL_NOTIFY); Private = WIN_NT_SERIAL_IO_PRIVATE_DATA_FROM_THIS (This); ByteBuffer = (UINT8 *) Buffer; TotalBytesWritten = 0; if (Private->SoftwareLoopbackEnable || Private->HardwareLoopbackEnable) { for (Index = 0; Index < *BufferSize; Index++) { if (IsaSerialFifoAdd (&Private->Fifo, ByteBuffer[Index]) == EFI_SUCCESS) { TotalBytesWritten++; } else { break; } } } else { BytesToGo = (DWORD) (*BufferSize); do { if (Private->HardwareFlowControl) { // // Send RTS // WinNtSerialIoGetControl (&Private->SerialIo, &Control); Control |= EFI_SERIAL_REQUEST_TO_SEND; WinNtSerialIoSetControl (&Private->SerialIo, Control); } // // Do the write // Result = Private->WinNtThunk->WriteFile ( Private->NtHandle, &ByteBuffer[TotalBytesWritten], BytesToGo, &BytesWritten, NULL ); if (Private->HardwareFlowControl) { // // Assert RTS // WinNtSerialIoGetControl (&Private->SerialIo, &Control); Control &= ~ (UINT32) EFI_SERIAL_REQUEST_TO_SEND; WinNtSerialIoSetControl (&Private->SerialIo, Control); } TotalBytesWritten += BytesWritten; BytesToGo -= BytesWritten; if (!Result) { Private->NtError = Private->WinNtThunk->GetLastError (); DEBUG ((EFI_D_ERROR, "SerialWrite: FileWrite %d\n", Private->NtError)); *BufferSize = TotalBytesWritten; gBS->RestoreTPL (Tpl); return EFI_DEVICE_ERROR; } } while (BytesToGo > 0); } *BufferSize = TotalBytesWritten; gBS->RestoreTPL (Tpl); return EFI_SUCCESS; } EFI_STATUS EFIAPI WinNtSerialIoRead ( IN EFI_SERIAL_IO_PROTOCOL *This, IN OUT UINTN *BufferSize, OUT VOID *Buffer ) /*++ Routine Description: TODO: Add function description Arguments: This - TODO: add argument description BufferSize - TODO: add argument description Buffer - TODO: add argument description Returns: EFI_DEVICE_ERROR - TODO: Add description for return value --*/ { WIN_NT_SERIAL_IO_PRIVATE_DATA *Private; BOOL Result; DWORD BytesRead; EFI_STATUS Status; UINT32 Index; UINT8 Data; UINT32 Control; EFI_TPL Tpl; Tpl = gBS->RaiseTPL (TPL_NOTIFY); Private = WIN_NT_SERIAL_IO_PRIVATE_DATA_FROM_THIS (This); // // Do the read // if (Private->SoftwareLoopbackEnable || Private->HardwareLoopbackEnable) { for (Index = 0, BytesRead = 0; Index < *BufferSize; Index++) { if (IsaSerialFifoRemove (&Private->Fifo, &Data) == EFI_SUCCESS) { ((UINT8 *) Buffer)[Index] = Data; BytesRead++; } else { break; } } } else { if (Private->HardwareFlowControl) { WinNtSerialIoGetControl (&Private->SerialIo, &Control); Control |= EFI_SERIAL_DATA_TERMINAL_READY; WinNtSerialIoSetControl (&Private->SerialIo, Control); } Result = Private->WinNtThunk->ReadFile ( Private->NtHandle, Buffer, (DWORD) *BufferSize, &BytesRead, NULL ); if (Private->HardwareFlowControl) { WinNtSerialIoGetControl (&Private->SerialIo, &Control); Control &= ~ (UINT32) EFI_SERIAL_DATA_TERMINAL_READY; WinNtSerialIoSetControl (&Private->SerialIo, Control); } if (!Result) { Private->NtError = Private->WinNtThunk->GetLastError (); gBS->RestoreTPL (Tpl); return EFI_DEVICE_ERROR; } } if (BytesRead != *BufferSize) { Status = EFI_TIMEOUT; } else { Status = EFI_SUCCESS; } *BufferSize = (UINTN) BytesRead; gBS->RestoreTPL (Tpl); return Status; } BOOLEAN IsaSerialFifoFull ( IN SERIAL_DEV_FIFO *Fifo ) /*++ Routine Description: Detect whether specific FIFO is full or not Arguments: Fifo SERIAL_DEV_FIFO *: A pointer to the Data Structure SERIAL_DEV_FIFO Returns: TRUE: the FIFO is full FALSE: the FIFO is not full --*/ { if (Fifo->Surplus == 0) { return TRUE; } return FALSE; } BOOLEAN IsaSerialFifoEmpty ( IN SERIAL_DEV_FIFO *Fifo ) /*++ Routine Description: Detect whether specific FIFO is empty or not Arguments: Fifo SERIAL_DEV_FIFO *: A pointer to the Data Structure SERIAL_DEV_FIFO Returns: TRUE: the FIFO is empty FALSE: the FIFO is not empty --*/ { if (Fifo->Surplus == SERIAL_MAX_BUFFER_SIZE) { return TRUE; } return FALSE; } EFI_STATUS IsaSerialFifoAdd ( IN SERIAL_DEV_FIFO *Fifo, IN UINT8 Data ) /*++ Routine Description: Add data to specific FIFO Arguments: Fifo SERIAL_DEV_FIFO *: A pointer to the Data Structure SERIAL_DEV_FIFO Data UINT8: the data added to FIFO Returns: EFI_SUCCESS: Add data to specific FIFO successfully EFI_OUT_RESOURCE: Failed to add data because FIFO is already full --*/ // TODO: EFI_OUT_OF_RESOURCES - add return value to function comment { // // if FIFO full can not add data // if (IsaSerialFifoFull (Fifo)) { return EFI_OUT_OF_RESOURCES; } // // FIFO is not full can add data // Fifo->Data[Fifo->Last] = Data; Fifo->Surplus--; Fifo->Last++; if (Fifo->Last >= SERIAL_MAX_BUFFER_SIZE) { Fifo->Last = 0; } return EFI_SUCCESS; } EFI_STATUS IsaSerialFifoRemove ( IN SERIAL_DEV_FIFO *Fifo, OUT UINT8 *Data ) /*++ Routine Description: Remove data from specific FIFO Arguments: Fifo SERIAL_DEV_FIFO *: A pointer to the Data Structure SERIAL_DEV_FIFO Data UINT8*: the data removed from FIFO Returns: EFI_SUCCESS: Remove data from specific FIFO successfully EFI_OUT_RESOURCE: Failed to remove data because FIFO is empty --*/ // TODO: EFI_OUT_OF_RESOURCES - add return value to function comment { // // if FIFO is empty, no data can remove // if (IsaSerialFifoEmpty (Fifo)) { return EFI_OUT_OF_RESOURCES; } // // FIFO is not empty, can remove data // *Data = Fifo->Data[Fifo->First]; Fifo->Surplus++; Fifo->First++; if (Fifo->First >= SERIAL_MAX_BUFFER_SIZE) { Fifo->First = 0; } return EFI_SUCCESS; }