.TITLE DKDRIVER - VAX/VMS SCSI Disk Class Driver .IDENT 'X-47' ;**************************************************************************** ;* * ;* COPYRIGHT (c) 1978, 1980, 1982, 1984, 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 Disk Class Driver ; ; ABSTRACT: ; ; This module contains the disk class driver to control read/write ; and read-only disks on the SCSI bus. The driver can have at most ; one active I/O per device on the SCSI bus at any one time. This ; driver relies on the SCSI port driver to provide the low-level ; communication of commands and data over the SCSI bus. ; ; AUTHOR: ; ; Jim Klumpp 4-May-1988 ; ; REVISION HISTORY: ; ; X-47 GCE Glenn C. Everhart 14-Jan-1996 ; Prevent user mode code doing io$_audio from crashing if the ; AUCB is bad or if mount verify might start. ; X-46 TGG0121 Tom Goodwin 18-Jan-1996 ; Add Mode Sense Common support changes. Removed old MODE_SENSE ; and MODE_SELECT routines and replaced with new ; PROCESS_MODE_INFO routine ported from OpenVMS ALPHA. ; ; Incorporate all VAX applicable bug fixes from the OpenVMS ; Alpha V6.2 release as detailed below: ; ; X-30 RCL0001 Rick Lord 3-May-95 ; Fold of X18U9 into Theta: Add bounds-checking code at label 36$ ; in DK_REG_DUMP to make sure that the routine does not overflow ; the buffer allocated by the error logging routine. ; X-18U8 JFD0818 James F. Dunham 14-APR-1995 ; o Skip flexible disk geometry page if mode sense for rigid disk ; geometry page succeeds ; o Add (disabled) support for RZ74 SCSI drive disable caching ; code ; o Make sure R1 is clear (MSB of xfer count) on error paths ; prior to calling REQCOM ; o Change some LOG_ERRORs, to return a fatal status .vs. ; SS$_NORMAL ; o Remove 1st error log entry for SCSI$C_BUSY, to last entry ; o Add SS$_MEDOFL to the list of errors, which DKDRIVER doesn't ; log ; o Remove DISPATCH macro usage, due to too not a dense set of ; values ; o Pick up X-26 from Theta Stream ; X-18U7 JFD0817 James F. Dunham 4-APR-1995 ; o Remove private defintions of IRP$V_FORCEMV and ; CAN$C_MSCPSERVER ; o Decrease queue table depth sizes. ; o Increate queue scan histogram counter ; X-18U6 RCL Rick Lord 5-Apr-95 ; 1) Make WCE a preferred bit in it's caching page descriptor ; rather than setting to explicitly in-line, since it is the ; more common case. The only section of code which did not ; alter it was for non-512-byte block size devices, which for ; now would be only CD-ROMs, and WCE is not an issue for them. ; 2) Create LOAD_TENBYTE and STORE_TENBYTE macros to simplify ; saving and restoring the TENBYTE bits in the UCB and SCDRP ; (they're not the same bit offset). ; 3) In PROCESS_MODE_INFO, make the error recovery page the first ; page for all types of devices, not just for CD-ROMs, and if ; it's successful then the state of the SCDRP.TENBYTE bit in ; the UCB. ; 4) In PROCESS_MODE_INFO, refresh the SCDRP.TENBYTE bit from ; UCB.TENBYTE if any DO_MODE_PAGE fails and the routine is ; going to continue - if it's going to exit then there's no ; need to do the refresh. ; 5) In PROCESS_MODE_FORMAT_FLOPPY, initialize the SCDRP.TENBYTE ; bit from UCB.TENBYTE prior to the first DO_MODE_PAGE. ; There's no need to refresh the SCDRP bit in this routine ; because it exits if DO_MODE_PAGE fails. ; X-18U4 SCS Sue Sommer 17-Mar-1995 ; - Convert any errors from PROCESS_MODE_INFO to SS$_MEDOFL ; so that retries are possible. ; X-18 RCL Rick Lord 1-Feb-95 ; In IO_PACKACK, if PROCESS_MODE_INFO fails for any reason and the ; TENTYE bit is still set, clear it and try PROCESS_MODE_INFO ; again. This works around devices which appear to complete ; 10-byte mode sense requests successfully but in fact don't ; send usable data. ; X-17 GWW Grace Wang 27-Jan-1995 ; In PROCESS_MODE_INFO, default sector=4, track=6 to avoid ; MSCP Server crash (see Zeta QAR 2688 - with UCB$B_TRACKS ; initialized to zero, an unknown cause left it zero after ; PROCESS_MODE_INFO and VALIDATE_GEOMETRY of a RZ25 device. ; MSCP Server copies this zero to MSCP$W_GROUP and bugchecks ; to show that it won't tolerate zero geometry value.) ; X-7 SCS Sue Sommer 7-Nov-1994 ; Add TENBYTE flag to DK_FLAGS to indicate support for 10-byte ; mode sense commands. Modify PROCESS_MODE_INFO to reference ; this flag and prevent unnecessary 10-byte command attempts. ; ; X-45 JJF0040 J. Jeffery Friedrichs 17-Nov-1995 ; Backport the following fix from THETA ; ; X-31 GWW002 Grace Wang 26-May-1995 ; Fix << the MOUNT/CLUSTER/NOWRITE does not write lock on ; the node which own the disk>> problem ; ; X-44 TGG0111 Tom Goodwin 7-Apr-1995 ; Add code to change CTRLERR,DRVERR and TIMEOUT errors to ; MEDOFL which will force mount verify to be invoked and retry ; the command. ; ; X-43 TGG0003 Tom Goodwin 20-Oct-1994 ; V6 QAR #774 - When a cancel I/O is issued, the ; UCB$V_GK_CHK_COND bit in the UCB$L_DK_FLAGS is cleared if ; the process ID matches the UCB$L_GK_PID. The UCB$L_GK_PID ; will also be cleared. ; ; X-42 TGG0002 Tom Goodwin 19-Apr-1994 ; V6 QAR #710 The UCB$B_SCSI_VERSION field only checks for ; CCS and SCSI-2. Checks for SCSI-1 value need to be added. ; ; X-41 KFM Kevin F. Martin 07-Feb-1994 ; Add DDR support. ; ; X-40 TGG0001 Tom Goodwin 3-Feb-1994 ; ; 1) V6 QAR #584 Add FDT dispatch for IO$_DSE function ; code to DK_DSE routine. ; 2) Found during work on above QAR, IO_DSE routine needs ; to segment large DSE's (>63.5kB) into multiple ; SCDRP's to prevent crash in PK_DRIVERs. ; ; RCL0001 Rick Lord 17-Dec-1993 ; ; 1) Change amount of data accepted in response to a 6-byte ; MODE SENSE command from 150 to 255 bytes in both the CDB ; and the DMA_LEN field to prevent pages from being ; truncated. ; ; 2) Modify the size of the MODE SENSE data section of an ; error log packet to allow up to 255 bytes to be logged. ; ; 3) Add checks to MODE_SENSE and MODE_SENSE_CHANGABLE to ; prevent garbage from being read as size bytes (this would ; have happened if a page code was the last byte of a ; transfer). ; ; 4) Add Control Mode QERR and EECA bits to bits checked in ; MODE_SENSE_CHANGABLE which will cause an error to be ; logged if found to be in wrong state and unchangable. ; ; 5) Change UCB$B_CTRL_MODE to UCB$B_CTRL_MODE_PAR to match ; the naming format of the other saved MODE SENSE pages. ; ; 6) In MODE_SELECT add Control Mode QERR and EECA to the bits ; which will cause a MODE SELECT to be issued if found to be ; in the wrong state. ; ; 7) Retry a command if it fails with a SENSE KEY of 0 - the ; conditions under which this may occur either do not apply ; to direct access devices, are caught by MODE SENSE or else ; do not even indicate an error condition. ; ; 8) Increased the size of the UCB Control Mode MODE SENSE ; pages to accomodate Seagate drives which are violating ; the SCSI spec by returning too much data. ; ; 9) Don't try to clear ARRE or AWRE in the Control MOde page if ; the device is SCSI-2 or later, as the BBR algorithms on ; these later devices are expected to be up to snuff. ; ; X-39 KFM Kevin F. Martin 08-Dec-1993 ; V552H4 QAR #121: Clear GK I/O active flag when read access to ; command buffer is denied. ; ; X-38 MCY Mary Yuryan 14-May-1993 ; Fold in J. Guineau's AUDIO bugfix. ; WJG John Guineau 09-Apr-1993 ; Fix error paths in IO$_AUDIO FDT/STARTIO routines related ; to bad/improperly initialized AUCB fields. ; ; X-37 RCL Rick Lord 6-May-1993 ; ; Define OPTICAL and WORM bits in UCB$L_DK_FLAGS. ; ; Modify INQUIRY subroutine to set OPTICAL bit in UCB$L_DK_FLAGS ; if the SCSI device type code is optical, or the WORM bit if the ; device type code is WORM. ; ; Modify MODE_SENSE to set HWL and SWL if the device is WORM, or ; if it's Optical with WORM media mounted; also, do not require ; track and sector information to be returned from Mode Sense ; for Optical devices. ; ; X-36 MCY Mary Yuryan 12-Apr-1993 ; Add the following SCSI devices - RW504, RW510, RW514, ; RW516, RWZ51, RWZ52, RWZ53, RWZ54, RWZ31 - RZ28L, RZ26M - ; EZ31, EZ32, EZ33, EZ34, EZ35, EZ31L, EZ32L, EZ33L, EZ56R ; - HSZ15. ; ; X-35 WJG W. John Guineau 6-Apr-1993 ; - No changes, FOLD VIKING TCQ stream into CORAL, only ; changed .IDENT to match CORAL stream ; - uncomment the define for V60_BUILD for Coral. ; ; X-18A7A7-Q WJG W. John Guineau 15-Mar-1993 ; - Lock out other processes in IO$_DIAGNOSE while ; a an I/O is pending for one GK process. Also, ; Make sure process for a pending check condition is ; the process that caused it. ; ; X-18A7A6-Q WJG W. John Guineau 3-Mar-1993 ; - Make TEST_UNIT_READY send TUR as NON-QUEUED which ; will still sync queues in a queued environment, but ; works around an HSZ10 bug where the first queued command ; sent when a unit attention is pending get's eaten. ; ; X-18A7A5-Q WJG W. John Guineau 2-Feb-1993 ; - Set UCB$M_BSY during PACKACK just to be safe. ; - Add BBR_IN_PROG flag to UCB$L_DK_FLAGS. This bit ; is set when a BBR starts, cleared when it completes. ; If BBR_IN_PROG is set when a second thread starts ; a BBR operation, that thread will FORK_WAIT until ; it clears. This prevents more than one thread from ; attempting a BBR operation at one time. ; - Move UCB$L_SCDRP_SAV2, UCB$L_ADDNL_INFO and ; UCB$B_SENSE_KEY into SCDRP for multi-threaded ; synchronization. ; ; X-18A7A4-Q WJG W. John Guineau 15-Jan-1993 ; - Add a G^ to the call to EXE$ALONONPAG in READ_CD_MCN ; to fix crash. ; ; X-18A7A3-Q WJG W. John Guineau 11-Jan-1993 ; - Fix floppy formatting bug in MODE_SENSE where a ; BGEQ was being used instead of BLSS causing ; UCB$L_FORMAT_PRAMS to never be allocated. ; - Add a '-Q' to the compiled in image ident (.IDENT) for ; easy detection of command queuing based drivers. ; ; X-18A7A2 WJG W. John Guineau 8-Jan-1993 ; - Remove unused/debug code ; - Add HSZ20, HSZ40 (FIB) to CHECK_FOR_RAID macro ; - fix bug in histogram which would corrupt pool on ; xfers larger than 127 blocks ; - Deal with MODE_SENSE page 0x0A (queueing parameters) ; to make sure the QErr bit is not set ; - Attempt to clear the ARRE/AWRE bits in MODE_SENSE page 1 ; - Make LUN information handling in INQUIRY SCSI-2 compliant ; ; X-18A7A1 WJG W. John Guineau 12-17-1992 ; - Add CHECK_FOR_RAID_DEVICE macro and UCB$V_RAID ; bit. RAID devices use 10 byte mode sense/select ; commands. ; - Fix bug in copying of additional sense bytes ; in SAVE_ADDNL_INFO: to do a signed compare of ; length of additional sense data against length ; of buffer field in UCB ; - Add command queuing support ; - merge BLADE X-31,X-32 changes for VIKING Checkin: ; X-32 MCY Mary Yuryan ; Add "B" variant disks to device recognition tab ; RZ27B, RZ28B, RZ29B, RZ73B, RZ74B, RZ75B. Add ; Add HSZ20,HSZ40 - Raid subsystem. ; ; X-31 MCY Mary Yuryan ; Fix 2 bugs in the ATTEMPT_REORDER routine. ; 1)Update R4 correctly so as not to alter the be ; the elevator algorithm; currently the scan di ; be effected. ; 2)Provide UCB$L_FAIRNESS_CNT (with value of 4) ; malicious user from looping infinitely while ; same LBN. Checks added in SWAP_IRP:, SCAN_DO ; PERFECT_IRP:. This could slow or even hang t ; ; X-30 JSSBLADE2 John S. Simakauskas 1-July-1992 ; Fix build bug introduced in X-29, ; SCSI_DEV_TYPES table was made too long. ; ; X-29 JSSBLADE John S. Simakauskas 24-June-1992 ; Add device recognition for RZ26L,RRD43,RRD44. ; ; X-28 MCY Mary Yuryan 12-Jun-1992 ; Always return good status in the READ_CD_MCN routine ; to keep 3rd party CDROMS that do not support SCSI-2 ; from failing. ; ; X-27 MCY Mary Yuryan 03-Jun-1992 ; Add check for RRD40S device type in IO_PACKACK, and ; branch around READ_CD_MCN for audio if device is ; ; X-26 MCY Mary Yuryan 03-Apr-1992 ; Add ASSUME statement before $DEFEND of UCB to check for ; illegal System disk UCB expansion. Value is calculated ; in longwords. ; ; X-25 MCY Mary Yuryan 11-Mar-1992 ; Add latent support for RZ28 and RZ29. ; Fold in John Guineau's AUDIO fix from AMBER - Change ; UCB$M_CDROM symbol to UCB$V_CDROM in BBC instruction ; in IO_PACKACK routine. ; ; X-24 MCY Mary Yuryan 20-Feb-1992 ; Add latent support for RAID controller - HSZ10. ; ; Fold in Richard Napolitano's changes for AUDIO fix. ; RLN Richard L. Napolitano 15-Dec-1991 ; 1) Format zero workaround ; 2) Modified LOG_EXTND_SENSE not to log read errors ; to CDROM's being mounted foreign. ; ; X-23 BP4282 Brian Porter 04-FEB-1992 ; Use an ADAWI to increment and decrement UCB$W_QLEN. ; ; Sync .IDENT to VSC. ; ; X-21 MCY Mary Yuryan 09-Jan-1992 ; Fix bug in GET_DEVICE_TYPE - Restore R0 containing ; INQUIRY command data pointer. ; ; X-20 MCY Mary Yuryan 23-Dec-1991 ; Merge AUDIO and Latent device support with C2. ; ; X-16A1A1A1 FAK003 Forrest A. Kenney 26-Nov-1991 ; Merge latest round of BLADE fixes. ; ; X-19 WJG0048 W. John Guineau 14-Nov-1991 ; Add DDR support. UCB$L_DISABLE_DDR is a flag to disable ; the use of DDR. If non-zero, no DDR will be used. ; ; X-18A1 MCY Mary Yuryan 11-Nov-1991 ; Merged Rich Napolitano's AUDIO changes into the driver. ; Redefine READPROMPT function to AUDIO using the same IODEF ; symbol value for READPROMPT. ; ; Richard L. Napolitano ; Implemented AUDIO extension to DKDRIVER to allow AUDIO ; commands to be sent to SCSI II compliant CD-ROM devices. ; These extensions allow the execution of CD AUDIO commands ; from application programs. ; ; Mary Yuryan 10-Dec-1991 ; Add latent device support: RZ27,RZ37,RZ38,RZ75,RZ59, ; EZ51,EZ52,EZ53,EZ54,EZ58,RZ13,RZ14,RZ15,RZ16,RZ17,RZ18, ; RZ34L,RZ35L,RZ36L. ; ; X-18 MCY Mary Yuryan 10-Oct-1991 ; Add latent support for the RZ26, RZ36, adn RZ74. ; (VSC Ident change.) ;------------------------------------------------------------------------------- ; ; X-32 MCY Mary Yuryan / John Guineau 2-Oct-1991 ; Fix DK_CTRL_INIT routine to properly save CDDB initialization ; registers before forking. ; ; X-31 JTK Jim Klumpp 13-Aug-1991 ; Change to place in the driver where ATTEMPT_REORDER ; is called to the end of STARTIO so third-party disk ; caching products can continue to intercept I/Os at ; the front of STARTIO. Change the ATTEMPT_REORDER ; routine to sort only the pending queue, ignoring ; the active IRP at UCB$L_IRP. Terminate the pending ; queue scan after a finite of IRPs have been considered ; for reorder. ; ; X-30 WJG W. John Guineau 16-May-1991 ; Use EXE$ZEROPARM as FDT routine for IO$_NOP to solve ; crashes from this function code. ; ; X-27U2 JJM Jeff McLeman 21-Mar-1991 ; Increase floppy disconnect timeouts to 30 seconds. ; ; X-27U1 MCY Mary Yuryan 20-Feb-1991 ; Add new SCSI devices: RWZ01 (SCSI_MO), RZ24L, RZ25L, ; RZ55L, RZ56L, RZ57L. ; ; X-27 KAH Kenneth A. House 08-Feb-1990 ; Add support for new SCSI-FDI adapter, including ; 1) RX23s, RX33s diskette drives may now use synchronous ; data transfers; ; 2) support RX26 diskette drive, including ; a) format DD, HD, and ED diskettes; ; b) add two-bit FLOPPY_MEDIA field to UCB$L_DK_FLAGS; ; c) fill in FLOPPY_MEDIA field in the MODE_SENSE routine; ; 3) delete misleading comments. ; ; X-26 JTK Jim Klumpp 30-Jan-1991 ; Add a workaround for RZ23 read long bug. After a read ; long command is sent to the RZ23, the next mode select ; command fails with an illegal request sense key. The ; fix is to simply retry the mode select command once. ; ; X-25 MCY Mary Yuryan 25-Jan-1991 ; Increase RRD42 Disconnect timeout to 120 seconds. ; ; X-14U12 GH001 Gary Hughes 21-Dec-1990 ; Fix up media ID mismatch for RZ23L, RZ57I. ; ; X-14U11 JTK Jim Klumpp 11-Dec-1990 ; 1) Add new devices: RZ72, RZ73, RZ35. ; ; 2) In SET_UNIT_ONLINE, set the LCL_VALID bit in the UCB ; if the PACKACK completes successfully for the system ; disk. This is required for host-based shadowing booting. ; ; X-14U10 JTK Jim Klumpp 28-Nov-1990 ; 1) Change the disconnect timeout time for the RRD42 to ; 45 seconds. ; ; 2) Add host-based shadowing support including routines to ; check that a device is capable of supporting HBS (CHECK_HBS), ; to force uncorrectable ECC errors on a set of blocks ; (FORCE_ERROR), and to erase a set of blocks (IO_DSE). ; ; 3) Fix clearing of LCL_VALID bit in UCB at the end of ; IO_PACKACK. ; ; 4) Use byte count divisor returned by SPI$CONNECT in ; DK_UNIT_INIT. ; ; WCY0146 Wai C. Yim 7-Nov-1990 ; 1) Add code to create CDDB in controller init. ; ; 2) Change all references of EXE$GL_SYSUCB to SYS$AR_BOOTUCB ; Since it is no longer the same for shadowing. ; ; 3) Add shadowing booting support. Synchronize with DSA ; class driver by checking for different SYS$AR_BOOTUCB ; from EXE$GL_SYSUCB. ; ; 4) Set SCSI bit in DEVCHAR2 in all devices by means ; of DPT_STORE UCB macro. ; ; 5) Add CRESHAD and REMSHAD FDT routines. ; ; X-14U9 BCL043 Barbara C. Leahy 27-Nov-1990 ; Added context save on IO_DIAGNOSE function for mixed-mode ; media support. ; ; X-14U8 JTK Jim Klumpp 26-Oct-1990 ; Send a request sense command immediately after sending a ; mode select to clear out a possible pending check condition ; that could otherwise cause a subsequent command to fail. ; Also, in TRANS_SENSE_KEY, treat mode select change status ; as normal. An assumption here is we're not running in a ; multi-initiator environment. ; ; X-14U7 MCY Mary Yuryan 29-Oct-1990 ; Change the spinup timeout value from 20 to 30 seconds ; to accomodate the RZ25. Change READY_POLL_INTERVAL to 1, ; and READY_POLL_CNT to 30. ; ; ; X-14U6 JTK Jim Klumpp 19-Sep-1990 ; Add processing of LCL_VALID bit in IO_FORMAT and IO_PACKACK. ; Call SET_CONN_CHAR in SET_UNIT_ONLINE to prevent format ; operations from timing out before a PACKACK function has ; successfully executed. ; ; X-14U5 MCY Mary Yuryan 23-Aug-1990 ; Change the symbol definition of LO_COST_CD to the ; correct device name of RRD42. ; ; X-14U4 JTK Jim Klumpp 13-Jul-1990 ; WJG W. John Guineau 04-Jun-1990 ; Added UCB$B_SCSI_VERSION to save INQUIRY response data ; format (SCSI version). This allows version-specific ; code paths in mode select, where the setting of the ; non-changable parameters is incompatible between SCSI-1 ; (CCS) and SCSI-2. ; ; Fix IO$_WRITECHECK function. Prior to this change, this ; function was identical to a write with the datacheck ; modifier. The correct operation is to compare the data ; in the user's buffer with that on the disk, and not ; modify the contents of either the user's buffer or the ; disk. ; ; X-14U3 JTK Jim Klumpp 6-Jun-1990 ; 1) In SETUP_CMD, map the buffer with high priority to avoid ; deadlock. The map buffers elsewhere can remain at low ; priority, as there's no danger of deadlock on the first ; call to map buffer per QIO function. ; ; 2) Calculate the value for UCB$W_CYLINDERS using the same ; algorithm as DUDRIVER so that all nodes in a cluster see ; see the same geometry. ; ; 3) Work around a bug in RZ5x drives which cause them to ; report busy SCSI status indefinitely after a reselection ; timeout. Pull on bus reset if this situation is detected. ; ; X-14U2 JTK Jim Klumpp 20-Dec-1989 ; Use extended read and write SCSI commands for transfers ; to blocks above 1FFFFF (hex). ; ; X-14U1 JTK Jim Klumpp 1-Dec-1989 ; Add seek optimization routine. ; ; X-14 JTK Jim Klumpp 22-Sep-1989 ; Return MEDOFL status when an attempt is made to mount a ; removable device whose media is not inserted. Increase ; default disconnect timeout time for all RZ disks to 20 ; seconds. ; ; X-13 JTK Jim Klumpp 20-Sep-1989 ; Fix setting of disconnect timeout for floppy formatting. ; ; X-12 RLN Richard L. Napolitano 29-Aug-1989 ; Workaround a microcode problem in the RZ55 where during ; recovered with retry read operation, the RZ55 returns ; an additional block if the DTE bit is set. ; ; ; X-11 RLN Richard L. Napolitano 25-Aug-1989 ; Fix problem where during a READ operation of a transfer ; with a bad block that is reassigned, the blocks following ; the reassigned blocks are erroneously written. ; ; X-10 DGB0317 Donald G. Blair 05-Aug-1989 ; Add DPT$V_NO_IDB_DISPATCH bit to the driver prologue table. ; In SETUP_CMD, use correct register to set up timeout ; fields. ; ; X-9 JTK Jim Klumpp 12-Jul-1989 ; Add LUN support. Don't allocate the IRP portion of ; the SCDRP. Remove references to the SCDT. Increase ; number of arguments passed to SET_CONN_CHAR. Change ; priv required for IO_DIAGNOSE function. ; ; X-8 JTK Jim Klumpp 22-Jun-1989 ; Add callback support and data structure version ; checking for SPI$CONNECT. Add fastboot support. ; Fix logging of reassign block error. Check HWL bit ; to prevent writes to a write-locked disk. Fix ; handling of media changes in TRANS_SENSE_KEY. ; ; X-7 JTK,KAH Jim Klumpp, Kenneth A. House 15-Jun-1989 ; ; 1) Change descriptor length in reassign parameter list to 4. ; 2) Fix checking of read/write flag in DK_DIAGNOSE. ; 3) Increase disconnect timeout for reassign command. ; 4) Allow writing of double-density (DD) microfloppies only ; when the UCB$L_DK_FLAGS flag is asserted, ; which it won't be unless the driver has been patched. ; Changes include ; A) add DD_BYPASS flag to UCB$L_DK_FLAGS, ; B) clear DD_BYPASS in UNIT_INIT to provide space for ; an in-place patch, ; C) set UCB$L_DEVCHAR flag in MODE_SENSE routine if an ; RX23 diskette has a 250 KHz transfer rate (implies DD), ; D) adding hooks for DD format and geometry parameters in ; MODE_SELECT_FORMAT_FLOPPY (only if DD_BYPASS is set). ; 5) Fix branch destination of a BBSS used for a bit set in ; MODE_SENSE routine. ; ; X-6 JTK,KAH Jim Klumpp, Kenneth A. House 25-Mar-1989 ; A number of misc changes for PVAX1 including: ; 1) Update ident to match that on master pack. ; 2) Add pass-through routine. ; 3) In INQUIRY, use just 5 bits if peripheral device type ; and 4 bits of response data format to conform to SCSI-2. ; 4) Change SCSI status byte mask from ^XE1 to ^XC1 to conform ; to SCSI-2. ; 5) In TRANS_SENSE_KEY, ignore unit attentions caused by media ; changes if the device is not volume enable to avoid mount ; failures. ; 6) Fix R6 bug in MODE_SELECT_FORMAT_FLOPPY. ; 7) Ensure unformatted diskette causes MODE_SENSE to return ; SS$_FORMAT status. ; 8) Move call of MODE_SENSE_CHANGABLE from within MODE_SENSE ; to follow each call to MODE_SENSE ; 9) Allow MODE_SENSE to return SS$_FORMAT in IO_FORMAT routine ; (implies that the diskette isn't currently formatted). ; 10) Avoid logging error when MODE_SENSE detects an unformatted ; floppy. ; ; X-20-B KAH079 Kenneth A. House 04-May-1989 ; Increase size of MODE_SELECT maximum data transfer; ; Merge changes from Jim Klumpp & Lee Leahy. ; ; X-20 JTK Jim Klumpp 31-Mar-1989 ; Clear UCB$L_BCNT in SETUP_CMD if no data is to ; be transferred to prevent possible corruption. ; ; X-19 LPL Lee Leahy 29 Mar 1989 ; Changed media ID for generic DK devices from ; 0 to 22C8BC0 (DK-DKX0) to enable them to be ; properly served via the MSCP. ; ; X-18-B KAH073 Kenneth A. House 17-Apr-1989 ; Debugging; ; Merge changes from Jim Klumpp. ; ; X-18-A KAH070 Kenneth A. House 14-Mar-1989 ; Add SCSI floppy support. ; ; X-18 JTK Jim Klumpp 27-Jan-1989 ; Add MCSP extension to UCB. Advance ident to match ; CMS generation on LES pack. ; ; X-4 JTK Jim Klumpp 19-Jan-1989 ; Perform minimum revision checking on devices. ; ; X-3 JTK Jim Klumpp 13-Jan-1989 ; Clean up comments, disable tracing for SDC release. ; ; X-2 RLN Richard L. Napolitano 20-DEC-1988 ; ; 1) Remove loop in MODE_SENSE_CHANGEABLE. ; 2) Complete fix to ignore DCR bit in MODE_SENSE routine ; 3) Fix register useage for flexible disk geometry page. ;- .SBTTL + .SBTTL + SYMBOL DEFINITIONS .SBTTL + .SBTTL External symbol definitions ; ; External symbols ; $ARBDEF ; Define ARB offsets $CANDEF ; Cancel reason codes $CDDBDEF ; Class Driver Data Block $CRBDEF ; Channel request block $DCDEF ; Device classes and types $DDBDEF ; Device data block $DEVDEF ; Device characteristics $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 $IRPEDEF ; I/O request packet extension $MODEDEF ; Mode page definitions $MSCPDEF ; Define MSCP symbols $NSADEF ; Security Symbols $ORBDEF ; Object Rights block $PCBDEF ; Process control block $PRVDEF ; Privilege mask $PTEDEF ; Page table entry symbols $SCSIDEF ; SCSI definitions $SCDRPDEF ; SCSI SCDRP symbols $SPDTDEF ; SCSI PDT symbols $SSDEF ; System status codes $UCBDEF ; Unit control block $VADEF ; Virtual address symbols $VCBDEF ; Volume Control Block symbols $VECDEF ; Interrupt vector block .SBTTL Misc local symbols ; ; Local symbols ; V60_BUILD = 1 ; Enable VMS V6.0 features COLLECT_PERF_DATA = 1 ; Enable collection of various stats ; 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 SCDRPS_PER_UNIT = 15 ; Number of SCRPs to pre-allocate per unit PACKACK_RETRY = 10 ; Number of times to retry PACKACK READY_POLL_INTERVAL = 1 ; Interval for test unit ready polling READY_POLL_CNT = 30 ; Number of times to poll for unit ready MAX_BCNT = <<64*2>-1>*512 ; Maximum byte count per ; transfer = 64KB - 1 page FMT_PARAM_SIZE = 24 ; Space for format page params FLX_PARAM_SIZE = 32 ; Space for flex disk page params UCB_REC_PARAM_SIZE = 12 ; Space in UCB for error recovery params UCB_CTRL_MODE_SIZE = 8+4 ; Space in UCB for control mode params RW_RETRY_CNT = 3 ; Read/write retry count REASSIGN_RETRY_CNT = 3 ; Retry count for block reassignment REWRITE_RETRY_CNT = 3 ; Retry count for rewrite after reassign HWERR_RETRY_CNT = 10 ; Retry count for hardware errors DEFAULT_DISCONNECT_TIMEOUT = 20 ; Default values in seconds for disconnect DEFAULT_PHASE_CHANGE_TIMEOUT = 4; and phase change timeouts RRD40_DISC_TMO = 20 ; RRD40 disconnect timeout value RRD42_DISC_TMO = 120 ; RRD42 disconnect timeout value RX_DISC_TMO = 30 ; Floppy disconnect timeout value RX_PHS_TMO = 10 ; Floppy phase change timeout value RX_FMT_DISC_TMO = 10*60 ; Floppy format disconnect timeout value REASSIGN_DISCON_TMO = 60 ; Reassign disconnect timeout SS$_RECOVERR = 2 ; Recoverable error detected. Note that ; this status code is used internlly ; to distinguish between recoverable and ; non-recoverable errors, but is never ; returned as a QIO status. DK_ERROR_REVISION = 2 ; Errorlogging revision supported by ; this driver. This should be incremented ; each time an incompatible change is ; made to the errorlog packet format. DTE_EXTRA_BYTES = 512 ; Number of additional bytes received RZ55_MIN_NODTE_ERROR = ^A'0900' ; Mininum revision with fixed microcode EXTND_LBN_LIMIT = ^X1FFFFF ; Maximum LBN for which a normal read or ; write command can be used. The extended ; read/write commands must be used for ; LBNs above this value. MAX_BUSY_TIME = 10 ; Maximum amount of time to allow a ; device to remain busy before resetting ; the bus SENSE_LEN = 18 ; Sense data length SENSE_ALLOC = 255 ; Sense data allocation length CD_MCN_LEN = 24 ; Media Catalog Number (UPC/Bar code) ; sub-channel data for CD-ROMs ; Histogram buckets are expressed as a power of 2 to make collection ; fast - basically we logically shift the datum by the bucket factor ; to get a bucket index. XLEN_HIST_BUCKET_SHIFT = 2 ; If MAX_XFER is 128, 128>>2 = 32 buckets XLEN_HIST_BUCKETS = 32 ; we will round down to 64K (128blk) xfers XLEN_HIST_SIZE = <<128/4>*4> ; 1 long/bucket XLEN_HIST_TURNOVER = 100 ; Recalc qdepth after this many I/O READ_LONG_DATA_LEN = 1024 ; Maximum data length associated WRITE_LONG_DATA_LEN = 1024 ; with read long and write long ; commands, respectively. ; Argument list offset definitions P1=0 ; First function dependent parameter P2=4 ; Second function dependent parameter P3=8 ; Third function dependent parameter P4=12 ; Fourth function dependent parameter P5=16 ; Fifth function dependent parameter P6=20 ; Sixth function dependent parameter ; seek reordering stuff FAIRNESS_CNT = 4 .SBTTL Disk class driver extensions to the UCB ; ; Disk class driver extensions to the UCB. ; $DEFINI UCB ; Start of UCB definitions . = UCB$K_MSCP_DISK_LENGTH $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$L_TRACE_BUF .BLKL 1 ; Address of trace buffer $DEF UCB$L_SCDT .BLKL 1 ; SCDT address $DEF UCB$L_DK_FLAGS .BLKL 1 ; Class driver flags $VIELD UCB,0,<- ; ,- ; Removable media ,- ; First unit attention seen (used to - ; prevent logging an error for the - ; first unit attention seen) ,- ; Spin-up in progress ,- ; Device supports disconnect ,- ; Device supports synchronous operation ,- ; Modes sense page 1 recieved ,- ; Modes sense page 10 recieved ,- ; Disable errorlogging ,- ; Device is out-of-rev - ; Hardware write-locked ,- ; Device is a flexible disk drive ,- ; Device supports FORMAT opcode ,- ; Drive doesn't support REASSIGN_BLOCK ,- ; Allow writing of DD diskette ,- ; Check made for host-based shadowing ,- ; Diskette type in floppy drive - ; 0 - none, or unknown - ; 1 - DD - ; 2 - HD - ; 3 - ED ,- ; Device is a CD-ROM ,- ; CD media change occured if bit is - ; cleared. Used for Audio CD's, which - ; are not mounted, to indicate UCB - ; stored CD-ROM Sub-channel data is - ; valid. ,- ; Set if device is known to be an array ,- ; Set if port supports command queing - ; Device supports command queing ,- ; Check cond seen by IO$_DIAGNOSE func ,- ; I/O active on IO$_DIAGNOSE ,- ; BBR in progress - lock access to UCB ,- ; Device is an optical device ,- ; Device is a WORM device, or an optical - ; device with WORM media > ; Device supports 10-byte mode sense command $DEF UCB$W_PHASE_TMO .BLKW 1 ; Phase change timeout $DEF UCB$W_DISC_TMO .BLKW 1 ; Disconnect timeout $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$B_RW_RETRY .BLKB 1 ; Read/write retry count $DEF UCB$B_REASSIGN_RETRY .BLKB 1 ; Reassign block retry count $DEF UCB$B_REWRITE_RETRY .BLKB 1 ; Rewrite after reassign retry count $DEF UCB$B_READY_RETRY .BLKB 1 ; Polling count for unit ready $DEF UCB$B_HWERR_RETRY .BLKB 1 ; Hardware error retry count $DEF UCB$W_BLOCK_SIZE .BLKW 1 ; Number of bytes per block $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$L_TR_QIO_STS .BLKL 1 ; Address in trace buf to put QIO status $DEF UCB$L_TR_SCSI_CMD .BLKL 1 ; Address in trace buf to put QIO status $DEF UCB$L_TR_SCSI_MSG .BLKL 1 ; Address in trace buf to put QIO status $DEF UCB$L_TR_SCSI_STS .BLKL 1 ; Address in trace buf to put QIO status $DEF UCB$L_HW_REV .BLKL 1 ; Hardware revision info from inquiry $DEF UCB$L_CUR_LBN .BLKL 1 ; Current LBN, used for read/write ; reordering $DEF UCB$L_BUSY_TIME .BLKL 1 ; Time at which bus should be reset if ; device remains busy $DEF UCB$B_RECOV_PAR .BLKB UCB_REC_PARAM_SIZE ; Error recovery parameters $DEF UCB$B_RECOV_CPAR .BLKB UCB_REC_PARAM_SIZE ; Err rec changable parameters $DEF UCB$B_CTRL_MODE_PAR .BLKB UCB_CTRL_MODE_SIZE ; Control mode parameters $DEF UCB$B_CTRL_MODE_CPAR .BLKB UCB_CTRL_MODE_SIZE ; Control mode changable parameters $DEF UCB$A_FORMAT_PARAM .BLKL 1 ; Address of sense information ; needed to format floppies $DEF UCB$L_SAVE_CONN_CHAR .BLKL 1 ; Address of saved connection ; characteristics buffer for IO ; diagnose buffer $DEF UCB$B_LUN .BLKB 1 ; Logical unit number (LUN) $DEF UCB$B_SEEK_DIR .BLKB 1 ; Current motion of heads, used for ; reordering of I/Os for seek optimization $DEF UCB$W_READL_LEN .BLKW 1 ; Value to use in read long len field $DEF UCB$B_SCSI_VERSION .BLKB 1 ; SCSI version (CCS or SCSI-2) $DEF UCB$L_DISABLE_DDR .BLKL 1 ; Non zero means disable DDR ; (default is DISABLED!) $DEF UCB$B_MCN_SCDATA ; For CD-ROMS, Sub-Channel data format2 .BLKL 1 ; Media Catalog Data (UPC) ;$DEF UCB$B_ALIGN_RSVD ; Align UCB ; .BLKB 3 $DEF UCB$L_FAIRNESS_CNT .BLKL 1 ; Fairness counter in ATTEMPT_REORDER $DEF UCB$L_QUEUED_IO_COUNT ; Number of I/O in the port .BLKL 1 $DEF UCB$L_GK_PID ; Process ID of check condition owner .BLKL 1 .IF DEFINED COLLECT_PERF_DATA $DEF UCB$L_QDEPTH ; The max qdepth for this device .BLKL 1 $DEF UCB$L_QDEPTH_TURNS ; How many times we've actually changed .BLKL 1 ; qdepth for this device $DEF UCB$L_READ_COUNT ; Count reads done .BLKL 1 $DEF UCB$L_WRITE_COUNT ; Count writes done .BLKL 1 $DEF UCB$L_OTHER_COUNT ; All other I/O functions .BLKL 1 $DEF UCB$L_READ_XLEN_HIST ; Pointer to Read histogram pool .BLKL 1 $DEF UCB$L_WRITE_XLEN_HIST ; Pointer to Write histogram pool .BLKL 1 $DEF UCB$L_XLEN_HIST ; Pointer to total histogram pool .BLKL 1 $DEF UCB$W_XLEN_HIST_CYCLE ; # I/Os per histogram turnover .BLKL 1 .ENDC ; DEFINED COLLECT_PERF_DATA $DEF UCB$K_DK_UCBLEN ; Length of extended UCB .IF DEFINED V60_BUILD ASSUME UCB$K_DK_UCBLEN LT- ; Check that System disk ucb expansion > ; size is not exceeded. .ENDC $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 SENSE_ALLOC ; 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 255 ; 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 $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 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 ; +-----------------------+ ; | Disc/synch flags | 1 byte ; +-----------------------+ ; | Phase change timout | 2 bytes ; +-----------------------+ ; | Disconnect timeout | 2 bytes ; +-----------------------+ ; ; The table is terminated with a VMS device code of 0. ;- .MACRO SCSI_DEV_TYPES, LIST .MACRO SCSI_DEV_TYPES1, ID_STRING, DEVICE_TYPE, MEDIA_ID,- MINIMUM_REVISION, DISCONNECT, SYNCHRONOUS,- PHASE_CHANGE_TIMEOUT, DISCONNECT_TIMEOUT ; 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 .IF IDN , GENERIC GENERIC_SCSI_DISK: .ENDC ; Minimum revision level. This field is compared to the revision field returned ; in the inquiry data. If the device is out of rev, an error is logged. .ASCII /MINIMUM_REVISION/ ; Media ID field, derived from the device type if possible, otherwise supplied ; in the table. .IF DIF , GENERIC .IF B MEDIA_ID MEDIA , .IFF MEDIA , .ENDC .IFF MEDIA , , DT$_Generic_DK ; Media value for generic DK device .ENDC ; Flags, including disconnect and synchronous $$$FLAGS = 0 .IIF IDN DISCONNECT, YES, $$$FLAGS = $$$FLAGS + 1 .IIF IDN SYNCHRONOUS, YES, $$$FLAGS = $$$FLAGS + 2 .BYTE $$$FLAGS ; Phase change timeout $$$PHASE_CHANGE_TIMEOUT = DEFAULT_PHASE_CHANGE_TIMEOUT .IF DIF PHASE_CHANGE_TIMEOUT, DEFAULT $$$PHASE_CHANGE_TIMEOUT = PHASE_CHANGE_TIMEOUT .ENDC .WORD $$$PHASE_CHANGE_TIMEOUT ; Disconnect timeout $$$DISCONNECT_TIMEOUT = DEFAULT_DISCONNECT_TIMEOUT .IF DIF DISCONNECT_TIMEOUT, DEFAULT $$$DISCONNECT_TIMEOUT = DISCONNECT_TIMEOUT .ENDC .WORD $$$DISCONNECT_TIMEOUT .ENDM SCSI_DEV_TYPES1 .IRP ENTRY, SCSI_DEV_TYPES1 ENTRY .ENDR .ENDM SCSI_DEV_TYPES .SBTTL SENSE_KEY - Build sense key to VMS status code translation table ;+ ; SENSE_KEY ; ; This macro is used to build a translation table of SCSI to VMS status codes. ; Each time extended sense 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 disk class driver error ;+ ; LOG_ERROR ; ; This macro logs a SCSI disk class driver error. The error type and VMS status ; code are placed in 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 retrying reads or writes ; to blocks with [non-]recoverable errors, errorlogging is disabled so that ; just one error is logged. Since the disabling of errorlogging can be nested, ; the old value of the DISABL_ERRORLOG flag is saved in the local UCB stack. .MACRO DISABLE_ERRLOG SUBPUSH UCB$L_DK_FLAGS(R3) ; Save current flags value ASSUME UCB$V_DISABL_ERRLOG LT 8 BISB #UCB$M_DISABL_ERRLOG,- ; Temporarily disable errorlogging UCB$L_DK_FLAGS(R3) ; .ENDM DISABLE_ERRLOG .MACRO REENABLE_ERRLOG SUBPOP UCB$L_DK_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 SCDRP stack .SBTTL SUBPOP - Pop an item from the SCDRP stack .SBTTL SUBSAVE - Save a return address on the SCDRP stack .SBTTL SUBRETURN - Return to the address saved on the SCDRP stack ;+ ; INIT_SCDRP_STACK ; SUBPUSH ; SUBPOP ; SUBSAVE ; SUBRETURN ; ; These macros manipulate the SCDRP internal stack which is used to save ; routine return address and temporary variables. ;- .MACRO INIT_SCDRP_STACK,SCDRP=R5,?l1 ; .if defined scsi_stack_checking ; .nlist meb ; cmpb ucb$b_type(ucb),#dyn$c_ucb ; beql l1 ; BUG_CHECK INCONSTATE,FATAL ;l1: ; .list meb ; .endc 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 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 DK_WAIT - Stall a thread for a specific number of seconds ;+ ; DK_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 DK_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 DK_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 DK_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 uses this information in preparing ; to send a SCSI command. The macro generates a label and the SCSI command ; information as follows: ; ; +-----------------------+ ; | SCSI cmd length | 1 byte ; +-----------------------+ ; | SCSI cmd bytes | n bytes ; +-----------------------+ ; | DMA buffer length | 2 bytes ; +-----------------------+ ; | DMA direction | 1 byte ; +-----------------------+ ; ; DMA direction is defined as: 0=write, 1=read. ;- .MACRO SCSI_CMD, NAME, CMD_BYTES, DMA_LEN=0, DMA_DIR=READ 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 DMA_LEN $$$DIRECTION = 0 .IIF IDN DMA_DIR, READ, $$$DIRECTION = 1 .BYTE $$$DIRECTION .ENDM SCSI_CMD ;+ ; Macro's used by the SCSI AUDIO code to validate user provided arguments and ; other AUDIO related functions. ;- .MACRO TSTBGTR, BYTE, VALUE, ?l9 CMPB 'BYTE','VALUE' BLEQU l9 BRW IO_BADPARM l9: .ENDM TSTBGTR .MACRO TSTBLSS, BYTE, VALUE, ?l8 CMPB 'BYTE','VALUE' BGEQU l8 BRW IO_BADPARM l8: .ENDM TSTBLSS ;+ ; This function will minimize to the smaller of val1 and val2. ; If val1 is greater than val2, then assign val1 the value of val2. ;- .MACRO MINUM val1,val2, ?l10 CMPL 'val1','val2' BLEQ l10 MOVL 'val2','val1' l10: .ENDM MINUM ; ; Macros used for RAID management ; ; ; CHECK_FOR_RAID_DEVICE ; Checks the current INQUIRY data for one of a list of known RAID device ; product names. If it's a known RAID device, the UCB$V_RAID bit is set. ; ; R0 - points to INQUIRY data, 16(R0) is PRODUCT_ID feild. ; R3 - UCB ; .MACRO CHECK_FOR_RAID_DEVICE, ?RAID,?NORAID,?RAIDE CMPL #^A/ADP-/,16(R0) ; Is this an HSZ10 proto? BEQL RAID CMPL #^A/HSZ1/,16(R0) ; Is this an HSZ10? BEQL RAID CMPL #^A/HSZ2/,16(R0) ; Is this an HSZ20? BEQL RAID CMPL #^A/HSZ4/,16(R0) ; Is this an HSZ40? BEQL RAID BRB NORAID RAID: BISL #UCB$M_RAID,UCB$L_DK_FLAGS(R3) BRB RAIDE NORAID: BICL #UCB$M_RAID,UCB$L_DK_FLAGS(R3) RAIDE: .ENDM CHECK_FOR_RAID_DEVICE ; ; IF_RAID, IF_NOT_RAID ; ; R3 - UCB ; .MACRO IF_RAID, LABEL BBS #UCB$V_RAID,UCB$L_DK_FLAGS(R3),LABEL .ENDM IF_RAID .MACRO IF_NOT_RAID, LABEL BBC #UCB$V_RAID,UCB$L_DK_FLAGS(R3),LABEL .ENDM IF_NOT_RAID ; ; SWAB ; .MACRO SWAB,REG .IIF IDN REG,R10, .ERROR Can't SWAB R10 PUSHL R10 ; Borrow R10 MOVZBL REG,R10 ; Get LSB ASHL #8,R10,R10 ; Shift LSB to MSB ASHL #-8,REG,REG ; Move MSB down MOVB REG,R10 ; Copy MSB ASHL #8,REG,REG ; Fix REG MSW MOVW R10,REG ; Copy back to REG POPL R10 ; Restore .ENDM SWAB ; ; CHECK_FOR_CMDQ ; Checks the current INQUIRY data for the CmdQue bit to be set indicating ; that the device supports command queing. If the device is not at least ; SCSI-2, no CMDQ check is done ; ; R0 - points to INQUIRY data, 7(R0) is INQUIRY options field ; R3 - UCB ; .MACRO CHECK_FOR_CMDQ,?L1 BICL #UCB$M_CMDQ,UCB$L_DK_FLAGS(R3) ; Assume no CMDQ support BBC #UCB$V_PORT_CMDQ,- ; Make sure port supports CMDQ UCB$L_DK_FLAGS(R3),L1 ; Br if no. CMPB #1,UCB$B_SCSI_VERSION(R3) ; Check SCSI version BGEQ L1 ; Br if CCS or SCSI-1 BBC #1,7(R0),L1 ; Br if CmdQue bit clear BISL #UCB$M_CMDQ,UCB$L_DK_FLAGS(R3) ; CMDQ supported! L1: .ENDM CHECK_FOR_CMDQ ; ; IF_CMDQ, IF_NOT_CMDQ ; ; R3 - UCB ; .MACRO IF_CMDQ,LABEL,UCB=R3 BBS #UCB$V_CMDQ,UCB$L_DK_FLAGS(UCB),LABEL .ENDM IF_CMDQ .MACRO IF_NOT_CMDQ,LABEL,UCB=R3 BBC #UCB$V_CMDQ,UCB$L_DK_FLAGS(UCB),LABEL .ENDM IF_NOT_CMDQ .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=DK_END,- ; End of driver label ADAPTER=NULL,- ; Adapter type UCBSIZE=,- ; Length of UCB NAME=DKDRIVER,- ; 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\F11\> ; Default ACP name DPT_STORE UCB,UCB$L_MAXBCNT,L,MAX_BCNT ; Max byte count DPT_STORE UCB,UCB$B_FLCK,B,SPL$C_IOLOCK8 ; Device FORK LOCK DPT_STORE UCB,UCB$B_DIPL,B,22 ; Device interrupt IPL DPT_STORE UCB,UCB$L_DEVCHAR,L,<- ; Device characteristics DEV$M_DIR!- ; Directory structured DEV$M_FOD!- ; Files oriented DEV$M_AVL!- ; Available DEV$M_ELG!- ; Error logging enabled DEV$M_IDV!- ; Input device DEV$M_ODV!- ; Output device DEV$M_SHR!- ; Shareable Device DEV$M_RND> ; Random Access Device DPT_STORE UCB,UCB$L_DEVCHAR2,L,<- ; Device characteristics DEV$M_SCSI!- ; SCSI device DEV$M_NNM!- ; Prefix name with "node$" DEV$M_NLT> ; No bad block information on ; last track DPT_STORE UCB,UCB$B_DEVCLASS,B,DC$_DISK ; Sample device class DPT_STORE UCB,UCB$W_DEVBUFSIZ,W,512 ; Default buffer size 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_DK_FLAGS,L,0 ; Initialize flags field DPT_STORE UCB,UCB$L_ERR_MASK,L,0 ; Initialize error mask field DPT_STORE UCB,UCB$L_HW_REV,L,0 ; Initialize HW revision level DPT_STORE UCB,UCB$W_DEVSTS,W,- ; Set no logical to physical UCB$M_NOCNVRT ; block number conversion DPT_STORE UCB,UCB$L_DISABLE_DDR,L,1 ; Disable DDR by default .IF DEFINED COLLECT_PERF_DATA DPT_STORE UCB,UCB$L_READ_XLEN_HIST,L,0 ; Make sure these are 0 DPT_STORE UCB,UCB$L_WRITE_XLEN_HIST,L,0 ; Make sure these are 0 DPT_STORE UCB,UCB$L_XLEN_HIST,L,0 ; Make sure these are 0 DPT_STORE UCB,UCB$L_QDEPTH,W,8 ; Default qdepth DPT_STORE UCB,UCB$L_QDEPTH_TURNS,L,1 ; Set counter .ENDC ; DEFINED COLLECT_PERF_DATA DPT_STORE REINIT ; Start of reload ; initialization table DPT_STORE DDB,DDB$L_DDT,D,DK$DDT ; Address of DDT DPT_STORE CRB,- ; Address of controller CRB$L_INTD+VEC$L_INITIAL,- ; initialization routine D,DK_CTRL_INIT DPT_STORE CRB,- ; Address of device CRB$L_INTD+VEC$L_UNITINIT,- ; unit initialization D,DK_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=DK,- ; Name of device START=DK_STARTIO,- ; Start I/O routine FUNCTB=DK_FUNCTABLE,- ; FDT address CANCEL=DK_CANCEL,- ; Cancel I/O routine REGDMP=DK_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. ;- DK_FUNCTABLE: ; FDT for driver FUNCTAB,<- ; Valid I/O functions ACCESS,- ; Access file and/or find directory ACPCONTROL,- ; ACP control function AUDIO,- ; Invoke CD-ROM Audio Functions AVAILABLE,- ; Available CREATE,- ; Create file and/or directory entry CRESHAD,- ; Create a shadow set virtual unit DEACCESS,- ; Deaccess file DELETE,- ; Delete file and/or directory entry DIAGNOSE,- ; Special pass-through function FORMAT,- ; Format floppy diskette MODIFY,- ; Modify file attributes MOUNT,- ; Mount volume NOP,- ; No operation PACKACK,- ; Pack acknowledge READLBLK,- ; Read logical READPBLK,- ; Read physical READVBLK,- ; Read virtual REMSHAD,- ; Remove a shadow set member SETCHAR,- ; Set device chars. SETMODE,- ; Set device mode SENSEMODE,- ; Sense mode SENSECHAR,- ; Sense characteristics UNLOAD,- ; Unload volume WRITECHECK,- ; Write check WRITELBLK,- ; Write logical WRITEPBLK,- ; Write physical WRITEVBLK,- ; Write virtual DSE> ; Data Security Erase FUNCTAB,<- ; Buffered I/O functions ACCESS,- ; Access file and/or find directory ACPCONTROL,- ; ACP control function AVAILABLE,- ; Available (rewind/nowait clear valid) CREATE,- ; Create file and/or directory entry DEACCESS,- ; Deaccess file DELETE,- ; Delete file and/or directory entry DRVCLR,- ; Driver clear MODIFY,- ; Modify file attributes MOUNT,- ; Mount volume NOP,- ; No operation PACKACK,- ; Pack acknowledge DSE,- ; Data Security Erase SEEK,- ; Seek SENSEMODE,- ; Sense mode SENSECHAR,- ; Sense characteristics UNLOAD> ; Unload volume FUNCTAB +ACP$ACCESS,<- ; Access & create file or directory ACCESS,- CREATE> FUNCTAB +ACP$DEACCESS,- ; Deaccess file FUNCTAB +ACP$MODIFY,<- ; set for dismounts ACPCONTROL,- ; for files 11 dismounts DELETE,- ; Delete file and/or directory entry MODIFY> ; Modify file attributes FUNCTAB DK_SHAD_WCHECK,<- ; Check write to shadow set mbr WRITELBLK,- ; Write LOGICAL Block WRITEPBLK,- ; Write Physical Block WRITEVBLK> ; Write VIRTUAL Block FUNCTAB +ACP$READBLK,<- ; Read functions READLBLK,- ; Read logical block forward READPBLK,- ; Read physical block forward ; READTRACKD,- ; Read track descriptor ; READHEAD,- ; Read track data and headers READVBLK> ; Read virtual block FUNCTAB +ACP$WRITEBLK,<- ; Write functions WRITECHECK,- ; Write check WRITELBLK,- ; Write Logical Block WRITEPBLK,- ; Write Physical Block WRITEVBLK> ; Write Virtual Block FUNCTAB +EXE$SENSEMODE,- ; Sense functions ; Sense mode FUNCTAB +EXE$SETMODE,<- ; FDT set mode routine SETCHAR,- ; for set chars. and SETMODE> ; set mode. FUNCTAB +EXE$LCLDSKVALID,<- ; Local disk valid functions UNLOAD,- ; Unload volume AVAILABLE,- ; Unit available PACKACK> ; Pack acknowledge FUNCTAB +EXE$ONEPARM,- ; Single parameter functions ; Format floppy diskette FUNCTAB +ACP$MOUNT,- ; Mount volume FUNCTAB DK_DIAGNOSE,<- ; Special pass-through function DIAGNOSE> ; FUNCTAB DK_AUDIO,<- ; CD-ROM Audio FDT entry (55) AUDIO> ; FUNCTAB DK_CRESHAD,- ; Create a shadow set virtual unit FUNCTAB DK_REMSHAD,- ; Remove a shadow set member FUNCTAB DK_DSE,- ; Data Security Erase FDT entry ; FUNCTAB +EXE$ZEROPARM,- ; NOP .SBTTL SCSI device types table ;+ ; SCSI_DEVICE_TABLE ; ; This table is used to translate the product ID field from the inquiry data ; to a VMS device type. Once the device type is determined, various information ; about the device such as disconnect and synchronous flags and timeout vaules ; is saved in the UCB. ;- SCSI_DEVICE_TABLE: .list meb SCSI_DEV_TYPES <- ; ; ID string Dev type Media ID Min Rev Disc Synch Phas tmo Disc tmo ; --------- -------- -------- ------- ---- ----- -------- ---------- ; <, RZ22, , <0615>, YES, YES, DEFAULT, DEFAULT>,- <, RZ23, , <0615>, YES, YES, DEFAULT, DEFAULT>,- <, RZ23L, RZL23, < >, YES, YES, DEFAULT, DEFAULT>,- <, RZ24, , < >, YES, YES, DEFAULT, DEFAULT>,- <, RZ25, , < >, YES, YES, DEFAULT, DEFAULT>,- <, RZ31, , < >, YES, YES, DEFAULT, DEFAULT>,- <, RZ55, , <0700>, YES, YES, DEFAULT, DEFAULT>,- <, RZ56, , < >, YES, YES, DEFAULT, DEFAULT>,- <, RZ57, , < >, YES, YES, DEFAULT, DEFAULT>,- <, RZ57I, RZI57, < >, YES, YES, DEFAULT, DEFAULT>,- <, RZ58, , < >, YES, YES, DEFAULT, DEFAULT>,- <, RRD40S, RRD40, <250D>, YES, NO, DEFAULT, RRD40_DISC_TMO>,- <, RRD42, , < >, YES, NO, DEFAULT, RRD42_DISC_TMO>> SCSI_DEV_TYPES <- <, RZ72, , < >, YES, YES, DEFAULT, DEFAULT>,- <, RZ73, , < >, YES, YES, DEFAULT, DEFAULT>,- <, RZ35, , < >, YES, YES, DEFAULT, DEFAULT>,- <, RWZ01, , < >, YES, YES, DEFAULT, DEFAULT>,- <, RZ24L, RZL24, < >, YES, YES, DEFAULT, DEFAULT>,- <, RZ25L, RZL25, < >, YES, YES, DEFAULT, DEFAULT>,- <, RZ55L, RZL55, < >, YES, YES, DEFAULT, DEFAULT>,- <, RZ56L, RZL56, < >, YES, YES, DEFAULT, DEFAULT>,- <, RZ57L, RZL57, < >, YES, YES, DEFAULT, DEFAULT>,- <, RZ26, , < >, YES, YES, DEFAULT, DEFAULT>,- <, RZ36, , < >, YES, YES, DEFAULT, DEFAULT>,- <, RZ74, , < >, YES, YES, DEFAULT, DEFAULT>> .IF DEFINED RZ74_CACHE .print ; Add comment to previous line <, RZ74, , <436A>, YES, YES, DEFAULT, DEFAULT>> .ENDC SCSI_DEV_TYPES <- <, RZ27, , < >, YES, YES, DEFAULT, DEFAULT>,- <, RZ37, , < >, YES, YES, DEFAULT, DEFAULT>,- <, RZ38, , < >, YES, YES, DEFAULT, DEFAULT>,- <, RZ75, , < >, YES, YES, DEFAULT, DEFAULT>,- <, RZ59, , < >, YES, YES, DEFAULT, DEFAULT>,- <, RZ34L, RZL34, < >, YES, YES, DEFAULT, DEFAULT>,- <, RZ35L, RZL35, < >, YES, YES, DEFAULT, DEFAULT>,- <, RZ36L, RZL36, < >, YES, YES, DEFAULT, DEFAULT>> SCSI_DEV_TYPES <- <, RZ13, , < >, YES, YES, DEFAULT, DEFAULT>,- <, RZ14, , < >, YES, YES, DEFAULT, DEFAULT>,- <, RZ15, , < >, YES, YES, DEFAULT, DEFAULT>,- <, RZ16, , < >, YES, YES, DEFAULT, DEFAULT>,- <, RZ17, , < >, YES, YES, DEFAULT, DEFAULT>,- <, RZ18, , < >, YES, YES, DEFAULT, DEFAULT>,- <, EZ51, , < >, YES, YES, DEFAULT, DEFAULT>,- <, EZ52, , < >, YES, YES, DEFAULT, DEFAULT>,- <, EZ53, , < >, YES, YES, DEFAULT, DEFAULT>,- <, EZ54, , < >, YES, YES, DEFAULT, DEFAULT>,- <, EZ58, , < >, YES, YES, DEFAULT, DEFAULT>> SCSI_DEV_TYPES <- <, RZ22, , < >, YES, YES, DEFAULT, DEFAULT>,- < RZ23, , < >, YES, YES, DEFAULT, DEFAULT>,- <<1578-15>, RZ55, , < >, NO, YES, DEFAULT, DEFAULT>,- <, RRD40S, RRD40, < >, YES, NO, DEFAULT, RRD40_DISC_TMO>,- <, RX23S, RX23, < >, YES, NO, RX_PHS_TMO, RX_DISC_TMO>,- <, RX33S, RX33, < >, YES, NO, RX_PHS_TMO, RX_DISC_TMO>,- <, RX26, , < >, YES, NO, RX_PHS_TMO, RX_DISC_TMO>,- <, RZ55, , , YES, NO, DEFAULT, DEFAULT>,- <, GENERIC_DK, , < >, YES, YES, DEFAULT, DEFAULT>> .IF DEFINED V60_BUILD SCSI_DEV_TYPES <- <, RRD43, , < >, YES, NO, DEFAULT, RRD42_DISC_TMO>,- <, RRD44, , < >, YES, NO, DEFAULT, RRD42_DISC_TMO>,- <, HSZ10, , < >, YES, YES, DEFAULT, DEFAULT>,- <, RZ28, , < >, YES, YES, DEFAULT, DEFAULT>,- <, RZ29, , < >, YES, YES, DEFAULT, DEFAULT>,- <, RZ26L, RZL26, < >, YES, YES, DEFAULT, DEFAULT>> SCSI_DEV_TYPES <- <, RZ26B, RZB26, < >, YES, YES, DEFAULT, DEFAULT>,- <, RZ27B, RZB27, < >, YES, YES, DEFAULT, DEFAULT>,- <, RZ28B, RZB28, < >, YES, YES, DEFAULT, DEFAULT>,- <, RZ29B, RZB29, < >, YES, YES, DEFAULT, DEFAULT>,- <, RZ73B, RZB73, < >, YES, YES, DEFAULT, DEFAULT>,- <, RZ74B, RZB74, < >, YES, YES, DEFAULT, DEFAULT>,- <, RZ75B, RZB75, < >, YES, YES, DEFAULT, DEFAULT>,- <, RZ27L, RZL27, < >, YES, YES, DEFAULT, DEFAULT>,- <, RWZ21, , < >, YES, YES, DEFAULT, DEFAULT>,- <, HSZ20, , < >, YES, YES, DEFAULT, DEFAULT>,- <, HSZ40, , < >, YES, YES, DEFAULT, DEFAULT>,- <, HSZ15, , < >, YES, YES, DEFAULT, DEFAULT>,- <, RZ26M, RZM26, < >, YES, YES, DEFAULT, DEFAULT>> SCSI_DEV_TYPES <- <, RW504, , < >, YES, YES, DEFAULT, DEFAULT>,- <, RW510, , < >, YES, YES, DEFAULT, DEFAULT>,- <, RW514, , < >, YES, YES, DEFAULT, DEFAULT>,- <, RW516, , < >, YES, YES, DEFAULT, DEFAULT>,- <, RWZ51, , < >, YES, YES, DEFAULT, DEFAULT>,- <, RWZ52, , < >, YES, YES, DEFAULT, DEFAULT>,- <, RWZ53, , < >, YES, YES, DEFAULT, DEFAULT>,- <, RWZ54, , < >, YES, YES, DEFAULT, DEFAULT>,- <, RWZ31, , < >, YES, YES, DEFAULT, DEFAULT>,- <, EZ31, , < >, YES, YES, DEFAULT, DEFAULT>,- <, EZ32, , < >, YES, YES, DEFAULT, DEFAULT>,- <, EZ33, , < >, YES, YES, DEFAULT, DEFAULT>,- <, EZ34, , < >, YES, YES, DEFAULT, DEFAULT>> SCSI_DEV_TYPES <- <, EZ35, , < >, YES, YES, DEFAULT, DEFAULT>,- <, EZ31L, EZL31, < >, YES, YES, DEFAULT, DEFAULT>,- <, EZ32L, EZL32, < >, YES, YES, DEFAULT, DEFAULT>,- <, EZ33L, EZL33, < >, YES, YES, DEFAULT, DEFAULT>,- <, RZ28L, RZL28, < >, YES, YES, DEFAULT, DEFAULT>,- <, EZ56R, EZR56, < >, YES, YES, DEFAULT, DEFAULT>> .ENDC ; DEFINED V60_BUILD .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 = REZERO_UNIT,- CMD_BYTES = <1, 0, 0, 0, 0, 0> SCSI_CMD - NAME = REQUEST_SENSE,- CMD_BYTES = <3, 0, 0, 0, SENSE_ALLOC, 0>,- DMA_LEN = SENSE_ALLOC,- DMA_DIR = READ SCSI_CMD - NAME = FORMAT_UNIT,- CMD_BYTES = <4, 0, 0, 0, 0, 0>,- DMA_LEN = 512,- DMA_DIR = WRITE SCSI_CMD - NAME = REASSIGN_BLOCKS,- CMD_BYTES = <7, 0, 0, 0, 0, 0>,- DMA_LEN = 8,- DMA_DIR = WRITE SCSI_CMD - NAME = READ,- CMD_BYTES = <8, 0, 0, 0, 0, 0>,- DMA_LEN = -1 SCSI_CMD - NAME = WRITE,- CMD_BYTES = <10, 0, 0, 0, 0, 0>,- DMA_LEN = -1 SCSI_CMD - NAME = EXTND_READ,- CMD_BYTES = <<^X28>, 0, 0, 0, 0, 0, 0, 0, 0, 0>,- DMA_LEN = -1 SCSI_CMD - NAME = EXTND_WRITE,- CMD_BYTES = <<^X2A>, 0, 0, 0, 0, 0, 0, 0, 0, 0>,- DMA_LEN = -1 SCSI_CMD - NAME = SEEK,- CMD_BYTES = <11, 0, 0, 0, 0, 0> SCSI_CMD - NAME = INQUIRY,- CMD_BYTES = <18 , 0, 0, 0, 36, 0>,- DMA_LEN = 36,- DMA_DIR = READ SCSI_CMD - NAME = MODE_SELECT,- CMD_BYTES = <21 , <^X10>, 0, 0, 20, 0>,- DMA_LEN = 150,- DMA_DIR = WRITE SCSI_CMD - NAME = MODE_SELECT_10,- CMD_BYTES = <85, <^X10>, 0,0,0,0,0, 0,20, 0>,- DMA_LEN = 255,- DMA_DIR = WRITE SCSI_CMD - NAME = RESERVE,- CMD_BYTES = <22, 0, 0, 0, 0, 0> SCSI_CMD - NAME = RELEASE,- CMD_BYTES = <23, 0, 0, 0, 0, 0> SCSI_CMD - NAME = COPY,- CMD_BYTES = <24, 0, 0, 0, 0, 0> SCSI_CMD - NAME = MODE_SENSE,- CMD_BYTES = <26, 0, <^X3F>, 0, 255, 0>,- DMA_LEN = 255,- DMA_DIR = READ SCSI_CMD - NAME = MODE_SENSE_10,- CMD_BYTES = <90, 0, <^X3F>, 0,0,0,0,0,<^XFF>,0>,- DMA_LEN = 255,- DMA_DIR = READ SCSI_CMD - NAME = START_UNIT,- CMD_BYTES = <27, 1, 0, 0, 1, 0> SCSI_CMD - NAME = STOP_UNIT,- CMD_BYTES = <27, 1, 0, 0, 0, 0> SCSI_CMD - NAME = RECEIVE_DIAG,- CMD_BYTES = <28, 0, 0, 0, 0, 0>,- DMA_LEN = 512,- DMA_DIR = READ SCSI_CMD - NAME = SEND_DIAG,- CMD_BYTES = <29, 0, 0, 0, 0, 0>,- DMA_LEN = 512,- DMA_DIR = WRITE SCSI_CMD - NAME = MEDIA_REMOVAL,- CMD_BYTES = <30, 0, 0, 0, 0, 0> SCSI_CMD - NAME = READ_CAPACITY,- CMD_BYTES = <37, 0, 0, 0, 0, 0, 0, 0, 0, 0>,- DMA_LEN = 8,- DMA_DIR = READ SCSI_CMD - NAME = READ_LONG,- CMD_BYTES = <<^X3E>, 0, 0, 0, 0, 0, 0, 0, 0, 0>,- DMA_LEN = READ_LONG_DATA_LEN,- DMA_DIR = READ SCSI_CMD - NAME = WRITE_LONG,- CMD_BYTES = <<^X3F>, 0, 0, 0, 0, 0, 0, 0, 0, 0>,- DMA_LEN = WRITE_LONG_DATA_LEN,- DMA_DIR = WRITE ;+ ; Beginning of CD-ROM specific SCSI Audio commands ;- SCSI_CMD - NAME = PAUSE,- CMD_BYTES = <75, 0, 0, 0, 0, 0, 0, 0, 0, 0> SCSI_CMD - NAME = RESUME,- CMD_BYTES = <75, 0, 0, 0, 0, 0, 0, 0, 1, 0> SCSI_CMD - NAME = PLAY_AUDIO10,- CMD_BYTES = <69, 0, 0, 0, 0, 0, 0, 0, 0, 0> SCSI_CMD - NAME = PLAY_TRACK,- CMD_BYTES = <72, 0, 0, 0, 3, 1, 0, 11, 1, 0> SCSI_CMD - NAME = REMOVAL,- CMD_BYTES = <30, 0, 0, 0, 0, 0, 0> SCSI_CMD - NAME = PLAY_AUDIO_MSF,- CMD_BYTES = <71, 0, 0, 0, 0, 0, 0, 0, 0, 0> SCSI_CMD - NAME = CD_MODE_SENSE,- CMD_BYTES = <26, 0, 0, 0, 0, 0>,- DMA_LEN = -1,- DMA_DIR = READ SCSI_CMD - NAME = CD_MODE_SELECT,- CMD_BYTES = <21 , <^X10>, 0, 0, 0, 0>,- DMA_LEN = -1,- DMA_DIR = WRITE SCSI_CMD - NAME = CD_READ_SUB,- CMD_BYTES = <66, 0, 64, 1, 0, 0, 0, 0, 48, 0>,- DMA_LEN = 48,- DMA_DIR = READ SCSI_CMD - NAME = CD_READ_TOC,- CMD_BYTES = <67, 0, 0, 0, 0, 0, 0, 3, 22, 0>,- DMA_LEN = 804,- DMA_DIR = READ .SBTTL Dummy CDB Definition for Error Logging DUMMY_MODE_SENSE_CDB: ; For error logging purposes. .LONG ^XFF ; Dummy status .LONG 6 ; Command length .BYTE ^X1A,0,0,0,100,0 ; Command bytes .BLKB 2 ; Pad for alignment .SBTTL Mode Descriptor Definitions ; Descriptors for mode page fields, used by MODE SENSE operations. PREF = MODE_DESC$M_PREFERRED REQ = MODE_DESC$M_REQUIRED MISS = MODE_DESC$M_IFMISSING MISM = MODE_DESC$M_IFMISMATCH DPTR = MODE_DESC$M_DESPTR APTR = MODE_DESC$M_ACTPTR LAST = MODE_DESC$M_LAST MBOFF = MODE_DESC$C_MEDTYP_BOFF DBOFF = MODE_DESC$C_DEVPAR_BOFF DNBOFF= MODE_DESC$C_DENS_BOFF BBOFF = MODE_DESC$C_BLKLEN_BOFF .MACRO MD_DSC PAGE,NAME,BOFF,BIT_OFFSET,BIT_SIZE,DESIRED,FLAGS 'PAGE'_'NAME'_DESC = . - DESCRIPTOR_BASE ; Offset from base .LONG 'BOFF' ; Byte offset in page .LONG 'BIT_OFFSET' ; Bit offset within byte .LONG 'BIT_SIZE' ; Size of field in bits .LONG 'FLAGS' ; Flag bits .QUAD 'DESIRED' ; Desired value .BLKL 2 ; Actual value .BLKL 1 ; Difference reason .BLKL 1 ; Reserved bits .ENDM MD_DSC ; When performing a Mode Sense command, it isn't always possible to determine the ; reason for a command failure. If we are issuing a 10-byte Mode Sense command and ; it fails, it may be because the page itself is not implemented by the device, or ; it may be because the device just doesn't support the 10-byte Mode Sense command. ; In an attempt to recover from an ambiguous failure, DO_MODE_PAGE backs off from ; 10-byte and tries a 6-byte command, which is a mandatory command; if that fails, ; rather than make any broad assumption about the nature of the failure based on ; the one page it's trying to process, it simply leaves the TENBYTE bit clear. ; ; We're assuming that this type of ambiguity is only a concern for certain pages. ; If the device is a disk, we can't tell if it's a floppy or not, so we don't know ; if it supports the flexible disk parameters page or not; also, the SCSI standard ; does not explicitly prohibit floppy disks from implementing the rigid disk geometry ; page. ; ; The first 10-byte Mode Sense command issued is to a required page, if ; it completes successfully then all subsequent mode sense commands should use ; the 10-byte format. But any time that the 10-byte command fails the 6-byte command ; is tried before reporting any failure. Therefore, after the first mode sense ; command the state of the SCDRP TENBYTE bit is saved in the UCB and it is refreshed ; to the SCDRP from the UCB before each subsequent mode sense command. ; Propagate the state of the UCB's TENBYTE bit to the SCDRP .MACRO LOAD_TENBYTE ucb=R3,scdrp=R5,?L1,?L2 BBC #UCB$V_TENBYTE,UCB$L_DK_FLAGS(ucb),L1 BISL #SCDRP$M_TENBYTE,SCDRP$L_SCSI_FLAGS(scdrp) BRW L2 L1: BBCC #SCDRP$V_TENBYTE,SCDRP$L_SCSI_FLAGS(scdrp),L2 L2: .ENDM LOAD_TENBYTE ; Propagate the state of the SCDRP's TENBYTE bit to the UCB .MACRO STORE_TENBYTE ucb=R3,scdrp=R5,?L1,?L2 BBC #SCDRP$V_TENBYTE,SCDRP$L_SCSI_FLAGS(scdrp),L1 BISL2 #UCB$M_TENBYTE,UCB$L_DK_FLAGS(ucb) BRW L2 L1: BBCC #UCB$V_TENBYTE,UCB$L_DK_FLAGS(ucb),L2 L2: .ENDM STORE_TENBYTE .ALIGN LONG DESCRIPTOR_BASE = . ;------------------------------------------------------------------------------- ERR_PAGE_DESC = . - DESCRIPTOR_BASE ; Page name Bitname Boff Bit Size Val Flags ; --------- ------- ---- --- ---- --- ----- MD_DSC ERR, BLKLEN, BBOFF, 0, 24, <^X200>, SIZEOF_ERR = . - ;------------------------------------------------------------------------------- SCSI2_TCQ_ERR_PAGE_DESC = . - DESCRIPTOR_BASE ; SCSI2 Tagged Queuing devices, R/W err rec page ; Same values required for SCSI clusters ; Page name Bitname Boff Bit Size Val Flags ; --------- ------- ---- --- ---- --- ----- MD_DSC SCSI2_TCQ_ERR, AWRE, 2, 7, 1, 1, MD_DSC SCSI2_TCQ_ERR, ARRE, 2, 6, 1, 1, MD_DSC SCSI2_TCQ_ERR, TB, 2, 5, 1, 1, MD_DSC SCSI2_TCQ_ERR, RC, 2, 4, 1, 0, MD_DSC SCSI2_TCQ_ERR, PER, 2, 2, 1, 1, MD_DSC SCSI2_TCQ_ERR, DTE, 2, 1, 1, 1, MD_DSC SCSI2_TCQ_ERR, MEDTYP, MBOFF, 0, 8, 0, 0 MD_DSC SCSI2_TCQ_ERR, DEVSPC, DBOFF, 0, 8, 0, SIZEOF_SCSI2_TCQ = . - ;------------------------------------------------------------------------------- SCSI2_NOTCQ_ERR_PAGE_DESC = . - DESCRIPTOR_BASE ; SCSI2 non-Tagged Queuing devices, R/W err rec pg ; If possible, should look like SCSI1 ; Page name Bitname Boff Bit Size Val Flags ; --------- ------- ---- --- ---- --- ----- MD_DSC SCSI2_NOTCQ_ERR, AWRE, 2, 7, 1, 0, MD_DSC SCSI2_NOTCQ_ERR, ARRE, 2, 6, 1, 0, MD_DSC SCSI2_NOTCQ_ERR, TB, 2, 5, 1, 1, MD_DSC SCSI2_NOTCQ_ERR, RC, 2, 4, 1, 0, MD_DSC SCSI2_NOTCQ_ERR, PER, 2, 2, 1, 1, MD_DSC SCSI2_NOTCQ_ERR, DTE, 2, 1, 1, 1, MD_DSC SCSI2_NOTCQ_ERR,MEDTYP, MBOFF, 0, 8, 0, 0 MD_DSC SCSI2_NOTCQ_ERR,DEVSPC, DBOFF, 0, 8, 0, SIZEOF_SCSI2_NOTCQ = . - ;------------------------------------------------------------------------------- SCSI1_ERR_PAGE_DESC = . - DESCRIPTOR_BASE ; SCSI1 devices, R/W error recovery page ; Page name Bitname Boff Bit Size Val Flags ; --------- ------- ---- --- ---- --- ----- MD_DSC SCSI1_ERR, AWRE, 2, 7, 1, 0, MD_DSC SCSI1_ERR, ARRE, 2, 6, 1, 0, MD_DSC SCSI1_ERR, TB, 2, 5, 1, 1, MD_DSC SCSI1_ERR, RC, 2, 4, 1, 0, MD_DSC SCSI1_ERR, PER, 2, 2, 1, 1, MD_DSC SCSI1_ERR, DTE, 2, 1, 1, 1, MD_DSC SCSI1_ERR, MEDTYP, MBOFF, 0, 8, 0, 0 MD_DSC SCSI1_ERR, DEVSPC, DBOFF, 0, 8, 0, SIZEOF_SCSI1 = . - ;------------------------------------------------------------------------------- NON512_ERR_PAGE_DESC = . - DESCRIPTOR_BASE ; Devices with blk size not = 512, R/W error recovery page ; Don't care about AWRE/ARRE for these devices ; Page name Bitname Boff Bit Size Val Flags ; --------- ------- ---- --- ---- --- ----- MD_DSC NON512_ERR, TB, 2, 5, 1, 1, MD_DSC NON512_ERR, RC, 2, 4, 1, 0, MD_DSC NON512_ERR, PER, 2, 2, 1, 1, MD_DSC NON512_ERR, DTE, 2, 1, 1, 1, MD_DSC NON512_ERR, MEDTYP, MBOFF, 0, 8, 0, 0 MD_DSC NON512_ERR, DEVSPC, DBOFF, 0, 8, 0, SIZEOF_NON512 = . - ;------------------------------------------------------------------------------- CACHING_PAGE_DESC = . - DESCRIPTOR_BASE ; Page name Bitname Boff Bit Size Val Flags ; --------- ------- ---- --- ---- --- ----- MD_DSC CACHE, WCE, 2, 2, 1, 0, SIZEOF_CACHING = . - .IF DEFINED RZ74_CACHE ;------------------------------------------------------------------------------- NOCACHING_PAGE_DESC = . - DESCRIPTOR_BASE ; Page name Bitname Boff Bit Size Val Flags ; --------- ------- ---- --- ---- --- ----- MD_DSC CACHE, RCD, 2, 0, 1, 1, MD_DSC CACHE, WCE, 2, 2, 1, 0, SIZEOF_NOCACHING = . - .ENDC ;------------------------------------------------------------------------------- CONTROL_MODE_PAGE_DESC = . - DESCRIPTOR_BASE ; Page name Bitname Boff Bit Size Val Flags ; --------- ------- ---- --- ---- --- ----- MD_DSC CTL, QERR, 3, 1, 1, 0, MD_DSC CTL, EECA, 4, 7, 1, 0, ;------------------------------------------------------------------------------- RIGID_DISK_PAGE_DESC = . - DESCRIPTOR_BASE ; Page name Bitname Boff Bit Size Val Flags ; --------- ------------------- ---- --- ---- --- ----- MD_DSC RGD, TRACKS_PER_CYLINDER, 5, 0, 8, 0, MD_DSC RGD, NUM_CYLINDERS, 3, 0, 16, 0, SIZEOF_RIGID_DISK = . - ;------------------------------------------------------------------------------- FORMAT_DEVICE_PAGE_DESC = . - DESCRIPTOR_BASE ; Page name Bitname Boff Bit Size Val Flags ; --------- ------------------- ---- --- ---- --- ----- MD_DSC FMT, SECTORS_PER_TRACK, 11, 0, 8, 0, MD_DSC FMT, SECTOR_SIZE_MSB, 12, 0, 8, 0, MD_DSC FMT, SECTOR_SIZE_LSB, 13, 0, 8, 0, SIZEOF_FORMAT = . - ;------------------------------------------------------------------------------- FORMAT_DEVICE_SEL_PAGE_DESC = . - DESCRIPTOR_BASE ; Page name Bitname Boff Bit Size Val Flags ; --------- ------------------- ---- --- ---- --- ----- MD_DSC FMT, SEL_SECTORS_PER_TRACK, 10, 0, 16, 0, ;------------------------------------------------------------------------------- FLEX_DISK_PAGE_DESC = . - DESCRIPTOR_BASE ; Page name Bitname Boff Bit Size Val Flags ; --------- ------------------- ---- --- ---- --- ----- MD_DSC FLX, TRANSFER_RATE, 2, 0, 16, 0, MD_DSC FLX, TRACKS_PER_CYLINDER, 4, 0, 8, 0, MD_DSC FLX, NUM_CYLINDERS, 8, 0, 16, 0, MD_DSC FLX, PAG_LEN, 1, 0, 8, 0, MD_DSC FLX, SPC, 22, 0, 4, 0, SIZEOF_FLEX_DISK = . - ;------------------------------------------------------------------------------- FLEX_DISK_SELECT_PAGE_DESC = . - DESCRIPTOR_BASE ; Page name Bitname Boff Bit Size Val Flags ; --------- ------------------- ---- --- ---- --- ----- MD_DSC FLX, SEL_TRANSFER_RATE, 2, 0, 16, 0, MD_DSC FLX, NUM_HEADS, 4, 0, 8, 0, MD_DSC FLX, SECTORS_PER_TRACK, 5, 0, 8, 0, MD_DSC FLX, SEL_NUM_CYLINDERS, 8, 0, 16, 0, MD_DSC FLX, START_SECTOR_NUM, 21, 6, 1, 0, MD_DSC FLX, SEL_SPC, 22, 0, 4, 0, ;------------------------------------------------------------------------------- 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, RECOVERR 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, DRVERR 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 + Miscellaneous routines .SBTTL + .SBTTL CHECK_QDEPTH - Check histogram data for qdepth change ;+ ; CHECK_QDEPTH ; ; This routine is called from startio when the UCB$W_XLEN_HIST_CYCLE ; counter reaches zero. It goes through the UCB$L_XLEN_HIST transfer ; length histogram to see where the concentration of I/O lengths ; are, and selects a new max qdepth based on this data. ; ; INPUTS: ; ; R3 - address of the UCB ; R5 - address of an SCDRP ; ; OUTPUTS: ; ; R0,R1 - destroyed ; all other registers preserved ;- QDEPTH_TABLE: .BYTE 16 ; 0 - 3 .BYTE 14 ; 4 - 7 .BYTE 12 ; 8 - 11 .BYTE 11 ; 12 - 15 .BYTE 10 ; 16 - 19 .BYTE 9 ; 20 - 23 .BYTE 8 ; 24 - 27 .BYTE 7 ; 28 - 31 .BYTE 6 ; 32 - 35 .BYTE 5 ; 36 - 39 .BYTE 5 ; 40 - 43 .BYTE 5 ; 44 - 47 .BYTE 5 ; 48 - 51 .BYTE 5 ; 52 - 55 .BYTE 5 ; 56 - 59 .BYTE 5 ; 60 - 63 .BYTE 5 ; 64 - 67 .BYTE 4 ; 68 - 71 .BYTE 4 ; 72 - 75 .BYTE 4 ; 76 - 79 .BYTE 4 ; 80 - 83 .BYTE 4 ; 84 - 87 .BYTE 4 ; 88 - 91 .BYTE 4 ; 92 - 95 .BYTE 3 ; 96 - 99 .BYTE 3 ; 100 - 103 .BYTE 3 ; 104 - 107 .BYTE 3 ; 108 - 111 .BYTE 3 ; 112 - 115 .BYTE 3 ; 116 - 119 .BYTE 3 ; 120 - 123 .BYTE 3 ; 124 - 127+ CHECK_QDEPTH: PUSHL R6 PUSHL R2 MOVL UCB$L_XLEN_HIST(R3),R6 ; Address of histogram table CLRL R1 ; Max hist entry count MOVL #31,R0 ; Index into hist table MOVL R0,R2 ; Save "max" index 10$: CMPL (R6)[R0],R1 ; Check entry against max seen BLEQ 20$ ; br if less than max MOVL (R6)[R0],R1 ; New max MOVL R0,R2 ; Store new index 20$: CLRL (R6)[R0] ; Restart histogram SOBGEQ R0,10$ ; Check all cells MOVAB QDEPTH_TABLE,R6 ; Address of qdepth vs xlen table MOVZBL (R6)[R2],R0 ; select new optimal depth CMPL R0,UCB$L_QDEPTH(R3) ; If same, skip SET_CONN_CHAR for speed BEQL 30$ MOVL R0,UCB$L_QDEPTH(R3) ; Set new qdepth INCL UCB$L_QDEPTH_TURNS(R3) ; Count adjustments BSBW SET_CONN_CHAR ; Tell port 30$: POPL R2 POPL R6 RSB ; This table is used to convert between the vendor specific (RX26) ; MEDIA_TYPE field in the MODE_SENSE header and the IRP$L_MEDIA value ; MODE_SENSE_MEDIA_TYPE_TABLE: .BYTE ^X00 ; default or unknown .BYTE ^X80 ; DD (double density) .BYTE ^X81 ; HD (high density) .BYTE ^X82 ; ED (extended density) MODE_SENSE_MEDIA_TYPE_MAX =<.-MODE_SENSE_MEDIA_TYPE_TABLE-1> ; Index into this table with irp$l_media-1 ; Note: Tables are biased to exclude (SCSI$FMT$R_PAGE_CODE, SCSI$FMT$B_PAGE_LENGTH) ; MODE_SELECT_FORMAT_TABLE: .LONG MODE_SELECT_FORMAT_RX26_DD-MODE_SELECT_FORMAT_TABLE-SCSI$FMT$W_TRACKS .LONG MODE_SELECT_FORMAT_RX26_HD-MODE_SELECT_FORMAT_TABLE-SCSI$FMT$W_TRACKS .LONG MODE_SELECT_FORMAT_RX26_ED-MODE_SELECT_FORMAT_TABLE-SCSI$FMT$W_TRACKS ; ; Index into this table with irp$l_media-1 ; MODE_SELECT_FLEXIBLE_TABLE: .LONG MODE_SELECT_FLEXIBLE_RX26_DD-MODE_SELECT_FLEXIBLE_TABLE-SCSI$FLX$W_TRANSFER_RATE .LONG MODE_SELECT_FLEXIBLE_RX26_HD-MODE_SELECT_FLEXIBLE_TABLE-SCSI$FLX$W_TRANSFER_RATE .LONG MODE_SELECT_FLEXIBLE_RX26_ED-MODE_SELECT_FLEXIBLE_TABLE-SCSI$FLX$W_TRANSFER_RATE MODE_SELECT_FORMAT_RX26_HD: MODE_SELECT_FORMAT_RX23_18: .BYTE 0,0 ; tracks per zone .BYTE 0,0 ; alt sectors per zone .BYTE 0,0 ; alt tracks per zone .BYTE 0,0 ; alt tracks per logical unit .BYTE 0,18 ; sectors per track ffh,ffh .BYTE 2,0 ; bytes per sector .BYTE 0,1 ; interleave .BYTE 0,0 ; track skew .BYTE 0,0 ; cylinder skew .BYTE ^xA0 ; SSEC, HSEC, RMB, SURF, reserved_4 10h .BYTE 0,0,0 ; reserved MODE_SELECT_FLEXIBLE_RX26_HD: MODE_SELECT_FLEXIBLE_RX23_18: .BYTE ^x01,^xF4 ; transfer rate = 500 KHz ffh,ffh .BYTE 2 ; number of heads = 2 ffh .BYTE 18 ; sectors per track = 18 ffh .BYTE 2,0 ; data bytes per sector = 512 .BYTE 0,80 ; number of cylinders = 80 ffh,ffh .BYTE 0,80 ; starting write precomp = 80 .BYTE 0,80 ; starting reduced write current = 80 .BYTE 0,0 ; step rate = default ffh,ffh .BYTE 0 ; step pulse width = default .BYTE 0,0 ; head settle delay = default ffh,ffh .BYTE 5 ; motor on delay = 1/2 Second ffh .BYTE 30 ; motor off delay = 3 Seconds ffh .BYTE ^x40 ; trdy=0, ssn=1, mo=0 60h .BYTE 0 ; extra step pulses per cylinder = 0 1fh .BYTE 0 ; write compensation level unsupported .BYTE 0 ; head load delay unsupported .BYTE 0 ; head unload delay unsupported .BYTE ^x23 ; pin 34=2, pin 2=3 .BYTE ^x00 ; pin 4=0 .BYTE 0,0,0,0 ; reserved MODE_SELECT_FORMAT_RX26_DD: MODE_SELECT_FORMAT_RX23_9: .BYTE 0,0 ; tracks per zone .BYTE 0,0 ; alt sectors per zone .BYTE 0,0 ; alt tracks per zone .BYTE 0,0 ; alt tracks per logical unit .BYTE 0,9 ; sectors per track ffh,ffh .BYTE 2,0 ; bytes per sector .BYTE 0,1 ; interleave .BYTE 0,0 ; track skew .BYTE 0,0 ; cylinder skew .BYTE ^xA0 ; SSEC, HSEC, RMB, SURF, reserved_4 10h .BYTE 0,0,0 ; reserved MODE_SELECT_FLEXIBLE_RX26_DD: MODE_SELECT_FLEXIBLE_RX23_9: .BYTE ^x00,^xFA ; transfer rate = 250 KHz ffh,ffh .BYTE 2 ; number of heads = 2 ffh .BYTE 9 ; sectors per track = 9 ffh .BYTE 2,0 ; data bytes per sector = 512 .BYTE 0,80 ; number of cylinders = 80 ffh,ffh .BYTE 0,80 ; starting write precomp = 80 .BYTE 0,80 ; starting reduced write current = 80 .BYTE 0,0 ; step rate = default ffh,ffh .BYTE 0 ; step pulse width = default .BYTE 0,0 ; head settle delay = default ffh,ffh .BYTE 5 ; motor on delay = 1/2 Second ffh .BYTE 30 ; motor off delay = 3 Seconds ffh .BYTE ^x40 ; trdy=0, ssn=1, mo=0 60h .BYTE 0 ; extra step pulses per cylinder = 0 1fh .BYTE 0 ; write compensation level unsupported .BYTE 0 ; head load delay unsupported .BYTE 0 ; head unload delay unsupported .BYTE ^x23 ; pin 34=2, pin 2=3 .BYTE ^x00 ; pin 4=0 .BYTE 0,0,0,0 ; reserved MODE_SELECT_FORMAT_RX33_15: .BYTE 0,0 ; tracks per zone .BYTE 0,0 ; alt sectors per zone .BYTE 0,0 ; alt tracks per zone .BYTE 0,0 ; alt tracks per logical unit .BYTE 0,15 ; sectors per track .BYTE 2,0 ; bytes per sector .BYTE 0,1 ; interleave .BYTE 0,0 ; track skew .BYTE 0,0 ; cylinder skew .BYTE ^xA0 ; SSEC, HSEC, RMB, SURF, reserved_4 .BYTE 0,0,0 ; reserved MODE_SELECT_FLEXIBLE_RX33_15: .BYTE ^x01,^xF4 ; transfer rate = 500 KHz .BYTE 2 ; number of heads = 2 .BYTE 15 ; sectors per track = 15 .BYTE 2,0 ; data bytes per sector = 512 .BYTE 0,80 ; number of cylinders = 80 .BYTE 0,80 ; starting write precomp = 80 .BYTE 0,80 ; starting reduced write current = 80 .BYTE 0,0 ; step rate = default .BYTE 0 ; step pulse width = default .BYTE 0,0 ; head settle delay = default .BYTE 5 ; motor on delay = 1/2 Second .BYTE 30 ; motor off delay = 3 Seconds .BYTE ^x40 ; trdy=0, ssn=1, mo=0 .BYTE 0 ; extra step pulses per cylinder = 0 .BYTE 0 ; write compensation level unsupported .BYTE 0 ; head load delay unsupported .BYTE 0 ; head unload delay unsupported .BYTE ^x23 ; pin 34=2, pin 2=3 .BYTE ^x00 ; pin 4=0 .BYTE 0,0,0,0 ; reserved MODE_SELECT_FORMAT_RX26_ED: .BYTE 0,0 ; tracks per zone .BYTE 0,0 ; alt sectors per zone .BYTE 0,0 ; alt tracks per zone .BYTE 0,0 ; alt tracks per logical unit .BYTE 0,36 ; sectors per track ffh,ffh .BYTE 2,0 ; bytes per sector .BYTE 0,1 ; interleave .BYTE 0,0 ; track skew .BYTE 0,0 ; cylinder skew .BYTE ^xA0 ; SSEC, HSEC, RMB, SURF, reserved_4 10h .BYTE 0,0,0 ; reserved MODE_SELECT_FLEXIBLE_RX26_ED: .BYTE ^x03,^xE8 ; transfer rate = 1000 KHz ffh,ffh .BYTE 2 ; number of heads = 2 ffh .BYTE 36 ; sectors per track = 36 ffh .BYTE 2,0 ; data bytes per sector = 512 .BYTE 0,80 ; number of cylinders = 80 ffh,ffh .BYTE 0,80 ; starting write precomp = 80 .BYTE 0,80 ; starting reduced write current = 80 .BYTE 0,0 ; step rate = default ffh,ffh .BYTE 0 ; step pulse width = default .BYTE 0,0 ; head settle delay = default ffh,ffh .BYTE 5 ; motor on delay = 1/2 Second ffh .BYTE 30 ; motor off delay = 3 Seconds ffh .BYTE ^x40 ; trdy=0, ssn=1, mo=0 60h .BYTE 0 ; extra step pulses per cylinder = 0 1fh .BYTE 0 ; write compensation level unsupported .BYTE 0 ; head load delay unsupported .BYTE 0 ; head unload delay unsupported .BYTE ^x23 ; pin 34=2, pin 2=3 .BYTE ^x00 ; pin 4=0 .BYTE 0,0,0,0 ; reserved .SBTTL + .SBTTL + DRIVER ENTRY POINTS .SBTTL + .SBTTL DK_CTRL_INIT - Controller initialization routine ;+ ; DK_CTRL_INIT ; ; This routine is called to perform controller-specific initialization and ; is called by the operating system in three places: ; ; - at system startup ; - during driver loading and reloading ; - during recovery from a power failure ; ; This routine is a NOP for driver reloading and power failure recovery. ; For system startup and driver loading it allocates a CDDB. ; ; 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: ; ; Registers preserved before forking. ; R0,R1,R2,R4,R5 destroyed. ;- DK_CTRL_INIT: PUSHQ R4 ; Save CSR & IDB MOVB #SPL$C_SCS,- ; Initialize device spin lock index. CRB$B_FLCK(R8) ; TSTL CRB$L_AUXSTRUC(R8) ; Check for CDDB already present. BEQL 10$ ; Branch if not 5$: POPQ R4 ; Restore registers RSB ; Otherwise, return to caller 10$: ; ; Create fork thread to finish controller init. ; MOVL R6,R4 ; Restore DDB MOVL R8,R5 ; Fork with CRB PUSHAB 5$ ; FORK ; ; ; Get pool for CDDB. ; 20$: MOVZWL #CDDB$K_LENGTH,R1 ; Size of CDDB JSB G^EXE$ALONONPAGED ; Allocate some pool BLBS R0,30$ ; Branch if successful BUG_CHECK INCONSTATE,FATAL ; Otherwise, bugcheck 30$: ; ; Clear pool. ; PUSHR #^M ; Save registers. MOVC5 #0, (SP), #0, R1, (R2) ; Zero entire block. POPR #^M ; Restore saved registers. ; ; Initialize necessary CDDB fields. ; MOVW R1,CDDB$W_SIZE(R2) ; Size ASSUME CDDB$B_SUBTYPE EQ CDDB$B_TYPE+1 MOVW #>,- ; CDDB$B_TYPE(R2) ; MOVL G^CLU$GL_ALLOCLS,- ; Allocation class CDDB$L_ALLOCLS(R2) ; MOVL R5,CDDB$L_CRB(R2) ; CRB address MOVL R4,CDDB$L_DDB(R2) ; DDC address MOVL R2,CRB$L_AUXSTRUC(R5) ; Save CDDB address in CRB. MOVL #SS$_NORMAL,R0 ; Set success status RSB ; Return to caller .SBTTL DK_UNIT_INIT - Unit initialization routine ;+ ; DK_UNIT_INIT ; ; This routine performs unit-specific initialization and is called for each ; disk found on the SCSI bus. A connection to the port driver is established, ; which lasts for the life of the system. All traffic to this SCSI device is ; directed over this connection. ; ; The first time through this routine a set of SPTEs are allocated which are ; used to double map the user's buffer during datacheck operations. The cells ; used to record the address of the SPTEs and the system virtual address ; mapped by them live in the driver image. This is possible since use of the ; SPTEs is synchronized with the fork lock. ; ; An inquiry command is sent to the target to determine the device type. ; In additon, several sanity checks are made on the inquiry data to determine ; if the device is valid. If so, the unit is placed online and any I/O queued ; to the device during initialization is started. ; ; INPUTS: ; ; R5 - UCB address ; ; OUTPUTS: ; ; R0-R3 - Destroyed ; All other registers preserved ;- DATACHECK_SPTE: ; SVA of SPTEs used to double map user .LONG 0 ; buffer during datacheck operation DATACHECK_SVA: ; SVA mapped by this set of SPTEs .LONG 0 ; DK_UNIT_INIT: ; Initialize unit BRB 1$ ; Skip call to INI$BRK jsb g^ini$brk ; *** Debug *** ; Fork twice for now to allow the port driver's unit init routine to execute ; before ours. 1$: BBC #UCB$V_POWER,- ; Branch if we're not here due to a UCB$W_STS(R5),2$ ; powerfail RSB ; Otherwise, exit immediately 2$: FORK ; Fork to drop IPL to SYNCH FORK ; Fork to drop IPL to SYNCH .IF DEFINED DEBUG BSBW SETUP_TRACE ; Set up trace buffer .ENDC 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_DK_FLAGS(R5) ; synchronous operation ASSUME UCB$V_DD_BYPASS LT 16 BICW2 #UCB$M_DD_BYPASS,- ; Ensure no writes to DD diskettes UCB$L_DK_FLAGS(R5) ; on an RX23S drive ; (provides space for an in-place ; patch) CLRB UCB$B_SEEK_DIR(R5) ; Initialize seek direction flag CLRL UCB$L_SAVE_CONN_CHAR(R5); Initialize location to hold ; connection characteristics used ; in IO_DIAGNOSE calls. BISW #UCB$M_ONLINE!- ; Set unit online and busy (in case this UCB$M_BSY,- ; is the system disk, we don't want to UCB$W_STS(R5) ; prevent I/O from being queued) 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. ; ; Setup UCB CDDB field. ; MOVL UCB$L_CRB(R5),R0 ; Get CRB address MOVL CRB$L_AUXSTRUC(R0),- ; Get CDDB address out of the CRB. UCB$L_CDDB(R5) ; ; ; Check system disk. ; CMPL G^SYS$AR_BOOTUCB,R5 ; Is this the system disk? BNEQ 5$ ; Branch if not MOVL #^X7FFFFFFF,- ; Set up a dummy MAXBLOCK value to UCB$L_MAXBLOCK(R5) ; support booting ; ; Check for host based shadowed system disk. If so, fork and wait until ; the controller init routine of SHDRIVER completes its execution. ; BLBC G^EXE$GL_SHADOW_SYS_DISK,5$ ; LBC not booting hbs. Continue. 3$: CMPL G^SYS$AR_BOOTUCB,- ; Is the system disk pointer updated? G^EXE$GL_SYSUCB ; BNEQ 5$ ; NEQ means updated. Continue. FORK_WAIT ; Fork and wait. (R3-R5 preserved.) BRB 3$ ; Try again. 5$: .IF DEFINED DEBUG MOVL TR$TRACE_BUFFER_ADDR,- ; Save address of trace buffer UCB$L_TRACE_BUF(R5) 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 ; Restore 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 CLRL UCB$L_QUEUED_IO_COUNT(R5) ; clear count .IF DEFINED COLLECT_PERF_DATA CLRL UCB$L_READ_COUNT(R5) ; Clear perf counters CLRL UCB$L_WRITE_COUNT(R5) ; Clear perf counters CLRL UCB$L_OTHER_COUNT(R5) ; Clear perf counters TSTL UCB$L_READ_XLEN_HIST(R5) ; Do we already have pool? BLSS 1000$ ; Br if yes MOVL #XLEN_HIST_SIZE,R1 ; Size of required pool BSBW ALLOC_POOL ; Allocate a histogram buffer MOVL R2,UCB$L_READ_XLEN_HIST(R5) ; Save histogram address MOVL #XLEN_HIST_SIZE,R1 ; Size of required pool BSBW ALLOC_POOL ; Allocate a histogram buffer MOVL R2,UCB$L_WRITE_XLEN_HIST(R5) ; Save histogram address MOVL #XLEN_HIST_SIZE,R1 ; Size of required pool BSBW ALLOC_POOL ; Allocate a histogram buffer MOVL R2,UCB$L_XLEN_HIST(R5) ; Save histogram address 1000$: PUSHR #^M ; Save regs PUSHL R5 ; Save UCB seperatly MOVL #XLEN_HIST_SIZE,R1 ; length MOVL UCB$L_READ_XLEN_HIST(R5),R2 ; Address MOVC5 #0,.,#0,R1,(R2) ; Clear read counters MOVL (SP),R5 ; Get copy of UCB MOVL #XLEN_HIST_SIZE,R1 ; length MOVL UCB$L_WRITE_XLEN_HIST(R5),R2 ; Address MOVC5 #0,.,#0,R1,(R2) ; clear write counters MOVL (SP),R5 ; Get copy of UCB MOVL #XLEN_HIST_SIZE,R1 ; length MOVL UCB$L_XLEN_HIST(R5),R2 ; Address MOVC5 #0,.,#0,R1,(R2) ; clear write counters POPL R5 ; Restore UCB POPR #^M ; Save regs MOVW #XLEN_HIST_TURNOVER,- ; Set I/O counter for qdepth check UCB$W_XLEN_HIST_CYCLE(R5) MOVAB UCB$L_QDEPTH(R5),- ; Point to start of our perf data area UCB$L_2P_LINK(R5) ; for DKPERF.C .ENDC ; DEFINED COLLECT_PERF_DATA ; All SCSI DISK 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 BBC #SPDT$V_CMDQ,R3,11$ ; See if port supports Command Queing BISL #UCB$M_PORT_CMDQ,- ; Set port_cmdq bit UCB$L_DK_FLAGS(R5) ; in UCB flags 11$: ASHL #-24,R3,R3 ; Get MAXBCNT divisor BNEQ 12$ ; Branch if divisor supplied by port MOVZBL #1,R3 ; Otherwise, use a divisor of 1 12$: DIVL R3,R1 ; Get MAXBCNT recommended by port BICL #511,R1 ; Make MAXBCNT an integral block count 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 16$: TSTL DATACHECK_SPTE ; Datacheck SPTEs already allocated BNEQ 18$ ; Branch if so ASHL #-9,UCB$L_MAXBCNT(R5),R2; Convert to max page count INCL R2 ; Account for non-page-alligned buffers JSB G^LDR$ALLOC_PT ; Allocate SPTEs to double map user buf BLBC R0,20$ ; Branch if failure MOVL R1,DATACHECK_SPTE ; Save SVA of the first SPTE SUBL2 G^MMG$GL_SPTBASE,R1 ; Get offset into page table ASHL #,R1,R1 ; Calculate system virtual address BISL3 #VA$M_SYSTEM,R1,- ; mapped by this set of SPTEs DATACHECK_SVA ; 18$: BISL2 #UCB$M_TENBYTE,- ; Assume 10-byte mode sense support UCB$L_DK_FLAGS(R5) 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 DK_STARTIO - Driver QIO entry point ;+ ; DK_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: l.o. word contains h.o. ; word of number of bytes transferred ; R4 - Destroyed ; All other registers preserved ;- DK_STARTIO: IF_NOT_CMDQ 1$, UCB=R5 ; Br if device does not support queueing BICW #UCB$M_BSY,- ; Clear the BSY bit to allow QIO to UCB$W_STS(R5) ; send multiple I/O's to the port 1$: .IF DEFINED DEBUG BSBW TRACE_QIO ; Trace the current I/O request .ENDC INCL UCB$L_QUEUED_IO_COUNT(R5) ; Bump count .IF DEFINED COLLECT_PERF_DATA INCL UCB$L_OTHER_COUNT(R5) ; Assume not a read/write for now .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 ; ; 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$ BBCW #UCB$V_VALID,- ; Logical or virtual I/O function UCB$W_STS(R3),- ; Branch if volume is invalid VOLUME_INVALID2 10$: 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 ,- ; ^X02 ,- ; ^X03 ,- ; ^X08 ,-; ^X0A ,- ; ^X0B ,- ; ^X0C ,- ; ^X0D ,- ; ^X0E ,- ; ^X11 , - ; ^X15 ,- ; ^X1D ,- ; ^X21 ,- ; ^X30 > ; ^X37 ; Bogus I/O function code if we fall through. Set illegal function code ; status and complete the I/O. IO_BOGUS: IO_SEEK: IO_RECAL: IO_WRITEHEAD: IO_READHEAD: IO_READLBLK: MOVZBL #SS$_ILLIOFUNC,R0 ; Specify the error type BRB COMPLETE_IO IO_NOP: DISABLE_ERRLOG ; Temporarily disable errorlogging BSBW TEST_UNIT_READY ; Issue an ORDERED TUR to sync queues REENABLE_ERRLOG ; Reenable errorlogging MOVZWL #SS$_NORMAL,R0 ; Set success status BRB COMPLETE_IO ; Complete the I/O ;+ ; The volume was not software enabled, however audio functions don't ; require that the volume (disk) be software enabled. If the I/O function is ; an audio function then goto the audio fdt code. ;- VOLUME_INVALID2: 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 CMPB #IO$_AUDIO,R1 ; Is this an AUDIO function? BNEQ VOLUME_INVALID ; No, return error BRW IO_AUDIO ; Yes, do audio otherwise return error VOLUME_INVALID: CLRL R1 ; Zero R1. MOVZWL #SS$_VOLINV,R0 ; It's not a valid volume ; BRB COMPLETE_IO ; Fall through to complete the I/O COMPLETE_IO: DECL UCB$L_QUEUED_IO_COUNT(R3) ; Adjust I/O count BGEQ 1$ BUG_CHECK INCONSTATE,FATAL ; Somethings wrong if this is neg! 1$: MOVL SCDRP$L_IRP(R5),- ; Set up UCB$L_IRP for the actual UCB$L_IRP(R3) ; IRP that's completing! BSBW DEALLOC_SCDRP ; Deallocate the SCDRP MOVL R3,R5 ; Copy UCB address .IF DEFINED DEBUG BSBW TRACE_QIO_STAT ; Save the final QIO status in trace buf BLBC R0,2$ ; Branch on error BRW 10$ ; Branch on success status 2$: NOP ; Instruction to trap on QIO with bad status .ENDC CLRL R1 ; Clear transfer byte count CMPL R0,#SS$_RECOVERR ; Recoverable error status? BEQL 100$ ; Branch if so, CMPL R0,#SS$_TIMEOUT ; Timeout error status? BEQL 110$ ; Branch if so, CMPL R0,#SS$_DRVERR ; Drive error status? BEQL 120$ ; Branch if so, CMPL R0,#SS$_CTRLERR ; Controller error status? BEQL 120$ ; Branch if so, CMPL R0,#SS$_DEVOFFLINE ; Offline error status? BEQL 120$ ; Branch if so, BRW 10$ ; Fall through ; In order to allow the lower layers to distinguish between recoverable and ; non-recoverable errors, a new status code was invented called SS$_RECOVERR. ; This status code should never make it up to this level unless the target ; returns a recoverable error sense key for a SCSI command from which we don't ; expect it. To prevent us from possibly returning a bogus status code for the ; QIO, translate this status to one that VMS knows about, namely, SS$_PARITY. 100$: MOVL #SS$_PARITY,R0 ; Change to a valid error status BRW 10$ ; Timeouts may have occurred that are merely the result of a bus reset (as ; frequently happens in multi-host configurations). Rather than failing ; this on the spot, convert the error to SS$_MEDOFL to allow retries. 110$: MOVL #SS$_MEDOFL,R0 ; Change to MEDOFL BRW 10$ ; For various other errors, force the driver into mount verify exactly ; once in order to give it a second chance. Set IRP$V_FORCEMV to indicate ; we are forcing mount verification. 120$: MOVL UCB$L_IRP(R5),R2 ; Get IRP address BBSS #IRP$V_FORCEMV,- IRP$W_STS2(R2),10$ ; Already MV'd once before, ; so no more allowed. MOVL #SS$_MEDOFL,R0 ; Convert to MEDOFL in order ; to force mount verify ; If the device does NOT support command queing and there are two or more ; IRPs in the pending queue, attempt to reorder the queue such that the IRP ; for the block closest to the current head position in the direction of the ; current head motion is executed first. 10$: IF_CMDQ 20$,UCB=R5 ; Skip reordering for CMDQ devices MOVAL UCB$L_IOQFL(R5),R2 ; Get address of I/O pending queue in U CMPL @(R2),R2 ; Fewer than two pending IPRs? BEQL 20$ ; Branch if so, no need to reorder PUSHQ R0 ; Save I/O status BSBW ATTEMPT_REORDER ; Attempt to reorder read/write request POPQ R0 ; Restore I/O status 20$: REQCOM ; Complete the I/O .SBTTL DK_CANCEL - Cancel I/O routine ;+ ; DK_CANCEL ; ; This routine cancels an I/O in progress. Since the timeouts for disks are ; relatively short, no device-dependent action is taken. Instead, it relies ; on the port driver's relatively short timeout to terminate any I/O in ; progress. ; ; Inputs: ; ; R2 - channel index number ; R3 - address of the current IRP (I/O request packet) ; R4 - address of the PCB (process control block) for the ; process canceling I/O ; R5 - address of the UCB (unit control block) ; R8 - cancel reason code, one of: ; CAN$C_CANCEL if called through $CANCEL or ; $DALLOC system service ; CAN$C_DASSGN if called through $DASSGN system ; service ; Outputs: ; ; R0-R3 - Destroyed ; All other registers preserved ;- DK_CANCEL: ; Cancel an I/O operation TSTL R3 ; IRP address of 0? (this can happen ; during UNIT INIT in bringing the ; device online) BEQL 10$ ; Branch if not ;TGG0003 CMPL IRP$L_PID(R3),- ; Test PID(IRP) equal GK_PID(UCB) UCB$L_GK_PID(R5) ; BNEQ 7$ ; Branch if no match CLRL UCB$L_GK_PID(R5) ; Allow other processes to use DIAGNOSE BBCC #UCB$V_GK_CHK_COND,- ; If prev I/O got CHECK_COND UCB$L_DK_FLAGS(R5),7$ ; clear CHECK_COND flag 7$: JSB G^IOC$CANCELIO ; Set cancel bit if appropriate. BBC #UCB$V_CANCEL,- ; If the cancel bit is not set, UCB$W_STS(R5),10$ ; just return. 10$: RSB ; Return .SBTTL DK_REG_DUMP - Device register dump routine ;+ ; DK_REG_DUMP ; ; This routine dumps device-specific infromation into an errorlog packet. ; The format of this information is as follows: ; ; +-----------------------+ ; | Longword count | 4 bytes ; +-----------------------+ ; | 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 ;- DK_REG_DUMP: PUSHAL (R0)+ ; Save start of data area MOVB #DK_ERROR_REVISION,(R0)+; Save revision level MOVL UCB$L_HW_REV(R5),(R0)+ ; Save hardware revision level MOVB 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 SUBL #3,R0 ; Back up pointer MOVL (R2)+,R3 ; Get number of SCSI command bytes MOVB R3,(R0)+ ; Save command length 10$: MOVB (R2)+,(R0)+ ; Save a command byte SOBGTR R3,10$ ; Continue for entire SCSI command 20$: MOVB #-1,(R0)+ ; Assume no valid status byte MOVL SCDRP$L_STS_PTR(R1),R2 ; Get address of status byte BEQL 25$ ; Branch if no status byte MOVB (R2),-1(R0) ; Save SCSI status byte 25$: CLRB (R0)+ ; Assume no additonal data 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 BRB 37$ ; Special entry point for reassign block error. The additional data length ; is contained in SCDRP$L_BCNT rather than in SCDRP$L_TRANS_CNT. 35$: MOVL SCDRP$L_SVA_USER(R1),R2 ; Get address of additional data MOVZBL SCDRP$L_BCNT(R1),R1 ; Get length of additional data 37$: MOVB R1,-1(R0) ; Save additional data length 40$: MOVB (R2)+,(R0)+ ; Save a byte of extended sense data SOBGTR R1,40$ ; Repeat for all extended sense data ; BRB 50$ ; Use common exit 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 DK_DSE - Data Security Erase FDT Routine ;+ ; DK_DSE ; ; This is the FDT routine for the Data Security Erase operation. ; The byte count (P2) is stored in IRP$L_BCNT. The starting logical ; block (P3) is stored in IRP$L_MEDIA. Control is transfered to ; EXE$QIODRVPKT, thus queueing the I/O request to the driver's start ; I/O routine. ; ; INPUTS: ; ; R3 - IRP address ; P2(AP) - Byte count ; P3(AP) - Starting logical block ; ; OUTPUTS: ; ; IRP$L_BCNT(R3) - Byte count ; IRP$L_MEDIA(R3) - Starting logical block ;- DK_DSE: MOVL P2(AP), IRP$L_BCNT(R3) ; Setup erase byte count MOVL P3(AP), IRP$L_MEDIA(R3) ; Setup erase starting LBN JMP G^EXE$QIODRVPKT ; Send request to STARTIO .SBTTL DK_DIAGNOSE - FDT preprocessing for Special pass-through function ;+ ; DK_DIAGNOSE ; ; This routine performs FDT preprocessing including: ; ; - Validating access to the descriptor buffer ; - Validating access to, and locking, the read/write buffer ; - Copying the SCSI command to a buffer in non-paged pool ; ; 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 DK_DIAGNOSE: BBCS #UCB$V_GK_ACTIVE,- ; Check for another process using UCB$L_DK_FLAGS(R5),1$ ; the DIAGNOSE entry BRW 35$ ; Lock out access 1$: BBC #UCB$V_GK_CHK_COND,- ; If check cond pending, make sure UCB$L_DK_FLAGS(R5),2$ ; this is the same proc that caused it CMPL IRP$L_PID(R3),- ; Is this the PID that caused chk cond? UCB$L_GK_PID(R5) ; BEQL 2$ ; OK, let it through BICL #UCB$M_GK_ACTIVE,- ; Clear lock out UCB$L_DK_FLAGS(R5) BRW 35$ ; nope, fail the I/O 2$: MOVL IRP$L_PID(R3),- ; Remember the process using DIAGNOSE UCB$L_GK_PID(R5) ; .IF DEFINED V60_BUILD MOVL UCB$L_ORB(R5),R9 ; Get address of object rights block CLRQ -(SP) ; terminator and retlen for privilege a MOVL ORB$L_NAME_POINTER(R9),-(SP) ; Bufadr for device name MOVZWL ORB$W_NAME_LENGTH(R9),R9 ; save size of device name ADDL3 #,R9,-(SP) ; itmcod and bufsiz of item ent AUDIT_S_ITMLST = 16 ; Size of the itemlist is 16 bytes MOVL SP,R9 ; save pointer to itemlist $IFPRIV DIAGNOSE, - ; Branch if process has DIAGNOSE priv 10$, - MSG=DIAGNOSE_7, - ITMLST=R9, - PRESERVE=NO MOVL #SS$_NOPRIV,R0 ; Set no privilege status ADDL #AUDIT_S_ITMLST,SP ; Restore stack BICL #UCB$M_GK_ACTIVE,- ; Clear lock out UCB$L_DK_FLAGS(R5) BRW 50$ ; Branch to abort the I/O .ENDC ; DEFINED V60_BUILD ; First, check that we have read access to the user's descriptor. 10$: 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? BGTRUW 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 35$: MOVL #SS$_CHANINTLK,R0 ; channel usage interlocked BRB 55$ 40$: MOVL #SS$_BADPARAM,R0 ; Set bad parameter status 50$: CLRL UCB$L_GK_PID(R5) ; Clear DIAGNOSE user PID BICL #UCB$M_GK_ACTIVE,- ; Clear lock out UCB$L_DK_FLAGS(R5) 55$: 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$: CLRL UCB$L_GK_PID(R5) ; Clear DIAGNOSE user PID BICL #UCB$M_GK_ACTIVE,- ; Clear lock out UCB$L_DK_FLAGS(R5) 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 DK_SHAD_WCHECK - Check write to shadow mbr for priv ;++ ; ; DK_SHAD_RWCHECK - Check read/write to shadow mbr for privilege ; ; Functional Description: ; ; Allow only processes with SYS privilege to perform WRITES to ; Host Based Shadowing shadow set members. ; ; Inputs: ; ; R3 IRP address ; R5 UCB address (member) ; ; Implicit inputs: None. ; ; Outputs:None. ; ; Implicit outputs: None. ; ; Condition codes: ; ; SS$_ILLIOFUNC - I/O directed to shadow set member by a process ; that doesn't have sys priv. ;-- DK_SHAD_WCHECK: BBS #DEV$V_SHD,- ; If this device is a shadow UCB$L_DEVCHAR2(R5),10$ ; set member, check RSB ; Else, continue FDT processing 10$: MOVL IRP$L_ARB(R3),R0 ; Get ARB address BEQL 99$ ; If ARB absent, exit ASSUME PRV$V_SYSPRV LT 32 BBC #PRV$V_SYSPRV,ARB$Q_PRIV(R0),99$; No SYSPRV, illegal RSB ; Continue FDT processing 99$: MOVZBL #SS$_ILLIOFUNC,R0 ; Set error status JMP G^EXE$FINISHIOC ; Complete I/O request .SBTTL DK_CRESHAD - CRESHAD FDT routine .SBTTL DK_REMSHAD - REMSHAD FDT routine ;++ ; ; DK_CRESHAD - CRESHAD FDT routine ; DK_REMSHAD - REMSHAD FDT routine ; ; Functional Description: ; ; Dispatch CRESHAD and REMSHAD requests to shadowing driver. ; ; Inputs: ; ; R3 IRP address ; R5 UCB address (member) ; ; Implicit inputs: Dispatch vector filled in. ; ; Outputs:None. ; ; Implicit outputs: None. ; ; Condition codes: ; ; SS$_ILLIOFUNC - Dispatch vector not set up. ;-- DK_CRESHAD: ; ----> IO$_CRESHAD DK_REMSHAD: ; ----> IO$_REMSHAD MOVL G^EXE$GL_HBS_PTR,R0 ; Shadow Dispatcher BGEQ 10$ ; Illegal if not filled in JMP (R0) ; Jump to dispatcher 10$: MOVZBL #SS$_ILLIOFUNC,R0 ; Set error status JMP G^EXE$FINISHIOC ; Complete I/O request .SBTTL DK_AUDIO - FDT preprocessing for CD-ROM Audio functions ;+ ; DK_AUDIO ; ; This routine performs FDT preprocessing for SCSI AUDIO commands. ; The application passes the address of a control structure called the Audio ; Control Block (AUCB) in P1 and the size of the AUCB in P2. In this FDT ; routine the AUCB and any other buffers presented by the users will be locked ; down and double mapped into S0 and P0 space. The AUCB is a control structure ; which passes audio command specific parameters as well as describes users ; buffers that may be used by the driver to return control or CD state ; information (TOC) to the application. ; ; In order to accommodate multiple user buffers being used in a single I/O, an ; IRP extension (IRPE) is allocated and linked to the original IRP. The ; original IRP contains the mapping information about the AUCB and the IRPE is ; used to contain the mapping information for the optional destination and ; sense buffers. ; ; 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: ; ;- ;+ ; Definition of the offsets into the Audio Control Block (AUCB) for DKDRIVER. ;- CD_FUNCTION_CODE = 0 ; Audio function code CD_AUCB_VERSION = 2 ; Version number of AUCB structure CD_ARG1 = 4 ; Command specific parameter CD_ARG2 = 8 ; Command specific parameter CD_ARG3 = 12 ; Command specific parameter CD_RSVD1 = 16 ; Reserved for future use (MBZ) CD_DEST_BUF_ADDR = 20 ; Buffer returned to user CD_DEST_BUF_CNT = 24 ; Size of buffer returned to user CD_DEST_BUF_TRANS_CNT = 28 ; Actual number of bytes received CD_COMMAND_STATUS = 32 ; VMS O.S. Return status CD_SCSI_STATUS = 36 ; SCSI command status (optional) CD_SENSE_ADDR = 40 ; Sense data buffer CD_SENSE_CNT = 44 ; Sense data buffer size CD_SENSE_TRANS_CNT = 48 ; Sense data transfer count CD_RESVD2 = 52 ; Reserved for future used (MBZ) CD_AUCB_SIZE = 52 ; Size in bytes of AUCB CD_AUCB_CUR_VERSION = 1 ; Current Version number of AUCB ;+ ; Definition of SCSI Audio command function codes. These are used by application ; to select individual audio functions supported by the driver. ;- PAUSE = 0 ; Pause RESUME = 1 ; Resume PREVENT_REMOVAL = 2 ; Prevent/Allow ALLOW_REMOVAL = 3 ; Prevent/Allow PLAY_AUDIO = 4 ; Play Audio LBA PLAY_AUDIO_MSF = 5 ; Play MSF PLAY_AUDIO_TRACK= 6 ; Play Audio Track PLAY_TRACK_REL = 7 ; Play Track Relative READ_HEADER = 8 ; Reserved GET_STATUS = 9 ; Read Subchannel-Q GET_TOC = 10 ; Read TOC SET_VOLUME = 11 ; Mode Select GET_VOLUME = 12 ; Mode Sense SET_DEFAULT = 13 ; Reserved GET_DEFAULT = 14 ; Reserved ;+ ; Definitions for offsets into the SCSI CCB as used by DKDRIVER. ;- PAGE_CODE = 6 ; Page Code Field A_LEN = 8 ; Allocation Length Field DK_AUDIO: .ENABLE LSB ;+ ; Check that the disk is not mounted and thus not subject to mount verification. ; A file structured mounted disk will not be an audio disk in general and ; we don't want to have any random audio errors result in mount verify ; trying to recycle IRPs after the audio code cleans up buffers. ; ; Disallow shadow set members too...we don't want anyone to be able to ; put the shadowset into MVfy somehow with this. ; ;- BBS #DEV$V_SSM,UCB$L_DEVCHAR2(R5),3$ ;U A; Shadow set member? If so reject BBC #DEV$V_MNT,UCB$L_DEVCHAR(R5),4$ ;U A; Are we mounted? BBS #DEV$V_FOR,UCB$L_DEVCHAR(R5),4$ ;U A; If so if it's /for, all ok ; If mounted and NOT foreign, we still must be sure the disk in fact has ; mount verify enabled. ISO disks will not, and one can mount with /nomount_ver ; also. In those cases we won't see the IRP again. (To be safe, there is a test ; at IO_AUDIO: also that will detect failed and cleaned-up functions and ; junk the I/O there too...but we don't want random users able to put disks ; into mount verify just by issuing audio functions.) ; MOVL UCB$L_VCB(R5),R0 ;U A; Got a VCB? BGEQ 4$ ;U A; if .ge.0 not valid anyhow ; Treat no VCB as equivalent to not mounted files-11. Lord knows what will be ; there but it won't get MV. ; ; Note: Checking the VCB may fail if Spiralog doesn't emulate it right!!! ; However, here goes! If mount verify is not enabled, allow the I/O. ; Otherwise junk it right here. ; BBC #VCB$V_MOUNTVER,VCB$B_STATUS2(R0),4$ ;U A; Is MV enabled? 3$: MOVL #SS$_ILLIOFUNC,R0 ;U A; Say illegal func JMP G^EXE$ABORTIO ; Abort the I/O with status in R0 4$: ;+ ; The AUCB is the Audio Control Block which is constructed by the application and ; passed to the driver during the QIO call. The AP points at the arguments ; to the QIO. The first parameter is the address of the AUCB and the second ; parameter is the size of AUCB. ; ; Now check to see that we have read access to the AUCB before attempting to ; access this structure and then verify the version number. The version is ; checked to be sure that it matchs the current code. In the future it may ; become necessary to allow backward compatibility with earlier versions ; of the audio interface within this driver. ;- 10$: BISW #IRP$M_PHYSIO,- ; All audio functions are physical IRP$W_STS(R3) CLRL IRP$L_WIND(R3) ; Clear this since we use if in startio CLRL IRP$L_EXTEND(R3) ; Clear for starters.. MOVQ P1(AP),R0 ; Get AUCB information BSBW AUDIO_MAP_PAGE ; Locks and Maps AUCB. BLBS R0,15$ ; Success, then continue BRW AUDIO_EXIT_FDT ; Exit FDT routine on error R0 = error 15$: CMPL #CD_AUCB_SIZE,P2(AP) ; There must be an AUCB BGTRW 40$ ; The AUCB must be at least this big. ;+ ; Now that we have locked down and prepared the AUCB for startio, determine ; whether or not there is a optional sense buffer. If there is a buffer then ; allocate an IRP extension to save the state of this buffer. If there is ; no sense buffer required, simply continue to process this request. ;- MOVL P1(AP),R0 ; Get address of AUCB MOVL CD_SENSE_ADDR(R0),R0 ; Get Address of Sense buffer BEQL 30$ ; No sense buffer continue BSBW SETUP_SENSE_BUFFER ; There's a sense buffer set it up. BLBC R0,AUDIO_EXIT_FDT ; Exit FDT routine on error R0 = error ;+ ; All audio I/O must go through the processing above, however some audio ; functions require additional processing before these requests can be queued ; to the startio routine. ; ; Now that we have double mapped the AUCB and Sense Buffer determine whether there are ; any additional buffers that need to be mapped. ;- 30$: MOVL P1(AP),R9 ; Get user AUCB address DISPATCH CD_FUNCTION_CODE(R9),TYPE=B,<- ; Dispatch based on function ,- > ;+ ; For all other requests, simply issue this IRP to the startio routine. ;- JMP G^EXE$QIODRVPKT ;QUEUE DRIVER PACKET BRW 40$ ;+ ; The following two AUDIO operations return data to an optional user buffer. ;- FDT_READ_TOC: FDT_READ_SUB: ;+ ; Check the source/destination buffer for access and protection. ;- TSTL CD_DEST_BUF_ADDR(R9) ; Test address of the dest buffer BEQLW 40$ ; TSTL CD_DEST_BUF_CNT(R9) ; Test size of destination buffer. BEQLW 40$ ; If there is a buffer, must <> 0 ;+ ; Since there is another buffer that needs to be processed, if an IRPE ; hasn't already been allocated, allocate it now. ;- TSTL IRP$L_EXTEND(R3) ; Test for IRP Extension BEQL NO_IRP ; No, IRPE already. MOVL IRP$L_EXTEND(R3),R3 ; Get IRPE address BRB GOT_IRPE ; Yes, already have an IRPE. NO_IRP: BSBW ALLOC_IRPE ; Allocate an IRP extension BLBC R0,AUDIO_EXIT_FDT ; Exit if no IRPE GOT_IRPE: ASSUME CD_DEST_BUF_ADDR+4 EQ CD_DEST_BUF_CNT MOVQ CD_DEST_BUF_ADDR(R9),R0 ; Get address and size of dest buffer.N MOVL R1,IRP$L_BCNT(R3) ; Copy Byte count to IRPE BICW3 #^C,R0,- ; Get BOFF for Destination buffer. IRP$W_BOFF(R3) BSBW AUDIO_MAP_PAGE ; Locks and Maps Destination Buffer. BLBC R0,AUDIO_EXIT_FDT ; Exit if fail. MOVL IRP$L_SEQNUM(R3),R3 ; Restore Original IRP Address JMP G^EXE$QIODRVPKT ; Queue the packet to the driver ;+ ; Error Paths for DK_AUDIO FDT routine. ;- 40$: PUSHL R2 MOVL R3,R2 ; Get IRP Address in R2 BSBW AUDIO_EXIT_FREE ; Free Allocated resources POPL R2 MOVZBL #SS$_BADPARAM,R0 ; Set bad parameter status 50$: JMP G^EXE$ABORTIO ; Abort the I/O with status in R0 ;+ ; Error exit path from audio fdt routines. ;- AUDIO_EXIT_FDT: PUSHR #^M MOVL R3,R2 ; Get IRP Address in R2 BSBW AUDIO_EXIT_FREE ; Free Allocated resources POPR #^M MOVZWL R0,CD_COMMAND_STATUS(AP); Set bad status is AUCB. BRB 50$ .DISABLE LSB ; DK_AUDIO .SBTTL + .SBTTL + QIO INTERFACE ROUTINES .SBTTL + .SBTTL IO_PACKACK - Pack acknowledge function ;+ ; IO_PACKACK ; ; This routine executes a PACKACK request by sending and inquiry, ensuring the ; device is spun up, reading its geometry information, and, if necessary, doing ; device specific setup such as setting the default block size and error ; recovery parameters. ; ; INPUTS: ; ; R3 - UCB address ; R4 - PDT address ; R5 - SCDRP address ; ; OUTPUTS: ; ; R0 - Status ; R1,R2 - Destroyed ; All other registers preserved ;- IO_PACKACK: BISW #UCB$M_BSY,- ; Set the BSY bit to prevent QIO from UCB$W_STS(R3) ; sending multiple I/O's to the port ; While PACKACK is in progress ; Clear write protect status, since the floppy diskette can be changed. BICL #UCB$M_HWL,- ; Assume medium is writeable UCB$L_DK_FLAGS(R3) ; until proven otherwise BSBW WAIT_UNIT_READY ; Wait for the device to spin up BLBC R0,30$ ; Branch on error BSBW INQUIRY ; Execute an INQUIRY command BLBC R0,30$ ; Branch on error BSBW PROCESS_MODE_INFO ; Do MODE SENSE/SELECT processing BLBS R0,16$ ; Branch on success BBSC #UCB$V_TENBYTE,- ; Branch if we can back off and UCB$L_DK_FLAGS(R3),14$ ; try a must-be-supported command BRW 30$ 14$: BSBW PROCESS_MODE_INFO ; Do MODE SENSE/SELECT processing BLBC R0,30$ ; Branch on error 16$: BSBW READ_CAPACITY ; Execute READ_CAPACITY command BLBC R0,30$ ; Branch on error BSBW VALIDATE_GEOMETRY ; Check disk geometry BLBC R0,30$ ; Branch on error BSBW SET_CONN_CHAR ; Set up the connection characteristics BLBC R0,30$ ; Branch on error BISL #,- ; Set volume valid UCB$L_STS(R3) ; BISB #UCB$M_FIRST_ATTN_SEEN,-; Indicate that the first unit attention UCB$L_DK_FLAGS(R3) ; has been seen (any future ones will ; be logged as errors) BBC #UCB$V_CDROM,- ; Is this device a CD-ROM drive? UCB$L_DK_FLAGS(R3),15$ ; If no, skip Subchannel data fetch CMPB #DT$_RRD40S,- ; Is device RRD40 ? If so, skip UCB$B_DEVTYPE(R3) ; subchannel data fetch BEQL 15$ BSBW READ_CD_MCN ; else read the subchannel data and 15$: BBSS #UCB$V_HBS_CHECK,- ; Branch if check for host-based UCB$L_DK_FLAGS(R3),20$ ; shadowing has already been made BSBW CHECK_HBS ; Check for host-based shadowing support 20$: BRW COMPLETE_IO ; Complete this I/O function 30$: MOVL SCDRP$L_IRP(R5),R2 ; Get IRP address TSTL IRP$L_PID(R2) ; Check for interanl IRP BLSS 20$ ; Skip local valid cleanup if internal BBCC #UCB$V_LCL_VALID, - ; Clear local valid bit and UCB$L_STS(R3), 20$ ; branch if its already clear. DECB UCB$B_ONLCNT(R3) ; Else, decrement the online count. BRB 20$ .SBTTL IO_READPBLK - Read a set of blocks from the SCSI drive .SBTTL IO_WRITEPBLK - Write a set of blocks to the SCSI drive ;+ ; IO_READPBLK ; IO_WRITEBLK ; ; This routine reads/writes a set of contiguous blocks from/to the SCSI ; disk. For those transfers that have not already been segmented by the ; exec to chunks LEQ MAXBCNT, this routine performs the segmentation. ; ; Recoverable and non-recoverable errors are dealt with by this routine. ; In summary, the algorithm is to retry the original operation several times. ; If the error persists, and is anything but a non-recoverable read, then ; a block reassignement is performed. The original data is then written to ; the reasigned block as reassignment is not guaranteed to move the data. ; Retries are performed if necessary at each stage. ; ; Because there is no forced error bit in SCSI, blocks with non-recoverable ; errors on reads can not be reassigned. (Otherwise, undetected corruption ; would result. It's preferable to return SS$_PARITY status each time the ; failing block is read.). ; ; If the DATACHECK qualifier is specified on the QIO request, the IO_DATACHECK ; routine is invoked to read the set of blocks just read/written. No block ; reassignment is performed by IO_DATACHECK. ; ; INPUTS: ; ; R2 - IPR address ; R3 - UCB address ; R4 - PDT address ; R5 - SCDRP address ; ; OUTPUTS: ; ; R0 - Status ; R1,R2 - Destroyed ; All other registers preserved ;- WRITE_LOCKED: MOVL #SS$_WRITLCK,R0 ; Set write-locked status BRW COMPLETE_IO ; Complete QIO with error status INVALID_BLOCK_NUMBER: MOVL #SS$_IVADDR,R0 ; Otherwise, set invalid address status BRW COMPLETE_IO ; Complete QIO with error status IO_WRITEPBLK: .IF DEFINED COLLECT_PERF_DATA INCL UCB$L_WRITE_COUNT(R3) ; Count writes ; ; Figure out which XLEN bucket this transfer fits in ; ADDL3 #511,IRP$L_BCNT(R2),R0 ; Round up byte count to block multiple ASHL #-9,R0,R0 ; and convert to block count ASHL #-XLEN_HIST_BUCKET_SHIFT,R0,R0 ; Convert to bucket index CMPL #XLEN_HIST_BUCKETS,R0 ; Boundary checks... BGTR 1000$ ; Looks OK MOVL #XLEN_HIST_BUCKETS-1,R0 ; Fix index - >64K in same 64K bucket 1000$: INCL @UCB$L_WRITE_XLEN_HIST(R3)[R0] ; Bump count INCL @UCB$L_XLEN_HIST(R3)[R0] ; Bump total count .ENDC BBS #UCB$V_HWL,- ; Branch if device is write-locked, UCB$L_DK_FLAGS(R3),- ; not possible to write device WRITE_LOCKED ; BICW #IRP$M_FUNC,- ; Clear the FUNC bit to indicate this SCDRP$W_STS(R5) ; is a write function BBC #IO$V_MSCPMODIFS,- ; Branch if this is NOT a function to IRP$W_FUNC(R2),- ; force a bad block IO_RW_COMMON ; Branch to common code BRW FORCE_ERROR ; Otherwise, go force an error IO_READPBLK: .IF DEFINED COLLECT_PERF_DATA INCL UCB$L_READ_COUNT(R3) ; Count reads ; ; Figure out which XLEN bucket this transfer fits in ; ADDL3 #511,IRP$L_BCNT(R2),R0 ; Round up byte count to block multiple ASHL #-9,R0,R0 ; and convert to block count ASHL #-XLEN_HIST_BUCKET_SHIFT,R0,R0 ; Convert to bucket index CMPL #XLEN_HIST_BUCKETS,R0 ; Boundary checks... BGTR 1500$ ; Looks OK MOVL #XLEN_HIST_BUCKETS-1,R0 ; Fix index - >64K in same 64K bucket 1500$: INCL @UCB$L_READ_XLEN_HIST(R3)[R0] ; Bump count INCL @UCB$L_XLEN_HIST(R3)[R0] ; Bump total count .ENDC BISW #IRP$M_FUNC,- ; Set the FUNC but to indicate this SCDRP$W_STS(R5) ; is a read function IO_RW_COMMON: .IF DEFINED COLLECT_PERF_DATA DECL UCB$L_OTHER_COUNT(R3) ; Fix count since this is a R/W ; ; See if we need to recalculate the qdepth ; DECW UCB$W_XLEN_HIST_CYCLE(R3) ; count down I/O's till recheck BNEQ 100$ BSBW CHECK_QDEPTH ; See if we need to set new depth ; based on xlen histogram info MOVW #XLEN_HIST_TURNOVER,- ; Reset I/O counter for qdepth check UCB$W_XLEN_HIST_CYCLE(R3) 100$: .ENDC BBS #UCB$V_VALID,- ; Branch if volume is valid. UCB$W_STS(R3),5$ BRW VOLUME_INVALID ; We need to have done a PACKACK ; before we can do a read or write 5$: ADDL3 #511,IRP$L_BCNT(R2),R0 ; Round up byte count to block multiple ASHL #-9,R0,R0 ; and convert to block count ADDL IRP$L_MEDIA(R2),R0 ; Calculate highest block # accessed CMPL R0,UCB$L_MAXBLOCK(R3) ; Valid block number? BGTRW INVALID_BLOCK_NUMBER ; Branch if not 10$: CLRL SCDRP$L_ABCNT(R5) ; Initialize accumulated byte count MOVW IRP$W_FUNC(R2),- ; Copy function code and modifiers, SCDRP$W_FUNC(R5) ; MEDIA, SVAPTE, and BOFF fields MOVL IRP$L_MEDIA(R2),- ; from the IRP to the SCDRP SCDRP$L_MEDIA(R5) ; MOVL IRP$L_SVAPTE(R2),- ; SCDRP$L_SVAPTE(R5) ; MOVW IRP$W_BOFF(R2),- ; SCDRP$W_BOFF(R5) ; IO_RW_LOOP: SUBL3 SCDRP$L_ABCNT(R5),- ; Attempt to transfer all remaining IRP$L_BCNT(R2),- ; bytes in user's buffer SCDRP$L_BCNT(R5) ; CMPL SCDRP$L_BCNT(R5),- ; Transfer length greater than maximum UCB$L_MAXBCNT(R3) ; supported? BLEQU 10$ ; Branch if not MOVL UCB$L_MAXBCNT(R3),- ; Otherwise, transfer must be segmented SCDRP$L_BCNT(R5) ; into pieces of MAXBCNT length 10$: BSBW READ_WRITE ; Send a SCSI read or write command MOVL SCDRP$L_IRP(R5),R2 ; Restore IRP address BLBCW R0,IO_RW_ERR ; Branch on error IO_RW_ACCUM: SUBL3 SCDRP$L_PAD_BCNT(R5),- ; Get actual number of bytes transferred SCDRP$L_TRANS_CNT(R5),R0; less any possible padding CMPL R0,SCDRP$L_BCNT(R5) ; Compare with requested transfer count BNEQ IO_RW_MISMATCH ; Branch if mismatch occurred ADDL R0,SCDRP$L_ABCNT(R5) ; Accumulate this piece of the transfer CMPL SCDRP$L_ABCNT(R5),- ; Is transfer complete? IRP$L_BCNT(R2) BLSSU IO_RW_SEGMENT_DONE ; Branch if not, accumulate this segment ; of the transfer BBSW #IO$V_DATACHECK,- ; Branch if the datacheck modifier SCDRP$W_FUNC(R5),- ; has been specified. Perform a IO_DATACHECK ; datacheck operation. MOVW #SS$_NORMAL,R0 ; Set success status IO_RW_EXIT: INSV SCDRP$L_ABCNT(R5),- ; Load low-order number of bytes #16,#16,R0 ; transferred into R0 MOVZWL SCDRP$L_ABCNT+2(R5),R1 ; Load high-order number of bytes ; transferred into R1 BRW COMPLETE_IO ; Complete the QIO ; Here we have completed one piece of a segmented transfer. Update the SVAPTE ; and MEDIA fields in the SCDRP and go perform the next segment of the transfer. IO_RW_SEGMENT_DONE: ASHL #-7,R0,R0 ; Convert byte count to longword index ADDL R0,SCDRP$L_SVAPTE(R5) ; Update SVAPTE field in SCDRP ASHL #-2,R0,R0 ; Convert to block count ADDL R0,SCDRP$L_MEDIA(R5) ; Update logical block number in SCDRP BRW IO_RW_LOOP ; Go perform next segment of transfer ; Here the transfer count returned by the port doesn't match the requested ; byte count. If it's greater, than truncate the transfer to what we ; requested. Otherwise, accumulate the piece of the transfer that just ; completed and continue with the transfer. IO_RW_MISMATCH: BLSS 10$ ; Branch if the transfer count is less ; than the requested count, accumulate ; this piece of the transfer ADDL3 SCDRP$L_PAD_BCNT(R5),- ; Truncate the transfer such that the SCDRP$L_BCNT(R5),- ; the transfer count we got is what we SCDRP$L_TRANS_CNT(R5) ; expected BRB IO_RW_ACCUM ; Accumulate this piece of the transfer 10$: BICW #^X1FF,R0 ; Round transfer down to block multiple BEQL IO_RW_SHORT_XFER ; Branch if no bytes to accumulate MOVL R0,SCDRP$L_BCNT(R5) ; Adjust byte count and transfer count ADDL3 SCDRP$L_PAD_BCNT(R5),- ; to accumulate this segment of the R0,SCDRP$L_TRANS_CNT(R5); transfer. BRB IO_RW_ACCUM ; Accumulate this segment of the transfer IO_RW_SHORT_XFER: MOVL #SS$_OPINCOMPL,R0 ; Set bad status BRB IO_RW_EXIT ; Complete I/O with error status ; Here an error occurred in performing the read or write. If it's due to a ; recoverable or non-recoverable (MEDIA) error, then accumulate the piece of ; the transfer that completed successfully and retry the original operation on ; failing block several times. ; ; For read-only devices, since we can't perform a reassign, retry the read ; only for non-recoverable errors. ; ; For devices (such as floppies) that don't support reassign, retry the read ; only for non-recoverable errors. ; ; To synchronize use of various UCB feilds, we set the BBR_IN_PROG bit ; in UCB$L_DK_FLAGS upon entry to IO_RW_ERR and clear it before returning. ; Any thread entering here that finds BBR_IN_PROG set will FORK_WAIT and ; try again later. This insures only one thread is doing bbr at a time. BBR_IO_RW_EXIT: BICL #UCB$M_BBR_IN_PROG,- ; Clear BBR interlock bit UCB$L_DK_FLAGS(R3) BRW IO_RW_EXIT ; Return to mainline code BBR_IO_RW_ACCUM: BICL #UCB$M_BBR_IN_PROG,- ; Clear BBR interlock bit UCB$L_DK_FLAGS(R3) BRW IO_RW_ACCUM ; Return to mainline code BBR_IO_RW_SHORT_XFER: ; Clear BBR interlock bit BICL #UCB$M_BBR_IN_PROG,- ; Clear BBR interlock bit UCB$L_DK_FLAGS(R3) BRW IO_RW_SHORT_XFER ; Return to mainline code BBR_WAIT: MOVL R0,SCDRP$L_TAG(R5) ; Preserve original status ; TAG is only used while in port, so ; it's a safe place across FORK_WAIT FORK_WAIT ; R3-R5 preserved: ; R3 = UCB ; R4 = SPDT ; R5 = SCDRP MOVL SCDRP$L_TAG(R5),R0 ; Restore original status MOVL SCDRP$L_IRP(R5),R2 ; Restore IRP ; Fall through to check BBR_IN_PROG IO_RW_ERR: BBSS #UCB$V_BBR_IN_PROG,- ; Some thread already doing BBR? UCB$L_DK_FLAGS(R3),- BBR_WAIT CMPL R0,#SS$_PARITY ; Media (non-recoverable) error? BEQL 10$ ; Branch if so CMPL R0,#SS$_RECOVERR ; Recoverable error? BNEQ BBR_IO_RW_EXIT ; Branch if not, return error to caller BBS #DEV$V_SWL,- ; Branch if device is software UCB$L_DEVCHAR(R3),5$ ; write locked BBC #UCB$V_NOREASSIGN,- ; Branch if device supports UCB$L_DK_FLAGS(R3),10$ ; reassign_block command 5$: BRW BBR_IO_RW_ACCUM ; Recoverable error can't be reassigned, ; treat as success ; The ADDNL_INFO field in the SCDRP contains a copy of the additional ; information field from the extended sense data. Make sure that it is valid ; and that the failing LBN it contains falls within the range of the original ; read or write command. If not, then assume the failing block is the first ; one in the range. If the failing LBN is valid, then accumulate all blocks ; of the transfer up to the failing LBN and update the MEDIA, ABCNT, and ; SVAPTE fields in the SCDRP. 10$: MOVL SCDRP$L_ADDNL_INFO(R5),R0 ; Get the failing LBN BLSS IO_RW_RETRY ; Branch if LBN is invalid CMPL R0,SCDRP$L_MEDIA(R5) ; Is failing LBN below range? BLSS IO_RW_RETRY ; Branch if so ASHL #-9,SCDRP$L_BCNT(R5),R1 ; Get number of blocks requested ADDL SCDRP$L_MEDIA(R5),R1 ; Get highest LBN requested CMPL R0,R1 ; Is failing LBN above range? BGEQ IO_RW_RETRY ; Branch if so SUBL3 SCDRP$L_MEDIA(R5),R0,R1 ; Calculate number of successfully ; transferred blocks BLEQ IO_RW_RETRY ; Branch if none ASHL #-9,SCDRP$L_TRANS_CNT(R5),-; Convert to block count R0 CMPL R0,R1 ; Were all blocks up to bad LBN transferred? BLSS 20$ ; Branch if not MOVL R1,R0 ; Accumulate all blocks up to the bad one 20$: ADDL R0,SCDRP$L_MEDIA(R5) ; Update LBN ASHL #2,R0,R0 ; Convert block count to longword index ADDL R0,SCDRP$L_SVAPTE(R5) ; Update SVAPTE ASHL #7,R0,R0 ; Convert to byte count ADDL R0,SCDRP$L_ABCNT(R5) ; Update accumulated byte count ; At this point all blocks of the transfer up to failing LBN has been ; accumulated. Retry the transfer of the failing LBN several times to see if ; the block can be read or written successfully. If so, then accumulate the ; data for this block and proceed with the transfer. Otherwise, attempt to ; reassign the block if appropriate. IO_RW_RETRY: MOVB #RW_RETRY_CNT,- ; Initialize read/write retry count UCB$B_RW_RETRY(R3) ; SUBL3 SCDRP$L_ABCNT(R5),- ; Get length of remaining transfer IRP$L_BCNT(R2),- ; SCDRP$L_BCNT(R5) ; CMPL SCDRP$L_BCNT(R5),#512 ; More than one block? BLEQ 10$ ; Branch if not MOVL #512,SCDRP$L_BCNT(R5) ; Limit transfer to one block 10$: DISABLE_ERRLOG ; Temporarily disable errorlogging BSBW READ_WRITE ; Perform the read or write REENABLE_ERRLOG ; Reenable errorlogging MOVL SCDRP$L_IRP(R5),R2 ; Get IRP address BLBSW R0,BBR_IO_RW_ACCUM ; Branch if success, pick up original ; transfer where we left off CMPL R0,#SS$_PARITY ; Non-recoverable error? BEQL 20$ ; Branch if so CMPL R0,#SS$_RECOVERR ; Recoverable error? BNEQW BBR_IO_RW_EXIT ; Branch if not, complete QIO with error ; The following code segment catches the case in which a read to a write-locked ; disk fails initially with a non-recoverable error, but returns a recoverable ; error on a read retry. In this case, we accept the data rather than risk ; loosing it with another read retry. The same logic holds true for disks that ; don't support reassign. BBS #DEV$V_SWL,- ; Branch if device is software UCB$L_DEVCHAR(R3),15$ ; write locked BBC #UCB$V_NOREASSIGN,- ; Branch if device supports UCB$L_DK_FLAGS(R3),20$ ; reassign_block command 15$: BRW BBR_IO_RW_ACCUM ; Recoverable error can't be reassigned, ; accumulate data 20$: DECB UCB$B_RW_RETRY(R3) ; Decrement read/write retry count BGTR 10$ ; Branch if count not exhausted ; Here we've been unsuccessful several times in performing the read or ; write to the failing LBN. If it's appropriate to reassign the block, do so. ; Note that since there is no forced error flag in SCSI, we can't reassign a ; block with a non-recoverable read error. IO_RW_REASSIGN: ; If we're here with a disk that doesn't support reassign, then all the ; retries above must have faild due to non-recoverable errors. Therfore, ; complete the QIO now with bad status. BBSW #UCB$V_NOREASSIGN,- ; Branch if device doesn't support UCB$L_DK_FLAGS(R3),- ; reassign_block command BBR_IO_RW_EXIT BBC #IRP$V_FUNC,- ; Branch if this is a write, OK to SCDRP$W_STS(R5),10$ ; try reassign CMPL R0,#SS$_PARITY ; Non-recoverable error? BEQLW BBR_IO_RW_EXIT ; Branch if so, NOT OK try reassign. 10$: MOVB #REASSIGN_RETRY_CNT,- ; Initialize the reassign retry count UCB$B_REASSIGN_RETRY(R3); IO_RW_REASSIGN_LOOP: 20$: MOVL R5,UCB$L_SCDRP_SAV1(R3) ; Save the original SCDRP address PUSHL SCDRP$L_MEDIA(R5) ; Save LBN of block to reassign BSBW ALLOC_SCDRP ; Allocate a new SCDRP for the reassign POPL SCDRP$L_MEDIA(R5) ; Copy LBN to reassign SCDRP BSBW REASSIGN_BLOCK ; Go attempt to reassign the block BSBW DEALLOC_SCDRP ; Deallocate the reassign SCDRP MOVL UCB$L_SCDRP_SAV1(R3),R5 ; Restore the original SCDRP address MOVL R5,UCB$L_SCDRP(R3) ; Save it in the UCB BLBS R0,IO_RW_REWRITE ; Branch if the reassign succeeded DECB UCB$B_REASSIGN_RETRY(R3); Decrement the reassign retry count BGTR 20$ ; Branch if count not exhausted ; Here we've attempted a reassign and failed. Write the original data to ; whereever the block is currently assigned as the failed reassign operations ; may have corrupted the data. ; BRB IO_RW_REWRITE ; Rewrite the block ; Here the reassign has completed successfully. Write the original data ; to the reassigned block. The assumption is that the reassign command ; doesn't necessarily move the data from the old to the new location. ; If the original command was a read and the last attempt to read the ; block returned a short transfer count, then don't rewrite the block ; because the data in the user's buffer is invalid. Instead, rely on the ; reassign command to move the data. IO_RW_REWRITE: MOVB #REWRITE_RETRY_CNT,- ; Initialize rewrite retry count UCB$B_REWRITE_RETRY(R3) ; BBC #IRP$V_FUNC,- ; Branch if this is a write, OK to SCDRP$W_STS(R5),10$ ; rewrite block CMPL SCDRP$L_TRANS_CNT(R5),- ; Valid transfer count? #512 ; BLSSW BBR_IO_RW_SHORT_XFER ; Branch if not, can't rewrite block 10$: BICW #IRP$M_FUNC,- ; Clear the FUNC bit to indicate this SCDRP$W_STS(R5) ; is a write function DISABLE_ERRLOG ; Temporarily disable errorlogging BSBW READ_WRITE ; Rewrite the original block REENABLE_ERRLOG ; Reenable errorlogging MOVL SCDRP$L_IRP(R5),R2 ; Restore the IRP address MOVW IRP$W_STS(R2),- ; Restore the setting of the func SCDRP$W_STS(R5) ; bit from the IRP BLBSW R0,BBR_IO_RW_ACCUM ; Branch if success, pick up original ; transfer where we left off DECB UCB$B_REWRITE_RETRY(R3) ; Decrement rewrite retry count BGTR 10$ ; Branch if count not exhausted ; Here we've failed to write the data to the reassigned block. Attempt to ; reassign the block again. If the reassign retry count is exhausted. Then ; we've done all we can so return the status of the last failing write. DECB UCB$B_REASSIGN_RETRY(R3); Decrement reassign retry count BGTRW IO_RW_REASSIGN_LOOP ; Branch if count not exhausted CMPL R0,#SS$_RECOVERR ; Was last write recoverable? BEQLW BBR_IO_RW_ACCUM ; Branch if so, treat as successful and ; proceed with transfer BRW BBR_IO_RW_EXIT ; Otherwise, complete QIO with error status ; Workaround a problem in RZ55's with microde versions less than 900. ; RZ55 with microcode version less than 900 will return one additional ; block of data when the drive successfully recovers a block by ; simply rereading it. This block contains ramdom data and the host assumes ; it contains valid data, the users I/O contains erroneous data. ; ; Called from TRANS_SENSE_KEY, which has R5 = SCDRP of REQUEST_SENSE command ; and SCDRP$L_SCDRP_SAV2(R5) as SCDRP of original command ; RZ55_WORKAROUND: ; Check the version number of microcode of the RZ55. If the first digit is ; non-zero or the second digit is greater than the ascii value of '8' then ; this microcode has been fixed. Otherwise the class driver must workaround ; the hardware problem by decrementing TRANS_CNT. CMPB #^A'0',- ; Check first digit. UCB$L_HW_REV(R3) ; BNEQ 110$ ; If non-zero, no fixup needed. CMPB #^A'9',- ; Check second digit. If >= UCB$L_HW_REV+1(R3) ; '9' then no fixup is needed. BLEQU 110$ ; Fixup needed. PUSHL R5 ; Save active SCDRP MOVL SCDRP$L_SCDRP_SAV2(R5),R5 ; Restore original SCDRP address SUBL2 #DTE_EXTRA_BYTES,- ; Subtract off the extra block, to SCDRP$L_TRANS_CNT(R5) ; determine exactly which block to ; resume transfer from. POPL R5 ; Restore active SCDRP 110$: RSB ; Read the rest of the I/O request. .SBTTL IO_WRITECHECK - Compare user's data with that on disk ;+ ; IO_WRITECHECK ; ; This routine compares the data in the user's buffer to that on the disk. ; No write is actually performed, just a read (to a buffer allocated from ; pool) followed by the compare operation. ; ; INPUTS: ; ; R2 - IPR address ; R3 - UCB address ; R4 - PDT address ; R5 - SCDRP address ; ; OUTPUTS: ; ; R0 - Status ; R1,R2 - Destroyed ; All other registers preserved ;- IO_WRITECHECK: ; BRB IO_DATACHECK ; Fall through to datacheck routine .SBTTL IO_DATACHECK - Check data that was just read or written ;+ ; IO_DATACHECK ; ; This routine is invoked if a QIO read or write specifies the DATACHECK ; qualifier. It reads the set of blocks that have just been read or written ; and compares the original and new data. A temporary buffer is allocated ; from non-paged pool to store the data being read, and whose contents is ; compared with that of the user's buffer. ; ; INPUTS: ; ; R3 - UCB address ; R4 - PDT address ; R5 - SCDRP address ; ; OUTPUTS: ; ; R0 - Status ; R1,R2 - Destroyed ; All other registers preserved ;- IO_DATACHECK: BISW #IRP$M_FUNC,- ; Set the FUNC but to indicate this SCDRP$W_STS(R5) ; is a read function MOVL SCDRP$L_IRP(R5),R2 ; Get IRP address CLRL SCDRP$L_ABCNT(R5) ; Initialize accumulated byte count MOVW IRP$W_FUNC(R2),- ; Copy function code and modifiers, SCDRP$W_FUNC(R5) ; MEDIA, SVAPTE, and BOFF fields MOVL IRP$L_MEDIA(R2),- ; from the IRP to the SCDRP SCDRP$L_MEDIA(R5) ; MOVL IRP$L_SVAPTE(R2),- ; SCDRP$L_SVAPTE(R5) ; MOVW IRP$W_BOFF(R2),- ; SCDRP$W_BOFF(R5) ; MOVL IRP$L_BCNT(R2),R1 ; Get transfer length CMPL R1,UCB$L_MAXBCNT(R3) ; Greater than max? BLEQ 10$ ; Branch if not MOVL UCB$L_MAXBCNT(R3),R1 ; Use MAXBCNT instead 10$: BSBW ALLOC_POOL ; Allocate a datacheck buffer MOVL R2,- ; Save datacheck buffer address SCDRP$L_DATACHECK(R5) ; MOVL R2,SCDRP$L_SVA_USER(R5) ; Datacheck buffer is also "user" buffer MOVL SCDRP$L_IRP(R5),R2 ; Restore IRP address IO_DC_LOOP: BISB #SCDRP$M_S0BUF,- ; Indicate that this is an S0 "user" SCDRP$L_SCSI_FLAGS(R5) ; buffer SUBL3 SCDRP$L_ABCNT(R5),- ; Attempt to transfer all remaining IRP$L_BCNT(R2),- ; bytes in user's buffer SCDRP$L_BCNT(R5) ; CMPL SCDRP$L_BCNT(R5),- ; Transfer length greater than maximum UCB$L_MAXBCNT(R3) ; supported? BLEQU 10$ ; Branch if not MOVL UCB$L_MAXBCNT(R3),- ; Otherwise, transfer must be segmented SCDRP$L_BCNT(R5) ; into pieces of MAXBCNT length 10$: BSBW READ_WRITE ; Send a SCSI read or write command BLBS R0,IO_DC_ACCUM ; Branch on success CMPL R0,#SS$_RECOVERR ; Recoverable error? BNEQ IO_DC_EXIT ; Branch if not, return with error status IO_DC_ACCUM: MOVL SCDRP$L_IRP(R5),R2 ; Restore IRP address SUBL3 SCDRP$L_PAD_BCNT(R5),- ; Get actual number of bytes transferred SCDRP$L_TRANS_CNT(R5),R0; less any possible padding CMPL R0,SCDRP$L_BCNT(R5) ; Compare with requested transfer count BNEQ IO_DC_MISMATCH ; Branch if mismatch occurred BSBW DATACHECK_CMP ; Compare the original and new data BLBC R0,IO_DC_EXIT ; Branch if a mismatch occurred SUBL3 SCDRP$L_PAD_BCNT(R5),- ; Get actual number of bytes transferred SCDRP$L_TRANS_CNT(R5),R0; less any possible padding ADDL R0,SCDRP$L_ABCNT(R5) ; Accumulate this piece of the transfer CMPL SCDRP$L_ABCNT(R5),- ; Is transfer complete? IRP$L_BCNT(R2) BLSSU IO_DC_SEGMENT_DONE ; Branch if not, accumulate this segment ; of the transfer INSV SCDRP$L_ABCNT(R5),- ; Load low-order number of bytes #16,#16,R0 ; transferred into R0 MOVZWL SCDRP$L_ABCNT+2(R5),R1 ; Load high-order number of bytes ; transferred into R1 MOVW #SS$_NORMAL,R0 ; Set success status IO_DC_EXIT: PUSHL R0 ; Save R0 MOVL SCDRP$L_DATACHECK(R5),R0; Get datacheck buffer address BSBW DEALLOC_POOL ; Deallocate the datacheck buffer POPL R0 ; Restore R0 BRW COMPLETE_IO ; Complete the QIO ; Here we have completed one piece of a segmented transfer. Update the SVAPTE ; and MEDIA fields in the SCDRP and go perform the next segment of the transfer. IO_DC_SEGMENT_DONE: ASHL #-7,R0,R0 ; Convert byte count to longword index ADDL R0,SCDRP$L_SVAPTE(R5) ; Update SVAPTE field in SCDRP ASHL #-2,R0,R0 ; Convert to block count ADDL R0,SCDRP$L_MEDIA(R5) ; Update logical block number in SCDRP BRW IO_DC_LOOP ; Go perform next segment of transfer ; Here the transfer count returned by the port doesn't match the requested ; byte count. If it's greater, than truncate the transfer to what we ; requested. Otherwise, accumulate the piece of the transfer that just ; completed and continue with the transfer. IO_DC_MISMATCH: BLSS 10$ ; Branch if the transfer count is less ; than the requested count, accumulate ; this piece of the transfer ADDL3 SCDRP$L_PAD_BCNT(R5),- ; Truncate the transfer such that the SCDRP$L_BCNT(R5),- ; the transfer count we got is what we SCDRP$L_TRANS_CNT(R5) ; expected BRB IO_DC_ACCUM ; Accumulate this piece of the transfer 10$: BICW #^X1FF,R0 ; Round transfer down to block multiple BEQL 20$ ; Branch if no bytes to accumulate MOVL R0,SCDRP$L_BCNT(R5) ; Adjust byte count and transfer count ADDL3 SCDRP$L_PAD_BCNT(R5),- ; to accumulate this segment of the R0,SCDRP$L_TRANS_CNT(R5); transfer. BRW IO_DC_ACCUM ; Accumulate this segment of the transfer 20$: MOVL #SS$_OPINCOMPL,R0 ; Set bad status BRB IO_DC_EXIT ; Complete I/O with error status .SBTTL IO_UNLOAD - Make drive available (and spin it down) .SBTTL IO_AVAILABLE - Make drive available (but don't spin it down) ;+ ; IO_UNLOAD ; IO_AVAILABLE ; ; This routine makes the drive available by clearing the VALID bit in the ; UCB. For the IO_UNLOAD entry point, the device is spun down if it has ; removable media. ; ; INPUTS: ; ; R3 - UCB address ; R4 - PDT address ; R5 - SCDRP address ; ; OUTPUTS: ; ; R0 - Status ; R1,R2 - Destroyed ; All other registers preserved ;- IO_UNLOAD: BBC #UCB$V_REMOVABLE,- ; Branch if this is a device with UCB$L_DK_FLAGS(R3),- ; non-removable media IO_AVAILABLE ; BSBW STOP_UNIT ; Spin down the unit IO_AVAILABLE: BICL #,- ; Set volume valid UCB$L_STS(R3) ; MOVZWL #SS$_NORMAL,R0 ; Set success status BRW COMPLETE_IO ; Complete the I/O .SBTTL IO_FORMAT - format a floppy diskette ;+ ; IO_FORMAT ; ; This routine formats a floppy diskette. ; ; An operator may request the format with the DCL command ; INIT/DENSITY=DOUBLE ; where the may be ; DOUBLE (or HD) for RX33s drives, ; DOUBLE (or HD), [or SINGLE (or DD) if DD_BYPASS set] for RX23s drives, ; DD (or SINGLE), HD (or DOUBLE), or ED for RX26 drives ; ; INPUTS: ; ; R2 - IRP address ; R3 - UCB address ; R4 - PDT address ; R5 - SCDRP address ; ; OUTPUTS: ; ; R0 - Status ; R1,R2 - Destroyed ; All other registers preserved ; ;- IO_FORMAT: ; Format command performs an implicit AVAILABLE. ; Update the UCB to reflect an AVAILABLE command. ASSUME UCB$V_VALID GE 8 BICB #, - ; Clear software volume valid. UCB$W_STS+1(R3) BBCC #UCB$V_LCL_VALID, - ; First FORMAT or AVAILABLE? UCB$L_STS(R3), 10$ ; If CC not first, "Can't happen" DECB UCB$B_ONLCNT(R3) ; Decrement online count. 10$: BBS #UCB$V_FORMAT,- ; Is FORMAT supported by the drive? UCB$L_DK_FLAGS(R3),100$ MOVL #SS$_FORMAT,R0 ; No, return an error BRW 1000$ 100$: BBC #UCB$V_HWL,- ; Can we write (to format) the diskette? UCB$L_DK_FLAGS(R3),120$ MOVL #SS$_WRITLCK,R0 ; No, return an error BRW 1000$ ; ; Determine that the correct /DENSITY qualifier was used on the INIT command. ; ; We support the following commands: ; ; INIT/DENSITY=SINGLE shows up here as IRP$L_MEDIA=1 ; INIT/DENSITY=DD shows up here as IRP$L_MEDIA=1 ; INIT/DENSITY=DOUBLE shows up here as IRP$L_MEDIA=2 ; INIT/DENSITY=HD shows up here as IRP$L_MEDIA=2 ; INIT/DENSITY=ED shows up here as IRP$L_MEDIA=3 ; ; The allowed formats for the various drives are: ; ; RX23s allows IRP$L_MEDIA=2 always, and ; IRP$L_MEDIA=1 only when UCB$L_DK_FLAGS is set; ; RX33s allows only IRP$L_MEDIA=2; ; RX26 allows IRP$L_MEDIA= 1, 2, or 3, but the appropriate diskette ; must be in the drive. ; 120$: CMPB #DT$_RX26,- ; Check RX26-specific format params UCB$B_DEVTYPE(R3) BNEQ 170$ ; Not RX26, go check the others CMPL #1,IRP$L_MEDIA(R2) ; Lowest value allowed for RX26 BGTRU 180$ ; Too low, go report error CMPL #3,IRP$L_MEDIA(R2) ; Highest value allowed for RX26 BLSSU 180$ ; Too high, go report error BRB 200$ ; Value's OK ; 170$: CMPL #2,IRP$L_MEDIA(R2) ; Verify /density=double BEQL 200$ ; Yep, media's = 2 ; Allow formatting with /density=single ; only for RX23 with a bypass override ; because 9-sector formats are ; unreliable BBC #UCB$V_DD_BYPASS,- ; Check for the special override bit UCB$L_DK_FLAGS(R3),- ; to see whether we can handle 180$ ; /density=single CMPB #DT$_RX23S,- ; Only worry about RX23S drive UCB$B_DEVTYPE(R3) BNEQ 180$ CMPL #1,IRP$L_MEDIA(R2) ; Verify /density=single BEQL 200$ ; Yep, media's = 1 180$: MOVL #SS$_BADPARAM,R0 ; Nope, report error BRW 1000$ ; ; Adjust format-sensitive parameters in the UCB here ; ; Check for appropriate diskette type for the density requested ; 200$: MOVL SCDRP$L_IRP(R5),R2 ; Restore IRP address CMPB #DT$_RX26,- ; Only RX26 reports diskette type UCB$B_DEVTYPE(R3) BNEQ 500$ ; Not RX26, skip this check CMPZV #UCB$V_FLOPPY_MEDIA,- #UCB$S_FLOPPY_MEDIA,- UCB$L_DK_FLAGS(R3),- IRP$L_MEDIA(R2) ; Check that the requested format ; is compatible with the diskette BEQL 500$ ; OK, let it through MOVL #SS$_FORMAT,R0 ; No, return an error BRB 1000$ 500$: BSBW PROCESS_MODE_FORMAT_FLOPPY ; Change format parameters BLBC R0,1000$ ; using MODE_SENSE/MODE_SELECT BSBW FORMAT ; Issue format command BLBC R0,1000$ MOVZWL #SS$_NORMAL,R0 ; Set success status 1000$: BRW COMPLETE_IO ; Complete the I/O .SBTTL IO_DSE - Data security erase function ;+ ; IO_DSE ; ; This routine erases a set of logical blocks by writing zeros to the ; blocks. ; ; INPUTS: ; ; R2 - IPR address ; R3 - UCB address ; R4 - PDT address ; R5 - SCDRP address ; ; OUTPUTS: ; ; R0 - Status ; R1,R2 - Destroyed ; All other registers preserved ;- MAX_BUF_SIZE = < 65535 & ^C<511> > ASSUME < MAX_BUF_SIZE & 511 > eq 0 IO_DSE: BICW #IRP$M_FUNC,- ; Clear the FUNC but to indicate this SCDRP$W_STS(R5) ; is a write function MOVL IRP$L_MEDIA(R2),- ; Copy starting block number from SCDRP$L_MEDIA(R5) ; IRP to SCDRP ADDL3 #511,IRP$L_BCNT(R2),R1 ; Round up byte count to a block BICL #511,R1 ; boundary MOVL R1,IRP$L_BCNT(R2) ; save modified byte count ; ; allocate zeroed buffer to write from ; CMPL R1,#MAX_BUF_SIZE ; is the buffer too big? BLEQ 10$ ; LEQ means it's OK MOVL #MAX_BUF_SIZE ,R1 ; if it's too big use max buffer size 10$: BSBW ALLOC_POOL ; Allocate a DSE buffer (note that ; ALLOC_POOL zeros the buffer) MOVL R2,SCDRP$L_SVA_USER(R5) ; Save address of the DSE buffer CLRL SCDRP$L_ABCNT(R5) ; set accumulated count to 0 ; ; This is the main write loop ; 20$: MOVL SCDRP$L_IRP(R5),R2 ; restore the IRP ; ; get bytes left to write in R0 (IRP$L_BCNT(R2)-SCDRP$L_ABCNT(R5)) ; MOVL IRP$L_BCNT(R2),R0 ; get QIO byte count SUBL SCDRP$L_ABCNT(R5),R0 ; get bytes left to write out BLEQU 32$ ; ; Check to see if we;ve gotten to the end of the disk ; CMPL SCDRP$L_MEDIA(R5),- UCB$L_MAXBLOCK(R3) ; are we past the end of the DISK? BGEQ 30$ ; GTR means yes don't write any more ; ; see if bytes left to write is too big ; CMPL R0,#MAX_BUF_SIZE ; is this too many to do now? BLEQ 22$ ; LEQ means no write them out MOVL #MAX_BUF_SIZE,R0 ; else use max size 22$: ; ; make sure we won't write past end of disk ; ASHL #-9,R0,R1 ; convert to blocks ADDL2 SCDRP$L_MEDIA(R5),R1 ; get block after last write CMPL R1,UCB$L_MAXBLOCK(R3) ; will we go past the end? BLEQ 25$ ; leq means no everything is OK SUBL3 SCDRP$L_MEDIA(R5),- UCB$L_MAXBLOCK(R3),R1 ; less starting block for # blocks left ASHL #9,R1,R0 ; convert BLOCKS to bytes 25$: MOVL R0,SCDRP$L_BCNT(R5) ; Save byte count for this write BISB #SCDRP$M_S0BUF,- ; Indicate that this is an S0 "user" SCDRP$L_SCSI_FLAGS(R5) ; buffer BSBW READ_WRITE ; write out the data BLBC R0,34$ ; LBC means some error MOVL SCDRP$L_BCNT(R5),R0 ; get the # bytes that we wrote ADDL2 R0,SCDRP$L_ABCNT(R5) ; update the accumulated byte count ; with bytes we just wrote ASHL #-9,R0,R0 ; convert to blocks ADDL2 R0,SCDRP$L_MEDIA(R5) ; update next block to erase BRW 20$ ; ; here if we get to the end of the disk ; 30$: MOVL #SS$_IVADDR,R0 BRW 36$ ; ; here if we finish without incident ; 32$: MOVL #SS$_NORMAL,R0 BRW 36$ ; ; here if we get an error from read_write ; 34$: BRW 36$ ; return the error as is ; ; here to deallocate pool - retuens status in R0 ; 36$: PUSHL R0 ; Save status MOVL SCDRP$L_SVA_USER(R5),R0 ; Get address of S0 buffer BSBW DEALLOC_POOL ; Deallocate the S0 buffer POPL R0 ; Restore R0 ; ; here after buffer is deallocated or to return errors that happen before ; the buffer is allocated or if the allocation fails. ; 37$: MOVZWL SCDRP$L_ABCNT+2(R5),R1 ; Set high-order word of transfer cnt INSV SCDRP$L_ABCNT(R5),- ; Copy low-order word of transfer cnt #16,#16,R0 ; BRW COMPLETE_IO ; Complete the QIO request .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: BSBW SAVE_CONN_CHAR ; Save our previous state MOVL SCDRP$L_IRP(R5),R2 ; Restore IRP address 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_DK_FLAGS(R3) ; EXTZV #2,#1,(R0),R1 ; Get synchronous flag INSV R1,#UCB$V_SYNCHRONOUS,- ; Fill in synchronous flag in UCB #1,UCB$L_DK_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$: MOVZBL #QCHAR$K_NOT_QUEUED,R0 ; Send GK stuff non-queued BBC #UCB$V_GK_CHK_COND,- ; Did last I/O get check cond? UCB$L_DK_FLAGS(R3),15$ ; Br if not MOVZBL #QCHAR$K_ERROR_RECOVERY,R0 ; Else send this as ERROR_RECOVERY 15$: SPI$QUEUE_COMMAND QCHAR=R0 ; Queue the SCSI command BBCC #UCB$V_GK_CHK_COND,- ; If prev I/O got CHECK_COND UCB$L_DK_FLAGS(R3),17$ ; Clear it and unfreeze queue CLRL UCB$L_GK_PID(R3) ; Allow other processes to use DIAGNOSE SPI$RELEASE_QUEUE ; Resume queue processing now 17$: CMPB #2,@SCDRP$L_STS_PTR(R5) ; Did we just get CHK COND? BNEQ 19$ ; Br if not BISL #UCB$M_GK_CHK_COND,- ; Flag we got check cond UCB$L_DK_FLAGS(R3) MOVL SCDRP$L_IRP(R5),R2 ; Get IRP MOVL IRP$L_PID(R2),- ; Save this processes PID UCB$L_GK_PID(R3) ; For later checking 19$: 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 BSBW RESTORE_CONN_CHAR ; Restore Connection Characteristics MOVL R0,R2 ; Save return status from subroutine POPL R1 ; Restore the SCSI status byte POPL R0 ; Restore the port status BLBC R0,30$ ; If bad status from sending command MOVL R2,R0 ; then return that status, else ; return status from restore_connection 30$: INSV SCDRP$L_TRANS_CNT(R5),- ; Copy the transfer count to the #16,#16,R0 ; high-order word of R0 BICL #UCB$M_GK_ACTIVE,- ; Clear lock out UCB$L_DK_FLAGS(R3) BRW COMPLETE_IO ; Complete the QIO .SBTTL IO_AUDIO - SCSI audio STARTIO function ;+ ; IO_AUDIO ; ; This routine is executed in the STARTIO context. It converts an AUCB into a SCSI AUDIO ; Command Descriptor Block (CDB). This CDB is then sent to and executed by the target ; device. If there is data to be returned to a user buffer the data is copied within ; this routine. The AUCB Operating System status and SCSI status fields are updated. ; ; After the I/O has been completed by the target, the AUDIO_EXIT code is called to unlock ; any users buffers other than the AUCB. The AUCB will be unlocked during I/O post- ; processing. The System PTE's allocated to double map any of the buffer will be ; deallocated by this routine, prior to calling REQCOM. ; ; ; INPUTS: ; R2 - IRP address ; R3 - UCB address ; R4 - PDT address ; R5 - SCDRP address ; ; OUTPUTS: ; ; R0 - Status ; R1,R2 - Destroyed ; All other registers preserved ;- IO_AUDIO: ; First check if this might be an audio packet that failed and has been requeued ; by shadowing, MV, or whatnot and which has no start address. Fail this ; RIGHT AWAY. ; MOVL IRP$L_SEGVBN(R2),R0 ; Get start address of S0 buffer. BLSS 10$ ; Bad segvbn here; this may be a MV replay of a failed audio request. ; Nothing much has been started, so just clean up. MOVL #SS$_ILLIOFUNC,R0 BRW COMPLETE_IO 10$: MOVL R0,SCDRP$L_MEDIA(R5) ; Get copy of AUCB address in SCDRP. ;+ ; Dispatch to the appropriate path based on the function code in the low ; order byte of the AUCB's function code field. ; ; R0 is the SVA of the allocated buffered I/O buffer or AUCB Address. ; R2 is the address of the original IRP. ; R5 is the address of the SCDRP. ;- DISPATCH CD_FUNCTION_CODE(R0),TYPE=B,<- ; Dispatch according to function ,- ,- ,- ,- ,- ,- ,- ,- ,- ,- ,- > ;+ ; Note: The SONY CDROM does not implement this command. ;- CD_PLAY_TRACK_REL: ;+ ; All unsupported I/O functions will be failed at this point. ;- MOVL SCDRP$L_MEDIA(R5),R0 ; Get copy of AUCB address from SCDRP. MOVZWL #SS$_BADPARAM,- ; Set bad parameter status CD_COMMAND_STATUS(R0) MOVZWL CD_COMMAND_STATUS(R0),R0 ; Copy VMS status to R0 for COMPLETE_IO BRW AUDIO_BAD_CMD_EXIT ; Complete this I/O function .SBTTL AUDIO_PLAY_FUNCTIONS - Audio CDROM play startio code ;+ ; AUDIO_PLAY_FUNCTIONS ; ; The following code, makes up the startio routine for all of the Audio ; play functions supported by this driver. Execution of these functions cause ; the CD to begin a play operation. ; ; INPUTS: ; R0 - AUCB address ; R2 - IRP address ; R3 - UCB address ; R4 - PDT address ; R5 - SCDRP address ; ; OUTPUTS: ; ; R0 - Status ; R1,R2 - Destroyed ; All other registers preserved ;- AUDIO_PLAY_FUNCTIONS: ;+ ; Now we will allocate a command buffer and fill it in with a SCSI CDB ; with all the required fields initialized. ; ; The PLAY_TRACK command requires a starting and ending track. The starting ; track is passed AUCB ARG1 and ending track in AUCB ARG2. ;- CD_PLAY_AUDIO_TRACK: MOVAL CMD_PLAY_TRACK,R2 ; Address of Play Track command BSBW SETUP_CMD ; Perform setup for SCSI command ADDL3 #4,SCDRP$L_CMD_PTR(R5),-; Get address of SCSI CDB (Command). R0 ; The CDB doesn't start at 0, but 4. MOVL SCDRP$L_MEDIA(R5),R1 ; Restore address of AUCB. ;+ ; The command bytes must be moved one byte at a time into the CDB, since ; the byte ordering is not the same as VAX! ;- TSTBGTR CD_ARG1(R1),#99 ; IF starting track > 99, then error. MOVB CD_ARG1(R1),4(R0) ; Copy Starting Track number to CDB. MOVB CD_ARG1+1(R1),5(R0) ; Copy Starting Index number. TSTBLSS CD_ARG2(R1),#1 ; If ending track is < 1 then error. MOVB CD_ARG2(R1),7(R0) ; Copy Ending Track number to CDB. MOVB CD_ARG2+1(R1),8(R0) ; Copy Ending Index number. 10$: BSBW SEND_COMMAND ; Send the SCSI command BRW AUDIO_EXIT ; Complete an audio SCSI function ;+ ; The Play Audio command requires a starting LBA address and block count which ; are passed in ARG1 and ARG2 of the AUCB respectively. ;- CD_PLAY_AUDIO: MOVAL CMD_PLAY_AUDIO10,R2 ; Address of Play Audio10 (45) command BSBW SETUP_CMD ; Perform setup for SCSI command ADDL3 #4,SCDRP$L_CMD_PTR(R5),-; Get address of SCSI CDB (Command). R0 ; The CDB doesn't start at 0, but 4. MOVL SCDRP$L_MEDIA(R5),R1 ; Restore address of AUCB. ;+ ; Setup the starting Logical Block Address(LBA) for the Play Audio 10 command. ; ; The command bytes must be moved one byte at a time into the CDB, since ; the byte ordering is not the same as VAX! ;- MOVB CD_ARG1+0(R1),5(R0) ; Copy Starting Logical Block Addr(LSB) MOVB CD_ARG1+1(R1),4(R0) ; Copy middle LBA byte to CDB. MOVB CD_ARG1+2(R1),3(R0) ; Copy middle LBA byte to CDB. MOVB CD_ARG1+3(R1),2(R0) ; Copy MSB LBA byte to CDB ;+ ; Now setup the transfer length in the CDB for the Play Audio 10 command. ;- MOVB CD_ARG2+0(R1),8(R0) ; LSB of the Transfer Length into CDB. MOVB CD_ARG2+1(R1),7(R0) ; MSB of Transfer Length. TSTBGTR CD_ARG2+2(R1),#0 ; This field must be zero. TSTBGTR CD_ARG2+3(R1),#0 ; This field must be zero. 10$: BSBW SEND_COMMAND ; Send the SCSI command BRW AUDIO_EXIT ; Complete an audio SCSI function ;+ ; The Play MSF command requires a starting and ending time expressed in Minutes, Seconds ; and frames format and are passed in ARG1 and ARG2 of the AUCB respectively. ;- CD_PLAY_AUDIO_MSF: MOVAL CMD_PLAY_AUDIO_MSF,R2 ; Address of Play Audio MSF (47) command BSBW SETUP_CMD ; Perform setup for SCSI command ADDL3 #4,SCDRP$L_CMD_PTR(R5),-; Get address of SCSI CDB (Command). R0 ; The CDB doesn't start at 0, but 4. MOVL SCDRP$L_MEDIA(R5),R1 ; Restore address of AUCB. ;+ ; Setup the starting Minute, Seconds and Frame fields for the Play MSF command. ; ; The command bytes must be moved one byte at a time into the CDB, since ; the byte ordering is not the same as VAX! ;- MOVB CD_ARG1+0(R1),5(R0) ; Copy Starting Frame byte to the CDB. MOVB CD_ARG1+1(R1),4(R0) ; Copy Seconds byte to the CDB. MOVB CD_ARG1+2(R1),3(R0) ; Copy Minutes byte to the CDB. ;+ ; Now setup the ending MSF fields for the Play Audio 12 command. ;- MOVB CD_ARG2+0(R1),8(R0) ; Copy ending Frames byte to the CDB. MOVB CD_ARG2+1(R1),7(R0) ; Copy Seconds byte to the CDB. MOVB CD_ARG2+2(R1),6(R0) ; Copy Minutes byte to the CDB. 10$: BSBW SEND_COMMAND ; Send the SCSI command BRW AUDIO_EXIT ; Complete an audio SCSI function .SBTTL AUDIO_CONTROL_FUNCTIONS - Audio CDROM control startio code ;+ ; AUDIO_CONTROL_FUNCTIONS ; ; The functions in this section of code are used to executed various audio ; control functions including pausing and resuming the CD. ; ; INPUTS: ; R0 - AUCB address ; R2 - IRP address ; R3 - UCB address ; R4 - PDT address ; R5 - SCDRP address ; ; OUTPUTS: ; ; R0 - Status ; R1,R2 - Destroyed ; All other registers preserved ;- AUDIO_CONTROL_FUNCTIONS: CD_PAUSE: MOVAL CMD_PAUSE,R2 ; Address of PAUSE command BSBW SETUP_CMD ; Perform setup for SCSI command BSBW SEND_COMMAND ; Send the SCSI command BRW AUDIO_EXIT ; Exit Audio Start I/O. CD_RESUME: MOVAL CMD_RESUME,R2 ; Address of RESUME command BSBW SETUP_CMD ; Perform setup for SCSI command BSBW SEND_COMMAND ; Send the SCSI command BRW AUDIO_EXIT ; Exit Audio Start I/O. CD_PREVENT_REMOVAL: MOVAL CMD_REMOVAL,R2 ; Address of Prevent Removal command BSBW SETUP_CMD ; Perform setup for SCSI command ADDL3 #4,SCDRP$L_CMD_PTR(R5),-; Get address of SCSI CDB (Command). R0 ; The CDB doesn't start at 0, but 4. MOVB #01,4(R0) ; Set the Prevent Removal bit. BSBW SEND_COMMAND ; Send the SCSI command BRW AUDIO_EXIT ; Exit Audio Start I/O. CD_ALLOW_REMOVAL: MOVAL CMD_REMOVAL,R2 ; Address of Prevent Removal command BSBW SETUP_CMD ; Perform setup for SCSI command ADDL3 #4,SCDRP$L_CMD_PTR(R5),-; Get address of SCSI CDB (Command). R0 ; The CDB doesn't start at 0, but 4. CLRB 4(R0) ; Clear the Prevent Removal bit. BSBW SEND_COMMAND ; Send the SCSI command BRW AUDIO_EXIT ; Exit Audio Start I/O. .SBTTL AUDIO_GETSET_FUNCTIONS - Audio CDROM get/set parameter code ;+ ; AUDIO_GETSET_FUNCTIONS ; ; The functions in this section of code are used to query or initialize ; specific parameters or data from the CDROM. For example, the Table Of ; Contents (TOC) can be read, the current position of the CDROM ; can be read and returned to the user or the volume of the CDROM can be set. ; ; INPUTS: ; R0 - AUCB address ; R2 - IRP address ; R3 - UCB address ; R4 - PDT address ; R5 - SCDRP address ; ; OUTPUTS: ; ; R0 - Status ; R1,R2 - Destroyed ; All other registers preserved ;- AUDIO_GETSET_FUNCTIONS: ;+ ; The GET_STATUS AUCB function does a SCSI READ SUBQ command and returns the ; requested subchannel data (format 1 and 2 are supported) to the application. ; From this information the application can determine the current track and ; head location of the CD-ROM as well as the UPC bar code data. ;- CD_READ_SUB: DISPATCH CD_ARG2(R0),TYPE=B,<- ; Dispatch based on format <0, 80$>,- ; fmt #0, Format 1 and 2 combined. <1, 40$>,- ; fmt #1, current location data <2, 20$>- ; fmt #2, Media Catalog Number data > MOVL #SS$_BADPARAM,R0 ; else bad parameter BRW AUDIO_EXIT_NO_CMD ;+ ; Get format 2 (MCN) data from UCB or by issuing a SCSI READ SUBQ command ;- 20$: BSBW FETCH_MCN ; get format 2 data BLBS R0,30$ ; R0 with status BRW AUDIO_EXIT_NO_CMD 30$: ; ; MCN data in UCB is good, copy to users buffer ; PUSHR #^M MOVL SCDRP$L_IRP(R5),R1 ; Get original IRP Address MOVL IRP$L_SEGVBN(R1),R2 ; Get start address of the AUCB. MOVL CD_DEST_BUF_CNT(R2),R2 ; Get size of destination buffer. MOVL #CD_MCN_LEN,R0 ; and size of stored MCN data MINUM R0,R2 ; Minimize between these two values. MOVL IRP$L_EXTEND(R1),R1 ; Get the linked/extended IRP address MOVC3 R0,- ; Copy the number of bytes received @UCB$B_MCN_SCDATA(R3),-; from UCB to the @IRP$L_SEGVBN(R1) ; start of the mapped user buffer POPR #^M MOVL #SS$_NORMAL,R0 ; return success BRW AUDIO_EXIT_NO_CMD 40$: BSBW READ_CD_SUBQ ; Get format 1 data. BLBCW R0,200$ ; On error exit ;+ ; Move READ SUBQ data from intermediate buffer to users destination buffer. ;- PUSHR #^M MOVL SCDRP$L_IRP(R5),R1 ; Get original IRP Address MOVL IRP$L_SEGVBN(R1),R2 ; Get start address of the AUCB. MOVL CD_DEST_BUF_CNT(R2),R2 ; Get size of destination buffer. MINUM SCDRP$L_TRANS_CNT(R5),- ; Minimize between these two values. R2 MOVL IRP$L_EXTEND(R1),R1 ; Get the linked/extended IRP address MOVC3 SCDRP$L_TRANS_CNT(R5),- ; Copy the number of bytes received @SCDRP$L_SVA_USER(R5),-; Copy from Nonpaged, to the @IRP$L_SEGVBN(R1) ; start of the mapped user buffer POPR #^M BRW 200$ ;+ ; issue the format 0 "read", this is done by combining format 1 and format ; 2 data. ;- 80$: BSBW READ_CD_SUBQ ; Get format 1 data. BLBCW R0,200$ ; On error exit ;+ ; Move READ SUBQ data from intermediate buffer to users destination buffer. ;- MOVL SCDRP$L_IRP(R5),R1 ; Get original IRP Address MOVL IRP$L_SEGVBN(R1),R2 ; Get start address of the AUCB. MOVL CD_DEST_BUF_CNT(R2),R2 ; Get size of destination buffer. MINUM SCDRP$L_TRANS_CNT(R5),- ; Minimize between these two values. R2 PUSHR #^M MOVL IRP$L_EXTEND(R1),R1 ; Get the linked/extended IRP address MOVC3 SCDRP$L_TRANS_CNT(R5),- ; Copy the number of bytes received @SCDRP$L_SVA_USER(R5),-; Copy from Nonpaged, to the @IRP$L_SEGVBN(R1) ; start of the mapped user buffer MOVL R3,R1 ; copy addr of next byte in user's ; buffer POPR #^M SUBL SCDRP$L_TRANS_CNT(R5),- ; Subtract what's already transfered R2 ; from size of users buffer SUBPUSH R2 ; save register across call SUBPUSH R1 BSBW CLEANUP_CMD ; cleanup from READ SUBQ command BSBW FETCH_MCN ; get format 2 data SUBPOP R1 SUBPOP R2 BLBS R0,90$ ; R0 with status BRW AUDIO_EXIT_NO_CMD 90$: PUSHR #^M MOVL #CD_MCN_LEN,R0 ; get size of stored MCN data MINUM R0,R2 ; Minimize between these two values. MOVC3 R0,- ; Copy the number of bytes received @UCB$B_MCN_SCDATA(R3),-; from UCB to the (R1) ; remainder of user's buffer POPR #^M MOVL #SS$_NORMAL,R0 ; return success BRW AUDIO_EXIT_NO_CMD 200$: BRW AUDIO_EXIT ; Complete an audio SCSI function ;+ ; The GET_TOC AUCB function does a SCSI READ command and returns the Table Of Contents ; (TOC) data to the application. From this information the appliaction can determine ; the starting location and data type for each track on the CD-ROM. ;- CD_READ_TOC: MOVAL CMD_CD_READ_TOC,R2 ; Address of READ TOC command BSBW SETUP_CMD ; Set Command ADDL3 #4,SCDRP$L_CMD_PTR(R5),-; Get address of SCSI CDB (Command). R0 ; The CDB doesn't start at 0, but 4. MOVL SCDRP$L_MEDIA(R5),R1 ; Restore address of AUCB. TSTBGTR CD_ARG2(R1),#99 ; Can't have more than 99 tracks, now! MOVB CD_ARG2(R1),6(R0) ; Write Starting Track to CDB TSTBGTR CD_ARG1+0(R1),#1 ; If greater than one error ASHL #1,CD_ARG1(R1),R1 ; Get LBA/MSF bit in left bit position BISB R1,1(R0) ; Select MSF or LBA address format. BSBW SEND_COMMAND ; Send the SCSI command BLBCW R0,250$ ; Branch on error ;+ ; Move TOC data from intermediate buffer to the destination buffer. ;- PUSHR #^M MOVL SCDRP$L_IRP(R5),R1 ; Get original IRP Address MOVL IRP$L_SEGVBN(R1),R2 ; Get start address of the AUCB. MOVL CD_DEST_BUF_CNT(R2),R2 ; Get size of destination buffer. MINUM SCDRP$L_TRANS_CNT(R5),- ; Minimize between these two values. R2 MOVL IRP$L_EXTEND(R1),R1 ; Get the linked/extended IRP address MOVC3 SCDRP$L_TRANS_CNT(R5),- ; Copy the number of bytes received @SCDRP$L_SVA_USER(R5),-; Copy from Nonpaged, to the @IRP$L_SEGVBN(R1) ; start of the mapped user buffer POPR #^M 250$: BRW AUDIO_EXIT ; Complete an audio SCSI function ;+ ; The GET_VOLUME AUCB function does a SCSI MODE SENSE command and returns the Sense Data ; containing the channel information to the application, from this the current volume ; can be determined. ;- CD_GET_VOLUME: ;+ ; Before doing a mode select (set volume) do a mode sense to get the current ; values of the parameters. ;- BSBW CD_GET_SENSE ; Issue CD-ROM specific Mode Sense BLBCW R0,AUDIO_EXIT ; Branch on error ;+ ; Copy the volume and channel control from the Sense data buffer to the ; AUCB. ;- PUSHL R0 MOVL SCDRP$L_SVA_USER(R5),R0 ; Get address of MODE SENSE Data MOVL SCDRP$L_MEDIA(R5),R1 ; Get copy of AUCB address from SCDRP. MOVL 20(R0),CD_ARG1(R1) ; Copy port(0,1) Volume and port info MOVL 24(R0),CD_ARG2(R1) ; Copy port(2,3) Volume and port info POPL R0 BRW AUDIO_EXIT ; Complete an audio SCSI function ;+ ; The SET_VOLUME AUCB function does a SCSI MODE SENSE, followed by a MODE SELECT command ; and sets the volume and channel information on the target device. ;- CD_SET_VOLUME: ;+ ; Before doing a mode select (set volume) do a mode sense to get the current ; values of the parameters. ;- BSBW CD_GET_SENSE ; Issue CD-ROM specific Mode Sense BLBCW R0,190$ ; Branch on error ;+ ; Since we want to reuse this SCDRP and call SETUP_CMD, deallocate the ; previously allocated Mode Sense COMMAND buffer. SETUP_CMD will allocate ; a new one for the MODE SELECT command. ; ; Note: We are using the previously allocated MODE SENSE buffer as our MODE ; Select buffer. CLEANUP_CMD will deallocate this buffer when we are done. ;- SPI$DEALLOCATE_COMMAND_BUFFER ; Deallocate the command buffer MOVAL CMD_CD_MODE_SELECT,R2 ; Address of MODE SENSE BSBW SETUP_CMD ; Set Command BLBCW R0,190$ ; Branch on error ;+ ; We must force this request to be a write request, since we are bye-passing ; some of the SETUP_CMD setup to allow us to use the MODE SENSES buffer. ;- BICW #IRP$M_FUNC,- ; Mode Select is "write" operation, SCDRP$W_STS(R5) ; with data going to the target. ;+ ; Now that all the setup is done, fixup the MODE SENSE bytes to be exactly ; the way we need them. ; ; Copy the volume and channel control from the AUCB into the CCB. ;- MOVL SCDRP$L_CMD_PTR(R5),R1 ; Get address of command buffer MOVB #<4+8+16>,A_LEN(R1) ; Number of Mode Select bytes. MOVL SCDRP$L_SVA_USER(R5),R0 ; Get address of MODE SENSE Data CLRB (R0) ; Clear the Sense Data Length MOVL SCDRP$L_MEDIA(R5),R1 ; Get copy of AUCB address from SCDRP. MOVL CD_ARG1(R1),20(R0) ; Copy port(0,1) Volume and port info MOVL CD_ARG2(R1),24(R0) ; Copy port(2,3) Volume and port info BSBW SEND_COMMAND ; Send the SCSI command BLBCW R0,190$ ; Branch on error 190$: BRW AUDIO_EXIT ; Complete an audio SCSI function ;+ ; Subroutine used to get the volume information by issuing a mode sense ; command for the Audio Control Parameters. ;- CD_GET_SENSE: SUBSAVE MOVAL CMD_MODE_SENSE,R2 ; Address of MODE SENSE BSBW SETUP_CMD ; Set Command ;+ ; Now that all the setup is done, fixup the MODE SENSE bytes to be exactly ; the way we need them. ;- MOVL SCDRP$L_CMD_PTR(R5),R1 ; Get address of command buffer ;+ ; For now just get the value for the CDROM page, later get it from the ; AUCB. ;- MOVB #^X0E,PAGE_CODE(R1) ; Interested in CDROM Audio Ctrl Page MOVB #28,A_LEN(R1) ; Number of Mode Sense bytes to receive. BSBW SEND_COMMAND ; Send the SCSI command. ; Status checked by caller. SUBRETURN RSB IO_BADPARM: MOVZBL #SS$_BADPARAM,R0 ; Set bad parameter status BRW AUDIO_EXIT .SBTTL READ_CD_MCN - reads CD-ROM Sub-Channel to get the Media Cat. # ;+ ; ; This routine sends a READ SUB-CHANNEL command requesting the 02h Data ; format code, Media Catalog Number, be returned for a CD-ROM media. ; This command causes the CD to stop playing audio, therefore it is only ; issued in the following cases (all indicated by the UCB$M_CD_VALID flag ; being clear): ; ; 1) When the "mount" of the device occurs (called from PACKACK ; routine). ; ; 2) If the CD_READ_SUB format 2 command is issued and a UNIT ATTENTION ; Sense key indicating a media change has occured was returned for ; a previous SCSI command. ; ; INPUTS: ; ; R3 - UCB address ; R4 - PDT address ; R5 - SCDRP address ; ; OUPUTS: ; R1,R2 - Destroyed ; R0 - Status ; ; UCB$B_MCN_DATA - filled in ; UCB$M_CD_VALID - set to indicate MCN data valid if command succeeded ; ;- READ_CD_MCN: SUBSAVE BICL #UCB$M_CD_VALID,- ; Indicate stored sub-channel data for UCB$L_DK_FLAGS(R3) ; CD-ROM is invalid. MOVAL CMD_CD_READ_SUB,R2 ; Address of READ SUB command BSBW SETUP_CMD ; Set Command ADDL3 #4,SCDRP$L_CMD_PTR(R5),-; Get address of SCSI CDB (Command). R0 ; The CDB doesn't start at 0, but 4. MOVB #2,3(R0) ; Write Sub Channel Data Format 2. BSBW SEND_COMMAND ; Send the SCSI command BLBCW R0,20$ ; Branch on error ;+ ; Get some pool to save the MCN data in ;- TSTL UCB$B_MCN_SCDATA(R3) ; Do we already have a buffer? BLSS 10$ ; Branch if yes MOVL #CD_MCN_LEN,R1 ; get required length PUSHL R3 ; Save UCB address JSB G^EXE$ALONONPAGED ; Get pool POPL R3 ; Restore UCB address BLBS R0,5$ ; Got the pool, OK BUG_CHECK INCONSTATE,FATAL ; BUGCHECK for now 5$: MOVL R2,UCB$B_MCN_SCDATA(R3) ; Save pointer ;+ ; Copy Subchannel format 2 data to UCB ;- 10$: PUSHR #^M MOVL #CD_MCN_LEN,R2 ; Get size of destination buffer. MINUM SCDRP$L_TRANS_CNT(R5),- ; Minimize between these two values. R2 MOVC3 SCDRP$L_TRANS_CNT(R5),- ; Copy the number of bytes received @SCDRP$L_SVA_USER(R5),-; Copy from Nonpaged, to the @UCB$B_MCN_SCDATA(R3) ; start of the area in the UCB POPR #^M BISL #UCB$M_CD_VALID,- ; Indicate stored sub-channel data for UCB$L_DK_FLAGS(R3) ; CD-ROM is valid. 20$: BSBW CLEANUP_CMD ; cleanup from SCSI command MOVL #SS$_NORMAL,R0 ; return success SUBRETURN ; and return ;+ ; ; This routine sends a READ SUB-CHANNEL command requesting the 01h Data ; ; INPUTS: ; ; R3 - UCB address ; R4 - PDT address ; R5 - SCDRP address ; ; OUPUTS: ; R1,R2 - Destroyed ; R0 - Status ; ;- READ_CD_SUBQ: SUBSAVE ;+ ; Read format #1 sub-channel, The Q-Sub channel with current location info. ;- MOVAL CMD_CD_READ_SUB,R2 ; Address of READ SUB command BSBW SETUP_CMD ; Set Command ADDL3 #4,SCDRP$L_CMD_PTR(R5),-; Get address of SCSI CDB (Command). R0 ; The CDB doesn't start at 0, but 4. MOVL SCDRP$L_MEDIA(R5),R1 ; Restore address of AUCB. MOVB #1,3(R0) ; Write Sub Channel Data Format. TSTBGTR CD_ARG1+0(R1),#1 ; If greater than one error ASHL #1,CD_ARG1+0(R1),R1 ; Get LBA/MSF bit in right bit position BISB R1,1(R0) ; Select MSF or LBA address format. BSBW SEND_COMMAND ; Send the SCSI command SUBRETURN ; and return ;+ ; ; This routine gets the SUB-CHANNEL format 2 data, by issuing a SCCI ; READ SUB-CHANNEL command requesting the 02h Data if the data in the ; UCB is invalid. ; ; INPUTS: ; ; R3 - UCB address ; R4 - PDT address ; R5 - SCDRP address ; ; OUPUTS: ; R1,R2 - Destroyed ; R0 - Status ; ;- MCN_DEF_STATUS: .BYTE 0 ; Status buffer if no SCSI cmd issued. FETCH_MCN: SUBSAVE ;+ ; We issue a extra read_sub command here to guarantee that the CD has not been ; changed. This command will clear the CD_VALID flag in the UCB if the a unit ; attention occurs during this commmand. ;- BSBW READ_CD_SUBQ ; Get format 1 data. BSBW CLEANUP_CMD ; toss data and cleanup BLBC R0,30$ ; On error exit MOVAB MCN_DEF_STATUS,- ; Assume no SCSI command and setup SCDRP$L_STS_PTR(R5) ; status buffer in SCDRP here. MOVB #SCSI$C_GOOD,- ; Assume status of sucesss MCN_DEF_STATUS BITL #UCB$M_CD_VALID,- ; Is the stored sub-channel data for UCB$L_DK_FLAGS(R3) ; CD-ROM is valid? BNEQW 30$ ; If NEQ yes just return BSBW READ_CD_MCN ; read format #2 data to UCB 30$: SUBRETURN .SBTTL + .SBTTL + SCSI COMMAND EXECUTION ROUTINES .SBTTL + .SBTTL INQUIRY - Send an inquiry command ;+ ; INQUIRY ; ; This routine sends an inquiry command to the target. At least 36 bytes of ; inquiry data are expected to be returned. The peripheral device type field ; is check to ensure the target is a disk-like device. The product ID field ; is used to determine the device type. In addition, several sanity checks are ; made on the inquiry data. ; ; 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),- ; Sufficient inquiry data returned? #36 ; BLSSUW 40$ ; Branch if not MOVL SCDRP$L_SVA_USER(R5),R0 ; Get address of inquiry data MOVL 32(R0),UCB$L_HW_REV(R3) ; Save hardware revision level ASSUME UCB$B_DEVCLASS+1 EQ UCB$B_DEVTYPE CLRW UCB$B_DEVCLASS(R3) ; Assume unknown device class and type BICL2 #,- ; type based on media - make sure UCB$L_DK_FLAGS(R3) ; that only one of these gets set EXTZV #5,#3,(R0),R1 ; Get peripheral qualifier BNEQW 40$ ; If not 0, no device here EXTZV #0,#5,(R0),R1 ; Get peripheral device type CMPB R1,#SCSI$C_DISK ; Is this a SCSI disk device? BEQL 10$ ; Branch if so CMPB R1,#SCSI$C_WORM ; Or a write-once read-multiple device? BNEQ 5$ ; Branch if not BISL2 #UCB$M_WORM,- ; Set a bit to indicate that it is a UCB$L_DK_FLAGS(R3) ; WORM device BRB 10$ ; Branch to continue disk device code 5$: CMPB R1,#SCSI$C_OPTICAL ; Or a optical device? BNEQ 7$ ; Branch if not BISL2 #UCB$M_OPTICAL,- ; Set a bit to indicate that it is an UCB$L_DK_FLAGS(R3) ; optical device BRB 10$ ; Branch to continue disk device code 7$: CMPB R1,#SCSI$C_CDROM ; Or a CDROM-like device BNEQ 40$ ; Branch if not, illegal device type BISL #UCB$M_CDROM,- ; Indicate CDROM-like device UCB$L_DK_FLAGS(R3) ; BISL2 #UCB$M_HWL,- ; This a read-only device so set UCB$L_DK_FLAGS(R3) ; the hardware write-lock bit BBSS #DEV$V_SWL,- ; This a read-only device so set UCB$L_DEVCHAR(R3),10$ ; the software write-lock bit 10$: MOVB #DC$_DISK,- ; Indicate that this a disk device UCB$B_DEVCLASS(R3) ; BBSS #UCB$V_REMOVABLE,- ; Assume drive has removable media UCB$L_DK_FLAGS(R3),15$ ; 15$: TSTW (R0) ; Is this a removable media device? BLSS 20$ ; Branch if so BBCC #UCB$V_REMOVABLE,- ; Clear the removable media bit UCB$L_DK_FLAGS(R3),20$ ; in the UCB 20$: EXTZV #0,#4,3(R0),R1 ; Get response data format MOVB R1, UCB$B_SCSI_VERSION(R3) ; Save for future checks ; Does drive conform to CCS? BEQL 25$ ; Branch if so - TGG0002 CMPB R1,#1 ; Does drive conform to CCS? BEQL 25$ ; Branch if so CMPB R1,#2 ; Does drive conform to SCSI-2? BNEQ 40$ ; Branch if not 25$: CMPB 4(R0),#^X1F ; Valid additional length field? BLSSU 40$ ; Branch if not BSBW GET_DEVICE_TYPE ; Determine device type from Vendor 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$_DRVERR,-; UCB=R3 ; MOVZWL #SS$_DRVERR,R0 ; Set bad status BRB 30$ ; 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. ; ; Request sense is always sent as QCHAR$K_ERROR_RECOVERY since the ; port will convert it to QCHAR$K_NOT_QUEUED if the connection does not ; support command queueing. ; ; 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 SPI$QUEUE_COMMAND QCHAR=#QCHAR$K_ERROR_RECOVERY ; Send the SCSI command BLBC R0,30$ ; Branch on error SPI$RELEASE_QUEUE ; Resume queue processing now ASSUME SCSI$C_GOOD EQ 0 MOVZBL @SCDRP$L_STS_PTR(R5),R1 ; Get SCSI status byte BICB #^XC1,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$: SPI$RELEASE_QUEUE ; Resume queue processing now 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 target to determine ; whether the device is spun up and ready. ; ; NOTE: The TUR command is also used by IO$_NOP for a cluster sequential ; NOP to sync queues. If this code is used in a multi-host environment, ; we may need to make this a ORDERED QUEUED command to sync with other ; initiators as well... ; ; 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_SPINUP_INPROG,- ; Set spinup-in-progress UCB$L_DK_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 non-queued TUR BSBW CLEANUP_CMD ; Cleanup from the SCSI command BICB #UCB$M_SPINUP_INPROG,- ; Clear spinup-in-progress UCB$L_DK_FLAGS(R3) ; SUBRETURN .SBTTL PROCESS_MODE_INFO - Send a mode sense command ;+ ; PROCESS_MODE_INFO ; ; This routine processes the mode pages for this target. It allocates pool ; to hold a copy of field descriptors. There is one field descriptor for ; each mode page field of interest. The local copy of the descriptors ; will be used to receive current values as well as to specify MODE SELECT ; values for those fields which need to be read and/or written. One call ; to the routine DO_MODE_PAGE will be made for each mode page we care about; ; DO_MODE_PAGE does a MODE SENSE current and, if appropriate, a MODE SENSE ; changeable and MODE SELECT for the page specified. ; ; Context: ; ; SCDRP thread ; IPL = Fork ; Fork lock held ; ; Inputs: ; ; R3 - UCB address ; R4 - PDT address ; R5 - SCDRP address ; ; Outputs: ; ; R0 - Status ; All other registers preserved ;- PROCESS_MODE_INFO:: SUBSAVE ; Save return address SUBPUSH R1 ; Save register SUBPUSH R2 ; Save register ; Allocate a local copy of the mode page descriptors out of pool MOVL #DESCRIPTOR_SIZE,R1 ; Setup size for allocation BSBW ALLOC_POOL ; Get buffer space for descriptor array PUSHR #^M MOVC3 #DESCRIPTOR_SIZE,- ; Copy the descriptors DESCRIPTOR_BASE,(R2) POPR #^M INSV UCB$B_SCSI_VERSION(R3),- ; Determine SCSI1 vs SCSI2 for #SCDRP$V_MODE_SENSE,- ; later use by do_mode_page #SCDRP$S_MODE_SENSE,- SCDRP$L_SCSI_FLAGS(R5) ; Read the error recovery page for two reasons: first, to establish a standard for ; whether or not the device supports 10-byte mode sense commands and second, to ; fetch the block descriptor, which contains the block length; this is necessary ; to make sure that CD-ROMs are set to a 512-byte block size. LOAD_TENBYTE ; UCB => SCDRP 12$: MOVL #SCSI$PGCD$C_READ_WRITE_ERR,R0 ; Page code = error recovery page MOVAL ERR_PAGE_DESC(R2),R1 ; Address of descriptors BSBW DO_MODE_PAGE ; Process the page BLBS R0,18$ ; Branch on error SUBPUSH #SIZEOF_ERR ; Size in bytes of descriptors BRW 95$ ; Log error ; Control Mode page (Queuing parameters) 18$: STORE_TENBYTE ; SCDRP => UCB MOVW #512,UCB$W_BLOCK_SIZE(R3) ; Set block size in UCB IF_NOT_CMDQ 20$ ; If device doesn't do queuing, ignore ; control mode page BICB #UCB$M_MODE_SENSE_PAG10,- ; Assume this page not seen UCB$L_DK_FLAGS(R3) MOVL #SCSI$PGCD$C_CONTROL_MODE,R0 ; Page code = control mode page MOVAL CONTROL_MODE_PAGE_DESC(R2),R1 ; Address of descriptors BSBW DO_MODE_PAGE ; Process the page BLBS R0,20$ ; If successful, do next page LOAD_TENBYTE ; Refresh SCDRP bit BICL #UCB$M_CMDQ,- ; Disable TCQ UCB$L_DK_FLAGS(R3) ; Error recovery parameters 20$: CMPW UCB$W_BLOCK_SIZE(R3),#512 ; Is block size 512? BEQL 21$ ; Branch if so BRW 28$ ; Branch if not 21$: CMPB UCB$B_SCSI_VERSION(R3),#2 ; Is this SCSI2? BLSS 26$ ; Branch if not IF_NOT_CMDQ 24$ ; Branch if not TCQ ; Case 1: SCSI2 device that supports TCQ SUBPUSH #SIZEOF_SCSI2_TCQ ; Size in bytes of descriptors MOVAL SCSI2_TCQ_ERR_PAGE_DESC(R2),R1 ; Address of err rec descriptors SUBPUSH #SCSI2_TCQ_ERR_DEVSPC_DESC ; Offset to dev specific param SUBPUSH #SCSI2_TCQ_ERR_MEDTYP_DESC ; Offset to medium type BRW 30$ ; Join common code ; Case 2: SCSI2 device that does not support TCQ 24$: SUBPUSH #SIZEOF_SCSI2_NOTCQ ; Size in bytes of descriptors MOVAL SCSI2_NOTCQ_ERR_PAGE_DESC(R2),R1; Address of err rec descriptors SUBPUSH #SCSI2_NOTCQ_ERR_DEVSPC_DESC ; Offset to dev specific param SUBPUSH #SCSI2_NOTCQ_ERR_MEDTYP_DESC ; Offset to medium type BRW 30$ ; Join common code ; Case 3: SCSI1 device (by definition, does not support TCQ) 26$: SUBPUSH #SIZEOF_SCSI1 ; Size in bytes of descriptors MOVAL SCSI1_ERR_PAGE_DESC(R2),R1 ; Address of err rec descriptors SUBPUSH #SCSI1_ERR_DEVSPC_DESC ; Offset to dev specific param SUBPUSH #SCSI1_ERR_MEDTYP_DESC ; Offset to medium type BRW 30$ ; Join common code ; Case 4: Device does not have block size = 512 28$: SUBPUSH #SIZEOF_NON512 ; Size in bytes of descriptors MOVAL NON512_ERR_PAGE_DESC(R2),R1 ; Address of err rec descriptors SUBPUSH #NON512_ERR_DEVSPC_DESC ; Offset to dev specific param SUBPUSH #NON512_ERR_MEDTYP_DESC ; Offset to medium type ; Common code to process Read Write Error Recovery Page 30$: MOVL #SCSI$PGCD$C_READ_WRITE_ERR,R0 ; Page code = error recovery page BSBW DO_MODE_PAGE ; Process the page BLBS R0,32$ ; Continue if success IF_NOT_CMDQ 31$ ; Fatal error if noTCQ LOAD_TENBYTE ; Refresh SCDRP bit BICL #UCB$M_CMDQ,- ; Else disable TCQ UCB$L_DK_FLAGS(R3) BRW 24$ ; Retry with noTCQ desc 31$: PUSHL R0 ; Save Status SUBPOP R0 ; Get rid of medium type offset from stack SUBPOP R0 ; Get rid of dev specific param from stack POPL R0 ; Restore Status BRW 95$ ; Error path 32$: ; Since the error recovery page is normally returned, fetch and process the ; the relevant mode page header parameters (namely, the device specific ; parameter and the medium type) as part of the error recovery page processing. SUBPOP R0 ; Get medium type offset ADDL2 R2,R0 ; Get address of medium type descriptor SUBPOP R1 ; Get dev specific param offset ; (used for error logging only) ADDL2 R2,R1 ; Get address of dev specific descriptor BBC #7,MODE_DESC$L_ACTUAL(R1),5$ ; Is write-protect bit clear? ; NOTE: Write-protect bit is the MSB of ; the byte which was read, bit 7. 140$: BISL2 #UCB$M_HWL,- ; This a read-only device so set UCB$L_DK_FLAGS(R3) ; the hardware write-lock bit BBSS #DEV$V_SWL,- ; Set the software write-lock bit to UCB$L_DEVCHAR(R3),5$ ; indicate device is write-locked 5$: SUBPOP R1 ; Get rid of desriptor size BICL2 #,- ; Assume FORMAT unsupported UCB$L_DK_FLAGS(R3) BBS #UCB$V_WORM,- ; If this is a WORM device we need to UCB$L_DK_FLAGS(R3),7$ ; protect it from the file system. BBC #UCB$V_OPTICAL,- ; If it's not WORM, it might be optical UCB$L_DK_FLAGS(R3),67$ ; with WORM media - check the media CMPB MODE_DESC$L_ACTUAL(R0),- ; type code in the mode sense data. #SCSI$C_WORM ; BNEQ 67$ ; branch past *WL bit sets if not. 7$: BISL2 #UCB$M_HWL,- ; Mark the device as Hardware Write UCB$L_DK_FLAGS(R3) ; Locked. BISL2 #DEV$M_SWL,- ; Mark the device as Software Write UCB$L_DEVCHAR(R3) ; Locked. 67$: ; Pre-Optical code path. PUSHL R2 ; Save descriptor pointer ; Determine diskette type in RX26 drive CLRL R2 ; Assume unknown or default medium CMPB #DT$_RX26,UCB$B_DEVTYPE(R3) ; Only RX26 reports diskette type BNEQ 506$ ; Not RX26, skip this check MOVZBL #MODE_SENSE_MEDIA_TYPE_MAX,R2 ; Start checking for match with ; last cell in table 502$: CMPB MODE_DESC$L_ACTUAL(R0),- ; Check against table entry MODE_SENSE_MEDIA_TYPE_TABLE[R2] BEQL 506$ ; Matches, save index SOBGTR R2,502$ ; No match, back up in table 506$: INSV R2,#UCB$V_FLOPPY_MEDIA,- ; remember medium type #UCB$S_FLOPPY_MEDIA,- ; UCB$L_DK_FLAGS(R3) ; POPL R2 ; Restore descriptor pointer ; Format parameters 40$: MOVL #SCSI$PGCD$C_FORMAT_DEVICE,R0 ; Page code = format device page MOVAL FORMAT_DEVICE_PAGE_DESC(R2),R1 ; Address of descriptors BSBW DO_MODE_PAGE ; Process the page BLBS R0,41$ ; If success, continue LOAD_TENBYTE ; Refresh SCDRP bit CMPL #SS$_NOSUCHSEC,R0 ; Illegal page, special success BEQL 50$ ; Just do next page CLRB UCB$B_SECTORS(R3) ; Invalidate sector field SUBPUSH #SIZEOF_FORMAT ; Size of format page descriptors BRW 95$ ; Log error 41$: MOVB - ; Save # of sectors per track in UCB (R2),- UCB$B_SECTORS(R3) ; ; Geometry parameters for rigid disks 50$: MOVL #SCSI$PGCD$C_RIGID_DISK,R0 ; Page code = rigid disk params MOVAL RIGID_DISK_PAGE_DESC(R2),R1 ; Address of descriptors BSBW DO_MODE_PAGE ; Process the page BLBS R0,51$ ; If success, continue LOAD_TENBYTE ; Refresh SCDRP bit CMPL #SS$_NOSUCHSEC,R0 ; Illegal page, special success BEQL 55$ ; Just do next page CLRB UCB$B_TRACKS(R3) ; Invalidate tracks CLRW UCB$W_CYLINDERS(R3) ; Invalidate cylinders SUBPUSH #SIZEOF_RIGID_DISK ; Size of rigid disk descriptors BRW 95$ ; Log error 51$: MOVB - ; Save number of tracks per cylinder (R2),- UCB$B_TRACKS(R3) ; MOVB - ; Save number of cylinders (R2),- UCB$W_CYLINDERS+1(R3) ; (high byte) MOVB - ; Save number of cylinders (R2),- UCB$W_CYLINDERS(R3) ; (low byte) BRW 60$ ; Skip the Flexible Disk page now ; Geometry parameters for flexible disks 55$: MOVL #SCSI$PGCD$C_FLEXIBLE_DISK,R0 ; Page code = flexible disk params MOVAL FLEX_DISK_PAGE_DESC(R2),R1 ; Address of descriptors BSBW DO_MODE_PAGE ; Process the page BLBS R0,56$ ; If success, continue LOAD_TENBYTE ; UCB => SCDRP CMPL #SS$_NOSUCHSEC,R0 ; Illegal page, special success BEQL 60$ ; Just do next page CLRW UCB$W_CYLINDERS(R3) ; Invalidate cylinders CLRB UCB$B_TRACKS(R3) ; Invalidate tracks SUBPUSH #SIZEOF_FLEX_DISK ; Size of flex. disk descriptors BRW 95$ ; Log error 56$: MOVB - ; Save number of tracks per cylinder (R2),- UCB$B_TRACKS(R3) ; MOVB - ; Save number of cylinders (R2),- UCB$W_CYLINDERS+1(R3) ; (high byte) MOVB - ; Save number of cylinders (R2),- UCB$W_CYLINDERS(R3) ; (low byte) BISL2 #,- ; Remember that this is a floppy UCB$L_DK_FLAGS(R3) ; Check for 250 KHz RX23 diskette, ; if so, then mark it write-protected ; unless UCB$L_DK_FLAGS is ; set to allow writing of these media. BBS #UCB$V_DD_BYPASS,- ; Don't bother checking if the UCB$L_DK_FLAGS(R3),558$ ; special override bit is set CMPB #DT$_RX23S,UCB$B_DEVTYPE(R3) ; Only worry about RX23 media BNEQ 558$ ; CMPW #SCSI$FLX$C_XFR_250KHZ,- ; Check for 250 KHz (R2) BEQL 559$ ; If 250 KHz, then write protect it ; Check for 48 TPI diskette, ; if so, then mark it ; software write-protected 558$: CMPB #SCSI$FLX$R_FLAGS,- ; Check length of page (R2) ; Skip test for SPC if page does BGEQ 60$ ; not include flags word CMPZV #SCSI$FLX$V_SPC,- ; Test for SPC #SCSI$FLX$S_SPC,- ; (steps per cylinder) (R2),- #0 ; equal to 0 BEQL 60$ ; If so, then skip 559$: BBSS #DEV$V_SWL,UCB$L_DEVCHAR(R3),60$; If not, then set the SWL ; Note: can't write 48 TPI diskettes ; Caching page 60$: .IF DEFINED RZ74_CACHE CMPB #DT$_RZ74,UCB$B_DEVTYPE(R3) ; Check for RZ74 disk drive BNEQ 65$ ; No, use caching page BBS #UCB$V_OUT_OF_REV,- ; Is this RZ74 out of rev? UCB$L_DK_FLAGS(R3),70$ ; Yes, use no-caching page 65$: .ENDC BBS #UCB$V_HWL,- ; Skip if writelocked UCB$L_DK_FLAGS(R3),75$ MOVL #SCSI$PGCD$C_CACHING,R0 ; Page code = caching page MOVAL CACHING_PAGE_DESC(R2),R1 ; Address of descriptors BSBW DO_MODE_PAGE ; Process the page BLBS R0,75$ ; If success, continue LOAD_TENBYTE ; Refresh SCDRP bit CMPL #SS$_NOSUCHSEC,R0 ; Illegal page, special success BEQL 75$ ; Continue SUBPUSH #SIZEOF_CACHING ; Size of caching page descriptors BRW 95$ ; Log error .IF DEFINED RZ74_CACHE ; NoCaching page 70$: MOVL #SCSI$PGCD$C_CACHING,R0 ; Page code = caching page MOVAL NOCACHING_PAGE_DESC(R2),R1 ; Address of descriptors BSBW DO_MODE_PAGE ; Process the page BLBS R0,75$ ; If success, continue LOAD_TENBYTE ; Refresh SCDRP bit CMPL #SS$_NOSUCHSEC,R0 ; Illegal page, special success BEQL 75$ ; Continue SUBPUSH #SIZEOF_NOCACHING ; Size of nocaching descriptors BRW 95$ ; Log error .ENDC 75$: MOVZWL #SS$_NORMAL,R0 ; Set success status 80$: PUSHL R0 ; Save return status MOVL R2,R0 ; Setup for deallocation BSBW DEALLOC_POOL ; Deallocate descriptors POPL R0 ; Restore return status SUBPOP R2 ; Restore register SUBPOP R1 ; Restore register SUBRETURN ; Return to caller 95$: CMPL R0,#SS$_CANCEL ; If cancel status BEQL 80$ ; Then leave now ; Since the real CDB has been deallocated by CLEANUP_CMD already, set up ; a dummy CDB for error logging purposes only. Note: All mode sense/select ; errors get logged as a 6-byte MODE_SENSE command with SCSI status FF. ; Since the register dump routine limits additional data to 255. bytes, ; We can store up to 6 descriptors (at 40. bytes each) in the error packet. PUSHL SCDRP$L_CMD_PTR(R5) ; Save values which will be PUSHL SCDRP$L_STS_PTR(R5) ; overwritten by the error PUSHL SCDRP$L_SVA_USER(R5) ; logging code path PUSHL SCDRP$L_TRANS_CNT(R5) PUSHL R2 ; Save descriptor pointer MOVAL DUMMY_MODE_SENSE_CDB+4,- ; Address of length longword SCDRP$L_CMD_PTR(R5) MOVAL DUMMY_MODE_SENSE_CDB,- ; Address of SCSI sts byte SCDRP$L_STS_PTR(R5) MOVL R1,SCDRP$L_SVA_USER(R5) ; Address of descriptors for ; this page SUBPOP R2 ; Get size of descriptors ASSUME MODE_DESC$S_FIELD_DESCRIPTOR EQ 40 CMPL R2,- ; Do we have too many to save? #<6*MODE_DESC$S_FIELD_DESCRIPTOR> BLEQ 98$ ; Good, we can save all desc MOVL #<6*MODE_DESC$S_FIELD_DESCRIPTOR>,- R2 ; Can save first six only. 98$: MOVL R2,SCDRP$L_TRANS_CNT(R5) ; Byte count of all descriptors ; for this page LOG_ERROR - ; Log a mode sense data error TYPE=MODE_SENSE_DATA,- VMS_STATUS=R0,- UCB=R3 POPL R2 ; Restore descriptor pointer POPL SCDRP$L_TRANS_CNT(R5) ; Restore values which were POPL SCDRP$L_SVA_USER(R5) ; overwritten by the error POPL SCDRP$L_STS_PTR(R5) ; logging code path POPL SCDRP$L_CMD_PTR(R5) MOVL #SS$_MEDOFL,R0 ; Convert debug-usable error ; to user-visible error BRW 80$ ; Use common exit path .PAGE .SBTTL PROCESS_MODE_FORMAT_FLOPPY ;+ ; PROCESS_MODE_FORMAT_FLOPPY ; ; This routine is called to change fields in the format parameters and the ; flexible disk parameter pages as necessary to conform to the requested ; format. ; ; Context: ; ; SCDRP thread ; IPL = Fork ; Fork lock held ; ; Inputs: ; ; R3 - UCB address ; R4 - PDT address ; R5 - SCDRP address ; ; Outputs: ; ; R0 - Status ; All other registers preserved ;- PROCESS_MODE_FORMAT_FLOPPY:: ; Send a mode select command SUBSAVE ; Save return address SUBPUSH R1 ; Save register SUBPUSH R2 ; Save register ; Allocate a local copy of the mode page descriptors out of pool MOVL #DESCRIPTOR_SIZE,R1 ; Setup size for allocation BSBW ALLOC_POOL ; Get buffer space for descriptor array PUSHR #^M MOVC3 #DESCRIPTOR_SIZE,- ; Copy the descriptors DESCRIPTOR_BASE,(R2) POPR #^M INSV UCB$B_SCSI_VERSION(R3),- ; Set SCSI version in SCDRP #SCDRP$V_MODE_SENSE,- #SCDRP$S_MODE_SENSE,- SCDRP$L_SCSI_FLAGS(R5) ; Set up format parameters depending ; on device: ; RX33s -> 15 sectors ; RX23s & /dens=double -> 18 sectors ; RX23s & /dens=single & bypass ; -> 9 sectors ; RX26 & /dens=DD -> 9 sectors ; RX26 & /dens=HD -> 18 sectors ; RX26 & /dens=ED -> 36 sectors MOVAB 15 sectors SCSI$FMT$W_TRACKS>,R0 CMPB #DT$_RX33S,UCB$B_DEVTYPE(R3) ; Check for RX33 drive BEQL 70$ ; Yep, use RX33 parameters MOVQ R1,-(SP) ; Save registers R1 and R2 MOVL SCDRP$L_IRP(R5),R2 ; Point to IRP SUBL3 #1,IRP$L_MEDIA(R2),R2 ; to get /density qualifier ; (adjusting for index offset of one) MOVAL MODE_SELECT_FORMAT_TABLE,R1 ; Point to table of addresses ADDL3 R1,(R1)[R2],R0 ; Get address from table MOVQ (SP)+,R1 ; Restore registers ; ; Build the format parameters page ; 70$: MOVW SCSI$FMT$W_SECTORS(R0),- ; Change sectors per track (R2) LOAD_TENBYTE ; Copy UCB.TENBYTE bit to SCDRP MOVL #SCSI$PGCD$C_FORMAT_DEVICE,R0 ; Page code = format device page MOVAL FORMAT_DEVICE_SEL_PAGE_DESC(R2),R1 ; Address of descriptors BSBW DO_MODE_PAGE ; get the mode page info BLBS R0,75$ ; Branch on success BRW 1100$ ; Branch on failure ; ; Build the flexible disk geometry parameters page ; ; Set up flexible geometry depending ; on device: ; RX33s -> 15 sectors ; RX23s & /dens=double -> 18 sectors ; RX23s & /dens=single & bypass ; -> 9 sectors ; RX26 & /dens=DD -> 9 sectors ; RX26 & /dens=HD -> 18 sectors ; RX26 & /dens=ED -> 36 sectors 75$: MOVAB 15 sectors SCSI$FLX$W_TRANSFER_RATE>,R0 ; CMPB #DT$_RX33S,UCB$B_DEVTYPE(R3) ; Check for RX33 drive BEQL 170$ ; Yes, take branch MOVQ R1,-(SP) ; Save registers R1 and R2 MOVL SCDRP$L_IRP(R5),R2 ; Point to IRP SUBL3 #1,IRP$L_MEDIA(R2),R2 ; to get /density qualifier ; (adjusting for index offset of one) MOVAL MODE_SELECT_FLEXIBLE_TABLE,R1 ; Point to table of addresses ADDL3 R1,(R1)[R2],R0 ; Get address from table MOVQ (SP)+,R1 ; Restore registers 170$: MOVW SCSI$FLX$W_TRANSFER_RATE(R0),- ; Use our transfer rate (R2) MOVB SCSI$FLX$B_HEADS(R0),- ; Use our number of heads (R2) MOVB SCSI$FLX$B_SECTORS_TRACK(R0),- ; Use our sectors per track (R2) MOVW SCSI$FLX$W_CYLINDERS(R0),- ; Use our number of cylinders (R2) EXTZV #SCSI$FLX$V_SSN,#1,- ; Use our start sector number SCSI$FLX$R_FLAGS(R0),- (R2) EXTZV #SCSI$FLX$V_SPC,#SCSI$FLX$S_SPC,- ; Use our steps/cyl SCSI$FLX$R_FLAGS(R0),- (R2) MOVL #SCSI$PGCD$C_FLEXIBLE_DISK,R0 ; Page code = flexible disk params MOVAL FLEX_DISK_SELECT_PAGE_DESC(R2),R1 ; Address of descriptors BSBW DO_MODE_PAGE BLBC R0,1100$ MOVL #SS$_NORMAL,R0 ; Set success status 900$: PUSHL R0 ; Save return status MOVL R2,R0 ; Setup for deallocation BSBW DEALLOC_POOL ; Deallocate descriptors POPL R0 ; Restore return status SUBPOP R2 ; Restore register SUBPOP R1 ; Restore register SUBRETURN ; Return to caller 1100$: MOVL #SS$_DRVERR,R0 BRB 900$ ; Join common exit code .PAGE .SBTTL VALIDATE_GEOMETRY ;+ ; VALIDATE_GEOMETRY ; ; In general, cylinders/tracks/sectors must be nonzero in order to set the device valid. ; This routine checks for zero values in various geometry fields. In special cases it ; will provide dummy values for fields; in other cases of zero fields it will return ; an error. ; Context: ; ; SCDRP thread ; IPL = Fork ; Fork lock held ; ; Inputs: ; ; R3 - UCB address ; R4 - PDT address ; R5 - SCDRP address ; ; Outputs: ; ; R0 - Status ; All other registers preserved ;- VALIDATE_GEOMETRY: PUSHR #^M ; Save registers ASSUME UCB$B_SECTORS+1 EQ UCB$B_TRACKS ASSUME UCB$B_TRACKS+1 EQ UCB$W_CYLINDERS MOVAL UCB$B_SECTORS(R3),R1 ; Get address sectors field in UCB TSTB (R1)+ ; Check that the tracks, sectors, and BEQL 10$ ; cylinders fields in the UCB are all TSTB (R1)+ ; non-zero. If any are zero, then possibly BEQL 10$ ; there's a problem and error status TSTW (R1)+ ; should be returned to prevent the ; volume valid bit from being set. BNEQ 30$ ; All fields nonzero, good. ; If here, at least one of cylinder/track/sector is zero. 10$: BITL #,- ; These devices do not always return UCB$L_DK_FLAGS(R3) ; cylinder and track info. BNEQ 30$ ; Set dummy values if so. ; Handle possibility of unformatted media in a floppy drive, which ; returns zeroes in the geometry fields. ; BBC #UCB$V_FLOPPY,- ; Report different error if floppy UCB$L_DK_FLAGS(R3),20$ MOVZWL #SS$_FORMAT,R0 ; Set bad status: unformatted media BRB 200$ ; Use common exit path ; If this is a read-only device such as a CDROM, it may not have real tracks, ; sectors, and cylinders. If this is the case, set up dummy values for the ; geometry parameters. 20$: BBS #DEV$V_SWL,- ; If this is a read-only device, it's not UCB$L_DEVCHAR(R3),30$ ; required to have non-zero values of ; tracks, sectors, and cylinders ; If here, at least one of cylinder/track/sector is zero, and this is an error. ; However, allow geometry to be generated if maxblock is nonzero ; since many opticals and the like do not supply geometry beyond disk ; size. TSTL UCB$L_MAXBLOCK(R3) ; Ensure not 0 len. dsk BNEQ 30$ MOVZWL #SS$_DRVERR,R0 ; Set bad status BRB 200$ ; Use common exit path ; Proceed to check geometry further. Set dummy values where appropriate so we may continue. 30$: MOVL UCB$L_MAXBLOCK(R3),R2 ; Get max block number INCL R2 ; Get max blocks MOVZBL UCB$B_TRACKS(R3),R1 ; Get number of tracks MOVZBL UCB$B_SECTORS(R3),R0 ; Get number of sectors MULL R0,R1 ; Multiply BEQL 35$ ; Branch if tracks or sectors is zero MOVZWL UCB$W_CYLINDERS(R3),R0 ; Get number of cylinders MULL R0,R1 ; Get tracks * sectors * cylinders CMPL R1,R2 ; Tracks * sectors * cyl = maxblock? BNEQ 40$ ; Branch if not BRW 190$ ; Set success status and exit ; Either tracks or sectors field (or both) is zero. Set up dummy values ; for these two fields. ASSUME UCB$B_SECTORS+1 EQ UCB$B_TRACKS 35$: MOVW #^X604,UCB$B_SECTORS(R3) ; Fill in dummy values for sector ; and track fields ; Here we've found that MAXBLOCK is not equal to tracks * sectors * cylinders. ; Calculate a new value for cylinders to ensure that the product is greater ; than maxblock. Use the same algorithm as DUDRIVER so that SCSI disks served ; in a cluster appear to have the same geometry on every node. 40$: MOVZBL UCB$B_TRACKS(R3),R1 ; Get number of tracks MOVZBL UCB$B_SECTORS(R3),R0 ; Get number of sectors MULL R1,R0 ; Multiply MOVL UCB$L_MAXBLOCK(R3),R1 ; Get number of blocks CLRL R2 ; Prepare for extended divide EDIV R0,R1,R0,R1 ; Calculate number of cylinders, remainder CMPL R0,#65534 ;S FF; Is cylinder number legal? BGTR 202$ ;S FF; If so go try a crude fix MOVW R0,UCB$W_CYLINDERS(R3) ; Save number of cylinders TSTL R1 ; Zero remaineder? BEQL 190$ ; Branch if so INCW UCB$W_CYLINDERS(R3) ; Otherwise, increment cylinders ; (tracks * sectors * cylinders must ; be >= maxblock) 190$: MOVL #SS$_NORMAL,R0 200$: POPR #^M ; Restore registers RSB ; ; Out of line code to handle really large geometries to the limits of what ; VMS can currently do. ; ; First method can waste 1024 blocks. ; Second method can waste 9216 blocks. Third can waste 65025. This is rather ; crude, but will at least allow the structure to be used. ; ; Note that we test against 65534 cylinders because cyl count may be ; incremented if maxblock field is more than trk*sect*cyl. This guarantees ; the cylinder field will be legal and thus the device will be usable, other ; things being equal. Note that because we accept devices with zeroes ; in trk or sect and will have filled in ^X604 above, we have four fake ; geometries in all: ; 6 x 4 x n ; 32 x 32 x n ; 96 x 96 x n ; 255 x255 x n ; ; This means geometry based loss of up to 23, 1023, 9215, or 65024 ; blocks as the device gets bigger, but the device will be usable over ; most of its surface. ; 202$: MOVB #32,UCB$B_TRACKS(R3) MOVB #32,UCB$B_SECTORS(R3) CMPL UCB$L_MAXBLOCK(R3),#<65534*32*32> ;does 32 by 32 by n work? BLSSU 40$ MOVB #96,UCB$B_TRACKS(R3) ;S FF; Set 96 by 96 by n geom MOVB #96,UCB$B_SECTORS(R3) CMPL UCB$L_MAXBLOCK(R3),#<65534*96*96> ;S FF; Be sure disk not too big BLSSU 40$ ;S FF; Redo computation if ok ; If disk is over 300Gb, try to allow 2TB MOVB #255,UCB$B_TRACKS(R3) ;S FF; Go for broke MOVB #255,UCB$B_SECTORS(R3) BRW 40$ ;This is as big as we can go. ; This should be adequate for any currently supported disk size (i.e., ; with a 32-bit block number a la SCSI-2), losing at most a small bit of ; capacity but allowing disk access for most of the surface. .SBTTL FORMAT - Send a format command ;+ ; FORMAT ; ; This routine sends a format command to the target. ; ; INPUTS: ; ; R4 - PDT address ; R5 - SCDRP address ; ; OUTPUTS: ; ; R0 - Status ; R1,R2 - Destroyed ; All other registers preserved ;- FORMAT: SUBSAVE ; Save return address MOVAL CMD_FORMAT_UNIT,R2 ; Address of FORMAT command BSBW SETUP_CMD ; Perform setup for SCSI command MOVL #RX_FMT_DISC_TMO,- ; Increase the disconnect timeout SCDRP$L_DISCON_TIMEOUT(R5); value for the format operation BSBW SEND_COMMAND ; Send the SCSI command BSBW CLEANUP_CMD ; Cleanup from the SCSI command SUBRETURN .SBTTL READ_CAPACITY - Send a read capacity command ;+ ; READ_CAPACITY ; ; This routine reads the capacity of the target. It is called AFTER MODE_SENSE ; and MODE_SELECT so that a possible change of block size will be reflected in ; the device's block count. It then ensures that the geometry information ; returned by MODE_SENSE agrees with the device's capacity (the product of ; tracks, sectors, and cylinders must not exceed the maximum block number). ; ; INPUTS: ; ; R3 - UCB address ; R4 - PDT address ; R5 - SCDRP address ; ; OUTPUTS: ; ; R0 - Status ; R1,R2 - Destroyed ; All other registers preserved ;- READ_CAPACITY: SUBSAVE ; Save return address MOVAL CMD_READ_CAPACITY,R2 ; Address of READ CAPACITY command BSBW SETUP_CMD ; Perform setup for SCSI command BSBW SEND_COMMAND ; Send the SCSI command BLBC R0,30$ ; Branch on error MOVL SCDRP$L_SVA_USER(R5),R1 ; Get address of read capacity data MOVAL UCB$L_MAXBLOCK+4(R3),R2 ; Get address just beyond MAXBLOCK .REPT 4 ; Copy four bytes of max block data MOVB (R1)+,-(R2) ; from SCSI buffer to UCB. Note that .ENDR ; bytes are reversed in SCSI buffer INCL (R2) ; Convert max block # to total blocks ADDL #2,R1 ; Step over irrelevant data MOVB (R1)+,UCB$W_BLOCK_SIZE+1(R3) ; Get MSB of block size MOVB (R1),UCB$W_BLOCK_SIZE(R3) ; Get LSB of block size CMPW UCB$W_BLOCK_SIZE(R3),#512 ; Is block size 512? BEQL 10$ ; If so, exit normally BITL #,- ; These devices may have ACPs to UCB$L_DK_FLAGS(R3) ; handle special geometry. BNEQ 10$ ; Exit normally if so. BBSS #DEV$V_SWL,UCB$L_DEVCHAR(R3),10$;S FF; Otherwise software writelock this device. 10$: MOVL #SS$_NORMAL,R0 ; Set success status 20$: BSBW CLEANUP_CMD ; Cleanup from the SCSI command SUBRETURN 30$: BBC #UCB$V_FLOPPY,- ; Report different error if floppy UCB$L_DK_FLAGS(R3),20$ MOVZWL #SS$_FORMAT,R0 ; Set bad status: unformatted media BRB 20$ ; Use common exit path .SBTTL START_UNIT - Send a start unit command .SBTTL STOP_UNIT - Send a stop unit command ;+ ; START_UNIT ; STOP_UNIT ; ; This routine sends a start or stop command to the target to spin up or ; spin down the device, respectively. ; ; INPUTS: ; ; R4 - PDT address ; R5 - SCDRP address ; ; OUTPUTS: ; ; R0 - Status ; R1,R2 - Destroyed ; All other registers preserved ;- START_UNIT: MOVAL CMD_START_UNIT,R2 ; Address of START UNIT command BRB START_STOP_COMMON ; Use common path STOP_UNIT: MOVAL CMD_STOP_UNIT,R2 ; Address of START UNIT command START_STOP_COMMON: SUBSAVE ; Save return address BSBW SETUP_CMD ; Perform setup for SCSI command MOVL SCDRP$L_CMD_PTR(R5),R0 ; Get address of command buffer BSBW SEND_COMMAND ; Send the SCSI command BSBW CLEANUP_CMD ; Cleanup from the SCSI command SUBRETURN .SBTTL READ_WRITE - Send a read or write command ;+ ; READ_WRITE ; ; This routine sends either a SCSI read or write command to the target based on ; setting of the FUNC bit in the SCDRP. The LBN in the command is filled in ; from the MEDIA field and the block count is filled in from the BCNT. The ; pad count field is filled in with the number of additional bytes over BNCT ; that must be transferred to make an integral number of blocks. Note that ; SCSI disks transfer an integral number of blocks, while a user can specify ; request for a fraction of a block. ; ; INPUTS: ; ; R2 - IPR address ; R3 - UCB address ; R4 - PDT address ; R5 - SCDRP address ; ; OUTPUTS: ; ; R0 - Status ; R1,R2 - Destroyed ; All other registers preserved ;- READ_WRITE: MOVL SCDRP$L_MEDIA(R5),- ; Save the current head position UCB$L_CUR_LBN(R3) ; CMPL SCDRP$L_MEDIA(R5),- ; Does the LBN specified require #EXTND_LBN_LIMIT ; an extended read or write command? BGTRU EXTND_READ_WRITE ; Branch if so SUBSAVE ; Save return address MOVAL CMD_WRITE,R2 ; Assume this is write command BBC #IRP$V_FUNC,- ; Branch if this is a write function SCDRP$W_STS(R5),10$ ; MOVAL CMD_READ,R2 ; Get address of SCSI read command 10$: BSBW SETUP_CMD ; Set up the command CLRL SCDRP$L_PAD_BCNT(R5) ; Assume no padding of last page required MOVL SCDRP$L_BCNT(R5),R0 ; Get byte count BICL3 #^C<^X1FF>,R0,R1 ; Number of bytes in last block BEQL 20$ ; Branch if block-integral transfer SUBL3 R1,#512,- ; Number of bytes of padding required SCDRP$L_PAD_BCNT(R5) ; for block-integral transfer 20$: ADDL2 #^X1FF,R0 ; Round up byte count to page boundary ASHL #-9,R0,R0 ; Convert to block count ADDL3 #<4+4>,- ; Address of transfer length field in SCDRP$L_CMD_PTR(R5),R1 ; SCSI command MOVB R0,(R1) ; Copy block count to transfer length MOVAL SCDRP$L_MEDIA(R5),R0 ; Get logical block number field in SCDRP .REPT 2 MOVB (R0)+,-(R1) ; Fill in logical block address .ENDR INSV (R0),#0,#5,-(R1) ; High-order 5 bits of logical block addr 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 buffer MOVL #QCHAR$K_UNORDERED,R0 ; Use simple queue tag BSBW QUEUE_COMMAND ; Queue the SCSI command BICL #SCDRP$M_S0BUF,- ; Don't deallocate the S0 "user" buffer SCDRP$L_SCSI_FLAGS(R5) ; at this time BSBW CLEANUP_CMD ; Clean up from the current command SUBRETURN ; Return to caller .SBTTL EXTND_READ_WRITE - Send an extended read or write command ;+ ; EXTND_READ_WRITE ; ; This routine sends either a SCSI extended read or write command to the ; target based on setting of the FUNC bit in the SCDRP. An extended command ; must be used when the LBN specified in the QIO won't fit in the LBN field ; of the normal read/write SCSI command. The LBN in the command is filled in ; from the MEDIA field and the block count is filled in from the BCNT. The ; pad count field is filled in with the number of additional bytes over BNCT ; that must be transferred to make an integral number of blocks. Note that ; SCSI disks transfer an integral number of blocks, while a user can specify ; request for a fraction of a block. ; ; INPUTS: ; ; R2 - IPR address ; R3 - UCB address ; R4 - PDT address ; R5 - SCDRP address ; ; OUTPUTS: ; ; R0 - Status ; R1,R2 - Destroyed ; All other registers preserved ;- EXTND_READ_WRITE: SUBSAVE ; Save return address MOVAL CMD_EXTND_WRITE,R2 ; Assume this is an extended write command BBC #IRP$V_FUNC,- ; Branch if this is a write function SCDRP$W_STS(R5),10$ ; MOVAL CMD_EXTND_READ,R2 ; Get address of SCSI extended read command 10$: BSBW SETUP_CMD ; Set up the command CLRL SCDRP$L_PAD_BCNT(R5) ; Assume no padding of last page required MOVL SCDRP$L_BCNT(R5),R0 ; Get byte count BICL3 #^C<^X1FF>,R0,R1 ; Number of bytes in last block BEQL 20$ ; Branch if block-integral transfer SUBL3 R1,#512,- ; Number of bytes of padding required SCDRP$L_PAD_BCNT(R5) ; for block-integral transfer 20$: ADDL2 #^X1FF,R0 ; Round up byte count to page boundary ASHL #-9,R0,R0 ; Convert to block count ADDL3 #<4+8>,- ; Address of transfer length field in SCDRP$L_CMD_PTR(R5),R1 ; SCSI command MOVB R0,(R1) ; Copy block count to transfer length MOVAL SCDRP$L_MEDIA(R5),R0 ; Get logical block number field in SCDRP SUBL #2,R1 ; Point just beyond LSB of LBN field in ; SCSI command packet .REPT 4 MOVB (R0)+,-(R1) ; Fill in logical block address .ENDR 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 buffer MOVL #QCHAR$K_UNORDERED,R0 ; Use simple queue tag BSBW QUEUE_COMMAND ; Queue the SCSI command BICL #SCDRP$M_S0BUF,- ; Don't deallocate the S0 "user" buffer SCDRP$L_SCSI_FLAGS(R5) ; at this time BSBW CLEANUP_CMD ; Clean up from the current command SUBRETURN ; Return to caller .SBTTL REASSIGN_BLOCK - Send a reassign block command ;+ ; REASSIGN_BLOCK ; ; This routine sends a reassign blocks command to a target to physically ; relocate the data for a specific logical block on the disk. This is done ; after a read operation fails with a recoverable error or a write operations ; fails with either a recoverable or non-recoverable error. Read operations ; with non-recoverable errors do not result in reassignment operations as this ; could result in undetected user data corruption. ; ; INPUTS: ; ; (R2) - Block number to reassign ; R3 - UCB address ; R5 - SCDRP address of original read/write command ; ; OUTPUTS: ; ; R0 - Status ; R1,R2 - Destroyed ;- REASSIGN_BLOCK: SUBSAVE ; Save return address BBS #DEV$V_SWL,- ; Branch if device is read-only, UCB$L_DEVCHAR(R3),10$ ; not possible to reassign block BBS #UCB$V_NOREASSIGN,- ; Branch if device doesn't support UCB$L_DK_FLAGS(R3),10$ ; reassign_block command MOVAL CMD_REASSIGN_BLOCKS,R2 ; Address of reassign blocks command BSBW SETUP_CMD ; Perform setup for SCSI command ADDL3 SCDRP$L_SVA_USER(R5),- ; Get defect list buffer address (and #3,R0 ; advance to defect list length field) MOVB #4,(R0)+ ; Just one defective block being ; reassigned MOVAL SCDRP$L_MEDIA+4(R5),R1 ; Get address beyond block number to ; reassign .REPT 4 MOVB -(R1),(R0)+ ; Fill in a byte of LBN (note bytes are swapped) .ENDR LOG_ERROR - ; Log a reassign block error (if the TYPE=REASSIGN_BLOCK,- ; reassign fails, SEND_COMMAND will VMS_STATUS=#SS$_NORMAL,-; log another error). UCB=R3 ; MOVL #REASSIGN_DISCON_TMO,- ; Set up extended disconnect timeout SCDRP$L_DISCON_TIMEOUT(R5) ; for reassign BSBW SEND_COMMAND ; Send the SCSI command BSBW CLEANUP_CMD ; Cleanup from the command SUBRETURN ; Return to caller 10$: BUG_CHECK INCONSTATE,FATAL ; Should never attempt to reassign a ; block for a write-locked disk, ; or for a drive which doesn't support ; reassign_block command .SBTTL + .SBTTL + UTILITY ROUTINES .SBTTL + .SBTTL SET_UNIT_ONLINE - Issue SCSI commands to bring unit online ;+ ; SET_UNIT_ONLINE ; ; This routine attempts to bring a unit online. First, an inquiry command is ; sent to the drive to ensure that it can communicate and that it's a valid ; disk-like device. If the INQUIRY fails, a fork thread is set up to ; periodically poll the drive. ; ; If the drive is the system disk, a PACKACK IRP is initiated for this unit ; to make the volume valid. The drive is then set online, and any I/O that had ; been queued to the device while the decice had been busy or offline is ; initiated. ; ; 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,11$ ; Branch on error BSBW SET_CONN_CHAR ; Initial connection characteristics 11$: BSBW DEALLOC_SCDRP ; Release the SCDRP MOVL R3,R5 ; Copy UCB address BLBC R0,20$ ; Branch if error BISW #UCB$M_ONLINE!- ; Set the device online, busy UCB$M_BSY,- ; UCB$W_STS(R5) ; ; If the MSCP server is loaded, call its "new device" routine to allow this ; unit to be served. MOVL G^SCS$GL_MSCP_NEWDEV,R2 ; Get address of MSCP routine BGEQ 12$ ; Branch if not available JSB (R2) ; Make this unit available to be served 12$: CMPL G^SYS$AR_BOOTUCB,R5 ; Is this the system disk? BNEQ 15$ ; Branch if not BSBW ALLOC_IRP ; Get an IRP MOVW #IO$_PACKACK,- ; Store function code IRP$W_FUNC(R3) ; BISW #IRP$M_PHYSIO,- ; Set physical I/O bit IRP$W_STS(R3) ; ADAWI #1,UCB$W_QLEN(R5) ; Increment the queue length MOVAL 13$,IRP$L_PID(R3) ; Set I/O completion return address JMP G^IOC$INITIATE ; Start the I/O 13$: BLBC IRP$L_IOST1(R5),14$ ; Branch if PACKACK failed MOVL IRP$L_UCB(R5),R0 ; Get UCB address BBSS #UCB$V_LCL_VALID, - ; Set local valid bit and UCB$L_STS(R0),14$ ; branch if its already set INCB UCB$B_ONLCNT(R0) ; Increment online count. 14$: BRW DEALLOC_IRP ; Deallocate the PACKACK IRP 15$: REMQUE @UCB$L_IOQFL(R5),R3 ; Any I/O queued to this device? BVS 17$ ; Branch if not JMP G^IOC$INITIATE ; Go initiate the I/O 17$: 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. In the case of the system disk, don't set the unit offline ; and don't flush the queues as this could cause a boot to fail. Instead, just ; poll and hope that eventually the system disk eventually comes back to life. 20$: CMPL G^SYS$AR_BOOTUCB,R5 ; Is this the system disk? BNEQ 30$ ; Branch if not DK_WAIT #2 ; Wait for 2 seconds BRW 10$ ; And try again 30$: BICW #UCB$M_ONLINE!- ; Set the unit offline, not busy UCB$M_BSY,- ; UCB$W_STS(R5) ; 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 CLRL R1 ; Clear transfer byte count MOVL #SS$_DEVOFFLINE,R0 ; Set device offline status JSB G^IOC$REQCOM ; Complete the QIO BRB 45$ ; Continue to flush queue 50$: DK_WAIT #60 ; Wait for 60 seconds BRW 10$ ; And try again .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 (spun up). The first time a test unit ready fails with ; DEVOFFLINE status (which comes from a NOT_READY status from the drive), a ; start unit command is sent to the drive in case the failure was ; due to the drive being spun down. Polling continues for READY_POLL_INTERVAL * ; READ_POLL_CNT seconds or until the device returns success status to a to a ; test unit ready command. ; ; 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 MOVB #READY_POLL_CNT,- ; Initialize retry count UCB$B_READY_RETRY(R3) ; 10$: BSBW TEST_UNIT_READY ; Execute TEST UNIT READY command BLBC R0,30$ ; Branch on error 20$: SUBRETURN ; Return to caller 30$: CMPL R0,#SS$_DEVOFFLINE ; Is device spun down? BNEQ 40$ ; Branch if not BBS #UCB$V_FLOPPY,- ; Branch if this is a floppy, return UCB$L_DK_FLAGS(R3),50$ ; immediately CMPB #DC$_DISK,- ; Is this really a disk device? UCB$B_DEVCLASS(R3) ; BNEQ 20$ ; Branch if not, can't issue start unit BBSS #SCDRP$V_DISK_SPUN_UP,- ; Attempt to spin up the disk just SCDRP$L_SCSI_FLAGS(R5),-; once per PACKACK (branch if this 40$ ; has already been done) BSBW START_UNIT ; Start up the drive 40$: DK_WAIT #READY_POLL_INTERVAL,- ; Wait a few seconds UCB=R3 ; DECB UCB$B_READY_RETRY(R3) ; Decrement retry count and try again BGTR 10$ ; if appropriate 50$: MOVZWL #SS$_MEDOFL,R0 ; Set bad status BRB 20$ ; Return with error status .SBTTL DATACHECK_CMP - Compare user buffer with datacheck buffer ;+ ; DATACHECK_CMP ; ; This routine is called by IO_DATACHECK and performs the actual comparison of ; the data in the user buffer with the data in the datacheck buffer. In doing ; this, the user's buffer is temporarily mapped into system space in order to ; perform the CMPC3. The SPTEs used to double map the user buffer were allocate ; when the driver was loaded and are shared between all units. ; ; INPUTS: ; ; R5 - SCDRP address ; ; OUTPUTS: ; ; R0 - Status (SS$_NORMAL or SS$_DATACHECK) ; All other registers preserved ;- DATACHECK_CMP: MOVL #SS$_DATACHECK,R0 ; Assume datacheck error PUSHR #^M ; Save regs ASSUME DATACHECK_SPTE+4 EQ DATACHECK_SVA MOVQ DATACHECK_SPTE,R0 ; Get SVA of datacheck SPTEs and SVA ; which these SPTEs map MOVL SCDRP$L_SVAPTE(R5),R2 ; SVAPTE of user buffer MOVZWL SCDRP$W_BOFF(R5),R4 ; Byte count ADDL2 SCDRP$L_BCNT(R5),R4 ; Byte offset ADDL #^X1FF,R4 ; Round up to next page ASHL #-9,R4,R4 ; Convert to page count 10$: MOVL (R2)+,R3 ; Get next user buffer SPTE BLSS 20$ ; Branch if valid JSB G^IOC$PTETOPFN ; Otherwise convert to valid PFN 20$: INVALIDATE_TB R1,ENVIRON=LOCAL,-; Double map next page of user's buffer INST1=,(R0)>,- INST2= ADDL #^X200,R1 ; Advance to next user buffer page ADDL #4,R0 ; Advance to next SPTE SOBGTR R4,10$ ; Repeat for entire user buffer MOVZWL SCDRP$W_BOFF(R5),R0 ; Get user buffer byte offset ADDL DATACHECK_SVA,R0 ; Address of double mapped user buffer CMPC3 SCDRP$L_BCNT(R5),(R0),- ; Compare user's buffer with datacheck @SCDRP$L_DATACHECK(R5) ; buffer POPR #^M ; Restore regs BNEQ 30$ ; Branch if mismatch occurred MOVL #SS$_NORMAL,R0 ; Set success status 30$: RSB ; Return to caller .SBTTL DK_WAIT - Stall for the specified number of seconds ;+ ; DK_WAIT ; ; This routine is used by the DK_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 DK_WAIT macro destroyes R0-R3 ;- DK_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 ALLOC_IRP - Allocate an I/O request packet ;+ ; ALLOC_IRP ; ; This routine is called during unit init for the system disk to allocate ; and IRP, which is used to force a PACKACK QIO through the driver. ; ; INPUTS: ; ; R5 - UCB address ; ; OUTPUTS: ; ; R3 - IRP address ; R0-R2 - Destroyed ; All other registers preserved ;- ALLOC_IRP: MOVZWL #IRP$C_LENGTH,R1 ; Load length of IRP JSB G^EXE$ALONONPAGED ; Allocate a block BLBC R0,10$ ; Branch if error PUSHR #^M ; Save regs MOVC5 #0,.,#0,R1,(R2) ; Initialize the packet POPR #^M ; Save regs MOVL R2,R3 ; Set up IRP pointer MOVB #DYN$C_IRP,- ; Store type IRP$B_TYPE(R3) ; MOVW #IRP$C_LENGTH,- ; Store size IRP$W_SIZE(R3) ; MOVL R5,IRP$L_UCB(R3) ; Set up UCB address MOVAL W^DEALLOC_IRP,- ; Store deallocation routine address IRP$L_PID(R3) ; RSB 10$: BUG_CHECK INCONSTATE, FATAL .SBTTL DEALLOC_IRP - Deallocate an I/O request packet ;+ ; DEALLOC_IRP ; ; The routine deallocates the IPR allocated during unit init to queue a PACKACK ; to the system disk. ; ; INPUTS: ; ; R5 - IRP address ; ; OUTPUTS: ; ; R0 - Destroyed ; All other registers preserved ;- DEALLOC_IRP: MOVL IRP$L_UCB(R5),R0 ; Get UCB address ADAWI #-1,UCB$W_QLEN(R0) ; Adjust queue length MOVL R5,R0 ; Copy IRP address JSB G^EXE$DEANONPAGED ; DEALLOCATE IRPE RSB .SBTTL SEND_COMMAND - Send a SCSI command ;+ ; SEND_COMMAND ; ; This routines sends a command to the SCSI device. It returns any failing ; port status to the caller. If the port status is success, it checks the ; SCSI status byte. If a check condition status is returned, a request ; sense command is sent to the target and the sense key is translated into a ; VMS status code, which is returned as status. ; ; If the target returns a hardware error, one retry is performed, on the ; assumption that the error could have been caused by vibration or some other ; spurious event. ; ; 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 #HWERR_RETRY_CNT,- ; Initialize hardware error retry count UCB$B_HWERR_RETRY(R3) ; SEND_COMMAND_RETRY: 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 #^XC1,R1 ; Clear reserved, vendor unique bits BNEQ 30$ ; Branch if bad status CLRL UCB$L_BUSY_TIME(R3) ; Device is not busy 10$: SUBRETURN ; Return to caller ; The port returned bad status from SPI$SEND_CMD. ; Conditionally log an error and return to the caller. 20$: CMPL R0,#SS$_ABORT ; Aborted command? BEQL 10$ ; Branch if so, don't log an error CMPL R0,#SS$_TIMEOUT ; Command timed out? BEQL 10$ ; Branch if so, don't log an error CMPL R0,#SS$_MEDOFL ; Medium offline? 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_BUSY,R1 ; Busy status? BEQL 35$ ; Branch if so CLRL UCB$L_BUSY_TIME(R3) ; Otherwise, indicate device is not busy CMPB #SCSI$C_CHECK_CONDITION,R1; Check condition status? BEQL 40$ ; Branch if so 31$: LOG_ERROR - ; Log a send command error TYPE=SEND_CMD_ERROR,- ; VMS_STATUS=#SS$_NOTQUEUED,-; UCB=R3 ; 32$: MOVL #SS$_MEDOFL,R0 ; Return a generic status code BRW 10$ ; The device has returned busy status. In order to catch the case in which ; the device continually returns busy status, set a limit on the time a device ; can continuously return busy status. If the doesn't successfully execute ; a command or return a status other than busy within this time, assume ; the device is hung and pull on bus reset. 35$: MOVL UCB$L_BUSY_TIME(R3),R0 ; Was device busy last time through? BNEQ 36$ ; Branch if so ADDL3 #MAX_BUSY_TIME,- ; Calculate busy timeout time G^EXE$GL_ABSTIM,- ; UCB$L_BUSY_TIME(R3) ; BRB 31$ ; Log an error and exit 36$: CMPL R0,G^EXE$GL_ABSTIM ; Had device been busy for an excessive ; amount of time BGTRU 32$ ; Branch if not CLRL UCB$L_BUSY_TIME(R3) ; Indicate device no longer busy LOG_ERROR - ; Log a send command error TYPE=SEND_CMD_ERROR,- VMS_STATUS=#SS$_RETRY,- UCB=R3 SPI$RESET ; Reset the SCSI bus to attempt to ; bring the device back to life BRB 32$ ; Use common exit ; 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$: CLRB SCDRP$B_SENSE_KEY(R5) ; Initialize saved sense key field MOVL R5,R2 ; Save this SCDRP address BSBW ALLOC_SCDRP ; Allocate an additional SCDRP MOVL R2,SCDRP$L_SCDRP_SAV2(R5) ; Save original SCDRP address 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 MOVL SCDRP$L_SCDRP_SAV2(R5),R2 ; Get original SCDRP address BSBW DEALLOC_SCDRP ; Deallocate the request sense SCDRP MOVL R2,R5 ; Restore original SCDRP address MOVL R5,UCB$L_SCDRP(R3) ; Copy it to the UCB TSTB SCDRP$B_SENSE_KEY(R5) ; Sense key of 0? BEQL 55$ ; Branch to retry command if so CMPB SCDRP$B_SENSE_KEY(R5),- ; Device (aborted command) error? #SCSI$C_ABORTED_COMMAND ; BEQL 55$ ; Branch if so, retry command CMPB SCDRP$B_SENSE_KEY(R5),- ; Device (hardware) error? #SCSI$C_HARDWARE_ERROR ; BNEQ 60$ ; Branch if not, no need to retry 55$: DECB UCB$B_HWERR_RETRY(R3) ; Decrement hardware retry count BGEQW SEND_COMMAND_RETRY ; Branch if retry not exhausted, retry ; the send command 60$: BRW 10$ ; Otherwise, return to caller .SBTTL QUEUE_COMMAND - Queue a SCSI command ;+ ; QUEUE_COMMAND ; ; This routines queues a command (QCHAR$K_UNORDERED) 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 as a QCHAR$K_ERROR_RECOVERY ; command to the target and the sense key is translated into a VMS status ; code, which is returned as status. ; ; If the target returns a hardware error, one retry is performed, on the ; assumption that the error could have been caused by vibration or some other ; spurious event. ; ; INPUTS: ; ; R0 - Queue characteristic ; R3 - UCB address ; R4 - PDT address ; R5 - SCDRP address ; ; OUTPUTS: ; ; R0 - Status ; R1,R2 - Destroyed ; All other registers preserved ;- QUEUE_COMMAND: SUBSAVE ; Save return address MOVB #HWERR_RETRY_CNT,- ; Initialize hardware error retry count UCB$B_HWERR_RETRY(R3) ; QUEUE_COMMAND_RETRY: SPI$QUEUE_COMMAND QCHAR=R0 ; Queue 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 #^XC1,R1 ; Clear reserved, vendor unique bits BNEQ 30$ ; Branch if bad status CLRL UCB$L_BUSY_TIME(R3) ; Device is not busy 10$: SUBRETURN ; Return to caller ; The port returned bad status from SPI$QUEUE_COMMAND. 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_BUSY,R1 ; Busy status? BEQL 35$ ; Branch if so CLRL UCB$L_BUSY_TIME(R3) ; Otherwise, indicate device is not busy CMPB #SCSI$C_CHECK_CONDITION,R1; Check condition status? BEQL 40$ ; Branch if so 31$: LOG_ERROR - ; Log a send command error TYPE=SEND_CMD_ERROR,- ; VMS_STATUS=#SS$_NORMAL,-; UCB=R3 ; 32$: MOVL #SS$_MEDOFL,R0 ; Return a generic status code BRW 10$ ; The device has returned busy status. In order to catch the case in which ; the device continually returns busy status, set a limit on the time a device ; can continuously return busy status. If the doesn't successfully execute ; a command or return a status other than busy within this time, assume ; the device is hung and pull on bus reset. 35$: MOVL UCB$L_BUSY_TIME(R3),R0 ; Was device busy last time through? BNEQ 36$ ; Branch if so ADDL3 #MAX_BUSY_TIME,- ; Calculate busy timeout time G^EXE$GL_ABSTIM,- ; UCB$L_BUSY_TIME(R3) ; BRB 31$ ; Log an error and exit 36$: CMPL R0,G^EXE$GL_ABSTIM ; Had device been busy for an excessive ; amount of time BGTRU 32$ ; Branch if not CLRL UCB$L_BUSY_TIME(R3) ; Indicate device no longer busy SPI$RESET ; Reset the SCSI bus to attempt to ; bring the device back to life BRB 32$ ; Use common exit ; 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$: CLRB SCDRP$B_SENSE_KEY(R5) ; Initialize saved sense key field MOVL R5,R2 ; Save this SCDRP address BSBW ALLOC_SCDRP ; Allocate an additional SCDRP MOVL R2,SCDRP$L_SCDRP_SAV2(R5) ; Save original SCDRP address 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 MOVL SCDRP$L_SCDRP_SAV2(R5),R2 ; Get original SCDRP address BSBW DEALLOC_SCDRP ; Deallocate the request sense SCDRP MOVL R2,R5 ; Restore original SCDRP address MOVL R5,UCB$L_SCDRP(R3) ; Copy it to the UCB TSTB SCDRP$B_SENSE_KEY(R5) ; Sense key of 0? BEQL 55$ ; Branch to retry command if so CMPB SCDRP$B_SENSE_KEY(R5),- ; Device (aborted command) error? #SCSI$C_ABORTED_COMMAND ; BEQL 55$ ; Branch if so, retry command CMPB SCDRP$B_SENSE_KEY(R5),- ; Device (hardware) error? #SCSI$C_HARDWARE_ERROR ; BNEQ 60$ ; Branch if not, no need to retry 55$: DECB UCB$B_HWERR_RETRY(R3) ; Decrement hardware retry count BLSS 60$ ; Exit if count exhausted MOVW SCDRP$W_QUEUE_CHAR(R5),R0; Restore QCHAR in R0 for SPI$QUEUE_CMD BRW QUEUE_COMMAND_RETRY ; Branch if retry not exhausted, retry ; the send command 60$: BRW 10$ ; Otherwise, 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 caused by media changes. ; ; - The first UNIT ATTENTION seen after a boot but before a successful PACKACK ; has completed. This unit attention is generally due to a bus reset having ; been issued during a boot or crash. IO_PACKACK sets the ATTN_SEEN flag so ; that any unit attentions seen after the first successful PACKACK will ; generate errors. ; ; - A NOT READY sense key if the original command was a test unit ready. This ; is considered normal as it's likely the disk is in the process of being ; spun up. ; ; - A recoverable error if the addition sense code is RECOVERED ERROR WITH READ ; RETRIES. This can occur due to disk vibrations and is thus considered ; acceptable. ; ; Before logging the error, play a trick with the two SCDRPs currently in use. ; Since we're really interested in logging the contents of the original command, ; and not the request sense command, copy the address of the original command ; buffer into the SCDRP for the request sense command. This causes the contents ; of the original command (a read or write, for example) to be logged along ; with the contents of the request sense data returned from the request sense ; command. ; ; INPUTS: ; ; R3 - UCB address ; R4 - PDT address ; R5 - SCDRP address for REQUEST_SENSE command ; ; SCDRP$L_SCDRP_SAV2(R5) - Address of SCDRP for original command ; ; OUTPUTS: ; ; R0-R2 - Destroyed ; All other registers preserved ;- LOG_EXTND_SENSE: PUSHL R7 ; Save register PUSHL R8 ; Save register MOVL SCDRP$L_SVA_USER(R5),R0 ; Get address of extended sense data EXTZV #SCSI$SNS$V_SENSE_KEY,- #SCSI$SNS$S_SENSE_KEY,- 2(R0),R1 ; Get sense key MOVZBL SCSI$SNS$B_ADD_SENSE_CODE(R0),R7 ; Get ASC MOVZBL SCSI$SNS$B_ADD_SENSE_QUAL(R0),R8 ; Get ASQ CMPB R1,- ; Unit attention sense key? #SCSI$C_UNIT_ATTENTION ; BNEQ 10$ ; Branch if not CMPB #SCSI$C_MEDIA_CHANGE,- ; Unit attention caused by media change? 12(R0) ; BNEQ 14$ 12$: BRW 40$ 14$: ;; BEQL 40$ ; Branch if so, don't log an error BBCS #UCB$V_FIRST_ATTN_SEEN,-; Branch if this is the first unit UCB$L_DK_FLAGS(R3),12$ ; attention seen. Don't log an error ; Log all recoverable errors for floppies. Since errors tend to grow on ; floppies, we want to alert the operator as soon as possible. 10$: BBS #UCB$V_FLOPPY,- ; Branch if this is a floppy, return UCB$L_DK_FLAGS(R3),15$ ; immediately CMPL R1,- ; Recovered error sense key? #SCSI$C_RECOVERED_ERROR ; BNEQ 15$ ; Branch if not CMPB #SCSI$C_RECOVERED_DATA,- ; Recovered error with read retries? 12(R0) ; BEQL 12$ ; Branch if so, dont log an error CMPB #^x18,- ; Recovered error with ecc or retries? 12(R0) ; BEQL 12$ ; Branch if so, dont log an error ; ; For CDROM devices, don't log commands that fail with a sense key of ; Illegal Command, if original QIO is a READ command (readvblk). ; This will prevent the driver from logging errors for reads ; of the home block on cdrom disks mounted foreign. ; 15$: BBC #UCB$V_CDROM,- ; Is this device a CD-ROM drive? UCB$L_DK_FLAGS(R3),19$ ; If NOT then proceed. CMPL R1,- ; Illegal command sense key #SCSI$C_ILLEGAL_REQUEST ; Returned for reads of audio tracks BNEQ 19$ ; Branch if not ; ; Look up the orginal commands SCDRP and determine whether or not this ; SCDRP is for a READPBLK QIO. If the command is then this is final condition ; in determining whether or not to log an error. ; PUSHL R0 MOVL SCDRP$L_SCDRP_SAV2(R5),R0 ; Get original command's SCDRP address CMPB #IO$_READPBLK,- ; Determine whether the original I/O SCDRP$W_FUNC(R0) ; was for a READ QIO request? BNEQ 17$ ; If not continue on, POPL R0 ; Otherwise, don't log this error BRB 40$ ; Branch if so, dont log an error ; ; Restore register R0 and continue on determining whether other errors ; need to be loggged. ; 17$: POPL R0 19$: PUSHL SCDRP$L_CMD_PTR(R5) ; Save address of request sense cmd MOVL SCDRP$L_SCDRP_SAV2(R5),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 ; Don't log errors with NOT_READY sense keys. CMPB R1,#SCSI$C_NOT_READY ; Device not ready sense key? BEQL 38$ ; Yes, don't log an error ; Don't log certain Illegal Request errors that can result from sending ; e.g. a 10-byte mode sense to a target that does not support it. CMPL R1,#SCSI$C_ILLEGAL_REQUEST ; Illegal command sense key BNEQ 36$ CMPL R8,#0 BNEQ 36$ CMPL R7,#^X20 BEQL 38$ ; Skip ASC/ASQ of 20/00 CMPL R7,#^X24 BEQL 38$ ; Skip ASC/ASQ of 24/00 36$: CMPL R1,#SCSI$C_ABORTED_COMMAND ; ABORTED command sense key BEQL 38$ ; BRANCH IF ABORTED COMMAND ; WITHOUT LOGGING ERROR LOG_ERROR - ; Log an extended sense data error TYPE=EXTND_SENSE_DATA,- ; VMS_STATUS=#SS$_NORMAL,-; UCB=R3 ; 38$: POPL SCDRP$L_CMD_PTR(R5) ; Restore address of request sense cmd 40$: POPL R8 ; Restore register POPL R7 ; Restore register 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. ; ; Certain situations cause an additional translation including: ; ; - Recoverable errors with read retries are considered acceptable. Thus, if ; this combination of sense key and additional sense code occurs, change the ; VMS status to SS$_NORMAL. ; ; - If the sense key was NOT_READY and the spinup-in-progress flag is set in ; the UCB, then this is an indication that the device is in the process of ; being spun up. Return a DEVOFFLINE status to allow the WAIT_UNIT_READY ; routine to continue to poll for spinup complete. Otherwise, change the ; status to SS$_MEDOFL to cause mount verification to occur for this device. ; In either case clear the CD_VALID bit to indicate any UCB stored media ; specific data is no longer valid. ; ; INPUTS: ; ; R5 - SCDRP address - of REQUEST SENSE SCDRP ; ; 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 EXTZV #0,#4,2(R2),R1 ; Get the sense key MOVL SCDRP$L_SCDRP_SAV2(R5),R0 ; Get original SCDRP MOVB R1,SCDRP$B_SENSE_KEY(R0) ; Save sense key in it ; If this is a UNIT ATTENTION sense key, a media change additional sense code, ; the device is NOT mounted, and the "spinup-in-progress" bit is set, then ; return success. This situation can occur if a piece of removable media, such ; as a floppy, is changed in between mounts. CMPB R1,- ; Unit attention sense key? #SCSI$C_UNIT_ATTENTION ; BNEQ 5$ ; Branch if not CMPB #SCSI$C_MODE_CHANGE,- ; Unit attention caused by mode select 12(R2) ; parameters change? BEQL 4$ ; Branch if so CMPB #SCSI$C_MEDIA_CHANGE,- ; Unit attention caused by media change? 12(R2) ; BNEQ 5$ ; Branch if not BBS #DEV$V_MNT,- ; Branch if device is mounted UCB$L_DEVCHAR(R3),5$ ; BBC #UCB$V_SPINUP_INPROG,- ; Branch if NOT waiting for unit ready UCB$L_DK_FLAGS(R3),5$ ; 3$: MOVZWL #SS$_NORMAL,R0 ; Set success status BRB 40$ ; Use common exit path 4$: EXTZV #IRP$V_FCODE,#IRP$S_FCODE,- ; Extract I/O function code SCDRP$W_FUNC(R0),R3 ; CMPL #IO$_PACKACK, R3 ; Was mode_change due to an IO$_PACKACK? BEQL 3$ ; If so, then unit attention was expected 5$: MOVAL SENSE_KEY_TABLE,R0 ; Get address of sense key to VMS status ; code translation table 10$: CMPB (R0),#^XFF ; End of table? BEQL 30$ ; 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 ; If this is a recoverable error due to read retries, then treat the error ; as success. CMPL R0,#SS$_RECOVERR ; Recoverable error? BNEQ 35$ ; Branch if not CMPB 12(R2),- ; Recoverable error with read retries? #SCSI$C_RECOVERED_DATA ; Beql 121$ ; Branch if so cmpb 12(r2),#^x18 ; Other recovered data? bneq 35$ ; br if not 121$: CMPB UCB$B_DEVTYPE(R3),- ; If this is an RZ55 then #DT$_RZ55 ; perform the required workaround. BNEQ 12$ ; If not a RZ55, continue. BSBW RZ55_WORKAROUND ; 12$: MOVZWL #SS$_NORMAL,R0 ; Treat this error as success ; The following code allows allows automatic spin-up of disks to work. If ; a NOT_READY SCSI status is returned by the drive, and the disk spinup is ; in progress, then return DEVOFFLINE status to notify the caller that the ; device is still not fully spun up. Otherwise, return MEDOFL status to ; cause mount verification to kick in. 35$: CMPL R0,#SS$_DEVOFFLINE ; Device offline status? BNEQ 40$ ; Branch if not BBS #UCB$V_SPINUP_INPROG,- ; Branch if spinup is in progress UCB$L_DK_FLAGS(R3),40$ ; 30$: MOVZWL #SS$_MEDOFL,R0 ; Otherwise, set medium offline status 40$: RSB ; Return to caller .SBTTL SAVE_ADDNL_INFO - Save additional extended sense information ;+ ; SAVE_ADDNL_INFO ; ; This routine is called whenever valid extended sense data is returned by a ; target to obtain any addition data. If a recoverable or non-recoverable error ; occurred, the additional data specifies the the failing LBN, which is saved ; in the UCB. Higher level routines (READPBLK, WRITEPBLK) use this information ; later to reassign a block if appropriate. ; ; The addition data is saved in the ORIGINAL SCDRP pointed to by ; SCDRP$L_SCDRP_SAV2(R5) (R5 has REQUEST_SENSE SCDRP). ; ; INPUTS: ; ; R3 - UCB address ; R5 - SCDRP address associated with request sense command ; ; SCDRP$L_SCDRP_SAV2(R5) - Address of original SCDRP ; ; OUTPUTS: ; ; R0,R1 - Destroyed ; ; SCDRP$B_ADDNL_INFO - Additional info field from the extended ; sense data or -1 if no valid data. ; UCB$B_SENSE_INFO - Balance of sense info returned by target. ; UCB$B_SENSE_LEN - Additional info length field set to number of ; additional sense data bytes. ;- .ENABLE LSB SAVE_ADDNL_INFO: MOVL SCDRP$L_SCDRP_SAV2(R5),R0 ; Get original SCDRP address MOVAL SCDRP$L_ADDNL_INFO(R0),R1 ; Get address of additional info field MOVL SCDRP$L_SVA_USER(R5),R0 ; Get address of request sense data MOVL #-1,(R1) ; Assume additonal info not valid TSTB (R0) ; Valid additional info? BGEQ 30$ ; Branch if not MOVAL 7(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 first four bytes of additional sense data, if the ; valid bit in byte 1 is set. This code segment copies the balanace of the ; additional sense data bytes returned by the target to the UCB. These ; additional bytes are used by the audio portion of the driver, but can be ; used for any device type. ;- 30$: PUSHL R7 ; 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 .DISABLE LSB .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 we allocate a new SCDRP from pool on ; the fly. ; ; 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_IRP - IRP 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$: PUSHR #^M ; Save regs MOVL #SCDRP$C_LENGTH,R1 ; Length of SCDRP ALLOC_STACK_SCDRP ; Get the SCDRP for a STACK BSBW ALLOC_POOL ; Go allocate an SCDRP FREE_STACK_SCDRP ; Give it back MOVW R1,SCDRP$W_SCDRPSIZE(R2); Save length of SCDRP MOVL R2,R5 ; Copy new SCDRP address POPR #^M ; Restore regs BRW 5$ BUG_CHECK INCONSTATE,FATAL ; Queue should never have been empty .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 ; R5 - UCB address (for _R5 entry point) ; 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, disconnect and ; synchronous flags, and device timeout values in the UCB. ; ; If the revision field in the inquiry data indicates the device is out-of-rev, ; then don't allow disconnects for this device. It's been shown that one of the ; hardest things to get right is the disconnect/reconnect logic. Thus, if the ; device is not up to rev there's a good chance this logic is broken and ; attempting to use the device with disconnects enabled could cause bus problems. ; ; INPUTS: ; ; R0 - Address of INQUIRY data ; R3 - UCB address ;- GET_DEVICE_TYPE: MOVAL SCSI_DEVICE_TABLE,R1 ; Get product ID translation table 10$: TSTB (R1)+ ; End of table BEQLW 40$ ; Branch if so CMPL (R1)+,16(R0) ; Product IDs match (low-order)? BEQL 20$ ; Branch if so ADDL #17,R1 ; Advance to next entry in table BRB 10$ ; Try next entry 20$: CMPL (R1)+,20(R0) ; Product IDs match (high-order)? BEQL 25$ ; Branch if so ADDL #13,R1 ; Advance to next entry in table BRB 10$ ; Try next entry in table 25$: MOVB -9(R1),UCB$B_DEVTYPE(R3); Found a match, extract device type BSBW CHECK_REV_LEVEL ; Check for an acceptable rev level .IF DEFINED V60_BUILD BBC #DEV$V_DTN,- ; If DTN bit is set, handle DDR UCB$L_DEVCHAR2(R3),27$ ; 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 .ENDC ; DEFINED V60_BUILD 27$: CHECK_FOR_RAID_DEVICE ; Set RAID bit if it's an Array. CHECK_FOR_CMDQ ; Set CMDQ bit if supported MOVL (R1)+,UCB$L_MEDIA_ID(R3); Save media ID PUSHL R0 ; Save pointer to INQUIRY data MOVB (R1)+,R0 ; Get disconnect, synchronous flags ASSUME UCB$V_DISCONNECT+1 EQ UCB$V_SYNCHRONOUS INSV R0,#UCB$V_DISCONNECT,- ; Initialize these flagse in the UCB #2,UCB$L_DK_FLAGS(R3) ; POPL R0 ; Restore pointer to INQUIRY data ASSUME UCB$W_PHASE_TMO+2 EQ UCB$W_DISC_TMO MOVL (R1),UCB$W_PHASE_TMO(R3) BBC #UCB$V_OUT_OF_REV,- ; Branch if the device is NOT out-of-rev UCB$L_DK_FLAGS(R3),30$ ; .IF DEFINED RZ74_CACHE CMPB #DT$_RZ74,UCB$B_DEVTYPE(R3) ; Is this the RZ74 disk drive? BEQL 30$ ; If so, don't disable disconnects .ENDC BICB #UCB$M_DISCONNECT,- ; If the device is out-of-rev, then UCB$L_DK_FLAGS(R3) ; don't allow disconnects 30$: .IF DEFINED V60_BUILD CMPB #DT$_GENERIC_DK,- ; Are we in DDR mode? UCB$B_DEVTYPE(R3) ; BEQL 32$ ; Yes BRW 35$ ; Nope ; ; 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. ; 32$: 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 .ENDC ; DEFINED V60_BUILD 35$: RSB ; Return to caller ; No matching product ID field was found in the table. Assume this is a generic ; SCSI disk. 40$: MOVAL GENERIC_SCSI_DISK,R1 ; Use generic SCSI disk type BRW 25$ ; .SBTTL CHECK_REV_LEVEL - Check for an out-of-rev device ;+ ; CHECK_REV_LEVEL ; ; This routine checks the revision level returned in the inquiry data with ; the minimum revision level stored in the SCSI_DEVICE_TABLE. If the device's ; revision level is below the minimum allowed, an invalid inquiry data error ; is logged. ; ; Note: since the revision field in the inquiry data is a 4 byte ascii string, ; the low-order byte is the most significant. Thus, the bytes must be compared ; from low byte to high byte, and a CMPL can not be used to perform the ; comparison. ; ; INPUTS: ; ; R0 - Address of inquiry data retured by target ; R1 - Address of minimum rev level field in SCSI_DEVICE_TABLE ; ; OUTPUTS: ; ; R1 - Advanced past revision level field ; All other registers preserved ; An error is logged of the device is out-of-rev ;- CHECK_REV_LEVEL: BICW #UCB$M_OUT_OF_REV,- ; Clear out-of-rev flag in the UCB UCB$L_DK_FLAGS(R3) ; PUSHQ R1 ; Save regs MOVAL 32(R0),R2 ; Address of rev level in inquiry data .REPT 4 ; Check four bytes of revision data CMPB (R2)+,(R1)+ ; Check a byte of revision BGTRU 10$ ; Branch if above minimum BLSSU 20$ ; Branch of below minimum .ENDR ; 10$: POPQ R1 ; Restore regs ADDL #4,R1 ; Advance beyond minimum rev field in table RSB ; Return to caller 20$: LOG_ERROR - ; Log an invalid inquiry data error TYPE=INV_INQUIRY_DATA,- ; VMS_STATUS=#SS$_NORMAL,-; UCB=R3 ; BISW #UCB$M_OUT_OF_REV,- ; Set out-of-rev flag in the UCB UCB$L_DK_FLAGS(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 DK_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 AUDIO_EXIT - Audio Function Completion Code ;+ ; AUDIO_EXIT - ; ; All audio functions that are not aborted during FDT processing exit the ; driver by executing this code. After a command has been sent, error ; recovery has been executed, SCSI status copied to the AUCB then deallocate ; the SCDRP and any other resources allocated for this request. The audio exit ; code is fairly complex due to the fact that we are double mapping and copying ; between three distinct user buffers. ; ; The original IRP (IRP pointed to by SCDRP$L_IRP) contains the mapping ; information for the AUCB. If a user destination or sense buffer is required ; then an IRP extension is allocated in the FDT code to contain the mapping ; information for these buffers. This routine will deallocated all the ; resources allocated for use in the original IRP and IRPE. ; ; ; INPUTS: ; R5 = SCDRP for this request. ; R3 = UCB ; R0 = VMS return status code ; ; ; OUTPUTS: ; AUCB = User Audio Control Block ; CD_COMMAND_STATUS = VMS O.S. Return status ; CD_SCSI_STATUS = SCSI command status and Sense Key ; CD_SENSE_ADDR = Sense data buffer ; CD_SENSE_TRANS_CNT = Sense data transfer count ; CD_DEST_BUF_TRANS_CNT = Number of bytes in destination buf ;- AUDIO_EXIT: .ENABLE LSB ; AUDIO_EXIT BSBW CLEANUP_CMD ; Cleanup from the SCSI command AUDIO_EXIT_NO_CMD: ;+ ; Now update the AUCB with the VMS Status, SCSI Status and Transfer Count. ;- PUSHR #^M ; Save some registers MOVL SCDRP$L_MEDIA(R5),R7 ; Get copy of AUCB address from SCDRP. BEQL 50$ ; Be sure it's valid MOVL SCDRP$L_TRANS_CNT(R5),- ; Get Transfer Count in Destination CD_DEST_BUF_TRANS_CNT(R7); XFER count field in the AUCB. TSTL SCDRP$L_STS_PTR(R5) ; Test for existance of status buffer. BEQL 10$ ; If no SCSI status buffer skip this.. MOVZBL @SCDRP$L_STS_PTR(R5),- ; Copy SCSI Status to AUCB. CD_SCSI_STATUS(R7) 10$: MOVZWL R0,CD_COMMAND_STATUS(R7); Copy VMS status to AUCB. ;+ ; If there was a check condition as the result of an audio command, copy ; the sense key and additional sense data to the AUCB from the UCB. ; During SEND_COMMAND, if a check condition occurs a request sense is ; issued to the target and the sense data is copied to the UCB. ;- ASSUME SCSI$C_GOOD EQ 0 TSTB CD_SCSI_STATUS(R7) ; Was status success? BLEQ 50$ ; If yes continue, else get sense data MOVZBW SCDRP$B_SENSE_KEY(R5),- ; Copy the Sense key to the AUCB. CD_SCSI_STATUS+2(R7) MOVL SCDRP$L_IRP(R5),R2 ; Get original IRP MOVL IRP$L_EXTEND(R2),R2 ; Get any IRP extension BEQL 50$ ; No, IRPE exit TSTL IRP$L_PID(R2) ; Test for Sense Buffer existance BEQL 50$ ; In no buffer don't copy sense data. PUSHR #^M MOVAB UCB$B_SENSE_INFO(R3),R0 ; Get address of additional info field MOVZBL UCB$B_SENSE_LEN(R3),R1 ; Get count of number of sense bytes MOVL CD_SENSE_CNT(R7),R5 ; Get size of user sense buffer MINUM R1,R5 ; Minimize between these two values MOVL R1,- ; Return Count of sense bytes to AUCB CD_SENSE_TRANS_CNT(R7) MOVC3 R1,(R0),- ; Copy SENSE Data to user buffer @IRP$L_SEGVBN(R2) ; SVA of SENSE buffer in SEGVBN. POPR #^M 50$: POPR #^M ; Restore saved registers ;+ ; Unlock any locked pages and free any allocated PTE's, then clear any fields ; in the IRP that might confuse IOPOST and return status in R0 and R1, which ; will be copied to the IOSB. ;- AUDIO_BAD_CMD_EXIT: PUSHR #^M MOVL SCDRP$L_IRP(R5),R2 ; Get original IRP address BSBW AUDIO_EXIT_FREE ; Unlock buffers, dealloc PTE's and IRPE MOVL SCDRP$L_IRP(R5),R2 ; Get original IRP address CLRL IRP$L_MEDIA(R2) ; Clear for IOPOST CLRL IRP$L_OBCNT(R2) ; ... CLRL IRP$L_SEGVBN(R2) ; ... POPR #^M ; Restore reg's CLRL R1 ; Clear R1, no additional info. BRW COMPLETE_IO ; Complete this I/O function .DISABLE LSB ; AUDIO_EXIT .SBTTL ALLOC_IRPE - Allocate IRP Extension. ;+ ; ALLOC_IRPE: ; ; This routine is called to allocated and link in an IRP extension (IRPE). ; IRP extensions are used in cases where a single IRP doesn't have enough ; space to maintain the entire state of a single I/O request. In this driver ; there are cases where a single request may have 1, 2 or 3 user buffers that ; must be made accessable to the startio routine. For this purpose we use an ; IRP extension. ; ; The original IRP will contain, the AUCB state. The IRPE will contain the ; state of destination buffers (TOC Data) and the Optional Sense Buffers. ; ; ; INPUTS: ; ; R3 - Original IRP ; Other registers have normal FDT context. ; ; OUTPUTS: ; R0 - Status ; R3 - Address of allocated IRPE. ; ; Original IRP field modified: ; IRPE$L_EXTEND - Assigned address of IRPE ; IRP$W_STS - IRP$M_EXTEND flag set ; ; Extended IRP field modified: ; IRP$L_SEQNUM - Address of original IRP ; IRPE$L_SVAPTE1 - Cleared ; IRPE$L_SVAPTE2 - Cleared ; ;- ALLOC_IRPE: .ENABLE LSB JSB G^EXE$ALLOCIRP ; Allocate extended IRP BLBC R0,50$ ; Exit if fails MOVL R2,IRP$L_EXTEND(R3) ; Link orig IRP to extended IRP. BISW #IRP$M_EXTEND,- ; Flag this as an extended IRP IRP$W_STS(R3) MOVW IRP$W_FUNC(R3),- ; Copy IO function code. IRP$W_FUNC(R2) MOVL R3,IRP$L_SEQNUM(R2) ; Save Original IRP Address MOVL R2,R3 ; Get IRPE address MOVW #,-; Signal to IOPOST that IRP$W_STS(R3) ; this is a Direct, Read, Physical I/O ;+ ; Clear the following fields in the IRPE, since the exit code in the audio ; path determines whether there are sense or destination buffers used for this ; I/O, based on whether or not these fields are zero. ;- CLRL IRPE$L_SVAPTE1(R3) ; Clear SVAPTE1 in IRPE CLRL IRPE$L_SVAPTE2(R3) ; Clear SVAPTE2 in IRPE CLRL IRP$L_PID(R3) ; If not clear SENSE buffer assumed! CLRL IRP$L_OBCNT(R3) ; If not zero destination buffer used. CLRL IRP$L_DIAGBUF(R3) ; Clear SVAPTE for IRPE(SENSE) buffer 50$: RSB .DISABLE LSB ; ALLOC_IRPE .SBTTL DEALLOC_IRPE - Deallocate an I/O request packet extension ;+ ; ; DEALLOC_IRPE - SUBROUTINE TO DEALLOCATE AN I/O REQUEST PACKET EXTENSION ; ; INPUTS: ; ; R2 = I/O REQUEST PACKET ADDRESS. ; ; OUTPUTS: ; ; THE I/O REQUEST PACKET EXTENSION IS DEALLOCATED TO NON-PAGED ; POOL. ; ; R0,R1,R2 ARE PRESERVED. ; ;- DEALLOC_IRPE: ; DEALLOCATE AN IRPE PUSHR #^M MOVL IRP$L_EXTEND(R2),R0 ; GET IRPE ADDRESS BEQL 20$ ; BR IF NONE CLRL IRP$L_EXTEND(R2) ; Delete extended IRPE pointer. BICW #IRP$M_EXTEND,- ; CLEAR EXTEND FLAG IRP$W_STS(R2) JSB G^EXE$DEANONPAGED ; DEALLOCATE IRPE 20$: POPR #^M ; RESTORE REGISTERS RSB ; DEALLOC_IRPE .SBTTL SETUP_CMD - Common setup for all SCSI commands ;+ ; SETUP_CMD ; ; This routine common setup prior to the sending of a SCSI command. This ; includes allocating a command buffer, filling in the pointers in the SCDRP ; to the command and status fields, copying the SCSI command to the command ; buffer, allocating an S0 "user" buffer if the command requires transferring ; data to or from the class driver, filling in the SCDRP fields used to map ; this buffer, and mapping the buffer. ; ; Since this routine calls SPI$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 UCB$W_PHASE_TMO(R3),- ; Fill in phase change and disconnect SCDRP$L_DMA_TIMEOUT(R5) ; timeout fields in the SCDRP MOVZWL UCB$W_DISC_TMO(R3),- ; SCDRP$L_DISCON_TIMEOUT(R5) CVTWL (R2)+,R1 ; Get length of send data buffer BLSS 20$ ; Branch if negative, no system buffer ; involved, leave SCDRP$L_BCNT unchanged 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,R1 ; Copy buffer address SUBPOP R2 ; Restore R2 MOVL R1,SCDRP$L_SVA_USER(R5) ; Save address of allocated buffer 18$: 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 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 SPI$DEALLOCATE_COMMAND_BUFFER ; Deallocate the command buffer 30$: POPR #^M ; Restore regs RSB .SBTTL SETUP_SENSE_BUFFER - Setup user sense data buffer ;+ ; SETUP_SENSE_BUFFER: ; ; This routine is called to allocate an IRPE and prepare the user supplied ; sense data buffer. The sense data buffer will be locked down and double ; mapped. The mapping information for this buffer will be saved in the IRPE. ; ; INPUTS: ; ; R3 - Original IRP ; Other registers have normal FDT context. ; ; OUTPUTS: ; R0 - Status ;- SETUP_SENSE_BUFFER: .ENABLE LSB BSBW ALLOC_IRPE ; Allocate an IRP extension BLBC R0,90$ ; Exit if no IRPE ;+ ; Now, lockdown and double map the users sense buffer, then save the ; the mapping information for this buffer in a "special" place in the IRPE. ;- ASSUME CD_SENSE_ADDR+4 EQ CD_SENSE_CNT MOVL (AP),R0 ; Get AUCB pointer MOVQ CD_SENSE_ADDR(R0),R0 ; Get address and size of SENSE buffer. MOVL R1,IRP$L_BCNT(R3) ; Copy Byte count to IRPE BICW3 #^C,R0,- ; Get BOFF for Destination buffer. IRP$W_BOFF(R3) BSBW AUDIO_MAP_PAGE ; Locks and Maps AUCB. BLBC R0,90$ ;+ ; Since this IRPE may be used to map two buffers, copy the SENSE buffer ; information into another place in the IRPE. ; ; IRPE$L_PID - The SVA of the first PTE that maps the Sense buffer. ; IRPE$L_AST - The number of PTE's allocated to double map the Sense buffer. ; IRPE$L_ASTPRM - The SVA of the user buffer to copy the SENSE data to. ; IRPE$L_DIAGBUF- The SVA of P0 PTE that maps first page of sense buffer. ;- MOVL IRP$L_WIND(R3),- ; Save SVAPTE of system buffer IRP$L_PID(R3) ; Not really the PID field! MOVL IRP$L_OBCNT(R3),- ; Save number of PTE's allocated IRP$L_AST(R3) ; Not really the AST field MOVL IRP$L_SVAPTE(R3),- ; Copy SVAPTE created by exe$writelock IRP$L_DIAGBUF(R3) MOVL IRP$L_SEGVBN(R3),- ; SVA User buffer saved here IRP$L_ASTPRM(R3) ; Not really the AST param field CLRL IRP$L_WIND(R3) ; Clear IRP WIND field CLRL IRP$L_SVAPTE(R3) ; Clear invalid svapte info. CLRL IRP$L_OBCNT(R3) ; Clear what will be dest page count 90$: MOVL IRP$L_SEQNUM(R3),R3 ; Restore Original IRP Address RSB .DISABLE LSB ; SETUP_SENSE_BUFFER .SBTTL AUDIO_MAP_BUFFER - Lock down and double map user buffer ;+ ; AUDIO_MAP_PAGE: ; ; This routine locks the users buffer down, allocates the PTE's required to ; double map the user buffer and saves this mapping information in the IRP. ; The STARTIO routine will use the information in the IRP to move data to ; or from the users buffer. ; ; INPUTS: ; ; R0 - P0 address of buffer ; R1 - Byte count ; R3 - IRP ; Other registers have normal FDT context. ; ; OUTPUTS: ; ; IRP$L_WIND - SVAPTE of mapped buffer. ; IRP$L_OBCNT - Number of PTE's allocated. ; IRP$L_SEGVBN - The SVA of the first PTE that maps the Sense buffer. ; ; R0 - Status ; All other registers preserved ;- AUDIO_MAP_PAGE: .ENABLE LSB JSB G^EXE$MODIFYLOCKR ; Lock pages and return BLBS R0,10$ ; Success, continue. BRW LOCK_ERROR ; Exit, if fail to lock user buffer. ;+ ; To check to see how many PTE's to allocate, ADD BOFF and BCNT if > 512 add one to page count. ; do a sobgtr to loop through and copy all the pte's ;- 10$: MOVL R1,R11 ; Save the SVAPTE for the AUCB MOVL IRP$L_BCNT(R3),R9 ; get requested transfer byte count MOVZWL IRP$W_BOFF(R3),R10 ; get byte offset in page MOVAB 511(R9)[R10],R9 ; combine offset and count and round ASHL #-VA$S_BYTE,R9,R2 ; Get number of PTE's needed. JSB G^LDR$ALLOC_PT ; Allocate the SPT's BLBS R0,15$ ; Success, then continue. BRW MAP_ERROR ; Exit, if can't map buffer. 15$: MOVL R1,IRP$L_WIND(R3) ; Save SVAPTE of system buffer MOVL R2,IRP$L_OBCNT(R3) ; Save number of PTE's allocated ;+ ; Now convert the SPTE address to a SVA address so that the startio routine ; can access the AUCB via this address. ;- MOVL R1,R10 ; Save the SVAPTE SUBL2 G^MMG$GL_SPTBASE,R1 ; Get offset into page table ASHL #,R1,R9 ; Save the S0 virtual address BISW IRP$W_BOFF(R3),R9 ; Add in byte offset of AUCB. BISL #VA$M_SYSTEM,R9 ; Set 80000000 bit for system address MOVL R9,IRP$L_SEGVBN(R3) ; SVA User buffer saved here ;+ ; Now loop through all of the PTE's required to map the buffer and copy the ; the PTE's from user page tables into the system page tables. ; ; Remember that R11 has the SVAPTE for the AUCB. ;- PUSHL R3 ; Save R3 COPY_PTE: MOVL (R11)+,R3 ; Copy buffers PTE's to R3. BLSS 60$ ; Valid PTE if LSS, otherwise NOT valid. JSB G^IOC$PTETOPFN ; Create vaild PTE from invalid PTE. 60$: INVALIDATE_TB R9,ENVIRON=LOCAL- ; Invalidate this address in the TB. INST1=,(R10)>,- INST2= ; Set VALID, KERNEL WRITE KERNEL MODE ; into the PTE. ; Set entry in SPT MOVAB 512(R9),R9 ; Point to next page ADDL2 #4,R10 ; Point to next entry in SPT SOBGTR R2,COPY_PTE ; Copy all required PTE's POPL R3 ; Restore R3 RSB ; AUDIO_MAP_PAGE ;+ ; An error occured in EXE$MODIFYLOCKR, we are called here as a coroutine. ; ; we need to clean up all allocated resources and rsb back to EXE$MODIFYLOCKR, ; which will unwind the stack, fail the I/O. Control DOES NOT return ; back to this routine (AUDIO_MAP_PAGE) ; ;- LOCK_ERROR: PUSHQ R0 ; Must preserve R0, R1 TSTL IRP$L_SEQNUM(R3) ; Is this the IRPE? BGEQ 100$ ; Nope, this is the original IRP MOVL IRP$L_SEQNUM(R3),R3 ; Restore original IRP 100$: PUSHL R2 ; Save R2 MOVL R3,R2 ; For AUDIO_EXIT_FREE JSB AUDIO_EXIT_FREE ; Free resources POPL R2 ; Restore R2 POPQ R0 ; Restore R0,R1 RSB ; Return to Coroutine, fail I/O, ; and exit FDT ;+ ; Error occured while trying to double map a user buffer, unlock the buffer ; that we just locked down and return to caller. ; ; R2 = Number of pages to unlock. ; R3 = IRP Address ;- MAP_ERROR: PUSHL R3 MOVL IRP$L_SVAPTE(R3),R3 ; Get SVAPTE for IRPE(Dest) buffer BEQL 150$ ; No buffer to unlock. MOVL R2,R1 ; Get number of pages locked down. BEQL 150$ ; No buffer to unlock PUSHL R0 JSB G^MMG$UNLOCK ; Unlock those pages. POPL R0 150$: POPL R3 BRB LOCK_ERROR .DISABLE LSB ; AUDIO_MAP_PAGE .SBTTL AUDIO_EXIT_FREE - Audio exit resource deallocation. ;+ ; AUDIO_EXIT_FREE - ; ; Given the original IRP address deallocate any allocated system resources. ; Unlock user buffers and the release PTE's ; ; INPUTS: ; R2 - Original IRP Address ; ; OUTPUTS: ; TBD ;- AUDIO_EXIT_FREE: .ENABLE LSB PUSHR #^M ;+ ; If there is an IRP extension in use then, unlock any locked pages. ;- PUSHL R2 ; Save original IRP address 30$: BBC #IRP$V_EXTEND,- ; Is this an extended IRP? IRP$W_STS(R2),REL_PTE ; If not exit, continue.. ;+ ; Since there was an IRP extension, unlock any pages that we locked down ; for use by the IRPE in startio. First unlock the destination buffer, then ; unlock the sense data buffer if they exist. ; ; IRP$L_WIND(R2) = S0 SVAPTE for IRPE Destination (user) buffer ; IRP$L_SVAPTE(R2) = P0 SVAPTE for IRPE Destination (user) buffer ; IRP$L_OBCNT(R2) = Number of pages locked down. ;- MOVL IRP$L_EXTEND(R2),R2 ; Get IRPE Address MOVL IRP$L_SVAPTE(R2),R3 ; Get SVAPTE for IRPE(Dest) buffer BEQL NO_DEST_BUF ; No destintation buffer to unlock. MOVL IRP$L_OBCNT(R2),R1 ; Get number of pages locked down. BEQL NO_DEST_BUF ; No SENSE buffer to unlock JSB G^MMG$UNLOCK ; Unlock those pages. ;+ ; Unlock Sense buffer pages, if in use. Use information in IRPE. ; ; IRP(E)$_PID = S0 SVAPTE Sense Buffer ; IRP(E)$_DIAGBUF = P0 SVAPTE Sense Buffer ; IRP(E)$_AST = Number of pages/pte's ; IRP(E)$_ASTPRM = SVA of Sense Buffer in S0. ;- NO_DEST_BUF: MOVL (SP),R2 ; "Restore" R2 MOVL IRP$L_EXTEND(R2),R2 ; Get IRPE Address MOVL IRP$L_DIAGBUF(R2),R3 ; Get SVAPTE for IRPE(SENSE) buffer BEQL REL_PTE ; No SENSE buffer to unlock MOVL IRP$L_AST(R2),R1 ; Get number of pages locked down. BEQL REL_PTE ; No SENSE buffer to unlock JSB G^MMG$UNLOCK ; Unlock those pages. ;+ ; After unlocking Destination and Sense Buffer deallocated PTE's ;- REL_PTE: POPL R2 ; Restore Original IRP Address BSBW AUDIO_FREE_PTES ; R2 = IRP, free allocated PTE's ;+ ; If there was an IRPE, now deallocate it before starting IOPOST. ;- BBC #IRP$V_EXTEND,- ; Is this an extended IRP? IRP$W_STS(R2),DONE_FREE; If not exit, continue.. BSBW DEALLOC_IRPE ; Deallocated IRPE, if the exist. ;+ ; Later Start Autosense if check condition. ;- DONE_FREE: POPR #^M RSB ; AUDIO_EXIT_FREE .DISABLE LSB ; AUDIO_EXIT_FREE .SBTTL AUDIO_FREE_PTES - Free any allocated PTE's ;+ ; AUDIO_FREE_PTES: ; ; Routine used by AUDIO code to free the PTE's allocated for double mapping. ; This routine is called to deallocated all of the PTE's that could possibly ; be in use by the original IRP or the IRP extension. ; ; INPUTS: ; R2 = IRP ; IRP$L_WIND = SVAPTE of PTE's for the AUCB ; IRP$L_OBCNT = Number of PTE's for the AUCB ; IRP$L_EXTEND = Valid if IRPE used. ; IRP(E)$L_WIND = SVAPTE of PTE's for Destination Buffer ; IRP(E)$L_OBCNT = Number of PTE's for Destination Buffer ; IRP(E)$L_PID = SVAPTE of Sense Buffer PTE's ; IRP(E)$L_AST = Number of PTE's for Sense Buffer ; ; OUTPUTS: ; R0 = Return status. ; ; R0,R1 Modified ;- AUDIO_FREE_PTES: .ENABLE LSB PUSHL R2 ; Save IRP address MOVL IRP$L_WIND(R2),R1 ; Get Address of PTE's Allocated BEQL OTHER_PTE ; If none allocated, check for others MOVL IRP$L_OBCNT(R2),R0 ; Get number of PTE's. BEQL OTHER_PTE ; If none allocated, check for others ;+ ; Before deallocating AUCB PTE's they must be zeroed. ;- PUSHL R9 MOVL IRP$L_SEGVBN(R2),R9 ; Get SVA of AUCB as saved by FDT call AUCB_PTE: INVALIDATE_TB R9,ENVIRON=LOCAL- ; Invalidate this address in the TB. INST1= MOVAB 512(R9),R9 ; Point to next page SOBGTR R0,AUCB_PTE ; Loop through all PTE's POPL R9 MOVL (SP),R2 ; "Restore" R2 MOVL IRP$L_WIND(R2),R1 ; Get Address of PTE's Allocated MOVL IRP$L_OBCNT(R2),R2 ; Get number of PTE's. JSB G^LDR$DEALLOC_PT ; Free the allocated PTE's BLBS R0,OTHER_PTE ; If success continue, otherwise BUG_CHECK INCONSTATE,FATAL ; BUGCHECK, this can never happen... ;+ ; Check for PTE's allocated for the SENSE buffer. But first we must be sure that ; this is an IRP extension, since only IRPE have additional PTE's allocated. ;- OTHER_PTE: MOVL (SP),R2 ; "Restore" R2 BBS #IRP$V_EXTEND,- ; Is this an extended IRP? IRP$W_STS(R2),20$ ; If so, continue. BRW FREE_DONE ; If not exit, otherwise continue.. 20$: MOVL IRP$L_EXTEND(R2),R2 ; Get IRPE Address MOVL IRP$L_PID(R2),R1 ; Get Address of PTE's allocated BEQL DEST_PTE ; If none allocated, check for others MOVL IRP$L_AST(R2),R0 ; Get number of PTE's. BEQL DEST_PTE ; If none allocated, check for others ;+ ; Before deallocating Sense buffer PTE's they must be zeroed and invalidated ;- PUSHL R9 MOVL IRP$L_ASTPRM(R2),R9 ; Get SVA of Sense Buf from FDT call SENSE_PTE: INVALIDATE_TB R9,ENVIRON=LOCAL- ; Invalidate this address in the TB. INST1= MOVAB 512(R9),R9 ; Point to next page SOBGTR R0,SENSE_PTE ; Loop through all PTE's POPL R9 MOVL (SP),R2 ; "Restore" R2 MOVL IRP$L_EXTEND(R2),R2 ; Get IRPE Address MOVL IRP$L_PID(R2),R1 ; Get Address of PTE's Allocated MOVL IRP$L_AST(R2),R2 ; Get number of PTE's. JSB G^LDR$DEALLOC_PT ; Free the allocated PTE's BLBS R0,DEST_PTE ; If success continue, otherwise BUG_CHECK INCONSTATE,FATAL ; BUGCHECK, this can never happen... ;+ ; Check for PTE's allocated for the Destination buffer. If allocated then ; deallocate them. ;- DEST_PTE: MOVL (SP),R2 ; "Restore" R2 MOVL IRP$L_EXTEND(R2),R2 ; Get IRPE Address MOVL IRP$L_WIND(R2),R1 ; Get Address of PTE's Allocated BEQL FREE_DONE ; If none allocated, check for others MOVL IRP$L_OBCNT(R2),R0 ; Get number of PTE's. BEQL FREE_DONE ; If none allocated, check for others ;+ ; Before deallocating Destination buffer PTE's they must be zeroed. ;- PUSHL R9 MOVL IRP$L_SEGVBN(R2),R9 ; Get SVA of Dest Buf from FDT call 50$: INVALIDATE_TB R9,ENVIRON=LOCAL- ; Invalidate this address in the TB. INST1= MOVAB 512(R9),R9 ; Point to next page SOBGTR R0,50$ ; Loop through all PTE's POPL R9 MOVL (SP),R2 ; "Restore" R2 MOVL IRP$L_EXTEND(R2),R2 ; Get IRPE Address MOVL IRP$L_WIND(R2),R1 ; Get Address of PTE's Allocated MOVL IRP$L_OBCNT(R2),R2 ; Get number of PTE's. JSB G^LDR$DEALLOC_PT ; Free the allocated PTE's BLBS R0,FREE_DONE ; If success continue, otherwise BUG_CHECK INCONSTATE,FATAL ; BUGCHECK, this can never happen... FREE_DONE: MOVL #SS$_NORMAL,R0 ; Assume success ERROR_DONE: POPL R2 ; Restore IRP address RSB .DISABLE LSB ; AUDIO_FREE_PTES .SBTTL LOG_ERROR - Write an entry to the errorlog file ;+ ; LOG_ERROR ; ; This routine writes an entry to the errorlog file. If the device is offline, ; no error is logged. This prevents the errorlog file from being filled up while ; the class driver does it its periodic polling of devices that have been set ; offline. The assumption is that the initial error that caused the device to ; be placed offline has been logged and that subsequent errorlog entries would ; be redundent. ; ; If the device class and/or type fields in the UCB have not been initialized, ; then fill in "DISK" and "GENERIC SCSI DISK" respectively so the packet can be ; properly formatted by ERF. This situation could arise if invalid inquiry data ; is received for the device, preventing these fields from being filled in ; properly. ; ; If no I/O is active for this device (for example, if an error is detected ; during unit initialization) log a device attention. Otherwise, log a ; device error and then release the errorlog entry. This prevents errors from ; being lost if multiple errors are logged for a single QIO. ; ; Some type of errors are logged just oncs 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 errorlogging is disabled UCB$L_DK_FLAGS(R5),40$ ; for this device 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 MOVB UCB$B_DEVTYPE(R5),R9 ; Save SCSI device type BNEQ 10$ ; Branch if device type known MOVB #DT$_GENERIC_DK,- ; Fill in generic type so ERF can UCB$B_DEVTYPE(R5) ; translate the errlog packet 10$: MOVB UCB$B_DEVCLASS(R5),R10 ; Save DEVCLASS field MOVB #DC$_DISK,- ; Temporarily set this field to a disk UCB$B_DEVCLASS(R5) ; so ERF can translate packet MOVL UCB$L_DDT(R5),R0 ; Get DDT address MOVW SCSI_ERROR_LEN_TAB-2[R7],-; Save required errorlog packet size DDT$W_ERRORBUF(R0) ; in the DDT TSTL UCB$L_QUEUED_IO_COUNT(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 POPR #^M ; Restore regs 40$: RSB ; Return to caller .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 = 11 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,(R2) ; Set argument count in buffer SPI$GET_CONNECTION_CHAR ; Get current connection characteristics BLBC R0,10$ ; Branch on error EXTZV #UCB$V_DISCONNECT,#1,- ; Fill in disconnect flag UCB$L_DK_FLAGS(R3),4(R2); EXTZV #UCB$V_SYNCHRONOUS,#1,- ; Fill in synchronous flag UCB$L_DK_FLAGS(R3),8(R2); CLRL ^X2C(R2) ; Clear FLAGS field IF_NOT_CMDQ 1$ ; Br if no CMDQ support BISL #1,^X2C(R2) ; Yes, set CMDQ char bit ; We leave FLUSHQ zero since we ; Resume queues on error MOVW UCB$L_QDEPTH(R3),^X2E(R2) ; Set max queue depth 1$: CMPB #2,UCB$B_SCSI_VERSION(R3) ; Are we at least SCSI-2? BGTR 2$ ; Nope BISL #4,^X2C(R2) ; Yes, set SCSI_2 char bit 2$: 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 .SBTTL SAVE_CONN_CHAR - Save connection characteristics ;+ ; SAVE_CONN_CHAR ; ; This routine saves the connection characteristics into a storage area ; allocated by this routine, and pointed to by the field UCB$L_SAVE_CONN_CHAR. ; The reason behind doing this is to save the device's characteristics prior ; to performing an operation that would change the device characteristics from ; what a set mode operation does. ; ; An example of such an operation is mixed-mode media support of CDROMS. ; ; INPUTS: ; R2 - IRP address ; R3 - UCB address ; R4 - PDT address ; R5 - SCDRP address ; ; OUTPUTS: ; ; R0 - Status ; R1,R2 - Destroyed ; All other registers preserved ; ;- SAVE_CONN_CHAR: ; Add code to save connection characteristics that may be changed from ; the functionality that is being performed. This code is specifically ; being added to support mixed mode audio/data disks, but can be used ; for other processing as well. MOVL UCB$L_SAVE_CONN_CHAR(R3),R2 ; Pick up the connection char buffer BNEQ 10$ ; Branch if we do indeed have one ; If we do not have one for this UCB, ; then allocate a permanent one MOVL #<*4>,R1 ; Size of get/set conn char buffer BSBW ALLOC_POOL ; Allocate the buffer MOVL R2,- ; Save the location into our storage UCB$L_SAVE_CONN_CHAR(R3); area in the UCB 10$: MOVL #NUM_ARGS,(R2) ; Set argument count in buffer SPI$GET_CONNECTION_CHAR ; Get current connection characteristics RSB .SBTTL RESTORE_CONN_CHAR - Restore connection characteristics ;+ ; RESTORE_CONN_CHAR ; ; The objective of this routine is to restore the connection characteristics ; save by a previous call to the SAVE_CONNECT_CHAR routine. ; ; INPUTS: ; R3 - UCB address ; R4 - PDT address ; R5 - SCDRP address ; ; OUTPUTS: ; ; R0 - Status ; R1,R2 - Destroyed ; All other registers preserved ;- RESTORE_CONN_CHAR: MOVL UCB$L_SAVE_CONN_CHAR(R3),- R2 ; Restore Address of Connection Buffer SPI$SET_CONNECTION_CHAR ; Set the connection characteristics ; Return status back from this routine RSB .SBTTL ATTEMPT_REORDER - Attempt to reorder read/write requests for .SBTTL - seek optimization ;+ ; ATTEMPT_REORDER ; ; This routine is called at the end of STARTIO to scan the pending queue in ; the UCB and determine the most more optimal request to service next. ; An elevator algorithm is used. Thus, the disk head moves in one direction, ; servicing all requests in that direction until no more exist. The CUR_LBN ; field keeps track of the current location of the heads, while the SEEK_DIR ; bit specifies the current direction of motion. An assumption here is that ; the disk is laid out in LBN order. ; ; Note that it's OK to reorder read and write operations in the pending ; queue, but not most other operations, such as PACKACKs. Therefore, scan ; of the pending queue stops if a non-read or -write operation is encountered. ; Also, reordering is not performed if the disk is currently in mount ; verification. ; ; Register usage within this routine: ; ; R0 - Contains the function code of each successive IRP in ; order to check that the function is a read or write ; R1 - Bitmap used to determine whether an IRP in the pending ; queue is "better" than the "best" IRP found so far ; R2 - Address of pending I/O queue listehead in UCB ; R3 - Address of the "best" IRP found at any point ; R4 - Signed displacement from the head position to the LBN of the ; "best" IRP found so far ; R5 - UCB address ; R6 - Address of each successive IRP in pending queue ; being considered as a posible optimal IRP ; R7 - Signed displacement from the heads to the LBN of each ; successive IRP in pending queue as the list is scanned ; R8 - Limit to the number of IRPs in the pending queue that ; get scanned ; INPUTS: ; ; R2 - Address of I/O pending queue listhead in UCB ; R5 - UCB address ; ; OUTPUTS: ; ; R0-R4 - Destroyed ; All other registers preserved ; Pending queue in UCB is sorted, with most appropriate IRP for current ; head position at front of queue ;- MAX_IRPS_SCANNED = 12 CANT_REORDER: RSB ATTEMPT_REORDER: ; If mount verification is in progress, don't perform any reordering. BBS #UCB$V_MNTVERIP,- ; Branch if mount verification is UCB$L_STS(R5),- ; in progress, can't reorder CANT_REORDER ; ; Check that the active IRP is either a read or write function. If not, ; we can't reorder. The following code sequence increments the function code, ; and clears the low-order bit so that both IO$_READPBLK and IO$_WRITEPBLK ; get converted to IO$_READPBLK, reducing the number of compare operations ; needed to determine whether the function is a read or write. MOVL (R2),R3 ; Get first IRP in pending queue ASSUME IRP$S_FCODE LE 7 ; Allow byte compare ASSUME IO$_WRITEPBLK EQ <^B1011> ASSUME IO$_READPBLK EQ <^B1100> ADDB3 #1,IRP$W_FUNC(R3),R0 ; Increment function code BICB #<^C>+1,R0 ; Extract function code and clear LSB CMPB R0,#IO$_READPBLK ; Read or write function? BNEQ CANT_REORDER ; Branch if not, can't reorder PUSHQ R6 ; Save regs PUSHL R8 ; MOVL R2,R6 ; Copy pending queue listhead address MOVZBL #MAX_IRPS_SCANNED,R8 ; Set limit on number of IRPs scanned SUBL3 UCB$L_CUR_LBN(R5),- ; Assume LBN of active IRP is directly IRP$L_MEDIA(R3),R4 ; under heads (ideal displacement) BNEQ NEXT_IRP ; Branch if not, continue scan BRW SCAN_DONE ; Otherwise, no need to continue scan SCAN_LOOP: ASSUME IRP$S_FCODE LE 7 ; Allow byte compare ASSUME IO$_WRITEPBLK EQ <^B1011> ASSUME IO$_READPBLK EQ <^B1100> ADDB3 #1,IRP$W_FUNC(R6),R0 ; Increment function code BICB #<^C>+1,R0 ; Extract function code and clear LSB CMPB R0,#IO$_READPBLK ; Read or write function? BNEQ SCAN_DONE ; Branch if not, stop scan ; Here we build a bitmap in R1 to indicate the following: ; ; Bit 3 - Set if the heads are moving towards higher LBNs ; Bit 2 - Set if the optimal I/O found so far is requesting a ; higher LBN than the current head position ; Bit 1 - Set if the pending I/O currently being considered ; is requesting a higher LBN than the current head position ; Bit 0 - Set if the pending I/O currently being considered is ; requesting a higher LBN than the optimal I/O found so far ; ; We can then use this bitmap to dispatch to the appropriate code to ; either continue to use the optimal IRP or to replace it with the ; IRP in the pending queue. 5$: MOVZBL UCB$B_SEEK_DIR(R5),R1 ; Set/clear seek direction bit in R1 10$: TSTL R4 ; Is optimal I/O LBN > head position? BLSS 20$ ; Branch if not ADDL #4,R1 ; Indicate optimal I/O LBN > head position 20$: SUBL3 UCB$L_CUR_LBN(R5),- ; Calculate displacement to pending I/O IRP$L_MEDIA(R6),R7 ; BEQL PERFECT_IRP ; Found the perfect I/O, get out now BLSS 30$ ; Branch if pending I/O LBN < head position ADDL #2,R1 ; Indicate pending I/O LBN > head position 30$: CMPL IRP$L_MEDIA(R6),- ; Compare LBNs of optimal and pending IRP$L_MEDIA(R3) ; I/Os BLEQ 40$ ; Branch if pending <= optimal INCL R1 ; Indicate pending > optimal 40$: DISPATCH R1,TYPE=B,<- ; Dispatch based on the state in R1 <0, NEXT_IRP>,- ; to either keep the current optimal <1, SWAP_IRP>,- ; IRP or replace it with one from the <3, NEXT_IRP>,- ; pending queue <4, SWAP_IRP>,- <6, SWAP_IRP>,- <7, NEXT_IRP>,- <8, NEXT_IRP>,- <9, SWAP_IRP>,- <11, SWAP_IRP>,- <12, NEXT_IRP>,- <14, SWAP_IRP>,- <15, NEXT_IRP>> BUG_CHECK INCONSTATE,FATAL ; This should never happen ; The IRP in the pending queue was found to be closer to the heads than ; the optimal one. Therefore replace the optimal IRP with the one from ; the pending queue. SWAP_IRP: MOVQ R6,R3 ; Replace the optimal IRP with the ; one from the pending queue REMQUE (R3),R3 ; Remove IRP from current position in INSQUE (R3),UCB$L_IOQFL(R5) ; I/O queue and place at front of queue SUBL3 UCB$L_CUR_LBN(R5),- ; Update distance from current to optim IRP$L_MEDIA(R3),R4 NEXT_IRP: MOVL (R6),R6 ; Get address of next IRP in list CMPL R6,R2 ; End of list? BEQL SCAN_DONE ; Branch if so SOBGTR R8,SCAN_LOOP ; Branch if more IRPs are allowed to be SCAN_DONE: ; R4 contains the signed displacement between the current head position and ; the LBN of the IRP that we've chosen to execute. Change the SEEK_DIR ; based on the which direction the heads will now be moving. If the LBN of ; the current IRP is directly under the heads, continue to scan in the same ; direction. INCL UCB$L_FAIRNESS_CNT(R5) ; Assume perfect IRP TSTL R4 ; Time to change seek direction? BEQL SCAN_RETURN ; Branch if not BGTR 10$ ; CLRB UCB$B_SEEK_DIR(R5) ; Start motion head towards lower LBNs BRB SCAN_RETURN ; Use common exit 10$: MOVB #8,UCB$B_SEEK_DIR(R5) ; Start motion towards higher LBNs 20$: CLRL UCB$L_FAIRNESS_CNT(R5) ; Indicate this is a new LBN SCAN_RETURN: POPL R8 ; Restore registers POPQ R6 ; RSB ; Return to caller ; While scanning the pending queue, an IRP was found which is requesting ; the LBN at the current head position. No need to scan any longer. Choose ; this IRP as the optimal one and get out. PERFECT_IRP: CMPL #FAIRNESS_CNT,- ; Is this LBN monopolizing service? UCB$L_FAIRNESS_CNT(R5) BLSS NEXT_IRP ; If so, don't choose this IRP after all REMQUE (R6),R3 ; Remove IRP from current position in INSQUE (R3),UCB$L_IOQFL(R5) ; I/O queue and place at front of queue SUBL3 UCB$L_CUR_LBN(R5),- ; Update distance from current to - IRP$L_MEDIA(R3),R4 ; optimal. BRB SCAN_DONE .SBTTL CHECK_HBS - Determine if the device supports host-based .SBTTL - shadowing ;+ ; CHECK_HBS ; ; This routine is called during the first PACKACK function to each unit to ; determine if the device is capable of supporting host-based shadowing. The ; criteria is that the device must allow the forcing of bad blocks using ; the read long / write long SCSI commands. This routine performs a series ; of read longs with varying length fields trying to find one that succeeds. ; The problem here is that these commands are both optional and vendor-unique. ; If a read long command succeeds, the length used for the command is saved ; in the UCB so it can be used for future read long / write long commands. ; If no read long commands succeed, then the device is marked as not supporting ; host-based shadowing. ; ; INPUTS: ; ; R3 - UCB address ; R4 - SPDT address ; R5 - SCDRP address ; ; OUTPUTS: ; ; R0 - SS$_NORMAL ; R1-R2 - Destroyed ; All other registers preserved ; ; UCB$L_ - UCB$V_ set if device supports host-based ; shadowing ; UCB$W_READL_LEN - Data length to use with future read long / write long ; commands ;- CHECK_HBS: SUBSAVE ; Save return address ; ; Assume failure. ; BISL #DEV$M_NOFE,- ; Drive does not support host based UCB$L_DEVCHAR2(R3) ; shadowing. (No Forced Error) BBS #DEV$V_SWL,- ; Branch if device is software UCB$L_DEVCHAR(R3),25$ ; write locked. Can't support HBS. MOVAL CMD_READ_LONG,R2 ; Address of read long command BSBW SETUP_CMD ; Perform setup for SCSI command ADDL3 #<2+4>,- ; Address of logical block address SCDRP$L_CMD_PTR(R5),R1 ; field in SCSI command CLRL (R1) ; Attempt a read long on block 0 ; Since the read long command is vendor-unique, the transfer length field ; is not not known apriori. Instead, we must send the device a number of ; read long commands with varying length fields until the correct value is ; determined. The current values we try are: 0, 1, 512 -> 1024. In addition ; to checking for good SCSI status returned from the command, we also check ; for a transfer length returned of >= 512 bytes, since read long should return ; at least a block of data (and hopefully some ECC bytes as well). CLRW UCB$W_READL_LEN(R3) ; First read long length to try 10$: ADDL3 #<7+4>,- ; Address of length field in SCSI SCDRP$L_CMD_PTR(R5),R1 ; command MOVB UCB$W_READL_LEN+1(R3),- ; Fill in high-order byte of transfer (R1)+ ; length MOVB UCB$W_READL_LEN(R3),(R1); Fill in low-order byte ; Use the SPI$SEND_COMMAND macro directly rather than the SEND_COMMAND routine ; as we don't all the error checking performed by that routine. SPI$SEND_COMMAND ; Send the command BLBC R0,30$ ; Branch on error ASSUME SCSI$C_GOOD EQ 0 MOVZBL @SCDRP$L_STS_PTR(R5),R1 ; Get SCSI status byte BICB #^XC1,R1 ; Clear reserved, vendor unique bits BNEQ 30$ ; Branch if bad status CMPL SCDRP$L_TRANS_CNT(R5),- ; Reasonable transfer length returned? #512 ; BLSSU 30$ ; Branch if not ; We've successfully sent a read long command and received a reasonable ; amount of data in return. Clear the bit in the UCB indicating this device ; supports host-based shadowing. BICL #DEV$M_NOFE,- ; Drive supports host based UCB$L_DEVCHAR2(R3) ; shadowing. 20$: BSBW CLEANUP_CMD ; Cleanup from the SCSI command 25$: MOVZWL #SS$_NORMAL,R0 ; Set unconditional success SUBRETURN ; Return to caller ; We've been unsuccessful in sending a read long command to the drive. Try ; the next data length value in the sequence, or, if we've reached the end of ; the sequence, return without setting the "host-based shadowing supported" ; bit in the UCB. 30$: SPI$RELEASE_QUEUE ; Free the queue from the CHK COND CMPW UCB$W_READL_LEN(R3),#1 ; First or second value in sequence? BLSSU 40$ ; Branch if first BEQL 50$ ; Branch if second 40$: INCW UCB$W_READL_LEN(R3) ; Go to next value in sequence CMPW UCB$W_READL_LEN(R3),- ; End of data length sequence? #READ_LONG_DATA_LEN ; BGTRU 20$ ; Branch if so BRB 10$ ; Try sending another read long command 50$: MOVW #512,UCB$W_READL_LEN(R3); Go to next value in sequence BRB 10$ ; Try sending another read long command ;+ ; FORCE_ERROR ; ; This routine is used to generate unrecoverable ECC errors on a set of ; logical blocks. It is used by shadowing code when the master copy of a ; particular block has gone bad and the "forced error" bit for this block ; must be set for all members of the shadow set. Since SCSI doesn't support ; the forced error bit, the alternative is simply to generate an unrecoverable ; ECC error, so all subsequent reads to this block generate an error. ; ; The ECC error is generated by performing a read long, inverting every byte ; in the block, and performing a write long. ; ; This routine performs it action by repeatedly calling FORCE_ONE_ERROR for ; each of the blocks associated with the QIO. ; ; INPUTS: ; ; R2 - IRP address ; R3 - UCB address ; R4 - SPDT address ; R5 - SCDRP address ; ; UCB$W_READL_LEN - Length to use for the read long / write long commands ; ; OUTPUTS: ; ; R0 - Status ; R1-R2 - Destroyed ; All other registers preserved ; ;- FORCE_ERROR: CLRL SCDRP$L_ABCNT(R5) ; Initialize accumulated byte count MOVL IRP$L_MEDIA(R2),- ; Copy disk logical block number from SCDRP$L_MEDIA(R5) ; the IRP to the SCDRP MOVL #512,SCDRP$L_BCNT(R5) ; Force errors on one block at a time BBC #MSCP$V_MD_ERROR,- ; Branch if the MD_ERROR bit not set. IRP$L_MEDIA+6(R2),50$ ; This bit must be set to force an error BBC #UCB$V_HBS_CHECK,- ; Branch if check for host-based UCB$L_DK_FLAGS(R3),50$ ; shadowing has not yet been made BBS #DEV$V_SWL,- ; Branch if device is software UCB$L_DEVCHAR(R3),60$ ; write locked. Can't support HBS. 10$: BSBB FORCE_ONE_ERROR ; Force an error on the next block BLBC R0,30$ ; Branch on error INCL SCDRP$L_MEDIA(R5) ; Advance to next block ADDL #512,SCDRP$L_ABCNT(R5) ; Update accumulated byte count MOVL SCDRP$L_IRP(R5),R2 ; Restore IRP address CMPL SCDRP$L_ABCNT(R5),- ; Done with entire transfer? IRP$L_BCNT(R2) ; BLSSU 10$ ; Branch if not 30$: MOVL SCDRP$L_ABCNT(R5),R1 ; Get accumulated transfer count INSV R1,#16,#16,R0 ; Load low-order transfer count BRW COMPLETE_IO ; Complete the QIO ; The check to determine if the device supports host-based shadowing has ; not yet been made. Therefore, we can attempt to for an error at this time. 50$: MOVZWL #SS$_UNSUPPORTED,R0 ; Set unsupported status BRB 30$ ; Use common exit ; The device is write-locked and therefore can not accept the commands ; necessary to cause an uncorrectable ECC error. 60$: MOVL #SS$_WRITLCK,R0 ; Set write-locked status BRB 30$ ; Use common exit .SBTTL FORCE_ONE_ERROR - Force an unrecoverable error on a block ;+ ; FORCE_ONE_ERROR ; ; This routine is used to generate an unrecoverable ECC error on the specified ; logical block and is called by the routine FORCE_ERROR. The ECC error is ; generated by performing a read long, inverting every byte in the block, and ; performing a write long. Prior to performing the read long / write long ; sequence, the block is read to determine if it already contains a ; non-recoverable error. If so, no action is taken. ; ; INPUTS: ; ; R2 - IRP address ; R3 - UCB address ; R4 - SPDT address ; R5 - SCDRP address ; ; UCB$W_READL_LEN - Length to use for the read long / write long commands ; ; OUTPUTS: ; ; R0 - Status ; R1-R2 - Destroyed ; All other registers preserved ; ;- FORCE_ONE_ERROR: SUBSAVE ; Save return address ; First, perform a normal read command on the block to determine if the block ; already contains an uncorrectable error. If so, don't bother forcing another. BISW #IRP$M_FUNC,- ; Set the FUNC but to indicate this SCDRP$W_STS(R5) ; is a read function MOVL #512,R1 ; Allocate an S0 buffer in which to read BSBW ALLOC_POOL ; the block BISB #SCDRP$M_S0BUF,- ; Indicate that this is an S0 "user" SCDRP$L_SCSI_FLAGS(R5) ; buffer MOVL R2,SCDRP$L_SVA_USER(R5) ; Save address of S0 "user" buffer DISABLE_ERRLOG ; Temporarily disable errorlogging BSBW READ_WRITE ; Send out a read command REENABLE_ERRLOG ; Reenable errorlogging PUSHL R0 ; Save status MOVL SCDRP$L_SVA_USER(R5),R0 ; Get address of S0 buffer BSBW DEALLOC_POOL ; Deallocate the S0 buffer POPL R0 ; Restore R0 CMPL R0,#SS$_PARITY ; Already a hard error on the block? BEQLW 40$ ; Branch if so ; Issue a read long command to get the current contents of the logical ; block. MOVAL CMD_READ_LONG,R2 ; Address of read long command BSBW SETUP_CMD ; Perform setup for SCSI command ADDL3 #<7+4>,- ; Address of length field in SCSI SCDRP$L_CMD_PTR(R5),R1 ; command MOVB UCB$W_READL_LEN+1(R3),- ; Use length determined by CHECK_HBS (R1)+ ; fill in high-order byte MOVB UCB$W_READL_LEN(R3),(R1); and low-order byte ADDL3 #<6+4>,- ; Address just beyond logical block SCDRP$L_CMD_PTR(R5),R1 ; field in SCSI command MOVAL SCDRP$L_MEDIA(R5),R0 .REPT 4 MOVB (R0)+,-(R1) ; Copy LBN number to SCSI command .ENDR BSBW SEND_COMMAND ; Send the command BLBC R0,20$ ; Branch on error ; Invert every byte in the original block to ensure that subsequent reads to ; the block will result in uncorrectable ECC errors. MOVL SCDRP$L_SVA_USER(R5),R0 ; Get address of read long data MOVZBL #<512/4>,R1 ; Length of disk block in longwords 10$: XORL2 #^XFFFFFFFF,(R0)+ ; Invert a longword SOBGTR R1,10$ ; Repeat for the entire disk block ; Save the original (read long) SCDRP and allocate another one in preparation ; for sending a write long command. MOVL R5,UCB$L_SCDRP_SAV1(R3) ; Save the original SCDRP BSBW ALLOC_SCDRP ; Allocate a new SCDRP for the write long MOVAL CMD_WRITE_LONG,R2 ; Address of write long command BSBW SETUP_CMD ; Perform setup for SCSI command ADDL3 #<7+4>,- ; Address of length field in SCSI SCDRP$L_CMD_PTR(R5),R1 ; command MOVB UCB$W_READL_LEN+1(R3),- ; Use length determined by CHECK_HBS (R1)+ ; fill in high-order byte MOVB UCB$W_READL_LEN(R3),(R1); and low-order byte ADDL3 #<6+4>,- ; Address just beyond logical block SCDRP$L_CMD_PTR(R5),R1 ; field in SCSI command MOVL UCB$L_SCDRP_SAV1(R3),R0 ; Restore original SCDRP address MOVAL SCDRP$L_MEDIA(R0),R2 ; Address of LBN field from original SCDRP .REPT 4 MOVB (R2)+,-(R1) ; Copy LBN number to SCSI command .ENDR ; Copy the data, which is now innverted, from the read long buffer to the ; write long buffer. MOVL SCDRP$L_SVA_USER(R0),R0 ; Get address of read long buffer MOVL SCDRP$L_SVA_USER(R5),R1 ; Get address of write long buffer ASSUME READ_LONG_DATA_LEN EQ WRITE_LONG_DATA_LEN MOVZWL #READ_LONG_DATA_LEN,R2 ; Number of bytes to copy PUSHR #^M ; Save regs MOVC3 R2,(R0),(R1) ; Copy read long buf to write long buf POPR #^M ; Restore regs ; Send the write long command, then perform the cleanup necessary for both ; commands. BSBW SEND_COMMAND ; Send the command BSBW CLEANUP_CMD ; Cleanup from the write long command BSBW DEALLOC_SCDRP ; Deallocate the write long SCDRP MOVL UCB$L_SCDRP_SAV1(R3),R5 ; Restore original SCDRP address 20$: BSBW CLEANUP_CMD ; Cleanup from the read long command 30$: SUBRETURN ; Return to caller ; The block already has a non-recoverable error associated with it. Don't ; bother forcing a second error, since if the current error is due to a ; previous force error function, issuing a second force error will have the ; effect of making the block good again. Instead, simply return success status. 40$: MOVZWL #SS$_NORMAL,R0 ; Set success status BRB 30$ ; Use common exit BRB 30$ ; Use common exit .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$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 <- DK$DDT,- DK_CTRL_INIT,- DK_UNIT_INIT,- DK_STARTIO,- DK_REG_DUMP,- IO_PACKACK,- IO_READPBLK,- IO_WRITEPBLK,- INQUIRY,- 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 - UCB address ; ; OUTPUTS: ; ; R0-R3 - Destroyed ; All other registers preserved ; ; CRB$L_AUXSTRUC - Address of cell containing trace buffer address ;- SETUP_TRACE: MOVL R5,R3 ; Copy UCB address SUBSAVE ; Save return address .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 BSBW ALLOC_POOL ; Allocate the trace buffer BLBC R0,10$ ; Branch if failure 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 MOVL UCB$L_CRB(R5),R0 ; Get CRB address MOVL CRB$L_AUXSTRUC(R0),R0 ; Get CDDB address MOVAL TR$TRACE_BUFFER_ADDR,- ; Save address of cell pointing to CDDB$L_PDT(R0) ; trace buffer in CRB SUBRETURN ; Return to caller .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 R1,UCB$L_TR_SCSI_CMD(R5); Save address to put SCSI command bytes 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 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 .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 10$: RSB .SBTTL TRACE_SCSI_CMD - Trace SCSI command bytes ;+ ; TRACE_SCSI_CMD ; ; This routine saves the contents of the SCSI command in the trace buffer. ;- TRACE_SCSI_CMD: rsb .if defined trace_scsi_commands PUSHR #^M ; Save regs MOVL SCDRP$L_UCB(R5),R3 ; Get UCB address MOVL TR$CURRENT_QIO,R0 ; Get QIO number BLSS 20$ ; Branch if negative, must be in UNIT INIT MULL3 #TR$QIO_SIZE,R0,R1 ; Get offset of this QIO in trace buffer MOVAL TR$TRACE_BUFFER,R2 ; Get trace buffer address ADDL R1,R2 ; Address to place info for this QIO ADDL #8,R2 ; Skip QIO number, valid flag fields CMPL (R2),- ; Maximum SCSI commands exceeded? #TR$SCSI_CMDS_PER_QIO BGEQ 20$ ; Branch if so INCL (R2) ; One more SCSI command sent MOVL SCDRP$L_CMD_PTR(R5),R0 ; Get address of SCSI command MOVL (R0)+,R1 ; Get size of SCSI command MOVL UCB$L_TR_SCSI_CMD(R3),R2; Address to save CMD in MOVL R1,(R2)+ ; Save length of command 10$: MOVB (R0)+,(R2)+ ; Save a command byte SOBGTR R1,10$ ; Repeat for entire command MOVL UCB$L_TR_SCSI_CMD(R3),R2; Restore command pointer ADDL3 R2,#TR$CMD_BYTES+4,- ; Save address to put SCSI status byte UCB$L_TR_SCSI_STS(R5) ; ADDL3 R2,#TR$SCSI_CMD_SIZE,- ; Save address to put next SCSI command UCB$L_TR_SCSI_CMD(R3) ; 20$: POPR #^M ; Restore regs RSB ; Return to caller .endc .SBTTL TRACE_SCSI_STS - Trace SCSI status bytes ;+ ; TRACE_SCSI_STS ; ; This routine saves the SCSI status byte in the trace buffer. ;- TRACE_SCSI_STS: rsb .if defined trace_scsi_status PUSHQ R1 ; Save reg MOVL SCDRP$L_UCB(R5),R1 ; Get UCB address MOVL UCB$L_TR_SCSI_STS(R1),R1; Get address to put status bytes CMPL (R1),#TR$STATUS_BYTES ; Status bytes exceeded? BGEQ 10$ ; Branch if so MOVAL 4(R1),R2 ; Get address of first status byte ADDL (R1),R2 ; Place to put this message byte MOVB R0,(R2) ; Save status byte INCL (R1) ; One more status byte received 10$: POPQ R1 ; Restore reg RSB ; Return to caller .endc .ENDC ; .IF DEFINED DEBUG DK_PATCH: .BLKB 200 ; Patch space DK_END: ; Last location in driver .END