.title ZTSERVER subroutines ; ; w.j.m. jun 1989 (0.2) ; documented 3-jul-1989 wjm ; mod 18-aug-1989 wjm (0.9): support VMS V5 ; mod 22-oct-1993 wjm (0.99A): port to AXP VMS 1.5 (needs EVAX defined) ; mod 29-jan-1994 wjm (0.99B): automatically defined EVAX; optionally ; pass name of ZT device on ZT_INIT() ; .ident /0.99B/ ; ZTDRIVER better had the same ident! ; ;***** ; entries: ; status=ZT_INIT(zt_name) - initialize, ... ; ... set up communication with ZTDRIVER. ; NOTE: an exit handler is set up, ; which MUST be executed eventually! ; character*(*) zt_name - optional argument (fixed length descriptor), ; defaults to "_ZTA0:" ; ; status=ZT_WAIT() - wait for message (I/O request) from ZTDRIVER. ; On return, the 'message buffer' has been filled in. ; ; status=ZT_REQCOM() - send (part of) 'message buffer' ; to ZTDRIVER and complete I/O. ; ; status=ZT_TOUSER() - simulates DMA transfer ; from 'data buffer' to ZTDRIVER's user buffer. ; Number of bytes tranferred: ; MIN(UCB$W_BCNT,ZT_BUFDSC.dsc$w_length) ; Can be called at most once per I/O request! ; ; status=ZT_FRUSER() - simulates DMA transfer ; from ZTDRIVER's user buffer to 'data buffer'. ; Number of bytes transferred: ; UCB$W_BCNT ; On return, ZT_BUFDSC.dsc$w_length is set to this number. ; Can be called at most once per I/O request! ; ; ZT_MSGDSC [globaldef]: ; Descriptor of 'message buffer' (see ZTDEF.MAR) ; used for communication with ZTDRIVER. ; Both buffer address and length are FIXED. ; ; ZT_BUFDSC [globaldef]: ; Descriptor of 'data buffer' (see above). ; Buffer address is FIXED, actual size is ^xFFFF . ; ZT_BUFDSC.dsc$w_length is set by ZT_FRUSER(), ; and by caller of ZT_TOUSER(). ; ;***** ; ; .ntype __,R31 ; set EVAX nonzero if R31 is a register .if eq <__ & ^xF0> - ^x50 EVAX = 1 .iff EVAX = 0 .endc ; ; .if ne EVAX .library "SYS$LIBRARY:LIB" .library "SYS$DISK:[]ZT" .iff .link "SYS$SYSTEM:SYS.STB"/selective_search .library "SYS$LIBRARY:LIB" .library "ZT" .endc ; $dvidef $iodef $prdef $prvdef $ucbdef ; smp_code=0 ;VMS V4 or earlier .iif df UCB$L_DLCK, smp_code=1 ;VMS V5 ; ztdef ; ZT definitions - need $ucbdef ; ; ;***** macros ; .macro chkr0 ?lab .iif ne EVAX, .branch_likely blbs r0,lab pushl r0 calls #1,g^lib$stop lab: .endm chkr0 ; ; ;***** PSECTs ; .psect _data,quad .psect _code,nowrt .if ne EVAX .iff .psect _nonpagedcode,page,nowrt nonpagedcode: ;start of psect .endc .psect _nonpageddata,page nonpageddata: ;start of psect ; .page ;***** nonpaged data ; .psect _nonpageddata ; zt_ucb: .long 0 mbx_ucb:.long 0 ; zt_name_size=32 zt_name: .blkb zt_name_size mbx_name_size=32 mbx_name: .blkb mbx_name_size .align quad zt_namedsc: .long 0-0 .long zt_name mbx_namedsc: .long 0-0 .long mbx_name ; .align quad zt_msgbuf: .byte 0[zt_msglen] ; ; ;***** get ucb from device name ; called in kernel mode, IPL=0 ; p1 = addr of descriptor ! must be nonpaged ; p2 = addr of ucb-address ! must be nonpaged ; .if ne EVAX $locked_page_start ;===== nonpaged routines start here ========== ; ; dev_to_ucb: .call_entry preserve= .iff .psect _nonpagedcode dev_to_ucb: .word ^m .endc ; movl g^CTL$GL_PCB,r4 ; jsb g^SCH$IOLOCKR ;;***** start IPL2 ********************* movaq @1*4(ap),r1 ;; jsb g^IOC$SEARCHDEV ;;search for device blbc r0,90$ ;; movl r1,@2*4(ap) ;;r1=UCB 90$: ;; pushl r0 ;; jsb g^SCH$IOUNLOCK ;; Unlock the I/O database setipl #0 ;;***** end IPL2 *********************** popl r0 ; ret ; ; ;***** fake ZT interrupt ; called in kernel mode, IPL=0 ; .if ne EVAX zt_int: .call_entry preserve= .iff .psect _nonpagedcode zt_int: .word ^m .endc ; movl zt_ucb,r5 beql 90$ ; .if eq smp_code dsbint ucb$b_dipl(r5) ;; .iff ;; devicelock - ;; lockaddr=ucb$l_dlck(r5),- ;; lockipl=ucb$b_dipl(r5),- ;; savipl=-(sp),- ;; preserve=NO ;; .endc ;; ;; jsb @ucb_l_inter(r5) ;; ;; .if eq smp_code ;; enbint ;; .iff ;; deviceunlock - ;; lockaddr=ucb$l_dlck(r5),- ;; newipl=(sp)+,- ;; preserve=NO ;; .endc ; 90$: movl #1,r0 ret ; ; ;***** kernel mode setup ; called via $CMKRNL ; .if ne EVAX k_setup: .call_entry preserve= .iff .psect _nonpagedcode k_setup: .word ^m .endc ; movab g^lib$sig_to_ret,(fp) ; pushal mbx_ucb pushaq mbx_namedsc calls #2,dev_to_ucb blbc r0,90$ ; pushal zt_ucb pushaq zt_namedsc calls #2,dev_to_ucb blbc r0,90$ ; movl zt_ucb,r5 cmpl ucb$l_media_id(r5),#<^x6D285010> ; plausibility check beql 20$ movl #ss$_ivdevnam,r0 ; ... failed brw 90$ ; get out of here! ; 20$: ; check o.k. movl mbx_ucb,ucb_l_ztmbx(r5) ; .if ne EVAX bicl #ucb$m_valid,ucb$l_sts(r5) bisl #ucb$m_online,ucb$l_sts(r5) .iff bicw #ucb$m_valid,ucb$w_sts(r5) bisw #ucb$m_online,ucb$w_sts(r5) .endc ; calls #0,zt_int ; interrupt unconditionally, ; to clear potential hang ; movl #1,r0 90$: ret ; ; ;***** kernel mode shutdown ; called via $CMKRNL ; .if ne EVAX k_shut: .call_entry preserve= .iff .psect _nonpagedcode k_shut: .word ^m .endc ; movab g^lib$sig_to_ret,(fp) ; movl zt_ucb,r5 beql 90$ ; clrl ucb_l_ztmbx(r5) .if ne EVAX bicl #,ucb$l_sts(r5) .iff bicw #,ucb$w_sts(r5) .endc ; ; ggf. terminate i/o ; .if ne EVAX bicl #,- zt_msgbuf+zt_l_ucbsts ;just in case ... .iff bicw #,- zt_msgbuf+zt_w_ucbsts ;just in case ... .endc ; calls #0,zt_int ;interrupt unconditionally ; 90$: movl #1,r0 ret ; ; ;***** kernel mode data transfer server -> driver's user ; called via $CMKRNL ; implicit inputs: ; - 'zt_ucb' ; - data in 'buffer' ; - byte count in 'bufbct' ; implicit outputs: ; - ucb$w_bcnt cleared ; .if ne EVAX k_touser: .call_entry preserve= .iff .psect _nonpagedcode k_touser: .word ^m .endc ; movab g^lib$sig_to_ret,(fp) ; clrl r0 movab buffer,r1 ; 'from' address movzwl bufbct,r2 ; byte count movl zt_ucb,r5 beql 90$ .if ne EVAX cmpl r2,ucb$l_bcnt(r5) ;sanity check bgtr 90$ ;br if it failed .iff cmpw r2,ucb$w_bcnt(r5) ;sanity check bgtru 90$ ;br if it failed .endc ; .if eq smp_code dsbint ucb$b_fipl(r5) ;; .iff ;; .if ne EVAX ;; forklock - ;; lock=ucb$b_flck(r5),- ;; savipl=-(sp),- ;; preserve=NO ;; .iff ;; forklock - ;; lock=ucb$b_flck(r5),- ;; savipl=-(sp),- ;; preserve=NO,- ;; fipl=YES ;; .endc ;; .endc ;; ;; tstl r2 ;; bleq 28$ ;; br if nothing to be moved! jsb g^ioc$movtouser ;; 28$: ;; ;; .if eq smp_code ;; enbint ;; .iff ;; forkunlock - ;; lock=ucb$b_flck(r5),- ;; newipl=(sp)+,- ;; preserve=NO ;; .endc ; .if ne EVAX clrl ucb$l_bcnt(r5) ;make sure we don't copy twice .iff clrw ucb$w_bcnt(r5) ;make sure we don't copy twice .endc ; movl #1,r0 90$: ret ; ; ;***** kernel mode data transfer, driver's user -> server ; called via $CMKRNL ; implicit inputs: ; - 'zt_ucb' ; - byte count in 'zt_msgbuf+zt_w_bnct' ; implicit outputs: ; - data in 'buffer' ; - byte count in 'bufbct' ; - ucb$w_bcnt cleared ; .if ne EVAX k_fruser: .call_entry preserve= .iff .psect _nonpagedcode k_fruser: .word ^m .endc ; movab g^lib$sig_to_ret,(fp) ; clrl r0 clrl bufbct movl zt_ucb,r5 beql 90$ ; movab buffer,r1 ;'to' address .if ne EVAX movl zt_msgbuf+zt_l_bcnt,r2 ;byte count cmpl r2,ucb$l_bcnt(r5) ;sanity check bgtr 90$ ;br if it failed .iff movzwl zt_msgbuf+zt_w_bcnt,r2 ;byte count cmpw r2,ucb$w_bcnt(r5) ;sanity check bgtru 90$ ;br if it failed .endc movl r2,bufbct ; .if eq smp_code dsbint ucb$b_fipl(r5) ;; .iff ;; .if ne EVAX ;; forklock - ;; lock=ucb$b_flck(r5),- ;; savipl=-(sp),- ;; preserve=NO ;; .iff ;; forklock - ;; lock=ucb$b_flck(r5),- ;; savipl=-(sp),- ;; preserve=NO,- ;; fipl=YES ;; .endc ;; .endc ;; ;; tstl r2 ;; bleq 28$ ;; br if nothing to be moved! jsb g^ioc$movfruser ;; 28$: ;; ;; .if eq smp_code ;; enbint ;; .iff ;; forkunlock - ;; lock=ucb$b_flck(r5),- ;; newipl=(sp)+,- ;; preserve=NO ;; .endc ; .if ne EVAX clrl ucb$l_bcnt(r5) ;make sure we don't copy twice .iff clrw ucb$w_bcnt(r5) ;make sure we don't copy twice .endc ; movl #1,r0 90$: ret ; ; ;***** kernel mode copy of 'result' to driver & activate driver ; called via $CMKRNL ; implicit inputs: ; - 'zt_ucb' ; - 'result' ; .if ne EVAX k_reqcom: .call_entry preserve= .iff .psect _nonpagedcode k_reqcom: .word ^m .endc ; movab g^lib$sig_to_ret,(fp) ; clrl r0 movl zt_ucb,r5 beql 90$ ; movc3 #zt_msglen,zt_msgbuf,ucb_a_ztmsg(r5) ;copy back ; calls #0,zt_int ;'interrupt' driver ; movl #1,r0 90$: ret ; ; .if ne EVAX $locked_page_end ;===== nonpaged routines end here ===== .endc ; ; ;***** get mailbox & its name ; .psect _data ; mbx_dvi_itm: .word mbx_name_size .word dvi$_fulldevnam .long mbx_name .long mbx_namedsc .long 0 ; .align quad mbxiosb: .blkw 4 ; mbxchan: .blkw ; ; .psect _code get_mbx: .if ne EVAX .call_entry .iff .word ^m<> .endc ; $crembx_s prmflg=#0,- chan=mbxchan,- maxmsg=#zt_msglen,- -; ??? bufquo=#zt_msglen,- promsk=#0 chkr0 ; $getdvi_s chan=mbxchan,- itmlst=mbx_dvi_itm chkr0 ; ret ; ; ;***** start mbx read ; .psect _code read_mbx: .if ne EVAX .call_entry .iff .word ^m<> .endc ; $qio_s efn=#1,- chan=mbxchan,- func=#io$_readvblk,- iosb=mbxiosb,- p1=zt_msgbuf,- p2=#zt_msglen chkr0 ; ret ; ; ;***** wait for mbx message ; .psect _code wait_mbx: .if ne EVAX .call_entry .iff .word ^m<> .endc ; $synch_s efn=#1,- iosb=mbxiosb chkr0 ; movzwl mbxiosb,r0 chkr0 ; cmpw mbxiosb+2,#zt_msglen beql 50$ clrl r0 chkr0 50$: ret ; ; ;***** exit handler ; ; .psect _data curprivs: .quad 0 ; ; .psect _code exith: .if ne EVAX .call_entry .iff .word ^m<> .endc ; $setprv_s enbflg=#1,- ;establish initial privileges prvadr=curprivs ;(sometimes removed by rundown) chkr0 ; .if ne EVAX $cmkrnl_s routin=k_shut .iff $cmkrnl_s routin=k_shut,- arglst=(ap) .endc chkr0 ; ret ; ; ;***** setup exit handler ; .psect _data ; .align long exstat: .blkl exblk: .long 0-0,exith,1,exstat ; ; .psect _code setup_exith: .if ne EVAX .call_entry .iff .word ^m<> .endc ; $dclexh_s desblk=exblk chkr0 ; ret ; .page ;***** externalized routines & data ; .psect _nonpageddata .align quad ZT_BUFDSC:: bufbct: .long 0-0,buffer ; ; .psect _data .align quad ZT_MSGDSC:: .long zt_msglen,zt_msgbuf ; ; ;***** initialize ; .psect _data ; .align quad lkwdata_inadr: .long nonpageddata,nonpageddata_end .if ne EVAX .iff lkwcode_inadr: .long nonpagedcode,nonpagedcode_end .endc .align quad zta0_dsc: .ascid "_ZTA0:" ; ; .psect _code .entry ZT_INIT,^m ; ; 4(ap) - address of descriptor of ZT device name (optional argument) ; tstb (ap) ; argument present? beql 25$ ; br if no tstl 4(ap) beql 25$ ; br if no .if ne EVAX ; avoid 'not naturally aligned' warning movaq @4(ap),r0 ; yes, fetch it movl 4(r0),r1 movl (r0),r0 .iff movq @4(ap),r0 ; yes, fetch it .endc brb 30$ 25$: movq zta0_dsc,r0 ; load default descriptor 30$: cmpw r0,#zt_name_size ; length ok? bleq 35$ ; br if yes movl #ss$_ivdevnam,r0 ; no, device name too long ... chkr0 ; ... abort! 35$: movw r0,zt_namedsc ; store length movc3 r0,(r1),zt_name ; copy ZT name ; $setprv_s prvprv=curprivs ; save current privileges chkr0 ; $lkwset_s inadr=lkwdata_inadr ; lock "nonpaged" data chkr0 ; .if ne EVAX $lock_page_init error=lock_page_error .iff $lkwset_s inadr=lkwcode_inadr ; lock "nonpaged" code .endc chkr0 ; calls #0,get_mbx ; create mbx etc. chkr0 ; calls #0,read_mbx ; post read on mbx chkr0 ; calls #0,setup_exith ; set up exit handler chkr0 ; .if ne EVAX $cmkrnl_s routin=k_setup ; start ZT: .iff $cmkrnl_s routin=k_setup,- ; start zt: arglst=(ap) .endc ret ; .if ne EVAX lock_page_error: pushl r0 calls #1,g^lib$stop ret .endc ; ; ;***** wait for MBX ; .psect _code .entry ZT_WAIT,^m<> ; calls #0,wait_mbx ; ret ; ; ;***** move data from 'buffer' to user ; .psect _code .entry ZT_TOUSER,^m<> ; .if ne EVAX $cmkrnl_s routin=k_touser .iff $cmkrnl_s routin=k_touser,- arglst=(ap) .endc ret ; ; ;***** move data from user to 'buffer' ; .psect _code .entry ZT_FRUSER,^m<> ; .if ne EVAX $cmkrnl_s routin=k_fruser .iff $cmkrnl_s routin=k_fruser,- arglst=(ap) .endc ret ; ; ;***** complete I/O ; .psect _code .entry ZT_REQCOM,^m<> ; calls #0,read_mbx ; post read on mbx chkr0 ; .if ne EVAX $cmkrnl_s routin=k_reqcom .iff $cmkrnl_s routin=k_reqcom,- arglst=(ap) .endc ret ; .page ;***** the end ; .psect _nonpageddata ; buffer: ;the data buffer .blkb ^xFFFF ;i.e. 64k-1 ; nonpageddata_end: .blkb ; .if ne EVAX .iff .psect _nonpagedcode nonpagedcode_end: .blkb .endc ; .end