;       SCCSID = @(#)demioctl.asm	6.6 91/11/23
;
;   Revisions:
;
;Local  DCR/
;Tag    PTM(R)  Who     When/What
;               danno   28-Feb-1989
;                       Created for MVDM
;
;   $IOCTL

;********************************************************************** ;BN001;
; 23Jun91 - G.L.Fulk                                                    ;BN001;
; Structure macros added.                                               ;BN001;
; Add new DOS 5.0 ioctl calls 10 and 11h.                               ;BN001;
;********************************************************************** ;BN004;
; 28Feb91 - G.L.Fulk                                                    ;BN004;
; IOCtl calls to an OS/2 device driver fail.  The routine IOFunc is     ;BN004;
; being changed to detect the presents of an OS/2 driver as opposed     ;BN004;
; to a DOS driver.  The BX register needs to be preserved in this file. ;BN004;
; Files affected:   v86code\DemIoctl.asm                                ;BN004;
;                   v86code\DemDev.asm                                  ;BN004;
;                   mvdm\DemIoc2.asm                                    ;BN004;
;********************************************************************** ;BN004;
; 9/26/91 - Mark Rogalski                                               ;BN005;
; CP20 PTR B724638  Change Ioctl calls 10 and 11 to return invalid      ;BN005;
;                   function.                                           ;BN005;
;********************************************************************** ;BN005;
; 23Nov91 - G.L.Fulk                                                    ;BN006;
; Test for Remote/local IOCTL (4409h) is returning the information in   ;BN006;
; the wrong register.                                                   ;BN006;
;********************************************************************** ;BN006;

.xlist
include v86seg.inc
.xcref
include basemaca.inc
include osmaca.inc
include oldmaca.inc
include demmac.inc
include dossymc.inc
include devsym.inc
include dos3dev.inc
include error.inc
include mvdmexp.inc
include demsvc.inc
include ioctl.inc
include bpb.inc
include dskioctl.inc
include curdir.inc
include sft.inc
include mi.inc
include struc30.inc                                                     ;BN001;
.cref
.list
        CpuMode 386
        ReferV86    User_In_AX,WORD
        ReferV86    ExtErr_Locus,BYTE

V86DATA SEGMENT

TrackStandardFlag       db      1       ; 1 = Standard track layout
                                        ; 0 = NON-Standard layout
DummyData       dw      0

SaveDataArea    dd      0

FakeSwitch      db      0

DeviceParm      db      SIZE DDI_DeviceParameters_param dup (0)

DeviceData      db      SIZE DDI_DeviceParameters_data dup (0)

TrackFormat     db      1               ; 0 = SECTOR #, SECTOR SIZE format
                                        ; 1 = <c,h,r,n> format
;
; trackTable contains a 4-tuples (c,h,r,n) for each sector in a track
; c = cylinder number, h = head number, r = sector id, n = bytes per sector
;       n       bytes per sector
;      ---      ----------------
;       0             128
;       1             256
;       2             512
;       3            1024

SectorsPerTrack dw      15

ParmPacket      db      0               ; DDI_RWV_Command
                dw      0               ; DDI_RWV_Head
                dw      0               ; DDI_RWV_Cyln
                dw      0               ; DDI_RWV_Sec
                dw      0               ; DDI_RWV_NumSec

TrackTable      db      0,0,1,2
                db      0,0,2,2
                db      0,0,3,2
                db      0,0,4,2
                db      0,0,5,2
                db      0,0,6,2
                db      0,0,7,2
                db      0,0,8,2
                db      0,0,9,2
                db      0,0,10,2
                db      0,0,11,2
                db      0,0,12,2
                db      0,0,13,2
                db      0,0,14,2
                db      0,0,15,2
                db      4 * MAX_SECTORS_IN_TRACK - ($ - trackTable) dup (0)

                PUBLIC  TempBX
TempBX          dw      0

                EXTRN   RMDevReqPkt: byte

V86DATA ENDS


V86CODE SEGMENT
        ASSUME  CS:V86GROUP,DS:NOTHING,ES:NOTHING,SS:NOTHING

        EXTRN   Sys_Return:NEAR
        EXTRN   VSFNFromHandle:NEAR
        EXTRN   RMDevCheck:NEAR
        EXTRN   gioctl_do_io:NEAR
        EXTRN   RMDevGIOCTL:NEAR
        EXTRN   Get_User_Stack:NEAR
        EXTRN   IOFunc:NEAR
        EXTRN   Get_IO_SFT:NEAR

page
BREAK <IOCTL - munge on a handle to do device specific stuff>

;
;   Assembler usage:
;           MOV     BX, Handle
;           MOV     DX, Data
;
;       (or LDS     DX,BUF
;           MOV     CX,COUNT)
;
;           MOV     AH, Ioctl
;           MOV     AL, Request
;           INT     21h
;
;   AH = 0  Return a combination of low byte of sf_flags and device driver
;           attribute word in DX, handle in BX:
;           DH = high word of device driver attributes
;           DL = low byte of sf_flags
;        1  Set the bits contained in DX to sf_flags.  DH MUST be 0.  Handle
;           in BX.
;        2  Read CX bytes from the device control channel for handle in BX
;           into DS:DX.  Return number read in AX.
;        3  Write CX bytes to the device control channel for handle in BX from
;           DS:DX.  Return bytes written in AX.
;        4  Read CX bytes from the device control channel for drive in BX
;           into DS:DX.  Return number read in AX.
;        5  Write CX bytes to the device control channel for drive in BX from
;           DS:DX.  Return bytes written in AX.
;        6  Return input status of handle in BX. If a read will go to the
;           device, AL = 0FFh, otherwise 0.
;        7  Return output status of handle in BX. If a write will go to the
;           device, AL = 0FFh, otherwise 0.
;        8  Given a drive in BX, return 1 if the device contains non-
;           removable media, 0 otherwise.
;        9  Return the contents of the device attribute word in DX for the
;           drive in BX.  0200h is the bit for shared.  1000h is the bit for
;           network. 8000h is the bit for local use.
;        A  Return 8000h if the handle in BX is for the network or not.
;        B  Change the retry delay and the retry count for the system. BX is
;           the count and CX is the delay.
;
;   Error returns:
;           AX = error_invalid_handle
;              = error_invalid_function
;              = error_invalid_data
;
;-------------------------------------------------------------------------------
;
;   This is the documentation copied from DOS 4.0 it is much better
;   than the above
;
;       There are several basic forms of IOCTL calls:
;
;
;       ** Get/Set device information:  **
;
;       ENTRY   (AL) = function code
;                 0 - Get device information
;                 1 - Set device information
;               (BX) = file handle
;               (DX) = info for "Set Device Information"
;       EXIT    'C' set if error
;                 (AX) = error code
;               'C' clear if OK
;                 (DX) = info for "Get Device Information"
;       USES    ALL
;
;
;       **  Read/Write Control Data From/To Handle  **
;
;       ENTRY   (AL) = function code
;                 2 - Read device control info
;                 3 - Write device control info
;               (BX) = file handle
;               (CX) = transfer count
;               (DS:DX) = address for data
;       EXIT    'C' set if error
;                 (AX) = error code
;               'C' clear if OK
;                 (AX) = count of bytes transfered
;       USES    ALL
;
;
;       **  Read/Write Control Data From/To Block Device  **
;
;       ENTRY   (AL) = function code
;                 4 - Read device control info
;                 5 - Write device control info
;               (BL) = Drive number (0=default, 1='A', 2='B', etc)
;               (CX) = transfer count
;               (DS:DX) = address for data
;       EXIT    'C' set if error
;                 (AX) = error code
;               'C' clear if OK
;                 (AX) = count of bytes transfered
;       USES    ALL
;
;
;       **  Get Input/Output Status  **
;
;       ENTRY   (AL) = function code
;                 6 - Get Input status
;                 7 - Get Output Status
;               (BX) = file handle
;       EXIT    'C' set if error
;                 (AX) = error code
;               'C' clear if OK
;                 (AL) = 00 if not ready
;                 (AL) = FF if ready
;       USES    ALL
;
;
;       **  Get Drive Information  **
;
;       ENTRY   (AL) = function code
;                 8 - Check for removable media
;                 9 - Get device attributes
;               (BL) = Drive number (0=default, 1='A', 2='B', etc)
;       EXIT    'C' set if error
;                 (AX) = error code
;               'C' clear if OK
;                 (AX) = 0/1 media is removable/fixed (func. 8)
;                 (DX) = device attribute word (func. 9)
;       USES    ALL
;
;
;       **  Get Redirected bit  **
;
;       ENTRY   (AL) = function code
;                 0Ah - Network stuff
;               (BX) = file handle
;       EXIT    'C' set if error
;                 (AX) = error code
;               'C' clear if OK
;                 (DX) = SFT flags word, 8000h set if network file
;       USES    ALL
;
;
;       **  Change sharer retry parameters  **
;
;       ENTRY   (AL) = function code
;                 0Bh - Set retry parameters
;               (CX) = retry loop count
;               (DX) = number of retries
;       EXIT    'C' set if error
;                 (AX) = error code
;               'C' clear if OK
;       USES    ALL
;
;
;   =================================================================
;
;       **  New Standard Control  **
;
;       ALL NEW IOCTL FACILITIES SHOULD USE THIS FORM.  THE OTHER
;       FORMS ARE OBSOLETE.
;
;   =================================================================
;
;       ENTRY   (AL) = function code
;                 0Ch - Control Function subcode
;               (BX) = File Handle
;               (CH) = Category Indicator
;               (CL) = Function within category
;               (DS:DX) = address for data, if any
;               (SI) = Passed to device as argument, use depends upon function
; BUGBUG danno 14-Mar-1989 ; SI is trashed in DOS 3.x/4.x
;               (DI) = Passed to device as argument, use depends upon function
;       EXIT    'C' set if error
;                 (AX) = error code
;               'C' clear if OK
;                 (SI) = Return value, meaning is function dependent
;                 (DI) = Return value, meaning is function dependent
;                 (DS:DX) = Return address, use is function dependent
;       USES    ALL
;
;    ============== Generic IOCTL Definitions for DOS 3.2 ============
;
;       ENTRY   (AL) = function code
;                 0Dh - Control Function subcode
;               (BL) = Drive Number (0 = Default, 1= 'A')
;               (CH) = Category Indicator
;               (CL) = Function within category
;               (DS:DX) = address for data, if any
;               (SI) = Passed to device as argument, use depends upon function
;               (DI) = Passed to device as argument, use depends upon function
;
;       EXIT    'C' set if error
;                 (AX) = error code
;               'C' clear if OK
;                 (DS:DX) = Return address, use is function dependent
;       USES    ALL
;

        PUBLIC  apFuncIOCTL
apFuncIOCTL     label   word
        dw      OFFSET V86GROUP:ioctl_get_dev_data      ;  0
        dw      OFFSET V86GROUP:ioctl_set_dev_data      ;  1
        dw      OFFSET V86GROUP:ioctl_read_handle       ;  2
        dw      OFFSET V86GROUP:ioctl_write_handle      ;  3
        dw      OFFSET V86GROUP:ioctl_read_block        ;  5
        dw      OFFSET V86GROUP:ioctl_write_block       ;  4
        dw      OFFSET V86GROUP:ioctl_input_status      ;  6
        dw      OFFSET V86GROUP:ioctl_output_status     ;  7
        dw      OFFSET V86GROUP:ioctl_removable_media   ;  8
        dw      OFFSET V86GROUP:ioctl_redir_block       ;  9
        dw      OFFSET V86GROUP:ioctl_redir_handle      ;  A
        dw      OFFSET V86GROUP:ioctl_get_retries       ;  B
        dw      OFFSET V86GROUP:ioctl_generic_handle    ;  C
        dw      OFFSET V86GROUP:ioctl_generic_block     ;  D
        dw      OFFSET V86GROUP:ioctl_get_log_drive     ;  E
        dw      OFFSET V86GROUP:ioctl_set_log_drive     ;  F
        dw      OFFSET V86GROUP:ioctl_query_handle      ;  10           ;BN001;
        dw      OFFSET V86GROUP:ioctl_query_block       ;  11           ;BN001;

cFuncIOCTL  equ ($-apFuncIOCTL) / 2

;
;INT 21h (AH=44h) $IOCTL             v86code\demioctl.asm          #1/2 ;BN001;
;INT 21h (AH=44h) {misc workers}     v86code\demioctl.asm          #2/2 ;BN001;
;

        ASSUME  DS:NOTHING,ES:NOTHING,SS:NOTHING

Procedure $IOCTL,NEAR
        .if     <AL b cFuncIOCTL>       ; if supported function
          mov     [TempBX],bx           ; then ... else Invalid_function error
          xor     ah,ah
          mov     bx,ax
          shl     bx,1
          push    apFuncIOCtl[bx]
          mov     bx,[TempBX]
          ret
        .endif

        mov     ax,ERROR_INVALID_FUNCTION
        stc

Entry   ioctl_exit
        jmp     Sys_Return

        subttl  IOCTL 4400h and 4401h
        page

;
;INT 21h (AX=4400h) ioctl_get_dev_data  v86code\demioctl.asm       #1/4 ;BN001;
;INT 21h (AX=4400h) [SVC]demIOCTL       mvdm\demioc2.asm           #2/4 ;BN001;
;INT 21h (AX=4400h) demiocGetDevData    mvdm\demioc2.asm           #3/4 ;BN001;
;INT 21h (AX=4400h) GetHandleInfo       filesys\devioctl.asm       #4/4 ;BN001;
;
;INT 21h (AX=4401h) ioctl_set_dev_data  v86code\demioctl.asm       #1/2 ;BN001;
;INT 21h (AX=4401h) [SVC]demIOCTL       mvdm\demioc2.asm           #2/2 ;BN001;
;
;***    ioctl_get_dev_data (0)
;***    ioctl_set_dev_data (1)
;
;       Get/Set device information
;
;       ENTRY   (AL) = function code
;                 0 - Get device information
;                 1 - Set device information
;               (BX) = VSFN
;               (DX) = info for "Set Device Information"
;       EXIT    'C' set if error
;                 (AX) = error code
;               'C' clear if OK
;                 (DX) = info for "Get Device Information"
;                        File: calls SVC, DH=0, DL=SFT low flags
;                        DOS device: no SVC, DH=high DD byte, DL=RMDI low flags
;                        OS/2 device: no SVC, DH=RMDI high flags,
;                                             DL=RMDI low flags
;       USES    ALL
;
        ASSUME  DS:NOTHING,ES:NOTHING,SS:NOTHING

Entry   ioctl_get_dev_data              ;IOCTL 4400h                    ;BN001;
Entry   ioctl_set_dev_data              ;IOCTL 4401h                    ;BN001;

;------ exit if bad handle
        call    VSFNFromHandle
        jc      ioctl_exit

;------ determine the type of handle
        call    RMDevCheck
        ;return CY ZF = File        (bx=OS/2 handle)
        ;       NC ZF = OS/2 device (bx=OS/2 handle) (es:di=RMDI)
        ;       NC NZ = DOS device                   (es:di=RMDI)
        .if     c
          ;---BX is a file
          .if     <ZERO al>             ;IOCTL 4400h BX=file
            SVC     SVC_DEMIOCTL        ; returns result in AX
            .if     nc
              call    Get_User_Stack
              mov     [si].user_dx,ax   ; return info in DX
              clc
            .endif
          .else                         ;IOCTL 4401h BX=file
            mov     ax,ERROR_INVALID_FUNCTION
            stc
          .endif
          jmp     ioctl_exit
        .endif

;------ BX=is a DOS device or OS/2 device.  Get/Set Device info.
        .if     <NONZERO al>
          ;*      Function 1 - Set device information
          .if     <NONZERO DH>                  ;DH must be 0
            mov     ax,ERROR_INVALID_DATA
            stc
            jmp     ioctl_exit
          .endif
          mov     ax,es:[di].rmdi_flags         ; get flags
          mov     [ExtErr_Locus],errLOC_SerDev
          or      dl,DEVID_DEVICE               ; ignore attempt to clr dev bit
          mov     byte ptr es:[di].rmdi_flags,dl  ; set low byte of flags
        .else
          ;*      Function 0 - Get device information
          mov     ax,es:[di].rmdi_flags           ; get flags
          mov     [ExtErr_Locus],errLOC_SerDev
          .if     <<es:[di].rmdi_DevPtr.Offst> ne -1> ; OS/2 device?
            ;--- DOS device
            lds     si,es:[di].rmdi_devPtr        ; (ds:si) -> device header
            mov     ah,byte ptr ds:[si].SDevAtt+1 ; get high byte of attributes
          .endif
        .endif

;------ return info in DX
        call    Get_User_Stack
        mov     [si].user_dx,ax         ; return info in DX
        clc

        jmp     ioctl_exit


        subttl  IOCTL 4402h and 4403h
        page

;
;INT 21h (AX=4402h) ioctl_read_handle   v86code\demioctl.asm       #1/4 ;BN001;
;INT 21h (AX=4402h) [SVC]demIOCTL       mvdm\demioc2.asm           #2/4 ;BN001;
;INT 21h (AX=4402h) demiocGetDevData    mvdm\demioc2.asm           #3/4 ;BN001;
;INT 21h (AX=4402h) GetHandleInfo       filesys\devioctl.asm       #4/4 ;BN001;
;
;***    ioctl_read_handle (2)
;***    ioctl_write_handle (3)
;
;       Read/Write Control Data From/To Handle
;
;       ENTRY   (AL) = function code
;                 CMDIOCTLR - Read device control info
;                 CMDIOCTLW - Write device control info
;               (BX) = VSFN
;               (CX) = transfer count
;               (DS:DX) = address for data
;       EXIT    'C' set if error
;                 (AX) = error code
;               'C' clear if OK
;                 (AX) = count of bytes transfered
;       USES    ALL
;
        ASSUME  DS:NOTHING,ES:NOTHING,SS:NOTHING

Entry   ioctl_read_handle               ;4402h
Entry   ioctl_write_handle              ;4403h

;------ exit if bad handle
        call    VSFNFromHandle
        jc      ioctl_exit

;------ Doing a read or write to a File is not permitted
        call    RMDevCheck
        ;return CY ZF = File        (bx=OS/2 handle)
        ;       NC ZF = OS/2 device (bx=OS/2 handle) (es:di=RMDI)
        ;       NC NZ = DOS device                   (es:di=RMDI)
        .if     c
          ;---if it's a file, return an error
          mov     ax,ERROR_INVALID_FUNCTION
          stc
          jmp     ioctl_exit
        .endif

;------ Try a DOS or OS/2 device
        mov     [EXTERR_LOCUS],errLOC_SerDev
        les     di,es:[di].rmdi_DevPtr  ; get device header
        .if     <DI eq -1>              ; skip if OS/2 device
          ;--- OS/2 device
          mov     bx,es                 ; bx=handle, ds:dx=data
          SVC     SVC_DEMIOCTL          ; do all work in protect mode
          jmp     ioctl_exit
        .endif

        ;--- DOS device
        push    es
        push    di
        push    ds
        pop     es
        pop     si
        pop     ds                    ; ds:si=devhdr, es:dx=data
        .if     <<ds:[si].SDevAtt> nand DEV_IOCTL>
          mov     ax,ERROR_INVALID_FUNCTION
          stc
          jmp     ioctl_exit
        .endif
        mov     bx,offset V86GROUP:RMDevReqPkt
        .if     <AL eq 2>
          mov     cs:[bx].PktCmd,CMDIOCTLR    ; ioctl read command
        .else
          mov     cs:[bx].PktCmd,CMDIOCTLW    ; ioctl write command
        .endif
        mov     cs:[bx].IOCount,cx
        mov     cs:[bx].PktLen,LENIO+PktHeadSize
        mov     cs:[bx].IOMedia,0
        mov     cs:[bx].IOpData.Offst,dx
        mov     cs:[bx].IOpData.Segmt,es
        call    gioctl_do_io
        jmp     ioctl_exit

        page

;***    ioctl_read_block (4)
;***    ioctl_write_block (5)
;
;       Read/Write Control Data From/To Block Device
;
;       ENTRY   (AL) = function code
;                 4 - Read device control info
;                 5 - Write device control info
;               (BL) = Drive number (0=default, 1='A', 2='B', etc)
;               (CX) = Transfer count
;               (DS:DX) = Address for data
;       EXIT    'C' set if error
;                 (AX) = error code
;               'C' clear if OK
;                 (AX) = count of bytes transfered
;       USES    ALL
;
;***    ioctl_removable_media (8)
;
;       Get removable media information
;
;       ENTRY   (AL) = function code
;                 8 - Check for removable media
;               (BL) = Drive number (0=default, 1='A', 2='B', etc)
;       EXIT    'C' set if error
;                 (AX) = error code
;               'C' clear if OK
;                 (AX) = 0/1 media is removable/fixed (func. 8)
;       USES    ALL
;
;***    ioctl_get_retries (0BH)
;
;       Change sharer retry parameters
;
;       ENTRY   (AL) = function code
;                 0Bh - Set retry parameters
;               (CX) = retry loop count (currently unused)
;               (DX) = number of retries
;       EXIT    'C' set if error
;                 (AX) = error code
;               'C' clear if OK
;       USES    ALL
;
;***    ioctl_get_log_drive
;***    ioctl_set_log_drive
;
;       Get or Set logical drive mapping.
;
;       ENTRY   (AL) = function code
;                 0Eh - Get logical drive map
;                 0Fh - Set logical drive map
;               (BL) = Drive Number (0 = Default, 1= 'A')
;       EXIT    'C' set if error
;                 (AX) = error code
;               'C' clear if OK
;                 (AL) = logical drive mapped onto physical (=0 if only
;                 one drive is assigned to this physical drive).
;       USES    ALL
;
        ASSUME  DS:NOTHING,ES:NOTHING,SS:NOTHING

Entry   ioctl_read_block                ;4404
Entry   ioctl_write_block               ;4405
Entry   ioctl_removable_media           ;4408
Entry   ioctl_get_retries               ;440B
Entry   ioctl_get_log_drive             ;440E
Entry   ioctl_set_log_drive             ;440F

        SVC     SVC_DEMIOCTL            ; do all work in protect mode
        jmp     ioctl_exit

;***    ioctl_input_status (6)
;***    ioctl_output_status (7)
;
;       Get Input/Output Status
;
;       ENTRY   (AL) = function code
;                 6 - Get Input status
;                 7 - Get Output Status
;               (BX) = file handle
;       EXIT    'C' set if error
;                 (AX) = error code
;               'C' clear if OK
;                 (AL) = 00 if not ready
;                 (AL) = FF if ready
;       USES    ALL
;
        ASSUME  DS:NOTHING,ES:NOTHING,SS:NOTHING

Entry   ioctl_input_status
        mov     ah,1
        jmp     short ioctl_io_status

Entry   ioctl_output_status
        mov     ah,3

Entry   ioctl_io_status
;       push    ax                                                      ;BD004;
        call    Get_IO_SFT
;       pop     bx                                                      ;BD004;
        jc      ioctl_exit              ; bad handle
;       mov     ax,bx                                                   ;BD004;
        call    IOFunc
        ;return ZF=busy (not ready)
        ;       NZ=not busy (ready)
        mov     ah,al                   ; return peeked input character
        mov     al,0FFH                 ;AL:=-1
        .if     z
          inc     al                    ;AL:=0
        .endif
        clc
        jmp     ioctl_exit

;****** 4409h
;***    ioctl_redir_block (9)
;
;       Get driver attributes
;
;       ENTRY   (AL) = function code
;                 9 - Get device attributes
;               (BL) = Drive number (0=default, 1='A', 2='B', etc)
;       EXIT    'C' set if error
;                 (AX) = error code
;               'C' clear if OK
;                 (DX) = device attribute word (func. 9)
;       USES    ALL
;
Entry   ioctl_redir_block               ; 4409h
        SVC     SVC_DEMIOCTL            ; returns result in DX          ;BC006;
        .if     nc
          call    Get_User_Stack
          mov     [si].user_dx,dx       ; return info in DX             ;BC006;
          clc
        .endif
        jmp     ioctl_exit

;***    ioctl_redir_handle (0AH)
;
;       Get Redirected bit
;
;       ENTRY   (AL) = function code
;                 0Ah - Network stuff
;               (BX) = file handle
;       EXIT    'C' set if error
;                 (AX) = error code
;               'C' clear if OK
;                 (DX) = SFT flags word, 8000h set if network file
;       USES    ALL
;
        ASSUME  DS:NOTHING,ES:NOTHING,SS:NOTHING

Entry   ioctl_redir_handle

;------ exit if bad handle
        call    VSFNFromHandle
        jc      ioctl_exit

        call    RMDevCheck
        ;return CY ZF = File        (bx=OS/2 handle)
        ;       NC ZF = OS/2 device (bx=OS/2 handle) (es:di=RMDI)
        ;       NC NZ = DOS device                   (es:di=RMDI)
        .if     c
          SVC     SVC_DEMIOCTL        ; returns result in AX
          .if     nc
            call    Get_User_Stack
            mov     [si].user_dx,ax         ; return info in DX
            clc
          .endif
          jmp     ioctl_exit
        .endif
        mov     ax,es:[di].rmdi_flags   ; never redirected
                                        ; AX to DX
        call    Get_User_Stack
        mov     [si].user_dx,ax         ; return info in DX
        clc
        jmp     ioctl_exit


;***    ioctl_generic_handle - Generic IOCTL (0CH)
;
;       This is the new, handle based form of generic IOCTL.
;
;       ENTRY   (AL) = function code
;                 0Ch - Control Function subcode
;               (BX) = File Handle
;               (CH) = Category Indicator
;               (CL) = Function within category
;               (DS:DX) = address for data, if any
;               (SI) = Passed to device as argument, use depends upon function
;               (DI) = Passed to device as argument, use depends upon function
;
;       EXIT    'C' set if error
;                 (AX) = error code
;               'C' clear if OK
;
;       USES    ALL
;
        ASSUME  DS:NOTHING,ES:NOTHING,SS:NOTHING

Entry   ioctl_generic_handle            ;IOCTL 440Ch

        call    VSFNFromHandle
        jc      ioctl_exit

        push    di
        call    RMDevCheck              ; real-mode device?
        ;return CY ZF = File        (bx=OS/2 handle)
        ;       NC ZF = OS/2 device (bx=OS/2 handle) (es:di=RMDI)
        ;       NC NZ = DOS device                   (es:di=RMDI)
        .if     nz
          mov     [EXTERR_LOCUS],ErrLOC_Serdev
          les     di,es:[di].rmdi_DevPtr
          push    ds                      ; swap es & ds
          push    es
          pop     ds                      ; ch=category, cl=function
          pop     es                      ; es:dx=data
          mov     ax,si
          mov     si,di                   ; ds:si=devhdr
          pop     di                      ; ax:di=(user_si, user_di)
          call    RMDevGIOCTL             ; issue the IOCTL to the RM device
          jmp     ioctl_exit
        .endif
        pop     di                      ; (BX) = OS/2 handle
        SVC     SVC_DEMIOCTL            ; do all work in protect mode
        jmp     ioctl_exit

;***    ioctl_generic_block - Generic IOCTL (0DH)
;
;       This is the drive based 3.2 form of the IOCTL.
;
;       ENTRY   (AL) = function code
;                 0Dh - Control Function subcode
;               (BL) = Drive Number (0 = Default, 1= 'A')
;               (CH) = Category Indicator
;               (CL) = Function within category
;               (DS:DX) = address for data, if any
;               (SI) = Passed to device as argument, use depends upon function
;               (DI) = Passed to device as argument, use depends upon function
;
;       EXIT    'C' set if error
;                 (AX) = error code
;               'C' clear if OK
;       USES    ALL
;
        ASSUME  DS:NOTHING,ES:NOTHING

Entry   ioctl_query_handle              ;IOCTL 4410h                    ;BC005;
Entry   ioctl_query_block               ;IOCTL 4411h                    ;BC005;
Entry   ioctl_invalid_function
        mov     ax,ERROR_INVALID_FUNCTION
        stc
        jmp     ioctl_exit

Entry   ioctl_generic_block             ;IOCTL 440Dh

        mov     [EXTERR_LOCUS],ErrLOC_Disk

        cmp     ch,IOC_DC               ; only disk devices are allowed to use
        jne     ioctl_invalid_function

        test    cl,IOFUNC_DRIVER        ; do only ioctl that are passed to dev
        jz      ioctl_invalid_function

        SVC     SVC_DEMGETDRIVEINFO     ; get the drive information
        jc      ioctl_exit

        test    ah,CD_ISNET             ; if the network drive, return error
        jnz     ioctl_invalid_function

;       We need to check to see if the command is a DOS 3.2 command
;       (i.e. 40H-42H and 60H-62H).  If it is we map these command
;       to  43H-45H and 63H-65H via "MapFunction".

        mov     bp,cx                   ; save the orginal function # in case
                                        ; MapFunction changes it.
        push    cx
        and     cl,IOFUNC_SUBFUNC       ; functions 40h-42h and 60h-62h == 0-2
        cmp     cl,2                    ; if (func AND 1FH) > 2, then no clear
        pop     cx                      ; restore function and category
        ja      short gib1              ; jump if this is a post DOS 3.2 IOCTL
        call    MapFunction             ; map 40->43, 41->44, 42->45, 60->63,
                                        ;     61->64, and 62->63.
        jnc     short gib1
        or      di,di                   ; if carry is set then either an error
        jz      short gib2              ; occured (di=random) or the request
        jmp     ioctl_invalid_function  ; is invalid

;       BL = unit number
;       CH = category
;       CL = function within the category
;       DS:DX = data packet
;       SI:DI = parameter packet address

gib1:   mov     ax,[User_In_AX]         ; Get back IOCTL function code
        SVC     SVC_DEMIOCTL            ; Finish up in prot-mode
        jc      ioctl_exit

        mov     cx,bp                   ; Restore original Category/sub-funtion
        cmp     cl,60h                  ; is it get device parameters
        jne     short gib2

;       For sub-function 60H (Get Device Parameters), we have to
;       transfer the data we got into the users buffer because the
;       formats are different.

        push    cs
        pop     ds
        ASSUME  DS:V86GROUP
        mov     si,offset V86GROUP:DeviceData

        mov     di,SaveDataArea.Offst           ; address destination
        mov     es,SaveDataArea.Segmt           ; (user's buffer)

        mov     ax,ds:[si].DDI_DP_Cyln          ; return cylinders
        mov     es:[di].DP_Cylinders,ax
        mov     al,ds:[si].DDI_DP_DeviceType    ; return device type
        mov     es:[di].DP_DeviceType,al
        mov     ax,ds:[si].DDI_DP_DeviceAttr    ; return device attributes
        mov     es:[di].DP_DeviceAttributes,ax
        mov     es:[di].DP_MediaType,0          ; always return 0 ??BUGBUG??

        mov     cx,SIZE a_bpb                   ; transfer BPB structure
        lea     di,ds:[di].DP_BPB
        lea     si,es:[si].DDI_DP_BPB
        rep     movsb
gib2:
        clc
        jmp     ioctl_exit
EndProc     $IOCTL

BREAK <MapFunction - map 3xBox ioctls into new disk ioctls>

;***    MapFunction - map 3xBox ioctls into new disk ioctls
;
;       map 40->43, 41->44, 42->45, 60->63,
;           61->64, and 62->63.
;
;       ENTRY:  BL = unit number
;               CH = category
;               CL = function within the category
;               DS:DX = data packet
;               ES:BX = device driver header
;               SI:DI = parameter packet address
;
;       EXIT:   IF Carry flag = 0  THEN no error
;               IF Carry flag = 1 and di = error_code  THEN  error
;               IF Carry flag = 1 and di = 0  THEN request finished OK
;
;       USES:   NONE
;
        ASSUME  DS:NOTHING,ES:NOTHING

Procedure MapFunction,NEAR,LOCAL
        SaveReg <es, bx, ax, cx>
        cld
        push    cs
        pop     es
        ASSUME  ES:V86GROUP                     ; (es) points to "our"  stuff
        mov     si,dx                           ; (ds:si) points to "user" stuff

        cmp     cl,40H                          ; if not 40 then try 60
        ljne    map6

;***    Set Device Parameters  -  Function 40H

map1:   mov     TrackStandardFlag,1             ; assume Standard
        test    ds:[si].DP_SpecialFunctions,TRACKLAYOUT_IS_GOOD
        jnz     short map2
        mov     TrackStandardFlag,0             ; wrong it's NON-Standard

map2:   mov     TrackFormat,0                   ; remember track format
        mov     ax,ds:[si].DP_TrackTableEntries
        mov     SectorsPerTrack,ax
        shl     ax,1                            ; multiply by 2
        SaveReg <si>
        mov     cx,ax
        mov     di,offset V86GROUP:TrackTable
        lea     si,ds:[si].DP_SectorTable
        rep     movsw                           ; make transfer (words)
        RestoreReg <si>

        test    ds:[si].DP_SpecialFunctions,ONLY_SET_TRACKLAYOUT
        jz      short map4                            ; set TrackLayout ONLY

        xor     di,di                           ; assume that request is OK
        stc
        pop     cx                              ; restore category/sub-function
        jmp     map18

;       Map the 3.x data into a OS/2 1.0 format (defined locally)

map4:   mov     di,offset V86GROUP:DeviceData
        mov     ax,ds:[si].DP_Cylinders         ; setup cylinders
        mov     es:[di].DDI_DP_Cyln,ax
        mov     al,ds:[si].DP_DeviceType        ; setup device type
        mov     es:[di].DDI_DP_DeviceType,al
        mov     ax,ds:[si].DP_DeviceAttributes  ; setup device attributes
        mov     es:[di].DDI_DP_DeviceAttr,ax

        SaveReg <si>
        mov     cx,SIZE a_bpb                   ; transfer BPB structure
        lea     si,ds:[si].DP_BPB
        lea     di,es:[di].DDI_DP_BPB
        rep     movsb
        RestoreReg <si>

;       Map the 3.x sub-function command into the OS/2 1.0 sub-func command
;       NOTE: - 1 (3.2) = 10 (OS/2 1.0) = copy DeviceBPB to BPB (fake bit is set)
;           - 0 and fakebit is 1 = 00B (OS/2 1.0) = (fake bit is cleared)
;           - 0 and fakebit is 0 = 01B (OS/2 1.0) = copy DeviceBPB to
;             BPBR (recomended/ default), (fake bit is unaffected)

        mov     di,offset V86GROUP:DeviceParm
        test    ds:[si].DP_SpecialFunctions,INSTALL_FAKE_BPB
        jnz     short map5

        mov     es:[di].DDI_DP_Command,1        ; assume 0000,0001B
        cmp     FakeSwitch,0                    ; check switch
        je      short map7                      ; command = 0000,0001B so jump

        mov     es:[di].DDI_DP_Command,0        ; command = 0000,0000B
        mov     FakeSwitch,0                    ; reset switch
        jmp     short map7

map5:   mov     es:[di].DDI_DP_Command,2        ; command = 0000,0010B
        mov     FakeSwitch,1                    ; set switch
        jmp     short map7

;***    Get Device Parameters  -  Function 60H

map6:   cmp     cl,60h                          ; if not 60 then try 41
        jne     short map8

        mov     di,offset V86GROUP:DeviceParm
        mov     al,ds:[si].DP_SpecialFunctions  ; get command
        mov     es:[di].DDI_DP_Command,al

        mov     SaveDataArea.Offst,si
        mov     SaveDataArea.Segmt,ds

;       Must return  (DS:DX) = data packet
;                    (SI:DI) = parameter packet address

map7:   mov     si,cs
        mov     ds,si
        ASSUME  DS:V86GROUP                 ; set up para and data pkt segs

;       (DI) = DeviceParm

        mov     dx,offset V86GROUP:DeviceData
   ASSUME    DS:NOTHING
        jmp     short map17

;***    Write a Track   - Function 41H

map8:   cmp     cl,41h                          ; if not 41 then try 61
        je      short map10

;***    Read a Track  -  Function 61H

map9:   cmp     cl,61h                          ; if not 61 then try 42
        jne     short map12                     ; do same thing for 61H as 41H

map10:  mov     di,offset V86GROUP:ParmPacket   ; es:di points to "our"  stuff
                                                ; ds:si points to "user" stuff
        SaveReg <si, di>                        ; save pointer to users' stuff
        mov     cx,(SIZE DDI_RWVPacket_param)-4 ; size of parameters
                                                ; minus size of tracktable entry
        rep     movsb
        RestoreReg <di, si>                     ; restore ptr to users' stuff

        call    ConvertTo3xLayout

        mov     al,TrackStandardFlag            ; Indicate if its standard
        mov     es:[di].DDI_RWV_Command,al      ; or not.

;       Must return     (DS:DX) = data packet
;                       (SI:DI) = parameter packet address

map11:  mov     dx,ds:[si].TRWP_TransferAddress.Offst
        mov     ds,ds:[si].TRWP_TransferAddress.Segmt
        mov     si,es                           ; si:di points to "our"  pseudo
        jmp     short map17

;***    Format/Verify a Track  -  Function 42H

map12:  cmp     cl,42h                          ; if not 42 then try 62
        jne     short map14

;       es:?? points to "our"  stuff
;       ds:si points to "user" stuff

        test    ds:[si].FP_SpecialFunctions,STATUS_FOR_FORMAT
        jz      short map13

        mov     ds:[si].FP_SpecialFunctions,0   ; Say we support any combination
        xor     di,di                           ; indicate that req finished OK
        stc                                     ; set at the same time!
        pop     cx                              ; restore category/sub-function
        jmp     short map18

map13:  call    ConvertToNewLayout
        jmp     short map16

;***    Verify a Track - Function 62H

map14:  cmp     cl,62h                          ; if not 62, we don't know what
        je      short map15                     ; it is 62
                                                ; so do nothing and return ..
        pop     cx                              ; restore category/sub-function
        jmp     short map18

map15:  call    ConvertTo3xLayout

map16:  mov     di,offset V86GROUP:ParmPacket   ; es:di points to "our"  stuff
                                                ; ds:si points to "user" stuff
        SaveReg <di>                            ; save pointer to our stuff
        mov     cx,(SIZE a_VerifyPacket)        ; size of parameters
        rep     movsb
        RestoreReg <di>                         ; restore pointer to our stuff

        mov     al,TrackStandardFlag            ; Indicate if its standard
        mov     es:[di].DDI_RWV_Command,al      ; or not.

        mov     ax,SectorsPerTrack
        mov     es:[di].DDI_FP_NumSec,ax        ; Let's get number of sectors
        mov     es:[di].DDI_FP_Reserved,0       ; Let's make this 0

;       (DS:DX) = data packet
;       (SI:DI) = parameter packet address

        mov     si,es                           ; si:di points to param packet
        push    cs
        pop     ds
        ASSUME  DS:V86GROUP
        mov     dx,offset V86GROUP:DummyData    ; ds:dx points dummy data packet

map17:  RestoreReg <cx>                         ; restore category/sub-function
        add     cl,3
        clc
map18:  RestoreReg <ax, bx, es>
        ret
EndProc   MapFunction


BREAK <ConvertToNewLayout - Convert a 3.x track layout to new track layout>

;***    ConvertToNewLayout - Convert a 3.x track layout to new track layout
;
;       ConvertToNewLayout converts the format table if it is in the form
;       of SECTOR #, SECTOR SIZE format (dw, dw) to the form of
;       <c,h,r,n> format (db, db, db, db)
;
;       NOTE: (0=>80H, 1=>100H, 2=>200H, 3=>400H)
;
;       ENTRY:  (ES) = kernel data segment (DATA)
;               (DS:SI) points to "user" stuff
;               TrackFormat = 0 = SECTOR #, SECTOR SIZE format (dw, dw)
;                           = 1 = <c,h,r,n> format (db, db, db, db)
;
;       EXIT:   Reformated Track_Table if necessary
;
;       USES:   NONE
;
        ASSUME  DS:NOTHING,ES:V86GROUP

Procedure ConvertToNewLayout,NEAR,LOCAL
        SaveReg <ax, bx, cx, dx, di>
        mov     cx,SectorsPerTrack
        jcxz    sst4
        mov     di,offset V86GROUP:TrackTable
        mov     dx,ds:[si].VP_Head              ; dl = head
        mov     ax,ds:[si].VP_Cylinder          ; al = cylinder

;       Convert sector size to index
;
;       NOTE:  (BX) = 80H, 100H, 200H, or 400H so  bh = 0H, 1H, 2H, or 4H

sst1:   cmp     TrackFormat,0                   ; if already converted, just
        jne     short sst3                      ;  put the head and cyl in

        mov     bx,es:[di].ST_SectorSize
        mov     bl,3                            ; assume index 3
        cmp     bh,2                            ; if 400H then jump (index 3)
        ja      short sst2
        mov     bl,bh                           ; is index 0, 1, or 2 !!

sst2:   mov     ah,es:[di].ST_C                 ; (AH) = sector ID
        mov     es:[di].ST_R,ah                 ; sector ID
        mov     es:[di].ST_N,bl                 ; sector index

sst3:   mov     es:[di].ST_C,al                 ; cylinder
        mov     es:[di].ST_H,dl                 ; head

        add     di,4
        loop    sst1

sst4:   mov     TrackFormat,1
        RestoreReg <di, dx, cx, bx, ax>
        ret
EndProc   ConvertToNewLayout


BREAK <ConvertTo3xLayout - Convert new track layout to 3.x's format>

;***    ConvertTo3xLayout - Convert new track layout to 3.x's format
;
;       ConvertTo3xLayout converts the format table if it is in the form
;       of <c,h,r,n> format (db, db, db, db) to the form of
;       SECTOR #, SECTOR SIZE format (dw, dw)
;
;       NOTE: (0=>80H, 1=>100H, 2=>200H, 3=>400H)
;
;       ENTRY:  (ES) = kernel data segment (DATA)
;               TrackFormat = 0 = SECTOR #, SECTOR SIZE format (dw, dw)
;                           = 1 = <c,h,r,n> format  (db, db, db, db)
;
;       EXIT:   Reformated Track_Table if necessary
;
;       USES:   NONE
;
        ASSUME  DS:NOTHING,ES:V86GROUP

Procedure ConvertTo3xLayout,NEAR,LOCAL
        .if     <<TrackFormat> ne 0>
          push    ax
          push    bx
          push    cx
          push    di
          mov     di,offset V86GROUP:TrackTable
          mov     cx,SectorsPerTrack
          .if     ncxz
            xor     ah,ah
            .repeat
              push    cx
              mov     cl,es:[di].ST_N                 ; convert sector index
              mov     bx,128                          ; to sector size
              shl     bx,cl
              pop     cx

              ; (BX) = sector size

              mov     al,es:[di].ST_R                 ; sector ID
              xor     ah,ah
              mov     es:[di].ST_SectorNumber,ax
              mov     es:[di].ST_SectorSize,bx        ; sector size

              add     di,4
            .loop
          .endif
          mov     TrackFormat,0
          pop     di
          pop     cx
          pop     bx
          pop     ax
        .endif
        ret

EndProc   ConvertTo3xLayout

V86CODE ENDS

        END
