| /** @file | |
| Serial driver that layers on top of a Serial Port Library instance. | |
| Copyright (c) 2008 - 2009, Apple Inc. All rights reserved.<BR> | |
| Copyright (c) 2013-2014, ARM Ltd. All rights reserved.<BR> | |
| Copyright (c) 2015, Intel Corporation. All rights reserved.<BR> | |
| This program and the accompanying materials | |
| are licensed and made available under the terms and conditions of the BSD License | |
| which accompanies this distribution. The full text of the license may be found at | |
| http://opensource.org/licenses/bsd-license.php | |
| THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, | |
| WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. | |
| **/ | |
| #include <Library/UefiBootServicesTableLib.h> | |
| #include <Library/SerialPortLib.h> | |
| #include <Library/DebugLib.h> | |
| #include <Library/PcdLib.h> | |
| #include <Protocol/SerialIo.h> | |
| #include <Protocol/DevicePath.h> | |
| typedef struct { | |
| VENDOR_DEVICE_PATH Guid; | |
| UART_DEVICE_PATH Uart; | |
| EFI_DEVICE_PATH_PROTOCOL End; | |
| } SERIAL_DEVICE_PATH; | |
| /** | |
| Reset the serial device. | |
| @param This Protocol instance pointer. | |
| @retval EFI_SUCCESS The device was reset. | |
| @retval EFI_DEVICE_ERROR The serial device could not be reset. | |
| **/ | |
| EFI_STATUS | |
| EFIAPI | |
| SerialReset ( | |
| IN EFI_SERIAL_IO_PROTOCOL *This | |
| ); | |
| /** | |
| Sets the baud rate, receive FIFO depth, transmit/receive time out, parity, | |
| data bits, and stop bits on a serial device. | |
| @param This Protocol instance pointer. | |
| @param BaudRate The requested baud rate. A BaudRate value of 0 will use the the | |
| device's default interface speed. | |
| @param ReceiveFifoDepth The requested depth of the FIFO on the receive side of the | |
| serial interface. A ReceiveFifoDepth value of 0 will use | |
| the device's default FIFO depth. | |
| @param Timeout The requested time out for a single character in microseconds. | |
| This timeout applies to both the transmit and receive side of the | |
| interface. A Timeout value of 0 will use the device's default time | |
| out value. | |
| @param Parity The type of parity to use on this serial device. A Parity value of | |
| DefaultParity will use the device's default parity value. | |
| @param DataBits The number of data bits to use on the serial device. A DataBits | |
| value of 0 will use the device's default data bit setting. | |
| @param StopBits The number of stop bits to use on this serial device. A StopBits | |
| value of DefaultStopBits will use the device's default number of | |
| stop bits. | |
| @retval EFI_SUCCESS The device was reset. | |
| @retval EFI_INVALID_PARAMETER One or more attributes has an unsupported value. | |
| @retval EFI_DEVICE_ERROR The serial device is not functioning correctly. | |
| **/ | |
| EFI_STATUS | |
| EFIAPI | |
| SerialSetAttributes ( | |
| 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 | |
| ); | |
| /** | |
| Set the control bits on a serial device | |
| @param This Protocol instance pointer. | |
| @param Control Set the bits of Control that are settable. | |
| @retval EFI_SUCCESS The new control bits were set on the serial device. | |
| @retval EFI_UNSUPPORTED The serial device does not support this operation. | |
| @retval EFI_DEVICE_ERROR The serial device is not functioning correctly. | |
| **/ | |
| EFI_STATUS | |
| EFIAPI | |
| SerialSetControl ( | |
| IN EFI_SERIAL_IO_PROTOCOL *This, | |
| IN UINT32 Control | |
| ); | |
| /** | |
| Retrieves the status of the control bits on a serial device | |
| @param This Protocol instance pointer. | |
| @param Control A pointer to return the current Control signals from the serial device. | |
| @retval EFI_SUCCESS The control bits were read from the serial device. | |
| @retval EFI_DEVICE_ERROR The serial device is not functioning correctly. | |
| **/ | |
| EFI_STATUS | |
| EFIAPI | |
| SerialGetControl ( | |
| IN EFI_SERIAL_IO_PROTOCOL *This, | |
| OUT UINT32 *Control | |
| ); | |
| /** | |
| Writes data to a serial device. | |
| @param This Protocol instance pointer. | |
| @param BufferSize On input, the size of the Buffer. On output, the amount of | |
| data actually written. | |
| @param Buffer The buffer of data to write | |
| @retval EFI_SUCCESS The data was written. | |
| @retval EFI_DEVICE_ERROR The device reported an error. | |
| @retval EFI_TIMEOUT The data write was stopped due to a timeout. | |
| **/ | |
| EFI_STATUS | |
| EFIAPI | |
| SerialWrite ( | |
| IN EFI_SERIAL_IO_PROTOCOL *This, | |
| IN OUT UINTN *BufferSize, | |
| IN VOID *Buffer | |
| ); | |
| /** | |
| Reads data from a serial device. | |
| @param This Protocol instance pointer. | |
| @param BufferSize On input, the size of the Buffer. On output, the amount of | |
| data returned in Buffer. | |
| @param Buffer The buffer to return the data into. | |
| @retval EFI_SUCCESS The data was read. | |
| @retval EFI_DEVICE_ERROR The device reported an error. | |
| @retval EFI_TIMEOUT The data write was stopped due to a timeout. | |
| **/ | |
| EFI_STATUS | |
| EFIAPI | |
| SerialRead ( | |
| IN EFI_SERIAL_IO_PROTOCOL *This, | |
| IN OUT UINTN *BufferSize, | |
| OUT VOID *Buffer | |
| ); | |
| EFI_HANDLE mSerialHandle = NULL; | |
| SERIAL_DEVICE_PATH mSerialDevicePath = { | |
| { | |
| { HARDWARE_DEVICE_PATH, HW_VENDOR_DP, { sizeof (VENDOR_DEVICE_PATH), 0} }, | |
| EFI_CALLER_ID_GUID // Use the driver's GUID | |
| }, | |
| { | |
| { MESSAGING_DEVICE_PATH, MSG_UART_DP, { sizeof (UART_DEVICE_PATH), 0} }, | |
| 0, // Reserved | |
| 0, // BaudRate | |
| 0, // DataBits | |
| 0, // Parity | |
| 0 // StopBits | |
| }, | |
| { END_DEVICE_PATH_TYPE, END_ENTIRE_DEVICE_PATH_SUBTYPE, { sizeof (EFI_DEVICE_PATH_PROTOCOL), 0 } } | |
| }; | |
| // | |
| // Template used to initialize the Serial IO protocols. | |
| // | |
| EFI_SERIAL_IO_MODE mSerialIoMode = { | |
| // | |
| // value field set in SerialDxeInitialize()? | |
| //--------- ------------------- ----------------------------- | |
| 0, // ControlMask | |
| 1000 * 1000, // Timeout | |
| 0, // BaudRate yes | |
| 1, // ReceiveFifoDepth | |
| 0, // DataBits yes | |
| 0, // Parity yes | |
| 0 // StopBits yes | |
| }; | |
| EFI_SERIAL_IO_PROTOCOL mSerialIoTemplate = { | |
| SERIAL_IO_INTERFACE_REVISION, | |
| SerialReset, | |
| SerialSetAttributes, | |
| SerialSetControl, | |
| SerialGetControl, | |
| SerialWrite, | |
| SerialRead, | |
| &mSerialIoMode | |
| }; | |
| /** | |
| Reset the serial device. | |
| @param This Protocol instance pointer. | |
| @retval EFI_SUCCESS The device was reset. | |
| @retval EFI_DEVICE_ERROR The serial device could not be reset. | |
| **/ | |
| EFI_STATUS | |
| EFIAPI | |
| SerialReset ( | |
| IN EFI_SERIAL_IO_PROTOCOL *This | |
| ) | |
| { | |
| EFI_STATUS Status; | |
| Status = SerialPortInitialize (); | |
| if (EFI_ERROR (Status)) { | |
| return Status; | |
| } | |
| // | |
| // 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 | |
| ); | |
| // | |
| // The serial device may not support some of the attributes. To prevent | |
| // later failure, always return EFI_SUCCESS when SetAttributes is returning | |
| // EFI_INVALID_PARAMETER. | |
| // | |
| if (Status == EFI_INVALID_PARAMETER) { | |
| return EFI_SUCCESS; | |
| } | |
| return Status; | |
| } | |
| /** | |
| Sets the baud rate, receive FIFO depth, transmit/receive time out, parity, | |
| data bits, and stop bits on a serial device. | |
| @param This Protocol instance pointer. | |
| @param BaudRate The requested baud rate. A BaudRate value of 0 will use the the | |
| device's default interface speed. | |
| @param ReceiveFifoDepth The requested depth of the FIFO on the receive side of the | |
| serial interface. A ReceiveFifoDepth value of 0 will use | |
| the device's default FIFO depth. | |
| @param Timeout The requested time out for a single character in microseconds. | |
| This timeout applies to both the transmit and receive side of the | |
| interface. A Timeout value of 0 will use the device's default time | |
| out value. | |
| @param Parity The type of parity to use on this serial device. A Parity value of | |
| DefaultParity will use the device's default parity value. | |
| @param DataBits The number of data bits to use on the serial device. A DataBits | |
| value of 0 will use the device's default data bit setting. | |
| @param StopBits The number of stop bits to use on this serial device. A StopBits | |
| value of DefaultStopBits will use the device's default number of | |
| stop bits. | |
| @retval EFI_SUCCESS The device was reset. | |
| @retval EFI_INVALID_PARAMETER One or more attributes has an unsupported value. | |
| @retval EFI_DEVICE_ERROR The serial device is not functioning correctly. | |
| **/ | |
| EFI_STATUS | |
| EFIAPI | |
| SerialSetAttributes ( | |
| 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 | |
| ) | |
| { | |
| EFI_STATUS Status; | |
| EFI_TPL Tpl; | |
| UINT64 OriginalBaudRate; | |
| UINT32 OriginalReceiveFifoDepth; | |
| UINT32 OriginalTimeout; | |
| EFI_PARITY_TYPE OriginalParity; | |
| UINT8 OriginalDataBits; | |
| EFI_STOP_BITS_TYPE OriginalStopBits; | |
| // | |
| // Preserve the original input values in case | |
| // SerialPortSetAttributes() updates the input/output | |
| // parameters even on error. | |
| // | |
| OriginalBaudRate = BaudRate; | |
| OriginalReceiveFifoDepth = ReceiveFifoDepth; | |
| OriginalTimeout = Timeout; | |
| OriginalParity = Parity; | |
| OriginalDataBits = DataBits; | |
| OriginalStopBits = StopBits; | |
| Status = SerialPortSetAttributes (&BaudRate, &ReceiveFifoDepth, &Timeout, &Parity, &DataBits, &StopBits); | |
| if (EFI_ERROR (Status)) { | |
| // | |
| // If it is just to set Timeout value and unsupported is returned, | |
| // do not return error. | |
| // | |
| if ((Status == EFI_UNSUPPORTED) && | |
| (This->Mode->Timeout != OriginalTimeout) && | |
| (This->Mode->ReceiveFifoDepth == OriginalReceiveFifoDepth) && | |
| (This->Mode->BaudRate == OriginalBaudRate) && | |
| (This->Mode->DataBits == (UINT32) OriginalDataBits) && | |
| (This->Mode->Parity == (UINT32) OriginalParity) && | |
| (This->Mode->StopBits == (UINT32) OriginalStopBits)) { | |
| // | |
| // Restore to the original input values. | |
| // | |
| BaudRate = OriginalBaudRate; | |
| ReceiveFifoDepth = OriginalReceiveFifoDepth; | |
| Timeout = OriginalTimeout; | |
| Parity = OriginalParity; | |
| DataBits = OriginalDataBits; | |
| StopBits = OriginalStopBits; | |
| Status = EFI_SUCCESS; | |
| } else if (Status == EFI_INVALID_PARAMETER || Status == EFI_UNSUPPORTED) { | |
| return EFI_INVALID_PARAMETER; | |
| } else { | |
| return EFI_DEVICE_ERROR; | |
| } | |
| } | |
| // | |
| // Set the Serial I/O mode and update the device path | |
| // | |
| Tpl = gBS->RaiseTPL (TPL_NOTIFY); | |
| // | |
| // Set the Serial I/O mode | |
| // | |
| This->Mode->ReceiveFifoDepth = ReceiveFifoDepth; | |
| This->Mode->Timeout = Timeout; | |
| This->Mode->BaudRate = BaudRate; | |
| This->Mode->DataBits = (UINT32) DataBits; | |
| This->Mode->Parity = (UINT32) Parity; | |
| This->Mode->StopBits = (UINT32) StopBits; | |
| // | |
| // Check if the device path has actually changed | |
| // | |
| if (mSerialDevicePath.Uart.BaudRate == BaudRate && | |
| mSerialDevicePath.Uart.DataBits == DataBits && | |
| mSerialDevicePath.Uart.Parity == (UINT8) Parity && | |
| mSerialDevicePath.Uart.StopBits == (UINT8) StopBits | |
| ) { | |
| gBS->RestoreTPL (Tpl); | |
| return EFI_SUCCESS; | |
| } | |
| // | |
| // Update the device path | |
| // | |
| mSerialDevicePath.Uart.BaudRate = BaudRate; | |
| mSerialDevicePath.Uart.DataBits = DataBits; | |
| mSerialDevicePath.Uart.Parity = (UINT8) Parity; | |
| mSerialDevicePath.Uart.StopBits = (UINT8) StopBits; | |
| Status = gBS->ReinstallProtocolInterface ( | |
| mSerialHandle, | |
| &gEfiDevicePathProtocolGuid, | |
| &mSerialDevicePath, | |
| &mSerialDevicePath | |
| ); | |
| gBS->RestoreTPL (Tpl); | |
| return Status; | |
| } | |
| /** | |
| Set the control bits on a serial device | |
| @param This Protocol instance pointer. | |
| @param Control Set the bits of Control that are settable. | |
| @retval EFI_SUCCESS The new control bits were set on the serial device. | |
| @retval EFI_UNSUPPORTED The serial device does not support this operation. | |
| @retval EFI_DEVICE_ERROR The serial device is not functioning correctly. | |
| **/ | |
| EFI_STATUS | |
| EFIAPI | |
| SerialSetControl ( | |
| IN EFI_SERIAL_IO_PROTOCOL *This, | |
| IN UINT32 Control | |
| ) | |
| { | |
| return SerialPortSetControl (Control); | |
| } | |
| /** | |
| Retrieves the status of the control bits on a serial device | |
| @param This Protocol instance pointer. | |
| @param Control A pointer to return the current Control signals from the serial device. | |
| @retval EFI_SUCCESS The control bits were read from the serial device. | |
| @retval EFI_DEVICE_ERROR The serial device is not functioning correctly. | |
| **/ | |
| EFI_STATUS | |
| EFIAPI | |
| SerialGetControl ( | |
| IN EFI_SERIAL_IO_PROTOCOL *This, | |
| OUT UINT32 *Control | |
| ) | |
| { | |
| return SerialPortGetControl (Control); | |
| } | |
| /** | |
| Writes data to a serial device. | |
| @param This Protocol instance pointer. | |
| @param BufferSize On input, the size of the Buffer. On output, the amount of | |
| data actually written. | |
| @param Buffer The buffer of data to write | |
| @retval EFI_SUCCESS The data was written. | |
| @retval EFI_DEVICE_ERROR The device reported an error. | |
| @retval EFI_TIMEOUT The data write was stopped due to a timeout. | |
| **/ | |
| EFI_STATUS | |
| EFIAPI | |
| SerialWrite ( | |
| IN EFI_SERIAL_IO_PROTOCOL *This, | |
| IN OUT UINTN *BufferSize, | |
| IN VOID *Buffer | |
| ) | |
| { | |
| UINTN Count; | |
| Count = SerialPortWrite (Buffer, *BufferSize); | |
| if (Count != *BufferSize) { | |
| *BufferSize = Count; | |
| return EFI_TIMEOUT; | |
| } | |
| return EFI_SUCCESS; | |
| } | |
| /** | |
| Reads data from a serial device. | |
| @param This Protocol instance pointer. | |
| @param BufferSize On input, the size of the Buffer. On output, the amount of | |
| data returned in Buffer. | |
| @param Buffer The buffer to return the data into. | |
| @retval EFI_SUCCESS The data was read. | |
| @retval EFI_DEVICE_ERROR The device reported an error. | |
| @retval EFI_TIMEOUT The data write was stopped due to a timeout. | |
| **/ | |
| EFI_STATUS | |
| EFIAPI | |
| SerialRead ( | |
| IN EFI_SERIAL_IO_PROTOCOL *This, | |
| IN OUT UINTN *BufferSize, | |
| OUT VOID *Buffer | |
| ) | |
| { | |
| UINTN Count; | |
| UINTN TimeOut; | |
| Count = 0; | |
| while (Count < *BufferSize) { | |
| TimeOut = 0; | |
| while (TimeOut < mSerialIoMode.Timeout) { | |
| if (SerialPortPoll ()) { | |
| break; | |
| } | |
| gBS->Stall (10); | |
| TimeOut += 10; | |
| } | |
| if (TimeOut >= mSerialIoMode.Timeout) { | |
| break; | |
| } | |
| SerialPortRead (Buffer, 1); | |
| Count++; | |
| Buffer = (VOID *) ((UINT8 *) Buffer + 1); | |
| } | |
| if (Count != *BufferSize) { | |
| *BufferSize = Count; | |
| return EFI_TIMEOUT; | |
| } | |
| return EFI_SUCCESS; | |
| } | |
| /** | |
| Initialization for the Serial Io Protocol. | |
| @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 | |
| SerialDxeInitialize ( | |
| IN EFI_HANDLE ImageHandle, | |
| IN EFI_SYSTEM_TABLE *SystemTable | |
| ) | |
| { | |
| EFI_STATUS Status; | |
| mSerialIoMode.BaudRate = PcdGet64 (PcdUartDefaultBaudRate); | |
| mSerialIoMode.DataBits = (UINT32) PcdGet8 (PcdUartDefaultDataBits); | |
| mSerialIoMode.Parity = (UINT32) PcdGet8 (PcdUartDefaultParity); | |
| mSerialIoMode.StopBits = (UINT32) PcdGet8 (PcdUartDefaultStopBits); | |
| mSerialIoMode.ReceiveFifoDepth = PcdGet16 (PcdUartDefaultReceiveFifoDepth); | |
| mSerialDevicePath.Uart.BaudRate = PcdGet64 (PcdUartDefaultBaudRate); | |
| mSerialDevicePath.Uart.DataBits = PcdGet8 (PcdUartDefaultDataBits); | |
| mSerialDevicePath.Uart.Parity = PcdGet8 (PcdUartDefaultParity); | |
| mSerialDevicePath.Uart.StopBits = PcdGet8 (PcdUartDefaultStopBits); | |
| // | |
| // Issue a reset to initialize the Serial Port | |
| // | |
| Status = mSerialIoTemplate.Reset (&mSerialIoTemplate); | |
| if (EFI_ERROR (Status)) { | |
| return Status; | |
| } | |
| // | |
| // Make a new handle with Serial IO protocol and its device path on it. | |
| // | |
| Status = gBS->InstallMultipleProtocolInterfaces ( | |
| &mSerialHandle, | |
| &gEfiSerialIoProtocolGuid, &mSerialIoTemplate, | |
| &gEfiDevicePathProtocolGuid, &mSerialDevicePath, | |
| NULL | |
| ); | |
| ASSERT_EFI_ERROR (Status); | |
| return Status; | |
| } | |