audk/OptionRomPkg/Bus/Usb/FtdiUsbSerialDxe/FtdiUsbSerialDriver.c

2581 lines
80 KiB
C

/** @file
USB Serial Driver that manages USB to Serial and produces Serial IO Protocol.
Copyright (c) 2004 - 2013, Intel Corporation. All rights reserved.
Portions Copyright 2012 Ashley DeSimone
SPDX-License-Identifier: BSD-2-Clause-Patent
**/
//
// Tested with VEND_ID 0x0403, DEVICE_ID 0x6001
//
// Driver starts the device with the following values:
// 115200, No parity, 8 data bits, 1 stop bit, No Flow control
//
#include "FtdiUsbSerialDriver.h"
//
// Table of supported devices. This is the device information that this
// driver was developed with. Add other FTDI devices as needed.
//
USB_DEVICE gUSBDeviceList[] = {
{VID_FTDI, DID_FTDI_FT232},
{0,0}
};
//
// USB Serial Driver Global Variables
//
EFI_DRIVER_BINDING_PROTOCOL gUsbSerialDriverBinding = {
UsbSerialDriverBindingSupported,
UsbSerialDriverBindingStart,
UsbSerialDriverBindingStop,
0xa,
NULL,
NULL
};
//
// Table with the nearest power of 2 for the numbers 0-15
//
UINT8 gRoundedPowersOf2[16] = { 0, 2, 2, 4, 4, 4, 8, 8, 8, 8, 8, 8, 16, 16, 16, 16 };
/**
Check to see if the device path node is the Flow control node
@param[in] FlowControl The device path node to be checked
@retval TRUE It is the flow control node
@retval FALSE It is not the flow control node
**/
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))
);
}
/**
Checks the device path to see if it contains flow control.
@param[in] DevicePath The device path to be checked
@retval TRUE It contains flow control
@retval FALSE It does not contain flow control
**/
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;
}
/**
Transfer the data between the device and host.
This function transfers the data between the device and host.
BOT transfer is composed of three phases: Command, Data, and Status.
This is the Data phase.
@param UsbBot[in] The USB BOT device
@param DataDir[in] The direction of the data
@param Data[in, out] The buffer to hold data
@param TransLen[in, out] The expected length of the data
@param Timeout[in] The time to wait the command to complete
@retval EFI_SUCCESS The data is transferred
@retval EFI_SUCCESS No data to transfer
@retval EFI_NOT_READY The device return NAK to the transfer
@retval Others Failed to transfer data
**/
EFI_STATUS
UsbSerialDataTransfer (
IN USB_SER_DEV *UsbBot,
IN EFI_USB_DATA_DIRECTION DataDir,
IN OUT VOID *Data,
IN OUT UINTN *TransLen,
IN UINT32 Timeout
)
{
EFI_USB_ENDPOINT_DESCRIPTOR *Endpoint;
EFI_STATUS Status;
UINT32 Result;
//
// If no data to transfer, just return EFI_SUCCESS.
//
if ((DataDir == EfiUsbNoData) || (*TransLen == 0)) {
return EFI_SUCCESS;
}
//
// Select the endpoint then issue the transfer
//
if (DataDir == EfiUsbDataIn) {
Endpoint = &UsbBot->InEndpointDescriptor;
} else {
Endpoint = &UsbBot->OutEndpointDescriptor;
}
Result = 0;
Status = UsbBot->UsbIo->UsbBulkTransfer (
UsbBot->UsbIo,
Endpoint->EndpointAddress,
Data,
TransLen,
Timeout,
&Result
);
if (EFI_ERROR (Status)) {
if (USB_IS_ERROR (Result, EFI_USB_ERR_NAK)) {
Status = EFI_NOT_READY;
} else {
UsbBot->Shutdown = TRUE; // Fixes infinite loop in older EFI
}
return Status;
}
return Status;
}
/**
Sets the status values of the Usb Serial Device.
@param UsbSerialDevice[in] Handle to the Usb Serial Device to set the status
for
@param StatusBuffer[in] Buffer holding the status values
@retval EFI_SUCCESS The status values were read and set correctly
**/
EFI_STATUS
EFIAPI
SetStatusInternal (
IN USB_SER_DEV *UsbSerialDevice,
IN UINT8 *StatusBuffer
)
{
UINT8 Msr;
Msr = (StatusBuffer[0] & MSR_MASK);
//
// set the Status values to disabled
//
UsbSerialDevice->StatusValues.CtsState = FALSE;
UsbSerialDevice->StatusValues.DsrState = FALSE;
UsbSerialDevice->StatusValues.RiState = FALSE;
UsbSerialDevice->StatusValues.SdState = FALSE;
//
// Check the values from the status buffer and set the appropriate status
// values to enabled
//
if ((Msr & CTS_MASK) == CTS_MASK) {
UsbSerialDevice->StatusValues.CtsState = TRUE;
}
if ((Msr & DSR_MASK) == DSR_MASK) {
UsbSerialDevice->StatusValues.DsrState = TRUE;
}
if ((Msr & RI_MASK) == RI_MASK) {
UsbSerialDevice->StatusValues.RiState = TRUE;
}
if ((Msr & SD_MASK) == SD_MASK) {
UsbSerialDevice->StatusValues.SdState = TRUE;
}
return EFI_SUCCESS;
}
/**
Initiates a read operation on the Usb Serial Device.
@param UsbSerialDevice[in] Handle to the USB device to read
@param BufferSize[in, out] On input, the size of the Buffer. On output,
the amount of data returned in Buffer.
Setting this to zero will initiate a read
and store all data returned in the internal
buffer.
@param Buffer [out] 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
ReadDataFromUsb (
IN USB_SER_DEV *UsbSerialDevice,
IN OUT UINTN *BufferSize,
OUT VOID *Buffer
)
{
EFI_STATUS Status;
UINTN ReadBufferSize;
UINT8 *ReadBuffer;
UINTN Index;
EFI_TPL Tpl;
UINT8 StatusBuffer[2]; // buffer to store the status bytes
ReadBufferSize = 512;
ReadBuffer = &(UsbSerialDevice->ReadBuffer[0]);
if (UsbSerialDevice->Shutdown) {
return EFI_DEVICE_ERROR;
}
Tpl = gBS->RaiseTPL (TPL_NOTIFY);
Status = UsbSerialDataTransfer (
UsbSerialDevice,
EfiUsbDataIn,
ReadBuffer,
&ReadBufferSize,
FTDI_TIMEOUT*2 //Padded because timers won't be exactly aligned
);
if (EFI_ERROR (Status)) {
gBS->RestoreTPL (Tpl);
if (Status == EFI_TIMEOUT) {
return EFI_TIMEOUT;
} else {
return EFI_DEVICE_ERROR;
}
}
//
// Store the status bytes in the status buffer
//
for (Index = 0; Index < 2; Index++) {//only the first 2 bytes are status bytes
StatusBuffer[Index] = ReadBuffer[Index];
}
//
// update the statusvalue field of the usbserialdevice
//
Status = SetStatusInternal (UsbSerialDevice, StatusBuffer);
if (Status != EFI_SUCCESS) {
}
//
// Store the read data in the read buffer, start at 2 to ignore status bytes
//
for (Index = 2; Index < ReadBufferSize; Index++) {
if (((UsbSerialDevice->DataBufferTail + 1) % SW_FIFO_DEPTH) == UsbSerialDevice->DataBufferHead) {
break;
}
if (ReadBuffer[Index] == 0x00) {
//
// This is null, do not add
//
} else {
UsbSerialDevice->DataBuffer[UsbSerialDevice->DataBufferTail] = ReadBuffer[Index];
UsbSerialDevice->DataBufferTail = (UsbSerialDevice->DataBufferTail + 1) % SW_FIFO_DEPTH;
}
}
//
// Read characters out of the buffer to satisfy caller's request.
//
for (Index = 0; Index < *BufferSize; Index++) {
if (UsbSerialDevice->DataBufferHead == UsbSerialDevice->DataBufferTail) {
break;
}
//
// Still have characters in the buffer to return
//
((UINT8 *)Buffer)[Index] = UsbSerialDevice->DataBuffer[UsbSerialDevice->DataBufferHead];
UsbSerialDevice->DataBufferHead = (UsbSerialDevice->DataBufferHead + 1) % SW_FIFO_DEPTH;
}
//
// Return actual number of bytes returned.
//
*BufferSize = Index;
gBS->RestoreTPL (Tpl);
return EFI_SUCCESS;
}
/**
Sets the initial status values of the Usb Serial Device by reading the status
bytes from the device.
@param UsbSerialDevice[in] Handle to the Usb Serial Device that needs its
initial status values set
@retval EFI_SUCCESS The status bytes were read successfully and the
initial status values were set correctly
@retval EFI_TIMEOUT The read of the status bytes was stopped due to a
timeout
@retval EFI_DEVICE_ERROR The device reported an error during the read of
the status bytes
**/
EFI_STATUS
EFIAPI
SetInitialStatus (
IN USB_SER_DEV *UsbSerialDevice
)
{
EFI_STATUS Status;
UINTN BufferSize;
EFI_TPL Tpl;
UINT8 StatusBuffer[2];
Status = EFI_UNSUPPORTED;
BufferSize = sizeof (StatusBuffer);
if (UsbSerialDevice->Shutdown) {
return EFI_DEVICE_ERROR;
}
Tpl = gBS->RaiseTPL (TPL_NOTIFY);
Status = UsbSerialDataTransfer (
UsbSerialDevice,
EfiUsbDataIn,
StatusBuffer,
&BufferSize,
40 //Slightly more than 2x the FTDI polling frequency to make sure that data will be returned
);
Status = SetStatusInternal (UsbSerialDevice, StatusBuffer);
gBS->RestoreTPL (Tpl);
return Status;
}
/**
UsbSerialDriverCheckInput.
attempts to read data in from the device periodically, stores any read data
and updates the control attributes.
@param Event[in]
@param Context[in]....The current instance of the USB serial device
**/
VOID
EFIAPI
UsbSerialDriverCheckInput (
IN EFI_EVENT Event,
IN VOID *Context
)
{
UINTN BufferSize;
USB_SER_DEV *UsbSerialDevice;
UsbSerialDevice = (USB_SER_DEV*)Context;
if (UsbSerialDevice->DataBufferHead == UsbSerialDevice->DataBufferTail) {
//
// Data buffer is empty, try to read from device
//
BufferSize = 0;
ReadDataFromUsb (UsbSerialDevice, &BufferSize, NULL);
if (UsbSerialDevice->DataBufferHead == UsbSerialDevice->DataBufferTail) {
//
// Data buffer still has no data, set the EFI_SERIAL_INPUT_BUFFER_EMPTY
// flag
//
UsbSerialDevice->ControlBits |= EFI_SERIAL_INPUT_BUFFER_EMPTY;
} else {
//
// Read has returned some data, clear the EFI_SERIAL_INPUT_BUFFER_EMPTY
// flag
//
UsbSerialDevice->ControlBits &= ~(EFI_SERIAL_INPUT_BUFFER_EMPTY);
}
} else {
//
// Data buffer has data, no read attempt required
//
UsbSerialDevice->ControlBits &= ~(EFI_SERIAL_INPUT_BUFFER_EMPTY);
}
}
/**
Encodes the baud rate into the format expected by the Ftdi device.
@param BaudRate[in] The baudrate to be set on the device
@param EncodedBaudRate[out] The baud rate encoded in the format
expected by the Ftdi device
@return EFI_SUCCESS Baudrate encoding was calculated
successfully
@return EFI_INVALID_PARAMETER An invalid value of BaudRate was received
**/
EFI_STATUS
EFIAPI
EncodeBaudRateForFtdi (
IN UINT64 BaudRate,
OUT UINT16 *EncodedBaudRate
)
{
UINT32 Divisor;
UINT32 AdjustedFrequency;
UINT16 Result;
//
// Check to make sure we won't get an integer overflow
//
if ((BaudRate < 178) || ( BaudRate > ((FTDI_UART_FREQUENCY * 100) / 97))) {
return EFI_INVALID_PARAMETER;
}
//
// Baud Rates of 2000000 and 3000000 are special cases
//
if ((BaudRate >= FTDI_SPECIAL_CASE_300_MIN) && (BaudRate <= FTDI_SPECIAL_CASE_300_MAX)) {
*EncodedBaudRate = 0;
return EFI_SUCCESS;
}
if ((BaudRate >= FTDI_SPECIAL_CASE_200_MIN) && (BaudRate <= FTDI_SPECIAL_CASE_200_MAX)) {
*EncodedBaudRate = 1;
return EFI_SUCCESS;
}
//
// Compute divisor
//
Divisor = (FTDI_UART_FREQUENCY << 4) / (UINT32)BaudRate;
//
// Round the last 4 bits to the nearest power of 2
//
Divisor = (Divisor & ~(0xF)) + (gRoundedPowersOf2[Divisor & 0xF]);
//
// Check to make sure computed divisor is within
// the min and max that FTDI controller will accept
//
if (Divisor < FTDI_MIN_DIVISOR) {
Divisor = FTDI_MIN_DIVISOR;
} else if (Divisor > FTDI_MAX_DIVISOR) {
Divisor = FTDI_MAX_DIVISOR;
}
//
// Check to make sure the frequency that the FTDI chip will need to
// generate to attain the requested Baud Rate is within 3% of the
// 3MHz clock frequency that the FTDI chip runs at.
//
// (3MHz * 1600) / 103 = 46601941
// (3MHz * 1600) / 97 = 49484536
//
AdjustedFrequency = (((UINT32)BaudRate) * Divisor);
if ((AdjustedFrequency < FTDI_MIN_FREQUENCY) || (AdjustedFrequency > FTDI_MAX_FREQUENCY)) {
return EFI_INVALID_PARAMETER;
}
//
// Encode the Divisor into the format FTDI expects
//
Result = (UINT16)(Divisor >> 4);
if ((Divisor & 0x8) != 0) {
Result |= 0x4000;
} else if ((Divisor & 0x4) != 0) {
Result |= 0x8000;
} else if ((Divisor & 0x2) != 0) {
Result |= 0xC000;
}
*EncodedBaudRate = Result;
return EFI_SUCCESS;
}
/**
Uses USB I/O to check whether the device is a USB Serial device.
@param UsbIo[in] Pointer to a USB I/O protocol instance.
@retval TRUE Device is a USB Serial device.
@retval FALSE Device is a not USB Serial device.
**/
BOOLEAN
IsUsbSerial (
IN EFI_USB_IO_PROTOCOL *UsbIo
)
{
EFI_STATUS Status;
EFI_USB_DEVICE_DESCRIPTOR DeviceDescriptor;
CHAR16 *StrMfg;
BOOLEAN Found;
UINT32 Index;
//
// Get the default device descriptor
//
Status = UsbIo->UsbGetDeviceDescriptor (
UsbIo,
&DeviceDescriptor
);
if (EFI_ERROR (Status)) {
return FALSE;
}
Found = FALSE;
Index = 0;
while (gUSBDeviceList[Index].VendorId != 0 &&
gUSBDeviceList[Index].DeviceId != 0 &&
!Found ) {
if (DeviceDescriptor.IdProduct == gUSBDeviceList[Index].DeviceId &&
DeviceDescriptor.IdVendor == gUSBDeviceList[Index].VendorId ){
//
// Checks to see if a string descriptor can be pulled from the device in
// the selected language. If not False is returned indicating that this
// is not a Usb Serial Device that can be managegd by this driver
//
StrMfg = NULL;
Status = UsbIo->UsbGetStringDescriptor (
UsbIo,
USB_US_LANG_ID, // LANGID selector, should make this
// more robust to verify lang support
// for device
DeviceDescriptor.StrManufacturer,
&StrMfg
);
if (StrMfg != NULL) {
FreePool (StrMfg);
}
if (EFI_ERROR (Status)) {
return FALSE;
}
return TRUE;
}
Index++;
}
return FALSE;
}
/**
Internal function that sets the Data Bits, Stop Bits and Parity values on the
Usb Serial Device with a single usb control transfer.
@param UsbIo[in] Usb Io Protocol instance pointer
@param DataBits[in] The data bits value to be set on the Usb
Serial Device
@param Parity[in] The parity type that will be set on the Usb
Serial Device
@param StopBits[in] The stop bits type that will be set on the
Usb Serial Device
@param LastSettings[in] A pointer to the Usb Serial Device's
PREVIOUS_ATTRIBUTES item
@retval EFI_SUCCESS The data items were correctly set on the
USB Serial Device
@retval EFI_INVALID_PARAMETER An invalid data parameter or an invalid
combination or parameters was used
@retval EFI_DEVICE_ERROR The device is not functioning correctly and
the data values were unable to be set
**/
EFI_STATUS
EFIAPI
SetDataInternal (
IN EFI_USB_IO_PROTOCOL *UsbIo,
IN UINT8 DataBits,
IN EFI_PARITY_TYPE Parity,
IN EFI_STOP_BITS_TYPE StopBits,
IN PREVIOUS_ATTRIBUTES *LastSettings
)
{
EFI_STATUS Status;
EFI_USB_DEVICE_REQUEST DevReq;
UINT32 ReturnValue;
UINT8 ConfigurationValue;
//
// Since data bits settings of 6,7,8 cannot be set with a stop bits setting of
// 1.5 check to see if this happens when the values of last settings are used
//
if ((DataBits == 0) && (StopBits == OneFiveStopBits)) {
if ((LastSettings->DataBits == 6) || (LastSettings->DataBits == 7) || (LastSettings->DataBits == 8)) {
return EFI_INVALID_PARAMETER;
}
} else if ((StopBits == DefaultStopBits) && ((DataBits == 6) || (DataBits == 7) || (DataBits == 8))) {
if (LastSettings->StopBits == OneFiveStopBits) {
return EFI_INVALID_PARAMETER;
}
} else if ((DataBits == 0) && (StopBits == DefaultStopBits)) {
if (LastSettings->StopBits == OneFiveStopBits) {
if ((LastSettings->DataBits == 6) || (LastSettings->DataBits == 7) || (LastSettings->DataBits == 8)) {
return EFI_INVALID_PARAMETER;
}
}
}
//
// set the DevReq.Value for the usb control transfer to the correct value
// based on the seleceted number of data bits if there is an invalid number of
// data bits requested return EFI_INVALID_PARAMETER
//
if (((DataBits < 5 ) || (DataBits > 8)) && (DataBits != 0)) {
return EFI_INVALID_PARAMETER;
}
if (DataBits == 0) {
//
// use the value of LastDataBits
//
DevReq.Value = SET_DATA_BITS (LastSettings->DataBits);
} else {
//
// use the value of DataBits
//
DevReq.Value = SET_DATA_BITS (DataBits);
}
//
// Set Parity
//
if (Parity == DefaultParity) {
Parity = LastSettings->Parity;
}
if (Parity == NoParity) {
DevReq.Value |= SET_PARITY_NONE;
} else if (Parity == EvenParity) {
DevReq.Value |= SET_PARITY_EVEN;
} else if (Parity == OddParity){
DevReq.Value |= SET_PARITY_ODD;
} else if (Parity == MarkParity) {
DevReq.Value |= SET_PARITY_MARK;
} else if (Parity == SpaceParity) {
DevReq.Value |= SET_PARITY_SPACE;
}
//
// Set Stop Bits
//
if (StopBits == DefaultStopBits) {
StopBits = LastSettings->StopBits;
}
if (StopBits == OneStopBit) {
DevReq.Value |= SET_STOP_BITS_1;
} else if (StopBits == OneFiveStopBits) {
DevReq.Value |= SET_STOP_BITS_15;
} else if (StopBits == TwoStopBits) {
DevReq.Value |= SET_STOP_BITS_2;
}
//
// set the rest of the DevReq parameters and perform the usb control transfer
// to set the data bits on the device
//
DevReq.Request = FTDI_COMMAND_SET_DATA;
DevReq.RequestType = USB_REQ_TYPE_VENDOR;
DevReq.Index = FTDI_PORT_IDENTIFIER;
DevReq.Length = 0; // indicates that there is no data phase in this request
Status = UsbIo->UsbControlTransfer (
UsbIo,
&DevReq,
EfiUsbDataOut,
WDR_SHORT_TIMEOUT,
&ConfigurationValue,
1,
&ReturnValue
);
if (EFI_ERROR (Status)) {
goto StatusError;
}
return Status;
StatusError:
if ((Status != EFI_INVALID_PARAMETER) || (Status != EFI_DEVICE_ERROR)) {
return EFI_DEVICE_ERROR;
} else {
return Status;
}
}
/**
Internal function that sets the baudrate on the Usb Serial Device.
@param UsbIo[in] Usb Io Protocol instance pointer
@param BaudRate[in] The baudrate value to be set on the device.
If this value is 0 the value of LastBaudRate
will be used instead
@param LastBaudRate[in] The baud rate value that was previously set
on the Usb Serial Device
@retval EFI_SUCCESS The baudrate was set succesfully
@retval EFI_INVALID_PARAMETER An invalid baudrate was used
@retval EFI_DEVICE_ERROR The device is not functioning correctly and
the baudrate was unable to be set
**/
EFI_STATUS
EFIAPI
SetBaudRateInternal (
IN EFI_USB_IO_PROTOCOL *UsbIo,
IN UINT64 BaudRate,
IN UINT64 LastBaudRate
)
{
EFI_STATUS Status;
EFI_USB_DEVICE_REQUEST DevReq;
UINT32 ReturnValue;
UINT8 ConfigurationValue;
UINT16 EncodedBaudRate;
EFI_TPL Tpl;
Tpl = gBS->RaiseTPL(TPL_NOTIFY);
//
// set the value of DevReq.Value based on the value of BaudRate
// if 0 is selected as baud rate use the value of LastBaudRate
//
if (BaudRate == 0) {
Status = EncodeBaudRateForFtdi (LastBaudRate, &EncodedBaudRate);
if (EFI_ERROR (Status)) {
gBS->RestoreTPL (Tpl);
//
// EncodeBaudRateForFtdi returns EFI_INVALID_PARAMETER when not
// succesfull
//
return Status;
}
DevReq.Value = EncodedBaudRate;
} else {
Status = EncodeBaudRateForFtdi (BaudRate, &EncodedBaudRate);
if (EFI_ERROR (Status)) {
gBS->RestoreTPL (Tpl);
//
// EncodeBaudRateForFtdi returns EFI_INVALID_PARAMETER when not
// successfull
//
return Status;
}
DevReq.Value = EncodedBaudRate;
}
//
// set the remaining parameters of DevReq and perform the usb control transfer
// to set the device
//
DevReq.Request = FTDI_COMMAND_SET_BAUDRATE;
DevReq.RequestType = USB_REQ_TYPE_VENDOR;
DevReq.Index = FTDI_PORT_IDENTIFIER;
DevReq.Length = 0; // indicates that there is no data phase in this request
Status = UsbIo->UsbControlTransfer (
UsbIo,
&DevReq,
EfiUsbDataOut,
WDR_SHORT_TIMEOUT,
&ConfigurationValue,
1,
&ReturnValue
);
if (EFI_ERROR (Status)) {
goto StatusError;
}
gBS->RestoreTPL (Tpl);
return Status;
StatusError:
gBS->RestoreTPL (Tpl);
if ((Status != EFI_INVALID_PARAMETER) || (Status != EFI_DEVICE_ERROR)) {
return EFI_DEVICE_ERROR;
} else {
return Status;
}
}
/**
Sets the baud rate, receive FIFO depth, transmit/receice time out, parity,
data bits, and stop bits on a serial device.
@param UsbSerialDevice[in] Pointer to the current instance of the USB Serial
Device.
@param BaudRate[in] The requested baud rate. A BaudRate value of 0
will use the device's default interface speed.
@param ReveiveFifoDepth[in] 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[in] 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[in] 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[in] 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[in] 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 attributes were set
@retval EFI_DEVICE_ERROR The attributes were not able to be set
**/
EFI_STATUS
EFIAPI
SetAttributesInternal (
IN USB_SER_DEV *UsbSerialDevice,
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;
UART_DEVICE_PATH *Uart;
EFI_DEVICE_PATH_PROTOCOL *RemainingDevicePath;
Status = EFI_UNSUPPORTED;
Tpl = gBS->RaiseTPL(TPL_NOTIFY);
Uart = NULL;
//
// check for invalid combinations of parameters
//
if (((DataBits >= 6) && (DataBits <= 8)) && (StopBits == OneFiveStopBits)) {
return EFI_INVALID_PARAMETER;
}
//
// set data bits, parity and stop bits
//
Status = SetDataInternal (
UsbSerialDevice->UsbIo,
DataBits,
Parity,
StopBits,
&(UsbSerialDevice->LastSettings)
);
if (EFI_ERROR (Status)) {
goto StatusError;
}
//
// set baudrate
//
Status = SetBaudRateInternal (
UsbSerialDevice->UsbIo,
BaudRate,
UsbSerialDevice->LastSettings.BaudRate
);
if (EFI_ERROR (Status)){
goto StatusError;
}
//
// update the values of UsbSerialDevice->LastSettings and UsbSerialDevice->SerialIo.Mode
//
if (BaudRate == 0) {
UsbSerialDevice->LastSettings.BaudRate = UsbSerialDevice->LastSettings.BaudRate;
UsbSerialDevice->SerialIo.Mode->BaudRate = UsbSerialDevice->LastSettings.BaudRate;
} else {
UsbSerialDevice->LastSettings.BaudRate = BaudRate;
UsbSerialDevice->SerialIo.Mode->BaudRate = BaudRate;
}
UsbSerialDevice->LastSettings.Timeout = FTDI_TIMEOUT;
UsbSerialDevice->LastSettings.ReceiveFifoDepth = FTDI_MAX_RECEIVE_FIFO_DEPTH;
if (Parity == DefaultParity) {
UsbSerialDevice->LastSettings.Parity = UsbSerialDevice->LastSettings.Parity;
UsbSerialDevice->SerialIo.Mode->Parity = UsbSerialDevice->LastSettings.Parity;
} else {
UsbSerialDevice->LastSettings.Parity = Parity;
UsbSerialDevice->SerialIo.Mode->Parity = Parity;
}
if (DataBits == 0) {
UsbSerialDevice->LastSettings.DataBits = UsbSerialDevice->LastSettings.DataBits;
UsbSerialDevice->SerialIo.Mode->DataBits = UsbSerialDevice->LastSettings.DataBits;
} else {
UsbSerialDevice->LastSettings.DataBits = DataBits;
UsbSerialDevice->SerialIo.Mode->DataBits = DataBits;
}
if (StopBits == DefaultStopBits) {
UsbSerialDevice->LastSettings.StopBits = UsbSerialDevice->LastSettings.StopBits;
UsbSerialDevice->SerialIo.Mode->StopBits = UsbSerialDevice->LastSettings.StopBits;
} else {
UsbSerialDevice->LastSettings.StopBits = StopBits;
UsbSerialDevice->SerialIo.Mode->StopBits = StopBits;
}
//
// See if the device path node has changed
//
if (UsbSerialDevice->UartDevicePath.BaudRate == BaudRate &&
UsbSerialDevice->UartDevicePath.DataBits == DataBits &&
UsbSerialDevice->UartDevicePath.StopBits == StopBits &&
UsbSerialDevice->UartDevicePath.Parity == Parity
) {
gBS->RestoreTPL (Tpl);
return EFI_SUCCESS;
}
//
// Update the device path
//
UsbSerialDevice->UartDevicePath.BaudRate = BaudRate;
UsbSerialDevice->UartDevicePath.DataBits = DataBits;
UsbSerialDevice->UartDevicePath.StopBits = (UINT8) StopBits;
UsbSerialDevice->UartDevicePath.Parity = (UINT8) Parity;
Status = EFI_SUCCESS;
if (UsbSerialDevice->ControllerHandle != NULL) {
RemainingDevicePath = UsbSerialDevice->DevicePath;
while (!IsDevicePathEnd (RemainingDevicePath)) {
Uart = (UART_DEVICE_PATH *) NextDevicePathNode (RemainingDevicePath);
if (Uart->Header.Type == MESSAGING_DEVICE_PATH &&
Uart->Header.SubType == MSG_UART_DP &&
sizeof (UART_DEVICE_PATH) == DevicePathNodeLength ((EFI_DEVICE_PATH *) Uart)) {
Uart->BaudRate = BaudRate;
Uart->DataBits = DataBits;
Uart->StopBits = (UINT8)StopBits;
Uart->Parity = (UINT8) Parity;
break;
}
RemainingDevicePath = NextDevicePathNode (RemainingDevicePath);
}
}
gBS->RestoreTPL (Tpl);
return Status;
StatusError:
gBS->RestoreTPL (Tpl);
if ((Status != EFI_INVALID_PARAMETER) || (Status != EFI_DEVICE_ERROR)) {
return EFI_DEVICE_ERROR;
} else {
return Status;
}
}
/**
Internal function that performs a Usb Control Transfer to set the flow control
on the Usb Serial Device.
@param UsbIo[in] Usb Io Protocol instance pointer
@param FlowControlEnable[in] Data on the Enable/Disable status of Flow
Control on the Usb Serial Device
@retval EFI_SUCCESS The flow control was set on the Usb Serial
device
@retval EFI_INVALID_PARAMETER An invalid flow control value was used
@retval EFI_EFI_UNSUPPORTED The operation is not supported
@retval EFI_DEVICE_ERROR The device is not functioning correctly
**/
EFI_STATUS
EFIAPI
SetFlowControlInternal (
IN EFI_USB_IO_PROTOCOL *UsbIo,
IN BOOLEAN FlowControlEnable
)
{
EFI_STATUS Status;
EFI_USB_DEVICE_REQUEST DevReq;
UINT32 ReturnValue;
UINT8 ConfigurationValue;
//
// set DevReq.Value based on the value of FlowControlEnable
//
if (!FlowControlEnable) {
DevReq.Value = NO_FLOW_CTRL;
}
if (FlowControlEnable) {
DevReq.Value = XON_XOFF_CTRL;
}
//
// set the remaining DevReq parameters and perform the usb control transfer to
// set the flow control on the device
//
DevReq.Request = FTDI_COMMAND_SET_FLOW_CTRL;
DevReq.RequestType = USB_REQ_TYPE_VENDOR;
DevReq.Index = FTDI_PORT_IDENTIFIER;
DevReq.Length = 0; // indicates that this transfer has no data phase
Status = UsbIo->UsbControlTransfer (
UsbIo,
&DevReq,
EfiUsbDataOut,
WDR_TIMEOUT,
&ConfigurationValue,
1,
&ReturnValue
);
if (EFI_ERROR (Status)) {
goto StatusError;
}
return Status;
StatusError:
if ((Status != EFI_INVALID_PARAMETER) ||
(Status != EFI_DEVICE_ERROR) ||
(Status != EFI_UNSUPPORTED) ) {
return EFI_DEVICE_ERROR;
} else {
return Status;
}
}
/**
Internal function that performs a Usb Control Transfer to set the Dtr value on
the Usb Serial Device.
@param UsbIo[in] Usb Io Protocol instance pointer
@param DtrEnable[in] Data on the Enable/Disable status of the
Dtr for the Usb Serial Device
@retval EFI_SUCCESS The Dtr value was set on the Usb Serial
Device
@retval EFI_INVALID_PARAMETER An invalid Dtr value was used
@retval EFI_UNSUPPORTED The operation is not supported
@retval EFI_DEVICE_ERROR The device is not functioning correctly
**/
EFI_STATUS
EFIAPI
SetDtrInternal (
IN EFI_USB_IO_PROTOCOL *UsbIo,
IN BOOLEAN DtrEnable
)
{
EFI_STATUS Status;
EFI_USB_DEVICE_REQUEST DevReq;
UINT32 ReturnValue;
UINT8 ConfigurationValue;
//
// set the value of DevReq.Value based on the value of DtrEnable
//
if (!DtrEnable) {
DevReq.Value = SET_DTR_LOW;
}
if (DtrEnable) {
DevReq.Value = SET_DTR_HIGH;
}
//
// set the remaining attributes of DevReq and perform the usb control transfer
// to set the device
//
DevReq.Request = FTDI_COMMAND_MODEM_CTRL;
DevReq.RequestType = USB_REQ_TYPE_VENDOR;
DevReq.Index = FTDI_PORT_IDENTIFIER;
DevReq.Length = 0; // indicates that there is no data phase in this transfer
Status = UsbIo->UsbControlTransfer (
UsbIo,
&DevReq,
EfiUsbDataOut,
WDR_TIMEOUT,
&ConfigurationValue,
1,
&ReturnValue
);
if (EFI_ERROR (Status)) {
goto StatusError;
}
return Status;
StatusError:
if ((Status != EFI_INVALID_PARAMETER) ||
(Status != EFI_DEVICE_ERROR) ||
(Status != EFI_UNSUPPORTED) ) {
return EFI_DEVICE_ERROR;
} else {
return Status;
}
}
/**
Internal function that performs a Usb Control Transfer to set the Dtr value on
the Usb Serial Device.
@param UsbIo[in] Usb Io Protocol instance pointer
@param RtsEnable[in] Data on the Enable/Disable status of the
Rts for the Usb Serial Device
@retval EFI_SUCCESS The Rts value was set on the Usb Serial
Device
@retval EFI_INVALID_PARAMETER An invalid Rts value was used
@retval EFI_UNSUPPORTED The operation is not supported
@retval EFI_DEVICE_ERROR The device is not functioning correctly
**/
EFI_STATUS
EFIAPI
SetRtsInternal (
IN EFI_USB_IO_PROTOCOL *UsbIo,
IN BOOLEAN RtsEnable
)
{
EFI_STATUS Status;
EFI_USB_DEVICE_REQUEST DevReq;
UINT32 ReturnValue;
UINT8 ConfigurationValue;
//
// set DevReq.Value based on the value of RtsEnable
//
if (!RtsEnable) {
DevReq.Value = SET_RTS_LOW;
}
if (RtsEnable) {
DevReq.Value = SET_RTS_HIGH;
}
//
// set the remaining parameters of DevReq and perform the usb control transfer
// to set the values on the device
//
DevReq.Request = FTDI_COMMAND_MODEM_CTRL;
DevReq.RequestType = USB_REQ_TYPE_VENDOR;
DevReq.Index = FTDI_PORT_IDENTIFIER;
DevReq.Length = 0; // indicates that there is no data phase in this request
Status = UsbIo->UsbControlTransfer (
UsbIo,
&DevReq,
EfiUsbDataOut,
WDR_TIMEOUT,
&ConfigurationValue,
1,
&ReturnValue
);
if (EFI_ERROR (Status)) {
goto StatusError;
}
return Status;
StatusError:
if ((Status != EFI_INVALID_PARAMETER) ||
(Status != EFI_DEVICE_ERROR) ||
(Status != EFI_UNSUPPORTED) ) {
return EFI_DEVICE_ERROR;
} else {
return Status;
}
}
/**
Internal function that checks for valid control values and sets the control
bits on the Usb Serial Device.
@param UsbSerialDevice[in] Handle to the Usb Serial Device whose
control bits are being set
@param Control[in] The control value passed to the function
that contains the values of the control
bits that are being set
@retval EFI_SUCCESS The control bits were set on the Usb Serial
Device
@retval EFI_INVALID_PARAMETER An invalid control value was encountered
@retval EFI_EFI_UNSUPPORTED The operation is not supported
@retval EFI_DEVICE_ERROR The device is not functioning correctly
**/
EFI_STATUS
EFIAPI
SetControlBitsInternal (
IN USB_SER_DEV *UsbSerialDevice,
IN CONTROL_BITS *Control
)
{
EFI_STATUS Status;
UART_FLOW_CONTROL_DEVICE_PATH *FlowControl;
EFI_DEVICE_PATH_PROTOCOL *RemainingDevicePath;
//
// check for invalid control parameters hardware and software loopback enabled
// must always be set to FALSE
//
Control->HardwareLoopBack = FALSE;
Control->SoftwareLoopBack = FALSE;
//
// set hardware flow control
//
Status = SetFlowControlInternal (
UsbSerialDevice->UsbIo,
Control->HardwareFlowControl
);
if (EFI_ERROR (Status)) {
goto StatusError;
}
//
// set Dtr state
//
Status = SetDtrInternal (UsbSerialDevice->UsbIo, Control->DtrState);
if (EFI_ERROR (Status)) {
goto StatusError;
}
//
// set Rts state
//
Status = SetRtsInternal (UsbSerialDevice->UsbIo, Control->RtsState);
if (EFI_ERROR (Status)){
goto StatusError;
}
//
// update the remaining control values for UsbSerialDevice->ControlValues
//
UsbSerialDevice->ControlValues.DtrState = Control->DtrState;
UsbSerialDevice->ControlValues.RtsState = Control->RtsState;
UsbSerialDevice->ControlValues.HardwareFlowControl = Control->HardwareFlowControl;
UsbSerialDevice->ControlValues.HardwareLoopBack = FALSE;
UsbSerialDevice->ControlValues.SoftwareLoopBack = FALSE;
Status = EFI_SUCCESS;
//
// Update the device path to have the correct flow control values
//
if (UsbSerialDevice->ControllerHandle != NULL) {
RemainingDevicePath = UsbSerialDevice->DevicePath;
while (!IsDevicePathEnd (RemainingDevicePath)) {
FlowControl = (UART_FLOW_CONTROL_DEVICE_PATH *) NextDevicePathNode (RemainingDevicePath);
if (FlowControl->Header.Type == MESSAGING_DEVICE_PATH &&
FlowControl->Header.SubType == MSG_VENDOR_DP &&
sizeof (UART_FLOW_CONTROL_DEVICE_PATH) == DevicePathNodeLength ((EFI_DEVICE_PATH *) FlowControl)){
if (UsbSerialDevice->ControlValues.HardwareFlowControl == TRUE) {
FlowControl->FlowControlMap = UART_FLOW_CONTROL_HARDWARE;
} else if (UsbSerialDevice->ControlValues.HardwareFlowControl == FALSE) {
FlowControl->FlowControlMap = 0;
}
break;
}
RemainingDevicePath = NextDevicePathNode (RemainingDevicePath);
}
}
return Status;
StatusError:
if ((Status != EFI_INVALID_PARAMETER) ||
(Status != EFI_DEVICE_ERROR) ||
(Status != EFI_UNSUPPORTED) ) {
return EFI_DEVICE_ERROR;
} else {
return Status;
}
}
/**
Internal function that calculates the Control value used by GetControlBits()
based on the status and control values of the Usb Serial Device.
@param UsbSerialDevice[in] Handle to the Usb Serial Devie whose status
and control values are being used to set
Control
@param Control[out] On output the formated value of Control
that has been calculated based on the
control and status values of the Usb Serial
Device
@retval EFI_SUCCESS The value of Control was successfully
calculated
**/
EFI_STATUS
EFIAPI
GetControlBitsInternal (
IN USB_SER_DEV *UsbSerialDevice,
OUT UINT32 *Control
)
{
*Control = 0;
//
// Check the values of UsbSerialDevice->Status Values and modify control
// accordingly these values correspond to the modem status register
//
if (UsbSerialDevice->StatusValues.CtsState) {
*Control |= EFI_SERIAL_CLEAR_TO_SEND;
}
if (UsbSerialDevice->StatusValues.DsrState) {
*Control |= EFI_SERIAL_DATA_SET_READY;
}
if (UsbSerialDevice->StatusValues.RiState) {
*Control |= EFI_SERIAL_RING_INDICATE;
}
if (UsbSerialDevice->StatusValues.SdState) {
*Control |= EFI_SERIAL_CARRIER_DETECT;
}
//
// check the values of UsbSerialDevice->ControlValues and modify control
// accordingly these values correspond to the values of the Modem Control
// Register
//
if (UsbSerialDevice->ControlValues.DtrState) {
*Control |= EFI_SERIAL_DATA_TERMINAL_READY;
}
if (UsbSerialDevice->ControlValues.RtsState) {
*Control |= EFI_SERIAL_REQUEST_TO_SEND;
}
if (UsbSerialDevice->ControlValues.HardwareLoopBack) {
*Control |= EFI_SERIAL_HARDWARE_LOOPBACK_ENABLE;
}
if (UsbSerialDevice->ControlValues.HardwareFlowControl) {
*Control |= EFI_SERIAL_HARDWARE_FLOW_CONTROL_ENABLE;
}
//
// check if the buffer is empty since only one is being used if it is empty
// set both the receive and transmit buffers to empty
//
if (UsbSerialDevice->DataBufferHead == UsbSerialDevice->DataBufferTail) {
*Control |= EFI_SERIAL_OUTPUT_BUFFER_EMPTY;
*Control |= EFI_SERIAL_INPUT_BUFFER_EMPTY;
}
//
// check for software loopback enable in UsbSerialDevice->ControlValues
//
if (UsbSerialDevice->ControlValues.SoftwareLoopBack) {
*Control |= EFI_SERIAL_SOFTWARE_LOOPBACK_ENABLE;
}
return EFI_SUCCESS;
}
/**
Resets the USB Serial Device
This function is the internal method for resetting the device and is called by
SerialReset()
@param UsbSerialDevice[in] A pointer to the USB Serial device
@retval EFI_SUCCESS The device was reset
@retval EFI_DEVICE_ERROR The device could not be reset
**/
EFI_STATUS
EFIAPI
ResetInternal (
IN USB_SER_DEV *UsbSerialDevice
)
{
EFI_STATUS Status;
EFI_USB_DEVICE_REQUEST DevReq;
UINT8 ConfigurationValue;
UINT32 ReturnValue;
DevReq.Request = FTDI_COMMAND_RESET_PORT;
DevReq.RequestType = USB_REQ_TYPE_VENDOR;
DevReq.Value = RESET_PORT_PURGE_RX;
DevReq.Index = FTDI_PORT_IDENTIFIER;
DevReq.Length = 0; //indicates that there is not data phase in this request
Status = UsbSerialDevice->UsbIo->UsbControlTransfer (
UsbSerialDevice->UsbIo,
&DevReq,
EfiUsbDataIn,
WDR_TIMEOUT,
&ConfigurationValue,
1,
&ReturnValue
);
if (EFI_ERROR (Status)) {
return EFI_DEVICE_ERROR;
}
DevReq.Request = FTDI_COMMAND_RESET_PORT;
DevReq.RequestType = USB_REQ_TYPE_VENDOR;
DevReq.Value = RESET_PORT_PURGE_TX;
DevReq.Index = FTDI_PORT_IDENTIFIER;
DevReq.Length = 0; //indicates that there is no data phase in this request
Status = UsbSerialDevice->UsbIo->UsbControlTransfer (
UsbSerialDevice->UsbIo,
&DevReq,
EfiUsbDataIn,
WDR_TIMEOUT,
&ConfigurationValue,
1,
&ReturnValue
);
if (EFI_ERROR (Status)) {
return EFI_DEVICE_ERROR;
}
return Status;
}
/**
Entrypoint of USB Serial Driver.
This function is the entrypoint of USB Serial Driver. It installs
Driver Binding Protocols together with Component Name Protocols.
@param ImageHandle[in] The firmware allocated handle for the EFI image.
@param SystemTable[in] A pointer to the EFI System Table.
@retval EFI_SUCCESS The entry point is executed successfully.
**/
EFI_STATUS
EFIAPI
FtdiUsbSerialEntryPoint (
IN EFI_HANDLE ImageHandle,
IN EFI_SYSTEM_TABLE *SystemTable
)
{
EFI_STATUS Status;
Status = EfiLibInstallDriverBindingComponentName2 (
ImageHandle,
SystemTable,
&gUsbSerialDriverBinding,
ImageHandle,
&gUsbSerialComponentName,
&gUsbSerialComponentName2
);
ASSERT_EFI_ERROR (Status);
return EFI_SUCCESS;
}
/**
Unload function for the Usb Serial Driver.
@param ImageHandle[in] The allocated handle for the EFI image
@retval EFI_SUCCESS The driver was unloaded successfully
**/
EFI_STATUS
EFIAPI
FtdiUsbSerialUnload (
IN EFI_HANDLE ImageHandle
)
{
EFI_STATUS Status;
EFI_HANDLE *HandleBuffer;
UINTN HandleCount;
UINTN Index;
//
// Retrieve all handles in the handle database
//
Status = gBS->LocateHandleBuffer (
AllHandles,
NULL,
NULL,
&HandleCount,
&HandleBuffer
);
if (EFI_ERROR (Status)) {
return Status;
}
//
// Disconnect the driver from the handles in the handle database
//
for (Index = 0; Index < HandleCount; Index++) {
Status = gBS->DisconnectController (
HandleBuffer[Index],
gImageHandle,
NULL
);
}
//
// Free the handle array
//
FreePool (HandleBuffer);
//
// Uninstall protocols installed by the driver in its entrypoint
//
Status = gBS->UninstallMultipleProtocolInterfaces (
ImageHandle,
&gEfiDriverBindingProtocolGuid,
&gUsbSerialDriverBinding,
&gEfiComponentNameProtocolGuid,
&gUsbSerialComponentName,
&gEfiComponentName2ProtocolGuid,
&gUsbSerialComponentName2,
NULL
);
if (EFI_ERROR (Status)) {
return Status;
}
return EFI_SUCCESS;
}
/**
Check whether USB Serial driver supports this device.
@param This[in] The USB Serial driver binding protocol.
@param Controller[in] The controller handle to check.
@param RemainingDevicePath[in] The remaining device path.
@retval EFI_SUCCESS The driver supports this controller.
@retval other This device isn't supported.
**/
EFI_STATUS
EFIAPI
UsbSerialDriverBindingSupported (
IN EFI_DRIVER_BINDING_PROTOCOL *This,
IN EFI_HANDLE Controller,
IN EFI_DEVICE_PATH_PROTOCOL *RemainingDevicePath
)
{
EFI_STATUS Status;
EFI_USB_IO_PROTOCOL *UsbIo;
UART_DEVICE_PATH *UartNode;
UART_FLOW_CONTROL_DEVICE_PATH *FlowControlNode;
UINTN Index;
UINTN EntryCount;
EFI_OPEN_PROTOCOL_INFORMATION_ENTRY *OpenInfoBuffer;
BOOLEAN HasFlowControl;
EFI_DEVICE_PATH_PROTOCOL *DevicePath;
EFI_DEVICE_PATH_PROTOCOL *ParentDevicePath;
if (RemainingDevicePath != NULL) {
if (!IsDevicePathEnd (RemainingDevicePath)) {
Status = EFI_UNSUPPORTED;
UartNode = (UART_DEVICE_PATH *) NextDevicePathNode (RemainingDevicePath);
if (UartNode->Header.Type != MESSAGING_DEVICE_PATH ||
UartNode->Header.SubType != MSG_UART_DP ||
sizeof (UART_DEVICE_PATH) != DevicePathNodeLength ((EFI_DEVICE_PATH *) UartNode)) {
goto Error;
}
FlowControlNode = (UART_FLOW_CONTROL_DEVICE_PATH *) NextDevicePathNode (UartNode);
if ((ReadUnaligned32 (&FlowControlNode->FlowControlMap) & ~UART_FLOW_CONTROL_HARDWARE) != 0) {
goto Error;
}
}
}
//
// Check if USB I/O Protocol is attached on the controller handle.
//
Status = gBS->OpenProtocol (
Controller,
&gEfiUsbIoProtocolGuid,
(VOID **) &UsbIo,
This->DriverBindingHandle,
Controller,
EFI_OPEN_PROTOCOL_BY_DRIVER
);
if (Status == EFI_ALREADY_STARTED) {
if (RemainingDevicePath == NULL || IsDevicePathEnd (RemainingDevicePath)) {
return EFI_SUCCESS;
}
Status = gBS->OpenProtocolInformation (
Controller,
&gEfiUsbIoProtocolGuid,
&OpenInfoBuffer,
&EntryCount
);
if (EFI_ERROR (Status)) {
return Status;
}
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,
Controller,
EFI_OPEN_PROTOCOL_GET_PROTOCOL
);
if (!EFI_ERROR (Status)) {
HasFlowControl = ContainsFlowControl (RemainingDevicePath);
if (HasFlowControl ^ ContainsFlowControl (DevicePath)) {
Status = EFI_UNSUPPORTED;
}
}
break;
}
}
FreePool (OpenInfoBuffer);
return Status;
}
if (EFI_ERROR (Status)) {
return Status;
}
gBS->CloseProtocol (
Controller,
&gEfiUsbIoProtocolGuid,
This->DriverBindingHandle,
Controller
);
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;
}
//
// Use the USB I/O Protocol interface to check whether Controller is
// a USB Serial device that can be managed by this driver.
//
Status = EFI_SUCCESS;
if (!IsUsbSerial (UsbIo)) {
Status = EFI_UNSUPPORTED;
goto Error;
}
Error:
gBS->CloseProtocol (
Controller,
&gEfiDevicePathProtocolGuid,
This->DriverBindingHandle,
Controller
);
return Status;
}
/**
Starts the USB Serial device with this driver.
This function produces initializes the USB Serial device and
produces the Serial IO Protocol.
@param This[in] The USB Serial driver binding instance.
@param Controller[in] Handle of device to bind driver to.
@param RemainingDevicePath[in] Optional parameter use to pick a specific
child device to start.
@retval EFI_SUCCESS The controller is controlled by the usb USB
Serial driver.
@retval EFI_UNSUPPORTED No interrupt endpoint can be found.
@retval Other This controller cannot be started.
**/
EFI_STATUS
EFIAPI
UsbSerialDriverBindingStart (
IN EFI_DRIVER_BINDING_PROTOCOL *This,
IN EFI_HANDLE Controller,
IN EFI_DEVICE_PATH_PROTOCOL *RemainingDevicePath
)
{
EFI_STATUS Status;
EFI_USB_IO_PROTOCOL *UsbIo;
USB_SER_DEV *UsbSerialDevice;
UINT8 EndpointNumber;
EFI_USB_ENDPOINT_DESCRIPTOR EndpointDescriptor;
UINT8 Index;
BOOLEAN FoundIn;
BOOLEAN FoundOut;
EFI_DEVICE_PATH_PROTOCOL *ParentDevicePath;
EFI_OPEN_PROTOCOL_INFORMATION_ENTRY *OpenInfoBuffer;
UINTN EntryCount;
EFI_SERIAL_IO_PROTOCOL *SerialIo;
UART_DEVICE_PATH *Uart;
UART_FLOW_CONTROL_DEVICE_PATH *FlowControl;
UINT32 Control;
EFI_DEVICE_PATH_PROTOCOL *TempDevicePath;
UsbSerialDevice = AllocateZeroPool (sizeof (USB_SER_DEV));
ASSERT (UsbSerialDevice != 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) {
goto ErrorExit1;
}
//
// Open USB I/O Protocol
//
Status = gBS->OpenProtocol (
Controller,
&gEfiUsbIoProtocolGuid,
(VOID **) &UsbIo,
This->DriverBindingHandle,
Controller,
EFI_OPEN_PROTOCOL_BY_DRIVER
);
if (EFI_ERROR (Status) && Status != EFI_ALREADY_STARTED) {
goto ErrorExit1;
}
if (Status == EFI_ALREADY_STARTED) {
if (RemainingDevicePath == NULL || IsDevicePathEnd (RemainingDevicePath)) {
FreePool (UsbSerialDevice);
return EFI_SUCCESS;
}
//
// Check to see if a child handle exists
//
Status = gBS->OpenProtocolInformation (
Controller,
&gEfiSerialIoProtocolGuid,
&OpenInfoBuffer,
&EntryCount
);
if (EFI_ERROR (Status)) {
goto ErrorExit1;
}
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,
Controller,
EFI_OPEN_PROTOCOL_GET_PROTOCOL
);
if (EFI_ERROR (Status)) {
}
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 (ReadUnaligned32 (&FlowControl->FlowControlMap) == UART_FLOW_CONTROL_HARDWARE) {
Control |= EFI_SERIAL_HARDWARE_FLOW_CONTROL_ENABLE;
} else {
Control &= ~EFI_SERIAL_HARDWARE_FLOW_CONTROL_ENABLE;
}
//
// Clear bits that are not allowed to be passed 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;
}
if (RemainingDevicePath != NULL) {
if (IsDevicePathEnd (RemainingDevicePath)) {
return EFI_SUCCESS;
}
}
UsbSerialDevice->UsbIo = UsbIo;
//
// Get interface & endpoint descriptor
//
UsbIo->UsbGetInterfaceDescriptor (
UsbIo,
&UsbSerialDevice->InterfaceDescriptor
);
EndpointNumber = UsbSerialDevice->InterfaceDescriptor.NumEndpoints;
//
// Traverse endpoints to find the IN and OUT endpoints that will send and
// receive data.
//
FoundIn = FALSE;
FoundOut = FALSE;
for (Index = 0; Index < EndpointNumber; Index++) {
Status = UsbIo->UsbGetEndpointDescriptor (
UsbIo,
Index,
&EndpointDescriptor
);
if (EFI_ERROR (Status)) {
return Status;
}
if (EndpointDescriptor.EndpointAddress == FTDI_ENDPOINT_ADDRESS_OUT) {
//
// Set the Out endpoint device
//
CopyMem (
&UsbSerialDevice->OutEndpointDescriptor,
&EndpointDescriptor,
sizeof(EndpointDescriptor)
);
FoundOut = TRUE;
}
if (EndpointDescriptor.EndpointAddress == FTDI_ENDPOINT_ADDRESS_IN) {
//
// Set the In endpoint device
//
CopyMem (
&UsbSerialDevice->InEndpointDescriptor,
&EndpointDescriptor,
sizeof(EndpointDescriptor)
);
FoundIn = TRUE;
}
}
if (!FoundIn || !FoundOut) {
//
// No interrupt endpoint found, then return unsupported.
//
Status = EFI_UNSUPPORTED;
goto ErrorExit;
}
//
// set the initial values of UsbSerialDevice->LastSettings to the default
// values
//
UsbSerialDevice->LastSettings.BaudRate = 115200;
UsbSerialDevice->LastSettings.DataBits = 8;
UsbSerialDevice->LastSettings.Parity = NoParity;
UsbSerialDevice->LastSettings.ReceiveFifoDepth = FTDI_MAX_RECEIVE_FIFO_DEPTH;
UsbSerialDevice->LastSettings.StopBits = OneStopBit;
UsbSerialDevice->LastSettings.Timeout = FTDI_TIMEOUT;
//
// set the initial values of UsbSerialDevice->ControlValues
//
UsbSerialDevice->ControlValues.DtrState = FALSE;
UsbSerialDevice->ControlValues.RtsState = FALSE;
UsbSerialDevice->ControlValues.HardwareFlowControl = FALSE;
UsbSerialDevice->ControlValues.HardwareLoopBack = FALSE;
UsbSerialDevice->ControlValues.SoftwareLoopBack = FALSE;
//
// set the values of UsbSerialDevice->UartDevicePath
//
UsbSerialDevice->UartDevicePath.Header.Type = MESSAGING_DEVICE_PATH;
UsbSerialDevice->UartDevicePath.Header.SubType = MSG_UART_DP;
UsbSerialDevice->UartDevicePath.Header.Length[0] = (UINT8) (sizeof (UART_DEVICE_PATH));
UsbSerialDevice->UartDevicePath.Header.Length[1] = (UINT8) ((sizeof (UART_DEVICE_PATH)) >> 8);
//
// set the values of UsbSerialDevice->FlowControlDevicePath
UsbSerialDevice->FlowControlDevicePath.Header.Type = MESSAGING_DEVICE_PATH;
UsbSerialDevice->FlowControlDevicePath.Header.SubType = MSG_VENDOR_DP;
UsbSerialDevice->FlowControlDevicePath.Header.Length[0] = (UINT8) (sizeof (UART_FLOW_CONTROL_DEVICE_PATH));
UsbSerialDevice->FlowControlDevicePath.Header.Length[1] = (UINT8) ((sizeof (UART_FLOW_CONTROL_DEVICE_PATH)) >> 8);
UsbSerialDevice->FlowControlDevicePath.FlowControlMap = 0;
Status = SetAttributesInternal (
UsbSerialDevice,
UsbSerialDevice->LastSettings.BaudRate,
UsbSerialDevice->LastSettings.ReceiveFifoDepth,
UsbSerialDevice->LastSettings.Timeout,
UsbSerialDevice->LastSettings.Parity,
UsbSerialDevice->LastSettings.DataBits,
UsbSerialDevice->LastSettings.StopBits
);
ASSERT_EFI_ERROR (Status);
Status = SetControlBitsInternal (
UsbSerialDevice,
&(UsbSerialDevice->ControlValues)
);
ASSERT_EFI_ERROR (Status);
//
// Publish Serial GUID and protocol
//
UsbSerialDevice->Signature = USB_SER_DEV_SIGNATURE;
UsbSerialDevice->SerialIo.Reset = SerialReset;
UsbSerialDevice->SerialIo.SetControl = SetControlBits;
UsbSerialDevice->SerialIo.SetAttributes = SetAttributes;
UsbSerialDevice->SerialIo.GetControl = GetControlBits;
UsbSerialDevice->SerialIo.Read = ReadSerialIo;
UsbSerialDevice->SerialIo.Write = WriteSerialIo;
//
// Set the static Serial IO modes that will display when running
// "sermode" within the UEFI shell.
//
UsbSerialDevice->SerialIo.Mode->Timeout = 0;
UsbSerialDevice->SerialIo.Mode->BaudRate = 115200;
UsbSerialDevice->SerialIo.Mode->DataBits = 8;
UsbSerialDevice->SerialIo.Mode->Parity = 1;
UsbSerialDevice->SerialIo.Mode->StopBits = 1;
UsbSerialDevice->ParentDevicePath = ParentDevicePath;
UsbSerialDevice->ControllerHandle = NULL;
FlowControl = NULL;
//
// Allocate space for the receive buffer
//
UsbSerialDevice->DataBuffer = AllocateZeroPool (SW_FIFO_DEPTH);
//
// Initialize data buffer pointers.
// Head==Tail = true means buffer is empty.
//
UsbSerialDevice->DataBufferHead = 0;
UsbSerialDevice->DataBufferTail = 0;
UsbSerialDevice->ControllerNameTable = NULL;
AddUnicodeString2 (
"eng",
gUsbSerialComponentName.SupportedLanguages,
&UsbSerialDevice->ControllerNameTable,
L"FTDI USB Serial Adapter",
TRUE
);
AddUnicodeString2 (
"en",
gUsbSerialComponentName2.SupportedLanguages,
&UsbSerialDevice->ControllerNameTable,
L"FTDI USB Serial Adapter",
FALSE
);
Status = SetInitialStatus (UsbSerialDevice);
ASSERT_EFI_ERROR (Status);
//
// Create a polling loop to check for input
//
gBS->CreateEvent (
EVT_TIMER | EVT_NOTIFY_SIGNAL,
TPL_CALLBACK,
UsbSerialDriverCheckInput,
UsbSerialDevice,
&(UsbSerialDevice->PollingLoop)
);
//
// add code to set trigger time based on baud rate
// setting to 0.5s for now
//
gBS->SetTimer (
UsbSerialDevice->PollingLoop,
TimerPeriodic,
EFI_TIMER_PERIOD_MILLISECONDS (500)
);
//
// Check if the remaining device path is null. If it is not null change the settings
// of the device to match those on the device path
//
if (RemainingDevicePath != NULL) {
CopyMem (
&UsbSerialDevice->UartDevicePath,
RemainingDevicePath,
sizeof (UART_DEVICE_PATH)
);
FlowControl = (UART_FLOW_CONTROL_DEVICE_PATH *) NextDevicePathNode (RemainingDevicePath);
if (IsUartFlowControlNode (FlowControl)) {
UsbSerialDevice->FlowControlDevicePath.FlowControlMap = ReadUnaligned32 (&FlowControl->FlowControlMap);
} else {
FlowControl = NULL;
}
}
//
// Build the device path by appending the UART node to the parent device path
//
UsbSerialDevice->DevicePath = AppendDevicePathNode (
ParentDevicePath,
(EFI_DEVICE_PATH_PROTOCOL *) &UsbSerialDevice->UartDevicePath
);
//
// Continue building the device path by appending the flow control node
//
TempDevicePath = UsbSerialDevice->DevicePath;
UsbSerialDevice->DevicePath = AppendDevicePathNode (
TempDevicePath,
(EFI_DEVICE_PATH_PROTOCOL *) &UsbSerialDevice->FlowControlDevicePath
);
FreePool (TempDevicePath);
if (UsbSerialDevice->DevicePath == NULL) {
Status = EFI_OUT_OF_RESOURCES;
goto ErrorExit;
}
//
// Install protocol interfaces for the device
//
Status = gBS->InstallMultipleProtocolInterfaces (
&UsbSerialDevice->ControllerHandle,
&gEfiDevicePathProtocolGuid,
UsbSerialDevice->DevicePath,
&gEfiSerialIoProtocolGuid,
&UsbSerialDevice->SerialIo,
NULL
);
if (EFI_ERROR (Status)){
goto ErrorExit;
}
//
// Open for child device
//
Status = gBS->OpenProtocol (
Controller,
&gEfiUsbIoProtocolGuid,
(VOID **) &UsbIo,
This->DriverBindingHandle,
UsbSerialDevice->ControllerHandle,
EFI_OPEN_PROTOCOL_BY_CHILD_CONTROLLER
);
UsbSerialDevice->Shutdown = FALSE;
return EFI_SUCCESS;
ErrorExit:
//
// Error handler
//
Status = gBS->UninstallMultipleProtocolInterfaces (
Controller,
&gEfiSerialIoProtocolGuid,
&UsbSerialDevice->SerialIo,
NULL
);
if (EFI_ERROR (Status)) {
goto ErrorExit1;
}
FreePool (UsbSerialDevice->DataBuffer);
FreePool (UsbSerialDevice);
UsbSerialDevice = NULL;
gBS->CloseProtocol (
Controller,
&gEfiUsbIoProtocolGuid,
This->DriverBindingHandle,
Controller
);
ErrorExit1:
return Status;
}
/**
Stop the USB Serial device handled by this driver.
@param This[in] The USB Serial driver binding protocol.
@param Controller[in] The controller to release.
@param NumberOfChildren[in] The number of handles in ChildHandleBuffer.
@param ChildHandleBuffer[in] The array of child handle.
@retval EFI_SUCCESS The device was stopped.
@retval EFI_UNSUPPORTED Serial IO Protocol is not installed on
Controller.
@retval EFI_DEVICE_ERROR The device could not be stopped due to a
device error.
@retval Others Fail to uninstall protocols attached on the
device.
**/
EFI_STATUS
EFIAPI
UsbSerialDriverBindingStop (
IN EFI_DRIVER_BINDING_PROTOCOL *This,
IN EFI_HANDLE Controller,
IN UINTN NumberOfChildren,
IN EFI_HANDLE *ChildHandleBuffer
)
{
EFI_STATUS Status;
EFI_SERIAL_IO_PROTOCOL *SerialIo;
EFI_USB_IO_PROTOCOL *UsbIo;
USB_SER_DEV *UsbSerialDevice;
UINTN Index;
BOOLEAN AllChildrenStopped;
Status = EFI_SUCCESS;
UsbSerialDevice = NULL;
if (NumberOfChildren == 0) {
//
// Close the driver
//
Status = gBS->CloseProtocol (
Controller,
&gEfiUsbIoProtocolGuid,
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 (Status == EFI_SUCCESS) {//!EFI_ERROR (Status)) {
UsbSerialDevice = USB_SER_DEV_FROM_THIS (SerialIo);
Status = gBS->CloseProtocol (
Controller,
&gEfiUsbIoProtocolGuid,
This->DriverBindingHandle,
ChildHandleBuffer[Index]
);
Status = gBS->UninstallMultipleProtocolInterfaces (
ChildHandleBuffer[Index],
&gEfiDevicePathProtocolGuid,
UsbSerialDevice->DevicePath,
&gEfiSerialIoProtocolGuid,
&UsbSerialDevice->SerialIo,
NULL
);
if (EFI_ERROR (Status)) {
gBS->OpenProtocol (
Controller,
&gEfiUsbIoProtocolGuid,
(VOID **) &UsbIo,
This->DriverBindingHandle,
ChildHandleBuffer[Index],
EFI_OPEN_PROTOCOL_BY_CHILD_CONTROLLER
);
} else {
if (UsbSerialDevice->DevicePath != NULL) {
gBS->FreePool (UsbSerialDevice->DevicePath);
}
gBS->SetTimer (
UsbSerialDevice->PollingLoop,
TimerCancel,
0
);
gBS->CloseEvent (UsbSerialDevice->PollingLoop);
UsbSerialDevice->Shutdown = TRUE;
FreeUnicodeStringTable (UsbSerialDevice->ControllerNameTable);
FreePool (UsbSerialDevice->DataBuffer);
FreePool (UsbSerialDevice);
}
}
if (EFI_ERROR (Status)) {
AllChildrenStopped = FALSE;
}
}
if (!AllChildrenStopped) {
return EFI_DEVICE_ERROR;
}
return EFI_SUCCESS;
}
//
// Serial IO Member Functions
//
/**
Reset the serial device.
@param This[in] 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;
USB_SER_DEV *UsbSerialDevice;
UsbSerialDevice = USB_SER_DEV_FROM_THIS (This);
Status = ResetInternal (UsbSerialDevice);
if (EFI_ERROR (Status)){
return EFI_DEVICE_ERROR;
}
return Status;
}
/**
Set the control bits on a serial device.
@param This[in] Protocol instance pointer.
@param Control[in] 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
SetControlBits (
IN EFI_SERIAL_IO_PROTOCOL *This,
IN UINT32 Control
)
{
EFI_STATUS Status;
USB_SER_DEV *UsbSerialDevice;
CONTROL_BITS ControlBits;
UsbSerialDevice = USB_SER_DEV_FROM_THIS (This);
//
// check for invalid control parameters
//
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))) != 0 ) {
return EFI_UNSUPPORTED;
}
//
// check the control parameters and set the correct setting for
// the paramerts of ControlBits
// both loopback enables are always set to FALSE
//
ControlBits.HardwareLoopBack = FALSE;
ControlBits.SoftwareLoopBack = FALSE;
//
// check for hardware flow control
//
if ((Control & EFI_SERIAL_HARDWARE_FLOW_CONTROL_ENABLE) == EFI_SERIAL_HARDWARE_FLOW_CONTROL_ENABLE) {
ControlBits.HardwareFlowControl = TRUE;
} else {
ControlBits.HardwareFlowControl = FALSE;
}
//
// check for DTR enabled
//
if ((Control & EFI_SERIAL_DATA_TERMINAL_READY) == EFI_SERIAL_DATA_TERMINAL_READY) {
ControlBits.DtrState = TRUE;
} else {
ControlBits.DtrState = FALSE;
}
//
// check for RTS enabled
//
if ((Control & EFI_SERIAL_REQUEST_TO_SEND) == EFI_SERIAL_REQUEST_TO_SEND) {
ControlBits.RtsState = TRUE;
} else {
ControlBits.RtsState = FALSE;
}
//
// set the control values with a call to SetControlBitsInternal()
//
Status = SetControlBitsInternal (UsbSerialDevice, &ControlBits);
return Status;
}
/**
calls SetAttributesInternal() to set the baud rate, receive FIFO depth,
transmit/receive time out, parity, data buts, and stop bits on a serial
device.
@param This[in] Protocol instance pointer.
@param BaudRate[in] The requested baud rate. A BaudRate value of 0
will use the device's default interface speed.
@param ReveiveFifoDepth[in] 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[in] 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[in] 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[in] The number of data bits to use on the serial
device. A DataBit vaule of 0 will use the
device's default data bit setting.
@param StopBits[in] 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 attributes were set
@retval EFI_DEVICE_ERROR The attributes were not able to be
**/
EFI_STATUS
EFIAPI
SetAttributes (
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;
USB_SER_DEV *UsbSerialDevice;
UsbSerialDevice = USB_SER_DEV_FROM_THIS (This);
Status = SetAttributesInternal (
UsbSerialDevice,
BaudRate,
ReceiveFifoDepth,
Timeout,
Parity,
DataBits,
StopBits
);
if (EFI_ERROR (Status)) {
return Status;
}
return Status;
}
/**
Retrieves the status of the control bits on a serial device.
@param This[in] Protocol instance pointer.
@param Control[out] 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
GetControlBits (
IN EFI_SERIAL_IO_PROTOCOL *This,
OUT UINT32 *Control
)
{
USB_SER_DEV *UsbSerialDevice;
EFI_STATUS Status;
UsbSerialDevice = USB_SER_DEV_FROM_THIS (This);
*Control = 0;
Status = GetControlBitsInternal (UsbSerialDevice, Control);
if (EFI_ERROR (Status)) {
return EFI_DEVICE_ERROR;
}
return Status;
}
/**
Reads data from a serial device.
@param This[in] Protocol instance pointer.
@param BufferSize[in, out] On input, the size of the Buffer. On output,
the amount of data returned in Buffer.
@param Buffer[out] 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
ReadSerialIo (
IN EFI_SERIAL_IO_PROTOCOL *This,
IN OUT UINTN *BufferSize,
OUT VOID *Buffer
)
{
UINTN Index;
UINTN RemainingCallerBufferSize;
USB_SER_DEV *UsbSerialDevice;
EFI_STATUS Status;
if (*BufferSize == 0) {
return EFI_SUCCESS;
}
if (Buffer == NULL) {
return EFI_DEVICE_ERROR;
}
Status = EFI_SUCCESS;
UsbSerialDevice = USB_SER_DEV_FROM_THIS (This);
//
// Clear out any data that we already have in our internal buffer
//
for (Index = 0; Index < *BufferSize; Index++) {
if (UsbSerialDevice->DataBufferHead == UsbSerialDevice->DataBufferTail) {
break;
}
//
// Still have characters in the buffer to return
//
((UINT8 *)Buffer)[Index] = UsbSerialDevice->DataBuffer[UsbSerialDevice->DataBufferHead];
UsbSerialDevice->DataBufferHead = (UsbSerialDevice->DataBufferHead + 1) % SW_FIFO_DEPTH;
}
//
// If we haven't filled the caller's buffer using data that we already had on
// hand We need to generate an additional USB request to try and fill the
// caller's buffer
//
if (Index != *BufferSize) {
RemainingCallerBufferSize = *BufferSize - Index;
Status = ReadDataFromUsb (
UsbSerialDevice,
&RemainingCallerBufferSize,
(VOID *)(((CHAR8 *)Buffer) + Index)
);
if (!EFI_ERROR (Status)) {
*BufferSize = RemainingCallerBufferSize + Index;
} else {
*BufferSize = Index;
}
}
if (UsbSerialDevice->DataBufferHead == UsbSerialDevice->DataBufferTail) {
//
// Data buffer has no data, set the EFI_SERIAL_INPUT_BUFFER_EMPTY flag
//
UsbSerialDevice->ControlBits |= EFI_SERIAL_INPUT_BUFFER_EMPTY;
} else {
//
// There is some leftover data, clear EFI_SERIAL_INPUT_BUFFER_EMPTY flag
//
UsbSerialDevice->ControlBits &= ~(EFI_SERIAL_INPUT_BUFFER_EMPTY);
}
return Status;
}
/**
Writes data to a serial device.
@param This[in] Protocol instance pointer.
@param BufferSize[in, out] On input, the size of the Buffer. On output,
the amount of data actually written.
@param Buffer[in] 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
WriteSerialIo (
IN EFI_SERIAL_IO_PROTOCOL *This,
IN OUT UINTN *BufferSize,
IN VOID *Buffer
)
{
EFI_STATUS Status;
USB_SER_DEV *UsbSerialDevice;
EFI_TPL Tpl;
UsbSerialDevice = USB_SER_DEV_FROM_THIS (This);
if (UsbSerialDevice->Shutdown) {
return EFI_DEVICE_ERROR;
}
Tpl = gBS->RaiseTPL (TPL_NOTIFY);
Status = UsbSerialDataTransfer (
UsbSerialDevice,
EfiUsbDataOut,
Buffer,
BufferSize,
FTDI_TIMEOUT
);
gBS->RestoreTPL (Tpl);
if (EFI_ERROR (Status)) {
if (Status == EFI_TIMEOUT){
return Status;
} else {
return EFI_DEVICE_ERROR;
}
}
return EFI_SUCCESS;
}