;*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 = SERIAL.ASM
;*
;* DESCRIPTIVE NAME = Serial Mouse specific code
;*
;*
;* VERSION      V2.0
;*
;* DATE         07/16/91
;*
;* DESCRIPTION  This file contains the code specific to serial
;*              mice.
;*
;*              The list of the currently known serial pointing
;*              devices that are detectible are listed below;
;*
;*              MS Serial Mouse
;*              MS BallPoint Mouse
;*              MousePen by Appoint
;*
;*              Other serial known serial pointing devices are
;*              listed below;
;*
;* NOTES         Autodetection not implemented for these
;*
;*               PC Mouse System Mouse
;*               Visi On Mouse
;*               Logitech Serial Mouse
;*               Kensignton Serial Mouse
;*
;* FUNCTIONS
;*
;*
;*
;*
;* STRUCTURES   NONE
;*
;* EXTERNAL REFERENCES
;*
;*              NONE
;*
;* EXTERNAL FUNCTIONS
;*
;*              NONE
;*
;* CHANGE ACTIVITY =
;*   DATE      FLAG        APAR   CHANGE DESCRIPTION
;*   --------  ----------  -----  --------------------------------------
;*   mm/dd/yy  @Vr.mpppxx  xxxxx  xxxxxxx
;*   04/04/93              53232  Add support for Logitech middle button
;**************************************************************************


.xlist
       include mouse.inc
;      include infoseg.inc
.list

.386p

;*
;*    External Mouse Module Data References
;*

       extrn DeviceData          : byte
       extrn actvirqbit          : word
       extrn gMEvent             : byte
       extrn LATLSBSave          : byte
       extrn LATMSBSave          : byte
       extrn LCRSave             : byte
       extrn MCRSave             : byte
       extrn IERSave             : byte
       extrn ABIOS_Mch           : byte


       extrn oddpts              : word
       extrn Int_Packet          : word
       extrn PortBase            : word
       extrn DevStatus           : word
       extrn ByteCount           : word
       extrn GDT_Seg             : word

       extrn Device_Help         : dword

       extrn SetupForWait        : near
       extrn IsWaitOver          : near

       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  CheckforMSSDevice
       public  TestforMSSDevice
       public  SaveCOMSetting
       public  RestoreCOMSetting
       public  SetupCOMForMouse
       public  ResetSerialMouse
       public  InitComPort


;/***************************************************************************
;*
;* FUNCTION NAME = CheckforMSSDevice
;*
;* DESCRIPTION   = This procedure will attempt to find a serial mouse
;*                 connected to the serial port at the given base I/O address
;*
;*
;* INPUT         =  NONE
;*
;* OUTPUT        =  carry clear     A serial mouse was found
;*                  AL              IRQ # for the mouse
;*                  BL              Bit mask giving interrupt level
;*                  DX              Base address of COM port
;*                  COM port is set up for mouse and mouse has been reset.
;*
;*                  carry set       No mouse was found
;*
;*                   ALTERS  AX, BX, CX
;*
;* RETURN-NORMAL =  NONE
;*
;*
;* RETURN-ERROR  =  NONE
;*
;* CALLS         =  TestforMSSDevice     CONTEXT = INIT.
;**************************************************************************/

CheckforMSSDevice  proc  near

        push es
        mov  cx, SHORTDELAY
cfmd_1:
        mov  DeviceData.ComNum, 0
        .repeat
           push 40h
           pop  es
           xor  bh, bh
           mov  bl, DeviceData.ComNum
           shl  bx, 1
           mov  dx, word ptr es:[bx]
           mov  PortBase, dx
           inc  DeviceData.ComNum
           .if <NONZERO dx>

              pusha
              push dx
              call _RM_MSE_AllocPorts
              or ax, ax
              pop dx
              popa
              jnz PortCOMClaimed

              call TestforMSSDevice
              jnc  cmd_exit

              pusha
              call _RM_MSE_DeallocPorts
              popa
PortCOMClaimed:
           .endif
        .until <DeviceData.ComNum eq 4>

;*
;* No response on normal BIOS com port addresses... now try others...
;*

        mov     si,offset oddpts
        mov     DeviceData.ComNum,255           ;255=non-standard port address
        mov     dx, word ptr [si]
        .repeat
           mov  PortBase, dx
           .if <NONZERO dx>
              pusha
              push dx
              call _RM_MSE_AllocPorts
              or ax, ax
              pop dx
              popa
              jnz  short PortMSSClaimed

              push si
              call TestforMSSDevice
              pop  si
              jnc  cmd_exit

              pusha
              call _RM_MSE_DeallocPorts
              popa
PortMSSClaimed:
           .endif
           add  si,2
         mov     dx, word ptr [si]
        .until <dx eq 0>

        .if <cx eq SHORTDELAY>
           mov  cx, LONGDELAY
           jmp  cfmd_1
        .endif

        mov  DeviceData.ComNum, 0
        stc

cmd_exit:
        pop  es
        ret

CheckforMSSDevice  endp

;/***************************************************************************
;*
;* FUNCTION NAME = TestforMSSDevice
;*
;* DESCRIPTION   = This procedure will attempt to find a serial mouse
;*                 connected to the serial port at the given base I/O address.
;*
;*
;*
;*
;*
;*
;*
;*
;* INPUT         = DX              Base address of COM port to be tested
;*                 CX              Delay time between power up and power down
;*
;* OUTPUT        = carry clear     A serial mouse was found
;*                 AL              IRQ # for the mouse
;*                 BL              Bit mask giving interrupt level
;*                 DX              Base address of COM port
;*                 COM port is set up for mouse and mouse has been reset.
;*
;*                 carry set       No mouse was found
;*
;*                 ALTERS  AX, BX, CX
;*
;* RETURN-NORMAL = NONE.
;*
;*
;* RETURN-ERROR  = NONE.
;*
;* CALLS         = SaveCOMSetting, SetupCOMForMouse, RestoreCOMSetting
;*                  ResetSerialMouse
;**************************************************************************/


TestforMSSDevice  proc  near

        push dx

        address IIR FROM RXB
        in   al,dx                      ; AL=contents of Interrupt ID reg.
        pop  dx

        .if <bit al z 0f8h>             ; Make sure bits 3-7 are all 0!

           call    SaveCOMSetting       ; Save the current setting
           call    SetupCOMForMouse     ; Set up COM port to talk to mouse.
           call    ResetSerialMouse     ; Reset mouse to see if it is there.
           .if <c>
              call RestoreCOMSetting    ; No Mouse! Restore COM port.
           .endif

        .else
           stc
        .endif
        ret


TestforMSSDevice  endp

;/***************************************************************************
;*
;* FUNCTION NAME = SaveCOMSetting
;*
;* DESCRIPTION   = This procedure will save the current state of the
;*                 COM port given.
;*
;* INPUT         = DX              Base address of COM port.
;*
;* OUTPUT        = NONE. ALTERS AX.
;*
;*
;* RETURN-NORMAL = NONE.
;*
;*
;* RETURN-ERROR  = NONE.
;*
;*
;**************************************************************************/


SaveCOMSetting  proc  near

        push dx                         ; Save base I/O address.
        address LCR FROM RXB            ; Get address of Line Control Register.
        DelayIn al,dx                   ; Get current contents.
        mov  LCRSave, al                ; Save them.
        or   al, LC_DLAB                ; Set up to access divisor latches.
        DelayOut dx,al
        address LATMSB FROM LCR         ; Get address of high word of divisor
        DelayIn al,dx                   ;  latch and save its current contents.
        mov LATMSBSave, al
        address LATLSB FROM LATMSB      ; Get address of low word of divisor
        DelayIn al,dx                   ;  latch and save its current contents.
        mov  LATLSBSave, al
        address LCR FROM LATLSB         ; Get address of Line Control Register
        mov  al, LCRSave                 ;  and disable access to divisor.
        and  al, NOT LC_DLAB
        DelayOut dx,al
        address MCR FROM LCR            ; Get address of Modem Control Register
        DelayIn al,dx                   ;  and save its current contents.
        mov  MCRSave, al
        address IER FROM MCR            ; Get address of Interrupt Enable Reg-
        DelayIn al,dx                   ;  ister and save its current contents.
        mov  IERSave, al
        pop  dx                         ; Restore base I/O address.
        ret

SaveCOMSetting  endp

;/***************************************************************************
;*
;* FUNCTION NAME = RestoreCOMSetting
;*
;* DESCRIPTION   = This procedure will restore the state of the COM port
;*
;* INPUT         = DX              Base address of COM port.
;*
;* OUTPUT        = NONE.  ALTERS AX.
;*
;*
;* RETURN-NORMAL = NONE
;*
;*
;* RETURN-ERROR  = NONE
;*
;*
;**************************************************************************/


RestoreCOMSetting  proc  near

        push    dx                      ; Save base I/O address.
        address LCR FROM RXB            ; Get address of Line Control Register.
        mov     al,LC_DLAB              ; Set up to access divisor latches.
        DelayOut dx,al
        address LATMSB FROM LCR         ; Get address of high word of divisor
        mov     al, LATMSBSave          ;  and restore it.
        DelayOut dx,al
        address LATLSB FROM LATMSB      ; Get address of low word of divisor
        mov     al, LATLSBSave          ;  and restore it.
        DelayOut dx,al
        address LCR FROM LATLSB         ; Get address of Line Control Register
        mov     al, LCRSave             ;  and restore it, disabling access to
        and     al,NOT LC_DLAB          ;  the divisor latches.
        DelayOut dx,al
        address MCR FROM LCR            ; Get addres of Modem Control Register
        mov     al, MCRSave             ;  and restore it.
        DelayOut dx,al
        address IER FROM MCR            ; Get address of Interrupt Enable Reg-
        mov     al, IERSave             ;  ister and restore it.
        DelayOut dx,al
        pop     dx                      ; Restore base I/O address.
        ret

RestoreCOMSetting  endp

;/***************************************************************************
;*
;* FUNCTION NAME = SetupCOMForMouse
;*
;* DESCRIPTION   = This procedure will set up the given COM port so that
;*                 it can talk to a serial mouse.
;*
;* INPUT         = DX              Base address of COM port to set up
;*
;* OUTPUT        = COM port set up, all interrupts disabled at COM port
;*
;* RETURN-NORMAL = NONE
;*
;*
;* RETURN-ERROR  = NONE
;*
;*
;**************************************************************************/

SetupCOMForMouse  proc  near

        push dx                         ; Save base I/O address.
        address LCR FROM RXB            ; Get address of Line Control Reg.
        mov  al, LC_DLAB                ; Set up to access divisor latches.
        DelayOut dx,al
        address LATMSB FROM LCR         ; Get address of high word of divisor
        mov  al, HIGH DIV_1200          ;  latch and set it with value for
        DelayOut dx,al                  ;  1200 baud.
        address LATLSB FROM LATMSB      ; Get address of low word of divisor
        mov  al, LOW DIV_1200           ;  latch and set it with value for
        DelayOut dx,al                  ;  1200 baud.
        address LCR FROM LATLSB         ; Get address of Line Control Reg.
        mov  al, LC_BITS7+LC_STOP1+LC_PNONE ;7 data bits, 1 stop bit, no parity
        DelayOut dx,al                  ; Set up data format.
        address IER FROM LCR            ; Get address of Int. Enable Register
        xor  al, al                     ; Disable all interrupts at the COM
        DelayOut dx,al                  ;  port level.
        address LSR FROM IER            ; Get address of Line Status Reg.
        DelayIn al,dx                   ; Read it to clear any errors.
        pop  dx                         ; Restore base I/O address
        ret

SetupCOMForMouse  endp

;/***************************************************************************
;*
;* FUNCTION NAME = ResetSerialMouse
;*
;* DESCRIPTION   = This procedure will reset a serial mouse on the given COM
;*                 port and will return an indication of whether a mouse
;*                 responded or not
;*
;* INPUT         = ENTRY   DX              Base I/O address of COM port to use
;*                         CX              Number of msecs to use for delays
;*
;* OUTPUT        = EXIT    carry clear     Mouse responded and is now reset
;*
;*                         carry set       No mouse responded!
;*
;* NOTES         = This routine assumes that all interrupts at the COM port
;*                 have been disabled
;*
;* RETURN-NORMAL = NONE
;*
;*
;* RETURN-ERROR  = NONE
;*
;*
;**************************************************************************/


ResetSerialMouse  proc  near

        push dx
        push cx

        address MCR FROM RXB            ; Get address of Modem Control Reg.
        mov     al,MC_DTR               ; Set DTR active; RTS, OUT1, and OUT2
        DelayOut dx,al                  ;  inactive. This powers down mouse.

        push    cx                      ; Save amount of time to delay.
        call    SetupForWait            ; Setup BX:CX & ES:DI for delay

;*
;* Now, we wait the specified amount of time, throwing away any stray data
;* data that we receive. This gives the mouse time to properly reset itself.
;*

        address RXB FROM MCR            ; Get address of Receive Buffer.
        .repeat
           DelayIn al,dx                ; Read and ignore any stray data.
           call    IsWaitOver           ; Determine if we've delayed enough.
        .until <c>

        address LSR FROM RXB            ; Get address of Line Status Reg.
        DelayIn al,dx                   ; Read it to clear any errors.
        address MCR FROM LSR            ; Get address of Modem Control Reg.
        mov  al, MC_DTR+MC_RTS+MC_OUT2  ; DTR,RTS,OUT2 active. OUT1 inactive.
        DelayOut dx,al                  ; This powers up the mouse.

        pop  cx                         ; Get amount of time to delay.
        call SetupForWait               ; Setup BX:CX & ES:DI for delay

;*
;* We give the mouse the specified amount of time to respond by sending us
;* an M or a B for a Ballpoint device. If it doesn't, or we get more than
;* 3 characters that aren't an M or a B, we return a failure indication.
;*

        address LSR FROM MCR            ; Get address of Line Status Reg.
        mov     si,3                    ; Read up to 3 chars from port.

rsm_lookforMorB:
        .repeat
           DelayIn al,dx                ; Get current status.
           test    al,LS_DR             ; Is there a character in Receive Buff?
           jnz     short rsm_gotchar    ; Yes! Go and read it.
           call    IsWaitOver           ; No, determine if we've timed out.
        .until <c>                      ; Haven't timed out; keep looking.
        jmp  short rsm_leave            ; Timed out. Leave with carry set.

rsm_gotchar:
        address RXB FROM LSR            ; Get address of Receive Buffer.
        DelayIn al,dx                   ; Get character that was sent to us.
        cmp     al, 'M'                 ; Is it an M?
        je      short rsm_lookfor3but   ; Yes! The mouse must be out there. ;           
        cmp     al, 'B'                 ; Was it a B?
        je      short rsm_leave         ; Yes, we have a ballpoint.
        address LSR FROM RXB            ; Oh well. Get address of LSR again.
        dec     si                      ; Have we read 3 chars yet?
        jnz     short rsm_lookforMorB   ; Nope, we'll give him another try.
        stc                             ; Didn't find an M. Indicate failure.
        jmp     short rsm_leave         ; No mouse, let exit.

rsm_lookfor3but:                        ;           
        mov     cx,SHORTDELAY           ; Get amount of time to delay.
        call    SetupForWait            ; Setup BX:CX & ES:DI for delay

        address LSR FROM RXB            ; Get address of Line Status Reg.
        .repeat
           DelayIn al,dx                ; Get current status.
           test    al,LS_DR             ; Is there a character in Receive Buff?
           jnz     short rsm_tst3but    ; Yes! Go and read it.
           call    IsWaitOver           ; No, determine if we've timed out.
        .until <c>                      ; Haven't timed out; keep looking.

        clc                             ; clear carry since we found MS mouse
        jmp  short rsm_leave            ; Timed out. Leave now

rsm_tst3but:
        address RXB FROM LSR            ; Get address of Receive Buffer.
        DelayIn al,dx                   ; Get character that was sent to us.

        .if <al eq '3'>                 ; Is three button mouse attached?
           mov DeviceData.NumButt, 3    ; Indicate three button mouse
        .endif

        clc                             ; clear carry since we found MS mouse
                                        ;           
rsm_leave:
        pop  cx
        pop  dx
        ret

ResetSerialMouse  endp

; Interrupt Entry Points for MSS_Find_Int
intjtb  proc    far
COPT1   equ     $
tint00: call    minint
COPT2   equ     $
tint01: call    minint
tint02: call    minint
tint03: call    minint
tint04: call    minint
tint05: call    minint
tint06: call    minint
tint07: call    minint
tint08: call    minint
tint09: call    minint
tint10: call    minint
tint11: call    minint
tint12: call    minint
tint13: call    minint
tint14: call    minint
tint15: call    minint
intjtb  endp

minint  proc    near
        pop     ax                      ;ax=ret offset for IRQ determination
        mov     bx,COPT2-COPT1
        sub     ax,offset tint01
        idiv    bl
        xor     ah,ah
        mov     si,ax
        inc     actvirqbit[si]
        stc                             ;don't claim the interrupt
        db      0cbh                    ; far RET
minint  endp

;/***************************************************************************
;*
;* FUNCTION NAME = MSS_Find_Int
;*
;* DESCRIPTION   = Find out what IRQ is selected for 8250
;*
;* INPUT         = BX = base port of chip
;*                 DS = our DS
;*
;* OUTPUT        =  NONE
;*
;*
;* RETURN-NORMAL =  NC - BH=IRQ level
;*                       AL=8259 mask byte
;*                       AH=2nd 8259 mask byte
;* RETURN-ERROR  =  C - multiple or no interrupt level found
;*
;**************************************************************************/

        public  MSS_Find_Int
MSS_Find_Int proc  near
        push    bx
        mov     ax,offset tint01
        mov     bx,1
        mov     dx,01bh                 ; non-shared & SetIRQ

finnx:  call    Device_Help             ; invoke Dev Help
        add     ax,COPT2-COPT1
        inc     bx
        cmp     bx,16
        jne     finnx
        pop     bx                      ; get base port back

        mov     dx,bx
        add     dx,3
        in      al,dx
        and     al,7Fh                  ;DLAB=0
        out     dx,al
        sub     dx,2
        mov     al,2
        out     dx,al                   ; turn on THRE interrupts
        add     dx,3
        mov     al,12                   ; turn on Out1 & Out2 for IRQs
                                        ; RTS & DTR are off so that mouse is
                                        ; dead; this prevents interrupts from
                                        ; stuff other than what we're doing.
        out     dx,al
        jmp     $+2
        jmp     $+2
        mov     dx,bx                   ; get back to THR
        out     dx,al
        jmp     $+2
        jmp     $+2
        jmp     $+2
        jmp     $+2
        jmp     $+2
        jmp     $+2
        out     dx,al                   ; fill THR after THR has gone to TSR
        mov     cx,0FFFFh
spinr:  loop    spinr

        add     dx,1                    ; get to IER
        mov     al,0
        out     dx,al                   ; turn off THRE interrupts
        add     dx,3
        mov     al,0Fh                  ; turn on OUT2, OUT1, RTS & DTR
        out     dx,al
        jmp     $+2
        jmp     $+2


        mov     bx,1
        mov     dx,01ch                 ; release the IRQ

finn2:  call    Device_Help             ; invoke Dev Help
        inc     bx
        cmp     bx,16
        jne     finn2
        mov     si,offset actvirqbit    ;get bitmap of triggered ints

;*
;* SI now has the starting offset to an array of interrupt counts... we want
;* to find which one had exactly 2 INTs (since we generated 2).
;*

        xor     bx,bx

fsornx: cmp     byte ptr [si],2
        jz      fndint
        inc     bx
        inc     si
        cmp     bx,16
        jne short fsornx
multint:stc                             ;opps! multiple or no interrupts!
        ret

fndint: mov     ax,1
        push    cx
        mov     cl,bl
        mov     bh,bl
        shl     ax,cl
        pop     cx
        clc
        ret


MSS_Find_Int endp
;/***************************************************************************
;*
;* FUNCTION NAME = InitComPort
;*
;* DESCRIPTION   = Enable mouse IRQ level at the 8259.
;*
;*
;* INPUT         = AX = function code
;*                 DS = our DS
;*                 ES = callers DS
;*
;* OUTPUT        = none (Stack is clean on return.  AX, CX, DX, SI, and DI
;*                      registers are changed.)
;*
;* RETURN-NORMAL = Always, mouse IRQ enabled.
;*
;*
;* RETURN-ERROR  = n/a
;*
;*
;**************************************************************************/

InitComPort  proc  near

        cli
        mov  dx, PortBase               ; get base port

;*
;* Set Data avail in INT Enable Reg
;*

        address IER FROM RXB
        mov  al, IERMASK
        delayout dx, al
;*
;* Read stray data
;*

        address RXB FROM IER
        xor  cx, cx                     ; Loop for a while

joesloop:
        delayin al, dx
        loop joesloop

        sti                             ; allow ints now

;*
;* We must zero out the BIOS Date Area for the Communications Line
;* Port Base address.
;*                     40:0 for Com Port 1
;*                     40:2 for Com Port 2
;*                     40:4 for Com Port 3
;*                     40:6 for Com Port 4
;*

        mov  bl, DeviceData.ComNum
        cmp  bl, 255                    ; nonstandard port address?
        je   inidun
        dec  bl
        xor  bh, bh
        shl  bx, 1
        push 40h
        pop  es
        mov  word ptr es:[bx], 0

        .if <ABIOS_Mch eq TRUE>
           mov  al, 06h                  ; Async Device ID
           mov  bl, DeviceData.ComNum    ; Get 1st LID
           xor  dh, dh                   ; reserved, must be 0
           mov  dl, DevHlp_GetLIDEntry   ; ABIOS function
           call Device_Help              ; invoke Dev Help
        .endif

inidun: ret

InitComPort  endp

CSEG     ENDS
         END
