/*
*   IBM Confidential
*
*   OCO Source Materials
*
*   (c) Copyright IBM Corp. 1994, 1998
*
*   The source code for this program is not published or otherwise divested of its
*   tradesecrets, irrespective of what has been deposited with the U.S. Copyright Office.
*/

/*****************************************************************************/
/*                                                                           */
/* SOURCE FILE NAME = KBDSTATE.C                                             */
/*                                                                           */
/* DESCRIPTIVE NAME = Physical Keyboard Device Driver Interrupt Handler      */
/*                                                                           */
/* VERSION      V2.2                                                         */
/*                                                                           */
/* DATE         01/11/94                                                     */
/*                                                                           */
/* DESCRIPTION  Finite State Machine (FSM) for Device Dependent Driver.      */
/*                                                                           */
/* FUNCTIONS   void IODelay(void)                                            */
/*             void KbdBeeper(void)                                          */
/*             void KeyERROR(void)                                           */
/*           USHORT KbdStatusWait(void)                                      */
/*             void SendPacket(BYTE,BYTE)                                    */
/*             void GetID(void)                                              */
/*         void FAR KbdTimerHandler(void)                                    */
/*             void StartTimer(void)                                         */
/*             void SendData(BYTE)                                           */
/*             void RESENDData(BYTE)                                         */
/*             void NoCmdIPg(BYTE)                                           */
/*             void RcvdE0SC(BYTE)                                           */
/*             void HotPlgPg(BYTE)                                           */
/*             void SentLEDC(BYTE)                                           */
/*             void SentLEDD(BYTE)                                           */
/*             void SentTYPC(BYTE)                                           */
/*             void SentTYPD(BYTE)                                           */
/*             void SentIDCM(BYTE)                                           */
/*             void WaitIDB1(BYTE)                                           */
/*             void WaitIDB2(BYTE)                                           */
/*             void Wait_ACK(BYTE)                                           */
/*             void STATE_MACH_CTL(void)                                     */
/*                                                                           */
/*                                                                           */
/*                                                                           */
/* NOTES       DEPENDENCIES:  Controller or keyboard must be set to the      */
/*                            PC compatible scan code set.                   */
/*             RESTRICTIONS:  Machine must be a 386 or compatible with       */
/*                            a 386.                                         */
/*___________________________________________________________________________*/
/*                                                                           */
/*                     STATE MACHINE FOR IBMKBD DRIVER                       */
/*                                                                           */
/*        start state                                                        */
/*            |                                                              */
/*            |<----------------------------------------------------+        */
/*            |                                                     |        */
/*            V                                                     |        */
/*        [NOCMDIPG]---->[SENTLEDC]---->[SENTLEDD]----------------->|        */
/*          | | | |                                                 |        */
/*          | | | +----->[HOTPLGPG]-------------------------------->|        */
/*          | | |            |                                      |        */
/*          | | |            V                                      |        */
/*          | | +------->[SENTIDCM]---->+---->[GTKBDCMD]---->+      |        */
/*          | |              |          |         |          |      |        */
/*          | |              V          |         V          |      |        */
/*          | |          [WAITIDB1]---->|     [SENTSCSC]     |      |        */
/*          | |              |          |         |          |      |        */
/*          | |              V          |         V          |      |        */
/*          | |          [WAITIDB2]---->+     [SENTSCSD]     |      |        */
/*          | |                                   |          |      |        */
/*          | |                                   V          |      |        */
/*          | |              +-------------------------------+      |        */
/*          | |              |                                      |        */
/*          | |              V                                      |        */
/*          | +--------->[SENTTYPC]---->[SENTTYPD]----------------->|        */
/*          V                                                       |        */
/*      [RCVDE0SC]------------------------------------------------->+        */
/*                                                                           */
/*                                                                           */
/*[NOCMDIPG] - No Command In Progress (*)                                    */
/*               This is the base state.  It accepts scan codes as           */
/*               keystrokes and it passes them in a key packet to the  DI    */
/*               driver.  It also checks for an override (FFh) scan code.    */
/*               This is usually the start of a hot plug sequence; therefore */
/*               move to the [HOTPLGPG] state.                               */
/*[RCVDE0SC] - Received E0 Scan Code (*)                                     */
/*               If [NOCMDIPG] receives an E0h scan code, it jumps to this   */
/*               state to await the remaining keystroke scan byte.  These    */
/*               two bytes are then passes to the DI driver in a key packet. */
/*               Upon completion, return to the state [NOCMDIPG].            */
/*[HOTPLGPG] - Notify Hot-Plug In Progress                                   */
/*               If [NOCMDIPG] receives FFh, it jumps to this state and      */
/*               waits for the next scan code. If the next scan code is an   */
/*               AAh, it starts the hot plug sequence.  If not then it beeps */
/*               the keyboard and returns to [NOCMDIPG] state to pass the    */
/*               scan code to the DI driver.                                 */
/*[SENTLEDC] - Sent LED Command (!)                                          */
/*               The change LEDs command has been sent to the keyboard and   */
/*               is now awaiting an ACK from the keyboard.  When one is      */
/*               received, send the new LED data byte to the keyboard and    */
/*               go to [SENDLEDD] to wait for an ACK.                        */
/*[SENTLEDD] - Sent LED Data (!)                                             */
/*               The LED byte is sent to the keyboard and are awaiting an    */
/*               ACK.  When one is received, go to [NOCMDIPG] and wait for   */
/*               the next scan code.                                         */
/*[SENTTYPC] - Sent Typamatic Command (!)                                    */
/*               The change type rate/delay command has been sent to the     */
/*               keyboard, and is now awaiting an ACK from the keyboard.     */
/*               When one is received, send the new type rate/delay data     */
/*               byte to the keyboard and go to [SENTTYPD] to wait for an    */
/*               ACK.                                                        */
/*[SENTTYPD] - Sent Typamatic Rate (!)                                       */
/*               The new type rate/delay byte has been sent to the keyboard  */
/*               and are awiating an ACK.  When one is received, go to       */
/*               [NOCMDIPG] and wait for the next scan code.                 */
/*[SENTIDCM] - Sent 'Read ID' Command                                        */
/*               The 'Read ID' command has been sent to the keyboard, and is */
/*               now awaiting an ACK from the keyboard.  When one is         */
/*               received, start a timer and go to [WAITIDB1] to await the   */
/*               first ID byte (go to [GTKBDCMD] upon timeout).              */
/*[WAITIDB1] - Wait For First ID Byte                                        */
/*               The 'Read ID' command has been sent and ACK has been        */
/*               received.  The device driver is waiting for the first ID    */
/*               byte.  When one is received, go to [WAITIDB2] to await the  */
/*               second ID byte.                                             */
/*[WAITIDB2] - Wait For Second ID Byte                                       */
/*               The first ID byte has been received, and the driver is now  */
/*               waiting for the second ID byte.  When the byte has been     */
/*               received, request the controller command byte and go to     */
/*               [GTKBDCMD].                                                 */
/*[GTKBDCMD] - Get Controller Command Byte(!)                                */
/*               The device driver waits for the command byte from the       */
/*               keyboard controller.  If the Scan-Code Set should be set    */
/*               to one then the the driver makes the request to change the  */
/*               Scan-Code Set and goes to [SENTSCSC].  If the Scan-Code Set */
/*               is two, then send off the change type rate/delay command    */
/*               and go to [SENTTYPC].                                       */
/*[SENTSCSC] - Sent Out Scan-Code Set Change(!)                              */
/*               When the device driver receives an ACK, it sends out the    */
/*               correct Scan-Code Set to the keyboard and then goes to      */
/*               [SENTSCSD] to wait for an ACK.                              */
/*[SENTSCSD] - Sent Out Correct Scan-Code Set(!)                             */
/*               The device driver is waiting for an ACK from the keyboard   */
/*               to acknowledge that it has completed the request.  To       */
/*               ensure that the type rate/delay is correct, send off the    */
/*               change type rate/delay command and go to [SENTTYPC].        */
/*                                                                           */
/*   NOTE: The state sequence from [SENTIDCM] to [SENTSCSD] should only      */
/*         occur during init and after every Hot Plug.  As it is part of     */
/*         the setup routine.                                                */
/*                                                                           */
/*(*) State can generate a keystroke packet                                  */
/*(!) State has a timer that reinits kbd if we timeout, and flags error.     */
/*___________________________________________________________________________*/
/*                                                                           */
/* STRUCTURES   KeyBuffer                                                    */
/*                                                                           */
/*                                                                           */
/* EXTERNAL FUNCTIONS                                                        */
/*              KbdDI_Entry                                                  */
/*              Run_Thread                                                   */
/*              Set_Typematic                                                */
/*              DevHelp_Beep                                                 */
/*              DevHelp_TickCount                                            */
/*              DevHelp_Yield                                                */
/*              DevHelp_EOI                                                  */
/*                                                                           */
/*   CHANGE ACTIVITY =                                                       */
/*     DATE      FLAG        CHANGE DESCRIPTION                              */
/*     --------  ----------  -----------------------------------------       */
/*     11/16/94  88898       Reduced the max number of possible yields in    */
/*                           kbdstatuswait from 64K to 16.  64K yields would */
/*                           make the system appear to be hung.              */
/*                                                                           */
/*     12/16/94  107569      Remove defect 88898.  It is causes system hangs */
/*                           while switching from desktop to full screens.   */
/*                           I even tried increasing the number of iterations*/
/*                           to 1k but it still hangs on faster machines.    */
/*                                                                           */
/*     10/07/96  melin_j     If def'd changes for merlin j                   */
/*     10/24/94  J-DBCSWARP  Base code change for WARP.              (H.Mori)*/
/*     07/08/97  CN181212           Added InitTime flag because DevHlp_Yield */
/*                                  can not be called at init time           */
/*     04/22/98  196425      Add fix from defect 196224 - Think pad lock up  */
/*     06/20/98  LR_USB      Modified to support USB Keyboard Client.        */
/*     10/20/98  IC22271     Added fix from problem - Shift locked           */
/*****************************************************************************/

/*-----------------------------*/
/*   Include files             */
/*-----------------------------*/

#include "os2.h"
#include "conio.h"
#include "devcmd.h"
#include "dhcalls.h"
#include "kbd.h"
#include "kbddd.h"
#include "cmdphdr.h"
//LR_USB begin
#include "strat2.h"
#include "reqpkt.h"
#include "kbdusb.h"
//LR_USB end

/*-----------------------------*/
/*     External Variables      */
/*-----------------------------*/

//LR_USB extern  BYTE      Keyboard;
extern  KeyStroke KeyBuffer;
extern  PFN       KbdDI_Entry;
extern  AttachDDStr KbdDI;

extern  USHORT  KbdCS;
extern  USHORT  KbdDS;
extern  USHORT  Keycount;
extern  USHORT  KbdDI_Handle;
extern  USHORT  Offset_IDC_Handler;
extern  USHORT  Offset_Timer_Handler;
extern  USHORT  Offset_BTTimer_Handler;        //SM148922

extern  BYTE    Current_State;
extern  BYTE    LED_State;
extern  BYTE    Type_Rate;
extern  BYTE    DataByte1;
extern  BYTE    DataByte2;
extern  BYTE    PendingReinitNotify;
extern  BYTE    SyncInterruptTime;
extern  BYTE    AsyncInterruptTime;
extern  BYTE    InitTime;
extern  BYTE    TIMERFLAG;
extern  BYTE    BTTIMERFLAG;                   //SM148922
extern  BYTE    KbdResend;
extern  BYTE    FirstByte;
extern  BYTE    KbdError;
extern  BYTE    PrevState;
extern  BYTE    Next_Block;
extern  BYTE    Next_Run;
extern  BYTE    SCSet;
extern  BYTE    DeferScans[DEFSIZ];   //up to DEFSIZ bytes deferred scan-codes
extern  BYTE    DeferPos;             //Position in deferral buffer
extern  BYTE    KbdIsLocked;            // AP-198827 1998-06-29
extern  BYTE	ShiftCondition;		// PMR 20502,624,760: Variable Shift Pressed/Released

void
DeferScan(BYTE scan)
{
  if (DeferPos >= DEFSIZ) {
    INT3;     /*As Scooby-Doo would say, "rut-roh"  We shouldn't get here*/
  }
  DeferScans[DeferPos++]=scan;
}

void
UnspoolScans(void)
{
  BYTE i;

  for (i=0 ; i < DeferPos ; i++) {
    STATE_MACH_CTL(DeferScans[i]);            //pump these scans through FSM
  }
  DeferPos=0;                                 //clear our spool count
}

/*****************************************************************************/
/*                                                                           */
/* FUNCTION NAME  : IODelay                                                  */
/*                 "IBM Unique Code"                                         */
/*                                                                           */
/* DESCRIPTION    : Delay for the out port command to allow setup time.      */
/*                                                                           */
/*****************************************************************************/

void IODelay (void)
{
  extern USHORT DOSIODELAYCNT;

  _asm {
     mov ax, OFFSET DOSIODELAYCNT
top: dec ax
     jnz top
  }
}

/*****************************************************************************/
/*                                                                           */
/* FUNCTION NAME  : KbdBeeper                                                */
/*                 "IBM Unique Code"                                         */
/*                                                                           */
/* DESCRIPTION    : Beeps the speaker when a FF scan code is received.       */
/*                                                                           */
/*****************************************************************************/

void KbdBeeper(void)
{
  DevHelp_Beep(2000, 1);           /* Beep if an 'FF' scan code is received. */
}

/*****************************************************************************/
/*                                                                           */
/* FUNCTION NAME  : KeyERROR                                                 */
/*                 "IBM Unique Code"                                         */
/*                                                                           */
/* DESCRIPTION    : This is the function that is called when an error occurs.*/
/*                                                                           */
/*****************************************************************************/

void KeyERROR (void)
{
  KbdError  = TURNON;                       /* Flag error.                   */
  KbdResend = 0;                            /* Reset count                   */
  PrevState = Current_State;                /* Save current state as previous*/
  Current_State = S_NOCMDIPG;               /* Set FSM to next state.        */
  inp(PORT_BUFFER);                         /* Clean out buffer.             */
  if (Next_Block != Next_Run) {             /* Check if blocked thread.      */
     Run_Thread();                          /* If yes then run it.           */
  }
}

/*****************************************************************************/
/*                                                                           */
/* FUNCTION NAME  : KbdStatusWait                                            */
/*                 "IBM Unique Code"                                         */
/*                                                                           */
/* DESCRIPTION    : Checks to see if there is info in the keyboard port or   */
/*                  if it is ready for data to be sent.                      */
/*                                                                           */
/* RETURN-NORMAL  : AX != 0 The keyboard port is ready.                      */
/*                  AX == 0 The keyboard port is not ready.                  */
/*                                                                           */
/*****************************************************************************/

USHORT KbdStatusWait(void)
{
  BYTE keyready;
  USHORT count;

  keyready = (BYTE)inp(PORT_CMD);           /* Get the controller status reg */
  count = MAX_Tick;                         /* Count = 64k                   */
  while((keyready & STATUS_CMDFULL)         /* If either bit is on AND       */
    && (keyready & STATUS_NOTINHIBITED)     /* if inhibited bit is on AND    */
    && (count != 0)) {                      /* if 64k loops yet              */
    if ((!INTERRUPTTIME) && (!InitTime)) {  /* If not interrupt time...      */
       DevHelp_Yield();                     /* Yield the Processor.          */
    }
    keyready = (BYTE)inp(PORT_CMD);         /* Get the controller status reg */
    count--;                                /* Count = 64k                   */
  }
  return(count);                            /* Return status.                */
}

/*****************************************************************************/

USHORT Call_DI (int func, char *buffer)
{
 extern USHORT KbdDI_Handle;                 /* Handle to KBDDI.             */
 USHORT rv;

 if (KbdDI_Handle == -1) {

#ifndef WATCOM
    _asm {
        mov KbdCS, cs                        /* Save the code segment value. */
        mov KbdDS, ds                        /* Save the data segment value. */
    }                                        /* Register with KBDDI.         */
#else
    KbdCS = CS();
    KbdDS = DS();
#endif

    KbdDI_Handle = KbdDI_Entry (CMD_Open, Offset_IDC_Handler, KbdDS, KbdCS);
 }
 rv = KbdDI_Entry (func, buffer, KbdDS, KbdDI_Handle);
 return(rv);
}

/*****************************************************************************/
/*                                                                           */
/* FUNCTION NAME  : SendPacket                                               */
/*                 "IBM Unique Code"                                         */
/*                                                                           */
/* DESCRIPTION    : Sends scan code packet to device independent driver.     */
/*                                                                           */
/*****************************************************************************/

void SendPacket (BYTE First, BYTE Second)
{
//LR_USB  Current_State = S_NOCMDIPG;    /* Reset FSM because packet is done.*/
  KeyBuffer.key1 = First;                /* Enter first scan code.           */
  KeyBuffer.key2 = Second;               /* Enter second scan code.          */

//LR_USB begin
#ifdef DEBUG
dsPrint2 (DBG_DETAILED, "IBMKBD: SendPacket (0x%x, 0x%x)\r\n", First, Second);
#endif
//LR_USB end

/*                                                                           */
/* Call KBDDI and send it the scan code.                                     */
/*                                                                           */
//LR_USB  Call_DI (CMD_ScanCode, &KeyBuffer);
  Call_DI (CMD_ScanCode, (UCHAR *)&KeyBuffer);
}

/*****************************************************************************/
/*                                                                           */
/* FUNCTION NAME  : GetID                                                    */
/*                 "IBM Unique Code"                                         */
/*                                                                           */
/* DESCRIPTION    : Stores the keyboard id information.                      */
/*                                                                           */
/*****************************************************************************/

void GetID (void)
{
  switch (PrevState) {                      /* Find out the last state.      */
    case S_SENTIDCM:                        /* AT Keyboard.                  */
         DataByte1 = 0;
         DataByte2 = AT;                    /* 1.                            */
         Keycount = 84;
         break;
    case S_WAITIDB1:                        /* Enhanced Keyboard.            */
         Keycount = 101;
         break;
    case S_WAITIDB2:                        /* Enhanced Plus.                */
         if (DataByte1 == KBDID_ENHANCED1){ /* Is it an enhanced keyboard.   */
            switch (DataByte2) {            /* Find out which enhanced.      */
              case KBD84:
                   Keycount = 84;           /* Is it an 84 Keyboard?         */
                   break;
              case KBD88:
                   Keycount = 88;           /* Is it an 88 Keyboard?         */
                   break;
              case KBD101:
              case KBD101x:
                   Keycount = 101;          /* Is it a 101 keyboard?         */
                   break;
              case KBD122:
              case KBD122x:
                   Keycount = 122;          /* Is it a 122 Keyboard?         */
                   break;
              default:
                   Keycount = UnKnown;      /* We don't know what it is.     */
                   break;
            }
         }
         else {
            Keycount = 101;                 /* Default to enhanced keyboard. */
         }
         break;
    default:
    Keycount = UnKnown;
    break;
  }
  if (KbdStatusWait()) {               /* Wait until KBD ready to pass data. */
    outp (PORT_CMD, CMD_READCOMMAND);
    IODelay();
    Current_State = S_GTKBDCMD;             /* Set FSM to next state.        */
    StartTimer();
    SCSet = (BYTE)inp (PORT_BUFFER);
  }
  else {
    KeyERROR();
  }
}

/*****************************************************************************/
/*                                                                           */
/* FUNCTION NAME  : KbdTimerHandler                                          */
/*                 "IBM Unique Code"                                         */
/*                                                                           */
/* DESCRIPTION    : Incase of a timeout while sending a command to the       */
/*                  keyboard this function is involked.  Handles ID cases,   */
/*                  finishes initialization, and catches errors.             */
/*                                                                           */
/*****************************************************************************/

void FAR KbdTimerHandler (void)
{
/*
   Reset tick count to maximum
   (SendToKbd routine will set as appropriate when the handler is needed again)
*/
  DevHelp_TickCount ((NPFN) Offset_Timer_Handler, MAX_Tick); /* Reset timer. */

  if (TIMERFLAG == TURNON) {                /* Check to see if this routine  */
                                            /*  is required.                 */
    TIMERFLAG = TURNOFF;                    /* Turn off timer flag.          */
    AsyncInterruptTime |= ASYNC_TIMER;
    if (Current_State == S_GTKBDCMD) {
      if (LED_State == 0x1F) {              /* This is a last resort.        */
        GTKbdCmd (SCSet);                  /* Avoid infinate loop if no int. */
      }                                  /* This is needed for UMC chipsets. */
      else {
        outp (PORT_CMD, CMD_WRITE_KYBD_OUTPUT);
        IODelay();                       /* Try to kick start the controller */
        outp (PORT_BUFFER, SCSet);          /* to create an interrupt.       */
        StartTimer();
        LED_State = 0x1F;                   /* Set to see if no int occures. */
      }
    }
    else if (Current_State >= S_SENTIDCM) {
      GetID();                              /* In Read ID process.           */
      // We get here because the keyboard fails to respond to send the ID bytes
      // in response to reset/hot plug command. Some keyboard (Compak Vocalist)
      // does not respond at all when there is a power on password
      // Set KbdIsLocked so that we can finish initialization in KBDINIT.C
      KbdIsLocked = TRUE;       // AP-198827 1998-06-29
    }
    else  {
      KeyERROR();                           /* Timed out and shouldn't have. */
    }
    AsyncInterruptTime &= ~ASYNC_TIMER;
  }
}

/*****************************************************************************/
/*                                                                           */
/* FUNCTION NAME  : KbdBTTimerHandler           //SM148922                   */
/*                 "IBM Unique Code"                                         */
/*                                                                           */
/* DESCRIPTION    : In case of a timeout while blocking a thread this        */
/*                  function is invoked.  Handles block thread in CheckKBD   */
/*                                                                           */
/*****************************************************************************/

void FAR KbdBTTimerHandler(void)
{
   DevHelp_TickCount((NPFN) Offset_BTTimer_Handler, MAX_Tick);/* Reset timer */

   if (BTTIMERFLAG == TURNON) {             /* Check to see if required...   */
        BTTIMERFLAG = TURNOFF;
        if (Next_Block != Next_Run) {       /* Check if blocked thread.      */
            Run_Thread();                   /* Run the blocking thread       */
        }
   }
}

/*****************************************************************************/
/*                                                                           */
/* FUNCTION NAME  : StartTimer                                               */
/*                 "IBM Unique Code"                                         */
/*                                                                           */
/* DESCRIPTION    : Sets the timer and flag incase of an error.              */
/*                                                                           */
/*****************************************************************************/

void StartTimer (void)
{
  TIMERFLAG = TURNON ;       /* Indicate the the timer handler is needed now */
  DevHelp_TickCount ((NPFN)Offset_Timer_Handler, Wait_Time);  /* Reset timer */
}

/*****************************************************************************/
/*                                                                           */
/* FUNCTION NAME  : StartBTTimer                //SM148922                   */
/*                 "IBM Unique Code"                                         */
/*                                                                           */
/* DESCRIPTION    : Sets the block thread timer and flag in case of a block  */
/*                  thread                                                   */
/*                                                                           */
/*****************************************************************************/

void StartBTTimer(void)
{
  BTTIMERFLAG = TURNON ;     /* Indicate the the timer handler is needed now */
  DevHelp_TickCount((NPFN)Offset_BTTimer_Handler, BTWait_Time); /*Reset timer*/
}

/*****************************************************************************/
/*                                                                           */
/* FUNCTION NAME  : SendData                                                 */
/*                 "IBM Unique Code"                                         */
/*                                                                           */
/* DESCRIPTION    : Sends a byte to the keyboard controller (8042).          */
/*                                                                           */
/*****************************************************************************/

void SendData (BYTE KbdData)
{
  CLI;
  IODelay();
  if (KbdStatusWait()) {                    /* Wait for controller ready.    */
    outp (PORT_BUFFER, KbdData);            /* Send to keyboard.             */
    StartTimer();                           /* Set up a timer.               */
    KbdResend = 0;                          /* Reset resend counter          */
  }
  else  {
    KeyERROR();                             /* We failed.                    */
  }
  STI;
}

/*****************************************************************************/
/*                                                                           */
/* FUNCTION NAME  : RESENDData                                               */
/*                 "IBM Unique Code"                                         */
/*                                                                           */
/* DESCRIPTION    : This function is called when a resend byte is returned.  */
/*                  This function decides what to do next.  Error on third   */
/*                  try.                                                     */
/*                                                                           */
/*****************************************************************************/

void RESENDData(BYTE DataResend)
{
  switch(KbdResend) {
  case 0:
    SendData(DataResend);                 /*  and resend data.          */
    KbdResend=1;                          /*  bump counter              */
  case 1:
    SendData(DataResend);                 /*  and resend data.          */
    KbdResend=2;                          /*  bump counter              */
    break;
  default:
    KeyERROR();                           /* It's the third resend.        */
    break;
  }
}

/*****************************************************************************/
/*                                                                           */
/* FUNCTION NAME  : NoCmdIPg                                                 */
/*                 "IBM Unique Code"                                         */
/*                                                                           */
/* DESCRIPTION    : Detect if this is a scan code, part of an extended scan  */
/*                  code, or a hotplug.  Set state machine and data bits     */
/*                  accordingly.                                             */
/*                                                                           */
/*****************************************************************************/

void NoCmdIPg (BYTE keystroke)
{
	
  switch (keystroke) {
    case SCAN_E0PREFIX:
         FirstByte = keystroke;                /* If it is place in holding. */
         Current_State = S_RCVDE0SC;           /* Set FSM to next state.     */
         break;
    case 0x2a:			//IC22271: If Shift Pressed scan 
	 ShiftCondition=1;	//IC22271: code received set ShiftCondition
         SendPacket (keystroke, NULL); //IC22271
	 break;			//IC22271: to non zero value
    case 0xaa:			//IC22271: If Shift Released scan code
	 ShiftCondition=0;	//IC22271: received set ShiftCondition
	 SendPacket (keystroke, NULL); //IC22271
	 break;			//IC22271: to zero value
    case KBD_RESET:
         FirstByte = keystroke;                /* Check for FF scan code.    */
         Current_State = S_HOTPLGPG;           /* Set FSM to next state.     */
         break;
    default:
         SendPacket (keystroke, NULL);          /* Send scan code to KBDDI.  */
         break;
  }
}

/*****************************************************************************/
/*                                                                           */
/* FUNCTION NAME  : RCVDE0SC                                                 */
/*                 "IBM Unique Code"                                         */
/*                                                                           */
/* DESCRIPTION    : We have received an extended scan code (E0) and are now  */
/*                  expecting the second half of that code so that we can    */
/*                  send it up to the device independent driver.             */
/*                                                                           */
/*****************************************************************************/

void RcvdE0SC (BYTE keystroke)
{
  Current_State = S_NOCMDIPG;        //LR_USB Reset FSM because packet is done.
  SendPacket (FirstByte, keystroke);             /* Send scan code to KBDDI. */
  FirstByte = 0;                                 /* Reset.                   */
}

/*****************************************************************************/
/*                                                                           */
/* FUNCTION NAME  : HOTPLGPG                                                 */
/*                 "IBM Unique Code"                                         */
/*                                                                           */
/* DESCRIPTION    : Are we being hotpluged? If so, then run the routine. If  */
/*                  not then send byte as a scan code.                       */
/*                                                                           */
/*****************************************************************************/

void HotPlgPg (BYTE keystroke)
{
  switch (keystroke) {
    case SCAN_ACK:
         break;
    case CMD_SELFTEST:              /* is this really a hotplug?    */
	if(!ShiftCondition)         //IC22271: Shift wasn't pressed? 
         {                          //IC22271: Yes it wasn't !
         DataByte1 = DataByte2 = 0; 
         PendingReinitNotify = 1;   
         LED_State = -1;            /* Make sure we reset the LEDs  */
//LR_USB Type_Rate = -1;            /* Make sure we reset Typematic */
         Current_State = S_SENTIDCM;
         SendData (KBD_REPORTID);   /* Request ID from keyboard.    */
         }                          //IC22271:
	else NoCmdIPg (keystroke);  //IC22271: If Shift was pressed before, then
	    			    //IC22271: it's probably Shift release code
				    //IC22271: after SCAN_OVERRUN. 
				    //IC22271: Send it to NoCmdIPG
	   break;

    case KBD_RESEND:
    case SCAN_ECHO:
         KeyERROR();
         break;
    default:
         KbdBeeper();                        /* Beep keyboard.               */
         NoCmdIPg (keystroke);               /* Fix think pad hang           */
         break;
  }
}

/*****************************************************************************/
/*                                                                           */
/* FUNCTION NAME  : SENTLEDC                                                 */
/*                 "IBM Unique Code"                                         */
/*                                                                           */
/* DESCRIPTION    : We have sent the LED change command to the keyboard and  */
/*                  are now expecting an ACK so that we can send it the new  */
/*                  LED state.                                               */
/*                                                                           */
/*****************************************************************************/

void SentLEDC(BYTE keystroke)
{
  switch(keystroke) {
  case SCAN_ACK:
    Current_State = S_SENTLEDD;             /* Set FSM to next state.        */
    SendData(LED_State);                    /* Send LED state to KBD.        */
    break;
  case KBD_RESEND:
    RESENDData(KBD_SETLEDS);                /* then we will.                 */
    break;
  case SCAN_OVERRUN:
  case SCAN_ECHO:
    KeyERROR();
    break;
  default:
    DeferScan(keystroke);              //defer this scan to later
    break;
  }
}

/*****************************************************************************/
/*                                                                           */
/* FUNCTION NAME  :  SENTLEDD                                                */
/*                 "IBM Unique Code"                                         */
/*                                                                           */
/* DESCRIPTION    : We have sent the new LED state to the keyboard and are   */
/*                  now expecting to receive and ACK.                        */
/*                                                                           */
/*****************************************************************************/

void SentLEDD(BYTE keystroke)
{
  switch(keystroke) {
  case SCAN_ACK:
    Current_State = S_NOCMDIPG;             /* Set FSM to begining state.    */
    if (Next_Block != Next_Run) {           /* Check if blocked thread.      */
       Run_Thread();                        /* If yes then run it.           */
    }
    break;
  case KBD_RESEND:
    RESENDData(LED_State);
    break;
  default:
    DeferScan(keystroke);                 //defer this scan to later
    break;
  }
}

/*****************************************************************************/
/*                                                                           */
/* FUNCTION NAME  : SENTTYPC                                                 */
/*                 "IBM Unique Code"                                         */
/*                                                                           */
/* DESCRIPTION    : We have sent the Type rate/delay command to the keyboard */
/*                  and are now expecting an ACK so that we can send it the  */
/*                  new type rate and delay.                                 */
/*                                                                           */
/*****************************************************************************/

void SentTYPC(BYTE keystroke)
{
  switch(keystroke) {
  case SCAN_ACK:
    Current_State = S_SENTTYPD;             /* Set FSM to next state.        */
    SendData(Type_Rate);                    /* Send type rate to KBD.        */
    break;
  case KBD_RESEND:
    RESENDData(KBD_SETREPEAT);
    break;
  default:
    DeferScan(keystroke);                 //defer this scan to later
    break;
  }
}

/*****************************************************************************/
/*                                                                           */
/* FUNCTION NAME  : SENTTYPD                                                 */
/*                 "IBM Unique Code"                                         */
/*                                                                           */
/* DESCRIPTION    : We have sent the new type rate/delay to the keyboard     */
/*                  and are now expecting to receive an ACK.                 */
/*                                                                           */
/*****************************************************************************/

void SentTYPD(BYTE keystroke)
{
  switch(keystroke) {
  case SCAN_ACK:
    Current_State = S_NOCMDIPG;             /* Set FSM to begining state.    */
    if (Next_Block != Next_Run) {           /* Check if blocked thread.      */
       Run_Thread();                        /* If yes then run it.           */
    }
    break;
  case KBD_RESEND:
    RESENDData(Type_Rate);
    break;
  default:
    DeferScan(keystroke);                 //defer this scan to later
    break;
  }
}

/*****************************************************************************/
/*                                                                           */
/* FUNCTION NAME  : GTKBDCMD                                                 */
/*                 "IBM Unique Code"                                         */
/*                                                                           */
/* DESCRIPTION    : We should receive the keyboard controller command byte.  */
/*                  This function should only be called during init or hot   */
/*                  plugging.                                                */
/*                                                                           */
/*****************************************************************************/

void GTKbdCmd(BYTE keystroke)
{
  BYTE temp;    //LR_USB

  SCSet = (BYTE)((keystroke & TRANSLATE_BIT)!=0 ? ScanCodeSet2 : ScanCodeSet1);
#ifdef DBCSKBD
  if (DataByte2 >= 0x90) {                  /* If 5576-00x keyboard          */  /* @IBM J-DBCSWARP(A) */
     if (SCSet == ScanCodeSet1) {           /*    without 8042 controller,   */  /* @IBM J-DBCSWARP(A) */
        SCSet = ScanCodeSet81;              /*        set ScanCodeSet 81.    */  /* @IBM J-DBCSWARP(A) */
     }                                      /*                               */  /* @IBM J-DBCSWARP(A) */
     else {                                 /*    with 8042 controller,      */  /* @IBM J-DBCSWARP(A) */
        SCSet = ScanCodeSet82;              /*        set ScanCodeSet 82.    */  /* @IBM J-DBCSWARP(A) */
     }                                      /*                               */  /* @IBM J-DBCSWARP(A) */
  }                                         /*                               */  /* @IBM J-DBCSWARP(A) */
#endif
  if (SCSet != ScanCodeSet2) {
    Current_State = S_SENTSCSC;             /* Set FSM to next state.        */
    SendData(KBD_NEW_SETSCAN);              /* Send Scan-Code Set to KBD.    */
  }
  else {
    Current_State = S_NOCMDIPG;
//LR_USB begin
    DisableFlag = TURNOFF;
    temp = Type_Rate;
    Type_Rate = -1;
    Set_Typematic (temp);                    /* Reset kbd type rate.          */
//LR_USB end
  }
}

/*****************************************************************************/
/*                                                                           */
/* FUNCTION NAME  : SENTSCSC                                                 */
/*                 "IBM Unique Code"                                         */
/*                                                                           */
/* DESCRIPTION    : We have sent the Scan-Code Set command to the keyboard   */
/*                  and are now expecting an ACK so that we can tell it to   */
/*                  use Scan-Code Set 1.                                     */
/*                  Assume that this only happens during Init or Hot Plug.   */
/*                                                                           */
/*****************************************************************************/

void SentSCSC(BYTE keystroke)
{
  switch(keystroke) {
  case SCAN_ACK:
    Current_State = S_SENTSCSD;             /* Set FSM to next state.        */
    SendData(SCSet);                        /* Send Scan-Code Set to KBD.    */
    break;
  case KBD_RESEND:
    RESENDData(KBD_NEW_SETSCAN);            /* then we will.                 */
    break;
  default:
    DeferScan(keystroke);                 //defer this scan to later
  }
}

/*****************************************************************************/
/*                                                                           */
/* FUNCTION NAME  : SENTSCSD                                                 */
/*                 "IBM Unique Code"                                         */
/*                                                                           */
/* DESCRIPTION    : We have sent the Scan-Code Set 1 to the keyboard         */
/*                  and are now expecting to receive an ACK.                 */
/*                                                                           */
/*****************************************************************************/

void SentSCSD(BYTE keystroke)
{
  switch(keystroke) {
  case SCAN_ACK:
    Current_State=S_NOCMDIPG;
    break;
  case KBD_RESEND:
    RESENDData(SCSet);                      /* then we will.                 */
    break;
  default:
    KeyERROR();
    break;
  }
}

/*****************************************************************************/
/*                                                                           */
/* FUNCTION NAME  : SENTIDCM                                                 */
/*                 "IBM Unique Code"                                         */
/*                                                                           */
/* DESCRIPTION    : We have sent a request for the keyboard id bytes to the  */
/*                  keyboard and are now expecting and ACK.  We will then    */
/*                  receive the id bytes in the next states.                 */
/*                                                                           */
/*****************************************************************************/

void SentIDCM (BYTE keystroke)
{
  switch (keystroke) {
    case SCAN_ACK:
         PrevState = Current_State;        /* Save current state as previous */
         Current_State = S_WAITIDB1;       /* Set FSM to next state.         */
         StartTimer();                     /* Restart timer for ID bytes.    */
         break;
    case KBD_RESEND:
         RESENDData (KBD_REPORTID);        /* then we will.                  */
         break;
    default:
         if (FakeKbd == 1) {
            DataByte1 = KBDID_ENHANCED1;   /* Store first ID byte.           */
            DataByte2 = KBD101;            /* Store second ID byte.          */
            Keycount = 101;                /* It is a 101 Keyboard           */
            PrevState = S_WAITIDB2;        /* Save current state as previous */
            Current_State = S_NOCMDIPG;    /* Set FSM to next state.         */
         }
         else {
            KeyERROR();
         }
         break;
  }
}

/*****************************************************************************/
/*                                                                           */
/* FUNCTION NAME  : WAITIDB1                                                 */
/*                 "IBM Unique Code"                                         */
/*                                                                           */
/* DESCRIPTION    : This should be the first id byte.                        */
/*                                                                           */
/*****************************************************************************/

void WaitIDB1 (BYTE IDreturn)
{
  DataByte1 = IDreturn;                     /* Store first ID byte.          */
  PrevState = Current_State;                /* Save current state as previous*/
  Current_State = S_WAITIDB2;               /* Set FSM to next state.        */
  StartTimer();                             /* Restart timer for 2nd ID byte.*/
}

/*****************************************************************************/
/*                                                                           */
/* FUNCTION NAME  : WAITIDB2                                                 */
/*                 "IBM Unique Code"                                         */
/*                                                                           */
/* DESCRIPTION    : This should be the second id byte.                       */
/*                                                                           */
/*****************************************************************************/

void WaitIDB2 (BYTE IDreturn)
{
  DataByte2 = IDreturn;                    /* Store second ID byte.          */
  PrevState = Current_State;               /* Save current state as previous */
  Current_State = S_NOCMDIPG;              /* Set FSM to next state.         */
  GetID();
}

/*****************************************************************************/
/*                                                                           */
/* FUNCTION NAME  : WAIT_ACK                                                 */
/*                 "IBM Unique Code"                                         */
/*                                                                           */
/* DESCRIPTION    : This state should be waiting for an ACK and then return. */
/*                                                                           */
/*****************************************************************************/

void Wait_ACK(BYTE keystroke)
{
  switch(keystroke) {
  case SCAN_ACK:
    Current_State=S_NOCMDIPG;           /* Set FSM to begining state.    */
    break;
  case KBD_RESEND:
    KeyERROR();
    break;
  default:
    DeferScan(keystroke);
    break;
  }
}

/*****************************************************************************/
/*                                                                           */
/* FUNCTION NAME  : STATE_MACH_CTL                                           */
/*                 "IBM Unique Code"                                         */
/*                                                                           */
/* DESCRIPTION    : Interrupt entry point.  This is also the state machine   */
/*                  controller.  It will branch to the current state for     */
/*                  processing.                                              */
/*                                                                           */
/*****************************************************************************/

void STATE_MACH_CTL (BYTE KeyInput)
{
  switch (Current_State) {                     /* Jump to the current state. */
    case S_NOCMDIPG: NoCmdIPg (KeyInput);      break;
    case S_RCVDE0SC: RcvdE0SC (KeyInput);      break;
    case S_HOTPLGPG: HotPlgPg (KeyInput);      break;
    case S_SENTLEDC: SentLEDC (KeyInput);      break;
    case S_SENTLEDD: SentLEDD (KeyInput);      break;
    case S_SENTTYPC: SentTYPC (KeyInput);      break;
    case S_SENTTYPD: SentTYPD (KeyInput);      break;
    case S_GTKBDCMD: GTKbdCmd (KeyInput);      break;
    case S_SENTSCSC: SentSCSC (KeyInput);      break;
    case S_SENTSCSD: SentSCSD (KeyInput);      break;
    case S_SENTIDCM: SentIDCM (KeyInput);      break;
    case S_WAITIDB1: WaitIDB1 (KeyInput);      break;
    case S_WAITIDB2: WaitIDB2 (KeyInput);      break;
    case S_WAIT_ACK: Wait_ACK (KeyInput);      break;
    default        : KbdError = TURNON;        break;
  }
}

/*****************************************************************************/

void FAR Kbd_Interrupt (void)
{
  BYTE KeyInput;

  AsyncInterruptTime |= ASYNC_KBDINT;
  PUSHF;
  STI;
  if (TIMERFLAG == TURNON) {                  /* Is timer flag still set?    */
    TIMERFLAG = TURNOFF;                      /* If yes then turn it off and */
    DevHelp_TickCount ((NPFN)Offset_Timer_Handler, MAX_Tick); /* Reset timer */
  }
  KbdStatusWait();                     /* Wait until KBD ready to pass data. */
  KeyInput = (BYTE)inp (PORT_BUFFER);   /* Get scan code.                    */

  STATE_MACH_CTL (KeyInput);

  if (Current_State == S_NOCMDIPG && PendingReinitNotify) {
    PendingReinitNotify = 0;
    Call_DI (CMD_Reinit, NULL);
  }
  else if (Current_State == S_NOCMDIPG && DeferPos) { // Unspool deferred scans
    UnspoolScans();
  }
  POPF;
  AsyncInterruptTime &= ~ASYNC_KBDINT;
  DevHelp_EOI ((USHORT)KBD_IRQ);                 /* Issue End Of Interrupt.  */
  CLC;                                           /* Interrupt always claimed */
}

