/**************************************************************************
 *
 * SOURCE FILE NAME = vkbdio.c
 *
 * DESCRIPTIVE NAME = Virtual Keyboard Device Driver (VKBD), VKBD I/O hooks
 *
 * Copyright : COPYRIGHT IBM CORPORATION, 1991, 1992
 *             Copyright Microsoft Corporation, 1990
 *             LICENSED MATERIAL - PROGRAM PROPERTY OF IBM
 *             REFER TO COPYRIGHT INSTRUCTION FORM#G120-2083
 *             RESTRICTED MATERIALS OF IBM
 *             IBM CONFIDENTIAL
 *
 * VERSION = V2.0
 *
 * DATE         05/07/91
 *
 * DESCRIPTION
 *          This module contains all the VKBD's I/O port handlers.
 *
 * FUNCTIONS   VKReadCmd
 *             VKWriteCmd
 *             VKReadBuff
 *             VKWriteBuff
 *             VKCheckKbdCmd
 *
 * DEPENDENCIES:
 *
 * NOTES
 *
 * STRUCTURES
 *
 * EXTERNAL REFERENCES
 *
 * EXTERNAL FUNCTIONS
 *
 * CHANGE ACTIVITY =
 *  DATE      FLAG        APAR     CHANGE DESCRIPTION
 *  --------  ----------  -----    --------------------------------------
 *  mm/dd/yy  @Vr.mpppxx  xxxxx    xxxxxxx
 *  02/23/89                       MTS Created.
 *  11/18/92                       RAR Added 0xD2 command support
 *                                 (written by Jeff Muir)
 *  02/17/93  @v2.0DDK01  DCR1131
 ***********************************************************************/

#define  INCL_DOSERRORS                /* @V2.0DDK01                         */
#include <mvdm.h>
#include <bseerr.h>                    /* @V2.0DDK01                         */
#include <basemid.h>
#include "vkbdp.h"

#ifdef   VDDSTRICT
MODNAME = __FILE__;
#endif

/*
***     External References
*/

/*      Global Data*/
extern FPFNPDD fpfnPKVDDProc;          /* address of PDD entry point         */
extern USHORT usKbdHWID;               /* keyboard hardware ID               */
extern HIRQ hirq;                      /* handle for keyboard IRQ interrupt  */

/*
** Instance Data
*/
extern FLAGS flVDM;
extern UINT uPortbuffstate;            /* index selects Kbd, Command,        */
                                       /* or Output                          */
extern BYTE virtCommand;               /* current virtual Command byte       */
extern BYTE virtTestInput;             /* current virtual Test-Input byte    */
extern BYTE virtKbdEnable;             /* virtual keyboard enable state      */
extern BYTE virtOldKbdEnable;          /* store virtKbdEnable during set     */
                                       /* LED and RPT                        */
extern BYTE virtStatus;                /* current virtual Status byte        */
extern BYTE virtInput;                 /* current virtual input port byte    */
extern BYTE virtKbdLEDs;               /* virtual keyboard LED states        */
extern BYTE virtKbd;                   /* current virtual Kbd byte           */
extern BYTE virtOutput;                /* current virtual Output byte        */
extern BYTE virtBuff;                  /* current virtual Buffer byte        */
extern BYTE virtKbdLastScan;           /* virtual keyboard last scan         */
                                       /* (for RESEND)                       */
extern BYTE virtScanSet;               /* virtual scan code set              */
extern BYTE iKPPopup;                  /* @V2.0DDK01 KP warning popup sw     */
extern BYTE virtmem1f;                 /* virtual 8042 memory 1F location     */
#pragma  BEGIN_SWAP_CODE


/***************************************************************************
 *
 * FUNCTION NAME = VKReadCmd
 *
 *  DESCRIPTIVE NAME: Byte input handler for PORT_CMD
 *                    See 8086 Emulation for complete semantics.
 *
 *                    This registered subroutine is called whenever byte
 *                    input is performed on the keyboard PORT_CMD.
 *
 *  FUNCTION:
 *
 *  ENTRY POINT:
 *
 *  INPUT:
 *          ulPort - port address (not used)
 *          pcrf   - pointer to client register frame (not used)
 *
 *  EXIT-NORMAL:
 *          returns data read
 *  EXIT-ERROR:
 *
 *  INTERNAL REFERENCES:
 *    ROUTINES: NONE
 *
 *  EXTERNAL REFERENCES:
 *    ROUTINES: None
 *
 *  CONTEXT
 *      VDM Task-time
 *
 *  NOTE
 *      VKBD makes the assumption that it does not have to update
 *      its inhibit switch state unless the user queries it via
 *      the status register or the input port.  This means that
 *      VKBD is not responsible for the actual effect of the
 *      inhibit switch.  PKBD should make sure that if the keylock
 *      is in the locked position, no scan code is sent to VKBD.
 *
 *  PSEUDOCODE
 *      return virtStatus
 *
 ****************************************************************************/


BYTE HOOKENTRY VKReadCmd(ULONG ulPort,PCRF pcrf)
{
  ADDDEBUG(CURRENT_VDM, (UCHAR)(ulPort+0x20), virtStatus);
  return  virtStatus;
}                                      /* VKReadCmd                          */

/***************************************************************************
 *
 * FUNCTION NAME = VKWriteCmd
 *
 *  DESCRIPTIVE NAME: Byte output handler for CMD port
 *
 *  FUNCTION:
 *
 *      Byte output handler for PORT_CMD
 *      See 8086 Emulation for complete semantics.
 *
 *      This registered subroutine is called whenever byte output
 *      is performed on the keyboard PORT_CMD.
 *
 *      ENTRY
 *          ulPort   - port address (not used)
 *          pcrf     - pointer to client register frame (not used)
 *          bOutData - output data
 *
 *      EXIT
 *          None
 *
 *      CONTEXT
 *          VDM Task-time
 *
 *      NOTE
 *          VKBD makes the assumption that it does not have to update
 *          its inhibit switch state unless the user queries it via
 *          the status register or the input port.  This means that
 *          VKBD is not responsible for the actual effect of the
 *          inhibit switch.  PKBD should make sure that if the keylock
 *          is in the locked position, no scan code is sent to VKBD.
 *
 *      PSEUDOCODE
 *          set STATUS_DATAEXPECTED in virtStatus
 *          switch (value)
 *            case CMD_READCOMMAND:
 *              call VKAddScan(virtCommand|FKEYPKT_WAITEOI)
 *              break
 *            case CMD_WRITECOMMAND:
 *              set uPortbuffstate to PORTBUFF_COMMAND
 *              break
 *            case CMD_SELFTEST:
 *              call VKAddScan(SCAN_TESTDONE|FKEYPKT_WAITEOI)
 *              break
 *            case CMD_INTERFACETEST:
 *              call VKAddScan(SCAN_TESTDONE|FKEYPKT_WAITEOI)
 *              //to indicate success
 *              break
 *            case CMD_DISABLEKBD:
 *              set COMMAND_DISABLEKBD in virtCommand
 *              break
 *            case CMD_ENABLEKBD:
 *              clear COMMAND_DISABLEKBD in virtCommand
 *              break
 *            case CMD_READINPUT:
 *              call PDD to update virtInput
 *              call VKAddScan(virtInput|FKEYPKT_WAITEOI)
 *              break
 *            case CMD_READOUTPUT:
 *              call VKAddScan(virtOutput|FKEYPKT_WAITEOI)
 *              break
 *            case CMD_WRITEOUTPUT:
 *              set uPortbuffstate to PORTBUFF_OUTPUT
 *              break
 *            case CMD_READTESTINPUT:
 *              call VKAddScan(virtTestInput|FKEYPKT_WAITEOI)
 *              break
 *          endswitch
 *
 ****************************************************************************/

VOID HOOKENTRY VKWriteCmd(BYTE bOutData,ULONG ulPort,PCRF pcrf)
{
  KINH kinh;
  KEYPKT kp;
  PKEYPKT pkp = SSToDS(&kp);
  FLAGS flResp;
  ADDDEBUG(CURRENT_VDM, (UCHAR)ulPort, bOutData);
  memset(pkp, 0, sizeof(kp));
  virtStatus |= STATUS_DATAEXPECTED;
  kp.kp_fsKey = FKEYPKT_WAITEOI;

  switch (bOutData)
  {
    case  CMD_READCOMMAND :
      kp.kp_Scan = virtCommand;
      VKAddScan(CURRENT_VDM, pkp, ADDSCAN_TASK|ADDSCAN_RESPONSE);
      break;

    case  CMD_WRITECOMMAND :
      uPortbuffstate = PORTBUFF_COMMAND;
      break;

    case  CMD_SELFTEST :
      kp.kp_Scan = SCAN_TESTDONE;
      VKAddScan(CURRENT_VDM, pkp, ADDSCAN_TASK|ADDSCAN_RESPONSE);
      break;

    case  CMD_INTERFACETEST :
      kp.kp_Scan = IFTEST_NOERROR;
      VKAddScan(CURRENT_VDM, pkp, ADDSCAN_TASK|ADDSCAN_RESPONSE);
      break;

    case  CMD_DISABLEKBD :
      virtCommand |= COMMAND_DISABLEKBD;
      break;

    case  CMD_ENABLEKBD :
      virtCommand &= ~COMMAND_DISABLEKBD;
      VKTransferScan((ULONG)CURRENT_VDM, 0);
      break;

    case  CMD_READINPUT :
      kinh.kinh_len = sizeof(KINH);
      AssertRC((*fpfnPKVDDProc)(PKBDCMD_QUERYINHIBIT, F16PNULL, F16PFROMSSP
         (&kinh)));

      if (kinh.kinh_fInhibited)
        virtInput &= ~INPUT_NOTINHIBITED;
      else
        virtInput |= INPUT_NOTINHIBITED;
      kp.kp_Scan = virtInput;
      VKAddScan(CURRENT_VDM, pkp, ADDSCAN_TASK|ADDSCAN_RESPONSE);
      break;

    case  CMD_READOUTPUT :
      kp.kp_Scan = virtOutput;
      VKAddScan(CURRENT_VDM, pkp, ADDSCAN_TASK|ADDSCAN_RESPONSE);
      break;

    case  CMD_WRITEOUTPUT :
      uPortbuffstate = PORTBUFF_OUTPUT;
      break;

    case  CMD_READTESTINPUT :
      virtTestInput ^= TESTINPUT_T0|TESTINPUT_T1; // toggle it
      kp.kp_Scan = virtTestInput;
      VKAddScan(CURRENT_VDM, pkp, ADDSCAN_TASK|ADDSCAN_RESPONSE);
      break;

    case  CMD_PS2_TESTPW :             /* @V2.0DDK01                         */
    case  CMD_PS2_LOADPW :             /* @V2.0DDK01                         */
    case  CMD_PS2_ENABLEPW :           /* @V2.0DDK01                         */
      if (iKPPopup)
      {                                /* @V2.0DDK01                         */
        iKPPopup = FALSE;              /* @V2.0DDK01                         */
        VDHPopup("VKBD\0A20\0",        /* @V2.0DDK01                         */
           2, ERROR_VDD_LOCK_USEAGE_DENIED, SSToDS(&flResp), VDHP_IGNORE+
           VDHP_TERMINATE_SESSION, NULL);
      }                                /* endif                              */
      break;

    case  CMD_WRITE_KYBD_OUTPUT :
      uPortbuffstate = PORTBUFF_KYBD_OUTPUT;
      break;

    case  CMD_PS2_READMEMHI:
      kp.kp_Scan = virtmem1f;
      VKAddScan(CURRENT_VDM, pkp, ADDSCAN_TASK|ADDSCAN_RESPONSE);
      break;

    case  CMD_PS2_WRITEMEMHI:
      uPortbuffstate = PORTBUFF_KYBD_WRIMHI;
      break;

#ifdef   VDDDEBUG
    default  :
      if ((bOutData >= CMD_PULSEOUTPUTLO) &&  /* pulse output other    */
         (bOutData <= CMD_PULSEOUTPUTHI) &&   /* than cpu reset        */
         (bOutData&OUTPUT_CPURESET))
        break;
      printf("VKWriteCmd: unsupported keyboard command (0x%lx, 0x%x)\n", ulPort
         , bOutData);
#endif
  }
}                                      /* VKWriteCmd                         */

/***************************************************************************
 *
 * FUNCTION NAME = VKReadBuff
 *
 *  DESCRIPTIVE NAME: Byte input handler for BUFFER port
 *
 *  FUNCTION:
 *
 *      Byte input handler for PORT_BUFFER
 *      See 8086 Emulation for complete semantics.
 *
 *      This registered subroutine is called whenever byte input
 *      is performed on the keyboard PORT_BUFFER.
 *
 *      ENTRY
 *          ulPort   - port address (not used)
 *          pcrf     - pointer to client register frame (not used)
 *
 *      EXIT
 *          returns data read
 *
 *      CONTEXT
 *          VDM Task-time
 *
 *      PSEUDOCODE
 *          clear STATUS_BUFFERFULL in virtStatus
 *          if VIRR was set {
 *              call VDHClearVIRR to lower the virtual keyboard IRQ line
 *              // note that we may have never actually asserted the IRQ
 *              clear VDM_EOIPENDING
 *              decrement number of pending irets;
 *          }
 *          call VKTransferScan to check if any more scan code pending
 *          return virtBuff
 ****************************************************************************/


BYTE HOOKENTRY VKReadBuff(ULONG ulPort,PCRF pcrf)
{
  register BYTE rc;
  rc = virtBuff;
  ADDDEBUG(CURRENT_VDM, (UCHAR)(ulPort+0x20), rc);
  virtStatus &= ~STATUS_BUFFERFULL;
  VDHClearVIRR(CURRENT_VDM, hirq);

  if (!(VDHQueryVIRQ(CURRENT_VDM, hirq)&VPICQ_IN_SERVICE) && (flVDM
     &VDM_EOIPENDING))
    flVDM &= ~(VDM_EOIPENDING|VDM_IRETPENDING);
  VKTransferScan((ULONG)CURRENT_VDM, 0);

  return  rc;
}                                      /* VKReadBuff                         */

/***************************************************************************
 *
 * FUNCTION NAME = VKWriteBuff
 *
 *  DESCRIPTIVE NAME: Byte output handler for BUFF port
 *
 *  FUNCTION:
 *
 *      Byte output handler for PORT_BUFFER
 *      See 8086 Emulation for complete semantics.
 *
 *      This registered subroutine is called whenever byte output
 *      is performed on the keyboard PORT_BUFFER.
 *
 *      ENTRY
 *          ulPort   - port address (not used)
 *          pcrf     - pointer to client register frame (not used)
 *          bOutData - output data
 *
 *      EXIT
 *          None
 *
 *      CONTEXT
 *          VDM Task-time
 *
 *      PSEUDOCODE
 *          clear STATUS_DATAEXPECTED in virtStatus
 *          switch (uPortbuffstate)
 *            case PORTBUFF_KBD:
 *              call VKCheckKbdCmd(value)
 *              break
 *            case PORTBUFF_COMMAND:
 *              update virtCommand (mask out PCMODE and XLTMODE bits)
 *              transfer COMMAND_SYSFLAG to STATUS_SYSFLAG bit in virtStatus
 *              call VKTransferScan     // since INTENABLE may have been set
 *              break                   // or DISABLEKBD cleared
 *            case PORTBUFF_OUTPUT:
 *              update virtOutput
 *              transfer OUTPUT_BUFFERFULL to STATUS_BUFFERFULL bit
 *                  // don't bother moving !OUTPUT_CMDEMPTY to STATUS_CMDFULL
 *                  // since we simulate a very fast CMD port (never full),
 *                  // and since the effect of doing this is unknown anyway....
 *
 *              call VKTransferScan     // since BUFFERFULL may have been set
 *              break
 *          endswitch
 *          set uPortbuffstate back to PORTBUFF_KBD
 *
 ****************************************************************************/

VOID HOOKENTRY VKWriteBuff(BYTE bOutData,ULONG ulPort,PCRF pcrf)
{
  FLAGS flResp;
  KEYPKT kp;
  PKEYPKT pkp = SSToDS(&kp);
  ADDDEBUG(CURRENT_VDM, (UCHAR)ulPort, bOutData);
  virtStatus &= ~STATUS_DATAEXPECTED;

  switch (uPortbuffstate)
  {
    case  PORTBUFF_KBD :
      VKCheckKbdCmd(bOutData);
      break;

    case  PORTBUFF_COMMAND :

#ifdef   VDDDEBUG
         if ((bOutData&COMMAND_PCMODE) || !(bOutData&COMMAND_XLTMODE))
        printf(
          "VKWriteBuff: unsupported pcmode or xltmode command (0x%lx, 0x%x)\n",
           ulPort, bOutData);
#endif
      virtCommand = (BYTE)(bOutData&~COMMAND_PCMODE|COMMAND_XLTMODE);
      virtStatus &= ~STATUS_SYSFLAG;   /* copy sysflag to virtStatus */
      virtStatus |= virtCommand&COMMAND_SYSFLAG;
      VKTransferScan((ULONG)CURRENT_VDM, 0);
      /* @IBM BUGBUG MTS how about inhibit override?*/
      break;

    case  PORTBUFF_OUTPUT :

#ifdef   VDDDEBUG
         if (!(bOutData&OUTPUT_CPURESET))
        printf("VKWriteBuff: unsupported CPU reset (0x%lx, 0x%x)\n", ulPort,
           bOutData);
#endif
      if ((virtOutput^bOutData)&OUTPUT_A20)
      {                                /* A20 has changed */
        VDHPopup("VKBD\0A20\0", 2, MSG_VDD_UNSUPPORTED_ACCESS, SSToDS(&flResp),
           VDHP_IGNORE+VDHP_TERMINATE_SESSION, NULL);
        if (flResp == VDHP_TERMINATE_SESSION)
          VDHKillVDM(CURRENT_VDM);
      }
      virtOutput = (BYTE)(bOutData&~(OUTPUT_KBDDATA+OUTPUT_KBDCLOCK)|
         OUTPUT_CPURESET);
      virtStatus &= ~STATUS_BUFFERFULL;
      virtStatus |= (bOutData&OUTPUT_BUFFERFULL) >> (ZEROBITS(OUTPUT_BUFFERFULL
         )-ZEROBITS(STATUS_BUFFERFULL));
      VKSimulateInt(CURRENT_VDM);
      VKTransferScan((ULONG)CURRENT_VDM, 0);
      break;

    case  PORTBUFF_KYBD_OUTPUT :
      memset(pkp, 0, sizeof(kp));
      kp.kp_fsKey = FKEYPKT_WAITEOI;
      kp.kp_Scan = bOutData;
      VKAddScan(CURRENT_VDM, pkp, ADDSCAN_TASK);
      break;

    case  PORTBUFF_KYBD_WRIMHI :
      virtmem1f=bOutData;
      break;
  }
  uPortbuffstate = PORTBUFF_KBD;
}                                 /* VKWriteBuff                             */
#pragma  END_SWAP_CODE            /* @IBM BUGBUG MTS bypass compiler problem */
#pragma  BEGIN_GLOBAL_CODE        /* @IBM BUGBUG                             */

/***************************************************************************
 *
 * FUNCTION NAME = VKCheckKbdCmd
 *
 *  DESCRIPTIVE NAME: Parse keyboard commands
 *
 *  FUNCTION:
 *
 *      Determines action to take based on command intended for the
 *      physical keyboard (not the controller, the *keyboard*).
 *
 *      ENTRY
 *          bCmd - keyboard command code
 *
 *      EXIT
 *          None
 *
 *      CONTEXT
 *          VDM Task-time
 *
 *      PSEUDOCODE
 *          switch (virtKbd)
 *            case KBD_SETLEDS:             // second part of LED Cmd
 *              set virtKbdLEDs to bCmd
 *              if VDM_FGND
 *                call (*fpfnPKVDDProc)(PKBDCMD_SETLEDS, bCmd, NULL)
 *              endif
 *              break
 *            case KBD_SETREPEAT:           // second part of REPEAT Cmd
 *              call (*pfnPKVDDProc)(PKBDCMD_SETREPEAT, bCmd, NULL)
 *              break
 *          endswitch
 *
 *          switch (virtKbd)
 *            case KBD_SETLEDS:
 *            case KBD_SETREPEAT:           // simulate ACK, PDD won't
 *              clear virtKbd
 *              call VKAddScan(SCAN_ACK|FKEYPKT_WAITEOI)
 *              break
 *          endswitch
 *
 *          switch (bCmd)
 *            case KBD_SETLEDS:
 *            case KBD_SETREPEAT:           // simulate ACK for 1st part
 *              set virtKbd to Cmd
 *              call VKAddScan(SCAN_ACK|FKEYPKT_WAITEOI)
 *              break
 *            case KBD_ECHO:
 *              call VKAddScan(SCAN_ECHO|FKEYPKT_WAITEOI)
 *              break
 *            case KBD_REPORTID:
 *              call VKAddScan(SCAN_ACK|FKEYPKT_WAITEOI)
 *              call VKAddScan(usKbdHWID.lobyte|FKEYPKT_WAITEOI)
 *              if usKbdHWID.hibyte is non-zero (ie, enhanced keyboard)
 *                call VKAddScan(usKbdHWID.hibyte|FKEYPKT_WAITEOI)
 *              endif
 *              break
 *            case KBD_DEFDISABLE:
 *              clear virtKbdEnable
 *            case KBD_RESET:
 *            case KBD_SETDEFAULT:
 *              clear virtKbdLEDs
 *              if VDM_FGND
 *                call (*pfnPKVDDProc)(PKBDCMD_SETLEDS, virtKbdLEDs, NULL)
 *              endif
 *              call (*pfnPKVDDProc)(PKBDCMD_SETREPEATRATE, 0, NULL)
 *              //to set default rate
 *              call VKClearScan
 *            case KBD_NEW_REPT:
 *            case KBD_NEW_MAKEBRK:
 *            case KBD_NEW_MAKEONLY:
 *            case KBD_NEW_REPTMAKEBRK:
 *            case KBD_NEW_KEYREPT:
 *            case KBD_NEW_KEYMAKEBRK:
 *            case KBD_NEW_KEYMAKEONLY:
 *              call VKAddScan(SCAN_ACK|FKEYPKT_WAITEOI)
 *              if KBD_RESET
 *                call VKAddScan(SCAN_BATCOMPLETE|FKEYPKT_WAITEOI)
 *              endif
 *              break
 *            case KBD_RESEND:
 *              call VKAddScan(virtKbdLastScan|FKEYPKT_WAITEOI)
 *              break
 *            case KBD_ENABLE:
 *              set virtKbdEnable
 *              call VKClearScan
 *            default:
 *              call VKAddScan(SCAN_ACK|FKEYPKT_WAITEOI)
 *              break
 *          endswitch
 *
 ****************************************************************************/

VOID PRIVENTRY VKCheckKbdCmd(BYTE bCmd)
{
  KLEDS kleds;
  KRPT krpt;
  KEYPKT kp;
  PKEYPKT pkp = SSToDS(&kp);
  BYTE bOldStatus;
  memset(pkp, 0, sizeof(kp));
  kp.kp_fsKey = FKEYPKT_WAITEOI;

  if (((virtKbd == KBD_SETLEDS) || (virtKbd == KBD_SETREPEAT)) && (bCmd >=
     KBD_LOWEST))
  {                                    /* invalid command parameter */
    virtKbd = 0;
    if (usKbdHWID)            /* 83-key keyboard does not re-enable scanner! */
      virtKbdEnable = virtOldKbdEnable;
  }
  if (virtKbd == KBD_SETLEDS)
  {
    if (bCmd&~LED_ALL)
      kp.kp_Scan = (UCHAR)(usKbdHWID?SCAN_RESEND:SCAN_ACK);
    else
    {
      virtKbd = 0;
      virtKbdEnable = virtOldKbdEnable;
      virtKbdLEDs = bCmd;
      if (flVDM&VDM_FGND)
      {
        kleds.kled_len = sizeof(KLEDS);
        kleds.kled_fsLEDs = virtKbdLEDs;
        AssertRC((*fpfnPKVDDProc)(PKBDCMD_SETLEDS, F16PFROMSSP(&kleds),
           F16PNULL));
      }
      kp.kp_Scan = SCAN_ACK;
    }
    VKAddScan(CURRENT_VDM, pkp, ADDSCAN_TASK|ADDSCAN_RESPONSE);
  }
  else
    if (virtKbd == KBD_SETREPEAT)
    {
      if (bCmd&0x80)
        kp.kp_Scan = (UCHAR)(usKbdHWID?SCAN_RESEND:SCAN_ACK);
      else
      {
        virtKbd = 0;
        virtKbdEnable = virtOldKbdEnable;
        if (!(flVDM&VDM_KBDRESTRICT))
        {
          krpt.krpt_len = sizeof(KRPT);
          krpt.krpt_usDelay = (USHORT)((bCmd&REPEAT_DELAYMASK) >> ZEROBITS(bCmd
             &REPEAT_DELAYMASK));
          krpt.krpt_usRate = (USHORT)(bCmd&REPEAT_RATEMASK);
          AssertRC((*fpfnPKVDDProc)(PKBDCMD_SETREPEATRATE, F16PFROMSSP(&krpt),
             F16PNULL));
        }
        kp.kp_Scan = SCAN_ACK;
      }
      VKAddScan(CURRENT_VDM, pkp, ADDSCAN_TASK|ADDSCAN_RESPONSE);
    }
    else
      if ((virtKbd == KBD_NEW_KEYREPT) || (virtKbd == KBD_NEW_KEYMAKEBRK) ||
         (virtKbd == KBD_NEW_KEYMAKEONLY))
      {
        virtKbdEnable = virtOldKbdEnable;
        virtKbd = 0;
        kp.kp_Scan = SCAN_ACK;
        VKAddScan(CURRENT_VDM, pkp, ADDSCAN_TASK|ADDSCAN_RESPONSE);
      }
      else
        if (virtKbd == KBD_NEW_SETSCAN)
        {
          virtKbdEnable = virtOldKbdEnable;
          virtKbd = 0;
          bOldStatus = virtStatus;
          virtStatus |= STATUS_BUFFERFULL;
          if (bCmd == 0)
          {
            kp.kp_Scan = virtScanSet;
            VKAddScan(CURRENT_VDM, pkp, ADDSCAN_TASK|ADDSCAN_RESPONSE);
          }
          else
            virtScanSet = bCmd;
          virtStatus = bOldStatus;
          kp.kp_Scan = SCAN_ACK;
          VKAddScan(CURRENT_VDM, pkp, ADDSCAN_TASK|ADDSCAN_RESPONSE);
        }
        else

          switch (bCmd)
          {
            case  KBD_NEW_KEYREPT :
            case  KBD_NEW_KEYMAKEBRK :
            case  KBD_NEW_KEYMAKEONLY :
              VKClearScan(CURRENT_VDM);

            case  KBD_NEW_SETSCAN :
            case  KBD_SETLEDS :
            case  KBD_SETREPEAT :
              virtKbd = bCmd;
              kp.kp_Scan = SCAN_ACK;
              VKAddScan(CURRENT_VDM, pkp, ADDSCAN_TASK|ADDSCAN_RESPONSE);
              virtOldKbdEnable = virtKbdEnable;
              virtKbdEnable = 0;   /* disable scanner while waiting for parm */
              break;

            case  KBD_ECHO :
              kp.kp_Scan = SCAN_ECHO;
              VKAddScan(CURRENT_VDM, pkp, ADDSCAN_TASK|ADDSCAN_RESPONSE);
              break;

            case  KBD_REPORTID :
              bOldStatus = virtStatus;
              virtStatus |= STATUS_BUFFERFULL;
              if ((usKbdHWID != KBDID_UNKNOWN) && (usKbdHWID != KBDID_PCAT))
              {
                kp.kp_Scan = BYTEOF(usKbdHWID, 1);
                VKAddScan(CURRENT_VDM, pkp, ADDSCAN_TASK|ADDSCAN_RESPONSE);
                kp.kp_Scan = BYTEOF(usKbdHWID, 0);
                VKAddScan(CURRENT_VDM, pkp, ADDSCAN_TASK|ADDSCAN_RESPONSE);
              }
              virtStatus = bOldStatus;
              kp.kp_Scan = SCAN_ACK;
              VKAddScan(CURRENT_VDM, pkp, ADDSCAN_TASK|ADDSCAN_RESPONSE);
              break;

            case  KBD_RESEND :
              kp.kp_Scan = virtKbdLastScan;
              VKAddScan(CURRENT_VDM, pkp, ADDSCAN_TASK|ADDSCAN_RESPONSE);
              break;

            case  KBD_DEFDISABLE :
              virtKbdEnable = 0;
            case  KBD_RESET :
            case  KBD_SETDEFAULT :
              virtKbdLEDs = 0;         /* assume default is all LEDs off */
              if ((flVDM&VDM_FGND) && (bCmd == KBD_RESET))
              {
                kleds.kled_len = sizeof(KLEDS);
                kleds.kled_fsLEDs = virtKbdLEDs;
                AssertRC((*fpfnPKVDDProc)(PKBDCMD_SETLEDS, F16PFROMSSP(&kleds),
                   F16PNULL));
              }
              if (!(flVDM&VDM_KBDRESTRICT))
              {
                krpt.krpt_len = sizeof(KRPT);
                krpt.krpt_usDelay = krpt.krpt_usRate = 0; /* defl repeat rate */
                AssertRC((*fpfnPKVDDProc)(PKBDCMD_SETREPEATRATE, F16PFROMSSP
                   (&krpt), F16PNULL));
              }
              VKClearScan(CURRENT_VDM);
              bOldStatus = virtStatus;
              virtStatus |= STATUS_BUFFERFULL;
              if (bCmd == KBD_RESET)
              {
                kp.kp_Scan = SCAN_BATCOMPLETE;
                VKAddScan(CURRENT_VDM, pkp, ADDSCAN_TASK|ADDSCAN_RESPONSE);
              }
              if (bCmd == KBD_RESET)
                virtKbdEnable++;
              virtStatus = bOldStatus;
              kp.kp_Scan = SCAN_ACK;
              VKAddScan(CURRENT_VDM, pkp, ADDSCAN_TASK|ADDSCAN_RESPONSE);
              break;

            case  KBD_ENABLE :
              virtKbdEnable = 1;
            case  KBD_NEW_REPT :       /* unsupported commands          */
            case  KBD_NEW_MAKEBRK :    /* so fake it                    */
            case  KBD_NEW_MAKEONLY :
            case  KBD_NEW_REPTMAKEBRK :
              VKClearScan(CURRENT_VDM);
              kp.kp_Scan = SCAN_ACK;
              VKAddScan(CURRENT_VDM, pkp, ADDSCAN_TASK|ADDSCAN_RESPONSE);
              break;

            default  :                 /* invalid commands */
              kp.kp_Scan = (UCHAR)(usKbdHWID?SCAN_RESEND:SCAN_ACK);
              VKAddScan(CURRENT_VDM, pkp, ADDSCAN_TASK|ADDSCAN_RESPONSE);
          }                            /* end switch                         */

}                                      /* VKCheckKbdCmd                      */

#pragma  END_GLOBAL_CODE               /* to bypass compiler problem         */
