/*++

Copyright (c) 2006, Intel Corporation                                                         
All rights reserved. This program and the accompanying materials                          
are licensed and made available under the terms and conditions of the BSD License         
which accompanies this distribution.  The full text of the license may be found at        
http://opensource.org/licenses/bsd-license.php                                            
                                                                                          
THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,                     
WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.             

Module Name:

  Keyboard.c
    
Abstract:

  Helper functions for USB Keyboard Driver

Revision History

--*/

#include "keyboard.h"

//
// USB Key Code to Efi key mapping table
// Format:<efi scan code>, <unicode without shift>, <unicode with shift>
//
STATIC
UINT8 KeyConvertionTable[USB_KEYCODE_MAX_MAKE][3] = {
  { SCAN_NULL,      'a',      'A' },      // 0x04
  { SCAN_NULL,      'b',      'B' },      // 0x05
  { SCAN_NULL,      'c',      'C' },      // 0x06
  { SCAN_NULL,      'd',      'D' },      // 0x07
  { SCAN_NULL,      'e',      'E' },      // 0x08
  { SCAN_NULL,      'f',      'F' },      // 0x09
  { SCAN_NULL,      'g',      'G' },      // 0x0A
  { SCAN_NULL,      'h',      'H' },      // 0x0B
  { SCAN_NULL,      'i',      'I' },      // 0x0C
  { SCAN_NULL,      'j',      'J' },      // 0x0D
  { SCAN_NULL,      'k',      'K' },      // 0x0E
  { SCAN_NULL,      'l',      'L' },      // 0x0F
  { SCAN_NULL,      'm',      'M' },      // 0x10
  { SCAN_NULL,      'n',      'N' },      // 0x11
  { SCAN_NULL,      'o',      'O' },      // 0x12
  { SCAN_NULL,      'p',      'P' },      // 0x13
  { SCAN_NULL,      'q',      'Q' },      // 0x14
  { SCAN_NULL,      'r',      'R' },      // 0x15
  { SCAN_NULL,      's',      'S' },      // 0x16
  { SCAN_NULL,      't',      'T' },      // 0x17
  { SCAN_NULL,      'u',      'U' },      // 0x18
  { SCAN_NULL,      'v',      'V' },      // 0x19
  { SCAN_NULL,      'w',      'W' },      // 0x1A
  { SCAN_NULL,      'x',      'X' },      // 0x1B
  { SCAN_NULL,      'y',      'Y' },      // 0x1C
  { SCAN_NULL,      'z',      'Z' },      // 0x1D
  { SCAN_NULL,      '1',      '!' },      // 0x1E
  { SCAN_NULL,      '2',      '@' },      // 0x1F
  { SCAN_NULL,      '3',      '#' },      // 0x20
  { SCAN_NULL,      '4',      '$' },      // 0x21
  { SCAN_NULL,      '5',      '%' },      // 0x22
  { SCAN_NULL,      '6',      '^' },      // 0x23
  { SCAN_NULL,      '7',      '&' },      // 0x24
  { SCAN_NULL,      '8',      '*' },      // 0x25
  { SCAN_NULL,      '9',      '(' },      // 0x26
  { SCAN_NULL,      '0',      ')' },      // 0x27
  { SCAN_NULL,      0x0d,     0x0d },     // 0x28   Enter
  { SCAN_ESC,       0x00,     0x00 },     // 0x29   Esc
  { SCAN_NULL,      0x08,     0x08 },     // 0x2A   Backspace
  { SCAN_NULL,      0x09,     0x09 },     // 0x2B   Tab
  { SCAN_NULL,      ' ',      ' ' },      // 0x2C   Spacebar
  { SCAN_NULL,      '-',      '_' },      // 0x2D
  { SCAN_NULL,      '=',      '+' },      // 0x2E
  { SCAN_NULL,      '[',      '{' },      // 0x2F
  { SCAN_NULL,      ']',      '}' },      // 0x30
  { SCAN_NULL,      '\\',     '|' },      // 0x31
  { SCAN_NULL,      '\\',     '|' },      // 0x32  Keyboard US \ and |
  { SCAN_NULL,      ';',      ':' },      // 0x33
  { SCAN_NULL,      '\'',     '"' },      // 0x34
  { SCAN_NULL,      '`',      '~' },      // 0x35  Keyboard Grave Accent and Tlide
  { SCAN_NULL,      ',',      '<' },      // 0x36
  { SCAN_NULL,      '.',      '>' },      // 0x37
  { SCAN_NULL,      '/',      '?' },      // 0x38
  { SCAN_NULL,      0x00,     0x00 },     // 0x39   CapsLock
  { SCAN_F1,        0x00,     0x00 },     // 0x3A
  { SCAN_F2,        0x00,     0x00 },     // 0x3B
  { SCAN_F3,        0x00,     0x00 },     // 0x3C  
  { SCAN_F4,        0x00,     0x00 },     // 0x3D  
  { SCAN_F5,        0x00,     0x00 },     // 0x3E
  { SCAN_F6,        0x00,     0x00 },     // 0x3F
  { SCAN_F7,        0x00,     0x00 },     // 0x40
  { SCAN_F8,        0x00,     0x00 },     // 0x41
  { SCAN_F9,        0x00,     0x00 },     // 0x42
  { SCAN_F10,       0x00,     0x00 },     // 0x43
  { SCAN_NULL,      0x00,     0x00 },     // 0x44   F11
  { SCAN_NULL,      0x00,     0x00 },     // 0x45   F12
  { SCAN_NULL,      0x00,     0x00 },     // 0x46   PrintScreen
  { SCAN_NULL,      0x00,     0x00 },     // 0x47   Scroll Lock
  { SCAN_NULL,      0x00,     0x00 },     // 0x48   Pause
  { SCAN_INSERT,    0x00,     0x00 },     // 0x49
  { SCAN_HOME,      0x00,     0x00 },     // 0x4A
  { SCAN_PAGE_UP,   0x00,     0x00 },     // 0x4B
  { SCAN_DELETE,    0x00,     0x00 },     // 0x4C
  { SCAN_END,       0x00,     0x00 },     // 0x4D
  { SCAN_PAGE_DOWN, 0x00,     0x00 },     // 0x4E
  { SCAN_RIGHT,     0x00,     0x00 },     // 0x4F
  { SCAN_LEFT,      0x00,     0x00 },     // 0x50
  { SCAN_DOWN,      0x00,     0x00 },     // 0x51
  { SCAN_UP,        0x00,     0x00 },     // 0x52
  { SCAN_NULL,      0x00,     0x00 },     // 0x53   NumLock
  { SCAN_NULL,      '/',      '/' },      // 0x54
  { SCAN_NULL,      '*',      '*' },      // 0x55
  { SCAN_NULL,      '-',      '-' },      // 0x56
  { SCAN_NULL,      '+',      '+' },      // 0x57
  { SCAN_NULL,      0x0d,     0x0d },     // 0x58
  { SCAN_END,       '1',      '1' },      // 0x59
  { SCAN_DOWN,      '2',      '2' },      // 0x5A
  { SCAN_PAGE_DOWN, '3',      '3' },      // 0x5B
  { SCAN_LEFT,      '4',      '4' },      // 0x5C
  { SCAN_NULL,      '5',      '5' },      // 0x5D
  { SCAN_RIGHT,     '6',      '6' },      // 0x5E
  { SCAN_HOME,      '7',      '7' },      // 0x5F
  { SCAN_UP,        '8',      '8' },      // 0x60
  { SCAN_PAGE_UP,   '9',      '9' },      // 0x61
  { SCAN_INSERT,    '0',      '0' },      // 0x62
  { SCAN_DELETE,    '.',      '.' },      // 0x63
  { SCAN_NULL,      '\\',     '|' },      // 0x64 Keyboard Non-US \ and |
  { SCAN_NULL,      0x00,     0x00 },     // 0x65 Keyboard Application
  { SCAN_NULL,      0x00,     0x00 },     // 0x66 Keyboard Power
  { SCAN_NULL,      '=' ,     '='  }      // 0x67 Keypad =
};

STATIC KB_MODIFIER  KB_Mod[8] = {
  { MOD_CONTROL_L,  0xe0 }, // 11100000 
  { MOD_CONTROL_R,  0xe4 }, // 11100100 
  { MOD_SHIFT_L,    0xe1 }, // 11100001 
  { MOD_SHIFT_R,    0xe5 }, // 11100101 
  { MOD_ALT_L,      0xe2 }, // 11100010 
  { MOD_ALT_R,      0xe6 }, // 11100110 
  { MOD_WIN_L,      0xe3 }, // 11100011 
  { MOD_WIN_R,      0xe7 }  // 11100111 
};


BOOLEAN
IsUSBKeyboard (
  IN  EFI_USB_IO_PROTOCOL       *UsbIo
  )
/*++
  
  Routine Description:
    Uses USB I/O to check whether the device is a USB Keyboard device.
  
  Arguments:
    UsbIo:    Points to a USB I/O protocol instance.
    
  Returns:
  
--*/  
{
  EFI_STATUS                    Status;
  EFI_USB_INTERFACE_DESCRIPTOR  InterfaceDescriptor;

  //
  // Get the Default interface descriptor, currently we
  // assume it is interface 1
  //
  Status = UsbIo->UsbGetInterfaceDescriptor (
                    UsbIo,
                    &InterfaceDescriptor
                    );

  if (EFI_ERROR (Status)) {
    return FALSE;
  }

  if (InterfaceDescriptor.InterfaceClass == CLASS_HID &&
      InterfaceDescriptor.InterfaceSubClass == SUBCLASS_BOOT &&
      InterfaceDescriptor.InterfaceProtocol == PROTOCOL_KEYBOARD
      ) {

    return TRUE;
  }

  return FALSE;
}


EFI_STATUS
InitUSBKeyboard (
  IN USB_KB_DEV   *UsbKeyboardDevice
  )
/*++
  
  Routine Description:
    Initialize USB Keyboard device and all private data structures.
    
  Arguments:
    UsbKeyboardDevice    The USB_KB_DEV instance.
    
  Returns:
    EFI_SUCCESS      - Success
    EFI_DEVICE_ERROR - Hardware Error
--*/
{
  UINT8               ConfigValue;
  UINT8               Protocol;
  UINT8               ReportId;
  UINT8               Duration;
  EFI_STATUS          Status;
  UINT32              TransferResult;

  KbdReportStatusCode (
    UsbKeyboardDevice->DevicePath,
    EFI_PROGRESS_CODE,
    (EFI_PERIPHERAL_KEYBOARD | EFI_P_KEYBOARD_PC_SELF_TEST)
    );

  InitUSBKeyBuffer (&(UsbKeyboardDevice->KeyboardBuffer));

  //
  // default configurations
  //
  ConfigValue = 0x01;

  //
  // Uses default configuration to configure the USB Keyboard device.
  //
  Status = UsbSetDeviceConfiguration (
            UsbKeyboardDevice->UsbIo,
            (UINT16) ConfigValue,
            &TransferResult
            );
  if (EFI_ERROR (Status)) {
    //
    // If configuration could not be set here, it means
    // the keyboard interface has some errors and could
    // not be initialized
    //
    KbdReportStatusCode (
      UsbKeyboardDevice->DevicePath,
      EFI_ERROR_CODE | EFI_ERROR_MINOR,
      (EFI_PERIPHERAL_KEYBOARD | EFI_P_EC_INTERFACE_ERROR)
      );

    return EFI_DEVICE_ERROR;
  }

  UsbGetProtocolRequest (
    UsbKeyboardDevice->UsbIo,
    UsbKeyboardDevice->InterfaceDescriptor.InterfaceNumber,
    &Protocol
    );
  //
  // Sets boot protocol for the USB Keyboard.
  // This driver only supports boot protocol.
  // !!BugBug: How about the device that does not support boot protocol?
  //
  if (Protocol != BOOT_PROTOCOL) {
    UsbSetProtocolRequest (
      UsbKeyboardDevice->UsbIo,
      UsbKeyboardDevice->InterfaceDescriptor.InterfaceNumber,
      BOOT_PROTOCOL
      );
  }
  //
  // the duration is indefinite, so the endpoint will inhibit reporting forever,
  // and only reporting when a change is detected in the report data.
  //

  //
  // idle value for all report ID
  //
  ReportId = 0;
  //
  // idle forever until there is a key pressed and released.
  //
  Duration = 0;
  UsbSetIdleRequest (
    UsbKeyboardDevice->UsbIo,
    UsbKeyboardDevice->InterfaceDescriptor.InterfaceNumber,
    ReportId,
    Duration
    );

  UsbKeyboardDevice->CtrlOn     = 0;
  UsbKeyboardDevice->AltOn      = 0;
  UsbKeyboardDevice->ShiftOn    = 0;
  UsbKeyboardDevice->NumLockOn  = 0;
  UsbKeyboardDevice->CapsOn     = 0;
  ZeroMem (UsbKeyboardDevice->LastKeyCodeArray, sizeof (UINT8) * 8);

  //
  // Set a timer for repeat keys' generation.
  //
  if (UsbKeyboardDevice->RepeatTimer) {
    gBS->CloseEvent (UsbKeyboardDevice->RepeatTimer);
    UsbKeyboardDevice->RepeatTimer = 0;
  }

  Status = gBS->CreateEvent (
                  EFI_EVENT_TIMER | EFI_EVENT_NOTIFY_SIGNAL,
                  EFI_TPL_NOTIFY,
                  USBKeyboardRepeatHandler,
                  UsbKeyboardDevice,
                  &UsbKeyboardDevice->RepeatTimer
                  );

  if (UsbKeyboardDevice->DelayedRecoveryEvent) {
    gBS->CloseEvent (UsbKeyboardDevice->DelayedRecoveryEvent);
    UsbKeyboardDevice->DelayedRecoveryEvent = 0;
  }

  Status = gBS->CreateEvent (
                  EFI_EVENT_TIMER | EFI_EVENT_NOTIFY_SIGNAL,
                  EFI_TPL_NOTIFY,
                  USBKeyboardRecoveryHandler,
                  UsbKeyboardDevice,
                  &UsbKeyboardDevice->DelayedRecoveryEvent
                  );

  return EFI_SUCCESS;
}

EFI_STATUS
EFIAPI
KeyboardHandler (
  IN  VOID          *Data,
  IN  UINTN         DataLength,
  IN  VOID          *Context,
  IN  UINT32        Result
  )
/*++
  
  Routine Description:
    Handler function for USB Keyboard's asynchronous interrupt transfer.
    
  Arguments:
    Data       A pointer to a buffer that is filled with key data which is
               retrieved via asynchronous interrupt transfer.
    DataLength Indicates the size of the data buffer.
    Context    Pointing to USB_KB_DEV instance.
    Result     Indicates the result of the asynchronous interrupt transfer.
    
  Returns:
    EFI_SUCCESS      - Success
    EFI_DEVICE_ERROR - Hardware Error
--*/  
{
  USB_KB_DEV          *UsbKeyboardDevice;
  EFI_USB_IO_PROTOCOL *UsbIo;
  UINT8               *CurKeyCodeBuffer;
  UINT8               *OldKeyCodeBuffer;
  UINT8               CurModifierMap;
  UINT8               OldModifierMap;
  UINT8               Index;
  UINT8               Index2;
  BOOLEAN             Down;
  BOOLEAN             KeyRelease;
  BOOLEAN             KeyPress;
  UINT8               SavedTail;
  USB_KEY             UsbKey;
  UINT8               NewRepeatKey;
  UINT32              UsbStatus;

  ASSERT (Context);

  NewRepeatKey      = 0;
  UsbKeyboardDevice = (USB_KB_DEV *) Context;
  UsbIo             = UsbKeyboardDevice->UsbIo;

  //
  // Analyzes the Result and performs corresponding action.
  //
  if (Result != EFI_USB_NOERROR) {
    //
    // Some errors happen during the process
    //
    KbdReportStatusCode (
      UsbKeyboardDevice->DevicePath,
      EFI_ERROR_CODE | EFI_ERROR_MINOR,
      (EFI_PERIPHERAL_KEYBOARD | EFI_P_EC_INPUT_ERROR)
      );

    //
    // stop the repeat key generation if any
    //
    UsbKeyboardDevice->RepeatKey = 0;

    gBS->SetTimer (
          UsbKeyboardDevice->RepeatTimer,
          TimerCancel,
          USBKBD_REPEAT_RATE
          );

    if ((Result & EFI_USB_ERR_STALL) == EFI_USB_ERR_STALL) {
      UsbClearEndpointHalt (
        UsbIo,
        UsbKeyboardDevice->IntEndpointDescriptor.EndpointAddress,
        &UsbStatus
        );
    }
    
    //
    // Delete & Submit this interrupt again
    //
    
    UsbIo->UsbAsyncInterruptTransfer (
             UsbIo,
             UsbKeyboardDevice->IntEndpointDescriptor.EndpointAddress,
             FALSE,
             0,
             0,
             NULL,
             NULL
             );

    gBS->SetTimer (
          UsbKeyboardDevice->DelayedRecoveryEvent,
          TimerRelative,
          EFI_USB_INTERRUPT_DELAY
          );

    return EFI_DEVICE_ERROR;
  }

  if (DataLength == 0 || Data == NULL) {
    return EFI_SUCCESS;
  }

  CurKeyCodeBuffer  = (UINT8 *) Data;
  OldKeyCodeBuffer  = UsbKeyboardDevice->LastKeyCodeArray;

  //
  // checks for new key stroke.
  // if no new key got, return immediately.
  //
  for (Index = 0; Index < 8; Index++) {
    if (OldKeyCodeBuffer[Index] != CurKeyCodeBuffer[Index]) {
      break;
    }
  }

  if (Index == 8) {
    return EFI_SUCCESS;
  }
  
  //
  // Parse the modifier key
  //
  CurModifierMap  = CurKeyCodeBuffer[0];
  OldModifierMap  = OldKeyCodeBuffer[0];

  //
  // handle modifier key's pressing or releasing situation.
  //
  for (Index = 0; Index < 8; Index++) {

    if ((CurModifierMap & KB_Mod[Index].Mask) != (OldModifierMap & KB_Mod[Index].Mask)) {
      //
      // if current modifier key is up, then
      // CurModifierMap & KB_Mod[Index].Mask = 0;
      // otherwize it is a non-zero value.
      // Inserts the pressed modifier key into key buffer.
      //
      Down = (UINT8) (CurModifierMap & KB_Mod[Index].Mask);
      InsertKeyCode (&(UsbKeyboardDevice->KeyboardBuffer), KB_Mod[Index].Key, Down);
    }
  }
  
  //
  // handle normal key's releasing situation
  //
  KeyRelease = FALSE;
  for (Index = 2; Index < 8; Index++) {

    if (!USBKBD_VALID_KEYCODE (OldKeyCodeBuffer[Index])) {
      continue;
    }

    KeyRelease = TRUE;
    for (Index2 = 2; Index2 < 8; Index2++) {

      if (!USBKBD_VALID_KEYCODE (CurKeyCodeBuffer[Index2])) {
        continue;
      }

      if (OldKeyCodeBuffer[Index] == CurKeyCodeBuffer[Index2]) {
        KeyRelease = FALSE;
        break;
      }
    }

    if (KeyRelease) {
      InsertKeyCode (
        &(UsbKeyboardDevice->KeyboardBuffer),
        OldKeyCodeBuffer[Index],
        0
        );
      //
      // the original reapeat key is released.
      //
      if (OldKeyCodeBuffer[Index] == UsbKeyboardDevice->RepeatKey) {
        UsbKeyboardDevice->RepeatKey = 0;
      }
    }
  }
    
  //
  // original repeat key is released, cancel the repeat timer
  //
  if (UsbKeyboardDevice->RepeatKey == 0) {
    gBS->SetTimer (
          UsbKeyboardDevice->RepeatTimer,
          TimerCancel,
          USBKBD_REPEAT_RATE
          );
  }
  
  //
  // handle normal key's pressing situation
  //
  KeyPress = FALSE;
  for (Index = 2; Index < 8; Index++) {

    if (!USBKBD_VALID_KEYCODE (CurKeyCodeBuffer[Index])) {
      continue;
    }

    KeyPress = TRUE;
    for (Index2 = 2; Index2 < 8; Index2++) {

      if (!USBKBD_VALID_KEYCODE (OldKeyCodeBuffer[Index2])) {
        continue;
      }

      if (CurKeyCodeBuffer[Index] == OldKeyCodeBuffer[Index2]) {
        KeyPress = FALSE;
        break;
      }
    }

    if (KeyPress) {
      InsertKeyCode (&(UsbKeyboardDevice->KeyboardBuffer), CurKeyCodeBuffer[Index], 1);
      //
      // NumLock pressed or CapsLock pressed
      //
      if (CurKeyCodeBuffer[Index] == 0x53 || CurKeyCodeBuffer[Index] == 0x39) {
        UsbKeyboardDevice->RepeatKey = 0;
      } else {
        NewRepeatKey = CurKeyCodeBuffer[Index];
        //
        // do not repeat the original repeated key
        //
        UsbKeyboardDevice->RepeatKey = 0;
      }
    }
  }
  
  //
  // Update LastKeycodeArray[] buffer in the
  // Usb Keyboard Device data structure.
  //
  for (Index = 0; Index < 8; Index++) {
    UsbKeyboardDevice->LastKeyCodeArray[Index] = CurKeyCodeBuffer[Index];
  }
  
  //
  // pre-process KeyboardBuffer, pop out the ctrl,alt,del key in sequence
  // and judge whether it will invoke reset event.
  //
  SavedTail = UsbKeyboardDevice->KeyboardBuffer.bTail;
  Index     = UsbKeyboardDevice->KeyboardBuffer.bHead;
  while (Index != SavedTail) {
    RemoveKeyCode (&(UsbKeyboardDevice->KeyboardBuffer), &UsbKey);

    switch (UsbKey.KeyCode) {

    case 0xe0:
    case 0xe4:
      if (UsbKey.Down) {
        UsbKeyboardDevice->CtrlOn = 1;
      } else {
        UsbKeyboardDevice->CtrlOn = 0;
      }
      break;

    case 0xe2:
    case 0xe6:
      if (UsbKey.Down) {
        UsbKeyboardDevice->AltOn = 1;
      } else {
        UsbKeyboardDevice->AltOn = 0;
      }
      break;

    //
    // Del Key Code
    //
    case 0x4c:
    case 0x63:
      if (UsbKey.Down) {
        if (UsbKeyboardDevice->CtrlOn && UsbKeyboardDevice->AltOn) {
          gRT->ResetSystem (EfiResetWarm, EFI_SUCCESS, 0, NULL);
        }
      }
      break;

    default:
      break;
    }
    
    //
    // insert the key back to the buffer.
    // so the key sequence will not be destroyed.
    //
    InsertKeyCode (
      &(UsbKeyboardDevice->KeyboardBuffer),
      UsbKey.KeyCode,
      UsbKey.Down
      );
    Index = UsbKeyboardDevice->KeyboardBuffer.bHead;

  }
  //
  // If have new key pressed, update the RepeatKey value, and set the
  // timer to repeate delay timer
  //
  if (NewRepeatKey != 0) {
    //
    // sets trigger time to "Repeat Delay Time",
    // to trigger the repeat timer when the key is hold long
    // enough time.
    //
    gBS->SetTimer (
          UsbKeyboardDevice->RepeatTimer,
          TimerRelative,
          USBKBD_REPEAT_DELAY
          );
    UsbKeyboardDevice->RepeatKey = NewRepeatKey;
  }

  return EFI_SUCCESS;
}

EFI_STATUS
USBParseKey (
  IN OUT  USB_KB_DEV  *UsbKeyboardDevice,
  OUT     UINT8       *KeyChar
  )
/*++
  
  Routine Description:
    Retrieves a key character after parsing the raw data in keyboard buffer.
    
  Arguments:
    UsbKeyboardDevice    The USB_KB_DEV instance.
    KeyChar              Points to the Key character after key parsing.
    
  Returns:
    EFI_SUCCESS   - Success
    EFI_NOT_READY - Device is not ready 
--*/  
{
  USB_KEY UsbKey;

  *KeyChar = 0;

  while (!IsUSBKeyboardBufferEmpty (&UsbKeyboardDevice->KeyboardBuffer)) {
    //
    // pops one raw data off.
    //
    RemoveKeyCode (&(UsbKeyboardDevice->KeyboardBuffer), &UsbKey);

    if (!UsbKey.Down) {
      switch (UsbKey.KeyCode) {

      case 0xe0:
      case 0xe4:
        UsbKeyboardDevice->CtrlOn = 0;
        break;

      case 0xe1:
      case 0xe5:
        UsbKeyboardDevice->ShiftOn = 0;
        break;

      case 0xe2:
      case 0xe6:
        UsbKeyboardDevice->AltOn = 0;
        break;

      default:
        break;
      }

      continue;
    }
    
    //
    // Analyzes key pressing situation
    //
    switch (UsbKey.KeyCode) {

    case 0xe0:
    case 0xe4:
      UsbKeyboardDevice->CtrlOn = 1;
      continue;
      break;

    case 0xe1:
    case 0xe5:
      UsbKeyboardDevice->ShiftOn = 1;
      continue;
      break;

    case 0xe2:
    case 0xe6:
      UsbKeyboardDevice->AltOn = 1;
      continue;
      break;

    case 0xe3:
    case 0xe7:
      continue;
      break;

    case 0x53:
      UsbKeyboardDevice->NumLockOn ^= 1;
      SetKeyLED (UsbKeyboardDevice);
      continue;
      break;

    case 0x39:
      UsbKeyboardDevice->CapsOn ^= 1;
      SetKeyLED (UsbKeyboardDevice);
      continue;
      break;

    //
    // F11,F12,PrintScreen,ScrollLock,Pause,Application,Power
    // keys are not valid EFI key
    //
    case 0x44:
    //
    // fall through
    //
    case 0x45:
    //
    // fall through
    //
    case 0x46:
    //
    // fall through
    //
    case 0x47:
  	  //
	    // Turn on the ScrollLock light on KB
	    //
      UsbKeyboardDevice->ScrollOn ^= 1;
      SetKeyLED (UsbKeyboardDevice);
      continue;
      break;    
    case 0x48:
    //
    // fall through
    //
    case 0x65:
    case 0x66:
      continue;
      break;

    default:
      break;
    }
    
    //
    // When encountered Del Key...
    //
    if (UsbKey.KeyCode == 0x4c || UsbKey.KeyCode == 0x63) {
      if (UsbKeyboardDevice->CtrlOn && UsbKeyboardDevice->AltOn) {
        gRT->ResetSystem (EfiResetWarm, EFI_SUCCESS, 0, NULL);
      }
    }

    *KeyChar = UsbKey.KeyCode;
    return EFI_SUCCESS;
  }

  return EFI_NOT_READY;

}


EFI_STATUS
USBKeyCodeToEFIScanCode (
  IN  USB_KB_DEV      *UsbKeyboardDevice,
  IN  UINT8           KeyChar,
  OUT EFI_INPUT_KEY   *Key
  )
/*++
  
  Routine Description:
    Converts USB Keyboard code to EFI Scan Code.
    
  Arguments:  
    UsbKeyboardDevice    The USB_KB_DEV instance.
    KeyChar              Indicates the key code that will be interpreted.    
    Key                  A pointer to a buffer that is filled in with 
                         the keystroke information for the key that 
                         was pressed.
  Returns:
    EFI_NOT_READY - Device is not ready
    EFI_SUCCESS   - Success
--*/
{
  UINT8 Index;

  if (!USBKBD_VALID_KEYCODE (KeyChar)) {
    return EFI_NOT_READY;
  }
  
  //
  // valid USB Key Code starts from 4
  //
  Index = (UINT8) (KeyChar - 4);

  if (Index >= USB_KEYCODE_MAX_MAKE) {
    return EFI_NOT_READY;
  }

  Key->ScanCode = KeyConvertionTable[Index][0];

  if (UsbKeyboardDevice->ShiftOn) {

    Key->UnicodeChar = KeyConvertionTable[Index][2];

  } else {

    Key->UnicodeChar = KeyConvertionTable[Index][1];
  }

  if (UsbKeyboardDevice->CapsOn) {

    if (Key->UnicodeChar >= 'a' && Key->UnicodeChar <= 'z') {

      Key->UnicodeChar = KeyConvertionTable[Index][2];

    } else if (Key->UnicodeChar >= 'A' && Key->UnicodeChar <= 'Z') {

      Key->UnicodeChar = KeyConvertionTable[Index][1];

    }
  }

  if (KeyChar >= 0x59 && KeyChar <= 0x63) {

    if (UsbKeyboardDevice->NumLockOn && !UsbKeyboardDevice->ShiftOn) {

      Key->ScanCode = SCAN_NULL;

    } else {

      Key->UnicodeChar = 0x00;
    }
  }

  if (Key->UnicodeChar == 0 && Key->ScanCode == SCAN_NULL) {
    return EFI_NOT_READY;
  }

  return EFI_SUCCESS;

}


EFI_STATUS
InitUSBKeyBuffer (
  IN OUT  USB_KB_BUFFER   *KeyboardBuffer
  )
/*++
  
  Routine Description:
    Resets USB Keyboard Buffer.
    
  Arguments:
    KeyboardBuffer - Points to the USB Keyboard Buffer.
    
  Returns:
    EFI_SUCCESS - Success
--*/  
{
  ZeroMem (KeyboardBuffer, sizeof (USB_KB_BUFFER));

  KeyboardBuffer->bHead = KeyboardBuffer->bTail;

  return EFI_SUCCESS;
}

BOOLEAN
IsUSBKeyboardBufferEmpty (
  IN  USB_KB_BUFFER   *KeyboardBuffer
  )
/*++
  
  Routine Description:
    Check whether USB Keyboard buffer is empty.
    
  Arguments:
    KeyboardBuffer - USB Keyboard Buffer.
    
  Returns:
  
--*/
{
  //
  // meet FIFO empty condition
  //
  return (BOOLEAN) (KeyboardBuffer->bHead == KeyboardBuffer->bTail);
}


BOOLEAN
IsUSBKeyboardBufferFull (
  IN  USB_KB_BUFFER   *KeyboardBuffer
  )
/*++
  
  Routine Description:
    Check whether USB Keyboard buffer is full.
    
  Arguments:
    KeyboardBuffer - USB Keyboard Buffer.
    
  Returns:
  
--*/
{
  return (BOOLEAN)(((KeyboardBuffer->bTail + 1) % (MAX_KEY_ALLOWED + 1)) == 
                                                        KeyboardBuffer->bHead);
}


EFI_STATUS
InsertKeyCode (
  IN OUT  USB_KB_BUFFER *KeyboardBuffer,
  IN      UINT8         Key,
  IN      UINT8         Down
  )
/*++
  
  Routine Description:
    Inserts a key code into keyboard buffer.
    
  Arguments:
    KeyboardBuffer - Points to the USB Keyboard Buffer.
    Key            - Key code
    Down           - Special key
  Returns:
    EFI_SUCCESS - Success
--*/
{
  USB_KEY UsbKey;

  //
  // if keyboard buffer is full, throw the
  // first key out of the keyboard buffer.
  //
  if (IsUSBKeyboardBufferFull (KeyboardBuffer)) {
    RemoveKeyCode (KeyboardBuffer, &UsbKey);
  }

  KeyboardBuffer->buffer[KeyboardBuffer->bTail].KeyCode = Key;
  KeyboardBuffer->buffer[KeyboardBuffer->bTail].Down    = Down;

  //
  // adjust the tail pointer of the FIFO keyboard buffer.
  //
  KeyboardBuffer->bTail = (UINT8) ((KeyboardBuffer->bTail + 1) % (MAX_KEY_ALLOWED + 1));

  return EFI_SUCCESS;
}

EFI_STATUS
RemoveKeyCode (
  IN OUT  USB_KB_BUFFER *KeyboardBuffer,
  OUT     USB_KEY       *UsbKey
  )
/*++
  
  Routine Description:
    Pops a key code off from keyboard buffer.
    
  Arguments:
    KeyboardBuffer -  Points to the USB Keyboard Buffer.
    UsbKey         -  Points to the buffer that contains a usb key code.
  
  Returns:
    EFI_SUCCESS      - Success
    EFI_DEVICE_ERROR - Hardware Error
--*/  
{
  if (IsUSBKeyboardBufferEmpty (KeyboardBuffer)) {
    return EFI_DEVICE_ERROR;
  }

  UsbKey->KeyCode = KeyboardBuffer->buffer[KeyboardBuffer->bHead].KeyCode;
  UsbKey->Down    = KeyboardBuffer->buffer[KeyboardBuffer->bHead].Down;

  //
  // adjust the head pointer of the FIFO keyboard buffer.
  //
  KeyboardBuffer->bHead = (UINT8) ((KeyboardBuffer->bHead + 1) % (MAX_KEY_ALLOWED + 1));

  return EFI_SUCCESS;
}

EFI_STATUS
SetKeyLED (
  IN  USB_KB_DEV    *UsbKeyboardDevice
  )
/*++
  
  Routine Description:
    Sets USB Keyboard LED state.
    
  Arguments:
    UsbKeyboardDevice - The USB_KB_DEV instance.
  
  Returns:
    EFI_SUCCESS - Success
--*/  
{
  LED_MAP Led;
  UINT8   ReportId;

  //
  // Set each field in Led map.
  //
  Led.NumLock   = (UINT8) UsbKeyboardDevice->NumLockOn;
  Led.CapsLock  = (UINT8) UsbKeyboardDevice->CapsOn;
  Led.ScrollLock = (UINT8) UsbKeyboardDevice->ScrollOn;  
  Led.Resrvd    = 0;

  ReportId      = 0;
  //
  // call Set Report Request to lighten the LED.
  //
  UsbSetReportRequest (
    UsbKeyboardDevice->UsbIo,
    UsbKeyboardDevice->InterfaceDescriptor.InterfaceNumber,
    ReportId,
    HID_OUTPUT_REPORT,
    1,
    (UINT8 *) &Led
    );

  return EFI_SUCCESS;
}

VOID
EFIAPI
USBKeyboardRepeatHandler (
  IN    EFI_EVENT    Event,
  IN    VOID         *Context
  )
/*++
  
  Routine Description:
    Timer handler for Repeat Key timer.
    
  Arguments:
    Event   - The Repeat Key event.
    Context - Points to the USB_KB_DEV instance.
    
  Returns:
  
--*/    
{
  USB_KB_DEV  *UsbKeyboardDevice;

  UsbKeyboardDevice = (USB_KB_DEV *) Context;

  //
  // Do nothing when there is no repeat key.
  //
  if (UsbKeyboardDevice->RepeatKey != 0) {
    //
    // Inserts one Repeat key into keyboard buffer,
    //
    InsertKeyCode (
      &(UsbKeyboardDevice->KeyboardBuffer),
      UsbKeyboardDevice->RepeatKey,
      1
      );

    //
    // set repeate rate for repeat key generation.
    //
    gBS->SetTimer (
          UsbKeyboardDevice->RepeatTimer,
          TimerRelative,
          USBKBD_REPEAT_RATE
          );

  }
}

VOID
EFIAPI
USBKeyboardRecoveryHandler (
  IN    EFI_EVENT    Event,
  IN    VOID         *Context
  )
/*++
  
  Routine Description:
    Timer handler for Delayed Recovery timer.
    
  Arguments:
    Event   -  The Delayed Recovery event.
    Context -  Points to the USB_KB_DEV instance.
    
  Returns:
  
--*/    
{

  USB_KB_DEV          *UsbKeyboardDevice;
  EFI_USB_IO_PROTOCOL *UsbIo;
  UINT8               PacketSize;

  UsbKeyboardDevice = (USB_KB_DEV *) Context;

  UsbIo             = UsbKeyboardDevice->UsbIo;

  PacketSize        = (UINT8) (UsbKeyboardDevice->IntEndpointDescriptor.MaxPacketSize);

  UsbIo->UsbAsyncInterruptTransfer (
          UsbIo,
          UsbKeyboardDevice->IntEndpointDescriptor.EndpointAddress,
          TRUE,
          UsbKeyboardDevice->IntEndpointDescriptor.Interval,
          PacketSize,
          KeyboardHandler,
          UsbKeyboardDevice
          );
}
