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