	.TITLE	DKDRIVER - VAX/VMS SCSI Disk Class Driver
	.SBTTL	Copyright Notice
	.IDENT	'X-41'
	.DISABLE	GLOBAL		; Disable Globals
	.ENABLE		SUPPRESSION	; Suppression list of unreferenced symbols STB
 	.SHOW		MEB		; Show Macro Expansion Binary
;	.SHOW		ME,MC,MD	; Show Macro Expansion, Conditionals, Definitions

;****************************************************************************
;*									    *
;*  COPYRIGHT © 1978, 1980, 1982, 1984, 1991, 1992, 1993, 1994, 1995 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.		    *
;*									    *
;*									    *
;****************************************************************************

	.PAGE
	.SBTTL	Revision History

;+
;
; FACILITY:
;
;	VAX/VMS SCSI Disk Class Driver
;
; ABSTRACT:
;
;	This module contains the disk class driver to control read/write and
;	read-only disks on the SCSI bus. The driver can have at most one active
;	I/O per device on the SCSI bus at any one time. This driver relies on
;	the SCSI port driver to provide the low-level communication of commands
;	and data over the SCSI bus.
;
; AUTHOR:
;
;	Jim Klumpp	4-May-1988
;
; REVISION HISTORY:
;
;	********************************************************************************
;
;	Driver weaknesses:
;		DSE operations should perform bad block replacement.
;		WRITE with DATACHECK should perform bad block replacement.
;		READ with DATACHECK should perform bad block replacement.
;
;	********************************************************************************
;
;	X-41	GCE212	Glenn C. Everhart		12-Feb-1996
;		Respond to EVMS-GOBLIN QAR #16 problem by adding
;		".PRESERVE ATOMICITY" brackets around BBSS and BBCC
;		in SET_SINGLE and CLEAR_SINGLE macros. In rare cases
;		it was possible to have two threads enter a region
;		delimited by these macros since if atomicity is not
;		preserved, the bit is loaded, then tested and set. This
;		caused a crash in badblock replacement. Also added some
;		more comments to explain the macro mods in X-40 in more
;		detail.
;	X-40	GCE126	Glenn C. Everhart		26-Jan-1996
;		To get Fujitsu optical working, I note that the common SCSI
;		modesense is being more picky than need be about format
;		of request sense data. This drive does not have some mode
;		sense pages (rigid disk format, floppy format) which are
;		asked for but have no required bits.
;		To localize changes, I added an assembly time field to the
;		MD_DSC macro which ORs together all flags bits, and added
;		code such that if no required pages are present, the NODATA
;		error is accepted for certain pages where also a NOSUCHSEC
;		error would also be expected. This code is conditionally 
;		assembled so that if any required bits should be added to
;		the tables, then it will not be present, nor will it be
;		necessary to make any extra edits other than to the MD_DSC
;		calls. This keeps the selection changes isolated from the
;		SCSI modesense common routines so that it only applies to
;		DKDRIVER, which does not need the pages in question.
;       X-39    JCH710a John C. Hallyburton, Jr.        18-Jan-1996
;		Happy birthday Kevin Costner. DKDRIVER maintains a local bit
;		UCB$M_CMDQ in status longword UCB$L_DK_FLAGS. SYSINIT needs
;		to get at this bit in order to verify that in SCSI clusters
;		a multihost system disk supports TCQ. We will do this by
;		keeping a "shadow copy" of the TCQ bit in UCB$Q_DEVDEPEND2
;		bit 24, setting and clearing it along with UCB$M_CMDQ. This
;		involves a total of 4 changes and parallels the way X-35˝
;		handles the "removeable media" bit.
;
;	X-38	GCE115  Glenn C. Everhart		15-Jan-1996
;		Add in Jim Dunham's fix to have DKdriver honor the DQUE
;		(disable tagged command queueing) mode parameter if it
;		finds it. Also disallow AWRE/ARRE in SCSI1 devices
;		since implementations often just recorded whatever data
;		was recovered onto good blocks even if the original data
;		could not be recovered correctly, thus causing silent data
;		loss.
;	X-37	GCE512	Glenn Everhart			26-Dec-1995
;		Remove auto writelock of optical WORM drives again (seems
;		to have been replaced since X-35). Also install mods so that
;		AWRE, ARRE, TB, and PER bits are preferred, not required,
;		for basic operation, to permit many 3rd party disks to work.
;		However ensure that the test for cluster mounts on shared
;		busses still requires the same characteristics.
;       X-36    MCY     Mary Yuryan                     28-Nov-1995
;               Add support for high SCSI ID's (wide devices).  In
;               DK_KP_UNIT_INIT change check for 7 valid SCSI ID's to 31.
;               Change LUN calculation to a clear for higher LUN's.
;
;               RCL004  Rick Lord                       13-Dec-95
;               Fold described by SCT-ZOMBIE #44:
;               In SET_UNIT_ONLINE and DK_END_MNTVER, make sure that the Golden
;               IRP is never sent off to be post processed. Its FLINK is used
;               as a means of determining whether it's queued or not, but
;               DKDRIVER otherwise has no means of determining whether it's on
;               the I/O queue or the Post Processing queue. Since the former is
;               an absolute queue and the latter a self-relative queue, the
;               same instruction cannot be used to remove the IRP from either
;               queue - this has led to at least one crash. To prevent the
;               Golden IRP from every being posted, add explicit checks for it
;               in the above routines.
;
;		During INQUIRY processing store SCSI device type and whether or
;		not it has removeable media in UCB$Q_DEVDEPEND2, where it will 
;		be visible to GETDVI users with item code of DVI$_DEVDEPEND3
;		(the code refers to the third device-dependent longword - both
;		device-dependent UCB fields are quadwords).
;
;       X-35  GCE001            Glenn Everhart          01-Dec-1995
;               Modify VALIDATE_GEOMETRY and a couple other places so that
;               geometry will be VMS-legal. In other words, number of cylinders
;               must fit in 16 bits, so use 32 by 32 by n, or 96 by 96 by n,
;               or 255 by 255 by n geometries if cylinder count comes out
;               bigger than 65535 via conventional fixes. Also arrange that
;               a geometry is generated if device gives a block count that
;               is nonzero if tracks or sectors comes out zero. This allows
;               functioning with opticals.
;
;	X-34	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-33  RCL003		Rick Lord		 2-Oct-1995
;
;		1. In or for subroutine CD_GET_SENSE::
;
;		   o Define symbol CMD_MODE_SENSE$R_ALLOC_LENGTH at SCSI_CMD macro which 
;		     creates the MODE SENSE command CDB
;		   o Use R0 (CDB address) instead of R1 (AUCB address) to set CDB page code
;		   o Use CDB symbol CMD_MODE_SENSE$R_ALLOC_LENGTH to set allocation length
;		     of data for MODE SENSE command instead of SCSI$MPH6$B_BLOCK_LENGTH 
;		     (Mode Parameter Header Block Descriptor length)
;
;		2. In or for subroutine CD_SET_VOLUME:
;
;		   o Define symbol CMD_MODE_SELECT$R_ALLOC_LENGTH at SCSI_CMD macro which 
;		     creates the MODE SELECT command CDB
;		   o Use the amount of data which was actually returned from the MODE SENSE
;		     command as an allocation length for MODE SELECT instead of a constant
;		     calculated from what should have been returned - this is to allow for
;		     an other-than-standard sized page (it happens)
;		   o Use CDB symbol CMD_MODE_SELECT$R_ALLOC_LENGTH to set allocation length
;		     of data for MODE SELECT command instead of SCSI$MPH6$B_BLOCK_LENGTH 
;		     (Mode Parameter Header Block Descriptor length)
;		   o The MODE SENSE data is being modified and sent back to the device as
;		     MODE SELECT data, so delete the clear of SCSI$MPH6$B_DATA_LENGTH, which
;		     tells how much data we got and how much is being sent back.
;		   o Change definition of SENSE_VOLUME to SELECT_VOLUME and modify it to 
;		     step past the Mode Parameter Header and Block Descriptor, then use the
;		     offset of Channel Volume instead of it's size.
;		   o Restore AUCB address to R0 instead of R1, since the MODE SELECT data 
;		     address is already in R1 (the volume was previously being read out of
;		     the mode select data instead of the AUCB).
;
;		3. Subroutine CD_GET_VOLUME:
;
;		   o Step past the Mode Parameter Header and Block Descriptor to read the
;		     Channel Volumes out of the Audio Control page into the AUCB
;
;		4. Change all CMD_PLAY_ADUIO10$* symbols to CMD_PLAY_AUDIO10$*
;
;	X-32  RCL002		Rick Lord		21-Jun-95
;
;	     	1. In FORCE_ONE_ERROR, clear SCDRP$V_FLAG_S0BUF in SCDRP$L_SCSI_FLAGS on
;		   successful completion of a READ LONG command to keep the data buffer
;		   from being deallocated by CLEANUP_CMD. This leaves the buffer available
;		   to be inverted and copied into the WRITE LONG buffer so the routine
;		   can complete it's corruption of the block.
;
;		2. In DK_CANCEL, change the MOVAB of IRP$L_IOQFL to a MOVL of the same
;		   cell. As is, it was guaranteed to bail out on the first IRP (which
;		   was really the address of UCB$Q_IRP_LIST) and never mark anything
;		   to expedite cancellation.
;
;	X-31  GWW002		Grace Wang		26-May-1995
;		Fix << the MOUNT/CLUSTER/NOWRITE does not write lock on 
;		the node which own the disk>> problem
;
;	X-30  RCL0001		Rick Lord		3-May-95
;		Fold of X18U9 into Theta: Add bounds-checking code at label 36$
;		in DK_REG_DUMP to make sure that the routine does not overflow the
;		buffer allocated by the error logging routine.
;
;	X-29  JFD0830		James F. Dunham		 2-MAY-1995
;		Fold of X18U8 into Theta
;
;	X-28  JFD0818		James F. Dunham		 6-APR-1995
;		Fold of X18U6 and X18U7 into Theta
;
;       X-27  SCS             Sue Sommer              21-Mar-1995
;		JFD0814		James F. Dunham		23-MAR-1995
;		o Allow certain errors to be converted to SS$_MEDOFL
;		  in order to permit retries.
;		o Make sure CD-ROM block size processing is a required mode
;		  sense operation.
;
;	X-26  RCL		Rick Lord		29-Mar-95
;
;		- Move the code which sets SINGLE_RW in IO_RW_COMMON to after
;		  the checks in which we validate that the volume and block
;		  number are valid, to prevent our leaving SINGLE_RW set and
;		  leaving the device permanently busy.
;
;       X-25  SCS             Sue Sommer              17-Mar-1995
;		- Convert any errors from PROCESS_MODE_INFO to SS$_MEDOFL
;		  so that retries are possible.
;		- Modify CHECK_HBS to retry any Read Long commands that
;		  return Unit Attention status, since such status may
;		  merely indicate a bus reset.
;		- Modify CHECK_HBS to circumvent unnecessary CLEANUP_CMD
;		  for writelocked devices.
;		- Remove support for ADU's in CHECK_HBS.
;		- Just before KP_REQCOM in startio, convert any status of
;		  SS$_TIMEOUT to SS$_MEDOFL in order to allow retries.
;
;	X-24	RCL		Rick Lord		13-Mar-95
;
;               1) In DK_BEGIN_MNTVER and in STOP_NEW_IO, fix the instructions which were
;                  checking to see if the Golden IRP (UCB$R_BUSY_BIT_IRP) was on the head
;                  of the I/O queue or not; this check was failing consistently.
;
;               2) In COMPLETE_IO, preserve UCB$L_IRP across the call to EXE$KP_RESTART,
;                  as the KP restarted by that call may call KP_REQCOM, which may start
;                  another I/O and so overwrite UCB$L_IRP.
;
;       X-23    SCS             Sue Sommer              10-Feb-1995
;               GWW             Grace Wang              28-Feb-1995
;               Add alternate startio routine DK_ALTSTARTIO for Quorum IO.
;
;	X-22	SCS		Sue Sommer		28-Feb-1995
;		Enhance cancel-I/O functionality to allow the MSCP 
;		server to cancel all its I/O's:  Whenever the MSCP server
;		calls DK_CANCEL (it does so with a reason code of
;		CAN$C_MSCPSERVER), walk the list of pending IRPs and
;		mark all server IRPs by setting the IRP$V_SRV_ABORT bit
;		in IRP$L_STS.  Then, just prior to sending a SCSI command
;		in SEND_COMMAND, check for IRP$V_SRV_ABORT and, if it is set,
;		return early with a status of SS$_CANCEL.  For those IRPs 
;		which issue multiple SCSI commands per single IRP, add code
;		to check for SS$_CANCEL after each call to SEND_COMMAND;  if
;		in fact SS$_CANCEL is returned, then bail out in mid-request.
;		Also, similar logic was added to CHECK_HBS, which is the
;		only routine in this driver which sends SCSI commands without
;		calling SEND_COMMAND to do so. 
;
;	X-21	SCS   		Sue Sommer		21-Feb-1995
;		Modify audio support to work with IRPE and MMG changes
;		introduced by 64-bit addressing project:
;		- Instead of one IRP and one doubly-used IRPE
;		  (to hold mapping information for AUCB, sense buffer
;		  and data buffer respectively), use one IRP and
;		  2 separate IRPE's, each linked via IPE(E)$L_EXTEND.
;		- Backward link to parent IRP(E) is stored in IRP(E)$L_SEQNUM.
;		- Use IRPE$L_DRIVER_P0 to flag whether the buffer is a 
;		  sense buffer.
;		- Use IRPE$L_DRIVER_P1 to point to the original IRP.
;
;	X-20	SCS   		Sue Sommer		14-Feb-1995
;		Declare various appropriate functions to be capable
;		of supporting 64-bit addresses.
;
;	X-19	JCH703	John C. Hallyburton, Jr.	9-Feb-95
;		(T3A and beyond only) Add FAST_FDT routine to DDT.
;		DOCD$:[EVMS.PROJECT_DOCUMENTS]FS-FASTIO.PS
;
;	X-18U8	JFD0818		James F. Dunham		14-APR-1995
;		o Skip flexible disk geometry page if mode sense for rigid disk
;		  geometry page succeeds
;		o Add (disabled) support for RZ74 SCSI drive disable caching code
;		o Make sure R1 is clear (MSB of xfer count) on error paths
;		  prior to calling REQCOM
;		o Change some LOG_ERRORs, to return a fatal status .vs. SS$_NORMAL
; 		o Remove 1st error log entry for SCSI$C_BUSY, to last entry
;		o Add SS$_MEDOFL to the list of errors, which DKDRIVER doesn't log
;		o Remove DISPATCH macro usage, due to too not a dense set of values
;		o Pick up X-26 from Theta Stream
;		  X-26  RCL		Rick Lord		29-Mar-95
;		  - Move the code which sets SINGLE_RW in IO_RW_COMMON to after
;		    the checks in which we validate that the volume and block
;		    number are valid, to prevent our leaving SINGLE_RW set and
;		    leaving the device permanently busy.
;
;	X-18U7	JFD0817		James F. Dunham		 4-APR-1995
;		o Remove private defintions of IRP$V_FORCEMV and  CAN$C_MSCPSERVER
;		o Decrease queue table depth sizes.
;		o Increate queue scan histogram counter
;
;       X-18U6  RCL	Rick Lord			5-Apr-95
;	
;		1) Make WCE a preferred bit in it's caching page descriptor rather than
;		   setting to explicitly in-line, since it is the more common case. The
;		   only section of code which did not alter it was for non-512-byte block
;		   size devices, which for now would be only CD-ROMs, and WCE is not an
;		   issue for them.
;
;		2) Create LOAD_TENBYTE and STORE_TENBYTE macros to simplify saving and
;		   restoring the TENBYTE bits in the UCB and SCDRP (they're not the same
;		   bit offset).
;
;		3) In PROCESS_MODE_INFO, make the error recovery page the first page for
;		   all types of devices, not just for CD-ROMs, and if it's successful then
;		   save the state of the SCDRP.TENBYTE bit in the UCB.
;
;		4) In PROCESS_MODE_INFO, refresh the SCDRP.TENBYTE bit from UCB.TENBYTE if
;		   any DO_MODE_PAGE fails and the routine is going to continue - if it's 
;		   going to exit then there's no need to do the refresh.
;
;		5) In PROCESS_MODE_FORMAT_FLOPPY, initialize the SCDRP.TENBYTE bit from
;		   UCB.TENBYTE prior to the first DO_MODE_PAGE. There's no need to refresh
;		   the SCDRP bit in this routine because it exits if DO_MODE_PAGE fails.
;
;       X-18U5  SCS             Sue Sommer              21-Mar-1995
;		JFD0814		James F. Dunham		23-MAR-1995
;		o Allow certain errors to be converted to SS$_MEDOFL
;		  in order to permit retries.
;		o Make sure CD-ROM block size processing is a required mode
;		  sense operation.
;
;       X-18U4  SCS             Sue Sommer              17-Mar-1995
;		- Convert any errors from PROCESS_MODE_INFO to SS$_MEDOFL
;		  so that retries are possible.
;		- Modify CHECK_HBS to retry any Read Long commands that
;		  return Unit Attention status, since such status may
;		  merely indicate a bus reset.
;		- Modify CHECK_HBS to circumvent unnecessary CLEANUP_CMD
;		  for writelocked devices.
;		- Remove support for ADU's in CHECK_HBS.
;		- Just before KP_REQCOM in startio, convert any status of
;		  SS$_TIMEOUT to SS$_MEDOFL in order to allow retries.
;
;	X-18U3	RCL		Rick Lord		9-Mar-95
;
;		1) In DK_BEGIN_MNTVER and in STOP_NEW_IO, fix the instructions which were
;		   checking to see if the Golden IRP (UCB$R_BUSY_BIT_IRP) was on the head
;		   of the I/O queue or not; this check was failing consistently.
;
;		2) In COMPLETE_IO, preserve UCB$L_IRP across the call to EXE$KP_RESTART,
;		   as the KP restarted by that call may call KP_REQCOM, which may start
;		   another I/O and so overwrite UCB$L_IRP.
;
;       X-18U2  SCS             Sue Sommer              10-Feb-1995
;               GWW             Grace Wang              28-Feb-1995
;               Add alternate startio routine DK_ALTSTARTIO for Quorum IO.
;
;	X-18U1	SCS		Sue Sommer		27-Feb-1995
;		Enhance cancel-I/O functionality to allow the MSCP 
;		server to cancel all its I/O's:  Whenever the MSCP server
;		calls DK_CANCEL (it does so with a reason code of
;		CAN$C_MSCPSERVER), walk the list of pending IRPs and
;		mark all server IRPs by setting the IRP$V_SRV_ABORT bit
;		in IRP$L_STS.  Then, just prior to sending a SCSI command
;		in SEND_COMMAND, check for IRP$V_SRV_ABORT and, if it is set,
;		return early with a status of SS$_CANCEL.  For those IRPs 
;		which issue multiple SCSI commands per single IRP, add code
;		to check for SS$_CANCEL after each call to SEND_COMMAND;  if
;		in fact SS$_CANCEL is returned, then bail out in mid-request.
;		Also, similar logic was added to CHECK_HBS, which is the
;		only routine in this driver which sends SCSI commands without
;		calling SEND_COMMAND to do so. 
;
;	X-18	RCL		Rick Lord		1-Feb-95
;		In IO_PACKACK, if PROCESS_MODE_INFO fails for any reason and the
;		TENTYE 	bit is still set, clear it and try PROCESS_MODE_INFO again.
;		This works around devices which appear to complete 10-byte mode
;		sense requests successfully but in fact don't send usable data.
;
;	X-17	GWW		Grace Wang		27-Jan-1995
;		In PROCESS_MODE_INFO, default sector=4, track=6 to avoid
;		MSCP Server crash (see Zeta QAR 2688 - with UCB$B_TRACKS 
;		initialized to zero, an unknown cause left it zero after
;		PROCESS_MODE_INFO and VALIDATE_GEOMETRY of a RZ25 device.
;		MSCP Server copies this zero to MSCP$W_GROUP and bugchecks
;		to show that it won't tolerate zero geometry value.)
;
;	X-16	RCL		Rick Lord		20-Jan-1995
;		In SEND_COMMAND_RETRY, provide one label which will peform the
;		retries-left check without altering R0 and another which will
;		preload it with SS$_MEDOFL, the status which should normally
;		be returned. Change the CHECK_CONDITION code immediately 
;		preceding the retry check to take the preserve-R0 branch.
;
;	X-15	CMF340          C M Fariz               15-Jan-1995
;		Add code to GET_DEVICE_TYPE that will take the value provided
;		in the SYSGEN parameter VMS7 and add it to UCB$W_DISC_TMO.
;		It may be a negative or positive number.  The value represents
;		the number of seconds by which to alter the existing time
;		out value.   This will affect RZ type devices only.
;	
;		The routine REASSIGN_BLOCK will also have the contents of
;		VMS7 added to SCDRP$L_DISCON_TIMEOUT.  This will affect
;		RZ type devices only.
;
;		NB:
;		   Decreasing the time out vlaue is VERY risky.
;
;		This code is being added as an investigative effort.  The
;		intention is to determine if an increase in this value is
;		helpful now that multi-initiator and SCSI2 are now implemented.
;
;		The use of this parameter needs to be reviewed at the
;		start of the next VMS release.  If it is determined that
;		it is useful, a more perminant solution must be designed.
;
;	X-14	SCS			Sue Sommer	10-Jan-1995
;		Correct the use of UCB$L_IRP so that it is properly
;		assigned before any calls to KP_REQCOM or CALL_REQCOM.
;		Also, clear it after calls to CALL_REQCOM since it 
;		is then invalid in a multi-initiator environment.
;
;	X-13	CMF339          C M Fariz               21-Dec-1994
;		Change the SEND_COMMAND routine so that if a SCSI BUSY status
;		is returned, and the timeout time has not been exceeded, the
;		IO will be retried.  There will only be one error log entry
;		per IO if BUSY status is returned.
;
;		This code does account for the CANCEL IO bit that will
;		exist shortly.  The code needs to just be added.
;
;		CMF141          C M Fariz               21-Dec-1994
;		Updated the copyright statement to use the symbol ©
;	        rather than the symbol (c).
;
;	X-12	JFD0705		James F. Dunham		14-DEC-1994
;	    	ROW		Ralph O. Weber		14-DEC-1994
;		o I/O Drain support for ordered strong sequential commands.
;		o Insure that any post-processing of the "golden" IRP is harmless.		
;		  Add routine DK_GOLDEN_IRP_POST.
;		
;       X-11    RAR060          Buzzy Ritter            21-Nov-1994
;               Modify SET_UNIT_ONLINE routive so that at boot time if packack
;		fails for the boot device then retry. This avoids a hang on 
;               startup where packack sees an error (probably induced by the 
;               other initialtor's reset) and doesn't set the valid bit. The 
;               swapper will hang if the system disk's valid bit isn't set. We 
;               will retry forever since the swapper will hang forever anyway.
;
;	X-10	JFD0701		James F. Dunham		 1-DEC-1994
;	    	ROW0853		Ralph O. Weber		22-NOV-1994 18:58
;		o If Mount Verification in progress is detected, (UCB$V_MNTVERIP),
;		  ignore establishing CB_MOUNTVERIP is this IRP is a shadowing I/O.
;
;		o Fix SET_SINGLE macro to reference UCB based on parameter value
;		  (not a fixed R5) when setting UCB$V_BSY.
;
;	X-9	ROW0852		Ralph O. Weber		15-NOV-1994 09:50
;		Add STOP_NEW_IO, a routine that is called before REQCOM to
;		block new I/O requests from coming to the DKDRIVER start I/O
;		routine whenever DKDRIVER is not ready to handle new I/Os.
;		Remember, blocking new I/O requests also keeps the UCB$V_BSY
;		bit set.  STOP_NEW_IO encapsulates some moderately complex
;		knowledge about when DKDRIVER is ready to accept new I/O
;		requests.  STOP_NEW_IO also is the one place where the
;		encapsulated knowledge needs to be changed as new features
;		(particularly failover) are added.
;
;		Prevent the "golden" IRP from becoming buried in the UCB$L_IOQFL
;
;		JFD0695		James F. Dunham		 	2-NOV-1994
;		Change the behavior of SET_SINGLE to always set the UCB$V_BSY
;		bit when setting a UCB$L_CLASS_BUSY bit. The makes DKDRIVER 
;		look busy when single threaded I/O operations are active.
;		This change in UCB$V_BSY functionality makes DKDRIVER appear
;		externally like the SCSI-1 DKDRIVER.
;
;		Add code prior to calling KP_REQCOM to implement BUSY_BIT_IRP,
;		functionality, which keeps the UCB$V_BSY bit set.
;
;		DK_BEGIN_MNTVER:
;		o Add shadowing I/O's to the list of IRP types which DKDRIVER does
;		  not perform mount verification on.
;		o Add a bugcheck, if DK_BEGIN_MNTVER ever attempts to MNTVER a MVIRP,
;		  and remove check for MVIRP on re-insert into UCB$L_IOQ code path.
;		o Make sure IRP's are re-inserted into UCB$L_IOQ in EXE$INSIOQ order.
;
;		In support of SCSI-Clusters, make sure IO_AVAILABLE and IO_UNLOAD
;		are single-threaded and are implemented. The STOP_UNIT SCSI command
;		is sent as an ordered command, (aka sequential-nop).
;
;	X-8	SCS			Sue Sommer		11-Nov-1994
;		Correct the input time value to exe$kp_tqe_wait.	
;		JFD		James F. Dunham		 	
;		In support of SCSI-Clusters, make sure IO_AVAILABLE and IO_UNLOAD
;		are single-threaded and are implemented. The STOP_UNIT SCSI command
;		is sent as an ordered command, (aka sequential-nop).
;
;	X-7	SCS			Sue Sommer		7-Nov-1994
;		Add TENBYTE flag to DK_FLAGS to indicate support for 10-byte
;		mode sense commands.  Modify PROCESS_MODE_INFO to reference
;		this flag and prevent unnecessary 10-byte command attempts.
;
;       X-6     WBF             Burns Fisher                    31-OCT-1994
;               In SET_UNIT_ONLINE, change the device characteristics to allow
;               disconnects.  We need this to avoid getting a continuous busy
;               return from the inquiry if there is another host on the bus
;               keeping the device's queue full (SCSI spec says device must or
;               at least can return busy for a non-disconnectable request if
;               it is currently servicing other disconnected requests).
;
;	X-6	JFD0692		James F. Dunham			31-OCT-1994
;		When re-inserted I/O's in pending I/O queue, insert them in
;		IRP$B_PRI and IRP$L_SEQNUM order.
;
;	X-6	RAR055		Buzzy Ritter			26-Oct-1994
;		Remove use of UCB$PS_SCDRP everywhere except logging errors.
;		Set this field in LOG_ERROR macro for DK_REG_DUMP use.
;
;	X-5	JFD0672		James F. Dunham			13-OCT-1994
;		When re-inserting I/O's into the pending I/O queue, never
;		place an entry in front of a IRP$V_MVIRP.
;	        SCS			Sue Sommer		14-oct-1994
;		Fix GET_DEVICE_TYPE to handle unknown SCSI devices correctly.
;
;	X-4	JFD0671		James F. Dunham		 	5-OCT-1994
;		If the driver is in single-threaded mode (UCB$V_BSY), then
;		determine if mount verification is active and we don't know
;		about it. If so setup to look like MV in Progress/ MV Pending.
;
;		Add support for CLASS_BUSY tracing, and leave it disabled.
;
;	************* Change Edit History to Match CMS ********************
;
;	X-3	JFD0660		James F. Dunham			28-SEP-1994
;		SCSI-2 Checkin
;
;	X-43	JFD0646		James F. Dunham			28-SEP-1994
;		Add support for MULTIHOST, if device is write enable and CMDQ
;
;	X-42	JFD0645		James F. Dunham			26-SEP-1994
;		Add support for EXE_STD$KP_STARTIO callback. This will allow
;		the driver to detect that a KPB thread startup is stalled and
;		process accordingly.
;
;	X-41	JFD0638		James F. Dunham			22-SEP-1994
;		o After issuing and I/O (KP_STARTIO) and it stalls, determine if
;		  there are any other reasons for not queuing the next I/O. If so
;		  set UCB$M_BSY and exit.
;		o Add support for MSCP_MV call back, to inform MSCP of MNTVERIP
;		o Add support for stalling the class driver, when no command bits
;		  are available at the SCSI port driver layer. This allows stalling
;		  without an associated KPB and its stack.
;		o After a sucessful Mount Verification, selectively enable UCB$M_BSY
;	          based on validation of CLASS_BUSY bits.
;
;	X-40 	SCS			Sue Sommer		21-Sep-1994
;		Add a TST_SINGLE to DK_STARTIO after calling kp_restart.
;
;	X-39	RAR049			Buzzy Ritter		15-Sep-1994
;		Change PORT_CALLBACK flag in UCB$L_CLASS_BUSY into 5 seperate
;		flags so that reset can have its own.
;
;	X-38	JFD0620			James F. Dunham		13-SEP-1994
;		- Mount Verification for Served I/O's, are just reposted
;		- Fix register usage for SET_SINGLE BUFFER_MAP
;		- Fix register usage for SET_SINGLE SINGLE_DC
;		- Clear geometry parameters (track, sector, cylinder) on errors
;		- Allow multiple MNTVERIP to occur
;
;	X-37 	SCS			Sue Sommer		 7-Sep-1994
;		Add mode page descriptors to force 512 blocksize for CDROMs
;		and to handle the WCE bit in the write caching page.
;
;	X-36	JFD0615			James F. Dunham		 6-SEP-1994
;		o Add DYNDEF and standard OpenVMS pool header to DK_ALLOC_POOL
;		o Use correct KPB in BBR wait routines
;		o Set SINGLE_RW when doing IO_WRITECHECK
;
;	X-35 	SCS		Sue Sommer			6-Sep-1994
;		Remove extraneous Request Sense call in SEND_COMMAND's
;		Check Condition path (port drivers handle this).
;		Also, even if sense data is invalid, log the Check Condition.
;		Modify IO_PACKACK to fail under certain conditions for
;		SCSI clusters.
;		
;	X-34	JFD0608			James F. Dunham		 2-SEP-1994
;		When performing datacheck and/or DSE operations, shared access
;		to the UCB fields are not allowed. These routines must wait
;		until they gain access to their respecitive CLASS_BUSY bit.
;
;	X-33	JFD0607			James F. Dunham		30-AUG-1994
;		Rework MOUNT Verification to retain single threadedness, 
;		and handle correctly setting CB_MNTVERIP in the CLASS_BUSY bits
;
;	X-32	JFD0598			James F. Dunham		25-AUG-1994
;		o Include part of IOC$INITIATE inside of DK_KP_STARTIO to prevent
;		  recursion, and subsequent kernel stack overflows
;		o Handle CLUSTER Mountver
;		o Single thread DKDRIVER around buffer mapping calls
;		o Add support for GO/STOP on send credits
;		o Change SINGLE_DC to be disable if CMDQ is valid
;
;	X-31 	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-30	SCS			Sue Sommer		17-Aug-1994
;		Include support for mscp$v_md_exprs (express QIO) bit.
;
;	X-29	JFD			James F. Dunham		27-JUL-1994
;		Set UCB address as SELECT_CONTEXT when issuing a SPI$CONNECT
;
;	X-28	JFD			James F. Dunham		21-JUL-1994
;		Remove COLLECT_PERF_DATA flags, since SCSI-2 queue depth
;		monitoring must be on at all times
;
;	X-27	SCS			Sue Sommer		20-Jul-1994
;		Modify SETUP_CMD so that the caller may specify scdrp$l_bcnt.
;
;	X-26	JFD			James F. Dunham		19-JUL-1994
;		Set UCB$M_NOCMDQ at driver load time
;		Add support for single threading the following operations:
;			o Unit Initialization
;			o Port Callback
;			o Mount Verification
;			o PACKACK
;			o Bad-Block Replacement
;			o IO$_UNLOAD
;			o IO$_FORMAT
;			o IO$_AUDIO
;			o Forced Error in last sector read
;
;	X-25	SCS			Sue Sommer		19-Jul-1994
;		Fix bugs in TRANS_SENSE_KEY that mistakenly referenced 
;		the SCDRP in R0 rather than the sense data in R2.
;
;	X-24	SCS			Sue Sommer		15-Jul-1994
;		Calculate ucb$b_scsi_version based on the ANSI-approved
;		version field of the INQUIRY (not on the format field as
;		was done previously).
;					
;	X-23	SCS			Sue Sommer		12-Jul-1994
;		Modify PROCESS_MODE_INFO to log errors.
;		Increase mode_sense_6 allocation length to 255.
;		Make descriptor table more compact.
;
;	X-22	JFD			James F. Dunham		11-JUL-1994
;		Backoff the increase to the KP stack, since KP_STARTIO_IO uses
;		cached KPB, and if DK's stack size is large then the cached KPB's
;		stack size, it bug checks
;
;	X-21	SCS			Sue Sommer		8-Jul-1994
;		Fix PROCESS_MODE_INFO to fetch mode page length correctly
;		for flexible disks.
;
;	X-20	SCS			Sue Sommer		8-Jul-1994
;		- Modify descriptors to remove VERIFY flag and set IFMISMATCH
;		  where appropriate.
;		- Eliminate obsolete use of ucb mode_sense_pag1/10 bits.
;		- Modify mode sense logic for TCQ devices:  if error recovery 
;		  page processing fails, just disable TCQ and retry as nonTCQ.
;		- Remove bad check for autosense in send_command.
;		- Double size of KP stack (temporary) for debug.
;
;	X-19	JFD			James F. Dunham		7-JUL-1994
;		Change location of where UCB$M_CB_NOCMDQ is set. Keep both setting
;		and clearing confined to IO_PACKACK.
;
;	X-18	SCS			Sue Sommer		7-Jul-1994
;		Modify PROCESS_MODE_INFO to get medium type correctly.
;
;	X-17	SCS			Sue Sommer		6-Jul-1994
;		Modify DK_PORT_CALLBACK to work with IO_DIAGNOSE.
; 
;	X-16	SCS			Sue Sommer		27-June-1994
;		Remove call to LOG_ERROR in mode page handling since 
;		CDB information is gone after returning from do_mode_page.
;		Convert mode sense errors to ss$_drverr.
;
;	X-15	JCN			Jan Nordh		23-Jun-1994
;		Fix DK_REG_DUMP routine, to dump just that info that ERF expects.
;
;	X-14	SCS			Sue Sommer		22-June-1994
;		Add enhanced mode page support to X-5 DKDRIVER.
;		- Change SETUP_CMD, SEND_COMMAND, CLEANUP_CMD to CALL interface.
;		- Make SCSI_CMD table entries global, and quad-aligned.
;		- Add data section for mode page field descriptors;  add $MODEDEF.
;		- Add new routines PROCESS_MODE_INFO and PROCESS_MODE_FORMAT_FLOPPY.
;		- Modify setup_cmd to allow a previously allocated private buffer.
;		- Modify cleanup_cmd to allow private buffer.
;		- Call READ_CAPACITY before PROCESS_MODE_INFO, not after.
;		- Get block size from READ_CAPACITY data, not FORMAT data.
;		- In IO_PACKACK, implement new call to PROCESS_MODE_INFO,
;		  and add call to new routine VALIDATE_GEOMETRY.
;		- In IO_FORMAT, implement new call to PROCESS_MODE_FORMAT_FLOPPY.
;		- Remove obsolete routines MODE_SENSE, MODE_SENSE_CHANGABLE,
;		  MODE_SELECT, MODE_SELECT_FORMAT_FLOPPY.
;		Add calls to common routines for IO$_DIAGNOSE.
;
;	X-13	JFD			James F. Dunham		21-JUN-1994 13:40 
;		1). Fix handling of MOUNT Verification's usage of CLASS_FLAGS
;		2). Fix calulation of DK$REG_DUMP's buffer length
;
;	X-12	JFD			James F. Dunham		14-JUN-1994
;		1). Fix I/O Diagnose interface, which references invalid DSC_FLAGS
;		    suffixed onto end of SCDRP
;		2). Fix enabling of TCQ, based on CMDQ & DISCONNECT support
;		3). Fix ridged disk cylinder field access
;		4). Re-introduce NOCMDQ qualifier into TST_SINGLE, SET_SINGLE and
;		    CLR_SINGLE macros.
;
;
;	X-11	JFD			James D. Dunham		 9-JUN-1994 08:27 
;		1). Fix call to EXE$KP_WAIT_TQE to use the SCDRP's KPB verses the
;		UCB$PS_UNITINIT_KPB.
;		2). Fix encoding of SECONDS value for TQE's
;		3). Fix invalid register for UCB$L_OTHER_COUNT
;		4). Invoke CLR_SINGLE SINGLE_RW at end of DATACHECK I/O's
;		5). Alter enabling CMDQ based on availability of _DISCONNECT's
;		6). Fix offeset to recovery flags
;		7). Address 'active' IRP usage in LOG_ERROR
;
;	X-43	JFD00221		James F. Dunham		19-OCT-1993
;		1). Migration merge of Tag-queueing changes to EPSILON
;		2). Rework of TRACE_SCSI routines
;		3). Correct time call arguments to KP_STALL_TQE
;		4). Pass Queuing parameters to SPI$CONNECTION_CHAR_?ET
;		5). Remove READY_POLL_INTERVAL, (as its a second)
;		6). Remove C2_BUILD & NEW_PKCDRIVER build flags
;		7). Remove (noted) comments regarding UCB$K_LENGTH being
;		    limited to a certain size. This is a VAX'ism.
;		8). Correct DK_ALLOC_SCDRP to correct preserve the previous
;		    SCDRP's address and KPB address.
;		9). Remove NOCMDQ qualifier from TST_SINGLE, SET_SINGLE and
;		    CLR_SINGLE macros.
;		10). Fix VMS date/time argument to KP_STALL_TQE from value 
;		     to reference value
;		11). Change the Inquiry CDB to be sent to the SCSI port as
;		     non-tagged. When it was 'tagged', other CDB's could
;		     be sent, even though a device is not configure until
;		     the Inquiry CDB completes.
;		12). Symbolize the SCSI$DK$V_WP, instead of a constat of '7'.
;		13). Change FMT$B_PAGE_LENGTH to CMP$B_PAGE_LENGTH for constancy
;		14). Change SCSI$FMT$W_SECTORS_SIZE to SCSI$FMT$W_SECTORS
;		15). Symbolize SCSI$ERP$?_PAGE_CODE from number values
;		16). Fix MODE_SELECT's calculation of CTRL_MODE_LENGTH
;		17). Change SCSI$MPDB$B_BLOCK to SCSI$MPDB$B_LENGTH
;		18). Change MODE_SELECT's referencing of error and control page
;		     offsets when filling in data.
;		19). Fix MODE_SELECT's calulation of FMT$B_PAR_PAGE_LENGTH
;		20). Symbolize SCSI$M_STATUS_BYTE_RESERVED from '^XC1'.
;		21). Cleanup ALLOC_SCDRP's zero'ing of memory, and set
;		     PHASE_TMO and DISC_TMO.
;		22). Cleanup SETUP_CMD processing
;		23). Add TCQ parameteres to SET_CONN_CHAR processing
;		24). Fix CHECK_HBS usage of R0, which get scratch via
;		     new CALLS interface.
;
;	X-42U1	RAR032		Buzzy Ritter		14-Mar-1994
;		Fix use of R6 & R8 after forking in DK_CTRL_INIT. 
;
;
;	X-42	RAR028		Buzzy Ritter		29-Dec-1993
;		Segment write transfers in IO_DSE so that they don't exceed
;		127 pagelets. Also fix return value in unit init and ctrl init.
;
;
;	X-41	PJH		Paul J. Houlihan	19-Nov-1993
;		Set UCB$V_EXFUNC_SUPP.
;
;	X-40	SCS	Sue Sommer			13-Dec-1993
;		In IO_PACKACK, remove an unnecessary call to fetch
;		audio MCN data;  this call was generating an error for
;		data CDs on mounted on RRD42s.
;		Resynchronize version numbers with CMS.
; 
;	X-38	RAR026	Buzzy Ritter			1-Dec-1993
;		Change loop in CHECK_HBS so that a new message buffer is
;		allocated each time through the loop.
;
;	X-37	MAS0E01	Michael A. Stams		 8-Nov-1993
;		Add DEV$M_CLU to DPT_STORE for DEVCHAR2.
;
;	X-36	LPL0001	Lee Leahy			 1 Oct 1993
;		Finish symbol name changes.
;
;	X-35	RCL	Rick Lord			30-Sep-1993
;
;		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-34	SCS	Sue Sommer			 16-SEP-1993
;		- Enable audio functionality for CDROMs.
;		- Fix FORCE_ONE_ERROR to calculate SVAPTE and BOFF before
;		  calling READ_WRITE.
;
;       X-33    WDBHLL80        Walter D. Blaschuk, Jr. 10-Sep-1993
;               HLLDD Project:  Change the Host Base Shadowing logic
;               to call the Upper Level FDT routines in SHDRIVER.MAR.
;
;	X-32	LSS0290		Leonard S. Szubowicz	 7-Sep-1993
;		HLLDD: CALL_PTETOPFN must assure that R1 is preserved as
;		well as R0, thus the parameter name changes to SAVE_R0R1.
;
;	X-31	WDA	W.D. Arbo			17-Aug-1993
;		Change old step 2 macro names to new names. (E.g.
;		$ABORTIO to CALL_ABORTIO).
;
;	X-30	WDA	W.D. Arbo			27-Jul-1993
;		Catch references to IOC$REQCOM and IOC$INITIATE in 
;		SET_UNIT_ONLINE which were missed in X-29.
;
;	X-29	WDA	W.D. Arbo			29-Jun-1993
;		Clean up remaining step 2 conversion items.  Note: DKAUDIO
;		and the subroutine AUDIO_MAP_PAGE have been modified for
;		step 2, but since these routines are turned off these modifications
;		have not been tested.
;
;	X-28	LSS0254		Leonard S. Szubowicz	10-Jun-1993
;		Merge X-23U2.
;		Fix support for the IO$M_DATACHECK modifier and IO$_WRITECHECK
;		function.  In the code after label IO_DATACHECK, do not alter
;		SCDRP$L_SVAPTE and SCDRP$L_BOFF for each segment checked.  In
;		the AXP version of this driver these cells point to the data-
;		check buffer and not the current segment of the user buffer.
;		Thus in routine DATACHECK_CMP, compute the mapping to the
;		current segment of the original user buffer accordingly.
;		Previously, this routine was double mapping the datacheck
;		buffer and comparing it against itself!
;
;	X-27	SCS	Sue Sommer			 14-May-1993
;		Modify IO_DSE routine to calculate SCDRP$L_SVAPTE so that
;		the DSE buffer may be successfully mapped.
;
;	X-26	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 entries in
;		the device table for RZ73, RZ57 and RZ25 disks are still
;		commented out.  Restore them to their rightful place.
;		
;	X-25	RWC122		Richard W. Critz, Jr.	23-Apr-1993
;		Integrate DDR support (in local routine GET_DEVICE_TYPE).
;		
;	X-24	WDA	W.D.Arbo			 14-Apr-1993
;		Conversion to Step 2.
;
;	X-23-JFD James F. Dunham			14-Sep-1993
;		1). Merge Tag Queueing changes from VAX to AXP
;		2). Changed comments to add thread, IPL and LOCK information.
;		     New comments start with
;			;<THREAD characters> <IPL character><LOCK characters>;
;		     Where:
;
;			THREAD character is one of:
;				F = Fork thread
;				I = Interrupt thread
;				P = Initialization or powerfail thread
;				Q = Queue Manager kernel process thread
;				S = I/O request SCDRP kernel process thread
;				U = User Mode FDT routines thread
;				C = Class Driver kernel process thread
;
;			IPL character is one of:
;				A = IPL$_ASTDEL - 2
;				P = IPL$_IOPOST - 4
;				F = Fork IPL	- 8
;				S = IPL$_SYNCH	- 8
;				T = IPL$_TIMER	- 8
;				D = Device IPL	- 21
;				X = IPL$_POWER	- 30
;
;			LOCK characters (last character => last spinlock taken) are one of:
;				Not specified = no locks held
;				F = fork lock (IOLOCK8)
;				D = Dual SCSI device lock
;				S = SPDT device lock
;				U = UCB device lock
;
;		     These comments can be used in combination with the DCL "search"
;		     command to determine if a variable is accessed in a synchronous
;		     manner.
;		3). Add BBR failure if VMSCluster has lost QUORUM
;
;		X-38	MCY		Mary Yuryan			14-May-1993
;		Fold in J. Guineau's AUDIO bugfix.
;		WJG		John Guineau			09-Apr-1993
;		Fix error paths in IO$_AUDIO FDT/STARTIO routines related
;		to bad/improperly initialized AUCB fields.
;
;		X-37	RCL		Rick Lord			6-May-1993
;
;		Define OPTICAL and WORM bits in UCB$L_DK_FLAGS.
;
;		Modify INQUIRY subroutine to set OPTICAL bit in UCB$L_DK_FLAGS
;		if the SCSI device type code is optical, or the WORM bit if the
;		device type code is WORM.
;
;		Modify MODE_SENSE to set HWL and SWL if the device is WORM, or
;		if it's Optical with WORM media mounted; also, do not require
;		track and sector information to be returned from Mode Sense
;		for Optical devices.
;
;		X-36	MCY		Mary Yuryan			12-Apr-1993
;		Add the following SCSI devices - RW504, RW510, RW514,
;		RW516, RWZ51, RWZ52, RWZ53, RWZ54, RWZ31 - RZ28L, RZ26M -
;		EZ31, EZ32, EZ33, EZ34, EZ35, EZ31L, EZ32L, EZ33L, EZ56R
;		- HSZ15.
;
;		X-35	WJG	W. John Guineau			6-Apr-1993
;		- No changes, FOLD VIKING TCQ stream into CORAL, only
;		  changed .IDENT to match CORAL stream
;		- uncomment the define for V60_BUILD for Coral.
;
;
;		X-18A7A7-Q WJG	W. John Guineau			 15-Mar-1993
;		- Lock out other processes in IO$_DIAGNOSE while
;		  a an I/O is pending for one GK process. Also,
;		  Make sure process for a pending check condition is
;		  the process that caused it.
;
;		X-18A7A6-Q WJG	W. John Guineau			 3-Mar-1993
;		- Make TEST_UNIT_READY send TUR as NON-QUEUED which
;		  will still sync queues in a queued environment, but
;		  works around an HSZ10 bug where the first queued command
;		  sent when a unit attention is pending get's eaten.
;
;		X-18A7A5-Q WJG	W. John Guineau			 2-Feb-1993
;		- Set UCB$M_BSY during PACKACK just to be safe.
;		- Add BBR_IN_PROG flag to UCB$L_DK_FLAGS. This bit
;		  is set when a BBR starts, cleared when it completes.
;		  If BBR_IN_PROG is set when a second thread starts
;		  a BBR operation, that thread will FORK_WAIT until
;		  it clears. This prevents more than one thread from
;		  attempting a BBR operation at one time.
;		- Move UCB$L_SCDRP_SAV2, UCB$L_ADDNL_INFO and
;		  UCB$B_SENSE_KEY into SCDRP for multi-threaded
;		  synchronization.
;
;		X-18A7A4-Q WJG	W. John Guineau			 15-Jan-1993
;		- Add a G^ to the call to EXE$ALONONPAG in READ_CD_MCN
;		  to fix crash.
;
;		X-18A7A3-Q WJG	W. John Guineau			 11-Jan-1993
;		- Fix floppy formatting bug in MODE_SENSE: where a
;		  BGEQ was being used instead of BLSS causing
;		  UCB$L_FORMAT_PRAMS to never be allocated.
;		- Add a '-Q' to the compiled in image ident (.IDENT) for
;		  easy detection of command queuing based drivers.
;
;		X-18A7A2 WJG	W. John Guineau			 8-Jan-1993
;		- Remove unused/debug code
;		- Add HSZ20, HSZ40 (FIB) to CHECK_FOR_RAID macro
;		- fix bug in histogram which would corrupt pool on
;		  xfers larger than 127 blocks
;		- Deal with MODE_SENSE page 0x0A (queueing parameters)
;		  to make sure the QErr bit is not set
;		- Attempt to clear the ARRE/AWRE bits in MODE_SENSE page 1
;		- Make LUN information handling in INQUIRY SCSI-2 compliant
;
;		X-18A7A1 WJG	W. John Guineau			 12-17-1992
;		- Add CHECK_FOR_RAID_DEVICE macro and UCB$V_RAID
;		  bit. RAID devices use 10 byte mode sense/select
;		  commands.
;		- Fix bug in copying of additional sense bytes
;		  in SAVE_ADDNL_INFO: to do a signed compare of
;		  length of additional sense data against length
;		  of buffer field in UCB
;		- Add command queuing support
;		- merge BLADE	X-31,X-32 changes for VIKING Checkin:
;		X-32	MCY		Mary Yuryan
;			Add "B" variant disks to device recognition tab
;			RZ27B, RZ28B, RZ29B, RZ73B, RZ74B, RZ75B.  Add
;			Add HSZ20,HSZ40 - Raid subsystem.
;
;		X-31	MCY		Mary Yuryan
;			Fix 2 bugs in the ATTEMPT_REORDER routine.
;			1)Update R4 correctly so as not to alter the be
;			the elevator algorithm; currently the scan di
;			be effected.
;			2)Provide UCB$L_FAIRNESS_CNT (with value of 4)
;			malicious user from looping infinitely while
;			same LBN.  Checks added in SWAP_IRP:, SCAN_DO
;			PERFECT_IRP:.  This could slow or even hang t
;
;		X-30	JSSBLADE2	John S. Simakauskas		 1-July-1992
;		Fix build bug introduced in X-29,
;               SCSI_DEV_TYPES table was made too long.
;
;		X-29	JSSBLADE	John S. Simakauskas		24-June-1992
;		Add device recognition for RZ26L,RRD43,RRD44.
;
;		X-28	MCY		Mary Yuryan		12-Jun-1992
;		Always return good status in the READ_CD_MCN routine
;		to keep 3rd party CDROMS that do not support SCSI-2 
;		from failing. 
;
;		X-27	MCY		Mary Yuryan 		03-Jun-1992
;		Add check for RRD40S device type in IO_PACKACK, and 
;		branch around READ_CD_MCN for audio if device is 
;
;	X-23-LPL
;		LPL1003	Lee Leahy			24 May 1993
;		1.)  Added .DISABLE GLOBAL to locate undefined symbols.
;		2.)  Added .SHOW MEB to display macro expansions for analysis.
;		3.)  Added $DDTDEF macro.
;		4.)  Added .EXTERNAL definition list.
;		5.)  Removed the .NLIST directive.
;		6.)  Added build time check for TQE size in PDT.
;		7.)  Added the following fields:
;			UCB$L_CTRL_MODE
;			UCB$L_CTRL_MODE_CPAR
;			UCB$L_QUEUED_IO_COUNT
;			UCB$L_QDEPTH
;			UCB$L_QDEPTH_TURNS
;			UCB$L_READ_COUNT
;			UCB$L_WRITE_COUNT
;			UCB$L_OTHER_COUNT
;			UCB$L_READ_XLEN_HIST
;			UCB$L_WRITE_XLEN_HIST
;			UCB$L_XLEN_HIST
;			UCB$L_XLEN_HIST_CYCLE
;			UCB$L_CLASS_BUSY
;			UCB$L_DK_FLAGS		(Added new bits)
;			UCB$Q_IRP_LIST
;		8.)  Added $SCDTDEF macro.
;		9.)  Added queuing characteristic to the SPI$SEND_COMMAND calls.
;		10.) Added form feeds.
;		11.) Changed references from UCB$L_IRP to SCDRP$L_IRP.
;		12.) Cleared UCB$L_IRP following usage to help prevent double deallocation.
;		13.) Modified the LOG_ERROR macro to set the current IRP.
;		14.) Added code to support UCB$Q_IRP_LIST.
;		15.) Made the routines global so that the driver symbol table
;		     could be used for debugging.
;		16.) Added code to support UCB$L_CLASS_BUSY.
;		17.) Added code to set and clear UCB$V_BSY.
;		18.) Added code to update UCB$L_QUEUED_IO_COUNT.
;
;	X-23	SCS	Sue Sommer			 12-Apr-1993
;		Tie off IO$_AUDIO function until it can be fixed later.
;
;	X-22	SCS	Sue Sommer			 24-Mar-1993
;		Add new devices to SCSI_DEV_TYPES table.
;
;	X-21	SCS	Sue Sommer			 24-Feb-1993
;		Add boot order (BT_ORDER) value to DPTAB to insure that DKDRIVER
;		init routines execute before those of SHDRIVER (shadowing);
;		choose value of 5000 to match DUDRIVER.
;
;	X-20	SCS	Sue Sommer			  9-Feb-1993
;		Add missing .JSB_ENTRY mask to DK_CRESHAD.  In INIT_SCDRP,
;		initialize SCDRP type for easier crash dump analysis.
;		In unit initialization routine, set MAXBLOCKS and
;		ONLINE!BSY UCB bits before forking, to resolve
;		synchronization problems.
;
;	X-19	SCS	Sue Sommer			  6-Nov-1992
;		Change the name of ALLOC_POOL to DK_ALLOC_POOL to reflect
;		the new input of R5;  insure that all calls to it have
;		a KPB address in R5.
;
;		Merge in Blade changes (through X-32) as follows.  Support
;		for DDR, C2, and register saving in DK_CTRL_INIT are not
;		included, but the following is merged:
;
;		MCY		Mary Yuryan			22-Oct-1992
;		Add "B" variant disks to device recognition tables - RZ26B,
;		RZ27B, RZ28B, RZ29B, RZ73B, RZ74B, RZ75B.  Add RZ27L, RWZ21.
;		Add HSZ20,HSZ40 - Raid subsystem.
;
;		MCY		Mary Yuryan			10-Sep-1992
;		Fix 2 bugs in the ATTEMPT_REORDER routine.
;		1)Update R4 correctly so as not to alter the behavior of
;		  the elevator algorithm; currently the scan direction may
;		  be effected.
;		2)Provide UCB$L_FAIRNESS_CNT (with value of 4) to prevent a
;		  malicious user from looping infinitely while using the
;		  same LBN.  Checks added in SWAP_IRP:, SCAN_DONE:, and
;		  PERFECT_IRP:.	 This could slow or even hang the system.
;
;		JSSBLADE2	John S. Simakauskas		 1-July-1992
;		Fix build bug introduced in X-29,
;		SCSI_DEV_TYPES table was made too long.
;
;		JSSBLADE	John S. Simakauskas		24-June-1992
;		Add device recognition for RZ26L,RRD43,RRD44.
;
;		MCY		Mary Yuryan		12-Jun-1992
;		Always return good status in the READ_CD_MCN routine
;		to keep 3rd party CDROMS that do not support SCSI-2
;		from failing.
;
;		MCY		Mary Yuryan		03-Jun-1992
;		Add check for RRD40S device type in IO_PACKACK, and
;		branch around READ_CD_MCN for audio if device is
;
;		MCY		Mary Yuryan		03-Apr-1992
;		Add ASSUME statement before $DEFEND of UCB to check for
;		illegal System disk UCB expansion.  Value is calculated
;		in longwords.  (Unnecessary for Alpha  /SCS 13-Nov-1992)
;
;		MCY		Mary Yuryan		11-Mar-1992
;		Add latent support for RZ28 and RZ29.
;		Fold in John Guineau's AUDIO fix from AMBER - Change
;		UCB$M_CDROM symbol to UCB$V_CDROM in BBC instruction
;		in IO_PACKACK routine.
;
;		MCY		Mary Yuryan		  20-Feb-1992
;		Add latent support for RAID controller - HSZ10.
;
;		Fold in Richard Napolitano's changes for AUDIO fix.
;		RLN		Richard L. Napolitano	15-Dec-1991
;		1) Format zero workaround
;		2) Modified LOG_EXTND_SENSE not to log read errors
;		   to CDROM's being mounted foreign.
;
;		BP4282		Brian Porter		04-FEB-1992
;		Use an ADAWI to increment and decrement UCB$W_QLEN.
;		(Alpha use PRESERVE atomicity instead /SCS 13-Nov-1992)
;		Sync .IDENT to VSC.
;
;		MCY		Mary Yuryan		09-Jan-1992
;		Fix bug in GET_DEVICE_TYPE - Restore R0 containing
;		INQUIRY command data pointer.
;
;		MCY		Mary Yuryan		23-Dec-1991
;		Merge AUDIO and Latent device support with C2.
;		(C2 not merged in Alpha version	 /SCS 13-Nov-1992)
;
;		FAK003	     Forrest A. Kenney	     26-Nov-1991
;		Merge latest round of BLADE fixes.
;
;		WJG0048		W. John Guineau		14-Nov-1991
;		Add DDR support. UCB$L_DISABLE_DDR is a flag to disable
;		the use of DDR. If non-zero, no DDR will be used.
;		(Note:	Support is commented for Alpha	/SCS  13-Nov-1992)
;
;		MCY		Mary Yuryan		11-Nov-1991
;		Merged Rich Napolitano's AUDIO changes into the driver.
;		Redefine READPROMPT function to AUDIO using the same IODEF
;		symbol value for READPROMPT.
;
;			Richard L. Napolitano
;		Implemented AUDIO extension to DKDRIVER to allow AUDIO
;		commands to be sent to SCSI II compliant CD-ROM devices.
;		These extensions allow the execution of CD AUDIO commands
;		from application programs.
;
;				Mary Yuryan		10-Dec-1991
;		Add latent device support: RZ27,RZ37,RZ38,RZ75,RZ59,
;		EZ51,EZ52,EZ53,EZ54,EZ58,RZ13,RZ14,RZ15,RZ16,RZ17,RZ18,
;		RZ34L,RZ35L,RZ36L.
;
;		MCY		Mary Yuryan		10-Oct-1991
;		Add latent support for the RZ26, RZ36, adn RZ74.
;		(VSC Ident change.)
;
;		MCY		Mary Yuryan / John Guineau  2-Oct-1991
;		Fix DK_CTRL_INIT routine to properly save CDDB initialization
;		registers before forking.
;		(Omit for Alpha	 /SCS 13-Nov-1992)
;
;		JTK		Jim Klumpp		13-Aug-1991
;		Change to place in the driver where ATTEMPT_REORDER
;		is called to the end of STARTIO so third-party disk
;		caching products can continue to intercept I/Os at
;		the front of STARTIO. Change the ATTEMPT_REORDER
;		routine to sort only the pending queue, ignoring
;		the active IRP at UCB$L_IRP. Terminate the pending
;		queue scan after a finite of IRPs have been considered
;		for reorder.
;
;	X-18	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-17	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-16	SWA		Scott W. Apgar		05-Aug-1992
;		Changing some references to the AUXSTRUC offset in the
;		CRB to use the new field SCS_STRUC.  AUXSTRUC was
;		overloaded with functionality.	The AUXSTRUC offset is
;		still used to hold the address of a Trace Buffer.
;
;	X-15	SFS0561		Stephen F. Shirron	06-Jul-1992
;		Add new SCSI devices.
;
;	X-14	SCS	Sue Sommer			 5-Jun-1992
;		Update BOFF and ABCNT correctly in routine IO_RW_ERR.
;
;	X-13	SCS	Sue Sommer			 27-May-1992
;		Add new devices to SCSI device type table.
;
;	X-12	SCS	Sue Sommer			 18-May-1992
;		Update SVAPTE and ABCNT correctly in routine IO_RW_ERR.
;
;	X-11	SCS	Sue Sommer			 29-Apr-1992
;		In ATTEMPT_REORDER, update R4 correctly and add fairness
;		counter to prevent the same LBN from being serviced repeatedly.
;
;	X-10	SCS	Sue Sommer			 28-Apr-1992
;		In CHECK_HBS, add check to branch around this code on
;		the ADU only.
;
;	X-9	SCS	Sue Sommer			 17-Apr-1992
;		Add symbol alignment where appropriate in calls to $DEFINI.
;		Merge in changes from VAX/VMS V5.4-3 as follows:
;
;		WJG		W. John Guineau		16-May-1991
;		Use EXE$ZEROPARM as FDT routine for IO$_NOP to solve
;		crashes from this function code.
;
;		JJM		Jeff McLeman		16-Apr-1991
;		Increase floppy disconnect timeouts to 30 seconds
;
;		MCY	Mary Yuryan			19-Feb-1991
;		Add new SCSI devices to the SCSI_DEV_TYPES symbol table.
;		RWZ01, RZ24L, RZ25L, RZ55L, RZ56L, RZ57L.
;
;		JTK	Jim Klumpp			30-Jan-1991
;		Add a workaround for RZ23 read long bug. After a read
;		long command is sent to the RZ23, the next mode select
;		command fails with an illegal request sense key. The
;		fix is to simply retry the mode select command once.
;
;		MCY	Mary Yuryan			25-Jan-1991
;		Increase RRD42 Disconnect timeout to 120 seconds.
;
;		GH001	Gary Hughes			21-Dec-1990
;		Fix up media ID mismatch for RZ23L, RZ57I.
;
;		JTK	Jim Klumpp			11-Dec-1990
;		1) Add new devices: RZ72, RZ73, RZ35.
;
;		2) In SET_UNIT_ONLINE, set the LCL_VALID bit in the UCB
;		if the PACKACK completes successfully for the system
;		disk. This is required for host-based shadowing booting.
;
;		JTK	Jim Klumpp			28-Nov-1990
;		1) Change the disconnect timeout time for the RRD42 to
;		45 seconds.
;
;		2) Add host-based shadowing support including routines to
;		check that a device is capable of supporting HBS (CHECK_HBS),
;		to force uncorrectable ECC errors on a set of blocks
;		(FORCE_ERROR), and to erase a set of blocks (IO_DSE).
;
;		3) Fix clearing of LCL_VALID bit in UCB at the end of
;		IO_PACKACK.
;
;		4) Use byte count divisor returned by SPI$CONNECT in
;		DK_UNIT_INIT.
;
;		WCY0146	Wai C. Yim			 7-Nov-1990
;		1) Add code to create CDDB in controller init.
;
;		2) Change all references of EXE$GL_SYSUCB to SYS$AR_BOOTUCB
;		Since it is no longer the same for shadowing.
;
;		3) Add shadowing booting support.  Synchronize with DSA
;		class driver by checking for different SYS$AR_BOOTUCB
;		from EXE$GL_SYSUCB.
;
;		4) Set SCSI bit in DEVCHAR2 in all devices by means
;		of DPT_STORE UCB macro.
;
;		5) Add CRESHAD and REMSHAD FDT routines.
;
;	------------- Ident change due to master pack cleanup --------
;
;	X-29	SCS	Sue Sommer			 12-Mar-1992
;		Add RX26 support.
;
;	X-28	LSS0243		Leonard S. Szubowicz	 3-Mar-1992
;		In FDT routine DK_DIAGNOSE obtain QIO parameters from the IRP
;		since AP no longer points to P1 in an FDT routine.  Also,
;		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 DK_ALLOC_SCDRP, assure that the SCDRP allocated on the
;		stack is quadword aligned for performance reasons.
;
;	X-27	SCS	Sue Sommer			 23-Feb-1992
;		Remove END argument in DPTAB so the driver may be sliced.
;
;	X-26	SCS	Sue Sommer			 6-Feb-1992
;		Reinstate the reordering of IRPs for performance.
;		Only reorder IRPs in the pending queue;	 exclude the
;		current IRP from reordering in order to avoid switching
;		KPBs after a kernel process has already started, and
;		in order to accommodate third party software.
;
;	X-25	ROW0802		Ralph O. Weber		28-JAN-1992 16:04
;		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-24	BJT291		Benjamin J. Thomas III	  9-Jan-1992
;		Promote UCB ERRCNT, ERTCNT and ERTMAX fields to longword
;
;	X-23	BJT286		Benjamin J. Thomas III	 5-Dec-1991
;		Use HLL KPB register mask rather than hand-built one
;
;	X-22	BJT271		Benjamin J. Thomas III	15-Nov-1991
;		Promote FUNC fields
;
;	X-21	BJT265		Benjamin J. Thomas III	11-Nov-1991
;		Promote more IRP and UCB fields to longwords.
;
;	X-20	BJT260		Benjamin J. Thomas III	31-Oct-1991
;		Promote more IRP and UCB fields to longwords.
;
;	X-19	BJT259		Benjamin J. Thomas III	25-Oct-1991
;		Promote word fields STS in IRP and UCB to longwords
;
;	X-18	SCS	Sue Sommer			 17-Oct-1991
;		Modify SAVE_CONNECT_CHAR to use correct argument count.
;
;	X-17	SCS	Sue Sommer			 8-Oct-1991
;		Promote remaining occurrences of IRP$W_STS to IRP$L_STS.
;
;	X-16	SCS	Sue Sommer			 5-Oct-1991
;		Promote various word fields to longwords in the SCDRP, SCDT,
;		SPDT, and UCB.
;		Remove the temporary check for a bad IRP.
;
;		Merge in changes by Mary Yuryan, Barbara Leahy, Jim Klumpp,
;		and John Guineau from VAX/VMS V5.4-2 as follows:
;		Create RRD42 disconnect timeout symbol definitions and
;		set that tmo to 120 seconds.
;		Added context save on IO_DIAGNOSE function for mixed-mode
;		media support.
;		Send a request sense command immediately after sending a
;		mode select to clear out a possible pending check condition
;		that could otherwise cause a subsequent command to fail.
;		Also, in TRANS_SENSE_KEY, treat mode select change status
;		as normal. An assumption here is we're not running in a
;		multi-initiator environment.
;		Change the spinup timeout value from 20 to 30 seconds
;		to accomodate the RZ25.	 Change READY_POLL_INTERVAL to 1,
;		and READY_POLL_CNT to 30.
;		Add processing of LCL_VALID bit in IO_FORMAT and IO_PACKACK.
;		Call SET_CONN_CHAR in SET_UNIT_ONLINE to prevent format
;		operations from timing out before a PACKACK function has
;		successfully executed.
;		Change the symbol definition of LO_COST_CD to the
;		correct device name of RRD42.
;		Added UCB$B_SCSI_VERSION to save INQUIRY response data
;		format (SCSI version). This allows version-specific
;		code paths in mode select, where the setting of the
;		non-changable parameters is incompatible between SCSI-1
;		(CCS) and SCSI-2.
;		Fix IO$_WRITECHECK function. Prior to this change, this
;		function was identical to a write with the datacheck
;		modifier. The correct operation is to compare the data
;		in the user's buffer with that on the disk, and not
;		modify the contents of either the user's buffer or the
;		disk.
;
;	X-15	TYC0001		Theresa Y. Chin		30-Sep-1991
;		Get rid of double fork in UNIT_INIT and implement
;		a new counter to wait for port to be initialized
;		before class driver init proceeds.
;
;	X-14	BJT247		Benjamin J. Thomas III	27-Sep-1991
;		Promote IRP$W_STS to IRP$L_STS
;
;	X-13	LSS0224		Leonard S. Szubowicz	 6-Sep-1991
;		Expand UCB$W_QLEN to a longword to avoid word granularity
;		problems within the same quadword.
;
;	X-12	SCS	Sue Sommer			 13-Aug-1991
;		Add temporary code to check for bad IRP.  Convert form
;		feeds to .PAGE for list file.
;
;	X-11	SCS	Sue Sommer			 29-Jul-1991
;		Revise SCSI return status codes SCSI$C_CONDITION_MET and
;		SCSI$C_RESERV_CONFLICT to have correct values.	Modify
;		unit routine to save the UCB address in the KPB before
;		calling KP_SWITCH_TO_KP_STACK, and to restore it afterwards.
;
;	X-10	JTK	Jim Klumpp			 15-Jul-1991
;		Add entry to FDT for NOP function.
;
;	X-9	SCS	Sue Sommer			  9-Jul-1991
;		Fix argument in call to KP_ALLOCATE_KPB
;
;	X-8	SCS	Sue Sommer			  2-Jul-1991
;		Modify unit initialization routine so that the KPB is
;		deallocated at the end of unit init.
;
;	X-7	SCS	Sue Sommer			 26-Apr-1991
;		Modify IO_DATACHECK routine to pass correct SVAPTE and
;		BOFF of the datacheck buffer to the port driver.  Fix
;		shift factor bug in calculating VBN's for segmented I/O.
;
;	X-6	SCS	Sue Sommer			 8-Apr-1991
;		(Temporary)  Comment out code in Startio which reorders
;		IRPs for performance.  Pending IRPs have no associated KPB,
;		which presents a problem should such a pending IRP be
;		chosen as the current optimal IRP.  Eventually this code
;		will be restored and IRP$PS_KPB will point to a valid KPB;
;		until the implications are fully explored, comment it out.
;
;
;	X-5	SCS	Sue Sommer			12-Mar-1991
;		Fix datacheck SPTE allocation in unit init to account
;		for non-page-aligned buffers.  Fix segmented I/O code
;		to update SVAPTE correctly.
;
;	---------- Numbering change due to master pack reorg -----------
;
;	X-1K6	SCS	Sue Sommer			4-Mar-1991
;		Replace INSV instruction in READ_WRITE routine to
;		correctly set LBN.
;
;	X-1K5	SCS	Sue Sommer			20-Feb-1991
;		Use FORK_ROUTINE macro to generate .JSB_ENTRY mask
;		in UNIT_INIT.  Change entry mask on DK_CTRL_INIT, DK_DIAGNOSE,
;		CHECK_REV_LEVEL, and TRACE_QIO_STAT.  Change SCSI_DEV_TYPES
;		macro to generate naturally aligned data;  likewise modify
;		GET_DEVICE_TYPE.
;
;	X-1K4	SCS	Sue Sommer			7-Feb-1991
;		Add support for new routines EXE$READLOCK_ERR and
;		EXE$WRITELOCK_ERR in IO_DIAGNOSE.
;
;	X-1K3	SCS	Sue Sommer			1-Feb-1991
;		Include $KPBDEF.
;
;	X-1K2	SCS	Sue Sommer			24-Jan-1991
;		Initial port to Alpha.
;
;	X-14U3	JTK	Jim Klumpp			6-Jun-1990
;		1) 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.
;
;		2) Calculate the value for UCB$W_CYLINDERS using the same
;		algorithm as DUDRIVER so that all nodes in a cluster see
;		see the same geometry.
;
;		3) Work around a bug in RZ5x drives which cause them to
;		report busy SCSI status indefinitely after a reselection
;		timeout. Pull on bus reset if this situation is detected.
;
;	X-14U2	JTK	Jim Klumpp			20-Dec-1989
;		Use extended read and write SCSI commands for transfers
;		to blocks above 1FFFFF (hex).
;
;	X-14U1	JTK	Jim Klumpp			1-Dec-1989
;		Add seek optimization routine.
;
;	X-14	JTK	Jim Klumpp			22-Sep-1989
;		Return MEDOFL status when an attempt is made to mount a
;		removable device whose media is not inserted. Increase
;		default disconnect timeout time for all RZ disks to 20
;		seconds.
;
;	X-13	JTK	Jim Klumpp			20-Sep-1989
;		Fix setting of disconnect timeout for floppy formatting.
;
;	X-12	RLN	Richard L. Napolitano		29-Aug-1989
;		Workaround a microcode problem in the RZ55 where during
;		recovered with retry read operation, the RZ55 returns
;		an additional block if the DTE bit is set.
;
;
;	X-11	RLN	Richard L. Napolitano		25-Aug-1989
;		Fix problem where during a READ operation of a transfer
;		with a bad block that is reassigned, the blocks following
;		the reassigned blocks are erroneously written.
;
;	X-10	DGB0317	Donald G. Blair			05-Aug-1989
;		Add DPT$V_NO_IDB_DISPATCH bit to the driver prologue table.
;		In SETUP_CMD, use correct register to set up timeout
;		fields.
;
;	X-9	JTK	Jim Klumpp			12-Jul-1989
;		Add LUN support. Don't allocate the IRP portion of
;		the SCDRP. Remove references to the SCDT. Increase
;		number of arguments passed to SET_CONN_CHAR. Change
;		priv required for IO_DIAGNOSE function.
;
;	X-8	JTK	Jim Klumpp			22-Jun-1989
;		Add callback support and data structure version
;		checking for SPI$CONNECT. Add fastboot support.
;		Fix logging of reassign block error. Check HWL bit
;		to prevent writes to a write-locked disk. Fix
;		handling of media changes in TRANS_SENSE_KEY.
;
;	X-7	JTK,KAH	Jim Klumpp, Kenneth A. House	15-Jun-1989
;
;		1) Change descriptor length in reassign parameter list to 4.
;		2) Fix checking of read/write flag in DK_DIAGNOSE.
;		3) Increase disconnect timeout for reassign command.
;		4) Allow writing of double-density (DD) microfloppies only
;		   when the UCB$L_DK_FLAGS<DD_BYPASS> flag is asserted,
;		   which it won't be unless the driver has been patched.
;		   Changes include
;		   A) add DD_BYPASS flag to UCB$L_DK_FLAGS,
;		   B) clear DD_BYPASS in UNIT_INIT to provide space for
;		      an in-place patch,
;		   C) set UCB$L_DEVCHAR<SWL> flag in MODE_SENSE routine if an
;		      RX23 diskette has a 250 KHz transfer rate (implies DD),
;		   D) adding hooks for DD format and geometry parameters in
;		      MODE_SELECT_FORMAT_FLOPPY (only if DD_BYPASS is set).
;		5) Fix branch destination of a BBSS used for a bit set in
;		   MODE_SENSE routine.
;
;	X-6	JTK,KAH	 Jim Klumpp, Kenneth A. House	25-Mar-1989
;		A number of misc changes for PVAX1 including:
;		1) Update ident to match that on master pack.
;		2) Add pass-through routine.
;		3) In INQUIRY, use just 5 bits if peripheral device type
;		   and 4 bits of response data format to conform to SCSI-2.
;		4) Change SCSI status byte mask from ^XE1 to ^XC1 to conform
;		   to SCSI-2.
;		5) In TRANS_SENSE_KEY, ignore unit attentions caused by media
;		   changes if the device is not volume enable to avoid mount
;		   failures.
;		6) Fix R6 bug in MODE_SELECT_FORMAT_FLOPPY.
;		7) Ensure unformatted diskette causes MODE_SENSE to return
;		   SS$_FORMAT status.
;		8) Move call of MODE_SENSE_CHANGABLE from within MODE_SENSE
;		   to follow each call to MODE_SENSE
;		9) Allow MODE_SENSE to return SS$_FORMAT in IO_FORMAT routine
;		   (implies that the diskette isn't currently formatted).
;	       10) Avoid logging error when MODE_SENSE detects an unformatted
;		   floppy.
;
;	X-20-B	KAH079	Kenneth A. House		04-May-1989
;		Increase size of MODE_SELECT maximum data transfer;
;		Merge changes from Jim Klumpp & Lee Leahy.
;
;		X-20	JTK	Jim Klumpp		31-Mar-1989
;			Clear UCB$L_BCNT in SETUP_CMD if no data is to
;			be transferred to prevent possible corruption.
;
;		X-19	LPL	Lee Leahy		29 Mar 1989
;			Changed media ID for generic DK devices from
;			0 to 22C8BC0 (DK-DKX0) to enable them to be
;			properly served via the MSCP.
;
;	X-18-B	KAH073	Kenneth A. House	17-Apr-1989
;		Debugging;
;		Merge changes from Jim Klumpp.
;
;	X-18-A	KAH070	Kenneth A. House	14-Mar-1989
;		Add SCSI floppy support.
;
;	X-18	JTK	Jim Klumpp		27-Jan-1989
;		Add MCSP extension to UCB.  Advance ident to match
;		CMS generation on LES pack.
;
;	X-4	JTK	Jim Klumpp		19-Jan-1989
;		Perform minimum revision checking on devices.
;
;	X-3	JTK	Jim Klumpp		13-Jan-1989
;		Clean up comments, disable tracing for SDC release.
;
;	X-2	RLN	Richard L. Napolitano	20-DEC-1988
;
;		1) Remove loop in MODE_SENSE_CHANGEABLE.
;		2) Complete fix to ignore DCR bit in MODE_SENSE routine
;		3) Fix register useage for flexible disk geometry page.

	.PAGE
	.SBTTL	+
	.SBTTL	+ SYMBOL DEFINITIONS
	.SBTTL	+
	.SBTTL	External symbol definitions

;
; External symbols
;
	$ARBDEF					; Define ARB offsets
	$BOOSTATEDEF				; boot state flags
	$CADEF					; Conditional Assembly Parameters
	$CANDEF					; Cancel reason codes
	$CDDBDEF				; Class Driver Data Block
	$CLUBDEF				; Cluster block
	$CRBDEF					; Channel request block
	$DCDEF					; Device classes and types
	$DDBDEF					; Device data block
	$DDTDEF					; Device dispatch table
	$DEVDEF					; Device characteristics
	$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
	$HWRPBDEF				; Hardware RPB
	$IDBDEF					; Interrupt data block
	$IODEF					; I/O function codes
	$IPLDEF					; Hardware IPL definitions
	$IRPDEF					; I/O request packet
	$IRPEDEF				; I/O request packet extension
	$KPBDEF					; Kernel process block
	$MODEDEF				; Mode page definitons
	$MSCPDEF				; Define MSCP symbols
	$NSADEF					; Security Symbols
	$ORBDEF					; Object Rights block
	$PAGEDEF				; Page size
	$PCBDEF					; Process control block
	$PRVDEF					; Privilege mask
	$PTEDEF					; Page table entry symbols
	$SCSIDEF				; SCSI Definitions
	$SCDRPDEF				; SCSI SCDRP symbols
	$SCDTDEF				; SCSI CDT symbols
	$SPDTDEF				; SCSI PDT symbols
	$SPIDEF					; SCSI SPI symbols
	$SSDEF					; System status codes
	$TQEDEF					; Timer Queue Entry definition
	$UCBDEF					; Unit control block
	$VADEF					; Virtual address symbols
	$VECDEF					; Interrupt vector block

	ASSUME	TQE$K_LENGTH EQ SPDT$S_TQE	; Validate TQE size.

	.SBTTL	External References

	.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	CLU$GL_CLUB

	.EXTERNAL	CTL$GL_PCB

	.EXTERNAL	DKMK$DIAGNOSE_INIT
	.EXTERNAL	DKMK$DIAGNOSE_FDT	
	.EXTERNAL	DKMK$DIAGNOSE_SIO	

	.EXTERNAL	DO_MODE_PAGE

	.EXTERNAL	EXE$ALONONPAGED
	.EXTERNAL	EXE$DEANONPGDSIZ
	.EXTERNAL	EXE$DEANONPAGED
	.EXTERNAL	EXE$GL_ABSTIM
	.EXTERNAL	EXE$GL_HBS_PTR
	.EXTERNAL	EXE$GL_SHADOW_SYS_DISK
	.EXTERNAL	EXE$GQ_SYSTIME
	.EXTERNAL	EXE$GQ_SYSTYPE
	.EXTERNAL	EXE$GL_SYSUCB
	.EXTERNAL	EXE$KP_ALLOCATE_KPB
	.EXTERNAL	EXE$KP_FORK_WAIT
	.EXTERNAL	EXE$KP_RESTART
	.EXTERNAL	EXE$KP_START
	.EXTERNAL	EXE$KP_STALL_GENERAL
	.EXTERNAL	EXE$KP_TQE_WAIT

	.EXTERNAL	EXE_STD$ABORTIO
	.EXTERNAL	EXE_STD$ALLOCIRP
	.EXTERNAL	EXE_STD$DEANONPGDSIZ
	.EXTERNAL	EXE_STD$FINISHIO
	.EXTERNAL	EXE_STD$LCLDSKVALID
	.EXTERNAL	EXE_STD$INSIOQ
	.EXTERNAL	EXE_STD$KP_STARTIO
	.EXTERNAL	EXE_STD$LOCK_ERR_CLEANUP
	.EXTERNAL	EXE_STD$MODIFYLOCK
	.EXTERNAL	EXE_STD$ONEPARM
	.EXTERNAL	EXE_STD$READLOCK
	.EXTERNAL	EXE_STD$SENSEMODE
	.EXTERNAL	EXE_STD$SETMODE
	.EXTERNAL	EXE_STD$WRITELOCK
	.EXTERNAL	EXE_STD$WRITECHK
	.EXTERNAL	EXE_STD$ZEROPARM

	.EXTERNAL	IOC$ADD_DEVICE_TYPE
	.EXTERNAL	IOC$GL_SPI_CONNECT
	.EXTERNAL	IOC$INITIATE
	.EXTERNAL	IOC$REMOVE_DEVICE_TYPE
	.EXTERNAL	IOC$REQCOM
	.EXTERNAL	IOC$RETURN

	.EXTERNAL	INI$BRK

	.EXTERNAL	LDR$ALLOC_PT
	.EXTERNAL	LDR$DEALLOC_PT

	.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

	.IF DF	CA$_MEASURE_IOT
	.EXTERNAL	PMS$START_IO
	.ENDC

	.EXTERNAL	SCS$GL_MSCP_NEWDEV
	.EXTERNAL	SCS$DISK_MSCP_DRIVER_MV

	.EXTERNAL	SGN$GL_VMS7

	.EXTERNAL	SMP$ACQUIRE
	.EXTERNAL	SMP$RESTORE
	.EXTERNAL	SMP$GL_FLAGS
	.EXTERNAL	SMP$AL_IPLVEC

	.EXTERNAL	SYS$AR_BOOTUCB

	.PAGE
	.SBTTL	+
	.SBTTL	+ SYMBOL DEFINITIONS (cont.)
	.SBTTL	+
	.SBTTL	Misc local symbols
;
; Local symbols
;

;	DEBUG = 1				; Flag to enable debuging
	.IF DEFINED DEBUG
	.print ; - DEBUG flag is enabled
	.IFF
	.print ; - DEBUG flag is not enabled
	.ENDC

;	TRACE = 1				; Flag to enable tracing
	.IF DEFINED TRACE
	.print ; - TRACE flag is enabled
	.IFF
	.print ; - TRACE flag is not enabled
	.ENDC

;	TRACE_SCSI = 1				; Enable SCSI command tracing
	.IF DEFINED TRACE_SCSI
	.print ; - TRACE_SCSI flag is enabled
	.IFF
	.print ; - TRACE_SCSI flag is not enabled
	.ENDC

;	TRACE_CB = 1				; Enable tracing of UCB$L_CLASS_BUSY
	.IF DEFINED TRACE_CB
	.print ; - TRACE_CB flag is enabled
	.IFF
	.print ; - TRACE_CB flag is not enabled
	.ENDC

; Argument list offset definitions

	P1			= 0		; First function dependent parameter
	P2			= 4		; Second function dependent parameter
	P3			= 8		; Third function dependent parameter
	P4			= 12		; Fourth function dependent parameter
	P5			= 16		; Fifth function dependent parameter
	P6			= 20		; Sixth function dependent parameter

	SCDRPS_PER_UNIT		= 15		; Number of SCRPs to pre-allocate per unit
	PACKACK_RETRY		= 10		; Number of times to retry PACKACK
	READY_POLL_CNT		= 30		; Number of times to poll for unit ready

	ASSUME IOC$C_DISK_BLKSIZ EQ 512
	IOC$S_DISK_BLKSIZ	= 9
	MAX_BCNT		= <<64*2>-1>*512; Maximum byte count/transfer (64KB - 1 block)
	RW_RETRY_CNT		= 3		; Read/write retry count
	REASSIGN_RETRY_CNT	= 3		; Retry count for block reassignment
	REWRITE_RETRY_CNT	= 3		; Retry count for rewrite after reassign
	SENDCMD_RETRY_CNT	= 10		; Retry count for send command SCSI$C_BUSY retries
	SCSI_CMD_BUF_OVHD	= 4 + 4		; 4 bytes to save status byte +
						; 4 bytes for SCSI command length


	DEFAULT_DISCONNECT_TIMEOUT = 20		; Default values in seconds for disconnect
	DEFAULT_PHASE_CHANGE_TIMEOUT = 4	; and phase change timeouts
	RRD40_DISC_TMO		= 20		; RRD40 disconnect timeout value
	RRD42_DISC_TMO		= 120		; RRD42 disconnect timeout value
	RRD42_DISC_TMO		= 120		; RRD42 disconnect timeout value
	RX_DISC_TMO		= 30		; Floppy disconnect timeout value
	RX_PHS_TMO		= 10		; Floppy phase change timeout value
	RX_FMT_DISC_TMO		= 10*60		; Floppy format disconnect timeout value
	REASSIGN_DISCON_TMO	= 60		; Reassign disconnect timeout
	DK_INIT_TIMEOUT = 60			; Init timeout value = 60 seconds

	SS$_RECOVERR		= 2		; Recoverable error detected. Note that
						; this status code is used internlly
						; to distinguish between recoverable and
						; non-recoverable errors, but is never
						; returned as a QIO status.
	DK_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.

	DTE_EXTRA_BYTES	= IOC$C_DISK_BLKSIZ	; Number of additional bytes received
	RZ55_MIN_NODTE_ERROR = ^A'0900'		; Mininum revision with fixed microcode

	EXTND_LBN_LIMIT	= ^X1FFFFF		; Maximum LBN for which a normal read or
						; write command can be used. The extended
						; read/write commands must be used for
						; LBNs above this value. (21-bits)
						; See SCSI-2 (LBA address for READ)

	MAX_BUSY_TIME	= 10			; Maximum amount of time to allow a
						; device to remain busy before resetting
						; the bus

	; Additional Sense Information Buffer Length
	ADD_SENSE_LEN	= SCSI$SNS$R_ADD_SENSE-SCSI$SNS$R_ADD_INFORMATION
	SENSE_ALLOC	= 255			; Sense data allocation length

; Histogram buckets are expressed as a power of 2 to make collection
; fast - basically we logically shift the datum by the bucket factor
; to get a bucket index.

	XLEN_HIST_BUCKET_SHIFT = 2		; If MAX_XFER is 128, 128>>2 = 32 buckets
	XLEN_HIST_BUCKETS = 32			; we will round down to 64K (128blk) xfers
	XLEN_HIST_SIZE = <<128/4>*4>		; 1 long/bucket
	XLEN_HIST_TURNOVER = 100		; Recalc qdepth after this many I/O

	READ_LONG_DATA_LEN = 1024		; Maximum data length associated
	WRITE_LONG_DATA_LEN = 1024		; with read long and write long
						; commands, respectively.
;+=
;
; Definition of the offsets into the Audio Control Block (AUCB) for DKDRIVER.
;
	$DEFINI	AUDIO_CONTROL_BLOCK

	CD_AUCB_CUR_VERSION	= 1		; Current Version number of AUCB
$DEF	CD_FUNCTION_CODE	.BLKB	2	; Audio function code
$DEF	CD_AUCB_VERSION		.BLKB	2	; Version number of AUCB structure
$DEF	CD_ARG1			.BLKL	1	; Command specific parameter
$DEF	CD_ARG2			.BLKL	1	; Command specific parameter
$DEF	CD_ARG3			.BLKL	1	; Command specific parameter
$DEF	CD_RSVD1		.BLKL	1	; Reserved for future use (MBZ)
$DEF	CD_DEST_BUF_ADDR	.BLKL	1	; Buffer returned to user
$DEF	CD_DEST_BUF_CNT		.BLKL	1	; Size of buffer returned to user
$DEF	CD_DEST_BUF_TRANS_CNT	.BLKL	1	; Actual number of bytes received
$DEF	CD_COMMAND_STATUS	.BLKL	1	; VMS O.S. Return status
$DEF	CD_SCSI_STATUS		.BLKL	1	; SCSI command status (optional)
$DEF	CD_SENSE_ADDR		.BLKL	1	; Sense data buffer
$DEF	CD_SENSE_CNT		.BLKL	1	; Sense data buffer size
$DEF	CD_SENSE_TRANS_CNT	.BLKL	1	; Sense data transfer count
$DEF	CD_RESVD2				; Reserved for future used (MBZ)
$DEF	CD_AUCB_SIZE				; Size in bytes of AUCB
	$DEFEND	AUDIO_CONTROL_BLOCK

; Definition of SCSI Audio command function codes. These are used by application
; to select individual audio functions supported by the driver.
;
	PAUSE		= 0			; Pause
	RESUME		= 1			; Resume
	PREVENT_REMOVAL = 2			; Prevent/Allow
	ALLOW_REMOVAL	= 3			; Prevent/Allow
	PLAY_AUDIO	= 4			; Play Audio LBA
	PLAY_AUDIO_MSF	= 5			; Play MSF
	PLAY_AUDIO_TRACK= 6			; Play Audio Track
	PLAY_TRACK_REL	= 7			; Play Track Relative
	READ_HEADER	= 8			; Reserved
	GET_STATUS	= 9			; Read Subchannel-Q
	GET_TOC		= 10			; Read TOC
	SET_VOLUME	= 11			; Mode Select
	GET_VOLUME	= 12			; Mode Sense
	SET_DEFAULT	= 13			; Reserved
	GET_DEFAULT	= 14			; Reserved

;+=
;+
; Definitions for offsets into the SCSI CCB as used by DKDRIVER.
;-
	PAGE_CODE	= 6			; Page Code Field
	A_LEN		= 8			; Allocation Length Field



	.PAGE
	.SBTTL	+
	.SBTTL	+ SYMBOL DEFINITIONS (cont.)
	.SBTTL	+
	.SBTTL	Disk class driver extensions to the UCB
;
; Disk class driver extensions to the UCB.
;

	.SYMBOL_ALIGNMENT QUAD
	$DEFINI	UCB			; Start of UCB definitions

	. = UCB$K_MSCP_DISK_LENGTH

$DEF	UCB$L_DK_FLAGS		.BLKL	1	; Class driver flags
	$VIELD	UCB,0,<-			;
		<REMOVABLE,,M>,-		; Removable media
		<FIRST_ATTN_SEEN,,M>,-		; First unit attention seen (used to
		-				; prevent logging an error for the
		-				; first unit attention seen)
		<SPINUP_INPROG,,M>,-		; Spin-up in progress
		<DISCONNECT,,M>,-		; Device supports disconnect
		<SYNCHRONOUS,,M>,-		; Device supports synchronous operation
		<MODE_SENSE_PAG1,,M>,-		; Modes sense page 1 recieved
		<MODE_SENSE_PAG10,,M>,-		; Modes sense page 10 recieved
		<DISABL_ERRLOG,,M>,-		; Disable errorlogging
		<OUT_OF_REV,,M>,-		; Device is out-of-rev
		<HWL,,M>-			; Hardware write-locked
		<FLOPPY,,M>,-			; Device is a flexible disk drive
		<FORMAT,,M>,-			; Device supports FORMAT opcode
		<NOREASSIGN,,M>,-		; Drive doesn't support REASSIGN_BLOCK
		<DD_BYPASS,,M>,-		; allow writing of DD diskette
		<HBS_CHECK,,M>,-		; Check made for host-based shadowing
		<FLOPPY_MEDIA,2,M>,-		; Diskette type in floppy drive
				-		;   0 - none, or unknown
				-		;   1 - DD
				-		;   2 - HD
				-		;   3 - ED
		<CDROM,,M>,-			; Device is a CD-ROM
		<CD_VALID,,M>,-			; CD media change occured if bit is
			      -			; cleared. Used for Audio CD's, which
			      -			; are not mounted, to indicate UCB
			      -			; stored CD-ROM Sub-channel data is
			      -			; valid.
		<RAID,,M>,-			; Set if device is known to be an array
		<PORT_CMDQ,,M>,-		; Set if port supports command queuing
		<CMDQ,,M>-			; Device supports command queuing
		<OPTICAL,,M>,-			; Device is an optical device
		<WORM,,M>,-			; Device is a WORM device, or optical WORM media
		<DDR,,M>,-			; Device supports DDR, (default is DISABLED!)
		<PORT_AEN,,M>,-			; Device supports AEN (Asynch-Event-Notification)
		<PORT_AUTOSENSE,,M>,-		; Device or Port supports Autosense
		<TENBYTE,,M>,-			; Device supports 10-byte mode sense command
		<CLUSQ,,M>>			; Device can be shared on SCSI cluster

$DEF	UCB$L_CLASS_BUSY	.BLKL 1		; Reasons why UCB$V_BSY should remain set.

	$VIELD	UCB,0,<-			; Single thread the following operations:
		-
		<CB_NOCMDQ,,M>,-		; Command queueing not supported.
		-
		-				; Initialization and error recovery.
		<CB_INIT,,M>,-			;    Executing UNIT_INIT code.
		-				; <CB_PORT_CALLBACK,,M>,-		;    PORT_CALLBACK received.
		<CB_BBR_IN_PROG,,M>,-		;    Bad Block Replacement in progress.
		<CB_MNTVERIP,,M>,-		;    Mount verification in progress.
		<CB_KP_STARTIO,,M>, -		;    EXE_STD$KP_STARTIO Stalled
		<CB_CHECK_CONDITION,,M>, -	;    Check_condition active
		<CB_QUEUE_FULL_EVNT,,M>, -	;    Queue full_event
		<CB_BUS_RESET,,M>, -		;    Bus reset in progress
		<CB_NO_SEND_CREDITS,,M> -	;    No send credits
		<CB_NO_CMD_BITS,,M>,-		;    No SPDT command bits
		-
		-				; Single threaded I/O Requests.
		<CB_SINGLE_RW,,M>,-		;   Single threaded read/write request in progress.
		<CB_SINGLE_DC,,M>,-		;   Single threaded datacheck request in progress.
		<CB_SINGLE_DSE,,M>,-		;   Single threaded data security erase in progress.
		<CB_FORCE_ERROR,,M>,-		;   Force error in progress.
		<CB_NOP,,M>,-			;   IO$_NOP in progress.
		<CB_AVAILABLE,,M>,-		;   IO$_AVAILABLE in progress.
		<CB_UNLOAD,,M>,-		;   IO$_UNLOAD in progress.
		<CB_PACKACK,,M>,-		;   IO$_PACKACK in progress.
		<CB_DIAGNOSE,,M>,-		;   IO$_DIAGNOSE in progress.
		<CB_FORMAT,,M>,-		;   IO$_FORMAT in progress.
		<CB_AUDIO,,M>, -		;   IO$_AUDIO in progress.
		<CB_BUSY,,M>, -			;   Wait while busy
		>

	.=<<.+3>& ^C3>		; Align data structure
$DEF	UCB$Q_IRP_LIST		.BLKQ	1	; List of active I/O requests.
$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

	.=<<.+3>& ^C3>		; Align data structure
$DEF	UCB$Q_DC_WAIT_LIST	.BLKQ	1	; Doublely linked list of Datacheck waiters
$DEF	UCB$Q_DRAIN_LIST	.BLKQ	1	; Doublely linked list of I/O Drain waiters
$DEF	UCB$IS_DRAIN_COUNT	.BLKL	1	; Count of stalled SCDRP's on above list
$DEF	UCB$L_QUEUED_IO_COUNT	.BLKL 	1	; Outstanding I/O count to all port drivers.

$DEF	UCB$PS_SCDRP		.BLKL	1	; Address of active SCDRP
$DEF	UCB$PS_UNITINIT_KPB	.BLKL	1	; Pointer to KPB allocated in unit init
$DEF	UCB$PS_SCDT		.BLKL	1	; Address of SCDT

$DEF	UCB$L_CUR_LBN		.BLKL	1	; Current LBN, used for read/write reordering
$DEF	UCB$L_FAIRNESS_CNT 	.BLKL	1	; Fairness counter in IRP reordering

$DEF	UCB$L_HW_REV		.BLKL	1	; Hardware revision info from inquiry
$DEF	UCB$L_ERROR_TYPE	.BLKL	1	; Error type used by LOG_ERROR
$DEF	UCB$L_ERR_MASK		.BLKL	1	; Mask of error types logged so far
$DEF	UCB$L_VMS_STATUS	.BLKL	1	; VMS status used by LOG_ERROR
						; reordering of I/Os for seek optimization
$DEF    UCB$L_DISABLE_DDR	.BLKL	1       ; Non zero means disable DDR
                                        	;  (default is DISABLED!)
$DEF	UCB$L_INITMO		.BLKL	1	; Unit init timeout value
$DEF	UCB$PS_AUCB_ADDR	.BLKL	1	; Audio control block address
$DEF	UCB$PS_FORMAT_PARAM	.BLKL	1	; Address of sense information
						; needed to format floppies
$DEF	UCB$PS_SAVE_CONN_CHAR	.BLKL	1	; Address of saved connection
						; characteristics buffer for IO
						; diagnose buffer


NUM_LONGWORDS_DIAGNOSE = 6			; UCB area used by IO_DIAGNOSE
$DEF	UCB$PS_DIAGNOSE	.BLKL NUM_LONGWORDS_DIAGNOSE  

$DEF	UCB$W_READL_LEN		.BLKW	1	; Value to use in read long len field
$DEF	UCB$W_PHASE_TMO		.BLKW	1	; Phase change timeout
$DEF	UCB$W_DISC_TMO		.BLKW	1	; Disconnect timeout
$DEF	UCB$W_BLOCK_SIZE	.BLKW	1	; Number of bytes per block

$DEF	UCB$B_LUN		.BLKB	1	; Logical unit number (LUN)
$DEF	UCB$B_SEEK_DIR		.BLKB	1	; Current motion of heads, used for
$DEF	UCB$B_SCSI_VERSION	.BLKB	1	; SCSI version (CCS or SCSI-2)
$DEF	UCB$B_RW_RETRY		.BLKB	1	; Read/write retry count

$DEF	UCB$B_REASSIGN_RETRY	.BLKB	1	; Reassign block retry count
$DEF	UCB$B_REWRITE_RETRY	.BLKB	1	; Rewrite after reassign retry count
$DEF	UCB$B_READY_RETRY	.BLKB	1	; Polling count for unit ready

; For CD-ROMS, Sub-Channel Q - Media Catalog Data (UPC)
$DEF	UCB$B_MCN_SCDATA	.BLKB SCSI$SUBQ$C_MCN_LENGTH
	.=<<.+3>& ^C3>		; Align data structure

$DEF	UCB$R_SENSE_INFO	.BLKB <ADD_SENSE_LEN>; Balance of additional sense info
$DEF	UCB$B_SENSE_LEN		.BLKB	2	; Count of additional bytes
	.=<<.+3>& ^C3>		; Align data structure

$DEF	UCB$R_RECOV_PAR		.BLKB SCSI$ERP$S_ERROR_RECOVERY ; Error recovery parameters
	UCB$B_RECOV_PAR_LENGTH = UCB$R_RECOV_PAR+SCSI$ERP$B_PAGE_LENGTH
	UCB$B_RECOV_PAR_FLAGS =	 UCB$R_RECOV_PAR+SCSI$ERP$R_RECOVERY_FLAGS
	.=<<.+3>& ^C3>		; Align data structure

$DEF	UCB$R_RECOV_CPAR 	.BLKB SCSI$ERP$S_ERROR_RECOVERY ; Err rec changable parameters
	UCB$B_RECOV_CPAR_FLAGS = UCB$R_RECOV_CPAR+SCSI$ERP$R_RECOVERY_FLAGS
	.=<<.+3>& ^C3>		; Align data structure

$DEF	UCB$R_CTRL_MODE		.BLKB SCSI$CMP$S_CONTROL_MODE	; Control mode parameters
	UCB$B_CTRL_MODE_LENGTH = UCB$R_CTRL_MODE+SCSI$CMP$B_PAGE_LENGTH
	UCB$B_CTRL_MODE_FLAGS  = UCB$R_CTRL_MODE+SCSI$CMP$R_CONTROL_MODE_FLAGS
	.=<<.+3>& ^C3>		; Align data structure

$DEF	UCB$R_CTRL_MODE_CPAR	.BLKB SCSI$CMP$S_CONTROL_MODE	; Control mode changable parameters
	UCB$B_CTRL_MODE_CFLAGS = UCB$R_CTRL_MODE_CPAR+SCSI$CMP$R_CONTROL_MODE_FLAGS
	.=<<.+3>& ^C3>		; Align data structure

$DEF	UCB$L_QDEPTH		.BLKL 1		; The max qdepth for this device
$DEF	UCB$L_QDEPTH_TURNS	.BLKL 1		; How many times we've actually changed
$DEF	UCB$L_READ_COUNT	.BLKL 1		; Count reads done
$DEF	UCB$L_WRITE_COUNT	.BLKL 1		; Count writes done
$DEF	UCB$L_OTHER_COUNT	.BLKL 1		; All other I/O functions
$DEF	UCB$L_READ_XLEN_HIST	.BLKL 1		; Pointer to Read histogram pool
$DEF	UCB$L_WRITE_XLEN_HIST	.BLKL 1		; Pointer to Write histogram pool
$DEF	UCB$L_XLEN_HIST		.BLKL 1		; Pointer to total histogram pool
$DEF	UCB$L_XLEN_HIST_CYCLE	.BLKL 1		; # I/Os per histogram turnover

	.=<<.+3>& ^C3>		; Align data structure
$DEF	UCB$R_BUSY_BIT_IRP .BLKB IRP$C_LENGTH	; BUSY_BIT_IRP to used to keep 
						; UCB$V_BSY active across KP_REQCOM

	.IF DEFINED TRACE_CB			; Class_busy Ring-Buffer Support
$DEF	UCB$L_TOP_OF_RING	.BLKL 1
$DEF	UCB$L_RING_POINTER	.BLKL 1
$DEF	UCB$L_RING_COUNTER	.BLKL 1
	.ENDC

$DEF	UCB$K_DK_UCBLEN				; Length of extended UCB

	$DEFEND	UCB				; End of UCB definitions
	.SYMBOL_ALIGNMENT NONE

	.PAGE
	.SBTTL	+
	.SBTTL	+ SYMBOL DEFINITIONS (cont.)
	.SBTTL	+
	.SBTTL	Disk class driver extensions to the SCDRP

;
; Disk class driver extensions to the SCDRP
;
	$DEFINI	SCDRP			; Start of SCDRP definitions
	. = SCDRP$K_LENGTH

	.IF DEFINED TRACE
$DEF	SCDRP$PS_TR_QIO_STS	.BLKL	1	; Address in trace buf to put QIO status
$DEF	SCDRP$PS_TR_SCSI_CMD	.BLKL	1	; Address in trace buf to put QIO status
$DEF	SCDRP$PS_TR_SCSI_STS	.BLKL	1	; Address in trace buf to put QIO status
	.ENDC

$DEF	SCDRP$K_DK_LENGTH			; Length of extended SCDRP
	$DEFEND	SCDRP				; End of SCDRP definitions

	.PAGE
	.SBTTL	+
	.SBTTL	+ SYMBOL DEFINITIONS (cont.)
	.SBTTL	+
	.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$L_LW_CNT		.BLKL	1	; Count of number of LWs that follow
$DEF	ERR$B_REVISION		.BLKB	1	; Revision level
$DEF	ERR$L_HW_REV		.BLKL	1	; Hardware revision
$DEF	ERR$B_TYPE		.BLKB	1	; Error type
$DEF	ERR$B_SCSI_ID		.BLKB	1	; SCSI ID
$DEF	ERR$B_SCSI_LUN		.BLKB	1	; SCSI logical unit
$DEF	ERR$B_SCSI_SUBLUN	.BLKB	1	; SCSI sub-logical unit
$DEF	ERR$L_PORT_STATUS	.BLKL	1	; Port status code
$DEF	ERR$A_CMD_LEN		.BLKB	1	; SCSI command length field
$DEF	ERR$B_SCSI_STS		.BLKB	1	; SCSI status byte
$DEF	ERR$A_ADD_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$A_CMD_BYTES		.BLKB	12	; Maximum possible command bytes
$DEF	ERR$K_COMMAND_LENGTH			; Length of packet containing SCSI command

$DEF	ERR$A_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
ASSUME MODE_DESC$S_FIELD_DESCRIPTOR EQ 40	
$DEF	ERR$A_MODE_SENSE_DATA	.BLKB 240	; Mode sense data
$DEF	ERR$K_MODE_SENSE_LENGTH			; Length of packet containing mode
						; sense data
	.=ERR$K_COMMAND_LENGTH
$DEF	ERR$A_REASSIGN_BLOCK_DATA	.BLKB 8	; Reassign block data
$DEF	ERR$K_REASSIGN_BLOCK_LENGTH		; Length of packet containing reassign
						; block data

	$DEFEND	ERROR_PACKETS

;
; Define storage offsets for format parameters in non-paged pool
;
	.SYMBOL_ALIGNMENT QUAD

	$DEFINI	DIAG_DESCRIPTOR
$DEF	DSC_OPCODE		.BLKL	1		; Opcode
	DSC$C_PASSTHRU = 1				; Passthru is only supported function
$DEF	DSC_FLAGS		.BLKL	1		; Operation flags
	$VIELD	DSC,0,<		<DATAIN,,M>,-		;    Direction (1=datain .vs. 0=dataout)
				<DISCON,,M>,-		;    Enable Disconnect
				<SYNCH,,M>,-		;    Enable Synchronous
				<DISPORT,,M>>		;    Disable Port Retry
$DEF	DSC_CMDADR		.BLKL	1		; SCSI command buffer address
$DEF	DSC_CMDLEN		.BLKL	1		; SCSI command buffer length
$DEF	DSC_DATADR		.BLKL	1		; Data buffer address
$DEF	DSC_DATLEN		.BLKL	1		; Data buffer length
$DEF	DSC_PADCNT		.BLKL	1		; Pad count
$DEF	DSC_PHSTMO		.BLKL	1		; SCSI Bus phase change timeout
$DEF	DSC_DSCTMO		.BLKL	1		; SCSI Bus disconnect duration
$DEF	DSC_RESERVED		.BLKB	<60-.>		; Reserved
$DEF	DSC_LENGTH					; Length of generic SCSI descriptor
	$DEFEND	DIAG_DESCRIPTOR

;
; Define offset for DK_ALLOC_POOL standard header
;
	$DEFINI	DK_POOL_HEADER
$DEF	DK$DYN$PS_UCB		.BLKL	1	; Pointer to requesting UCB
$DEF	DK$DYN$PS_KPB		.BLKL	1	; Pointer to requesting KPB
$DEF	DK$DYN$W_SIZE		.BLKW	1	; Size of pool allocation requested
$DEF	DK$DYN$B_TYPE		.BLKB	1	; Allocation type (DYN$C_SCSICLS)
$DEF	DK$DYN$B_SUBTYPE	.BLKB	1	; Allocation subtype (DYN$C_MISC)
$DEF	DK$DYN$L_ALLOC_LEN	.BLKL	1	; Allocation length
$DEF	DK$DYN$C_LENGTH				; Length of DK non-paged pool header
	$DEFEND	DK_POOL_HEADER

	.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	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:
;
	$DEFINI	SCSI_DEVICE_ENTRY
$DEF	DTYP_TYPE	.BLKB	1		; VMS Device type
$DEF	DTYP_FLAGS	.BLKB	1		; Disc/synch flags
$DEF	<>		.BLKW	1  		; Pad for alignment
$DEF	DTYP_ID_STRING	.BLKL	2		; ID String
$DEF	DTYP_MINREV	.BLKL	1		; Min. Revision level
$DEF	DTYP_MEDIA_ID	.BLKL	1		; Media Id
$DEF	DTYP_PHASE_TMO	.BLKW	1		; Phase Change Timeout
$DEF	DTYP_DISC_TMO	.BLKW	1		; Disconnect Timeout
$DEF   	DTYP_TABLE_ENTRY_SIZE			; Size of SCSI Device Table Entry
	$DEFINI	SCSI_DEVICE_ENTRY

; Each table entry is  24 bytes, which allows successive entries
; to be naturally aligned.

	ASSUME	DTYP_TABLE_ENTRY_SIZE EQ 24

; The table is terminated with a VMS device code of 0.
;-
	.MACRO	SCSI_DEV_TYPES, LIST
	.ALIGN	LONG

	.MACRO	SCSI_DEV_TYPES1, ID_STRING, DEVICE_TYPE, MEDIA_ID,-
		MINIMUM_REVISION, DISCONNECT, SYNCHRONOUS,-
		PHASE_CHANGE_TIMEOUT, DISCONNECT_TIMEOUT

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

; Device type

	.BYTE	DT$_'DEVICE_TYPE'

; Flags, including disconnect and synchronous

	$$$FLAGS = 0
	.IIF IDN DISCONNECT, YES, $$$FLAGS = $$$FLAGS + 1
	.IIF IDN SYNCHRONOUS, YES, $$$FLAGS = $$$FLAGS + 2
	.BYTE	$$$FLAGS

; Pad to longword align the entry

	.WORD	0

; 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.

	.ASCII	/MINIMUM_REVISION/

; Media ID field, derived from the device type if possible, otherwise supplied
; in the table.

	.IF DIF <ID_STRING>, GENERIC
	.IF B MEDIA_ID
	MEDIA <DK>, <DEVICE_TYPE>
	.IFF
	MEDIA <DK>, <MEDIA_ID>
	.ENDC
	.IFF
	MEDIA	<DK>, <DKX00>, DT$_Generic_DK	; Media value for generic DK device
	.ENDC

; Phase change timeout

	$$$PHASE_CHANGE_TIMEOUT = DEFAULT_PHASE_CHANGE_TIMEOUT
	.IF DIF PHASE_CHANGE_TIMEOUT, DEFAULT
		$$$PHASE_CHANGE_TIMEOUT = PHASE_CHANGE_TIMEOUT
	.ENDC
	.WORD	$$$PHASE_CHANGE_TIMEOUT

; Disconnect timeout

	$$$DISCONNECT_TIMEOUT = DEFAULT_DISCONNECT_TIMEOUT
	.IF DIF DISCONNECT_TIMEOUT, DEFAULT
		$$$DISCONNECT_TIMEOUT = DISCONNECT_TIMEOUT
	.ENDC
	.WORD	$$$DISCONNECT_TIMEOUT


	.ENDM	SCSI_DEV_TYPES1

	.IRP	ENTRY,<LIST>
	SCSI_DEV_TYPES1 ENTRY
	.ENDR

	.ENDM	SCSI_DEV_TYPES

	.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 table will be
; scanned 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 disk class driver error
;+
; LOG_ERROR
;
; This macro logs a SCSI disk class driver error. The error type and VMS status
; code are placed in the appropriate UCB fields, and the LOG_ERROR routine is 
; called, which does most of the work.
; Context:
;
;	SCDRP/FORK thread
;	IPL = Fork
;	Fork lock held
;
;-

	.MACRO	LOG_ERROR type,vms_status,ucb=R3,scdrp	;SF FF;
	PUSHR	#^M<R4,R5>			;SF FF; Save registers
	.IF	NOT_BLANK,scdrp                  
	MOVL	scdrp,R4			;SF FF; Get SCDRP address
	.IFF
	CLRL	R4				;SS FF; No SCDRP address
	.ENDC
	.IF	DIF ucb,R5
	MOVL	ucb,R5				;SF FF; Get UCB address
	.ENDC
	MOVL	R4,UCB$PS_SCDRP(R5)		;SF FF; for reg_dump only
	MOVL	#SCSI$C_'type',-		;SF FF; Get error code
		UCB$L_ERROR_TYPE(R5)		;SF FF;
	MOVL	vms_status,UCB$L_VMS_STATUS(R5)	;SF FF; And VMS Status code
	BSBW	LOG_ERROR			;SF FF; Write an errorlog entry
	POPR	#^M<R4,R5>			;SF FF; Restore registers
	.ENDM	LOG_ERROR

	.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

	.PAGE
	.SBTTL	DISABLE_ERRLOG	- Temporarily disable errorlogging
	.SBTTL	REENABLE_ERRLOG	- Reenable errorlogging
;+
; DISABLE_ERRLOG
; REENABLE_ERRLOG
;
; Context:
;
;	SCDRP Thread
;	IPL = Fork
;	Fork lock held
;
; 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 retrying reads or writes
; to blocks with [non-]recoverable errors, errorlogging is disabled so that
; just one error is logged. Since the disabling of errorlogging can be nested,
; the old value of the DISABL_ERRORLOG flag is saved in the local UCB stack.

	.MACRO	DISABLE_ERRLOG			;S FF;
	PUSHL	UCB$L_DK_FLAGS(R3)		;S FF; Save current flags value
	ASSUME	UCB$V_DISABL_ERRLOG LT 8	;S FF;
	BISB	#UCB$M_DISABL_ERRLOG,-		;S FF; Temporarily disable errorlogging
		UCB$L_DK_FLAGS(R3)		;S FF;
	.ENDM	DISABLE_ERRLOG

	.MACRO	REENABLE_ERRLOG			;S FF;
	POPL	UCB$L_DK_FLAGS(R3)		;S FF; Reenable errorlogging
	.ENDM	REENABLE_ERRLOG

	.PAGE
	.SBTTL	DK_ALLOC_SCDRP	- Allocate an SCDRP
;+
; DK_ALLOC_SCDRP
;
; This macro is used to allocate an SCDRP on the kernel process stack.
;
; INPUTS:
;	R3	- UCB address
;	R5	- Old SCDRP Address
;
; OUTPUTS:
;	R5	- SCDRP address
;	R0	- destroyed
;

	.MACRO	DK_ALLOC_SCDRP
	PUSHL	R5				;F FF; Save Original SCDRP address
	PUSHL	SCDRP$PS_KPB(R5)		;F FF; Save Original KPB Address
	SUBL3	#SCDRP$K_DK_LENGTH, SP, R5	;F FF; Subtract size of SCDRP
	BICL	#7, R5				;F FF; Quadword align SCDRP
	SUBL	#SCDRP$K_DK_LENGTH+8, SP	;F FF; Account for SCDRP + unalignment
	BSBW	INIT_SCDRP			;S FF; Initialize allocated SCDRP
	MOVL	SCDRP$K_DK_LENGTH+8(SP),-	;S FF; Copy Original KPB address
		SCDRP$PS_KPB(R5)		;S FF; ... to this SCDRP's KPB address
	MOVL	SCDRP$K_DK_LENGTH+8+4(SP),-	;S FF; Copy Original SCDRP address
		SCDRP$PS_PREV_SCDRP(R5)		;S FF; ... to this SCDRP's PREV SCDRP
	.ENDM	DK_ALLOC_SCDRP

	.SBTTL	DK_DEALLOC_SCDRP	- Deactivate an SCDRP
;+
; DK_DEALLOC_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:
;	none
;
	.MACRO	DK_DEALLOC_SCDRP,CLRSTK=NO,?L1,?L2 ;S FF;
	.IF DEFINED DEBUG
	TSTL	SCDRP$IS_ITEM_NUM(R5)		;S FF; Hanging on to any mapping regs?
	BNEQ	L1				;S FF; Branch if so
	TSTL	SCDRP$IS_ITEM_CNT(R5)		;S FF; Hanging on to any mapping regs?
	BEQL	L2				;S FF; Branch if so
L1:	BUG_CHECK INCONSTATE,FATAL		;S FF; Should not deactivate an SCDRP
	.ENDC
L2:	.IF IDN <CLRSTK>,<YES>			;S FF; If explicit deallocation is required
	ADDL	#SCDRP$K_DK_LENGTH+8+4,SP	;S FF; Clear SCDRP + Original KPB Address
	POPL	R5				;S FF; Restore Original SCDRP Address
	.ENDC
	.ENDM	DK_DEALLOC_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 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
;	+-----------------------+
;	|   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

	.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	DMA_LEN
	$$$DIRECTION = 0
	.IIF IDN DMA_DIR, READ, $$$DIRECTION = 1
	.BYTE	$$$DIRECTION

	.ENDM	SCSI_CMD

;+
; This function will minimize to the smaller of val1 and val2.
; If val1 is greater than val2, then assign val1 the value of val2.
;-
	.MACRO	MINUM	val1,val2, ?l10
	CMPL	'val1','val2'
	BLEQ	l10
	MOVL	'val2','val1'
l10:
	.ENDM	MINUM



;+
; $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


;
; Macros used for RAID management
;
; IF_RAID, IF_NOT_RAID
;	R3 - UCB
;
	.MACRO	IF_RAID, LABEL
	BBS	#UCB$V_RAID,-			;S FF
		UCB$L_DK_FLAGS(R3),LABEL
	.ENDM	IF_RAID

	.MACRO	IF_NOT_RAID, LABEL
	BBC	#UCB$V_RAID,-			;S FF
		UCB$L_DK_FLAGS(R3),LABEL
	.ENDM	IF_NOT_RAID

;
; Macros for Command-Queue management
;
; IF_CMDQ, IF_NOT_CMDQ
;	R3 ~ UCB
;
	.MACRO	IF_CMDQ,LABEL,UCB=R3		;SF FF
	.BRANCH_LIKELY
	BBS	#UCB$V_CMDQ,-			;SF FF; Command queueing support
		UCB$L_DK_FLAGS(UCB),LABEL	;SF FF;
	.ENDM	IF_CMDQ

	.MACRO	IF_NOT_CMDQ,LABEL,UCB=R3	;SF FF
	.BRANCH_UNLIKELY
	BBC	#UCB$V_CMDQ,-			;SF FF No Command queueing support
		UCB$L_DK_FLAGS(UCB),LABEL	;SF FF;
	.ENDM	IF_NOT_CMDQ

;
; Macros for UCB busy single threaded management
;
; SET_SINGLE
; CLR_SINGLE
; TST_SINGLE
;	R3 ~ UCB

	.MACRO	SET_SINGLE flag,ucb,lab,scdrp,drain_cancel,?l1,?l2,?l3

	; flag	 = One of the single thread states, define in UCB$L_CLASS_BUSY
	; ucb	 = The current UCB address
	; scdrp	 = The current SCDRP address
	; lab	 = (optional) label to transfer control to if bit is already set
	; drain_cancel	 = (optional) label to transfer control to if the macro
	;		   IF_CANCEL detects a canceled drain
			   

	.IF DEFINED TRACE_CB
	PUSHL	R7				;S FF; Save register
	MOVL	UCB$L_RING_POINTER(ucb), R7	;S FF; Get pointer to ring buffer
	MOVL	#UCB$M_CB_'flag, TRB$L_TYPE(R7)	;S FF; Save operation type
	TRACE_CB = TRACE_CB + 1
	.PRINT	TRACE_CB			;S FF; Current value of tracer
	MOVL	#TRACE_CB, TRB$L_PC(R7)		;S FF; Save caller's marker
	MOVL	UCB$L_CLASS_BUSY(ucb),-		;S FF; Save class busy bits
		TRB$L_CLASS_BUSY(R7)		;S FF;
	MOVL	UCB$L_STS(ucb),-		;S FF; Save UCB status bits
		TRB$L_STS(R7)
	MOVAL	TRB$C_LENGTH(R7),-		;S FF; Update ring pointer
		UCB$L_RING_POINTER(ucb)
	DECL	UCB$L_RING_COUNTER(ucb)		;S FF; Update cntr
	BNEQ	l3				;S FF; Wrap?
	MOVL	#1024, UCB$L_RING_COUNTER(ucb)	;S FF; If yes, reinit
	MOVL	UCB$L_TOP_OF_RING(ucb),-	;S FF;
		UCB$L_RING_POINTER(ucb)		;S FF;
l3:	POPL	R7				;S FF; Restore register
	.ENDC

	.IF	BLANK lab
	.PRESERVE ATOMICITY
	BBSS	#UCB$V_CB_'flag,-		;SF FF; Single stream this I/O request
		UCB$L_CLASS_BUSY(ucb),l2	;SF FF;
	.NOPRESERVE ATOMICITY
	BRB	l1				;SF FF;
l2:	BUG_CHECK INCONSTATE,FATAL		;SF FF; Busy bit already set.
	.IFF
	.PRESERVE ATOMICITY
	BBSS	#UCB$V_CB_'flag,-		;SF FF; Single stream this I/O request
		UCB$L_CLASS_BUSY(ucb),lab	;SF FF;
	.NOPRESERVE ATOMICITY
	.ENDC
l1:	BISL	#UCB$M_BSY,UCB$L_STS(ucb)	;SF FF; Set UCB busy bit

	.IF	NOT_BLANK drain_cancel
	PUSHL	scdrp				;S FF; Address of current SCDRP
	PUSHL	ucb				;S FF; Address of current SCDRP
	CALLS	#2, DK_DRAIN_IO			;S FF; Wait for I/O's to DRAIN
;	IF_CANCEL <drain_cancel>,<scdrp>	;S FF; Check for cancel under I/O drain
	.ENDC

	.ENDM	SET_SINGLE

	.MACRO	CLR_SINGLE flag,ucb,lab,?l1,?l2,?l3

	; flag	 = One of the single thread states, define in UCB$L_CLASS_BUSY
	; ucb	 = The current UCB address (default = R5)
	; lab	 = (optional) label to transfer control to if bit is already set

	.IF DEFINED TRACE_CB
	PUSHL	R7				;S FF; Save register
	MOVL	UCB$L_RING_POINTER(ucb), R7	;S FF; Get pointer to ring buffer
	MOVL	#^cUCB$M_CB_'flag,TRB$L_TYPE(R7);S FF; Save operation type
	TRACE_CB = TRACE_CB + 1
	.PRINT	TRACE_CB			;S FF; Current value of tracer
	MOVL	#TRACE_CB, TRB$L_PC(R7)		;S FF; Save caller's marker
	MOVL	UCB$L_CLASS_BUSY(ucb),-		;S FF; Save class busy bits
		TRB$L_CLASS_BUSY(R7)		;S FF;
	MOVL	UCB$L_STS(ucb),-		;S FF; Save UCB status bits
		TRB$L_STS(R7)
	MOVAL	TRB$C_LENGTH(R7),-		;S FF; Update ring pointer
		UCB$L_RING_POINTER(ucb)
	DECL	UCB$L_RING_COUNTER(ucb)		;S FF; Update cntr
	BNEQ	l3				;S FF; Wrap?
	MOVL	#1024, UCB$L_RING_COUNTER(ucb)	;S FF; If yes, reinit
	MOVL	UCB$L_TOP_OF_RING(ucb),-	;S FF;
		UCB$L_RING_POINTER(ucb)		;S FF;
l3:	POPL	R7				;S FF; Restore register
	.ENDC

	.IF	BLANK lab
	.PRESERVE ATOMICITY
	BBCC	#UCB$V_CB_'flag,-		;SF FF; Single stream this I/O request
		UCB$L_CLASS_BUSY(ucb), l2	;SF FF;
	.NOPRESERVE ATOMICITY
	BRB	l1				;SF FF;
l2:	BUG_CHECK INCONSTATE,FATAL		;SF FF; Busy bit already set.
	.IFF
	.PRESERVE ATOMICITY
	BBCC	#UCB$V_CB_'flag,-		;SF FF; Single stream this I/O request
		UCB$L_CLASS_BUSY(ucb), lab	;SF FF;
	.NOPRESERVE ATOMICITY
	.ENDC
l1:	.ENDM	CLR_SINGLE

	.MACRO	TST_SINGLE label,ucb=R5,flag_reg=R1,?l3

	; label	 = label to transfer control to if any bits are set
	; ucb	 = The current UCB address (default = R5)
	; flag_reg = (optional) register to place current value of UCB$L_CLASS_BUSY;

	.IF DEFINED TRACE_CB
	PUSHL	R7				;S FF; Save register
	MOVL	UCB$L_RING_POINTER(ucb), R7	;S FF; Get pointer to ring buffer
	MOVL	#0,TRB$L_TYPE(R7)		;S FF; Save operation type
	TRACE_CB = TRACE_CB + 1
	.PRINT	TRACE_CB			;S FF; Current value of tracer
	MOVL	#TRACE_CB, TRB$L_PC(R7)		;S FF; Save caller's marker
	MOVL	UCB$L_CLASS_BUSY(ucb),-		;S FF; Save class busy bits
		TRB$L_CLASS_BUSY(R7)		;S FF;
	MOVL	UCB$L_STS(ucb),-		;S FF; Save UCB status bits
		TRB$L_STS(R7)
	MOVAL	TRB$C_LENGTH(R7),-		;S FF; Update ring pointer
		UCB$L_RING_POINTER(ucb)
	DECL	UCB$L_RING_COUNTER(ucb)		;S FF; Update cntr
	BNEQ	l3				;S FF; Wrap?
	MOVL	#1024, UCB$L_RING_COUNTER(ucb)	;S FF; If yes, reinit
	MOVL	UCB$L_TOP_OF_RING(ucb),-		;S FF;
		UCB$L_RING_POINTER(ucb)		;S FF;
l3:	POPL	R7				;S FF; Restore register
	.ENDC

	BICL3	#UCB$M_CB_NOCMDQ,-		;S FF; Single threaded operation needed
		UCB$L_CLASS_BUSY(ucb), flag_reg	;S FF; (
	BNEQ	label				;S FF; thread busy branch.
	.ENDM	TST_SINGLE

	.MACRO	TRACE_SINGLE trace_flag,ucb=R5,?l3
	.IF DEFINED TRACE_CB
	PUSHL	R7				;S FF; Save register
	MOVL	UCB$L_RING_POINTER(ucb), R7	;S FF; Get pointer to ring buffer
	MOVL	#^X'trace_flag',TRB$L_TYPE(R7)	;S FF; Save operation type
	TRACE_CB = TRACE_CB + 1
	.PRINT	TRACE_CB			;S FF; Current value of tracer
	MOVL	#TRACE_CB, TRB$L_PC(R7)		;S FF; Save caller's marker
	MOVL	UCB$L_CLASS_BUSY(ucb),-		;S FF; Save class busy bits
		TRB$L_CLASS_BUSY(R7)		;S FF;
	MOVL	UCB$L_STS(ucb),-		;S FF; Save UCB status bits
		TRB$L_STS(R7)
	MOVAL	TRB$C_LENGTH(R7),-		;S FF; Update ring pointer
		UCB$L_RING_POINTER(ucb)
	DECL	UCB$L_RING_COUNTER(ucb)		;S FF; Update cntr
	BNEQ	l3				;S FF; Wrap?
	MOVL	#1024, UCB$L_RING_COUNTER(ucb)	;S FF; If yes, reinit
	MOVL	UCB$L_TOP_OF_RING(ucb),-		;S FF;
		UCB$L_RING_POINTER(ucb)		;S FF;
l3:	POPL	R7				;S FF; Restore register
	.ENDC
	.ENDM	TRACE_SINGLE

	.MACRO	TRACE_SINDATA trace_flag,clsbsy,sts,ucb=R5,?l3
	.IF DEFINED TRACE_CB
	PUSHL	R7				;S FF; Save register
	MOVL	UCB$L_RING_POINTER(ucb), R7	;S FF; Get pointer to ring buffer
	MOVL	#^X'trace_flag',TRB$L_TYPE(R7)	;S FF; Save operation type
	TRACE_CB = TRACE_CB + 1
	.PRINT	TRACE_CB			;S FF; Current value of tracer
	MOVL	#TRACE_CB, TRB$L_PC(R7)		;S FF; Save caller's marker
	MOVL	clsbsy, TRB$L_CLASS_BUSY(R7)	;S FF; Save what was specified
						;S FF;   for class busy bits
	MOVL	sts, TRB$L_STS(R7)		;S FF; Save what was specified
						;S FF;   for UCB status bits
	MOVAL	TRB$C_LENGTH(R7),-		;S FF; Update ring pointer
		UCB$L_RING_POINTER(ucb)
	DECL	UCB$L_RING_COUNTER(ucb)		;S FF; Update cntr
	BNEQ	l3				;S FF; Wrap?
	MOVL	#1024, UCB$L_RING_COUNTER(ucb)	;S FF; If yes, reinit
	MOVL	UCB$L_TOP_OF_RING(ucb),-		;S FF;
		UCB$L_RING_POINTER(ucb)		;S FF;
l3:	POPL	R7				;S FF; Restore register
	.ENDC
	.ENDM	TRACE_SINDATA

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

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

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


	.MACRO	ONCE_DK_DEBUG	FLAG,?label
	.IF	DEFINED DEBUG
	BBCC	# DK$V_'FLAG',DK$DEBUG_FLAGS,label
	JSB	G^ INI$BRK
label:	.ENDC
	.ENDM
	
	.MACRO	DK_DEBUG	FLAG,?label
	.IF	DEFINED DEBUG
	BBC 	# DK$V_'FLAG',DK$DEBUG_FLAGS,label
	JSB	G^ INI$BRK
label:	.ENDC
	.ENDM


; IF_CANCEL
;
; This macro checks to see whether the MSCP server has 
; marked the IRP for abort.  If so, it terminates the
; request with SS$_CANCEL status.  Only SCDRPs with
; a nonzero value in SCDRP$L_IRP will be checked;  
; therefore, private SCDRPs (such as for REASSIGN and
; READ_LONG) will not be canceled, since those SCDRPs
; have a zero value in SCDRP$L_IRP.  In order to terminate
; such operations, this macro should be invoked immediately
; before even allocating a private SCDRP of this kind,
; so that the operation may be cancelled via the parent
; SCDRP.

	.MACRO	IF_CANCEL CANCELLED,SCDRP=R5,?L1
	MOVL	SCDRP,R0 			;S FF; Copy SCDRP address
	MOVL	SCDRP$L_IRP(R0),R1             	;S FF; Get IRP address
	BEQL	L1                            	;S FF; Branch if no IRP found

	.BRANCH_LIKELY                         	;S FF; IRP has been found
    	BBC	#IRP$V_SRV_ABORT,-             	;S FF; Has quick kill been
		IRP$L_STS(R1),L1              	;S FF; requested?  Br if no
	MOVL	#SS$_CANCEL,R0                 	;S FF; Else, do the deed
	CLRL	R1                             	;S FF; Clear scratch reg
	BRW	CANCELLED                      	;S FF; And leave
L1:		                               	;S FF;
	.ENDM	IF_CANCEL		       	;S FF;	

	.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_DK_UCBLEN>,-	; Length of UCB
		NAME=DKDRIVER,-			; Driver name
		BT_ORDER=5000,-			; Insure proper init order
		SMP=YES,-			; Driver runs in SMP environment
		FLAGS=<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\F11\>	; 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_DIR!-			; Directory structured
		DEV$M_FOD!-			; Files oriented
		DEV$M_AVL!-			; Available
		DEV$M_ELG!-			; Error logging enabled
		DEV$M_IDV!-			; Input device
		DEV$M_ODV!-			; Output device
		DEV$M_SHR!-			; Shareable Device
		DEV$M_RND>			; Random Access Device
	DPT_STORE UCB,UCB$L_DEVCHAR2,L,<-	; Device characteristics
		DEV$M_SCSI!-			; SCSI device
		DEV$M_NNM!-			; Prefix name with "node$"
		DEV$M_NLT!-                     ; No bad block information on last track
		DEV$M_CLU>			; Cluster Accessable devices

	DPT_STORE UCB,UCB$B_DEVCLASS,B,DC$_DISK	; Sample device class
	DPT_STORE UCB,UCB$W_DEVBUFSIZ,W,IOC$C_DISK_BLKSIZ ; Default buffer size
	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_DK_FLAGS,L,0	; Initialize flags field
	DPT_STORE UCB,UCB$L_ERR_MASK,L,0	; Initialize error mask field
	DPT_STORE UCB,UCB$L_HW_REV,L,0		; Initialize HW revision level
	DPT_STORE UCB,UCB$L_DEVSTS,L,-		; Set no logical to physical
		  UCB$M_NOCNVRT			; block number conversion
	DPT_STORE UCB,UCB$L_STS,L,-		; Say we support EXFUNC bit
		  UCB$M_EXFUNC_SUPP		; in $QIO interface
        DPT_STORE UCB,UCB$L_DISABLE_DDR,L,1     ; Disable DDR by default

	DPT_STORE UCB,UCB$L_READ_XLEN_HIST,L,0	; Make sure these are 0
	DPT_STORE UCB,UCB$L_WRITE_XLEN_HIST,L,0	; Make sure these are 0
	DPT_STORE UCB,UCB$L_XLEN_HIST,L,0	; Make sure these are 0
	DPT_STORE UCB,UCB$L_QDEPTH,W,8		; Default qdepth
	DPT_STORE UCB,UCB$L_QDEPTH_TURNS,L,1	; Set counter

	DPT_STORE REINIT			; Start of reload
						; initialization table
	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=DK,-			; Name of device
		FUNCTB=DK_FUNCTABLE,-		; FDT address
		KP_STACK_SIZE=8192,-		; Size of kernel process stack
		KP_REG_MASK=KPREG$K_HLL_REG_MASK,-; Kernel process reg save mask
		START=DK_STARTIO,-		;F FF; Caller of Start I/O routine
		KP_STARTIO=DK_KP_STARTIO,-	;C FF; Start I/O routine
                ALTSTART=DK_ALTSTARTIO,-        ;F FF; Altstart I/O routine
		CTRLINIT=DK_CTRL_INIT,-		;P X;  Controller init routine
		UNITINIT=DK_UNIT_INIT,-		;P X;  Unit init routine
		MNTVER=DK_MOUNT_VER,-		;F FF; Mount verification routine
		CANCEL=DK_CANCEL,-		;F FF; Cancel I/O routine
		REGDMP=DK_REG_DUMP,-		;? ??; Register dump routine
		FAST_FDT=ACP_STD$FASTIO_BLOCK	; Fast-IO FAST_FDT routine

	.PAGE
	.SBTTL	Function decision table
;+
; Function decision table
;
; This table lists the QIO function codes implemented by the driver and the
; preprocssing routines used by each function.
;-

	FDT_INI	DK_FUNCTABLE
	FDT_BUF <-				; Buffered I/O functions
		ACCESS,-			; Access file and/or find directory
		ACPCONTROL,-			; ACP control function
		AVAILABLE,-			; Available (rewind/nowait clear valid)
		CREATE,-			; Create file and/or directory entry
		DEACCESS,-			; Deaccess file
		DELETE,-			; Delete file and/or directory entry
		DRVCLR,-			; Driver clear
		MODIFY,-			; Modify file attributes
		MOUNT,-				; Mount volume
		NOP,-				; No operation
		PACKACK,-			; Pack acknowledge
		DSE,-				; Data Security Erase
		SEEK,-				; Seek
		SENSEMODE,-			; Sense mode
		SENSECHAR,-			; Sense characteristics
		UNLOAD>				; Unload volume

	FDT_64	<-				; Functions supporting 64-bit addresses
		AVAILABLE,-			; Available (rewind/nowait clear valid)
		DIAGNOSE,-			; Special pass-thru function 
		NOP,-				; No operation
		PACKACK,-			; Pack acknowledge
		READLBLK,-			; Read logical block forward
		READPBLK,-			; Read physical block forward
		READVBLK,-			; Read virtual block
		SENSECHAR,-			; Sense characteristics
		SENSEMODE,-			; Sense mode
		SETCHAR,-			; Set characterisitics 
		SETMODE,-			; Set mode
		UNLOAD,-			; Unload volume
		WRITECHECK,-			; Write check
		WRITELBLK,-			; Write LOGICAL Block
		WRITEPBLK,-			; Write Physical Block
		WRITEVBLK>			; Write VIRTUAL Block

	FDT_ACT	ACP_STD$ACCESS,<-		; Access & create file or directory
		ACCESS,-
		CREATE> 

	FDT_ACT	ACP_STD$DEACCESS,<-		; Deaccess file
		DEACCESS> 

	FDT_ACT ACP_STD$MODIFY,<-		; set for dismounts
		ACPCONTROL,-			; for files 11 dismounts
		DELETE,-			; Delete file and/or directory entry
		MODIFY>				; Modify file attributes

	FDT_ACT	ACP_STD$MOUNT,<-
		MOUNT>				; Mount volume

	FDT_ACT	ACP_STD$READBLK,<-		; Read functions
		READLBLK,-			; Read logical block forward
		READPBLK,-			; Read physical block forward
;	 	READTRACKD,-			; Read track descriptor
;		READHEAD,-			; Read track data and headers
		READVBLK>			; Read virtual block

	FDT_ACT	ACP_STD$WRITEBLK,<-		; Write functions
		WRITECHECK>			; Write check

	FDT_ACT	DK_SHAD_WRITE,<-		;U A; Check write to shadow set mbr
		WRITELBLK,-			; Write LOGICAL Block
		WRITEPBLK,-			; Write Physical Block
		WRITEVBLK>			; Write VIRTUAL Block

	FDT_ACT	EXE_STD$SENSEMODE,<-		; Sense functions
		SENSECHAR,-			; Sense characteristics
		SENSEMODE>			; Sense mode

	FDT_ACT	EXE_STD$SETMODE,<-		; FDT set mode routine
		SETCHAR,-			; for set chars. and
		SETMODE>			; set mode.

	FDT_ACT EXE_STD$LCLDSKVALID,<-		; Local disk valid functions
		UNLOAD,-			; Unload volume
		AVAILABLE,-			; Unit available
		PACKACK>			; Pack acknowledge

	FDT_ACT	EXE_STD$ONEPARM,<-		; Single parameter functions
		FORMAT>				; Format floppy diskette

	FDT_ACT	EXE_STD$ZEROPARM,<-		; Zero parameter functions
		NOP>				; NOP

	FDT_ACT	DK_AUDIO,<-			;U A; CD-ROM Audio FDT entry (55)
		AUDIO>				; 

	FDT_ACT	DK_DSE,<-			;U A; Data Security Erase FDT Routine
		DSE>				;

	FDT_ACT	DK_DIAGNOSE,<-			;U A; Special pass-through function
		DIAGNOSE>			; 

	FDT_ACT	DK_CRESHAD,<-			;U A; Create a shadow set virtual unit
		CRESHAD>

	FDT_ACT	DK_REMSHAD,<-			;U A; Remove a shadow set member
		REMSHAD>

	.PAGE
	.SBTTL	DKDRIVER Data
	DRIVER_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

DATACHECK_SPTE::.LONG	0		; SVA of SPTEs to double map user buffer
DATACHECK_SVA:: .LONG	0		; SVA mapped by this set of SPTEs
TRACE_BUFFER::	.LONG	0		; Trace buffer address
MCN_DEF_STATUS:	.LONG	0		; Status buffer if no SCSI cmd issued.
DUMMY_MODE_SENSE_CDB:			; For error logging purposes.
		.LONG	^XFF		; Dummy status
		.LONG	6		; Command length
		.BYTE	^X1A,0,0,0,100,0 ; Command bytes
		.BLKB	2		; Pad for alignment 
;
; DKDriver DEBUG flags
;
	.WEAK	DK$C_DEBUG_FLAGS
DK$DEBUG_FLAGS::.LONG	0 \ DK$C_DEBUG_FLAGS	; DK Driver debug flags
	$VIELD	DK, 0, < -
		< CTRL_INIT,,M>, -		; (%X0001) Controller Initialization
		< UNIT_INIT,,M>, -		; (%X0002) Unit Initialization
		< PORT_CALLBACK,,M>, -		; (%X0004) Port State Callback
		< MOUNT_VER,,M>, -		; (%X0008) Mount Verification
		< DK_STARTIO,,M>, -		; (%X0010) Start I/O Routine for KP Drivers
		< DK_KP_STARTIO,,M>, -		; (%X0020) DK Start Qio Entry Point
		< ILLEGAL_IO,,M>, -		; (%X0040) Illegal I/O
		< IO_NOP,,M>, - 		; (%X0080) IO_NOP
		< DK_CANCEL,,M>, -		; (%X0100) DK_CANCEL
		< IO_PACKACK,,M>, -		; (%X0200) IO_PACKACK
		< IO_READPBLK,,M>, -		; (%X0400) IO_READPBLK
		< IO_DATACHECK,,M>, -		; (%X0800) IO_DATACHECK
		< RQST_SENSE,,M>, -		; (%X1000) Request Sense
		>

	.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. Once the device type is determined, various information
; about the device such as disconnect and synchronous flags and timeout vaules
; is saved in the UCB.
;-

SCSI_DEVICE_TABLE::

SCSI_DEV_TYPES <-
;
; ID string  Dev type	 Media ID Min Rev Disc Synch Phas tmo Disc tmo
; ---------  --------	 -------- ------- ---- ----- -------- ----------
;
  <<RZ22>,    RZ22,	  ,	  <0615>, YES,	YES, DEFAULT, DEFAULT>,-
  <<RZ23>,    RZ23,	  ,	  <0615>, YES,	YES, DEFAULT, DEFAULT>,-
  <<RZ23L>,   RZ23L,	  RZL23,  <    >, YES,	YES, DEFAULT, DEFAULT>,-
  <<RZ24>,    RZ24,	  ,	  <    >, YES,	YES, DEFAULT, DEFAULT>,-
  <<RZ25>,    RZ25,	  ,	  <    >, YES,	YES, DEFAULT, DEFAULT>,-
  <<RZ31>,    RZ31,	  ,	  <    >, YES,	YES, DEFAULT, DEFAULT>,-
  <<RZ55>,    RZ55,	  ,	  <0700>, YES,	YES, DEFAULT, DEFAULT>,-
  <<RZ56>,    RZ56,	  ,	  <    >, YES,	YES, DEFAULT, DEFAULT>,-
  <<RZ57>,    RZ57,	  ,	  <    >, YES,	YES, DEFAULT, DEFAULT>,-
  <<RZ57I>,   RZ57I,	  RZI57,  <    >, YES,	YES, DEFAULT, DEFAULT>,-
  <<RZ58>,    RZ58,	  ,	  <    >, YES,	YES, DEFAULT, DEFAULT>,-
  <<RRD40>,   RRD40S,	  RRD40,  <250D>, YES,	 NO, DEFAULT, RRD40_DISC_TMO>,-
  <<RRD42>,   RRD42,	  ,	  <    >, YES,	 NO, DEFAULT, RRD42_DISC_TMO>>

SCSI_DEV_TYPES <-
  <<RZ72>,    RZ72,	  ,	  <    >, YES,	YES, DEFAULT, DEFAULT>,-
  <<RZ73>,    RZ73,	  ,	  <    >, YES,	YES, DEFAULT, DEFAULT>,-
  <<RZ35>,    RZ35,	  ,	  <    >, YES,	YES, DEFAULT, DEFAULT>,-
  <<RWZ01>,   RWZ01,	  ,	  <    >, YES,	YES, DEFAULT, DEFAULT>,-
  <<RZ24L>,   RZ24L,	  RZL24,  <    >, YES,	YES, DEFAULT, DEFAULT>,-
  <<RZ25L>,   RZ25L,	  RZL25,  <    >, YES,	YES, DEFAULT, DEFAULT>,-
  <<RZ26L>,   RZ26L,	  RZL26,  <    >, YES,	YES, DEFAULT, DEFAULT>,-
  <<RZ55L>,   RZ55L,	  RZL55,  <    >, YES,	YES, DEFAULT, DEFAULT>,-
  <<RZ56L>,   RZ56L,	  RZL56,  <    >, YES,	YES, DEFAULT, DEFAULT>,-
  <<RZ57L>,   RZ57L,	  RZL57,  <    >, YES,	YES, DEFAULT, DEFAULT>,-
  <<RZ26>,    RZ26,	  ,	  <    >, YES,	YES, DEFAULT, DEFAULT>,-
  <<RZ36>,    RZ36,	  ,	  <    >, YES,	YES, DEFAULT, DEFAULT>,-
  <<RZ74>,    RZ74,	  ,	  <    >, YES,	YES, DEFAULT, DEFAULT>>
	.IF DEFINED RZ74_CACHE
	.print ; Add comment to previous line
<<RZ74>,    RZ74,	  ,	  <436A>, YES,	YES, DEFAULT, DEFAULT>>
	.ENDC

SCSI_DEV_TYPES <-
  <<RZ27>,    RZ27,	  ,	  <    >, YES,	YES, DEFAULT, DEFAULT>,-
  <<RZ37>,    RZ37,	  ,	  <    >, YES,	YES, DEFAULT, DEFAULT>,-
  <<RZ38>,    RZ38,	  ,	  <    >, YES,	YES, DEFAULT, DEFAULT>,-
  <<RZ75>,    RZ75,	  ,	  <    >, YES,	YES, DEFAULT, DEFAULT>,-
  <<RZ59>,    RZ59,	  ,	  <    >, YES,	YES, DEFAULT, DEFAULT>,-
  <<RZ34L>,   RZ34L,	  RZL34,  <    >, YES,	YES, DEFAULT, DEFAULT>,-
  <<RZ35L>,   RZ35L,	  RZL35,  <    >, YES,	YES, DEFAULT, DEFAULT>,-
  <<RZ36L>,   RZ36L,	  RZL36,  <    >, YES,	YES, DEFAULT, DEFAULT>,-
  <<RRD43>,   RRD43,	  ,	  <    >, YES,	 NO, DEFAULT, RRD42_DISC_TMO>,-
  <<RRD44>,   RRD44,	  ,	  <    >, YES,	 NO, DEFAULT, RRD42_DISC_TMO>>

SCSI_DEV_TYPES <-
  <<RZ13>,    RZ13,	  ,	  <    >, YES,	YES, DEFAULT, DEFAULT>,-
  <<RZ14>,    RZ14,	  ,	  <    >, YES,	YES, DEFAULT, DEFAULT>,-
  <<RZ15>,    RZ15,	  ,	  <    >, YES,	YES, DEFAULT, DEFAULT>,-
  <<RZ16>,    RZ16,	  ,	  <    >, YES,	YES, DEFAULT, DEFAULT>,-
  <<RZ17>,    RZ17,	  ,	  <    >, YES,	YES, DEFAULT, DEFAULT>,-
  <<RZ18>,    RZ18,	  ,	  <    >, YES,	YES, DEFAULT, DEFAULT>,-
  <<EZ51>,    EZ51,	  ,	  <    >, YES,	YES, DEFAULT, DEFAULT>,-
  <<EZ52>,    EZ52,	  ,	  <    >, YES,	YES, DEFAULT, DEFAULT>,-
  <<EZ53>,    EZ53,	  ,	  <    >, YES,	YES, DEFAULT, DEFAULT>,-
  <<EZ54>,    EZ54,	  ,	  <    >, YES,	YES, DEFAULT, DEFAULT>,-
  <<EZ58>,    EZ58,	  ,	  <    >, YES,	YES, DEFAULT, DEFAULT>>

SCSI_DEV_TYPES <-
  <<HSZ10>,   HSZ10,	  ,	  <    >, YES,	YES, DEFAULT, DEFAULT>,-
  <<RZ28>,    RZ28,	  ,	  <    >, YES,	YES, DEFAULT, DEFAULT>,-
  <<RZ29>,    RZ29,	  ,	  <    >, YES,	YES, DEFAULT, DEFAULT>,-
  <<Cp350>,   RZ22,	  ,	  <    >, YES,	YES, DEFAULT, DEFAULT>,-
  <<Cp3100-1> RZ23,	  ,	  <    >, YES,	YES, DEFAULT, DEFAULT>,-
  <<1578-15>, RZ55,	  ,	  <    >,  NO,	YES, DEFAULT, DEFAULT>,-
  <<CM 210>,  RRD40S,	  RRD40,  <    >, YES,	 NO, DEFAULT, RRD40_DISC_TMO>,-
  <<RX23>,    RX23S,	  RX23,	  <    >, YES,	 NO, RX_PHS_TMO, RX_DISC_TMO>,-
  <<RX33>,    RX33S,	  RX33,	  <    >, YES,	 NO, RX_PHS_TMO, RX_DISC_TMO>,-
  <<RX26>,    RX26,	  ,	  <    >, YES,	 NO, RX_PHS_TMO, RX_DISC_TMO>,-
  <<XT-4380S>, RZ55,	  ,	  <B2  >, YES,	 NO, DEFAULT, DEFAULT>,-
  <<GENERIC>, GENERIC_DK, ,	  <    >, YES,	YES, DEFAULT, DEFAULT>>

SCSI_DEV_TYPES <-
  <<RZ26B>,   RZ26B,	  RZB26,  <    >, YES,	YES, DEFAULT, DEFAULT>,-
  <<RZ27B>,   RZ27B,	  RZB27,  <    >, YES,	YES, DEFAULT, DEFAULT>,-
  <<RZ28B>,   RZ28B,	  RZB28,  <    >, YES,	YES, DEFAULT, DEFAULT>,-
  <<RZ29B>,   RZ29B,	  RZB29,  <    >, YES,	YES, DEFAULT, DEFAULT>,-
  <<RZ73B>,   RZ73B,	  RZB73,  <    >, YES,	YES, DEFAULT, DEFAULT>,-
  <<RZ74B>,   RZ74B,	  RZB74,  <    >, YES,	YES, DEFAULT, DEFAULT>,-
  <<RZ75B>,   RZ75B,	  RZB75,  <    >, YES,	YES, DEFAULT, DEFAULT>,-
  <<RZ27L>,   RZ27L,	  RZL27,  <    >, YES,	YES, DEFAULT, DEFAULT>,-
  <<RWZ21>,   RWZ21,	  ,	  <    >, YES,	YES, DEFAULT, DEFAULT>,-
  <<HSZ20>,   HSZ20,	  ,	  <    >, YES,	YES, DEFAULT, DEFAULT>,-
  <<HSZ40>,   HSZ40,	  ,	  <    >, YES,	YES, DEFAULT, DEFAULT>>

SCSI_DEV_TYPES <-
  <<HSX00>,   HSX00,	  ,	  <    >, YES,	YES, DEFAULT, DEFAULT>,-
  <<HSX01>,   HSX01,	  ,	  <    >, YES,	YES, DEFAULT, DEFAULT>,-
  <<HSZ15>,   HSZ15,	  ,	  <    >, YES,	YES, DEFAULT, DEFAULT>,-
  <<RZ26M>,   RZ26M,	  RZM26,  <    >, YES,	YES, DEFAULT, DEFAULT>,-
  <<RW504>,   RW504,	  ,	  <    >, YES,	YES, DEFAULT, DEFAULT>,-
  <<RW510>,   RW510,	  ,	  <    >, YES,	YES, DEFAULT, DEFAULT>,-
  <<RW514>,   RW514,	  ,	  <    >, YES,	YES, DEFAULT, DEFAULT>,-
  <<RW516>,   RW516,	  ,	  <    >, YES,	YES, DEFAULT, DEFAULT>>

SCSI_DEV_TYPES <-
  <<RWZ52>,   RWZ52,	  ,	  <    >, YES,	YES, DEFAULT, DEFAULT>,-
  <<RWZ53>,   RWZ53,	  ,	  <    >, YES,	YES, DEFAULT, DEFAULT>,-
  <<RWZ54>,   RWZ54,	  ,	  <    >, YES,	YES, DEFAULT, DEFAULT>,-
  <<RWZ31>,   RWZ31,	  ,	  <    >, YES,	YES, DEFAULT, DEFAULT>>

SCSI_DEV_TYPES <-
  <<EZ31>,    EZ31,	  ,	  <    >, YES,	YES, DEFAULT, DEFAULT>,-
  <<EZ32>,    EZ32,	  ,	  <    >, YES,	YES, DEFAULT, DEFAULT>,-
  <<EZ33>,    EZ33,	  ,	  <    >, YES,	YES, DEFAULT, DEFAULT>,-
  <<EZ34>,    EZ34,	  ,	  <    >, YES,	YES, DEFAULT, DEFAULT>,-
  <<EZ35>,    EZ35,	  ,	  <    >, YES,	YES, DEFAULT, DEFAULT>,-
  <<EZ31L>,   EZ31L,	  EZL31,  <    >, YES,	YES, DEFAULT, DEFAULT>,-
  <<EZ32L>,   EZ32L,	  EZL32,  <    >, YES,	YES, DEFAULT, DEFAULT>,-
  <<EZ33L>,   EZ33L,	  EZL33,  <    >, YES,	YES, DEFAULT, DEFAULT>>

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

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

	SCSI_CMD -
		NAME = REZERO_UNIT,-
		CMD_BYTES = <1, 0, 0, 0, 0, 0>

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

	SCSI_CMD -
		NAME = FORMAT_UNIT,-
		CMD_BYTES = <4, 0, 0, 0, 0, 0>,-
		DMA_LEN = 512,-
		DMA_DIR = WRITE

	SCSI_CMD -
		NAME = REASSIGN_BLOCKS,-
		CMD_BYTES = <7, 0, 0, 0, 0, 0>,-
		DMA_LEN = 8,-
		DMA_DIR = WRITE

	SCSI_CMD -
		NAME = READ,-
		CMD_BYTES = <8, 0, 0, 0, 0, 0>,-
		DMA_LEN = -1
		CMD_READ$R_LBA = 1		; 21-bit Logical Block Address
		CMD_READ$B_XFER_COUNT = 4	; 8-bit Transfer count

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

	SCSI_CMD -
		NAME = EXTND_READ,-
		CMD_BYTES = <<^X28>, 0, 0, 0, 0, 0, 0, 0, 0, 0>,-
		DMA_LEN = -1
		CMD_EXTND_READ$R_LBA = 2		; 32-bit Logical Block Address
		CMD_EXTND_READ$W_XFER_COUNT = 7		; 16-bit Transfer count

	SCSI_CMD -
		NAME = EXTND_WRITE,-
		CMD_BYTES = <<^X2A>, 0, 0, 0, 0, 0, 0, 0, 0, 0>,-
		DMA_LEN = -1

	SCSI_CMD -
		NAME = SEEK,-
		CMD_BYTES = <11, 0, 0, 0, 0, 0>

	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, 255, 0>,-
		DMA_LEN = 255,-
		DMA_DIR = WRITE
		CMD_MODE_SELECT$L_LENGTH = 4	; Parameter List Length
		CMD_MODE_SELECT$R_ALLOC_LENGTH = 4 ; Mode Select Buffer Size

		; The audio functions manipulate the data returned from MODE
		; SENSE to alter the audio volume level, turning it around and
		; using it as MODE SELECT data. The following symbol just makes
		; referencing the volume fields a bit cleaner; note that it assumes
		; a size for the block descriptor (SCSI$MPBD$S_MODE_PARAMETER).

		SELECT_VOLUME = SCSI$MPH6$S_MODE_PARAM_HDR_6 + -	
				SCSI$MPBD$S_MODE_PARAMETER   + -
				SCSI$ACP$W_CHANNEL_VOLUME

	SCSI_CMD -
		NAME = MODE_SELECT_10,-
		CMD_BYTES = <85, <^X10>, 0,0,0,0,0, <^X04>,0, 0>,-
		DMA_LEN = 1024,-
		DMA_DIR = WRITE

	SCSI_CMD -
		NAME = RESERVE,-
		CMD_BYTES = <22, 0, 0, 0, 0, 0>

	SCSI_CMD -
		NAME = RELEASE,-
		CMD_BYTES = <23, 0, 0, 0, 0, 0>

	SCSI_CMD -
		NAME = COPY,-
		CMD_BYTES = <24, 0, 0, 0, 0, 0>

	SCSI_CMD -
		NAME = MODE_SENSE,-
		CMD_BYTES = <26, 0, <^X3F>, 0, 255, 0>,-
		DMA_LEN = 255,-
		DMA_DIR = READ
		CMD_MODE_SENSE$R_PAGE_CODE = 2	; Mode Sense Page Code
		CMD_MODE_SENSE$R_ALLOC_LENGTH = 4 ; Mode Sense Buffer Size

		; See notes above for MODE_SELECT command
 
		SENSE_VOLUME = SELECT_VOLUME

; note: cmd_bytes below should be 0,255 instead of <^X04>,<^X00> to avoid
; a (currently benign on alpha) possible buffer overwrite. Tom Goodwin will
; fix this later.
;was	CMD_BYTES = <90, 0, <^X3F>, 0,0,0,0, <^X04>,<^X00>,0>,-
	SCSI_CMD -
		NAME = MODE_SENSE_10,-
		CMD_BYTES = <90, 0, <^X3F>, 0,0,0,0, <^X0>,<^XFF>,0>,-
		DMA_LEN = 255,-
		DMA_DIR = READ

	SCSI_CMD -
		NAME = START_UNIT,-
		CMD_BYTES = <27, 1, 0, 0, 1, 0>

	SCSI_CMD -
		NAME = STOP_UNIT,-
		CMD_BYTES = <27, 1, 0, 0, 0, 0>

	SCSI_CMD -
		NAME = RECEIVE_DIAG,-
		CMD_BYTES = <28, 0, 0, 0, 0, 0>,-
		DMA_LEN = 512,-
		DMA_DIR = READ

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

	SCSI_CMD -
		NAME = MEDIA_REMOVAL,-
		CMD_BYTES = <30, 0, 0, 0, 0, 0>

	SCSI_CMD -
		NAME = READ_CAPACITY,-
		CMD_BYTES = <37, 0, 0, 0, 0, 0, 0, 0, 0, 0>,-
		DMA_LEN = 8,-
		DMA_DIR = READ

	SCSI_CMD -
		NAME = READ_LONG,-
		CMD_BYTES = <<^X3E>, 0, 0, 0, 0, 0, 0, 0, 0, 0>,-
		DMA_LEN = READ_LONG_DATA_LEN,-
		DMA_DIR = READ
		CMD_READ_LONG$L_LBA = 2			; Logical Block Address
		CMD_READ_LONG$W_XFER_LENGTH = 7		; Byte Transfer Length

	SCSI_CMD -
		NAME = WRITE_LONG,-
		CMD_BYTES = <<^X3F>, 0, 0, 0, 0, 0, 0, 0, 0, 0>,-
		DMA_LEN = WRITE_LONG_DATA_LEN,-
		DMA_DIR = WRITE

	.PAGE
	.SBTTL	SCSI AUDIO command definition tables

;+
; Beginning of CD-ROM specific SCSI Audio commands
;-
	SCSI_CMD -
		NAME = PAUSE,-
		CMD_BYTES = <75, 0, 0, 0, 0, 0, 0, 0, 0, 0>

	SCSI_CMD -
		NAME = RESUME,-
		CMD_BYTES = <75, 0, 0, 0, 0, 0, 0, 0, 1, 0>

	SCSI_CMD -
		NAME = PLAY_AUDIO10,-
		CMD_BYTES = <69, 0, 0, 0, 0, 0, 0, 0, 0, 0>
		CMD_PLAY_AUDIO10$B_LBA_0 = 5	; Starting Logical Block Address
		CMD_PLAY_AUDIO10$B_LBA_1 = 4
		CMD_PLAY_AUDIO10$B_LBA_2 = 3
		CMD_PLAY_AUDIO10$B_LBA_3 = 2
		CMD_PLAY_AUDIO10$B_XFR_0 = 8	; Transfer Length
		CMD_PLAY_AUDIO10$B_XFR_1 = 7
		

	SCSI_CMD -
		NAME = PLAY_TRACK,-
		CMD_BYTES = <72, 0, 0, 0, 3, 1, 0, 11, 1, 0>
		CMD_PLAY_TRACK$B_STARTING_TRACK = 4	; Starting Track
		CMD_PLAY_TRACK$B_STARTING_INDEX = 5	; Starting Track
		CMD_PLAY_TRACK$B_END_TRACK = 7		; Ending Track
		CMD_PLAY_TRACK$B_END_INDEX = 8		; Ending Track


	SCSI_CMD -
		NAME = REMOVAL,-
		CMD_BYTES = <30, 0, 0, 0, 0, 0, 0>
		CMD_REMOVAL$R_PREVENT = 4		; <0,1> offset to bit to prevent removal

	SCSI_CMD -
		NAME = PLAY_AUDIO_MSF,-
		CMD_BYTES = <71, 0, 0, 0, 0, 0, 0, 0, 0, 0>
		CMD_PLAY_AUDIO$B_START_FRM = 5	; Starting Frame
		CMD_PLAY_AUDIO$B_START_SEC = 4	; Starting Seconds
		CMD_PLAY_AUDIO$B_START_MIN = 3	; Starting Minutes
		CMD_PLAY_AUDIO$B_END_FRM = 8	; Ending Frame
		CMD_PLAY_AUDIO$B_END_SEC = 7	; Ending Seconds
		CMD_PLAY_AUDIO$B_END_MIN = 6	; Ending Minutes

	SCSI_CMD -
		NAME = CD_MODE_SENSE,-
		CMD_BYTES = <26, 0, 0, 0, 0, 0>,-
		DMA_LEN	= -1,-
		DMA_DIR = READ

	SCSI_CMD -
		NAME = CD_MODE_SELECT,-
		CMD_BYTES = <21 , <^X10>, 0, 0, 0, 0>,-
		DMA_LEN = -1,-
		DMA_DIR = WRITE
		CMD_CD_MODE_SELECT$L_LENGTH = 4	; Parameter List Length

	SCSI_CMD -
		NAME = CD_READ_SUB,-
		CMD_BYTES = <66, 0, 64, 1, 0, 0, 0, 0, 48, 0>,-
		DMA_LEN = 48,-
		DMA_DIR = READ
		CMD_CD_READ_SUB$R_FLAGS = 1
		CMD_CD_READ_SUB$B_SUB_DATA_FMT=3

	SCSI_CMD -
		NAME = CD_READ_TOC,-
		CMD_BYTES = <67, 0, 0, 0, 0, 0, 0, 3, 22, 0>,-
		DMA_LEN = 804,-
		DMA_DIR = READ
		CMD_CD_READ_TOC$R_FLAGS = 1
		CMD_CD_READ_TOC$B_START_TRACK = 6

	.=<<.+7>& ^C7>			; Align data structure


	.PAGE
	.SBTTL	Mode Sense macros 
;+
;	The 10-byte Mode Sense command, while important because of the amount
;	of data which newer devices are returning, has been problematic because
;	some devices support it properly, some lie about it (they don't fail
;	the command but they return too much data, too little data or bad data),
;	and some do not support it at all but fail it in unusual ways.
;
;	Because we can't count on devices to tell us whether or not they
;	support 10-byte Mode Sense commands intelligently (meaning in 
;	accordance with our interpretation of the SCSI specification), we'll
;	start off in DO_MODE_PAGE trying 10-byte commands, and will back off to
;	6-byte commands in the case of just about any Mode Sense failure
;
;	The SCDRP's TENBYTE bit tells DO_MODE_PAGE whether to do 10-byte or 
;	6-byte commands, and in the event of a successful command it tells us
;	which command it used; however, in the event of a failing command the
;	bit returns no useful information to us because it's cleared when
;	DO_MODE_PAGE decides to back off to a 6-byte Mode Sense command but
;	not reasserted if that fails. 
;
;	At the completion of the first successful DO_MODE_PAGE, we'll load the
;	state of the SCDRP's TENBYTE bit into the UCB; the assumption is that
;	if one 10-byte Mode Sense command succeeds, all will (should).
;
;	Whenever a DO_MODE_PAGE fails we'll refresh the SCDRP bit from the UCB,
;	just in case it backed off to the 6-byte command and that too failed;
;	this keeps us consistenly trying 10-byte commands first if the first
;	one, for a page which all devices are expected to support, completed
;	successfully.
;
;	All of this is to protect us against the day when some intelligent device
;	decides to return more than 256 bytes of Mode Sense data for a single
;	page, in which case a 6-byte command could not get it all.
;-

;	Propagate the state of the UCB's TENBYTE bit to the SCDRP

	.MACRO	LOAD_TENBYTE	ucb=R3,scdrp=R5,?L1,?L2
 	BBC	#UCB$V_TENBYTE,UCB$L_DK_FLAGS(ucb),L1
	BBCS	#SCDRP$V_FLAG_TENBYTE,SCDRP$L_SCSI_FLAGS(scdrp),L2
	BRW	L2 ; Oh well, we guessed wrong, it was set
L1:	BBCC	#SCDRP$V_FLAG_TENBYTE,SCDRP$L_SCSI_FLAGS(scdrp),L2
L2:	.ENDM	LOAD_TENBYTE

;	Propagate the state of the SCDRP's TENBYTE bit to the UCB

	.MACRO	STORE_TENBYTE	ucb=R3,scdrp=R5,?L1,?L2
	BBC	#SCDRP$V_FLAG_TENBYTE,SCDRP$L_SCSI_FLAGS(scdrp),L1
	BBSS	#UCB$V_TENBYTE,UCB$L_DK_FLAGS(ucb),L2
	BRW	L2 ; Oh well, we guessed wrong, it was clear
L1:	BBCC	#UCB$V_TENBYTE,UCB$L_DK_FLAGS(ucb),L2
L2:	.ENDM	STORE_TENBYTE

;
; Descriptors for mode page fields, used by MODE SENSE operations.
;
PREF  = MODE_DESC$M_PREFERRED	
REQ   = MODE_DESC$M_REQUIRED	
MISS  = MODE_DESC$M_IFMISSING	
MISM  = MODE_DESC$M_IFMISMATCH	
DPTR  = MODE_DESC$M_DESPTR	
APTR  = MODE_DESC$M_ACTPTR	
LAST  = MODE_DESC$M_LAST	
MBOFF = MODE_DESC$C_MEDTYP_BOFF
DBOFF = MODE_DESC$C_DEVPAR_BOFF
DNBOFF= MODE_DESC$C_DENS_BOFF
BBOFF = MODE_DESC$C_BLKLEN_BOFF

; This macro will presume that a symbol 'page'_sumflg must be zeroed
; before the call, and it will accumulate, in an assembly time symbol,
; the OR of all flags bits seen in the page. In this way the entire
; page can be tested at assembly time for required bits by testing this
; "summary variable", since if a bit appears in the calls to the macro
; for any page, it will appear in the summary. This means that code
; which should occur only where there are no required bits in the page
; can be conditionally included at compiler (assembly) time, not having to
; await a runtime check. The symbols are defined outside the macro and
; in a couple of cases (where 'page' is the same for two page calls
; used in different circumstances) is saved in a separate assembly time
; symbol to reflect the tests on the one page needed.
;
; Note that code of form
;	.if eq,<err_sumflg&req>
;	...
;	.endc
;
; will be assembled/compiled in ONLY if the "req" (required) bit is set
; somewhere in the err_page part of the table. Similarly this works the
; same for other table parts.
;
; In this way, table definitions can be altered freely and code which must
; be included or omitted if required fields are present will function right
; with no further alterations deep in dkdriver. (Obviously, a construct like
; ".if ne,<err_sumflg & req>" can be used also to condition in code where
; required fields ARE present.)
;
	.MACRO	MD_DSC PAGE,NAME,BOFF,BIT_OFFSET,BIT_SIZE,DESIRED,FLAGS
		'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
		'PAGE'_SUMFLG = 'PAGE'_SUMFLG ! 'FLAGS'
	.ENDM	MD_DSC


	.ALIGN LONG
DESCRIPTOR_BASE = .

;-------------------------------------------------------------------------------
ERR_PAGE_DESC = . - DESCRIPTOR_BASE
ERR_SUMFLG = 0
;          Page name       Bitname  Boff  Bit  Size  Val    Flags   
;          ---------	   -------  ----  ---  ----  ---    -----
MD_DSC	         ERR,      BLKLEN,  BBOFF, 0,   24, <^X200>,<PREF!MISS!MISM!LAST>
SIZEOF_ERR = . - <DESCRIPTOR_BASE + ERR_PAGE_DESC>

;-------------------------------------------------------------------------------
; Cluster page reflects state wanted for cluster shared disks on shared busses
SCSI2_CLU_ERR_PAGE_DESC = . - DESCRIPTOR_BASE    				; SCSI2 Tagged Queuing devices, R/W err rec page
SCSI2_CLU_ERR_SUMFLG = 0							; Same values required for SCSI clusters 
;          Page name       Bitname  Boff  Bit  Size  Val    Flags   
;          ---------	   -------  ----  ---  ----  ---    -----
MD_DSC	  SCSI2_CLU_ERR,    AWRE,     2,   7,    1,    1,   <REQ!MISS!MISM>
MD_DSC	  SCSI2_CLU_ERR,    ARRE,     2,   6,    1,    1,   <REQ!MISS!MISM>
MD_DSC	  SCSI2_CLU_ERR,      TB,     2,   5,    1,    1,   <REQ!MISS!MISM>
MD_DSC	  SCSI2_CLU_ERR,      RC,     2,   4,    1,    0,   <REQ!MISS!MISM>
MD_DSC	  SCSI2_CLU_ERR,     PER,     2,   2,    1,    1,   <REQ!MISS!MISM>
MD_DSC	  SCSI2_CLU_ERR,     DTE,     2,   1,    1,    1,   <PREF>
MD_DSC	  SCSI2_CLU_ERR,  MEDTYP, MBOFF,   0,    8,    0,   0
MD_DSC	  SCSI2_CLU_ERR,  DEVSPC, DBOFF,   0,    8,    0,   <LAST>
SIZEOF_SCSI2_CLU = . - <DESCRIPTOR_BASE + SCSI2_CLU_ERR_PAGE_DESC>
;-------------------------------------------------------------------------------
SCSI2_TCQ_ERR_PAGE_DESC = . - DESCRIPTOR_BASE    				; SCSI2 Tagged Queuing devices, R/W err rec page
SCSI2_TCQ_ERR_SUMFLG=0								; Same values required for SCSI clusters 
;          Page name       Bitname  Boff  Bit  Size  Val    Flags   
;          ---------	   -------  ----  ---  ----  ---    -----
MD_DSC	  SCSI2_TCQ_ERR,    AWRE,     2,   7,    1,    1,   <PREF>
MD_DSC	  SCSI2_TCQ_ERR,    ARRE,     2,   6,    1,    1,   <PREF>
MD_DSC	  SCSI2_TCQ_ERR,      TB,     2,   5,    1,    1,   <PREF>
MD_DSC	  SCSI2_TCQ_ERR,      RC,     2,   4,    1,    0,   <REQ!MISS!MISM>
MD_DSC	  SCSI2_TCQ_ERR,     PER,     2,   2,    1,    1,   <PREF>
MD_DSC	  SCSI2_TCQ_ERR,     DTE,     2,   1,    1,    1,   <PREF>
MD_DSC	  SCSI2_TCQ_ERR,  MEDTYP, MBOFF,   0,    8,    0,   0
MD_DSC	  SCSI2_TCQ_ERR,  DEVSPC, DBOFF,   0,    8,    0,   <LAST>
SIZEOF_SCSI2_TCQ = . - <DESCRIPTOR_BASE + SCSI2_TCQ_ERR_PAGE_DESC>

;-------------------------------------------------------------------------------
SCSI2_NOTCQ_ERR_PAGE_DESC = . - DESCRIPTOR_BASE             			; SCSI2 non-Tagged Queuing devices, R/W err rec pg
SCSI2_NOTCQ_ERR_SUMFLG = 0							; If possible, should look like SCSI1 
;          Page name       Bitname  Boff  Bit  Size  Val    Flags   
;          ---------	   -------  ----  ---  ----  ---    -----
MD_DSC	  SCSI2_NOTCQ_ERR,  AWRE,     2,   7,    1,    0,   <PREF>
MD_DSC	  SCSI2_NOTCQ_ERR,  ARRE,     2,   6,    1,    0,   <PREF>
MD_DSC	  SCSI2_NOTCQ_ERR,    TB,     2,   5,    1,    1,   <PREF>
MD_DSC	  SCSI2_NOTCQ_ERR,    RC,     2,   4,    1,    0,   <REQ!MISS!MISM>
MD_DSC	  SCSI2_NOTCQ_ERR,   PER,     2,   2,    1,    1,   <PREF>
MD_DSC	  SCSI2_NOTCQ_ERR,   DTE,     2,   1,    1,    1,   <PREF>
MD_DSC	  SCSI2_NOTCQ_ERR,MEDTYP, MBOFF,   0,    8,    0,   0
MD_DSC	  SCSI2_NOTCQ_ERR,DEVSPC, DBOFF,   0,    8,    0,   <LAST>
SIZEOF_SCSI2_NOTCQ = . - <DESCRIPTOR_BASE + SCSI2_NOTCQ_ERR_PAGE_DESC>

;-------------------------------------------------------------------------------
SCSI1_ERR_PAGE_DESC = . - DESCRIPTOR_BASE                    			; SCSI1 devices, R/W error recovery page
;
; Disallow AWRE/ARRE for SCSI1 since most SCSI 1 implementations got it
; wrong and rerecorded blocks even though the original data was lost. This
; silent data loss cannot be allowed.
;
SCSI1_ERR_SUMFLG=0
;          Page name       Bitname  Boff  Bit  Size  Val    Flags   
;          ---------	   -------  ----  ---  ----  ---    -----
MD_DSC	  SCSI1_ERR,        AWRE,     2,   7,    1,    0,   <REQ!MISS!MISM>
MD_DSC	  SCSI1_ERR,        ARRE,     2,   6,    1,    0,   <REQ!MISS!MISM>
MD_DSC	  SCSI1_ERR,          TB,     2,   5,    1,    1,   <PREF>
MD_DSC	  SCSI1_ERR,          RC,     2,   4,    1,    0,   <REQ!MISS!MISM>
MD_DSC	  SCSI1_ERR,         PER,     2,   2,    1,    1,   <PREF>
MD_DSC	  SCSI1_ERR,         DTE,     2,   1,    1,    1,   <PREF>
MD_DSC	  SCSI1_ERR,      MEDTYP, MBOFF,   0,    8,    0,   0
MD_DSC	  SCSI1_ERR,      DEVSPC, DBOFF,   0,    8,    0,   <LAST>
SIZEOF_SCSI1 = . - <DESCRIPTOR_BASE + SCSI1_ERR_PAGE_DESC>

;-------------------------------------------------------------------------------
NON512_ERR_PAGE_DESC = . - DESCRIPTOR_BASE                      ; Devices with blk size not = 512, R/W error recovery page
NON512_ERR_SUMFLG=0						; Don't care about AWRE/ARRE for these devices
;          Page name       Bitname  Boff  Bit  Size  Val    Flags   
;          ---------	   -------  ----  ---  ----  ---    -----
MD_DSC	  NON512_ERR,         TB,     2,   5,    1,    1,   <PREF>
MD_DSC	  NON512_ERR,         RC,     2,   4,    1,    0,   <REQ!MISS!MISM>
MD_DSC	  NON512_ERR,        PER,     2,   2,    1,    1,   <PREF>
MD_DSC	  NON512_ERR,        DTE,     2,   1,    1,    1,   <PREF>
MD_DSC	  NON512_ERR,     MEDTYP, MBOFF,   0,    8,    0,   0
MD_DSC	  NON512_ERR,     DEVSPC, DBOFF,   0,    8,    0,   <LAST>
SIZEOF_NON512 = . - <DESCRIPTOR_BASE + NON512_ERR_PAGE_DESC>

;-------------------------------------------------------------------------------
CACHING_PAGE_DESC = . - DESCRIPTOR_BASE
CACHE_SUMFLG=0
;          Page name       Bitname  Boff  Bit  Size  Val    Flags   
;          ---------	   -------  ----  ---  ----  ---    -----
MD_DSC	       CACHE,         WCE,    2,   2,    1,    0,   <PREF!LAST>
SIZEOF_CACHING = . - <DESCRIPTOR_BASE + CACHING_PAGE_DESC>

	.IF DEFINED RZ74_CACHE
;-------------------------------------------------------------------------------
NOCACHING_PAGE_DESC = . - DESCRIPTOR_BASE

;          Page name       Bitname  Boff  Bit  Size  Val    Flags   
;          ---------	   -------  ----  ---  ----  ---    -----
MD_DSC	       CACHE,         RCD,    2,   0,    1,    1,   <REQ!MISS!MISM>
MD_DSC	       CACHE,         WCE,    2,   2,    1,    0,   <REQ!MISS!MISM!LAST>
SIZEOF_NOCACHING = . - <DESCRIPTOR_BASE + NOCACHING_PAGE_DESC>

	.ENDC
;-------------------------------------------------------------------------------
CONTROL_MODE_PAGE_DESC = . - DESCRIPTOR_BASE
CTL_SUMFLG=0
;          Page name       Bitname  Boff  Bit  Size  Val    Flags   
;          ---------	   -------  ----  ---  ----  ---    -----
MD_DSC		 CTL,	     DQUE,    3,   0,    1,    0,   0
MD_DSC	         CTL,        QERR,    3,   1,    1,    0,   <REQ!MISS!MISM>
MD_DSC	         CTL,        EECA,    4,   7,    1,    0,   <REQ!MISS!MISM!LAST>
;-------------------------------------------------------------------------------
RIGID_DISK_PAGE_DESC = . - DESCRIPTOR_BASE
RGD_SUMFLG=0
;          Page name       Bitname          Boff  Bit  Size  Val    Flags   
;          --------- -------------------    ----  ---  ----  ---    -----
MD_DSC	    RGD,     TRACKS_PER_CYLINDER,    5,   0,    8,    0,   <MISS>
MD_DSC	    RGD,           NUM_CYLINDERS,    3,   0,   16,    0,   <MISS!LAST>
SIZEOF_RIGID_DISK = . - <DESCRIPTOR_BASE + RIGID_DISK_PAGE_DESC>
;-------------------------------------------------------------------------------
FORMAT_DEVICE_PAGE_DESC = . - DESCRIPTOR_BASE
FMT_SUMFLG=0
;          Page name       Bitname          Boff  Bit  Size  Val    Flags   
;          --------- -------------------    ----  ---  ----  ---    -----
MD_DSC	    FMT,      SECTORS_PER_TRACK,     11,   0,    8,    0,   <MISS>
MD_DSC	    FMT,      SECTOR_SIZE_MSB,       12,   0,    8,    0,   <MISS>
MD_DSC	    FMT,      SECTOR_SIZE_LSB,       13,   0,    8,    0,   <MISS!LAST>
SIZEOF_FORMAT = . - <DESCRIPTOR_BASE + FORMAT_DEVICE_PAGE_DESC>
FMT_USE_SUMFLG=FMT_SUMFLG
;-------------------------------------------------------------------------------
FORMAT_DEVICE_SEL_PAGE_DESC = . - DESCRIPTOR_BASE
FMT_SUMFLG=0
;          Page name       Bitname         Boff  Bit  Size  Val    Flags   
;          --------- -------------------   ----  ---  ----  ---    -----
MD_DSC	    FMT,     SEL_SECTORS_PER_TRACK,  10,  0,   16,    0,   <REQ!MISS!MISM!LAST>
;-------------------------------------------------------------------------------
FLEX_DISK_PAGE_DESC = . - DESCRIPTOR_BASE
FLX_SUMFLG=0
;          Page name       Bitname         Boff  Bit  Size  Val    Flags   
;          --------- -------------------   ----  ---  ----  ---    -----
MD_DSC	    FLX,       TRANSFER_RATE,        2,   0,   16,   0,   <MISS>
MD_DSC	    FLX, TRACKS_PER_CYLINDER,        4,   0,    8,   0,   <MISS>
MD_DSC	    FLX,       NUM_CYLINDERS,        8,   0,   16,   0,   <MISS>
MD_DSC	    FLX,             PAG_LEN,        1,   0,    8,   0,   <MISS>
MD_DSC	    FLX,                 SPC,       22,   0,    4,   0,   <LAST>
SIZEOF_FLEX_DISK = . - <DESCRIPTOR_BASE + FLEX_DISK_PAGE_DESC>
FLX_USE_SUMFLG=FLX_SUMFLG
;-------------------------------------------------------------------------------
FLEX_DISK_SELECT_PAGE_DESC = . - DESCRIPTOR_BASE
FLX_SUMFLG=0
;          Page name       Bitname         Boff  Bit  Size  Val    Flags   
;          --------- -------------------   ----  ---  ----  ---    -----
MD_DSC	    FLX,   SEL_TRANSFER_RATE,        2,   0,   16,   0,   <REQ!MISS!MISM>
MD_DSC	    FLX,           NUM_HEADS,        4,   0,    8,   0,   <REQ!MISS!MISM>
MD_DSC	    FLX,   SECTORS_PER_TRACK,        5,   0,    8,   0,   <REQ!MISS!MISM>
MD_DSC	    FLX,   SEL_NUM_CYLINDERS,        8,   0,   16,   0,   <REQ!MISS!MISM>
MD_DSC	    FLX,    START_SECTOR_NUM,       21,   6,    1,   0,   <REQ!MISS!MISM>
MD_DSC	    FLX,             SEL_SPC,       22,   0,    4,   0,   <REQ!MISS!MISM!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.
; (SCSI-2 ref. == Table 7-13 (Sense Key Descriptions)
; -

SENSE_KEY_TABLE::
;			VMS status	; SCSI sense key	 - Description
;			----------	; ---------------------------------------------------
	SENSE_KEY	NORMAL		; SCSI$C_NO_SENSE	 - No sense key information returned
	SENSE_KEY	RECOVERR	; SCSI$C_RECOVERED_ERROR - Command completed with some recovery action
	SENSE_KEY	DEVOFFLINE	; SCSI$C_NOT_READY	 - Logical unit cannot be accessed
	SENSE_KEY	PARITY		; SCSI$C_MEDIUM_ERROR	 - Command failed with non-recovered medium error
	SENSE_KEY	DRVERR		; SCSI$C_HARDWARE_ERROR	 - Command failed with non-recovered hardware error
	SENSE_KEY	DRVERR		; SCSI$C_ILLEGAL_REQUEST - Illegal parameter in the command descriptor block
	SENSE_KEY	MEDOFL		; SCSI$C_UNIT_ATTENTION	 - Removable medium change or target has been reset
	SENSE_KEY	WRITLCK		; SCSI$C_DATA_PROTECT	 - Read/Write of medium failed due to protection
	SENSE_KEY	DRVERR		; SCSI$C_BLANK_CHECK	 - Read/Write found blank medium, end-of-data, etc.
	SENSE_KEY	MEDOFL		; SCSI$C_VENDOR_SPECIFIC - Vendor specific conditions reported
	SENSE_KEY	MEDOFL		; SCSI$C_COPY_ABORTED	 - Copy, compare or Copy/Verify aborted due to error
	SENSE_KEY	DRVERR		; SCSI$C_ABORTED_COMMAND - Targeted aborted current command
	SENSE_KEY	NORMAL		; SCSI$C_EQUAL		 - Search Data command has equal comparison
	SENSE_KEY	DRVERR		; SCSI$C_VOLUME_OVERFLOW - Buffered device has reach end of parition
	SENSE_KEY	DATACHECK	; SCSI$C_MISCOMPARE	 - Source data command has unequal comparison
	SENSE_KEY	MEDOFL		; SCSI$C_RESERVED	 - Reserved

	.PAGE
	.SBTTL	Queue Depth Histogram Table

;
; Queue Depth - Histogram of optimal queue depths based on past I/O performance
;
QDEPTH_TABLE:
	.LONG	16	;   0 - 3
	.LONG	14	;   4 - 7
	.LONG	12	;   8 - 11
	.LONG	11	;  12 - 15
	.LONG	10	;  16 - 19
	.LONG	9 	;  20 - 23
	.LONG	8 	;  24 - 27
	.LONG	7 	;  28 - 31
	.LONG	6	;  32 - 35
	.LONG	5	;  36 - 39
	.LONG	5	;  40 - 43
	.LONG	5	;  44 - 47
	.LONG	5	;  48 - 51
	.LONG	5	;  52 - 55
	.LONG	5	;  56 - 59
	.LONG	5	;  60 - 63
	.LONG	5	;  64 - 67
	.LONG	4	;  68 - 71
	.LONG	4	;  72 - 75
	.LONG	4	;  76 - 79
	.LONG	4	;  80 - 83
	.LONG	4	;  84 - 87
	.LONG	4	;  88 - 91
	.LONG	4	;  92 - 95
	.LONG	3	;  96 - 99
	.LONG	3	; 100 - 103
	.LONG	3	; 104 - 107
	.LONG	3	; 108 - 111
	.LONG	3	; 112 - 115
	.LONG	3	; 116 - 119
	.LONG	3	; 120 - 123
	.LONG	3	; 124 - 127+

	.PAGE
	.SBTTL	Misc. SCSI data tables
;+
; 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>>

;
; Bitmask of error types that can be logged more than once
; referenced in LOG_ERROR.
;
DUPLICATE_ERR_MASK:
	.LONG	-
	<1@SCSI$C_MAP_BUFFER_ERROR>!-
	<1@SCSI$C_SEND_CMD_ERROR>!-
	<1@SCSI$C_EXTND_SENSE_DATA>!-
	<1@SCSI$C_REASSIGN_BLOCK>

; This table is used to convert between the vendor specific (RX26) 
; MEDIA_TYPE field in the MODE_SENSE header and the IRP$L_MEDIA value
;
MODE_SENSE_MEDIA_TYPE_TABLE:
	.BYTE	^X00		; default or unknown
	.BYTE	^X80		; DD (double density)
	.BYTE	^X81		; HD (high density)
	.BYTE	^X82		; ED (extended density)
MODE_SENSE_MEDIA_TYPE_MAX =<.-MODE_SENSE_MEDIA_TYPE_TABLE-1>

; index into this table with irp$l_media-1
; Note tables is biased to exclude (SCSI$FMT$R_PAGE_CODE, SCSI$FMT$B_PAGE_LENGTH)
;
MODE_SELECT_FORMAT_TABLE:
	.LONG	MODE_SELECT_FORMAT_RX26_DD-MODE_SELECT_FORMAT_TABLE-SCSI$FMT$W_TRACKS
	.LONG	MODE_SELECT_FORMAT_RX26_HD-MODE_SELECT_FORMAT_TABLE-SCSI$FMT$W_TRACKS
	.LONG	MODE_SELECT_FORMAT_RX26_ED-MODE_SELECT_FORMAT_TABLE-SCSI$FMT$W_TRACKS

;
; index into this table with irp$l_media-1
;
MODE_SELECT_FLEXIBLE_TABLE:
	.LONG	MODE_SELECT_FLEXIBLE_RX26_DD-MODE_SELECT_FLEXIBLE_TABLE-SCSI$FLX$W_TRANSFER_RATE
	.LONG	MODE_SELECT_FLEXIBLE_RX26_HD-MODE_SELECT_FLEXIBLE_TABLE-SCSI$FLX$W_TRANSFER_RATE
	.LONG	MODE_SELECT_FLEXIBLE_RX26_ED-MODE_SELECT_FLEXIBLE_TABLE-SCSI$FLX$W_TRANSFER_RATE

MODE_SELECT_FORMAT_RX26_HD:
MODE_SELECT_FORMAT_RX23_18:
	.BYTE	0,0		; tracks per zone
	.BYTE	0,0		; alt sectors per zone
	.BYTE	0,0		; alt tracks per zone
	.BYTE	0,0		; alt tracks per logical unit
	.BYTE	0,18		; sectors per track			ffh,ffh
	.BYTE	2,0		; bytes per sector
	.BYTE	0,1		; interleave
	.BYTE	0,0		; track skew
	.BYTE	0,0		; cylinder skew
	.BYTE	^xA0		; SSEC, HSEC, RMB, SURF, reserved_4	10h
	.BYTE	0,0,0		; reserved

MODE_SELECT_FLEXIBLE_RX26_HD:
MODE_SELECT_FLEXIBLE_RX23_18:
	.BYTE	^x01,^xF4	; transfer rate = 500 KHz		ffh,ffh
	.BYTE	2		; number of heads = 2			ffh
	.BYTE	18		; sectors per track = 18		ffh
	.BYTE	2,0		; data bytes per sector = 512
	.BYTE	0,80		; number of cylinders = 80		ffh,ffh
	.BYTE	0,80		; starting write precomp = 80
	.BYTE	0,80		; starting reduced write current = 80
	.BYTE	0,0		; step rate = default			ffh,ffh
	.BYTE	0		; step pulse width = default
	.BYTE	0,0		; head settle delay = default		ffh,ffh
	.BYTE	5		; motor on delay = 1/2 Second		ffh
	.BYTE	30		; motor off delay = 3 Seconds		ffh
	.BYTE	^x40		; trdy=0, ssn=1, mo=0			60h
	.BYTE	0		; extra step pulses per cylinder = 0	1fh
	.BYTE	0		; write compensation level unsupported
	.BYTE	0		; head load delay unsupported
	.BYTE	0		; head unload delay unsupported
	.BYTE	^x23		; pin 34=2, pin 2=3
	.BYTE	^x00		; pin 4=0
	.BYTE	0,0,0,0		; reserved

MODE_SELECT_FORMAT_RX26_DD:
MODE_SELECT_FORMAT_RX23_9:
	.BYTE	0,0		; tracks per zone
	.BYTE	0,0		; alt sectors per zone
	.BYTE	0,0		; alt tracks per zone
	.BYTE	0,0		; alt tracks per logical unit
	.BYTE	0,9		; sectors per track			ffh,ffh
	.BYTE	2,0		; bytes per sector
	.BYTE	0,1		; interleave
	.BYTE	0,0		; track skew
	.BYTE	0,0		; cylinder skew
	.BYTE	^xA0		; SSEC, HSEC, RMB, SURF, reserved_4	10h
	.BYTE	0,0,0		; reserved

MODE_SELECT_FLEXIBLE_RX26_DD:
MODE_SELECT_FLEXIBLE_RX23_9:
	.BYTE	^x00,^xFA	; transfer rate = 250 KHz		ffh,ffh
	.BYTE	2		; number of heads = 2			ffh
	.BYTE	9		; sectors per track = 9			ffh
	.BYTE	2,0		; data bytes per sector = 512
	.BYTE	0,80		; number of cylinders = 80		ffh,ffh
	.BYTE	0,80		; starting write precomp = 80
	.BYTE	0,80		; starting reduced write current = 80
	.BYTE	0,0		; step rate = default			ffh,ffh
	.BYTE	0		; step pulse width = default
	.BYTE	0,0		; head settle delay = default		ffh,ffh
	.BYTE	5		; motor on delay = 1/2 Second		ffh
	.BYTE	30		; motor off delay = 3 Seconds		ffh
	.BYTE	^x40		; trdy=0, ssn=1, mo=0			60h
	.BYTE	0		; extra step pulses per cylinder = 0	1fh
	.BYTE	0		; write compensation level unsupported
	.BYTE	0		; head load delay unsupported
	.BYTE	0		; head unload delay unsupported
	.BYTE	^x23		; pin 34=2, pin 2=3
	.BYTE	^x00		; pin 4=0
	.BYTE	0,0,0,0		; reserved

MODE_SELECT_FORMAT_RX33_15:
	.BYTE	0,0		; tracks per zone
	.BYTE	0,0		; alt sectors per zone
	.BYTE	0,0		; alt tracks per zone
	.BYTE	0,0		; alt tracks per logical unit
	.BYTE	0,15		; sectors per track
	.BYTE	2,0		; bytes per sector
	.BYTE	0,1		; interleave
	.BYTE	0,0		; track skew
	.BYTE	0,0		; cylinder skew
	.BYTE	^xA0		; SSEC, HSEC, RMB, SURF, reserved_4
	.BYTE	0,0,0		; reserved

MODE_SELECT_FLEXIBLE_RX33_15:
	.BYTE	^x01,^xF4	; transfer rate = 500 KHz
	.BYTE	2		; number of heads = 2
	.BYTE	15		; sectors per track = 15
	.BYTE	2,0		; data bytes per sector = 512
	.BYTE	0,80		; number of cylinders = 80
	.BYTE	0,80		; starting write precomp = 80
	.BYTE	0,80		; starting reduced write current = 80
	.BYTE	0,0		; step rate = default
	.BYTE	0		; step pulse width = default
	.BYTE	0,0		; head settle delay = default
	.BYTE	5		; motor on delay = 1/2 Second
	.BYTE	30		; motor off delay = 3 Seconds
	.BYTE	^x40		; trdy=0, ssn=1, mo=0
	.BYTE	0		; extra step pulses per cylinder = 0
	.BYTE	0		; write compensation level unsupported
	.BYTE	0		; head load delay unsupported
	.BYTE	0		; head unload delay unsupported
	.BYTE	^x23		; pin 34=2, pin 2=3
	.BYTE	^x00		; pin 4=0
	.BYTE	0,0,0,0		; reserved

MODE_SELECT_FORMAT_RX26_ED:
	.BYTE	0,0		; tracks per zone
	.BYTE	0,0		; alt sectors per zone
	.BYTE	0,0		; alt tracks per zone
	.BYTE	0,0		; alt tracks per logical unit
	.BYTE	0,36		; sectors per track			ffh,ffh
	.BYTE	2,0		; bytes per sector
	.BYTE	0,1		; interleave
	.BYTE	0,0		; track skew
	.BYTE	0,0		; cylinder skew
	.BYTE	^xA0		; SSEC, HSEC, RMB, SURF, reserved_4	10h
	.BYTE	0,0,0		; reserved

MODE_SELECT_FLEXIBLE_RX26_ED:
	.BYTE	^x03,^xE8	; transfer rate = 1000 KHz		ffh,ffh
	.BYTE	2		; number of heads = 2			ffh
	.BYTE	36		; sectors per track = 36		ffh
	.BYTE	2,0		; data bytes per sector = 512
	.BYTE	0,80		; number of cylinders = 80		ffh,ffh
	.BYTE	0,80		; starting write precomp = 80
	.BYTE	0,80		; starting reduced write current = 80
	.BYTE	0,0		; step rate = default			ffh,ffh
	.BYTE	0		; step pulse width = default
	.BYTE	0,0		; head settle delay = default		ffh,ffh
	.BYTE	5		; motor on delay = 1/2 Second		ffh
	.BYTE	30		; motor off delay = 3 Seconds		ffh
	.BYTE	^x40		; trdy=0, ssn=1, mo=0			60h
	.BYTE	0		; extra step pulses per cylinder = 0	1fh
	.BYTE	0		; write compensation level unsupported
	.BYTE	0		; head load delay unsupported
	.BYTE	0		; head unload delay unsupported
	.BYTE	^x23		; pin 34=2, pin 2=3
	.BYTE	^x00		; pin 4=0
	.BYTE	0,0,0,0		; reserved


	.PAGE
	.SBTTL	+
	.SBTTL	+ DRIVER ENTRY POINTS
	.SBTTL	+
	.SBTTL	DK_CTRL_INIT	- Controller initialization routine
;+
; DK_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
;
; This routine is a NOP for power failure recovery.
; For system startup and driver loading it allocates a CDDB.
;
; Context:
;
;	Powerfail thread
;	IPL = POWER
;	No Locks held
;
; STEP 2 INTERFACE
;
;	int CTRLINIT (IDB *, DDB *, CRB *)
;
; OBSOLETE STEP 1 INPUTS:
;
;	R4 & R5	- address of the IDB (controller status register)
;	R6	- address of the DDB (device data block)
;	R8	- address of the CRB (channel request block)
;
; OBSOLETE STEP 1 OUTPUTS:
;
;	R0	- Status
;	R1,R2	- Destroyed
;	All other registers preserved
;-
	DRIVER_CODE
DK_CTRL_INIT::					;P X; Controller Initialization Routine
	$DRIVER_CTRLINIT_ENTRY PRESERVE=<R2,R3,R4,R5,R6,R8>

	ONCE_DK_DEBUG CTRL_INIT

	MOVB	#SPL$C_SCS,CRB$B_FLCK(R8)	;P X; Initialize device spin lock index.
	TSTL	CRB$L_SCS_STRUC(R8)		;P X; Check for CDDB already present.
	BEQL	10$				;P X; Branch if not
	MOVZWL	#SS$_NORMAL, R0			;P X; Set success status
	RET					;P X; Otherwise, return to caller

;
; Create fork thread to finish controller init.
;
10$:	MOVL	R8,R5				;P X; Fork with CRB
	MOVL	R6,R3				;P X; Save DDB for after fork
	FORK    ROUTINE=DK_CTRL_INIT_FORK, CONTINUE=20$

20$:						;P X; Now that the fork routine is queued...
	MOVL	#SS$_NORMAL,R0			;P X; set return value to normal and
	RET					;P X; return to caller.
;
; Get pool for CDDB.
;
DK_CTRL_INIT_FORK:
	FORK_ROUTINE				;F FF; Generate a .JSB_ENTRY
	MOVL	R3,R6				;F FF; get DDB in R6 
	MOVL	R5,R8				;F FF; get CRB in expected register
	MOVZWL	#CDDB$K_LENGTH,R1		;F FF; Size of CDDB
	JSB	G^EXE$ALONONPAGED		;F FF; Allocate some pool
	BLBS	R0,30$				;F FF; Branch if successful
	BUG_CHECK INCONSTATE,FATAL		;F FF; Otherwise, bugcheck

;
; Clear pool.
;
30$:	PUSHR	#^M<R1,R2,R5>			;F FF; Save registers.
	MOVC5	#0,.,#0,R1,(R2)			;F FF; Zero entire block.
	POPR	#^M<R1,R2,R5>			;F FF; Restore saved registers.

;
; Initialize necessary CDDB fields.
;
	MOVW	R1,CDDB$W_SIZE(R2)		;F FF; Size

	ASSUME CDDB$B_SUBTYPE EQ CDDB$B_TYPE+1
	MOVW	#<DYN$C_CLASSDRV!-		;F FF; Type
		 <DYN$C_CD_CDDB@8>>,-		;F FF; Subtype
		 CDDB$B_TYPE(R2)		;F FF;
	MOVL	G^CLU$GL_ALLOCLS,-		;F FF; Allocation class
		CDDB$L_ALLOCLS(R2)		;F FF;
	MOVL	R8,CDDB$L_CRB(R2)		;F FF; CRB address
	MOVL	R6,CDDB$L_DDB(R2)		;F FF; DDB address
	MOVL	R2,CRB$L_SCS_STRUC(R5)		;F FF; Save CDDB address in CRB.
	MOVL	#SS$_NORMAL,R0			;F FF; Set success status
	RSB					;F FF; Return to caller

	.PAGE
	.SBTTL	DK_UNIT_INIT	- Unit initialization routine
;+
; DK_UNIT_INIT
;
; This routine performs unit-specific initialization and is called for each
; disk 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.
;
; The first time through this routine a set of SPTEs are allocated which are
; used to double map the user's buffer during datacheck operations. The cells
; used to record the address of the SPTEs and the system virtual address
; mapped by them live in the driver image. This is possible since use of the
; SPTEs is synchronized with the fork lock.
;
; An inquiry command is sent to the target to determine the device type.
; In additon, 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.
;
; Context:
;
;	Powerfail thread
;	IPL = POWER
;	No Locks held
;
; STEP 2 INTERFACE
;
;	int UNITINIT (IDB *, UCB *)
;
; OBSOLETE STEP 1 INPUTS:
;
;	R3 & R4	- address of the IDB (controller status register)
;	R5	- UCB address
;
; OBSOLETE STEP 1 OUTPUTS:
;
;	R0	- Status
;	R1,R2	- Destroyed
;	All other registers preserved
;-
;
DK_UNIT_INIT::					;P X; Unit Initialization Routine
	$DRIVER_UNITINIT_ENTRY PRESERVE = <R2,R3,R4,R5>	  

	ONCE_DK_DEBUG UNIT_INIT

	BBS	#UCB$V_POWER,UCB$L_STS(R5),20$	;P X; Return immediately if powerfail

	BISL	#UCB$M_ONLINE!UCB$M_BSY,-	;P X; Set unit online and busy
		UCB$L_STS(R5)			;P X;
	CMPL	G^SYS$AR_BOOTUCB,R5		;P X; Is this the system disk?
	BNEQ	10$				;P X; Branch if not
	MOVL	#^X7FFFFFFF,UCB$L_MAXBLOCK(R5)	;P X; Set up a MAXBLOCK for booting
10$:	FORK ROUTINE=DK_UNIT_INIT_FORK, -	;P X; Fork to drop IPL to SYNCH
	CONTINUE=20$				;P X; not that the fork routine is queued
20$:	MOVL	#SS$_NORMAL,R0			;P X; set the return value to normal
	RET					;P X; and return

DK_UNIT_INIT_FORK:
	FORK_ROUTINE				;F FF; Generate .JSB_ENTRY

	.IF DEFINED TRACE_CB
	MOVL	#1024 * TRB$C_LENGTH, R1	;F FF; Size of required pool
	JSB	G^EXE$ALONONPAGED		;F FF; Allocate a histogram buffer
	BLBC	R0,10$				;F FF; Pool allocation successful?

	MOVL	R2, UCB$L_TOP_OF_RING(R5)	;F FF; Save top of ring buffer
	MOVL	R2, UCB$L_RING_POINTER(R5)	;F FF; Init ring buffer
	MOVL	#1024, UCB$L_RING_COUNTER(R5)	;F FF; Init counter
	.ENDC

	BISL	#UCB$M_CB_NOCMDQ,-		;F FF; Don't attempt CMDQ until IO_PACKACK
		UCB$L_CLASS_BUSY(R5)		;F FF; completes, and validated device's support

	SET_SINGLE	INIT,R5,10$		;F FF; Single threaded UNIT initialization.

	KP_ALLOCATE_KPB -			;F FF; Allocate kernel process block
		KPB=UCB$PS_UNITINIT_KPB(R5),-	;F FF
		FLAGS=#KP$M_IO			;F FF; Specify deallocation after init ends
	BLBC	R0,10$				;F FF; Branch if error allocating KPB
	MOVL	UCB$PS_UNITINIT_KPB(R5),R2	;F FF; Get KPB address from UCB
	MOVL	R5,KPB$PS_UCB(R2)		;F FF; Save UCB address in KPB
	KP_START -				;F FF; Start the kernel process that does
		KPB=R2,-			;F FF;	most of the unit init work
		ROUTINE=DK_KP_UNIT_INIT,-
		REGISTERS=#KPREG$K_HLL_REG_MASK
	RSB					;F FF; Return from DK_UNIT_INIT
10$:	BUG_CHECK INCONSTATE,FATAL		;F FF; 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.

DK_KP_UNIT_INIT::

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

	$ARG_DEF <kpb>				; KPB address

	MOVL	kpb(AP),R2			;F FF; Pick up pointer to KPB
	MOVL	KPB$PS_UCB(R2),R5		;F FF; Restore UCB address
	BSBW	SETUP_TRACE			;F FF; Set up trace buffer

	ASSUME	UCB$V_DISCONNECT LT 8
	MOVB	#UCB$M_DISCONNECT!-		;F FF; By default, assume the target device
		 UCB$M_SYNCHRONOUS,-		;F FF; is capable of both disconnecting and
		UCB$L_DK_FLAGS(R5)		;F FF; synchronous operation

	ASSUME	UCB$V_DD_BYPASS LT 16
	BICW2	#UCB$M_DD_BYPASS,-		;F FF; Ensure no writes to DD diskettes
		UCB$L_DK_FLAGS(R5)		;F FF; on an RX23S drive
						;F FF; (provides space for an in-place
						;F FF; patch)

	CLRB	UCB$B_SEEK_DIR(R5)		;F FF; Initialize seek direction flag
	CLRL	UCB$PS_SAVE_CONN_CHAR(R5)	;F FF; Initialize location to hold
						;F FF; connection characteristics used
						;F FF; in IO_DIAGNOSE calls.

	MOVAL	UCB$L_FLUSH_IOQFL(R5),R0	;F FF; Initialize the queue used to flush
	MOVL	R0,(R0)				;F FF; I/Os which are queued during unit
	MOVL	R0,4(R0)			;F FF; init when unit init fails.

	MOVAB	UCB$Q_IRP_LIST(R5), R0		;F FF; Initialize the queue used to hold
	MOVL	R0,(R0)				;F FF; the active IRPs.
	MOVL	R0,4(R0)			;F FF;
	CLRL	UCB$L_QUEUED_IO_COUNT(R5)	;F FF; clear count

	MOVAL	UCB$Q_DC_WAIT_LIST(R5), R0	;F FF; Initialize the queue used to hold
	MOVL	R0,(R0)				;F FF; DC (datacheck) SCDRP's waiting
	MOVL	R0,4(R0)			;F FF; for exclusive access to the UCB

	MOVAL	UCB$Q_DRAIN_LIST(R5), R0	;F FF; Initialize the queue used to hold
	MOVL	R0,(R0)				;F FF; SCDRP's waiting for I/O's to be
	MOVL	R0,4(R0)			;F FF; drained, before proceeding

;
; Allocate and initialize BUSY_BIT_IRP used to keep UCB$V_BSY set during times
; when UCB$L_CLASS_BUSY bits are still set.
;
	MOVAL	UCB$R_BUSY_BIT_IRP(R5), R0	;F FF; Get starting address of BUSY_BIT_IRP
	ASSUME	IRP$W_SIZE  EQ 8
	ASSUME	IRP$B_TYPE  EQ 10
	ASSUME	IRP$B_RMOD  EQ 11
	MOVL	#<DYN$C_IRP@16>!IRP$K_LENGTH,-	;F FF; Set the size, type and access mode fields
		IRP$W_SIZE(R0)			;F FF: fields if the IRP
	MOVL	R5, IRP$L_UCB(R0)		;F FF; Set ownership UCB
	MOVAL	DK_GOLDEN_IRP_POST,-            ;F FF; Insure against post-
		IRP$L_PID(R0)                   ;F FF; processing of "golden" IRP

;
; Setup UCB CDDB field.
;
	MOVL	UCB$L_CRB(R5),R0		;F FF; Get CRB address
	MOVL	CRB$L_SCS_STRUC(R0),-		;F FF; Get CDDB address out of the CRB.
		UCB$L_CDDB(R5)			;F FF;

;
; Check system disk.
;
	CMPL	G^SYS$AR_BOOTUCB,R5		;F FF; Is this the system disk?
	BNEQ	5$				;F FF; Branch if not

;
; Check for host based shadowed system disk.  If so, fork and wait until
; the controller init routine of SHDRIVER completes its execution.
;
	BLBC	G^EXE$GL_SHADOW_SYS_DISK,5$	;F FF; LBC not booting hbs. Continue.

3$:	CMPL	G^SYS$AR_BOOTUCB,G^EXE$GL_SYSUCB;F FF; Is the system disk pointer updated?
	BNEQ	5$				;F FF; NEQ means updated. Continue.

	MOVL	UCB$PS_UNITINIT_KPB(R5),R8	;F FF; Get KPB address
	KP_STALL_FORK_WAIT  KPB=R8,-		;F FF; Fork wait using UCB fork block
			    FKB=R5
	BRB	3$				;F FF; Try again.

5$:
	CLRL	UCB$L_READ_COUNT(R5)		;F FF; Clear perf counters
	CLRL	UCB$L_WRITE_COUNT(R5)		;F FF; Clear perf counters
	CLRL	UCB$L_OTHER_COUNT(R5)		;F FF; Clear perf counters
	TSTL	UCB$L_READ_XLEN_HIST(R5)	;F FF; Do we already have pool?
	BLSS	1000$				;F FF; Br if yes
	MOVL	#XLEN_HIST_SIZE,R1		;F FF; Size of required pool
	JSB	G^EXE$ALONONPAGED		;F FF; Allocate a histogram buffer
	BLBC	R0,30$				;F FF; Pool allocation successful?
	MOVL	R2,UCB$L_READ_XLEN_HIST(R5)	;F FF; Save histogram address
	MOVL	#XLEN_HIST_SIZE,R1		;F FF; Size of required pool
	JSB	G^EXE$ALONONPAGED		;F FF; Allocate a histogram buffer
	BLBC	R0,30$				;F FF; Pool allocation successful?
	MOVL	R2,UCB$L_WRITE_XLEN_HIST(R5)	;F FF; Save histogram address
	MOVL	#XLEN_HIST_SIZE,R1		;F FF; Size of required pool
	JSB	G^EXE$ALONONPAGED		;F FF; Allocate a histogram buffer
	BLBC	R0,30$				;F FF; Pool allocation successful?
	MOVL	R2,UCB$L_XLEN_HIST(R5)		;F FF; Save histogram address
1000$:	PUSHR	#^M<R0,R1,R2,R3,R4>		;F FF; Save regs
	PUSHL	R5				;F FF; Save UCB seperatly
	MOVL	#XLEN_HIST_SIZE,R1		;F FF; length
	MOVL	UCB$L_READ_XLEN_HIST(R5),R2	;F FF; Address
	MOVC5	#0,.,#0,R1,(R2)			;F FF; Clear read counters
	MOVL	(SP),R5				;F FF; Get copy of UCB
	MOVL	#XLEN_HIST_SIZE,R1		;F FF; length
	MOVL	UCB$L_WRITE_XLEN_HIST(R5),R2	;F FF; Address
	MOVC5	#0,.,#0,R1,(R2)			;F FF; clear write counters
	MOVL	(SP),R5				;F FF; Get copy of UCB
	MOVL	#XLEN_HIST_SIZE,R1		;F FF; length
	MOVL	UCB$L_XLEN_HIST(R5),R2		;F FF; Address
	MOVC5	#0,.,#0,R1,(R2)			;F FF; clear write counters
	POPL	R5				;F FF; Restore UCB
	POPR	#^M<R0,R1,R2,R3,R4>		;F FF; Save regs
	MOVL	#XLEN_HIST_TURNOVER,-		;F FF; Set I/O counter for qdepth check
		UCB$L_XLEN_HIST_CYCLE(R5)
	MOVAB	UCB$L_QDEPTH(R5),-		;F FF; Point to start of our perf data area
		UCB$L_2P_LINK(R5)		;F FF; for DKPERF.C

; All SCSI DISK 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		;F FF; Assume bad LUN or SUBLUN specified
	MOVZWL	UCB$W_UNIT(R5),R1		;F FF; Get device unit number
	CLRL	R2				;F FF; Prepare for extended divide
	EDIV	#100,R1,R1,R2			;F FF; Extract SCSI bus ID from LUN
	CMPL	R1,#31				;F FF; Valid SCSI ID (0 <= n <= 7)?
	BGTRU	40$				;F FF; Branch if not
	CMPL	R2,#7				;F FF; Valid LUN (0 <= n <= 7)?
	BGTRU	40$				;F FF; Branch if not
        CLRB    UCB$B_LUN(R5)                   ;F FF; Clear for higher LUN num
	ASHL	#16,R1,R1			;F FF; Place SCSI ID in high-order word of R1
	ASHL	#16,R2,R2			;F FF; Place LUN in high-order word of R2
	MOVL	UCB$L_DDB(R5),R0		;F FF; Get DDB address
	SUBB3	#^A'A',DDB$T_NAME+3(R0),R1	;F FF; Translate controller letter to
						;F FF; SCSI bus ID.

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

6$:	ADDL3	G^EXE$GL_ABSTIM,-		;F FF; Calculate and save init
		#DK_INIT_TIMEOUT,-		;F FF; expiration time
		UCB$L_INITMO(R5)
	MOVL	UCB$PS_UNITINIT_KPB(R5),R8	;F FF; Get KPB address
	MOVL	R1,KPB$PS_SCSI_PTR1(R8)		;F FF; Save SCSI IDs
	MOVL	R2,KPB$PS_SCSI_PTR2(R8)		;F FF; Save LUN value
	
7$:	SPI$CONNECT -				;F FF; Connect to the port driver
		PORT_CALLBACK=DK_PORT_CALLBACK-	;F FF; Set callback routine
		PORT_STATE_CONTEXT=R5		;F FF; Set callback context (UCB)

	;
	; On return from SPI$CONNECT
	;	R0 = Call status
	; 	R1 = Maximum byte count
	;	R2 = SCDT address
	;	R3 = PORT Capability Flags
	;	R4 = SPDT address

	BLBS	R0,10$				;F FF; Branch if connect attempt succeeded
	CMPL	UCB$L_INITMO(R5),G^EXE$GL_ABSTIM;F FF; Has the timeout time passed?
	BLEQU	40$				;F FF; If so, set this unit OFFLINE
	KP_STALL_FORK_WAIT  KPB=R8,-
			    FKB=R5		;F FF; Fork wait using UCB fork block
	MOVL	KPB$PS_SCSI_PTR1(R8),R1		;F FF; Restore SCSI IDs
	MOVL	KPB$PS_SCSI_PTR2(R8),R2		;F FF; Restore LUN value
	BRB	7$				;F FF; Try connecting again
10$:	BBC	#SPDT$V_PFLG_AEN,R3,101$	;F FF; See if port supports AEN
	BISL	#UCB$M_PORT_AEN,-		;F FF; Set port_aen bit
		UCB$L_DK_FLAGS(R5)		;F FF;	 in UCB flags
101$:	BITL	#<SPDT$M_PFLG_AUTOSENSE+-	;F FF; See if Device and/or Port
		  SPDT$M_PFLG_PORT_AUTOSENSE>,R3;F FF;    supports Autosense
	BEQL	102$
	BISL	#UCB$M_PORT_AUTOSENSE,-		;F FF; Set port_autosense bit
		UCB$L_DK_FLAGS(R5)		;F FF;	 in UCB flags
102$:	BBC	#SPDT$V_PFLG_CMDQ,R3,11$	;F FF; See if port supports Command Queuing
	BISL	#UCB$M_PORT_CMDQ,-		;F FF; Set port_cmdq bit
		UCB$L_DK_FLAGS(R5)		;F FF;	 in UCB flags

	.print	;SPI$CONNECT returns: SCQ$IS_MAX_BCNT in R1, SCQ$IS_PORT_SERV_FLAGS in R3

11$:	ASHL	#-24,R3,R3			;F FF; Get MAXBCNT divisor
	BNEQ	12$				;F FF; Branch if divisor supplied by port
	MOVZBL	#1,R3				;F FF; Otherwise, use a divisor of 1
12$:	DIVL	R3,R1				;F FF; Get MAXBCNT recommended by port
	BICL	#<IOC$C_DISK_BLKSIZ-1>,R1	;F FF; Make MAXBCNT an integral block count
	CMPL	R1,UCB$L_MAXBCNT(R5)		;F FF; For MAXBCNT, use minimum supported
	BGEQ	15$				;F FF; value of port and class drivers
	MOVL	R1,UCB$L_MAXBCNT(R5)		;F FF; Save maximum byte count in UCB
15$:	MOVL	R2,UCB$PS_SCDT(R5)		;F FF; Save SCDT address
	MOVL	R4,UCB$L_PDT(R5)		;F FF; Save PDT address

16$:	TSTL	DATACHECK_SPTE			;F FF; Datacheck SPTEs already allocated
	BNEQ	20$				;F FF; Branch if so
	$BYTES_TO_PAGES -			;F FF; Convert to max page count
		SOURCE_BYTCNT=UCB$L_MAXBCNT(R5),-
		DEST_PAGCNT=R2,-
		ROUNDUP=YES
	INCL	R2				;F FF; Account for non-page-aligned buffers
	JSB	G^LDR$ALLOC_PT			;F FF; Allocate SPTEs to double map user buf
	BLBC	R0,40$				;F FF; Branch if failure
	MOVL	R1,DATACHECK_SPTE		;F FF; Save SVA of the first SPTE
	SUBL2	G^MMG$GL_SPTBASE,R1		;F FF; Get offset into page table
	ASHL	G^MMG$GL_PTE_OFFSET_TO_VA,R1,R1	;F FF; Calculate system virtual addr
	BISL3	#VA$M_SYSTEM,R1,-		;F FF; mapped by this set of SPTEs
		DATACHECK_SVA			;F FF;

20$:	
	PUSHL	#NUM_LONGWORDS_DIAGNOSE		;F FF; Number of longwords in UCB 
						;F FF; reserved for IO$_DIAGNOSE
	PUSHAL	UCB$PS_DIAGNOSE(R5)		;F FF; Start of DIAGNOSE longwords
	CALLS	#2,DKMK$DIAGNOSE_INIT		;F FF; Initialize DIAGNOSE area
	BLBC	R0,40$				;F FF; Set offline if error
	BISL2	#UCB$M_TENBYTE,-		;F FF; Assume 10-byte mode sense support
		UCB$L_DK_FLAGS(R5)

        CLR_SINGLE	INIT,R5,30$		;F FF; UNIT initialization is complete
	BSBW	SET_UNIT_ONLINE			;F FF; Go bring the unit online
	RET					;F FF; Return to caller within KP services

30$:	BUG_CHECK INCONSTATE,FATAL		;F FF; bugcheck

;
; Connection failure. Log an error and set the unit offline.
;
40$:	LOG_ERROR -				;F FF; Log a connection error
		TYPE=CONNECTION_ERROR,-		;F FF;
		VMS_STATUS=R0,-			;F FF;
		UCB=R5				;F FF;
	BICL	#UCB$M_ONLINE!UCB$M_BSY,-	;F FF; Set the unit offline and not busy
		UCB$L_STS(R5)			;F FF; Return to caller within KP services
	CLR_SINGLE	INIT,R5,30$		;F FF; Unit initialization is complete.
	RET					;F FF

	.PAGE
	.SBTTL	DK_GOLDEN_IRP_POST

;++
; DK_GOLDEN_IRP_POST
;
;	The "golden" IRP might accidentially get placed on the I/O post
;	processing queue.  If that happens, we want it to be harmless.
;	To make attempts to post process the "golden" IRP harmless,
;	the address of this routine is placed in IRP$L_PID.  When post-
;	processing sees this routine address in IRP$L_PID, it will call
;	this routine.  This routine does only two things.  It marks the
;	"golden" IRP as not queued, and it returns success.
;
;	Even with this routine, there is a very small window where attempts
;	to post process the "golden" IRP will cause memory corruption.  If
;	I/O post-processing has dequeued the "golden" IRP and STOP_NEW_IO
;	tries to REMQUE it again, corruption will result.  The window for
;	this corruption is from the time that I/O post-processing dequeues
;	the "golden" IRP until the time that this routine clears the
;	"golden" IRP is queued field.
;
; Context
;
;	I/O Post Processing
;	IPL = IOPOST
;
; Calling sequence:
;
;	PUSHL	irp		; "golden" IRP address
;	CALL	#1,DK_GOLDEN_IRP_POST
;
;	Input:
;		4(AP)	"golden" IRP address
;
;	Output:
;		R0	SS$_NORMAL
;--

DK_GOLDEN_IRP_POST::		; PP; Handle posted "golden" IRP
	.CALL_ENTRY	PRESERVE=<R5>

        $ARG_DEF <irp>				; PP; IRP Address

	MOVL	irp(AP), R5			; PP; Get IRP Address
	CLRL	(R5)				; PP; Clear 1st IRP longword

	MOVL	#SS$_NORMAL, R0			; PP; Set success statusn
	RET					; PP; All done, exit

	.PAGE
	.SBTTL	DK_PORT_CALLBACK - Callback Routine to Handle Port Busy Events.

;++
; DK_PORT_CALLBACK
;
;	The port driver is either declaring an event which is of interest to the
;	Class Driver. On a request specific bases this driver will alter its current
;	state (STOP or GO).
;
; ACA Processing Description
;
;	During normal class driver processing a SPI$K_PBCB_STOP_CHECK_CONDITION call will be
;	invoked by the port driver telling the class driver about a future check condition and
;	the need for ACA processing. The class driver should ONLY freeze its operations and not
;	send any further SCSI requests to the port queue (including ACA requests). The Class Driver
;	will now wait for "a" SCSI request to with a check condition, which will give the class driver
;	a chance to analyze the request sense data.  
;
;	After the class driver receives the request sense data, if the class driver supports 
;	ACA operations, then the ACA requests should be sent to the port driver (not queued), so they are
;	processed in sequential order.  When the class driver is done issuing ACA requests to correct the
;	failure then it must issue a final SCDRP with the SCDRP$V_FLAG_CLEAR_ACA_MSG bit set. 
;
;	Summary of class driver check condition processing:
;		1.)  Receive SPI$K_PBCB_STOP_CHECK_CONDITION via PORT_CALLBACK routine.
;		     As a result, stop the class driver initiation of SCSI requests for this device.
;		2.)  Wait for the SCSI request to complete with the check condition status.
;		3.)  Analyze the data from the request sense operation.
;		4.)  If the class driver is performing ACA error processing:
;			a.)  Allocate the next SCDRP required for ACA error processing.
;			b.)  Format this SCDRP.
;			c.)  Initiate this request with the SCDRP$K_QCHAR_ACA queue characteristic.
;			d.)  Wait for the SCDRP to complete.
;			e.)  Repeat step (a-d) as required to complete error recovery.
;			f.)  Allocate the next SCDRP required for CLEAR_ACA_MSG.
;			g.)  Set SCDRP$V_FLAG_CLEAR_ACA_MSG in SCDRP$L_SCSI_FLAGS.
;			h.)  Initiate this request with the SCDRP$K_QCHAR_ACA queue characteristic.
;		5.)  Receive SPI$K_PBCB_GO_ACA_COMPLETE via PORT_CALLBACK routine.
;		     As a result, start the class driver initiation of SCSI requests for this device.
;
; Note:	All of the callback states in this routine with the exception of 
;	reset will crash if an attempt is made to set them more than once.
;	Reset should tolerate that condition.
;
; Context
;
;	SCDRP Thread
;	IPL = Fork
;	Fork lock held
;
; Calling sequence:
;
;	PUSHL	#state		; SPI$K_PBCB_STOP_???? or SPI$K_PBCB_GO_????
;	PUSHAB	UCB		; UCB address associated with this connection.
;	CALL	#1, DK_PORT_CALLBACK
;
;	Input:
;		4(AP)	UCB address
;		8(AP)	Busy State: SPI$K_PBCB_STOP_BUSY or SPI$K_PBCB_GO_READY
;
;	Output:
;		R0	Destroyed
;		R1	Destroyed
;		none to the caller
;		but the class busy bit is set appropriately.
;--

DK_PORT_CALLBACK::			;S FF; Handle Port Busy Events.
	.CALL_ENTRY	PRESERVE=<R2,R3,R4,R5,R6,R7,R8,R9,R10,R11>

        $ARG_DEF <ucb,-				; UCB Address
		  pk_state>			; Busy State: SPI$K_PBCB_STOP_BUSY or SPI$K_PBCB_GO_READY

	DK_DEBUG PORT_CALLBACK

;	Get the port's busy status.

	MOVL	ucb(AP), R5			;S FF; Get the UCB address.
	MOVL	pk_state(AP), R0		;S FF; Get the port's busy state.

	DISPATCH R0,TYPE=B,<-			;? ??; Dispatch according to function
	    <SPI$K_PBCB_STOP_BUSY,		100$>,-	; Port is busy
	    <SPI$K_PBCB_STOP_CHECK_CONDITION,	110$>,-	; Port has seen a check condition event.
	    <SPI$K_PBCB_STOP_QUEUE_FULL_EVNT,	120$>,-	; Port has seen a queue full event.
	    <SPI$K_PBCB_STOP_BUS_RESET,		130$>,-	; Port has seen a bus reset
	    <SPI$K_PBCB_STOP_NO_SEND_CREDITS,	140$>,-	; Port has run out of send credits.
	    <SPI$K_PBCB_STOP_NO_CMD_BITS,	150$>,- ; Port has run out of SPDT CMD bits
	    <SPI$K_PBCB_GO_READY,		200$>,-	; Port is no longer busy
	    <SPI$K_PBCB_GO_ACA_COMPLETE,	210$>,-	; Port has finished the ACA processing.
	    <SPI$K_PBCB_GO_QUEUE_STARTING,	220$>,-	; Port is attempting to restart the queue following the queue full event.
	    <SPI$K_PBCB_GO_BUS_FREE,		230$>,-	; Port has finished bus reset processing
	    <SPI$K_PBCB_GO_SEND_CREDITS,	240$>,-	; Port has send credits available
	    <SPI$K_PBCB_GO_CMD_BITS,		250$>>	; Port has send credits available

	BRB	400$					; Fell off end of table with unsupported callback

;	The port is busy and can no longer process any requests from the
;	class driver.  Queue any new I/O requests until the condition in
;	the port driver clears and a port driver ready notification is
;	delivered.

100$:	SET_SINGLE	BUSY,R5,400$		;S FF; set busy state
	BRB		190$

110$:	SET_SINGLE	CHECK_CONDITION,R5,400$	;S FF; set check condition wait
	BRB		190$

120$:	SET_SINGLE	QUEUE_FULL_EVNT,R5,400$	;S FF; set queue full wait
	BRB		190$

130$:	SET_SINGLE	BUS_RESET,R5,190$	;S FF; Set reset wait-don't
	BRB		190$			;S FF; if already set don't bug_check

140$:	SET_SINGLE	NO_SEND_CREDITS,R5,400$	;S FF; Set no send credits wait
	BRB		190$

150$:	SET_SINGLE	NO_CMD_BITS,R5,190$	;S FF; Set no SPDT command bits
	BRB		190$

190$:	BRB	350$

;	The port is no longer busy, clear the busy state and restart any
;	pending I/O requests if necessary.
;


200$:	CLR_SINGLE	BUSY,R5,400$		;S FF; BUSY is complete
	BRB		290$

210$:	CLR_SINGLE	CHECK_CONDITION,R5,400$	;S FF; Check condition is complete
	BRB		290$

220$:	CLR_SINGLE	QUEUE_FULL_EVNT,R5,400$	;S FF; Queue event is complete
	BRB		290$

230$:	CLR_SINGLE	BUS_RESET,R5,290$	;S FF; Bus reset is complete
	BRB		290$			;S FF; If already clear, don't crash

240$:	CLR_SINGLE	NO_SEND_CREDITS,R5,400$	;S FF; Send credits is complete
	BRB		290$

250$:	CLR_SINGLE	NO_CMD_BITS,R5,290$	;S FF; SPDT command bits are available
	BRB		290$

;
;	Start another I/O request if one is pending.
;
290$:	REMQUE	@UCB$L_IOQFL(R5), R3		;S FF; Remove IRP from device pending queue
	BVS	300$				;S FF; if found one then
	CALL_INITIATE				;S FF;	 initiate I/O
	RET					;S FF;   and return
300$:	TST_SINGLE	350$			;S FF; else are there any other reasons not clear bsy?
	BICL	#UCB$M_BSY, UCB$L_STS(R5)	;S FF; Clear UCB busy bit
350$:	RET					;S FF; Return
400$:	BUG_CHECK INCONSTATE,FATAL		;S FF; bugcheck

	.PAGE
	.SBTTL	DK_MOUNT_VER - Mount Verification Routine

;++
; DK_MOUNT_VER
;
;	The disk class driver and mount verification are in bed together.
;	They are so intimately related that one of them is most certainly
;	"with child" and nuptial announcements will soon be made.
;
;	The following is a list of reasons why this routine might be called:
;
;	   -	starting mount verification due to appropriate status is a
;		normal I/O request.  This probably is the result of the a
;		change of state in the drive.  Hopefully, mount verification
;		will restore the drive to the online state and work can
;		continue.
;	   -	completing mount verification for the above.
;
; Calling Sequence:
;
;	JSB	DK_MOUNT_VER
;
; Context:
;
;	Fork thread
;	IPL = Fork
;	Fork lock held
;
; Inputs:
;
;	For beginning mount verification:
;
;	R3	IRP address
;	R5	UCB address
;
;	For ending mount verification:
;
;	R3	0(zero)
;	R5	UCB address
;
; Outputs:
;
;	the contents of R0,R1,R2 and R4 may be overwritten (from [SYS]MOUNTVER.MAR)
;
;--
DK_MOUNT_VER::					;F FF; Mount Verification Routine
	$DRIVER_MNTVER_ENTRY

	DK_DEBUG MOUNT_VER

	TSTL	R3				;F FF; Is this begin or end of MNTVER?
	BNEQ	DK_BEGIN_MNTVER			;F FF; R3 <> 0 - Begin mount verification.
	BRB	DK_END_MNTVER			;F FF; R3 == 0 - End the mount verification.

	.PAGE
	.SBTTL	DK_BEGIN_MNTVER - Begin mount verification
;
; Functional Description:
;
;	This routine is called whenever mount verification needs to present a
;	new IRP to the driver to be "remembered" and restarted after mount
;	verification completes.	 If this is a "normal" mount verification
;	operation, the IRP is inserted into the pending I/O request queue,
;	ordered	 by sequence number.
;
;	If a "normal" IRP with the IRP$V_SRVIO bit set is input, the IRP is sent
;	to the post queue instead of placed back in the pending I/O request
;	queue.
;
; Context:
;
;	Fork thread
;	IPL = Fork
;	Fork lock held
;
; Inputs:
;
;	R3	IRP address
;	R5	UCB address
;
; Implicit inputs:
;
;	IRP$L_SEQNUM(R3)	monotonically increasing I/O request sequence
;				number
;	UCB$L_IOQ[F/B]L(R5)	header for queue of pending I/O requests for
;				this unit
;
; Outputs:
;
; Outputs:
;
;	the contents of R0,R1,R2 and R4 may be overwritten (from [SYS]MOUNTVER.MAR)
;

DK_BEGIN_MNTVER:: .GLOBAL_LABEL			;F FF; Begin mount verification

;
;	If the IRP is a served or shadowing I/O, no need to do MOUNT
;	verification on this IRP.
;
10$:   	BBS	#IRP$V_SRVIO,IRP$L_STS(R3),15$	;F FF; If served-I/O post it
	BBC	#IRP$V_SHDIO,IRP$L_STS2(R3),20$ ;F FF; If shadowing-I/O post it
						;F FF; else place on pending I/O queue
15$:	CALL_POST_IRP				;F FF; Post IRP
	RET					;F FF; return

;
; Set the CLASS_BUSY flag for mount verification in progress.
; Reinsert IRP in pending I/O queue in sequence number order.
;
20$:	BBS	#IRP$V_MVIRP,IRP$L_STS(R3),50$	;F FF; If this is a MVIRP, BUG_CHECK

	MSCP_MV					;F FF; Inform MSCP Server that MV is in progress
	SET_SINGLE	MNTVERIP,R5,lab=25$	;F FF; Single thread MOUNT Verification

25$:	MOVAB	<UCB$L_IOQFL-IRP$L_IOQFL>(R5),R1;F FF; Get pending I/O queue addr.
	MOVL	R1, R0				;F FF; Copy listhead address.
30$:	MOVL	IRP$L_IOQBL(R0),R0		;F FF; Get address of next entry
	CMPL	R0, R1				;F FF; Reached end of list yet?
	BEQL	40$				;F FF; Branch if end of list.
	CMPB	IRP$B_PRI(R3),IRP$B_PRI(R0)	;F FF; New entry priority greater?
	BLSSU	30$				;F FF; If lss yes
	CMPL	IRP$L_SEQNUM(R0),-		;F FF; Does new IRP belong here
		IRP$L_SEQNUM(R3)		;F FF; based on sequence #?
	BLSSU	30$				;F FF; Branch if IRP doesn't belong.
40$:	INSQUE	IRP$L_IOQFL(R3),IRP$L_IOQFL(R0) ;F FF; Insert new IRP at this point

;
; Prevent the "golden" IRP from becoming buried in the UCB$L_IOQFL
;
	TSTL	UCB$R_BUSY_BIT_IRP(R5)		;F FF; Is "golden" IRP queued?
	BEQL	49$				;F FF; NO
	MOVAL	UCB$L_IOQFL(R5),R0		;F FF; Get the correct back pointer
	CMPL	UCB$R_BUSY_BIT_IRP+4(R5),R0	;F FF; Is "golden" IRP first in Q?
	BEQL	49$				;F FF; Yes
	;
	; OK!  The "golden" IRP is buried in the I/O request queue.
	; Since it's not doing us any good there, simply remove it.
	;
	REMQUE	UCB$R_BUSY_BIT_IRP(R5), R0	;F FF; Remque the "golden" IRP
	CLRL	UCB$R_BUSY_BIT_IRP(R5)		;F FF; Clear it's "in use" flag
	TRACE_SINGLE 8108			;F FF; Trace golden IRP lost
49$:	RET					;F FF; Return.
50$:	BUG_CHECK INCONSTATE,FATAL		;S FF; Should not requeue MVIRP

	.PAGE
	.SBTTL	DK_END_MNTVER - End mount verification processing

;++
;
;	DK_END_MNTVER - End mount verification
;
; Functional Description:
;
;
;	This routine completes mount verification processing.
;
;	The suppress Mount Verification messages flags is cleared
;	The mount verification in progress flag is cleared.
;
;	If mount verification failed to bring the disk back online, all
;	requests are aborted with SS$_VOLINV to indicate that the volume is no
;	longer valid.
;
;	If mount verification succeded in bringing the disk online, this routine
;	performs the end of mount verification processing necessary to unstall
;	the single threaded processing of SCDRPs, caused by the initial startup
;	Mount Verification.
;
; Context:
;
;	Fork thread
;	IPL = Fork
;	Fork lock held
;
; Inputs:
;
;	R3	0
;	R5	UCB address
;
; Implicit inputs:
;
;	UCB$L_IOQ[F/B]L(R5)	header for queue of pending I/O requests for
;				this unit
;	UCB$L_STS(R5)		bit UCB$V_VALID clear if mount verification
;				failed
;
; Outputs:
;
;	the contents of R0,R1,R2 and R4 may be overwritten (from [SYS]MOUNTVER.MAR)
;
; Implicit outputs:
;
;	UCB$L_DEVSTS(R5)	UCB$V_MSCP_MNTVERIP bit cleared.
;	UCB$L_STS(R5)		UCB$V_SUPMVMSG bit cleared.
;
;--

DK_END_MNTVER::	.GLOBAL_LABEL			;F FF; End mount verification processing

	CLR_SINGLE	MNTVERIP,R5,40$		;F FF; End of MOUNT Verification.
						;F FF; Don't care if MNTVER

	BICL	#UCB$M_SUPMVMSG, UCB$L_STS(R5)	;F FF; Clear suppresion bit
	.BRANCH_LIKELY
	BBS	#UCB$V_VALID, UCB$L_STS(R5),20$	;F FF; Branch if mount verification successful

	;
	; Mount verification was not successful, abort all outstanding I/O's
	;

	;
	; Flush pending I/O queues
	;
10$:	REMQUE	@UCB$L_IOQFL(R5), R3		;F FF; Get next pending IRP.
	BVS	30$				;F FF; Branch if no more IRP

        MOVAL   UCB$R_BUSY_BIT_IRP(R5), -(SP)   ;F FF; Get the address of the Golden IRP
        CMPL    R3, (SP)+                       ;F FF; Is this is?
        BNEQ    15$                             ;F FF; Branch if not, OK to flush this IRP
        CLRL    (R3)                            ;F FF; Clear FLINK to show its not queued
        TRACE_SINGLE 8109                       ;F FF; Trace this event
        BRW     10$                             ;F FF; Get next IRP off of I/O queue
15$:    MOVL    R3,UCB$L_IRP(R5)                ;S FF; Set up UCB$L_IRP for the
						;S FF; IRP that's completing!
	CLRL	R1				;S FF; Clear transfer byte count
	MOVL	#SS$_VOLINV,R0			;F FF; Set volume invalid
	CALL_REQCOM				;F FF; Complete the QIO
	BRW	10$				;F FF; Loop till no more IRPs.

	;
	; Mount verification was successful, restart I/O's
	;
20$:	REMQUE	@UCB$L_IOQFL(R5),R3		;F FF; Remove IRP from device pending queue
	BVS	30$				;F FF; if found one then
	CALL_INITIATE 				;F FF;	 initiate I/O
	RET					;F FF;	 and return
30$:	CLRL	UCB$L_IRP(R5)			;F FF; All I/O has been processed.
	TST_SINGLE	40$			;S FF; else are there any other reasons not clear bsy?
	BICL	#UCB$M_BSY, UCB$L_STS(R5)	;S FF; Clear UCB busy bit
40$:	RET					;F FF; Return.

	.PAGE
	.SBTTL	DK_DRAIN_IO - Stall current I/O function, until I/O's are drained
;
;	DK_DRAIN_IO will be optionally called from within the SET_SINGLE marco
;	when the caller desires that all outstanding I/O's be drained from I/O
;	execution, before the current I/O (an SCDRP), is allowed to continue.
;
;	This routine when used in conjuntion with SET_SINGLE and its setting of
;	UCB$V_BSY, and pairing a SET/CLR_SINGLE pair around a SCSI ordered command
;	(which provides 'sequentiality' within a SCSI target), offers the functionality
;	of "strong sequentiality". "Strong sequentiality", is a guarantee that "all"
;	I/O's issued before this I/O are executed to completion, that this I/O is 
;	executed while no other I/O's are active, and upon completion of this I/O,
;	other I/O (if present), will be started, unless other reason are indicated
;	in UCB$L_CLASS_BUSY.
;
;
; Context:
;
;	Fork thread or Class Driver thread
;	IPL = Fork
;	Fork lock held
;
; Calling sequence:
;
;	SET_SINGLE	<a class busy-bit>,ucb,label,scdrp,can$
;	MOVAL	<some-scsi-CDB>, R2		; A SCSI command
;	SETUP_CMD				; Perform setup for SCSI command
;	SEND_COMMAND_ORDERED			; Send non-queued (ordered) command
;	CLEANUP_CMD				; Cleanup from the SCSI command
;       movl    final_status,r0                 ; Set final I/O status (if needed)
; can$:	CLR_SINGLE	<a class busy-bit>,ucb,label
;
;	Note: the presence of the can$ parameter in the SET_SINGLE invocation generates
;       the call to this routine and produces the "strong sequentiality" function.
;
;	Input:	UCB	- Address of the UCB
;		SCDRP	- Address of SCDRP
;
;	Output:	R0 - STATUS	SS$_NORMAL
;
DK_DRAIN_IO:
	.CALL_ENTRY PRESERVE=<R3,R5>

	$ARG_DEF <ucb,-				;F FF; UCB address
		  scdrp>			;F FF; SCDRP address

;
;	Get input parameters

	MOVL	ucb(AP), R3			;F FF; Get current UCB addres
	MOVL	scdrp(AP), R5			;F FF; Get current SCDRP address

;	Deterimine if all outstanding I/O's (include this one), are all stalled on 
;	the UCB's list of I/O's waiting for I/O drain to complete.
;
	INCL	UCB$IS_DRAIN_COUNT(R3)		;F FF; One more I/O wait for drain
	CMPL	UCB$IS_DRAIN_COUNT(R3),-	;F FF; Are there still I/O's waiting?
		UCB$L_QUEUED_IO_COUNT(R3)	;F FF;
	BEQL	10$				;F FF; If equal, all I/O's accounted for
	BGTR	20$				;F FF; If greater, too many I/O's, BUG_CHECK

;	This I/O operation (SCDRP and associated KPB), needs to stall waiting for
;	other active I/O's to complete.	Place this I/O on the drain list, and stall
;	waiting for I/O's to complete
;
	INSQUE	SCDRP$PS_PQFL(R5), -		;F FF; Enqueue SCDRP on tail of I/O drain
		@UCB$Q_DRAIN_LIST(R3)		;F FF; wait queue
	MOVL	SCDRP$PS_KPB(R5), R0		;F FF; Get SCDRP's KPB address
	MOVAL	G^IOC$RETURN, -			;F FF; Set NULL stall routine address
		KPB$PS_SCH_STALL_RTN(R0)	;F FF;
	PUSHL	R0				;F FF; Push KPB address
	CALLS	#1,G^EXE$KP_STALL_GENERAL 	;F FF; Wait until other I/O's complete
10$:	DECL	UCB$IS_DRAIN_COUNT(R3)		;F FF; I/O is no longer on drain list
	RET					;F FF; Draining operation complete
20$:	BUG_CHECK INCONSTATE,FATAL		;S FF; bugcheck

	.PAGE
	.SBTTL	STOP_NEW_IO - Block Unwanted I/O Initiation from REQCOM

;++
; STOP_NEW_IO
;
;	STOP_NEW_IO must be called before any operation that gives control
;	to REQCOM when one or more CLASS_BUSY bits are set (see TST_SINGLE).
;	If necessary, STOP_NEW_IO will place a "golden" IRP at the front of
;	the UCB$L_IOQ to prevent new work from being started.
;
;	Remember, any call to REQCOM authorizes the VMS I/O executive to
;	send another IRP to this driver's start I/O routine.  The VMS I/O
;	executive views all drivers that do not use the alternate start I/O
;	routine as single-threaded drivers.  From this perspective, this
;	driver has just completed its one and only outstanding unit of work.
;	Therefore, this driver must be ready for more work.
;
;	Unfortunately, this driver may not always be ready to accept new
;	work when REQCOM is called.  Readiness to accept new work must
;	be determined from information in the CLASS_BUSY bits and the
;	QUEUED_IO_COUNT field.  When new work cannot be accepted, normal
;	REQCOM work will be blocked by placing a "golden" IRP at the front
;	of the UCB$L_IOQ.  REQCOM will deliver the "golden" IRP to this
;	driver's start I/O routine.  The start I/O routine has been designed
;	to recognize and discard the "golden" IRP.  In this way, the normal
;	(start a new I/O) function of REQCOM is subvirted and blocked.
;
;	To a first order measure, new I/O requests must be blocked when
;	any CLASS_BUSY bits are set.  This would be the only rule, if all 
;	CLASS_BUSY bits were set and cleared based on the needs of this
;	class driver or the port drivers that it calls.  However, some of
;	the CLASS_BUSY bits are set and cleared based on the needs of layers
;	above this class driver.  For convenient reference these bits are
;	call the UL_BUSY bits.  Bits that fall in UL_BUSY catagory are:
;
;		UCB$V_CB_MNTVERIP
;
;	When only UL_BUSY bits are set, then QUEUED_IO_COUNT determines
;	when new I/O requests should be blocked.  QUEUED_IO_COUNT enters
;	the equation at this point because we cannot let more I/O requests
;	in to the driver until the driver is truely single streamed (no
;	outstanding requests in the port driver).  So, if only UL_BUSY
;	bits are set and QUEUED_IO_COUNT is non-zero, then new I/O requests
;	still must be blocked.  But when only UL_BUSY bits are set and
;	QUEUED_IO_COUNT is zero, then new I/O requests can be (and must be)
;	allowed.
;
;	Allowing new I/O requests through when only UL_BUSY bits are set is
;	very important.  Generally, the upper layer that caused the related
;	UL_BUSY bit to be set cannot perform the I/O requests that it needs
;	to do when this driver blocks those I/O requests because the UL_BUSY
;	bit is set.  For example, mount verification cannot do its IO$_PACKACK
;	if this driver blocks it just because the UCB$V_CB_MNTVERIP bit is
;	set.
;
;	"Golden" IRP requeue protection:  When mount verification is in
;	progress, it is possible for mount verification IRPs to displace
;	the "golden" IRP at the head of the I/O request queue.  As more
;	in-progress I/O requests complete and this routine gets called
;	for each completing request, it is possible that this routine will
;	attempt to queue the "golden" IRP multiple times.  To prevent this,
;	we use the forward pointer of the "golden" IRP as a "in use" flag.
;	If the forward pointer is non-zero, then the "golden" IRP is queued.
;	If the forward pointer is zer, then the "golden" IRP is not queued.
;	DK_STARTIO collaborates with this routine by zeroing the forward
;	pointer of the "golden" IRP everytime it is processed (which is the
;	same as everytime the "golden" IRP is dequeued).
;
; Context:
;
;	Fork thread or Class Driver thread
;	IPL = Fork
;	Fork lock held
;
; Calling sequence:
;
;		TST_SINGLE 10$		; For best performance
;		BRB	ios_not_stopped
;
;	   10$:	PUSHL ucb
;		CALLS #1,STOP_NEW_IO
;		BLBS  R0,ios_not_stopped
;		;
;		; Other blocked I/O handling (if any)
;		;
;
;	Input:	UCB (val) - Base address of the UCB
;
;	Output:	R0 - STATUS	SS$_NORMAL - Requests Not Blocked
;				SS$_TOOMUCHDATA - Requests Blocked
;
;		UCB$R_BUSY_BIT_IRP may be inserted at the head of
;		UCB$L_IOQFL.
;
;		All other registers are preserved.
;--

UCB$M_CB_UL_BUSY = <UCB$M_CB_MNTVERIP>

STOP_NEW_IO:					;FC FF; Block Unwanted I/Os
	.CALL_ENTRY PRESERVE=<R5,R8>

	$ARG_DEF <ucb>				;FC FF; UCB Address

	MOVL	ucb(AP), R5			;FC FF; Get current UCB
	MOVL	#SS$_NORMAL, R0			;FC FF; Set preferred status

	; Begin with CLASS_BUSY bit tests.

	TST_SINGLE	10$,flag_reg=R8		;FC FF; Any class busy bits set?
	BRB	90$				;FC FF; No busy bits set.
10$:	BICL	#UCB$M_CB_UL_BUSY, R8		;FC FF; Clear UL_BUSY bits.
	BNEQ	70$				;FC FF; Any other busy bits set?

	; At this point, we know that only UL_BUSY bits are set.
	; It's time to check UCB$L_QUEUED_IO_COUNT.

	TSTL	UCB$L_QUEUED_IO_COUNT(R5)	;FC FF; I/O still in port?
	BEQL	90$				;FC FF; No, let I/Os pass.

	; At this point, we know new I/O requests must be blocked.
	; If the "golden" IRP is not already queued, then queue it.

70$:	TSTL	UCB$R_BUSY_BIT_IRP(R5)		;FC FF; Is golden IRP queued
	BEQL	75$				;FC FF; No, go queue it
	MOVAL	UCB$L_IOQFL(R5),R0		;F FF; Get the correct back pointer
	CMPL	UCB$R_BUSY_BIT_IRP+4(R5),R0	;F FF; Is "golden" IRP first in Q?
	BEQL	77$				;F FF; Yes, leave it there.
	;
	; OK!  The "golden" IRP is buried in the I/O request queue.
	; Remove it (then reinsert it at the beginning).
	;
	REMQUE	UCB$R_BUSY_BIT_IRP(R5), R0	;F FF; Remque the "golden" IRP
	TRACE_SINGLE 8101			;F FF; Trace golden IRP lost
75$:	INSQUE	UCB$R_BUSY_BIT_IRP(R5),-	;FC FF; Queue BUSY_BIT_IRP to
		UCB$L_IOQFL(R5)			;FC FF; head of UCB I/O queue.
77$:	MOVL	#SS$_TOOMUCHDATA, R0		;FC FF; Set blocked status
	TRACE_SINGLE 1111			;FC FF; Trace golden IRP insert

	; All done!

90$:	RET

	.PAGE
	.SBTTL	DK_STARTIO - The Start I/O Routine for KP Drivers

;++
; DK_STARTIO
;
;	Drivers using KP services in their start I/O thread replace the
; DPT start I/O routine pointer with a pointer to this routine.  This routine
; then starts a kernel process using the contents of DPT$PS_KP_STARTIO, 
; which the driver setups up as the top-level routine for the thread.
;
; Context:
;
;	Fork thread
;	IPL = Fork
;	Fork lock held
;
; Calling sequence:
;
;	Standard step 2 driver start I/O interface:
;
;		KP_STARTIO(IRP,UCB)
;
;	Input:	IRP (val) - Base address of the IRP
;		UCB (val) - Base address of the UCB
;
;	Output:	none to the caller
;		but the I/O thread is started
;--
DK_STARTIO::					;F FF; Start I/O Routine for KP Drivers
	$DRIVER_START_ENTRY  PRESERVE = <R2,R3,R4,R5,R6> FETCH=YES

	$ARG_DEF <irp,-				;F FF; IRP Address
		  ucb>				;F FF; UCB address
		  
	ONCE_DK_DEBUG DK_STARTIO

;
;	Get input parameters
;

	MOVL	irp(AP), R3			;F FF; Get current IRP
	MOVL	ucb(AP), R5			;F FF; Get current UCB
	MOVAL	UCB$R_BUSY_BIT_IRP(R5), R6	;F FF; Get address of BUSY_BIT_IRP

;	Determine if the IRP just dequeue is the BUSY BIT IRP.

10$:	CMPL	R3, R6 				;F FF; Is the current IRP, our BUSY_BIT_IRP?
	BEQL	100$				;F FF; Yes, do special 'busy-bit' processing

;	Verify that the driver is ready for another I/O request.
;	If the driver is busy, then just requeue this I/O
;	request to the head of the I/O queue

20$:	MOVL	UCB$L_STS(R5), R1		;F FF; Get current I/O status
	TST_SINGLE	80$,flag_reg=r0		;F FF; Branch if there any BUSY Bits sets

;	The class driver can accept this I/O request for processing.
;	Handle Mount Verification in progress, but not seen

	.BRANCH_LIKELY				;F FF;
	BBC	#UCB$V_MNTVERIP, R1, 30$	;F FF; Branch if mount ver not active
	BBS	#IRP$V_SHDIO,IRP$L_STS2(R3),30$	;F FF; Branch if this is a shadowing I/O

;	At this point, we have found that mount verification is in progress but our
;	class driver CLASS_BUSY flags don't show mount verification in progress.  This
;	must have occured because mount verification was started when there were no
;	I/O's active.  That most likely happened in a cluster state transition, but
;	we don't really care about the reason.  What we MUST do is get the CLASS_BUSY
;	flag set for mount verification in progress.  After that, we can assume that
;	the I/O we have in hand is a mount verification IRP.  So, it needs to be 
;	processed in the usual way for MVIRPs.
;
	SET_SINGLE	MNTVERIP,R5		;F FF; Single thread MOUNT Verification
	BRB	40$				;F FF; Continue without clearing BUSY

30$:	BICL	#UCB$M_BSY, R1			;F FF; Clear busy
40$:	BICL	#UCB$M_CANCEL!UCB$M_TIMOUT,R1	;F FF; Clear cancel, timout
	MOVL	R1, UCB$L_STS(R5)		;F FF; Set current device status

	PUSHAL	DK_STARTIO_PAUSE		;F FF; Address of pause routine
	PUSHL	R5				;F FF; UCB start I/O parameter
	PUSHL	R3				;F FF; IRP start I/O parameter
	CALLS	#3,G^EXE_STD$KP_STARTIO		;F FF; Start the I/O request.

;	This I/O request has been started and has stalled.
;	Dequeue another I/O and attempt to start it
;

50$:	REMQUE	@UCB$L_IOQFL(R5), R3		;F FF; Any I/O queued to this device?
	BVC	70$				;F FF; Yes, process I/O
	TST_SINGLE	60$			;F FF; Are there any other reasons not to clear busy?
	BICL	#UCB$M_BSY, UCB$L_STS(R5)	;S FF; Clear UCB busy bit
60$:	RET					;F FF;

;	Parts of the following code segement is lifted from [SYS]IOSUBNPAG.MAR IOC_STD$INITIATE:
;	This was done to to prevent a recusive call to IOC$INITIATE at this call level.
;
70$:	ASSUME	DDT$PS_START_2 NE 0

	.IF DF	CA$_MEASURE_IOT
	BSBW	PMS$START_IO			;F FF; Insert start of I/O transaction message
	.ENDC
 
;	Determine if diagnostic buffer needs to be setup
;
	.BRANCH_LIKELY
	BBC	#IRP$V_DIAGBUF,IRP$L_STS(R3),10$;F FF; If clear, no diagnostic buffer
	MOVL	@IRP$L_DIAGBUF(R3),R0		;F FF; Get address of diagnostic buffer data area
	.Disable Flagging			; Known quadword access
	MOVQ	G^EXE$GQ_SYSTIME,(R0) 		;F FF; Insert I/O operation start time
	.Enable Flagging
	BRB	10$

;	The driver is in single-threaded mode.
;	There are one or more class-busy bits set and thus the UCB$V_BSY is set.
;	If Mount Verification is in progress and this is a MVIRP and there are
;	no other reasons for blocking new I/O requests, the send the IRP to
;	the real (KP) start I/O routine.  Otherwise, gum-up the UCB pending
;	I/O queue and blast the current request directly back to mount
;	verification.
;
;	NOTE: (Care was taken to place UCB$L_STS(R5) in  R1

80$:	BBC	#UCB$V_MNTVERIP,R1,85$		;F FF; If Mount Verification in progress
	BBC	#IRP$V_MVIRP,IRP$L_STS(R3),85$	;F FF; and Mount Verification IRP
	PUSHL	R5				;F FF; P1 = UCB address
	CALLS	#1,STOP_NEW_IO			;F FF; Are new I/Os blocked?
	BLBS	R0,40$				;F FF; No, start the MVIRP.
	CLRL	R1				;S FF; Clear transfer byte count
	MOVL	#SS$_PARITY,R0			;F FF; Can't do MVIRP now.
	MOVL	R3,UCB$L_IRP(R5)                ;S FF; Set up UCB$L_IRP for the actual
						;S FF; IRP that's completing!
	CALL_REQCOM				;F FF; let MV handle retry
	CLRL	UCB$L_IRP(R5)			;F FF; This I/O has been processed.
	BRB	90$				;F FF; Nothing else to do

;	Non-Mount Verification I/O, just requeue and it will be retried later
;
85$:	INSQUE	IRP$L_IOQFL(R3),UCB$L_IOQFL(R5)	;F FF; else Queue IRP to head of queue
	CLRL	UCB$L_IRP(R5)			;F FF; No "current" request is pending.

;	The driver needs to run in single threaded mode for some reason.
;	This reason is enumerated in UCB$L_CLASS_BUSY.	No matter what
;	the reason, we should make the attempt to prevent further I/O
;	requests from starting until the driver is not busy.

90$:	BISL	#UCB$M_BSY, UCB$L_STS(R5)	;F FF; Set UCB busy bit
	RET

;	This IRP is the BUSY_BIT_IRP.
;	Test the CLASS_BUSY bits to determine if:
;	--- DK_STARTIO just exits leaving UCB$V_BSY set
;	--- An attempt is made to dequeue another I/O.
;	In both cases the current BUSY_BIT_IRP is ingored and left dequeued.
;
100$:	CLRL	UCB$R_BUSY_BIT_IRP(R5)		;F FF; Clear golden IRP usage flag
	TST_SINGLE	90$			;F FF; If busy bits set, exit setting busy
	BRB	50$				;F FF; No busy bits, attempt another I/O


;
; If EXE_STD$DK_STARTIO can't start the KPB requested, it will callback this routine
; to tell DKDRIVER, that the I/O is being stalled and once again when the I/O is
; being unstalled.
;
DK_STARTIO_PAUSE::
	.CALL_ENTRY INPUT=<>,OUTPUT=<R0>,-
		    SCRATCH=<R0,R1>, PRESERVE=<R3,R5>
	$ARG_DEF <ucb,- 			;F FF;	UCB Address
		  irp,-                         ;F FF;	IRP Address
		  flag>				;F FF;	Flag (1=stall, 0=unstall)

	MOVL	ucb(AP), R5			;F FF; Get UCB Address
	TSTL	flag(AP)			;F FF; Is this a stall call?
	BEQL	10$				;F FF; If zero, than unstall
	SET_SINGLE	KP_STARTIO,R5,30$	;F FF; Single thread KP_STARTIO stall
	BRB	20$				;F FF; Return
10$:	CLR_SINGLE	KP_STARTIO,R5,30$	;F FF; Single thread KP_STARTIO unstall
20$:	RET
30$:	BUG_CHECK INCONSTATE,FATAL		;F FF; bugcheck

	.PAGE
	.SBTTL	DK_KP_STARTIO	- Driver QIO entry point
;+
; DK_KP_STARTIO
;
; This routine is the QIO entry point into the driver. Its main function
; is to dispatch to the function-code-specific routine which then executes
; the QIO.  The routine runs as a kernel process, which terminates through
; a call to the KP_REQCOM macro.
;
; Context:                          
;
;	Class Driver thread
;	IPL = Fork
;	Fork lock held
;
; Inputs:
;
;	KPB(AP)	- KPB address
;
; Outputs:
;
;	R0	- 1st longword of I/O status: contains status code and
;		  number of bytes transferred
;	R1	- 2nd longword of I/O status: l.o. word contains h.o.
;		  word of number of bytes transferred
;	R2-R8	- Preserved
;-

DK_KP_STARTIO::					;C FF; Driver QIO entry point

	; Compiler uses R13-R15 for RET-under-JSB calls
	;
	.CALL_ENTRY <R2,R3,R4,R5,R6,R7,R8,R13,R14,R15>

	$ARG_DEF <kpb>				;C FF; KPB address

	DK_DEBUG DK_KP_STARTIO

	MOVL	kpb(AP),R0			;C FF; Get KPB address
	MOVL	KPB$PS_UCB(R0),R5		;C FF; Get UCB address
	MOVL	KPB$PS_IRP(R0),R3		;C FF; Get IRP address
	MOVL	UCB$L_PDT(R5),R4		;C FF; Get PDT address
	MOVL	R3,R2				;C FF; Copy IRP address
	MOVL	R5,R3				;C FF; Copy UCB address
	DK_ALLOC_SCDRP				;C FF; Allocate an SCDRP
	MOVL	R2,SCDRP$L_IRP(R5)		;S FF; Save IRP address in SCDRP
	CLRL	UCB$L_IRP(R3)			;S FF; Multiple I/Os are now possible.

	.IF DEFINED TRACE
	BSBW	TRACE_QIO			;C FF; Trace the current I/O request
	.ENDC

	INCL	UCB$L_QUEUED_IO_COUNT(R3)	;S FF; Account for this I/O request.
	INCL	UCB$L_OTHER_COUNT(R3)		;S FF; Assume not a read/write for now

	ASSUME	IRP$L_IOQBL EQ 4+IRP$L_IOQFL

	INSQUE	IRP$L_IOQFL(R2),-		;S FF; Remember this IRP is active.
		@4+UCB$Q_IRP_LIST(R3)		;S FF;
	MOVL	IRP$PS_KPB(R2),-		;S FF; Save KPB address in SCDRP
		SCDRP$PS_KPB(R5)

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

10$:	EXTZV	#IRP$V_FCODE,#IRP$S_FCODE,-	;S FF; Extract I/O function code
		IRP$L_FUNC(R2),R1		;S FF;

	ASSUME	IRP$S_FCODE LE 7		;S FF; Allow byte mode dispatch
	DISPATCH R1,TYPE=B,<-			;S FF; Dispatch according to function
		<IO$_NOP,	IO_NOP>,-	;S FF; No operation
		<IO$_UNLOAD,	IO_UNLOAD>,-	;S FF; Unload drive
		<IO$_SEEK,	IO_SEEK>,-	;S FF; Seek cylinder
		<IO$_RECAL,	IO_RECAL>,-	;S FF; Recalibrate drive
		<IO$_PACKACK,	IO_PACKACK>,-	;S FF; Pack acknowledge
		<IO$_WRITECHECK,IO_WRITECHECK>,-;S FF; Write check data
		<IO$_WRITEPBLK,	IO_WRITEPBLK>,-	;S FF; Write physical block
		<IO$_READPBLK,	IO_READPBLK>,-	;S FF; Read physical block
		<IO$_WRITEHEAD,	IO_WRITEHEAD>,-	;S FF; Write header and data
		<IO$_READHEAD,	IO_READHEAD>,-	;S FF; Read header and data
		<IO$_AVAILABLE,	IO_AVAILABLE>,-	;S FF; Available (disk and tape class)
		<IO$_DSE,	IO_DSE>,-	;S FF; Data security erase (and rewind)
		<IO$_DIAGNOSE,	IO_DIAGNOSE>,-	;S FF; Diagnose
		<IO$_READLBLK,	IO_READLBLK>,-	;S FF; Read logical block
		<IO$_FORMAT,	IO_FORMAT>,-	;S FF; Format
		<IO$_AUDIO,	IO_AUDIO>>	;S FF; Cd-rom audio

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

IO_BOGUS::	.GLOBAL_LABEL
IO_SEEK::	.GLOBAL_LABEL
IO_RECAL::	.GLOBAL_LABEL
IO_WRITEHEAD::	.GLOBAL_LABEL
IO_READHEAD::	.GLOBAL_LABEL
IO_READLBLK::	.GLOBAL_LABEL

	DK_DEBUG ILLEGAL_IO

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

;+
; The volume was not software enabled, however audio functions don't
; require that the volume (disk)  be software enabled. If the I/O function is
; an audio function then goto the audio fdt code.
;-
VOLUME_INVALID2:: .GLOBAL_LABEL

	EXTZV	#IRP$V_FCODE,#IRP$S_FCODE,-	;S FF; Extract I/O function code
		IRP$L_FUNC(R2),R1		;S FF;
	ASSUME	IRP$S_FCODE LE 7		;S FF; Allow byte mode dispatch
	CMPB	#IO$_AUDIO,R1			;S FF; Is this an AUDIO function?
	BEQL	IO_AUDIO			;S FF; Yes, do audio function

VOLUME_INVALID:: .GLOBAL_LABEL

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

;
; R0 = QIO status
;
COMPLETE_IO::	.GLOBAL_LABEL

	ASSUME	IRP$L_IOQFL EQ 0
	ASSUME	IRP$L_IOQBL EQ 4+IRP$L_IOQFL

	DECL	UCB$L_QUEUED_IO_COUNT(R3)	;S FF; Adjust I/O count
	BGEQ	1$				;S FF
	BUG_CHECK INCONSTATE,FATAL		;S FF; Somethings wrong if this is neg!
1$:	REMQUE	@SCDRP$L_IRP(R5),UCB$L_IRP(R3)	;S FF; Set up UCB$L_IRP for the actual
						;S FF; IRP that's completing!
	.IF DEFINED TRACE
	BSBW	TRACE_QIO_STAT			;C FF; Save the final QIO status in trace buf
	BLBS	R0,5$				;C FF; Branch on success status
	NOP					;C FF; Instruction to trap on QIO with bad status
5$:
	.ENDC
	DK_DEALLOC_SCDRP CLRSTK=NO		;S FF; Deactivate the SCDRP
	MOVL	R3,R5				;C FF; Copy UCB address
	.BRANCH_LIKELY
	BLBS	R0,10$				;C FF; Success, continue

	CLRL	R1				;C FF; Clear transfer byte count
	CMPL	R0,#SS$_RECOVERR		;C FF; Recoverable error status?
	BEQL	100$				;C FF; Branch if so,
	CMPL	R0,#SS$_TIMEOUT			;C FF; Timeout error status?
	BEQL	110$				;C FF; Branch if so,
	CMPL	R0,#SS$_DRVERR			;C FF; Drive error status?
	BEQL	120$				;C FF; Branch if so,
	CMPL	R0,#SS$_CTRLERR			;C FF; Controller error status?
	BEQL	120$				;C FF; Branch if so,
	CMPL	R0,#SS$_DEVOFFLINE		;C FF; Offline error status?
	BEQL	120$				;C FF; Branch if so,
	BRW	10$				;S FF; Fall through 

; In order to allow the lower layers to distinguish between recoverable and
; non-recoverable errors, a new status code was invented called SS$_RECOVERR.
; This status code should never make it up to this level unless the target
; returns a recoverable error sense key for a SCSI command from which we don't
; expect it. To prevent us from possibly returning a bogus status code for the
; QIO, translate this status to one that VMS knows about, namely, SS$_PARITY.

100$:	MOVL	#SS$_PARITY,R0			;C FF; Change to a valid error status
	BRW	10$

; Timeouts may have occurred that are merely the result of a bus reset (as
; frequently happens in multi-host configurations).  Rather than failing
; this on the spot, convert the error to SS$_MEDOFL to allow retries.

110$:	MOVL	#SS$_MEDOFL,R0			;C FF; Change to MEDOFL
	BRW	10$

; For various other errors, force the driver into mount verify exactly
; once in order to give it a second chance.  Set IRP$V_FORCEMV to indicate
; we are forcing mount verification.

120$:   MOVL	UCB$L_IRP(R5),R2		;C FF; Get IRP address
	BBSS	#IRP$V_FORCEMV,-
		IRP$L_STS2(R2),10$  		;C FF; Already MV'd once before,
						;C FF;  so no more allowed.
	MOVL	#SS$_MEDOFL,R0			;C FF; Convert to MEDOFL in order
						;C FF;  to force Mount Ver.

; If there are two or more IRPs in the pending queue, attempt to reorder
; the queue such that the IRP for the block closest to the current head
; position in the direction of the current head motion is executed first.
;
; Note: Don't perform re-order on devices with tagged queue support
;
10$:	IF_CMDQ	20$,UCB=R5			;C FF; Skip reordering for CMDQ devices
	MOVAL	UCB$L_IOQFL(R5),R2		;C FF; Get address of I/O pending queue in UCB
	CMPL	@(R2),R2			;C FF; Fewer than two pending IRPs?
	BEQL	20$				;C FF; Branch if so, no need to reorder
	PUSHR	#^M<R0,R1>			;C FF; Save I/O status
	BSBW	ATTEMPT_REORDER			;C FF; Attempt to reorder read/write request
	POPR	#^M<R0,R1>			;C FF; Restore I/O status
20$:	TST_SINGLE	40$,flag_reg=R2		;C FF; Are any class driver busy bits set?
30$:	KP_REQCOM				;C FF; Complete the I/O, implied return

; By design, CALL_REQCOM will clear UCB$V_BSY if there are no I/O's present 
; on the UCB I/O Pending Queue.  Since the busy bit is referenced by various
; OpenVMS subsystems, DKDRIVER cannot let the busy bit to be cleared if in
; fact it is still busy.  STOP_NEW_IO manages this.
;
40$:	PUSHR	#^M<R0,R1>			;C FF; Save I/O status
	PUSHL	UCB$L_IRP(R5)			;C FF; Save current IRP
	CMPL	UCB$IS_DRAIN_COUNT(R5),-	;C FF; Are there only draining I/O's left?
		UCB$L_QUEUED_IO_COUNT(R5)	;C FF;
	BLSS	50$				;C FF; If less, then I/O's still outstanding
	BNEQ	60$				;C FF: If not equal, too many I/O's, BUG_CHECK
45$:	REMQUE	@UCB$Q_DRAIN_LIST(R5), R1	;C FF; Dequeue entry from I/O drain list
	BVS	50$				;C FF; Branch if list is empty
	MOVAL	-SCDRP$PS_PQFL(R1), R1		;S FF; Get starting address of SCDRP
	MOVL	SCDRP$PS_KPB(R1), R1		;S FF; Get KPB address
	PUSHL	#SS$_NORMAL			;S FF; Return SS$_NORMAL
	PUSHL	R1				;S FF; KPB to restart
	CALLS	#2,EXE$KP_RESTART		;S FF; restart this kernel process
50$:	PUSHL	R5				;C FF; P1 = UCB address
	CALLS	#1,STOP_NEW_IO			;C FF; Call STOP_NEW_IO
	POPL	UCB$L_IRP(R5)			;C FF; Restore current IRP
	POPR	#^M<R0,R1>			;C FF; Restore I/O status
	BRB	30$				;C FF; Complete current I/O
60$:	BUG_CHECK INCONSTATE,FATAL		;S FF; bugcheck

        .PAGE
        .SBTTL  DK_ALTSTARTIO - The Alternate Start I/O Routine

;++
; DK_ALTSTARTIO
;
; Implementation of an alternate startio routine grows out of the
; following problem:
;
; Two-node SCSI clusters require a quorum disk for high availability
; operation.  The quorum disk must be located on the SCSI bus shared by the
; two OpenVMS hosts.  Successful counting of the quorum disk vote requires
; that quorum I/O reads and writes complete in a timely manner, even when
; other I/O operations normally are blocked (such as while the disk
; is undergoing mount verification).
;
; DKDRIVER must provide a mechanism for expeditious processing of quorum 
; I/O requests.  This mechanism must not block quorum I/O operations unless 
; it is absolutely necessary to do so.
;
; The OpenVMS driver interface already defines a driver routine that is 
; used to start I/O requests while bypassing the UCB$V_BSY bit and the 
; pending I/O queue.  That interface is the alternate start I/O routine.  
; The alternate start I/O routine is exactly designed to meet the needs of 
; the quorum I/O requests.  Therefore, the code issuing quorum I/O requests 
; has been modified to use EXE$ALTQUEPKT for drivers that have an alternate
; start I/O routine and DKDRIVER has been enhanced to have the
; alternate start I/O routine below.
;
; The operational details of DKDRIVER require that some restrictions be
; enforced in the alternate start I/O routine.  There are two classes of
; restrictions:  those that will never change for any given device;  and
; those that may come and go during some time intervals.  The former will
; result in termination of the request with a failure status;  the latter
; will be retried.
;
; The failure status for restrictions that will never change is
; SS$_ILLIOFUNC.  These restrictions are as follows.
;
; Tagged command queuing is required for expeditious handling of I/O opera-
; tions within a SCSI device.  Some SCSI devics (or SCSI bus ports) do not
; support tagged command queuing.  Expeditious request handling is not
; possible on these devices, or on any devices connects to a non tagged
; command queuing port.  All IRPs received by the alternate start I/O 
; routine for non tagged command queuing devices shall be terminated without 
; execution.  This is not an issue for quorum I/O requests because all shared 
; SCSI busses and devices are required to support tagged command queuing.
;
; Only reads and writes are supported.  It is very difficult for
; DKDRIVER to provide expeditious handling of functions other than reads
; and writes.  Any I/O function other than IO$_READLBLK, IO$_READPBLK,
; IO$_WRITELBLK, and IO$_WRITEPBLK shall not be performed.  Clearly, this
; is not a problem for quorum I/O, which are exclusively reads or writes.
;
; DKDRIVER cannot provide expeditious handling for the IO$M_DATACHECK
; modifier in particular and most read/write function modifiers in general.
; Therefore, any IRP with function modifiers set shall not be performed.
; Hopefully, this is not a problem for quorum I/Os.
;
; The following conditions may be resolved in a short time interval and 
; will result in a retry of the operation:
;
;           o one or more IRPs are waiting for outstanding I/O to drain
;           o unit initialization in progress
;           o SCSI bus reset processing is in progress
;           o an I/O is stalled due to:
;             - inability to allocate a KPB
;             - lack of send credits
;             - lack of queue tag values
;           o one of the following I/O functions is being processed
;             - IO$_UNLOAD
;             - IO$_AVAILABLE
;             - IO$_FORMAT
;
; DKDRIVER must never set the UCB$V_ALTBSY bit.  Setting this bit would 
; cause EXE$ALTQUEPKT to queue the IRPs that it receives in an alternate 
; pending I/O queue.  Such IRP queuing violates the design guarantees 
; defined for the DKDRIVER alternate start I/O routine.
;
; Context:
;
;       Fork thread
;       IPL = Fork
;       Fork lock held
;
; Calling sequence:
;
;       Standard step 2 driver alternate start I/O interface:
;
;               KP_STARTIO(IRP,UCB)
;
;       Input:  IRP (val) - Base address of the IRP
;               UCB (val) - Base address of the UCB
;
;       Output: the I/O thread is started, or
;               SS$_UNSAFE is returned in IRP$L_IOST1
;
;                      **************************
;                         Implementation Notes
;                       ************************
;
; The current issuer of quorum IO request is a system routine which only 
; calls EXE$ALTQUEPKT with "perfect request" which satisfies all static
; checking listed above.  This fact eliminates the need of a DKDRIVER FDT 
; routine for checking of static conditions, hence EXE$ALTQUEPKT is called
; by the Quorum IO issuer directly in the current implementation.  
;
; The redundant inclusion of static checking of IRP & UCB here is for the 
; completeness of Quorum io processing in case *OTHER* Quorum io issuer 
; may enter this routine without its IRP & UCB checked out beforehand.  
;
; If in the future, there is new caller to this ALTSTARTIO routine for 
; processing other than Quorum IO and it requires its own set of IRP & UCB 
; checking, then all the existing and necessary static checking code here 
; should be moved to FDT routines where they belong and to leave this 
; routine as simple and generic as it should be to handle the starting of 
; IOs only. [ This implies future Quorum IO call interace change.]
;       ************************************************************
;
;--
DK_ALTSTARTIO::                         ;F FF; Alternate Start I/O Rtn
        $DRIVER_ALTSTART_ENTRY  PRESERVE = <R3,R4,R5>

        $ARG_DEF <irp,-                         ;F FF; IRP Address
                  ucb>                          ;F FF; UCB address

        MOVL    irp(AP),R3                      ;F FF; Get current IRP
        MOVL    ucb(AP),R5                      ;F FF; Get current UCB
                                                ;   (an arbitrary choice)
; static UCB checking

        IF_NOT_CMDQ 20$,UCB=R5                  ;F FF; If not TCQ, return

; static IRP checking
                                                ;   with SS$_ILLIOFUNC
	EXTZV   #IRP$V_FCODE,#IRP$S_FCODE,-     ;F FF; Extract I/O func code
                IRP$L_FUNC(R3),R1               ;
        ASSUME  IRP$S_FCODE LE 7                ;F FF; Allow byte mode dispatch
        DISPATCH R1,TYPE=B,<-                   ;F FF; Dispatch via function
                <IO$_WRITEPBLK, 10$>,-          ;   Write physical block
                <IO$_WRITELBLK, 10$>,-          ;   Write logical block
                <IO$_READPBLK,  10$>,-          ;   Read physical block
                <IO$_READLBLK,  10$>>           ;   Read logical block

; All other function codes return SS$_ILLIOFUNC.
        BRB     20$                             ;F FF; Go to error exit

10$:    EXTZV   #IRP$V_FMOD,#IRP$S_FMOD,-       ;F FF; Extract I/O modifiers
                IRP$L_FUNC(R3),R1
        BEQL    30$                             ;F FF; If no modifier, continue
20$:    MOVQ    #SS$_ILLIOFUNC,IRP$L_IOST1(R3)  ;F FF; otherwise, error exit
	BRB	70$				;F FF;  

30$:    MOVW    #3,IRP$L_IOST2+2(R3)	        ;F FF; maximum retry twice 

; transient UCB condition checking

40$:    TSTL    UCB$IS_DRAIN_COUNT(R5)          ;F FF; If drain count nonzero,
        BNEQ    60$                             ;   then error exit

; Now test the various relevant class busy bits.
; The CB_NOCMDQ bit need not be tested because it already has been tested
; above.  The following class busy bits need not be tested because only
; tagged command queuing devices are supported in the alternate start I/O
; routine and because tagged command queuing devices are required to do
; their own bad block replacement: CB_SINGLE_RW, CB_BBR_IN_PROG, and CB_
; FORCE_ERROR.  The following class busy bits need not be tested because a
; tagged command queuing device can process the condition that they reflect
; and still accept another read or write: CB_MNTVERIP, CB_CHECK_CONDITION,
; CB_QUEUE_FULL_EVT, CB_SINGLE_DC, CB_SINGLE_DSE, CB_PACKACK, CB_NOP, and
; CB_AUDIO.  The CB_BUSY bit is not listed above because there is no 
; apparent instance where it is used.

        BITL    #<UCB$M_CB_INIT!-               ;F FF; Unit init in progress
                UCB$M_CB_KP_STARTIO!-		;  unable to allocate a KPB
                UCB$M_CB_BUS_RESET!-		;  SCSI bus reset in progress
                UCB$M_CB_NO_SEND_CREDITS!-	;  stall on lack of Send Credit
                UCB$M_CB_NO_CMD_BITS!-		;  Stall on lack of Que tag value
                UCB$M_CB_UNLOAD!-		;  IO$_UNLOAD in progress
                UCB$M_CB_AVAILABLE!-		;  IO$_AVAILABLE in progress
                UCB$M_CB_FORMAT>,-		;  IO$_FORMAT in progress
                UCB$L_CLASS_BUSY(R5)		;  Test class busy bits
        BNEQ    50$                             ;F FF; Branch if any set

; If the DKDRIVER alternate start I/O routine finds that the IRP passed to 
; it can be processed, it simply calls EXE$KP_STARTIO.  This starts KP 
; processing of the request in exactly the same way that a similar call in 
; DK_START-IO starts normally queued requests.  However, the DKDRIVER 
; alternate start I/O routine does not contain a loop that looks for 
; additional requests to ; start (such as is found in DK_STARTIO).  The 
; alternate start I/O routine is a one-shot (do this one I/O and quit) routine.

        PUSHAL  DK_STARTIO_PAUSE                ;F FF; Address of pause routine
        PUSHL   R5                              ;F FF; UCB start I/O parameter
        PUSHL   R3                              ;F FF; IRP start I/O parameter
        CALLS   #3,G^EXE_STD$KP_STARTIO         ;F FF; Start the I/O request.

; Typically, alternate start I/O routines complete I/O requests using
; the COM$POST routine.  DKDRIVER need not do this.  For DKDRIVER, the only
; difference between an IRP that passes through the alternate start I/O
; routine and a normally queued IRP is the path by which that IRP got to
; EXE$KP_STARTIO.  After that, the existing (normal) DKDRIVER request queue
; and busy bit management all apply.  Therefore, DKDRIVER completes all
; IRPs in the same way, using REQCOM without regard for the source (start
; I/O or alternate start I/O) that started the IRPs KP thread.

        BRB     80$                             ;F FF; and return

50$:    DECW    IRP$L_IOST2+2(R3)               ;F FF; Any more retry?
        BEQL    60$                             ;F FF; No, both retries failed
        MOVL    R5,R4                           ;F FF; Save UCB address
        MOVAB   IRP$L_FQFL(R3),R5               ;F FF; Get address of fork blk
        MOVB    UCB$B_FLCK(R4),FKB$B_FLCK(R5)   ;F FF; Copy forklock index
        FORK_WAIT ENVIRONMENT=CALL              ;F FF; Go wait for 1 sec.
        MOVL    R4,R5                           ;F FF; Restore UCB address
        BRW     40$                             ;F FF; Go to top of retry loop

60$:    MOVQ    #SS$_UNSAFE,IRP$L_IOST1(R3)     ;F FF; Store I/O status code, 
                                                ;  Clear rest of I/O status block.
70$:    CALL_POST SAVE_R1=NO                    ;F FF; COM$POST the request 
80$:    RET                                     ;F FF; Return

	.PAGE
	.SBTTL	DK_CANCEL	- Cancel I/O routine
;+
; DK_CANCEL
;
; This routine cancels an I/O in progress. Since the timeouts for disks are
; relatively short, no device-dependent action is taken. Instead, it relies
; on the port driver's relatively short timeout to terminate any I/O in
; progress.
;
; Context:
;
;	Fork thread
;	IPL = Fork
;	Fork lock held
;
; Step 2 Interface
;
;	void CANCEL (channel , IRP *, PCB *, UCB *, reason)
;
; Obsolete Step 1 Inputs:
;
;	R2	- channel index number
;	R3	- address of the current IRP (I/O request packet)
;	R4	- address of the PCB (process control block) for the
;		  process canceling I/O
;	R5	- address of the UCB (unit control block)
;	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
;			CAN$C_MSCPSERVER  if called through MSCP server
; Obsolete Step 1 Outputs:
;
;	R0,R1	- Destroyed
;	All other registers preserved
;-

DK_CANCEL::					;F FF; Cancel an I/O operation
	$DRIVER_CANCEL_ENTRY PRESERVE = <R2,R3,R4,R5,R6,R7,R8>

	ONCE_DK_DEBUG DK_CANCEL

	CMPL	R8,#CAN$C_MSCPSERVER		;F FF; MSCP server cancel?
	BEQL	400$				;F FF; Branch if yes

; N.B. Determine if IRP equals 0, this can happen during UNIT INIT
; in bringing the  device online
;
	TSTL	R3				;F FF; IRP address of 0?
	BEQL	10$				;F FF; Branch if not

	CALL_CANCELIO SAVE_R0R1=NO		;F FF; Set cancel bit if appropriate.
10$:	RET					;F FF; Return

; MSCP server has requested cancel of all its I/O's.
; Find them, and mark them so that they will make a 
; speedy exit before their next attempt to send a 
; command to the port.

	ASSUME	IRP$L_IOQFL EQ 0

400$:	MOVAB	UCB$Q_IRP_LIST(R5),R6		;F FF Get know IRPs listhead
	MOVL	R6,R7                           ;F FF Save it
                                                ;F FF
440$:	MOVL	IRP$L_IOQFL(R6),R6              ;F FF Get next known IRP
	CMPL	R6,R7                           ;F FF Back to listhead?
	BEQL	480$                            ;F FF Branch if yes
	                                        ;F FF
	CMPL	R2,IRP$L_CHAN(R6)               ;F FF Is this a server IRP?
	BNEQ	440$                            ;F FF Branch if no
	CMPL	R4,IRP$L_PID(R6)                ;F FF Is this a server IRP?
	BNEQ	440$                            ;F FF Branch if no
                                                ;F FF
	BISL	#IRP$M_SRV_ABORT,IRP$L_STS(R6)  ;F FF Mark server's IRP for
                                                ;F FF speedy exit
	BRW	440$                            ;F FF Look for more
                                                ;F FF
480$:	RET                                     ;F FF Server cancel done, exit

	.PAGE
	.SBTTL	DK_REG_DUMP	- Device register dump routine
;+
; DK_REG_DUMP
;
; This routine dumps device-specific infromation into an errorlog packet.
; The format of this information is as follows:
;
;	+-----------------------+
;	|	Longword count	| 4 bytes	; ERR$L_LW_CNT
;	+-----------------------+
;	|	Revision	| 1 byte	; ERR$B_REVISION
;	+-----------------------+
;	|	HW revision	| 4 bytes	; ERR$L_HW_REV
;	+-----------------------+
;	|	Error Type	| 1 byte	; ERR$B_TYPE
;	+-----------------------+
;	|	SCSI ID		| 1 byte	; ERR$B_SCSI_ID
;	+-----------------------+
;	|	SCSI LUN	| 1 byte	; ERR$B_SCSI_LUN
;	+-----------------------+
;	|	SCSI SUBLUN	| 1 byte	; ERR$B_SCSI_SUBLUN
;	+-----------------------+
;	|	Port status	| 4 bytes	; ERR$L_PORT_STATUS
;	+-----------------------+
;	|	SCSI CMD	| n bytes	; ERR$A_CMD_LEN
;	+-----------------------+
;	|	SCSI STS	| 1 byte	; ERR$B_SCSI_STS
;	+-----------------------+
;	|			|
;	|    Additional Data	| n bytes	; ERR$A_ADD_LEN
;	|			|
;	+-----------------------+
;
; 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.
;
;
; Context
;
;	Same IPL, Fork and Locks as calls to:
;		ERL$DEVICERR, ERL$DEVICTMO, ERL$DEVICEATTN and IOC$DIAGBUFILL
;
; Step 2 Interface
;
;	void REGDUMP  (buffer *, CRAM *, UCB *)
;
; Obsolete Step 1 Inputs:
;
;	R0	- Output buffer address
;	R5	- UCB address
;
; Obsolete Step 1 Outputs:
;
;	R0-R2	- Destroyed
;	All other registers perserved
;-

DK_REG_DUMP::					;? ??; Device register dump routine
	$DRIVER_REGDUMP_ENTRY PRESERVE=<R2,R3,R5>

	PUSHAL	(R0)+				;? ??; Save start of data area
	MOVB	#DK_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$PS_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_REASSIGN_BLOCK,		35$>>

	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
	BRB	36$				;? ??; Branch to bounds-check code

; Special entry point for reassign block error. The additional data length
; is contained in SCDRP$L_BCNT rather than in SCDRP$L_TRANS_CNT.

35$:	MOVL	SCDRP$L_SVA_USER(R1),R2		;? ??; Get address of additional data
	MOVZBL	SCDRP$L_BCNT(R1),R1		;? ??; Get length of additional data

36$:

;	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	37$				;? ??; Branch if so
	MOVL	R3,R1				;? ??; Use all of what's left

37$:	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	DK_DSE		- Data Security Erase FDT Routine
;+
; DK_DSE
;
; This is the FDT routine for the Data Security Erase operation.
; The byte count (P2) is stored in IRP$L_BCNT. The starting logical
; block (P3) is stored in IRP$L_MEDIA. Control is transfered to
; EXE$QIODRVPKT, thus queueing the I/O request to the driver's start
; I/O routine.
;
; Context:
;
;	User thread
;	IPL = ASTDEL
;	Locks - none
;
; STEP 2 INTERFACE:
;
;	int FDT_ROUTINE (IRP *, PCB *, UCB *, CCB *)
;
; IMPLICIT INPUTS:
;	P2	- Byte count
;	P3	- Starting logical block
;
; IMPLICIT OUTPUTS:
;
;	IRP$L_BCNT(R3)  - Byte count
;	IRP$L_MEDIA(R3) - Starting logical block
;
; Obsolete Step 1 Inputs:
;
;	R3	- IRP address
;
;-

DK_DSE::					;U A; Data Security Erase FDT Routine
	$DRIVER_FDT_ENTRY PRESERVE = <R3>

	MOVL	IRP$L_QIO_P2(R3),IRP$L_BCNT(R3)	;U A; Setup erase byte count
	MOVL	IRP$L_QIO_P3(R3),IRP$L_MEDIA(R3);U A; Setup erase starting LBN
	CALL_QIODRVPKT				;U A; Send request to STARTIO and RET

	.PAGE
	.SBTTL	DK_DIAGNOSE	- FDT preprocessing for Special pass-through function
;+
; DK_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
;
; Context:
;
;	User thread
;	IPL = ASTDEL
;	Locks - none
;
; STEP 2 INTERFACE:
;
;	int FDT_ROUTINE (IRP *, PCB *, UCB *, CCB *)
;
; IMPLICIT INPUTS:
;
;	P1,P2	- QIO function parameters P1 and P2 in the IRP
;
; OBSOLETE STEP 1 INPUTS:
;
;	R3	- IRP address
;	R4	- PCB address
;	R5	- UCB address
;	R6	- CCB address
;
; OBSOLETE STEP 1 OUTPUTS:
;
;	R0-R2	- Destroyed
;-

DK_DIAGNOSE::					;U A; Special pass-through function
	$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	DK_SHAD_WRITE - Check write to shadow mbr for priv
;++
;
; DK_SHAD_RWCHECK - Check read/write to shadow mbr for privilege
;
; Functional Description:
;
;	Allow only processes with SYS privilege to perform WRITES to
;	Host Based Shadowing shadow set members.
;
; Context:
;
;	User thread
;	IPL = ASTDEL
;	Locks - none
;
; Step 2 Interface:
;
;	int FDT_ROUTINE (IRP *, PCB *, UCB *, CCB *)
;
; Step 1 Inputs:
;
;	R3	IRP address
;	R5	UCB address (member)
;
; Implicit inputs: None.
;
; Outputs:None.
;
; Implicit outputs: None.
;
; Condition codes:
;
;	SS$_ILLIOFUNC	- I/O directed to shadow set member by a process
;			  that doesn't have sys priv.
;--

DK_SHAD_WRITE::				;U A; Check write to shadow mbr for priv
	$DRIVER_FDT_ENTRY PRESERVE=<R3,R5>

	BBS	#DEV$V_SHD,-			;U A; If this device is a shadow
		UCB$L_DEVCHAR2(R5),10$		;U A;	set member, check
	BRB	30$				;U A; Else, call ACP$WRITEBLK

10$:	MOVL	IRP$L_ARB(R3),R0		;U A; Get ARB address
	BEQL	99$				;U A; If ARB absent, exit

	ASSUME	PRV$V_SYSPRV LT 32
	BBC	#PRV$V_SYSPRV,-			;U A; No SYSPRV, illegal
		ARB$Q_PRIV(R0),99$		;U A;

30$:	PUSHL	R6				;U A: Stack args : CCB
	PUSHL	R5				;U A: UCB
	PUSHL	R4				;U A: PCB
	PUSHL	R3				;U A: IRP
	CALLS	#4, ACP_STD$WRITEBLK		;U A: Call WRITEBLK
	RET					;U A: Continue FDT processing

99$:	MOVZBL	#SS$_ILLIOFUNC,R0		;U A; Set error status
	CALL_FINISHIOC				;U A; Complete I/O request and RET

	.PAGE
	.SBTTL	DK_CRESHAD - CRESHAD FDT routine
	.SBTTL	DK_REMSHAD - REMSHAD FDT routine
;++
;
; DK_CRESHAD - CRESHAD FDT routine
; DK_REMSHAD - REMSHAD FDT routine
;
; Functional Description:
;
;	Dispatch CRESHAD and REMSHAD requests to shadowing driver.
;
; Context:
;
;	User thread
;	IPL = ASTDEL
;	Locks - none
;
; Step 2 Interface:
;
;	int FDT_ROUTINE (IRP *, PCB *, UCB *, CCB *)
;
; Step 1 Inputs:
;
;	R3	IRP address
;	R5	UCB address (member)
;
; Implicit inputs: Dispatch vector filled in.
;
; Outputs:None.
;
; Implicit outputs: None.
;
; Condition codes:
;
;	SS$_ILLIOFUNC	- Dispatch vector not set up.
;--

DK_CRESHAD::
DK_REMSHAD::
	;
 	; Save all registers in case we RET under JSB from a branch
	$DRIVER_FDT_ENTRY FETCH=YES -
		PRESERVE=<R2,R3,R4,R5,R6,R7,R8,R9,-
			  R10,R11,R12,R13,R14,R15>

	MOVL	G^EXE$GL_HBS_PTR,R0		;U A; Shadow Dispatcher
	BGEQ	10$				;U A; Illegal if not filled in
        PUSHL   R6                    		;U A; Push CCB address,
        PUSHL   R5                      	;U A; Push UCB address,
        PUSHL   R4	                     	;U A; Push PCB address,
        PUSHL   R3      	                ;U A; Push IRP address.
        CALLS   #4,(R0)         	        ;U A; Jump to shadow dispatcher
        RET                             	;U A; return to caller.

10$:	MOVZBL	#SS$_ILLIOFUNC,R0		;U A; Set error status
        CALL_FINISHIOC DO_RET=YES       	;U A; Complete I/O request


;+=

	.PAGE
	.SBTTL	DK_AUDIO	- FDT preprocessing for CD-ROM Audio functions
;+
; DK_AUDIO
;
; This routine performs FDT preprocessing for SCSI AUDIO commands.
; The application passes the address of a control structure called the Audio
; Control Block (AUCB) in P1 and the size of the AUCB in P2. In this FDT
; routine the AUCB and any other buffers presented by the users will be locked
; down and double mapped into S0 and P0 space. The AUCB is a control structure
; which passes audio command specific parameters as well as describes users
; buffers that may be used by the driver to return control or CD state
; information (TOC) to the application.
;
; In order to accommodate multiple user buffers being used in a single I/O, one or more 
; IRP extensions (IRPE's) are allocated and linked to the original IRP. The
; original IRP contains the mapping information about the AUCB and the IRPE's are
; used to contain the mapping information for the optional destination and
; sense buffers.
;
; Context:
;
;	User thread
;	IPL = ASTDEL
;	Locks - none
;
; Step 2 Interface:
;
;	int FDT_ROUTINE (IRP *, PCB *, UCB *, CCB *)
;
; Step 1 Inputs:
;
;	R0	- Address of FDT routine
;	R3	- IRP address
;	R4	- PCB address
;	R5	- UCB address
;	R6	- CCB address
;
; Obsolete Step 1 Inputs:
;
;	R7	- Bit number of user-specified I/O function code
;	R8	- Address of current entry in FDT
;
; Outputs:
;
;-

	.ENABLE	LSB
DK_AUDIO::					;U A; CD-ROM Audio functions
	;
 	; Save all registers in case we RET under JSB from a branch
	$DRIVER_FDT_ENTRY PRESERVE=<R2,R3,R4,R5,R6,R7,R8,R9,-
			  R10,R11,R12,R13,R14,R15>

;+
; The AUCB is the Audio Control Block which is constructed by the application and
; passed to the driver during the QIO call. The appication passes arguments
; to the QIO. The first parameter is the address of the AUCB and the second
; parameter is the size of AUCB.
;
; Now check to see that we have read access to the AUCB before attempting to
; access this structure and then verify the version number. The version is
; checked to be sure that it matchs the current code. In the future it may
; become necessary to allow backward compatibility with earlier versions
; of the audio interface within this driver.
;-
	MOVL	IRP$L_QIO_P1(R3),-		;U A; Save user descriptor address
		UCB$PS_AUCB_ADDR(R5)
10$:	BISL	#IRP$M_PHYSIO,IRP$L_STS(R3)	;U A; All audio functions are physical
	CLRL	IRP$L_WIND(R3)			;U A; Clear this since we use if in startio
	CLRL	IRP$L_EXTEND(R3)		;U A; Clear for starters, no IRPE below
	CLRL	IRP$L_SEQNUM(R3)		;U A; Clear for starters, no parent above
	MOVL	IRP$L_QIO_P1(R3),R0		;U A; Get user descriptor address
	MOVL	IRP$L_QIO_P2(R3),R1		;U A; Get user descriptor length
	BSBW	AUDIO_MAP_PAGE			;U A; Locks and Maps AUCB.
	BLBC	R0,AUDIO_EXIT_FDT		;U A; Exit FDT routine on error R0 = error
	CMPL	#CD_AUCB_SIZE,IRP$L_QIO_P2(R3)	;U A; There must be an AUCB
	BGTR	40$				;U A; The AUCB must be at least this big.

;+
; Now that we have locked down and prepared the AUCB for startio, determine
; whether or not there is a optional sense buffer. If there is a buffer then
; allocate an IRP extension to save the state of this buffer. If there is
; no sense buffer required, simply continue to process this request.
;-
	MOVL	UCB$PS_AUCB_ADDR(R5),R0		;U A; Get user descriptor address
	MOVL	CD_SENSE_ADDR(R0),R0		;U A; Get Address of Sense buffer
	BEQL	30$				;U A; No sense buffer continue

	BSBW	SETUP_SENSE_BUFFER		;U A; There's a sense buffer set it up.
	BLBC	R0,AUDIO_EXIT_FDT		;U A; Exit FDT routine on error R0 = error

;+
; All audio I/O must go through the processing above, however some audio
; functions require additional processing before these requests can be queued
; to the startio routine.
;
; Now that we have double mapped the AUCB and Sense Buffer determine whether there are
; any additional buffers that need to be mapped.
;-
30$:	MOVL	UCB$PS_AUCB_ADDR(R5),R9		;U A; Get user descriptor address

	DISPATCH CD_FUNCTION_CODE(R9),TYPE=B,<-	;U A; Dispatch based on function
		<GET_STATUS,	FDT_READ_SUB>,-	;U A; Read Subchannel-Q
		<GET_TOC,	FDT_READ_TOC>>	;U A; Read TOC

;+
; For all other requests, simply issue this IRP to the startio routine.
;-
	CALL_QIODRVPKT				;U A; QUEUE DRIVER PACKET

;+
; The following two AUDIO operations return data to an optional user buffer.
;-
FDT_READ_TOC:
FDT_READ_SUB:

;+
; Check the source/destination buffer for access and protection.
;-
	TSTL	CD_DEST_BUF_ADDR(R9)		;U A; Test address of the dest buffer
	BEQL	40$				;U A;
	TSTL	CD_DEST_BUF_CNT(R9)		;U A; Test size of destination buffer.
	BEQL	40$				;U A; If there is a buffer, must <> 0

;+
; Since there is another buffer that needs to be processed, allocate an IRPE 
; for it now.
;-
	BSBW	ALLOC_IRPE			;U A; Allocate an IRP extension
	BLBC	R0,AUDIO_EXIT_FDT		;U A; Exit if no IRPE
	MOVL	CD_DEST_BUF_ADDR(R9),R0		;U A; Get address of dest buffer
	MOVL	CD_DEST_BUF_CNT(R9),R1		;U A; Get size of dest buffer
	MOVL	R1,IRP$L_BCNT(R3)		;U A; Copy Byte count to IRPE
	MOVL	G^MMG$GL_BWP_MASK,R2		;U A; Byte-within-page mask
	EVAX_AND R0,R2,R2			;U A; Get new byte offset within page
	MOVL	R2,IRP$L_BOFF(R3)		;U A; Update BOFF
	BSBW	AUDIO_MAP_PAGE			;U A; Locks and Maps Destination Buffer.
	BLBC	R0,AUDIO_EXIT_FDT		;U A; Exit FDT routine on error R0 = error

	MOVL	IRPE$L_DRIVER_P1(R3),R3		;U A; Restore  Original IRP Address
	CALL_QIODRVPKT				;U A; Queue the packet to the driver and RET

;+
; Error Paths for DK_AUDIO FDT routine.
; On entry here, R3 = Original IRP address.
;-
40$:	PUSHL	R2
	MOVL	R3,R2				;U A; Get IRP Address in R2
	BSBW	AUDIO_CLEANUP			;U A; Free Allocated resources
	POPL	R2
	MOVZBL	#SS$_BADPARAM,R0		;U A; Set bad parameter status
50$:	CALL_ABORTIO				;U A; Abort the I/O with status in R0 and RET

;+
; Error exit path from audio fdt routines.
; If SS$_FDT_COMPL is returned (eg. because EXE_STD$MODIFYLOCK failed
; and therefore executed its error co-routine), it means that any
; PTE's have already been deallocated, so don't do it again.
;-
AUDIO_EXIT_FDT:: .GLOBAL_LABEL

	CMPL	#SS$_FDT_COMPL,R0		;U A; Have we already freed PTE	
	BEQL	50$				;U A; Yes, don't do it again
	PUSHL	R2
	CMPB	IRP$B_TYPE(R3),#DYN$C_IRP	;U A; Is this the original IRP?
	BNEQ	60$				;U A; No, it's an IRPE
	MOVL	R3,R2				;U A; Get original IRP in R2
	BRW	80$
60$:	MOVL	IRPE$L_DRIVER_P1(R3),R2		;U A; Get original IRP in R2	
80$:	BSBW	AUDIO_CLEANUP			;U A; Free Allocated resources
	MOVL	UCB$PS_AUCB_ADDR(R5),R2		;U A; Get AUCB address
	MOVZWL	R0,CD_COMMAND_STATUS(R2)	;U A; Set bad status is AUCB.
	POPL	R2
	BRB	50$
	.DISABLE LSB				;U A; DK_AUDIO

;+=

	.PAGE
	.SBTTL	+
	.SBTTL	+ QIO INTERFACE ROUTINES
	.SBTTL	+
	.SBTTL	IO_PACKACK	- Pack acknowledge function
;+
; IO_PACKACK
;
; This routine executes a PACKACK request by sending and inquiry, ensuring the
; device is spun up, reading its geometry information, and, if necessary, doing
; device specific setup such as setting the default block size and error
; recovery parameters.
;
; Context:
;
;	SCDRP Thread
;	IPL = Fork
;	Fork lock held
;
; Inputs:
;
;	R3	- UCB address
;	R4	- PDT address
;	R5	- SCDRP address
;
; Outputs:
;
;	R0	- Status
;	R1,R2	- Destroyed
;	All other registers preserved
;-

IO_PACKACK::	.GLOBAL_LABEL			;S FF; Pack acknowledge function

	DK_DEBUG IO_PACKACK

	SET_SINGLE	PACKACK,R3,40$,R5,30$	;S FF; Single threaded PACKACK request.

; Clear write protect status, since the floppy diskette can be changed.

	BICL	#UCB$M_HWL,UCB$L_DK_FLAGS(R3)	;S FF; Assume medium is writeable
						;S FF; until proven otherwise
	BSBW	WAIT_UNIT_READY			;S FF; Wait for the device to spin up
	BLBC	R0,30$				;S FF; Branch on error
	BSBW	INQUIRY				;S FF; Execute an INQUIRY command
	BLBC	R0,30$				;S FF; Branch on error
	BSBW	READ_CAPACITY			;S FF; Execute READ CAPACITY command
	BLBC	R0,30$				;S FF; Branch on error
	BSBW	PROCESS_MODE_INFO		;S FF; Do MODE SENSE/SELECT processing
	BLBS	R0,16$				;S FF; Branch on success
	BBCC	#UCB$V_TENBYTE,-     		;S FF; Branch if we can't back off and
		 UCB$L_DK_FLAGS(R3),30$		;S FF; try a must-be-supported command
	BSBW	PROCESS_MODE_INFO		;S FF; Do MODE SENSE/SELECT processing
	BLBC	R0,30$				;S FF; Branch on error
16$:	BSBW	VALIDATE_GEOMETRY
	BLBC	R0,30$				;S FF; Branch on error
	BSBW	SET_CONN_CHAR			;S FF; Set up the connection characteristics
	BLBC	R0,30$				;S FF; Branch on error

;  If write-enabled and multihost => the drive must support TCQ and AWRE/ARRE.
;  We only test for CLUSQ since that implies TCQ and AWRE/ARRE.

	BBS	#DEV$V_SWL,UCB$L_DEVCHAR(R3),19$  ;S FF; skip if SW writelocked
	BBS	#UCB$V_HWL,UCB$L_DK_FLAGS(R3),19$ ;S FF; skip if HW writelocked

	BBC	#SPDT$V_STS_MULTIHOST,-         ;S FF; skip if not multi-host
		SPDT$L_STS(R4),19$		;S FF;
	BITL	#UCB$M_CLUSQ,-			;S FF; check "cluster ok" bit
		UCB$L_DK_FLAGS(R3)		;S FF; and if not there fail
	BNEQ	19$
	MOVL	#SS$_DRVERR, R0			;S FF; set error status 
	BRW	30$

19$:	BISL	#UCB$M_VALID,UCB$L_STS(R3)	;S FF; Set volume valid
	BISB	#UCB$M_FIRST_ATTN_SEEN,-	;S FF; Indicate that the first unit attention
		UCB$L_DK_FLAGS(R3)		;S FF; has been seen (any future ones will
						;S FF; be logged as errors)
	BBSS	#UCB$V_HBS_CHECK,-		;S FF; Branch if check for host-based
		UCB$L_DK_FLAGS(R3),20$		;S FF; shadowing has already been made
	BSBW	CHECK_HBS			;S FF; Check for host-based shadowing support
	BLBC	R0,30$				;S FF; Failure path
20$:	CLR_SINGLE	PACKACK,R3,40$		;S FF; PACKACK is complete.
	IF_NOT_CMDQ	25$			;S FF; Skip enabling CMDQ processing if not supported
	BICL	#UCB$M_CB_NOCMDQ,-		;S FF; CMDQ support in both the port driver
		UCB$L_CLASS_BUSY(R3)		;S FF; and device. Clear no CMDQ bit
25$:	BRW	COMPLETE_IO			;S FF; Complete this I/O function

30$:	MOVL	SCDRP$L_IRP(R5),R2		;S FF; Get IRP address
	TSTL	IRP$L_PID(R2)			;S FF; Check for interanl IRP
	BLSS	20$				;S FF; Skip local valid cleanup if internal
	BBCC	#UCB$V_LCL_VALID,-		;S FF; Clear local valid bit and
		UCB$L_STS(R3), 20$		;S FF; branch if its already clear.
	DECB	UCB$B_ONLCNT(R3)		;S FF; Else, decrement the online count.
	BRB	20$
40$:	BUG_CHECK INCONSTATE,FATAL		;S FF; bugcheck

	.PAGE
	.SBTTL	IO_WRITEPBLK	- Write a set of blocks to the SCSI drive
	.SBTTL	IO_READPBLK	- Read a set of blocks from the SCSI drive
;+
; IO_READPBLK
; IO_WRITEBLK
;
; This routine reads/writes a set of contiguous blocks from/to the SCSI
; disk. For those transfers that have not already been segmented by the
; exec to chunks LEQ MAXBCNT, this routine performs the segmentation.
;
; Recoverable and non-recoverable errors are dealt with by this routine.
; In summary, the algorithm is to retry the original operation several times.
; If the error persists, and is anything but a non-recoverable read, then
; a block reassignement is performed. The original data is then written to
; the reasigned block as reassignment is not guaranteed to move the data.
; Retries are performed if necessary at each stage.
;
; Because there is no forced error bit in SCSI, blocks with non-recoverable
; errors on reads can not be reassigned. (Otherwise, undetected corruption
; would result. It's preferable to return SS$_PARITY status each time the
; failing block is read.).
;
; If the DATACHECK qualifier is specified on the QIO request, the IO_DATACHECK
; routine is invoked to read the set of blocks just read/written. No block
; reassignment is performed by IO_DATACHECK.
;
; Context:
;
;	SCDRP thread
;	IPL = Fork
;	Fork lock held
;
; Inputs:
;
;	R2	- IPR address
;	R3	- UCB address
;	R4	- PDT address
;	R5	- SCDRP address
;
; Outputs:
;
;	R0	- Status
;	R1,R2	- Destroyed
;	All other registers preserved
;-

WRITE_LOCKED:					;S FF;

	MOVL	#SS$_WRITLCK,R0			;S FF; Set write-locked status
	BRW	COMPLETE_IO			;S FF; Complete QIO with error status

INVALID_BLOCK_NUMBER:

	MOVL	#SS$_IVADDR,R0			;S FF; Otherwise, set invalid address status
	BRW	COMPLETE_IO			;S FF; Complete QIO with error status

IO_WRITEPBLK::	.GLOBAL_LABEL			;S FF; Write a set of blocks to the SCSI drive

	INCL	UCB$L_WRITE_COUNT(R3)		;S FF; Count writes
;
; Figure out which XLEN bucket this transfer fits in
;
	ADDL3	#<IOC$C_DISK_BLKSIZ-1>,-	;S FF; Round up byte count to block multiple
		IRP$L_BCNT(R2),R0		;S FF; of disk block size
	ASHL	#-IOC$S_DISK_BLKSIZ,R0,R0	;S FF; and convert to block count
	ASHL	#-XLEN_HIST_BUCKET_SHIFT,R0,R0	;S FF; Convert to bucket index
	CMPL	#XLEN_HIST_BUCKETS,R0		;S FF; Boundary checks...
	BGTR	1000$				;S FF; Looks OK
	MOVL	#XLEN_HIST_BUCKETS-1,R0		;S FF; Fix index - >64K in same 64K bucket
1000$:	INCL	@UCB$L_WRITE_XLEN_HIST(R3)[R0]	;S FF; Bump count
	INCL	@UCB$L_XLEN_HIST(R3)[R0]	;S FF; Bump total count

	BBS	#UCB$V_HWL,UCB$L_DK_FLAGS(R3),-	;S FF; Branch if device is write-locked,
		WRITE_LOCKED			;S FF; not possible to write device
	BICL	#IRP$M_FUNC,-			;S FF; Clear the FUNC but to indicate this
		SCDRP$IS_STS(R5)		;S FF; is a write function
	BBC	#IO$V_MSCPMODIFS,-		;S FF; Branch if this is NOT a function to
		IRP$L_FUNC(R2),-		;S FF; force a bad block
		IO_RW_COMMON			;S FF; Branch to common code
	BBC	#MSCP$V_MD_ERROR,-		;S FF; Do we want to force error?
		IRP$L_MEDIA+6(R2),-		;S FF; No, continue common path
		IO_RW_COMMON
	BRW	FORCE_ERROR			;S FF; Otherwise, go force an error

IO_READPBLK::	.GLOBAL_LABEL			;S FF;	Read a set of blocks from the SCSI drive

	DK_DEBUG IO_READPBLK

	INCL	UCB$L_READ_COUNT(R3)		;S FF; Count reads
;
; Figure out which XLEN bucket this transfer fits in
;
	ADDL3	#<IOC$C_DISK_BLKSIZ-1>,-	;S FF; Round up byte count to block multiple
		IRP$L_BCNT(R2),R0		;S FF; of disk block size
	ASHL	#-IOC$S_DISK_BLKSIZ,R0,R0	;S FF; and convert to block count
	ASHL	#-XLEN_HIST_BUCKET_SHIFT,R0,R0	;S FF; Convert to bucket index
	CMPL	#XLEN_HIST_BUCKETS,R0		;S FF; Boundary checks...
	BGTR	1500$				;S FF; Looks OK
	MOVL	#XLEN_HIST_BUCKETS-1,R0		;S FF; Fix index - >64K in same 64K bucket
1500$:	INCL	@UCB$L_READ_XLEN_HIST(R3)[R0]	;S FF; Bump count
	INCL	@UCB$L_XLEN_HIST(R3)[R0]	;S FF; Bump total count

	BISL	#IRP$M_FUNC,-			;S FF; Set the FUNC but to indicate this
		SCDRP$IS_STS(R5)		;S FF; is a read function

IO_RW_COMMON:

;
; See if we need to recalculate the qdepth
;
	DECL	UCB$L_XLEN_HIST_CYCLE(R3)	;S FF; count down I/O's till recheck
	BNEQ	100$				;S FF; If neq, not counted down yet
	BSBW	CHECK_QDEPTH			;S FF; See if we need to set new depth
						;S FF; based on xlen histogram info
	MOVL	#XLEN_HIST_TURNOVER,-		;S FF; Reset I/O counter for qdepth check
		UCB$L_XLEN_HIST_CYCLE(R3)
100$:

	BBC	#UCB$V_VALID,-			;S FF; Branch if volume is invalid.
		UCB$L_STS(R3),-			;S FF; We need to have done a PACKACK
		VOLUME_INVALID			;S FF; before we can do a read or write
	ADDL3	#<IOC$C_DISK_BLKSIZ-1>,-	;S FF; Round up byte count
		IRP$L_BCNT(R2),R0		;S FF;	to block multiple
	ASHL	#-IOC$S_DISK_BLKSIZ,R0,R0	;S FF; and convert to block count
	ADDL	IRP$L_MEDIA(R2),R0		;S FF; Calculate highest block # accessed
	CMPL	R0,UCB$L_MAXBLOCK(R3)		;S FF; Valid block number?
	BGTR	INVALID_BLOCK_NUMBER		;S FF; Branch if not

	BBC	#UCB$V_CB_NOCMDQ,-		;SF FF; If device supports CMDQ, then no need
		UCB$L_CLASS_BUSY(R3), 10$	;SF FF; to set_single

	SET_SINGLE SINGLE_RW,R3			;S FF; Single thread R/W operations

10$:	DECL	UCB$L_OTHER_COUNT(R3)		;S FF; Fix count since this is a R/W

	CLRL	SCDRP$L_ABCNT(R5)		;S FF; Initialize accumulated byte count
	MOVL	IRP$L_FUNC(R2),-		;S FF; Copy function code and modifiers,
		SCDRP$L_FUNC(R5)		;S FF; from the IRP to the SCDRP
	MOVL	IRP$L_MEDIA(R2),-		;S FF; Copy IRP$L_MEDIA (LBN #)
		SCDRP$L_MEDIA(R5)		;S FF; from the IRP to the SCDRP
	MOVL	IRP$L_SVAPTE(R2),-		;S FF; Copy SVA of buffer to
		SCDRP$L_SVAPTE(R5)		;S FF; from the IRP to the SCDRP
	MOVL	IRP$L_BOFF(R2),-		;S FF; Copy buffer length
		SCDRP$L_BOFF(R5)		;S FF; from the IRP to the SCDRP

	BBC	#IO$V_MSCPMODIFS,-		;S FF; Branch if no MSCP modifiers
		IRP$L_FUNC(R2),-		
		110$                              
	BBC	#MSCP$V_MD_EXPRS,-		;S FF; Is this an express request?
		IRP$L_MEDIA+6(R2),110$		;S FF; 
	MOVL	#SCDRP$K_QCHAR_HEAD,-           ;S FF; Yes, send to head of queue
                SCDRP$IS_QUEUE_CHAR(R5)	
 	BRW	IO_RW_LOOP
110$:	MOVL	#SCDRP$K_QCHAR_UNORDERED,-      ;S FF; Send a Simple tagged request
                SCDRP$IS_QUEUE_CHAR(R5)	

IO_RW_LOOP:

	SUBL3	SCDRP$L_ABCNT(R5),-		;S FF; Attempt to transfer all remaining
		IRP$L_BCNT(R2),-		;S FF; bytes in user's buffer
		SCDRP$L_BCNT(R5)		;S FF;
	CMPL	SCDRP$L_BCNT(R5),-		;S FF; Transfer length greater than maximum
		UCB$L_MAXBCNT(R3)		;S FF; supported?
	BLEQU	10$				;S FF; Branch if not
	MOVL	UCB$L_MAXBCNT(R3),-		;S FF; Otherwise, transfer must be segmented
		SCDRP$L_BCNT(R5)		;S FF; into pieces of MAXBCNT length
10$:	BSBW	READ_WRITE			;S FF; Send a SCSI read or write command
						;S FF; SCDRP$IS_QUEUE_CHAR was set earlier
	MOVL	SCDRP$L_IRP(R5),R2		;S FF; Restore IRP address
	BLBC	R0,IO_RW_ERR			;S FF; Branch on error

IO_RW_ACCUM:

	SUBL3	SCDRP$L_PAD_BCNT(R5),-		;S FF; Get actual number of bytes transferred
		SCDRP$L_TRANS_CNT(R5),R0	;S FF; less any possible padding
	CMPL	R0,SCDRP$L_BCNT(R5)		;S FF; Compare with requested transfer count
	BNEQ	IO_RW_MISMATCH			;S FF; Branch if mismatch occurred
	ADDL	R0,SCDRP$L_ABCNT(R5)		;S FF; Accumulate this piece of the transfer
	CMPL	SCDRP$L_ABCNT(R5),IRP$L_BCNT(R2);S FF; Is transfer complete?
	BLSSU	IO_RW_SEGMENT_DONE		;S FF; Branch if not, accumulate this segment
						;S FF; of the transfer

	BBS	#IO$V_DATACHECK,-		;S FF; Branch if the datacheck modifier
		SCDRP$L_FUNC(R5),-		;S FF; has been specified. Perform a
		IO_DATACHECK			;S FF; datacheck operation.

	MOVW	#SS$_NORMAL,R0			;S FF; Set success status

IO_RW_EXIT:

	BBC	#UCB$V_CB_NOCMDQ,-		;SF FF; If device supports CMDQ, then no need
		UCB$L_CLASS_BUSY(R3), 10$	;SF FF; to clr_single

	CLR_SINGLE SINGLE_RW,R3 		;S FF; Clear thread R/W operations

10$:	INSV	SCDRP$L_ABCNT(R5),-		;S FF; Load low-order number of bytes
		#16,#16,R0			;S FF; transferred into R0
	MOVZWL	SCDRP$L_ABCNT+2(R5),R1		;S FF; Load high-order number of bytes
						;S FF; transferred into R1
	BRW	COMPLETE_IO			;S FF; Complete the QIO

; Here we have completed one piece of a segmented transfer. Update the MEDIA,
; SVAPTE and BOFF fields in the SCDRP and go perform the next segment of
; the transfer.

IO_RW_SEGMENT_DONE:

	ASHL	#-IOC$S_DISK_BLKSIZ,R0,R1	;S FF; Convert byte count to block count
	ADDL2	R1,SCDRP$L_MEDIA(R5)		;S FF; to logical block number in SCDRP
	MOVL	SCDRP$L_BOFF(R5),R1		;S FF; byte offset into register
	ADDL2	R1,R0				;S FF; offset plus byte count
	MOVL	G^MMG$GL_VPN_TO_VA,R1		;S FF; right shift factor
	EVAX_SRL R0,R1,R1			;S FF; calulate number of VPN's to advance
	EVAX_SLL R1,#PTE$C_SHIFT_SIZE,R1	;S FF; number of PTE's to advance
	ADDL2	R1,SCDRP$L_SVAPTE(R5)		;S FF; Update SVAPTE field in SCDRP
	MOVL	G^MMG$GL_BWP_MASK,R1		;S FF; Byte within page mask of virtual addr
	EVAX_AND R0,R1,R1			;S FF; Get new byte offset within page
	MOVL	R1,SCDRP$L_BOFF(R5)		;S FF; Save byte offset in SCDRP
	BRW	IO_RW_LOOP			;S FF; Go perform next segment of transfer

; Here the transfer count returned by the port doesn't match the requested
; byte count. If it's greater, than truncate the transfer to what we
; requested. Otherwise, accumulate the piece of the transfer that just
; completed and continue with the transfer.

IO_RW_MISMATCH:

	BLSS	10$				;S FF; Branch if the transfer count is less
						;S FF; than the requested count, accumulate
						;S FF; this piece of the transfer
	ADDL3	SCDRP$L_PAD_BCNT(R5),-		;S FF; Truncate the transfer such that the
		SCDRP$L_BCNT(R5),-		;S FF; the transfer count we got is what we
		SCDRP$L_TRANS_CNT(R5)		;S FF; expected
	BRB	IO_RW_ACCUM			;S FF; Accumulate this piece of the transfer

10$:	BICW	#<IOC$C_DISK_BLKSIZ-1>,R0	;S FF; Round transfer down to block multiple
	BEQL	IO_RW_SHORT_XFER		;S FF; Branch if no bytes to accumulate
	MOVL	R0,SCDRP$L_BCNT(R5)		;S FF; Adjust byte count and transfer count
	ADDL3	SCDRP$L_PAD_BCNT(R5),-		;S FF; to accumulate this segment of the
		R0,SCDRP$L_TRANS_CNT(R5)	;S FF; transfer.
	BRB	IO_RW_ACCUM			;S FF; Accumulate this segment of the transfer

IO_RW_SHORT_XFER:

	MOVL	#SS$_OPINCOMPL,R0		;S FF; Set bad status
	BRB	IO_RW_EXIT			;S FF; Complete I/O with error status

; Here an error occurred in performing the read or write. If it's due to a
; recoverable or non-recoverable (MEDIA) error, then accumulate the piece of
; the transfer that completed successfully and retry the original operation on
; failing block several times.
;
; For read-only devices, since we can't perform a reassign, retry the read
; only for non-recoverable errors.
;
; For devices (such as floppies) that don't support reassign, retry the read
; only for non-recoverable errors.

IO_RW_ERR:

	CMPL	R0,#SS$_PARITY			;S FF; Media (non-recoverable) error?
	BEQL	10$				;S FF; Branch if so
	CMPL	R0,#SS$_RECOVERR		;S FF; Recoverable error?
	BNEQ	IO_RW_EXIT			;S FF; Branch if not, return error to caller

	BBS	#DEV$V_SWL,UCB$L_DEVCHAR(R3),5$	;S FF; Branch if device is SWL
	BBC	#UCB$V_NOREASSIGN,-		;S FF; Branch if device supports
		UCB$L_DK_FLAGS(R3),10$		;S FF;	 reassign_block command
5$:	BRW	IO_RW_ACCUM			;S FF; Recoverable error can't be reassigned,
						;S FF;	 treat as success

; The ADDNL_INFO field in the SCDRP contains a copy of the additional
; information field from the extended sense data. Make sure that it is valid
; and that the failing LBN it contains falls within the range of the original
; read or write command. If not, then assume the failing block is the first
; one in the range. If the failing LBN is valid, then accumulate all blocks
; of the transfer up to the failing LBN and update the MEDIA, ABCNT, BOFF, and
; SVAPTE fields in the SCDRP.

10$:	MOVL	SCDRP$L_ADDNL_INFO(R5),R0	;S FF; Get the failing LBN
	BLSS	IO_RW_RETRY			;S FF; Branch if LBN is invalid
	CMPL	R0,SCDRP$L_MEDIA(R5)		;S FF; Is failing LBN below range?
	BLSS	IO_RW_RETRY			;S FF; Branch if so
	ASHL	#-IOC$S_DISK_BLKSIZ,-		;S FF; Get number of blocks requested
		SCDRP$L_BCNT(R5),R1
	ADDL	SCDRP$L_MEDIA(R5),R1		;S FF; Get highest LBN requested
	CMPL	R0,R1				;S FF; Is failing LBN above range?
	BGEQ	IO_RW_RETRY			;S FF; Branch if so
	SUBL3	SCDRP$L_MEDIA(R5),R0,R1		;S FF; Calculate number of successfully
						;S FF; transferred blocks
	BLEQ	IO_RW_RETRY			;S FF; Branch if none
	ASHL	#-IOC$S_DISK_BLKSIZ,-		;S FF; Convert to block count
		SCDRP$L_TRANS_CNT(R5),R0
	CMPL	R0,R1				;S FF; Were all blocks up to bad LBN transferred?
	BLSS	20$				;S FF; Branch if not
	MOVL	R1,R0				;S FF; Accumulate all blocks up to the bad one
20$:	ADDL	R0,SCDRP$L_MEDIA(R5)		;S FF; Update LBN
	ASHL	#IOC$S_DISK_BLKSIZ,R0,R1	;S FF; Get number of bytes transferred,
	ADDL	R1,SCDRP$L_ABCNT(R5)		;S FF; Update accumulated byte count
	ADDL	SCDRP$L_BOFF(R5),R1		;S FF; Get byte offset plus byte count
	MOVL	G^MMG$GL_BWP_MASK,R0		;S FF; Byte-within-page mask
	EVAX_AND R1,R0,R0			;S FF; Get new byte offset within page
	MOVL	R0,SCDRP$L_BOFF(R5)		;S FF; Update BOFF

	ASHL	G^MMG$GL_VA_TO_VPN,R1,R0	;S FF; Convert bytes to pages
	ASHL	#PTE$C_SHIFT_SIZE,R0,R0		;S FF; Convert page count to PTE offset
	ADDL	R0,SCDRP$L_SVAPTE(R5)		;S FF; Update SVAPTE

; At this point all blocks of the transfer up to failing LBN has been
; accumulated. Retry the transfer of the failing LBN several times to see if
; the block can be read or written successfully. If so, then accumulate the
; data for this block and proceed with the transfer. Otherwise, attempt to
; reassign the block if appropriate.

	.ENABLE	LOCAL_BLOCK

IO_RW_RETRY:

	SET_SINGLE	BBR_IN_PROG,R3,BBR_WAIT	;S FF; Single thread bad block replacement.
   
	MOVB	#RW_RETRY_CNT,UCB$B_RW_RETRY(R3);S FF; Initialize read/write retry count
	SUBL3	SCDRP$L_ABCNT(R5),-		;S FF; Get length of remaining transfer
		IRP$L_BCNT(R2),SCDRP$L_BCNT(R5)	;S FF;
	CMPL	SCDRP$L_BCNT(R5),-		;S FF; More than one block?
		#IOC$C_DISK_BLKSIZ		;S FF;
	BLEQ	10$				;S FF; Branch if not
	MOVL	#IOC$C_DISK_BLKSIZ,-		;S FF; Limit transfer to one block
		SCDRP$L_BCNT(R5)		;S FF;
10$:	DISABLE_ERRLOG				;S FF; Temporarily disable errorlogging
	BSBW	READ_WRITE			;S FF; Perform the read or write
						;S FF; SCDRP$IS_QUEUE_CHAR was set earlier
	REENABLE_ERRLOG				;S FF; Reenable errorlogging
	MOVL	SCDRP$L_IRP(R5),R2		;S FF; Get IRP address
	BLBS	R0,IO_RW_ACCUM_BBR		;S FF; Branch if success, pick up original
						;S FF; transfer where we left off
	CMPL	R0,#SS$_PARITY			;S FF; Non-recoverable error?
	BEQL	20$				;S FF; Branch if so
	CMPL	R0,#SS$_RECOVERR		;S FF; Recoverable error?
	BNEQ	IO_RW_EXIT_BBR			;S FF; Branch if not, complete QIO with error

; The following code segment catches the case in which a read to a write-locked
; disk fails initially with a non-recoverable error, but returns a recoverable
; error on a read retry. In this case, we accept the data rather than risk
; loosing it with another read retry. The same logic holds true for disks that
; don't support reassign.

	BBS	#DEV$V_SWL,UCB$L_DEVCHAR(R3),15$;S FF; Branch if device is SWL
	BBC	#UCB$V_NOREASSIGN,-		;S FF; Branch if device supports
		UCB$L_DK_FLAGS(R3),20$		;S FF;	 reassign_block command
15$:
IO_RW_ACCUM_BBR:
	CLR_SINGLE	BBR_IN_PROG,R3		;S FF; Bad Block Replacement is complete
	BRW	IO_RW_ACCUM			;S FF; Recoverable error can't be reassigned,
						;S FF; accumulate data

BBR_WAIT::	.GLOBAL_LABEL

;
;		R2	IRP address
;		R3	UCB address
;		R4	SPDT address
;		R5	SCDRP address
;

	KP_STALL_FORK_WAIT  KPB=SCDRP$PS_KPB(R5),-
			    FKB=R5		;S FF; Fork wait using SCDRP fork block

;
;		R2	IRP address
;		R3	UCB address
;		R4	SPDT address
;		R5	SCDRP address
;

	BRB	IO_RW_RETRY			;S FF;

20$:	DECB	UCB$B_RW_RETRY(R3)		;S FF; Decrement read/write retry count
	BGTR	10$				;S FF; Branch if count not exhausted

	.DISABLE	LOCAL_BLOCK

; Here we've been unsuccessful several times in performing the read or
; write to the failing LBN. If it's appropriate to reassign the block, do so.
; Note that since there is no forced error flag in SCSI, we can't reassign a
; block with a non-recoverable read error.

IO_RW_REASSIGN:

; If we're here with a disk that doesn't support reassign, then all the
; retries above must have faild due to non-recoverable errors. Therfore,
; complete the QIO now with bad status.

	BBS	#UCB$V_NOREASSIGN,-		;S FF; Branch if device doesn't support
		UCB$L_DK_FLAGS(R3),-		;S FF;	 reassign_block command
		IO_RW_EXIT_BBR			;S FF;

	BBC	#IRP$V_FUNC,-			;S FF; Branch if this is a write, OK to
		SCDRP$IS_STS(R5),10$		;S FF; try reassign
	CMPL	R0,#SS$_PARITY			;S FF; Non-recoverable error?
	BEQL	IO_RW_EXIT_BBR			;S FF; Branch if so, NOT OK try reassign.

10$:	MOVB	#REASSIGN_RETRY_CNT,-		;S FF; Initialize the reassign retry count
		UCB$B_REASSIGN_RETRY(R3);

IO_RW_REASSIGN_LOOP:

20$:
	MOVL	SCDRP$L_MEDIA(R5),R1		;S FF; Save LBN of block to reassign
	IF_CANCEL IO_RW_EXIT_BBR		;S FF; Check for cancel request before
						;S FF; allocating private SCDRP
	DK_ALLOC_SCDRP				;S FF; Allocate a new SCDRP for the reassign
	MOVL	R1,SCDRP$L_MEDIA(R5)		;Są FF; Copy LBN to reassign SCDRP
	BSBW	REASSIGN_BLOCK			;Są FF; Go attempt to reassign the block
	DK_DEALLOC_SCDRP CLRSTK=YES		;Są FF; Deallocate the reassign SCDRP
	BLBS	R0,IO_RW_REWRITE		;S FF; Branch if the reassign succeeded
	CMPL	R0,#SS$_CANCEL			;S FF; Was cancel sts returned
	BEQL	IO_RW_EXIT_BBR			;S FF; If so, exit
	DECB	UCB$B_REASSIGN_RETRY(R3)	;S FF; Decrement the reassign retry count
	BGTR	20$				;S FF; Branch if count not exhausted

; Here we've attempted a reassign and failed. Write the original data to
; whereever the block is currently assigned as the failed reassign operations
; may have corrupted the data.

;	BRB	IO_RW_REWRITE			;S FF; Rewrite the block
						;S FF; Fall-Thru

; Here the reassign has completed successfully. Write the original data
; to the reassigned block. The assumption is that the reassign command
; doesn't necessarily move the data from the old to the new location.
; If the original command was a read and the last attempt to read the
; block returned a short transfer count, then don't rewrite the block
; because the data in the user's buffer is invalid. Instead, rely on the
; reassign command to move the data.

IO_RW_REWRITE:

	MOVB	#REWRITE_RETRY_CNT,-		;S FF; Initialize rewrite retry count
		UCB$B_REWRITE_RETRY(R3)		;S FF;
	BBC	#IRP$V_FUNC,-			;S FF; Branch if this is a write, OK to
		SCDRP$IS_STS(R5),10$		;S FF; rewrite block
	CMPL	SCDRP$L_TRANS_CNT(R5),-		;S FF; Valid transfer count?
		#IOC$C_DISK_BLKSIZ		;S FF;
	BLSS	IO_RW_SHORT_XFER		;S FF; Branch if not, can't rewrite block

10$:	BICL	#IRP$M_FUNC,SCDRP$IS_STS(R5)	;S FF; Clear FUNC indicate write function
	DISABLE_ERRLOG				;S FF; Temporarily disable errorlogging
	BSBW	READ_WRITE			;S FF; Rewrite the original block
						;S FF; SCDRP$IS_QUEUE_CHAR was set earlier
	REENABLE_ERRLOG				;S FF; Reenable errorlogging
	MOVL	SCDRP$L_IRP(R5),R2		;S FF; Restore the IRP address
	MOVL	IRP$L_STS(R2),-			;S FF; Restore the setting of the func
		SCDRP$IS_STS(R5)		;S FF; bit from the IRP
	BLBS	R0,IO_RW_ACCUM_BBR		;S FF; Branch if success, pick up original
						;S FF; transfer where we left off
	CMPL	R0,#SS$_CANCEL			;S FF; Was cancel sts returned
	BEQL	IO_RW_EXIT_BBR			;S FF; If so, exit
	DECB	UCB$B_REWRITE_RETRY(R3)		;S FF; Decrement rewrite retry count
	BGTR	10$				;S FF; Branch if count not exhausted

; Here we've failed to write the data to the reassigned block. Attempt to
; reassign the block again. If the reassign retry count is exhausted. Then
; we've done all we can so return the status of the last failing write.

	DECB	UCB$B_REASSIGN_RETRY(R3)	;S FF; Decrement reassign retry count
	BGTR	IO_RW_REASSIGN_LOOP		;S FF; Branch if count not exhausted
	CMPL	R0,#SS$_RECOVERR		;S FF; Was last write recoverable?
	BEQL	IO_RW_ACCUM			;S FF; Branch if so, treat as successful and
						;S FF; proceed with transfer
IO_RW_EXIT_BBR:
	CLR_SINGLE	BBR_IN_PROG,R3		;S FF; Bad Block Replacement is complete
	BRW	IO_RW_EXIT			;S FF; Otherwise, complete QIO with error status

; Workaround a problem in RZ55's with microde versions less than 900.
; RZ55 with microcode version less than 900 will return one additional
; block of data when the drive successfully recovers a block by
; simply rereading it. This block contains ramdom data and the host assumes
; it contains valid data, the users I/O contains erroneous data.
;
;	INPUTS:
;		R3 - UCB Address
;
RZ55_WORKAROUND:

; Check the product revision number (version) of the microcode of the RZ55. If the first
; digit is non-zero or the second digit is greater than the ascii value of '8' then
; this microcode has been fixed. Otherwise the class driver must workaround
; the hardware problem by decrementing TRANS_CNT.

	.JSB_ENTRY INPUT=<R3>,PRESERVE=<R5>
	CMPB	#^A'0',UCB$L_HW_REV(R3)		;S FF; If first digit non-zero
	BNEQ	110$				;S FF; then no fixup needed.
	CMPB	#^A'9',UCB$L_HW_REV+1(R3)	;S FF; If second digit <= '9'
	BLEQU	110$				;S FF; then Fixup needed.
	PUSHL	R5				;S FF; Save active SCDRP
	MOVL	SCDRP$PS_PREV_SCDRP(R5),R5	;S FF; Restore original SCDRP address
	SUBL2	#DTE_EXTRA_BYTES,-		;S FF; Subtract off the extra block, to
		 SCDRP$L_TRANS_CNT(R5)		;S FF; determine exactly which block to
						;S FF; resume transfer from.
	POPL	R5				;S FF; Restore active SCDRP
110$:	RSB					;S FF; Read the rest of the I/O request.

	.PAGE
	.SBTTL	IO_WRITECHECK	- Compare user's data with that on disk
;+
; IO_WRITECHECK
;
; This routine compares the data in the user's buffer to that on the disk.
; No write is actually performed, just a read (to a buffer allocated from
; pool) followed by the compare operation.
;
; Context:
;
;	SCDRP thread
;	IPL = Fork
;	Fork lock held
;
; Inputs:
;
;	R2	- IRP address
;	R3	- UCB address
;	R4	- PDT address
;	R5	- SCDRP address
;
; Outputs:
;
;	R0	- Status
;	R1,R2	- Destroyed
;	All other registers preserved
;-

IO_WRITECHECK:					;S FF; Compare user's data with that on disk

	BBC	#UCB$V_CB_NOCMDQ,-		;SF FF; If device supports CMDQ, then no need
		UCB$L_CLASS_BUSY(R3), 10$	;SF FF; to set_single

	SET_SINGLE SINGLE_RW,R3			;S FF; Single thread R/W operations

10$:;;;	BRB	IO_DATACHECK			;S FF; Fall through to datacheck routine

	.PAGE
	.SBTTL	IO_DATACHECK	- Check data that was just read or written
;+
; IO_DATACHECK
;
; This routine is invoked if a QIO read or write specifies the DATACHECK
; qualifier. It reads the set of blocks that have just been read or written
; and compares the original and new data. A temporary buffer is allocated
; from non-paged pool to store the data being read, and whose contents is
; compared with that of the user's buffer.
;
; Note that SCDRP$L_SVAPTE and SCDRP$L_BOFF describe the datacheck buffer in
; the AXP version of this driver.  However, in the VAX version, SCDRP$L_SVAPTE
; and SCDRP$L_BOFF describe the current segment of the user buffer.  As a
; result, the AXP version of this routine leaves those cells unaltered for each
; segment of the user buffer.  This also affects the method used by routine
; DATACHECK_CMP to doubly map the current segment of the user buffer.
;
; Context:
;
;	Fork thread
;	IPL = Fork
;	Fork lock held
;
; Inputs:
;
;	R3	- UCB address
;	R4	- PDT address
;	R5	- SCDRP address
;
; Outputs:
;
;	R0	- Status
;	R1,R2	- Destroyed
;	All other registers preserved
;-

IO_DATACHECK::	.GLOBAL_LABEL			;S FF; Check data that was just read or written

	DK_DEBUG IO_DATACHECK

	SET_SINGLE SINGLE_DC,R3,IO_DC_WAIT	;S FF; Single thread Data Check

	BISL	#IRP$M_FUNC,SCDRP$IS_STS(R5)	;S FF; Set FUNC to indicate read
	MOVL	SCDRP$L_IRP(R5),R2		;S FF; Get IRP address
	CLRL	SCDRP$L_ABCNT(R5)		;S FF; Initialize accumulated byte count
	MOVL	IRP$L_FUNC(R2),-		;S FF; Copy function code and modifiers,
		SCDRP$L_FUNC(R5)		;S FF; MEDIA, SVAPTE and BOFF fields from
	MOVL	IRP$L_MEDIA(R2),-		;S FF; the IRP to the SCDRP
		SCDRP$L_MEDIA(R5)		;S FF;
	MOVL	IRP$L_SVAPTE(R2),-		;S FF;
		SCDRP$L_SVAPTE(R5)		;S FF;
	MOVL	IRP$L_BOFF(R2),-		;S FF;
		SCDRP$L_BOFF(R5)		;S FF;
	MOVL	IRP$L_BCNT(R2),R1		;S FF; Get transfer length
	CMPL	R1,UCB$L_MAXBCNT(R3)		;S FF; Greater than max?
	BLEQ	10$				;S FF; Branch if not
	MOVL	UCB$L_MAXBCNT(R3),R1		;S FF; Use MAXBCNT instead
10$:	PUSHL	R5				;S FF; Save SCDRP address
	MOVL	SCDRP$PS_KPB(R5),R5		;S FF; Get address of KPB
	BSBW	DK_ALLOC_POOL			;S FF; Allocate a datacheck buffer
	POPL	R5				;S FF; Restore SCDRP address
	MOVL	R2,SCDRP$L_DATACHECK(R5)	;S FF; Save datacheck buffer address
	MOVL	R2,SCDRP$L_SVA_USER(R5)		;S FF; Datacheck buffer is also "user" buffer
	MOVL	G^MMG$GL_BWP_MASK,R1		;S FF; Mask of BWP portion of virtual addr
	EVAX_AND R2,R1,R1			;S FF; Get byte offset in page
	MOVL	R1,SCDRP$L_BOFF(R5)		;S FF; Save byte offset
	PUSHL	R3				;S FF; Save UCB
	JSB	G^MMG$SVAPTECHK			;S FF; Get SVAPTE of allocated system buffer
	MOVL	R3,SCDRP$L_SVAPTE(R5)		;S FF; Save SVAPTE in SCDRP
	POPL	R3				;S FF; Restore UCB address
	MOVL	SCDRP$L_IRP(R5),R2		;S FF; Restore IRP address

IO_DC_LOOP:

	BISB	#SCDRP$M_FLAG_S0BUF,-		;S FF; Indicate that this is an S0 "user"
		SCDRP$L_SCSI_FLAGS(R5)		;S FF; buffer
	SUBL3	SCDRP$L_ABCNT(R5),-		;S FF; Attempt to transfer all remaining
		IRP$L_BCNT(R2),-		;S FF; bytes in user's buffer
		SCDRP$L_BCNT(R5)		;S FF;
	CMPL	SCDRP$L_BCNT(R5),-		;S FF; Transfer length greater than maximum
		UCB$L_MAXBCNT(R3)		;S FF; supported?
	BLEQU	10$				;S FF; Branch if not
	MOVL	UCB$L_MAXBCNT(R3),-		;S FF; Otherwise, transfer must be segmented
		SCDRP$L_BCNT(R5)		;S FF; into pieces of MAXBCNT length
10$:	MOVL	#SCDRP$K_QCHAR_UNORDERED,-      ;S FF; Send a Simple tagged request
                SCDRP$IS_QUEUE_CHAR(R5)	
	BSBW	READ_WRITE			;S FF; Send a SCSI read or write command
	BLBS	R0,IO_DC_ACCUM			;S FF; Branch on success
	CMPL	R0,#SS$_RECOVERR		;S FF; Recoverable error?
	BNEQ	IO_DC_EXIT			;S FF; Branch if not, return with error status

IO_DC_ACCUM:

	MOVL	SCDRP$L_IRP(R5),R2		;S FF; Restore IRP address
	SUBL3	SCDRP$L_PAD_BCNT(R5),-		;S FF; Get actual number of bytes transferred
		SCDRP$L_TRANS_CNT(R5),R0	;S FF; less any possible padding
	CMPL	R0,SCDRP$L_BCNT(R5)		;S FF; Compare with requested transfer count
	BNEQ	IO_DC_MISMATCH			;S FF; Branch if mismatch occurred
	BSBW	DATACHECK_CMP			;S FF; Compare the original and new data
	BLBC	R0,IO_DC_EXIT			;S FF; Branch if a mismatch occurred
	SUBL3	SCDRP$L_PAD_BCNT(R5),-		;S FF; Get actual number of bytes transferred
		SCDRP$L_TRANS_CNT(R5),R0	;S FF; less any possible padding
	ADDL	R0,SCDRP$L_ABCNT(R5)		;S FF; Accumulate this piece of the transfer
	CMPL	SCDRP$L_ABCNT(R5),IRP$L_BCNT(R2);S FF; Is transfer complete?
	BLSSU	IO_DC_SEGMENT_DONE		;S FF; Branch if not, accumulate this segment
						;S FF; of the transfer

	INSV	SCDRP$L_ABCNT(R5),-		;S FF; Load low-order number of bytes
		#16,#16,R0			;S FF; transferred into R0
	MOVZWL	SCDRP$L_ABCNT+2(R5),R1		;S FF; Load high-order number of bytes
						;S FF; transferred into R1
	MOVW	#SS$_NORMAL,R0			;S FF; Set success status

IO_DC_EXIT:

	PUSHR	#^M<R0,R1>			;S FF; Save registers
	MOVL	SCDRP$L_DATACHECK(R5),R0	;S FF; Get datacheck buffer address
	BSBW	DEALLOC_POOL			;S FF; Deallocate the datacheck buffer

	BBC	#UCB$V_CB_NOCMDQ,-		;S FF; If device supports CMDQ, then no need
		UCB$L_CLASS_BUSY(R3), 10$	;S FF; to clr_single
	CLR_SINGLE SINGLE_RW,R3 		;S FF; Clear thread R/W operations

10$:	CLR_SINGLE SINGLE_DC,R3 		;S FF; Datacheck request is now complete.

	REMQUE	@UCB$Q_DC_WAIT_LIST(R3), R1	;S FF; Dequeue entry from datacheck wait list
	BVS	20$				;S FF; Branch if list is empty
	MOVAL	-SCDRP$PS_PQFL(R1), R1		;S FF; Get starting address of SCDRP
	MOVL	SCDRP$PS_KPB(R1), R1		;S FF; Get KPB address
	PUSHL	#SS$_NORMAL			;S FF; Return SS$_NORMAL
	PUSHL	R1				;S FF; KPB to restart
	CALLS	#2,EXE$KP_RESTART		;S FF; restart this kernel process
20$:	POPR	#^M<R0,R1>			;S FF; Restore registers
	BRW	COMPLETE_IO			;S FF; Complete the QIO

; Here we have completed one piece of a segmented transfer. Update the MEDIA
; cell in the SCDRP to point to the LBN of the next segment.  However, leave
; SCDRP$L_SVAPTE and SCDRP$L_BOFF pointing to the start of the datacheck buffer
; and go perform the next segment of the transfer.  

IO_DC_SEGMENT_DONE:

	ASHL	#-IOC$S_DISK_BLKSIZ,R0,R1	;S FF; Convert byte count to block count
	ADDL2	R1,SCDRP$L_MEDIA(R5)		;S FF; Update logical block number in SCDRP
	BRW	IO_DC_LOOP			;S FF; Go perform next segment of transfer

; Here the transfer count returned by the port doesn't match the requested
; byte count. If it's greater, than truncate the transfer to what we
; requested. Otherwise, accumulate the piece of the transfer that just
; completed and continue with the transfer.

IO_DC_MISMATCH:

	BLSS	10$				;S FF; Branch if the transfer count is less
						;S FF; than the requested count, accumulate
						;S FF; this piece of the transfer
	ADDL3	SCDRP$L_PAD_BCNT(R5),-		;S FF; Truncate the transfer such that the
		SCDRP$L_BCNT(R5),-		;S FF; the transfer count we got is what we
		SCDRP$L_TRANS_CNT(R5)		;S FF; expected
	BRB	IO_DC_ACCUM			;S FF; Accumulate this piece of the transfer

10$:	BICW	#<IOC$C_DISK_BLKSIZ-1>,R0	;S FF; Round transfer down to block multiple
	BEQL	20$				;S FF; Branch if no bytes to accumulate
	MOVL	R0,SCDRP$L_BCNT(R5)		;S FF; Adjust byte count and transfer count
	ADDL3	SCDRP$L_PAD_BCNT(R5),-		;S FF; to accumulate this segment of the
		R0,SCDRP$L_TRANS_CNT(R5)	;S FF; transfer.
	BRW	IO_DC_ACCUM			;S FF; Accumulate this segment of the transfer

20$:	MOVL	#SS$_OPINCOMPL,R0		;S FF; Set bad status
	BRB	IO_DC_EXIT			;S FF; Complete I/O with error status

;
;	Stall datacheck operations waiting for other SCDRPs to release UCB field
;	ownership
;

IO_DC_WAIT:: .GLOBAL_LABEL

	INSQUE	SCDRP$PS_PQFL(R5), -		;F FF; Enqueue SCDRP on tail of DC wait queue
		@UCB$Q_DC_WAIT_LIST(R3)		;F FF;
	MOVL	SCDRP$PS_KPB(R5), R0		;F FF; Get SCDRP's KPB address
	MOVAL	G^IOC$RETURN, -			;F FF; Set NULL stall routine address
		KPB$PS_SCH_STALL_RTN(R0)	;F FF;
	PUSHL	R0				;F FF; Push KPB address
	CALLS	#1,G^EXE$KP_STALL_GENERAL 	;F FF; Wait until other datachecks are done
	BRB	IO_DATACHECK			;F FF; Try again to gain UCB ownership

;+
; IO_NOP
;
; Additionaly this I/O function provides I/O sequentiality for use by OpenVMS I/O subsystems
; (Shadowing, MSCP Server, Cluster-I/O).  This I/O function does NOT alter device media or
; mode parameters.  I/O Sequentiality guarantees that all active I/Os received prior to this 
; I/O operation will be executed to completion. Then this sequential I/O operation is performed. 
; While the sequential command is active, no other I/Os will be started. Upon completion of this 
; I/O operation, new I/O will be started, if no other conditions (busy bits), are set.
;
; As part of the I/O operation, an "ordered test-unit-ready" command to the associcated
; SCSI target is done. SCSI order commands provide and I/O ordering barrier for all initiators
; (VMSCluster members). This functionality is simialar to the Alpha MB (memory-barrier)
; instruction.
;
; Context:
;
;	SCDRP thread
;	IPL = Fork
;	Fork lock held
;
; Inputs:
;
;	R3	- UCB address
;	R4	- PDT address
;	R5	- SCDRP address
;
; Outputs:
;
;	R0	- Status
;	R1,R2	- Destroyed
;	All other registers preserved
;-
IO_NOP::	.GLOBAL_LABEL

	DK_DEBUG IO_NOP

	SET_SINGLE	NOP,R3,20$,R5,10$	;S FF; Single thread NOP request.

	DISABLE_ERRLOG				;S FF; Temporarily disable errorlogging
	BSBW	TEST_UNIT_READY			;S FF; Issue an ORDERED TUR to sync queues
	REENABLE_ERRLOG				;S FF; Reenable errorlogging
	MOVZWL	#SS$_NORMAL,R0			;S FF; Set success status

10$:	CLR_SINGLE	NOP,R3,20$ 		;S FF; NOP request is complete.
	BRB	COMPLETE_IO			;S FF; Complete the I/O

20$:	BUG_CHECK INCONSTATE,FATAL		;S FF; bugcheck

	.PAGE
	.SBTTL	IO_UNLOAD	- Make drive available (and spin it down)
	.SBTTL	IO_AVAILABLE	- Make drive available (but don't spin it down)

;+
; IO_UNLOAD
; IO_AVAILABLE
;
; These two routines make the drive available by clearing the VALID bit in the
; specificed UCB. For the IO_UNLOAD entry point, the device is spun down if it has
; removable media.
;
; Additionaly this routine provides I/O sequentiality for use by OpenVMS I/O subsystems
; (Shadowing, MSCP Server, Cluster-I/O). I/O Sequentiality guarantees that all active
; I/Os received prior to this I/O operation will be executed to completion. The this
; ; sequential I/O operation is started. While the sequential command is active, no other
; I/Os will be started. Upon completion of this I/O operation, new I/O will be started, if no
; other conditions (busy bits), are set.
;
; As part of the I/O operation, an "ordered test-unit-ready" command to the associcated
; SCSI target is done. SCSI order commands provide and I/O ordering barrier for all initiators
; (VMSCluster members). This functionality is simialar to the Alpha MB (memory-barrier)
; instruction.
;
; Context:
;
;	SCDRP thread
;	IPL = Fork
;	Fork lock held
;
; Inputs:
;
;	R3	- UCB address
;	R4	- PDT address
;	R5	- SCDRP address
;
; Outputs:
;
;	R0	- Status
;	R1,R2	- Destroyed
;	All other registers preserved
;-

IO_AVAILABLE::	.GLOBAL_LABEL			;S FF; Make drive available (but don't spin it down)

	SET_SINGLE	AVAILABLE,R3,20$,R5,10$	;S FF; Single thread AVAILABLE request.

	BSBW	TEST_UNIT_READY			;S FF; Issue an ORDERED TUR to sync queues

	BICL	#UCB$M_VALID,-			;S FF; Clear the volume valid bit in
		UCB$L_STS(R3)			;S FF; the UCB
	MOVZWL	#SS$_NORMAL,R0			;S FF; Set success status

10$:	CLR_SINGLE	AVAILABLE,R3,20$	;S FF; Available request is complete.
	BRW	COMPLETE_IO			;S FF; Complete the I/O

20$:	BUG_CHECK INCONSTATE,FATAL		;S FF; bugcheck

IO_UNLOAD::	.GLOBAL_LABEL			;S FF; Make drive available (and spin it down)

	SET_SINGLE	UNLOAD,R3,20$,R5,15$	;S FF; Single thread UNLOAD request.

	BSBW	TEST_UNIT_READY			;S FF; Issue an ORDERED TUR to sync queues

	BBC	#UCB$V_REMOVABLE,-		;S FF; Branch if this is a device with
		UCB$L_DK_FLAGS(R3), 10$		;S FF; non-removable media

	BSBW	STOP_UNIT			;S FF; Spin down the unit

10$:	BICL	#UCB$M_VALID,-			;S FF; Clear the volume valid bit in
		UCB$L_STS(R3)			;S FF; the UCB
	MOVZWL	#SS$_NORMAL,R0			;S FF; Set success status

15$:	CLR_SINGLE	UNLOAD,R3,20$		;S FF; Unload request is complete.
	BRW	COMPLETE_IO			;S FF; Complete the I/O

20$:	BUG_CHECK INCONSTATE,FATAL		;S FF; bugcheck

	.PAGE
	.SBTTL	IO_FORMAT	- format a floppy diskette
;+
; IO_FORMAT
;
; This routine formats a floppy diskette.
;
; An operator may request the format with the DCL command
;	INIT/DENSITY=DOUBLE <device_name> <volume_label>
; where the <density_value> may be
;	DOUBLE (or HD) for RX33s drives,
;	DOUBLE (or HD), [or SINGLE (or DD) if DD_BYPASS set] for RX23s drives,
;	DD (or SINGLE), HD (or DOUBLE), or ED for RX26 drives
;
; Context:
;
;	SCDRP thread
;	IPL = Fork
;	Fork lock held
;
; Inputs:
;
;	R2	- IRP address
;	R3	- UCB address
;	R4	- PDT address
;	R5	- SCDRP address
;
; Outputs:
;
;	R0	- Status
;	R1,R2	- Destroyed
;	All other registers preserved
;-

IO_FORMAT::	.GLOBAL_LABEL			;S FF; format a floppy diskette

	SET_SINGLE	FORMAT,R3		;S FF; Single thread FORMAT request.

; Format command performs an implicit AVAILABLE.
; Update the UCB to reflect an AVAILABLE command.

	ASSUME	UCB$V_VALID GE 8
	BICL	#<UCB$M_VALID>,UCB$L_STS(R3)	;S FF; Clear software volume valid.
	BBCC	#UCB$V_LCL_VALID,-		;S FF; First FORMAT or AVAILABLE?
		UCB$L_STS(R3), 10$		;S FF; If CC not first, "Can't happen"
	DECB	UCB$B_ONLCNT(R3)		;S FF; Decrement online count.

10$:	BBS	#UCB$V_FORMAT,-			;S FF; Is FORMAT supported by the drive?
		UCB$L_DK_FLAGS(R3),100$
	MOVL	#SS$_FORMAT,R0			;S FF; No, return an error
	BRW	1000$

100$:	BBC	#UCB$V_HWL,-			;S FF; Can we write (to format) the diskette?
		UCB$L_DK_FLAGS(R3),120$		;S FF;
	MOVL	#SS$_WRITLCK,R0			;S FF; No, return an error
	BRW	1000$

;
; Determine that the correct /DENSITY qualifier was used on the INIT command.
;
; We support the following commands:
;
;	INIT/DENSITY=SINGLE shows up here as IRP$L_MEDIA=1
;	INIT/DENSITY=DD shows up here as IRP$L_MEDIA=1
;	INIT/DENSITY=DOUBLE shows up here as IRP$L_MEDIA=2
;	INIT/DENSITY=HD shows up here as IRP$L_MEDIA=2
;	INIT/DENSITY=ED shows up here as IRP$L_MEDIA=3
;
; The allowed formats for the various drives are:
;
;	RX23s allows IRP$L_MEDIA=2 always, and
;	  IRP$L_MEDIA=1 only when UCB$L_DK_FLAGS<DD_BYPASS> is set;
;	RX33s allows only IRP$L_MEDIA=2;
;	RX26 allows IRP$L_MEDIA= 1, 2, or 3, but the appropriate diskette
;	  must be in the drive.
;
120$:	CMPB	#DT$_RX26,UCB$B_DEVTYPE(R3)	;S FF; Check RX26-specific format params
	BNEQ	170$				;S FF; Not RX26, go check the others
	CMPL	#SCSI$DK$C_SS,IRP$L_MEDIA(R2)	;S FF; Lowest value allowed for RX26
	BGTRU	180$				;S FF; Too low, go report error
	CMPL	#SCSI$DK$C_DD,IRP$L_MEDIA(R2)	;S FF; Highest value allowed for RX26
	BLSSU	180$				;S FF; Too high, go report error
	BRB	200$				;S FF; Value's OK
170$:	CMPL	#SCSI$DK$C_DS,IRP$L_MEDIA(R2)	;S FF; Verify /density=double
	BEQL	200$				;S FF;

						;S FF; Allow formatting with /density=single
						;S FF; only for RX23 with a bypass override
						;S FF; because 9-sector formats are
						;S FF; unreliable

	BBC	#UCB$V_DD_BYPASS,-		;S FF; Check for the special override bit
		UCB$L_DK_FLAGS(R3),180$		;S FF; to see if we can handle /density=single
	CMPB	#DT$_RX23S,UCB$B_DEVTYPE(R3)	;S FF; Only worry about RX23 media
	BNEQ	180$
	CMPL	#SCSI$DK$C_SS,IRP$L_MEDIA(R2)	;S FF; Verify /density=single
	BEQL	200$				;S FF; Yep, media's = singler

180$:	MOVL	#SS$_BADPARAM,R0		;S FF; Nope, report error
	BRW	1000$

;
; Adjust format-sensitive parameters in the UCB here
;
; Check for appropriate diskette type for the density requested
;
200$:	MOVL	SCDRP$L_IRP(R5),R2		;S FF; Restore IRP address
	CMPB	#DT$_RX26,UCB$B_DEVTYPE(R3)	;S FF; Only RX26 reports diskette type
	BNEQ	500$				;S FF; Not RX26, skip this check
	CMPZV	#UCB$V_FLOPPY_MEDIA,-		;S FF; Check that the requested format
		#UCB$S_FLOPPY_MEDIA,-		;S FF;	 is compatible with the diskette
		UCB$L_DK_FLAGS(R3),-		;S FF;
		IRP$L_MEDIA(R2)			;S FF;
	BEQL	500$				;S FF; OK, let it through
	MOVL	#SS$_FORMAT,R0			;S FF; No, return an error
	BRB	1000$				;S FF; Return error

500$:	BSBW	PROCESS_MODE_FORMAT_FLOPPY	;S FF; get changable parameters
	BLBC	R0,1000$			;S FF; Return on error

	BSBW	FORMAT				;S FF; Issue format command
	BLBC	R0,1000$			;S FF; Return on error

	MOVZWL	#SS$_NORMAL,R0			;S FF; Set success status

1000$:	CLR_SINGLE	FORMAT,R3		;S FF; FORMAT request is now complete
	BRW	COMPLETE_IO			;S FF; Complete the I/O

	.PAGE
	.SBTTL	IO_DSE		- Data security erase function
;+
; IO_DSE
;
; This routine erases a set of logical blocks by writing zeros to the
; blocks.
;
; Context:
;
;	SCDRP thread
;	IPL = Fork
;	Fork lock held
;
; Inputs:
;
;	R2	- IRP address
;	R3	- UCB address
;	R4	- PDT address
;	R5	- SCDRP address
;
; Outputs:
;
;	R0	- Status
;	R1,R2	- Destroyed
;	All other registers preserved
;-

	MAX_BUF_SIZE = 65535 & < ^C < IOC$C_DISK_BLKSIZ - 1> >
	ASSUME	MAX_BUF_SIZE&<IOC$C_DISK_BLKSIZ-1> eq 0

IO_DSE::	.GLOBAL_LABEL			;S FF; Data security erase function

	BBC	#UCB$V_CB_NOCMDQ,-		;SF FF; If device supports CMDQ, then no need
		UCB$L_CLASS_BUSY(R3), 5$	;SF FF; to set_single

	SET_SINGLE SINGLE_DSE,R3 		;S FF; Single thread Data security erase request 

5$:	BICL	#IRP$M_FUNC,-			;S FF; Clear the FUNC but to indicate this
		SCDRP$IS_STS(R5)		;S FF; is a write function
	MOVL	IRP$L_MEDIA(R2),-		;S FF; Copy starting block number from
		SCDRP$L_MEDIA(R5)		;S FF; IRP to SCDRP
	ADDL3	#<IOC$C_DISK_BLKSIZ-1>,-	;S FF; Round up byte count
		IRP$L_BCNT(R2),R1		;S FF;	to a block
	BICL	#<IOC$C_DISK_BLKSIZ-1>,R1	;S FF; boundary
	MOVL	R1,IRP$L_BCNT(R2)		;S FF; save modified byte count

;
;	allocate zeroed buffer to write from
;
	CMPL	R1,#MAX_BUF_SIZE		;S FF; is the buffer too big?
	BLEQ	10$				;S FF; LEQ means it's OK
	MOVL	#MAX_BUF_SIZE ,R1		;S FF; if it's too big use max buffer size

10$:	PUSHL	R5				;S FF; Save SCDRP address
	MOVL	SCDRP$PS_KPB(R5),R5		;S FF; Get KPB address
	BSBW	DK_ALLOC_POOL			;S FF; Allocate pool
	POPL	R5				;S FF; REstore SCDRP address
	BLBC	R0,37$				;S FF; Exit if allocation failed

	BISB	#SCDRP$M_FLAG_S0BUF,-		;S FF; Indicate that this is an S0 "user"
		SCDRP$L_SCSI_FLAGS(R5)		;S FF; buffer
	MOVL	R2,SCDRP$L_SVA_USER(R5)		;S FF; Save address of the DSE buffer

;
;	get svapte of allocated buffer
;
	PUSHL	R3				;S FF; Save R3 (svapte will be returned in R3)
	JSB	G^MMG$SVAPTECHK			;S FF; Get SVAPTE of allocated system buffer
	MOVL	R3,SCDRP$L_SVAPTE(R5)		;S FF; Save SVAPTE in SCDRP

;
;	generate byte offset from va of allocated buffer
;
	MCOML	G^MMG$GL_BWP_MASK,R3		;S FF; get mask of bits to clear
	BICL3	R3,R2,SCDRP$L_BOFF(R5)		;S FF; mask of non-offset part of the VA
	POPL	R3				;S FF; Restore R3

	CLRL	SCDRP$L_ABCNT(R5)		;S FF; set accumulated count to 0

;
;	This is the main write loop 
;
20$:	MOVL	SCDRP$L_IRP(R5),R2		;S FF; restore the IRP

;
;	get bytes left to write in R0   (IRP$L_BCNT(R2)-SCDRP$L_ABCNT(R5))
;
	MOVL	IRP$L_BCNT(R2),R0		;S FF; get QIO byte count
	SUBL	SCDRP$L_ABCNT(R5),R0		;S FF; get bytes left to write out
	BLEQU	32$

;
;	Check to see if we've gotten to the end of the disk
;
	CMPL	SCDRP$L_MEDIA(R5),- 		;S FF; are we past the end of the DISK?
		UCB$L_MAXBLOCK(R3)
	BGEQ	30$				;S FF; GTR means yes don't write any more

;
;	see if bytes left to write is too big 	
;
	CMPL	R0,#MAX_BUF_SIZE		;S FF; is this too many to do now?
	BLEQ	22$				;S FF; LEQ means no write them out
	

	MOVL	#MAX_BUF_SIZE,R0		;S FF; else use max size

;
;	make sure we won't write past end of disk
;
22$:	ASHL	#-IOC$S_DISK_BLKSIZ,R0,R1  	;S FF; convert to blocks		
	ADDL2	SCDRP$L_MEDIA(R5),R1		;S FF; get block after last write
	CMPL	R1,UCB$L_MAXBLOCK(R3)		;S FF; will we go past the end?
	BLEQ	25$				;S FF; leq means no everything is OK
	SUBL3	SCDRP$L_MEDIA(R5),-		;S FF; less starting block for # blocks left
		UCB$L_MAXBLOCK(R3),R1
	ASHL	#IOC$S_DISK_BLKSIZ,R1,R0	;S FF; convert BLOCKS to bytes
	
25$:	MOVL	R0,SCDRP$L_BCNT(R5)		;S FF; Save byte count for this write
	
	MOVL	#SCDRP$K_QCHAR_UNORDERED,-      ;S FF; Send a Simple tagged request
                SCDRP$IS_QUEUE_CHAR(R5)	
	BSBW	READ_WRITE			;S FF; write out the data
	BLBC	R0,34$				;S FF; LBC means some error
	MOVL	SCDRP$L_BCNT(R5),R0		;S FF; get the # bytes that we wrote
	ADDL2	R0,SCDRP$L_ABCNT(R5)		;S FF; update the accumulated byte count 
						;S FF; with bytes we just wrote
	ASHL	#-IOC$S_DISK_BLKSIZ,R0,R0	;S FF; convert to blocks
	ADDL2	R0,SCDRP$L_MEDIA(R5)		;S FF; update next block to erase
	BRW	20$

30$:	MOVL	#SS$_IVADDR,R0			;S FF; here if we get to the end of the disk
	BRW	36$

32$:	MOVL	#SS$_NORMAL,R0			;S FF; here if we finish without incident
	BRW	36$

34$:						;S FF; here if we get an error from read_write
						;S FF; return the error as is
;
;	here to deallocate pool - retuens status in R0
;
36$:	PUSHL	R0				;S FF; Save status
	MOVL	SCDRP$L_SVA_USER(R5),R0		;S FF; Get address of S0 buffer
	BSBW	DEALLOC_POOL			;S FF; Deallocate the S0 buffer
	POPL	R0				;S FF; Restore R0

;
;	here after buffer is deallocated or to return errors that happen before
;	the buffer is allocated or if the allocation fails.
;
37$:	MOVZWL	SCDRP$L_ABCNT+2(R5),R1		;S FF; Set high-order word of transfer cnt
	INSV	SCDRP$L_ABCNT(R5),#16,#16,R0	;S FF; Copy low-order word of transfer cnt

	BBC	#UCB$V_CB_NOCMDQ,-		;SF FF; If device supports CMDQ, then no need
		UCB$L_CLASS_BUSY(R3), 40$	;SF FF; to clr_single

	CLR_SINGLE SINGLE_DSE,R3 		;S FF; Data security erase request is now complete.

40$:	BRW	COMPLETE_IO			;S FF; Complete the QIO request

	.PAGE
	.SBTTL	IO_DIAGNOSE	- Special pass-through function
;+
; IO_DIAGNOSE
;
; Context:
;
;	SCDRP thread
;	IPL = Fork
;	Fork lock held
;
; 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::	.GLOBAL_LABEL			;S FF;	Special pass-through function
	PUSHAL	UCB$PS_DIAGNOSE(R3)		;S FF; Address of DIAGNOSE area in UCB
	PUSHL	R5                      	;S FF; SCDRP address
	PUSHL	SCDRP$L_CDT(R5)			;S FF; SCDT address
	PUSHL	R2				;S FF; IRP address
	CALLS	#4,DKMK$DIAGNOSE_SIO		;S FF; Call common DIAGNOSE routine

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

	EVAX_SRL R0,#32,R1              	;S FF; Return status in R0/R1
	BRW	COMPLETE_IO			;S FF; Complete the QIO

;+=

	.PAGE
	.SBTTL	IO_AUDIO	- SCSI audio STARTIO function
	.ENABLE	LOCAL_BLOCK

;+
; IO_AUDIO
;
; This routine is executed in the STARTIO context. It converts an AUCB into a SCSI AUDIO
; Command Descriptor Block (CDB). This CDB is then sent to and executed by the target
; device. If there is data to be returned to a user buffer the data is copied within
; this routine. The AUCB Operating System status and SCSI status fields are updated.
;
; After the I/O has been completed by the target, the AUDIO_EXIT code is called to unlock
; any users buffers other than the AUCB. The AUCB will be unlocked during I/O post-
; processing. The System PTE's allocated to double map any of the buffer will be
; deallocated by this routine, prior to calling REQCOM.
;
; Context:
;
;	SCRDP thread
;	IPL = Fork
;	Fork lock held
;
; Inputs:
;	R2	- IRP address
;	R3	- UCB address
;	R4	- PDT address
;	R5	- SCDRP address
;
; Outputs:
;
;	R0	- Status
;	R1,R2	- Destroyed
;	All other registers preserved
;-

IO_AUDIO::	.GLOBAL_LABEL			;S FF; SCSI audio STARTIO function

	SET_SINGLE	AUDIO,R3		;S FF; Single thread the AUDIO request

	MOVL	IRP$L_SEGVBN(R2),R0		;S FF; Get start address of S0 buffer.
	MOVL	R0,SCDRP$L_MEDIA(R5)		;S FF; Get copy of AUCB address in SCDRP.

;+
; Dispatch to the appropriate path based on the function code in the low
; order byte of the AUCB's function code field.
;
; R0 is the SVA of the allocated buffered I/O buffer or AUCB Address.
; R2 is the address of the original IRP.
; R5 is the address of the SCDRP.
;-

	DISPATCH CD_FUNCTION_CODE(R0),TYPE=B,<-	;S FF; Dispatch according to function
	<PAUSE,			CD_PAUSE>,-		; Pause
	<RESUME,		CD_RESUME>,-		; Resume
	<PREVENT_REMOVAL,	CD_PREVENT_REMOVAL>,-	; Prevent removal
	<ALLOW_REMOVAL,		CD_ALLOW_REMOVAL>,-	; Allow removal
	<PLAY_AUDIO,		CD_PLAY_AUDIO>,-	; Play Audio LBA
	<PLAY_AUDIO_MSF,	CD_PLAY_AUDIO_MSF>,-	; Play MSF
	<PLAY_AUDIO_TRACK,	CD_PLAY_AUDIO_TRACK>,-	; Play Audio Track
	<PLAY_TRACK_REL,	CD_PLAY_TRACK_REL>,-	; Play Track Relative
	<GET_STATUS,		CD_READ_SUB>,-		; Read Subchannel-Q
	<GET_TOC,		CD_READ_TOC>,-		; Read TOC
	<GET_VOLUME,		CD_GET_VOLUME>,-	; Mode Sense
	<SET_VOLUME,		CD_SET_VOLUME>>		; Mode Select

;+
; Note: The SONY CDROM does not implement this command.
;-
CD_PLAY_TRACK_REL:				;S FF; Audio CDROM control - Play Track

;+
; All unsupported I/O functions will be failed at this point.
;-
	MOVL	SCDRP$L_MEDIA(R5),R0		;S FF; Get copy of AUCB address from SCDRP.
	MOVZWL	#SS$_BADPARAM,-			;S FF; Set bad parameter status
		CD_COMMAND_STATUS(R0)
	MOVZWL	CD_COMMAND_STATUS(R0),R0	;S FF; Copy VMS status to R0 for COMPLETE_IO
	BRW	AUDIO_BAD_CMD_EXIT		;S FF; Complete this I/O function

	.DISABLE	LOCAL_BLOCK

	.PAGE
	.SBTTL	AUDIO_PLAY_FUNCTIONS	- Audio CDROM play startio code
;+
; AUDIO_PLAY_FUNCTIONS
;
; The following code, makes up the startio routine for all of the Audio
; play functions supported by this driver. Execution of these functions cause
; the CD to begin a play operation.
;
; Context:
;
;	SCDRP thread
;	IPL = Fork
;	Fork lock held
;
; Inputs:
;	R0	- AUCB address
;	R2	- IRP address
;	R3	- UCB address
;	R4	- PDT address
;	R5	- SCDRP address
;
; Outputs:
;
;	R0	- Status
;	R1,R2	- Destroyed
;	All other registers preserved
;-
; AUDIO_PLAY_FUNCTIONS:

;+
; Now we will allocate a command buffer and fill it in with a SCSI CDB
; with all the required fields initialized.
;
; The PLAY_TRACK command requires a starting and ending track. The starting
; track is passed AUCB ARG1 and ending track in AUCB ARG2.
;-
CD_PLAY_AUDIO_TRACK:				;S FF; Audio CDROM control - Play Audio

	MOVAL	CMD_PLAY_TRACK,R2		;S FF; Address of Play Track command
	SETUP_CMD				;S FF; Perform setup for SCSI command
	ADDL3	#4,SCDRP$L_CMD_PTR(R5),R0	;S FF; Get address of SCSI CDB (Command).
						;S FF; The CDB doesn't start at 0, but 4.
	MOVL	SCDRP$L_MEDIA(R5),R1		;S FF; Restore address of AUCB.

;+
; The command bytes must be moved one byte at a time into the CDB, since
; the byte ordering is not the same as VAX!
;-
	CMPB	CD_ARG1(R1),#99			;S FF; IF starting track > 99
	BGTRU	IO_BADPARM			;S FF;	then error.
	MOVB	CD_ARG1(R1),-			;S FF; Copy Starting Track number to CDB.
		CMD_PLAY_TRACK$B_STARTING_TRACK(R0)
	MOVB	CD_ARG1+1(R1),-			;S FF; Copy Starting Index number.
		CMD_PLAY_TRACK$B_STARTING_INDEX(R0)
	MOVB	CD_ARG2(R1),-			;S FF; Copy Ending Track number to CDB.
		CMD_PLAY_TRACK$B_END_TRACK(R0)	;S FF; If ending track is 0
	BEQL	IO_BADPARM			;S FF;	then error.
	MOVB	CD_ARG2+1(R1),-			;S FF; Copy Ending Index number.
		CMD_PLAY_TRACK$B_END_INDEX(R0)
10$:	
	SEND_COMMAND_ORDERED			;S FF; Send the SCSI command
	BRW	AUDIO_EXIT			;S FF; Complete an audio SCSI function

;+
; The Play Audio command requires a starting LBA address and block count which
; are passed in ARG1 and ARG2 of the AUCB respectively.
;-

CD_PLAY_AUDIO:					;S FF; Audio CDROM control - Play Audio

	MOVAL	CMD_PLAY_AUDIO10,R2		;S FF; Address of Play Audio10 command
	SETUP_CMD				;S FF; Perform setup for SCSI command
	ADDL3	#4,SCDRP$L_CMD_PTR(R5),R0	;S FF; Get address of SCSI CDB (Command).
						;S FF; The CDB doesn't start at 0, but 4.
	MOVL	SCDRP$L_MEDIA(R5),R1		;S FF; Restore address of AUCB.
;+
; Setup the starting Logical Block Address(LBA) for the Play Audio 10 command.
;
; The command bytes must be moved one byte at a time into the CDB, since
; the byte ordering is not the same as VAX!
;-
	MOVB	CD_ARG1+0(R1),-			;S FF; Copy Starting Logical Block Addr(LSB)
		CMD_PLAY_AUDIO10$B_LBA_0(R0)
	MOVB	CD_ARG1+1(R1),-			;S FF; Copy middle LBA byte to CDB.
		CMD_PLAY_AUDIO10$B_LBA_1(R0)
	MOVB	CD_ARG1+2(R1),-			;S FF; Copy middle LBA byte to CDB.
		CMD_PLAY_AUDIO10$B_LBA_2(R0)
	MOVB	CD_ARG1+3(R1),-			;S FF; Copy MSB LBA byte to CDB
		CMD_PLAY_AUDIO10$B_LBA_3(R0)

;+
; Now setup the transfer length in the CDB for the Play Audio 10 command.
;-
	MOVB	CD_ARG2+0(R1),-			;S FF; LSB of the Transfer Length into CDB.
		CMD_PLAY_AUDIO10$B_XFR_0(R0)
	MOVB	CD_ARG2+1(R1),-			;S FF; MSB of Transfer Length.
		CMD_PLAY_AUDIO10$B_XFR_1(R0)
	TSTW	CD_ARG2+2(R1)			;S FF; These 2 fields must be zero.
	BNEQ	IO_BADPARM			;S FF; No, bad parameter
10$:
	SEND_COMMAND_ORDERED			;S FF; Send the SCSI command
	BRW	AUDIO_EXIT			;S FF; Complete an audio SCSI function

;+
; The Play MSF command requires a starting and ending time expressed in Minutes, Seconds
; and frames format and are passed in ARG1 and ARG2 of the AUCB respectively.
;-
CD_PLAY_AUDIO_MSF:				;S FF; Audio CDROM control - Play Audio HH:SS:F

	MOVAL	CMD_PLAY_AUDIO_MSF,R2		;S FF; Address of Play Audio MSF (47) command
	SETUP_CMD				;S FF; Perform setup for SCSI command
	ADDL3	#4,SCDRP$L_CMD_PTR(R5),R0	;S FF; Get address of SCSI CDB (Command).
						;S FF; The CDB doesn't start at 0, but 4.
	MOVL	SCDRP$L_MEDIA(R5),R1		;S FF; Restore address of AUCB.

;+
; Setup the starting Minute, Seconds and Frame fields for the Play MSF command.
;
; The command bytes must be moved one byte at a time into the CDB, since
; the byte ordering is not the same as VAX!
;-
	MOVB	CD_ARG1+0(R1),-			;S FF; Copy Starting Frame byte to the CDB.
		CMD_PLAY_AUDIO$B_START_FRM(R0)
	MOVB	CD_ARG1+1(R1),-			;S FF; Copy Seconds byte to the CDB.
		CMD_PLAY_AUDIO$B_START_SEC(R0)
	MOVB	CD_ARG1+2(R1),-			;S FF; Copy Minutes byte to the CDB.
		CMD_PLAY_AUDIO$B_START_MIN(R0)

;+
; Now setup the ending MSF fields for the Play Audio 12 command.
;-
	MOVB	CD_ARG2+0(R1),-			;S FF; Copy ending Frames byte to the CDB.
		CMD_PLAY_AUDIO$B_END_FRM(R0)
	MOVB	CD_ARG2+1(R1),-			;S FF; Copy Seconds byte to the CDB.
		CMD_PLAY_AUDIO$B_END_SEC(R0)
	MOVB	CD_ARG2+2(R1),-			;S FF; Copy Minutes byte to the CDB.
		CMD_PLAY_AUDIO$B_END_MIN(R0)

10$:	
	SEND_COMMAND_ORDERED			;S FF; Send the SCSI command
	BRW	AUDIO_EXIT			;S FF; Complete an audio SCSI function

	.PAGE
	.SBTTL	AUDIO_CONTROL_FUNCTIONS	- Audio CDROM control startio code
;+
; AUDIO_CONTROL_FUNCTIONS
;
; The functions in this section of code are used to executed various audio
; control functions including pausing and resuming the CD.
;
; Context:
;
;	SCDRP thread
;	IPL = Fork
;	Fork lock held
;
; Inputs:
;	R0	- AUCB address
;	R2	- IRP address
;	R3	- UCB address
;	R4	- PDT address
;	R5	- SCDRP address
;
; Outputs:
;	R0	- Status
;	R1,R2	- Destroyed
;	All other registers preserved
;-
; AUDIO_CONTROL_FUNCTIONS:

CD_PAUSE:					;S FF; Audio CDROM control - Pause

	MOVAL	CMD_PAUSE,R2			;S FF; Address of PAUSE command
	SETUP_CMD				;S FF; Perform setup for SCSI command
	SEND_COMMAND_ORDERED			;S FF; Send the SCSI command
	BRW	AUDIO_EXIT			;S FF; Exit Audio Start I/O.

CD_RESUME:					;S FF; Audio CDROM control - Resume

	MOVAL	CMD_RESUME,R2			;S FF; Address of RESUME command
	SETUP_CMD				;S FF; Perform setup for SCSI command
	SEND_COMMAND_ORDERED			;S FF; Send the SCSI command
	BRW	AUDIO_EXIT			;S FF; Exit Audio Start I/O.

CD_PREVENT_REMOVAL:				;S FF; Audio CDROM control - no REMOVE

	MOVAL	CMD_REMOVAL,R2			;S FF; Address of Prevent Removal command
	SETUP_CMD				;S FF; Perform setup for SCSI command
	ADDL3	#4,SCDRP$L_CMD_PTR(R5),R0	;S FF; Get address of SCSI CDB (Command).
						;S FF; The CDB doesn't start at 0, but 4.
	MOVB	#1,CMD_REMOVAL$R_PREVENT(R0)	;S FF; Set the Prevent Removal bit.
	SEND_COMMAND_ORDERED			;S FF; Send the SCSI command
	BRW	AUDIO_EXIT			;S FF; Exit Audio Start I/O.

CD_ALLOW_REMOVAL:				;S FF; Audio CDROM control - yes REMOVE

	MOVAL	CMD_REMOVAL,R2			;S FF; Address of Prevent Removal command
	SETUP_CMD				;S FF; Perform setup for SCSI command
	ADDL3	#4,SCDRP$L_CMD_PTR(R5),R0	;S FF; Get address of SCSI CDB (Command).
						;S FF; The CDB doesn't start at 0, but 4.
	CLRB	CMD_REMOVAL$R_PREVENT(R0)	;S FF; Clear the Prevent Removal bit.
	SEND_COMMAND_ORDERED			;S FF; Send the SCSI command
	BRW	AUDIO_EXIT			;S FF; Exit Audio Start I/O.

	.PAGE
	.SBTTL	AUDIO_GETSET_FUNCTIONS	- Audio CDROM get/set parameter code
;+
; AUDIO_GETSET_FUNCTIONS
;
; The functions in this section of code are used to query or initialize
; specific parameters or data from the CDROM.  For example, the Table Of
; Contents (TOC) can be read, the current position of the CDROM
; can be read and returned to the user or the volume of the CDROM can be set.
;
; Context:
;
;	SCDRP thread
;	IPL = Fork
;	Fork lock held
;
; Inputs:
;	R0	- AUCB address
;	R2	- IRP address
;	R3	- UCB address
;	R4	- PDT address
;	R5	- SCDRP address
;
; Outputs:
;
;	R0	- Status
;	R1,R2	- Destroyed
;	All other registers preserved
;-
; AUDIO_GETSET_FUNCTIONS:

;+
; The GET_STATUS AUCB function does a SCSI READ SUBQ command and returns the
; requested subchannel data (format 1 and 2 are supported) to the application.
; From this information the application can determine the current track and
; head location of the CD-ROM as well as the UPC bar code data.
;-
CD_READ_SUB:					;S FF; Audio CDROM control - read subchannel

	DISPATCH CD_ARG2(R0),TYPE=B,<-		;S FF; Dispatch based on format
	    <SCSI$SUB$C_SUBQ_CHANNEL_DATA,80$>,-;S FF; - Sub-Q Channel data
	    <SCSI$SUB$C_CD_ROM_POSITION,  40$>,-;S FF; - CD-ROM Current Position
	    <SCSI$SUB$C_MCN,		  20$>,-;S FF; - Media Catalog Number (UPC/Bar Code)
	    <SCSI$SUB$C_ISRC,             80$>>	;S FF; - Track International-Standard-Recording-Code (ISRC)
	MOVL	#SS$_BADPARAM,R0		;S FF; else bad parameter
	BRW	AUDIO_EXIT_NO_CMD		;S FF;

;+
;  Get format 2 (MCN) data from UCB or by issuing a SCSI READ SUBQ command
;-
20$:	BSBW	FETCH_MCN			;S FF; get format 2 data
	BLBC	R0,AUDIO_EXIT_NO_CMD		;S FF;	R0 with status

;
; MCN data in UCB is good, copy to users buffer
;
	PUSHR	#^M<R0,R1,R2,R3,R4,R5>
	MOVL	SCDRP$L_IRP(R5),R1		;S FF; Get original IRP Address
	MOVL	IRP$L_SEGVBN(R1),R2		;S FF; Get start address of the AUCB.
	MOVL	CD_DEST_BUF_CNT(R2),R2		;S FF; Get size of destination buffer.
	MOVL	#SCSI$SUBQ$C_MCN_LENGTH,R0	;S FF; and size of stored MCN data
	MINUM	R0,R2				;S FF; Minimize between these two values.
	MOVL	IRP$L_EXTEND(R1),R1		;S FF; Get the linked/extended IRP address
	TSTL	IRPE$L_DRIVER_P0(R1)		;S FF: Is this the sense buffer IRPE?
	BEQL	30$				;S FF; No, it's the destination buffer
	MOVL	IRPE$L_EXTEND(R1),R1		;S FF; Get address of destination buffer
30$:	MOVC3	R0,UCB$B_MCN_SCDATA(R3),-	;S FF; Copy the number of bytes received from
		@IRP$L_SEGVBN(R1)		;S FF; UCB to the start of the mapped user buffer
	POPR	#^M<R0,R1,R2,R3,R4,R5>
	MOVL	#SS$_NORMAL,R0			;S FF; return success
	BRW	AUDIO_EXIT_NO_CMD		;S FF;

40$:	BSBW	READ_CD_SUBQ			;S FF; Get format 1 data.
	BLBC	R0,AUDIO_EXIT			;S FF; On error, complete audio function

;+
; Move READ SUBQ data from intermediate buffer to users destination buffer.
;-
	PUSHR	#^M<R0,R1,R2,R3,R4,R5>
	MOVL	SCDRP$L_IRP(R5),R1		;S FF; Get original IRP Address
	MOVL	IRP$L_SEGVBN(R1),R2		;S FF; Get start address of the AUCB.
	MOVL	CD_DEST_BUF_CNT(R2),R2		;S FF; Get size of destination buffer.
	MINUM	SCDRP$L_TRANS_CNT(R5),R2	;S FF; Minimize between these two values.
	MOVL	IRP$L_EXTEND(R1),R1		;S FF; Get the linked/extended IRP address
	TSTL	IRPE$L_DRIVER_P0(R1)		;S FF: Is this the sense buffer IRPE?
	BEQL	50$				;S FF; No, it's the destination buffer
	MOVL	IRPE$L_EXTEND(R1),R1		;S FF; Get address of destination buffer
50$:	MOVC3	SCDRP$L_TRANS_CNT(R5),-		;S FF; Copy the number of bytes received
		 @SCDRP$L_SVA_USER(R5),-	;S FF; Copy from Nonpaged, to the
		  @IRP$L_SEGVBN(R1)		;S FF; start of the mapped user buffer
	POPR	#^M<R0,R1,R2,R3,R4,R5>
	BRW	AUDIO_EXIT			;S FF; Complete an audio SCSI function

;+
; issue the format 0 "read", this is done by combining format 1 and format
; 2 data.
;-
80$:	BSBW	READ_CD_SUBQ			;S FF; Get format 1 data.
	BLBC	R0,AUDIO_EXIT			;S FF; On error, complete audio function

;+
; Move READ SUBQ data from intermediate buffer to users destination buffer.
;-
	MOVL	SCDRP$L_IRP(R5),R1		;S FF; Get original IRP Address
	MOVL	IRP$L_SEGVBN(R1),R2		;S FF; Get start address of the AUCB.
	MOVL	CD_DEST_BUF_CNT(R2),R2		;S FF; Get size of destination buffer.
	MINUM	SCDRP$L_TRANS_CNT(R5),R2	;S FF; Minimize between these two values.
	PUSHR	#^M<R0,R2,R3,R4,R5>
	MOVL	IRP$L_EXTEND(R1),R1		;S FF; Get the linked/extended IRP address
	TSTL	IRPE$L_DRIVER_P0(R1)		;S FF: Is this the sense buffer IRPE?
	BEQL	90$				;S FF; No, it's the destination buffer
	MOVL	IRPE$L_EXTEND(R1),R1		;S FF; Get address of destination buffer
90$:	MOVC3	SCDRP$L_TRANS_CNT(R5),-		;S FF; Copy the number of bytes received
		 @SCDRP$L_SVA_USER(R5),-	;S FF; Copy from Nonpaged, to the
		  @IRP$L_SEGVBN(R1)		;S FF; start of the mapped user buffer
	MOVL	R3,R1				;S FF; copy addr of next byte in buffer
	POPR	#^M<R0,R2,R3,R4,R5>
	SUBL	SCDRP$L_TRANS_CNT(R5),R2	;S FF; Subtract what's already transfered
						;S FF;	from size of users buffer
	PUSHR	#^M<R1,R2>
	CLEANUP_CMD				;S FF; cleanup from READ SUBQ command
	BSBW	FETCH_MCN			;S FF; get format 2 data
	POPR	#^M<R1,R2>
	BLBC	R0,AUDIO_EXIT_NO_CMD		;S FF;	R0 with status
	PUSHR	#^M<R0,R3,R4,R5>
	MOVL	#SCSI$SUBQ$C_MCN_LENGTH,R0	;S FF; get size of stored MCN data
	MINUM	R0,R2				;S FF; Minimize between these two values.
	MOVC3	R0,UCB$B_MCN_SCDATA(R3),(R1)	;S FF; Copy the number of bytes received from
						;S FF; from UCB to the remainder of user's buffer
	POPR	#^M<R0,R3,R4,R5>
	MOVL	#SS$_NORMAL,R0			;S FF; return success
	BRW	AUDIO_EXIT_NO_CMD		;S FF;

;+
; The GET_TOC AUCB function does a SCSI READ command and returns the Table Of Contents
; (TOC) data to the application. From this information the appliaction can determine
; the starting location and data type for each track on the CD-ROM.
;-
CD_READ_TOC:					;S FF; Audio CDROM control - Read TOC

	MOVAL	CMD_CD_READ_TOC,R2		;S FF; Address of READ TOC command
	SETUP_CMD				;S FF; Set Command
	ADDL3	#4,SCDRP$L_CMD_PTR(R5),R0	;S FF; Get address of SCSI CDB (Command).
						;S FF; The CDB doesn't start at 0, but 4.
	MOVL	SCDRP$L_MEDIA(R5),R1		;S FF; Restore address of AUCB.
	CMPB	CD_ARG2(R1),#99			;S FF; Can't have more than 99 tracks, now!
	BGTRU	IO_BADPARM			;S FF;	then error.
	MOVB	CD_ARG2(R1),-			;S FF; Write Starting Track into
		CMD_CD_READ_TOC$B_START_TRACK(R0);S FF; the SCSI CDB (command)
	CMPB	CD_ARG1+0(R1),#1		;S FF; If greater than 1
	BGTRU	IO_BADPARM			;S FF;	error
	ASHL	#1,CD_ARG1(R1),R1		;S FF; Get LBA/MSF bit in left bit position
	BISB	R1,CMD_CD_READ_TOC$R_FLAGS(R0)	;S FF; Select MSF or LBA address format.
	SEND_COMMAND_ORDERED			;S FF; Send the SCSI command
	BLBC	R0,AUDIO_EXIT			;S FF; On error, complete audio function

;+
; Move TOC data from intermediate buffer to the destination buffer.
;-
	PUSHR	#^M<R0,R1,R2,R3,R4,R5>
	MOVL	SCDRP$L_IRP(R5),R1		;S FF; Get original IRP Address
	MOVL	IRP$L_SEGVBN(R1),R2		;S FF; Get start address of the AUCB.
	MOVL	CD_DEST_BUF_CNT(R2),R2		;S FF; Get size of destination buffer.
	MINUM	SCDRP$L_TRANS_CNT(R5),R2	;S FF; Minimize between these two values.
	MOVL	IRP$L_EXTEND(R1),R1		;S FF; Get the linked/extended IRP address
	TSTL	IRPE$L_DRIVER_P0(R1)		;S FF: Is this the sense buffer IRPE?
	BEQL	100$				;S FF; No, it's the destination buffer
	MOVL	IRPE$L_EXTEND(R1),R1		;S FF; Get address of destination buffer
100$:	MOVC3	SCDRP$L_TRANS_CNT(R5),-		;S FF; Copy the number of bytes received
		 @SCDRP$L_SVA_USER(R5),-	;S FF; Copy from Nonpaged, to the
		 @IRP$L_SEGVBN(R1)		;S FF; start of the mapped user buffer
	POPR	#^M<R0,R1,R2,R3,R4,R5>
	BRW	AUDIO_EXIT			;S FF; Complete an audio SCSI function

;+
; The GET_VOLUME AUCB function does a SCSI MODE SENSE command and returns the Sense Data
; containing the channel information to the application, from this the current volume
; can be determined.
;-
CD_GET_VOLUME:					;S FF; Audio CDROM control - Get Volume

;+
; Before doing a mode select (set volume) do a mode sense to get the current
; values of the parameters.
;-
	BSBW	CD_GET_SENSE			;S FF; Issue CD-ROM specific Mode Sense
	BLBC	R0,AUDIO_EXIT			;S FF; Branch on error

;+
; Copy the volume and channel control from the Sense data buffer to the
; AUCB.
;-
	PUSHL	R0				;S FF; Save CD_GET_SENSE status 
	MOVL	SCDRP$L_SVA_USER(R5),R0		;S FF; Get address of MODE SENSE Data
	MOVL	SCDRP$L_MEDIA(R5),R1		;S FF; Get copy of AUCB address from SCDRP.

	ASSUME	SCSI$ACP$S_CHANNEL_VOLUME EQ <4*2>

	MOVL    SENSE_VOLUME(R0), -		;S FF; Read the channel 0 volume out of the 
		CD_ARG1(R1)			;S FF; MODE SENSE data

	MOVL    SENSE_VOLUME+4(R0), -		;S FF; Read the channel 1 volume out of the 
		CD_ARG1+4(R1)			;S FF; MODE SENSE data

	POPL	R0				;S FF; Restore CD_GET_SENSE status
	BRW	AUDIO_EXIT			;S FF; Complete an audio SCSI function

;+
; The SET_VOLUME AUCB function does a SCSI MODE SENSE, followed by a MODE SELECT command
; and sets the volume and channel information on the target device.
;-
CD_SET_VOLUME:					;S FF; Audio CDROM control - Set Volume

;+
; Before doing a mode select (set volume) do a mode sense to get the current
; values of the parameters.
;-
	BSBW	CD_GET_SENSE			;S FF; Issue CD-ROM specific Mode Sense
	BLBC	R0,AUDIO_EXIT			;S FF; On error, complete audio function

;+
; Since we want to reuse this SCDRP and call SETUP_CMD, deallocate the
; previously allocated Mode Sense COMMAND buffer. SETUP_CMD will allocate
; a new one for the MODE SELECT command.
;
; Note: We are using the previously allocated MODE SENSE buffer as our MODE
; Select buffer. CLEANUP_CMD will deallocate this buffer when we are done.
;-
	SPI$CMD_BUFFER_DEALLOC			;S FF; Deallocate the command buffer

	MOVAL	CMD_CD_MODE_SELECT,R2		;S FF; Address of MODE SENSE
	SETUP_CMD				;S FF; Set Command
	ADDL3	#4,SCDRP$L_CMD_PTR(R5),R0	;S FF; Get address of SCSI CDB (Command).
						;S FF; The CDB doesn't start at 0, but 4.

;+
; We must force this request to be a write request, since we are bye-passing
; some of the SETUP_CMD setup to allow us to use the MODE SENSES buffer.
;-
	BICL	#IRP$M_FUNC,SCDRP$IS_STS(R5)	;S FF; Mode Select is "write" operation,
						;S FF; with data going to the target.

;+
; Now that all the setup is done, fixup the MODE SENSE bytes to be exactly
; the way we need them.
;
; Copy the volume and channel control from the AUCB into the CCB.
;-

	MOVL	SCDRP$L_SVA_USER(R5),R1		;S FF; Get address of MODE SENSE Data
	MOVB	SCSI$MPH6$B_DATA_LENGTH(R1),-	;S FF; Set Mode Select Data Length to match
		CMD_MODE_SELECT$R_ALLOC_LENGTH(R0) ;S FF; that returned from Mode Sense
	INCB	CMD_MODE_SELECT$R_ALLOC_LENGTH(R0) ;S FF; Add in the length byte itself

	ASSUME	SCSI$ACP$S_CHANNEL_VOLUME EQ <4*2>

	MOVL	SCDRP$L_MEDIA(R5),R0		;S FF; Restore address of AUCB.
	MOVL	CD_ARG1(R0),SELECT_VOLUME(R1)	;S FF; Copy Volume and port info
	MOVL	CD_ARG1+4(R0),SELECT_VOLUME+4(R1) 

	SEND_COMMAND_ORDERED			;S FF; Send the SCSI command
	BRW	AUDIO_EXIT			;S FF; Complete an audio SCSI function

;+
; Subroutine used to get the volume information by issuing a mode sense
; command for the Audio Control Parameters.
;-
; Context:
;
;	SCDRP thread
;	IPL = Fork
;	Fork lock held
;
; Inputs:
;
;	R3 - UCB address
;	R4 - SPDT address
;	R5 - SCDRP address
;
; Outputs:
;	R0 - Status
;	R1,R2 - destroyed

CD_GET_SENSE::					;S FF;	Audio CDROM get parameter code
	.JSB_ENTRY INPUT=<R3,R4,R5>,OUTPUT=<R0>,SCRATCH=<R1,R2>

	MOVAL	CMD_MODE_SENSE,R2		;S FF; Address of MODE SENSE
	SETUP_CMD				;S FF; Set Command
	ADDL3	#4,SCDRP$L_CMD_PTR(R5),R0	;S FF; Get address of SCSI CDB (Command).
						;S FF; The CDB doesn't start at 0, but 4.
	MOVL	SCDRP$L_MEDIA(R5),R1		;S FF; Restore address of AUCB.

; For now just get the value for the CDROM page, later get it from the
; AUCB.
	MOVB	#SCSI$C_AUDIO_CONTROL_PAGE,-	;S FF; Interested in CDROM Audio Ctrl Page
		CMD_MODE_SENSE$R_PAGE_CODE(R0)

	SCSI_RESPONSE_LENGTH = -
		SCSI$MPH6$S_MODE_PARAM_HDR_6 +-	; Mode Parameter Header
		SCSI$MPBD$S_MODE_PARAMETER +-	; Mode Parameter Block
		SCSI$ACP$S_AUDIO_CONTROL	; Audio Control Block
	MOVB	#SCSI_RESPONSE_LENGTH,-		;S FF; Number of Mode Sense bytes to receive.
		CMD_MODE_SENSE$R_ALLOC_LENGTH(R0) ;S FF; In response to MODE SENSE Command
	SEND_COMMAND_ORDERED			;S FF; Send the SCSI command.
10$:	RSB					;S FF; Status checked by caller.

; Note:  IO_BADPARM may be reached from several paths with varying stack 
; contents (possibly due to nested JSBs) but the eventual RET will clean up
; the stack correctly in all cases.

IO_BADPARM::	.GLOBAL_LABEL

	MOVZBL	#SS$_BADPARAM,R0		;S FF; Set bad parameter status
	BRW	AUDIO_EXIT			;S FF;


	.PAGE
	.SBTTL	AUDIO_MAP_PAGE - Lock down and double map user buffer
;+
; AUDIO_MAP_PAGE:
;
; This routine locks the users buffer down, allocates the PTE's required to
; double map the user buffer and saves this mapping information in the IRP(E).
; The STARTIO routine will use the information in the IRP(E) to move data to
; or from the users buffer.
;
; Context:
;
;	User thread
;	IPL = ASTDEL
;	Locks - none
;
; Inputs:
;
;	R0 - P0 address of buffer
;	R1 - Byte count
;	R3 - IRP(E) that holds double-mapping information about 
;	     this P0 buffer. 
;	Other registers have normal FDT context.
;
; Outputs:
;
;	IRP$L_WIND   -	SVAPTE of mapped  buffer.
;	IRP$L_OBCNT  -	Number of PTE's allocated.
;	IRP$L_SEGVBN -	The SVA of the first PTE that maps the Sense buffer.
;
;	R0	- Status
;	R1,R2,R9-R11	- Destroyed
;	R3	- Current IRP(E) address, preserved
;	All other registers preserved
;-
	.ENABLE		LSB
AUDIO_MAP_PAGE::				;U A: Lock down and double map user buffer
	.JSB_ENTRY INPUT=<R0,R1,R3>,OUTPUT=<R0>,SCRATCH=<R1,R2,R9,R10,R11>,PRESERVE=<R3>

	MOVAL	LOCK_ERROR,R2           	;U A; Set up error callback routine

	PUSHL	R2				;U A; Pass the error callback routine,
	PUSHL	R1				;U A; Pass length of transfer in bytes,
	PUSHL	R0				;U A; Pass pointer to I/O buffer,
	PUSHL	R6				;U A; Pass pointer to CCB,
	PUSHL	R5				;U A; Pass pointer to UCB,
	PUSHL	R4				;U A; Pass pointer to PCB,
	PUSHL	R3				;U A; Pass pointer to IRP(E).
	CALLS	#7,G^EXE_STD$MODIFYLOCK		;U A; Call the routine.
	.BRANCH_LIKELY
	BLBS	R0,5$				;U A; Success: YES - Cont. FDT processing.
	RSB					;U A; Return. Callback & ABORTIO called.
						;U A; Now preserve JSB interface outputs. 
5$:	MOVL	IRP$L_SVAPTE(R3),R1		;U A; Get SVA of start of buffer in R1.
	MOVL	#5,R2				;U A; Indicate modify function.

;+
; To check to see how many PTE's to allocate.
; Do a sobgtr to loop through and copy all the pte's.
;-
10$:	MOVL	R1,R11				;U A; Save the SVAPTE for the AUCB
	MOVL	IRP$L_BCNT(R3),R9		;U A; get requested transfer byte count
	ADDL2	IRP$L_BOFF(R3),R9		;U A; Get offset plus count
	$BYTES_TO_PAGES -			;U A; Convert to page count
		SOURCE_BYTCNT=R9,-
		DEST_PAGCNT=R2,-
		ROUNDUP=YES			;U A; Round up
	JSB	G^LDR$ALLOC_PT			;U A; Allocate the SPT's
	BLBC	R0,80$				;U A; Can't map buffer, so return
						;U A; error (Let IOPOST unlock buffer).

15$:	MOVL	R1,IRP$L_WIND(R3)		;U A; Save SVAPTE of system buffer
	MOVL	R2,IRP$L_OBCNT(R3)		;U A; Save number of PTE's allocated

;+
; Now convert the SPTE address to a SVA address so that the startio routine
; can access the AUCB via this address.
;-
	MOVL	R1,R10				;U A; Save the SVAPTE of S0 buffer
	SUBL2	G^MMG$GL_SPTBASE,R1		;U A; Get offset into page table
	ASHL	G^MMG$GL_PTE_OFFSET_TO_VA,R1,R9	;U A; Save the S0 virtual address
	BISL2	IRP$L_BOFF(R3),R9		;U A; Add in byte offset of AUCB
	BISL	#VA$M_SYSTEM,R9			;U A; Set 80000000 bit for system address
	MOVL	R9,IRP$L_SEGVBN(R3)		;U A; SVA User buffer saved here

;+
; Now loop through all of the PTE's required to map the buffer and copy the
; the PTE's from user page tables into the system page tables.
;
; Remember that R11 has the SVAPTE for the AUCB.
;-
COPY_PTE:
	EVAX_LDQ R3,(R11)			;U A; Copy buffer's P0 PTE to R3

	ASSUME PTE$M_VALID EQ 1
	BLBC	R3,60$				;U A; Branch if PTE not valid
	EVAX_SRL R3,#PTE$V_PFN,R3		;U A; Else, shift PFN to low-order bits
	BRW	65$				;U A; Join common new PTE builder code
60$:	CALL_PTETOPFN SAVE_R0R1=YES		;U A; Create vaild PTE from invalid PTE.
65$:	EVAX_SLL R3,#PTE$V_PFN,R3		;U A; Get PFN in correct position
	EVAX_OR	 R3,-				;U A; Set software PTE bits
		#<PTE$M_VALID!PTE$C_KW!PTE$C_KOWN!PTE$M_ASM!PTE$M_WINDOW>,R3
	EVAX_STQ R3,(R10)			;U A; Store the new (S0) PTE
	TBI_SINGLE R9				;U A; Do TB invalidation
	ADDL	G^MMG$GL_PAGE_SIZE,R9		;U A; Point to next page
	ADDL	#PTE$C_BYTES_PER_PTE,R10	;U A; Point to next entry in S0 SPT
	ADDL	#PTE$C_BYTES_PER_PTE,R11	;U A; Point to next entry in P0 PTE
	SOBGTR	R2,COPY_PTE			;U A; Copy all required PTE's

80$:	RSB					;U A; AUDIO_MAP_PAGE

;+
; An error occured in CALL_MODIFYLOCK_ERR, we are called here as a coroutine,
; with an IRP(E) in R3.
;
; We need to clean up all allocated resources and rsb back to EXE$MODIFYLOCKR,
; which will unwind the stack, fail the I/O. 
; Eventually control returns to AUDIO_MAP_PAGE with status SS$_FDT_COMPL.
;-
LOCK_ERROR:
	$DRIVER_ERRRTN_ENTRY
	PUSHR	#^M<R0,R1>			;U A; Save I/O status   
	TSTL	IRP$L_SEQNUM(R3)		;U A; Is this an IRPE?
	BEQL	100$				;U A; Nope, this is the original IRP
	MOVL	IRPE$L_DRIVER_P1(R3),R3		;U A; Restore original IRP
	MOVL	R3,@ERRARG$_STATUS+4(AP)	;U A; Original IRP, not IRPE, must be input to ABORTIO


100$:	PUSHL	R2				;U A; Save R2
	MOVL	R3,R2				;U A; For AUDIO_CLEANUP
	JSB	AUDIO_CLEANUP			;U A; Free resources
	POPL	R2				;U A; Restore R2
	POPR	#^M<R0,R1>			;U A; Restore I/O status
	RET					;U A; Return to Coroutine, fail I/O,
						;U A; and exit FDT

	.DISABLE LSB


	.PAGE
	.SBTTL	AUDIO_CLEANUP	- Free any allocated PTE's
;+
; AUDIO_CLEANUP:
;
; Routine used by AUDIO code to free any resources that were allocated.
; Specifically, the routine will deallocate all of the PTE's that could 
; possibly be in use by the original IRP or the IRPE's;  it also unlocks any
; user buffers that were locked down.
;
; Note:  IOPOST is also capable of unlocking user buffers.  However, if
; MODIFYLOCK fails due to the need to pagefault, that particular kind
; of error will result in the IRP being discarded by the MODIFYLOCK error
; path without ever being sent to IOPOST.  So we will unlock the user
; buffer(s) here;  if the request does go through IOPOST, IOPOST will
; simply have less work to do.
;
; Context:
;
;	User thread
;	IPL = ASTDEL
;	Locks - none
;
; Inputs:
;	R2 = Original IRP.
;
;       Original IRP fields:
;		IRP$L_WIND	= SVAPTE of PTE's for the AUCB
;		IRP$L_OBCNT	= Number of PTE's for the AUCB
;		IRP$L_EXTEND	= Valid if IRPE used, points to first IRPE.
;
;	Fields from first IRPE (if any):
;		   IRP(E)$L_WIND =  SVAPTE of PTE's for Sense Buffer if specified.
;				    If no sense buffer , then this field contains the				  
;				    SVAPTE of PTE's for Destination Buffer.
;		   IRP(E)$L_OBCNT = Number of PTE's for Sense Buffer if specified.
;				    If no sense buffer , then this field contains the				  
;				    number of PTE's for Destination Buffer.
;		   IRP$L_EXTEND	 =  Valid if a second IRPE is used, in which case that 
;				    IRPE will always be used for the destination buffer.
;
;	Fields from second IRPE (if any):
;			IRP(E)$L_WIND	= SVAPTE of PTE's for Destination Buffer.
;			IRP(E)$L_OBCNT	= Number of PTE's for Destination Buffer.
;
; Outputs:
;	R0 = Return status.
;
;	
;	R2 = Original IRP address, preserved.
;-
	.ENABLE		LSB
AUDIO_CLEANUP::				;U A; Free any allocated PTE's

	.JSB_ENTRY INPUT=<R2>,SCRATCH=<R1>,PRESERVE=<R0,R2,R8,R9,R10>

	MOVL	R2,R10				;U A; Save original IRP address

; Loop through IRP and any IRPE's to deallocate PTE's.

10$:	MOVL	R2,R8				;U A; Save current IRP(E) to clean up
	MOVL	IRP$L_WIND(R2),R1		;U A; Get Address of PTE's Allocated
	BEQL	50$				;U A; If none allocated, check for others
	MOVL	IRP$L_OBCNT(R2),R0		;U A; Get number of PTE's.
	BEQL	50$				;U A; If none allocated, check for others
	MOVL	IRP$L_SEGVBN(R2),R9		;U A; Get S0 SVA of doubly-mapped buffer
						;U A; as saved by FDT call

						;U A; Free any allocated PTE's
20$:	EVAX_STQ R31,(R1)			;U A; Clear the PTE
	TBI_SINGLE R9				;U A; Invalidate Single TBI
	ADDL	G^MMG$GL_PAGE_SIZE,R9		;U A; Point to next page
	ADDL	#PTE$C_BYTES_PER_PTE,R1		;U A; Point to next entry in P0 PTE
	SOBGTR	R0,20$				;U A; Loop through all PTE's

	MOVL	R8,R2				;U A; Restore current IRP(E) address
	MOVL	IRP$L_WIND(R2),R1		;U A; Get Address of PTE's Allocated for AUCB
	MOVL	IRP$L_OBCNT(R2),R2		;U A; Get number of PTE's for AUCB
	JSB	G^LDR$DEALLOC_PT		;U A; Free the allocated PTE's for AUCB
	BLBS	R0,50$				;U A; If success continue, otherwise
	BUG_CHECK INCONSTATE,FATAL		;U A; BUGCHECK, this can never happen...

;+
; Check for other PTE's allocated.  Only IRPE's have additional PTE's allocated.
;-
50$:	MOVL	R8,R2				;U A; Restore current IRP(E) address
	CLRL	IRP$L_WIND(R2)			;U A; Zero S0 SVAPTE for buffer
	CLRL	IRP$L_OBCNT(R2)                 ;U A; Zero # of PTE's allocated
	CLRL	IRP$L_SEGVBN(R2)                ;U A; Zero S0 VA of buffer
	BBC	#IRP$V_EXTEND,IRP$L_STS(R2),-	;U A; Is there an IRPE?
		100$				;U A; If not, exit
	MOVL	IRP$L_EXTEND(R2),R2		;U A; Get IRPE Address
	BRW	10$				;U A; Free this IRPE's PTE's

; Previously allocated PTE's have now been deallocated.  The other part of 
; cleanup is to unlock any previously locked user buffers.

100$:	PUSHL	R10				;U A; Pointer to original IRP.
	CALLS	#1,G^EXE_STD$LOCK_ERR_CLEANUP	;U A; Call the routine.
	RSB					;U A;
	.DISABLE LSB

	.PAGE
	.SBTTL	AUDIO_EXIT	- Audio Function Completion Code
;+
; AUDIO_EXIT -
;
; All audio functions that are not aborted during FDT processing exit the
; driver by executing this code. After a command has been sent, error
; recovery has been executed, SCSI status copied to the AUCB then deallocate
; the SCDRP and any other resources allocated for this request. The audio exit
; code is fairly complex due to the fact that we are double mapping and copying
; between three distinct user buffers.
;
; The original IRP (IRP pointed to by SCDRP$L_IRP) contains the mapping
; information for the AUCB. If a user destination or sense buffer is required
; then IRP extensions are allocated in the FDT code to contain the mapping
; information for these buffers. This routine will deallocated all the
; resources allocated for use in the original IRP and its one or two IPRE's.
;
; Context:
;
;	SCDRP thread
;	IPL = Fork
;	Fork lock held
;
; Inputs:
;	R5 = SCDRP for this request.
;	R3 = UCB
;	R0 = VMS return status code
;
;
; Outputs:
;	AUCB = User Audio Control Block
;		CD_COMMAND_STATUS	= VMS O.S. Return status
;		CD_SCSI_STATUS		= SCSI command status and Sense Key
;		CD_SENSE_ADDR		= Sense data buffer
;		CD_SENSE_TRANS_CNT	= Sense data transfer count
;		CD_DEST_BUF_TRANS_CNT	= Number of bytes in destination buf
;-
	.ENABLE	LSB
AUDIO_EXIT:					;S FF; Audio Function Completion Code

	CLEANUP_CMD				;S FF; Cleanup from the SCSI command

AUDIO_EXIT_NO_CMD:

;+
; Now update the AUCB with the VMS Status, SCSI Status and Transfer Count.
;-
	PUSHR	#^M<R2,R7>			;S FF; Save some registers
	MOVL	SCDRP$L_MEDIA(R5),R7		;S FF; Get copy of AUCB address from SCDRP.
	MOVL	SCDRP$L_TRANS_CNT(R5),-		;S FF; Get Transfer Count in Destination
		 CD_DEST_BUF_TRANS_CNT(R7)	;S FF; XFER count field in the AUCB.
	TSTL	SCDRP$L_STS_PTR(R5)		;S FF; Test for existance of status buffer.
	BEQL	10$				;S FF; If no SCSI status buffer skip this..
	MOVZBL	@SCDRP$L_STS_PTR(R5),-		;S FF; Copy SCSI Status to AUCB.
		 CD_SCSI_STATUS(R7)		;S FF;
10$:	MOVZWL	R0,CD_COMMAND_STATUS(R7)	;S FF; Copy VMS status to AUCB.

;+
; If there was a check condition as the result of an audio command, copy
; the sense key and additional sense data to the AUCB from the UCB.
; During SEND_COMMAND, if a check condition occurs a request sense is
; issued to the target and the sense data is copied to the UCB.
;-
	ASSUME	 SCSI$C_GOOD EQ 0
	TSTB	CD_SCSI_STATUS(R7)		;S FF; Was status success?
	BLEQ	50$				;S FF; If yes continue, else get sense data

	MOVZBW	SCDRP$B_SENSE_KEY(R5),-		;S FF; Copy the Sense key to the AUCB.
		CD_SCSI_STATUS+2(R7)		;S FF;
	MOVL	SCDRP$L_IRP(R5),R2		;S FF; Get original IRP
	MOVL	IRP$L_EXTEND(R2),R2		;S FF; Get any IRP extension
	BEQL	50$				;S FF; No IRPE, therefore no sense buffer
	TSTL	IRPE$L_DRIVER_P0(R2)		;S FF; Is this a sense buffer IRPE?
	BEQL	50$				;S FF; If no buffer don't copy sense data.

	PUSHR	#^M<R0,R1,R2,R3,R4,R5>          ;S FF; Set up to copy sense data
	MOVL	SCDRP$PS_SENSE_BUFFER(R5),R0	;S FF; Get address of sense data
	MOVZBL	7(R0),R1			;S FF; Get count of number of sense bytes:
						;S FF; The length field is byte 7 of the sense_data 
						;S FF; and represents the number of bytes after itself.  
	ADDL2	#8,R1				;S FF; So add 8 to get total length.
	MOVL	CD_SENSE_CNT(R7),R5		;S FF; Get size of user sense buffer
	MINUM	R1,R5				;S FF; Minimize between these two values
	MOVL	R1,CD_SENSE_TRANS_CNT(R7)	;S FF; Return Count of sense bytes to AUCB
	MOVC3	R1,(R0),@IRP$L_SEGVBN(R2)	;S FF; Copy SENSE Data to user buffer to
						;S FF; SVA of SENSE buffer in SEGVBN.
	POPR	#^M<R0,R1,R2,R3,R4,R5>

50$:	POPR	#^M<R2,R7>			;S FF; Restore saved registers

;+
; Unlock any locked pages and free any allocated PTE's, then clear any fields
; in the IRP that might confuse IOPOST and return status in R0 and R1, which
; will be copied to the IOSB.
;-
AUDIO_BAD_CMD_EXIT:
	PUSHR	#^M<R0,R2>
	MOVL	SCDRP$L_IRP(R5),R2		;S FF; Get original IRP address
	BSBW	AUDIO_CLEANUP			;S FF; Free resources
	MOVL	SCDRP$L_IRP(R5),R2		;S FF; Get original IRP address
	CLRL	IRP$L_MEDIA(R2)			;S FF; Clear for IOPOST
	POPR	#^M<R0,R2>			;S FF; Restore reg's

	CLR_SINGLE	AUDIO,R3		;S FF; Audio request is complete

	CLRL	R1				;S FF; Clear R1, no additional info.
	BRW	COMPLETE_IO			;S FF; Complete this I/O function
	.DISABLE LSB

	.SBTTL	READ_CD_MCN - reads CD-ROM Sub-Channel to get the Media Cat. #
;+
;
; This routine sends a READ SUB-CHANNEL command requesting the 02h Data
; format code, Media Catalog Number, be returned for a CD-ROM media.
; This command causes the CD to stop playing audio, therefore it is only
; issued in the following cases (all indicated by the UCB$M_CD_VALID flag
; being clear):
;
;	1) When the "mount" of the device occurs (called from PACKACK
;	   routine).
;
;	2) If the CD_READ_SUB format 2 command is issued and a UNIT ATTENTION
;	   Sense key indicating a media change has occured was returned for
;	   a previous SCSI command.
;
; Context:
;
;	SCDRP thread
;	IPL = Fork
;	Fork lock held
;
; Inputs:
;
;	R3	- UCB address
;	R4	- PDT address
;	R5	- SCDRP address
;
; Outputs:
;
;	R1,R2	- Destroyed
;	R0	- Status
;
;	UCB$B_MCN_DATA - filled in
;	UCB$M_CD_VALID - set to indicate MCN data valid if command succeeded
;
;-
READ_CD_MCN::					;S FF; Read CD-ROM Sub-Channel
	.JSB_ENTRY INPUT=<R3,R4,R5>,OUTPUT=<R0>,SCRATCH=<R1,R2>

	BICL	#UCB$M_CD_VALID,-		;S FF; Indicate stored sub-channel data for
		UCB$L_DK_FLAGS(R3)		;S FF;	CD-ROM is invalid.
	MOVAL	CMD_CD_READ_SUB,R2		;S FF; Address of READ SUB command
	SETUP_CMD				;S FF; Set Command
	ADDL3	#4,SCDRP$L_CMD_PTR(R5),R0	;S FF; Get address of SCSI CDB (Command).
						;S FF; The CDB doesn't start at 0, but 4.
	MOVB	#SCSI$SUB$C_MCN,-		;S FF; Write Sub Channel Data Format
		CMD_CD_READ_SUB$B_SUB_DATA_FMT(R0); S FF; MCN = Media Catalog Number (UPC/Bar Code)
	SEND_COMMAND_ORDERED			;S FF; Send the SCSI command
	BLBC	R0,20$				;S FF; Branch on error

;+
; Copy Subchannel format 2 data to UCB
;-
	PUSHR	#^M<R0,R1,R2,R3,R4,R5>
	MOVL	#SCSI$SUBQ$C_MCN_LENGTH,R2	;S FF; Get size of destination buffer.
	MINUM	SCDRP$L_TRANS_CNT(R5),R2	;S FF; Minimize between these two values.
	MOVC3	SCDRP$L_TRANS_CNT(R5),-		;S FF; Copy the number of bytes received
		 @SCDRP$L_SVA_USER(R5),-	;S FF; Copy from Nonpaged, to the
		  UCB$B_MCN_SCDATA(R3)		;S FF; start of the area in the UCB
	POPR	#^M<R0,R1,R2,R3,R4,R5>
	BISL	#UCB$M_CD_VALID,-		;S FF; Indicate stored sub-channel data for
		UCB$L_DK_FLAGS(R3)		;S FF;	CD-ROM is valid.
	MOVL	#SS$_NORMAL,R0			;S FF; return success
20$:	CLEANUP_CMD				;S FF; cleanup from SCSI command
	RSB					;S FF;


	.PAGE
	.SBTTL	READ_SUB_CHANNEL
;+
;
; This routine sends a READ SUB-CHANNEL command requesting the 01h Data
;
; Context:
;
;	SCDRP thread
;	IPL = Fork
;	Fork lock held
;
; Inputs:
;
;	R3	- UCB address
;	R4	- PDT address
;	R5	- SCDRP address
;
; Outputs:
;
;	R1,R2	- Destroyed
;	R0	- Status
;
;-
READ_CD_SUBQ::					;S FF; READ SUB-CHANNEL Data
	.JSB_ENTRY INPUT=<R3,R4,R5>,OUTPUT=<R0>,SCRATCH=<R1,R2>

;+
; Read format #1 sub-channel, The Q-Sub channel with current location info.
;-
	MOVAL	CMD_CD_READ_SUB,R2		;S FF; Address of READ SUB command
	SETUP_CMD				;S FF; Set Command
	ADDL3	#4,SCDRP$L_CMD_PTR(R5),R0	;S FF; Get address of SCSI CDB (Command).
						;S FF; The CDB doesn't start at 0, but 4.
	MOVL	SCDRP$L_MEDIA(R5),R1		;S FF; Restore address of AUCB.
	MOVB	#SCSI$SUB$C_CD_ROM_POSITION,-	;S FF; Write Sub Channel Data Format
		CMD_CD_READ_SUB$B_SUB_DATA_FMT(R0) ;S FF; CD-ROM Current Position
	CMPB	CD_ARG1+0(R1),#1		;S FF; If greater than 1
	BGTRU	IO_BADPARM			;S FF; error, which branches back to STARTIO
	ASHL	#1,CD_ARG1+0(R1),R1		;S FF; Get LBA/MSF bit in right bit position
	BISB	R1,CMD_CD_READ_SUB$R_FLAGS(R0)	;S FF; Select MSF or LBA address format.

	; Note:  this path may branch back to STARTIO, which will
	;	 either RET or RET under JSB.
	;
	SEND_COMMAND_ORDERED			;S FF; Send the SCSI command
	RSB					;S FF;

	.PAGE
	.SBTTL	FETCH_MCN
;+
; This routine gets the SUB-CHANNEL format 2 data, by issuing a SCCI
; READ SUB-CHANNEL command requesting the 02h Data if the data in the
; UCB is invalid.
;
; Context:
;
;	SCDRP thread
;	IPL = Fork
;	Fork lock held
;
; Inputs:
;
;	R3	- UCB address
;	R4	- PDT address
;	R5	- SCDRP address
;
; Outputs:
;
;	R1,R2	- Destroyed
;	R0	- Status
;
;-
FETCH_MCN::					;S FF; Fetch SUB-Channel MCN data
	.JSB_ENTRY INPUT=<R3,R4,R5>,OUTPUT=<R0>,SCRATCH=<R1,R2>

;+
; We issue a extra read_sub command here to guarantee that the CD has not been
; changed. This command will clear the CD_VALID flag in the UCB if the a unit
; attention occurs during this commmand.
;-
	BSBW	READ_CD_SUBQ			;S FF; Get format 1 data.
	CLEANUP_CMD				;S FF; toss data and cleanup
	BLBC	R0,30$				;S FF; On error exit

	MOVAB	MCN_DEF_STATUS,-		;S FF; Assume no SCSI command and setup
		 SCDRP$L_STS_PTR(R5)		;S FF;	status buffer in SCDRP here.
	MOVB	#SCSI$C_GOOD,MCN_DEF_STATUS	;S FF; Assume status of sucesss
	BITL	#UCB$M_CD_VALID,-		;S FF; Is the stored sub-channel data for
		UCB$L_DK_FLAGS(R3)		;S FF;	CD-ROM is valid?
	BNEQ	30$				;S FF; If NEQ yes just return

	BSBW	READ_CD_MCN			;S FF; read format #2 data to UCB
30$:	RSB					;S FF;

;+=

	.PAGE
	.SBTTL	+
	.SBTTL	+ SCSI COMMAND EXECUTION ROUTINES
	.SBTTL	+
	.SBTTL	INQUIRY		- Send an inquiry command
;+
; INQUIRY
;
; This routine sends an inquiry command to the target. At least 36 bytes of
; inquiry data are expected to be returned. The peripheral device type field
; is check to ensure the target is a disk-like device. The product ID field
; is used to determine the device type. In addition, several sanity checks are
; made on the inquiry data.
;
; Context:
;
;	SCDRP thread
;	IPL = Fork
;	Fork lock held
;
; Inputs:
;
;	R3	- UCB address
;	R4	- PDT address
;	R5	- SCDRP address
;
; Outputs:
;
;	R0	- Status
;	R1,R2	- Destroyed
;-

INQUIRY::					;S FF;	Send an inquiry command
	.JSB_ENTRY INPUT=<R3,R4,R5>,OUTPUT=<R0>,SCRATCH=<R1,R2>,PRESERVE=<R7,R8>

	CLRL	UCB$Q_DEVDEPEND2+0(R3)		;S FF; Invalidate GETDVI-visible field
	MOVAL	CMD_INQUIRY,R2			;S FF; Address of INQUIRY command
	SETUP_CMD				;S FF; Perform setup for SCSI command
	MOVL	#SCDRP$K_QCHAR_NOT_QUEUED,-	;S FF; Send this as an ordered command.
		SCDRP$IS_QUEUE_CHAR(R5) 
	PUSHL	R5				;S FF; 
	PUSHL	R4				;S FF; 
	PUSHL	R3				;S FF; 
	CALLS	#3,SEND_COMMAND			;S FF; Send the SCSI command
	BLBC	R0,30$				;S FF; Branch on error
	CMPL	SCDRP$L_TRANS_CNT(R5),-		;S FF; Sufficient inquiry data returned?
		#SCSI$INQ$S_INQUIRY_DATA	;S FF;
	BLSSU	40$				;S FF; Branch if not
	MOVL	SCDRP$L_SVA_USER(R5),R0		;S FF; Get address of inquiry data

	ASSUME	SCSI$INQ$S_PRODUCT_REVISION EQ 4
	MOVL	SCSI$INQ$B_PRODUCT_REVISION(R0),-;S FF; Save hardware revision level
		UCB$L_HW_REV(R3)
	ASSUME	UCB$B_DEVCLASS+1 EQ UCB$B_DEVTYPE
	CLRW	UCB$B_DEVCLASS(R3)		;S FF; Assume unknown device class and type
	BICL2	#<UCB$M_WORM+-			;S FF; Some optical device can change their
		  UCB$M_OPTICAL>,-		;S FF; type based on media - make sure
		  UCB$L_DK_FLAGS(R3)		;S FF; that only one of these gets set
	EXTZV	#SCSI$INQ$V_QUALIFIER,-		;S FF; Get peripheral qualifier
		#SCSI$INQ$S_QUALIFIER,-		;S FF;
		SCSI$INQ$R_PERIPHERAL(R0),R1	;S FF;
	BNEQ	40$				;S FF; If not 0, no device here
	EXTZV	#SCSI$INQ$V_DEVICE_TYPE,-	;S FF; Get peripheral device type
		#SCSI$INQ$S_DEVICE_TYPE,-
		SCSI$INQ$R_PERIPHERAL(R0),R1
	MOVB	R1, UCB$Q_DEVDEPEND2+1(R3)	;S FF; Make device type GETDVI-visible
	CMPB	R1,#SCSI$C_DISK			;S FF; Is this a SCSI disk device?
	BEQL	10$				;S FF; Branch if so
	CMPB	R1,#SCSI$C_WORM			;S FF; Or a write-once read-multiple device?
	BNEQ	5$				;S FF; Branch if not
	BISL2	#UCB$M_WORM,UCB$L_DK_FLAGS(R3)	;S FF; Set a bit to indicate that it is a WORM
	BRB	10$				;S FF; Branch to continue disk device code
5$:	CMPB	R1,#SCSI$C_OPTICAL		;S FF; Or a optical device?
	BNEQ	7$				;S FF; Branch if not
	BISL2	#UCB$M_OPTICAL,-		;S FF; Set a bit to indicate that it is an
		 UCB$L_DK_FLAGS(R3)		;S FF;	optical device
	BRB	10$				;S FF; Branch to continue disk device code
7$:	CMPB	R1,#SCSI$C_CDROM		;S FF; Or a CDROM-like device
	BNEQ	40$				;S FF; Branch if not, illegal device type
	BISL	#UCB$M_CDROM,-			;S FF; Indicate CDROM-like device
		UCB$L_DK_FLAGS(R3)		;S FF;
	BISL2	#UCB$M_HWL,-			;S FF; This a read-only device so set
		UCB$L_DK_FLAGS(R3)		;S FF; the hardware write-lock bit
	BBSS	#DEV$V_SWL,-			;S FF; This a read-only device so set
		UCB$L_DEVCHAR(R3),10$		;S FF; the software write-lock bit
10$:	MOVB	#DC$_DISK,-			;S FF; Indicate that this a disk device
		UCB$B_DEVCLASS(R3)		;S FF;

	MOVB	#1, UCB$Q_DEVDEPEND2+2(R3)	;S FF; Make assumed RMB GETDVI-visible
	BBSS	#UCB$V_REMOVABLE,-		;S FF; Assume drive has removable media
		UCB$L_DK_FLAGS(R3),15$		;S FF;
	ASSUME	SCSI$INQ$M_RMB EQ <^X80>
15$:	TSTB	SCSI$INQ$R_DEVICE_TYPE_MOD(R0)	;S FF; Is this a removable media device?
	BLSS	20$				;S FF; Branch if so
	BBCC	#UCB$V_REMOVABLE,-		;S FF; Clear the removable media bit
		UCB$L_DK_FLAGS(R3),17$		;S FF; in the UCB
17$:	MOVB	#0, UCB$Q_DEVDEPEND2+2(R3)	;S FF; Make RMB GETDVI-visible
20$:	EXTZV	#SCSI$INQ$V_ANSI_VERSION,-	;S FF; Get response data format
		#SCSI$INQ$S_ANSI_VERSION,-	;S FF;
		SCSI$INQ$R_VERSION(R0),R1	;S FF;
	MOVB	R1, UCB$B_SCSI_VERSION(R3)	;S FF; Save for future checks
	CMPB	R1,#SCSI$C_ANSI_SCSI_1		;S FF; Does drive conform to CCS?
	BEQL	25$				;S FF; Branch if so
	CMPB	R1,#SCSI$C_ANSI_SCSI_2		;S FF; Does drive conform to SCSI-2?
	BNEQ	40$				;S FF; Branch if not
25$:	CMPB	SCSI$INQ$B_ADD_LENGTH(R0),#^X1F	;S FF; Valid additional length field?
	BLSSU	40$				;S FF; Branch if not
	BSBW	GET_DEVICE_TYPE			;S FF; Determine device type from Vendor
	MOVB	#1, UCB$Q_DEVDEPEND2+0(R3)	;S FF; Validate GETDVI-visible field
	MOVZWL	#SS$_NORMAL,R0			;S FF; Set success status

30$:	CLEANUP_CMD				;S FF; Cleanup from the SCSI command
	RSB					;S FF; Return to caller

40$:	LOG_ERROR -				;S FF; Log an invalid inquiry data error
		TYPE=INV_INQUIRY_DATA,-
		VMS_STATUS=#SS$_DRVERR,-
		UCB=R3,-
		SCDRP=R5
	MOVZWL	#SS$_DRVERR,R0			;S FF; Set bad status
	BRB	30$				;S FF; Use common exit

	.PAGE
	.SBTTL	TEST_UNIT_READY	- Send a test unit ready command
;+
; TEST_UNIT_READY
;
; This routine sends a test unit ready command to the target to determine
; whether the device is spun up and ready.
;
; Context:
;
;	SCDRP thread
;	IPL = Fork
;	Fork lock held
;
; Inputs:
;
;	R3	- UCB address
;	R4	- PDT address
;	R5	- SCDRP address
;
; Outputs:
;
;	R0	- Status
;	R1,R2	- Destroyed
;	All other registers preserved
;-

TEST_UNIT_READY::				;S FF; Send a test unit ready command
	.JSB_ENTRY INPUT=<R3,R4,R5>,OUTPUT=<R0>,SCRATCH=<R1,R2>

	BISB	#UCB$M_SPINUP_INPROG,-		;S FF; Set spinup-in-progress
		UCB$L_DK_FLAGS(R3)		;S FF;
	MOVAL	CMD_TEST_UNIT_READY,R2		;S FF; Address of TEST UNIT READY command
	SETUP_CMD				;S FF; Perform setup for SCSI command
	SEND_COMMAND_ORDERED			;S FF; Send non-queued TUR
	CLEANUP_CMD				;S FF; Cleanup from the SCSI command
	BICB	#UCB$M_SPINUP_INPROG,-		;S FF; Clear spinup-in-progress
		UCB$L_DK_FLAGS(R3)		;S FF;
	RSB					;S FF;

	.SBTTL	PROCESS_MODE_INFO	- Send a mode sense command
;+
; PROCESS_MODE_INFO
;
; This routine processes the mode pages for this target.  It allocates pool
; to hold a copy of field descriptors.  There is one field descriptor for
; each mode page field of interest.  The local copy of the descriptors
; will be used to receive current values as well as to specify MODE SELECT
; values for those fields which need to be read and/or written.  One call
; to the routine DO_MODE_PAGE will be made for each mode page we care about;
; DO_MODE_PAGE does a MODE SENSE current and, if appropriate, a MODE SENSE
; changeable and MODE SELECT for the page specified.
;
; Context:
;
;	SCDRP thread
;	IPL = Fork
;	Fork lock held
;
; Inputs:
;
;	R3	- UCB address
;	R4	- PDT address
;	R5	- SCDRP address
;
; Outputs:
;
;	R0	- Status
;	R1,R2	- Destroyed
;	All other registers preserved
;-

PROCESS_MODE_INFO::
	.JSB_ENTRY INPUT=<R3,R4,R5>,OUTPUT=<R0>,SCRATCH=<R1,R2>,PRESERVE=<R6,R7,R8,R9,R10>

	ASSUME	UCB$B_SECTORS+1 EQ UCB$B_TRACKS ;*GWW
	MOVW	#^X604,UCB$B_SECTORS(R3)	;*GWW Fill in dummy values for 
						; sector and track fields
; Allocate a local copy of the mode page descriptors on the stack

	SUBL3	#DESCRIPTOR_SIZE,SP,R7		;S FF; Size of descriptor array 
	BICL	#7,R7				;S FF; Quadword align
	SUBL	#DESCRIPTOR_SIZE+8,SP		;S FF; Account for desc + unalignment
	PUSHR	#^M<R3,R4,R5>
	MOVC5	#0,.,#0,#DESCRIPTOR_SIZE,(R7)	;S FF; Initialize the desc area
	MOVC3	#DESCRIPTOR_SIZE,-		;S FF; Copy the descriptors
		DESCRIPTOR_BASE,(R7)	
	POPR	#^M<R3,R4,R5>
	INSV	UCB$B_SCSI_VERSION(R3),-	;S FF; Determine SCSI1 vs SCSI2 for 
		#SCDRP$V_FLAG_MODE_SENSE,-      ;S FF; later use by do_mode_page
		#SCDRP$S_FLAG_MODE_SENSE,-
		SCDRP$L_SCSI_FLAGS(R5)

; Read the error recovery page for two reasons: first, to establish a standard for
; whether or not the device supports 10-byte Mode Sense commands and second, to
; fetch the block descriptor, which contains the block length; this is necessary
; to make sure that CD-ROMs are set to a 512-byte block size.

	LOAD_TENBYTE				;S FF; Initialize the SCDRP with 
						;S FF; the current UCB.TENBYTE bit

	PUSHL	#SCSI$PGCD$C_READ_WRITE_ERR	;S FF; Page code = error recovery page 
	PUSHAL	ERR_PAGE_DESC(R7)		;S FF; Address of descriptors
	PUSHL	R5                              ;S FF; SCDRP address
	CALLS	#3,DO_MODE_PAGE                 ;S FF; Process the page
	BLBC	R0, 18$				;S FF; Handle failure
	STORE_TENBYTE				;S FF; Save SCDRP.TENBYTE bit in UCB
	BBC	#UCB$V_CDROM, -			;S FF; If not CDROM, tben skip the
		UCB$L_DK_FLAGS(R3),200$		;S FF; block size check
	CMPW	#512,UCB$W_BLOCK_SIZE(R3)	;S FF; Is this already size 512?
	BEQL	200$				;S FF; Yes, so don't change it
	BGTR	18$				;S FF; Can't handle page size < 512
  	MOVW	#512,UCB$W_BLOCK_SIZE(R3)	;S FF; new Set block size in UCB
	BSBW	READ_CAPACITY			;S FF; Re invoke READ CAPACITY command
	BLBS	R0, 200$			;S FF; Success, continue
18$:	MOVAL	ERR_PAGE_DESC(R7),R8            ;S FF; Address of descriptor
	MOVL	#SIZEOF_ERR,R6                  ;S FF; Size
	BRW	95$                             ;S FF; Log error

; Control Mode page (Queuing parameters)

200$:	
	IF_NOT_CMDQ	20$			;S FF; If device doesn't do queuing, ignore
						;S FF; control mode page
	PUSHL	#SCSI$PGCD$C_CONTROL_MODE	;S FF; Page code = control mode page 
	PUSHAL	CONTROL_MODE_PAGE_DESC(R7)	;S FF; Address of descriptors
	PUSHL	R5                              ;S FF; SCDRP address
	CALLS	#3,DO_MODE_PAGE                 ;S FF; Process the page
	BLBS	R0,10$				;S FFL If successful, do next page
	CMPL	R0,#SS$_CANCEL			;S FF; If cancel status
	BEQL	80$                             ;S FF; Then leave now
	LOAD_TENBYTE				;S FF; Refresh SCDRP bit
	BICL	#UCB$M_CMDQ,-			;S FF; Else disable TCQ
		UCB$L_DK_FLAGS(R3)
	BICB	#1,UCB$Q_DEVDEPEND2+3(R3)	;S FF; Clear TCQ "shadow copy"

;
; Determine if the disable queuing (Dque) bits is enabled.
; 0 = tagged queuing shall be enabled if the target supports tagged command queueing
; 1 = tagged queuing shall be disabled
;
10$:	BLBC    <CTL_DQUE_DESC+MODE_DESC$L_ACTUAL>(R7),20$
	BICL	#UCB$M_CMDQ,UCB$L_DK_FLAGS(R3)  ;S FF; Disable TCQ for this device
	BICB	#1,UCB$Q_DEVDEPEND2+3(R3)       ;S FF; Clear TCQ "shadow copy"

; Error recovery parameters

20$:	
	CMPW	UCB$W_BLOCK_SIZE(R3),#512	;S FF; Is block size 512?
	BNEQ	28$				;S FF; Branch if not
	CMPB	UCB$B_SCSI_VERSION(R3),#2	;S FF; Is this SCSI2?
	BLSS	26$				;S FF; Branch if not
	IF_NOT_CMDQ 24$				;S FF; Branch if not TCQ

; Case 1:  SCSI2 device that supports TCQ
;
; First try the mode settings preferred for clusters and flag that cluster
; shared operation conditions are all OK if so. If this is not successful
; just try the more relaxed TCQ conditions.
;
	BICL	#UCB$M_CLUSQ,UCB$L_DK_FLAGS(R3)	;S FF; Clear "OK in cluster" bit
	MOVAL	SCSI2_CLU_ERR_PAGE_DESC(R7),R8	;S FF; Address of err rec descriptors
	MOVL 	#SCSI2_CLU_ERR_DEVSPC_DESC,R9	;S FF; Offset to dev specific param
	MOVL 	#SCSI2_CLU_ERR_MEDTYP_DESC,R10	;S FF; Offset to medium type
	PUSHL	#SCSI$PGCD$C_READ_WRITE_ERR	;S FF; Page code = error recovery page 
	PUSHL	R8				;S FF; Address of descriptors
	PUSHL	R5                              ;S FF; SCDRP address
	CALLS	#3,DO_MODE_PAGE                 ;S FF; Process the page
	BLBC	R0,22$                          ;S FF; Continue if success
	BISL	#UCB$M_CLUSQ,UCB$L_DK_FLAGS(R3)	;S FF; Set "OK in cluster" bit
22$:	MOVL	#SIZEOF_SCSI2_TCQ,R6		;S FF; Size in bytes of descriptors
	MOVAL	SCSI2_TCQ_ERR_PAGE_DESC(R7),R8	;S FF; Address of err rec descriptors
	MOVL 	#SCSI2_TCQ_ERR_DEVSPC_DESC,R9	;S FF; Offset to dev specific param
	MOVL 	#SCSI2_TCQ_ERR_MEDTYP_DESC,R10	;S FF; Offset to medium type
	BRW	30$				;S FF; Join common code

; Case 2:  SCSI2 device that does not support TCQ
24$:	
	MOVL	#SIZEOF_SCSI2_NOTCQ,R6		;S FF; Size in bytes of descriptors
	MOVAL	SCSI2_NOTCQ_ERR_PAGE_DESC(R7),R8;S FF; Address of err rec descriptors
	MOVL 	#SCSI2_NOTCQ_ERR_DEVSPC_DESC,R9	;S FF; Offset to dev specific param
	MOVL 	#SCSI2_NOTCQ_ERR_MEDTYP_DESC,R10;S FF; Offset to medium type
	BRW	30$				;S FF; Join common code

; Case 3:  SCSI1 device (by definition, does not support TCQ)
26$:	
	MOVL	#SIZEOF_SCSI1,R6		;S FF; Size in bytes of descriptors
	MOVAL	SCSI1_ERR_PAGE_DESC(R7),R8	;S FF; Address of err rec descriptors
	MOVL 	#SCSI1_ERR_DEVSPC_DESC,R9	;S FF; Offset to dev specific param
	MOVL 	#SCSI1_ERR_MEDTYP_DESC,R10	;S FF; Offset to medium type
	BRW	30$				;S FF; Join common code

; Case 4:  Device does not have block size = 512
28$:	
	MOVL	#SIZEOF_NON512,R6		;S FF; Size in bytes of descriptors
	MOVAL	NON512_ERR_PAGE_DESC(R7),R8	;S FF; Address of err rec descriptors
	MOVL 	#NON512_ERR_DEVSPC_DESC,R9	;S FF; Offset to dev specific param
	MOVL 	#NON512_ERR_MEDTYP_DESC,R10	;S FF; Offset to medium type
						;S FF; Note:  Don't care about WCE
; Common code to process Read Write Error Recovery Page

30$:	PUSHL	#SCSI$PGCD$C_READ_WRITE_ERR	;S FF; Page code = error recovery page 
	PUSHL	R8				;S FF; Address of descriptors
	PUSHL	R5                              ;S FF; SCDRP address
	CALLS	#3,DO_MODE_PAGE                 ;S FF; Process the page
	BLBS	R0,32$                          ;S FF; Continue if success
	CMPL	R0,#SS$_CANCEL			;S FF; If cancel status
	BEQL	80$                             ;S FF; Then leave now
	IF_NOT_CMDQ 95$				;S FF; Fatal error if noTCQ
	LOAD_TENBYTE				;S FF; Refresh SCDRP bit
	BICL	#UCB$M_CMDQ,-			;S FF; Else disable TCQ
		UCB$L_DK_FLAGS(R3)
	BICB	#1,UCB$Q_DEVDEPEND2+3(R3)	;S FF; Clear TCQ "shadow copy"
	BRW	24$				;S FF; Retry with noTCQ desc

32$:
; Since the error recovery page is normally returned, fetch and process the
; the relevant mode page header parameters (namely, the device specific
; parameter and the medium type) as part of the error recovery page processing.

	ADDL2	R7,R9				;S FF; Get address of dev specific descriptor
	ADDL2	R7,R10				;S FF; Get address of medium type descriptor
	BBC	#7,MODE_DESC$L_ACTUAL(R9),5$	;S FF; Is write-protect bit clear?
		
140$:	BISL2	#UCB$M_HWL,-			;S FF; This a read-only device so set
		UCB$L_DK_FLAGS(R3)		;S FF; the hardware write-lock bit
	BBSS	#DEV$V_SWL,-			;S FF; Set the software write-lock bit to
		UCB$L_DEVCHAR(R3),5$		;S FF; indicate device is write-locked
5$:	BICL2	#<UCB$M_NOREASSIGN!-		;S FF; Assume REASSIGN_BLOCK supported,
		  UCB$M_FLOPPY!-		;S FF; Assume not a flexible diskette,
		  UCB$M_FORMAT>,-		;S FF; Assume FORMAT unsupported
		UCB$L_DK_FLAGS(R3)
						;S FF; Determine diskette type in RX26 drive
;
	CLRL	R2				;S FF; Assume unknown or default medium
	CMPB	#DT$_RX26,UCB$B_DEVTYPE(R3)	;S FF; Only RX26 reports diskette type
	BNEQ	506$				;S FF; Not RX26, skip this check
;
	MOVZBL	#MODE_SENSE_MEDIA_TYPE_MAX,R2	;S FF; Start checking for match with
						;S FF; last cell in table
502$:	CMPB	MODE_DESC$L_ACTUAL(R10),-	;S FF; Check against table entry
		MODE_SENSE_MEDIA_TYPE_TABLE[R2]
	BEQL	506$				;S FF; Matches, save index
	SOBGTR	R2,502$				;S FF; No match, back up in table
;
506$:	INSV	R2,#UCB$V_FLOPPY_MEDIA,-	;S FF; remember medium type
		#UCB$S_FLOPPY_MEDIA,-		;S FF;
		UCB$L_DK_FLAGS(R3)		;S FF;

; Format parameters

40$:	
	PUSHL	#SCSI$PGCD$C_FORMAT_DEVICE	;S FF; Page code = format device page 
	PUSHAL	FORMAT_DEVICE_PAGE_DESC(R7)	;S FF; Address of descriptors
	PUSHL	R5                              ;S FF; SCDRP address
	CALLS	#3,DO_MODE_PAGE                 ;S FF; Process the page
        BLBS	R0,41$				;S FF; If success, continue
	CMPL	R0,#SS$_CANCEL			;S FF; If cancel status
	BEQL	80$                             ;S FF; Then leave now
	LOAD_TENBYTE				;S FF; Refresh SCDRP bit
	CMPL	#SS$_NOSUCHSEC,R0		;S FF; Illegal page, special success
	BEQL	50$				;S FF; Just do next page
	CLRB	UCB$B_SECTORS(R3)		;S FF; Invalidate sector field
; If there are no required bits in the page, allow SS$_NODATA return too
	.IF EQ,<FMT_USE_SUMFLG & REQ>		;S FF; if no required fields
	CMPL	#SS$_NODATA,R0			;S FF; NODATA means chk cond
	BEQL	50$				;S FF; PATCH AROUND LOSS
	.ENDC
	MOVAL	FORMAT_DEVICE_PAGE_DESC(R7),R8  ;S FF; Address of descriptor
	MOVL	#SIZEOF_FORMAT,R6               ;S FF; Size
	BRW	95$                             ;S FF; Log error

41$:	MOVB	-				;S FF; Save # of sectors per track in UCB
		<FMT_SECTORS_PER_TRACK_DESC+MODE_DESC$L_ACTUAL>(R7),-
		UCB$B_SECTORS(R3)		;S FF;

; Geometry parameters for rigid disks

50$:
	PUSHL	#SCSI$PGCD$C_RIGID_DISK		;S FF; Page code = rigid disk params
	PUSHAL	RIGID_DISK_PAGE_DESC(R7)	;S FF; Address of descriptors
	PUSHL	R5                              ;S FF; SCDRP address
	CALLS	#3,DO_MODE_PAGE                 ;S FF; Process the page
        BLBS	R0,51$				;S FF; If success, continue
	CMPL	R0,#SS$_CANCEL			;S FF; If cancel status
	BEQL	80$                             ;S FF; Then leave now
	LOAD_TENBYTE				;S FF; Refresh SCDRP bit
	CMPL	#SS$_NOSUCHSEC,R0		;S FF; Illegal page, special success
	BEQL	55$				;S FF; Just do next page
	CLRB	UCB$B_TRACKS(R3)		;S FF; Invalidate tracks
	CLRW	UCB$W_CYLINDERS(R3)		;S FF; Invalidate cylinders
; If there are no required bits in the RGD rigid disk page, allow NODATA
; to return also. This means check condition returned in the mode_sense_common
; code but the req sense data was not quite as expected for a "canonical"
; nonimplemented page (if such a notion makes sense.)
	.IF EQ,<RGD_SUMFLG & REQ>
	CMPL	#SS$_NODATA,R0
	BEQL	60$				;S FF;  hack way around bugs
	.ENDC
	MOVAL	RIGID_DISK_PAGE_DESC(R7),R8  	;S FF; Address of descriptor
	MOVL	#SIZEOF_RIGID_DISK,R6           ;S FF; Size
	BRW	95$                             ;S FF; Log error

51$:	MOVB	-				;S FF; Save number of tracks per cylinder
		<RGD_TRACKS_PER_CYLINDER_DESC+MODE_DESC$L_ACTUAL>(R7),-
		UCB$B_TRACKS(R3)		;S FF;
	MOVB	-				;S FF; Save number of cylinders
		<RGD_NUM_CYLINDERS_DESC+MODE_DESC$L_ACTUAL>(R7),-
		UCB$W_CYLINDERS+1(R3)		;S FF; (high byte)
	MOVB	-				;S FF; Save number of cylinders
		<RGD_NUM_CYLINDERS_DESC+MODE_DESC$L_ACTUAL+1>(R7),-
		UCB$W_CYLINDERS(R3)		;S FF; (low byte)

	BRW	60$				;S FF; Skip the Flexible Disk page now

; Geometry parameters for flexible disks

55$:
	PUSHL	#SCSI$PGCD$C_FLEXIBLE_DISK	;S FF; Page code = flexible disk params 
	PUSHAL	FLEX_DISK_PAGE_DESC(R7)		;S FF; Address of descriptors
	PUSHL	R5                              ;S FF; SCDRP address
	CALLS	#3,DO_MODE_PAGE                 ;S FF; Process the page
        BLBS	R0,56$				;S FF; If success, continue
	CMPL	R0,#SS$_CANCEL			;S FF; If cancel status
	BEQL	80$                             ;S FF; Then leave now
	LOAD_TENBYTE				;S FF; Refresh SCDRP bit
	CMPL	#SS$_NOSUCHSEC,R0		;S FF; Illegal page, special success
	BEQL	60$				;S FF; Just do next page
; If there are no required bits in the RFLX floppy disk page, allow NODATA
; to return also. This means check condition returned in the mode_sense_common
; code but the req sense data was not quite as expected for a "canonical"
; nonimplemented page (if such a notion makes sense.)
	.IF EQ,<FLX_USE_SUMFLG & REQ>
	CMPL	#SS$_NODATA,R0
	BEQL	60$				;S FF;  hack way around bugs
	.ENDC
	CLRW	UCB$W_CYLINDERS(R3)		;S FF; Invalidate cylinders
	CLRB	UCB$B_TRACKS(R3)		;S FF; Invalidate tracks
	MOVAL	FLEX_DISK_PAGE_DESC(R7),R8  	;S FF; Address of descriptor
	MOVL	#SIZEOF_FLEX_DISK,R6            ;S FF; Size
	BRW	95$                             ;S FF; Log error

56$:	MOVB	-				;S FF; Save number of tracks per cylinder
		<FLX_TRACKS_PER_CYLINDER_DESC+MODE_DESC$L_ACTUAL>(R7),-
		UCB$B_TRACKS(R3)		;S FF;
	MOVB	-				;S FF; Save number of cylinders
                <FLX_NUM_CYLINDERS_DESC+MODE_DESC$L_ACTUAL>(R7),-
		UCB$W_CYLINDERS+1(R3)		;S FF; (high byte)
	MOVB	-				;S FF; Save number of cylinders
                <FLX_NUM_CYLINDERS_DESC+MODE_DESC$L_ACTUAL+1>(R7),-
		UCB$W_CYLINDERS(R3)		;S FF; (low byte)

	BISL2	#<UCB$M_NOREASSIGN!-		;S FF; Floppies don't do REASSIGN_BLOCK
		  UCB$M_FORMAT!-		;S FF; This unit is formatable
		  UCB$M_FLOPPY>,-		;S FF; Remember that this is a floppy
		UCB$L_DK_FLAGS(R3)

						;S FF; Check for 250 KHz RX23 diskette,
						;S FF; if so, then mark it write-protected
						;S FF; unless UCB$L_DK_FLAGS<DD_BYPASS> is
						;S FF; set to allow writing of these media.

	BBS	#UCB$V_DD_BYPASS,-		;S FF; Don't bother checking if the
		UCB$L_DK_FLAGS(R3),558$		;S FF; special override bit is set
	CMPB	#DT$_RX23S,UCB$B_DEVTYPE(R3)	;S FF; Only worry about RX23 media
	BNEQ	558$				;S FF;
	CMPW	#SCSI$FLX$C_XFR_250KHZ,-	;S FF; Check for 250 KHz
		<FLX_TRANSFER_RATE_DESC+MODE_DESC$L_ACTUAL>(R7)
	BEQL	559$				;S FF; If 250 KHz, then write protect it

						;S FF; Check for 48 TPI diskette,
						;S FF; if so, then mark it
						;S FF; software write-protected
558$:	CMPB	#SCSI$FLX$R_FLAGS,-		;S FF; Check length of page
		<FLX_PAG_LEN_DESC+MODE_DESC$L_ACTUAL>(R7)
						;S FF; Skip test for SPC if page does
	BGEQ	60$				;S FF; not include flags word
	CMPZV	#SCSI$FLX$V_SPC,-		;S FF; Test for SPC
		#SCSI$FLX$S_SPC,-		;S FF; (steps per cylinder)
		<FLX_SPC_DESC+MODE_DESC$L_ACTUAL>(R7),-
		#0				;S FF; equal to 0
	BEQL	60$				;S FF; If so, then skip
559$:	BBSS	#DEV$V_SWL,UCB$L_DEVCHAR(R3),60$;S FF; If not, then set the SWL
						;S FF; Note: can't write 48 TPI diskettes
; Caching page

60$:   	.IF	DEFINED RZ74_CACHE
	.BRANCH_LIKELY
	CMPB	#DT$_RZ74,UCB$B_DEVTYPE(R3)	;S FF; Check for RZ74 disk drive
	BNEQ	65$				;S FF; No, use caching page
	BBS	#UCB$V_OUT_OF_REV,-		;S FF; Is this RZ74 out of rev?
		UCB$L_DK_FLAGS(R3),70$		;S FF; Yes, use no-caching page
65$:	.ENDC

	BBS	#UCB$V_HWL,-          		;S FF; Skip if writelocked
		UCB$L_DK_FLAGS(R3),75$
	PUSHL	#SCSI$PGCD$C_CACHING		;S FF; Page code = caching page 
	PUSHAL	CACHING_PAGE_DESC(R7)		;S FF; Address of descriptors
	PUSHL	R5                              ;S FF; SCDRP address
	CALLS	#3,DO_MODE_PAGE                 ;S FF; Process the page
        BLBS	R0,75$				;S FF; If success, continue
	CMPL	R0,#SS$_CANCEL			;S FF; If cancel status
	BEQL	80$                             ;S FF; Then leave now
	LOAD_TENBYTE				;S FF; Refresh SCDRP bit
	CMPL	#SS$_NOSUCHSEC,R0		;S FF; Illegal page, special success
	BEQL	75$				;S FF; Continue
; If there are no required bits in the CACHE page, allow NODATA
; to return also. This means check condition returned in the mode_sense_common
; code but the req sense data was not quite as expected for a "canonical"
; nonimplemented page (if such a notion makes sense.)
	.IF EQ,<CACHE_SUMFLG & REQ>
	CMPL	#SS$_NODATA,R0
	BEQL	75$				;S FF;  hack way around bugs
	.ENDC
	MOVAL	CACHING_PAGE_DESC(R7),R8        ;S FF; Address of descriptor
	MOVL	#SIZEOF_CACHING,R6              ;S FF; Size
	BRW	95$                             ;S FF; Log error

	.IF	DEFINED RZ74_CACHE
; NoCaching page

70$:	PUSHL	#SCSI$PGCD$C_CACHING		;S FF; Page code = caching page 
	PUSHAL	NOCACHING_PAGE_DESC(R7)		;S FF; Address of descriptors
	PUSHL	R5                              ;S FF; SCDRP address
	CALLS	#3,DO_MODE_PAGE                 ;S FF; Process the page
        BLBS	R0,75$				;S FF; If success, continue
	CMPL	R0,#SS$_CANCEL			;S FF; If cancel status
	BEQL	80$                             ;S FF; Then leave now
	LOAD_TENBYTE				;S FF; Refresh SCDRP bit
	CMPL	#SS$_NOSUCHSEC,R0		;S FF; Illegal page, special success
	BEQL	75$				;S FF; Continue
	MOVAL	NOCACHING_PAGE_DESC(R7),R8	;S FF; Address of descriptor
	MOVL	#SIZEOF_NOCACHING,R6		;S FF; Size
	BRW	95$                             ;S FF; Log error
	.ENDC

75$:	MOVZWL	#SS$_NORMAL,R0			;S FF; Set success status
80$:	                                        ;S FF; Deallocate descriptors
	ADDL	#DESCRIPTOR_SIZE+8,SP		;S FF; Account for desc + unalignment
	BBS	#SCDRP$V_FLAG_TENBYTE,-     	;S FF; 10-byte mode sense support detected?
		SCDRP$L_SCSI_FLAGS(R5),85$
	BICL2	#UCB$M_TENBYTE,-		;S FF; Copy back to UCB - no 10-byte suppport      	
		UCB$L_DK_FLAGS(R3)           	;S FF; next time through mode sense logic
	BRW	90$				;S FF; Continue
85$:	BISL2	#UCB$M_TENBYTE,-		;S FF; Copy back to UCB - 10-byte suppport      	
		UCB$L_DK_FLAGS(R3)           	;S FF; 
90$:	RSB					;S FF; Return to caller

95$:	CMPL	R0,#SS$_CANCEL			;S FF; If cancel status
	BEQL	80$                             ;S FF; Then leave now

; Since the real CDB has been deallocated by CLEANUP_CMD already, set up
; a dummy CDB for error logging purposes only.  Note:  All mode sense/select
; errors get logged as a 6-byte MODE_SENSE command with SCSI status FF.
; Since the register dump routine limits additional data to 255. bytes,
; We can store up to 6 descriptors (at 40. bytes each) in the error packet.

	PUSHL	SCDRP$L_CMD_PTR(R5)
	PUSHL	SCDRP$L_STS_PTR(R5)
	PUSHL	SCDRP$L_SVA_USER(R5)
	PUSHL	SCDRP$L_TRANS_CNT(R5)

	MOVAL	DUMMY_MODE_SENSE_CDB+4,-	; Address of length longword
		SCDRP$L_CMD_PTR(R5)
	MOVAL	DUMMY_MODE_SENSE_CDB,-          ; Address of SCSI sts byte
		SCDRP$L_STS_PTR(R5)
	MOVL	R8,SCDRP$L_SVA_USER(R5)   	; Address of descriptors for
						; this page
	ASSUME MODE_DESC$S_FIELD_DESCRIPTOR EQ 40	
	CMPL	R6,-				; Do we have too many to save?
		#<6*MODE_DESC$S_FIELD_DESCRIPTOR>	
	BLEQ	98$				; Good, we can save all desc
	MOVL	#<6*MODE_DESC$S_FIELD_DESCRIPTOR>,-
		R6				; Can save first six only.	
98$:	MOVL	R6,SCDRP$L_TRANS_CNT(R5)       	; Byte count of all descriptors
						; for this page

	LOG_ERROR -				;S FF; Log a mode sense data error
		TYPE=MODE_SENSE_DATA,-
		VMS_STATUS=R0,-
		UCB=R3,-
		SCDRP=R5

	POPL	SCDRP$L_TRANS_CNT(R5)
	POPL	SCDRP$L_SVA_USER(R5)
	POPL	SCDRP$L_STS_PTR(R5)
	POPL	SCDRP$L_CMD_PTR(R5)
	MOVL	#SS$_MEDOFL,R0			;S FF; Convert debug-usable error
						;S FF;   to user-visible error
	BRB	80$				;S FF; Use common exit path

	.PAGE
	.SBTTL	PROCESS_MODE_FORMAT_FLOPPY 
;+
; PROCESS_MODE_FORMAT_FLOPPY
;
; This routine is called to change fields in the format parameters and the
; flexible disk parameter pages as necessary to conform to the requested
; format.
;
; Context:
;
;	SCDRP thread
;	IPL = Fork
;	Fork lock held
;
; Inputs:
;
;	R3	- UCB address
;	R4	- PDT address
;	R5	- SCDRP address
;
; Outputs:
;
;	R0	- Status
;	R1,R2	- Destroyed
;	All other registers preserved
;-
PROCESS_MODE_FORMAT_FLOPPY::			;S FF; Send a mode select command
	.JSB_ENTRY INPUT=<R3,R4,R5>,OUTPUT=<R0>,SCRATCH=<R1,R2>,PRESERVE=<R6,R7>

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

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

	INSV	UCB$B_SCSI_VERSION(R3),-	;S FF; Determine SCSI1 vs SCSI2
		#SCDRP$V_FLAG_MODE_SENSE,-
		#SCDRP$S_FLAG_MODE_SENSE,-
		SCDRP$L_SCSI_FLAGS(R5)

						;S FF; Set up format parameters depending
						;S FF; on device:
						;S FF; RX33s -> 15 sectors
						;S FF; RX23s & /dens=double -> 18 sectors
						;S FF; RX23s & /dens=single & bypass
						;S FF;	 -> 9 sectors
						;S FF; RX26 & /dens=DD -> 9 sectors
						;S FF; RX26 & /dens=HD -> 18 sectors
						;S FF; RX26 & /dens=ED -> 36 sectors

	MOVAB	<MODE_SELECT_FORMAT_RX33_15+-	;S FF; RX33s -> 15 sectors
		SCSI$FMT$W_TRACKS>,R6
	CMPB	#DT$_RX33S,UCB$B_DEVTYPE(R3)	;S FF; Check for RX33 drive
	BEQL	70$				;S FF; Yep, use RX33 parameters

	MOVQ	R1,-(SP)			;S FF; Save registers R1 and R2
	MOVL	SCDRP$L_IRP(R5),R2		;S FF; Point to IRP
	SUBL3	#1,IRP$L_MEDIA(R2),R2		;S FF;	 to get /density qualifier
						;S FF; (adjusting for index offset of one)
	MOVAL	MODE_SELECT_FORMAT_TABLE,R1	;S FF; Point to table of addresses
	ADDL3	R1,(R1)[R2],R6			;S FF; Get address from table
	MOVQ	(SP)+,R1			;S FF; Restore registers

;
; Build the format parameters page
;
70$:	
	MOVW	SCSI$FMT$W_SECTORS(R6),-	; S FF; Change sectors per track
		<FMT_SEL_SECTORS_PER_TRACK_DESC+MODE_DESC$L_DESIRED>(R7)

	LOAD_TENBYTE				;S FF; Copy UCB.TENBYTE bit to SCDRP

	PUSHL	#SCSI$PGCD$C_FORMAT_DEVICE	;S FF; Page code = format device page 
	PUSHAL	FORMAT_DEVICE_SEL_PAGE_DESC(R7)	;S FF; Address of descriptors
	PUSHL	R5                              ;S FF; SCDRP address
	CALLS	#3,DO_MODE_PAGE
	BLBC	R0,1100$
;
; Build the flexible disk geometry parameters page
;
						;S FF; Set up flexible geometry depending
						;S FF; on device:
						;S FF; RX33s -> 15 sectors
						;S FF; RX23s & /dens=double -> 18 sectors
						;S FF; RX23s & /dens=single & bypass
						;S FF;	 -> 9 sectors
						;S FF; RX26 & /dens=DD -> 9 sectors
						;S FF; RX26 & /dens=HD -> 18 sectors
						;S FF; RX26 & /dens=ED -> 36 sectors

	MOVAB	<MODE_SELECT_FLEXIBLE_RX33_15+-	;S FF; RX33s -> 15 sectors
		SCSI$FLX$W_TRANSFER_RATE>,R6	;S FF;
	CMPB	#DT$_RX33S,UCB$B_DEVTYPE(R3)	;S FF; Check for RX33 drive
	BEQL	170$				;S FF; Yes, take branch

	MOVQ	R1,-(SP)			;S FF; Save registers R1 and R2
	MOVL	SCDRP$L_IRP(R5),R2		;S FF; Point to IRP
	SUBL3	#1,IRP$L_MEDIA(R2),R2		;S FF;	 to get /density qualifier
						;S FF; (adjusting for index offset of one)
	MOVAL	MODE_SELECT_FLEXIBLE_TABLE,R1	;S FF; Point to table of addresses
	ADDL3	R1,(R1)[R2],R6			;S FF; Get address from table
	MOVQ	(SP)+,R1			;S FF; Restore registers
170$:	

 	MOVW	SCSI$FLX$W_TRANSFER_RATE(R6),-				;S FF; Use our transfer rate
		<FLX_SEL_TRANSFER_RATE_DESC+MODE_DESC$L_DESIRED>(R7)
	MOVB	SCSI$FLX$B_HEADS(R6),-					;S FF; Use our number of heads
		<FLX_NUM_HEADS_DESC+MODE_DESC$L_DESIRED>(R7)
	MOVB	SCSI$FLX$B_SECTORS_TRACK(R6),-				;S FF; Use our sectors per track
		<FLX_SECTORS_PER_TRACK_DESC+MODE_DESC$L_DESIRED>(R7)
	MOVW	SCSI$FLX$W_CYLINDERS(R6),-				;S FF; Use our number of cylinders
		<FLX_SEL_NUM_CYLINDERS_DESC+MODE_DESC$L_DESIRED>(R7)
	EXTZV	#SCSI$FLX$V_SSN,#1,-					;S FF; Use our start sector number
		SCSI$FLX$R_FLAGS(R6),-		
                <FLX_START_SECTOR_NUM_DESC+MODE_DESC$L_DESIRED>(R7)
	EXTZV	#SCSI$FLX$V_SPC,#SCSI$FLX$S_SPC,- 			;S FF; Use our steps/cyl
		SCSI$FLX$R_FLAGS(R6),-		
		<FLX_SEL_SPC_DESC+MODE_DESC$L_DESIRED>(R7)

	PUSHL	#SCSI$PGCD$C_FLEXIBLE_DISK	;S FF; Page code = flexible disk params   55$
	PUSHAL	FLEX_DISK_SELECT_PAGE_DESC(R7)	;S FF; Address of descriptors
	PUSHL	R5                              ;S FF; SCDRP address
	CALLS	#3,DO_MODE_PAGE
	BLBC	R0,1100$

	MOVL	#SS$_NORMAL,R0			;S FF; Set success status

900$:						;S FF; Deallocate descriptors	
	ADDL	#DESCRIPTOR_SIZE+8,SP		;S FF; Account for desc + unalignment
	RSB					;S FF; Return to caller

1100$:	CMPL	R0,#SS$_CANCEL			;S FF; If cancel status
	BEQL	900$                             ;S FF; Then leave now
	MOVL	#SS$_DRVERR,R0
	BRB	900$				;S FF; Join common exit code

	.PAGE
	.SBTTL	VALIDATE_GEOMETRY
;+
; VALIDATE_GEOMETRY 
;
; In general, clyinders/tracks/sectors must be nonzero in order to set the device valid.
; This routine checks for zero values in various geometry fields.  In special cases it
; will provide dummy values for fields;  in other cases of zero fields it will return
; an error.

; Context:
;
;	SCDRP thread
;	IPL = Fork
;	Fork lock held
;
; Inputs:
;
;	R3	- UCB address
;	R4	- PDT address
;	R5	- SCDRP address
;
; Outputs:
;
;	R0	- Status
;	R1,R2	- Destroyed
;	All other registers preserved
;-

VALIDATE_GEOMETRY:

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

	ASSUME	UCB$B_SECTORS+1 EQ UCB$B_TRACKS
	ASSUME	UCB$B_TRACKS+1 EQ UCB$W_CYLINDERS
	MOVAL	UCB$B_SECTORS(R3),R1		;S FF; Get address sectors field in UCB
	TSTB	(R1)+				;S FF; Check that the tracks, sectors, and
	BEQL	10$ 				;S FF; cylinders fields in the UCB are all
	TSTB	(R1)+				;S FF; non-zero. If any are zero, then possibly
	BEQL	10$				;S FF; there's a problem and error status
	TSTW	(R1)+				;S FF; should be returned to prevent the
						;S FF; volume valid bit from being set.
	BNEQ	30$				;S FF; All fields nonzero, good.

; If here, at least one of cylinder/track/sector is zero.

10$:	BITL	#<UCB$M_OPTICAL+-		;S FF; Is this an optical or a WORM device?
		  UCB$M_WORM>,-			;S FF; These devices do not always return
		  UCB$L_DK_FLAGS(R3)		;S FF; cylinder and track info.
	BNEQ	30$				;S FF; Set dummy values if so.

; Handle possibility of unformatted media in a floppy drive, which
; returns zeroes in the geometry fields.
;
	BBC	#UCB$V_FLOPPY,-			;S FF; Report different error if floppy
		UCB$L_DK_FLAGS(R3),20$
	MOVZWL	#SS$_FORMAT,R0			;S FF; Set bad status:	unformatted media
	BRB	200$				;S FF; Use common exit path

; If this is a read-only device such as a CDROM, it may not have real tracks,
; sectors, and cylinders. If this is the case, set up dummy values for the 
; geometry parameters. 

20$:	BBS	#DEV$V_SWL,-			;S FF; If this is a read-only device, it's not
		UCB$L_DEVCHAR(R3),30$		;S FF; required to have non-zero values of
						;S FF; tracks, sectors, and cylinders

; If here, at least one of cylinder/track/sector is zero, and this is an error.
; However, allow geometry to be generated if maxblock is nonzero
; since many opticals and the like do not supply geometry beyond disk
; size.

	TSTL	UCB$L_MAXBLOCK(R3)		;S FF; Ensure not 0 len. dsk
	BNEQ	30$
	MOVZWL	#SS$_DRVERR,R0			;S FF; Set bad status
	BRB	200$				;S FF; Use common exit path

; Proceed to check geometry further. Set dummy values where appropriate so we may continue.

30$:
	MOVL	UCB$L_MAXBLOCK(R3),R2		;S FF; Get max block number
	INCL	R2				;S FF; Get max blocks
	MOVZBL	UCB$B_TRACKS(R3),R1		;S FF; Get number of tracks
	MOVZBL	UCB$B_SECTORS(R3),R0		;S FF; Get number of sectors
	MULL	R0,R1				;S FF; Multiply
	BEQL	35$				;S FF; Branch if tracks or sectors is zero
	MOVZWL	UCB$W_CYLINDERS(R3),R0		;S FF; Get number of cylinders
	MULL	R0,R1				;S FF; Get tracks * sectors * cylinders
	CMPL	R1,R2				;S FF; Tracks * sectors * cyl = maxblock?
	BNEQ	40$				;S FF; Branch if not
	BRW	190$				;S FF; Set success status and exit

; Either tracks or sectors field (or both) is zero. Set up dummy values
; for these two fields.

	ASSUME	UCB$B_SECTORS+1 EQ UCB$B_TRACKS
35$:	MOVW	#^X604,UCB$B_SECTORS(R3)	;S FF; Fill in dummy values for sector
						;S FF; and track fields

; Here we've found that MAXBLOCK is not equal to tracks * sectors * cylinders.
; Calculate a new value for cylinders to ensure that the product is greater
; than maxblock. Use the same algorithm as DUDRIVER so that SCSI disks served
; in a cluster appear to have the same geometry on every node.

40$:	MOVZBL	UCB$B_TRACKS(R3),R1		;S FF; Get number of tracks
	MOVZBL	UCB$B_SECTORS(R3),R0		;S FF; Get number of sectors
	MULL	R1,R0				;S FF; Multiply
	MOVL	UCB$L_MAXBLOCK(R3),R1		;S FF; Get number of blocks
	CLRL	R2				;S FF; Prepare for extended divide
	EDIV	R0,R1,R0,R1			;S FF; Calculate number of cylinders, remainder
	CMPL	R0,#65534			;S FF; Is cylinder number legal?
	BGTR	202$				;S FF; If so go try a crude fix
	MOVW	R0,UCB$W_CYLINDERS(R3)		;S FF; Save number of cylinders
	TSTL	R1				;S FF; Zero remaineder?
	BEQL	190$				;S FF; Branch if so
	INCW	UCB$W_CYLINDERS(R3)		;S FF; Otherwise, increment cylinders
						;S FF; (tracks * sectors * cylinders must
						;S FF; be >= maxblock)
190$:	MOVL	#SS$_NORMAL,R0
200$:	RSB
;
; Out of line code to handle really large geometries to the limits of what
; VMS can currently do.
;
; First method can waste 1024 blocks.
; Second method can waste 9216 blocks. Third can waste 65025. This is rather
; crude, but will at least allow the structure to be used.
;
; Note that we test against 65534 cylinders because cyl count may be 
; incremented if maxblock field is more than trk*sect*cyl. This guarantees
; the cylinder field will be legal and thus the device will be usable, other
; things being equal. Note that because we accept devices with zeroes
; in trk or sect and will have filled in ^X604 above, we have four fake
; geometries in all:
;    6 x  4 x n
;   32 x 32 x n
;   96 x 96 x n
;  255 x255 x n
;
; This means geometry based loss of up to 23, 1023, 9215, or 65024
; blocks as the device gets bigger, but the device will be usable over
; most of its surface.
;
202$:	MOVB	#32,UCB$B_TRACKS(R3)
	MOVB	#32,UCB$B_SECTORS(R3)
	CMPL	UCB$L_MAXBLOCK(R3),#<65534*32*32> ;does 32 by 32 by n work?
	BLSSU	40$
	MOVB	#96,UCB$B_TRACKS(R3)		;S FF; Set 96 by 96 by n geom
	MOVB	#96,UCB$B_SECTORS(R3)
	CMPL	UCB$L_MAXBLOCK(R3),#<65534*96*96> ;S FF; Be sure disk not too big
	BLSSU	40$				;S FF; Redo computation if ok
; If disk is over 300Gb, try to allow 2TB
	MOVB	#255,UCB$B_TRACKS(R3)		;S FF; Go for broke
	MOVB	#255,UCB$B_SECTORS(R3)
	BRW	40$				;This is as big as we can go.
; This should be adequate for any currently supported disk size (i.e.,
; with a 32-bit block number a la SCSI-2), losing at most a small bit of
; capacity but allowing disk access for most of the surface.

	.PAGE
	.SBTTL	FORMAT		- Send a format command
;+
; FORMAT
;
; This routine sends a format command to the target.
;
; Context:
;
;	SCDRP thread
;	IPL = Fork
;	Fork lock held
;
; Inputs:
;
;	R3	- UCB address
;	R4	- PDT address
;	R5	- SCDRP address
;
; Outputs:
;
;	R0	- Status
;	R1,R2	- Destroyed
;	All other registers preserved
;-

FORMAT::					;S FF; Send a format command
	.JSB_ENTRY INPUT=<R3,R4,R5>,OUTPUT=<R0>,SCRATCH=<R1,R2>

	MOVAL	CMD_FORMAT_UNIT,R2		;S FF; Address of FORMAT command
	SETUP_CMD				;S FF; Perform setup for SCSI command
	MOVL	#RX_FMT_DISC_TMO,-		;S FF; Increase the disconnect timeout
		SCDRP$L_DISCON_TIMEOUT(R5)	;S FF; value for the format operation
	SEND_COMMAND_ORDERED			;S FF; Send the SCSI command
	CLEANUP_CMD				;S FF; Cleanup from the SCSI command
	RSB					;S FF;

	.PAGE
	.SBTTL	READ_CAPACITY	- Send a read capacity command
;+
; READ_CAPACITY
;
; This routine reads the capacity of the target. It is called AFTER MODE_SENSE
; and MODE_SELECT so that a possible change of block size will be reflected in
; the device's block count. It then ensures that the geometry information
; returned by MODE_SENSE agrees with the device's capacity (the product of
; tracks, sectors, and cylinders must not exceed the maximum block number).
;
; Context:
;
;	SCDRP thread
;	IPL = Fork
;	Fork lock held
;
; Inputs:
;
;	R3	- UCB address
;	R4	- PDT address
;	R5	- SCDRP address
;
; Outputs:
;
;	R0	- Status
;	R1,R2	- Destroyed
;	All other registers preserved
;-

READ_CAPACITY::					;S FF; Send a read capacity command
	.JSB_ENTRY INPUT=<R3,R4,R5>,OUTPUT=<R0>,SCRATCH=<R1,R2>

	MOVAL	CMD_READ_CAPACITY,R2		;S FF; Address of READ CAPACITY command
	SETUP_CMD				;S FF; Perform setup for SCSI command
	SEND_COMMAND_ORDERED			;S FF; Send the SCSI command
	BLBC	R0,20$				;S FF; Branch on error
	MOVL	SCDRP$L_SVA_USER(R5),R1		;S FF; Get address of read capacity data
	MOVAL	UCB$L_MAXBLOCK+4(R3),R2		;S FF; Get address just beyond MAXBLOCK
	.REPT	4				;S FF; Copy four bytes of max block data
	MOVB	(R1)+,-(R2)			;S FF; from SCSI buffer to UCB. Note that
	.ENDR					;S FF; bytes are reversed in SCSI buffer
	INCL	(R2)				;S FF; Convert to maxblocks
	ADDL	#2,R1				;S FF; Step over irrelevant data
	MOVB	(R1)+,UCB$W_BLOCK_SIZE+1(R3)	;S FF; Get MSB of block size
	MOVB	(R1),UCB$W_BLOCK_SIZE(R3)       ;S FF; Get LSB of block size

	CMPW	UCB$W_BLOCK_SIZE(R3),#512	;S FF; Is block size 512?
	BEQL	10$				;S FF; If so, exit normally

	BITL	#<UCB$M_OPTICAL+-		;S FF; Is this an optical or a WORM device?
		  UCB$M_WORM>,-			;S FF; These devices may have ACPs to
		  UCB$L_DK_FLAGS(R3)		;S FF; handle special geometry.
	BNEQ	10$				;S FF; Exit normally if so.

        BBSS	#DEV$V_SWL,UCB$L_DEVCHAR(R3),10$;S FF; Otherwise software writelock this device.

10$:	MOVL	#SS$_NORMAL,R0			;S FF; Set success status
20$:	CLEANUP_CMD				;S FF; Cleanup from the SCSI command
	RSB					;S FF;

	.PAGE
	.SBTTL	START_UNIT	- Send a start unit command
	.SBTTL	STOP_UNIT	- Send a stop unit command
;+
; START_UNIT	- Start command to spin up device
; STOP_UNIT	- Stop command to spin down device
;
; This routine sends a start or stop command to the target to spin up or
; spin down the device, respectively.
;
; Context:
;
;	SCDRP thread
;	IPL = Fork
;	Fork lock held
;
; Inputs:
;
;	r3	- UCB address
;	R4	- PDT address
;	R5	- SCDRP address
;
; Outputs:
;
;	R0	- Status
;	R1,R2	- Destroyed
;	All other registers preserved
;-

START_UNIT::					;S FF; Send a start unit command
	.JSB_ENTRY INPUT=<R3,R4,R5>,OUTPUT=<R0>,SCRATCH=<R1,R2>

	MOVAL	CMD_START_UNIT,R2		;S FF; Address of START UNIT command
	BRB	START_STOP_COMMON		;S FF; Use common path

STOP_UNIT::					;S FF; Send a start unit command
	.JSB_ENTRY INPUT=<R3,R4,R5>,OUTPUT=<R0>,SCRATCH=<R1,R2>

	MOVAL	CMD_STOP_UNIT,R2		;S FF; Address of START UNIT command

START_STOP_COMMON:

	SETUP_CMD				;S FF; Perform setup for SCSI command
	MOVL	SCDRP$L_CMD_PTR(R5),R0		;S FF; Get address of command buffer
	SEND_COMMAND_ORDERED			;S FF; Send the SCSI command
	CLEANUP_CMD				;S FF; Cleanup from the SCSI command
	RSB					;S FF;

	.PAGE
	.SBTTL	READ_WRITE	- Send a read or write command
;+
; READ_WRITE
;
; This routine sends either a SCSI read or write command to the target based on
; setting of the FUNC bit in the SCDRP. The LBN in the command is filled in
; from the MEDIA field and the block count is filled in from the BCNT. The
; pad count field is filled in with the number of additional bytes over BNCT
; that must be transferred to make an integral number of blocks. Note that
; SCSI disks transfer an integral number of blocks, while a user can specify
; request for a fraction of a block.
;
; Context:
;
;	SCDRP thread
;	IPL = Fork
;	Fork lock held
;
; Inputs:
;
;	R2	- IRP address
;	R3	- UCB address
;	R4	- PDT address
;	R5	- SCDRP address
;	scdrp$is_queue_char - tag type
;
; Outputs:
;
;	R0	- Status
;	R1,R2	- Destroyed
;	All other registers preserved
;-

READ_WRITE::					;S FF; Send a read or write command
	.JSB_ENTRY INPUT=<R2,R3,R4,R5>,OUTPUT=<R0>,SCRATCH=<R1,R2>

	MOVL	SCDRP$L_MEDIA(R5),-		;S FF; Save the current head position
		UCB$L_CUR_LBN(R3)		;S FF;
	CMPL	SCDRP$L_MEDIA(R5),-		;S FF; Does the LBN specified require
		#EXTND_LBN_LIMIT		;S FF; an extended read or write command?
	BGTRU	EXTND_READ_WRITE		;S FF; Branch if so
	BBC	#IRP$V_FUNC,-			;S FF; Branch if this is a write function
		SCDRP$IS_STS(R5),5$		;S FF;
	MOVAL	CMD_READ,R2			;S FF; Get address of SCSI read command
	BRB	10$
5$:	MOVAL	CMD_WRITE,R2			;S FF; Assume this is write command
10$:	SETUP_CMD				;S FF; Set up the command
	CLRL	SCDRP$L_PAD_BCNT(R5)		;S FF; Assume no padding of last page required
	MOVL	SCDRP$L_BCNT(R5),R1		;S FF; Get byte count
	BICL3	#^C<IOC$C_DISK_BLKSIZ-1>,R1,R0	;S FF; Number of bytes in last block
	BEQL	20$				;S FF; Branch if block-integral transfer
	SUBL3	R0,#IOC$C_DISK_BLKSIZ,-		;S FF; Number of bytes of padding required
		SCDRP$L_PAD_BCNT(R5)		;S FF; for block-integral transfer
20$:	ADDL2	#<IOC$C_DISK_BLKSIZ-1>,R1	;S FF; Round up byte count to block boundary
	ASHL	#-IOC$S_DISK_BLKSIZ,R1,R1	;S FF; Convert to block count

	ADDL3	#<4+CMD_READ$B_XFER_COUNT>,-;S FF; Address of transfer length in CDB,
		SCDRP$L_CMD_PTR(R5),R0		;S FF; The CDB doesn't start at 0, but 4

	MOVB	R1,(R0)				;S FF: Set transfer count in SCSI command
						;S FF; CMD_READ$B_XFER_COUNT(R0)

	MOVAL	SCDRP$L_MEDIA(R5),R1		;S FF; Get logical block number field in SCDRP
	MOVB	(R1)+,-(R0)			;S FF; Fill in logical block address
	MOVB	(R1)+,-(R0)			;S FF; CMD_READ$R_LBA, +2, +1
	MOVB	(R1),R1				;S FF; Get high order LBN
	BICL	#^C^X1F,R1			;S FF; Clear LUN bits
	MOVB	R1,-(R0)			;S FF; CMD_READ$R_LBA, +0 
	BISL	#SCDRP$M_FLAG_BUFFER_MAPPED,-	;S FF; Remember the fact that a buffer
		SCDRP$L_SCSI_FLAGS(R5)		;S FF; has been mapped
	SPI$BUFFER_MAP				;S FF; Map the user buffer
	PUSHL	R5				;S FF; SCDRP
	PUSHL	R4				;S FF; PDT
	PUSHL	R3				;S FF; UCB
	CALLS	#3,SEND_COMMAND			;S FF; Send the SCSI command

	BICL	#SCDRP$M_FLAG_S0BUF,-		;S FF; Don't deallocate the S0 "user" buffer
		SCDRP$L_SCSI_FLAGS(R5)		;S FF; at this time
	CLEANUP_CMD				;S FF; Clean up from the current command
	RSB					;S FF; Return to caller

	.PAGE
	.SBTTL	EXTND_READ_WRITE - Send an extended read or write command
;+
; EXTND_READ_WRITE
;
; This routine sends either a SCSI extended read or write command to the
; target based on setting of the FUNC bit in the SCDRP. An extended command
; must be used when the LBN specified in the QIO won't fit in the LBN field
; of the normal read/write SCSI command. The LBN in the command is filled in
; from the MEDIA field and the block count is filled in from the BCNT. The
; pad count field is filled in with the number of additional bytes over BNCT
; that must be transferred to make an integral number of blocks. Note that
; SCSI disks transfer an integral number of blocks, while a user can specify
; request for a fraction of a block.
;
; Context:
;
;	SCDRP thread
;	IPL = Fork
;	Fork lock held
;
; Inputs:
;
;	R2	- IPR address
;	R3	- UCB address
;	R4	- PDT address
;	R5	- SCDRP address
;
; Outputs:
;
;	R0	- Status
;	R1,R2	- Destroyed
;	All other registers preserved
;-

EXTND_READ_WRITE:				;S FF; Send an extended read or write command

	BBC	#IRP$V_FUNC,-			;S FF; Branch if this is a write function
		SCDRP$IS_STS(R5),5$		;S FF;
	MOVAL	CMD_EXTND_READ,R2		;S FF; Get address of SCSI extended read command
	BRB	10$
5$:	MOVAL	CMD_EXTND_WRITE,R2		;S FF; Assume this is an extended write command
10$:	SETUP_CMD				;S FF; Set up the command
	CLRL	SCDRP$L_PAD_BCNT(R5)		;S FF; Assume no padding of last page required
	MOVL	SCDRP$L_BCNT(R5),R1		;S FF; Get byte count
	BICL3	#^C<IOC$C_DISK_BLKSIZ-1>,R1,R0	;S FF; Number of bytes in last block
	BEQL	20$				;S FF; Branch if block-integral transfer
	SUBL3	R0,#IOC$C_DISK_BLKSIZ,-		;S FF; Number of bytes of padding required
		SCDRP$L_PAD_BCNT(R5)		;S FF; for block-integral transfer
20$:	ADDL2	#<IOC$C_DISK_BLKSIZ-1>,R1	;S FF; Round up byte count to block boundary
	ASHL	#-IOC$S_DISK_BLKSIZ,R1,R1	;S FF; Convert to block count
	ADDL3	#4,SCDRP$L_CMD_PTR(R5),R0	;S FF; Get address of SCSI CDB (Command).
						;S FF; The CDB doesn't start at 0, but 4.

	MOVB	R1,CMD_EXTND_READ$W_XFER_COUNT+1(R0) ;S FF; Set LSB transfer count
	ASHL	#-8,R1,R1			     ;S FF; Get MSB transfer count
	MOVB	R1,CMD_EXTND_READ$W_XFER_COUNT(R0)   ;S FF; Set LSB transfer count

	ADDL3	#SCDRP$L_MEDIA,R5,R1		;S FF; Get logical block number field in SCDRP
	ADDL	#<CMD_EXTND_READ$R_LBA+4>,R0	;S FF; Point just beyond LSB of LBN field in
						;S FF; SCSI command packet
	.REPT	4
	MOVB	(R1)+,-(R0)			;S FF; Fill in logical block address
	.ENDR
	BISL	#SCDRP$M_FLAG_BUFFER_MAPPED,-	;S FF; Remember the fact that a buffer
		SCDRP$L_SCSI_FLAGS(R5)		;S FF; has been mapped
	SPI$BUFFER_MAP				;S FF; Map the user buffer
	PUSHL	R5				;S FF; 
	PUSHL	R4				;S FF; 
	PUSHL	R3				;S FF; 
	CALLS	#3,SEND_COMMAND			;S FF; Send the SCSI command
	BICL	#SCDRP$M_FLAG_S0BUF,-		;S FF; Don't deallocate the S0 "user" buffer
		SCDRP$L_SCSI_FLAGS(R5)		;S FF; at this time
	CLEANUP_CMD				;S FF; Clean up from the current command
	RSB					;S FF; Return to caller

	.PAGE
	.SBTTL	REASSIGN_BLOCK	- Send a reassign block command
;+
; REASSIGN_BLOCK
;
; This routine sends a reassign blocks command to a target to physically
; relocate the data for a specific logical block on the disk. This is done
; after a read operation fails with a recoverable error or a write operations
; fails with either a recoverable or non-recoverable error. Read operations
; with non-recoverable errors do not result in reassignment operations as this
; could result in undetected user data corruption.
;
; Context:
;
;	SCDRP thread
;	IPL = Fork
;	Fork lock held
;
; Inputs:
;
;	R3	- UCB address
;	R4	- PDT address
;	R5	- SCDRP address of original read/write command
;
; Outputs:
;
;	R0	- Status
;	R1,R2	- Destroyed
;-

REASSIGN_BLOCK::				;S FF; Send a reassign block command
	.JSB_ENTRY INPUT=<R3,R4,R5>,OUTPUT=<R0>,SCRATCH=<R1,R2>,PRESERVE=<R7,R8>

	BBS	#DEV$V_SWL,UCB$L_DEVCHAR(R3),10$;S FF; Branch if device is read-only,
						;S FF; not possible to reassign block

	BBS	#UCB$V_NOREASSIGN,-		;S FF; Branch if device doesn't support
		UCB$L_DK_FLAGS(R3),10$		;S FF; reassign_block command

	MOVAL	CMD_REASSIGN_BLOCKS,R2		;S FF; Address of reassign blocks command
	SETUP_CMD				;S FF; Perform setup for SCSI command
	ADDL3	SCDRP$L_SVA_USER(R5),-		;S FF; Get defect list buffer address (and
		#3,R0				;S FF; advance to defect list length field)
	MOVB	#4,(R0)+			;S FF; Just one defective block being
						;S FF; reassigned
	MOVAL	SCDRP$L_MEDIA+4(R5),R1		;S FF; Get address beyond block number to
						;S FF; reassign
	.REPT	4
	MOVB	-(R1),(R0)+			;S FF; Fill in a byte of LBN (note bytes are swapped)
	.ENDR
	LOG_ERROR -				;S FF; Log a reassign block error (if the
		TYPE=REASSIGN_BLOCK,-		;S FF; reassign fails, SEND_COMMAND will
		VMS_STATUS=#SS$_NORMAL,-	;S FF; log another error).
		UCB=R3,-
		SCDRP=R5
	MOVL	#REASSIGN_DISCON_TMO,-		;S FF; Set up extended disconnect timeout
		SCDRP$L_DISCON_TIMEOUT(R5)	;S FF; for reassign
	ADDL	G^SGN$GL_VMS7,-			;S FF; Alter the time out value by
		SCDRP$L_DISCON_TIMEOUT(R5)	;S FF;  the amount specified
	SEND_COMMAND_ORDERED			;S FF; Send the SCSI command
	CLEANUP_CMD				;S FF; Cleanup from the command
	RSB					;S FF; Return to caller

10$:	BUG_CHECK INCONSTATE,FATAL		;S FF; Should never attempt to reassign a
						;S FF; block for a write-locked disk,
						;S FF; or for a drive which doesn't support
						;S FF; reassign_block command

	.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, an inquiry command is
; sent to the drive to ensure that it can communicate and that it's a valid
; disk-like device. If the INQUIRY fails, a fork thread is set up to
; periodically poll the drive.
;
; If the drive is the system disk, a PACKACK IRP is initiated for this unit
; to make the volume valid. The drive is then set online, and any I/O that had
; been queued to the device while the decice had been busy or offline is
; initiated.
;
; Context
;
;	Fork Thread
;	IPL = Fork
;	Fork lock held
;
; Inputs:
;
;	R5	- UCB address
;
; Outputs:
;
;	R0	- Status
;	R1-R4	- Destroyed
;
;	ONLINE bit set in the UCB, BSY bit cleared
;-

SET_UNIT_ONLINE::				;F FF; Issue SCSI commands to bring unit online
	.JSB_ENTRY INPUT=<R5>,OUTPUT=<R0>,SCRATCH=<R1,R2,R3,R4>

10$:	MOVL	UCB$L_PDT(R5),R4		;F FF; Get PDT address
	MOVL	R5,R3				;F FF; Copy UCB address
	DK_ALLOC_SCDRP				;F FF; Allocate an SCDRP
	MOVL	UCB$PS_UNITINIT_KPB(R3),- 	;S FF; Save KPB address in SCDRP
		SCDRP$PS_KPB(R5)		;S FF;
        SUBL    #SPI$K_CC_LENGTH,SP             ;S FF; Allocate buffer on the KP stack
        MOVL    SP,R2                           ;S FF; Copy buffer address
        MOVL    #SPI$K_CC_QNUM_ARGS,-           ;S FF; Set argument count in buffer
                SPI$IL_CC_COUNT(R2)
        SPI$CONNECTION_CHAR_GET                 ;S FF; Get current connection characteristics
        BLBC    R0,9$                           ;S FF; Forget it if error
        BISL    #SPI$M_CC_ENA_DISCON,-          ;S FF; Enable disconnects
                SPI$IL_CC_CON_FLAGS(R2)
        SPI$CONNECTION_CHAR_SET                 ;S FF; Set connection characteristics
9$:     ADDL    #SPI$K_CC_LENGTH,SP             ;S FF; Deallocate buffer
	BSBW	INQUIRY				;S FF; Send an inquiry command
	BLBC	R0,11$				;S FF; Branch on error
	BSBW	SET_CONN_CHAR			;S FF; Initial connection characteristics
11$:	DK_DEALLOC_SCDRP CLRSTK=YES		;S FF; Deallocate the SCDRP
	MOVL	R3,R5				;F FF; Copy UCB address
	BLBC	R0,20$				;F FF; Branch if error
	BISL	#UCB$M_ONLINE!UCB$M_BSY,-	;F FF; Set the device online, busy
		UCB$L_STS(R5)			;F FF;

; If the MSCP server is loaded, call its "new device" routine to allow this
; unit to be served.

	MOVL	G^SCS$GL_MSCP_NEWDEV,R2		;F FF; Get address of MSCP routine
	BGEQ	12$				;F FF; Branch if not available
	JSB	(R2)				;F FF; Make this unit available to be served

12$:	CMPL	G^SYS$AR_BOOTUCB,R5		;F FF; Is this the system disk?
	BNEQ	15$				;F FF; Branch if not
	BSBW	ALLOC_IRP			;F FF; Get an IRP
	MOVL	#IO$_PACKACK,IRP$L_FUNC(R3)	;F FF; Store function code
	BISL	#IRP$M_PHYSIO,IRP$L_STS(R3)	;F FF; Set physical I/O bit
	.PRESERVE ATOMICITY
	INCL	UCB$L_QLEN(R5)			;F FF; Increment the queue length
	.NOPRESERVE ATOMICITY
	MOVAL	13$,IRP$L_PID(R3)		;F FF; Set I/O completion return address
	BRB	16$				;F FF; Start the I/O

13$:	.JSB_ENTRY INPUT=<R5>,SCRATCH=<R0>
	BLBC	IRP$L_IOST1(R5),19$		;F FF; Branch if PACKACK failed
	MOVL	IRP$L_UCB(R5),R0		;F FF; Get UCB address
	BBSS	#UCB$V_LCL_VALID,-		;F FF; Set local valid bit and
		UCB$L_STS(R0),14$		;F FF; branch if its already set
	INCB	UCB$B_ONLCNT(R0)		;F FF; Increment online count.
14$:	BRW	DEALLOC_IRP			;F FF; Deallocate the PACKACK IRP

19$:
	
	MOVL	R5,R3				;F FF; Get IRP in r3
	MOVL	IRP$L_UCB(R5),R5		;F FF; Get UCB address IN R5

	FORKLOCK LOCK=UCB$B_FLCK(R5),-		;F FF; Take out device's fork;
		PRESERVE=NO,-			;F FF; lock
		SAVIPL= -(SP)

        CALL_INITIATE                           ;F FF; initiate I/O, again

	FORKUNLOCK LOCK=UCB$B_FLCK(R5),-	;F FF; release device's fork
		PRESERVE=NO,-			;F FF; lock
		NEWIPL= (SP)+ ,-
		CONDITION=RESTORE		;F FF; and up as we were
	RSB


15$:	REMQUE	@UCB$L_IOQFL(R5),R3		;F FF; Remove IRP from device pending queue
	BVS	17$				;F FF; If found one then
16$:	CALL_INITIATE				;F FF;   initiate I/O
	RSB					;F FF;   and return
17$:	TST_SINGLE	18$			;F FF; else are there any other reasons not clear bsy?
	BICL	#UCB$M_BSY, UCB$L_STS(R5)	;F FF; Clear UCB busy bit
18$:	RSB					;F FF; Return

; 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. In the case of the system disk, don't set the unit offline
; and don't flush the queues as this could cause a boot to fail. Instead, just
; poll and hope that eventually the system disk eventually comes back to life.

20$:	CMPL	G^SYS$AR_BOOTUCB,R5		;F FF; Is this the system disk?
	BNEQ	30$				;F FF; Branch if not
	
	MOVZBL	UCB$B_FLCK(R5),-(SP)		;F FF; Set the required fork lock
	PUSHAQ	TWO_SECONDS			;F FF; Set the time delay in ticks
	PUSHL	UCB$PS_UNITINIT_KPB(R5)		;F FF; Get address of KPB
	CALLS	# 3, G^ EXE$KP_TQE_WAIT		;F FF; Fork and wait the desired time
	BRW	10$				;F FF; And try again

30$:	BICL	#UCB$M_ONLINE!-			;F FF; Set the unit offline, not busy
		 UCB$M_BSY,-			;F FF;
		UCB$L_STS(R5)			;F FF;
40$:    REMQUE  @UCB$L_IOQFL(R5),R3             ;F FF; Remove an IRP from the q
        BVS     45$                             ;F FF; Branch if queue was empty
                                                ;F FF; RCL004 13-Dec-95
        MOVAL   UCB$R_BUSY_BIT_IRP(R5), -(SP)   ;F FF; Get the address of the G
        CMPL    R3, (SP)+                       ;F FF; Is this is?
        BNEQ    43$                             ;F FF; Branch if not, OK to flu
        CLRL    (R3)                            ;F FF; Clear FLINK to show its 
        TRACE_SINGLE 810A                       ;F FF; Trace this event
        BRW     40$                             ;F FF; Get next IRP off of I/O 
43$:    INSQUE  (R3),@UCB$L_FLUSH_IOQBL(R5)     ;F FF; Place on end of flush qu
        BRB     40$                             ;F FF; Repeat until I/O queue i
45$:	REMQUE	@UCB$L_FLUSH_IOQFL(R5),R3	;F FF; Remove an IRP from the flush queue
	BVS	50$				;F FF; Branch if queue was empty
	MOVL	R3, UCB$L_IRP(R5)		;F FF; Copy IRP addess to UCB
	CLRL	R1				;F FF; Clear transfer byte count
	MOVL	#SS$_DEVOFFLINE,R0		;F FF; Set device offline status
	CALL_REQCOM				;F FF; Complete the QIO
	BRB	45$				;F FF; Continue to flush queue
50$:
	CLRL	UCB$L_IRP(R5)			;F FF; This I/O has been processed.
	MOVZBL	UCB$B_FLCK(R5),-(SP)		;F FF; Set the required fork lock
	PUSHAQ	THIRTY_SECONDS			;F FF; Set the time delay in ticks
	PUSHL	UCB$PS_UNITINIT_KPB(R5)		;F FF; Get address of KPB
	CALLS	# 3, G^ EXE$KP_TQE_WAIT		;F FF; Fork and wait the desired time
	BRW	10$				;F FF; And try again

	.PAGE
	.SBTTL	WAIT_UNIT_READY	- Wait for unit to become ready
;+
; WAIT_UNIT_READY
;
; This routine polls once every second waiting for the
; drive to become ready (spun up). The first time a test unit ready fails with
; DEVOFFLINE status (which comes from a NOT_READY status from the drive), a
; start unit command is sent to the drive in case the failure was
; due to the drive being spun down. Polling continues for  READ_POLL_CNT seconds
; or until the device returns success status to a to a test unit ready command.
;
; Context:
;
;	SCDRP thread
;	IPL = Fork
;	Fork lock held
;
; Inputs:
;
;	R3	- UCB address
;	R4	- PDT address
;	R5	- SCDRP address
;
; Outputs:
;
;	R0	- Status
;	R1,R2	- Destroyed
;	All other registers preserved
;-

WAIT_UNIT_READY::				;S FF; Wait for unit to become ready
	.JSB_ENTRY INPUT=<R3,R4,R5>,OUTPUT=<R0>,SCRATCH=<R1,R2>

	MOVB	#READY_POLL_CNT,-		;S FF; Initialize retry count
		UCB$B_READY_RETRY(R3)	;
10$:	BSBW	TEST_UNIT_READY			;S FF; Execute TEST UNIT READY command
	BLBC	R0,30$				;S FF; Branch on error
20$:	RSB					;S FF; Return to caller

30$:	CMPL	R0,#SS$_CANCEL			;S FF; If cancel status	
	BEQL	20$                             ;S FF; Then leave now
	CMPL	R0,#SS$_DEVOFFLINE		;S FF; Is device spun down?
	BNEQ	40$				;S FF; Branch if not
	BBS	#UCB$V_FLOPPY,-			;S FF; Branch if this is a floppy, return
		UCB$L_DK_FLAGS(R3),50$		;S FF; immediately
	CMPB	#DC$_DISK,UCB$B_DEVCLASS(R3)	;S FF; Is this really a disk device?
	BNEQ	20$				;S FF; Branch if not, can't issue start unit
	BBSS	#SCDRP$V_FLAG_DISK_SPUN_UP,-	;S FF; Attempt to spin up the disk just
		SCDRP$L_SCSI_FLAGS(R5),-	;S FF; once per PACKACK (branch if this
		40$				;S FF; has already been done)
	BSBW	START_UNIT			;S FF; Start up the drive
	CMPL	R0,#SS$_CANCEL			;S FF; If cancel status	
	BEQL	20$                             ;S FF; Then leave now
40$:	PUSHL	#SPL$C_IOLOCK8			;S FF; Set the required fork lock
	PUSHAQ	ONE_SECOND			;S FF; Set the time delay in ticks
	PUSHL	SCDRP$PS_KPB(R5)		;S FF; Get address of KPB
	CALLS	# 3, G^ EXE$KP_TQE_WAIT		;S FF; Fork and wait the desired time
	DECB	UCB$B_READY_RETRY(R3)		;S FF; Decrement retry count and try again
	BGTR	10$				;S FF; if appropriate
50$:	MOVZWL	#SS$_MEDOFL,R0			;S FF; Set bad status
	BRB	20$				;S FF; Return with error status

	.PAGE
	.SBTTL	DATACHECK_CMP	- Compare user buffer with datacheck buffer
;+
; DATACHECK_CMP
;
; This routine is called by IO_DATACHECK and performs the actual comparison of
; the data in the user buffer with the data in the datacheck buffer. In doing
; this, the user's buffer is temporarily mapped into system space in order to
; perform the CMPC3. The SPTEs used to double map the user buffer were allocate
; when the driver was loaded and are shared between all units.
;
; Note that SCDRP$L_SVAPTE describes the datacheck buffer in the AXP version 
; of this driver.  However, in the VAX version, SCDRP$L_SVAPTE describes the 
; current segment of the user buffer.  In both cases, the original user buffer
; is described by IRP$L_SVAPTE and IRP$L_BOFF.  The mapping to the current user
; buffer segment is obtained by taking into account the bytes that have been
; transferred in prior segments (SCDRP$L_ABCNT) to compute a new SVAPTE and a
; new BOFF into the page mapped by that PTE.
;
; Context:
;
;	SCDRP thread
;	IPL = Fork
;	Fork lock held
;
; Inputs:
;
;	R5	- SCDRP address
;
; Outputs:
;
;	R0	- Status (SS$_NORMAL or SS$_DATACHECK)
;	All other registers preserved
;-

DATACHECK_CMP::					;S FF; Compare user buffer with datacheck buffer
	.JSB_ENTRY INPUT=<R5>,OUTPUT=<R0>,PRESERVE=<R1,R2,R3,R4,R5,R6>

	ASSUME	DATACHECK_SPTE+4 EQ DATACHECK_SVA
	MOVQ	DATACHECK_SPTE,R0		;S FF; Get SVA of datacheck SPTEs and SVA
						;S FF; which these SPTEs map

	; Compute the SVA of the PTE that maps the start of the current
	; segment of the user buffer.  Note that the PTE that maps the start
	; of the user buffer is located via IRP$L_SVAPTE but we have already
	; completed SCDRP$L_ABCNT bytes in prior segments.  Also, compute the
	; BOFF of the start of the segment in this page and the number of pages
	; that are spanned by the current segment.
	;
	MOVL	SCDRP$L_IRP(R5),R3		;S FF; R3 = corresponding IRP
	MOVL	IRP$L_BOFF(R3),R6		;S FF; R6 = byte offset into user buffer
	ADDL	SCDRP$L_ABCNT(R5),R6		;S FF;      (initial boff+completed segments)
	MOVL	MMG$GL_VPN_TO_VA,R2		;S FF; R2 = number of completed pages so far
	EVAX_SRL R6,R2,R2			;S FF;
	EVAX_SLL R2,#PTE$C_SHIFT_SIZE,R2	;S FF; R2 = va offset to skip completed PTEs
	ADDL	IRP$L_SVAPTE(R3),R2		;S FF; R2 = SVAPTE user buffer current segment
	MOVL	MMG$GL_BWP_MASK,R4		;S FF;
	EVAX_AND R6,R4,R6			;S FF; R6 = BOFF into user buffer @SVAPTE=R2
	ADDL3	SCDRP$L_BCNT(R5),R6,R4		;S FF; R4 = byte count of pages in cur seg
	$BYTES_TO_PAGES -			;S FF; Convert to page count
		SOURCE_BYTCNT=R4,-		;S FF;
		DEST_PAGCNT=R4,-		;S FF;
		ROUNDUP=YES			;S FF; Round up

	;
	; Load the previously allocated SPTEs to map the current segment of
	; the user buffer into system space.
	;
10$:	EVAX_LDQ R3,(R2)			;S FF; Get user's PTE
	ADDL	#PTE$C_BYTES_PER_PTE,R2		;S FF; Advance to next PTE

	ASSUME	PTE$M_VALID EQ 1
	BLBC	R3,15$				;S FF; Branch if PTE not valid
	EVAX_SRL R3,#PTE$V_PFN,R3		;S FF; Else, shift PFN to low-order bits
	BRB	20$				;S FF; Join common new PTE builder code
15$:	CALL_PTETOPFN SAVE_R0R1=YES		;S FF; If PTE not valid, convert to PFN
20$:	EVAX_SLL R3,#PTE$V_PFN,R3		;S FF; Get PFN in the right place
	EVAX_OR	 R3,-				;S FF; Set software PTE bits
		#<PTE$M_VALID!PTE$C_KW!PTE$C_KOWN!PTE$M_ASM!PTE$M_WINDOW>,R3
	EVAX_STQ R3,(R0)			;S FF; Store the new PTE
	TBI_SINGLE R1				;S FF; Do TB invalidation

	ADDL	G^MMG$GL_PAGE_SIZE,R1		;S FF; Point to next page
	ADDL	#PTE$C_BYTES_PER_PTE,R0		;S FF; Point to next entry in SPT
	SOBGTR	R4,10$				;S FF; Loop till done

	;
	; Compare the user buffer (via the system space mapping just completed)
	; with the contents of the data check buffer (nonpaged pool).
	;
	ADDL	DATACHECK_SVA,R6		;S FF; Address of double mapped user buffer
	CMPC3	SCDRP$L_BCNT(R5),(R6),-		;S FF; Compare user's buffer with datacheck
		@SCDRP$L_DATACHECK(R5)		;S FF; buffer
	BNEQ	30$				;S FF; Branch if mismatch occurred
	MOVL	#SS$_NORMAL,R0			;S FF; Set success status
	BRB	40$				;S FF; And return
30$:	MOVL	#SS$_DATACHECK,R0		;S FF; Datacheck error
40$:	RSB					;S FF; 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.
;
; If the target returns a hardware error, one retry is performed, on the
; assumption that the error could have been caused by vibration or some other
; spurious event.
;
; Context:
;
;	SCDRP thread
;	IPL = Fork
;	Fork lock held
;
; 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::					;S FF; Send a SCSI command

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

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

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

	MOVL	#SENDCMD_RETRY_CNT,-		;S FF; Initialize send command retry counter
		SCDRP$IS_CMD_RETRY_CNT(R5)	;S FF;

SEND_COMMAND_RETRY:

	.IF DEFINED TRACE_SCSI
	BSBW	TRACE_SCSI_CMD			;S FF Save SCSI command in trace buf
	.ENDC

	IF_CANCEL	10$			;S FF; If server requests
						;S FF; cancel, then return
	SPI$SEND_COMMAND -			;S FF; Send the SCSI command
		TYPE=SCDRP$IS_QUEUE_CHAR(R5)	;S FF; ... based on queue type

	.IF DEFINED TRACE_SCSI
	BSBW	TRACE_SCSI_STS			;S FF; Save SCSI status in trace buf
	.ENDC

	BLBC	R0,20$				;S FF; Branch on error

	ASSUME	SCSI$C_GOOD EQ 0
	BICL3	#SCSI$M_STATUS_BYTE_RESERVED,-	;S DS; Mask off reserved status bits
		@SCDRP$L_STS_PTR(R5), R1	;S DS;
	BNEQ	30$				;S FF; Branch if bad status
	CLRL	SCDRP$L_CL_RETRY(R5)		;S FF; Reset Class Driver retry

10$:	RET					;S FF; Return to caller

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

20$:	CMPL	R0,#SS$_ABORT			;S FF; Aborted command?
	BEQL	10$				;S FF; Branch if so, don't log an error
	CMPL	R0,#SS$_TIMEOUT			;S FF; Command timed out?
	BEQL	10$				;S FF; Branch if so, don't log an error
	CMPL	R0,#SS$_MEDOFL			;S FF; Medium offline?
	BEQL	10$				;S FF; Branch if so, don't log an error
	LOG_ERROR -				;S FF; Log a send command error
		TYPE=SEND_CMD_ERROR,-
		VMS_STATUS=R0,-
		UCB=R3,-
		SCDRP=R5
	BRB	10$				;S FF; 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_BUSY,R1			;S FF; Busy status?
	BEQL	35$				;S FF; Branch if so
	CLRL	SCDRP$L_CL_RETRY(R5)		;S FF; Otherwise, clear Class Driver retry
	CMPB	#SCSI$C_CHECK_CONDITION,R1	;S FF; Check condition status?
	BEQL	40$				;S FF; Branch if so
	LOG_ERROR -				;S FF; Log a send command error
		TYPE=SEND_CMD_ERROR,-
		VMS_STATUS=#SS$_NOTQUEUED,-
		UCB=R3,-
		SCDRP=R5
32$:	MOVL	#SS$_MEDOFL,R0			;S FF; Return a generic status code
	BRW	10$

; The device has returned busy status. In order to catch the case in which
; the device continually returns busy status, set a limit on the time a device
; can continuously return busy status. If the doesn't successfully execute
; a command or return a status other than busy within this time, assume
; the device is hung and pull on bus reset.

35$:	MOVL	SCDRP$L_CL_RETRY(R5),R0		;S FF; Was Class Driver retry set last time?
	BNEQ	36$				;S FF; Branch if so
	ADDL3	#MAX_BUSY_TIME,-		;S FF; Calculate busy timeout time
		G^EXE$GL_ABSTIM,-		;S FF;
		SCDRP$L_CL_RETRY(R5)		;S FF;
	BRB	55$				

36$:	CMPL	R0,G^EXE$GL_ABSTIM		;S FF; Had device been retrying for an excessive
						;S FF; amount of time
	BGTRU	55$				;S FF; Branch if not
	CLRL	SCDRP$L_CL_RETRY(R5)		;S FF; Reset Class Driver retry
	LOG_ERROR -				;S FF; Log a send command error
		TYPE=SEND_CMD_ERROR,-
		VMS_STATUS=#SS$_RETRY,-
		UCB=R3,-
		SCDRP=R5
	SPI$RESET_SCSI_BUS			;S FF; Reset the SCSI bus to attempt to
						;S FF; bring the device back to life
	BRB	32$				;S FF; Use common exit

; A check condition status code was returned. Determine if the associated port driver
; supports AUTO_SENSE, and if the AUTO_SENSE buffer is valid. 
; AUTO Sense buffer is located at SCDRP$PS_SENSE_BUFFER(R5)

40$:	CLRB	SCDRP$B_SENSE_KEY(R5)		;S FF; Initialize saved sense key field
	MOVL	R5,SCDRP$PS_PREV_SCDRP(R5)	;S FF; We don't need additional SCDRP
						;S FF; so we reference our own

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

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

; 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      	;S FF; Sense data address
	MOVZBL	SCSI$SNS$B_ADD_SENSE_LEN(R0),R0	;S FF; Addn sense length
	ADDL3   #1,#SCSI$SNS$B_ADD_SENSE_LEN,R1 ;S FF; Preceding bytes	
	ADDL3	R1,R0,SCDRP$L_TRANS_CNT(R5)    	;S FF; Total sense length

	BSBW	LOG_EXTND_SENSE			;S FF; Log an extended sense error
	BSBW	SAVE_ADDNL_INFO			;S FF; Save any valid additional information
						;S FF; from the extended sense data
	BSBW	TRANS_SENSE_KEY			;S FF; Translate the extended sense key to
						;S FF; a VMS status code in R0

	MOVL	R6,SCDRP$L_SVA_USER(R5) 	;S FF; Restore user buffer address
	MOVL	R7,SCDRP$L_TRANS_CNT(R5) 	;S FF; Restore transfer count
	CLRL	SCDRP$PS_PREV_SCDRP(R5)		;S FF; No additional SCDRP

        CMPW    R0,#SS$_RECOVERR                ;S FF; Recoverable err to retry?
        BEQL    57$                             ;S FF; If so, retry it.
	CMPB	SCDRP$B_SENSE_KEY(R5),-		;S FF; Device (aborted command) error?
		#SCSI$C_ABORTED_COMMAND		;S FF;
	BEQL	57$				;S FF; Branch if so, retry command
	CMPB	SCDRP$B_SENSE_KEY(R5),-		;S FF; Device (hardware) error?
		#SCSI$C_HARDWARE_ERROR		;S FF;
	BEQL	57$				;S FF; Branch if so, retry command
	BRW	10$				;S FF; Branch if not, no need to retry
55$:	MOVZWL	#SS$_MEDOFL,R0			;S FF; Assume failure, preload status
57$:	DECL	SCDRP$IS_CMD_RETRY_CNT(R5)	;S FF; Decrement send command retry count
	BGEQ	SEND_COMMAND_RETRY		;S FF; Branch if retry not exhausted, retry
						;S FF; the send command
	BRW	10$				;S FF; Otherwise, 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 caused by media changes.
;
; - The first UNIT ATTENTION seen after a boot but before a successful PACKACK
;   has completed. This unit attention is generally due to a bus reset having
;   been issued during a boot or crash. IO_PACKACK sets the ATTN_SEEN flag so
;   that any unit attentions seen after the first successful PACKACK will
;   generate errors.
;
; - A NOT READY sense key if the original command was a test unit ready. This
;   is considered normal as it's likely the disk is in the process of being
;   spun up.
;
; - A recoverable error if the addition sense code is RECOVERED ERROR WITH READ
;   RETRIES. This can occur due to disk vibrations and is thus considered
;   acceptable.
;
; 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.
;
; Context:
;
;	SCDRP thread
;	IPL = Fork
;	Fork lock held
;
; Inputs:
;
;	R3	- UCB address
;	R4	- PDT address
;	R5	- SCDRP address for REQUEST_SENSE command
;	SCDRP$PS_PREV_SCDRP(R5)	- Address of SCDRP for original command
;	SCDRP$PS_SVA_USER(R5)   - Current Sense mode data
;
; Outputs:
;
;	R0-R2	- Destroyed
;	All other registers preserved
;-

LOG_EXTND_SENSE::				;S FF; Log an extended sense data error
	.JSB_ENTRY INPUT=<R3,R4,R5>,SCRATCH=<R0,R1,R2>,PRESERVE=<R7,R8>

	MOVL	SCDRP$L_SVA_USER(R5),R0		;S FF; Get address of extended sense data
	EXTZV	#SCSI$SNS$V_SENSE_KEY,-		;S FF; Get sense key
		#SCSI$SNS$S_SENSE_KEY,-		;S FF;
		SCSI$SNS$R_FLAGS(R0),R1		;S FF;
	MOVZBL	SCSI$SNS$B_ADD_SENSE_CODE(R0),R7;S FF; Get ASC
	MOVZBL	SCSI$SNS$B_ADD_SENSE_QUAL(R0),R8;S FF; Get ASQ
	CMPB	R1,#SCSI$C_UNIT_ATTENTION	;S FF; Unit attention sense key?
	BNEQ	10$				;S FF; Branch if not
	CMPB	#SCSI$C_MEDIA_CHANGE,-		;S FF; Unit attention caused by media change?
		SCSI$SNS$B_ADD_SENSE_CODE(R0)	;S FF;
	BEQL	40$				;S FF; Branch if so, don't log an error
	BBCS	#UCB$V_FIRST_ATTN_SEEN,-	;S FF; Branch if this is the first unit
		UCB$L_DK_FLAGS(R3),40$		;S FF; attention seen. Don't log an error

; Log all recoverable errors for floppies. Since errors tend to grow on
; floppies, we want to alert the operator as soon as possible.

10$:	BBS	#UCB$V_FLOPPY,-			;S FF; Branch if this is a floppy, return
		UCB$L_DK_FLAGS(R3),15$		;S FF; immediately

	CMPL	R1,#SCSI$C_RECOVERED_ERROR	;S FF; Recovered error sense key?
	BNEQ	15$				;S FF; Branch if not
; If WORM or Optical drive, ECC correction is common too; do not log that.
; Rather, just ignore these.
	BITL	#<UCB$M_WORM+-			;S FF; WORM and opticals get ECC
		  UCB$M_OPTICAL>,-		;S FF; recovered error a lot. Not
		  UCB$L_DK_FLAGS(R3)		;S FF; significant; do not log it.
	BNEQ	40$
	CMPB	#SCSI$C_RECOVERED_DATA,-	;S FF; Recovered error with read retries?
		SCSI$SNS$B_ADD_SENSE_CODE(R0)	;S FF;
	BEQL	40$				;S FF; Branch if so, dont log an error

;
; For CDROM devices, don't log commands that fail with a sense key of
; Illegal Command, if original QIO is a READ command (readvblk).
; This will prevent the driver from logging errors for reads
; of the home block on cdrom disks mounted foreign.
;
15$:	BBC	#UCB$V_CDROM,-			;S FF; Is this device a CD-ROM drive?
		 UCB$L_DK_FLAGS(R3),19$		;S FF; If NOT then proceed.
	CMPL	R1,#SCSI$C_ILLEGAL_REQUEST	;S FF; Illegal command sense key
						;S FF; Returned for reads of audio tracks
	BNEQ	19$				;S FF; Branch if not
;
; Look up the orginal commands SCDRP and determine whether or not this
; SCDRP is for a READPBLK QIO. If the command is then this is final condition
; in determining whether or not to log an error.
;
	PUSHL	R0
	MOVL	SCDRP$PS_PREV_SCDRP(R5),R0	;S FF; Get original command's SCDRP address
	CMPB	#IO$_READPBLK,-			;S FF; Determine whether the original I/O
		 SCDRP$L_FUNC(R0)		;S FF; was for a READ QIO request?
	BNEQ	17$				;S FF; If not continue on,
	POPL	R0				;S FF; Otherwise, don't log this error
	BRB	40$				;S FF; Branch if so, dont log an error
;
; Restore register R0 and continue on determining whether other errors
; need to be loggged.
;
17$:	POPL	R0

19$:	MOVL	SCDRP$L_CMD_PTR(R5),R2		;S FF; Save address of request sense cmd
	MOVL	SCDRP$PS_PREV_SCDRP(R5),R0	;S FF; Get original command's SCDRP address
	MOVL	SCDRP$L_CMD_PTR(R0),-		;S FF; Copy address of original command to
		SCDRP$L_CMD_PTR(R5)		;S FF; request sense's SCDRP
	MOVB	#SCSI$C_CHECK_CONDITION,-	;S FF; Original command failed with request
		@SCDRP$L_STS_PTR(R5)		;S FF; sense status

; Don't log errors with NOT_READY sense keys.

	CMPB	R1,#SCSI$C_NOT_READY		;S FF; Device not ready sense key?
	BEQL	38$				;S FF; Yes, don't log an error

; Don't log certain Illegal Request errors that can result from sending
; e.g. a 10-byte mode sense to a target that does not support it.

	CMPL	R1,#SCSI$C_ILLEGAL_REQUEST	;S FF; Illegal command sense key
        BNEQ	36$
; Illegal request errors are fairly common on opticals also,due to
; their more limited support of SCSI2; no point logging these
; errors either.
;
	BITL	#<UCB$M_WORM+-			;S FF; WORM and opticals get ECC
		  UCB$M_OPTICAL>,-		;S FF; recovered error a lot. Not
		  UCB$L_DK_FLAGS(R3)		;S FF; significant; do not log it.
	BNEQ	38$				;S FF; Ignore these on optical too
	CMPL	R8,#0
        BNEQ	36$
        CMPL	R7,#^X20
	BEQL	38$ 				;S FF; Skip ASC/ASQ of 20/00
        CMPL	R7,#^X24
	BEQL	38$                            	;S FF; Skip ASC/ASQ of 24/00
36$:	LOG_ERROR -				;S FF; Log an extended sense data error
		TYPE=EXTND_SENSE_DATA,-
		VMS_STATUS=#SS$_NORMAL,-
		UCB=R3,-
		SCDRP=R5
38$:	MOVL	R2,SCDRP$L_CMD_PTR(R5)		;S FF; Restore address of request sense cmd
40$:	RSB					;S FF; 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.
;
; Certain situations cause an additional translation including:
;
; - Recoverable errors with read retries are considered acceptable. Thus, if
;   this combination of sense key and additional sense code occurs, change the
;   VMS status to SS$_NORMAL.
;
; - If the sense key was NOT_READY and the spinup-in-progress flag is set in
;   the UCB, then this is an indication that the device is in the process of
;   being spun up. Return a DEVOFFLINE status to allow the WAIT_UNIT_READY
;   routine to continue to poll for spinup complete. Otherwise, change the
;   status to SS$_MEDOFL to cause mount verification to occur for this device.
;   In either case clear the CD_VALID bit to indicate any UCB stored media
;   specific data is no longer valid.
;
; Context:
;
;	SCDRP thread
;	IPL = Fork
;	Fork lock held
;
; Inputs:
;
;	R5	- SCDRP address - of REQUEST SENSE SCDRP
;
; Outputs:
;
;	R0	- VMS status code
;	R1,R2	- Destroyed
;	All other registers perserved
;-

TRANS_SENSE_KEY::				;S FF; Translate extended sense key to VMS status code
	.JSB_ENTRY INPUT=<R5>,OUTPUT=<R0>,PRESERVE=<R3,R11>,SCRATCH=<R1,R2>

	MOVL	SCDRP$L_SVA_USER(R5),R2		;S FF; Get address of request sense data
	EXTZV	#SCSI$SNS$V_SENSE_KEY,-		;S FF; Get sense key
		#SCSI$SNS$S_SENSE_KEY,-		;S FF;
		SCSI$SNS$R_FLAGS(R2),R1		;S FF;
	MOVL	SCDRP$PS_PREV_SCDRP(R5),R0	;S FF; Get original SCDRP
	MOVB	R1,SCDRP$B_SENSE_KEY(R0)	;S FF; Save sense key in it

; If this is a UNIT ATTENTION sense key, a media change additional sense code,
; the device is NOT mounted, and the "spinup-in-progress" bit is set, then
; return success. This situation can occur if a piece of removable media, such
; as a floppy, is changed in between mounts.

	CMPB	R1,#SCSI$C_UNIT_ATTENTION	;S FF; Unit attention sense key?
	BNEQ	5$				;S FF; Branch if not
	CMPB	#SCSI$C_MODE_CHANGE,-		;S FF; Unit attention caused by mode select
		SCSI$SNS$B_ADD_SENSE_CODE(R2)	;S FF; parameters change?
	BEQL	4$				;S FF; Branch if so
	CMPB	#SCSI$C_MEDIA_CHANGE,-		;S FF; Unit attention caused by media 
		SCSI$SNS$B_ADD_SENSE_CODE(R2)	;S FF; parameters change?
	BNEQ	5$				;S FF; Branch if not
	BBS	#DEV$V_MNT,UCB$L_DEVCHAR(R3),5$	;S FF; Branch if device is mounted
	BBC	#UCB$V_SPINUP_INPROG,-		;S FF; Branch if NOT waiting for unit ready
		UCB$L_DK_FLAGS(R3),5$		;S FF;
3$:	MOVZWL	#SS$_NORMAL,R0			;S FF; Set success status
	BRB	40$				;S FF; Use common exit path
4$:	EXTZV	#IRP$V_FCODE,#IRP$S_FCODE,-	;S FF; Extract I/O function code
		SCDRP$L_FUNC(R0),R11		;S FF;
	CMPL	#IO$_PACKACK, R11		;S FF; Was mode_change due to an IO$_PACKACK?
	BEQL	3$				;S FF; If so, then unit attention was expected
5$:	MOVAL	SENSE_KEY_TABLE,R0		;S FF; Get address of sense key to VMS status
	MOVL	(R0)[R1],R0			;S FF; Pick up VMS error code

; If this is a recoverable error due to read retries, then treat the error
; as success.

	CMPL	R0,#SS$_RECOVERR		;S FF; Recoverable error?
	BNEQ	35$				;S FF; Branch if not
; Recovered errors on optical/WORM are common; do not treat them as problems
	BITL	#<UCB$M_WORM+-			;S FF; WORM and opticals get ECC
		  UCB$M_OPTICAL>,-		;S FF; recovered error a lot. Not
		  UCB$L_DK_FLAGS(R3)		;S FF; significant; do not log it.
	BNEQ	12$				;S FF; Treat recovered err on optical as OK
	CMPB	#SCSI$C_RECOVERED_DATA,-	;S FF; Recovered error with read retries?
		SCSI$SNS$B_ADD_SENSE_CODE(R2)	;S FF;
	BNEQ	40$				;S FF; Branch if not
	CMPB	UCB$B_DEVTYPE(R3),#DT$_RZ55	;S FF; If this is an RZ55 then
						;S FF; perform the required workaround.
	BNEQ	12$				;S FF; If not a RZ55, continue.
	BSBW	RZ55_WORKAROUND			;S FF;
12$:	MOVZWL	#SS$_NORMAL,R0			;S FF; Treat this error as success

; The following code allows allows automatic spin-up of disks to work. If
; a NOT_READY SCSI status is returned by the drive, and the disk spinup is
; in progress, then return DEVOFFLINE status to notify the caller that the
; device is still not fully spun up. Otherwise, return MEDOFL status to
; cause mount verification to kick in.

35$:	CMPL	R0,#SS$_DEVOFFLINE		;S FF; Device offline status?
	BNEQ	40$				;S FF; Branch if not
	BBS	#UCB$V_SPINUP_INPROG,-		;S FF; Branch if spinup is in progress
		UCB$L_DK_FLAGS(R3),40$		;S FF;
30$:	MOVZWL	#SS$_MEDOFL,R0			;S FF; Otherwise, set medium offline status
40$:	RSB					;S FF; Return to caller

	.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. If a recoverable or non-recoverable error
; occurred, the additional data specifies the the failing LBN, which is saved
; in the UCB. Higher level routines (READPBLK, WRITEPBLK) use this information
; later to reassign a block if appropriate.
;
; The addition data is saved in the ORIGINAL SCDRP pointed to by
; SCDRP$PS_PREV_SCDRP(R5) (R5 has REQUEST_SENSE SCDRP).
;
; Context:
;
;	SCDRP thread
;	IPL = Fork
;	Fork lock held
;
; Inputs:
;
;	R3	- UCB address
;	R5	- SCDRP address associated with request sense command
;
;	SCDRP$PS_PREV_SCDRP(R5)	- Address of original SCDRP
;
; Outputs:
;
;	R0,R1	- Destroyed
;
;	SCDRP$B_ADDNL_INFO	- Additional info field from the extended
;				  sense data or -1 if no valid data.
;	UCB$R_SENSE_INFO	- Balance of sense info returned by target.
;	UCB$B_SENSE_LEN		- Additional info length field set to number of
;				  additional sense data bytes.
;-
SAVE_ADDNL_INFO:

	.JSB_ENTRY INPUT=<R3,R5>,SCRATCH=<R0,R1>
	MOVL	SCDRP$PS_PREV_SCDRP(R5),R0	;S FF; Get original SCDRP address
	MOVAL	SCDRP$L_ADDNL_INFO(R0),R1	;S FF; Get address of additional info field
	MOVL	SCDRP$L_SVA_USER(R5),R0		;S FF; Get address of request sense data
	MOVL	#-1,(R1)			;S FF; Assume additional info not valid

	ASSUME	SCSI$SNS$V_VALID EQ 7
	TSTB	(R0)				;S FF; Valid additional info?
	BGEQ	30$				;S FF; Branch if not
	ADDL	#<SCSI$SNS$R_INFORMATION+-	;S FF; Get address just beyond
		SCSI$SNS$S_INFORMATION>,R0	;S FF; Additional Information field
	.REPT	4
	MOVB	-(R0),(R1)+			;S FF; Copy a byte of additional info from
	.ENDR					;S FF; the extended sense data to the UCB.
						;S FF; (Note that bytes are reversed in the
						;S FF; extended sense data).
;+
; The code above copies the first four bytes of additional sense data, if the
; valid bit in byte 1 is set. This code segment copies the balanace of the
; additional sense data bytes returned by the target to the UCB. These
; additional bytes are used by the audio portion of the driver, but can be
; used for any device type.
;-
30$:	PUSHL	R7				;S FF; Save scratch register
	MOVL	SCDRP$L_SVA_USER(R5),R0		;S FF; Get address of request sense data
	MOVAB	UCB$R_SENSE_INFO(R3),R1		;S FF; Get address of additional sense field
	MOVZBL	SCSI$SNS$B_ADD_SENSE_LEN(R0),R7	;S FF; Get count of addl sense bytes
	CMPB	R7,#<ADD_SENSE_LEN>		;S FF; Is the count <= the size of buffer?
	BLEQU	40$				;S FF; Yes, use this count
	MOVZBL	#<ADD_SENSE_LEN>,R7
40$:	MOVAB	SCSI$SNS$R_ADD_INFORMATION(R0),R0 ;S FF; Get address of additional information
	MOVB	R7,UCB$B_SENSE_LEN(R3)		;S FF; Save count of actual number of bytes
						;S FF;	saved in UCB

50$:	MOVB	(R0)+,(R1)+			;S FF; Copy a byte of additional info from
						;S FF; the extended sense data to the UCB.
	SOBGTR	R7,50$				;S FF; Copy all Addl sense bytes
	POPL	R7				;S FF; Restore scratch register.
	RSB					;S FF;
	.DISABLE	LSB

	.PAGE
	.SBTTL	ALLOC_IRP	- Allocate an I/O request packet
;+
; ALLOC_IRP
;
; This routine is called during unit init for the system disk to allocate
; and IRP, which is used to force a PACKACK QIO through the driver.
;
; Context
;
;	Fork Thread
;	IPL = Fork
;	Fork lock held
;
; Inputs:
;
;	R5	- UCB address
;
; Outputs:
;
;	R3	- IRP address
;	R0-R2	- Destroyed
;	All other registers preserved
;-

ALLOC_IRP::					;F FF; Allocate an I/O request packet
	.JSB_ENTRY INPUT=<R5>,OUTPUT=<R3>,SCRATCH=<R0,R1,R2>

	MOVZWL	#IRP$C_LENGTH,R1		;F FF; Load length of IRP
	MOVL	R5,R3				;F FF; Copy UCB address
	MOVL	UCB$PS_UNITINIT_KPB(R5),R5	;F FF; Get KPB address
	BSBW	DK_ALLOC_POOL			;F FF; Get nonpaged pool
	MOVL	R3,R5				;F FF; Restore UCB address
	MOVL	R2,R3				;F FF; Set up IRP pointer
	MOVB	#DYN$C_IRP,IRP$B_TYPE(R3)	;F FF; Store type
	MOVL	R5,IRP$L_UCB(R3)		;F FF; Set up UCB address
	MOVAL	W^DEALLOC_IRP,IRP$L_PID(R3)	;F FF; Store deallocation routine address
	RSB					;F FF; Return to caller

	.PAGE
	.SBTTL	DEALLOC_IRP	- Deallocate an I/O request packet
;+
; DEALLOC_IRP
;
; The routine deallocates the IPR allocated during unit init to queue a PACKACK
; to the system disk.
;
; Context
;
;	Fork Thread
;	IPL = Fork
;	Fork lock held
;
; Inputs:
;
;	R5	- IRP address
;
; Outputs:
;
;	R0	- Destroyed
;	All other registers preserved
;-

DEALLOC_IRP::					;F FF; Deallocate an I/O request packet
	.JSB_ENTRY INPUT=<R5>,SCRATCH=<R0>

	MOVL	IRP$L_UCB(R5),R0		;F FF; Get UCB address
	.PRESERVE ATOMICITY
	DECL	UCB$L_QLEN(R0)			;F FF; Adjust queue length
	.NOPRESERVE ATOMICITY
	MOVL	R5,R0				;F FF; Copy IRP address
	BSBW	DEALLOC_POOL			;F FF; Deallocate the IRP
	RSB					;F FF;

	.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.
;
; Context:
;
;	SCDRP thread
;	IPL = Fork
;	Fork lock held
;
; Inputs:
;
;	R3	- UCB address
;	R5	- SCDRP address
;
; Outputs:
;
;	SCDRP$L_UCB	- UCB address
;	SCDRP$B_FLCK	- Fork lock
;	SCDRP$L_CDT	- CDT address
;	SCDRP$L_SCSI_FLAGS - Initialized
;-

INIT_SCDRP::					;S FF; Initialize an SCDRP
	.JSB_ENTRY INPUT=<R3,R5>,PRESERVE=<R0,R1,R2,R3,R4,R5>

	PUSHR	#^M<R3,R5>			;S FF; Save regs
	MOVC5	#0,.,#0,#SCDRP$K_DK_LENGTH,(R5)	;S FF; Initialize the SCDRP
	POPR	#^M<R3,R5>			;S FF; Restore regs

	MOVW	#SCDRP$K_LENGTH,-		;S FF; Setup SCDRP length field
		SCDRP$W_SCDRPSIZE(R5)
	MOVB	#DYN$C_SCDRP,-			;S FF; Setup SCDRP structure type
		SCDRP$B_CD_TYPE(R5)

	MOVB	UCB$B_FLCK(R3),SCDRP$B_FLCK(R5)	;S FF; Copy the fork lock from UCB to the SCDRP
	MOVL	R3,SCDRP$L_UCB(R5)		;S FF; Save UCB address in SCDRP
	MOVL	UCB$PS_SCDT(R3),SCDRP$L_CDT(R5)	;S FF; Save CDT address in SCDRP
	MOVZWL	UCB$W_PHASE_TMO(R3),-		;S FF; Fill in phase change timeout
		SCDRP$L_DMA_TIMEOUT(R5)		;S FF; field in the SCDRP
	MOVZWL	UCB$W_DISC_TMO(R3),-		;S FF; Fill in disconnect timeout
		SCDRP$L_DISCON_TIMEOUT(R5)	;S FF; field in the SCDRP
	RSB					;S FF;

	.PAGE
	.SBTTL	DK_ALLOC_POOL	- Allocate a block of non-paged pool
;+
; DK_ALLOC_POOL
;
; This routine was formerly called ALLOC_POOL.	Its name was changed to reflect
; a new input:	R5 is a KPB address, not an 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.
;
; Context:
;
;	SCDRP/FORK thread
;	IPL = Fork
;	Fork lock held
;
; 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
;
; The allocated pool is given the following header:
;
; +-------------------------------------------------------+
; |                   requesting UCB                      |
; +-------------------------------------------------------+
; |                   requesting KPB                      |
; +-------------+-------------+---------------------------+
; |DYN$C_SCSICLS|  DYN$C_MISC |          size             |
; +-------------+-------------+---------------------------+
; |              longword allocation length               |
; +-------------------------------------------------------+
;
; The returned R2 points to the first byte following this
; header.
;-

DK_ALLOC_POOL::					;SF FF; Allocate a block of non-paged pool
	.JSB_ENTRY INPUT=<R1,R3,R5>,OUTPUT=<R0,R1,R2>,PRESERVE=<R3,R4,R5>

	ADDL	#DK$DYN$C_LENGTH,R1		;SF FF; Reserve space for a DK pool header
	CMPL	R1,#FKB$C_LENGTH		;SF FF; Requested size smaller than fork block?
	BGEQ	10$				;SF FF; Branch if not
	MOVL	#FKB$C_LENGTH,R1		;SF FF; Use fork block size as minimum
10$:	PUSHL	R1				;SF FF; Save allocation length
	JSB	G^EXE$ALONONPAGED		;SF FF; Allocate a block
	BLBC	R0,20$				;SF FF; Branch if error
	ADDL	#4,SP				;SF FF; Remove allocation length from stack
	PUSHR	#^M<R0,R1,R2>			;SF FF; Save regs
	MOVC5	#0,.,#0,R1,(R2)			;SF FF; Initialize the packet
	POPR	#^M<R0,R1,R2>			;SF FF; Restore regs
	MOVL	R3,DK$DYN$PS_UCB(R2)		;SF FF; Put UCB address in header
	MOVL	R5,DK$DYN$PS_KPB(R2)		;SF FF; Put KPB address in header
	MOVW	R1,DK$DYN$W_SIZE(R2)		;SF FF; Put structure size in header
	MOVB	#DYN$C_MISC,DK$DYN$B_TYPE(R2)	;SF FF; Put structure type in header
	MOVB	#DYN$C_SCSICLS,DK$DYN$B_SUBTYPE(R2) ;SF FF; Put structure sub-type in header
	MOVL	R1,DK$DYN$L_ALLOC_LEN(R2)	;SF FF; Put longword size in header
	ADDL	#DK$DYN$C_LENGTH,R2		;SF FF; Skip point caller past the header
	SUBL	#DK$DYN$C_LENGTH,R1		;SF FF; don't give header memory to caller
	RSB					;SF FF; Return to caller, r0 = status

;
; A pool allocation failure occurred.
; Come back in 2 seconds and retry the operation until successful
;
20$:	MOVZBL	UCB$B_FLCK(R3),-(SP)		;F FF; Set the required fork lock
	PUSHAQ	TWO_SECONDS			;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				;SF FF; Restore allocation length
	BRW	10$				;SF FF; 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 block must
; have been allocated by DK_ALLOC_POOL.
;
; Context:
;
;	SCDRP/FORK thread
;	IPL = Fork
;	Fork lock held
;
; Inputs:
;
;	R0	- Address of block to deallocate
;		DK$DYN$C_LENGTH bytes preceding this address are assumed to have
;		been initialized by DK_ALLOC_POOl
;
; Outputs:
;
;	R0	- Destroyed
;	All other registers perserved
;-

DEALLOC_POOL::					;SF FF; Deallocate a block of non-paged pool
	.JSB_ENTRY INPUT=<R0>,SCRATCH=<R0>,PRESERVE=<R1,R2>

	SUBL	#DK$DYN$C_LENGTH,R0		;SF FF; Point to beginning of allocated block
	PUSHL	DK$DYN$L_ALLOC_LEN(R0)		;SF FF; Set block size parameter
	PUSHL	R0				;SF FF; Set block address parameter
	CALLS	#2,g^EXE_STD$DEANONPGDSIZ	;SF FF; Call deallocation routine
	RSB					;SF FF;

;+=

	.PAGE
	.SBTTL	ALLOC_IRPE	- Allocate IRP Extension.
;+
; ALLOC_IRPE:
;
; This routine is called to allocated and link in an IRP extension (IRPE).
; IRP extensions are used in cases where a single IRP doesn't have enough
; space to maintain the entire state of a single I/O request. In this driver
; there are cases where a single request may have 1, 2 or 3 user buffers that
; must be made accessable to the startio routine. For this purpose we use 
; IRP extensions.
;
; The original IRP will contain the AUCB state. One IRPE will contain the
; state of destination buffer (TOC Data), and another IRPE will contain
; the state of the optional sense buffer.  
;
; If a sense buffer is specified, then the original IRP will point directly to
; the sense buffer IRPE, and the sense buffer IRPE will in turn point to
; the destination buffer IRPE, if any.  So there will be one IRP and
; 2 IRPE's, singly linked via irp(e)$l_extend.
;
; If a sense buffer is not specified, then the original IRP will point 
; directly to the destination buffer IRPE, if any.  So there will
; be one IRP and one IRPE.
;
;
; Context:
;
;	User thread
;	IPL = ASTDEL
;	Locks - none
;
; Inputs:
;
;	R3 - Original IRP 
;	Other registers have normal FDT context.
;
; Outputs:
;	R0	- Status
;	R1	- size allocated
;	R2	- Address of allocated IRPE.
;	R3	- Address of allocated IRPE.
;	R4	- possible use by exe$allocirp
;
;	Parent IRP(E) fields modified:
;		IRPE$L_EXTEND	- Assigned address of IRPE
;		IRPE$L_STS	- IRPE$M_EXTEND flag set
;
;	Extended IRP field modified:
;		IRPE$L_SEQNUM	- Address of parent IRP(E)
;		IRPE$L_SVAPTE	- Cleared
;
;-
ALLOC_IRPE::					;U A; Allocate IRP Extension.
	.JSB_ENTRY INPUT=<R3>,OUTPUT=<R0,R1,R2,R3,R4>

	CALL_ALLOCIRP				;U A; Allocate extended IRP (in R2)
	BLBC	R0,50$				;U A; Exit if fails
	MOVL	R3,IRPE$L_DRIVER_P1(R2)		;U A; Save original IRP address in IRPE
	TSTL	IRP$L_EXTEND(R3)		;U A; Does a previous IRPE already 
						;U A; exist (for sense buffer)?
	BEQL	10$				;U A; No, so link the current IRPE directly 
						;U A; to the IRP.
	MOVL	IRP$L_EXTEND(R3),R3             ;U A; Else get addr of sense buffer IRPE.

10$:	MOVL	R2,IRP$L_EXTEND(R3)		;U A; Link parent IRP(E) to descendant IRP(E).
	BISL	#IRP$M_EXTEND,-			;U A; Flag this as an extended IRP
		 IRP$L_STS(R3)			;U A;
	CLRL	IRPE$L_DRIVER_P0(R2)		;U A; Mark as a destination IRPE for starters.
						;U A; 0 = destination buffer IRPE
						;U A; nonzero = sense buffer IRPE
	MOVB	#DYN$C_IRPE,IRPE$B_TYPE(R2)	;U A; Mark this as an IRPE
	MOVB	IRPE$B_RMOD(R3),-		;U A; Copy RMOD field
		IRPE$B_RMOD(R2)	
	MOVL	IRP$L_FUNC(R3),-		;U A; Copy IO function code from 
		 IRP$L_FUNC(R2)			;U A; parent IRP(E)
	MOVL	R3,IRP$L_SEQNUM(R2)		;U A; Save Parent IRP(E) Address
	MOVL	R2,R3				;U A; Get IRPE address into R3
	MOVL	#<IRP$M_FUNC!IRP$M_PHYSIO>,-	;U A; Signal to IOPOST that
		  IRP$L_STS(R3)			;U A; this is a Direct, Read, Physical I/O

;+
; Clear the following fields in the IRPE, since the exit code in the audio
; path determines whether there are sense or destination buffers used for this
; I/O, based on whether or not these fields are zero.
;-
	CLRL	IRPE$L_SVAPTE(R3)		;U A; Clear P0 SVAPTE in IRPE
	CLRL	IRP$L_WIND(R3)                  ;U A; Clear S0 SVAPTE 
	CLRL	IRP$L_OBCNT(R3)                 ;U A; Clear #PTEs allocated
	CLRL	IRP$L_SEGVBN(R3)                ;U A; Clear S0 VA of buffer.

50$:	RSB					;U A;

;+=

	.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, disconnect and
; synchronous flags, and device timeout values in the UCB.
;
; If the revision field in the inquiry data indicates the device is out-of-rev,
; then don't allow disconnects for this device. It's been shown that one of the
; hardest things to get right is the disconnect/reconnect logic. Thus, if the
; device is not up to rev there's a good chance this logic is broken and
; attempting to use the device with disconnects enabled could cause bus problems.
;
; Context:
;
;	SCDRP thread
;	IPL = Fork
;	Fork lock held
;
; Inputs:
;
;	R0	- Address of INQUIRY data
;	R3	- UCB address
;
; Outputs:
;
;	R0,R1	- Destroyed
;-

GET_DEVICE_TYPE::				;S FF; Determine device type from inquiry data
	.JSB_ENTRY INPUT=<R0,R3>,SCRATCH=<R0,R1>,PRESERVE=<R2,R4,R5,R6>
  
	MOVAL	SCSI_DEVICE_TABLE,R1		;S FF; Get product ID translation table
10$:	TSTB	DTYP_TYPE(R1)			;S FF; End of table
	BEQL	40$				;S FF; Branch if so
	CMPL	DTYP_ID_STRING(R1),-		;S FF; Product IDs match?
		SCSI$INQ$B_PRODUCT_ID(R0)	;S FF;  (low-order)
	BEQL	20$				;S FF; Branch if so
	ADDL	#DTYP_TABLE_ENTRY_SIZE,R1	;S FF; Advance to next entry in table
	BRB	10$				;S FF; Try next entry
20$:	CMPL	DTYP_ID_STRING+4(R1),-		;S FF; Product IDs match?
		SCSI$INQ$B_PRODUCT_ID+4(R0)	;S FF; (high-order)
	BEQL	21$				;S FF; Branch if so
	ADDL	#DTYP_TABLE_ENTRY_SIZE,R1	;S FF; Advance to next entry in table
	BRB	10$				;S FF; Try next entry in table
21$:	MOVB	DTYP_TYPE(R1),-			;S FF; Found a match, extract device type
		UCB$B_DEVTYPE(R3)
	BSBW	CHECK_REV_LEVEL			;S FF; Check for an acceptable rev level
	BBC	#DEV$V_DTN,-			;S FF; If DTN bit is set, handle DDR
		UCB$L_DEVCHAR2(R3),22$		;S FF;	mechanism
	PUSHR	#^M<R0,R1>			;S FF; Save registers across call
        PUSHL	R3				;S FF; ioc$remove_device_type(&ucb) 
        CALLS	#1,G^IOC$REMOVE_DEVICE_TYPE	;S FF; 
	POPR	#^M<R0,R1>			;S FF; Restore registers

	;
	; Detemine if device is a RAID device
	;
22$:	CMPL	#^A/ADP-/,SCSI$INQ$B_PRODUCT_ID(R0) ;S FF; Is this an HSZ10 proto?
	BEQL	23$				;S FF; Yes, set RAID device
	CMPL	#^A/HSZ1/,SCSI$INQ$B_PRODUCT_ID(R0) ;S FF; Is this an HSZ10?
	BEQL	23$				;S FF; Yes, set RAID device
	CMPL	#^A/HSZ2/,SCSI$INQ$B_PRODUCT_ID(R0) ;S FF; Is this an HSZ20?
	BEQL	23$				;S FF; Yes, set RAID device
	CMPL	#^A/HSZ4/,SCSI$INQ$B_PRODUCT_ID(R0) ;S FF; Is this an HSZ40?
	BEQL	23$				;S FF; Yes, set RAID device
	BRB	24$
23$:	BISL	#UCB$M_RAID,UCB$L_DK_FLAGS(R3)	;S FF; RAID device
	BRB	25$
24$:	BICL	#UCB$M_RAID,UCB$L_DK_FLAGS(R3)	;S FF; Not RAID device

25$:	MOVB	DTYP_FLAGS(R1),R2		;S FF; Get disconnect, synchronous flags
	ASSUME	UCB$V_DISCONNECT+1 EQ UCB$V_SYNCHRONOUS
	INSV	R2,#UCB$V_DISCONNECT,-		;S FF; Initialize these flags in the UCB
		#2,UCB$L_DK_FLAGS(R3)		;S FF;

	;
	; Determine if device supports command queueing
	;
	BBC	#UCB$V_PORT_CMDQ, -		;S FF; Make sure port supports CMDQ
		UCB$L_DK_FLAGS(R3), 28$		;S FF;
	BBC	#UCB$V_DISCONNECT,-		;S FF; Make sure port supports disconnects
		UCB$L_DK_FLAGS(R3), 28$		;S FF;
	CMPB	#SCSI$C_ANSI_SCSI_1,-		;S FF; Check SCSI Version CCS(SCSI-1) or SCSI 2?
		UCB$B_SCSI_VERSION(R3)		;S FF;
	BGEQ	28$				;S FF; Br if CCS or SCSI-1
	BBC	#SCSI$INQ$V_CMDQUE,-		;S FF; Br if CmdQue bit clear
		SCSI$INQ$R_FLAGS(R0),28$
	BISL	#UCB$M_CMDQ,UCB$L_DK_FLAGS(R3)	;S FF; CMDQ supported!
	BISB	#1,UCB$Q_DEVDEPEND2+3(R3)	;S FF; Set TCQ "shadow copy"
28$:	MOVL	DTYP_MEDIA_ID(R1),-		;S FF; Save media ID
		UCB$L_MEDIA_ID(R3)
	MOVW	DTYP_PHASE_TMO(R1),-		;S FF; Phase change timeout
		UCB$W_PHASE_TMO(R3)
	MOVW	DTYP_DISC_TMO(R1),-		;S FF; Disconnect timeout
		UCB$W_DISC_TMO(R3)
	CMPW	#DEFAULT_DISCONNECT_TIMEOUT,-	;S FF; Is this an RZ type device?
	   	UCB$W_DISC_TMO(R3)
	BNEQ    29$				;S FF; BR if it is not
	ADDL	G^SGN$GL_VMS7,-			;S FF; Alter the time out by the
	        UCB$W_DISC_TMO(R3)		;S FF;  specified amount.
29$:	BBC	#UCB$V_OUT_OF_REV,-		;S FF; Branch if the device is NOT out-of-rev
		UCB$L_DK_FLAGS(R3),30$		;S FF;
	.IF DEFINED RZ74_CACHE
	CMPB	#DT$_RZ74,UCB$B_DEVTYPE(R3)	;S FF; Is this the RZ74 disk drive?
	BEQL	30$				;S FF; If so, don't disable disconnects
	.ENDC
	BICB	#UCB$M_DISCONNECT,-		;S FF; If the device is out-of-rev, then
		UCB$L_DK_FLAGS(R3)		;S FF; don't allow disconnects

30$:	CMPB    #DT$_GENERIC_DK,-       	;S FF; Is the device in the table?
                UCB$B_DEVTYPE(R3)       	;S FF;
        BNEQ    35$                     	;S FF; Yes, don'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				;S FF; Save stack pointer
	CLRL	R5				;S FF; Initialize "last was blank" flag
	BICL	#7,SP				;S FF; Quad align the stack
	SUBL	#32,SP				;S FF; Allocate space for name and such
	MOVL	SP,R1				;S FF; Setup pointer into name buffer
	MOVAB	SCSI$INQ$B_VENDOR_ID(R0),R0	;S FF; Point to vendor ID
	MOVAB	SCSI$INQ$S_VENDOR_ID(R0),R4	;S FF; Vendor ID is 8 bytes long

301$:	CMPL	R0,R4				;S FF; End of field?
	BEQL	320$				;S FF; Yes, continue with product ID
	CMPB	#^X20,(R0)+			;S FF; Is character a blank
	BNEQ	302$				;S FF; No, clear flag and copy
	BLBS	R5,301$				;S FF; If previous char blank, skip this one
	MOVL	#1,R5				;S FF; Otherwise, set "last was blank" flag
	MOVB	#^X20,(R1)+			;S FF;   and store a blank
	BRB	301$				;S FF; Back for next character
302$:	CLRL	R5				;S FF; Clear "last was blank" flag
	MOVB	-1(R0),(R1)+			;S FF;   and copy the character
	BRB	301$				;S FF; 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	SCSI$INQ$S_PRODUCT_ID(R0),R4	;S FF; Product ID is 16 bytes long
	BLBS	R5,321$				;S FF; If trailing blank on Vendor, continue
	MOVL	#1,R5				;S FF; Otherwise, insert a blank between
	MOVB	#^X20,(R1)+			;S FF;   vendor and product IDs
321$:	CMPL	R0,R4				;S FF; See if we have a full type name
	BEQL	330$				;S FF; Yes, we're done
	CMPB	#^X20,(R0)+			;S FF; Is the next character a blank?
	BNEQ	322$				;S FF; No, go check for copyright
	BLBS	R5,321$				;S FF; If previous char blank, skip this one
	MOVL	#1,R5				;S FF; Otherwise, set "last was blank" flag
	MOVB	#^X20,(R1)+			;S FF;   and store a blank
	BRB	321$				;S FF; Back for next character

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

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

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

35$:	RSB					;S FF;S FF; Return to caller


; No matching product ID field was found in the table. Assume this is a generic
; SCSI disk.

40$:	MOVAL	GENERIC_SCSI_DISK,R1		;S FF; Use generic SCSI disk type
	BRB	21$				;S FF;

	.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 data with
; the minimum revision level stored in the SCSI_DEVICE_TABLE. 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.
;
; Context:
;
;	SCDRP thread
;	IPL = Fork
;	Fork lock held
;
; Inputs:
;
;	R0	- Address of inquiry data retured by target
;	R1	- Address of table entry in SCSI_DEVICE_TABLE
;	R3	- UCB address
;
; Outputs:
;
;	All registers preserved
;	An error is logged of the device is out-of-rev
;-

CHECK_REV_LEVEL::				;S FF; Check for an out-of-rev device
	.JSB_ENTRY INPUT=<R0,R1,R3>,PRESERVE=<R1,R2,R7,R8>

	BICL	#UCB$M_OUT_OF_REV,-		;S FF; Clear out-of-rev flag in the UCB
		UCB$L_DK_FLAGS(R3)		;S FF;
	ADDL	#DTYP_MINREV,R1			;S FF; Min rev level in SCSI_DEVICE_TABLE
	MOVAL	SCSI$INQ$B_PRODUCT_REVISION(R0),R2 ;S FF; Address of rev level in inquiry data
	.REPT	SCSI$INQ$S_PRODUCT_REVISION	;S FF; Check four bytes of revision data
	CMPB	(R2)+,(R1)+			;S FF; Check a byte of revision
	BGTRU	10$				;S FF; Branch if above minimum
	BLSSU	20$				;S FF; Branch of below minimum
	.ENDR					;S FF;
10$:	RSB					;S FF; Return to caller

20$:	LOG_ERROR -				;S FF; Log an invalid inquiry data error
		TYPE=INV_INQUIRY_DATA,-		;S FF;
		VMS_STATUS=#SS$_NORMAL,-	;S FF;
		UCB=R3				;S FF;
	BISL	#UCB$M_OUT_OF_REV,-		;S FF; Set out-of-rev flag in the UCB
		UCB$L_DK_FLAGS(R3)		;S FF;
	BRB	10$				;S FF; Use common exit

	.PAGE
	.SBTTL	SETUP_CMD	- Common setup for all SCSI commands
;+
; SETUP_CMD
;
; This routine 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.
;
; Context:
;
;	SCDRP thread
;	IPL = Fork
;	Fork lock held
;
; 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_BOFF	- Byte offset of S0 "user" buffer
;	SCDRP$L_SVAPTE	- SVAPTE of of S0 "user" buffer
;
; If a class driver private buffer is allocated elsewhere (e.g. mode sense processing), then the allocating routine fills in the
; following fields.  Otherwise, SETUP_CMD has these outputs:
;
;	SCDRP$L_SVA_USER- Address of S0 "user" buffer
;	SCDRP$L_BCNT	- Length of S0 "user" buffer (should be >= alloc len inCDB)
;	IRP$V_FUNC	- SET/CLEAR to indicate READ/WRITE from S0 "user" buffer
;
; 	
;-

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

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

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

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

; NOTE: SCSI-2 suggests that the SCSI LUN field be left with a zero and
; that message handshaking between the port driver and target convey LUN
; information. UCB$B_LUN(R3) is this value

	ASHL	#-1,R0,R0			;S FF; Change byte count to word count
10$:	MOVW	(R2)+,(R1)+			;S FF; Copy a byte of SCSI command
	SOBGTR	R0,10$				;S FF; Repeat for entire SCSI command

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

	.BRANCH_LIKELY
	BBC -                             	;S FF; Did we already allocate a buffer?
		#SCDRP$V_FLAG_CL_PRIVATE_BUFF,-
		SCDRP$L_SCSI_FLAGS(R5),15$

; Code for pre-supplied private buffer

	TSTL	SCDRP$L_BCNT(R5)	     	;S FF; Branch if zero length
	BEQL	20$
	MOVL	SCDRP$L_SVA_USER(R5),R1		;S FF; Get start address of buffer		
	ADDL	#2,R2                           ;S FF; Skip over length field
        BRW	18$                             ;S FF; Branch to common code

; Code to supply a buffer now

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

; Common code

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

20$:	MOVZWL	#SS$_NORMAL,R0			;S FF; Set success status
	RET					;S FF;

30$:	CLRL	SCDRP$L_BCNT(R5)		;S FF; No data being transferred
	BRB	20$				;S FF; 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.
;
; Context:
;
;	SCDRP thread
;	IPL = Fork
;	Fork lock held
;
; Inputs:
;
;      4(AP)	- PDT address
;      8(AP)	- SCDRP address
;
; Outputs:
;
;	R2	- Destroyed
;	All other registers preserved
;-

CLEANUP_CMD::					;S FF; Common cleanup for all SCSI commands
	.CALL_ENTRY INPUT=<>,SCRATCH=<R0,R1>,PRESERVE=<R2,R4,R5>

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

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

	BBCC	#SCDRP$V_FLAG_BUFFER_MAPPED,-	;S FF; Branch if no buffer has been mapped
		SCDRP$L_SCSI_FLAGS(R5),10$	;S FF;
	SPI$BUFFER_UNMAP			;S FF; Unmap the mapped buffer
10$:	BBCC	#SCDRP$V_FLAG_S0BUF,-		;S FF; Branch if this is not an S0 "user"
		SCDRP$L_SCSI_FLAGS(R5),-	;S FF; buffer
		20$				;S FF;
	MOVL	SCDRP$L_SVA_USER(R5),R0		;S FF; Get address of S0 user buffer
	CLRL	SCDRP$L_SVA_USER(R5)		;S FF; Buffer no longer owned
	BBS -                             	;S FF; Private buffer to be handled by other code
		#SCDRP$V_FLAG_CL_PRIVATE_BUFF,-
		SCDRP$L_SCSI_FLAGS(R5),20$
	BSBW	DEALLOC_POOL			;S FF; Deallocate the buffer
20$:	MOVL	SCDRP$L_CMD_BUF(R5),R0		;S FF; Get address of command buffer
	SPI$CMD_BUFFER_DEALLOC			;S FF; Deallocate the command buffer
30$:	RET					;S FF;

;+=

	.PAGE
	.SBTTL	SETUP_SENSE_BUFFER - Setup user sense data buffer
;+
; SETUP_SENSE_BUFFER:
;
; This routine is called to allocate an IRPE and prepare the user supplied
; sense data buffer. The sense data buffer will be locked down and double
; mapped. The mapping information for this buffer will be saved in the IRPE.
;
; Context:
;
;	User thread
;	IPL = ASTDEL
;	Locks - none
;
; Inputs:
;
;	R3 - Original IRP
;	R5 - UCB Address
;	Other registers have normal FDT context.
;
; Outputs:
;
;	R0	- Status
;	R3	- Original IRP, preserved.
;-
SETUP_SENSE_BUFFER::				;U A; Setup user sense data buffer
	.JSB_ENTRY INPUT=<R3,R5>,OUTPUT=<R0>,SCRATCH=<R1,R2>,PRESERVE=<R3>

	BSBW	ALLOC_IRPE			;U A; Allocate an IRP extension
	BLBC	R0,90$				;U A; Exit if no IRPE

;+
; Now, lockdown and double map the users sense buffer, then
; mark the IRPE as a "sense buffer IRPE" by setting a nonzero
; value in IRPE$L_DRIVER_P0.  (A zero in IRPE$L_DRIVER_P0
; indicates this is a destination buffer IRPE).
;-

	ASSUME	CD_SENSE_ADDR+4 EQ CD_SENSE_CNT
	MOVL	UCB$PS_AUCB_ADDR(R5),R2		;U A; Get AUCB pointer
	MOVL	CD_SENSE_ADDR(R2),R0		;U A; Get address of SENSE buffer.
	MOVL	CD_SENSE_CNT(R2),R1		;U A; Get size of SENSE buffer.
	MOVL	R1,IRP$L_BCNT(R3)		;U A; Copy Byte count to IRPE
	MOVL	G^MMG$GL_BWP_MASK,R2		;U A; Byte-within-page mask
	EVAX_AND R0,R2,R2			;U A; Get new byte offset within page
	MOVL	R2,IRP$L_BOFF(R3)		;U A; Update BOFF
	BSBW	AUDIO_MAP_PAGE			;U A; Locks and Maps sense buffer
	BLBC	R0,90$				;U A; Return on error
	MOVL	#1,IRPE$L_DRIVER_P0(R3)		;U A; Indicate this is
						;U A; a sense buffer IRPE
90$:	RSB					;U A;

;+=

	.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 "DISK" and "GENERIC SCSI DISK" 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 type 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:
;
;	R4	- SCDRP address (or zero if none)
;	R5	- UCB address
;
; Outputs:
;
;	All registers preserved
;-
LOG_ERROR::					;? ??; Write an entry to the errorlog file
	.JSB_ENTRY INPUT=<R4,R5>,PRESERVE=<R0,R1,R2,R7,R9,R10>

	BBS	#UCB$V_DISABL_ERRLOG,-		;? ??; Branch if errorlogging is disabled
		UCB$L_DK_FLAGS(R5),40$		;? ??; for this device
	BBC	#UCB$V_ONLINE,UCB$L_STS(R5),40$	;? ??; Branch if device is offline
	MOVL	UCB$L_ERROR_TYPE(R5),R7		;? ??; Save error type
	BBCS	R7,UCB$L_ERR_MASK(R5),5$	;? ??; Branch if error type not logged yet
	BBC	R7,DUPLICATE_ERR_MASK,40$	;? ??; Branch if this is a type of error
						;? ??; that should be logged just once
5$:	MOVB	UCB$B_DEVTYPE(R5),R9		;? ??; Save SCSI device type
	BNEQ	10$				;? ??; Branch if device type known
	MOVB	#DT$_GENERIC_DK,-		;? ??; Fill in generic type so ERF can
		UCB$B_DEVTYPE(R5)		;? ??; translate the errlog packet
10$:	MOVB	UCB$B_DEVCLASS(R5),R10		;? ??; Save DEVCLASS field
	MOVB	#DC$_DISK,-			;? ??; Temporarily set this field to a disk
		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	R4				;? ??; Was an SCDRP passed?
	BEQL	20$				;? ??; No, I/O not in progress
	MOVL	SCDRP$L_IRP(R4),UCB$L_IRP(R5)	;? ??; Copy 'active' IRP address
	BEQL	20$				;? ??; Branch if no 'active' IRP
	CALL_DEVICERR SAVE_R0R1=NO		;? ??; Log a device error
	CLRL	UCB$L_IRP(R5)			;? ??; No longer intersted in 'active' IRP
	BBCC	#UCB$V_ERLOGIP,UCB$L_STS(R5),30$;? ??; Clear error log in progress.
	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 REQCOM, but such
; code does not execute when UCB$V_ERLOGIP is clear, so we do it here:

	MOVL	UCB$L_STS(R5),EMB$L_DV_STS(R2)	;? ??; Insert final device status.
	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)		;? ??;
	CLRL	EMB$Q_DV_IOSB(R2)		;? ??; Clear I/O status, since we
	CLRL	EMB$Q_DV_IOSB+4(R2)		;? ??; 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
40$:	RSB					;? ??; Return to caller

	.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.
;
; Context:
;
;	SCDRP Thread
;	IPL = Fork
;	Fork lock held
;
; Inputs:
;
;	R3	- UCB address
;	R4	- SPDT address
;	R5	- SCDRP address
;
; Outputs:
;
;	R0	- Status
;	R1,R2	- Destroyed
;	All other registers preserved
;-

SET_CONN_CHAR::					;S FF; Modify connection characteristics
	.JSB_ENTRY INPUT=<R3,R4,R5>,OUTPUT=<R0>,SCRATCH=<R1,R2>

	SUBL	#SPI$K_CC_LENGTH,SP		;S FF; Allocate buffer on the KP stack
	MOVL	SP,R2				;S FF; Copy buffer address
	MOVL	#SPI$K_CC_QNUM_ARGS,-		;S FF; Set argument count in buffer
		SPI$IL_CC_COUNT(R2)
	SPI$CONNECTION_CHAR_GET			;S FF; Get current connection characteristics
	BLBC	R0,10$				;S FF; Branch on error

	ASSUME	SPI$M_CC_ENA_DISCON EQ 1
	EXTZV	#UCB$V_DISCONNECT,#1,-		;S FF; Fill in disconnect flag
		UCB$L_DK_FLAGS(R3),-		;S FF;
		SPI$IL_CC_CON_FLAGS(R2)		;S FF;

	EXTZV	#UCB$V_SYNCHRONOUS,#1,-		;S FF; Fill in synchronous flag
		UCB$L_DK_FLAGS(R3),-		;S FF;
		SPI$IL_CC_SYNCHRONOUS(R2)	;S FF;

	CLRL	SPI$IL_CC_SCSI_FLAGS(R2)	;S FF; We leave FLUSHQ & FREEZEQ zero

	IF_NOT_CMDQ	1$			;S FF; Br if no CMDQ support
	BISL	#SPI$M_CC_CMDQ,-		;S FF; Fill in Command queue flag
		SPI$IL_CC_SCSI_FLAGS(R2)	;S FF;
	MOVL	UCB$L_QDEPTH(R3),-		;S FF; Set max queue depth
		SPI$IL_CC_MAX_QDEPTH(R2)	;S FF;
1$:	CMPB	#SCSI$C_ANSI_SCSI_2,-		;S FF; Are we at least SCSI-2?
		UCB$B_SCSI_VERSION(R3)		;S FF;
	BGTR	2$				;S FF; Nope
	BISL	#SPI$M_CC_SCSI_2,-		;S FF; set SCSI_2 char bit
		SPI$IL_CC_SCSI_FLAGS(R2)	;S FF;
2$:	SPI$CONNECTION_CHAR_SET			;S FF; Set the connection characteristics
10$:	ADDL	#SPI$K_CC_LENGTH,SP		;S FF; Clear buffer off the stack
	BLBS	R0,20$				;S FF; Branch if success status
	MOVL	#SS$_CTRLERR,R0			;S FF; Otherwise, return a reasonable status
20$:	RSB					;S FF; Return to caller

	.PAGE
	.SBTTL	SAVE_CONN_CHAR	- Save Connection Characteristics
;+
; SAVE_CONNECTION_CHAR
;
; This routine saves the connection characteristics into a storage area
; allocated by this routine, and pointed to by the field UCB$PS_SAVE_CONN_CHAR.
; The reason behind doing this is to save the device's characteristics prior
; to performing an operation that would change the device characteristics from
; what a set mode operation does.
;
; An example of such an operation is mixed-mode media support of CDROMS.
;
; Context:
;
;	SCDRP thread
;	IPL = Fork
;	Fork lock held
;
; Inputs:
;	R3	- UCB address
;	R4	- PDT address
;	R5	- SCDRP address
;
; Outputs:
;
;	R0	- Status
;	R1,R2	- Destroyed
;	All other registers preserved
;
;-

SAVE_CONN_CHAR::				;S FF; Save Connection Characteristics
	.JSB_ENTRY INPUT=<R3,R4,R5>,OUTPUT=<R0>,SCRATCH=<R1,R2>

; Add code to save connection characteristics that may be changed from
; the functionality that is being performed.  This code is specifically
; being added to support mixed mode audio/data disks, but can be used
; for other processing as well.
;

	MOVL	UCB$PS_SAVE_CONN_CHAR(R3),R2	;S FF; Pick up the connection char buffer
	BNEQ	10$				;S FF; Branch if we do indeed have one
						;S FF; If we do not have one for this UCB,
						;S FF; then allocate a permanent one
	MOVL	#SPI$K_CC_LENGTH,R1		;S FF; Size of get/set conn char buffer
	PUSHL	R5				;S FF; Save SCDRP address
	MOVL	SCDRP$PS_KPB(R5),R5		;S FF; Get KPB address
	BSBW	DK_ALLOC_POOL			;S FF; Allocate pool
	POPL	R5				;S FF; REstore SCDRP address
	MOVL	R2,-				;S FF; Save the location into our storage
		UCB$PS_SAVE_CONN_CHAR(R3)	;S FF; area in the UCB
10$:	MOVL	#SPI$K_CC_QNUM_ARGS,(R2)	;S FF; Set argument count in buffer
	SPI$CONNECTION_CHAR_GET			;S FF; Get current connection characteristics
	RSB					;S FF;

	.PAGE
	.SBTTL	RESTORE_CONN_CHAR	-	Restore Connection
	.SBTTL	- Characteristics From UCB Area
;+
; RESTORE_CONN_CHAR - Restore Connection Characteristics
;
; The objective of this routine is to restore the connection characteristics
; save by a previous call to the SAVE_CONNECT_CHAR routine.
;
; Context:
;
;	SCDRP thread
;	IPL = Fork
;	Fork lock held
;
; Inputs:
;	R3	- UCB address
;	R4	- PDT address
;	R5	- SCDRP address
;
; Outputs:
;
;	R0	- Status
;	R1,R2	- Destroyed
;	All other registers preserved
;-
RESTORE_CONN_CHAR::				;S FF;	Restore Connection Charateristics
	.JSB_ENTRY INPUT=<R3,R4,R5>,OUTPUT=<R0>,SCRATCH=<R1,R2>

	MOVL	UCB$PS_SAVE_CONN_CHAR(R3),R2	;S FF; Restore Address of Connection Buffer
	SPI$CONNECTION_CHAR_SET			;S FF; Set the connection characteristics
	RSB					;S FF; Return status back from this routine

	.PAGE
	.SBTTL	ATTEMPT_REORDER	- Attempt to reorder read/write requests for
	.SBTTL	-		  seek optimization
;+
; ATTEMPT_REORDER
;
; This routine is called at the end of STARTIO to scan the pending queue in
; the UCB and determine the most more optimal request to service next.
; An elevator algorithm is used. Thus, the disk head moves in one direction,
; servicing all requests in that direction until no more exist. The CUR_LBN
; field keeps track of the current location of the heads, while the SEEK_DIR
; bit specifies the current direction of motion. An assumption here is that
; the disk is laid out in LBN order.
;
; Note that it's OK to reorder read and write operations in the pending
; queue, but not most other operations, such as PACKACKs. Therefore, scan
; of the pending queue stops if a non-read or -write operation is encountered.
; Also, reordering is not performed if the disk is currently in mount
; verification.
;
; Register usage within this routine:
;
;	R0	- Contains the function code of each successive IRP in
;		  order to check that the function is a read or write
;	R1	- Bitmap used to determine whether an IRP in the pending
;		  queue is "better" than the "best" IRP found so far
;	R2	- Address of pending I/O queue listehead in UCB
;	R3	- Address of the "best" IRP found at any point
;	R4	- Signed displacement from the head position to the LBN of the
;		  "best" IRP found so far
;	R5	- UCB address
;	R6	- Address of each successive IRP in pending queue
;		  being considered as a posible optimal IRP
;	R7	- Signed displacement from the heads to the LBN of each
;		  successive IRP in pending queue as the list is scanned
;	R8	- Limit to the number of IRPs in the pending queue that
;		  get scanned
;
; Context:
;
;	Fork thread
;	IPL = Fork
;	Fork lock held
;
; Inputs:
;
;	R2	- Address of I/O pending queue listhead in UCB
;	R5	- UCB address
;
; Outputs:
;
;	R0-R4	- Destroyed
;	All other registers preserved
;	Pending queue in UCB is sorted, with most appropriate IRP for current
;	head position at front of queue
;-

MAX_IRPS_SCANNED = 12
FAIRNESS_CNT = 4

ATTEMPT_REORDER::				;C FF; Attempt to reorder read/write requests for
	.JSB_ENTRY INPUT=<R2,R5>,SCRATCH=<R0,R1,R2,R3,R4>,PRESERVE=<R6,R7,R8>

; If mount verification is in progress, don't perform any reordering.

	BBS	#UCB$V_MNTVERIP,-		;C FF; Branch if mount verification is
		UCB$L_STS(R5),10$		;C FF; in progress, can't reorder

; Check that the active IRP is either a read or write function. If not,
; we can't reorder. The following code sequence increments the function code,
; and clears the low-order bit so that both IO$_READPBLK and IO$_WRITEPBLK
; get converted to IO$_READPBLK, reducing the number of compare operations
; needed to determine whether the function is a read or write.

	MOVL	(R2),R3				;C FF; Get first IRP in pending queue

	ASSUME	IRP$S_FCODE LE 7		;C FF; Allow byte compare
	ASSUME	IO$_WRITEPBLK EQ <^B1011>
	ASSUME	IO$_READPBLK EQ <^B1100>
	ADDB3	#1,IRP$L_FUNC(R3),R0		;C FF; Increment function code
	BICB	#<^C<IRP$M_FCODE>>+1,R0		;C FF; Extract function code and clear LSB
	CMPB	R0,#IO$_READPBLK		;C FF; Read or write function?
	BNEQ	10$				;C FF; Branch if not, can't reorder

	MOVL	R2,R6				;C FF; Copy pending queue listhead address
	MOVZBL	#MAX_IRPS_SCANNED,R8		;C FF; Set limit on number of IRPs scanned

	SUBL3	UCB$L_CUR_LBN(R5),-		;C FF; Assume LBN of active IRP is directly
		IRP$L_MEDIA(R3),R4		;C FF; under heads (ideal displacement)
	BNEQ	NEXT_IRP			;C FF; Branch if not, continue scan
	BRW	SCAN_DONE			;C FF; Otherwise, no need to continue scan
10$:	RSB					;C FF;

SCAN_LOOP:
	ASSUME	IRP$S_FCODE LE 7		;C FF; Allow byte compare
	ASSUME	IO$_WRITEPBLK EQ <^B1011>
	ASSUME	IO$_READPBLK EQ <^B1100>
	ADDB3	#1,IRP$L_FUNC(R6),R0		;C FF; Increment function code
	BICB	#<^C<IRP$M_FCODE>>+1,R0		;C FF; Extract function code and clear LSB
	CMPB	R0,#IO$_READPBLK		;C FF; Read or write function?
	BNEQ	SCAN_DONE			;C FF; Branch if not, stop scan

; Here we build a bitmap in R1 to indicate the following:
;	Bit 3	- Set if the heads are moving towards higher LBNs
;	Bit 2	- Set if the optimal I/O found so far is requesting a
;		  higher LBN than the current head position
;	Bit 1	- Set if the pending I/O currently being considered
;		  is requesting a higher LBN than the current head position
;	Bit 0	- Set if the pending I/O currently being considered is
;		  requesting a higher LBN than the optimal I/O found so far
;
; We can then use this bitmap to dispatch to the appropriate code to
; either continue to use the optimal IRP or to replace it with the
; IRP in the pending queue.

5$:	MOVZBL	UCB$B_SEEK_DIR(R5),R1		;C FF; Set/clear seek direction bit in R1
10$:	TSTL	R4				;C FF; Is optimal I/O LBN > head position?
	BLSS	20$				;C FF; Branch if not
	ADDL	#4,R1				;C FF; Indicate optimal I/O LBN > head position
20$:	SUBL3	UCB$L_CUR_LBN(R5),-		;C FF; Calculate displacement to pending I/O
		IRP$L_MEDIA(R6),R7		;C FF;
	BEQL	PERFECT_IRP			;C FF; Found the perfect I/O, get out now
	BLSS	30$				;C FF; Branch if pending I/O LBN < head position
	ADDL	#2,R1				;C FF; Indicate pending I/O LBN > head position
30$:	CMPL	IRP$L_MEDIA(R6),-		;C FF; Compare LBNs of optimal and pending
		IRP$L_MEDIA(R3)			;C FF; I/Os
	BLEQ	40$				;C FF; Branch if pending <= optimal
	INCL	R1				;C FF; Indicate pending > optimal

40$:	DISPATCH R1,TYPE=B,<-			;C FF; Dispatch based on the state in R1
		<0,	NEXT_IRP>,-		;C FF; to either keep the current optimal
		<1,	SWAP_IRP>,-		;C FF; IRP or replace it with one from the
		<3,	NEXT_IRP>,-		;C FF; pending queue
		<4,	SWAP_IRP>,-
		<6,	SWAP_IRP>,-
		<7,	NEXT_IRP>,-
		<8,	NEXT_IRP>,-
		<9,	SWAP_IRP>,-
		<11,	SWAP_IRP>,-
		<12,	NEXT_IRP>,-
		<14,	SWAP_IRP>,-
		<15,	NEXT_IRP>>

	BUG_CHECK INCONSTATE,FATAL		;C FF; This should never happen

; The IRP in the pending queue was found to be closer to the heads than
; the optimal one. Therefore replace the optimal IRP with the one from
; the pending queue.

SWAP_IRP:

	MOVQ	R6,R3				;C FF; Replace the optimal IRP with the
						;C FF; one from the pending queue
	REMQUE	(R3),R3				;C FF; Remove IRP from current position in
	INSQUE	(R3),UCB$L_IOQFL(R5)		;C FF; I/O queue and place at front of queue
	SUBL3	UCB$L_CUR_LBN(R5),-		;C FF; Update distance from current to optimal
		IRP$L_MEDIA(R3),R4		;C FF;

NEXT_IRP:

	MOVL	(R6),R6				;C FF; Get address of next IRP in list
	CMPL	R6,R2				;C FF; End of list?
	BEQL	SCAN_DONE			;C FF; Branch if so
	SOBGTR	R8,SCAN_LOOP			;C FF; Branch if more IRPs are allowed to be scanned

SCAN_DONE:

; R4 contains the signed displacement between the current head position and
; the LBN of the IRP that we've chosen to execute. Change the SEEK_DIR
; based on the which direction the heads will now be moving. If the LBN of
; the current IRP is directly under the heads, continue to scan in the same
; direction.

	INCL	UCB$L_FAIRNESS_CNT(R5)		;C FF; Assume perfect IRP
	TSTL	R4				;C FF; Time to change seek direction?
	BEQL	SCAN_RETURN			;C FF; Branch if not
	BGTR	10$				;C FF;
	CLRB	UCB$B_SEEK_DIR(R5)		;C FF; Start motion head towards lower LBNs
	BRB	SCAN_RETURN			;C FF; Use common exit
10$:	MOVB	#8,UCB$B_SEEK_DIR(R5)		;C FF; Start motion towards higher LBNs
20$:	CLRL	UCB$L_FAIRNESS_CNT(R5)		;C FF; Indicate this is a new LBN

SCAN_RETURN:
	RSB					;C FF; Return to caller

; While scanning the pending queue, an IRP was found which is requesting
; the LBN at the current head position. No need to scan any longer. Choose
; this IRP as the optimal one and get out.

PERFECT_IRP:

	CMPL	#FAIRNESS_CNT,-			;C FF; Is this LBN monopolizing service?
		UCB$L_FAIRNESS_CNT(R5)
	BLSS	NEXT_IRP			;C FF; If so, don't choose this IRP after all
	REMQUE	(R6),R3				;C FF; Remove IRP from current position in
	INSQUE	(R3),UCB$L_IOQFL(R5)		;C FF; I/O queue and place at front of queue
	SUBL3	UCB$L_CUR_LBN(R5),-		;C FF; Update distance from current to optimal
		IRP$L_MEDIA(R3),R4		;C FF;
	BRB	SCAN_DONE

	.PAGE
	.SBTTL	CHECK_HBS - Determine if device supports HBS
;+
; CHECK_HBS
;
; This routine is called during the first PACKACK function to each unit to
; determine if the device is capable of supporting host-based shadowing. The
; criteria is that the device must allow the forcing of bad blocks using
; the read long / write long SCSI commands. This routine performs a series
; of read longs with varying length fields trying to find one that succeeds.
; The problem here is that these commands are both optional and vendor-unique.
; If a read long command succeeds, the length used for the command is saved
; in the UCB so it can be used for future read long / write long commands.
; If no read long commands succeed, then the device is marked as not supporting
; host-based shadowing.
;
; Context:
;
;	SCDRP Thread
;	IPL = Fork
;	Fork lock held
;
; Inputs:
;
;	R3	- UCB address
;	R4	- SPDT address
;	R5	- SCDRP address
;
; Outputs:
;
;	R0	- SS$_NORMAL
;	R1-R2	- Destroyed
;	All other registers preserved
;
;	UCB$L_		- UCB$V_ set if device supports host-based
;			  shadowing
;	UCB$W_READL_LEN	- Data length to use with future read long / write long
;			  commands
;-

READL_RETRY_CNT = 8

CHECK_HBS::					;S FF; Determine if device supports HBS
	.JSB_ENTRY INPUT=<R3,R4,R5>,OUTPUT=<R0>,SCRATCH=<R1,R2,R6>,PRESERVE=<R7,R8>

;
; Assume failure.
;
	BISL	#DEV$M_NOFE,-			;S FF; Drive does not support host based
		UCB$L_DEVCHAR2(R3)		;S FF;	 shadowing. (No Forced Error)
	.BRANCH_LIKELY
	BBC	#DEV$V_SWL,-			;S FF; Branch if device is software
		UCB$L_DEVCHAR(R3),5$		;S FF; write locked. Can't support HBS.
	MOVL	#SS$_NORMAL,R0			;S FF; Set success status
	BRW	29$				;S FF; And exit

5$:	MOVL	#READL_RETRY_CNT,R7            	;S FF; Initialize counter for retries 
10$:	CLRW	UCB$W_READL_LEN(R3)		;S FF; First read long length to try
20$:	MOVAL	CMD_READ_LONG,R2		;S FF; Address of read long command
	SETUP_CMD				;S FF; Perform setup for SCSI command

	ADDL3	#4,SCDRP$L_CMD_PTR(R5),R6	;S FF; Get address of SCSI CDB (Command).
						;S FF; The CDB doesn't start at 0, but 4.

	.Disable Flagging			; CMD_READ_LONG$L_LBA is not aligned
	CLRL	CMD_READ_LONG$L_LBA(R6)		;S FF; Attempt a read long on block 0
	.Enable Flagging

; Since the read long command is vendor-unique, the transfer length field
; is not not known apriori. Instead, we must send the device a number of
; read long commands with varying length fields until the correct value is
; determined. The current values we try are: 0, 1, 512 -> 1024. In addition
; to checking for good SCSI status returned from the command, we also check
; for a transfer length returned of >= 512 bytes, since read long should return
; at least a block of data (and hopefully some ECC bytes as well).

	MOVB	UCB$W_READL_LEN+1(R3),-		;S FF; Use length determined by CHECK_HBS
		CMD_READ_LONG$W_XFER_LENGTH(R6)	;S FF; to fill in MSB of xfer length
	MOVB	UCB$W_READL_LEN(R3),-		;S FF; and low-order byte
		CMD_READ_LONG$W_XFER_LENGTH+1(R6);S FF;

; Use the SPI$SEND_COMMAND macro directly rather than the SEND_COMMAND routine
; as we don't want all the error checking performed by that routine.

	.IF DEFINED TRACE_SCSI
	BSBW	TRACE_SCSI_CMD			;S FF Save SCSI command in trace buf
	.ENDC

	IF_CANCEL	27$			;S FF; If server requested
						;S FF; cancel, then return
	SPI$SEND_COMMAND TYPE=ORDERED		;S FF; Send the command

	.IF DEFINED TRACE_SCSI
	BSBW	TRACE_SCSI_STS			;S FF; Save SCSI status in trace buf
	.ENDC

	ASSUME	SCSI$C_GOOD EQ 0
	BICL3	#SCSI$M_STATUS_BYTE_RESERVED,-	;S DS; Mask off reserved status bits
		@SCDRP$L_STS_PTR(R5), R8	;S DS;
	BNEQ	30$				;S FF; Branch if bad SCSI status
	BLBC	R0,30$				;S FF; Branch on error
	CMPL	SCDRP$L_TRANS_CNT(R5),-		;S FF; Reasonable transfer length returned?
		#IOC$C_DISK_BLKSIZ		;S FF;
	BLSSU	30$				;S FF; Branch if not

; We've successfully sent a read long command and received a reasonable
; amount of data in return. Clear the bit in the UCB indicating this device
; supports host-based shadowing.

	BICL	#DEV$M_NOFE,-			;S FF; Drive supports host based
		UCB$L_DEVCHAR2(R3)		;S FF;	 shadowing.
25$:	MOVZWL	#SS$_NORMAL,R0			;S FF; Set unconditional success
27$:	CLEANUP_CMD				;S FF; Cleanup from the SCSI command
29$:	RSB					;S FF; Return to caller

; We've been unsuccessful in sending a read long command to the drive. 
; Unless this is the result of a unit attention, try the next data length 
; value in the sequence (or, if we've reached the end of the sequence, 
; return without setting the "host-based shadowing supported" bit in the UCB).
; If a unit attention occurred, the failure may have been caused by a bus reset,
; so retry the whole operation beginning with length 0.  We start all the
; way back from 0 since, by the time we see the unit attention on the 
; current length, we may already be past the correct length.

	ASSUME	READ_LONG_DATA_LEN LT 32768

30$:	
	CMPB	R8,#SCSI$C_CHECK_CONDITION	;S FF; Was this a Check Condition?
    	BNEQ	35$				;S FF; No, just get next data value

; If the Check Condition indicates a sense key of UNIT ATTENTION, then there
; may have been a bus reset, so retry this same data length.  The sense
; information must be read before CLEANUP_CMD.

	BBC	#SCDRP$V_FLAG_ASENSE_VALID, -	;S FF; Is the autosense data valid ?
		SCDRP$L_SCSI_FLAGS(R5), 32$	;S FF; No, better start over
	MOVL	SCDRP$PS_SENSE_BUFFER(R5),R0	;S FF; Copy Auto Sense buffer address 
	EXTZV	#SCSI$SNS$V_SENSE_KEY,-		;S FF; Get sense key
		#SCSI$SNS$S_SENSE_KEY,-		;S FF;
		SCSI$SNS$R_FLAGS(R0),R1		;S FF;
	CMPB	R1,#SCSI$C_UNIT_ATTENTION	;S FF; Unit attention sense key?
	BNEQ	35$				;S FF; No, so get next length
32$:
	SPI$QUEUE_RELEASE			;S FF; Free the queue from the CHK COND
	CLEANUP_CMD				;S FF; Cleanup from the read long command
	SOBGTR	R7,10$				;S FF; Else retry from 0
	MOVL	#SS$_NORMAL,R0			;S FF; Set status
        BRW	29$				;S FF; Exit when retries exhausted

35$:	
	SPI$QUEUE_RELEASE			;S FF; Free the queue from the CHK COND
	CLEANUP_CMD				;S FF; Cleanup from the read long command
	MOVZWL	UCB$W_READL_LEN(R3),R0		;S FF; get the count that we just used
	CMPL	#1,R0				;S FF; did we just do 1
	BNEQ	40$				;S FF; neq means no- just inc the value

	MOVL	#<512-1>,R0			;S FF; Skip 2->511 the seq should be
						;S FF; 0,1,512,513...READ_LONG_DATA_LEN

40$:	INCL	R0				;S FF;S FF; update count
	MOVW	R0,UCB$W_READL_LEN(R3)		;S FF; put it back in ucb
	CMPL	R0,#READ_LONG_DATA_LEN		;S FF; did we get to the max value?
	.BRANCH_LIKELY
	BLEQU	20$                             ;S FF; Try sending another read long command
	MOVL	#SS$_NORMAL,R0			;S FF; Set status
	BRW	29$				;S FF; And exit

	.PAGE
	.SBTTL	FORCE_ERROR	- Force an ECC error on a block
;+
; FORCE_ERROR - Force ECC error
;
; This routine is used to generate unrecoverable ECC errors on a set of
; logical blocks. It is used by shadowing code when the master copy of a
; particular block has gone bad and the "forced error" bit for this block
; must be set for all members of the shadow set. Since SCSI doesn't support
; the forced error bit, the alternative is simply to generate an unrecoverable
; ECC error, so all subsequent reads to this block generate an error.
;
; The ECC error is generated by performing a read long, inverting every byte
; in the block, and performing a write long.
;
; This routine performs it action by repeatedly calling FORCE_ONE_ERROR for
; each of the blocks associated with the QIO.
;
; Context:
;
;	SCDRP Thread
;	IPL = Fork
;	Fork lock held
;
; Inputs:
;
;	R2	- IRP address
;	R3	- UCB address
;	R4	- SPDT address
;	R5	- SCDRP address
;
;	UCB$W_READL_LEN	- Length to use for the read long / write long commands
;
; Outputs:
;
;	R0	- Status
;	R1-R2	- Destroyed
;	All other registers preserved
;
;-

FORCE_ERROR:					;S FF; Force ECC error

	CLRL	SCDRP$L_ABCNT(R5)		;S FF; Initialize accumulated byte count
	MOVL	IRP$L_MEDIA(R2),-		;S FF; Copy disk logical block number from
		SCDRP$L_MEDIA(R5)		;S FF; the IRP to the SCDRP
	MOVL	#IOC$C_DISK_BLKSIZ,-		;S FF; Force errors on one block at a time
		SCDRP$L_BCNT(R5)		;S FF;

	.print	; What is this MD_ERROR at IRP$L_MEDIA+6?

	BBC	#MSCP$V_MD_ERROR,-		;S FF; Branch if the MD_ERROR bit not set.
		IRP$L_MEDIA+6(R2),50$		;S FF; This bit must be set to force an error
	BBC	#UCB$V_HBS_CHECK,-		;S FF; Branch if check for host-based
		UCB$L_DK_FLAGS(R3),50$		;S FF; shadowing has not yet been made
	BBS	#DEV$V_SWL,-			;S FF; Branch if device is software
		UCB$L_DEVCHAR(R3),60$		;S FF; write locked. Can't support HBS.

	SET_SINGLE	FORCE_ERROR,R3 		;S FF; Single thread force error request

10$:	BSBB	FORCE_ONE_ERROR			;S FF; Force an error on the next block
	BLBC	R0,20$				;S FF; Branch on error
	INCL	SCDRP$L_MEDIA(R5)		;S FF; Advance to next block
	ADDL	#IOC$C_DISK_BLKSIZ,-		;S FF; Update accumulated byte count
		SCDRP$L_ABCNT(R5)		;S FF;
	MOVL	SCDRP$L_IRP(R5),R2		;S FF; Restore IRP address
	CMPL	SCDRP$L_ABCNT(R5),-		;S FF; Done with entire transfer?
		IRP$L_BCNT(R2)			;S FF;
	BLSSU	10$				;S FF; Branch if not

20$:	CLR_SINGLE	FORCE_ERROR,R3		;S FF; Force error request is complete

30$:	MOVL	SCDRP$L_ABCNT(R5),R1		;S FF; Get accumulated transfer count
	INSV	R1,#16,#16,R0			;S FF; Load low-order transfer count
	BRW	COMPLETE_IO			;S FF; Complete the QIO

; The check to determine if the device supports host-based shadowing has
; not yet been made. Therefore, we can attempt to for an error at this time.

50$:	MOVZWL	#SS$_UNSUPPORTED,R0		;S FF; Set unsupported status
	BRB	30$				;S FF; Use common exit

; The device is write-locked and therefore can not accept the commands
; necessary to cause an uncorrectable ECC error.

60$:	MOVL	#SS$_WRITLCK,R0			;S FF; Set write-locked status
	BRB	30$				;S FF; Use common exit

	.PAGE
	.SBTTL	FORCE_ONE_ERROR	- Force an unrecoverable error on a block
;+
; FORCE_ONE_ERROR
;
; This routine is used to generate an unrecoverable ECC error on the specified
; logical block and is called by the routine FORCE_ERROR. The ECC error is
; generated by performing a read long, inverting every byte in the block, and
; performing a write long. Prior to performing the read long / write long
; sequence, the block is read to determine if it already contains a
; non-recoverable error. If so, no action is taken.
;
; Context:
;
;	SCDRP Thread
;	IPL = Fork
;	Fork lock held
;
; Inputs:
;
;	R2	- IRP address
;	R3	- UCB address
;	R4	- SPDT address
;	R5	- SCDRP address
;
;	UCB$W_READL_LEN	- Length to use for the read long / write long commands
;
; Outputs:
;
;	R0	- Status
;	R1-R2	- Destroyed
;	All other registers preserved
;
;-

FORCE_ONE_ERROR::				;S FF; Force an unrecoverable error on a block
	.JSB_ENTRY INPUT=<R3,R4,R5>,OUTPUT=<R0>,SCRATCH=<R1,R2>


; First, perform a normal read command on the block to determine if the block
; already contains an uncorrectable error. If so, don't bother forcing another.


	BISL	#IRP$M_FUNC,-			;S FF; Set the FUNC but to indicate this
		SCDRP$IS_STS(R5)		;S FF; is a read function
	MOVL	#IOC$C_DISK_BLKSIZ,R1		;S FF; Allocate an S0 buffer in which to read
	PUSHL	R5				;S FF; Save SCDRP address
	MOVL	SCDRP$PS_KPB(R5),R5		;S FF; Get KPB address
	BSBW	DK_ALLOC_POOL			;S FF; Allocate pool
	POPL	R5				;S FF; REstore SCDRP address
	BISB	#SCDRP$M_FLAG_S0BUF,-		;S FF; Indicate that this is an S0 "user"
		SCDRP$L_SCSI_FLAGS(R5)		;S FF; buffer
	MOVL	R2,SCDRP$L_SVA_USER(R5)		;S FF; Save address of S0 "user" buffer
	PUSHL	R3				;S FF; Save R3
	JSB	G^MMG$SVAPTECHK			;S FF; Get SVAPTE of allocated system buffer
	MOVL	R3,SCDRP$L_SVAPTE(R5)		;S FF; Save SVAPTE in SCDRP
	MCOML	G^ MMG$GL_BWP_MASK, R3		;S FF; Get mask for BOFF.
	BICL3	R3, R2, SCDRP$L_BOFF(R5) 	;S FF; Set offset from beginning of page.
	POPL	R3				;S FF; Restore R3
	DISABLE_ERRLOG				;S FF; Temporarily disable errorlogging
	MOVL	#SCDRP$K_QCHAR_UNORDERED,-      ;S FF; Send a Simple tagged request
                SCDRP$IS_QUEUE_CHAR(R5)	
	BSBW	READ_WRITE			;S FF; Send out a read command
	REENABLE_ERRLOG				;S FF; Reenable errorlogging
	PUSHL	R0				;S FF; Save status
	MOVL	SCDRP$L_SVA_USER(R5),R0		;S FF; Get address of S0 buffer
	BSBW	DEALLOC_POOL			;S FF; Deallocate the S0 buffer
	CLEANUP_CMD				;S FF; Clean up SCSI command
	POPL	R0				;S FF; Restore R0
	CMPL	R0,#SS$_PARITY			;S FF; Already a hard error on the block?
	BEQL	40$				;S FF; Branch if so
	CMPL	R0,#SS$_CANCEL			;S FF; If cancel status
	BEQL	30$

; Issue a read long command to get the current contents of the logical
; block.

	MOVAL	CMD_READ_LONG,R2		;S FF; Address of read long command
	SETUP_CMD				;S FF; Perform setup for SCSI command
	ADDL3	#4,SCDRP$L_CMD_PTR(R5),R0	;S FF; Get address of SCSI CDB (Command).
						;S FF; The CDB doesn't start at 0, but 4.
	MOVB	UCB$W_READL_LEN+1(R3),-		;S FF; Use length determined by CHECK_HBS
		CMD_READ_LONG$W_XFER_LENGTH(R0)	;S FF; to fill in MSB of xfer length
	MOVB	UCB$W_READL_LEN(R3),-		;S FF; and low-order byte
		CMD_READ_LONG$W_XFER_LENGTH+1(R0);S FF;
	ADDL	#<CMD_READ_LONG$L_LBA+4>,R0	;S FF; Address just beyond LBA
	MOVAL	SCDRP$L_MEDIA(R5),R1		;S FF; Get pointer LBN field
	.REPT	4
	MOVB	(R1)+,-(R0)			;S FF; Copy LBN number to SCSI command
	.ENDR
	SEND_COMMAND_ORDERED			;S FF; Send the command
	BLBC	R0,5$				;S FF; Branch on failure
	BICL	#SCDRP$M_FLAG_S0BUF, -		;S FF; Prevent CLEANUP_CMD from freeing data buffer,
		SCDRP$L_SCSI_FLAGS(R5)		;S FF; still frees mapping resources and command buffer
5$:	CLEANUP_CMD				;S FF; Clean up the SCSI command
	BLBC	R0,30$				;S FF; Branch on error

; Invert every byte in the original block to ensure that subsequent reads to
; the block will result in uncorrectable ECC errors.

	MOVL	SCDRP$L_SVA_USER(R5),R0		;S FF; Get address of read long data
	MOVZBL	#<IOC$C_DISK_BLKSIZ/4>,R1	;S FF; Length of disk block in longwords
10$:	XORL2	#-1,(R0)+			;S FF; Invert a longword
	SOBGTR	R1,10$				;S FF; Repeat for the entire disk block

; Save the original (read long) SCDRP and allocate another one in preparation
; for sending a write long command.

	MOVL	SCDRP$L_MEDIA(R5),R1		;S FF; Save LBN of block to reassign
	IF_CANCEL 30$				;S FF; Check for cancel request before
						;S FF; allocating private SCDRP
	DK_ALLOC_SCDRP				;S FF; Allocate a new SCDRP for the write long
	MOVAL	CMD_WRITE_LONG,R2		;Są FF; Address of write long command
	SETUP_CMD				;Są FF; Perform setup for SCSI command
	ADDL3	#4,SCDRP$L_CMD_PTR(R5),R0	;Są FF; Get address of SCSI CDB (Command).
						;Są FF; The CDB doesn't start at 0, but 4.
	MOVB	UCB$W_READL_LEN+1(R3),-		;Są FF; Use length determined by CHECK_HBS
		CMD_READ_LONG$W_XFER_LENGTH(R0)	;Są FF; to fill in MSB of xfer length
	MOVB	UCB$W_READL_LEN(R3),-		;Są FF; and low-order byte
		CMD_READ_LONG$W_XFER_LENGTH+1(R0);Są FF;
	ADDL	#<CMD_READ_LONG$L_LBA+4>,R0	;Są FF; Address just beyond LBA
	MOVAL	SCDRP$L_MEDIA(R5),R1		;S FF; Get pointer LBN field
	.REPT	4
	MOVB	(R1)+,-(R0)			;S FF; Copy LBN number to SCSI command
	.ENDR

; Copy the data, which is now inverted, from the read long buffer to the
; write long buffer.

	MOVL	SCDRP$PS_PREV_SCDRP(R5),R1	;Są FF; Restore original SCDRP address
	MOVL	SCDRP$L_SVA_USER(R1),R0		;Są FF; Get address of read long buffer
	MOVL	SCDRP$L_SVA_USER(R5),R1		;Są FF; Get address of write long buffer
	ASSUME READ_LONG_DATA_LEN EQ WRITE_LONG_DATA_LEN
	MOVZWL	#READ_LONG_DATA_LEN,R2		;Są FF; Number of bytes to copy
	PUSHR	#^M<R0,R3,R4,R5>		;Są FF; Save regs
	MOVC3	R2,(R0),(R1)			;Są FF; Copy read long buf to write long buf
	POPR	#^M<R0,R3,R4,R5>		;Są FF; Restore regs
	BSBW	DEALLOC_POOL			;Są FF; Deallocate read long buffer now

; Send the write long command, then perform the cleanup necessary for both
; commands.

	SEND_COMMAND_ORDERED			;Są FF; Send the command
	CLEANUP_CMD				;Są FF; Cleanup from the write long command
	DK_DEALLOC_SCDRP CLRSTK=YES		;Są FF; Deallocate the reassign SCDRP
30$:	RSB					;S FF; Return to caller

; The block already has a non-recoverable error associated with it. Don't
; bother forcing a second error, since if the current error is due to a
; previous force error function, issuing a second force error will have the
; effect of making the block good again. Instead, simply return success status.

40$:	MOVZWL	#SS$_NORMAL,R0			;S FF; Set success status
	BRB	30$				;S FF; Use common exit

	.PAGE
	.SBTTL	CHECK_QDEPTH - Check histogram data for qdepth change
;+
; CHECK_QDEPTH
;
; The startio routine in conjuntion with this routine, maintains and analyzies a
; histogram of I/O transfer requests sizes (UCB$L_XLEN_HIST). After every 'n'
; transfers (UCB$L_XLEN_HIST_CYCLE), the histogram is scanned to find the
; maximum value, which is the most popular transfer size used in the past 'n'
; I/O's. The table of queue depths is then indexed and a new queue depth
; appropriate for the current workload is selected.
;
; Context:
;
;	SCDRP thread
;	IPL = Fork
;	Fork lock held
;
; Inputs:
;
;	R3	- address of the UCB
;	R5	- address of an SCDRP
;
; Outputs:
;
;	R0,R1 - destroyed
;	all other registers preserved
;-
CHECK_QDEPTH:					;S FF;
	.JSB_ENTRY INPUT=<R3,R5>,PRESERVE=<R2,R6>
	MOVL	UCB$L_XLEN_HIST(R3),R6		;S FF; Address of histogram table
	CLRL	R1				;S FF; Max hist entry count
	MOVL	#XLEN_HIST_BUCKETS-1,R0		;S FF; Index into histogram table
	MOVL	R0,R2				;S FF; Save "max" index
10$:	CMPL	(R6)[R0],R1			;S FF; Check entry against max seen
	BLEQ	20$				;S FF; br if less than max
	MOVL	(R6)[R0],R1			;S FF; New max
	MOVL	R0,R2				;S FF; Store new index
20$:	CLRL	(R6)[R0]			;S FF; Restart histogram
	SOBGEQ	R0,10$				;S FF; Check all cells
	MOVAL	QDEPTH_TABLE,R6			;S FF; Address of qdepth vs xlen table
	MOVL	(R6)[R2],R0			;S FF; select new optimal depth
	CMPL	R0,UCB$L_QDEPTH(R3)		;S FF; If same depth value
	BEQL	30$				;S FF; then skip SET_CONN_CHAR
	MOVL	R0,UCB$L_QDEPTH(R3)		;S FF; Set new qdepth
	INCL	UCB$L_QDEPTH_TURNS(R3)		;S FF; Count adjustments
	BSBW	SET_CONN_CHAR			;S FF; Tell port
30$:	RSB					;S FF;

	.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_TRACE_BUF_SIZE	.BLKL	1	; Size of trace header, plus buffers
$DEF	TR$L_HEADER_SIZE	.BLKL	1	; Size of trace header
$DEF	TR$L_TOTAL_QIOS		.BLKL	1	; Number of QIO's to trace
$DEF	TR$L_CURRENT_QIO	.BLKL	1	; Current QIO number
$DEF	TR$L_QIO_SEQNUM		.BLKL	1	; Current QIO sequence number
$DEF	TR$L_QIO_SIZE		.BLKL	1	; Size of QIO header, plus 'n' SCSI commands
$DEF	TR$L_QIO_HEADER_SIZE	.BLKL	1	; Size of QIO header
$DEF	TR$L_TOTAL_SCSI_CMDS	.BLKL	1	; 'n' SCSI commands per QIO
$DEF	TR$L_SCSI_CMD_SIZE	.BLKL	1	; SCSI command bytes + status bytes
$DEF	TR$C_HEADER_SIZE

	$DEFEND	TRACE_BUFFER_HEADER

	$DEFINI	TRACE_BUFFER_QIO_HEADER

$DEF	TR$L_QIO_NUMBER		.BLKL	1	; TR$L_CURRENT_QIO number
$DEF	TR$L_SEQNUM		.BLKL	1	; TR$L_QIO_SEQNUM number
$DEF	TR$L_DEVICE_NAME	.BLKL	1	; Device name
$DEF	TR$L_SCSI_UNIT_NUMBER	.BLKL	1	; Unit number
$DEF	TR$L_VALID_FLAG		.BLKL	1	; Valid flag
$DEF	TR$L_SCSI_CMD_CNT	.BLKL	1	; Number of SCSI commands
$DEF	TR$L_QIO_FUNC		.BLKL	1	; $QIO function code
$DEF	TR$L_QIO_MEDIA		.BLKL	1	; $QIO media (LBN)
$DEF	TR$L_QIO_BCNT		.BLKL	1	; $QIO byte count
$DEF	TR$L_QIO_BOFF		.BLKL	1	; $QIO buffer offser
$DEF	TR$L_QIO_SVAPTE		.BLKL	3	; $QIO SVAPTE and entry
$DEF	TR$L_QIO_STATUS		.BLKL	1	; $QIO status
$DEF	TR$C_QIO_HEADER_SIZE

	$DEFEND	TRACE_BUFFER_QIO_HEADER

	$DEFINI	TRACE_BUFFER_SCSI_COMMAND

$DEF	TR$L_SCSI_CMD_LENGTH	.BLKB	4	; Length of SCSI command
$DEF	TR$L_SCSI_COMMAND	.BLKB	12	; SCSI command
$DEF	TR$L_SCSI_MESSAGE 	.BLKB	12	; SCSI message
$DEF	TR$L_SCSI_STATUS	.BLKB	4	; SCSI command status
$DEF	TR$C_SCSI_CMD_SIZE

	$DEFEND	TRACE_BUFFER_SCSI_COMMAND

TR$C_TOTAL_QIOS = 2000			; Total number of QIO's to trace
TR$C_TOTAL_SCSI_CMDS = 10		; Total number of SCSI commands/QIO to trace
TR$C_QIO_SIZE = <<TR$C_QIO_HEADER_SIZE + <TR$C_TOTAL_SCSI_CMDS * TR$C_SCSI_CMD_SIZE>> + 31> & ^C31
TR$C_TRACE_BUF_SIZE =	TR$C_HEADER_SIZE + <TR$C_TOTAL_QIOS * TR$C_QIO_SIZE>

	$DEFINI	TRACE_CLASS_BUSY_BITS

$DEF	TRB$L_TYPE		.BLKB	4	; Type of Operation
$DEF	TRB$L_PC		.BLKB	4	; Routine address
$DEF	TRB$L_CLASS_BUSY	.BLKB	4	; Class Busy Bits
$DEF	TRB$L_STS		.BLKB	4	; UCB$L_STS
$DEF	TRB$C_LENGTH				; Size of TRB entry

	$DEFEND	TRACE_CLASS_BUSY_BITS

	.PAGE
	.SBTTL	SETUP_TRACE - Allocate and set up trace buffer
;+
; SETUP_TRACE
;
; This routine is called 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 TRACE_BUFFER 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.
;
; Context:
;
;	Fork thread
;	IPL = Fork
;	Fork lock held
;
; Inputs:
;
;	R2	- KPB ADDRESS
;	R5	- UCB ADDRESS
;
; Outputs:
;
;	R0-R3	- Destroyed
;	All other registers preserved
;
;	CRB$L_AUXSTRUC	- Address of cell containing trace buffer address
;-
SETUP_TRACE::					;F FF; Allocate and set up trace buffer
	.JSB_ENTRY INPUT=<R2,R5>,SCRATCH=<R0,R1,R2,R3>,PRESERVE=<R5>
	
	.IF DEFINED TRACE
	TSTL	TRACE_BUFFER			;F FF; Trace buffer already set up?
	BNEQ	10$				;F FF; Branch if so
	INCL	TRACE_BUFFER			;F FF; Trace buffer setup in progress
	MOVL	#TR$C_TRACE_BUF_SIZE,R1		;F FF; Get size of trace buffer
	MOVL	R5, R3				;F FF; Save copy of UCB
	MOVL	R2, R5				;F FF; Get KPB address
	BSBW	DK_ALLOC_POOL			;F FF; Allocate the trace buffer
	MOVL	R3, R5				;F FF; Restore UCB address
	BLBC	R0,10$				;F FF; Branch if failure
	MOVL	R2,TRACE_BUFFER			;F FF; Save trace buffer address
	MOVL	#TR$C_TRACE_BUF_SIZE,-		;F FF; Save trace buffer size
		 TR$L_TRACE_BUF_SIZE(R2)	;F FF;
	MOVL	#TR$C_HEADER_SIZE,-		;F FF; Save header size
		 TR$L_HEADER_SIZE(R2)		;F FF;
	MOVL	#TR$C_TOTAL_QIOS,-		;F FF; Save total number of QIOs in
		 TR$L_TOTAL_QIOS(R2)		;F FF; buffer
;;;;;;;;CLRL	 TR$L_CURRENT_QIO(R2)		;F FF; Reset current QIO number
	MOVL	#TR$C_QIO_SIZE,-		;F FF; Save size of QIO
		 TR$L_QIO_SIZE(R2)		;F FF;
	MOVL	#TR$C_QIO_HEADER_SIZE,-		;F FF; Save size of QIO header
		 TR$L_QIO_HEADER_SIZE(R2); 	;F FF;
	MOVL	#TR$C_TOTAL_SCSI_CMDS,-		;F FF; Save number of SCSI commands
		 TR$L_TOTAL_SCSI_CMDS(R2) 	;F FF; per QIO
	MOVL	#TR$C_SCSI_CMD_SIZE,-		;F FF; Save SCSI command size
		 TR$L_SCSI_CMD_SIZE(R2)		;F FF;
10$:
	.ENDC

	MOVL	UCB$L_CRB(R5),R0		;F FF; Get CRB address
	MOVAL	TRACE_BUFFER,-			;F FF; Save address of cell pointing to
		CRB$L_AUXSTRUC(R0)		;F FF; trace buffer in CRB
	RSB					;F FF; Return to caller

	.PAGE
	.IF DEFINED TRACE
	.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.
;
; Context:
;
;	Fork thread
;	IPL = Fork
;	Fork lock held
;
; Inputs:
;
;	R5	- SCDRP address
;
; Outputs:
;
;	All registers preserved
;-

TRACE_QIO::					;CF FF; Trace QIO requests
	.JSB_ENTRY INPUT=<R5>,PRESERVE=<R0,R1,R2,R3,R4>

	MOVL	TRACE_BUFFER,R0			;CF FF; Get trace buffer address
	BGEQ	30$				;CF FF; Branch if none
	ADDL3	TR$L_CURRENT_QIO(R0),#1,R1	;CF FF; Bump QIO number
	CMPL	R1,#TR$C_TOTAL_QIOS		;CF FF; Time to wrap QIO number?
	BLSS	10$				;CF FF; Branch if not
	CLRL	R1				;CF FF; Wrap QIO number
10$:	MOVL	R1,TR$L_CURRENT_QIO(R0)		;CF FF; Save QIO number
	MULL3	#TR$C_QIO_SIZE,R1,R2		;CF FF; Get offset of this QIO in trace buffer
	ADDL	R0,R2				;CF FF; Address to place info for this QIO
	ADDL	#TR$C_HEADER_SIZE,R2		;CF FF; Account for trace buffer header
	PUSHR	#^M<R0,R2,R5>			;CF FF; Save regs
	MOVC5	#0,.,#0,#TR$C_QIO_SIZE,(R2)	;CF FF; Clear buffer used to trace this QIO
	POPR	#^M<R0,R2,R5>			;CF FF; Restore regs
	ADDL3	#TR$C_QIO_HEADER_SIZE,-		;CF FF; Get address of place to store
		R2,R1				;CF FF; first SCSI command
	MOVL	R1,SCDRP$PS_TR_SCSI_CMD(R5)	;CF FF; Save address to put SCSI command bytes
	MOVL	TR$L_CURRENT_QIO(R0),-		;CF FF; Save QIO number
		TR$L_QIO_NUMBER(R2)		;CF FF;
	MOVL	TR$L_QIO_SEQNUM(R0),-		;CF FF; Save QIO sequence number
		TR$L_SEQNUM(R2)			;CF FF;
	INCL	TR$L_QIO_SEQNUM(R0)		;CF FF; Bump QIO sequence number
	MOVL	SCDRP$L_UCB(R5),R1		;CF FF; Get UCB address
	MOVZWL	UCB$W_UNIT(R1),-		;CF FF; Trace SCSI bus ID
		TR$L_SCSI_UNIT_NUMBER(R2)
	MOVL	UCB$L_DDB(R1),R1		;CF FF; Get DDB address

	.Disable Flagging			; DDB$T_NAME_STR is not aligned
	MOVL	DDB$T_NAME_STR(R1),-		;CF FF; Save SCSI device name
		TR$L_DEVICE_NAME(R2)		;CF FF;
	.Enable Flagging

	MOVL	#1,TR$L_VALID_FLAG(R2)		;CF FF; Set valid flag
	CLRL	TR$L_SCSI_CMD_CNT(R2)		;CF FF; Clear SCSI command count
	MOVL	SCDRP$L_IRP(R5),R3		;CF FF; IRP address of 0?
	BEQL	15$				;CF FF; Branch if so
	MOVL	IRP$L_FUNC(R3),-		;CF FF; Trace QIO function code
		TR$L_QIO_FUNC(R2)		;CF FF;
	MOVL	IRP$L_MEDIA(R3),-		;CF FF; Trace media number (LBN)
		TR$L_QIO_MEDIA(R2)		;CF FF;
	MOVL	IRP$L_BCNT(R3),-		;CF FF; Trace byte count
		TR$L_QIO_BCNT(R2)		;CF FF;
	MOVL	IRP$L_BOFF(R3),-		;CF FF; Trace byte offset
		TR$L_QIO_BOFF(R2)		;CF FF;
	MOVL	IRP$L_SVAPTE(R3),R0		;CF FF; Get SVAPTE value
	BEQL	20$				;CF FF; If Zero, don't trace it
	MOVL	R0,TR$L_QIO_SVAPTE(R2)		;CF FF; Trace SVAPTE value

	ASSUME	PTE$C_BYTES_PER_PTE EQ 8         
	MOVL	(R0),TR$L_QIO_SVAPTE+4(R2) 	;CF FF; ... and its quadword
	MOVL	4(R0),TR$L_QIO_SVAPTE+8(R2) 	;CF FF; ... contents

	BRB	20$				;CF FF; Skip unit init path
15$:	MCOML	#0,TR$L_QIO_FUNC(R2)		;CF FF; Special function code for UNIT INIT
20$:	MOVAL	TR$L_QIO_STATUS(R2),-		;CF FF;
		SCDRP$PS_TR_QIO_STS(R5)		;CF FF; Save address to put QIO status
	MCOML	#0,TR$L_QIO_STATUS(R2)		;CF FF; Initialize QIO status
30$:	RSB					;CF FF; Return to caller

	.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.
;
; Context:
;
;	Fork thread
;	IPL = Fork
;	Fork lock held
;
; Inputs:
;	R5	- SCDRP address
;
; Outputs:
;	R2	- Destroyed
;
;-

TRACE_QIO_STAT::				;C FF; Save final status from QIO in trace buffer
	.JSB_ENTRY INPUT=<R5>,SCRATCH=<R2>

	MOVL	SCDRP$PS_TR_QIO_STS(R5),R2	;C FF; Get address to put status
	BEQL	10$				;C FF; Branch if uninitialized
	MOVL	R0,(R2)				;C FF; Save status in trace buffer
10$:	RSB					;C FF;

	.PAGE
	.SBTTL	TRACE_SCSI_CMD - Trace SCSI command bytes
;+
; TRACE_SCSI_CMD
;
; This routine saves the contents of the SCSI command in the trace buffer.
;
; Context:
;
;	SCDRP Thread
;	IPL = Fork
;	Fork lock held
;
;-

TRACE_SCSI_CMD::				;S FF; Trace SCSI command bytes

	.JSB_ENTRY INPUT=<R5>
	.IF DEFINED TRACE_SCSI
	PUSHR	#^M<R0,R1,R2,R3>		;S FF; Save regs
	MOVL	SCDRP$L_UCB(R5),R3		;S FF; Get UCB address
	MOVL	TRACE_BUFFER,R0			;S FF; Get trace buffer address
	BGEQ	20$				;S FF; Branch if none
	MOVL	TR$L_CURRENT_QIO(R0),R1		;S FF; Get current QIO number
	MULL3	#TR$C_QIO_SIZE,R1,R2		;S FF; Get offset of this QIO in trace buffer
	ADDL	R0,R2				;S FF; Address to place info for this QIO
	ADDL	#TR$C_HEADER_SIZE,R2		;S FF; Account for trace buffer header
	CMPL	TR$L_SCSI_CMD_CNT(R2),-		;S FF; Maximum SCSI commands exceeded?
		#TR$C_TOTAL_SCSI_CMDS		;S FF;
	BGEQ	20$				;S FF; Branch if so
	INCL	TR$L_SCSI_CMD_CNT(R2)		;S FF; One more SCSI command sent
	MOVL	SCDRP$L_CMD_PTR(R5),R0		;S FF; Get address of SCSI command
	MOVL	(R0)+,R1			;S FF; Get size of SCSI command
	MOVL	SCDRP$PS_TR_SCSI_CMD(R5),R2	;S FF; Address of SCSI command
	BEQL	20$				;S FF; Not setup yet
	MOVL	R1,TR$L_SCSI_CMD_LENGTH(R2)  	;S FF; Save length of command
	MOVAL	TR$L_SCSI_COMMAND(R2),R2	;S FF; Set pointer to command byte
10$:	MOVB	(R0)+,(R2)+			;S FF; Save a command byte
	SOBGTR	R1,10$				;S FF; Repeat for entire command
	MOVL	SCDRP$PS_TR_SCSI_CMD(R5),R2	;S FF; Restore SCSI command pointer
	MOVAL	TR$L_SCSI_STATUS(R2),-		;S FF; Save address to put SCSI status byte
		SCDRP$PS_TR_SCSI_STS(R5)	;S FF;
	MOVAL	TR$C_SCSI_CMD_SIZE(R2),-	;S FF; Save address to put next SCSI command
		SCDRP$PS_TR_SCSI_CMD(R5)	;S FF;
20$:	POPR	#^M<R0,R1,R2,R3>		;S FF; Restore regs
	.endc
	RSB					;S FF; Return to caller

	.PAGE
	.SBTTL	TRACE_SCSI_STS	- Trace SCSI status bytes
;+
; TRACE_SCSI_STS
;
; This routine saves the SCSI status byte in the trace buffer.
;-
; Context:
;
;	SCDRP Thread
;	IPL = Fork
;	Fork lock held
;

TRACE_SCSI_STS::				;S FF; Trace SCSI status bytes

	.JSB_ENTRY INPUT=<R0,R5>
	.IF DEFINED TRACE_SCSI
	PUSHL	R1				;S FF; Save register
	MOVL	SCDRP$PS_TR_SCSI_STS(R5),R1	;S FF; Get address to put status bytes
 	BEQL	10$				;S FF; Not initialized yet
 	MOVL	R0,(R1)				;S FF; Save status byte
10$:	POPL	R1				;S FF; Restore register
	.ENDC
	RSB					;S FF; Return to caller
	.ENDC

	.SBTTL	End of DKDRIVER
DK_END:	.END
