.TITLE MKDRIVER - VAX/VMS SCSI Tape Class Driver .IDENT 'X-39' ; .LIST MEB ;**************************************************************************** ;* * ;* COPYRIGHT (c) 1978, 1980, 1982, 1984, 1986, 1988, 1991 1992, 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. * ;* * ;* * ;**************************************************************************** ; ;+ ; ; FACILITY: ; ; VAX/VMS SCSI Tape Class Driver ; ; ABSTRACT: ; ; This module contains the class driver to control tape devices on ; a SCSI bus. ; ; ; AUTHOR: ; ; Jim Klumpp 13-Jan-1989 ; ; REVISION HISTORY: ; ; X-39 GCE Glenn C. Everhart 10-Oct-1996 ; Change the skip by filemarks algorithm to use the ; UCB$L_MK_FLAGS bits, ALLOWFAST_PER_IO and ALLOWFAST_ ; PER_IO, and the IO$_SKIPFILE modifier ALLOWFAST. ; Skip by filemarks will also set MT$_FASTSKIP_USED ; in the IOSB upon return. Create MK_SETMODE to key ; on the SETCHAR modifiers, IO$M_ALLOWFAST_NEVER, ALLOWFAST_PER_IO ; and ALLOWFAST_ALWAYS to set/clear UCB$L_MK_FLAGS bits. ; ; X-38 TGG0115 Tom Goodwin 4-Dec-1995 ; Incorporate mode sense common changes ported over from ; Alpha. Incorporate all bug fixes made to the Alpha ; MKDRIVER for OpenVMS Release V6.2 (X-12 version dated ; 10-May-95). ; ; This includes the following fixes ported back from Alpha: ; X-12 RCL Rick Lord 10-May-95 ; -Fold in the X-6U6 fix from Zeta: this fix adds bounds checking ; to MK_REG_DUMP just before label 35$, preventing the routine ; from overwriting the error log buffer entry it was handed and ; possibly corrupting the header of the error log buffer right ; after it. ; X-11 SCS Sue Sommer 19-Apr-1995 ; -Turn off skipfile enhancement by unconditionally clearing ; the readpos_supported bit in ucb$l_mk_flags during unit init. ; This enhancement is deemed too risky for the current release ; and is planned for reinstatement in some future release ; after broader testing is completed. ; -Modify the Mode Sense descriptors so that any Vendor Unique ; Required mode page values are now only Preferred. This is ; more compatible with the previous mode page handling and ; resolves unusual cases: eg. a Mode Select with vendor- ; unique values may return success but without actually ; setting the desired value, and that should count as success. ; -Also create a new UCB field ucb$l_sav_send_status to save ; the return status from each SEND_COMMAND call. Restore ; that status in MODE_SENSE, since it was possibly overwritten ; by Do_Mode_Page, and the original SEND_COMMAND status must ; be returned by MODE_SENSE in order for all tapes to function ; properly (eg. SS$_MEDOFL was overwritten by SS$_NODATA, ; causing TURs not to be retried when they should have been). ; ; X-37 SCS Sue Sommer 21-Nov-1995 ; Re-instate skip-by-file, implementing fixes: ; - Return correct space count in IOSB. ; - Set EOF bit when appropriate. ; - Modify TRANS_SENSE_KEY to return ENDOFVOLUME properly. ; - Remove references to ucb$l_boff/bcnt. ; - Exempt TSZ05/7 from skipfile due to lack of BLANK CHECK. ; - Check the LOST bit in SPACE_FILEMARKS before exiting. ; - For skip count of 1 or 2, skip by record instead of by file; ; the overhead isn't worth it. ; - Rename for the sake of clarity: ; CHECK_READPOS_SUPPORT to CHECK_SKIPFILE_SUPPORT; ; UCB$V_READPOS_SUPPORTED to UCB$V_SKIPFILE_SUPPORTED; ; UCB$V_SKIPREC_INPROG to UCB$V_SKIP_INPROG. ; ; X-36 TGG0117 Tom Goodwin 10-Oct-1995 ; Disable the skipfiles functionality by clearing the ; READPOS_SUPPORTED bit of the UCB at unit init time. ; ; X-35 RCL Rick Lord 10-May-95 ; Add last-minute check of the UCB$V_CANCEL bit at the end of ; IO_DIAGNOSE to prevent sending an already-deallocated IRP ; off to REQCOM [QAR V6 #927] ; ; X-34 TGG0101 Tom Goodwin 9-Jan-1995 ; Modify skipfile operation to use Space Filemarks instead ; of Skip Records wherever possible; major performance ; enhancement. Specific modifications: ; - Add READ_POSITION command to SCSI command table ; - Add bits to ucb$l_mk_flags ; - In PACKACK_SEQ, BSBW to a new routine CHECK_READPOS_SUPPORT. ; - In IO_SKIP_FILE, BSBW to a new routine SKIP_FILE. ; - In IO_SKIP_FILE, add new code to support SPACE FILEMARKS. ; - Exempt READ POSITION support checks from error logging ; in LOG_EXTND_SENSE. ; ; X-33 MCY Mary Yuryan 13-Jun-1994 ; Modification of X-23. Branch around the INSV instruction ; in GET_DEVICE_TYPE after checking for compaction bit ; enabled, rather than for a specific device type. ; ; X-32 KFM Kevin F. Martin 07-Feb-1994 ; Add DDR support. ; ; X-31 KFM Kevin F. Martin 07-Feb-1994 ; Remove the Receive Diagnostic command from UNIT_INIT path ; for the TZK50 only, due to fatal errors on the SCSI bus ; during DATA IN phase. The code always brought the device ; online regardless of the revision level check outcome but ; did flag the user that the TZK50 did not meet a minimum ; revision level via errorlog output. This errorlog output ; is also disabled for the TZK50. ; ; X-30 KFM Kevin F. Martin 07-Sep-1993 ; Remove the Receive Diagnostic command from the PACKACK ; sequence for TZK50 only. This was causing fatal errors ; on the SCSI bus when executed as part of the PACKACK ; sequence. The hardware/firmware revision level for the TZK50 ; is obtained via the UNIT_INIT sequence. ; ; X-29 MCY Mary Yuryan 11-Aug-1993 ; Add latent support for TZ865. ; ; X-28 MCY Mary Yuryan 05-May-1993 ; Three Bugfixes. ; - change error logged for BLANK CHECK in the SENSE_KEY_TABLE ; from DRVERR to OPINCOMPL. ; - Add check for 0 trans_cnt (scdrp$l_trans_cnt) in ; SETUP_COMPACTION. ; - In SETUP_CMD: increase DMA_OUT timeout to 10 secs for ; TZ30 end of tape problem. (Bill Peters - 2/17/93) ; ; X-27 MCY Mary Yuryan 14-Apr-1993 ; Add the following SCSI devices - TZ88, TZ875, TZ885, TZ887, ; TZ89, TZ895, TZ897, TL810, TL820, TLZ07, TLZ7. ; ; X-26 MCY Mary Yuryan 13-Apr-1993 ; Add RETENSION support for the TZK11. Add the IF_TKZ11 and ; IFNOT_TZK11 macros, and make the device check in IO_RETENSION. ; ; X-25 MCY Mary Yuryan / Rick Lord 17-Sep-1992 ; - Enhance loader support, delete old CHECK_FOR_LOADER macro, ; Add loader byte to SCSI_DEV_TYPES1, check for loader in ; GET_DEVICE_TYPE, add loader byte to device string in ; SCSI_DEV_TYPES. RCL ; - In LOG_EXTND_SENSE, add check for BOT and BLANK CHECK error ; & supress logging the error - for new tapes. MCY ; - Add check for cancelled IO in IO_SENSECHAR: to avoid reading ; cancelled IO/deallocated IRP. RCL ; - Complete TKZ09 support - add check for TKZ09 after check for ; 8mm tape in GET_DEVICE_TYPE. ; - Merge in John Meneghini's fixes below. MCY ; ; JAM0008 John Meneghini 23-JUL-1992 ; Lengthen Disconnect & DMA/Phase change timeouts with ; ERASE, SPACE and RECIEVE DIAGNOSTICS commands. ; - Increased DISCONNECT timeouts to 5 hours w/ SPACE & ERASE ; - Increased DMA timeouts to 1 min. w/ RECIEVE_DIAGNOSTICS ; ; X-24 JSSBLADE John S. Simakauskas 24-June-1992 ; Add TKZ09 - 5GB, 8mm, SCSI Tape (TKZ08 Follow-on) ; ; X-23 JAM001 John Meneghini 06-MAY-1992 ; Branch around INSV instruction in GET_DEVICE_TYPE routine ; setting MT$S_DENSITY field in UCB$_DEVDEPEND. Was ; improperly resetting Compaction (to zero) w/TLZ06. ; ; X-22 MCY Mary Yuryan 25-Feb-1992 ; Merge loader support from Amber. Create the CHECK_FOR_LOADER ; macro which will set the loader bit if TZ857 or TLZ6 is ; present, called from the INQUIRY routine. ; Sync the VSC idents... ; ; X-20 MCY Mary Yuryan 20-Dec-1991 ; Add latent support for SCSI loaders - TKZ60, TZ857, ; TZ867,TZ877, and TLZ6 (TLZ06 loader.) ; ; X-14A1A1A1 FAK002 Forrest A. Kenney 26-Nov-1991 ; Merge in latest Blade changes. ; ; X-19 WJG0049 W. John Guineau 15-Nov-1991 ; Add DDR support. ; ; X-18 DWF0022 David W. Freund 12-Nov-1991 ; Fold in X-16A1 by W. John Guineau: ; - Fix bug introduced in X-16 for rewind timout check ; WAIT_UNIT_READY - add a BRB around UNLOAD timeout ; code path. ; ; x-17 WJG W. John Guineau 8-Oct-1991 ; - Fix EXABYTE (8MM) DCL COPY command problems. ; - Fix resource contention problems due to dma buffer ; deadlock conditions when a SCSI error occurs and all ; buffer space is allocated. Now we allocate enough ; mapping resources at UNIT_INIT in ALLOC_REQSNS_RESOURCE ; for the REQUEST SENSE to get through (which was prevoiusly ; hanging waiting for resources which were locked until the ; REQUEST SENSE completed, which coudn't because it couldn't ; get resources to execute!) ; ; X-14A1A1FAK001 Forrest A. Kenney 07-Nov-1991 ; Merge in MAGIC and BLADE changes since T2 snapshot. ; ; X-16 WJG W. John Guineau 7-Oct-1991 ; - Change unload timeout logic from X-15 to use separate ; bit (UCB$M_UNLOAD_INPROG not UCB$M_REWIND_INPROG) ; and time field (UCB$L_UNLOAD_TIME) so we don't hang on ; IO$_AVAILABLE after a DISMOUNT. This is necessary ; since we must not timeout after an UNLOAD with media ; stackers for at least 3 minutes to give the stacker ; time to get the next tape loaded. ; ; X-15 WJG W. John Guineau 1-Oct-1991 ; - Add REWIND timeout logic to IO_UNLOAD code path to ; prevent loaders from timwing out while traversing a ; media stack. ; - Make ident match VSC ident ; ; X-14A1 FAK001 Forrest A. Kenne 23-Sep-1991 ; Merge C2 changes into Blade. Make module ident and revision ; history agree with CMS after the master pack cleanup. ; ; X-25 WJG W. John Guineau 14-Aug-1991 ; Return the compaction "density" values in the sense mode ; data packet for BACKUP. ; ; x-24 WJG W. John Guineau 22-May-1991 ; Add UCB$V_COMPCHK_IN_PROG bit to flag when compaction ; check is in progress to prevent bad scsi status on ; devices which don't support compaction from setting tape ; lost ; ; x-23 WJG W. John Guineau 15-May-1991 ; Fix DMA Length field in MODE_SENSE_COMP SCSI_CMD ; descriptor to be the exact value of data in expected ; to work around a data pointer manipulation bug in ; PKIDRIVER ; ; X-22 WJG W. John Guineau 17-April-1991 ; More compaction fixes - this time with real hardware to test ; on! Moved check for compaction support into separate ; routine CHECK_COMPACTION_SUPPORT, utilized MT2$V_COMP_ENA ; bit in DEVDEPND2 instead of density field. Also, now ; returns SS$_NORMAL if you set a density a device doesn't ; support (previous behaviour) - CHECK_COMPACTION_SUPPORT is ; called from UNIT_INIT (via SET_UNIT_ONLINE) and from PACKACK. ; ; X-21 WJG W. John Guineau 3-April-1991 ; Allow SCSI-1 compliant devices to be used. The check added ; in X-18U3 only allowed SCSI-2 via UCB$B_SCSI_VERSION ; ; X-20 WJG W. John Guineau 7-Mar-1991 ; Merge Magic and Sigma code streams ; ; X-19 Was Magic code stream ident for X-16U6 below: ; X-16U6 MCY Mary Yuryan 19-Feb-1991 ; Remove default mode_select parameters from the SCSI_DEV_ ; TABLES for the TSZ05,(TZX0) & TSZ07. The RDEW bit caused ; mutiple volume backup problems when set, causing the backup ; operation to fail. ; ; X-18U3 WJG W. John Guineau 4-March-1991 ; Make data compaction support more generic for SCSI ; in VMS. Enable automatic recognition of devices which ; support compaction. Add DISABLE/REENABLE_ERRLOG macros ; from DKDRIVER. Add UCB$B_SCSI_VERSION field in UCB for ; recognition of SCSI2 compliant devices. ; ; X-18U2 WJG W. John Guineau 28-Feb-1991 ; Add TZK11 and TLZ06 data compaction support. ; ; X-18U1 MCY Mary Yuryan, Howard Palmer 20-Feb-1991 ; Change default mode_select paramters for the TSZ05, ; TSZ07 to fix the multi-volume backup failures with ; the RDEW bit set. ; ; ; X-16U5 MCY Mary Yuryan 20-Dec-1990 ; Move the TZK10 RETENSION function from SKIPFILES to ; the UNLOAD and REWIND functions. Add TLZ06 tape symbol, ; next generation RDAT. ; ; X-16U4 JTK Jim Klumpp 18-Dec-1990 ; Change the algorithm for determining whether a device ; is not ready due to a previous rewind/immediate command. ; Before, the driver would always assume a rewind was in ; progress and wait at least the maximum rewind time before ; timing out a wait unit ready polling loop. Now, whenever ; a rewind/immediate is sent to the drive, the time by which ; this command should complete is recorded in the UCB. This ; information can then be used to time out wait unit ready ; polling activity. ; ; X-16U3 JTK Jim Klumpp 11-Dec-1990 ; Add a workaround for the TZK50 phase error bug. Delay for ; one second after sending a SCSI unload command to the TZK50 ; to prevent subsequent test unit ready commands from being ; told the device is ready, when in fact it's really in the ; process of being rewound and unloaded. ; ; X-16U2 MCY Mary Yuryan, Barbara Leahy 27-Nov-1990 ; Add the RETENSION command for the TZK10 tape drive. ; Create new SCSI command packet for "RETENSION" using ; the SCSI "LOAD" command. Created modifier IO$M_RETENSION ; IODEF for IO$_SKIPFILE function code. Created IFNOT_TZK10 ; macro to check for proper device type. ; ; X-16U1 MCY Mary Yuryan, Howard Palmer 20-Nov-1990 ; Remove the very short timeout period for the TSZ07 ; in the WAIT_UNIT_READY: routine. The current timeout ; caused DCL commands such as mount/init to timeout while ; waiting for the tape to rewind. ; ; X-16 JTK Jim Klumpp, Howard Palmer 13-Jul-1990 ; Add support for 9-track, dual density TSZ07 device. ; IO_SETMODE, IO_SENSEMODE routines modified to provide ; setting/reporting of TSZ07 density. Return SS$_DATAOVERUN ; status if a read fails with ILI status and the user ; requested less data than was in the actual record. ; ; X-15 JTK Jim Klumpp 06-Jun-1990 ; In SETUP_CMD, map the buffer with high priority to avoid ; deadlock. The map buffers elsewhere can remain at low ; priority, as there's no danger of deadlock on the first ; call to map buffer per QIO function. ; ; X-14 MCY Mary Yuryan 06-Mar-1990 ; Increase SCSI command timeout values to match those ; of the RDAT tape drive. Increase the default timeout ; from 4 seconds to 30 seconds. Add new inquiry entry ; for ucode change made to fix PVAX console output. ; Add device name for QIC tape - TZK10. ; ; X-13 JTK Jim Klumpp 23-Jan-1990 ; Fix rewind/immediate. The command following a ; rewind/immediate can fail if the rewind is still in ; progress. Call the wait unit ready routine in this ; situation to prevent the new command from failing. ; ; X-12 MCY Mary Yuryan 18-Jan-1990 ; Fix device identification field returned by the ; inquiry command that changed with new micro-code. ; ; X-11 JTK Jim Klumpp 5-Jan-1990 ; Fix revision checking. Bring a drive online whether it's ; out of rev or not (to prevent problems during installation). ; ; X-10 MCY Mary Yuryan 28-Dec-1989 ; Add TLZ04 (RDAT) tape support. ; ; X-9 JTK Jim Klumpp 28-Sep-1989 ; Merge changes from 5.3 stream including: ; ; X-7U2 JTK Jim Klumpp 25-Sep-1989 ; Fix read/reverse bug. Remove revision checking of ; third party drives. Decrease mount timeout time. ; ; X-7U1 DGB0318 Donald G. Blair 05-Aug-1989 ; Add DPT$V_NO_IDB_DISPATCH bit to the driver prologue table. ; ; X-8 JTK Jim Klumpp 4-Aug-1989 ; Change logical end of volume handling to ignore the ; mount status. Fix ident to match master pack. ; ; X-6 JTK Jim Klumpp 22-Jun-1989 ; Add callback support and data structure version ; checking for SPI$CONNECT. Remove TZ30-specific timeout ; support. Add fastboot support. Fix bug in IO_SKIP_RECORD_REV. ; Add IO_WRITEMARK routine which is equivalent to IO_WRITEOF. ; Translate all media errors to SS$_PARITY. Add density field ; to device type table. Fix multi volume test code. Add ; workaround for tapemark handling synchronization bug. ; Remove $SCDTDEF macro. Change number of arguments passed ; to SET_CONN_CHAR. Change priv needed for IO_DIAGNOSE function. ; ; X-5 JTK Jim Klumpp 16-Jun-1989 ; Add TZ30-specific timeout values, retry in SEND_COMMAND ; if drive returns BUSY status, several minor bugfixes. ; ; X-4 JTK Jim Klumpp 1-Jun-1989 ; Add more robust checking of the additional field in ; extended sense data. ; ; X-3 JTK Jim Klumpp 12-May-1989 ; SCSI tape class driver: complete replacement of ; PVAX monolithic tape driver. ;- .SBTTL + .SBTTL + SYMBOL DEFINITIONS .SBTTL + .SBTTL External symbol definitions ; ; External symbols ; $CANDEF ; Cancel reason codes $CRBDEF ; Channel request block $DCDEF ; Device classes and types $DDBDEF ; Device data block $DEVDEF ; Device characteristics $DTNDEF ; DDR DTN offsets $DTUDEF ; DDR DTU offsets $DYNDEF ; Data strucure types $EMBDEF ; Errorlog message buffer $FKBDEF ; Define fork block symbols $IDBDEF ; Interrupt data block $IODEF ; I/O function codes $IPLDEF ; Hardware IPL definitions $IRPDEF ; I/O request packet $MODEDEF ; Mode page handling defs $MTDEF ; Magtape status codes $MT2DEF ; Extended Magtape status codes $NSADEF ; Security Symbols $ORBDEF ; Object rights block $PCBDEF ; Process control block $PRVDEF ; Privilege mask $PTEDEF ; Page table entry symbols $SCDRPDEF ; SCSI SCDRP symbols $SCSIDEF ; SCSI definitions $SPDTDEF ; SCSI PDT symbols $SSDEF ; System status codes $UCBDEF ; Unit control block $VADEF ; Virtual address symbols $VECDEF ; Interrupt vector block $WCBDEF ; Window control block ; ; Define external references to force failure to assembly phase rather ; than being deferred until linking ; .EXTERNAL DO_MODE_PAGE .SBTTL Misc local symbols ; ; Local symbols ; LOADER = 1 ; Specify that a device is a loader - ; mcy / rcl - 9/18/92 ; DEBUG = 1 ; Flag to enable various tracing and ; debug features. .IF DEFINED DEBUG .PRINT ; - DEBUG flag is enabled .ENDC ; SCSI_STACK_CHECKING = 1 ; Flag to enable bounds checking by ; SUBSAVE and SUBRETURN macros .IF DEFINED SCSI_STACK_CHECKING .PRINT ; - SCSI stack bounds checking is enabled .ENDC ; MULTI_VOLUME_TEST = 1 ; Flag to enable multi-volume tape ; testing by causing ENDOFTAPE status ; to be returned on any writes after ; record 100 (hex). .IF DEFINED MULTI_VOLUME_TEST .PRINT ; - Remove code to test multi-volume tapes .ENDC P1 = 0 ; Offset to P1 parameter in FDT routine P2 = 4 ; Offset to P2 parameter in FDT routine SCDRPS_PER_UNIT = <3+1> ; Number of SCRPs to allocate per unit READY_POLL_INTERVAL = 1 ; Interval for test unit ready polling BUSY_RETRY_CNT = 10 ; Number of times to retry sending ; command if device is busy MAX_BCNT = 65535 ; Maximum byte count per transfer UCB_STACK_SIZE = 10 ; Size of internal stack in UCB DEFAULT_DISCONNECT_TIMEOUT = 30 ; Default values in seconds for disconnect DEFAULT_PHASE_CHANGE_TIMEOUT = 30 ; and phase change timeouts MK_ERROR_REVISION = 2 ; Errorlogging revision supported by ; this driver. This should be incremented ; each time an incompatible change is ; made to the errorlog packet format. MAX_REWIND_TIME = 3*60 ; Maximum time a rewind command should ; take MAX_UNLOAD_TIME = 3*60 ; Maximum time an unload command should ; take DEF_TAPE_PARAMS = - ; Default tape parameters: !- ; default density ; BLK_833 format SENSE_LEN = 18 ; Sense data length BPU_BIT = 2 ; Block position unknown in READ POS data .SBTTL SCSI status codes ; Define SCSI status mask SCSI$M_STAT_MASK = ^XC1 ; SCSI status byte mask ; Define offsets in various SCSI command packets. SCSI_XS$B_ADDNL_CODE30 = 8 ; Extended sense additional code (TZ30) SCSI_WFM$B_CNT = 2 ; Write filemarks count SCSI_RD$B_LEN = 2 ; Read transfer length SCSI_WRT$B_LEN = 2 ; Write transfer length SCSI_RWND$B_IMMED = 1 ; Rewind immediate flag SCSI_SKIP$B_CNT = 2 ; Skip record count SCSI_RCVD$B_HW_REV = 0 ; Receive diagnostic HW revision field SCSI_RCVD$B_SW_REV = 1 ; Receive diagnostic SW revision field SCSI_MSNS$B_WP = 2 ; Mode sense write protect field SCSI_MSNS$V_WP = 7 ; Mode sense write protect bit SCSI_MSNS$B_BLOCK = 3 ; Mode sense block descriptor length SCSI_MSNS$V_BLOCK = 3 ; Mode sense block descriptor bit SCSI_MSNS$B_DENSITY = 4 ; Mode sense density code SCSI_MSEL$W_RSVD0 = 0 ; Mode select reserved SCSI_MSEL$B_SPEED = 2 ; Mode select speed field SCSI_MSEL$B_MODE = 2 ; Mode select buffered mode SCSI_MSEL$B_DSCLEN = 3 ; Mode select record descriptor length SCSI_MSEL$C_DSCLEN = 8 ; Mode select record descriptor length SCSI_MSEL$B_DENS = 4 ; Mode select density SCSI_MSEL$B_BLOCKS = 5 ; Mode select number of blocks SCSI_MSEL$B_RSVD1 = 8 ; Mode select reserved SCSI_MSEL$B_BLKLEN = 9 ; Mode select block length SCSI_MSEL$B_VULEN = 12 ; Mode select vendor unique length SCSI_MSEL$B_VU = 13 ; Mode select vendor unique field SCSI_MSEL$M_BUF = ^X10 ; Mode select buffered mode SCSI_MSEL$B_COMP = 14 ; Mode select data compression algorithm SCSI_MSEL$M_NOF = 7 ; Number of fillers for generic device SCSI_MSEL$M_NOF50 = 7 ; Number of fillers for TZK50 SCSI_MSEL$M_NOF30 = ^X0F ; Number of fillers for TZ30 SCSI_MSEL$M_RESEL = ^X40 ; Reselection timeout flag ; Get/set connect characteristics symbols. SET_CON$L_LEN = 0 ; Length field SET_CON$L_CON_FLAGS = 4 ; Flags field SET_CON$M_DISC = 1 ; Enable disconnect flag SET_CON$M_NORETRY = 2 ; Disable command retry flag SET_CON$L_SYN_FLAG = 8 ; Synchronous flag field SET_CON$M_SYN = 1 ; Synchronous flag ; Request Sense resource REQSNS_SIZE = 19 ; 19 bytes for request sense data .SBTTL Tape class driver extensions to the UCB ; ; Tape class driver extensions to the UCB. ; $DEFINI UCB ; Start of UCB definitions . = UCB$K_LCL_TAPE_LENGTH $DEF UCB$L_HW_REV .BLKL 1 ; Hardware revision field $DEF UCB$L_STACK_PTR .BLKL 1 ; Internal stack pointer $DEF UCB$L_STACK .BLKL UCB_STACK_SIZE ; Internal stack $DEF UCB$L_SCDRP .BLKL 1 ; Address of active SCDRP $DEF UCB$L_SCDRP_SAV1 .BLKL 1 ; Address of saved SCDRP $DEF UCB$L_STACK_SCDRP .BLKL 1 ; Address of spare SCDRP used soley for it's ; SCDRP$L_CLASS_STACK when no SCDRP is available $DEF UCB$L_FLUSH_IOQFL .BLKL 1 ; I/O flush queue forward link $DEF UCB$L_FLUSH_IOQBL .BLKL 1 ; I/O flush queue backward link $DEF UCB$L_ERR_MASK .BLKL 1 ; Mask of error types logged so far $DEF UCB$B_ERR_ARRAY .BLKB 16 ; Saved error array used to filter errors $DEF UCB$L_SCDT .BLKL 1 ; SCDT address $DEF UCB$L_MK_FLAGS .BLKL 1 ; Class driver flags $VIELD UCB,0,<- ; ,- ; Removable media ,- ; Wait for unit ready in progress ,- ; Device supports disconnect ,- ; Device supports synchronous operation ,- ; Disable errorlogging ,- ; Reverse motion in progress ,- ; Additional data is valid ,- ; Skip record operation in progress ,- ; Rewind operation may still be active ,- ; Unload operation may still be active ,- ; Compaction support check in progress ,- ; Device is an 8MM device ,-; Request Sense resources in use ,-; Device supports READ POSITION cmd ,- ; Space filemarks operation in progress ,-; READ POSITION support check in progress ,-; Position should not be updated ,-; Set until first READ POSITION completes ,- ; Fast skip when func code modifier present > ; Fast skip all the time $DEF UCB$L_SCDRPQ_FL .BLKL 1 ; Queue of free SCDRPs used to $DEF UCB$L_SCDRPQ_BL .BLKL 1 ; send SCSI commands $DEF UCB$L_ADDNL_INFO .BLKL 1 ; Additional extended sense info $DEF UCB$L_PREV_TM .BLKL 1 ; Position on tape of last filemark $DEF UCB$L_MIN_REV .BLKL 1 ; Minimum revision level $DEF UCB$L_MSEL_INFO .BLKL 1 ; Pointer to vendor-unique mode select info $DEF UCB$L_COMP_PAGE .BLKL 1 ; Pointer to mode select page 10 (compaction) $DEF UCB$L_TR_QIO_STS .BLKL 1 ; Address in trace buf to put QIO status $DEF UCB$L_REWIND_TIME .BLKL 1 ; Time by which a rewind/immediate command ; must be completed $DEF UCB$L_UNLOAD_TIME .BLKL 1 ; Time by which a unload command ; must be completed $DEF UCB$L_SKIPFILE_STAT .BLKL 1 ; Saved status after skipfile $DEF UCB$B_BUSY_RETRY .BLKB 1 ; Retry count for BUSY during send $DEF UCB$B_SENSE_KEY .BLKB 1 ; Saved extended sense key $DEF UCB$B_SENSE_INFO .BLKB ; Balance of additional sense info UCB$B_ADDL_SENSE_INFO==UCB$B_SENSE_INFO ; Make it a global symbol for use ; by mode sense common $DEF UCB$B_SENSE_LEN .BLKB 2 ; Count of additional bytes $DEF UCB$B_SCSI_VERSION .BLKB 1 ; SCSI version from INQUIRY $DEF UCB$B_LUN .BLKB 1 ; Logical unit number (LUN) $DEF UCB$B_COMP_STATE .BLKB 1 ; saved data compaction state for ; SETUP_COMPACTION routine $DEF UCB$B_SAVED_COMP .BLKB 1 ; Current compaction state before ; CHECK_COMPACTION_SUPPORT called $DEF UCB$W_MK_DENSITY .BLKW 1 ; Saved 9-track density $DEF UCB$B_8MM_CHK .BLKB 1 ; 8MM $ COPY check-in-progress flag $DEF UCB$L_REQSNS_PAGE .BLKL 1 ; Requese Sense dma page resource $DEF UCB$L_DISABLE_DDR .BLKL 1 ; Non-zero disables DDR ; (DEFAULT IS DISABLED!) $DEF UCB$L_SAV_SEND_STATUS .BLKL 1 ; Status returned by SEND_COMMAND $DEF UCB$K_MK_UCBLEN ; Length of extended UCB $DEFEND UCB ; End of UCB definitions .SBTTL Errorlog packet formats ;+ ; Following are the definitions for class driver errorlog packets. Each packet ; has a section common for all error types followed by an error-specific section. ;- $DEFINI ERROR_PACKETS . = EMB$L_DV_REGSAV ; Start of area to dump error info $DEF ERR$LW_CNT .BLKL 1 ; Count of number of LWs that follow $DEF ERR$REVISION .BLKB 1 ; Revision level $DEF ERR$HW_REV .BLKL 1 ; Hardware revision $DEF ERR$TYPE .BLKB 1 ; Error type $DEF ERR$SCSI_ID .BLKB 1 ; SCSI ID $DEF ERR$SCSI_LUN .BLKB 1 ; SCSI logical unit $DEF ERR$SCSI_SUBLUN .BLKB 1 ; SCSI sub-logical unit $DEF ERR$PORT_STATUS .BLKL 1 ; Port status code $DEF ERR$CMD_LEN .BLKB 1 ; SCSI command length field $DEF ERR$SCSI_STS .BLKB 1 ; SCSI status byte $DEF ERR$ADDIT_LEN .BLKB 1 ; Additional length field $DEF ERR$K_STANDARD_LENGTH ; Standard length of error packet ; Now define packets that have one or more of the variable length fields ; filled in. These fields consist of a byte count followed by n bytes of ; data. In the standard packet defined above, the byte count field would ; contain a zero for each possible variable length field. The list of variable ; length fields is: ; ; o SCSI command data (up to 12 bytes) ; o Additional data which depends upon the error type $DEF ERR$CMD_BYTES .BLKB 12 ; Maximum possible command bytes $DEF ERR$K_COMMAND_LENGTH ; Length of packet containing SCSI command $DEF ERR$INQUIRY_DATA .BLKB 36 ; Inquiry data $DEF ERR$K_INQUIRY_LENGTH ; Length of packet containing INQUIRY data .=ERR$K_COMMAND_LENGTH $DEF ERR$EXTND_SENSE_DATA .BLKB 18 ; Extended sense data $DEF ERR$K_EXTND_SENSE_LENGTH ; Length of packet containing extended ; sense data .=ERR$K_COMMAND_LENGTH $DEF ERR$MODE_SENSE_DATA .BLKB 150 ; Mode sense data $DEF ERR$K_MODE_SENSE_LENGTH ; Length of packet containing mode ; sense data .=ERR$K_COMMAND_LENGTH $DEF ERR$REASSIGN_BLOCK_DATA .BLKB 8 ; Reassign block data $DEF ERR$K_REASSIGN_BLOCK_LENGTH ; Length of packet containing reassign ; block data .=ERR$K_COMMAND_LENGTH $DEF ERR$DIAGNOSTIC_DATA .BLKB 18 ; Received diagnostic data $DEF ERR$K_DIAGNOSTIC_DATA_LENGTH ; Length of packet containing received ; diagnostic data $DEFEND ERROR_PACKETS .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$$,
$$TEMP$$ = ^A/$$L$$/ - ^X40 .IF GT $$TEMP$$ $$MEDIA$$ = $$MEDIA$$ + <$$TEMP$$ @ $$S$$> .ENDC $$S$$ = $$S$$ - 5 .ENDR .IRPC $$L$$, .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 .SBTTL SENSE_KEY - Build sense key to VMS status code translation table ;+ ; SENSE_KEY ; ; This macro is used to build a translation table of sense key to VMS status ; codes. Each time extended sense infromation is returned by the target, the ; sense key is translated to a VMS status code using this table. Each entry in ; the table has the following format: ; ; +-----------------------+ ; | Sense key | 1 byte ; +-----------------------+ ; | VMS status code | 4 bytes ; +-----------------------+ ; ; The table is terminated by a sense key of -1. ;- .MACRO SENSE_KEY, SCSI_STATUS, VMS_STATUS .BYTE SCSI$C_'SCSI_STATUS' .WORD SS$_'VMS_STATUS' .ENDM SENSE_KEY .SBTTL LOG_ERROR - Log a SCSI tape class driver error ;+ ; LOG_ERROR ; ; This macro logs a SCSI tape class driver error. The error type and VMS status ; code are placed in R7 and R8 respectively, and the LOG_ERROR routine is ; called, which does most of the work. ;- .MACRO LOG_ERROR,TYPE,VMS_STATUS,UCB=R3 PUSHR #^M ; Save regs .IF DIF UCB,R5 MOVL UCB,R5 ; Get UCB address .ENDC MOVL #SCSI$C_'TYPE',R7 ; Get error code MOVL VMS_STATUS,R8 ; And VMS status code BSBW LOG_ERROR ; Write an errorlog entry POPR #^M ; Restore regs .ENDM LOG_ERROR .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, SCSI_ERROR_CODES1 LIST_ENTRY .ENDR .ENDM SCSI_ERROR_CODES .SBTTL DISABLE_ERRLOG - Temporarily disable errorlogging .SBTTL REENABLE_ERRLOG - Reenable errorlogging ;+ ; DISABLE_ERRLOG ; REENABLE_ERRLOG ; ; This macros are used to disable and reenable errorlogging respectively. ; The DISABL_ERRLOG flag in the UCB is used to temporarily disable errorlogging ; when the class driver prepares to do something which is likely to cause an ; error that should be supressed. For example, when checking to see if a ; device supports COMPACTION, we don;t want a failed MODE_SELECT to generate ; an error log. ; Since the disabling of errorlogging can be nested, the old value of the ; DISABL_ERRORLOG flag is saved in the local SCDRP stack. .MACRO DISABLE_ERRLOG SUBPUSH UCB$L_MK_FLAGS(R3) ; Save current flags value ASSUME UCB$V_DISABL_ERRLOG LT 8 BISB #UCB$M_DISABL_ERRLOG,- ; Temporarily disable errorlogging UCB$L_MK_FLAGS(R3) ; .ENDM DISABLE_ERRLOG .MACRO REENABLE_ERRLOG SUBPOP UCB$L_MK_FLAGS(R3) ; Reenable errorlogging .ENDM REENABLE_ERRLOG .SBTTL WORD_BRANCHES - Define word displacement branches ;+ ; WORD_BRANCHES ; ; This macro defines for each Bxxx (conditional branch) instruction an equivalent ; macro named BxxxW with a word displacement. The macro takes as an argument ; a list of tuples, each tuple containing 3 items: 1) a conditional branch ; opcode; 2) the opcode with the opposite polarity; and 3) the number of ; arguments required by the opcode. ;- .MACRO WORD_BRANCHES LIST .MACRO WORD_BRANCHES2, OPCODE1, OPCODE2, ARGCNT .IF EQ ARGCNT-0 .MACRO OPCODE1, DST, ?L OPCODE2 L BRW DST L: .ENDM OPCODE1 .ENDC .IF EQ ARGCNT-1 .MACRO OPCODE1, FIELD, DST, ?L OPCODE2 FIELD,L BRW DST L: .ENDM OPCODE1 .ENDC .IF EQ ARGCNT-2 .MACRO OPCODE1, BIT, FIELD, DST, ?L OPCODE2 BIT,FIELD,L BRW DST L: .ENDM OPCODE1 .ENDC .ENDM WORD_BRANCHES2 .MACRO WORD_BRANCHES1, OPCODE1, OPCODE2, ARGCNT WORD_BRANCHES2 'OPCODE1'W, OPCODE2, ARGCNT WORD_BRANCHES2 'OPCODE2'W, OPCODE1, ARGCNT .ENDM WORD_BRANCHES1 .IRP ENTRY, WORD_BRANCHES1 ENTRY .ENDR .ENDM WORD_BRANCHES WORD_BRANCHES <- ,- ,- ,- ,- ,- ,- ,- ,- ,- ,- ,- > .SBTTL INIT_SCDRP_STACK - Initialize the internal SCDRP stack .SBTTL SUBPUSH - Push an item on the UCB stack .SBTTL SUBPOP - Pop an item from the UCB stack .SBTTL SUBGET - Retrieve an item from the UCB stack (but don't pop) .SBTTL SUBSAVE - Save a return address on the UCB stack .SBTTL SUBRETURN - Return to the address saved on the UCB stack ;+ ; INIT_SCDRP_STACK ; SUBPUSH ; SUBPOP ; SUBGET ; SUBSAVE ; SUBRETURN ; These macros manipulate the SCDRP internal stack which is used to save ; routine return address and temporary variables. ; ; NOTE: The argument ARG can only be used once in each macro so that the ; use of autoincrement/autodecrement involving ARG will work. ;- .MACRO INIT_SCDRP_STACK,SCDRP=R5,?l1 MOVAL SCDRP$L_CLASS_STACK-4(SCDRP),- SCDRP$L_CLASS_STACK_PTR(SCDRP) .ENDM INIT_SCDRP_STACK .MACRO SUBPUSH,ARG,SCDRP=R5,?l1,?l2 .if defined scsi_stack_checking .nlist meb pushal scdrp$l_class_stack+(scdrp) cmpl (sp)+,scdrp$l_class_stack_ptr(scdrp) bgtru l2 l1: BUG_CHECK INCONSTATE,FATAL ; SCSI stack overflow l2: .list meb .endc ADDL #4,SCDRP$L_CLASS_STACK_PTR(SCDRP) MOVL ARG,@SCDRP$L_CLASS_STACK_PTR(SCDRP) .ENDM SUBPUSH .MACRO SUBPOP,ARG,SCDRP=R5,?l1,?l2 .if defined scsi_stack_checking .nlist meb pushal scdrp$l_class_stack-4(scdrp) cmpl (sp)+,scdrp$l_class_stack_ptr(scdrp) blssu l2 l1: BUG_CHECK INCONSTATE,FATAL ; SCSI stack underflow l2: .list meb .endc MOVL @SCDRP$L_CLASS_STACK_PTR(SCDRP),ARG SUBL #4,SCDRP$L_CLASS_STACK_PTR(SCDRP) .ENDM SUBPOP .MACRO SUBGET,ARG,SCDRP=R5,?l1,?l2 .if defined scsi_stack_checking .nlist meb pushal scdrp$l_class_stack-4(scdrp) cmpl (sp)+,scdrp$l_class_stack_ptr(scdrp) blssu l2 l1: BUG_CHECK INCONSTATE,FATAL ; SCSI stack underflow l2: .list meb .endc MOVL @SCDRP$L_CLASS_STACK_PTR(SCDRP),ARG .ENDM SUBGET .MACRO SUBSAVE,SCDRP=R5,?l1,?l2 SUBPUSH (SP)+,SCDRP .ENDM SUBSAVE .MACRO SUBRETURN,SCDRP=R5,?l1,?l2 SUBPOP -(SP),SCDRP RSB .ENDM SUBRETURN .MACRO ALLOC_STACK_SCDRP, UCB=R3,?L1,?L2 L1: MOVL UCB$L_STACK_SCDRP(UCB),R5 ; Get address of STACK UCB TSTL SCDRP$L_SVA_USER(R5) ; Does anyone own it now? BEQL L2 ; Nope BUG_CHECK INCONSTATE, FATAL ; Someone else has it! L2: MOVAB L1,SCDRP$L_SVA_USER(R5) ; Save PC of allocater .ENDM ALLOC_STACK_SCDRP .MACRO FREE_STACK_SCDRP, SCDRP=R5, ?L1 TSTL SCDRP$L_SVA_USER(SCDRP) ; Is it owned? BNEQ L1 ; If yes, looks OK. BUG_CHECK INCONSTATE, FATAL ; Not good! L1: CLRL SCDRP$L_SVA_USER(SCDRP) ; No owner now .ENDM FREE_STACK_SCDRP .SBTTL MK_WAIT - Stall a thread for a specific number of seconds ;+ ; MK_WAIT ; ; This macro uses the device timeout mechanism to stall a thread for a specified ; number of seconds. The UCB address and stall time are required as inputs. ;- .MACRO MK_WAIT,SECONDS,UCB=R5,SCRATCH=R0,?L .IF DIF UCB,R5 MOVL R5,SCRATCH MOVL UCB,R5 MOVL SCRATCH,UCB .ENDC DSBINT ENVIRON=UNIPROCESSOR PUSHL SECONDS BSBW MK_WAIT .WORD L-. L: IOFORK BICW #UCB$M_TIMOUT,- UCB$W_STS(R5) .IF DIF UCB,R5 MOVL UCB,SCRATCH MOVL R5,UCB MOVL SCRATCH,R5 .ENDC .ENDM MK_WAIT .SBTTL SCSI_CMD - Define a SCSI command packet ;+ ; SCSI_CMD ; ; This macro defines the contents of a SCSI command packet. Each SCSI command ; can have associated with it a DMA buffer used during the DATAIN/DATAOUT bus ; phases. A DMA length of zero indicates there is no DATA(IN/OUT) phase ; associated with this command (except in the case of a read/write SCSI command ; which is handled specially. The SETUP_CMD routine uses this information in ; preparing to send a SCSI command. The macro generates a label and the SCSI ; command information as follows: ; ; +-----------------------+ ; | SCSI cmd length | 1 byte ; +-----------------------+ ; | SCSI cmd bytes | n bytes ; +-----------------------+ ; |Timeout value (seconds)| 2 bytes ; +-----------------------+ ; | DMA buffer length | 2 bytes ; +-----------------------+ ; | DMA direction | 1 byte ; +-----------------------+ ; ; DMA direction is defined as: 0=write, 1=read. ;- .MACRO SCSI_CMD, NAME, CMD_BYTES, DMA_LEN=0, DMA_DIR=READ, TIMEOUT=30 CMD_'NAME':: $$$BYTE_COUNT=0 .IRP CMD_BYTE, $$$BYTE_COUNT = $$$BYTE_COUNT + 1 .IIF EQ $$$BYTE_COUNT-1, SCSI$C_'NAME' = CMD_BYTE ; Define opcode .ENDR .BYTE $$$BYTE_COUNT .IRP CMD_BYTE, .BYTE CMD_BYTE .ENDR .WORD TIMEOUT .WORD DMA_LEN $$$DIRECTION = 0 .IIF IDN DMA_DIR, READ, $$$DIRECTION = 1 .BYTE $$$DIRECTION .IIF IDN NAME,MODE_SELECT_TMP, MODE_SEL_TMP_LEN = .-CMD_'NAME' .ENDM SCSI_CMD .SBTTL IF_TZ30 - Branch if device a TZ30 .SBTTL IF_TK50 - Branch if device is a TZK50 .SBTTL IFNOT_TZ30 - Branch if device is not a TZ300 .SBTTL IFNOT_TK50 - Branch if device is not a TZK50 .SBTTL IF_TK - Branch if device is a TZK50 or TZ30 .SBTTL IFNOT_TK - Branch if device is not a TZK50 or TZ30 .SBTTL IF_TSZ05 - Branch if device is a TSZ05 .SBTTL IFNOT_TSZ05 - Branch if device is not a TSZ05 .SBTTL IF_TSZ07 - Branch if device is a TSZ07 .SBTTL IFNOT_TSZ07 - Branch if device is not a TSZ07 .SBTTL IF_TZK10 - Branch if device is a TZK10 .SBTTL IFNOT_TZK10 - Branch if device is not a TZK10 .SBTTL IF_TZK11 - Branch if device is a TZK11 .SBTTL IFNOT_TZK11 - Branch if device is not a TZK11 .SBTTL IF_8MM - Branch if device is an 8mm device .SBTTL IFNOT_8MM - Branch if device is not an 8mm device .SBTTL IF_TKZ09 - Branch if device is a TKZ09 .SBTTL IF_TKZ60 - Branch if device is a TKZ60 .SBTTL IFNOT_TKZ60 - Branch if device is not a TKZ60 ;- ; IF_TZ30 ; IF_TK50 ; IFNOT_TZ30 ; IFNOT_TK50 ; IF_TK ; IFNOT_TK ; IF_TSZ05 ; IFNOT_TSZ05 ; IF_TSZ07 ; IFNOT_TSZ07 ; IF_TZK10 ; IFNOT_TZK10 ; IF_TZK11 ; IFNOT_TZK11 ; IF_8MM ; IFNOT_8MM ; IF_TKZ09 ; IF_TKZ60 ; IFNOT_TKZ60 ; These macros are used for device-dependent dispatching. ;- .MACRO IF_TZ30, LABEL, UCB=R3 CMPB UCB$B_DEVTYPE(R3),- ; Is device a TZ30? #DT$_TZ30 ; BEQL LABEL ; Branch if so .ENDM IF_TZ30 .MACRO IF_TK50, LABEL, UCB=R3 CMPB UCB$B_DEVTYPE(R3),- ; Is device a TK50? #DT$_TK50 ; BEQL LABEL ; Branch if so .ENDM IF_TK50 .MACRO IFNOT_TK50, LABEL, UCB=R3 CMPB UCB$B_DEVTYPE(R3),- ; Is device a TK50? #DT$_TK50 ; BNEQ LABEL ; Branch if not .ENDM IFNOT_TK50 .MACRO IFNOT_TZ30, LABEL, UCB=R3 CMPB UCB$B_DEVTYPE(R3),- ; Is device a TZ30? #DT$_TZ30 ; BNEQ LABEL ; Branch if not .ENDM IFNOT_TZ30 .MACRO IF_TK, LABEL, UCB=R3 CMPB UCB$B_DEVTYPE(R3),- ; Is device a TZ30? #DT$_TZ30 ; BEQL LABEL ; Branch if so CMPB UCB$B_DEVTYPE(R3),- ; Is device a TK50? #DT$_TK50 ; BEQL LABEL ; Branch if so .ENDM IF_TK .MACRO IFNOT_TK, LABEL, UCB=R3, ?L CMPB UCB$B_DEVTYPE(R3),- ; Is device a TZ30? #DT$_TZ30 ; BEQL L ; Branch if so CMPB UCB$B_DEVTYPE(R3),- ; Is device a TK50? #DT$_TK50 ; BNEQ LABEL ; Branch if not L: .ENDM IFNOT_TK .MACRO IF_TSZ05, LABEL, UCB=R3 CMPB UCB$B_DEVTYPE(R3),- ; Is device a TSZ05? #DT$_TSZ05 ; BEQL LABEL ; Branch if so .ENDM IF_TSZ05 .MACRO IFNOT_TSZ05, LABEL, UCB=R3, CMPB UCB$B_DEVTYPE(R3),- ; Is device a TSZ05? #DT$_TSZ05 ; BNEQ LABEL ; Branch if not .ENDM IFNOT_TSZ05 .MACRO IF_TSZ07, LABEL, UCB=R3 CMPB UCB$B_DEVTYPE(R3),- ; Is device a TSZ07? #DT$_TSZ07 ; BEQL LABEL ; Branch if so .ENDM IF_TSZ07 .MACRO IFNOT_TSZ07, LABEL, UCB=R3 CMPB UCB$B_DEVTYPE(R3),- ; Is device a TSZ07? #DT$_TSZ07 ; BNEQ LABEL ; Branch if not .ENDM IFNOT_TSZ07 .MACRO IF_TZK10, LABEL, UCB=R3 CMPB UCB$B_DEVTYPE(R3),- ; Is device a TZK10 ? #DT$_TZK10 ; for retensioning BEQL LABEL ; Branch if yes .ENDM IF_TZK10 .MACRO IFNOT_TZK10, LABEL, UCB=R3 CMPB UCB$B_DEVTYPE(R3),- ; Is device a TZK10 ? #DT$_TZK10 ; for retensioning BNEQ LABEL ; Branch if not .ENDM IFNOT_TZK10 .MACRO IF_TZK11, LABEL, UCB=R3 CMPB UCB$B_DEVTYPE(R3),- ; Is device a TZK11 ? #DT$_TZK11 ; for retensioning BEQL LABEL ; Branch if yes .ENDM IF_TZK11 .MACRO IFNOT_TZK11, LABEL, UCB=R3 CMPB UCB$B_DEVTYPE(R3),- ; Is device a TZK11 ? #DT$_TZK11 ; for retensioning BNEQ LABEL ; Branch if not .ENDM IFNOT_TZK11 .MACRO IF_8MM, LABEL, UCB=R3 BITL #UCB$M_DEVICE_IS_8MM,- UCB$L_MK_FLAGS(R3) ; Is device an 8mm tape BNEQ LABEL ; Branch if so .ENDM IF_8MM .MACRO IFNOT_8MM, LABEL, UCB=R3 BITL #UCB$M_DEVICE_IS_8MM,- UCB$L_MK_FLAGS(R3) ; Is device an 8mm tape BEQL LABEL ; Branch if not .ENDM IFNOT_8MM .MACRO IF_TKZ09, LABEL, UCB=R3 CMPB UCB$B_DEVTYPE(R3),- ; Is device a TKZ09? #DT$_TKZ09 ; BEQL LABEL ; Branch if so .ENDM IF_TKZ09 .MACRO IF_TKZ60, LABEL, UCB=R3 CMPB UCB$B_DEVTYPE(R3),- ; Is device a TKZ60? #DT$_TKZ60 ; BEQL LABEL ; Branch if so .ENDM IF_TKZ60 .MACRO IFNOT_TKZ60, LABEL, UCB=R3 CMPB UCB$B_DEVTYPE(R3),- ; Is device a TKZ60? #DT$_TKZ60 ; BNEQ LABEL ; Branch if not .ENDM IFNOT_TKZ60 .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 END=MK_END,- ; End of driver label ADAPTER=NULL,- ; Adapter type UCBSIZE=,- ; Length of UCB NAME=MKDRIVER,- ; Driver name FLAGS= ; Don't fill in IDB$L_UCBLST DPT_STORE INIT ; Start of load ; initialization table DPT_STORE DDB,DDB$L_ACPD,L,<^A\MTA\> ; Default ACP name DPT_STORE UCB,UCB$L_MAXBCNT,L,MAX_BCNT ; Max byte count DPT_STORE UCB,UCB$B_FLCK,B,SPL$C_IOLOCK8 ; Device FORK LOCK DPT_STORE UCB,UCB$B_DIPL,B,22 ; Device interrupt IPL DPT_STORE UCB,UCB$L_DEVCHAR,L,<- ; Device characteristics DEV$M_FOD!- ; Files oriented DEV$M_DIR!- ; Directory structured DEV$M_AVL!- ; Available DEV$M_ELG!- ; Error logging enabled DEV$M_IDV!- ; Input device DEV$M_ODV!- ; Output device DEV$M_SDI!- ; Single directory device DEV$M_SQD> ; Random Access Device DPT_STORE UCB,UCB$L_DEVCHAR2,L,<- ; Device characteristics DEV$M_SCSI!- ; device is a SCSI device DEV$M_NNM> ; Prefix name with "node$" DPT_STORE UCB,UCB$B_DEVCLASS,B,DC$_TAPE ; Sample device class DPT_STORE UCB,UCB$B_DEVTYPE,B,- ; Device type (default) DT$_GENERIC_MK DPT_STORE UCB,UCB$W_DEVBUFSIZ,W,2048 ; Default buffer size DPT_STORE UCB,UCB$L_DEVDEPEND,W,- ; Default tape parameters DEF_TAPE_PARAMS ; Format=DEFAULT, Density=BLK_833 DPT_STORE UCB,UCB$B_ERTCNT,B,16 ; Error retry count DPT_STORE UCB,UCB$B_ERTMAX,B,16 ; Max error retry count DPT_STORE UCB,UCB$L_MK_FLAGS,L,0 ; Initialize flags field DPT_STORE UCB,UCB$L_ERR_MASK,L,0 ; Initialize error mask field DPT_STORE UCB,UCB$W_MK_DENSITY,W,- ; Initialize 9-track H/W MT$K_GCR_6250-2 ; density. DPT_STORE UCB,UCB$L_REQSNS_PAGE,L,0 ; Initialize pointer DPT_STORE UCB,UCB$L_DISABLE_DDR,L,1 ; Non zero disables DDR DPT_STORE REINIT ; Start of reload ; initialization table DPT_STORE DDB,DDB$L_DDT,D,MK$DDT ; Address of DDT DPT_STORE CRB,- ; Address of controller CRB$L_INTD+VEC$L_INITIAL,- ; initialization routine D,MK_CTRL_INIT DPT_STORE CRB,- ; Address of device CRB$L_INTD+VEC$L_UNITINIT,- ; unit initialization D,MK_UNIT_INIT ; routine DPT_STORE CRB,CRB$B_FLCK,B,IPL$_IOLOCK8 ; Initialize fork lock field DPT_STORE END ; End of initialization ; tables .SBTTL Driver dispatch table ;+ ; Driver dispatch table ; ; This table defines the entry points into the driver. ;- DDTAB - ; DDT-creation macro DEVNAM=MK,- ; Name of device START=MK_STARTIO,- ; Start I/O routine FUNCTB=MK_FUNCTABLE,- ; FDT address CANCEL=MK_CANCEL,- ; Cancel I/O routine REGDMP=MK_REG_DUMP ; Register dump routine .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. ;- MK_FUNCTABLE: ; FDT for driver FUNCTAB,<- ; Valid I/O functions NOP,- ; No operation UNLOAD,- ; Unload volume SPACERECORD,- ; Space records RECAL,- ; Recalibrate (rewind) DRVCLR,- ; Driver clear READPRESET,- ; Read in preset PACKACK,- ; Pack acknowledge ERASETAPE,- ; Erase tape DSE,- ; Data security erase SENSECHAR,- ; Sense tape characteristics SETCHAR,- ; Set characteristics DIAGNOSE,- ; Special pass-through function SPACEFILE,- ; Space file WRITECHECK,- ; Write check forward WRITEPBLK,- ; Write physical block WRITERET,- ; Write physical block retry READPBLK,- ; Read physical block REREADN,- ; Reread next REREADP,- ; Reread previous AVAILABLE,- ; Available (rewind/nowait clear valid) WRITEMARK,- ; Write tape mark READLBLK,- ; Read logical block WRITELBLK,- ; Write logical block SENSEMODE,- ; Sense tape mode SETMODE,- ; Set mode REWIND,- ; Rewind REWINDOFF,- ; Rewind and set offline SKIPRECORD,- ; Skip records SKIPFILE,- ; Skip files WRITEOF,- ; Write end of file READVBLK,- ; Read virtual block WRITEVBLK,- ; Write virtual block ACCESS,- ; Access file and/or find directory ACPCONTROL,- ; ACP control function CREATE,- ; Create file and/or create directory DEACCESS,- ; Deaccess file DELETE,- ; Delete file and/or directory entry MODIFY,- ; Modify file attributes MOUNT> ; Mount volume FUNCTAB,- ; Buffered I/O functions ; Mount volume FUNCTAB +ACP$READBLK,- ; Read functions ; Read virtual block FUNCTAB +ACP$WRITEBLK,- ; Write functions ; Write virtual block FUNCTAB +ACP$ACCESS, ; Access & create file or directory FUNCTAB +ACP$DEACCESS, ; Deaccess file FUNCTAB +ACP$MODIFY,- ; ; Modify file attributes FUNCTAB +ACP$MOUNT, ; Mount volume FUNCTAB +MT$CHECK_ACCESS,- ; Magtape check access funcitons ; Write end of file FUNCTAB +EXE$ZEROPARM,- ; Zero parameter functions ; Write end of file FUNCTAB +EXE$ONEPARM,- ; One parameter functions ; Skip files FUNCTAB MK_SENSEMODE,- ; Sense tape characteristics ; Sense mode ; Our local setmode processing, handles it where fcn modifiers exist only. FUNCTAB MK_SETMODE,- ; Set tape characteristics ; FUNCTAB +EXE$SETMODE,- ; Set tape characteristics ; FUNCTAB MK_DIAGNOSE,<- ; Special pass-through function DIAGNOSE> ; .SBTTL SCSI_DEV_TYPES - Build SCSI device table ;+ ; SCSI_DEV_TYPES ; ; This macro builds a table of pre-defined SCSI device types. During unit ; initialization, an inquiry command is sent to the target which returns ; 8 bytes of ID string. The table is then scanned for a matching ID. If one ; is found, information for that entry is copied into the UCB, including the ; device type, media ID, disconnect/synchronous flags, and various timeout ; values. If no matching entry is found, the device is assumed to be a ; "generic" SCSI disk, and the entry for generic devices is used. Each entry ; in the device type table has the following format: ; +-----------------------+ ; | VMS device type | 1 byte ; +-----------------------+ ; | ID string | 8 bytes ; +-----------------------+ ; | Min revision level | 4 bytes ; +-----------------------+ ; | Media ID | 4 bytes ; +-----------------------+ ; | Vendor-unique info | 10 bytes ; +-----------------------+ ; | Density code | 1 byte ; +-----------------------+ ; ; The table is terminated with a VMS device code of 0. ;- .MACRO SCSI_DEV_TYPES, LIST .MACRO SCSI_DEV_TYPES1, ID_STRING, DEVICE_TYPE, MINIMUM_REVISION, - DENSITY, MODE_SEL_INFO, LOADER=0 ; loader added mcy/rcl 9/18/92 .IF IDN , TZ30 TZ30_DEV_TYPE: .ENDC .IF IDN , TK50 TK50_DEV_TYPE: .ENDC .IF IDN , GENERIC GENERIC_DEV_TYPE: .ENDC .IIF NDF SDTE_BEGIN, SDTE_BEGIN = . ; Adjust table length for loader ; mcy/rcl - 9/18/92 ; Device type .BYTE DT$_'DEVICE_TYPE' ; 8 character product ID string, padded with spaces .NCHR $$$STRLEN, .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 .IF IDN DEVICE_TYPE,GENERIC_MK MEDIA , , DT$_GENERIC_MK ; Media value for generic MK device .IFF MEDIA , .ENDC ; Vendor-unique mode select data. This is a counted string of bytes with the ; first byte specifying the number of bytes that follow. The total field length, ; including the count, is 10 bytes. $$$COUNT = 0 .IRP MODE_SEL_BYTE, $$$COUNT = $$$COUNT + 1 .ENDR .IIF GT $$$COUNT-9, .ERROR ; - Illegal number of mode sense bytes .BYTE $$$COUNT .IRP MODE_SEL_BYTE, .BYTE MODE_SEL_BYTE .ENDR .REPT 9-$$$COUNT .BYTE 0 .ENDR ; .BYTE MT$K_'DENSITY' .BYTE LOADER ; RCL/MCY - loader support 9/18/92 .IIF NDF SDTE_END, SDTE_END = . ; Adjust table length for loader - ; mcy/rcl - 9/18/92 .ENDM SCSI_DEV_TYPES1 .IRP ENTRY, SCSI_DEV_TYPES1 ENTRY .ENDR .ENDM SCSI_DEV_TYPES .NLIST ; RCL .SBTTL SCSI device types table ;+ ; SCSI_DEVICE_TABLE ; ; This table is used to translate the product ID field from the inquiry data ; to a VMS device type and internal MK device type. The internal type code is ; used so that we can support multiple SCSI tape drives with a single VMS device ; code (generic_mk), yet distinguish between these drives, which may require ; different vendor-unique mode select data. The mode select data in this table ; is appended to the standard 4 byte parameter list header and 8 byte block ; descriptor. Several reserved entries are left in the table to allow for ; patching the driver to add support for new devices in the future. Note that ; because the TZ30 and TK50 don't conform to the standard inquiry data format, ; the GET_DEVICE_TYPE routine uses special case code to find their entries ; in the table. ;- SCSI_DEVICE_TABLE: .list meb SCSI_DEV_TYPES <- ; ; ID string VMS dev type Min Rev Density Mode select info ; --------- ------------ ------- ------- ---------------- ; <, TZ30, <0701>, BLK_833, <1, <^X4F>>>,- <, TK50, <2D05>, BLK_833, <1, <^X47>>>,- <, TLZ04, < >, DEFAULT, <>>,- <, TLZ04, < >, DEFAULT, <>>,- <, TLZ04, < >, DEFAULT, <>>,- <, TZK10, < >, DEFAULT, <>>,- <<0-STD1-0>, TSZ05, < >, PE_1600, <>>,- <, TSZ05, < >, PE_1600, <>>,- <, TSZ07, < >, GCR_6250, <>>,- <, TZK10, < >, DEFAULT, <>>> SCSI_DEV_TYPES <- ; ; ID string VMS dev type Min Rev Density Mode select info ; --------- ------------ ------- ------- ---------------- ; <, TZ85, < >, DEFAULT, <>>,- <, TZ86, < >, DEFAULT, <>>,- <, TZ87, < >, DEFAULT, <>>,- <, TLZ06, < >, DEFAULT, <>>,- <, TKZ60, < >, DEFAULT, <>>,- <, TZK11, < >, DEFAULT, <>>,- <, TKZ09, < >, DEFAULT, <>>,- <, TZ88, < >, DEFAULT, <>>,- <, TZ89, < >, DEFAULT, <>>,- <, TKZ09, < >, DEFAULT, <>>,- <, GENERIC_MK, < >, DEFAULT, <>>,- <, GENERIC_MK, < >, DEFAULT, <>>,- <, GENERIC_MK, < >, DEFAULT, <>>,- <, GENERIC_MK, < >, DEFAULT, <>>,- <, GENERIC_MK, < >, DEFAULT, <>>> SCSI_DEV_TYPES <- ; ; ID string VMS dev type Min Rev Density Mode select info ; --------- ------------ ------- ------- ---------------- ; <, TLZ06, < >, DEFAULT, <>, LOADER >,- <, TKZ60, < >, DEFAULT, <>, LOADER >,- <, TZ85, < >, DEFAULT, <>, LOADER >,- <, TZ86, < >, DEFAULT, <>, LOADER >,- <, TZ87, < >, DEFAULT, <>, LOADER >,- <, TZ87, < >, DEFAULT, <>, LOADER >,- <, TZ88, < >, DEFAULT, <>, LOADER >,- <, TZ88, < >, DEFAULT, <>, LOADER >,- <, TZ89, < >, DEFAULT, <>, LOADER >,- <, TZ89, < >, DEFAULT, <>, LOADER >,- <, TLZ07, < >, DEFAULT, <>, LOADER >> SCSI_DEV_TYPES <- ; ; ID string VMS dev type Min Rev Density Mode select info ; --------- ------------ ------- ------- ---------------- <, TZ86, < >, DEFAULT, <>, LOADER >,- <, GENERIC_MK, < >, DEFAULT, <<^X22>>>,- <, GENERIC_MK, < >, DEFAULT, <<^X22>>>,- <, TL810, < >, DEFAULT, <>>,- <, TL820, < >, DEFAULT, <>>> .list .BYTE 0 ; End of table .nlist meb .SBTTL SCSI command definition tables SCSI_CMD - NAME = TEST_UNIT_READY,- CMD_BYTES = <0, 0, 0, 0, 0, 0> SCSI_CMD - NAME = REQUEST_SENSE,- CMD_BYTES = <3, 0, 0, 0, , 0>,- DMA_LEN = -1,- DMA_DIR = READ SCSI_CMD - NAME = READ,- CMD_BYTES = <8, 0, 0, 0, 0, 0>,- DMA_LEN = -1,- TIMEOUT=420 SCSI_CMD - NAME = WRITE,- CMD_BYTES = <10, 0, 0, 0, 0, 0>,- DMA_LEN = -1,- TIMEOUT=420 SCSI_CMD - NAME = READ_8MM,- ;Special 8MM READ CMD_BYTES = <8, 2, 0, 0, 0, 0>,- DMA_LEN = 80,- TIMEOUT=420 SCSI_CMD - ;Special 8MM WRITE NAME = WRITE_8MM,- CMD_BYTES = <10, 0, 0, 0, 0, 0>,- TIMEOUT=420 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, 0, 0, 0, 40, 0>,- DMA_LEN = 40,- DMA_DIR = WRITE,- TIMEOUT = 420 SCSI_CMD - NAME = MODE_SELECT_TMP,- CMD_BYTES = <21, 0, 0, 0, 40, 0>,- DMA_LEN = 40,- DMA_DIR = WRITE,- TIMEOUT = 420 SCSI_CMD - NAME = MODE_SENSE,- CMD_BYTES = <26, 0, 0, 0, 255, 0>,- DMA_LEN = 255,- DMA_DIR = READ,- TIMEOUT = 420 SCSI_CMD - NAME = RECEIVE_DIAG,- CMD_BYTES = <28, 0, 0, 6, 0, 0>,- DMA_LEN = 6,- DMA_DIR = READ,- TIMEOUT = 420 SCSI_CMD - NAME = SEND_DIAG,- CMD_BYTES = <29, 0, 0, 0, 0, 0>,- DMA_LEN = 512,- DMA_DIR = WRITE SCSI_CMD - NAME = ERASE,- CMD_BYTES = <25, 1, 0, 0, 0, 0>,- TIMEOUT = 5*60*60 SCSI_CMD - NAME = REWIND,- CMD_BYTES = <1, 0, 0, 0, 0, 0>,- TIMEOUT=420 SCSI_CMD - NAME = UNLOAD,- CMD_BYTES = <27, 0, 0, 0, 0, 0>,- ;force unload TIMEOUT=420 SCSI_CMD - ;Create the RETENSION NAME = RETENSION,- ;packet using the SCSI CMD_BYTES = <27, 1, 0, 0, 3, 0>,- ;LOAD cmd. Byte 5 set TIMEOUT=420 ;with 3 to cause "RETEN, ;load, move to BOT". Uzd ;for RETENSIONING TZK10 ;only. MCY - 11/27/90 SCSI_CMD - NAME = SPACE,- CMD_BYTES = <17, 0, 0, 0, 0, 0>,- TIMEOUT=5*60*60 SCSI_CMD - NAME = WRITE_FILEMARKS,- CMD_BYTES = <16, 0, 0, 0, 0, 0>,- TIMEOUT=420 SCSI_CMD - NAME = READ_POSITION,- CMD_BYTES = <<^X34>, 0, 0, 0, 0, 0, 0, 0, 0, 0>,- DMA_LEN = 20,- DMA_DIR = READ,- TIMEOUT=420 ; Descriptors for mode page fields, used by MODE SENSE operations. .MACRO MODE_FIELD_DESC PAGE,NAME,BOFF,BIT_OFFSET,BIT_SIZE,FLAGS,DESIRED 'PAGE'_'NAME'_DESC = . - DESCRIPTOR_BASE ; Offset from base .LONG 'BOFF' ; Byte offset in page .LONG 'BIT_OFFSET' ; Bit offset within byte .LONG 'BIT_SIZE' ; Size of field in bits .LONG 'FLAGS' ; Flag bits .QUAD 'DESIRED' ; Desired value .BLKL 2 ; Actual value .BLKL 1 ; Difference reason .BLKL 1 ; Reserved bits .ENDM MODE_FIELD_DESC .ALIGN LONG DESCRIPTOR_BASE = . ; Descriptors for use by Mode_Sense routine VENDOR_UNIQUE_PAGE_DESC = . - DESCRIPTOR_BASE MODE_FIELD_DESC - PAGE=VENDOR_UNIQUE,- NAME=DENS,- BOFF=MODE_DESC$C_DENS_BOFF,- BIT_OFFSET=0,- BIT_SIZE=8 MODE_FIELD_DESC - PAGE=VENDOR_UNIQUE,- NAME=BDLEN,- BOFF=MODE_DESC$C_BDLEN_BOFF,- BIT_OFFSET=0,- BIT_SIZE=8 MODE_FIELD_DESC - PAGE=VENDOR_UNIQUE,- NAME=DEVSPC,- BOFF=MODE_DESC$C_DEVPAR_BOFF,- BIT_OFFSET=0,- BIT_SIZE=8,- FLAGS= ; Descriptors for use by Setup_Compaction routine DEV_CONFIG_PAGE_DESC = . - DESCRIPTOR_BASE MODE_FIELD_DESC - PAGE=DEV_CONFIG,- NAME=SEL_DATA_COMP,- BOFF=14,- BIT_OFFSET=0,- BIT_SIZE=1,- FLAGS= ; at run time MODE_FIELD_DESC - PAGE=DEV_CONFIG,- NAME=DEVSPC,- BOFF=MODE_DESC$C_DEVPAR_BOFF,- BIT_OFFSET=0,- BIT_SIZE=8,- DESIRED=SCSI_MSEL$M_BUF,- FLAGS= MODE_FIELD_DESC - PAGE=DEV_CONFIG,- NAME=DENS,- BOFF=MODE_DESC$C_DENS_BOFF,- BIT_OFFSET=0,- BIT_SIZE=8,- DESIRED=0,- FLAGS= ; Descriptors for use by Mode_Select routine VENDOR_UNIQUE_SEL_DESC = . - DESCRIPTOR_BASE MODE_FIELD_DESC - PAGE=VENDOR_UNIQUE_SEL,- NAME=PARAMS,- BOFF=0,- BIT_OFFSET=0,- BIT_SIZE=0,- FLAGS= BASIC_SELECT_DESC = . - DESCRIPTOR_BASE MODE_FIELD_DESC - PAGE=BASIC_SELECT_SEL,- NAME=DEVSPC,- BOFF=MODE_DESC$C_DEVPAR_BOFF,- BIT_OFFSET=0,- BIT_SIZE=8,- DESIRED=SCSI_MSEL$M_BUF,- FLAGS= MODE_FIELD_DESC - PAGE=BASIC_SELECT_SEL,- NAME=DENS,- BOFF=MODE_DESC$C_DENS_BOFF,- BIT_OFFSET=0,- BIT_SIZE=8,- DESIRED=0,- FLAGS= DESCRIPTOR_SIZE = . - DESCRIPTOR_BASE .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 byte of sense key followed by a word of VMS status. ; Since the sense key space is sparse, the table is sequentially scanned, rather ; than indexed by sense key value. The table is terminated with a byte if -1. ; - SENSE_KEY_TABLE: ; SCSI sense key VMS status ; -------------- ---------- SENSE_KEY NO_SENSE, NORMAL SENSE_KEY RECOVERED_ERROR, NORMAL SENSE_KEY NOT_READY, DEVOFFLINE SENSE_KEY MEDIUM_ERROR, PARITY SENSE_KEY HARDWARE_ERROR, DRVERR SENSE_KEY ILLEGAL_REQUEST, DRVERR SENSE_KEY UNIT_ATTENTION, MEDOFL SENSE_KEY DATA_PROTECT, WRITLCK SENSE_KEY BLANK_CHECK, OPINCOMPL SENSE_KEY ABORTED_COMMAND, DRVERR SENSE_KEY EQUAL, NORMAL SENSE_KEY VOLUME_OVERFLOW, DRVERR SENSE_KEY MISCOMPARE, DATACHECK .BYTE ^XFF ; End of sense key table .SBTTL SCSI error length table ;+ ; SCSI_ERROR_LEN_TAB ; ; This table is indexed by the SCSI error type and specifies the length of ; the errorlog packet. ;- SCSI_ERROR_LEN_TAB: SCSI_ERROR_CODES <- ,- ,- ,- ,- ,- ,- ,- > .SBTTL + .SBTTL + DRIVER ENTRY POINTS .SBTTL + .SBTTL MK_CTRL_INIT - Controller initialization routine ;+ ; MK_CTRL_INIT ; ; This routine is called to perform controller-specific initialization and ; is called by the operating system in three places: ; ; - at system startup ; - during driver loading and reloading ; - during recovery from a power failure ; ; Currently this routine simply creates a fork thread to setup a trace buffer. ; ; INPUTS: ; ; R4 - address of the CSR (controller status register) ; R5 - address of the IDB (interrupt data block) ; R6 - address of the DDB (device data block) ; R8 - address of the CRB (channel request block) ; ; OUTPUTS: ; ; All registers preserved ;- MK_CTRL_INIT: .IF DEFINED DEBUG PUSHL R5 ; Save R5 PUSHAL 10$ ; Set up dummy return address MOVL R8,R5 ; Copy CRB address FORK ; Fork using the CRB fork block BSBW SETUP_TRACE ; Set up trace buffer RSB 10$: POPL R5 ; Restore R5 .ENDC RSB ; Return to caller (SYSGEN) .SBTTL MK_UNIT_INIT - Unit initialization routine ;+ ; MK_UNIT_INIT ; ; This routine performs unit-specific initialization and is called for each ; tape found on the SCSI bus. A connection to the port driver is established, ; which lasts for the life of the system. All traffic to this SCSI device is ; directed over this connection. ; ; An inquiry command is sent to the target to determine the device type. ; In 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. ; ; INPUTS: ; ; R5 - UCB address ; ; OUTPUTS: ; ; R0-R3 - Destroyed ; All other registers preserved ;- MK_UNIT_INIT: ; Initialize unit BBC #UCB$V_POWER,- ; Branch if powerfail has not been UCB$W_STS(R5),2$ ; NOT been detected RSB ; Otherwise, exit immediately ; Fork twice for now to allow the port driver's unit init routine to execute ; before ours. 2$: FORK ; Fork to drop IPL to SYNCH FORK ; Fork to drop IPL to SYNCH ASSUME UCB$V_DISCONNECT LT 8 MOVB #UCB$M_DISCONNECT!- ; By default, assume the target device UCB$M_SYNCHRONOUS,- ; is capable of both disconnecting and UCB$L_MK_FLAGS(R5) ; synchronous operation BISW #UCB$M_ONLINE!- ; Set unit online and busy to allow I/O UCB$M_BSY,- ; to be queued UCB$W_STS(R5) ; MOVAL UCB$L_FLUSH_IOQFL(R5),R0; Initialize the queue used to flush MOVL R0,(R0) ; I/Os which are queued during unit MOVL R0,4(R0) ; init when unit init fails CLRL UCB$L_REWIND_TIME(R5) ; Indicate no rewind command has been ; recently CLRL UCB$L_UNLOAD_TIME(R5) ; Indicate no unload has been issued ; recently .IF DEFINED DEBUG CLRL R3 ; Prepare to call TRACE_QIO BSBW TRACE_QIO ; Trace this QIO (special case when ; called from UNIT INIT, R3 must be 0) .ENDC MOVL #SCDRP$C_LENGTH,R1 ; Length of SCDRP packet JSB G^EXE$ALONONPAGED ; Allocate a block BLBS R0,7$ ; Branch if success BUG_CHECK INCONSTATE,FATAL ; Otherwise, bugcheck 7$: PUSHR #^M ; Save regs MOVC5 #0,(SP),#0,R1,(R2) ; Initialize the packet POPR #^M ; Restore regs MOVL R2, UCB$L_STACK_SCDRP(R5) ; Save address of SCDRP INIT_SCDRP_STACK SCDRP=R2 ; Initialize the internal stack in the SCDRP MOVAL UCB$L_SCDRPQ_FL(R5),R0 ; Initialize the SCDRP queue header MOVL R0,(R0) ; in the UCB MOVL R0,4(R0) ; MOVL #SCDRPS_PER_UNIT,R4 ; Number of SCDRPs to allocate per unit 10$: MOVL #SCDRP$C_LENGTH,R1 ; Length of SCDRP MOVL R5,R3 ; Copy UCB address ALLOC_STACK_SCDRP ; Get the SCDRP for a STACK BSBW ALLOC_POOL ; Go allocate an SCDRP FREE_STACK_SCDRP ; Give it back MOVL R3,R5 ; Copy UCB address MOVW R1,SCDRP$W_SCDRPSIZE(R2); Save length of SCDRP INSQUE SCDRP$L_FQFL(R2),- ; Place SCDRP in UCB queue UCB$L_SCDRPQ_FL(R5) ; SOBGTR R4,10$ ; Repeat for all SCDRPs ; All SCSI tape unit numbers should be of the form "n0m" where n is the SCSI ; ID between 0 and 7 and m is the LUN between 0 and 7. Extract the ID from the ; LUN by dividing the unit number by 100. The quotient is the used as the ID ; while the remainder is the LUN. Note that the unit number contains three ; digits because early version of SCSI provided for sub-logical unit numbers. ; This feature has since been removed and the second digit in the unit number ; is not used. MOVL #SS$_BADPARAM,R0 ; Assume bad LUN or SUBLUN specified MOVZWL UCB$W_UNIT(R5),R1 ; Get device unit number CLRL R2 ; Prepare for extended divide EDIV #100,R1,R1,R2 ; Extract SCSI bus ID from LUN CMPL R1,#7 ; Valid SCSI ID (0 <= n <= 7)? BGTRUW 20$ ; Branch if not CMPL R2,#7 ; Valid LUN (0 <= n <= 7)? BGTRUW 20$ ; Branch if not MULB3 #<1@5>,R2,UCB$B_LUN(R5) ; Save LUN (shifted left 5 bits for use ; later in SETUP_CMD) ASHL #16,R1,R1 ; Place SCSI ID in high-order word of R1 ASHL #16,R2,R2 ; Place LUN in high-order word of R2 MOVL UCB$L_DDB(R5),R0 ; Get DDB address SUBB3 #^A'A',- ; Translate controller letter to DDB$T_NAME+3(R0),R1 ; SCSI bus ID. SPI$CONNECT ; Connect to the port driver BLBC R0,20$ ; Branch if connect attempt failed CMPL R1,UCB$L_MAXBCNT(R5) ; For MAXBCNT, use minimum supported BGEQ 15$ ; value of port and class drivers MOVL R1,UCB$L_MAXBCNT(R5) ; Save maximum byte count in UCB 15$: MOVL R2,UCB$L_SCDT(R5) ; Save SCDT address MOVL R4,UCB$L_PDT(R5) ; Save PDT address BSBW ALLOC_REQSNS_RESOURCE ; Get REQUEST SENSE Resource BLBC R0,20$ ; Branch if error BSBW SET_UNIT_ONLINE ; Go bring the unit online RSB ; Return to caller ; Connection failure. Log an error and set the unit offline. 20$: LOG_ERROR - ; Log a connection error TYPE=CONNECTION_ERROR,- ; VMS_STATUS=R0,- ; UCB=R5 ; BICW #UCB$M_ONLINE!- ; Set the unit offline and not busy UCB$M_BSY,- ; UCB$W_STS(R5) ; RSB .SBTTL ALLOC_REQSNS_RESOURCE ;+ ; This routine will allocate an SCDRP and call SPI$MAP_BUFFER to get ; a DMA buffer for the REQUEST SENSE command. This is done up front ; to prevent deadlocks when using tape devices and 1 or more data transfer ; requests have all the DMA resources allocated and a check_condition ; occurs on all connections. When this happens, the request_sense routine ; hangs waiting for DMA resources which will not be freed until the ; REQUEST_SENSE completes and allows the original command's to free their ; buffer resources! This situation occurs frequently when doing a DUMP ; of more than 1 tape simultaneously and the record lengths used by DUMP ; do not match the record length on tape, which generates an ILI check ; condition. ; ; REQUEST_SENSE will use this pre-allocated resource instead of trying ; to get a resource on the fly. We allocate one per UCB (unit) with ; the assumption that only one command can be outstanding on a connection ; at once, and so only one REQUEST_SENSE command can happen at a time ; per UCB. This will break with command queuing. ;- ALLOC_REQSNS_RESOURCE: TSTL UCB$L_REQSNS_PAGE(R5) ; We already have a page? BNEQ 10$ ; Branch if yes PUSHR #^M ; Save registers MOVL R5,R3 ; Copy UCB address BSBW ALLOC_SCDRP ; Get a fake SCDRP to interface ; with SPI_MAP_BUFFER MOVL UCB$L_SCDRP(R3),- ; Save our SCDRP address in special UCB$L_REQSNS_PAGE(R3) ; UCB location CLRL UCB$L_SCDRP(R3) ; Not in use here! MOVL UCB$L_REQSNS_PAGE(R3),R5; Pick up our SCDRP's address MOVL #REQSNS_SIZE,R1 ; Size of DMA buffer needed for data BSBW ALLOC_POOL ; Allocate a buffer to receive response MOVL R2,SCDRP$L_SVA_USER(R5) ; Save address of allocated buffer MOVL #REQSNS_SIZE,SCDRP$L_BCNT(R5) ; Set DMA request size CLRL SCDRP$L_PAD_BCNT(R5) ; No padding required BICW3 #^C<^X1FF>,R2,- ; And byte offset within page SCDRP$W_BOFF(R5) ; BISL #IRP$M_FUNC,- ; Set FUNC bit to indicate READ SCDRP$W_STS(R5) ; function PUSHL R3 ; Save R3 JSB G^MMG$SVAPTECHK ; Get SVAPTE of allocated system buffer MOVL R3,SCDRP$L_SVAPTE(R5) ; Save SVAPTE in SCDRP POPL R3 ; Restore R3 BISB #SCDRP$M_S0BUF!- ; This buffer is an S0 "user" buffer SCDRP$M_BUFFER_MAPPED,-; and it has been mapped SCDRP$L_SCSI_FLAGS(R5) ; SPI$MAP_BUFFER PRIO=HIGH ; Map the "user" buffer for read access POPR #^M ; Restore registers 10$: RSB .SBTTL MK_SENSEMODE ;+ ; MK_SENSEMODE - SENSE DEVICE CHARACTERISTICS AND MODE ; ; This routine gets the device characteristics and returns them ; in the IOSB and optionally in a user specified buffer. ; If the user specified buffer is of the correct length, extended ; device characteristics will also be returned. ; ; If P1 is not specified, then IRP$L_SVAPTE in the I/O packet is ; cleared and only the device-dependent information will be returned ; in the second longword of the IOSB when the packet is queued via ; EXE$QIODRVPKT. ; ; If P1 is specified, then this routine allocates a system buffer ; and builds a buffer header using the user buffer address in P1 ; and the length as specified by P2. ; ; If P2 is not specified or is specified as a value other than ; 12, then a user buffer length of 8 bytes is assumed. The device ; class, type and buffer size will be returned in the user buffer ; along with the device-dependent information. ; ; If P2 is specified as 12, then the device class, type, buffer ; size, device-dependent information and extended device ; characteristics will be returned in the user buffer. ; ; INPUTS: ; ; R3 - I/O packet address ; R4 - Current pcb ; R5 - UCB address ; R6 - Assigned CCB address ; AP - Address of the QIO argumemt P1 ; ; OUTPUTS: ; ; R0 - Status of the operation ; R1,R2 - Destroyed ; All other registers preserved ; ; COMPLETION CODES: ; ; SS$_NORMAL - Successful ; SS$_ACCVIO - Buffer access violation ;- MK_SENSEMODE: CLRL IRP$L_SVAPTE(R3) ; Assume no user supplied buffer. MOVL P1(AP), R0 ; Get buffer address. BEQL 20$ ; Branch if none supplied. MOVZWL #8, R1 ; Assume buffer length of 8 bytes. CMPL #12, P2(AP) ; Length of 12 specified? BNEQ 10$ ; Branch if not. MOVZWL #12, R1 ; Set length in R1. 10$: JSB G^EXE$READCHK ; Check buffer for write access. ADDL #12, R1 ; Allow 12 bytes for system overhead. PUSHL R3 ; Save IRP address. JSB G^EXE$DEBIT_BYTCNT_ALO POPL R3 ; Restore IRP address. BLBC R0,99$ ; Branch on error (SS$_INSFMEM). MOVL R2, IRP$L_SVAPTE(R3) ; Store address of system buffer. ADDL3 #12, R2,- ; Store starting address of system @IRP$L_SVAPTE(R3) ; buffer data area in buffer header. MOVL P1(AP), 4(R2) ; Store user buffer address in header. MOVW R1, IRP$W_BOFF(R3) ; Store total size of system buffer. 20$: JMP G^EXE$QIODRVPKT ; Queue the packet. 90$: POPL R3 ; Clear IRP address off stack. 99$: JMP G^EXE$ABORTIO ; Abort I/O request. .SBTTL MK_SETMODE - Set mode FDT entry point ;+ ; MK_SETMODE ; ; Set a mode bit, or clear it, to turn on the skip by filemarks algorithm or ; turn it off. If io$_setchar with IO$M_ALLOWFAST_ALWAYS, turn on new algorithm ; for all IO$_SKIPFILE calls. If modifier is IO$_ALLOWFAST_PER_IO, turn on new ; algorithm for IO$_SKIPFILE calls with the IO$M_ALLOWFAST modifier. If ; modifier is IO$M_ALLOWFAST_NEVER instead, turn it off for all cases. Return success ; always. If there are none of these modifiers, just do normal SETCHAR. ; ; The following table describes state uses: ; UCB$L_MK_FLAGS QIO QIO ; ALLOWFAST_ ALLOWFAST_ UCB$M_SKIPFILE_ IO$_SKIPFILE IO$_ ; PER_IO ALWAYS SUPPORTED SKIPFILE ; and ; IO$M_ ; ALLOWFAST ;------------------------------------------------------------------------------ ;MKSET/NEVER 0 0 1 record record ;illegal 0 1 1 X X ;MKSET/PER_IO* 1 0 1 record filemark ;MKSET/ALWAYS 1 1 1 filemark filemark ; X X 0** record record ; ;* Default after autoconfig. ;** The default is "supported". May become unsupported at unit init. (as a ; result of an explicit table entry, or at PACKACK (as a result of failing ; the Read Position test). ; ; ; Inputs: ; R6 = CCB Channel control block address ; R5 = UCB Unit control block address ; R4 = PCB Process Control Block address ; R3 = IRP I/O Request Packet address ; ; Output: ; R0 = status ;- MK_SETMODE: BITW #,- ; IRP$W_FUNC(R3) ; BNEQ 10$ ; Banch if modifiers are found ; This is the normal setchar, so pass it to the normal routine. ; RSB ; Let the normal entry do it ; ; Test for ALLOWFAST_NEVER modifier and clear the UCB bits for ALLOWFAST_ALWAYS ; and ALLOWFAST_PER_IO if modifier is found. ; 10$: ; Save UCB flags for debug if needed. This stores the state of the flags ; BEFORE we alter the skipfile information, to permit diagnosis and possible ; restoration should we want to use the ability. This R1 value will be returned ; to the user at 20$ below. MOVL UCB$L_MK_FLAGS(R5),R1 ; Now get the forklock to be certain no other CPU can collide with our ; mods to this longword. FORKLOCK PRESERVE=YES BBC #IO$V_ALLOWFAST_NEVER,- IRP$W_FUNC(R3),15$ ; Skip if not disabling fast skip BICL #,- UCB$L_MK_FLAGS(R5) ; Turn off all fast skipfile ; functions BRW 20$ ; ; Test for ALLOWFAST_PER_IO modifier and clear the UCB bit for ALLOWFAST_ALWAYS ; and set the UCB bit for ALLOWFAST_PER_IO if modifier is found. ; 15$: BBC #IO$V_ALLOWFAST_PER_IO,- IRP$W_FUNC(R3),17$ ; Skip if not setting fast skip for ; per-IO (with fcn mod) only BICL #UCB$M_ALLOWFAST_ALWAYS,- UCB$L_MK_FLAGS(R5) ; Turn off fast skipfile by default BISL #UCB$M_ALLOWFAST_PER_IO,- UCB$L_MK_FLAGS(R5) ; Turn on fast skipfile functions for ; use with IO$_SKIPFILE modifier BRW 20$ ; ; Test for ALLOWFAST_ALWAYS modifier and set the UCB bits for ALLOWFAST_ALWAYS ; and ALLOWFAST_PER_IO if modifier is found. ; 17$: BBC #IO$V_ALLOWFAST_ALWAYS,- IRP$W_FUNC(R3),20$ ; Skip if not setting for use of fast skip BISL #,- UCB$L_MK_FLAGS(R5) ; Turn on all fast skipfile functions ; BRW 20$ ; (common exit. Omit if it follows. ; ; Now we're done. Declare victory and exit. ; 20$: ; Now unlock from the forklock above at 10$ and be sure R1 is still left ; alone as the previous value of UCB$L_MK_FLAGS. (This means that it is ; of some value in debug or development in restoring initial conditions.) ; Use "condition=restore" so we only give the lock up if we were the only ; holder, so as not to interfere with anyone else. FORKUNLOCK CONDITION=RESTORE,PRESERVE=YES MOVL #SS$_NORMAL,R0 ; Set success status JMP G^EXE$FINISHIO ; and return .SBTTL MK_STARTIO - Driver QIO entry point ;+ ; MK_STARTIO ; ; This routine is the QIO entry point into the driver. It's main function ; is to dispatch to the function-code-specific routine which then executes ; the QIO. ; ; INPUTS: ; ; R3 - IRP address ; R5 - UCB address ; ; OUTPUTS: ; ; R0 - 1st longword of I/O status: contains status code and ; number of bytes transferred ; R1 - 2nd longword of I/O status: contains a copy of the ; UCB$L_DEVDEPEND field ; R2-R4 - Destroyed ; All other registers preserved ;- MK_STARTIO: .IF DEFINED DEBUG BSBW TRACE_QIO ; Trace the current I/O request .ENDC MOVL UCB$L_PDT(R5),R4 ; Get PDT address MOVL R3,R2 ; Copy IRP address MOVL R5,R3 ; Copy UCB address BSBW ALLOC_SCDRP ; Allocate an SCDRP MOVL R2,SCDRP$L_IRP(R5) ; Save IRP address in SCDRP 1$: MOVW IRP$W_FUNC(R2),- ; Copy function code and modifiers SCDRP$W_FUNC(R5) ; from the IRP to the SCDRP MOVL IRP$L_MEDIA(R2),- ; and media field, SCDRP$L_MEDIA(R5) ; MOVL IRP$L_SVAPTE(R2),- ; SVAPE, SCDRP$L_SVAPTE(R5) ; MOVL IRP$L_BCNT(R2),- ; BCNT, SCDRP$L_BCNT(R5) ; MOVW IRP$W_BOFF(R2),- ; and BOFF SCDRP$W_BOFF(R5) ; ; Fix a synchronization bug in the I/O subsystem which allows virtual functions ; to sneak through FDT processing and be queued to the driver even though ; mapping is disable. BBC #IRP$V_VIRTUAL,- ; Branch if not a virtual I/O function IRP$W_STS(R2),5$ ; MOVL IRP$L_WIND(R2),R0 ; Get window block TSTW WCB$W_NMAP(R0) ; Mapping enabled? BNEQ 5$ ; Branch if so BSBW DEALLOC_SCDRP ; Deallocate the SCDRP MOVL R3,R5 ; Copy UCB address MOVL UCB$L_VCB(R5),R1 ; Get address of VCB listhead INSQUE (R2),@4(R1) ; Insert entry in blocked I/O list REMQUE @UCB$L_IOQFL(R5),R3 ; Remove I/O packet from device unit queue BVS 3$ ; Branch if no I/O queued JMP G^IOC$INITIATE ; Go initiate next function 3$: BICW #UCB$M_BSY,UCB$W_STS(R5); Clear unit busy JMP G^IOC$RELCHAN ; Release the channel 5$: ; ; ; Allow only physical I/O functions before a PACKACK is issued. ; BBS #IRP$V_PHYSIO,- ; Branch if physical I/O function IRP$W_STS(R2),10$ BBC #UCB$V_VALID,- ; Logical or virtual I/O function UCB$W_STS(R3),- ; Branch if volume is invalid VOLUME_INVALID ; If the last command was a rewind/nowait, the rewind might still be in ; progress. If so, wait for the rewind to complete before executing this ; QIO function. 10$: BBCC #UCB$V_REWIND_INPROG,- ; Branch if last command was not a UCB$L_MK_FLAGS(R3),11$ ; rewind/immediate BSBW WAIT_UNIT_READY ; Otherwise, wait for rewind to complete BLBC R0,COMPLETE_IO ; Branch on error MOVL UCB$L_IRP(R3),R2 ; Restore IRP address BRW 1$ ; Now go execute original command 11$: EXTZV #IRP$V_FCODE,- ; Extract I/O function code #IRP$S_FCODE,- ; IRP$W_FUNC(R2),R1 ; ASSUME IRP$S_FCODE LE 7 ; Allow byte mode dispatch DISPATCH R1,TYPE=B,<- ; Dispatch according to function ,- ; ^X00 ,- ; ^X01 ,- ; ^x06 ,- ; ^X08 ,- ; ^X0B ,- ; ^X0C ,- ; ^X11 ,- ; ^X15 ,- ; ^X1B ,- ; ^X1C ,- ; ^X1D ,- ; ^X20 ,- ; ^X21 ,- ; ^X22 ,- ; ^X23 ,- ; ^X24 ,- ; ^X25 ,- ; ^X26 ,- ; ^X27 ,- ; ^X28 > ; Bogus I/O function code if we fall through. Set illegal function code ; status and complete the I/O. IO_BOGUS: MOVZBL #SS$_ILLIOFUNC,R0 ; Specify the error type BRB COMPLETE_IO IO_NOP: MOVZWL #SS$_NORMAL,R0 ; Set success status BRB COMPLETE_IO ; Complete the I/O VOLUME_INVALID: MOVZWL #SS$_VOLINV,R0 ; It's not a valid volume ; BRB COMPLETE_IO ; Fall through to complete the I/O COMPLETE_IO: BSBW DEALLOC_SCDRP ; Deallocate the SCDRP MOVL R3,R5 ; Copy UCB address ; If the CANCEL bit is set in the UCB, then the I/O has been canceled and ; REQCOM has already been called for this IRP. The SCSI command, however, is ; allowed to complete (since there's no clean way of aborting a command to ; the tape). In the meantime, the busy bit in the UCB remains set in order to ; prevent any new I/O from being started. Now that the command for the canceled ; I/O has completed, see if any I/O is pending. If so, initiate it. BBCC #UCB$V_CANCEL,- ; Branch if I/O has not been canceled UCB$W_STS(R5),1$ ; REMQUE @UCB$L_IOQFL(R5),R3 ; Any I/O queued to this device? BVS 0$ ; Branch if not JMP G^IOC$INITIATE ; Go initiate the I/O 0$: BICW #UCB$M_BSY,- ; Clear busy bit to allow future I/O UCB$W_STS(R5) ; to be initiated RSB ; Return to caller 1$: .IF DEFINED DEBUG BSBW TRACE_QIO_STAT ; Save the final QIO status in trace buf BLBS R0,5$ ; Branch on success status NOP ; Instruction to trap on QIO with bad status 5$: .ENDC ; The following code segment checks for an error on a virtual I/O function. In ; this case, all virtual I/O packets in the pending queue are removed and placed ; on the blocked I/O list of the volume control block. This allows the magtape ; ACP to perform any operations necessary to recover from the error. It then ; requeues all the I/O from the VCB to the UCB. One specific case in which this ; action is necessary is in encountering a tape mark at the end of a volume. ; The ACP is responsible for the activity necessary to switch to a new volume. PUSHL R0 ; Save final status and count JSB G^IOC$DIAGBUFILL ; Fill diagnostic buffer if present BLBS (SP),20$ ; Branch if successful completion MOVL UCB$L_IRP(R5),R4 ; Get current IRP BBC #IRP$V_VIRTUAL,- ; Branch if not a virtual I/O function IRP$W_STS(R4),20$ ; ; Temporarily prevent mount verification if a virtual I/O function fails ; with a status that would normally kick in mount verification. CMPW (SP),#SS$_VOLINV ; Volume invalid status? BEQL 7$ ; Branch if so CMPW (SP),#SS$_MEDOFL ; Medium offline status? BNEQ 8$ ; Branch if not 7$: MOVL #SS$_DEVOFFLINE,(SP) ; Return device offline status instead BRB 20$ ; Don't queue I/Os to the blocked queue 8$: MOVL IRP$L_WIND(R4),R4 ; Get window block CLRW WCB$W_NMAP(R4) ; Clear number of mapping pointers MOVL UCB$L_VCB(R5),R4 ; Get address of VCB listhead MOVAB UCB$L_IOQFL(R5),R2 ; Get address of I/O queue listhead MOVL R2,R3 ; Save listhead address 10$: MOVL (R3),R3 ; Get next entry in list CMPL R3,R2 ; End of list? BEQL 20$ ; Branch if so BBC #IRP$V_VIRTUAL,- ; Branch if not a virtual function IRP$W_STS(R3),10$ ; MOVL 4(R3),R3 ; Retrieve address of previous entry REMQUE @(R3),R1 ; Remove entry from Driver queue INSQUE (R1),@4(R4) ; Insert entry in blocked I/O list BRB 10$ ; Repeat for all IPRs in list 20$: POPL R0 ; Retrieve final I/O status MOVL UCB$L_DEVDEPEND(R5),R1 ; Set magtape status and characteristics REQCOM ; Complete I/O request .SBTTL MK_CANCEL - Cancel I/O routine ;+ ; MK_CANCEL ; ; This routine cancels an I/O in progress. Since there is no clean way to abort ; a command in progress to the tape, pass the active IRP to REQCOM, but keep ; the BSY bit set in the UCB to prevent any new I/O from being initiated. This ; is accomplished by temporarily emptying the pending I/O queue and calling ; REQCOM with the active IRP. REQCOM sees the pending queue empty and clears ; the BSY bit. When control returns here, the pending queue is restored and ; the BSY bit is set. The CANCEL bit in the UCB is set to indicate that the I/O ; has been canceled. All this activity is synchronized using the fork lock. ; ; Eventually, the SCSI command for the canceled I/O will complete. Just before ; that thread calls REQCOM, it checks the BSY bit. If it finds it set, it ; avoids calling REQCOM. Instead, it attempts to initiate any I/O in the ; pending queue, or clears the BSY bit if no I/O is pending. ; ; INPUTS: ; ; R2 - Channel index number ; R3 - IRP address ; R4 - PCB address of procedd canceling I/O ; R5 - UCB address ; R8 - Cancel reason code, one of: ; CAN$C_CANCEL if called through $CANCEL or ; $DALLOC system service ; CAN$C_DASSGN if called through $DASSGN system ; service ; OUTPUTS: ; ; R0-R3 - Destroyed ; All other registers preserved ;- MK_CANCEL: BBS #UCB$V_CANCEL,- ; Don't cancel the same IRP twice UCB$W_STS(R5),20$ ; TSTL R3 ; IRP address of 0? (this can happen ; during UNIT INIT in bringing the ; device online) BEQL 20$ ; Branch if not JSB G^IOC$CANCELIO ; Set cancel bit if appropriate. BBC #UCB$V_CANCEL,- ; If the cancel bit is not set, UCB$W_STS(R5),20$ ; just return. EXTZV #IRP$V_FCODE,- ; Extract I/O function code #IRP$S_FCODE,- ; IRP$W_FUNC(R3),R0 ; CMPB #IO$_READPBLK,R0 ; Read in progress? BEQL 10$ ; Branch if so, can't cancel CMPB #IO$_WRITEPBLK,R0 ; Write in progress? BEQL 10$ ; Branch if so, can't cancel MOVAL UCB$L_IOQFL(R5),R0 ; Get I/O pending queue listhead address MOVAL UCB$L_FLUSH_IOQFL(R5),R1; And flush I/O queue address MOVQ (R0),(R1) ; Temporarily save I/O queue contents MOVL R0,(R0) ; Initialize pending queue MOVL R0,4(R0) ; PUSHQ R0 ; Save regs MOVL #SS$_ABORT,R0 ; Set abort status MOVL UCB$L_DEVDEPEND(R5),R1 ; Set magtape status and characteristics .IF DEFINED DEBUG BSBW TRACE_QIO_STAT ; Save the final QIO status in trace buf .ENDC JSB G^IOC$REQCOM ; Complete the current I/O but do not ; start a new one CLRL UCB$L_IRP(R5) ; I/O packet no longer active CLRB UCB$B_8MM_CHK(R5) ; Clear 8mm COPY check flag BISW #UCB$M_BSY,- ; Set the busy bit to prevent any new UCB$W_STS(R5) ; I/O from being initiated POPQ R0 ; Restore regs MOVQ (R1),(R0) ; Restore pending I/O queue MOVL R1,(R1) ; Reinitialize flush I/O queue MOVL R1,4(R1) ; RSB ; Return to caller 10$: BICB #UCB$M_CANCEL,- ; Indicate cancel is not in progress UCB$W_STS(R5) ; 20$: RSB ; Return to caller .SBTTL MK_REG_DUMP - Device register dump routine ;+ ; MK_REG_DUMP ; ; This routine dumps device-specific infromation into an errorlog packet. ; The format of this information is as follows: ; ; +-----------------------+ ; | Longword count | 4 bytes ; +-----------------------+ ; | Revision | 1 byte ; +-----------------------+ ; | HW revision | 4 bytes ; +-----------------------+ ; | Error Type | 1 byte ; +-----------------------+ ; | SCSI ID | 1 byte ; +-----------------------+ ; | SCSI LUN | 1 byte ; +-----------------------+ ; | SCSI SUBLUN | 1 byte ; +-----------------------+ ; | Port status | 4 bytes ; +-----------------------+ ; | SCSI CMD | n bytes ; +-----------------------+ ; | SCSI STS | 1 byte ; +-----------------------+ ; | | ; | Additional Data | n bytes ; | | ; +-----------------------+ ; ; The contents of the addition data field depends upon the error type. For ; example, extended sense errors save the extended sense data returned by the ; target in this field. ; ; Inputs: ; ; R0 - Output buffer address ; R5 - UCB address ; ; Outputs: ; ; R1-R3 - Destroyed ; All other registers perserved ;- MK_REG_DUMP: PUSHAL (R0)+ ; Save start of data area MOVB #MK_ERROR_REVISION,(R0)+; Save revision level MOVL UCB$L_HW_REV(R5),(R0)+ ; Save hardware revision level MOVB R7,(R0)+ ; Save error type 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 R8,(R0)+ ; Save port status code MOVW #^XFF00,(R0)+ ; Assume no SCSI CMD,STS available CLRB (R0)+ ; Assume no additional data MOVL UCB$L_SCDRP(R5),R1 ; Get active SCDRP address BEQL 50$ ; Branch if none active MOVL SCDRP$L_CMD_PTR(R1),R2 ; Get address of SCSI command BEQL 50$ ; Branch if none active MOVL (R2)+,R3 ; Get number of SCSI command bytes CMPL #^X10,R3 ; Test for bogus command BGTR 9$ ; branch if length < 16 BRW 50$ ; Otherwise, don't log command 9$: SUBL #3,R0 ; Back up pointer 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 DISPATCH R7,TYPE=B,<- ; Dispatch according to error code ,- ,- ,- ,- ,- ,- > BUG_CHECK INCONSTATE,FATAL ; Unknown error type. This should ; never happen. ; Error types which have additional data come here. 30$: MOVL SCDRP$L_SVA_USER(R1),R2 ; Get address of additional data BEQL 50$ MOVZBL SCDRP$L_TRANS_CNT(R1),R1; Get length of additional data BEQL 50$ ; Branch if none available ; When we invoke a LOG_ERROR macro, ERL_STD$DEVICERR or ERL_STD$DEVICEATTN ; is invoked to do the following: ; ; 1) Allocate an error log buffer entry; to do this it adds EMB$K_LENGTH ; bytes to the size in DDT$W_ERRORBUF and allocates that much space ; in the current error log buffer; the EMB$K_LENGTH bytes are for a ; header used to manage buffer entries. ; ; 2) Store the address of the error log buffer entry in UCB$L_EMB. The ; base address of the buffer is considered to be the address of the ; first byte after the EMB$K_LENGTH byte header - which is why the ; symbols defined in $EMBHDDEF used to reference the header fields ; are negative. ; ; 3) Load standard, device-independent information into the beginning ; of the buffer; this section is fixed at EMB$L_DV_REGSAV bytes. ; ; 4) Call a device-specific register dump routine passing a buffer ; address of UCB$L_EMB+EMB$L_DV_REGSAV; this is where the register ; dump routine can start writing device and error specific data. ; ; This register dump routine writes two sections of data into the buffer. ; ; The first is device specific but error independent; it will be written ; for every type of error logged by this routine, and it's format is ; described in this routine's header. It's size is not quite fixed, as ; the size of the SCSI command can make it vary, but there's really no ; risk of running off the end of the buffer writing it. ; ; The second is error specific, and it's size will vary with the type of ; error being logged; depending on the amount of error specific data ; we're told is available, it is possible for this section to run off the ; end of the buffer, which could corrupt the header of the next error log ; buffer. For that reason, some special steps are take here to make sure ; that it doesn't happen. ; ; * UCB$L_EMB contains the address of a DDT$W_ERRORBUF byte buffer ; ; * On entry to this routine, a total of DDT$W_ERRORBUF-EMB$L_DV_REGSAV ; bytes are available to be written ; ; * R0 always contains the address of the next byte to be written ; ; The following code calculates the amount of space left in the buffer and ; makes sure that no matter how many bytes of error specific data we have ; available, we don't write past the end of the buffer. MOVL UCB$L_DDT(R5),R3 ; Get the DDT address MOVZWL DDT$W_ERRORBUF(R3),R3 ; Get the buffer size ADDL2 UCB$L_EMB(R5),R3 ; Address after buffer SUBL2 R0,R3 ; Convert to bytes-left CMPL R1,R3 ; Enough room available? BLEQ 35$ ; Branch if so MOVL R3,R1 ; Use all of what's left 35$: MOVB R1,-1(R0) ; Save additional data length 40$: MOVB (R2)+,(R0)+ ; Save a byte of extended sense data SOBGTR R1,40$ ; Repeat for all extended sense data 50$: SUBL (SP),R0 ; Calculate number of bytes saved DECL R0 ; Round up to next longword (+3), but ; don't count LW count field itelf in ; LW count (-4) ASHL #-2,R0,@(SP)+ ; Save in LW count field at top of buffer RSB ; Return .SBTTL MK_DIAGNOSE - FDT preprocessing for Special pass-through function ;+ ; MK_DIAGNOSE ; ; This routine performs FDT preprocessing including: ; ; - Validating access to the descriptor buffer ; - Validating access to, and locking, the read/write buffer ; - Copying the SCSI command to a buffer in non-paged pool ; ; INPUTS: ; ; R0 - Address of FDT routine ; R3 - IRP address ; R4 - PCB address ; R5 - UCB address ; R6 - CCB address ; R7 - Bit number of user-specified I/O function code ; R8 - Address of current entry in FDT ; AP - Address of first function-dependent argument (P1) ; ; OUTPUTS: ; ;- DSC_OPCODE = 0 DSC_FLAGS = 4 DSC_CMDADR = 8 DSC_CMDLEN = 12 DSC_DATADR = 16 DSC_DATLEN = 20 DSC_PADCNT = 24 DSC_PHSTMO = 28 DSC_DSCTMO = 32 MK_DIAGNOSE: MOVL R2,-(SP) ; Save R2 MOVL UCB$L_ORB(R5),R2 ; Get orb address CLRQ -(SP) ; clear terminator and retlen MOVL ORB$L_NAME_POINTER(R2),-(SP) ; bufadr for priv audit MOVZWL ORB$W_NAME_LENGTH(R2),R2 ; ADDL3 #,R2,-(SP) ; itmcod + bufsiz MOVL SP,R2 ; start of item list AUDIT_S_ITMLST = 16 ; size of item list $IFPRIV DIAGNOSE, - ; Branch if process has diagnose priv 10$, - MSG=DIAGNOSE_7, - ITMLST=R2, - PRESERVE=YES ADDL #AUDIT_S_ITMLST,SP ; Clear stack by removing priv audit itemlist MOVL (SP)+,R2 ; Restore R2 MOVL #SS$_NOPRIV,R0 ; Set no privilege status BRW 50$ ; Branch to abort the I/O ; First, check that we have read access to the user's descriptor. 10$: ADDL #AUDIT_S_ITMLST,SP ; Clear stack by removing priv audit itemlist MOVL (SP)+,R2 ; Restore R2 MOVQ (AP),R0 ; Get user descriptor address, length MOVL R0,R9 ; Save a copy of descriptor address CMPL R1,#60 ; Valid descriptor length BLSSW 40$ ; Branch if not JSB G^EXE$WRITECHK ; Check for read access to the descriprot ; buffer (don't return if no access) CMPL DSC_OPCODE(R9),#1 ; Valid opcode? BNEQW 40$ ; Branch if not CMPL DSC_DATLEN(R9),- ; Reasonable read/write data buffer UCB$L_MAXBCNT(R5) ; length? BGTRUW 40$ ; Branch if not CMPL DSC_PADCNT(R9),#511 ; Reasonable pad count? BGTRU 40$ ; Branch if not MOVQ DSC_CMDADR(R9),R0 ; Get SCSI command buffer address, len CMPL R1,#248 ; Valid command length? BGTRU 40$ ; Branch if not JSB G^EXE$WRITECHK ; Check for read access to the command ; buffer (don't return if no access) ADDL #8,R1 ; Reserve space for command buf overhead JSB G^EXE$ALONONPAGED ; Allocate a buffer in which to copy ; the SCSI command BLBC R0,50$ ; Branch on error MOVL R1,(R2)+ ; Save length of buffer MOVL R2,IRP$L_MEDIA(R3) ; Save the command buffer address MOVL DSC_CMDLEN(R9),R0 ; Get length of the SCSI command MOVL R0,(R2)+ ; Save it in the command buffer PUSHR #^M ; Save regs MOVC3 R0,@DSC_CMDADR(R9),(R2) ; Copy the SCSI command from the user's ; buffer to the buffer in pool POPR #^M ; Restore regs CLRL IRP$L_BCNT(R3) ; Assume no user read/write data MOVL DSC_DATADR(R9),R0 ; Get address of user data buffer BEQL 30$ ; Branch if no user read/write data MOVL DSC_DATLEN(R9),R1 ; Get length of user data buffer BEQL 30$ ; Branch if no user read/write data MOVAL G^EXE$READLOCKR,R2 ; Assume user is performing a read BLBS DSC_FLAGS(R9),20$ ; Branch if this is a read operation MOVAL G^EXE$WRITELOCKR,R2 ; Other check for read access 20$: JSB (R2) ; Check access to and lock down buffer BLBC R0,60$ ; Branch on error 30$: MOVAL IRP$C_CDRP(R3),R0 ; Get address of SCDRP within IRP MOVL DSC_FLAGS(R9),(R0)+ ; Save flags field in IRP/CDRP MOVAL DSC_PADCNT(R9),R1 ; Get address of pad count field .REPT 3 MOVL (R1)+,(R0)+ ; Save pad count, timeout values .ENDR JMP G^EXE$QIODRVPKT ; Queue the packet to the driver 40$: MOVL #SS$_BADPARAM,R0 ; Set bad parameter status 50$: JMP G^EXE$ABORTIO ; Abort the I/O with status in R0 ; We arrive here if the last FDT operation - checking access to and locking ; down the user's read/write buffer fails. EXE$READLOCKR or EXE$WRITELOCKR ; returns to us through a co-routine call to allow us to give up any resources ; which we have allocated during FDT processing. Deallocate the buffer ; containing a copy of the SCSI command, then return from the co-routine call. ; R0 and R1 must be preserved. 60$: PUSHQ R0 ; Save regs MOVL IRP$L_MEDIA(R3),R0 ; Get address of non-paged pool buffer ; containing SCSI command MOVL -(R0),R1 ; Get length of buffer JSB G^EXE$DEANONPGDSIZ ; Deallocate the packet POPQ R0 ; Restore regs RSB ; Return from co-routine call .SBTTL + .SBTTL + QIO INTERFACE ROUTINES .SBTTL + .SBTTL IO_PACKACK - Pack acknowledge function .SBTTL IO_SENSECHAR - Get device characteristics .SBTTL IO_SENSEMODE - Get device characteristics ;+ ; IO_PACKACK ; IO_SENSEMODE ; IO_SENSECHAR ; ; This routine executes a PACKACK request by polling the device with TEST UNIT ; READY commands until the device is ready, then sending INQUIRY, RECEIVE ; DIAGNOSTIC DATA, MODE SENSE, and MODE SELECT commands. The effect of these ; commands is to prepare the drive to accept READ, WRITE, and tape motion ; commands. ; ; INPUTS: ; ; R3 - UCB address ; R4 - PDT address ; R5 - SCDRP address ; ; OUTPUTS: ; ; R0 - Status ; R1,R2 - Destroyed ; All other registers preserved ;- IO_PACKACK: BSBW PACKACK_SEQ BRW COMPLETE_IO ; Complete this I/O function PACKACK_SEQ: SUBSAVE ; Save SR return address BSBW WAIT_UNIT_READY ; Wait for the device to spin up BLBC R0,30$ ; Branch on error BSBW INQUIRY ; Execute an INQUIRY command BLBC R0,30$ ; Branch on error IF_TK50 10$ ; If TZK50 don't do receive diagnostic ; command. Hardware/Firmware revision ; available via UNIT_INIT. BSBW RECEIVE_DIAG ; Execute a received diagnostic data cmd BLBC R0,30$ ; Branch on error 10$: BSBW MODE_SENSE ; Execute a MODE SENSE command BLBC R0,30$ ; Branch on error BSBW MODE_SELECT ; Set error recovery parameters, block ; size, etc. (if necessary) BLBC R0,30$ ; Branch on error BSBW CHECK_SKIPFILE_SUPPORT ; Check if READ POSITION cmd supported BSBW CHECK_COMPACTION_SUPPORT ; check if device supports data compaction 20$: BSBW SET_CONN_CHAR ; Set up the connection characteristics BLBC R0,30$ ; Branch on error BISW #UCB$M_VALID,- ; Set volume valid UCB$W_STS(R3) ; 30$: SUBRETURN ; Return to caller IO_SENSEMODE: IO_SENSECHAR: BSBW PACKACK_SEQ BLBC R0,20$ ; Branch on error ; ; Return device characteristics if user supplied buffer ; ; ; the IFNOT_TSZO7 is commented out below to allow the DEVDEPEND info ; to be returned for all devices ; ; IFNOT_TSZ07 10$ ; Branch if not TSZ07 BBS #UCB$V_CANCEL,- ; If request has been cancelled, skip UCB$L_STS(R3),- ; to 20$ - the IRP has already been 20$ ; deallocated. - RCL 9/21/92 MOVL SCDRP$L_IRP(R5),R1 ; Get pointer to IRP TSTL IRP$L_SVAPTE(R1) ; Is there a user supplied buffer? BEQL 10$ ; Branch if not. ASSUME UCB$L_DEVDEPEND EQ UCB$B_DEVCLASS+4 ASSUME UCB$L_DEVDEPND2 EQ UCB$L_DEVDEPEND+4 MOVL @IRP$L_SVAPTE(R1),R0 ; Get system buffer data area address. MOVL UCB$B_DEVCLASS(R3),(R0)+ ; Store dev class, type, buffer size. MOVL UCB$L_DEVDEPEND(R3),- ; Store device characteristics. (R0)+ CMPW IRP$W_BCNT(R1), #12 ; User buffer length = 12? BNEQ 10$ ; Branch if not. MOVL UCB$L_DEVDEPND2(R3),- ; Store extended device characteristics. (R0)+ ; 10$: MOVZWL #SS$_NORMAL,R0 ; Success status 20$: BRW COMPLETE_IO ; Complete this I/O function .SBTTL IO_DSE - Data security erase ;+ ; IO_DSE ; ; This routine sends an ERASE command to the tape to erase all the data from ; the current position to the end of the tape. The tape is then rewound. ; ; INPUTS: ; ; R3 - UCB address ; R4 - PDT address ; R5 - SCDRP address ; ; OUTPUTS: ; ; R0 - Status ; R1,R2 - Destroyed ; All other registers preserved ;- IO_DSE: IO_ERASETAPE: BICW #,- ; UCB$L_DEVDEPEND+2(R3) ; MOVAL CMD_ERASE,R2 ; Get address of erase command BSBW SETUP_CMD ; Setup the command BSBW SEND_COMMAND ; Send the command BSBW CLEANUP_CMD ; Cleanup from the SCSI command BLBCW R0,COMPLETE_IO ; Branch on error BRW IO_REWIND ; Otherwise, go rewind the tape .SBTTL IO_SETMODE - Set mode ;+ ; IO_SETMODE ; ; This routine sends a MODE_SELECT command to the tape to set up various drive ; characteristics including default block size, buffered (streaming) mode, ; number of fillers, and infinite reselection timeout. ; ; INPUTS: ; ; R3 - UCB address ; R4 - PDT address ; R5 - SCDRP address ; ; OUTPUTS: ; ; R0 - Status ; R1,R2 - Destroyed ; All other registers preserved ; ;- IO_SETMODE: MOVW SCDRP$L_MEDIA+2(R5),- ; Set new default buffer size UCB$W_DEVBUFSIZ(R3) ; IF_TSZ07 10$ ; Branch if TSZ07 BSBW MODE_SELECT ; Send a mode select command BLBS R0,10$ ; attempt to set compaction/density BRW 30$ ; branch on error ; ; - TSZ07 set new density if at BOT ; - other devices check for compaction enable/disable ; 10$: TSTL UCB$L_RECORD(R3) ; Are we at BOT? BNEQW 25$ ; Branch if not, don't change density MOVL SCDRP$L_IRP(R5),R1 ; Get pointer to IRP MOVW IRP$L_MEDIA+4(R1),- ; Save characteristics in unused UCB$W_BOFF(R3) ; UCB location. EXTZV #MT$V_DENSITY,- ; Get density selection #MT$S_DENSITY,- UCB$W_BOFF(R3),R0 ; ; Do density/compaction checks ; IFNOT_TSZ07 20$ ; ; TSZ07 density checks ; ASSUME MT$K_DEFAULT EQ 0 TSTW R0 ; default density ? BNEQ 13$ ; NEQ = no CLRW UCB$W_MK_DENSITY(R3) ; Set density to default BRB 17$ ; issue default density mode_select 13$: CMPW #MT$K_PE_1600,R0 ; Set 1600 bpi? BEQL 15$ ; Branch if so CMPW #MT$K_GCR_6250,R0 ; Set 6250 bpi? BEQL 15$ ; Branch if so MOVZWL #SS$_BADPARAM,R0 ; Invalid density selected BRW COMPLETE_IO ; Finished 15$: SUBW3 #2,R0,UCB$W_MK_DENSITY(R3) ; Compute device density (2 less ; less than VMS density values). 17$: BSBW MODE_SELECT ; Send a mode select command BLBC R0,30$ ; Branch on error MOVL SCDRP$L_IRP(R5),R1 ; Get pointer to IRP MOVW IRP$L_MEDIA+4(R1),- ; Save characteristics in unused UCB$W_BOFF(R3) ; UCB location. EXTZV #MT$V_DENSITY,- ; Get density selection again #MT$S_DENSITY,- UCB$W_BOFF(R3),R0 INSV R0,- ; Store density value in UCB #MT$V_DENSITY,- ; device characteristics field. #MT$S_DENSITY,- UCB$L_DEVDEPEND(R3) BRB 25$ ; all done ; ; All other devices get checked for compaction enable/disable ; 20$: CMPW #MT$K_SCSI_DC1,R0 ; Set Compaction mode? BNEQ 21$ ; *** IGNORE ANY OTHER DENSITY VALUES MOVL #1, R0 ; flag for SETUP_COMPACTION = ON BRB 22$ 21$: CLRL R0 ; flag compaction off 22$: ; Only attempt to issue mode_select if BITL #MT2$M_COMP_SUP,- ; compaction is supported UCB$L_DEVDEPND2(R3) ; BEQL 25$ ; branch if not BSBW SETUP_COMPACTION ; do MODE_SELECT for compaction page BLBC R0,30$ ; Branch if error MOVL SCDRP$L_IRP(R5),R1 ; Get pointer to IRP EXTZV #MT$V_DENSITY,- ; Get density selection again #MT$S_DENSITY,- IRP$L_MEDIA+4(R1),R1 INSV R1,- ; Store density value in UCB #MT$V_DENSITY,- ; device characteristics field. #MT$S_DENSITY,- UCB$L_DEVDEPEND(R3) BRB 30$ ; all done 25$: MOVZWL #SS$_NORMAL,R0 ; Success status 30$: BRW COMPLETE_IO ; Complete this I/O function .SBTTL IO_WRITEOF - Write tape mark .SBTTL IO_WRITEMARK - Write tape mark ;+ ; IO_WRITEOF ; IO_WRITEMARK ; ; This routine writes one tapemark at the current position on the tape. ; ; INPUTS: ; ; R3 - UCB address ; R4 - PDT address ; R5 - SCDRP address ; ; OUTPUTS: ; ; R0 - Status ; R1,R2 - Destroyed ; All other registers preserved ;- IO_WRITEMARK: IO_WRITEOF: BICW #,- ; UCB$L_DEVDEPEND+2(R3) ; MOVAL CMD_WRITE_FILEMARKS,R2 ; Get address of write filemarks command BSBW SETUP_CMD ; Setup the command MOVL SCDRP$L_CMD_PTR(R5),R1 ; Get the SCSI command address MOVB #1,SCSI_WFM$B_CNT+4+2(R1); Specify one filemark 10$: BSBW SEND_COMMAND ; Send the command BLBS R0,15$ ; Branch if success CMPL R0,#SS$_ENDOFTAPE ; End of tape? If so, treat as success BEQL 15$ ; Branch if so CMPL R0,#SS$_PARITY ; Media error? BNEQ 20$ ; Branch if not 15$: INCL UCB$L_RECORD(R3) ; Update record count 20$: BSBW CLEANUP_CMD ; Cleanup from the SCSI command BRW COMPLETE_IO ; Complete this I/O function .SBTTL IO_READPBLK - Read a set of blocks from the SCSI drive ;+ ; IO_READPBLK ; ; This routine reads a record from the tape. If the function is a REVERSE READ, ; a backspace of one record is performed both prior to and after the read, as ; many SCSI tape drives, including the TZK50 and TZ30, don't support the READ ; REVERSE command. ; ; INPUTS: ; ; R3 - UCB address ; R4 - PDT address ; R5 - SCDRP address ; ; OUTPUTS: ; ; R0 - Status ; R1,R2 - Destroyed ; All other registers preserved ;- IO_READPBLK: IO_READLBLK: BICW #,- ; UCB$L_DEVDEPEND+2(R3) ; BBC #IO$V_REVERSE,- ; Branch if this is NOT a read reverse SCDRP$W_FUNC(R5),10$ ; function MNEGW #1,R0 ; Prepare to backup one record SUBPUSH SCDRP$L_BCNT(R5) ; Save read byte count BSBW SKIP_RECORD ; Backup one record SUBPOP SCDRP$L_BCNT(R5) ; Restore read byte count BLBCW R0,50$ ; Branch on error 10$: MOVAL CMD_READ,R2 ; Address of read command BSBW SETUP_CMD ; Setup the command ADDL3 #<4+SCSI_RD$B_LEN+3>,- ; Get address just beyond transfer SCDRP$L_CMD_PTR(R5),R0 ; length field in the SCSI command MOVAL SCDRP$L_BCNT(R5),R1 ; Get address of byte count field in SCRRP .REPT 3 MOVB (R1)+,-(R0) ; Fill in the transfer length field .ENDR MOVW #IRP$M_FUNC,- ; Set the FUNC but to indicate this SCDRP$W_STS(R5) ; is a read function BISB #SCDRP$M_BUFFER_MAPPED,-; Remember the fact that a buffer SCDRP$L_SCSI_FLAGS(R5) ; has been mapped SPI$MAP_BUFFER ; Map the user's buffer BSBW SEND_COMMAND ; Send the SCSI command BSBW CLEANUP_CMD ; Cleanup from the command INCL UCB$L_RECORD(R3) ; Keep track of forward tape position BLBS R0,15$ ; Branch on success CMPL R0,#SS$_DATAOVERUN ; If overrun, treat as success BEQL 15$ ; Branch if so CMPL R0,#SS$_ENDOFTAPE ; If EOT, treat as success BNEQ 50$ ; Branch if not MOVW #SS$_NORMAL,R0 ; Change EOT status to success 15$: MOVL SCDRP$L_TRANS_CNT(R5),R1; Get the transfer count value BBC #IO$V_REVERSE,- ; Branch if this is NOT a read reverse SCDRP$W_FUNC(R5),20$ ; function SUBPUSH R0 ; Save status from read SUBPUSH R1 ; Save the transfer count value MNEGW #1,R0 ; Prepare to backup one record BSBW SKIP_RECORD ; Backup one record BLBC R0,50$ ; If skip fails, use that as the status ; code SUBPOP R1 ; Restore transfer count SUBPOP R0 ; Restore status from read 20$: INSV R1,#16,#16,R0 ; Place transfer count in high-order ; word of R0 50$: BRW COMPLETE_IO ; Complete this I/O function 60$: SUBPOP R2 ; Clean off the internal stack SUBPOP R2 ; BRB 50$ ; Use common exit .SBTTL IO_WRITEPBLK - Write a set of blocks to the SCSI drive ;+ ; IO_WRITEBLK ; ; This routine writes a record to the tape. If the function is a REVERSE WRITE, ; a backspace of one record is performed both prior to and after the write, as ; many SCSI tape drives, including the TZK50 and TZ30, don't support the WRITE ; REVERSE command. ; ; INPUTS: ; ; R3 - UCB address ; R4 - PDT address ; R5 - SCDRP address ; ; OUTPUTS: ; ; R0 - Status ; R1,R2 - Destroyed ; All other registers preserved ;- IO_WRITEPBLK: IO_WRITELBLK: BICW #,- ; UCB$L_DEVDEPEND+2(R3) ; BBC #IO$V_REVERSE,- ; Branch if this is NOT a write reverse SCDRP$W_FUNC(R5),10$ ; function MNEGW #1,R0 ; Prepare to backup one record SUBPUSH SCDRP$L_BCNT(R5) ; Save read byte count BSBW SKIP_RECORD ; Backup one record SUBPOP SCDRP$L_BCNT(R5) ; Restore read byte count BLBCW R0,50$ ; Branch on error 10$: MOVAL CMD_WRITE,R2 ; Address of read command BSBW SETUP_CMD ; Setup the command ADDL3 #<4+SCSI_WRT$B_LEN+3>,- ; Get address just beyond transfer SCDRP$L_CMD_PTR(R5),R0 ; length field in the SCSI command MOVAL SCDRP$L_BCNT(R5),R1 ; Get address of byte count field in IRP .REPT 3 MOVB (R1)+,-(R0) ; Fill in the transfer length field .ENDR CLRW SCDRP$W_STS(R5) ; Clear the FUNC but to indicate this ; is a write function BISB #SCDRP$M_BUFFER_MAPPED,-; Remember the fact that a buffer SCDRP$L_SCSI_FLAGS(R5) ; has been mapped SPI$MAP_BUFFER ; Map the user's buffer BSBW SEND_COMMAND ; Send the SCSI command BLBS R0,15$ ; Branch on success IFNOT_8MM 15$ ; Br if not 8MM TSTB UCB$B_8MM_CHK(R3) ; Should 8MM COPY check be done? BEQL 15$ ; Br if no BSBW DO_8MM_COPY_CHK ; Attempt recovery processing BLBC R0,15$ ; Branch on error BSBW SEND_COMMAND ; Send the SCSI WRITE command again 15$: BSBW CLEANUP_CMD ; Cleanup from the command BLBS R0,20$ ; Branch on success CMPL R0,#SS$_ENDOFTAPE ; End of tape? If so, treat as success BEQL 20$ ; Branch if so CMPL R0,#SS$_PARITY ; Media error? BNEQ 50$ ; Branch if not 20$: INCL UCB$L_RECORD(R3) ; Keep track of forward tape position MOVL SCDRP$L_TRANS_CNT(R5),R1; Get transfer count BBC #IO$V_REVERSE,- ; Branch if this is NOT a read reverse SCDRP$W_FUNC(R5),30$ ; function SUBPUSH R0 ; Save write command status SUBPUSH R1 ; Save the transfer count value MNEGW #1,R0 ; Prepare to backup one record BSBW SKIP_RECORD ; Backup one record MOVL R0,R2 ; Copy status for backspace command SUBPOP R1 ; Restore write status SUBPOP R0 ; Restore transfer count BLBS R2,30$ ; Branch if backspace suceeded MOVL R2,R0 ; Otherwise, return backupspace status 30$: INSV R1,#16,#16,R0 ; Return transfer count in h.o. word of R0 50$: BRW COMPLETE_IO ; Complete this I/O function .SBTTL IO_SKIP_RECORD - Move n records ;+ ; IO_SKIP_RECORD ; ; This routine skips n records, where n is a signed word in the SCRP$L_MEDIA ; field. The skip operation stops upon encountering a tape mark. The actual ; number of records skipped is returned in the h.o. word of R0. If two ; consecutive tape marks are encountered while skipping forward, and the NOT ; mounted files-11, then SS$_ENDOFVOLUME status is returned. ; ; INPUTS: ; ; R3 - UCB address ; R4 - PDT address ; R5 - SCDRP address ; ; OUTPUTS: ; ; R0 - Status, actual skip count in h.o. word ; R1,R2 - Destroyed ; All other registers preserved ; ;- .ENABL LSB IO_SKIP_RECORD: MOVW SCDRP$L_MEDIA(R5),R0 ; Number of records to skip BLSS IO_SKIP_RECORD_REV ; Branch if negative BEQL 50$ ; Branch if zero (nothing to do) BSBW SKIP_RECORD ; Skip the specified number of records BLBS R0,10$ ; Branch on success CMPL R0,#SS$_ENDOFTAPE ; End of tape? BEQL 10$ ; Branch if so CMPL R0,#SS$_ENDOFFILE ; Tapemark encountered? BNEQ 20$ ; Branch if not BSBW CHECK_EOV ; Check for logical end-of-volume CMPL R0,#SS$_ENDOFVOLUME ; End of volume? BNEQ 10$ ; Branch if so DECW R1 ; Tape was backed up one record 10$: INSV R1,#16,#16,R0 ; Return skip count in high-order 20$: BRW COMPLETE_IO ; Complete the I/O request IO_SKIP_RECORD_REV: CLRW UCB$W_BOFF(R3) ; Initialize accumulated skip count 30$: BSBW SKIP_RECORD ; Skip the specified number of records SUBW R1,UCB$W_BOFF(R3) ; Update accumulated skip count BLBS R0,40$ ; Branch on success CMPL R0,#SS$_ENDOFFILE ; Tapemark encountered? BEQL 40$ ; Branch if so CMPL R0,#SS$_ENDOFTAPE ; End of tape? BNEQ 20$ ; Branch if not TSTL UCB$L_RECORD(R3) ; Are we at BOT? BEQL 35$ ; Branch if so TSTW R1 ; Any records skipped? BGEQ 35$ ; Branch if not making any progress ADDW3 SCDRP$L_MEDIA(R5),- ; Prepare to skip the remaining number UCB$W_BOFF(R3),R0 ; of records BLSS 30$ ; Branch if more records to skip 35$: MOVL #SS$_NORMAL,R0 ; Treat EOT or BOT as success 40$: MOVW UCB$W_BOFF(R3),R1 ; Place total skip count in R1 BRB 10$ ; Use common exit path 50$: CLRL R1 ; No records skipped MOVZWL #SS$_NORMAL,R0 ; Set success status BRB 10$ ; Use common exit path .DSABL LSB .SBTTL IO_SKIP_FILE - Move n tapemarks (in either direction) ;+ ; IO_SKIP_FILE ; ; This routine spaces n tapemarks in either direction. SCDRP$L_MEDIA contains ; a signed integer specifying the number of tapemarks to space. If the ; device supports the READ POSITION command, allowing us to keep track ; of first block location, then we can use the SCSI SPACE FILEMARKS command ; followed by the READ POSITION command. Otherwise we will use ; the space record option, and each time a filemark is encountered, we ; accumulate the number of records skipped. ; ; When skipping forward, a record skip count less than the maximum is used ; in order to allow for reasonable responsive I/O cancels. Because of the way ; tape drives implement reverse skip record (rewind, space forward), this same ; method can not be used for space reverse operations (i.e., the maximum ; reverse record count is used). ; ; INPUTS: ; ; R3 - UCB address ; R4 - PDT address ; R5 - SCDRP address ;- .ENABL LSB IO_SKIP_FILE: MOVW SCDRP$L_MEDIA(R5),R1 ; Get number of files to skip MOVW R1,UCB$W_BOFF(R3) ; Save it in a convenient place MOVW R1,UCB$W_BCNT(R3) ; Save another copy ; ; Set the flag to indicate we do NOT use skip by filemarks until the tests ; below may show that we should and can do so. ; BICL #MT$M_FASTSKIP_USED,- ; Clear skip by filemarks bit UCB$L_DEVDEPEND(R3) CMPW #1,R1 ; Optimize for the most common case ; of skip count = 1 BEQL 10$ CMPW #2,R1 ; Optimize for the next common case ; of skip count = 2 BEQL 10$ BBC #UCB$V_SKIPFILE_SUPPORTED,- UCB$L_MK_FLAGS(R3),- ; Test for hardware support for 5$ ; skip by filemarks, branch if no ; support BBC #UCB$V_ALLOWFAST_ALWAYS,- UCB$L_MK_FLAGS(R3),- ; Test for skip by filemarks 2$ ; enabled as default operation, BRW SPACE_FILEMARKS ; branch if default is set 2$: BBC #UCB$V_ALLOWFAST_PER_IO,- UCB$L_MK_FLAGS(R3),- ; Test for skip by filemarks 5$ ; enabled for function code ; modifier operations, branch ; if not enabled BBC #IO$V_ALLOWFAST,- ; Branch if IO$V_ALLOWFAST function SCDRP$W_FUNC(R5),- ; code modifer is clear 5$ ; BRW SPACE_FILEMARKS ; Branch for set modifier 5$: TSTW R1 ; Test space count BLSS IO_SKIP_FILE_REV ; Branch if negative count BEQL 20$ ; Branch if zero skip count 10$: BBS #UCB$V_CANCEL,- ; Branch if I/O has been canceled? UCB$L_STS(R3),20$ ; MOVW #^X7FFF,R0 ; Forward skip count BSBW SKIP_RECORD ; Skip records (updating UCB$L_RECORD) BLBS R0,10$ ; Branch if no tapemark found CMPL R0,#SS$_ENDOFFILE ; Tape mark encountered? BNEQ 40$ ; Branch if not BSBW CHECK_EOV ; Check for logical end-of-volume CMPL R0,#SS$_ENDOFVOLUME ; Locical EOV? BEQL 30$ ; Branch if so CMPL R0,#SS$_ENDOFFILE ; EOF status? BNEQ 40$ ; Branch if not DECW UCB$W_BOFF(R3) ; One more tapemark found BGTR 10$ ; Branch if not done yet 20$: MOVW #SS$_NORMAL,R0 ; Set success status 30$: SUBW3 UCB$W_BOFF(R3),- ; Calculate actual number of UCB$W_BCNT(R3),R1 ; files skipped INSV R1,#16,#16,R0 ; Save in high-order word of R0 40$: BRW COMPLETE_IO IO_SKIP_FILE_REV: 50$: BBS #UCB$V_CANCEL,- ; Branch if I/O has been canceled? UCB$L_STS(R3),20$ ; MOVW #^X8000,R0 ; Maximum reverse skip count BSBW SKIP_RECORD ; Skip records (updating UCB$L_RECORD) BLBS R0,50$ ; Branch if no tapemark found CMPL R0,#SS$_ENDOFTAPE ; End of tape? BEQL 20$ ; Branch if not CMPL R0,#SS$_ENDOFFILE ; Tape mark encountered? BNEQ 40$ ; Branch if not 60$: INCW UCB$W_BCNT(R3) ; One more tapemark found BLSS 50$ ; Branch if more to do BRB 20$ ; Use common exit SPACE_FILEMARKS: ; Do Space Filemarks to move the tape BISL #MT$M_FASTSKIP_USED,- ; Set skip by filemarks bit UCB$L_DEVDEPEND(R3) BBS #UCB$V_CANCEL,- ; Branch if I/O has been canceled? UCB$L_STS(R3),20$ ; CLRL R0 ; Clear upper word of R0 MOVW R1,R0 ; Skip count BGTR 65$ ; Branch if positive BEQL 20$ ; Exit if zero count BRW SPACE_FILEMARKS_REV ; Branch if negative count 65$: BSBW SKIP_FILE ; Skip tapemarks, return count in R1 BLBS R0,70$ ; Case 1: SS$_NORMAL was returned CMPL R0,#SS$_ENDOFVOLUME ; Case 2: Logical EOV status? BEQL 775$ ; BRW 140$ ; Case 3: Fall through, serious error 775$: BRW 75$ ; Case 1: SS$_NORMAL was returned. ; ; If here, then SS$_NORMAL has just been returned. There are two sub-cases: ; the tape is now positioned on some tapemark in the middle of the tape, ; or the tape is positioned on the second tapemark within an EOV tapemark pair. ; In the latter case it is important to return SS$_ENDOFVOLUME instead of ; success. In order to determine which case we have, employ the following ; logic: upon SS$_NORMAL, back up one position to see if we hit a tapemark in ; the preceding record position. If not, then we know we are not at EOV after ; all; so skip forward to get to our original position. If we are in an ; an EOV situation, however, we must change the status code from SS$_NORMAL to ; SS$_ENDOFVOLUME. ; ; Check whether the preceding record position is a tape mark as well. ; Refer to the diagram below and note that: ; At this point the tape is positioned on the end-of-partition side ("A") ; of the tape mark. We must space back one file to get to the beginning-of- ; partition side ("B") of the tape mark, then space back one record (to "C") ; and test for a tapemark-encounterd Check Condition. ; ; ----+----------+----------+-------------------------------------- ; |Record to | Tapemark | ; |be tested | | ; ----+----------+----------+-------------------------------------- ; (C) (B) (A) ; Space reverse by one filemark 70$: MOVL #-1,R0 ; Prepare to back up to beg-of-partn SUBPUSH R1 ; Save actual skip count BSBW SKIP_FILE ; Get to beginning-of-partition side SUBPOP R1 ; Restore actual skip count BLBS R0,71$ ; Exit if error BRW 140$ ; Space reverse by one record 71$: MOVL #-1,R0 ; Prepare to back up one record SUBPUSH R1 ; Save actual skip count BISL #UCB$M_NO_POSITION_UPDATE,- UCB$L_MK_FLAGS(R3) BSBW SKIP_RECORD ; Jump back over preceding record BICL #UCB$M_NO_POSITION_UPDATE,- UCB$L_MK_FLAGS(R3) SUBPOP R1 ; Restore actual skip count BLBS R0,72$ ; The preceding record is not a tapemk CMPL R0,#SS$_ENDOFFILE ; EOF status? BEQL 711$ BRW 140$ ; Branch if not, serious problem exists ; Case 1a: ; We have hit the case of 2 consecutive tapemarks, so space forward one ; tapemark to position the tape between the 2 marks. 711$: MOVL #1,R0 ; Prepare to position between 2 tm's SUBPUSH R1 ; Save actual skip count BSBW SKIP_FILE ; Position the tape SUBPOP R1 ; Restore actual skip count BLBS R0,712$ ; Continue on success BRW 140$ ; Exit if error 712$: MOVL #SS$_ENDOFVOLUME,R0 ; We hit a tapemark, so return end-of- ; volume status DECW R1 ; We just backed up one filemark BRW 80$ ; Join common exit code ; Case 1b: ; The record we just backed up to is not a tapemark. So we are not at end ; of logical volume. So restore position by spacing forward (across both ; the record and the subsequent tapemark). Space by file instead of by record ; to avoid a Check Condition. 72$: MOVL #1,R0 ; Prepare to skip forward acros tpmark SUBPUSH R1 ; Save actual skip count BSBW SKIP_FILE ; Skip forward one tapemark SUBPOP R1 ; Restore actual skip count BLBS R0,80$ ; Tape is now back to original position BRW 140$ ; Branch if err, serious problem exists ; Case 2: Logical EOV status ; ; If here, then SS$_ENDOFVOLUME has just been returned. Back up one tapemark. ; While it may seem similar to the code above, it is kept separate in ; order to detect the error condition that SS$_ENDOVOLUME (signified by a ; BLANK CHECK) was returned by SKIP_FILE, but the tape does not end in ; a pair of tapemarks. 75$: MOVL #-1,R0 ; Prepare to back up one record SUBPUSH R1 ; Save actual skip count BISL #UCB$M_NO_POSITION_UPDATE,- UCB$L_MK_FLAGS(R3) BSBW SKIP_RECORD ; Position tape between filemarks BICL #UCB$M_NO_POSITION_UPDATE,- UCB$L_MK_FLAGS(R3) SUBPOP R1 ; Restore actual skip count CMPL R0,#SS$_ENDOFFILE ; EOF status? BNEQ 140$ ; Branch if not, serious problem exists MOVL #SS$_ENDOFVOLUME,R0 ; Return end of volume status DECW R1 ; We just backed up one filemark ; Common code for Space Forward processing 80$: BSBW READ_POSITION ; Update ucb$l_record BISL #MT$M_EOF,- ; We are positioned on a tapemark UCB$L_DEVDEPEND(R3) MOVL UCB$L_RECORD(R3),- ; Update last tapemark found during UCB$L_PREV_TM(R3) ; a forward search INSV R1,#16,#16,R0 ; Save in high-order word of R0 BRW 140$ ; Go to COMPLETE_IO SPACE_FILEMARKS_REV: BSBW SKIP_FILE ; Skip files, return actual count in R1 BLBS R0,100$ ; Branch if success CMPL R0,#SS$_ENDOFTAPE ; Beginning of tape? BNEQ 140$ ; Other errors serious - exit MOVL #SS$_NORMAL,R0 ; But BOT is not an error 100$: BISL #UCB$M_REV_SKIP,- ; Indicate direction (needed by UCB$L_MK_FLAGS(R3) ; READ POSITION) BSBW READ_POSITION ; Update ucb$l_record BICL #UCB$M_REV_SKIP,- ; Clear flag UCB$L_MK_FLAGS(R3) MNEGW R1,R1 ; IOSB needs unsigned count INSV R1,#16,#16,R0 ; Save in high-order word of R0 ; Space Filemarks exit path (all cases, Forward and Reverse) 140$: BRW 40$ ; Go to common exit and COMPLETE_IO .DSABL LSB .PAGE .SBTTL READ_POSITION - read tape record position ; READ_POSITION ; ; Do Read Position command to fill in UCB$L_RECORD ; ; INPUTS: ; ; R0 - VMS status from last Space command ; R3 - UCB address ; R5 - SCDRP address ; ; OUTPUTS: ; ; R2 - Destroyed ; All other registers preserved. ; READ_POSITION: SUBSAVE SUBPUSH R0 SUBPUSH R1 MOVL R0,- ; Save status UCB$L_SKIPFILE_STAT(R3) MOVAL CMD_READ_POSITION,R2 ; Address of READ POSITION command BSBW SETUP_CMD ; Setup the command BSBW SEND_COMMAND ; Send the command BLBC R0,30$ ; Branch on error CMPL SCDRP$L_TRANS_CNT(R5),- ; Sufficient data returned? #20 ; READ POS data length should equal 20. BLSS 30$ ; No, declare the command unsupported MOVL SCDRP$L_SVA_USER(R5),R0 ; Get address of data BBS #BPU_BIT,- ; If block position unknown, (R0),30$ ; declare the command unsupported MOVL UCB$L_RECORD(R3),R2 ; Save current position MOVAB 8(R0),R0 ; Prepare to update UCB$L_RECORD MOVAB UCB$L_RECORD(R3),R1 .REPT 4 MOVB -(R0),(R1)+ ; Update UCB$L_RECORD .ENDR ; Sanity check: If the skip direction was forward, UCB$L_RECORD should ; increase, otherwise decrease. BBS #UCB$V_REV_SKIP,- ; Is this a reverse skip? UCB$L_MK_FLAGS(R3),10$ ; Forward skip sanity check CMPL UCB$L_RECORD(R3),R2 ; Is new value > old value ? BGTR 20$ ; Yes, good, so exit normally. BLSS 30$ ; It went backward, something is wrong. ; Allow for the possibility that a skip forward was issued while the previous ; position was EOV, so that both old and new values are the same. CMPL UCB$L_SKIPFILE_STAT(R3),- #SS$_ENDOFVOLUME ; EOV? BEQL 20$ ; Yes, it's okay to have same position BRW 30$ ; Else error ; Reverse skip sanity check 10$: CMPL UCB$L_RECORD(R3),R2 ; Is new value < old value ? BLSS 20$ ; Yes, so this is success ; Allow for the possibility that a skip reverse was issued while the ; previous position was BOT, so that both old and new values = 0. BBC #MT$V_BOT,- ; If new value >= old value, and tape UCB$L_DEVDEPEND(R3),30$ ; is not at BOT, something is wrong. ; Common Exit 20$: CLRL UCB$L_SKIPFILE_STAT(R3) ; Clear UCB field BSBW CLEANUP_CMD ; Cleanup from the SCSI command SUBPOP R1 SUBPOP R0 SUBRETURN ; Error path when READ_POSITION fails 30$: BICL2 #UCB$M_SKIPFILE_SUPPORTED,-; READ POSITION command unsupported UCB$L_MK_FLAGS(R3) LOG_ERROR - TYPE=SEND_CMD_ERROR,- VMS_STATUS=#SS$_NORMAL,- UCB=R3 BSBW SET_TAPE_LOST ; Set tape lost BRW 20$ .SBTTL IO_UNLOAD - Rewind and unload the tape ;+ ; IO_UNLOAD ; ; This routine rewinds amd unloads the drive and clears the VALID bit in the ; UCB. ; ; INPUTS: ; ; R2 - IRP address ; R3 - UCB address ; R4 - PDT address ; R5 - SCDRP address ; ; OUTPUTS: ; ; R0 - Status ; R1,R2 - Destroyed ; All other registers preserved ;- IO_RETENSION: .ENABLE LSB IF_TZK10 2$ ; If TZK10, do retension IF_TZK11 2$ ; If TZK11, do retension BRW 11$ 2$: MOVAL CMD_RETENSION,R2 ; Get address of Retension command BRB 5$ ; Continue with CMD set up IO_UNLOAD: IO_REWINDOFF: BBS #IO$V_RETENSION,- ; Check to see if the RETENSION bit is SCDRP$W_FUNC(R5),- IO_RETENSION ; set - if so, branch to IO_RETENSION ADDL3 #MAX_UNLOAD_TIME,- ; Save time at which unload must G^EXE$GL_ABSTIM,- ; be complete UCB$L_UNLOAD_TIME(R3) ; BISW #UCB$M_UNLOAD_INPROG,- ; Indicate unload may be in pro UCB$L_MK_FLAGS(R3) ; when next QIO is issued MOVAL CMD_UNLOAD,R2 ; Get address of unload command 5$: BSBW SETUP_CMD ; Setup the command BSBW SEND_COMMAND ; Send the command BSBW CLEANUP_CMD ; Cleanup from the SCSI command BLBC R0,7$ ; Branch on error ; Workaround for the TZK50: the unload command can take up to 20 ms ; to "take", i.e., subsequent commands sent to the drive within 20 ms ; of an unload can operate as though the unload never occurred. For example, ; a test unit ready command sent immediately after an unload will return ; success, when it should return unit not ready. This can cause errors ; during multi-volume backup operations. Therefore, stall for at least a ; second to allow the unload to do its thing. IFNOT_TK50 7$ ; Branch if not a TK50 MK_WAIT #2,UCB=R3 ; Wait at least 1 second MOVZWL #SS$_NORMAL,R0 ; Restore success status 7$: BICW #UCB$M_VALID,- ; Clear the volume valid bit in UCB$W_STS(R3) ; the UCB BISW #,- ; Mark beginning of tape UCB$L_DEVDEPEND+2(R3) ; BICW #,- ; and end of file UCB$L_DEVDEPEND+2(R3) ; CLRL UCB$L_RECORD(R3) ; Initialize record field MNEGL #2,UCB$L_PREV_TM(R3) ; No previous tape mark 10$: BRW COMPLETE_IO ; Complete this I/O function 11$: MOVZBL #SS$_ILLIOFUNC,R0 ; Not a TZK10, bad func code BRB 10$ ; for this device .DISABLE LSB .SBTTL IO_AVAILABLE - Make drive available (but don't unload it) ;+ ; IO_AVAILABLE ; ; This routine rewinds the tape and clears the VALID bit in the UCB. Since ; this function commonly follows an IO$_UNLOAD function, the drive can ; potentially be unloaded and therefore unable to accept the rewind command. ; Therefore, if UCB$L_RECORD contains a zero, and MT$M_LOST is not set don't ; perform a rewind. ; ; INPUTS: ; ; R2 - IRP address ; R3 - UCB address ; R4 - PDT address ; R5 - SCDRP address ; ; OUTPUTS: ; ; R0 - Status ; R1,R2 - Destroyed ; All other registers preserved ;- IO_AVAILABLE: BICW #UCB$M_VALID,- ; Clear the volume valid bit in UCB$W_STS(R3) ; the UCB TSTL UCB$L_RECORD(R3) ; Is tape already at BOT? BNEQ 10$ ; Branch if not BBC #MT$V_LOST,- ; Branch if tape position not lost, OK UCB$L_DEVDEPEND(R3),20$ ; to skip rewind function 10$: MOVAL CMD_REWIND,R2 ; Get address of rewind command BSBW SETUP_CMD ; Setup the command MOVL SCDRP$L_CMD_PTR(R5),R1 ; Get the SCSI command address MOVB #1,4+SCSI_RWND$B_IMMED(R1) ; Set the immediate bit BSBW SEND_COMMAND ; Send the command BLBC R0,15$ ; Branch on error ADDL3 #MAX_REWIND_TIME,- ; Save time at which rewind must G^EXE$GL_ABSTIM,- ; be complete UCB$L_REWIND_TIME(R3) ; 15$: BSBW CLEANUP_CMD ; Cleanup from the SCSI command 20$: MOVZWL #SS$_NORMAL,R0 ; Set success status BISW #,- ; Mark beginning of tape UCB$L_DEVDEPEND+2(R3) ; BICW #,- ; and end of file UCB$L_DEVDEPEND+2(R3) ; CLRL UCB$L_RECORD(R3) ; Initialize record field MNEGL #2,UCB$L_PREV_TM(R3) ; No previous tape mark BRW COMPLETE_IO ; Complete this I/O function .SBTTL IO_REWIND - Rewind the tape ;+ ; IO_REWIND ; ; This routine rewinds the tape. ; ; INPUTS: ; ; R2 - IRP address ; R3 - UCB address ; R4 - PDT address ; R5 - SCDRP address ; ; OUTPUTS: ; ; R0 - Status ; R1,R2 - Destroyed ; All other registers preserved ;- IO_REWIND: BBS #IO$V_RETENSION,- ; Check to see if the RETENSION bit is SCDRP$W_FUNC(R5),25$ ; set - if so, branch to IO_RETENSION MOVAL CMD_REWIND,R2 ; Get address of rewind command BSBW SETUP_CMD ; Setup the command MOVL SCDRP$L_CMD_PTR(R5),R1 ; Get the SCSI command address BBC #IO$V_NOWAIT,- ; Branch if command should complete SCDRP$W_FUNC(R5),10$ ; only after tape finishes rewinding MOVB #1,4+SCSI_RWND$B_IMMED(R1) ; Set the immediate bit ADDL3 #MAX_REWIND_TIME,- ; Save time at which rewind must G^EXE$GL_ABSTIM,- ; be complete UCB$L_REWIND_TIME(R3) ; BISW #UCB$M_REWIND_INPROG,- ; Indicate rewind may be in progress UCB$L_MK_FLAGS(R3) ; when next QIO is issued 10$: BSBW SEND_COMMAND ; Send the command BLBC R0,20$ ; Branch on error BISW #,- ; Mark beginning of tape UCB$L_DEVDEPEND+2(R3) ; BICW #,- ; and end of file UCB$L_DEVDEPEND+2(R3) ; CLRL UCB$L_RECORD(R3) ; Tape is positioned at start MNEGL #2,UCB$L_PREV_TM(R3) ; No previous tape mark 20$: BSBW CLEANUP_CMD ; Cleanup from the SCSI command BRW COMPLETE_IO ; Complete this I/O function 25$: JMP IO_RETENSION ; BRANCH is out of range, uz JMP, ; expected use is small .SBTTL IO_DIAGNOSE - Special pass-through function ;+ ; IO_DIAGNOSE ; ; INPUTS: ; ; R2 - IPR address ; R3 - UCB address ; R4 - PDT address ; R5 - SCDRP address ; ; OUTPUTS: ; ; R0 - Status ; R1,R2 - Destroyed ; All other registers preserved ;- IO_DIAGNOSE: MOVL IRP$L_MEDIA(R2),- ; Copy command buffer from IRP to SCDRP$L_MEDIA(R5) ; SCDRP MOVL IRP$L_SVAPTE(R2),- ; and SVAPE, SCDRP$L_SVAPTE(R5) ; MOVL IRP$L_BCNT(R2),- ; BCNT, SCDRP$L_BCNT(R5) ; MOVW IRP$W_BOFF(R2),- ; and BOFF SCDRP$W_BOFF(R5) ; MOVW IRP$W_STS(R2),- ; and STS SCDRP$W_STS(R5) ; MOVAL IRP$C_CDRP(R2),R0 ; Get address of CDRP portion of IRP EXTZV #1,#1,(R0),R1 ; Get disconnect flag INSV R1,#UCB$V_DISCONNECT,- ; Fill in disconnect flag in UCB #1,UCB$L_MK_FLAGS(R3) ; EXTZV #2,#1,(R0),R1 ; Get synchronous flag INSV R1,#UCB$V_SYNCHRONOUS,- ; Fill in synchronous flag in UCB #1,UCB$L_MK_FLAGS(R3) ; ADDL #4,R0 ; Advance to pad count field MOVL (R0)+,- ; Fill in the pad count in the SCDRP SCDRP$L_PAD_BCNT(R5) ; MOVL (R0)+,- ; Fill in the phase change (DMA) timeout SCDRP$L_DMA_TIMEOUT(R5) ; in the SCDRP MOVL (R0)+,- ; Fill in the disconnect timeout in the SCDRP$L_DISCON_TIMEOUT(R5) ; SCDRP BSBW SET_CONN_CHAR ; Set up the connect characteristics MOVL SCDRP$L_MEDIA(R5),R1 ; Get address of SCSI command in pool MOVL (R1)+,R1 ; Get length of SCSI command ADDL #8,R1 ; Account for overhead SPI$ALLOCATE_COMMAND_BUFFER ; Allocate a command buffer MOVL R2,SCDRP$L_CMD_BUF(R5) ; Save address of command buffer CLRL (R2)+ ; Reserve a longword for status MOVB #^XFF,-1(R2) ; Initialize status field MOVAL -1(R2),- ; Address to save status byte SCDRP$L_STS_PTR(R5) ; MOVL R2,SCDRP$L_CMD_PTR(R5) ; Address of SCSI command in cmd buffer MOVL SCDRP$L_MEDIA(R5),R0 ; Get SCSI command in pool again MOVL (R0),(R2)+ ; Copy SCSI command length PUSHR #^M ; Save regs MOVC3 (R0),4(R0),(R2) ; Copy SCSI command to command buffer POPR #^M ; Restore regs MOVL -(R0),R1 ; Get length of command buffer in pool JSB G^EXE$DEANONPGDSIZ ; Deallocate the buffer TSTL SCDRP$L_BCNT(R5) ; Any user data buffer? BEQL 10$ ; Branch if not SPI$MAP_BUFFER ; Map the user's data buffer 10$: SPI$SEND_COMMAND ; Send the SCSI command PUSHL R0 ; Save returned port status TSTL SCDRP$L_BCNT(R5) ; User buffer mapped? BEQL 20$ ; Branch if not SPI$UNMAP_BUFFER ; Unmap the user's data buffer 20$: MOVL SCDRP$L_CMD_BUF(R5),R0 ; Get the command buffer address PUSHL (R0) ; Save the SCSI status byte SPI$DEALLOCATE_COMMAND_BUFFER ; Deallocate the command buffer POPL R1 ; Restore the SCSI status byte BICL #^X00FFFFFF,R1 ; Clear all but SCSI status byte POPL R0 ; Restore the port status INSV SCDRP$L_TRANS_CNT(R5),- ; Copy the transfer count to the #16,#16,R0 ; high-order word of R0 BSBW DEALLOC_SCDRP ; Deallocate the SCDRP MOVL R3,R5 ; Copy UCB address BBCC #UCB$V_CANCEL, - ; Branch if I/O has UCB$W_STS(R5),40$ ; not been cancelled REMQUE @UCB$L_IOQFL(R5),R3 ; Any I/O queued to device? BVS 30$ ; Branch if not JMP G^IOC$INITIATE ; Go initiate the I/O 30$: BICW #UCB$M_BSY, - ; Clear busy bit to allow future I/O UCB$W_STS(R5) ; to be initiated RSB ; Return to caller 40$: REQCOM ; Complete I/O request .SBTTL + .SBTTL + SCSI COMMAND EXECUTION ROUTINES .SBTTL + .SBTTL INQUIRY - Send an inquiry command ;+ ; INQUIRY ; ; This routine sends an inquiry command to the target. The peripheral device ; type field is checked to make sure we do in fact have a SCSI tape device. In ; addition, the device type qualifier is used to determine if the device is a ; TZK50 or TZ30. ; ; INPUTS: ; ; R3 - UCB address ; R4 - PDT address ; R5 - SCDRP address ; ; OUPUTS: ; ; R0 - Status ;- INQUIRY: SUBSAVE ; Save return address MOVAL CMD_INQUIRY,R2 ; Address of INQUIRY command BSBW SETUP_CMD ; Perform setup for SCSI command BSBW SEND_COMMAND ; Send the SCSI command BLBCW R0,30$ ; Branch on error CMPL SCDRP$L_TRANS_CNT(R5),#5; Sufficient inquiry data returned? BLSSU 17$ ; Branch if not MOVL SCDRP$L_SVA_USER(R5),R0 ; Get address of inquiry data CMPL SCDRP$L_TRANS_CNT(R5),- ; Revision field valid? #36 ; BLSSU 10$ ; Branch if not MOVL 32(R0),UCB$L_HW_REV(R3) ; Save hardware revision level ASSUME UCB$B_DEVCLASS+1 EQ UCB$B_DEVTYPE 10$: CLRW UCB$B_DEVCLASS(R3) ; Assume unknown device class and type CMPB SCSI$INQ$R_PERIPHERAL(R0),-; Is this a SCSI tape device? #SCSI$C_TAPE ; BNEQ 40$ ; Branch if not, illegal device type MOVB #DC$_TAPE,- ; Indicate that this a tape device UCB$B_DEVCLASS(R3) ; BBSS #UCB$V_REMOVABLE,- ; Assume drive has removable media UCB$L_MK_FLAGS(R3),15$ ; 15$: BBS #SCSI$INQ$V_RMB,- ; Is this a removable media device? SCSI$INQ$R_DEVICE_TYPE_MOD(R0),- 20$ ; Branch if so BBCC #UCB$V_REMOVABLE,- ; Clear the removable media bit UCB$L_MK_FLAGS(R3),20$ ; in the UCB 17$: BRB 40$ ; 20$: EXTZV #0,#4,3(R0),R1 ; Get response data format MOVB R1, UCB$B_SCSI_VERSION(R3) ; Save for future checks BEQL 25$ ; Does drive conform to SCSI-1? CMPB R1,#1 ; Does drive conform to CCS? BEQL 25$ ; Branch if so CMPB R1,#2 ; Does drive conform to SCSI-2? BEQL 25$ ; Yes LOG_ERROR - ; Log an invalid inquiry data error TYPE=INV_INQUIRY_DATA,- ; as a warning that we got unknown VMS_STATUS=#SS$_NORMAL,-; SCSI version - not a fatal error. UCB=R3 ; 25$: BSBW GET_DEVICE_TYPE ; Determine device type from Vendor 29$: MOVZWL #SS$_NORMAL,R0 ; Set success status 30$: BSBW CLEANUP_CMD ; Cleanup from the SCSI command SUBRETURN ; Return to caller 40$: LOG_ERROR - ; Log an invalid inquiry data error TYPE=INV_INQUIRY_DATA,- ; VMS_STATUS=#SS$_NORMAL,-; UCB=R3 ; MOVZWL #SS$_DRVERR,R0 ; Set bad status BRB 30$ ; Use common exit .SBTTL SKIP_RECORD - Move n records (in either direction) ;+ ; SKIP_RECORD ; ; This routine skips the number of records specified by the signed integer ; in R0. The operation is aborted if a tapemark is encountered. ; ; INPUTS: ; ; R0 - Signed word containing number of records skip ; R3 - UCB address ; R5 - SCDRP address ; ; OUTPUTS: ; ; R0 - Status ; R1 - Actual number of records skipped ; ; UCB$L_RECORD - Updated ;- SKIP_RECORD: BISB #UCB$M_SKIP_INPROG,- ; Indicate skip command in UCB$L_MK_FLAGS(R3) ; progress BICW #,- ; and end of file UCB$L_DEVDEPEND+2(R3) ; SUBSAVE ; Save return address CVTWL R0,R0 ; Change word count to longword count BGEQ 5$ ; Branch if positive skip count BISB #UCB$M_REV_SKIP,- ; Indicate reverse motion in progress UCB$L_MK_FLAGS(R3) ; 5$: SUBPUSH R0 ; Save space count MOVAB CMD_SPACE,R2 ; Get address of SPACE command BSBW SETUP_CMD ; Setup the command ADDL3 #<4+SCSI_SKIP$B_CNT+3>,-; Get address just beyond count field SCDRP$L_CMD_PTR(R5),R1 ; in the SCSI command SUBGET R0 ; Restore space count (but leave saved ; value on stack) .REPT 2 MOVB R0,-(R1) ; Copy two bytes of space count ASHL #-8,R0,R0 ; to the SCSI command .ENDR MOVB R0,-(R1) ; Copy the third byte CLRL UCB$L_ADDNL_INFO(R3) ; Initialize additional info field BSBW SEND_COMMAND ; Send the SCSI command BSBW CLEANUP_CMD ; Cleanup from the SCSI command SUBPOP R1 ; Restore space count BLBC R0,20$ ; Branch on error 10$: BBS #UCB$V_NO_POSITION_UPDATE,- ; It is not appropriate to update UCB$L_MK_FLAGS(R3),15$ ; position now because IO_SKIP_FILE ; will ADDL R1,UCB$L_RECORD(R3) ; Update record number in UCB BGEQ 15$ ; Branch if non-negative record value BSBW SET_TAPE_LOST ; Otherwise, assume we are lost 15$: BICB #,- ; Indicate skip command no UCB$L_MK_FLAGS(R3) ; longer in progress SUBRETURN ; Return to caller ; Perform a sanity check on the additional info (residue) value returned by ; the tape. If the tape told us it skipped more records than we asked for ; then set tape position lost. Note that the residue field, like the skip ; count, is a signed longword. Therefore, in order to do the comparison, ; we have to distinguish between cases of skipping forward vs. reverse. 20$: MOVL UCB$L_ADDNL_INFO(R3),R2 ; Get number of unskipped records (residue) BBC #UCB$V_REV_SKIP,- ; Branch if original skip count was UCB$L_MK_FLAGS(R3),25$ ; positive (skipping forward) MNEGL R1,R1 ; Invert sign of requested skip count MNEGL R2,R2 ; Invert sign of residue BGEQ 25$ ; Branch if residue was negative MNEGL R2,R2 ; Otherwise, reinvert sign of residue MNEGL R2,UCB$L_ADDNL_INFO(R3) ; Save residue value as a negative 25$: CMPL R2,R1 ; Compare requested skip count with residue BLEQU 27$ ; Branch if residue is reasonable BSBW SET_TAPE_LOST ; Otherwise, assume we are lost BRB 15$ ; Use common exit 27$: BBC #UCB$V_REV_SKIP,- ; Branch if original skip count was UCB$L_MK_FLAGS(R3),29$ ; positive (skipping forward) MNEGL R1,R1 ; Otherwise, reinvert sign of requested skip count 29$: SUBL3 UCB$L_ADDNL_INFO(R3),- ; Get the actual number of records R1,R2 ; skipped ; If a tapemark was encountered, the actual skip count must be adjusted. This ; accounts for the fact that tape drives do not consider tapemarks to be ; records, but the software does (i.e., the UCB$L_RECORD field includes both ; normal records AND tapemarks). CMPL R0,#SS$_ENDOFFILE ; Tape mark encountered? BNEQ 30$ ; Branch if not INCL R2 ; Assume forward skipping TSTL R1 ; Was original skip count positive? BGEQ 30$ ; Branch if so SUBL #2,R2 ; Reverse skipping, fix up skip count 30$: MOVL R2,R1 ; Return actual space count in R1 BRB 10$ ; Use common exit .SBTTL SKIP_FILE - Move n filemarks (in either direction) ;+ ; SKIP_FILE ; ; This routine skips the number of filemarks specified by the signed integer ; in R0. ; ; INPUTS: ; ; R0 - Signed word containing number of filemarks to skip ; R3 - UCB address ; R4 - PDT address ; R5 - SCDRP address ; ; OUTPUTS: ; ; R0 - Status ; R1 - Actual number of filemarks skipped ; R2 - Destroyed ;- SKIP_FILE: BISL #,- UCB$L_MK_FLAGS(R3) ; BICW #,- ; and end of file UCB$L_DEVDEPEND+2(R3) ; SUBSAVE ; Save return address CVTWL R0,R0 ; Change word count to longword count BGEQ 10$ ; Positive or negative space count? BISB #UCB$M_REV_SKIP,- ; Indicate reverse motion in progress UCB$L_MK_FLAGS(R3) ; 10$: SUBPUSH R0 ; Save space count MOVAB CMD_SPACE,R2 ; Get address of SPACE command BSBW SETUP_CMD ; Setup the command ADDL3 #<4+SCSI_SKIP$B_CNT+3>,-; Get address just beyond count field SCDRP$L_CMD_PTR(R5),R1 ; in the SCSI command SUBGET R0 ; Restore space count (but leave saved ; value on stack) .REPT 2 MOVB R0,-(R1) ; Copy two bytes of space count ASHL #-8,R0,R0 ; to the SCSI command .ENDR MOVB R0,-(R1) ; Copy the third byte MOVB #1,-(R1) ; Specify filemarks code CLRL UCB$L_ADDNL_INFO(R3) ; Initialize additional info field BSBW SEND_COMMAND ; Send the SCSI command BSBW CLEANUP_CMD ; Cleanup from the SCSI command SUBPOP R1 ; Restore space count BLBC R0,20$ ; Branch on error BITW #,- ; Are we at either BOT or EOT? UCB$L_DEVDEPEND+2(R3) BNEQ 15$ ; Yes, so the correct DEVDEPEND bit was ; set already in TRANS_SENSE_KEY as ; the Check Condition was processed. BISW #,- ; Else, there was a success with no UCB$L_DEVDEPEND+2(R3) ; Check Condition, so the correct ; DEVDEPEND bit must be set now. 15$: BICL #,- UCB$L_MK_FLAGS(R3) ; SUBRETURN ; Return to caller ; Perform a sanity check on the additional info (residue) value returned by ; the tape. If the tape told us it skipped more records than we asked for ; then set tape position lost. Note that the residue field, like the skip ; count, is a signed longword. Therefore, in order to do the comparison, ; we have to distinguish between cases of skipping forward vs. reverse. 20$: MOVL UCB$L_ADDNL_INFO(R3),R2 ; Get number of unskipped records (residue) BBC #UCB$V_REV_SKIP,- ; Branch if original skip count was UCB$L_MK_FLAGS(R3),25$ ; positive (skipping forward) MNEGL R1,R1 ; Invert sign of requested skip count MNEGL R2,R2 ; Invert sign of residue BGEQ 25$ ; Branch if residue was negative MNEGL R2,R2 ; Otherwise, reinvert sign of residue MNEGL R2,UCB$L_ADDNL_INFO(R3) ; Save residue value as a negative 25$: CMPL R2,R1 ; Compare requested skip count with residue BLEQU 27$ ; Branch if residue is reasonable BSBW SET_TAPE_LOST ; Otherwise, assume we are lost BRB 15$ ; Use common exit 27$: BBC #UCB$V_REV_SKIP,- ; Branch if original skip count was UCB$L_MK_FLAGS(R3),29$ ; positive (skipping forward) MNEGL R1,R1 ; Otherwise, reinvert sign of requested skip count 29$: SUBL2 UCB$L_ADDNL_INFO(R3),- ; Get the actual number of records R1 ; skipped BRB 15$ ; Use common exit .PAGE .SBTTL RECEIVE_DIAG - Receive diagnostic results ;+ ; RECEIVE_DIAG ; ; This routine sends a RECEIVE DIAGNOSTIC RESULTS command to the tape ; to obtain the controller hardware and software revision levels for ; TZ30s and TK50s. The revision info is translated from hex to text and ; saved in the UCB. All other drives return revision information their ; inquiry data, and it is already in text format. ; ; INPUTS: ; ; R3 - UCB address ; R4 - PDT address ; R5 - SCDRP address ; ; OUPUTS: ; ; R0 - Status ;- HEX_TO_TXT: .ASCII /0123456789ABCDEF/ RECEIVE_DIAG: IF_TK 5$ ; Branch if device is a TZK50 or TZ30 MOVZWL #SS$_NORMAL,R0 ; Otherwise, this routine is a NOP RSB ; Return to caller 5$: SUBSAVE ; Save return address MOVAL CMD_RECEIVE_DIAG,R2 ; Address of RECEIVE_DIAG command BSBW SETUP_CMD ; Perform setup for SCSI command MOVZWL #60,- ; Set up command-specific DMA timeout SCDRP$L_DMA_TIMEOUT(R5) BSBW SEND_COMMAND ; Send the SCSI command BLBC R0,10$ ; Branch on error CMPL SCDRP$L_TRANS_CNT(R5),#2; Sufficient diag data returned? BLSSU 20$ ; Branch if not MOVL SCDRP$L_SVA_USER(R5),R0 ; Get address of received diag data ASSUME SCSI_RCVD$B_HW_REV+1 EQ SCSI_RCVD$B_SW_REV PUSHL R3 ; Save reg MOVAL UCB$L_HW_REV(R3),R3 ; Get address of HW rev field in UCB MOVL #12,R1 ; Prepare to translate HW rev field 7$: EXTZV R1,#4,(R0),R2 ; Get a nibble of HW revision MOVB HEX_TO_TXT[R2],(R3)+ ; Translate to text and save in UCB SUBL #4,R1 ; Prepare to extract next nibble BGEQ 7$ ; Repeat for entire REV field POPL R3 ; Restore reg MOVZWL #SS$_NORMAL,R0 ; Set success status 10$: BSBW CLEANUP_CMD ; Cleanup from the SCSI command SUBRETURN ; Return to caller 20$: LOG_ERROR - ; Log an invalid diagnostic data error TYPE=DIAGNOSTIC_DATA,- ; VMS_STATUS=#SS$_NORMAL,-; UCB=R3 ; MOVZWL #SS$_DRVERR,R0 ; Set bad status BRB 10$ ; Use common exit .SBTTL REQUEST_SENSE - Send a request sense command ;+ ; REQUEST_SENSE ; ; This routine is called by SEND_COMMAND when a command fails with check ; condition status. A REQEST SENSE command is sent to the target. ; ; INPUTS: ; ; R3 - UCB address ; R4 - PDT address ; R5 - SCDRP address ; ; OUTPUTS: ; ; R0 - Status ; R1,R2 - Destroyed ; All other registers preserved ;- REQUEST_SENSE: SUBSAVE ; Save return address MOVAL CMD_REQUEST_SENSE,R2 ; Address of REQUEST_SENSE command BSBW SETUP_CMD ; Perform setup for SCSI command MOVL UCB$L_REQSNS_PAGE(R3),- ; Point to our pre-allocated resource R0 MOVL SCDRP$L_SVA_USER(R0),- ; Copy relevant fields SCDRP$L_SVA_USER(R5) ; so we use our pre-allocated resource MOVL SCDRP$L_SVAPTE(R0),- ; SCDRP$L_SVAPTE(R5) ; MOVL SCDRP$L_SVA_SPTE(R0),- ; SCDRP$L_SVA_SPTE(R5) ; MOVL SCDRP$L_SVA_DMA(R0),- ; SCDRP$L_SVA_DMA(R5) ; MOVW SCDRP$W_NUMREG(R0),- ; SCDRP$W_NUMREG(R5) ; MOVW SCDRP$W_MAPREG(R0),- ; SCDRP$W_MAPREG(R5) ; MOVL SCDRP$L_BCNT(R0),- ; SCDRP$L_BCNT(R5) ; MOVL SCDRP$W_BOFF(R0),- ; SCDRP$W_BOFF(R5) ; BISL #IRP$M_FUNC,- ; Set FUNC bit to indicate READ SCDRP$W_STS(R5) ; function SPI$SEND_COMMAND ; Send the SCSI command BICB #SCDRP$M_S0BUF!- ; Prevent CLEANUP_CMD from releasing SCDRP$M_BUFFER_MAPPED,-; our preallocated resource. SCDRP$L_SCSI_FLAGS(R5) ; CLRW SCDRP$W_NUMREG(R5) ; There are no resources leftover here! CLRW SCDRP$W_MAPREG(R5) ; BLBC R0,30$ ; Branch on SPI$SEND_COMMAND error ASSUME SCSI$C_GOOD EQ 0 MOVZBL @SCDRP$L_STS_PTR(R5),R1 ; Get SCSI status byte BICB #SCSI$M_STAT_MASK,R1 ; Clear reserved, vendor unique bits BNEQ 20$ ; Branch if bad status 10$: SUBRETURN ; Return to caller 20$: MOVL #SS$_MEDOFL,R0 ; Set bad status 30$: LOG_ERROR - ; Log a send command error TYPE=SEND_CMD_ERROR,- ; VMS_STATUS=R0,- ; UCB=R3 ; BRB 10$ ; Clean up and return to caller .SBTTL TEST_UNIT_READY - Send a test unit ready command ;+ ; TEST_UNIT_READY ; ; This routine sends a TEST UNIT READY command to the tape to determine ; whether the device is ready to receive read, write, and tape motion commands. ; ; INPUTS: ; ; R3 - UCB address ; R4 - PDT address ; R5 - SCDRP address ; ; OUTPUTS: ; ; R0 - Status ; R1,R2 - Destroyed ; All other registers preserved ;- TEST_UNIT_READY: SUBSAVE ; Save return address BISB #UCB$M_WAITRDY_INPROG,- ; Set wait for unit ready in progress UCB$L_MK_FLAGS(R3) ; MOVAL CMD_TEST_UNIT_READY,R2 ; Address of TEST UNIT READY command BSBW SETUP_CMD ; Perform setup for SCSI command BSBW SEND_COMMAND ; Send the SCSI command BSBW CLEANUP_CMD ; Cleanup from the SCSI command BICB #UCB$M_WAITRDY_INPROG,- ; Clear wait for unit ready in progress UCB$L_MK_FLAGS(R3) ; SUBRETURN .SBTTL MODE_SENSE - Send a mode sense command ;+ ; MODE_SENSE ; ; This routine sends a MODE SENSE command. The only mode sense information ; currently used is the write protect bit (bit 7 of the device-specific param) ; and (if it is available) the density from the first byte of the first block ; descriptor. ; ; INPUTS: ; ; R3 - UCB address ; R4 - PDT address ; R5 - SCDRP address ; ; OUPUTS: ; ; R0 - Status ;- MODE_SENSE: SUBSAVE ; Save return address SUBPUSH R1 ; Save register SUBPUSH R2 ; Save register ; Allocate a local copy of the mode page descriptors MOVL #DESCRIPTOR_SIZE,R1 ; Size of descriptor array BSBW ALLOC_POOL ; Allocate memory PUSHR #^M MOVC3 #DESCRIPTOR_SIZE,- ; Copy the descriptors DESCRIPTOR_BASE,(R2) POPR #^M MOVL #SCSI$PGCD$C_VENDOR_SPECIFIC,R0 ; Vendor specific page code MOVAL VENDOR_UNIQUE_PAGE_DESC(R2),R1 ; Address of descriptors BSBW DO_MODE_PAGE ; Process the page BLBS R0,10$ ; Branch on success BRW 50$ ; Branch on failure 10$: BICL #MT$M_HWL,- ; Assume tape is not hardware w UCB$L_DEVDEPEND(R3) ; locked MOVL #VENDOR_UNIQUE_DEVSPC_DESC,R0 ; Offset to dev specific param ADDL2 R2,R0 ; Get address of dev specific d BBC #SCSI$DK$V_WP,- MODE_DESC$L_ACTUAL(R0),20$ ; Is write-protect bit clear? BISL #MT$M_HWL,- ; Indicate that tape is in fact UCB$L_DEVDEPEND(R3) ; write locked 20$: IFNOT_TSZ07 30$ ; Branch if not TSZ07 MOVL #VENDOR_UNIQUE_BDLEN_DESC,R0 ; Offset to block desc length ADDL2 R2,R0 ; Get address of blk desc len TSTL MODE_DESC$L_ACTUAL(R0) ; Check for a block descriptor, BEQL 30$ ; Branch if no block descriptor CLRL R1 ; Clear in case default (0) den MOVL #VENDOR_UNIQUE_DENS_DESC,R0 ; Offset to block descriptor ADDL2 R2,R0 ; Get address of block desc MOVB MODE_DESC$L_ACTUAL(R0),- ; Get the current device density UCB$W_MK_DENSITY(R3) ; and save it BEQL 25$ ; If default (0), done ADDW3 #2,UCB$W_MK_DENSITY(R3),- ; Translate to VMS density R1 ; 25$: INSV R1,#MT$V_DENSITY,- ; Store density in device #MT$S_DENSITY,- ; characteristics UCB$L_DEVDEPEND(R3) ; 30$: MOVZWL #SS$_NORMAL,R0 ; Set success status 40$: PUSHL R0 ; Save return status MOVL R2,R0 ; Get memory base address BSBW DEALLOC_POOL ; Release memory POPL R0 ; Restore return status SUBPOP R2 ; Restore register SUBPOP R1 ; Restore register SUBRETURN ; Return to caller 50$: ; Since DO_MODE_PAGE overwrote the SEND_COMMAND return status with SS$_NODATA ; whenever a bad SCSI status was returned, restore the SEND_COMMAND status ; for that case. Note: DO_MODE_PAGE propagates the SEND_COMMAND status ; for all other cases. CMPL #SS$_NODATA,R0 ; Do we need to restore status? BNEQ 40$ ; No, just exit MOVL UCB$L_SAV_SEND_STATUS(R3),R0 ; Restore status BRW 40$ .SBTTL MODE_SELECT - Send a mode select command ;+ ; MODE_SELECT ; ; This routine sends a MODE_SELECT command to the tape to set up various drive ; characteristics including default block size, buffered (streaming) mode, ; number of fillers, and infinite reselection timeout. ; ; This command is different from others in that each device has its own ; vendor-unique data. At unit init, when the device type was determined, a ; pointer to this vendor-unique mode select data was placed in the UCB. This ; data is appended to the standard end of the standard mode select data. ; ; Rather than have a unique SCSI_CMD table entry for device type, we have ; a template entry containing the generic mode select data, and a seond entry ; used to actually build the mode select command. The template entry is first ; copied to the active entry, then the BCNT and parameter list length fields ; are increased by the length of the vendor-unique info length. ; ; INPUTS: ; ; R3 - UCB address ; R4 - PDT address ; R5 - SCDRP address ; ; OUPUTS: ; ; R0 - Status ;- MODE_SELECT: SUBSAVE ; Save return address SUBPUSH R1 ; Save register SUBPUSH R2 ; Save register SUBPUSH R4 ; Save register ; Restore original mode select command from template so that previous ; vendor specific data does not get propagated into new commands. MOVAL CMD_MODE_SELECT_TMP,R1 ; Get the mode select command template MOVAL CMD_MODE_SELECT,R2 ; Get the mode select command PUSHR #^M ; Save regs MOVC3 #MODE_SEL_TMP_LEN,- ; Copy the mode select infro from the (R1),(R2) ; template to the active command POPR #^M ; Restore regs MOVL UCB$L_MSEL_INFO(R3),R4 ; Get address of vendor-unique info MOVZBL (R4)+,R0 ; Get length of vendor-unique info ; Modify both the data length in the SCSI command and the associated DMA ; length to account for vendor specific data. ADDB R0,5(R2) ; Increase parameter list length in cmd ; by vendor-unique length ADDW R0,9(R2) ; Increase BCNT by vendor-unique length MOVL #DESCRIPTOR_SIZE,R1 ; Size of descriptor array BSBW ALLOC_POOL ; Allocate memory space PUSHR #^M MOVC3 #DESCRIPTOR_SIZE,- ; Copy the descriptors DESCRIPTOR_BASE,(R2) POPR #^M ; Assume no vendor unique info MOVAL BASIC_SELECT_DESC(R2),- R1 ; Address of descriptors 15$: MOVL UCB$L_MSEL_INFO(R3),R4 ; Get address of vendor-unique info MOVZBL (R4)+,R0 ; Get length of vendor unique info BNEQ 18$ ; If nonzero, go get it ; If here, then there is no vendor unique info. Set density if it's a TSZ07. IFNOT_TSZ07 25$ ; If not a TSZ07, join common code ADDL3 - ; Get address of density descriptor #BASIC_SELECT_SEL_DENS_DESC,- R2,R1 MOVB UCB$W_MK_DENSITY(R3),- ; Set desired density in descriptor MODE_DESC$L_DESIRED(R1) MOVAL BASIC_SELECT_DESC(R2),- R1 ; Address of descriptors BRW 25$ ; Join common code to do Mode Select ; Yes, there is vendor unique info 18$: MOVAL (R2),R1 MULL3 #8,R0,(R1) ; Get bit size of vendor info MOVAL (R2),R1 20$: MOVB (R4)+,(R1)+ ; Copy a byte of vendor-unique data SOBGTR R0,20$ ; Repeat for all vendor-unique data MOVAL VENDOR_UNIQUE_SEL_DESC(R2),R1 ; Address of descriptors ; Common code 25$: MOVL #SCSI$PGCD$C_VENDOR_SPECIFIC,R0 ; Vendor specific page code BSBW DO_MODE_PAGE ; Process the page BLBS R0,30$ ; LOG_ERROR - ; Log a send command error TYPE=SEND_CMD_ERROR,- ; VMS_STATUS=R0,- ; UCB=R3 ; MOVL #SS$_DRVERR,R0 ; Return error 30$: PUSHL R0 ; Save return status MOVL R2,R0 ; Get memory base address BSBW DEALLOC_POOL ; Free allocated memory POPL R0 ; Restore return status SUBPOP R4 ; Restore register SUBPOP R2 ; Restore register SUBPOP R1 ; Restore register SUBRETURN ; Return to caller .SBTTL DO_8MM_COPY_CHK - 8mm COPY from BOT processing ;+ ; DO_8MM_COPY_CHK ; ; This routine is called from IO_WRITPBLK as a result of a SCSI error ; status returned in response to an attempt to begin a $COPY operation ; to a tape positioned at BOT and containing no files ($INIT done but ; tape void of files). ; ; During $COPY from BOT processing, the positioning command SPACE RECORD ; REVERSE is used to position the tape after the "VOL1..." record (1) and ; is followed by a WRITE command to write the "HDR1..." record (2). This is ; an illegal command sequence on the 8mm devices and results in CHECK ; CONDITION - ILLEGAL REQUEST status (SS$_DRVERR). ; ; To accommodate the 8mm behavior this routine creates an additional ; SCDRP and uses it and an S0 buffer to generate a REWIND, READ rec 1 ; ("VOL1..."), REWIND, WRITE rec 1 ("VOL1...") sequence to accomplish ; correct positioning for the write that follows the "VOL1..." record (1). ; ; ; INPUTS: ; ; R3 - UCB address ; R4 - PDT address ; R5 - SCDRP address ; ; OUTPUTS: ; ; R0 - Status ; R1, R2 - Destroyed ; All other registers preserved ;- DO_8MM_COPY_CHK: SUBSAVE MOVL R5,UCB$L_SCDRP_SAV1(R3) ; Save original SCDRP address BSBW ALLOC_SCDRP ; Allocate additional SCDRP ; ; This seems to be the COPY from BOT situation...REWIND and ; READ the first record to determine if it is "VOL1...". ; BSBW DO_8MM_REWIND ; Do 1st Rewind BLBS R0,20$ ; Br if ok BRW 70$ ; Error status -- return 20$: MOVL SCDRP$L_CMD_BUF(R5),R0 ; Get address of command buffer SPI$DEALLOCATE_COMMAND_BUFFER ; Dealloc command buffer MOVAL CMD_READ_8MM,R2 ; Address of 8mm READ command BSBW SETUP_CMD ; Perform setup for SCSI command ADDL3 #<4+SCSI_RD$B_LEN+3>,- ; Get address just beyond transfer SCDRP$L_CMD_PTR(R5),R0 ; length field in the SCSI command MOVAL SCDRP$L_BCNT(R5),R1 ; Get addr of byte count field in SCDRP .REPT 3 MOVB (R1)+,-(R0) ; Fill in the transfer length .ENDR MOVW #IRP$M_FUNC,- ; Set the FUNC bit to indicate a READ SCDRP$W_STS(R5) BSBW SEND_COMMAND ; Send the SCSI command BLBS R0,25$ ; Br if ok BRW 70$ ; Error status -- return 25$: CMPL @SCDRP$L_SVA_USER(R5),- ; Is this the "VOL1..." record #^A/VOL1/ BEQL 30$ ; Br if it is BRW 70$ ; It isn't -- just return 30$: MOVL SCDRP$L_CMD_BUF(R5),R0 ; Get address of command buffer SPI$DEALLOCATE_COMMAND_BUFFER ; Dealloc command buffer (but don't ; unmap data buffer) INCL UCB$L_RECORD(R3) ; Keep track of forward tape position ; ; Rewind again and WRITE "VOL1..." ; SUBPUSH SCDRP$L_BCNT(R5) ; Save the byte count for use by WRITE BSBW DO_8MM_REWIND ; Do 2nd Rewind BLBS R0,40$ ; Br if error SUBPOP SCDRP$L_BCNT(R5) ; Restore the byte count BRB 70$ 40$: MOVL SCDRP$L_CMD_BUF(R5),R0 ; Get address of command buffer SPI$DEALLOCATE_COMMAND_BUFFER ; Dealloc command buffer ; ; The SCDRP still points to the S0 buffer containing the "VOL1..." ; record that was READ above, so WRITE it back to tape. Once this is ; done, subsequent WRITES will complete successfully. ; MOVAL CMD_WRITE_8MM,R2 ; Address of 8mm WRITE command BSBW SETUP_CMD ; Perform setup for SCSI command SUBPOP SCDRP$L_BCNT(R5) ; Restore the byte count ADDL3 #<4+SCSI_WRT$B_LEN+3>,- ; Get address just beyond transfer SCDRP$L_CMD_PTR(R5),R0 ; length field in the SCSI command MOVAL SCDRP$L_BCNT(R5),R1 ; Get addr of byte count field in SCDRP .REPT 3 MOVB (R1)+,-(R0) ; Fill in the transfer length .ENDR CLRW SCDRP$W_STS(R5) ; Clr the FUNC bit to indicate a WRITE BSBW SEND_COMMAND ; Send the SCSI command BLBS R0,80$ ; Br if no error 70$: BSBW SET_TAPE_LOST ; Set position lost LOG_ERROR - ; Log an extended sense data error TYPE=EXTND_SENSE_DATA,- ; VMS_STATUS=#SS$_NORMAL,-; UCB=R3 ; MOVZWL #SS$_DRVERR,R0 ; Return error status 80$: INCL UCB$L_RECORD(R3) ; Keep track of forward tape position BSBW CLEANUP_CMD ; Cleanup from the SCSI command BSBW DEALLOC_SCDRP ; Dealloc additional SCDRP MOVL UCB$L_SCDRP_SAV1(R3),R5 ; Restore original SCDRP MOVL R5,UCB$L_SCDRP(R3) ; copy to UCB 90$: CLRB UCB$B_8MM_CHK(R3) ; Clear COPY check in progress flag SUBRETURN .SBTTL DO_8MM_REWIND - Rewind the 8MM device ;+ ; This subroutine is called from DO_8MM_COPY_CHK to send a rewind command to ; the device. ; ; INPUTS: ; ; R3 - UCB address ; R4 - PDT address ; R5 - SCDRP address ; ; OUTPUTS: ; ; R0 - Status ; R1, R2 - Destroyed ; All other registers preserved ;- DO_8MM_REWIND: SUBSAVE MOVAL CMD_REWIND,R2 ; Address of REWIND command BSBW SETUP_CMD ; Setup the command BSBW SEND_COMMAND ; Send the command BLBC R0,20$ ; Br if error BISW #,- ; Mark the beginning of tape UCB$L_DEVDEPEND+2(R3) ; BICW #,- ; and end of file UCB$L_DEVDEPEND+2(R3) ; CLRL UCB$L_RECORD(R3) ; Tape is positioned at start MNEGL #2,UCB$L_PREV_TM(R3) ; No previous tape mark 20$: SUBRETURN .SBTTL + .SBTTL + UTILITY ROUTINES .SBTTL + .SBTTL SET_UNIT_ONLINE - Issue SCSI commands to bring unit online ;+ ; SET_UNIT_ONLINE ; ; This routine attempts to bring a unit online. First, INQUIRY and RECEIVE ; DIAGNOSTIC RESULTS commands are sent to the drive to ensure that it can ; communicate, that it's a valid tape-like device, and that it's up to rev. ; A check is also done to see if the unit supports data compaction. ; If a failure occurs, a fork thread is set up to periodically poll the drive. ; ; INPUTS: ; ; R5 - UCB address ; ; OUTPUTS: ; ; R0-R5 - Destroyed ; ; ONLINE bit set in the UCB, BSY bit cleared ;- SET_UNIT_ONLINE: 10$: MOVL UCB$L_PDT(R5),R4 ; Get PDT address MOVL R5,R3 ; Copy UCB address BSBW ALLOC_SCDRP ; Allocate an SCDRP BSBW INQUIRY ; Send an inquiry command BLBC R0,15$ ; Branch on error BSBW TEST_UNIT_READY ; Clear out a possible pending UNIT ; ATTENTION sense key IF_TK50 12$ ; If TKZ50 do not send a RECEIVE ; DIAGNOSTIC command. BSBW RECEIVE_DIAG ; Send a receive diag data command BLBC R0,15$ ; Branch on error 12$: BSBW CHECK_COMPACTION_SUPPORT ; check if device supports data compaction IF_TZK10 13$ ; TZK10 does not support BT bit of 0 IF_TSZ07 13$ ; TSZ07/5 don't BLANK-CHECK when IF_TSZ05 13$ ; hitting end-of-volume IF_TKZ09 13$ ; TKZ09: READ POSITION data may be ; unpredictable after rewind IF_TKZ60 13$ ; Errors during space by filemarks BISL2 #,- ; and indicate UCB$L_MK_FLAGS(R3) ; initial READ POSITION pending IF_TK50 14$ ; If TZK50 do not output revsion ; check errorlog. 13$: BSBW CHECK_REV_LEVEL ; Check for minimum rev level 14$: MOVZWL #SS$_NORMAL,R0 ; Ignore revision check failure (bring ; drive online regardless of rev) 15$: BSBW DEALLOC_SCDRP ; Release the SCDRP MOVL R3,R5 ; Copy UCB address BLBC R0,30$ ; Branch if error BISW #UCB$M_ONLINE!- ; Set the device online, busy UCB$M_BSY,- ; UCB$W_STS(R5) ; ; Since we don't know where the tape is at this point, initialize state in the ; UCB to indicate that position is lost. Normally the tape position is at BOT ; when the driver is loaded. However, it's possible that if we're booting ; standalone backup from tape, the position could be at some random point in ; the tape. Therefore, place it safe and assume we're lost. BSBW SET_TAPE_LOST ; Indicate we don't know where tape is .IF DEFINED DEBUG MOVL #SS$_NORMAL,R0 ; Set normal status BSBW TRACE_QIO_STAT ; Save the final QIO status in trace buf .ENDC REMQUE @UCB$L_IOQFL(R5),R3 ; Any I/O queued to this device? BVS 20$ ; Branch if not JMP G^IOC$INITIATE ; Go initiate the I/O 20$: BICW #UCB$M_BSY,- ; Clear the busy bit UCB$W_STS(R5) ; RSB ; Return to caller ; We've tried and failed at least once to send an inquiry command. Clear the ; online and busy bits and set up a fork thread to poll the device once ; a minute in an attempt to bring it online. In the meantime, any I/O queued to ; this device will fail with device offline status. Flush the queue of any I/O ; that might have been queued to the device during the period that it was ; online but busy. 30$: BICW #UCB$M_ONLINE,- ; Set the unit offline, but leave device UCB$W_STS(R5) ; busy since polling is in progress 40$: REMQUE @UCB$L_IOQFL(R5),R3 ; Remove an IRP from the queue BVS 45$ ; Branch if queue was empty INSQUE (R3),- ; Place on end of flush queue @UCB$L_FLUSH_IOQBL(R5) ; BRB 40$ ; Repeat until I/O queue is empty 45$: REMQUE @UCB$L_FLUSH_IOQFL(R5),-; Remove an IRP from the flush queue R3 ; BVS 50$ ; Branch if queue was empty MOVL R3,UCB$L_IRP(R5) ; Copy IRP addess to UCB MOVL #SS$_DEVOFFLINE,R0 ; Set device offline status JSB G^IOC$REQCOM ; Complete the QIO BRB 45$ ; Continue to flush queue 50$: MK_WAIT #60 ; Wait for 60 seconds BRW 10$ ; And try again .SBTTL SET_TAPE_LOST - Set state in UCB to indicate tape position lost ;+ ; SET_TAPE_LOST ; ; This routine sets state in thge UCB to indicate that the tape position is ; lost, and is called at unit init and any time are error occurs which causes ; us to suspect that we may have lost tape position. ; ; INPUTS: ; ; R3 - UCB ; ; OUTPUTS: ; ; MT$M_LOST set in DEVDEPEND of UCB ; MT$M_BOT, MT$M_EOT, MT$M_EOF cleared in DEVDEPEND of UCB ; UCB$L_PREV_TM gets -2 (no previous TM found) ; All registers preserved ;- SET_TAPE_LOST: BITL #UCB$M_COMPCHK_IN_PROG,-; Compaction check in progress? UCB$L_MK_FLAGS(R3) ; BNEQ 10$ ; Skip set lost if yes BISW #MT$M_LOST@-16,- ; Set position lost UCB$L_DEVDEPEND+2(R3) ; BICW #,- ; and end of file UCB$L_DEVDEPEND+2(R3) ; MNEGL #2,UCB$L_PREV_TM(R3) ; No previous tape mark 10$: RSB ; Return to caller .SBTTL WAIT_UNIT_READY - Wait for unit to become ready ;+ ; WAIT_UNIT_READY ; ; This routine polls once every READY_POLL_INTERVAL seconds, waiting for the ; drive to become ready. The drive can be not ready for two reasons: a tape ; is not mounted in the drive; or a previous rewind/nowait command is still ; in progress. In SCSI, there's no way to distinguish between these two cases. ; However, the driver keeps track of the time that the last rewind command ; sent to the drive should have completed (in UCB$L_REWIND_TIME), and polling ; continues only until that time is exceeded. This prevents unnecessary polling ; when the tape drive is clearly not ready. In the case of a media loader, ; a previous UNLOAD operation to change cartridges will also make the unit ; not ready for some time. In this case we wait until UCB$L_UNLOAD_TIME is ; reached to decide when the tape is ready. ; ; INPUTS: ; ; R3 - UCB address ; R4 - PDT address ; R5 - SCDRP address ; ; OUTPUTS: ; ; R0 - Status ; R1,R2 - Destroyed ; All other registers preserved ;- WAIT_UNIT_READY: SUBSAVE ; Save return address 10$: BSBW TEST_UNIT_READY ; Execute TEST UNIT READY command BLBC R0,30$ ; Branch on error 20$: BICL #UCB$M_UNLOAD_INPROG,- ; Clear UNLOAD bit UCB$L_MK_FLAGS(R3) SUBRETURN ; Return to caller 30$: CMPL R0,#SS$_DEVOFFLINE ; Device offline? BNEQ 20$ ; Branch if not MOVL #SS$_ABORT,R0 ; Assume I/O got canceled BBS #UCB$V_CANCEL,- ; Branch if it did UCB$W_STS(R3),20$ ; MOVZWL #SS$_MEDOFL,R0 ; Assume device is not ready BBS #UCB$V_UNLOAD_INPROG,- ; Branch if last operation was an UNLOAD UCB$L_MK_FLAGS(R3),35$ ; that we may be waiting for a media stacker CMPL G^EXE$GL_ABSTIM,- ; Could the not ready condition be UCB$L_REWIND_TIME(R3) ; due to a recent rewind or unload command? BGEQU 20$ ; Branch if not, no need for more polling BRB 40$ ; Continue polling for rewind completion 35$: CMPL G^EXE$GL_ABSTIM,- ; Could the not ready condition be UCB$L_UNLOAD_TIME(R3) ; due to a recent unload operation? BGEQU 20$ ; Branch if not, no need for more polling 40$: MK_WAIT #READY_POLL_INTERVAL,- ; Wait a few seconds UCB=R3 ; BRB 10$ ; Continue polling .SBTTL CHECK_EOV - Check for logical end-of-volume ;+ ; CHECK_EOV ; ; This routine checks for logical end-of-volume, which has the following ; prerequisites: ; ; - Two consecutive tape marks are encountered during ; a SKIP_FILE or SKIP_RECORD operation ; ; If logical end-of-volume is detected, the tape is backspaced one record to ; position it between the two tapemarks. ; ; INPUTS: ; ; R0 - SS$_ENDOFVOLUME ; R3 - UCB address ; R4 - PDT address ; R5 - SCDRP address ; ; UCB$L_RECORD - Current record (on which a tape mark has been found) ; UCB$L_PREV_TM - Record of last tapemark encountered ; ; OUPUTS: ; ; R0 - Status (SS$_ENDOFVOLUME or SS$_ENDOFFILE) ; R2 - Destroyed ; All other registers preserved ; ; UCB$L_PREV_TM - Updated ;- CHECK_EOV: SUBSAVE ; Save return address SUBL3 UCB$L_PREV_TM(R3),- ; Calculate difference between last UCB$L_RECORD(R3),-(SP) ; tapemark and this one. If consecutive, DECL (SP)+ ; then check for logical end-of-volume BNEQ 20$ ; Branch if not ; Logical end-of-volume detected. Position the tape between the two tapemarks ; by backspacing one record. MOVL #-1,R0 ; Prepare to back up one record SUBPUSH R1 ; Save R1 BSBW SKIP_RECORD ; Position tape between filemarks SUBPOP R1 ; Restore R1 CMPL R0,#SS$_ENDOFFILE ; EOF status? BNEQ 30$ ; Branch if not, serious problem exists MOVL #SS$_ENDOFVOLUME,R0 ; Return end of volume status 20$: MOVL UCB$L_RECORD(R3),- ; Update previous tapemark field in UCB$L_PREV_TM(R3) ; UCB with current record number 30$: SUBRETURN ; Return to caller .SBTTL MK_WAIT - Stall for the specified number of seconds ;+ ; MK_WAIT ; ; This routine is used by the MK_WAIT macro to stall a thread for a specified ; number of seconds. It sets the timeout bit in the UCB and relies on the ; device timeout mechanism to resume the stalled thread. ; ; INPUTS: ; ; IPL - 31 ; R5 - UCB address ; (SP) - Return address ; 4(SP) - Wait time in seconds ; 8(SP) - Saved IPL ; ; OUPUTS: ; ; Stack - Return address, wait time, IPL removed ; Control returns to caller's caller ; All registers preserved ; ; NOTE: The use of the MK_WAIT macro destroyes R0-R3 ;- MK_WAIT: MOVQ R3,UCB$L_FR3(R5) ; Save R3 and R4 in fork block ADDL3 #2,(SP)+,UCB$L_FPC(R5) ; Save return address in fork block BISW #UCB$M_TIM,UCB$W_STS(R5); Set timer expected bit ADDL3 (SP)+,G^EXE$GL_ABSTIM,- ; Set up timeout time in UCB UCB$L_DUETIM(R5) ; BICW #UCB$M_TIMOUT,- ; Clear timer expired bit UCB$W_STS(R5) ; ENBINT ; Reenable interrupts RSB ; Return to caller's caller .SBTTL SEND_COMMAND - Send a SCSI command ;+ ; SEND_COMMAND ; ; This routines sends a command to the SCSI device. It returns any failing ; port status to the caller. If the port status is success, it checks the ; SCSI status byte. If a check condition status is returned, a request ; sense command is sent to the target and the sense key is translated into a ; VMS status code, which is returned as status. ; ; INPUTS: ; ; R3 - UCB address ; R4 - PDT address ; R5 - SCDRP address ; ; OUTPUTS: ; ; R0 - Status ; R1,R2 - Destroyed ; All other registers preserved ;- SEND_COMMAND:: SUBSAVE ; Save return address MOVB #BUSY_RETRY_CNT,- ; Initialize busy retry count UCB$B_BUSY_RETRY(R3) ; BICB #UCB$M_ADDNL_INFO,- ; Indicate additional info is not UCB$L_MK_FLAGS(R3) ; valid 1$: SPI$SEND_COMMAND ; Send the SCSI command BLBC R0,20$ ; Branch on error ASSUME SCSI$C_GOOD EQ 0 MOVZBL @SCDRP$L_STS_PTR(R5),R1 ; Get SCSI status byte BICB #SCSI$M_STAT_MASK,R1 ; Clear reserved, vendor unique bits BNEQ 30$ ; Branch if bad status .IF DEFINED MULTI_VOLUME_TEST ; Code for testing multi-volume savesets MOVL UCB$L_IRP(R3),R1 ; Get IRP address BEQL 6$ ; Branch if not available EXTZV #IRP$V_FCODE,- ; Extract function code #IRP$S_FCODE,- ; IRP$W_FUNC(R1),R1 ; CMPL R1,#IO$_WRITEPBLK ; Write function? BEQL 5$ ; Branch if so CMPL R1,#IO$_WRITEOF ; Write tapemark? BNEQ 6$ ; Branch if not 5$: CMPL UCB$L_RECORD(R3),#^X50 ; Beyond artificial end of tape mark? BGEQUW 40$ ; Branch if so 6$: .ENDC 10$: MOVL R0,UCB$L_SAV_SEND_STATUS(R3) ; Save status SUBRETURN ; Return to caller ; The port returned bad status from SPI$SEND_CMD. Log an error and return to ; the caller. 20$: CMPL R0,#SS$_ABORT ; Aborted command? BEQL 10$ ; Branch if so, don't log an error LOG_ERROR - ; Log a send command error TYPE=SEND_CMD_ERROR,- ; VMS_STATUS=R0,- ; UCB=R3 ; BRB 10$ ; Use common exit ; A bad SCSI status code was returned. If the code is a check condition, then ; send a request sense command to the device. Otherwise, the status code is ; something unexpected. Log an error and return SS$_MEDOFL status. 30$: CMPB #SCSI$C_CHECK_CONDITION,R1; Check condition status? BEQL 40$ ; Branch if so CMPB #SCSI$C_BUSY,R1 ; Device busy? BNEQ 35$ ; Branch if not BBC #UCB$V_WAITRDY_INPROG,- ; Branch if waiting for unit ready is UCB$L_MK_FLAGS(R3),35$ ; not in progress MOVL #SS$_DEVOFFLINE,R0 ; Return device off line status BRW 10$ ; Return to caller 35$: DECB UCB$B_BUSY_RETRY(R3) ; Decrement busy retry count BLEQ 37$ ; Branch if count expired MK_WAIT #1,UCB=R3 ; Wait a second BRW 1$ ; Go send the command again 37$: LOG_ERROR - ; Log a send command error TYPE=SEND_CMD_ERROR,- ; VMS_STATUS=#SS$_NORMAL,-; UCB=R3 ; MOVL #SS$_MEDOFL,R0 ; Return a generic status code BRW 10$ ; Return to caller ; A check condition status code was returned. Save the original SCDRP address ; allocate a second one and send a request sense command. If the request ; sense succeeds, translate the sense key to a VMS status code and return that ; as the status code for the original command. 40$: TSTB UCB$B_8MM_CHK(R3) ; If an 8mm COPY check flag is set, BEQL 45$ ; just Cleanup and Deallocate... ; BSBW CLEANUP_CMD ; Clean up the request sense command ; BSBW DEALLOC_SCDRP ; Deallocate the request sense SCDRP BRW 10$ ; Return to caller 45$: CLRB UCB$B_SENSE_KEY(R3) ; Initialize saved sense key field MOVL R5,UCB$L_SCDRP_SAV1(R3) ; Save original SCDRP address BSBW ALLOC_SCDRP ; Allocate an additional SCDRP BSBW REQUEST_SENSE ; Send a request sense command BLBC R0,50$ ; Branch on error BSBW LOG_EXTND_SENSE ; Log an extended sense error BSBW SAVE_ADDNL_INFO ; Save any valid additional information ; from the extended sense data BSBW TRANS_SENSE_KEY ; Translate the extended sense key to ; a VMS status code in R0 50$: BSBW CLEANUP_CMD ; Clean up the request sense command BSBW DEALLOC_SCDRP ; Deallocate the request sense SCDRP MOVL UCB$L_SCDRP_SAV1(R3),R5 ; Restore original SCDRP address MOVL R5,UCB$L_SCDRP(R3) ; Copy it to the UCB BRW 10$ ; Return to caller .SBTTL LOG_EXTND_SENSE - Log an extended sense data error ;+ ; LOG_EXTND_SENSE ; ; This routine logs an extended sense data error. ; ; Certain errors are suppressed including: ; ; - UNIT ATTENTIONS (we can't tell the difference between device resets ; and media changes, so we log nothing) ; ; - A NOT READY sense key. ; ; Before logging the error, play a trick with the two SCDRPs currently in use. ; Since we're really interested in logging the contents of the original command, ; and not the request sense command, copy the address of the original command ; buffer into the SCDRP for the request sense command. This causes the contents ; of the original command (a read or write, for example) to be logged along ; with the contents of the request sense data returned from the request sense ; command. ; ; INPUTS: ; ; R3 - UCB address ; R4 - PDT address ; R5 - SCDRP address ; ; UCB$L_SCDRP_SAV1(R3) - Address of SCDRP for original command ; ; OUTPUTS: ; ; R0-R2 - Destroyed ; All other registers preserved ;- LOG_EXTND_SENSE: MOVL SCDRP$L_SVA_USER(R5),R0 ; Get address of extended sense data BITB #SCSI$SNS$M_ILI,- ; Illegal length indicator? SCSI$SNS$R_FLAGS(R0) ; BNEQW 10$ ; Branch if so BITB #,- ; End of medium? SCSI$SNS$R_FLAGS(R0) ; BEQL 5$ ; Branch if not ; The following sequence checks that a skip record operation which encounters ; a tape mark, BOT, or EOT returns valid additional info (residue) from the ; subsequent request sense command. If not, the driver has no way of keeping ; track of the UCB$L_RECORD value. BBS #UCB$V_SKIP_INPROG,- ; Branch if skip command in UCB$L_MK_FLAGS(R3),4$ ; progress BRW 10$ ; Branch for no skip record in progress 4$: ASSUME SCSI$SNS$V_VALID EQ 7 TSTB SCSI$SNS$R_ERROR_CODE_FIELD(R0) ; Valid additional info? BGEQ 7$ ; Branch if not BRW 10$ ; Otherwise, leave without logging an error 5$: EXTZV #SCSI$SNS$V_SENSE_KEY,- ; Get the extended sense key #SCSI$SNS$S_SENSE_KEY,- ; SCSI$SNS$R_FLAGS(R0),R1 ; BNEQ 6$ ; Branch if sense data BRW 10$ ; Branch if no sense data 6$: CMPB R1,- ; Unit attention sense key? #SCSI$C_UNIT_ATTENTION ; BNEQ 66$ ; Branch if not unit attention BRW 10$ ; Branch if so, don't log an error 66$: CMPB R1,#SCSI$C_NOT_READY ; Device not ready sense key? BNEQ 7$ ; Branch if not 'device not ready' BRW 10$ ; Branch if so, don't log an error 7$: PUSHL SCDRP$L_CMD_PTR(R5) ; Save address of request sense cmd MOVL UCB$L_SCDRP_SAV1(R3),R0 ; Get original command's SCDRP address MOVL SCDRP$L_CMD_PTR(R0),- ; Copy address of original command to SCDRP$L_CMD_PTR(R5) ; request sense's SCDRP MOVB #SCSI$C_CHECK_CONDITION,- ; Original command failed with request @SCDRP$L_STS_PTR(R5) ; sense status ; ; If this is an 8mm device we may have arrived here as a result of the ; SPACE REC REV - WRITE SCSI command sequence which $COPY from BOT processing ; generates. That command sequence is illegal on the 8mm and results ; in a CHECK CONDITION - ILLEGAL REQUEST error response from the device. ; DO_8MM_COPY_CHK will subsequently generate an alternate SCSI command sequence ; that will recover from the error and accommodate the 8mm behavior. ; IFNOT_8MM 700$ ; Br if not 8MM CMPB R1,- ; Illegal Request sense key? #SCSI$C_ILLEGAL_REQUEST BNEQ 700$ ; Br if not Illegal Request BBC #DEV$V_MNT,- ; Br if not mounted UCB$L_DEVCHAR(R3),8$ BBS #DEV$V_FOR,- ; Br if mounted foreign UCB$L_DEVCHAR(R3),8$ MOVL UCB$L_IRP(R3),R1 ; Restore IRP address EXTZV #IRP$V_FCODE,- ; Extract I/O function code #IRP$S_FCODE,- ; IRP$W_FUNC(R1),R1 ; CMPB #IO$_WRITEPBLK,R1 ; Write in progress? BNEQ 8$ ; Br if not CMPL #1,UCB$L_RECORD(R3) ; Br if the record # is not consistent BNEQ 8$ ; with the $COPY from BOT error MOVB #1,UCB$B_8MM_CHK(R3) ; Set COPY check in progress flag BRB 9$ ; Go to return 700$: CMPB R1,#SCSI$C_BLANK_CHECK ; Check for blank-check error and BOT, BNEQ 8$ BBS #UCB$V_SPACEFILE_INPROG,- ; Suppress if SPACE FILEMARKS just UCB$L_MK_FLAGS(R3),9$ ; hit logical EOV TSTL UCB$L_RECORD(R3) ; Supress logging error since it is - BEQL 9$ ; new tape. mcy 8/28/92 ; Don't log certain Illegal Request errors that can result from sending ; e.g. a READ POSITION commnand to a target that does not support it. 8$: CMPL R1,#SCSI$C_ILLEGAL_REQUEST ; Illegal command sense key? BNEQ 36$ ; No, so log it BBS #UCB$V_READPOSCHK_IN_PROG,- ; Is READ POSITION check in progress? UCB$L_MK_FLAGS(R3),9$ ; If so, don't log error 36$: LOG_ERROR - ; Log an extended sense data error TYPE=EXTND_SENSE_DATA,- ; VMS_STATUS=#SS$_NORMAL,-; UCB=R3 ; 9$: POPL SCDRP$L_CMD_PTR(R5) ; Restore address of request sense cmd 10$: RSB ; Return to caller .SBTTL TRANS_SENSE_KEY - Translate extended sense key to VMS status code ;+ ; TRANS_SENSE_KEY ; ; This routine translates an extended sense key to a VMS status code using ; SENSE_KEY_TABLE. If the sense key is not found in the table (indicating an ; invalid sense key), then a generic VMS status code (SS$_MEDOFL) is returned. ; ; The filemark and EOM bits generate status codes of SS$_ENDOFFILE and ; SS$_ENDOFTAPE respectively. ; ; Certain situations cause an additional translation including: ; ; - If the sense key was NOT_READY and the wait for unit ready flag is set in ; the UCB, then this is an indication that the device is in the process of ; becoming ready. Return a DEVOFFLINE status to allow the WAIT_UNIT_READY ; routine to continue to poll for unit ready. Otherwise, change the ; status to SS$_MEDOFL to cause mount verification to occur for this device. ; ; - Any medium error which does not have an additional sense code of ; non-recoverable ECC error generates SS$_DRVERR status. This is considered ; "more fatal" as it could cause us to loose tape position. ; ; INPUTS: ; ; R5 - SCDRP address ; ; OUTPUTS: ; ; R0 - VMS status code ; R1,R2 - Destroyed ; All other registers perserved ;- TRANS_SENSE_KEY: MOVL SCDRP$L_SVA_USER(R5),R2 ; Get address of request sense data .IF DEFINED MULTI_VOLUME_TEST ; Code for testing multi-volume savesets MOVL UCB$L_IRP(R3),R1 ; Get IRP address BEQL 3$ ; Branch if not available EXTZV #IRP$V_FCODE,- ; Extract function code #IRP$S_FCODE,- ; IRP$W_FUNC(R1),R1 ; CMPL R1,#IO$_WRITEPBLK ; Write function? BEQL 2$ ; Branch if so CMPL R1,#IO$_WRITEOF ; Write tapemark? BNEQ 3$ ; Branch if not 2$: CMPL UCB$L_RECORD(R3),#^X50 ; Beyond artificial end of tape mark? BLSSU 3$ ; Branch if not BISB #^X40,2(R2) ; Simulate end of tape 3$: .ENDC ; If the key is recorvered error or no sense, then go ahead and look at the ; EOF, EOM, and ILI flags in the extended sense data. Any other type of sense ; key takes precedence over these flags. EXTZV #SCSI$SNS$V_SENSE_KEY,- ; Get the extended sense key #SCSI$SNS$S_SENSE_KEY,- ; SCSI$SNS$R_FLAGS(R2),R1 ; ASSUME SCSI$C_NO_SENSE EQ 0 BEQL 4$ ; Branch if no sense CMPB R1,- ; Recovered error? #SCSI$C_RECOVERED_ERROR ; BEQL 4$ ; Branch if so CMPB R1,#SCSI$C_BLANK_CHECK ; End of data? BNEQ 35$ ; No, continue BBC #UCB$V_SPACEFILE_INPROG,- ; SPACE FILEMARKS in progress? UCB$L_MK_FLAGS(R3),35$ ; MOVL #SS$_ENDOFVOLUME,R0 ; Yes, just hit EOV BISL #MT$M_EOF,- ; and is at a tapemark UCB$L_DEVDEPEND(R3) BRW 80$ ; Workaround for a bug in the TK50: if this is a medium error sense key and the ; EOM bit is set, then this indicates we're at BOT or EOT and the medium error ; sense key can be ignored. 35$: CMPB R1,- ; Medium error? #SCSI$C_MEDIUM_ERROR ; BNEQ 6$ ; Branch if not IFNOT_TK50 6$ ; Branch if not a TK50 4$: BITB #SCSI$SNS$M_ILI,- ; Illegal length indicator? SCSI$SNS$R_FLAGS(R2) ; BNEQW 90$ ; Branch if so BITB #SCSI$SNS$M_FILEMARK,- ; Filemark? SCSI$SNS$R_FLAGS(R2) ; BNEQW 50$ ; Branch if so BITB #SCSI$SNS$M_EOM,- ; End of medium? SCSI$SNS$R_FLAGS(R2) ; BNEQW 60$ ; Branch if so ; If this is a UNIT ATTENTION sense key, the volume valid bit is NOT set ; in the UCB, and the "wait for unit ready" bit is set, then return success. ; This situation can occur for multi-volume tape backups when one tape is ; unloaded and the next is loaded. We'll eventually need a PACACK to make the ; drive usable. 6$: CMPB R1,- ; Unit attention sense key? #SCSI$C_UNIT_ATTENTION ; BNEQ 5$ ; Branch if not BBS #UCB$V_VALID,- ; Branch if volume valid bit is set UCB$W_STS(R3),5$ ; in the UCB BBC #UCB$V_WAITRDY_INPROG,- ; Branch if NOT waiting for unit ready UCB$L_MK_FLAGS(R3),5$ ; MOVZWL #SS$_NORMAL,R0 ; Set success status BRB 40$ ; Use common exit path 5$: MOVB R1,UCB$B_SENSE_KEY(R3) ; Save sense key in the UCB MOVAL SENSE_KEY_TABLE,R0 ; Get address of sense key to VMS status ; code translation table 10$: CMPB (R0),#^XFF ; End of table? BEQL 24$ ; Branch of so CMPB (R0)+,R1 ; Sense keys match? BEQL 20$ ; Branch if so ADDL #2,R0 ; Skip VMS error code BRB 10$ ; Try next sense key 20$: MOVZWL (R0),R0 ; Pick up VMS error code BRB 25$ ; Join common path 24$: MOVL #SS$_DRVERR,R0 ; Status code for unknown sense key ; A status code of SS$_DRVERR indicates a serious error occurred. If this is ; the case, set the tape position lost. 25$: CMPL R0,#SS$_DRVERR ; Serious error? BNEQ 26$ ; Branch if not BSBW SET_TAPE_LOST ; Set tape lost ; The following code allows the routine WAIT_UNIT_READY to poll for unit ; ready. If a NOT_READY sense key is returned by the drive, and the wait for ; unit ready flas is set, then return DEVOFFLINE status to notify the caller ; that the device is still not ready, Otherwise, return MEDOFL status to ; cause mount verification to kick in. 26$: CMPL R0,#SS$_DEVOFFLINE ; Device offline status? BNEQ 40$ ; Branch if not BBS #UCB$V_WAITRDY_INPROG,- ; Branch if waiting for unit ready is UCB$L_MK_FLAGS(R3),40$ ; set 30$: MOVZWL #SS$_MEDOFL,R0 ; Otherwise, set medium offline status 40$: RSB ; Return to caller ; EOF set. This indicates a filemark was encountered during the previous tape ; motion command. Set SS$_ENDOFFILE status and set the EOF bit in the device ; dependent field. 50$: MOVZWL #SS$_ENDOFFILE,R0 ; Assume filemark (end of file) BISW #,- ; Set end of file bit in UCB UCB$L_DEVDEPEND+2(R3) ; BRB 80$ ; Perform sanity check on additional ; info (if skip record in progress) ; EOM set. This indicates that the tape reached one end or the other, based ; on the direction of motion. In the case of reverse motion, BOT is set. ; Otherwise, EOT is set. In either case, ENDOFTAPE status is returned. 60$: MOVW #,R1 ; Assume reverse motion BBS #UCB$V_REV_SKIP,- ; Branch if reverse motion in UCB$L_MK_FLAGS(R3),70$ ; progress MOVW #,R1 ; Prepare to set end of tape 70$: BISW R1,UCB$L_DEVDEPEND+2(R3); Set BOT/EOT based on current direction MOVL #SS$_ENDOFTAPE,R0 ; Set end of tape status ; If the original command was a skip record, and no valid additional info was ; returned, then set a fatal error status and mark the tape position as lost, ; as we need the additional info to keep track of our position on the tape. 80$: BBC #UCB$V_SKIP_INPROG,- ; Branch if skip command not in UCB$L_MK_FLAGS(R3),40$ ; progress BBS #UCB$V_ADDNL_INFO,- ; Branch if valid additional info UCB$L_MK_FLAGS(R3),40$ ; returned IF_TSZ05 40$ ; For TSZ05 and TSZ07 devices, skip rev IF_TSZ07 40$ ; doesn't return "addl info" flag set. MOVL #SS$_DRVERR,R0 ; Otherwise, change to fatal error BSBW SET_TAPE_LOST ; Set tape position lost BRB 40$ ; Use common exit ; ILI set. This indicates that the requested record length did not match the ; actual record length on tape. If the record on tape was shorter than the ; requested length, ignore the ILI bit as utility doing the read does not ; necessarily know ahead of time the length of the record on tape. If the ; record on tape was longer than the requested length, return a data overrun ; status code. The data overrun condition is detected by the ILI bit being ; set and a TRANS_CNT equal to BCNT. It's assumed that the drive would have ; returned more data if the host had requested it. 90$: MOVZWL #SS$_NORMAL,R0 ; Assume no data overrun MOVL UCB$L_SCDRP_SAV1(R3),R1 ; Get SCDRP of original read command CMPW SCDRP$L_TRANS_CNT(R1),- ; Data overrun condition? SCDRP$L_BCNT(R1) ; BLSSU 40$ ; Branch if not MOVZWL #SS$_DATAOVERUN,R0 ; Return data overrun status BRB 40$ .SBTTL SAVE_ADDNL_INFO - Save additional extended sense information ;+ ; SAVE_ADDNL_INFO ; ; This routine is called whenever valid extended sense data is returned by a ; target to obtain any addition data. In the case of a skip record command, ; this information is used later to determine the actual number of records ; skipped. ; ; INPUTS: ; ; R3 - UCB address ; R5 - SCDRP address associated with request sense command ; ; OUTPUTS: ; ; R0,R1 - Destroyed ; ; UCB$L_ADDNL_INFO - Additional info field from the extended ; sense data or -1 if no valid data. ;- SAVE_ADDNL_INFO: MOVL SCDRP$L_SVA_USER(R5),R0 ; Get address of request sense data MOVAL UCB$L_ADDNL_INFO(R3),R1 ; Get address of additional info field CLRL (R1) ; Assume additonal info not valid ASSUME SCSI$SNS$V_VALID EQ 7 BITB #SCSI$SNS$M_VALID,- ; Valid additional info? SCSI$SNS$R_ERROR_CODE_FIELD(R0) BEQL 30$ ; Branch if not BISB #UCB$M_ADDNL_INFO,- ; Indicate additional info is valid UCB$L_MK_FLAGS(R3) ; MOVAL SCSI$SNS$R_INFORMATION+4(R0),R0 ; Get address just beyond additonal ; info field .REPT 4 MOVB -(R0),(R1)+ ; Copy a byte of additional info from ; the extended sense data to the UCB. ; (Note that bytes are reversed in the ; extended sense data). .ENDR ;+ ; The code above copies the second four bytes of additional sense data, if the ; valid bit in byte 1 is set. This code segment copies the balance of the ; additional sense data bytes returned by the target to the UCB. These ; additional bytes are used by mode sense common, but can be used for any device ; type. ;- 30$: PUSHL R7 ; Save scratch register MOVL SCDRP$L_SVA_USER(R5),R0 ; Get address of request sense data MOVAB UCB$B_SENSE_INFO(R3),R1 ; Get address of additional sense field MOVZBL 7(R0),R7 ; Get count of addl sense bytes CMPB R7,# ; Is the count <= the size of buffer? BLEQU 40$ ; Yes, use this count MOVZBL #,R7 40$: MOVAB 8(R0),R0 ; Get address just beyond Add Sense Len MOVB R7,UCB$B_SENSE_LEN(R3) ; Save count of actual number of bytes ; saved in UCB COPY_BYTE: MOVB (R0)+,(R1)+ ; Copy a byte of additional info from ; the extended sense data to the UCB. SOBGTR R7,COPY_BYTE ; Copy all Addl sense bytes POPL R7 ; Restore scratch register. RSB .SBTTL ALLOC_SCDRP - Allocate an SCDRP ;+ ; ALLOC_SCDRP ; ; This routine allocates an SCDRP by attempting to remove one from the queue ; in the UCB. If the queue is empty (which should never happen), then allocate ; a new SCDRP from pool. The entire SCDRP is zero'ed and various fields are ; initialized. ; ; INPUTS: ; ; R3 - UCB address ; ; UCB$L_SCDRPQ_FL - Queue of SCDRPs ; ; OUTPUTS: ; ; R5 - SCDRP address ; All other register preserved ; ; SCDRP$L_UCB - UCB address ; SCDRP$L_CDT - CDT address ; SCDRP$L_SCSI_FLAGS - Initialized ; SCDRP$L_CL_STK_PTR - Initialized ;- ALLOC_SCDRP: REMQUE @UCB$L_SCDRPQ_FL(R3),R5 ; Remove an SCDRP from the queue BVS 10$ ; Branch if queue was empty 5$: PUSHR #^M ; Save regs MOVC5 #0,.,#0,- ; Initialize the SCDRP #SCDRP$C_LENGTH-12,- ; 12(R5) ; POPR #^M ; Restore regs MOVL R5,UCB$L_SCDRP(R3) ; Save SCDRP address in UCB MOVL R3,SCDRP$L_UCB(R5) ; Save UCB address in SCDRP MOVB UCB$B_FLCK(R3),- ; Copy the fork lock field from the SCDRP$B_FLCK(R5) ; UCB to the SCDRP MOVL UCB$L_SCDT(R3),- ; Save CDT address in SCDRP SCDRP$L_CDT(R5) ; MOVAL SCDRP$L_SCSI_STK-4(R5),-; Initialize the SCDRP stack pointer SCDRP$L_SCSI_STK_PTR(R5); INIT_SCDRP_STACK ; Initialize the internal stack in the SCDRP RSB 10$: ALLOC_STACK_SCDRP ; Get the SCDRP for a STACK SUBPUSH R0 ; Save register SUBPUSH R1 ; Save register SUBPUSH R2 ; Save register MOVL #SCDRP$C_LENGTH,R1 ; Length of SCDRP BSBW ALLOC_POOL ; Go allocate an SCDRP MOVW R1,SCDRP$W_SCDRPSIZE(R2); Save length of SCDRP MOVL R2,R5 ; Copy new SCDRP address SUBPUSH R2 ; Restore register SUBPUSH R1 ; Restore register SUBPUSH R0 ; Restore register FREE_STACK_SCDRP ; Give it back BRW 5$ .SBTTL DEALLOC_SCDRP - Deallocate an SCDRP ;+ ; DEALLOC_SCDRP ; ; This routine deallocates an SCDRP by returning it the the queue in the ; UCB. A sanity check is made to ensure that any map registers for this ; command have been deallocated. ; ; INPUTS: ; ; R3 - UCB address ; R5 - SCDRP address ; ; OUTPUTS: ; ; R3 - UCB address ; All other register preserved ; ; UCB$L_SCDRP - Cleared to indicate no active SCDRP ;- DEALLOC_SCDRP: TSTW SCDRP$W_MAPREG(R5) ; Are we hanging on to any mapping regs? BNEQ 10$ ; Branch if so TSTW SCDRP$W_NUMREG(R5) ; Are we hanging on to any mapping regs? BNEQ 10$ ; Branch if so INSQUE SCDRP$L_FQFL(R5),- ; Insert SCDRP in UCB queue UCB$L_SCDRPQ_FL(R3) ; CLRL UCB$L_SCDRP(R3) ; No active SCDRP for this UCB RSB 10$: BUG_CHECK INCONSTATE,FATAL ; Should never deallocate an SCDRP ; without first unmapping buffer .DSABL LSB .SBTTL GET_DEVICE_TYPE - Determine device type from inquiry data ;+ ; GET_DEVICE_TYPE ; ; This routine translates the product ID field from the INQUIRY data to ; a VMS device type. In addition, it fills in the media ID field in the UCB. ; The TZK50 and TZ30 are handled specially, since these devices have no product ; ID field in the inquiry data. For the TZK50 and TZ30, there is exactly 5 ; bytes if inquiry data, and the device qualifier fields contain 50 (hex) and ; 30 (hex), respectively. ; ; INPUTS: ; ; R0 - Address of INQUIRY data ; R3 - UCB address ; ; OUTPUTS: ; ; R0-R2 - Destroyed ; All other registers preserved ;- GET_DEVICE_TYPE: CMPL SCDRP$L_TRANS_CNT(R5),#5; 5 bytes of inquiry data? BNEQ 20$ ; Branch if not, can't be a TK50 or TZ30 EXTZV #SCSI$INQ$V_MODIFIER,- ; Get device type qualifier #SCSI$INQ$S_MODIFIER,- ; SCSI$INQ$R_DEVICE_TYPE_MOD(R0),-; R1 ; ; Check for device type of TZ30 CMPB R1,#^X30 ; TZ30? BNEQ 10$ ; Branch if not MOVAL TZ30_DEV_TYPE,R1 ; Get table entry for TZ30 BRB 60$ ; Check for device type of TZK50 10$: CMPB R1,#^X50 ; TZK50? BNEQ 20$ ; Branch if not MOVAL TK50_DEV_TYPE,R1 ; Get table entry for TK50 BRB 60$ ; Branch to common code ; Device is not a TZ30 or TK50. Scan the device type table to see if we have ; a matching produce ID field. If not, then treat this device as a generic SCSI ; tape. 20$: CMPL SCDRP$L_TRANS_CNT(R5),- ; Enough inquiry data to have a valid #24 ; produce ID field? BLSS 50$ ; Branch if not MOVAL SCSI_DEVICE_TABLE,R1 ; Get SCSI device table 30$: CMPL 1(R1),16(R0) ; Matching product ID (low LW)? BNEQ 40$ ; Branch if not CMPL 5(R1),20(R0) ; Matching product ID (high LW)? BEQL 60$ ; Branch if so 40$: ADDL #,R1 ; Advance to next entry in table TSTB (R1) ; End of table BNEQ 30$ ; Branch if not ; Device is not a TZ30 or TK50, nor was a matching product ID field found in ; the device type tape. Treat the device as a generic SCSI tape. 50$: MOVAL GENERIC_DEV_TYPE,R1 ; Get table entry for generic tape 60$: MOVB (R1),UCB$B_DEVTYPE(R3) ; Save VMS device type MOVL 9(R1),UCB$L_MIN_REV(R3) ; Save minimum revision level BBC #DEV$V_DTN,- ; If DTN bit is set, handle DDR UCB$L_DEVCHAR2(R3),65$ ; mechanism PUSHR #^M ; Save registers across JSB MOVL R3,R5 ; Get UCB in R5 for IOC$ JSB G^IOC$REMOVE_DEVICE_TYPE ; Remove our reference to the DTN POPR #^M ; Restore registers 65$: MOVL 13(R1),- ; Save media ID field UCB$L_MEDIA_ID(R3) ; MOVAL 17(R1),- ; Save address of vendor-unique mode UCB$L_MSEL_INFO(R3) ; select info BBS #MT2$V_COMP_ENA, - ; Check current compaction enabled UCB$L_DEVDEPND2(R3), 64$; INSV 27(R1),- ; Save density information #MT$V_DENSITY,- ; #MT$S_DENSITY,- ; UCB$L_DEVDEPEND(R3) ; 64$: BLBC 28(R1),67$ ; Check for loader device - mcy/rcl 9/18 BISL2 #DEV$M_LDR,- ; Set loader bit if device is loader UCB$L_DEVCHAR2(R3) ; 67$: BICL #UCB$M_DEVICE_IS_8MM,- ; Assume not an 8mm device UCB$L_MK_FLAGS(R3) CMPL #^A/EXB-/, 16(R0) ; Is it an EXABYTE tape (8mm)? BEQL 68$ ; Branch if not CMPL #^A/TKZ0/, 16(R0) ; Is it a TKZ09 tape (8mm)? BNEQ 70$ ; Branch if not (1st 4 bytes) CMPL #^A/9 /, 20(R0) ; Check rest of TZK09 string BNEQ 70$ ; Branch if not, else set 8mm flag 68$: BISL #UCB$M_DEVICE_IS_8MM,- ; Set "it's an 8mm device" flag UCB$L_MK_FLAGS(R3) 70$: CMPB #DT$_GENERIC_MK,- ; Are we in DDR mode? UCB$B_DEVTYPE(R3) ; BEQL 75$ ; Nope 73$: BRW 80$ 75$: CMPL SCDRP$L_TRANS_CNT(R5),- ; A proper INQUIRY response is at least #32 ; 32 bytes. Do we have that? BLSS 73$ ; Nope, can't do dynamic naming ; ; At this point: ; R0 = Address of the inquiry data ; R3 = UCB ; ; Register usage: ; R0 = input name pointer ; R1 = output name pointer ; R2 = original SP ; R4 = end of field pointer ; R5 = "last character was a blank" flag ; R6 = scratch ; ; First, process the Vendor ID, compressing multiple blanks into one. ; PUSHR #^M ; Save registers MOVL SP,R2 ; Save stack pointer CLRL R5 ; Initialize "last was blank" flag BICL #7,SP ; Quad align the stack SUBL #32,SP ; Allocate space for name and such MOVL SP,R1 ; Setup pointer into name buffer MOVAB 8(R0),R0 ; Point to vendor ID MOVAB 8(R0),R4 ; Vendor ID is 8 bytes long 301$: CMPL R0,R4 ; End of field? BEQL 320$ ; Yes, continue with product ID CMPB #^X20,(R0)+ ; Is character a blank BNEQ 302$ ; No, clear flag and copy BLBS R5,301$ ; If previous char blank, skip this one MOVL #1,R5 ; Otherwise, set "last was blank" flag MOVB #^X20,(R1)+ ; and store a blank BRB 301$ ; Back for next character 302$: CLRL R5 ; Clear "last was blank" flag MOVB -1(R0),(R1)+ ; and copy the character BRB 301$ ; Back for next character ; ; Now, setup to process the product ID. Again, compress multiple blanks ; into one. Also, the copyright symbol "(C)" or "(c)" is treated as a ; name terminator. ; 320$: MOVAB 16(R0),R4 ; Product ID is 16 bytes long BLBS R5,321$ ; If trailing blank on Vendor, continue MOVL #1,R5 ; Otherwise, insert a blank between MOVB #^X20,(R1)+ ; vendor and product IDs 321$: CMPL R0,R4 ; See if we have a full type name BEQL 330$ ; Yes, we're done CMPB #^X20,(R0)+ ; Is the next character a blank? BNEQ 322$ ; No, go check for copyright BLBS R5,321$ ; If previous char blank, skip this one MOVL #1,R5 ; Otherwise, set "last was blank" flag MOVB #^X20,(R1)+ ; and store a blank BRB 321$ ; Back for next character ; ; Check for copyright... ; 322$: CMPB #^A/(/,-1(R0) ; Was character a "(" BNEQ 323$ ; Nope, go copy it BICB3 #^X20,(R0),R6 ; Convert next char to upper case CMPB #^A/C/,R6 ; See if it's a "C" BNEQ 323$ ; Nope, just copy data CMPB #^A/)/,1(R0) ; Look for closing paren BEQL 330$ ; Found copyright, so we're done 323$: CLRL R5 ; Clear "last was blank" flag MOVB -1(R0),(R1)+ ; and copy the character BRB 321$ ; Back for next character 330$: SUBL3 SP,R1,R0 ; Calculate name length SUBL3 R5,R0,R1 ; Remove any trailing blank ; name length MOVL SP,R0 ; Point to INQUIRY product name MOVL R3, R5 ; UCB in R5 for IOC$ADD_DEVICE_TYPE JSB G^IOC$ADD_DEVICE_TYPE ; Add us to the DTN list MOVL R2,SP ; Clean the stack POPR #^M ; Save registers 80$: RSB ; Return to caller .SBTTL CHECK_REV_LEVEL - Check for an out-of-rev device ;+ ; CHECK_REV_LEVEL ; ; This routine checks the revision level returned in the INQUIRY or RECEIVE ; DIAGNOSTIC data with the minimum revision level for that device. If the ; device's revision level is below the minimum allowed, an invalid inquiry ; data error is logged. ; ; Note: since the revision field in the inquiry data is a 4 byte ascii string, ; the low-order byte is the most significant. Thus, the bytes must be compared ; from low byte to high byte, and a CMPL can not be used to perform the ; comparison. ; ; INPUTS: ; ; R3 - UCB address ; UCB$L_HW_REV - Revision level ; UCB$L_MIN_REV - Minimum revision level ; ; OUTPUTS: ; ; R0 - Status (LBS if rev level OK) ; R1,R2 - Destroyed ; All other registers preserved ; An error is logged of the device is out-of-rev ;- CHECK_REV_LEVEL: CLRL R0 ; Assume out of rev MOVAL UCB$L_HW_REV(R3),R1 ; Get addr of actual revision level MOVAL UCB$L_MIN_REV(R3),R2 ; Get addr of minimum revision level .REPT 4 CMPB (R1)+,(R2)+ ; Compare a byte of revision level BLSSU 20$ ; Branch if out of rev BGTRU 5$ ; Branch if rev OK .ENDR 5$: MOVL #SS$_NORMAL,R0 ; Set success status 10$: RSB ; Return to caller 20$: LOG_ERROR - ; Log an invalid inquiry data error TYPE=INV_INQUIRY_DATA,- ; VMS_STATUS=#SS$_NORMAL,-; UCB=R3 ; BRB 10$ ; Use common exit .SBTTL ALLOC_POOL - Allocate a block of non-paged pool ;+ ; ALLOC_POOL ; ; This routine allocates a block of non-paged pool no smaller than the ; size of a fork block (allowing COM$DRVDEALMEM to fork on this block ; during deallocation). An extra quadword at the top of the block is reserved ; to save the size field, relieving the caller this responsibility. The caller ; is presented with the address just beyond the reserved quadword. Although a ; word would be sufficient for this field, a quadword is used for allignment ; purposes (some blocks are used as IRPs, which are placed on self-relative ; queues and require quadword allignment). ; ; If an allocation failure occurs, the thread is stalled and wakes up once a ; a second to retry the allocation. ; ; INPUTS: ; ; R1 - Size of block to allocate ; R3 - UCB address ; R5 - SCDRP address ; ; OUTPUTS: ; ; R0 - Destroyed ; R1 - Size of block allocated ; R2 - Address of allocated block ; -8(R2) - Length of allocated block (used by DEALLOC_POOL) ; All other registers perserved ;- ALLOC_POOL:: ADDL #8,R1 ; Reserve a quadword to save size CMPL R1,#FKB$C_LENGTH ; Requested size smaller than fork block? BGEQ 10$ ; Branch if not MOVL #FKB$C_LENGTH,R1 ; Use fork block size as minimum 10$: PUSHL R1 ; Save allocation length PUSHL R3 ; Save UCB address JSB G^EXE$ALONONPAGED ; Allocate a block POPL R3 ; Restore reg BLBC R0,20$ ; Branch if error ADDL #4,SP ; Remove allocation length from stack PUSHR #^M ; Save regs MOVC5 #0,.,#0,R1,(R2) ; Initialize the packet POPR #^M ; Save regs MOVL R1,(R2)+ ; Save size of block ADDL #4,R2 ; Skip a longword RSB ; Return to caller ; A pool allocation failure occurred. Come back once a second and retry the ; operation until successful. 20$: SUBPUSH (SP)+ ; Save allocation length (PUSHL R1 above) SUBSAVE ; Save return address MK_WAIT #1,UCB=R3 ; Wait a second SUBPOP -(SP) ; Restore return address SUBPOP R1 ; Restore allocation length BRW 10$ ; Try again .SBTTL DEALLOC_POOL - Deallocate a block of non-paged pool ;+ ; DEALLOC_POOL ; ; This routine deallocates a block of non-paged pool. The size of the block ; is stored in the reserved quadword at a negative offset from the beginning ; of the block. ; ; INPUTS: ; ; R0 - Address of block to deallocate ; -8(R0) - Length of block to deallocate ; ; OUTPUTS: ; ; R0 - Destroyed ; All other registers perserved ;- DEALLOC_POOL:: PUSHQ R1 ; Save R1,R2 SUBL #4,R0 ; Skip a longword MOVL -(R0),IRP$W_SIZE(R0) ; Copy size field CLRB IRP$B_TYPE(R0) ; Clear type field (prevents block from ; being interpreted as shared memory ; during deallocation) JSB G^EXE$DEANONPAGED ; Deallocate the block POPQ R1 ; Restore R1,R2 RSB .SBTTL SETUP_CMD - Common setup for all SCSI commands ;+ ; SETUP_CMD ; ; This routine performs common setup prior to the sending of a SCSI command. ; This includes allocating a command buffer, filling in the pointers in the ; SCDRP to the command and status fields, copying the SCSI command to the command ; buffer, allocating an S0 "user" buffer if the command requires transferring ; data to or from the class driver, filling in the SCDRP fields used to map ; this buffer, and mapping the buffer. ; ; Since this routine calls SPI$ALLOCATE_COMMAND_BUFFER, which can suspend ; the thread, the return PC must be saved in the SCDRP. ; ; INPUTS: ; ; R2 - Pointer to entry in SCSI_CMD table ; R4 - PDT address ; R5 - SCDRP address ; ; OUTPUTS: ; ; R0 - Status ; R1,R2 - Destroyed ; ; SCDRP$L_CMD_BUF - Address of SCSI command buffer ; SCDRP$L_CMD_PTR - Address of SCSI command ; SCDRP$L_STS_PTR - Address to save SCSI status byte ; SCDRP$L_SVA_USER- Address of S0 "user" buffer ; SCDRP$L_BCNT - Length of S0 "user" buffer ; SCDRP$W_BOFF - Byte offset of S0 "user" buffer ; SCDRP$L_SVAPTE - SVAPTE of of S0 "user" buffer ; IRP$V_FUNC - SET/CLEAR to indicate READ/WRITE from S0 "user" buffer ;- SETUP_CMD:: SCSI_CMD_BUF_OVHD = 4 + 4 ; 4 bytes to save status byte + ; 4 bytes for SCSI command length SUBSAVE ; Save return address MOVZBL (R2),R1 ; Get size SCSI command ADDL #SCSI_CMD_BUF_OVHD,R1 ; Add in command buffer overhead SUBPUSH R2 ; Save R2 SPI$ALLOCATE_COMMAND_BUFFER ; Allocate a command buffer MOVL R2,R1 ; Copy command buffer address SUBPOP R2 ; Restore R2 MOVB #^XFF,(R1) ; Initialize status field MOVAL (R1)+,- ; Address to put SCSI status byte SCDRP$L_STS_PTR(R5) ; MOVL R1,SCDRP$L_CMD_PTR(R5) ; Save address of SCSI command MOVZBL (R2)+,R0 ; Get SCSI command length MOVL R0,(R1)+ ; Save length in command buffer ; The PUSHAB and BISB2 instructions are used to fill in the LUN field in the ; command packet. SCSI-2 suggests that this field be left with a zero and ; that message handshaking between the port driver and target convey LUN ; information. Therefore, these two instructions have been commented out. ; PUSHAB 1(R1) ; Save address of SCSI command LUN field ASHL #-1,R0,R0 ; Change byte count to word count 10$: MOVW (R2)+,(R1)+ ; Copy a byte of SCSI command SOBGTR R0,10$ ; Repeat for entire SCSI command ; BISB UCB$B_LUN(R3),@(SP)+ ; Fill in SCSI LUN field MOVZWL (R2)+,- ; Set up command-specific timeout value SCDRP$L_DISCON_TIMEOUT(R5) MOVZWL #10,- ; 10 seconds for - SCDRP$L_DMA_TIMEOUT(R5) ; DMA DATA OUT timeout value CVTWL (R2)+,R1 ; Get length of send data buffer BLSS 20$ ; Branch if negative, no system buffer ; involved, leave SCDRP$L_BCNT unchanged MOVL R1,SCDRP$L_BCNT(R5) ; Save length of transfer BEQL 20$ ; Branch if zero length, zero SCDRP$L_BCNT CLRL SCDRP$L_PAD_BCNT(R5) ; No padding required INSV (R2),#IRP$V_FUNC,#1,- ; Set/clear FUNC bit to indicate READ/ SCDRP$W_STS(R5) ; WRITE function BBC - ; Did we already allocate a buffer? #SCDRP$V_CL_PRIVATE_BUFF,- SCDRP$L_SCSI_FLAGS(R5),15$ MOVL SCDRP$L_SVA_USER(R5),R2 ; Get start address of buffer BRW 18$ ; Branch to common code 15$: SUBPUSH R2 ; Save R2 BSBW ALLOC_POOL ; Allocate a buffer to receive response MOVL R2,SCDRP$L_SVA_USER(R5) ; Save address of allocated buffer SUBPOP R2 ; Restore R2 18$: MOVL SCDRP$L_SVA_USER(R5),R1 ; Get start address of buffer BICW3 #^C<^X1FF>,R1,- ; And byte offset within page SCDRP$W_BOFF(R5) ; PUSHL R3 ; Save R3 MOVL SCDRP$L_SVA_USER(R5),R2 ; Get user buffer address JSB G^MMG$SVAPTECHK ; Get SVAPTE of allocated system buffer MOVL R3,SCDRP$L_SVAPTE(R5) ; Save SVAPTE in SCDRP POPL R3 ; Restore R3 ASSUME SCDRP$M_S0BUF LT 256 ASSUME SCDRP$M_BUFFER_MAPPED LT 256 BISB #SCDRP$M_S0BUF!- ; This buffer is an S0 "user" buffer SCDRP$M_BUFFER_MAPPED,-; and it has been mapped SCDRP$L_SCSI_FLAGS(R5) ; SPI$MAP_BUFFER PRIO=HIGH ; Map the "user" buffer for read access 20$: MOVZWL #SS$_NORMAL,R0 ; Set success status SUBRETURN .SBTTL CLEANUP_CMD - Common cleanup for all SCSI commands ;+ ; CLEANUP_CMD ; ; This routine performs common cleanup after the sending of a SCSI command ; including unmapping the user buffer and deallocating the command buffer. ; ; INPUTS: ; ; R4 - PDT address ; R5 - SCDRP address ; ; OUTPUTS: ; ; R2 - Destroyed ; All other registers preserved ;- CLEANUP_CMD:: PUSHR #^M ; Save regs BBCC #SCDRP$V_BUFFER_MAPPED,-; Branch if no buffer has been mapped SCDRP$L_SCSI_FLAGS(R5),-; 10$ SPI$UNMAP_BUFFER ; Unmap the mapped buffer 10$: BBCC #SCDRP$V_S0BUF,- ; Branch if this is not an S0 "user" SCDRP$L_SCSI_FLAGS(R5),-; buffer 20$ ; MOVL SCDRP$L_SVA_USER(R5),R0 ; Get address of S0 user buffer CLRL SCDRP$L_SVA_USER(R5) ; Buffer no longer owned BBS - ; Private buffer to be handled by other code #SCDRP$V_CL_PRIVATE_BUFF,- SCDRP$L_SCSI_FLAGS(R5),20$ BSBW DEALLOC_POOL ; Deallocate the buffer 20$: MOVL SCDRP$L_CMD_BUF(R5),R0 ; Get address of command buffer CLRL SCDRP$L_CMD_PTR(R5) ; Remove command pointer CLRL SCDRP$L_STS_PTR(R5) ; Remove status pointer SPI$DEALLOCATE_COMMAND_BUFFER ; Deallocate the command buffer 30$: POPR #^M ; Restore regs RSB .SBTTL LOG_ERROR - Write an entry to the errorlog file ;+ ; LOG_ERROR ; ; This routine writes an entry to the errorlog file. If the device is offline, ; no error is logged. This prevents the errorlog file from being filled up while ; the class driver does it its periodic polling of devices that have been set ; offline. The assumption is that the initial error that caused the device to ; be placed offline has been logged and that subsequent errorlog entries would ; be redundent. ; ; If the device class and/or type fields in the UCB have not been initialized, ; then fill in "TAPE" and "GENERIC SCSI TAPE" respectively so the packet can be ; properly formatted by ERF. This situation could arise if invalid inquiry data ; is received for the device, preventing these fields from being filled in ; properly. ; ; If no I/O is active for this device (for example, if an error is detected ; during unit initialization) log a device attention. Otherwise, log a ; device error and then release the errorlog entry. This prevents errors from ; being lost if multiple errors are logged for a single QIO. ; ; Some types of errors are logged just once per system boot. For example, if ; invalid mode sense data is returned by the target, just one INVALID_SENSE_DATA ; error is logged to prevent filling the errorlog with duplicate packets. The ; DUPLICATE_ERR_MASK table specifies those error types which should be logged ; just once. A bitmask in the UCB records those error types that have been ; logged already. ; ; INPUTS: ; ; R5 - UCB address ; R7 - Error type ; R8 - VMS status code ; ; OUTPUTS: ; ; All registers preserved ;- DUPLICATE_ERR_MASK: ; Bitmask of error types that can .LONG - ; be logged more than once <1@SCSI$C_MAP_BUFFER_ERROR>!- <1@SCSI$C_SEND_CMD_ERROR>!- <1@SCSI$C_EXTND_SENSE_DATA>!- <1@SCSI$C_REASSIGN_BLOCK> LOG_ERROR: BBS #UCB$V_DISABL_ERRLOG,- ; Branch if errlogging is disabled UCB$L_MK_FLAGS(R5),40$ BBC #UCB$V_ONLINE,- ; Branch if device is offline (don't UCB$W_STS(R5),40$ ; log an error) BBCS R7,UCB$L_ERR_MASK(R5),- ; Branch if this error type has not 5$ ; been logged yet BBC R7,DUPLICATE_ERR_MASK,- ; Branch if this is a type of error 40$ ; that should be logged just once 5$: PUSHR #^M ; Save regs BSBW FILTER_ERROR ; OK to filter error? BLBS R0,35$ ; Branch if so MOVB UCB$B_DEVTYPE(R5),R9 ; Save SCSI device type BNEQ 10$ ; Branch if device type known MOVB #DT$_GENERIC_MK,- ; Fill in generic type so ERF can UCB$B_DEVTYPE(R5) ; translate the errlog packet ; The following code segment temporarily replaces the values TK50 and TZ30 ; with TK50S and TZ30S, respectively. This allows ERF to distinguish errlog ; packets logged by this driver from those logged by the VS2000 driver. 10$: IF_TZ30 11$ ; Branch if this is a TZ30 IFNOT_TK50 12$ ; Branch if this is not a TK50 MOVB #DT$_TK50S,- ; Change TK50 to TK50S UCB$B_DEVTYPE(R5) BRB 12$ ; Join common path 11$: MOVB #DT$_TZ30S,- ; Change TZ30 to TZ30S UCB$B_DEVTYPE(R5) ; 12$: MOVB UCB$B_DEVCLASS(R5),R10 ; Save DEVCLASS field MOVB #DC$_TAPE,- ; Temporarily set this field to a tape UCB$B_DEVCLASS(R5) ; so ERF can translate packet MOVL UCB$L_DDT(R5),R0 ; Get DDT address MOVW SCSI_ERROR_LEN_TAB-2[R7],-; Save required errorlog packet size DDT$W_ERRORBUF(R0) ; in the DDT TSTL UCB$L_IRP(R5) ; I/O in progress? BEQL 20$ ; Branch if not JSB G^ERL$DEVICERR ; Log a device error BBCC #UCB$V_ERLOGIP,- ; Clear error log in progress. UCB$W_STS(R5),30$ ; MOVL UCB$L_EMB(R5),R2 ; Get address of error message buffer BEQL 30$ ; Branch if none available JSB G^ERL$RELEASEMB ; Realease the errorlog buffer BRB 30$ ; Skip no-I/O-in-progress path 20$: JSB G^ERL$DEVICEATTN ; Log a device attention 30$: MOVB R10,UCB$B_DEVCLASS(R5) ; Restore original devclass value MOVB R9,UCB$B_DEVTYPE(R5) ; Restore device type 35$: POPR #^M ; Restore regs 40$: RSB ; Return to caller .SBTTL FILTER_ERROR - Attempt to filter error ;+ ; FILTER ERROR ; ; This routine attempts to filter the current error being logged by ; checking to see if the same error has been logged recently. If so, ; don't log it again. This prevents the errlog file from filling up ; if the same error is occurring repeatedly. ; ; An array in the UCB contains the last n errors logged. Each error is ; timestamped so that eventually it will go stale and a duplicate ; error will be logged. ; ; INPUTS: ; ; R5 - UCB address ; R7 - Error type ; ; UCB$L_SCDRP - Active SCDRP address ; ; OUPUTS: ; ; R0 - Status (LBS if error can be filtered) ;- FILTER_ERROR: PUSHR #^M ; Save regs IFNOT_TK 45$ ; Branch if not a TK50 or TZ30, can't ; try to filter ; First, assemble the information for the current error in R1 as follows: ; ; +-------+------+---------------+ ; | MBZ | ASC | KEY | ERR | ; +-------+------+---------------+ ; ; ERR - Error code ; KEY - Sense key (extended sense data errors only) ; ASC - Additional sense key (extended sense data errors only) ; ; This will be used later to compare against the entries in the error array ; and to save information for this error if it's not being fioltered. MOVZBL R7,R1 ; Copy error type CMPL R7,- ; Extended sense data error? #SCSI$C_EXTND_SENSE_DATA; BNEQ 10$ ; Branch if not MOVL UCB$L_SCDRP(R5),R0 ; Get SCDRP address BEQL 10$ ; Branch if no SCDRP active TSTL SCDRP$L_TRANS_CNT(R0) ; Extended sense data available? BEQL 10$ ; Branch if not MOVL SCDRP$L_SVA_USER(R0),R0 ; Get address extended sense BEQL 10$ ; Branch if none available INSV SCSI$SNS$R_FLAGS(R0),- ; Get sense key #8,#8,R1 INSV SCSI_XS$B_ADDNL_CODE30(R0),- ; Get additional sense code #16,#8,R1 ; CMPB SCSI$SNS$R_FLAGS(R0),- ; Recovered error? #SCSI$C_RECOVERED_ERROR ; BEQL 45$ ; Yes, don't filter it CMPB SCSI$SNS$R_FLAGS(R0),- ; Medium error? #SCSI$C_MEDIUM_ERROR ; BEQL 45$ ; Yes, don't filter it ; Next, scan the error array to see if an identical error has already ; been logged. 10$: MOVL #2,R3 ; Number of entries in error array MOVAL UCB$B_ERR_ARRAY(R5),R2 ; Get address of error array 20$: CMPL (R2),G^EXE$GL_ABSTIM ; Stale entry? BLSSU 30$ ; Branch if so CMPL R1,4(R2) ; Matching error? BEQL 60$ ; Branch if so, OK to filter ADDL #8,R2 ; Advance to next entry in array SOBGTR R3,20$ ; Repeat for all entries in array ; This error was not found in the array. Push all the entries in the array ; down (the array is ordered on expiration time) and add this error to the ; array. 30$: MOVL #2-1,R3 ; Number of entries in array less 1 MOVAQ UCB$B_ERR_ARRAY(R5)[R3],R2 ; Get address just beyond error array 40$: MOVQ -(R2),8(R2) ; Push an entry down in the array SOBGTR R3,40$ ; Repeat for entire array ADDL3 #60,G^EXE$GL_ABSTIM,- ; Set expiration time for new entry (R2)+ ; to one minute from now MOVL R1,(R2) ; Save new entry 45$: CLRL R0 ; Can't filter this entry 50$: POPR #^M ; Save regs RSB ; Return to caller 60$: MOVL #1,R0 ; This error can be filtered BRB 50$ ; Use common exit .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 does a SPI$GET_CONNECTION_CHAR to get the current ; values of the connection characteristics, modifies the values of interest, ; then does a SPI$SET_CONNECTION_CHAR to set up the new values. This allows ; the class driver to change a subset of the characteristics and leave the ; rest unmodified. ; ; INPUTS: ; ; R3 - UCB address ; R4 - SPDT address ; R5 - SCDRP address ; ; OUTPUTS: ; ; R0-R2 - Destroyed ; All other registers preserved ;- NUM_ARGS = 10 SET_CONN_CHAR: SUBSAVE ; Save return address MOVL #<*4>,R1 ; Size of get/set conn char buffer BSBW ALLOC_POOL ; Allocate the buffer SUBPUSH R2 ; Save address of buffer MOVL #NUM_ARGS,- ; Set argument count in buffer SET_CON$L_LEN(R2) ; SPI$GET_CONNECTION_CHAR ; Get current connection characteristics BLBC R0,10$ ; Branch on error ASSUME SET_CON$M_DISC EQ 1 EXTZV #UCB$V_DISCONNECT,#1,- ; Fill in disconnect flag UCB$L_MK_FLAGS(R3),- ; SET_CON$L_CON_FLAGS(R2) ; BISL #SET_CON$M_NORETRY,- ; Disable port driver command retry SET_CON$L_CON_FLAGS(R2) ; ASSUME SET_CON$M_SYN EQ 1 EXTZV #UCB$V_SYNCHRONOUS,#1,- ; Fill in synchronous flag UCB$L_MK_FLAGS(R3),- ; SET_CON$L_SYN_FLAG(R2) ; SPI$SET_CONNECTION_CHAR ; Set the connection characteristics 10$: PUSHL R0 ; Save return status SUBPOP R0 ; Get address of characteristics buffer BSBW DEALLOC_POOL ; Deallocate the buffer POPL R0 ; Restore return status BLBS R0,20$ ; Branch if success status MOVL #SS$_CTRLERR,R0 ; Otherwise, return a reasonable status 20$: SUBRETURN ; Return to caller .PAGE .SBTTL CHECK_SKIPFILE_SUPPORT - Check if device supports READ POSITION command ;+ ; CHECK_SKIPFILE_SUPPORT ; ; This routine is called by IO$_PACKACK code in order to determine whether ; the device supports the READ POSITION command. If it does, then the ; ucb$l_mk_flags bit SKIPFILE_SUPPORTED will be set. That bit is later ; checked when doing IO$_SKIPFILE operations; if READ POSITION is supported, ; a skipfiles operation may be done most efficiently by sending Space ; Filemarks advance the tape, followed by a Read Position to obtain ; position information needed to fill in ucb$l_record. ; ; INPUTS: ; ; R3 - UCB address ; R4 - PDT address ; R5 - SCDRP address ; ; OUPUTS: ; ; R0 - Status ; R1,R2 - destroyed ;- CHECK_SKIPFILE_SUPPORT: SUBSAVE ; Save return address BBC #UCB$V_SKIPFILE_SUPPORTED,- ; If cmd was previously declared UCB$L_MK_FLAGS(R3),40$ ; unsupported, don't even try. MOVAL CMD_READ_POSITION,R2 ; Address of READ POSITION command BSBW SETUP_CMD ; Setup the command BISL #UCB$M_READPOSCHK_IN_PROG,- ; Set READ POSITION chk-in-prog UCB$L_MK_FLAGS(R3) BSBW SEND_COMMAND ; Send the command BICL #UCB$M_READPOSCHK_IN_PROG,- ; Clear READ POSITION chk-in-prog UCB$L_MK_FLAGS(R3) BLBC R0,20$ ; Branch on error CMPL SCDRP$L_TRANS_CNT(R5),- ; Sufficient data returned? #20 ; READ POS data len should equal 20 BLSS 20$ ; No, so declare cmd unsupported MOVL SCDRP$L_SVA_USER(R5),R0 ; Get address of data BBS #BPU_BIT,- ; If block position unknown, (R0),20$ ; declare the command unsupported ; Sanity check: the value just returned should equal UCB$L_RECORD. MOVAB 8(R0),R0 ; Prepare to get READ POS value MOVAB UCB$L_RECORD(R3),R1 ; Prepare to get UCB$L_RECORD .REPT 4 CMPB -(R0),(R1)+ ; Compare READ POS, UCB$L_RECORD BNEQ 20$ ; If unequal, declare READ POS .ENDR ; cmd to be unsupported BISL2 #UCB$M_SKIPFILE_SUPPORTED,- ; READ POSITION command supported UCB$L_MK_FLAGS(R3) 30$: BSBW CLEANUP_CMD ; Cleanup from the SCSI command 40$: MOVL #SS$_NORMAL,R0 ; Always return success BICL #UCB$M_FIRST_READPOS_CMD,- ; The first one is done UCB$L_MK_FLAGS(R3) SUBRETURN ; Return to caller ; Error path: READ POSITION cannot be supported. 20$: BICL2 #UCB$M_SKIPFILE_SUPPORTED,- ; READ POSITION command unsupported UCB$L_MK_FLAGS(R3) BBS #UCB$V_FIRST_READPOS_CMD,- ; Suppress logging the very first UCB$L_MK_FLAGS(R3),30$ ; one (eg. for devices that do not ; support the READ POSITION cmd) LOG_ERROR - TYPE=SEND_CMD_ERROR,- VMS_STATUS=#SS$_NORMAL,- UCB=R3 BRW 30$ ; Common exit .SBTTL CHECK_COMPACTION_SUPPORT - Check if device supports data compaction ;+ ; CHECK_COMPACTION_SUPPORT ; ; This routine calls the SETUP_COMPACTION routine attempting to enable ; compaction on the device. If it succeeds in enabling compaction, it is ; assumed that the device supports compaction and the MT2$M_COMP_SUP bit is ; set in UCB$L_DEVDEPND2, otherwise it is cleared. If supported, compaction ; set back to whatever state it was previously in (MT2$M_COMP_ENA). ; ; INPUTS: ; ; R3 - UCB address ; R4 - PDT address ; R5 - SCDRP address ; ; OUPUTS: ; ; R0 - Status ; R1,R2 - destroyed ;- CHECK_COMPACTION_SUPPORT: SUBSAVE ; Save return address CLRB UCB$B_SAVED_COMP(R3) ; Assume compaction is off BBC #MT2$V_COMP_ENA, - ; Check current compaction state UCB$L_DEVDEPND2(R3), 5$; MOVB #1, UCB$B_SAVED_COMP(R3); Flag: compaction was enabled 5$: CMPB #2,- ; Only attempt compaction on SCSI-2+ UCB$B_SCSI_VERSION(R3) ; compliant devices BGTR 10$ ; Branch if scsi-1 DISABLE_ERRLOG ; Temporarily disable errorlogging BISL #UCB$M_COMPCHK_IN_PROG,-; Set compaction check in progress flag UCB$L_MK_FLAGS(R3) MOVL #1, R0 ; Attempt to turn on compaction BSBW SETUP_COMPACTION BICL #UCB$M_COMPCHK_IN_PROG,-; Clear compaction check in progress fl UCB$L_MK_FLAGS(R3) REENABLE_ERRLOG ; Reenable errorlogging BLBC R0,10$ ; LBC = not supported BISL #MT2$M_COMP_SUP,- ; Set DEVDPEND2 comp supported bit UCB$L_DEVDEPND2(R3) ; TSTB UCB$B_SAVED_COMP(R3) ; If compaction was already on, BNEQ 20$ ; leave it on CLRL R0 ; Compaction off flag BSBW SETUP_COMPACTION ; Turn it back off BRB 20$ 10$: BICL #MT2$M_COMP_SUP,- ; Clear DEVDPEND2 comp supported bit UCB$L_DEVDEPND2(R3) ; 20$: MOVL #1, R0 ; Always success SUBRETURN ; Return to caller .SBTTL SETUP_COMPACTION - Use mode sense/select page 10 for compaction ;+ ; SETUP_COMPACTION ; ; This routine sends a MODE SENSE command which requests page 10. This is ; the Device Configuration Page. At offset SCSI_MSEL$B_COMP is a byte ; which specifies if data compaction is enabled or not. ; ; INPUTS: ; ; R0 - desired compaction state (0 = off, 1 = on) ; R3 - UCB address ; R4 - PDT address ; R5 - SCDRP address ; ; OUPUTS: ; ; R0 - Status ;- SETUP_COMPACTION: ; ; issue a MODE_SENSE command to get the contents of page 10(hex) ; SUBSAVE ; Save return address SUBPUSH R1 ; Save register SUBPUSH R2 ; Save register MOVB R0, UCB$B_COMP_STATE(R3) ; Save requested compaction state MOVL #DESCRIPTOR_SIZE,R1 ; Size of descriptor array BSBW ALLOC_POOL ; Allocate memory PUSHR #^M MOVC3 #DESCRIPTOR_SIZE,- ; Copy the descriptors DESCRIPTOR_BASE,(R2) POPR #^M MOVB UCB$B_COMP_STATE(R3),- ; Set desired compaction state (R2) MOVL #SCSI$PGCD$C_DEVICE_CONFIG,R0 ; Device config page code MOVAL DEV_CONFIG_PAGE_DESC(R2),R1 ; Address of descriptors BSBW DO_MODE_PAGE ; Process the page BLBS R0,30$ ; Branch on success LOG_ERROR - ; Log a send command error TYPE=SEND_CMD_ERROR,- ; VMS_STATUS=R0,- ; UCB=R3 ; MOVL #SS$_DRVERR,R0 ; Load error status BRW 40$ ; Exit with error 30$: BICL #MT2$M_COMP_ENA,- ; Assume compaction is disabled UCB$L_DEVDEPND2(R3) TSTB UCB$B_COMP_STATE(R3) ; Check for state of comp_enabled flag BEQL 40$ ; 0 == compaction disabled BISL #MT2$M_COMP_ENA,- ; Indicate compaction is enabled UCB$L_DEVDEPND2(R3) ; 40$: PUSHL R0 ; Save return status MOVL R2,R0 ; Get memory address BSBW DEALLOC_POOL ; Deallocate descriptor memory POPL R0 ; Restore return status SUBPOP R2 ; Restore register SUBPOP R1 ; Restore register SUBRETURN ; Return to caller .SBTTL + .SBTTL + TRACE ROUTINES .SBTTL + .SBTTL Trace buffer, symbols ;+ ; TRACE_BUFFER ; ; The driver uses a ring buffer to trace at the QIO, SCSI command, and SCSI ; cmd/status byte level. For each QIO issued, there can be zero or more SCSI ; commands sent, each of which can result in SCSI command, message, and status ; bytes. The trace buffer has the following format: ; ; +-----------------------+ <---- Start of trace buffer header ; | Size of trace buf | ; +-----------------------+ ; | Trace header size | ; +-----------------------+ ; | Total QIOs in buf | ; +-----------------------+ ; | Size of each QIO | ; +-----------------------+ ; | QIO header size | ; +-----------------------+ ; | # SCSI cmds/QIO | ; +-----------------------+ ; | Size of SCSI cmd | ; +-----------------------+ ; | Offset to SCSI sts | ; +-----------------------+ ; | Current QIO # | ; +-----------------------+ ; | | ; | RESERVED | ; | | ; +-----------------------+ <---- End of trace buffer header ; | QIO 0 | ; +-----------------------+ ; | QIO 1 | ; +-----------------------+ ; | . | ; | . | ; +-----------------------+ ; | QIO n-1 | ; +-----------------------+ ; ; For each QIO, the following information is logged: ; ; +-----------------------+ <---- Start of QIO header ; | QIO number | ; +-----------------------+ ; | Valid flag | ; +-----------------------+ ; | SCSI command cnt | ; +-----------------------+ ; | SCSI bus ID | ; +-----------------------+ ; | Function code | ; +-----------------------+ ; | Media (LBN) | ; +-----------------------+ ; | Byte count | ; +-----------------------+ ; | Byte offset | ; +-----------------------+ ; | QIO status | ; +-----------------------+ ; | Sequence number | ; +-----------------------+ ; | | ; | RESERVED | ; | | ; +-----------------------+ <---- End of QIO header ; | SCSI CMD 0 | ; +-----------------------+ ; | SCSI CMD 1 | ; +-----------------------+ ; | . | ; | . | ; +-----------------------+ ; | SCSI CMD n-1 | ; +-----------------------+ ; ; Finally, for each SCSI command, the following information is logged: ; ; +-----------------------+ ; | CMD byte cnt | ; +-----------------------+ ; | SCSI CMD bytes | ; +-----------------------+ ; | SCSI status byte | ; +-----------------------+ ; ; Note that CMD byte count is kept to allow the tracing routines to know where ; to put the next CMD byte and to allow the user to know how much data to ; interpret. Also note that, in order to make it easier to interpret, the data ; is not packed in as well as it could be. ;- $DEFINI TRACE_BUFFER_HEADER $DEF TR$L_SIZE .BLKL 1 $DEF TR$L_HEADER_SIZE .BLKL 1 $DEF TR$L_TOTAL_QIOS .BLKL 1 $DEF TR$L_CURRENT_QIO .BLKL 1 $DEF TR$L_QIO_SEQNUM .BLKL 1 $DEF TR$L_QIO_SIZE .BLKL 1 $DEF TR$L_QIO_HEADER_SIZE .BLKL 1 $DEF TR$L_SCSI_CMDS_PER_QIO .BLKL 1 $DEF TR$L_SCSI_CMD_SIZE .BLKL 1 $DEF TR$L_SCSI_MSG_OFFSET .BLKL 1 $DEF TR$L_SCSI_STATUS_OFFSET .BLKL 1 $DEF TR$C_HEADER_SIZE $DEFEND TRACE_BUFFER_HEADER $DEFINI TRACE_BUFFER_QIO_HEADER $DEF TR$L_QIO_NUMBER .BLKL 1 $DEF TR$L_VALID_FLAG .BLKL 1 $DEF TR$L_SCSI_CMD_CNT .BLKL 1 $DEF TR$L_DEVICE_NAME .BLKL 1 $DEF TR$L_SCSI_UNIT_NUMBER .BLKL 1 $DEF TR$L_QIO_FUNC .BLKL 1 $DEF TR$L_QIO_MEDIA .BLKL 1 $DEF TR$L_QIO_BCNT .BLKL 1 $DEF TR$L_QIO_BOFF .BLKL 1 $DEF TR$L_QIO_STATUS .BLKL 1 $DEF TR$L_SEQNUM .BLKL 1 $DEF TR$L_RECORD .BLKL 1 $DEF TR$L_DEVDEPEND .BLKL 1 $DEF TR$C_QIO_HEADER_SIZE $DEFEND TRACE_BUFFER_QIO_HEADER TR$TOTAL_QIOS = 200 TR$SCSI_CMDS_PER_QIO = 0 TR$CMD_BYTES = 12 TR$MSG_BYTES = 12 TR$STATUS_BYTES = 1 TR$SCSI_MSG_OFFSET = TR$CMD_BYTES+4 TR$SCSI_STATUS_OFFSET = TR$CMD_BYTES+4 + TR$MSG_BYTES+4 TR$SCSI_CMD_SIZE = < + 15> & ^C15 TR$QIO_SIZE = <> + 15> & ^C15 TR$TRACE_BUFFER_SIZE = TR$C_HEADER_SIZE + TR$TRACE_BUFFER_ADDR: .LONG 0 TR$TRACE_ROUTINES: ;+ ; TRACE_ROUTINES ; ; This macro generates a table of routine names and relative offsets. A kernel ; mode program can use this to display the address of various driver routines, ; making debug easier. ;- .MACRO TRACE_ROUTINES, LIST, ?L .IRP ROUTINE, .ENABL LSB .LONG ROUTINE-. .SAVE_PSECT .PSECT $$$114_DRIVER L: .ASCIZ /ROUTINE/ .RESTORE .LONG L-. .DSABL LSB .ENDR .LONG -1 .ENDM TRACE_ROUTINES TRACE_ROUTINES <- MK$DDT,- MK_CTRL_INIT,- MK_UNIT_INIT,- MK_STARTIO,- MK_REG_DUMP,- IO_PACKACK,- IO_READPBLK,- IO_WRITEPBLK,- IO_REWIND,- IO_SKIP_FILE,- IO_SKIP_RECORD,- IO_SETMODE,- INQUIRY,- MODE_SENSE,- MODE_SELECT,- SET_UNIT_ONLINE,- SEND_COMMAND,- LOG_ERROR,- SET_CONN_CHAR> .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 TR$TRACE_BUFFER_ADDR cell is given a value of ; 1 (positive integer) to indicate that poll allocation is on progress. This ; prevents a second thread from allocating another buffer. ; ; INPUTS: ; ; R5 - CRB address ; ; OUTPUTS: ; ; R0-R3 - Destroyed ; All other registers preserved ; ; CRB$L_AUXSTRUC - Address of cell containing trace buffer address ;- SETUP_TRACE: .IF DEFINED DEBUG TSTL TR$TRACE_BUFFER_ADDR ; Trace buffer already set up? BNEQ 10$ ; Branch if so INCL TR$TRACE_BUFFER_ADDR ; Trace buffer setup in progress MOVL #TR$TRACE_BUFFER_SIZE,R1; Get size of trace buffer JSB G^EXE$ALONONPAGED ; Allocate pool BLBC R0,30$ ; Branch if failure PUSHR #^M ; Save regs MOVC5 #0,.,#0,R1,(R2) ; Initialize the packet POPR #^M ; Save regs MOVL #TR$TRACE_BUFFER_SIZE,- ; Save trace buffer size TR$L_SIZE(R2) ; MOVL #TR$C_HEADER_SIZE,- ; Save header size TR$L_HEADER_SIZE(R2) ; MOVL #TR$TOTAL_QIOS,- ; Save total number of QIOs in TR$L_TOTAL_QIOS(R2) ; buffer MOVL #TR$QIO_SIZE,- ; Save size of QIO TR$L_QIO_SIZE(R2) ; MOVL #TR$C_QIO_HEADER_SIZE,- ; Save size of QIO header TR$L_QIO_HEADER_SIZE(R2); MOVL R2,TR$TRACE_BUFFER_ADDR ; Save trace buffer address 10$: .ENDC 20$: MOVAL TR$TRACE_BUFFER_ADDR,- ; Save address of cell pointing to CRB$L_AUXSTRUC(R5) ; trace buffer in CRB RSB 30$: CLRL TR$TRACE_BUFFER_ADDR ; Trace buffer not allocated BRB 20$ ; Use common exit .IF DEFINED DEBUG .SBTTL TRACE_QIO - Trace QIO requests ;+ ; TRACE_QIO ; ; This routine logs information from the current QIO packet including the ; function code, MEDIA, BCNT, and BOFF fields. ; ; INPUTS: ; ; R3 - IPR address or 0 if UNIT INIT ; R5 - UCB address ; ; OUTPUTS: ; ; All registers preserved ;- TRACE_QIO: PUSHR #^M ; Save regs MOVL TR$TRACE_BUFFER_ADDR,R0 ; Get trace buffer address BGEQ 30$ ; Branch if none ADDL3 #1,- ; Bump QIO number TR$L_CURRENT_QIO(R0),R1 ; CMPL R1,#TR$TOTAL_QIOS ; Time to wrap QIO number? BLSS 10$ ; Branch if not CLRL R1 ; Wrap QIO number 10$: MOVL R1,TR$L_CURRENT_QIO(R0) ; Save QIO number MULL3 #TR$QIO_SIZE,R1,R2 ; Get offset of this QIO in trace buffer ADDL R0,R2 ; Address to place info for this QIO ADDL #TR$C_HEADER_SIZE,R2 ; Account for trace buffer header PUSHR #^M ; Save regs MOVC5 #0,.,#0,#TR$QIO_SIZE,- ; Clear entire buffer used to trace (R2) ; this QIO POPR #^M ; Restore regs ADDL3 #TR$C_QIO_HEADER_SIZE,- ; Get address of place to store R2,R1 ; first SCSI command MOVL TR$L_CURRENT_QIO(R0),- ; Save QIO number (R2)+ ; MOVL #1,(R2)+ ; Set valid flag CLRL (R2)+ ; Clear SCSI command count MOVL UCB$L_DDB(R5),R1 ; Get DDB address MOVL DDB$T_NAME_STR(R1),(R2)+; Save SCSI device name MOVZWL UCB$W_UNIT(R5),(R2)+ ; Trace SCSI bus ID TSTL R3 ; IRP address of 0 (unit init)? BEQL 15$ ; Branch if so MOVZWL IRP$W_FUNC(R3),(R2)+ ; Trace QIO function code MOVL IRP$L_MEDIA(R3),(R2)+ ; Trace media number (LBN) MOVL IRP$W_BCNT(R3),(R2)+ ; Trace byte count MOVL IRP$W_BOFF(R3),(R2)+ ; Trace byte offset BRB 20$ ; Skip unit init path 15$: MCOML #0,(R2)+ ; Special function code for build UNIT INIT ADDL #12,R2 ; Skip MEDIA, BCNT, BOFF fields 20$: MOVL R2,UCB$L_TR_QIO_STS(R5) ; Save address to put QIO status MCOML #0,(R2)+ ; Initialize QIO status MOVL TR$L_QIO_SEQNUM(R0),- ; Save QIO sequence number (R2)+ ; INCL TR$L_QIO_SEQNUM(R0) ; Bump QIO sequence number 30$: POPR #^M ; Restore regs RSB ; Return to caller .ENDC ; .IF DEFINED DEBUG .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. ;- TRACE_QIO_STAT: MOVL UCB$L_TR_QIO_STS(R5),R2 ; Get address to put status BEQL 10$ ; Branch if uninitialized MOVL R0,(R2) ; Save status in trace buffer ASSUME TR$L_QIO_STATUS+8 EQ TR$L_RECORD ASSUME TR$L_QIO_STATUS+12 EQ TR$L_DEVDEPEND MOVL UCB$L_RECORD(R5),8(R2) ; Save current record number MOVL UCB$L_DEVDEPEND(R5),- ; Save DEVDEPND info 12(R2) ; 10$: RSB MK_PATCH: .BLKB 200 ; Patch space MK_END: ; Last location in driver .END ;==============================================================================