;       SCCSID = @(#)i_devct1.asm	6.1 91/02/15
;       SCCSID = @(#)i_devct1.asm	6.2 90/01/22
; ****************************************************************************
; *									     *
; *			  IBM/Microsoft Confidential			     *
; *									     *
; *		    Copyright (c) IBM Corporation  1987, 1990		     *
; *		    Copyright (c) Microsoft Corp.  1987, 1990		     *
; *			      All Rights Reserved			     *
; *									     *
; ****************************************************************************
;***  i_devct1.asm - routines for category 1 IOCTL calls
;
;       Internal routines used by API IOCTL library routines
;       David Silverstein
;       Copyright MicroSoft Corporation, 1986
;       15-May-1986
;
;       This file contains support routines for serial io IOCTLs
;
;
;       Special Information
;
;           This file references a table called __api_dev_table
;           defined in d_open_asm.  Access to this table is necessary to
;           obtain the device number of character devices.
;
;           If a device driver running under Dos 3.2 or above (except 3xBox)
;           supports the serial generic ioctls, they will not be called
;           through this interface.
;
;       Modification History
;
;       15-Jan-87 (billme): Change reference to __api_dev_table to
;       call to __api_devtab_ptr ... to reduce code size.
;
;       04-May-87 (robertc): Install hooks for DOSErrorClass Support.
;       All int 21h errors are passed through api_get_extended to get the
;       extended errorcode and to provide the class, action, and locus for
;       the dynamic error FIFO queue.
;
;       10-Jun-87 (robertc): changed error at sioctl5: for error_invalid_handle
;       to error_bad_command. The handles are valid but this command is not
;       valid for the CON or PRN devices.
;
;       22-Jun-87 (robertc): PTM 5590 use exact port address from bios data area
;
;       22-Jul-87 (robertc): PTM 6137 pass 3.3 calls to direct port setting
;
;       17-Aug-88 (robertc): PTM 3382 use proper unit number in getting COMPORT
;
include dos2equ.inc
include error.inc
include api.inc
include d_ioctl.inc
include i_devct1.inc

BIOS_SEGMENT    EQU     40H                     ; BIOS data segment

_TEXT   segment byte public 'CODE'

        assume  CS:_TEXT, DS:nothing

        PUBLIC  SERIALCTRL

        EXTRN   api_get_extended:far    ;@@ added support for DOSErrorClass
        EXTRN   __api_devtab_ptr:near
        EXTRN   __api_dev_table : WORD
        EXTRN   _api_getvrsn    : NEAR

; Table of serial port base addresses indexed by serial device number

;@5590 We will now look into the BIOS Data area for the COM Port Address

ComPort         dw      ?                       ;@@5590

SERIALCTRL      proc    near
        push    ds
        push    es
        push    cx
        push    dx
        push    di
        call    _api_getvrsn            ; get Dos version
        cmp     ah, 10                  ; is it in the 3xBox
        jb      sioctl2                 ; we are not running the 3xBox

        ; We are running the 3xBox.  The IOCTL info is passed to the
        ; driver through an int 21 interface.

        mov     ch, SERIAL_CATEGORY
        lds     di, [ParmList]          ; si:di <= parameter list pointer
        mov     ax, ds
        mov     si, ax
        lds     dx, [DataArea]          ; ds:dx <= data packet pointer
        mov     ax, D2_IOCTL SHL 8 + D2_HANDLE_IOCTL ; 2.x IOCTL for handles
        mov     bx, [DevHandle]          ;@@  bx <= device handle
        mov     cl, [Function]           ;@@  cl <= function code
        int     21h
        jc      sioctl1
        xor     ax, ax
        jmp     short sioctl6

        ; get the extended error code - hopefully what the driver returned.
        ; Because this code only runs in the 3xBox, we don't have to worry about
        ; Get Extended Error not being supported in Dos 2.x.

sioctl1:
        call    api_get_extended        ;@@ added support for DOSErrorClass
        jmp     short sioctl6

; Get device number from handle table

sioctl2:
        mov     ax, [DevHandle]                 ; device handle
        cmp     ax, MAX_HANDLES                 ; handle within range?
        ja      sioctl5

        call    __api_devtab_ptr                ; BX <- address of entry


        mov     al,_TEXT:[bx].hi_validity       ;@@PTM 6137
        test    al, HANDLE_VALID                ;@@  priority of error codes
        jnz     sioctl21                        ;@@  must be Bad handle before
        mov     AX,ERROR_INVALID_HANDLE         ;@@  bad command.
        jmp     sioctl6                         ;@@

sioctl21:
        xor     dx, dx                          ; dh must be clear
        mov     bl, _TEXT:[bx].hi_attrdev       ; get device number
                                                ; should be byte, temp. word.
        xor     bh, bh
        test    bl, DEV_FLAG                    ; is it the device number?
        jz      sioctl5                         ; error, not a device handle

;@@5590 test    bl, LPT_FLAG                    ; is it the printer device?
;@@5590 jnz     sioctl5                         ; error, wrong device

        test    bl,COM_FLAG                     ;@@5590  is it a com device
        jz      sioctl5                         ;@@5590  error, not a com device

        and     bl, UNIT_FLAG                   ;@@5590  clear all but unit flags
        cmp     bl, MAX_COM                     ;  is this within range?
        ja      sioctl5

;@@5590 Get COM Port address from BIOS Data SEGMENT and check for validity
        push    ds                              ;@@5590  save our ds
        push    di
        mov     ax,BIOS_SEGMENT                 ;@@5590  address BIOS data area
        mov     ds,ax                           ;@@5590
        dec     bx                              ;@P3382 convert unit to zero origin
        shl     bx,1                            ;@P3382 and make it a word offset
        mov     di,bx
        mov     ax,WORD PTR ds:[di]        ;@@5590  get COM PORT address
        mov     ComPort,ax
        pop     di
        pop     ds                              ;@@5590  restore our ds

        lds     si, [ParmList]                  ; address of parameter list
        mov     ax, [Function]
        and     ax, NOT IGNORE_ERROR            ; mask off ignore error bit

        cmp     ax, D5_SetBaudRate
        jne     sioctl3
        call    SetBaudRate                     ; set baud rate
        jmp     short sioctl6

sioctl3:
        cmp     ax, D5_SetLineCtrl
        jne     sioctl4
        call    SetLineCtrl                     ; set line control
        jmp     short sioctl6
sioctl4:
        mov     ax, error_invalid_function      ; all others are invalid
        jmp     short sioctl6
sioctl5:
        mov     ax, ERROR_BAD_COMMAND           ;@@P5354  handle
                                                ;Command bad for CON or PRN
sioctl6:
        pop     di
        pop     dx
        pop     cx
        pop     es
        pop     ds
        ret

; ***** SetBaudRate - Set Baud Rate
;
; ENTRY: @@5590 (bx not needed) bx = com port number
;        ds:[si] = pointer to parameter list

SetBaudRate:
        mov     cx, ds:[si]             ; Get the requested baudrate
        cmp     cx, MIN_BAUD            ; see if in valid range
        jb      badbaud
        cmp      cx, MAX_BAUD
        ja      badbaud

        mov     dx, 1
        mov     ax, 0c200h              ; dx:ax = 115,200
        div     cx                      ; ax = dx:ax/baud (in cx)
        mov     cx, ax                  ; cx = quotient

        mov     dx, ComPort             ;@@5590 get com port address base
        add     dx, R_LINEC             ; Point at the line control reg
        in      al, dx                  ; Get existing copy
        mov     ah, al                  ; Save it for later
        or      al, LC_DLAB             ; Set DLAB
        IODELAY
        out     dx, al                  ; Enable Baud Latch
        add     dx, R_BAUDH - R_LINEC   ; MSB of Baud Latch
        mov     al, ch
        IODELAY
        out     dx, al                  ; Set MSB of Div
        dec     dx
        mov     al, cl
        IODELAY
        out     dx, al                  ; Set LSB of Div
        add     dx, R_LINEC - R_BAUDL   ; Back to line control reg
        mov     al, ah
        IODELAY
        out     dx, al                  ; Turn off DLAB
        xor     ax, ax
        ret
badbaud:
        mov     ax, error_invalid_parameter
        ret


; ***** SetLineCtrl - Set Line Control
;
; ENTRY: @@5590 (bx not needed) bx = comm port number
;        ds:[si] = pointer to parameter list

SetLineCtrl:
        xor     ax, ax
        mov     dl, [si]                ; get requested data bytesize
        cmp     dl, MIN_WORD_LEN
        jae     slc1
        jmp     short slc10
slc1:
        cmp     dl, MAX_WORD_LEN
        jbe     slc2
        jmp     short slc10

; convert the bytesize parameter into the format of the LCR

slc2:
        xor     ax, ax
        sub     dl, 5
;       shr     dl, 1         <- these do not figure (commented out to
;       shr     dl, 1         <- correctly.          (fix ptm 3479
        or      al, dl

; convert the parity parameter into the format of the LCR

        mov     dl, [si + 1]            ; get parity description
        cmp     dl, D5_SPACE_PARITY
        ja      slc10
slc3:
        cmp     dl, D5_NO_PARITY        ; no parity requested?
        je      slc7
        cmp     dl, D5_ODD_PARITY       ; odd parity requested?
        jne     slc4
        or      al, PEN                 ; set parity enable
        jmp     short slc7
slc4:
        cmp     dl, D5_EVEN_PARITY      ; even parity requested?
        jne     slc5
        or      al, PEN + EPS           ; set parity enable, even parity select
        jmp     short slc7
slc5:
        cmp     dl, D5_MARK_PARITY      ; mark parity requested?
        jne     slc6
        or      al, PEN + STICKP        ; set parity enable, stick parity
        jmp     short slc7
slc6:                                   ; assume space parity
        or      al, PEN + EPS + STICKP  ; set parity enable, even parity select
                                        ; stick parity

; convert the stop bits requested into the format of the LCR

slc7:
        mov     dl, [si + 2]            ; get the stopbits data
        cmp     dl, ONE_STOP_BIT        ; one stop bit requested?
        je      slc9
        cmp     dl, TWO_STOP_BITS       ; two stop bits requested?
        jne     slc8
        cmp     byte ptr [si], D5_5_BIT_WORD
        je      slc10                   ; error, a 5 bit word with 2 stop bits
                                        ; is not a legal combination
        or      al, STB
        jmp     short slc9
slc8:
        cmp     dl, ONEP5_STOP_BITS     ; one and a half stop bits requested?
        jne     slc10
        cmp     byte ptr [si], D5_5_BIT_WORD
        jne     slc10
        or      al, STB
slc9:
        mov     dx, ComPort             ;@@5590 get com port address base

        add     dx, R_LINEC             ;Point at the line control reg
        and     al, LC_MASK             ;Clear any garbage
        out     dx, al                  ;And output to comm card
        xor     ax, ax
        ret
slc10:
        mov     ax, error_invalid_parameter
        ret

SERIALCTRL      endp
_TEXT           ends

                end
