;*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 = INPORT.ASM
;*
;* DESCRIPTIVE NAME = MS Inport Mouse specific code
;*
;*
;* VERSION      V2.0
;*
;* DATE         10/13/91
;*
;* DESCRIPTION  MS Inport Mouse specific code
;*
;* FUNCTIONS    CheckforINPDevice  -to find an InPort mouse in the system.
;*              TestForInport      -to find an InPort mouse at the given
;*                                  base I/O address
;*              FindInportInterrupt-
;*              InportInit         -called at the end of the initialization
;*
;*
;* NOTES         This file contains the code specific to the MS
;*               Inport mouse.
;*
;*
;*
;* STRUCTURES   NONE
;*
;* EXTERNAL REFERENCES
;*
;*              NONE
;*
;* EXTERNAL FUNCTIONS
;*
;*              NONE
;*
;* CHANGE ACTIVITY =
;*   DATE      FLAG        APAR   CHANGE DESCRIPTION
;*   --------  ----------  -----  --------------------------------------
;*   mm/dd/yy  @Vr.mpppxx  xxxxx  xxxxxxx
;**************************************************************************


.xlist
       include mouse.inc
.list

.386p

;*
;*   External Mouse Module Data References
;*

       extrn DeviceData          : byte
       extrn InpVersion          : byte

       extrn DevStatus           : word
       extrn ByteCount           : word
       extrn InportAddr          : word
       extrn Int_Packet          : word

       extrn Device_Help         : dword

       extrn GIDC_Entry          : far

       extrn _RM_MSE_AllocPorts    : far
       extrn _RM_MSE_DeallocPorts  : far


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

;*
;*    Module Procs made Public for other Mouse Modules
;*

       public  CheckforINPDevice
       public  TestForInport
       public  FindInportInterrupt
       public  InportInit

;*****************************************************************************
;*
;* FUNCTION NAME  : CheckforINPDevice
;*
;* DESCRIPTION    : This procedure will try to find an InPort mouse
;*                  in the system.
;*
;*
;* EXIT           : Carry clear     No errors were encountered
;*                  NZ              No inport was found
;*
;*                  ZR              An inport was found
;*                  AL              IRQ inport is jumpered at
;*                  BL              Bit mask for disabling interrupts
;*                  BH              Interrupt rate
;*                  DX              I/O address of 8259 to use
;*                  SI              Base address of inport
;*                  DI              Offset of InportRelocTable
;*                  InpVersion      Version/revision of inport
;*
;*                  Carry set       An error occurred
;*                  NZ              Multiple interrupts were found
;*                  ZR              No interrupts were found
;*
;* EFFECTS        : ALTERS  AX, BX, CX, DX, SI
;*
;* CALLS          : TestForInport
;*
;*****************************************************************************

CheckforINPDevice  proc  near

        mov  dx,INP_FIRST_PORT+INP_ID    ; Get address of ID register.

        pusha
        push es
        push dx
        call _RM_MSE_AllocPorts
        or ax, ax
        pop dx
        pop es
        popa
        jnz short PortINPClaimed

        call TestForInport               ; Does an InPort exist at this address?
        jc   short fi_leave              ; Yes, but it has either no interrupts
                                         ;  or multiple interrupts.
        jz   short fi_success            ; Yes, and it has only 1 interrupt!!
        mov  dx, INP_LAST_PORT+INP_ID

        call TestForInport
        jc   short fi_leave              ; Yes, but it has either no interrupts
        jz   short fi_success            ; Yes, and it has only 1 interrupt!!
        stc
        jmp  fi_leave                    ; Return showing no inport found.

fi_success:                              ; An InPort was found!!!
        mov  InportAddr, si
        jmp  short fi_exit

fi_leave:                                ; Flags set properly.
        pusha
        push es
        call _RM_MSE_DeallocPorts
        pop es
        popa

PortINPClaimed:
        stc

fi_exit:
        ret




CheckforINPDevice  endp

;******************************************************************************
;*
;* FUNCTION NAME  : TestForInport
;*
;* DESCRIPTION    : This procedure will attempt to find an InPort mouse at the
;*                  given baseI/O address. Note that if an InPort is found, it
;*                  will be left in a state where it will not be generating any
;*                  interrupts.
;* INPUT          :  DX      I/O address of InPort Identification Register
;*
;* OUTPUT         :  NC NZ   No InPort was found
;*                   DX      I/O address of InPort Identification Register
;*
;*                   NC ZR    An Inport was found
;*                   AL       Interrupt InPort is jumpered on
;*                   BL       Bit mask for this interrupt
;*                   SI       Base address of InPort
;*                   DX       I/O address for mask register of 8259A to use.
;*                   InpVersion   Version/revision of inport
;*
;*                   CY NZ     Multiple interrupts were found
;*                   SI        Base address of InPort
;*
;*                   CY ZR     No interrupts were found
;*                   SI        Base address of InPort
;*
;* EFFECTS        :  ALTERS  AX, BX, CX, DX, SI
;*
;* CALLS          :  FindInportInterrupt
;*
;*****************************************************************************

TestForInport   PROC    NEAR

;*
;* Since the identification register alternates between returning back
;* the Inport chip signature and the version/revision, if we have an
;* InPort chip, the chip signature will be read in one of the following
;* two reads. If it isn't, we do not have an InPort chip.
;*

     in      al,dx                   ; Read ID register.
     cmp     al, INPORT_ID           ; Is value the InPort chip signature?
     je      short tfi_possible_inport ; Yes, go make sure we have an InPort.
     in      al,dx                   ; Read ID register again.
     cmp     al, INPORT_ID           ; Is value the InPort chip signature?
     jne     short tfi_not_found     ; No, return error
;*
;* At this point, we managed to read the InPort chip signature, so we have
;* a possible InPort chip. The next read from the ID register will return
;* the version/revision. We then make sure that the ID register alternates
;* between the chip signature and this version/revision. If it does, we have
;* an InPort chip.
;*

tfi_possible_inport:
        in      al,dx                ; Read version/revision.
        mov     ah,al                ; Save it.
        mov     cx,5                 ; Test ID register 5 times.

tfi_inport_check:
        in      al,dx                ; Read ID register.
        cmp     al, INPORT_ID        ; Make sure it is the chip signature.
        jne     short tfi_not_found  ; If not, we don't have an InPort chip.
        in      al,dx                ; Read ID register.
        cmp     al,ah                ; Make sure version/revision is same.
        jne     short tfi_not_found  ; If not, we don't have an InPort chip.
        loop    tfi_inport_check     ; Test desired number of times.

;*
;* At this point, we know we have an InPort chip. We now try to determine
;* what interrupt level it is jumpered at.
;*
        mov     InpVersion, ah        ; Save version/revision of InPort.
        address INP_ADDR FROM INP_ID  ; Get base address of InPort.
        mov     si,dx                 ; Parm for FindInportInterrupt.
        mov     dx,MASTER_MASK_ADDR   ; Get address of 1st 8259A's mask reg.
        mov     bl,INP_MASTER_LEVELS  ; Test for IRQs on master.
        call    FindInportInterrupt   ; Find interrupt level.
        jnc     short tfi_leave       ; If no error or multiple ints found,
        jnz     short tfi_leave       ;  the return conditions are set.
        mov     dx,SLAVE_MASK_ADDR    ; Get address of 2nd 8259A's mask reg.
        mov     bl,INP_SLAVE_LEVELS   ; Test for IRQs on slave.
        call    FindInportInterrupt   ; Find interrupt level.
        jc      short tfi_leave       ; If error, return conditions are set.
        add     al,8                  ; Adjust IRQ level because on slave.
        xor     cl,cl                 ; Set ZR NC.

tfi_leave:
        ret

tfi_not_found:                        ; We don't have an InPort chip.
        clc                           ; Show failure.
        ret                           ; Return to caller.

TestForInport   ENDP

;*****************************************************************************
;*
;* FUNCTION NAME  : FindInportInterrupt
;*
;* DESCRIPTION    : This procedure determines if the interrupt level that the
;*                  InPort is jumpered at is in one of the given positions on
;*                  the given 8259A.
;*
;* INPUT          : SI   Base address of InPort
;*                  DX   I/O address of mask register in 8259A to check
;*                  BL   Bit mask giving interrupt levels to check
;*
;* OUTPUT         : BL       Bit mask giving InPort interrupt level(s)
;*
;*                  carry clear     Only 1 interrupt level was found
;*                  ZR
;*                  AL              IRQ # of this interrupt level
;*
;*                  carry set       Some error occurred
;*                  NZ              Multiple interrupts were found
;*                  ZR              No interrupts were found
;*
;* EFFECTS       : ALTERS  AH, CX
;*
;* CALLS         : none
;****************************************************************************

FindInportInterrupt     PROC    NEAR

        in      al,dx                   ; Get current mask value.
        MyIODelay                       ; Generate delay between I/O instructions
        push    ax                      ; Save it.
        or      al,bl                   ; Mask off those interrupt levels that
        out     dx,al                   ;  we wish to check.
        dec     dx                      ; Get address of control port.
        xchg    si,dx                   ; Set up to access InPort.
        mov     al,INP_RESET            ; Reset the InPort and select the mode
        out     dx,al                   ;  register.
        mov     al,INP_MODE_REG         ;  ...
        out     dx,al                   ;  ...
        address INP_DATA FROM INP_ADDR  ; Get address of data register.

;*
;* We now toggle the InPort's interrupt output between 0 and 1 and
;* determine which (if any) of the interrupt levels are following it
;* by reading the IRR of the 8259A.
;*
;* Throughout this loop, AH will hold those interrupt levels that can
;* still belong to the InPort. At the top of the loop, DX has the
;* address of the InPort's data register and SI has the address of
;* the 8259A's control port.
;*
;* Note that we select the IRR each time through the loop while interrupts
;* are disabled in case an interrupt comes in and the 8259A is set up to
;* access the ISR during the interrupt routine.
;*

        mov     ah,bl                   ; All desired levels are still possible
        mov     cx,10                   ; Number of times to test.

fii_check_int_loop:
        mov     al,TIMER_INT_ENAB + HZ0INTR0 ; Generate a 0 on InPort's line.
        out     dx,al
        xchg    si,dx                   ; Get address of control port.
        cli                             ; Start of critical section
        mov     al,SELECT_IRR           ; Select the IRR.
        out     dx,al                   ; ...
        MyIODelay                       ; Generate delay between I/O instructions
        in      al,dx                   ; Read current contents of IRR.
        MyIODelay                       ; Generate delay between I/O instructions
        and     al,bl                   ; Keep only levels we are testing.
        xor     al,bl                   ; Only keep those levels that are
        and     ah,al                   ;  currently 0.
        xchg    si,dx                   ; Get address of data register.
        mov     al,TIMER_INT_ENAB + HZ0INTR1 ; Generate a 1 on InPort's line.
        out     dx,al
        xchg    si,dx                   ; Get address of control port.
        in      al,dx                   ; Read current contents of IRR.
        MyIOdelay
        sti                             ; End of critical section.
        and     al,bl                   ; Keep only levels we are testing.
        and     ah,al                   ; Only keep levels that are 1.
        xchg    si,dx                   ; Restore DX and SI for top of loop.
        loopnz  fii_check_int_loop      ; Continue while some levels remain.

        mov     bl,ah                   ; Save interrupt mask for return.
;*
;* We select the ISR in the 8259A (I'm not exactly sure why we seem to have
;* the assumption that that's the way it was) and restore the mask register.
;*

        mov     al,HZ0INTR1             ; Disable inport.
        out     dx,al                   ; ...

        address INP_ADDR FROM INP_DATA  ; Restore InPort base address.
        xchg    si,dx                   ; Get address of control port.
        mov     al,SELECT_ISR           ; Reselect the ISR.
        out     dx,al                   ; ...
        MyIOdelay
        pop     ax                      ; Get saved mask value.
        inc     dx                      ; Address 8259A mask register.
        out     dx,al                   ; Restore mask register.
        MyIOdelay
;*
;* At this point, AH has the bit(s) that followed us around. We now
;* determine the IRQ number of the interrupt as well as if multiple
;* levels were found.
;*

        mov     ah,bl                   ; Get back saved interrupt mask.
        or      ah,ah                   ; Leave with error if there are no
        jz      short fii_no_ints_found ;  possible interrupts.
        xor     al,al                   ; AL will end up with IRQ number.

fii_find_irq_number:
        shr     ah,1                    ; Test next bit in the mask.
        jc      short fii_found_irq_number ; If set, we have found the IRQ number.
        inc     ax                      ; Try next IRQ number.
        jmp     short fii_find_irq_number

fii_found_irq_number:
        jnz     short fii_found_multiple_ints ; If AH is still non-zero, we have
                                        ;  multiple interrupts responding!
        clc                             ; Indicate success.
        ret

fii_no_ints_found:
fii_found_multiple_ints:                ; Zero flag set properly already.
        stc                             ; Show failure.
        ret

FindInportInterrupt     ENDP

;*****************************************************************************
;*
;* FUNCTION NAME  : InportInit
;*
;* DESCRIPTION    : This procedure is called at the end of the initialization
;*                  process in order to finish the initialization of the
;*                  inport mouse. It has to perform whatever actions that are
;*                  necessary in order to put the inport mouse into the state it
;*                  needs to be in.
;*
;* INPUT          : [InportAddr]    Base I/O address of the InPort.
;*
;* OUTPUT         : Carry clear     Init was successful.
;*
;* EFFECTS        : ALTERS  AX, DX
;*
;* CALLS          : none
;*
;****************************************************************************

InportInit      PROC    NEAR

        mov     dx, InportAddr          ; Get base I/O address of InPort.
        mov     al,INP_MODE_REG         ; Get the value that selects the mode
        out     dx,al                   ;  register and select it.
        address INP_DATA FROM INP_ADDR  ; Get address of data register.
        mov     al,HZ50 + DATA_INT_ENAB ; Set up inport at 50 Hz.
        out     dx,al                   ; ...
        clc                             ; Show init successful.
        ret

InportInit      ENDP


CSEG     ENDS
         END
