blob: c03606b20bbd3d7b308a3adb95137a16ec7bb35c [file] [log] [blame]
/** @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
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.
**/
//
// 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;
}