+-+-+-+ Beginning of part 4 +-+-+-+ X`009`009`009n_buf.seq = no_seq++; X`009`009`009n_buf.func = ZTNS_F_IOREPLY; X`009`009`009n_buf.iosb.status = dmasts; X X`009`009`009CHECK(sys$qiow(0,n_chan,IO$_WRITEVBLK, X`009`009`009`009&n_iosb,0,0, X`009`009`009`009&n_buf,ZTNS_LEN1,0,0,0,0)); X`009`009`009CHECK(n_iosb.status); X X`009`009`009datap = NULL; X`009`009`125 X`009`009break; X`009`125 X `125 X`125 $ GOSUB UNPACK_FILE $ FILE_IS = "ZTDEF.MAR" $ CHECKSUM_IS = 1801324618 $ COPY SYS$INPUT VMS_SHARE_DUMMY.DUMMY X.macro`009ztdef X; X; driver/server communication area: X;`009sent to server via MBX, X;`009copied back by server X; X`009$defini`009ZT X.if ne EVAX X$def`009zt_l_media`009.blkl 2`009; IRP$L_MEDIA,.+4 (Q-align)`009d -> s X$def`009zt_l_devdepend`009.blkl`009; UCB$W_DEVDEPEND = IOSB+4 `009d -> s -> d X$def`009zt_l_record`009.blkl`009; UBC$L_RECORD`009`009`009d -> s -> d X$def`009zt_l_ucbsts`009.blkl`009; UCB$L_STS`009`009`009d -> s -> d (*) X`009`009`009`009; `009(*) only UCB$M_VALID to be copied back X$def`009zt_l_devchar`009.blkl`009; UCB$L_DEVCHAR`009`009`009d -> s X$def`009zt_l_func`009.blkl`009; IRP$L_FUNC`009`009`009d -> s X$def`009zt_l_bcnt`009.blkl`009; UCB$L_BCNT`009`009`009d -> s X$def`009zt_l_iosts`009.blkl`009; IOSB+0 (in low word)`009`009[d ->] s -> d X$def`009zt_l_iobct`009.blkl`009; IOSB+2`009`009`009[d ->] s -> d X.iff X$def`009zt_w_iosts`009.blkw`009; IOSB+0`009`009`009[d ->] s -> d X$def`009zt_w_iobct`009.blkw`009; IOSB+2`009`009`009[d ->] s -> d X$def`009zt_l_devdepend`009.blkl`009; UCB$W_DEVDEPEND = IOSB+4 `009d -> s -> d X$def`009zt_l_record`009.blkl`009; UBC$L_RECORD`009`009`009d -> s -> d X$def`009zt_w_ucbsts`009.blkw`009; UCB$W_STS`009`009`009d -> s -> d (*) X`009`009`009`009; `009(*) only UCB$M_VALID to be copied back X$def`009zt_w_fill1`009.blkw`009; "reserved" X$def`009zt_l_devchar`009.blkl`009; UCB$L_DEVCHAR`009`009`009d -> s X$def`009zt_w_func`009.blkw`009; IRP$W_FUNC`009`009`009d -> s X$def`009zt_w_bcnt`009.blkw`009; UCB$W_BCNT`009`009`009d -> s X$def`009zt_l_media`009.blkl`0092 ; IRP$L_MEDIA`009`009`009d -> s X.endc X$def`009zt_msglen X`009$defend`009ZT X; X`009$defini`009ZTUCB,dot=ucb$k_lcl_tape_length X;data cells known to server X$def`009ucb_l_inter`009.blkl`009; pointer to pseudo-interrupt routine X$def`009ucb_l_ztmbx`009.blkl`009; pointer to server's mailbox UCB X; X$def`009ucb_a_ztmsg`009.blkb `009zt_msglen`009; above data X$def`009ucb_k_ztend X`009$defend`009ZTUCB X; X.endm`009ztdef $ GOSUB UNPACK_FILE $ FILE_IS = "ZTNS.H" $ CHECKSUM_IS = 290012993 $ COPY SYS$INPUT VMS_SHARE_DUMMY.DUMMY X/* definitions for ZT<->ZTnetserver protocol */ X Xtypedef struct IOSB `123 X`009unsigned short status; X`009unsigned short count; X`009unsigned int devdep; X`125 IOSB; X X X/***** ZTNS function codes *****/ X X#define ZTNS_F_IOREQ`0091`009/* ZT -> NS: iofunc + count`009`009*/ X#define ZTNS_F_IOREPLY`0092`009/* NS -> ZT: iosb,`009`009`009*/ X`009`009`009`009/* ZT -> NS: iosb.status only`009`009*/ X`009`009`009`009/*`009`009(after CMP & PUT)`009*/ X#define ZTNS_F_DATA`0093`009/* 'DMA' data (both directions): count`009*/ X#define ZTNS_F_R_DATA`0094`009/* NS -> ZT: accept 'DMA' data`009*/ X#define ZTNS_F_W_DATA`0095`009/* NS -> ZT: send 'DMA' data`009*/ X#define ZTNS_F_C_DATA`0096`009/* NS -> ZT: rcv.&check 'DMA' data */ X X X/***** ZTNS network messages *****/ X X#define ZTNS_DATALEN 1400`009/* max. #bytes transferred in 1 message */ X`009`009`009`009/* ? ought to be a single Ethernet packet ? */ X Xtypedef struct ZTNS_MSG `123 X`009int seq;`009`009/* sequence number, starts at 1 */ X`009unsigned short func;`009/* ZTNS_F_xxxx */ X`009unsigned short iofunc;`009/* used by:`009IOREQ, IO$_xxx + modifiers */ X`009int count;`009`009/* used by:`009IOREQ (signed!),`032 X`009`009`009`009`009`009xxxDATA (total cnt), X`009`009`009`009`009`009DATA (#bytes in 'data') */ X`009IOSB iosb;`009`009/* used by:`009IOREPLY */ X`009unsigned char data[ZTNS_DATALEN];`009/* used with DATA only */ X`125 ZTNS_MSG; X X#define ZTNS_LEN1 (sizeof(ZTNS_MSG) - ZTNS_DATALEN)`009/* short packet */ X X/* note: length of a DATA packet must be ZTNS_LEN1 + (count) */ $ GOSUB UNPACK_FILE $ FILE_IS = "ZTNS2.C" $ CHECKSUM_IS = 2084655331 $ COPY SYS$INPUT VMS_SHARE_DUMMY.DUMMY X/* serve real tape, for ZT2/ZTserver */ X/* w.j.m. jun 1989 X * mod 03-feb-1995 wjm: Try to always return a valid 2nd IOSB longword; X *`009`009`009Add checks around sys$hiber(); X *`009`009`009Support known IO$_DISPLAY functions via standard output, X *`009`009`009also send them to the tape driver (will be ignored X *`009`009`009without PHY_IO privilege) X * Note 07-mar-1995 wjm: IO$_DISPLAY doesn't work for a foreign mounted, X *`009`009`009but offline tape (MSCP TA90, VMS V5.5-2) - X *`009`009`009IOSB status: SS$_VOLINV. IO$_PACKACK won't help. X *`009`009`009So even with PHY_IO, this program may be unable X *`009`009`009to physically display a "MOUNT XXXXXX" request. X */ X X/* implicit inputs: X`009logical name ZT_TAPE points to a tape unit mounted foreign X`009logical name ZT_NETOBJECT has equivalence `096node::"0=name/' X*/ X X#define T_LOGNAME "ZT_TAPE" X#define N_LOGNAME "ZT_NETOBJECT" X X#include "ztns.h"`009/* includes IOSB */ X X#include ssdef X#include lnmdef X#include dvidef X#include "iodef.h"`009/* my own! */ X#include "mtdef.h" X X#ifndef IO$_DISPLAY X#define IO$_DISPLAY 19 X#endif X X#include stdio X#include stddef X#include string X#include assert X#include descrip Xtypedef struct dsc$descriptor DESCR; X X#define CHECK(x) do `123unsigned s=x; if(!(s&1)) lib$stop(s);`125 while(0) X#define FEHLER(m) do `123$DESCRIPTOR(d,m); Fehler(&d);`125 while(0) X#define CHECKX(x) do `123unsigned s=x; if(!(s&1)) sys$exit(s);`125 while(0) X#define MIN(a,b) ((a) < (b)) ? (a) : (b) X Xextern unsigned lib$get_foreign(),lib$stop(),lib$getdvi(); Xextern unsigned sys$exit(),sys$assign(),sys$wake(),sys$hiber(),sys$trnlnm(); Xextern unsigned sys$qiow(int,int,int, X`009`009`009IOSB*,void(*)(),int, X`009`009`009void*,int,int,int,int,int); Xextern unsigned sys$qio(int,int,int, X`009`009`009IOSB*,void(*)(),int, X`009`009`009void*,int,int,int,int,int); X Xtypedef struct VMS_ITEM `123 X`009unsigned short size; X`009unsigned short code; X`009void *bufp; X`009unsigned short *lenp; X`125 VMS_ITEM; X X X/***** tape data *****/ Xstatic IOSB t_iosb; Xstatic short t_chan; Xstatic unsigned char t_buffer[0xFFFF]; X Vstatic unsigned short ti_iofunc = 0;`009/* request function, !=0 while busy * X/ Xstatic int ti_count;`009`009`009/* request count */ Xstatic struct `123 X`009unsigned char ub[8]; X`125 ti_media;`009`009`009`009/* request parameter(s) */ X Xstatic unsigned char *ti_datap = NULL;`009/* input 'DMA' pointer, X`009`009`009`009`009`009==NULL unless data expected */ Xstatic int ti_datacnt = 0;`009`009/* input 'DMA' counter */ X Xstatic int/*logical*/ ti_replyexp = 0;`009/* IOREPLY expected */ Xstatic unsigned short ti_replysts;`009/* status from same */ X X/***** network data *****/ Xstatic short n_chan; X X`009/* ZT -> NS */ Xstatic IOSB ni_iosb; Xstatic ZTNS_MSG ni_buf; Xstatic int ni_seq = 1; X X`009/* NS -> ZT (our output) */ Xstatic IOSB no_iosb; Xstatic ZTNS_MSG no_buf; Xstatic int no_seq = 1; X X X/*****/ X Xstatic void net_ast();`009`009/*forward*/ X Xstatic void net_read() X`123 X`009CHECKX(sys$qio(1,n_chan,IO$_READVBLK, X`009`009&ni_iosb,net_ast,0, X`009`009&ni_buf,sizeof(ni_buf),0,0,0,0)); X`125 X X Xstatic void net_send(fct) Xint fct;`009/* ZTNS_F_xxxx */ X`123 X`009no_buf.seq = no_seq++; X`009no_buf.func = fct; X X`009CHECKX(sys$qiow(0,n_chan,IO$_WRITEVBLK, X`009`009&no_iosb,0,0, X`009`009&no_buf, X`009`009((fct == ZTNS_F_DATA) ? (ZTNS_LEN1 + no_buf.count) : ZTNS_LEN1), X`009`0090,0,0,0)); X`009CHECKX(no_iosb.status); X`125 X X Xstatic void net_start() X`123 X`009char ncb[512]; X`009DESCR ncb_dsc = `1230-0,0,0,ncb`125; X X X/* translate n_logname */ X X`009`123 X`009`009$DESCRIPTOR(lnmtabdsc,"LNM$FILE_DEV"); X`009`009$DESCRIPTOR(lognamedsc,N_LOGNAME); X`009`009VMS_ITEM lnmlist[] = X`009`009`009`123`123sizeof(ncb),LNM$_STRING,ncb,&ncb_dsc.dsc$w_length`125, X`009`009`009 `1230,0,NULL,NULL`125`125; X X`009`009CHECK(sys$trnlnm(0,&lnmtabdsc,&lognamedsc,0,lnmlist)); X`009`125 X X/* assign channel to NET */ X X`009`123 X`009`009$DESCRIPTOR(netdsc,"_NET:"); X X`009`009CHECK(sys$assign(&netdsc,&n_chan,0,0)); X`009`125 X X/* open connection */ X X`009if(ncb_dsc.dsc$w_length+2+1+16+1 > sizeof(ncb)) `123 X`009`009FEHLER("ncb too small"); X`009`125 X X`009/* clear conn.id & ncb info */ X`009memset(ncb+ncb_dsc.dsc$w_length,0,2+1+16); X`009ncb_dsc.dsc$w_length += 2+1+16; X X`009/* add trailing quotemark */ X`009ncb[ncb_dsc.dsc$w_length] = '\"'; X`009ncb_dsc.dsc$w_length += 1; X X`009CHECK(sys$qiow(0,n_chan,IO$_ACCESS, X`009`009`009&no_iosb,0,0, X`009`009`0090,(int) (&ncb_dsc),0,0,0,0)); X`009CHECK(no_iosb.status); X X X/* start async. read from net */ X X`009net_read(); X`125 X X Xstatic void net_ast()`009/* here when we receive net msg */ X`123 X/* check I/O */ X`009CHECKX(ni_iosb.status); X X/* check seq# */ X`009if(ni_buf.seq != ni_seq++) FEHLER("prot.error: iseq"); X X/* check minimum length */ X`009if(ni_iosb.count < ZTNS_LEN1) FEHLER("prot.error: msg too short"); X X/* process low level function */ X`009switch(ni_buf.func) `123 X`009 default: X`009`009FEHLER("prot.error: bad func1"); X`009`009return; X X`009 case ZTNS_F_IOREPLY: X`009`009if(ti_replyexp == 0) `123 X`009`009`009FEHLER("prot.error: unexpected IOREPLY"); X`009`009`125 X`009`009ti_replyexp = 0; X`009`009ti_replysts = ni_buf.iosb.status; X`009`009CHECK(sys$wake(0,0)); X`009`009break; X X`009 case ZTNS_F_IOREQ: X`009`009if(ti_iofunc != 0) `123`009/* still busy ... */ X`009`009`009FEHLER("prot.error: unexpected IOREQ"); X`009`009`125 X`009`009ti_iofunc = ni_buf.iofunc; X`009`009ti_count = ni_buf.count; X`009`009assert(sizeof(ti_media) == 8); X`009`009assert(sizeof(ni_buf.iosb) == 8); X`009`009memcpy(&ti_media,&ni_buf.iosb,8);`009/* for DISPLAY */ X`009`009CHECK(sys$wake(0,0)); X`009`009break; X X`009 case ZTNS_F_DATA: X`009`009if(ti_datap == NULL) `123 X`009`009`009FEHLER("prot.error: unexpected DATA"); X`009`009`125 X`009`009if(ni_iosb.count != ZTNS_LEN1 + ni_buf.count) `123 X`009`009`009FEHLER("prot.error: bad DATA msg length"); X`009`009`125 X`009`009if(ni_buf.count > ti_datacnt) `123 X`009`009`009FEHLER("prot.error: too much data"); X`009`009`125 X X`009`009memcpy(ti_datap,ni_buf.data,ni_buf.count); X`009`009ti_datacnt -= ni_buf.count; X`009`009ti_datap += ni_buf.count; X X`009`009if(ti_datacnt == 0) `123`009/* data transfer complete */ X`009`009`009ti_datap = NULL; X`009`009`009CHECK(sys$wake(0,0)); X`009`009`125 X`009`009break; X`009`125 X X/* wait for next msg */ X`009net_read(); X`125 X X X/*****/ X Xstatic unsigned int get_devdep(int chan)`009/* get UCB$L_DEVDEPEND */ X`123 X`009unsigned int devdep, item = DVI$_DEVDEPEND; X X`009CHECK(lib$getdvi(&item,&chan,0,&devdep)); X X`009return devdep; X`125 X X/*****/ X Xstatic void tap_invfun()`009/* unsupported function or modifier */ X`123 X`009t_iosb.status = SS$_ILLIOFUNC; X`009t_iosb.count = 0; X`009t_iosb.devdep = get_devdep(t_chan); X`125 X X Xstatic void tap_0par()`009`009/* REWIND, REWINDOFF, WRITEOF */ X`123 X`009CHECK(sys$qiow(0,t_chan,ti_iofunc, X`009`009`009&t_iosb,0,0, X`009`009`0090,0,0,0,0,0)); X`125 X X Xstatic void tap_display()`009/* somewhat reduced functionality, X`009`009`009`009 * have to guess at actual parameters */ X`123 X`009static $DESCRIPTOR(mountdsc," Mount "); /* fake MOUN$_MOUNT text */ X`009static DESCR labeldsc = `1236,0,0,&ti_media.ub[2]`125; X`009unsigned int itemcode,mode,list[3],p1,sts; X X`009itemcode = ti_media.ub[0]; X`009mode = ti_media.ub[1]; X`009 X`009if(itemcode == 0 `124`124 (itemcode == 1 && mode == 0)) `123 X`009`009p1 = 0; X X`009`009if(itemcode != 0) printf("DISPLAY - Cleared\n"); X X`009`125 else if(itemcode == 1 && mode == 2) `123 X`009`009list[0] = 2; X`009`009list[1] = (unsigned int)(&labeldsc); X`009`009list[2] = (unsigned int)(&mountdsc); X`009`009p1 = (unsigned int)(&list[0]); X X`009`009printf("DISPLAY - Please mount volume `096%.6s'\n", X`009`009`009&ti_media.ub[2]); X X`009`125 else if(itemcode == 1 && mode == 1) `123 X`009`009list[0] = 1; X`009`009list[1] = (unsigned int)(&labeldsc); X`009`009p1 = (unsigned int)(&list[0]); X X`009`009printf("DISPLAY - Volume `096%.6s' mounted\n",&ti_media.ub[2]); X X`009`125 else `123`009`009`009`009/* undefined function ... */ X`009`009t_iosb.status = SS$_BADPARAM; X`009`009t_iosb.count = 0; X`009`009t_iosb.devdep = get_devdep(t_chan); X`009`009return; X`009`125 X X`009sts = sys$qiow(0,t_chan,ti_iofunc,`009/* may return immediately */ X`009`009`009&t_iosb,0,0, X`009`009`009p1,0,itemcode,mode,0,0); X X`009if(!(sts & 1)) `123`009`009/* must fill in t_iosb ourselves */ X`009`009if(sts == SS$_NOPRIV `124`124 X`009`009 sts == SS$_ILLIOFUNC) `123 X`009`009`009t_iosb.status = SS$_NORMAL;`009/* stdout display ok */ X`009`009`125 else `123 X`009`009`009t_iosb.status = sts;`009/* pass unexpected status */ X`009`009`125 X`009`009t_iosb.count = 0; X`009`009t_iosb.devdep = get_devdep(t_chan); X`009`125`009 X`125 X X Xstatic void tap_skiprec()`009/* skip records, forward/backward */ X`123 X`009CHECK(sys$qiow(0,t_chan,IO$_SKIPRECORD, X`009`009`009&t_iosb,0,0, X`009`009`009(void *) ti_count,0,0,0,0,0)); X X/* must undo "end-of-volume" recognition */ X`009if(t_iosb.status == SS$_ENDOFVOLUME) `123 X`009`009unsigned char dummybuf[80]; X X`009`009if(t_iosb.count != 0) FEHLER("skipcount !=0 on e.o.v."); X X`009`009CHECK(sys$qiow(0,t_chan,IO$_READVBLK, X`009`009`009`009&t_iosb,0,0, X`009`009`009`009dummybuf,sizeof(dummybuf),0,0,0,0)); X`009`009if(t_iosb.status != SS$_ENDOFFILE) `123 X`009`009`009/* tape position lost since EOF must be next */ X`009`009`009t_iosb.status = SS$_TAPEPOSLOST; X`009`009`125 X`009`009t_iosb.count = 1; X`009`125 X`125 X X Xstatic void tap_read_wchk(wchk)`009/* wchk==0: read, possibly w/datacheck */ Xint wchk;`009`009`009/* wchk==1: simulated writecheck */ X`123 X`009CHECK(sys$qiow(0,t_chan,wchk ? IO$_READLBLK : ti_iofunc, X`009`009`009&t_iosb,0,0, X`009`009`009t_buffer,ti_count,0,0,0,0)); X`009if(t_iosb.count > 0) `123 X`009`009int bcnt,c; X`009`009unsigned char *bpt; X X`009`009no_buf.count = bcnt = MIN(t_iosb.count,ti_count); X`009`009net_send(wchk ? ZTNS_F_C_DATA : ZTNS_F_R_DATA); X`009`009bpt = t_buffer; X`009`009do `123 X`009`009`009no_buf.count = c = MIN(bcnt,ZTNS_DATALEN); X`009`009`009bcnt -= c; X`009`009`009memcpy(no_buf.data,bpt,c); X`009`009`009bpt += c; X`009`009`009if(bcnt == 0) `123`009/* last message */ X`009`009`009`009ti_replyexp = 1; X`009`009`009`125 X`009`009`009net_send(ZTNS_F_DATA); X`009`009`125 while(bcnt > 0); X`009`009do `123 X`009`009`009CHECK(sys$hiber());`009/* wait for reply */ X`009`009`125 while(ti_replyexp != 0); X X`009`009if(wchk) `123`009/* CMP may have error = SS$_DATACHECK */ X`009`009`009if((ti_replysts & 1) == 0) `123 X`009`009`009`009t_iosb.status = ti_replysts; X`009`009`009`125 X`009`009`125 else `123`009/* GET must be o.k. */ X`009`009`009CHECKX(ti_replysts); X`009`009`125 X`009`125 X`125 X X Xstatic void tap_write()`009`009/* write, possibly w/datacheck */ X`123 X`009if(ti_count > 0) `123 X`009`009no_buf.count = ti_datacnt = ti_count; X`009`009ti_datap = t_buffer; X`009`009net_send(ZTNS_F_W_DATA); X X`009`009do `123 X`009`009`009CHECK(sys$hiber());`009/* wait for data */ X`009`009`125 while(ti_datap != NULL); X`009`125 X X`009CHECK(sys$qiow(0,t_chan,ti_iofunc, X`009`009`009&t_iosb,0,0, X`009`009`009t_buffer,ti_count,0,0,0,0)); X`125 X X Xstatic unsigned ztns()`009`009/* the action routine */ X`123 X`009int/*logical*/ ok = 1; X`009unsigned fcode,fmodif; X X X`009while(ok) `123 X X/* sleep waiting for function */ X`009`009do `123 X`009`009`009CHECK(sys$hiber()); X`009`009`125 while(ti_iofunc == 0); X X/* check & dispatch function */ X`009`009fcode = ti_iofunc & IO$M_FCODE; X`009`009fmodif = ti_iofunc & IO$M_FMODIFIERS; X`009`009switch(fcode) `123 X X`009`009 case IO$_WRITEOF:`009/* no modifiers */ X`009`009`009if(fmodif != 0) `123 X`009`009`009`009tap_invfun(); X`009`009`009`125 else `123 X`009`009`009`009tap_0par(); X`009`009`009`125 X`009`009`009break; X X`009`009 case IO$_REWIND:`009/* mod: NOWAIT */ X`009`009 case IO$_REWINDOFF:`009/* mod: NOWAIT */ X`009`009`009if((fmodif & `126IO$M_NOWAIT) != 0) `123 X`009`009`009`009tap_invfun(); X`009`009`009`125 else `123 X`009`009`009`009tap_0par(); X`009`009`009`125 X`009`009`009break; X X`009`009 case IO$_SKIPRECORD:`009/* no modifiers */ X`009`009`009if(fmodif != 0) `123 X`009`009`009`009tap_invfun(); X`009`009`009`125 else `123 X`009`009`009`009tap_skiprec(); X`009`009`009`125 X`009`009`009break; X X`009`009 case IO$_READLBLK:`009/* mod: datacheck */ X`009`009`009`009`009/* note: datacheck is not end-to-end */ X`009`009`009if((fmodif & `126IO$M_DATACHECK) != 0) `123 X`009`009`009`009tap_invfun(); X`009`009`009`125 else `123 X`009`009`009`009tap_read_wchk(0); X`009`009`009`125 X`009`009`009break; X X`009`009 case IO$_WRITELBLK:`009/* mod: datacheck */ X`009`009`009`009`009/* note: datacheck is not end-to-end */ X`009`009`009if((fmodif & `126IO$M_DATACHECK) != 0) `123 X`009`009`009`009tap_invfun(); X`009`009`009`125 else `123 X`009`009`009`009tap_write(); X`009`009`009`125 X`009`009`009break; X X`009`009 case IO$_WRITECHECK:`009/* no modifiers */ X`009`009`009`009`009/* note: this PHYSICAL I/O,`032 X`009`009`009`009`009simulated as end-to-end check */ X`009`009`009if(fmodif != 0) `123 X`009`009`009`009tap_invfun(); X`009`009`009`125 else `123 X`009`009`009`009tap_read_wchk(1); X`009`009`009`125 X`009`009`009break; X X`009`009 case IO$_DISPLAY:`009/* no modifiers */ X`009`009`009`009`009/* reduced data come in IOSB (dirty!) */ X`009`009`009if(fmodif != 0) `123 X`009`009`009`009tap_invfun(); X`009`009`009`125 else `123 X`009`009`009`009tap_display(); X`009`009`009`125 X`009`009`009break; X X`009`009 default: X`009`009`009tap_invfun(); X`009`009`125 X X/* fudge */ X`009`009if(t_iosb.status == SS$_VOLINV) `123`009 X`009`009`009/* 'real' VALID bit apparently clear, X`009`009`009/* since we can't change that, X`009`009`009/* say MEDOFL (manual intervention required) */ X`009`009`009t_iosb.status = SS$_MEDOFL; X`009`009`125 X X/* say we're done */ X`009`009ti_iofunc = 0; X X/* return result from t_iosb */ X`009`009no_buf.iosb = t_iosb; X`009`009net_send(ZTNS_F_IOREPLY); X`009`125 X X`009return 1;`009/* not reached */ X`125 X X`009`009 X/*****/ X Xmain(int argc, char **argv) X`123 X`009static $DESCRIPTOR(tape_dsc,T_LOGNAME); X X X#ifdef VMS X`009redirect(&argc,&argv); X#endif X X X/* assign channel to tape */ X`009CHECK(sys$assign(&tape_dsc,&t_chan,0,0)); X X/* rewind tape. this MUST succeed, otherwise something is wrong */ X/* in particular, this checks that the tape is mounted foreign */ X`009CHECK(sys$qiow(0,t_chan,IO$_REWIND, X`009`009`009&t_iosb,0,0, X`009`009`0090,0,0,0,0,0)); X`009CHECK(t_iosb.status); X X/* start the network connection & remote ZTserver */ X`009net_start(); X X/* do our work ... */ X X`009return ztns(); X`125 $ GOSUB UNPACK_FILE $ FILE_IS = "ZTSERVER.MAR" $ CHECKSUM_IS = 1748850598 $ COPY SYS$INPUT VMS_SHARE_DUMMY.DUMMY X`009.title`009ZTSERVER subroutines X; X;`009w.j.m. jun 1989 (0.2) X;`009documented 3-jul-1989 wjm X;`009mod 18-aug-1989 wjm (0.9): support VMS V5 X;`009mod 22-oct-1993 wjm (0.99A): port to AXP VMS 1.5 (needs EVAX defined) X;`009mod 29-jan-1994 wjm (0.99B): automatically defined EVAX; optionally X;`009`009`009`009`009pass name of ZT device on ZT_INIT() X; X`009.ident`009/0.99B/`009`009; ZTDRIVER better had the same ident! X; X;***** X; entries: X;`009status=ZT_INIT(zt_name)`009- initialize, ... X;`009`009`009`009... set up communication with ZTDRIVER. X;`009`009`009`009NOTE: an exit handler is set up, X;`009`009`009`009`009which MUST be executed eventually! X;`009character*(*) zt_name`009- optional argument (fixed length descriptor), X;`009`009`009`009`009defaults to "_ZTA0:" X; X;`009status=ZT_WAIT()`009- wait for message (I/O request) from ZTDRIVER. X;`009`009`009 On return, the 'message buffer' has been filled in. X; X;`009status=ZT_REQCOM()`009- send (part of) 'message buffer' X;`009`009`009to ZTDRIVER and complete I/O. X; X;`009status=ZT_TOUSER()`009- simulates DMA transfer X;`009`009`009from 'data buffer' to ZTDRIVER's user buffer. X;`009`009`009Number of bytes tranferred: X;`009`009`009`009 MIN(UCB$W_BCNT,ZT_BUFDSC.dsc$w_length) X;`009`009`009Can be called at most once per I/O request! X; X;`009status=ZT_FRUSER()`009- simulates DMA transfer X;`009`009`009from ZTDRIVER's user buffer to 'data buffer'. X;`009`009`009Number of bytes transferred: X;`009`009`009`009UCB$W_BCNT X;`009`009`009On return, ZT_BUFDSC.dsc$w_length is set to this number. X;`009`009`009Can be called at most once per I/O request! X;`009`009`009`009`009 X;`009ZT_MSGDSC [globaldef]:`032 X;`009`009`009Descriptor of 'message buffer' (see ZTDEF.MAR) X;`009`009`009`009used for communication with ZTDRIVER. X;`009`009`009Both buffer address and length are FIXED. X; X;`009ZT_BUFDSC [globaldef]: X;`009`009`009Descriptor of 'data buffer' (see above). X;`009`009`009Buffer address is FIXED, actual size is `094xFFFF . X;`009`009`009ZT_BUFDSC.dsc$w_length is set by ZT_FRUSER(), X;`009`009`009`009and by caller of ZT_TOUSER(). X; X;***** X; X; X.ntype`009__,R31`009`009`009; set EVAX nonzero if R31 is a register X.if eq <__ & `094xF0> - `094x50 XEVAX = 1 X.iff XEVAX = 0 X.endc X; X; X.if ne EVAX X`009.library`009"SYS$LIBRARY:LIB" X`009.library`009"SYS$DISK:[]ZT" X.iff X`009.link`009`009"SYS$SYSTEM:SYS.STB"/selective_search X`009.library`009"SYS$LIBRARY:LIB" X`009.library`009"ZT" X.endc X; X`009$dvidef X`009$iodef X`009$prdef X`009$prvdef X`009$ucbdef X; Xsmp_code=0`009`009`009`009;VMS V4 or earlier X.iif df UCB$L_DLCK, smp_code=1`009`009;VMS V5`032 X; X`009ztdef`009`009; ZT definitions - need $ucbdef X; X; X;*****`009macros X; X.macro`009chkr0`009?lab X.iif ne EVAX,`009.branch_likely X`009blbs`009r0,lab X`009pushl`009r0 X`009calls`009#1,g`094lib$stop Xlab: X.endm`009chkr0 X; X; X;*****`009PSECTs X; X`009.psect`009_data,quad X`009.psect`009_code,nowrt X.if ne EVAX X.iff X`009.psect`009_nonpagedcode,page,nowrt Xnonpagedcode:`009`009`009;start of psect X.endc X`009.psect`009_nonpageddata,page Xnonpageddata:`009`009`009;start of psect X; X`009.page X;*****`009nonpaged data X; X`009.psect`009_nonpageddata X; Xzt_ucb:`009.long`0090 Xmbx_ucb:.long`0090 X; Xzt_name_size=32 Xzt_name: X`009.blkb`009zt_name_size Xmbx_name_size=32 Xmbx_name: X`009.blkb`009mbx_name_size X`009.align`009quad Xzt_namedsc: X`009.long`0090-0 X`009.long`009zt_name Xmbx_namedsc: X`009.long`0090-0 X`009.long`009mbx_name X; X`009.align`009quad Xzt_msgbuf: X`009.byte`0090[zt_msglen] X; X; X;*****`009get ucb from device name X;`009`009called in kernel mode, IPL=0 X;`009`009p1 = addr of descriptor`009`009! must be nonpaged X;`009`009p2 = addr of ucb-address`009! must be nonpaged X; X.if ne EVAX X`009$locked_page_start`009;=====`009nonpaged routines start here ========== X; X; Xdev_to_ucb:`009.call_entry`009preserve= X.iff X`009.psect`009_nonpagedcode Xdev_to_ucb:`009.word`009`094m X.endc X; X`009movl`009g`094CTL$GL_PCB,r4 X; X`009jsb`009g`094SCH$IOLOCKR`009`009;;***** start IPL2 ********************* X`009movaq`009@1*4(ap),r1`009`009;; X`009jsb`009g`094IOC$SEARCHDEV`009`009;;search for device X`009blbc`009r0,90$`009`009`009;; X`009movl`009r1,@2*4(ap)`009`009;;r1=UCB X90$:`009`009`009`009`009;; X`009pushl`009r0`009`009`009;; X`009jsb`009g`094SCH$IOUNLOCK`009`009;; Unlock the I/O database X`009setipl`009#0`009`009`009;;***** end IPL2 *********************** X`009popl`009r0 X; X`009ret X; X; X;*****`009fake ZT interrupt X;`009`009called in kernel mode, IPL=0 X; X.if ne EVAX Xzt_int:`009.call_entry`009preserve= X.iff X`009.psect`009_nonpagedcode Xzt_int:`009.word`009`094m X.endc X; X`009movl`009zt_ucb,r5 X`009beql`00990$ X; X.if eq smp_code X`009dsbint`009ucb$b_dipl(r5)`009`009`009;; X.iff`009`009`009`009`009`009;; X`009devicelock`009-`009`009`009;; X`009`009lockaddr=ucb$l_dlck(r5),-`009;; X`009`009lockipl=ucb$b_dipl(r5),-`009;; X`009`009savipl=-(sp),-`009`009`009;; X`009`009preserve=NO`009`009`009;; X.endc`009`009`009`009`009`009;; X`009`009`009`009`009`009;; X`009jsb`009@ucb_l_inter(r5)`009`009;; X`009`009`009`009`009`009;; X.if eq smp_code`009`009`009`009`009;; X`009enbint`009`009`009`009`009;; X.iff`009`009`009`009`009`009;; X`009deviceunlock`009-`009`009`009;; X`009`009lockaddr=ucb$l_dlck(r5),-`009;; X`009`009newipl=(sp)+,-`009`009`009;; X`009`009preserve=NO`009`009`009;; X.endc X; X90$: X`009movl`009#1,r0 X`009ret X; X; X;*****`009kernel mode setup X;`009`009called via $CMKRNL X; X.if ne EVAX Xk_setup:`009.call_entry`009preserve= X.iff X`009.psect`009_nonpagedcode Xk_setup:`009.word`009`094m X.endc X; X`009movab`009g`094lib$sig_to_ret,(fp) X; X`009pushal`009mbx_ucb X`009pushaq`009mbx_namedsc X`009calls`009#2,dev_to_ucb X`009blbc`009r0,90$ X; X`009pushal`009zt_ucb X`009pushaq`009zt_namedsc X`009calls`009#2,dev_to_ucb X`009blbc`009r0,90$ X; X`009movl`009zt_ucb,r5 X`009cmpl`009ucb$l_media_id(r5),#<`094x6D285010>`009; plausibility check X`009beql`00920$ X`009movl`009#ss$_ivdevnam,r0`009`009`009; ... failed X`009brw`00990$`009`009`009`009`009; get out of here! X; X20$:`009`009`009`009`009`009`009; check o.k. X`009movl`009mbx_ucb,ucb_l_ztmbx(r5) X; X.if ne EVAX X`009bicl`009#ucb$m_valid,ucb$l_sts(r5) X`009bisl`009#ucb$m_online,ucb$l_sts(r5) X.iff X`009bicw`009#ucb$m_valid,ucb$w_sts(r5) X`009bisw`009#ucb$m_online,ucb$w_sts(r5) X.endc X; X`009calls`009#0,zt_int`009`009; interrupt unconditionally, X`009`009`009`009`009; to clear potential hang X; X`009movl`009#1,r0 X90$: X`009ret X; X; X;*****`009kernel mode shutdown X;`009`009called via $CMKRNL X; X.if ne EVAX Xk_shut:`009.call_entry`009preserve= X.iff X`009.psect`009_nonpagedcode Xk_shut:`009.word`009`094m X.endc X; X`009movab`009g`094lib$sig_to_ret,(fp) X; X`009movl`009zt_ucb,r5 X`009beql`00990$ X; X`009clrl`009ucb_l_ztmbx(r5) X.if ne EVAX X`009bicl`009#,ucb$l_sts(r5) X.iff X`009bicw`009#,ucb$w_sts(r5) X.endc X; X; ggf. terminate i/o X; X.if ne EVAX X`009bicl`009#,- X`009`009zt_msgbuf+zt_l_ucbsts`009`009;just in case ... X.iff X`009bicw`009#,- X`009`009zt_msgbuf+zt_w_ucbsts`009`009;just in case ... X.endc X; X`009calls`009#0,zt_int`009`009`009;interrupt unconditionally X; X90$: X`009movl`009#1,r0 X`009ret X; X; X;*****`009kernel mode data transfer server -> driver's user X;`009`009called via $CMKRNL X;`009`009implicit inputs:`032 X;`009`009`009- 'zt_ucb' X;`009`009`009- data in 'buffer' X;`009`009`009- byte count in 'bufbct' X;`009`009implicit outputs: X;`009`009`009- ucb$w_bcnt cleared X; X.if ne EVAX Xk_touser:`009.call_entry`009preserve= X.iff X`009.psect`009_nonpagedcode Xk_touser:`009.word`009`094m X.endc X; X`009movab`009g`094lib$sig_to_ret,(fp) X; X`009clrl`009r0 X`009movab`009buffer,r1`009`009; 'from' address X`009movzwl`009bufbct,r2`009`009; byte count X`009movl`009zt_ucb,r5 X`009beql`00990$ X.if ne EVAX X`009cmpl`009r2,ucb$l_bcnt(r5)`009;sanity check X`009bgtr`00990$`009`009`009;br if it failed X.iff X`009cmpw`009r2,ucb$w_bcnt(r5)`009;sanity check X`009bgtru`00990$`009`009`009;br if it failed X.endc X; X.if eq smp_code X`009dsbint`009ucb$b_fipl(r5)`009`009`009;; X.iff`009`009`009`009`009`009;; X.if ne EVAX`009`009`009`009;; X`009forklock`009-`009`009`009;; X`009`009lock=ucb$b_flck(r5),-`009`009;; X`009`009savipl=-(sp),-`009`009`009;; X`009`009preserve=NO`009`009`009;; X.iff`009`009`009`009`009`009;; X`009forklock`009-`009`009`009;; X`009`009lock=ucb$b_flck(r5),-`009`009;; X`009`009savipl=-(sp),-`009`009`009;; X`009`009preserve=NO,-`009`009`009;; X`009`009fipl=YES`009`009`009;;`032 X.endc`009`009`009`009`009`009;; X.endc`009`009`009`009`009`009;; X`009`009`009`009`009`009;; X`009tstl`009r2`009`009`009`009;; X`009bleq`00928$`009`009`009`009;; br if nothing to be moved! X`009jsb`009g`094ioc$movtouser`009`009`009;; X28$:`009`009`009`009`009`009;; X`009`009`009`009`009`009;; X.if eq smp_code`009`009`009`009`009;; X`009enbint`009`009`009`009`009;; X.iff`009`009`009`009`009`009;; X`009forkunlock`009-`009`009`009;; X`009`009lock=ucb$b_flck(r5),-`009`009;; X`009`009newipl=(sp)+,-`009`009`009;; X`009`009preserve=NO`009`009`009;; X.endc X; X.if ne EVAX X`009clrl`009ucb$l_bcnt(r5)`009`009;make sure we don't copy twice X.iff X`009clrw`009ucb$w_bcnt(r5)`009`009;make sure we don't copy twice X.endc X; X`009movl`009#1,r0 X90$: X`009ret X; X; X;*****`009kernel mode data transfer, driver's user -> server X;`009`009called via $CMKRNL X;`009`009implicit inputs:`032 X;`009`009`009- 'zt_ucb' X;`009`009`009- byte count in 'zt_msgbuf+zt_w_bnct' X;`009`009implicit outputs: X;`009`009`009- data in 'buffer' X;`009`009`009- byte count in 'bufbct' X;`009`009`009- ucb$w_bcnt cleared X; X.if ne EVAX Xk_fruser:`009.call_entry`009preserve= X.iff X`009.psect`009_nonpagedcode Xk_fruser:`009.word`009`094m X.endc X; X`009movab`009g`094lib$sig_to_ret,(fp) X; X`009clrl`009r0 X`009clrl`009bufbct X`009movl`009zt_ucb,r5 X`009beql`00990$ X; X`009movab`009buffer,r1`009`009;'to' address X.if ne EVAX X`009movl`009zt_msgbuf+zt_l_bcnt,r2`009;byte count X`009cmpl`009r2,ucb$l_bcnt(r5)`009;sanity check X`009bgtr`00990$`009`009`009;br if it failed X.iff X`009movzwl`009zt_msgbuf+zt_w_bcnt,r2`009;byte count X`009cmpw`009r2,ucb$w_bcnt(r5)`009;sanity check X`009bgtru`00990$`009`009`009;br if it failed X.endc X`009movl`009r2,bufbct X; X.if eq smp_code X`009dsbint`009ucb$b_fipl(r5)`009`009`009;; X.iff`009`009`009`009`009`009;; X.if ne EVAX`009`009`009`009`009;; X`009forklock`009-`009`009`009;; X`009`009lock=ucb$b_flck(r5),-`009`009;; X`009`009savipl=-(sp),-`009`009`009;; X`009`009preserve=NO`009`009`009;; X.iff`009`009`009`009`009`009;; X`009forklock`009-`009`009`009;; X`009`009lock=ucb$b_flck(r5),-`009`009;; X`009`009savipl=-(sp),-`009`009`009;; X`009`009preserve=NO,-`009`009`009;; X`009`009fipl=YES`009`009`009;;`032 X.endc`009`009`009`009`009`009;; X.endc`009`009`009`009`009`009;; X`009`009`009`009`009`009;; X`009tstl`009r2`009`009`009`009;; X`009bleq`00928$`009`009`009`009;; br if nothing to be moved! X`009jsb`009g`094ioc$movfruser`009`009`009;; X28$:`009`009`009`009`009`009;; X`009`009`009`009`009`009;; X.if eq smp_code`009`009`009`009`009;; X`009enbint`009`009`009`009`009;; X.iff`009`009`009`009`009`009;; X`009forkunlock`009-`009`009`009;; X`009`009lock=ucb$b_flck(r5),-`009`009;; X`009`009newipl=(sp)+,-`009`009`009;; X`009`009preserve=NO`009`009`009;; X.endc X; X.if ne EVAX X`009clrl`009ucb$l_bcnt(r5)`009`009;make sure we don't copy twice X.iff X`009clrw`009ucb$w_bcnt(r5)`009`009;make sure we don't copy twice X.endc X; X`009movl`009#1,r0 X90$: X`009ret X; X; X;*****`009kernel mode copy of 'result' to driver & activate driver X;`009`009called via $CMKRNL X;`009`009implicit inputs:`032 X;`009`009`009- 'zt_ucb' X;`009`009`009- 'result' X; X.if ne EVAX Xk_reqcom:`009.call_entry`009preserve= X.iff X`009.psect`009_nonpagedcode Xk_reqcom:`009.word`009`094m X.endc X; X`009movab`009g`094lib$sig_to_ret,(fp) X; X`009clrl`009r0 X`009movl`009zt_ucb,r5 X`009beql`00990$ X; X`009movc3`009#zt_msglen,zt_msgbuf,ucb_a_ztmsg(r5)`009;copy back X; X`009calls`009#0,zt_int`009`009`009`009;'interrupt' driver X; X`009movl`009#1,r0 X90$: X`009ret X; X; X.if ne EVAX X`009$locked_page_end`009;=====`009nonpaged routines end here ===== X.endc X; X; X;*****`009get mailbox & its name X; X`009.psect`009_data X; Xmbx_dvi_itm: X`009.word`009mbx_name_size X`009.word`009dvi$_fulldevnam X`009.long`009mbx_name X`009.long`009mbx_namedsc X`009.long`0090 X; X`009.align `009quad Xmbxiosb: X`009.blkw`0094 X; Xmbxchan: X`009.blkw X; X; X`009.psect`009_code Xget_mbx: X.if ne EVAX X`009.call_entry X.iff X`009.word`009`094m<> X.endc X; X`009$crembx_s`009prmflg=#0,- X`009`009`009chan=mbxchan,- X`009`009`009maxmsg=#zt_msglen,- X`009`009`009-;`009???`009`009bufquo=#zt_msglen,- X`009`009`009promsk=#0 X`009chkr0 X; X`009$getdvi_s`009chan=mbxchan,- X`009`009`009itmlst=mbx_dvi_itm X`009chkr0 X; X`009ret X; X; X;*****`009start mbx read X; X`009.psect`009_code Xread_mbx: X.if ne EVAX X`009.call_entry X.iff X`009.word`009`094m<> X.endc X; X`009$qio_s`009efn=#1,- X`009`009chan=mbxchan,- X`009`009func=#io$_readvblk,- X`009`009iosb=mbxiosb,- X`009`009p1=zt_msgbuf,- X`009`009p2=#zt_msglen X`009chkr0 X; X`009ret X; X; X;*****`009wait for mbx message X; X`009.psect`009_code Xwait_mbx: X.if ne EVAX X`009.call_entry X.iff X`009.word`009`094m<> X.endc X; X`009$synch_s`009efn=#1,- X`009`009`009iosb=mbxiosb X`009chkr0 X; X`009movzwl`009mbxiosb,r0 X`009chkr0 X; X`009cmpw`009mbxiosb+2,#zt_msglen X`009beql`00950$ X`009clrl`009r0 X`009chkr0 X50$: X`009ret X; X; X;*****`009exit handler X; X; X`009.psect`009_data Xcurprivs: X`009.quad`0090 X; X; X`009.psect`009_code Xexith: X.if ne EVAX X`009.call_entry X.iff X`009.word`009`094m<> X.endc X; X`009$setprv_s`009enbflg=#1,-`009`009;establish initial privileges X`009`009`009prvadr=curprivs`009`009;(sometimes removed by rundown) X`009chkr0 X; X.if ne EVAX X`009$cmkrnl_s`009routin=k_shut X.iff X`009$cmkrnl_s`009routin=k_shut,- X`009`009`009arglst=(ap) X.endc X`009chkr0 X; X`009ret X; X; X;*****`009setup exit handler X; X`009.psect`009_data X; X`009.align`009long Xexstat: X`009.blkl Xexblk: X`009.long`0090-0,exith,1,exstat X; X; X`009.psect`009_code Xsetup_exith: X.if ne EVAX X`009.call_entry X.iff X`009.word`009`094m<> X.endc X; X`009$dclexh_s`009desblk=exblk X`009chkr0 X; X`009ret X; X`009.page X;*****`009externalized routines & data X; X`009.psect`009_nonpageddata X`009.align`009quad XZT_BUFDSC:: Xbufbct: X`009.long`0090-0,buffer X; X; X`009.psect`009_data X`009.align`009quad XZT_MSGDSC:: X`009.long`009zt_msglen,zt_msgbuf X; X; X;*****`009initialize X; X`009.psect`009_data X; X`009.align`009quad Xlkwdata_inadr: X`009.long`009nonpageddata,nonpageddata_end X.if ne EVAX X.iff Xlkwcode_inadr: X`009.long`009nonpagedcode,nonpagedcode_end X.endc X`009.align`009quad Xzta0_dsc: X`009.ascid`009"_ZTA0:" X; X; X`009.psect`009_code X.entry`009ZT_INIT,`094m X; X; 4(ap) - address of descriptor of ZT device name (optional argument) X; X`009tstb`009(ap)`009`009`009`009; argument present? X`009beql`00925$`009`009`009`009; br if no X`009tstl`0094(ap) X`009beql`00925$`009`009`009`009; br if no X.if ne EVAX`009`009; avoid 'not naturally aligned' warning X`009movaq`009@4(ap),r0`009`009`009; yes, fetch it X`009movl`0094(r0),r1 X`009movl`009(r0),r0 X.iff X`009movq`009@4(ap),r0`009`009`009; yes, fetch it X.endc X`009brb`00930$ X25$: -+-+-+-+-+ End of part 4 +-+-+-+-+-