; SCCSID = src/dev/kbd/kbdbase.ssc/kbdstra.asm, kbd, c.basedd 98/01/16
    Page    58,132
    Title   KBDSTRA - Strategy Entry Point
    Name    KBDSTRA

;/*************************************************************************
;*
;* SOURCE FILE NAME = KBDSTRA.ASM
;*
;* DESCRIPTIVE NAME = Physical Keyboard Device Driver Strategy Router.
;*
;* 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         03/05/91
;*
;* DESCRIPTION  Keyboard Strategy time requests are fielded and routed to
;*              the appropriate keyboard IOCTL or worker routine.
;*
;*
;* FUNCTIONS    KBDSTRA()       Keyboard Device Driver Strategy routine
;*              KbdFlush()      Keyboard Input Flush request processor.
;*              KbdCmdErr       Keyboard Command and General Error Routine
;*              KbdInit         Keyboard Device Driver Initialization
;*              KbdOpen         Open Kbd or Keystroke Monitor request processor
;*              KbdClose        Close Kbd or Keystroke Monitor request processor
;*
;*
;* NOTES        DEPENDENCIES:  Controller or keyboard must be set to the PC compati-
;*                             ble scan code set.
;*              RESTRICTIONS:  Machine must be a 386 or compatible with a 386.
;*
;*
;* STRUCTURES   NONE
;*
;* EXTERNAL REFERENCES
;*
;*              NONE
;*
;* EXTERNAL FUNCTIONS
;*
;*              NONE
;*
;* CHANGE ACTIVITY =
;*   DATE      FLAG        APAR   CHANGE DESCRIPTION
;*   --------  ----------  -----  --------------------------------------
;*   mm/dd/yy  @Vr.mpppxx  xxxxx  xxxxxxx
;*             @V2.0WCH01  56771
;*             @V2.0XXX01  PTM 1339
;*   06/08/95  121142      PJ18302  No longer rely on stack vars (StkKCBS &
;*             StkKCBO) for kcb addr -> these are being invalidated by
;*             devHelp calls &/or blocks/yields in certain situations
;*             (these values are obtained through a devHelp PhysToVirt;
;*             PhysToVirt values are no longer valid after another devHelp
;*             or a block/yield).
;*   UNKNOWN   J-DBCS003          DBCS enabling  (ss0004)          (S.Sakamoto)
;*
;*
;*   09/30/96  SM168075    PJ23304  Introduced a small delay (of max. 100ms)
;*             before calling DevHlp_MonFlush.  A talked to Allen Wynn and
;*             his opinion was that giving some time to monitor dispatcher
;*             to move some pakcets around will make room for our flush request.
;*
;**************************************************************************



.286p
.xcref
.sall
.xlist
  Include     basemaca.inc    ;* DOS macros.
  Include     osmaca.inc      ;* Added for DCR 652, Macro file for OS/2 kernel
  CPUMODE     286             ;*
  Include     devhlp.inc      ;* DevHlp & Signal equates, char queue structure.
  Include     struc.inc       ;* Structured assembly macros.
  Include     devsym.inc      ;* Device driver header definitions.
  Include     kbdseg.inc      ;* Segment definitions.
  Include     filemode.inc    ;* For monitor open/close checking.      (PTM 5627)
  Include     kbdddr.inc      ;* Keyboard Device Driver structures & equates.
.list

  StkFrameVars                ;* Macro to define the local
                              ;* stack variables.

;* External Routines
    Extrn    AccessKCB      :Near               ;now called by kbdflush ;121142
    Extrn    PurgeKIB       :Near
    Extrn    DiscardElement :Near
    Extrn    ThrowMeAway    :Near
    Extrn    KBDIOCTL       :Near
    Extrn    MonDispEntry   :Near
    Extrn    SetLocalVar    :Near
    Extrn    BlockCheck     :Near
    Extrn    UnBlockCheck   :Near
    Extrn    DD_Entry       :Far

;* External    Variables
    Extrn    KbdOpenReq     :Byte
    Extrn    MiscFlags      :Byte
    Extrn    MaxSG          :Word
    Extrn    PSGPointers    :Word
    Extrn    KeyPacket1     :Word                                       ;@IBM J-DBCS003(A)
    Extrn    DeviceHelp     :DWord
    Extrn    LEDSaveRestoreFlag :Word


Code Segment

  Assume  cs:Code,ds:DGROUP,es:NOTHING,ss:NOTHING

BREAK <KBDSTRA routine>

;/***************************************************************************
;*
;* FUNCTION NAME =  KBDSTRA
;*
;* DESCRIPTION   =  Keyboard Device Driver Strategy Routine
;*
;*                  Processes device driver function calls to the keyboard
;*                  device driver.
;*
;*
;* INPUT         =  RequestBlock - standard device driver request block as
;*                   (@ ES:BX)     specified in the CP/DOS functional spec.
;*
;* OUTPUT        =  Very heavily depends on individual function requested.
;*                  See sublogues for individual routines for details.
;*
;* RETURN-NORMAL =  "Request Complete" status in RequestBlock. Other
;*                  returned parms depend on function requested. See
;*                  sublogues of individual routines for details.
;*
;* RETURN-ERROR  =  Unrecognized Command for invalid function or category.
;*                  Char Call Interrupted for abnormal wake-up from block.
;*                  General Error if Mon Dispatcher gives error on Mon calls
;*
;*
;**************************************************************************/



Public KBDSTRA
KBDSTRA Proc Far

  EnterProc                            ;* Set up local storage.

  Or  ES:[BX+PktStatus],CmdComplete    ;* Set good completion status.
  Mov DL,ES:[BX+PktCmd]                ;* Get request number from request block.
 .If <DL lt KbdTblLen>                 ;* If a valid command
    Xor  AH, AH                        ;* Zero out sign bit for next call.
    Call SetLocalVar                   ;* Set up local stack frame
    .If <nc>                           ;* @V2.0WCH01
       Xor  DH,DH                      ;* Make the Request number an index.
       Mov  SI,DX                      ;* Put in index register.
       Shl  SI,1                       ;* Make request index an offset.
       Add  SI,Offset KbdCode:KbdTbl   ;* Point to processor address.
       Call CS:[SI]            ;* Go process request via KbdTbl jump table below.
    .Else                      ;* @V2.0WCH01  Carry flag is set, if a detached
       Or   ES:[BX+PktStatus],CmdError ;* @V2.0WCH01  process is detected
    .Endif                     ;* @V2.0WCH01  Indicate it is a command error

 .Elseif < DL eq 27 >                  ;* If the request is to initialize
    Xor  DH,DH                         ;* Make the Request number an index.
    Mov  SI,DX                         ;* Put in index register.
    Shl  SI,1                          ;* Make request index an offset.
    Add  SI,Offset KbdCode:KbdTbl      ;* Point to processor address.
    Call CS:[SI]              ;* Go process request via KbdTbl jump table below.

 .Else
    Or   ES:[BX+PktStatus],CmdError    ;* Indicate it is a command error
 .Endif

  LeaveProc                            ;* Free up local storage.
  Ret

KBDSTRA  Endp


  Break <*** Strategy Call Table ***>

   ;*
   ;** On entry to following routines, the following regs are set:
   ;**
   ;**    AX = SG that the caller is running in.
   ;** ES:BX = Ptr to Request Block
   ;**    DI = PSG address
   ;**
   ;**
   ;**
   ;**  NOTE:  If the process is running detached, then AX and DI may not be
   ;**         set to the indicated values.  The PSG and KCB stack values
   ;**         described below will also be undefined.
   ;**
   ;**  The local storage on the stack contains:
   ;**    - DD's data segment/selector.
   ;**    - Caller's PSG offset.
   ;**    - Selector to KIB/CPCB data area (not valid in real mode).
   ;**    - Caller's KCB physical address (doubleword).
   ;**    - Caller's Request Packet selector/segment.
   ;**    - Caller's Request Packet offset.
   ;**    - Caller's Process ID.
   ;*

 Even                  ;*  Put call table on word boundary.
KbdTbl:                ;*  Case table for keyboard device driver requests.
  dw     KbdCmdErr     ;*  0 = Loadable Device Drivers.    @V2.0XXX01
  dw     KbdCmdErr     ;*  1 = Media Check. Not supported.
  dw     KbdCmdErr     ;*  2 = Build BPB. Not supported.
  dw     KbdCmdErr     ;*  3 = Old IOCTL Input.  OBSOLETE
  dw     KbdCmdErr     ;*  4 = Input (Read keystrokes).  OBSOLETE
  dw     KbdCmdErr     ;*  5 = Non-Destructive read no wait (peek).  OBSOLETE
  dw     KbdStatusDone ;*  6 = Input Status.
  dw     KbdFlush      ;*  7 = Input Flush.
  dw     KbdCmdErr     ;*  8 = used to be Output (Write to keyboard input stream).
  dw     KbdCmdErr     ;*  9 = used to be Output with verify ( " " ").
  dw     KbdStatusDone ;*  10 = Ouput Status.
  dw     KbdStatusDone ;*  11 = Output Flush.
  dw     KbdCmdErr     ;*  12 = Old IOCTL Output.  OBSOLETE
  dw     KbdOpen       ;*  13 = Device Open (Kbd or monitor open).
  dw     KbdClose      ;*  14 = Device Close (Kbd or monitor close).
  dw     KbdCmdErr     ;*  15 = Removable Media. Not supported.
  dw     KbdIOCTL      ;*  16 = Generic IOCTL.
  dw     KbdCmdErr     ;*  17 = Reset uncertain media.  Not supported.
  dw     KbdCmdErr     ;*  18 = Get logical drive mapping.  Not supported.
  dw     KbdCmdErr     ;*  19 = Set logical drive mapping.  Not supported.
  dw     KbdCmdErr     ;*  20 = De-install device.  PTM 1699 only if no open han.
                       ;*  PTM 2810 Refuse Deinstall. @IBM
  dw     KbdCmdErr     ;*  21 = Undefined for KBD.  @V2.0XXX01 Move init to be
  dw     KbdCmdErr     ;*  22 = Undefined for KBD.  @V2.0XXX01. 27 instead of
  dw     KbdCmdErr     ;*  23 = Undefined for KBD.  @V2.0XXX01. 0 in order to
  dw     KbdCmdErr     ;*  24 = Undefined for KBD.  @V2.0XXX01. prevent error
  dw     KbdCmdErr     ;*  25 = Undefined for KBD.  @V2.0XXX01. of loading a
  dw     KbdCmdErr     ;*  26 = Undefined for KBD.  @V2.0XXX01. base device driver.
  dw     KbdInit       ;*  27 = Initialize.         @V2.0XXX01.
  dw     KbdCmdErr     ;*  27 = Undefined for KBD.  @V2.0XXX01. base device driver.
  dw     KbdCmdErr     ;*  28 = Undefined for KBD.  @V2.0XXX01. base device driver.
  dw     KbdCmdErr     ;*  29 = Undefined for KBD.  @V2.0XXX01. base device driver.
  dw     KbdCmdErr     ;*  30 = Undefined for KBD.  @V2.0XXX01. base device driver.
  dw     KbdCmdErr     ;*  31 = Undefined for KBD.  @V2.0XXX01. base device driver.
  dw     KbdSaveRestore;*  32 = Undefined for KBD.  @V2.0XXX01. base device driver.

KbdTblLen Equ ($-KbdTbl)/2-1;* Highest request number allowed.


BREAK <KbdFlush routine>


;/**************************************************************************
;*
;* FUNCTION NAME = KbdFlush
;*
;* DESCRIPTION   = Keyboard Input Flush request processor.
;*
;*                 Flushes keyboard input buffer and any associated
;*                 keystroke monitors for the requesting session.
;*
;* INPUT         = ES:BX = Strategy Request Packet.
;*                    DI = Offset of PSG to use.
;*
;*                 Upon entry, the stack's local storage contains various
;*                 selectors/offsets/misc. information.
;*
;* OUTPUT        = Buffer pointers are altered
;*
;*
;* RETURN-NORMAL = RequestBlock.Status = CompleteNoError
;*                 Keystroke buffer for requestor session is empty.
;*
;* RETURN-ERROR  = The "Unknown Command" code is returned in the request
;*                 packet if this command was issued while in Single
;*                 Queue mode.
;*
;**************************************************************************/


Public KbdFlush
KbdFlush Proc

  Test [DI].PSGFlags, SQMODE           ;* @IBM DCR 8: ;*  If in Single Queue
 .If < nz >                            ;*  Mode,
    LES BX, Dword Ptr StkRPO           ;* Restore request packet address.
    Mov ES:[BX+PktStatus],CmdError     ;* Indicate invalid parameter in RP
    Ret                                ;* Get Out Now.
 .Endif                                ;* End if in Single Queue mode.
                                       ;* DCR 408 Begin   @IBM
 .If <bit MiscFlags nz MLayerMode>     ;* If Multi Layer codepage supported
    Ret                                ;* NO KBDFLUSH MAY BE DONE, Don't
 .Endif                                ;* DCR 408 End @IBM

  Call BlockCheck                      ;* Go check if block required.
                                       ;* PTM 4480, 5841:   @IBM
  Test ES:[BX].PktStatus, STERR        ;* If error occurred during
 .If < nz >                            ;*  BlockCheck,
    Call UnBlockCheck                  ;* Free KIB serialization semaphore.
    Ret                                ;* Get Out Now.
 .Endif                                ;* End if BlockCheck error.

  Push 2222h                           ;* Var2 (Not used for this call)
  Push 1111h                           ;* Var1 (Not used for this call)
  Push DD_Cmd_Disable                  ;* Function
  Call DD_Entry                        ;* Device Independent Entry Point

  ;* DI -> Strategy caller's PSG.

  Or [DI].PSGFlags,Flushing            ;* Indicate now flushing.

; --  Do not rely on stack variables - get kcb address ourselves        ;121142
;121142  Mov DS, StkKCBS                      ;* Get selector to caller's KCB.
;121142  Mov SI, StkKCBO                      ;* Get offset to caller's KCB.
  Push DX                               ;save reg                       ;121142
  Mov DH,DS_SI                          ;retn kcb slctr/offset in ds/si ;121142
  CALL AccessKCB                        ;get  kcb address               ;121142
  Pop DX                                ;restore reg                    ;121142

                                       ;* DS:SI -> Strategy caller's KCB.

  ;*
  ;* If the caller is using a free list element (but its own KIB is not
  ;* full) then attempt to pass one element to the foreground session's
  ;* KIB.  The rest of the KIB for the caller's session is then purged.
  ;*

  Mov AX,[SI].KCBOutPtr                ;* Get pointer to front of queue.
  Or  AX,AX                            ;* Check if pointing to element zero.
 .If <nz> AND SHORT                    ;* Is it?
 .If <AX ne -1>                        ;* Or is the KIB empty?
    Mov  ES, StkCBData                 ;* Point to the segment containing the
                                       ;*  KIB.
    Push AX                            ;* Save the KIB index just obtained.
    Dec  AX                            ;* Element list first element has offset zero.
    IMul AX, KIBElLen                  ;* Calculate its offset in the list.
    Add  AX, FLISTOFFSET               ;* Add offset to first list element.
    Mov  DI,AX                         ;* Put index into an index reg.

                                       ;* ES:DI = KIB Element to discard.

    Mov  AX,ES:[DI].KIBRevPtr          ;* Get the pointer to the next oldest
                                       ;*  element.
    Mov  [SI].KCBOutPtr,AX             ;* Change the KCB's OutPtr to point to
                                       ;*  the next oldest element.
    Pop  AX                            ;* Restore the index of the element to
                                       ;*  be freed.
    Mov  SI, StkPSG                    ;* Restore the PSG offset.
    Mov  DS, StkDS                     ;* Point back to our data segment.

    Call DiscardElement                ;* Free an element from caller's KIB.
 .Endif                                ;* End if element needs to be freed.

  Mov DI, StkPSG                       ;* Restore the PSG offset.
  Mov DS, StkDS                        ;* Point back to our data segment.

  Lea BX,[DI].MonBlockID               ;* Get ID monitors Block ID that
                                       ;* may be blocked
  Mov  AX,DS                           ;*
  Mov  DL,DevHlp_ProcRun               ;* Set ProcRun DevHlp function.
  Call [DeviceHelp]                    ;* Go run the thread.

  Push AX                              ;* PTR B789221 Begin,@IBM ;* Save AX
  Xor  AX,AX                           ;*  Indicate to Access the KCB
  Call PurgeKIB                        ;*  Now purge KIB.
  Pop  AX                              ;* PTR B789221 End,@IBM ;* Restore AX
                                       ;*
 .If <[DI].MonsInstalled a 0>          ;* Are any monitors installed?

;SM168075                              ;The MonBlockID thread (awaken earlier)
    Push CX                            ;needs some time to run before we send
    Mov  CX,10                         ;the MonFlush request.  We will wait
MonFlushLoop:                          ;for 10ms before writing the MonFlush
    PUSHA                              ;request.  If an error occured in MonFlush,
    Mov  BX,1                          ;we retry for a max. of 10 times, altogether
    Mov  AX,CS                         ;allowing the monitor dispatcher 100 ms.
    Xor  DI,DI                         ;We don't need to check the ProcBlock
    Mov  CX,10                         ;ret. code because the sleep is uninterruptible
    Mov  DH,1                          ;and it will timeout anyway
    Mov  DL,DevHlp_ProcBlock
    Call [DeviceHelp]
    POPA
;SM168075
    Mov  AX,[DI].MonHandle       ;* Yes, get handle for this PSG's monitor chain.
    Mov  DL,DevHlp_MonFlush            ;* Set DevHlp function.
    Call [DeviceHelp]                  ;* Go flush.
ifdef DBCSKBD
   .If <c>                             ;* if DevHlp returns Error,      ;@IBM J-DBCS003(A)
      And [DI].PSGFlags,Not Flushing   ;* clear the flushing flag.      ;@IBM J-DBCS003(A)
   .Endif                              ;*                               ;@IBM J-DBCS003(A)
endif ;DBCSKBD
;SM168075
   .If <c>
       Loop  MonFlushLoop              ;Loop back if DevHlp_MonFlush error
   .Endif
    Pop  CX
   .If <c>                             ;If still error after 10 attempts, modify
       And [DI].PSGFlags,Not Flushing  ;PSG flags appropriately
   .Endif
;SM168075
 .Else                                 ;* Else no monitors installed.
    And  [DI].PSGFlags,Not Flushing    ;* So clear the flag.
 .Endif


  Push 2222h                           ;* Var2 (Not used for this call)
  Push 1111h                           ;* Var1 (Not used for this call)
  Push DD_Cmd_Enable                   ;* Function
  Call DD_Entry                        ;* Device Independent Entry Point
  LES  BX, Dword Ptr StkRPO            ;* Restore request packet address.
  Call UnBlockCheck                    ;* Go check if unblock required.
  Ret
KbdFlush Endp

BREAK <KbdCmdErr routine>


;/***************************************************************************
;*
;* FUNCTION NAME = KbdCmdErr
;*
;* DESCRIPTION   = Keyboard Command and General Error Routine
;*
;*                 Set and handle keyboard strategy error conditions
;*
;*
;* INPUT         = RequestBlock is passed.
;*
;* OUTPUT        = NONE
;*
;*
;* RETURN-NORMAL = RequestBlock.Status = GenErr or CmdErr
;*
;*
;* RETURN-ERROR  = NONE
;*
;*
;**************************************************************************/


Public KbdCmdErr
KbdCmdErr Proc

  Mov ES:[BX+PktStatus],CmdError   ;* Set Unknown Command error status.

  Ret
KbdCmdErr Endp

Public KbdStatusDone
KbdStatusDone Proc                 ;* Called when cmd requires no action.

  Ret
KbdStatusDone Endp

BREAK <KbdInit routine>

;/***************************************************************************
;*
;* FUNCTION NAME = KbdInit
;*
;* DESCRIPTION   = Keyboard Device Driver Initialization
;*
;*                 Initializes keyboard device hardware and internal data
;*                 structures for the keyboard device driver.
;*
;* INPUT         = RequestBlock is passed, only status is set.
;*
;*
;* OUTPUT        = Device Driver is initialized. PSG pointers and attendant
;*                 data areas are initialized.
;*
;* RETURN-NORMAL = RequestBlock.Status = CompleteNoError
;*
;*
;* RETURN-ERROR  = CS & DS offset in Req Block set to zero
;*
;*
;**************************************************************************/


Public KbdInit
KbdInit Proc

  Test MiscFlags, InitAttempted        ;* If we have NOT initialzed yet.
 .If <z>                               ;* Indicate that we have tried to
    Or  MiscFlags, InitAttempted       ;* initialize the PKBD DD for the
                                       ;* first time.
    Jmp ThrowMeAway                    ;* Go execute initialization.
 .Else                                 ;*
    LES BX, Dword Ptr StkRPO           ;* Restore request packet address.
    Mov ES:[BX+PktStatus],CmdError     ;* Indicate invalid parameter in RP
 .Endif
  Ret

KbdInit Endp

BREAK <KbdOpen routine>


;/***************************************************************************
;*
;* FUNCTION NAME = KbdOpen
;*
;* DESCRIPTION   = Open Kbd or Keystroke Monitor request processor
;*
;*                  Always returns successful status. Monitor chains are not
;*                  created until the first attempt to register one within
;*                  a screen group. See IOCTL Category 0Ah, Function 40.
;*
;*                  Keep a count of the number of open handles
;*                  so that the device driver can only DeInstall if there
;*                  is a handle count of 0.;@IBM PTM 1699 2iBM
;*
;*                  The keyboard device driver has been made.; PTM 2810  @IBM
;*                  non deinstallable.  Therefore the count of open handles
;*                  is no longer used to determine whether to deinstall.
;*
;* NOTE:        If the function is a MONITOR open (signified by a
;*              non-zero value in the error code nibble of the reques
;*              packet status field), then the values of the StkPSG,
;*              StkKCBS, and StkKCBO variables are undefined.
;*
;*
;* INPUT         = RequestBlock
;*
;* OUTPUT        = NONE
;*
;*
;* RETURN-NORMAL = RequestBlock.Status = CompleteNoError
;*
;*
;* RETURN-ERROR  = NONE
;*
;*
;**************************************************************************/


Public KbdOpen
KbdOpen Proc

  And ES:[BX].PktStatus, NOT open_monitor  ;* Clear monitor open indicator.
  Inc KbdOpenReq                           ;* Inc num of reqs for a kbd handle.
  Ret
KbdOpen Endp

BREAK <KbdClose routine>

;/***************************************************************************
;*
;* FUNCTION NAME =  KbdClose
;*
;* DESCRIPTION   =  Close Kbd or Keystroke Monitor request processor
;*
;*
;*                  If keyboard close, just returns completion status. If
;*                  monitor close, attempts to de-register all monitors in
;*                  the system with the caller's PID. In each case, if the
;*                  last monitor in a chain is de-registered then the chai
;*                  itself is deleted.
;*
;*   NOTE:        If the function is a MONITOR open (signified by a
;*                non-zero value in the error code nibble of the request
;*                packet status field), then the values of the StkPSG,
;*                StkKCBS, and StkKCBO variables are undefined.
;*
;*                Keep a count of the number of open handles
;*                so that the device driver can only DeInstall if there
;*                is a handle count of 0. ; PTM 1699 @IBM
;*
;*                The keyboard device driver has been made
;*                non deinstallable.  Therefore the count of open handles
;*                is no longer used to determine whether to deinstall.
;*                ; && PTM 2810 @IBM
;*
;* INPUT         =  RequestBlock, with Non-0 Status Field, if monitor close req.
;*
;*
;* OUTPUT        =  If monitor close, monitor flags and counters are updated.
;*
;*
;* RETURN-NORMAL =  RequestBlock.Status = CompleteNoError
;*                  If monitor close, monitor is deregistered.
;*
;* RETURN-ERROR  =  General Error if finds invalid monitor handle in a PSG.
;*
;*
;**************************************************************************/


Public KbdClose
KbdClose Proc

  Test ES:[BX].PktStatus, open_monitor       ;* Check monitor indicator.
 .If < nz >                                  ;* Non-zero means monitor close
    And ES:[BX].PktStatus,NOT open_monitor   ;* Clear monitor open
                                             ;* indicator.
    Xor CX,CX                                ;* Yes, so start with SG0.
   .Repeat                                   ;* Do following for all SGs.
      Mov DI,CX                              ;* Put PSG pointer index in index reg.
      Shl DI, 1                              ;* Make it an offset.
      Mov DI,[DI+PSGPointers]                ;* Get next PSG address.
     .If <[DI].MonHandle ne 0>               ;* Is there a monitor chain?
       .If <[DI].MonsInstalled ne 0>         ;* Yes, any monitors in this SG?
          Push BX                            ;* Save BX for now.
          Mov  BX, StkPID           ;* Put caller's PID where DevHlp expects it.
          Mov  AX,[DI].MonHandle             ;* Get the chain handle.
          Mov  DL,DevHlp_DeRegister          ;* Set DevHlp function.
          Call [DeviceHelp]                  ;* Go De-Register monitor.
          Pop  BX                            ;* Restore BX.
         .If <c>                             ;* Check if deregister succeded.
             LES BX, Dword Ptr StkRPO        ;* Restore the request packet address.
             Mov ES:[BX+PktStatus],GenError  ;* Set General command error status.
             Ret
         .Else                               ;* Else okay, so fix monitor count.
            Mov [DI].MonsInstalled,AL        ;* Do it.
         .Endif
       .Endif
       .If <[DI].MonsInstalled eq 0>         ;* Any monitors left in this SG?
          Push ES                            ;* No, so save ES for now.
          Push DS                            ;* Also DS.
          Push DI                            ;* Save DI also.
          Mov  AX,[DI].MonHandle             ;* Get handle of chain to delete.
          Mov  SI,DI                         ;* Move PSG pointer to SI.
          Add  SI,MonChainEnd           ;* Set end-of-chain bfr offset for this SG.
          Mov  DI,Offset KbdCode:MonDispEntry;* Set notify routine offset
          Push DS                            ;* Set segment for...
          Pop  ES                            ;* ..end-of-chain buffer.
          Push CS                            ;* Set segment for...
          Pop  DS                            ;* ...notify routine.
          Mov  DL,DevHlp_MonitorCreate       ;* Set DevHlp function.
          Cli                                ;* Don't allow ints while deleting.
          Call ES:[DeviceHelp]               ;* Go tear-down monitor chain.
          Pop  DI                            ;* Restore DI.
          Pop  DS                            ;* Restore DS.
          Pop  ES                            ;* Restore ES.
          Mov  [DI].MonHandle,0              ;* Zero my monitor handle.
          Sti                                ;* Ints okay now.
       .Endif
     .Endif
      Inc CX                                 ;* Process next PSG.
   .Until <CX eq MaxSG>                      ;* Repeat for each SG.
 .Endif
  Dec KbdOpenReq        ;* @IBM PTM 1699:
                        ;* Decrement the number of requests for a keyboard handle.
  Ret
KbdClose Endp

BREAK <KbdSaveRestore routine>

;/***************************************************************************
;*
;* FUNCTION NAME = KbdSaveRestore
;*
;* DESCRIPTION   = Keyboard Device Driver SaveRestore for Trapdoor
;*
;*                 Saves and restores the keyboards leds before and after
;*                 a trapdoor call.
;*
;* INPUT         = RequestBlock is passed, only status is set.
;*
;*
;* OUTPUT        = Device Driver's leds are either saved or restored
;*                 before or after a trapdoor depending on the specifics of
;*                 the command
;* RETURN-NORMAL = RequestBlock.Status = CompleteNoError
;*
;*
;* RETURN-ERROR  = CS & DS offset in Req Block set to zero
;*
;*
;**************************************************************************/


Public KbdSaveRestore
KbdSaveRestore Proc
         push    bx
         mov     al,es:[bx].SRFuncCode
         cmp     al,SRFC_Save
         jne     KBDRestore
         mov     DH,ES_DI               ;Tell PhysToVirt where to put it
         call   AccessKCB               ;Go get access to this SG's KCB
         mov     BX,DI                  ;Put KCB in BX
         mov     AX,ES:[BX].KCBShift    ;Put them in the KCB
         shr     AX,4                   ;Move LED bits to low nibble
         and     AX,7                   ;Turn off unwanted bits
         mov     LEDSaveRestoreFlag,ax
         jmp     KBDSaveRestoreReturn
KBDRestore:
         mov     ax,LEDSaveRestoreFlag
         push    2222h                  ;Var2 not used for this call
         push    AX                     ;Var1 (LED state)
         push    DD_Cmd_Indicators      ;Set the indicators
         call    DD_Entry               ;Device Independent Entry Point

KBDSaveRestoreReturn:
        Ret

KbdSaveRestore Endp
Code Ends
     End
