/*DDK*************************************************************************/
/*                                                                           */
/* COPYRIGHT (C) Microsoft Corporation, 1989                                 */
/* COPYRIGHT    Copyright (C) 1995 IBM Corporation                           */
/*                                                                           */
/*    The following IBM OS/2 WARP source code is provided to you solely for  */
/*    the purpose of assisting you in your development of OS/2 WARP device   */
/*    drivers. You may use this code in accordance with the IBM License      */
/*    Agreement provided in the IBM Device Driver Source Kit for OS/2. This  */
/*    Copyright statement may not be removed.                                */
/*                                                                           */
/*****************************************************************************/

/*      Page  58,132
**      Title   KbdState - Kbd Device Driver Interrupt Processor
**      Name    KbdState
*/

/*****************************************************************************/
/*                                                                           */
/* 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.    */
/*                                                                           */
/*****************************************************************************/

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

 #include "os2.h"
 #include "conio.h"
 #include "devcmd.h"
 #include "dhcalls.h"
 #include "kbd.h"
 #include "kbddd.h"

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

 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  BYTE    Current_State;
 extern  BYTE    LED_State;
 extern  BYTE    Type_Rate;
 extern  BYTE    DataByte1;
 extern  BYTE    DataByte2;
 extern  BYTE    CMD_In_Progress;
 extern  BYTE    InitTime;
 extern  BYTE    InterruptTime;
 extern  BYTE    TIMERFLAG;
 extern  BYTE    KbdResend;
 extern  BYTE    FirstByte;
 extern  BYTE    KbdError;
 extern  BYTE    PrevState;
 extern  BYTE    HotPlugInit;
 extern  BYTE    Next_Block;
 extern  BYTE    Next_Run;
 extern  BYTE    Enabler;
 extern  BYTE    DisableFlag;
 extern  BYTE    SCSet;

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

void IODelay(void)
{
  _asm {
         push bx                            /* This routine is just a delay  */
         xchg bx, bx                        /* for the keyboard out command  */
         xchg bx, bx                        /* to have time to set up.       */
         xchg bx, bx
         pop bx
  }
}

/*****************************************************************************/
/*                                                                           */
/* 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 = TURNOFF;                      /* Reset flag.                   */
  PrevState = Current_State;                /* Save current state as previous*/
  Current_State = S_NOCMDIPG;               /* Set FSM to next state.        */
  CMD_In_Progress = TURNOFF;                /* Clear CMD in Progress flag.   */
  inp(PORT_BUFFER);                         /* Clean out buffer.             */
  Enable_Keyboard();
  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=1  : 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 int AND not init time  */
       DevHelp_Yield();                     /* Yield the Processor.          */
/* 107569 count = (count / 2) + 1;             88898 - max of 16 iterations  */
    }
    keyready = (BYTE)inp(PORT_CMD);         /* Get the controller status reg */
    count--;                                /* Count = 64k                   */
  }
  return(count != 0 ? 1 : 0);               /* Return status.                */
}

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

void SendPacket(BYTE First,BYTE Second)
{
  SHORT IsItOpen;

  Current_State = S_NOCMDIPG;            /* Reset FSM because packet is done.*/
  KeyBuffer.key1 = First;                /* Enter first scan code.           */
  KeyBuffer.key2 = Second;               /* Enter second scan code.          */
/*                                                                           */
/* Call KBDDI and send it the scan code.                                     */
/*                                                                           */
  IsItOpen = KbdDI_Entry(CMD_ScanCode, &KeyBuffer, KbdDS, KbdDI_Handle);

  if (IsItOpen == -1) {                  /* If KBDDI hasn't been opened yet. */
                                         /* Open it, resend the scan code,   */
                                         /*  and then close it.              */
    _asm{
          mov KbdCS, cs                  /* Save the code segment value.     */
          mov KbdDS, ds                  /* Save the data segment value.     */
    }                                    /* Register with KBDDI.             */
    KbdDI_Handle = KbdDI_Entry(CMD_Open, Offset_IDC_Handler, KbdDS,KbdCS);
    KbdDI_Entry(CMD_ScanCode, &KeyBuffer, KbdDS, KbdDI_Handle);
    KbdDI_Entry(CMD_Close, NULL, KbdDS, KbdDI_Handle);
  }
/*                                                                           */
/* If KBDDI is full so there are no more slots open, then all three of these */
/*  commands will fail with a -1 and the scan code sequence will be lost.    */
/*                                                                           */
}

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

  Enabler = TYPE_Enable;               /* Type Rate will enable keyboard.    */
  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.          */
    InterruptTime = TURNON;
    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 ((HotPlugInit == TURNON) && (Current_State >= S_SENTIDCM)) {
      GetID();                              /* In Read ID process.           */
    }
    else  {
      KeyERROR();                           /* Timed out and shouldn't have. */
      if (HotPlugInit == TURNON) {
        SENTTYPD(SCAN_ACK);        /* Keyboard is not attached or responding.*/
      }
    }
    InterruptTime = TURNOFF;
  }
}

/*****************************************************************************/
/*                                                                           */
/* 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  : 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.               */
  }
  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 TURNOFF  :
    case TURNON   :
      KbdResend++;                          /* If resend then set flag.       */
      SendData(DataResend);                 /*  and resend data.             */
      break;
    case TRYAGAIN :
      KeyERROR();                           /* It's the third resend.        */
      break;
    default       :
      KbdResend = TURNOFF;                  /* Reset flag and move on.       */
      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 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)
{
  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)
{
  if (keystroke == CMD_SELFTEST) {               /* Is this really a Hot Plug?    */
    DataByte1 = DataByte2 = 0;              /* Reset Data byte to 0.         */
    LED_State = -1;
    HotPlugInit = TURNON;                   /* Flag that it is a Hot Plug.   */
    CMD_In_Progress = TURNON;               /* Start a CMD to keyboard.      */
    Current_State = S_SENTIDCM;             /* Change FSM (Finite State Mach)*/
    SendData(KBD_REPORTID);                 /* Request ID from keyboard.     */
  }
  else {
    KbdBeeper();                            /* Beep keyboard.                */
    NOCMDIPG(keystroke);                    /* Send scan code to be processed*/
  }
}

/*****************************************************************************/
/*                                                                           */
/* 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 kbdreturn)
{
  if (kbdreturn == SCAN_ACK) {              /* If KBD acknowledges.          */
    Current_State = S_SENTLEDD;             /* Set FSM to next state.        */
    KbdResend = TURNOFF;                    /* Turn off resend flag.         */
    SendData(LED_State);                    /* Send LED state to KBD.        */
  }
  else if (kbdreturn == KBD_RESEND) {       /* Are we asked to resend?       */
    RESENDData(KBD_SETLEDS);                /* then we will.                 */
  }
  else  {
    KeyERROR();                             /* It's the second time to fail. */
  }
}

/*****************************************************************************/
/*                                                                           */
/* FUNCTION NAME  :                                                          */
/*                 "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 kbdreturn)
{
  if (kbdreturn == SCAN_ACK) {              /* If KBD acknowledges.          */
    Current_State = S_NOCMDIPG;             /* Set FSM to begining state.    */
    KbdResend = TURNOFF;                    /* Turn off resend flag.         */
    CMD_In_Progress = TURNOFF;              /* Clear CMD in Progress flag.   */
    if (Enabler == LEDS_Enable) {           /* If LEDs disabled kbd.         */
       Enabler = INT_Enable;                /* Then set to be enabled.       */
    }
    else {
      DisableFlag = TURNOFF;
      Disable_Keyboard();                   /* Make sure kbd is disabled.    */
    }
    if (Next_Block != Next_Run) {           /* Check if blocked thread.      */
       Run_Thread();                        /* If yes then run it.           */
    }
  }
  else if (kbdreturn == KBD_RESEND)         /* Are we asked to resend?       */
      RESENDData(LED_State);                /* then we will.                 */
  else  {
    KeyERROR();
  }
}

/*****************************************************************************/
/*                                                                           */
/* 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 kbdreturn)
{
  if (kbdreturn == SCAN_ACK) {              /* If KBD acknowledges.          */
    Current_State = S_SENTTYPD;             /* Set FSM to next state.        */
    KbdResend = TURNOFF;                    /* Turn off resend flag.         */
    SendData(Type_Rate);                    /* Send type rate to KBD.        */
  }
  else if (kbdreturn == KBD_RESEND) {       /* Are we asked to resend?       */
    RESENDData(KBD_SETREPEAT);              /* then we will.                 */
  }
  else  {
    KeyERROR();
  }
}

/*****************************************************************************/
/*                                                                           */
/* 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 kbdreturn)
{
  if (kbdreturn == SCAN_ACK) {              /* If KBD acknowledges.          */
    Current_State = S_NOCMDIPG;             /* Set FSM to begining state.    */
    KbdResend = TURNOFF;                    /* Turn off resend flag.         */
    CMD_In_Progress = TURNOFF;              /* Clear CMD in Progress flag.   */
    if (HotPlugInit == TURNON) {            /* If Hot Plug is occuring.      */
      if (InitTime == TURNON) {             /* If it's Initialization time.  */
        InitTime = TURNOFF;
        _asm {
              mov KbdCS, cs                 /* Save the code segment value.  */
              mov KbdDS, ds                 /* Save the data segment value.  */
        }                                   /* Register with KBDDI.          */
        KbdDI_Handle = KbdDI_Entry(CMD_Open, Offset_IDC_Handler, KbdDS,KbdCS);
      }
      KbdDI_Entry(CMD_Reinit, NULL, KbdDS, KbdDI_Handle);    /* Notify KBDDI.*/
      HotPlugInit = TURNOFF;                /* Turn off hot plug flag.       */
    }
    if (Enabler == TYPE_Enable) {           /* If type rate or               */
      Enabler = INT_Enable;                 /* Then set to be enabled.       */
    }
    else {
      DisableFlag = TURNOFF;
      Disable_Keyboard();                   /* Make sure kbd is disabled.    */
    }
    if (Next_Block != Next_Run) {           /* Check if blocked thread.      */
       Run_Thread();                        /* If yes then run it.           */
    }
  }
  else if (kbdreturn == KBD_RESEND) {       /* Are we asked to resend?       */
    RESENDData(Type_Rate);                  /* then we will.                 */
  }
  else  {
    KeyERROR();
  }
}

/*****************************************************************************/
/*                                                                           */
/* 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 kbdreturn)
{
  BYTE temp;

  SCSet = (BYTE)((kbdreturn & TRANSLATE_BIT)!=0 ? ScanCodeSet2 : ScanCodeSet1);

  if (SCSet == ScanCodeSet1) {
    Current_State = S_SENTSCSC;             /* Set FSM to next state.        */
    SendData(KBD_NEW_SETSCAN);              /* Send Scan-Code Set to KBD.    */
  }
  else {
    DisableFlag = TURNOFF;                  /* This will disable kbd and set */
                                            /*  Enabler to Type_Enable.      */
    CMD_In_Progress = TURNOFF;              /* Clear CMD in Progress flag.   */
    temp = Type_Rate;                       /* Get latest type rate.         */
    Type_Rate = -1;                         /* Reset value.                  */
    Set_Typematic(temp);                    /* Reset kbd type rate.          */
  }
}

/*****************************************************************************/
/*                                                                           */
/* 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 kbdreturn)
{
  if (kbdreturn == SCAN_ACK) {              /* If KBD acknowledges.          */
    Current_State = S_SENTSCSD;             /* Set FSM to next state.        */
    KbdResend = TURNOFF;                    /* Turn off resend flag.         */
    SendData(SCSet);                        /* Send Scan-Code Set to KBD.    */
  }
  else if (kbdreturn == KBD_RESEND) {       /* Are we asked to resend?       */
    RESENDData(KBD_NEW_SETSCAN);            /* then we will.                 */
  }
  else  {
    KeyERROR();
  }
}

/*****************************************************************************/
/*                                                                           */
/* 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 kbdreturn)
{
  BYTE temp;

  if (kbdreturn == SCAN_ACK) {              /* If KBD acknowledges.          */
    KbdResend = TURNOFF;                    /* Turn off resend flag.         */
    DisableFlag = TURNOFF;                  /* This will disable kbd and set */
                                            /*  Enabler to Type_Enable.      */
    CMD_In_Progress = TURNOFF;              /* Clear CMD in Progress flag.   */
    temp = Type_Rate;                       /* Get latest type rate.         */
    Type_Rate = -1;                         /* Reset value.                  */
    Set_Typematic(temp);                    /* Reset kbd type rate.          */

  }
  else if (kbdreturn == KBD_RESEND) {       /* Are we asked to resend?       */
    RESENDData(SCSet);                      /* then we will.                 */
  }
  else  {
    KeyERROR();
  }
}

/*****************************************************************************/
/*                                                                           */
/* 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 kbdreturn)
{
  if (kbdreturn == SCAN_ACK) {              /* If KBD acknowledges.          */
    PrevState = Current_State;              /* Save current state as previous*/
    Current_State = S_WAITIDB1;             /* Set FSM to next state.        */
    KbdResend = TURNOFF;                    /* Reset the flag to resend data.*/
    StartTimer();                           /* Restart timer for ID bytes.   */
  }
  else if (kbdreturn == KBD_RESEND) {       /* Are we asked to resend?       */
    RESENDData(KBD_REPORTID);               /* then we will.                 */
  }
  else  {
    KeyERROR();
  }
}

/*****************************************************************************/
/*                                                                           */
/* 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 kbdreturn)
{
  Current_State = S_NOCMDIPG;               /* Set FSM to begining state.    */
  if (kbdreturn == SCAN_ACK) {              /* If KBD acknowledges.          */
     KbdResend = TURNOFF;                   /* Turn off resend flag.         */
  }
  else if(kbdreturn==KBD_RESEND) {          /* Are we asked to resend?       */
    switch(KbdResend) {
      case TURNOFF  :
      case TURNON   : KbdResend++;            break;
      case TRYAGAIN : KeyERROR();             break;
      default       : KbdResend = TURNOFF;    break;
    }
  }
  else  {
    KeyERROR();
  }
}

/*****************************************************************************/
/*                                                                           */
/* 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 FAR STATE_MACH_CTL(void)
{
  BYTE   KeyInput;

  STI;
  InterruptTime = TURNON;
  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.*/
  }
  if (Disable_Keyboard()) {                 /* Disable keyboard.             */
    IODelay();                              /*  then delay for a little.     */
    Enabler = INT_Enable;                   /* Set enable flag.              */
  }
  KbdStatusWait();                     /* Wait until KBD ready to pass data. */
  KeyInput = (BYTE)inp(PORT_BUFFER);        /* Get scan code.                */

  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;
  }
  if (Enabler == INT_Enable) {              /* If we disabled keyboard       */
    Enable_Keyboard();                      /*  then reenable it.            */
  }
  if ((KbdError) && (HotPlugInit == TURNON)) {
    SENTTYPD(SCAN_ACK);            /* Keyboard is not attached or responding.*/
  }
  InterruptTime = TURNOFF;
  CLI;
  DevHelp_EOI((USHORT) KBD_IRQ );           /* Issue End Of Interrupt.       */
  _asm{ Clc }                               /* Interrupt always claimed      */
}


