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