        .TITLE  ZEDRIVER - Driver for example "pseudodevice" ACP
        .IDENT  'X01-000'

;++
; ZEDRIVER - Driver for example "pseudodevice" ACP
;
; ABSTRACT:
;
;       This driver passes write IRPs to ZEACP, which in turn issues
;       corresponding $QIO requests to a physical device. 
;--

        .PAGE
        .SBTTL  External and local symbol definitions

;       standard VMS data structures

        .LIBRARY \SYS$LIBRARY:LIB\

        $AQBDEF                                 ; ACP queue block
        $CANDEF                                 ; Cancel reason codes
        $CCBDEF                                 ; Channel control block
        $CRBDEF                                 ; Controller request block
        $DCDEF                                  ; Device classes and types
        $DDBDEF                                 ; Device data block
        $DDTDEF                                 ; Driver dispatch table
        $DEVDEF                                 ; Device characteristics
        $DYNDEF                                 ; Data structure ID codes
        $IDBDEF                                 ; Interrupt data block
        $IODEF                                  ; I/O function codes
        $IPLDEF                                 ; Hardware IPL definitions
        $IRPDEF                                 ; I/O request packet
        $JIBDEF                                 ; Job information block
        $PCBDEF                                 ; Process control block
        $PRDEF                                  ; Processor register names
        $PRVDEF                                 ; Privilege bits
        $PSLDEF                                 ; Processor status longword
        $SPLDEF                                 ; Spinlock definitions
        $SSDEF                                  ; System status codes
        $UCBDEF                                 ; Unit control block
        $VCBDEF                                 ; Volume control block
        $VECDEF                                 ; Interrupt vector block

        $DEFINI UCB

        _VIELD  UCB,0,<-                        ; bits in UCB$L_DEVDEPEND
                <ACPEXISTS,,M>>

. = UCB$K_LENGTH

$DEF    UCB_L_TMPLTUCB          .BLKL 1         ; pointer to template UCB
$DEF    UCB_Q_PNDIRP            .BLKQ 1         ; queue of IRPs started by ACP
$DEF    ZE_UCB_K_LENGTH

        $DEFEND UCB

        $DEFINI BUF                             ; I/O buffer
$DEF    BUF_L_SVAUSRDATA        .BLKL 1         ;  sva of user data in buffer
$DEF    BUF_L_PVAUSRBUFF        .BLKL 1         ;  process virt addr of user's buffer
$DEF    BUF_W_SIZE              .BLKW 1         ;  allocated size
$DEF    BUF_B_TYPE              .BLKB 1         ;  type = DYN$C_BUFIO
$DEF    BUF_B_spare             .BLKB 1         ;  not used
$DEF    BUF_T_DLL               .BLKB 8         ;  data link scratch area
$DEF    BUF_T_USRDATA                           ;  end of header
        $DEFEND BUF

;       Argument list (AP) offsets for device-dependent QIO parameters

P1      = 0                                     ; first QIO parameter
P2      = 4                                     ; second QIO parameter
P3      = 8                                     ; third QIO parameter
P4      = 12                                    ; fourth QIO parameter
P5      = 16                                    ; fifth QIO parameter
P6      = 20                                    ; sixth QIO parameter

;       driver/ACP-specific values

ZE_K_ACPTYPE = 252
ZE_K_ACPCLASS = 253

        .PAGE
        .SBTTL  Standard tables and local storage

; Driver prologue table

        DPTAB   -                               ; DPT-creation macro
                END=ZE_END,-                    ; End of driver label
                ADAPTER=NULL,-                  ; Adapter type
                UCBSIZE=<ZE_UCB_K_LENGTH>,-     ; Length of UCB
                MAXUNITS=1,-                    ; Length of UCB vector in IDB
                NAME=ZEDRIVER,-                 ; Driver name
                FLAGS=DPT$M_SMPMOD              ; Ok to run in SMP environment
        DPT_STORE INIT                          ; load initialization table
        DPT_STORE DDB,DDB$L_ACPD,L,<^A\ZE\>     ; default ACP name
        DPT_STORE DDB,DDB$B_ACPCLASS,B,-        ; ACP class 
                ZE_K_ACPCLASS                   ;  field
        DPT_STORE UCB,UCB$B_FLCK,B,SPL$C_IOLOCK8; Device fork lock index
        DPT_STORE UCB,UCB$B_DIPL,B,20           ; Dvc IPL (not really used)
        DPT_STORE UCB,UCB$L_DEVCHAR,L,<-        ; Device characteristics
                DEV$M_SQD!-                     ;  sequential
                DEV$M_AVL!-                     ;  available
                DEV$M_IDV!-                     ;  input device
                DEV$M_ODV>                      ;  output device
        DPT_STORE UCB,UCB$B_DEVCLASS,B,255      ; device class
        DPT_STORE UCB,UCB$B_DEVTYPE,B,255       ;  and type
        DPT_STORE UCB,UCB$L_DEVDEPEND,L,0       ; flags
        DPT_STORE UCB,UCB$W_DEVBUFSIZ,W,0       ; Default buffer size

        DPT_STORE REINIT                        ; reload initialization table
        DPT_STORE DDB,DDB$L_DDT,D,ZE$DDT        ; Address of DDT
        DPT_STORE CRB,-                         ; Address of controller 
                CRB$L_INTD+VEC$L_INITIAL,-      ;  initialization routine
                D,ZE_CONTROL_INIT
        DPT_STORE CRB,-                         ; Address of unit 
                CRB$L_INTD+VEC$L_UNITINIT,-     ;  initialization routine
                D,ZE_UNIT_INIT                  ; routine
        DPT_STORE END                           ; End of initialization
                                                ; tables

; Driver dispatch table

        DDTAB   -
                DEVNAM=ZE,-                     ; Name of device
                FUNCTB=ZE_FUNCTABLE,-           ; FDT address
                CLONEDUCB=ZE_CLONED_UCB         ; Cloned UCB routine

        .PAGE
        .SBTTL  Function Decision Table

ZE_FUNCTABLE:

        FUNCTAB ,-                              ; Valid I/O functions
                <WRITEPBLK,WRITELBLK,WRITEVBLK> ;  writes

        FUNCTAB ,-                              ; Buffered I/O functions
                <WRITEPBLK,WRITELBLK,WRITEVBLK> ;  writes

        ; function selection masks

        FUNCTAB FDT_BUFFERED_WRITE, -
                <WRITEPBLK,WRITELBLK,WRITEVBLK>

        FUNCTAB FDT_QUEUE_TO_ACP, -
                <WRITEPBLK,WRITELBLK,WRITEVBLK>

        .PAGE
        .SBTTL  FDT_BUFFERED_WRITE, FDT routine for write requests

;++
; FDT_BUFFERED_WRITE, FDT routine for write requests
;
; This routine checks the user's buffer for access, allocates a system
; buffer, and copies the user's data to it.  
;--

FDT_BUFFERED_WRITE:

        BBS     #DEV$V_MNT,UCB$L_DEVCHAR(R5),1$ ; check "mounted" bit
        MOVZWL  #SS$_DEVNOTMOUNT, R0            ; error, ACP doesn't exist
        JSB     G^EXE$ABORTIO

1$:     MOVL    P1(AP), R0                      ; get buffer address
        MOVZWL  P2(AP), R1                      ; get buffer length
        JSB     G^EXE$WRITECHK                  ; check for accessibility

        PUSHR   #^M<R0,R3>                      ; save registers
        ADDL2   #BUF_T_USRDATA, R1              ; account for buffer header
        JSB     G^EXE$DEBIT_BYTCNT_ALO          ; check quota, allocate buffer
        BLBC    R0, 30$                         ;  br if bad
        POPR    #^M<R0,R3>                      ; restore registers
        MOVW    R1, IRP$W_BOFF(R3)              ; save process BYTCNT charge

        ; initialize buffer

        MOVL    R2, IRP$L_SVAPTE(R3)            ; connect buffer to IRP
        MOVAB   BUF_T_USRDATA(R2), (R2)         ; set pointers to data region
        MOVL    R0, BUF_L_PVAUSRBUFF(R2)        ;  of buffer and to user buffer
        BBS     #IRP$V_FUNC, IRP$W_STS(R3), 10$ ; br if read function
        PUSHR   #^M<R3,R4,R5>                   ; protect registers from MOVC3
        MOVC3   P2(AP), (R0), BUF_T_USRDATA(R2) ; copy user data to sys buffer
        POPR    #^M<R3,R4,R5>                   ; restore registers
10$:    RSB

        ; come here with saved R0, R3 on stack, desired return status in R0

30$:    POPR    #^M<R2,R3>                      ; clean up stack and return 
        JMP     G^EXE$ABORTIO                   ;  quota or buffer error

;++
; FDT_QUEUE_TO_ACP, FDT routine to queue IRP to ACP
;
; This routine checks for the existence of a VCB, then queues the IRP to
; the ACP.
;--

FDT_QUEUE_TO_ACP:
        MOVZWL  #SS$_DEVNOTMOUNT, R0            ; assume sync error
        MOVL    UCB$L_VCB(R5), R1               ; get VCB address
        BEQL    ACP_REQ_ERR                     ;  should never happen, but...

        ADAWI   #1, VCB$W_TRANS(R1)             ; bump VCB transaction count
        JMP     G^EXE$QIOACPPKT                 ; queue IRP to ACP

ACP_REQ_ERR:
        JMP     G^EXE$ABORTIO

        .PAGE
        .SBTTL  ZE_CONTROL_INIT, Controller initialization routine

;++
; ZE_CONTROL_INIT, Readies controller for I/O operations
;
; Functional description:
;
;       The operating system calls this routine in 2 places:
;               during driver loading and reloading
;               during recovery from a power failure
;
; Inputs:
;
;       R4      - address of the CSR (controller status register)
;       R5      - address of the IDB (interrupt data block)
;       R6      - address of the DDB (device data block)
;       R8      - address of the CRB (channel request block)
;
; Outputs:
;
;       The routine must preserve all registers except R0-R3.
;
;--

ZE_CONTROL_INIT:
        MOVL    #SS$_NORMAL, R0
        RSB                                     ; return

        .PAGE
        .SBTTL  ZE_UNIT_INIT, Unit initialization routine

;++
; ZE_UNIT_INIT, Readies unit for I/O operations
;
; Functional description:
;
;       The operating system calls this routine after calling the
;       controller initialization routine:
;
;               during driver loading (but not reloading)
;               during recovery from a power failure
;
; Inputs:
;
;       R4      - address of the CSR (controller status register)
;       R5      - address of the UCB (unit control block)
;
; Outputs:
;
;       Sets the ONLINE bit in the UCB status longword.  
;       The routine must preserve all registers except R0-R3.
;
;--

ZE_UNIT_INIT:
        MOVL    R5, UCB_L_TMPLTUCB(R5)          ; save address of template UCB
        BBSS    #UCB$V_ONLINE,UCB$L_STS(R5),10$ ; Set unit online

        ; if first time (not power failure), do other initializations 
        ; (none for this driver)
        
10$:    MOVL    #SS$_NORMAL, R0
        RSB                                     ; Return

        .PAGE
        .SBTTL  ZE_CLONED_UCB, Cloned UCB initialization routine

;++
; ZE_CLONED_UCB, Cloned UCB initialization routine
;
; Functional description:
;
;       The operating system calls this routine after creating a cloned 
;       UCB and doing the device-independent initialization thereof 
;       (see the "VMS Device Support Manual", Appendix D, page 5).  
;
; Context:
;
;       Called from the $ASSIGN channel system service, in the context of
;       the requesting process.
;
;       IPL = IPL$_ASTDEL
;
;       The IOC$GL_MUTEX is held for write access.
;
; Inputs:
;
;       R2      - address of cloned UCB
;       R3      - address of DDT
;       R4      - address of current PCB
;       R5      - address of template UCB 
;
; Outputs:
;
;       R0      - return status (if low bit clear, the system will deallocate
;                 the cloned UCB, and return the error status to the user as
;                 the result of the $ASSIGN call)
;       
;       This routine should perform any required device-specific initialization
;       of the cloned UCB.  
;
;       R2, R4, and R5 must be preserved.  (The "VMS Device Support Manual"
;       specifies only that R2 must be preserved, but inspection of the code
;       indicates otherwise.)
;--

ZE_CLONED_UCB:

        CLRQ    UCB_Q_PNDIRP(R2)                ; init the pending IRP queue hdr

        MOVL    #SS$_NORMAL, R0                 ; return with good status
        RSB

        .PAGE
        .SBTTL  ZE_END, End of driver

;++
; Label that marks the end of the driver
;--

ZE_END:                                         ; Last location in driver
        .END

