/** @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; | |
} |