	.TITLE	MKDRIVER - VAX/VMS SCSI Tape Class Driver
	.IDENT	'X-25'
;	.LIST	MEB
;****************************************************************************
;*									    *
;*  COPYRIGHT (c) 1978, 1980, 1982, 1984, 1986, 1988, 1991, 1992-1996 BY    *
;*  DIGITAL EQUIPMENT CORPORATION, MAYNARD, MASSACHUSETTS.		    *
;*  ALL RIGHTS RESERVED.						    *
;* 									    *
;*  THIS SOFTWARE IS FURNISHED UNDER A LICENSE AND MAY BE USED AND COPIED   *
;*  ONLY IN  ACCORDANCE WITH  THE  TERMS  OF  SUCH  LICENSE  AND WITH THE   *
;*  INCLUSION OF THE ABOVE COPYRIGHT NOTICE. THIS SOFTWARE OR  ANY  OTHER   *
;*  COPIES THEREOF MAY NOT BE PROVIDED OR OTHERWISE MADE AVAILABLE TO ANY   *
;*  OTHER PERSON.  NO TITLE TO AND OWNERSHIP OF  THE  SOFTWARE IS  HEREBY   *
;*  TRANSFERRED.							    *
;* 									    *
;*  THE INFORMATION IN THIS SOFTWARE IS  SUBJECT TO CHANGE WITHOUT NOTICE   *
;*  AND  SHOULD  NOT  BE  CONSTRUED AS  A COMMITMENT BY DIGITAL EQUIPMENT   *
;*  CORPORATION.							    *
;* 									    *
;*  DIGITAL ASSUMES NO RESPONSIBILITY FOR THE USE  OR  RELIABILITY OF ITS   *
;*  SOFTWARE ON EQUIPMENT WHICH IS NOT SUPPLIED BY DIGITAL.		    *
;* 									    *
;*									    *
;****************************************************************************
;
;
;+
;
; FACILITY:
;
;	VAX/VMS SCSI Tape Class Driver
;
; ABSTRACT:
;
;	This module contains the class driver to control tape devices on
;	a SCSI bus.
;		
;
; AUTHOR:
;
;	Jim Klumpp	13-Jan-1989
;
; REVISION HISTORY:  
;
;	X-25	GCE						2-Jan-1997
;		Add in software front panel stuff again
;	X-24	TGGGCE	Tom Goodwin and Glenn Everhart		10-Oct-1996
;		Change the skip by filemarks algorithm to use the
;		UCB$L_MK_FLAGS bits, ALLOWFAST_PER_IO and ALLOWFAST_PER_IO,
;		and the IO$_SKIPFILE modifier IO$M_ALLOWFAST.
;		Skip by filemarks will also set MT$_FASTSKIP_USED
;		in the IOSB upon return.  Create MK_SETCHAR to key
;		on the SETCHAR modifiers, IO$M_ALLOWFAST_NEVER, 
;		IO$M_ALLOWFAST_PER_IO and ALLOWFAST_ALWAYS to 
;		set/clear UCB$L_MK_FLAGS bits. (see design document
;		in EVMS::DOCD$:[EVMS.SCSI.REFLIB]SKIPFILE.DESIGN.)
;
;	X-23	SCS		Sue Sommer		31-Jul-1996
;		Save the Inquiry data in a new UCB extension called
;		ucb$l_inquiry_data.  The extension will be pointed to by 
;		scdt$l_rsvd_long, so that Inquiry data may be referenced 
;		outside this module.  This is needed by Mode_Sense_Common 
;		to access the peripheral device type.
;
;	X-22 	SCS		Sue Sommer			2-Apr-1996
;	   	Modify SCSI_DEV_TYPES and the SCSI_DEVICE_TABLE to allow
;		both sync mode and skpfile support to be table-controllable.	
;
;       X-21    GWW		Grace Wang                      29-Mar-1996
;               * Gryphon QAR 146:
;               Fix cancel completion in IO_DIAGNOSE after current IRP
;               has been returned with SS$_ABORT in MK_CANCEL earlier.
;               This is a part of the solution which includes DKMK chan
;
;	X-20	RCL		Rick Lord			26-Mar-1996
;               Near the end of MK_KP_UNIT_INIT, just before the BSBW to
;		SET_UNIT_ONLINE after 15$, invoke SPI$INITIAL_PROCESSING
;		to run the common tolerant INQUIRY command routine.
;		
;	X-19 	SCS		Sue Sommer			20-Mar-1996
;		In IO_WRITEPBLK, modify the special path for 8MM devices.
;		This path formerly used the same SCDRP for two consecutive
;		write attempts;  while it still does so now, it takes care
;		to deallocate/reallocate the command buffer and unmap/remap
;		the user buffer.  This is necessary for smart adapters 
;		which assume a 1-1 correspondense between SCDRPs and requests.
;
;	X-18	TGG0123		Tom Goodwin			15-Mar-1996
;		Add the TKZ60 to the list of skipfiles exclusions.
;
;	X-17	SGS0088	Steve Skonetski				26-Feb-1996
;		Bump timeout values to 10 minutes.  TLZ6L, 7L autoloaders
;		timing out with 7 minute timeout values.
;
;       X-16    MCY     Mary Yuryan                             28-Nov-1995
;               Add support for high SCSI ID's (wide devices).  Change
;               check in MK_KP_UNIT_INIT for valid SCSI ID's from 7
;               to 31.  Clear calculation for higher LUN numbers.
;
;	X-15 SCS		Sue Sommer			4-Dec-1995
;               Re-instate skip-by-file, implementing fixes:
;		- Return correct space count in IOSB.
;		- Set EOF bit when appropriate.
;		- Modify TRANS_SENSE_KEY to return ENDOFVOLUME properly.
;		- Remove references to ucb$l_boff/bcnt.
;		- Exempt TSZ05/7 from skipfile due to lack of BLANK CHECK.
;		- Check the LOST bit in SPACE_FILEMARKS before exiting.
;		- Rename skiprec_in_prog to skip_in_prog.
;		- For skip count of 1 or 2, skip by record instead of by file;
;		  the overhead isn't worth it.
;		- Rename for the sake of clarity:  
;			CHECK_READPOS_SUPPORT to CHECK_SKIPFILE_SUPPORT;
;		  	UCB$V_READPOS_SUPPORTED to UCB$V_SKIPFILE_SUPPORTED; 
;			UCB$V_SKIPREC_INPROG to UCB$V_SKIP_INPROG. 
;
;	X-14	JCH710	John C. Hallyburton, Jr.		23-Oct-1995
;		STAR:: DOCD$:[EVMS.PROJECT_DOCUMENTS]FS-SCSI-NAMING.PS
;		New Naming: Get controller letter from DDB$L_PORT_ID if
;		DDB$V_PAC is set, indicating IOGEN copied the controller
;		letter to DDB$L_PORT_ID before potentially folding the
;		DDB$T_NAME controller letter to `A'.
;
;	X-13 	SCS		Sue Sommer			29-Jun-1995
;		Modify MODE_SELECT so that the density field gets filled
;		in for a TSZ07.
;
;	X-12	RCL		Rick Lord			10-May-95
;		Fold in the X-6U6 fix from Zeta: this fix adds bounds checking
;		to MK_REG_DUMP just before label 35$, preventing the routine
;		from overwriting the error log buffer entry it was handed and
;		possibly corrupting the header of the error log buffer right
;		after it.
;
;	X-11 	SCS		Sue Sommer			19-Apr-1995
;		Turn off skipfile enhancement by unconditionally clearing
;		the readpos_supported bit in ucb$l_mk_flags during unit init.
;		This enhancement is deemed too risky for the current release
;		and is planned for reinstatement in some future release
;		after broader testing is completed.
;
;		Modify the Mode Sense descriptors so that any Vendor Unique
;		Required mode page values are now only Preferred.  This is 
;		more compatible with the previous mode page handling and
;		resolves unusual cases:  eg. a Mode Select with vendor-
;		unique values may return success but without actually
;		setting the desired value, and that should count as success.
;
;		Also create a new UCB field ucb$l_sav_send_status to save
;		the return status from each SEND_COMMAND call.  Restore
;		that status in MODE_SENSE, since it was possibly overwritten
;		by Do_Mode_Page, and the original SEND_COMMAND status must
;		be returned by MODE_SENSE in order for all tapes to function
;		properly (eg. SS$_MEDOFL was overwritten by SS$_NODATA,
;		causing TURs not to be retried when they should have been).
;
;	X-10 	SCS		Sue Sommer			11-Apr-1995
;		In the READ_POSITION routine, fix the sanity check regarding
;		record position.  Namely, for reverse skips, allow success
;		whenever the new position is at beginning of tape.
;		Previously, we checked that the new position was strictly
;		less than old position, which did not account for BOT cases.
;
;       X-9     MCY             Mary Yuryan                     27-Mar-1995
;               - Add the IF_TZK11 macro to retension the QIC media, the change
;                 was never ported from the VAX side.  Add check in IO_RETENSION
;               - Modify CHECK_READPOS_SUPPORT routine to check for TZK10 device
;                 type, - TZK10 does not support BT (block Type) bit of 0 - in
;                 the same routine, change the check from READPOS_IN_PROG bit,
;                 to READPOS_SUPPORTED bit - we were checking the wrong bit.
;
;	X-8	JCH703	John C. Hallyburton, Jr.	13-Mar-1994
;		Add FAST_FDT offset to DDT for Fast-IO
;
;	X-7	SCS			Sue Sommer	14-Feb-1995
;		Declare various appropriate functions to be capable
;		of supporting 64-bit addresses.
;
;	X-6 	SCS		Sue Sommer			16-Nov-1994
;		Modify skipfile operation to use Space Filemarks instead
;		of Skip Records wherever possible;  major performance 
;		enhancement.  Specific modifications:
;		- Add READ_POSITION command to SCSI command table.
;		- Add bits to ucb$l_mk_flags.
;		- In PACKACK_SEQ, BSBW to a new routine CHECK_READPOS_SUPPORT.
;		- In IO_SKIP_FILE, BSBW to a new routine SKIP_FILE.
;		- In IO_SKIP_FILE, add new code to support SPACE FILEMARKS.
;		- Exempt READ POSITION support checks from error logging
;		  in LOG_EXTND_SENSE.
;
;		- Add keyword to SPI$CONNECT call for CLUE SCSI hooks.
;
;	X-5 	SCS		Sue Sommer			11-Nov-1994
;		Correct delta time passed to exe$kp_tqe_wait.
;
;	X-4	TEC 		Thomas E. Coughlan		7-OCT-1994
;		Back-out the change in X-15 that set the CLU bit.
;		Tapes aren't supported on the shared bus in this
;		version, so we can avoid the device naming disruption
;		that is caused by setting this bit.
;		
;	X-3	JFD0660		James F. Dunham			28-SEP-1994
;		SCSI-2 Checkin
;
;	************* Change Edit History to Match CMS ********************
;
;	X-16 	SCS		Sue Sommer		7-Sep-1994
;		Change mode page descriptor for block descriptor to
;		reflect only the density field, not the whole block
;		descriptor.
;
;	X-15 	SCS		Sue Sommer		6-Sep-1994
;		Modify SEND_COMMAND's Check Condition path to log 
;		errors even when the autosense data is invalid.
;		Also set the CLU bit in DDTAB for SCSI clusters.
;
;	X-14 	SCS		Sue Sommer		29-Aug-1994
;		In WRITEPBLK path, convert a leftover BSBW to CALL
;		into SEND_COMMAND.
;
;	X-13 	SCS		Sue Sommer		25-Aug-1994
;		Make the device specific parameter in the mode page
;		header a Preferred rather than Required field to
;		avoid a mode page verify error on the writelock bit.
;
;	X-12	JFD0598		James F. Dunham		25-AUG-1994
;		Add $SCSIDEF (in more than just the comment field)
;
;	X-11 	SCS		Sue Sommer		25-Aug-1994
;		Add $scsidef.
;
;	X-10 	SCS		Sue Sommer		24-Aug-1994
;		Modify Check Condition path in SEND_COMMAND so that
;		SCDRP$L_TRANS_CNT reflects the sense data length
;		(not the the original command's trans_cnt) and
;		the sense data can be correctly logged in errlog file.
;
;	X-9	SCS		Sue Sommer		         22-Aug-1994
;		Add appropriate flags to mode descriptors for header fields,
;               so they are treated as Required fields.
;
;	X-8	SCS		Sue Sommer		         4-Aug-1994
;		Fix SCSI_DEV_TYPES macro so that DTYP offsets are correct.
;
;	X-7	SCS		Sue Sommer			27-JUL-1994
;		Add common mode sense and diagnose routines.
;		Modify MODE_SENSE, MODE_SELECT, SETUP_COMPACTION.
;		Add descriptors.
;		Change SETUP_CMD, SEND_COMMAND, CLEANUP_CMD to call interfaces.
;		Limit vendor-unique info to 8 bytes to fit in descriptor.
;		Modify LOG_XTND_SENSE to not log ILLEGAL REQUEST errors.
;		Move b_sense_key from UCB to SCDRP.
;
;	X-6	SCS		Sue Sommer			 20-Jul-1994
;		Fix ucb$b_scsi_version to be fetched from proper INQUIRY field.
;
;	X-5	SCS		Sue Sommer			 12-Jul-1994
;		Use scdrp$ps_kpb rather than unit_init_kpb to stall
;		in exe$kp_tqe_wait in wait_unit_ready.
;
;	X-4	SCS		Sue Sommer			 8-Jul-1994
;		Restore UCB address into R5 before calling KP_REQCOM in
;		IO_DIAGNOSE.
;		Fix bad time values for call to spi$tqe_wait.
;
;	X-3	SCS		Sue Sommer			 6-Jul-1994
;		Remove references to ucb$l_scdrp_sav1 in request sense
;		processing.
;
;	X-32	SCS		Sue Sommer			20-May-1994
;		Change SPI$xxx names for SCSI2.
;		Replace SPI$TQE_WAIT with calls to exe$kp_tqe_wait.
;               Modify SEND_COMMAND to handle Autosense.
;               Modify SETUP_CMD, CLEANUP_CMD to allow previously allocated
;		    buffer.
;
;	X-31	RAR028		Buzzy Ritter			20-Jan-1993
;		Add proper return values after forking in unit_init and
;		ctrl_init.
;
;	X-30	SGS0032		Steve Skonetski			25-oct-1993
;		Add callouts for new MME (Media Management Extenstion)
;		facility.
;
;	X-29	MCY		Mary Yuryan 	       11-Oct-1993
;		In the SENSE_KEY_TABLE, change the error returned 
;		for BLANK CHECK from DRVERR to OPINCOMPL.  This 
;		prevents a fatal drive error when performing 
;		multi-volume backups, on tapes that have not been 
;		initialized. 
;
;	X-28	LPL		Lee Leahy		8-Oct-1993
;		
;
;	X-27	RCL		Rick Lord		1-Oct-1993
;
;		Add external symbol references to force failure to
;		assembly phase rather than link.
;
;	X-26	RCL		Rick Lord			
;
;		Field name changes:
;
;			SCDRP$V_S0BUF => SCDRP$V_FLAG_S0BUF
;			SCDRP$M_S0BUF => SCDRP$M_FLAG_S0BUF
;			SCDRP$V_BUFFER_MAPPED => SCDRP$V_FLAG_BUFFER_MAPPED
;			SCDRP$M_BUFFER_MAPPED => SCDRP$M_FLAG_BUFFER_MAPPED
;			SCDRP$V_DISK_SPUN_UP => SCDRP$V_FLAG_DISK_SPUN_UP
;			SCDRP$M_DISK_SPUN_UP => SCDRP$M_FLAG_DISK_SPUN_UP
;
;	X-25	WDA		W.D. Arbo			20-Aug-1993
;		Change old step 2 macro names to new names. (E.g.
;		$ABORTIO to CALL_ABORTIO).
;
;	X-24	WDA		W.D. Arbo			27-Jul-1993
;		More of X-23.
;
;	X-23	WDA		W.D. Arbo			29-Jun-1993
;		Tie up loose ends in step 2 conversion.
;
;	X-22	NT014		Nora Tanner			11-May-1993
;		Filesystem structure promotions: WCB$W_NMAP has become a
;		longword.
;
;	X-21	RWC124		Richard W. Critz, Jr.		 7-May-1993
;		I failed to notice that YELLOW found the wrong copy of this
;		module when I checked in RWC122.  As a result, the entry in the
;		device table for the TZ85 is still commented out.  Restore it
;		to its rightful place.
;
;	X-20	RWC122		Richard W. Critz, Jr.		23-Apr-1993
;		Integrate DDR support (in local routine GET_DEVICE_TYPE).
;		
;	X-19	WDA	W.D.Arbo				18-Feb-1993
;		Convert driver to Step 2. 
;
;		Fix bug in LOG_ERROR.  The IF_TZxx macros assume the UCB is in 
;		R3 but here it is in R5.  This fix has been applied to the VAX
;		side also.
;
;	X-18	SCS	Sue Sommer			 	24-Mar-1993
;		Add new devices to SCSI_DEV_TYPES table.
;
;	X-17	SCS	Sue Sommer			 	15-Mar-1993
;		In SETUP_COMPACTION, return an error whenever zero bytes
;		of mode sense data are returned;  prevents later crash
;		when referencing SCDRP$L_SVA_USER.
;
;	X-16	SCS	Sue Sommer			 	15-Feb-1993
;		In the INIT_SCDRP routine, initialize SCDRP size and length
;		fields to make crash dump analysis easier.
;
;	X-15	SCS	Sue Sommer			 	13-Nov-1992
;		Rename ALLOC_POOL to MK_ALLOC_POOL and insure that all calls
;		to it have KPB address in R5.
;
;		Modify DO_8MM_COPY_CHK to unmap and then re-map the data
;		buffer after a READ_8MM command.  This accommodates those
;		platforms which always unmap a buffer when the command 
;		buffer is deallocated.
;
;		Merge in Blade changes to the level of X-25:  (Exclude C2
;		changes, DDR support, allocation of SCDRP for REQUEST_SENSE;  
;		also, 8MM changes had already been merged earlier.)
;
;		MCY		Mary Yuryan / Rick Lord		17-Sep-1992
;		- Enhance loader support, delete old CHECK_FOR_LOADER macro,
;		  Add loader byte to SCSI_DEV_TYPES1, check for loader in
;		  GET_DEVICE_TYPE, add loader byte to device string in 
;		  SCSI_DEV_TYPES.	RCL
;		- In LOG_EXTND_SENSE, add check for BOT and BLANK CHECK error
;		  & supress logging the error - for new tapes.   MCY
;		- Add check for cancelled IO in IO_SENSECHAR: to avoid reading
;		  cancelled IO/deallocated IRP.   RCL
;		  (SENSECHAR not cancellable in Alpha  /SCS  13-Nov-1992)
;		- Complete TKZ09 support - add check for TKZ09 after check for 
;		  8mm tape in GET_DEVICE_TYPE.
;		- Merge in John Meneghini's fixes below.  MCY
;
;		JAM0008	John Meneghini			23-JUL-1992
;		Lengthen Disconnect & DMA/Phase change timeouts with 
;		ERASE, SPACE and RECIEVE DIAGNOSTICS commands.
;		- Increased DISCONNECT timeouts to 5 hours w/ SPACE & ERASE
;		- Increased DMA timeouts to 1 min. w/ RECIEVE_DIAGNOSTICS 
;
;		JSSBLADE	John S. Simakauskas		24-June-1992
;		Add  TKZ09 - 5GB, 8mm, SCSI Tape (TKZ08 Follow-on) 
;
;		JAM001		John Meneghini		06-MAY-1992
;		Branch around INSV instruction in GET_DEVICE_TYPE routine
;		setting MT$S_DENSITY field in UCB$_DEVDEPEND.  Was
;		improperly resetting Compaction (to zero) w/TLZ06.
;
;		MCY 	Mary Yuryan			25-Feb-1992
;		Merge loader support from Amber. Create the CHECK_FOR_LOADER
;		macro which will set the loader bit if TZ857 or TLZ6 is 
;		present, called from the INQUIRY routine.  
;		Sync the VSC idents...
;
;		MCY	Mary Yuryan 			20-Dec-1991
;               Add latent support for SCSI loaders - TKZ60, TZ857,
;               TZ867,TZ877, and TLZ6 (TLZ06 loader.)
;
;		FAK002	Forrest A. Kenney	26-Nov-1991
;		Merge in latest Blade changes.
;
;		WJG0049		W. John Guineau		15-Nov-1991
;			Add DDR support. (Comment for Alpha /SCS 13-Nov-1992)
;
;		DWF0022		David W. Freund		12-Nov-1991
;			Fold in X-16A1 by W. John Guineau:
;			- Fix bug introduced in X-16 for rewind timout check
;			  WAIT_UNIT_READY - add a BRB around UNLOAD timeout
;			  code path.
;
;		WJG     W. John Guineau                 8-Oct-1991
;			- Fix EXABYTE (8MM) DCL COPY command problems.
;			- Fix resource contention problems due to dma buffer
;			  deadlock conditions when a SCSI error occurs and all
;	 		  buffer space is allocated. Now we allocate enough
;			  mapping resources at UNIT_INIT in ALLOC_REQSNS_RESOURCE
;			  for the REQUEST SENSE to get through (which was prevoiusly
;			  hanging waiting for resources which were locked until the 
;		 	  REQUEST SENSE completed, which coudn't because it couldn't
;			  get resources to execute!)
;                         (Note:  Irrelevant for Alpha, not merged due to lack
;			  of DMA buffer contention  /SCS   13-Nov-1992)
;
;		Forrest A. Kenney	07-Nov-1991
;		Merge in MAGIC and BLADE changes since T2 snapshot.
;
;		WJG	W. John Guineau			7-Oct-1991
;			- Change unload timeout logic from X-15 to use separate
;			  bit (UCB$M_UNLOAD_INPROG not UCB$M_REWIND_INPROG)
;			  and time field (UCB$L_UNLOAD_TIME) so we don't hang on
;			  IO$_AVAILABLE after a DISMOUNT. This is necessary 
;			  since we must not timeout after an UNLOAD with media 
;			  stackers for at least 3 minutes to give the stacker 
;			  time to get the next tape loaded.
;
;		WJG	W. John Guineau			1-Oct-1991
;		- Add REWIND timeout logic to IO_UNLOAD code path to
;		  prevent loaders from timwing out while traversing a 
;		  media stack.
;		- Make ident match VSC ident
;
;		FAK001		Forrest A. Kenne	23-Sep-1991
;		Merge C2 changes into Blade.  Make module ident and revision
;		history agree with CMS after the master pack cleanup.
;		(C2 changes not merged for Alpha  /SCS 13-Nov-1992)
;
;		WJG	W. John Guineau			14-Aug-1991
;		Return the compaction "density" values in the sense mode
;		data packet for BACKUP.
;
;       	WJG     W. John Guineau                 22-May-1991
;               Add UCB$V_COMPCHK_IN_PROG  bit to flag when compaction
;               check is in progress to prevent bad scsi status on
;               devices which don't support compaction from setting tape
;               lost
;
;       	WJG     W. John Guineau                 15-May-1991
;               Fix DMA Length field in MODE_SENSE_COMP SCSI_CMD
;               descriptor to be the exact value of data in expected
;               to work around a data pointer manipulation bug in
;               PKIDRIVER
;
;	X-14	SCS	Sue Sommer			 27-Oct-1992
;		Modify LOG_ERROR routine to write EMB$L_DV_STS, EMB$L_DV_ERTCNT
;		and EMB$L_DV_ERTMAX in error buffer, since otherwise they
;		contain uninitialized data;  also zero EMB$Q_DV_IOSB, since
;		the IOSB is not yet known at this point.
;
;	X-13	SCS	Sue Sommer			 1-Oct-1992
;               In LOG_ERROR macro and LOG_ERROR and REGDUMP routines, replace 
;		use of R7 and R8 with UCB fields.  This accommodates ERL$DEVxxx
;		convention, which does not declare R7 and R8 as inputs.
;
;	X-12	SCS	Sue Sommer			  9-Sep-1992
;		Increase ERASE timeout value to 5 hours to accommodate 
;		90m TLZ06 tape.
;		Disallow cancel operations for SENSEMODE/SENSECHAR/SETMODE;
;		otherwise the canceled IRP will sometimes be given to
;		another device before the canceled I/O completes, resulting
;		in simultaneous use of that IRP by two devices.  (Only these
;		3 functions reference the IRP after the cancel occurs.)
;
;	X-11	SCS	Sue Sommer			  20-Jul-1992
;               Merge in John Guineau's VAX bug fix for the EXABYTE (8MM
;		TKZ09) DCL COPY problems.  
;		
;	X-10	SFS0563		Stephen F. Shirron	06-Jul-1992
;		Add new SCSI devices.
;
;	X-9	SCS	Sue Sommer			  27-May-1992
;               Add new devices to SCSI device table.
;
;	X-8	SCS	Sue Sommer			  8-Apr-1992
;		Include symbol alignment for UCB extensions.
;		Merge in VAX/VMS V5.4-3 changes by John Guineau, Mary
;		Yuryan, Howard Palmer as follows:
;
;		Add UCB$V_COMPCHK_IN_PROG  bit to flag when compaction 
;		check is in progress to prevent bad scsi status on 
;		devices which don't support compaction from setting tape 
;		lost
;
;		Fix DMA Length field in MODE_SENSE_COMP SCSI_CMD
;		descriptor to be the exact value of data in expected
;		to work around a data pointer manipulation bug in 
;		PKIDRIVER
;
;		More compaction fixes - this time with real hardware to test
;		on!  Moved check for compaction support into separate
;		routine CHECK_COMPACTION_SUPPORT, utilized MT2$V_COMP_ENA
;		bit in DEVDEPND2 instead of density field. Also, now
;		returns SS$_NORMAL if you set a density a device doesn't
;		support (previous behaviour) -  CHECK_COMPACTION_SUPPORT is 
;		called from UNIT_INIT (via SET_UNIT_ONLINE) and from PACKACK.
;
;		Allow SCSI-1 compliant devices to be used. The check added
;		in X-18U3 only allowed SCSI-2 via UCB$B_SCSI_VERSION
;
;		Merge Magic and Sigma code streams
;
;               Remove default mode_select parameters from the SCSI_DEV_
;               TABLES for the TSZ05,(TZX0) & TSZ07.  The RDEW bit caused
;               mutiple volume backup problems when set, causing the backup
;               operation to fail.
;
;		Make data compaction support more generic for SCSI
;		in VMS. Enable automatic recognition of devices which
;		support compaction. Add DISABLE/REENABLE_ERRLOG macros
;		from DKDRIVER. Add UCB$B_SCSI_VERSION field in UCB for 
;		recognition of SCSI2 compliant devices.
;
;		Add TZK11 and TLZ06 data compaction support. 
;
;		Change default mode_select paramters for the TSZ05,
;		TSZ07 to fix the multi-volume backup failures with 
;		the RDEW bit set. 
;
;	------------ Ident change due to master pack cleanup -----------
;
;	X-19	SCS	Sue Sommer			  8-Mar-1992
;		Remove the TRACE_TABLE since the cross-PSECT offsets it contains
;		prevent the "sliced" loading of this driver.  Moreover, this
;		table is unused in this version of this driver.  Finally, in
;		macro MK_ALLOC_SCDRP, assure that the SCDRP allocated on the
;		stack is quadword aligned for performance reasons.
;
;	X-18	SCS	Sue Sommer			 23-Feb-1992
;               Remove END keyword in DPT so driver may be sliced.
;		Remove unreachable code in MK_SENSEMODE.
;
;	X-17	ROW0802		Ralph O. Weber		28-JAN-1992 16:20
;		Fix the unit routine to start the KP stack usage with a
;		KP register save mask that is suitable for HLL port drivers.
;		The Cobra port driver is HLL and needs this fix.
;
;	X-16	SCS	Sue Sommer			 17-Jan-1992
;		Edit IF*_* macros to allow a UCB macro argument.  In 
;		FILTER_ERROR, call IFNOT_TK with UCB=R5.
;
;	X-15	BJT291		Benjamin J. Thomas III	  9-Jan-1992
;		Promote UCB ERRCNT, ERTCNT and ERTMAX fields to longwords
;
;	X-14	BJT286		Benjamin J. Thomas III	 5-Dec-1991
;		Use HLL KPB register mask rather than hand built one
;
;	X-13	BJT277		Benjamin J. Thomas III	21-Nov-1991
;		Use IRP for passing of QIO arguments P1 - P6
;
;	X-12	BJT271	Benjamin J. Thomas III		15-Nov-1991
;		Promote FUNC fields
;
;	X-11	BJT265	Benjamin J. Thomas III		11-Nov-1991
;		More UCB, IRP promotions to longword.
;
;	X-10	SCS	Sue Sommer			 6-Nov-1991
;		In IO_SKIP_RECORD and IO_SKIP_FILE, copy SCDRP$L_MEDIA
;		correctly.  In FILTER_ERROR, fill error array correctly.
;
;	X-9	BJT260	Benjamin J. Thomas III		31-Oct-1991
;		More promotions to longword.  UCB BOFF, IRP BOFF and BCNT
;
;	X-8	SCS	Sue Sommer			 7-Oct-1991
;		Promote SCDRP$W_STS to SCDRP$IS_STS and convert remaining
;		occurrences of IRP$W_STS to IRP$L_STS.
;		Also replace the double fork in unit init with a fork-and-wait
;		mechanism.
;
;		Merge in changes by Mary Yuryan, Barbara Leahy, Jim Klumpp,
;		and Howard Palmer from VAX/VMS V5.4-2 as follows:
;		In RETENSION support, change status of drive to be 
;		"valid" and "online" after the RETENSION operation.
;		Move the TZK10 RETENSION function from SKIPFILES to
;		the UNLOAD and REWIND functions.  Add TLZ06 tape symbol,
;		next generation RDAT. 
;		Change the algorithm for determining whether a device
;		is not ready due to a previous rewind/immediate command.
;		Before, the driver would always assume a rewind was in 
;		progress and wait at least the maximum rewind time before 
;		timing out a wait unit ready polling loop. Now, whenever 
;		a rewind/immediate is sent to the drive, the time by which 
;		this command should complete is recorded in the UCB. This 
;		information can then be used to time out wait unit ready
;		polling activity.
;		Add a workaround for the TZK50 phase error bug. Delay for
;		one second after sending a SCSI unload command to the TZK50
;		to prevent subsequent test unit ready commands from being
;		told the device is ready, when in fact it's really in the
;		process of being rewound and unloaded.
;		Add the RETENSION command for the TZK10 tape drive.
;		Create new SCSI command packet for "RETENSION" using
;		the SCSI "LOAD" command.  Created modifier IO$M_RETENSION
;		IODEF for IO$_SKIPFILE function code.  Created IFNOT_TZK10
;		macro to check for proper device type.
;		Remove the very short timeout period for the TSZ07
;		in the WAIT_UNIT_READY: routine.  The current timeout
;		caused DCL commands such as mount/init to timeout while
;		waiting for the tape to rewind. 
;		Add support for 9-track, dual density TSZ07 device.
;		IO_SETMODE, IO_SENSEMODE routines modified to provide
;		setting/reporting of TSZ07 density. Return SS$_DATAOVERUN 
;		status if a read fails with ILI status and the user 
;		requested less data than was in the actual record.
;
;	X-7	BJT247	Benjamin J. Thomas III		27-Sep-1991
;		Promote IRP$W_STS to IRP$L_STS
;
;	X-6	SCS	Sue Sommer			 4-Sep-1991
;		Modify unit routine to save the UCB address in the KPB before
;		calling KP_SWITCH_TO_KP_STACK, and to restore it afterwards.
;
;	X-5	SCS	Sue Sommer			12-Jul-1991
;		Fix argument in call to KP_ALLOCATE_KPB.
;	
;	X-4	SCS	Sue Sommer			 2-Jul-1991
;               Modify unit initialization routine to deallocate KPB
;		after unit init completes.
;
;	X-3	SCS	Sue Sommer			22-Mar-1991
;		Miscellaneous cleanup for alignment.  Sanity check minimum
;		rev level argument for its proper length in SCSI_DEV_TYPES.
;
;       ----------Version number change due to reorg of master pack -----------
;
;	X-1K2	SCS	Sue Sommer			21-Feb-1991
;		Initial Alpha changes.
;
;	X-14K2	LSS0168		Leonard S. Szubowicz	14-Sep-1990
;		Fork block FR3 and FR4 have each been expanded to a quadword
;		to allow the preservation of their full 64-bit values on EVAX.
;		Use MOVX macro to copy these quantities in an architecture
;		independent fashion.
;
;	X-15	JTK	Jim Klumpp			06-Jun-1990
;		In SETUP_CMD, map the buffer with high priority to avoid
;		deadlock. The map buffers elsewhere can remain at low
;		priority, as there's no danger of deadlock on the first
;		call to map buffer per QIO function.
;
;	X-14	MCY	Mary Yuryan			06-Mar-1990
;		Increase SCSI command timeout values to match those
;		of the RDAT tape drive.  Increase the default timeout
;		from 4 seconds to 30 seconds.  Add new inquiry entry
;		for ucode change made to fix PVAX console output. 
;		Add device name for QIC tape - TZK10.
;
;	X-13	JTK	Jim Klumpp			23-Jan-1990
;		Fix rewind/immediate. The command following a 
;		rewind/immediate can fail if the rewind is still in
;		progress. Call the wait unit ready routine in this
;		situation to prevent the new command from failing.
;
;	X-12	MCY	Mary Yuryan		       18-Jan-1990
;		Fix device identification field returned by the 
;		inquiry command that changed with new micro-code.
;
;	X-11	JTK	Jim Klumpp			5-Jan-1990
;		Fix revision checking. Bring a drive online whether it's
;		out of rev or not (to prevent problems during installation).
;
;	X-10	MCY 	Mary Yuryan 			28-Dec-1989
;		Add  TLZ04 (RDAT) tape support. 
;
;	X-9	JTK	Jim Klumpp			28-Sep-1989
;		Merge changes from 5.3 stream including:
;
;		X-7U2	JTK	Jim Klumpp			25-Sep-1989
;			Fix read/reverse bug. Remove revision checking of
;			third party drives. Decrease mount timeout time.
;
;		X-7U1	DGB0318	Donald G. Blair			05-Aug-1989
;			Add DPT$V_NO_IDB_DISPATCH bit to the driver prologue table.
;
;	X-8	JTK	Jim Klumpp			4-Aug-1989
;		Change logical end of volume handling to ignore the 
;		mount status. Fix ident to match master pack. 
;
;	X-6	JTK	Jim Klumpp			22-Jun-1989
;		Add callback support and data structure version
;		checking for SPI$CONNECT. Remove TZ30-specific timeout
;		support. Add fastboot support. Fix bug in IO_SKIP_RECORD_REV.
;		Add IO_WRITEMARK routine which is equivalent to IO_WRITEOF.
;		Translate all media errors to SS$_PARITY. Add density field
;		to device type table. Fix multi volume test code. Add
;		workaround for tapemark handling synchronization bug.
;		Remove $SCDTDEF macro. Change number of arguments passed
;		to SET_CONN_CHAR. Change priv needed for IO_DIAGNOSE function.
;
;	X-5	JTK	Jim Klumpp			16-Jun-1989
;		Add TZ30-specific timeout values, retry in SEND_COMMAND
;		if drive returns BUSY status, several minor bugfixes.
;
;	X-4	JTK	Jim Klumpp			 1-Jun-1989
;		Add more robust checking of the additional field in
;		extended sense data.
;
;	X-3	JTK	Jim Klumpp			12-May-1989
;		SCSI tape class driver: complete replacement of
;		PVAX monolithic tape driver.
;-
.PAGE
	.SBTTL	+
	.SBTTL	+ SYMBOL DEFINITIONS
	.SBTTL	+
	.SBTTL	External symbol definitions
;
; External symbols
;

	$CANDEF				; Cancel reason codes
	$CRBDEF				; Channel request block
	$DCDEF				; Device classes and types
	$DDBDEF				; Device data block
	$DEVDEF				; Device characteristics
;;;	$DTNDEF				; DDR DTN offsets
;;;	$DTUDEF				; DDR DTU offsets
	$DYNDEF				; Data strucure types
	$EMBDEF				; Errorlog message buffer
	$FDTARGDEF			; Define FDT routine input arg offsets
	$FDT_CONTEXTDEF			; Define FDT context structure
	$FKBDEF				; Define fork block symbols
	$IDBDEF				; Interrupt data block
	$IODEF				; I/O function codes
	$IPLDEF				; Hardware IPL definitions
	$IRPDEF				; I/O request packet
	$KPBDEF				; Kernel process block symbols
	$MMEDEF				; Media Management defs
	$MODEDEF			; Mode page handling defs
	$MTDEF				; Magtape status codes
	$MT2DEF				; Extended Magtape status codes
	$NSADEF				; Security symbols
	$ORBDEF				; Object rights
	$PCBDEF				; Process control block
	$PRVDEF				; Privilege mask
	$PTEDEF				; Page table entry symbols
	$SCDRPDEF			; SCSI SCDRP symbols
	$SCSIDEF			; SCSI Definitions
	$SPDTDEF			; SCSI PDT symbols
	$SPIDEF				; SCSI port interface
	$SSDEF				; System status codes
	$UCBDEF				; Unit control block
	$VADEF				; Virtual address symbols
	$VECDEF				; Interrupt vector block
	$WCBDEF				; Window control block

	.DISABLE GLOBAL		

	;
	; Define external references to force failure to assembly phase rather
	; than being deferred until linking.
	;

	.EXTERNAL ACP_STD$ACCESS
	.EXTERNAL ACP_STD$DEACCESS
	.EXTERNAL ACP_STD$FASTIO_BLOCK
	.EXTERNAL ACP_STD$MODIFY
	.EXTERNAL ACP_STD$MOUNT
	.EXTERNAL ACP_STD$READBLK
	.EXTERNAL ACP_STD$WRITEBLK
	.EXTERNAL BUG$_INCONSTATE
	.EXTERNAL CLU$GL_ALLOCLS
	.EXTERNAL COM$DRVDEALMEM

	.EXTERNAL DO_MODE_PAGE

	.EXTERNAL EXE$ALONONPAGED
	.EXTERNAL EXE$ALONONPAGED_ALN 
	.EXTERNAL EXE$ALOPHYCNTG
	.EXTERNAL EXE$DEANONPAGED
	.EXTERNAL EXE$DEANONPGDSIZ
	.EXTERNAL EXE$DEBIT_BYTCNT_ALO
	.EXTERNAL EXE$GL_ABSTIM
	.EXTERNAL EXE$GL_HBS_PTR
	.EXTERNAL EXE$GL_SHADOW_SYS_DISK
	.EXTERNAL EXE$GL_SYSUCB
	.EXTERNAL EXE$GQ_SYSTYPE
	.EXTERNAL EXE$INSTIMQ
	.EXTERNAL EXE$KP_ALLOCATE_KPB
	.EXTERNAL EXE$KP_FORK_WAIT
	.EXTERNAL EXE$KP_RESTART
	.EXTERNAL EXE$KP_STALL_GENERAL
	.EXTERNAL EXE$KP_START
	.EXTERNAL EXE$KP_TQE_WAIT
	.EXTERNAL EXE$OUTZSTRING
	.EXTERNAL EXE_STD$ABORTIO
	.EXTERNAL EXE_STD$INSIOQ
	.EXTERNAL EXE_STD$READLOCK
	.EXTERNAL EXE_STD$FINISHIO
	.EXTERNAL EXE_STD$ALLOCIRP
	.EXTERNAL CTL$GL_PCB
	.EXTERNAL DKMK$DIAGNOSE_INIT
	.EXTERNAL DKMK$DIAGNOSE_FDT	
	.EXTERNAL DKMK$DIAGNOSE_SIO	
	.EXTERNAL EXE_STD$MODIFYLOCK
	.EXTERNAL EXE_STD$KP_STARTIO
	.EXTERNAL EXE_STD$LCLDSKVALID
	.EXTERNAL EXE_STD$ONEPARM
	.EXTERNAL EXE_STD$SENSEMODE
	.EXTERNAL EXE_STD$SETMODE
	.EXTERNAL EXE_STD$READCHK
	.EXTERNAL EXE_STD$WRITECHK
	.EXTERNAL EXE_STD$WRITELOCK
	.EXTERNAL EXE_STD$ZEROPARM
	.EXTERNAL IOC$ADD_DEVICE_TYPE
	.EXTERNAL IOC$GL_SPDT_LIST
	.EXTERNAL IOC$GL_SPI_CONNECT
	.EXTERNAL IOC$PTETOPFN
	.EXTERNAL IOC$REMOVE_DEVICE_TYPE
	.EXTERNAL IOC$REQCOM
	.EXTERNAL IOC$RETURN
	.EXTERNAL IOC$RETURN_UNSUPPORTED
	.EXTERNAL LDR$ALLOC_PT
	.EXTERNAL LDR$DEALLOC_PT
	.EXTERNAL MME$$DEV_EVENT
	.EXTERNAL MMG$GL_BWP_MASK
	.EXTERNAL MMG$GL_BWP_MASK
	.EXTERNAL MMG$GL_PAGE_SIZE
	.EXTERNAL MMG$GL_PTE_OFFSET_TO_VA
	.EXTERNAL MMG$GL_SPTBASE
	.EXTERNAL MMG$GL_VA_TO_VPN
	.EXTERNAL MMG$GL_VPN_TO_VA
	.EXTERNAL MMG$SVAPTECHK
	.EXTERNAL MMG$TBI_SINGLE
	.EXTERNAL MT_STD$CHECK_ACCESS
	.EXTERNAL SCS$GL_MSCP_NEWDEV
	.EXTERNAL SMP$AL_IPLVEC
	.EXTERNAL SMP$GL_FLAGS
	.EXTERNAL SYS$AR_BOOTUCB
	.EXTERNAL IOC$CRAM_IO
	.EXTERNAL IOC$CRAM_CMD
	.EXTERNAL IOC$NODE_FUNCTION
	.EXTERNAL EXE$GL_CPUNODSP
	.EXTERNAL IOC$ALLOC_CRAB
	.EXTERNAL IOC$KP_WFIKPCH
	.EXTERNAL EXE$KP_FORK
	.EXTERNAL ERL_STD$DEVICEATTN
	.EXTERNAL EXE$TIMEDWAIT_SETUP
	.EXTERNAL EXE$TIMEDWAIT_COMPLETE
	.EXTERNAL IOC$LOAD_MAP

	.SBTTL	Misc local symbols
;       
; Local symbols
;

        LOADER = 1                      ; Specify that a device is a loader -
					;  	mcy / rcl - 9/18/92
;	DEBUG = 1			; Flag to enable various tracing and
					; debug features.
	.IF DEFINED DEBUG
	.PRINT	; - DEBUG flag is enabled
	.ENDC


;	MULTI_VOLUME_TEST = 1		; Flag to enable multi-volume tape
					; testing by causing ENDOFTAPE status
					; to be returned on any writes after
					; record 100 (hex).
	.IF DEFINED MULTI_VOLUME_TEST
	.PRINT	; - Remove code to test multi-volume tapes
	.ENDC


	SCDRPS_PER_UNIT		= <3+1>	; Number of SCRPs to allocate per unit
	READY_POLL_INTERVAL	= 1	; Interval for test unit ready polling
	BUSY_RETRY_CNT		= 10	; Number of times to retry sending 
					; command if device is busy

	MAX_BCNT		= 65535	; Maximum byte count per transfer
					
	DEFAULT_DISCONNECT_TIMEOUT = 30	; Default values in seconds for disconnect
	DEFAULT_PHASE_CHANGE_TIMEOUT = 30 ; and phase change timeouts

	MK_ERROR_REVISION = 2		; Errorlogging revision supported by
					; this driver. This should be incremented
					; each time an incompatible change is
					; made to the errorlog packet format.

	MAX_REWIND_TIME = 3*60		; Maximum time a rewind command should
					; take

	MAX_UNLOAD_TIME = 3*60		; Maximum time an unload command should
					; take
	DEF_TAPE_PARAMS = -		; Default tape parameters: 
	 <MT$K_DEFAULT @ MT$V_FORMAT>!-	; default density
	 <MT$K_BLK_833 @ MT$V_DENSITY>	; BLK_833 format

	BPU_BIT	= 2			; Block position unknown in READ POS data
	.SBTTL	SCSI status codes

; Define SCSI status codes

	SCSI$M_STAT_MASK	= ^XE1	; SCSI status byte mask
	SCSI$C_GOOD_STATUS	= 0	; Good (normal) status
	SCSI$C_CHK_CONDITION	= 2	; Check condition (send a request sense)
	SCSI$C_BUSY		= 8	; Device is busy

	.SBTTL	Peripheral devices types

; Peripheral device type returned by the INQUIRY command

	SCSI$C_TAPE		= 1	; Tape device

	.SBTTL	Sense key codes

; Define SCSI sense key codes.

	SCSI$C_NO_SENSE		= 0	; No sense data
	SCSI$C_RECOVERED_ERROR	= 1	; Recovered error (treated as success)
	SCSI$C_NOT_READY	= 2	; Device not ready
	SCSI$C_MEDIUM_ERROR	= 3	; Medium (parity) error
	SCSI$C_HARDWARE_ERROR	= 4	; Hardware error
	SCSI$C_ILLEGAL_REQUEST	= 5	; Illegal request
	SCSI$C_UNIT_ATTENTION	= 6	; Unit attention (media change, reset)
	SCSI$C_DATA_PROTECT	= 7	; Data protection (writelock error)
	SCSI$C_BLANK_CHECK	= 8	; Blank check (advance past end of data)
	SCSI$C_VENDOR_UNIQUE	= 9	; Vendor unique key
	SCSI$C_COPY_ABORTED	= 10	; Copy operation aborted
	SCSI$C_ABORTED_COMMAND	= 11	; Command aborted
	SCSI$C_EQUAL		= 12	; Compare operation, data match
	SCSI$C_VOLUME_OVERFLOW	= 13	; Write beyond physical end of tape
	SCSI$C_MISCOMPARE	= 14	; Compare operation, data mismatch
	SCSI$C_RESERVED		= 15	; Reserved

; Define one additional sense code. This particular one is used to distinguish
; between fatal and non-fatal medium errors. In a medium error sense key is
; returned and the additional sense code is uncorrectable ECC error, then return
; SS$_PARITY status but continue to process commands for this device. Otherwise,
; set position list as the media error may have caused the tape to loose its
; position.

	SCSI$C_UNCORECT_ECC	= ^X11	; Uncorrectable ECC error

; Define offsets in various SCSI command packets.

	SCSI_XS$B_ERR_CODE	= 0	; Extended sense error code
	SCSI_XS$B_KEY		= 2	; Extended sense KEY field
	SCSI_XS$V_KEY		= 0	; Extended sense KEY bit number
	SCSI_XS$S_KEY		= 4	; Extended sense KEY length
	SCSI_XS$B_ADDNL_INFO	= 3	; Extended sense additional code
	SCSI_XS$B_ADDNL_CODE	= 12	; Extended sense additional code
	SCSI_XS$B_ADDNL_CODE30	= 8	; "			       " (TZ30)
	SCSI_XS$B_ADDNL_CODE50	= 8	; "			       " (TZK50)
	SCSI_XS$M_EOF		= ^X80	; Extended sense end of file
	SCSI_XS$M_EOM		= ^X40	; Extended sense end of medium
	SCSI_XS$M_ILI		= ^X20	; Extended sense illegal length indicator
	SCSI_XS$V_ADDNL_VALID	= 7	; Extended sense additional data valid


	SCSI_WFM$B_CNT		= 2	; Write filemarks count
	SCSI_RD$B_LEN		= 2	; Read transfer length
	SCSI_WRT$B_LEN		= 2	; Write transfer length
	SCSI_RWND$B_IMMED	= 1	; Rewind immediate flag

	SCSI_INQ$B_DEVTYPE	= 0	; Inquiry device type
	SCSI_INQ$B_DEVQUAL	= 1	; Inquiry device qualifier field
	SCSI_INQ$V_DEVQUAL	= 0	; Inquiry device qualifier starting bit
	SCSI_INQ$S_DEVQUAL	= 7	; Inquiry device qualifier length
	SCSI_INQ$V_REMOVABLE	= 7	; Inquiry removable bit

	SCSI_SKIP$B_CNT		= 2	; Skip record count

	SCSI_RCVD$B_HW_REV	= 0	; Receive diagnostic HW revision field
	SCSI_RCVD$B_SW_REV	= 1	; Receive diagnostic SW revision field

	SCSI_MSNS$B_WP		= 2	; Mode sense write protect field
	SCSI_MSNS$V_WP		= 7	; Mode sense write protect bit
	SCSI_MSNS$B_BLOCK	= 3	; Mode sense block descriptor length
	SCSI_MSNS$V_BLOCK	= 3	; Mode sense block descriptor bit
	SCSI_MSNS$B_DENSITY	= 4	; Mode sense density code

	SCSI_MSEL$W_RSVD0	= 0	; Mode select reserved
	SCSI_MSEL$B_SPEED	= 2	; Mode select speed field
	SCSI_MSEL$B_MODE	= 2	; Mode select buffered mode
	SCSI_MSEL$B_DSCLEN	= 3	; Mode select record descriptor length
	SCSI_MSEL$C_DSCLEN	= 8	; Mode select record descriptor length
	SCSI_MSEL$B_DENS	= 4	; Mode select density
	SCSI_MSEL$B_BLOCKS	= 5	; Mode select number of blocks
	SCSI_MSEL$B_RSVD1	= 8	; Mode select reserved
	SCSI_MSEL$B_BLKLEN	= 9	; Mode select block length 
	SCSI_MSEL$B_VULEN	= 12	; Mode select vendor unique length
	SCSI_MSEL$B_VU		= 13	; Mode select vendor unique field
	SCSI_MSEL$M_BUF		= ^X10	; Mode select buffered mode
	SCSI_MSEL$B_COMP	= 14	; Mode select data compression algorithm
	SCSI_MSEL$M_NOF 	= 7	; Number of fillers for generic device
	SCSI_MSEL$M_NOF50 	= 7	; Number of fillers for TZK50
	SCSI_MSEL$M_NOF30 	= ^X0F	; Number of fillers for TZ30
	SCSI_MSEL$M_RESEL 	= ^X40	; Reselection timeout flag

; Get/set connect characteristics symbols.

	SET_CON$L_LEN		= 0	; Length field
	SET_CON$L_CON_FLAGS	= 4	; Flags field
	SET_CON$M_DISC		= 1	; Enable disconnect flag
	SET_CON$M_NORETRY	= 2	; Disable command retry flag
	SET_CON$L_SYN_FLAG	= 8	; Synchronous flag field
	SET_CON$M_SYN		= 1	; Synchronous flag

; Request Sense resource

	REQSNS_SIZE = 19		; 19 bytes for request sense data

	.PAGE
	.SBTTL	Tape class driver extensions to the UCB
;
; Tape class driver extensions to the UCB.
;

	.SYMBOL_ALIGNMENT QUAD
	$DEFINI	UCB			; Start of UCB definitions

	. = UCB$K_LCL_TAPE_LENGTH
	
$DEF	UCB$L_HW_REV 	.BLKL	1	; Hardware revision field
$DEF	UCB$PS_UNITINIT_KPB  .BLKL 1	; Pointer to KPB allocated in unit init
$DEF	UCB$L_SCDRP	.BLKL	1	; Address of active SCDRP
$DEF	UCB$L_SCDRP_SAV1 .BLKL	1	; Address of saved SCDRP
$DEF	UCB$L_KPB_SAV1   .BLKL  1	; Address of saved KPB
$DEF	UCB$L_FLUSH_IOQFL .BLKL	1	; I/O flush queue forward link
$DEF	UCB$L_FLUSH_IOQBL .BLKL	1	; I/O flush queue backward link
$DEF	UCB$L_ERR_MASK	.BLKL	1	; Mask of error types logged so far
$DEF	UCB$B_ERR_ARRAY	.BLKB	16	; Saved error array used to filter errors
$DEF	UCB$L_SCDT	.BLKL	1	; SCDT address
$DEF	UCB$L_MK_FLAGS	.BLKL	1	; Class driver flags
	$VIELD	UCB,0,<-		;
		<REMOVABLE,,M>,-	; Removable media
		<WAITRDY_INPROG,,M>,-	; Wait for unit ready in progress
		<DISCONNECT,,M>,-	; Device supports disconnect
		<SYNCHRONOUS,,M>,-	; Device supports synchronous operation
		<DISABL_ERRLOG,,M>,-	; Disable errorlogging
		<REV_SKIP,,M>,-		; Reverse motion in progress
		<ADDNL_INFO,,M>,-	; Additional data is valid
		<SKIP_INPROG,,M>,-	; Skip file/record operation in progress
		<REWIND_INPROG,,M>,-	; Rewind operation may still be active
		<UNLOAD_INPROG,,M>,-	; Unload operation may still be active
                <COMPCHK_IN_PROG,,M>,-	; Compaction support check in progress
		<DEVICE_IS_8MM,,M>,- 	; Device is an 8MM device
		<SKIPFILE_SUPPORTED,,M>,-; Device supports READ POSITION cmd
		<SPACEFILE_INPROG,,M>,-	; Space filemarks operation in progress
                <READPOSCHK_IN_PROG,,M>,-; READ POSITION support check in progress
		<NO_POSITION_UPDATE,,M>,-; Position should not be updated
		<FIRST_READPOS_CMD,,M>,-; Set until first READ POSITION completes
		<ALLOWFAST_PER_IO,,M>,- ; Fast skip when func code modifier present
		<ALLOWFAST_ALWAYS,,M>,- ; Fast skip all the time
		<FAST_1FILE,,M>>	; Fast skip even if 1 or 2 files
$DEF	UCB$L_ADDNL_INFO .BLKL	1	; Additional extended sense info
$DEF	UCB$L_PREV_TM .BLKL 1		; Position on tape of last filemark
$DEF	UCB$L_MIN_REV .BLKL	1	; Minimum revision level
$DEF	UCB$L_MSEL_INFO .BLKL	1	; Pointer to vendor-unique mode select info
$DEF	UCB$L_COMP_PAGE .BLKL	1	; Pointer to mode select page 10 (compaction)
$DEF	UCB$L_TR_QIO_STS .BLKL	1	; Address in trace buf to put QIO status
$DEF    UCB$L_REWIND_TIME .BLKL 1       ; Time by which a rewind/immediate command
					; must be completed
$DEF    UCB$L_UNLOAD_TIME .BLKL 1       ; Time by which a unload command
					; must be completed
$DEF	UCB$B_BUSY_RETRY .BLKB	1	; Retry count for BUSY during send
$DEF	UCB$B_SENSE_KEY	 .BLKB	1	; Saved extended sense key (obsolete)
$DEF	UCB$B_SCSI_VERSION .BLKB 1	; SCSI version from INQUIRY
$DEF	UCB$B_LUN	.BLKB	1	; Logical unit number (LUN)
$DEF	UCB$B_COMP_STATE .BLKB	1	; saved data compaction state for
					; SETUP_COMPACTION routine
$DEF	UCB$B_SAVED_COMP .BLKB 1	; Current compaction state before
					; CHECK_COMPACTION_SUPPORT called
$DEF	UCB$W_MK_DENSITY .BLKW	1	; Saved 9-track density
$DEF	UCB$L_8MM_CHK 	.BLKL 1		; 8MM $ COPY check-in-progress flag
$DEF	UCB$L_DISABLE_DDR .BLKL 1	; Non-zero disables DDR
					;  (DEFAULT IS DISABLED!)
$DEF	UCB$L_ERROR_TYPE .BLKL	1	; Error type used by LOG_ERROR
$DEF	UCB$L_VMS_STATUS .BLKL  1	; VMS status used by LOG_ERROR
$DEF	UCB$L_SAV_SEND_STATUS .BLKL  1	; Status returned by SEND_COMMAND
$DEF	UCB$L_INITMO	.BLKL	1	; Unit init timeout value
	MK_INIT_TIMEOUT = 60		; Init timeout value = 60 seconds
NUM_LONGWORDS_DIAGNOSE = 6			; UCB area used by IO_DIAGNOSE
$DEF	UCB$PS_DIAGNOSE	.BLKL NUM_LONGWORDS_DIAGNOSE  
$DEF	UCB$L_INQUIRY_DATA	.BLKB SCSI$INQ$S_INQUIRY_DATA ; Inquiry data

; front panel dens stuff
$def    ucb$l_fpfind0   .blkl   4       ; safety
$def    ucb$l_fpfind    .blkl   1       ; pattern to make frontpanel easier to $
$def    ucb$l_frontpnl  .blkl   1       ; density override

$DEF	UCB$K_MK_UCBLEN			; Length of extended UCB
					
	$DEFEND	UCB			; End of UCB definitions
	.SYMBOL_ALIGNMENT NONE

	.PAGE
	.SBTTL	Errorlog packet formats
;+
; Following are the definitions for class driver errorlog packets. Each packet
; has a section common for all error types followed by an error-specific section.
;-
	$DEFINI ERROR_PACKETS

	. = EMB$L_DV_REGSAV		; Start of area to dump error info

$DEF	ERR$LW_CNT	.BLKL	1	; Count of number of LWs that follow
$DEF	ERR$REVISION	.BLKB	1	; Revision level
$DEF	ERR$HW_REV	.BLKL	1	; Hardware revision
$DEF	ERR$TYPE	.BLKB	1	; Error type
$DEF	ERR$SCSI_ID	.BLKB	1	; SCSI ID
$DEF	ERR$SCSI_LUN	.BLKB	1	; SCSI logical unit
$DEF	ERR$SCSI_SUBLUN	.BLKB	1	; SCSI sub-logical unit
$DEF	ERR$PORT_STATUS	.BLKL	1	; Port status code
$DEF	ERR$CMD_LEN	.BLKB	1	; SCSI command length field
$DEF	ERR$SCSI_STS	.BLKB	1	; SCSI status byte
$DEF	ERR$ADDIT_LEN	.BLKB	1	; Additional length field
$DEF	ERR$K_STANDARD_LENGTH		; Standard length of error packet

; Now define packets that have one or more of the variable length fields
; filled in. These fields consist of a byte count followed by n bytes of 
; data. In the standard packet defined above, the byte count field would
; contain a zero for each possible variable length field. The list of variable 
; length fields is:
;
;	o SCSI command data (up to 12 bytes)
;	o Additional data which depends upon the error type


$DEF	ERR$CMD_BYTES	.BLKB	12	; Maximum possible command bytes
$DEF	ERR$K_COMMAND_LENGTH		; Length of packet containing SCSI command

$DEF	ERR$INQUIRY_DATA .BLKB	36	; Inquiry data
$DEF	ERR$K_INQUIRY_LENGTH		; Length of packet containing INQUIRY data

	.=ERR$K_COMMAND_LENGTH
$DEF	ERR$EXTND_SENSE_DATA .BLKB 18	; Extended sense data
$DEF	ERR$K_EXTND_SENSE_LENGTH	; Length of packet containing extended 
					; sense data

	.=ERR$K_COMMAND_LENGTH
$DEF	ERR$MODE_SENSE_DATA .BLKB 150	; Mode sense data
$DEF	ERR$K_MODE_SENSE_LENGTH		; Length of packet containing mode
					; sense data
	.=ERR$K_COMMAND_LENGTH
$DEF	ERR$REASSIGN_BLOCK_DATA .BLKB 8	; Reassign block data
$DEF	ERR$K_REASSIGN_BLOCK_LENGTH	; Length of packet containing reassign 
					; block data

	.=ERR$K_COMMAND_LENGTH
$DEF	ERR$DIAGNOSTIC_DATA .BLKB 18	; Received diagnostic data
$DEF	ERR$K_DIAGNOSTIC_DATA_LENGTH	; Length of packet containing received
					; diagnostic data

	$DEFEND	ERROR_PACKETS
	.PAGE
	.SBTTL	+
	.SBTTL	+ MACRO DEFINITIONS
	.SBTTL	+
	.SBTTL	MEDIA		- MSCP media identifier to VMS device type conversion
;+
; MEDIA - modified version of the macro used in DUTUSUBS.MAR (DUDRIVER)
;
; Functional description:
;
;	This macro produces one entry in the MSCP media identifier to VMS 
;	device type conversion table.
;
; Parameters:
;
;	dd	the two character prefered device controller name ( the DD 
;		part of DDCn )
;	devnam	the hardware device name ( e.g. RA81 )
;	dtname	if DT$_'devnam' is not a legal VMS device type, this parameter 
;		gives the correct VMS device type for the device ( should be 
;		used only when DT$_'devnam' is not correct )
;-

	.MACRO	MEDIA DD, DEVNAM, DTNAME

$$BEGIN$$=-1
$$MEDIA$$=0
$$S$$=27
	.IRPC	$$L$$,<DD>
	$$TEMP$$ = ^A/$$L$$/ - ^X40
	.IF	GT $$TEMP$$
	$$MEDIA$$ = $$MEDIA$$ + <$$TEMP$$ @ $$S$$>
	.ENDC
	$$S$$ = $$S$$ - 5
	.ENDR
	.IRPC	$$L$$,<DEVNAM>
	.IF	GE <$$S$$ - 7>
	$$TEMP$$ = ^A/$$L$$/ - ^X40
	.IF	GT $$TEMP$$
	$$MEDIA$$ = $$MEDIA$$ + <$$TEMP$$ @ $$S$$>
	.IF_FALSE
	.IIF	LT $$BEGIN$$, $$BEGIN$$ = <17-$$S$$>/5
	.ENDC
	$$S$$ = $$S$$ - 5
	.ENDC
	.ENDR
	.IIF	LT $$BEGIN$$, $$BEGIN$$ = 3
	$$N$$ = %EXTRACT( $$BEGIN$$, 3, DEVNAM )
	$$MEDIA$$ = $$MEDIA$$ + $$N$$
	.LONG	$$MEDIA$$
	.ENDM	MEDIA
	.PAGE
	.SBTTL	SENSE_KEY	- Build sense key to VMS status code translation table
;+
; SENSE_KEY
;
; This macro is used to build a translation table of SCSI to VMS status codes.
; Each time extended sense information is returned by the target, the sense
; key is translated to a VMS status code using this table.  The status code
; will be located using the SCSI sense key as an index.
;
;
;-
	.MACRO	SENSE_KEY, VMS_STATUS

	.LONG	SS$_'VMS_STATUS'

	.ENDM	SENSE_KEY
	.PAGE
	.SBTTL	LOG_ERROR	- Log a SCSI tape class driver error
;+
; LOG_ERROR
;
; This macro logs a SCSI tape class driver error. The error type and VMS status
; code are placed in R7 and R8 respectively, and the LOG_ERROR routine is 
; called, which does most of the work.
;-

	.MACRO	LOG_ERROR,TYPE,VMS_STATUS,UCB=R3

	PUSHL	R5			; Save regs
	.IF DIF UCB,R5 
	MOVL	UCB,R5			; Get UCB address
	.ENDC
	MOVL	#SCSI$C_'TYPE',-	; Get error code
		UCB$L_ERROR_TYPE(R5)
	MOVL	VMS_STATUS,-		; And VMS status code
		UCB$L_VMS_STATUS(R5)
	BSBW	LOG_ERROR		; Write an errorlog entry
	POPL	R5			; Restore regs

	.ENDM	LOG_ERROR

	.MACRO	SETUP_CMD cmd=r2,ucb=R3,pdt=R4,scdrp_addr=R5
	PUSHL	'scdrp_addr'			; 
	PUSHL	'pdt'				; 
	PUSHL	'ucb'				; 
	PUSHL	'cmd'
	CALLS	#4,SETUP_CMD			; Setup the SCSI command
	.ENDM	SETUP_CMD

	.MACRO	SEND_COMMAND_ORDERED ucb=R3,pdt=R4,scdrp_addr=R5
	MOVL	#SCDRP$K_QCHAR_ORDERED,-	; Send this as an ordered command.
		SCDRP$IS_QUEUE_CHAR(scdrp_addr) 
	PUSHL	'scdrp_addr'			; 
	PUSHL	'pdt'				; 
	PUSHL	'ucb'				; 
	CALLS	#3,SEND_COMMAND			; Send the SCSI command
	.ENDM	SEND_COMMAND_ORDERED

	.MACRO	CLEANUP_CMD pdt=R4,scdrp_addr=R5
	PUSHR	#^M<R0,R1>  			; Save R0, R1
	PUSHL	'scdrp_addr'			; 
	PUSHL	'pdt'				; 
	CALLS	#2,CLEANUP_CMD			; Cleanup the SCSI command
	POPR	#^M<R0,R1>  			; Restore R0, R1
	.ENDM	CLEANUP_CMD

	.PAGE
	.SBTTL	SCSI_ERROR_CODES - Define SCSI error codes, build error length table
;+
; SCSI_ERROR_CODES
;
; This macro defines the class driver error codes and generates a table of 
; error lengths used during errorlogging to determine the size of the errorlog 
; packet to allocate. The table is indexed by the error type to find the 
; length of the packet which is stored as a word.
;-
	.MACRO	SCSI_ERROR_CODES, ERROR_LIST

	$$CODE_VALUE = 1

	.MACRO	SCSI_ERROR_CODES1 CODE, LEN

	SCSI$C_'CODE' = $$CODE_VALUE
	$$CODE_VALUE = $$CODE_VALUE + 1
	.WORD	ERR$K_'LEN'

	.ENDM	SCSI_ERROR_CODES1

	.IRP	LIST_ENTRY, <ERROR_LIST>
	SCSI_ERROR_CODES1 LIST_ENTRY
	.ENDR

	.ENDM	SCSI_ERROR_CODES
        .SBTTL  DISABLE_ERRLOG  - Temporarily disable errorlogging
        .SBTTL  REENABLE_ERRLOG - Reenable errorlogging
;+
; DISABLE_ERRLOG
; REENABLE_ERRLOG
;
; This macros are used to disable and reenable errorlogging respectively.
; The DISABL_ERRLOG flag in the UCB is used to temporarily disable errorlogging
; when the class driver prepares to do something which is likely to cause an
; error that should be supressed. For example, when checking to see if a 
; device supports COMPACTION, we don;t want a failed MODE_SELECT to generate
; an error log.
; Since the disabling of errorlogging can be nested, the old value of the 
; DISABL_ERRORLOG flag is saved in the stack.

        .MACRO  DISABLE_ERRLOG

        PUSHL UCB$L_MK_FLAGS(R3)      ; Save current flags value
        ASSUME  UCB$V_DISABL_ERRLOG LT 8
        BISB    #UCB$M_DISABL_ERRLOG,-  ; Temporarily disable errorlogging
                UCB$L_MK_FLAGS(R3)      ;

        .ENDM   DISABLE_ERRLOG

        .MACRO  REENABLE_ERRLOG

        POPL  UCB$L_MK_FLAGS(R3)      ; Reenable errorlogging

        .ENDM   REENABLE_ERRLOG

	.PAGE
	.SBTTL	MK_ALLOC_SCDRP	- Allocate an SCDRP
;+
; MK_ALLOC_SCDRP
;
; This macro is used to allocate an SCDRP on the kernel process stack.
;
; INPUTS:
;	R3	- UCB address
;
; OUTPUTS:
;	R5	- SCDRP address
;	R0	- destroyed
;

	SCDRP_ALLO_LEN = <<<SCDRP$K_LENGTH+7>&^C7> + 8>

	.MACRO	MK_ALLOC_SCDRP
	SUBL	#SCDRP_ALLO_LEN,SP	      ; Allocate SCDRP on the stack
	ADDL3	#7,SP,R5                      ; R5 = SCDRP address, quadword
	BICL	#7,R5			      ;      aligned
	.SET_REGISTERS	ALIGNED=<R5>
	BSBW	INIT_SCDRP		      ; Initialize allocated SCDRP
	.ENDM	MK_ALLOC_SCDRP

	.PAGE
	.SBTTL	MK_DEACTIVATE_SCDRP	- Deactivate an SCDRP
;+
; MK_DEACTIVATE_SCDRP
; 
; This macro deactivates an SCDRP by clearing the appropriate UCB field.
; A sanity check is performed to ensure that any map registers for this
; command have been deallocated.  By default, deallocation of the SCDRP
; will occur implicitly via kernel process termination, although 
; explicit deallocation may be requested by specifying CLRSTK=YES.
;
; INPUTS: 
;	R3	- UCB address
;	R5	- SCDRP address
;
; OUTPUTS:
;	UCB$L_SCDRP - Initialized
;
	.MACRO	MK_DEACTIVATE_SCDRP,CLRSTK=NO,?L1,?L2

	.IF DEFINED DEBUG
	TSTW	SCDRP$W_MAPREG(R5)	; Hanging on to any mapping regs?
	BNEQ	L1			; Branch if so
	TSTW	SCDRP$W_NUMREG(R5)	; Hanging on to any mapping regs?
	BEQL	L2			; Branch if not
L1:	BUG_CHECK INCONSTATE,FATAL	; Should not deactivate an SCDRP 
	.ENDC

L2:	CLRL	UCB$L_SCDRP(R3)		; No active SCDRP for this UCB
	.IF IDN <CLRSTK>,<YES>		; If explicit deallocation is required
	    ADDL #SCDRP_ALLO_LEN,SP 	; Clear SCDRP off the stack
	.ENDC
	.ENDM	MK_DEACTIVATE_SCDRP


	.PAGE
	.SBTTL	SCSI_CMD	- Define a SCSI command packet
;+
; SCSI_CMD
;
; This macro defines the contents of a SCSI command packet. Each SCSI command
; can have associated with it a DMA buffer used during the DATAIN/DATAOUT bus
; phases. A DMA length of zero indicates there is no DATA(IN/OUT) phase 
; associated with this command (except in the case of a read/write SCSI command
; which is handled specially. The SETUP_CMD routine uses this information in 
; preparing to send a SCSI command. The macro generates a label and the SCSI 
; command information as follows:
;
;	+-----------------------+
;	|    SCSI cmd length	| 1 byte
;	+-----------------------+
;	|    SCSI cmd bytes	| n bytes
;	+-----------------------+
;	|Timeout value (seconds)| 2 bytes
;	+-----------------------+
;	|   DMA buffer length	| 2 bytes
;	+-----------------------+
;	|     DMA direction	| 1 byte
;	+-----------------------+
;
; DMA direction is defined as: 0=write, 1=read.
;-

	.MACRO	SCSI_CMD, NAME, CMD_BYTES, DMA_LEN=0, DMA_DIR=READ, TIMEOUT=30
	.ALIGN QUAD	
CMD_'NAME'::
	$$$BYTE_COUNT=0
	.IRP CMD_BYTE, <CMD_BYTES>
	$$$BYTE_COUNT = $$$BYTE_COUNT + 1
	.IIF EQ $$$BYTE_COUNT-1, SCSI$C_'NAME' = CMD_BYTE	; Define opcode
	.ENDR
	.BYTE	$$$BYTE_COUNT
	.IRP CMD_BYTE, <CMD_BYTES>
	.BYTE	CMD_BYTE
	.ENDR
	.WORD	TIMEOUT
	.WORD	DMA_LEN
	$$$DIRECTION = 0
	.IIF IDN DMA_DIR, READ, $$$DIRECTION = 1
	.BYTE	$$$DIRECTION
	.IIF	IDN NAME,MODE_SELECT_TMP, MODE_SEL_TMP_LEN = .-CMD_'NAME' 

	.ENDM	SCSI_CMD
	.PAGE
	.SBTTL	IF_TZ30		- Branch if device a TZ30
	.SBTTL	IF_TK50		- Branch if device is a TZK50
	.SBTTL	IFNOT_TZ30	- Branch if device is not a TZ300
	.SBTTL	IFNOT_TK50	- Branch if device is not a TZK50
	.SBTTL	IF_TK		- Branch if device is a TZK50 or TZ30
	.SBTTL	IFNOT_TK	- Branch if device is not a TZK50 or TZ30
	.SBTTL	IF_TSZ05	- Branch if device is a TSZ05
	.SBTTL	IFNOT_TSZ05	- Branch if device is not a TSZ05
	.SBTTL	IF_TSZ07	- Branch if device is a TSZ07
	.SBTTL	IFNOT_TSZ07	- Branch if device is not a TSZ07
	.SBTTL	IF_TZK10	- Branch if device is a TZK10
	.SBTTL	IFNOT_TZK10	- Branch if device is not a TZK10
	.SBTTL	IF_8MM		- Branch if device is an 8mm device
	.SBTTL	IFNOT_8MM	- Branch if device is not an 8mm device
	.SBTTL	IF_TZK11	- Branch if device is a TZK11
	.SBTTL	IF_TKZ09	- Branch if device is a TKZ09
	.SBTTL	IF_TKZ60	- Branch if device is a TKZ60
	.SBTTL	IFNOT_TKZ60	- Branch if device is not a TKZ60

;-
; IF_TZ30
; IF_TK50
; IFNOT_TZ30
; IF_TK
; IFNOT_TK
; IF_TSZ05
; IFNOT_TSZ05
; IF_TSZ07
; IFNOT_TSZ07
; IF_TZK10
; IFNOT_TZK10
; IF_TZK11
; IF_TKZ09
; IF_TKZ60
; IFNOT_TKZ60
;
; These macros are used for device-dependent dispatching. 
;-

	.MACRO	IF_TZ30, LABEL, UCB=R3

	CMPB	UCB$B_DEVTYPE(UCB),-	; Is device a TZ30?
		#DT$_TZ30		;
	BEQL	LABEL			; Branch if so

	.ENDM	IF_TZ30

	.MACRO	IF_TK50, LABEL, UCB=R3

	CMPB	UCB$B_DEVTYPE(UCB),-	; Is device a TK50?
		#DT$_TK50		;
	BEQL	LABEL			; Branch if so

	.ENDM	IF_TK50

	.MACRO	IFNOT_TK50, LABEL, UCB=R3

	CMPB	UCB$B_DEVTYPE(UCB),-	; Is device a TK50?
		#DT$_TK50		;
	BNEQ	LABEL			; Branch if not

	.ENDM	IFNOT_TK50

	.MACRO	IFNOT_TZ30, LABEL, UCB=R3

	CMPB	UCB$B_DEVTYPE(UCB),-	; Is device a TZ30?
		#DT$_TZ30		;
	BNEQ	LABEL			; Branch if not

	.ENDM	IFNOT_TZ30

	.MACRO	IF_TK, LABEL, UCB=R3

	CMPB	UCB$B_DEVTYPE(UCB),-	; Is device a TZ30?
		#DT$_TZ30		;
	BEQL	LABEL			; Branch if so
	CMPB	UCB$B_DEVTYPE(UCB),-	; Is device a TK50?
		#DT$_TK50		;
	BEQL	LABEL			; Branch if so

	.ENDM	IF_TK

	.MACRO	IFNOT_TK, LABEL, UCB=R3, ?L

	CMPB	UCB$B_DEVTYPE(UCB),-	; Is device a TZ30?
		#DT$_TZ30		;
	BEQL	L			; Branch if so
	CMPB	UCB$B_DEVTYPE(UCB),-	; Is device a TK50?
		#DT$_TK50		;
	BNEQ	LABEL			; Branch if not
L:
	.ENDM	IFNOT_TK

	.MACRO	IF_TSZ05, LABEL, UCB=R3
	CMPB	UCB$B_DEVTYPE(UCB),-	; Is device a TSZ05?
		#DT$_TSZ05		;
	BEQL	LABEL			; Branch if so
	.ENDM	IF_TSZ05

	.MACRO	IFNOT_TSZ05, LABEL, UCB=R3,
	CMPB	UCB$B_DEVTYPE(UCB),-	; Is device a TSZ05?
		#DT$_TSZ05		;
	BNEQ	LABEL			; Branch if not
	.ENDM	IFNOT_TSZ05

	.MACRO	IF_TSZ07, LABEL, UCB=R3
	CMPB	UCB$B_DEVTYPE(UCB),-	; Is device a TSZ07?
		#DT$_TSZ07		;
	BEQL	LABEL			; Branch if so
	.ENDM	IF_TSZ07

	.MACRO	IFNOT_TSZ07, LABEL, UCB=R3
	CMPB	UCB$B_DEVTYPE(UCB),-	; Is device a TSZ07?
		#DT$_TSZ07		;
	BNEQ	LABEL			; Branch if not
	.ENDM	IFNOT_TSZ07

	.MACRO	IF_TZK10, LABEL, UCB=R3
	CMPB	UCB$B_DEVTYPE(UCB),-	; Is device a TZK10 ?
		#DT$_TZK10		; for retensioning
	BEQL	LABEL			; Branch if yes
	.ENDM	IF_TZK10

	.MACRO	IFNOT_TZK10, LABEL, UCB=R3
	CMPB	UCB$B_DEVTYPE(UCB),-	; Is device a TZK10 ?
		#DT$_TZK10		; for retensioning
	BNEQ	LABEL			; Branch if not
	.ENDM	IFNOT_TZK10

	.MACRO	IF_TZK11, LABEL, UCB=R3
	CMPB	UCB$B_DEVTYPE(UCB),-	; Is device a TZK11 ?
		#DT$_TZK11		; for retensioning
	BEQL	LABEL			; Branch if yes
	.ENDM	IF_TZK11

	.MACRO	IF_TKZ09, LABEL, UCB=R3
	CMPB	UCB$B_DEVTYPE(UCB),-	; Is device a TKZ09 ?
		#DT$_TKZ09		; 
	BEQL	LABEL			; Branch if yes
	.ENDM	IF_TKZ09

	.MACRO	IF_TKZ60, LABEL, UCB=R3
	CMPB	UCB$B_DEVTYPE(UCB),-	; Is device a TKZ60 ?
		#DT$_TKZ60		; 
	BEQL	LABEL			; Branch if yes
	.ENDM	IF_TKZ60

	.MACRO	IFNOT_TKZ60, LABEL, UCB=R3
	CMPB	UCB$B_DEVTYPE(UCB),-	; Is device a TKZ60 ?
		#DT$_TKZ60		; 
	BNEQ	LABEL			; Branch if no
	.ENDM	IFNOT_TKZ60

	.MACRO	IF_8MM, LABEL, UCB=R3
	BITL	#UCB$M_DEVICE_IS_8MM,-
		UCB$L_MK_FLAGS(UCB)	; Is device an 8mm tape
	BNEQ	LABEL                   ; Branch if so
	.ENDM	IF_8MM

	.MACRO	IFNOT_8MM, LABEL, UCB=R3
	BITL	#UCB$M_DEVICE_IS_8MM,-
		UCB$L_MK_FLAGS(UCB)	; Is device an 8mm tape
	BEQL	LABEL                   ; Branch if not
	.ENDM	IFNOT_8MM

;+
; $ARG_DEF - Define a Routine's Input Parameters.
;-
	.MACRO	$ARG_DEF argument_list
	argument_offset = 4
	.IRP	argument, <argument_list>
	argument = argument_offset
	argument_offset = argument_offset + 4
	.ENDR
	.ENDM	$ARG_DEF


	.PAGE
	.SBTTL	+
	.SBTTL	+ DRIVER TABLES
	.SBTTL	+
	.SBTTL	Driver prologue table
;+
; Driver prologue table
;
; This table provides various information about the driver such as its name
; and length, and causes initialization of various fields in the I/O database 
; when the driver is loaded.
;-

	DPTAB	-				; DPT-creation macro
		STEP=2,-			; Driver is Step 2
		ADAPTER=NULL,-			; Adapter type
		UCBSIZE=<UCB$K_MK_UCBLEN>,-	; Length of UCB
		NAME=ZMDRIVER,-			; Driver name
		SMP=YES,-                       ; Driver runs in SMP environment
		FLAGS=<DPT$M_TPALLOC!-		; Driver uses tape allocation class
		       DPT$M_SNAPSHOT!-		; Driver supports snapshots
		       DPT$M_NO_IDB_DISPATCH>	; Don't fill in IDB$L_UCBLST
	DPT_STORE INIT				; Start of load
						; initialization table
	DPT_STORE DDB,DDB$L_ACPD,L,<^A\MTA\>	; Default ACP name
	DPT_STORE UCB,UCB$L_MAXBCNT,L,MAX_BCNT	; Max byte count
	DPT_STORE UCB,UCB$B_FLCK,B,SPL$C_IOLOCK8 ; Device FORK LOCK
	DPT_STORE UCB,UCB$B_DIPL,B,22		; Device interrupt IPL
	DPT_STORE UCB,UCB$L_DEVCHAR,L,<-	; Device characteristics
		DEV$M_FOD!-			; Files oriented
		DEV$M_DIR!-			; Directory structured
		DEV$M_AVL!-			; Available
		DEV$M_ELG!-			; Error logging enabled
		DEV$M_IDV!-			; Input device
		DEV$M_ODV!-			; Output device
 		DEV$M_SDI!-			; Single directory device
		DEV$M_SQD>			; Random Access Device
	DPT_STORE UCB,UCB$L_DEVCHAR2,L,<-	; Device characteristics
		DEV$M_SCSI!-			; device is a SCSI device
		DEV$M_NNM>                      ; Prefix name with "node$"
	DPT_STORE UCB,UCB$B_DEVCLASS,B,DC$_TAPE	; Sample device class
 	DPT_STORE UCB,UCB$B_DEVTYPE,B,-		; Device type (default)
		DT$_GENERIC_MK
	DPT_STORE UCB,UCB$W_DEVBUFSIZ,W,2048	; Default buffer size
 	DPT_STORE UCB,UCB$L_DEVDEPEND,W,-	; Default tape parameters
		DEF_TAPE_PARAMS			; Format=DEFAULT, Density=BLK_833
	DPT_STORE UCB,UCB$L_ERTCNT,L,16		; Error retry count
	DPT_STORE UCB,UCB$L_ERTMAX,L,16		; Max error retry count
	DPT_STORE UCB,UCB$L_MK_FLAGS,L,0	; Initialize flags field
	DPT_STORE UCB,UCB$L_ERR_MASK,L,0	; Initialize error mask field
        DPT_STORE UCB,UCB$W_MK_DENSITY,W,-      ; Initialize 9-track H/W
                MT$K_GCR_6250-2                 ; density.
        DPT_STORE UCB,UCB$L_DISABLE_DDR,L,1     ; Non zero disables DDR
	DPT_STORE REINIT			; Start of reload
						; initialization table
	DPT_STORE CRB,CRB$B_FLCK,B,IPL$_IOLOCK8	; Initialize fork lock field

	DPT_STORE END				; End of initialization
						; tables
	.PAGE
	.SBTTL	Driver dispatch table
;+
; Driver dispatch table
;
; This table defines the entry points into the driver.
;-

	DDTAB	-				; DDT-creation macro
		DEVNAM=ZM,-			; Name of device
		START=EXE_STD$KP_STARTIO,-	; Caller of Start I/O routine
		KP_STARTIO=MK_STARTIO,-		; Start I/O routine
		KP_STACK_SIZE=8192,-		; Size of kernel process stack
		KP_REG_MASK=KPREG$K_HLL_REG_MASK,- ; Kernel process reg save mask
		CTRLINIT=MK_CTRL_INIT,-		; Controller init routine
		UNITINIT=MK_UNIT_INIT,-		; Unit init routine
		FUNCTB=MK_FUNCTABLE,-		; FDT address
		CANCEL=MK_CANCEL,-		; Cancel I/O routine
		REGDMP=MK_REG_DUMP,-		; Register dump routine
		FAST_FDT=ACP_STD$FASTIO_BLOCK	; Fast-FDT routine address

	.SBTTL	Function decision table
;+
; Step 2 Function decision table
;
; This table lists the buffered QIO function codes and the action routines with their
; valid function codes.   
;-

	FDT_INI MK_FUNCTABLE			; FDT for Driver
	FDT_BUF	-				; Buffered I/O functions
 		<NOP,-				; No operation
 		 UNLOAD,-			; Unload volume
 		 SPACERECORD,-			; Space records
 		 RECAL,-			; Recalibrate (rewind)
 		 DRVCLR,-			; Drive clear
 		 READPRESET,-			; Read preset
 		 PACKACK,-			; Pack acknowledge
 		 ERASETAPE,-			; Erase tape
 		 DSE,-				; Data security erase
 		 SENSECHAR,-			; Sense characteristics
 		 SETCHAR,-			; Set characteristics
 		 SPACEFILE,-			; Space files
 		 WRITEMARK,-			; Write tape mark
 		 SENSEMODE,-			; Sense mode
 		 SETMODE,-			; Set mode
 		 REWIND,-			; Rewind
 		 REWINDOFF,-			; Rewind and unload
 		 SKIPRECORD,-			; Skip records
 		 SKIPFILE,-			; Skip files
 		 WRITEOF,-			; Write end of file
 		 ACCESS,-			; Access file &/or find directory entry
 		 ACPCONTROL,-			; ACP control function
 		 CREATE,-			; Create file &/or create dir entry
 		 DEACCESS,-			; Deaccess file
 		 DELETE,-			; Delete file and/or directory entry
 		 MODIFY,-			; Modify file attributes
 		 MOUNT>				; Mount volume
 
	FDT_64	<-				; Functions supporting 64-bit addresses
 		 AVAILABLE,-			; Available (rewind/nowait clear valid)
		 DIAGNOSE,-			; Special pass-thru function
 		 DRVCLR,-			; Drive clear
 		 DSE,-				; Data security erase
 		 ERASETAPE,-			; Erase tape
 		 NOP,-				; No operation
 		 PACKACK,-			; Pack acknowledge
 		 READLBLK,-			; Read logical block forward
 		 READPBLK,-			; Read physical block forward
 		 READPRESET,-			; Read in preset
 		 READVBLK,-			; Read virtual block
 		 RECAL,-			; Recalibrate (rewind)
 		 REREADN,-			; Read next
 		 REREADP,-			; Read previous
 		 REWIND,-			; Rewind
 		 REWINDOFF,-			; Rewind and set offline
 		 SETCHAR,-			; Set characteristics
 		 SETMODE,-			; Set mode
 		 UNLOAD,-			; Unload volume
 		 WRITECHECK,-			; Write check forward
 		 WRITEOF,-			; Write end of file
 		 WRITELBLK,-			; Write logical block
 		 WRITEMARK,-			; Write tape mark
 		 WRITEPBLK,-			; Write physical block
 		 WRITERET,-			; Write retry
 		 WRITEVBLK>			; Write virtual block

 	FDT_ACT	ACP_STD$READBLK,-		; Read functions
 		<READLBLK,-			; Read logical block forward
 		 READPBLK,-			; Read physical block forward
 		 REREADN,-			; Read next
 		 REREADP,-			; Read previous
 		 READVBLK>			; Read virtual block
 
 	FDT_ACT	ACP_STD$WRITEBLK,-		; Write functions
 		<WRITECHECK,-			; Write check forward
 		 WRITELBLK,-			; Write logical block
 		 WRITEPBLK,-			; Write physical block
 		 WRITERET,-			; Write retry
 		 WRITEVBLK>			; Write virtual block
 
 	FDT_ACT	ACP_STD$ACCESS,<ACCESS,CREATE> ; Access & create file or directory
 
 	FDT_ACT	ACP_STD$DEACCESS,<DEACCESS> 	; Deaccess file
 
 	FDT_ACT	ACP_STD$MODIFY,-		;
 		<ACPCONTROL,-			; ACP control function
 		 DELETE,-			; Delete file or directory entry
 		 MODIFY>			; Modify file attributes
 
 	FDT_ACT	ACP_STD$MOUNT,<MOUNT>		; Mount volume
 
 	FDT_ACT	MT_STD$CHECK_ACCESS,-		; Magtape check access funcitons
 		<ERASETAPE,-			; Erase tape
 		 DSE,-				; Data security erase
 		 WRITEMARK,-			; Write tape mark
 		 WRITEOF>			; Write end of file
 
 	FDT_ACT	EXE_STD$ZEROPARM,-		; Zero parameter functions
 		<NOP,-				; No operation
 		 UNLOAD,-			; Unload volume
 		 RECAL,-			; Recalibrate (rewind)
 		 REWIND,-			; Rewind
 		 REWINDOFF,-			; Rewind and set offline
 		 DRVCLR,-			; Drive clear
 		 READPRESET,-			; Read in preset
 		 PACKACK,-			; Pack acknowledge
 		 AVAILABLE>			; Available (rewind/nowait clear valid)
 
 	FDT_ACT	EXE_STD$ONEPARM,-		; One parameter functions
 		<SPACERECORD,-			; Space records
 		 SPACEFILE,-			; Space files
 		 SKIPRECORD,-			; Skip records
 		 SKIPFILE>			; Skip files
 
 	FDT_ACT	MK_SENSEMODE,-			; Sense tape characteristics
		<SENSECHAR,-			;  Sense characteristics
		 SENSEMODE>			;  Sense mode

; Notice that the SETCHAR function is used for our tape skipfile control
; also because it requires PHY_IO privilege to use.

 	FDT_ACT	MK_SETCHAR,-			; Set tape characteristics
 		<SETCHAR>			;

 	FDT_ACT	EXE_STD$SETMODE,-		; Set tape characteristics
 		<SETMODE>			;

	FDT_ACT	MK_DIAGNOSE,<-			; Special pass-through function
		DIAGNOSE>			; 


	.PAGE	
; TQE_WAIT data
SECOND =	10000000 			; Number of clock ticks in a second
ONE_SECOND:	.LONG	< 1*SECOND>,0		; 1 second
TWO_SECONDS:	.LONG	< 2*SECOND>,0		; 2 seconds
THIRTY_SECONDS:	.LONG	<30*SECOND>,0		; 30 seconds
SIXTY_SECONDS:	.LONG	<60*SECOND>,0		; 60 seconds
POLL_INTERVAL:	.LONG	<READY_POLL_INTERVAL*SECOND>,0		


	.PAGE	
	.SBTTL	SCSI_DEV_TYPES	- Build SCSI device table
;+
; SCSI_DEV_TYPES
;
; This macro builds a table of pre-defined SCSI device types. During unit 
; initialization, an inquiry command is sent to the target which returns
; 8 bytes of ID string. The table is then scanned for a matching ID. If one
; is found, information for that entry is copied into the UCB, including the 
; device type, media ID, disconnect/synchronous flags, and various timeout 
; values. If no matching entry is found, the device is assumed to be a 
; "generic" SCSI disk, and the entry for generic devices is used. Each entry 
; in the device type table has the following format:

;	+-----------------------+
;	|    VMS device type	| 1 byte
;	+-----------------------+
;	|     Density code	| 1 byte
;	+-----------------------+
;	|  Vendor-unique info	| 10 bytes
;	+-----------------------+
;	|	ID string	| 8 bytes
;	+-----------------------+
;	|  Min revision level	| 4 bytes
;	+-----------------------+
;	|	Media ID	| 4 bytes
;	+-----------------------+
;	|	Loader info	| 1 byte
;	+-----------------------+
;	|  Pad for alignment	| 3 bytes
;	+-----------------------+
;
;
; The table is terminated with a VMS device code of 0.
;-
	$DEFINI	SCSI_DEVICE_ENTRY
$DEF	DTYP_TYPE	.BLKB	1		 
$DEF	DTYP_DENSITY	.BLKB	1		 
$DEF	DTYP_VENDOR	.BLKB	10		 
$DEF	DTYP_ID_LOW	.BLKL	1		 
$DEF	DTYP_ID_HIGH	.BLKL	1		 
$DEF	DTYP_MINREV	.BLKL	1		 
$DEF	DTYP_MEDIA_ID	.BLKL	1		 
$DEF	DTYP_FLAGS	.BLKL	1		; 
	$VIELD	DTYP,0,<-			;
	  <LOADER,,M>,-				; Bit 0 = loader support
	  <NO_SYNC_MODE,,M>,-			; Bit 1 = inhibit sync mode 
	  <NO_SKIPFILE ,,M>>			; Bit 2 = inhibit skip-by-file
$DEF   	DTYP_TABLE_ENTRY_SIZE			; Size of SCSI Device Table Entry
	$DEFEND	SCSI_DEVICE_ENTRY

	ASSUME	DTYP_TABLE_ENTRY_SIZE EQ 32

	.MACRO	SCSI_DEV_TYPES, LIST

	.MACRO	SCSI_DEV_TYPES1, ID_STRING, DEVICE_TYPE, MINIMUM_REVISION, -
		DENSITY, MODE_SEL_INFO, LOADER, SYNC_MODE, SKIPFILE

	.IF IDN <ID_STRING>, TZ30
TZ30_DEV_TYPE:
	.ENDC

	.IF IDN <ID_STRING>, TK50
TK50_DEV_TYPE:
	.ENDC

	.IF IDN <ID_STRING>, GENERIC
GENERIC_DEV_TYPE:
	.ENDC

; Device type

	.BYTE	DT$_'DEVICE_TYPE'

; Density code

	.BYTE	MT$K_'DENSITY'


; Vendor-unique mode select data. This is a counted string of bytes with the
; first byte specifying the number of bytes that follow. The total field length,
; including the count, is 10 bytes.

	$$$COUNT = 0
	.IRP	MODE_SEL_BYTE,<MODE_SEL_INFO>
	$$$COUNT = $$$COUNT + 1
	.ENDR

	.IIF	GT $$$COUNT-7,	.ERROR ; - Illegal number of mode sense bytes

	.BYTE	$$$COUNT
	.IRP	MODE_SEL_BYTE,<MODE_SEL_INFO>
	.BYTE	MODE_SEL_BYTE
	.ENDR

	.REPT	9-$$$COUNT
	.BYTE	0
	.ENDR

; 8 character product ID string, padded with spaces

	.NCHR	$$$STRLEN,<ID_STRING>
	.IIF GT $$$STRLEN-8, .ERROR ;Illegal SCSI product ID: ID_STRING
	$$$PADCNT = 8-$$$STRLEN
	.ASCII	/ID_STRING/
	.REPT	$$$PADCNT
	.ASCII	/ /
	.ENDR

; Minimum revision level. This field is compared to the revision field returned
; in the inquiry data. If the device is out of rev, an error is logged.

	.NCHR	$$$MINREV,<MINIMUM_REVISION>
	.IIF GT $$$MINREV-4, .ERROR ;Illegal minimum rev level: MINIMUM_REVISION
	.ASCII	/MINIMUM_REVISION/

; Media ID field

	.IF IDN DEVICE_TYPE,GENERIC_MK
	MEDIA   <MK>, <MKX00>, DT$_GENERIC_MK   ; Media value for generic MK device
	.IFF
	MEDIA <MK>, <DEVICE_TYPE>
	.ENDC
	

; Flags

	$$$FLAGS = 0
	.IIF IDN LOADER, YES, $$$FLAGS = $$$FLAGS + DTYP$M_LOADER
	.IIF IDN SYNC_MODE, NO, $$$FLAGS = $$$FLAGS + DTYP$M_NO_SYNC_MODE
	.IIF IDN SKIPFILE, NO, $$$FLAGS = $$$FLAGS + DTYP$M_NO_SKIPFILE
	.BYTE	$$$FLAGS


; Pad table entry for longword alignment

	.BLKB	3
	.ENDM	SCSI_DEV_TYPES1

	.IRP	ENTRY,<LIST>
	SCSI_DEV_TYPES1 ENTRY
	.ENDR

	.ENDM	SCSI_DEV_TYPES

	.PAGE
	.SBTTL	SCSI device types table
;+
; SCSI_DEVICE_TABLE
;
; This table is used to translate the product ID field from the inquiry data
; to a VMS device type and internal MK device type. The internal type code is
; used so that we can support multiple SCSI tape drives with a single VMS device
; code (generic_mk), yet distinguish between these drives, which may require 
; different vendor-unique mode select data. The mode select data in this table
; is appended to the standard 4 byte parameter list header and 8 byte block
; descriptor. Several reserved entries are left in the table to allow for
; patching the driver to add support for new devices in the future. Note that
; because the TZ30 and TK50 don't conform to the standard inquiry data format,
; the GET_DEVICE_TYPE routine uses special case code to find their entries
; in the table.
;
; Clarification of the meaning of some fields:
;
; Sync mode:  DEFAULT means that the default algorithms in the drivers
;             should be used to determine the correct transfer mode.
;	      NO means that, no matter what the algorithms may determine,
;	      the device must be forced to async mode.
;
; Skipfile:   DEFAULT means that the default algorithms in the drivers
;             should be used to determine whether skipfile should be
;	      supported. NO means that, no matter what the algorithms may determine,
;	      the device must be forced to *not* skip by filemarks.
;
; Note:  Any new devices added to this table should be assigned a VMS dev type
; of GENERIC_MK in order to avoid the need to define a DT$ symbol in Starlet.
;-

SCSI_DEVICE_TABLE:
	.list	meb
SCSI_DEV_TYPES <-
;
;       ID string     VMS dev type     Min Rev    Density   Mode select info  Loader  Sync mode	  Skipfile   
;       ---------     ------------     -------    -------   ----------------  ------  ---------   --------
;
	<<TZ30>,       TZ30,           <0701>,    BLK_833,  <1, <^X4F>>,      NO,     DEFAULT,    DEFAULT>,-
	<<TK50>,       TK50,           <2D05>,    BLK_833,  <1, <^X47>>,      NO,     DEFAULT,    DEFAULT>,-
	<<TLZ04 19>,   TLZ04,	       <    >,	  DEFAULT,  <>,               NO,     DEFAULT,    DEFAULT>,-
	<<TLZ04   >,   TLZ04,	       <    >,	  DEFAULT,  <>,               NO,     DEFAULT,    DEFAULT>,-                
	<<HSCAN 19>,   TLZ04,	       <    >,    DEFAULT,  <>,               NO,     DEFAULT,    DEFAULT>,-
	<<TZK10   >,   TZK10,	       <    >,	  DEFAULT,  <>,               NO,     DEFAULT,    DEFAULT>,-
	<<0-STD1-0>,   TSZ05,          <    >,    PE_1600,  <>,               NO,     DEFAULT,    DEFAULT>,-
	<<TSZ05>,      TSZ05,          <    >,    PE_1600,  <>,               NO,     DEFAULT,    NO>,-
	<<TSZ07>,      TSZ07,          <    >,    GCR_6250, <>,               NO,     DEFAULT,    NO>,-
	<<TZK10>,      TZK10,          <    >,    DEFAULT,  <>,               NO,     DEFAULT,    NO>>                 

SCSI_DEV_TYPES <-
;
;       ID string     VMS dev type     Min Rev    Density   Mode select info  Loader  Sync mode	  Skipfile   
;       ---------     ------------     -------    -------   ----------------  ------  ---------   --------
;
	<<TZ85>,       TZ85,           <    >,    DEFAULT,  <>,               NO,     DEFAULT,    DEFAULT>,-
	<<TZ86>,       TZ86,           <    >,    DEFAULT,  <>,               NO,     DEFAULT,    DEFAULT>,-
	<<TZ87>,       TZ87,           <    >,    DEFAULT,  <>,               NO,     DEFAULT,    DEFAULT>,-
        <<TLZ06>,      TLZ06,          <    >,    DEFAULT,  <>,               NO,     DEFAULT,    DEFAULT>,-
        <<TKZ60>,      TKZ60,          <    >,    DEFAULT,  <>,               NO,     DEFAULT,    NO>,-
        <<TZK11>,      TZK11,          <    >,    DEFAULT,  <>,               NO,     DEFAULT,    DEFAULT>,-
        <<TKZ09>,      TKZ09,          <    >,    DEFAULT,  <>,               NO,     DEFAULT,    NO>>

SCSI_DEV_TYPES <-
	<<GENERIC>,    GENERIC_MK,     <    >,    DEFAULT,  <>,               NO,     DEFAULT,    DEFAULT>,-
	<<RESERVE1>,   GENERIC_MK,     <    >,    DEFAULT,  <>,               NO,     DEFAULT,    DEFAULT>,-
	<<RESERVE2>,   GENERIC_MK,     <    >,    DEFAULT,  <>,               NO,     DEFAULT,    DEFAULT>,-
	<<RESERVE3>,   GENERIC_MK,     <    >,    DEFAULT,  <>,               NO,     DEFAULT,    DEFAULT>,-
	<<RESERVE4>,   GENERIC_MK,     <    >,    DEFAULT,  <>,               NO,     DEFAULT,    DEFAULT>>

SCSI_DEV_TYPES <-
;
;       ID string     VMS dev type     Min Rev    Density   Mode select info  Loader  Sync mode	  Skipfile   
;       ---------     ------------     -------    -------   ----------------  ------  ---------   --------
;
        <<TLZ6>,       TLZ06,           <    >,    DEFAULT,  <>, 	      YES,    DEFAULT,    DEFAULT>,-
        <<TKZ60L>,     TKZ60,           <    >,    DEFAULT,  <>, 	      YES,    DEFAULT,    DEFAULT>,-
        <<TZ857>,      TZ85,            <    >,    DEFAULT,  <>,              YES,    DEFAULT,    DEFAULT>,-
        <<TZ867>,      TZ86,            <    >,    DEFAULT,  <>, 	      YES,    DEFAULT,    DEFAULT>,-
        <<TZ877>,      TZ87,            <    >,    DEFAULT,  <>, 	      YES,    DEFAULT,    DEFAULT>,-
	<<EXB-8200>,  GENERIC_MK,      	<    >,    DEFAULT,  <<^X22>>,        NO,     DEFAULT,    DEFAULT>,-
	<<EXB-8500>,  GENERIC_MK,      	<    >,    DEFAULT,  <<^X22>>,        NO,     DEFAULT,    DEFAULT>>

SCSI_DEV_TYPES <-
	<<HST00>,      HST00,          <    >,    DEFAULT,  <>,		      NO,     DEFAULT,    DEFAULT>,-
	<<HST01>,      HST01,          <    >,    DEFAULT,  <>,               YES,    DEFAULT,    DEFAULT>,-
	<<TLZ07>,      TLZ07,          <    >,    DEFAULT,  <>,		      NO,     DEFAULT,    DEFAULT>,-
	<<TLZ7>,       TLZ07,          <    >,    DEFAULT,  <>, 	      YES,    DEFAULT,    DEFAULT>,-
	<<TZ88>,       TZ88,           <    >,    DEFAULT,  <>		      NO,     DEFAULT,    DEFAULT>,-
        <<TZ885>,      TZ88,           <    >,    DEFAULT,  <>, 	      YES,    DEFAULT,    DEFAULT>,-
        <<TZ887>,      TZ88,           <    >,    DEFAULT,  <>, 	      YES,    DEFAULT,    DEFAULT>>

SCSI_DEV_TYPES <-
	<<TZ89>,       TZ89,           <    >,    DEFAULT,  <>		      NO,     DEFAULT,    DEFAULT>,-
        <<TZ895>,      TZ89,           <    >,    DEFAULT,  <>, 	      YES,    DEFAULT,    DEFAULT>,-
        <<TZ897>,      TZ89,           <    >,    DEFAULT,  <>, 	      YES,    DEFAULT,    DEFAULT>,-
        <<TZ875>,      TZ87,           <    >,    DEFAULT,  <>, 	      YES,    DEFAULT,    DEFAULT>,-
        <<TL810>,      TL810,          <    >,    DEFAULT,  <>, 	      YES,    DEFAULT,    DEFAULT>,-
        <<TL820>,      TL820,          <    >,    DEFAULT,  <>, 	      YES,    DEFAULT,    DEFAULT>>

	.BYTE	0	; End of table
	.nlist	meb
	.PAGE
	.SBTTL	SCSI command definition tables

	SCSI_CMD -
		NAME = TEST_UNIT_READY,-
		CMD_BYTES = <0, 0, 0, 0, 0, 0>

	SCSI_CMD -
		NAME = REQUEST_SENSE,-
		CMD_BYTES = <3, 0, 0, 0, <REQSNS_SIZE>, 0>,-
		DMA_LEN = REQSNS_SIZE,-
		DMA_DIR = READ

	SCSI_CMD -
		NAME = READ,-
		CMD_BYTES = <8, 0, 0, 0, 0, 0>,-
		DMA_LEN = -1,-
		TIMEOUT=600

	SCSI_CMD -
		NAME = WRITE,-
		CMD_BYTES = <10, 0, 0, 0, 0, 0>,-
		DMA_LEN = -1,-
		TIMEOUT=600

        SCSI_CMD -
                NAME = READ_8MM,-                      	;Special 8MM READ
                CMD_BYTES = <8, 2, 0, 0, 0, 0>,-
                DMA_LEN = 80,-
                TIMEOUT=600

        SCSI_CMD -                                      ;Special 8MM WRITE
                NAME = WRITE_8MM,-
                CMD_BYTES = <10, 0, 0, 0, 0, 0>,-
                TIMEOUT=600

	SCSI_CMD -
		NAME = INQUIRY,-
		CMD_BYTES = <18 , 0, 0, 0, 36, 0>,-
		DMA_LEN = 36,-
		DMA_DIR = READ 

	SCSI_CMD -
		NAME = MODE_SELECT,-
		CMD_BYTES = <21 , <^X10>, 0, 0, 12, 0>,-
		DMA_LEN = 12,-
		DMA_DIR = WRITE,-
		TIMEOUT = 600

	SCSI_CMD -
		NAME = MODE_SELECT_TMP,-
		CMD_BYTES = <21, <^X10>, 0, 0, 12, 0>,-
		DMA_LEN = 12,-
		DMA_DIR = WRITE,-
		TIMEOUT = 600

	SCSI_CMD -
		NAME = MODE_SELECT_10,-
		CMD_BYTES = <85, <^X10>, 0,0,0,0,0, 0,12, 0>,-
		DMA_LEN = 12,-
		DMA_DIR = WRITE,-
		TIMEOUT = 600

	SCSI_CMD -
		NAME = MODE_SENSE,-
		CMD_BYTES = <26, 0, 0, 0, 100, 0>,-
		DMA_LEN = 100,-
		DMA_DIR = READ,-
		TIMEOUT = 600

	SCSI_CMD -
		NAME = MODE_SENSE_10,-
		CMD_BYTES = <90, 0, 0, 0,0,0,0, <^X04>,<^X00>,0>,-
		DMA_LEN = 1024,-
		DMA_DIR = READ,-
		TIMEOUT = 600

	SCSI_CMD -
		NAME = MODE_SENSE_COMP,- 		; get page ^X10
		CMD_BYTES = <26, 0, <^X10>, 0, 12+16, 0>,- ; for compaction
		DMA_LEN = 12+16,-
		DMA_DIR = READ,-
		TIMEOUT = 600

	SCSI_CMD -
		NAME = RECEIVE_DIAG,-
		CMD_BYTES = <28, 0, 0, 6, 0, 0>,-
		DMA_LEN = 6,-
		DMA_DIR = READ,-
		TIMEOUT = 600

	SCSI_CMD -
		NAME = SEND_DIAG,-
		CMD_BYTES = <29, 0, 0, 0, 0, 0>,-
		DMA_LEN = 512,-
		DMA_DIR = WRITE

	SCSI_CMD -
		NAME = ERASE,-
		CMD_BYTES = <25, 1, 0, 0, 0, 0>,-
		TIMEOUT = 5*60*60

	SCSI_CMD -
		NAME = REWIND,-
		CMD_BYTES = <1, 0, 0, 0, 0, 0>,-
		TIMEOUT=600

	SCSI_CMD -					;SCSI LOAD Command with 
		NAME = RETENSION,-			;retension set for QIC
		CMD_BYTES = <27, 1, 0, 0, 3, 0>,-	;tape drives... 
		TIMEOUT=600				;

	SCSI_CMD -
		NAME = UNLOAD,-
		CMD_BYTES = <27, 0, 0, 0, 0, 0>,-	;tell it to unload!
		TIMEOUT=600

	SCSI_CMD -
		NAME = SPACE,-
		CMD_BYTES = <17, 0, 0, 0, 0, 0>,-
		TIMEOUT=5*60*60

	SCSI_CMD -
		NAME = WRITE_FILEMARKS,-
		CMD_BYTES = <16, 0, 0, 0, 0, 0>,-
		TIMEOUT=600

	SCSI_CMD -
		NAME = READ_POSITION,-
		CMD_BYTES = <<^X34>, 0, 0, 0, 0, 0, 0, 0, 0, 0>,-
		DMA_LEN = 20,-
		DMA_DIR = READ,-
		TIMEOUT=600

	.PAGE


; Descriptors for mode page fields, used by MODE SENSE operations.

	.MACRO	MODE_FIELD_DESC PAGE,NAME,BOFF,BIT_OFFSET,BIT_SIZE,FLAGS,DESIRED

		'PAGE'_'NAME'_DESC = . - DESCRIPTOR_BASE ; Offset from base
		.LONG 'BOFF'            ; Byte offset in page
		.LONG 'BIT_OFFSET'      ; Bit offset within byte
		.LONG 'BIT_SIZE'        ; Size of field in bits
		.LONG 'FLAGS'           ; Flag bits
		.QUAD 'DESIRED'		; Desired value
		.BLKL  2		; Actual value
		.BLKL  1		; Difference reason
		.BLKL  1		; Reserved bits

	.ENDM	MODE_FIELD_DESC

	.ALIGN LONG

DESCRIPTOR_BASE = .

; Descriptors for use by Mode_Sense routine

VENDOR_UNIQUE_PAGE_DESC = . - DESCRIPTOR_BASE

	MODE_FIELD_DESC -
		PAGE=VENDOR_UNIQUE,-
		NAME=DENS,-
		BOFF=MODE_DESC$C_DENS_BOFF,-
		BIT_OFFSET=0,-
		BIT_SIZE=8
		
	MODE_FIELD_DESC -
		PAGE=VENDOR_UNIQUE,-
		NAME=BDLEN,-
		BOFF=MODE_DESC$C_BDLEN_BOFF,-
		BIT_OFFSET=0,-
		BIT_SIZE=8

	MODE_FIELD_DESC -
		PAGE=VENDOR_UNIQUE,-
		NAME=DEVSPC,-
		BOFF=MODE_DESC$C_DEVPAR_BOFF,-
		BIT_OFFSET=0,-
		BIT_SIZE=8,-
		FLAGS=<MODE_DESC$M_LAST>

; Descriptors for use by Setup_Compaction routine

DEV_CONFIG_PAGE_DESC = . - DESCRIPTOR_BASE

	MODE_FIELD_DESC -
		PAGE=DEV_CONFIG,-
		NAME=SEL_DATA_COMP,-		
		BOFF=14,-
		BIT_OFFSET=0,-
		BIT_SIZE=1,-
		FLAGS=<MODE_DESC$M_REQUIRED!-
		       MODE_DESC$M_IFMISSING!-
		       MODE_DESC$M_IFMISMATCH>
	MODE_FIELD_DESC -
		PAGE=DEV_CONFIG,-
		NAME=DEVSPC,-
		BOFF=MODE_DESC$C_DEVPAR_BOFF,-
		BIT_OFFSET=0,-
		BIT_SIZE=8,-
		DESIRED=SCSI_MSEL$M_BUF,-
		FLAGS=<MODE_DESC$M_PREFERRED>

	MODE_FIELD_DESC -
		PAGE=DEV_CONFIG,-
		NAME=DENS,-
		BOFF=MODE_DESC$C_DENS_BOFF,-
		BIT_OFFSET=0,-
		BIT_SIZE=8,-
		DESIRED=0,-
		FLAGS=<MODE_DESC$M_PREFERRED!-   
		       MODE_DESC$M_LAST>



; Descriptors for use by Mode_Select routine

VENDOR_UNIQUE_SEL_DESC = . - DESCRIPTOR_BASE

	MODE_FIELD_DESC -
                NAME=BLKLEN,-
                BOFF=MODE_DESC$C_BLKLEN_BOFF,-
                BIT_OFFSET=0,-
                BIT_SIZE=24,-
                DESIRED=0,-
                FLAGS=<MODE_DESC$M_PREFERRED>
	MODE_FIELD_DESC -
		PAGE=VENDOR_UNIQUE_SEL,-
		NAME=PARAMS,-
		BOFF=0,-
		BIT_OFFSET=0,-
		BIT_SIZE=0,-
		FLAGS=<MODE_DESC$M_PREFERRED>
BASIC_SELECT_DESC = . - DESCRIPTOR_BASE
 	MODE_FIELD_DESC -
		PAGE=VENDOR_UNIQUE_SEL,-
		NAME=DEVSPC,-
		BOFF=MODE_DESC$C_DEVPAR_BOFF,-
		BIT_OFFSET=0,-
		BIT_SIZE=8,-
		DESIRED=SCSI_MSEL$M_BUF,-
		FLAGS=<MODE_DESC$M_PREFERRED>

	MODE_FIELD_DESC -
		PAGE=VENDOR_UNIQUE_SEL,-
		NAME=DENS,-
		BOFF=MODE_DESC$C_DENS_BOFF,-
		BIT_OFFSET=0,-
		BIT_SIZE=8,-
		DESIRED=0,-
		FLAGS=<MODE_DESC$M_PREFERRED!-   
		       MODE_DESC$M_LAST>


DESCRIPTOR_SIZE = . - DESCRIPTOR_BASE

	.PAGE
	.SBTTL	Sense key to VMS status translation table
;+
; SENSE_KEY_TABLE
;
; This table is used to translate SCSI extended sense keys to VMS status codes.
; Each entry contains a longword;  its low word is VMS status and its high word
; is 0.  The table is indexed by sense key value.
; -

SENSE_KEY_TABLE:

;			VMS status              ; SCSI sense key
;			----------		; ----------

	SENSE_KEY	NORMAL			; NO_SENSE		
	SENSE_KEY	NORMAL			; RECOVERED_ERROR	
	SENSE_KEY	DEVOFFLINE		; NOT_READY		
	SENSE_KEY	PARITY			; MEDIUM_ERROR		
	SENSE_KEY	DRVERR			; HARDWARE_ERROR		
	SENSE_KEY	DRVERR			; ILLEGAL_REQUEST	
	SENSE_KEY	MEDOFL			; UNIT_ATTENTION		
	SENSE_KEY	WRITLCK			; DATA_PROTECT		
	SENSE_KEY	OPINCOMPL		; BLANK_CHECK
	SENSE_KEY	DRVERR			; VENDOR_UNIQUE		
	SENSE_KEY	DRVERR			; COPY_ABORTED
	SENSE_KEY	DRVERR			; ABORTED_COMMAND	
	SENSE_KEY	NORMAL			; EQUAL			
	SENSE_KEY	DRVERR			; VOLUME_OVERFLOW	
	SENSE_KEY	DATACHECK		; MISCOMPARE		
	SENSE_KEY	DRVERR			; RESERVED

	.PAGE
	.SBTTL	SCSI error length table
;+
; SCSI_ERROR_LEN_TAB
;
; This table is indexed by the SCSI error type and specifies the length of
; the errorlog packet.
;-

SCSI_ERROR_LEN_TAB:

	SCSI_ERROR_CODES <-
		<CONNECTION_ERROR,	STANDARD_LENGTH>,-
		<MAP_BUFFER_ERROR,	STANDARD_LENGTH>,-
		<SEND_CMD_ERROR,	COMMAND_LENGTH>,-
		<INV_INQUIRY_DATA,	INQUIRY_LENGTH>,-
		<EXTND_SENSE_DATA,	EXTND_SENSE_LENGTH>,-
		<MODE_SENSE_DATA,	MODE_SENSE_LENGTH>,-
		<REASSIGN_BLOCK,	REASSIGN_BLOCK_LENGTH>,-
		<DIAGNOSTIC_DATA,	DIAGNOSTIC_DATA_LENGTH>>
	.PAGE
	.SBTTL	+
	.SBTTL	+ DRIVER ENTRY POINTS
	.SBTTL	+
	.SBTTL	MK_CTRL_INIT	- Controller initialization routine
;+
; MK_CTRL_INIT
;
; This routine is called to perform controller-specific initialization and
; is called by the operating system in three places:
;
;	- at system startup
;	- during driver loading and reloading
;	- during recovery from a power failure
;
; Currently this routine simply creates a fork thread to setup a trace buffer.
;
; STEP 2 INTERFACE
;
;	int CTRLINIT (IDB *, DDB *, CRB *)
;
; OBSOLETE STEP 1 INPUTS:
;
;	R5	- address of the IDB
;	R8	- address of the CRB (channel request block)
;
; OBSOLETE STEP 1 OUTPUTS:
;       R0	- Status
;	R1-R3	- Destroyed
;-

	DRIVER_CODE
MK_CTRL_INIT:

	$DRIVER_CTRLINIT_ENTRY FETCH=NO PRESERVE=<R2,R3,R4,R5,R8>
	.IF DEFINED DEBUG
	MOVL	CTRLARG$_IDB(AP),R4
	MOVL	CTRLARG$_CRB(AP),R5			; Copy CRB address 
	FORK	ROUTINE = SETUP_TRACE CONTINUE=10$     ; Fork using the CRB fork block, R3 and R4 are outputs
	
10$:	
	.ENDC
	MOVL	#SS$_NORMAL,R0
	RET				; Return to caller 
	.PAGE
	.SBTTL	MK_UNIT_INIT	- Unit initialization routine
;+
; MK_UNIT_INIT
;
; This routine performs unit-specific initialization and is called for each
; tape found on the SCSI bus. A connection to the port driver is established, 
; which lasts for the life of the system. All traffic to this SCSI device is 
; directed over this connection.
;
; An inquiry command is sent to the target to determine the device type.
; In addition, several sanity checks are made on the inquiry data to determine 
; if the device is valid. If so, the unit is placed online and any I/O queued
; to the device during initialization is started.
;
; STEP 2 INTERFACE
;
;	int UNITINIT (IDB *, UCB *)
;
; OBSOLETE STEP 1 INPUTS:
;
;	R5	- UCB address
;
; OBSOLETE STEP 1 OUTPUTS:
;
;	R0-R5	- Destroyed
;	All other registers preserved
;-

MK_UNIT_INIT:				; Initialize unit
					; Generate a .CALL_ENTRY mask and move args into standard registers
	$DRIVER_UNITINIT_ENTRY PRESERVE = <R2,R3,R4,R5>	  
	BBC     #UCB$V_POWER,-          ; Branch if powerfail has not been
		UCB$L_STS(R5),2$	; NOT been detected
	RET                             ; Otherwise, exit immediately

					; Que the fork block and RET
2$:	FORK ENVIRONMENT=CALL, CONTINUE=10$
					; fork then to to 10$ and set return val
	KP_ALLOCATE_KPB -		; Allocate kernel process block
		KPB=UCB$PS_UNITINIT_KPB(R5),-
		FLAGS=#KP$M_IO		; Specify deallocation at end of unit init
	BLBC	R0,30$			; Branch if error allocating KPB
	MOVL	UCB$PS_UNITINIT_KPB(R5),- ; Save KPB address in UCB
		R2
	MOVL	R5,KPB$PS_UCB(R2)	; Save UCB address in KPB

	KP_START -			; Start the kernel process that does
		KPB=R2, -		;  most of the unit init work
		ROUTINE=MK_KP_UNIT_INIT, -
		REGISTERS=#KPREG$K_HLL_REG_MASK

	RET				; Return from DK_UNIT_INIT
;
;	here after for is queued. return success
;
10$:
	MOVL	#SS$_NORMAL,R0		; set return value to success
	RET				; and return

30$:	BUG_CHECK INCONSTATE,FATAL	; Bugcheck

; The remainder of this routine runs as a kernel process.
; This is necessary to make SPI$ calls and access the port driver.
;
; The KP register save mask (above) saves all registers appropriate to a HLL
; driver.  This permits port drivers to be written in a high-level language.
; The starting KP routine (call entry below) saves only the registers used
; by that routine.

MK_KP_UNIT_INIT:
	.CALL_ENTRY -
		PRESERVE=<R2,R3,R4,R5,R6,R7,R8>

	MOVL	4(AP),R2		; Pick up pointer to KPB
	MOVL	KPB$PS_UCB(R2),R5	; Restore UCB address
        clrw    ucb$l_frontpnl(r5)      ; initially set default density ops
        movl    #^xB00BFACE,ucb$l_fpfind(r5) ;add easy to spot pattern
	ASSUME	UCB$V_DISCONNECT LT 8
	MOVB	#UCB$M_DISCONNECT!-	; By default, assume the target device
		 UCB$M_SYNCHRONOUS,-	; is capable of both disconnecting and
		UCB$L_MK_FLAGS(R5)	; synchronous operation
	BISL	#UCB$M_ONLINE!-		; Set unit online and busy to allow I/O
		 UCB$M_BSY,-		; to be queued
		UCB$L_STS(R5)		;
	MOVAL	UCB$L_FLUSH_IOQFL(R5),R0; Initialize the queue used to flush
	MOVL	R0,(R0)			; I/Os which are queued during unit
	MOVL	R0,4(R0)		; init when unit init fails
	CLRL	UCB$L_REWIND_TIME(R5)	; Indicate no rewind command has been
					; recently
	CLRL	UCB$L_UNLOAD_TIME(R5)	; Indicate no unload has been issued
					; recently

	.IF DEFINED DEBUG
	CLRL	R3			; Prepare to call TRACE_QIO
	BSBW	TRACE_QIO		; Trace this QIO (special case when
					; called from UNIT INIT, R3 must be 0)
	.ENDC

; All SCSI tape unit numbers should be of the form "n0m" where n is the SCSI
; ID between 0 and 31 and m is the LUN between 0 and 7. Extract the ID from the
; LUN by dividing the unit number by 100. The quotient is the used as the ID
; while the remainder is the LUN. Note that the unit number contains three
; digits because early version of SCSI provided for sub-logical unit numbers.
; This feature has since been removed and the second digit in the unit number
; is not used.

	MOVL	#SS$_BADPARAM,R0	; Assume bad LUN or SUBLUN specified
	MOVZWL	UCB$W_UNIT(R5),R1	; Get device unit number
	CLRL	R2			; Prepare for extended divide
	EDIV	#100,R1,R1,R2		; Extract SCSI bus ID from LUN
	CMPL	R1,#31			; Valid SCSI ID (0 <= n <= 7)?
	BGTRU	20$			; Branch if not 
	CMPL	R2,#7			; Valid LUN (0 <= n <= 7)?
	BGTRU	20$			; Branch if not 
        CLRB    UCB$B_LUN(R5)           ; For Lun's bigger than 7
	ASHL	#16,R1,R1		; Place SCSI ID in high-order word of R1
	ASHL	#16,R2,R2		; Place LUN in high-order word of R2
	MOVL	UCB$L_DDB(R5),R0	; Get DDB address
	SUBB3	#^A'A',-		; Translate controller letter to
		DDB$T_NAME+3(R0),R1	; SCSI bus ID.

.IIF NDF, DDB$V_PAC, DDB$V_PAC=1	; X-14
        BBC     #DDB$V_PAC,-		; Using port allocation class?
		DDB$B_FLAGS(R0),6$	; No, never mind
        SUBB3   #^A'A',-		; Yes, translate using original
		DDB$L_PORT_ID(R0),R1	;  controller letter.

6$:	ADDL3	G^EXE$GL_ABSTIM,#MK_INIT_TIMEOUT,- ; Calculate and save init
		UCB$L_INITMO(R5)	; expiration time 
	MOVL	UCB$PS_UNITINIT_KPB(R5),R8 ; Get KPB address
	MOVL	R1,KPB$PS_SCSI_PTR1(R8)	; Save SCSI IDs
	MOVL	R2,KPB$PS_SCSI_PTR2(R8)	; Save LUN value
7$:	SPI$CONNECT -			; Connect to the port driver
		PORT_STATE_CONTEXT=R5	; Set callback context (UCB) for
					; debugging purposes (CLUE SCSI)
	BLBS	R0,10$			; Branch if connect attempt succeeded
	CMPL	UCB$L_INITMO(R5),-	; Has the timeout time passed?
		G^EXE$GL_ABSTIM
	BLEQU	20$			; If so, set this unit OFFLINE	
	KP_STALL_FORK_WAIT  KPB=R8,-
			    FKB=R5	; Fork wait using UCB fork block
	MOVL	KPB$PS_SCSI_PTR1(R8),R1	; Restore SCSI IDs
	MOVL    KPB$PS_SCSI_PTR2(R8),R2	; Restore LUN value
	BRB	7$			; Try connecting again
10$:
	CMPL	R1,UCB$L_MAXBCNT(R5)	; For MAXBCNT, use minimum supported
	BGEQ	15$			; value of port and class drivers
	MOVL	R1,UCB$L_MAXBCNT(R5)	; Save maximum byte count in UCB
15$:	MOVL	R2,UCB$L_SCDT(R5)	; Save SCDT address
	MOVAL	UCB$L_INQUIRY_DATA(R5),-	; Save SCDT pointer to the
		SCDT$L_RSVD_LONG(R2)            ; Inquiry data
	MOVL	R4,UCB$L_PDT(R5)		; Save PDT address
	PUSHL	#NUM_LONGWORDS_DIAGNOSE		; Number of longwords in UCB 
						; reserved for IO$_DIAGNOSE
	PUSHAL	UCB$PS_DIAGNOSE(R5)		; Start of DIAGNOSE longwords
	CALLS	#2,DKMK$DIAGNOSE_INIT		; Initialize DIAGNOSE area
	BLBC	R0,20$				; Set offline if error

	PUSHL	R5			; Save working register
	MOVL	R5,R3			; Copy UCB address for MK_ALLOC_SCDRP
	MK_ALLOC_SCDRP			; Allocate an SCDRP off of the stack
	MOVL	R8,SCDRP$PS_KPB(R5)	; Save KPB address in SCDRP
	SPI$INITIAL_PROCESSING		; Run the tolerant INQUIRY routine
	MK_DEACTIVATE_SCDRP CLRSTK=YES	; Free the SCDRP from the stack
	POPL	R5			; Restore working register

	BSBW	SET_UNIT_ONLINE		; Go bring the unit online

; Notify the Media Manager (MME) that the device is online

	MOVL	#MME$_NOTIFY_DEVICE, R0	; load the event code.
	JSB	G^MME$$DEV_EVENT	; invoke the MME call-out.
	RET				; Return to caller within KP services

; Connection failure. Log an error and set the unit offline.	
	
20$:	LOG_ERROR -			; Log a connection error
		TYPE=CONNECTION_ERROR,-	;
		VMS_STATUS=R0,-		;
		UCB=R5			;
	BICL	#UCB$M_ONLINE!-		; Set the unit offline and not busy
		 UCB$M_BSY,-		;
		UCB$L_STS(R5)		;
	RET				; Return to caller within KP services


	.PAGE

	.SBTTL	MK_SENSEMODE
;+
; MK_SENSEMODE - SENSE DEVICE CHARACTERISTICS AND MODE
;
; This routine gets the device characteristics and returns them
; in the IOSB and optionally in a user specified buffer.
; If the user specified buffer is of the correct length, extended
; device characteristics will also be returned.
;
; If P1 is not specified, then IRP$L_SVAPTE in the I/O packet is
; cleared and only the device-dependent information will be returned
; in the second longword of the IOSB when the packet is queued via
; EXE$QIODRVPKT.
;
; If P1 is specified, then this routine allocates a system buffer
; and builds a buffer header using the user buffer address in P1
; and the length as specified by P2.
;
; If P2 is not specified or is specified as a value other than
; 12, then a user buffer length of 8 bytes is assumed.  The device
; class, type and buffer size will be returned in the user buffer
; along with the device-dependent information.
;
; If P2 is specified as 12, then the device class, type, buffer
; size, device-dependent information and extended device
; characteristics will be returned in the user buffer.
;
; STEP 2 INTERFACE:
;
;	int FDT_ROUTINE (IRP *, PCB *, UCB *, CCB *)
;
; STEP 1 INPUT REGISTERS:
;
;	R3 	- I/O packet address
;	R4 	- Current pcb
;	R5 	- UCB address
;	R6 	- Assigned CCB address
;	IRP$L_QIO_P1(R3) - Address of buffer
;	IRP$L_QIO_P2(R3) - Length of buffer
;
;
; STEP 1 OUTPUT REGISTERS:
;
;	R0 	- Status of the operation
;	R1,R2	- Destroyed
;	All other registers preserved
;
; COMPLETION CODES:
;
;	SS$_NORMAL - Successful
;	SS$_ACCVIO - Buffer access violation
;-

MK_SENSEMODE:
	.CALL_ENTRY PRESERVE=<R2,R3,R4,R5>
	MOVL	FDTARG$_IRP(AP), R3	; Fetch	IRP and UCB
	MOVL	FDTARG$_UCB(AP), R5
	CLRL	IRP$L_SVAPTE(R3)	; Assume no user supplied buffer.
	MOVL	IRP$L_QIO_P1(R3), R0	; Get buffer address.
	BEQL	20$			; Branch if none supplied.
 
	MOVZWL	#8, R1			; Assume buffer length of 8 bytes.
	CMPL	#12,IRP$L_QIO_P2(R3)	; Length of 12 specified?
	BNEQ	10$			; Branch if not.
	MOVZWL	#12, R1			; Set length in R1.
 
10$:	CALL_READCHK			; Check buffer for write access.  Macro RETs if R0  = SS$_FTD_COMPL
	ADDL3	#12, IRP$L_BCNT(R3), R1 ; Add 12 bytes for sys. overhd to size saved by CALL_READCHK in the IRP
	PUSHL	R3			; Save IRP address.

	JSB	G^EXE$DEBIT_BYTCNT_ALO  ; R2 and R3 are destroyed
	POPL	R3			; Restore IRP address.
	BLBC	R0,99$			; Branch on error (SS$_INSFMEM).

	MOVL	R2, IRP$L_SVAPTE(R3)	; Store address of system buffer.
	ADDL3	#12, R2,- 		; Store starting address of system
		@IRP$L_SVAPTE(R3)	;  buffer data area in buffer header.
	MOVL	IRP$L_QIO_P1(R3), 4(R2)	; Store user buffer address in header.
	MOVL	R1, IRP$L_BOFF(R3)	; Store total size of system buffer.

20$:	CALL_QIODRVPKT			; Queue the packet and RET (input=R3,R5)

99$:	MOVL	FDTARG$_PCB(AP), R4	; Fetch PCB into R4
	CALL_ABORTIO			; Abort I/O request and RET (input=R0,R3,R4,R5)

	.PAGE
	.SBTTL	MK_SETCHAR	- Set char FDT entry point
;+
; MK_SETCHAR
;
; Set a mode bit, or clear it, to turn on the skip by filemarks algorithm or
; turn it off. If io$_setchar with IO$M_ALLOWFAST_ALWAYS, turn on new algorithm
; for all IO$_SKIPFILE calls. If modifier is IO$_ALLOWFAST_PER_IO, turn on new
; algorithm for IO$_SKIPFILE calls with the IO$M_ALLOWFAST modifier. If
; modifier is IO$M_ALLOWFAST_NEVER instead, turn it off for all cases. Return success
; always.  If there are none of these modifiers, just do normal SETCHAR.
;
; The following table describes state uses:

;		   UCB$L_MK_FLAGS			    QIO         QIO
;	       ALLOWFAST_  ALLOWFAST_  UCB$M_SKIPFILE_  IO$_SKIPFILE  IO$_
;	       PER_IO      ALWAYS      SUPPORTED                      SKIPFILE	
;								      and
;								      IO$M_
;								      ALLOWFAST
;------------------------------------------------------------------------------
;MKSET/NEVER      0           0            1              record       record
;illegal	  0           1            1                X            X
;MKSET/PER_IO*    1           0            1              record       filemark
;MKSET/ALWAYS     1           1            1              filemark     filemark
;                 X           X            0**            record       record
;
;*  Default after autoconfig.
;** The default is "supported".  May become unsupported at unit init. (as a
;   result of an explicit table entry, or at PACKACK (as a result of failing
;   the Read Position test).
;
;
; Inputs:
;  R6 = CCB	Channel control block address
;  R5 = UCB	Unit control block address
;  R4 = PCB	Process Control Block address
;  R3 = IRP	I/O Request Packet address
;
;  Output:
;  R0 = status
;-
MK_SETCHAR: $DRIVER_FDT_ENTRY FETCH=YES
        BITL    #<IO$M_ALLOWFAST_ALWAYS!-	; Are any modifiers present?
		  IO$M_ALLOWFAST_PER_IO!-	;
		  IO$M_ALLOWFAST_NEVER>,-		;
		  IRP$L_FUNC(R3)		;
        BNEQ    10$				; Banch if modifiers are found

; This is the normal setchar, so pass it to the normal routine.
; Note we use EXE_STD$SETMODE instead of EXE$STD_SETCHAR since this
; is what MKdriver HAS been using before edit X-24.
;
	CALL_SETMODE				; Call EXE_STD$SETMODE
        RET

10$:
;
; Special control values:
; always+never: set to allow new alg even if 1 or 2 filemarks
; always+per_io: use P2 of the $qio in ucb$l_frontpnl
;
; (This is a bit of a temporary till final density stuff is in)
	movl	irp$l_func(r3),r1		; get the function
	bicl	#63,R1				; zero off the func codes
	cmpl	r1,#<IO$M_ALLOWFAST_ALWAYS!IO$M_ALLOWFAST_NEVER>
	bneq	11$				;if not 1-2 filemarks skip
	bisl	#ucb$m_fast_1file,ucb$l_mk_flags(r5)
	brw	22$
11$:
	cmpl	r1,#<IO$M_ALLOWFAST_ALWAYS!IO$M_ALLOWFAST_PER_IO>
	bneq	12$				; see if loading density
; Set "front panel" density. Use P2 to pass this since nothing usually
; assumes it is an address.
	movl	irp$q_qio_p2(r3),ucb$l_frontpnl(r5) ;set front panel
	brw	22$
12$:
; We are getting the state of the flags BEFORE the call here so that at
; 20$ below it can be returned in the second IOSB longword.
	MOVL    UCB$L_MK_FLAGS(R5),R1		; Get the MK Flags for debug
; Now get the fork lock so that we can be sure nothing alters this longword
; while we are modifying it.
;
	FORKLOCK PRESERVE=YES
;
; Test for ALLOWFAST_NEVER modifier and clear the UCB bits for ALLOWFAST_ALWAYS
; and ALLOWFAST_PER_IO if modifier is found.
;
	BBC     #IO$V_ALLOWFAST_NEVER,-
		IRP$L_FUNC(R3),15$		; Skip if not disabling fast skip
        BICL    #<UCB$M_ALLOWFAST_ALWAYS!-
		  UCB$M_ALLOWFAST_PER_IO>,-
                UCB$L_MK_FLAGS(R5)		; Turn off all fast skipfile
						; functions 
	BRW	20$

;
; Test for ALLOWFAST_PER_IO modifier and clear the UCB bit for ALLOWFAST_ALWAYS
; and set the UCB bit for ALLOWFAST_PER_IO if modifier is found.
;
15$:
	BBC     #IO$V_ALLOWFAST_PER_IO,-
		IRP$L_FUNC(R3),17$		; Skip if not setting fast skip for 
						; per-IO (with fcn mod) only
        BICL    #UCB$M_ALLOWFAST_ALWAYS,-
                UCB$L_MK_FLAGS(R5)		; Turn off fast skipfile by default
        BISL    #UCB$M_ALLOWFAST_PER_IO,-
                UCB$L_MK_FLAGS(R5)		; Turn on fast skipfile functions for
						; use with IO$_SKIPFILE modifier
	BRW	20$

;
; Test for ALLOWFAST_ALWAYS modifier and set the UCB bits for ALLOWFAST_ALWAYS
; and ALLOWFAST_PER_IO if modifier is found.
;
17$:
	BBC     #IO$V_ALLOWFAST_ALWAYS,-
		IRP$L_FUNC(R3),20$		; Skip if not setting for use of fast skip
        BISL    #<UCB$M_ALLOWFAST_ALWAYS!-	; always.
		  UCB$M_ALLOWFAST_PER_IO>,-
                UCB$L_MK_FLAGS(R5)		; Turn on all fast skipfile functions 
;	BRW	20$				; (common exit. Omit if it follows.
;
; Now we're done. Declare victory and exit.
;
20$:
; Now unlock from the forklock above at 10$ and be sure R1 is still left
; alone as the previous value of UCB$L_MK_FLAGS. (This means that it is
; of some value in debug or development in restoring initial conditions.)
	FORKUNLOCK CONDITION=RESTORE,PRESERVE=YES
22$:    MOVL    #SS$_NORMAL,R0          ; Set success status
        CALL_FINISHIO DO_RET=NO        ; and return
        RET
 

	.PAGE
	.SBTTL	MK_STARTIO	- Driver QIO entry point
;+
; MK_STARTIO
;
; This routine is the QIO entry point into the driver and is called by
; kernel process sevices as a procedure.  Its main function is to dispatch 
; to the function-code-specific routine which then executes the QIO.
;
; INPUTS:
;
;	R3	- IRP address
;	R5	- UCB address
;
; OUTPUTS:
;
;	R0	- 1st longword of I/O status: contains status code and
;		  number of bytes transferred
;	R1	- 2nd longword of I/O status: contains a copy of the
;		  UCB$L_DEVDEPEND field
;	All other registers preserved
;-

MK_STARTIO:

	.CALL_ENTRY <R2,R3,R4,R5,R6,R7,R8>
	MOVL	4(AP),R0		; Get KPB address
	MOVL	KPB$PS_UCB(R0),R5	; Get UCB address
	MOVL	KPB$PS_IRP(R0),R3	; Get IRP address

	.IF DEFINED DEBUG
	BSBW	TRACE_QIO		; Trace the current I/O request
	.ENDC

 	MOVL	UCB$L_PDT(R5),R4	; Get PDT address
	MOVL	R3,R2			; Copy IRP address
	MOVL	R5,R3			; Copy UCB address
	MK_ALLOC_SCDRP			; Allocate an SCDRP
	MOVL	R2,SCDRP$L_IRP(R5)	; Save IRP address in SCDRP
	MOVL	IRP$PS_KPB(R2),-	; Save KPB address in SCDRP
		SCDRP$PS_KPB(R5)
1$:	MOVL	IRP$L_FUNC(R2),-	; Copy function code and modifiers
		SCDRP$L_FUNC(R5)	; from the IRP to the SCDRP
	MOVL	IRP$L_MEDIA(R2),-	; and media field,
		SCDRP$L_MEDIA(R5)	;
	MOVL	IRP$L_SVAPTE(R2),-	; SVAPE,
		SCDRP$L_SVAPTE(R5)	;
	MOVL	IRP$L_BCNT(R2),-	; BCNT,
		SCDRP$L_BCNT(R5)	;
	MOVL	IRP$L_BOFF(R2),-	; and BOFF
		SCDRP$L_BOFF(R5)	;

; Fix a synchronization bug in the I/O subsystem which allows virtual functions 
; to sneak through FDT processing and be queued to the driver even though
; mapping is disable. 

	BBC	#IRP$V_VIRTUAL,-	; Branch if not a virtual I/O function
		IRP$L_STS(R2),5$	;
	MOVL	IRP$L_WIND(R2),R0	; Get window block
	TSTL	WCB$L_NMAP(R0)		; Mapping enabled?
	BNEQ	5$			; Branch if so
	MK_DEACTIVATE_SCDRP CLRSTK=NO	; Deactivate the SCDRP
	MOVL	R3,R5			; Copy UCB address
	MOVL	UCB$L_VCB(R5),R1	; Get address of VCB listhead
	INSQUE	(R2),@4(R1)		; Insert entry in blocked I/O list
	REMQUE	@UCB$L_IOQFL(R5),R3	; Remove I/O packet from device unit queue
	BVS	3$			; Branch if no I/O queued
	CALL_INITIATE			; Go initiate next function
	RET
3$:	BICL	#UCB$M_BSY,UCB$L_STS(R5); Clear unit busy
	CALL_RELCHAN			; Release the channel
	RET
5$:

;
;
; Allow only physical I/O functions before a PACKACK is issued.
;
	BBS	#IRP$V_PHYSIO,-		; Branch if physical I/O function
		IRP$L_STS(R2),10$
	BBC	#UCB$V_VALID,-		; Logical or virtual I/O function
		UCB$L_STS(R3),-		; Branch if volume is invalid
		VOLUME_INVALID

; If the last command was a rewind/nowait, the rewind might still be in 
; progress. If so, wait for the rewind to complete before executing this
; QIO function.

10$:	BBCC	#UCB$V_REWIND_INPROG,-	; Branch if last command was not a
		UCB$L_MK_FLAGS(R3),16$	; rewind/immediate
	BSBW	WAIT_UNIT_READY		; Otherwise, wait for rewind to complete
	BLBC	R0,COMPLETE_IO		; Branch on error
; If front panel density is here we better set it again after a rewind.
        tstb    ucb$l_frontpnl(r3)      ; Is the user density null?
        beql    12$                      ; if so skip special stuff
; Set density value.
        movw    ucb$l_frontpnl(r3),UCB$W_MK_DENSITY(R3)  ; Copy density
12$:
	BSBW	MODE_SELECT		; Send a mode select command
	MOVL	UCB$L_IRP(R3),R2	; Restore IRP address
	BRW	1$			; Now go execute original command

16$:	EXTZV	#IRP$V_FCODE,-		; Extract I/O function code
		#IRP$S_FCODE,- 		;
		IRP$L_FUNC(R2),R1	;
	ASSUME	IRP$S_FCODE LE 7	; Allow byte mode dispatch

	DISPATCH R1,TYPE=B,<-		; Dispatch according to function
		<IO$_NOP,	IO_NOP>,- 	; ^X00
		<IO$_UNLOAD,	IO_UNLOAD>,- 	; ^X01
		<IO$_ERASETAPE,	IO_ERASETAPE>,-	; ^x06
		<IO$_PACKACK,	IO_PACKACK>,- 	; ^X08
		<IO$_WRITEPBLK,	IO_WRITEPBLK>,-	; ^X0B
		<IO$_READPBLK,	IO_READPBLK>,- 	; ^X0C
		<IO$_AVAILABLE,	IO_AVAILABLE>,-	; ^X11
		<IO$_DSE,	IO_DSE>,-	; ^X15
		<IO$_SENSECHAR,	IO_SENSECHAR>,-	; ^X1B
		<IO$_WRITEMARK,	IO_WRITEMARK>,-	; ^X1C
		<IO$_DIAGNOSE,	IO_DIAGNOSE>,- 	; ^X1D
		<IO$_WRITELBLK,	IO_WRITELBLK>,-	; ^X20
		<IO$_READLBLK,	IO_READLBLK>,- 	; ^X21
		<IO$_REWINDOFF, IO_REWINDOFF>,- ; ^X22
		<IO$_SETMODE,	IO_SETMODE>,-	; ^X23
		<IO$_REWIND,	IO_REWIND>,-	; ^X24
		<IO$_SKIPFILE, 	IO_SKIP_FILE>,- ; ^X25
		<IO$_SKIPRECORD, IO_SKIP_RECORD>,- ; ^X26
		<IO$_SENSEMODE,	IO_SENSEMODE>,-	; ^X27
		<IO$_WRITEOF,	IO_WRITEOF>,-	; ^X28
		>

; Bogus I/O function code if we fall through. Set illegal function code
; status and complete the I/O.

IO_BOGUS:

	MOVZBL	#SS$_ILLIOFUNC,R0	; Specify the error type
	BRB	COMPLETE_IO

IO_NOP:

	MOVZWL	#SS$_NORMAL,R0		; Set success status
	BRB	COMPLETE_IO		; Complete the I/O

VOLUME_INVALID:

	MOVZWL	#SS$_VOLINV,R0		; It's not a valid volume
;	BRB	COMPLETE_IO		; Fall through to complete the I/O	

COMPLETE_IO:

	MK_DEACTIVATE_SCDRP CLRSTK=NO	; Deactivate the SCDRP
	MOVL	R3,R5			; Copy UCB address

; If the CANCEL bit is set in the UCB, then the I/O has been canceled and 
; REQCOM has already been called for this IRP. The SCSI command, however, is
; allowed to complete (since there's no clean way of aborting a command to 
; the tape). In the meantime, the busy bit in the UCB remains set in order to
; prevent any new I/O from being started. Now that the command for the canceled 
; I/O has completed, see if any I/O is pending. If so, initiate it.

	BBCC	#UCB$V_CANCEL,-		; Branch if I/O has not been canceled
		UCB$L_STS(R5),1$	;
	REMQUE	@UCB$L_IOQFL(R5),R3	; Any I/O queued to this device?
	BVS	0$			; Branch if not	
	CALL_INITIATE			; Go initiate the I/O
	RET

0$:	BICL	#UCB$M_BSY,-		; Clear busy bit to allow future I/O
		UCB$L_STS(R5)		; to be initiated
	RET				; Return to caller within KP services

1$:
	.IF DEFINED DEBUG
	BSBW	TRACE_QIO_STAT		; Save the final QIO status in trace buf
	BLBS	R0,5$			; Branch on success status
	NOP				; Instruction to trap on QIO with bad status
5$:
	.ENDC

; The following code segment checks for an error on a virtual I/O function. In
; this case, all virtual I/O packets in the pending queue are removed and placed
; on the blocked I/O list of the volume control block. This allows the magtape 
; ACP to perform any operations necessary to recover from the error. It then 
; requeues all the I/O from the VCB to the UCB. One specific case in which this 
; action is necessary is in encountering a tape mark at the end of a volume. 
; The ACP is responsible for the activity necessary to switch to a new volume.

	PUSHL	R0			; Save final status and count
	CALL_DIAGBUFILL			; Fill diagnostic buffer if present
	BLBS	(SP),20$		; Branch if successful completion
	MOVL	UCB$L_IRP(R5),R4	; Get current IRP
	BBC	#IRP$V_VIRTUAL,-	; Branch if not a virtual I/O function
		IRP$L_STS(R4),20$	;

; Temporarily prevent mount verification if a virtual I/O function fails
; with a status that would normally kick in mount verification. 

	CMPW	(SP),#SS$_VOLINV	; Volume invalid status?
	BEQL	7$			; Branch if so
	CMPW	(SP),#SS$_MEDOFL	; Medium offline status?
	BNEQ	8$			; Branch if not
7$:	MOVL	#SS$_DEVOFFLINE,(SP)	; Return device offline status instead
	BRB	20$			; Don't queue I/Os to the blocked queue
8$:
	MOVL	IRP$L_WIND(R4),R4	; Get window block
	CLRL	WCB$L_NMAP(R4)		; Clear number of mapping pointers
	MOVL	UCB$L_VCB(R5),R4	; Get address of VCB listhead
	MOVAB	UCB$L_IOQFL(R5),R2	; Get address of I/O queue listhead
	MOVL	R2,R3			; Save listhead address
10$:	MOVL	(R3),R3			; Get next entry in list
	CMPL	R3,R2			; End of list?
	BEQL	20$			; Branch if so
	BBC	#IRP$V_VIRTUAL,-	; Branch if not a virtual function
		IRP$L_STS(R3),10$	;
	MOVL	4(R3),R3		; Retrieve address of previous entry
	REMQUE	@(R3),R1		; Remove entry from Driver queue
	INSQUE	(R1),@4(R4)		; Insert entry in blocked I/O list
	BRB	10$			; Repeat for all IPRs in list

20$:	POPL	R0			; Retrieve final I/O status
	MOVL	UCB$L_DEVDEPEND(R5),R1	; Set magtape status and characteristics
	KP_REQCOM				; Complete I/O request
	.PAGE
	.SBTTL	MK_CANCEL	- Cancel I/O routine
;+
; MK_CANCEL
;
; This routine cancels an I/O in progress. Since there is no clean way to abort
; a command in progress to the tape, pass the active IRP to REQCOM, but keep
; the BSY bit set in the UCB to prevent any new I/O from being initiated. This
; is accomplished by temporarily emptying the pending I/O queue and calling 
; REQCOM with the active IRP. REQCOM sees the pending queue empty and clears
; the BSY bit. When control returns here, the pending queue is restored and
; the BSY bit is set. The CANCEL bit in the UCB is set to indicate that the I/O
; has been canceled. All this activity is synchronized using the fork lock.
;
; Eventually, the SCSI command for the canceled I/O will complete. Just before
; that thread calls REQCOM, it checks the BSY bit. If it finds it set, it 
; avoids calling REQCOM. Instead, it attempts to initiate any I/O in the
; pending queue, or clears the BSY bit if no I/O is pending.
;
; Disallow cancel operations for SENSEMODE/SENSECHAR/SETMODE;
; otherwise the canceled IRP will sometimes be given to
; another device before the canceled I/O completes, resulting
; in simultaneous use of that IRP by two devices.  (Only these
; 3 functions reference the IRP after the cancel occurs.)
;
; STEP 2 INTERFACE
;
;	void CANCEL (channel , IRP *, PCB *, UCB *, reason)
;
; OBSOLETE STEP 1 INPUTS:
;
;	R2	- Channel index number
;	R3	- IRP address
;	R4	- PCB address of procedd canceling I/O
;	R5	- UCB address
;	R8	- Cancel reason code, one of:
;			CAN$C_CANCEL	if called through $CANCEL or 
;					$DALLOC system service
;			CAN$C_DASSGN	if called through $DASSGN system 
;					service
; OBSOLETE STEP 1 OUTPUTS:
;
;	R0-R3	- Destroyed
;	All other registers preserved
;-

MK_CANCEL:
					; Generate a .CALL_ENTRY and fetch args from stack
	$DRIVER_CANCEL_ENTRY PRESERVE = <R2,R3,R4,R5,R8>
	BBS	#UCB$V_CANCEL,-		; Don't cancel the same IRP twice
		UCB$L_STS(R5),20$	;
	TSTL	R3			; IRP address of 0? (this can happen
					; during UNIT INIT in bringing the
					; device online)
	BEQL	20$			; Branch if not
	CALL_CANCELIO  SAVE_R0R1 = NO	; Set cancel bit if appropriate, trash R0 and R1
	BBC	#UCB$V_CANCEL,-		; If the cancel bit is not set,
		UCB$L_STS(R5),20$	; just return.
    	EXTZV	#IRP$V_FCODE,-		; Extract I/O function code
		#IRP$S_FCODE,-		;
		IRP$L_FUNC(R3),R0	;
	CMPB	#IO$_READPBLK,R0	; Read in progress?
	BEQL	10$			; Branch if so, can't cancel
	CMPB	#IO$_WRITEPBLK,R0	; Write in progress?
	BEQL	10$			; Branch if so, can't cancel
	CMPB	#IO$_SENSEMODE,R0	; Sensemode in progress?        
	BEQL	10$			; Branch if so, can't cancel    
	CMPB	#IO$_SENSECHAR,R0	; Sensechar in progress?        
	BEQL	10$			; Branch if so, can't cancel    
	CMPB	#IO$_SETMODE,R0		; Setmode in progress?          
	BEQL	10$			; Branch if so, can't cancel    
	MOVAL	UCB$L_IOQFL(R5),R0	; Get I/O pending queue listhead address
	MOVAL	UCB$L_FLUSH_IOQFL(R5),R1; And flush I/O queue address
	MOVL	(R0),(R1)		; Temporarily save I/O queue contents
	MOVL	4(R0),4(R1)		; Including back link
	MOVL	R0,(R0)			; Initialize pending queue
	MOVL	R0,4(R0)		;
	PUSHR	#^M<R0,R1>		; Save regs
	MOVL	#SS$_ABORT,R0		; Set abort status
	MOVL	UCB$L_DEVDEPEND(R5),R1	; Set magtape status and characteristics

	.IF DEFINED DEBUG
	BSBW	TRACE_QIO_STAT		; Save the final QIO status in trace buf
	.ENDC

	CALL_REQCOM			; Complete the current I/O but do not
					; start a new one
	CLRL	UCB$L_IRP(R5)		; I/O packet no longer active
	CLRL	UCB$L_8MM_CHK(R5)	; Clear 8mm COPY check flag
	BISL	#UCB$M_BSY,-		; Set the busy bit to prevent any new
		UCB$L_STS(R5)		; I/O from being initiated
	POPR	#^M<R0,R1>		; Restore regs
	MOVL	(R1),(R0)		; Restore pending I/O queue, fwd link
	MOVL	4(R1),4(R0)		; Restore back link
	MOVL	R1,(R1)			; Reinitialize flush I/O queue
	MOVL	R1,4(R1)		;
	RET				; Return to caller

10$:	BICL	#UCB$M_CANCEL,-		; Indicate cancel is not in progress
		UCB$L_STS(R5)		;
20$:	RET				; Return to caller
	.PAGE
	.SBTTL	MK_REG_DUMP	- Device register dump routine
;+
; MK_REG_DUMP
;
; This routine dumps device-specific infromation into an errorlog packet.
; The format of this information is as follows:
;
;	+-----------------------+
;	|	Longword count	| 4 bytes
;	+-----------------------+
;	|	Revision	| 1 byte
;	+-----------------------+
;	|	HW revision	| 4 bytes
;	+-----------------------+
;	|	Error Type	| 1 byte
;	+-----------------------+
;	|	SCSI ID		| 1 byte
;	+-----------------------+
;	|	SCSI LUN	| 1 byte
;	+-----------------------+
;	|	SCSI SUBLUN	| 1 byte
;	+-----------------------+
;	|	Port status	| 4 bytes
;	+-----------------------+
;	|	SCSI CMD	| n bytes
;	+-----------------------+
;	|	SCSI STS	| 1 byte
;	+-----------------------+
;	|			|
;	|    Additional Data 	| n bytes
;	|			|
;	+-----------------------+
;
; The contents of the addition data field depends upon the error type. For 
; example, extended sense errors save the extended sense data returned by the
; target in this field.
;
; Step 2 Interface
;
;	void REGDUMP  (buffer *, CRAM *, UCB *)
;
; Obsolete Step 1 Inputs:
;
;	R0	- Output buffer address
;	R5	- UCB address 
;
; Obsolete Step 1 Outputs:
;
;	R0-R3	- Destroyed
;	All other registers perserved
;-

MK_REG_DUMP:
					; Generate a .CALL_ENTRY and fetch args from stack
	$DRIVER_REGDUMP_ENTRY PRESERVE=<R2,R3,R5>
	PUSHAL	(R0)+			; Save start of data area
	MOVB	#MK_ERROR_REVISION,(R0)+; Save revision level
	MOVL	UCB$L_HW_REV(R5),(R0)+	; Save hardware revision level
	MOVB	UCB$L_ERROR_TYPE(R5),-	; Save error type
		(R0)+
	MOVZWL	UCB$W_UNIT(R5),R1	; Get unit number
	CLRL	R2			; Prepare for extended divide
	EDIV	#100,R1,R1,R2		; Extract SCSI bus ID from unit number
	MOVB	R1,(R0)+		; Save SCSI bus ID
	MOVL	R2,R1			; Copy LUN, SUBLUN
	CLRL	R2			; Prepare for extended divide
	EDIV	#10,R1,R1,R2		; Extract LUN and SUBLUN 
	MOVB	R1,(R0)+		; Save LUN field
	MOVB	R2,(R0)+		; Save SUBLUN field 
	MOVL	UCB$L_VMS_STATUS(R5),-	; Save port status code
		(R0)+
	MOVW	#^XFF00,(R0)+		; Assume no SCSI CMD,STS available
	CLRB	(R0)+			; Assume no additional data
	MOVL	UCB$L_SCDRP(R5),R1	; Get active SCDRP address
	BEQL	50$			; Branch if none active
	MOVL	SCDRP$L_CMD_PTR(R1),R2	; Get address of SCSI command
	BEQL	50$			; Branch if none active
	SUBL	#3,R0			; Back up pointer
	MOVL	(R2)+,R3		; Get number of SCSI command bytes
	MOVB	R3,(R0)+		; Save command length
10$:	MOVB	(R2)+,(R0)+		; Save a command byte
	SOBGTR	R3,10$			; Continue for entire SCSI command
20$:	MOVB	#-1,(R0)+		; Assume no valid status byte
	MOVL	SCDRP$L_STS_PTR(R1),R2	; Get address of status byte
	BEQL	25$			; Branch if no status byte
	MOVB	(R2),-1(R0)		; Save SCSI status byte
25$:	CLRB	(R0)+			; Assume no additonal data
	MOVL	UCB$L_ERROR_TYPE(R5),R2 ; Save error type
	DISPATCH R2,TYPE=B,<-		; Dispatch according to error code
		<SCSI$C_CONNECTION_ERROR,	50$>,-
		<SCSI$C_MAP_BUFFER_ERROR,	50$>,-
		<SCSI$C_SEND_CMD_ERROR,		50$>,-
		<SCSI$C_INV_INQUIRY_DATA,	30$>,-
		<SCSI$C_MODE_SENSE_DATA,	30$>,-
		<SCSI$C_EXTND_SENSE_DATA,	30$>,-
		<SCSI$C_DIAGNOSTIC_DATA,	30$>>

	BUG_CHECK INCONSTATE,FATAL	; Unknown error type. This should
					; never happen.

; Error types which have additional data come here.

30$:	MOVL	SCDRP$L_SVA_USER(R1),R2	; Get address of additional data
	BEQL	50$
	MOVZBL	SCDRP$L_TRANS_CNT(R1),R1; Get length of additional data
	BEQL	50$			; Branch if none available

;	When we invoke a LOG_ERROR macro, ERL_STD$DEVICERR or ERL_STD$DEVICEATTN
;	is invoked to do the following:
;
;	1) Allocate an error log buffer entry; to do this it adds EMB$K_LENGTH
;	   bytes to the size in DDT$W_ERRORBUF and allocates that much space
;	   in the current error log buffer; the EMB$K_LENGTH bytes are for a
;	   header used to manage buffer entries. 
;
;	2) Store the address of the error log buffer entry in UCB$L_EMB. The
;	   base address of the buffer is considered to be the address of the
;	   first byte after the EMB$K_LENGTH byte header - which is why the
;	   symbols defined in $EMBHDDEF used to reference the header fields
;	   are negative.
;
;	3) Load standard, device-independent information into the beginning 
;	   of the buffer; this section is fixed at EMB$L_DV_REGSAV bytes.
;
;	4) Call a device-specific register dump routine passing a buffer
;	   address of UCB$L_EMB+EMB$L_DV_REGSAV; this is where the register
;	   dump routine can start writing device and error specific data.
;
;	This register dump routine writes two sections of data into the buffer.
;
;	The first is device specific but error independent; it will be written
;	for every type of error logged by this routine, and it's format is
;	described in this routine's header. It's size is not quite fixed, as
;	the size of the SCSI command can make it vary, but there's really no
;	risk of running off the end of the buffer writing it.
;
;	The second is error specific, and it's size will vary with the type of
;	error being logged; depending on the amount of error specific data 
;	we're told is available, it is possible for this section to run off the
;	end of the buffer, which could corrupt the header of the next error log
;	buffer. For that reason, some special steps are take here to make sure
;	that it doesn't happen.
;
;	* UCB$L_EMB contains the address of a DDT$W_ERRORBUF byte buffer
;
;	* On entry to this routine, a total of DDT$W_ERRORBUF-EMB$L_DV_REGSAV
;	  bytes are available to be written
;
;	* R0 always contains the address of the next byte to be written
;
;	The following code calculates the amount of space left in the buffer and
;	makes sure that no matter how many bytes of error specific data we have
;	available, we don't write past the end of the buffer.

	MOVL	UCB$L_DDT(R5),R3	; Get the DDT address
	MOVZWL	DDT$W_ERRORBUF(R3),R3	; Get the buffer size
	ADDL2	UCB$L_EMB(R5),R3	; Address after buffer
	SUBL2	R0,R3			; Convert to bytes-left
	CMPL	R1,R3			; Enough room available?
	BLEQ	35$			; Branch if so
	MOVL	R3,R1			; Use all of what's left

35$:	MOVB	R1,-1(R0)		; Save additional data length
40$:	MOVB	(R2)+,(R0)+		; Save a byte of extended sense data
	SOBGTR	R1,40$			; Repeat for all extended sense data

50$:	SUBL	(SP),R0			; Calculate number of bytes saved
	DECL	R0			; Round up to next longword (+3), but
					; don't count LW count field itelf in
					; LW count (-4)
	ASHL	#-2,R0,@(SP)+		; Save in LW count field at top of buffer
	RET				; Return
	.PAGE
	.SBTTL	MK_DIAGNOSE	- FDT preprocessing for Special pass-through function
;+
; MK_DIAGNOSE
;
; This routine performs FDT preprocessing including:
;
;	- Validating access to the descriptor buffer
;	- Validating access to, and locking, the read/write buffer 
;	- Copying the SCSI command to a buffer in non-paged pool
;
; STEP 2 INTERFACE:
;
;	int FDT_ROUTINE (IRP *, PCB *, UCB *, CCB *)
;
; STEP 1 JSB INPUTS:
;
;	R3	- IRP address
;	R4	- PCB address
;	R5	- UCB address
;	R6	- CCB address
;	AP	- Address of first function-dependent argument (P1)
;
; STEP 1 JSB OUTPUTS:
;
;	R0-R2	- Destroyed
;-


MK_DIAGNOSE:
	$DRIVER_FDT_ENTRY PRESERVE=<R2,R3,R4,R5,R6>

	MOVL	UCB$L_ORB(R5),R9		;U A; Get address of object rights block
	CLRQ	-(SP)				;U A; terminator and retlen for privilege a
	MOVL	ORB$L_NAME_POINTER(R9),-(SP)	;U A; Bufadr for device name
	MOVZWL	ORB$W_NAME_LENGTH(R9),R9	;U A; save size of device name
	ADDL3	#<NSA$_DEVICE_NAME@16>,R9,-(SP) ;U A; itmcod and bufsiz of item ent

	AUDIT_S_ITMLST = 16			;U A; Size of the itemlist is 16 bytes
	MOVL	SP,R9				;U A; save pointer to itemlist
	$IFPRIV DIAGNOSE,	-		;U A; Branch if process has DIAGNOSE priv
		10$,		-		;U A;	success branch
		MSG=DIAGNOSE_7,-		;U A;	Audit message
		ITMLST=R9,     -		;U A;	Item List
		PRESERVE=NO			;U A;
	MOVL	#SS$_NOPRIV,R0			;U A; Set no privilege status
	ADDL	#AUDIT_S_ITMLST,SP		;U A; Restore stack
	BRW	50$				;U A; Branch to abort the I/O
10$:
	PUSHAL	UCB$PS_DIAGNOSE(R5)            ;U A; UCB autosense info
	PUSHL	R6	                        ;U A; CCB address
	PUSHL	R5	                        ;U A; UCB address
	PUSHL	R4	                        ;U A; PCB address
	PUSHL	R3				;U A; UCB address	
	CALLS	#5,DKMK$DIAGNOSE_FDT            ;U A; Call common FDT routine
	RET
	
50$:	CALL_ABORTIO				;U A; Abort the I/O with stat	

	.PAGE
	.SBTTL	+
	.SBTTL	+ QIO INTERFACE ROUTINES
	.SBTTL	+
	.SBTTL	IO_PACKACK	- Pack acknowledge function
	.SBTTL	IO_SENSECHAR	- Get device characteristics
	.SBTTL	IO_SENSEMODE	- Get device characteristics
;+
; IO_PACKACK
; IO_SENSEMODE
; IO_SENSECHAR
;
; This routine executes a PACKACK request by polling the device with TEST UNIT
; READY commands until the device is ready, then sending INQUIRY, RECEIVE 
; DIAGNOSTIC DATA, MODE SENSE, and MODE SELECT commands. The effect of these 
; commands is to prepare the drive to accept READ, WRITE, and tape motion 
; commands.
;
; INPUTS:
;
;	R3	- UCB address
;	R4	- PDT address
;	R5	- SCDRP address
;
; OUTPUTS:
;
;	R0	- Status
;	R1,R2	- Destroyed
;	All other registers preserved
;-

IO_PACKACK:
	BSBW	PACKACK_SEQ
	BRW	COMPLETE_IO		; Complete this I/O function

PACKACK_SEQ:

	.JSB_ENTRY INPUT=<R3,R4,R5>,OUTPUT=<R0>,SCRATCH=<R1,R2>
	BSBW	WAIT_UNIT_READY		; Wait for the device to spin up
	BLBC	R0,30$			; Branch on error
	BSBW	INQUIRY			; Execute an INQUIRY command
	BLBC	R0,30$			; Branch on error
	BSBW	RECEIVE_DIAG		; Execute a received diagnostic data cmd
	BLBC	R0,30$			; Branch on error
	BSBW	MODE_SENSE		; Execute a MODE SENSE command
	BLBC	R0,30$			; Branch on error
	BSBW	MODE_SELECT		; Set error recovery parameters, block
					; size, etc. (if necessary)
	BLBC	R0,30$			; Branch on error
	BSBW	CHECK_SKIPFILE_SUPPORT	; Check if READ POSITION cmd supported
	BSBW	CHECK_COMPACTION_SUPPORT ; check if device supports data compaction
20$:	BSBW	SET_CONN_CHAR		; Set up the connection characteristics
	BLBC	R0,30$			; Branch on error
    	BISL	#UCB$M_VALID,-		; Set volume valid
		UCB$L_STS(R3)		; 
30$:	RSB				; Return to caller

IO_SENSEMODE:
IO_SENSECHAR:

	BSBW	PACKACK_SEQ
	BLBC	R0,20$			; Branch on error
;
; Return device characteristics if user supplied buffer
;
;
; the IFNOT_TSZO7 is commented out below to allow the DEVDEPEND info
; to be returned for all devices
;
;	IFNOT_TSZ07	10$		; Branch if not TSZ07 

	MOVL	SCDRP$L_IRP(R5),R1	; Get pointer to IRP
	TSTL	IRP$L_SVAPTE(R1)	; Is there a user supplied buffer?
	BEQL	10$			; Branch if not.

	ASSUME UCB$L_DEVDEPEND EQ UCB$B_DEVCLASS+4
	ASSUME UCB$L_DEVDEPND2 EQ UCB$L_DEVDEPEND+4

	MOVL	@IRP$L_SVAPTE(R1),R0	 ; Get system buffer data area address.
	MOVL	UCB$B_DEVCLASS(R3),(R0)+ ; Store dev class, type, buffer size.
	MOVL	UCB$L_DEVDEPEND(R3),-	 ; Store device characteristics.
		(R0)+
	CMPL	IRP$L_BCNT(R1), #12	; User buffer length = 12?
	BNEQ	10$			; Branch if not.
	MOVL	UCB$L_DEVDEPND2(R3),-	; Store extended device characteristics.
		(R0)+			;
10$:	MOVZWL	#SS$_NORMAL,R0		; Success status
20$:	BRW	COMPLETE_IO		; Complete this I/O function
	.PAGE
	.SBTTL	IO_DSE		- Data security erase
;+
; IO_DSE
;
; This routine sends an ERASE command to the tape to erase all the data from
; the current position to the end of the tape. The tape is then rewound.
;
; INPUTS:
;
;	R3	- UCB address
;	R4	- PDT address
;	R5	- SCDRP address
;
; OUTPUTS:
;
;	R0	- Status
;	R1,R2	- Destroyed
;	All other registers preserved
;-

IO_DSE:
IO_ERASETAPE:

 	BICL	#<MT$M_BOT!-		; Indicate that we're not at beginning
 		  MT$M_EOT!-		; of tape, end of tape, or end of file
 		  MT$M_EOF>,-	;
 		UCB$L_DEVDEPEND(R3)	;
	MOVAL	CMD_ERASE,R2		; Get address of erase command
	SETUP_CMD			; Setup the command
	SEND_COMMAND_ORDERED		; Send the command
	CLEANUP_CMD			; Cleanup from the SCSI command
	BLBC	R0,COMPLETE_IO		; Branch on error
	BRW	IO_REWIND		; Otherwise, go rewind the tape
	.PAGE
	.SBTTL	IO_SETMODE	- Set mode
;+
; IO_SETMODE
;
; This routine sends a MODE_SELECT command to the tape to set up various drive
; characteristics including default block size, buffered (streaming) mode, 
; number of fillers, and infinite reselection timeout.
;
; INPUTS:
;
;	R3	- UCB address
;	R4	- PDT address
;	R5	- SCDRP address
;
; OUTPUTS:
;
;	R0	- Status
;	R1,R2	- Destroyed
;	All other registers preserved
;
;-

IO_SETMODE:

	MOVW	SCDRP$L_MEDIA+2(R5),-	; Set new default buffer size
		UCB$W_DEVBUFSIZ(R3)	;
	IF_TSZ07	10$		; Branch if TSZ07
; Add code for sw front panel density control
; Density will be in frontpnl lo byte as binary number. If zero, do usual
;Note we leave tsz07 alone more or less.
        tstb    ucb$l_frontpnl(r3)      ; Is the user density null?
        beql    7$                      ; if so skip special stuff
; Set density value.
        movw    ucb$l_frontpnl(r3),UCB$W_MK_DENSITY(R3)  ; Copy density
7$:
	BSBW	MODE_SELECT		; Send a mode select command
	BLBS	R0,10$			; attempt to set compaction/density
	BRW	30$   			; branch on error
;
; 	- TSZ07 set new density if at BOT
;       - other devices check for compaction enable/disable
;
10$:	TSTL	UCB$L_RECORD(R3)	; Are we at BOT?
	BNEQ	25$			; Branch if not,  don't change density
	MOVL	SCDRP$L_IRP(R5),R1	; Get pointer to IRP
	MOVZWL	IRP$L_MEDIA+4(R1),-	; Save characteristics in unused
 		UCB$L_BOFF(R3)		; UCB location.
	EXTZV	#MT$V_DENSITY,-		; Get density selection
		#MT$S_DENSITY,-
		UCB$L_BOFF(R3),R0
;
; Do density/compaction checks
;
        tstb    ucb$l_frontpnl(r3)      ; Is the user density null?
        bneq    11$                     ; If not accept some /dens switches
	IFNOT_TSZ07	20$
11$:
;
; TSZ07 density checks
;
	ASSUME MT$K_DEFAULT EQ 0
	TSTW	R0			; default density ?
	BNEQ	13$			; NEQ = no
	CLRW	UCB$W_MK_DENSITY(R3)	; Set density to default
	BRB	17$			; issue default density mode_select
13$:
	CMPW	#MT$K_PE_1600,R0	; Set 1600 bpi?
	BEQL	15$			; Branch if so
	CMPW	#MT$K_GCR_6250,R0	; Set 6250 bpi?
	BEQL	15$			; Branch if so
	tstl	ucb$l_frontpnl(r3)	; leave frontpanel dens alone too
	bneq	17$
	MOVZWL	#SS$_BADPARAM,R0	; Invalid density selected
	BRW	COMPLETE_IO		; Finished
15$:
	SUBW3	#2,R0,UCB$W_MK_DENSITY(R3)  ; Compute device density (2 less
					    ; less than VMS density values).
17$:	BSBW	MODE_SELECT		; Send a mode select command
	BLBC	R0,30$			; Branch on error
	MOVL	SCDRP$L_IRP(R5),R1	; Get pointer to IRP
	MOVZWL	IRP$L_MEDIA+4(R1),-	; Save characteristics in unused
 		UCB$L_BOFF(R3)		; UCB location.
	EXTZV	#MT$V_DENSITY,-		; Get density selection again
		#MT$S_DENSITY,-
		UCB$L_BOFF(R3),R0
	INSV	R0,-			; Store density value in UCB
		#MT$V_DENSITY,-		; device characteristics field.
		#MT$S_DENSITY,-
		UCB$L_DEVDEPEND(R3)
	BRB	25$			; all done
;
; All other devices get checked for compaction enable/disable
;
20$:	CMPW	#MT$K_SCSI_DC1,R0	; Set Compaction mode?
	BNEQ	21$                     ; *** IGNORE ANY OTHER DENSITY VALUES
	MOVL	#1, R0			; flag for SETUP_COMPACTION = ON
	BRB	22$
21$:	CLRL	R0			; flag compaction off
22$:					; Only attempt to issue mode_select if 
	BITL	#MT2$M_COMP_SUP,-	; compaction is supported
		UCB$L_DEVDEPND2(R3)	;
	BEQL	25$			; branch if not
        tstw    ucb$l_frontpnl(r3)      ; front panel overrides compaction
        bneq    23$
	BSBW	SETUP_COMPACTION	; do MODE_SELECT for compaction page
	BLBC	R0,30$			; Branch if error
23$:	MOVL	SCDRP$L_IRP(R5),R1	; Get pointer to IRP
	EXTZV	#MT$V_DENSITY,-		; Get density selection again
		#MT$S_DENSITY,-
		IRP$L_MEDIA+4(R1),R1
	INSV	R1,-			; Store density value in UCB
		#MT$V_DENSITY,-		; device characteristics field.
		#MT$S_DENSITY,-
		UCB$L_DEVDEPEND(R3)
	BRB	30$			; all done

25$:	MOVZWL	#SS$_NORMAL,R0		; Success status
30$:	BRW	COMPLETE_IO		; Complete this I/O function

	.PAGE
	.SBTTL	IO_WRITEOF	- Write tape mark
	.SBTTL	IO_WRITEMARK	- Write tape mark
;+
; IO_WRITEOF
; IO_WRITEMARK
;
; This routine writes one tapemark at the current position on the tape.
;
; INPUTS:
;
;	R3	- UCB address
;	R4	- PDT address
;	R5	- SCDRP address
;
; OUTPUTS:
;
;	R0	- Status
;	R1,R2	- Destroyed
;	All other registers preserved
;-

IO_WRITEMARK:
IO_WRITEOF:

 	BICL	#<MT$M_BOT!-		; Indicate that we're not at beginning
 		  MT$M_EOT!-		; of tape, end of tape, or end of file
 		  MT$M_EOF>,-	;
 		UCB$L_DEVDEPEND(R3)	;
	MOVAL	CMD_WRITE_FILEMARKS,R2	; Get address of write filemarks command
	SETUP_CMD			; Setup the command
	MOVL	SCDRP$L_CMD_PTR(R5),R1	; Get the SCSI command address
	MOVB	#1,SCSI_WFM$B_CNT+4+2(R1); Specify one filemark
10$:	SEND_COMMAND_ORDERED		; Send the command
	BLBS	R0,15$			; Branch if success
	CMPL	R0,#SS$_ENDOFTAPE	; End of tape? If so, treat as success
	BEQL	15$			; Branch if so
	CMPL	R0,#SS$_PARITY		; Media error?
	BNEQ	20$			; Branch if not
15$: 	INCL	UCB$L_RECORD(R3)	; Update record count
20$:	CLEANUP_CMD			; Cleanup from the SCSI command
	BRW	COMPLETE_IO		; Complete this I/O function
	.PAGE
	.SBTTL	IO_READPBLK	- Read a set of blocks from the SCSI drive
;+
; IO_READPBLK
;
; This routine reads a record from the tape. If the function is a REVERSE READ,
; a backspace of one record is performed both prior to and after the read, as
; many SCSI tape drives, including the TZK50 and TZ30, don't support the READ
; REVERSE command.
;
; INPUTS:
;
;	R3	- UCB address
;	R4	- PDT address
;	R5	- SCDRP address
;
; OUTPUTS:
;
;	R0	- Status
;	R1,R2	- Destroyed
;	All other registers preserved
;-

IO_READPBLK:
IO_READLBLK:

 	BICL	#<MT$M_BOT!-		; Indicate that we're not at beginning
 		  MT$M_EOT!-		; of tape, end of tape, or end of file
 		  MT$M_EOF>,-	;
 		UCB$L_DEVDEPEND(R3)	;
	BBC	#IO$V_REVERSE,-		; Branch if this is NOT a read reverse
 		SCDRP$L_FUNC(R5),10$	; function
	MNEGW	#1,R0			; Prepare to backup one record
        PUSHL	SCDRP$L_BCNT(R5)        ; Save read byte count
        BSBW    SKIP_RECORD             ; Backup one record
        POPL  SCDRP$L_BCNT(R5)        ; Restore read byte count
	BLBC	R0,50$			; Branch on error
10$:	MOVAL	CMD_READ,R2		; Address of read command
	SETUP_CMD			; Setup the command
	ADDL3	#<4+SCSI_RD$B_LEN+3>,-	; Get address just beyond transfer 
		SCDRP$L_CMD_PTR(R5),R0	; length field in the SCSI command
	MOVAL	SCDRP$L_BCNT(R5),R1	; Get address of byte count field in SCRRP
	.REPT	3
	MOVB	(R1)+,-(R0)		; Fill in the transfer length field
	.ENDR	
	MOVL	#IRP$M_FUNC,-		; Set the FUNC but to indicate this
		SCDRP$IS_STS(R5)	; is a read function
	BISB	#SCDRP$M_FLAG_BUFFER_MAPPED,-; Remember the fact that a buffer
		SCDRP$L_SCSI_FLAGS(R5)	; has been mapped
	SPI$BUFFER_MAP			; Map the user's buffer
	SEND_COMMAND_ORDERED		; Send the SCSI command
	CLEANUP_CMD			; Cleanup from the command
 	INCL	UCB$L_RECORD(R3)	; Keep track of forward tape position
	BLBS	R0,15$			; Branch on success
	CMPL	R0,#SS$_DATAOVERUN	; If overrun, treat as success
	BEQL	15$			; Branch if so
	CMPL	R0,#SS$_ENDOFTAPE	; If EOT, treat as success
	BNEQ	50$			; Branch if not
	MOVW	#SS$_NORMAL,R0		; Change EOT status to success
15$:	MOVL	SCDRP$L_TRANS_CNT(R5),R1; Get the transfer count value
	BBC	#IO$V_REVERSE,-		; Branch if this is NOT a read reverse
 		SCDRP$L_FUNC(R5),20$	; function
	PUSHL	R0			; Save status from read
	PUSHL	R1			; Save the transfer count value
	MNEGW	#1,R0			; Prepare to backup one record
	BSBW	SKIP_RECORD		; Backup one record
	BLBC	R0,60$			; If skip fails, use that as the status
					; code
	POPL	R1			; Restore transfer count
	POPL	R0			; Restore status from read
20$:	INSV	R1,#16,#16,R0		; Place transfer count in high-order 
					; word of R0
50$:	BRW	COMPLETE_IO		; Complete this I/O function

60$:	POPL	R2			; Clean off the internal stack
	POPL	R2			;
	BRB	50$			; Use common exit
	.PAGE
	.SBTTL	IO_WRITEPBLK	- Write a set of blocks to the SCSI drive
;+
; IO_WRITEBLK
;
; This routine writes a record to the tape. If the function is a REVERSE WRITE,
; a backspace of one record is performed both prior to and after the write, as
; many SCSI tape drives, including the TZK50 and TZ30, don't support the WRITE
; REVERSE command.
;
; INPUTS:
;
;	R3	- UCB address
;	R4	- PDT address
;	R5	- SCDRP address
;
; OUTPUTS:
;
;	R0	- Status
;	R1,R2	- Destroyed
;	All other registers preserved
;-

IO_WRITEPBLK:
IO_WRITELBLK:
	.if	eq,1
	bitl	#mt$m_bot,UCB$L_DEVDEPEND(R3)
	beql	2$
        tstb    ucb$l_frontpnl(r3)      ; Is the user density null?
        beql    2$                      ; if so skip special stuff
; Set density value.
        movw    ucb$l_frontpnl(r3),UCB$W_MK_DENSITY(R3)  ; Copy density
	BSBW	MODE_SELECT		; Send a mode select command
2$:
	.endc
	BICL	#<MT$M_BOT!-		; Indicate that we're not at beginning
		  MT$M_EOT!-		; of tape, end of tape, or end of file
		  MT$M_EOF>,-	;
		UCB$L_DEVDEPEND(R3)	;
	BBC	#IO$V_REVERSE,-		; Branch if this is NOT a write reverse
 		SCDRP$L_FUNC(R5),10$	; function
	MNEGW	#1,R0			; Prepare to backup one record
        PUSHL	SCDRP$L_BCNT(R5)        ; Save read byte count
	BSBW	SKIP_RECORD		; Backup one record
        POPL  	SCDRP$L_BCNT(R5)        ; Restore read byte count
	BLBC	R0,50$			; Branch on error
10$:	MOVAL	CMD_WRITE,R2		; Address of read command
	SETUP_CMD			; Setup the command
	ADDL3	#<4+SCSI_WRT$B_LEN+3>,-	; Get address just beyond transfer 
		SCDRP$L_CMD_PTR(R5),R0	; length field in the SCSI command
	MOVAL	SCDRP$L_BCNT(R5),R1	; Get address of byte count field in IRP
	.REPT	3
	MOVB	(R1)+,-(R0)		; Fill in the transfer length field
	.ENDR	
	CLRL	SCDRP$IS_STS(R5)	; Clear the FUNC but to indicate this
					; is a write function
	BISB	#SCDRP$M_FLAG_BUFFER_MAPPED,-; Remember the fact that a buffer
		SCDRP$L_SCSI_FLAGS(R5)	; has been mapped
	SPI$BUFFER_MAP			; Map the user's buffer
	SEND_COMMAND_ORDERED		; Send the SCSI command
        BLBS    R0,15$                  ; Branch on success
        IFNOT_8MM	15$             ; Br if not 8MM
        TSTL    UCB$L_8MM_CHK(R3)       ; Should 8MM COPY check be done?
        BEQL    15$                     ; Br if no
        BSBW    DO_8MM_COPY_CHK         ; Attempt recovery processing
        BLBC    R0,15$                  ; Branch on error

; We have just successfully repositioned the tape in the special case of 8MM
; drives, so we may now retry the write command that originally failed.
; Before re-using the SCDRP, deallocate/reallocate a command buffer and
; unmap/remap the user buffer, as smart adapters require.
 
	SPI$BUFFER_UNMAP		; Un-map data buffer (but don't
					; deallocate data buffer)
	MOVL	SCDRP$L_CMD_BUF(R5),R0	; Get address of command buffer
	SPI$CMD_BUFFER_DEALLOC		; Dealloc command buffer 
	MOVAL	CMD_WRITE,R2		; Address of read command
	SETUP_CMD			; Setup the command
	ADDL3	#<4+SCSI_WRT$B_LEN+3>,-	; Get address just beyond transfer 
		SCDRP$L_CMD_PTR(R5),R0	; length field in the SCSI command
	MOVAL	SCDRP$L_BCNT(R5),R1	; Get address of byte count field in IRP
	.REPT	3
	MOVB	(R1)+,-(R0)		; Fill in the transfer length field
	.ENDR	
	CLRL	SCDRP$IS_STS(R5)	; Clear the FUNC but to indicate this
					; is a write function
	BISB	#SCDRP$M_FLAG_BUFFER_MAPPED,-; Remember the fact that a buffer
		SCDRP$L_SCSI_FLAGS(R5)	; has been mapped
	SPI$BUFFER_MAP			; Map the user's buffer
        SEND_COMMAND_ORDERED		; Send the SCSI WRITE command again

; Common cleanup processing

15$:	CLEANUP_CMD			; Cleanup from the command
	BLBS	R0,20$			; Branch on success
	CMPL	R0,#SS$_ENDOFTAPE	; End of tape? If so, treat as success
	BEQL	20$			; Branch if so
	CMPL	R0,#SS$_PARITY		; Media error?
	BNEQ	50$			; Branch if not
20$:	INCL	UCB$L_RECORD(R3)	; Keep track of forward tape position
	MOVL	SCDRP$L_TRANS_CNT(R5),R1; Get transfer count
	BBC	#IO$V_REVERSE,-		; Branch if this is NOT a read reverse
 		SCDRP$L_FUNC(R5),30$	; function
	PUSHR	#^M<R0,R1>		; Save command status, transfer count
	MNEGW	#1,R0			; Prepare to backup one record
	BSBW	SKIP_RECORD		; Backup one record
	MOVL	R0,R2			; Copy status for backspace command
	POPR	#^M<R0,R1>		; Restore write status, transfer count
	BLBS	R2,30$			; Branch if backspace suceeded
	MOVL	R2,R0			; Otherwise, return backupspace status
30$:	INSV	R1,#16,#16,R0		; Return transfer count in h.o. word of R0
50$:	BRW	COMPLETE_IO		; Complete this I/O function
	.PAGE
	.SBTTL	IO_SKIP_RECORD	- Move n records
;+
; IO_SKIP_RECORD
;
; This routine skips n records, where n is a signed word in the SCRP$L_MEDIA
; field. The skip operation stops upon encountering a tape mark. The actual
; number of records skipped is returned in the h.o. word of R0. If two 
; consecutive tape marks are encountered while skipping forward, and the NOT 
; mounted files-11, then SS$_ENDOFVOLUME status is returned.
;
; INPUTS:
;
;	R3	- UCB address
;	R4	- PDT address
;	R5	- SCDRP address
;
; OUTPUTS:
;
;	R0	- Status, actual skip count in h.o. word
;	R1,R2	- Destroyed
;	All other registers preserved
;
;-
	.ENABL	LSB

IO_SKIP_RECORD:

	CVTWL	SCDRP$L_MEDIA(R5),R0	; Number of records to skip
	BLSS	IO_SKIP_RECORD_REV	; Branch if negative
	BEQL	50$			; Branch if zero (nothing to do)
	BSBW	SKIP_RECORD		; Skip the specified number of records
	BLBS	R0,10$			; Branch on success
	CMPL	R0,#SS$_ENDOFTAPE	; End of tape?
	BEQL	10$			; Branch if so
	CMPL	R0,#SS$_ENDOFFILE	; Tapemark encountered?
	BNEQ	20$			; Branch if not
	BSBW	CHECK_EOV		; Check for logical end-of-volume
	CMPL	R0,#SS$_ENDOFVOLUME	; End of volume?
	BNEQ	10$			; Branch if so
	DECW	R1			; Tape was backed up one record
10$:	INSV	R1,#16,#16,R0		; Return skip count in high-order

20$:	BRW	COMPLETE_IO		; Complete the I/O request

IO_SKIP_RECORD_REV:

	CLRL	UCB$L_BOFF(R3)		; Initialize accumulated skip count
30$:	BSBW	SKIP_RECORD		; Skip the specified number of records
	SUBL	R1,UCB$L_BOFF(R3)	; Update accumulated skip count
	BLBS	R0,40$			; Branch on success
	CMPL	R0,#SS$_ENDOFFILE	; Tapemark encountered?
	BEQL	40$			; Branch if so
	CMPL	R0,#SS$_ENDOFTAPE	; End of tape?
	BNEQ	20$			; Branch if not
	TSTL	UCB$L_RECORD(R3)	; Are we at BOT?
	BEQL	35$			; Branch if so
	TSTW	R1			; Any records skipped?
	BGEQ	35$			; Branch if not making any progress
	ADDL3	SCDRP$L_MEDIA(R5),-	; Prepare to skip the remaining number
		UCB$L_BOFF(R3),R0	; of records
	BLSS	30$			; Branch if more records to skip

35$:	MOVL	#SS$_NORMAL,R0		; Treat EOT or BOT as success
40$:	MOVL	UCB$L_BOFF(R3),R1	; Place total skip count in R1
	BRB	10$			; Use common exit path

50$:	CLRL	R1			; No records skipped
	MOVZWL	#SS$_NORMAL,R0		; Set success status
	BRB	10$			; Use common exit path

	.DSABL	LSB
	.PAGE
	.SBTTL	IO_SKIP_FILE	- Move n tapemarks (in either direction)
;+
; IO_SKIP_FILE
;
; This routine spaces n tapemarks in either direction. SCDRP$L_MEDIA contains
; a signed integer specifying the number of tapemarks to space.  If possible,
; use the SCSI SPACE FILEMARKS command followed by the READ POSITION command
; (READ POSITION allows us to keep track of first block location).  Otherwise 
; use the space record option, and each time a filemark is encountered, we 
; accumulate the number of records skipped.
; Pre-requisites for employing the faster performing space-by-filemark are:
;
; 	o Device must support the READ_POSITION command
;	o Device must generate Blank Check when encountering logical EOV
;	  (TSZ05/7 do not meet this prerequisite)
;	o The space count must be other than +1 or +2, since in those cases
;	  the overhead of Space by Filemarks actually degrades performance.
;
; A note on the skip-by-record algorithm:
; When skipping forward, a record skip count less than the maximum is used 
; in order to allow for reasonable responsive I/O cancels. Because of the way
; tape drives implement reverse skip record (rewind, space forward), this same 
; method can not be used for space reverse operations (i.e., the maximum 
; reverse record count is used).
;
; INPUTS:
;
;	R3	- UCB address
;	R4	- PDT address
;	R5	- SCDRP address
;-
	.ENABL	LSB

IO_SKIP_FILE:
	CVTWL	SCDRP$L_MEDIA(R5),R1	; Get number of files to skip
	MOVL	R1,UCB$L_BOFF(R3)	; Save it in a convenient place
	MOVL	R1,UCB$L_BCNT(R3)	; Save another copy
;
; Set the flag to indicate we do NOT use skip by filemarks until the tests
; below may show that we should and can do so.
;
	BICL	#MT$M_FASTSKIP_USED,-	; Clear skip by filemarks bit
		UCB$L_DEVDEPEND(R3)
; If user set up to do fast skip even on one or two files, do it.
	bitl	#ucb$m_fast_1file,ucb$l_mk_flags(r3)
	bneq	3$
	CMPL	#1,R1			; Optimize for the most common case
					; of skip count = 1
	BEQL	10$                     ; Skip-by-record is faster in this case
	CMPL	#2,R1			; Optimize for the next common case
					; of skip count = 2
	BEQL	10$                     ; Skip-by-record is faster in this case

					; If possible, for count > 2,
3$:					; use SPACE FILEMARKS/READ POSITION
	BBC	#UCB$V_SKIPFILE_SUPPORTED,- 
		UCB$L_MK_FLAGS(R3),-    ; Test for hardware support for
		5$			; skip by filemarks, branch if no
					; support
	BBS	#UCB$V_ALLOWFAST_ALWAYS,- 
		UCB$L_MK_FLAGS(R3),-    ; Test for skip by filemarks
		SPACE_FILEMARKS		; enabled as default operation,
					; branch if default is set
	BBC	#UCB$V_ALLOWFAST_PER_IO,- 
		UCB$L_MK_FLAGS(R3),-    ; Test for skip by filemarks
		5$			; enabled for function code
					; modifier operations, branch
					; if not enabled
	BBS	#IO$V_ALLOWFAST,-	; Branch if IO$V_ALLOWFAST function
 		SCDRP$L_FUNC(R5),-	; code modifer is set
		SPACE_FILEMARKS

5$:	TSTL	R1			; Test space count
	BLSS	IO_SKIP_FILE_REV	; Branch if negative count
	BEQL	20$			; Branch if zero skip count

; If here, then use the skip-by-record method

10$:	BBS	#UCB$V_CANCEL,-		; Branch if I/O has been canceled?
		UCB$L_STS(R3),20$	;
	MOVW	#^X7FFF,R0		; Forward skip count 
	BSBW	SKIP_RECORD		; Skip records (updating UCB$L_RECORD)
	BLBS	R0,10$			; Branch if no tapemark found
	CMPL	R0,#SS$_ENDOFFILE	; Tape mark encountered?
	BNEQ	40$			; Branch if not
	BSBW	CHECK_EOV		; Check for logical end-of-volume
	CMPL	R0,#SS$_ENDOFVOLUME	; Logical EOV?
	BEQL	30$			; Branch if so
	CMPL	R0,#SS$_ENDOFFILE	; EOF status?
	BNEQ	40$			; Branch if not
	DECL	UCB$L_BOFF(R3)		; One more tapemark found
	BGTR	10$			; Branch if not done yet

20$:	MOVW	#SS$_NORMAL,R0		; Set success status
30$:	SUBL3	UCB$L_BOFF(R3),-	; Calculate actual number of
		UCB$L_BCNT(R3),R1	; files skipped
	INSV	R1,#16,#16,R0		; Save in high-order word of R0

40$:	BRW	COMPLETE_IO


IO_SKIP_FILE_REV:

50$:	BBS	#UCB$V_CANCEL,-		; Branch if I/O has been canceled?
		UCB$L_STS(R3),20$	;
	MOVW	#^X8000,R0		; Maximum reverse skip count
	BSBW	SKIP_RECORD		; Skip records (updating UCB$L_RECORD)
	BLBS	R0,50$			; Branch if no tapemark found
	CMPL	R0,#SS$_ENDOFTAPE	; End of tape?
	BEQL	20$			; Branch if not
	CMPL	R0,#SS$_ENDOFFILE	; Tape mark encountered?
	BNEQ	40$			; Branch if not

60$:	INCL	UCB$L_BCNT(R3)		; One more tapemark found
	BLSS	50$			; Branch if more to do
	BRB	20$			; Use common exit

; If here, then use the skip-by-file method

SPACE_FILEMARKS:			; Do Space Filemarks to move the tape
                                        
	BISL	#MT$M_FASTSKIP_USED,-	; Set skip by filemarks bit
		UCB$L_DEVDEPEND(R3)
	BBS	#UCB$V_CANCEL,-		; Branch if I/O has been canceled?
		UCB$L_STS(R3),20$	;
	MOVL	R1,R0			; Forward skip count 
	BLSS	SPACE_FILEMARKS_REV	; Branch if negative count
	BEQL	20$			; Exit if zero count
	BSBW	SKIP_FILE		; Skip tapemarks, return count in R1
	BLBS	R0,70$			; Case 1:  SS$_NORMAL was returned
	CMPL	R0,#SS$_ENDOFVOLUME	; Case 2:  Logical EOV status?
	BNEQ	140$	                ; Case 3:  Fall through, serious error
	BRW	75$			; Go to Logical EOV processing

; Case 1:  SS$_NORMAL was returned.
;
; If here, then SS$_NORMAL has just been returned.  There are two sub-cases:
; the tape is now positioned on some tapemark in the middle of the tape,
; or the tape is positioned on the second tapemark within an EOV tapemark pair. 
; In the latter case it is important to return SS$_ENDOFVOLUME instead of
; success.  In order to determine which case we have, employ the following 
; logic:  upon SS$_NORMAL, back up to the position just before the current 
; tapemark, and examine whether it is a tapemark as well.  If not, then we 
; know we are not at EOV after all;  so skip forward to get to our original 
; position.  If we are in an EOV situation, however, we must change the status 
; code from SS$_NORMAL to SS$_ENDOFVOLUME.
;
; (In more detail), to check whether the preceding record position is a tape 
; mark as well, refer to the diagram below and note that:  
; At this point the tape is positioned on the end-of-partition side ("A")
; of the tape mark.  We must space back one file to get to the beginning-of-
; partition side ("B") of the tape mark, then space back one record (to "C")
; and test for a tapemark-encounterd Check Condition.
;
;	----+----------+----------+--------------------------------------
;	    |Record to | Tapemark |
;	    |be tested |          |
;	----+----------+----------+--------------------------------------
;          (C)        (B)        (A)

; Space reverse by one filemark

70$:	MOVL	#-1,R0			; Prepare to back up to beg-of-partn
	PUSHL	R1			; Save actual skip count
	BSBW	SKIP_FILE		; Get to beginning-of-partition side
	POPL	R1			; Restore actual skip count
	BLBC	R0,140$			; Exit if error
	
; Space reverse by one record

	MOVL	#-1,R0			    ; Prepare to back up one record
	PUSHL	R1			    ; Save actual skip count
	BISL	#UCB$M_NO_POSITION_UPDATE,- ; Do not update UCB$L_RECORD
		UCB$L_MK_FLAGS(R3)          ; at this time
	BSBW	SKIP_RECORD		    ; Jump back over preceding record
	BICL	#UCB$M_NO_POSITION_UPDATE,- ; Clear indicator flag
		UCB$L_MK_FLAGS(R3)
	POPL	R1			    ; Restore actual skip count
	.BRANCH_LIKELY
	BLBS	R0,72$			    ; Preceding record is not a tapemk
	CMPL	R0,#SS$_ENDOFFILE	    ; EOF status?
	BNEQ	140$			    ; Branch if not, serious problem 

; Case 1a:
; We have hit the case of 2 consecutive tapemarks, so space forward one
; tapemark to position the tape between the 2 marks.

	MOVL	#1,R0			; Prepare to position between 2 tm's
	PUSHL	R1			; Save actual skip count
	BSBW	SKIP_FILE		; Position the tape 
	POPL	R1			; Restore actual skip count
	BLBC	R0,140$			; Exit if error
	MOVL	#SS$_ENDOFVOLUME,R0	; We hit a tapemark, so return end-of-
					;    volume status
	DECL	R1			; We just backed up one filemark
	BRW	80$			; Join common exit code

; Case 1b:
; The record we just backed up to is not a tapemark.  So we are not at end
; of logical volume.  So restore position by spacing forward (across both
; the record and the subsequent tapemark).  Space by file instead of by record 
; to avoid a Check Condition. 

72$:	MOVL	#1,R0			; Prepare to skip forward acros tpmark
	PUSHL	R1			; Save actual skip count
	BSBW	SKIP_FILE		; Skip forward one tapemark
	POPL	R1			; Restore actual skip count
	BLBS	R0,80$			; Tape is now back to original position
	BRW	140$			; Branch if err, serious problem exists

; Case 2:  Logical EOV status
;
; If here, then SS$_ENDOFVOLUME has just been returned.  Back up one tapemark.
; While it may seem similar to the code above, it is kept separate in
; order to detect the error condition that SS$_ENDOVOLUME (signified by a
; BLANK CHECK) was returned by SKIP_FILE, but the tape does not end in
; a pair of tapemarks.

75$:	MOVL	#-1,R0			; Prepare to back up one record
	PUSHL	R1			; Save actual skip count
	BISL	#UCB$M_NO_POSITION_UPDATE,-
		UCB$L_MK_FLAGS(R3)
	BSBW	SKIP_RECORD		; Position tape between filemarks
	BICL	#UCB$M_NO_POSITION_UPDATE,-
		UCB$L_MK_FLAGS(R3)
	POPL	R1			; Restore actual skip count
	CMPL	R0,#SS$_ENDOFFILE	; EOF status?
	BNEQ	140$			; Branch if not, serious problem exists
	MOVL	#SS$_ENDOFVOLUME,R0	; Return end of volume status
	DECL	R1			; We just backed up one filemark

; Common code for Space Forward processing

80$:	BSBW	READ_POSITION		; Update ucb$l_record
	BISL	#MT$M_EOF,-             ; We are positioned on a tapemark
		UCB$L_DEVDEPEND(R3)
	MOVL	UCB$L_RECORD(R3),-	; Update last tapemark found during
		UCB$L_PREV_TM(R3)       ; a forward search
	INSV	R1,#16,#16,R0		; Save in high-order word of R0
	BRW	140$                    ; Go to COMPLETE_IO

SPACE_FILEMARKS_REV:

	BSBW	SKIP_FILE		; Skip files, return actual count in R1
	BLBS	R0,100$			; Branch if success
	CMPL	R0,#SS$_ENDOFTAPE	; Beginning of tape?
	BNEQ	140$			; Other errors serious - exit
	MOVL	#SS$_NORMAL,R0		; But BOT is not an error
100$:	
	BISL	#UCB$M_REV_SKIP,-	; Indicate direction (needed by
		UCB$L_MK_FLAGS(R3)      ; READ POSITION)
	BSBW	READ_POSITION		; Update ucb$l_record
	BICL	#UCB$M_REV_SKIP,-       ; Clear flag
		UCB$L_MK_FLAGS(R3)
	MNEGL	R1,R1			; IOSB needs unsigned count
	INSV	R1,#16,#16,R0		; Save in high-order word of R0

; Space Filemarks exit path (all cases, Forward and Reverse)

140$:	BRW	40$			; Go to common exit and COMPLETE_IO
	.DSABL	LSB

	
	.PAGE
	.SBTTL	READ_POSITION  - read tape record position

; READ_POSITION
;
; Do Read Position command to fill in UCB$L_RECORD
;
; INPUTS:
;
;	R0	- VMS status from last Space command
;	R3	- UCB address
;	R5	- SCDRP address
;
; OUTPUTS:
;
;	R2 - Destroyed
;	All other registers preserved.
; 
READ_POSITION:				
					
        .JSB_ENTRY INPUT=<R0,R3,R5>,SCRATCH=<R2>,PRESERVE=<R0,R1,R6>
	MOVL	R0,R6			; Save status
	MOVAL	CMD_READ_POSITION,R2	; Address of READ POSITION command
	SETUP_CMD			; Perform setup for SCSI command
	SEND_COMMAND_ORDERED		; Send the SCSI command
	BLBC	R0,30$			; Branch on error
	CMPL	SCDRP$L_TRANS_CNT(R5),- ; Sufficient data returned?
		#20			; READ POS data length should equal 20.
	BLSS	30$			; No, declare the command unsupported
	MOVL	SCDRP$L_SVA_USER(R5),R0	; Get address of READ POSITION data
	BBS	#BPU_BIT,-		; If block position unknown,
		(R0),30$		; declare the command unsupported
                                        ; Note: BPU bit is in first data byte
	MOVL	UCB$L_RECORD(R3),R2	; Save current position
	MOVAB	8(R0),R0		; Prepare to update UCB$L_RECORD
					; Note: Block position occupies bytes 
					; 4,5,6,7 (MSB->LSB) in READ POS data
	MOVAB	UCB$L_RECORD(R3),R1     
	.REPT	4                       
	MOVB	-(R0),(R1)+		; Update UCB$L_RECORD
	.ENDR

; Sanity check:  If the skip direction was forward, UCB$L_RECORD should
; increase, otherwise decrease.

	BBS	#UCB$V_REV_SKIP,-	; Is this a reverse skip?
		UCB$L_MK_FLAGS(R3),10$

; Forward skip sanity check

	CMPL	UCB$L_RECORD(R3),R2     ; Is new value > old value ?
	BGTR	20$			; Yes, good, so exit normally.
        BLSS	30$			; It went backward, something is wrong.

; Allow for the possibility that a skip forward was issued while the previous
; position was EOV, so that both old and new values are the same. 

	CMPL    R6,#SS$_ENDOFVOLUME     ; EOV?
	BEQL	20$			; Yes, it's okay to have same position
	BRW	30$			; Else error 

; Reverse skip sanity check

10$:	CMPL	UCB$L_RECORD(R3),R2     ; Is new value < old value ?
	BLSS	20$			; Yes, so this is success

; Allow for the possibility that a skip reverse was issued while the
; previous position was BOT, so that both old and new values = 0.

	BBC	#MT$V_BOT,-		; If new value >= old value, and tape
		UCB$L_DEVDEPEND(R3),30$	; is not at BOT, something is wrong.

; Common exit

20$:	CLEANUP_CMD                     ; Clean up the command
	RSB

; Error path when READ_POSITION fails

30$:	BICL2	#UCB$M_SKIPFILE_SUPPORTED,-; READ POSITION command unsupported
		UCB$L_MK_FLAGS(R3)		
	LOG_ERROR  -
		TYPE=SEND_CMD_ERROR,-
		VMS_STATUS=#SS$_NORMAL,-
		UCB=R3
	BSBW	SET_TAPE_LOST	     	; Set tape lost
	BRW	20$



	.PAGE
	.SBTTL	IO_UNLOAD	- Rewind and unload the tape
;+
; IO_UNLOAD
;
; This routine rewinds amd unloads the drive and clears the VALID bit in the 
; UCB.
;
; INPUTS:
;
;	R2	- IRP address
;	R3	- UCB address
;	R4	- PDT address
;	R5	- SCDRP address
;
; OUTPUTS:
;
;	R0	- Status
;	R1,R2	- Destroyed
;	All other registers preserved
;-

IO_RETENSION:
	.ENABLE LSB

	IF_TZK10	2$              ; If TZK10, do retension
	IF_TZK11	2$              ; If TZK11, do retension
	BRW	11$			
2$:
	MOVAL	CMD_RETENSION,R2	; Get address of Retension command
	BRB	5$			; Continue with CMD set up
	
IO_UNLOAD:
IO_REWINDOFF:
 	BBS	#IO$V_RETENSION,-	; Check to see if the RETENSION bit is
		SCDRP$L_FUNC(R5),-
		IO_RETENSION		; set - if so, branch to IO_RETENSION
	ADDL3   #MAX_UNLOAD_TIME,-      ; Save time at which unload must
		G^EXE$GL_ABSTIM,-       ; be complete
		UCB$L_UNLOAD_TIME(R3)   ;
	BISW    #UCB$M_UNLOAD_INPROG,-  ; Indicate unload may be in pro
		UCB$L_MK_FLAGS(R3)      ; when next QIO is issued
	MOVAL	CMD_UNLOAD,R2		; Get address of unload command
 5$:	SETUP_CMD			; Setup the command
	SEND_COMMAND_ORDERED		; Send the command
	CLEANUP_CMD			; Cleanup from the SCSI command
	BLBC	R0,7$			; Branch on error

; Workaround for the TZK50: the unload command can take up to 20 ms
; to "take", i.e., subsequent commands sent to the drive within 20 ms
; of an unload can operate as though the unload never occurred. For example, 
; a test unit ready command sent immediately after an unload will return 
; success, when it should return unit not ready. This can cause errors 
; during multi-volume backup operations. Therefore, stall for at least a 
; second to allow the unload to do its thing.

	IFNOT_TK50 7$			; Branch if not a TK50
	PUSHL	# SPL$C_IOLOCK8		; Set the required fork lock
	PUSHAQ	TWO_SECONDS		; Set time delay
	PUSHL	SCDRP$PS_KPB(R5)	; Set KPB address
	CALLS	#3,G^EXE$KP_TQE_WAIT	; Fork and wait the desired time
	MOVZWL	#SS$_NORMAL,R0		; Restore success status
7$:	BICL	#UCB$M_VALID,-		; Clear the volume valid bit in
		UCB$L_STS(R3)		; the UCB
	BISL	#<MT$M_BOT>,-		; Mark beginning of tape
 		UCB$L_DEVDEPEND(R3)	;
 	BICL	#<MT$M_LOST!-		; Clear position lost,
 		  MT$M_EOT!-		; end of tape,
 		  MT$M_EOF>,-		; and end of file
 		UCB$L_DEVDEPEND(R3)	;
	CLRL	UCB$L_RECORD(R3)	; Initialize record field
 	MNEGL	#2,UCB$L_PREV_TM(R3)	; No previous tape mark

;	Unload requested, inform the Media Management System

	MOVL    #MME$_UNLOAD, R0    	; load MME event code.
	PUSHL   R5			; save the CDRP address on the stack.
	MOVL    R3, R5			; copy the UCB address into R5.
	JSB	G^MME$$DEV_EVENT	; invoke the MME call-out routine.
	POPL    R5			; restore the CDRP address.

10$:	BRW	COMPLETE_IO		; Complete this I/O function

11$:	MOVZBL	#SS$_ILLIOFUNC,R0	; Not a TZK10, bad func code
	BRB	10$			; for this device 
	.DISABLE LSB

	.PAGE
	.SBTTL	IO_AVAILABLE	- Make drive available (but don't unload it)
;+
; IO_AVAILABLE
;
; This routine rewinds the tape and clears the VALID bit in the UCB. Since 
; this function commonly follows an IO$_UNLOAD function, the drive can 
; potentially be unloaded and therefore unable to accept the rewind command.
; Therefore, if UCB$L_RECORD contains a zero, and MT$M_LOST is not set don't
; perform a rewind.
;
; INPUTS:
;
;	R2	- IRP address
;	R3	- UCB address
;	R4	- PDT address
;	R5	- SCDRP address
;
; OUTPUTS:
;
;	R0	- Status
;	R1,R2	- Destroyed
;	All other registers preserved
;-

IO_AVAILABLE:

	BICL	#UCB$M_VALID,-		; Clear the volume valid bit in
		UCB$L_STS(R3)		; the UCB
	TSTL	UCB$L_RECORD(R3)	; Is tape already at BOT?
	BNEQ	10$			; Branch if not
	BBC	#MT$V_LOST,-		; Branch if tape position not lost, OK
		UCB$L_DEVDEPEND(R3),20$	; to skip rewind function
10$:	MOVAL	CMD_REWIND,R2		; Get address of rewind command
	SETUP_CMD			; Setup the command
	MOVL	SCDRP$L_CMD_PTR(R5),R1	; Get the SCSI command address
	MOVB	#1,4+SCSI_RWND$B_IMMED(R1) ; Set the immediate bit
	SEND_COMMAND_ORDERED		; Send the command
	BLBC	R0,15$			; Branch on error
	ADDL3	#MAX_REWIND_TIME,-	; Save time at which rewind must
		G^EXE$GL_ABSTIM,-	; be complete
		UCB$L_REWIND_TIME(R3)	;
15$:	CLEANUP_CMD			; Cleanup from the SCSI command
20$:	MOVZWL	#SS$_NORMAL,R0		; Set success status
	BISL	#<MT$M_BOT>,-		; Mark beginning of tape
 		UCB$L_DEVDEPEND(R3)	;
 	BICL	#<MT$M_LOST!-		; Clear position lost,
 		  MT$M_EOT!-		; end of tape,
 		  MT$M_EOF>,-		; and end of file
 		UCB$L_DEVDEPEND(R3)	;
	CLRL	UCB$L_RECORD(R3)	; Initialize record field
 	MNEGL	#2,UCB$L_PREV_TM(R3)	; No previous tape mark
	BRW	COMPLETE_IO		; Complete this I/O function
	.PAGE
	.SBTTL	IO_REWIND	- Rewind the tape
;+
; IO_REWIND
;
; This routine rewinds the tape.
;
; INPUTS:
;
;	R2	- IRP address
;	R3	- UCB address
;	R4	- PDT address
;	R5	- SCDRP address
;
; OUTPUTS:
;
;	R0	- Status
;	R1,R2	- Destroyed
;	All other registers preserved
;-

IO_REWIND:
 	BBS	#IO$V_RETENSION,-	; Check to see if the RETENSION bit is
		SCDRP$L_FUNC(R5),25$	; set - if so, branch to IO_RETENSION
	MOVAL	CMD_REWIND,R2		; Get address of rewind command
	SETUP_CMD			; Setup the command
	MOVL	SCDRP$L_CMD_PTR(R5),R1	; Get the SCSI command address
	BBC	#IO$V_NOWAIT,-		; Branch if command should complete
		SCDRP$L_FUNC(R5),10$	; only after tape finishes rewinding
	MOVB	#1,4+SCSI_RWND$B_IMMED(R1) ; Set the immediate bit
	ADDL3	#MAX_REWIND_TIME,-	; Save time at which rewind must
		G^EXE$GL_ABSTIM,-	; be complete
		UCB$L_REWIND_TIME(R3)	;
	BISW	#UCB$M_REWIND_INPROG,-	; Indicate rewind may be in progress
		UCB$L_MK_FLAGS(R3)	; when next QIO is issued
10$:	SEND_COMMAND_ORDERED		; Send the command
	BLBC	R0,20$			; Branch on error
	BISL	#<MT$M_BOT>,-  		; Mark beginning of tape
 		UCB$L_DEVDEPEND(R3)	;
 	BICL	#<MT$M_LOST!-		; Clear position lost,
 		  MT$M_EOT!-		; end of tape,
 		  MT$M_EOF>,-		; and end of file
 		UCB$L_DEVDEPEND(R3)	;
 	CLRL	UCB$L_RECORD(R3)	; Tape is positioned at start
 	MNEGL	#2,UCB$L_PREV_TM(R3)	; No previous tape mark
20$:	CLEANUP_CMD			; Cleanup from the SCSI command
	.if	ndf,norwxx
        tstw    ucb$l_frontpnl(r3)      ; front panel stuff active?
        beql    22$
	bsbw	wait_unit_ready
        bsbw    mode_select             ; select density again since at bot
22$:
	.endc
	BRW	COMPLETE_IO		; Complete this I/O function
25$:	JMP	IO_RETENSION		; BRANCH is out of range, uz JMP,
	.PAGE
	.SBTTL	IO_DIAGNOSE	- Special pass-through function
;+
; IO_DIAGNOSE
;
; INPUTS:
;
;	R2	- IPR address
;	R3	- UCB address
;	R4	- PDT address
;	R5	- SCDRP address
;
; OUTPUTS:
;
;	R0	- Status
;	R1,R2	- Destroyed
;	All other registers preserved
;-

IO_DIAGNOSE:

	PUSHAL	UCB$PS_DIAGNOSE(R3)	; Address of DIAGNOSE area in UCB
	PUSHL	R5                      ; SCDRP address
	PUSHL	SCDRP$L_CDT(R5)		; SCDT address
	PUSHL	R2			; IRP address
	CALLS	#4,DKMK$DIAGNOSE_SIO	; Call common DIAGNOSE routine

; Quadword status is returned in R0.  Get high longword of R0 into R1.

	EVAX_SRL R0,#32,R1              ; Return status in R0/R1
					; X-21 GWW
	BBC	#UCB$V_CANCEL,-		; Branch if I/O has not been canceled
		UCB$L_STS(R5),5$	; Otherwise,
	BRW	COMPLETE_IO		;  handle Cancelled one in the
5$:					;  general IO completion routine
	MOVL	R3,R5			; KP_REQCOM requires UCB addr in R5
	KP_REQCOM

	.PAGE
	.SBTTL	+
	.SBTTL	+ SCSI COMMAND EXECUTION ROUTINES
	.SBTTL	+
	.SBTTL	INQUIRY		- Send an inquiry command
;+
; INQUIRY
;
; This routine sends an inquiry command to the target. The peripheral device
; type field is checked to make sure we do in fact have a SCSI tape device. In
; addition, the device type qualifier is used to determine if the device is a 
; TZK50 or TZ30.
;
; INPUTS:
;
;	R3	- UCB address
;	R4	- PDT address
;	R5	- SCDRP address
;
; OUPUTS:
;
;	R0	- Status
;	R1,R2	- Destroyed
;-

INQUIRY:

	.JSB_ENTRY INPUT=<R3,R4,R5>,OUTPUT=<R0>,SCRATCH=<R1,R2>,-
		   PRESERVE=<R7,R8>
	MOVAL	CMD_INQUIRY,R2		; Address of INQUIRY command
	SETUP_CMD			; Perform setup for SCSI command
	SEND_COMMAND_ORDERED		; Send the SCSI command
	BLBC	R0,30$			; Branch on error
	CMPL	SCDRP$L_TRANS_CNT(R5),#5; Sufficient inquiry data returned?
	BLSSU	17$			; Branch if not
	MOVL	SCDRP$L_SVA_USER(R5),R0	; Get address of inquiry data
	CMPL	SCDRP$L_TRANS_CNT(R5),-	; Revision field valid?
		#36			;
	BLSSU	10$			; Branch if not
	MOVL	32(R0),UCB$L_HW_REV(R3)	; Save hardware revision level
10$:	

	PUSHR	#^M<R0,R1,R2,R3,R4,R5>
	MOVC3	SCDRP$L_TRANS_CNT(R5),-		; Copy standard Inquiry
		(R0),UCB$L_INQUIRY_DATA(R3)     ; data to the UCB
	POPR	#^M<R0,R1,R2,R3,R4,R5>
	ASSUME	UCB$B_DEVCLASS+1 EQ UCB$B_DEVTYPE
	CLRW	UCB$B_DEVCLASS(R3)	; Assume unknown device class and type
	CMPB	SCSI_INQ$B_DEVTYPE(R0),-; Is this a SCSI tape device?
		#SCSI$C_TAPE		;
	BNEQ	40$			; Branch if not, illegal device type
	MOVB	#DC$_TAPE,-		; Indicate that this a tape device
		UCB$B_DEVCLASS(R3)	;
	BBSS	#UCB$V_REMOVABLE,-	; Assume drive has removable media
		UCB$L_MK_FLAGS(R3),15$	;
	ASSUME	SCSI_INQ$B_DEVTYPE+1 EQ SCSI_INQ$B_DEVQUAL 
	ASSUME	SCSI_INQ$V_REMOVABLE EQ 7
15$:	TSTW	(R0)			; Is this a removable media device?
	BLSS	20$			; Branch if so
	BBCC	#UCB$V_REMOVABLE,-	; Clear the removable media bit
		UCB$L_MK_FLAGS(R3),20$	; in the UCB
17$:	BRB	40$			; 

20$:	EXTZV   #0,#3,2(R0),R1	  	; Get ANSI-approved version
	MOVB    R1, UCB$B_SCSI_VERSION(R3) ; Save for future checks
	BEQL	25$			; Does drive conform to SCSI-1?
	CMPB    R1,#1			; Does drive conform to CCS?
	BEQL    25$			; Branch if so
	CMPB    R1,#2			; Does drive conform to SCSI-2?
        BEQL    25$                     ; Yes
	LOG_ERROR -			; Log an invalid inquiry data error
		TYPE=INV_INQUIRY_DATA,-	; as a warning that we got unknown
		VMS_STATUS=#SS$_NORMAL,-; SCSI version - not a fatal error.
		UCB=R3			; 
25$:	BSBW	GET_DEVICE_TYPE		; Determine device type from Vendor
29$:	MOVZWL	#SS$_NORMAL,R0		; Set success status	
30$:	CLEANUP_CMD			; Cleanup from the SCSI command
	RSB

40$:	LOG_ERROR -			; Log an invalid inquiry data error
		TYPE=INV_INQUIRY_DATA,-	;
		VMS_STATUS=#SS$_NORMAL,-;
		UCB=R3			;
	MOVZWL	#SS$_DRVERR,R0		; Set bad status
	BRB	30$			; Use common exit
	.PAGE
	.SBTTL	SKIP_RECORD	- Move n records (in either direction)
;+
; SKIP_RECORD
;
; This routine skips the number of records specified by the signed integer
; in R0. The operation is aborted if a tapemark is encountered.
;
; INPUTS:
;
;	R0	- Signed word containing number of records skip
;       R3	- UCB address
;	R4	- PDT address
;	R5	- SCDRP address
;
; OUTPUTS:
;
;	R0	- Status
;	R1	- Actual number of records skipped
;	UCB$L_RECORD	- Updated
;	R2	- Destroyed
;-

SKIP_RECORD:

	.JSB_ENTRY INPUT=<R0,R3,R4,R5>,OUTPUT=<R0,R1>,SCRATCH=<R2>
	BISB	#UCB$M_SKIP_INPROG,-	; Indicate skip command in
		UCB$L_MK_FLAGS(R3)	; progress
 	BICL	#<MT$M_BOT!-		; Clear beginning of tape
 		  MT$M_EOT!-		; end of tape,
 		  MT$M_EOF>,-		; and end of file
 		UCB$L_DEVDEPEND(R3)	;
	CVTWL	R0,R0			; Change word count to longword count
	BGEQ	5$			; Branch if positive skip count
	BISB	#UCB$M_REV_SKIP,-	; Indicate reverse motion in progress
		UCB$L_MK_FLAGS(R3)	;
5$: 	PUSHL	R0			; Save space count
 	MOVAB	CMD_SPACE,R2		; Get address of SPACE command
	SETUP_CMD			; Setup the command
	ADDL3	#<4+SCSI_SKIP$B_CNT+3>,-; Get address just beyond count field 
		SCDRP$L_CMD_PTR(R5),R1	; in the SCSI command
	MOVL	(SP),R0			; Restore space count (but leave saved
					; value on stack)
	.REPT	2	
	MOVB	R0,-(R1)		; Copy two bytes of space count
	ASHL	#-8,R0,R0		; to the SCSI command
	.ENDR
	MOVB	R0,-(R1)		; Copy the third byte
	CLRL	UCB$L_ADDNL_INFO(R3)	; Initialize additional info field
	SEND_COMMAND_ORDERED		; Send the SCSI command
	CLEANUP_CMD			; Cleanup from the SCSI command
	POPL	R1			; Restore space count
	BLBC	R0,20$			; Branch on error
10$:	
	BBS	#UCB$V_NO_POSITION_UPDATE,- ; It is not appropriate to update
		UCB$L_MK_FLAGS(R3),15$  ; position now because IO_SKIP_FILE
					; will
	ADDL	R1,UCB$L_RECORD(R3)	; Update record number in UCB
	BGEQ	15$			; Branch if non-negative record value
	BSBW	SET_TAPE_LOST		; Otherwise, assume we are lost
15$:	BICB	#<UCB$M_REV_SKIP!-	; Reverse motion no longer in progress
		UCB$M_SKIP_INPROG>,-	; Indicate skip command no
		UCB$L_MK_FLAGS(R3)	; longer in progress
	RSB				; Return to caller

; Perform a sanity check on the additional info (residue) value returned by
; the tape. If the tape told us it skipped more records than we asked for
; then set tape position lost. Note that the residue field, like the skip
; count, is a signed longword. Therefore, in order to do the comparison,
; we have to distinguish between cases of skipping forward vs. reverse.

20$:	MOVL	UCB$L_ADDNL_INFO(R3),R2	; Get number of unskipped records (residue)
	BBC	#UCB$V_REV_SKIP,-	; Branch if original skip count was
		UCB$L_MK_FLAGS(R3),25$	; positive (skipping forward)
	MNEGL	R1,R1			; Invert sign of requested skip count
	MNEGL	R2,R2			; Invert sign of residue
	BGEQ	25$			; Branch if residue was negative
	MNEGL	R2,R2			; Otherwise, reinvert sign of residue
	MNEGL	R2,UCB$L_ADDNL_INFO(R3)	; Save residue value as a negative

25$:	CMPL	R2,R1			; Compare requested skip count with residue
	BLEQU	27$			; Branch if residue is reasonable
	BSBW	SET_TAPE_LOST		; Otherwise, assume we are lost
	BRB	15$			; Use common exit

27$:	BBC	#UCB$V_REV_SKIP,-	; Branch if original skip count was
		UCB$L_MK_FLAGS(R3),29$	; positive (skipping forward)
	MNEGL	R1,R1			; Otherwise, reinvert sign of requested skip count
29$:	SUBL3	UCB$L_ADDNL_INFO(R3),-	; Get the actual number of records
		R1,R2			; skipped

; If a tapemark was encountered, the actual skip count must be adjusted. This
; accounts for the fact that tape drives do not consider tapemarks to be 
; records, but the software does (i.e., the UCB$L_RECORD field includes both
; normal records AND tapemarks).

	CMPL	R0,#SS$_ENDOFFILE	; Tape mark encountered?
	BNEQ	30$			; Branch if not
	INCL	R2			; Assume forward skipping 
	TSTL	R1			; Was original skip count positive?
	BGEQ	30$			; Branch if so
	SUBL	#2,R2			; Reverse skipping, fix up skip count
30$:	MOVL	R2,R1			; Return actual space count in R1
	BRB	10$			; Use common exit
	.PAGE

	.SBTTL	SKIP_FILE - Move n filemarks (in either direction)
;+
; SKIP_FILE
;
; This routine skips the number of filemarks specified by the signed integer
; in R0. 
;
; INPUTS:
;
;	R0	- Signed word containing number of filemarks to skip
;       R3	- UCB address
;	R4	- PDT address
;	R5	- SCDRP address
;
; OUTPUTS:
;
;	R0	- Status
;	R1	- Actual number of filemarks skipped
;	R2	- Destroyed
;-

SKIP_FILE:

	.JSB_ENTRY INPUT=<R0,R3,R4,R5>,OUTPUT=<R0,R1>,SCRATCH=<R2>
	BISL	#<UCB$M_SKIP_INPROG!-   ; Indicate skip command in progress
		UCB$M_SPACEFILE_INPROG>,-
		UCB$L_MK_FLAGS(R3)	; 
 	BICL	#<MT$M_BOT!-		; Clear beginning of tape
 		  MT$M_EOT!-		; end of tape,
 		  MT$M_EOF>,-		; and end of file
 		UCB$L_DEVDEPEND(R3)	;
	CVTWL	R0,R0			; Change word count to longword count
	BGEQ	10$			; Positive or negative space count?
	BISB	#UCB$M_REV_SKIP,-	; Indicate reverse motion in progress
		UCB$L_MK_FLAGS(R3)	;
10$: 	PUSHL	R0			; Save space count
 	MOVAB	CMD_SPACE,R2		; Get address of SPACE command
	SETUP_CMD			; Setup the command
	ADDL3	#<4+SCSI_SKIP$B_CNT+3>,-; Get address just beyond count field 
		SCDRP$L_CMD_PTR(R5),R1	; in the SCSI command
	MOVL	(SP),R0			; Restore space count (but leave saved
					; value on stack)
	.REPT	2	
	MOVB	R0,-(R1)		; Copy two bytes of space count
	ASHL	#-8,R0,R0		; to the SCSI command
	.ENDR
	MOVB	R0,-(R1)		; Copy the third byte
	MOVB	#1,-(R1)		; Filemarks code (from SCSI spec)
	CLRL	UCB$L_ADDNL_INFO(R3)	; Initialize additional info field
	SEND_COMMAND_ORDERED		; Send the SCSI command
	CLEANUP_CMD			; Cleanup from the SCSI command
	POPL	R1			; Restore space count
	BLBC	R0,20$			; Branch on error
	BITL	#<MT$M_BOT!MT$M_EOT>,-	; Are we at either BOT or EOT?
		UCB$l_DEVDEPEND(R3)
	BNEQ	15$			; Yes, so the correct DEVDEPEND bit was
					; set already in TRANS_SENSE_KEY as
					; the Check Condition was processed.
	BISL	#MT$M_EOF,-		; Else, there was a success with no
		UCB$L_DEVDEPEND(R3)	; Check Condition, so the correct
					; DEVDEPEND bit must be set now.
15$:	BICL	#<UCB$M_SKIP_INPROG!-   ; Indicate skip command no longer in
		UCB$M_SPACEFILE_INPROG!- ; progress
		UCB$M_REV_SKIP>,-
		UCB$L_MK_FLAGS(R3)	; 
	RSB				; Return to caller

; Perform a sanity check on the additional info (residue) value returned by
; the tape. If the tape told us it skipped more files than we asked for
; then set tape position lost. Note that the residue field, like the skip
; count, is a signed longword. Therefore, in order to do the comparison,
; we have to distinguish between cases of skipping forward vs. reverse.

20$:	MOVL	UCB$L_ADDNL_INFO(R3),R2	; Get number of unskipped files (residue)
	BBC	#UCB$V_REV_SKIP,-	; Branch if original skip count was
		UCB$L_MK_FLAGS(R3),25$	; positive (skipping forward)
	MNEGL	R1,R1			; Invert sign of requested skip count
	MNEGL	R2,R2			; Invert sign of residue
	BGEQ	25$			; Branch if residue was negative
	MNEGL	R2,R2			; Otherwise, reinvert sign of residue
	MNEGL	R2,UCB$L_ADDNL_INFO(R3)	; Save residue value as a negative

25$:	CMPL	R2,R1			; Compare requested skip count with residue
	BLEQU	27$			; Branch if residue is reasonable
	BSBW	SET_TAPE_LOST		; Otherwise, assume we are lost
	BRB	15$			; Use common exit

27$:	BBC	#UCB$V_REV_SKIP,-	; Branch if original skip count was
		UCB$L_MK_FLAGS(R3),29$	; positive (skipping forward)
	MNEGL	R1,R1			; Otherwise, reinvert sign of requested skip count
29$:	SUBL2	UCB$L_ADDNL_INFO(R3),-	; Get the actual number of files
		R1			; skipped
	BRB	15$			; Use common exit
	.PAGE
	.SBTTL	RECEIVE_DIAG	- Receive diagnostic results
;+
; RECEIVE_DIAG
;
; This routine sends a RECEIVE DIAGNOSTIC RESULTS command to the tape
; to obtain the controller hardware and software revision levels for 
; TZ30s and TK50s. The revision info is translated from hex to text and
; saved in the UCB. All other drives return revision information their
; inquiry data, and it is already in text format.
;
; INPUTS:
;
;	R3	- UCB address
;	R4	- PDT address
;	R5	- SCDRP address
;
; OUPUTS:
;
;	R0	- Status
;	R1,R2	- Destroyed
;-

	DRIVER_DATA	
HEX_TO_TXT:	.ASCII	/0123456789ABCDEF/

	DRIVER_CODE
RECEIVE_DIAG:

	.JSB_ENTRY INPUT=<R3,R4,R5>,OUTPUT=<R0>,SCRATCH=<R1,R2>,-
		   PRESERVE=<R3,R7,R8>
	IF_TK	5$			; Branch if device is a TZK50 or TZ30
	MOVZWL	#SS$_NORMAL,R0		; Otherwise, this routine is a NOP
	RSB				; Return to caller

5$:	MOVAL	CMD_RECEIVE_DIAG,R2	; Address of RECEIVE_DIAG command
	SETUP_CMD			; Perform setup for SCSI command
	MOVZWL	#60,-			; Set up command-specific DMA timeout
		SCDRP$L_DMA_TIMEOUT(R5)
	SEND_COMMAND_ORDERED		; Send the SCSI command
	BLBC	R0,10$			; Branch on error
	CMPL	SCDRP$L_TRANS_CNT(R5),#2; Sufficient diag data returned?
	BLSSU	20$			; Branch if not
	MOVL	SCDRP$L_SVA_USER(R5),R0	; Get address of received diag data
	ASSUME	SCSI_RCVD$B_HW_REV+1 EQ SCSI_RCVD$B_SW_REV
	MOVAL	UCB$L_HW_REV(R3),R3	; Get address of HW rev field in UCB
	MOVL	#12,R1			; Prepare to translate HW rev field
7$:	EXTZV	R1,#4,(R0),R2		; Get a nibble of HW revision
	MOVB	HEX_TO_TXT[R2],(R3)+	; Translate to text and save in UCB
	SUBL	#4,R1			; Prepare to extract next nibble
	BGEQ	7$			; Repeat for entire REV field
	MOVZWL	#SS$_NORMAL,R0		; Set success status
10$:	CLEANUP_CMD			; Cleanup from the SCSI command
	RSB

20$:	LOG_ERROR -			; Log an invalid diagnostic data error
		TYPE=DIAGNOSTIC_DATA,-	;
		VMS_STATUS=#SS$_NORMAL,-;
		UCB=R3			;
	MOVZWL	#SS$_DRVERR,R0		; Set bad status
	BRB	10$			; Use common exit
	.PAGE
	.SBTTL	REQUEST_SENSE	- Send a request sense command
;+
; REQUEST_SENSE
;
; This routine is called by SEND_COMMAND when a command fails with check 
; condition status. A REQEST SENSE command is sent to the target.
;
; INPUTS:
;
;	R3	- UCB address
;	R4	- PDT address
;	R5	- SCDRP address
;
; OUTPUTS:
;
;	R0	- Status
;	R1,R2	- Destroyed
;	All other registers preserved
;-

REQUEST_SENSE:

	.JSB_ENTRY INPUT=<R3,R4,R5>,OUTPUT=<R0>,SCRATCH=<R1,R2>,-
		   PRESERVE=<R7,R8>
	MOVAL	CMD_REQUEST_SENSE,R2	; Address of REQUEST_SENSE command
	SETUP_CMD			; Perform setup for SCSI command
	SPI$SEND_COMMAND		; Send the SCSI command
	BLBC	R0,30$			; Branch on SPI$SEND_COMMAND error
	ASSUME	SCSI$C_GOOD_STATUS EQ 0
	MOVZBL	@SCDRP$L_STS_PTR(R5),R1	; Get SCSI status byte
	BICB	#SCSI$M_STAT_MASK,R1	; Clear reserved, vendor unique bits
	BNEQ	20$			; Branch if bad status
10$:	RSB				; Return to caller

20$:	MOVL	#SS$_MEDOFL,R0		; Set bad status
30$:	LOG_ERROR -			; Log a send command error
		TYPE=SEND_CMD_ERROR,-	;
		VMS_STATUS=R0,-		;
		UCB=R3			;
	BRB	10$			; Clean up and return to caller
	.PAGE
	.SBTTL	TEST_UNIT_READY	- Send a test unit ready command
;+
; TEST_UNIT_READY
;
; This routine sends a TEST UNIT READY command to the tape to determine
; whether the device is ready to receive read, write, and tape motion commands.
;
; INPUTS:
;
;	R3	- UCB address
;	R4	- PDT address
;	R5	- SCDRP address
;
; OUTPUTS:
;
;	R0	- Status
;	R1,R2	- Destroyed
;	All other registers preserved
;-

TEST_UNIT_READY:

	.JSB_ENTRY INPUT=<R3,R4,R5>,OUTPUT=<R0>,SCRATCH=<R1,R2>
	BISB	#UCB$M_WAITRDY_INPROG,-	; Set wait for unit ready in progress
		UCB$L_MK_FLAGS(R3)	; 
	MOVAL	CMD_TEST_UNIT_READY,R2	; Address of TEST UNIT READY command
	SETUP_CMD			; Perform setup for SCSI command
	SEND_COMMAND_ORDERED		; Send the SCSI command
	CLEANUP_CMD			; Cleanup from the SCSI command
	BICB	#UCB$M_WAITRDY_INPROG,-	; Clear wait for unit ready in progress
		UCB$L_MK_FLAGS(R3)	; 
	RSB
	.PAGE
	.SBTTL	MODE_SENSE	- Send a mode sense command
;+
; MODE_SENSE
;
; This routine sends a MODE SENSE command. The only mode sense information 
; currently used is the write protect bit (bit 7 of the device-specific param)
; and (if it is available) the density from the first byte of the first block
; descriptor.
;
; INPUTS:
;
;	R3	- UCB address
;	R4	- PDT address
;	R5	- SCDRP address
;
; OUPUTS:
;
;	R0	- Status
;	R1,R2	- Destroyed
;-

MODE_SENSE:

	.JSB_ENTRY INPUT=<R3,R4,R5>,OUTPUT=<R0>,SCRATCH=<R1,R2>,PRESERVE=<R7,R9>

; Allocate a local copy of the mode page descriptors on the stack

	SUBL3	#DESCRIPTOR_SIZE,SP,R7		; Size of descriptor array 
	BICL	#7,R7				; Quadword align
	SUBL	#DESCRIPTOR_SIZE+8,SP		; Account for desc + unalignment
	PUSHR	#^M<R3,R4,R5>
	MOVC5	#0,.,#0,#DESCRIPTOR_SIZE,(R7)	; Initialize the desc area
	MOVC3	#DESCRIPTOR_SIZE,-		; Copy the descriptors
		DESCRIPTOR_BASE,(R7)	
	POPR	#^M<R3,R4,R5>

	PUSHL	#0				; Vendor specific page code
        PUSHAL	VENDOR_UNIQUE_PAGE_DESC(R7)	; Address of descriptors
	PUSHL	R5				; SCDRP Address
	CALLS	#3,DO_MODE_PAGE			; Process the page
; if we failed and front panel in effect, do not fail here.
	blbs	R0,18$
	tstw    ucb$l_frontpnl(r3)      ;frontpanel
	beql	21$
18$:
	BLBC	R0,50$				; Branch on error
	
	BICL	#MT$M_HWL,-			; Assume tape is not hardware write 
		UCB$L_DEVDEPEND(R3)		; locked
        MOVL	#VENDOR_UNIQUE_DEVSPC_DESC,R9	; Offset to dev specific param
	ADDL2	R7,R9				; Get address of dev specific descriptor
	BBC	#7,MODE_DESC$L_ACTUAL(R9),20$	; Is write-protect bit clear?
	BISL	#MT$M_HWL,-			; Indicate that tape is in fact hardware
		UCB$L_DEVDEPEND(R3)		; write locked
20$:
	tstw    ucb$l_frontpnl(r3)      ;frontpanel
	beql	22$
21$:	movw	ucb$l_frontpnl(r3),UCB$W_MK_DENSITY(R3)
	insv	ucb$l_frontpnl(r3),-
		#MT$V_DENSITY,-                 ; Store density in device
                #MT$S_DENSITY,-                 ; characteristics
                UCB$L_DEVDEPEND(R3)             ;
	brb	30$
22$:	
	IFNOT_TSZ07	30$			; Branch if not TSZ07
        MOVL	#VENDOR_UNIQUE_BDLEN_DESC,R9	; Offset to block desc length
	ADDL2	R7,R9				; Get address of blk desc len
	BBC	#3,-				; Check for a block descriptor,
		MODE_DESC$L_ACTUAL(R9),-	; if none, leave
		30$  
	CLRL	R1				; Clear in case default (0) density
        MOVL	#VENDOR_UNIQUE_DENS_DESC,R9	; Offset to block descriptor
	ADDL2	R7,R9				; Get address of block desc
	MOVB	MODE_DESC$L_ACTUAL(R9),-	; Get the current device density
		UCB$W_MK_DENSITY(R3)		; and save it
	BEQL	25$				; If default (0), done
	ADDW3	#2,UCB$W_MK_DENSITY(R3),-	; Translate to VMS density
		R1 				;
25$:	INSV	R1,#MT$V_DENSITY,-		; Store density in device 
		#MT$S_DENSITY,-			; characteristics
		UCB$L_DEVDEPEND(R3)		;
30$:	MOVZWL	#SS$_NORMAL,R0			; Set success status
40$:	
	ADDL	#DESCRIPTOR_SIZE+8,SP		; Account for desc + unalignment
	RSB

50$:	

; Since DO_MODE_PAGE overwrote the SEND_COMMAND return status with SS$_NODATA
; whenever a bad SCSI status was returned, restore the SEND_COMMAND status
; for that case.  Note:  DO_MODE_PAGE propagates the SEND_COMMAND status
; for all other cases.

	CMPL	#SS$_NODATA,R0			; Do we need to restore status?
	BNEQ	40$                             ; No, just exit
	MOVL	UCB$L_SAV_SEND_STATUS(R3),R0	; Restore status 
	BRW	40$
	.PAGE
	.SBTTL	MODE_SELECT	- Send a mode select command
;+
; MODE_SELECT
;
; This routine sends a MODE_SELECT command to the tape to set up various drive
; characteristics including default block size, buffered (streaming) mode, 
; number of fillers, and infinite reselection timeout.
;
; This command is different from others in that each device has its own
; vendor-unique data. At unit init, when the device type was determined, a
; pointer to this vendor-unique mode select data was placed in the UCB. This
; data is appended to the standard end of the standard mode select data. 
;
; Rather than have a unique SCSI_CMD table entry for device type, we have 
; a template entry containing the generic mode select data, and a seond entry
; used to actually build the mode select command. The template entry is first
; copied to the active entry, then the BCNT and parameter list length fields
; are increased by the length of the vendor-unique info length.
;
; INPUTS:
;
;	R3	- UCB address
;	R4	- PDT address
;	R5	- SCDRP address
;
; OUPUTS:
;
;	R0	- Status
;	R1,R2	- Destroyed
;-

MODE_SELECT:

	.JSB_ENTRY INPUT=<R3,R4,R5>,OUTPUT=<R0>,SCRATCH=<R1,R2>,-
		PRESERVE=<R7,R8>

	MOVAL	CMD_MODE_SELECT_TMP,R1	; Get the mode select command template
	MOVAL	CMD_MODE_SELECT,R2	; Get the mode select command
	PUSHR	#^M<R2,R3,R4,R5>	; Save regs
	MOVC3	#MODE_SEL_TMP_LEN,-	; Copy the mode select info from the
		(R1),(R2)		; template to the active command
	POPR	#^M<R2,R3,R4,R5>	; Restore regs
	MOVL	UCB$L_MSEL_INFO(R3),R1	; Get address of vendor-unique info
	MOVZBL	(R1)+,R0		; Get length of vendor-unique info
	ADDB	R0,5(R2)		; Increase parameter list length in cmd 
					; by vendor-unique length	
	ADDW	R0,9(R2)		; Increase BCNT by vendor-unique length	

	SUBL3	#DESCRIPTOR_SIZE,SP,R7	; Size of descriptor array 
	BICL	#7,R7			; Quadword align
	SUBL	#DESCRIPTOR_SIZE+8,SP	; Account for desc + unalignment
	PUSHR	#^M<R3,R4,R5>
	MOVC5	#0,.,#0,-
		#DESCRIPTOR_SIZE,(R7)	; Initialize the desc area
	MOVC3	#DESCRIPTOR_SIZE,-	; Copy the descriptors
		DESCRIPTOR_BASE,(R7)	
	POPR	#^M<R3,R4,R5>

; Assume no vendor unique info

        MOVAL	BASIC_SELECT_DESC(R7),-
		R8 			; Address of descriptors
15$:	MOVL	UCB$L_MSEL_INFO(R3),R2	; Get address of vendor-unique info
	MOVZBL	(R2)+,R0		; Get length of vendor unique info
	tstw	ucb$l_frontpnl(r3)      ; front panel active?
	beql	16$
; Front panel dens active
	movw	ucb$l_frontpnl(r3),UCB$W_MK_DENSITY(R3)
	ADDL3 	-			; Get address of density descriptor
		#VENDOR_UNIQUE_SEL_DENS_DESC,-
		R7,R1
	MOVB	UCB$W_MK_DENSITY(R3),-	; Set desired density in descriptor 
		MODE_DESC$L_DESIRED(R1)
16$:
	TSTL	R0
	BNEQ	18$			; If nonzero, go get it

; If here, then there is no vendor unique info.  Set density if it's a TSZ07.

	IFNOT_TSZ07 25$			; If not a TSZ07, join common code
	ADDL3 	-			; Get address of density descriptor
		#VENDOR_UNIQUE_SEL_DENS_DESC,-
		R7,R1
	MOVB	UCB$W_MK_DENSITY(R3),-	; Set desired density in descriptor 
		MODE_DESC$L_DESIRED(R1)
        BRW	25$			; Join common code to do Mode Select


; Yes, there is vendor unique info

18$:	MOVAL	<VENDOR_UNIQUE_SEL_DESC+MODE_DESC$L_BIT_SIZE>(R7),R1
	MULL3	#8,R0,(R1)		; Get bit size of vendor info
        MOVAL	VENDOR_UNIQUE_SEL_DESC(R7),R8 ; Address of descriptors
	MOVAL	<VENDOR_UNIQUE_SEL_DESC+MODE_DESC$L_DESIRED>(R7),R1
20$:	MOVB	(R2)+,(R1)+		; Copy a byte of vendor-unique data
	SOBGTR	R0,20$			; Repeat for all vendor-unique data

; Common code

25$:	PUSHL	#0			; Vendor specific page code
        PUSHL	R8			; Address of descriptors
	PUSHL	R5			; SCDRP Address
	CALLS	#3,DO_MODE_PAGE		; Process the page
	BLBS	R0,30$                  ; 
	MOVL	#SS$_DRVERR,R0          ; Return error 
30$:	
	ADDL	#DESCRIPTOR_SIZE+8,SP	; Account for desc + unalignment
	RSB
	.PAGE
	.SBTTL	DO_8MM_COPY_CHK	- 8mm COPY from BOT processing
;+
; DO_8MM_COPY_CHK 
;
; This routine is called from IO_WRITPBLK as a result of a SCSI error
; status returned in response to an attempt to begin a $COPY operation
; to a tape positioned at BOT and containing no files ($INIT done but
; tape void of files).
;
; During $COPY from BOT processing, the positioning command SPACE RECORD
; REVERSE is used to position the tape after the "VOL1..." record (1) and
; is followed by a WRITE command to write the "HDR1..." record (2). This is
; an illegal command sequence on the 8mm devices and results in CHECK 
; CONDITION - ILLEGAL REQUEST status (SS$_DRVERR).
;
; To accommodate the 8mm behavior this routine creates an additional
; SCDRP and uses it and an S0 buffer to generate a REWIND, READ rec 1
; ("VOL1..."), REWIND, WRITE rec 1 ("VOL1...") sequence to accomplish
; correct positioning for the write that follows the "VOL1..." record (1).
;
;
; INPUTS:
;
;	R3	- UCB address
;	R4	- PDT address
;	R5	- SCDRP address
;
; OUTPUTS:
;
;	R0	- Status
;	R1, R2	- Destroyed
;	All other registers preserved
;-
	
DO_8MM_COPY_CHK:

	.JSB_ENTRY INPUT=<R3,R4,R5>,OUTPUT=<R0>,SCRATCH=<R1,R2>
	MOVL	R5,UCB$L_SCDRP_SAV1(R3)	; Save original SCDRP address
	MOVL	SCDRP$PS_KPB(R5),-	; Save original KPB address
		UCB$L_KPB_SAV1(R3)
	MK_ALLOC_SCDRP			; Allocate additional SCDRP
	MOVL	UCB$L_KPB_SAV1(R3),-	; Store KPB address in new SCDRP
		SCDRP$PS_KPB(R5)
;
;	This seems to be the COPY from BOT situation...REWIND and
;	READ the first record to determine if it is "VOL1...".
;
	BSBW	DO_8MM_REWIND		; Do 1st Rewind
	BLBS	R0,20$			; Br if ok
	BRW	70$			; Error status -- return
20$:	MOVL	SCDRP$L_CMD_BUF(R5),R0	; Get address of command buffer
	SPI$CMD_BUFFER_DEALLOC	; Dealloc command buffer
	MOVAL	CMD_READ_8MM,R2		; Address of 8mm READ command
	SETUP_CMD			; Perform setup for SCSI command
	ADDL3	#<4+SCSI_RD$B_LEN+3>,-	; Get address just beyond transfer 
		SCDRP$L_CMD_PTR(R5),R0	; length field in the SCSI command
	MOVAL	SCDRP$L_BCNT(R5),R1	; Get addr of byte count field in SCDRP
	.REPT	3
	MOVB	(R1)+,-(R0)		; Fill in the transfer length
	.ENDR
	MOVL	#IRP$M_FUNC,-		; Set the FUNC bit to indicate a READ
		SCDRP$IS_STS(R5)
	SEND_COMMAND_ORDERED		; Send the SCSI command
	BLBS	R0,25$			; Br if ok
	BRW	70$			; Error status -- return
25$:	CMPL	@SCDRP$L_SVA_USER(R5),-	; Is this the "VOL1..." record
		#^A/VOL1/
	BEQL	30$			; Br if it is
	BRW	70$			; It isn't -- just return
30$:	
	SPI$BUFFER_UNMAP		; Un-map data buffer (but don't
					; deallocate data buffer)
	MOVL	SCDRP$L_CMD_BUF(R5),R0	; Get address of command buffer
	SPI$CMD_BUFFER_DEALLOC	; Dealloc command buffer 
	INCL	UCB$L_RECORD(R3)	; Keep track of forward tape position
;
;	Rewind again and WRITE "VOL1..."
;
	PUSHL	SCDRP$L_BCNT(R5)	; Save the byte count for use by WRITE
	BSBW	DO_8MM_REWIND		; Do 2nd Rewind
	BLBS	R0,40$			; Br if error
	POPL	SCDRP$L_BCNT(R5)	; Restore the byte count
	BRB	70$
40$:	MOVL	SCDRP$L_CMD_BUF(R5),R0	; Get address of command buffer
	SPI$CMD_BUFFER_DEALLOC	; Dealloc command buffer
        tstw    ucb$l_frontpnl(r3)      ; front panel stuff active?
        beql    42$
	bsbw	wait_unit_ready
        bsbw    mode_select             ; select density again since at bot
42$:
;
;	The SCDRP still points to the S0 buffer containing the "VOL1..."
;	record that was READ above, so WRITE it back to tape. Once this is
;	done, subsequent WRITES will complete successfully.
;
	MOVAL	CMD_WRITE_8MM,R2	; Address of 8mm WRITE command
	SETUP_CMD			; Perform setup for SCSI command
	POPL	SCDRP$L_BCNT(R5)	; Restore the byte count
	SPI$BUFFER_MAP PRIO=HIGH	; Re-map same user buffer used in READ
	ADDL3	#<4+SCSI_WRT$B_LEN+3>,-	; Get address just beyond transfer
		SCDRP$L_CMD_PTR(R5),R0	; length field in the SCSI command
	MOVAL	SCDRP$L_BCNT(R5),R1	; Get addr of byte count field in SCDRP
	.REPT	3
	MOVB	(R1)+,-(R0)		; Fill in the transfer length
	.ENDR
	CLRL	SCDRP$IS_STS(R5)	; Clr the FUNC bit to indicate a WRITE
	SEND_COMMAND_ORDERED		; Send the SCSI command
	BLBS	R0,80$			; Br if no error
70$:	BSBW	SET_TAPE_LOST		; Set position lost
	LOG_ERROR -			; Log an extended sense data error
		TYPE=EXTND_SENSE_DATA,-	;
		VMS_STATUS=#SS$_NORMAL,-;
		UCB=R3			;
	MOVZWL	#SS$_DRVERR,R0		; Return error status
80$:
	INCL	UCB$L_RECORD(R3)	; Keep track of forward tape position
	CLEANUP_CMD			; Cleanup from the SCSI command
	MK_DEACTIVATE_SCDRP -		; Deallocate the request sense SCDRP
		CLRSTK=YES
	MOVL	UCB$L_SCDRP_SAV1(R3),R5	; Restore original SCDRP
	MOVL	R5,UCB$L_SCDRP(R3)	; copy to UCB
90$:
	CLRL	UCB$L_8MM_CHK(R3)	; Clear COPY check in progress flag
	RSB

	.PAGE
	.SBTTL	DO_8MM_REWIND	- Rewind the 8MM device
;+
; This subroutine is called from DO_8MM_COPY_CHK to send a rewind command to
; the device.
;
; INPUTS:
;
;	R3	- UCB address
;	R4	- PDT address
;	R5	- SCDRP address
;
; OUTPUTS:
;
;	R0	- Status
;	R1, R2	- Destroyed
;	All other registers preserved
;-

DO_8MM_REWIND:

	.JSB_ENTRY INPUT=<R3,R4,R5>,OUTPUT=<R0>,SCRATCH=<R1,R2>
	MOVAL	CMD_REWIND,R2		; Address of REWIND command
	SETUP_CMD			; Setup the command
	SEND_COMMAND_ORDERED		; Send the command
	BLBC	R0,20$			; Br if error
	BISL	#<MT$M_BOT>,-		; Mark the beginning of tape
		UCB$L_DEVDEPEND(R3)	;
	BICW	#<MT$M_LOST!-		; Clear position lost,
		  MT$M_EOT!-		; end of tape,
		  MT$M_EOF@-16>,-	; and end of file
		UCB$L_DEVDEPEND+2(R3)	;
	CLRL	UCB$L_RECORD(R3)	; Tape is positioned at start
	MNEGL	#2,UCB$L_PREV_TM(R3)	; No previous tape mark
20$:	RSB

	.PAGE
	.SBTTL	+
	.SBTTL	+ UTILITY ROUTINES  
	.SBTTL	+
	.SBTTL	SET_UNIT_ONLINE	- Issue SCSI commands to bring unit online
;+
; SET_UNIT_ONLINE
;
; This routine attempts to bring a unit online. First, INQUIRY and RECEIVE
; DIAGNOSTIC RESULTS commands are sent to the drive to ensure that it can 
; communicate, that it's a valid tape-like device, and that it's up to rev. 
; A check is also done to see if the unit supports data compaction.
; If a failure occurs, a fork thread is set up to periodically poll the drive. 
;
; INPUTS:
;
;	R5	- UCB address
;
; OUTPUTS:
;
;	R0-R5	- Destroyed
;
;	ONLINE bit set in the UCB, BSY bit cleared
;-

SET_UNIT_ONLINE:


	.JSB_ENTRY INPUT=<R5>,SCRATCH=<R0,R1,R2,R3,R4,R5>
10$:	MOVL	UCB$L_PDT(R5),R4	; Get PDT address
	MOVL	R5,R3			; Copy UCB address
	MK_ALLOC_SCDRP			; Allocate an SCDRP
	MOVL	UCB$PS_UNITINIT_KPB(R3),- ; Save KPB address in SCDRP
		SCDRP$PS_KPB(R5)
	BISL2	#<UCB$M_SKIPFILE_SUPPORTED!-  ; Assume
		UCB$M_ALLOWFAST_PER_IO!-      ; skipfile supported and enabled
		UCB$M_FIRST_READPOS_CMD>,-    ; and indicate
		UCB$L_MK_FLAGS(R3)	      ; initial READ POSITION pending
	BSBW	INQUIRY			; Send an inquiry command
	BLBC	R0,15$			; Branch on error
	BSBW	TEST_UNIT_READY		; Clear out a possible pending UNIT 
					; ATTENTION sense key
	BSBW	RECEIVE_DIAG		; Send a receive diag data command
	BLBC	R0,15$			; Branch on error
	BSBW	CHECK_COMPACTION_SUPPORT ; check if device supports data compaction
	BSBW	CHECK_REV_LEVEL		; Check for minimum rev level
	MOVZWL	#SS$_NORMAL,R0		; Ignore revision check failure (bring
					; drive online regardless of rev)
15$:	MK_DEACTIVATE_SCDRP CLRSTK=YES  ; Deallocate the SCDRP
	MOVL	R3,R5			; Copy UCB address
	BLBC	R0,30$			; Branch if error
	BISL	#UCB$M_ONLINE!-		; Set the device online, busy
		 UCB$M_BSY,-		;
		UCB$L_STS(R5)		;

; Since we don't know where the tape is at this point, initialize state in the
; UCB to indicate that position is lost. Normally the tape position is at BOT
; when the driver is loaded. However, it's possible that if we're booting
; standalone backup from tape, the position could be at some random point in
; the tape. Therefore, place it safe and assume we're lost.

	BSBW	SET_TAPE_LOST		; Indicate we don't know where tape is

	.IF DEFINED DEBUG
	MOVL	#SS$_NORMAL,R0		; Set normal status
	BSBW	TRACE_QIO_STAT		; Save the final QIO status in trace buf
	.ENDC

	REMQUE	@UCB$L_IOQFL(R5),R3	; Any I/O queued to this device?
	BVS	20$			; Branch if not	
	CALL_INITIATE		        ; Go initiate the I/O
        RSB

20$:	BICL	#UCB$M_BSY,-		; Clear the busy bit
		UCB$L_STS(R5)		;
	RSB				; Return to caller

; We've tried and failed at least once to send an inquiry command. Clear the
; online and busy bits and set up a fork thread to poll the device once 
; a minute in an attempt to bring it online. In the meantime, any I/O queued to
; this device will fail with device offline status. Flush the queue of any I/O
; that might have been queued to the device during the period that it was
; online but busy. 

30$:	BICL	#UCB$M_ONLINE,-		; Set the unit offline, but leave device
		UCB$L_STS(R5)		; busy since polling is in progress
40$:	REMQUE	@UCB$L_IOQFL(R5),R3	; Remove an IRP from the queue
	BVS	45$			; Branch if queue was empty
	INSQUE	(R3),-			; Place on end of flush queue
		@UCB$L_FLUSH_IOQBL(R5)	; 
	BRB	40$			; Repeat until I/O queue is empty
45$:	REMQUE	@UCB$L_FLUSH_IOQFL(R5),-; Remove an IRP from the flush queue
		R3			;
	BVS	50$			; Branch if queue was empty
	MOVL	R3,UCB$L_IRP(R5)	; Copy IRP addess to UCB
	MOVL	#SS$_DEVOFFLINE,R0	; Set device offline status
	CALL_REQCOM			; Complete the QIO
	BRB	45$			; Continue to flush queue

50$:	
	PUSHL	UCB$B_FLCK(R5)			;F FF; Set the required fork lock
	PUSHAQ	SIXTY_SECONDS			;F FF; Set the time delay in ticks
	PUSHL	UCB$PS_UNITINIT_KPB(R5)		;F FF; Set the KPB address
	CALLS	# 3, G^ EXE$KP_TQE_WAIT		;F FF; Fork and wait the desired time
	BRW	10$			; And try again
	.PAGE
	.SBTTL	SET_TAPE_LOST	- Set state in UCB to indicate tape position lost
;+
; SET_TAPE_LOST
;
; This routine sets state in thge UCB to indicate that the tape position is 
; lost, and is called at unit init and any time are error occurs which causes
; us to suspect that we may have lost tape position.
;
; INPUTS:
;
;	R3	- UCB
;
; OUTPUTS:
;
;	MT$M_LOST set in DEVDEPEND of UCB
;	MT$M_BOT, MT$M_EOT, MT$M_EOF cleared in DEVDEPEND of UCB
;	UCB$L_PREV_TM gets -2 (no previous TM found)
;	All registers preserved
;-

SET_TAPE_LOST:

	.JSB_ENTRY INPUT=<R3>
	BITL	#UCB$M_COMPCHK_IN_PROG,-; Compaction check in progress?
		UCB$L_MK_FLAGS(R3)	; 
	BNEQ	10$			; Skip set lost if yes
 	BISL	#MT$M_LOST,-		; Set position lost
 		UCB$L_DEVDEPEND(R3)	;
 	BICL	#<MT$M_BOT!-		; Clear beginning of tape,
 		  MT$M_EOT!-		; end of tape,
 		  MT$M_EOF>,-		; and end of file
 		UCB$L_DEVDEPEND(R3)	;
 	MNEGL	#2,UCB$L_PREV_TM(R3)	; No previous tape mark
10$:	RSB				; Return to caller
	.PAGE
	.SBTTL	WAIT_UNIT_READY	- Wait for unit to become ready
;+
; WAIT_UNIT_READY
;
; This routine polls once every READY_POLL_INTERVAL seconds, waiting for the
; drive to become ready. The drive can be not ready for two reasons: a tape
; is not mounted in the drive; or a previous rewind/nowait command is still
; in progress. In SCSI, there's no way to distinguish between these two cases.
; However, the driver keeps track of the time that the last rewind command
; sent to the drive should have completed (in UCB$L_REWIND_TIME), and polling 
; continues only until that time is exceeded. This prevents unnecessary polling 
; when the tape drive is clearly not ready.
;
; INPUTS:
;
;	R3	- UCB address
;	R4	- PDT address
;	R5	- SCDRP address
;
; OUTPUTS:
;
;	R0	- Status
;	R1,R2	- Destroyed
;	All other registers preserved
;-

WAIT_UNIT_READY:

	.JSB_ENTRY INPUT=<R3,R4,R5>,OUTPUT=<R0>,SCRATCH=<R1,R2>
                                        ; ready fails
10$:	BSBW	TEST_UNIT_READY		; Execute TEST UNIT READY command
	BLBC	R0,30$			; Branch on error
20$:	BICL    #UCB$M_UNLOAD_INPROG,-  ; Clear UNLOAD bit
		UCB$L_MK_FLAGS(R3)
	RSB				; Return to caller

30$:	CMPL	R0,#SS$_DEVOFFLINE	; Device offline?
	BNEQ	20$			; Branch if not
	MOVL	#SS$_ABORT,R0		; Assume I/O got canceled
	BBS	#UCB$V_CANCEL,-		; Branch if it did
		UCB$L_STS(R3),20$	;
	MOVZWL  #SS$_MEDOFL,R0          ; Assume device is not ready
	BBS     #UCB$V_UNLOAD_INPROG,-  ; Branch if last operation was an UNLOAD
		UCB$L_MK_FLAGS(R3),35$  ; that we may be waiting for a media stacker
	CMPL	G^EXE$GL_ABSTIM,-	; Could the not ready condition be
		UCB$L_REWIND_TIME(R3)	; due to a recent rewind command?
	BGEQU	20$			; Branch if not, no need for more polling
	BRB	40$			; Continue polling for rewind completion
35$:    CMPL    G^EXE$GL_ABSTIM,-       ; Could the not ready condition be
		UCB$L_UNLOAD_TIME(R3)   ; due to a recent unload operation?
	BGEQU   20$                     ; Branch if not, no need for more polling

40$:	PUSHL	# SPL$C_IOLOCK8			;F FF; Set the required fork lock
	PUSHAQ	POLL_INTERVAL			;F FF; Set the time delay in ticks
	PUSHL	SCDRP$PS_KPB(R5)		; Get address of KPB
	CALLS	# 3, G^ EXE$KP_TQE_WAIT		;F FF; Fork and wait the desired time
	BRB	10$			; Continue polling
	.PAGE
	.SBTTL	CHECK_EOV	- Check for logical end-of-volume
;+
; CHECK_EOV
;
; This routine checks for logical end-of-volume, which has the following
; prerequisites:
;
;	- Two consecutive tape marks are encountered during
;	  a SKIP_FILE or SKIP_RECORD operation
;
; If logical end-of-volume is detected, the tape is backspaced one record to
; position it between the two tapemarks.
;
; INPUTS:
;
;	R0	- SS$_ENDOFVOLUME
;	R3	- UCB address
;	R4	- PDT address
;	R5	- SCDRP address
;
;	UCB$L_RECORD	- Current record (on which a tape mark has been found)
;	UCB$L_PREV_TM	- Record of last tapemark encountered
;
; OUPUTS:
;
;	R0	- Status (SS$_ENDOFVOLUME or SS$_ENDOFFILE)
;	R2	- Destroyed
;	All other registers preserved
;
;	UCB$L_PREV_TM	- Updated
;-

CHECK_EOV:

	.JSB_ENTRY INPUT=<R0,R3,R4,R5>,OUTPUT=<R0>,SCRATCH=<R2>,PRESERVE=<R1>
	SUBL3	UCB$L_PREV_TM(R3),-	; Calculate difference between last 
		UCB$L_RECORD(R3),-(SP)	; tapemark and this one. If consecutive,
	DECL	(SP)+			; then check for logical end-of-volume
	BNEQ	20$			; Branch if not

; Logical end-of-volume detected. Position the tape between the two tapemarks
; by backspacing one record.

	MOVL	#-1,R0			; Prepare to back up one record
	BSBW	SKIP_RECORD		; Position tape between filemarks
	CMPL	R0,#SS$_ENDOFFILE	; EOF status?
	BNEQ	30$			; Branch if not, serious problem exists
	MOVL	#SS$_ENDOFVOLUME,R0	; Return end of volume status

20$:	MOVL	UCB$L_RECORD(R3),-	; Update previous tapemark field in
		UCB$L_PREV_TM(R3)	; UCB with current record number
30$:	RSB				; Return to caller
	.PAGE
	.SBTTL	SEND_COMMAND	- Send a SCSI command
;+
; SEND_COMMAND
;
; This routines sends a command to the SCSI device. It returns any failing
; port status to the caller. If the port status is success, it checks the
; SCSI status byte. If a check condition status is returned, a request 
; sense command is sent to the target and the sense key is translated into a 
; VMS status code, which is returned as status.
;
; INPUTS:
;
;	4(AP)	- UCB address
;	8(AP)	- PDT address
;	12(AP)	- SCDRP address
;
; OUTPUTS:
;
;	R0	- Status
;	R1,R2	- Destroyed
;	All other registers preserved
;-

SEND_COMMAND::

	.CALL_ENTRY INPUT=<>,OUTPUT=<R0>,-
		    SCRATCH=<R0,R1>, PRESERVE=<R2,R3,R4,R5,R6,R7,R8>

	$ARG_DEF <ucb,- 			;	UCB Address
		  pdt,-                         ;	PDT Address
		  scdrp>                        ;	SCDRP Address

	MOVL	ucb(AP),R3			;
	MOVL	pdt(AP),R4                      ;
	MOVL	scdrp(AP),R5			;

	MOVB	#BUSY_RETRY_CNT,-	; Initialize busy retry count
		UCB$B_BUSY_RETRY(R3)	;
	BICB	#UCB$M_ADDNL_INFO,-	; Indicate additional info is not
		UCB$L_MK_FLAGS(R3)	; valid
1$:	SPI$SEND_COMMAND		; Send the SCSI command
	BLBC	R0,20$			; Branch on error
	ASSUME	SCSI$C_GOOD_STATUS EQ 0
	MOVZBL	@SCDRP$L_STS_PTR(R5),R1	; Get SCSI status byte
	BICB	#SCSI$M_STAT_MASK,R1	; Clear reserved, vendor unique bits
	BNEQ	30$			; Branch if bad status

	.IF DEFINED MULTI_VOLUME_TEST	; Code for testing multi-volume savesets
	MOVL	UCB$L_IRP(R3),R1	; Get IRP address
	BEQL	6$			; Branch if not available
	EXTZV	#IRP$V_FCODE,-		; Extract function code
		#IRP$S_FCODE,-		;
		IRP$L_FUNC(R1),R1	;
	CMPL	R1,#IO$_WRITEPBLK	; Write function?
	BEQL	5$			; Branch if so
	CMPL	R1,#IO$_WRITEOF		; Write tapemark?
	BNEQ	6$			; Branch if not
5$:	CMPL	UCB$L_RECORD(R3),#^X50	; Beyond artificial end of tape mark?
	BGEQU	40$			; Branch if so
6$:
	.ENDC

10$:	MOVL	R0,UCB$L_SAV_SEND_STATUS(R3) ; Save status
	RET				; Return to caller

; The port returned bad status from SPI$SEND_CMD. Log an error and return to
; the caller.

20$:	CMPL	R0,#SS$_ABORT		; Aborted command?
	BEQL	10$			; Branch if so, don't log an error
	LOG_ERROR -			; Log a send command error
		TYPE=SEND_CMD_ERROR,-	;
		VMS_STATUS=R0,-		;
		UCB=R3			;
	BRB	10$			; Use common exit

; A bad SCSI status code was returned. If the code is a check condition, then 
; send a request sense command to the device. Otherwise, the status code is 
; something unexpected. Log an error and return SS$_MEDOFL status.

30$:	CMPB	#SCSI$C_CHK_CONDITION,R1; Check condition status?
	BEQL	40$			; Branch if so
	CMPB	#SCSI$C_BUSY,R1		; Device busy?
	BNEQ	35$			; Branch if not
	BBC	#UCB$V_WAITRDY_INPROG,-	; Branch if waiting for unit ready is
		UCB$L_MK_FLAGS(R3),35$	; not in progress
	MOVL	#SS$_DEVOFFLINE,R0	; Return device off line status
	BRW	10$			; Return to caller
	
35$:	DECB	UCB$B_BUSY_RETRY(R3)	; Decrement busy retry count
	BLEQ	37$			; Branch if count expired
	PUSHL	# SPL$C_IOLOCK8		; Set the required fork lock
	PUSHAQ	ONE_SECOND		; Set time delay
	PUSHL	SCDRP$PS_KPB(R5)	; Set KPB address
	CALLS	#3,G^EXE$KP_TQE_WAIT	; Fork and wait the desired time
	BRW	1$			; Go send the command again

37$:	LOG_ERROR -			; Log a send command error
		TYPE=SEND_CMD_ERROR,-	;
		VMS_STATUS=#SS$_NORMAL,-;
		UCB=R3			;
	MOVL	#SS$_MEDOFL,R0		; Return a generic status code
	BRW	10$			; Return to caller

; A check condition status code was returned. Save the original SCDRP address
; allocate a second one and send a request sense command. If the request 
; sense succeeds, translate the sense key to a VMS status code and return that
; as the status code for the original command.

40$:	TSTL    UCB$L_8MM_CHK(R3)       ; If an 8mm COPY check flag is set,
     	BNEQ    10$                     ; Return to caller
	CLRB	SCDRP$B_SENSE_KEY(R5)	; Initialize saved sense key field

; Handle the case of invalid autosense data.

	BBS	#SCDRP$V_FLAG_ASENSE_VALID, -	; Is the autosense data valid ?
		SCDRP$L_SCSI_FLAGS(R5), 45$	; Yes, continue
	CLRL	SCDRP$L_SVA_USER(R5)		; Else set up for reg dump rtn
	LOG_ERROR -				; Log an extended sense data error
		TYPE=EXTND_SENSE_DATA,-
		VMS_STATUS=#SS$_NORMAL,-
		UCB=R3
	CLRL	SCDRP$PS_PREV_SCDRP(R5)		; No additional SCDRP
	MOVL	#SS$_DRVERR,R0			; Return general failure
	BRW	10$				; Return

45$:	MOVL	SCDRP$L_SVA_USER(R5),R6		; Save user buffer address
	MOVL	SCDRP$L_TRANS_CNT(R5),R7	; Save transfer count
	MOVL	SCDRP$PS_SENSE_BUFFER(R5),-	; Copy Auto Sense buffer address 
		SCDRP$L_SVA_USER(R5)		; to User buffer address

; The length field is byte 7 of the sense data and represents the number
; of bytes after itself.  So add 8 to get total length.  Note: Trans_cnt is
; needed by the register dump routine.

	MOVL	SCDRP$L_SVA_USER(R5),R0      	; Sense data address
	MOVZBL	SCSI$SNS$B_ADD_SENSE_LEN(R0),R0	; Addn sense length
	ADDL3   #1,#SCSI$SNS$B_ADD_SENSE_LEN,R1 ; Preceding bytes	
	ADDL3	R1,R0,SCDRP$L_TRANS_CNT(R5)    	; Total sense length
	BSBW	LOG_EXTND_SENSE		; Log an extended sense error
	MOVL	R7,SCDRP$L_TRANS_CNT(R5); Restore transfer count
	BSBW	SAVE_ADDNL_INFO		; Save any valid additional information
					; from the extended sense data
	BSBW	TRANS_SENSE_KEY		; Translate the extended sense key to 
					; a VMS status code in R0
	
	MOVL	R6,SCDRP$L_SVA_USER(R5) ; Restore user buffer address
	BRW	10$			; Return to caller
	.PAGE
	.SBTTL	LOG_EXTND_SENSE	- Log an extended sense data error
;+
; LOG_EXTND_SENSE
;
; This routine logs an extended sense data error.
;
; Certain errors are suppressed including:
;
; - UNIT ATTENTIONS (we can't tell the difference between device resets
;   and media changes, so we log nothing)
;
; - A NOT READY sense key.
;
; Before logging the error, play a trick with the two SCDRPs currently in use.
; Since we're really interested in logging the contents of the original command,
; and not the request sense command, copy the address of the original command
; buffer into the SCDRP for the request sense command. This causes the contents
; of the original command (a read or write, for example) to be logged along 
; with the contents of the request sense data returned from the request sense 
; command.
;
; INPUTS:
;
;	R3	- UCB address
;	R4	- PDT address
;	R5	- SCDRP address
;
;
; OUTPUTS:
;
;	R0-R2 	- Destroyed
;	All other registers preserved
;-

LOG_EXTND_SENSE:

	.JSB_ENTRY INPUT=<R3,R4,R5>,SCRATCH=<R0,R1,R2>,PRESERVE=<R7,R8>
	MOVL	SCDRP$L_SVA_USER(R5),R0	; Get address of extended sense data
	MOVZBL	12(R0),R7		; Get ASC
	MOVZBL	13(R0),R8		; Get ASQ
	BITB	#SCSI_XS$M_ILI,-	; Illegal length indicator?
		SCSI_XS$B_KEY(R0)	;
	BNEQ	10$			; Branch if so
	BITB	#<SCSI_XS$M_EOF!-	; Filemark?
		 SCSI_XS$M_EOM>,-	; End of medium?
		SCSI_XS$B_KEY(R0)	;
	BEQL	5$			; Branch if not

; The following sequence checks that a skip record operation which encounters
; a tape mark, BOT, or EOT returns valid additional info (residue) from the
; subsequent request sense command. If not, the driver has no way of keeping
; track of the UCB$L_RECORD value.

	BBC	#UCB$V_SKIP_INPROG,-	; Branch if skip command not in
		UCB$L_MK_FLAGS(R3),4$	; progress
	ASSUME	SCSI_XS$V_ADDNL_VALID EQ 7
	TSTB	SCSI_XS$B_ERR_CODE(R0)	; Valid additional info?
	BGEQ	7$			; Branch if not
4$:	BRB	10$			; Otherwise, leave without logging an error

5$:	EXTZV	#SCSI_XS$V_KEY,-	; Get the extended sense key
		#SCSI_XS$S_KEY,-	;
		SCSI_XS$B_KEY(R0),R1	;
	BEQL	10$			; Branch if no sense data
	CMPB	R1,-			; Unit attention sense key?
		#SCSI$C_UNIT_ATTENTION 	;
	BEQL	10$			; Branch if so, don't log an error
	CMPB	R1,#SCSI$C_NOT_READY	; Device not ready sense key?
	BEQL	10$			; Branch if so, don't log an error

7$:	MOVL	SCDRP$L_CMD_PTR(R5),R2	; Save address of request sense cmd
	MOVB	#SCSI$C_CHK_CONDITION,-	; Original command failed with request
		@SCDRP$L_STS_PTR(R5)	; sense status
;
; If this is an 8mm device we may have arrived here as a result of the
; SPACE REC REV - WRITE SCSI command sequence which $COPY from BOT processing
; generates. That command sequence is illegal on the 8mm and results
; in a CHECK CONDITION - ILLEGAL REQUEST error response from the device.
; DO_8MM_COPY_CHK will subsequently generate an alternate SCSI command sequence
; that will recover from the error and accommodate the 8mm behavior.
;
        IFNOT_8MM      700$              	; Br if not 8MM
        CMPB    R1,-                    ; Illegal Request sense key?
                #SCSI$C_ILLEGAL_REQUEST
        BNEQ    700$                      ; Br if not Illegal Request
        BBC     #DEV$V_MNT,-            ; Br if not mounted
                UCB$L_DEVCHAR(R3),8$
        BBS     #DEV$V_FOR,-            ; Br if mounted foreign
                UCB$L_DEVCHAR(R3),8$
        MOVL    UCB$L_IRP(R3),R1        ; Restore IRP address
        EXTZV   #IRP$V_FCODE,-          ; Extract I/O function code
                #IRP$S_FCODE,-          ;
                IRP$L_FUNC(R1),R1       ;
        CMPB    #IO$_WRITEPBLK,R1       ; Write in progress?
        BNEQ    8$                      ; Br if not
        CMPL    #1,UCB$L_RECORD(R3)     ; Br if the record # is not consistent
        BNEQ    8$                      ; with the $COPY from BOT error
        MOVL    #1,UCB$L_8MM_CHK(R3)   	; Set COPY check in progress flag
        BRB     9$                      ; Go to return
700$:   CMPB    R1,#SCSI$C_BLANK_CHECK  ; Check for blank-check error and BOT,
	BNEQ	8$
	BBS	#UCB$V_SPACEFILE_INPROG,- ; Suppress if SPACE FILEMARKS just
		UCB$L_MK_FLAGS(R3),9$	; hit logical EOV
        TSTL    UCB$L_RECORD(R3)        ; Supress logging error since it is -
        BEQL    9$                      ; new tape.    mcy  8/28/92

; Don't log certain Illegal Request errors that can result from sending
; e.g. a READ POSITION command to a target that does not support it.

8$:	CMPL	R1,#SCSI$C_ILLEGAL_REQUEST	; Illegal command sense key?
	BNEQ	36$                            	; No, so log it
        BBS    #UCB$V_READPOSCHK_IN_PROG,-	; Is READ POSITION check in progress?
                UCB$L_MK_FLAGS(R3),9$		; If so, don't log error

36$:	LOG_ERROR -			; Log an extended sense data error
		TYPE=EXTND_SENSE_DATA,-	;
		VMS_STATUS=#SS$_NORMAL,-;
		UCB=R3			;
9$:	MOVL	R2,SCDRP$L_CMD_PTR(R5)	; Restore address of request sense cmd
10$:	RSB				; Return to caller
	.PAGE
	.SBTTL	TRANS_SENSE_KEY	- Translate extended sense key to VMS status code
;+
; TRANS_SENSE_KEY
;
; This routine translates an extended sense key to a VMS status code using
; SENSE_KEY_TABLE. If the sense key is not found in the table (indicating an
; invalid sense key), then a generic VMS status code (SS$_MEDOFL) is returned.
;
; The filemark and EOM bits generate status codes of SS$_ENDOFFILE and
; SS$_ENDOFTAPE respectively.
;
; Certain situations cause an additional translation including:
;
; - If the sense key was NOT_READY and the wait for unit ready flag is set in
;   the UCB, then this is an indication that the device is in the process of
;   becoming ready. Return a DEVOFFLINE status to allow the WAIT_UNIT_READY
;   routine to continue to poll for unit ready. Otherwise, change the
;   status to SS$_MEDOFL to cause mount verification to occur for this device.
;
; - Any medium error which does not have an additional sense code of 
;   non-recoverable ECC error generates SS$_DRVERR status. This is considered 
;   "more fatal" as it could cause us to lose tape position.
;
; INPUTS:
;
;	R3	- UCB address
;	R5	- SCDRP address
;
; OUTPUTS:
;
;	R0	- VMS status code
;	R1,R2	- Destroyed
;	All other registers perserved
;-

TRANS_SENSE_KEY:

	.JSB_ENTRY INPUT=<R3,R5>,OUTPUT=<R0>,SCRATCH=<R1,R2>
	MOVL	SCDRP$L_SVA_USER(R5),R2	; Get address of request sense data

	.IF DEFINED MULTI_VOLUME_TEST	; Code for testing multi-volume savesets
	MOVL	UCB$L_IRP(R3),R1	; Get IRP address
	BEQL	3$			; Branch if not available
	EXTZV	#IRP$V_FCODE,-		; Extract function code
		#IRP$S_FCODE,-		;
		IRP$L_FUNC(R1),R1	;
	CMPL	R1,#IO$_WRITEPBLK	; Write function?
	BEQL	2$			; Branch if so
	CMPL	R1,#IO$_WRITEOF		; Write tapemark?
	BNEQ	3$			; Branch if not
2$:	CMPL	UCB$L_RECORD(R3),#^X50	; Beyond artificial end of tape mark?
	BLSSU	3$			; Branch if not
	BISB	#^X40,2(R2)		; Simulate end of tape
3$:
	.ENDC

; If the key is recorvered error or no sense, then go ahead and look at the
; EOF, EOM, and ILI flags in the extended sense data. Any other type of sense 
; key takes precedence over these flags.

	ASSUME SCSI_XS$S_KEY EQ 4
	EXTZV	#SCSI_XS$V_KEY,-	; Get the extended sense key
		#SCSI_XS$S_KEY,-	;
		SCSI_XS$B_KEY(R2),R1	;
	ASSUME	SCSI$C_NO_SENSE EQ 0
	BEQL	4$			; Branch if no sense
	CMPB	R1,-			; Recovered error?
		#SCSI$C_RECOVERED_ERROR	; 
	BEQL	4$			; Branch if so

	CMPB	R1,#SCSI$C_BLANK_CHECK	; End of data?
	BNEQ	35$	                ; No, continue
	BBC	#UCB$V_SPACEFILE_INPROG,- ; SPACE FILEMARKS in progress?
		UCB$L_MK_FLAGS(R3),35$	 
        MOVL	#SS$_ENDOFVOLUME,R0	; Yes, just hit EOV
	BISL	#MT$M_EOF,-		; and is at a tapemark
		UCB$L_DEVDEPEND(R3)
	BRW	80$


; Workaround for a bug in the TK50: if this is a medium error sense key and the 
; EOM bit is set, then this indicates we're at BOT or EOT and the medium error
; sense key can be ignored. 

35$:	CMPB	R1,-			; Medium error?
		#SCSI$C_MEDIUM_ERROR	; 
	BNEQ	6$			; Branch if not
	IFNOT_TK50 6$			; Branch if not a TK50

4$:	BITB	#SCSI_XS$M_ILI,-	; Illegal length indicator?
		SCSI_XS$B_KEY(R2)	;
	BNEQ	90$			; Branch if so
	BITB	#SCSI_XS$M_EOF,-	; Filemark?
		SCSI_XS$B_KEY(R2)	;
	BNEQ	50$			; Branch if so
	BITB	#SCSI_XS$M_EOM,-	; End of medium?
		SCSI_XS$B_KEY(R2)	;
	BNEQ	60$			; Branch if so

; If this is a UNIT ATTENTION sense key, the volume valid bit is NOT set
; in the UCB, and the "wait for unit ready" bit is set, then return success. 
; This situation can occur for multi-volume tape backups when one tape is
; unloaded and the next is loaded. We'll eventually need a PACACK to make the 
; drive usable.

6$:	CMPB	R1,-			; Unit attention sense key?
		#SCSI$C_UNIT_ATTENTION 	;
	BNEQ	5$			; Branch if not
    	BBS	#UCB$V_VALID,-		; Branch if volume valid bit is set
		UCB$L_STS(R3),5$	; in the UCB
	BBC	#UCB$V_WAITRDY_INPROG,-	; Branch if NOT waiting for unit ready
		UCB$L_MK_FLAGS(R3),5$	;
	MOVZWL	#SS$_NORMAL,R0		; Set success status
	BRB	40$			; Use common exit path

5$:	MOVB	R1,SCDRP$B_SENSE_KEY(R5); Save sense key in the SCDRP
	MOVAL	SENSE_KEY_TABLE,R0	; Get address of sense key to VMS status
					; code translation table
	MOVL	(R0)[R1],R0		; Pick up VMS error code

; A status code of SS$_DRVERR indicates a serious error occurred. If this is
; the case, set the tape position lost.

25$:	CMPL	R0,#SS$_DRVERR		; Serious error?
	BNEQ	26$			; Branch if not
	BSBW	SET_TAPE_LOST		; Set tape lost

; The following code allows the routine WAIT_UNIT_READY to poll for unit
; ready. If a NOT_READY sense key is returned by the drive, and the wait for
; unit ready flas is set, then return DEVOFFLINE status to notify the caller 
; that the device is still not ready, Otherwise, return MEDOFL status to 
; cause mount verification to kick in.

26$:	CMPL	R0,#SS$_DEVOFFLINE	; Device offline status?
	BNEQ	40$			; Branch if not
	BBS	#UCB$V_WAITRDY_INPROG,-	; Branch if waiting for unit ready is
		UCB$L_MK_FLAGS(R3),40$	; set
;
; Notify MME that the device is unavailable and allow them to correct
; if possible.
;
	MOVL    #MME$_UNLOAD, R0	; load MME event code.
	PUSHL   R5			; save the CDRP address on the stack.
	MOVL    R3, R5			; copy the UCB address into R5.
	JSB     G^MME$$DEV_EVENT	; invoke the MME call-out routine.
	POPL    R5			; restore the CDRP address.

30$:	MOVZWL	#SS$_MEDOFL,R0		; Otherwise, set medium offline status
40$:	RSB				; Return to caller

; EOF set. This indicates a filemark was encountered during the previous tape
; motion command. Set SS$_ENDOFFILE status and set the EOF bit in the device
; dependent field.

50$: 	MOVZWL	#SS$_ENDOFFILE,R0	; Assume filemark (end of file)
	BISL	#<MT$M_EOF>,-		; Set end of file bit in UCB
 		UCB$L_DEVDEPEND(R3)	;
	BRB	80$			; Perform sanity check on additional
					; info (if skip record in progress)

; EOM set. This indicates that the tape reached one end or the other, based
; on the direction of motion. In the case of reverse motion, BOT is set. 
; Otherwise, EOT is set. In either case, ENDOFTAPE status is returned.

60$:	MOVL	#<MT$M_BOT>,R1		; Assume reverse motion
	BBS	#UCB$V_REV_SKIP,-	; Branch if reverse motion in 
		UCB$L_MK_FLAGS(R3),70$	; progress
	MOVL	#<MT$M_EOT>,R1		; Prepare to set end of tape
70$:	BISL	R1,UCB$L_DEVDEPEND(R3)  ; Set BOT/EOT based on current direction
	MOVL	#SS$_ENDOFTAPE,R0	; Set end of tape status

; If the original command was a skip record, and no valid additional info was
; returned, then set a fatal error status and mark the tape position as lost,
; as we need the additional info to keep track of our position on the tape.

80$:	BBC	#UCB$V_SKIP_INPROG,-	; Branch if skip command not in 
		UCB$L_MK_FLAGS(R3),40$	; progress
	BBS	#UCB$V_ADDNL_INFO,-	; Branch if valid additional info
		UCB$L_MK_FLAGS(R3),40$	; returned
	IF_TSZ05	40$		; For TSZ05 and TSZ07 devices, skip rev
	IF_TSZ07	40$		; doesn't return "addl info" flag set.
	MOVL	#SS$_DRVERR,R0		; Otherwise, change to fatal error
	BSBW	SET_TAPE_LOST		; Set tape position lost
	BRB	40$			; Use common exit

; ILI set. This indicates that the requested record length did not match the
; actual record length on tape. If the record on tape was shorter than the
; requested length, ignore the ILI bit as utility doing the read does not
; necessarily know ahead of time the length of the record on tape. If the
; record on tape was longer than the requested length, return a data overrun
; status code. The data overrun condition is detected by the ILI bit being
; set and a TRANS_CNT equal to BCNT. It's assumed that the drive would have
; returned more data if the host had requested it.

90$:	MOVZWL	#SS$_NORMAL,R0		; Assume no data overrun
	CMPW	SCDRP$L_TRANS_CNT(R5),-	; Data overrun condition?
		SCDRP$L_BCNT(R5)	;
	BLSSU	40$			; Branch if not
	MOVZWL	#SS$_DATAOVERUN,R0	; Return data overrun status
	BRB	40$
	.PAGE
	.SBTTL	SAVE_ADDNL_INFO	- Save additional extended sense information
;+
; SAVE_ADDNL_INFO
;
; This routine is called whenever valid extended sense data is returned by a
; target to obtain any addition data. In the case of a skip command, 
; this information is used later to determine the actual number of records or 
; files skipped. 
;
; INPUTS:
;
; 	R3	- UCB address
;	R5	- SCDRP address associated with request sense command
;
; OUTPUTS:
;
;	R0,R1	- Destroyed
;
;	UCB$L_ADDNL_INFO	- Additional info field from the extended
;				  sense data or -1 if no valid data.
;-

SAVE_ADDNL_INFO:

	.JSB_ENTRY INPUT=<R3,R5>,SCRATCH=<R0,R1>
	MOVL	SCDRP$L_SVA_USER(R5),R0	; Get address of request sense data
	MOVAL	UCB$L_ADDNL_INFO(R3),R1	; Get address of additional info field
	CLRL	(R1)			; Assume additonal info not valid
	ASSUME	SCSI_XS$V_ADDNL_VALID EQ 7
	TSTB	SCSI_XS$B_ERR_CODE(R0)	; Valid additional info?
	BGEQ	30$			; Branch if not
	BISB	#UCB$M_ADDNL_INFO,-	; Indicate additional info is valid
		UCB$L_MK_FLAGS(R3)	;
	MOVAL	SCSI_XS$B_ADDNL_INFO+4(R0),R0 ; Get address just beyond 
					; additonal info field
	.REPT	4
	MOVB	-(R0),(R1)+		; Copy a byte of additional info from 
					; the extended sense data to the UCB. 
					; (Note that bytes are reversed in the 
					; extended sense data).
	.ENDR
30$:	RSB
	.PAGE
	.SBTTL	INIT_SCDRP	- Initialize an SCDRP
;+
; INIT_SCDRP
;
; This routine initializes an SCDRP which has already been allocated on the
; kernel process stack.  The entire SCDRP is zero'ed and various fields 
; are initialized.
;
; INPUTS:
;
;	R3	- UCB address
;	R5	- SCDRP address
;
; OUTPUTS:
;
;	UCB$L_SCDRP	- SCDRP address
;	SCDRP$L_UCB	- UCB address
;	SCDRP$B_FLCK	- Fork lock
;	SCDRP$L_CDT	- CDT address
;	SCDRP$L_SCSI_FLAGS - Initialized
;-

INIT_SCDRP:

	.JSB_ENTRY INPUT=<R3,R5>,PRESERVE=<R0,R1,R2,R3,R4,R5>
	PUSHR	#^M<R3,R5>		; Save regs
	MOVC5	#0,(SP),#0,-		; Initialize the SCDRP
		#SCDRP$C_LENGTH-12,-	;
		12(R5)			;
	POPR	#^M<R3,R5>		; Restore regs
	MOVL	R5,UCB$L_SCDRP(R3)	; Save SCDRP address in UCB
	MOVL	R3,SCDRP$L_UCB(R5)	; Save UCB address in SCDRP
	MOVB	UCB$B_FLCK(R3),-	; Copy the fork lock field from the
		SCDRP$B_FLCK(R5)	; UCB to the SCDRP
	MOVL	UCB$L_SCDT(R3),-	; Save CDT address in SCDRP
		SCDRP$L_CDT(R5)		; 
	MOVW	#SCDRP$K_LENGTH, -	; Setup SCDRP length field
		SCDRP$W_SCDRPSIZE(R5)
	MOVB	#DYN$C_SCDRP, -		; Setup SCDRP structure type
		SCDRP$B_CD_TYPE(R5)
	RSB

	.PAGE
	.SBTTL	GET_DEVICE_TYPE	- Determine device type from inquiry data
;+
; GET_DEVICE_TYPE
;
; This routine translates the product ID field from the INQUIRY data to
; a VMS device type. In addition, it fills in the media ID field in the UCB.
; The TZK50 and TZ30 are handled specially, since these devices have no product
; ID field in the inquiry data. For the TZK50 and TZ30, there is exactly 5
; bytes if inquiry data, and the device qualifier fields contain 50 (hex) and
; 30 (hex), respectively.
; 
; INPUTS:
;
;	R0	- Address of INQUIRY data
;	R3	- UCB address
;	R5	- SCDRP address
;
; OUTPUTS:
;
;	R1	- Destroyed
;	All other registers preserved
;-

GET_DEVICE_TYPE:

	.JSB_ENTRY INPUT=<R0,R3,R5>,SCRATCH=<R1>,PRESERVE=<R2,R4>
	CMPL	SCDRP$L_TRANS_CNT(R5),#5; 5 bytes of inquiry data?
	BNEQ	20$			; Branch if not, can't be a TK50 or TZ30
	EXTZV	#SCSI_INQ$V_DEVQUAL,-	; Get device type qualifier
		#SCSI_INQ$S_DEVQUAL,-	;
		SCSI_INQ$B_DEVQUAL(R0),-;
		R1			;

; Check for device type of TZ30

	CMPB	R1,#^X30		; TZ30?
	BNEQ	10$			; Branch if not
	MOVAL	TZ30_DEV_TYPE,R1	; Get table entry for TZ30
	BRB	60$
	
; Check for device type of TZK50

10$:	CMPB	R1,#^X50		; TZK50?
	BNEQ	20$			; Branch if not
	MOVAL	TK50_DEV_TYPE,R1	; Get table entry for TK50
	BRB	60$			; Branch to common code

; Device is not a TZ30 or TK50. Scan the device type table to see if we have 
; a matching produce ID field. If not, then treat this device as a generic SCSI
; tape.

20$:	CMPL	SCDRP$L_TRANS_CNT(R5),-	; Enough inquiry data to have a valid
		#24			; produce ID field?
	BLSS	50$			; Branch if not
	MOVAL	SCSI_DEVICE_TABLE,R1	; Get SCSI device table
30$:	CMPL	DTYP_ID_LOW(R1),16(R0)	; Matching product ID (low LW)?
	BNEQ	40$			; Branch if not
	CMPL	DTYP_ID_HIGH(R1),20(R0)	; Matching product ID (high LW)?
	BEQL	60$			; Branch if so
40$:	ADDL	#DTYP_TABLE_ENTRY_SIZE,-
		R1			; Advance to next entry in table
	TSTB	DTYP_TYPE(R1)		; End of table
	BNEQ	30$			; Branch if not

; Device is not a TZ30 or TK50, nor was a matching product ID field found in
; the device type tape. Treat the device as a generic SCSI tape.

50$:	MOVAL	GENERIC_DEV_TYPE,R1	; Get table entry for generic tape

60$:	MOVB	DTYP_TYPE(R1),-		; Save VMS device type   
		UCB$B_DEVTYPE(R3)
	MOVL	DTYP_MINREV(R1),-	; Save minimum revision level
		UCB$L_MIN_REV(R3)
	BBC	#DTYP$V_NO_SKIPFILE,-	; Should skipfile be avoided?
		DTYP_FLAGS(R1),62$
	BICL2	#UCB$M_SKIPFILE_SUPPORTED,-  ; Yes, so disable skip-by-file
		UCB$L_MK_FLAGS(R3)	      
	
62$:    BBC     #DEV$V_DTN,-            ; If DTN bit is set, remove the dynamic
                UCB$L_DEVCHAR2(R3),65$  ;  name
	PUSHR	#^M<R0,R1>		; Save registers across call
        PUSHL	R3			; ioc$remove_device_type(&ucb) 
        CALLS	#1,G^IOC$REMOVE_DEVICE_TYPE
	POPR	#^M<R0,R1>		; Restore registers
;
65$:	MOVL	DTYP_MEDIA_ID(R1),-	; Save media ID field
		UCB$L_MEDIA_ID(R3)	; 
	MOVAB	DTYP_VENDOR(R1),-	; Save address of vendor-unique mode
		UCB$L_MSEL_INFO(R3)	; select info
        BBS     #MT2$V_COMP_ENA, -      ; Compaction enabled ?
                UCB$L_DEVDEPND2(R3), 64$;
	INSV	DTYP_DENSITY(R1),-	; Save density information
		#MT$V_DENSITY,-		;
		#MT$S_DENSITY,-		;
		UCB$L_DEVDEPEND(R3)	;
64$:    BBC     #DTYP$V_LOADER,-
		DTYP_FLAGS(R1),67$	; Check for loader device - mcy/rcl 9/18
        BISL2   #DEV$M_LDR,-            ; Set loader bit if device is loader	
                UCB$L_DEVCHAR2(R3)      ;
67$:	BICL	#UCB$M_DEVICE_IS_8MM,-	; Assume not an 8mm device
		UCB$L_MK_FLAGS(R3)

	CMPL	#^A/EXB-/, 16(R0)	; Is it an EXABYTE tape (8mm)?
	BEQL	68$               	; Branch if so

	CMPL	#^A/TKZ0/,16(R0)	; Is it a TKZ09?
	BNEQ	70$
	CMPL	#^A/9   /,20(R0)
	BNEQ	70$			; Branch if not

68$:	BISL	#UCB$M_DEVICE_IS_8MM,-	; Set "it's an 8mm device" flag
		UCB$L_MK_FLAGS(R3)

;
; At this point:
;	R0 = Address of the inquiry data
;	R3 = UCB
;	R5 = SCDRP
;	
70$:	CMPB	#DT$_GENERIC_MK,-	; Did we find this device in the table?
		UCB$B_DEVTYPE(R3)
	BNEQ	80$			; Yes, don't do dynamic naming
	CMPL	SCDRP$L_TRANS_CNT(R5),-	; A proper INQUIRY response is at least
		#32			;   32 bytes. Do we have that?
	BLSS	80$			; Nope, can't do dynamic naming

;
; At this point:
;	R0 = Address of the inquiry data
;	R3 = UCB
;
; Register usage:
;	R0 = input name pointer
;	R1 = output name pointer
;	R2 = original SP
;	R4 = end of field pointer
;	R5 = "last character was a blank" flag
;	R6 = scratch
;
;	First, process the Vendor ID, compressing multiple blanks into one.
;	
	MOVL	SP,R2			; Save stack pointer
	CLRL	R5			; Initialize "last was blank" flag
	BICL	#7,SP			; Quad align the stack
	SUBL	#32,SP			; Allocate space for name and such
	MOVL	SP,R1			; Setup pointer into name buffer
	MOVAB	8(R0),R0		; Point to vendor ID
	MOVAB	8(R0),R4		; Vendor ID is 8 bytes long

301$:	CMPL	R0,R4			; End of field?
	BEQL	320$			; Yes, continue with product ID
	CMPB	#^X20,(R0)+		; Is character a blank
	BNEQ	302$			; No, clear flag and copy
	BLBS	R5,301$			; If previous char blank, skip this one
	MOVL	#1,R5			; Otherwise, set "last was blank" flag
	MOVB	#^X20,(R1)+		;   and store a blank
	BRB	301$			; Back for next character
302$:	CLRL	R5			; Clear "last was blank" flag
	MOVB	-1(R0),(R1)+		;   and copy the character
	BRB	301$			; Back for next character

;
;	Now, setup to process the product ID.  Again, compress multiple blanks
;	into one.  Also, the copyright symbol "(C)" or "(c)" is treated as a
;	name terminator.
;	
320$:	MOVAB	16(R0),R4		; Product ID is 16 bytes long
	BLBS	R5,321$			; If trailing blank on Vendor, continue
	MOVL	#1,R5			; Otherwise, insert a blank between
	MOVB	#^X20,(R1)+		;   vendor and product IDs
321$:	CMPL	R0,R4			; See if we have a full type name
	BEQL	330$			; Yes, we're done
	CMPB	#^X20,(R0)+		; Is the next character a blank?
	BNEQ	322$			; No, go check for copyright
	BLBS	R5,321$			; If previous char blank, skip this one
	MOVL	#1,R5			; Otherwise, set "last was blank" flag
	MOVB	#^X20,(R1)+		;   and store a blank
	BRB	321$			; Back for next character

;
;	Check for copyright...
;	
322$:	CMPB	#^A/(/,-1(R0)		; Was character a "("
	BNEQ	323$			; Nope, go copy it
	BICB3	#^X20,(R0),R6		; Convert next char to upper case
	CMPB	#^A/C/,R6		; See if it's a "C"
	BNEQ	323$			; Nope, just copy data
	CMPB	#^A/)/,1(R0)		; Look for closing paren
	BEQL	330$			; Found copyright, so we're done

323$:	CLRL	R5			; Clear "last was blank" flag
	MOVB	-1(R0),(R1)+		;   and copy the character
	BRB	321$			; Back for next character

330$:	SUBL3	SP,R1,R0		; Calculate name length
	SUBL	R5,R0			; Remove any trailing blank
	PUSHAL	28(SP)			; Add dynamic name:
	PUSHL	R3			; status = ioc$add_device_type(
	PUSHL	R0			;	   &name, namelen, &ucb &(&dtn))
	PUSHAB	12(SP)			; (Buffer is now under 3 longword args)
	CALLS	#4,G^IOC$ADD_DEVICE_TYPE
	MOVL	R2,SP			; Clean the stack

80$:	RSB				; Return to caller
	.PAGE
	.SBTTL	CHECK_REV_LEVEL	- Check for an out-of-rev device
;+
; CHECK_REV_LEVEL
;
; This routine checks the revision level returned in the INQUIRY or RECEIVE
; DIAGNOSTIC data with the minimum revision level for that device. If the 
; device's revision level is below the minimum allowed, an invalid inquiry 
; data error is logged.
;
; Note: since the revision field in the inquiry data is a 4 byte ascii string,
; the low-order byte is the most significant. Thus, the bytes must be compared 
; from low byte to high byte, and a CMPL can not be used to perform the 
; comparison.
;
; INPUTS:
;
;	R3	- UCB address
;	UCB$L_HW_REV  - Revision level
;	UCB$L_MIN_REV - Minimum revision level
;
; OUTPUTS:
;
;	R0	- Status (LBS if rev level OK)
;	R1,R2	- Destroyed
;	All other registers preserved
;	An error is logged of the device is out-of-rev
;-

CHECK_REV_LEVEL:

	.JSB_ENTRY INPUT=<R3>,OUTPUT=<R0>,SCRATCH=<R1,R2>,-
		   PRESERVE=<R7,R8>
	CLRL	R0			; Assume out of rev
	MOVAL	UCB$L_HW_REV(R3),R1	; Get addr of actual revision level
	MOVAL	UCB$L_MIN_REV(R3),R2	; Get addr of minimum revision level
	.REPT	4
	CMPB	(R1)+,(R2)+		; Compare a byte of revision level
	BLSSU	20$			; Branch if out of rev
	BGTRU	5$			; Branch if rev OK
	.ENDR

5$:	MOVL	#SS$_NORMAL,R0		; Set success status
10$:	RSB				; Return to caller

20$:	LOG_ERROR -			; Log an invalid inquiry data error
		TYPE=INV_INQUIRY_DATA,-	;
		VMS_STATUS=#SS$_NORMAL,-;
		UCB=R3			;
	BRB	10$			; Use common exit
	.PAGE
	.SBTTL	MK_ALLOC_POOL	- Allocate a block of non-paged pool
;+
; MK_ALLOC_POOL
;
; This routine was formerly called ALLOC_POOL and was renamed to reflect 
; the new meaning of R5 as an input (now KPB address, not SCDRP address).
;
; This routine allocates a block of non-paged pool no smaller than the
; size of a fork block (allowing COM$DRVDEALMEM to fork on this block 
; during deallocation). An extra quadword at the top of the block is reserved 
; to save the size field, relieving the caller this responsibility. The caller 
; is presented with the address just beyond the reserved quadword. Although a 
; word would be sufficient for this field, a quadword is used for allignment 
; purposes (some blocks are used as IRPs, which are placed on self-relative 
; queues and require quadword allignment).
;
; If an allocation failure occurs, the thread is stalled and wakes up once a
; a second to retry the allocation.
;
; INPUTS:
;
;	R1	- Size of block to allocate
;	R3	- UCB address
;	R5	- KPB address
;
; OUTPUTS:
;
;	R0	- Output status
;	R1	- Size of block allocated
;	R2	- Address of allocated block
;	-8(R2)	- Length of allocated block (used by DEALLOC_POOL)
;	All other registers perserved
;-

MK_ALLOC_POOL:

	.JSB_ENTRY INPUT=<R1,R3,R5>,OUTPUT=<R0,R1,R2>,PRESERVE=<R3,R4,R5>
	ADDL	#8,R1			; Reserve a quadword to save size
	CMPL	R1,#FKB$C_LENGTH	; Requested size smaller than fork block?
	BGEQ	10$			; Branch if not
	MOVL	#FKB$C_LENGTH,R1	; Use fork block size as minimum
10$:	PUSHL	R1			; Save allocation length
	JSB	G^EXE$ALONONPAGED	; Allocate a block
	BLBC	R0,20$			; Branch if error
	ADDL	#4,SP			; Remove allocation length from stack
	PUSHR	#^M<R0,R1,R2>		; Save regs
	MOVC5	#0,(SP),#0,R1,(R2)	; Initialize the packet
	POPR	#^M<R0,R1,R2>		; Restore regs
	MOVL	R1,(R2)+		; Save size of block
	ADDL	#4,R2			; Skip a longword
	RSB				; Return to caller

; A pool allocation failure occurred. Come back once a second and retry the
; operation until successful.

20$:	PUSHL	UCB$B_FLCK(R3)			;F FF; Set the required fork lock
	PUSHAQ	ONE_SECOND			;F FF; Set the time delay in ticks
	PUSHL	R5				;F FF; Set the KPB address
	CALLS	# 3, G^ EXE$KP_TQE_WAIT		;F FF; Fork and wait the desired time
	POPL	R1			; Restore allocation length			
	BRW	10$			; Try again	

	.PAGE
	.SBTTL	DEALLOC_POOL	- Deallocate a block of non-paged pool
;+
; DEALLOC_POOL
;
; This routine deallocates a block of non-paged pool. The size of the block
; is stored in the reserved quadword at a negative offset from the beginning 
; of the block.
; 
; INPUTS:
;
;	R0	- Address of block to deallocate
;	-8(R0)	- Length of block to deallocate
;
; OUTPUTS:
;
;	R0	- Destroyed
;	All other registers perserved
;-

DEALLOC_POOL:

	.JSB_ENTRY INPUT=<R0>,SCRATCH=<R0>,PRESERVE=<R1,R2>
	SUBL	#4,R0			; Skip a longword
	MOVL	-(R0),IRP$W_SIZE(R0)	; Copy size field
	CLRB	IRP$B_TYPE(R0)		; Clear type field (prevents block from
					; being interpreted as shared memory
					; during deallocation)
	JSB	G^EXE$DEANONPAGED	; Deallocate the block
	RSB
	.PAGE
	.SBTTL	SETUP_CMD	- Common setup for all SCSI commands
;+
; SETUP_CMD
;
; This routine performs common setup prior to the sending of a SCSI command. 
; This includes allocating a command buffer, filling in the pointers in the 
; SCDRP to the command and status fields, copying the SCSI command to the command 
; buffer, allocating an S0 "user" buffer if the command requires transferring 
; data to or from the class driver, filling in the SCDRP fields used to map 
; this buffer, and mapping the buffer.
;
; Since this routine calls SPI$CMD_BUFFER_ALLOC, which can suspend
; the thread, the return PC must be saved in the SCDRP.
;
; INPUTS:
;
;      4(AP)	- Pointer to entry in SCSI_CMD table
;      8(AP)	- UCB address
;      12(AP)	- PDT address
;      16(AP)	- SCDRP address
;
; OUTPUTS:
;
;	R0	- Status
;	R1,R2	- Destroyed
;
;	SCDRP$L_CMD_BUF	- Address of SCSI command buffer
;	SCDRP$L_CMD_PTR	- Address of SCSI command 
;	SCDRP$L_STS_PTR	- Address to save SCSI status byte
;	SCDRP$L_SVA_USER- Address of S0 "user" buffer
;	SCDRP$L_BCNT	- Length of S0 "user" buffer
;	SCDRP$L_BOFF	- Byte offset of S0 "user" buffer
;	SCDRP$L_SVAPTE	- SVAPTE of of S0 "user" buffer
;	IRP$V_FUNC	- SET/CLEAR to indicate READ/WRITE from S0 "user" buffer
;-

SCSI_CMD_BUF_OVHD = 4 + 4		; 4 bytes to save status byte +
					; 4 bytes for SCSI command length

SETUP_CMD::					; Common setup for all SCSI commands
	.CALL_ENTRY INPUT=<>,OUTPUT=<R0>,SCRATCH=<R0,R1>,-
		PRESERVE=<R2,R3,R4,R5>

	$ARG_DEF <cmd,-				;  CMD tbl entry addr
		  ucb,- 			;	UCB Address
		  pdt,-                         ;	PDT Address
		  scdrp>                        ;	SCDRP Address

	MOVL	cmd(AP),R2			;
	MOVL	ucb(AP),R3			;
	MOVL	pdt(AP),R4                      ;
	MOVL	scdrp(AP),R5			;

	MOVZBL	(R2),R1			; Get size SCSI command
	ADDL	#SCSI_CMD_BUF_OVHD,R1	; Add in command buffer overhead
	PUSHL	R2			; Save R2
	SPI$CMD_BUFFER_ALLOC	; Allocate a command buffer
	MOVL	R2,R1			; Copy command buffer address
	POPL	R2			; Restore R2
	MOVB	#^XFF,(R1)		; Initialize status field
	MOVAL	(R1)+,-			; Address to put SCSI status byte
		SCDRP$L_STS_PTR(R5)	;
	MOVL	R1,SCDRP$L_CMD_PTR(R5)	; Save address of SCSI command
	MOVZBL	(R2)+,R0		; Get SCSI command length
	MOVL	R0,(R1)+		; Save length in command buffer

; The PUSHAB and BISB2 instructions are used to fill in the LUN field in the
; command packet. SCSI-2 suggests that this field be left with a zero and 
; that message handshaking between the port driver and target convey LUN
; information. Therefore, these two instructions have been commented out.

;**	PUSHAB	1(R1)			; Save address of SCSI command LUN field
	ASHL	#-1,R0,R0		; Change byte count to word count
10$:	MOVW	(R2)+,(R1)+		; Copy a byte of SCSI command
	SOBGTR	R0,10$			; Repeat for entire SCSI command
;**	BISB	UCB$B_LUN(R3),@(SP)+	; Fill in SCSI LUN field

	MOVZWL	(R2)+,-			; Set up command-specific timeout value
		SCDRP$L_DISCON_TIMEOUT(R5)

	CVTWL	(R2),R1			; Get length of send data buffer
	BLSS	20$			; Branch if negative, no system buffer
					; involved, leave SCDRP$L_BCNT unchanged
	BEQL	30$			; Branch if zero length, zero SCDRP$L_BCNT 

	BBC -                             	; Did we already allocate a buffer?
		#SCDRP$V_FLAG_CL_PRIVATE_BUFF,-
		SCDRP$L_SCSI_FLAGS(R5),15$
	MOVL	SCDRP$L_SVA_USER(R5),R1		; Get start address of buffer		
	ADDL	#2,R2				; Skip over length
        BRW	18$                             ; Branch to common code

15$:	PUSHL	R2			; Save R2
	PUSHL	R5			; Save SCDRP address
	MOVL	SCDRP$PS_KPB(R5),R5	; Get KPB address
	BSBW	MK_ALLOC_POOL		; Allocate a buffer to receive response
	POPL	R5			; Restore SCDRP address
	MOVL	R2,R1			; Copy buffer address
	POPL	R2			; Restore R2
	MOVL	R1,SCDRP$L_SVA_USER(R5)	; Save address of allocated buffer
	MOVZWL	(R2)+,SCDRP$L_BCNT(R5)	; Save length of transfer

18$:
	CLRL	SCDRP$L_PAD_BCNT(R5)	; No padding required
	INSV	(R2),#IRP$V_FUNC,#1,-	; Set/clear FUNC bit to indicate READ/
		SCDRP$IS_STS(R5)	; WRITE function
	MOVL	G^MMG$GL_BWP_MASK,R2	; Mask of BWP portion of virtual address
	EVAX_AND R1,R2,R2		; Get byte offset within page
	MOVL	R2,SCDRP$L_BOFF(R5)	; Save byte offset
	PUSHL	R3			; Save R3
	MOVL	SCDRP$L_SVA_USER(R5),R2	; Get user buffer address
	JSB	G^MMG$SVAPTECHK		; Get SVAPTE of allocated system buffer
	MOVL	R3,SCDRP$L_SVAPTE(R5)	; Save SVAPTE in SCDRP
	POPL	R3			; Restore R3
	BISB	#SCDRP$M_FLAG_S0BUF!-	; This buffer is an S0 "user" buffer
		 SCDRP$M_FLAG_BUFFER_MAPPED,-; and it has been mapped
		SCDRP$L_SCSI_FLAGS(R5)	; 
	SPI$BUFFER_MAP PRIO=HIGH	; Map the "user" buffer for read access
20$:	MOVZWL	#SS$_NORMAL,R0		; Set success status
	RET

30$:	CLRL	SCDRP$L_BCNT(R5)	; No data being transferred
	BRB	20$			; Use common exit
	.PAGE
		.SBTTL	CLEANUP_CMD	- Common cleanup for all SCSI commands
;+
; CLEANUP_CMD
;
; This routine performs common cleanup after the sending of a SCSI command 
; including unmapping the user buffer and deallocating the command buffer.
;
; INPUTS:
;
;      4(AP)	- PDT address
;      8(AP)	- SCDRP address
;
; OUTPUTS:
;
;	R2	- Destroyed
;	All other registers preserved
;-

CLEANUP_CMD::

	.CALL_ENTRY INPUT=<>,SCRATCH=<R0,R1>,PRESERVE=<R2,R4,R5>

	$ARG_DEF <pdt,-	                        ;	PDT Address
                  scdrp>			;S FF:  SCDRP Address

	MOVL	pdt(AP),R4                      ;
	MOVL	scdrp(AP),R5			;

	BBCC	#SCDRP$V_FLAG_BUFFER_MAPPED,-; Branch if no buffer has been mapped
		SCDRP$L_SCSI_FLAGS(R5),-;
		10$
	SPI$BUFFER_UNMAP		; Unmap the mapped buffer
10$:	BBCC	#SCDRP$V_FLAG_S0BUF,-	; Branch if this is not an S0 "user"
		SCDRP$L_SCSI_FLAGS(R5),-; buffer
		20$			;
	MOVL	SCDRP$L_SVA_USER(R5),R0	; Get address of S0 user buffer
	CLRL	SCDRP$L_SVA_USER(R5)	; Buffer no longer owned
	BBS -                             	; Private buffer to be handled by other code
		#SCDRP$V_FLAG_CL_PRIVATE_BUFF,-
		SCDRP$L_SCSI_FLAGS(R5),20$
	BSBW	DEALLOC_POOL		; Deallocate the buffer
20$:	MOVL	SCDRP$L_CMD_BUF(R5),R0	; Get address of command buffer
	SPI$CMD_BUFFER_DEALLOC	; Deallocate the command buffer
30$:	RET	
	.PAGE
	.SBTTL	LOG_ERROR	- Write an entry to the errorlog file
;+
; LOG_ERROR
;
; This routine writes an entry to the errorlog file. If the device is offline,
; no error is logged. This prevents the errorlog file from being filled up while
; the class driver does it its periodic polling of devices that have been set 
; offline. The assumption is that the initial error that caused the device to
; be placed offline has been logged and that subsequent errorlog entries would
; be redundent.
;
; If the device class and/or type fields in the UCB have not been initialized, 
; then fill in "TAPE" and "GENERIC SCSI TAPE" respectively so the packet can be
; properly formatted by ERF. This situation could arise if invalid inquiry data
; is received for the device, preventing these fields from being filled in
; properly.
;
; If no I/O is active for this device (for example, if an error is detected 
; during unit initialization) log a device attention. Otherwise, log a
; device error and then release the errorlog entry. This prevents errors from
; being lost if multiple errors are logged for a single QIO.
;
; Some types of errors are logged just once per system boot. For example, if 
; invalid mode sense data is returned by the target, just one INVALID_SENSE_DATA
; error is logged to prevent filling the errorlog with duplicate packets. The
; DUPLICATE_ERR_MASK table specifies those error types which should be logged
; just once. A bitmask in the UCB records those error types that have been
; logged already.
;
; INPUTS:
;
;	R5	- UCB address
;
; OUTPUTS:
;
;	All registers preserved
;-
	DRIVER_DATA
DUPLICATE_ERR_MASK:			; Bitmask of error types that can
	.LONG	-			; be logged more than once
	<1@SCSI$C_MAP_BUFFER_ERROR>!-	
	<1@SCSI$C_SEND_CMD_ERROR>!-
	<1@SCSI$C_EXTND_SENSE_DATA>!-
	<1@SCSI$C_REASSIGN_BLOCK>

	DRIVER_CODE
LOG_ERROR:

	.JSB_ENTRY INPUT=<R5>,PRESERVE=<R0,R1,R2,R7,R9,R10>
	BBS	#UCB$V_DISABL_ERRLOG,-	; Branch if errlogging is disabled
		UCB$L_MK_FLAGS(R5),35$
	BBC	#UCB$V_ONLINE,-		; Branch if device is offline (don't
		UCB$L_STS(R5),35$	; log an error)
	MOVL	UCB$L_ERROR_TYPE(R5),R7 ; Save error type
	BBCS	R7,UCB$L_ERR_MASK(R5),-	; Branch if this error type has not
		5$			; been logged yet
	BBC	R7,DUPLICATE_ERR_MASK,-	; Branch if this is a type of error
		35$			; that should be logged just once
5$:	BSBW	FILTER_ERROR		; OK to filter error?
	BLBS	R0,35$			; Branch if so
	MOVB	UCB$B_DEVTYPE(R5),R9	; Save SCSI device type
	BNEQ	10$			; Branch if device type known
	MOVB	#DT$_GENERIC_MK,-	; Fill in generic type so ERF can
		UCB$B_DEVTYPE(R5)	; translate the errlog packet

; The following code segment temporarily replaces the values TK50 and TZ30 
; with TK50S and TZ30S, respectively. This allows ERF to distinguish errlog 
; packets logged by this driver from those logged by the VS2000 driver.

10$:	IF_TZ30 11$, R5			; Branch if this is a TZ30
	IFNOT_TK50 12$, R5		; Branch if this is not a TK50
	MOVB	#DT$_TK50S,-		; Change TK50 to TK50S
		UCB$B_DEVTYPE(R5)
	BRB	12$			; Join common path

11$:	MOVB	#DT$_TZ30S,-		; Change TZ30 to TZ30S
		UCB$B_DEVTYPE(R5)	; 

12$:	MOVB	UCB$B_DEVCLASS(R5),R10	; Save DEVCLASS field
	MOVB	#DC$_TAPE,-		; Temporarily set this field to a tape
		UCB$B_DEVCLASS(R5)	; so ERF can translate packet
	MOVL	UCB$L_DDT(R5),R0	; Get DDT address
	MOVW	SCSI_ERROR_LEN_TAB-2[R7],-; Save required errorlog packet size
		DDT$W_ERRORBUF(R0)	; in the DDT
	TSTL	UCB$L_IRP(R5)		; I/O in progress?
	BEQL	20$			; Branch if not
	CALL_DEVICERR SAVE_R0R1=NO	; Log a device error
	BBCC	#UCB$V_ERLOGIP,-	; Clear error log in progress.
		UCB$L_STS(R5),30$	;
	MOVL	UCB$L_EMB(R5),R2	; Get address of error message buffer
	BEQL	30$			; Branch if none available

; The following four fields are normally filled in by IOC$REQCOM, but such
; code does not execute when UCB$V_ERLOGIP is clear, so we do it here:

	MOVL	UCB$L_STS(R5),-		; Insert final device status.
		EMB$L_DV_STS(R2)	;
	MOVL	UCB$L_ERTCNT(R5),-	; Insert final error counters.
		EMB$L_DV_ERTCNT(R2)	;
	MOVL	UCB$L_ERTMAX(R5),-	; Insert final error counters.
		EMB$L_DV_ERTMAX(R2)	;
	CLRQ	EMB$Q_DV_IOSB(R2)	; Insert zero I/O status, since we 
					; cannot know IOSB at this point.

	CALL_RELEASEMB			; Realease the errorlog buffer
	BRB	30$			; Skip no-I/O-in-progress path
20$:	CALL_DEVICEATTN SAVE_R0R1=NO	; Log a device attention
30$:	MOVB	R10,UCB$B_DEVCLASS(R5)	; Restore original devclass value
	MOVB	R9,UCB$B_DEVTYPE(R5)	; Restore device type
35$:	RSB				; Return to caller
	.PAGE
	.SBTTL	FILTER_ERROR	- Attempt to filter error
;+
; FILTER ERROR
;
; This routine attempts to filter the current error being logged by
; checking to see if the same error has been logged recently. If so,
; don't log it again. This prevents the errlog file from filling up
; if the same error is occurring repeatedly.
;
; An array in the UCB contains the last n errors logged. Each error is
; timestamped so that eventually it will go stale and a duplicate
; error will be logged.
;
; INPUTS:
;
;	R5	- UCB address
;	R7	- Error type
;
;	UCB$L_SCDRP	- Active SCDRP address
;
; OUTPUTS:
;
;	R0	- Status (LBS if error can be filtered)
;-

FILTER_ERROR:

	.JSB_ENTRY INPUT=<R5,R7>,OUTPUT=<R0>,PRESERVE=<R1,R2,R3>

	IFNOT_TK LABEL=45$,UCB=R5	; Branch if not a TK50 or TZ30, can't
					; try to filter

; First, assemble the information for the current error in R1 as follows:
;
;	+-------+------+---------------+
;	|  MBZ	|  ASC |  KEY  |  ERR  |
;	+-------+------+---------------+
;	
;	ERR - Error code
;	KEY - Sense key (extended sense data errors only)
;	ASC - Additional sense key (extended sense data errors only)
;
; This will be used later to compare against the entries in the error array
; and to save information for this error if it's not being fioltered.
					
	MOVZBL	R7,R1			; Copy error type
	CMPL	R7,-			; Extended sense data error?
		#SCSI$C_EXTND_SENSE_DATA; 
	BNEQ	10$			; Branch if not
	MOVL	UCB$L_SCDRP(R5),R0	; Get SCDRP address
	BEQL	10$			; Branch if no SCDRP active
	TSTL	SCDRP$L_TRANS_CNT(R0)	; Extended sense data available?
	BEQL	10$			; Branch if not
	MOVL	SCDRP$L_SVA_USER(R0),R0	; Get address extended sense
	BEQL	10$			; Branch if none available
	INSV	SCSI_XS$B_KEY(R0),-	; Get sense key
		#8,#8,R1
	INSV	SCSI_XS$B_ADDNL_CODE30(R0),- ; Get additional sense code
		#16,#8,R1		;
	CMPB	SCSI_XS$B_KEY(R0),-	; Recovered error?
		#SCSI$C_RECOVERED_ERROR	;
	BEQL	45$			; Yes, don't filter it
	CMPB	SCSI_XS$B_KEY(R0),-	; Medium error?
		#SCSI$C_MEDIUM_ERROR	;
	BEQL	45$			; Yes, don't filter it

; Next, scan the error array to see if an identical error has already
; been logged.

10$:	MOVL	#2,R3			; Number of entries in error array
	MOVAL	UCB$B_ERR_ARRAY(R5),R2	; Get address of error array
20$:	CMPL	(R2),G^EXE$GL_ABSTIM	; Stale entry?
	BLSSU	30$			; Branch if so
	CMPL	R1,4(R2)		; Matching error?
	BEQL	60$			; Branch if so, OK to filter
	ADDL	#8,R2			; Advance to next entry in array
	SOBGTR	R3,20$			; Repeat for all entries in array

; This error was not found in the array. Push all the entries in the array
; down (the array is ordered on expiration time) and add this error to the
; array.

30$:	MOVL	#2-1,R3			; Number of entries in array less 1
	MOVAQ	UCB$B_ERR_ARRAY(R5)[R3],R2 ; Get address just beyond error array
40$:	MOVL	-(R2),8(R2)		; Push an entry down in the array
	MOVL	-(R2),8(R2)
	SOBGTR	R3,40$			; Repeat for entire array
	ADDL3	#60,G^EXE$GL_ABSTIM,-	; Set expiration time for new entry
		(R2)+			; to one minute from now
	MOVL	R1,(R2)			; Save new entry
45$:	CLRL	R0			; Can't filter this entry
50$:	RSB				; Return to caller	
	
60$:	MOVL	#1,R0			; This error can be filtered
	BRB	50$			; Use common exit
	.PAGE
	.SBTTL	SET_CONN_CHAR	- Modify connection characteristics
;+
; SET_CONN_CHAR
;
; This routine is called at the end of PACKACK to initialize the connection
; characteristics, which specify such things as whether the device supports 
; disconnect and synchronous operation, and the bus busy, arbitration, and
; selection retry counters.
;
; This routine first allocates a connection characteristics buffer on the
; kernel process stack, then calls SPI$CONNECTION_CHAR_GET to get the current
; values of the connection characteristics, modifies the values of interest,
; and calls SPI$CONNECTION_CHAR_SET to set up the new values. This allows
; the class driver to change a subset of the characteristics and leave the
; rest unmodified.
;
; INPUTS:
;
;	R3	- UCB address
;	R4	- SPDT address
;	R5	- SCDRP address
;
; OUTPUTS:
;
;	R0	- Status
;	R1,R2	- Destroyed
;	All other registers preserved
;-


SET_CONN_CHAR:

	.JSB_ENTRY INPUT=<R3,R4,R5>,OUTPUT=<R0>,SCRATCH=<R1,R2>
	SUBL	#SPI$K_CC_LENGTH,SP	; Allocate buffer on the KP stack
	MOVL	SP,R2			; Copy buffer address
	MOVL	#SPI$K_CC_NUM_ARGS,-	; Set argument count in buffer
		SPI$IL_CC_COUNT(R2)
	SPI$CONNECTION_CHAR_GET		; Get current connection characteristics
	BLBC	R0,10$			; Branch on error
	ASSUME	SET_CON$M_DISC EQ 1
	EXTZV	#UCB$V_DISCONNECT,#1,-	; Fill in disconnect flag
		UCB$L_MK_FLAGS(R3),-	;
		SET_CON$L_CON_FLAGS(R2)	;
	BISL	#SET_CON$M_NORETRY,-	; Disable port driver command retry
		SET_CON$L_CON_FLAGS(R2)	;
	ASSUME	SET_CON$M_SYN EQ 1
	EXTZV	#UCB$V_SYNCHRONOUS,#1,-	; Fill in synchronous flag
		UCB$L_MK_FLAGS(R3),-	;
		SET_CON$L_SYN_FLAG(R2)	;
	SPI$CONNECTION_CHAR_SET		; Set the connection characteristics
10$:	ADDL	#SPI$K_CC_LENGTH,SP	; Clear buffer off the stack
	BLBS	R0,20$			; Branch if success status
	MOVL	#SS$_CTRLERR,R0		; Otherwise, return a reasonable status
20$:	RSB				; Return to caller



	.PAGE
	.SBTTL	CHECK_SKIPFILE_SUPPORT - Check if device supports READ POSITION command
;+
; CHECK_SKIPFILE_SUPPORT
;
; This routine is called by IO$_PACKACK code in order to determine whether
; the device supports the READ POSITION command.  If it does, then the
; ucb$l_mk_flags bit SKIPFILE_SUPPORTED will be set.  That bit is later
; checked when doing IO$_SKIPFILE operations;  if READ POSITION is supported,
; a skipfiles operation may be done most efficiently by sending a Space
; Filemarks to advance the tape, followed by a Read Position to obtain
; position information needed to fill in ucb$l_record.
;
; INPUTS:
;
;	R3	- UCB address
;	R4	- PDT address
;	R5	- SCDRP address
;
; OUPUTS:
;
;	R0	- Status
;	R1,R2	- destroyed
;-
CHECK_SKIPFILE_SUPPORT:

	.JSB_ENTRY INPUT=<R3,R4,R5>,OUTPUT=<R0>,SCRATCH=<R1,R2>
	BBC	#UCB$V_SKIPFILE_SUPPORTED,-  ; If cmd was previously declared
                UCB$L_MK_FLAGS(R3),40$      ; unsupported, don't even try.
	MOVAL	CMD_READ_POSITION,R2	    ; Address of READ POSITION command
	SETUP_CMD			    ; Perform setup for SCSI command
        BISL    #UCB$M_READPOSCHK_IN_PROG,- ; Set READ POSITION chk-in-prog
                UCB$L_MK_FLAGS(R3)
	SEND_COMMAND_ORDERED		    ; Send the SCSI command
        BICL    #UCB$M_READPOSCHK_IN_PROG,- ; Clear READ POSITION chk-in-prog
                UCB$L_MK_FLAGS(R3)
	BLBC	R0,20$			    ; Branch on error
	CMPL	SCDRP$L_TRANS_CNT(R5),-     ; Sufficient data returned?
		#20			    ; READ POS data len should equal 20
	BLSS	20$			    ; No, so declare cmd unsupported
	MOVL	SCDRP$L_SVA_USER(R5),R0	    ; Get address of data
	BBS	#BPU_BIT,-		    ; If block position unknown,
		(R0),20$		    ; declare the command unsupported

; Sanity check:  the value just returned should equal UCB$L_RECORD.

	MOVAB	8(R0),R0		    ; Prepare to get READ POS value
	MOVAB	UCB$L_RECORD(R3),R1         ; Prepare to get UCB$L_RECORD
	.REPT	4
	CMPB	-(R0),(R1)+		    ; Compare READ POS, UCB$L_RECORD
	BNEQ	20$			    ; If unequal, declare READ POS
	.ENDR                               ;   cmd to be unsupported
	
	BISL2	#UCB$M_SKIPFILE_SUPPORTED,-  ; READ POSITION command supported
		UCB$L_MK_FLAGS(R3)		
30$:	CLEANUP_CMD			    ; Clean up command
40$:	MOVL	#SS$_NORMAL,R0		    ; Always return success
	BICL	#UCB$M_FIRST_READPOS_CMD,-  ; The first one is done 
		UCB$L_MK_FLAGS(R3)		
	RSB

; Error path:  READ POSITION cannot be supported.

20$:    BICL2	#UCB$M_SKIPFILE_SUPPORTED,-  ; READ POSITION command unsupported
		UCB$L_MK_FLAGS(R3)		
	BBS	#UCB$V_FIRST_READPOS_CMD,-   ; Suppress logging the very first
		UCB$L_MK_FLAGS(R3),30$       ; one (eg. for devices that do not
					     ; support the READ POSITION cmd)
	LOG_ERROR -
		TYPE=SEND_CMD_ERROR,-
		VMS_STATUS=#SS$_NORMAL,-
		UCB=R3
	BRW	30$			    ; Common exit


       .PAGE
	.SBTTL	CHECK_COMPACTION_SUPPORT - Check if device supports data compaction
;+
; CHECK_COMPACTION_SUPPORT
;
; This routine calls the SETUP_COMPACTION routine attempting to enable
; compaction on the device. If it succeeds in enabling compaction, it is
; assumed that the device supports compaction and the MT2$M_COMP_SUP bit is
; set in UCB$L_DEVDEPND2, otherwise it is cleared. If supported, compaction 
; set back to whatever state it was previously in (MT2$M_COMP_ENA).
;
; INPUTS:
;
;	R3	- UCB address
;	R4	- PDT address
;	R5	- SCDRP address
;
; OUPUTS:
;
;	R0	- Status
;	R1,R2	- destroyed
;-
CHECK_COMPACTION_SUPPORT:

	.JSB_ENTRY INPUT=<R3,R4,R5>,OUTPUT=<R0>,SCRATCH=<R1,R2>
	CLRB	UCB$B_SAVED_COMP(R3)	; Assume compaction is off
	BBC	#MT2$V_COMP_ENA, -	; Check current compaction state
		UCB$L_DEVDEPND2(R3), 5$; 
	MOVB	#1, UCB$B_SAVED_COMP(R3); Flag: compaction was enabled
5$:	CMPB	#2,-			; Only attempt compaction on SCSI-2+
		UCB$B_SCSI_VERSION(R3)	; compliant devices
	BGTR	10$			; Branch if scsi-1

	DISABLE_ERRLOG			; Temporarily disable errorlogging
        BISL    #UCB$M_COMPCHK_IN_PROG,-; Set compaction check in progress flag
                UCB$L_MK_FLAGS(R3)
	MOVL	#1, R0			; Attempt to turn on compaction
	BSBW	SETUP_COMPACTION	
        BICL    #UCB$M_COMPCHK_IN_PROG,-; Clear compaction check in progress fl
                UCB$L_MK_FLAGS(R3)
	REENABLE_ERRLOG			; Reenable errorlogging
	BLBC	R0,10$			; LBC = not supported
	BISL	#MT2$M_COMP_SUP,-	; Set DEVDPEND2 comp supported bit
		UCB$L_DEVDEPND2(R3)	;
	TSTB	UCB$B_SAVED_COMP(R3)	; If compaction was already on, 
	BNEQ	20$			; leave it on
	CLRL	R0			; Compaction off flag
	BSBW	SETUP_COMPACTION	; Turn it back off
	BRB	20$

10$:	BICL	#MT2$M_COMP_SUP,-	; Clear DEVDPEND2 comp supported bit
		UCB$L_DEVDEPND2(R3)	;
20$:	MOVL	#1, R0			; Always success
	RSB				; Return to caller

	.PAGE
	.SBTTL	SETUP_COMPACTION - Use mode sense/select page 10 for compaction
;+
; SETUP_COMPACTION
;
; This routine sends a MODE SENSE command which requests page 10. This is
; the Device Configuratin Page. At offset SCSI_MSEL$B_COMP is a byte
; which specifies if data compaction enabled or not. 
;
; INPUTS:
;
;	R0	- desired compaction state (0 = off, 1 = on)
;                 Values other than 0 or 1 are illegal.
;	R3	- UCB address
;	R4	- PDT address
;	R5	- SCDRP address
;
; OUPUTS:
;
;	R0	- Status
;	R1,R2	- destroyed
;-

	ASSUME MT$K_DEFAULT EQ 0

SETUP_COMPACTION:

	; 
	; issue a MODE_SENSE command to get the contents of page 10
	;

	.JSB_ENTRY INPUT=<R0,R3,R4,R5>,OUTPUT=<R0>,SCRATCH=<R1,R2>,-
		   PRESERVE=<R7,R9>


; Allocate a local copy of the mode page descriptors on the stack

	SUBL3	#DESCRIPTOR_SIZE,SP,R7		; Size of descriptor array 
	BICL	#7,R7				; Quadword align
	SUBL	#DESCRIPTOR_SIZE+8,SP		; Account for desc + unalignment
	PUSHR	#^M<R0,R3,R4,R5>
	MOVC5	#0,.,#0,#DESCRIPTOR_SIZE,(R7)	; Initialize the desc area
	MOVC3	#DESCRIPTOR_SIZE,-		; Copy the descriptors
		DESCRIPTOR_BASE,(R7)	
	POPR	#^M<R0,R3,R4,R5>

	MOVB	R0, UCB$B_COMP_STATE(R3)	; Save requested compaction state
	MOVB	UCB$B_COMP_STATE(R3),-          ; Set desired compaction state
		<DEV_CONFIG_SEL_DATA_COMP_DESC+MODE_DESC$L_DESIRED>(R7)

	PUSHL	#^X10				; Device configuration pg code
        PUSHAL	DEV_CONFIG_PAGE_DESC(R7)	; Address of descriptors
	PUSHL	R5				; SCDRP Address
	CALLS	#3,DO_MODE_PAGE			; Process the page
	BLBC	R0,50$				; Branch on error

;;;	MOVL	UCB$L_COMP_PAGE(R3),R1	; Get address of compaction page info
;;;	MOVZBL	(R1)+,R0		; Get length of info
;;;	MOVB	R0,5(R2)		; Put parameter list length in cmd 
;;;	MOVZBW	R0,9(R2)		; Put BCNT in SCSI_CMD description
;;;	SETUP_CMD			; Perform setup for SCSI command
;;;	CLRL	UCB$L_COMP_PAGE(R3)	; No longer valid pool address

	BICL	#MT2$M_COMP_ENA,-       ; Assume compaction is disabled
                UCB$L_DEVDEPND2(R3)
	TSTB	UCB$B_COMP_STATE(R3)	; Check for state of comp_enabled flag
	BEQL	40$			;   0 == compaction disabled
	BISL	#MT2$M_COMP_ENA,-	; Indicate compaction is enabled
		UCB$L_DEVDEPND2(R3)	;

40$:	ADDL	#DESCRIPTOR_SIZE+8,SP	; Account for desc + unalignment
	RSB				; Return to caller
50$:	MOVL	#SS$_DRVERR,R0
	BRW	40$

	.PAGE
	.SBTTL	+
	.SBTTL	+ TRACE ROUTINES  
	.SBTTL	+
	.SBTTL	Trace buffer, symbols
;+
; TRACE_BUFFER
;
; The driver uses a ring buffer to trace at the QIO, SCSI command, and SCSI 
; cmd/status byte level. For each QIO issued, there can be zero or more SCSI 
; commands sent, each of which can result in SCSI command, message, and status 
; bytes. The trace buffer has the following format:
;
;	+-----------------------+ <---- Start of trace buffer header
;	|    Size of trace buf	|
;	+-----------------------+
;	|    Trace header size	|
;	+-----------------------+
;	|    Total QIOs in buf	|
;	+-----------------------+
;	|    Size of each QIO	|
;	+-----------------------+
;	|    QIO header size	|
;	|-----------------------+
;	|    # SCSI cmds/QIO	|
;	+-----------------------+
;	|    Size of SCSI cmd	|
;	+-----------------------+
;	|    Offset to SCSI sts	|
;	+-----------------------+
;	|    Current QIO #	|
;	+-----------------------+
;	|			|
;	|	RESERVED	|
;	|			|
;	+-----------------------+ <---- End of trace buffer header
;	|	  QIO 0		|
;	+-----------------------+
;	|	  QIO 1		|
;	+-----------------------+
;	|	    .		|
;	|	    .		|
;	+-----------------------+
;	|	  QIO n-1	|
;	+-----------------------+
;
; For each QIO, the following information is logged:
;
;	+-----------------------+ <---- Start of QIO header
;	|    QIO number		|
;	+-----------------------+
;	|    Valid flag		|
;	+-----------------------+
;	|    SCSI command cnt	|
;	+-----------------------+
;	|    SCSI bus ID	|
;	+-----------------------+
;	|    Function code	|
;	+-----------------------+
;	|    Media (LBN)	|
;	+-----------------------+
;	|    Byte count		|
;	+-----------------------+
;	|    Byte offset	|
;	+-----------------------+
;	|    QIO status		|
;	+-----------------------+
;	|    Sequence number	|
;	+-----------------------+
;	|    			|
;	|    RESERVED		|
;	|    			|
;	+-----------------------+ <---- End of QIO header
;	|    SCSI CMD 0		|
;	+-----------------------+
;	|    SCSI CMD 1		|
;	+-----------------------+
;	|	    .		|
;	|	    .		|
;	+-----------------------+
;	|    SCSI CMD n-1	|
;	+-----------------------+
;
; Finally, for each SCSI command, the following information is logged:
;
;	+-----------------------+
;	|    CMD byte cnt	|
;	+-----------------------+
;	|    SCSI CMD bytes	|
;	+-----------------------+
;	|    SCSI status byte	|
;	+-----------------------+
;
; Note that CMD byte count is kept to allow the tracing routines to know where 
; to put the next CMD byte and to allow the user to know how much data to 
; interpret. Also note that, in order to make it easier to interpret, the data 
; is not packed in as well as it could be.
;-

	$DEFINI	TRACE_BUFFER_HEADER

$DEF	TR$L_SIZE		.BLKL	1
$DEF	TR$L_HEADER_SIZE	.BLKL	1
$DEF	TR$L_TOTAL_QIOS		.BLKL	1
$DEF	TR$L_CURRENT_QIO	.BLKL	1
$DEF	TR$L_QIO_SEQNUM		.BLKL	1
$DEF	TR$L_QIO_SIZE		.BLKL	1
$DEF	TR$L_QIO_HEADER_SIZE	.BLKL	1
$DEF	TR$L_SCSI_CMDS_PER_QIO	.BLKL	1
$DEF	TR$L_SCSI_CMD_SIZE	.BLKL	1
$DEF	TR$L_SCSI_MSG_OFFSET	.BLKL	1
$DEF	TR$L_SCSI_STATUS_OFFSET	.BLKL	1
$DEF	TR$C_HEADER_SIZE

	$DEFEND	TRACE_BUFFER_HEADER

	$DEFINI	TRACE_BUFFER_QIO_HEADER

$DEF	TR$L_QIO_NUMBER		.BLKL	1
$DEF	TR$L_VALID_FLAG		.BLKL	1
$DEF	TR$L_SCSI_CMD_CNT	.BLKL	1
$DEF	TR$L_DEVICE_NAME	.BLKL	1
$DEF	TR$L_SCSI_UNIT_NUMBER	.BLKL	1
$DEF	TR$L_QIO_FUNC		.BLKL	1
$DEF	TR$L_QIO_MEDIA		.BLKL	1
$DEF	TR$L_QIO_BCNT		.BLKL	1
$DEF	TR$L_QIO_BOFF		.BLKL	1
$DEF	TR$L_QIO_STATUS		.BLKL	1
$DEF	TR$L_SEQNUM		.BLKL	1
$DEF	TR$L_RECORD		.BLKL	1
$DEF	TR$L_DEVDEPEND		.BLKL	1
$DEF	TR$C_QIO_HEADER_SIZE

	$DEFEND	TRACE_BUFFER_QIO_HEADER

TR$TOTAL_QIOS = 200
TR$SCSI_CMDS_PER_QIO = 0
TR$CMD_BYTES = 12
TR$MSG_BYTES = 12
TR$STATUS_BYTES = 1
TR$SCSI_MSG_OFFSET = TR$CMD_BYTES+4
TR$SCSI_STATUS_OFFSET = TR$CMD_BYTES+4 + TR$MSG_BYTES+4

TR$SCSI_CMD_SIZE = 	<<TR$CMD_BYTES+4 + TR$STATUS_BYTES+4> + 15> & ^C15
TR$QIO_SIZE = 		<<TR$C_QIO_HEADER_SIZE + <TR$SCSI_CMDS_PER_QIO * TR$SCSI_CMD_SIZE>> + 15> & ^C15
TR$TRACE_BUFFER_SIZE =	TR$C_HEADER_SIZE + <TR$TOTAL_QIOS * TR$QIO_SIZE>

	DRIVER_DATA
TR$TRACE_BUFFER_ADDR:
	.LONG	0


	.PAGE
	.SBTTL	SETUP_TRACE	- Allocate and set up trace buffer
;+
; SETUP_TRACE
;
; This is a fork routine queued by unit init to allocate a trace buffer, which
; is used to trace QIOs through the driver. The auxstruc field in the CRB is
; filled in with the address of a cell which contains the address of the trace
; buffer. Since the driver can have more than one unit, only the first
; call to this routine actually performs the allocation. Since pool allocation 
; can stall this thread, the TR$TRACE_BUFFER_ADDR cell is given a value of
; 1 (positive integer) to indicate that poll allocation is on progress. This
; prevents a second thread from allocating another buffer.
;
; INPUTS:
;
;	R5	- CRB address
;
; OUTPUTS:
;
;	R0-R3	- Destroyed
;	All other registers preserved
;
;	CRB$L_AUXSTRUC	- Address of cell containing trace buffer address
;-

	DRIVER_CODE
SETUP_TRACE:

	FORK_ROUTINE			; INPUT=<R3,R4,R5>,SCRATCH=<R0,R1,R2>
	.IF DEFINED DEBUG
	TSTL	TR$TRACE_BUFFER_ADDR	; Trace buffer already set up?
	BNEQ	10$			; Branch if so
	INCL	TR$TRACE_BUFFER_ADDR	; Trace buffer setup in progress
	MOVL	#TR$TRACE_BUFFER_SIZE,R1; Get size of trace buffer
	JSB	G^EXE$ALONONPAGED	; Allocate pool

	BLBS	R0,5$			; Branch if success
	CLRL	TR$TRACE_BUFFER_ADDR	; Trace buffer not allocated
	BRB	20$			; Use common exit
5$:	PUSHL	R2			; Save reg
	MOVC5	#0,(SP),#0,R1,(R2)		; Initialize the packet
	POPL	R2			; Restore reg
	MOVL	#TR$TRACE_BUFFER_SIZE,-	; Save trace buffer size
		TR$L_SIZE(R2)		;
	MOVL	#TR$C_HEADER_SIZE,-	; Save header size
		TR$L_HEADER_SIZE(R2)	;
	MOVL	#TR$TOTAL_QIOS,-	; Save total number of QIOs in
		TR$L_TOTAL_QIOS(R2)	; buffer
	MOVL	#TR$QIO_SIZE,-		; Save size of QIO
		TR$L_QIO_SIZE(R2)	;
	MOVL	#TR$C_QIO_HEADER_SIZE,-	; Save size of QIO header
		TR$L_QIO_HEADER_SIZE(R2); 
	MOVL	R2,TR$TRACE_BUFFER_ADDR	; Save trace buffer address
10$:
	.ENDC

20$:	MOVAL	TR$TRACE_BUFFER_ADDR,-	; Save address of cell pointing to 
		CRB$L_AUXSTRUC(R5)	; trace buffer in CRB
	RSB

		
	.PAGE
	.IF DEFINED DEBUG
	.SBTTL	TRACE_QIO	- Trace QIO requests
;+
; TRACE_QIO
;
; This routine logs information from the current QIO packet including the
; function code, MEDIA, BCNT, and BOFF fields.
;
; INPUTS:
;
;	R3	- IRP address or 0 if UNIT INIT
;	R5	- UCB address
;
; OUTPUTS:
;
;	All registers preserved
;-

TRACE_QIO:

	.JSB_ENTRY INPUT=<R3,R5>,PRESERVE=<R0,R1,R2,R4>
	MOVL	TR$TRACE_BUFFER_ADDR,R0	; Get trace buffer address
	BGEQ	30$			; Branch if none
	ADDL3	#1,-			; Bump QIO number
		TR$L_CURRENT_QIO(R0),R1	;
	CMPL	R1,#TR$TOTAL_QIOS	; Time to wrap QIO number?
	BLSS	10$			; Branch if not
	CLRL	R1			; Wrap QIO number
10$:	MOVL	R1,TR$L_CURRENT_QIO(R0)	; Save QIO number
	MULL3	#TR$QIO_SIZE,R1,R2	; Get offset of this QIO in trace buffer
	ADDL	R0,R2			; Address to place info for this QIO
	ADDL	#TR$C_HEADER_SIZE,R2	; Account for trace buffer header
	PUSHR	#^M<R0,R2,R3,R5>	; Save regs
	MOVC5	#0,(SP),#0,#TR$QIO_SIZE,-	; Clear entire buffer used to trace
		(R2)			; this QIO
	POPR	#^M<R0,R2,R3,R5>	; Restore regs
	ADDL3	#TR$C_QIO_HEADER_SIZE,-	; Get address of place to store
		R2,R1			; first SCSI command
	MOVL	TR$L_CURRENT_QIO(R0),-	; Save QIO number
		(R2)+			;
	MOVL	#1,(R2)+		; Set valid flag
	CLRL	(R2)+			; Clear SCSI command count
	MOVL	UCB$L_DDB(R5),R1	; Get DDB address
	MOVL	DDB$T_NAME_STR(R1),(R2)+; Save SCSI device name
	MOVZWL	UCB$W_UNIT(R5),(R2)+	; Trace SCSI bus ID
	TSTL	R3			; IRP address of 0 (unit init)?
	BEQL	15$			; Branch if so
	MOVL	IRP$L_FUNC(R3),(R2)+	; Trace QIO function code
	MOVL	IRP$L_MEDIA(R3),(R2)+	; Trace media number (LBN)
	MOVL	IRP$L_BCNT(R3),(R2)+	; Trace byte count
	MOVL	IRP$L_BOFF(R3),(R2)+	; Trace byte offset
	BRB	20$			; Skip unit init path
15$:	MCOML	#0,(R2)+		; Special function code for UNIT INIT
	ADDL	#12,R2			; Skip MEDIA, BCNT, BOFF fields
20$:	MOVL	R2,UCB$L_TR_QIO_STS(R5)	; Save address to put QIO status
	MCOML	#0,(R2)+		; Initialize QIO status
	MOVL	TR$L_QIO_SEQNUM(R0),-	; Save QIO sequence number
		(R2)+			;
	INCL	TR$L_QIO_SEQNUM(R0)	; Bump QIO sequence number
30$:	RSB				; Return to caller
	.ENDC				; .IF DEFINED DEBUG
	.PAGE
	.SBTTL	TRACE_QIO_STAT	- Save final status from QIO in trace buffer
;+
; TRACE_QIO_STAT
;
; This routine saves the final QIO status in the trace buffer.
;
; INPUTS:
;	R5	- UCB address
;	
; OUTPUTS:
;	R2	- Destroyed
;
;-

TRACE_QIO_STAT:

	.JSB_ENTRY INPUT=<R5>,SCRATCH=<R2>
	MOVL	UCB$L_TR_QIO_STS(R5),R2	; Get address to put status
	BEQL	10$			; Branch if uninitialized
	MOVL	R0,(R2)			; Save status in trace buffer
	ASSUME	TR$L_QIO_STATUS+8 EQ TR$L_RECORD
	ASSUME	TR$L_QIO_STATUS+12 EQ TR$L_DEVDEPEND
	MOVL	UCB$L_RECORD(R5),8(R2)	; Save current record number
	MOVL	UCB$L_DEVDEPEND(R5),-	; Save DEVDEPND info
		12(R2)			;
10$:	RSB

MK_END:					; Last location in driver
	.END
