;       SCCSID = @(#)devioctl.asm       6.12 92/03/18
TITLE   DEVIOCTL - API IOCTL system call
NAME    DEVIOCTL
PAGE    ,132

SCCSID  equ     "@(#)devioctl.asm       6.12 92/03/18"

;       IBM/Microsoft Confidential
;
;       Copyright (c) IBM Corporation 1987, 1989
;       Copyright (c) Microsoft Corporation 1987, 1989
;
;       All Rights Reserved
;
;   Device IO Control support
;
;       Public routines:    Local routines:
;       ----------------    ---------------
;
;       w_DevIOCtl
;                           SpoolIOCtl
;                           wdi_BeginFormat
;                           wdi_updateVPB
;       sw_DevIOCtl
;                           swioc_calldriver
;                           ErrorTriage
;       GetDriveInfo
;       GetHandleInfo
;                           GetHandleSFT
;                           Update_SFT_Size
;                           Install_ExtDrive
;
;   Modifications:
;       59263   12/17/92 Bundy  Save DS register around call to FuByte,
;                               which sets DS to 0
;       73961   09/15/93 Nizinski Move code out of HIGH2CODE segment
;                                 into HIGH3CODE segment since HIGH2CODE
;                                 got > 64K when defect 72291 was put in.
;       83608   05/10/94 ACW      Move code out of HIGH2CODE segment
;                                 into HIGH3CODE segment since HIGH2CODE
;                                 got > 64K again.
;               04/05/95 Dillon   Added security callout support.       ;@SEC
;       72417   18-11-95 MLM      Reduce fscrit serialization.  Changed to
;                                 LJ* macros.
;       147814  04/26/96 DRB      Add category 08 IOCTLS for multi-tray ATAPI
;                                 CD drives

;

.xlist
include dosseg.inc
include basemaca.inc
include osmaca.inc
include fnofs.inc
include oldmaca.inc
.xcref
include argframe.inc
include bpb.inc
include curdir.inc
include devsym.inc
include dossymc.inc
include dpb.inc
include dskioctl.inc    ; in bios
include error.inc
include filemode.inc
include filesys.inc
include fsc.inc
include mi.inc
include mult.inc
include protmode.inc
include seldesc.inc
include selxport.inc
include virtsym.inc
include proc.inc
include sft.inc
include sicg.inc
include tcbdata.inc
include vpb.inc
include fsh.inc                 ; depends on vmowner,filemode, seldesc, and
                                ; virtsym incs
.cref
.list

include fileio.inc
include inicfg.inc
include ioctl.inc
include splmsgp.inc


        ModName , FNO_DEVIOCTL_ASM

        ReferGDT        GDT_VPB,WORD

        ReferTask       CurrTCB,WORD
        ReferTask       CurrTSD,WORD
        ReferTask       ExtErr_Locus,BYTE
                                            ;   Defect 72417 change starts
        ReferTask       Pid,WORD            ; process ID
        ReferTask       ProcType,BYTE
                                            ;   Defect 72417 change ends

        EXTRNFAR        AllocReqPacketWait
        EXTRNFAR        DevIOCall2
        EXTRNFAR        DevLockPhysDrive
        EXTRNFAR        DevUnlockPhysDrive
        EXTRNFAR        ErrorTriage           ; 73961
        EXTRNFAR        ForceMount            ; 73961
        EXTRNFAR        FreeReqPacket
        EXTRNFAR        FreeTempBuffer
        EXTRN           FreeTempBuffer:NEAR   ; 83608
        EXTRNFAR        fscFind               ; 73961
        EXTRNFAR        FuWord
        EXTRNFAR        FuBuff
        EXTRNFAR        FuBuffz
        EXTRNFAR        SuWordNF
        EXTRNFAR        SuByteNF              ; added CP20 B730217  @PN1
        EXTRNFAR        FuByteNF              ; added CP20 J200457  @85
        EXTRNFAR        FuWordNF              ; added CP20 J200457  @85
        EXTRNFAR        GetTempBuffer
        EXTRN           GetTempBuffer:NEAR    ; 83608
        EXTRNFAR        GetDriveInfo          ; 73961
        EXTRNFAR        GetHandleSFT          ; 73961
        EXTRNFAR        GetThisDrv            ; 73961
        EXTRNFAR        SetVPB
        EXTRNFAR        SFFromHandle          ; 73961
        EXTRNFAR        ShSU                  ; 73961
        EXTRNFAR        w_SICG
        EXTRNFAR        LockDrive
        EXTRNFAR        w_TransactNmPipe
        EXTRNFAR        UnlockDrive
        EXTRNFAR        w_FileIO
        EXTRNFAR        UseCache
        EXTRNFAR        wdi_BeginFormat       ; 73961
ifdef SECURITY
        EXTRNFAR        SecDevIoctl                                     ;@SEC
        EXTRNFAR        SpoolIOCtl      ; moved from high2/high3code seg;@SEC
endif

        EXTRN           GetCDSFromDrv:NEAR
        EXTRN           $TJMP:NEAR

        EXTRN           ErrMap24:BYTE
        EXTRN           minimumBPB:BYTE
        EXTRN           DecVPBRef:NEAR
        EXTRN           DevIOCall2:NEAR
        EXTRN           FuBuff:NEAR
        EXTRN           FuBuffz:NEAR
        EXTRN           FuByte:NEAR
        EXTRN           IncVPBRef:NEAR
        EXTRN           JFNFindFree:NEAR
        EXTRN           ProtMode:NEAR
        EXTRN           RealMode:NEAR
        EXTRN           RedetermineMedia:NEAR
        EXTRN           ResetCurrency:NEAR
        EXTRN           SelToSeg:NEAR
        EXTRN           SFFromHandle:NEAR
        EXTRN           SFFromSFN:NEAR
        EXTRN           SFLockNoSD:NEAR
        EXTRN           SFUnlock:NEAR
        EXTRN           SuByte:NEAR
        EXTRN           SuWord:NEAR
        EXTRNFAR        TransactSpooler   ;83608
        EXTRN           w_Write:NEAR

        EXTRNF32        demMapSeg
        EXTRN32         _BufFlushAll
        EXTRN32         _BufDiscard

;*  equates used for device/file flag

IOC_NotDevice   equ 0
IOC_IsDevice    equ 1

        FARCODE HIGH2
HIGH2CODE SEGMENT

;***    w_16DevIOCtl - worker for DOS16DEVIOCTL
;
;       This worker handles the calls from DOS16DEVIOCTL, and a few other places
;       This routine calls w_devioctl to do the actual work after setting up the
;       two extra parameters on stack (ptrs to return data and param len)
;
;       ENTRY:  AL     = Category code
;               AH     = Function code
;               BX     = File Handle
;               ES:DI -> parameter packet
;               CX     = length of parameter packet (0 if unknown)
;               DS:DX -> data packet
;               SI     = length of data packet (0 if unknown)
;
;       EXIT:   Carry set -- error
;                   AX = error code
;               Carry clear -- no error
;
;       USES: AX, BX, CX, DX, DI, SI, DS, ES, flags
;

Procedure       w_16DevIOCtl,<faronly,far16>

        Assume  DS:NOTHING,ES:NOTHING,SS:TASKAREA

LocalVar        w16di_catfunc,WORD

LocalVar        w16di_ParmLenOut,WORD
LocalVar        w16di_DataLenOut,WORD


        EnterProc       <ax>

        mov     w16di_ParmLenOut,cx
        mov     w16di_DataLenOut,si
        push    ss
        lea     ax, TASKAREA:w16di_ParmLenOut
        push    ax
        push    ss
        lea     ax, TASKAREA:w16di_DataLenOut
        push    ax

        mov     ax,w16di_catfunc        ; ax = cat+function
        call    w_DevIOCtl
        LeaveProc
        RETP

EndProc w_16DevIOCtl

;***    w_32DevIOCtl - worker for DOS32DEVIOCTL
;
;       This worker handles the calls from DOS32DEVIOCTL
;       This routine calls w_devioctl to do the actual work after setting up the
;       Out Length with default values
;
;       ENTRY:  SS:BX  = ptr to parameter packet (see argframe.inc)
;
;       EXIT:   Carry set -- error
;                   AX = error code
;               Carry clear -- no error
;
;       USES: AX, BX, CX, DX, DI, SI, DS, ES, flags
;

        CpuMode 386

Procedure       w_32DevIOCtl,<faronly,far16>

        Assume  DS:NOTHING,ES:NOTHING,SS:TASKAREA

        les     di, TASKAREA:[bx].DIOC_pParmLenInOut ; es:di paramleninout        ;### b722959
        mov     ax,es              ; Thunking causes the selector to not be zero  ;### b722959
        and     ax, SELECTOR_MASK  ; if it was intended to be zero.  We must reset;### b722959
        or      ax, ax             ; to zero.                                     ;### b722959
        jnz     short w32dc0a      ; Continue                                     ;### b722959
        mov     es, ax             ; Set to zero.                                 ;### b722959
w32dc0a:                                                                          ;### b722959
        push    es                                                                ;### b722959
        push    di                                                                ;### b722959
;       push    dword ptr TASKAREA:[bx].DIOC_pParmLenInOut                        ;### b722959
        les     di, TASKAREA:[bx].DIOC_pDataLenInOut                              ;### b722959
        mov     ax,es              ; Thunking causes the selector to not be zero  ;### b722959
        and     ax, SELECTOR_MASK  ; if it was intended to be zero.  We must reset;### b722959
        or      ax, ax             ; to zero.                                     ;### b722959
        jnz     short w32dc0b      ; Continue                                     ;### b722959
        mov     es, ax             ; Set to zero.                                 ;### b722959
w32dc0b:                                                                          ;### b722959
        push    es                                                                ;### b722959
        push    di                                                                ;### b722959
;       push    dword ptr TASKAREA:[bx].DIOC_pDataLenInOut                        ;### b722959
        mov     al,byte ptr TASKAREA:[bx].DIOC_Category
        mov     ah,byte ptr TASKAREA:[bx].DIOC_Function
        les     di,TASKAREA:[bx].DIOC_pParm
        mov     cx,TASKAREA:[bx].DIOC_ParmLenMax
        mov     si,TASKAREA:[bx].DIOC_DataLenMax
        lds     dx,TASKAREA:[bx].DIOC_pData
        mov     bx,TASKAREA:[bx].DIOC_DevHandle

; at this point
;               AL     = Category code
;               AH     = Function code
;               BX     = File Handle
;               ES:DI -> parameter packet
;               CX     = length of parameter packet
;               DS:DX -> data packet
;               SI     = length of data packet
        push    ax
        or      cx,cx           ; check param length
        jnz     short w32dc10
        mov     ax,es           ; param length is zero
        and     ax,SELECTOR_MASK; Is selector and offset zero?
        or      ax,di           ;
        jnz     short w32dc90   ; pointer non NULL, return inval_param error
w32dc10: or     si,si           ; is the data max len zero ?
        jnz     short w32dc20   ; no , go call devioctl worker
        mov     ax,ds           ; data length is zero.
        and     ax,SELECTOR_MASK; Is pointer zero?
        or      ax,dx           ;
        jnz     short w32dc90   ; pointer non NULL, return inval_param error
w32dc20: pop    ax
        call    w_DevIOCtl
        ret

w32dc90: pop    ax
        mov     ax,ERROR_INVALID_PARAMETER
        add     sp,8            ; Dump the two dword pointers
        stc
        ret

        CpuMode reset

EndProc w_32DevIOCtl

;***    w_DevIOCtl - worker for DOSDEVIOCTL
;
;       This worker handles the calls from DOSDEVIOCTL, and a few other places.
;       This routine (or some lower code) takes care of getting the request to
;       the device driver (or elsewhere if necessary).
;
;       When we get a IOCtl request, we must either pass it to the net, to an
;       FSD, or pass it to the sub-worker (sw_DevIOCtl) to handle the local or
;       FAT cases.
;
;       ENTRY:  AL     = Category code
;               AH     = Function code
;               BX     = File Handle
;               ES:DI -> parameter packet
;               CX     = length of parameter packet (0 if unknown)
;               DS:DX -> data packet
;               SI     = length of data packet (0 if unknown)
;               PUSH    @OTHER ParmLenInOut
;               PUSH    @OTHER DataLenInOut
;               call    w_DevIOCtl
;
;       EXIT:   Carry set -- error
;                   AX = error code
;               Carry clear -- no error
;
;       USES: AX, BX, CX, DX, DI, SI, DS, ES, flags
;
;       ALGORITHM
;       {
;           SFFromHandle (Handle)           /* returns pointer to SFT */
;           if (not a physical disk call)
;               if (device == physical disk handle)
;                   return (invalid handle)
;           else
;               if (device != physical disk handle)
;                   return (invalid handle)
;           SFLockNoSD & save lock address
;           if (category == 8)              /* disk(ette) control */
;               if (function == RedetermineMedia) {
;                   RedetermineMedia
;                   Jump to wdi_done
;               }
;               else if (function == BeginFormat) {
;                   wdi_BeginFormat
;                   Jump to wdi_done
;               }
;           else
;               if (sf_FSC == reserved) {   /* special FAT file system case */
;                   if (category == 9)              /* physical disk control */
;                       if (function == lockdrive) {
;                           DevLockPhysDrive
;                           Jump to wdi_done
;                       }
;                       else if (function == unlockdrive) {
;                           DevUnlockPhysDrive
;                           Jump to wdi_done
;                       }
;                       else
;                           hDev = sf_devptr (pointer to PhysDiskInfoTbl entry)
;                   else if (device, net or pipe SFT)
;                       hDev = pointer to device driver header
;                   else                            /* normal file case */
;                       hDev = pDPB
;                   set up registers, etc.
;                   call sw_DevIOCtl
;               }                           /* end: FAT file system case */
;               else {                      /* IFS/FSD case */
;                   if (Physical disk request)
;                       Set invalid handle error & carry
;                   else {
;                       push necessary parameters
;                       call sf_FSC.fsc_dispatch.FS_IOCTL
;                       if (error)
;                           set carry
;                   }                       /* end: IFS/FSD case */
;               }
;               if (not Carry)
;                   if (Category == 8: Disk/Diskette Control)
;                       if (Function == 43h: Set Device Parameters)
;                           if (Command == 2: Specify new BPB)
;                               wdi_UpdateVPB (FAT flag)
; wdi_done: SFUnlock
;           Return
;       }

        ASSUME  DS:NOTHING,ES:NOTHING,SS:TASKAREA
Procedure w_DevIOCtl,near
ArgVar   wdi_pDataLenInOut,DWORD
ArgVar   wdi_pParmLenInOut,DWORD

; WARNING WARNING WARNING WARNING WARNING WARNING WARNING WARNING       ;@SEC
; WARNING WARNING WARNING WARNING WARNING WARNING WARNING WARNING       ;@SEC
; WARNING WARNING WARNING WARNING WARNING WARNING WARNING WARNING       ;@SEC
;                                                                       ;@SEC
; Note: The following LocalVars directly map to the secioctl.inc  struc.;@SEC
;       If you change their order (or size), you may then need to make  ;@SEC
;       a corresponding change to secioctl.inc.                         ;@SEC
;       Also note that the address of the bottom-most parm              ;@SEC
;       (wdi_SFN) is passed to the code which does the callout          ;@SEC
LocalVar wdi_SFTLockAddr,DWORD
LocalVar wdi_ParmPackAddr,DWORD
LocalVar wdi_cwParm,WORD
LocalVar wdi_DataPackAddr,DWORD
LocalVar wdi_cwData,WORD
LocalVar wdi_Handle,WORD
LocalVar wdi_Function,BYTE                      ; Function must immediately
LocalVar wdi_Category,BYTE                      ; precede category for mov word
LocalVar wdi_SFN,WORD
; end of LocalVars which map to structure in secioctl.inc.              ;@SEC
; end WARNING WARNING WARNING WARNING WARNING WARNING WARNING WARNING   ;@SEC

LocalVar wdi_dummycds,<SIZE curdir>
LocalVar wdi_SaveAX,WORD

        EnterProc <0,0,es,di,cx,ds,dx,si,bx,ax,-1> ; initialize stack frame
                                                ; (includes category & function

        lea     di,wdi_dummycds
        push    bx                              ;### PTR B720732
        mov     bx,TASKAREA:[CurrTCB]                      ;### PTR B720732
        mov     TASKAREA:[bx].TCBpTmpCDS.Offst,di       ; Initialize TCBpTmpCDS ;### PTR B720732
;### PTR B720732        mov     TASKAREA:[ptda_pCDS],di ; Initialize ptda_pCDS
        pop     bx                              ;### PTR B720732

;       MMIOPH Performance Hook Removed.
;       Maj:Min = 6062H, Data = bx (handle), ax (category/function)

;*  Check for category 8

        cmp     al, IOC_DC                      ; check for category 8
        jne     short wdi01                     ; no, go on

        cmp     bx, -1                          ; null handle ?
        jne     short wdi01                     ; no

        or      cx, cx                          ; parm-pkt length specified?
        jz      short wdi00                     ; no, gotta trust the caller
        cmp     cx, 1                           ; parm-pkt length > 1?
        ja      short wdi00                     ; yes

        mov     ax,ERROR_INVALID_DRIVE          ; set error return code
        stc
        jmp     wdi170a                         ; bail out

;*  Check for special category 8 cases that do not need media in the drive.

wdi00:
        cmp     ah, IODC_BR                     ; removeable media?
        je      wdi90                           ; yes
        cmp     ah, IODC_PLK                    ; physical lock drive and/or
        je      wdi90                           ; eject media? yes
        cmp     ah, IODC_SP                     ; set device parameters?
        jne     short wdi00a                    ; no
        pusha
        push    ds                              ; 59263
        mov     bx, wdi_ParmPackAddr.Segmt      ;
        mov     si, wdi_ParmPackAddr.Offst      ; bx:si -> parameter pkt
        call    FuByte                          ; AL = command byte
        pop     ds                              ; 59263
        jc      short wdi01ex
        cmp     al, 01h                         ; must = 01h
        jmp short wdi00b

wdi00a:
        cmp     ah, IODC_GP                     ; get device parameters?
        jne     short wdi01a                    ; no
        pusha
        push    ds                              ; 59263
        mov     bx, wdi_ParmPackAddr.Segmt      ;
        mov     si, wdi_ParmPackAddr.Offst      ; bx:si -> parameter pkt
        call    FuByte                          ; AL = command byte
        jc      short wdi01ex
        or      al, al                          ; must = zero
wdi00b:
        pop     ds                              ; 59263
        popa
        jz      wdi90                           ; yes, command byte ok
        mov     ax,ERROR_INVALID_PARAMETER
        stc
        jmp     wdi170a                         ; bail out

wdi01ex:
        pop     ds                              ; 59263
        popa
        mov     ax,ERROR_PROTECTION_VIOLATION
        jmp     wdi170a                         ; bail out

wdi01a:
        cmp     ah, IODC_QD                     ; quiesce/restart diskette ctl?
        je      wdi90                           ; yes
        cmp     ah, IODC_PLS                    ; physical lock status?
        je      wdi90                           ; yes
        cmp     ah, IODC_SM                     ; 147814
        je      wdi90                           ; 147814
        cmp     ah, IODC_GA                     ; 147814
        je      wdi90                           ; 147814
wdi01:
        call    SFFromHandle                    ; ES:DI -> sft
        jc      wdi30                           ; error

        push    bx                              ;                       ;@SEC
        mov     bx,es:[di.sf_fsi].sfi_selfsfn   ; BX = SFN              ;@SEC
        mov     wdi_SFN,bx                      ;                       ;@SEC
        pop     bx                              ;                       ;@SEC

        test    es:[di].sf_flags2, SFF2_SPOOLED ; spooled file?
        jz      short wdi05                     ; no, go ahead

; For spooled files, SpoolIOCtl takes care of handling the request

        mov     ah, BYTE ptr [wdi_Function]
        mov     al, BYTE ptr [wdi_Category]
        push    wdi_ParmPackAddr.Segmt
        push    wdi_ParmPackAddr.Offst
        push    wdi_pDataLenInOut.Segmt
        push    wdi_pDataLenInOut.Offst
        push    wdi_pParmLenInOut.Segmt
        push    wdi_pParmLenInOut.Offst

        CALLFAR SpoolIOCtl      ;83608          ; handle spooled file IOCtls
        jmp     wdi160                          ; return

wdi05:  cmp     al,IOC_PD                       ; is this a phys dsk IOCTL call?
        je      short wdi10                     ; yes, make sure phys disk handle

;*  not a phys disk request
        test    es:[di].sf_fsi.sfi_mode2,open2_phys_disk ; is this a phys disk?
        jz      short wdi20                     ; no, continue
        jmp     short wdi30                     ; go return error

;*  phys disk request

wdi10:  test    es:[di].sf_fsi.sfi_mode2,open2_phys_disk ; is this a phys disk?
        jz      short wdi30                     ; no, return error

wdi20:  SaveReg <ax,bx,cx,dx>
        call    SFLockNoSD                      ; Lock down SFT
        RestoreReg <dx,cx,bx,ax>                ; (do no shutdown checking)

        mov     wdi_SFTLockAddr.Offst,di
        mov     wdi_SFTLockAddr.Segmt,es        ; Save lock address
        jc      short wdi30                     ; SFT vanished under us
                                                ; cleanup and return

;*  Check for special category 8 cases

        cmp     al,IOC_DC                       ; check for category 8
                                                ;   Defect 72417 change starts
        LJNE    wdi60                           ; no, go on
                                                ;   Defect 72417 change ends

;*  Category 8 (disk/diskette control)
;
;   Some requests are handled here, because they don't go to the device driver

        cmp     ah,IODC_RM                      ; check for RedetermineMedia
        jne     short wdi50                     ; no, go on

;*  RedetermineMedia
;
        mov     bx,wdi_Handle
;*  Changed to CALLFAR for 73961.
        CALLFAR GetHandleSFT                    ; ES:DI -> SFT, DS:SI -> DPB
        jc      short wdi40                     ; error, go return

        test    es:[di].sf_flags,DEVID_DEVICE   ; is file SFT?
        jnz     short wdi30                     ; not DASD SFT, return error

ifdef SECURITY
;                                                                       ;@SEC
; Security callout.                                                     ;@SEC
; Note that we don't need to save/restore ax around the callout since   ;@SEC
; Redeterminemedia doesn't use it as an input parm.                     ;@SEC
                                                                        ;@SEC
        push    bx                                                      ;@SEC
        lea     bx,wdi_SFN                      ; point to parms        ;@SEC
                                                                        ;@SEC
        CALLFAR SecDevIoctl                                             ;@SEC
        pop     bx                                                      ;@SEC
        jc      short wdi25                     ; rejected. get out.    ;@SEC
endif


        call    RedetermineMedia                ; Tell the FSD to UNMOUNT-MOUNT
                                                ; the volume
        jc      short wdi40                     ; Some sort of error. Return.
wdi25:
        pushf                                   ; save error state
        lds     si,wdi_SFTLockAddr              ; DS:SI -> SFT
        and     ds:[si].sf_flags2,NOT SF_FORMAT_MOUNT ; clear format_mount flag
        popf                                    ; restore error state
        jmp     wdi160                          ; go cleanup and return

wdi30:  mov     ax,ERROR_INVALID_HANDLE         ; return error
        stc
wdi40:  jmp     wdi160

wdi50:  cmp     ah,IODC_BF                      ; check for BeginFormat
        jne     short wdi60                     ; no, go on

;*  BeginFormat

        mov     bx,wdi_Handle
;*  Changed to CALLFAR for 73961.
        CALLFAR GetHandleSFT                    ; ES:DI -> SFT, DS:SI -> DPB
        jc      short wdi40                     ; error, go return

;*  First check to be sure that we have this drive locked

        mov     cx,DS:[SI].dpb_drive_lock       ; (cx) = drive lock word
        cmp     cx,SS:Pid                       ; Is the drive locked by us?
        jnz     short wdi55                     ; not locked by us, error

;*  Now check to be sure that we have this drive opened DirectAccess

        test    es:[di].sf_fsi.sfi_mode,open_direct
        jz      short wdi57                     ; not DirectAccess - error
ifdef SECURITY
;                                                                       ;@SEC
; Security callout.                                                     ;@SEC
; Note that we don't need to save/restore ax around the callout since   ;@SEC
; wdi_BeginFormat doesn't use it as an input parm.                      ;@SEC
                                                                        ;@SEC
        push    bx                                                      ;@SEC
        lea     bx,wdi_SFN                      ; point to parms        ;@SEC
                                                                        ;@SEC
        CALLFAR SecDevIoctl                                             ;@SEC
        pop     bx                                                      ;@SEC
        jc      wdi160                          ; rejected. get out.    ;@SEC
endif

        lds     dx,wdi_DataPackAddr             ; DS:DX -> Data packet

;*  Changed to CALLFAR for 73961
        CALLFAR wdi_BeginFormat                 ; go unmount & mount
        jmp     wdi160                          ; go cleanup and return

wdi55:  mov     ax,ERROR_LOCK_VIOLATION ; drive not locked, or not owned by us
wdi56:  stc
        jmp     wdi160

wdi57:  mov     ax,ERROR_INVALID_ACCESS ; must be opened DirectAccess
        jmp     short wdi56

;*  Handle FAT (local) & FSD routing

wdi60:  cmp     es:[di].sf_FSC.Offst,FSC_RESERVED   ; test for FAT case
        je      short wdi90                         ; local/FAT case

;*  IFS/FSD case
                                                ; ES:DI -> SFT
        cmp     al,IOC_PD                       ; check for phys. disk category
        je      short wdi30                     ; return invalid handle

ifdef SECURITY
;                                                                       ;@SEC
; Security callout.                                                     ;@SEC
                                                                        ;@SEC
        push    bx                                                      ;@SEC
        lea     bx,wdi_SFN                      ; point to parms        ;@SEC
                                                                        ;@SEC
        CALLFAR SecDevIoctl                                             ;@SEC
        pop     bx                                                      ;@SEC
        jc      wdi160                          ; rejected. get out.    ;@SEC
        mov     al,wdi_Category                 ; al might have been    ;@SEC
                                                ; trashed during callout;@SEC
endif

        lea     bx,es:[di].sf_fsi               ; ES:BX -> SFT.fs-independent
        push    es
        push    bx
        lea     bx,es:[di].sf_fsd               ; ES:BX -> SFT.fs-independent
        push    es
        push    bx
        xor     ah,ah                           ; AX = category
        push    ax                              ; category
        mov     al,wdi_Function                 ; AX = function
        push    ax                              ; function
        push    wdi_ParmPackAddr.Segmt          ; ptr to parameter packet
        push    wdi_ParmPackAddr.Offst
        push    cx                              ; length of parameter packet

        push    wdi_pParmLenInOut.Segmt         ; ptr to Parm length InOut
        push    wdi_pParmLenInOut.Offst
        push    ds
        push    dx                              ; ptr to data packet
        push    si                              ; length of data packet
        push    wdi_pDataLenInOut.Segmt         ; ptr to Data length InOut
        push    wdi_pDataLenInOut.Offst
        xor     ax,ax
        mov     ds,ax                           ; zero out user selector

        les     di,es:[di].sf_FSC               ; ES:DI -> FSC
        call    es:[di].FS_IOCTL                ; call FSD routine

        or      ax,ax                           ; error code? (clears carry)
        LJZ     wdi150                          ; no, continue (AX = 0 -> non-FAT)

        stc                                     ; set carry (error)
        jmp     wdi160                          ; go return error

;*  FAT / local device case
;


;* HACKING; we need to copy the Max Lengths of Data and Parameter pkts
;* (both input) into wdi_[param/data]LenInOut for the FAT case;  (since we
;* don't know the size, we assume it to be the maximum). The 16 bit devioctl
;* didn't have this and so to use the common code, we invented these two vars
;* on the stack for 16 bit. And so, we don't need to do FUBUFF stuff for 16
;* bit IOCtl;
;* The hack is : check on the selectors - if they are the same as SS, then
;* we are 16 bit


wdi90:  mov     bx,ss
        cmp     bx,wdi_pDataLenInOut.Segmt      ;
        je      short wdi95                     ; 16 bit; no need to copy
                                                ; done by w_16Devioctl
; we need to initialize wdi_p[data/param]LenInOut to their max lengths
        SaveReg <es,di,ds>
        mov     bx,wdi_pDataLenInOut.Segmt
        mov     di,wdi_pDataLenInOut.Offst
        mov     ax,bx
        and     ax,SELECTOR_MASK                ;b715949 a null pointer is valid
        or      ax,di
        jz      short wdi91
        mov     ax,wdi_cwData

        CALL    SuWord                          ;
wdi91:  jc      short wdi92
        mov     bx,wdi_pParmLenInOut.Segmt
        mov     di,wdi_pParmLenInOut.Offst
        mov     ax,bx
        and     ax,SELECTOR_MASK                ;b715949 a null pointer is valid
        or      ax,di                           ;clears carry
        jz      short wdi92
        mov     ax,wdi_cwParm
        CALL    SuWord                          ;
wdi92:  RestoreReg      <ds,di,es>
        jc      wdi160                          ; ax = error code

        CpuMode Reset

wdi95:
        mov     ax, word ptr wdi_Category       ; ah = function;al = category
        cmp     al, IOC_DC                      ; category 8 ?
        jne     short wdi95a                    ; no
        mov     wdi_SFN, -1
        cmp     [wdi_Handle], -1                ; category 8, null handle case?
        je      wdi140                          ; yes

;****old wdi95:
wdi95a:
        mov     bx,es:[di.sf_fsi].sfi_selfsfn   ; BX = SFN
        mov     wdi_SFN,bx

        cmp     al,IOC_PD                       ; check for category 9
        jne     short wdi120                    ; no, continue

;*  Category 9 (physical disk control)
;
;   Some requests are handled here, because they don't go to the device driver

        mov     bx,[wdi_Handle]                 ; BX = handle
        cmp     ah,IOPD_LK                      ; check for lock drive
        je      short wdi97                     ; yes, go lock drive

        cmp     ah,IOPD_UL                      ; check for unlock drive
        je      short wdi97                     ; yes, go unlock drive

        les     di,es:[di].sf_devptr            ; ES:DI -> PhysDiskTbl entry
        jmp     wdi140                          ; continue

wdi97:
ifdef SECURITY
        mov     wdi_SaveAX,ax                   ; callout will alter ax.;@SEC
        push    bx                                                      ;@SEC
        lea     bx,wdi_SFN                      ; point to parms        ;@SEC
                                                                        ;@SEC
        CALLFAR SecDevIoctl                                             ;@SEC
        pop     bx                                                      ;@SEC
        jc      wdi160                          ; rejected. get out.    ;@SEC
        mov     ax,wdi_SaveAX                                           ;@SEC
                                                                        ;@SEC
endif
        cmp     ah,IOPD_UL                      ; check for unlock drive;@SEC
        je      short wdi110                    ; yes, go unlock drive  ;@SEC
; fall through for DevLockPhysDrive                                     ;@SEC

wdi100: CALLFAR DevLockPhysDrive                ; lock the drive
        jmp     wdi160                          ; go cleanup and return

wdi110: CALLFAR DevUnlockPhysDrive              ; unlock the drive
        jmp     wdi160                          ; go cleanup and return

; check for character device (and others)

wdi120: test    es:[di].sf_flags,DEVID_DEVICE+SF_ISNET+SF_PIPE
                                                ; is it device, net, or pipe
        jz      short wdi130                    ; no, use DPB

;*  Character device, net, or pipe: use sf_devptr
;
;   Check to make sure that the value in sf_devptr is non-zero. If it is
;   zero, then this must be a pipe and it is not legal to do DevIOCtl on
;   a pipe handle.
;
        les     di,es:[di].sf_devptr            ; ES:DI -> device driver ptr
        mov     bx,es
        or      bx,bx                           ; Is there a device for this handle?
        jz      short wdi125                    ; No. Return invalid handle error.
        mov     bl,IOC_IsDevice                 ; BL = 1 (is a device)
    .errnz  IOC_IsDevice-1
        jmp     short wdi140                    ; Go call the driver

;
; The handle specified has no device associated with it. The only case that
; can have this is a pipe. We return ERROR_INVALID_FUNCTION as this is what
; earlier versions of OS/2 returned.
;
wdi125: mov     ax,ERROR_INVALID_FUNCTION
        STC
        jmp     wdi160


;*  Not a device, use pDPB

wdi130: mov     di,es:[di].sf_fsi.sfi_hVPB      ; DI = hVPB
        VPBContext  es,di                       ; ES:DI -> VPB
        mov     ax, word ptr wdi_Category       ; ah = function;al = category
        cmp     al,IOC_DC                       ; check for category 8
        jne     short wdi35                     ; no, go on

        SaveReg <ax,bx>
        mov     bx,di
        CALLFAR UseCache                        ; Can we use the cache?
        RestoreReg <bx,ax>
        jc      short wdi35                     ; No, skip initializing cache
CpuMode 386
        SaveReg <ds,es>
        pusha
        movzx   edi,di
        FlatContext <ds,es>
        CCALL32  _BufFlushAll,<edi>             ; Flush all FAT cache buffers
        popa
        RestoreReg <es,ds>
        ASSUME  DS:NOTHING,ES:NOTHING
        SaveReg <ds,es>
        pusha
        movzx   eax,di
        FlatContext <ds,es>
        CCALL32  _BufDiscard,<eax>              ; Discard all clean Cache buffers
CpuMode reset
        popa
        RestoreReg <es,ds>
    ASSUME  DS:NOTHING,ES:NOTHING

wdi35:  les     di,es:[di].vpb_fsi.vpi_pDPB     ; ES:DI -> DPB
        xor     bl,bl                           ; BL = 0 (not a device)
        .errnz  IOC_NotDevice

wdi140: push    wdi_ParmPackAddr.Segmt          ; ptr to parameter packet
        push    wdi_ParmPackAddr.Offst
        push    es                              ; pDPB, pDevDrvrHdr, or pPhysDiskTblEnt
        push    di
        mov     di,wdi_SFN                      ; DI = SFN
        cmp     di, -1                          ; null handle case?
        jne     short wdi140a                   ; no
        mov     ax, word ptr wdi_Category       ; ah = function;al = category
        xor     bl,bl                           ; BL = 0 (not a device)
wdi140a:
        mov     bh,FSH_NONFSD_IOCTL

ifdef SECURITY
;                                                                       ;@SEC
; Security callout.                                                     ;@SEC
        mov     wdi_SaveAX,ax                   ; callout will alter ax.;@SEC
                                                                        ;@SEC
        push    bx                                                      ;@SEC
        lea     bx,wdi_SFN                      ; point to parms        ;@SEC
                                                                        ;@SEC
        CALLFAR SecDevIoctl                                             ;@SEC
        pop     bx                                                      ;@SEC
        jc      short wdi160                    ; rejected. get out.    ;@SEC
        mov     ax,wdi_SaveAX                                           ;@SEC
endif
        call    sw_DevIOCtl                     ; call sub-worker

        jc      short wdi160                    ; if carry, go return error
        mov     ax,FSC_LOCAL                    ; AX -> FAT specific case

;*  Check to see if this was a Set Device Parameters call (special handling)

wdi150: cmp     word ptr wdi_Category,((IODC_SP shl 8) + IOC_DC)
                                                ; check for cat=8, fun=43h
        je      short wdi180                    ; yes, go handle special case
        clc                                     ; clear carry, no error

;*  Cleanup before return

wdi160: pushf
        cmp     wdi_SFTLockAddr.Segmt,0         ; check for locked SFT
        jz      short wdi170                    ; not locked, skip around
        les     di,wdi_SFTLockAddr              ; ES:DI -> SFT
        call    SFUnlock                        ; unlock SFT
wdi170: popf
;### PTR B720732        mov     SS:[ptda_pCDS],0                ; Clear ptda_pCDS
wdi170a:
        LeaveProc
        ret     ?aframe

;*  Category = Disk control, Function = Set Device Paramters

wdi180: mov     si,wdi_ParmPackAddr.Offst
        mov     bx,wdi_ParmPackAddr.Segmt       ; BX:SI -> parameter packet
        push    ax                              ; save FAT flag

        call    FuByte                          ; AL = command byte
        jc      short wdi190                    ; carry means error, AX = error code

        test    al,IODC_SP_MEDIA                ; check for set new BPB
        jz      short wdi190                    ; no, go cleanup & exit

;*  Set new BPB
;       We need to use the BPB passed in the Set Dev Parms call
;       to the disk driver to set up the fields in the VPB for the drive.

        pop     ax                              ; AX = FAT flag
        les     di,wdi_SFTLockAddr              ; ES:DI -> SFT
        mov     bx,wdi_DataPackAddr.Segmt
        mov     si,wdi_DataPackAddr.Offst       ; BX:SI -> Data packet
;*  Changed to CALLFAR for 73961.
        CALLFAR wdi_updateVPB                   ; go update the VPB and SFTs
        jmp     short wdi160                    ; go finish up

;*  Got error, pop ax off stack before going on

wdi190: pop     bx                              ; pop trash, leave error in AX
        jmp     short wdi160                    ; go exit


EndProc w_DevIOCtl

HIGH2CODE ENDS                               ; 73961 83608

        FARCODE HIGH3                        ; 73961 83608
HIGH3CODE SEGMENT                            ; 73961 83608


;***    SpoolIOCtl - worker for Spooled file IOCTL
;
;
;       ENTRY:  AL     = Category code
;               AH     = Function code
;               BX     = handle
;               ES:DI -> pointer to SFT
;               CX     = length of parameter packet (0 if unknown)
;               DS:DX -> data packet
;               SI     = length of data packet (0 if unknown)
;               PUSH    pointer to paramter packet
;               PUSH    @pParmLenInOut
;               PUSH    @pDataLenInOut
;               call    SpoolIOCtl
;
;       EXIT:   Carry set -- error
;                   AX = error code
;               Carry clear -- no error
;
;       USES: AX, BX, CX, DX, DI, SI, DS, ES, flags
;
;   Algorithm:
;
;       if (Category == 5) {
;         switch(Function) {
;
;           case PRT_QUERYJOBHANDLE:
;               return Job Handle (SFN);
;
;           case PRT_QUERYACTIVEFONT:
;               return codepage from SFT;
;
;           case QUERY_SPOOLER_STATUS:
;               return 1 (active);
;
;           case PRT_SETJOBTITLE:
;               TransactNmPipe(Job handle, title, &err);
;               if (err)
;                   return err;
;
;           case PRT_SETDATATYPE:
;               TransactNmPipe(Job handle, type_string, &err);
;               if (err)
;                   return err;
;
;           case PRT_ACTIVATEFONT:
;               TransactNmPipe(Job handle,codepage, &err);
;               if (err)
;                   return err;
;               else
;                   remember codepage in SFT;
;
;           case PRT_VERIFYFONT:
;               TransactNmPipe(Job handle,codepage, &err);
;               if (err)
;                   return err;
;
;           case PRT_GETPRINTERSTATUS:  (0x0066)      @PN1
;               set Data to 0x90(Not busy, Selected)
;               return 0 (success);
;
;           case (IOCTL in not_supported_set):
;               return ERROR_INVALID_FUNCTION;
;
;           default:
;               return error; /* invalid function */
;         }
;       } else
;       if (Category == 85) { /* special user defined IOCtl for CP20 J200457 */
;         switch(Function) {
;
;           case PRT_SETPRINTERINFO(47h):
;                  TransactNmPipe(Job handle, DataPacket, &err);
;                  For now DataPacket supported will be only 6 bytes
;                  if (Parmeter packet sub-function code == 00)
;                     Spooler will have set printer using DataPacket info
;                  if (err)
;                      return err;
;
;           case PRT_GETPRINTERINFO(67h):
;                  TransactNmPipe(Job handle, DataPacket, &err);
;                  For now DataPacket supported will be only 6 bytes
;                  if (Parmeter packet sub-function code == 00 or 01)
;                      Spooler will return status via pipe and we will store
;                        the return value in the Datapacket
;                  if (err)
;                      return err;
;
;         return error; /* invalid category */
;
Procedure SpoolIOCtl, faronly                      ;83608
        ASSUME  DS:NOTHING,ES:NOTHING,SS:TASKAREA

;
; Old Bug - spl_pParmLenInOut and spl_pParm were incorrectly reversed @J200457
;
ArgVar  spl_pParmLenInOut,DWORD
ArgVar  spl_pDataLenInOut,DWORD
ArgVar  spl_pParm, DWORD

LocalVar spl_SFT, DWORD                         ; SFT of file
LocalVar spl_SFN,WORD                           ; SFN of named pipe
LocalVar spl_Codepage, WORD
LocalVar spl_NmPipeStruc, <size DosTransactNmPipeStruc>
LocalVar spl_TempBuff, DWORD                    ; Temp. buffer
LocalVar spl_pData, DWORD                       ; Store pDataIn/Out @J200457

        EnterProc <es, di>

; Check for category 5 (printer). Then jump to the code in charge of
; the function requested.

        cmp     al, IOC_PC                      ; Printer Control Category 5?
        je      short spl010                    ; Check Cat 5 functions

                                                ; @85 - Printer Control J200457
                                                ;       Catagory 95 for Japan
                                                ;       Defined in /h/splmsgp.h
        cmp     al, IOC_PC_JA                   ; Printer Control Category 85?
        jnz     spl900                          ; No, error out

        cmp     ah, IOJW_SI                     ; PRT_SETPRINTERINFO ?
        jz      spl400

        cmp     ah, IOJR_GI                     ; PRT_GETPRINTERINFO ?
        jz      spl400

        jmp     spl800                          ; Check for not supported.

                                                ; @85 end

spl010:
        cmp     ah, IOLR_QJ                     ; PRT_QUERYJOBHANDLE ?
        jz      short spl100

        cmp     ah, IOLR_QF                     ; PRT_QUERYACTIVEFONT ?
        jz      short spl200

        cmp     ah, IOLR_MS                     ; Get spooler status ?
        jz      short spl300

        cmp     ah, IOLW_ST                     ; PRT_SETJOBTITLE ?
        jz      short spl400

        cmp     ah, IOLW_AF                     ; PRT_ACTIVATEFONT ?
        jz      short spl400

        cmp     ah, IOLR_VF                     ; PRT_VERIFYFONT ?
        jz      short spl400

        cmp     ah, IOLW_SD                     ; PRT_SETDATATYPE ?
        jz      short spl400

        cmp     ah, IOLR_GS                     ; PRT_GETPRINTERSTATUS ? @PN1
        jz      short spl350

        cmp     ah, IOLR_IR                     ; Query Infinite Retry ;53772
        jz      spl375                                                 ;53772

        jmp     spl800                          ; Check for not supported.

; *****************************************************************
; *
; *     PRT_QUERYJOBHANDLE, return Job Handle
; *
; *****************************************************************

spl100: mov     ax, es:[di].sf_fsi.sfi_selfsfn  ; Get SFN (job handle)
        jmp short spl310                        ; return it in data buffer


; *****************************************************************
; *
; *     PRT_QUERYACTIVEFONT, return codepage
; *
; *****************************************************************

spl200: mov     ax, es:[di].sf_codepage         ; get current codepage
        jmp short spl310                        ; return it in data buffer


; *****************************************************************
; *
; *     Get Spooler status
; *
; *****************************************************************

spl300: mov     ax, 1                           ; Status == Active
spl310: mov     bx, ds
        mov     di, dx                          ; BX:DI -> Data Buffer
        CALLFAR SuWordNF                        ; Store in Data Buffer

        jc      spl950                          ; return if error

        mov     bx, spl_pDataLenInOut.Segmt
        mov     di, spl_pDataLenInOut.Offst     ; BX:DI -> Data length
        mov     ax, 2
        CALLFAR SuWordNF                        ; Return data length
        jmp     spl950                          ; We're done

; *****************************************************************
; *
; *     PRT_GETPRINTERSTATUS, return printer status   @PN1
; *
; *         Added for CP20 B730217 to return status NotBusy/Selected
; *         when going to a spooled device.
; *
; *
; *****************************************************************

spl350: mov     ax, 90H                         ; get NotBusy/Selected bits
        mov     bx, ds
        mov     di, dx                          ; BX:DI -> Data Buffer
        CALLFAR SuByteNF                        ; Store in Data Buffer

        jc      spl950                          ; return if error

        mov     bx, spl_pDataLenInOut.Segmt
        mov     di, spl_pDataLenInOut.Offst     ; BX:DI -> Data length
        mov     ax, 1
        CALLFAR SuWordNF                        ; Return data length
        jmp     spl950                          ; We're done

; *****************************************************************  ;53772
; *                                                                  ;53772
; *     Query Infinite Retry                                         ;53772
; *                                                                  ;53772
; *         Added for CP20 53772   to return no error on print IOCTL ;53772
; *         64H                                                      ;53772
; *                                                                  ;53772
; *                                                                  ;53772
; *****************************************************************  ;53772
                                                                     ;53772
spl375: mov     ax, NO_ERROR                    ; return no error    ;53772
        clc                                     ; clear carry        ;53772
        jmp     spl950                          ; We're done         ;53772
; *****************************************************************
; *
; *     PRT_SETJOBTITLE,    Set the job's title
; *     PRT_ACTIVATEFONT,   Activate a font
; *     PRT_VERIFYFONT,     Verify a font
; *     PRT_SETDATATYPE,    Set data type
; *     PRT_GETPRINTERINFO  Get printer information      @85
; *     PRT_SETPRINTERINFO  Set printer information      @85
; *
; *         All these functions require communication with the spooler
; *     via the named pipe. We will first get a buffer and initialize
; *     some of the fields of the TransactNmPipe structure, then we
; *     jump to the code in charge of each function.
; *
; *         Note that we use the same buffer for input and output. The
; *     input message starts where the output message ends.
; *
; *
; *     NOTE2: Messages from Kernel to Spooler are MSGOUT = outbound
; *            Messages from Spooler to Kernel are MSGIN  = inbound
; *            spl_TempBuff -> temp storage for communicating with spooler
; *                             via named pipe.
; *                            Data stored in here:
; *                              Outbound buffer - msg to send to spooler
; *                            After reply from spooler
; *                              Inbound message - stored immediately following
; *                                                where Outbound msg ended
; *
; *            Inbound and Outbound should not be confused with InBuf/OutBuf
; *            InBuffer  is the buffer to write(to spooler = Outbound)
; *            OutBuffer is the buffer to read(from spooler = Inbound)
; *
; *            The following is from dosinc/argframe.inc for clarity:
; *
; *        DosTransactNmPipeStruc struc
; *           tnmp_BytesOut        dd      ?       ; bytes read (returned)
; *           tnmp_OutBufferLen    dw      ?       ; length of read buffer
; *           tnmp_OutBuffer       dd      ?       ; pointer to read buffer
; *           tnmp_InBufferLen     dw      ?       ; length of write buffer
; *           tnmp_InBuffer        dd      ?       ; pointer to write buffer
; *           tnmp_Handle          dw      ?       ; pipe file handle
; *        DosTransactNmPipeStruc ends
; *
; *
; *
; *     NOTE3: The following code that handles new IOCtl Category 85h
; *              Get/SetPrinterInfo(Functions 47h and 87h)
; *              make the assumption that the pointer to the Data Packet
; *              and the pointer to Parameter List have already been converted
; *              to protect mode 16-bit address.
; *
; *
; *****************************************************************
;

spl400: SaveReg <ax>                            ; Remeber IOCtl
        mov     ax, es:[di].sf_NmPipeSfn        ; AX == SFN of named pipe
        mov     spl_SFN, ax                     ; save the SFN
        mov     ax, es:[di].sf_fsi.sfi_selfsfn  ; AX == "Job Handle"
        push    ax                              ; Remember the job handle
        call    GetTempBuffer                   ; ES:DI -> Temp Buffer
        mov     spl_TempBuff.Segmt, es
        mov     spl_TempBuff.Offst, di
        mov     [spl_NmPipeStruc].tnmp_InBuffer.Segmt,  es
        mov     [spl_NmPipeStruc].tnmp_InBuffer.Offst,  di
        mov     [spl_NmPipeStruc].tnmp_OutBuffer.Segmt, es
        mov     [spl_NmPipeStruc].tnmp_OutBufferLen, size MSG_HDR_IN
        pop     ax                              ; AX == "Job Handle"
        mov     es:[di].MSGOUT_Header.MHO_uHandle, ax
        RestoreReg <ax>                         ; AX == IOCtl
        SaveReg <ax>                            ; Remember IOCtl, will be
                                                ; used after the transaction
                                                ; with the spooler.

; Now jump to the code in charge of each function

        cmp     ah, IOLW_AF                     ; PRT_ACTIVATEFONT ?
        jz      spl600

        cmp     ah, IOLR_VF                     ; PRT_VERIFYFONT ?
        jz      spl600

        cmp     ah, IOJW_SI                     ; PRT_SETPRINTERINFO ? @85
        jz      spl630

        cmp     ah, IOJR_GI                     ; PRT_GETPRINTERINFO ? @85
        jz      spl630

        ;cmp     ah, IOLW_ST                     ; PRT_SETJOBTITLE ?
        ;jz      spl500

        ;cmp     ah, IOLW_SD                     ; PRT_SETDATATYPE ?
        ;jz      spl500

; *****************************************************************
; *
; *     PRT_SETJOBTITLE
; *     PRT_SETDATATYPE
; *
; *     These two functions have the same message format and thus
; *     execute the same code. Only the message type is set
; *     differently.
; *
; *****************************************************************

spl500: cmp     ah, IOLW_ST                     ; PRT_SETJOBTITLE?
        jnz     short spl505
        mov     byte ptr es:[di].MSGOUT_Header.MHO_Type, HDR_SETJOBTITLE
        jmp short spl506

spl505: mov     byte ptr es:[di].MSGOUT_Header.MHO_Type, HDR_SETDATATYPE
spl506: mov     bx,  ds
        mov     si,dx                           ; BX:SI ->data buffer
        SaveReg <bx, es>
        CALLFAR FuWord                          ; AX: size of title.
        RestoreReg <es, bx>
        jc      spl660                          ; error, get out

        mov     cx, ax                          ; CX == title length
        cmp     cx, (PMPATHLEN  - size MSG_ST_OUT - size MSG_HDR_OUT)
        jng     short spl510
        stc                                     ; Title too long, error out
        jmp     spl660

spl510: add     ax, (size MSG_ST_OUT + size MSG_HDR_OUT - 1) ; AX == message length
        mov     [spl_NmPipeStruc].tnmp_InBufferLen, ax
        mov     es:[di].MSGOUT_Msg.MSTO_usLen, cx
        add     di, (size MSG_ST_OUT + size MSG_HDR_OUT - 1) ; ES:DI -> title field

;   Get pointer to source (title)
        SaveReg <bx, es>
        CALLFAR FuWord
        RestoreReg <es, bx>
        jc      spl660
        push    ax
        SaveReg <es>
        CALLFAR FuWord
        RestoreReg <es>
        mov     bx, ax
        pop     si                              ; BX:SI -> title
        jc      spl660

        cmp     ProcType,PT_REALMODE            ; Real Mode ?
        je      short spl519                    ; yes, go convert pointer

;   The following hack determines if we were called from 32-bit API

        mov     ax,ss
        cmp     ax,spl_pDataLenInOut.Segmt      ;
        je      short spl520                    ; 16 bit, no conversion

;   We were called by a 32-bit app. Convert pointer
;
;   BX:SI -> flat pointer
;   ES:DI -> Target buffer (in kernel)
;   CX:      string length

        CpuMode 386

        movzx   eax,ss:CurrTSD
        mov     edx,DWORD PTR SS:[eax].TSDUserSS

;   Get the pointer to the buffer and convert it to a sel:offset

        shl     bx,3
        arpl    bx,dx
        or      bl,TABLE_MASK                   ;Force the LDT bit on.

        CpuMode Reset

;   We may need a partial transfer (in case of segment crossing)

        mov     ax, 0ffffh
        sub     ax, si
        cmp     ax, cx                          ; all string in one segment?
        ja      short spl520                    ; yes, go do it

;   Segment crossing, transfer one chunk

        push    es
        mov     cx, ax
        CALLFAR FuBuff                          ; transfer one chunk
        pop     es
        or      bx, 8H                          ; Get next selector
        xor     si,si
        jmp short spl520

;   We are in real mode. The address in BX:SI is a real mode address. Have
;   to convert it.
;   Note AX == BX and BX:SI -> title (real mode)

spl519: SaveReg <ds, es>
        FlatContext <ds,es>
        CALLF32 demMapSeg
        assume  ds:nothing,es:nothing
        RestoreReg <es,ds>
        mov     bx, ax                          ; BX:SI -> title (protect mode)

spl520: push    es
        CALLFAR FuBuffz                         ; transfer
        pop     es
        jc      spl660

        jcxz    short spl530                    ; correct length, continue
        stc                                     ; incorrecto length, get out
        jmp     spl660

spl530: mov     [spl_NmPipeStruc].tnmp_OutBuffer.Offst, di
        jmp     spl650                          ; do TransactNmPipe


; *****************************************************************
; *
; *     PRT_ACTIVATEFONT
; *     PRT_VERIFYFONT
; *
; *****************************************************************

;   Get codepage from data buffer

spl600: cmp     ah, IOLW_AF                     ; PRT_ACTIVATEFONT ?
        jnz     short spl610
        mov     byte ptr es:[di].MSGOUT_Header.MHO_Type, HDR_NEWCODEPAGE
        jmp short spl620
spl610: mov     byte ptr es:[di].MSGOUT_Header.MHO_Type, HDR_VERCODEPAGE
spl620: push    ds
        pop     bx
        mov     si,dx                           ; BX:SI -> data buffer
        CALLFAR FuWord                          ; AX: new codepage
        jc      spl660                          ; error, get out
        mov     spl_Codepage, ax
        mov     es:[di].MSGOUT_Msg.MCPO_Codepage, ax
        mov     cx, (size MSG_CP_OUT + size MSG_HDR_OUT)

;   Now fill the fields of the NmPipeStruc for InBuffer and OutBuffer

        mov     [spl_NmPipeStruc].tnmp_InBufferLen, cx

        add     di, cx
        mov     [spl_NmPipeStruc].tnmp_OutBufferLen, (size MSG_HDR_IN)
        mov     [spl_NmPipeStruc].tnmp_OutBuffer.Offst, di

        jmp     spl650                        ; do TransactNmPipe


; *****************************************************************
; *
; *     PRT_GETPRINTERINFO   added to support Category 85 @J200457
; *     PRT_SETPRINTERINFO
; *
; *****************************************************************

;   Get subfunction from parameter buffer
;   Right now AX=IOCtl category,function
;             ES:DI -> buffer to pass to spooler
;             DS:DX -> DataPacket
;   Must set  [spl_NmPipeStruc].tnmp_OutBuffer.Offst to point to place to
;                      store data read from Pipe(Inbound msg).
;                      This will be TempBuffer offset plus size of
;                      buffer written to pipe(Outbound msg)
;

spl630:
        SaveReg <ax,bx,es,di,si>                ; Save regs - must retore
        mov     spl_pData.Segmt, ds             ; Save pointer to Data packet
        mov     spl_pData.Offst, dx
        mov     bx,spl_pParm.Segmt              ; Get pointer to Paramters
        mov     si,spl_pParm.Offst              ;

                                                ; BX:SI -> SubFunction to get
        CALLFAR FuByteNF
        jc      spl654                          ; error - get out, popping regs
        mov     dx,ax                           ; save subfunction in dx
        mov     dh,0                            ; zero out high byte
        RestoreReg <si,di,es,bx,ax>
                                                ; pass SubFunction to spooler
                                                ;   in same place for Get/Set
        mov     es:[di].MSGOUT_Msg.MSETO_usSubFunc, dx
        cmp     ah, IOJR_GI                     ; PRT_GETPRINTERINFO ?
        je      short spl649                    ; No other data to pass to splr

;
;   Setting printer information
;   Must retrieve Data Packet from user's buffer and put into buffer
;   to pass to spooler
;
        mov     byte ptr es:[di].MSGOUT_Header.MHO_Type, HDR_SETPRINTERINFO

        SaveReg <ax,bx,es,di,si>                ; Save regs
        mov     bx,spl_pData.Segmt              ; Get pointer to Data packet
        mov     si,spl_pData.Offst              ;

;
;   Ready to get data from BX:SI and place into buffer to send to spooler
;
                                               ; BX:SI -> Data Packet
                                               ; ES:DI -> begin of Pipe buffer
        mov     ax, di
        add     ax, (size MSG_HDR_OUT + size MSETO_usSubFunc)
                                          ; AX == offset to put Data Packet
        mov     di, ax                    ; ES:DI -> next place to put data
        SaveReg <bx>                           ; FuWordNF trashes BX
        CALLFAR FuWordNF
        RestoreReg <bx>
        jc      spl654                         ; error - get out

        mov     word ptr es:[di], ax
        mov     ax,2
        add     ax,di
        mov     di,ax                          ; ES:DI -> next place for data

        SaveReg <bx>
        CALLFAR FuWordNF
        RestoreReg <bx>
        jc      spl654                         ; error - get out

        mov     word ptr es:[di], ax
        mov     ax,2
        add     ax,di
        mov     di,ax                          ; ES:DI -> next place for data

        SaveReg <bx>
        CALLFAR FuWordNF
        RestoreReg <bx>
        jc      spl654                         ; error - get out

        mov     word ptr es:[di], ax

        RestoreReg <si,di,es,bx,ax>

;   Now fill the fields of the NmPipeStruc for InBuffer and OutBuffer
;
;   Remember OutBuffer is read buffer(from pipe = inbound message)
;   and it starts AFTER the InBuffer storage for write buffer(outbound msg)
;

        mov     ax, spl_TempBuff.Offst        ; this is begin of InBuffer
        add     ax, (size MSG_SET_OUT + size MSG_HDR_OUT) ; size outbound msg

        mov     [spl_NmPipeStruc].tnmp_OutBuffer.Offst, ax

        mov     [spl_NmPipeStruc].tnmp_InBufferLen, (size MSG_SET_OUT + size MSG_HDR_OUT)

        mov     [spl_NmPipeStruc].tnmp_OutBufferLen, (size MSG_HDR_IN)

        jmp     short spl650                       ; do TransactNmPipe


;
;   GetPrinterInfo setup
;

spl649:
        mov     byte ptr es:[di].MSGOUT_Header.MHO_Type, HDR_GETPRINTERINFO

        mov     ax, spl_TempBuff.Offst        ; this is begin of InBuffer
        add     ax, (size MSG_GET_OUT + size MSG_HDR_OUT) ; size outbound msg

        mov     [spl_NmPipeStruc].tnmp_OutBuffer.Offst, ax

        mov     [spl_NmPipeStruc].tnmp_InBufferLen, (size MSG_GET_OUT + size MSG_HDR_OUT)

        mov     [spl_NmPipeStruc].tnmp_OutBufferLen, (size MSG_GET_IN + size MSG_HDR_IN)



; *****************************************************************
; *
; *     Perform transaction to spooler
; *
; *****************************************************************
;   Perform the transaction

spl650: mov     dx, [spl_SFN]                   ; DX = handle (pipe)
        lea     si, [spl_NmPipeStruc]           ; SS:SI -> struct
        CALLFAR TransactSpooler                 ; 83608
        jc      spl660                          ; error, get out

;   Get error code
        mov     es,[spl_NmPipeStruc].tnmp_OutBuffer.Segmt
        mov     di,[spl_NmPipeStruc].tnmp_OutBuffer.Offst

        mov     ax, es:[di].MSGIN_Header.MHI_Error       ; Get error code
        cmp     ax, 0                                   ; Error returned
        jnz     spl655                                  ; yes, return it

        RestoreReg <ax>                         ; AH = Function

        cmp     ah, IOLW_AF                     ; Set codepage?
        jz      spl656                          ; yes, set SFT codepage

        cmp     ah, IOJR_GI                     ; PRT_GETPRINTERINFO ?
        jz      short spl651                    ; yes, return info to user

        clc                                     ; reset carry - no error
        jmp     short spl660                    ; done

;
;   Handle GetPrinterInformation by passing returned info to user Data Packet
;
;   Right now ES:DI -> OutBuffer that has the returned information
;         but SuWord requires BX:DI to point to user buffer to store AX into
;         so for now move DI into SI and use ES:SI to get returned data
;
spl651: SaveReg <ax,bx,es,di,si>                ; Save regs
        mov     ax, di
        add     ax, (size MSG_HDR_IN)           ; get past inbound error code
        mov     si, ax                          ; ES:SI -> returned info

        mov     bx,spl_pData.Segmt              ; Get pointer to Data packet
        mov     di,spl_pData.Offst              ;   to store returned data

;
;   Ready to get data from spooler's pipe buffer and return it in Data Packet
;   For now we only return 6 bytes of data.
;   Later for Get/SetPrinterInfo we should look at first field which
;     contains packet length(currently is 6)
;
                                          ; BX:DI -> Data Packet to get data
                                          ; ES:SI -> info to store from pipe

        mov     ax, word ptr es:[si]      ; AX == retrieved word(packet len)
        SaveReg <es>
        CALLFAR SuWordNF                  ; store AX at BX:DI
        RestoreReg <es>
        jc      short spl654              ; error - get out

        mov     ax,2
        add     ax,si
        mov     si,ax                     ; ES:SI -> next place for data

        mov     ax, word ptr es:[si]      ; AX == retrieved word
        SaveReg <es>
        CALLFAR SuWordNF                  ; store AX at BX:DI
        RestoreReg <es>
        jc      short spl654              ; error - get out

        mov     ax,2
        add     ax,si
        mov     si,ax                     ; ES:SI -> next place for data

        mov     ax, word ptr es:[si]      ; AX == retrieved word
        SaveReg <es>
        CALLFAR SuWordNF                  ; store AX at BX:DI
        RestoreReg <es>
        jc      short spl654              ; error - get out

;  Set returned data length to number of bytes copied

        mov     bx, spl_pDataLenInOut.Segmt
        mov     di, spl_pDataLenInOut.Offst     ; BX:DI -> Data length
        mov     ax, 6
        CALLFAR SuWordNF                        ; Return data length

        RestoreReg <si,di,es,bx,ax>
        jmp     short spl660
;
;   Error exit for Get/SetPrinterInfo
;   Must restore all Saved registers, don't use RestoreReg because MASM
;   gives SaveReg/RestoreReg mismatch
;
spl654:
        pop     ax                              ; restore regs
        pop     bx
        pop     es
        pop     di
        pop     si
        mov     ax, ERROR_INVALID_PARAMETER     ; set error exit and fall thru

;
;   AX has error, set carry
;
spl655:
        stc
        jmp short spl660


;   For PRT_ACTIVATEFONT, store the new codepage in the SFT

spl656:
        les     di, [spl_SFT]
        mov     ax, [spl_Codepage]
        mov     es:[di].sf_codepage, ax         ; set new codepage

;   Free temporary buffer

spl660: les     di, spl_TempBuff
        pushf
        call    FreeTempBuffer
        popf
        jmp short spl950

spl670: pop     bx
        jmp short spl950

spl680: pop     bx
        jmp short spl660

spl700: pop     bx
        jmp short spl660

spl800: mov     ax, ERROR_INVALID_FUNCTION
        jmp short spl940

spl900: mov     ax, ERROR_INVALID_CATEGORY

spl940: stc                                     ; return with CY
spl950: LeaveProc
        ret

EndProc SpoolIOCtl





;***    wdi_BeginFormat
;       ---------------
;
;   Unmounts any current FSD for the specified (drive) handle.  Forces a
;   mount to the specified FSD.
;
;   ENTRY   DS:DX -> data packet (FSD_Name) (Null string -> FAT)
;           ES:DI -> SFT
;
;   EXIT    'NC' - no error
;           'C'  - error
;               AX = return code
;
;   USES    AX,BX,CX,DX,DI,SI,DS,ES,flags
;
;   PSEUDOCODE
;       {
;           get buffer and copy name into it
;           if (FSD name == "\0")
;               fsc.Offst = FSC_RESERVED
;           else {                                  /* not FAT */
;               fscFind (FSD_name)
;               if ('C') {
;                   free temp buffer
;                   return FSD_not_found
;               }
;           }
;           free temp buffer
;
;           ForceMount()
;       }

Procedure wdi_BeginFormat,HYBRID             ; 73961

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

LocalVar    wdibf_pSFT,DWORD                ; pointer to SFT
LocalVar    wdibf_pFSC,DWORD                ; pointer to specified FSC

        EnterProc   <es,di>                 ; save SFT pointer

;*  Get temp buffer and copy FSD_name into it

        CALLFAR GetTempBuffer               ; ES:DI -> temp buffer

        SaveReg <es,di>                     ; save pointer to temp buffer

        mov     bx,ds
        mov     si,dx                       ; BX:SI -> FSD name (data)
        mov     cx,MAXPATHLEN               ; max name length
;*  Changed to CALLFAR for 73961
        CALLFAR FuBuffz                     ; copy FSD name into buffer
        LJC     wbf85                       ; 'C' means error, return buffer

        RestoreReg  <si,ds>                 ; DS:SI -> FSD name

;*  If first character = 0, then FAT was specified

        SaveReg <ds,si>                     ; remember temp buffer pointer
        cmp     BYTE PTR ds:[si],0          ; check first character
        jnz     short wbf10                 ; not FAT, lookup FSC

;*  FAT was specified, set up FSC

        mov     wdibf_pFSC.Segmt,0          ; save FAT FSC
        mov     wdibf_pFSC.Offst,FSC_RESERVED
        jmp     short wbf20                 ; go return buffer

;*  If not FAT, then look up FSD name (return FSC)

;*  Changed to CALLFAR for 73961
wbf10:  CALLFAR fscFind                     ; search for FSD name
        LJC     wbf90

        mov     wdibf_pFSC.Segmt,es         ; save FSC pointer
        mov     wdibf_pFSC.Offst,di

wbf20:  RestoreReg  <di,es>                 ; ES:DI -> temp buffer
        CALLFAR FreeTempBuffer              ; return buffer

        lds     si,wdibf_pSFT               ; DS:SI -> SFT
        les     di,wdibf_pFSC               ; ES:DI -> FSC
;*  Changed to CALLFAR for 73961
        CALLFAR ForceMount                  ; Make the FSD(s) unmount and mount the volume

wbf80:  LeaveProc
        ret


wbf85:  pop     di
        pop     es                          ; ES:DI -> FSD name
        CALLFAR FreeTempBuffer              ; return buffer
        stc
        jmp     short wbf80                 ; go return error

wbf90:  mov     ax,ERROR_INVALID_FSD_NAME   ; return FSD_Not_Found
        jmp     short wbf85                 ; return buffer, then return error

EndProc wdi_BeginFormat


;***    wdi_updateVPB
;
;       We need to use the BPB passed in the Set Dev Parms call
;       to the disk driver to set up the fields in the VPB for the drive.
;       (Also SFT field <size> is updated if DASD open.)
;
;       ENTRY:  ES:DI -> SFT
;               BX:SI -> data packet
;               AX = FAT flag (1 = true; 0 = false)
;
;       EXIT:   Carry clear, no error
;               Carry set, error
;                       (AX) = error code
;
;       USES:   AX, BX, CX, DX, DI, SI, DS, ES, flags (all)
;
;       ALGORITHM
;       {
;           Copy BPB into local buffer
;           If (not Carry)
;               SetVPB (pTempBPB, pVPB, FAT flag)
;               If (DASD Open)
;                   Update_SFT_size
;       }
;

Procedure wdi_updateVPB,HYBRID              ; 73961

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

LocalVar    w_uvpb_pSFT,DWORD               ; holds SFT pointer
LocalVar    w_uvpb_TempBPB,<size a_BPB>     ; holds temp BPB

        EnterProc <es,di>                   ; save SFT pointer
                                            ; BX:SI -> data packet
        .errnz  DDI_DP_BPB
        TaskContext es
        lea     di,w_uvpb_TempBPB           ; ES:DI -> temporary BPB buffer
        mov     cx,size a_BPB               ; CX = size of BPB
        push    ax                          ; save FAT flag

;*  Changed to CALLFAR for 73961
        CALLFAR FuBuff                      ; copy BPB into temporary buffer
        pop     ax                          ; AX = FAT flag
        jc      short wuvpb2                ; if carry set, (AX) = error code

        les     di,w_uvpb_pSFT              ; ES:DI -> SFT
        mov     di,es:[di].sf_fsi.sfi_hVPB  ; DI     = VPB offset
        VPBContext es,di                    ; ES:DI -> VPB

        TaskContext ds
        lea     si,w_uvpb_TempBPB           ; DS:SI -> TempBPB

        CALLFAR SetVPB                      ; Set up VPB using this BPB
        jc      short wuvpb2                ; SetVPB failed

        lds     si,w_uvpb_pSFT              ; DS:SI -> SFT

        cmp     ds:[si].sf_fsi.sfi_mode,open_direct ; direct_access handle?
        jz      short wuvpb1                        ; no, don't update size

        call    Update_SFT_size             ; Update sf_fsi.sfi_size field
wuvpb1: clc

wuvpb2: LeaveProc
        ret

EndProc wdi_updateVPB

HIGH3CODE ENDS                              ; 73961

        FARCODE HIGH2                       ; 73961
HIGH2CODE SEGMENT                           ; 73961

;***    sw_DevIOCtl - sub-worker for DOSDEVIOCTL, w_DevIOCtl & FSH_DevIOCtl
;
;       This routine does the real work for both w_DevIOCtl & FSH_DevIOCtl.
;       It handles all IOCtl requests.  Any special routing or convolution
;       is done here.  Usually the request is sent to the device driver.
;
;       FAT specific cases are handled here.  FSD's handle their own special
;       cases and should not get sent here.  Routine IOCtls are handled here
;       for FSD's though.
;
;       Entry:  AL     = Category code
;               AH     = Function code
;               CX     = length of parameter packet
;               DS:DX -> data packet
;               SI     = length of data packet
;               DI     = SFN (from original request) (optional)
;               BL     = Device? (1=true, 0=false) (valid iff cat != phys disk)
;
;               BH     = call raised by FSD ? ( 0 = no, 1 = yes)
;                        (if this is set, the pointers are protect mode and so
;                        irrespective of the current ProcType we should call the
;                        device driver in protect mode)
; peterwu/nagara : instead of saying this call is raised by fsd, we can also
;                say that the pointers passed are of type real, and hence
;                drivers should be called in real mode
;
;               PUSH  pointer to parameter packet
;               PUSH  Device Handle (DWORD) (one of the following):
;                   Physical Disk category  :  pPhysDiskInfoTblEntry
;                   Device, etc. (BL = 1)   :  pDevDriverHeader
;                   Other (file) (BL = 0)   :  pDPB
;
;               CALL (Near only, because of stack parameters)
;
;       Exit:   Parameters are cleaned off the stack (4)
;
;               Carry set  :  AX = error code
;                 or
;               Carry clear:  no error
;
;       USES:   AX, BX, CX, DX, DI, SI, DS, ES, flags (ALL!)
;
;       ALGORITHM
;       {
;           AllocReqPacketWait
;           Fill in request packet
;           If (User defined category) OR ('pass to driver' function)
;               IOCtl_CallDriver
;           Else
;               If (Category IN [1-7, 9-10, 12])
;                   set invalid function error & carry
;               Else If (Category = 8) {        /* device handle = pDPB */
;                   If (Function = LockDrive)
;                       LockDrive (logical lock option)
;                   Else If (Function = UnlockDrive) {
;                       UnlockDrive
;                       Clear error & carry (no error)
;                   }
;                   Else If (Function = SetLogicalMap) }
;                       set up 'Set logical map' command
;                       Jump IDC2
;                   }
;                   Else If (Function = BlockRemovable) {
;                       Modify request packet (command & length)
;                       IOCtl_CallDriver
;                       If (no Carry)
;                           return 'busy bit' from packet status, in data packet
;                   }
;                   Else If (Function = GetLogicalMap) {
;                       set up 'Get logical map' command
;   IDC2                Modify request packet (command & length)
;                       fetch drive number from data packet
;                       GetDriveInfo        /* modify to return pDPB */
;                       If (not Carry) {
;                           set unit code in request packet
;                           IOCtl_CallDriver
;                           Set drive number in data packet
;                       }
;                   }
;                   Else If (Function = InstallExtDrive) {
;                       IOCtl_CallDriver
;                       If (no Carry)
;                           call InstallBPB (to w_SICG (7)) to set up new DPB
;                   }
;                   Else
;                       Set invalid function error & carry
;               }
;               Else If (Category = 11) {
;                   If (not a character device)
;                       set invalid function error
;                   Else
;                       If (Function = FlushInput) {
;                           set up 'FlushInput' command
;                           Jump IBC2
;                       }
;                       Else If (Function = FlushOutput) {
;                           set up 'FlushOutput' command
;   IBC2:                   Modify request packet (command & length)
;                           IOCtl_CallDriver
;                       }
;                       Else
;                           set invalid function error & carry
;               }
;               Else
;                   set invalid category error & carry
;           FreeReqPacket
;           Return
;       }

Procedure sw_DevIOCtl,near

ArgVar      swdi_hDev,DWORD
ArgVar      swdi_pParm,DWORD

LocalVar    swdi_SFN,WORD
LocalVar    swdi_category,BYTE
LocalVar    swdi_ReqPktAddr,DWORD
LocalVar    swdi_IsDevice,BYTE
LocalVar    swdi_FSDRaised,Byte
LocalVar    swdi_PktUnitSet,Byte

        EnterProc <di>                          ; Store SFN

        mov     [swdi_PktUnitSet],0             ; PktUnit not set yet

        push    cx                              ; save size of parm packet
        mov     cx,ax                           ; CL = category, CH = function
        mov     swdi_category,cl                ; save category
        mov     swdi_IsDevice,bl                ; save IsDevice flag
        mov     swdi_FSDRaised,bh               ; save FSDRAISED flag

        CALLFAR AllocReqPacketWait              ; ES:BX -> tiled request packet
        mov     swdi_ReqPktAddr.Segmt,es        ; save request packet pointer
        mov     swdi_ReqPktAddr.Offst,bx
        push    di                              ; save SFN
        mov     di,bx                           ; ES:DI -> request packet
        cld                                     ; assure clear direction flag

        mov     al,LENGenIOCTL2+PktHeadSize     ; fill in request packet
        .ERRNZ  PktLen
        stosb
        .ERRNZ  PktUnit-PktLen-1

        cmp     swdi_SFN, -1            ; null SFN case?
        jne     short swdi0             ; no
        push    es
        push    bx
        les     bx,swdi_pParm           ; pointer to parm packet
        mov     al, byte ptr es:[bx+1]  ; get drive unit number
        pop     bx
        pop     es
        stosb
        jmp short swdi0a
swdi0:
        add     di,PktCmd-PktUnit               ; skip unit code for now
swdi0a:
        mov     al,CMDGenIOCTL
        stosb

        xor     ax,ax
        .ERRNZ  PktStatus-PktCmd-1              ; put 0 in status
        stosw

        .ERRNZ  PktDOSLink-PktStatus-2
        add     di,GIOCategory-PktDOSLink       ; skip around unused fields

        mov     al,cl                           ; category
        stosb

        mov     al,ch                           ; function
        .ERRNZ  GIOFunction-GIOCategory-1
        stosb

        mov     ax,swdi_pParm.Offst             ; pointer to parm packet
        .ERRNZ  GIOParaPack-GIOFunction-1
        stosw
        mov     ax,swdi_pParm.Segmt
        stosw

        mov     ax,dx                           ; pointer to data packet
        .ERRNZ  GIODataPack-GIOParaPack-4
        stosw
        mov     ax,ds
        stosw

        pop     ax                              ; SFN
        .ERRNZ  GIOSFN-GIODataPack-4
        stosw

        pop     ax                              ; length of parm packet
        .ERRNZ  GIOParaLen-GIOSFN-2
        stosw

        mov     ax,si                           ; length of data packet
        .ERRNZ  GIODataLen-GIOParaLen-2
        stosw                                   ; request packet is ready

        test    cl,IOCAT_USERDEF                ; user defined category?
        jnz     short swdi1                     ; yes, go do it

        test    ch,IOFUNC_DRIVER                ; func to be passed to driver?
        jz      short swdi3                     ; no, go intercept if necessary

swdi1:  call    swioc_calldriver                ; call the device driver

swdi2:  mov     bx,swdi_ReqPktAddr.Offst        ; ES:BX -> request packet
        pushf
        CALLFAR FreeReqPacket                   ; get rid of the request packet
        popf

        LeaveProc                               ; EXIT
        ret     ?aframe                         ; clean up stack

;*  We now have a OS defined category and a function which
;   is intercepted by OS.  Note that $TJMP destroys AX and BX.

;*  Jump by category

swdi3:  mov     al,cl                           ; AL = category
        call    $TJMP                           ; jump through table (by category)
        db      MAX_CAT_CODE                    ; 0-MAX_CAT_CODE are valid
        dw      DOSHIGH2CODE:swdi_invalid_category
        .errnz  0
        dw      DOSHIGH2CODE:swdi_invalid_category  ; Not used (category 0)
        .errnz  IOC_SE-1
        dw      DOSHIGH2CODE:swdi_invalid_func      ; Serial device control
        .errnz  IOC_TC-2
        dw      DOSHIGH2CODE:swdi_invalid_func      ; Reserved
        .errnz  IOC_SC-3
        dw      DOSHIGH2CODE:swdi_invalid_func      ; Screen Control
        .errnz  IOC_KC-4
        dw      DOSHIGH2CODE:swdi_invalid_func      ; Keyboard Control
        .errnz  IOC_PC-5
        dw      DOSHIGH2CODE:swdi_invalid_func      ; Printer Control
        .errnz  IOC_LP-6
        dw      DOSHIGH2CODE:swdi_invalid_func      ; Light Pen
        .errnz  IOC_MC-7
        dw      DOSHIGH2CODE:swdi_invalid_func      ; Mouse Control
        .errnz  IOC_DC-8
        dw      DOSHIGH2CODE:swdi_cg_dc             ; Disk/Diskette Control
        .errnz  IOC_PD-9
        DW      DOSHIGH2CODE:swdi_invalid_func      ; Physical Disk Control
        .errnz  IOC_MO-10
        dw      DOSHIGH2CODE:swdi_invalid_func      ; Monitor Control
        .errnz  IOC_BC-11
        dw      DOSHIGH2CODE:swdi_cg_bc             ; Device buffer Control
        .errnz  IOC_SQ-12
        dw      DOSHIGH2CODE:swdi_invalid_func      ; Single queue dev driver
        .errnz  IOC_CK-13
        dw      DOSHIGH2CODE:swdi_invalid_func      ; CLOCK$ control
        .errnz  MAX_CAT_CODE-IOC_CK

;**     Disk/Diskette Control
;
;       There are eight possible actions (6 are valid):
;               Function        Action
;                 00            Lock Drive
;                 01            Unlock Drive
;                 02            Redetermine Media   (handled by upper level)
;                 03            Set Logical Map
;                 04            Begin Format        (handled by upper level)
;                 20            Block removable
;                 21            Get Logical Map
;                 22            Install external drive - PRIVATE FUNCTION!!
;
;       Note that $TJMP destroys AX and BX.

swdi_cg_dc:
        mov     al,ch                   ; by function
        cmp     al,IODC_BR              ; check value
        jae     short swdi_cg_dc1       ; 20h or bigger, use another table

        call    $TJMP                               ; jump by function (0-4)
        db      3                                   ; 0,1,3 are valid
        dw      DOSHIGH2CODE:swdi_invalid_func
        .errnz  IODC_LK
        dw      DOSHIGH2CODE:swdi_dc_lock_drive     ; Lock drive
        .errnz  IODC_UL-1
        dw      DOSHIGH2CODE:swdi_dc_unlock_drive   ; Unlock drive
        .errnz  IODC_RM-2
        dw      DOSHIGH2CODE:swdi_invalid_func      ; Redetermine Media
                                                    ; done at upper level
        .errnz  IODC_SL-3
        dw      DOSHIGH2CODE:swdi_dc_set_log_map    ; Set logical map
        .errnz  IODC_BF-4
        dw      DOSHIGH2CODE:swdi_invalid_func      ; Begin Format
                                                    ; done at upper level

swdi_cg_dc1:
        and     al,IOFUNC_SUBFUNC                   ; get only low order 5 bits
        call    $TJMP
        db      2                                   ; 0 - 2 are valid
        dw      DOSHIGH2CODE:swdi_invalid_func
        .errnz  IODC_BR-20h
        dw      DOSHIGH2CODE:swdi_dc_block_removable; Block Removable
        .errnz  IODC_GL-21h
        dw      DOSHIGH2CODE:swdi_dc_get_log_map    ; Get logical map
        .errnz  0
        dw      DOSHIGH2CODE:swdi_dc_install_ext    ; Install external drive

;**     Lock drive disk control function
;
;       ES = selector for request packet
;
swdi_dc_lock_drive:
        lds     si,swdi_hDev            ; (DS:SI) = DPB pointer
        SaveReg <ds,si>                 ; d59054
        mov     al,[si].dpb_drive       ; d59054
        call    GetCDSFromDrv           ; d59054
        RestoreReg <si,ds>              ; d59054
        xor     al,al                   ; AL = 0 -> logical lock
        CALLFAR LockDrive
        jc      short swdi2             ; Error. Return indication.
        SaveReg <es>
        mov     bx,swdi_SFN
        call    SFFromSFN               ; ES:DI -> SFT
        jc      short swlk10
        or      es:[di].sf_flags2,SFF2_LOCKED_DRIVE
swlk10: RestoreReg <es>                 ; ES = selector for request packet
        jmp     short swdi2             ; successful return

;**     Unlock drive disk control function
;
;       ES = selector for request packet
;
swdi_dc_unlock_drive:
        lds     si,swdi_hDev            ; (DS:SI) = DPB pointer
        CALLFAR UnlockDrive
        push    es                      ; Preserve selector for request packet
        mov     bx,swdi_SFN
        call    SFFromSFN               ; ES:DI -> SFT
        jc      short swlk10            ; Error. Clean stack and return it.
        and     es:[di].sf_flags2,NOT SFF2_LOCKED_DRIVE
        xor     ax,ax                   ; clear error code and clear carry
        jmp     short swlk10            ; Restore ES and return

;**     Set logical map

swdi_dc_set_log_map:
        mov     al,CMDSetLogMap         ; set command code
        jmp     short swdi_dc2

;**   Get logical map

swdi_dc_get_log_map:
        mov     al,CMDGetLogMap         ; get command code
swdi_dc2:
        push    ds                      ; save data packet selector

        mov     bx,swdi_ReqPktAddr.Offst    ; (ES:BX) -> request packet
        mov     es:[bx].PktCmd,al           ; set command code
        mov     es:[bx].PktLen,LenGetSetLog+PktHeadSize

        mov     bx,ds
        mov     si,dx                   ; (BX:SI) -> data packet
        call    FuByte                  ; (AL) = logical drive number
        jc      short swdi_dc3          ; if carry set, (AX) = error code

;*  Get pointer to drive (AL = logical drive number)

;*  Changed to CALLFAR for 73961.
        CALLFAR GetDriveInfo            ; DS:SI -> pDPB, AL = unit
        jc      short swdi_dc3          ; if carry set, (AX) = error code

        mov     bx,swdi_ReqPktAddr.Offst    ; ES:BX -> request packet
        mov     es:[bx].PktUnit,al          ; set unit number
        mov     [swdi_PktUnitSet],1         ; PktUnit already set

;       mov     swdi_hDev.Segmt,ds
;       mov     swdi_hDev.Offst,si      ; hDev -> correct dev driver header

        call    swioc_calldriver        ; call the driver
        jc      short swdi_dc3          ; carry set, return with error

        mov     al,es:[bx].PktUnit      ; get value returned from driver

        mov     di,dx
        pop     bx                      ; (BX:DI) -> data packet

        push    es                      ; save ES
        call    SuByte                  ; save in return data packet
        pop     es                      ; restore ES
        jmp     swdi2                   ; go finish up & return

swdi_dc3:
        pop     di                      ; pop trash (bs & di)
        jmp     swdi2                   ; go handle error

;**     Block device removable
;
;       (DS:DX) = data packet address

swdi_dc_block_removable:
        push    ds                      ; save data packet selector

;       For block removable we remap the command to a Removable Media command.

        mov     bx,swdi_ReqPktAddr.Offst    ; ES:BX -> request packet
        mov     es:[bx].PktCmd,CMDRemMed
        mov     es:[bx].PktLen,LENRemMed+PktHeadSize

        call    swioc_calldriver
        jc      short swidc4            ; carry set -- return with error

        mov     ax,es:[bx].PktStatus    ; indicating removeable media
        and     ax,STBUI                ; get just the busy bit
        shr     ah,1                    ; get bit in correct place

        mov     di,dx
        pop     bx                      ; (BX:DI) = data packet address

        mov     al,ah
        push    es                      ; save ES
        call    SuByte                  ; save in return data packet
        pop     ES                      ; restore ES
        jmp     swdi2                   ; go finish up & return

swidc4: pop     di                      ; pop trash
        jmp     swdi2                   ; go handle error

;**     Install BDS and DPB for new external driver
;
;       WARNING!! This is a PRIVATE function that is used by the external
;       block driver to use the routines in the internal disk driver. It
;       is only invoked at driver initialisation time. The internal driver
;       is passed the IOCTL packet, and it returns certain information that
;       is then used to make the DOS initialize a DPB for the new device.
;       The information returned is:
;               - a unit number for the drive
;               - an "array" of BPBs
;       A FAR call is made to Install_ExtDrive to set up a new DPB.
;       NOTE that Install_ExtDrive (and w_SICG) is in the INITCODE segment.
;

swdi_dc_install_ext:
        mov     bx,swdi_ReqPktAddr.Offst    ; ES:BX -> request packet
        call    swioc_calldriver            ; let dev drv handle IOCTL first
        jc      short swidc5                ; error, cannot install driver

        lds     si,swdi_hDev                ; (DS:SI) = DPB pointer
        CALLFAR Install_ExtDrive            ; call init segment to do work

swidc5: jmp     swdi2

;**     Device buffer control
;
;       Allow selective flushing of device driver input and output queues.
;       Currently, we change the flush devioctl into a direct flush request

swdi_cg_bc:
        cmp     ch,IOBC_FI              ; is this flush input?
        jne     short swibc1            ; no, go try for flush output

        mov     al,CMDInputF            ; command for flushing input
        jmp     short swibc2            ; go call device

swibc1: cmp     ch,IOBC_FO              ; is this flush output?
        jnz     short swdi_invalid_func ; no, go signal invalid function

        mov     al,CMDOutputF           ; command for flushing input

swibc2: mov     bx,swdi_ReqPktAddr.Offst            ; ES:BX -> request packet
        mov     es:[bx].PktCmd,al                   ; set command
        mov     es:[bx].PktLen,LENFlush+PktHeadSize ; & packet length

        lds     si,swdi_hDev            ; DS:SI -> device driver header
        mov     dx,ds:[si].SDevAtt      ; DX = dev driver attributes
        test    dx,DEV_CHAR_DEV         ; Is it a character device
        jz      short swdi_invalid_func ; jump if SFT is not a character device

        call    swioc_calldriver
ibc3:   jmp     swdi2                   ; exit, carry set if error


swdi_invalid_func:                      ; api ioctl error bad function number
        mov     ax,ERROR_INVALID_FUNCTION
        stc                             ; set carry
        jmp     swdi2                   ; return

swdi_invalid_category:                  ; api ioctl error invalid category
        mov     ax,ERROR_INVALID_CATEGORY
        stc                             ; set carry
        jmp     swdi2                   ; return

;***    swioc_calldriver
;
;       This routine takes the packaged request and sends it to appropriate
;       device driver.
;
;       This routine is considered internal to sw_DevIOCtl.
;
;       It handles devices and files.  It is either given the driver pointer, or
;       a way to get it.  It sets up the unitNumber according to its rules.
;
;       ENTRY   (swdi_hDev) = Device Handle
;                           Physical Disk request: = pPhysDiskInfoTblEntry
;                           Character device     : = pDevDriverHeader
;                           Other (File, etc.)   : = pDPB
;               ES:BX -> Request Packet pointer
;               (swdi_FSDRaised) = call internally raised by FSD;
;                         Device driver shd be called in protect mode
;               (swdi_category) = category
;               (swdi_IsDevice) = Device flag (1=true, 0=false)
;               (swdi_PktUnitSet) = PktUnit already set? (1=true, 0=false)
;
;       EXIT:   Carry clear, no error
;                       (ES:BX) = request packet address (ReqPacketAddr)
;               Carry set, error
;                       (AX) = error code
;
;       USES:   DS, SI, AX, CH, flags
;
;       Algorithm
;       {
;           If (physical disk request) {
;               get unit code from pPhysDiskInfoTblEnt
;               get pDevDrvr from pPhysDiskInfoTblEnt
;           }
;           Else
;               If (Character Device)
;                   unit code = 0       /* pDevDriver already provided */
;               Else {
;                   get unit code from DPB
;                   get pDevDrvr from DPB
;               }
;           If (unit code in request packet not set yet)
;               Store unit code in request packet
;
;           DevIOCall2
;           ErrorTriage
;       }   /* End of IOCtl_CallDriver
;

Procedure swioc_calldriver,NEAR,LOCAL

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

        lds     si,swdi_hDev            ; DS:SI = hDev

        cmp     swdi_category,IOC_PD    ; is this phys disk control request?
        jnz     short swcd1_5           ; no, go check for char. device

;*  Physical disk request
                                        ; DS:SI -> PhysDiskInfoTblEnt
        mov     al,ds:[si].reldisknum   ; AL = unit code
        lds     si,ds:[si].ddheader     ; DS:SI -> device driver header
        jmp     short swcd3

;*  Not physical disk request
swcd1_5:cmp     swdi_IsDevice,0         ; Is it a device?
        je      short swcd2             ; no, then treat as file

;*  Character device
        xor     al,al                   ; AL = 0 (unit code)
                                        ; DS:SI -> device driver header
        jmp     short swcd3

;*  File / block device
;*  ds:si -> DPB
swcd2:  cmp     swdi_SFN, -1            ; null SFN case?
        jne     short swcd2a            ; no

        cmp     swdi_category,IOC_DC    ; category 8?
        jne     short swcd2a            ; no, must be a user-defined category

        push    bx
        mov     bx,swdi_pParm.Segmt     ; pointer to parm packet
        mov     si,swdi_pParm.Offst     ;
        inc     si                      ; BX:SI -> drive unit code
        call    FuByte                  ; AL = drive unit code
        inc     al                      ; AL = logical drive unit code
        pop     bx
        jc      short swcd8             ; if carry set, (AX) = error code
;*  Changed to CALLFAR for 73961.
        CALLFAR GetDriveInfo            ; DS:SI -> device driver header
        jc      short swcd8             ; if carry set, (AX) = error code
        SaveReg <ds,si>                 ;                          ;d57955
        mov     si,ss:[CurrTCB]         ; (SI) = current TCB       ;d57955
        lds     si,ss:[si].TCBThisCDS   ; (DS:SI) = CDS pointer    ;d57955
        mov     ah,ds:[si].cd_flags     ; (AH) = cds flags         ;d57955
        test    ah,CD_ISNET             ; network drive ?          ;d57955
;51637; mov     ax,ERROR_NOT_SUPPORTED  ;  assume yes              ;d57955
;51637; RestoreReg <si,ds>              ;                          ;d57955
;51637; jnz     short swcd8             ;  jmp if yes              ;d57955
;51637; jmp short swcd3                 ; check PktUnit
        RestoreReg <si,ds>              ;                               ;51637;
        jz      short swcd3             ;if not network continue PktUnit;51637;
        mov     ax,ERROR_NOT_SUPPORTED  ;if a network drive then return ;51637;
        jmp     short swcd8             ;   error 50=not supported      ;51637;
swcd2a:
        mov     al,ds:[si].dpb_unit         ; AL = unit code
        lds     si,ds:[si].dpb_driver_addr  ; DS:SI -> device driver header

;       Set PktUnit, unless it already has been set (ie ioctl8 cat21)

swcd3:  cmp     [swdi_PktUnitSet],1     ; has PktUnit already been set?
        je      short swcd3a            ; if so, skip this
        mov     es:[bx].PktUnit,al      ; store the unit code in the packet

;       (DS:SI) = driver address
;       (ES:BX) = request packet address

swcd3a: CALLFAR DevIOCall2              ; call device

swcd6:  mov     ch,swdi_category        ; CH = category
;*  Changed to CALLFAR for 73961.
        CALLFAR ErrorTriage
        ret

swcd7:  mov     ax,ERROR_INVALID_FUNCTION   ; AX = error code (same as DOS 3.3)
        stc                                 ; set carry so error is recognized

swcd8:  ret

EndProc swioc_calldriver

EndProc sw_DevIOCtl

HIGH2CODE ENDS                              ; 73961

        FARCODE HIGH3                       ; 73961
HIGH3CODE SEGMENT                           ; 73961

BREAK <ErrorTriage - examine DD packet and detect device signalled errors>

;/********************** START OF SPECIFICATIONS *********************/
;/*                                                                  */
;/* SUBROUTINE NAME: ErrorTriage                                     */
;/*                                                                  */
;/* DESCRIPTIVE NAME: Examine DD packet and detect device signalled  */
;/*                   errors                                         */
;/*                                                                  */
;/* FUNCTION: Examine the status field of a recently completed packet*/
;/*           and, if necessary, extracts and maps the device driver */
;/*           error code into the DOS standard errors. It will also  */
;/*           pass back any device driver defined or user defined    */
;/*           error codes.                                           */
;/*                                                                  */
;/* NOTES:                                                           */
;/*                                                                  */
;/* ENTRY POINT:  ErrorTriage                                        */
;/*    LINKAGE:   Call Near, (local or public)                       */
;/*                                                                  */
;/* INPUT:       ES:BX = pointer to completed DD packet              */
;/*              CH    = category of the function.                   */
;/*                                                                  */
;/* EXIT-NORMAL: Carry Clear, no regs modified                       */
;/*                                                                  */
;/* EXIT-ERROR:  Carry Set                                           */
;/*              AX = DOS error code                                 */
;/*                   User defined error code                        */
;/*                   Device Driver defined error code               */
;/*                                                                  */
;/* EFFECTS:  uses DS, AX                                            */
;/*                                                                  */
;/* INTERNAL REFERENCES:                                             */
;/*    ROUTINES:    none                                             */
;/*                                                                  */
;/*    DATA AREA:                                                    */
;/*                                                                  */
;/* EXTERNAL REFERENCES:                                             */
;/*    ROUTINES:   none                                              */
;/*                                                                  */
;/*    DATA AREA:                                                    */
;/*********************** END OF SPECIFICATIONS **********************/

Procedure ErrorTriage,HYBRID            ; 73961
        push    bx
        mov     bx,es:[bx].PktStatus    ; get packet return status
        test    bx,STERR                ; Error ? (CLEARS CARRY)
        jz      short etr4              ; no, go return OK

        mov     al,bl                   ; AL = error code
        xor     ah,ah                   ; Zero out high byte of error AH = 0

        test    ch,IOCAT_USERDEF        ; is user-defined category?
        jz      short etr1              ; no, go test for device driver error
        or      ax,ERROR_USER_DEFINED_BASE  ; indicate user-defined error
        jmp     short etr3              ; go return it

etr1:   test    bx,STDDERROR            ; is device driver defined error ?
        jz      short etr2              ; no, go map standard dos errors
        or      ax,ERROR_DEVICEDRIVER_DEFINED_BASE  ; indicate DD defined error
        jmp     short etr3              ; go return it

etr2:   DosContext ds
        xor     bh,bh                   ; Zero out High byte
        mov     al,ErrMap24[bx]         ; get dos error code

etr3:   stc                             ; indicate error

etr4:   pop     bx
        ret
EndProc   ErrorTriage


BREAK <GetDriveInfo - Get ptr to device driver from drive number>

;***    GetDriveInfo - Get ptr to device driver from drive number
;
;       ENTRY:  (AL) = drive number (0=default drive, 1=A, 2=B, ...)
;
;       EXIT:   Carry Clear,
;                   (AH) = CDS flags (cd_flags)
;                   (AL) = unit number
;                   (DS:SI) = device header
;               Carry Set,
;                   AX = error code
;
;       USES:   AX, SI, DS, Flags
;
        ASSUME  DS:NOTHING,ES:NOTHING,SS:TASKAREA

Procedure GetDriveInfo,<HYBRID,far16>           ; 73961
;*  Changed to CALLFAR for 73961.
        CALLFAR GetThisDrv
        jc      short gdiex

        mov     si,TASKAREA:CurrTCB             ; (SI) = current TCB     ;### PTR B720732
        lds     si,TASKAREA:[si].TCBThisCDS     ; (DS:SI) = CDS pointer  ;### PTR B720732
;### PTR B720732        lds     si,[ThisCDS]                    ; (DS:SI) = CDS pointer
        mov     ah,ds:[si].cd_flags             ; (AH) = cds flags
        lds     si,ds:[si].cd_devptr            ; (DS:SI) = DPB pointer

        test    ah,CD_ISNET
        jz      gdie1                           ; jump if not net
        xor     al,al                           ; unit zero on Net device
        mov     [ExtErr_Locus],ERRLOC_NET
        jmp     short gdie2

;       Return pointer to device driver

gdie1:  mov     [ExtErr_Locus],ERRLOC_DISK
        mov     al,ds:[si].dpb_unit             ; (AL) = unit number
        lds     si,ds:[si].dpb_driver_addr      ; (DS:SI) = driver header addr         ;#62179
;#62179 gdie2:  lds     si,ds:[si].dpb_driver_addr      ; (DS:SI) = driver header addr ;#62179
gdie2:  clc                                     ;Need to jump past lds instruction as  ;#62179
                                                ;dpb_driver_addr for  network drive    ;#62179
                                                ;could cause a trap.                   ;#62179
        RETP

;       Invalid drive, so return error

gdiex:  mov     ax,ERROR_INVALID_DRIVE
        stc
        RETP

EndProc  GetDriveInfo


BREAK <GetHandleInfo - get information about handle>

;***    GetHandleInfo - get information about handle
;
;       This procedure is called by DevIOCtl.  DevIOCtl
;       will be broken if there are Real Mode file handles without
;       associated devices.   The only known handles without associated
;       devices are pipes, a Protect Mode only entity.
;
;       ENTRY:  (BX) = handle
;
;       EXIT:   If carry clear
;                       (AL) = unit number
;                       (DX) = SFT flags
;                   if ZR
;                       (DS:SI) = NULL ( pipe or network files )
;                   else
;                       (DS:SI) = device driver header address
;               If carry set,
;                       (AX) = error code
;
;       USES:   AX, BX, DX, SI, DI, DS, ES, Flags
;
        ASSUME DS:NOTHING,ES:NOTHING,SS:NOTHING

Procedure GetHandleInfo,<far16,HYBRID>          ; 73961
        call    GetHandleSFT                    ; (ES:DI),(DS:SI) = SFT & DPB
        jc      short ghi3                      ; if carry set, AX = error code

        mov     dx,es:[di].sf_flags
        test    dx, DEVID_DEVICE + SF_ISNET + SF_PIPE
        jz      short ghi1                      ;    THEN get Device Header
                                                ;         address from DPB

                                                ;    ELSE get Dev Hdr from SFT
ghi0:   xor     al,al                           ; (AL) = unit number (zero)
        lds     si,es:[di].sf_devptr            ; (DS:SI) = device header addr
        jmp     short ghi2

ghi1:   mov     al,ds:[si].dpb_unit             ; (AL) = unit number from DPB
        lds     si,ds:[si].dpb_driver_addr      ; (DS:SI) = device header addr

ghi2:
        test    es:[di].sf_flags2, SFF2_SPOOLED ; spooled file?
        jz      short ghi25                     ; no, return

        or      dx, DEVID_DEVICE                ; yes, say it is a device.
ghi25:
        mov     bx,ds                           ; zero flag set for null devices
        or      bx,bx                           ; Is segment valid? (CY cleared)

ghi3:   RETP

EndProc  GetHandleInfo


BREAK <GetHandleSFT - get SFT and DPB addresses for a handle>

;***    GetHandleSFT - get SFT and DPB addresses for a handle
;
;       ENTRY:  (BX) = handle
;
;       EXIT:   If carry clear
;                       (ES:DI) = sft address for handle
;                       if file or DASD open SFT
;                               (DS:SI) = dpb address for handle
;               If carry set,
;                       (AX) = error code
;
;       USES:   AX, SI, DI, DS, ES, Flags
;
        ASSUME DS:NOTHING,ES:NOTHING,SS:NOTHING

Procedure GetHandleSFT,HYBRID                   ; 73961
;*  Changed to CALLFAR for 73961.
        CALLFAR SFFromHandle                    ; (ES:DI) = SFT pointer
        jc      short ghd1                      ; if carry set, AX = error code

                                                ; IF ('Local File' != SFT flags)
        test    es:[di].sf_flags,DEVID_DEVICE + SF_ISNET + SF_PIPE
        jnz     short ghd1                      ;     THEN just retrun
                                                ;     ELSE get pDPB
;       now get the pointer to the DPB from the VPB

        mov     si,es:[di].sf_fsi.sfi_hVPB              ; (SI) = hVPB
        VPBContext ds,si                        ; (DS:SI) -> VPB
        lds     si,ds:[si].vpb_fsi.vpi_pDPB     ; (DS:SI) -> DPB
        clc
ghd1:   ret

EndProc   GetHandleSFT


BREAK   <Update_SFT_size - Update the size fields in the SFTs for the files>

;***    Update_SFT_size - Update the size fields in the SFTs for the files
;
;       Update_SFT_size updates the sf_fsi.sfi_size field in the SFTs for all the
;       files of this handle from the VPB.
;
;       ENTRY:  ES:DI   VPB pointer
;               DS:SI   SFT pointer
;
;       EXIT:   SFT updated
;
;       USES:   AX, BX, CX, DX, SI, DI, DS, ES, Flags
;
;       NOTE:   No error checking is done to see if the number of sectors
;               is within the 32-bit range.
;
        ASSUME DS:NOTHING,ES:NOTHING,SS:NOTHING

Procedure Update_SFT_size,NEAR,LOCAL
        xor     cx,cx
        mov     ax,cx
        mov     dx,es:[di].vpb_fsd.vpdFAT_max_cluster   ; dx = # of clusters + 1
        dec     dx
        mov     cl,es:[di].vpb_fsd.vpdFAT_cluster_shift
        jcxz    short done_uss_loop
uss_loop:
        shl     dx,1
        rcl     ax,1
        loop    uss_loop
done_uss_loop:
;
; AX:DX = # of sectors in data image
;
        add     dx,word ptr es:[di].vpb_fsd.vpdFAT_first_sector
        adc     ax,word ptr es:[di].vpb_fsd.vpdFAT_first_sector+2
        push    dx
        mul     es:[di].vpb_fsi.vpi_cbSector
        or      DX,DX
        pop     cx
        xchg    cx,ax
        mul     es:[di].vpb_fsi.vpi_cbSector
        add     dx,cx
;
; DX:AX = number of bytes on device
;
        mov     ds:[si].sf_fsi.sfi_size.LowWord,ax
        mov     ds:[si].sf_fsi.sfi_size.HighWord,dx

        push    ds
        pop     es
        mov     di,si                       ; (DS:SI) = SFT pointer

        mov     ax,2
;*  Changed to CALLFAR for 73961.
        CALLFAR ShSU                        ; Update all other SFTs as well
        ret
EndProc   Update_SFT_size



HIGH3CODE ENDS                            ; 73961



        FARCODE INIT
INITCODE segment

;***    Install_ExtDrive - Initialize a DPB for an external drive
;
;       WARNING!! - This code only exists during system initialization and
;                   should not be called after such time.
;
;       Calls W_SICG to set up a DPB for an external drive which is being
;       installed.
;
;       ENTRY   ES:BX -> request packet (after ioctl)
;               DS:SI -> DPB
;
;       EXIT    'C'  = error
;                   AX = return code
;               'NC' = no error
;
;       USES    AX,BX,CX,DX,DI,SI,DS,flags
;
;       ALGORITHM
;       {
;           Setup for sicg function 7
;           move data from request packet into TempBuff
;               (unit & pointer to BPB array)
;           get pointer to device driver header & put into TempBuff
;           W_SICG
;       }
;

Procedure Install_ExtDrive,faronly,local

LocalVar  TempBuff, <size sicg_pkt7 + 2>        ; holds a sicg_pkt7 + 2

        EnterProc
        SaveReg <ES>                            ; Save selector of request packet

; Set up structure for call to w_SICG

        mov     word ptr TempBuff,7             ; we want to go to sicgf7

; Device driver returns a unit #
                                                ; (ES:BX) -> request packet
        mov     al,es:[bx].PktUnit              ; (AL) = unit for new drive
        mov     TempBuff[2].si7_unit,al
        mov     TempBuff[2].si7_nunits,1        ; install 1 unit

; Device driver returns an "array" of BPBs

        les     bx,es:[bx].InitpBPB             ; (ES:BX) = array of BPB's
        mov     TempBuff[2].si7_bpbvec.Offst,bx
        mov     TempBuff[2].si7_bpbvec.Segmt,es

; DPB contains pointer to device driver
                                                ; (DS:SI) -> DPB
        lds     si,ds:[si].dpb_driver_addr      ; (DS:SI) =  device header addr
        mov     TempBuff[2].si7_devhdr.Offst,si
        mov     TempBuff[2].si7_devhdr.Segmt,ds

        TaskContext ds
        lea     si,TempBuff                     ; (DS:SI) = structure for w_SICG

        CALLFAR w_SICG                          ; Set up new DPB

        RestoreReg <ES>                         ; Restore selector of request packet
        LeaveProc
        ret

EndProc Install_ExtDrive

INITCODE ends

        END
