SCCSID  equ     "@(#)dev.asm    6.8 92/03/11"
;        IBM/Microsoft Confidential
;
;        Copyright (c) IBM Corporation 1987, 1989
;        Copyright (c) Microsoft Corporation 1987, 1989
;
;        All Rights Reserved
;
;        MODIFICATIONS
;
;  94/14/05   84997   Change Team   FindDevName is called when a fault occurs.
;                                   If it is interrupt time, do not try and set
;                                   the trap handler inside FindDevName. Else,
;                                   the interrupt stack will overflow (Trap 8).
;                                   Regression from defect 73418.
;
;  02/05/95  148006   Change Team   HIGH2CODE was getting > 64K. Moved code to
;                                   HIGH3CODE.
;
;  03/15/96  148753   Joel Smith    Interim PNP Solution.
;
;
PAGE    ,132
TITLE   DEV - Device call routines
NAME    DEV
.286p

; Misc Routines to do 1-12 low level I/O and call devices
;
;   Public routines:
;
;       Dev_OC_SFT
;       Dev_Open_SFT
;       Dev_Close_SFT
;       DevDeInstall
;       DevIOCall2
;       DevWOHandle                    ; B731797
;       InitReqPackets
;       AllocReqPacketWait
;       FreeReqPacket
;       SetRead
;       SetWrite
;       SetReadNoAlloc
;       SetWriteNoAlloc
;       InitDevice
;       InitDeviceRes
;

.xlist
.xcref
include dosseg.inc
include basemaca.inc
include fnodev.inc
include osconst.inc
include osmaca.inc
include devsym.inc
include mi.inc
include filemode.inc
include harderr.inc
include ioctl.inc
include pmode.inc
include sft.inc
include error.inc
include dpb.inc
include vpb.inc
include pgexport.inc
include tkexport.inc
include vmowner.inc
include infoseg.inc
include except.inc
include strat2.inc
include buffer.inc
include dossymc.inc
.cref
.list

        ModName dev, FNO_DEV_ASM
        CpuMode 386

        ReferTask       VerFlg,BYTE
        ReferTask       KBD_Mode,BYTE
        ReferTask       CurrTCB,WORD
        ReferTask       CurrTSD,WORD

        ReferGlobal     InterruptLevel,DWORD ;84997
        ReferGlobal     NumIO,BYTE      ; Number of physical drives
        ReferGlobal     GDT,WORD
        ReferGlobal     MaxThreads,WORD
        ReferGlobal     DevHead,DWORD
        ReferGlobal     SISData,BYTE
        ReferGlobal     _Sis2,BYTE
        ReferGlobal     DevHlp,DWORD

        ReferGDT        GDT_OS2LDR,WORD
        ReferGDT        GDT_VPB,WORD

        ReferFlat       _pTCBCur,dword

        EXTRNFAR        FuByte
        EXTRNFAR        HardError
        EXTRNFAR        Bmp_Init
        EXTRNFAR        Bmp_Get
        EXTRNFAR        Bmp_Free
        EXTRNFAR        SegAlloc
        extrn           Bmp_SegSize:ABS
        EXTRNFAR        DeviceHelpDM

        EXTRN           VirtToPhysDM:NEAR
        EXTRN           DevWait:NEAR
        EXTRN           SelToSeg:NEAR
        EXTRN           RealMode:NEAR
        EXTRN           ProtMode:NEAR
        EXTRN           ProcBlock:NEAR
        EXTRN           ProcRun:NEAR
        EXTRN           _VMUnlock:NEAR
;       EXTRN           Find_DPB:NEAR        ; B731797
        EXTRNFAR        Find_DPB        ; B731797 -- 148006

        EXTRN           ErrMap24:BYTE
        EXTRN           CONdev  :DWORD                  ;@V2.0SEN01

DATA    SEGMENT
Public  ReqPktSegs
ReqPktSegs      dw      10 dup(0)
Public  MaxReqPktsSegs
MaxReqPktsSegs  db      ?
BMPWait         db      ?                                               ;b720100
DATA    ENDS
REQPKT_GROWDELT EQU     5

; 148006        FARCODE HIGH2
; 148006   HIGH2CODE  SEGMENT
        FARCODE HIGH3
HIGH3CODE  SEGMENT
        ASSUME  DS:NOTHING,ES:NOTHING,SS:TASKAREA

; BUGBUG danno 29-Nov-1988
; CharHard was moved from 3xbox/ctrlc.asm

;***    CharHard - Character device error handler
;***    CharHardAllowed - Character device error handler
;
;       Produce a hard error on a character device.  The alternate
;       entry point CharHardAllowed is used when a specific allowed
;       action is desired; otherwise all actions are allowed.
;
;       ENTRY   DS:SI   pointer to device
;               DI      device error code
;               ES:BX   point to packet
;               AH = Allowed actions (CharHardAllowed only)
;
;       EXIT    ???
;
;       USES    AX
;
;       CALLED FROM     DEV: IOFunc,
;                       DISK: LOAD, STORE

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

Procedure   CharHard,HYBRID

        or      ah,ALLOWED_FAIL + ALLOWED_IGNORE + ALLOWED_RETRY

Entry       CharHardAllowed
;       PMONLY

        push    cx
        push    bp
        mov     bp,CurrTCB
        mov     [bp].TCBAllowed,ah
        pop     bp
        mov     ax,HEPacket
        mov     cx,-1
        CALLFAR HardError
        pop     cx
        ret
EndProc     CharHard


;***    Dev_OC_SFT - common open/close requesting
;
;       Dev_OC_SFT sends the open/close to the device and handles errors.
;
;       ENTRY   (es:di)     point to sft to close
;               (cl)        command to execute
;       EXIT    carry clear successful close
;               carry set   error
;       USES    flags
;

Procedure  Dev_OC_SFT,near
        assume  ds:nothing,es:nothing,ss:TASKAREA
        LocalVar    pSFT,DWORD
        LocalVar    ErrCode,WORD        ; Error code from device driver
        LocalVar    dcs_pDev,DWORD

        EnterProc   <es,di,ax>          ; pSFT = es:di, ErrCode = ax
        pusha
        push    ds
        push    es

        xor     dx,dx                   ; dx = 0 => no error

        test    es:[di].sf_flags,SF_ISNET
        jnz     dcs60                   ; Remote, go return OK

        mov     al,0                    ; assume character device, unit 0  @DB75366
        test    es:[di].sf_flags,DEVID_DEVICE  ; character device?
        jnz     short dcs10             ;   YES, use sf_devptr

; use VPB to find DPB to get device driver address
        mov     di,es:[di].sf_fsi.sfi_hVPB
        VPBContext es,di                ; es:di -> VPB
        mov     al,es:[di].vpb_fsi.vpi_unit     ; al = drive number     @DB75366
                                                ; al = unit instead of drive
                                                ; device driver needs unit
                                                ; for CDROM to work     @DB82481
        les     di,es:[di].vpb_fsi.vpi_pDPB     ; es:di -> DPB
        les     di,es:[di].dpb_driver_addr ; es:di -> device driver
        jmp     short dcs20

dcs10:  les     di,es:[di].sf_devptr    ; (es:di) -> device driver
dcs20:  test    es:[di].SDevAtt,DEV_30
        jz      short dcs60             ; doesn't support open/close, return OK

        mov     dcs_pDev.Offst,di
        mov     dcs_pDev.Segmt,es       ; save device driver (es:di)

        push    ax                      ; save unit number              @DB75366
        CALLFAR AllocReqPacketWait      ; (es:bx) point to request packet

;       Top of error-retry loop
;
;       (es:bx) point to request packet
;       (cl)    command to execute
;       (al)    unit to execute command on                              @DB75366

dcs30:  pop     ax                      ; restore unit number           @DB75366
        push    ax                      ; save unit number              @DB75366
        mov     es:[bx].PktLen,PktHeadSize + LENOpnCls
        mov     es:[bx].PktUnit,al                                     ;@DB75366
        mov     es:[bx].PktCmd,cl

        lds     si,[pSFT]               ; (ds:si) point to sft
        mov     ax,[si].sf_fsi.sfi_mode
        and     ax,open_monitor
        mov     es:[bx].PktStatus,ax    ; Status is monitor bit from open mode
        mov     ax,[si].sf_fsi.sfi_selfsfn
        mov     es:[bx].OCSFN,ax        ; Pass SFN to device driver

        xor     ax,ax                   ; presume not last close
        cmp     [si].sf_ref_count,1
        jnz     short dcs40             ; not last close, go store indicator
        inc     ax                      ; is last close
dcs40:  mov     es:[bx].OCLastRef,ax    ; Pass last close indicator to DD

        lds     si,dcs_pDev             ; Device driver from stack frame (ds:si)
        CALLFAR DevIOCall2              ; Request to device driver
        mov     dx,es:[bx].PktStatus
        and     dx,STERR                ; Was there an error? (DX == STERR bit from PktStatus)
        jz      short dcs50             ; no error, go free packet, return OK

;
; Preserve the error code returned by the device driver in a local variable
; for later translation to a DOS error code.
; Be careful to preserve DX for setting 'C' later on exit from this routine.
;
; ES:BX -> request packet
; DX = STERR bit from PktStatus (==0 if no error from device driver)
;
        mov     ax,es:[bx].PktStatus
        and     ax,STECODE              ; AX = error code from device driver
        mov     [ErrCode],ax            ; Save error code for return

;       (es:bx) point to request packet
;       (ds:si) point to device driver

        mov     AH,ALLOWED_FAIL+ALLOWED_RETRY
        call    CharHardAllowed         ; signal error event
        cmp     al,HDERR_RETRY
        jz      short dcs30             ; retry, go reload packet

;
; Convert the I24 error returned by the device driver to an error code
; that can be returned to the user.
;
        DosContext DS
        mov     di,[ErrCode]            ; DI = device driver error code
        xor     ax,ax
        mov     al,ErrMap24[di]         ; AX = DOS error code
        xchg    ax,[ErrCode]            ; Store error code in local var

;       (es:bx) point to request packet
;       (dx)    STERR bit of packet status
;
dcs50:  pop     ax                      ; cleanup stack                 @DB75366
        CALLFAR FreeReqPacket           ; free packet

;       (dx)    STERR bit of packet status

dcs60:  neg     dx                      ; carry clear iff ret == 0
        pop     es
        pop     ds
        popa
        mov     ax,[ErrCode]            ; AX = error code, if there was an
                                        ; error, otherwise it is unchanged
                                        ; from entry
        LeaveProc
        ret
EndProc Dev_OC_SFT


;***    Dev_Open_SFT - Issue an OPEN call to a device
;
;       Entry   ES:DI Points to SFT
;
;       EXIT    'C' set if error (failed by user)
;
;       USES    flags

        ASSUME  DS:NOTHING,ES:NOTHING,SS:TASKAREA
Procedure  Dev_Open_SFT,hybrid
;       PMONLY
        push    cx
        mov     cl,CMDOpen
        call    Dev_OC_SFT
        pop     cx
        ret
EndProc Dev_Open_SFT

;***    Dev_Close_SFT - Issue a CLOSE call to a device
;
;       Entry   ES:DI Points to SFT
;
;       EXIT    'C' set if error (failed by user)
;
;       USES    flags
;

        ASSUME  DS:NOTHING,ES:NOTHING,SS:TASKAREA
Procedure  Dev_Close_SFT,hybrid
;       PMONLY

        push    cx
        mov     cl,CMDClose
        call    Dev_OC_SFT
        pop     cx
        ret
EndProc Dev_Close_SFT

; ******************************  B731797  Begin *************************
;***    w_DevWOHandle   - Call disk drivers strat routine without a handle
;
;       This guy is the worker called by DevWOHandle
;
;       Entry
;               Builds parameter packet from parms passed in through regs
;
;               AL    is the unit number (A is 0, B is 1, etc)
;               CH    is the ioctl category
;               CL    is the ioctl function
;               DL    holds value for pointer GIOParaPack
;
;
;       EXIT    returns (no need to check for error)
;
;       USES    flags, ds:si, es:bx, ax, cx, dx
;
;       PSEUDO
;
;       Derive addr of strat routine (based on unit number),
;       build packet based on values passed in through registers,
;       Call that strat routine with packet built here,
;       Return

Procedure  w_DevWOHandle,<near,faronly,far16>
;       PMONLY
        assume  ds:nothing,es:nothing,ss:TASKAREA

LocalVar        dwoh_ParaBufValue,WORD

        EnterProc

        CALLFAR    Find_DPB                ; get DPB from unit number (al)
; 148006    call    Find_DPB               ; get DPB from unit number (al)
        jnc     dwoh10                     ; Found the DPB
        mov     al,ERROR_I24_BAD_UNIT      ; (AX) = bad drive specified
        jmp short dwohxt                   ; Go clean up and exit
dwoh10:
        lds     si,ds:[si].dpb_driver_addr  ; DS:SI -> device driver header

        push    ax
        push    cx
        push    dx

        mov     dh,1                    ; because we're not a dh_allocreqpacket
        CALLFAR AllocReqPacketWait      ; (es:bx) point to request packet

        pop     dx
        pop     cx
        pop     ax

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

        mov     es:[bx].PktStatus,0     ; initialize values and store
        mov     es:[bx].PktFlag,0       ; parms passed in registers
        mov     es:[bx].PktUnit,al
        mov     es:[bx].PktCmd,10h
        mov     es:[bx].GIOCategory,ch
        mov     es:[bx].GIOFunction,cl

;   Take value passed in DL and set up a pointer to that value (GIOParaPack)

        mov     dh,0
        mov     dwoh_ParaBufValue,dx
        lea     ax, TASKAREA:dwoh_ParaBufValue
        mov     es:[bx].GIOParaPack.Offst,ax
        mov     ax,ss
        mov     es:[bx].GIOParaPack.Segmt,ax

;   Passing a NULL pointer for GIODataPack

        xor     ax,ax
        mov     es:[bx].GIODataPack.Offst,ax
        mov     es:[bx].GIODataPack.Segmt,ax

        CALLFAR DevIOCall2              ; call device
        CALLFAR FreeReqPacket           ;71308  free requested packet
dwohxt:
        LeaveProc
        RETP
EndProc w_DevWOHandle
; ******************************  B731797  End ***************************


;***    FindDevName - Find Device driver name
;
;       FindDevName scans the dualmode device driver list, looking for
;       the device driver whose code segment matches the input CS.
;       Realmode only (old) device drivers will not be found.
;
;       ENTRY   (ES:DI) = Address of buffer to hold faulting device name
;               (CX)    = Faulting device driver Code Segment
;
;       EXIT    Carry Flag clear if device name found (buffer filled in)
;               Carry Flag set device name not found
;
;       USES    AX, BX, CX, DX, SI, DI, DS, Flags
;
        ASSUME DS:NOTHING, ES:NOTHING, SS:NOTHING

NAME_LEN        equ     4

Procedure FindDevName,<near,faronly,far16>
        cmp     cx,DOSCODE              ; check if in DOSCODE
        je      fdn40                   ;                           73418 csc

        xor     dx,dx                   ; clear block number counter

;       Point to 1st device header in the linked list

        DosContext ds
        cmp     InterruptLevel,-1       ; 84997 - Is it interrupt time?
        jne     fdn                     ; 84997 - Yes, don't set trap handler
        push    bp                      ;                           73418 csc
        mov     bp,ss:CurrTSD           ;                           73418 csc
        SetTrap <offset cs:fdnset>,cx,bp  ; set trap handler        73418 csc
fdn:                                    ; 84997
        mov     si,OFFSET DevHead

fdn00:  cmp     word ptr [si],-1        ; have we reached the end of the chain?
        je      short fdnset            ;                           73418 csc

        cmp     [si].SDevProtCS,cx      ; Is this our faulting device?
        jne     short fdn20

        cld
        test    [si].SDevAtt,DEV_CHAR_DEV ; character device?
        jz      short fdn10

;       Copy the character device name to the buffer

        add     si,( OFFSET SDevName - OFFSET SDevNext ) ; point to device name
        mov     cx,NAME_LEN             ; copy device name
        rep     movsw
        jmp     short fdnclr

;       Set the  block device drive letter

fdn10:  add     dl,'A'
        mov     dh,':'
        mov     es:[di],dx
        jmp     short fdnclr

fdn20:  test    [si].SDevAtt,DEV_CHAR_DEV ; character device?
        jnz     short fdn30

        add     dl,[si].SDevName        ; bump up block number
fdn30:  push    [si].SDevNext.Segmt     ; point to next device head
        mov     si,[si].SDevNext.Offst
        pop     ds
        jmp     short fdn00

fdnclr: DosContext ds                   ; 84997
        cmp     InterruptLevel,-1       ; 84997 - Is it interrupt time?
        jne     fdn90                   ; 84997 - Yes, trap handler never set
        ClrTrap bp                      ; unset trap handler        73418 csc
        pop     bp                      ;                           73418 csc
fdn90:                                  ; 84997
        clc                             ; matching device driver    73418 csc
        RETP

fdnset: DosContext ds                   ; 84997
        cmp     InterruptLevel,-1       ; 84997 - Is it interrupt time?
        jne     fdn40                   ; 84997 - Yes, trap handler never set
        ClrTrap bp                      ; unset trap handler        73418 csc
        pop     bp                      ;                           73418 csc
fdn40:  stc                             ; no matching device driver 73418 csc
fdnx:   RETP
EndProc FindDevName

; 148006  HIGH2CODE  ENDS
HIGH3CODE  ENDS


        FARCODE LOW
CODE    SEGMENT

;***    DevDeInstall - Sends a DEINSTALL request to a device driver.
;
;       Entry   (DS:SI) = device header
;
;       Exit    CARRY - clear if OK
;                       set if deinstall refused
;
;       USES:           AX, BX, ES
;
;
Procedure DevDeInstall,hybrid

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

        call    AllocReqPacketWait
        mov     es:[bx].PktLen,PktHeadSize+LENDeIn
        mov     es:[bx].PktCmd,CMDDeInstall
        mov     es:[bx].PktStatus,0     ; initialize status
        call    DevIOCall2
        mov     ax, es:[bx].PktStatus
        call    FreeReqPacket           ; Does not effect any registers
        shl     ax, 1                   ; shift error bit into carry flag
        ret

EndProc DevDeInstall



;***    CmdTranTable
;
;       CmdTranTable is a table of bytes for every I/O packet type (command)
;       that has a transfer address which must be converted from a virtual
;       to a physical address.  Only enough entries are needed to span the
;       first command to the last one which needs translation.
;
;       DevIOCall2 indexes into this table to decide whether translation is
;       needed or not.  This technique is faster than testing a bit vector,
;       which is why we spend the few extra bytes here.
;
;       Bit 1 set -> translation required.  No other bits defined.

CmdTranTable    db      3 dup(0)                        ; 0-2

                .errnz  (CMDIOCTLR - ($-CmdTranTable))
                db      1                               ; 3

                .errnz  (CMDINPUT - ($-CmdTranTable))
                db      1                               ; 4

                db      3 dup(0)                        ; 5-7

                .errnz  (CMDOUTPUT - ($-CmdTranTable))
                db      1                               ; 8

                .errnz  (CMDOUTPUTV - ($-CmdTranTable))
                db      1                               ; 9

                db      2 dup(0)                        ; 10-11

                .errnz  (CMDIOCTLW - ($-CmdTranTable))
                db      1                               ; 12

                db      11 dup(0)                       ; 13-23

                .errnz  (CMDInputBypass - ($-CmdTranTable))
                db      1                               ; 24

                .errnz  (CMDOutputBypass - ($-CmdTranTable))
                db      1                               ; 25

                .errnz  (CMDOutputBypassV - ($-CmdTranTable))
                db      1                               ; 26

                .errnz  (CMDInitBase - ($-CmdTranTable))
                db      0                               ; 27

CmdTranLimit    equ     $-CmdTranTable



;***    DevIOCall2 - Call a device
;
;       Perform a long call to the device driver to process a request packet.
;       The device driver may block inside itself or may elect to have us
;       block in DevWait.
;
;       ENTRY:  DS:SI = points to device header
;               ES:BX = tiled request packet
;
;       EXIT:   NONE
;
;       USES:   Flags
;
;       NOTES:  This routine must be passed a tiled request packet
;               and if DEVSTRICT is defined this routine will check
;               to make sure the request packet is tiled.
;
;               If calling a old device driver from protected mode
;               this routine will switch modes, but it will not
;               translate any addresses in the request packet to
;               real mode address.  So only request packets that
;               contain no address can be sent to old device drivers
;               from protected mode. If DEVSTRICT is defined it
;               will check to make sure the request packets don't
;               need address translation if called from protected
;               mode.
;
;               This routine converts the virtual transfer address
;               for calls to new device drivers to a physical address
;               saving the virtual address and restoring it back into
;               the request packet after calling the device driver. The
;               request packet commands it does this for are 3, 4, 8,
;               9, 12, 24, 25, 26.
;
;       NOTE:   This routine is performance critical, especially in the
;               dos5 level device driver path, so it's been hacked up
;               a little to optimized for speed.
;
;       EFFECTS Switches Context (DevWait or inside device driver)

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

IFDEF   DEVSTRICT
dioie1: InternalError <DevIOCall2: unlock>
ENDIF

Procedure DevIOCall,<faronly>
          call   DevIOCall
          ret
EndProc   DevIOCall

Procedure DevIOCall2,<near,faronly,far16>

LocalVar ReqPacketAddr,DWORD            ; es:bx on entry
LocalVar DevHeader,DWORD                ; ds:si on entry
; these two variables must be together in order to be initialized correctly
LocalVar Filler,BYTE
LocalVar IsKbdModeIoctl,BYTE            ; if !0, ioctl sets kbd mode
; these two variables must be together in order to be initialized correctly
LocalVar SavedKbdMode,BYTE              ; previous keboard mode
LocalVar SavedTransAddr,BYTE            ; if !0, restore the transfer address
LocalVar TransferAddr,DWORD             ; saved transfer address
LocalVar DevEntry,DWORD                 ; far address of entry point

        mov     es:[bx].PktFlag.HighByte,0

Entry   DevIOCall

        EnterProc <es,bx,ds,si,0,0>     ; establish stack frame data

        pushad                          ; preserve caller's registers
        push    ds                      ;
        push    es                      ;
        PushTrap CLEAR,di               ; Disable local fault handler (if any)

;*  Check if this request packet command has a transfer address to xlate.
;       (ES:BX) -> request packet
;       (DS:SI) -> device header

        mov     di,word ptr es:[bx].PktCmd      ; (DI) = pkt command + garbage
        and     di,00ffh                        ; (DI) = packet command
        cmp     di,CmdTranLimit         ; command in range of interest?
        jae     short dio10             ; no xlate needed, go make request
        test    byte ptr cs:CmdTranTable[di],1  ; cmd need addr translation?
        jz      short dio10             ; no xlate needed, go make request

        test    es:[bx].PktFlag.HighByte,0ffh   ; Need xlate ?
        jnz     short dio10             ; No (see volio.asm:DoVolIO#dov12)
        mov     SavedTransAddr,1        ; mark that the transfer addr is saved

;*  Save the transfer address of request packet commands that have them.

        mov     ax,es:[bx].IOpData.Segmt
        mov     TransferAddr.Segmt,ax
        mov     ax,es:[bx].IOpData.Offst
        mov     TransferAddr.Offst,ax

;*  Convert the virtual transfer address into a physical one

        lds     si,es:[bx].IOpData      ; (DS:SI) point to transfer address
        push    bx                      ; preserve packet offset
        call    VirtToPhysDM            ; convert address
        pop     si                      ; restore offset to packet
        mov     es:[si].IOpData.Segmt,ax; set up msw of address
        mov     es:[si].IOpData.Offst,bx; set up lsw of address
        mov     bx,si                   ; restore packet offset
        lds     si,DevHeader            ; (DS:SI) = device header address

;*  Set up segment registers appropriate to the current mode
;       (ES:BX) -> request packet
;       (DS:SI) -> device header

dio10:  mov     ax,[si].SDevStrat       ; (AX) = offset of strategy routine
        mov     DevEntry.Offst,ax       ; set offset of strategy routine
        mov     ax,[si].SDevProtCS      ; (AX) = selector of strategy routine
        mov     DevEntry.Segmt,ax       ; set up segment of strategy routine

;*  Check for generic IOCTL for some (possible) special handling (details below)

;       (ES:BX) -> request packet
;       (DS:SI) -> device header, thus, DS points to device data segment.

        cmp     es:[bx].PktCmd,CMDGenIOCTL      ; Is it a generic IOCTL ?
        jne     short dio30                     ; skip if not (to do io)

;*  Check for KBD request to set input mode, and record new mode setting
;   in PTDA for use at process termination.

        test    [si].SDevAtt,DEV_CIN    ; console Input Device ?
        jz      short dio25             ; skip if not (for another check)

;       Skip if
;           GIOCategory != IOC_KC - Keyboard  - low byte  OR
;           GIOFunction != IOKC_IM - Set input mode - high byte

        .errnz  (GIOFunction-GIOCategory-1)
        cmp     word ptr es:[bx].GIOCategory,((IOKC_IM shl 8) + IOC_KC)
        jne     short dio25

        push    es
        push    ds
        push    si
        push    bx
        mov     si,es:[bx].GIOParaPack.Offst
        mov     bx,es:[bx].GIOParaPack.Segmt
        CALLFAR FuByte                  ; al = input mode
        jc      short dio20

        mov     SavedKbdMode,al         ; preserve keyboard mode
        mov     IsKbdModeIoctl,-1       ; keyboard set mode ioctl

dio20:  pop     bx
        pop     si
        pop     ds
        pop     es

;*  Check the device driver level: if DevLev_2 or greater, then it can handle
;   new length.  OTHERWISE, we MUST set to the old length

dio25:  mov     cx,[si].SDevAtt         ; (CX) = Device attribute word
        Dev_FuncLev DEVLEV_2,cx         ; 'C' if level < DEVLEV2
;       jnc     short dio30             ; new enough - go ahead        ;b726522
        jc      short dio27             ; set old length               ;b726522

        cmp     cx,DEVLEV_2                                            ;b726522
        je      short dio30             ; level is 2 so if can handle new length
                                        ; check if level 3 has bit 0 set
        test    ds:[si].SDevCaps,DEV_IOCTL2                            ;b726522
        jnz      short dio30            ; if set can handle new length ;b726522

dio27:                                                                 ;b726522
        mov     es:[bx].PktLen,PktHeadSize+LENGenIOCTL  ; put old len in packet



;*  Call the device driver; the device driver is free
;   to use all the registers.
;       (ES:BX) -> request packet

dio30:  push    bp
        call    DevEntry                ; Call device driver strategy
        pop     bp

;*  If request wasn't completed, we must wait for the device.

        les     bx,ReqPacketAddr        ; (ES:BX) = caller's packet

dio45:  cli                             ; turn off interrupts to check bit
        test    es:[bx].PktStatus,STDON ; completed?
        jnz     short dio50             ; yes
        call    DevWait                 ; wait until device done
        jmp     short dio45

dio50:  sti                             ; enable interrupts
        test    IsKbdModeIoctl,-1       ; keyboard mode change ioctl?
        jz      short dio55             ; no
        test    es:[bx].PktStatus,STERR ; ioctl succeeded?
        jnz     short dio55             ; no
        mov     al,SavedKbdMode         ; ioctl succeeded, save kbd mode
        mov     KBD_Mode,al             ; save mode in the PTDA

dio55:  lds     si,[DevHeader]          ; (DS:SI) = device header address
        test    [si].SDevAtt,DEV_CLOCK  ; was it the clock device?
        jz      short dio65             ; no

;       We dispatched to the clock device driver.  If it was a write,
;       then copy the time and date fields in the master system infoseg
;       to the infoseg copy.
;
;       NOTE:  This assumes the clock driver completes the operation
;       without blocking.

        mov     al,es:[bx].PktCmd
        cmp     al,CMDOUTPUT            ; copy only device writes
        je      short dio60
        cmp     al,CMDOUTPUTV
        jne     short dio65

dio60:  FlatContext <ds,es>,ax
        DosContext fs,ax
        mov     edi,fs:[_Sis2].is_va
        mov     esi,offset FLAT:SISData
        mov     ecx,(SIS_TIMELEN + 3) SHR 2
        cld
        rep     movs dword ptr es:[edi], dword ptr ds:[esi]

        assume  ds:nothing, es:nothing, fs:nothing

;*  Restore the request packet transfer address

dio65:  les     bx,ReqPacketAddr        ; (ES:BX) = caller's packet
        cmp     SavedTransAddr,0
        je      short dio70             ; go to exit if transfer addr not saved

        mov     ax,TransferAddr.Segmt
        mov     es:[bx].IOpData.Segmt,ax
        mov     ax,TransferAddr.Offst
        mov     es:[bx].IOpData.Offst,ax

dio70:  PopTrap bx                      ; Reenable local fault handler
        pop     es                      ; restore caller's registers
        pop     ds                      ;
        popad                           ;
        LeaveProc                       ; remove stack frame
        RETP
EndProc   DevIOCall2

;************************************************************************
;               DEVICE DRIVER REQUEST PACKET ALLOCATION
;************************************************************************
;
;  We need two types of req.pkts -
;       i) strategy1 req pkts (32 bytes const.size) for floppies
;               and those pre-os/2 2.0 DDs that do not support Scatter/Gather
;       ii) Strategy2 req pkts (variable size)
;               - for DDs that support scatter/gather
; We have a BMP (actually  there can be a max of 10 segs , the selectors
;               being stored in ReqPktSel[] or whatever) for Strategy1
;               req pkts. We allocate one req pkt for each thread at
;               thread creation time (GetTCBReqPkt procedure) and store
;               the pointer in the TCB (TCBReqPkt and there is also a flag
;               that keeps track of whether the req pkt is in use or not).
;
; We have a static req pkt pool of the max.strat2 req pkt size for the
;               swapper/pager. In the worst case, we will need three req
;               pkts for each swapper thread (at present there can be 4
;               swapper threads - defined by MAXSIMULSWAP). Actually we
;               need one strategy2 for the original swapper req pkt, one
;               for the DD for vol.check etc (this is a strat1 pkt)., and
;               one for the kernel use in volmount logic ?
;
; We also have a static req pkt pool for Strat1 requests.
;
; The allocation logic is as follows:
;
;       IF (STRAT2 & not-for-swapper) {
;               get a pkt from the BMP
;       }
;
;       /* else --- i.e. if strat1 */
;       If TCBReqPkt is free grab it;
;       else {
;         if (SWAPPER) get one pkt from the swapper reqpkt pool
;         else get one from the static strat1 pool;
;       }
;
; Currently we don't allocate variable size strat2 pkt for the following
; reasons:
;       - BMP does not support variable size record
;       - we can't use RMP because RMP recs can move.
;       - we don't want to allocate it statically.
;
; we declare the max. size for it - i.e. headers + 8 bytes for the pageinfo
;       * 17 pages  ( we do, at the most, 64k at a time, and 64 k can at the
;       most, be split into 17 pages.)
;
;************************************************************************
;*      GetTCBReqPkt - Get a request packet for a TCB
;
;       ENTRY   NONE
;
;       EXIT    AX = 0 => CX:DX = Req.Pkt addr
;               AX = non-zero => ERROR
;
;       USES    AX,BX,CX,DS
;
        ASSUME  DS:NOTHING,ES:NOTHING

Procedure GetTCBReqPkt,<near,faronly,far16>

        DosContext DS

        xor     ch,ch
        mov     cl,MaxReqPktsSegs       ; (CX) = maximum number of reqpkt segs
        xor     bx,bx
grp0:   mov     ax,ReqPktSegs[bx]       ; (AX) = Request Pkt segment
        or      ax,ax                   ; Request pkt segment 0 ?
        jnz     grp1                    ; no, go get block
        SaveReg <ds,bx,cx>
        xor     ax, ax
        mov     bx, Bmp_SegSize
        mov     cx, (D_DATA0 SHL 8) + SA_GDT
        .ERRNZ  HF_MOVABLE
        xor     si, si
        mov     di,REQPKTTCBOWNER
        CALLFAR SegAlloc                ; Allocate Bmp segment
        RestoreReg <cx,dx,ds>
        jc      grp3
        SaveReg <dx,cx,bx>
;
; Initialize the segment to be a BMP managed segment.
;       ax = segment handle
;       bx = segment selector
;       cx = block size
;       dx = grow delta (in blocks)
;
        mov     cx, ReqPktSize
        mov     dx, REQPKT_GROWDELT
        CALLFAR Bmp_Init                ; initialize Bmp segment
        RestoreReg <ax,cx,bx>
        mov     ReqPktSegs[bx],ax       ; save selector in ReqPkt array

grp1:   mov     es,ax                   ; (ES) = Bmp managed segment
        CALLFAR Bmp_Get                 ; Get Block
        jnc     grp2
        add     bx,2
        loop    grp0                    ; Get next Request array entry

; no packet available in any of the req pkt segments

        mov     ax,ERROR_NOT_ENOUGH_MEMORY
        jmp     short grp3

grp2:   mov     dx,di
        mov     cx,es                   ; (CX:DX) = Request addr
        xor     ax,ax
grp3:   ret
EndProc   GetTCBReqPkt
;@V2.0SEN01     start
;************************************************************************
;*      w_CallPddIoctl - Locate the device header of the PDD whose
;                        name is passed in DS:DI. Call the IOCTL of the
;                        device and pass the request packet ES:BX.
;
;       ENTRY   NONE
;
;       EXIT    AX = 0 success, bit 15 set if error.
;
;       USES    AX,CX,DI,SI. Preserves DS, ES, BX.
;
Procedure w_CallPddIoctl, <faronly>
        SaveReg         <ds,es,bx>
        mov     ax, ds
        mov     es, ax                  ;es:di holds PDD name
        DosContext      ds              ;load DS
        mov     bx, offset DOSGROUP:CONdev

SearchLoop:
        or      ax, STERR               ;set bit 15 for error.
        cmp     word ptr ds:[bx].SDevNext, -1
        je      SearchExit
        lds     bx, ds:[bx].SDevNext
        lea     si, ds:[bx].SDevName
        mov     ax, di                  ;save di
        mov     cx, 4                   ;4 words
        repe    cmpsw                   ;es:di target, ds:si source
        mov     di, ax                  ;restore es:di
        jne     SearchLoop
        mov     si, bx                  ;ds:si points to device header.
        RestoreReg   <bx,es>            ;es:bx points to request packet
        CALLFAR DevIOCall2
        SaveReg      <es,bx>
        xor     ax, ax                  ; reset for success
SearchExit:
        RestoreReg      <bx,es,ds>
        ret
EndProc   w_CallPddIoctl
;@V2.0SEN01     end
CODE    ENDS


DATA    SEGMENT

;***    Device driver request packet pools.

ReqPktHead      DW      ?               ; first free static packet

SwpReqPktHead   DW      ?               ; first free swapper req packet (strat2)

        public  FirstPacket,LastPacket

EVEN
FirstPacket     LABEL   BYTE            ; Start of array of normal packets
        DB      StaticPackets*ReqPktSize DUP(?)

SwapFirstPacket LABEL   BYTE            ; Start of array of swapper packets
        DB      3 * MAXSIMULSWAP * S2_RW_RQPKT_SIZE     DUP(?)

LastPacket      LABEL   BYTE            ; end of all packets

DATA    ENDS


DATA segment

public s2BmpSel
s2BmpSel        dw      0

DATA ends


        FARCODE INIT
INITCODE segment

;***    InitReqPackets - Initialize request packet table>
;
;       Link packets together.  Forward link is first word of packet.
;
;       InitReqPackets is called AFTER the per-thread resizing
;       is done for the system.  Therefore, we will init and link-up the
;       swapper pool  and get the first BMP segment for the TCBReqPkt
;       allocations in this call.
;
;       WARNING ** Don't call this routine until AFTER MaxThreads is set **
;
;       ENTRY   NONE
;
;       EXIT    NONE
;
;       USES    AX, BX, CX, DI, SI, DS, ES, Flags
;

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

Procedure InitReqPackets,NEAR
        DosContext  DS

;       Initialize swapper request packet list

        MOV     SI,OFFSET DOSGROUP:SwapFirstPacket  ; (SI) = offset of first packet
        MOV     SwpReqPktHead,SI        ; Set head of packet list
        MOV     CX, (3 * MAXSIMULSWAP)-1; (CX) = # of packets to link

irp1:   MOV     DI,SI                   ; (DI) = offset of last packet
        ADD     SI,S2_RW_RQPKT_SIZE     ; (SI) = next request packet
        MOV     [DI],SI                 ; Set link pointer
        LOOP    irp1                    ; Advance to next packet

        MOV     [SI],CX                 ; Put null pointer in last packet

; initialize Strat1 request packets
        MOV     SI,OFFSET DOSGROUP:FirstPacket  ; (SI) = offset of first packet
        MOV     ReqPktHead,SI           ; Set head of packet list
        MOV     CX,StaticPackets-1      ; (CX) = # of packets to link
irp2:   MOV     DI,SI                   ; (DI) = offset of last packet
        ADD     SI,ReqPktSize           ; (SI) = next request packet
        MOV     [DI],SI                 ; Set link pointer
        LOOP    irp2                    ; Advance to next packet

        MOV     [SI],CX                 ; Put null pointer in last packet

; Get the first BMP segment for the TCBReqPkt allocations of strat1 packets.
; also initialise MaxReqPktsSegs to the proper value.
; For 2.0 max threads = 32k, and hence, maxreqpktsSegs can at the most be
; 16 . ( But we will run out of memory or some such thing VERY MUCH BEFORE
; we get to 32k threads). ( THERE IS ONLY PLACE FOR 10 SEGMENT POINTERS NOW)

        mov     ax,MaxThreads           ; Config.sys MaxThreads
        xor     dx,dx
        mov     cx,ReqPktSize           ; size of a request packet
        mul     cx
        mov     cx,0ffffh               ; size of a segment
        div     cx
        inc     ax                      ; just for safe keeps
        mov     MaxReqPktsSegs,al

        xor     ax, ax
        mov     bx, Bmp_SegSize
        mov     cx, (D_DATA0 SHL 8) + SA_GDT
        .ERRNZ  HF_MOVABLE
        xor     si, si
        mov     di,REQPKT1OWNER
        push    ds
        CALLFAR SegAlloc
        pop     ds
        jc      ieinreq
;
; Initialize the segment to be a BMP managed segment.
;       ax = segment handle
;       bx = segment selector
;       cx = block size
;       dx = grow delta (in blocks)
;
        mov     ReqPktSegs[0],bx        ; Initial Bmp segment
        mov     cx, ReqPktSize
        mov     dx, REQPKT_GROWDELT
        CALLFAR Bmp_Init

; allocate and init the BMP segment for strat2 req packets
        xor     ax,ax
        mov     bx,Bmp_SegSize
        mov     cx,(D_DATA0 SHL 8) + SA_GDT
        mov     si, (HF_MOVABLE )
        mov     di,REQPKT2OWNER
        push    ds
        CALLFAR SegAlloc
        pop     ds
        jc      ieinreq

        DosContext      <ds>
        mov     [s2BmpSel],bx           ; Save selector returned by SegAlloc
        ASSUME  DS:NOTHING

        mov     cx,S2_RW_RQPKT_SIZE
        mov     dx,3

;*      Initialize the segment to be a BMP managed segment.
;
;       (ax) = segment handle
;       (bx) = segment selector
;       (cx) = block size
;       (dx) = grow delta (in blocks)

        CALLFAR Bmp_Init

        ret


ieinreq:
        InternalError   <InitReqPackets: cannot allocate segment>

EndProc   InitReqPackets

INITCODE ends


        FARCODE LOW
CODE    SEGMENT

;***    AllocReqPacketWait  - Allocate request packet
;***    S2AllocReqPkt   - Allocate a strat2 request packet
;
;       ENTRY   DH = 0 if waiting is OK ( for dh_AllocReqPacket only )
;
;       EXIT    (ES:BX) = tiled request packed , Carry clear
;               Carry set if no packet available
;
;       USES    BX, DX, ES, Flags
;
;               FS and GS must be preserved (DevHelp requirement)
;
;       PSEUDOCODE:
;       for AllocReqPktWait:
;               If (TCBReqPkt available) {
;                       grab this packet and return success;
;               }
;               /* else */
;               if (Swapper) {
;                       call dh_allocreqpacket to get a strat1 pkt from the
;                       swapper pool
;               }
;               else {
;                       get a strat2 BMP req pkt and use it for strat1
;               }
;
;       for S2AllocReqPk:
;               if (Swapper) {
;                       call dh_allocreqpacket to get a pkt from the
;                       swapper pool
;               }
;               else {
;                       get a strat2 BMP req pkt
;               }
;
        ASSUME  DS:NOTHING,ES:NOTHING,SS:TASKAREA

Procedure S2AllocReqPkt,<faronly>
        call    S2AllocReqPkt
        ret
EndProc S2AllocReqPkt

Procedure AllocReqPacketWait,<far16>
        call    AllocReqPacketWait
        RETP
EndProc AllocReqPacketWait

Procedure AllocReqPacketWait,<hybrid>

        SaveReg <dx,bp,di>
        mov     bp,[CurrTCB]
;
; strat1 req pkt - try tcb first
;
        mov     bl,[bp].TCBReqPktFlg            ; (BL) = flags
        xor     bl,TK_RP_ALLOCATED              ; Toggle the allocated bit
        test    bl,TK_RP_ALLOCATED + TK_RP_INUSE; Packet non-existent or busy?
        jnz     short rqpw0                     ;  yes, bad luck
        les     bx,[bp].TCBReqPkt
        or      [bp].TCBReqPktFlg,TK_RP_INUSE   ; Packet busy, carry clear
        jmp     short rqpw15

Entry   S2AllocReqPkt

        push    dx
        push    bp
        push    di
        mov     bp,[CurrTCB]
;
; TCB req pkt is alread used up or
; we need a strat2 packet
; If this is for swapper, get the pkt from swapper pool
; else call BMP to get a packet
; BP -> CurrTCB
rqpw0:  TEST    [bp].TCBfSwapping,-1    ; Are we swapping?
        jnz     rqpw17                  ; go get one from swapper pool

;       Do we have a buffer?

        test    [bp].TCBForce_Flg,FF_BUF
        jz      rqpw5                   ; No force flag, go alloc using Bmp

;       Is the buffer busy.  (We shouldn't ever have FF_BUF set and then
;       not be pointing to a busy buffer, but RlseBuffr doesn't enforce
;       this, so we can't either.)

        SaveReg <ds,bp>
        lds     bp,[bp].TCBCurBuf       ; (DS:BP) -> buffer
        test    DS:[BP].buf_flags,BUF_BUSY
        RestoreReg <bp,ds>
        jnz     rqpw17                  ; Busy, go alloc from swapper pool

rqpw5:  DosContext <es>
rqpw5a: mov     es,[s2BmpSel]
        CALLFAR Bmp_Get
        mov     bx,di                   ; es:bx -> req packet
        jc      rqpw30

; Strat2 packets cannot cross page boundaries that aren't contiguous.
; If this packet is going to cross the boundary, get another one and
; leave this one.
        and     di, 0FFFh
        cmp     di, 0FFFh - S2_RW_RQPKT_SIZE
        ja      rqpw20

rqpw15: RestoreReg <di,bp,dx>
        RETP

rqpw17: xor     dh,dh                   ;
        call    near ptr dh_AllocReqPacket
        jmp     short rqpw15

rqpw20: mov     DWORD PTR es:[bx], 0FFFFh
        jmp     rqpw5


rqpw30: SaveReg <fs,gs>                                                  ;b720100
        pusha                                                            ;b720100
        NullContext <fs,gs>,ax          ; Zero FS and GS before blocking ;b720100
        DosContext <es>,ax                                               ;b720100
        MOV     bx,OFFSET DOSGROUP:BMPWait                               ;b720100
                                        ; (AX:BX) = event identifier     ;b720100
        MOV     cx,-1                                                    ;b720100
        MOV     di,cx                   ; (CX) = no timeout interval     ;b720100
        MOV     dh,1                    ; (DH) = not interruptible       ;b720100
        cli                                                              ;b720100
        mov     es:BMPWait, 1                                            ;b720100
        call    ProcBlock               ; Wait for wake up               ;b720100
        popa                                                             ;b720100
        RestoreReg <gs,fs>                                               ;b720100
        JMP     SHORT rqpw5a            ; See if a packet is available   ;b720100


EndProc AllocReqPacketWait


Procedure dh_AllocReqPacket,near

        PUSH    AX
        PUSH    DI
        CMP     DH,1
        JA      arp3                    ; Return error if wait flag (DH) > 1

arp1:   CLI                             ; Don't let list change!
        DosContext <ES>
        MOV     DI,OFFSET DOSGROUP:ReqPktHead

        mov     bx,[CurrTCB]
        TEST    TASKAREA:[bx].TCBfSwapping,-1   ; Are we swapping?
        JNZ     arp10                   ; yes, go use swapper packet

;       Do we have a buffer?

        test    TASKAREA:[bx].TCBForce_Flg,FF_BUF
        jz      arp1a                   ; No force flag, go get normal packet

;       Is the buffer busy.  (We shouldn't ever have FF_BUF set and then
;       not be pointing to a busy buffer, but RlseBuffr doesn't enforce
;       this, so we can't either.)

        SaveReg <ds,bp>
        lds     bp,TASKAREA:[bx].TCBCurBuf       ; (DS:BP) -> buffer
        test    DS:[BP].buf_flags,BUF_BUSY
        RestoreReg <bp,ds>
        jz      arp1a                   ; Not Busy, go alloc from normal pool

arp10:  MOV     DI,OFFSET DOSGROUP:SwpReqPktHead

;       (ES:DI) -> ReqPktHead OR SwpReqPktHead, as appropriate

arp1a:  MOV     BX,ES:[DI]              ; (BX) = offset of packet to allocate
        OR      BX,BX                   ; Check for null pointer
        JNZ     arp2                    ; Packet is available
        OR      DH,DH
        JNZ     arp3                    ; Request is to not wait - ret with CY

;       Both Normal and Swapper requests block (and run) using the Normal
;       header as the block key.

        SaveReg <CX,DX,FS,GS>
        NullContext <fs,gs>,ax          ; Zero FS and GS before blocking
        MOV     AX,ES                   ; (AX:BX) = event identifier
        MOV     BX,OFFSET DOSGROUP:ReqPktHead
        MOV     CX,-1
        MOV     DI,CX                   ; (CX) = no timeout interval
        MOV     DH,1                    ; (DH) = not interruptible
        call    ProcBlock               ; Wait for wake up
        RestoreReg <GS,FS,DX,CX>
        JMP     SHORT arp1              ; See if a packet is available

arp2:   MOV     AX,ES:[BX]              ; (AX) = link from allocated packet
        MOV     ES:[DI],AX              ; Update head of list (either one.)
        CLC
        JMP     SHORT arp4

arp3:   STC                             ; Some error occurred

arp4:   STI                             ; Done with list
        POP     DI
        POP     AX
        RET
EndProc   dh_AllocReqPacket



;***    dh_FreeReqPacket - Free request packet
;
;       NOTE    There are two tiled request packet pools.  One is used
;               for all non-swapper requests.  The other is reserved, and
;               only used for, swapper requests.  Doing this solves a certain
;               very important deadlock case (deadlocks involving the swapper),
;               but does not solve all deadlock cases.
;               There is an additional request packet used in real mode only,
;               we don't deal with it here.
;
;       ENTRY   (ES:BX) = tiled request packed
;
;       EXIT    (CF) = 0, packet freed
;               (CF) = 1, (ES:BX) was not an allocated packet
;
;       USES    Flags
;
;               FS and GS must be preserved (DevHelp requirement)
;
;       NOTE    The packet checking code is closely related to, though
;               slightly weaker than, the code in DevDone.  Examine
;               that code when changing this code.
;
        ASSUME  DS:NOTHING,ES:NOTHING,SS:TASKAREA

Procedure dh_FreeReqPacket,<near,faronly,far16>
        SaveReg <AX,BX,DI>
        MOV     AX,ES                   ; (AX) = caller's packet segment
        CMP     AX,DOSGROUP             ; Are the segments the same?
        JNE     frp4                    ; Segments don't match, not our packet!
   ASSUME   ES:DOSGROUP

;   Packet in static pool?

        CMP     BX,OFFSET DOSGROUP:FirstPacket
        JB      frp4                    ; Offset too small, not in static pool
        CMP     BX,OFFSET DOSGROUP:LastPacket
        JAE     frp4                    ; not in static pool

frp0b:
IFDEF   DEVSTRICT
        SaveReg <DI>                    ; Check if the req packet on free list
        MOV     DI,OFFSET DOSGROUP:ReqPktHead
frp1:   MOV     DI,ES:[DI]              ; (DI) = next entry in free list
        CMP     BX,DI                   ; is this entry already in the list?
        JE      frp4
        OR      DI,DI                   ; end of linked list?
        JNZ     frp1
        RestoreReg <DI>
ENDIF
        CLI                             ; Don't let list change!
        MOV     DI,OFFSET DOSGROUP:ReqPktHead
        CMP     BX,OFFSET DOSGROUP:SwapFirstPacket
        JB      frp1a
        MOV     DI,OFFSET DOSGROUP:SwpReqPktHead

;       (ES:DI) -> ReqPktHead OR SwpReqPktHead, as appropriate

frp1a:  MOV     AX,ES:[DI]              ; (AX) = offset of first packet
        MOV     ES:[BX],AX              ; Set link in freed packet
        MOV     ES:[DI],BX              ; Update head of list

        OR      AX,AX                   ; If AX is zero then wake up AllocReqPac
        JNZ     frp2
        MOV     AX,ES                   ; (AX:BX) = event identifier

;       Both Normal and Swapper requests run (and block) using the Normal
;       header as the block key.

        MOV     BX,OFFSET DOSGROUP:ReqPktHead
        SaveReg <CX,DX,FS,GS>
        call    ProcRun                 ; Wake up any waiters
        RestoreReg <GS,FS,DX,CX>

frp2:   CLC                             ; Indicate success

frp3:   STI                             ; Done with list
        RestoreReg  <DI,BX,AX>
        RETP

frp4:   STC                             ; Indicate failure

IFDEF   DEVSTRICT
;       InternalError <FreeReqPacket: bad packet address> 58515
ENDIF
        JMP     SHORT frp3
EndProc   dh_FreeReqPacket


;***    FreeReqPacket - Free request packet
;
;       ENTRY   (ES:BX) = request packet
;
;       EXIT    NONE
;
;
;       USES    Flags
;
        ASSUME  DS:NOTHING,ES:NOTHING,SS:TASKAREA

Procedure FreeReqPacket,<near,faronly,far16>
        push    bp
        push    eax
        mov     bp,CurrTCB              ; (SS:BP) = current TCB
        mov     ax,es
        shl     eax,16
        mov     ax,bx                   ; (EAX) = packet address
        cmp     eax,[bp].TCBReqPkt      ; Are we freeing the TCB packet?
        jnz     short frpt10            ;  no, skip ahead
ifdef DEVSTRICT
        test    [bp].TCBReqPktFlg,TK_RP_INUSE ; Is packet busy?
        jz      short frptie            ;  no, internal error
endif
        and     [bp].TCBReqPktFlg,NOT TK_RP_INUSE ; Clear the busy flag
        jmp     short frpt30            ; Done

frpt10: SaveReg <ds>
        DosContext      DS
        mov     ax,es
        cmp     ax,[s2BmpSel]           ; was this allocated out of strat2 bmp ?
        jne     frpt20
ASSUME  DS:NOTHING
        push    di
        mov     di,bx                   ; es:di -> req packet
        CALLFAR Bmp_Free
        pop     di

                                        ;If there's a thread waiting    ;b720100
        cmp     ds:BMPWait, 0           ;in AllocReqPacketWait() for    ;b720100
        jz      frpt25                  ;a BMP_Alloc(), then ProcRun(). ;b720100
        mov     ds:BMPWait, 0                                           ;b720100
        SaveReg <bx>                                                    ;b720100
        mov     ax, ds                                                  ;b720100
        mov     bx,OFFSET DOSGROUP:BMPWait                              ;b720100
        call    ProcRun                                                 ;b720100
        RestoreReg <bx>                                                 ;b720100
        jmp     short frpt25            ; all done                      ;b720100

; strat1 req packet - either from the static or swapper pool
frpt20: call    dh_FreeReqPacket        ; Free the packet
frpt25: RestoreReg <ds>                                                 ;b720100

frpt30: pop     eax
        pop     bp
        RETP

ifdef DEVSTRICT
frptie: Panic   <"FreeReqPacket: packet not in use">
endif

EndProc   FreeReqPacket


;***    SetRead - Set up a device request header
;***    SetRdNA - Set up the given device request header
;
;       A tiled device request header is allocated and filled in
;       appropriately for a device read request.  The request
;       header must be deallocated by the caller after he's
;       done with it.
;
;       SetRdNA does not allocate the request packet
;
;       ENTRY   DS:BX = Transfer Address
;               CX = Record Count
;               DX = Starting Record
;               AH = Media Byte
;               AL = Unit Code
;       for SetRdNA only:
;               SI = non zero
;               ES:DI = request packet
;
;       EXIT    ES:BX Points to request packet
;
;       USES    BX, ES, Flags

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

Procedure SetRead,HYBRID

        xor     si,si
Entry   SetRdNA
        PUSH    DI
        PUSH    CX
        PUSH    AX
        MOV     CL,CMDINPUT
setrd0: or      si,si                   ;
        jnz             setrd2          ; don't allocate req. packet
setrd1:                                 ; SetWrite Entry point
        PUSH    BX
        call    AllocReqPacketWait      ; (es:bx) = tiled request packet
        MOV     DI,BX
        POP     BX
;
; common point for both SetRead and SetReadNoAlloc
; CL : CMD
; ES:DI -> tiled request packet
;
setrd2:
        MOV     AL,(size Packet) + LENIO
        STOSB                           ; length
        POP     AX
        STOSB                           ; Unit
        PUSH    AX
        MOV     AL,CL
        STOSB                           ; Command code
        XOR     AX,AX
        STOSW                           ; Status
        ADD     DI,8                    ; Skip link fields
        POP     AX
        XCHG    AH,AL
        STOSB                           ; Media byte
        XCHG    AL,AH
        PUSH    AX
        MOV     AX,BX
        STOSW                           ; Transfer addr (low)
        MOV     AX,DS
        STOSW                           ; Transfer addr (high)
        POP     CX                      ; Real AX
        POP     AX                      ; Real CX
        STOSW                           ; Count
        XCHG    AX,DX                   ; AX=Real DX, DX=real CX, CX=real AX
        STOSW                           ; Start low word
        MOV     BX,AX                   ; BX = Real DX
        XOR     AX,AX                   ; high work of start sector is zero
        STOSW                           ; Start high word
        MOV     AX,CX                   ; AX = Real AX
        MOV     CX,DX                   ; CX = Real CX
        MOV     DX,BX                   ; DX = Real DX
        LEA     BX,[DI-(IOstart+4)]     ; BX = start of request packet
        POP     DI
        RET


;***    SetWrite - Set up a device request header
;***    SetWrNA - Set up the given device request header
;
;       A tiled device request header is allocated and filled in
;       appropriately for a device write request.  The request
;       header must be deallocated by the caller after he's
;       done with it.
;
;       ENTRY   DS:BX = Transfer Address
;               CX = Record Count
;               DX = Starting Record
;               AH = Media Byte
;               AL = Unit Code
;       for SetWrNA only:
;               SI = non zero
;           ES:DI = request packet
;
;       EXIT    ES:BX Points to request packet
;
;       USES    BX, ES, Flags

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

Entry   SetWrite,HYBRID
        xor     si,si
Entry   SetWrNA
        PUSH    DI
        PUSH    CX
        PUSH    AX
        MOV     CL,CMDOUTPUT
        ADD     CL,[VerFlg]
        jmp     short setrd0

EndProc SetRead

;***    SetReadNoAlloc  - Set up the given device request header
;
;               set up the given request packet for device read
;
;       ENTRY   DS:BX = Transfer Address
;               CX = Record Count
;               DX = Starting Record
;               AH = Media Byte
;               AL = Unit Code
;               ES:DI = request packet
;
;       EXIT    ES:BX Points to request packet
;
;       USES    BX, ES, Flags

Procedure SetReadNoAlloc,faronly
        call    SetRdNA
        ret
EndProc SetReadNoAlloc

;***    SetWriteNoAlloc - Set up the given device request header
;
;               set up the given request packet for device Write
;
;       ENTRY   DS:BX = Transfer Address
;               CX = Record Count
;               DX = Starting Record
;               AH = Media Byte
;               AL = Unit Code
;               ES:DI = request packet
;
;       EXIT    ES:BX Points to request packet
;
;       USES    BX, ES, Flags

Procedure       SetWriteNoAlloc,faronly
        call    SetWrNA
        ret
EndProc SetWriteNoAlloc

;**     NULdev - Entry points for NUL device
;
;       Two entry points are defined; sNULdev for the strategy
;       routine and iNULdev for the `interrupt' routine.
;
;       ENTRY   ES:BX   Request header
;       EXIT    The request is marked done.
;       USES    - NONE -
;
        ASSUME  DS:NOTHING,ES:NOTHING,SS:TASKAREA

Procedure sNULdev,far
        or      es:[bx].PktStatus,STDON ; Set done bit
Entry   iNULdev,far
        ret
EndProc sNULdev

;**     sCONdev - Entry points for CON pseudo device
;
;       One entry point is defined sCONdev for the strategy
;       routine.  We mark the packet as complete with error code
;       BAD CMD
;
;       ENTRY   ES:BX   Request header
;       EXIT    NONE
;       USES    - NONE -
;
;
        ASSUME  DS:NOTHING,ES:NOTHING,SS:TASKAREA

Procedure sCONdev,far
        mov     es:[bx].PktStatus,STDON + STERR + ERROR_I24_BAD_COMMAND
Entry   iCONdev,far
        ret
EndProc sCONdev

CODE    ENDS


DATA    SEGMENT

; Used only at initialization

        public iDevCall
                EVEN
iDevCall        Packet  <>
                db      LENInit dup (?)

        public iRMNCall
                EVEN
iRMNCall        Packet  <>
                db      LENGenIOCTL dup (?)

                EVEN
DPBArea         dd      0               ; pointer to DPB area

                EVEN
ResourceSYSName db      "RESMGR$ "

DATA    ENDS

        FARCODE LOW
CODE    SEGMENT

;***    InitDeviceRes - Initialize a base BIOS device driver
;***    InitDevice - Initialize a BIOS device driver
;
;       The routine sets up a initialization request packet and
;       calls the device driver (thru DevIOCall2).
;
;       ENTRY:  (DS:SI) = addr of device header
;               (ES) = DOSGROUP segment
;               (CX:DX) = address of parameter list
;
;       EXIT:   NONE
;
;       USES:   AX, Flags
;
;       NOTE:   This is in low code because it's called by
;               the real mode 3xBox device loader.
;
    ASSUME      DS:NOTHING,ES:DOSGROUP,SS:NOTHING

Procedure InitDeviceRes,faronly
        mov     ah,PktHeadSize + LENInitRes
        mov     al,CMDInitBase
        call    id00
        ret
EndProc   InitDeviceRes

Procedure InitDevice

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

        mov     ah,PktHeadSize + LENInit
        mov     al,CMDInit
id00:   push    bx
        mov     bx,offset DOSGROUP:iDevCall
        mov     es:[bx].PktCmd,al
        mov     es:[bx].PktLen,ah
        mov     es:[bx].PktUnit,0

;       Pass the address of the DevHlp dispatcher to driver

        push    ds
        DosContext  ds
        push    ax
        mov     ax, offset h_DeviceHelpDM
        mov     es:[bx].InitpEnd.Offst,ax
        mov     ax, CODE
        mov     es:[bx].InitpEnd.Segmt,ax       ; Pass devhlp addr to device
        pop     ax
        pop     ds
        assume ds:nothing

        mov     al,NumIO
        mov     es:[bx].Initdrv,al              ; Set next block dev. number

;       If a resident device driver, also pass os2ldr's data selector

        cmp     ah,PktHeadSize + LENInitRes
        jnz     id50
        mov     es:[bx].InitSysiData,GSEL GDT_OS2LDR ; data selector

;       and the parameter list

        mov     es:[bx].InitParms.Segmt,dx
        mov     es:[bx].InitParms.Offst,cx

id50:   mov     es:[bx].PktStatus,0             ; initialize status
        call    DevIOCall2                      ; Initialize the device
        pop     bx
        ret
EndProc   InitDevice

;
; RMNotifyWork - Worker to notify Resource Manager device driver
;   Input:
;       AX = IOCTL function
;       ES:DI = Pointer to parameter packet
;       DS:SI = Pointer to data packet
;   Output:
;       AX      = IOCtl Status/return code
;       DS:SI   = IOCtl Request Packet
;
;   Uses:
;       AX, BX, ES
;
Procedure RMNotifyWork,faronly

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

        push    ds
        push    si
        push    es
	push    di
;148753        push    bx
        DosContext  es
        mov     bx,offset DOSGROUP:iRMNCall
        mov     es:[bx].PktCmd,CMDGenIOCTL
        mov     es:[bx].PktLen,PktHeadSize+LENGenIOCTL
        mov     es:[bx].PktUnit,0
        mov     es:[bx].GIOCategory,80h
        mov     es:[bx].GIOFunction,al
;148753        mov     word ptr es:[bx].GIOParaPack,dx
;148753        mov     word ptr es:[bx].GIOParaPack+2,cx

        pop     word ptr es:[bx].GIOParaPack	;148753
        pop     word ptr es:[bx].GIOParaPack+2  ;148753

        pop     word ptr es:[bx].GIODataPack    ;148753
        pop     word ptr es:[bx].GIODataPack+2  ;148753
        mov     es:[bx].PktStatus,0             ; initialize status

        DosContext  ds
        lea     di,ResourceSYSName
        CALLFAR w_CallPddIoctl
        mov     ax, es:[bx].PktStatus           ; retreive IOCtl return code

;148753        pop     bx
;148753        pop     di
;148753        pop     si
;148753        pop     es
;148753        pop     ds
        ret
EndProc   RMNotifyWork

CODE    ENDS

; *******************************  B731797  Begin **********************
           FARCODE HIGH32
HIGH32CODE segment

;***    DevWOHandle   - Call disk drivers strat routine without a handle
;
;       Calls 16 bit worker (w_DevWOHandle) from this 32 bit segment
;       allowing 32 bit routines access to the 16 bit worker
;
;

Procedure  DevWOHandle,<far>
;       PMONLY

        assume  ds:nothing,es:nothing,ss:TASKAREA

        CALL16   w_DevWOHandle
        ret

EndProc DevWOHandle

HIGH32CODE ends
; *******************************  B731797  End   **********************

        END
