From: Ashley DeSimone Date: Tue, 29 Oct 2013 17:46:03 +0000 (+0000) Subject: OptionRomPkg: Added bus driver for FTDI USB to serial adapters X-Git-Tag: edk2-stable201903~12140 X-Git-Url: https://git.proxmox.com/?p=mirror_edk2.git;a=commitdiff_plain;h=3f1484f6f7bc3184a7e06fed414ae7eb4ae3ecfe;ds=sidebyside OptionRomPkg: Added bus driver for FTDI USB to serial adapters Contributed-under: TianoCore Contribution Agreement 1.0 Signed-off-by: Ashley DeSimone git-svn-id: https://svn.code.sf.net/p/edk2/code/trunk/edk2@14817 6f19259b-4bc3-4df7-8a09-765794883524 --- diff --git a/OptionRomPkg/Bus/Usb/FtdiUsbSerialDxe/CompatibleDevices.txt b/OptionRomPkg/Bus/Usb/FtdiUsbSerialDxe/CompatibleDevices.txt new file mode 100644 index 0000000000..1ec1cce0d1 --- /dev/null +++ b/OptionRomPkg/Bus/Usb/FtdiUsbSerialDxe/CompatibleDevices.txt @@ -0,0 +1,5 @@ +The following devices have been confirmed to work with the USB Serial Driver: + +Brand Model Name Product Name Vendor ID Device ID +Gearmo USA_FTDI-36 USB to RS-232 0x0403 0x6001 +Sabrent CB-FTDI 0x0403 0x6001 \ No newline at end of file diff --git a/OptionRomPkg/Bus/Usb/FtdiUsbSerialDxe/ComponentName.c b/OptionRomPkg/Bus/Usb/FtdiUsbSerialDxe/ComponentName.c new file mode 100644 index 0000000000..c200878d47 --- /dev/null +++ b/OptionRomPkg/Bus/Usb/FtdiUsbSerialDxe/ComponentName.c @@ -0,0 +1,224 @@ +/** @file + UEFI Component Name(2) protocol implementation for USB Serial driver. + +Copyright (c) 2004 - 2013, Intel Corporation. All rights reserved.
+This program and the accompanying materials +are licensed and made available under the terms and conditions of the BSD +License which accompanies this distribution. The full text of the license may +be found at http://opensource.org/licenses/bsd-license.php + +THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, +WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. + +**/ + +#include "FtdiUsbSerialDriver.h" + +// +// EFI Component Name Protocol +// +GLOBAL_REMOVE_IF_UNREFERENCED EFI_COMPONENT_NAME_PROTOCOL gUsbSerialComponentName = { + (EFI_COMPONENT_NAME_GET_DRIVER_NAME) UsbSerialComponentNameGetDriverName, + (EFI_COMPONENT_NAME_GET_CONTROLLER_NAME) UsbSerialComponentNameGetControllerName, + "eng" +}; + +// +// EFI Component Name 2 Protocol +// +GLOBAL_REMOVE_IF_UNREFERENCED EFI_COMPONENT_NAME2_PROTOCOL gUsbSerialComponentName2 = { + UsbSerialComponentNameGetDriverName, + UsbSerialComponentNameGetControllerName, + "en" +}; + +// +// Driver name string table +// +GLOBAL_REMOVE_IF_UNREFERENCED EFI_UNICODE_STRING_TABLE mUsbSerialDriverNameTable[] = { + { "eng;en", L"FTDI-232 USB Serial Driver" }, + { NULL , NULL } +}; + +/** + Retrieves a Unicode string that is the user readable name of the driver. + + This function retrieves the user readable name of a driver in the form of a + Unicode string. If the driver specified by This has a user readable name in + the language specified by Language, then a pointer to the driver name is + returned in DriverName, and EFI_SUCCESS is returned. If the driver specified + by This does not support the language specified by Language, + then EFI_UNSUPPORTED is returned. + + @param This A pointer to the EFI_COMPONENT_NAME2_PROTOCOL or + EFI_COMPONENT_NAME_PROTOCOL instance. + @param Language A pointer to a Null-terminated ASCII string + array indicating the language. This is the + language of the driver name that the caller is + requesting, and it must match one of the + languages specified in SupportedLanguages. The + number of languages supported by a driver is up + to the driver writer. Language is specified + in RFC 4646 or ISO 639-2 language code format. + @param DriverName A pointer to the Unicode string to return. + This Unicode string is the name of the + driver specified by This in the language + specified by Language. + + @retval EFI_SUCCESS The Unicode string for the Driver specified by + This and the language specified by Language was + returned in DriverName. + @retval EFI_INVALID_PARAMETER Language is NULL. + @retval EFI_INVALID_PARAMETER DriverName is NULL. + @retval EFI_UNSUPPORTED The driver specified by This does not support + the language specified by Language. + +**/ +EFI_STATUS +EFIAPI +UsbSerialComponentNameGetDriverName ( + IN EFI_COMPONENT_NAME2_PROTOCOL *This, + IN CHAR8 *Language, + OUT CHAR16 **DriverName + ) +{ + return LookupUnicodeString2 ( + Language, + This->SupportedLanguages, + mUsbSerialDriverNameTable, + DriverName, + (BOOLEAN)(This == &gUsbSerialComponentName2) + ); +} + + +/** + Retrieves a Unicode string that is the user readable name of the controller + that is being managed by a driver. + + This function retrieves the user readable name of the controller specified by + ControllerHandle and ChildHandle in the form of a Unicode string. If the + driver specified by This has a user readable name in the language specified by + Language, then a pointer to the controller name is returned in ControllerName, + and EFI_SUCCESS is returned. If the driver specified by This is not currently + managing the controller specified by ControllerHandle and ChildHandle, + then EFI_UNSUPPORTED is returned. If the driver specified by This does not + support the language specified by Language, then EFI_UNSUPPORTED is returned. + + @param This A pointer to the EFI_COMPONENT_NAME2_PROTOCOL or + EFI_COMPONENT_NAME_PROTOCOL instance. + @param ControllerHandle The handle of a controller that the driver + specified by This is managing. This handle + specifies the controller whose name is to be + returned. + @param ChildHandle The handle of the child controller to retrieve + the name of. This is an optional parameter that + may be NULL. It will be NULL for device + drivers. It will also be NULL for a bus drivers + that wish to retrieve the name of the bus + controller. It will not be NULL for a bus + driver that wishes to retrieve the name of a + child controller. + @param Language A pointer to a Null-terminated ASCII string + array indicating the language. This is the + language of the driver name that the caller is + requesting, and it must match one of the + languages specified in SupportedLanguages. The + number of languages supported by a driver is up + to the driver writer. Language is specified in + RFC 4646 or ISO 639-2 language code format. + @param ControllerName A pointer to the Unicode string to return. + This Unicode string is the name of the + controller specified by ControllerHandle and + ChildHandle in the language specified by + Language from the point of view of the driver + specified by This. + + @retval EFI_SUCCESS The Unicode string for the user readable name in + the language specified by Language for the + driver specified by This was returned in + DriverName. + @retval EFI_INVALID_PARAMETER ControllerHandle is not a valid EFI_HANDLE. + @retval EFI_INVALID_PARAMETER ChildHandle is not NULL and it is not a valid + EFI_HANDLE. + @retval EFI_INVALID_PARAMETER Language is NULL. + @retval EFI_INVALID_PARAMETER ControllerName is NULL. + @retval EFI_UNSUPPORTED The driver specified by This is not currently + managing the controller specified by + ControllerHandle and ChildHandle. + @retval EFI_UNSUPPORTED The driver specified by This does not support + the language specified by Language. + +**/ +EFI_STATUS +EFIAPI +UsbSerialComponentNameGetControllerName ( + IN EFI_COMPONENT_NAME2_PROTOCOL *This, + IN EFI_HANDLE ControllerHandle, + IN EFI_HANDLE ChildHandle OPTIONAL, + IN CHAR8 *Language, + OUT CHAR16 **ControllerName + ) +{ + EFI_STATUS Status; + USB_SER_DEV *UsbSerDev; + EFI_SERIAL_IO_PROTOCOL *SerialIo; + EFI_USB_IO_PROTOCOL *UsbIoProtocol; + + // + // This is a device driver, so ChildHandle must be NULL. + // + if (ChildHandle != NULL) { + return EFI_UNSUPPORTED; + } + + // + // Check Controller's handle + // + Status = gBS->OpenProtocol ( + ControllerHandle, + &gEfiUsbIoProtocolGuid, + (VOID **) &UsbIoProtocol, + gUsbSerialDriverBinding.DriverBindingHandle, + ControllerHandle, + EFI_OPEN_PROTOCOL_BY_DRIVER + ); + if (!EFI_ERROR (Status)) { + gBS->CloseProtocol ( + ControllerHandle, + &gEfiUsbIoProtocolGuid, + gUsbSerialDriverBinding.DriverBindingHandle, + ControllerHandle + ); + + return EFI_UNSUPPORTED; + } + + if (Status != EFI_ALREADY_STARTED) { + return EFI_UNSUPPORTED; + } + // + // Get the device context + // + Status = gBS->OpenProtocol ( + ControllerHandle, + &gEfiSerialIoProtocolGuid, + (VOID **) &SerialIo, + gUsbSerialDriverBinding.DriverBindingHandle, + ControllerHandle, + EFI_OPEN_PROTOCOL_GET_PROTOCOL + ); + if (EFI_ERROR (Status)) { + return Status; + } + + UsbSerDev = USB_SER_DEV_FROM_THIS (SerialIo); + + return LookupUnicodeString2 ( + Language, + This->SupportedLanguages, + UsbSerDev->ControllerNameTable, + ControllerName, + (BOOLEAN)(This == &gUsbSerialComponentName2) + ); +} diff --git a/OptionRomPkg/Bus/Usb/FtdiUsbSerialDxe/FtdiUsbSerialDriver.c b/OptionRomPkg/Bus/Usb/FtdiUsbSerialDxe/FtdiUsbSerialDriver.c new file mode 100644 index 0000000000..595ef4a3e2 --- /dev/null +++ b/OptionRomPkg/Bus/Usb/FtdiUsbSerialDxe/FtdiUsbSerialDriver.c @@ -0,0 +1,2588 @@ +/** @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 reseting 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 FlowControlMap; + 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; + FlowControlMap = 0; + + // + // 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; +} diff --git a/OptionRomPkg/Bus/Usb/FtdiUsbSerialDxe/FtdiUsbSerialDriver.h b/OptionRomPkg/Bus/Usb/FtdiUsbSerialDxe/FtdiUsbSerialDriver.h new file mode 100644 index 0000000000..07f4133ccf --- /dev/null +++ b/OptionRomPkg/Bus/Usb/FtdiUsbSerialDxe/FtdiUsbSerialDriver.h @@ -0,0 +1,595 @@ +/** @file + Header file for USB Serial Driver's Data Structures. + +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. + +**/ + +#ifndef _FTDI_USB_SERIAL_DRIVER_H_ +#define _FTDI_USB_SERIAL_DRIVER_H_ + +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +// +// US English LangID +// +#define USB_US_LANG_ID 0x0409 + +// +// Supported Vendor Ids +// +#define VID_FTDI 0x0403 + +// +// Supported product ids +// +#define DID_FTDI_FT232 0x6001 + +// +// FTDI Commands +// +#define FTDI_COMMAND_RESET_PORT 0 +#define FTDI_COMMAND_MODEM_CTRL 1 +#define FTDI_COMMAND_SET_FLOW_CTRL 2 +#define FTDI_COMMAND_SET_BAUDRATE 3 +#define FTDI_COMMAND_SET_DATA 4 +#define FTDI_COMMAND_GET_MODEM_STATUS 5 +#define FTDI_COMMAND_SET_EVENT_CHAR 6 +#define FTDI_COMMAND_SET_ERROR_CHAR 7 +#define FTDI_COMMAND_SET_LATENCY_TIMER 9 +#define FTDI_COMMAND_GET_LATENCY_TIMER 10 + +// +// FTDI_PORT_IDENTIFIER +// Used in the usb control transfers that issue FTDI commands as the index value. +// +#define FTDI_PORT_IDENTIFIER 0x1 // For FTDI USB serial adapter the port + // identifier is always 1. + +// +// RESET_PORT +// +#define RESET_PORT_RESET 0x0 // Purges RX and TX, clears DTR and RTS sets + // flow control to none, disables event + // trigger, sets the event char to 0x0d and + // does nothing to baudrate or data settings +#define RESET_PORT_PURGE_RX 0x1 +#define RESET_PORT_PURGE_TX 0x2 + +// +// SET_FLOW_CONTROL +// +#define NO_FLOW_CTRL 0x0 +#define XON_XOFF_CTRL 0x4 + +// +// SET_BAUD_RATE +// To set baud rate, one must calculate an encoding of the baud rate from +// UINT32 to UINT16.See EncodeBaudRateForFtdi() for details +// +#define FTDI_UART_FREQUENCY 3000000 +#define FTDI_MIN_DIVISOR 0x20 +#define FTDI_MAX_DIVISOR 0x3FFF8 +// +// Special case baudrate values +// 300,000 and 200,000 are special cases for calculating the encoded baudrate +// +#define FTDI_SPECIAL_CASE_300_MIN (3000000 * 100) / 103 // minimum adjusted + // value for 300,000 +#define FTDI_SPECIAL_CASE_300_MAX (3000000 * 100) / 97 // maximum adjusted + // value for 300,000 +#define FTDI_SPECIAL_CASE_200_MIN (2000000 * 100) / 103 // minimum adjusted + // value for 200,000 +#define FTDI_SPECIAL_CASE_200_MAX (2000000 * 100) / 97 // maximum adjusted + // value for 200,000 +// +// Min and max frequency values that the FTDI chip can attain +//.all generated frequencies must be between these values +// +#define FTDI_MIN_FREQUENCY 46601941 // (3MHz * 1600) / 103 = 46601941 +#define FTDI_MAX_FREQUENCY 49484536 // (3MHz * 1600) / 97 = 49484536 + +// +// SET_DATA_BITS +// +#define SET_DATA_BITS(n) (n) + +// +// SET_PARITY +// +#define SET_PARITY_NONE 0x0 +#define SET_PARITY_ODD BIT8 // (0x1 << 8) +#define SET_PARITY_EVEN BIT9 // (0x2 << 8) +#define SET_PARITY_MARK BIT9 | BIT8 // (0x3 << 8) +#define SET_PARITY_SPACE BIT10 // (0x4 << 8) + +// +// SET_STOP_BITS +// +#define SET_STOP_BITS_1 0x0 +#define SET_STOP_BITS_15 BIT11 // (0x1 << 11) +#define SET_STOP_BITS_2 BIT12 // (0x2 << 11) + +// +// SET_MODEM_CTRL +// SET_DTR_HIGH = (1 | (1 << 8)), SET_DTR_LOW = (0 | (1 << 8) +// SET_RTS_HIGH = (2 | (2 << 8)), SET_RTS_LOW = (0 | (2 << 8) +// +#define SET_DTR_HIGH (BIT8 | BIT0) +#define SET_DTR_LOW (BIT8) +#define SET_RTS_HIGH (BIT9 | BIT1) +#define SET_RTS_LOW (BIT9) + +// +// MODEM_STATUS +// +#define CTS_MASK BIT4 +#define DSR_MASK BIT5 +#define RI_MASK BIT6 +#define SD_MASK BIT7 +#define MSR_MASK (CTS_MASK | DSR_MASK | RI_MASK | SD_MASK) + +// +// Macro used to check for USB transfer errors +// +#define USB_IS_ERROR(Result, Error) (((Result) & (Error)) != 0) + +// +// USB request timeouts +// +#define WDR_TIMEOUT 5000 // default urb timeout in ms +#define WDR_SHORT_TIMEOUT 1000 // shorter urb timeout in ms + +// +// FTDI timeout +// +#define FTDI_TIMEOUT 16 + +// +// FTDI FIFO depth +// +#define FTDI_MAX_RECEIVE_FIFO_DEPTH 384 + +// +// FTDI Endpoint Descriptors +// +#define FTDI_ENDPOINT_ADDRESS_IN 0x81 //the endpoint address for the in enpoint generated by the device +#define FTDI_ENDPOINT_ADDRESS_OUT 0x02 //the endpoint address for the out endpoint generated by the device + +// +// Max buffer size for USB transfers +// +#define SW_FIFO_DEPTH 1024 + +// +// struct to define a usb device as a vendor and product id pair +// +typedef struct { + UINTN VendorId; + UINTN DeviceId; +} USB_DEVICE; + +// +//struct to describe the control bits of the device +//true indicates enabled +//false indicates disabled +// +typedef struct { + BOOLEAN HardwareFlowControl; + BOOLEAN DtrState; + BOOLEAN RtsState; + BOOLEAN HardwareLoopBack; + BOOLEAN SoftwareLoopBack; +} CONTROL_BITS; + +// +//struct to describe the status bits of the device +//true indicates enabled +//false indicated disabled +// +typedef struct { + BOOLEAN CtsState; + BOOLEAN DsrState; + BOOLEAN RiState; + BOOLEAN SdState; +} STATUS_BITS; + +// +// Structure to describe the last attributes of the Usb Serial device +// +typedef struct { + UINT64 BaudRate; + UINT32 ReceiveFifoDepth; + UINT32 Timeout; + EFI_PARITY_TYPE Parity; + UINT8 DataBits; + EFI_STOP_BITS_TYPE StopBits; +} PREVIOUS_ATTRIBUTES; + +// +// Structure to describe USB serial device +// +#define USB_SER_DEV_SIGNATURE SIGNATURE_32 ('u', 's', 'b', 's') + +typedef struct { + UINTN Signature; + EFI_HANDLE ControllerHandle; + EFI_DEVICE_PATH_PROTOCOL *DevicePath; + EFI_DEVICE_PATH_PROTOCOL *ParentDevicePath; + UART_DEVICE_PATH UartDevicePath; + UART_FLOW_CONTROL_DEVICE_PATH FlowControlDevicePath; + EFI_USB_IO_PROTOCOL *UsbIo; + EFI_USB_INTERFACE_DESCRIPTOR InterfaceDescriptor; + EFI_USB_ENDPOINT_DESCRIPTOR InEndpointDescriptor; + EFI_USB_ENDPOINT_DESCRIPTOR OutEndpointDescriptor; + EFI_UNICODE_STRING_TABLE *ControllerNameTable; + UINT32 DataBufferHead; + UINT32 DataBufferTail; + UINT8 *DataBuffer; + EFI_SERIAL_IO_PROTOCOL SerialIo; + BOOLEAN Shutdown; + EFI_EVENT PollingLoop; + UINT32 ControlBits; + PREVIOUS_ATTRIBUTES LastSettings; + CONTROL_BITS ControlValues; + STATUS_BITS StatusValues; + UINT8 ReadBuffer[512]; +} USB_SER_DEV; + +#define USB_SER_DEV_FROM_THIS(a) \ + CR(a, USB_SER_DEV, SerialIo, USB_SER_DEV_SIGNATURE) + +// +// Global Variables +// +extern EFI_DRIVER_BINDING_PROTOCOL gUsbSerialDriverBinding; +extern EFI_COMPONENT_NAME_PROTOCOL gUsbSerialComponentName; +extern EFI_COMPONENT_NAME2_PROTOCOL gUsbSerialComponentName2; + +// +// Functions of Driver Binding Protocol +// +/** + 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 + ); + +/** + Starts the Serial device with this driver. + + This function produces Serial IO Protocol and initializes the USB + Serial device to manage this USB Serial device. + + @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 + 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 + ); + +/** + 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 Simple Text In Protocol or Simple Text In Ex + 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 + ); + +// +// Serial IO Member Functions +// + +/** + 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 + ); + +/** + 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 + ); + +/** + 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 + ); + +/** + 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 + ); + +/** + Calls SetAttributesInternal() to set the baud rate, receive FIFO depth, + transmit/receice 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 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 + +**/ +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 + ); + +/** + Reset the serial device. + + @param This Protocol instance pointer. + + @retval EFI_SUCCESS The device was reset. + @retval EFI_DEVICE_ERROR The serial device could not be reset. + +**/ +EFI_STATUS +EFIAPI +SerialReset ( + IN EFI_SERIAL_IO_PROTOCOL *This + ); + +// +// EFI Component Name Functions +// + +/** + Retrieves a Unicode string that is the user readable name of the driver. + + This function retrieves the user readable name of a driver in the form of a + Unicode string. If the driver specified by This has a user readable name in + the language specified by Language, then a pointer to the driver name is + returned in DriverName, and EFI_SUCCESS is returned. If the driver specified + by This does not support the language specified by Language, + then EFI_UNSUPPORTED is returned. + + @param This[in] A pointer to the EFI_COMPONENT_NAME2_PROTOCOL + or EFI_COMPONENT_NAME_PROTOCOL instance. + @param Language[in] A pointer to a Null-terminated ASCII string + array indicating the language. This is the + language of the driver name that the caller + is requesting, and it must match one of the + languages specified in SupportedLanguages. + The number of languages supported by a + driver is up to the driver writer. Language + is specified in RFC 4646 or ISO 639-2 + language code format. + @param DriverName[out] A pointer to the Unicode string to return. + This Unicode string is the name of the + driver specified by This in the language + specified by Language. + + @retval EFI_SUCCESS The Unicode string for the Driver specified + by This and the language specified by + Language was returned in DriverName. + @retval EFI_INVALID_PARAMETER Language is NULL. + @retval EFI_INVALID_PARAMETER DriverName is NULL. + @retval EFI_UNSUPPORTED The driver specified by This does not + support the language specified by Language. + +**/ +EFI_STATUS +EFIAPI +UsbSerialComponentNameGetDriverName ( + IN EFI_COMPONENT_NAME2_PROTOCOL *This, + IN CHAR8 *Language, + OUT CHAR16 **DriverName + ); + +/** + Retrieves a Unicode string that is the user readable name of the controller + that is being managed by a driver. + + This function retrieves the user readable name of the controller specified by + ControllerHandle and ChildHandle in the form of a Unicode string. If the + driver specified by This has a user readable name in the language specified by + Language, then a pointer to the controller name is returned in ControllerName, + and EFI_SUCCESS is returned. If the driver specified by This is not currently + managing the controller specified by ControllerHandle and ChildHandle, + then EFI_UNSUPPORTED is returned. If the driver specified by This does not + support the language specified by Language, then EFI_UNSUPPORTED is returned. + + @param This[in] A pointer to the EFI_COMPONENT_NAME2_PROTOCOL + or EFI_COMPONENT_NAME_PROTOCOL instance. + @param ControllerHandle[in] The handle of a controller that the driver + specified by This is managing. This handle + specifies the controller whose name is to + be returned. + @param ChildHandle[in] The handle of the child controller to + retrieve the name of. This is an optional + parameter that may be NULL. It will be NULL + for device drivers. It will also be NULL + for a bus drivers that wish to retrieve the + name of the bus controller. It will not be + NULL for a bus driver that wishes to + retrieve the name of a child controller. + @param Language[in] A pointer to a Null-terminated ASCII string + array indicating the language. This is the + language of the driver name that the caller + is requesting, and it must match one of the + languages specified in SupportedLanguages. + The number of languages supported by a + driver is up to the driver writer. Language + is specified in RFC 4646 or ISO 639-2 + language code format. + @param ControllerName[out] A pointer to the Unicode string to return. + This Unicode string is the name of the + controller specified by ControllerHandle + and ChildHandle in the language specified + by Language from the point of view of the + driver specified by This. + + @retval EFI_SUCCESS The Unicode string for the user readable + name in the language specified by Language + for the driver specified by This was + returned in DriverName. + @retval EFI_INVALID_PARAMETER ControllerHandle is not a valid EFI_HANDLE. + @retval EFI_INVALID_PARAMETER ChildHandle is not NULL and it is not a + valid EFI_HANDLE. + @retval EFI_INVALID_PARAMETER Language is NULL. + @retval EFI_INVALID_PARAMETER ControllerName is NULL. + @retval EFI_UNSUPPORTED The driver specified by This is not + currently managing the controller specified + by ControllerHandle and ChildHandle. + @retval EFI_UNSUPPORTED The driver specified by This does not + support the language specified by Language. + +**/ +EFI_STATUS +EFIAPI +UsbSerialComponentNameGetControllerName ( + IN EFI_COMPONENT_NAME2_PROTOCOL *This, + IN EFI_HANDLE ControllerHandle, + IN EFI_HANDLE ChildHandle OPTIONAL, + IN CHAR8 *Language, + OUT CHAR16 **ControllerName + ); + +#endif diff --git a/OptionRomPkg/Bus/Usb/FtdiUsbSerialDxe/FtdiUsbSerialDxe.inf b/OptionRomPkg/Bus/Usb/FtdiUsbSerialDxe/FtdiUsbSerialDxe.inf new file mode 100644 index 0000000000..e0381b1c44 --- /dev/null +++ b/OptionRomPkg/Bus/Usb/FtdiUsbSerialDxe/FtdiUsbSerialDxe.inf @@ -0,0 +1,61 @@ +## @file +# USB Serial Driver that manages USB Serial device and produces Serial IO +# Protocol. +# +# USB Serial Driver consumes USB I/O Protocol and Device Path Protocol, and +# produces Serial IO Protocol on USB Serial devices. +# It manages the USB Serial device via USB Bulk Transfer of USB I/O Protocol. +# This module refers to following specifications: +# 1. UEFI Specification, v2.1 +# +# Copyright (c) 2006 - 2013, Intel Corporation. All rights reserved.
+# +# 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. +# +## + +[Defines] + INF_VERSION = 0x00010005 + BASE_NAME = FtdiUsbSerialDxe + FILE_GUID = A8154B55-2021-4D40-AE81-2E23A02dCC46 + MODULE_TYPE = UEFI_DRIVER + VERSION_STRING = 1.0 + ENTRY_POINT = FtdiUsbSerialEntryPoint + UNLOAD_IMAGE = FtdiUsbSerialUnload + +# +# VALID_ARCHITECTURES = IA32 X64 IPF EBC +# + +[Sources] + FtdiUsbSerialDriver.c + FtdiUsbSerialDriver.h + ComponentName.c + +[Packages] + MdePkg/MdePkg.dec + +[LibraryClasses] + UefiDriverEntryPoint + BaseMemoryLib + DebugLib + MemoryAllocationLib + UefiBootServicesTableLib + UefiLib + DevicePathLib + +[Guids] + gEfiUartDevicePathGuid + +[Protocols] + ## TO_START + ## BY_START + gEfiDevicePathProtocolGuid + gEfiUsbIoProtocolGuid ## TO_START + gEfiSerialIoProtocolGuid ## BY_START diff --git a/OptionRomPkg/Bus/Usb/FtdiUsbSerialDxe/ReadMe.txt b/OptionRomPkg/Bus/Usb/FtdiUsbSerialDxe/ReadMe.txt new file mode 100644 index 0000000000..d8ca227a41 --- /dev/null +++ b/OptionRomPkg/Bus/Usb/FtdiUsbSerialDxe/ReadMe.txt @@ -0,0 +1,32 @@ + +=== FTDI USB SERIAL OVERVIEW === + +This is a bus driver that enables the EfiSerialIoProtocol interface +for FTDI8U232AM based USB-to-Serial adapters. + +=== STATUS === + +Serial Input: Functional on real hardware. +Serial Output: Functional on real hardware. + +Operating Modes: Currently the user is able to change all operating modes +except timeout and FIFO depth. +The default operating mode is: + Baudrate: 115200 + Parity: None + Flow Control: None + Data Bits: 8 + Stop Bits: 1 +Notes: + Data Bits setting of 6,7,8 can not be combined with a Stop Bits setting of 1.5 + + At baudrates less than 9600 some of the characters may be transmitted incorrectly. + +=== COMPATIBILITY === + +Tested with: +An FTDI8U232AM based USB-To-Serial adapter, the UEFI Shell, and the SerialTest application +using a PuTTY Terminal + +See CompatibleDevices.txt for a list of devices which have been confirmed to work with this +driver. \ No newline at end of file diff --git a/OptionRomPkg/OptionRomPkg.dsc b/OptionRomPkg/OptionRomPkg.dsc index 4fdcf6d9b3..afd7046126 100644 --- a/OptionRomPkg/OptionRomPkg.dsc +++ b/OptionRomPkg/OptionRomPkg.dsc @@ -104,6 +104,7 @@ OptionRomPkg/AtapiPassThruDxe/AtapiPassThruDxe.inf OptionRomPkg/CirrusLogic5430Dxe/CirrusLogic5430Dxe.inf OptionRomPkg/UndiRuntimeDxe/UndiRuntimeDxe.inf + OptionRomPkg/Usb/FtdiUsbSerialDxe/FtdiUsbSerialDxe.inf [Components.IA32, Components.X64, Components.IPF] OptionRomPkg/Application/BltLibSample/BltLibSample.inf