From 637ff81988c4fab871c5293a931d866ec051a107 Mon Sep 17 00:00:00 2001 From: yshang1 Date: Tue, 10 Jul 2007 09:38:02 +0000 Subject: Import IsaSerialDxe in IntelFrameworkModulePkg. git-svn-id: https://edk2.svn.sourceforge.net/svnroot/edk2/trunk/edk2@3170 6f19259b-4bc3-4df7-8a09-765794883524 --- .../Bus/Isa/IsaSerialDxe/Serial.c | 1902 ++++++++++++++++++++ 1 file changed, 1902 insertions(+) create mode 100644 IntelFrameworkModulePkg/Bus/Isa/IsaSerialDxe/Serial.c (limited to 'IntelFrameworkModulePkg/Bus/Isa/IsaSerialDxe/Serial.c') diff --git a/IntelFrameworkModulePkg/Bus/Isa/IsaSerialDxe/Serial.c b/IntelFrameworkModulePkg/Bus/Isa/IsaSerialDxe/Serial.c new file mode 100644 index 0000000000..b21f635fee --- /dev/null +++ b/IntelFrameworkModulePkg/Bus/Isa/IsaSerialDxe/Serial.c @@ -0,0 +1,1902 @@ +/**@file + Serial driver for standard UARTS on an ISA bus. + +Copyright (c) 2006 - 2007, Intel Corporation. All rights reserved.
+This software and associated documentation (if any) is furnished +under a license and may only be used or copied in accordance +with the terms of the license. Except as permitted by such +license, no part of this software or documentation may be +reproduced, stored in a retrieval system, or transmitted in any +form or by any means without the express written consent of +Intel Corporation. + +**/ + +#include "Serial.h" + +// +// ISA Serial Driver Global Variables +// +EFI_DRIVER_BINDING_PROTOCOL gSerialControllerDriver = { + SerialControllerDriverSupported, + SerialControllerDriverStart, + SerialControllerDriverStop, + 0xa, + NULL, + NULL +}; + + +/** + The user Entry Point for module IsaSerial. 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 +InitializeIsaSerial( + IN EFI_HANDLE ImageHandle, + IN EFI_SYSTEM_TABLE *SystemTable + ) +{ + EFI_STATUS Status; + + // + // Install driver model protocol(s). + // + Status = EfiLibInstallAllDriverProtocols ( + ImageHandle, + SystemTable, + &gSerialControllerDriver, + ImageHandle, + &gIsaSerialComponentName, + NULL, + NULL + ); + ASSERT_EFI_ERROR (Status); + + + return Status; +} + + +EFI_STATUS +EFIAPI +SerialControllerDriverSupported ( + IN EFI_DRIVER_BINDING_PROTOCOL *This, + IN EFI_HANDLE Controller, + IN EFI_DEVICE_PATH_PROTOCOL *RemainingDevicePath + ) +/*++ + + Routine Description: + + Check to see if this driver supports the given controller + + Arguments: + + This - A pointer to the EFI_DRIVER_BINDING_PROTOCOL instance. + Controller - The handle of the controller to test. + RemainingDevicePath - A pointer to the remaining portion of a device path. + + Returns: + + EFI_SUCCESS - This driver can support the given controller + +--*/ +{ + EFI_STATUS Status; + EFI_DEVICE_PATH_PROTOCOL *ParentDevicePath; + EFI_ISA_IO_PROTOCOL *IsaIo; + UART_DEVICE_PATH UartNode; + + // + // Ignore the RemainingDevicePath + // + // + // Open the IO Abstraction(s) needed to perform the supported test + // + Status = gBS->OpenProtocol ( + Controller, + &gEfiDevicePathProtocolGuid, + (VOID **) &ParentDevicePath, + This->DriverBindingHandle, + Controller, + EFI_OPEN_PROTOCOL_BY_DRIVER + ); + if (Status == EFI_ALREADY_STARTED) { + return EFI_SUCCESS; + } + + if (EFI_ERROR (Status)) { + return Status; + } + + gBS->CloseProtocol ( + Controller, + &gEfiDevicePathProtocolGuid, + This->DriverBindingHandle, + Controller + ); + + Status = gBS->OpenProtocol ( + Controller, + &gEfiIsaIoProtocolGuid, + (VOID **) &IsaIo, + This->DriverBindingHandle, + Controller, + EFI_OPEN_PROTOCOL_BY_DRIVER + ); + + if (Status == EFI_ALREADY_STARTED) { + return EFI_SUCCESS; + } + + if (EFI_ERROR (Status)) { + return Status; + } + // + // Use the ISA I/O Protocol to see if Controller is standard ISA UART that + // can be managed by this driver. + // + Status = EFI_SUCCESS; + if (IsaIo->ResourceList->Device.HID != EISA_PNP_ID (0x501)) { + Status = EFI_UNSUPPORTED; + goto Error; + } + // + // Make sure RemainingDevicePath is valid + // + if (RemainingDevicePath != NULL) { + Status = EFI_UNSUPPORTED; + CopyMem ( + &UartNode, + (UART_DEVICE_PATH *) RemainingDevicePath, + sizeof (UART_DEVICE_PATH) + ); + if (UartNode.Header.Type != MESSAGING_DEVICE_PATH || + UartNode.Header.SubType != MSG_UART_DP || + sizeof (UART_DEVICE_PATH) != DevicePathNodeLength ((EFI_DEVICE_PATH_PROTOCOL *) &UartNode) + ) { + 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; + } + + Status = EFI_SUCCESS; + } + +Error: + // + // Close the I/O Abstraction(s) used to perform the supported test + // + gBS->CloseProtocol ( + Controller, + &gEfiIsaIoProtocolGuid, + This->DriverBindingHandle, + Controller + ); + + return Status; +} + +EFI_STATUS +EFIAPI +SerialControllerDriverStart ( + IN EFI_DRIVER_BINDING_PROTOCOL *This, + IN EFI_HANDLE Controller, + IN EFI_DEVICE_PATH_PROTOCOL *RemainingDevicePath + ) +/*++ + + Routine Description: + + Start to management the controller passed in + + Arguments: + + This - A pointer to the EFI_DRIVER_BINDING_PROTOCOL instance. + Controller - The handle of the controller to test. + RemainingDevicePath - A pointer to the remaining portion of a device path. + + Returns: + + EFI_SUCCESS - Driver is started successfully + +--*/ +{ + EFI_STATUS Status; + EFI_ISA_IO_PROTOCOL *IsaIo; + SERIAL_DEV *SerialDevice; + UINTN Index; + UART_DEVICE_PATH Node; + EFI_DEVICE_PATH_PROTOCOL *ParentDevicePath; + EFI_OPEN_PROTOCOL_INFORMATION_ENTRY *OpenInfoBuffer; + UINTN EntryCount; + EFI_SERIAL_IO_PROTOCOL *SerialIo; + + SerialDevice = NULL; + // + // 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 + // + Status = gBS->OpenProtocol ( + Controller, + &gEfiIsaIoProtocolGuid, + (VOID **) &IsaIo, + This->DriverBindingHandle, + Controller, + EFI_OPEN_PROTOCOL_BY_DRIVER + ); + if (EFI_ERROR (Status) && Status != EFI_ALREADY_STARTED) { + goto Error; + } + + if (Status == EFI_ALREADY_STARTED) { + + if (RemainingDevicePath == NULL) { + 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 ( + Controller, + &gEfiIsaIoProtocolGuid, + &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) { + Status = gBS->OpenProtocol ( + OpenInfoBuffer[Index].ControllerHandle, + &gEfiSerialIoProtocolGuid, + (VOID **) &SerialIo, + This->DriverBindingHandle, + Controller, + EFI_OPEN_PROTOCOL_GET_PROTOCOL + ); + if (!EFI_ERROR (Status)) { + CopyMem (&Node, RemainingDevicePath, sizeof (UART_DEVICE_PATH)); + Status = SerialIo->SetAttributes ( + SerialIo, + Node.BaudRate, + SerialIo->Mode->ReceiveFifoDepth, + SerialIo->Mode->Timeout, + (EFI_PARITY_TYPE) Node.Parity, + Node.DataBits, + (EFI_STOP_BITS_TYPE) Node.StopBits + ); + } + break; + } + } + + gBS->FreePool (OpenInfoBuffer); + return Status; + } + // + // Initialize the serial device instance + // + SerialDevice = AllocatePool (sizeof (SERIAL_DEV)); + if (SerialDevice == NULL) { + Status = EFI_OUT_OF_RESOURCES; + goto Error; + } + + ZeroMem (SerialDevice, sizeof (SERIAL_DEV)); + + SerialDevice->IsaIo = IsaIo; + SerialDevice->ParentDevicePath = ParentDevicePath; + SerialDevice->ControllerNameTable = NULL; + + ADD_SERIAL_NAME (SerialDevice, IsaIo); + + for (Index = 0; SerialDevice->IsaIo->ResourceList->ResourceItem[Index].Type != EfiIsaAcpiResourceEndOfList; Index++) { + if (SerialDevice->IsaIo->ResourceList->ResourceItem[Index].Type == EfiIsaAcpiResourceIo) { + SerialDevice->BaseAddress = (UINT16) SerialDevice->IsaIo->ResourceList->ResourceItem[Index].StartRange; + } + } + // + // Report status code the serial present + // + REPORT_STATUS_CODE_WITH_DEVICE_PATH ( + EFI_PROGRESS_CODE, + EFI_P_PC_PRESENCE_DETECT | EFI_PERIPHERAL_SERIAL_PORT, + ParentDevicePath + ); + + if (!IsaSerialPortPresent (SerialDevice)) { + Status = EFI_DEVICE_ERROR; + REPORT_STATUS_CODE_WITH_DEVICE_PATH ( + EFI_ERROR_CODE, + EFI_P_EC_NOT_DETECTED | EFI_PERIPHERAL_SERIAL_PORT, + ParentDevicePath + ); + goto Error; + } + + SerialDevice->Signature = SERIAL_DEV_SIGNATURE; + SerialDevice->Type = UART16550A; + SerialDevice->SoftwareLoopbackEnable = FALSE; + SerialDevice->HardwareFlowControl = FALSE; + SerialDevice->Handle = NULL; + SerialDevice->Receive.First = 0; + SerialDevice->Receive.Last = 0; + SerialDevice->Receive.Surplus = SERIAL_MAX_BUFFER_SIZE; + SerialDevice->Transmit.First = 0; + SerialDevice->Transmit.Last = 0; + SerialDevice->Transmit.Surplus = SERIAL_MAX_BUFFER_SIZE; + + // + // Serial I/O + // + SerialDevice->SerialIo.Revision = SERIAL_IO_INTERFACE_REVISION; + SerialDevice->SerialIo.Reset = IsaSerialReset; + SerialDevice->SerialIo.SetAttributes = IsaSerialSetAttributes; + SerialDevice->SerialIo.SetControl = IsaSerialSetControl; + SerialDevice->SerialIo.GetControl = IsaSerialGetControl; + SerialDevice->SerialIo.Write = IsaSerialWrite; + SerialDevice->SerialIo.Read = IsaSerialRead; + SerialDevice->SerialIo.Mode = &(SerialDevice->SerialMode); + + if (RemainingDevicePath != NULL) { + // + // Match the configuration of the RemainingDevicePath. IsHandleSupported() + // already checked to make sure the RemainingDevicePath contains settings + // that we can support. + // + CopyMem (&SerialDevice->UartDevicePath, RemainingDevicePath, sizeof (UART_DEVICE_PATH)); + } else { + // + // 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 (&SerialDevice->UartDevicePath, sizeof (UART_DEVICE_PATH)); + SerialDevice->UartDevicePath.Header.Type = MESSAGING_DEVICE_PATH; + SerialDevice->UartDevicePath.Header.SubType = MSG_UART_DP; + SetDevicePathNodeLength ((EFI_DEVICE_PATH_PROTOCOL *) &SerialDevice->UartDevicePath, sizeof (UART_DEVICE_PATH)); + } + // + // 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. + // + SerialDevice->DevicePath = AppendDevicePathNode ( + ParentDevicePath, + (EFI_DEVICE_PATH_PROTOCOL *) &SerialDevice->UartDevicePath + ); + + if (SerialDevice->DevicePath == NULL) { + Status = EFI_DEVICE_ERROR; + goto Error; + } + // + // Fill in Serial I/O Mode structure based on either the RemainingDevicePath or defaults. + // + SerialDevice->SerialMode.ControlMask = SERIAL_PORT_DEFAULT_CONTROL_MASK; + SerialDevice->SerialMode.Timeout = SERIAL_PORT_DEFAULT_TIMEOUT; + SerialDevice->SerialMode.BaudRate = SerialDevice->UartDevicePath.BaudRate; + SerialDevice->SerialMode.ReceiveFifoDepth = SERIAL_PORT_DEFAULT_RECEIVE_FIFO_DEPTH; + 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, + ParentDevicePath + ); + goto Error; + } + // + // Install protocol interfaces for the serial device. + // + Status = gBS->InstallMultipleProtocolInterfaces ( + &SerialDevice->Handle, + &gEfiDevicePathProtocolGuid, + SerialDevice->DevicePath, + &gEfiSerialIoProtocolGuid, + &SerialDevice->SerialIo, + NULL + ); + if (EFI_ERROR (Status)) { + goto Error; + } + // + // Open For Child Device + // + Status = gBS->OpenProtocol ( + Controller, + &gEfiIsaIoProtocolGuid, + (VOID **) &IsaIo, + This->DriverBindingHandle, + SerialDevice->Handle, + EFI_OPEN_PROTOCOL_BY_CHILD_CONTROLLER + ); + +Error: + if (EFI_ERROR (Status)) { + gBS->CloseProtocol ( + Controller, + &gEfiDevicePathProtocolGuid, + This->DriverBindingHandle, + Controller + ); + gBS->CloseProtocol ( + Controller, + &gEfiIsaIoProtocolGuid, + This->DriverBindingHandle, + Controller + ); + if (SerialDevice) { + if (SerialDevice->DevicePath) { + gBS->FreePool (SerialDevice->DevicePath); + } + + FreeUnicodeStringTable (SerialDevice->ControllerNameTable); + gBS->FreePool (SerialDevice); + } + } + + return Status; +} + +EFI_STATUS +EFIAPI +SerialControllerDriverStop ( + IN EFI_DRIVER_BINDING_PROTOCOL *This, + IN EFI_HANDLE Controller, + IN UINTN NumberOfChildren, + IN EFI_HANDLE *ChildHandleBuffer + ) +/*++ + + Routine Description: + + Disconnect this driver with the controller, uninstall related protocol instance + + Arguments: + + This - A pointer to the EFI_DRIVER_BINDING_PROTOCOL instance. + Controller - The handle of the controller to test. + NumberOfChildren - Number of child device. + RemainingDevicePath - A pointer to the remaining portion of a device path. + + Returns: + + EFI_SUCCESS - Operation successfully + EFI_DEVICE_ERROR - Cannot stop the driver successfully + +--*/ +{ + EFI_STATUS Status; + UINTN Index; + BOOLEAN AllChildrenStopped; + EFI_SERIAL_IO_PROTOCOL *SerialIo; + SERIAL_DEV *SerialDevice; + EFI_ISA_IO_PROTOCOL *IsaIo; + EFI_DEVICE_PATH_PROTOCOL *DevicePath; + + 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 + ); + + // + // 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 ( + Controller, + &gEfiIsaIoProtocolGuid, + This->DriverBindingHandle, + Controller + ); + + Status = gBS->CloseProtocol ( + Controller, + &gEfiDevicePathProtocolGuid, + This->DriverBindingHandle, + Controller + ); + return Status; + } + + 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); + + Status = gBS->CloseProtocol ( + Controller, + &gEfiIsaIoProtocolGuid, + This->DriverBindingHandle, + ChildHandleBuffer[Index] + ); + + Status = gBS->UninstallMultipleProtocolInterfaces ( + ChildHandleBuffer[Index], + &gEfiDevicePathProtocolGuid, + SerialDevice->DevicePath, + &gEfiSerialIoProtocolGuid, + &SerialDevice->SerialIo, + NULL + ); + if (EFI_ERROR (Status)) { + gBS->OpenProtocol ( + Controller, + &gEfiIsaIoProtocolGuid, + (VOID **) &IsaIo, + This->DriverBindingHandle, + ChildHandleBuffer[Index], + EFI_OPEN_PROTOCOL_BY_CHILD_CONTROLLER + ); + } else { + if (SerialDevice->DevicePath) { + gBS->FreePool (SerialDevice->DevicePath); + } + + FreeUnicodeStringTable (SerialDevice->ControllerNameTable); + gBS->FreePool (SerialDevice); + } + } + + if (EFI_ERROR (Status)) { + AllChildrenStopped = FALSE; + } + } + + if (!AllChildrenStopped) { + return EFI_DEVICE_ERROR; + } + + return EFI_SUCCESS; +} + +BOOLEAN +IsaSerialFifoFull ( + IN SERIAL_DEV_FIFO *Fifo + ) +/*++ + + Routine Description: + + Detect whether specific FIFO is full or not + + Arguments: + + 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 - 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 - A pointer to the Data Structure SERIAL_DEV_FIFO + Data - the data added to FIFO + + Returns: + + EFI_SUCCESS - Add data to specific FIFO successfully + EFI_OUT_OF_RESOURCE - Failed to add data because FIFO is already full + +--*/ +{ + // + // 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 - A pointer to the Data Structure SERIAL_DEV_FIFO + Data - the data removed from FIFO + + Returns: + EFI_SUCCESS - Remove data from specific FIFO successfully + EFI_OUT_OF_RESOURCE - Failed to remove data because FIFO is empty + +--*/ +{ + // + // 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; +} + +EFI_STATUS +IsaSerialReceiveTransmit ( + IN SERIAL_DEV *SerialDevice + ) +/*++ + + Routine Description: + + Reads and writes all avaliable data. + + Arguments: + + SerialDevice - The device to flush + + Returns: + + EFI_SUCCESS - Data was read/written successfully. + EFI_OUT_OF_RESOURCE - Failed because software receive FIFO is full. Note, when + this happens, pending writes are not done. + +--*/ +{ + SERIAL_PORT_LSR Lsr; + UINT8 Data; + BOOLEAN ReceiveFifoFull; + SERIAL_PORT_MSR Msr; + SERIAL_PORT_MCR Mcr; + UINTN TimeOut; + + Data = 0; + + // + // Begin the read or write + // + if (SerialDevice->SoftwareLoopbackEnable) { + do { + ReceiveFifoFull = IsaSerialFifoFull (&SerialDevice->Receive); + if (!IsaSerialFifoEmpty (&SerialDevice->Transmit)) { + IsaSerialFifoRemove (&SerialDevice->Transmit, &Data); + if (ReceiveFifoFull) { + return EFI_OUT_OF_RESOURCES; + } + + IsaSerialFifoAdd (&SerialDevice->Receive, Data); + } + } while (!IsaSerialFifoEmpty (&SerialDevice->Transmit)); + } else { + ReceiveFifoFull = IsaSerialFifoFull (&SerialDevice->Receive); + do { + Lsr.Data = READ_LSR (SerialDevice->IsaIo, SerialDevice->BaseAddress); + + if (FeaturePcdGet (PcdNtEmulatorEnable)) { + // + // This is required for NT to avoid a forever-spin... + // This would be better if READ_LSR was a polling operation + // that would timeout. + // + Lsr.Bits.THRE = 1; + } + // + // Flush incomming data to prevent a an overrun during a long write + // + if (Lsr.Bits.DR && !ReceiveFifoFull) { + ReceiveFifoFull = IsaSerialFifoFull (&SerialDevice->Receive); + if (!ReceiveFifoFull) { + if (Lsr.Bits.FIFOE || Lsr.Bits.OE || Lsr.Bits.PE || Lsr.Bits.FE || Lsr.Bits.BI) { + REPORT_STATUS_CODE_WITH_DEVICE_PATH ( + EFI_ERROR_CODE, + EFI_P_EC_INPUT_ERROR | EFI_PERIPHERAL_SERIAL_PORT, + SerialDevice->DevicePath + ); + if (Lsr.Bits.FIFOE || Lsr.Bits.PE || Lsr.Bits.FE || Lsr.Bits.BI) { + Data = READ_RBR (SerialDevice->IsaIo, SerialDevice->BaseAddress); + continue; + } + } + // + // Make sure the receive data will not be missed, Assert DTR + // + if (SerialDevice->HardwareFlowControl) { + Mcr.Data = READ_MCR (SerialDevice->IsaIo, SerialDevice->BaseAddress); + Mcr.Bits.DTRC &= 0; + WRITE_MCR (SerialDevice->IsaIo, SerialDevice->BaseAddress, Mcr.Data); + } + + Data = READ_RBR (SerialDevice->IsaIo, SerialDevice->BaseAddress); + + // + // Deassert DTR + // + if (SerialDevice->HardwareFlowControl) { + Mcr.Data = READ_MCR (SerialDevice->IsaIo, SerialDevice->BaseAddress); + Mcr.Bits.DTRC |= 1; + WRITE_MCR (SerialDevice->IsaIo, SerialDevice->BaseAddress, Mcr.Data); + } + + IsaSerialFifoAdd (&SerialDevice->Receive, Data); + + continue; + } else { + REPORT_STATUS_CODE_WITH_DEVICE_PATH ( + EFI_PROGRESS_CODE, + EFI_P_SERIAL_PORT_PC_CLEAR_BUFFER | EFI_PERIPHERAL_SERIAL_PORT, + SerialDevice->DevicePath + ); + } + } + // + // Do the write + // + if (Lsr.Bits.THRE && !IsaSerialFifoEmpty (&SerialDevice->Transmit)) { + // + // Make sure the transmit data will not be missed + // + if (SerialDevice->HardwareFlowControl) { + // + // Send RTS + // + Mcr.Data = READ_MCR (SerialDevice->IsaIo, SerialDevice->BaseAddress); + Mcr.Bits.RTS |= 1; + WRITE_MCR (SerialDevice->IsaIo, SerialDevice->BaseAddress, Mcr.Data); + // + // Wait for CTS + // + TimeOut = 0; + Msr.Data = READ_MSR (SerialDevice->IsaIo, SerialDevice->BaseAddress); + while (!Msr.Bits.CTS) { + gBS->Stall (TIMEOUT_STALL_INTERVAL); + TimeOut++; + if (TimeOut > 5) { + break; + } + + Msr.Data = READ_MSR (SerialDevice->IsaIo, SerialDevice->BaseAddress); + } + + if (Msr.Bits.CTS) { + IsaSerialFifoRemove (&SerialDevice->Transmit, &Data); + WRITE_THR (SerialDevice->IsaIo, SerialDevice->BaseAddress, Data); + } + } + // + // write the data out + // + if (!SerialDevice->HardwareFlowControl) { + IsaSerialFifoRemove (&SerialDevice->Transmit, &Data); + WRITE_THR (SerialDevice->IsaIo, SerialDevice->BaseAddress, Data); + } + // + // Make sure the transmit data will not be missed + // + if (SerialDevice->HardwareFlowControl) { + // + // Assert RTS + // + Mcr.Data = READ_MCR (SerialDevice->IsaIo, SerialDevice->BaseAddress); + Mcr.Bits.RTS &= 0; + WRITE_MCR (SerialDevice->IsaIo, SerialDevice->BaseAddress, Mcr.Data); + } + } + } while (Lsr.Bits.THRE && !IsaSerialFifoEmpty (&SerialDevice->Transmit)); + } + + return EFI_SUCCESS; +} +// +// Interface Functions +// +EFI_STATUS +EFIAPI +IsaSerialReset ( + IN EFI_SERIAL_IO_PROTOCOL *This + ) +/*++ + + Routine Description: + + Reset serial device + + Arguments: + + This - Pointer to EFI_SERIAL_IO_PROTOCOL + + Returns: + + EFI_SUCCESS - Reset successfully + EFI_DEVICE_ERROR - Failed to reset + +--*/ +{ + EFI_STATUS Status; + SERIAL_DEV *SerialDevice; + SERIAL_PORT_LCR Lcr; + SERIAL_PORT_IER Ier; + SERIAL_PORT_MCR Mcr; + SERIAL_PORT_FCR Fcr; + EFI_TPL Tpl; + + SerialDevice = SERIAL_DEV_FROM_THIS (This); + + // + // Report the status code reset the serial + // + REPORT_STATUS_CODE_WITH_DEVICE_PATH ( + EFI_PROGRESS_CODE, + EFI_P_PC_RESET | EFI_PERIPHERAL_SERIAL_PORT, + SerialDevice->DevicePath + ); + + Tpl = gBS->RaiseTPL (TPL_NOTIFY); + + // + // Make sure DLAB is 0. + // + Lcr.Data = READ_LCR (SerialDevice->IsaIo, SerialDevice->BaseAddress); + Lcr.Bits.DLAB = 0; + WRITE_LCR (SerialDevice->IsaIo, SerialDevice->BaseAddress, Lcr.Data); + + // + // Turn off all interrupts + // + Ier.Data = READ_IER (SerialDevice->IsaIo, SerialDevice->BaseAddress); + Ier.Bits.RAVIE = 0; + Ier.Bits.THEIE = 0; + Ier.Bits.RIE = 0; + Ier.Bits.MIE = 0; + WRITE_IER (SerialDevice->IsaIo, SerialDevice->BaseAddress, Ier.Data); + + // + // Disable the FIFO. + // + Fcr.Bits.TRFIFOE = 0; + WRITE_FCR (SerialDevice->IsaIo, SerialDevice->BaseAddress, Fcr.Data); + + // + // Turn off loopback and disable device interrupt. + // + Mcr.Data = READ_MCR (SerialDevice->IsaIo, SerialDevice->BaseAddress); + Mcr.Bits.OUT1 = 0; + Mcr.Bits.OUT2 = 0; + Mcr.Bits.LME = 0; + WRITE_MCR (SerialDevice->IsaIo, SerialDevice->BaseAddress, Mcr.Data); + + // + // Clear the scratch pad register + // + WRITE_SCR (SerialDevice->IsaIo, SerialDevice->BaseAddress, 0); + + // + // Go set the current attributes + // + Status = 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 + ); + + if (EFI_ERROR (Status)) { + gBS->RestoreTPL (Tpl); + return EFI_DEVICE_ERROR; + } + // + // Go set the current control bits + // + Status = This->SetControl ( + This, + This->Mode->ControlMask + ); + + if (EFI_ERROR (Status)) { + gBS->RestoreTPL (Tpl); + return EFI_DEVICE_ERROR; + } + // + // for 16550A enable FIFO, 16550 disable FIFO + // + Fcr.Bits.TRFIFOE = 1; + Fcr.Bits.RESETRF = 1; + Fcr.Bits.RESETTF = 1; + WRITE_FCR (SerialDevice->IsaIo, SerialDevice->BaseAddress, Fcr.Data); + + // + // Reset the software FIFO + // + SerialDevice->Receive.First = 0; + SerialDevice->Receive.Last = 0; + SerialDevice->Receive.Surplus = SERIAL_MAX_BUFFER_SIZE; + SerialDevice->Transmit.First = 0; + SerialDevice->Transmit.Last = 0; + SerialDevice->Transmit.Surplus = SERIAL_MAX_BUFFER_SIZE; + + gBS->RestoreTPL (Tpl); + + // + // Device reset is complete + // + return EFI_SUCCESS; +} + +EFI_STATUS +EFIAPI +IsaSerialSetAttributes ( + 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: + + Set new attributes to a serial device + + Arguments: + + This - Pointer to EFI_SERIAL_IO_PROTOCOL + BaudRate - The baudrate of the serial device + ReceiveFifoDepth - The depth of receive FIFO buffer + Timeout - The request timeout for a single char + Parity - The type of parity used in serial device + DataBits - Number of databits used in serial device + StopBits - Number of stopbits used in serial device + + Returns: + + EFI_SUCCESS - The new attributes were set + EFI_INVALID_PARAMETERS - One or more attributes have an unsupported value + EFI_UNSUPPORTED - Data Bits can not set to 5 or 6 + EFI_DEVICE_ERROR - The serial device is not functioning correctly (no return) + +--*/ +{ + EFI_STATUS Status; + SERIAL_DEV *SerialDevice; + UINT32 Divisor; + UINT32 Remained; + SERIAL_PORT_LCR Lcr; + EFI_DEVICE_PATH_PROTOCOL *NewDevicePath; + EFI_TPL Tpl; + + SerialDevice = SERIAL_DEV_FROM_THIS (This); + + // + // Check for default settings and fill in actual values. + // + if (BaudRate == 0) { + BaudRate = SERIAL_PORT_DEFAULT_BAUD_RATE; + } + + if (ReceiveFifoDepth == 0) { + ReceiveFifoDepth = SERIAL_PORT_DEFAULT_RECEIVE_FIFO_DEPTH; + } + + if (Timeout == 0) { + Timeout = SERIAL_PORT_DEFAULT_TIMEOUT; + } + + if (Parity == DefaultParity) { + Parity = SERIAL_PORT_DEFAULT_PARITY; + } + + if (DataBits == 0) { + DataBits = SERIAL_PORT_DEFAULT_DATA_BITS; + } + + if (StopBits == DefaultStopBits) { + StopBits = (EFI_STOP_BITS_TYPE) SERIAL_PORT_DEFAULT_STOP_BITS; + } + // + // 5 and 6 data bits can not be verified on a 16550A UART + // Return EFI_INVALID_PARAMETER if an attempt is made to use these settings. + // + if ((DataBits == 5) || (DataBits == 6)) { + return EFI_INVALID_PARAMETER; + } + // + // Make sure all parameters are valid + // + if ((BaudRate > SERIAL_PORT_MAX_BAUD_RATE) || (BaudRate < SERIAL_PORT_MIN_BAUD_RATE)) { + return EFI_INVALID_PARAMETER; + } + // + // 50,75,110,134,150,300,600,1200,1800,2000,2400,3600,4800,7200,9600,19200, + // 38400,57600,115200 + // + if (BaudRate < 75) { + BaudRate = 50; + } else if (BaudRate < 110) { + BaudRate = 75; + } else if (BaudRate < 134) { + BaudRate = 110; + } else if (BaudRate < 150) { + BaudRate = 134; + } else if (BaudRate < 300) { + BaudRate = 150; + } else if (BaudRate < 600) { + BaudRate = 300; + } else if (BaudRate < 1200) { + BaudRate = 600; + } else if (BaudRate < 1800) { + BaudRate = 1200; + } else if (BaudRate < 2000) { + BaudRate = 1800; + } else if (BaudRate < 2400) { + BaudRate = 2000; + } else if (BaudRate < 3600) { + BaudRate = 2400; + } else if (BaudRate < 4800) { + BaudRate = 3600; + } else if (BaudRate < 7200) { + BaudRate = 4800; + } else if (BaudRate < 9600) { + BaudRate = 7200; + } else if (BaudRate < 19200) { + BaudRate = 9600; + } else if (BaudRate < 38400) { + BaudRate = 19200; + } else if (BaudRate < 57600) { + BaudRate = 38400; + } else if (BaudRate < 115200) { + BaudRate = 57600; + } else if (BaudRate <= SERIAL_PORT_MAX_BAUD_RATE) { + BaudRate = 115200; + } + + 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 ((DataBits < 5) || (DataBits > 8)) { + return EFI_INVALID_PARAMETER; + } + + if ((StopBits < OneStopBit) || (StopBits > TwoStopBits)) { + return EFI_INVALID_PARAMETER; + } + // + // for DataBits = 5, StopBits can not set TwoStopBits + // + // if ((DataBits == 5) && (StopBits == TwoStopBits)) { + // return EFI_INVALID_PARAMETER; + // } + // + // for DataBits = 6,7,8, StopBits can not set OneFiveStopBits + // + if ((DataBits >= 6) && (DataBits <= 8) && (StopBits == OneFiveStopBits)) { + return EFI_INVALID_PARAMETER; + } + + // + // Compute divisor use to program the baud rate using a round determination + // + Divisor = (UINT32) DivU64x32Remainder ( + SERIAL_PORT_INPUT_CLOCK, + ((UINT32) BaudRate * 16), + &Remained + ); + if (Remained) { + Divisor += 1; + } + + if ((Divisor == 0) || (Divisor & 0xffff0000)) { + return EFI_INVALID_PARAMETER; + } + + Tpl = gBS->RaiseTPL (TPL_NOTIFY); + + // + // Compute the actual baud rate that the serial port will be programmed for. + // + BaudRate = SERIAL_PORT_INPUT_CLOCK / Divisor / 16; + + // + // Put serial port on Divisor Latch Mode + // + Lcr.Data = READ_LCR (SerialDevice->IsaIo, SerialDevice->BaseAddress); + Lcr.Bits.DLAB = 1; + WRITE_LCR (SerialDevice->IsaIo, SerialDevice->BaseAddress, Lcr.Data); + + // + // Write the divisor to the serial port + // + WRITE_DLL (SerialDevice->IsaIo, SerialDevice->BaseAddress, (UINT8) (Divisor & 0xff)); + WRITE_DLM (SerialDevice->IsaIo, SerialDevice->BaseAddress, (UINT8) ((Divisor >> 8) & 0xff)); + + // + // Put serial port back in normal mode and set remaining attributes. + // + Lcr.Bits.DLAB = 0; + + switch (Parity) { + case NoParity: + Lcr.Bits.PAREN = 0; + Lcr.Bits.EVENPAR = 0; + Lcr.Bits.STICPAR = 0; + break; + + case EvenParity: + Lcr.Bits.PAREN = 1; + Lcr.Bits.EVENPAR = 1; + Lcr.Bits.STICPAR = 0; + break; + + case OddParity: + Lcr.Bits.PAREN = 1; + Lcr.Bits.EVENPAR = 0; + Lcr.Bits.STICPAR = 0; + break; + + case SpaceParity: + Lcr.Bits.PAREN = 1; + Lcr.Bits.EVENPAR = 1; + Lcr.Bits.STICPAR = 1; + break; + + case MarkParity: + Lcr.Bits.PAREN = 1; + Lcr.Bits.EVENPAR = 0; + Lcr.Bits.STICPAR = 1; + break; + + default: + break; + } + + switch (StopBits) { + case OneStopBit: + Lcr.Bits.STOPB = 0; + break; + + case OneFiveStopBits: + case TwoStopBits: + Lcr.Bits.STOPB = 1; + break; + + default: + break; + } + // + // DataBits + // + Lcr.Bits.SERIALDB = (UINT8) ((DataBits - 5) & 0x03); + WRITE_LCR (SerialDevice->IsaIo, SerialDevice->BaseAddress, Lcr.Data); + + // + // Set the Serial I/O mode + // + This->Mode->BaudRate = BaudRate; + This->Mode->ReceiveFifoDepth = ReceiveFifoDepth; + This->Mode->Timeout = Timeout; + This->Mode->Parity = Parity; + This->Mode->DataBits = DataBits; + This->Mode->StopBits = StopBits; + + // + // See if Device Path Node has actually changed + // + if (SerialDevice->UartDevicePath.BaudRate == BaudRate && + SerialDevice->UartDevicePath.DataBits == DataBits && + SerialDevice->UartDevicePath.Parity == Parity && + SerialDevice->UartDevicePath.StopBits == StopBits + ) { + gBS->RestoreTPL (Tpl); + return EFI_SUCCESS; + } + // + // Update the device path + // + SerialDevice->UartDevicePath.BaudRate = BaudRate; + SerialDevice->UartDevicePath.DataBits = DataBits; + SerialDevice->UartDevicePath.Parity = (UINT8) Parity; + SerialDevice->UartDevicePath.StopBits = (UINT8) StopBits; + + NewDevicePath = AppendDevicePathNode ( + SerialDevice->ParentDevicePath, + (EFI_DEVICE_PATH_PROTOCOL *) &SerialDevice->UartDevicePath + ); + if (NewDevicePath == NULL) { + gBS->RestoreTPL (Tpl); + return EFI_DEVICE_ERROR; + } + + if (SerialDevice->Handle != NULL) { + Status = gBS->ReinstallProtocolInterface ( + SerialDevice->Handle, + &gEfiDevicePathProtocolGuid, + SerialDevice->DevicePath, + NewDevicePath + ); + if (EFI_ERROR (Status)) { + gBS->RestoreTPL (Tpl); + return Status; + } + } + + if (SerialDevice->DevicePath) { + gBS->FreePool (SerialDevice->DevicePath); + } + + SerialDevice->DevicePath = NewDevicePath; + + gBS->RestoreTPL (Tpl); + + return EFI_SUCCESS; +} + +EFI_STATUS +EFIAPI +IsaSerialSetControl ( + IN EFI_SERIAL_IO_PROTOCOL *This, + IN UINT32 Control + ) +/*++ + + Routine Description: + + Set Control Bits + + Arguments: + + This - Pointer to EFI_SERIAL_IO_PROTOCOL + Control - Control bits that can be settable + + Returns: + + EFI_SUCCESS - New Control bits were set successfully + EFI_UNSUPPORTED - The Control bits wanted to set are not supported + +--*/ +{ + SERIAL_DEV *SerialDevice; + SERIAL_PORT_MCR Mcr; + EFI_TPL Tpl; + + // + // The control bits that can be set are : + // EFI_SERIAL_DATA_TERMINAL_READY: 0x0001 // WO + // EFI_SERIAL_REQUEST_TO_SEND: 0x0002 // WO + // EFI_SERIAL_HARDWARE_LOOPBACK_ENABLE: 0x1000 // RW + // EFI_SERIAL_SOFTWARE_LOOPBACK_ENABLE: 0x2000 // RW + // + SerialDevice = SERIAL_DEV_FROM_THIS (This); + + // + // first determine the parameter is invalid + // + if (Control & 0xffff8ffc) { + return EFI_UNSUPPORTED; + } + + Tpl = gBS->RaiseTPL (TPL_NOTIFY); + + Mcr.Data = READ_MCR (SerialDevice->IsaIo, SerialDevice->BaseAddress); + Mcr.Bits.DTRC = 0; + Mcr.Bits.RTS = 0; + Mcr.Bits.LME = 0; + SerialDevice->SoftwareLoopbackEnable = FALSE; + SerialDevice->HardwareFlowControl = FALSE; + + if (Control & EFI_SERIAL_DATA_TERMINAL_READY) { + Mcr.Bits.DTRC = 1; + } + + if (Control & EFI_SERIAL_REQUEST_TO_SEND) { + Mcr.Bits.RTS = 1; + } + + if (Control & EFI_SERIAL_HARDWARE_LOOPBACK_ENABLE) { + Mcr.Bits.LME = 1; + } + + if (Control & EFI_SERIAL_HARDWARE_FLOW_CONTROL_ENABLE) { + SerialDevice->HardwareFlowControl = TRUE; + } + + WRITE_MCR (SerialDevice->IsaIo, SerialDevice->BaseAddress, Mcr.Data); + + if (Control & EFI_SERIAL_SOFTWARE_LOOPBACK_ENABLE) { + SerialDevice->SoftwareLoopbackEnable = TRUE; + } + + gBS->RestoreTPL (Tpl); + + return EFI_SUCCESS; +} + +EFI_STATUS +EFIAPI +IsaSerialGetControl ( + IN EFI_SERIAL_IO_PROTOCOL *This, + OUT UINT32 *Control + ) +/*++ + + Routine Description: + + Get ControlBits + + Arguments: + + This - Pointer to EFI_SERIAL_IO_PROTOCOL + Control - Control signals of the serial device + + Returns: + + EFI_SUCCESS - Get Control signals successfully + +--*/ +{ + SERIAL_DEV *SerialDevice; + SERIAL_PORT_MSR Msr; + SERIAL_PORT_MCR Mcr; + EFI_TPL Tpl; + + Tpl = gBS->RaiseTPL (TPL_NOTIFY); + + SerialDevice = SERIAL_DEV_FROM_THIS (This); + + *Control = 0; + + // + // Read the Modem Status Register + // + Msr.Data = READ_MSR (SerialDevice->IsaIo, SerialDevice->BaseAddress); + + if (Msr.Bits.CTS) { + *Control |= EFI_SERIAL_CLEAR_TO_SEND; + } + + if (Msr.Bits.DSR) { + *Control |= EFI_SERIAL_DATA_SET_READY; + } + + if (Msr.Bits.RI) { + *Control |= EFI_SERIAL_RING_INDICATE; + } + + if (Msr.Bits.DCD) { + *Control |= EFI_SERIAL_CARRIER_DETECT; + } + // + // Read the Modem Control Register + // + Mcr.Data = READ_MCR (SerialDevice->IsaIo, SerialDevice->BaseAddress); + + if (Mcr.Bits.DTRC) { + *Control |= EFI_SERIAL_DATA_TERMINAL_READY; + } + + if (Mcr.Bits.RTS) { + *Control |= EFI_SERIAL_REQUEST_TO_SEND; + } + + if (Mcr.Bits.LME) { + *Control |= EFI_SERIAL_HARDWARE_LOOPBACK_ENABLE; + } + + if (SerialDevice->HardwareFlowControl) { + *Control |= EFI_SERIAL_HARDWARE_FLOW_CONTROL_ENABLE; + } + // + // See if the Transmit FIFO is empty + // + IsaSerialReceiveTransmit (SerialDevice); + + if (IsaSerialFifoEmpty (&SerialDevice->Transmit)) { + *Control |= EFI_SERIAL_OUTPUT_BUFFER_EMPTY; + } + // + // See if the Receive FIFO is empty. + // + IsaSerialReceiveTransmit (SerialDevice); + + if (IsaSerialFifoEmpty (&SerialDevice->Receive)) { + *Control |= EFI_SERIAL_INPUT_BUFFER_EMPTY; + } + + if (SerialDevice->SoftwareLoopbackEnable) { + *Control |= EFI_SERIAL_SOFTWARE_LOOPBACK_ENABLE; + } + + gBS->RestoreTPL (Tpl); + + return EFI_SUCCESS; +} + +EFI_STATUS +EFIAPI +IsaSerialWrite ( + IN EFI_SERIAL_IO_PROTOCOL *This, + IN OUT UINTN *BufferSize, + IN VOID *Buffer + ) +/*++ + + Routine Description: + + Write the specified number of bytes to serial device + + Arguments: + + This - Pointer to EFI_SERIAL_IO_PROTOCOL + BufferSize - On input the size of Buffer, on output the amount of + data actually written + Buffer - The buffer of data to write + + Returns: + + EFI_SUCCESS - The data were written successfully + EFI_DEVICE_ERROR - The device reported an error + EFI_TIMEOUT - The write operation was stopped due to timeout + +--*/ +{ + SERIAL_DEV *SerialDevice; + UINT8 *CharBuffer; + UINT32 Index; + UINTN Elapsed; + UINTN ActualWrite; + EFI_TPL Tpl; + + SerialDevice = SERIAL_DEV_FROM_THIS (This); + Elapsed = 0; + ActualWrite = 0; + + if (*BufferSize == 0) { + return EFI_SUCCESS; + } + + if (!Buffer) { + REPORT_STATUS_CODE_WITH_DEVICE_PATH ( + EFI_ERROR_CODE, + EFI_P_EC_OUTPUT_ERROR | EFI_PERIPHERAL_SERIAL_PORT, + SerialDevice->DevicePath + ); + + return EFI_DEVICE_ERROR; + } + + Tpl = gBS->RaiseTPL (TPL_NOTIFY); + + CharBuffer = (UINT8 *) Buffer; + + for (Index = 0; Index < *BufferSize; Index++) { + IsaSerialFifoAdd (&SerialDevice->Transmit, CharBuffer[Index]); + + while (IsaSerialReceiveTransmit (SerialDevice) != EFI_SUCCESS || !IsaSerialFifoEmpty (&SerialDevice->Transmit)) { + // + // Unsuccessful write so check if timeout has expired, if not, + // stall for a bit, increment time elapsed, and try again + // + if (Elapsed >= This->Mode->Timeout) { + *BufferSize = ActualWrite; + gBS->RestoreTPL (Tpl); + return EFI_TIMEOUT; + } + + gBS->Stall (TIMEOUT_STALL_INTERVAL); + + Elapsed += TIMEOUT_STALL_INTERVAL; + } + + ActualWrite++; + // + // Successful write so reset timeout + // + Elapsed = 0; + } + + gBS->RestoreTPL (Tpl); + + return EFI_SUCCESS; +} + +EFI_STATUS +EFIAPI +IsaSerialRead ( + IN EFI_SERIAL_IO_PROTOCOL *This, + IN OUT UINTN *BufferSize, + OUT VOID *Buffer + ) +/*++ + + Routine Description: + + Read the specified number of bytes from serial device + + Arguments: + + This - Pointer to EFI_SERIAL_IO_PROTOCOL + BufferSize - On input the size of Buffer, on output the amount of + data returned in buffer + Buffer - The buffer to return the data into + + Returns: + + EFI_SUCCESS - The data were read successfully + EFI_DEVICE_ERROR - The device reported an error + EFI_TIMEOUT - The read operation was stopped due to timeout + +--*/ +{ + SERIAL_DEV *SerialDevice; + UINT32 Index; + UINT8 *CharBuffer; + UINTN Elapsed; + EFI_STATUS Status; + EFI_TPL Tpl; + + SerialDevice = SERIAL_DEV_FROM_THIS (This); + Elapsed = 0; + + if (*BufferSize == 0) { + return EFI_SUCCESS; + } + + if (!Buffer) { + return EFI_DEVICE_ERROR; + } + + Tpl = gBS->RaiseTPL (TPL_NOTIFY); + + Status = IsaSerialReceiveTransmit (SerialDevice); + + if (EFI_ERROR (Status)) { + *BufferSize = 0; + + REPORT_STATUS_CODE_WITH_DEVICE_PATH ( + EFI_ERROR_CODE, + EFI_P_EC_INPUT_ERROR | EFI_PERIPHERAL_SERIAL_PORT, + SerialDevice->DevicePath + ); + + gBS->RestoreTPL (Tpl); + + return EFI_DEVICE_ERROR; + } + + CharBuffer = (UINT8 *) Buffer; + for (Index = 0; Index < *BufferSize; Index++) { + while (IsaSerialFifoRemove (&SerialDevice->Receive, &(CharBuffer[Index])) != EFI_SUCCESS) { + // + // Unsuccessful read so check if timeout has expired, if not, + // stall for a bit, increment time elapsed, and try again + // Need this time out to get conspliter to work. + // + if (Elapsed >= This->Mode->Timeout) { + *BufferSize = Index; + gBS->RestoreTPL (Tpl); + return EFI_TIMEOUT; + } + + gBS->Stall (TIMEOUT_STALL_INTERVAL); + Elapsed += TIMEOUT_STALL_INTERVAL; + + Status = IsaSerialReceiveTransmit (SerialDevice); + if (Status == EFI_DEVICE_ERROR) { + *BufferSize = Index; + gBS->RestoreTPL (Tpl); + return EFI_DEVICE_ERROR; + } + } + // + // Successful read so reset timeout + // + Elapsed = 0; + } + + IsaSerialReceiveTransmit (SerialDevice); + + gBS->RestoreTPL (Tpl); + + return EFI_SUCCESS; +} + +BOOLEAN +IsaSerialPortPresent ( + IN SERIAL_DEV *SerialDevice + ) +/*++ + + Routine Description: + + Use scratchpad register to test if this serial port is present + + Arguments: + + SerialDevice - Pointer to serial device structure + + Returns: + + TRUE - The serial port is present + FALSE - The serial port is NOT present + +--*/ +{ + UINT8 Temp; + BOOLEAN Status; + + Status = TRUE; + + // + // Save SCR reg + // + Temp = READ_SCR (SerialDevice->IsaIo, SerialDevice->BaseAddress); + WRITE_SCR (SerialDevice->IsaIo, SerialDevice->BaseAddress, 0xAA); + + if (READ_SCR (SerialDevice->IsaIo, SerialDevice->BaseAddress) != 0xAA) + { + if (!FeaturePcdGet (PcdNtEmulatorEnable)) { + Status = FALSE; + } + } + + WRITE_SCR (SerialDevice->IsaIo, SerialDevice->BaseAddress, 0x55); + + if (READ_SCR (SerialDevice->IsaIo, SerialDevice->BaseAddress) != 0x55) + { + if (!FeaturePcdGet (PcdNtEmulatorEnable)) { + Status = FALSE; + } + } + // + // Restore SCR + // + WRITE_SCR (SerialDevice->IsaIo, SerialDevice->BaseAddress, Temp); + return Status; +} + +UINT8 +IsaSerialReadPort ( + IN EFI_ISA_IO_PROTOCOL *IsaIo, + IN UINT16 BaseAddress, + IN UINT32 Offset + ) +/*++ + + Routine Description: + + Use IsaIo protocol to read serial port + + Arguments: + + IsaIo - Pointer to EFI_ISA_IO_PROTOCOL instance + BaseAddress - Serial port register group base address + Offset - Offset in register group + + Returns: + + Data read from serial port + +--*/ +{ + UINT8 Data; + + // + // Use IsaIo to access IO + // + IsaIo->Io.Read ( + IsaIo, + EfiIsaIoWidthUint8, + BaseAddress + Offset, + 1, + &Data + ); + return Data; +} + +VOID +IsaSerialWritePort ( + IN EFI_ISA_IO_PROTOCOL *IsaIo, + IN UINT16 BaseAddress, + IN UINT32 Offset, + IN UINT8 Data + ) +/*++ + + Routine Description: + + Use IsaIo protocol to write serial port + + Arguments: + + IsaIo - Pointer to EFI_ISA_IO_PROTOCOL instance + BaseAddress - Serial port register group base address + Offset - Offset in register group + Data - data which is to be written to some serial port register + + Returns: + + None + +--*/ +{ + // + // Use IsaIo to access IO + // + IsaIo->Io.Write ( + IsaIo, + EfiIsaIoWidthUint8, + BaseAddress + Offset, + 1, + &Data + ); +} + -- cgit v1.2.3