;*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.;
;*****************************************************************************/
;/*************************************************************************
;*
;* SOURCE FILE NAME = UTIL1.ASM
;*
;* DESCRIPTIVE NAME = Mouse nonswappable utility routines.
;*
;*
;* VERSION      V2.0
;*
;* DATE         08/13/91
;*
;* DESCRIPTION  This file contains mouse utility routines.
;*              These routines are called at interrupt time and
;*              must always reside in the low fixed non-swappable
;*              segment (CSEG). These routines might be called by
;*              routines in a swappable segment. If so they must
;*              be declated HYBRID. Meaning they are NEAR to any
;*              caller in CSEG and FAR to any caller in a swappable
;*              segment.
;*
;*              See linker control file for location in link list.
;*
;* FUNCTIONS    CollisionChk   Find if the pointer is in the collision area.
;*              Calc_Num       Calculate (AX * BX + (CX / 2)) / CX and
;*                              return in AX.
;*              QueueWrite     Write an event record to the session
;*                              even queue.
;*              HookHotPlugPDI ReInitialize the PDI mouse after a hot
;*                              plug of the physical mouse or a APM
;*                              Power saving event.
;*              Update_Ptr     Update the pointer position.
;*
;* STRUCTURES   NONE
;*
;* EXTERNAL REFERENCES
;*
;*              NONE
;*
;* EXTERNAL FUNCTIONS
;*
;*              NONE
;*
;* CHANGE ACTIVITY =
;*   DATE      FLAG        APAR   CHANGE DESCRIPTION
;*   --------  ----------  -----  --------------------------------------
;*   04/21/93              67424  Loose mouse when coming back from suspend
;*                                mode on Dell 325nc laptop.
;*   06/03/93              69671  Lose pointing stick when coming back from
;*                                suspend mode on IBM 700 series laptops.
;*   03/14/94              79119  Add rounding to calc_num which performs
;*                                the math for mapping of mouse pointer
;*                                coordinates when switching between modes.
;*   05/06/94  83195       83195  Add call to ClearAuxPort to APMResume to
;*                                make sure Output Buffer is empty before
;*                                attempting to write to port 64h.
;**************************************************************************


.xlist
        include mouse.inc
        include basemaca.inc
        include osmaca.inc
.list

CPUMODE 386

;*
;* External symbols used in this module
;*

       extrn  DDD              : byte
       extrn  APMDDName        : byte                ;           
       extrn  APMDD            : byte                ;           
       extrn  APMIDCReg        : byte                ;           
       extrn  FgndSessn        : byte
       extrn  DeviceData       : byte                ;           
       extrn  TranslateMode    : byte                ;           
       extrn  SynchFlag        : word                ;           
       extrn  APMIDCHandle     : word                ;           

       extrn  Eq_Length        : word

       extrn  Device_Help      : dword

       extrn  CheckforPDIDevice    : near
       extrn  WriteInputBuffer     : near
       extrn  SendToMouse          : near
       extrn  GetMouseData         : near
       extrn  ClearAuxPort         : near            ;83195

CSEG    SEGMENT   WORD  PUBLIC  USE16 'CODE'
        ASSUME    CS:CSEG, SS:NOTHING, ES:NOTHING, DS:NOTHING

;*
;* Publics defined by this module
;*

       public  QueueWrite
       public  HookHotPlugPDI
       public  RegisterForAPM                        ;           
       public  APMResume
       public  WriteControlRegister


;**********************************************************************
;*
;*
;* FUNCTION NAME : CollisionChk
;*
;* DESCRIPTION   : Determine if the pointer is in the collision area.
;*
;* INPUT         :  None.
;*
;* OUTPUT        : AX=0 if no colision, <>0 if there is a collison.
;*
;* SIDE EFFECTS  : All other registers preserved.
;*
;**********************************************************************
;* PSEUDOCODE :
;*
;*BeginSub  CollisionChk  (check pointer collision area)
;*
;* IF <pointer is in collision area>
;*    set error code
;* ELSE
;*    set no error code
;* ENDIF
;* return
;*
;*EndSub  CollisionChk
;*
;***********************************************************************

Procedure CollisionChk, HYBRID

        push    cx                      ; Save Reg input value
        mov     ax, 1                   ; Preset collision return code
        test    [si].area_flags, 1      ; Check collision area defined
        .if     z       or              ; If No collision area defined

        mov     cx, [si].ptr_row_pos    ; Calculate the Ptr's
        sub     cx, [si].ptr_rowhot     ; Upper row coordinate
        .if     <cx g [si].area_Bot> or ; If Ptr below collision area

        add     cx,[si].ptr_height      ; Calculate the Ptr's bottom row
        dec     cx                      ; coordinate, account for 1
        .if     <cx l [si].area_Top> or ; If Ptr above collision area

        mov     cx,[si].ptr_col_pos     ; Calculate the Ptr's
        sub     cx,[si].ptr_colhot      ; Left column coordinate
        .if     <cx g [si].area_Right> or ; If Ptr right of "C" area

        add     cx,[si].ptr_width       ; Calculate the Ptr's right col
        dec     cx                      ; coordinate, account for 1
        .if     <cx l [si].Area_Left>   ; If Ptr left of "C" area
            dec     ax                  ; Clear the error indicator
        .endif                          ; Ptr Collision Tests
        pop     cx                      ; Restore Reg input value

        ret                             ; Return to caller

Endproc CollisionChk

;**********************************************************************
;*
;* FUNCTION NAME : Calc_Num
;*
;* DESCRIPTION   : Calculate (AX * BX + (CX / 2)) / CX and return in AX.
;*
;* INPUT         : AX, BX, CX set to desired values.
;*
;* OUTPUT        : AX=result, flags set accordingly.
;*
;* SIDE EFFECTS  : flags changed, AX has result, others preserved.
;*
;**********************************************************************
;* PSEUDOCODE :
;*
;* BeginSub  Calc_Num
;*
;*  calculate (AX * BX + (CX / 2)) / CX
;*  return calculated value
;*
;* EndSub  Calc_Num
;*
;**********************************************************************

Procedure  Calc_Num,  HYBRID
         push dx              ; Preserve DX Reg value
         push si              ; Preserve SI                               
         mov  si, cx          ; Copy                                      
         shr  si, 1           ; Divide by 2                               
         xor  dx, dx          ; Clear DX
         imul bx              ; Multiply 1st & 2nd values
         add  ax, si          ; Add CX/2 to AX (low word)                 
         adc  dx, 0           ; Add CX/2 to AX (high word)                
         idiv cx              ; Divide (1*2) by third value
         pop  si              ; Restore SI                                
         pop  dx              ; Return only AX changed = ans
         ret                  ; Return to caller
Endproc  Calc_Num


;**********************************************************************
;*
;*  FUNCTION NAME :  Update_Ptr
;*
;*  DESCRIPTION   :  Update the pointer position.
;*
;*  DESCRIPTION   :  This routine is used to update the pointer
;*                   position for protect mode.  Input is new pointer
;*                   position.  First the image is removed then the
;*                   new image is displayed.  If either operation is
;*                   in the collision area then that operation is
;*                   ignored.
;*
;*  ENTRY POINT   :  Update_Ptr
;*     LINKAGE    :   CALL NEAR
;*
;*  INPUT         :  CX = new column position, DX = new row position.
;*
;*  EXIT-NORMAL   :  Always.
;*
;*  EXIT-ERROR    :  None.
;*
;*  EFFECTS       :  Pointer image location updated.
;*
;*  INTERNAL REFERENCES:
;*     ROUTINES:  CollisionChk
;*
;*  EXTERNAL REFERENCES:
;*     ROUTINES:  Ptr Draw(DrawPointer, RemovePointer)
;*     DevHlps :   NONE.
;*
;**********************************************************************
;* PSEUDOCODE :
;*
;* BeginSub  Update_Ptr  (update pointer position on the screen)
;*
;* IF <pointer position has moved>
;*    IF <Ptr draw routines are used>
;*       IF <new ptr pos is not in a collision area>
;*          set flag for NOT an unconditional pointer removal
;*       ELSE   /* ptr is going into a no draw area
;*          set flag for UNCONDITIONAL pointer removal
;*       ENDIF
;*       call Ptr Draw(RemovePointer) using removal flag just set
;*       IF <flag indicated not an unconditional remove>
;*          call Ptr Draw (DrawPointer) to draw image at new location
;*       ENDIF
;*    ENDIF
;* ENDIF
;* return
;*
;* EndSub  Update_Ptr
;*
;***********************************************************************

Procedure  Update_Ptr,  HYBRID

       .if <[si].Ptr_Col_Pos ne cx> NEAR OR   ; If ptr image has moved
       .if <[si].Ptr_Row_Pos ne dx> NEAR      ; Then

          push cx                       ; Save new Ptr col pos
          push dx                       ; Save new Ptr row pos

          test  [si].D_Status, PtrDraw  ; see if ptr draw is to be called
          .if <z>                       ; If so then continue

             push [si].Ptr_Col_Pos      ; Save current col pos
             push [si].Ptr_Row_Pos      ; Save current row pos

             mov  [si].Ptr_Col_Pos, cx  ; Update SG CB with new
             mov  [si].Ptr_Row_Pos, dx  ; Event's Ptr Position

             call CollisionChk          ; check new ptr pos for visibility
             .if <ax eq 0>              ; if visible then
                mov  cx, 1              ; a draw pointer call is coming
             .else                      ; else new pos is hidden so
                xor  cx, cx             ; Remove Ptr Only call
             .endif                     ; New Ptr pos Collision Test

             pop  [si].Ptr_Row_Pos      ; Restore current row pos
             pop  [si].Ptr_Col_Pos      ; Restore current col pos

             mov  al, fs:FgndSessn      ; Load Foreground SG ID
             RemovePointer              ; Remove old ptr pos image
          .endif                        ; Ptr Draw Rtn Support Test

          pop  [si].Ptr_Row_Pos         ; new ptr row pos from stack
          pop  [si].Ptr_Col_Pos         ; new ptr col pos from stack

          test [si].D_Status, PtrDraw
          .if <z>         AND           ; If Ptr Draw Rtns Used
          .if <cl ne 0>                 ; And Draw expected then
             mov  al, fs:FgndSessn      ; Load Foreground SG ID
             DrawPointer                ; Draw ptr image at new pos
          .endif                        ; Ptr Draw Rtn Support Test
       .endif                           ; System Pointer Draw Tests
       ret                              ; return to caller

Endproc  Update_Ptr

;**********************************************************************
;*
;*  FUNCTION NAME :  QueueWrite
;*
;*  DESCRIPTION   :  Write an event record to the session
;*                     event queue.
;*
;*  DESCRIPTION   :  This function is called to write an event record
;*                   to the session's event queue.  It maintains the
;*                   the event queue head and tail pointers.  The
;*                   queue is maintained as a FIFO circular queue. If
;*                   the queue is full then any new events overwrite
;*                   the oldest event in the queue.
;*
;*  ENTRY POINT   :  QueueWrite
;*     LINKAGE    :  CALL NEAR
;*
;*  INPUT         :  Stack frame as follows:
;*                   SS:[SP]   --> Return address (IP)
;*                   SS:[SP]+2 --> Return address (CS)
;*                   SS:[SP]+4 --> offset of event record
;*                   SS:[SP]+6 --> selector of event record
;*
;*  EXIT-NORMAL   :  Always
;*
;*  EXIT-ERROR    :  N/A
;*
;*  EFFECTS       :  Queue is updated along with queue pointers.
;*                   Stack is cleaned and no registers modified.
;*
;*
;**********************************************************************
;*  PSEUDOCODE :
;*
;*BeginSub  QueueWrite
;*
;*   move event record to tail of event queue
;*   IF <Queue is full>
;*      HeadPtr <- HeadPtr + RecordSize
;*   ELSE
;*      QueueSize <- QueueSize + 1
;*   ENDIF
;*
;*   TailPtr <- TailPtr + RecordSize
;*   IF <TailPtr is at end of queue memory>
;*      TailPtr <- Start of queue memory
;*   ENDIF
;*
;*   IF <HeadPtr is at end of queue memory>
;*      HeadPtr <- Start of queue memory
;*   ENDIF
;*
;*   IF <a process is blocked waiting on mouse events>
;*      call DeviceHelp(ProcRun) to run the blocked process
;*   ENDIF
;*
;*   return
;*
;*EndSub  QueueWrite
;*
;**********************************************************************

QueueWrite  proc  far

;*
;* These equates are used to access the stack frame.
;*

EventOffset   equ     <(word ptr [bp+6])>
EventSelector equ     <(word ptr [bp+8])>

       enter 0,0                    ; resever no local space

;*
;*      The event is pointed to by the parameters on the stack.  Set up to
;*      do a move string to the next element in the queue.
;*

       push di                      ; save di
       push si                      ; save si

       push ds                      ; put our ds in
       pop  es                      ; es for data move
       mov  di, [si].Eq_Tail        ; get tail pointer
       push ds                      ; save ds
       mov  ds, EventSelector       ; and put in ds for data move
       mov  si, EventOffset         ; get offset of event record
       mov  cx, ElRec_Size          ; get count of bytes in record
       cld                          ; go forward
       rep  movsb                   ; write record into queue
       pop  ds                      ; restore ds
       pop  si                      ; restore si
       pop  di                      ; restore di

       mov  dx, ElRec_Size          ; Get # of bytes per que element
       mov  cx, dx                  ; save size of queue element
       mov  ax, fs:Eq_Length        ; Get total # of bytes per queue
       idiv dl                      ; Calc # of que elements
       mov  dl, al                  ; Save calc'd # of elements

       .if <dl eq [si].EQ_Size>     ; if queue is full
          add  [si].Eq_Head, cx     ; by adding size of queue record
       .else                        ; otherwise
          inc  [si].Eq_Size         ; increment the size of the queue
       .endif                       ; end full queue test

       add  [si].Eq_Tail, cx        ; update tail pointer

       mov  cx, [si].E_Queue        ; get start of queue memory
       add  cx, fs:Eq_Length        ; get end of queue memory

       .if <[si].Eq_Head eq cx>     ; if head is at end of queue memory
          mov  ax, [si].E_Queue     ; get start of queue memory
          mov  [si].Eq_Head, ax     ; and reset head to start of queue
       .endif                       ; end head wrap test

       .if <[si].Eq_Tail eq cx>     ; if Tail is at end of queue memory
          mov  ax, [si].E_Queue     ; get start of queue memory
          mov  [si].Eq_Tail, ax     ; and reset tail to start of queue
       .endif                       ; end tail wrap test

       test [si].D_Status, Block_Mask  ; Check Blocked Bit
       .if nz                       ; If Thread(s) awaiting data and

         mov  bx, ds                ; Event queue address low word
         mov  ax, [si].E_Queue      ; Event queue address high word
         mov  dl, DevHlp_ProcRun    ; Specify request function number
         call fs:Device_Help        ; Invoke Run Function
       .endif                       ; Blocked Thread test

       leave
       ret 4                        ; pop 4 bytes of parameter data

QueueWrite  endp

;**********************************************************************
;*
;*  FUNCTION NAME :  HookHotPlugPDI
;*
;*  DESCRIPTION   :   ReInitialize the PDI mouse after a hot
;*                    plug of the physical mouse or a APM
;*                    Power saving event.
;*
;*  DESCRIPTION   :  This routine is called to initialize the mouse
;*                   hardware attached to the PS/2 or PDI port.
;*
;*  INPUT         :  NONE
;*
;*  OUTPUT        :  SynchFlag is reset to indicate PDI support has completed
;*                :  carry is clear because failure should not be passed
;*                :  AX register MUST be 0
;*
;*  SIDE EFFECTS  :  stack is clean.
;*
;**********************************************************************
;* PSEUDOCODE :
;*
;* BeginSub  HookHotPlugPDI
;*
;* call CheckforPDIDevice to reset the mouse
;* return
;*
;*EndSub  InitFSCB
;*
;**********************************************************************

HookHotPlugPDI  proc  far

        pusha
        push es                          ;           
        call CheckforPDIDevice           ; Reload the PDI Mouse support
        and  SynchFlag, HOTPLUG          ; Clear all HotPlug bits
        pop  es                          ;           
        popa

        clc                              ;           
        mov  ax, 0
        ret

HookHotPlugPDI  endp

;**********************************************************************
;*
;*  FUNCTION NAME :  APMResume                         ;           
;*
;*  DESCRIPTION   :  This routine is registered with the APM.SYS device
;*                   driver at INIT_COMPLETE time if the system is APM
;*                   aware.  The function is to reset the pointing device
;*                   when an APM RESUME event is processed.
;*
;*  INPUT         :  NONE
;*
;*  OUTPUT        :  The pointing device is reset to default configuration.
;*                :  carry is clear because failure should not be passed
;*                :  AX register MUST be 0
;*
;*  SIDE EFFECTS  :  stack is clean.
;*
;**********************************************************************
APMResume proc far

        pushad
        push es

        mov  al, 60h                   ; Tell 8042 that we want to write the
        call WriteControlRegister      ; controller command byte from (60h).
        jc  apm_exit

;**************************************************************************
;* The bit definitions for the controller command byte on any system that
;* has an AUX port are listed below;
;*
;*      Bit   Function
;*
;*  0  7 -  Reserved (Always set to 0)
;*  1  6 -  IBM Keyboard Translate Mode
;*  1  5 -  Disable AUX Device  (0 = Enabled state, 1 = disabled state)
;*  1  4 -  Disable Keyboard    (0 = Enabled state, 1 = disabled state)
;*  0  3 -  Reserved (Always set to 0)
;*  1  2 -  System Flag (Always set to 1)
;*  0  1 -  Enable Auxiliary Interrupt (0 = No Aux Ints, 1 = AUX ints enabled)
;*  0  0 -  Enable Keyboard Interrupt  (0 = No Kbd Ints, 1 = Kbd ints enabled)
;*
;**************************************************************************

        mov  al, 34h           ; Write the command byte to controller
        or   al, TranslateMode ; Set Keyboard Translate bit of port 60h
        call WriteInputBuffer  ; Send it to 8042 port (60h).
                               ;    (DISABLE KBD & AUX)
        jc   apm_exit

        call ClearAuxPort      ; Empty AuxOutputBuffer in prep for write ;83195

        mov  al, 0e9h          ; status request (will generate 4 bytes port 60h)
        call SendToMouse       ; send command to mouse
        jc   apm_error

        call GetMouseData      ; Verify SendToMouse worked
        jc   apm_error

        cmp  al, 0fah
        jc   apm_error

        call GetMouseData      ; Get first status byte from port 60h
        jc   apm_error

        test al, 10000b        ; Is scaling factor 2:1 bit set?
        .if <z>
          or    SynchFlag, APM_RESETMOUSE_ON
        .endif

        call GetMouseData      ; Get current resolution setting
        jc   apm_error

        cmp  al, PDI_RESOLUTION ; Is resolution 8/mm?
        .if <nz>
          or    SynchFlag, APM_RESETMOUSE_ON
        .endif

        call GetMouseData      ; Get current sample rate
        jc   apm_error

        cmp  al, PDI_SAMPLERATE ; Is sample rate 60/second?
        .if <nz>
          or    SynchFlag, APM_RESETMOUSE_ON
        .endif

        test  SynchFlag, APM_RESETMOUSE_ON
        .if <nz>
          call  CheckforPDIDevice
        .endif

apm_error:
        mov  al, 60h               ; Tell 8042 that we want to write the
        call WriteControlRegister  ; controller command byte to port 60h.
        jc  apm_exit

        mov  al, 07h
        or   al, TranslateMode     ; set KeyboardTranslate bit of port 60h
        call WriteInputBuffer      ; Send it to 8042 port (60h).
                                   ;    (ENABLE KBD & AUX)

apm_exit:
        and  SynchFlag, APM_RESETMOUSE_CLEAR
        pop  es
        popad

        clc
        mov  ax, 0
        ret

APMResume  endp

;**********************************************************************
;*
;*  FUNCTION NAME :  RegisterForAPM                
;*
;*  DESCRIPTION   :  Register as an Advanced Power Management client
;*                   so that when a pointing device is repowered after
;*                   a power saving event, we can reinitialilze it.
;*
;*  DESCRIPTION   :  This routine is to register our mouse hardware
;*                   initialization routine for mice attached to the
;*                   PS/2, (PDI) port.
;*
;*  INPUT         :  NONE
;*
;*  OUTPUT        :  NO_ERROR
;*                :  carry is clear
;*                :  AX register MUST be 0
;*
;*  SIDE EFFECTS  :  stack is cleaned up in INITCOMPLETE routine
;*
;**********************************************************************
;* PSEUDOCODE :
;*
;* BeginSub  RegisterForAPM
;*
;* IF (The mouse type is a PDI mouse)
;*   Attach APMDD IDC
;*   IF (Attach to APMDD IDC routine is SUCCESSFUL)
;*     Register our PDI mouse reset routine run at RESUME time
;*     IF (Register worked)
;*       save handle
;*       indicate APM active via SynchFlag bit
;*     ENDIF
;*   ENDIF
;* ENDIF
;*
;* return
;*
;*EndSub  InitFSCB
;*
;**********************************************************************

RegisterForAPM  proc  far

       pushad
       push ds                   ; Save Mouse Data Segment
       push es                   ; Save Request Selector

       .if<DeviceData.MouseType eq PDI_DEVICE> ; Is this a PDI mouse ?
          mov  bx, offset APMDDName     ; get offset to APM name
          mov  di, offset APMDD         ; offset to put Attach addresses
          mov  dl, DevHlp_AttachDD      ; DevHelp function
          call Device_Help              ; do the function

          .if <nc>                      ; if the AttachDD worked
             mov  APMIDCReg.REG_Function, APMIDC_Register
             mov  APMIDCReg.REG_hClient, 0
             mov  APMIDCReg.REG_EventHandler_off, OFFSET APMResume
             mov  APMIDCReg.REG_EventHandler_sel, cs
                                          ; Need both masks because some BIOS'
                                          ; pass Critical and some pass Normal
             mov  APMIDCReg.REG_NotifyMask, APMMASK_NormalResume + APMMASK_CriticalResume
             mov  APMIDCReg.REG_ClientDS, ds
             mov  APMIDCReg.REG_DeviceId, APM_DeviceId ; BIOS controlled device
             lea  bx, APMIDCReg
             push ds
             pop  es                    ; es:bx -> APM registration packet
             call ds:APMDD.ProtEntry    ; go do the call

             .if <nc>
                mov  ax, APMIDCReg.REG_hClient
                mov  APMIDCHandle, ax   ; save IDC Handle
                or   SynchFlag, APM_IDCACTIVE ; Indicate IDC enabled
             .endif

          .endif                        ; end AttachDD worked test
       .endif                           ; end APM support only for PDI

       clc                              ; set no error flag

       pop  es
       pop  ds
       popad
       ret                              ; return to INITCOMPLETE routine

RegisterForAPM  endp                    ;           

;****************************************************************
;*
;*  FUNCTION NAME :  WriteControlRegister
;*
;*  DESCRIPTION   : This procedure will send a command byte to the
;*                  8042's control register (64h).
;*
;*  NOTE: Nothing will be sent as long as the 8042's input or
;*        output buffer is full.
;*
;*        Only 64K attempts will be made before timing out.
;*
;*   INPUT        :  AL - Command byte to be sent.
;*
;*   OUTPUT       :  Carry clear - Write successful.
;*                   Carry set   - Write unsuccessful.
;*
;*   ALTERS       :  CX register
;*
;****************************************************************

WriteControlRegister  Proc  Near

        push ax                      ; Save callers value
        xor  cx,cx                   ; Loop counter

wcr_loop:
        in   al, 64h                 ; Get 8042's current status.
        test al, 03h                 ; Are input and output buffers empty?
        jz   wcr_ok                  ; Yes, Go write the command byte.
        loop wcr_loop                ; the desired number of times.
        pop  ax                      ; Callers value is on top of the stack
        stc                          ; Show that we failed.
        jmp  wcr_exit

wcr_ok:
        pop  ax                      ; Get callers value back
        out  64h, al                 ; Write command byte out.
        clc                          ; Clear carry for successful operation

wcr_exit:
        ret

WriteControlRegister  endp


CSEG   ENDS
       END
