;       SCCSID = @(#)atioctl.asm        6.6 91/11/11
; ***************************************************************************
; *
; *                       IBM/Microsoft Confidential
; *
; *                 Copyright (c) IBM Corporation  1987, 1990
; *                 Copyright (c) Microsoft Corp.  1987, 1990
; *                           All Rights Reserved
; *
; ***************************************************************************

        PAGE    80,132
        .286p

        TITLE   com01.sys - Asynchronous Communication Device Driver
        NAME com01

;       Bryan Diehl
;       David Gilman

;***    atioctl.asm - IOCTL Routines
;
;       Support routines for generic IOCTLs.
;
;       LoadGIODataPack - Put a byte or word into the IOCTL data packet
;       GetGIOParaPack  - Get a byte or word from the IOCTL parameter packet
;       VerifyAddr      - Verify an IOCTL packet pointers
;       VerifyNull      - Verify a NULL IOCTL packet pointer
;       ComIoctl        - IOCTL dispatcher
;
;       Modification History
;
;       BD      01/18/87        Re-written to conform to MS standard for
;                               style, clarity and efficiency.
;                               New queue structure.
;       JGT     05/10/88        Add enhanced baud rate support
;       YN      05/25/89        MVDM Support - @VDM
;       YN      10/06/89        PTR 707056 - @@1
;       ACW     04/16/91        @PVW Added perfview counters/timers
;       YN      08/13/91        CP20D1390 - Bulletin Board Support
;       WDM     04/21/94        82548 - pvwxport.inc now included in atcom.inc
;       PMS     07/27/94        89343 - Only call CheckAPO when FIFO flags change
;       JAG     12/17/96        173636 - Compare MSR shadow with MSR to determine
;                               182253   if interupt is needed.
;

.xlist
include devhlp.inc
include devsym.inc
include basemaca.inc
include realmac.inc
include ioctl.inc
include seldesc.inc
include atcom.inc               ; local include
include atesp.inc
.list

        extrn   DevHlp:dword
        extrn   ComInit:near
        extrn   CheckTX:near
        extrn   CmxDone:near
        extrn   CmxGenFail:near
        extrn   CmxInvalidParameter:near
        extrn   CmxInUse:near
        extrn   CmxMonitor:near
        extrn   CmxUnknown:near
        extrn   DisableRemoteTX:near
        extrn   EnableRemoteTXXO:near
        extrn   Get_OCI_Sem:near
        extrn   CheckLCR:near
        extrn   ComputeAPO:near
        extrn   ComputeHHS:near
        extrn   ComputeRTO:near
        extrn   ComputeWTO:near
        extrn   EnableRemoteTX:near
        extrn   MxInt:near
        extrn   SetLineC:near
        extrn   SetBaud:near


DSEG SEGMENT

;* IoctlTab - dispatch table for IOCTLs
;
        EVEN
        PUBLIC  IoctlTab
IoctlTab LABEL WORD
        DW      OFFSET ioc_bad          ; 40
        DW      OFFSET ioc_sbaud        ; 41 set baud rate
        DW      OFFSET ioc_slinec       ; 42 set line control
        DW      OFFSET ioc_sbaudenh     ; 43 set extended bit baud
        DW      OFFSET ioc_tximmed      ; 44 trans immed
        DW      OFFSET ioc_break0       ; 45 break off
        DW      OFFSET ioc_smodemc      ; 46 set modem control
        DW      OFFSET ioc_rxoff        ; 47 as if XOFF received
        DW      OFFSET ioc_rxon         ; 48 as if XON  received
        DW      OFFSET ioc_bad          ; 49
        DW      OFFSET ioc_bad          ; 4A
        DW      OFFSET ioc_break1       ; 4B break on
        DW      OFFSET ioc_bad          ; 4C
        DW      OFFSET ioc_bad          ; 4D
        DW      OFFSET ioc_bad          ; 4E
        DW      OFFSET ioc_bad          ; 4F
        DW      OFFSET ioc_bad          ; 50
        DW      OFFSET ioc_bad          ; 51
        DW      OFFSET ioc_bad          ; 52
        DW      OFFSET ioc_sdcb         ; 53 set device control block
        DW      OFFSET ioc_senh         ; 54 set enhanced parms
        DW      OFFSET ioc_bad          ; 55
        DW      OFFSET ioc_bad          ; 56
        DW      OFFSET ioc_bad          ; 57
        DW      OFFSET ioc_bad          ; 58
        DW      OFFSET ioc_bad          ; 59
        DW      OFFSET ioc_bad          ; 5A
        DW      OFFSET ioc_bad          ; 5B
        DW      OFFSET ioc_bad          ; 5C
        DW      OFFSET ioc_bad          ; 5D
        DW      OFFSET ioc_bad          ; 5E
        DW      OFFSET ioc_bad          ; 5F
        DW      OFFSET ioc_bad          ; 60
        DW      OFFSET ioc_gbaud        ; 61 get baud rate
        DW      OFFSET ioc_glinec       ; 62 get line control
        DW      OFFSET ioc_gbaudenh     ; 63 get baud enhanced
        DW      OFFSET ioc_gcomstat     ; 64 get com status
        DW      OFFSET ioc_gtxstat      ; 65 get tx status
        DW      OFFSET ioc_gmodemc      ; 66 get modem control output signals
        DW      OFFSET ioc_gmodems      ; 67 get modem control input  signals
        DW      OFFSET ioc_gistat       ; 68 input queue status
        DW      OFFSET ioc_gostat       ; 69 output queue status
        DW      OFFSET ioc_bad          ; 6A
        DW      OFFSET ioc_bad          ; 6B
        DW      OFFSET ioc_bad          ; 6C
        DW      OFFSET ioc_gcomerr      ; 6D get com error word
        DW      OFFSET ioc_bad          ; 6E
        DW      OFFSET ioc_bad          ; 6F
        DW      OFFSET ioc_bad          ; 70
        DW      OFFSET ioc_bad          ; 71
        DW      OFFSET ioc_gevent       ; 72 get comm event word
        DW      OFFSET ioc_gdcb         ; 73 get device control block
        DW      OFFSET ioc_genh         ; 74 get enhanced parms
MAXIOCTL = (($ - IoctlTab)/2) - 1

DSEG    ENDS


CSEG    SEGMENT
        ASSUME cs:CSEG,ds:DSEG,es:NOTHING,ss:NOTHING

;**     LoadGIODataPackB - load a byte into the data packet
;**     LoadGIODataPackW - load a word into the data packet
;
;       ENTRY   (es:di) -> request packet
;               al      =  byte to put in GIODataPack
;               ax      =  word to put in GIODataPack
;
;       EXIT
;
;       USES    cx es di
;
;       WARNING will not return if IOCTL data packet pointer is bad.
;               See VerifyAddrD
;
;       NOTE    IOCTL packet pointers are virtual addresses.

Procedure LoadGIODataPackB,NEAR
        ASSUME cs:CSEG,ds:DSEG,es:NOTHING,ss:NOTHING

        ChkRPPtr

        mov     cx,1                    ; byte
        call    VerifyAddrD             ; verify data address
        SaveReg         <es,di>
        les     di,es:[di].GIODataPack  ; (ds:si) -> IOCTL data packet
        stosb                           ; Put al into the data packet
        RestoreReg      <di,es>
        ASSUME  es:NOTHING
        ret

Entry LoadGIODataPackW

        ChkRPPtr

        mov     cx,2                    ; word
        call    VerifyAddrD             ; verify data address
        SaveReg         <es,di>
        les     di,es:[di].GIODataPack  ; (ds:si) -> IOCTL data packet
        stosw                           ; Put ax into the data packet
        RestoreReg      <di,es>
        ASSUME  es:NOTHING
        ret

EndProc LoadGIODataPackB


;**     GetGIOParaPackB - get a byte from the parameter packet
;**     GetGIOParaPackW - get a word from the parameter packet
;
;       ENTRY   (es:di) -> request packet
;
;       EXIT    al      =  byte from GIOParaPack
;               ax      =  word from GIOParaPack
;
;       USES    cx es di
;
;       WARNING will not return if IOCTL parameter packet pointer is bad.
;               See VerifyAddrP
;
;       NOTE    IOCTL packet pointers are virtual addresses.

Procedure GetGIOParaPackB,NEAR
        ASSUME cs:CSEG,ds:DSEG,es:NOTHING,ss:NOTHING

        mov     cx,1
        jmp     short GetGIOx


Entry GetGIOParaPackW
        mov     cx,2

GetGIOx:
        ChkRPPtr

        call    VerifyAddrP             ; verify parameter address, cx = size

        SaveReg         <es,di>

        les     di,es:[di].GIOParaPack  ; (ds:si) -> IOCTL paramaters
        cmp     cx,1
        je      GetPack10               ; load a byte

        mov     ax,es:[di]              ; load a word
        jmp     short GetPack99

GetPack10:
        mov     al,es:[di]              ; load a byte

GetPack99:
        RestoreReg      <di,es>

        ret

EndProc GetGIOParaPackB


;**     VerifyAddrP - Verify IOCTL Parameter pointer
;**     VerifyAddrD - Verify IOCTL Data      pointer
;
;       ENTRY   (es:di) -> request packet
;               cx         length to verify
;
;       EXIT    will not return if invalid address
;
;       USES    none
;
;       WARNING cx == 0 => 65536
;       WARNING will not return if IOCTL parameter/data packet pointer is bad
;               jumps indirectly to CmxGenFail (general failure)
;               process will terminate when it returns from the kernel

Procedure VerifyAddrD,NEAR
        ASSUME cs:CSEG,ds:DSEG,es:NOTHING,ss:NOTHING

        push    ax
        push    dx
        push    di
        mov     ax,es:[di].GIODataPack._hi
        mov     di,es:[di].GIODataPack._lo
        mov     dx,(0100h OR DevHlp_VerifyAccess)       ; read/write
        jmp     short VerifyAddr10

Entry VerifyAddrP
        push    ax
        push    dx
        push    di
        mov     ax,es:[di].GIOParaPack._hi
        mov     di,es:[di].GIOParaPack._lo
        mov     dx,(0000h OR DevHlp_VerifyAccess)       ; read only

VerifyAddr10:
        test    [si].ci_vdm_flag,VDM_Flag_InUse ; @VDM if in use by VDM
        jnz     ver99                           ; @VDM no check for pointer

        devhelp
        jc      ioc_99          ;  bad address

ver99:  pop     di
        pop     dx
        pop     ax

        ret

EndProc VerifyAddrD


;**     VerifyNullP - verify IOCTL parameter packet pointer is null
;**     VerifyNullD - verify IOCTL data      packet pointer is null
;
;       ENTRY   (es:di) -> request packet
;
;       EXIT    none
;
;       USES    none
;
;       WARNING will NOT return if IOCTL parameter/data packet pointer
;               is not null.
;               jumps indirectly to CmxGenFail (general failure)

Procedure VerifyNullD,NEAR
        ASSUME cs:CSEG,ds:DSEG,es:NOTHING,ss:NOTHING

        push    bx
        mov     bx,GIODataPack          ; offset of data packet
        jmp     short vn10

Entry VerifyNullP

        push    bx
        mov     bx,GIOParaPack          ; offset of parameter packet

vn10:   ChkRPPtr
        test    es:[di+bx]._hi,SELECTOR_MASK
        jnz     ioc_99                  ; not a null selector
        cmp     es:[di+bx]._lo,0
        jnz     ioc_99                  ; not a null offset

        pop     bx
        ret

EndProc VerifyNullD


;**     ComIoctl - Generic IOCTL Function Dispatcher
;
;       ENTRY   (ds:si) -> ComInfo
;               (es:di) -> Request packet
;
;       EXIT    none
;
;       USES    ax bx
;
;       Each IOCTL subroutine is called with:
;               (ds:si) -> ComInfo
;               (es:di) -> Request packet
;               ah      =  major code (category)
;               al      =  minor code (subfunction)
;               df      =  direction flag cleared for auto increment
;
;       IOCTL subroutines can use any register except bp.

Procedure ComIoctl,NEAR
        ASSUME cs:CSEG,ds:DSEG,es:NOTHING,ss:NOTHING

        ChkComInfoPtr
        ChkRPPtr

; We need to insure mutual exclusion between opens, closes and ioctls.

        call    Get_OCI_Sem                     ; will not return if error

        mov     ah,es:[di].GIOCategory  ; ah = category
        mov     al,es:[di].GIOFunction  ; al = function

;       We must handle monitor IOCTLs separately since they have
;       a specific error code.

        cmp     ah,IOC_MO
        je      ioc_monitor             ; monitor category not supported

;       Sort out which IOCTL function we have.

        cmp     ah,IOC_SE
        jne     ioc_unknown             ; not serial device category

        mov     bl,al                   ; function in bx for table index
        and     bx,IOMIN_SUBFUNC OR IOMIN_READ ; subfunction and read bits only
        cmp     bx,MAXIOCTL
        ja      ioc_bad                 ; function out of range

        shl     bx,1                    ; word offset for dispatch table
        cld                             ; clear direction flag (auto increment)

; If an IOCTL completes successfully, it does a RET or a jump to CmxDone.
; If it errs (like "invalid baud rate"), it can jump to ioc_99 or
; Cmx* instead no matter what stack depth it is at, as long
; as it hasn't modified bp (bp is used to restore the stack).

        call    IoctlTab[bx]
        jmp     CmxDone                 ; done and no error.

ioc_99: jmp     CmxGenFail              ; "general failure"


;**     ioc_bad - unknown ioctl category and/or function
;       don't return error if they don't want to see it
;
;       ENTRY   (es:di) -> request packet
;
;       EXIT    jumps to CmxDone or CmxUnknown
;
;       USES    none

ioc_bad:
        test    es:[di].GIOFunction,IOMIN_IGNERR
        jz      ioc_unknown             ; don't ignore error

        jmp     CmxDone                 ; ignore error

ioc_unknown:
        jmp     CmxUnknown              ; return 'unknown command'

ioc_monitor:
        jmp     CmxMonitor              ; return 'monitors not supported'


;**     IOCTL #41 - set baud rate
;
ioc_sbaud:
        call    VerifyNullD             ; verify null data pointer
.386p
        xor     eax,eax
.286p
        call    GetGIOParaPackW         ; (ax) = baud requested

        call    SetBaud                 ; set baud rate if legal
        jnc     ioc_sbx                 ; ok

        jmp     CmxInvalidParameter     ; bad baud

ioc_sbx:
        ret                             ; return normally.


;**     IOCTL #42 - set line control (byte size, parity, stop bits)
;
; Parmlist contains three bytes: al = ByteSize, ah = Parity, ch = StopBits
;
ioc_slinec:
        call    VerifyNullD             ; verify null data pointer
        mov     cx,3                    ; number of bytes in the LCR structure
        call    VerifyAddrP             ; verify address
        les     di,es:[di].GIOParaPack  ; es:di -> Data Buffer.
        mov     ax,es:[di]              ; get byte size and parity
        mov     ch,es:[di]+2            ; get stopbits
        SaveReg         <ax,cx>         ; save LCR bytes
        call    CheckLCR                ; verify input values
        jnc     ioc_linec10             ; "General Failure" err w/LCR input

        jmp     CmxGenFail              ; bad line control, general failure

ioc_linec10:
        RestoreReg      <cx,dx>         ; restore byte size and parity
        mov     [si].ci_bytesize,dl     ; No error: put input into ComInfo.
        mov     [si].ci_parity,dh       ;
        mov     [si].ci_stopbits,ch     ;
        call    SetLineC                ; Set [si].ci_linec, .ci_cmask
        ret


;**     IOCTL #46 - set modem control output signals (DTR and RTS only)
;
; uses masks of signals to turn off and on:
;
;   WORD where high byte = mask of bits to turn OFF.
;               low byte = mask of bits to turn ON.
;
; Examples:             input              high     low        result
;  Set    RTS:  WORD = (NOT 0 shl 8 + 2) = 11111111 00000010 ; bit 2 ON
;  Clear  RTS:  WORD = (NOT 2 shl 8 + 0) = 11111101 00000000 ; bit 2 OFF
;  Set    DTR:  WORD = (NOT 0 shl 8 + 1) = 11111111 00000001 ; bit 1 ON
;  Clear  DTR:  WORD = (NOT 1 shl 8 + 0) = 11111110 00000000 ; bit 1 OFF
;  Set    both: WORD = (NOT 0 shl 8 + 3) = 11111111 00000011 ; bits 1+2 ON
;  Clear  both: WORD = (NOT 3 shl 8 + 0) = 11111100 00000000 ; bits 1+2 OFF
;
; return general failure if the app tries to modify a signal that is being
;        controled automatically by the drvice driver.
;
; return ComErr
;
ioc_smodemc:
        ; load up return value now in case of bad pointer
        mov     ax,[si].ci_comerr
        call    LoadGIODataPackW        ; return ComErr word

        call    GetGIOParaPackW         ; ax = control masks
        mov     bx,ax                   ; save in bx

        ; verify not trying to modify a signal being used for handshaking
        not     ah                      ; bits being turned off
        or      al,ah                   ; bits being modified
        test    al,NOT (MC_DTR OR MC_RTS OR MC_LOOP)    ; trying to set
                                                        ; reserved fields?
        jnz     ioc_99                  ;  yes, error

        test    al,MC_DTR
        jz      ioc_smodemc10           ; not modifying DTR

        ; don't have to mask because only DTR_FLOW has bit 10b on
        .errnz  F1_DTR_FLOW    - 00000010b
        .errnz  F1_DTR_INVALID - 00000011b
        test    [si].ci_dcb_flags1,F1_DTR_FLOW
        jnz     ioc_99                          ; DTR handshake - error

ioc_smodemc10:
        test    al,MC_RTS
        jz      ioc_smodemc15           ; not modifying RTS

        ; don't have to mask because both auto modes have bit 10000000b on
        .errnz  F2_RTS_FLOW   - 10000000b
        .errnz  F2_RTS_TOGGLE - 11000000b
        test    [si].ci_dcb_flags2,F2_RTS_FLOW
        jnz     ioc_99                  ; RTS handshake or toggle - error

ioc_smodemc15:

        ; Set MCR for standard port. (bx) = control masks
        mov     dx,[si].ci_port         ; need port address for in/out
        add     dx,R_MODMC              ; MCR port
        min     al,dx                   ; Get old MCR value.
        and     al,bh                   ; Turn OFF desired bits.
        or      al,bl                   ; Turn ON desired bits.
        mout    dx,al                   ; Output new MCR value.

ioc_smodemcx:
        ret

;**     IOCTL #61 - get baud rate
ioc_gbaud:
        call    VerifyNullP              ; verify null parameter pointer
.386p
        test    [si].ci_baud, 0ffff0000h ; mask off bottom word
        jz      ioc_gbok                 ; anything in top word
        mov     eax, DEFAULT_BAUD        ; set to default baud
        call    SetBaud                  ; set the baud

ioc_gbok:
        mov     eax,[si].ci_baud
        call    LoadGIODataPackW        ; load baud rate
.286p
        ret


;**     IOCTL #62 - get line control (byte size, parity, stop bits)
ioc_glinec:
        call    VerifyNullP             ; verify null parameter pointer
        mov     cx,4                    ; verify a write of four bytes
        call    VerifyAddrD             ; verify a data address
        les     di,es:[di].GIODataPack  ; es:di -> Data Buffer.
        mov     al,[si].ci_bytesize     ; Data buffer:  byte 0 = bytesize
        mov     ah,[si].ci_parity       ;               byte 1 = parity
        stosw
        mov     al,[si].ci_stopbits     ;               byte 2 = stopbits
        mov     ah,[si].ci_linec        ;               byte 3 = Tx break
        rol     ah,2                    ; roll break bit to bit 0
        and     ah,1                    ; break bit only
        stosw
        ret


;**     IOCTL #65 - return tx status
;
; Bit   Description
; 0     queued write requests
; 1     data in DD tx queue
; 2     data in holding/shift register
; 3     character to tx immed
; 4     xon pending
; 5     xoff pending
; 6     reserved
; 7     reserved
;
ioc_gtxstat:
        call    VerifyNullP             ; verify null parameter pointer
        xor     ah,ah                   ; clear ah

        ; Queued write requests?
        cmp     [si].ci_w_rp._hi,0
        je      short ioc_gtx10         ;  no
        or      ah,1 shl 0              ;  yes
ioc_gtx10:

        ; Data in driver transmit queue?
        cmp     [si].ci_qout.ioq_count,0
        je      short ioc_gtx20         ;  no
        or      ah,1 shl 1              ;  yes

ioc_gtx20:

        ; If standard port, look at LSR to see if data in tx hardware.
        cli
        ReadLSR                         ; (al) = LSR
        sti
        and     al,LS_THRE OR LS_TSRE
        cmp     al,LS_THRE OR LS_TSRE   ; data in tx hardware?
        je      ioc_gtx30               ;  no
        or      ah,1 shl 2              ;  yes

ioc_gtx30:

        test    [si].ci_hsflag,HS_TX_IMMED ; character waiting to tx immed?
        jz      short ioc_gtx40         ;       no
        or      ah,1 shl 3              ;  yes
ioc_gtx40:

        test    [si].ci_hsflag,HS_XON_PENDING ; XON pending?
        jz      short ioc_gtx50         ;  no
        or      ah,1 shl 4              ;  yes
ioc_gtx50:

        test    [si].ci_hsflag,HS_XOFF_PENDING ; XOFF pending?
        jz      short ioc_gtx60         ;  no
        or      ah,1 shl 5              ;  yes
ioc_gtx60:

        xchg    ah,al
        call    LoadGIODataPackB        ; load tx status
        ret


;**     IOCTL #66 - get modem control output signals (DTR and RTS only)
ioc_gmodemc:
        call    VerifyNullP             ; verify null parameter pointer

ioc_gmc20:
        ; If standard port, just read MCR.
        mov     dx,[si].ci_port         ; need port address for in/out
        add     dx,R_MODMC              ; Get Modem Control Reg value.
        min     al,dx

ioc_gmcx:
        ; (al) = MCR.
        and     al,MC_DTR OR MC_RTS     ; mask reserved bits
        call    LoadGIODataPackB        ; Put AL in the Req Packet data buffer.
        ret


;**     IOCTL #67 - get control input signals (from shadow)
public ioc_gmodems
ioc_gmodems:                            ; PTR B708056 @@1
        call    VerifyNullP             ; verify null parameter pointer


gms50:
        ; If standard port, just read MSR.
        mov     dx,[si].ci_port         ; get port addr
        add     dx,R_MODMS              ; (dx) -> modem status reg
        cli                             ; disable ints
        min     al,dx                   ; (al) = modem status
;       test    al,MS_DCTS OR MS_DDSR OR MS_TERI OR MS_DDCD             173636
;       jz      gms90                   ; no MSR changes (interrupt)    173636
;       jmp     SHORT gms80                                             173636
        mov     ah, [si].ci_msrshadow   ; (ah) = MSR shadow            ;173636
        xor     ah, al                  ; find differences             ;173636

        cmp     ah, 0                   ; are there differences?       ;173636
        je      gms90                   ; no MSR changes (interrupt)   ;173636

gms80:  call    MxInt                   ; process modem status interrupt
        mov     al,[si].ci_msrshadow

gms90:  sti                             ; enable ints
        call    LoadGIODataPackB        ; Put AL in the Req Packet data buffer.
        ret


;**     IOCTL #44 - transmit immediate
ioc_tximmed:
        test    [si].ci_vdm_flag,VDM_Flag_InUse ;CP20D1390 If the port is opened
        jz      tximm_normal_req                ; with special vdm access,
        cmp     [si].ci_nvdmopens,0             ; Transmit Imm is not allowed.
        ljg     CmxInUse                        ;+yn error General Failure

tximm_normal_req:
        call    VerifyNullD                     ; verify null data pointer
        call    GetGIOParaPackB                 ; get byte to Tx
        cli
        test    [si].ci_hsflag,HS_TX_IMMED ; already one pending?
        jz      txim10                          ;  no, were cool
        sti
        jmp     CmxGenFail                      ;  yes, general failure

txim10: mov     [si].ci_tximm,al             ; save char
        or      [si].ci_hsflag,HS_TX_IMMED      ; flag TxImm char pending

txim20:
        ;NOTE interrupts are still off for CheckTX
        call    CheckTX                         ; kick transmitter into action.

tximmx: sti
        ret


;**     IOCTL #47 - as if XOFF received
ioc_rxoff:
        call    VerifyNullP             ; verify null parameter pointer
        call    VerifyNullD             ; verify null data pointer

off20:
        cli
        or      [si].ci_HSFlag,HS_XOFF_RECEIVED
        call    CheckTX

        sti
offx:   ret


;**     IOCTL #48 - as if XON received
ioc_rxon:
        call    VerifyNullP             ; verify null parameter pointer
        call    VerifyNullD             ; verify null data pointer

on20:
        cli
        and     [si].ci_HSFlag,NOT HS_XOFF_RECEIVED ; Show XOff NOT received
        call    CheckTX
        sti

onx:    ret


;**     IOCTL #4B - tx break on (stop transmitting)
ioc_break1:
        call    VerifyNullP             ; verify null parameter pointer
        call    LoadGIODataPackW        ; verify valid data parameter
        cli                             ; See ioctl 45

        or      [si].ci_HSFlag,LC_BREAK ; set the break bit
        .errnz  HS_BREAK_SET-LC_BREAK

brk110:
        mov     al,[si].ci_linec        ; get line control
        or      al,LC_BREAK             ; set break bit
        call    SetLineC
        call    CheckTX

brk1x:  sti
        mov     ax,[si].ci_comerr
        call    LoadGIODataPackW        ; Return ComErr word, w/o clearing it.
        ret


;**     IOCTL #45 - tx break off (resume tranmitting)
ioc_break0:
        call    VerifyNullP             ; verify null parameter pointer
        call    LoadGIODataPackW        ; verify valid data parameter
        cli
        and     [si].ci_HSFlag,not LC_BREAK     ; clear the break bit
        .errnz  HS_BREAK_SET-LC_BREAK

brk010:
        mov     al,[si].ci_linec                ; get line control
        and     al,not LC_BREAK                 ; clear break bit
        call    SetLineC
        call    CheckTX                         ; restart transmission

brkx:   sti
        mov     ax,[si].ci_comerr
        call    LoadGIODataPackW        ; Return ComErr word, w/o clearing it.
        ret


;**     IOCTL #68 - return # of characters in and size of receive queue
;
;       NOTE    Usable size of queue is one less than declared (QI_SIZE - 1).

ioc_gistat:
        lea     bx,[si].ci_qin          ; get pointer to input queue
        mov     dx,QI_SIZE - 1
        jmp     SHORT ioc_giostat


;**     IOCTL #69 - return # of characters in and size of transmit queue
;
;       NOTE    Usable size of queue is one less than declared (QO_SIZE - 1).

ioc_gostat:
        lea     bx,[si].ci_qout         ; get pointer to queue
        mov     dx,QO_SIZE - 1

ioc_giostat:
        call    VerifyNullP             ; verify null parameter pointer
        mov     cx,4                    ; verify a write of four bytes
        call    VerifyAddrD             ; verify a data address
        les     di,es:[di].GIODataPack  ; es:di -> Data Buffer.
        mov     ax,[bx].ioq_count       ; # of chars in Queue
        stosw
        mov     ax,dx                   ; size
        stosw
        ret


;**     IOCTL #72 - get (and clear) com event word
ioc_gevent:
        call    VerifyNullP             ; verify null parameter pointer
        xor     ax,ax                   ; ax = 0
        xchg    ax,[si].ci_event        ; Get the current event word
        call    LoadGIODataPackW        ; Return the event
        ret


;**     IOCTL #64 - return com status
;
; the ComStatus byte is defined as follows:
;
;   CST_CTS_HOLD    = 01h  Tx waiting for CTS
;   CST_DSR_HOLD    = 02h  Tx waiting for DSR
;   CST_DCD_HOLD    = 04h  Tx waiting for DCD
;   CST_XOFF_HOLD   = 08h  Tx waiting because XOFF received
;   CST_XOFF_SENT   = 10h  Tx waiting because XOFF transmitted
;   CST_BREAK_HOLD  = 20h  Tx waiting because break being xmitted
;   CST_TX_IMMED    = 40h  Character waiting to transmitt immediate
;   CST_RX_DSR_HOLD = 80h  Receive waiting for DSR
;
; Need to get the status for a com port.  Since not all the status is
; contained within ci_status, it has to be assembled.
; Note: MSR has active low values (0 = on, 1 = off)

ioc_gcomstat:
        call    VerifyNullP             ; verify null parameter pointer
        xor     ah,ah

        ; get DSR CTS DCD
        mov     al,[si].ci_msrshadow    ; (al) = msrshadow

        ; (al) = MSR
gcs03:  not     al                      ; (al) = NOT msrshadow - which are down
        mov     bl,al                   ; (bl) = NOT msrshadow - which are down
        and     al,[si].ci_outhhslines  ; only bits being used for handshaking
        shl     ax,1                    ; shift DCD into ah
        shl     al,1                    ; shift DSR and CTS next to DCD
        shr     ax,6                    ; shift into position
        .errnz        MS_CTS-00010000b
        .errnz        MS_DSR-00100000b
        .errnz        MS_DCD-10000000b
        .errnz  CST_CTS_HOLD-00000001b
        .errnz  CST_DSR_HOLD-00000010b
        .errnz  CST_DCD_HOLD-00000100b


        ; Standard port:
        cli
        mov     ah,[si].ci_HSFlag                       ; handshake flag
        and     ah,HS_XOFF_RECEIVED OR HS_XOFF_SENT     ; xoff rx/txed flags
        or      al,ah                                   ;Fix
        sti
        test    [si].ci_dcb_flags2,F2_FULL_DUP
        jz      gcs09                           ; full dup NOT on
        and     ah,NOT HS_XOFF_SENT             ; full dup ON, tx doesn't wait
gcs05:  or      al,ah
        .errnz  HS_XOFF_RECEIVED-00001000b
        .errnz     CST_XOFF_HOLD-00001000b
        .errnz      HS_XOFF_SENT-00010000b
        .errnz     CST_XOFF_SENT-00010000b


gcs09:
        ; Get BreakHold
        test    [si].ci_hsflag,HS_BREAK_SET     ; handshake flag
        jz      gcs10
        or      al,CST_BREAK_HOLD
gcs10:

        ; Get TxImmed
        test    [si].ci_hsflag,HS_TX_IMMED
        jz      gcs20
        or      al,CST_TX_IMMED         ; show TX_IMMED pending
gcs20:

        ; Get F1_IN_DSR_SENSE
        test    [si].ci_dcb_flags1,F1_IN_DSR_SENSE ; input sensitivity to DSR?
        jz      ioc_comstatx            ;  no, can't be waiting for it
        and     bl,MS_DSR               ; mask off DSR from MSR above
        shl     bl,2                    ; shift into position
        or      al,bl
        .errnz           MS_DSR-00100000b
        .errnz          CST_RX_DSR_HOLD-10000000b

ioc_comstatx:
        call    LoadGIODataPackB        ; load status
        ret


;**     IOCTL #6D - return (and clear) com error word
ioc_gcomerr:
        call    VerifyNullP             ; verify null parameter pointer
        xor     ax,ax
        xchg    ax,[si].ci_comerr
        call    LoadGIODataPackW
        ret


;**     IOCTL #53 - set device control block (DCB)
;
; see spec and include files for definitions of the DCB structure
;
public ioc_sdcb
ioc_sdcb:
        call    VerifyNullD             ; verify null data pointer
        mov     cx,DCB_SIZE             ; # bytes in the DCB structure
        .errnz  DCB_SIZE - 11
        call    VerifyAddrP             ; verify address

        xchg    si,di                   ; (ds:di) -> ComInfo
                                        ; (es:si) -> request packet
        SaveReg         <ds>
        lds     si,es:[si].GIOParaPack  ; (ds:si) -> new DCB parameters
        RestoreReg      <es>            ; (es:di) -> ComInfo struc

        ; check for invalid values in the DCB request

        mov     al,[si].ci_dcb_flags1           ; al = new flags1
        mov     bl,[si].ci_dcb_flags3           ; bl = new flags3

        test    al,F1_RESERVED
        ljnz    ioc_genfail                     ; reserved bits on

        .errnz  F3_READ_TO_MASK - 00000110b
        .errnz  F3_READ_TO_BAD  - 00000000b
        test    bl,F3_READ_TO_MASK
        jz      ioc_genfail                     ; bad read timeout

; if (new flags3 fifo mode is zero)
;   use old flags3 fifo bits and new flags3 other bits
; else if (new flags3 is non-zero)
;   if (fifo not available)
;     general failure
; set flags3 after copying to dd memory (can't change user memory)

        mov     bh,es:[di].ci_dcb_flags3        ; bh = old f3
        test    bl,F3_FIFO_MASK
        jnz     sdcb2                           ; setting fifo mode, go verify

; the FIFO mode is zero, ignore all the fifo flags (use old values)
        and     bh,F3_FIFO_ALL                  ; bh = old f3 with fifo only
        and     bl,NOT F3_FIFO_ALL              ; bl = new f3 with no fifo
        or      bl,bh                           ; bl = new f3 with old fifo
        jmp     SHORT sdcb4

; user is attempting to set the FIFO mode.  Fail if no FIFO available.
sdcb2:  test    bh,F3_FIFO_MASK
        jz      ioc_genfail                     ; fifo not available

        ; RTS mode cannot be invalid

        ; check DTR modes
        .errnz  F1_DTR_ENABLE - 00000001b
        .errnz  F1_DTR_FLOW   - 00000010b

sdcb4:  and     al,F1_DTR_MASK          ; only the DTR bits
        cmp     al,F1_DTR_INVALID
        je      ioc_genfail             ; invalid DRT mode


        ; all values are valid - put values into ComInfo

        ; save old flags1 and flags2 for ComputeHHS
        mov     al,es:[di].ci_dcb_flags1        ; al = old flags1
        mov     ah,es:[di].ci_dcb_flags2        ; ah = old flags2
        SaveReg         <ax>                    ; save old flags 1 and 2

        ; all values are valid - put values into ComInfo
        SaveReg         <di>                    ; save ComInfo pointer
        .errnz  ci_dcb_writeto
        mov     cx,DCB_SIZE
        .errnz  DCB_SIZE - 11

        cli     ; no interrupts while we set up the new modes

        rep movsb                               ; copy DCB structure

        RestoreReg      <si>                    ; ComInfo pointer

        push    es
        pop     ds                              ; (ds:si) -> ComInfo struc
        ChkComInfoPtr

        mov     [si].ci_dcb_flags3,bl           ; save valid flags3

; ComputeAPO MUST be called before ComputeXTO because the timeouts
; are based on the number of characters per interrupt.

;@89343 Because some applications may wish to modify the Read T/O or
;       the Write T/O and these values are part of the Function #53,
;       we do not want to be changing the FIFO unless the FIFO flags
;       have been changed.  This is especially important because of
;       the SMC patches applied in ComputeAPO.  Also remember that
;       ComputeAPO will be called at ComOpen if a FIFO exists so the
;       initial values will be set at least one time.  This fix is to
;       handle the function 53 calls made while actively reading /
;       writing to the port.
;
                                        ; @89343 start
        cmp         bh,bl               ; bh = old, bl = new
        je          sdcb10              ; No change, then skip FIFO

        SaveReg     <bx>                ; Save register
        and         bh,F3_FIFO_ALL      ; mask bits 0 - 2
        and         bl,F3_FIFO_ALL      ; mask bits 0 - 2
        cmp         bh,bl               ; bh,bl masked off
        RestoreReg  <bx>                ; Restore Register Back
        je          sdcb10              ; No Status Change in Flags.
                                        ; @89343 end

        call    ComputeAPO              ; compute FIFO modes

sdcb10:
; always recalc read/write timeouts as timeout, RX trig or TX count
; may have changed.  Easier to always recalc than to check.
        call    ComputeRTO              ; calculate new read  timeout values
        call    ComputeWTO              ; calculate new write timeout values

        RestoreReg      <bx>            ; bl = old flags1, bh = old flags2
        ; set DTR/RTS according to old/new DTR/RTS modes
        call    ComputeHHS                      ; calculate new hs masks


;Port is standard:
;if buffer full
;  call DisableRemoteTX
;else
;  call EnableRemoteTX.  (This call is necessary to get DTR and RTS
;  raised when we are transitioning from DTR and RTS off to input HS.
;endif

        cmp     [si].ci_qin.ioq_count,RX_HIGH_HS ;cj test to see if it's
                                                 ;cj above HW mark
        jle     sdcb12                  ;cj jump if RXQ is below high water mark
        call    DisableRemoteTX         ;cj go drop input HS lines or
                                        ;cj send XOFF as appropriate
        jmp     SHORT sdcb14            ;cj

sdcb12:
        call    EnableRemoteTX          ;cj go raise HS lines or
                                        ;cj send XON as appropriate

;Port is standard:
;if ATF disabled
;  reset XOFF_RECEIVED flag
;endif

sdcb14:
        test    [si].ci_dcb_flags2,F2_OUT_XO  ;cj test if ATF enabled
        jnz     sdcb16                        ;cj jump if yes
        and     [si].ci_hsflag,NOT HS_XOFF_RECEIVED
                                              ;cj else turn off XOFF_Received
                                              ;cj flag

;Port is standard:
;if ARF disabled
;  call EnableRemoteTXXO.  We may have to send an XON, and we would
;  have to do this regardless of the status of the receive buffer.
;  We can't call EnableRemoteTX because EnableRemoteTX only works
;  when ARF is enabled.
;endif

sdcb16:
        test    [si].ci_dcb_flags2,F2_IN_XO  ;cj test if ARF is enabled
        jnz     sdcb18                       ;cj jump if yes
        call    EnableRemoteTXXO             ;cj else go send XON

;if input HS was disabled, then the HS lines would have been set
;appropriately when ComputeHHS was called above.
;

sdcb18:
        call    CheckTX                 ;cj

sdcb20:
        ; call MxInt to set FX_IN_DSR_OK in case F1_IN_DSR_SENSE mode changed
        mov     al,[si].ci_msrshadow    ; (al) =  modem status register shadow
        call    MxInt                   ; calls CheckTX

sdcbx:  sti                     ; all modes set, ok for interrupts again

        ret

ioc_genfail:                            ; intermediate jump point
        jmp     CmxGenFail              ; general failure


;**     IOCTL #73 - get device control block
ioc_gdcb:
        call    VerifyNullP             ; verify null parameter pointer
        mov     cx,DCB_SIZE             ; # bytes in the DCB structure
        call    VerifyAddrD             ; verify data address

        .errnz  DCB_SIZE - 11

        lea     si,[si].ci_dcb_writeto          ; (ds:si) -> DCB in ComInfo
        les     di,es:[di].GIODataPack          ; (es:di) -> Data Buffer.

        rep movsb                               ; copy DCB structure

        ret

;************************************************************************
;* IOCTL #74   ioc_gENH
;*
;*   Return Com Enhanced parameters  Information (74h)
;*
;* FUNCTION:    Retrieves the Enhanced Mode information from ComInfo.
;
; Bit           Description
;
; 0             Enhanced mode supported by haardware
; 1             Enhanced mode enabled
; 2,3           DMA Recieve Operation Request
;       00      DMA Receive Disabled
;       01      DMA Receive Auto
;       10      DMA Receive Dedicated
;       11      Reserved
; 4,5           DMA Transmit Operation Request
;       00      DMA Transmit Disabled
;       01      DMA Transmit Auto
;       10      DMA Transmit Dedicated
;       11      Reserved
; 6             Last receive used DMA
; 7             Last transmit used DMA
ioc_genh:

        call    VerifyNullP             ; make sure param pkt pointer is NULL
        mov      cx,1
        call    VerifyAddrD             ; make sure data buffer addr is valid

        ; (AL) = enhanced flags
        ; set bits 0,1,6,7
        mov     al,[si].ci_eflags
        and     al,11000011b

        ; set bits 2,3
        mov     ah,[si].ci_tx_request
        shl     ah,2
        or      al,ah
        ; set bits 4,5
        mov     ah,[si].ci_rx_request
        shl     ah,4
        or      al,ah

        call    LoadGIODataPackB

       ret                              ; return through ComIoctl

;************************************************************************
;* IOCTL #54   ioc_sENH
;*
;*   Set Enhanced Mode Parameters (54h)
;*
;* FUNCTION:    Verifies that all Flags passed in are valid.
;*              Updates the Enhanced flags in ComInfo.
;*

ioc_sENH:
        jmp     CmxGenFail              ; hardware doesn't support enhanced

;************************************************************************
;* IOCTL #63  ioc_gbaudENH
;*
;*  Get Extended Baud Rate (63h)
;*
;*  Returns port Data Transfer Rate (in ComInfo) to user.
;*

public ioc_gbaudenh
ioc_gbaudenh:

     call    VerifyNullP                ; make sure null parm pkt pointer
     mov     cx,15                      ; length of packet
     call    VerifyAddrD                ; verify data address

     ; put baud rate into RP Data Area
     SaveReg         <si>
     .386p
     les     di,es:[di].GIODataPack     ; (es:di) -> IOCTL data packet

     mov     eax,[si].ci_baud           ; rax = baud data
     stosd                              ; Baud
     xor     al,al
     stosb                              ; Baud Frac

     mov     eax,MINBAUD
     stosd                              ; Baud Min
     xor     al,al
     stosb                              ; Min Frac

     ; determine maximum baud rate

     test    [si].ci_flagx, FX_16550A   ; is 16550?
     jnz     ioc_gbe16550               ; yes

     mov     eax, MAX_SBAUD             ; no, max = 57600
     jmp     ioc_gbeok                  ; go

ioc_gbe16550:                           ; 16550 UART
     mov     eax,MAX_EBAUD              ; max = 115200

ioc_gbeok:
     stosd                              ; Baud Max
     xor     al,al
     stosb                              ; Max Frac

   .286p
     RestoreReg      <si>
     ret

;************************************************************************
;* IOCtl #43 ioc_sbaudENH
;*
;* Set Extended (bit) Baud Rate  (43h)
;*
;*   Calls SetBaud to validate and set the Baud Rate.
;*   Calls ComputeTPC to compute the new tick per char
;*   value.
;*
public ioc_sbaudenh
ioc_sbaudenh:

        call    VerifyNullD            ; verify null data pointer
        mov     cx,5                   ; parameter packet is 5 bytes long
        call    VerifyAddrP            ; verify parameter address, cx = size
        push    bx
        SaveReg <es,di>
        les     di,es:[di].GIOParaPack ; (ds:si) -> IOCTL paramaters
        .386p
        mov     eax,es:[di]            ; load double word baud rate
        .286p
        xor     bx,bx
        mov     bl,es:[di]+4           ; fraction
        RestoreReg <di,es>
        call    SetBaud                ; set baud rate if legal

        pop     bx
        jnc     ioc_sbex

        jmp     CmxInvalidParameter    ; bad baud

ioc_sbex:
        ret                            ; return normally.



EndProc ComIoctl

CSEG ENDS

END

