================================================================================ Note: ZTDRIVER is NOT public domain. COPYRIGHT (c) 1989,1991 W.J.Moeller, all rights reserved ... ================================================================================ Note to the January 1991 distribution: The programs as "described" below assume VMS V5.4 (and up?). Due to a change in the documented behaviour of magtape drivers, you'll have have to compile ZT1.C and ZT2.C with /DEFINE="PRE_V54=1" in order to generate programs that correctly support earlier VMS versions. For VMS 4.7, you also have to re-assemble the .MAR programs on a 4.7 system. If you have an older distribution, the only changes are in ZT1.C and ZT2.C - no need to rebuild the driver. (Also, the writeup below did *not* change - sorry.) w.j.m. 21-jan-1991 ================================================================================ ZTDRIVER has been tested & used under VMS V4.7, V5.1-1 and V5.4 . I myself hardly use it for "production"; in fact I mainly did it (a) as an exercise to port a driver from V4 to V5 (b) to test VMS V5's great new magtape (BACKUP) features - we had tapes only on the 8650 Be warned: In my experience DECnet/Ethernet (around 50kb/s) is just SLOW, compared to the speed of 'real' tapes. Anyway I think that ZT won't be faster than BACKUP to a remote disk file. So there is no point in tying up a 'served' real tape for the duration of DECnet data transfer, unless you're really short of disk space. =============== A little description/technical note, made up in a hurry ======== ZTDRIVER owes most of its size to my (essentially) copying of MTDRIVER's FDT stuff, since I didn't know at the outset which I/O functions are in actual use (MTAACP/RMS/BACKUP). Now I know - it's indeed only the documented ones plus IO$_NOP, sometimes also represented as a SKIP 0 blocks (if I remember correctly), with the ACP adding an apparently un-named modifier bit 1@9. It handles a request in its STARTIO routine the following way: copy pertinent data into its UCB extension, send that as a mailbox msg to the server (which indicates its presence by storing its mailbox into the UCB). It then WFIKPCH's where the timeout is not only very long, but the timeout routine itself is a NOP (Same for CANCEL!). At this point the server process is supposed to do just everything on its own, except for modifying externally visible UCB fields. Currently, the server need not go into kernel mode initially; only when it has figured out what is actually to be done, it possibly calls IOC$MOVxxUSER to transfer data. Finally the server into kernel mode, copies back its results into the UCB extension, and does a fake 'interrupt' to wake the driver. When awake, the driver just updates the 'real' UCB fields from the data it finds in the UCB extension and completes the I/O request. Currently this description is not completely correct, since there are a few error paths where the driver decides on its own to reject a request; I don't like them anymore. The driver starts out as OFFLINE, the server will set it ONLINE (when storing its mailbox address) and turn it off again in its exit handler - note that there is currently no EXEC mode exit handler! *** CAUTION: don't STOP the server, always $FORCEX it! *** Actually, the worst that can happen is that the driver gets stuck, and as far as I know this should block just 1 process (and possibly its ACP - MTAACP is not shared). The server initialization, is intended to clear this condition in any case: it just overwrites the UCB extension fields that may be left from an earlier instance and also triggers a pseudo-interrupt (which will normally be ignored). So if the server dies, just re-start it! *** CAUTION: the server does not check if it is the only one! Once I had 3 servers running by mistake - just "force"d all of them (ZTA0 went offline) and started a new one (ZTA0 online again) - my first chance to experience VMS V5's new mount verification stuff ... Files: ZTDEF.MAR (defines message area / 'shared' UCB extension) must be made into ZT.MLB, since the .MAR use '.include "ZT"' ZTDRIVER.MAR is the driver (VMS 4.7 .. 5.4), to make: $ MAC ZTDRIVER $ LINK ZTDRIVER/OPT ZTSERVER.MAR encapsulated the server's kernel mode stuff in a few simple-minded subroutines. (needs just CMKRNL privilege to run, by the way). *** CAUTION: ZTSERVER and ZTDRIVER have to be generated and *used* *together* for a particular VMS version, there is no built-in version check! (I think the fact that both link with SYS.STB will make it impossible to run a wrong server) Now for the server: what could it do??? A first 'test' version (ZT1 + ZT1T) tried to make up a 'memory tape'. I think such a thing is utterly useless, and as it stands, it can serve as a rudimentary test device only since it won't switch reels. [Just for fun: naturally I included EOT detection, but no way to simulate an unloaded tape. The ACP bravely asks the operator, but silly old BACKUP indeed initiates UNLOAD, then immediately tests if the tape is loaded; if that succeeds - as in my case, and allegedly for some initial V5 TUDRIVER - it happily continues without asking anyone!] If desired, I might include this program for completeness ... The second program (and the only one mentioned above) is a DECnet tape server pair. The program serving the pseudo-tape is called ZT2 and the real server, which serves a real tape, is called ZTNS2. There is quite a bit of black magic involved in getting a real tape served, the biggest problem being that there is nowhere a documentation for the 'correct' behaviour of a tape driver - in fact different DEC drivers do return different status codes on identical conditions. The second trouble comes with hard errors: if the real-tape-server (ZTNS2, for short) receives a media/hardware related error status, it does not know what exactly happened and in particular, where the tape is positioned (and of course ZTNS2 ought to be device-independent)! Here comes a horrible thing with VMS: apparently the author(s) of MTAACP noted this problem, and so there was created a cell UCB$L_RECORD where a magtape driver has to put the current block number (0 at BOT, EOF marks are counted as blocks). I know that the ACP looks at this cell, but normal programs can't, since it's not on the list of $GETDVI! Currently, ZTNS2 (intended to be a harmless, unprivileged program) does not go into kernel mode to find out about UCB$L_RECORD, so it has to guess and also return "tape position lost" status. There are actually 2 ways to do just that, the first is to say so (SS$_TAPEPOSLOST) and the second is to set a status bit MT$V_LOST (does anyone look at this status word, anyway?? - there is a bit MT$V_EOF allegedly indicating "end of file mark passed", MTDRIVER sets it from some hardware register, newer drivers apparently never set it (?)). ZTNS2 has a conditional of which behaviour is preferred. I have to admit that all of the code has not yet been sufficiently tested, because I was too lazy to implement "error insertion" in my memory tape, and with real tapes you have to wait and see - and that takes time...! The 'protocol' used between ZT2 and ZTNS2 is strictly half-duplex: ZT2 sends (sort of) I/O function, ZTNS2 finally replies with status & some more information. In between, ZTNS2 may send at most a single 'DMA request': - on tape read, this is immediately followed by data (split into reasonably-sized packets) , upon which ZT2 replies (with a 'DMA status', always good so far) - on tape write, ZT2 replies with the data requested I unnecessarily wrote ZTNS2 such that it would support full-duplex operation of the DECnet link; on the other hand, ZT2 is completely synchronous. ZT2/ZTNS2 is not complete. Both programs conspire to ignore most of the I/O function modifier bits, notably IO$M_INHRETRY; they do not implement read backward (I don't even know in what direction and where data would have to be placed in the user buffer); DATACHECK is not end-to-end, but only done on the ZTNS2 end. Also, IO$_SETMODE/CHAR is not propagated to ZTNS2 at all, and neither are the real tape "characteristics" to ZT2 - this is truly a shortcoming in the task-to-task protocol. The virtual ZT defaults to "TE16 at 1600bpi"; in fact this can be changed with IO$_SETCHAR without any influence on the driver's behaviour. I did implement the IO$_WRITECHECK function from MTDRIVER, which actually means "compare tape data to memory". Unfortunately, no program uses it, so it's completely untested. Files: ZT1.C + ZT1T.C make up the memory tapes (ZT1 is the "driver" part, ZT1T is just subroutines for in-core storage of tape blocks and EOF marks). ZT2.C is the ZT-server for the task-to-task design *** CAUTION: ZT1.C *and* ZT2.C each carry their own definition of the message area defined in ZTDEF.MAR *** Both ZT1 and ZT2 have an options to log all I/O operations; so far I had that always enabled. ZTNS2.C is the real-tape-server ZT1, ZT2 and ZTNS2 include: IODEF.H, MTDEF.H, UCBDEF.H which I created with my 'C_DEFS' procedure (recently re-posted on info-vax by Jerry Leichter). Note: IODEF.H from VAXC (VMS V4 version) won't do. By silly design, UCBDEF is currently *only* required to define a single bit position, UCB$M_VALID (this bit did *not* change from VMS V4 to V5 - UCBDEF changed a lot). ZTNS.H is included by ZT2.C and ZTNS2.C, it defines the task-to-task messages. First: $ MACRO ZTSERVER $ MACRO FEHLER $ CC REDIRECT To make ZT1: $ CC ZT1,ZT1T $ LINK ZT1,ZT1T,ZTSERVER,FEHLER,REDIRECT ! links with SYS To make ZT2 and ZTNS2: $ CC ZT2 $ LINK ZT2,ZTSERVER,FEHLER,REDIRECT ! links with SYS $ CC ZTNS2 $ LINK ZTNS2,FEHLER,REDIRECT To use the programs, (a) $ SYSGEN CONNECT ZTA0/noadapter *** BUG: ZTSERVER.MAR has _ZTA0 hard-coded. *** (b1) $ run ZT1 ! from anywhere: terminal, batch, detached ... (c1) when you are done, somehow stop ZT1 by $FORCEX, CTRL-Y, or STOP/QUE/ENTRY when in batch. (b2a) on the ZT-side, make up a Network object, e.g. ZT2.COM, like: $ set proc/priv=cmkrnl $ run ZT2 (b2b) on the real tape side: $ define ZT_TAPE $ mount/foreign ZT_TAPE/multi_volume ^-- for V5, I guess (my tapes are still with V4) $ define ZT_NETOBJECT "node::""task=ZT2/" ^---------^ wierd (sorry), task spec starts with quotation mark and ends with slash. $ run ZTNS2 (c2) when done, either kill ZT2 with $FORCEX (ZT-side), or stop ZTNS2 (by any means). *** BUG: ZT2 will *not* instantly notice that ZTNS2 has gone, but only on the next I/O. MOUNT/FOREIGN is ok to force an I/O (=> SS$_DEVOFFLINE). =============================================================================== Enjoy, -wjm Wolfgang J. Moeller, GWDG, D-3400 Goettingen, F.R.Germany | Disclaimer ... Bitnet/Earn: U0012@DGOGWDG5 Phone: +49 551 201516 | No claim intended! Internet: Moeller@gwdgv1.dnet.gwdg.de | This space intentionally left blank.