;       SCCSID = @(#)atcom.asm  6.14 92/03/18
; ***************************************************************************
; *
; *                       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

;***    atcom.asm
;
;       This file contains the strategy routine for the com01.sys
;       device driver. This is the asynch device driver for the AT.
;       The device driver handles up to four com ports.

;       The asynch device driver is an installable driver. It is
;       installed via the 'device=' command in config.sys. It has no
;       command line arguments, therefore installation amounts to
;       the command:
;
;               device=com.sys
;
;       being present in config.sys.

;       Com.sys must use low memory for its I/O buffers. Therefore
;       it is advisable not to install the driver unless it is needed.

;       Modification History
;
;       DJG     01/06/87  Re-written to conform to MS standard for
;                         style, clarity and efficiency.
;       JGT     05/10/88  Add enhanced baud rate support (p587-cpd12)
;       JGT     07/25/88  Disable to read MsCount in ComClose - p1355
;       YN      05/25/89  MVDM Support   @VDM
;       ACW     04/16/91  @PVW Added perfview counters/timers
;       YN      08/13/91  CP20D1390 - Bulletin Board Support
;       YN      10/30/91  CP20PB729004 - Shutdown Support (Reset FIFO)
;       MF      02/21/92  PB732220 - Procomm Plus host mode fix (@0001)
;       JAG     07/29/93  72032 Fix detection of FIFO buffer for PCMCIA
;       JAG     09/14/93  73679 change IDC_handler to behave correctly
;                                (Cy set on error; more parm checking)
;       RAC     12/7/93   Add define statements to conditionally
;                         assemble PERFVIEW and TRACE code
;       RDW     03/17/94  77701 same as defect 76009 (add getlid to ABIOS)
;       RDW     03/30/94  81245  SMC SIO chip cause hang enabling FIFO UART
;       JAG     07/11/94  85558  Update 40:X for PCMCIA card inst/deinst.
;       JAG     08/26/94  95202  Check ABIOS rc for NOT_VALID; ThinkPad 720
;       JAG     09/26/94  99428  Resource manager fixes
;       JAG     09/29/94  100764 High speed pcmcia modems busted.
;       PMS     11/16/94  103070 -1 ComClose does not CLI before ProcBlockNI
;                         Found ComWFlushSub / ComRFlushSub were trashing
;                         ES:DI registers (ReqPacket).
;       PMS     11/16/94  103070 -2 PERFVIEW addition removed JMPs from
;                         ComRead and ComWrite code
;       DBH     09/05/95  125791 ComOpen does not free locked segments when
;                                attempt to allocate IRQ level fails.
;       PMS     10/17/95  139350 - Found that we need to skip the SMC fix if
;                         not a 16550a (FIFO) UART.  Some chip sets seem to
;                         generate spurious interrupts.
;       JAG     10/31/96  169017 - multiple small DosWrites hang system
;       DHD     05/15/97  177200 - long delay in closing


COM_ATTRIB      EQU     DEV_CHAR_DEV OR DEV_30 OR DEVLEV_3 OR DEV_GIOCTL OR DEV_SHARE OR DEV_IDC


.xlist
include devhlp.inc
include devsym.inc
include realmac.inc
include basemaca.inc
include error.inc
include filemode.inc
include infoseg.inc
include aberror.inc
.list
include atcom.inc
include gas.inc
include ateisa.inc
        EXTRN   Com1:WORD
        EXTRN   Com2:WORD
        EXTRN   Com3:WORD
        EXTRN   Com4:WORD
        EXTRN   Com1P:WORD
        EXTRN   Com2P:WORD
        EXTRN   Com3P:WORD
        EXTRN   Com4P:WORD
        EXTRN   Kernel_Type:WORD
        EXTRN   ComInit:FAR
        EXTRN   ComIoctl:NEAR
        EXTRN   DevHlp:DWORD
        EXTRN   Ticker:NEAR
        extrn   CheckLCR:near

        extrn   CheckTX:near
        extrn   Get_OCI_Sem:near
        extrn   LinkRP:near
        extrn   ProcBlock:near
        extrn   ProcBlockNI:near
        extrn   ProcRun:near
        extrn   ReadQueue:near
        extrn   StartNextRRP:near
        extrn   StartNextWRP:near
        extrn   UnLinkHeadRP:near
        extrn   UnLinkRP:near
        extrn   WriteQueue:near
        extrn   EnqComInfo:near
        extrn   DeqComInfo:near

        extrn   SetUpGDT:near

        extrn   endswapcode:byte
        extrn   ENDSWAPDATA:word
        extrn   MxInt:near
        extrn   EnableRemoteTX:near
        extrn   ComputeAPO:near
        extrn   ComputeHHS:near
        extrn   ComputeRTO:near
        extrn   ComputeWTO:near
        extrn   SetLineC:near
        extrn   SetBaud:near
        extrn   CheckLCR:near
        EXTRN   ComInit:FAR
        EXTRN   ComInt1:FAR
        EXTRN   ComInt2:FAR
        EXTRN   ComInt3:FAR
        EXTRN   ComInt4:FAR
        EXTRN   SComInt1:FAR
        EXTRN   SComInt2:FAR
        EXTRN   SComInt3:FAR
        EXTRN   SComInt4:FAR
        EXTRN   ShrdIRQ1:BYTE
        EXTRN   _RMHELP_CreateLDev:FAR
        EXTRN   _RMHELP_DestroyLDev:FAR
        EXTRN   _RMHELP_FindPCMCIANode:FAR



HSEG SEGMENT

;       Com1Dev and Com2Dev are the headers for the asynch
;       device drivers.

;       WARNING : If Com1Dev is the first header in the device
;       driver chain, the DOS expects to find it at the beginning
;       of the device driver's data segment.

SDev macro baselabel, devnext, devattr, devstrat, devintr, devname, devpCS, devpDS, devrCS, devrDS, devcap
    ?SDev       macro   field, size, value
        .errnz ($ - baselabel - offset field)
        size value
    endm
    baselabel label byte
    ?SDev  SDevNext     dd      <devnext>
    ?SDev  SDevAtt      dw      <devattr>
    ?SDev  SDevStrat    dw      <devstrat>
    ?SDev  SDevInt      dw      <devintr>
    ?SDev  SDevName     db      <devname>
    ?SDev  SDevProtCS   dw      <devpCS>
    ?SDev  SDevProtDS   dw      <devpDS>
    ?SDev  SDevRealCS   dw      <devrCS>
    ?SDev  SDevRealDS   dw      <devrDS>
    ?SDev  SDevCaps     dd      <devcap OR 0000000000000001b>
    .errnz ($ - baselabel - size SysDev3)
endm

;IFDEF    COMDEBUG
;SDev Com1Dev -1, COM_ATTRIB, Com1Strat, 0, 'COM1    '
;ELSE
SDev Com1Dev <offset Com2Dev>,COM_ATTRIB,Com1Strat,COM1_IDC_Entry,'COM1    ',0,0,0,0,DEV_16MB
SDev Com2Dev <offset Com3Dev>,COM_ATTRIB,Com2Strat,COM2_IDC_Entry,'COM2    ',0,0,0,0,DEV_16MB
SDev Com3Dev <offset Com4Dev>,COM_ATTRIB,Com3Strat,COM3_IDC_Entry,'COM3    ',0,0,0,0,DEV_16MB
SDev Com4Dev -1,              COM_ATTRIB,Com4Strat,COM4_IDC_Entry,'COM4    ',0,0,0,0,DEV_16MB
;ENDIF

.ERRNZ  OFFSET HSEG:Com1Dev             ; enforce the above warning

HSEG    ENDS

DSEG SEGMENT

public startswapdata
startswapdata   label byte

;       There are 13 (out of a possible 21) commands that can be
;       made of a character device driver. Communication with a
;       device driver is performed through packets. Within this
;       packet is the command code. We use this value to drive the
;       strategy routine of com01.sys. This is accomplished via the
;       following jump table.

        EVEN                            ; force word alignment
ComStratTab     LABEL   WORD
        DW      0                       ;0: Init (not used in this table)
        DW      OFFSET CSEG:CmxUnknown  ;1: Media Check (Block Devices Only)
        DW      OFFSET CSEG:CmxUnknown  ;2: Build BPB (Block Devices Only)
        DW      OFFSET CSEG:CmxUnknown  ;3: Reserved (used to be Ioctl Input)
        DW      OFFSET CSEG:ComRead     ;4: Input (Read)
        DW      OFFSET CSEG:ComNDRead   ;5: Non-Destructive Input, no wait.
        DW      OFFSET CSEG:ComRStat    ;6: Input Status
        DW      OFFSET CSEG:ComRFlush   ;7: Input Flush
        DW      OFFSET CSEG:ComWrite    ;8: Output (Write)
        DW      OFFSET CSEG:ComWrite    ;9: Output (Write) with verify
        DW      OFFSET CSEG:ComWStat    ;10: Output Status
        DW      OFFSET CSEG:ComWFlush   ;11: Output Flush
        DW      OFFSET CSEG:CmxUnknown  ;12: Reserved (used to be Ioctl Output)
        DW      OFFSET CSEG:ComOpen     ;13: Device Open
        DW      OFFSET CSEG:ComClose    ;14: Device Close
        DW      OFFSET CSEG:CmxUnknown  ;15: Removable Media
        DW      OFFSET CSEG:ComIoctl    ;16: Generic Ioctl
        DW      OFFSET CSEG:CmxUnknown  ;17: Reset Media
        DW      OFFSET CSEG:CmxUnknown  ;18: Get Logical Drive Map
        DW      OFFSET CSEG:CmxUnknown  ;19: Set Logical Drive Map
        DW      OFFSET CSEG:CmxUnknown  ;20: DeInstall
MAXCMD = (($ - ComStratTab)/2) - 1

TimerHook       DB      0       ; timer hook count
; actually a count of the number of ports that are open.
; incremented on a first open, decremented on a last close for any port.

        PUBLIC  VCOMAddress                             ; @VDM
VCOMAddress     df      0                               ; @VDM
; VCOM interrupt handler entry point                      @VDM

        PUBLIC  ClkIntrvl
ClkIntrvl       DW      0       ; System clock interval in 10000s of a second

SysInfoSeg      DW      0       ; for getting running MsCount for close timeout

        PUBLIC  Flags
Flags   DB      0               ; flags for driver shut down

PUBLIC  ComLHead, ComGAS

ComLHead  LOGHEAD  <,GasSize-$-2,SNAGENALERT,,,,,,,>        ; SNA ALERT log entry header
          PSIDS    <>                                       ; product set ID SV
ComGAS    GAS      <,,,GAS_PERM_LOSS,GAS_ADC_DEVCOMERROR,0> ; alert Sub Vector
ComPCS    GAS_PCS  <,,GAS_PCD_COMSTARTSTOP>                 ; prob cause SV
ComFCS    GAS_FCS  <,,,,GAS_FCC_DEVOUT,,,GAS_RAC_READY_DEVICE> ; fail cause SV
GasSize LABEL WORD

codelockhandle  db      lhlen   dup(0)
datalockhandle  db      lhlen   dup(0)

public IntRoutines
IntRoutines     DW      OFFSET RSEG:ComInt1     ; Int. routine for COM1
                DW      OFFSET RSEG:ComInt2     ; Int. routine for COM2
                DW      OFFSET RSEG:ComInt3     ; Int. routine for COM3
                DW      OFFSET RSEG:ComInt4     ; Int. routine for COM4


DSEG    ENDS


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

public startswapcode
startswapcode   label   byte

;**     ComStrat - strategy entry point for COM
;
;       Com1Strat and Com2Strat are the entry points for the strategy
;       routine for the asynch device driver. The strategy routine is
;       called by the DOS in order to handle device requests.  This is
;       the combined portion of the strategy code.  This portion is
;       swappable until the first open completes.
;
;       ENTRY   (es:bx) -> request packet
;               (ds) DSEG segment for com01.sys
;
;       EXIT    result of operation set in request
;               packet status field (PktStatus)
;
;       USES    di
Procedure ComStrat,FAR

        mov     di,bx                   ; (es:di) -> request packet

IFDEF RPSTRICT
        or      es:[di].PktStatus,SIGNATURE
ENDIF

        SaveReg         <es,di>         ; save pointer to request packet
        SaveReg         <ds,si>         ; save pointer to ComInfo structure
        mov     bp,sp                   ; allocate NULL stack frame

;        cmp     es:[di].PktCmd,01BH
        cmp     es:[di].PktCmd,CMDInit
        jne     cst10                   ; init command, call directly

        call    ComInit                 ; do init
        jmp     bx                      ; do end processing

cst10:
        cmp     es:[di].PktCmd,CMDShutdown
        jne     cst20                   ; init command, call directly

        call    ComShutdown
        jmp     CmxDone

cst20:
        or      si,si
        jz      CmxNotReady             ; device not installed

        xor     bx,bx
        mov     bl,es:[di].PktCmd

        cmp     bl,MAXCMD
        ja      CmxUnknown              ; command is invalid

IFDEF   COMDEBUG
;       int     3
ENDIF

        shl     bx,1                    ; (bx) = word index to ComStratTab

; On entry to each of the command handlers:
;
;       (ds:si)         -> device info structure for COM1 or COM2.
;       (es:di)         -> request packet
;       ax              = device minor number

        jmp     ComStratTab[bx]         ; go execute command

; Following are the six exit points for the driver. Request
; handlers all jump to one of these labels to have their exit
; condition set. The stack is permitted to be in an inconsistent
; state here because we are going to deallocate the stack frame.

Entry CmxDone,,,nocheck                 ; no error

        mov     ax,STDON
        jmp     SHORT cmx

Entry CmxUnknown,,,nocheck              ; error - unknown command

        mov     ax,STDON OR STERR OR ERROR_I24_BAD_COMMAND
        jmp     SHORT cmx

Entry CmxNotReady,,,nocheck             ; error - device not ready

        mov     ax,STDON OR STERR OR ERROR_I24_NOT_READY
        jmp     SHORT cmx

Entry CmxBusy,,,nocheck                 ; no error, device busy

        mov     ax,STDON OR STBUI
        jmp     SHORT cmx

Entry CmxMonitor,,,nocheck              ; error - monitors not supported

        mov     ax,STDON OR STERR OR ERROR_I24_NO_MONITOR_SUPPORT
        jmp     SHORT cmx

Entry CmxInterrupted,,,nocheck          ; error - interrupted ProcBlock

        mov     ax,STDON OR STERR OR ERROR_I24_CHAR_CALL_INTERRUPTED
        jmp     SHORT cmx

Entry CmxInvalidParameter,,,nocheck     ; error - invalid parameter

        mov     ax,STDON OR STERR OR ERROR_I24_INVALID_PARAMETER
        jmp     SHORT cmx

Entry CmxBadLock,,,nocheck              ; error, Lock failed
Entry CmxGenFail,,,nocheck              ; error, general failure

        mov     ax,STDON OR STERR OR ERROR_I24_GEN_FAILURE
        jmp     SHORT cmx

Entry CmxInUse,,,nocheck                ; error, device in use
        mov     ax,STDON OR STERR OR ERROR_I24_DEVICE_IN_USE

; Common exit point.

cmx:    sti                             ; just in case someone left them off
        mov     sp,bp                   ; deallocate NULL stack frame
        RestoreReg      <si,ds>         ; (ds:si) -> ComInfo structure
        RestoreReg      <di,es>         ; (es:di) -> request packet
        ASSUME  ds:DSEG,es:NOTHING
        ;BUGBUG internal error if es:di not a req packet
        ;can't call ChkRPPtr because already poped stack frame.

        mov     es:[di].PktStatus,ax    ; update status field

        or      si,si
        jz      cmret                   ; port is not installed or ShtuDown

        test    Flags,F_SHUT_DOWN
        jnz     cmret                   ; driver is shut down

;**  Logging Facility support:
;
;   if ci_flagGA != 0                   ; is there an alert condition active?
;     if  SIS_SysLog != 0               ; yes, is the system logging active?
;       ax = 8                          ; yes, so set cx = max flag bit value
;       GASLoop: if ci_flagGA[cx] != 0  ; is the bit on?
;               es:bx --> ci_GAS        ; yes, set up pointer to alert struc
;               CALL DevHlp_LogEntry    ;   send the alert struc to sys log
;       end_GASLoop;                    ; no....
;             if ci_flagGA == 0         ; any other bits on?
;                or ax == 0
;               jump to postGAS         ; no, so quit
;             ax = ax - 1               ; test next bit in ci_flagGA
;             ci_flagGA = ci_flagGA/2   ; shift right, lose low order bit
;             jump to GASLoop           ; and iterate
;     postGAS:  ci_flagGA = 0
;
        mov     cx,[si].ci_flagGA       ; do we have an alert condition?
        or      cx,cx
        jz      cmx_oci                 ; no, so skip to check OCI Sem

        mov     ax,SysInfoSeg           ; get SysInfoSeg pointer
        push    ds
        mov     ds,ax
        mov     ax,ds:SIS_SysLog        ; get SysLog word from SysInfoSeg
        test    ax,LF_LOGENABLE         ; is system logging enabled?
        pop     ds
        jz      postGAS                 ; no, reset GAS flag & continue on

; from here, check and service each bit in ci_flagGA that is set

        mov     ax,GAS_FLAG_MAX         ; start with highest valid GAS bit

GASloop:
        test    cx,ax                   ; is this bit on?
        jz      end_GASLoop             ; no, so check next bit

; now we know we have a specific alert condition (index = ax)

        SaveReg <es,cx>                 ; preserve RP address
        mov     cx,LOGERROR             ; function is LOGERROR
        mov     WORD PTR DS:ComGAS.GAS_ALERTID,ax  ; IBMHACK use this for now
        mov     WORD PTR DS:ComGAS.GAS_ALERTID+2,0 ; IBMHACK
        push    ds                      ; es needs to point to COM data seg
        pop     es
        lea     bx,ComLHead             ; (es:bx) --> ComLHead struc

        mov     dl,DevHlp_LogEntry      ; log this generic alert
        DevHelp                         ; via the DevHlp

        RestoreReg <cx,es>              ; restore RP pointer

end_GASLoop:

        shr     ax,1                    ; bump test bit over one
        jnz     GASloop                 ; else try next bit

postGAS:
        mov     [si].ci_flagGA,0        ; reset all flags for next request

;** If this request is the owner of the oci_sem semaphore, clear it.
; We do this here, rather than in the respective ComXXX routines,
; because of the many places in which these routines can 'exit'.

        ;BUGBUG internal error if ds:si not a ComInfo
        ;can't call ChkComInfoPtr because already poped stack frame.
cmx_oci:
        mov     ax,es                           ; (ax) = es
        cmp     ax,[si].ci_oci_sem_own._hi
        jne     cmret                           ; not oci_sem owner

        cmp     di,[si].ci_oci_sem_own._lo
        jne     cmret                           ; not oci_sem owner

        ; this request owns the semaphore so clear it
        mov     ax,ds
        lea     bx,[si].ci_oci_sem              ; (ax:bx) -> semaphore
        mov     dl,DevHlp_SemClear
        DevHelp

        mov     [si].ci_oci_sem_own._hi,0       ; zap owner high (segment)

cmret:  ret

EndProc ComStrat


;**     IDC_Handler - IDC strategy entry point for COM
;
;       IDC_Handler is the entry point for the IDC strategy
;       routine for the asynch device driver. The strategy routine is
;       called by the PCMCIA drivers.
;
;       ENTRY   (es:bx) -> request packet
;               (ds) DSEG segment for com01.sys
;
;       EXIT    result of operation set in request
;               packet status field (Return_Code)
;
;       USES    di
Procedure IDC_Handler,FAR

        push    es
        push    ds
        pusha
        setDS   DSEG
        mov     di,bx                   ; (es:di) -> request packet
        mov     dx,es:[di].Command_Flag        ;Check Command Flag
        cmp     dx,INSTALL_CMD                 ; Is it for Install COM port?
        je      idc10                          ; Yes
        cmp     dx,DEINSTALL_CMD               ; Is it for Deinstall COM port?
        lje     idc_deinstall                  ; Yes
        mov     cx,ERROR_INVALID_FUNCTION      ; Otherwise, invalid Command
        jmp     idc88                          ; Set Return Code and end
; Process Install COM port command
idc10:                                         ;
        mov     ax,es:[di].Com_Port_Num        ; Check for Port Number conflict
        cmp     ax,MAXCOMPORTS                 ; Com port > 4?
        ljg     port_num_conflict              ; yes
        dec     ax                             ;
        shl     ax,1                           ;
        push    bx
        lea     bx,Com1                        ; Check 40:X for conflict
        add     bx,ax                          ;
        mov     bx,DS:[bx]                     ;
        cmp     bx,0                           ;
        pop     bx
        ljne    port_num_conflict              ;

        ; COM port address verification        ;
no_40x_conflict:
        mov     dx,es:[bx].Com_Port_Addr
        cmp     dx,0                           ;
        lje     port_addr_conflict
        mov     si,Com1                        ;
        cmp     si,0                           ;
        je      addr_check2                    ;
        mov     dx,ds:[si].ci_port             ;
        cmp     es:[bx].Com_Port_Addr,dx       ;
        jne     addr_check2                    ;
        jmp     port_addr_conflict             ;
addr_check2:                                   ;
        mov     si,Com2                        ;
        cmp     si,0                           ;
        je      addr_check3                    ;
        mov     dx,ds:[si].ci_port             ;
        cmp     es:[bx].Com_Port_Addr,dx       ;
        jne     addr_check3                    ;
        jmp     port_addr_conflict             ;
addr_check3:                                   ;
        mov     si,Com3                        ;
        cmp     si,0                           ;
        je      addr_check4                    ;
        mov     dx,ds:[si].ci_port             ;
        cmp     es:[bx].Com_Port_Addr,dx       ;
        jne     addr_check4                    ;
        jmp     port_addr_conflict             ;
addr_check4:                                   ;
        mov     si,Com4                        ;
        cmp     si,0                           ;
        je      irq_check                      ;
        mov     dx,ds:[si].ci_port             ;
        cmp     es:[bx].Com_Port_Addr,dx       ;
        jne     irq_check                      ;
        jmp     port_addr_conflict             ;
        ;  IRQ verification
public irq_check
irq_check:                                     ;
        mov     dx,es:[di].Com_Port_IRQ        ; Copy port IRQ from IDC packet
        cmp     dx,0
        lje     port_irq_conflict

        mov     ax,es:[di].Com_Port_Num        ;
        dec     ax                             ;
        shl     ax,1                           ;
        lea     bx,Com1P                       ;  Copy ComNP to ComN
        add     bx,ax                          ;  to install COM port
        mov     dx,DS:[bx]                     ;
        mov     si,dx                          ;

        mov     dx,es:[di].Com_Port_IRQ        ; Copy port IRQ from IDC packet


        lea     bx,Com1                        ;
        add     bx,ax                          ;
        mov     DS:[bx],si                     ;

        shr     ax,1

no_irq_conflict:                               ;
        ; SetUp ComInfo Structure!
.386p
        mov     ds:[si].ci_port_number,al      ;
        mov     ds:[si].ci_irq,dl              ; to ComInfo structure
        mov     dx,es:[di].Com_Port_Addr       ; Copy port addr from IDC packet
        mov     ds:[si].ci_port,dx             ; to ComInfo structure
        mov     [si].ci_flagx,0                ; chip type = ?  @72032
        mov     [si].ci_dcb_writeto, WRITE_TO_INIT    ; write timeout
        mov     [si].ci_dcb_readto, READ_TO_INIT      ; read  timeout
        mov     [si].ci_dcb_flags1, F1_INIT           ; first  flags byte
        mov     [si].ci_dcb_flags2, F2_INIT           ; second flags byte
        mov     [si].ci_dcb_flags3, F3_INIT           ; third  flags byte
        mov     [si].ci_dcb_XonChar, xonequ           ; XON  character
        mov     [si].ci_dcb_XoffChar, xoffequ         ; XOFF character
        mov     [si].ci_signature, SIGNATURE          ; signature ID word

        mov     [si].ci_baud, DEFAULT_BAUD            ; baud rate
        mov     [si].ci_bytesize, DEFAULT_BYTESIZE    ; TX/RX byte size
        mov     [si].ci_parity, DEFAULT_PARITY        ; parity
        mov     [si].ci_stopbits, DEFAULT_STOPBITS    ; stop bits
        mov     [si].ci_cmask, DEFAULT_CHAR_MASK      ; received character mask
        mov     [si].ci_depth, D_NONE                 ; interrupt depth
        mov     [si].ci_r_rpl.rpl_signature, SIGNATURE
        mov     [si].ci_w_rpl.rpl_signature, SIGNATURE
        mov     [si].ci_int_sharing, 0                ; int. sharing
        mov     [si].ci_mult_COMs_IRQ, 0              ; no other COM ports
                                                      ; on this IRQ line
        or      [si].ci_dcb_flags2,F2_BRK_CHAR        ;break replace active

        or      [si].ci_vdm_flag,VDM_Flag_notify_the_VCOM_TX   ;@VDM
        or      [si].ci_vdm_flag,VDM_Flag_notify_the_VCOM_RX   ;@VDM
        or      [si].ci_vdm_flag,VDM_Flag_InOut_Handshake_Init
        mov     [si].ci_vdm_Rx_State,1                ;@VDM set to Rx state 1
        mov     [si].ci_vdm_Tx_State,1                ;@VDM set to Tx state 1
        mov     [si].ci_vdm_Rx_Count,0                ;@VDM
        mov     [si].ci_vdm_Tx_Count,0                ;@VDM
        mov     [si].ci_vdm_LastMSR,0                 ;@VDM
        or      [si].ci_flagx1, FX1_PCMCIA_MODEM      ; Leave pcmcia bit
.286p
        push    es
        push    ax
        push    dx

        push    DSEG
        push    si
        call    _RMHELP_FindPCMCIANode
        add     sp,4

        push    DSEG
        push    si
        call    _RMHELP_CreateLDev
        add     sp,4
        pop     dx
        pop     ax
        pop     es


public idc_40_0
idc_40_0:
                                        ; update 40:0 area ; 85558
        mov     bx,ax                   ; port num
        shl     bx,1                    ; com minor
        mov     ax,COM_SEG              ; 40: tiled selector to ROM data
        push    es                      ; save es
        mov     es,ax                   ; (es:bx) -> 40:X
        mov     es:[bx],dx              ; (dx) -> port addr
        pop     es                      ; restore es

public idc_uart
idc_uart:
        ; check for 16550A (FIFO)
        add     dx,R_FIFOC                     ; (dx) -> FIFO control register
        mov     al,FF_CLEAR_RX OR FF_CLEAR_TX
        mout    dx,al                          ; try to turn off the 16550A FIFOs

        mov     al,FF_ENABLE
        mout    dx,al                          ; try to turn on the 16550A FIFOs
        min     al,dx                          ; (al) = interrupt id
        and     al,II_16550A
        test    [si].ci_flagx,FX_16550A        ; chip type = 16550A?
        jnz     idc20
        cmp     al,II_16550A
        jne     idc21                          ; NO 16550A FIFO; go check for 16450

idc20:
        or      [si].ci_flagx,FX_16550A        ; chip type = 16550A
        and     [si].ci_dcb_flags3,NOT F3_FIFO_ALL ; FIFO auto priority override
        or      [si].ci_dcb_flags3,F3_FIFO_ON OR F3_RX_8 OR F3_TX_16 ;

        xor     al,al
        mout    dx,al                          ; disable the 16550 FIFOs

        jmp     short idc23

idc21:  ; check for 16450 (or 16550) by looking for the scratch register
        ; that is only on the 16450, 16550 and 16550A (not on 8250x).
        MAGIC   EQU     10101100b

        xor     al,al
        mout    dx,al                   ; disable the 16550 FIFOs

        add     dx,R_SCRATCH-R_FIFOC    ; (dx) -> scratch register
        mov     al,MAGIC
        mout    dx,al                   ; (scratch) = MAGIC
        min     al,dx                   ; (al) = (scratch)
        cmp     al,MAGIC
        jne     idc23                   ; NO scratch register; must be 8250

        or      [si].ci_flagx,FX_16450  ; set type flag to 16450

idc23:
        cmp     Kernel_Type, ABIOS_COM         ;
        jne     idc30                          ;
        cli
        call    InitSharedIRQ
        sti
        mov     [si].ci_int_sharing, INT_SHARING ; sharing com irq
        mov     [si].ci_mult_COMs_IRQ, INT_SHARING ;
        cli
        call    OptimizeSharedIRQs
        sti
idc30:
        mov     dx,[si].ci_port         ; get port address
        add     dx,R_MODMC              ; (dx) -> modem control reg.
        xor     al,al                   ; (al) = 0
        mout    dx,al                   ; lower all modem control lines

        add     dx,R_LINEC-R_MODMC      ; (dx) -> line control reg.
        min     al,dx                   ; (al) = line control register
        and     al,NOT LC_BREAK         ; turn off break
        mout    dx,al                   ; send to port

; save interrupt handler offset

; To make multiple COM ports easier to handle, this code is now
; table driven.
idc40:
        mov     bx,es:[di].Com_Port_Num
        dec     bx                      ; (bx) = minor number
        shl     bx,1                    ; (es:bx) -> 40:X
        mov     dx, IntRoutines[bx]     ; bx = com_minor*2
        add     bx, OFFSET DSEG:Com1    ; adjust to com. data struc.

idc45:
        setDS   DSEG
        mov     [si].ci_isr,dx          ; save in cominfo
        mov     [bx],si                 ; init ComInfo ptr.

        call    InitQueues              ; initialize input and output queues

        xor     cx,cx                        ; NO_ERROR return code
        jmp     SHORT idc89                  ; CY cleared by XOR

; Process Deinstall COM port command
public idc_deinstall
idc_deinstall:
        mov     ax,es:[di].Com_Port_Num
        dec     ax
        shl     ax,1
        lea     bx,Com1
        add     bx,ax
        mov     ax,ds:[bx]
        mov     si,ax
        cmp     ax,0
        je      port_not_exist
        xor     ax,ax
        mov     ds:[bx],ax
                                        ; 85558
        mov     bx,es:[di].Com_Port_Num ; Update 40:X BIOS data area
        dec     bx                      ; Zero based
        shl     bx,1                    ; Offset into 40:0
        mov     ax,COM_SEG              ; 40: tiled selector to ROM data
        push    es                      ; save es
        mov     es,ax                   ; (es:bx) -> 40:X
        xor     ax,ax                   ; clear ax
        mov     es:[bx],ax              ; (dx) -> port addr


        push    DSEG
        push    si
        call    _RMHELP_DestroyLDev
        add     sp,4

        pop     es                      ; restore es

        cli
        call    OptimizeSharedIRQs             ; Clean up SharedIRQ structure
        sti
        xor     cx,cx                          ; NO_ERROR return code
        jmp     SHORT idc89                    ; CY cleared by XOR
port_not_exist:                                ;
        mov     cx,ERROR_DEV_NOT_EXIST         ;
        jmp     SHORT idc88                    ;
port_irq_conflict:                             ;
        mov     cx,ERROR_INTERRUPT             ;
        jmp     SHORT idc88                    ;
port_addr_conflict:                            ;
        mov     cx,487                         ; ERROR_INVALID_ADDRESS
        jmp     SHORT idc88                    ;
port_num_conflict:                             ;
        mov     cx,ERROR_DEVICE_IN_USE         ;
idc88:  stc                                    ; CY set for ERROR
idc89:                                         ;
        mov     es:[di].Return_Code,cx         ;
idc90:                                         ;
        popa
        pop  ds
        pop  es
        ret                                    ;

EndProc IDC_Handler

;**     ComOpen - open request handler
;
;       Most of the work performed by ComOpen occurs if the device
;       is being opened for the first time. If it isn't all we have
;       to do is increment the count of the number of opens (this is
;       needed for close processing). If it is the first open then we
;       have to initialize the device and the driver. Specifically, we
;       have to;
;               - hook the appropriate IRQ
;               - hook the timer ticks
;               - initialize the clock interval value (done once per boot)
;               - flush the I/O queues
;               - initialize the DCB structure, line control register,
;                 hardware registers and hardware handshake status
;               - enable the modem control signals which in turn enables
;                 the remote transmitter
;
;       ENTRY   (ds:si) -> ComInfo
;               (es:di) -> request packet
;
;       EXIT    hardware and driver initialized, open count incremented
;
;       USES    ax bx cx dx di

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

        ChkComInfoPtr
        ChkRPPtr

        mov     [si].ci_vdm_OpenError ,1        ;@VDM set up for general failure

        test    es:[di].PktStatus,open_monitor
        jz      cop01

        test    [si].ci_vdm_flag,VDM_Flag_InUse  ; FIX ---------------
        ljz     CmxMonitor                       ; no monitor support
        jmp     CmxGenFail                       ; if vdm, it is a failure

; We need to insure mutual exclusion between opens, closes and ioctls.
cop01:
        call    Get_OCI_Sem                     ; will not return if error

        ChkComInfoPtr

        cmp     [si].ci_nopens,0
        je      cop05                           ; not first open

;@VDM Begin
; check for VDM-Protectmode conflict
;    IF (OpenCount != 0) AND (ComInfo.vdm_flag.InUse = TRUE)
        test    [si].ci_vdm_flag,VDM_Flag_InUse  ;CP20D1390 ---------------
        jz      vdm_sup001                       ;  |
        push    es                               ;  |
        push    ax
        mov     al,LocINFOseg             ;Set VAR index for Local InfoSeg.
        mov     dl,DevHlp_GetDOSVar       ;Set DevHlp function.
        xor     dh,dh
        call    [DevHlp]                  ;Go get variable address.
        mov     es,ax                     ;Make ES:BX point to InfoSeg address variable.
        mov     ax,es:[bx]+2
        mov     bx,es:[bx]
        mov     es,ax                     ;Make ES:BX point to InfoSeg address variable.
        mov     ax,es:[bx].LIS_CurScrnGrp ;Get Screen Group ID = Session ID
        cmp     [si].ci_sid,ax                   ;
        pop     ax                               ;  |
        pop     es                               ;  |
        je      UsedOpen                         ;CP20D1390

;    THEN
        jmp     CmxInUse

UsedOpen:                               ; CP20D1390
        inc     [si].ci_nvdmopens       ; Increment the counter for special VDM open

vdm_sup001:                             ; not in use by VDM
;    ENDIF
;@VDM End

        jmp     cop80

; When the device is opened for the first time there are a number of
; initializations that need to be performed.

cop05:
        test    [si].ci_vdm_flag,VDM_Flag_No_IRQ_Open
        ljnz     cop90

        test    [si].ci_vdm_flag,VDM_Flag_InOut_Handshake_Init
        jz      cop0505
        and     [si].ci_vdm_flag,NOT VDM_Flag_InOut_Handshake_Init
        push    ax
        push    es                               ;  |
        mov     al,LocINFOseg             ;Set VAR index for Local InfoSeg.
        mov     dl,DevHlp_GetDOSVar       ;Set DevHlp function.
        xor     dh,dh
        call    [DevHlp]                  ;Go get variable address.
        mov     es,ax                     ;Make ES:BX point to InfoSeg address variable.
        mov     ax,es:[bx]+2
        mov     bx,es:[bx]
        mov     es,ax                     ;Make ES:BX point to InfoSeg address variable.
        cmp     es:[bx].LIS_ProcType,LIS_PT_REALMODE  ;If VDM session
        jne     cop0504

        mov     al,[si].ci_dcb_flags1
        and     al,NOT F1_IN_DSR_SENSE  ; IDSR=OFF
        and     al,NOT F1_OUT_DSR_FLOW  ; ODSR=OFF
        and     al,NOT F1_OUT_CTS_FLOW  ; OCTS=OFF
        mov     [si].ci_dcb_flags1,al

;       mov     al,[si].ci_dcb_flags3
;       test    al,F3_FIFO_ON           ; Is FIFO enabled?
;       jz      cop0504                 ; no
;                                       ; yes
;       and     al,NOT F3_FIFO_ON       ; then,
;       or      al,F3_FIFO_OFF          ; disable FIFO
;
;       and     al,NOT F3_RX_MASK       ; set Receive Triger Level to 1 char
;       and     al,NOT F3_TX_16         ; set Transmit Triger Level to 1 char
;       mov     [si].ci_dcb_flags3,al

cop0504:
        pop     es                               ;  |
        pop     ax                               ;  |


; Rules for locking code and data segments:
;   if (port is only one open)
;       lock non-resident code and data segments;
cop0505:
.386p
        cmp     TimerHook,0             ; has any port been opened?
        ljne    codelocked              ; at least one opened so already locked

        push    si
        ; lock driver's non-resident code segment
        ; do a virttolin DevHelp (linear address)
        mov     ax,CSEG
        lea     esi,startswapcode
        mov     dl,DevHlp_VirtToLin
        DevHelp
        mov     edi,eax
        ; do a virttolin DevHelp (lockhandle)
        mov     ax,DSEG
        mov     esi,OFFSET codelockhandle
        mov     dl,DevHlp_VirtToLin
        DevHelp
        ; do a VMLock DevHelp
        mov     esi,eax         ; get offset to lock handle
        mov     eax,16          ; long term lock
        mov     ebx,edi         ; get offset to area to lock
        mov     edi,-1          ; set to no page list
        lea     ecx,endswapcode ; size to lock
        sub     ecx,OFFSET startswapcode ; size to lock
        mov     dl,DevHlp_VMLock
        DevHelp
        ljc     CmxBadLock

        ; Lock driver's non-resident data segment.

        ; If ANY enhanced ports were initialized, then this segment must
        ; be both physically contiguous and writable. This segment contains
        ; all ComInfos, and ComInfos contain the port's in/out queues.
        ; An enhanced port may do DMA operations to/from these queues, so
        ; the segment must be contiguous and writable. Since this lock is
        ; done when the FIRST port is opened, the lock must be "DMA aware"
        ; (subsequent enhanced port opens may need this).

        mov     ax,DSEG
        lea     esi,startswapdata
        mov     dl,DevHlp_VirtToLin
        DevHelp
        mov     edi,eax
        ; do a virttolin DevHelp (lockhandle)
        mov     ax,DSEG
        mov     esi,OFFSET datalockhandle
        mov     dl,DevHlp_VirtToLin
        DevHelp
        ; do a VMLock DevHelp
        mov     esi,eax         ; get offset to lock handle
        mov     ebx,edi         ; get offset to area to lock
        mov     edi,-1          ; set to no page list
        movzx   ecx,endswapdata ; size to lock
        sub     ecx,OFFSET startswapdata ; size to lock
LOCK_CONTIGUOUS EQU 0000010b
LOCK_WRITE      EQU 0001000b
LOCK_LONG_TERM  EQU 0010000b
        mov     eax,LOCK_LONG_TERM
        test    Flags,F_LOCK_CONTIGUOUS
        jz      no_dma
        or      eax,LOCK_CONTIGUOUS OR LOCK_WRITE
no_dma: mov     dl,DevHlp_VMLock
        DevHelp

        pop     si
        jnc     codelocked

        ; unlock code
        push    si
        ; do a virttolin DevHelp (lockhandle)
        mov     ax,DSEG
        mov     esi,OFFSET codelockhandle
        mov     dl,DevHlp_VirtToLin
        DevHelp
        ; do a VMUnlock DevHelp using previously saved lock handle
        mov     esi,eax         ; get offset to lock handle
        mov     dl,DevHlp_VMUnlock
        DevHelp
        pop     si

        jmp     CmxBadLock


codelocked:
.286p
        SetDS   DSEG
;;==================== Enable LID again  RAY Change Team 11\30\93 =====
; defect 77701 rdw  BUGBUG The following code was removed because of problems
;                   wtih Resource Manager on Thinkpad with PCMCIA. There could
;                   still be a problem if resource manger is not fixed.
;
      cmp     Kernel_Type, ABIOS_COM
      jne     cop06c

      mov     al,DEVICE_ID                    ; set device ID value
      mov     bl, [si].ci_port_number         ; get port number
      inc     bl
      mov     dl,DevHlp_GetLIDEntry
      xor     dh, dh
      call    [DevHlp]                     ; go for it!
      jnc     cop06d
      mov     [si].ci_LID,0                ;clear       @95202
      cmp     ax, ERROR_LID_DOES_NOT_EXIST ;PCMCIA?     @95202
      je      cop06c                       ;no LID      @95202
      mov     ax,99h                       ; 125791 We want JMP CmxInUse
      jmp     copFreeMem                   ; 125791 Free Memory if Locked
;     jmp     CmxInUse                     ; 125791 COMMENT OUT LINE

cop06d:
      mov     [si].ci_LID, ax              ; remember it for Close LID!
;;==========================================================================

cop06c:
        test    [si].ci_mult_COMs_IRQ, INT_SHARING ; sharing the IRQ with
        jnz     cop069                          ; other COM ports?

cop06z:
        mov     ax,[si].ci_isr                  ; (ds:ax) -> handler
        mov     bl,[si].ci_irq                  ; (bx) = interrupt to be set

cop07:
        xor     bh,bh
        mov     dh,[si].ci_int_sharing          ; 0 on ISA, var. on EISA
        mov     dl,DevHlp_SetIRQ                ; devhlp request code

        ; Grab the interrupt vector.
        ; The interrupt handler will ignore interrupts (disable 16450 chip interrupts
        ; and EOI) if ci_nopens is zero, so interrupts can remain enabled until
        ; the interrupt enable register is to be set.

        push    es
        push    ds
        pop     es

        setDS   HSEG                     ; DS = driver's res. data seg
        call    es:[DevHlp]
        pop     es
        setDS   DSEG

        jnc     cop06                    ; error, couldn't get IRQ

copFreeLID:
        mov     ax,[si].ci_LID                  ; set device ID value
        mov     bl, [si].ci_port_number         ; get port number
        inc     bl
        mov     dl,DevHlp_FreeLIDEntry          ; Free LID
        xor     dh,dh
        call    [DevHlp]
;       jmp     CmxGenFail                      ; 125791 Comment out line; need
                                                ; 125791 to free locked
                                                ; 125791 segments before
                                                ; 125791 failing.

copFreeMem:                                     ; 125791 Free VM Locks
.386p                                           ; 125791 Need BIG registers
        cmp     TimerHook,0                     ; 125791 Code Locked and no
                                                ;        other port open
        jne     copNoOpen                       ; 125791 Somebody must be open

        push    ax                              ; 125791 Save AX for later

                                                ; 125791 Unlock Code
        push    si                              ; 125791 Save SI
        mov     ax,ds                           ; 125791 Need linear address
        mov     esi,OFFSET codelockhandle       ; 125791 to locked code
        mov     dl,DevHlp_VirtToLin             ; 125791 segment
        DevHelp                                 ; 125791
        mov     esi,eax                         ; 125791 get offset to lock
        mov     dl,DevHlp_VMUnlock              ; 125791 handle and unlock
        DevHelp                                 ; 125791 code

                                                ; 125791 Unlock Data
        mov     ax,ds                           ; 125791 Need Linear address
        mov     esi,OFFSET datalockhandle       ; 125791 to locked data
        mov     dl,DevHlp_VirtToLin             ; 125791 segment
        DevHelp                                 ; 125791
        mov     esi,eax                         ; 125791 get offset to lock
        mov     dl,DevHlp_VMUnlock              ; 125791 handle and unlock
        DevHelp                                 ; 125791 data
        pop     si                              ; 125791 Restore SI
        pop     ax                              ; 125791 Restore AX

copNoOpen:                                      ; 125791 DosOpen has failed
.286p
        cmp     ax,99h                          ; 125791 See where to JMP to
        lje     CmxInUse                        ; 125791 Set device in use err
        jmp     CmxGenFail                      ; 125791 Set device fail error

cop069:
        pushf                                   ; Y: save interrupt flag
        cli                                     ; while updating linked list
        call    EnqComInfo                      ; add to queue
        jnc     cop053                          ; no errors, so continue
        call    DeqComInfo                      ; add to queue
        popf                                    ; restore interrupt flag
        jmp     CmxGenFail                      ; error, couldn't get IRQ

cop053:
        popf                                    ; restore interrupt flag

; We hook the timer once for the driver, not for each port.

cop06:
        cmp     TimerHook,0
        ljne    cop40                   ; timer already hooked

; The system clock interval is retrieved once, the very first time we
; get a first open.

        cmp     ClkIntrvl,0
        jne     cop30                   ; ClkIntrvl already known

        mov     al,1
        mov     dl,DevHlp_GetDOSVar
        DevHelp                         ; (ax:bx) -> SysInfoSeg
        jnc     cop10

        ComErr  <ComOpen : DOSvars unavailable>

cop10:  mov     es,ax
        mov     es,es:[bx]              ; (es:0) -> InfoSeg
        mov     SysInfoSeg,es           ; save SysInfoSeg pointer for close
        mov     ax,es:SIS_ClkIntrvl     ; (ax) = clock interval in 0.0001 secs.

; BUGBUG 11-29-88 bryand - min clock interval = 100/10000 sec
; The test below insures that we can divide the caller's timeout values
; by the system clock interval and still store the results in a word.

        cmp     ax,100
        jae     cop20                   ; safe timeout value

        ComErr  <ComOpen : system timeout value too small>

cop20:  mov     ClkIntrvl,ax            ; save the value

; Hook the timer

cop30:  mov     dl,DevHlp_SetTimer
        mov     ax,OFFSET Ticker

        setES   DSEG                            ; ES = seg where DevHlp is
        setDS   HSEG                            ; DS = driver's res. data seg
        call    es:[DevHlp]
        setDS   DSEG

        jnc     cop40                   ; sucessfully got timer

        ; could not hook timer - unset IRQ and fail the open
        test    [si].ci_mult_COMs_IRQ, INT_SHARING ; sharing the IRQ with
                                                ;  other COM ports?
        jz      cop35                           ; N: unset normally
        pushf                                   ; Y: save interrupt flag
        cli                                     ; while updating linked list
        call    DeqComInfo                      ; remove from queue
        popf                                    ; restore interrupt flag
        jmp     CmxGenFail                      ;  then continue

cop35:
        xor     bh,bh
        mov     bl,[si].ci_irq
        mov     dl,DevHlp_UnSetIRQ
        setES   DSEG                            ; ES = seg where DevHlp is
        setDS   HSEG                            ; DS = driver's res. data seg
        call    es:[DevHlp]
        setDS   DSEG
        jmp     CmxGenFail


cop40:  inc     TimerHook               ; got timer - inc count
        FlushQueue      ci_qin          ; Flush the I/O queues.
        FlushQueue      ci_qout

; Initialize dcb values and internal flags needed on a first open.

        mov     [si].ci_dcb_XonChar,xonequ
        mov     [si].ci_dcb_XoffChar,xoffequ

        mov     [si].ci_dcb_writeto,WRITE_TO_INIT
        mov     [si].ci_dcb_readto,READ_TO_INIT

        xor     ax,ax                   ; (AX) = 0
        mov     [si].ci_dcb_ErrChar,al
        mov     [si].ci_dcb_BrkChar,al

        and     [si].ci_dcb_flags2,NOT (F2_ERR_CHAR OR F2_NULL_STRIP OR F2_BRK_CHAR)
        and     [si].ci_dcb_flags3,NOT F3_READ_TO_MASK
        or      [si].ci_dcb_flags3,F3_READ_TO_NORM

        mov     [si].ci_event,ax
        mov     [si].ci_comerr,ax
        mov     [si].ci_hsflag,al

        mov     al,[si].ci_bytesize
        mov     ah,[si].ci_parity
        mov     ch,[si].ci_stopbits
        ;
        ; reset VDM stuff for this port
        ;
;    IF (ComInfo.vdm_flag.InUse = TRUE)
        test    [si].ci_vdm_flag,VDM_Flag_InUse ;@VDM
        jz      vdm_sup010                      ;@VDM
;    THEN
        or      [si].ci_dcb_flags2,F2_BRK_CHAR  ;break replace active

        or      [si].ci_vdm_flag,VDM_Flag_notify_the_VCOM_TX   ;@VDM
        or      [si].ci_vdm_flag,VDM_Flag_notify_the_VCOM_RX   ;@VDM
        mov     [si].ci_vdm_Rx_State,1          ;@VDM set to Rx state 1
        mov     [si].ci_vdm_Tx_State,1          ;@VDM set to Tx state 1
        mov     [si].ci_vdm_Rx_Count,0                         ;@VDM
        mov     [si].ci_vdm_Tx_Count,0                         ;@VDM
        mov     [si].ci_vdm_LastMSR,0                          ;@VDM

vdm_sup010:                             ; not in use by VDM

        call    CheckLCR                        ; set up line control reg.
        jnc     cop50

        ComErr  <ComOpen : invalid line control>

cop50:


        call    SetLineC          ; set LCR

;
;       vdm NO FIFO
;
;       test    [si].ci_vdm_flag,VDM_Flag_InUse
;       jnz     cop55

; ComputeAPO MUST be called before SetBaud so SetBaud can set
; the timeouts based on the baud rate.

        call    ComputeAPO                  ; compute FIFO modes

cop55:
.386p
        mov     eax,[si].ci_baud
.286p
        call    SetBaud                         ; set baud rate and timeouts
        jnc     short cop70

        ComErr  <SetBaud : invalid baud rate>


; Standard port: initialize UART registers.
cop70:  mov     dx,[si].ci_port

; WARNING: From here to the end of ComOpen (dx) is used to address a number
; of different ports. These port addresses are computed relative to the
; address currently in (dx). Any changes to this code should be sensitive
; to the order in which things occur.

; rdw 81245 -  The following code has been added to handle bug on SMC UART's
;              per the recommendation from from SMC Engineering.
; jag 102436 - Make sure interrupts are turned off at chip before prodding.
; PMS 139350 - Only run fix for 16550a (FIFO) chips as some non FIFO chips
;              give spurious interrupts if the IIR is written to.

; check for 16550A (FIFO)
public smcuart1
smcuart1:
        test    [si].ci_flagx1, FX1_PCMCIA_MODEM
        jnz     rlines

        test    [si].ci_flagx, FX_16550A; Only needed for 16550a      ;139350;
        jz      rlines                  ; FIFO not available, skip    ;139350;

        xor     ax,ax                   ; clear ax - disable all ints
        add     dx,R_INTEN              ; (dx) -> interrupt enable reg
        mout    dx,al                   ; turn off ints

        add     dx, R_FIFOC-R_INTEN             ; 81245 (dx) -> FIFO Control Register
        mov     al, FF_CLEAR_RX OR FF_CLEAR_TX  ; 81245
        mout    dx, al                  ; 81245 try to turn of the 16550a FIFOs

        add     dx, R_MODMC-R_FIFOC     ; 81245 (dx) -> MCR
        min     al, dx                  ; 81245 Get current value of MCR
        or      al, MC_LOOP             ; 81245 Set loopback ON to clear THR

        mout    dx, al                  ; 81245
        add     dx, R_DATA-R_MODMC      ; 81245 (dx) -> RBR
        min     al, dx                  ; 81245 Clear the 1 byte RBR

; 81245 At this point, we don't have to worry about clearing the loop back
;       in the MCR as we will do this later when we clear MCR.
rlines:
        add     dx,R_LINES              ; (dx) -> line status reg
        min     al,dx                   ; (al) = line status (clear it)
        mov     [si].ci_lsrshadow,al    ; shadow the contents of the LSR

        add     dx,R_DATA-R_LINES       ; (dx) -> data reg
        min     al,dx                   ; clear RX buffer

        add     dx,R_FIFOC-R_DATA       ; (dx) -> fifo control reg
        test    [si].ci_dcb_flags3,F3_FIFO_HW_ON
        jz      cop75                   ; FIFO HW off
        mov     al,[si].ci_dcb_flags3   ; (al) = flags3
        and     al,F3_RX_MASK           ; (al) = RX trigger level
        shl     al,1                    ; (al) = RX trigger level bits for FIFO
        or      al,FF_ENABLE OR FF_CLEAR_RX OR FF_CLEAR_TX
        mout    dx,al                   ; clear RX and TX FIFOs

cop75:  .errnz  R_INTID-R_FIFOC
;        add     dx,R_INTID-R_FIFOC      ; (dx) -> int id reg.
        min     al,dx                   ; clear int id

        mov     [si].ci_msrshadow,0     ; zero previous msrshadow
        add     dx,R_MODMS-R_INTID      ; (dx) -> modem status reg.
        min     al,dx                   ; clear modem status
        call    MxInt                   ; update msrshadow

        xor     bx,bx                   ; show 0 for previous flags1 and flags2
        call    ComputeHHS              ; set up handshake state and
                                        ; initialize the MCR

        call    EnableRemoteTX          ; we're all set up, tell the remote TXer

        cli                     ; disable processor interrupts before
                                ; enabling 16450 chip interrupts

; Enable Receive Data and nModem Status interrupts.
; TX Holding Register interrupt will be enabled by CheckTx.
        mov     dx,[si].ci_port         ; (dx) -> port


public smcuart2
smcuart2:
        mov     al,IE_RX OR IE_MX       ; (al) = interrupts to enable
        add     dx,R_INTEN              ; (dx) -> interrupt enable reg
        mout    dx,al

cop80:


        inc     [si].ci_nopens          ; count the open calls

cop90:
        push    es                        ;CP20D1390 Store the Session ID
        push    ax
        mov     al,LocINFOseg             ;Set VAR index for Local InfoSeg.
        mov     dl,DevHlp_GetDOSVar       ;Set DevHlp function.
        xor     dh,dh
        call    [DevHlp]                  ;Go get variable address.
        mov     es,ax                     ;Make ES:BX point to InfoSeg address variable.
        mov     ax,es:[bx]+2
        mov     bx,es:[bx]
        mov     es,ax                     ;Make ES:BX point to InfoSeg address variable.
        mov     ax,es:[bx].LIS_CurScrnGrp ;Get Screen Group ID = Session ID
        mov     [si].ci_sid,ax            ;Save the Session ID
        pop     ax
        pop     es

cop100:
        sti                     ; enable processor interrupts now that
                                ; UART/ESP interrupts are enabled and the
                                ; open count has been incremented.

        jmp     CmxDone                 ; done, no error

EndProc ComOpen


;**     ComClose - close request handler
;
;       ComClose performs most of its functions if it the close request
;       leaves no pending opens on the device. If there are pending opens
;       then ComClose does nothing. In the case where the close request
;       will close the device, the following major functions are performed:
;
;               - I/O queues are flushed
;               - XON is sent if a previous XOFF was sent
;               - async interrupts are disabled
;               - the TX hardware is flushed
;               - the timer hook and IRQ are released
;
;       ENTRY   (ds:si) -> ComInfo
;
;       EXIT    open count decremented, device may be closed
;
;       USES    ax bx cx dx di

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

;/*********************************************************************/
;/* Need to ensure mutual exclusion between opens, closes and ioctls. */
;/*********************************************************************/

.386p

        call    Get_OCI_Sem                     ; will not return if error

        ChkComInfoPtr
        ChkRPPtr
        ChkRPType       CMDClose

        cmp     [si].ci_nopens,0
        je      CmxDone                         ; done, no opens

ccl1:   cmp     [si].ci_nopens,1
        jne     ccl99           ; not the last close, port is still open


;/*********************************************************************/
;/* Set the last close flag so that the RX interrupt service routine  */
;/* will not enqueue any more data.                                   */
;/*********************************************************************/

        test    [si].ci_vdm_flag,VDM_Flag_No_IRQ_Open
        jnz     ccl99

        or      [si].ci_flagx,FX_LAST_CLOSE     ; flag last close in progress

        mov     [si].ci_sid,0           ; Clear the Session ID (@0001)

        SaveReg <es,di>                 ; 103070-1 Keep our Req. Packet PTR
        call    ComWFlushSub            ; flush all outstanding write requests
        call    ComRFlushSub            ; flush all outstanding read  requests
        RestoreReg <di,es>              ; 103070-1 Restore our Req. Packet PTR

        mov     al,[si].ci_linec        ; (al) = line control value
        and     al,NOT LC_BREAK         ; turn off break
        and     [si].ci_hsflag, NOT HS_BREAK_SET
                                        ; break no longer set
        call    SetLineC

        ; Standard port:
ccl13:  call    EnableRemoteTX          ; send an XON if XOFF previously sent

        mov     dx,[si].ci_port
        add     dx,R_INTEN              ; (dx) -> interruprt enable reg
        xor     al,al
        mout    dx,al                   ; disable all com interrupts

        test    [si].ci_mult_COMs_IRQ, INT_SHARING ; sharing the IRQ with
                                        ; other COM ports?
        jz      ccl12                   ; N: unset normally
        pushf                           ; Y: save interrupt flag
        cli                             ; while updating linked list
        call    DeqComInfo              ; remove from queue
        popf                            ; restore interrupt flag
        jmp     short ccl15             ;  then continue

ccl12:

ccl14:
        xor     bh,bh
        mov     bl,[si].ci_irq
        mov     dl,DevHlp_UnSetIRQ
        push    es
        push    ds
        pop     es
        setDS   HSEG
        call    es:[DevHlp]             ; release the IRQ
        pop     es
        setDS   DSEG
        jnc     ccl15

        ComErr  <ComClose : could not release the IRQ>

        ; BUGBUG Should we compute wait time based on byte size too?
        ; if either DTR or RTS is ON, we need to wait:
        ;   for data to get out of TX hardware (THRE & TSRE)
        ;     up to 2 character times (.5 char time at a time up to 1 second)
        ;   for data to be received by remote
        ;     10 character times up to a 1 second maximum
        ; before turning OFF DTR and RTS.

ccl15:  mov     dx,[si].ci_port
        add     dx,R_MODMC              ; (dx) -> modem control reg
        min     al,dx                   ; al = modem output signals
        test    al,MC_DTR OR MC_RTS
        jz      ccl60                   ; lines are already down, don't wait

        ; wait for data to get out of TX hardware (THRE & TSRE)
        ; 6000 (bits*ms/sec) = 12 bits * .5 chars * 1000 (ms/sec)
        ; ms = 6000 (bits*ms/sec) / baud (bits/sec)

        xor     edx,edx
        mov     eax,06000d              ; edx:eax = 6000d

        ; trap 0 problem: ci_baud MUST be > 0 for the div to work
        ;                 the result MUST fit into ax for the div to work
        ;                 (dx = 0 so the above is true)

        cmp     [si].ci_baud,0
        je      ccl151
        div     [si].ci_baud            ; ax = ms for 6 bits
        jmp     short   ccl152
ccl151:
        mov     ax,998d                 ; set AX value for ci_baud = 0
ccl152:

        inc     ax                      ; round up
        mov     cx,ax                   ; (0:cx)  = timeout
        cmp     cx,1000d
        jb      ccl20                   ; cx < 1 second timeout

        mov     cx,1000d                ; cx = 1 second timeout

ccl20:  ; don't have to use ReadLSR because IRQ is already disabled
        mov     dx,[si].ci_port
        add     dx,R_LINES              ; (dx) -> LSR
        min     al,dx                   ; (al) =  LSR
        and     al,LS_THRE OR LS_TSRE
        cmp     al,LS_THRE OR LS_TSRE
        je      ccl30                   ; transmit hardware already empty
        xor     dx,dx                   ; (dx:cx) = timeout value
        cli                             ; 103070-1 must cli before block
        call    ProcBlockNI             ; wait for data to get out of hardware
        jmp     SHORT ccl20             ; go check if done

        ; wait 10 char times for data to be received by remote (1 sec max)
        ; 120000 (bits*ms/sec) = 12 bits * 10 chars * 1000 (ms/sec)
        ; ms = 120000 (bits*ms/sec) / baud (bits/sec)
        ; NOTE: baud must be >= 2 or div will overflow
        ;
        ; timeout = (120000/baud)+1
        ; if (timeout > 1000)
        ;     timeout = 1000
        ; endtime = MsCount + timeout
        ; do {
        ;     block(timeout)
        ;     timeout = endtime - MsCount
        ; } while (timeout > 0);

; Begin fix for defect 177200
ccl30:  xor     edx,edx                 ; clear 32 bit register edx
        mov     eax,01d4c0h             ; == 120000 ==
                                  ; (12 bits/char * 10 chars * 1000 ms/sec)
; @177200    ccl30:  mov     dx,1
; @177200            mov     ax,0d4c0h               ; (dx:ax) = 120000
; End fix for defect 177200

        cmp     [si].ci_baud,2          ; remove trap 0
        jb      ccl301
        div     [si].ci_baud            ; eax = ms for 12 bits
        jmp     short   ccl302
ccl301:
        mov     ax,998d
ccl302:

        inc     ax                      ; round up
        mov     cx,ax
        xor     dx,dx                   ; (dx:cx) = timeout value
        cmp     cx,1000d
        jb      ccl40                   ; cx < 1 second timeout

        mov     cx,1000d                ; cx = 1 second timeout

ccl40:  ; get current time (running MS count from sysinfoseg)
        SaveReg         <ds>
        mov     ax,SysInfoSeg           ; get SysInfoSeg pointer for close
        mov     ds,ax
        cli                             ;JGT  disable to read MsCount
        mov     ax,ds:SIS_MsCount._hi
        mov     bx,ds:SIS_MsCount._lo   ; (ax:bx) = running MS count
        sti                             ;JGT  and reenable
        RestoreReg      <ds>
        add     bx,cx
        adc     ax,0                    ; (ax:bx) = completion MS count

ccl50:  SaveReg         <ax,bx>         ; save completion MS count

        cli                             ; 103070-1 must cli before block
        call    ProcBlockNI             ; wait for remote to get data
        ; blocks NON-INTERRUPTABLY for up to 1 second
        ; if the block times out, then the full time was waited;
        ; else spurious ProcRun, we have to make sure enough time elapsed

        RestoreReg      <bx,ax>         ; (ax:bx) = completion MS count
        jc      ccl60                   ; block timed out, were done

        SaveReg         <ds>
        mov     cx,SysInfoSeg
        mov     ds,cx                   ; (ds:0) -> SysInfoSeg
        mov     cx,bx
        mov     dx,ax                   ; (dx:cx) = completion MS count
        cli                             ;JGT  disable to read MsCount
        sub     cx,ds:SIS_MsCount._lo
        sbb     dx,ds:SIS_MsCount._hi   ; (dx:cx) = MS remaining
        sti                             ;JGT  and reenable
        RestoreReg      <ds>

        js      ccl60                   ; remaining is neg, done
        jnz     ccl50                   ; remaining is NZ, not done
        or      cx,cx
        jnz     ccl50                   ; remaining is NZ, not done

ccl60:  mov     dx,[si].ci_port
        add     dx,R_MODMC              ; (dx) -> modem control reg
        xor     al,al
        mout    dx,al                   ; turn off all MCR signals


ccl70:

        ; Unhook timer if all devices closed
        dec     TimerHook
        jnz     ccl90                   ; one of the ports still has
                                        ; outstanding opens

        mov     dl,DevHlp_ResetTimer
        mov     ax,OFFSET Ticker

        setES   DSEG
        setDS   HSEG
        call    es:[DevHlp]
        setDS   DSEG

        ; unlock code
        push    si
        ; do a virttolin DevHelp (lockhandle)
        mov     ax,ds
        mov     esi,OFFSET codelockhandle
        mov     dl,DevHlp_VirtToLin
        DevHelp
        ; do a VMUnlock DevHelp using previously saved lock handle
        mov     esi,eax         ; get offset to lock handle
        mov     dl,DevHlp_VMUnlock
        DevHelp
        pop     si

        ; unlock common data
        push    si
        ; do a virttolin DevHelp (lockhandle)
        mov     ax,ds
        mov     esi,OFFSET datalockhandle
        mov     dl,DevHlp_VirtToLin
        DevHelp
        ; do a VMUnlock DevHelp using previously saved lock handle
        mov     esi,eax         ; get offset to lock handle
        mov     dl,DevHlp_VMUnlock
        DevHelp
        pop     si

        jnc     ccl90

        ComErr  <ComClose : could not reset timer>

ccl90:  and     [si].ci_flagx,NOT FX_LAST_CLOSE  ; clear last close flag

        cmp     Kernel_Type, ABIOS_COM
        jne     ccl99

        mov     ax, [si].ci_LID            ; retrieve the LID for him
        mov     dl, DevHlp_FreeLIDEntry
        call    [DevHlp]


ccl99:  dec     [si].ci_nopens          ; one less open

        cmp     [si].ci_nvdmopens,0     ; CP20D1390 If special VDM open was done
        jle     ccl100                  ; then
        dec     [si].ci_nvdmopens       ; decrement the counter
ccl100:
        jmp     CmxDone

.286p

EndProc ComClose



;**     ComDeInstall - deinstall request handler
;
;       Deinstallation is not supported.
;
;       ENTRY
;
;       EXIT
;
;       USES

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

; Since we are not supporting deinstallation, we optimize the handling
; of this request by making its table entry CmxUnknown.

;EndProc ComDeInstall


;**     ComRead - read request handler
;
;       ComRead handles read requests for the driver. If there are no
;       outstanding requests, we will attempt to satisfy the current
;       one immediately. If we can't then we will block the thread
;       until we can satisfy it or we time out. Once the thread wakes
;       up from the block it will attempt to satisfy its request.
;
;       ENTRY   (ds:si) -> ComInfo
;               (es:di) -> request packet
;
;       EXIT
;
;       USES    al cx dx si

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

        ChkComInfoPtr
        ChkRPPtr
        ChkRPType  CMDINPUT

ifdef PERFVIEW
        pvw_Read START                  ;@PVW start timer
endif

        cli

        cmp     [si].ci_r_rp._hi,0
        jne     crr30           ; current request active, queue this one


crr_normal_req:
; If there are no outstanding read requests we attempt to satisfy the
; one we just got.

        sti

        mov     cx,es:[di].IOcount      ; (cx) = requested bytes
        or      cx,cx
ifdef PERFVIEW
        ljz     crrx                    ; request is for 0 bytes, done;@PVW
else
        ljz     CmxDone
endif

        mov     [si].ci_r_to_move,cx    ; initialize number of bytes to move

        ; set up GDT selector which we allocated at init time
        ;; PhysToGDTSel (ComInfo[si].GDTSelRead, physical @, length)
        ;; move ComInfo[si].GDTSelRead to Physical @. _hi
        ;; move 0 to Physical @. _lo
        push    si
        mov     si,[si].ci_GDTSelRead
        ; es:di -> request packet
        call    SetUpGDT
        pop     si

        cmp     [si].ci_qin.ioq_count,0
        je      crr00                   ; no data in queue

        call    ReadQueue               ; returns (cx) = bytes not dequeued
                                        ; updates ci_r_to_move

; The current request may be satisfied if any of the following are true
;       - all the data requested was moved (ci_r_to_move == cx == 0)
;       - read mode is no wait
;       - read mode is wait for something and something was moved
;               (ci_r_to_move != IOcount)

; If the request cannot be completed now, it is either normal timeout
; mode or 'wait for something' mode and nothing received yet.
; If it is normal timeout mode, the request will be run when there is
; enough data in the RXQ to satisfy the request (RxInt) or when the
; request times out (ReadTick).
; If it is 'wait for something' mode, the request will be run when there
; is ANY data in the RXQ (RxInt) or when the request times out (ReadTick).

        jcxz    crr10                   ; all the data was moved (done)

crr00:  mov     al,[si].ci_dcb_flags3
        and     al,F3_READ_TO_MASK      ; al = read timeout mode
        cmp     al,F3_READ_TO_NW
        je      crr10                   ; read mode is no wait (done)

        cmp     al,F3_READ_TO_NORM
        je      crr20                   ; read mode normal (block)

        cmp     cx,es:[di].IOcount
        je      crr20                   ; nothing moved and wait for something

crr10:  sub     es:[di].IOcount,cx      ; update number of bytes read
ifdef PERFVIEW
        jmp     crrx                    ; request is done   ;@PVW
else
        jmp     CmxDone
endif

; If we get here we couldn't satisfy the request. This means
; that we couldn't completely satisfy a normal request or there
; was nothing to move for a wait for something request.

crr20:  cli

        mov     [si].ci_r_rp._hi,es     ; save request packet pointer
        mov     [si].ci_r_rp._lo,di

        mov     cx,[si].ci_r_to_start   ; initialize request time out value
        mov     [si].ci_r_to,cx

        jmp       SHORT crr40             ; go block


crr30:
; We get here when there is already a current request, so this request
; must be added to the read request packet list and processed later.

        SaveReg         <si>
        lea     si,[si].ci_r_rpl        ; (ds:si) -> read request packet list
        call    LinkRP                  ; add RP to list of reads
                                        ; outstanding requests
        RestoreReg      <si>            ; (ds:si) -> ComInfo


crr40:
; Block until the request:
;    completes  - normal wait mode - ProcRun'ed - carry clear
;                 enough data in qin to finish request
;
;    completes  - no wait mode - ProcRun'ed - carry clear
;                 may be some data in qin (could be 0)
;                 may be enough data in qin to finish request (could be 0)
;
;    completes  - wait for something - ProcRun'ed - carry clear
;                 some data in qin (could be 1)
;                 may be enough data in qin to finish request (could be 1)
;
;    times out  - ProcRun'ed - carry clear
;                 may not be enough data in qin to finish request
;
;    is flushed - ProcRun'ed - carry clear
;                 request packet shows zero bytes requested
;
;    block is interrupted (usually for a signal to the thread).
;                 may be enough data in qin to finish request (could be 0)

ifdef PERFVIEW
        pvw_Read STOP                   ;@PVW Stop perfview timer
endif

        mov     cx,-1                   ; (dx:cx) = infinite time out
        mov     dx,cx
        call    ProcBlock

        ChkRPType       CMDINPUT

ifdef PERFVIEW
        pushf                           ;@PVW save flags
        pvw_Read START                  ;@PVW start timer again
        popf                            ;@PVW restore flags
endif
        cli

        jc      crr50                   ; interrupted ProcBlock

        test    es:[di].PktStatus,STDON
        jz      crr40                   ; spurious ProcRun go ProcBlock again

; We were run, lets finish the request.

        jmp     SHORT crr60

; Interrupted ProcBlock.

crr50:  cmp     di,[si].ci_r_rp._lo
        jne     crr80                   ; not the current request
        mov     ax,es
        cmp     ax,[si].ci_r_rp._hi
        jne     crr80                   ; not the current request

; As the current request we were either run or interrupted.
; Either way, we will finish the request know.

crr60:  mov     cx,es:[di].IOcount
        jcxz    crr70                   ; request is satisfied (zero length)
                                        ; could have been forced to zero
                                        ; because it was flushed

        mov     cx,[si].ci_r_to_move    ; (cx) = number of bytes to transfer
        jcxz    crr70                   ; already done

        cmp     [si].ci_qin.ioq_count,0
        je      crr70                   ; there is nothing to transfer

        sti
        call    ReadQueue               ; returns (cx) = bytes not dequeued
                                        ; updates ci_r_to_move
        cli

crr70:  sub     es:[di].IOcount,cx      ; update number of bytes transfered
        mov     [si].ci_r_rp._hi,0      ; no longer current
        call    StartNextRRP            ; start the next read request
ifdef PERFVIEW
        jmp     SHORT crrx              ; this request is done
else
        jmp     CmxDone
endif

; The ProcBlock was interrupted but we are not the current request.
; So we have to pull ourselves out of the read request list.

crr80:  cmp     [si].ci_r_rpl._hi,0     ; 103070-1 See if RPL exists
        je      crr81                   ; 103070-1 No, must be ci_r_rp._hi=0
        lea     si,[si].ci_r_rpl        ; (ds:si) -> read request packet list
        call    UnLinkRP                ; unlink the specific interrupted RP

crr81:                                  ; 103070-1 Clear IO count and return
        mov     es:[di].IOcount,0       ; interrupted, therefore 0 bytes moved

ifdef PERFVIEW
crrx:
        pvw_Read STOP,DOCOUNTERS        ;@PVW stop timer, inc num_reads,
                                        ;@PVW update bytes read
        jmp     CmxDone
else
        jmp     CmxDone                 ; 103070-2 no Perfview enabled
endif

EndProc ComRead


;**     ComWrite - write request handler
;
;       ComWrite handles write requests for the driver. When we get a write
;       request we check to see if there are any others active. If there
;       aren't we transfer as much data into the queue as we can. Either
;       way we then block the process until we send the data out the hardware.
;       When we wake up from the block we adjust the number of bytes moved
;       and return. If we are interrupted and are not the current request
;       we pull ourselves out of the queue and set the number of bytes
;       transferred to zero.
;
;       ENTRY   (ds:si) -> ComInfo
;               (es:di) -> request packet
;
;       EXIT
;
;
;       USES    ax bx cx

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

        ChkComInfoPtr
        ChkRPPtr

IFDEF RPTSTRICT
        mov     es:[di].PktCmd,CMDOUTPUT        ; write with verity => write
ENDIF

ifdef PERFVIEW
        pvw_Write START                  ;@PVW start timer
endif

        cli

cwr_normal_req:
        cmp     [si].ci_w_rp._hi,0
        jne     cwr1                    ; no current request

; If there are no outstanding write requests we move as much data
; as we can into the output queue.

        sti

        mov     cx,es:[di].IOcount
        or      cx,cx
ifdef PERFVIEW
        ljz     cwrx                    ; request is for 0 bytes, done;@PVW
else
        ljz     CmxDone
endif

        ; set up GDT selector which we allocated at init time
        ;; PhysToGDTSel (ComInfo[si].GDTSelWrite, physical @, length)
        ;; move ComInfo[si].GDTSelWrite to Physical @. _hi
        ;; move 0 to Physical @. _lo
        push    si
        mov     si,[si].ci_GDTSelWrite
        ; es:di -> request packet
        call    SetUpGDT
        pop     si

        call    WriteQueue              ; returns (cx) = bytes not enqueued
                                        ; updates ci_w_to_move

        cli

        mov     [si].ci_w_rp._hi,es     ; save request packet pointer
        mov     [si].ci_w_rp._lo,di

        mov     cx,[si].ci_w_to_start   ; initialize request time out value
        mov     [si].ci_w_to,cx

        SaveReg         <di>            ; save request pkt ptr
        call    CheckTX                 ; adjust TX empty interrupt
        RestoreReg      <di>            ; restore request pkt ptr
        jmp     SHORT cwr2

; Whether or not we are the current request we have to block until
; the data moves out of the hardware.

cwr1:   SaveReg         <si>
        lea     si,[si].ci_w_rpl        ; (ds:si) -> write request packet list
        call    LinkRP                  ; add RP to list of writes
        RestoreReg      <si>            ; (ds:si) -> ComInfo

; Now that we have linked the request we want to block it
; until we are able to satisfy it.

cwr2:
ifdef PERFVIEW
        pvw_Write STOP                  ;@PVW stop timer
endif

        mov     cx,-1                   ; (dx:cx) = no ProcBlock time out
        mov     dx,cx
        call    ProcBlock

        ChkRPType       CMDOUTPUT

ifdef PERFVIEW
        pushf                           ;@PVW save flags
        pvw_Write START                 ;@PVW start timer again
        popf                            ;@PVW restore flags
endif

        cli

        jc      cwr3                    ; interrupted ProcBlock

        test    es:[di].PktStatus,STDON
        jz      cwr2                    ; spurious ProcRun go ProcBlock again
;;MF    jmp     cwrx                    ; valid ProcRun, request done  ;@PVW
        jmp     cwr3_5          ;;MF    ; valid ProcRun, run next request


; Interrupted ProcBlock.

cwr3:   test    es:[di].PktStatus,STDON
;;MF    jnz     cwrx                    ; request already complete
        jnz     cwr3_5                  ; request complete, run next request

        cmp     di,[si].ci_w_rp._lo
        jne     cwr4                    ; not the current request
        mov     ax,es
        cmp     ax,[si].ci_w_rp._hi
        jne     cwr4                    ; not the current request

; Current request was interrupted.

        mov     [si].ci_w_rp._hi,0      ; no longer a current request
        mov     ax,[si].ci_w_to_move
        add     ax,[si].ci_qout.ioq_count
        sub     es:[di].IOcount,ax      ; number of bytes sent to HW

        FlushQueue      ci_qout

        sti

cwr3_5:                 ;;MF
        mov     [si].ci_w_rp._hi,0      ; no longer a current request
        call    StartNextWRP            ; start the next write request

ifdef PERFVIEW
        jmp     SHORT cwrx              ; this request is done
else
        jmp     CmxDone
endif


; The ProcBlock was interrupted but we were not the current request.

cwr4:   cmp     [si].ci_w_rpl._hi,0     ; 103070-1 Check to see that RPL exists
        je      cwr4_5                  ; 103070-1 No, must be ci_w_rp._hi=0
        lea     si,[si].ci_w_rpl        ; (ds:si) -> write request packet list
        call    UnLinkRP                ; unlink the specific interrupted RP
cwr4_5:                                 ; 103070-1 Clear IO Count and return
        mov     es:[di].IOcount,0       ; interrupted therefore 0 bytes written

ifdef PERFVIEW
cwrx:
        pvw_Write STOP,DOCOUNTERS       ;@PVW stop timer, inc num_writes,
                                        ;@PVW update bytes written
        jmp     CmxDone
else
        jmp     CmxDone                 ; 103070-2 no PERVIEW enabled
endif

EndProc ComWrite


;**     ComNDRead - Non-destructive read request handler
;
;       ComNDRead will return the first character in the input queue
;       if there is one. Otherwise it returns an error. The read is
;       non-destructive in that the character is not removed from the
;       queue.
;
;       ENTRY   (ds:si) -> ComInfo
;               (es:di) -> request packet
;
;       EXIT
;
;       USES    bx

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

        ChkComInfoPtr
        ChkRPPtr

;       test    [si].ci_vdm_flag,VDM_Flag_InUse  ;CP20D1390  -- Fix CP20PB726384
;       jz      cndr_normal_req                  ; If the port is opened with special
;       cmp     [si].ci_nvdmopens,0              ; vdm access, none distructive Read is not allowed.
;       ljg     CmxInUse                         ;+yn error General Failure

cndr_normal_req:
        cmp     [si].ci_qin.ioq_count,0
        lje     CmxBusy                 ; no data in queue, device busy

        mov     bx,[si].ci_qin.ioq_out  ; Get the first char
        mov     bl,[bx]
        mov     es:[di].NDRbyte,bl
        jmp     CmxDone                 ; Return w/ "Done, no error."

EndProc ComNDRead


;**     ComRStat - input status request handler
;
;       We return busy if the input queue is empty or
;       if there is an outstanding read request.
;       Otherwise we return done.
;
;       ENTRY   (ds:si) -> ComInfo
;
;       EXIT
;
;       USES    NONE

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

        cmp     [si].ci_qin.ioq_count,0
        lje     CmxBusy                 ; queue is empty

        cmp     [si].ci_r_rp._hi,0
        ljne    CmxBusy                 ; request outstanding

        jmp     CmxDone                 ; we're not busy

EndProc ComRStat


;**     ComWStat - output status request handler
;
;       We return busy if there is an outstanding write request
;       or we are unable to transmit.
;       Otherwise we return done.
;
;       ENTRY   (ds:si) -> ComInfo
;
;       EXIT
;
;       USES    al

Procedure ComWStat,NEAR

        ChkComInfoPtr

        cmp     [si].ci_w_rp._hi,0
        ljne    CmxBusy                 ; request outstanding


; If a BREAK is set or an XOFF was sent or recieved, we can not transmit.

        test    [si].ci_hsflag,HS_BREAK_SET OR HS_XOFF_RECEIVED OR HS_XOFF_SENT
        ljnz    CmxBusy

        mov     al,[si].ci_msrshadow    ; (al) = msr_shadow
        and     al,[si].ci_outhhslines  ; mask bits of interest
        cmp     al,[si].ci_outhhslines
        ljne    CmxBusy                 ; modem lines down

        jmp     CmxDone                 ; we're not busy


EndProc ComWStat




;**     ComRFlush - read flush request handler
;**     ComWFlush - write flush request handler
;
;       ENTRY   (ds:si) -> ComInfo
;
;       EXIT
;
;       USES

Procedure ComRFlush,NEAR
;                                                 -- Fix CP20PB726384
;       test    [si].ci_vdm_flag,VDM_Flag_InUse ;CP20D1390 If the port is opened
;       jz      crf_normal_req                  ; with special vdm access,
;       cmp     [si].ci_nvdmopens,0             ; Read Buffer Flush is not allowed.
;       ljg     CmxInUse                        ;+yn error General Failure

crf_normal_req:
        call    ComRFlushSub

cr10:   call    EnableRemoteTX          ; send an XON if XOFF previously sent
        jmp     CmxDone



Entry ComWFlush,,,nocheck
        ASSUME cs:CSEG,ds:DSEG,es:NOTHING,ss:NOTHING

        call    ComWFlushSub
        jmp     CmxDone

EndProc ComRFlush

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

        ChkComInfoPtr
;                                                 -- Fix CP20PB726384
;       test    [si].ci_vdm_flag,VDM_Flag_InUse ;CP20D1390 If the port is opened
;       jz      cwf_normal_req                  ; with special vdm access,
;       cmp     [si].ci_nvdmopens,0             ; Write Buffer Flush is not allowed.
;       ljg     CmxInUse                        ;+yn error General Failure

cwf_normal_req:

        cli

        or      [si].ci_Flagx1,FX1_FLUSH_RX_IP ;;MF

        FlushQueue      ci_qin

        cmp     [si].ci_r_rp._hi,0
        je      crf1                    ; no current request

        les     di,[si].ci_r_rp         ; (es:di) -> current read request
        ChkRPPtr
        ChkRPType       CMDINPUT
        mov     es:[di].IOcount,0       ; update number of bytes transfered

        mov     es:[di].PktStatus,STDON ;mw mark request done
        mov     [si].ci_r_rp._hi,0      ; no more read requests

        sti

        call    ProcRun

crf1:   push    si
        lea     si,[si].ci_r_rpl        ; (ds:si) -> read request packet list
        jmp     SHORT cmf1


Entry ComWFlushSub,,,nocheck

        cli

        or      [si].ci_Flagx1,FX1_FLUSH_TX_IP ;;MF

        mov     dx,[si].ci_port
        add     dx,R_INTEN              ; (dx) -> interrupt enable reg
        in      al,dx
        and     al,NOT IE_TX            ; disable TX interrupts first
        out     dx,al

        ChkComInfoPtr

        mov     cx,[si].ci_qout.ioq_count

        FlushQueue      ci_qout

        cmp     [si].ci_w_rp._hi,0
        je      cwf1                    ; no current requests

        les     di,[si].ci_w_rp         ; (es:di) -> current write request
        ChkRPPtr
        ChkRPType       CMDOUTPUT
        add     cx,[si].ci_w_to_move
        sub     es:[di].IOcount,cx      ; update number of bytes transfered

        mov     es:[di].PktStatus,STDON ;mw mark request done

        mov     [si].ci_w_rp._hi,0      ; no more write requests
        mov     [si].ci_w_to_move,0   ;;MF reset the # of bytes left to move

        sti

        call    ProcRun

cwf1:   push    si
        lea     si,[si].ci_w_rpl        ; (ds:si) -> write request packet list

; We enter here, after the current requests are flushed, in
; order to flush all outstanding requests.

cmf1:   sti

;BUGBUG must remove all requests from the list BEFORE enabling interrupts.
;unless ci_r_rp is always checked before trying to use ci_r_rpl

        ChkRPLPtr
        call     UnLinkHeadRP            ; (es:di) -> runnable request packet
        jc      cmfx                    ; no more to run

        ChkRPPtr
        mov     es:[di].IOcount,0       ; update number of bytes transfered
        call    ProcRun
        jmp     SHORT cmf1              ; try to run another

cmfx:   pop     si

        and     [si].ci_Flagx1, not (FX1_FLUSH_TX_IP+FX1_FLUSH_RX_IP) ;;MF

        ret

EndProc ComRFlushSub


;**     ComShutdown - Shutdown Handler
;
;       ComShutdown turns off the FIFO enabling bit of the given COM port.
;       ComShutdown also turns off the loop back bit of MCR.
;
;
;       ENTRY   (ds:si) -> ComInfo
;               (es:di) -> request packet
;
;       EXIT
;
;       USES    bx

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

        ChkComInfoPtr
        ChkRPPtr
        cmp     si,0
        je      End_Shutdown

        test    [si].ci_dcb_flags3,F3_FIFO_ON
        jz      End_Shutdown

        mov     dx,[si].ci_port
        add     dx,R_FIFOC
; 81245        mov     al,FF_ENABLE
; 81245        mout    dx,al
        min     al,dx
        and     al,NOT FF_RX_MASK
        mout    dx,al
        mov     al,NOT FF_ENABLE
        mout    dx,al

        and     [si].ci_dcb_flags3,NOT F3_FIFO_ON

        mov     dx,[si].ci_port
        add     dx,R_MODMC
        min     al,dx
        and     al,NOT MC_LOOP
        mout    dx,al

End_Shutdown:
        ret
EndProc ComShutdown


;------- !WARNING! Keep InitQueues at the bottom or risk assmembly errors!
;**     InitQueues - initialize the qin and qout IO_Queues
;
;       InitQueues merely sets up the input and output queues
;       to there initial state. That is,
;
;               base = in = out = start of queue
;               end = start of queue + size of queue
;               count = 0
;
;       ENTRY   (ds:si) -> ComInfo structure
;
;       EXIT    ioq structures initialized
;
;       USES    ax
Procedure InitQueues,HYBRID

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

        ChkComInfoPtr

        lea     ax,WORD PTR [si].ci_qin_q       ; (ax) -> input queue

; Initialize input queue.

        mov     [si].ci_qin.ioq_base,ax
        mov     [si].ci_qin.ioq_in,ax
        mov     [si].ci_qin.ioq_out,ax
        add     ax,QI_SIZE
        mov     [si].ci_qin.ioq_end,ax
        mov     [si].ci_qin.ioq_count,0

        lea     ax,WORD PTR [si].ci_qout_q      ; (ax) -> output queue

; Initialize output queue.

        mov     [si].ci_qout.ioq_base,ax
        mov     [si].ci_qout.ioq_in,ax
        mov     [si].ci_qout.ioq_out,ax
        add     ax,QO_SIZE
        mov     [si].ci_qout.ioq_end,ax
        mov     [si].ci_qout.ioq_count,0

        ret

EndProc InitQueues

;**     InitSharedIRQ - initialize the ShrdIRQData structure
;
;       InitSharedIRQ - sets up the ShrdIRQData structure for
;       a shared interrupt line.  This data structure is used
;       as the "head" pointer for the linked list of ComInfo structures
;       that share an IRQ line.  There is a two-way link used
;       to connect the data structures (NOTE: this picture is of the
;       FIRST ComInfo structure in the chain - the variable si_firstCOM
;       is the "head" pointer to the start of the chain.)
;
;               ShrdIRQData             ComInfo
;                structure              structure
;
;                                       +----------------+
;                                    +->| ci_dcb_writeto |
;                                    |  +----------------+
;               +-------------+      |         .
;               | si_irq      | <--+ |         .
;               +-------------+    | |  +----------------+
;               | si_opens    |    +----| ci_int_data    |
;               +-------------+      |  +----------------+
;               | si_firstCOM |------+  | ci_nextCOM     |--->next ComInfo
;               +-------------+         +----------------+    in the chain
;
;       This way, when the COM port is opened, the ci_int_data field in
;       ComInfo can be used to determine
;       which of the ShrdIRQData structures the COM port is associated with.

;       The chain can then be walked to determine where the COM port
;       should be placed (based on highest baud rate first).
;
;
;      The responsiblity of this routine is to initialize ci_int_data
;      and, if necessary, si_irq.
;
;      ENTRY   (ds:si) -> ComInfo structure
;
;      EXIT    ComInfo and ShrdIRQData structures initialized
;
;      USES    flags

Procedure InitSharedIRQ,HYBRID
      ASSUME  cs:CSEG,ds:DSEG,es:NOTHING,ss:NOTHING

      pusha
      push    ds
      push    es

        setDS   DSEG                    ; DSEG selector

        xor     bx,bx                   ; init. index
        mov     al, [si].ci_irq         ; IRQ number to search for
        mov     cx, MAXCOMPORTS         ; max. COM ports = max. IRQ lines

sinit5:
        cmp     ShrdIRQ1[bx].si_irq, al ; IRQ line matches?
        je      sinit40                 ; Y: update data structures
        add     bx, size SharedIntData  ; N: increment index to next IRQ struc.
        loop    sinit5                  ; and try again

        ; No ShrdIRQ structure has been setup yet for this IRQ line,
        ; so find the first empty ShrdIRQ structure and use it.
        xor     bx, bx                  ; re-init.
        mov     cx, MAXCOMPORTS         ; re-init.

sinit10:
        cmp     ShrdIRQ1[bx].si_irq, 0  ; this entry is empty?
        je      sinit20                 ; Y: initialize this entry
        add     bx, size SharedIntData  ; N: increment index to next IRQ struc.
        loop    sinit10                 ; and try again

        mov     byte ptr [si].int_sharing, 0 ; don't support int. sharing
        jmp     short sinit50

sinit20:
        mov     ShrdIRQ1[bx].si_irq, al ; store the IRQ line
        mov     ShrdIRQ1[bx].si_count, 0 ; initialize the total count value

sinit40:
       inc     ShrdIRQ1[bx].si_count   ; total number of COMs on this IRQ line
       mov     [si].ci_int_data, bx    ; save offset for use at open time

sinit50:
       pop     es
       pop     ds
       popa
       ret

EndProc InitSharedIRQ


;**     OptimizeSharedIRQs - check the ShrdIRQData structures for
;                            unnecessary entries.
;       OptimizeSharedIRQs - examines the ShrdIRQData structures that are in
;       use.  If the total COM port count for an IRQ line is equal to 1,
;       then there is no reason to use the "shared" entry point in to
;       the interrupt service routine.  The "shared" entry points have
;       a small amount of extra overhead for handling multiple COM ports
;       on the same IRQ line.  If, however, a COM port is not sharing
;       its IRQ line with one or more other COM ports (instead, it is
;       being shared with some other peripheral in the system), then
;       there is no reason to use the "shared" entry point.  The regular
;       entry point for the COM port can be called directly, which
;       saves on that small amount of overhead.  When this routine finds a
;       ShrdIRQData structure that has a value of 1, it sets the
;       ci_mult_COMs_IRQ flag for that COM port to zero, so that at
;       DosOpen time the regular interrupt entry point will be registered
;       via DevHlp_SetIRQ.
;
;       ENTRY   none
;
;       EXIT    ComInfo structures updated if needed
;
;       USES    flags

Procedure OptimizeSharedIRQs,HYBRID
        ASSUME  cs:CSEG,ds:NOTHING,es:NOTHING,ss:NOTHING

        pusha
        push    ds

        setDS   DSEG                    ; DSEG selector

        xor     bx,bx                   ; init. index
        mov     cx, MAXCOMPORTS         ; examine all COM ports

optz5:
        cmp     word ptr Com1[bx], 0    ; Valid COM port?
        je      optz40                  ; N: try the next one
        mov     si, word ptr Com1[bx]   ; Y: load the offset
        cmp     [si].ci_mult_COMs_IRQ, INT_SHARING ; possibly sharing with
                                                   ;  another COM port?
        jne     optz40                  ; N: go to next COM port
        mov     di, [si].ci_int_data    ; Y: get offset of shared IRQ data
        cmp     ShrdIRQ1[di].si_count, 1 ; Only 1 COM port on this IRQ line?

        jne     optz40                  ; N: No changes needed, go to next COM
        mov     [si].ci_mult_COMs_IRQ, 0 ; Y: indicate only 1 COM on this IRQ

optz40:
        inc     bx
        inc     bx                      ; next ComInfo offset in the array
        loop    optz5                   ; and try again

optz50:
        pop     ds
        popa
        ret

EndProc OptimizeSharedIRQs


CSEG    ENDS


RSEG    SEGMENT
        ASSUME cs:RSEG

;**     Com1Strat - strategy entry point for COM1
;**     Com2Strat - strategy entry point for COM2
;
;       Com1Strat and Com2Strat are the entry points for the strategy
;       routine for the asynch device driver. The strategy routine is
;       called by the OS to handle device requests.
;
;       ENTRY   (es:bx) -> request packet
;               (ds) HSEG segment for com01.sys
;
;       EXIT    none (doesn't come through here on exit)
;
;       USES    ax si

Procedure Com1Strat,FAR
        ASSUME cs:RSEG,ds:HSEG,es:NOTHING,ss:NOTHING

IFDEF COMDEBUG
        int 3
ENDIF
        setDS   DSEG
        mov     si,Com1                 ; (ds:si) -> COM1 info
        mov     ax,Com1Minor
        jmp     ComStrat

Entry Com2Strat,,,nocheck
        ASSUME cs:RSEG,ds:HSEG,es:NOTHING,ss:NOTHING

IFDEF COMDEBUG
        int 3
ENDIF

        setDS   DSEG
        mov     si,Com2                 ; (ds:si) -> COM2 info
        mov     ax,Com2Minor
        jmp     ComStrat

Entry Com3Strat,,,nocheck

IFDEF COMDEBUG
        int 3
ENDIF

        setDS   DSEG
        mov     si,Com3                 ; (ds:si) -> COM3 info
        mov     ax,Com3Minor
        jmp     ComStrat

Entry Com4Strat,,,nocheck

IFDEF COMDEBUG
        int 3
ENDIF

        setDS   DSEG
        mov     si,Com4                 ; (ds:si) -> COM4 info
        mov     ax,Com4Minor
        jmp     ComStrat

EndProc Com1Strat

;********************************************************************
;*
;*  FUNCTION NAME :  HIDC_Entry
;*
;*  DESCRIPTION   :  Hardware DD IDC entry point.
;*
;*                   Route all IDC function requests.
;*
;*  ENTRY POINT   :  COM1_IDC_Entry        LINKAGE:  CALL FAR
;*
;*  INPUT         :  ES:BX = Segemrnt:Offset of IDC packet
;*
;*  RETURN-NORMAL :  Function performed, carry clear
;*
;*  RETURN-ERROR  :  Function rejected, carry set.
;*
;*  EFFECTS       :  Stack is clean on return.  AX, CX, DX, SI, and DI
;*                   registers are changed.
;*
;*  INTERNAL REFERENCES:
;*     ROUTINES:  Query_Config, Read_Enable, Read_Disable,
;*                Enable_Device, Disable_Device
;*
;*  EXTERNAL REFERENCES:
;*     ROUTINES:  NONE.
;*     DevHlps:  None.
;*
;********************************************************************


Procedure COM1_IDC_Entry,FAR
        ASSUME cs:RSEG,ds:HSEG,es:NOTHING,ss:NOTHING

       jmp  IDC_Handler

Entry COM2_IDC_Entry,,,nocheck

       jmp  IDC_Handler

Entry COM3_IDC_Entry,,,nocheck

       jmp  IDC_Handler

Entry COM4_IDC_Entry,,,nocheck

       jmp  IDC_Handler

EndProc COM1_IDC_Entry

RSEG    ENDS



        END
