SCCSID  equ     "@(#)devphys.asm        6.9 92/03/11"
        PAGE    ,132                                       ; (CTRL-OH)   IBM PC PRINTER CONDENSED MODE
        TITLE   DevPhys - Device Manager Physical Disk Routines
;
;
;   Source File Name: DEVPHYS.ASM                Status: Version 0.0
;
;   Descriptive Name: Device Manager Physical Disk Routines
;
;   Function:   This source file contains all the Device Manager
;               routines to handle the DosPhysicalDisk command and
;               DosDevIOCtl Category 9 Lock and Unlock Physical Drive
;               commands.
;
;   Entry Points:
;               w_PhysicalDisk          BuildPhysDiskTable
;               DevLockPhysDrive        DevReturnHandle
;               DevUnlockPhysDrive      DevReleaseHandle
;               InitDeviceMgr           LockPhysDrv
;               SetDPBRemovFlag         UnLockPhysDrv
;
;   External References:
;               SuWord                  SFFromHandle
;               FuBuff                  DoOpenCreate
;               LockDrv                 w_Close
;               UnLockDrv               InitBaseDDs
;               InitReqPacket           AllocReqPacketWait
;               FreeReqPacket           Init3xboxIoctl
;                                       DevIOCall2
;
;
;   In order to support utilities like FDISK that need to access the
;   whole PHYSICAL disk regardless of what partitions are on the disk,
;   we need to have support for locking/unlocking the whole disk. This
;   ensures that the layout of the disk will not change underneath an
;   existing application that may be running at the time.
;   The particular call that is used for this is DOSPHYSICALDISK. The
;   call is only relevant for partitionable disks.
;
;   In the kernel, we need to keep information on the total number of
;   partitionable disks, and which device driver manages the specific
;   disks. We also keep a bitmap of the logical units on the physical
;   disk.
;
;   The information is kept in a table (PhysDiskTable) that is pointed to
;   by the entry in PhysDiskTblPtr.
;
;   The layout of the table is as follows:
;
;                   --------------------------
;                   |  Header      (4 BYTES) |
;                   --------------------------
;                   |  Entry for disk 1      |
;                   --------------------------
;                   |  Entry for disk 2      |
;                   --------------------------
;                   .                        .
;                   .                        .
;                   --------------------------
;                   |  Entry for disk n      |
;                   --------------------------
;
;   Each entry is of the type PhysDiskTblEntry. We allow a maximum of
;   26 physical disks to be supported (this is because the maximum number
;   of logical drives in the system is restricted by the number of DPBs
;   that we can have (which is 26), and we could conceivable have one
;   physical disk for each logical disk.
;
;   The PhysDiskTable is stored in the same segment as the DPB. We initially
;   allocate the maximum possible sized segment (26 DPBs and table with
;   entries for 26 physical disks), and then move the PhysDiskTable down
;   to just after the last DPB. We then re-allocate the segment to shrink it
;   down to size.
;
;   The segment is laid out as follows:
;
;                       --------------------------
;   DPBHead ----------> |                        |
;                       |       Area for         |
;                       |         DPBs           |
;                       |                        |
;                       |                        |
;   LastDPB ----------> |                        |
;                       --------------------------
;   PhysDiskTablePtr -> |                        |
;                       |       Area for         |
;                       |       PhysDiskTable    |
;                       |                        |
;                       --------------------------
;
;***********************************************************************
;* Change log                                                          ;
;*  72417 MLM        11/18/95                                          ;
;*          Added tkexport.inc since most of dirent.inc was moved to it;
;*          as part of FSCRIT serialization.                           ;
;***********************************************************************
;

.xlist
include basemaca.inc
include osmaca.inc
include fnodev.inc
include dosseg.inc
include dossymc.inc
include curdir.inc
include tkexport.inc                            ;72417 added
include dirent.inc
include devsym.inc
include dpb.inc
include error.inc
include filemode.inc
include mhandle.inc
include mi.inc
include opncreat.inc
include pmode.inc
include sas.inc
include sft.inc
include struc.inc
include osconst.inc
include pgexport.inc
include vmexport.inc
include argframe.inc

.list

        ModName , FNO_DEVPHYS_ASM
        CpuMode 386

        EXTRNFAR        AllocReqPacketWait
        EXTRNFAR        DevIOCall2
        EXTRNFAR        DoOpenCreate
        EXTRNFAR        FreeReqPacket
        EXTRNFAR        FuBuff
        EXTRNFAR        LockDrive
        EXTRNFAR        SegRealloc
        EXTRNFAR        SFFromHandle
        EXTRNFAR        SuWord
        EXTRNFAR        UnlockDrive
        EXTRNFAR        w_Close


        EXTRN           _SELAllocKHB:near
        EXTRN           _SELReallocKHB:near
        extrn           BaseDevInit:near
        extrn           InitReqPackets:near
        extrn           InitBaseDDs:near

        ReferTask       Pid, WORD

        ReferGlobal     CONdev,DWORD
        ReferGlobal     DPBHead,DWORD     ; long pointer to DPB chain
        ReferGlobal     LastDPB,DWORD     ; address of last DPB
        ReferGlobal     NumIO,BYTE        ; number of physical drives
        ReferGlobal     OEMdev,DWORD      ; OEMHLP dd header
        ReferGlobal     SAS_dd_area,BYTE  ; SAS dd section
        ReferGlobal     DevHead,DWORD

DATA segment

; PhysDiskTbl Equates
;


;
; We can support a maximum of 26 physical partitionable disks. This can never
; be bigger since we only allow 26 logical disks in the system.
;
MAX_NUM_PART_DISKS      EQU    128      ; Equate for max. number of disk entries
                                        ;  in PhysDiskTbl
DEV_OPENMODE = open_no_inherit or open_autofail or sharing_deny_both
                                        ; Used for getting a SFT entry
DEV_OPENFLAG = exist_open               ; Used for getting a SFT entry


; PhysDiskTbl - Physical Disk Entry Flag EQUATES
;
                                        ;    1     /   0
DEV_LOCK        EQU  01h                ; Locked   / Unlocked
DEV_LOGUNITS    EQU  02h                ; Logical Units Exist / No Logical Units


; Equates for Device Driver Commands Packet Length
;
PARTFIXEDDISKS_PKTLENGTH EQU 18         ; Equate for the packet length of
                                        ;  Partitionable Fixed Disks

GETFD_LOGUNITS_PKTLENGTH EQU 21         ; Equate for the packet length of
                                        ;  Get Fixed Disk/Logical Unit Map

; General Flags and Equates
;
NO                      EQU     0
YES                     EQU     1

FIRST_TIME              EQU     0

PDF_QDISKCOUNT          EQU     1
PDF_GETHANDLE           EQU     2
PDF_FREEHANDLE          EQU     3

RETURN_BUFFER_LENGTH    EQU     2
INPUT_BUFFER_LENGTH     EQU     2

MIN_STRING_LENGTH       EQU     3
MAX_STRING_LENGTH       EQU     4


; PhysDiskTblHdr - Header of physical disk table
;
PhysDiskTblHdr  struc
    pdth_tbllen     DW      0           ; Length of PhysDiskTbl
    pdth_reserved   DB      0           ; Reserved
    pdth_totaldisks DB      0           ; Total number of Partitionable Disks
PhysDiskTblHdr  ends


; diskentry  structure declared in DEVSYM.INC for use by other Device Manager
; files.

;
; The maximum size of PhysDiskTable is what would be used for the maximum
; number of physical disks.
;
MAXPHYSDISKTBLSIZE      EQU     size PhysDiskTblHdr + MAX_NUM_PART_DISKS * size diskentry


; PhysDiskTablePtr - Pointer to physical disk table
;
public PhysDiskTablePtr                                                   ;whs
PhysDiskTablePtr    DD      0

size_entry          db      size diskentry  ; PhysDiskTable entry size
init_devmgr_flag    db      0
input_buffer        db      4 DUP (0)

DATA ends

          FARCODE HIGH4
FSSWAPCODE SEGMENT
        PUBLIC  _StartPHYSDSKGROUP
_StartPHYSDSKGROUP      label byte
;/********************** START OF SPECIFICATIONS *********************/
;/*                                                                  */
;/* SUBROUTINE NAME: w_PhysicalDisk                                  */
;/*                                                                  */
;/* DESCRIPTIVE NAME: Worker routine for Partitionable Disk Support  */
;/*                                                                  */
;/* FUNCTION: Provide Information and access to partitionable media  */
;/*                                                                  */
;/* NOTES:  The handle returned for the specified partitionable      */
;/*         disk can ONLY be used with the Category 9 Generic IOCtl. */
;/*                                                                  */
;/* ENTRY POINT:  w_PhysicalDisk                                     */
;/*    LINKAGE:   Call Near if in High2code segment                  */
;/*                otherwise Call Far                                */
;/*                                                                  */
;/* INPUT: AX               = Type of information on the             */
;/*                           partitionable disk(s) to obtain:       */
;/*                            1 = Obtain the total number of        */
;/*                                partitionable disks               */
;/*                            2 = Obtain a handle to use with       */
;/*                                Category 9 IOCtls                 */
;/*                            3 = Release a handle for a            */
;/*                                partitionable disk                */
;/*        [SS:BX]          =  Pointer to argframe on stack
;/*                                                                  */
;/* EXIT-NORMAL: Carry Clear                                         */
;/*              Partitional Disk Information returned               */
;/*                                                                  */
;/* EXIT-ERROR:  Carry Set                                           */
;/*              AX = ERROR_INVALID_FUNCTION                         */
;/*                 = ERROR_INVALID_PARAMETER                        */
;/*                   ERROR_INVALID_HANDLE                           */
;/*                   ERROR_ACCESS_DENIED                            */
;/*                                                                  */
;/* EFFECTS:  AX, BX, CX, DX, SI, DI, DS, ES, Flags                  */
;/*                                                                  */
;/* INTERNAL REFERENCES:                                             */
;/*    ROUTINES:  DevReturnHandle                                    */
;/*               DevReleaseHandle                                   */
;/*                                                                  */
;/*    DATA AREA:  PhysDiskTable                                     */
;/*                                                                  */
;/* EXTERNAL REFERENCES:                                             */
;/*    ROUTINES:   CodeLockProc,CodeUnlockProc                       */
;/*    DATA AREA:   SFT                                              */
;/*********************** END OF SPECIFICATIONS **********************/

Procedure w_PhysicalDisk,<faronly,far16>

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

LocalVar        wpd_physdskargs,WORD

        EnterProc       <bx>

        cmp     ax,PDF_QDISKCOUNT
        jne     wpd20

; GET NUMBER OF PARTITIONABLE DISKS FUNCTION

        mov dx,TASKAREA:[bx].dpd_RetBufPtr.Segmt ; dx = selector value
        and dl,RPL_CLR                  ; If the selector is zero, the 286
                                        ;  could put in the rpl level

        or      dx,TASKAREA:[bx].dpd_RetBufPtr.Offst
        je      wpd90                   ; and if Input Pointer is null

        cmp     TASKAREA:[BX].dpd_RetBufLen,RETURN_BUFFER_LENGTH
                                        ; Is Return Buffer Length big enough
        jne     wpd90                   ; for Number of partitionable disks?


        Test    TASKAREA:[bx].dpd_UsrInfoLen,-1
        jnz     wpd90           ; and Input Parm length = 0

        ; make sure pointer is NULL
        mov     dx,TASKAREA:[bx].dpd_UsrInfoPtr.Segmt
        and     dl,RPL_CLR              ; Remove the rpl information
        or      dx,TASKAREA:[bx].dpd_UsrInfoPtr.Offst
        jnz     wpd90

; Return the total number of Partitionable disks

       DosContext ds
       lds si,PhysDiskTablePtr          ; DS:SI -> PhysDiskTable
   ASSUME DS:NOTHING

        mov     al,ds:[si].pdth_totaldisks      ; Setup for SuWord
        xor     ah,ah                   ; clear ah
        mov     di,TASKAREA:[bx].dpd_RetBufPtr.Offst
        mov     bx,TASKAREA:[bx].dpd_RetBufPtr.Segmt

       CALLFAR SuWord                   ; (OUT: Carry and AX)

wpdx:   LeaveProc
        ret

wpd20:  cmp     ax,PDF_GETHANDLE
        jne     wpd40

; GET A HANDLE FOR DEVIOCTL CAT 9 USAGE FUNCTION

        cmp     TASKAREA:[bx].dpd_RetBufLen, RETURN_BUFFER_LENGTH
        jne     wpd90

        mov     cx, TASKAREA:[bx].dpd_UsrInfoLen
        cmp     cx,MIN_STRING_LENGTH
        jl      wpd90

        cmp     cx,MAX_STRING_LENGTH    ;; ASCIIZ string has a valid length
        jg      wpd90


        PUSH    dword ptr TASKAREA:[bx].dpd_UsrInfoPtr
        push    dword ptr TASKAREA:[bx].dpd_RetBufPtr
        mov     bx,TASKAREA:[bx].dpd_RetBufLen

;       (TOS+6) =  Pointer to ASCIIZ string that specifies the
;                 partitionable disk
;       CX      =  Length of ASCIIZ string
;       (TOS+2) =  Pointer to the return buffer
;       BX      =  Length of return buffer

        call DevReturnHandle
        jmp     short   wpdx            ;


wpd40:  cmp     ax,PDF_FREEHANDLE
        jne     wpd80                   ; invalid function

; RELEASE THE HANDLE FOR PHYSDISK FUNCTION

        mov     dx,TASKAREA:[bx].dpd_RetBufPtr.Segmt
                                        ; dx = selector value
        and dl,RPL_CLR                  ; If the selector is zero, the 286
                                        ;  could put in the rpl level

        mov     cx,TASKAREA:[bx].dpd_UsrInfoLen
        cmp     cx,INPUT_BUFFER_LENGTH  ;
        jne     wpd90                   ;
;
        or      dx,TASKAREA:[bx].dpd_RetBufPtr.Offst
        jnz     wpd90                   ; and if output Pointer is null

        Test    TASKAREA:[bx].dpd_RetBufLen,-1
        jnz     wpd90           ; and Input Parm length = 0

        mov     si,TASKAREA:[bx].dpd_UsrInfoPtr.Offst
        mov     bx,TASKAREA:[bx].dpd_UsrInfoPtr.Segmt

; bx:si = ptr to user info  (ptr to the handle)
; cx = length of user info

        call    DevReleaseHandle
        jmp     wpdx


wpd90:  mov     ax,ERROR_INVALID_PARAMETER
        stc
        jmp     wpdx


; not a valid function no

wpd80:  mov ax,ERROR_INVALID_FUNCTION
        stc
        jmp     wpdx

EndProc w_PhysicalDisk

;
;/********************** START OF SPECIFICATIONS *********************/
;/*                                                                  */
;/* SUBROUTINE NAME:  DevLockPhysDrive                               */
;/*                                                                  */
;/* DESCRIPTIVE NAME:  Lock Physical Drive                           */
;/*                                                                  */
;/* FUNCTION:  Lock the Physical Drive to prevent other processes    */
;/*            from accessing the physical drive.                    */
;/*                                                                  */
;/* NOTES:                                                           */
;/*                                                                  */
;/* ENTRY POINT: DevLockPhysDrive                                    */
;/*    LINKAGE:   Call Near                                          */
;/*                                                                  */
;/* INPUT:   BX = user handle                                        */
;/*                                                                  */
;/* EXIT-NORMAL:  Carry Flag Clear                                   */
;/*                                                                  */
;/* EXIT-ERROR:   Carry Flag Set                                     */
;/*               AX =  ERROR_INVALID_HANDLE                         */
;/*                     ERROR_ACCESS_DENIED                          */
;/*                     ERROR_LOCK_VIOLATION                         */
;/*                                                                  */
;/* EFFECTS:  Carry and AX is modified                               */
;/*                                                                  */
;/* INTERNAL REFERENCES:                                             */
;/*    ROUTINES:   LockPhysDrv                                       */
;/*                                                                  */
;/*    DATA AREA:  PhysDiskTable                                     */
;/*                                                                  */
;/* EXTERNAL REFERENCES:                                             */
;/*    ROUTINES:                                                     */
;/*                DevIOCall2                                        */
;/*                SFFromHandle                                      */
;/*                AllocReqPacketWait                                */
;/*                FreeReqPacket                                     */
;/*                                                                  */
;/*    DATA AREA:                                                    */
;/*                                                                  */
;/*********************** END OF SPECIFICATIONS **********************/

Procedure DevLockPhysDrive,HYBRID

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

 LocalVar entry_ptr,DWORD       ; Establish Local Variables on the Stack
 EnterProc                      ; Save Space on the stack for above

; Save registers used
   SaveReg <bx,dx,di,si,ds,es>

; Get the associated SFT entry

  CALLFAR       SFFromHandle            ; (IN  BX = user_handle
                                ;  OUT  ES:DI = SFT_ptr)
                                ; **********************
                                ; * ES:DI -> SFT entry *
                                ; **********************

  .if nc  near      ; User Handle is a valid SFT entry

;   Continue processing if SFT entry is a Physical Disk entry

    .if <bit  es:[di].sf_fsi.sfi_mode2 and open2_phys_disk>  near    ; NZ

       ; Get the pointer to the PhysDiskTable entry from the SFT

;       entry_ptr = SFT_ptr.sf_devptrh:SFT_ptr.sf_devptrl

        mov ax,word ptr es:[di].sf_devptr        ; Get the Offset
        mov entry_ptrl,ax                        ; Save for later use
        mov si,ax                                ; Setup offset for addressing
        mov ax,word ptr es:[di].sf_devptr + 2    ; Get the Selector
        mov entry_ptrh,ax                        ; Save for later use
        mov ds,ax                                ; Setup Selector for addressing
                                ; ********************************
                                ; * DS:SI -> Physical Disk Entry *
                                ; ********************************

;      ********************************************
;      * Check if Caller had previously opened it *
;      ********************************************

;      ************************************************************************
;      * Get addressability to the ptda (ss points to it, make the assembler  *
;      * think that its the TASKAREA since the PTDA isn't a struc)            *
;      *************************************************************************

        mov ax,Pid
        .if <ax eq ds:[si].devpid>  near

          ; Continue Processing

;         Check if the physical disk is already locked

          .if <bit ds:[si].deveflags nand DEV_LOCK>   ; Z

;            *****************************************
;            * Physical disk is not locked, Continue *
;            *****************************************

;            ********************************************************************
;            * See if there are any logical units attached to the physical disk *
;            * by calling device command Get Fixed Disk/Logical Unit Map        *
;            ********************************************************************

             CALLFAR AllocReqPacketWait            ; Allocate a request packet
                                                   ; (OUT  ES:BX = request_packet)
                                                   ; ***************************
                                                   ; * ES:BX -> Request Packet *
                                                   ; ***************************

             ; Set up Request packet for Get Fixed Disk/Logical Unit Map
             ; command for current device driver

             mov es:[bx].PktCmd,CMDGetfd_logunitsmap                   ; Command #
             mov byte ptr es:[bx].PktLen,GETFD_LOGUNITS_PKTLENGTH      ; Packet length
             mov es:[bx].PktStatus,0                                   ; Initialize status
             mov al,ds:[si].reldisknum                                 ; Fill in which disk
             mov es:[bx].PktUnit,al                                    ; you want info for

             ; Get the device driver header from PhysDiskTable entry
             lds si,ds:[si].ddheader                  ; *********************************
                                                      ; * DS:SI -> Device Driver Header *
                                                      ; *********************************

             ; Send device driver command Get Fixed Disk/Logical Unit Map

             CALLFAR DevIOCall2            ; (IN  DS:SI = dd_header_ptr,
                                           ;      ES:BX = request_packet

;            * Save Bit Map

             mov ax,word ptr es:[bx].PktData       ; Get Bit Map
             mov dx,word ptr es:[bx].PktData + 2

;            * Free Request Packet

             CALLFAR FreeReqPacket         ; IN ES:BX = request_packet

;            ************************************
;            * Check if any logical units exist *
;            ************************************

             .if <zero ax> and
             .if <zero dx>

                ; No logical units exist, lock the disk

                lds si,entry_ptr                 ; ********************************
                                                 ; * DS:SI -> Physical Disk Entry *
                                                 ; ********************************
                or ds:[si].deveflags,DEV_LOCK

             .else       ; Logical units exist, do more processing


;               **********************************************************
;               * Call File System to lock all logical units on the disk *
;               **********************************************************

                mov bx,ax

                SaveReg <bx,cx,dx,di,es>
                call LockPhysDrv              ; (IN  ds:si = dd_header_ptr,
                                              ;    dx:bx = Logical Unit Bit Map
                                              ;  OUT carry flag )
                RestoreReg <es,di,dx,cx,bx>

                .if nc

;                  *********************************************
;                  * Copy Bit Map into the Physical Disk Entry *
;                  *********************************************

                   lds si,entry_ptr                ; ********************************
                                                   ; * DS:SI -> Physical Disk Entry *
                                                   ; ********************************
                   mov word ptr ds:[si].logunitbitmap,bx
                   mov word ptr ds:[si].logunitbitmap + 2,dx

;                  ***********************************************
;                  * Set Locked and Logical Units Exist Flags on *
;                  ***********************************************

                   or ds:[si].deveflags,DEV_LOCK + DEV_LOGUNITS
                                                    ; carry flag clear by OR instr

                .else        ; Locking the logical units failed, return failure

                   mov ax,ERROR_LOCK_VIOLATION
                   stc                              ;set carry flag

                .endif      ; carry clear

             .endif   ; request_packet.PktData(1:4) EQ 0

          .else  near

           ; Caller is trying to lock the physical disk he had previously
           ; locked, return success

              clc                         ;Clear Carry flag

          .endif  ; entry_ptr.flags NE DEV_LOCK

        .else  near  ; Caller had not previously opened it

             mov ax,ERROR_ACCESS_DENIED
             stc                         ;set carry flag

        .endif ; caller's PID EQ PhysDiskTable_ptr.pid

    .else  near       ; SFT entry is not a Physical Disk entry

        mov ax,ERROR_INVALID_HANDLE
        stc                             ; set carry flag

    .endif            ; SFT entry is a Physical Disk entry

  .else near  ; User Handle is not a valid SFT entry

      mov ax,ERROR_INVALID_HANDLE
      stc                         ; set carry flag

  .endif

; Restore Registers used
  RestoreReg <es,ds,si,di,dx,bx>
  LeaveProc
  Ret

EndProc     DevLockPhysDrive


;/********************** START OF SPECIFICATIONS *********************/
;/*                                                                  */
;/* SUBROUTINE NAME:  DevUnlockPhysDrive                             */
;/*                                                                  */
;/* DESCRIPTIVE NAME:  Unlock Physical Drive                         */
;/*                                                                  */
;/* FUNCTION:  UnLock the Physical Drive to allow other processes to */
;/*            access the physical drive.                            */
;/*                                                                  */
;/* NOTES:                                                           */
;/*                                                                  */
;/* ENTRY POINT:  DevUnlockPhysDrive                                 */
;/*    LINKAGE:   Call Near                                          */
;/*                                                                  */
;/* INPUT:      BX = user handle                                     */
;/*                                                                  */
;/* EXIT-NORMAL:  Carry Flag Clear                                   */
;/*                                                                  */
;/* EXIT-ERROR:   Carry Flag Set                                     */
;/*               AX = ERROR_INVALID_HANDLE                          */
;/*                    ERROR_ACCESS_DENIED                           */
;/*                                                                  */
;/* EFFECTS:  Carry and AX is modified                               */
;/*                                                                  */
;/* INTERNAL REFERENCES:                                             */
;/*    ROUTINES:   UnLockPhysDrv                                     */
;/*                                                                  */
;/*    DATA AREA:  PhysDiskTable                                     */
;/*                                                                  */
;/* EXTERNAL REFERENCES:                                             */
;/*    ROUTINES:                                                     */
;/*                SFFromHandle                                      */
;/*                                                                  */
;/*    DATA AREA:  SFT                                               */
;/*                                                                  */
;/*********************** END OF SPECIFICATIONS **********************/

Procedure DevUnlockPhysDrive,HYBRID

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

; Save registers used
   SaveReg <bx,dx,di,es,ds,si>

; Get the associated SFT entry
                                            ; (IN  BX = user_handle
   CALLFAR      SFFromHandle                        ;  OUT  ES:DI = SFT_ptr)
                                            ; **********************
                                            ; * ES:DI -> SFT entry *
                                            ; **********************
   .if nc            ; User Handle is a valid SFT entry

;   Continue processing if SFT entry is a Physical Disk entry

    test es:[di].sf_fsi.sfi_mode2,open2_phys_disk
     .if nz

       ; Get the pointer to the PhysDiskTable entry from the SFT

        les di,es:[di].sf_devptr            ; ********************************
                                            ; * ES:DI -> Physical Disk Entry *
                                            ; ********************************

;      ********************************************
;      * Check if Caller had previously opened it *
;      ********************************************

;      ************************************************************************
;      * Get addressability to the ptda (ss points to it, make the assembler  *
;      * think that its the TASKAREA since the PTDA isn't a struc)            *
;      *************************************************************************

        mov ax,Pid
        .if <ax eq es:[di].devpid>

          ; Continue Processing

;         ************************************************
;         * Check if the physical disk is already locked *
;         ************************************************

          .if <bit es:[di].deveflags and DEV_LOCK>   ; NZ

;            *************************************
;            * Physical disk is locked, Continue *
;            *************************************

;            ************************************
;            * Check if any logical units exist *
;            ************************************

             .if <bit es:[di].deveflags nand DEV_LOGUNITS>   ; Z

                ; No logical units exist, unlock the disk

                mov es:[di].deveflags,0
                clc                       ; clear carry flag

             .else       ; Logical units exist, do more processing

                ; Get the device driver header from PhysDiskTable entry

                lds si,es:[di].ddheader ; *********************************
                                        ; * DS:SI -> Device Driver Header *
                                        ; *********************************

;               ************************************************************
;               * Call File System to unlock all logical units on the disk *
;               ************************************************************

                mov bx,word ptr es:[di].logunitbitmap   ; Get Bit Map
                mov dx,word ptr es:[di].logunitbitmap + 2
                SaveReg <bx,cx,dx,si,di,ds,es>
                call    UnlockPhysDrv   ; (IN  ds:si = dd_header_ptr,
                                        ;      dx:bx = entry_ptr.logunitbitmap)
                RestoreReg <es,ds,di,si,dx,cx,bx>

;               ********************************
;               * Clear all flags in the entry *
;               ********************************

                mov es:[di].deveflags,0
                clc                         ; clear carry flag

             .endif   ; entry_ptr.flags NE DEV_LOGUNITS

          .else

           ; Caller is trying to unlock the physical disk he had previously
           ; unlocked, return success

              clc                   ;Clear Carry flag

          .endif  ; entry_ptr.flags E DEV_LOCK

        .else  ; Caller had not previously opened it

             mov ax,ERROR_ACCESS_DENIED
             stc                      ; set carry flag

        .endif ; caller's PID EQ PhysDiskTable_ptr.pid

     .else             ; SFT entry is not a Physical Disk entry

        mov ax,ERROR_INVALID_HANDLE
        stc                     ; set carry flag

     .endif            ; SFT entry is a Physical Disk entry

   .else  ; User Handle is not a valid SFT entry

      mov ax,ERROR_INVALID_HANDLE
      stc                      ; set carry flag

   .endif

; Restore Registers used
  RestoreReg <si,ds,es,di,dx,bx>
  Ret

EndProc DevUnlockPhysDrive


;/********************** START OF SPECIFICATIONS *********************/
;/*                                                                  */
;/* SUBROUTINE NAME:  DevReturnHandle                                */
;/*                                                                  */
;/* DESCRIPTIVE NAME:  Return a handle for a partitionable disk      */
;/*                                                                  */
;/* FUNCTION:  Obtain a handle to use with Category 9 IOCtls         */
;/*                                                                  */
;/* NOTES:  The number portion of the ASCIIZ string corresponds      */
;/*         to an entry in the PhysDiskTable structure.              */
;/*                                                                  */
;/* ENTRY POINT:  DevReturnHandle                                    */
;/*    LINKAGE:   PUSH  @RetBuffer                                   */
;/*               PUSH  @UsrInfoBuffer                               */
;/*               call  DevReturnHandle
;/*                                                                  */
;/* INPUT:  (TOS+2)=  Pointer to ASCIIZ string that specifies the    */
;/*                   partitionable disk                             */
;/*         (TOS+6)=  Pointer to the return buffer                   */
;/*          CX    =  Length of ASCIIZ string                        */
;/*          BX    =  Length of return buffer                        */
;/*                                                                  */
;/* EXIT-NORMAL:   Carry Flag Clear                                  */
;/*                                                                  */
;/* EXIT-ERROR:    Carry Flag Set                                    */
;/*                AX =  ERROR_INVALID_PARAMETER                     */
;/*                      ERROR_ACCESS_DENIED                         */
;/*                                                                  */
;/* EFFECTS:  AX, BX, CX, DX, DI, SI, DS, ES, Flags are modified     */
;/*           POPS OFF THE INPUT ARGS FROM THE STACK                 */
;/*                                                                  */
;/* INTERNAL REFERENCES:                                             */
;/*    ROUTINES:                                                     */
;/*                                                                  */
;/*    DATA AREA:    PhysDiskTable                                   */
;/*                                                                  */
;/* EXTERNAL REFERENCES:                                             */
;/*    ROUTINES:     DoOpenCreate                                    */
;/*                  SFFromHandle                                    */
;/*                  FuBuff                                          */
;/*                  SuWord                                          */
;/*                                                                  */
;/*    DATA AREA:    SFT                                             */
;/*                                                                  */
;/*********************** END OF SPECIFICATIONS **********************/

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

Procedure DevReturnHandle,NEAR,LOCAL

ArgVar  drh_RetBufPtr,DWORD     ; ptr to Return buffer
ArgVar  drh_UsrInfoPtr,DWORD    ; ptr to user input buffer

   EnterProc                    ;

   DosContext es
   lea di,input_buffer          ; ES:DI - > Device Manager's buffer

   mov bx,drh_UsrInfoPtr.Segmt  ; BX:SI - > User's buffer
   mov si,drh_UsrInfoPtr.Offst

;  (BX:SI) -> pointer to source buffer
;  (ES:DI) -> target buffer
;  (CX)   =   number of bytes to copy

   SaveReg <cx,di,es>
   CALLFAR FuBuff
   RestoreReg <es,di,cx>

   .if nc  near

;  * Check ASCIIZ string for validity - Does it end with ":null" ?
;  * and convert number portion of string into a number

     mov ax,es                  ; Load string byte operation uses ds:si
     mov ds,ax
     mov si,di
     xor ax,ax                  ; AX holds the individual char in the string
     xor bx,bx                  ; BX holds the converted number

     .repeat

        lodsb                   ; Get a character from ds:si

        .if < AL eq ':'>        ; Is it a colon ?

          lodsb                 ;  Yes, get next character

          .if <AL ne 0>

;           *  Leave if a zero (null byte) does not follow the colon

            mov ax,ERROR_INVALID_PARAMETER
            stc
            jmp returnhandleret

          .else

            jmp validstring     ;  Found a ':0' - Processing finished

          .endif ; <AL ne 0>

        .endif         ; < AL EQ ':'>

        .leave  <al b '0'> or   ; Number portion has invalid value
        .leave  <al a '9'> or   ; Leave if not between 0 and 9
        .leave  <bx a 9>        ; Total so far is 10 or greater before it has
                                ;  even been raised to its proper range which
                                ;  would put it over 99
;       * Process character

        sub al,'0'              ; Strip off '3' in front of number
        imul bx,10              ; Multiple total so far by 10
        add bx,ax               ; (BX) = total so far + new digit

     .loop nz

;    * If you got here, a character is not a digit or there are more than 2 digits

      mov ax,ERROR_INVALID_PARAMETER
      stc
      jmp returnhandleret

;     * ASCIIZ string is valid and has been converted, Continue

validstring:
;     PhysDiskTable_entry = PhysDiskTable

      DosContext DS
      lds si,PhysDiskTablePtr              ; DS:SI -> PhysDiskTable
  ASSUME DS:NOTHING

     .if <bl A 0> and                      ; BX contains the converted value
     .if <bl BE ds:[si].pdth_totaldisks>

;       * Point Offset to the PhysDiskTable entry corresponding to the
;       * physical disk number

;       * Index = ((physdisknum - 1) * size_entry) + size PhysDiskTblHdr

        mov ax,bx               ; physdisknum
        dec ax
        mul size_entry
        add ax,size PhysDiskTblHdr
                                ; ******************
        add si,ax               ; * DS:SI -> entry *
                                ; ******************

;       **********************************************
;       * Check if Entry has not been opened already *
;       **********************************************

        .if <ds:[si].devpid eq 0>
;
;          * Set up a physical disk entry in the SFT

           SaveReg <bx,cx,dx,si,di,bp,ds,es>
           mov ah,attr_hidden+attr_system   ; AH = search attributes
           xor al,al                        ; Set SAttrib directly
    .errnz OPCR_SATTR_DIRECT
           mov di,DEV_OPENMODE
           mov si,open2_phys_disk
           mov cx,DEV_OPENFLAG
           xor bx,bx
           mov ds,bx
           xor dx,dx

           CALLFAR      DoOpenCreate                ; AX = user_handle

           RestoreReg <es,ds,bp,di,si,dx,cx,bx>
           .if nc
;          * Got a SFT entry, Continue

              mov bx,ax                   ; Set up the user handle
              CALLFAR   SFFromHandle         ;(IN  BX = user_handle
                                          ; OUT ES:DI = SFT_ptr)

;             * Fill in the Physical Disk Entry pointer in the SFT entry

              mov word ptr es:[di].sf_devptr,si
              mov word ptr es:[di].sf_devptr + 2,ds

;             * Put the caller's PID in the PhysDiskTable entry

              mov cx,Pid
              mov ds:[si].devpid,cx

;             * Put the user handle in caller's return buffer

              mov bx,drh_RetBufPtr.Segmt
              mov di,drh_RetBufPtr.Offst

;             (AX) = word to write into user space
;             (BX:DI) -> pointer to destination buffer

              CALLFAR SuWord

;             * Carry set or cleared by SuWord, also AX for carry set

           .else                ; Could not get a SFT entry
                                ; Use error code returned from
                                ; w_OpenCreate_all
           .endif               ; Carry Clear - Could get a SFT entry

        .else   ; Pid is nonzero -  entry already open

          mov ax,ERROR_ACCESS_DENIED
          stc                   ; set carry flag

        .endif                  ;  entry_ptr.pid EQ zero

     .else                      ; physdisknum is out of range

       mov ax,ERROR_INVALID_PARAMETER
       stc                      ; set carry flag

     .endif                     ; IF physdisknum G zero AND
                                ; IF physdisknum BE MAX_NUM_PART_DISKS

   .endif  near                 ; Fubuff return code clear

returnhandleret:

  LeaveProc
  ret   ?aframe

EndProc     DevReturnHandle


;/********************** START OF SPECIFICATIONS *********************/
;/*                                                                  */
;/* SUBROUTINE NAME:  DevReleaseHandle                               */
;/*                                                                  */
;/* DESCRIPTIVE NAME: Release a handle for a partitionable disk      */
;/*                                                                  */
;/* FUNCTION:  Release the handle used with Category 9 IOCtls        */
;/*                                                                  */
;/* NOTES:                                                           */
;/*                                                                  */
;/* ENTRY POINT:  DevReleaseHandle                                   */
;/*    LINKAGE:   Call Near                                          */
;/*                                                                  */
;/* INPUT:   BX:SI =  Pointer to user handle corresponding to the    */
;/*                   partitionable disk                             */
;/*          CX    =  Length of handle                               */
;/*                                                                  */
;/* EXIT-NORMAL:   Carry Flag Clear                                  */
;/*                                                                  */
;/* EXIT-ERROR:    Carry Flag Set                                    */
;/*                AX = ERROR_ACCESS_DENIED                          */
;/*                     ERROR_INVALID_HANDLE                         */
;/*                     ERROR_LOCK_VIOLATION                         */
;/*                                                                  */
;/* EFFECTS:  AX, BX, CX, DX, DI, SI, DS, ES, Flags                  */
;/*                                                                  */
;/* INTERNAL REFERENCES:                                             */
;/*    ROUTINES:                                                     */
;/*                                                                  */
;/*    DATA AREA:   PhysDiskTable                                    */
;/*                                                                  */
;/* EXTERNAL REFERENCES:                                             */
;/*    ROUTINES:    SFFromHandle                                     */
;/*                 w_Close                                          */
;/*                                                                  */
;/*    DATA AREA:   SFT                                              */
;/*                                                                  */
;/*********************** END OF SPECIFICATIONS **********************/

Procedure DevReleaseHandle,HYBRID

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

; Get the user handle out of the user's buffer

   DosContext es
   lea di,input_buffer          ; (ES:DI) - > Device Manager's buffer

;  (ES:DI) - > Device Manager's buffer
;  (BX:SI) - > User's buffer
;  (CX)   =    Number of bytes to copy

   SaveReg <di,es>
   CALLFAR FuBuff
   RestoreReg <es,di>

   .if nc  near

;     Get the associated SFT entry

      mov bx,word ptr es:[di]                  ; Get handle out of input buffer
                                               ; (IN  BX = user_handle
      CALLFAR   SFFromHandle                   ;  OUT  ES:DI = SFT_ptr)
                                               ; **********************
                                               ; * ES:DI -> SFT entry *
                                               ; **********************
      .if nc                    ; User Handle is a valid SFT entry

;       Continue processing if SFT entry is a Physical Disk entry

        test es:[di].sf_fsi.sfi_mode2,open2_phys_disk
        .if nz

;          Get the pointer to the PhysDiskTable entry from the SFT

           lds si,es:[di].sf_devptr         ; ********************************
                                            ; * DS:SI -> Physical Disk Entry *
                                            ; ********************************

;         ********************************************
;         * Check if Caller had previously opened it *
;         ********************************************

;************************************************************************
;* Get addressability to the ptda (ss points to it, make the assembler  *
;* think that its the TASKAREA since the PTDA isn't a struc)            *
;************************************************************************

           mov ax,Pid
           .if <ax eq ds:[si].devpid>

             ; Continue Processing

             ; Check if handle is locked

             .if <bit ds:[si].deveflags nand DEV_LOCK>   ; z

                ; * Handle is not locked, Continue


                ; Call w_Close to free the caller's handle in the caller's
                ; handle table and free the corresponding SFT entry.

                pusha
                push ds
                push es
                push    cx
                push    dx
                xor     cx,cx           ; fake filehandlelockID lowword
                xor     dx,dx           ; fake filehandlelockID highword
                CALLFAR w_Close         ; error = w_close(BX = user_handle)
                pop     dx
                pop     cx
                .if C                   ; For a Physical Disk close request,
                                        ;  w_Close should should never fail.
                   Panic <'DevRelease: w_Close'>
                .endif                  ; All necessary error checking is done
                                        ;  before calling w_Close.
                pop  es
                pop  ds
                popa

;              * Clear PID in PhysDiskTable to show status of Not Open *

                mov ds:[si].devpid,0

             .else      ; Handle is locked down

                mov ax,ERROR_LOCK_VIOLATION
                stc     ; Set carry flag

             .endif     ; <BIT ds:[si].deveflags NAND DEV_LOCK>

           .else        ; Caller had not previously opened it

                mov ax,ERROR_ACCESS_DENIED
                stc     ; set carry flag

           .endif       ; caller's PID EQ PhysDiskTable_ptr.pid

        .else           ; SFT entry is not a Physical Disk entry

           mov ax,ERROR_INVALID_HANDLE
           stc          ; set carry flag

        .endif          ; SFT entry is a Physical Disk entry

      .else             ; User Handle is not a valid SFT entry

         mov ax,ERROR_INVALID_HANDLE
         stc            ; set carry flag

      .endif            ; Valid SFT entry ?

   .endif  NEAR         ; Fubuff return code clear
  Ret

EndProc     DevReleaseHandle


                page
;************************** START OF SPECIFICATIONS ***************
;
;    MAJOR ROUTINE NAME: LockPhysDrv
;
;      DESCRIPTIVE NAME: Lock Physical Drive
;
;      FUNCTION: Given a bit map of logical drive units and
;                a pointer to a specific device driver header,
;                lock all DPBs that use that device driver and
;                whose unit number has a corresponding bit set
;                in the drive bit map.
;
;      ENTRY POINT:    LockPhysDrv
;         CALLED BY:   DevLockPhysDrive
;
;      INPUT: (PARAMETERS)
;         DS:SI = Pointer to device header
;         DX:BX = Bit map of logical drives
;                 (from GetFixedDisk/LogicalUnitMap call)
;                   Bit number is unit number
;                    i.e. if DX = 0 and BX = 0014H,
;                   00000000010100 corresponds to units 2 and 4
;
;      OUTPUT: (RETURNED)
;         Carry Clear  (CFL = 0)
;               AX = 0 (zero)
;
;         Carry Set    (CFL = 1)
;               AX    = error return code
;               DX:BX = Bit map with failing drive bit set
;                       (all others 0)
;
;         Registers used:   AX,BX,CX,DX,SI,DI,ES,DS
;
;      INTERNAL REFERENCES:
;         ROUTINES:
;                  LockDrive     - Perform drive locking
;                  UnlockPhysDrv - Perform drive unlocking
;
;      EXTERNAL REFERENCES:
;
;*************************** END OF SPECIFICATIONS ****************
                page
;************************** START OF PSEUDOCODE *******************
;   allocate space for local variables (donemap, unitno);
;   initialize donemap;
;   unitno = 0;
;   rc = NO_ERROR;
;
;   /* while bits exist to lock */
;   while (bitmap != 0)
;   {
;       if (bitmap & 1)
;       {
;           /* search dpb chain for match */
;           dpb_ptr = DPBHead;
;           while (dpb_ptr != NULL &&
;                   (dpb_ptr -> dpb_unit != unitno ||
;                    dpb_ptr -> dpb_driver_addr != caller_dd_addr))
;           {
;               dpb_ptr = dpb_ptr -> dpb_next_dpb;
;           }
;           /* check to see if dpb was found */
;           if (dpb_ptr != NULL)
;           {
;               rc = LockDrive(dpb_ptr);
;               if (rc == NO_ERROR)
;               {
;                   donemap |= 1 << unitno;
;               }
;           }
;           else
;           {
;               rc = ERROR_DRIVE_LOCKED;
;           }
;           if (rc != NO_ERROR)
;           {
;               UnlockPhysDrv(caller_dd_addr, donemap);
;               bitmap = 1 << unitno;
;               break;
;           }
;       }
;       unitno++;           /* bump unit number */
;       bitmap >>= 1;       /* shift bitmap right one bit */
;   }                       /* end of while (bitmap != 0) */
;   return(rc);
;*************************** END OF PSEUDOCODE ********************
                page
;   local structures and equates

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

Procedure LockPhysDrv,NEAR,local

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

;   allocate space for local variables (donemap, unitno);
;   initialize donemap;
;   unitno = 0;
;   rc = NO_ERROR;
;
        LocalVar    bitmap,DWORD

        EnterProc

        mov     ax,ds                   ; move device driver address to
        mov     es,ax
        mov     di,si                   ; es:di
        xor     ax,ax                   ; initialize bitmap
        mov     bitmap.LowWord,ax
        mov     bitmap.HighWord,ax
        mov     ch,al                   ; set ch to 0 for unit number

;   /* while bits exist to lock */
;   while (bitmap != 0)
bmploop:
        mov     ax,dx                   ; AX = HighWord of bit map
        or      ax,bx                   ; or with LowWord of bit map
        jnz     next_lock               ; jump if locks not completed
        mov     ax,NO_ERROR             ; set no error
        clc                             ; indicate no error
        jmp     ld_ret                  ; exit this routine
;   {
next_lock:                              ;
;       if (bitmap & 1)
        test    bx,1                    ; test for low order bit on
        jz      next_unit               ; jump if no unit (do next unit)

;       {
;           /* search dpb chain for match */
;           dpb_ptr = DPBHead;
        DosContext DS
        lds     si,DPBHead              ; get anchor to dpb chain
        ASSUME  DS:NOTHING

;           while (dpb_ptr != NULL &&
;                   (dpb_ptr -> dpb_unit != unitno ||
;                    dpb_ptr -> dpb_driver_addr != caller_dd_addr))
dpbloop:
        cmp     si,-1                   ; test for end of dpb chain
        jz      check_ptr
        cmp     [si].dpb_unit,ch        ; check unit number
        jnz     next_dpb
        cmp     di,[si].dpb_driver_addr.Offst
        jnz     next_dpb
        mov     ax,es
        cmp     ax,[si].dpb_driver_addr.Segmt
        jz      check_ptr
next_dpb:
;           {
;               dpb_ptr = dpb_ptr -> dpb_next_dpb;
        lds     si,[si].dpb_next_dpb    ; get next dpb
        jmp     dpbloop

;           }
;           /* check to see if dpb was found */
check_ptr:

;           if (dpb_ptr != NULL)
        cmp     si,-1                   ; check for end of dpb chain
        jz      ptr_null

;           {
;               rc = LockDrive(dpb_ptr);
        pusha
        push    ds
        push    es
        mov     al,1                    ; indicate physical lock to LockDrive
        CALLFAR LockDrive
        pop     es
        pop     ds
        popa
;               if (rc == NO_ERROR)
        jc      lock_failed

;               {
;                   donemap |= 1 << unitno;
        mov     ax,1                    ; set a bit
        mov     cl,ch                   ; unit number to cl
        xor     si,si                   ; clear si as index
        cmp     ch,15                   ; check to see if low or high
        jbe     doshift                 ; jump if low
        inc     si                      ; bump si
        inc     si                      ;  for high
doshift:
        rol     ax,cl                   ; rotate mod 16
        or      [si].bitmap.LowWord,ax  ; turn on appropriate bit
        jmp     next_unit               ; do next unit

;               }
;           }
lock_failed:

;           else
;           {

;               rc = ERROR_DRIVE_LOCKED;

        mov     ax,ERROR_DRIVE_LOCKED   ; set error code

;           }

ptr_null:

;           if (rc != NO_ERROR)
        .errnz  NO_ERROR
        or      ax,ax
        jz      next_unit
;           {
;               UnlockPhysDrv(caller_dd_addr, donemap);
        push    ax                      ; save return code
        mov     bx,bitmap.LowWord
        mov     dx,bitmap.HighWord
        mov     ax,es
        mov     ds,ax
        mov     si,di
        SaveReg <CX>
        call    UnlockPhysDrv
        RestoreReg <CX>

;               bitmap = 1 << unitno;
        xor     dx,dx
        mov     cl,ch
        mov     bx,1
        rol     bx,cl
        cmp     ch,15                   ; test for high word
        jbe     mask_done
        xchg    bx,dx
mask_done:

;               break;
        pop     ax                      ; restore return code
        stc                             ; set CY since error
        jmp     ld_ret

;           }
;       }

next_unit:
;       unitno++;           /* bump unit number */
        inc     ch

;       bitmap >>= 1;       /* shift bitmap right one bit */
        shr     dx,1
        rcr     bx,1
        jmp     bmploop
;   }
ld_ret:
;   return(rc);
        LeaveProc                       ; leave procedure
        ret                             ; return to caller
EndProc     LockPhysDrv
;
            page
;************************** START OF SPECIFICATIONS ***************
;
;    MAJOR ROUTINE NAME: UnlockPhysDrv
;
;      DESCRIPTIVE NAME: Unlock Physical Drive
;
;      FUNCTION: This routine will convert a bit map passed by the
;                caller into unit numbers.  The DPB chain will be
;                scanned for matches between the DPB header address
;                with the address passed by the caller and the unit
;                number converted with the DPB unit number.  If both
;                match, drive is unlocked if locked by the current
;                process.  This process is repeated for all bits
;                set in the callers bit map.
;
;      ENTRY POINT:    UnlockPhysDrv
;         CALLED BY:   DevUnlockPhysDrive
;
;      INPUT: (PARAMETERS)
;         DS:SI   = Pointer to device header
;         DX:BX   = Bit map of logical drives (DPB_UNIT bit map
;                    from GetFixedDisk/LogicalUnitMap call)
;
;      OUTPUT: (RETURNED)
;         None
;
;         Registers used:   AX,BX,CX,DX,SI,DI,ES,DS
;
;      INTERNAL REFERENCES:
;         ROUTINES:
;                  UnlockDrive - Perform drive unlocking
;
;      EXTERNAL REFERENCES:
;
;      NOTES:   New code for 1.1
;
;*************************** END OF SPECIFICATIONS ****************
            page
;************************** START OF PSEUDOCODE *******************
;   allocate space for local variable on stack (unitno);
;!! unitno = 32; (upped from 31 because unitno is decremented before
;!!               checking the bit)
;   while (bitmap != 0)     /* while bits exist to lock */
;   {
;     shift bitmap left 1 bit;
;     unitno--;
;     if (bit == 1)
;     {
;       dpb_ptr = DPBHead   /* get anchor to dpb chain */
;       while (dpb_ptr != NULL)
;       {
;         /* check dpb_driver_addr against callers devdrvr addr */
;         if (dpb_ptr.driver_addr == caller_dd_addr)
;         {
;           if (dpb_ptr.dpb_unit == unitno)
;           {
;             call UnlockDrive(dpb_ptr);  /* unlock drive */
;             break;                      /* exit dpb while loop */
;           }
;         }
;         /* get pointer to next dpb */
;         dpb_ptr = dpb_ptr.dpb_next_dpb;
;       }
;     }
;   }
;*************************** END OF PSEUDOCODE ********************
            page

Procedure   UnlockPhysDrv, NEAR,local

        ASSUME  DS:NOTHING, ES:NOTHING,SS:TASKAREA
LocalVar        pddHead,DWORD           ; save callers ddheader pointer


        EnterProc <ds,si>

        mov     ch,31+1                 ; set unit number to 32 because
bitloop:                                ; unit# is dec'd before checking bit
        mov     ax,bx                   ; move low map to ax
        or      ax,dx                   ; test with high map
        jz      unlock_done             ; jump if finished with unlock
        dec     ch                      ; subtract 1 from unit
        shl     bx,1                    ; else shift low left
        rcl     dx,1                    ; rotate high left
        jnc     bitloop                 ; jump if unit bit not set

;   found unit to unlock (in ch), pddHead hold dd address
                                        ;  callers device driver address
        les     di,pddHead              ;   to es:di
        DosContext DS
        lds     si,DPBHead              ;  point to start of the dpb's
        ASSUME  DS:NOTHING
duloop:
        cmp     di,[si].dpb_driver_addr.Offst
        jnz     not_addr
        mov     ax,es                   ; move device driver segment to ax
        cmp     ax,[si].dpb_driver_addr.Segmt
        jnz     not_addr                ; not correct dd if segs mismatch
        cmp     [si].dpb_unit,ch        ; check unit number
        jnz     not_addr                ; not correct dd if units mismatch

;       if we get here, it's correct device driver and unit number

        SaveReg <BX,CX,DX,ES,DI>
        CALLFAR UnlockDrive
        RestoreReg <DI,ES,DX,CX,BX>
        jmp     bitloop                 ; do next drive

not_addr:

; get address of next dpb, check for end of chain

        lds     si,[si].dpb_next_dpb    ; get next dpb
        cmp     si,-1                   ; NOT end of chain mark ?
        jnz     duloop                  ; yes, so check dd addr
        mov     ax,es                   ; else, restore dd addr segment
        mov     ds,ax
        mov     si,di                   ; restore dd addr offset
        jmp     bitloop                 ; check for more units

unlock_done:
        mov     ax,NO_ERROR
        clc
        LeaveProc                       ; leave procedure
        ret                             ; return to caller
EndProc UnlockPhysDrv

        PUBLIC  _EndPHYSDSKGROUP
_EndPHYSDSKGROUP        label   byte

FSSWAPCODE  ends

        FARCODE INIT
INITCODE segment

;/********************** START OF SPECIFICATIONS *********************/
;/*                                                                  */
;/* SUBROUTINE NAME:  InitDeviceMgr                                  */
;/*                                                                  */
;/* DESCRIPTIVE NAME: Initialize Device Manager structures           */
;/*                                                                  */
;/* FUNCTION:                                                        */
;/*   First time:  Initialize Request Packet Table, Resident devices */
;/*   Second time: Build the PhysDiskTable structure, and call       */
;/*                all installed device drivers with re-init packet. */
;/*                                                                  */
;/* NOTES:  In Init Code Segment                                     */
;/*                                                                  */
;/* ENTRY POINT:  InitDeviceMgr                                      */
;/*    LINKAGE:   Call Near                                          */
;/*                                                                  */
;/* INPUT:  none                                                     */
;/*                                                                  */
;/* EXIT-NORMAL:  N/A                                                */
;/*                                                                  */
;/* EXIT-ERROR:   N/A                                                */
;/*                                                                  */
;/* EFFECTS:  all registers are preserved                            */
;/*                                                                  */
;/* INTERNAL REFERENCES:                                             */
;/*    ROUTINES:   BuildPhysDiskTable                                */
;/*                                                                  */
;/*    DATA AREA:  PhysDiskTable                                     */
;/*                                                                  */
;/* EXTERNAL REFERENCES:                                             */
;/*    ROUTINES:   Init3xboxIoctl                                    */
;/*                InitReqPackets                                    */
;/*                InitBaseDDs                                       */
;/*                _SELAllocKRH                                      */
;/*                _SELReallocKRH                                    */
;/*                                                                  */
;/*    DATA AREA:  OEMdev (Bimodal Device Driver Chain)              */
;/*                                                                  */
;/*********************** END OF SPECIFICATIONS **********************/

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

Procedure   InitDeviceMgr,NEAR
        ASSUME  DS:NOTHING, ES:NOTHING, SS:TASKAREA
        LocalVar vmacHeapBlockInfo,<size vmac_s>

        EnterProc

;
; Save registers used
   pusha
   push ds
   push es
   pushf

  DosContext ds
  cmp byte ptr [init_devmgr_flag],FIRST_TIME
  je  short tmse1
  jmp tmse2

tmse1:

if 0 ; BUGBUG danno 25-Nov-1988
     ; temporarily disable for MVDM

;    * Initialize 3xBox IOCtl code

     call Init3xboxIoctl
endif ; BUGBUG danno 25-Nov-1988

;    * Initialize Request Packet Table

     call InitReqPackets

;    Allocate DPB segment

     ; _SELAllocKHB(VM_HKH_PUB_RESRW,size,0,0,FSDPBOWNER,&vmacHeapBlockInfo)
     FlatContext <ds,es>,ax
     SSToDS  eax,vmacHeapBlockInfo
     xor     ecx,ecx
     mov     edx, (NUMDRIVE * size dpb) + MAXPHYSDISKTBLSIZE
     CCALL32 _SELAllocKHB <VM_HKH_PUB_RESRW, edx, ecx, ecx, FSDPBOWNER, eax>
     or      eax,eax
     jnz     idmie0                ; error in allocation attempt
     DosContext <ds>,ax
     mov     ax,vmacHeapBlockInfo.ac_sel
     mov     DPBHead.Segmt,ax      ; Save address of DPB chain
     mov     DPBHead.Offst,0
     mov     SAS_dd_area.SAS_dd_DPB_segment,ax
     mov     es,ax                 ; Zerofill the block
     xor     ax,ax
     mov     di,ax
     mov     cx,((NUMDRIVE * size dpb) + MAXPHYSDISKTBLSIZE) / 2
     .errnz  ((NUMDRIVE * size dpb) + MAXPHYSDISKTBLSIZE) and 1
     rep stosw

;    Initialize the oemhlp resident driver
     mov     si,offset OEMdev
     SaveReg <es>
     call    BaseDevInit
     RestoreReg <es>
     or      cx,dx
     .if     <z>
        Panic <'InitDevMgr: OEMHLP driver'>
     .endif

     mov     ax,es             ; AX -> DPB segment

;    Load and initialize base device drivers
     call    InitBaseDDs

;    Chain all the DPBs together

     DosContext ds
     SaveReg <ds>
     xor     cx,cx                        ; CX = number of DPBs needed
     mov     cl,NumIO                     ;
     lds     si,DPBHead                   ; DS:SI points to DPB segment
     jcxz    no_dpbs1                     ; possibly no dpb's
     mov     si,0                         ; set initial offset
     mov     di,si                        ; DI contains DPB offset
     SaveReg  <ds>                        ; save DPB seg ptr
     DosContext ds                        ; get seg of DPBHead
     mov     DPBHead.Offst,di             ; remember to fix the head ptr
     RestoreReg <ds>                      ; restore DPB seg ptr
     .repeat
       add   di,size dpb                  ; Compute address of next DPB
       mov   [si].dpb_next_dpb.Offst,di   ; Set the link to next DPB
       mov   [si].dpb_next_dpb.Segmt,ds
       mov   si,di                          ; Point to next DPB
     .loop
     sub     si,size dpb                    ; Point to last DPB
     mov     [si].dpb_next_dpb.Offst,0ffffh ; terminate the chain
     mov     [si].dpb_next_dpb.Segmt,ds
     jmp short lastdpb
no_dpbs1:
     mov     si,0FFFFh                    ; if no dpb's, offset will be 0ffffh
     push    ds
     DosContext ds
     mov     DPBHead.Offst,si             ; put offset into DPBHead pointer
     pop     ds
lastdpb:
     mov     di,ds
     RestoreReg <ds>
     mov     LastDPB.Offst,si             ; save address of last DPB
     mov     LastDPB.Segmt,di

;    Set init_devmgr_flag for second time through

     DosContext ds
     inc init_devmgr_flag
     jmp short tmse3               ;skip the rest 'till second time through

tmse2:                 ; Second time through

;   *All the device drivers have been installed into the system at
;    this point.
;
;    * Build the PhysDiskTable structure

     call BuildPhysDiskTable             ; DI = size of the segment

;    We have put in all the data that we need into the segment. We can now
;    realloc the segment to shrink it down to the size that is used.
;
     DosContext ds,ax
     movzx   edi,di                   ; (edi) = desired new size
     movzx   ecx,[DPBHead].Segmt      ; (ecx) = selector for segment
     ; _SELReallocKHB(VM_HKH_PUB_RESRW,sel,newcb,0, &vmacHeapBlockInfo.ac_va)
     FlatContext <ds,es>,ax
     SSToDS  eax,vmacHeapBlockInfo.ac_va
     xor     edx,edx
     CCALL32 _SELReallocKHB <VM_HKH_PUB_RESRW, ecx, edi, edx, eax>
     or      eax,eax
     jnz     idmie1                  ; reallocation failed

;    Let physical device drivers know that all PDD's have been initialized
;    so they can establish inter-device driver communications.

     call    ReinitDDs

tmse3:        ;init_devmgr_flag

; Restore Registers used
  popf
  pop es
  pop ds
  popa

  LeaveProc
  Ret

idmie0: Panic <'InitDeviceMgr: DPB alloc'>
idmie1: Panic <'InitDeviceMgr: DPB realloc'>

EndProc     InitDeviceMgr


;***    SetDPBRemovFlag - Determine whether a drive supports removable media
;
;       This routine determines, given a DPB, whether the drive is for
;       removable or non-removable media.  This information is stored in
;       the DPBNONREMOV bit in dpb_flags.
;
;       ENTRY:  ds:si -> dpb
;
;       EXIT:   DPB's dpb_flags field set up correctly
;
;       USES:   AL, BX, ES, FLAGS
;
;       ALGORITHM
;
;       AllocReqPacketWait()
;       Set up packet for CmdRemMed
;       DevIOCall2()
;       if (!PktStatus AND STERR) {
;           if (PktStatus AND STBUI) {
;               Set "Removable" bit in dpb_flags
;           }
;       } else {        /* We got an error. Assume removable */
;           Set "removable" bit in dpb_flags
;       }
;       FreeReqPacket()
;
Procedure   SetDPBRemovFlag,near
        ASSUME  DS:NOTHING, ES:NOTHING, SS:TASKAREA

        CALLFAR AllocReqPacketWait              ; ES:BX -> request packet
        and     ds:[si].dpb_flags,NOT DPBNONREMOV ; Clear "non-removable" bit
        SaveReg <ds,si>                         ; Save pointer to DPB
        mov     es:[bx].PktCmd,CMDRemMed        ; Set up command code
        mov     es:[bx].PktLen,LENRemMed+PktHeadSize
        mov     al,ds:[si].dpb_unit
        mov     es:[bx].PktUnit,al              ; unit number
        mov     es:[bx].PktStatus,0             ; initial status
        lds     si,ds:[si].dpb_driver_addr      ; DS:SI -> device header
        CALLFAR DevIOCall2                      ; Call the device driver
        RestoreReg <si,ds>                      ; DS:SI -> DPB
        test    es:[bx].ReqStat,STERR           ; error from driver?
        jnz     rdrx                            ; yes--assume removable
        test    es:[bx].ReqStat,STBUI           ; removable?
        jz      rdrx                            ; yes, don't set nonremov flag
        or      ds:[si].dpb_flags,DPBNONREMOV   ; Mark DPB as "non-removable"
rdrx:   CALLFAR FreeReqPacket
        ret

EndProc SetDPBRemovFlag


;***    BuildPhysDiskTable - Build the physical disk table
;
;       BuildPhysDiskTable builds the table for all the partitionable
;       physical disks supported by the system. It queries the block
;       device drivers for the information on the physical drives, and
;       fills it into the relevant entry in the table.
;
;       ENTRY   [LastDPB] points to the last DPB
;
;       EXIT    DI -> Byte after the last entry in the table
;                     (= size of segment)
;               [PhysDiskTablePtr] -> BYTE after last DPB in segment
;                                     (Beginning of PhysDiskTable)
;
;       USES    AX, BX, CX, DX, DS, ES, DI, SI, Flags
;
;       ALGORITHM
;
;       [PhysDiskTablePtr] = [LastDPB] + size DPB
;       DevPtr -> CONDev
;       NumDisks = 0
;       AllocReqPacketWait()
;       while (! End of device header chain) {
;           if (!char device) {
;               Set up packet for CMDPartFixedDisks
;               DevIOCall2()
;               if (!error) {
;                   RelDiskNum = 0
;                   for (each physdisk supported by device driver) {
;                       NumDisks++
;                       Set up diskentry for this disk
;                       RelDiskNum++
;                       Move to next entry in PhysDiskTable
;                   }
;               }
;           }
;           if (DevPtr->SDevNext.Offst != -1)
;               DevPtr = DevPtr->SDevNext
;       }
;       FreeReqPacket()
;       Save NumDisks and Table size in Table Header
;       Return (Pointer to byte after end of table)
;
;
Procedure   BuildPhysDiskTable,NEAR,local
        ASSUME  DS:NOTHING, ES:NOTHING, SS:TASKAREA

;
; Set up the location where the PhysDiskTable should start. We place it right
; after the last DPB since we know when we get here that all the DPBs for the
; system have already been set up.
;
        DosContext ES
        DosContext DS
        mov     ax,[LastDPB].Segmt
        mov     di,[LastDPB].Offst        ; AX:DI -> last DPB in segment
        add     di,size dpb                 ; AX:DI -> byte after last DPB
        mov     [PhysDiskTablePtr].Segmt,AX
        mov     [PhysDiskTablePtr].Offst,DI   ; Set up pointer to PhysDiskTable
        mov     ES,AX                       ; ES:DI -> PhysDiskTable
    ASSUME ES:NOTHING

;   Get Addressibility to the first PhysDiskTable entry

        add     di, size PhysDiskTblHdr     ; ES:DI - > PhysDiskTable entry #1 *

;  Get access to CONdev (Bimodal Device Driver Chain)

        lea     si,CONdev                   ; DS:SI - > First header in device driver chain
        xor     cx,cx                       ; NumDisks = 0

;    * Allocate a request packet

        CALLFAR AllocReqPacketWait          ; ES:BX -> request packet

    assume ds:nothing

bpdt10: test    ds:[si].SDevAtt,DEV_CHAR_DEV
        jnz     bpdt30                      ; Character device. Get next device.
;
; This is a block device driver. We query it to see if it supports any
; partitionable disks.

; Set up Request packet for Partitionable Fixed Disks command
; for current device driver.

        mov     es:[bx].PktCmd,CMDPartfixeddisks          ; Command
        mov     es:[bx].PktLen,PARTFIXEDDISKS_PKTLENGTH   ; Packet Length
        mov     es:[bx].PktStatus,0                       ; Initialize status
        mov     es:[bx].PktUnit,0

; * Send device driver command Partitionable Fixed Disks
;
; DS:SI -> Device header
; ES:BX -> Request packet

        CALLFAR DevIOCall2                  ; Issue command to the device driver

; * Save Number of Partitionable disks supported if no error from driver

        test    es:[bx].PktStatus,STERR
        jnz     bpdt30                      ; Got an error from device. Get next device.
        mov     dl,es:[bx].PktData          ; DL = number of drives supported

; * Check if current device driver supports any Partitionable
; *  Fixed Disks

        or      dl,dl
        jz      bpdt30                      ; No partitionable disks. Try next driver.

; * Device Driver supports a disk. Continue.

        xor     ax,ax                       ; RelDiskNum = 0

; * Fill in an entry for each disk the DD supports
;
bpdt20: cmp     cx,MAX_NUM_PART_DISKS
        jae     bpdt50                      ; Cannot support any more disks

; Haven't gone over limit of partitionable disks in the system. Continue.
;
        inc     cx                          ; NumDisks++
        push     es
        DosContext ES
        mov      es,[PhysDiskTablePtr].Segmt ; ES:DI - > PhysDiskTable entry
    ASSUME ES:NOTHING

; Fill in relative disk number field in PhysDiskTable Entry
; Fill in disk device header field in PhysDiskTable Entry
;
        mov     es:[di].reldisknum,al
        mov     es:[di].ddheader.Offst,si
        mov     es:[di].ddheader.Segmt,ds

; Point to next entry in PhysDiskTable

        add     di,size diskentry       ; ES:DI - > Next PhysDiskTable entry
        pop     es                      ; ES:BX - > Request Packet
        inc     ax                      ; RelDiskNum++
        dec     dl                      ; Any more physical disks?
        jnz     bpdt20                  ; Yes. Fill in information for next physical disk
;
; No more physical disks supported by the device driver. See if any more devices
; that support block devices.
;
bpdt30: cmp     ds:[si].SDevNext.Offst,-1
        jz      bpdt50                  ; No more device drivers

; We still have more devices to query. Get next device header.

        lds     si,ds:[si].SDevNext     ; DS:SI -> Next device header
    ASSUME DS:NOTHING
        jmp     short bpdt10            ; Process this device

;
; We are done querying the devices. Now free the request packet and return.
; ES:BX -> request packet
; DI -> Byte after last diskentry
;
bpdt50: CALLFAR FreeReqPacket

; Fill in PhysDiskTable's total number of partitionable disks field

        DosContext DS
        lds     si,PhysDiskTablePtr     ; DS:SI - > PhysDiskTable
    ASSUME DS:NOTHING
        mov     ds:[si].pdth_totaldisks,cl

; Fill in the Length field of PhysDiskTable
; DI points to byte pass PhysDiskTable. This is also the size of the segment.
;
        DosContext ES
        SaveReg <di>
        sub     di,[PhysDiskTablePtr].Offst     ; DI = Byte past PhysDiskTable -
        mov     ds:[si].pdth_tbllen,di          ;      Start of PhysDiskTable
        RestoreReg <di>                         ; DI = size of segment
        Ret

EndProc     BuildPhysDiskTable

;***    ReinitDDs -
;
;       Let physical device drivers know that all PDD's have been initialized
;       so they can establish inter-device driver communications.
;
;       ENTRY:  none
;
;       EXIT:   none
;
;       USES:   Doesn't matter (they are restored after this call returns)
;
;
;
Procedure   ReinitDDs,near
        ASSUME  DS:NOTHING, ES:NOTHING, SS:TASKAREA

        CALLFAR AllocReqPacketWait              ; ES:BX -> request packet

;       Point to 1st device header in the linked list

        DosContext ds
        mov     si,OFFSET DevHead

rid00:
        mov     es:[bx].PktCmd,CMDInitComplete
        mov     es:[bx].PktLen,PktHeadSize + 0
        mov     es:[bx].PktUnit,0
        mov     es:[bx].PktStatus,0
        mov     es:[bx].PktFlag,0

        mov     ax,ds:[si].SDevAtt   ;get device attributes from header
        and     ax,DEVLEV_3          ;mask off all but DEVLEV_3
        cmp     ax,DEVLEV_3          ;Is it DEVLEV_3
        jne     rid_next

        test    ds:[si].SDevCaps,DEV_INITCOMPLETE ;supports CMDInitComplete?
        jz      short rid_next                    ; no


        SaveReg <bx,si,ds,es>

        CALLFAR DevIOCall2                      ; Reinitialize the device

        RestoreReg <es,ds,si,bx>

rid_next:
        cmp     [si].SDevNext.Segmt,-1  ; have we reached the end of the chain?
        je      short rid_x

        push    [si].SDevNext.Segmt     ; point to next device head
        mov     si,[si].SDevNext.Offst
        pop     ds
        jmp     rid00

rid_x:

        CALLFAR FreeReqPacket         ; IN ES:BX = request_packet
        ret
EndProc ReinitDDs

INITCODE ends
END
