.TITLE DKDRIVER - VAX/VMS SCSI Disk Class Driver .SBTTL Copyright Notice .IDENT 'X-18U9' .DISABLE GLOBAL ; Disable Globals .ENABLE SUPPRESSION ; Suppression list of unreferenced symbols STB .SHOW MEB ; Show Macro Expansion Binary ; .SHOW ME,MC,MD ; Show Macro Expansion, Conditionals, Definitions ;**************************************************************************** ;* * ;* COPYRIGHT © 1978, 1980, 1982, 1984, 1991, 1992, 1993, 1994, 1995 BY * ;* DIGITAL EQUIPMENT CORPORATION, MAYNARD, MASSACHUSETTS. * ;* ALL RIGHTS RESERVED. * ;* * ;* THIS SOFTWARE IS FURNISHED UNDER A LICENSE AND MAY BE USED AND COPIED * ;* ONLY IN ACCORDANCE WITH THE TERMS OF SUCH LICENSE AND WITH THE * ;* INCLUSION OF THE ABOVE COPYRIGHT NOTICE. THIS SOFTWARE OR ANY OTHER * ;* COPIES THEREOF MAY NOT BE PROVIDED OR OTHERWISE MADE AVAILABLE TO ANY * ;* OTHER PERSON. NO TITLE TO AND OWNERSHIP OF THE SOFTWARE IS HEREBY * ;* TRANSFERRED. * ;* * ;* THE INFORMATION IN THIS SOFTWARE IS SUBJECT TO CHANGE WITHOUT NOTICE * ;* AND SHOULD NOT BE CONSTRUED AS A COMMITMENT BY DIGITAL EQUIPMENT * ;* CORPORATION. * ;* * ;* DIGITAL ASSUMES NO RESPONSIBILITY FOR THE USE OR RELIABILITY OF ITS * ;* SOFTWARE ON EQUIPMENT WHICH IS NOT SUPPLIED BY DIGITAL. * ;* * ;* * ;**************************************************************************** .PAGE .SBTTL Revision History ;+ ; ; FACILITY: ; ; VAX/VMS SCSI Disk Class Driver ; ; ABSTRACT: ; ; This module contains the disk class driver to control read/write and ; read-only disks on the SCSI bus. The driver can have at most one active ; I/O per device on the SCSI bus at any one time. This driver relies on ; the SCSI port driver to provide the low-level communication of commands ; and data over the SCSI bus. ; ; AUTHOR: ; ; Jim Klumpp 4-May-1988 ; ; REVISION HISTORY: ; ; ******************************************************************************** ; ; Driver weaknesses: ; DSE operations should perform bad block replacement. ; WRITE with DATACHECK should perform bad block replacement. ; READ with DATACHECK should perform bad block replacement. ; ; ******************************************************************************** ; ; X-18U9 RCL0006 Rick Lord 2-May-95 ; 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-26 RCL Rick Lord 29-Mar-95 ; - Move the code which sets SINGLE_RW in IO_RW_COMMON to after ; the checks in which we validate that the volume and block ; number are valid, to prevent our leaving SINGLE_RW set and ; leaving the device permanently busy. ; ; X-18U7 JFD0817 James F. Dunham 4-APR-1995 ; o Remove private defintions of IRP$V_FORCEMV and CAN$C_MSCPSERVER ; o Decrease queue table depth sizes. ; o Increate queue scan histogram counter ; ; X-18U6 RCL Rick Lord 5-Apr-95 ; ; 1) Make WCE a preferred bit in it's caching page descriptor rather than ; setting to explicitly in-line, since it is the more common case. The ; only section of code which did not alter it was for non-512-byte block ; size devices, which for now would be only CD-ROMs, and WCE is not an ; issue for them. ; ; 2) Create LOAD_TENBYTE and STORE_TENBYTE macros to simplify saving and ; restoring the TENBYTE bits in the UCB and SCDRP (they're not the same ; bit offset). ; ; 3) In PROCESS_MODE_INFO, make the error recovery page the first page for ; all types of devices, not just for CD-ROMs, and if it's successful then ; save the state of the SCDRP.TENBYTE bit in the UCB. ; ; 4) In PROCESS_MODE_INFO, refresh the SCDRP.TENBYTE bit from UCB.TENBYTE if ; any DO_MODE_PAGE fails and the routine is going to continue - if it's ; going to exit then there's no need to do the refresh. ; ; 5) In PROCESS_MODE_FORMAT_FLOPPY, initialize the SCDRP.TENBYTE bit from ; UCB.TENBYTE prior to the first DO_MODE_PAGE. There's no need to refresh ; the SCDRP bit in this routine because it exits if DO_MODE_PAGE fails. ; ; X-18U5 SCS Sue Sommer 21-Mar-1995 ; JFD0814 James F. Dunham 23-MAR-1995 ; o Allow certain errors to be converted to SS$_MEDOFL ; in order to permit retries. ; o Make sure CD-ROM block size processing is a required mode ; sense operation. ; ; X-18U4 SCS Sue Sommer 17-Mar-1995 ; - Convert any errors from PROCESS_MODE_INFO to SS$_MEDOFL ; so that retries are possible. ; - Modify CHECK_HBS to retry any Read Long commands that ; return Unit Attention status, since such status may ; merely indicate a bus reset. ; - Modify CHECK_HBS to circumvent unnecessary CLEANUP_CMD ; for writelocked devices. ; - Remove support for ADU's in CHECK_HBS. ; - Just before KP_REQCOM in startio, convert any status of ; SS$_TIMEOUT to SS$_MEDOFL in order to allow retries. ; ; X-18U3 RCL Rick Lord 9-Mar-95 ; ; 1) In DK_BEGIN_MNTVER and in STOP_NEW_IO, fix the instructions which were ; checking to see if the Golden IRP (UCB$R_BUSY_BIT_IRP) was on the head ; of the I/O queue or not; this check was failing consistently. ; ; 2) In COMPLETE_IO, preserve UCB$L_IRP across the call to EXE$KP_RESTART, ; as the KP restarted by that call may call KP_REQCOM, which may start ; another I/O and so overwrite UCB$L_IRP. ; ; X-18U2 SCS Sue Sommer 10-Feb-1995 ; GWW Grace Wang 28-Feb-1995 ; Add alternate startio routine DK_ALTSTARTIO for Quorum IO. ; ; X-18U1 SCS Sue Sommer 27-Feb-1995 ; Enhance cancel-I/O functionality to allow the MSCP ; server to cancel all its I/O's: Whenever the MSCP server ; calls DK_CANCEL (it does so with a reason code of ; CAN$C_MSCPSERVER), walk the list of pending IRPs and ; mark all server IRPs by setting the IRP$V_SRV_ABORT bit ; in IRP$L_STS. Then, just prior to sending a SCSI command ; in SEND_COMMAND, check for IRP$V_SRV_ABORT and, if it is set, ; return early with a status of SS$_CANCEL. For those IRPs ; which issue multiple SCSI commands per single IRP, add code ; to check for SS$_CANCEL after each call to SEND_COMMAND; if ; in fact SS$_CANCEL is returned, then bail out in mid-request. ; Also, similar logic was added to CHECK_HBS, which is the ; only routine in this driver which sends SCSI commands without ; calling SEND_COMMAND to do so. ; ; X-18 RCL Rick Lord 1-Feb-95 ; In IO_PACKACK, if PROCESS_MODE_INFO fails for any reason and the ; TENTYE bit is still set, clear it and try PROCESS_MODE_INFO again. ; This works around devices which appear to complete 10-byte mode ; sense requests successfully but in fact don't send usable data. ; ; X-17 GWW Grace Wang 27-Jan-1995 ; In PROCESS_MODE_INFO, default sector=4, track=6 to avoid ; MSCP Server crash (see Zeta QAR 2688 - with UCB$B_TRACKS ; initialized to zero, an unknown cause left it zero after ; PROCESS_MODE_INFO and VALIDATE_GEOMETRY of a RZ25 device. ; MSCP Server copies this zero to MSCP$W_GROUP and bugchecks ; to show that it won't tolerate zero geometry value.) ; ; X-16 RCL Rick Lord 20-Jan-1995 ; In SEND_COMMAND_RETRY, provide one label which will peform the ; retries-left check without altering R0 and another which will ; preload it with SS$_MEDOFL, the status which should normally ; be returned. Change the CHECK_CONDITION code immediately ; preceding the retry check to take the preserve-R0 branch. ; ; X-15 CMF340 C M Fariz 15-Jan-1995 ; Add code to GET_DEVICE_TYPE that will take the value provided ; in the SYSGEN parameter VMS7 and add it to UCB$W_DISC_TMO. ; It may be a negative or positive number. The value represents ; the number of seconds by which to alter the existing time ; out value. This will affect RZ type devices only. ; ; The routine REASSIGN_BLOCK will also have the contents of ; VMS7 added to SCDRP$L_DISCON_TIMEOUT. This will affect ; RZ type devices only. ; ; NB: ; Decreasing the time out vlaue is VERY risky. ; ; This code is being added as an investigative effort. The ; intention is to determine if an increase in this value is ; helpful now that multi-initiator and SCSI2 are now implemented. ; ; The use of this parameter needs to be reviewed at the ; start of the next VMS release. If it is determined that ; it is useful, a more perminant solution must be designed. ; ; X-14 SCS Sue Sommer 10-Jan-1995 ; Correct the use of UCB$L_IRP so that it is properly ; assigned before any calls to KP_REQCOM or CALL_REQCOM. ; Also, clear it after calls to CALL_REQCOM since it ; is then invalid in a multi-initiator environment. ; ; X-13 CMF339 C M Fariz 21-Dec-1994 ; Change the SEND_COMMAND routine so that if a SCSI BUSY status ; is returned, and the timeout time has not been exceeded, the ; IO will be retried. There will only be one error log entry ; per IO if BUSY status is returned. ; ; This code does account for the CANCEL IO bit that will ; exist shortly. The code needs to just be added. ; ; CMF141 C M Fariz 21-Dec-1994 ; Updated the copyright statement to use the symbol © ; rather than the symbol (c). ; ; X-12 JFD0705 James F. Dunham 14-DEC-1994 ; ROW Ralph O. Weber 14-DEC-1994 ; o I/O Drain support for ordered strong sequential commands. ; o Insure that any post-processing of the "golden" IRP is harmless. ; Add routine DK_GOLDEN_IRP_POST. ; ; X-11 RAR060 Buzzy Ritter 21-Nov-1994 ; Modify SET_UNIT_ONLINE routive so that at boot time if packack ; fails for the boot device then retry. This avoids a hang on ; startup where packack sees an error (probably induced by the ; other initialtor's reset) and doesn't set the valid bit. The ; swapper will hang if the system disk's valid bit isn't set. We ; will retry forever since the swapper will hang forever anyway. ; ; X-10 JFD0701 James F. Dunham 1-DEC-1994 ; ROW0853 Ralph O. Weber 22-NOV-1994 18:58 ; o If Mount Verification in progress is detected, (UCB$V_MNTVERIP), ; ignore establishing CB_MOUNTVERIP is this IRP is a shadowing I/O. ; ; o Fix SET_SINGLE macro to reference UCB based on parameter value ; (not a fixed R5) when setting UCB$V_BSY. ; ; X-9 ROW0852 Ralph O. Weber 15-NOV-1994 09:50 ; Add STOP_NEW_IO, a routine that is called before REQCOM to ; block new I/O requests from coming to the DKDRIVER start I/O ; routine whenever DKDRIVER is not ready to handle new I/Os. ; Remember, blocking new I/O requests also keeps the UCB$V_BSY ; bit set. STOP_NEW_IO encapsulates some moderately complex ; knowledge about when DKDRIVER is ready to accept new I/O ; requests. STOP_NEW_IO also is the one place where the ; encapsulated knowledge needs to be changed as new features ; (particularly failover) are added. ; ; Prevent the "golden" IRP from becoming burried in the UCB$L_IOQFL ; ; JFD0695 James F. Dunham 2-NOV-1994 ; Change the behavior of SET_SINGLE to always set the UCB$V_BSY ; bit when setting a UCB$L_CLASS_BUSY bit. The makes DKDRIVER ; look busy when single threaded I/O operations are active. ; This change in UCB$V_BSY functionality makes DKDRIVER appear ; externally like the SCSI-1 DKDRIVER. ; ; Add code prior to calling KP_REQCOM to implement BUSY_BIT_IRP, ; functionality, which keeps the UCB$V_BSY bit set. ; ; DK_BEGIN_MNTVER: ; o Add shadowing I/O's to the list of IRP types which DKDRIVER does ; not perform mount verification on. ; o Add a bugcheck, if DK_BEGIN_MNTVER ever attempts to MNTVER a MVIRP, ; and remove check for MVIRP on re-insert into UCB$L_IOQ code path. ; o Make sure IRP's are re-inserted into UCB$L_IOQ in EXE$INSIOQ order. ; ; In support of SCSI-Clusters, make sure IO_AVAILABLE and IO_UNLOAD ; are single-threaded and are implemented. The STOP_UNIT SCSI command ; is sent as an ordered command, (aka sequential-nop). ; ; X-8 SCS Sue Sommer 11-Nov-1994 ; Correct the input time value to exe$kp_tqe_wait. ; JFD James F. Dunham ; In support of SCSI-Clusters, make sure IO_AVAILABLE and IO_UNLOAD ; are single-threaded and are implemented. The STOP_UNIT SCSI command ; is sent as an ordered command, (aka sequential-nop). ; ; X-7 SCS Sue Sommer 7-Nov-1994 ; Add TENBYTE flag to DK_FLAGS to indicate support for 10-byte ; mode sense commands. Modify PROCESS_MODE_INFO to reference ; this flag and prevent unnecessary 10-byte command attempts. ; ; X-6 WBF Burns Fisher 31-OCT-1994 ; In SET_UNIT_ONLINE, change the device characteristics to allow ; disconnects. We need this to avoid getting a continuous busy ; return from the inquiry if there is another host on the bus ; keeping the device's queue full (SCSI spec says device must or ; at least can return busy for a non-disconnectable request if ; it is currently servicing other disconnected requests). ; ; X-6 JFD0692 James F. Dunham 31-OCT-1994 ; When re-inserted I/O's in pending I/O queue, insert them in ; IRP$B_PRI and IRP$L_SEQNUM order. ; ; X-6 RAR055 Buzzy Ritter 26-Oct-1994 ; Remove use of UCB$PS_SCDRP everywhere except logging errors. ; Set this field in LOG_ERROR macro for DK_REG_DUMP use. ; ; X-5 JFD0672 James F. Dunham 13-OCT-1994 ; When re-inserting I/O's into the pending I/O queue, never ; place an entry in front of a IRP$V_MVIRP. ; SCS Sue Sommer 14-oct-1994 ; Fix GET_DEVICE_TYPE to handle unknown SCSI devices correctly. ; ; X-4 JFD0671 James F. Dunham 5-OCT-1994 ; If the driver is in single-threaded mode (UCB$V_BSY), then ; determine if mount verification is active and we don't know ; about it. If so setup to look like MV in Progress/ MV Pending. ; ; Add support for CLASS_BUSY tracing, and leave it disabled. ; ; ************* Change Edit History to Match CMS ******************** ; ; X-3 JFD0660 James F. Dunham 28-SEP-1994 ; SCSI-2 Checkin ; ; X-43 JFD0646 James F. Dunham 28-SEP-1994 ; Add support for MULTIHOST, if device is write enable and CMDQ ; ; X-42 JFD0645 James F. Dunham 26-SEP-1994 ; Add support for EXE_STD$KP_STARTIO callback. This will allow ; the driver to detect that a KPB thread startup is stalled and ; process accordingly. ; ; X-41 JFD0638 James F. Dunham 22-SEP-1994 ; o After issuing and I/O (KP_STARTIO) and it stalls, determine if ; there are any other reasons for not queuing the next I/O. If so ; set UCB$M_BSY and exit. ; o Add support for MSCP_MV call back, to inform MSCP of MNTVERIP ; o Add support for stalling the class driver, when no command bits ; are available at the SCSI port driver layer. This allows stalling ; without an associated KPB and its stack. ; o After a sucessful Mount Verification, selectively enable UCB$M_BSY ; based on validation of CLASS_BUSY bits. ; ; X-40 SCS Sue Sommer 21-Sep-1994 ; Add a TST_SINGLE to DK_STARTIO after calling kp_restart. ; ; X-39 RAR049 Buzzy Ritter 15-Sep-1994 ; Change PORT_CALLBACK flag in UCB$L_CLASS_BUSY into 5 seperate ; flags so that reset can have its own. ; ; X-38 JFD0620 James F. Dunham 13-SEP-1994 ; - Mount Verification for Served I/O's, are just reposted ; - Fix register usage for SET_SINGLE BUFFER_MAP ; - Fix register usage for SET_SINGLE SINGLE_DC ; - Clear geometry parameters (track, sector, cylinder) on errors ; - Allow multiple MNTVERIP to occur ; ; X-37 SCS Sue Sommer 7-Sep-1994 ; Add mode page descriptors to force 512 blocksize for CDROMs ; and to handle the WCE bit in the write caching page. ; ; X-36 JFD0615 James F. Dunham 6-SEP-1994 ; o Add DYNDEF and standard OpenVMS pool header to DK_ALLOC_POOL ; o Use correct KPB in BBR wait routines ; o Set SINGLE_RW when doing IO_WRITECHECK ; ; X-35 SCS Sue Sommer 6-Sep-1994 ; Remove extraneous Request Sense call in SEND_COMMAND's ; Check Condition path (port drivers handle this). ; Also, even if sense data is invalid, log the Check Condition. ; Modify IO_PACKACK to fail under certain conditions for ; SCSI clusters. ; ; X-34 JFD0608 James F. Dunham 2-SEP-1994 ; When performing datacheck and/or DSE operations, shared access ; to the UCB fields are not allowed. These routines must wait ; until they gain access to their respecitive CLASS_BUSY bit. ; ; X-33 JFD0607 James F. Dunham 30-AUG-1994 ; Rework MOUNT Verification to retain single threadedness, ; and handle correctly setting CB_MNTVERIP in the CLASS_BUSY bits ; ; X-32 JFD0598 James F. Dunham 25-AUG-1994 ; o Include part of IOC$INITIATE inside of DK_KP_STARTIO to prevent ; recursion, and subsequent kernel stack overflows ; o Handle CLUSTER Mountver ; o Single thread DKDRIVER around buffer mapping calls ; o Add support for GO/STOP on send credits ; o Change SINGLE_DC to be disable if CMDQ is valid ; ; X-31 SCS Sue Sommer 24-Aug-1994 ; Modify Check Condition path in SEND_COMMAND so that ; SCDRP$L_TRANS_CNT reflects the sense data length ; (not the the original command's trans_cnt) and ; the sense data can be correctly logged in errlog file. ; ; X-30 SCS Sue Sommer 17-Aug-1994 ; Include support for mscp$v_md_exprs (express QIO) bit. ; ; X-29 JFD James F. Dunham 27-JUL-1994 ; Set UCB address as SELECT_CONTEXT when issuing a SPI$CONNECT ; ; X-28 JFD James F. Dunham 21-JUL-1994 ; Remove COLLECT_PERF_DATA flags, since SCSI-2 queue depth ; monitoring must be on at all times ; ; X-27 SCS Sue Sommer 20-Jul-1994 ; Modify SETUP_CMD so that the caller may specify scdrp$l_bcnt. ; ; X-26 JFD James F. Dunham 19-JUL-1994 ; Set UCB$M_NOCMDQ at driver load time ; Add support for single threading the following operations: ; o Unit Initialization ; o Port Callback ; o Mount Verification ; o PACKACK ; o Bad-Block Replacement ; o IO$_UNLOAD ; o IO$_FORMAT ; o IO$_AUDIO ; o Forced Error in last sector read ; ; X-25 SCS Sue Sommer 19-Jul-1994 ; Fix bugs in TRANS_SENSE_KEY that mistakenly referenced ; the SCDRP in R0 rather than the sense data in R2. ; ; X-24 SCS Sue Sommer 15-Jul-1994 ; Calculate ucb$b_scsi_version based on the ANSI-approved ; version field of the INQUIRY (not on the format field as ; was done previously). ; ; X-23 SCS Sue Sommer 12-Jul-1994 ; Modify PROCESS_MODE_INFO to log errors. ; Increase mode_sense_6 allocation length to 255. ; Make descriptor table more compact. ; ; X-22 JFD James F. Dunham 11-JUL-1994 ; Backoff the increase to the KP stack, since KP_STARTIO_IO uses ; cached KPB, and if DK's stack size is large then the cached KPB's ; stack size, it bug checks ; ; X-21 SCS Sue Sommer 8-Jul-1994 ; Fix PROCESS_MODE_INFO to fetch mode page length correctly ; for flexible disks. ; ; X-20 SCS Sue Sommer 8-Jul-1994 ; - Modify descriptors to remove VERIFY flag and set IFMISMATCH ; where appropriate. ; - Eliminate obsolete use of ucb mode_sense_pag1/10 bits. ; - Modify mode sense logic for TCQ devices: if error recovery ; page processing fails, just disable TCQ and retry as nonTCQ. ; - Remove bad check for autosense in send_command. ; - Double size of KP stack (temporary) for debug. ; ; X-19 JFD James F. Dunham 7-JUL-1994 ; Change location of where UCB$M_CB_NOCMDQ is set. Keep both setting ; and clearing confined to IO_PACKACK. ; ; X-18 SCS Sue Sommer 7-Jul-1994 ; Modify PROCESS_MODE_INFO to get medium type correctly. ; ; X-17 SCS Sue Sommer 6-Jul-1994 ; Modify DK_PORT_CALLBACK to work with IO_DIAGNOSE. ; ; X-16 SCS Sue Sommer 27-June-1994 ; Remove call to LOG_ERROR in mode page handling since ; CDB information is gone after returning from do_mode_page. ; Convert mode sense errors to ss$_drverr. ; ; X-15 JCN Jan Nordh 23-Jun-1994 ; Fix DK_REG_DUMP routine, to dump just that info that ERF expects. ; ; X-14 SCS Sue Sommer 22-June-1994 ; Add enhanced mode page support to X-5 DKDRIVER. ; - Change SETUP_CMD, SEND_COMMAND, CLEANUP_CMD to CALL interface. ; - Make SCSI_CMD table entries global, and quad-aligned. ; - Add data section for mode page field descriptors; add $MODEDEF. ; - Add new routines PROCESS_MODE_INFO and PROCESS_MODE_FORMAT_FLOPPY. ; - Modify setup_cmd to allow a previously allocated private buffer. ; - Modify cleanup_cmd to allow private buffer. ; - Call READ_CAPACITY before PROCESS_MODE_INFO, not after. ; - Get block size from READ_CAPACITY data, not FORMAT data. ; - In IO_PACKACK, implement new call to PROCESS_MODE_INFO, ; and add call to new routine VALIDATE_GEOMETRY. ; - In IO_FORMAT, implement new call to PROCESS_MODE_FORMAT_FLOPPY. ; - Remove obsolete routines MODE_SENSE, MODE_SENSE_CHANGABLE, ; MODE_SELECT, MODE_SELECT_FORMAT_FLOPPY. ; Add calls to common routines for IO$_DIAGNOSE. ; ; X-13 JFD James F. Dunham 21-JUN-1994 13:40 ; 1). Fix handling of MOUNT Verification's usage of CLASS_FLAGS ; 2). Fix calulation of DK$REG_DUMP's buffer length ; ; X-12 JFD James F. Dunham 14-JUN-1994 ; 1). Fix I/O Diagnose interface, which references invalid DSC_FLAGS ; suffixed onto end of SCDRP ; 2). Fix enabling of TCQ, based on CMDQ & DISCONNECT support ; 3). Fix ridged disk cylinder field access ; 4). Re-introduce NOCMDQ qualifier into TST_SINGLE, SET_SINGLE and ; CLR_SINGLE macros. ; ; ; X-11 JFD James D. Dunham 9-JUN-1994 08:27 ; 1). Fix call to EXE$KP_WAIT_TQE to use the SCDRP's KPB verses the ; UCB$PS_UNITINIT_KPB. ; 2). Fix encoding of SECONDS value for TQE's ; 3). Fix invalid register for UCB$L_OTHER_COUNT ; 4). Invoke CLR_SINGLE SINGLE_RW at end of DATACHECK I/O's ; 5). Alter enabling CMDQ based on availability of _DISCONNECT's ; 6). Fix offeset to recovery flags ; 7). Address 'active' IRP usage in LOG_ERROR ; ; X-43 JFD00221 James F. Dunham 19-OCT-1993 ; 1). Migration merge of Tag-queueing changes to EPSILON ; 2). Rework of TRACE_SCSI routines ; 3). Correct time call arguments to KP_STALL_TQE ; 4). Pass Queuing parameters to SPI$CONNECTION_CHAR_?ET ; 5). Remove READY_POLL_INTERVAL, (as its a second) ; 6). Remove C2_BUILD & NEW_PKCDRIVER build flags ; 7). Remove (noted) comments regarding UCB$K_LENGTH being ; limited to a certain size. This is a VAX'ism. ; 8). Correct DK_ALLOC_SCDRP to correct preserve the previous ; SCDRP's address and KPB address. ; 9). Remove NOCMDQ qualifier from TST_SINGLE, SET_SINGLE and ; CLR_SINGLE macros. ; 10). Fix VMS date/time argument to KP_STALL_TQE from value ; to reference value ; 11). Change the Inquiry CDB to be sent to the SCSI port as ; non-tagged. When it was 'tagged', other CDB's could ; be sent, even though a device is not configure until ; the Inquiry CDB completes. ; 12). Symbolize the SCSI$DK$V_WP, instead of a constat of '7'. ; 13). Change FMT$B_PAGE_LENGTH to CMP$B_PAGE_LENGTH for constancy ; 14). Change SCSI$FMT$W_SECTORS_SIZE to SCSI$FMT$W_SECTORS ; 15). Symbolize SCSI$ERP$?_PAGE_CODE from number values ; 16). Fix MODE_SELECT's calculation of CTRL_MODE_LENGTH ; 17). Change SCSI$MPDB$B_BLOCK to SCSI$MPDB$B_LENGTH ; 18). Change MODE_SELECT's referencing of error and control page ; offsets when filling in data. ; 19). Fix MODE_SELECT's calulation of FMT$B_PAR_PAGE_LENGTH ; 20). Symbolize SCSI$M_STATUS_BYTE_RESERVED from '^XC1'. ; 21). Cleanup ALLOC_SCDRP's zero'ing of memory, and set ; PHASE_TMO and DISC_TMO. ; 22). Cleanup SETUP_CMD processing ; 23). Add TCQ parameteres to SET_CONN_CHAR processing ; 24). Fix CHECK_HBS usage of R0, which get scratch via ; new CALLS interface. ; ; X-42U1 RAR032 Buzzy Ritter 14-Mar-1994 ; Fix use of R6 & R8 after forking in DK_CTRL_INIT. ; ; ; X-42 RAR028 Buzzy Ritter 29-Dec-1993 ; Segment write transfers in IO_DSE so that they don't exceed ; 127 pagelets. Also fix return value in unit init and ctrl init. ; ; ; X-41 PJH Paul J. Houlihan 19-Nov-1993 ; Set UCB$V_EXFUNC_SUPP. ; ; X-40 SCS Sue Sommer 13-Dec-1993 ; In IO_PACKACK, remove an unnecessary call to fetch ; audio MCN data; this call was generating an error for ; data CDs on mounted on RRD42s. ; Resynchronize version numbers with CMS. ; ; X-38 RAR026 Buzzy Ritter 1-Dec-1993 ; Change loop in CHECK_HBS so that a new message buffer is ; allocated each time through the loop. ; ; X-37 MAS0E01 Michael A. Stams 8-Nov-1993 ; Add DEV$M_CLU to DPT_STORE for DEVCHAR2. ; ; X-36 LPL0001 Lee Leahy 1 Oct 1993 ; Finish symbol name changes. ; ; X-35 RCL Rick Lord 30-Sep-1993 ; ; Field name changes: ; ; SCDRP$V_S0BUF => SCDRP$V_FLAG_S0BUF ; SCDRP$M_S0BUF => SCDRP$M_FLAG_S0BUF ; SCDRP$V_BUFFER_MAPPED => SCDRP$V_FLAG_BUFFER_MAPPED ; SCDRP$M_BUFFER_MAPPED => SCDRP$M_FLAG_BUFFER_MAPPED ; SCDRP$V_DISK_SPUN_UP => SCDRP$V_FLAG_DISK_SPUN_UP ; SCDRP$M_DISK_SPUN_UP => SCDRP$M_FLAG_DISK_SPUN_UP ; ; X-34 SCS Sue Sommer 16-SEP-1993 ; - Enable audio functionality for CDROMs. ; - Fix FORCE_ONE_ERROR to calculate SVAPTE and BOFF before ; calling READ_WRITE. ; ; X-33 WDBHLL80 Walter D. Blaschuk, Jr. 10-Sep-1993 ; HLLDD Project: Change the Host Base Shadowing logic ; to call the Upper Level FDT routines in SHDRIVER.MAR. ; ; X-32 LSS0290 Leonard S. Szubowicz 7-Sep-1993 ; HLLDD: CALL_PTETOPFN must assure that R1 is preserved as ; well as R0, thus the parameter name changes to SAVE_R0R1. ; ; X-31 WDA W.D. Arbo 17-Aug-1993 ; Change old step 2 macro names to new names. (E.g. ; $ABORTIO to CALL_ABORTIO). ; ; X-30 WDA W.D. Arbo 27-Jul-1993 ; Catch references to IOC$REQCOM and IOC$INITIATE in ; SET_UNIT_ONLINE which were missed in X-29. ; ; X-29 WDA W.D. Arbo 29-Jun-1993 ; Clean up remaining step 2 conversion items. Note: DKAUDIO ; and the subroutine AUDIO_MAP_PAGE have been modified for ; step 2, but since these routines are turned off these modifications ; have not been tested. ; ; X-28 LSS0254 Leonard S. Szubowicz 10-Jun-1993 ; Merge X-23U2. ; Fix support for the IO$M_DATACHECK modifier and IO$_WRITECHECK ; function. In the code after label IO_DATACHECK, do not alter ; SCDRP$L_SVAPTE and SCDRP$L_BOFF for each segment checked. In ; the AXP version of this driver these cells point to the data- ; check buffer and not the current segment of the user buffer. ; Thus in routine DATACHECK_CMP, compute the mapping to the ; current segment of the original user buffer accordingly. ; Previously, this routine was double mapping the datacheck ; buffer and comparing it against itself! ; ; X-27 SCS Sue Sommer 14-May-1993 ; Modify IO_DSE routine to calculate SCDRP$L_SVAPTE so that ; the DSE buffer may be successfully mapped. ; ; X-26 RWC124 Richard W. Critz, Jr. 7-May-1993 ; I failed to notice that YELLOW found the wrong copy of this ; module when I checked in RWC122. As a result, the entries in ; the device table for RZ73, RZ57 and RZ25 disks are still ; commented out. Restore them to their rightful place. ; ; X-25 RWC122 Richard W. Critz, Jr. 23-Apr-1993 ; Integrate DDR support (in local routine GET_DEVICE_TYPE). ; ; X-24 WDA W.D.Arbo 14-Apr-1993 ; Conversion to Step 2. ; ; X-23-JFD James F. Dunham 14-Sep-1993 ; 1). Merge Tag Queueing changes from VAX to AXP ; 2). Changed comments to add thread, IPL and LOCK information. ; New comments start with ; ; ; ; Where: ; ; THREAD character is one of: ; F = Fork thread ; I = Interrupt thread ; P = Initialization or powerfail thread ; Q = Queue Manager kernel process thread ; S = I/O request SCDRP kernel process thread ; U = User Mode FDT routines thread ; C = Class Driver kernel process thread ; ; IPL character is one of: ; A = IPL$_ASTDEL - 2 ; P = IPL$_IOPOST - 4 ; F = Fork IPL - 8 ; S = IPL$_SYNCH - 8 ; T = IPL$_TIMER - 8 ; D = Device IPL - 21 ; X = IPL$_POWER - 30 ; ; LOCK characters (last character => last spinlock taken) are one of: ; Not specified = no locks held ; F = fork lock (IOLOCK8) ; D = Dual SCSI device lock ; S = SPDT device lock ; U = UCB device lock ; ; These comments can be used in combination with the DCL "search" ; command to determine if a variable is accessed in a synchronous ; manner. ; 3). Add BBR failure if VMSCluster has lost QUORUM ; ; X-38 MCY Mary Yuryan 14-May-1993 ; Fold in J. Guineau's AUDIO bugfix. ; WJG John Guineau 09-Apr-1993 ; Fix error paths in IO$_AUDIO FDT/STARTIO routines related ; to bad/improperly initialized AUCB fields. ; ; X-37 RCL Rick Lord 6-May-1993 ; ; Define OPTICAL and WORM bits in UCB$L_DK_FLAGS. ; ; Modify INQUIRY subroutine to set OPTICAL bit in UCB$L_DK_FLAGS ; if the SCSI device type code is optical, or the WORM bit if the ; device type code is WORM. ; ; Modify MODE_SENSE to set HWL and SWL if the device is WORM, or ; if it's Optical with WORM media mounted; also, do not require ; track and sector information to be returned from Mode Sense ; for Optical devices. ; ; X-36 MCY Mary Yuryan 12-Apr-1993 ; Add the following SCSI devices - RW504, RW510, RW514, ; RW516, RWZ51, RWZ52, RWZ53, RWZ54, RWZ31 - RZ28L, RZ26M - ; EZ31, EZ32, EZ33, EZ34, EZ35, EZ31L, EZ32L, EZ33L, EZ56R ; - HSZ15. ; ; X-35 WJG W. John Guineau 6-Apr-1993 ; - No changes, FOLD VIKING TCQ stream into CORAL, only ; changed .IDENT to match CORAL stream ; - uncomment the define for V60_BUILD for Coral. ; ; ; X-18A7A7-Q WJG W. John Guineau 15-Mar-1993 ; - Lock out other processes in IO$_DIAGNOSE while ; a an I/O is pending for one GK process. Also, ; Make sure process for a pending check condition is ; the process that caused it. ; ; X-18A7A6-Q WJG W. John Guineau 3-Mar-1993 ; - Make TEST_UNIT_READY send TUR as NON-QUEUED which ; will still sync queues in a queued environment, but ; works around an HSZ10 bug where the first queued command ; sent when a unit attention is pending get's eaten. ; ; X-18A7A5-Q WJG W. John Guineau 2-Feb-1993 ; - Set UCB$M_BSY during PACKACK just to be safe. ; - Add BBR_IN_PROG flag to UCB$L_DK_FLAGS. This bit ; is set when a BBR starts, cleared when it completes. ; If BBR_IN_PROG is set when a second thread starts ; a BBR operation, that thread will FORK_WAIT until ; it clears. This prevents more than one thread from ; attempting a BBR operation at one time. ; - Move UCB$L_SCDRP_SAV2, UCB$L_ADDNL_INFO and ; UCB$B_SENSE_KEY into SCDRP for multi-threaded ; synchronization. ; ; X-18A7A4-Q WJG W. John Guineau 15-Jan-1993 ; - Add a G^ to the call to EXE$ALONONPAG in READ_CD_MCN ; to fix crash. ; ; X-18A7A3-Q WJG W. John Guineau 11-Jan-1993 ; - Fix floppy formatting bug in MODE_SENSE: where a ; BGEQ was being used instead of BLSS causing ; UCB$L_FORMAT_PRAMS to never be allocated. ; - Add a '-Q' to the compiled in image ident (.IDENT) for ; easy detection of command queuing based drivers. ; ; X-18A7A2 WJG W. John Guineau 8-Jan-1993 ; - Remove unused/debug code ; - Add HSZ20, HSZ40 (FIB) to CHECK_FOR_RAID macro ; - fix bug in histogram which would corrupt pool on ; xfers larger than 127 blocks ; - Deal with MODE_SENSE page 0x0A (queueing parameters) ; to make sure the QErr bit is not set ; - Attempt to clear the ARRE/AWRE bits in MODE_SENSE page 1 ; - Make LUN information handling in INQUIRY SCSI-2 compliant ; ; X-18A7A1 WJG W. John Guineau 12-17-1992 ; - Add CHECK_FOR_RAID_DEVICE macro and UCB$V_RAID ; bit. RAID devices use 10 byte mode sense/select ; commands. ; - Fix bug in copying of additional sense bytes ; in SAVE_ADDNL_INFO: to do a signed compare of ; length of additional sense data against length ; of buffer field in UCB ; - Add command queuing support ; - merge BLADE X-31,X-32 changes for VIKING Checkin: ; X-32 MCY Mary Yuryan ; Add "B" variant disks to device recognition tab ; RZ27B, RZ28B, RZ29B, RZ73B, RZ74B, RZ75B. Add ; Add HSZ20,HSZ40 - Raid subsystem. ; ; X-31 MCY Mary Yuryan ; Fix 2 bugs in the ATTEMPT_REORDER routine. ; 1)Update R4 correctly so as not to alter the be ; the elevator algorithm; currently the scan di ; be effected. ; 2)Provide UCB$L_FAIRNESS_CNT (with value of 4) ; malicious user from looping infinitely while ; same LBN. Checks added in SWAP_IRP:, SCAN_DO ; PERFECT_IRP:. This could slow or even hang t ; ; X-30 JSSBLADE2 John S. Simakauskas 1-July-1992 ; Fix build bug introduced in X-29, ; SCSI_DEV_TYPES table was made too long. ; ; X-29 JSSBLADE John S. Simakauskas 24-June-1992 ; Add device recognition for RZ26L,RRD43,RRD44. ; ; X-28 MCY Mary Yuryan 12-Jun-1992 ; Always return good status in the READ_CD_MCN routine ; to keep 3rd party CDROMS that do not support SCSI-2 ; from failing. ; ; X-27 MCY Mary Yuryan 03-Jun-1992 ; Add check for RRD40S device type in IO_PACKACK, and ; branch around READ_CD_MCN for audio if device is ; ; X-23-LPL ; LPL1003 Lee Leahy 24 May 1993 ; 1.) Added .DISABLE GLOBAL to locate undefined symbols. ; 2.) Added .SHOW MEB to display macro expansions for analysis. ; 3.) Added $DDTDEF macro. ; 4.) Added .EXTERNAL definition list. ; 5.) Removed the .NLIST directive. ; 6.) Added build time check for TQE size in PDT. ; 7.) Added the following fields: ; UCB$L_CTRL_MODE ; UCB$L_CTRL_MODE_CPAR ; UCB$L_QUEUED_IO_COUNT ; UCB$L_QDEPTH ; UCB$L_QDEPTH_TURNS ; UCB$L_READ_COUNT ; UCB$L_WRITE_COUNT ; UCB$L_OTHER_COUNT ; UCB$L_READ_XLEN_HIST ; UCB$L_WRITE_XLEN_HIST ; UCB$L_XLEN_HIST ; UCB$L_XLEN_HIST_CYCLE ; UCB$L_CLASS_BUSY ; UCB$L_DK_FLAGS (Added new bits) ; UCB$Q_IRP_LIST ; 8.) Added $SCDTDEF macro. ; 9.) Added queuing characteristic to the SPI$SEND_COMMAND calls. ; 10.) Added form feeds. ; 11.) Changed references from UCB$L_IRP to SCDRP$L_IRP. ; 12.) Cleared UCB$L_IRP following usage to help prevent double deallocation. ; 13.) Modified the LOG_ERROR macro to set the current IRP. ; 14.) Added code to support UCB$Q_IRP_LIST. ; 15.) Made the routines global so that the driver symbol table ; could be used for debugging. ; 16.) Added code to support UCB$L_CLASS_BUSY. ; 17.) Added code to set and clear UCB$V_BSY. ; 18.) Added code to update UCB$L_QUEUED_IO_COUNT. ; ; X-23 SCS Sue Sommer 12-Apr-1993 ; Tie off IO$_AUDIO function until it can be fixed later. ; ; X-22 SCS Sue Sommer 24-Mar-1993 ; Add new devices to SCSI_DEV_TYPES table. ; ; X-21 SCS Sue Sommer 24-Feb-1993 ; Add boot order (BT_ORDER) value to DPTAB to insure that DKDRIVER ; init routines execute before those of SHDRIVER (shadowing); ; choose value of 5000 to match DUDRIVER. ; ; X-20 SCS Sue Sommer 9-Feb-1993 ; Add missing .JSB_ENTRY mask to DK_CRESHAD. In INIT_SCDRP, ; initialize SCDRP type for easier crash dump analysis. ; In unit initialization routine, set MAXBLOCKS and ; ONLINE!BSY UCB bits before forking, to resolve ; synchronization problems. ; ; X-19 SCS Sue Sommer 6-Nov-1992 ; Change the name of ALLOC_POOL to DK_ALLOC_POOL to reflect ; the new input of R5; insure that all calls to it have ; a KPB address in R5. ; ; Merge in Blade changes (through X-32) as follows. Support ; for DDR, C2, and register saving in DK_CTRL_INIT are not ; included, but the following is merged: ; ; MCY Mary Yuryan 22-Oct-1992 ; Add "B" variant disks to device recognition tables - RZ26B, ; RZ27B, RZ28B, RZ29B, RZ73B, RZ74B, RZ75B. Add RZ27L, RWZ21. ; Add HSZ20,HSZ40 - Raid subsystem. ; ; MCY Mary Yuryan 10-Sep-1992 ; Fix 2 bugs in the ATTEMPT_REORDER routine. ; 1)Update R4 correctly so as not to alter the behavior of ; the elevator algorithm; currently the scan direction may ; be effected. ; 2)Provide UCB$L_FAIRNESS_CNT (with value of 4) to prevent a ; malicious user from looping infinitely while using the ; same LBN. Checks added in SWAP_IRP:, SCAN_DONE:, and ; PERFECT_IRP:. This could slow or even hang the system. ; ; JSSBLADE2 John S. Simakauskas 1-July-1992 ; Fix build bug introduced in X-29, ; SCSI_DEV_TYPES table was made too long. ; ; JSSBLADE John S. Simakauskas 24-June-1992 ; Add device recognition for RZ26L,RRD43,RRD44. ; ; MCY Mary Yuryan 12-Jun-1992 ; Always return good status in the READ_CD_MCN routine ; to keep 3rd party CDROMS that do not support SCSI-2 ; from failing. ; ; MCY Mary Yuryan 03-Jun-1992 ; Add check for RRD40S device type in IO_PACKACK, and ; branch around READ_CD_MCN for audio if device is ; ; MCY Mary Yuryan 03-Apr-1992 ; Add ASSUME statement before $DEFEND of UCB to check for ; illegal System disk UCB expansion. Value is calculated ; in longwords. (Unnecessary for Alpha /SCS 13-Nov-1992) ; ; MCY Mary Yuryan 11-Mar-1992 ; Add latent support for RZ28 and RZ29. ; Fold in John Guineau's AUDIO fix from AMBER - Change ; UCB$M_CDROM symbol to UCB$V_CDROM in BBC instruction ; in IO_PACKACK routine. ; ; MCY Mary Yuryan 20-Feb-1992 ; Add latent support for RAID controller - HSZ10. ; ; Fold in Richard Napolitano's changes for AUDIO fix. ; RLN Richard L. Napolitano 15-Dec-1991 ; 1) Format zero workaround ; 2) Modified LOG_EXTND_SENSE not to log read errors ; to CDROM's being mounted foreign. ; ; BP4282 Brian Porter 04-FEB-1992 ; Use an ADAWI to increment and decrement UCB$W_QLEN. ; (Alpha use PRESERVE atomicity instead /SCS 13-Nov-1992) ; Sync .IDENT to VSC. ; ; MCY Mary Yuryan 09-Jan-1992 ; Fix bug in GET_DEVICE_TYPE - Restore R0 containing ; INQUIRY command data pointer. ; ; MCY Mary Yuryan 23-Dec-1991 ; Merge AUDIO and Latent device support with C2. ; (C2 not merged in Alpha version /SCS 13-Nov-1992) ; ; FAK003 Forrest A. Kenney 26-Nov-1991 ; Merge latest round of BLADE fixes. ; ; WJG0048 W. John Guineau 14-Nov-1991 ; Add DDR support. UCB$L_DISABLE_DDR is a flag to disable ; the use of DDR. If non-zero, no DDR will be used. ; (Note: Support is commented for Alpha /SCS 13-Nov-1992) ; ; MCY Mary Yuryan 11-Nov-1991 ; Merged Rich Napolitano's AUDIO changes into the driver. ; Redefine READPROMPT function to AUDIO using the same IODEF ; symbol value for READPROMPT. ; ; Richard L. Napolitano ; Implemented AUDIO extension to DKDRIVER to allow AUDIO ; commands to be sent to SCSI II compliant CD-ROM devices. ; These extensions allow the execution of CD AUDIO commands ; from application programs. ; ; Mary Yuryan 10-Dec-1991 ; Add latent device support: RZ27,RZ37,RZ38,RZ75,RZ59, ; EZ51,EZ52,EZ53,EZ54,EZ58,RZ13,RZ14,RZ15,RZ16,RZ17,RZ18, ; RZ34L,RZ35L,RZ36L. ; ; MCY Mary Yuryan 10-Oct-1991 ; Add latent support for the RZ26, RZ36, adn RZ74. ; (VSC Ident change.) ; ; MCY Mary Yuryan / John Guineau 2-Oct-1991 ; Fix DK_CTRL_INIT routine to properly save CDDB initialization ; registers before forking. ; (Omit for Alpha /SCS 13-Nov-1992) ; ; JTK Jim Klumpp 13-Aug-1991 ; Change to place in the driver where ATTEMPT_REORDER ; is called to the end of STARTIO so third-party disk ; caching products can continue to intercept I/Os at ; the front of STARTIO. Change the ATTEMPT_REORDER ; routine to sort only the pending queue, ignoring ; the active IRP at UCB$L_IRP. Terminate the pending ; queue scan after a finite of IRPs have been considered ; for reorder. ; ; X-18 SCS Sue Sommer 27-Oct-1992 ; Modify LOG_ERROR routine to write EMB$L_DV_STS, EMB$L_DV_ERTCNT ; and EMB$L_DV_ERTMAX in error buffer, since otherwise they ; contain uninitialized data; also zero EMB$Q_DV_IOSB, since ; the IOSB is not yet known at this point. ; ; X-17 SCS Sue Sommer 1-Oct-1992 ; In LOG_ERROR macro and LOG_ERROR and REGDUMP routines, replace ; use of R7 and R8 with UCB fields. This accommodates ERL$DEVxxx ; convention, which does not declare R7 and R8 as inputs. ; ; X-16 SWA Scott W. Apgar 05-Aug-1992 ; Changing some references to the AUXSTRUC offset in the ; CRB to use the new field SCS_STRUC. AUXSTRUC was ; overloaded with functionality. The AUXSTRUC offset is ; still used to hold the address of a Trace Buffer. ; ; X-15 SFS0561 Stephen F. Shirron 06-Jul-1992 ; Add new SCSI devices. ; ; X-14 SCS Sue Sommer 5-Jun-1992 ; Update BOFF and ABCNT correctly in routine IO_RW_ERR. ; ; X-13 SCS Sue Sommer 27-May-1992 ; Add new devices to SCSI device type table. ; ; X-12 SCS Sue Sommer 18-May-1992 ; Update SVAPTE and ABCNT correctly in routine IO_RW_ERR. ; ; X-11 SCS Sue Sommer 29-Apr-1992 ; In ATTEMPT_REORDER, update R4 correctly and add fairness ; counter to prevent the same LBN from being serviced repeatedly. ; ; X-10 SCS Sue Sommer 28-Apr-1992 ; In CHECK_HBS, add check to branch around this code on ; the ADU only. ; ; X-9 SCS Sue Sommer 17-Apr-1992 ; Add symbol alignment where appropriate in calls to $DEFINI. ; Merge in changes from VAX/VMS V5.4-3 as follows: ; ; WJG W. John Guineau 16-May-1991 ; Use EXE$ZEROPARM as FDT routine for IO$_NOP to solve ; crashes from this function code. ; ; JJM Jeff McLeman 16-Apr-1991 ; Increase floppy disconnect timeouts to 30 seconds ; ; MCY Mary Yuryan 19-Feb-1991 ; Add new SCSI devices to the SCSI_DEV_TYPES symbol table. ; RWZ01, RZ24L, RZ25L, RZ55L, RZ56L, RZ57L. ; ; JTK Jim Klumpp 30-Jan-1991 ; Add a workaround for RZ23 read long bug. After a read ; long command is sent to the RZ23, the next mode select ; command fails with an illegal request sense key. The ; fix is to simply retry the mode select command once. ; ; MCY Mary Yuryan 25-Jan-1991 ; Increase RRD42 Disconnect timeout to 120 seconds. ; ; GH001 Gary Hughes 21-Dec-1990 ; Fix up media ID mismatch for RZ23L, RZ57I. ; ; JTK Jim Klumpp 11-Dec-1990 ; 1) Add new devices: RZ72, RZ73, RZ35. ; ; 2) In SET_UNIT_ONLINE, set the LCL_VALID bit in the UCB ; if the PACKACK completes successfully for the system ; disk. This is required for host-based shadowing booting. ; ; JTK Jim Klumpp 28-Nov-1990 ; 1) Change the disconnect timeout time for the RRD42 to ; 45 seconds. ; ; 2) Add host-based shadowing support including routines to ; check that a device is capable of supporting HBS (CHECK_HBS), ; to force uncorrectable ECC errors on a set of blocks ; (FORCE_ERROR), and to erase a set of blocks (IO_DSE). ; ; 3) Fix clearing of LCL_VALID bit in UCB at the end of ; IO_PACKACK. ; ; 4) Use byte count divisor returned by SPI$CONNECT in ; DK_UNIT_INIT. ; ; WCY0146 Wai C. Yim 7-Nov-1990 ; 1) Add code to create CDDB in controller init. ; ; 2) Change all references of EXE$GL_SYSUCB to SYS$AR_BOOTUCB ; Since it is no longer the same for shadowing. ; ; 3) Add shadowing booting support. Synchronize with DSA ; class driver by checking for different SYS$AR_BOOTUCB ; from EXE$GL_SYSUCB. ; ; 4) Set SCSI bit in DEVCHAR2 in all devices by means ; of DPT_STORE UCB macro. ; ; 5) Add CRESHAD and REMSHAD FDT routines. ; ; ------------- Ident change due to master pack cleanup -------- ; ; X-29 SCS Sue Sommer 12-Mar-1992 ; Add RX26 support. ; ; X-28 LSS0243 Leonard S. Szubowicz 3-Mar-1992 ; In FDT routine DK_DIAGNOSE obtain QIO parameters from the IRP ; since AP no longer points to P1 in an FDT routine. Also, ; remove the TRACE_TABLE since the cross-PSECT offsets it contains ; prevent the "sliced" loading of this driver. Moreover, this ; table is unused in this version of this driver. Finally, in ; macro DK_ALLOC_SCDRP, assure that the SCDRP allocated on the ; stack is quadword aligned for performance reasons. ; ; X-27 SCS Sue Sommer 23-Feb-1992 ; Remove END argument in DPTAB so the driver may be sliced. ; ; X-26 SCS Sue Sommer 6-Feb-1992 ; Reinstate the reordering of IRPs for performance. ; Only reorder IRPs in the pending queue; exclude the ; current IRP from reordering in order to avoid switching ; KPBs after a kernel process has already started, and ; in order to accommodate third party software. ; ; X-25 ROW0802 Ralph O. Weber 28-JAN-1992 16:04 ; Fix the unit routine to start the KP stack usage with a ; KP register save mask that is suitable for HLL port drivers. ; The Cobra port driver is HLL and needs this fix. ; ; X-24 BJT291 Benjamin J. Thomas III 9-Jan-1992 ; Promote UCB ERRCNT, ERTCNT and ERTMAX fields to longword ; ; X-23 BJT286 Benjamin J. Thomas III 5-Dec-1991 ; Use HLL KPB register mask rather than hand-built one ; ; X-22 BJT271 Benjamin J. Thomas III 15-Nov-1991 ; Promote FUNC fields ; ; X-21 BJT265 Benjamin J. Thomas III 11-Nov-1991 ; Promote more IRP and UCB fields to longwords. ; ; X-20 BJT260 Benjamin J. Thomas III 31-Oct-1991 ; Promote more IRP and UCB fields to longwords. ; ; X-19 BJT259 Benjamin J. Thomas III 25-Oct-1991 ; Promote word fields STS in IRP and UCB to longwords ; ; X-18 SCS Sue Sommer 17-Oct-1991 ; Modify SAVE_CONNECT_CHAR to use correct argument count. ; ; X-17 SCS Sue Sommer 8-Oct-1991 ; Promote remaining occurrences of IRP$W_STS to IRP$L_STS. ; ; X-16 SCS Sue Sommer 5-Oct-1991 ; Promote various word fields to longwords in the SCDRP, SCDT, ; SPDT, and UCB. ; Remove the temporary check for a bad IRP. ; ; Merge in changes by Mary Yuryan, Barbara Leahy, Jim Klumpp, ; and John Guineau from VAX/VMS V5.4-2 as follows: ; Create RRD42 disconnect timeout symbol definitions and ; set that tmo to 120 seconds. ; Added context save on IO_DIAGNOSE function for mixed-mode ; media support. ; Send a request sense command immediately after sending a ; mode select to clear out a possible pending check condition ; that could otherwise cause a subsequent command to fail. ; Also, in TRANS_SENSE_KEY, treat mode select change status ; as normal. An assumption here is we're not running in a ; multi-initiator environment. ; Change the spinup timeout value from 20 to 30 seconds ; to accomodate the RZ25. Change READY_POLL_INTERVAL to 1, ; and READY_POLL_CNT to 30. ; Add processing of LCL_VALID bit in IO_FORMAT and IO_PACKACK. ; Call SET_CONN_CHAR in SET_UNIT_ONLINE to prevent format ; operations from timing out before a PACKACK function has ; successfully executed. ; Change the symbol definition of LO_COST_CD to the ; correct device name of RRD42. ; Added UCB$B_SCSI_VERSION to save INQUIRY response data ; format (SCSI version). This allows version-specific ; code paths in mode select, where the setting of the ; non-changable parameters is incompatible between SCSI-1 ; (CCS) and SCSI-2. ; Fix IO$_WRITECHECK function. Prior to this change, this ; function was identical to a write with the datacheck ; modifier. The correct operation is to compare the data ; in the user's buffer with that on the disk, and not ; modify the contents of either the user's buffer or the ; disk. ; ; X-15 TYC0001 Theresa Y. Chin 30-Sep-1991 ; Get rid of double fork in UNIT_INIT and implement ; a new counter to wait for port to be initialized ; before class driver init proceeds. ; ; X-14 BJT247 Benjamin J. Thomas III 27-Sep-1991 ; Promote IRP$W_STS to IRP$L_STS ; ; X-13 LSS0224 Leonard S. Szubowicz 6-Sep-1991 ; Expand UCB$W_QLEN to a longword to avoid word granularity ; problems within the same quadword. ; ; X-12 SCS Sue Sommer 13-Aug-1991 ; Add temporary code to check for bad IRP. Convert form ; feeds to .PAGE for list file. ; ; X-11 SCS Sue Sommer 29-Jul-1991 ; Revise SCSI return status codes SCSI$C_CONDITION_MET and ; SCSI$C_RESERV_CONFLICT to have correct values. Modify ; unit routine to save the UCB address in the KPB before ; calling KP_SWITCH_TO_KP_STACK, and to restore it afterwards. ; ; X-10 JTK Jim Klumpp 15-Jul-1991 ; Add entry to FDT for NOP function. ; ; X-9 SCS Sue Sommer 9-Jul-1991 ; Fix argument in call to KP_ALLOCATE_KPB ; ; X-8 SCS Sue Sommer 2-Jul-1991 ; Modify unit initialization routine so that the KPB is ; deallocated at the end of unit init. ; ; X-7 SCS Sue Sommer 26-Apr-1991 ; Modify IO_DATACHECK routine to pass correct SVAPTE and ; BOFF of the datacheck buffer to the port driver. Fix ; shift factor bug in calculating VBN's for segmented I/O. ; ; X-6 SCS Sue Sommer 8-Apr-1991 ; (Temporary) Comment out code in Startio which reorders ; IRPs for performance. Pending IRPs have no associated KPB, ; which presents a problem should such a pending IRP be ; chosen as the current optimal IRP. Eventually this code ; will be restored and IRP$PS_KPB will point to a valid KPB; ; until the implications are fully explored, comment it out. ; ; ; X-5 SCS Sue Sommer 12-Mar-1991 ; Fix datacheck SPTE allocation in unit init to account ; for non-page-aligned buffers. Fix segmented I/O code ; to update SVAPTE correctly. ; ; ---------- Numbering change due to master pack reorg ----------- ; ; X-1K6 SCS Sue Sommer 4-Mar-1991 ; Replace INSV instruction in READ_WRITE routine to ; correctly set LBN. ; ; X-1K5 SCS Sue Sommer 20-Feb-1991 ; Use FORK_ROUTINE macro to generate .JSB_ENTRY mask ; in UNIT_INIT. Change entry mask on DK_CTRL_INIT, DK_DIAGNOSE, ; CHECK_REV_LEVEL, and TRACE_QIO_STAT. Change SCSI_DEV_TYPES ; macro to generate naturally aligned data; likewise modify ; GET_DEVICE_TYPE. ; ; X-1K4 SCS Sue Sommer 7-Feb-1991 ; Add support for new routines EXE$READLOCK_ERR and ; EXE$WRITELOCK_ERR in IO_DIAGNOSE. ; ; X-1K3 SCS Sue Sommer 1-Feb-1991 ; Include $KPBDEF. ; ; X-1K2 SCS Sue Sommer 24-Jan-1991 ; Initial port to Alpha. ; ; X-14U3 JTK Jim Klumpp 6-Jun-1990 ; 1) In SETUP_CMD, map the buffer with high priority to avoid ; deadlock. The map buffers elsewhere can remain at low ; priority, as there's no danger of deadlock on the first ; call to map buffer per QIO function. ; ; 2) Calculate the value for UCB$W_CYLINDERS using the same ; algorithm as DUDRIVER so that all nodes in a cluster see ; see the same geometry. ; ; 3) Work around a bug in RZ5x drives which cause them to ; report busy SCSI status indefinitely after a reselection ; timeout. Pull on bus reset if this situation is detected. ; ; X-14U2 JTK Jim Klumpp 20-Dec-1989 ; Use extended read and write SCSI commands for transfers ; to blocks above 1FFFFF (hex). ; ; X-14U1 JTK Jim Klumpp 1-Dec-1989 ; Add seek optimization routine. ; ; X-14 JTK Jim Klumpp 22-Sep-1989 ; Return MEDOFL status when an attempt is made to mount a ; removable device whose media is not inserted. Increase ; default disconnect timeout time for all RZ disks to 20 ; seconds. ; ; X-13 JTK Jim Klumpp 20-Sep-1989 ; Fix setting of disconnect timeout for floppy formatting. ; ; X-12 RLN Richard L. Napolitano 29-Aug-1989 ; Workaround a microcode problem in the RZ55 where during ; recovered with retry read operation, the RZ55 returns ; an additional block if the DTE bit is set. ; ; ; X-11 RLN Richard L. Napolitano 25-Aug-1989 ; Fix problem where during a READ operation of a transfer ; with a bad block that is reassigned, the blocks following ; the reassigned blocks are erroneously written. ; ; X-10 DGB0317 Donald G. Blair 05-Aug-1989 ; Add DPT$V_NO_IDB_DISPATCH bit to the driver prologue table. ; In SETUP_CMD, use correct register to set up timeout ; fields. ; ; X-9 JTK Jim Klumpp 12-Jul-1989 ; Add LUN support. Don't allocate the IRP portion of ; the SCDRP. Remove references to the SCDT. Increase ; number of arguments passed to SET_CONN_CHAR. Change ; priv required for IO_DIAGNOSE function. ; ; X-8 JTK Jim Klumpp 22-Jun-1989 ; Add callback support and data structure version ; checking for SPI$CONNECT. Add fastboot support. ; Fix logging of reassign block error. Check HWL bit ; to prevent writes to a write-locked disk. Fix ; handling of media changes in TRANS_SENSE_KEY. ; ; X-7 JTK,KAH Jim Klumpp, Kenneth A. House 15-Jun-1989 ; ; 1) Change descriptor length in reassign parameter list to 4. ; 2) Fix checking of read/write flag in DK_DIAGNOSE. ; 3) Increase disconnect timeout for reassign command. ; 4) Allow writing of double-density (DD) microfloppies only ; when the UCB$L_DK_FLAGS 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. .PAGE .SBTTL + .SBTTL + SYMBOL DEFINITIONS .SBTTL + .SBTTL External symbol definitions ; ; External symbols ; $ARBDEF ; Define ARB offsets $BOOSTATEDEF ; boot state flags $CADEF ; Conditional Assembly Parameters $CANDEF ; Cancel reason codes $CDDBDEF ; Class Driver Data Block $CLUBDEF ; Cluster block $CRBDEF ; Channel request block $DCDEF ; Device classes and types $DDBDEF ; Device data block $DDTDEF ; Device dispatch table $DEVDEF ; Device characteristics $DYNDEF ; Data strucure types $EMBDEF ; Errorlog message buffer $FDTARGDEF ; Define FDT routine input arg offsets $FDT_CONTEXTDEF ; Define FDT context structure $FKBDEF ; Define fork block symbols $HWRPBDEF ; Hardware RPB $IDBDEF ; Interrupt data block $IODEF ; I/O function codes $IPLDEF ; Hardware IPL definitions $IRPDEF ; I/O request packet $IRPEDEF ; I/O request packet extension $KPBDEF ; Kernel process block $MODEDEF ; Mode page definitons $MSCPDEF ; Define MSCP symbols $NSADEF ; Security Symbols $ORBDEF ; Object Rights block $PAGEDEF ; Page size $PCBDEF ; Process control block $PRVDEF ; Privilege mask $PTEDEF ; Page table entry symbols $SCSIDEF ; SCSI Definitions $SCDRPDEF ; SCSI SCDRP symbols $SCDTDEF ; SCSI CDT symbols $SPDTDEF ; SCSI PDT symbols $SPIDEF ; SCSI SPI symbols $SSDEF ; System status codes $TQEDEF ; Timer Queue Entry definition $UCBDEF ; Unit control block $VADEF ; Virtual address symbols $VECDEF ; Interrupt vector block ASSUME TQE$K_LENGTH EQ SPDT$S_TQE ; Validate TQE size. .SBTTL External References .EXTERNAL ACP_STD$ACCESS .EXTERNAL ACP_STD$DEACCESS .EXTERNAL ACP_STD$MODIFY .EXTERNAL ACP_STD$MOUNT .EXTERNAL ACP_STD$READBLK .EXTERNAL ACP_STD$WRITEBLK .EXTERNAL BUG$_INCONSTATE .EXTERNAL CLU$GL_ALLOCLS .EXTERNAL CLU$GL_CLUB .EXTERNAL CTL$GL_PCB .EXTERNAL DKMK$DIAGNOSE_INIT .EXTERNAL DKMK$DIAGNOSE_FDT .EXTERNAL DKMK$DIAGNOSE_SIO .EXTERNAL DO_MODE_PAGE .EXTERNAL EXE$ALONONPAGED .EXTERNAL EXE$DEANONPGDSIZ .EXTERNAL EXE$DEANONPAGED .EXTERNAL EXE$GL_ABSTIM .EXTERNAL EXE$GL_HBS_PTR .EXTERNAL EXE$GL_SHADOW_SYS_DISK .EXTERNAL EXE$GQ_SYSTIME .EXTERNAL EXE$GQ_SYSTYPE .EXTERNAL EXE$GL_SYSUCB .EXTERNAL EXE$KP_ALLOCATE_KPB .EXTERNAL EXE$KP_FORK_WAIT .EXTERNAL EXE$KP_RESTART .EXTERNAL EXE$KP_START .EXTERNAL EXE$KP_STALL_GENERAL .EXTERNAL EXE$KP_TQE_WAIT .EXTERNAL EXE_STD$ABORTIO .EXTERNAL EXE_STD$ALLOCIRP .EXTERNAL EXE_STD$DEANONPGDSIZ .EXTERNAL EXE_STD$FINISHIO .EXTERNAL EXE_STD$LCLDSKVALID .EXTERNAL EXE_STD$INSIOQ .EXTERNAL EXE_STD$KP_STARTIO .EXTERNAL EXE_STD$MODIFYLOCK .EXTERNAL EXE_STD$ONEPARM .EXTERNAL EXE_STD$READLOCK .EXTERNAL EXE_STD$SENSEMODE .EXTERNAL EXE_STD$SETMODE .EXTERNAL EXE_STD$WRITELOCK .EXTERNAL EXE_STD$WRITECHK .EXTERNAL EXE_STD$ZEROPARM .EXTERNAL IOC$ADD_DEVICE_TYPE .EXTERNAL IOC$GL_SPI_CONNECT .EXTERNAL IOC$INITIATE .EXTERNAL IOC$REMOVE_DEVICE_TYPE .EXTERNAL IOC$REQCOM .EXTERNAL IOC$RETURN .EXTERNAL INI$BRK .EXTERNAL LDR$ALLOC_PT .EXTERNAL LDR$DEALLOC_PT .EXTERNAL MMG$GL_BWP_MASK .EXTERNAL MMG$GL_PAGE_SIZE .EXTERNAL MMG$GL_PTE_OFFSET_TO_VA .EXTERNAL MMG$GL_SPTBASE .EXTERNAL MMG$GL_VA_TO_VPN .EXTERNAL MMG$GL_VPN_TO_VA .EXTERNAL MMG$SVAPTECHK .EXTERNAL MMG$TBI_SINGLE .IF DF CA$_MEASURE_IOT .EXTERNAL PMS$START_IO .ENDC .EXTERNAL SCS$GL_MSCP_NEWDEV .EXTERNAL SCS$DISK_MSCP_DRIVER_MV .EXTERNAL SGN$GL_VMS7 .EXTERNAL SMP$ACQUIRE .EXTERNAL SMP$RESTORE .EXTERNAL SMP$GL_FLAGS .EXTERNAL SMP$AL_IPLVEC .EXTERNAL SYS$AR_BOOTUCB .PAGE .SBTTL + .SBTTL + SYMBOL DEFINITIONS (cont.) .SBTTL + .SBTTL Misc local symbols ; ; Local symbols ; ; DEBUG = 1 ; Flag to enable debuging .IF DEFINED DEBUG .print ; - DEBUG flag is enabled .IFF .print ; - DEBUG flag is not enabled .ENDC ; TRACE = 1 ; Flag to enable tracing .IF DEFINED TRACE .print ; - TRACE flag is enabled .IFF .print ; - TRACE flag is not enabled .ENDC ; TRACE_SCSI = 1 ; Enable SCSI command tracing .IF DEFINED TRACE_SCSI .print ; - TRACE_SCSI flag is enabled .IFF .print ; - TRACE_SCSI flag is not enabled .ENDC ; TRACE_CB = 1 ; Enable tracing of UCB$L_CLASS_BUSY .IF DEFINED TRACE_CB .print ; - TRACE_CB flag is enabled .IFF .print ; - TRACE_CB flag is not enabled .ENDC ; Argument list offset definitions P1 = 0 ; First function dependent parameter P2 = 4 ; Second function dependent parameter P3 = 8 ; Third function dependent parameter P4 = 12 ; Fourth function dependent parameter P5 = 16 ; Fifth function dependent parameter P6 = 20 ; Sixth function dependent parameter SCDRPS_PER_UNIT = 15 ; Number of SCRPs to pre-allocate per unit PACKACK_RETRY = 10 ; Number of times to retry PACKACK READY_POLL_CNT = 30 ; Number of times to poll for unit ready ASSUME IOC$C_DISK_BLKSIZ EQ 512 IOC$S_DISK_BLKSIZ = 9 MAX_BCNT = <<64*2>-1>*512; Maximum byte count/transfer (64KB - 1 block) RW_RETRY_CNT = 6 ; Read/write retry count REASSIGN_RETRY_CNT = 6 ; Retry count for block reassignment REWRITE_RETRY_CNT = 6 ; Retry count for rewrite after reassign SENDCMD_RETRY_CNT = 16 ; Retry count for send command SCSI$C_BUSY retries SCSI_CMD_BUF_OVHD = 4 + 4 ; 4 bytes to save status byte + ; 4 bytes for SCSI command length DEFAULT_DISCONNECT_TIMEOUT = 40 ; Default values in seconds for disconnect DEFAULT_PHASE_CHANGE_TIMEOUT = 6 ; and phase change timeouts RRD40_DISC_TMO = 40 ; RRD40 disconnect timeout value RRD42_DISC_TMO = 150 ; RRD42 disconnect timeout value RRD42_DISC_TMO = 150 ; RRD42 disconnect timeout value RX_DISC_TMO = 50 ; Floppy disconnect timeout value RX_PHS_TMO = 20 ; Floppy phase change timeout value RX_FMT_DISC_TMO = 20*60 ; Floppy format disconnect timeout value REASSIGN_DISCON_TMO = 90 ; Reassign disconnect timeout DK_INIT_TIMEOUT = 60 ; Init timeout value = 60 seconds SS$_RECOVERR = 2 ; Recoverable error detected. Note that ; this status code is used internlly ; to distinguish between recoverable and ; non-recoverable errors, but is never ; returned as a QIO status. DK_ERROR_REVISION = 2 ; Errorlogging revision supported by ; this driver. This should be incremented ; each time an incompatible change is ; made to the errorlog packet format. DTE_EXTRA_BYTES = IOC$C_DISK_BLKSIZ ; Number of additional bytes received RZ55_MIN_NODTE_ERROR = ^A'0900' ; Mininum revision with fixed microcode EXTND_LBN_LIMIT = ^X1FFFFF ; Maximum LBN for which a normal read or ; write command can be used. The extended ; read/write commands must be used for ; LBNs above this value. (21-bits) ; See SCSI-2 (LBA address for READ) MAX_BUSY_TIME = 40 ; Maximum amount of time to allow a ; device to remain busy before resetting ; the bus ; Additional Sense Information Buffer Length ADD_SENSE_LEN = SCSI$SNS$R_ADD_SENSE-SCSI$SNS$R_ADD_INFORMATION SENSE_ALLOC = 255 ; Sense data allocation length ; Histogram buckets are expressed as a power of 2 to make collection ; fast - basically we logically shift the datum by the bucket factor ; to get a bucket index. XLEN_HIST_BUCKET_SHIFT = 2 ; If MAX_XFER is 128, 128>>2 = 32 buckets XLEN_HIST_BUCKETS = 32 ; we will round down to 64K (128blk) xfers XLEN_HIST_SIZE = <<128/4>*4> ; 1 long/bucket XLEN_HIST_TURNOVER = 100 ; Recalc qdepth after this many I/O READ_LONG_DATA_LEN = 1024 ; Maximum data length associated WRITE_LONG_DATA_LEN = 1024 ; with read long and write long ; commands, respectively. ; ; Definition of the offsets into the Audio Control Block (AUCB) for DKDRIVER. ; $DEFINI AUDIO_CONTROL_BLOCK CD_AUCB_CUR_VERSION = 1 ; Current Version number of AUCB $DEF CD_FUNCTION_CODE .BLKB 2 ; Audio function code $DEF CD_AUCB_VERSION .BLKB 2 ; Version number of AUCB structure $DEF CD_ARG1 .BLKL 1 ; Command specific parameter $DEF CD_ARG2 .BLKL 1 ; Command specific parameter $DEF CD_ARG3 .BLKL 1 ; Command specific parameter $DEF CD_RSVD1 .BLKL 1 ; Reserved for future use (MBZ) $DEF CD_DEST_BUF_ADDR .BLKL 1 ; Buffer returned to user $DEF CD_DEST_BUF_CNT .BLKL 1 ; Size of buffer returned to user $DEF CD_DEST_BUF_TRANS_CNT .BLKL 1 ; Actual number of bytes received $DEF CD_COMMAND_STATUS .BLKL 1 ; VMS O.S. Return status $DEF CD_SCSI_STATUS .BLKL 1 ; SCSI command status (optional) $DEF CD_SENSE_ADDR .BLKL 1 ; Sense data buffer $DEF CD_SENSE_CNT .BLKL 1 ; Sense data buffer size $DEF CD_SENSE_TRANS_CNT .BLKL 1 ; Sense data transfer count $DEF CD_RESVD2 ; Reserved for future used (MBZ) $DEF CD_AUCB_SIZE ; Size in bytes of AUCB $DEFEND AUDIO_CONTROL_BLOCK ; Definition of SCSI Audio command function codes. These are used by application ; to select individual audio functions supported by the driver. ; PAUSE = 0 ; Pause RESUME = 1 ; Resume PREVENT_REMOVAL = 2 ; Prevent/Allow ALLOW_REMOVAL = 3 ; Prevent/Allow PLAY_AUDIO = 4 ; Play Audio LBA PLAY_AUDIO_MSF = 5 ; Play MSF PLAY_AUDIO_TRACK= 6 ; Play Audio Track PLAY_TRACK_REL = 7 ; Play Track Relative READ_HEADER = 8 ; Reserved GET_STATUS = 9 ; Read Subchannel-Q GET_TOC = 10 ; Read TOC SET_VOLUME = 11 ; Mode Select GET_VOLUME = 12 ; Mode Sense SET_DEFAULT = 13 ; Reserved GET_DEFAULT = 14 ; Reserved ;+ ; Definitions for offsets into the SCSI CCB as used by DKDRIVER. ;- PAGE_CODE = 6 ; Page Code Field A_LEN = 8 ; Allocation Length Field .PAGE .SBTTL + .SBTTL + SYMBOL DEFINITIONS (cont.) .SBTTL + .SBTTL Disk class driver extensions to the UCB ; ; Disk class driver extensions to the UCB. ; .SYMBOL_ALIGNMENT QUAD $DEFINI UCB ; Start of UCB definitions . = UCB$K_MSCP_DISK_LENGTH $DEF UCB$L_DK_FLAGS .BLKL 1 ; Class driver flags $VIELD UCB,0,<- ; ,- ; Removable 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 queuing - ; Device supports command queuing ,- ; Device is an optical device ,- ; Device is a WORM device, or optical WORM media ,- ; Device supports DDR, (default is DISABLED!) ,- ; Device supports AEN (Asynch-Event-Notification) ,- ; Device or Port supports Autosense > ; Device supports 10-byte mode sense command $DEF UCB$L_CLASS_BUSY .BLKL 1 ; Reasons why UCB$V_BSY should remain set. $VIELD UCB,0,<- ; Single thread the following operations: - ,- ; Command queueing not supported. - - ; Initialization and error recovery. ,- ; Executing UNIT_INIT code. - ; ,- ; PORT_CALLBACK received. ,- ; Bad Block Replacement in progress. ,- ; Mount verification in progress. , - ; EXE_STD$KP_STARTIO Stalled , - ; Check_condition active , - ; Queue full_event , - ; Bus reset in progress - ; No send credits ,- ; No SPDT command bits - - ; Single threaded I/O Requests. ,- ; Single threaded read/write request in progress. ,- ; Single threaded datacheck request in progress. ,- ; Single threaded data security erase in progress. ,- ; Force error in progress. ,- ; IO$_NOP in progress. ,- ; IO$_AVAILABLE in progress. ,- ; IO$_UNLOAD in progress. ,- ; IO$_PACKACK in progress. ,- ; IO$_DIAGNOSE in progress. ,- ; IO$_FORMAT in progress. , - ; IO$_AUDIO in progress. , - ; Wait while busy > .=<<.+3>& ^C3> ; Align data structure $DEF UCB$Q_IRP_LIST .BLKQ 1 ; List of active I/O requests. $DEF UCB$L_FLUSH_IOQFL .BLKL 1 ; I/O flush queue forward link $DEF UCB$L_FLUSH_IOQBL .BLKL 1 ; I/O flush queue backward link .=<<.+3>& ^C3> ; Align data structure $DEF UCB$Q_DC_WAIT_LIST .BLKQ 1 ; Doublely linked list of Datacheck waiters $DEF UCB$Q_DRAIN_LIST .BLKQ 1 ; Doublely linked list of I/O Drain waiters $DEF UCB$IS_DRAIN_COUNT .BLKL 1 ; Count of stalled SCDRP's on above list $DEF UCB$L_QUEUED_IO_COUNT .BLKL 1 ; Outstanding I/O count to all port drivers. $DEF UCB$PS_SCDRP .BLKL 1 ; Address of active SCDRP $DEF UCB$PS_UNITINIT_KPB .BLKL 1 ; Pointer to KPB allocated in unit init $DEF UCB$PS_SCDT .BLKL 1 ; Address of SCDT $DEF UCB$L_CUR_LBN .BLKL 1 ; Current LBN, used for read/write reordering $DEF UCB$L_FAIRNESS_CNT .BLKL 1 ; Fairness counter in IRP reordering $DEF UCB$L_HW_REV .BLKL 1 ; Hardware revision info from inquiry $DEF UCB$L_ERROR_TYPE .BLKL 1 ; Error type used by LOG_ERROR $DEF UCB$L_ERR_MASK .BLKL 1 ; Mask of error types logged so far $DEF UCB$L_VMS_STATUS .BLKL 1 ; VMS status used by LOG_ERROR ; reordering of I/Os for seek optimization $DEF UCB$L_DISABLE_DDR .BLKL 1 ; Non zero means disable DDR ; (default is DISABLED!) $DEF UCB$L_INITMO .BLKL 1 ; Unit init timeout value $DEF UCB$PS_AUCB_ADDR .BLKL 1 ; Audio control block address $DEF UCB$PS_FORMAT_PARAM .BLKL 1 ; Address of sense information ; needed to format floppies $DEF UCB$PS_SAVE_CONN_CHAR .BLKL 1 ; Address of saved connection ; characteristics buffer for IO ; diagnose buffer NUM_LONGWORDS_DIAGNOSE = 6 ; UCB area used by IO_DIAGNOSE $DEF UCB$PS_DIAGNOSE .BLKL NUM_LONGWORDS_DIAGNOSE $DEF UCB$W_READL_LEN .BLKW 1 ; Value to use in read long len field $DEF UCB$W_PHASE_TMO .BLKW 1 ; Phase change timeout $DEF UCB$W_DISC_TMO .BLKW 1 ; Disconnect timeout $DEF UCB$W_BLOCK_SIZE .BLKW 1 ; Number of bytes per block $DEF UCB$B_LUN .BLKB 1 ; Logical unit number (LUN) $DEF UCB$B_SEEK_DIR .BLKB 1 ; Current motion of heads, used for $DEF UCB$B_SCSI_VERSION .BLKB 1 ; SCSI version (CCS or SCSI-2) $DEF UCB$B_RW_RETRY .BLKB 1 ; Read/write retry count $DEF UCB$B_REASSIGN_RETRY .BLKB 1 ; Reassign block retry count $DEF UCB$B_REWRITE_RETRY .BLKB 1 ; Rewrite after reassign retry count $DEF UCB$B_READY_RETRY .BLKB 1 ; Polling count for unit ready ; For CD-ROMS, Sub-Channel Q - Media Catalog Data (UPC) $DEF UCB$B_MCN_SCDATA .BLKB SCSI$SUBQ$C_MCN_LENGTH .=<<.+3>& ^C3> ; Align data structure $DEF UCB$R_SENSE_INFO .BLKB ; Balance of additional sense info $DEF UCB$B_SENSE_LEN .BLKB 2 ; Count of additional bytes .=<<.+3>& ^C3> ; Align data structure $DEF UCB$R_RECOV_PAR .BLKB SCSI$ERP$S_ERROR_RECOVERY ; Error recovery parameters UCB$B_RECOV_PAR_LENGTH = UCB$R_RECOV_PAR+SCSI$ERP$B_PAGE_LENGTH UCB$B_RECOV_PAR_FLAGS = UCB$R_RECOV_PAR+SCSI$ERP$R_RECOVERY_FLAGS .=<<.+3>& ^C3> ; Align data structure $DEF UCB$R_RECOV_CPAR .BLKB SCSI$ERP$S_ERROR_RECOVERY ; Err rec changable parameters UCB$B_RECOV_CPAR_FLAGS = UCB$R_RECOV_CPAR+SCSI$ERP$R_RECOVERY_FLAGS .=<<.+3>& ^C3> ; Align data structure $DEF UCB$R_CTRL_MODE .BLKB SCSI$CMP$S_CONTROL_MODE ; Control mode parameters UCB$B_CTRL_MODE_LENGTH = UCB$R_CTRL_MODE+SCSI$CMP$B_PAGE_LENGTH UCB$B_CTRL_MODE_FLAGS = UCB$R_CTRL_MODE+SCSI$CMP$R_CONTROL_MODE_FLAGS .=<<.+3>& ^C3> ; Align data structure $DEF UCB$R_CTRL_MODE_CPAR .BLKB SCSI$CMP$S_CONTROL_MODE ; Control mode changable parameters UCB$B_CTRL_MODE_CFLAGS = UCB$R_CTRL_MODE_CPAR+SCSI$CMP$R_CONTROL_MODE_FLAGS .=<<.+3>& ^C3> ; Align data structure $DEF UCB$L_QDEPTH .BLKL 1 ; The max qdepth for this device $DEF UCB$L_QDEPTH_TURNS .BLKL 1 ; How many times we've actually changed $DEF UCB$L_READ_COUNT .BLKL 1 ; Count reads done $DEF UCB$L_WRITE_COUNT .BLKL 1 ; Count writes done $DEF UCB$L_OTHER_COUNT .BLKL 1 ; All other I/O functions $DEF UCB$L_READ_XLEN_HIST .BLKL 1 ; Pointer to Read histogram pool $DEF UCB$L_WRITE_XLEN_HIST .BLKL 1 ; Pointer to Write histogram pool $DEF UCB$L_XLEN_HIST .BLKL 1 ; Pointer to total histogram pool $DEF UCB$L_XLEN_HIST_CYCLE .BLKL 1 ; # I/Os per histogram turnover .=<<.+3>& ^C3> ; Align data structure $DEF UCB$R_BUSY_BIT_IRP .BLKB IRP$C_LENGTH ; BUSY_BIT_IRP to used to keep ; UCB$V_BSY active across KP_REQCOM .IF DEFINED TRACE_CB ; Class_busy Ring-Buffer Support $DEF UCB$L_TOP_OF_RING .BLKL 1 $DEF UCB$L_RING_POINTER .BLKL 1 $DEF UCB$L_RING_COUNTER .BLKL 1 .ENDC $DEF UCB$K_DK_UCBLEN ; Length of extended UCB $DEFEND UCB ; End of UCB definitions .SYMBOL_ALIGNMENT NONE .PAGE .SBTTL + .SBTTL + SYMBOL DEFINITIONS (cont.) .SBTTL + .SBTTL Disk class driver extensions to the SCDRP ; ; Disk class driver extensions to the SCDRP ; $DEFINI SCDRP ; Start of SCDRP definitions . = SCDRP$K_LENGTH .IF DEFINED TRACE $DEF SCDRP$PS_TR_QIO_STS .BLKL 1 ; Address in trace buf to put QIO status $DEF SCDRP$PS_TR_SCSI_CMD .BLKL 1 ; Address in trace buf to put QIO status $DEF SCDRP$PS_TR_SCSI_STS .BLKL 1 ; Address in trace buf to put QIO status .ENDC $DEF SCDRP$K_DK_LENGTH ; Length of extended SCDRP $DEFEND SCDRP ; End of SCDRP definitions .PAGE .SBTTL + .SBTTL + SYMBOL DEFINITIONS (cont.) .SBTTL + .SBTTL Errorlog packet formats ;+ ; Following are the definitions for class driver errorlog packets. Each packet ; has a section common for all error types followed by an error-specific section. ;- $DEFINI ERROR_PACKETS . = EMB$L_DV_REGSAV ; Start of area to dump error info $DEF ERR$L_LW_CNT .BLKL 1 ; Count of number of LWs that follow $DEF ERR$B_REVISION .BLKB 1 ; Revision level $DEF ERR$L_HW_REV .BLKL 1 ; Hardware revision $DEF ERR$B_TYPE .BLKB 1 ; Error type $DEF ERR$B_SCSI_ID .BLKB 1 ; SCSI ID $DEF ERR$B_SCSI_LUN .BLKB 1 ; SCSI logical unit $DEF ERR$B_SCSI_SUBLUN .BLKB 1 ; SCSI sub-logical unit $DEF ERR$L_PORT_STATUS .BLKL 1 ; Port status code $DEF ERR$A_CMD_LEN .BLKB 1 ; SCSI command length field $DEF ERR$B_SCSI_STS .BLKB 1 ; SCSI status byte $DEF ERR$A_ADD_LEN .BLKB 1 ; Additional length field $DEF ERR$K_STANDARD_LENGTH ; Standard length of error packet ; Now define packets that have one or more of the variable length fields ; filled in. These fields consist of a byte count followed by n bytes of ; data. In the standard packet defined above, the byte count field would ; contain a zero for each possible variable length field. The list of variable ; length fields is: ; ; o SCSI command data (up to 12 bytes) ; o Additional data which depends upon the error type $DEF ERR$A_CMD_BYTES .BLKB 12 ; Maximum possible command bytes $DEF ERR$K_COMMAND_LENGTH ; Length of packet containing SCSI command $DEF ERR$A_INQUIRY_DATA .BLKB 36 ; Inquiry data $DEF ERR$K_INQUIRY_LENGTH ; Length of packet containing INQUIRY data .=ERR$K_COMMAND_LENGTH $DEF ERR$EXTND_SENSE_DATA .BLKB 18 ; Extended sense data $DEF ERR$K_EXTND_SENSE_LENGTH ; Length of packet containing extended ; sense data .=ERR$K_COMMAND_LENGTH ASSUME MODE_DESC$S_FIELD_DESCRIPTOR EQ 40 $DEF ERR$A_MODE_SENSE_DATA .BLKB 240 ; Mode sense data $DEF ERR$K_MODE_SENSE_LENGTH ; Length of packet containing mode ; sense data .=ERR$K_COMMAND_LENGTH $DEF ERR$A_REASSIGN_BLOCK_DATA .BLKB 8 ; Reassign block data $DEF ERR$K_REASSIGN_BLOCK_LENGTH ; Length of packet containing reassign ; block data $DEFEND ERROR_PACKETS ; ; Define storage offsets for format parameters in non-paged pool ; .SYMBOL_ALIGNMENT QUAD $DEFINI DIAG_DESCRIPTOR $DEF DSC_OPCODE .BLKL 1 ; Opcode DSC$C_PASSTHRU = 1 ; Passthru is only supported function $DEF DSC_FLAGS .BLKL 1 ; Operation flags $VIELD DSC,0,< ,- ; Direction (1=datain .vs. 0=dataout) ,- ; Enable Disconnect ,- ; Enable Synchronous > ; Disable Port Retry $DEF DSC_CMDADR .BLKL 1 ; SCSI command buffer address $DEF DSC_CMDLEN .BLKL 1 ; SCSI command buffer length $DEF DSC_DATADR .BLKL 1 ; Data buffer address $DEF DSC_DATLEN .BLKL 1 ; Data buffer length $DEF DSC_PADCNT .BLKL 1 ; Pad count $DEF DSC_PHSTMO .BLKL 1 ; SCSI Bus phase change timeout $DEF DSC_DSCTMO .BLKL 1 ; SCSI Bus disconnect duration $DEF DSC_RESERVED .BLKB <60-.> ; Reserved $DEF DSC_LENGTH ; Length of generic SCSI descriptor $DEFEND DIAG_DESCRIPTOR ; ; Define offset for DK_ALLOC_POOL standard header ; $DEFINI DK_POOL_HEADER $DEF DK$DYN$PS_UCB .BLKL 1 ; Pointer to requesting UCB $DEF DK$DYN$PS_KPB .BLKL 1 ; Pointer to requesting KPB $DEF DK$DYN$W_SIZE .BLKW 1 ; Size of pool allocation requested $DEF DK$DYN$B_TYPE .BLKB 1 ; Allocation type (DYN$C_SCSICLS) $DEF DK$DYN$B_SUBTYPE .BLKB 1 ; Allocation subtype (DYN$C_MISC) $DEF DK$DYN$L_ALLOC_LEN .BLKL 1 ; Allocation length $DEF DK$DYN$C_LENGTH ; Length of DK non-paged pool header $DEFEND DK_POOL_HEADER .PAGE .SBTTL + .SBTTL + MACRO DEFINITIONS .SBTTL + .SBTTL MEDIA - MSCP media identifier to VMS device type conversion ;+ ; MEDIA - modified version of the macro used in DUTUSUBS.MAR (DUDRIVER) ; ; Functional description: ; ; This macro produces one entry in the MSCP media identifier to VMS ; device type conversion table. ; ; Parameters: ; ; dd the two character prefered device controller name ( the DD ; part of DDCn ) ; devnam the hardware device name ( e.g. RA81 ) ; dtname if DT$_'devnam' is not a legal VMS device type, this parameter ; gives the correct VMS device type for the device ( should be ; used only when DT$_'devnam' is not correct ) ;- .MACRO MEDIA DD, DEVNAM, DTNAME $$BEGIN$$=-1 $$MEDIA$$=0 $$S$$=27 .IRPC $$L$$,
$$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 .PAGE .SBTTL SCSI_DEV_TYPES - Build SCSI device table ;+ ; SCSI_DEV_TYPES ; ; This macro builds a table of pre-defined SCSI device types. During unit ; initialization, an inquiry command is sent to the target which returns ; 8 bytes of ID string. The table is then scanned for a matching ID. If one ; is found, information for that entry is copied into the UCB, including the ; device type, media ID, disconnect/synchronous flags, and various timeout ; values. If no matching entry is found, the device is assumed to be a ; "generic" SCSI disk, and the entry for generic devices is used. Each entry ; in the device type table has the following format: ; $DEFINI SCSI_DEVICE_ENTRY $DEF DTYP_TYPE .BLKB 1 ; VMS Device type $DEF DTYP_FLAGS .BLKB 1 ; Disc/synch flags $DEF <> .BLKW 1 ; Pad for alignment $DEF DTYP_ID_STRING .BLKL 2 ; ID String $DEF DTYP_MINREV .BLKL 1 ; Min. Revision level $DEF DTYP_MEDIA_ID .BLKL 1 ; Media Id $DEF DTYP_PHASE_TMO .BLKW 1 ; Phase Change Timeout $DEF DTYP_DISC_TMO .BLKW 1 ; Disconnect Timeout $DEF DTYP_TABLE_ENTRY_SIZE ; Size of SCSI Device Table Entry $DEFINI SCSI_DEVICE_ENTRY ; Each table entry is 24 bytes, which allows successive entries ; to be naturally aligned. ASSUME DTYP_TABLE_ENTRY_SIZE EQ 24 ; The table is terminated with a VMS device code of 0. ;- .MACRO SCSI_DEV_TYPES, LIST .ALIGN LONG .MACRO SCSI_DEV_TYPES1, ID_STRING, DEVICE_TYPE, MEDIA_ID,- MINIMUM_REVISION, DISCONNECT, SYNCHRONOUS,- PHASE_CHANGE_TIMEOUT, DISCONNECT_TIMEOUT .IF IDN , GENERIC GENERIC_SCSI_DISK: .ENDC ; Device type .BYTE DT$_'DEVICE_TYPE' ; Flags, including disconnect and synchronous $$$FLAGS = 0 .IIF IDN DISCONNECT, YES, $$$FLAGS = $$$FLAGS + 1 .IIF IDN SYNCHRONOUS, YES, $$$FLAGS = $$$FLAGS + 2 .BYTE $$$FLAGS ; Pad to longword align the entry .WORD 0 ; 8 character product ID string, padded with spaces .NCHR $$$STRLEN, .IIF GT $$$STRLEN-8, .ERROR ;Illegal SCSI product ID: ID_STRING $$$PADCNT = 8-$$$STRLEN .ASCII /ID_STRING/ .REPT $$$PADCNT .ASCII / / .ENDR ; Minimum revision level. This field is compared to the revision field returned ; in the inquiry data. If the device is out of rev, an error is logged. .ASCII /MINIMUM_REVISION/ ; Media ID field, derived from the device type if possible, otherwise supplied ; in the table. .IF DIF , GENERIC .IF B MEDIA_ID MEDIA , .IFF MEDIA , .ENDC .IFF MEDIA , , DT$_Generic_DK ; Media value for generic DK device .ENDC ; Phase change timeout $$$PHASE_CHANGE_TIMEOUT = DEFAULT_PHASE_CHANGE_TIMEOUT .IF DIF PHASE_CHANGE_TIMEOUT, DEFAULT $$$PHASE_CHANGE_TIMEOUT = PHASE_CHANGE_TIMEOUT .ENDC .WORD $$$PHASE_CHANGE_TIMEOUT ; Disconnect timeout $$$DISCONNECT_TIMEOUT = DEFAULT_DISCONNECT_TIMEOUT .IF DIF DISCONNECT_TIMEOUT, DEFAULT $$$DISCONNECT_TIMEOUT = DISCONNECT_TIMEOUT .ENDC .WORD $$$DISCONNECT_TIMEOUT .ENDM SCSI_DEV_TYPES1 .IRP ENTRY, SCSI_DEV_TYPES1 ENTRY .ENDR .ENDM SCSI_DEV_TYPES .PAGE .SBTTL SENSE_KEY - Build sense key to VMS status code translation table ;+ ; SENSE_KEY ; ; This macro is used to build a translation table of SCSI to VMS status codes. ; Each time extended sense information is returned by the target, the sense ; key is translated to a VMS status code using this table. The table will be ; scanned using the SCSI sense key as an index. ; ;- .MACRO SENSE_KEY, VMS_STATUS .LONG SS$_'VMS_STATUS' .ENDM SENSE_KEY .PAGE .SBTTL LOG_ERROR - Log a SCSI disk class driver error ;+ ; LOG_ERROR ; ; This macro logs a SCSI disk class driver error. The error type and VMS status ; code are placed in the appropriate UCB fields, and the LOG_ERROR routine is ; called, which does most of the work. ; Context: ; ; SCDRP/FORK thread ; IPL = Fork ; Fork lock held ; ;- .MACRO LOG_ERROR type,vms_status,ucb=R3,scdrp ;SF FF; PUSHR #^M ;SF FF; Save registers .IF NOT_BLANK,scdrp MOVL scdrp,R4 ;SF FF; Get SCDRP address .IFF CLRL R4 ;SS FF; No SCDRP address .ENDC .IF DIF ucb,R5 MOVL ucb,R5 ;SF FF; Get UCB address .ENDC MOVL R4,UCB$PS_SCDRP(R5) ;SF FF; for reg_dump only MOVL #SCSI$C_'type',- ;SF FF; Get error code UCB$L_ERROR_TYPE(R5) ;SF FF; MOVL vms_status,UCB$L_VMS_STATUS(R5) ;SF FF; And VMS Status code BSBW LOG_ERROR ;SF FF; Write an errorlog entry POPR #^M ;SF FF; Restore registers .ENDM LOG_ERROR .PAGE .SBTTL SCSI_ERROR_CODES - Define SCSI error codes, build error length table ;+ ; SCSI_ERROR_CODES ; ; This macro defines the class driver error codes and generates a table of ; error lengths used during errorlogging to determine the size of the errorlog ; packet to allocate. The table is indexed by the error type to find the ; length of the packet which is stored as a word. ;- .MACRO SCSI_ERROR_CODES, ERROR_LIST $$CODE_VALUE = 1 .MACRO SCSI_ERROR_CODES1 CODE, LEN SCSI$C_'CODE' = $$CODE_VALUE $$CODE_VALUE = $$CODE_VALUE + 1 .WORD ERR$K_'LEN' .ENDM SCSI_ERROR_CODES1 .IRP LIST_ENTRY, SCSI_ERROR_CODES1 LIST_ENTRY .ENDR .ENDM SCSI_ERROR_CODES .PAGE .SBTTL DISABLE_ERRLOG - Temporarily disable errorlogging .SBTTL REENABLE_ERRLOG - Reenable errorlogging ;+ ; DISABLE_ERRLOG ; REENABLE_ERRLOG ; ; Context: ; ; SCDRP Thread ; IPL = Fork ; Fork lock held ; ; This macros are used to disable and reenable errorlogging respectively. ; The DISABL_ERRLOG flag in the UCB is used to temporarily disable errorlogging ; when the class driver prepares to do something which is likely to cause an ; error that should be supressed. For example, when retrying reads or writes ; to blocks with [non-]recoverable errors, errorlogging is disabled so that ; just one error is logged. Since the disabling of errorlogging can be nested, ; the old value of the DISABL_ERRORLOG flag is saved in the local UCB stack. .MACRO DISABLE_ERRLOG ;S FF; PUSHL UCB$L_DK_FLAGS(R3) ;S FF; Save current flags value ASSUME UCB$V_DISABL_ERRLOG LT 8 ;S FF; BISB #UCB$M_DISABL_ERRLOG,- ;S FF; Temporarily disable errorlogging UCB$L_DK_FLAGS(R3) ;S FF; .ENDM DISABLE_ERRLOG .MACRO REENABLE_ERRLOG ;S FF; POPL UCB$L_DK_FLAGS(R3) ;S FF; Reenable errorlogging .ENDM REENABLE_ERRLOG .PAGE .SBTTL DK_ALLOC_SCDRP - Allocate an SCDRP ;+ ; DK_ALLOC_SCDRP ; ; This macro is used to allocate an SCDRP on the kernel process stack. ; ; INPUTS: ; R3 - UCB address ; R5 - Old SCDRP Address ; ; OUTPUTS: ; R5 - SCDRP address ; R0 - destroyed ; .MACRO DK_ALLOC_SCDRP PUSHL R5 ;F FF; Save Original SCDRP address PUSHL SCDRP$PS_KPB(R5) ;F FF; Save Original KPB Address SUBL3 #SCDRP$K_DK_LENGTH, SP, R5 ;F FF; Subtract size of SCDRP BICL #7, R5 ;F FF; Quadword align SCDRP SUBL #SCDRP$K_DK_LENGTH+8, SP ;F FF; Account for SCDRP + unalignment BSBW INIT_SCDRP ;S FF; Initialize allocated SCDRP MOVL SCDRP$K_DK_LENGTH+8(SP),- ;S FF; Copy Original KPB address SCDRP$PS_KPB(R5) ;S FF; ... to this SCDRP's KPB address MOVL SCDRP$K_DK_LENGTH+8+4(SP),- ;S FF; Copy Original SCDRP address SCDRP$PS_PREV_SCDRP(R5) ;S FF; ... to this SCDRP's PREV SCDRP .ENDM DK_ALLOC_SCDRP .SBTTL DK_DEALLOC_SCDRP - Deactivate an SCDRP ;+ ; DK_DEALLOC_SCDRP ; ; This macro deactivates an SCDRP by clearing the appropriate UCB field. ; A sanity check is performed to ensure that any map registers for this ; command have been deallocated. By default, deallocation of the SCDRP ; will occur implicitly via kernel process termination, although ; explicit deallocation may be requested by specifying CLRSTK=YES. ; ; INPUTS: ; R3 - UCB address ; R5 - SCDRP address ; ; OUTPUTS: ; none ; .MACRO DK_DEALLOC_SCDRP,CLRSTK=NO,?L1,?L2 ;S FF; .IF DEFINED DEBUG TSTL SCDRP$IS_ITEM_NUM(R5) ;S FF; Hanging on to any mapping regs? BNEQ L1 ;S FF; Branch if so TSTL SCDRP$IS_ITEM_CNT(R5) ;S FF; Hanging on to any mapping regs? BEQL L2 ;S FF; Branch if so L1: BUG_CHECK INCONSTATE,FATAL ;S FF; Should not deactivate an SCDRP .ENDC L2: .IF IDN , ;S FF; If explicit deallocation is required ADDL #SCDRP$K_DK_LENGTH+8+4,SP ;S FF; Clear SCDRP + Original KPB Address POPL R5 ;S FF; Restore Original SCDRP Address .ENDC .ENDM DK_DEALLOC_SCDRP .PAGE .SBTTL SCSI_CMD - Define a SCSI command packet ;+ ; SCSI_CMD ; ; This macro defines the contents of a SCSI command packet. Each SCSI command ; can have associated with it a DMA buffer used during the DATAIN/DATAOUT bus ; phases. A DMA length of zero indicates there is no DATA(IN/OUT) phase ; associated with this command (except in the case of a read/write SCSI command ; which is handled specially. The SETUP_CMD uses this information in preparing ; to send a SCSI command. The macro generates a label and the SCSI command ; information as follows: ; ; +-----------------------+ ; | SCSI cmd length | 1 byte ; +-----------------------+ ; | SCSI cmd bytes | n bytes ; +-----------------------+ ; | DMA buffer length | 2 bytes ; +-----------------------+ ; | DMA direction | 1 byte ; +-----------------------+ ; ; DMA direction is defined as: 0=write, 1=read. ;- .MACRO SCSI_CMD, NAME, CMD_BYTES, DMA_LEN=0, DMA_DIR=READ .ALIGN QUAD CMD_'NAME':: $$$BYTE_COUNT=0 .IRP CMD_BYTE, $$$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 ;+ ; This function will minimize to the smaller of val1 and val2. ; If val1 is greater than val2, then assign val1 the value of val2. ;- .MACRO MINUM val1,val2, ?l10 CMPL 'val1','val2' BLEQ l10 MOVL 'val2','val1' l10: .ENDM MINUM ;+ ; $ARG_DEF - Define a Routine's Input Parameters. ;- .MACRO $ARG_DEF argument_list argument_offset = 4 .IRP argument, argument = argument_offset argument_offset = argument_offset + 4 .ENDR .ENDM $ARG_DEF ; ; Macros used for RAID management ; ; IF_RAID, IF_NOT_RAID ; R3 - UCB ; .MACRO IF_RAID, LABEL BBS #UCB$V_RAID,- ;S FF UCB$L_DK_FLAGS(R3),LABEL .ENDM IF_RAID .MACRO IF_NOT_RAID, LABEL BBC #UCB$V_RAID,- ;S FF UCB$L_DK_FLAGS(R3),LABEL .ENDM IF_NOT_RAID ; ; Macros for Command-Queue management ; ; IF_CMDQ, IF_NOT_CMDQ ; R3 ~ UCB ; .MACRO IF_CMDQ,LABEL,UCB=R3 ;SF FF .BRANCH_LIKELY BBS #UCB$V_CMDQ,- ;SF FF; Command queueing support UCB$L_DK_FLAGS(UCB),LABEL ;SF FF; .ENDM IF_CMDQ .MACRO IF_NOT_CMDQ,LABEL,UCB=R3 ;SF FF .BRANCH_UNLIKELY BBC #UCB$V_CMDQ,- ;SF FF No Command queueing support UCB$L_DK_FLAGS(UCB),LABEL ;SF FF; .ENDM IF_NOT_CMDQ ; ; Macros for UCB busy single threaded management ; ; SET_SINGLE ; CLR_SINGLE ; TST_SINGLE ; R3 ~ UCB .MACRO SET_SINGLE flag,ucb,lab,scdrp,drain_cancel,?l1,?l2,?l3 ; flag = One of the single thread states, define in UCB$L_CLASS_BUSY ; ucb = The current UCB address ; scdrp = The current SCDRP address ; lab = (optional) label to transfer control to if bit is already set ; drain_cancel = (optional) label to transfer control to if the macro ; IF_CANCEL detects a canceled drain .IF DEFINED TRACE_CB PUSHL R7 ;S FF; Save register MOVL UCB$L_RING_POINTER(ucb), R7 ;S FF; Get pointer to ring buffer MOVL #UCB$M_CB_'flag, TRB$L_TYPE(R7) ;S FF; Save operation type TRACE_CB = TRACE_CB + 1 .PRINT TRACE_CB ;S FF; Current value of tracer MOVL #TRACE_CB, TRB$L_PC(R7) ;S FF; Save caller's marker MOVL UCB$L_CLASS_BUSY(ucb),- ;S FF; Save class busy bits TRB$L_CLASS_BUSY(R7) ;S FF; MOVL UCB$L_STS(ucb),- ;S FF; Save UCB status bits TRB$L_STS(R7) MOVAL TRB$C_LENGTH(R7),- ;S FF; Update ring pointer UCB$L_RING_POINTER(ucb) DECL UCB$L_RING_COUNTER(ucb) ;S FF; Update cntr BNEQ l3 ;S FF; Wrap? MOVL #1024, UCB$L_RING_COUNTER(ucb) ;S FF; If yes, reinit MOVL UCB$L_TOP_OF_RING(ucb),- ;S FF; UCB$L_RING_POINTER(ucb) ;S FF; l3: POPL R7 ;S FF; Restore register .ENDC .IF BLANK lab BBSS #UCB$V_CB_'flag,- ;SF FF; Single stream this I/O request UCB$L_CLASS_BUSY(ucb),l2 ;SF FF; BRB l1 ;SF FF; l2: BUG_CHECK INCONSTATE,FATAL ;SF FF; Busy bit already set. .IFF BBSS #UCB$V_CB_'flag,- ;SF FF; Single stream this I/O request UCB$L_CLASS_BUSY(ucb),lab ;SF FF; .ENDC l1: BISL #UCB$M_BSY,UCB$L_STS(ucb) ;SF FF; Set UCB busy bit .IF NOT_BLANK drain_cancel PUSHL scdrp ;S FF; Address of current SCDRP PUSHL ucb ;S FF; Address of current SCDRP CALLS #2, DK_DRAIN_IO ;S FF; Wait for I/O's to DRAIN ; IF_CANCEL , ;S FF; Check for cancel under I/O drain .ENDC .ENDM SET_SINGLE .MACRO CLR_SINGLE flag,ucb,lab,?l1,?l2,?l3 ; flag = One of the single thread states, define in UCB$L_CLASS_BUSY ; ucb = The current UCB address (default = R5) ; lab = (optional) label to transfer control to if bit is already set .IF DEFINED TRACE_CB PUSHL R7 ;S FF; Save register MOVL UCB$L_RING_POINTER(ucb), R7 ;S FF; Get pointer to ring buffer MOVL #^cUCB$M_CB_'flag,TRB$L_TYPE(R7);S FF; Save operation type TRACE_CB = TRACE_CB + 1 .PRINT TRACE_CB ;S FF; Current value of tracer MOVL #TRACE_CB, TRB$L_PC(R7) ;S FF; Save caller's marker MOVL UCB$L_CLASS_BUSY(ucb),- ;S FF; Save class busy bits TRB$L_CLASS_BUSY(R7) ;S FF; MOVL UCB$L_STS(ucb),- ;S FF; Save UCB status bits TRB$L_STS(R7) MOVAL TRB$C_LENGTH(R7),- ;S FF; Update ring pointer UCB$L_RING_POINTER(ucb) DECL UCB$L_RING_COUNTER(ucb) ;S FF; Update cntr BNEQ l3 ;S FF; Wrap? MOVL #1024, UCB$L_RING_COUNTER(ucb) ;S FF; If yes, reinit MOVL UCB$L_TOP_OF_RING(ucb),- ;S FF; UCB$L_RING_POINTER(ucb) ;S FF; l3: POPL R7 ;S FF; Restore register .ENDC .IF BLANK lab BBCC #UCB$V_CB_'flag,- ;SF FF; Single stream this I/O request UCB$L_CLASS_BUSY(ucb), l2 ;SF FF; BRB l1 ;SF FF; l2: BUG_CHECK INCONSTATE,FATAL ;SF FF; Busy bit already set. .IFF BBCC #UCB$V_CB_'flag,- ;SF FF; Single stream this I/O request UCB$L_CLASS_BUSY(ucb), lab ;SF FF; .ENDC l1: .ENDM CLR_SINGLE .MACRO TST_SINGLE label,ucb=R5,flag_reg=R1,?l3 ; label = label to transfer control to if any bits are set ; ucb = The current UCB address (default = R5) ; flag_reg = (optional) register to place current value of UCB$L_CLASS_BUSY; .IF DEFINED TRACE_CB PUSHL R7 ;S FF; Save register MOVL UCB$L_RING_POINTER(ucb), R7 ;S FF; Get pointer to ring buffer MOVL #0,TRB$L_TYPE(R7) ;S FF; Save operation type TRACE_CB = TRACE_CB + 1 .PRINT TRACE_CB ;S FF; Current value of tracer MOVL #TRACE_CB, TRB$L_PC(R7) ;S FF; Save caller's marker MOVL UCB$L_CLASS_BUSY(ucb),- ;S FF; Save class busy bits TRB$L_CLASS_BUSY(R7) ;S FF; MOVL UCB$L_STS(ucb),- ;S FF; Save UCB status bits TRB$L_STS(R7) MOVAL TRB$C_LENGTH(R7),- ;S FF; Update ring pointer UCB$L_RING_POINTER(ucb) DECL UCB$L_RING_COUNTER(ucb) ;S FF; Update cntr BNEQ l3 ;S FF; Wrap? MOVL #1024, UCB$L_RING_COUNTER(ucb) ;S FF; If yes, reinit MOVL UCB$L_TOP_OF_RING(ucb),- ;S FF; UCB$L_RING_POINTER(ucb) ;S FF; l3: POPL R7 ;S FF; Restore register .ENDC BICL3 #UCB$M_CB_NOCMDQ,- ;S FF; Single threaded operation needed UCB$L_CLASS_BUSY(ucb), flag_reg ;S FF; ( BNEQ label ;S FF; thread busy branch. .ENDM TST_SINGLE .MACRO TRACE_SINGLE trace_flag,ucb=R5,?l3 .IF DEFINED TRACE_CB PUSHL R7 ;S FF; Save register MOVL UCB$L_RING_POINTER(ucb), R7 ;S FF; Get pointer to ring buffer MOVL #^X'trace_flag',TRB$L_TYPE(R7) ;S FF; Save operation type TRACE_CB = TRACE_CB + 1 .PRINT TRACE_CB ;S FF; Current value of tracer MOVL #TRACE_CB, TRB$L_PC(R7) ;S FF; Save caller's marker MOVL UCB$L_CLASS_BUSY(ucb),- ;S FF; Save class busy bits TRB$L_CLASS_BUSY(R7) ;S FF; MOVL UCB$L_STS(ucb),- ;S FF; Save UCB status bits TRB$L_STS(R7) MOVAL TRB$C_LENGTH(R7),- ;S FF; Update ring pointer UCB$L_RING_POINTER(ucb) DECL UCB$L_RING_COUNTER(ucb) ;S FF; Update cntr BNEQ l3 ;S FF; Wrap? MOVL #1024, UCB$L_RING_COUNTER(ucb) ;S FF; If yes, reinit MOVL UCB$L_TOP_OF_RING(ucb),- ;S FF; UCB$L_RING_POINTER(ucb) ;S FF; l3: POPL R7 ;S FF; Restore register .ENDC .ENDM TRACE_SINGLE .MACRO TRACE_SINDATA trace_flag,clsbsy,sts,ucb=R5,?l3 .IF DEFINED TRACE_CB PUSHL R7 ;S FF; Save register MOVL UCB$L_RING_POINTER(ucb), R7 ;S FF; Get pointer to ring buffer MOVL #^X'trace_flag',TRB$L_TYPE(R7) ;S FF; Save operation type TRACE_CB = TRACE_CB + 1 .PRINT TRACE_CB ;S FF; Current value of tracer MOVL #TRACE_CB, TRB$L_PC(R7) ;S FF; Save caller's marker MOVL clsbsy, TRB$L_CLASS_BUSY(R7) ;S FF; Save what was specified ;S FF; for class busy bits MOVL sts, TRB$L_STS(R7) ;S FF; Save what was specified ;S FF; for UCB status bits MOVAL TRB$C_LENGTH(R7),- ;S FF; Update ring pointer UCB$L_RING_POINTER(ucb) DECL UCB$L_RING_COUNTER(ucb) ;S FF; Update cntr BNEQ l3 ;S FF; Wrap? MOVL #1024, UCB$L_RING_COUNTER(ucb) ;S FF; If yes, reinit MOVL UCB$L_TOP_OF_RING(ucb),- ;S FF; UCB$L_RING_POINTER(ucb) ;S FF; l3: POPL R7 ;S FF; Restore register .ENDC .ENDM TRACE_SINDATA .MACRO SETUP_CMD cmd=r2,ucb=R3,pdt=R4,scdrp_addr=R5 PUSHL 'scdrp_addr' ;S FF; PUSHL 'pdt' ;S FF; PUSHL 'ucb' ;S FF; PUSHL 'cmd' CALLS #4,SETUP_CMD ;S FF; Setup the SCSI command .ENDM SETUP_CMD .MACRO SEND_COMMAND_ORDERED ucb=R3,pdt=R4,scdrp_addr=R5 MOVL #SCDRP$K_QCHAR_ORDERED,- ;S FF; Send this as an ordered command. SCDRP$IS_QUEUE_CHAR(scdrp_addr) PUSHL 'scdrp_addr' ;S FF; PUSHL 'pdt' ;S FF; PUSHL 'ucb' ;S FF; CALLS #3,SEND_COMMAND ;S FF; Send the SCSI command .ENDM SEND_COMMAND_ORDERED .MACRO CLEANUP_CMD pdt=R4,scdrp_addr=R5 PUSHR #^M ;S FF; Save R0, R1 PUSHL 'scdrp_addr' ;S FF; PUSHL 'pdt' ;S FF; CALLS #2,CLEANUP_CMD ;S FF; Cleanup the SCSI command POPR #^M ;S FF; Restore R0, R1 .ENDM CLEANUP_CMD .MACRO ONCE_DK_DEBUG FLAG,?label .IF DEFINED DEBUG BBCC # DK$V_'FLAG',DK$DEBUG_FLAGS,label JSB G^ INI$BRK label: .ENDC .ENDM .MACRO DK_DEBUG FLAG,?label .IF DEFINED DEBUG BBC # DK$V_'FLAG',DK$DEBUG_FLAGS,label JSB G^ INI$BRK label: .ENDC .ENDM ; IF_CANCEL ; ; This macro checks to see whether the MSCP server has ; marked the IRP for abort. If so, it terminates the ; request with SS$_CANCEL status. Only SCDRPs with ; a nonzero value in SCDRP$L_IRP will be checked; ; therefore, private SCDRPs (such as for REASSIGN and ; READ_LONG) will not be canceled, since those SCDRPs ; have a zero value in SCDRP$L_IRP. In order to terminate ; such operations, this macro should be invoked immediately ; before even allocating a private SCDRP of this kind, ; so that the operation may be cancelled via the parent ; SCDRP. .MACRO IF_CANCEL CANCELLED,SCDRP=R5,?L1 MOVL SCDRP,R0 ;S FF; Copy SCDRP address MOVL SCDRP$L_IRP(R0),R1 ;S FF; Get IRP address BEQL L1 ;S FF; Branch if no IRP found .BRANCH_LIKELY ;S FF; IRP has been found BBC #IRP$V_SRV_ABORT,- ;S FF; Has quick kill been IRP$L_STS(R1),L1 ;S FF; requested? Br if no MOVL #SS$_CANCEL,R0 ;S FF; Else, do the deed CLRL R1 ;S FF; Clear scratch reg BRW CANCELLED ;S FF; And leave L1: ;S FF; .ENDM IF_CANCEL ;S FF; .PAGE .SBTTL + .SBTTL + DRIVER TABLES .SBTTL + .SBTTL Driver prologue table ;+ ; Driver prologue table ; ; This table provides various information about the driver such as its name ; and length, and causes initialization of various fields in the I/O database ; when the driver is loaded. ;- DPTAB - ; DPT-creation macro STEP=2,- ; Driver is Step 2 ADAPTER=NULL,- ; Adapter type UCBSIZE=,- ; Length of UCB NAME=DKDRIVER,- ; Driver name BT_ORDER=5000,- ; Insure proper init order SMP=YES,- ; Driver runs in SMP environment 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 DEV$M_CLU> ; Cluster Accessable devices DPT_STORE UCB,UCB$B_DEVCLASS,B,DC$_DISK ; Sample device class DPT_STORE UCB,UCB$W_DEVBUFSIZ,W,IOC$C_DISK_BLKSIZ ; Default buffer size DPT_STORE UCB,UCB$L_ERTCNT,L,16 ; Error retry count DPT_STORE UCB,UCB$L_ERTMAX,L,16 ; Max error retry count DPT_STORE UCB,UCB$L_DK_FLAGS,L,0 ; Initialize flags field DPT_STORE UCB,UCB$L_ERR_MASK,L,0 ; Initialize error mask field DPT_STORE UCB,UCB$L_HW_REV,L,0 ; Initialize HW revision level DPT_STORE UCB,UCB$L_DEVSTS,L,- ; Set no logical to physical UCB$M_NOCNVRT ; block number conversion DPT_STORE UCB,UCB$L_STS,L,- ; Say we support EXFUNC bit UCB$M_EXFUNC_SUPP ; in $QIO interface DPT_STORE UCB,UCB$L_DISABLE_DDR,L,1 ; Disable DDR by default DPT_STORE UCB,UCB$L_READ_XLEN_HIST,L,0 ; Make sure these are 0 DPT_STORE UCB,UCB$L_WRITE_XLEN_HIST,L,0 ; Make sure these are 0 DPT_STORE UCB,UCB$L_XLEN_HIST,L,0 ; Make sure these are 0 DPT_STORE UCB,UCB$L_QDEPTH,W,8 ; Default qdepth DPT_STORE UCB,UCB$L_QDEPTH_TURNS,L,1 ; Set counter DPT_STORE REINIT ; Start of reload ; initialization table DPT_STORE END ; End of initialization ; tables .PAGE .SBTTL Driver dispatch table ;+ ; Driver dispatch table ; ; This table defines the entry points into the driver. ;- DDTAB - ; DDT-creation macro DEVNAM=DK,- ; Name of device FUNCTB=DK_FUNCTABLE,- ; FDT address KP_STACK_SIZE=8192,- ; Size of kernel process stack KP_REG_MASK=KPREG$K_HLL_REG_MASK,-; Kernel process reg save mask START=DK_STARTIO,- ;F FF; Caller of Start I/O routine KP_STARTIO=DK_KP_STARTIO,- ;C FF; Start I/O routine ALTSTART=DK_ALTSTARTIO,- ;F FF; Altstart I/O routine CTRLINIT=DK_CTRL_INIT,- ;P X; Controller init routine UNITINIT=DK_UNIT_INIT,- ;P X; Unit init routine MNTVER=DK_MOUNT_VER,- ;F FF; Mount verification routine CANCEL=DK_CANCEL,- ;F FF; Cancel I/O routine REGDMP=DK_REG_DUMP ;? ??; Register dump routine .PAGE .SBTTL Function decision table ;+ ; Function decision table ; ; This table lists the QIO function codes implemented by the driver and the ; preprocssing routines used by each function. ;- FDT_INI DK_FUNCTABLE FDT_BUF <- ; Buffered I/O functions ACCESS,- ; Access file and/or find directory ACPCONTROL,- ; ACP control function AVAILABLE,- ; Available (rewind/nowait clear valid) CREATE,- ; Create file and/or directory entry DEACCESS,- ; Deaccess file DELETE,- ; Delete file and/or directory entry DRVCLR,- ; Driver clear MODIFY,- ; Modify file attributes MOUNT,- ; Mount volume NOP,- ; No operation PACKACK,- ; Pack acknowledge DSE,- ; Data Security Erase SEEK,- ; Seek SENSEMODE,- ; Sense mode SENSECHAR,- ; Sense characteristics UNLOAD> ; Unload volume FDT_ACT ACP_STD$ACCESS,<- ; Access & create file or directory ACCESS,- CREATE> FDT_ACT ACP_STD$DEACCESS,<- ; Deaccess file DEACCESS> FDT_ACT ACP_STD$MODIFY,<- ; set for dismounts ACPCONTROL,- ; for files 11 dismounts DELETE,- ; Delete file and/or directory entry MODIFY> ; Modify file attributes FDT_ACT ACP_STD$MOUNT,<- MOUNT> ; Mount volume FDT_ACT ACP_STD$READBLK,<- ; Read functions READLBLK,- ; Read logical block forward READPBLK,- ; Read physical block forward ; READTRACKD,- ; Read track descriptor ; READHEAD,- ; Read track data and headers READVBLK> ; Read virtual block FDT_ACT ACP_STD$WRITEBLK,<- ; Write functions WRITECHECK> ; Write check FDT_ACT DK_SHAD_WRITE,<- ;U A; Check write to shadow set mbr WRITELBLK,- ; Write LOGICAL Block WRITEPBLK,- ; Write Physical Block WRITEVBLK> ; Write VIRTUAL Block FDT_ACT EXE_STD$SENSEMODE,<- ; Sense functions SENSECHAR,- ; Sense characteristics SENSEMODE> ; Sense mode FDT_ACT EXE_STD$SETMODE,<- ; FDT set mode routine SETCHAR,- ; for set chars. and SETMODE> ; set mode. FDT_ACT EXE_STD$LCLDSKVALID,<- ; Local disk valid functions UNLOAD,- ; Unload volume AVAILABLE,- ; Unit available PACKACK> ; Pack acknowledge FDT_ACT EXE_STD$ONEPARM,<- ; Single parameter functions FORMAT> ; Format floppy diskette FDT_ACT EXE_STD$ZEROPARM,<- ; Zero parameter functions NOP> ; NOP FDT_ACT DK_AUDIO,<- ;U A; CD-ROM Audio FDT entry (55) AUDIO> ; FDT_ACT DK_DSE,<- ;U A; Data Security Erase FDT Routine DSE> ; FDT_ACT DK_DIAGNOSE,<- ;U A; Special pass-through function DIAGNOSE> ; FDT_ACT DK_CRESHAD,<- ;U A; Create a shadow set virtual unit CRESHAD> FDT_ACT DK_REMSHAD,<- ;U A; Remove a shadow set member REMSHAD> .PAGE .SBTTL DKDRIVER Data DRIVER_DATA ; ; SECOND = 10000000 ; Number of clock ticks in a second ONE_SECOND: .LONG < 1*SECOND>,0 ; 1 second TWO_SECONDS: .LONG < 2*SECOND>,0 ; 2 seconds THIRTY_SECONDS: .LONG <30*SECOND>,0 ; 30 seconds DATACHECK_SPTE::.LONG 0 ; SVA of SPTEs to double map user buffer DATACHECK_SVA:: .LONG 0 ; SVA mapped by this set of SPTEs TRACE_BUFFER:: .LONG 0 ; Trace buffer address MCN_DEF_STATUS: .LONG 0 ; Status buffer if no SCSI cmd issued. DUMMY_MODE_SENSE_CDB: ; For error logging purposes. .LONG ^XFF ; Dummy status .LONG 6 ; Command length .BYTE ^X1A,0,0,0,100,0 ; Command bytes .BLKB 2 ; Pad for alignment ; ; DKDriver DEBUG flags ; .WEAK DK$C_DEBUG_FLAGS DK$DEBUG_FLAGS::.LONG 0 \ DK$C_DEBUG_FLAGS ; DK Driver debug flags $VIELD DK, 0, < - < CTRL_INIT,,M>, - ; (%X0001) Controller Initialization < UNIT_INIT,,M>, - ; (%X0002) Unit Initialization < PORT_CALLBACK,,M>, - ; (%X0004) Port State Callback < MOUNT_VER,,M>, - ; (%X0008) Mount Verification < DK_STARTIO,,M>, - ; (%X0010) Start I/O Routine for KP Drivers < DK_KP_STARTIO,,M>, - ; (%X0020) DK Start Qio Entry Point < ILLEGAL_IO,,M>, - ; (%X0040) Illegal I/O < IO_NOP,,M>, - ; (%X0080) IO_NOP < DK_CANCEL,,M>, - ; (%X0100) DK_CANCEL < IO_PACKACK,,M>, - ; (%X0200) IO_PACKACK < IO_READPBLK,,M>, - ; (%X0400) IO_READPBLK < IO_DATACHECK,,M>, - ; (%X0800) IO_DATACHECK < RQST_SENSE,,M>, - ; (%X1000) Request Sense > .PAGE .SBTTL SCSI device types table ;+ ; SCSI_DEVICE_TABLE ; ; This table is used to translate the product ID field from the inquiry data ; to a VMS device type. Once the device type is determined, various information ; about the device such as disconnect and synchronous flags and timeout vaules ; is saved in the UCB. ;- SCSI_DEVICE_TABLE:: SCSI_DEV_TYPES <- ; ; ID string Dev type Media ID Min Rev Disc Synch Phas tmo Disc tmo ; --------- -------- -------- ------- ---- ----- -------- ---------- ; <, RZ22, , <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>,- <, RZ26L, RZL26, < >, 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>,- <, RRD43, , < >, YES, NO, DEFAULT, RRD42_DISC_TMO>,- <, RRD44, , < >, YES, NO, DEFAULT, RRD42_DISC_TMO>> 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 <- <, HSZ10, , < >, YES, YES, DEFAULT, DEFAULT>,- <, RZ28, , < >, YES, YES, DEFAULT, DEFAULT>,- <, RZ29, , < >, YES, YES, DEFAULT, DEFAULT>,- <, 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>> 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>> SCSI_DEV_TYPES <- <, HSX00, , < >, YES, YES, DEFAULT, DEFAULT>,- <, HSX01, , < >, YES, YES, DEFAULT, DEFAULT>,- <, HSZ15, , < >, YES, YES, DEFAULT, DEFAULT>,- <, RZ26M, RZM26, < >, YES, YES, DEFAULT, DEFAULT>,- <, RW504, , < >, YES, YES, DEFAULT, DEFAULT>,- <, RW510, , < >, YES, YES, DEFAULT, DEFAULT>,- <, RW514, , < >, YES, YES, DEFAULT, DEFAULT>,- <, RW516, , < >, YES, YES, DEFAULT, DEFAULT>> SCSI_DEV_TYPES <- <, RWZ52, , < >, YES, YES, DEFAULT, DEFAULT>,- <, RWZ53, , < >, YES, YES, DEFAULT, DEFAULT>,- <, RWZ54, , < >, YES, YES, DEFAULT, DEFAULT>,- <, RWZ31, , < >, YES, YES, DEFAULT, DEFAULT>> SCSI_DEV_TYPES <- <, EZ31, , < >, YES, YES, DEFAULT, DEFAULT>,- <, EZ32, , < >, YES, YES, DEFAULT, DEFAULT>,- <, EZ33, , < >, YES, YES, DEFAULT, DEFAULT>,- <, EZ34, , < >, YES, YES, DEFAULT, DEFAULT>,- <, EZ35, , < >, YES, YES, DEFAULT, DEFAULT>,- <, EZ31L, EZL31, < >, YES, YES, DEFAULT, DEFAULT>,- <, EZ32L, EZL32, < >, YES, YES, DEFAULT, DEFAULT>,- <, EZ33L, EZL33, < >, YES, YES, DEFAULT, DEFAULT>> .BYTE 0 ; End of table .PAGE .SBTTL SCSI command definition tables SCSI_CMD - NAME = TEST_UNIT_READY,- CMD_BYTES = <0, 0, 0, 0, 0, 0> SCSI_CMD - NAME = REZERO_UNIT,- CMD_BYTES = <1, 0, 0, 0, 0, 0> SCSI_CMD - NAME = REQUEST_SENSE,- CMD_BYTES = <3, 0, 0, 0, SENSE_ALLOC, 0>,- DMA_LEN = SENSE_ALLOC,- DMA_DIR = READ SCSI_CMD - NAME = FORMAT_UNIT,- CMD_BYTES = <4, 0, 0, 0, 0, 0>,- DMA_LEN = 512,- DMA_DIR = WRITE SCSI_CMD - NAME = REASSIGN_BLOCKS,- CMD_BYTES = <7, 0, 0, 0, 0, 0>,- DMA_LEN = 8,- DMA_DIR = WRITE SCSI_CMD - NAME = READ,- CMD_BYTES = <8, 0, 0, 0, 0, 0>,- DMA_LEN = -1 CMD_READ$R_LBA = 1 ; 21-bit Logical Block Address CMD_READ$B_XFER_COUNT = 4 ; 8-bit Transfer count SCSI_CMD - NAME = WRITE,- CMD_BYTES = <10, 0, 0, 0, 0, 0>,- DMA_LEN = -1 SCSI_CMD - NAME = EXTND_READ,- CMD_BYTES = <<^X28>, 0, 0, 0, 0, 0, 0, 0, 0, 0>,- DMA_LEN = -1 CMD_EXTND_READ$R_LBA = 2 ; 32-bit Logical Block Address CMD_EXTND_READ$W_XFER_COUNT = 7 ; 16-bit Transfer count SCSI_CMD - NAME = EXTND_WRITE,- CMD_BYTES = <<^X2A>, 0, 0, 0, 0, 0, 0, 0, 0, 0>,- DMA_LEN = -1 SCSI_CMD - NAME = SEEK,- CMD_BYTES = <11, 0, 0, 0, 0, 0> SCSI_CMD - NAME = INQUIRY,- CMD_BYTES = <18 , 0, 0, 0, 36, 0>,- DMA_LEN = 36,- DMA_DIR = READ SCSI_CMD - NAME = MODE_SELECT,- CMD_BYTES = <21 , <^X10>, 0, 0, 255, 0>,- DMA_LEN = 255,- DMA_DIR = WRITE CMD_MODE_SELECT$L_LENGTH = 4 ; Parameter List Length SCSI_CMD - NAME = MODE_SELECT_10,- CMD_BYTES = <85, <^X10>, 0,0,0,0,0, <^X04>,0, 0>,- DMA_LEN = 1024,- DMA_DIR = WRITE SCSI_CMD - NAME = RESERVE,- CMD_BYTES = <22, 0, 0, 0, 0, 0> SCSI_CMD - NAME = RELEASE,- CMD_BYTES = <23, 0, 0, 0, 0, 0> SCSI_CMD - NAME = COPY,- CMD_BYTES = <24, 0, 0, 0, 0, 0> SCSI_CMD - NAME = MODE_SENSE,- CMD_BYTES = <26, 0, <^X3F>, 0, 255, 0>,- DMA_LEN = 255,- DMA_DIR = READ CMD_MODE_SENSE$R_PAGE_CODE = 2 ; Mode Sense Page Code SCSI_CMD - NAME = MODE_SENSE_10,- CMD_BYTES = <90, 0, <^X3F>, 0,0,0,0, <^X04>,<^X00>,0>,- DMA_LEN = 1024,- DMA_DIR = READ SCSI_CMD - NAME = START_UNIT,- CMD_BYTES = <27, 1, 0, 0, 1, 0> SCSI_CMD - NAME = STOP_UNIT,- CMD_BYTES = <27, 1, 0, 0, 0, 0> SCSI_CMD - NAME = RECEIVE_DIAG,- CMD_BYTES = <28, 0, 0, 0, 0, 0>,- DMA_LEN = 512,- DMA_DIR = READ SCSI_CMD - NAME = SEND_DIAG,- CMD_BYTES = <29, 0, 0, 0, 0, 0>,- DMA_LEN = 512,- DMA_DIR = WRITE SCSI_CMD - NAME = MEDIA_REMOVAL,- CMD_BYTES = <30, 0, 0, 0, 0, 0> SCSI_CMD - NAME = READ_CAPACITY,- CMD_BYTES = <37, 0, 0, 0, 0, 0, 0, 0, 0, 0>,- DMA_LEN = 8,- DMA_DIR = READ SCSI_CMD - NAME = READ_LONG,- CMD_BYTES = <<^X3E>, 0, 0, 0, 0, 0, 0, 0, 0, 0>,- DMA_LEN = READ_LONG_DATA_LEN,- DMA_DIR = READ CMD_READ_LONG$L_LBA = 2 ; Logical Block Address CMD_READ_LONG$W_XFER_LENGTH = 7 ; Byte Transfer Length SCSI_CMD - NAME = WRITE_LONG,- CMD_BYTES = <<^X3F>, 0, 0, 0, 0, 0, 0, 0, 0, 0>,- DMA_LEN = WRITE_LONG_DATA_LEN,- DMA_DIR = WRITE .PAGE .SBTTL SCSI AUDIO command definition tables ;+ ; Beginning of CD-ROM specific SCSI Audio commands ;- SCSI_CMD - NAME = PAUSE,- CMD_BYTES = <75, 0, 0, 0, 0, 0, 0, 0, 0, 0> SCSI_CMD - NAME = RESUME,- CMD_BYTES = <75, 0, 0, 0, 0, 0, 0, 0, 1, 0> SCSI_CMD - NAME = PLAY_AUDIO10,- CMD_BYTES = <69, 0, 0, 0, 0, 0, 0, 0, 0, 0> CMD_PLAY_ADUIO10$B_LBA_0 = 5 ; Starting Logical Block Address CMD_PLAY_ADUIO10$B_LBA_1 = 4 CMD_PLAY_ADUIO10$B_LBA_2 = 3 CMD_PLAY_ADUIO10$B_LBA_3 = 2 CMD_PLAY_ADUIO10$B_XFR_0 = 8 ; Transfer Length CMD_PLAY_ADUIO10$B_XFR_1 = 7 SCSI_CMD - NAME = PLAY_TRACK,- CMD_BYTES = <72, 0, 0, 0, 3, 1, 0, 11, 1, 0> CMD_PLAY_TRACK$B_STARTING_TRACK = 4 ; Starting Track CMD_PLAY_TRACK$B_STARTING_INDEX = 5 ; Starting Track CMD_PLAY_TRACK$B_END_TRACK = 7 ; Ending Track CMD_PLAY_TRACK$B_END_INDEX = 8 ; Ending Track SCSI_CMD - NAME = REMOVAL,- CMD_BYTES = <30, 0, 0, 0, 0, 0, 0> CMD_REMOVAL$R_PREVENT = 4 ; <0,1> offset to bit to prevent removal SCSI_CMD - NAME = PLAY_AUDIO_MSF,- CMD_BYTES = <71, 0, 0, 0, 0, 0, 0, 0, 0, 0> CMD_PLAY_AUDIO$B_START_FRM = 5 ; Starting Frame CMD_PLAY_AUDIO$B_START_SEC = 4 ; Starting Seconds CMD_PLAY_AUDIO$B_START_MIN = 3 ; Starting Minutes CMD_PLAY_AUDIO$B_END_FRM = 8 ; Ending Frame CMD_PLAY_AUDIO$B_END_SEC = 7 ; Ending Seconds CMD_PLAY_AUDIO$B_END_MIN = 6 ; Ending Minutes SCSI_CMD - NAME = CD_MODE_SENSE,- CMD_BYTES = <26, 0, 0, 0, 0, 0>,- DMA_LEN = -1,- DMA_DIR = READ SCSI_CMD - NAME = CD_MODE_SELECT,- CMD_BYTES = <21 , <^X10>, 0, 0, 0, 0>,- DMA_LEN = -1,- DMA_DIR = WRITE CMD_CD_MODE_SELECT$L_LENGTH = 4 ; Parameter List Length SCSI_CMD - NAME = CD_READ_SUB,- CMD_BYTES = <66, 0, 64, 1, 0, 0, 0, 0, 48, 0>,- DMA_LEN = 48,- DMA_DIR = READ CMD_CD_READ_SUB$R_FLAGS = 1 CMD_CD_READ_SUB$B_SUB_DATA_FMT=3 SCSI_CMD - NAME = CD_READ_TOC,- CMD_BYTES = <67, 0, 0, 0, 0, 0, 0, 3, 22, 0>,- DMA_LEN = 804,- DMA_DIR = READ CMD_CD_READ_TOC$R_FLAGS = 1 CMD_CD_READ_TOC$B_START_TRACK = 6 .=<<.+7>& ^C7> ; Align data structure .PAGE .SBTTL Mode Sense macros ;+ ; The 10-byte Mode Sense command, while important because of the amount ; of data which newer devices are returning, has been problematic because ; some devices support it properly, some lie about it (they don't fail ; the command but they return too much data, too little data or bad data), ; and some do not support it at all but fail it in unusual ways. ; ; Because we can't count on devices to tell us whether or not they ; support 10-byte Mode Sense commands intelligently (meaning in ; accordance with our interpretation of the SCSI specification), we'll ; start off in DO_MODE_PAGE trying 10-byte commands, and will back off to ; 6-byte commands in the case of just about any Mode Sense failure ; ; The SCDRP's TENBYTE bit tells DO_MODE_PAGE whether to do 10-byte or ; 6-byte commands, and in the event of a successful command it tells us ; which command it used; however, in the event of a failing command the ; bit returns no useful information to us because it's cleared when ; DO_MODE_PAGE decides to back off to a 6-byte Mode Sense command but ; not reasserted if that fails. ; ; At the completion of the first successful DO_MODE_PAGE, we'll load the ; state of the SCDRP's TENBYTE bit into the UCB; the assumption is that ; if one 10-byte Mode Sense command succeeds, all will (should). ; ; Whenever a DO_MODE_PAGE fails we'll refresh the SCDRP bit from the UCB, ; just in case it backed off to the 6-byte command and that too failed; ; this keeps us consistenly trying 10-byte commands first if the first ; one, for a page which all devices are expected to support, completed ; successfully. ; ; All of this is to protect us against the day when some intelligent device ; decides to return more than 256 bytes of Mode Sense data for a single ; page, in which case a 6-byte command could not get it all. ;- ; Propagate the state of the UCB's TENBYTE bit to the SCDRP .MACRO LOAD_TENBYTE ucb=R3,scdrp=R5,?L1,?L2 BBC #UCB$V_TENBYTE,UCB$L_DK_FLAGS(ucb),L1 BBCS #SCDRP$V_FLAG_TENBYTE,SCDRP$L_SCSI_FLAGS(scdrp),L2 BRW L2 ; Oh well, we guessed wrong, it was set L1: BBCC #SCDRP$V_FLAG_TENBYTE,SCDRP$L_SCSI_FLAGS(scdrp),L2 L2: .ENDM LOAD_TENBYTE ; Propagate the state of the SCDRP's TENBYTE bit to the UCB .MACRO STORE_TENBYTE ucb=R3,scdrp=R5,?L1,?L2 BBC #SCDRP$V_FLAG_TENBYTE,SCDRP$L_SCSI_FLAGS(scdrp),L1 BBSS #UCB$V_TENBYTE,UCB$L_DK_FLAGS(ucb),L2 BRW L2 ; Oh well, we guessed wrong, it was clear L1: BBCC #UCB$V_TENBYTE,UCB$L_DK_FLAGS(ucb),L2 L2: .ENDM STORE_TENBYTE ; ; Descriptors for mode page fields, used by MODE SENSE operations. ; PREF = MODE_DESC$M_PREFERRED REQ = MODE_DESC$M_REQUIRED MISS = MODE_DESC$M_IFMISSING MISM = MODE_DESC$M_IFMISMATCH DPTR = MODE_DESC$M_DESPTR APTR = MODE_DESC$M_ACTPTR LAST = MODE_DESC$M_LAST MBOFF = MODE_DESC$C_MEDTYP_BOFF DBOFF = MODE_DESC$C_DEVPAR_BOFF DNBOFF= MODE_DESC$C_DENS_BOFF BBOFF = MODE_DESC$C_BLKLEN_BOFF .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 .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 .PAGE .SBTTL Sense key to VMS status translation table ;+ ; SENSE_KEY_TABLE ; ; This table is used to translate SCSI extended sense keys to VMS status codes. ; Each entry contains a longword; its low word is VMS status and its high word ; is 0. The table is indexed by sense key value. ; (SCSI-2 ref. == Table 7-13 (Sense Key Descriptions) ; - SENSE_KEY_TABLE:: ; VMS status ; SCSI sense key - Description ; ---------- ; --------------------------------------------------- SENSE_KEY NORMAL ; SCSI$C_NO_SENSE - No sense key information returned SENSE_KEY RECOVERR ; SCSI$C_RECOVERED_ERROR - Command completed with some recovery action SENSE_KEY DEVOFFLINE ; SCSI$C_NOT_READY - Logical unit cannot be accessed SENSE_KEY PARITY ; SCSI$C_MEDIUM_ERROR - Command failed with non-recovered medium error SENSE_KEY DRVERR ; SCSI$C_HARDWARE_ERROR - Command failed with non-recovered hardware error SENSE_KEY DRVERR ; SCSI$C_ILLEGAL_REQUEST - Illegal parameter in the command descriptor block SENSE_KEY MEDOFL ; SCSI$C_UNIT_ATTENTION - Removable medium change or target has been reset SENSE_KEY WRITLCK ; SCSI$C_DATA_PROTECT - Read/Write of medium failed due to protection SENSE_KEY DRVERR ; SCSI$C_BLANK_CHECK - Read/Write found blank medium, end-of-data, etc. SENSE_KEY MEDOFL ; SCSI$C_VENDOR_SPECIFIC - Vendor specific conditions reported SENSE_KEY MEDOFL ; SCSI$C_COPY_ABORTED - Copy, compare or Copy/Verify aborted due to error SENSE_KEY DRVERR ; SCSI$C_ABORTED_COMMAND - Targeted aborted current command SENSE_KEY NORMAL ; SCSI$C_EQUAL - Search Data command has equal comparison SENSE_KEY DRVERR ; SCSI$C_VOLUME_OVERFLOW - Buffered device has reach end of parition SENSE_KEY DATACHECK ; SCSI$C_MISCOMPARE - Source data command has unequal comparison SENSE_KEY MEDOFL ; SCSI$C_RESERVED - Reserved .PAGE .SBTTL Queue Depth Histogram Table ; ; Queue Depth - Histogram of optimal queue depths based on past I/O performance ; QDEPTH_TABLE: .LONG 16 ; 0 - 3 .LONG 14 ; 4 - 7 .LONG 12 ; 8 - 11 .LONG 11 ; 12 - 15 .LONG 10 ; 16 - 19 .LONG 9 ; 20 - 23 .LONG 8 ; 24 - 27 .LONG 7 ; 28 - 31 .LONG 6 ; 32 - 35 .LONG 5 ; 36 - 39 .LONG 5 ; 40 - 43 .LONG 5 ; 44 - 47 .LONG 5 ; 48 - 51 .LONG 5 ; 52 - 55 .LONG 5 ; 56 - 59 .LONG 5 ; 60 - 63 .LONG 5 ; 64 - 67 .LONG 4 ; 68 - 71 .LONG 4 ; 72 - 75 .LONG 4 ; 76 - 79 .LONG 4 ; 80 - 83 .LONG 4 ; 84 - 87 .LONG 4 ; 88 - 91 .LONG 4 ; 92 - 95 .LONG 3 ; 96 - 99 .LONG 3 ; 100 - 103 .LONG 3 ; 104 - 107 .LONG 3 ; 108 - 111 .LONG 3 ; 112 - 115 .LONG 3 ; 116 - 119 .LONG 3 ; 120 - 123 .LONG 3 ; 124 - 127+ .PAGE .SBTTL Misc. SCSI data tables ;+ ; SCSI_ERROR_LEN_TAB ; ; This table is indexed by the SCSI error type and specifies the length of ; the errorlog packet. ;- SCSI_ERROR_LEN_TAB:: SCSI_ERROR_CODES <- ,- ,- ,- ,- ,- ,- > ; ; Bitmask of error types that can be logged more than once ; referenced in LOG_ERROR. ; DUPLICATE_ERR_MASK: .LONG - <1@SCSI$C_MAP_BUFFER_ERROR>!- <1@SCSI$C_SEND_CMD_ERROR>!- <1@SCSI$C_EXTND_SENSE_DATA>!- <1@SCSI$C_REASSIGN_BLOCK> ; This table is used to convert between the vendor specific (RX26) ; MEDIA_TYPE field in the MODE_SENSE header and the IRP$L_MEDIA value ; MODE_SENSE_MEDIA_TYPE_TABLE: .BYTE ^X00 ; default or unknown .BYTE ^X80 ; DD (double density) .BYTE ^X81 ; HD (high density) .BYTE ^X82 ; ED (extended density) MODE_SENSE_MEDIA_TYPE_MAX =<.-MODE_SENSE_MEDIA_TYPE_TABLE-1> ; index into this table with irp$l_media-1 ; Note tables is biased to exclude (SCSI$FMT$R_PAGE_CODE, SCSI$FMT$B_PAGE_LENGTH) ; MODE_SELECT_FORMAT_TABLE: .LONG MODE_SELECT_FORMAT_RX26_DD-MODE_SELECT_FORMAT_TABLE-SCSI$FMT$W_TRACKS .LONG MODE_SELECT_FORMAT_RX26_HD-MODE_SELECT_FORMAT_TABLE-SCSI$FMT$W_TRACKS .LONG MODE_SELECT_FORMAT_RX26_ED-MODE_SELECT_FORMAT_TABLE-SCSI$FMT$W_TRACKS ; ; index into this table with irp$l_media-1 ; MODE_SELECT_FLEXIBLE_TABLE: .LONG MODE_SELECT_FLEXIBLE_RX26_DD-MODE_SELECT_FLEXIBLE_TABLE-SCSI$FLX$W_TRANSFER_RATE .LONG MODE_SELECT_FLEXIBLE_RX26_HD-MODE_SELECT_FLEXIBLE_TABLE-SCSI$FLX$W_TRANSFER_RATE .LONG MODE_SELECT_FLEXIBLE_RX26_ED-MODE_SELECT_FLEXIBLE_TABLE-SCSI$FLX$W_TRANSFER_RATE MODE_SELECT_FORMAT_RX26_HD: MODE_SELECT_FORMAT_RX23_18: .BYTE 0,0 ; tracks per zone .BYTE 0,0 ; alt sectors per zone .BYTE 0,0 ; alt tracks per zone .BYTE 0,0 ; alt tracks per logical unit .BYTE 0,18 ; sectors per track ffh,ffh .BYTE 2,0 ; bytes per sector .BYTE 0,1 ; interleave .BYTE 0,0 ; track skew .BYTE 0,0 ; cylinder skew .BYTE ^xA0 ; SSEC, HSEC, RMB, SURF, reserved_4 10h .BYTE 0,0,0 ; reserved MODE_SELECT_FLEXIBLE_RX26_HD: MODE_SELECT_FLEXIBLE_RX23_18: .BYTE ^x01,^xF4 ; transfer rate = 500 KHz ffh,ffh .BYTE 2 ; number of heads = 2 ffh .BYTE 18 ; sectors per track = 18 ffh .BYTE 2,0 ; data bytes per sector = 512 .BYTE 0,80 ; number of cylinders = 80 ffh,ffh .BYTE 0,80 ; starting write precomp = 80 .BYTE 0,80 ; starting reduced write current = 80 .BYTE 0,0 ; step rate = default ffh,ffh .BYTE 0 ; step pulse width = default .BYTE 0,0 ; head settle delay = default ffh,ffh .BYTE 5 ; motor on delay = 1/2 Second ffh .BYTE 30 ; motor off delay = 3 Seconds ffh .BYTE ^x40 ; trdy=0, ssn=1, mo=0 60h .BYTE 0 ; extra step pulses per cylinder = 0 1fh .BYTE 0 ; write compensation level unsupported .BYTE 0 ; head load delay unsupported .BYTE 0 ; head unload delay unsupported .BYTE ^x23 ; pin 34=2, pin 2=3 .BYTE ^x00 ; pin 4=0 .BYTE 0,0,0,0 ; reserved MODE_SELECT_FORMAT_RX26_DD: MODE_SELECT_FORMAT_RX23_9: .BYTE 0,0 ; tracks per zone .BYTE 0,0 ; alt sectors per zone .BYTE 0,0 ; alt tracks per zone .BYTE 0,0 ; alt tracks per logical unit .BYTE 0,9 ; sectors per track ffh,ffh .BYTE 2,0 ; bytes per sector .BYTE 0,1 ; interleave .BYTE 0,0 ; track skew .BYTE 0,0 ; cylinder skew .BYTE ^xA0 ; SSEC, HSEC, RMB, SURF, reserved_4 10h .BYTE 0,0,0 ; reserved MODE_SELECT_FLEXIBLE_RX26_DD: MODE_SELECT_FLEXIBLE_RX23_9: .BYTE ^x00,^xFA ; transfer rate = 250 KHz ffh,ffh .BYTE 2 ; number of heads = 2 ffh .BYTE 9 ; sectors per track = 9 ffh .BYTE 2,0 ; data bytes per sector = 512 .BYTE 0,80 ; number of cylinders = 80 ffh,ffh .BYTE 0,80 ; starting write precomp = 80 .BYTE 0,80 ; starting reduced write current = 80 .BYTE 0,0 ; step rate = default ffh,ffh .BYTE 0 ; step pulse width = default .BYTE 0,0 ; head settle delay = default ffh,ffh .BYTE 5 ; motor on delay = 1/2 Second ffh .BYTE 30 ; motor off delay = 3 Seconds ffh .BYTE ^x40 ; trdy=0, ssn=1, mo=0 60h .BYTE 0 ; extra step pulses per cylinder = 0 1fh .BYTE 0 ; write compensation level unsupported .BYTE 0 ; head load delay unsupported .BYTE 0 ; head unload delay unsupported .BYTE ^x23 ; pin 34=2, pin 2=3 .BYTE ^x00 ; pin 4=0 .BYTE 0,0,0,0 ; reserved MODE_SELECT_FORMAT_RX33_15: .BYTE 0,0 ; tracks per zone .BYTE 0,0 ; alt sectors per zone .BYTE 0,0 ; alt tracks per zone .BYTE 0,0 ; alt tracks per logical unit .BYTE 0,15 ; sectors per track .BYTE 2,0 ; bytes per sector .BYTE 0,1 ; interleave .BYTE 0,0 ; track skew .BYTE 0,0 ; cylinder skew .BYTE ^xA0 ; SSEC, HSEC, RMB, SURF, reserved_4 .BYTE 0,0,0 ; reserved MODE_SELECT_FLEXIBLE_RX33_15: .BYTE ^x01,^xF4 ; transfer rate = 500 KHz .BYTE 2 ; number of heads = 2 .BYTE 15 ; sectors per track = 15 .BYTE 2,0 ; data bytes per sector = 512 .BYTE 0,80 ; number of cylinders = 80 .BYTE 0,80 ; starting write precomp = 80 .BYTE 0,80 ; starting reduced write current = 80 .BYTE 0,0 ; step rate = default .BYTE 0 ; step pulse width = default .BYTE 0,0 ; head settle delay = default .BYTE 5 ; motor on delay = 1/2 Second .BYTE 30 ; motor off delay = 3 Seconds .BYTE ^x40 ; trdy=0, ssn=1, mo=0 .BYTE 0 ; extra step pulses per cylinder = 0 .BYTE 0 ; write compensation level unsupported .BYTE 0 ; head load delay unsupported .BYTE 0 ; head unload delay unsupported .BYTE ^x23 ; pin 34=2, pin 2=3 .BYTE ^x00 ; pin 4=0 .BYTE 0,0,0,0 ; reserved MODE_SELECT_FORMAT_RX26_ED: .BYTE 0,0 ; tracks per zone .BYTE 0,0 ; alt sectors per zone .BYTE 0,0 ; alt tracks per zone .BYTE 0,0 ; alt tracks per logical unit .BYTE 0,36 ; sectors per track ffh,ffh .BYTE 2,0 ; bytes per sector .BYTE 0,1 ; interleave .BYTE 0,0 ; track skew .BYTE 0,0 ; cylinder skew .BYTE ^xA0 ; SSEC, HSEC, RMB, SURF, reserved_4 10h .BYTE 0,0,0 ; reserved MODE_SELECT_FLEXIBLE_RX26_ED: .BYTE ^x03,^xE8 ; transfer rate = 1000 KHz ffh,ffh .BYTE 2 ; number of heads = 2 ffh .BYTE 36 ; sectors per track = 36 ffh .BYTE 2,0 ; data bytes per sector = 512 .BYTE 0,80 ; number of cylinders = 80 ffh,ffh .BYTE 0,80 ; starting write precomp = 80 .BYTE 0,80 ; starting reduced write current = 80 .BYTE 0,0 ; step rate = default ffh,ffh .BYTE 0 ; step pulse width = default .BYTE 0,0 ; head settle delay = default ffh,ffh .BYTE 5 ; motor on delay = 1/2 Second ffh .BYTE 30 ; motor off delay = 3 Seconds ffh .BYTE ^x40 ; trdy=0, ssn=1, mo=0 60h .BYTE 0 ; extra step pulses per cylinder = 0 1fh .BYTE 0 ; write compensation level unsupported .BYTE 0 ; head load delay unsupported .BYTE 0 ; head unload delay unsupported .BYTE ^x23 ; pin 34=2, pin 2=3 .BYTE ^x00 ; pin 4=0 .BYTE 0,0,0,0 ; reserved .PAGE .SBTTL + .SBTTL + DRIVER ENTRY POINTS .SBTTL + .SBTTL DK_CTRL_INIT - Controller initialization routine ;+ ; DK_CTRL_INIT ; ; This routine is called to perform controller-specific initialization and ; is called by the operating system in three places: ; ; - at system startup ; - during driver loading and reloading ; - during recovery from a power failure ; ; This routine is a NOP for power failure recovery. ; For system startup and driver loading it allocates a CDDB. ; ; Context: ; ; Powerfail thread ; IPL = POWER ; No Locks held ; ; STEP 2 INTERFACE ; ; int CTRLINIT (IDB *, DDB *, CRB *) ; ; OBSOLETE STEP 1 INPUTS: ; ; R4 & R5 - address of the IDB (controller status register) ; R6 - address of the DDB (device data block) ; R8 - address of the CRB (channel request block) ; ; OBSOLETE STEP 1 OUTPUTS: ; ; R0 - Status ; R1,R2 - Destroyed ; All other registers preserved ;- DRIVER_CODE DK_CTRL_INIT:: ;P X; Controller Initialization Routine $DRIVER_CTRLINIT_ENTRY PRESERVE= ONCE_DK_DEBUG CTRL_INIT MOVB #SPL$C_SCS,CRB$B_FLCK(R8) ;P X; Initialize device spin lock index. TSTL CRB$L_SCS_STRUC(R8) ;P X; Check for CDDB already present. BEQL 10$ ;P X; Branch if not MOVZWL #SS$_NORMAL, R0 ;P X; Set success status RET ;P X; Otherwise, return to caller ; ; Create fork thread to finish controller init. ; 10$: MOVL R8,R5 ;P X; Fork with CRB MOVL R6,R3 ;P X; Save DDB for after fork FORK ROUTINE=DK_CTRL_INIT_FORK, CONTINUE=20$ 20$: ;P X; Now that the fork routine is queued... MOVL #SS$_NORMAL,R0 ;P X; set return value to normal and RET ;P X; return to caller. ; ; Get pool for CDDB. ; DK_CTRL_INIT_FORK: FORK_ROUTINE ;F FF; Generate a .JSB_ENTRY MOVL R3,R6 ;F FF; get DDB in R6 MOVL R5,R8 ;F FF; get CRB in expected register MOVZWL #CDDB$K_LENGTH,R1 ;F FF; Size of CDDB JSB G^EXE$ALONONPAGED ;F FF; Allocate some pool BLBS R0,30$ ;F FF; Branch if successful BUG_CHECK INCONSTATE,FATAL ;F FF; Otherwise, bugcheck ; ; Clear pool. ; 30$: PUSHR #^M ;F FF; Save registers. MOVC5 #0,.,#0,R1,(R2) ;F FF; Zero entire block. POPR #^M ;F FF; Restore saved registers. ; ; Initialize necessary CDDB fields. ; MOVW R1,CDDB$W_SIZE(R2) ;F FF; Size ASSUME CDDB$B_SUBTYPE EQ CDDB$B_TYPE+1 MOVW #>,- ;F FF; Subtype CDDB$B_TYPE(R2) ;F FF; MOVL G^CLU$GL_ALLOCLS,- ;F FF; Allocation class CDDB$L_ALLOCLS(R2) ;F FF; MOVL R8,CDDB$L_CRB(R2) ;F FF; CRB address MOVL R6,CDDB$L_DDB(R2) ;F FF; DDB address MOVL R2,CRB$L_SCS_STRUC(R5) ;F FF; Save CDDB address in CRB. MOVL #SS$_NORMAL,R0 ;F FF; Set success status RSB ;F FF; Return to caller .PAGE .SBTTL DK_UNIT_INIT - Unit initialization routine ;+ ; DK_UNIT_INIT ; ; This routine performs unit-specific initialization and is called for each ; disk found on the SCSI bus. A connection to the port driver is established, ; which lasts for the life of the system. All traffic to this SCSI device is ; directed over this connection. ; ; The first time through this routine a set of SPTEs are allocated which are ; used to double map the user's buffer during datacheck operations. The cells ; used to record the address of the SPTEs and the system virtual address ; mapped by them live in the driver image. This is possible since use of the ; SPTEs is synchronized with the fork lock. ; ; An inquiry command is sent to the target to determine the device type. ; In additon, several sanity checks are made on the inquiry data to determine ; if the device is valid. If so, the unit is placed online and any I/O queued ; to the device during initialization is started. ; ; Context: ; ; Powerfail thread ; IPL = POWER ; No Locks held ; ; STEP 2 INTERFACE ; ; int UNITINIT (IDB *, UCB *) ; ; OBSOLETE STEP 1 INPUTS: ; ; R3 & R4 - address of the IDB (controller status register) ; R5 - UCB address ; ; OBSOLETE STEP 1 OUTPUTS: ; ; R0 - Status ; R1,R2 - Destroyed ; All other registers preserved ;- ; DK_UNIT_INIT:: ;P X; Unit Initialization Routine $DRIVER_UNITINIT_ENTRY PRESERVE = ONCE_DK_DEBUG UNIT_INIT BBS #UCB$V_POWER,UCB$L_STS(R5),20$ ;P X; Return immediately if powerfail BISL #UCB$M_ONLINE!UCB$M_BSY,- ;P X; Set unit online and busy UCB$L_STS(R5) ;P X; CMPL G^SYS$AR_BOOTUCB,R5 ;P X; Is this the system disk? BNEQ 10$ ;P X; Branch if not MOVL #^X7FFFFFFF,UCB$L_MAXBLOCK(R5) ;P X; Set up a MAXBLOCK for booting 10$: FORK ROUTINE=DK_UNIT_INIT_FORK, - ;P X; Fork to drop IPL to SYNCH CONTINUE=20$ ;P X; not that the fork routine is queued 20$: MOVL #SS$_NORMAL,R0 ;P X; set the return value to normal RET ;P X; and return DK_UNIT_INIT_FORK: FORK_ROUTINE ;F FF; Generate .JSB_ENTRY .IF DEFINED TRACE_CB MOVL #1024 * TRB$C_LENGTH, R1 ;F FF; Size of required pool JSB G^EXE$ALONONPAGED ;F FF; Allocate a histogram buffer BLBC R0,10$ ;F FF; Pool allocation successful? MOVL R2, UCB$L_TOP_OF_RING(R5) ;F FF; Save top of ring buffer MOVL R2, UCB$L_RING_POINTER(R5) ;F FF; Init ring buffer MOVL #1024, UCB$L_RING_COUNTER(R5) ;F FF; Init counter .ENDC BISL #UCB$M_CB_NOCMDQ,- ;F FF; Don't attempt CMDQ until IO_PACKACK UCB$L_CLASS_BUSY(R5) ;F FF; completes, and validated device's support SET_SINGLE INIT,R5,10$ ;F FF; Single threaded UNIT initialization. KP_ALLOCATE_KPB - ;F FF; Allocate kernel process block KPB=UCB$PS_UNITINIT_KPB(R5),- ;F FF FLAGS=#KP$M_IO ;F FF; Specify deallocation after init ends BLBC R0,10$ ;F FF; Branch if error allocating KPB MOVL UCB$PS_UNITINIT_KPB(R5),R2 ;F FF; Get KPB address from UCB MOVL R5,KPB$PS_UCB(R2) ;F FF; Save UCB address in KPB KP_START - ;F FF; Start the kernel process that does KPB=R2,- ;F FF; most of the unit init work ROUTINE=DK_KP_UNIT_INIT,- REGISTERS=#KPREG$K_HLL_REG_MASK RSB ;F FF; Return from DK_UNIT_INIT 10$: BUG_CHECK INCONSTATE,FATAL ;F FF; bugcheck ; The remainder of this routine runs as a kernel process. ; This is necessary to make SPI$ calls and access the port driver. ; ; The KP register save mask (above) saves all registers appropriate to a HLL ; driver. This permits port drivers to be written in a high-level language. ; The starting KP routine (call entry below) saves only the registers used ; by that routine. DK_KP_UNIT_INIT:: .CALL_ENTRY PRESERVE= $ARG_DEF ; KPB address MOVL kpb(AP),R2 ;F FF; Pick up pointer to KPB MOVL KPB$PS_UCB(R2),R5 ;F FF; Restore UCB address BSBW SETUP_TRACE ;F FF; Set up trace buffer ASSUME UCB$V_DISCONNECT LT 8 MOVB #UCB$M_DISCONNECT!- ;F FF; By default, assume the target device UCB$M_SYNCHRONOUS,- ;F FF; is capable of both disconnecting and UCB$L_DK_FLAGS(R5) ;F FF; synchronous operation ASSUME UCB$V_DD_BYPASS LT 16 BICW2 #UCB$M_DD_BYPASS,- ;F FF; Ensure no writes to DD diskettes UCB$L_DK_FLAGS(R5) ;F FF; on an RX23S drive ;F FF; (provides space for an in-place ;F FF; patch) CLRB UCB$B_SEEK_DIR(R5) ;F FF; Initialize seek direction flag CLRL UCB$PS_SAVE_CONN_CHAR(R5) ;F FF; Initialize location to hold ;F FF; connection characteristics used ;F FF; in IO_DIAGNOSE calls. MOVAL UCB$L_FLUSH_IOQFL(R5),R0 ;F FF; Initialize the queue used to flush MOVL R0,(R0) ;F FF; I/Os which are queued during unit MOVL R0,4(R0) ;F FF; init when unit init fails. MOVAB UCB$Q_IRP_LIST(R5), R0 ;F FF; Initialize the queue used to hold MOVL R0,(R0) ;F FF; the active IRPs. MOVL R0,4(R0) ;F FF; CLRL UCB$L_QUEUED_IO_COUNT(R5) ;F FF; clear count MOVAL UCB$Q_DC_WAIT_LIST(R5), R0 ;F FF; Initialize the queue used to hold MOVL R0,(R0) ;F FF; DC (datacheck) SCDRP's waiting MOVL R0,4(R0) ;F FF; for exclusive access to the UCB MOVAL UCB$Q_DRAIN_LIST(R5), R0 ;F FF; Initialize the queue used to hold MOVL R0,(R0) ;F FF; SCDRP's waiting for I/O's to be MOVL R0,4(R0) ;F FF; drained, before proceeding ; ; Allocate and initialize BUSY_BIT_IRP used to keep UCB$V_BSY set during times ; when UCB$L_CLASS_BUSY bits are still set. ; MOVAL UCB$R_BUSY_BIT_IRP(R5), R0 ;F FF; Get starting address of BUSY_BIT_IRP ASSUME IRP$W_SIZE EQ 8 ASSUME IRP$B_TYPE EQ 10 ASSUME IRP$B_RMOD EQ 11 MOVL #!IRP$K_LENGTH,- ;F FF; Set the size, type and access mode fields IRP$W_SIZE(R0) ;F FF: fields if the IRP MOVL R5, IRP$L_UCB(R0) ;F FF; Set ownership UCB MOVAL DK_GOLDEN_IRP_POST,- ;F FF; Insure against post- IRP$L_PID(R0) ;F FF; processing of "golden" IRP ; ; Setup UCB CDDB field. ; MOVL UCB$L_CRB(R5),R0 ;F FF; Get CRB address MOVL CRB$L_SCS_STRUC(R0),- ;F FF; Get CDDB address out of the CRB. UCB$L_CDDB(R5) ;F FF; ; ; Check system disk. ; CMPL G^SYS$AR_BOOTUCB,R5 ;F FF; Is this the system disk? BNEQ 5$ ;F FF; Branch if not ; ; Check for host based shadowed system disk. If so, fork and wait until ; the controller init routine of SHDRIVER completes its execution. ; BLBC G^EXE$GL_SHADOW_SYS_DISK,5$ ;F FF; LBC not booting hbs. Continue. 3$: CMPL G^SYS$AR_BOOTUCB,G^EXE$GL_SYSUCB;F FF; Is the system disk pointer updated? BNEQ 5$ ;F FF; NEQ means updated. Continue. MOVL UCB$PS_UNITINIT_KPB(R5),R8 ;F FF; Get KPB address KP_STALL_FORK_WAIT KPB=R8,- ;F FF; Fork wait using UCB fork block FKB=R5 BRB 3$ ;F FF; Try again. 5$: CLRL UCB$L_READ_COUNT(R5) ;F FF; Clear perf counters CLRL UCB$L_WRITE_COUNT(R5) ;F FF; Clear perf counters CLRL UCB$L_OTHER_COUNT(R5) ;F FF; Clear perf counters TSTL UCB$L_READ_XLEN_HIST(R5) ;F FF; Do we already have pool? BLSS 1000$ ;F FF; Br if yes MOVL #XLEN_HIST_SIZE,R1 ;F FF; Size of required pool JSB G^EXE$ALONONPAGED ;F FF; Allocate a histogram buffer BLBC R0,30$ ;F FF; Pool allocation successful? MOVL R2,UCB$L_READ_XLEN_HIST(R5) ;F FF; Save histogram address MOVL #XLEN_HIST_SIZE,R1 ;F FF; Size of required pool JSB G^EXE$ALONONPAGED ;F FF; Allocate a histogram buffer BLBC R0,30$ ;F FF; Pool allocation successful? MOVL R2,UCB$L_WRITE_XLEN_HIST(R5) ;F FF; Save histogram address MOVL #XLEN_HIST_SIZE,R1 ;F FF; Size of required pool JSB G^EXE$ALONONPAGED ;F FF; Allocate a histogram buffer BLBC R0,30$ ;F FF; Pool allocation successful? MOVL R2,UCB$L_XLEN_HIST(R5) ;F FF; Save histogram address 1000$: PUSHR #^M ;F FF; Save regs PUSHL R5 ;F FF; Save UCB seperatly MOVL #XLEN_HIST_SIZE,R1 ;F FF; length MOVL UCB$L_READ_XLEN_HIST(R5),R2 ;F FF; Address MOVC5 #0,.,#0,R1,(R2) ;F FF; Clear read counters MOVL (SP),R5 ;F FF; Get copy of UCB MOVL #XLEN_HIST_SIZE,R1 ;F FF; length MOVL UCB$L_WRITE_XLEN_HIST(R5),R2 ;F FF; Address MOVC5 #0,.,#0,R1,(R2) ;F FF; clear write counters MOVL (SP),R5 ;F FF; Get copy of UCB MOVL #XLEN_HIST_SIZE,R1 ;F FF; length MOVL UCB$L_XLEN_HIST(R5),R2 ;F FF; Address MOVC5 #0,.,#0,R1,(R2) ;F FF; clear write counters POPL R5 ;F FF; Restore UCB POPR #^M ;F FF; Save regs MOVL #XLEN_HIST_TURNOVER,- ;F FF; Set I/O counter for qdepth check UCB$L_XLEN_HIST_CYCLE(R5) MOVAB UCB$L_QDEPTH(R5),- ;F FF; Point to start of our perf data area UCB$L_2P_LINK(R5) ;F FF; for DKPERF.C ; All SCSI DISK unit numbers should be of the form "n0m" where n is the SCSI ; ID between 0 and 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 ;F FF; Assume bad LUN or SUBLUN specified MOVZWL UCB$W_UNIT(R5),R1 ;F FF; Get device unit number CLRL R2 ;F FF; Prepare for extended divide EDIV #100,R1,R1,R2 ;F FF; Extract SCSI bus ID from LUN CMPL R1,#7 ;F FF; Valid SCSI ID (0 <= n <= 7)? BGTRU 40$ ;F FF; Branch if not CMPL R2,#7 ;F FF; Valid LUN (0 <= n <= 7)? BGTRU 40$ ;F FF; Branch if not MULB3 #<1@5>,R2,UCB$B_LUN(R5) ;F FF; Save LUN (shifted left 5 bits for use ;F FF; later in SETUP_CMD) ASHL #16,R1,R1 ;F FF; Place SCSI ID in high-order word of R1 ASHL #16,R2,R2 ;F FF; Place LUN in high-order word of R2 MOVL UCB$L_DDB(R5),R0 ;F FF; Get DDB address SUBB3 #^A'A',DDB$T_NAME+3(R0),R1 ;F FF; Translate controller letter to ;F FF; SCSI bus ID. ADDL3 G^EXE$GL_ABSTIM,- ;F FF; Calculate and save init #DK_INIT_TIMEOUT,- ;F FF; expiration time UCB$L_INITMO(R5) MOVL UCB$PS_UNITINIT_KPB(R5),R8 ;F FF; Get KPB address MOVL R1,KPB$PS_SCSI_PTR1(R8) ;F FF; Save SCSI IDs MOVL R2,KPB$PS_SCSI_PTR2(R8) ;F FF; Save LUN value 7$: SPI$CONNECT - ;F FF; Connect to the port driver PORT_CALLBACK=DK_PORT_CALLBACK- ;F FF; Set callback routine PORT_STATE_CONTEXT=R5 ;F FF; Set callback context (UCB) ; ; On return from SPI$CONNECT ; R0 = Call status ; R1 = Maximum byte count ; R2 = SCDT address ; R3 = PORT Capability Flags ; R4 = SPDT address BLBS R0,10$ ;F FF; Branch if connect attempt succeeded CMPL UCB$L_INITMO(R5),G^EXE$GL_ABSTIM;F FF; Has the timeout time passed? BLEQU 40$ ;F FF; If so, set this unit OFFLINE KP_STALL_FORK_WAIT KPB=R8,- FKB=R5 ;F FF; Fork wait using UCB fork block MOVL KPB$PS_SCSI_PTR1(R8),R1 ;F FF; Restore SCSI IDs MOVL KPB$PS_SCSI_PTR2(R8),R2 ;F FF; Restore LUN value BRB 7$ ;F FF; Try connecting again 10$: BBC #SPDT$V_PFLG_AEN,R3,101$ ;F FF; See if port supports AEN BISL #UCB$M_PORT_AEN,- ;F FF; Set port_aen bit UCB$L_DK_FLAGS(R5) ;F FF; in UCB flags 101$: BITL #,R3;F FF; supports Autosense BEQL 102$ BISL #UCB$M_PORT_AUTOSENSE,- ;F FF; Set port_autosense bit UCB$L_DK_FLAGS(R5) ;F FF; in UCB flags 102$: BBC #SPDT$V_PFLG_CMDQ,R3,11$ ;F FF; See if port supports Command Queuing BISL #UCB$M_PORT_CMDQ,- ;F FF; Set port_cmdq bit UCB$L_DK_FLAGS(R5) ;F FF; in UCB flags .print ;SPI$CONNECT returns: SCQ$IS_MAX_BCNT in R1, SCQ$IS_PORT_SERV_FLAGS in R3 11$: ASHL #-24,R3,R3 ;F FF; Get MAXBCNT divisor BNEQ 12$ ;F FF; Branch if divisor supplied by port MOVZBL #1,R3 ;F FF; Otherwise, use a divisor of 1 12$: DIVL R3,R1 ;F FF; Get MAXBCNT recommended by port BICL #,R1 ;F FF; Make MAXBCNT an integral block count CMPL R1,UCB$L_MAXBCNT(R5) ;F FF; For MAXBCNT, use minimum supported BGEQ 15$ ;F FF; value of port and class drivers MOVL R1,UCB$L_MAXBCNT(R5) ;F FF; Save maximum byte count in UCB 15$: MOVL R2,UCB$PS_SCDT(R5) ;F FF; Save SCDT address MOVL R4,UCB$L_PDT(R5) ;F FF; Save PDT address 16$: TSTL DATACHECK_SPTE ;F FF; Datacheck SPTEs already allocated BNEQ 20$ ;F FF; Branch if so $BYTES_TO_PAGES - ;F FF; Convert to max page count SOURCE_BYTCNT=UCB$L_MAXBCNT(R5),- DEST_PAGCNT=R2,- ROUNDUP=YES INCL R2 ;F FF; Account for non-page-aligned buffers JSB G^LDR$ALLOC_PT ;F FF; Allocate SPTEs to double map user buf BLBC R0,40$ ;F FF; Branch if failure MOVL R1,DATACHECK_SPTE ;F FF; Save SVA of the first SPTE SUBL2 G^MMG$GL_SPTBASE,R1 ;F FF; Get offset into page table ASHL G^MMG$GL_PTE_OFFSET_TO_VA,R1,R1 ;F FF; Calculate system virtual addr BISL3 #VA$M_SYSTEM,R1,- ;F FF; mapped by this set of SPTEs DATACHECK_SVA ;F FF; 20$: PUSHL #NUM_LONGWORDS_DIAGNOSE ;F FF; Number of longwords in UCB ;F FF; reserved for IO$_DIAGNOSE PUSHAL UCB$PS_DIAGNOSE(R5) ;F FF; Start of DIAGNOSE longwords CALLS #2,DKMK$DIAGNOSE_INIT ;F FF; Initialize DIAGNOSE area BLBC R0,40$ ;F FF; Set offline if error BISL2 #UCB$M_TENBYTE,- ;F FF; Assume 10-byte mode sense support UCB$L_DK_FLAGS(R5) CLR_SINGLE INIT,R5,30$ ;F FF; UNIT initialization is complete BSBW SET_UNIT_ONLINE ;F FF; Go bring the unit online RET ;F FF; Return to caller within KP services 30$: BUG_CHECK INCONSTATE,FATAL ;F FF; bugcheck ; ; Connection failure. Log an error and set the unit offline. ; 40$: LOG_ERROR - ;F FF; Log a connection error TYPE=CONNECTION_ERROR,- ;F FF; VMS_STATUS=R0,- ;F FF; UCB=R5 ;F FF; BICL #UCB$M_ONLINE!UCB$M_BSY,- ;F FF; Set the unit offline and not busy UCB$L_STS(R5) ;F FF; Return to caller within KP services CLR_SINGLE INIT,R5,30$ ;F FF; Unit initialization is complete. RET ;F FF .PAGE .SBTTL DK_GOLDEN_IRP_POST ;++ ; DK_GOLDEN_IRP_POST ; ; The "golden" IRP might accidentially get placed on the I/O post ; processing queue. If that happens, we want it to be harmless. ; To make attempts to post process the "golden" IRP harmless, ; the address of this routine is placed in IRP$L_PID. When post- ; processing sees this routine address in IRP$L_PID, it will call ; this routine. This routine does only two things. It marks the ; "golden" IRP as not queued, and it returns success. ; ; Even with this routine, there is a very small window where attempts ; to post process the "golden" IRP will cause memory corruption. If ; I/O post-processing has dequeued the "golden" IRP and STOP_NEW_IO ; tries to REMQUE it again, corruption will result. The window for ; this corruption is from the time that I/O post-processing dequeues ; the "golden" IRP until the time that this routine clears the ; "golden" IRP is queued field. ; ; Context ; ; I/O Post Processing ; IPL = IOPOST ; ; Calling sequence: ; ; PUSHL irp ; "golden" IRP address ; CALL #1,DK_GOLDEN_IRP_POST ; ; Input: ; 4(AP) "golden" IRP address ; ; Output: ; R0 SS$_NORMAL ;-- DK_GOLDEN_IRP_POST:: ; PP; Handle posted "golden" IRP .CALL_ENTRY PRESERVE= $ARG_DEF ; PP; IRP Address MOVL irp(AP), R5 ; PP; Get IRP Address CLRL (R5) ; PP; Clear 1st IRP longword MOVL #SS$_NORMAL, R0 ; PP; Set success statusn RET ; PP; All done, exit .PAGE .SBTTL DK_PORT_CALLBACK - Callback Routine to Handle Port Busy Events. ;++ ; DK_PORT_CALLBACK ; ; The port driver is either declaring an event which is of interest to the ; Class Driver. On a request specific bases this driver will alter its current ; state (STOP or GO). ; ; ACA Processing Description ; ; During normal class driver processing a SPI$K_PBCB_STOP_CHECK_CONDITION call will be ; invoked by the port driver telling the class driver about a future check condition and ; the need for ACA processing. The class driver should ONLY freeze its operations and not ; send any further SCSI requests to the port queue (including ACA requests). The Class Driver ; will now wait for "a" SCSI request to with a check condition, which will give the class driver ; a chance to analyze the request sense data. ; ; After the class driver receives the request sense data, if the class driver supports ; ACA operations, then the ACA requests should be sent to the port driver (not queued), so they are ; processed in sequential order. When the class driver is done issuing ACA requests to correct the ; failure then it must issue a final SCDRP with the SCDRP$V_FLAG_CLEAR_ACA_MSG bit set. ; ; Summary of class driver check condition processing: ; 1.) Receive SPI$K_PBCB_STOP_CHECK_CONDITION via PORT_CALLBACK routine. ; As a result, stop the class driver initiation of SCSI requests for this device. ; 2.) Wait for the SCSI request to complete with the check condition status. ; 3.) Analyze the data from the request sense operation. ; 4.) If the class driver is performing ACA error processing: ; a.) Allocate the next SCDRP required for ACA error processing. ; b.) Format this SCDRP. ; c.) Initiate this request with the SCDRP$K_QCHAR_ACA queue characteristic. ; d.) Wait for the SCDRP to complete. ; e.) Repeat step (a-d) as required to complete error recovery. ; f.) Allocate the next SCDRP required for CLEAR_ACA_MSG. ; g.) Set SCDRP$V_FLAG_CLEAR_ACA_MSG in SCDRP$L_SCSI_FLAGS. ; h.) Initiate this request with the SCDRP$K_QCHAR_ACA queue characteristic. ; 5.) Receive SPI$K_PBCB_GO_ACA_COMPLETE via PORT_CALLBACK routine. ; As a result, start the class driver initiation of SCSI requests for this device. ; ; Note: All of the callback states in this routine with the exception of ; reset will crash if an attempt is made to set them more than once. ; Reset should tolerate that condition. ; ; Context ; ; SCDRP Thread ; IPL = Fork ; Fork lock held ; ; Calling sequence: ; ; PUSHL #state ; SPI$K_PBCB_STOP_???? or SPI$K_PBCB_GO_???? ; PUSHAB UCB ; UCB address associated with this connection. ; CALL #1, DK_PORT_CALLBACK ; ; Input: ; 4(AP) UCB address ; 8(AP) Busy State: SPI$K_PBCB_STOP_BUSY or SPI$K_PBCB_GO_READY ; ; Output: ; R0 Destroyed ; R1 Destroyed ; none to the caller ; but the class busy bit is set appropriately. ;-- DK_PORT_CALLBACK:: ;S FF; Handle Port Busy Events. .CALL_ENTRY PRESERVE= $ARG_DEF ; Busy State: SPI$K_PBCB_STOP_BUSY or SPI$K_PBCB_GO_READY DK_DEBUG PORT_CALLBACK ; Get the port's busy status. MOVL ucb(AP), R5 ;S FF; Get the UCB address. MOVL pk_state(AP), R0 ;S FF; Get the port's busy state. DISPATCH R0,TYPE=B,<- ;? ??; Dispatch according to function ,- ; Port is busy ,- ; Port has seen a check condition event. ,- ; Port has seen a queue full event. ,- ; Port has seen a bus reset ,- ; Port has run out of send credits. ,- ; Port has run out of SPDT CMD bits ,- ; Port is no longer busy ,- ; Port has finished the ACA processing. ,- ; Port is attempting to restart the queue following the queue full event. ,- ; Port has finished bus reset processing ,- ; Port has send credits available > ; Port has send credits available BRB 400$ ; Fell off end of table with unsupported callback ; The port is busy and can no longer process any requests from the ; class driver. Queue any new I/O requests until the condition in ; the port driver clears and a port driver ready notification is ; delivered. 100$: SET_SINGLE BUSY,R5,400$ ;S FF; set busy state BRB 190$ 110$: SET_SINGLE CHECK_CONDITION,R5,400$ ;S FF; set check condition wait BRB 190$ 120$: SET_SINGLE QUEUE_FULL_EVNT,R5,400$ ;S FF; set queue full wait BRB 190$ 130$: SET_SINGLE BUS_RESET,R5,190$ ;S FF; Set reset wait-don't BRB 190$ ;S FF; if already set don't bug_check 140$: SET_SINGLE NO_SEND_CREDITS,R5,400$ ;S FF; Set no send credits wait BRB 190$ 150$: SET_SINGLE NO_CMD_BITS,R5,190$ ;S FF; Set no SPDT command bits BRB 190$ 190$: BRB 350$ ; The port is no longer busy, clear the busy state and restart any ; pending I/O requests if necessary. ; 200$: CLR_SINGLE BUSY,R5,400$ ;S FF; BUSY is complete BRB 290$ 210$: CLR_SINGLE CHECK_CONDITION,R5,400$ ;S FF; Check condition is complete BRB 290$ 220$: CLR_SINGLE QUEUE_FULL_EVNT,R5,400$ ;S FF; Queue event is complete BRB 290$ 230$: CLR_SINGLE BUS_RESET,R5,290$ ;S FF; Bus reset is complete BRB 290$ ;S FF; If already clear, don't crash 240$: CLR_SINGLE NO_SEND_CREDITS,R5,400$ ;S FF; Send credits is complete BRB 290$ 250$: CLR_SINGLE NO_CMD_BITS,R5,290$ ;S FF; SPDT command bits are available BRB 290$ ; ; Start another I/O request if one is pending. ; 290$: REMQUE @UCB$L_IOQFL(R5), R3 ;S FF; Remove IRP from device pending queue BVS 300$ ;S FF; if found one then CALL_INITIATE ;S FF; initiate I/O RET ;S FF; and return 300$: TST_SINGLE 350$ ;S FF; else are there any other reasons not clear bsy? BICL #UCB$M_BSY, UCB$L_STS(R5) ;S FF; Clear UCB busy bit 350$: RET ;S FF; Return 400$: BUG_CHECK INCONSTATE,FATAL ;S FF; bugcheck .PAGE .SBTTL DK_MOUNT_VER - Mount Verification Routine ;++ ; DK_MOUNT_VER ; ; The disk class driver and mount verification are in bed together. ; They are so intimately related that one of them is most certainly ; "with child" and nuptial announcements will soon be made. ; ; The following is a list of reasons why this routine might be called: ; ; - starting mount verification due to appropriate status is a ; normal I/O request. This probably is the result of the a ; change of state in the drive. Hopefully, mount verification ; will restore the drive to the online state and work can ; continue. ; - completing mount verification for the above. ; ; Calling Sequence: ; ; JSB DK_MOUNT_VER ; ; Context: ; ; Fork thread ; IPL = Fork ; Fork lock held ; ; Inputs: ; ; For beginning mount verification: ; ; R3 IRP address ; R5 UCB address ; ; For ending mount verification: ; ; R3 0(zero) ; R5 UCB address ; ; Outputs: ; ; the contents of R0,R1,R2 and R4 may be overwritten (from [SYS]MOUNTVER.MAR) ; ;-- DK_MOUNT_VER:: ;F FF; Mount Verification Routine $DRIVER_MNTVER_ENTRY DK_DEBUG MOUNT_VER TSTL R3 ;F FF; Is this begin or end of MNTVER? BNEQ DK_BEGIN_MNTVER ;F FF; R3 <> 0 - Begin mount verification. BRB DK_END_MNTVER ;F FF; R3 == 0 - End the mount verification. .PAGE .SBTTL DK_BEGIN_MNTVER - Begin mount verification ; ; Functional Description: ; ; This routine is called whenever mount verification needs to present a ; new IRP to the driver to be "remembered" and restarted after mount ; verification completes. If this is a "normal" mount verification ; operation, the IRP is inserted into the pending I/O request queue, ; ordered by sequence number. ; ; If a "normal" IRP with the IRP$V_SRVIO bit set is input, the IRP is sent ; to the post queue instead of placed back in the pending I/O request ; queue. ; ; Context: ; ; Fork thread ; IPL = Fork ; Fork lock held ; ; Inputs: ; ; R3 IRP address ; R5 UCB address ; ; Implicit inputs: ; ; IRP$L_SEQNUM(R3) monotonically increasing I/O request sequence ; number ; UCB$L_IOQ[F/B]L(R5) header for queue of pending I/O requests for ; this unit ; ; Outputs: ; ; Outputs: ; ; the contents of R0,R1,R2 and R4 may be overwritten (from [SYS]MOUNTVER.MAR) ; DK_BEGIN_MNTVER:: .GLOBAL_LABEL ;F FF; Begin mount verification ; ; If the IRP is a served or shadowing I/O, no need to do MOUNT ; verification on this IRP. ; 10$: BBS #IRP$V_SRVIO,IRP$L_STS(R3),15$ ;F FF; If served-I/O post it BBC #IRP$V_SHDIO,IRP$L_STS2(R3),20$ ;F FF; If shadowing-I/O post it ;F FF; else place on pending I/O queue 15$: CALL_POST_IRP ;F FF; Post IRP RET ;F FF; return ; ; Set the CLASS_BUSY flag for mount verification in progress. ; Reinsert IRP in pending I/O queue in sequence number order. ; 20$: BBS #IRP$V_MVIRP,IRP$L_STS(R3),50$ ;F FF; If this is a MVIRP, BUG_CHECK MSCP_MV ;F FF; Inform MSCP Server that MV is in progress SET_SINGLE MNTVERIP,R5,lab=25$ ;F FF; Single thread MOUNT Verification 25$: MOVAB (R5),R1;F FF; Get pending I/O queue addr. MOVL R1, R0 ;F FF; Copy listhead address. 30$: MOVL IRP$L_IOQBL(R0),R0 ;F FF; Get address of next entry CMPL R0, R1 ;F FF; Reached end of list yet? BEQL 40$ ;F FF; Branch if end of list. CMPB IRP$B_PRI(R3),IRP$B_PRI(R0) ;F FF; New entry priority greater? BLSSU 30$ ;F FF; If lss yes CMPL IRP$L_SEQNUM(R0),- ;F FF; Does new IRP belong here IRP$L_SEQNUM(R3) ;F FF; based on sequence #? BLSSU 30$ ;F FF; Branch if IRP doesn't belong. 40$: INSQUE IRP$L_IOQFL(R3),IRP$L_IOQFL(R0) ;F FF; Insert new IRP at this point ; ; Prevent the "golden" IRP from becoming burried in the UCB$L_IOQFL ; TSTL UCB$R_BUSY_BIT_IRP(R5) ;F FF; Is "golden" IRP queued? BEQL 49$ ;F FF; NO MOVAL UCB$L_IOQFL(R5),R0 ;F FF; Get the correct back pointer CMPL UCB$R_BUSY_BIT_IRP+4(R5),R0 ;F FF; Is "golden" IRP first in Q? BEQL 49$ ;F FF; Yes ; ; OK! The "golden" IRP is burried in the I/O request queue. ; Since it's not doing us any good there, simply remove it. ; REMQUE UCB$R_BUSY_BIT_IRP(R5), R0 ;F FF; Remque the "golden" IRP CLRL UCB$R_BUSY_BIT_IRP(R5) ;F FF; Clear it's "in use" flag TRACE_SINGLE 8108 ;F FF; Trace golden IRP lost 49$: RET ;F FF; Return. 50$: BUG_CHECK INCONSTATE,FATAL ;S FF; Should not requeue MVIRP .PAGE .SBTTL DK_END_MNTVER - End mount verification processing ;++ ; ; DK_END_MNTVER - End mount verification ; ; Functional Description: ; ; ; This routine completes mount verification processing. ; ; The suppress Mount Verification messages flags is cleared ; The mount verification in progress flag is cleared. ; ; If mount verification failed to bring the disk back online, all ; requests are aborted with SS$_VOLINV to indicate that the volume is no ; longer valid. ; ; If mount verification succeded in bringing the disk online, this routine ; performs the end of mount verification processing necessary to unstall ; the single threaded processing of SCDRPs, caused by the initial startup ; Mount Verification. ; ; Context: ; ; Fork thread ; IPL = Fork ; Fork lock held ; ; Inputs: ; ; R3 0 ; R5 UCB address ; ; Implicit inputs: ; ; UCB$L_IOQ[F/B]L(R5) header for queue of pending I/O requests for ; this unit ; UCB$L_STS(R5) bit UCB$V_VALID clear if mount verification ; failed ; ; Outputs: ; ; the contents of R0,R1,R2 and R4 may be overwritten (from [SYS]MOUNTVER.MAR) ; ; Implicit outputs: ; ; UCB$L_DEVSTS(R5) UCB$V_MSCP_MNTVERIP bit cleared. ; UCB$L_STS(R5) UCB$V_SUPMVMSG bit cleared. ; ;-- DK_END_MNTVER:: .GLOBAL_LABEL ;F FF; End mount verification processing CLR_SINGLE MNTVERIP,R5,40$ ;F FF; End of MOUNT Verification. ;F FF; Don't care if MNTVER BICL #UCB$M_SUPMVMSG, UCB$L_STS(R5) ;F FF; Clear suppresion bit .BRANCH_LIKELY BBS #UCB$V_VALID, UCB$L_STS(R5),20$ ;F FF; Branch if mount verification successful ; ; Mount verification was not successful, abort all outstanding I/O's ; ; ; Flush device and port queues ; ; If the "golden" IRP is queued, remove it TSTL UCB$R_BUSY_BIT_IRP(R5) ;F FF; Is it queued? BEQL 10$ ;F FF; No, so continue REMQUE UCB$R_BUSY_BIT_IRP(R5),R31 ;F FF; Remove from queue CLRL UCB$R_BUSY_BIT_IRP(R5) ;F FF; Indicate no longer queued TRACE_SINGLE 8109 ;F FF; Trace ; ; Flush pending I/O queues ; 10$: REMQUE @UCB$L_IOQFL(R5), R3 ;F FF; Get next pending IRP. BVS 30$ ;F FF; Branch if no more IRP MOVL R3,UCB$L_IRP(R5) ;S FF; Set up UCB$L_IRP for the actual ;S FF; IRP that's completing! CLRL R1 ;S FF; Clear transfer byte count MOVL #SS$_VOLINV,R0 ;F FF; Set volume invalid CALL_REQCOM ;F FF; Complete the QIO BRW 10$ ;F FF; Loop till no more IRPs. ; ; Mount verification was successful, restart I/O's ; 20$: REMQUE @UCB$L_IOQFL(R5),R3 ;F FF; Remove IRP from device pending queue BVS 30$ ;F FF; if found one then CALL_INITIATE ;F FF; initiate I/O RET ;F FF; and return 30$: CLRL UCB$L_IRP(R5) ;F FF; All I/O has been processed. TST_SINGLE 40$ ;S FF; else are there any other reasons not clear bsy? BICL #UCB$M_BSY, UCB$L_STS(R5) ;S FF; Clear UCB busy bit 40$: RET ;F FF; Return. .PAGE .SBTTL DK_DRAIN_IO - Stall current I/O function, until I/O's are drained ; ; DK_DRAIN_IO will be optionally called from within the SET_SINGLE marco ; when the caller desires that all outstanding I/O's be drained from I/O ; execution, before the current I/O (an SCDRP), is allowed to continue. ; ; This routine when used in conjuntion with SET_SINGLE and its setting of ; UCB$V_BSY, and pairing a SET/CLR_SINGLE pair around a SCSI ordered command ; (which provides 'sequentiality' within a SCSI target), offers the functionality ; of "strong sequentiality". "Strong sequentiality", is a guarantee that "all" ; I/O's issued before this I/O are executed to completion, that this I/O is ; executed while no other I/O's are active, and upon completion of this I/O, ; other I/O (if present), will be started, unless other reason are indicated ; in UCB$L_CLASS_BUSY. ; ; ; Context: ; ; Fork thread or Class Driver thread ; IPL = Fork ; Fork lock held ; ; Calling sequence: ; ; SET_SINGLE ,ucb,label,scdrp,can$ ; MOVAL , R2 ; A SCSI command ; SETUP_CMD ; Perform setup for SCSI command ; SEND_COMMAND_ORDERED ; Send non-queued (ordered) command ; CLEANUP_CMD ; Cleanup from the SCSI command ; movl final_status,r0 ; Set final I/O status (if needed) ; can$: CLR_SINGLE ,ucb,label ; ; Note: the presence of the can$ parameter in the SET_SINGLE invocation generates ; the call to this routine and produces the "strong sequentiality" function. ; ; Input: UCB - Address of the UCB ; SCDRP - Address of SCDRP ; ; Output: R0 - STATUS SS$_NORMAL ; DK_DRAIN_IO: .CALL_ENTRY PRESERVE= $ARG_DEF ;F FF; SCDRP address ; ; Get input parameters MOVL ucb(AP), R3 ;F FF; Get current UCB addres MOVL scdrp(AP), R5 ;F FF; Get current SCDRP address ; Deterimine if all outstanding I/O's (include this one), are all stalled on ; the UCB's list of I/O's waiting for I/O drain to complete. ; INCL UCB$IS_DRAIN_COUNT(R3) ;F FF; One more I/O wait for drain CMPL UCB$IS_DRAIN_COUNT(R3),- ;F FF; Are there still I/O's waiting? UCB$L_QUEUED_IO_COUNT(R3) ;F FF; BEQL 10$ ;F FF; If equal, all I/O's accounted for BGTR 20$ ;F FF; If greater, too many I/O's, BUG_CHECK ; This I/O operation (SCDRP and associated KPB), needs to stall waiting for ; other active I/O's to complete. Place this I/O on the drain list, and stall ; waiting for I/O's to complete ; INSQUE SCDRP$PS_PQFL(R5), - ;F FF; Enqueue SCDRP on tail of I/O drain @UCB$Q_DRAIN_LIST(R3) ;F FF; wait queue MOVL SCDRP$PS_KPB(R5), R0 ;F FF; Get SCDRP's KPB address MOVAL G^IOC$RETURN, - ;F FF; Set NULL stall routine address KPB$PS_SCH_STALL_RTN(R0) ;F FF; PUSHL R0 ;F FF; Push KPB address CALLS #1,G^EXE$KP_STALL_GENERAL ;F FF; Wait until other I/O's complete 10$: DECL UCB$IS_DRAIN_COUNT(R3) ;F FF; I/O is no longer on drain list RET ;F FF; Draining operation complete 20$: BUG_CHECK INCONSTATE,FATAL ;S FF; bugcheck .PAGE .SBTTL STOP_NEW_IO - Block Unwanted I/O Initiation from REQCOM ;++ ; STOP_NEW_IO ; ; STOP_NEW_IO must be called before any operation that gives control ; to REQCOM when one or more CLASS_BUSY bits are set (see TST_SINGLE). ; If necessary, STOP_NEW_IO will place a "golden" IRP at the front of ; the UCB$L_IOQ to prevent new work from being started. ; ; Remember, any call to REQCOM authorizes the VMS I/O executive to ; send another IRP to this driver's start I/O routine. The VMS I/O ; executive views all drivers that do not use the alternate start I/O ; routine as single-threaded drivers. From this perspective, this ; driver has just completed its one and only outstanding unit of work. ; Therefore, this driver must be ready for more work. ; ; Unfortunately, this driver may not always be ready to accept new ; work when REQCOM is called. Readiness to accept new work must ; be determined from information in the CLASS_BUSY bits and the ; QUEUED_IO_COUNT field. When new work cannot be accepted, normal ; REQCOM work will be blocked by placing a "golden" IRP at the front ; of the UCB$L_IOQ. REQCOM will deliver the "golden" IRP to this ; driver's start I/O routine. The start I/O routine has been designed ; to recognize and discard the "golden" IRP. In this way, the normal ; (start a new I/O) function of REQCOM is subvirted and blocked. ; ; To a first order measure, new I/O requests must be blocked when ; any CLASS_BUSY bits are set. This would be the only rule, if all ; CLASS_BUSY bits were set and cleared based on the needs of this ; class driver or the port drivers that it calls. However, some of ; the CLASS_BUSY bits are set and cleared based on the needs of layers ; above this class driver. For convenient reference these bits are ; call the UL_BUSY bits. Bits that fall in UL_BUSY catagory are: ; ; UCB$V_CB_MNTVERIP ; ; When only UL_BUSY bits are set, then QUEUED_IO_COUNT determines ; when new I/O requests should be blocked. QUEUED_IO_COUNT enters ; the equation at this point because we cannot let more I/O requests ; in to the driver until the driver is truely single streamed (no ; outstanding requests in the port driver). So, if only UL_BUSY ; bits are set and QUEUED_IO_COUNT is non-zero, then new I/O requests ; still must be blocked. But when only UL_BUSY bits are set and ; QUEUED_IO_COUNT is zero, then new I/O requests can be (and must be) ; allowed. ; ; Allowing new I/O requests through when only UL_BUSY bits are set is ; very important. Generally, the upper layer that caused the related ; UL_BUSY bit to be set cannot perform the I/O requests that it needs ; to do when this driver blocks those I/O requests because the UL_BUSY ; bit is set. For example, mount verification cannot do its IO$_PACKACK ; if this driver blocks it just because the UCB$V_CB_MNTVERIP bit is ; set. ; ; "Golden" IRP requeue protection: When mount verification is in ; progress, it is possible for mount verification IRPs to displace ; the "golden" IRP at the head of the I/O request queue. As more ; in-progress I/O requests complete and this routine gets called ; for each completing request, it is possible that this routine will ; attempt to queue the "golden" IRP multiple times. To prevent this, ; we use the forward pointer of the "golden" IRP as a "in use" flag. ; If the forward pointer is non-zero, then the "golden" IRP is queued. ; If the forward pointer is zer, then the "golden" IRP is not queued. ; DK_STARTIO collaborates with this routine by zeroing the forward ; pointer of the "golden" IRP everytime it is processed (which is the ; same as everytime the "golden" IRP is dequeued). ; ; Context: ; ; Fork thread or Class Driver thread ; IPL = Fork ; Fork lock held ; ; Calling sequence: ; ; TST_SINGLE 10$ ; For best performance ; BRB ios_not_stopped ; ; 10$: PUSHL ucb ; CALLS #1,STOP_NEW_IO ; BLBS R0,ios_not_stopped ; ; ; ; Other blocked I/O handling (if any) ; ; ; ; Input: UCB (val) - Base address of the UCB ; ; Output: R0 - STATUS SS$_NORMAL - Requests Not Blocked ; SS$_TOOMUCHDATA - Requests Blocked ; ; UCB$R_BUSY_BIT_IRP may be inserted at the head of ; UCB$L_IOQFL. ; ; All other registers are preserved. ;-- UCB$M_CB_UL_BUSY = STOP_NEW_IO: ;FC FF; Block Unwanted I/Os .CALL_ENTRY PRESERVE= $ARG_DEF ;FC FF; UCB Address MOVL ucb(AP), R5 ;FC FF; Get current UCB MOVL #SS$_NORMAL, R0 ;FC FF; Set preferred status ; Begin with CLASS_BUSY bit tests. TST_SINGLE 10$,flag_reg=R8 ;FC FF; Any class busy bits set? BRB 90$ ;FC FF; No busy bits set. 10$: BICL #UCB$M_CB_UL_BUSY, R8 ;FC FF; Clear UL_BUSY bits. BNEQ 70$ ;FC FF; Any other busy bits set? ; At this point, we know that only UL_BUSY bits are set. ; It's time to check UCB$L_QUEUED_IO_COUNT. TSTL UCB$L_QUEUED_IO_COUNT(R5) ;FC FF; I/O still in port? BEQL 90$ ;FC FF; No, let I/Os pass. ; At this point, we know new I/O requests must be blocked. ; If the "golden" IRP is not already queued, then queue it. 70$: TSTL UCB$R_BUSY_BIT_IRP(R5) ;FC FF; Is golden IRP queued BEQL 75$ ;FC FF; No, go queue it MOVAL UCB$L_IOQFL(R5),R0 ;F FF; Get the correct back pointer CMPL UCB$R_BUSY_BIT_IRP+4(R5),R0 ;F FF; Is "golden" IRP first in Q? BEQL 77$ ;F FF; Yes, leave it there. ; ; OK! The "golden" IRP is burried in the I/O request queue. ; Remove it (then reinsert it at the beginning). ; REMQUE UCB$R_BUSY_BIT_IRP(R5), R0 ;F FF; Remque the "golden" IRP TRACE_SINGLE 8101 ;F FF; Trace golden IRP lost 75$: INSQUE UCB$R_BUSY_BIT_IRP(R5),- ;FC FF; Queue BUSY_BIT_IRP to UCB$L_IOQFL(R5) ;FC FF; head of UCB I/O queue. 77$: MOVL #SS$_TOOMUCHDATA, R0 ;FC FF; Set blocked status TRACE_SINGLE 1111 ;FC FF; Trace golden IRP insert ; All done! 90$: RET .PAGE .SBTTL DK_STARTIO - The Start I/O Routine for KP Drivers ;++ ; DK_STARTIO ; ; Drivers using KP services in their start I/O thread replace the ; DPT start I/O routine pointer with a pointer to this routine. This routine ; then starts a kernel process using the contents of DPT$PS_KP_STARTIO, ; which the driver setups up as the top-level routine for the thread. ; ; Context: ; ; Fork thread ; IPL = Fork ; Fork lock held ; ; Calling sequence: ; ; Standard step 2 driver start I/O interface: ; ; KP_STARTIO(IRP,UCB) ; ; Input: IRP (val) - Base address of the IRP ; UCB (val) - Base address of the UCB ; ; Output: none to the caller ; but the I/O thread is started ;-- get$ddtstart=0 DK_STARTIO:: ;F FF; Start I/O Routine for KP Drivers $DRIVER_START_ENTRY PRESERVE = FETCH=YES $ARG_DEF ;F FF; UCB address ONCE_DK_DEBUG DK_STARTIO ; ; Get input parameters ; MOVL irp(AP), R3 ;F FF; Get current IRP MOVL ucb(AP), R5 ;F FF; Get current UCB MOVAL UCB$R_BUSY_BIT_IRP(R5), R6 ;F FF; Get address of BUSY_BIT_IRP ; Determine if the IRP just dequeue is the BUSY BIT IRP. 10$: CMPL R3, R6 ;F FF; Is the current IRP, our BUSY_BIT_IRP? BEQL 100$ ;F FF; Yes, do special 'busy-bit' processing ; Verify that the driver is ready for another I/O request. ; If the driver is busy, then just requeue this I/O ; request to the head of the I/O queue 20$: MOVL UCB$L_STS(R5), R1 ;F FF; Get current I/O status TST_SINGLE 80$,flag_reg=r0 ;F FF; Branch if there any BUSY Bits sets ; The class driver can accept this I/O request for processing. ; Handle Mount Verification in progress, but not seen .BRANCH_LIKELY ;F FF; BBC #UCB$V_MNTVERIP, R1, 30$ ;F FF; Branch if mount ver not active BBS #IRP$V_SHDIO,IRP$L_STS2(R3),30$ ;F FF; Branch if this is a shadowing I/O ; At this point, we have found that mount verification is in progress but our ; class driver CLASS_BUSY flags don't show mount verification in progress. This ; must have occured because mount verification was started when there were no ; I/O's active. That most likely happened in a cluster state transition, but ; we don't really care about the reason. What we MUST do is get the CLASS_BUSY ; flag set for mount verification in progress. After that, we can assume that ; the I/O we have in hand is a mount verification IRP. So, it needs to be ; processed in the usual way for MVIRPs. ; SET_SINGLE MNTVERIP,R5 ;F FF; Single thread MOUNT Verification BRB 40$ ;F FF; Continue without clearing BUSY 30$: BICL #UCB$M_BSY, R1 ;F FF; Clear busy 40$: BICL #UCB$M_CANCEL!UCB$M_TIMOUT,R1 ;F FF; Clear cancel, timout MOVL R1, UCB$L_STS(R5) ;F FF; Set current device status PUSHAL DK_STARTIO_PAUSE ;F FF; Address of pause routine PUSHL R5 ;F FF; UCB start I/O parameter PUSHL R3 ;F FF; IRP start I/O parameter CALLS #3,G^EXE_STD$KP_STARTIO ;F FF; Start the I/O request. ; This I/O request has been started and has stalled. ; Dequeue another I/O and attempt to start it ; 50$: REMQUE @UCB$L_IOQFL(R5), R3 ;F FF; Any I/O queued to this device? BVC 70$ ;F FF; Yes, process I/O TST_SINGLE 60$ ;F FF; Are there any other reasons not to clear busy? BICL #UCB$M_BSY, UCB$L_STS(R5) ;S FF; Clear UCB busy bit 60$: RET ;F FF; ; Parts of the following code segement is lifted from [SYS]IOSUBNPAG.MAR IOC_STD$INITIATE: ; This was done to to prevent a recusive call to IOC$INITIATE at this call level. ; 70$: ASSUME DDT$PS_START_2 NE 0 .IF DF CA$_MEASURE_IOT BSBW PMS$START_IO ;F FF; Insert start of I/O transaction message .ENDC ; Determine if diagnostic buffer needs to be setup ; .BRANCH_LIKELY BBC #IRP$V_DIAGBUF,IRP$L_STS(R3),10$;F FF; If clear, no diagnostic buffer MOVL @IRP$L_DIAGBUF(R3),R0 ;F FF; Get address of diagnostic buffer data area .Disable Flagging ; Known quadword access MOVQ G^EXE$GQ_SYSTIME,(R0) ;F FF; Insert I/O operation start time .Enable Flagging ;; BRB 10$ .IF DF,GET$DDTSTART ; Fork here to let the stack clear and then, in the fork routine, call ; the real start entry. Use call_initiate so we can be sure CPU is right ; after the fork and so the irp<->ucb linkage gets set up right. BRW 110$ .IFF BRB 10$ .ENDC ; The driver is in single-threaded mode. ; There are one or more class-busy bits set and thus the UCB$V_BSY is set. ; If Mount Verification is in progress and this is a MVIRP and there are ; no other reasons for blocking new I/O requests, the send the IRP to ; the real (KP) start I/O routine. Otherwise, gum-up the UCB pending ; I/O queue and blast the current request directly back to mount ; verification. ; ; NOTE: (Care was taken to place UCB$L_STS(R5) in R1 80$: BBC #UCB$V_MNTVERIP,R1,85$ ;F FF; If Mount Verification in progress BBC #IRP$V_MVIRP,IRP$L_STS(R3),85$ ;F FF; and Mount Verification IRP PUSHL R5 ;F FF; P1 = UCB address CALLS #1,STOP_NEW_IO ;F FF; Are new I/Os blocked? BLBS R0,40$ ;F FF; No, start the MVIRP. CLRL R1 ;S FF; Clear transfer byte count MOVL #SS$_PARITY,R0 ;F FF; Can't do MVIRP now. MOVL R3,UCB$L_IRP(R5) ;S FF; Set up UCB$L_IRP for the actual ;S FF; IRP that's completing! CALL_REQCOM ;F FF; let MV handle retry CLRL UCB$L_IRP(R5) ;F FF; This I/O has been processed. BRB 90$ ;F FF; Nothing else to do ; Non-Mount Verification I/O, just requeue and it will be retried later ; 85$: INSQUE IRP$L_IOQFL(R3),UCB$L_IOQFL(R5) ;F FF; else Queue IRP to head of queue CLRL UCB$L_IRP(R5) ;F FF; No "current" request is pending. ; The driver needs to run in single threaded mode for some reason. ; This reason is enumerated in UCB$L_CLASS_BUSY. No matter what ; the reason, we should make the attempt to prevent further I/O ; requests from starting until the driver is not busy. 90$: BISL #UCB$M_BSY, UCB$L_STS(R5) ;F FF; Set UCB busy bit RET ; This IRP is the BUSY_BIT_IRP. ; Test the CLASS_BUSY bits to determine if: ; --- DK_STARTIO just exits leaving UCB$V_BSY set ; --- An attempt is made to dequeue another I/O. ; In both cases the current BUSY_BIT_IRP is ingored and left dequeued. ; 100$: CLRL UCB$R_BUSY_BIT_IRP(R5) ;F FF; Clear golden IRP usage flag TST_SINGLE 90$ ;F FF; If busy bits set, exit setting busy BRB 50$ ;F FF; No busy bits, attempt another I/O .IF DF,GET$DDTSTART ; Fork here to let the stack clear and then, in the fork routine, call ; the real start entry. Use call_initiate so we can be sure CPU is right ; after the fork and so the irp<->ucb linkage gets set up right. ; do this at the end so we don't break the routine up internally. ; Presumes R5 = UCB and R3 = IRP here. 108$: CALL_INITIATE RET 110$: lcldk_f=0 .if df,lcldk_f minstkctx=512 ;number bytes to require left on stack ; If the stack pointer is not within a pagelet of the page granularity, ; we can know a priori that it has at least that much further it can be ; safely pushed, since it is page aligned. Therefore we can use this fact ; to avoid any PALcode calls from probe if so. ; ; Use the current page size globals for generality. mcoml g^MMG$GL_BWP_MASK,R0 ; get the bytes within page mask ; Now R0 has all bits set EXCEPT those within the page granularity bicl3 r0,sp,r0 ; Now R0 has the page part of sp cmpl r0,#minstkctx ; Is the SP above the min? bgtru 108$ ; if so, just keep using this ; don't use probew here because there aren't always guard pages ; Stack is short, so fork, THEN call the initiate routine. 115$: ; space is still adequate. FORK ENVIRONMENT=CALL CALL_INITIATE RET .iff brw 108$ .endc .ENDC ; ; If EXE_STD$DK_STARTIO can't start the KPB requested, it will callback this routine ; to tell DKDRIVER, that the I/O is being stalled and once again when the I/O is ; being unstalled. ; DK_STARTIO_PAUSE:: .CALL_ENTRY INPUT=<>,OUTPUT=,- SCRATCH=, PRESERVE= $ARG_DEF ;F FF; Flag (1=stall, 0=unstall) MOVL ucb(AP), R5 ;F FF; Get UCB Address TSTL flag(AP) ;F FF; Is this a stall call? BEQL 10$ ;F FF; If zero, than unstall SET_SINGLE KP_STARTIO,R5,30$ ;F FF; Single thread KP_STARTIO stall BRB 20$ ;F FF; Return 10$: CLR_SINGLE KP_STARTIO,R5,30$ ;F FF; Single thread KP_STARTIO unstall 20$: RET 30$: BUG_CHECK INCONSTATE,FATAL ;F FF; bugcheck .PAGE .SBTTL DK_KP_STARTIO - Driver QIO entry point ;+ ; DK_KP_STARTIO ; ; This routine is the QIO entry point into the driver. Its main function ; is to dispatch to the function-code-specific routine which then executes ; the QIO. The routine runs as a kernel process, which terminates through ; a call to the KP_REQCOM macro. ; ; Context: ; ; Class Driver thread ; IPL = Fork ; Fork lock held ; ; Inputs: ; ; KPB(AP) - KPB address ; ; Outputs: ; ; R0 - 1st longword of I/O status: contains status code and ; number of bytes transferred ; R1 - 2nd longword of I/O status: l.o. word contains h.o. ; word of number of bytes transferred ; R2-R8 - Preserved ;- DK_KP_STARTIO:: ;C FF; Driver QIO entry point ; Compiler uses R13-R15 for RET-under-JSB calls ; .CALL_ENTRY $ARG_DEF ;C FF; KPB address DK_DEBUG DK_KP_STARTIO MOVL kpb(AP),R0 ;C FF; Get KPB address MOVL KPB$PS_UCB(R0),R5 ;C FF; Get UCB address MOVL KPB$PS_IRP(R0),R3 ;C FF; Get IRP address MOVL UCB$L_PDT(R5),R4 ;C FF; Get PDT address MOVL R3,R2 ;C FF; Copy IRP address MOVL R5,R3 ;C FF; Copy UCB address DK_ALLOC_SCDRP ;C FF; Allocate an SCDRP MOVL R2,SCDRP$L_IRP(R5) ;S FF; Save IRP address in SCDRP CLRL UCB$L_IRP(R3) ;S FF; Multiple I/Os are now possible. .IF DEFINED TRACE BSBW TRACE_QIO ;C FF; Trace the current I/O request .ENDC INCL UCB$L_QUEUED_IO_COUNT(R3) ;S FF; Account for this I/O request. INCL UCB$L_OTHER_COUNT(R3) ;S FF; Assume not a read/write for now ASSUME IRP$L_IOQBL EQ 4+IRP$L_IOQFL INSQUE IRP$L_IOQFL(R2),- ;S FF; Remember this IRP is active. @4+UCB$Q_IRP_LIST(R3) ;S FF; MOVL IRP$PS_KPB(R2),- ;S FF; Save KPB address in SCDRP SCDRP$PS_KPB(R5) ; ; Allow only physical I/O functions before a PACKACK is issued. ; BBS #IRP$V_PHYSIO,IRP$L_STS(R2),10$ ;S FF; Branch if physical I/O function BBC #UCB$V_VALID,UCB$L_STS(R3),- ;S FF; Logical or virtual I/O function VOLUME_INVALID2 ;S FF; Branch if volume is invalid 10$: EXTZV #IRP$V_FCODE,#IRP$S_FCODE,- ;S FF; Extract I/O function code IRP$L_FUNC(R2),R1 ;S FF; ASSUME IRP$S_FCODE LE 7 ;S FF; Allow byte mode dispatch DISPATCH R1,TYPE=B,<- ;S FF; Dispatch according to function ,- ;S FF; No operation ,- ;S FF; Unload drive ,- ;S FF; Seek cylinder ,- ;S FF; Recalibrate drive ,- ;S FF; Pack acknowledge ,-;S FF; Write check data ,- ;S FF; Write physical block ,- ;S FF; Read physical block ,- ;S FF; Write header and data ,- ;S FF; Read header and data ,- ;S FF; Available (disk and tape class) ,- ;S FF; Data security erase (and rewind) ,- ;S FF; Diagnose ,- ;S FF; Read logical block ,- ;S FF; Format > ;S FF; Cd-rom audio ; Bogus I/O function code if we fall through. Set illegal function code ; status and complete the I/O. IO_BOGUS:: .GLOBAL_LABEL IO_SEEK:: .GLOBAL_LABEL IO_RECAL:: .GLOBAL_LABEL IO_WRITEHEAD:: .GLOBAL_LABEL IO_READHEAD:: .GLOBAL_LABEL IO_READLBLK:: .GLOBAL_LABEL DK_DEBUG ILLEGAL_IO MOVZBL #SS$_ILLIOFUNC,R0 ;S FF; Specify the error type BRB COMPLETE_IO ;S FF; ;+ ; The volume was not software enabled, however audio functions don't ; require that the volume (disk) be software enabled. If the I/O function is ; an audio function then goto the audio fdt code. ;- VOLUME_INVALID2:: .GLOBAL_LABEL EXTZV #IRP$V_FCODE,#IRP$S_FCODE,- ;S FF; Extract I/O function code IRP$L_FUNC(R2),R1 ;S FF; ASSUME IRP$S_FCODE LE 7 ;S FF; Allow byte mode dispatch CMPB #IO$_AUDIO,R1 ;S FF; Is this an AUDIO function? BEQL IO_AUDIO ;S FF; Yes, do audio function VOLUME_INVALID:: .GLOBAL_LABEL CLRL R1 ;S FF; Zero R1. MOVZWL #SS$_VOLINV,R0 ;S FF; It's not a valid volume ; BRB COMPLETE_IO ;S FF; Fall through to complete the I/O ; ; R0 = QIO status ; COMPLETE_IO:: .GLOBAL_LABEL ASSUME IRP$L_IOQFL EQ 0 ASSUME IRP$L_IOQBL EQ 4+IRP$L_IOQFL DECL UCB$L_QUEUED_IO_COUNT(R3) ;S FF; Adjust I/O count BGEQ 1$ ;S FF BUG_CHECK INCONSTATE,FATAL ;S FF; Somethings wrong if this is neg! 1$: REMQUE @SCDRP$L_IRP(R5),UCB$L_IRP(R3) ;S FF; Set up UCB$L_IRP for the actual ;S FF; IRP that's completing! .IF DEFINED TRACE BSBW TRACE_QIO_STAT ;C FF; Save the final QIO status in trace buf BLBS R0,5$ ;C FF; Branch on success status NOP ;C FF; Instruction to trap on QIO with bad status 5$: .ENDC DK_DEALLOC_SCDRP CLRSTK=NO ;S FF; Deactivate the SCDRP MOVL R3,R5 ;C FF; Copy UCB address .BRANCH_LIKELY BLBS R0,10$ ;C FF; Success, continue CLRL R1 ;C FF; Clear transfer byte count CMPL R0,#SS$_RECOVERR ;C FF; Recoverable error status? BEQL 100$ ;C FF; Branch if so, CMPL R0,#SS$_TIMEOUT ;C FF; Timeout error status? BEQL 110$ ;C FF; Branch if so, CMPL R0,#SS$_DRVERR ;C FF; Drive error status? BEQL 120$ ;C FF; Branch if so, CMPL R0,#SS$_CTRLERR ;C FF; Controller error status? BEQL 120$ ;C FF; Branch if so, CMPL R0,#SS$_DEVOFFLINE ;C FF; Offline error status? BEQL 120$ ;C FF; Branch if so, BRW 10$ ;S FF; Fall through ; In order to allow the lower layers to distinguish between recoverable and ; non-recoverable errors, a new status code was invented called SS$_RECOVERR. ; This status code should never make it up to this level unless the target ; returns a recoverable error sense key for a SCSI command from which we don't ; expect it. To prevent us from possibly returning a bogus status code for the ; QIO, translate this status to one that VMS knows about, namely, SS$_PARITY. 100$: MOVL #SS$_PARITY,R0 ;C FF; Change to a valid error status BRW 10$ ; Timeouts may have occurred that are merely the result of a bus reset (as ; frequently happens in multi-host configurations). Rather than failing ; this on the spot, convert the error to SS$_MEDOFL to allow retries. 110$: MOVL #SS$_MEDOFL,R0 ;C FF; Change to MEDOFL BRW 10$ ; For various other errors, force the driver into mount verify exactly ; once in order to give it a second chance. Set IRP$V_FORCEMV to indicate ; we are forcing mount verification. 120$: MOVL UCB$L_IRP(R5),R2 ;C FF; Get IRP address BBSS #IRP$V_FORCEMV,- IRP$L_STS2(R2),10$ ;C FF; Already MV'd once before, ;C FF; so no more allowed. MOVL #SS$_MEDOFL,R0 ;C FF; Convert to MEDOFL in order ;C FF; to force Mount Ver. ; If there are two or more IRPs in the pending queue, attempt to reorder ; the queue such that the IRP for the block closest to the current head ; position in the direction of the current head motion is executed first. ; ; Note: Don't perform re-order on devices with tagged queue support ; 10$: IF_CMDQ 20$,UCB=R5 ;C FF; Skip reordering for CMDQ devices MOVAL UCB$L_IOQFL(R5),R2 ;C FF; Get address of I/O pending queue in UCB CMPL @(R2),R2 ;C FF; Fewer than two pending IRPs? BEQL 20$ ;C FF; Branch if so, no need to reorder PUSHR #^M ;C FF; Save I/O status BSBW ATTEMPT_REORDER ;C FF; Attempt to reorder read/write request POPR #^M ;C FF; Restore I/O status 20$: TST_SINGLE 40$,flag_reg=R2 ;C FF; Are any class driver busy bits set? 30$: KP_REQCOM ;C FF; Complete the I/O, implied return ; By design, CALL_REQCOM will clear UCB$V_BSY if there are no I/O's present ; on the UCB I/O Pending Queue. Since the busy bit is referenced by various ; OpenVMS subsystems, DKDRIVER cannot let the busy bit to be cleared if in ; fact it is still busy. STOP_NEW_IO manages this. ; 40$: PUSHR #^M ;C FF; Save I/O status PUSHL UCB$L_IRP(R5) ;C FF; Save current IRP CMPL UCB$IS_DRAIN_COUNT(R5),- ;C FF; Are there only draining I/O's left? UCB$L_QUEUED_IO_COUNT(R5) ;C FF; BLSS 50$ ;C FF; If less, then I/O's still outstanding BNEQ 60$ ;C FF: If not equal, too many I/O's, BUG_CHECK 45$: REMQUE @UCB$Q_DRAIN_LIST(R5), R1 ;C FF; Dequeue entry from I/O drain list BVS 50$ ;C FF; Branch if list is empty MOVAL -SCDRP$PS_PQFL(R1), R1 ;S FF; Get starting address of SCDRP MOVL SCDRP$PS_KPB(R1), R1 ;S FF; Get KPB address PUSHL #SS$_NORMAL ;S FF; Return SS$_NORMAL PUSHL R1 ;S FF; KPB to restart CALLS #2,EXE$KP_RESTART ;S FF; restart this kernel process 50$: PUSHL R5 ;C FF; P1 = UCB address CALLS #1,STOP_NEW_IO ;C FF; Call STOP_NEW_IO POPL UCB$L_IRP(R5) ;C FF; Restore current IRP POPR #^M ;C FF; Restore I/O status BRB 30$ ;C FF; Complete current I/O 60$: BUG_CHECK INCONSTATE,FATAL ;S FF; bugcheck .PAGE .SBTTL DK_ALTSTARTIO - The Alternate Start I/O Routine ;++ ; DK_ALTSTARTIO ; ; Implementation of an alternate startio routine grows out of the ; following problem: ; ; Two-node SCSI clusters require a quorum disk for high availability ; operation. The quorum disk must be located on the SCSI bus shared by the ; two OpenVMS hosts. Successful counting of the quorum disk vote requires ; that quorum I/O reads and writes complete in a timely manner, even when ; other I/O operations normally are blocked (such as while the disk ; is undergoing mount verification). ; ; DKDRIVER must provide a mechanism for expeditious processing of quorum ; I/O requests. This mechanism must not block quorum I/O operations unless ; it is absolutely necessary to do so. ; ; The OpenVMS driver interface already defines a driver routine that is ; used to start I/O requests while bypassing the UCB$V_BSY bit and the ; pending I/O queue. That interface is the alternate start I/O routine. ; The alternate start I/O routine is exactly designed to meet the needs of ; the quorum I/O requests. Therefore, the code issuing quorum I/O requests ; has been modified to use EXE$ALTQUEPKT for drivers that have an alternate ; start I/O routine and DKDRIVER has been enhanced to have the ; alternate start I/O routine below. ; ; The operational details of DKDRIVER require that some restrictions be ; enforced in the alternate start I/O routine. There are two classes of ; restrictions: those that will never change for any given device; and ; those that may come and go during some time intervals. The former will ; result in termination of the request with a failure status; the latter ; will be retried. ; ; The failure status for restrictions that will never change is ; SS$_ILLIOFUNC. These restrictions are as follows. ; ; Tagged command queuing is required for expeditious handling of I/O opera- ; tions within a SCSI device. Some SCSI devics (or SCSI bus ports) do not ; support tagged command queuing. Expeditious request handling is not ; possible on these devices, or on any devices connects to a non tagged ; command queuing port. All IRPs received by the alternate start I/O ; routine for non tagged command queuing devices shall be terminated without ; execution. This is not an issue for quorum I/O requests because all shared ; SCSI busses and devices are required to support tagged command queuing. ; ; Only reads and writes are supported. It is very difficult for ; DKDRIVER to provide expeditious handling of functions other than reads ; and writes. Any I/O function other than IO$_READLBLK, IO$_READPBLK, ; IO$_WRITELBLK, and IO$_WRITEPBLK shall not be performed. Clearly, this ; is not a problem for quorum I/O, which are exclusively reads or writes. ; ; DKDRIVER cannot provide expeditious handling for the IO$M_DATACHECK ; modifier in particular and most read/write function modifiers in general. ; Therefore, any IRP with function modifiers set shall not be performed. ; Hopefully, this is not a problem for quorum I/Os. ; ; The following conditions may be resolved in a short time interval and ; will result in a retry of the operation: ; ; o one or more IRPs are waiting for outstanding I/O to drain ; o unit initialization in progress ; o SCSI bus reset processing is in progress ; o an I/O is stalled due to: ; - inability to allocate a KPB ; - lack of send credits ; - lack of queue tag values ; o one of the following I/O functions is being processed ; - IO$_UNLOAD ; - IO$_AVAILABLE ; - IO$_FORMAT ; ; DKDRIVER must never set the UCB$V_ALTBSY bit. Setting this bit would ; cause EXE$ALTQUEPKT to queue the IRPs that it receives in an alternate ; pending I/O queue. Such IRP queuing violates the design guarantees ; defined for the DKDRIVER alternate start I/O routine. ; ; Context: ; ; Fork thread ; IPL = Fork ; Fork lock held ; ; Calling sequence: ; ; Standard step 2 driver alternate start I/O interface: ; ; KP_STARTIO(IRP,UCB) ; ; Input: IRP (val) - Base address of the IRP ; UCB (val) - Base address of the UCB ; ; Output: the I/O thread is started, or ; SS$_UNSAFE is returned in IRP$L_IOST1 ; ; ************************** ; Implementation Notes ; ************************ ; ; The current issuer of quorum IO request is a system routine which only ; calls EXE$ALTQUEPKT with "perfect request" which satisfies all static ; checking listed above. This fact eliminates the need of a DKDRIVER FDT ; routine for checking of static conditions, hence EXE$ALTQUEPKT is called ; by the Quorum IO issuer directly in the current implementation. ; ; The redundant inclusion of static checking of IRP & UCB here is for the ; completeness of Quorum io processing in case *OTHER* Quorum io issuer ; may enter this routine without its IRP & UCB checked out beforehand. ; ; If in the future, there is new caller to this ALTSTARTIO routine for ; processing other than Quorum IO and it requires its own set of IRP & UCB ; checking, then all the existing and necessary static checking code here ; should be moved to FDT routines where they belong and to leave this ; routine as simple and generic as it should be to handle the starting of ; IOs only. [ This implies future Quorum IO call interace change.] ; ************************************************************ ; ;-- DK_ALTSTARTIO:: ;F FF; Alternate Start I/O Rtn $DRIVER_ALTSTART_ENTRY PRESERVE = $ARG_DEF ;F FF; UCB address MOVL irp(AP),R3 ;F FF; Get current IRP MOVL ucb(AP),R5 ;F FF; Get current UCB ; (an arbitrary choice) ; static UCB checking IF_NOT_CMDQ 20$,UCB=R5 ;F FF; If not TCQ, return ; static IRP checking ; with SS$_ILLIOFUNC EXTZV #IRP$V_FCODE,#IRP$S_FCODE,- ;F FF; Extract I/O func code IRP$L_FUNC(R3),R1 ; ASSUME IRP$S_FCODE LE 7 ;F FF; Allow byte mode dispatch DISPATCH R1,TYPE=B,<- ;F FF; Dispatch via function ,- ; Write physical block ,- ; Write logical block ,- ; Read physical block > ; Read logical block ; All other function codes return SS$_ILLIOFUNC. BRB 20$ ;F FF; Go to error exit 10$: EXTZV #IRP$V_FMOD,#IRP$S_FMOD,- ;F FF; Extract I/O modifiers IRP$L_FUNC(R3),R1 BEQL 30$ ;F FF; If no modifier, continue 20$: MOVQ #SS$_ILLIOFUNC,IRP$L_IOST1(R3) ;F FF; otherwise, error exit BRB 70$ ;F FF; 30$: MOVW #3,IRP$L_IOST2+2(R3) ;F FF; maximum retry twice ; transient UCB condition checking 40$: TSTL UCB$IS_DRAIN_COUNT(R5) ;F FF; If drain count nonzero, BNEQ 60$ ; then error exit ; Now test the various relevant class busy bits. ; The CB_NOCMDQ bit need not be tested because it already has been tested ; above. The following class busy bits need not be tested because only ; tagged command queuing devices are supported in the alternate start I/O ; routine and because tagged command queuing devices are required to do ; their own bad block replacement: CB_SINGLE_RW, CB_BBR_IN_PROG, and CB_ ; FORCE_ERROR. The following class busy bits need not be tested because a ; tagged command queuing device can process the condition that they reflect ; and still accept another read or write: CB_MNTVERIP, CB_CHECK_CONDITION, ; CB_QUEUE_FULL_EVT, CB_SINGLE_DC, CB_SINGLE_DSE, CB_PACKACK, CB_NOP, and ; CB_AUDIO. The CB_BUSY bit is not listed above because there is no ; apparent instance where it is used. BITL #,- ; IO$_FORMAT in progress UCB$L_CLASS_BUSY(R5) ; Test class busy bits BNEQ 50$ ;F FF; Branch if any set ; If the DKDRIVER alternate start I/O routine finds that the IRP passed to ; it can be processed, it simply calls EXE$KP_STARTIO. This starts KP ; processing of the request in exactly the same way that a similar call in ; DK_START-IO starts normally queued requests. However, the DKDRIVER ; alternate start I/O routine does not contain a loop that looks for ; additional requests to ; start (such as is found in DK_STARTIO). The ; alternate start I/O routine is a one-shot (do this one I/O and quit) routine. PUSHAL DK_STARTIO_PAUSE ;F FF; Address of pause routine PUSHL R5 ;F FF; UCB start I/O parameter PUSHL R3 ;F FF; IRP start I/O parameter CALLS #3,G^EXE_STD$KP_STARTIO ;F FF; Start the I/O request. ; Typically, alternate start I/O routines complete I/O requests using ; the COM$POST routine. DKDRIVER need not do this. For DKDRIVER, the only ; difference between an IRP that passes through the alternate start I/O ; routine and a normally queued IRP is the path by which that IRP got to ; EXE$KP_STARTIO. After that, the existing (normal) DKDRIVER request queue ; and busy bit management all apply. Therefore, DKDRIVER completes all ; IRPs in the same way, using REQCOM without regard for the source (start ; I/O or alternate start I/O) that started the IRPs KP thread. BRB 80$ ;F FF; and return 50$: DECW IRP$L_IOST2+2(R3) ;F FF; Any more retry? BEQL 60$ ;F FF; No, both retries failed MOVL R5,R4 ;F FF; Save UCB address MOVAB IRP$L_FQFL(R3),R5 ;F FF; Get address of fork blk MOVB UCB$B_FLCK(R4),FKB$B_FLCK(R5) ;F FF; Copy forklock index FORK_WAIT ENVIRONMENT=CALL ;F FF; Go wait for 1 sec. MOVL R4,R5 ;F FF; Restore UCB address BRW 40$ ;F FF; Go to top of retry loop 60$: MOVQ #SS$_UNSAFE,IRP$L_IOST1(R3) ;F FF; Store I/O status code, ; Clear rest of I/O status block. 70$: CALL_POST SAVE_R1=NO ;F FF; COM$POST the request 80$: RET ;F FF; Return .PAGE .SBTTL DK_CANCEL - Cancel I/O routine ;+ ; DK_CANCEL ; ; This routine cancels an I/O in progress. Since the timeouts for disks are ; relatively short, no device-dependent action is taken. Instead, it relies ; on the port driver's relatively short timeout to terminate any I/O in ; progress. ; ; Context: ; ; Fork thread ; IPL = Fork ; Fork lock held ; ; Step 2 Interface ; ; void CANCEL (channel , IRP *, PCB *, UCB *, reason) ; ; Obsolete Step 1 Inputs: ; ; R2 - channel index number ; R3 - address of the current IRP (I/O request packet) ; R4 - address of the PCB (process control block) for the ; process canceling I/O ; R5 - address of the UCB (unit control block) ; R8 - cancel reason code, one of: ; CAN$C_CANCEL if called through $CANCEL or ; $DALLOC system service ; CAN$C_DASSGN if called through $DASSGN system ; service ; CAN$C_MSCPSERVER if called through MSCP server ; Obsolete Step 1 Outputs: ; ; R0,R1 - Destroyed ; All other registers preserved ;- DK_CANCEL:: ;F FF; Cancel an I/O operation $DRIVER_CANCEL_ENTRY PRESERVE = ONCE_DK_DEBUG DK_CANCEL CMPL R8,#CAN$C_MSCPSERVER ;F FF; MSCP server cancel? BEQL 400$ ;F FF; Branch if yes ; N.B. Determine if IRP equals 0, this can happen during UNIT INIT ; in bringing the device online ; TSTL R3 ;F FF; IRP address of 0? BEQL 10$ ;F FF; Branch if not CALL_CANCELIO SAVE_R0R1=NO ;F FF; Set cancel bit if appropriate. 10$: RET ;F FF; Return ; MSCP server has requested cancel of all its I/O's. ; Find them, and mark them so that they will make a ; speedy exit before their next attempt to send a ; command to the port. ASSUME IRP$L_IOQFL EQ 0 400$: MOVAB UCB$Q_IRP_LIST(R5),R6 ;F FF Get know IRPs listhead MOVL R6,R7 ;F FF Save it ;F FF 440$: MOVAB IRP$L_IOQFL(R6),R6 ;F FF Get next known IRP CMPL R6,R7 ;F FF Back to listhead? BEQL 480$ ;F FF Branch if yes ;F FF CMPL R2,IRP$L_CHAN(R6) ;F FF Is this a server IRP? BNEQ 440$ ;F FF Branch if no CMPL R4,IRP$L_PID(R6) ;F FF Is this a server IRP? BNEQ 440$ ;F FF Branch if no ;F FF BISL #IRP$M_SRV_ABORT,IRP$L_STS(R6) ;F FF Mark server's IRP for ;F FF speedy exit BRW 440$ ;F FF Look for more ;F FF 480$: RET ;F FF Server cancel done, exit .PAGE .SBTTL DK_REG_DUMP - Device register dump routine ;+ ; DK_REG_DUMP ; ; This routine dumps device-specific infromation into an errorlog packet. ; The format of this information is as follows: ; ; +-----------------------+ ; | Longword count | 4 bytes ; ERR$L_LW_CNT ; +-----------------------+ ; | Revision | 1 byte ; ERR$B_REVISION ; +-----------------------+ ; | HW revision | 4 bytes ; ERR$L_HW_REV ; +-----------------------+ ; | Error Type | 1 byte ; ERR$B_TYPE ; +-----------------------+ ; | SCSI ID | 1 byte ; ERR$B_SCSI_ID ; +-----------------------+ ; | SCSI LUN | 1 byte ; ERR$B_SCSI_LUN ; +-----------------------+ ; | SCSI SUBLUN | 1 byte ; ERR$B_SCSI_SUBLUN ; +-----------------------+ ; | Port status | 4 bytes ; ERR$L_PORT_STATUS ; +-----------------------+ ; | SCSI CMD | n bytes ; ERR$A_CMD_LEN ; +-----------------------+ ; | SCSI STS | 1 byte ; ERR$B_SCSI_STS ; +-----------------------+ ; | | ; | Additional Data | n bytes ; ERR$A_ADD_LEN ; | | ; +-----------------------+ ; ; The contents of the addition data field depends upon the error type. For ; example, extended sense errors save the extended sense data returned by the ; target in this field. ; ; ; Context ; ; Same IPL, Fork and Locks as calls to: ; ERL$DEVICERR, ERL$DEVICTMO, ERL$DEVICEATTN and IOC$DIAGBUFILL ; ; Step 2 Interface ; ; void REGDUMP (buffer *, CRAM *, UCB *) ; ; Obsolete Step 1 Inputs: ; ; R0 - Output buffer address ; R5 - UCB address ; ; Obsolete Step 1 Outputs: ; ; R0-R2 - Destroyed ; All other registers perserved ;- DK_REG_DUMP:: ;? ??; Device register dump routine $DRIVER_REGDUMP_ENTRY PRESERVE= PUSHAL (R0)+ ;? ??; Save start of data area MOVB #DK_ERROR_REVISION,(R0)+ ;? ??; Save revision level MOVL UCB$L_HW_REV(R5),(R0)+ ;? ??; Save hardware revision level MOVB UCB$L_ERROR_TYPE(R5),- ;? ??; Save error type (R0)+ MOVZWL UCB$W_UNIT(R5),R1 ;? ??; Get unit number CLRL R2 ;? ??; Prepare for extended divide EDIV #100,R1,R1,R2 ;? ??; Extract SCSI bus ID from unit number MOVB R1,(R0)+ ;? ??; Save SCSI bus ID MOVL R2,R1 ;? ??; Copy LUN, SUBLUN CLRL R2 ;? ??; Prepare for extended divide EDIV #10,R1,R1,R2 ;? ??; Extract LUN and SUBLUN MOVB R1,(R0)+ ;? ??; Save LUN field MOVB R2,(R0)+ ;? ??; Save SUBLUN field MOVL UCB$L_VMS_STATUS(R5),- ;? ??; Save port status code (R0)+ MOVW #^XFF00,(R0)+ ;? ??; Assume no SCSI CMD,STS available CLRB (R0)+ ;? ??; Assume no additional data MOVL UCB$PS_SCDRP(R5),R1 ;? ??; Get active SCDRP address BEQL 50$ ;? ??; Branch if none active MOVL SCDRP$L_CMD_PTR(R1),R2 ;? ??; Get address of SCSI command BEQL 50$ ;? ??; Branch if none active SUBL #3,R0 ;? ??; Back up pointer MOVL (R2)+,R3 ;? ??; Get number of SCSI command bytes MOVB R3,(R0)+ ;? ??; Save command length 10$: MOVB (R2)+,(R0)+ ;? ??; Save a command byte SOBGTR R3,10$ ;? ??; Continue for entire SCSI command 20$: MOVB #-1,(R0)+ ;? ??; Assume no valid status byte MOVL SCDRP$L_STS_PTR(R1),R2 ;? ??; Get address of status byte BEQL 25$ ;? ??; Branch if no status byte MOVB (R2),-1(R0) ;? ??; Save SCSI status byte 25$: CLRB (R0)+ ;? ??; Assume no additonal data MOVL UCB$L_ERROR_TYPE(R5),R2 ;? ??; Save error type DISPATCH R2,TYPE=B,<- ;? ??; Dispatch according to error code ,- ,- ,- ,- ,- ,- > BUG_CHECK INCONSTATE,FATAL ;? ??; Unknown error type. This should ;? ??; never happen. ; Error types which have additional data come here. 30$: MOVL SCDRP$L_SVA_USER(R1),R2 ;? ??; Get address of additional data BEQL 50$ MOVZBL SCDRP$L_TRANS_CNT(R1),R1 ;? ??; Get length of additional data BEQL 50$ ;? ??; Branch if none available BRB 36$ ;? ??; Branch to bounds-check code ; Special entry point for reassign block error. The additional data length ; is contained in SCDRP$L_BCNT rather than in SCDRP$L_TRANS_CNT. 35$: MOVL SCDRP$L_SVA_USER(R1),R2 ;? ??; Get address of additional data MOVZBL SCDRP$L_BCNT(R1),R1 ;? ??; Get length of additional data 36$: ; When we invoke a LOG_ERROR macro, ERL_STD$DEVICERR or ERL_STD$DEVICEATTN ; is invoked to do the following: ; ; 1) Allocate an error log buffer entry; to do this it adds EMB$K_LENGTH ; bytes to the size in DDT$W_ERRORBUF and allocates that much space ; in the current error log buffer; the EMB$K_LENGTH bytes are for a ; header used to manage buffer entries. ; ; 2) Store the address of the error log buffer entry in UCB$L_EMB. The ; base address of the buffer is considered to be the address of the ; first byte after the EMB$K_LENGTH byte header - which is why the ; symbols defined in $EMBHDDEF used to reference the header fields ; are negative. ; ; 3) Load standard, device-independent information into the beginning ; of the buffer; this section is fixed at EMB$L_DV_REGSAV bytes. ; ; 4) Call a device-specific register dump routine passing a buffer ; address of UCB$L_EMB+EMB$L_DV_REGSAV; this is where the register ; dump routine can start writing device and error specific data. ; ; This register dump routine writes two sections of data into the buffer. ; ; The first is device specific but error independent; it will be written ; for every type of error logged by this routine, and it's format is ; described in this routine's header. It's size is not quite fixed, as ; the size of the SCSI command can make it vary, but there's really no ; risk of running off the end of the buffer writing it. ; ; The second is error specific, and it's size will vary with the type of ; error being logged; depending on the amount of error specific data ; we're told is available, it is possible for this section to run off the ; end of the buffer, which could corrupt the header of the next error log ; buffer. For that reason, some special steps are take here to make sure ; that it doesn't happen. ; ; * UCB$L_EMB contains the address of a DDT$W_ERRORBUF byte buffer ; ; * On entry to this routine, a total of DDT$W_ERRORBUF-EMB$L_DV_REGSAV ; bytes are available to be written ; ; * R0 always contains the address of the next byte to be written ; ; The following code calculates the amount of space left in the buffer and ; makes sure that no matter how many bytes of error specific data we have ; available, we don't write past the end of the buffer. MOVL UCB$L_DDT(R5),R3 ;? ??; Get the DDT address MOVZWL DDT$W_ERRORBUF(R3),R3 ;? ??; Get the buffer size ADDL2 UCB$L_EMB(R5),R3 ;? ??; Address after buffer SUBL2 R0,R3 ;? ??; Convert to bytes-left CMPL R1,R3 ;? ??; Enough room available? BLEQ 37$ ;? ??; Branch if so MOVL R3,R1 ;? ??; Use all of what's left 37$: MOVB R1,-1(R0) ;? ??; Save additional data length 40$: MOVB (R2)+,(R0)+ ;? ??; Save a byte of extended sense data SOBGTR R1,40$ ;? ??; Repeat for all extended sense data 50$: SUBL (SP),R0 ;? ??; Calculate number of bytes saved DECL R0 ;? ??; Round up to next longword (+3), but ;? ??; don't count LW count field itelf in ;? ??; LW count (-4) ASHL #-2,R0,@(SP)+ ;? ??; Save in LW count field at top of buffer RET ;? ??; Return .PAGE .SBTTL DK_DSE - Data Security Erase FDT Routine ;+ ; DK_DSE ; ; This is the FDT routine for the Data Security Erase operation. ; The byte count (P2) is stored in IRP$L_BCNT. The starting logical ; block (P3) is stored in IRP$L_MEDIA. Control is transfered to ; EXE$QIODRVPKT, thus queueing the I/O request to the driver's start ; I/O routine. ; ; Context: ; ; User thread ; IPL = ASTDEL ; Locks - none ; ; STEP 2 INTERFACE: ; ; int FDT_ROUTINE (IRP *, PCB *, UCB *, CCB *) ; ; IMPLICIT INPUTS: ; P2 - Byte count ; P3 - Starting logical block ; ; IMPLICIT OUTPUTS: ; ; IRP$L_BCNT(R3) - Byte count ; IRP$L_MEDIA(R3) - Starting logical block ; ; Obsolete Step 1 Inputs: ; ; R3 - IRP address ; ;- DK_DSE:: ;U A; Data Security Erase FDT Routine $DRIVER_FDT_ENTRY PRESERVE = MOVL IRP$L_QIO_P2(R3),IRP$L_BCNT(R3) ;U A; Setup erase byte count MOVL IRP$L_QIO_P3(R3),IRP$L_MEDIA(R3);U A; Setup erase starting LBN CALL_QIODRVPKT ;U A; Send request to STARTIO and RET .PAGE .SBTTL DK_DIAGNOSE - FDT preprocessing for Special pass-through function ;+ ; DK_DIAGNOSE ; ; This routine performs FDT preprocessing including: ; ; - Validating access to the descriptor buffer ; - Validating access to, and locking, the read/write buffer ; - Copying the SCSI command to a buffer in non-paged pool ; ; Context: ; ; User thread ; IPL = ASTDEL ; Locks - none ; ; STEP 2 INTERFACE: ; ; int FDT_ROUTINE (IRP *, PCB *, UCB *, CCB *) ; ; IMPLICIT INPUTS: ; ; P1,P2 - QIO function parameters P1 and P2 in the IRP ; ; OBSOLETE STEP 1 INPUTS: ; ; R3 - IRP address ; R4 - PCB address ; R5 - UCB address ; R6 - CCB address ; ; OBSOLETE STEP 1 OUTPUTS: ; ; R0-R2 - Destroyed ;- DK_DIAGNOSE:: ;U A; Special pass-through function $DRIVER_FDT_ENTRY PRESERVE= MOVL UCB$L_ORB(R5),R9 ;U A; Get address of object rights block CLRQ -(SP) ;U A; terminator and retlen for privilege a MOVL ORB$L_NAME_POINTER(R9),-(SP) ;U A; Bufadr for device name MOVZWL ORB$W_NAME_LENGTH(R9),R9 ;U A; save size of device name ADDL3 #,R9,-(SP) ;U A; itmcod and bufsiz of item ent AUDIT_S_ITMLST = 16 ;U A; Size of the itemlist is 16 bytes MOVL SP,R9 ;U A; save pointer to itemlist $IFPRIV DIAGNOSE, - ;U A; Branch if process has DIAGNOSE priv 10$, - ;U A; success branch MSG=DIAGNOSE_7,- ;U A; Audit message ITMLST=R9, - ;U A; Item List PRESERVE=NO ;U A; MOVL #SS$_NOPRIV,R0 ;U A; Set no privilege status ADDL #AUDIT_S_ITMLST,SP ;U A; Restore stack BRW 50$ ;U A; Branch to abort the I/O 10$: PUSHAL UCB$PS_DIAGNOSE(R5) ;U A; UCB autosense info PUSHL R6 ;U A; CCB address PUSHL R5 ;U A; UCB address PUSHL R4 ;U A; PCB address PUSHL R3 ;U A; UCB address CALLS #5,DKMK$DIAGNOSE_FDT ;U A; Call common FDT routine RET 50$: CALL_ABORTIO ;U A; Abort the I/O with stat .PAGE .SBTTL DK_SHAD_WRITE - Check write to shadow mbr for priv ;++ ; ; DK_SHAD_RWCHECK - Check read/write to shadow mbr for privilege ; ; Functional Description: ; ; Allow only processes with SYS privilege to perform WRITES to ; Host Based Shadowing shadow set members. ; ; Context: ; ; User thread ; IPL = ASTDEL ; Locks - none ; ; Step 2 Interface: ; ; int FDT_ROUTINE (IRP *, PCB *, UCB *, CCB *) ; ; Step 1 Inputs: ; ; R3 IRP address ; R5 UCB address (member) ; ; Implicit inputs: None. ; ; Outputs:None. ; ; Implicit outputs: None. ; ; Condition codes: ; ; SS$_ILLIOFUNC - I/O directed to shadow set member by a process ; that doesn't have sys priv. ;-- DK_SHAD_WRITE:: ;U A; Check write to shadow mbr for priv $DRIVER_FDT_ENTRY PRESERVE= BBS #DEV$V_SHD,- ;U A; If this device is a shadow UCB$L_DEVCHAR2(R5),10$ ;U A; set member, check BRB 30$ ;U A; Else, call ACP$WRITEBLK 10$: MOVL IRP$L_ARB(R3),R0 ;U A; Get ARB address BEQL 99$ ;U A; If ARB absent, exit ASSUME PRV$V_SYSPRV LT 32 BBC #PRV$V_SYSPRV,- ;U A; No SYSPRV, illegal ARB$Q_PRIV(R0),99$ ;U A; 30$: PUSHL R6 ;U A: Stack args : CCB PUSHL R5 ;U A: UCB PUSHL R4 ;U A: PCB PUSHL R3 ;U A: IRP CALLS #4, ACP_STD$WRITEBLK ;U A: Call WRITEBLK RET ;U A: Continue FDT processing 99$: MOVZBL #SS$_ILLIOFUNC,R0 ;U A; Set error status CALL_FINISHIOC ;U A; Complete I/O request and RET .PAGE .SBTTL DK_CRESHAD - CRESHAD FDT routine .SBTTL DK_REMSHAD - REMSHAD FDT routine ;++ ; ; DK_CRESHAD - CRESHAD FDT routine ; DK_REMSHAD - REMSHAD FDT routine ; ; Functional Description: ; ; Dispatch CRESHAD and REMSHAD requests to shadowing driver. ; ; Context: ; ; User thread ; IPL = ASTDEL ; Locks - none ; ; Step 2 Interface: ; ; int FDT_ROUTINE (IRP *, PCB *, UCB *, CCB *) ; ; Step 1 Inputs: ; ; R3 IRP address ; R5 UCB address (member) ; ; Implicit inputs: Dispatch vector filled in. ; ; Outputs:None. ; ; Implicit outputs: None. ; ; Condition codes: ; ; SS$_ILLIOFUNC - Dispatch vector not set up. ;-- DK_CRESHAD:: DK_REMSHAD:: ; ; Save all registers in case we RET under JSB from a branch $DRIVER_FDT_ENTRY FETCH=YES - PRESERVE= MOVL G^EXE$GL_HBS_PTR,R0 ;U A; Shadow Dispatcher BGEQ 10$ ;U A; Illegal if not filled in PUSHL R6 ;U A; Push CCB address, PUSHL R5 ;U A; Push UCB address, PUSHL R4 ;U A; Push PCB address, PUSHL R3 ;U A; Push IRP address. CALLS #4,(R0) ;U A; Jump to shadow dispatcher RET ;U A; return to caller. 10$: MOVZBL #SS$_ILLIOFUNC,R0 ;U A; Set error status CALL_FINISHIOC DO_RET=YES ;U A; Complete I/O request .PAGE .SBTTL DK_AUDIO - FDT preprocessing for CD-ROM Audio functions ;+ ; DK_AUDIO ; ; This routine performs FDT preprocessing for SCSI AUDIO commands. ; The application passes the address of a control structure called the Audio ; Control Block (AUCB) in P1 and the size of the AUCB in P2. In this FDT ; routine the AUCB and any other buffers presented by the users will be locked ; down and double mapped into S0 and P0 space. The AUCB is a control structure ; which passes audio command specific parameters as well as describes users ; buffers that may be used by the driver to return control or CD state ; information (TOC) to the application. ; ; In order to accommodate multiple user buffers being used in a single I/O, 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. ; ; Context: ; ; User thread ; IPL = ASTDEL ; Locks - none ; ; Step 2 Interface: ; ; int FDT_ROUTINE (IRP *, PCB *, UCB *, CCB *) ; ; Step 1 Inputs: ; ; R0 - Address of FDT routine ; R3 - IRP address ; R4 - PCB address ; R5 - UCB address ; R6 - CCB address ; ; Obsolete Step 1 Inputs: ; ; R7 - Bit number of user-specified I/O function code ; R8 - Address of current entry in FDT ; ; Outputs: ; ;- .ENABLE LSB DK_AUDIO:: ;U A; CD-ROM Audio functions ; ; Save all registers in case we RET under JSB from a branch $DRIVER_FDT_ENTRY PRESERVE= ;+ ; The AUCB is the Audio Control Block which is constructed by the application and ; passed to the driver during the QIO call. The appication passes arguments ; to the QIO. The first parameter is the address of the AUCB and the second ; parameter is the size of AUCB. ; ; Now check to see that we have read access to the AUCB before attempting to ; access this structure and then verify the version number. The version is ; checked to be sure that it matchs the current code. In the future it may ; become necessary to allow backward compatibility with earlier versions ; of the audio interface within this driver. ;- MOVL IRP$L_QIO_P1(R3),- ;U A; Save user descriptor address UCB$PS_AUCB_ADDR(R5) 10$: BISL #IRP$M_PHYSIO,IRP$L_STS(R3) ;U A; All audio functions are physical CLRL IRP$L_WIND(R3) ;U A; Clear this since we use if in startio CLRL IRP$L_EXTEND(R3) ;U A; Clear for starters.. MOVL IRP$L_QIO_P1(R3),R0 ;U A; Get user descriptor address MOVL IRP$L_QIO_P2(R3),R1 ;U A; Get user descriptor length BSBW AUDIO_MAP_PAGE ;U A; Locks and Maps AUCB. BLBC R0,AUDIO_EXIT_FDT ;U A; Exit FDT routine on error R0 = error CMPL #CD_AUCB_SIZE,IRP$L_QIO_P2(R3) ;U A; There must be an AUCB BGTR 40$ ;U A; The AUCB must be at least this big. ;+ ; Now that we have locked down and prepared the AUCB for startio, determine ; whether or not there is a optional sense buffer. If there is a buffer then ; allocate an IRP extension to save the state of this buffer. If there is ; no sense buffer required, simply continue to process this request. ;- MOVL UCB$PS_AUCB_ADDR(R5),R0 ;U A; Get user descriptor address MOVL CD_SENSE_ADDR(R0),R0 ;U A; Get Address of Sense buffer BEQL 30$ ;U A; No sense buffer continue BSBW SETUP_SENSE_BUFFER ;U A; There's a sense buffer set it up. BLBC R0,AUDIO_EXIT_FDT ;U A; Exit FDT routine on error R0 = error ;+ ; All audio I/O must go through the processing above, however some audio ; functions require additional processing before these requests can be queued ; to the startio routine. ; ; Now that we have double mapped the AUCB and Sense Buffer determine whether there are ; any additional buffers that need to be mapped. ;- 30$: MOVL UCB$PS_AUCB_ADDR(R5),R9 ;U A; Get user descriptor address DISPATCH CD_FUNCTION_CODE(R9),TYPE=B,<- ;U A; Dispatch based on function ,- ;U A; Read Subchannel-Q > ;U A; Read TOC ;+ ; For all other requests, simply issue this IRP to the startio routine. ;- CALL_QIODRVPKT ;U A; QUEUE DRIVER PACKET ;+ ; The following two AUDIO operations return data to an optional user buffer. ;- FDT_READ_TOC: FDT_READ_SUB: ;+ ; Check the source/destination buffer for access and protection. ;- TSTL CD_DEST_BUF_ADDR(R9) ;U A; Test address of the dest buffer BEQL 40$ ;U A; TSTL CD_DEST_BUF_CNT(R9) ;U A; Test size of destination buffer. BEQL 40$ ;U A; If there is a buffer, must <> 0 ;+ ; Since there is another buffer that needs to be processed, if an IRPE ; hasn't already been allocated, allocate it now. ;- TSTL IRP$L_EXTEND(R3) ;U A; Test for IRP Extension BEQL NO_IRP ;U A; No, IRPE already. MOVL IRP$L_EXTEND(R3),R3 ;U A; Get IRPE address BRB GOT_IRPE ;U A; Yes, already have an IRPE. NO_IRP: BSBW ALLOC_IRPE ;U A; Allocate an IRP extension BLBC R0,AUDIO_EXIT_FDT ;U A; Exit if no IRPE GOT_IRPE: MOVL CD_DEST_BUF_ADDR(R9),R0 ;U A; Get address of dest buffer MOVL CD_DEST_BUF_CNT(R9),R1 ;U A; Get size of dest buffer MOVL R1,IRP$L_BCNT(R3) ;U A; Copy Byte count to IRPE MOVL G^MMG$GL_BWP_MASK,R2 ;U A; Byte-within-page mask EVAX_AND R0,R2,R2 ;U A; Get new byte offset within page MOVL R2,IRP$L_BOFF(R3) ;U A; Update BOFF BSBW AUDIO_MAP_PAGE ;U A; Locks and Maps Destination Buffer. BLBC R0,AUDIO_EXIT_FDT ;U A; Exit FDT routine on error R0 = error MOVL IRP$L_SEQNUM(R3),R3 ;U A; Restore Original IRP Address CALL_QIODRVPKT ;U A; Queue the packet to the driver and RET ;+ ; Error Paths for DK_AUDIO FDT routine. ;- 40$: PUSHL R2 MOVL R3,R2 ;U A; Get IRP Address in R2 BSBW AUDIO_EXIT_FREE ;U A; Free Allocated resources POPL R2 MOVZBL #SS$_BADPARAM,R0 ;U A; Set bad parameter status 50$: CALL_ABORTIO ;U A; Abort the I/O with status in R0 and RET ;+ ; Error exit path from audio fdt routines. ;- AUDIO_EXIT_FDT:: .GLOBAL_LABEL PUSHL R2 MOVL R3,R2 ;U A; Get IRP Address in R2 BSBW AUDIO_EXIT_FREE ;U A; Free Allocated resources MOVL UCB$PS_AUCB_ADDR(R5),R2 ;U A; Get AUCB address MOVZWL R0,CD_COMMAND_STATUS(R2) ;U A; Set bad status is AUCB. POPL R2 BRB 50$ .DISABLE LSB ;U A; DK_AUDIO .PAGE .SBTTL + .SBTTL + QIO INTERFACE ROUTINES .SBTTL + .SBTTL IO_PACKACK - Pack acknowledge function ;+ ; IO_PACKACK ; ; This routine executes a PACKACK request by sending and inquiry, ensuring the ; device is spun up, reading its geometry information, and, if necessary, doing ; device specific setup such as setting the default block size and error ; recovery parameters. ; ; Context: ; ; SCDRP Thread ; IPL = Fork ; Fork lock held ; ; Inputs: ; ; R3 - UCB address ; R4 - PDT address ; R5 - SCDRP address ; ; Outputs: ; ; R0 - Status ; R1,R2 - Destroyed ; All other registers preserved ;- IO_PACKACK:: .GLOBAL_LABEL ;S FF; Pack acknowledge function DK_DEBUG IO_PACKACK SET_SINGLE PACKACK,R3,40$,R5,30$ ;S FF; Single threaded PACKACK request. ; Clear write protect status, since the floppy diskette can be changed. BICL #UCB$M_HWL,UCB$L_DK_FLAGS(R3) ;S FF; Assume medium is writeable ;S FF; until proven otherwise BICL #DEV$M_SWL,UCB$L_DEVCHAR(R3) ;S FF; Assume not writelocked ;S FF; until proven otherwise BSBW WAIT_UNIT_READY ;S FF; Wait for the device to spin up BLBC R0,30$ ;S FF; Branch on error BSBW INQUIRY ;S FF; Execute an INQUIRY command BLBC R0,30$ ;S FF; Branch on error BSBW READ_CAPACITY ;S FF; Execute READ CAPACITY command BLBC R0,30$ ;S FF; Branch on error BSBW PROCESS_MODE_INFO ;S FF; Do MODE SENSE/SELECT processing BLBS R0,16$ ;S FF; Branch on success BBCC #UCB$V_TENBYTE,- ;S FF; Branch if we can't back off and UCB$L_DK_FLAGS(R3),30$ ;S FF; try a must-be-supported command BSBW PROCESS_MODE_INFO ;S FF; Do MODE SENSE/SELECT processing BLBC R0,30$ ;S FF; Branch on error 16$: BSBW VALIDATE_GEOMETRY BLBC R0,30$ ;S FF; Branch on error BSBW SET_CONN_CHAR ;S FF; Set up the connection characteristics BLBC R0,30$ ;S FF; Branch on error ; If write-enabled and multihost => the drive must support TCQ and AWRE/ARRE. ; We only test for CMDQ since that implies TCQ and AWRE/ARRE. BBS #DEV$V_SWL,UCB$L_DEVCHAR(R3),19$ ;S FF; skip if SW writelocked BBS #UCB$V_HWL,UCB$L_DK_FLAGS(R3),19$ ;S FF; skip if HW writelocked BBC #SPDT$V_STS_MULTIHOST,- ;S FF; skip if not multi-host SPDT$L_STS(R4),19$ ;S FF; IF_CMDQ 19$ ;S FF; continue if CMDQ and AWRE/ARRE MOVL #SS$_DRVERR, R0 ;S FF; set error status BRW 30$ 19$: BISL #UCB$M_VALID,UCB$L_STS(R3) ;S FF; Set volume valid BISB #UCB$M_FIRST_ATTN_SEEN,- ;S FF; Indicate that the first unit attention UCB$L_DK_FLAGS(R3) ;S FF; has been seen (any future ones will ;S FF; be logged as errors) BBSS #UCB$V_HBS_CHECK,- ;S FF; Branch if check for host-based UCB$L_DK_FLAGS(R3),20$ ;S FF; shadowing has already been made BSBW CHECK_HBS ;S FF; Check for host-based shadowing support BLBC R0,30$ ;S FF; Failure path 20$: CLR_SINGLE PACKACK,R3,40$ ;S FF; PACKACK is complete. IF_NOT_CMDQ 25$ ;S FF; Skip enabling CMDQ processing if not supported BICL #UCB$M_CB_NOCMDQ,- ;S FF; CMDQ support in both the port driver UCB$L_CLASS_BUSY(R3) ;S FF; and device. Clear no CMDQ bit 25$: BRW COMPLETE_IO ;S FF; Complete this I/O function 30$: MOVL SCDRP$L_IRP(R5),R2 ;S FF; Get IRP address TSTL IRP$L_PID(R2) ;S FF; Check for interanl IRP BLSS 20$ ;S FF; Skip local valid cleanup if internal BBCC #UCB$V_LCL_VALID,- ;S FF; Clear local valid bit and UCB$L_STS(R3), 20$ ;S FF; branch if its already clear. DECB UCB$B_ONLCNT(R3) ;S FF; Else, decrement the online count. BRB 20$ 40$: BUG_CHECK INCONSTATE,FATAL ;S FF; bugcheck .PAGE .SBTTL IO_WRITEPBLK - Write a set of blocks to the SCSI drive .SBTTL IO_READPBLK - Read a set of blocks from the SCSI drive ;+ ; IO_READPBLK ; IO_WRITEBLK ; ; This routine reads/writes a set of contiguous blocks from/to the SCSI ; disk. For those transfers that have not already been segmented by the ; exec to chunks LEQ MAXBCNT, this routine performs the segmentation. ; ; Recoverable and non-recoverable errors are dealt with by this routine. ; In summary, the algorithm is to retry the original operation several times. ; If the error persists, and is anything but a non-recoverable read, then ; a block reassignement is performed. The original data is then written to ; the reasigned block as reassignment is not guaranteed to move the data. ; Retries are performed if necessary at each stage. ; ; Because there is no forced error bit in SCSI, blocks with non-recoverable ; errors on reads can not be reassigned. (Otherwise, undetected corruption ; would result. It's preferable to return SS$_PARITY status each time the ; failing block is read.). ; ; If the DATACHECK qualifier is specified on the QIO request, the IO_DATACHECK ; routine is invoked to read the set of blocks just read/written. No block ; reassignment is performed by IO_DATACHECK. ; ; Context: ; ; SCDRP thread ; IPL = Fork ; Fork lock held ; ; Inputs: ; ; R2 - IPR address ; R3 - UCB address ; R4 - PDT address ; R5 - SCDRP address ; ; Outputs: ; ; R0 - Status ; R1,R2 - Destroyed ; All other registers preserved ;- WRITE_LOCKED: ;S FF; MOVL #SS$_WRITLCK,R0 ;S FF; Set write-locked status BRW COMPLETE_IO ;S FF; Complete QIO with error status INVALID_BLOCK_NUMBER: MOVL #SS$_IVADDR,R0 ;S FF; Otherwise, set invalid address status BRW COMPLETE_IO ;S FF; Complete QIO with error status IO_WRITEPBLK:: .GLOBAL_LABEL ;S FF; Write a set of blocks to the SCSI drive INCL UCB$L_WRITE_COUNT(R3) ;S FF; Count writes ; ; Figure out which XLEN bucket this transfer fits in ; ADDL3 #,- ;S FF; Round up byte count to block multiple IRP$L_BCNT(R2),R0 ;S FF; of disk block size ASHL #-IOC$S_DISK_BLKSIZ,R0,R0 ;S FF; and convert to block count ASHL #-XLEN_HIST_BUCKET_SHIFT,R0,R0 ;S FF; Convert to bucket index CMPL #XLEN_HIST_BUCKETS,R0 ;S FF; Boundary checks... BGTR 1000$ ;S FF; Looks OK MOVL #XLEN_HIST_BUCKETS-1,R0 ;S FF; Fix index - >64K in same 64K bucket 1000$: INCL @UCB$L_WRITE_XLEN_HIST(R3)[R0] ;S FF; Bump count INCL @UCB$L_XLEN_HIST(R3)[R0] ;S FF; Bump total count BBS #UCB$V_HWL,UCB$L_DK_FLAGS(R3),- ;S FF; Branch if device is write-locked, WRITE_LOCKED ;S FF; not possible to write device BICL #IRP$M_FUNC,- ;S FF; Clear the FUNC but to indicate this SCDRP$IS_STS(R5) ;S FF; is a write function BBC #IO$V_MSCPMODIFS,- ;S FF; Branch if this is NOT a function to IRP$L_FUNC(R2),- ;S FF; force a bad block IO_RW_COMMON ;S FF; Branch to common code BBC #MSCP$V_MD_ERROR,- ;S FF; Do we want to force error? IRP$L_MEDIA+6(R2),- ;S FF; No, continue common path IO_RW_COMMON BRW FORCE_ERROR ;S FF; Otherwise, go force an error IO_READPBLK:: .GLOBAL_LABEL ;S FF; Read a set of blocks from the SCSI drive DK_DEBUG IO_READPBLK INCL UCB$L_READ_COUNT(R3) ;S FF; Count reads ; ; Figure out which XLEN bucket this transfer fits in ; ADDL3 #,- ;S FF; Round up byte count to block multiple IRP$L_BCNT(R2),R0 ;S FF; of disk block size ASHL #-IOC$S_DISK_BLKSIZ,R0,R0 ;S FF; and convert to block count ASHL #-XLEN_HIST_BUCKET_SHIFT,R0,R0 ;S FF; Convert to bucket index CMPL #XLEN_HIST_BUCKETS,R0 ;S FF; Boundary checks... BGTR 1500$ ;S FF; Looks OK MOVL #XLEN_HIST_BUCKETS-1,R0 ;S FF; Fix index - >64K in same 64K bucket 1500$: INCL @UCB$L_READ_XLEN_HIST(R3)[R0] ;S FF; Bump count INCL @UCB$L_XLEN_HIST(R3)[R0] ;S FF; Bump total count BISL #IRP$M_FUNC,- ;S FF; Set the FUNC but to indicate this SCDRP$IS_STS(R5) ;S FF; is a read function IO_RW_COMMON: ; ; See if we need to recalculate the qdepth ; DECL UCB$L_XLEN_HIST_CYCLE(R3) ;S FF; count down I/O's till recheck BNEQ 100$ ;S FF; If neq, not counted down yet BSBW CHECK_QDEPTH ;S FF; See if we need to set new depth ;S FF; based on xlen histogram info MOVL #XLEN_HIST_TURNOVER,- ;S FF; Reset I/O counter for qdepth check UCB$L_XLEN_HIST_CYCLE(R3) 100$: BBC #UCB$V_VALID,- ;S FF; Branch if volume is invalid. UCB$L_STS(R3),- ;S FF; We need to have done a PACKACK VOLUME_INVALID ;S FF; before we can do a read or write ADDL3 #,- ;S FF; Round up byte count IRP$L_BCNT(R2),R0 ;S FF; to block multiple ASHL #-IOC$S_DISK_BLKSIZ,R0,R0 ;S FF; and convert to block count ADDL IRP$L_MEDIA(R2),R0 ;S FF; Calculate highest block # accessed CMPL R0,UCB$L_MAXBLOCK(R3) ;S FF; Valid block number? BGTR INVALID_BLOCK_NUMBER ;S FF; Branch if not BBC #UCB$V_CB_NOCMDQ,- ;SF FF; If device supports CMDQ, then no need UCB$L_CLASS_BUSY(R3), 10$ ;SF FF; to set_single SET_SINGLE SINGLE_RW,R3 ;S FF; Single thread R/W operations 10$: DECL UCB$L_OTHER_COUNT(R3) ;S FF; Fix count since this is a R/W CLRL SCDRP$L_ABCNT(R5) ;S FF; Initialize accumulated byte count MOVL IRP$L_FUNC(R2),- ;S FF; Copy function code and modifiers, SCDRP$L_FUNC(R5) ;S FF; from the IRP to the SCDRP MOVL IRP$L_MEDIA(R2),- ;S FF; Copy IRP$L_MEDIA (LBN #) SCDRP$L_MEDIA(R5) ;S FF; from the IRP to the SCDRP MOVL IRP$L_SVAPTE(R2),- ;S FF; Copy SVA of buffer to SCDRP$L_SVAPTE(R5) ;S FF; from the IRP to the SCDRP MOVL IRP$L_BOFF(R2),- ;S FF; Copy buffer length SCDRP$L_BOFF(R5) ;S FF; from the IRP to the SCDRP BBC #IO$V_MSCPMODIFS,- ;S FF; Branch if no MSCP modifiers IRP$L_FUNC(R2),- 110$ BBC #MSCP$V_MD_EXPRS,- ;S FF; Is this an express request? IRP$L_MEDIA+6(R2),110$ ;S FF; MOVL #SCDRP$K_QCHAR_HEAD,- ;S FF; Yes, send to head of queue SCDRP$IS_QUEUE_CHAR(R5) BRW IO_RW_LOOP 110$: MOVL #SCDRP$K_QCHAR_UNORDERED,- ;S FF; Send a Simple tagged request SCDRP$IS_QUEUE_CHAR(R5) IO_RW_LOOP: SUBL3 SCDRP$L_ABCNT(R5),- ;S FF; Attempt to transfer all remaining IRP$L_BCNT(R2),- ;S FF; bytes in user's buffer SCDRP$L_BCNT(R5) ;S FF; CMPL SCDRP$L_BCNT(R5),- ;S FF; Transfer length greater than maximum UCB$L_MAXBCNT(R3) ;S FF; supported? BLEQU 10$ ;S FF; Branch if not MOVL UCB$L_MAXBCNT(R3),- ;S FF; Otherwise, transfer must be segmented SCDRP$L_BCNT(R5) ;S FF; into pieces of MAXBCNT length 10$: BSBW READ_WRITE ;S FF; Send a SCSI read or write command ;S FF; SCDRP$IS_QUEUE_CHAR was set earlier MOVL SCDRP$L_IRP(R5),R2 ;S FF; Restore IRP address BLBC R0,IO_RW_ERR ;S FF; Branch on error IO_RW_ACCUM: SUBL3 SCDRP$L_PAD_BCNT(R5),- ;S FF; Get actual number of bytes transferred SCDRP$L_TRANS_CNT(R5),R0 ;S FF; less any possible padding CMPL R0,SCDRP$L_BCNT(R5) ;S FF; Compare with requested transfer count BNEQ IO_RW_MISMATCH ;S FF; Branch if mismatch occurred ADDL R0,SCDRP$L_ABCNT(R5) ;S FF; Accumulate this piece of the transfer CMPL SCDRP$L_ABCNT(R5),IRP$L_BCNT(R2);S FF; Is transfer complete? BLSSU IO_RW_SEGMENT_DONE ;S FF; Branch if not, accumulate this segment ;S FF; of the transfer BBS #IO$V_DATACHECK,- ;S FF; Branch if the datacheck modifier SCDRP$L_FUNC(R5),- ;S FF; has been specified. Perform a IO_DATACHECK ;S FF; datacheck operation. MOVW #SS$_NORMAL,R0 ;S FF; Set success status IO_RW_EXIT: BBC #UCB$V_CB_NOCMDQ,- ;SF FF; If device supports CMDQ, then no need UCB$L_CLASS_BUSY(R3), 10$ ;SF FF; to clr_single CLR_SINGLE SINGLE_RW,R3 ;S FF; Clear thread R/W operations 10$: INSV SCDRP$L_ABCNT(R5),- ;S FF; Load low-order number of bytes #16,#16,R0 ;S FF; transferred into R0 MOVZWL SCDRP$L_ABCNT+2(R5),R1 ;S FF; Load high-order number of bytes ;S FF; transferred into R1 BRW COMPLETE_IO ;S FF; Complete the QIO ; Here we have completed one piece of a segmented transfer. Update the MEDIA, ; SVAPTE and BOFF fields in the SCDRP and go perform the next segment of ; the transfer. IO_RW_SEGMENT_DONE: ASHL #-IOC$S_DISK_BLKSIZ,R0,R1 ;S FF; Convert byte count to block count ADDL2 R1,SCDRP$L_MEDIA(R5) ;S FF; to logical block number in SCDRP MOVL SCDRP$L_BOFF(R5),R1 ;S FF; byte offset into register ADDL2 R1,R0 ;S FF; offset plus byte count MOVL G^MMG$GL_VPN_TO_VA,R1 ;S FF; right shift factor EVAX_SRL R0,R1,R1 ;S FF; calulate number of VPN's to advance EVAX_SLL R1,#PTE$C_SHIFT_SIZE,R1 ;S FF; number of PTE's to advance ADDL2 R1,SCDRP$L_SVAPTE(R5) ;S FF; Update SVAPTE field in SCDRP MOVL G^MMG$GL_BWP_MASK,R1 ;S FF; Byte within page mask of virtual addr EVAX_AND R0,R1,R1 ;S FF; Get new byte offset within page MOVL R1,SCDRP$L_BOFF(R5) ;S FF; Save byte offset in SCDRP BRW IO_RW_LOOP ;S FF; Go perform next segment of transfer ; Here the transfer count returned by the port doesn't match the requested ; byte count. If it's greater, than truncate the transfer to what we ; requested. Otherwise, accumulate the piece of the transfer that just ; completed and continue with the transfer. IO_RW_MISMATCH: BLSS 10$ ;S FF; Branch if the transfer count is less ;S FF; than the requested count, accumulate ;S FF; this piece of the transfer ADDL3 SCDRP$L_PAD_BCNT(R5),- ;S FF; Truncate the transfer such that the SCDRP$L_BCNT(R5),- ;S FF; the transfer count we got is what we SCDRP$L_TRANS_CNT(R5) ;S FF; expected BRB IO_RW_ACCUM ;S FF; Accumulate this piece of the transfer 10$: BICW #,R0 ;S FF; Round transfer down to block multiple BEQL IO_RW_SHORT_XFER ;S FF; Branch if no bytes to accumulate MOVL R0,SCDRP$L_BCNT(R5) ;S FF; Adjust byte count and transfer count ADDL3 SCDRP$L_PAD_BCNT(R5),- ;S FF; to accumulate this segment of the R0,SCDRP$L_TRANS_CNT(R5) ;S FF; transfer. BRB IO_RW_ACCUM ;S FF; Accumulate this segment of the transfer IO_RW_SHORT_XFER: MOVL #SS$_OPINCOMPL,R0 ;S FF; Set bad status BRB IO_RW_EXIT ;S FF; Complete I/O with error status ; Here an error occurred in performing the read or write. If it's due to a ; recoverable or non-recoverable (MEDIA) error, then accumulate the piece of ; the transfer that completed successfully and retry the original operation on ; failing block several times. ; ; For read-only devices, since we can't perform a reassign, retry the read ; only for non-recoverable errors. ; ; For devices (such as floppies) that don't support reassign, retry the read ; only for non-recoverable errors. IO_RW_ERR: CMPL R0,#SS$_PARITY ;S FF; Media (non-recoverable) error? BEQL 10$ ;S FF; Branch if so CMPL R0,#SS$_RECOVERR ;S FF; Recoverable error? BNEQ IO_RW_EXIT ;S FF; Branch if not, return error to caller BBS #DEV$V_SWL,UCB$L_DEVCHAR(R3),5$ ;S FF; Branch if device is SWL BBC #UCB$V_NOREASSIGN,- ;S FF; Branch if device supports UCB$L_DK_FLAGS(R3),10$ ;S FF; reassign_block command 5$: BRW IO_RW_ACCUM ;S FF; Recoverable error can't be reassigned, ;S FF; treat as success ; The ADDNL_INFO field in the SCDRP contains a copy of the additional ; information field from the extended sense data. Make sure that it is valid ; and that the failing LBN it contains falls within the range of the original ; read or write command. If not, then assume the failing block is the first ; one in the range. If the failing LBN is valid, then accumulate all blocks ; of the transfer up to the failing LBN and update the MEDIA, ABCNT, BOFF, and ; SVAPTE fields in the SCDRP. 10$: MOVL SCDRP$L_ADDNL_INFO(R5),R0 ;S FF; Get the failing LBN BLSS IO_RW_RETRY ;S FF; Branch if LBN is invalid CMPL R0,SCDRP$L_MEDIA(R5) ;S FF; Is failing LBN below range? BLSS IO_RW_RETRY ;S FF; Branch if so ASHL #-IOC$S_DISK_BLKSIZ,- ;S FF; Get number of blocks requested SCDRP$L_BCNT(R5),R1 ADDL SCDRP$L_MEDIA(R5),R1 ;S FF; Get highest LBN requested CMPL R0,R1 ;S FF; Is failing LBN above range? BGEQ IO_RW_RETRY ;S FF; Branch if so SUBL3 SCDRP$L_MEDIA(R5),R0,R1 ;S FF; Calculate number of successfully ;S FF; transferred blocks BLEQ IO_RW_RETRY ;S FF; Branch if none ASHL #-IOC$S_DISK_BLKSIZ,- ;S FF; Convert to block count SCDRP$L_TRANS_CNT(R5),R0 CMPL R0,R1 ;S FF; Were all blocks up to bad LBN transferred? BLSS 20$ ;S FF; Branch if not MOVL R1,R0 ;S FF; Accumulate all blocks up to the bad one 20$: ADDL R0,SCDRP$L_MEDIA(R5) ;S FF; Update LBN ASHL #IOC$S_DISK_BLKSIZ,R0,R1 ;S FF; Get number of bytes transferred, ADDL R1,SCDRP$L_ABCNT(R5) ;S FF; Update accumulated byte count ADDL SCDRP$L_BOFF(R5),R1 ;S FF; Get byte offset plus byte count MOVL G^MMG$GL_BWP_MASK,R0 ;S FF; Byte-within-page mask EVAX_AND R1,R0,R0 ;S FF; Get new byte offset within page MOVL R0,SCDRP$L_BOFF(R5) ;S FF; Update BOFF ASHL G^MMG$GL_VA_TO_VPN,R1,R0 ;S FF; Convert bytes to pages ASHL #PTE$C_SHIFT_SIZE,R0,R0 ;S FF; Convert page count to PTE offset ADDL R0,SCDRP$L_SVAPTE(R5) ;S FF; Update SVAPTE ; At this point all blocks of the transfer up to failing LBN has been ; accumulated. Retry the transfer of the failing LBN several times to see if ; the block can be read or written successfully. If so, then accumulate the ; data for this block and proceed with the transfer. Otherwise, attempt to ; reassign the block if appropriate. .ENABLE LOCAL_BLOCK IO_RW_RETRY: SET_SINGLE BBR_IN_PROG,R3,BBR_WAIT ;S FF; Single thread bad block replacement. MOVB #RW_RETRY_CNT,UCB$B_RW_RETRY(R3);S FF; Initialize read/write retry count SUBL3 SCDRP$L_ABCNT(R5),- ;S FF; Get length of remaining transfer IRP$L_BCNT(R2),SCDRP$L_BCNT(R5) ;S FF; CMPL SCDRP$L_BCNT(R5),- ;S FF; More than one block? #IOC$C_DISK_BLKSIZ ;S FF; BLEQ 10$ ;S FF; Branch if not MOVL #IOC$C_DISK_BLKSIZ,- ;S FF; Limit transfer to one block SCDRP$L_BCNT(R5) ;S FF; 10$: DISABLE_ERRLOG ;S FF; Temporarily disable errorlogging BSBW READ_WRITE ;S FF; Perform the read or write ;S FF; SCDRP$IS_QUEUE_CHAR was set earlier REENABLE_ERRLOG ;S FF; Reenable errorlogging MOVL SCDRP$L_IRP(R5),R2 ;S FF; Get IRP address BLBS R0,IO_RW_ACCUM_BBR ;S FF; Branch if success, pick up original ;S FF; transfer where we left off CMPL R0,#SS$_PARITY ;S FF; Non-recoverable error? BEQL 20$ ;S FF; Branch if so CMPL R0,#SS$_RECOVERR ;S FF; Recoverable error? BNEQ IO_RW_EXIT_BBR ;S FF; Branch if not, complete QIO with error ; The following code segment catches the case in which a read to a write-locked ; disk fails initially with a non-recoverable error, but returns a recoverable ; error on a read retry. In this case, we accept the data rather than risk ; loosing it with another read retry. The same logic holds true for disks that ; don't support reassign. BBS #DEV$V_SWL,UCB$L_DEVCHAR(R3),15$;S FF; Branch if device is SWL BBC #UCB$V_NOREASSIGN,- ;S FF; Branch if device supports UCB$L_DK_FLAGS(R3),20$ ;S FF; reassign_block command 15$: IO_RW_ACCUM_BBR: CLR_SINGLE BBR_IN_PROG,R3 ;S FF; Bad Block Replacement is complete BRW IO_RW_ACCUM ;S FF; Recoverable error can't be reassigned, ;S FF; accumulate data BBR_WAIT:: .GLOBAL_LABEL ; ; R2 IRP address ; R3 UCB address ; R4 SPDT address ; R5 SCDRP address ; KP_STALL_FORK_WAIT KPB=SCDRP$PS_KPB(R5),- FKB=R5 ;S FF; Fork wait using SCDRP fork block ; ; R2 IRP address ; R3 UCB address ; R4 SPDT address ; R5 SCDRP address ; BRB IO_RW_RETRY ;S FF; 20$: DECB UCB$B_RW_RETRY(R3) ;S FF; Decrement read/write retry count BGTR 10$ ;S FF; Branch if count not exhausted .DISABLE LOCAL_BLOCK ; Here we've been unsuccessful several times in performing the read or ; write to the failing LBN. If it's appropriate to reassign the block, do so. ; Note that since there is no forced error flag in SCSI, we can't reassign a ; block with a non-recoverable read error. IO_RW_REASSIGN: ; If we're here with a disk that doesn't support reassign, then all the ; retries above must have faild due to non-recoverable errors. Therfore, ; complete the QIO now with bad status. BBS #UCB$V_NOREASSIGN,- ;S FF; Branch if device doesn't support UCB$L_DK_FLAGS(R3),- ;S FF; reassign_block command IO_RW_EXIT_BBR ;S FF; BBC #IRP$V_FUNC,- ;S FF; Branch if this is a write, OK to SCDRP$IS_STS(R5),10$ ;S FF; try reassign CMPL R0,#SS$_PARITY ;S FF; Non-recoverable error? BEQL IO_RW_EXIT_BBR ;S FF; Branch if so, NOT OK try reassign. 10$: MOVB #REASSIGN_RETRY_CNT,- ;S FF; Initialize the reassign retry count UCB$B_REASSIGN_RETRY(R3); IO_RW_REASSIGN_LOOP: 20$: MOVL SCDRP$L_MEDIA(R5),R1 ;S FF; Save LBN of block to reassign IF_CANCEL IO_RW_EXIT_BBR ;S FF; Check for cancel request before ;S FF; allocating private SCDRP DK_ALLOC_SCDRP ;S FF; Allocate a new SCDRP for the reassign MOVL R1,SCDRP$L_MEDIA(R5) ;Są FF; Copy LBN to reassign SCDRP BSBW REASSIGN_BLOCK ;Są FF; Go attempt to reassign the block DK_DEALLOC_SCDRP CLRSTK=YES ;Są FF; Deallocate the reassign SCDRP BLBS R0,IO_RW_REWRITE ;S FF; Branch if the reassign succeeded CMPL R0,#SS$_CANCEL ;S FF; Was cancel sts returned BEQL IO_RW_EXIT_BBR ;S FF; If so, exit DECB UCB$B_REASSIGN_RETRY(R3) ;S FF; Decrement the reassign retry count BGTR 20$ ;S FF; Branch if count not exhausted ; Here we've attempted a reassign and failed. Write the original data to ; whereever the block is currently assigned as the failed reassign operations ; may have corrupted the data. ; BRB IO_RW_REWRITE ;S FF; Rewrite the block ;S FF; Fall-Thru ; Here the reassign has completed successfully. Write the original data ; to the reassigned block. The assumption is that the reassign command ; doesn't necessarily move the data from the old to the new location. ; If the original command was a read and the last attempt to read the ; block returned a short transfer count, then don't rewrite the block ; because the data in the user's buffer is invalid. Instead, rely on the ; reassign command to move the data. IO_RW_REWRITE: MOVB #REWRITE_RETRY_CNT,- ;S FF; Initialize rewrite retry count UCB$B_REWRITE_RETRY(R3) ;S FF; BBC #IRP$V_FUNC,- ;S FF; Branch if this is a write, OK to SCDRP$IS_STS(R5),10$ ;S FF; rewrite block CMPL SCDRP$L_TRANS_CNT(R5),- ;S FF; Valid transfer count? #IOC$C_DISK_BLKSIZ ;S FF; BLSS IO_RW_SHORT_XFER ;S FF; Branch if not, can't rewrite block 10$: BICL #IRP$M_FUNC,SCDRP$IS_STS(R5) ;S FF; Clear FUNC indicate write function DISABLE_ERRLOG ;S FF; Temporarily disable errorlogging BSBW READ_WRITE ;S FF; Rewrite the original block ;S FF; SCDRP$IS_QUEUE_CHAR was set earlier REENABLE_ERRLOG ;S FF; Reenable errorlogging MOVL SCDRP$L_IRP(R5),R2 ;S FF; Restore the IRP address MOVL IRP$L_STS(R2),- ;S FF; Restore the setting of the func SCDRP$IS_STS(R5) ;S FF; bit from the IRP BLBS R0,IO_RW_ACCUM_BBR ;S FF; Branch if success, pick up original ;S FF; transfer where we left off CMPL R0,#SS$_CANCEL ;S FF; Was cancel sts returned BEQL IO_RW_EXIT_BBR ;S FF; If so, exit DECB UCB$B_REWRITE_RETRY(R3) ;S FF; Decrement rewrite retry count BGTR 10$ ;S FF; Branch if count not exhausted ; Here we've failed to write the data to the reassigned block. Attempt to ; reassign the block again. If the reassign retry count is exhausted. Then ; we've done all we can so return the status of the last failing write. DECB UCB$B_REASSIGN_RETRY(R3) ;S FF; Decrement reassign retry count BGTR IO_RW_REASSIGN_LOOP ;S FF; Branch if count not exhausted CMPL R0,#SS$_RECOVERR ;S FF; Was last write recoverable? BEQL IO_RW_ACCUM ;S FF; Branch if so, treat as successful and ;S FF; proceed with transfer IO_RW_EXIT_BBR: CLR_SINGLE BBR_IN_PROG,R3 ;S FF; Bad Block Replacement is complete BRW IO_RW_EXIT ;S FF; Otherwise, complete QIO with error status ; Workaround a problem in RZ55's with microde versions less than 900. ; RZ55 with microcode version less than 900 will return one additional ; block of data when the drive successfully recovers a block by ; simply rereading it. This block contains ramdom data and the host assumes ; it contains valid data, the users I/O contains erroneous data. ; ; INPUTS: ; R3 - UCB Address ; RZ55_WORKAROUND: ; Check the product revision number (version) of the microcode of the RZ55. If the first ; digit is non-zero or the second digit is greater than the ascii value of '8' then ; this microcode has been fixed. Otherwise the class driver must workaround ; the hardware problem by decrementing TRANS_CNT. .JSB_ENTRY INPUT=,PRESERVE= CMPB #^A'0',UCB$L_HW_REV(R3) ;S FF; If first digit non-zero BNEQ 110$ ;S FF; then no fixup needed. CMPB #^A'9',UCB$L_HW_REV+1(R3) ;S FF; If second digit <= '9' BLEQU 110$ ;S FF; then Fixup needed. PUSHL R5 ;S FF; Save active SCDRP MOVL SCDRP$PS_PREV_SCDRP(R5),R5 ;S FF; Restore original SCDRP address SUBL2 #DTE_EXTRA_BYTES,- ;S FF; Subtract off the extra block, to SCDRP$L_TRANS_CNT(R5) ;S FF; determine exactly which block to ;S FF; resume transfer from. POPL R5 ;S FF; Restore active SCDRP 110$: RSB ;S FF; Read the rest of the I/O request. .PAGE .SBTTL IO_WRITECHECK - Compare user's data with that on disk ;+ ; IO_WRITECHECK ; ; This routine compares the data in the user's buffer to that on the disk. ; No write is actually performed, just a read (to a buffer allocated from ; pool) followed by the compare operation. ; ; Context: ; ; SCDRP thread ; IPL = Fork ; Fork lock held ; ; Inputs: ; ; R2 - IRP address ; R3 - UCB address ; R4 - PDT address ; R5 - SCDRP address ; ; Outputs: ; ; R0 - Status ; R1,R2 - Destroyed ; All other registers preserved ;- IO_WRITECHECK: ;S FF; Compare user's data with that on disk BBC #UCB$V_CB_NOCMDQ,- ;SF FF; If device supports CMDQ, then no need UCB$L_CLASS_BUSY(R3), 10$ ;SF FF; to set_single SET_SINGLE SINGLE_RW,R3 ;S FF; Single thread R/W operations 10$:;;; BRB IO_DATACHECK ;S FF; Fall through to datacheck routine .PAGE .SBTTL IO_DATACHECK - Check data that was just read or written ;+ ; IO_DATACHECK ; ; This routine is invoked if a QIO read or write specifies the DATACHECK ; qualifier. It reads the set of blocks that have just been read or written ; and compares the original and new data. A temporary buffer is allocated ; from non-paged pool to store the data being read, and whose contents is ; compared with that of the user's buffer. ; ; Note that SCDRP$L_SVAPTE and SCDRP$L_BOFF describe the datacheck buffer in ; the AXP version of this driver. However, in the VAX version, SCDRP$L_SVAPTE ; and SCDRP$L_BOFF describe the current segment of the user buffer. As a ; result, the AXP version of this routine leaves those cells unaltered for each ; segment of the user buffer. This also affects the method used by routine ; DATACHECK_CMP to doubly map the current segment of the user buffer. ; ; Context: ; ; Fork thread ; IPL = Fork ; Fork lock held ; ; Inputs: ; ; R3 - UCB address ; R4 - PDT address ; R5 - SCDRP address ; ; Outputs: ; ; R0 - Status ; R1,R2 - Destroyed ; All other registers preserved ;- IO_DATACHECK:: .GLOBAL_LABEL ;S FF; Check data that was just read or written DK_DEBUG IO_DATACHECK SET_SINGLE SINGLE_DC,R3,IO_DC_WAIT ;S FF; Single thread Data Check BISL #IRP$M_FUNC,SCDRP$IS_STS(R5) ;S FF; Set FUNC to indicate read MOVL SCDRP$L_IRP(R5),R2 ;S FF; Get IRP address CLRL SCDRP$L_ABCNT(R5) ;S FF; Initialize accumulated byte count MOVL IRP$L_FUNC(R2),- ;S FF; Copy function code and modifiers, SCDRP$L_FUNC(R5) ;S FF; MEDIA, SVAPTE and BOFF fields from MOVL IRP$L_MEDIA(R2),- ;S FF; the IRP to the SCDRP SCDRP$L_MEDIA(R5) ;S FF; MOVL IRP$L_SVAPTE(R2),- ;S FF; SCDRP$L_SVAPTE(R5) ;S FF; MOVL IRP$L_BOFF(R2),- ;S FF; SCDRP$L_BOFF(R5) ;S FF; MOVL IRP$L_BCNT(R2),R1 ;S FF; Get transfer length CMPL R1,UCB$L_MAXBCNT(R3) ;S FF; Greater than max? BLEQ 10$ ;S FF; Branch if not MOVL UCB$L_MAXBCNT(R3),R1 ;S FF; Use MAXBCNT instead 10$: PUSHL R5 ;S FF; Save SCDRP address MOVL SCDRP$PS_KPB(R5),R5 ;S FF; Get address of KPB BSBW DK_ALLOC_POOL ;S FF; Allocate a datacheck buffer POPL R5 ;S FF; Restore SCDRP address MOVL R2,SCDRP$L_DATACHECK(R5) ;S FF; Save datacheck buffer address MOVL R2,SCDRP$L_SVA_USER(R5) ;S FF; Datacheck buffer is also "user" buffer MOVL G^MMG$GL_BWP_MASK,R1 ;S FF; Mask of BWP portion of virtual addr EVAX_AND R2,R1,R1 ;S FF; Get byte offset in page MOVL R1,SCDRP$L_BOFF(R5) ;S FF; Save byte offset PUSHL R3 ;S FF; Save UCB JSB G^MMG$SVAPTECHK ;S FF; Get SVAPTE of allocated system buffer MOVL R3,SCDRP$L_SVAPTE(R5) ;S FF; Save SVAPTE in SCDRP POPL R3 ;S FF; Restore UCB address MOVL SCDRP$L_IRP(R5),R2 ;S FF; Restore IRP address IO_DC_LOOP: BISB #SCDRP$M_FLAG_S0BUF,- ;S FF; Indicate that this is an S0 "user" SCDRP$L_SCSI_FLAGS(R5) ;S FF; buffer SUBL3 SCDRP$L_ABCNT(R5),- ;S FF; Attempt to transfer all remaining IRP$L_BCNT(R2),- ;S FF; bytes in user's buffer SCDRP$L_BCNT(R5) ;S FF; CMPL SCDRP$L_BCNT(R5),- ;S FF; Transfer length greater than maximum UCB$L_MAXBCNT(R3) ;S FF; supported? BLEQU 10$ ;S FF; Branch if not MOVL UCB$L_MAXBCNT(R3),- ;S FF; Otherwise, transfer must be segmented SCDRP$L_BCNT(R5) ;S FF; into pieces of MAXBCNT length 10$: MOVL #SCDRP$K_QCHAR_UNORDERED,- ;S FF; Send a Simple tagged request SCDRP$IS_QUEUE_CHAR(R5) BSBW READ_WRITE ;S FF; Send a SCSI read or write command BLBS R0,IO_DC_ACCUM ;S FF; Branch on success CMPL R0,#SS$_RECOVERR ;S FF; Recoverable error? BNEQ IO_DC_EXIT ;S FF; Branch if not, return with error status IO_DC_ACCUM: MOVL SCDRP$L_IRP(R5),R2 ;S FF; Restore IRP address SUBL3 SCDRP$L_PAD_BCNT(R5),- ;S FF; Get actual number of bytes transferred SCDRP$L_TRANS_CNT(R5),R0 ;S FF; less any possible padding CMPL R0,SCDRP$L_BCNT(R5) ;S FF; Compare with requested transfer count BNEQ IO_DC_MISMATCH ;S FF; Branch if mismatch occurred BSBW DATACHECK_CMP ;S FF; Compare the original and new data BLBC R0,IO_DC_EXIT ;S FF; Branch if a mismatch occurred SUBL3 SCDRP$L_PAD_BCNT(R5),- ;S FF; Get actual number of bytes transferred SCDRP$L_TRANS_CNT(R5),R0 ;S FF; less any possible padding ADDL R0,SCDRP$L_ABCNT(R5) ;S FF; Accumulate this piece of the transfer CMPL SCDRP$L_ABCNT(R5),IRP$L_BCNT(R2);S FF; Is transfer complete? BLSSU IO_DC_SEGMENT_DONE ;S FF; Branch if not, accumulate this segment ;S FF; of the transfer INSV SCDRP$L_ABCNT(R5),- ;S FF; Load low-order number of bytes #16,#16,R0 ;S FF; transferred into R0 MOVZWL SCDRP$L_ABCNT+2(R5),R1 ;S FF; Load high-order number of bytes ;S FF; transferred into R1 MOVW #SS$_NORMAL,R0 ;S FF; Set success status IO_DC_EXIT: PUSHR #^M ;S FF; Save registers MOVL SCDRP$L_DATACHECK(R5),R0 ;S FF; Get datacheck buffer address BSBW DEALLOC_POOL ;S FF; Deallocate the datacheck buffer BBC #UCB$V_CB_NOCMDQ,- ;S FF; If device supports CMDQ, then no need UCB$L_CLASS_BUSY(R3), 10$ ;S FF; to clr_single CLR_SINGLE SINGLE_RW,R3 ;S FF; Clear thread R/W operations 10$: CLR_SINGLE SINGLE_DC,R3 ;S FF; Datacheck request is now complete. REMQUE @UCB$Q_DC_WAIT_LIST(R3), R1 ;S FF; Dequeue entry from datacheck wait list BVS 20$ ;S FF; Branch if list is empty MOVAL -SCDRP$PS_PQFL(R1), R1 ;S FF; Get starting address of SCDRP MOVL SCDRP$PS_KPB(R1), R1 ;S FF; Get KPB address PUSHL #SS$_NORMAL ;S FF; Return SS$_NORMAL PUSHL R1 ;S FF; KPB to restart CALLS #2,EXE$KP_RESTART ;S FF; restart this kernel process 20$: POPR #^M ;S FF; Restore registers BRW COMPLETE_IO ;S FF; Complete the QIO ; Here we have completed one piece of a segmented transfer. Update the MEDIA ; cell in the SCDRP to point to the LBN of the next segment. However, leave ; SCDRP$L_SVAPTE and SCDRP$L_BOFF pointing to the start of the datacheck buffer ; and go perform the next segment of the transfer. IO_DC_SEGMENT_DONE: ASHL #-IOC$S_DISK_BLKSIZ,R0,R1 ;S FF; Convert byte count to block count ADDL2 R1,SCDRP$L_MEDIA(R5) ;S FF; Update logical block number in SCDRP BRW IO_DC_LOOP ;S FF; Go perform next segment of transfer ; Here the transfer count returned by the port doesn't match the requested ; byte count. If it's greater, than truncate the transfer to what we ; requested. Otherwise, accumulate the piece of the transfer that just ; completed and continue with the transfer. IO_DC_MISMATCH: BLSS 10$ ;S FF; Branch if the transfer count is less ;S FF; than the requested count, accumulate ;S FF; this piece of the transfer ADDL3 SCDRP$L_PAD_BCNT(R5),- ;S FF; Truncate the transfer such that the SCDRP$L_BCNT(R5),- ;S FF; the transfer count we got is what we SCDRP$L_TRANS_CNT(R5) ;S FF; expected BRB IO_DC_ACCUM ;S FF; Accumulate this piece of the transfer 10$: BICW #,R0 ;S FF; Round transfer down to block multiple BEQL 20$ ;S FF; Branch if no bytes to accumulate MOVL R0,SCDRP$L_BCNT(R5) ;S FF; Adjust byte count and transfer count ADDL3 SCDRP$L_PAD_BCNT(R5),- ;S FF; to accumulate this segment of the R0,SCDRP$L_TRANS_CNT(R5) ;S FF; transfer. BRW IO_DC_ACCUM ;S FF; Accumulate this segment of the transfer 20$: MOVL #SS$_OPINCOMPL,R0 ;S FF; Set bad status BRB IO_DC_EXIT ;S FF; Complete I/O with error status ; ; Stall datacheck operations waiting for other SCDRPs to release UCB field ; ownership ; IO_DC_WAIT:: .GLOBAL_LABEL INSQUE SCDRP$PS_PQFL(R5), - ;F FF; Enqueue SCDRP on tail of DC wait queue @UCB$Q_DC_WAIT_LIST(R3) ;F FF; MOVL SCDRP$PS_KPB(R5), R0 ;F FF; Get SCDRP's KPB address MOVAL G^IOC$RETURN, - ;F FF; Set NULL stall routine address KPB$PS_SCH_STALL_RTN(R0) ;F FF; PUSHL R0 ;F FF; Push KPB address CALLS #1,G^EXE$KP_STALL_GENERAL ;F FF; Wait until other datachecks are done BRB IO_DATACHECK ;F FF; Try again to gain UCB ownership ;+ ; IO_NOP ; ; Additionaly this I/O function provides I/O sequentiality for use by OpenVMS I/O subsystems ; (Shadowing, MSCP Server, Cluster-I/O). This I/O function does NOT alter device media or ; mode parameters. I/O Sequentiality guarantees that all active I/Os received prior to this ; I/O operation will be executed to completion. Then this sequential I/O operation is performed. ; While the sequential command is active, no other I/Os will be started. Upon completion of this ; I/O operation, new I/O will be started, if no other conditions (busy bits), are set. ; ; As part of the I/O operation, an "ordered test-unit-ready" command to the associcated ; SCSI target is done. SCSI order commands provide and I/O ordering barrier for all initiators ; (VMSCluster members). This functionality is simialar to the Alpha MB (memory-barrier) ; instruction. ; ; Context: ; ; SCDRP thread ; IPL = Fork ; Fork lock held ; ; Inputs: ; ; R3 - UCB address ; R4 - PDT address ; R5 - SCDRP address ; ; Outputs: ; ; R0 - Status ; R1,R2 - Destroyed ; All other registers preserved ;- IO_NOP:: .GLOBAL_LABEL DK_DEBUG IO_NOP SET_SINGLE NOP,R3,20$,R5,10$ ;S FF; Single thread NOP request. DISABLE_ERRLOG ;S FF; Temporarily disable errorlogging BSBW TEST_UNIT_READY ;S FF; Issue an ORDERED TUR to sync queues REENABLE_ERRLOG ;S FF; Reenable errorlogging MOVZWL #SS$_NORMAL,R0 ;S FF; Set success status 10$: CLR_SINGLE NOP,R3,20$ ;S FF; NOP request is complete. BRB COMPLETE_IO ;S FF; Complete the I/O 20$: BUG_CHECK INCONSTATE,FATAL ;S FF; bugcheck .PAGE .SBTTL IO_UNLOAD - Make drive available (and spin it down) .SBTTL IO_AVAILABLE - Make drive available (but don't spin it down) ;+ ; IO_UNLOAD ; IO_AVAILABLE ; ; These two routines make the drive available by clearing the VALID bit in the ; specificed UCB. For the IO_UNLOAD entry point, the device is spun down if it has ; removable media. ; ; Additionaly this routine provides I/O sequentiality for use by OpenVMS I/O subsystems ; (Shadowing, MSCP Server, Cluster-I/O). I/O Sequentiality guarantees that all active ; I/Os received prior to this I/O operation will be executed to completion. The this ; ; sequential I/O operation is started. While the sequential command is active, no other ; I/Os will be started. Upon completion of this I/O operation, new I/O will be started, if no ; other conditions (busy bits), are set. ; ; As part of the I/O operation, an "ordered test-unit-ready" command to the associcated ; SCSI target is done. SCSI order commands provide and I/O ordering barrier for all initiators ; (VMSCluster members). This functionality is simialar to the Alpha MB (memory-barrier) ; instruction. ; ; Context: ; ; SCDRP thread ; IPL = Fork ; Fork lock held ; ; Inputs: ; ; R3 - UCB address ; R4 - PDT address ; R5 - SCDRP address ; ; Outputs: ; ; R0 - Status ; R1,R2 - Destroyed ; All other registers preserved ;- IO_AVAILABLE:: .GLOBAL_LABEL ;S FF; Make drive available (but don't spin it down) SET_SINGLE AVAILABLE,R3,20$,R5,10$ ;S FF; Single thread AVAILABLE request. BSBW TEST_UNIT_READY ;S FF; Issue an ORDERED TUR to sync queues BICL #UCB$M_VALID,- ;S FF; Clear the volume valid bit in UCB$L_STS(R3) ;S FF; the UCB MOVZWL #SS$_NORMAL,R0 ;S FF; Set success status 10$: CLR_SINGLE AVAILABLE,R3,20$ ;S FF; Available request is complete. BRW COMPLETE_IO ;S FF; Complete the I/O 20$: BUG_CHECK INCONSTATE,FATAL ;S FF; bugcheck IO_UNLOAD:: .GLOBAL_LABEL ;S FF; Make drive available (and spin it down) SET_SINGLE UNLOAD,R3,20$,R5,15$ ;S FF; Single thread UNLOAD request. BSBW TEST_UNIT_READY ;S FF; Issue an ORDERED TUR to sync queues BBC #UCB$V_REMOVABLE,- ;S FF; Branch if this is a device with UCB$L_DK_FLAGS(R3), 10$ ;S FF; non-removable media BSBW STOP_UNIT ;S FF; Spin down the unit 10$: BICL #UCB$M_VALID,- ;S FF; Clear the volume valid bit in UCB$L_STS(R3) ;S FF; the UCB MOVZWL #SS$_NORMAL,R0 ;S FF; Set success status 15$: CLR_SINGLE UNLOAD,R3,20$ ;S FF; Unload request is complete. BRW COMPLETE_IO ;S FF; Complete the I/O 20$: BUG_CHECK INCONSTATE,FATAL ;S FF; bugcheck .PAGE .SBTTL IO_FORMAT - format a floppy diskette ;+ ; IO_FORMAT ; ; This routine formats a floppy diskette. ; ; An operator may request the format with the DCL command ; INIT/DENSITY=DOUBLE ; 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 ; ; Context: ; ; SCDRP thread ; IPL = Fork ; Fork lock held ; ; Inputs: ; ; R2 - IRP address ; R3 - UCB address ; R4 - PDT address ; R5 - SCDRP address ; ; Outputs: ; ; R0 - Status ; R1,R2 - Destroyed ; All other registers preserved ;- IO_FORMAT:: .GLOBAL_LABEL ;S FF; format a floppy diskette SET_SINGLE FORMAT,R3 ;S FF; Single thread FORMAT request. ; Format command performs an implicit AVAILABLE. ; Update the UCB to reflect an AVAILABLE command. ASSUME UCB$V_VALID GE 8 BICL #,UCB$L_STS(R3) ;S FF; Clear software volume valid. BBCC #UCB$V_LCL_VALID,- ;S FF; First FORMAT or AVAILABLE? UCB$L_STS(R3), 10$ ;S FF; If CC not first, "Can't happen" DECB UCB$B_ONLCNT(R3) ;S FF; Decrement online count. 10$: BBS #UCB$V_FORMAT,- ;S FF; Is FORMAT supported by the drive? UCB$L_DK_FLAGS(R3),100$ MOVL #SS$_FORMAT,R0 ;S FF; No, return an error BRW 1000$ 100$: BBC #UCB$V_HWL,- ;S FF; Can we write (to format) the diskette? UCB$L_DK_FLAGS(R3),120$ ;S FF; MOVL #SS$_WRITLCK,R0 ;S FF; No, return an error BRW 1000$ ; ; Determine that the correct /DENSITY qualifier was used on the INIT command. ; ; We support the following commands: ; ; INIT/DENSITY=SINGLE shows up here as IRP$L_MEDIA=1 ; INIT/DENSITY=DD shows up here as IRP$L_MEDIA=1 ; INIT/DENSITY=DOUBLE shows up here as IRP$L_MEDIA=2 ; INIT/DENSITY=HD shows up here as IRP$L_MEDIA=2 ; INIT/DENSITY=ED shows up here as IRP$L_MEDIA=3 ; ; The allowed formats for the various drives are: ; ; RX23s allows IRP$L_MEDIA=2 always, and ; IRP$L_MEDIA=1 only when UCB$L_DK_FLAGS is set; ; RX33s allows only IRP$L_MEDIA=2; ; RX26 allows IRP$L_MEDIA= 1, 2, or 3, but the appropriate diskette ; must be in the drive. ; 120$: CMPB #DT$_RX26,UCB$B_DEVTYPE(R3) ;S FF; Check RX26-specific format params BNEQ 170$ ;S FF; Not RX26, go check the others CMPL #SCSI$DK$C_SS,IRP$L_MEDIA(R2) ;S FF; Lowest value allowed for RX26 BGTRU 180$ ;S FF; Too low, go report error CMPL #SCSI$DK$C_DD,IRP$L_MEDIA(R2) ;S FF; Highest value allowed for RX26 BLSSU 180$ ;S FF; Too high, go report error BRB 200$ ;S FF; Value's OK 170$: CMPL #SCSI$DK$C_DS,IRP$L_MEDIA(R2) ;S FF; Verify /density=double BEQL 200$ ;S FF; ;S FF; Allow formatting with /density=single ;S FF; only for RX23 with a bypass override ;S FF; because 9-sector formats are ;S FF; unreliable BBC #UCB$V_DD_BYPASS,- ;S FF; Check for the special override bit UCB$L_DK_FLAGS(R3),180$ ;S FF; to see if we can handle /density=single CMPB #DT$_RX23S,UCB$B_DEVTYPE(R3) ;S FF; Only worry about RX23 media BNEQ 180$ CMPL #SCSI$DK$C_SS,IRP$L_MEDIA(R2) ;S FF; Verify /density=single BEQL 200$ ;S FF; Yep, media's = singler 180$: MOVL #SS$_BADPARAM,R0 ;S FF; Nope, report error BRW 1000$ ; ; Adjust format-sensitive parameters in the UCB here ; ; Check for appropriate diskette type for the density requested ; 200$: MOVL SCDRP$L_IRP(R5),R2 ;S FF; Restore IRP address CMPB #DT$_RX26,UCB$B_DEVTYPE(R3) ;S FF; Only RX26 reports diskette type BNEQ 500$ ;S FF; Not RX26, skip this check CMPZV #UCB$V_FLOPPY_MEDIA,- ;S FF; Check that the requested format #UCB$S_FLOPPY_MEDIA,- ;S FF; is compatible with the diskette UCB$L_DK_FLAGS(R3),- ;S FF; IRP$L_MEDIA(R2) ;S FF; BEQL 500$ ;S FF; OK, let it through MOVL #SS$_FORMAT,R0 ;S FF; No, return an error BRB 1000$ ;S FF; Return error 500$: BSBW PROCESS_MODE_FORMAT_FLOPPY ;S FF; get changable parameters BLBC R0,1000$ ;S FF; Return on error BSBW FORMAT ;S FF; Issue format command BLBC R0,1000$ ;S FF; Return on error MOVZWL #SS$_NORMAL,R0 ;S FF; Set success status 1000$: CLR_SINGLE FORMAT,R3 ;S FF; FORMAT request is now complete BRW COMPLETE_IO ;S FF; Complete the I/O .PAGE .SBTTL IO_DSE - Data security erase function ;+ ; IO_DSE ; ; This routine erases a set of logical blocks by writing zeros to the ; blocks. ; ; Context: ; ; SCDRP thread ; IPL = Fork ; Fork lock held ; ; Inputs: ; ; R2 - IRP address ; R3 - UCB address ; R4 - PDT address ; R5 - SCDRP address ; ; Outputs: ; ; R0 - Status ; R1,R2 - Destroyed ; All other registers preserved ;- MAX_BUF_SIZE = 65535 & < ^C < IOC$C_DISK_BLKSIZ - 1> > ASSUME MAX_BUF_SIZE& eq 0 IO_DSE:: .GLOBAL_LABEL ;S FF; Data security erase function BBC #UCB$V_CB_NOCMDQ,- ;SF FF; If device supports CMDQ, then no need UCB$L_CLASS_BUSY(R3), 5$ ;SF FF; to set_single SET_SINGLE SINGLE_DSE,R3 ;S FF; Single thread Data security erase request 5$: BICL #IRP$M_FUNC,- ;S FF; Clear the FUNC but to indicate this SCDRP$IS_STS(R5) ;S FF; is a write function MOVL IRP$L_MEDIA(R2),- ;S FF; Copy starting block number from SCDRP$L_MEDIA(R5) ;S FF; IRP to SCDRP ADDL3 #,- ;S FF; Round up byte count IRP$L_BCNT(R2),R1 ;S FF; to a block BICL #,R1 ;S FF; boundary MOVL R1,IRP$L_BCNT(R2) ;S FF; save modified byte count ; ; allocate zeroed buffer to write from ; CMPL R1,#MAX_BUF_SIZE ;S FF; is the buffer too big? BLEQ 10$ ;S FF; LEQ means it's OK MOVL #MAX_BUF_SIZE ,R1 ;S FF; if it's too big use max buffer size 10$: PUSHL R5 ;S FF; Save SCDRP address MOVL SCDRP$PS_KPB(R5),R5 ;S FF; Get KPB address BSBW DK_ALLOC_POOL ;S FF; Allocate pool POPL R5 ;S FF; REstore SCDRP address BLBC R0,37$ ;S FF; Exit if allocation failed BISB #SCDRP$M_FLAG_S0BUF,- ;S FF; Indicate that this is an S0 "user" SCDRP$L_SCSI_FLAGS(R5) ;S FF; buffer MOVL R2,SCDRP$L_SVA_USER(R5) ;S FF; Save address of the DSE buffer ; ; get svapte of allocated buffer ; PUSHL R3 ;S FF; Save R3 (svapte will be returned in R3) JSB G^MMG$SVAPTECHK ;S FF; Get SVAPTE of allocated system buffer MOVL R3,SCDRP$L_SVAPTE(R5) ;S FF; Save SVAPTE in SCDRP ; ; generate byte offset from va of allocated buffer ; MCOML G^MMG$GL_BWP_MASK,R3 ;S FF; get mask of bits to clear BICL3 R3,R2,SCDRP$L_BOFF(R5) ;S FF; mask of non-offset part of the VA POPL R3 ;S FF; Restore R3 CLRL SCDRP$L_ABCNT(R5) ;S FF; set accumulated count to 0 ; ; This is the main write loop ; 20$: MOVL SCDRP$L_IRP(R5),R2 ;S FF; restore the IRP ; ; get bytes left to write in R0 (IRP$L_BCNT(R2)-SCDRP$L_ABCNT(R5)) ; MOVL IRP$L_BCNT(R2),R0 ;S FF; get QIO byte count SUBL SCDRP$L_ABCNT(R5),R0 ;S FF; get bytes left to write out BLEQU 32$ ; ; Check to see if we've gotten to the end of the disk ; CMPL SCDRP$L_MEDIA(R5),- ;S FF; are we past the end of the DISK? UCB$L_MAXBLOCK(R3) BGEQ 30$ ;S FF; GTR means yes don't write any more ; ; see if bytes left to write is too big ; CMPL R0,#MAX_BUF_SIZE ;S FF; is this too many to do now? BLEQ 22$ ;S FF; LEQ means no write them out MOVL #MAX_BUF_SIZE,R0 ;S FF; else use max size ; ; make sure we won't write past end of disk ; 22$: ASHL #-IOC$S_DISK_BLKSIZ,R0,R1 ;S FF; convert to blocks ADDL2 SCDRP$L_MEDIA(R5),R1 ;S FF; get block after last write CMPL R1,UCB$L_MAXBLOCK(R3) ;S FF; will we go past the end? BLEQ 25$ ;S FF; leq means no everything is OK SUBL3 SCDRP$L_MEDIA(R5),- ;S FF; less starting block for # blocks left UCB$L_MAXBLOCK(R3),R1 ASHL #IOC$S_DISK_BLKSIZ,R1,R0 ;S FF; convert BLOCKS to bytes 25$: MOVL R0,SCDRP$L_BCNT(R5) ;S FF; Save byte count for this write MOVL #SCDRP$K_QCHAR_UNORDERED,- ;S FF; Send a Simple tagged request SCDRP$IS_QUEUE_CHAR(R5) BSBW READ_WRITE ;S FF; write out the data BLBC R0,34$ ;S FF; LBC means some error MOVL SCDRP$L_BCNT(R5),R0 ;S FF; get the # bytes that we wrote ADDL2 R0,SCDRP$L_ABCNT(R5) ;S FF; update the accumulated byte count ;S FF; with bytes we just wrote ASHL #-IOC$S_DISK_BLKSIZ,R0,R0 ;S FF; convert to blocks ADDL2 R0,SCDRP$L_MEDIA(R5) ;S FF; update next block to erase BRW 20$ 30$: MOVL #SS$_IVADDR,R0 ;S FF; here if we get to the end of the disk BRW 36$ 32$: MOVL #SS$_NORMAL,R0 ;S FF; here if we finish without incident BRW 36$ 34$: ;S FF; here if we get an error from read_write ;S FF; return the error as is ; ; here to deallocate pool - retuens status in R0 ; 36$: PUSHL R0 ;S FF; Save status MOVL SCDRP$L_SVA_USER(R5),R0 ;S FF; Get address of S0 buffer BSBW DEALLOC_POOL ;S FF; Deallocate the S0 buffer POPL R0 ;S FF; Restore R0 ; ; here after buffer is deallocated or to return errors that happen before ; the buffer is allocated or if the allocation fails. ; 37$: MOVZWL SCDRP$L_ABCNT+2(R5),R1 ;S FF; Set high-order word of transfer cnt INSV SCDRP$L_ABCNT(R5),#16,#16,R0 ;S FF; Copy low-order word of transfer cnt BBC #UCB$V_CB_NOCMDQ,- ;SF FF; If device supports CMDQ, then no need UCB$L_CLASS_BUSY(R3), 40$ ;SF FF; to clr_single CLR_SINGLE SINGLE_DSE,R3 ;S FF; Data security erase request is now complete. 40$: BRW COMPLETE_IO ;S FF; Complete the QIO request .PAGE .SBTTL IO_DIAGNOSE - Special pass-through function ;+ ; IO_DIAGNOSE ; ; Context: ; ; SCDRP thread ; IPL = Fork ; Fork lock held ; ; Inputs: ; ; R2 - IPR address ; R3 - UCB address ; R4 - PDT address ; R5 - SCDRP address ; ; Outputs: ; ; R0 - Status ; R1,R2 - Destroyed ; All other registers preserved ;- IO_DIAGNOSE:: .GLOBAL_LABEL ;S FF; Special pass-through function PUSHAL UCB$PS_DIAGNOSE(R3) ;S FF; Address of DIAGNOSE area in UCB PUSHL R5 ;S FF; SCDRP address PUSHL SCDRP$L_CDT(R5) ;S FF; SCDT address PUSHL R2 ;S FF; IRP address CALLS #4,DKMK$DIAGNOSE_SIO ;S FF; Call common DIAGNOSE routine ; Quadword status is returned in R0. Get high longword of R0 into R1. EVAX_SRL R0,#32,R1 ;S FF; Return status in R0/R1 BRW COMPLETE_IO ;S FF; Complete the QIO .PAGE .SBTTL IO_AUDIO - SCSI audio STARTIO function .ENABLE LOCAL_BLOCK ;+ ; IO_AUDIO ; ; This routine is executed in the STARTIO context. It converts an AUCB into a SCSI AUDIO ; Command Descriptor Block (CDB). This CDB is then sent to and executed by the target ; device. If there is data to be returned to a user buffer the data is copied within ; this routine. The AUCB Operating System status and SCSI status fields are updated. ; ; After the I/O has been completed by the target, the AUDIO_EXIT code is called to unlock ; any users buffers other than the AUCB. The AUCB will be unlocked during I/O post- ; processing. The System PTE's allocated to double map any of the buffer will be ; deallocated by this routine, prior to calling REQCOM. ; ; Context: ; ; SCRDP thread ; IPL = Fork ; Fork lock held ; ; Inputs: ; R2 - IRP address ; R3 - UCB address ; R4 - PDT address ; R5 - SCDRP address ; ; Outputs: ; ; R0 - Status ; R1,R2 - Destroyed ; All other registers preserved ;- IO_AUDIO:: .GLOBAL_LABEL ;S FF; SCSI audio STARTIO function SET_SINGLE AUDIO,R3 ;S FF; Single thread the AUDIO request MOVL IRP$L_SEGVBN(R2),R0 ;S FF; Get start address of S0 buffer. MOVL R0,SCDRP$L_MEDIA(R5) ;S FF; Get copy of AUCB address in SCDRP. ;+ ; Dispatch to the appropriate path based on the function code in the low ; order byte of the AUCB's function code field. ; ; R0 is the SVA of the allocated buffered I/O buffer or AUCB Address. ; R2 is the address of the original IRP. ; R5 is the address of the SCDRP. ;- DISPATCH CD_FUNCTION_CODE(R0),TYPE=B,<- ;S FF; Dispatch according to function ,- ; Pause ,- ; Resume ,- ; Prevent removal ,- ; Allow removal ,- ; Play Audio LBA ,- ; Play MSF ,- ; Play Audio Track ,- ; Play Track Relative ,- ; Read Subchannel-Q ,- ; Read TOC ,- ; Mode Sense > ; Mode Select ;+ ; Note: The SONY CDROM does not implement this command. ;- CD_PLAY_TRACK_REL: ;S FF; Audio CDROM control - Play Track ;+ ; All unsupported I/O functions will be failed at this point. ;- MOVL SCDRP$L_MEDIA(R5),R0 ;S FF; Get copy of AUCB address from SCDRP. MOVZWL #SS$_BADPARAM,- ;S FF; Set bad parameter status CD_COMMAND_STATUS(R0) MOVZWL CD_COMMAND_STATUS(R0),R0 ;S FF; Copy VMS status to R0 for COMPLETE_IO BRW AUDIO_BAD_CMD_EXIT ;S FF; Complete this I/O function .DISABLE LOCAL_BLOCK .PAGE .SBTTL AUDIO_PLAY_FUNCTIONS - Audio CDROM play startio code ;+ ; AUDIO_PLAY_FUNCTIONS ; ; The following code, makes up the startio routine for all of the Audio ; play functions supported by this driver. Execution of these functions cause ; the CD to begin a play operation. ; ; Context: ; ; SCDRP thread ; IPL = Fork ; Fork lock held ; ; Inputs: ; R0 - AUCB address ; R2 - IRP address ; R3 - UCB address ; R4 - PDT address ; R5 - SCDRP address ; ; Outputs: ; ; R0 - Status ; R1,R2 - Destroyed ; All other registers preserved ;- ; AUDIO_PLAY_FUNCTIONS: ;+ ; Now we will allocate a command buffer and fill it in with a SCSI CDB ; with all the required fields initialized. ; ; The PLAY_TRACK command requires a starting and ending track. The starting ; track is passed AUCB ARG1 and ending track in AUCB ARG2. ;- CD_PLAY_AUDIO_TRACK: ;S FF; Audio CDROM control - Play Audio MOVAL CMD_PLAY_TRACK,R2 ;S FF; Address of Play Track command SETUP_CMD ;S FF; Perform setup for SCSI command ADDL3 #4,SCDRP$L_CMD_PTR(R5),R0 ;S FF; Get address of SCSI CDB (Command). ;S FF; The CDB doesn't start at 0, but 4. MOVL SCDRP$L_MEDIA(R5),R1 ;S FF; Restore address of AUCB. ;+ ; The command bytes must be moved one byte at a time into the CDB, since ; the byte ordering is not the same as VAX! ;- CMPB CD_ARG1(R1),#99 ;S FF; IF starting track > 99 BGTRU IO_BADPARM ;S FF; then error. MOVB CD_ARG1(R1),- ;S FF; Copy Starting Track number to CDB. CMD_PLAY_TRACK$B_STARTING_TRACK(R0) MOVB CD_ARG1+1(R1),- ;S FF; Copy Starting Index number. CMD_PLAY_TRACK$B_STARTING_INDEX(R0) MOVB CD_ARG2(R1),- ;S FF; Copy Ending Track number to CDB. CMD_PLAY_TRACK$B_END_TRACK(R0) ;S FF; If ending track is 0 BEQL IO_BADPARM ;S FF; then error. MOVB CD_ARG2+1(R1),- ;S FF; Copy Ending Index number. CMD_PLAY_TRACK$B_END_INDEX(R0) 10$: SEND_COMMAND_ORDERED ;S FF; Send the SCSI command BRW AUDIO_EXIT ;S FF; Complete an audio SCSI function ;+ ; The Play Audio command requires a starting LBA address and block count which ; are passed in ARG1 and ARG2 of the AUCB respectively. ;- CD_PLAY_AUDIO: ;S FF; Audio CDROM control - Play Audio MOVAL CMD_PLAY_AUDIO10,R2 ;S FF; Address of Play Audio10 command SETUP_CMD ;S FF; Perform setup for SCSI command ADDL3 #4,SCDRP$L_CMD_PTR(R5),R0 ;S FF; Get address of SCSI CDB (Command). ;S FF; The CDB doesn't start at 0, but 4. MOVL SCDRP$L_MEDIA(R5),R1 ;S FF; Restore address of AUCB. ;+ ; Setup the starting Logical Block Address(LBA) for the Play Audio 10 command. ; ; The command bytes must be moved one byte at a time into the CDB, since ; the byte ordering is not the same as VAX! ;- MOVB CD_ARG1+0(R1),- ;S FF; Copy Starting Logical Block Addr(LSB) CMD_PLAY_ADUIO10$B_LBA_0(R0) MOVB CD_ARG1+1(R1),- ;S FF; Copy middle LBA byte to CDB. CMD_PLAY_ADUIO10$B_LBA_1(R0) MOVB CD_ARG1+2(R1),- ;S FF; Copy middle LBA byte to CDB. CMD_PLAY_ADUIO10$B_LBA_2(R0) MOVB CD_ARG1+3(R1),- ;S FF; Copy MSB LBA byte to CDB CMD_PLAY_ADUIO10$B_LBA_3(R0) ;+ ; Now setup the transfer length in the CDB for the Play Audio 10 command. ;- MOVB CD_ARG2+0(R1),- ;S FF; LSB of the Transfer Length into CDB. CMD_PLAY_ADUIO10$B_XFR_0(R0) MOVB CD_ARG2+1(R1),- ;S FF; MSB of Transfer Length. CMD_PLAY_ADUIO10$B_XFR_1(R0) TSTW CD_ARG2+2(R1) ;S FF; These 2 fields must be zero. BNEQ IO_BADPARM ;S FF; No, bad parameter 10$: SEND_COMMAND_ORDERED ;S FF; Send the SCSI command BRW AUDIO_EXIT ;S FF; Complete an audio SCSI function ;+ ; The Play MSF command requires a starting and ending time expressed in Minutes, Seconds ; and frames format and are passed in ARG1 and ARG2 of the AUCB respectively. ;- CD_PLAY_AUDIO_MSF: ;S FF; Audio CDROM control - Play Audio HH:SS:F MOVAL CMD_PLAY_AUDIO_MSF,R2 ;S FF; Address of Play Audio MSF (47) command SETUP_CMD ;S FF; Perform setup for SCSI command ADDL3 #4,SCDRP$L_CMD_PTR(R5),R0 ;S FF; Get address of SCSI CDB (Command). ;S FF; The CDB doesn't start at 0, but 4. MOVL SCDRP$L_MEDIA(R5),R1 ;S FF; Restore address of AUCB. ;+ ; Setup the starting Minute, Seconds and Frame fields for the Play MSF command. ; ; The command bytes must be moved one byte at a time into the CDB, since ; the byte ordering is not the same as VAX! ;- MOVB CD_ARG1+0(R1),- ;S FF; Copy Starting Frame byte to the CDB. CMD_PLAY_AUDIO$B_START_FRM(R0) MOVB CD_ARG1+1(R1),- ;S FF; Copy Seconds byte to the CDB. CMD_PLAY_AUDIO$B_START_SEC(R0) MOVB CD_ARG1+2(R1),- ;S FF; Copy Minutes byte to the CDB. CMD_PLAY_AUDIO$B_START_MIN(R0) ;+ ; Now setup the ending MSF fields for the Play Audio 12 command. ;- MOVB CD_ARG2+0(R1),- ;S FF; Copy ending Frames byte to the CDB. CMD_PLAY_AUDIO$B_END_FRM(R0) MOVB CD_ARG2+1(R1),- ;S FF; Copy Seconds byte to the CDB. CMD_PLAY_AUDIO$B_END_SEC(R0) MOVB CD_ARG2+2(R1),- ;S FF; Copy Minutes byte to the CDB. CMD_PLAY_AUDIO$B_END_MIN(R0) 10$: SEND_COMMAND_ORDERED ;S FF; Send the SCSI command BRW AUDIO_EXIT ;S FF; Complete an audio SCSI function .PAGE .SBTTL AUDIO_CONTROL_FUNCTIONS - Audio CDROM control startio code ;+ ; AUDIO_CONTROL_FUNCTIONS ; ; The functions in this section of code are used to executed various audio ; control functions including pausing and resuming the CD. ; ; Context: ; ; SCDRP thread ; IPL = Fork ; Fork lock held ; ; Inputs: ; R0 - AUCB address ; R2 - IRP address ; R3 - UCB address ; R4 - PDT address ; R5 - SCDRP address ; ; Outputs: ; R0 - Status ; R1,R2 - Destroyed ; All other registers preserved ;- ; AUDIO_CONTROL_FUNCTIONS: CD_PAUSE: ;S FF; Audio CDROM control - Pause MOVAL CMD_PAUSE,R2 ;S FF; Address of PAUSE command SETUP_CMD ;S FF; Perform setup for SCSI command SEND_COMMAND_ORDERED ;S FF; Send the SCSI command BRW AUDIO_EXIT ;S FF; Exit Audio Start I/O. CD_RESUME: ;S FF; Audio CDROM control - Resume MOVAL CMD_RESUME,R2 ;S FF; Address of RESUME command SETUP_CMD ;S FF; Perform setup for SCSI command SEND_COMMAND_ORDERED ;S FF; Send the SCSI command BRW AUDIO_EXIT ;S FF; Exit Audio Start I/O. CD_PREVENT_REMOVAL: ;S FF; Audio CDROM control - no REMOVE MOVAL CMD_REMOVAL,R2 ;S FF; Address of Prevent Removal command SETUP_CMD ;S FF; Perform setup for SCSI command ADDL3 #4,SCDRP$L_CMD_PTR(R5),R0 ;S FF; Get address of SCSI CDB (Command). ;S FF; The CDB doesn't start at 0, but 4. MOVB #1,CMD_REMOVAL$R_PREVENT(R0) ;S FF; Set the Prevent Removal bit. SEND_COMMAND_ORDERED ;S FF; Send the SCSI command BRW AUDIO_EXIT ;S FF; Exit Audio Start I/O. CD_ALLOW_REMOVAL: ;S FF; Audio CDROM control - yes REMOVE MOVAL CMD_REMOVAL,R2 ;S FF; Address of Prevent Removal command SETUP_CMD ;S FF; Perform setup for SCSI command ADDL3 #4,SCDRP$L_CMD_PTR(R5),R0 ;S FF; Get address of SCSI CDB (Command). ;S FF; The CDB doesn't start at 0, but 4. CLRB CMD_REMOVAL$R_PREVENT(R0) ;S FF; Clear the Prevent Removal bit. SEND_COMMAND_ORDERED ;S FF; Send the SCSI command BRW AUDIO_EXIT ;S FF; Exit Audio Start I/O. .PAGE .SBTTL AUDIO_GETSET_FUNCTIONS - Audio CDROM get/set parameter code ;+ ; AUDIO_GETSET_FUNCTIONS ; ; The functions in this section of code are used to query or initialize ; specific parameters or data from the CDROM. For example, the Table Of ; Contents (TOC) can be read, the current position of the CDROM ; can be read and returned to the user or the volume of the CDROM can be set. ; ; Context: ; ; SCDRP thread ; IPL = Fork ; Fork lock held ; ; Inputs: ; R0 - AUCB address ; R2 - IRP address ; R3 - UCB address ; R4 - PDT address ; R5 - SCDRP address ; ; Outputs: ; ; R0 - Status ; R1,R2 - Destroyed ; All other registers preserved ;- ; AUDIO_GETSET_FUNCTIONS: ;+ ; The GET_STATUS AUCB function does a SCSI READ SUBQ command and returns the ; requested subchannel data (format 1 and 2 are supported) to the application. ; From this information the application can determine the current track and ; head location of the CD-ROM as well as the UPC bar code data. ;- CD_READ_SUB: ;S FF; Audio CDROM control - read subchannel DISPATCH CD_ARG2(R0),TYPE=B,<- ;S FF; Dispatch based on format ,-;S FF; - Sub-Q Channel data ,-;S FF; - CD-ROM Current Position ,-;S FF; - Media Catalog Number (UPC/Bar Code) > ;S FF; - Track International-Standard-Recording-Code (ISRC) MOVL #SS$_BADPARAM,R0 ;S FF; else bad parameter BRW AUDIO_EXIT_NO_CMD ;S FF; ;+ ; Get format 2 (MCN) data from UCB or by issuing a SCSI READ SUBQ command ;- 20$: BSBW FETCH_MCN ;S FF; get format 2 data BLBC R0,AUDIO_EXIT_NO_CMD ;S FF; R0 with status ; ; MCN data in UCB is good, copy to users buffer ; PUSHR #^M MOVL SCDRP$L_IRP(R5),R1 ;S FF; Get original IRP Address MOVL IRP$L_SEGVBN(R1),R2 ;S FF; Get start address of the AUCB. MOVL CD_DEST_BUF_CNT(R2),R2 ;S FF; Get size of destination buffer. MOVL #SCSI$SUBQ$C_MCN_LENGTH,R0 ;S FF; and size of stored MCN data MINUM R0,R2 ;S FF; Minimize between these two values. MOVL IRP$L_EXTEND(R1),R1 ;S FF; Get the linked/extended IRP address MOVC3 R0,UCB$B_MCN_SCDATA(R3),- ;S FF; Copy the number of bytes received from @IRP$L_SEGVBN(R1) ;S FF; UCB to the start of the mapped user buffer POPR #^M MOVL #SS$_NORMAL,R0 ;S FF; return success BRW AUDIO_EXIT_NO_CMD ;S FF; 40$: BSBW READ_CD_SUBQ ;S FF; Get format 1 data. BLBC R0,AUDIO_EXIT ;S FF; On error, complete audio function ;+ ; Move READ SUBQ data from intermediate buffer to users destination buffer. ;- PUSHR #^M MOVL SCDRP$L_IRP(R5),R1 ;S FF; Get original IRP Address MOVL IRP$L_SEGVBN(R1),R2 ;S FF; Get start address of the AUCB. MOVL CD_DEST_BUF_CNT(R2),R2 ;S FF; Get size of destination buffer. MINUM SCDRP$L_TRANS_CNT(R5),R2 ;S FF; Minimize between these two values. MOVL IRP$L_EXTEND(R1),R1 ;S FF; Get the linked/extended IRP address MOVC3 SCDRP$L_TRANS_CNT(R5),- ;S FF; Copy the number of bytes received @SCDRP$L_SVA_USER(R5),- ;S FF; Copy from Nonpaged, to the @IRP$L_SEGVBN(R1) ;S FF; start of the mapped user buffer POPR #^M BRW AUDIO_EXIT ;S FF; Complete an audio SCSI function ;+ ; issue the format 0 "read", this is done by combining format 1 and format ; 2 data. ;- 80$: BSBW READ_CD_SUBQ ;S FF; Get format 1 data. BLBC R0,AUDIO_EXIT ;S FF; On error, complete audio function ;+ ; Move READ SUBQ data from intermediate buffer to users destination buffer. ;- MOVL SCDRP$L_IRP(R5),R1 ;S FF; Get original IRP Address MOVL IRP$L_SEGVBN(R1),R2 ;S FF; Get start address of the AUCB. MOVL CD_DEST_BUF_CNT(R2),R2 ;S FF; Get size of destination buffer. MINUM SCDRP$L_TRANS_CNT(R5),R2 ;S FF; Minimize between these two values. PUSHR #^M MOVL IRP$L_EXTEND(R1),R1 ;S FF; Get the linked/extended IRP address MOVC3 SCDRP$L_TRANS_CNT(R5),- ;S FF; Copy the number of bytes received @SCDRP$L_SVA_USER(R5),- ;S FF; Copy from Nonpaged, to the @IRP$L_SEGVBN(R1) ;S FF; start of the mapped user buffer MOVL R3,R1 ;S FF; copy addr of next byte in buffer POPR #^M SUBL SCDRP$L_TRANS_CNT(R5),R2 ;S FF; Subtract what's already transfered ;S FF; from size of users buffer PUSHR #^M CLEANUP_CMD ;S FF; cleanup from READ SUBQ command BSBW FETCH_MCN ;S FF; get format 2 data POPR #^M BLBC R0,AUDIO_EXIT_NO_CMD ;S FF; R0 with status PUSHR #^M MOVL #SCSI$SUBQ$C_MCN_LENGTH,R0 ;S FF; get size of stored MCN data MINUM R0,R2 ;S FF; Minimize between these two values. MOVC3 R0,UCB$B_MCN_SCDATA(R3),(R1) ;S FF; Copy the number of bytes received from ;S FF; from UCB to the remainder of user's buffer POPR #^M MOVL #SS$_NORMAL,R0 ;S FF; return success BRW AUDIO_EXIT_NO_CMD ;S FF; ;+ ; The GET_TOC AUCB function does a SCSI READ command and returns the Table Of Contents ; (TOC) data to the application. From this information the appliaction can determine ; the starting location and data type for each track on the CD-ROM. ;- CD_READ_TOC: ;S FF; Audio CDROM control - Read TOC MOVAL CMD_CD_READ_TOC,R2 ;S FF; Address of READ TOC command SETUP_CMD ;S FF; Set Command ADDL3 #4,SCDRP$L_CMD_PTR(R5),R0 ;S FF; Get address of SCSI CDB (Command). ;S FF; The CDB doesn't start at 0, but 4. MOVL SCDRP$L_MEDIA(R5),R1 ;S FF; Restore address of AUCB. CMPB CD_ARG2(R1),#99 ;S FF; Can't have more than 99 tracks, now! BGTRU IO_BADPARM ;S FF; then error. MOVB CD_ARG2(R1),- ;S FF; Write Starting Track into CMD_CD_READ_TOC$B_START_TRACK(R0);S FF; the SCSI CDB (command) CMPB CD_ARG1+0(R1),#1 ;S FF; If greater than 1 BGTRU IO_BADPARM ;S FF; error ASHL #1,CD_ARG1(R1),R1 ;S FF; Get LBA/MSF bit in left bit position BISB R1,CMD_CD_READ_TOC$R_FLAGS(R0) ;S FF; Select MSF or LBA address format. SEND_COMMAND_ORDERED ;S FF; Send the SCSI command BLBC R0,AUDIO_EXIT ;S FF; On error, complete audio function ;+ ; Move TOC data from intermediate buffer to the destination buffer. ;- PUSHR #^M MOVL SCDRP$L_IRP(R5),R1 ;S FF; Get original IRP Address MOVL IRP$L_SEGVBN(R1),R2 ;S FF; Get start address of the AUCB. MOVL CD_DEST_BUF_CNT(R2),R2 ;S FF; Get size of destination buffer. MINUM SCDRP$L_TRANS_CNT(R5),R2 ;S FF; Minimize between these two values. MOVL IRP$L_EXTEND(R1),R1 ;S FF; Get the linked/extended IRP address MOVC3 SCDRP$L_TRANS_CNT(R5),- ;S FF; Copy the number of bytes received @SCDRP$L_SVA_USER(R5),- ;S FF; Copy from Nonpaged, to the @IRP$L_SEGVBN(R1) ;S FF; start of the mapped user buffer POPR #^M BRW AUDIO_EXIT ;S FF; Complete an audio SCSI function ;+ ; The GET_VOLUME AUCB function does a SCSI MODE SENSE command and returns the Sense Data ; containing the channel information to the application, from this the current volume ; can be determined. ;- CD_GET_VOLUME: ;S FF; Audio CDROM control - Get Volume ;+ ; Before doing a mode select (set volume) do a mode sense to get the current ; values of the parameters. ;- BSBW CD_GET_SENSE ;S FF; Issue CD-ROM specific Mode Sense BLBC R0,AUDIO_EXIT ;S FF; Branch on error ;+ ; Copy the volume and channel control from the Sense data buffer to the ; AUCB. ;- PUSHL R0 MOVL SCDRP$L_SVA_USER(R5),R0 ;S FF; Get address of MODE SENSE Data MOVL SCDRP$L_MEDIA(R5),R1 ;S FF; Get copy of AUCB address from SCDRP. ASSUME SCSI$ACP$S_CHANNEL_VOLUME EQ <4*2> MOVL SCSI$ACP$W_CHANNEL_VOLUME(R0),- ;S FF; Copy port(0,1) Volume and port info CD_ARG1(R1) MOVL SCSI$ACP$W_CHANNEL_VOLUME+4(R0),- CD_ARG1+4(R1) POPL R0 BRW AUDIO_EXIT ;S FF; Complete an audio SCSI function ;+ ; The SET_VOLUME AUCB function does a SCSI MODE SENSE, followed by a MODE SELECT command ; and sets the volume and channel information on the target device. ;- CD_SET_VOLUME: ;S FF; Audio CDROM control - Set Volume ;+ ; Before doing a mode select (set volume) do a mode sense to get the current ; values of the parameters. ;- BSBW CD_GET_SENSE ;S FF; Issue CD-ROM specific Mode Sense BLBC R0,AUDIO_EXIT ;S FF; On error, complete audio function ;+ ; Since we want to reuse this SCDRP and call SETUP_CMD, deallocate the ; previously allocated Mode Sense COMMAND buffer. SETUP_CMD will allocate ; a new one for the MODE SELECT command. ; ; Note: We are using the previously allocated MODE SENSE buffer as our MODE ; Select buffer. CLEANUP_CMD will deallocate this buffer when we are done. ;- SPI$CMD_BUFFER_DEALLOC ;S FF; Deallocate the command buffer MOVAL CMD_CD_MODE_SELECT,R2 ;S FF; Address of MODE SENSE SETUP_CMD ;S FF; Set Command ADDL3 #4,SCDRP$L_CMD_PTR(R5),R0 ;S FF; Get address of SCSI CDB (Command). ;S FF; The CDB doesn't start at 0, but 4. MOVL SCDRP$L_MEDIA(R5),R1 ;S FF; Restore address of AUCB. ;+ ; We must force this request to be a write request, since we are bye-passing ; some of the SETUP_CMD setup to allow us to use the MODE SENSES buffer. ;- BICL #IRP$M_FUNC,SCDRP$IS_STS(R5) ;S FF; Mode Select is "write" operation, ;S FF; with data going to the target. ;+ ; Now that all the setup is done, fixup the MODE SENSE bytes to be exactly ; the way we need them. ; ; Copy the volume and channel control from the AUCB into the CCB. ;- SCSI_RESPONSE_LENGTH = - SCSI$MPH6$S_MODE_PARAM_HDR_6 +- ; Mode Parameter Header SCSI$MPBD$S_MODE_PARAMETER +- ; Mode Parameter Block SCSI$ACP$S_AUDIO_CONTROL ; Audio Control Block MOVB #SCSI_RESPONSE_LENGTH,- ;S FF; Number of Mode Select bytes expected SCSI$MPH6$B_BLOCK_LENGTH(R0) ;S FF; In response to MODE SENSE Command MOVL SCDRP$L_SVA_USER(R5),R0 ;S FF; Get address of MODE SENSE Data CLRB SCSI$MPH6$B_DATA_LENGTH(R0) ;S FF; Clear the Sense Data Length SENSE_VOLUME =- SCSI$MPH6$S_MODE_PARAM_HDR_6 +- ; Mode Parameter Header SCSI$MPBD$S_MODE_PARAMETER +- ; Mode Parameter Block SCSI$ACP$S_CHANNEL_VOLUME ; CD-ROM ACB Volume ASSUME SCSI$ACP$S_CHANNEL_VOLUME EQ <4*2> MOVL CD_ARG1(R0),SENSE_VOLUME(R0) ;S FF; Copy port(0,1,2,3) Volume and port info MOVL CD_ARG1+4(R0),SENSE_VOLUME+4(R0) SEND_COMMAND_ORDERED ;S FF; Send the SCSI command BRW AUDIO_EXIT ;S FF; Complete an audio SCSI function ;+ ; Subroutine used to get the volume information by issuing a mode sense ; command for the Audio Control Parameters. ;- ; Context: ; ; SCDRP thread ; IPL = Fork ; Fork lock held ; ; Inputs: ; ; R3 - UCB address ; R4 - SPDT address ; R5 - SCDRP address ; ; Outputs: ; R0 - Status ; R1,R2 - destroyed CD_GET_SENSE:: ;S FF; Audio CDROM get parameter code .JSB_ENTRY INPUT=,OUTPUT=,SCRATCH= MOVAL CMD_MODE_SENSE,R2 ;S FF; Address of MODE SENSE SETUP_CMD ;S FF; Set Command ADDL3 #4,SCDRP$L_CMD_PTR(R5),R0 ;S FF; Get address of SCSI CDB (Command). ;S FF; The CDB doesn't start at 0, but 4. MOVL SCDRP$L_MEDIA(R5),R1 ;S FF; Restore address of AUCB. ; For now just get the value for the CDROM page, later get it from the ; AUCB. MOVB #SCSI$C_AUDIO_CONTROL_PAGE,- ;S FF; Interested in CDROM Audio Ctrl Page CMD_MODE_SENSE$R_PAGE_CODE(R1) SCSI_RESPONSE_LENGTH = - SCSI$MPH6$S_MODE_PARAM_HDR_6 +- ; Mode Parameter Header SCSI$MPBD$S_MODE_PARAMETER +- ; Mode Parameter Block SCSI$ACP$S_AUDIO_CONTROL ; Audio Control Block MOVB #SCSI_RESPONSE_LENGTH,- ;S FF; Number of Mode Sense bytes to receive. SCSI$MPH6$B_BLOCK_LENGTH(R0) ;S FF; In response to MODE SENSE Command SEND_COMMAND_ORDERED ;S FF; Send the SCSI command. 10$: RSB ;S FF; Status checked by caller. ; Note: IO_BADPARM may be reached from several paths with varying stack ; contents (possibly due to nested JSBs) but the eventual RET will clean up ; the stack correctly in all cases. IO_BADPARM:: .GLOBAL_LABEL MOVZBL #SS$_BADPARAM,R0 ;S FF; Set bad parameter status BRW AUDIO_EXIT ;S FF; .PAGE .SBTTL AUDIO_MAP_PAGE - Lock down and double map user buffer ;+ ; AUDIO_MAP_PAGE: ; ; This routine locks the users buffer down, allocates the PTE's required to ; double map the user buffer and saves this mapping information in the IRP. ; The STARTIO routine will use the information in the IRP to move data to ; or from the users buffer. ; ; Context: ; ; User thread ; IPL = ASTDEL ; Locks - none ; ; Inputs: ; ; R0 - P0 address of buffer ; R1 - Byte count ; R3 - IRP ; Other registers have normal FDT context. ; ; Outputs: ; ; IRP$L_WIND - SVAPTE of mapped buffer. ; IRP$L_OBCNT - Number of PTE's allocated. ; IRP$L_SEGVBN - The SVA of the first PTE that maps the Sense buffer. ; ; R0 - Status ; R1,R2,R9-R11 - Destroyed ; All other registers preserved ;- .ENABLE LSB AUDIO_MAP_PAGE:: ;U A: Lock down and double map user buffer .JSB_ENTRY INPUT=,OUTPUT=,SCRATCH= MOVAL LOCK_ERROR,R2 ;U A; Set up error callback routine PUSHL R2 ;U A; Pass the error callback routine, PUSHL R1 ;U A; Pass length of transfer in bytes, PUSHL R0 ;U A; Pass pointer to I/O buffer, PUSHL R6 ;U A; Pass pointer to CCB, PUSHL R5 ;U A; Pass pointer to UCB, PUSHL R4 ;U A; Pass pointer to PCB, PUSHL R3 ;U A; Pass pointer to IRP. CALLS #7,G^EXE_STD$MODIFYLOCK ;U A; Call the routine. .BRANCH_LIKELY BLBS R0,5$ ;U A; Success: YES - Cont. FDT processing. RSB ;U A; Return. Callback & ABORTIO called. ;U A; Now preserve JSB interface outputs. 5$: MOVL IRP$L_SVAPTE(R3),R1 ;U A; Get SVA of start of buffer in R1. MOVL #5,R2 ;U A; Indicate modify function. ;+ ; To check to see how many PTE's to allocate. ; Do a sobgtr to loop through and copy all the pte's. ;- 10$: MOVL R1,R11 ;U A; Save the SVAPTE for the AUCB MOVL IRP$L_BCNT(R3),R9 ;U A; get requested transfer byte count ADDL2 IRP$L_BOFF(R3),R9 ;U A; Get offset plus count $BYTES_TO_PAGES - ;U A; Convert to page count SOURCE_BYTCNT=R9,- DEST_PAGCNT=R2,- ROUNDUP=YES ;U A; Round up JSB G^LDR$ALLOC_PT ;U A; Allocate the SPT's BLBC R0,MAP_ERROR ;U A; Can't map buffer 15$: MOVL R1,IRP$L_WIND(R3) ;U A; Save SVAPTE of system buffer MOVL R2,IRP$L_OBCNT(R3) ;U A; Save number of PTE's allocated ;+ ; Now convert the SPTE address to a SVA address so that the startio routine ; can access the AUCB via this address. ;- MOVL R1,R10 ;U A; Save the SVAPTE of S0 buffer SUBL2 G^MMG$GL_SPTBASE,R1 ;U A; Get offset into page table ASHL G^MMG$GL_PTE_OFFSET_TO_VA,R1,R9 ;U A; Save the S0 virtual address BISL2 IRP$L_BOFF(R3),R9 ;U A; Add in byte offset of AUCB BISL #VA$M_SYSTEM,R9 ;U A; Set 80000000 bit for system address MOVL R9,IRP$L_SEGVBN(R3) ;U A; SVA User buffer saved here ;+ ; Now loop through all of the PTE's required to map the buffer and copy the ; the PTE's from user page tables into the system page tables. ; ; Remember that R11 has the SVAPTE for the AUCB. ;- PUSHL R3 ;U A; Save R3 COPY_PTE: EVAX_LDQ R3,(R11) ;U A; Copy buffer's P0 PTE to R3 ASSUME PTE$M_VALID EQ 1 BLBC R3,60$ ;U A; Branch if PTE not valid EVAX_SRL R3,#PTE$V_PFN,R3 ;U A; Else, shift PFN to low-order bits BRW 65$ ;U A; Join common new PTE builder code 60$: CALL_PTETOPFN SAVE_R0R1=YES ;U A; Create vaild PTE from invalid PTE. 65$: EVAX_SLL R3,#PTE$V_PFN,R3 ;U A; Get PFN in correct position EVAX_OR R3,- ;U A; Set software PTE bits #,R3 EVAX_STQ R3,(R10) ;U A; Store the new (S0) PTE TBI_SINGLE R9 ;U A; Do TB invalidation ADDL G^MMG$GL_PAGE_SIZE,R9 ;U A; Point to next page ADDL #PTE$C_BYTES_PER_PTE,R10 ;U A; Point to next entry in S0 SPT ADDL #PTE$C_BYTES_PER_PTE,R11 ;U A; Point to next entry in P0 PTE SOBGTR R2,COPY_PTE ;U A; Copy all required PTE's POPL R3 ;U A; Restore R3 RSB ;U A; AUDIO_MAP_PAGE ;+ ; An error occured in CALL_MODIFYLOCK_ERR, 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: $DRIVER_ERRRTN_ENTRY PUSHR #^M ;U A; Save I/O status TSTL IRP$L_SEQNUM(R3) ;U A; Is this the IRPE? BGEQ 100$ ;U A; Nope, this is the original IRP MOVL IRP$L_SEQNUM(R3),R3 ;U A; Restore original IRP MOVL R3,@ERRARG$_STATUS+4(AP) ;U A; Original IRP, not IRPE, will be input to ABORTIO 100$: PUSHL R2 ;U A; Save R2 MOVL R3,R2 ;U A; For AUDIO_EXIT_FREE JSB AUDIO_EXIT_FREE ;U A; Free resources POPL R2 ;U A; Restore R2 POPR #^M ;U A; Restore I/O status RET ;U A; Return to Coroutine, fail I/O, ;U A; 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: ;U A; PUSHL R0 ;U A; Save register used PUSHL R3 ;U A; Save register used MOVL IRP$L_SVAPTE(R3),R3 ;U A; Get SVAPTE for IRPE(Dest) buffer BEQL 150$ ;U A; No buffer to unlock. MOVL R2,R1 ;U A; Get number of pages locked down. BEQL 150$ ;U A; No buffer to unlock CALL_UNLOCK ;U A; Unlock those pages. 150$: POPL R3 ;U A; Restore register used POPL R0 ;U A; Restore register used RSB ;U A; .DISABLE LSB .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 ; ; Context: ; ; User thread ; IPL = ASTDEL ; Locks - none ; ; Inputs: ; R2 - Original IRP Address ; ; Outputs: ; TBD ;- .ENABLE LSB AUDIO_EXIT_FREE:: ;U A; Audio exit resource deallocation .JSB_ENTRY INPUT=,SCRATCH=,PRESERVE= ;+ ; If there is an IRP extension in use then, unlock any locked pages. ;- MOVL R2,R9 ;U A; Save original IRP address 30$: BBC #IRP$V_EXTEND,IRP$L_STS(R2),- ;U A; Is this an extended IRP? REL_PTE ;U A; 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 ;U A; Get IRPE Address MOVL IRP$L_SVAPTE(R2),R3 ;U A; Get SVAPTE for IRPE(Dest) buffer BEQL NO_DEST_BUF ;U A; No destintation buffer to unlock. MOVL IRP$L_OBCNT(R2),R1 ;U A; Get number of pages locked down. BEQL NO_DEST_BUF ;U A; No SENSE buffer to unlock CALL_UNLOCK ;U A; 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 R9,R2 ;U A; "Restore" R2 MOVL IRP$L_EXTEND(R2),R2 ;U A; Get IRPE Address MOVL IRP$L_DIAGBUF(R2),R3 ;U A; Get SVAPTE for IRPE(SENSE) buffer BEQL REL_PTE ;U A; No SENSE buffer to unlock MOVL IRP$L_AST(R2),R1 ;U A; Get number of pages locked down. BEQL REL_PTE ;U A; No SENSE buffer to unlock CALL_UNLOCK ;U A; Unlock those pages. ;+ ; After unlocking Destination and Sense Buffer deallocated PTE's ;- REL_PTE: MOVL R9,R2 ;U A; Restore Original IRP Address BSBW AUDIO_FREE_PTES ;U A; R2 = IRP, free allocated PTE's ;+ ; If there was an IRPE, now deallocate it before starting IOPOST. ;- BBC #IRP$V_EXTEND,IRP$L_STS(R2),- ;U A; Is this an extended IRP? DONE_FREE ;U A; If not exit, continue.. BSBW DEALLOC_IRPE ;U A; Deallocated IRPE, if the exist. ;+ ; Later Start Autosense if check condition. ;- DONE_FREE: RSB ;U A; AUDIO_EXIT_FREE .DISABLE LSB .PAGE .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. ; ; Context: ; ; User thread ; IPL = ASTDEL ; Locks - none ; ; 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 ;- .ENABLE LSB AUDIO_FREE_PTES:: ;U A; Free any allocated PTE's .JSB_ENTRY INPUT=,OUTPUT=,SCRATCH=,PRESERVE= MOVL R2,R10 ;U A; Save IRP address MOVL IRP$L_WIND(R2),R1 ;U A; Get Address of PTE's Allocated BEQL OTHER_PTE ;U A; If none allocated, check for others MOVL IRP$L_OBCNT(R2),R0 ;U A; Get number of PTE's. BEQL OTHER_PTE ;U A; If none allocated, check for others ;+ ; Before deallocating AUCB PTE's they must be zeroed. ;- MOVL IRP$L_SEGVBN(R2),R9 ;U A; Get SVA of AUCB as saved by FDT call AUCB_PTE: ;U A; Free any allocated PTE's EVAX_STQ R31,(R1) ;U A; Clear the PTE TBI_SINGLE R9 ;U A; Invalidate Single TBI ADDL G^MMG$GL_PAGE_SIZE,R9 ;U A; Point to next page ADDL #PTE$C_BYTES_PER_PTE,R1 ;U A; Point to next entry in P0 PTE SOBGTR R0,AUCB_PTE ;U A; Loop through all PTE's MOVL R10,R2 ;U A; "Restore" R2 MOVL IRP$L_WIND(R2),R1 ;U A; Get Address of PTE's Allocated MOVL IRP$L_OBCNT(R2),R2 ;U A; Get number of PTE's. JSB G^LDR$DEALLOC_PT ;U A; Free the allocated PTE's BLBS R0,OTHER_PTE ;U A; If success continue, otherwise BUG_CHECK INCONSTATE,FATAL ;U A; 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 R10,R2 ;U A; "Restore" R2 BBC #IRP$V_EXTEND,IRP$L_STS(R2),- ;U A; Is this an extended IRP? FREE_DONE ;U A; If not exit MOVL IRP$L_EXTEND(R2),R2 ;U A; Get IRPE Address MOVL IRP$L_PID(R2),R1 ;U A; Get Address of PTE's allocated BEQL DEST_PTE ;U A; If none allocated, check for others MOVL IRP$L_AST(R2),R0 ;U A; Get number of PTE's. BEQL DEST_PTE ;U A; If none allocated, check for others ;+ ; Before deallocating Sense buffer PTE's they must be zeroed and invalidated ;- MOVL IRP$L_ASTPRM(R2),R9 ;U A; Get SVA of Sense Buf from FDT call SENSE_PTE: EVAX_STQ R31,(R1) ;U A; Clear the PTE TBI_SINGLE R9 ;U A; Invalidate Single TBI ADDL G^MMG$GL_PAGE_SIZE,R9 ;U A; Point to next page ADDL #PTE$C_BYTES_PER_PTE,R1 ;U A; Point to next PTE SOBGTR R0,SENSE_PTE ;U A; Loop through all PTE's MOVL R10,R2 ;U A; "Restore" R2 MOVL IRP$L_EXTEND(R2),R2 ;U A; Get IRPE Address MOVL IRP$L_PID(R2),R1 ;U A; Get Address of PTE's Allocated MOVL IRP$L_AST(R2),R2 ;U A; Get number of PTE's. JSB G^LDR$DEALLOC_PT ;U A; Free the allocated PTE's BLBS R0,DEST_PTE ;U A; If success continue, otherwise BUG_CHECK INCONSTATE,FATAL ;U A; BUGCHECK, this can never happen... ;+ ; Check for PTE's allocated for the Destination buffer. If allocated then ; deallocate them. ;- DEST_PTE: MOVL R10,R2 ;U A; "Restore" R2 MOVL IRP$L_EXTEND(R2),R2 ;U A; Get IRPE Address MOVL IRP$L_WIND(R2),R1 ;U A; Get Address of PTE's Allocated BEQL FREE_DONE ;U A; If none allocated, check for others MOVL IRP$L_OBCNT(R2),R0 ;U A; Get number of PTE's. BEQL FREE_DONE ;U A; If none allocated, check for others ;+ ; Before deallocating Destination buffer PTE's they must be zeroed. ;- MOVL IRP$L_SEGVBN(R2),R9 ;U A; Get SVA of Dest Buf from FDT call 50$: EVAX_STQ R31,(R1) ;U A; Clear the PTE TBI_SINGLE R9 ;U A; Invalidate Single TBI ADDL G^MMG$GL_PAGE_SIZE,R9 ;U A; Point to next page ADDL #PTE$C_BYTES_PER_PTE,R1 ;U A; Point to next PTE SOBGTR R0,50$ ;U A; Loop through all PTE's MOVL R10,R2 ;U A; "Restore" R2 MOVL IRP$L_EXTEND(R2),R2 ;U A; Get IRPE Address MOVL IRP$L_WIND(R2),R1 ;U A; Get Address of PTE's Allocated MOVL IRP$L_OBCNT(R2),R2 ;U A; Get number of PTE's. JSB G^LDR$DEALLOC_PT ;U A; Free the allocated PTE's BLBS R0,FREE_DONE ;U A; If success continue, otherwise BUG_CHECK INCONSTATE,FATAL ;U A; BUGCHECK, this can never happen... FREE_DONE: MOVL #SS$_NORMAL,R0 ;U A; Assume success ERROR_DONE: MOVL R10,R2 ;U A; Restore IRP address RSB ;U A; .DISABLE LSB .PAGE .SBTTL AUDIO_EXIT - Audio Function Completion Code ;+ ; AUDIO_EXIT - ; ; All audio functions that are not aborted during FDT processing exit the ; driver by executing this code. After a command has been sent, error ; recovery has been executed, SCSI status copied to the AUCB then deallocate ; the SCDRP and any other resources allocated for this request. The audio exit ; code is fairly complex due to the fact that we are double mapping and copying ; between three distinct user buffers. ; ; The original IRP (IRP pointed to by SCDRP$L_IRP) contains the mapping ; information for the AUCB. If a user destination or sense buffer is required ; then 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. ; ; Context: ; ; SCDRP thread ; IPL = Fork ; Fork lock held ; ; Inputs: ; R5 = SCDRP for this request. ; R3 = UCB ; R0 = VMS return status code ; ; ; Outputs: ; AUCB = User Audio Control Block ; CD_COMMAND_STATUS = VMS O.S. Return status ; CD_SCSI_STATUS = SCSI command status and Sense Key ; CD_SENSE_ADDR = Sense data buffer ; CD_SENSE_TRANS_CNT = Sense data transfer count ; CD_DEST_BUF_TRANS_CNT = Number of bytes in destination buf ;- .ENABLE LSB AUDIO_EXIT: ;S FF; Audio Function Completion Code CLEANUP_CMD ;S FF; Cleanup from the SCSI command AUDIO_EXIT_NO_CMD: ;+ ; Now update the AUCB with the VMS Status, SCSI Status and Transfer Count. ;- PUSHR #^M ;S FF; Save some registers MOVL SCDRP$L_MEDIA(R5),R7 ;S FF; Get copy of AUCB address from SCDRP. MOVL SCDRP$L_TRANS_CNT(R5),- ;S FF; Get Transfer Count in Destination CD_DEST_BUF_TRANS_CNT(R7) ;S FF; XFER count field in the AUCB. TSTL SCDRP$L_STS_PTR(R5) ;S FF; Test for existance of status buffer. BEQL 10$ ;S FF; If no SCSI status buffer skip this.. MOVZBL @SCDRP$L_STS_PTR(R5),- ;S FF; Copy SCSI Status to AUCB. CD_SCSI_STATUS(R7) ;S FF; 10$: MOVZWL R0,CD_COMMAND_STATUS(R7) ;S FF; Copy VMS status to AUCB. ;+ ; If there was a check condition as the result of an audio command, copy ; the sense key and additional sense data to the AUCB from the UCB. ; During SEND_COMMAND, if a check condition occurs a request sense is ; issued to the target and the sense data is copied to the UCB. ;- ASSUME SCSI$C_GOOD EQ 0 TSTB CD_SCSI_STATUS(R7) ;S FF; Was status success? BLEQ 50$ ;S FF; If yes continue, else get sense data MOVZBW SCDRP$B_SENSE_KEY(R5),- ;S FF; Copy the Sense key to the AUCB. CD_SCSI_STATUS+2(R7) ;S FF; MOVL SCDRP$L_IRP(R5),R2 ;S FF; Get original IRP MOVL IRP$L_EXTEND(R2),R2 ;S FF; Get any IRP extension BEQL 50$ ;S FF; No, IRPE exit TSTL IRP$L_PID(R2) ;S FF; Test for Sense Buffer existance BEQL 50$ ;S FF; In no buffer don't copy sense data. PUSHR #^M MOVAL UCB$R_SENSE_INFO(R3),R0 ;S FF; Get address of additional info field MOVZBL UCB$B_SENSE_LEN(R3),R1 ;S FF; Get count of number of sense bytes MOVL CD_SENSE_CNT(R7),R5 ;S FF; Get size of user sense buffer MINUM R1,R5 ;S FF; Minimize between these two values MOVL R1,CD_SENSE_TRANS_CNT(R7) ;S FF; Return Count of sense bytes to AUCB MOVC3 R1,(R0),@IRP$L_SEGVBN(R2) ;S FF; Copy SENSE Data to user buffer ;S FF; SVA of SENSE buffer in SEGVBN. POPR #^M 50$: POPR #^M ;S FF; Restore saved registers ;+ ; Unlock any locked pages and free any allocated PTE's, then clear any fields ; in the IRP that might confuse IOPOST and return status in R0 and R1, which ; will be copied to the IOSB. ;- AUDIO_BAD_CMD_EXIT: PUSHR #^M MOVL SCDRP$L_IRP(R5),R2 ;S FF; Get original IRP address BSBW AUDIO_EXIT_FREE ;S FF; Unlock buffers, dealloc PTE's and IRPE MOVL SCDRP$L_IRP(R5),R2 ;S FF; Get original IRP address CLRL IRP$L_MEDIA(R2) ;S FF; Clear for IOPOST CLRL IRP$L_OBCNT(R2) ;S FF; ... CLRL IRP$L_SEGVBN(R2) ;S FF; ... POPR #^M ;S FF; Restore reg's CLR_SINGLE AUDIO,R3 ;S FF; Audio request is complete CLRL R1 ;S FF; Clear R1, no additional info. BRW COMPLETE_IO ;S FF; Complete this I/O function .DISABLE LSB .SBTTL READ_CD_MCN - reads CD-ROM Sub-Channel to get the Media Cat. # ;+ ; ; This routine sends a READ SUB-CHANNEL command requesting the 02h Data ; format code, Media Catalog Number, be returned for a CD-ROM media. ; This command causes the CD to stop playing audio, therefore it is only ; issued in the following cases (all indicated by the UCB$M_CD_VALID flag ; being clear): ; ; 1) When the "mount" of the device occurs (called from PACKACK ; routine). ; ; 2) If the CD_READ_SUB format 2 command is issued and a UNIT ATTENTION ; Sense key indicating a media change has occured was returned for ; a previous SCSI command. ; ; Context: ; ; SCDRP thread ; IPL = Fork ; Fork lock held ; ; Inputs: ; ; R3 - UCB address ; R4 - PDT address ; R5 - SCDRP address ; ; Outputs: ; ; R1,R2 - Destroyed ; R0 - Status ; ; UCB$B_MCN_DATA - filled in ; UCB$M_CD_VALID - set to indicate MCN data valid if command succeeded ; ;- READ_CD_MCN:: ;S FF; Read CD-ROM Sub-Channel .JSB_ENTRY INPUT=,OUTPUT=,SCRATCH= BICL #UCB$M_CD_VALID,- ;S FF; Indicate stored sub-channel data for UCB$L_DK_FLAGS(R3) ;S FF; CD-ROM is invalid. MOVAL CMD_CD_READ_SUB,R2 ;S FF; Address of READ SUB command SETUP_CMD ;S FF; Set Command ADDL3 #4,SCDRP$L_CMD_PTR(R5),R0 ;S FF; Get address of SCSI CDB (Command). ;S FF; The CDB doesn't start at 0, but 4. MOVB #SCSI$SUB$C_MCN,- ;S FF; Write Sub Channel Data Format CMD_CD_READ_SUB$B_SUB_DATA_FMT(R0); S FF; MCN = Media Catalog Number (UPC/Bar Code) SEND_COMMAND_ORDERED ;S FF; Send the SCSI command BLBC R0,20$ ;S FF; Branch on error ;+ ; Copy Subchannel format 2 data to UCB ;- PUSHR #^M MOVL #SCSI$SUBQ$C_MCN_LENGTH,R2 ;S FF; Get size of destination buffer. MINUM SCDRP$L_TRANS_CNT(R5),R2 ;S FF; Minimize between these two values. MOVC3 SCDRP$L_TRANS_CNT(R5),- ;S FF; Copy the number of bytes received @SCDRP$L_SVA_USER(R5),- ;S FF; Copy from Nonpaged, to the UCB$B_MCN_SCDATA(R3) ;S FF; start of the area in the UCB POPR #^M BISL #UCB$M_CD_VALID,- ;S FF; Indicate stored sub-channel data for UCB$L_DK_FLAGS(R3) ;S FF; CD-ROM is valid. MOVL #SS$_NORMAL,R0 ;S FF; return success 20$: CLEANUP_CMD ;S FF; cleanup from SCSI command RSB ;S FF; .PAGE .SBTTL READ_SUB_CHANNEL ;+ ; ; This routine sends a READ SUB-CHANNEL command requesting the 01h Data ; ; Context: ; ; SCDRP thread ; IPL = Fork ; Fork lock held ; ; Inputs: ; ; R3 - UCB address ; R4 - PDT address ; R5 - SCDRP address ; ; Outputs: ; ; R1,R2 - Destroyed ; R0 - Status ; ;- READ_CD_SUBQ:: ;S FF; READ SUB-CHANNEL Data .JSB_ENTRY INPUT=,OUTPUT=,SCRATCH= ;+ ; Read format #1 sub-channel, The Q-Sub channel with current location info. ;- MOVAL CMD_CD_READ_SUB,R2 ;S FF; Address of READ SUB command SETUP_CMD ;S FF; Set Command ADDL3 #4,SCDRP$L_CMD_PTR(R5),R0 ;S FF; Get address of SCSI CDB (Command). ;S FF; The CDB doesn't start at 0, but 4. MOVL SCDRP$L_MEDIA(R5),R1 ;S FF; Restore address of AUCB. MOVB #SCSI$SUB$C_CD_ROM_POSITION,- ;S FF; Write Sub Channel Data Format CMD_CD_READ_SUB$B_SUB_DATA_FMT(R0) ;S FF; CD-ROM Current Position CMPB CD_ARG1+0(R1),#1 ;S FF; If greater than 1 BGTRU IO_BADPARM ;S FF; error, which branches back to STARTIO ASHL #1,CD_ARG1+0(R1),R1 ;S FF; Get LBA/MSF bit in right bit position BISB R1,CMD_CD_READ_SUB$R_FLAGS(R0) ;S FF; Select MSF or LBA address format. ; Note: this path may branch back to STARTIO, which will ; either RET or RET under JSB. ; SEND_COMMAND_ORDERED ;S FF; Send the SCSI command RSB ;S FF; .PAGE .SBTTL FETCH_MCN ;+ ; This routine gets the SUB-CHANNEL format 2 data, by issuing a SCCI ; READ SUB-CHANNEL command requesting the 02h Data if the data in the ; UCB is invalid. ; ; Context: ; ; SCDRP thread ; IPL = Fork ; Fork lock held ; ; Inputs: ; ; R3 - UCB address ; R4 - PDT address ; R5 - SCDRP address ; ; Outputs: ; ; R1,R2 - Destroyed ; R0 - Status ; ;- FETCH_MCN:: ;S FF; Fetch SUB-Channel MCN data .JSB_ENTRY INPUT=,OUTPUT=,SCRATCH= ;+ ; We issue a extra read_sub command here to guarantee that the CD has not been ; changed. This command will clear the CD_VALID flag in the UCB if the a unit ; attention occurs during this commmand. ;- BSBW READ_CD_SUBQ ;S FF; Get format 1 data. CLEANUP_CMD ;S FF; toss data and cleanup BLBC R0,30$ ;S FF; On error exit MOVAB MCN_DEF_STATUS,- ;S FF; Assume no SCSI command and setup SCDRP$L_STS_PTR(R5) ;S FF; status buffer in SCDRP here. MOVB #SCSI$C_GOOD,MCN_DEF_STATUS ;S FF; Assume status of sucesss BITL #UCB$M_CD_VALID,- ;S FF; Is the stored sub-channel data for UCB$L_DK_FLAGS(R3) ;S FF; CD-ROM is valid? BNEQ 30$ ;S FF; If NEQ yes just return BSBW READ_CD_MCN ;S FF; read format #2 data to UCB 30$: RSB ;S FF; .PAGE .SBTTL + .SBTTL + SCSI COMMAND EXECUTION ROUTINES .SBTTL + .SBTTL INQUIRY - Send an inquiry command ;+ ; INQUIRY ; ; This routine sends an inquiry command to the target. At least 36 bytes of ; inquiry data are expected to be returned. The peripheral device type field ; is check to ensure the target is a disk-like device. The product ID field ; is used to determine the device type. In addition, several sanity checks are ; made on the inquiry data. ; ; Context: ; ; SCDRP thread ; IPL = Fork ; Fork lock held ; ; Inputs: ; ; R3 - UCB address ; R4 - PDT address ; R5 - SCDRP address ; ; Outputs: ; ; R0 - Status ; R1,R2 - Destroyed ;- INQUIRY:: ;S FF; Send an inquiry command .JSB_ENTRY INPUT=,OUTPUT=,SCRATCH=,PRESERVE= MOVAL CMD_INQUIRY,R2 ;S FF; Address of INQUIRY command SETUP_CMD ;S FF; Perform setup for SCSI command MOVL #SCDRP$K_QCHAR_NOT_QUEUED,- ;S FF; Send this as an ordered command. SCDRP$IS_QUEUE_CHAR(R5) PUSHL R5 ;S FF; PUSHL R4 ;S FF; PUSHL R3 ;S FF; CALLS #3,SEND_COMMAND ;S FF; Send the SCSI command BLBC R0,30$ ;S FF; Branch on error CMPL SCDRP$L_TRANS_CNT(R5),- ;S FF; Sufficient inquiry data returned? #SCSI$INQ$S_INQUIRY_DATA ;S FF; BLSSU 40$ ;S FF; Branch if not MOVL SCDRP$L_SVA_USER(R5),R0 ;S FF; Get address of inquiry data ASSUME SCSI$INQ$S_PRODUCT_REVISION EQ 4 MOVL SCSI$INQ$B_PRODUCT_REVISION(R0),-;S FF; Save hardware revision level UCB$L_HW_REV(R3) ASSUME UCB$B_DEVCLASS+1 EQ UCB$B_DEVTYPE CLRW UCB$B_DEVCLASS(R3) ;S FF; Assume unknown device class and type BICL2 #,- ;S FF; type based on media - make sure UCB$L_DK_FLAGS(R3) ;S FF; that only one of these gets set EXTZV #SCSI$INQ$V_QUALIFIER,- ;S FF; Get peripheral qualifier #SCSI$INQ$S_QUALIFIER,- ;S FF; SCSI$INQ$R_PERIPHERAL(R0),R1 ;S FF; BNEQ 40$ ;S FF; If not 0, no device here EXTZV #SCSI$INQ$V_DEVICE_TYPE,- ;S FF; Get peripheral device type #SCSI$INQ$S_DEVICE_TYPE,- SCSI$INQ$R_PERIPHERAL(R0),R1 CMPB R1,#SCSI$C_DISK ;S FF; Is this a SCSI disk device? BEQL 10$ ;S FF; Branch if so CMPB R1,#SCSI$C_WORM ;S FF; Or a write-once read-multiple device? BNEQ 5$ ;S FF; Branch if not BISL2 #UCB$M_WORM,UCB$L_DK_FLAGS(R3) ;S FF; Set a bit to indicate that it is a WORM BRB 10$ ;S FF; Branch to continue disk device code 5$: CMPB R1,#SCSI$C_OPTICAL ;S FF; Or a optical device? BNEQ 7$ ;S FF; Branch if not BISL2 #UCB$M_OPTICAL,- ;S FF; Set a bit to indicate that it is an UCB$L_DK_FLAGS(R3) ;S FF; optical device BRB 10$ ;S FF; Branch to continue disk device code 7$: CMPB R1,#SCSI$C_CDROM ;S FF; Or a CDROM-like device BNEQ 40$ ;S FF; Branch if not, illegal device type BISL #UCB$M_CDROM,- ;S FF; Indicate CDROM-like device UCB$L_DK_FLAGS(R3) ;S FF; BISL2 #UCB$M_HWL,- ;S FF; This a read-only device so set UCB$L_DK_FLAGS(R3) ;S FF; the hardware write-lock bit BBSS #DEV$V_SWL,- ;S FF; This a read-only device so set UCB$L_DEVCHAR(R3),10$ ;S FF; the software write-lock bit 10$: MOVB #DC$_DISK,- ;S FF; Indicate that this a disk device UCB$B_DEVCLASS(R3) ;S FF; BBSS #UCB$V_REMOVABLE,- ;S FF; Assume drive has removable media UCB$L_DK_FLAGS(R3),15$ ;S FF; ASSUME SCSI$INQ$M_RMB EQ <^X80> 15$: TSTB SCSI$INQ$R_DEVICE_TYPE_MOD(R0) ;S FF; Is this a removable media device? BLSS 20$ ;S FF; Branch if so BBCC #UCB$V_REMOVABLE,- ;S FF; Clear the removable media bit UCB$L_DK_FLAGS(R3),20$ ;S FF; in the UCB 20$: EXTZV #SCSI$INQ$V_ANSI_VERSION,- ;S FF; Get response data format #SCSI$INQ$S_ANSI_VERSION,- ;S FF; SCSI$INQ$R_VERSION(R0),R1 ;S FF; MOVB R1, UCB$B_SCSI_VERSION(R3) ;S FF; Save for future checks CMPB R1,#SCSI$C_ANSI_SCSI_1 ;S FF; Does drive conform to CCS? BEQL 25$ ;S FF; Branch if so CMPB R1,#SCSI$C_ANSI_SCSI_2 ;S FF; Does drive conform to SCSI-2? BNEQ 40$ ;S FF; Branch if not 25$: CMPB SCSI$INQ$B_ADD_LENGTH(R0),#^X1F ;S FF; Valid additional length field? BLSSU 40$ ;S FF; Branch if not BSBW GET_DEVICE_TYPE ;S FF; Determine device type from Vendor MOVZWL #SS$_NORMAL,R0 ;S FF; Set success status 30$: CLEANUP_CMD ;S FF; Cleanup from the SCSI command RSB ;S FF; Return to caller 40$: LOG_ERROR - ;S FF; Log an invalid inquiry data error TYPE=INV_INQUIRY_DATA,- VMS_STATUS=#SS$_DRVERR,- UCB=R3,- SCDRP=R5 MOVZWL #SS$_DRVERR,R0 ;S FF; Set bad status BRB 30$ ;S FF; Use common exit .PAGE .SBTTL TEST_UNIT_READY - Send a test unit ready command ;+ ; TEST_UNIT_READY ; ; This routine sends a test unit ready command to the target to determine ; whether the device is spun up and ready. ; ; Context: ; ; SCDRP thread ; IPL = Fork ; Fork lock held ; ; Inputs: ; ; R3 - UCB address ; R4 - PDT address ; R5 - SCDRP address ; ; Outputs: ; ; R0 - Status ; R1,R2 - Destroyed ; All other registers preserved ;- TEST_UNIT_READY:: ;S FF; Send a test unit ready command .JSB_ENTRY INPUT=,OUTPUT=,SCRATCH= BISB #UCB$M_SPINUP_INPROG,- ;S FF; Set spinup-in-progress UCB$L_DK_FLAGS(R3) ;S FF; MOVAL CMD_TEST_UNIT_READY,R2 ;S FF; Address of TEST UNIT READY command SETUP_CMD ;S FF; Perform setup for SCSI command SEND_COMMAND_ORDERED ;S FF; Send non-queued TUR CLEANUP_CMD ;S FF; Cleanup from the SCSI command BICB #UCB$M_SPINUP_INPROG,- ;S FF; Clear spinup-in-progress UCB$L_DK_FLAGS(R3) ;S FF; RSB ;S FF; .SBTTL PROCESS_MODE_INFO - Send a mode sense command ;+ ; PROCESS_MODE_INFO ; ; This routine processes the mode pages for this target. It allocates pool ; to hold a copy of field descriptors. There is one field descriptor for ; each mode page field of interest. The local copy of the descriptors ; will be used to receive current values as well as to specify MODE SELECT ; values for those fields which need to be read and/or written. One call ; to the routine DO_MODE_PAGE will be made for each mode page we care about; ; DO_MODE_PAGE does a MODE SENSE current and, if appropriate, a MODE SENSE ; changeable and MODE SELECT for the page specified. ; ; Context: ; ; SCDRP thread ; IPL = Fork ; Fork lock held ; ; Inputs: ; ; R3 - UCB address ; R4 - PDT address ; R5 - SCDRP address ; ; Outputs: ; ; R0 - Status ; R1,R2 - Destroyed ; All other registers preserved ;- PROCESS_MODE_INFO:: .JSB_ENTRY INPUT=,OUTPUT=,SCRATCH=,PRESERVE= ASSUME UCB$B_SECTORS+1 EQ UCB$B_TRACKS ;*GWW MOVW #^X604,UCB$B_SECTORS(R3) ;*GWW Fill in dummy values for ; sector and track fields ; Allocate a local copy of the mode page descriptors on the stack SUBL3 #DESCRIPTOR_SIZE,SP,R7 ;S FF; Size of descriptor array BICL #7,R7 ;S FF; Quadword align SUBL #DESCRIPTOR_SIZE+8,SP ;S FF; Account for desc + unalignment PUSHR #^M MOVC5 #0,.,#0,#DESCRIPTOR_SIZE,(R7) ;S FF; Initialize the desc area MOVC3 #DESCRIPTOR_SIZE,- ;S FF; Copy the descriptors DESCRIPTOR_BASE,(R7) POPR #^M INSV UCB$B_SCSI_VERSION(R3),- ;S FF; Determine SCSI1 vs SCSI2 for #SCDRP$V_FLAG_MODE_SENSE,- ;S FF; later use by do_mode_page #SCDRP$S_FLAG_MODE_SENSE,- SCDRP$L_SCSI_FLAGS(R5) ; Read the error recovery page for two reasons: first, to establish a standard for ; whether or not the device supports 10-byte Mode Sense commands and second, to ; fetch the block descriptor, which contains the block length; this is necessary ; to make sure that CD-ROMs are set to a 512-byte block size. LOAD_TENBYTE ;S FF; Initialize the SCDRP with ;S FF; the current UCB.TENBYTE bit PUSHL #SCSI$PGCD$C_READ_WRITE_ERR ;S FF; Page code = error recovery page PUSHAL ERR_PAGE_DESC(R7) ;S FF; Address of descriptors PUSHL R5 ;S FF; SCDRP address CALLS #3,DO_MODE_PAGE ;S FF; Process the page BLBC R0, 18$ ;S FF; Handle failure STORE_TENBYTE ;S FF; Save SCDRP.TENBYTE bit in UCB BBC #UCB$V_CDROM, - ;S FF; If not CDROM, tben skip the UCB$L_DK_FLAGS(R3),200$ ;S FF; block size check CMPW #512,UCB$W_BLOCK_SIZE(R3) ;S FF; Is this already size 512? BEQL 200$ ;S FF; Yes, so don't change it BGTR 18$ ;S FF; Can't handle page size < 512 MOVW #512,UCB$W_BLOCK_SIZE(R3) ;S FF; new Set block size in UCB BSBW READ_CAPACITY ;S FF; Re invoke READ CAPACITY command BLBS R0, 200$ ;S FF; Success, continue 18$: MOVAL ERR_PAGE_DESC(R7),R8 ;S FF; Address of descriptor MOVL #SIZEOF_ERR,R6 ;S FF; Size BRW 95$ ;S FF; Log error ; Control Mode page (Queuing parameters) 200$: IF_NOT_CMDQ 20$ ;S FF; If device doesn't do queuing, ignore ;S FF; control mode page PUSHL #SCSI$PGCD$C_CONTROL_MODE ;S FF; Page code = control mode page PUSHAL CONTROL_MODE_PAGE_DESC(R7) ;S FF; Address of descriptors PUSHL R5 ;S FF; SCDRP address CALLS #3,DO_MODE_PAGE ;S FF; Process the page BLBS R0,20$ ;S FFL If successful, do next page CMPL R0,#SS$_CANCEL ;S FF; If cancel status BEQL 80$ ;S FF; Then leave now LOAD_TENBYTE ;S FF; Refresh SCDRP bit BICL #UCB$M_CMDQ,- ;S FF; Else disable TCQ UCB$L_DK_FLAGS(R3) ; Error recovery parameters 20$: CMPW UCB$W_BLOCK_SIZE(R3),#512 ;S FF; Is block size 512? BNEQ 28$ ;S FF; Branch if not CMPB UCB$B_SCSI_VERSION(R3),#2 ;S FF; Is this SCSI2? BLSS 26$ ;S FF; Branch if not IF_NOT_CMDQ 24$ ;S FF; Branch if not TCQ ; Case 1: SCSI2 device that supports TCQ MOVL #SIZEOF_SCSI2_TCQ,R6 ;S FF; Size in bytes of descriptors MOVAL SCSI2_TCQ_ERR_PAGE_DESC(R7),R8 ;S FF; Address of err rec descriptors MOVL #SCSI2_TCQ_ERR_DEVSPC_DESC,R9 ;S FF; Offset to dev specific param MOVL #SCSI2_TCQ_ERR_MEDTYP_DESC,R10 ;S FF; Offset to medium type BRW 30$ ;S FF; Join common code ; Case 2: SCSI2 device that does not support TCQ 24$: MOVL #SIZEOF_SCSI2_NOTCQ,R6 ;S FF; Size in bytes of descriptors MOVAL SCSI2_NOTCQ_ERR_PAGE_DESC(R7),R8;S FF; Address of err rec descriptors MOVL #SCSI2_NOTCQ_ERR_DEVSPC_DESC,R9 ;S FF; Offset to dev specific param MOVL #SCSI2_NOTCQ_ERR_MEDTYP_DESC,R10;S FF; Offset to medium type BRW 30$ ;S FF; Join common code ; Case 3: SCSI1 device (by definition, does not support TCQ) 26$: MOVL #SIZEOF_SCSI1,R6 ;S FF; Size in bytes of descriptors MOVAL SCSI1_ERR_PAGE_DESC(R7),R8 ;S FF; Address of err rec descriptors MOVL #SCSI1_ERR_DEVSPC_DESC,R9 ;S FF; Offset to dev specific param MOVL #SCSI1_ERR_MEDTYP_DESC,R10 ;S FF; Offset to medium type BRW 30$ ;S FF; Join common code ; Case 4: Device does not have block size = 512 28$: MOVL #SIZEOF_NON512,R6 ;S FF; Size in bytes of descriptors MOVAL NON512_ERR_PAGE_DESC(R7),R8 ;S FF; Address of err rec descriptors MOVL #NON512_ERR_DEVSPC_DESC,R9 ;S FF; Offset to dev specific param MOVL #NON512_ERR_MEDTYP_DESC,R10 ;S FF; Offset to medium type ;S FF; Note: Don't care about WCE ; Common code to process Read Write Error Recovery Page 30$: PUSHL #SCSI$PGCD$C_READ_WRITE_ERR ;S FF; Page code = error recovery page PUSHL R8 ;S FF; Address of descriptors PUSHL R5 ;S FF; SCDRP address CALLS #3,DO_MODE_PAGE ;S FF; Process the page BLBS R0,32$ ;S FF; Continue if success CMPL R0,#SS$_CANCEL ;S FF; If cancel status BEQL 80$ ;S FF; Then leave now IF_NOT_CMDQ 95$ ;S FF; Fatal error if noTCQ LOAD_TENBYTE ;S FF; Refresh SCDRP bit BICL #UCB$M_CMDQ,- ;S FF; Else disable TCQ UCB$L_DK_FLAGS(R3) BRW 24$ ;S FF; Retry with noTCQ desc 32$: ; Since the error recovery page is normally returned, fetch and process the ; the relevant mode page header parameters (namely, the device specific ; parameter and the medium type) as part of the error recovery page processing. ADDL2 R7,R9 ;S FF; Get address of dev specific descriptor ADDL2 R7,R10 ;S FF; Get address of medium type descriptor BBC #7,MODE_DESC$L_ACTUAL(R9),5$ ;S FF; Is write-protect bit clear? 140$: BISL2 #UCB$M_HWL,- ;S FF; This a read-only device so set UCB$L_DK_FLAGS(R3) ;S FF; the hardware write-lock bit BBSS #DEV$V_SWL,- ;S FF; Set the software write-lock bit to UCB$L_DEVCHAR(R3),5$ ;S FF; indicate device is write-locked 5$: BICL2 #,- ;S FF; Assume FORMAT unsupported UCB$L_DK_FLAGS(R3) BBS #UCB$V_WORM,- ;S FF; If this is a WORM device we need to UCB$L_DK_FLAGS(R3),7$ ;S FF; protect it from the file system. BBC #UCB$V_OPTICAL,- ;S FF; If it's not WORM, it might be optical UCB$L_DK_FLAGS(R3),67$ ;S FF; with WORM media - check the media CMPB MODE_DESC$L_ACTUAL(R10),- ;S FF; type code in the mode sense data. #SCSI$C_WORM ;S FF; BNEQ 67$ ;S FF; branch past *WL bit sets if not. 7$: BISL2 #UCB$M_HWL,- ;S FF; Mark the device as Hardware Write UCB$L_DK_FLAGS(R3) ;S FF; Locked. BISL2 #DEV$M_SWL,- ;S FF; Mark the device as Software Write UCB$L_DEVCHAR(R3) ;S FF; Locked. 67$: ;S FF; Pre-Optical code path. ;S FF; Determine diskette type in RX26 drive ; CLRL R2 ;S FF; Assume unknown or default medium CMPB #DT$_RX26,UCB$B_DEVTYPE(R3) ;S FF; Only RX26 reports diskette type BNEQ 506$ ;S FF; Not RX26, skip this check ; MOVZBL #MODE_SENSE_MEDIA_TYPE_MAX,R2 ;S FF; Start checking for match with ;S FF; last cell in table 502$: CMPB MODE_DESC$L_ACTUAL(R10),- ;S FF; Check against table entry MODE_SENSE_MEDIA_TYPE_TABLE[R2] BEQL 506$ ;S FF; Matches, save index SOBGTR R2,502$ ;S FF; No match, back up in table ; 506$: INSV R2,#UCB$V_FLOPPY_MEDIA,- ;S FF; remember medium type #UCB$S_FLOPPY_MEDIA,- ;S FF; UCB$L_DK_FLAGS(R3) ;S FF; ; Format parameters 40$: PUSHL #SCSI$PGCD$C_FORMAT_DEVICE ;S FF; Page code = format device page PUSHAL FORMAT_DEVICE_PAGE_DESC(R7) ;S FF; Address of descriptors PUSHL R5 ;S FF; SCDRP address CALLS #3,DO_MODE_PAGE ;S FF; Process the page BLBS R0,41$ ;S FF; If success, continue CMPL R0,#SS$_CANCEL ;S FF; If cancel status BEQL 80$ ;S FF; Then leave now LOAD_TENBYTE ;S FF; Refresh SCDRP bit CMPL #SS$_NOSUCHSEC,R0 ;S FF; Illegal page, special success BEQL 50$ ;S FF; Just do next page CLRB UCB$B_SECTORS(R3) ;S FF; Invalidate sector field MOVAL FORMAT_DEVICE_PAGE_DESC(R7),R8 ;S FF; Address of descriptor MOVL #SIZEOF_FORMAT,R6 ;S FF; Size BRW 95$ ;S FF; Log error 41$: MOVB - ;S FF; Save # of sectors per track in UCB (R7),- UCB$B_SECTORS(R3) ;S FF; ; Geometry parameters for rigid disks 50$: PUSHL #SCSI$PGCD$C_RIGID_DISK ;S FF; Page code = rigid disk params PUSHAL RIGID_DISK_PAGE_DESC(R7) ;S FF; Address of descriptors PUSHL R5 ;S FF; SCDRP address CALLS #3,DO_MODE_PAGE ;S FF; Process the page BLBS R0,51$ ;S FF; If success, continue CMPL R0,#SS$_CANCEL ;S FF; If cancel status BEQL 80$ ;S FF; Then leave now LOAD_TENBYTE ;S FF; Refresh SCDRP bit CMPL #SS$_NOSUCHSEC,R0 ;S FF; Illegal page, special success BEQL 55$ ;S FF; Just do next page CLRB UCB$B_TRACKS(R3) ;S FF; Invalidate tracks CLRW UCB$W_CYLINDERS(R3) ;S FF; Invalidate cylinders MOVAL RIGID_DISK_PAGE_DESC(R7),R8 ;S FF; Address of descriptor MOVL #SIZEOF_RIGID_DISK,R6 ;S FF; Size BRW 95$ ;S FF; Log error 51$: MOVB - ;S FF; Save number of tracks per cylinder (R7),- UCB$B_TRACKS(R3) ;S FF; MOVB - ;S FF; Save number of cylinders (R7),- UCB$W_CYLINDERS+1(R3) ;S FF; (high byte) MOVB - ;S FF; Save number of cylinders (R7),- UCB$W_CYLINDERS(R3) ;S FF; (low byte) BRW 60$ ;S FF; Skip the Flexible Disk page now ; Geometry parameters for flexible disks 55$: PUSHL #SCSI$PGCD$C_FLEXIBLE_DISK ;S FF; Page code = flexible disk params PUSHAL FLEX_DISK_PAGE_DESC(R7) ;S FF; Address of descriptors PUSHL R5 ;S FF; SCDRP address CALLS #3,DO_MODE_PAGE ;S FF; Process the page BLBS R0,56$ ;S FF; If success, continue CMPL R0,#SS$_CANCEL ;S FF; If cancel status BEQL 80$ ;S FF; Then leave now LOAD_TENBYTE ;S FF; Refresh SCDRP bit CMPL #SS$_NOSUCHSEC,R0 ;S FF; Illegal page, special success BEQL 60$ ;S FF; Just do next page CLRW UCB$W_CYLINDERS(R3) ;S FF; Invalidate cylinders CLRB UCB$B_TRACKS(R3) ;S FF; Invalidate tracks MOVAL FLEX_DISK_PAGE_DESC(R7),R8 ;S FF; Address of descriptor MOVL #SIZEOF_FLEX_DISK,R6 ;S FF; Size BRW 95$ ;S FF; Log error 56$: MOVB - ;S FF; Save number of tracks per cylinder (R7),- UCB$B_TRACKS(R3) ;S FF; MOVB - ;S FF; Save number of cylinders (R7),- UCB$W_CYLINDERS+1(R3) ;S FF; (high byte) MOVB - ;S FF; Save number of cylinders (R7),- UCB$W_CYLINDERS(R3) ;S FF; (low byte) BISL2 #,- ;S FF; Remember that this is a floppy UCB$L_DK_FLAGS(R3) ;S FF; Check for 250 KHz RX23 diskette, ;S FF; if so, then mark it write-protected ;S FF; unless UCB$L_DK_FLAGS is ;S FF; set to allow writing of these media. BBS #UCB$V_DD_BYPASS,- ;S FF; Don't bother checking if the UCB$L_DK_FLAGS(R3),558$ ;S FF; special override bit is set CMPB #DT$_RX23S,UCB$B_DEVTYPE(R3) ;S FF; Only worry about RX23 media BNEQ 558$ ;S FF; CMPW #SCSI$FLX$C_XFR_250KHZ,- ;S FF; Check for 250 KHz (R7) BEQL 559$ ;S FF; If 250 KHz, then write protect it ;S FF; Check for 48 TPI diskette, ;S FF; if so, then mark it ;S FF; software write-protected 558$: CMPB #SCSI$FLX$R_FLAGS,- ;S FF; Check length of page (R7) ;S FF; Skip test for SPC if page does BGEQ 60$ ;S FF; not include flags word CMPZV #SCSI$FLX$V_SPC,- ;S FF; Test for SPC #SCSI$FLX$S_SPC,- ;S FF; (steps per cylinder) (R7),- #0 ;S FF; equal to 0 BEQL 60$ ;S FF; If so, then skip 559$: BBSS #DEV$V_SWL,UCB$L_DEVCHAR(R3),60$;S FF; If not, then set the SWL ;S FF; Note: can't write 48 TPI diskettes ; Caching page 60$: .IF DEFINED RZ74_CACHE .BRANCH_LIKELY CMPB #DT$_RZ74,UCB$B_DEVTYPE(R3) ;S FF; Check for RZ74 disk drive BNEQ 65$ ;S FF; No, use caching page BBS #UCB$V_OUT_OF_REV,- ;S FF; Is this RZ74 out of rev? UCB$L_DK_FLAGS(R3),70$ ;S FF; Yes, use no-caching page 65$: .ENDC BBS #UCB$V_HWL,- ;S FF; Skip if writelocked UCB$L_DK_FLAGS(R3),75$ PUSHL #SCSI$PGCD$C_CACHING ;S FF; Page code = caching page PUSHAL CACHING_PAGE_DESC(R7) ;S FF; Address of descriptors PUSHL R5 ;S FF; SCDRP address CALLS #3,DO_MODE_PAGE ;S FF; Process the page BLBS R0,75$ ;S FF; If success, continue CMPL R0,#SS$_CANCEL ;S FF; If cancel status BEQL 80$ ;S FF; Then leave now LOAD_TENBYTE ;S FF; Refresh SCDRP bit CMPL #SS$_NOSUCHSEC,R0 ;S FF; Illegal page, special success BEQL 75$ ;S FF; Continue MOVAL CACHING_PAGE_DESC(R7),R8 ;S FF; Address of descriptor MOVL #SIZEOF_CACHING,R6 ;S FF; Size BRW 95$ ;S FF; Log error .IF DEFINED RZ74_CACHE ; NoCaching page 70$: PUSHL #SCSI$PGCD$C_CACHING ;S FF; Page code = caching page PUSHAL NOCACHING_PAGE_DESC(R7) ;S FF; Address of descriptors PUSHL R5 ;S FF; SCDRP address CALLS #3,DO_MODE_PAGE ;S FF; Process the page BLBS R0,75$ ;S FF; If success, continue CMPL R0,#SS$_CANCEL ;S FF; If cancel status BEQL 80$ ;S FF; Then leave now LOAD_TENBYTE ;S FF; Refresh SCDRP bit CMPL #SS$_NOSUCHSEC,R0 ;S FF; Illegal page, special success BEQL 75$ ;S FF; Continue MOVAL NOCACHING_PAGE_DESC(R7),R8 ;S FF; Address of descriptor MOVL #SIZEOF_NOCACHING,R6 ;S FF; Size BRW 95$ ;S FF; Log error .ENDC 75$: MOVZWL #SS$_NORMAL,R0 ;S FF; Set success status 80$: ;S FF; Deallocate descriptors ADDL #DESCRIPTOR_SIZE+8,SP ;S FF; Account for desc + unalignment BBS #SCDRP$V_FLAG_TENBYTE,- ;S FF; 10-byte mode sense support detected? SCDRP$L_SCSI_FLAGS(R5),85$ BICL2 #UCB$M_TENBYTE,- ;S FF; Copy back to UCB - no 10-byte suppport UCB$L_DK_FLAGS(R3) ;S FF; next time through mode sense logic BRW 90$ ;S FF; Continue 85$: BISL2 #UCB$M_TENBYTE,- ;S FF; Copy back to UCB - 10-byte suppport UCB$L_DK_FLAGS(R3) ;S FF; 90$: RSB ;S FF; Return to caller 95$: CMPL R0,#SS$_CANCEL ;S FF; If cancel status BEQL 80$ ;S FF; Then leave now ; Since the real CDB has been deallocated by CLEANUP_CMD already, set up ; a dummy CDB for error logging purposes only. Note: All mode sense/select ; errors get logged as a 6-byte MODE_SENSE command with SCSI status FF. ; Since the register dump routine limits additional data to 255. bytes, ; We can store up to 6 descriptors (at 40. bytes each) in the error packet. PUSHL SCDRP$L_CMD_PTR(R5) PUSHL SCDRP$L_STS_PTR(R5) PUSHL SCDRP$L_SVA_USER(R5) PUSHL SCDRP$L_TRANS_CNT(R5) MOVAL DUMMY_MODE_SENSE_CDB+4,- ; Address of length longword SCDRP$L_CMD_PTR(R5) MOVAL DUMMY_MODE_SENSE_CDB,- ; Address of SCSI sts byte SCDRP$L_STS_PTR(R5) MOVL R8,SCDRP$L_SVA_USER(R5) ; Address of descriptors for ; this page ASSUME MODE_DESC$S_FIELD_DESCRIPTOR EQ 40 CMPL R6,- ; Do we have too many to save? #<6*MODE_DESC$S_FIELD_DESCRIPTOR> BLEQ 98$ ; Good, we can save all desc MOVL #<6*MODE_DESC$S_FIELD_DESCRIPTOR>,- R6 ; Can save first six only. 98$: MOVL R6,SCDRP$L_TRANS_CNT(R5) ; Byte count of all descriptors ; for this page LOG_ERROR - ;S FF; Log a mode sense data error TYPE=MODE_SENSE_DATA,- VMS_STATUS=R0,- UCB=R3,- SCDRP=R5 POPL SCDRP$L_TRANS_CNT(R5) POPL SCDRP$L_SVA_USER(R5) POPL SCDRP$L_STS_PTR(R5) POPL SCDRP$L_CMD_PTR(R5) MOVL #SS$_MEDOFL,R0 ;S FF; Convert debug-usable error ;S FF; to user-visible error BRB 80$ ;S FF; Use common exit path .PAGE .SBTTL PROCESS_MODE_FORMAT_FLOPPY ;+ ; PROCESS_MODE_FORMAT_FLOPPY ; ; This routine is called to change fields in the format parameters and the ; flexible disk parameter pages as necessary to conform to the requested ; format. ; ; Context: ; ; SCDRP thread ; IPL = Fork ; Fork lock held ; ; Inputs: ; ; R3 - UCB address ; R4 - PDT address ; R5 - SCDRP address ; ; Outputs: ; ; R0 - Status ; R1,R2 - Destroyed ; All other registers preserved ;- PROCESS_MODE_FORMAT_FLOPPY:: ;S FF; Send a mode select command .JSB_ENTRY INPUT=,OUTPUT=,SCRATCH=,PRESERVE= ; Allocate a local copy of the mode page descriptors on the stack SUBL3 #DESCRIPTOR_SIZE,SP,R7 ;S FF; Size of descriptor array BICL #7,R7 ;S FF; Quadword align SUBL #DESCRIPTOR_SIZE+8,SP ;S FF; Account for desc + unalignment PUSHR #^M MOVC5 #0,.,#0,#DESCRIPTOR_SIZE,(R7) ;S FF; Initialize the desc area MOVC3 #DESCRIPTOR_SIZE,- ;S FF; Copy the descriptors DESCRIPTOR_BASE,(R7) POPR #^M INSV UCB$B_SCSI_VERSION(R3),- ;S FF; Determine SCSI1 vs SCSI2 #SCDRP$V_FLAG_MODE_SENSE,- #SCDRP$S_FLAG_MODE_SENSE,- SCDRP$L_SCSI_FLAGS(R5) ;S FF; Set up format parameters depending ;S FF; on device: ;S FF; RX33s -> 15 sectors ;S FF; RX23s & /dens=double -> 18 sectors ;S FF; RX23s & /dens=single & bypass ;S FF; -> 9 sectors ;S FF; RX26 & /dens=DD -> 9 sectors ;S FF; RX26 & /dens=HD -> 18 sectors ;S FF; RX26 & /dens=ED -> 36 sectors MOVAB 15 sectors SCSI$FMT$W_TRACKS>,R6 CMPB #DT$_RX33S,UCB$B_DEVTYPE(R3) ;S FF; Check for RX33 drive BEQL 70$ ;S FF; Yep, use RX33 parameters MOVQ R1,-(SP) ;S FF; Save registers R1 and R2 MOVL SCDRP$L_IRP(R5),R2 ;S FF; Point to IRP SUBL3 #1,IRP$L_MEDIA(R2),R2 ;S FF; to get /density qualifier ;S FF; (adjusting for index offset of one) MOVAL MODE_SELECT_FORMAT_TABLE,R1 ;S FF; Point to table of addresses ADDL3 R1,(R1)[R2],R6 ;S FF; Get address from table MOVQ (SP)+,R1 ;S FF; Restore registers ; ; Build the format parameters page ; 70$: MOVW SCSI$FMT$W_SECTORS(R6),- ; S FF; Change sectors per track (R7) LOAD_TENBYTE ;S FF; Copy UCB.TENBYTE bit to SCDRP PUSHL #SCSI$PGCD$C_FORMAT_DEVICE ;S FF; Page code = format device page PUSHAL FORMAT_DEVICE_SEL_PAGE_DESC(R7) ;S FF; Address of descriptors PUSHL R5 ;S FF; SCDRP address CALLS #3,DO_MODE_PAGE BLBC R0,1100$ ; ; Build the flexible disk geometry parameters page ; ;S FF; Set up flexible geometry depending ;S FF; on device: ;S FF; RX33s -> 15 sectors ;S FF; RX23s & /dens=double -> 18 sectors ;S FF; RX23s & /dens=single & bypass ;S FF; -> 9 sectors ;S FF; RX26 & /dens=DD -> 9 sectors ;S FF; RX26 & /dens=HD -> 18 sectors ;S FF; RX26 & /dens=ED -> 36 sectors MOVAB 15 sectors SCSI$FLX$W_TRANSFER_RATE>,R6 ;S FF; CMPB #DT$_RX33S,UCB$B_DEVTYPE(R3) ;S FF; Check for RX33 drive BEQL 170$ ;S FF; Yes, take branch MOVQ R1,-(SP) ;S FF; Save registers R1 and R2 MOVL SCDRP$L_IRP(R5),R2 ;S FF; Point to IRP SUBL3 #1,IRP$L_MEDIA(R2),R2 ;S FF; to get /density qualifier ;S FF; (adjusting for index offset of one) MOVAL MODE_SELECT_FLEXIBLE_TABLE,R1 ;S FF; Point to table of addresses ADDL3 R1,(R1)[R2],R6 ;S FF; Get address from table MOVQ (SP)+,R1 ;S FF; Restore registers 170$: MOVW SCSI$FLX$W_TRANSFER_RATE(R6),- ;S FF; Use our transfer rate (R7) MOVB SCSI$FLX$B_HEADS(R6),- ;S FF; Use our number of heads (R7) MOVB SCSI$FLX$B_SECTORS_TRACK(R6),- ;S FF; Use our sectors per track (R7) MOVW SCSI$FLX$W_CYLINDERS(R6),- ;S FF; Use our number of cylinders (R7) EXTZV #SCSI$FLX$V_SSN,#1,- ;S FF; Use our start sector number SCSI$FLX$R_FLAGS(R6),- (R7) EXTZV #SCSI$FLX$V_SPC,#SCSI$FLX$S_SPC,- ;S FF; Use our steps/cyl SCSI$FLX$R_FLAGS(R6),- (R7) PUSHL #SCSI$PGCD$C_FLEXIBLE_DISK ;S FF; Page code = flexible disk params 55$ PUSHAL FLEX_DISK_SELECT_PAGE_DESC(R7) ;S FF; Address of descriptors PUSHL R5 ;S FF; SCDRP address CALLS #3,DO_MODE_PAGE BLBC R0,1100$ MOVL #SS$_NORMAL,R0 ;S FF; Set success status 900$: ;S FF; Deallocate descriptors ADDL #DESCRIPTOR_SIZE+8,SP ;S FF; Account for desc + unalignment RSB ;S FF; Return to caller 1100$: CMPL R0,#SS$_CANCEL ;S FF; If cancel status BEQL 900$ ;S FF; Then leave now MOVL #SS$_DRVERR,R0 BRB 900$ ;S FF; Join common exit code .PAGE .SBTTL VALIDATE_GEOMETRY ;+ ; VALIDATE_GEOMETRY ; ; In general, clyinders/tracks/sectors must be nonzero in order to set the device valid. ; This routine checks for zero values in various geometry fields. In special cases it ; will provide dummy values for fields; in other cases of zero fields it will return ; an error. ; Context: ; ; SCDRP thread ; IPL = Fork ; Fork lock held ; ; Inputs: ; ; R3 - UCB address ; R4 - PDT address ; R5 - SCDRP address ; ; Outputs: ; ; R0 - Status ; R1,R2 - Destroyed ; All other registers preserved ;- VALIDATE_GEOMETRY: .JSB_ENTRY INPUT=,OUTPUT=,SCRATCH= ASSUME UCB$B_SECTORS+1 EQ UCB$B_TRACKS ASSUME UCB$B_TRACKS+1 EQ UCB$W_CYLINDERS MOVAL UCB$B_SECTORS(R3),R1 ;S FF; Get address sectors field in UCB TSTB (R1)+ ;S FF; Check that the tracks, sectors, and BEQL 10$ ;S FF; cylinders fields in the UCB are all TSTB (R1)+ ;S FF; non-zero. If any are zero, then possibly BEQL 10$ ;S FF; there's a problem and error status TSTW (R1)+ ;S FF; should be returned to prevent the ;S FF; volume valid bit from being set. BNEQ 30$ ;S FF; All fields nonzero, good. ; If here, at least one of cylinder/track/sector is zero. 10$: BITL #,- ;S FF; These devices do not always return UCB$L_DK_FLAGS(R3) ;S FF; cylinder and track info. BNEQ 30$ ;S FF; Set dummy values if so. ; Handle possibility of unformatted media in a floppy drive, which ; returns zeroes in the geometry fields. ; BBC #UCB$V_FLOPPY,- ;S FF; Report different error if floppy UCB$L_DK_FLAGS(R3),20$ MOVZWL #SS$_FORMAT,R0 ;S FF; Set bad status: unformatted media BRB 200$ ;S FF; Use common exit path ; If this is a read-only device such as a CDROM, it may not have real tracks, ; sectors, and cylinders. If this is the case, set up dummy values for the ; geometry parameters. 20$: BBS #DEV$V_SWL,- ;S FF; If this is a read-only device, it's not UCB$L_DEVCHAR(R3),30$ ;S FF; required to have non-zero values of ;S FF; tracks, sectors, and cylinders ; If here, at least one of cylinder/track/sector is zero, and this is an error. TSTL UCB$L_MAXBLOCK(R3) ;S FF; Ensure not 0 len. dsk BNEQ 30$ MOVZWL #SS$_DRVERR,R0 ;S FF; Set bad status BRB 200$ ;S FF; Use common exit path ; Proceed to check geometry further. Set dummy values where appropriate so we may continue. 30$: MOVL UCB$L_MAXBLOCK(R3),R2 ;S FF; Get max block number INCL R2 ;S FF; Get max blocks MOVZBL UCB$B_TRACKS(R3),R1 ;S FF; Get number of tracks MOVZBL UCB$B_SECTORS(R3),R0 ;S FF; Get number of sectors MULL R0,R1 ;S FF; Multiply BEQL 35$ ;S FF; Branch if tracks or sectors is zero MOVZWL UCB$W_CYLINDERS(R3),R0 ;S FF; Get number of cylinders MULL R0,R1 ;S FF; Get tracks * sectors * cylinders CMPL R1,R2 ;S FF; Tracks * sectors * cyl = maxblock? BNEQ 40$ ;S FF; Branch if not BRW 190$ ;S FF; Set success status and exit ; Either tracks or sectors field (or both) is zero. Set up dummy values ; for these two fields. ASSUME UCB$B_SECTORS+1 EQ UCB$B_TRACKS 35$: MOVW #^X604,UCB$B_SECTORS(R3) ;S FF; Fill in dummy values for sector ;S FF; and track fields ; Here we've found that MAXBLOCK is not equal to tracks * sectors * cylinders. ; Calculate a new value for cylinders to ensure that the product is greater ; than maxblock. Use the same algorithm as DUDRIVER so that SCSI disks served ; in a cluster appear to have the same geometry on every node. 40$: MOVZBL UCB$B_TRACKS(R3),R1 ;S FF; Get number of tracks MOVZBL UCB$B_SECTORS(R3),R0 ;S FF; Get number of sectors MULL R1,R0 ;S FF; Multiply MOVL UCB$L_MAXBLOCK(R3),R1 ;S FF; Get number of blocks CLRL R2 ;S FF; Prepare for extended divide EDIV R0,R1,R0,R1 ;S FF; Calculate number of cylinders, remainder CMPL R0,#65534 ;S FF; Is cylinder number legal? BGTR 202$ ;S FF; If so go try a crude fix MOVW R0,UCB$W_CYLINDERS(R3) ;S FF; Save number of cylinders TSTL R1 ;S FF; Zero remaineder? BEQL 190$ ;S FF; Branch if so INCW UCB$W_CYLINDERS(R3) ;S FF; Otherwise, increment cylinders ;S FF; (tracks * sectors * cylinders must ;S FF; be >= maxblock) 190$: MOVL #SS$_NORMAL,R0 200$: RSB ; ill geom. try fixup. 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 big BLSSU 40$ ;S FF; Redo computation if ok ; If disk is over 300Gb, try to allow 2TB MOVB #255,UCB$B_TRACKS(R3) ;S FF; Go for broke MOVB #255,UCB$B_SECTORS(R3) BRW 40$ ;This is as big as we can go. ; This should be adequate for any currently supported disk size (i.e., ; with a 32-bit block number a la SCSI-2), losing at most a small bit of ; capacity but allowing disk access for most of the surface. .PAGE .SBTTL FORMAT - Send a format command ;+ ; FORMAT ; ; This routine sends a format command to the target. ; ; Context: ; ; SCDRP thread ; IPL = Fork ; Fork lock held ; ; Inputs: ; ; R3 - UCB address ; R4 - PDT address ; R5 - SCDRP address ; ; Outputs: ; ; R0 - Status ; R1,R2 - Destroyed ; All other registers preserved ;- FORMAT:: ;S FF; Send a format command .JSB_ENTRY INPUT=,OUTPUT=,SCRATCH= MOVAL CMD_FORMAT_UNIT,R2 ;S FF; Address of FORMAT command SETUP_CMD ;S FF; Perform setup for SCSI command MOVL #RX_FMT_DISC_TMO,- ;S FF; Increase the disconnect timeout SCDRP$L_DISCON_TIMEOUT(R5) ;S FF; value for the format operation SEND_COMMAND_ORDERED ;S FF; Send the SCSI command CLEANUP_CMD ;S FF; Cleanup from the SCSI command RSB ;S FF; .PAGE .SBTTL READ_CAPACITY - Send a read capacity command ;+ ; READ_CAPACITY ; ; This routine reads the capacity of the target. It is called AFTER MODE_SENSE ; and MODE_SELECT so that a possible change of block size will be reflected in ; the device's block count. It then ensures that the geometry information ; returned by MODE_SENSE agrees with the device's capacity (the product of ; tracks, sectors, and cylinders must not exceed the maximum block number). ; ; Context: ; ; SCDRP thread ; IPL = Fork ; Fork lock held ; ; Inputs: ; ; R3 - UCB address ; R4 - PDT address ; R5 - SCDRP address ; ; Outputs: ; ; R0 - Status ; R1,R2 - Destroyed ; All other registers preserved ;- READ_CAPACITY:: ;S FF; Send a read capacity command .JSB_ENTRY INPUT=,OUTPUT=,SCRATCH= MOVAL CMD_READ_CAPACITY,R2 ;S FF; Address of READ CAPACITY command SETUP_CMD ;S FF; Perform setup for SCSI command SEND_COMMAND_ORDERED ;S FF; Send the SCSI command BLBC R0,20$ ;S FF; Branch on error MOVL SCDRP$L_SVA_USER(R5),R1 ;S FF; Get address of read capacity data MOVAL UCB$L_MAXBLOCK+4(R3),R2 ;S FF; Get address just beyond MAXBLOCK .REPT 4 ;S FF; Copy four bytes of max block data MOVB (R1)+,-(R2) ;S FF; from SCSI buffer to UCB. Note that .ENDR ;S FF; bytes are reversed in SCSI buffer INCL (R2) ;S FF; Convert to maxblocks ADDL #2,R1 ;S FF; Step over irrelevant data MOVB (R1)+,UCB$W_BLOCK_SIZE+1(R3) ;S FF; Get MSB of block size MOVB (R1),UCB$W_BLOCK_SIZE(R3) ;S FF; Get LSB of block size CMPW UCB$W_BLOCK_SIZE(R3),#512 ;S FF; Is block size 512? BEQL 10$ ;S FF; If so, exit normally BITL #,- ;S FF; These devices may have ACPs to UCB$L_DK_FLAGS(R3) ;S FF; handle special geometry. BNEQ 10$ ;S FF; Exit normally if so. BBSS #DEV$V_SWL,UCB$L_DEVCHAR(R3),10$;S FF; Otherwise software writelock this device. 10$: MOVL #SS$_NORMAL,R0 ;S FF; Set success status 20$: CLEANUP_CMD ;S FF; Cleanup from the SCSI command RSB ;S FF; .PAGE .SBTTL START_UNIT - Send a start unit command .SBTTL STOP_UNIT - Send a stop unit command ;+ ; START_UNIT - Start command to spin up device ; STOP_UNIT - Stop command to spin down device ; ; This routine sends a start or stop command to the target to spin up or ; spin down the device, respectively. ; ; Context: ; ; SCDRP thread ; IPL = Fork ; Fork lock held ; ; Inputs: ; ; r3 - UCB address ; R4 - PDT address ; R5 - SCDRP address ; ; Outputs: ; ; R0 - Status ; R1,R2 - Destroyed ; All other registers preserved ;- START_UNIT:: ;S FF; Send a start unit command .JSB_ENTRY INPUT=,OUTPUT=,SCRATCH= MOVAL CMD_START_UNIT,R2 ;S FF; Address of START UNIT command BRB START_STOP_COMMON ;S FF; Use common path STOP_UNIT:: ;S FF; Send a start unit command .JSB_ENTRY INPUT=,OUTPUT=,SCRATCH= MOVAL CMD_STOP_UNIT,R2 ;S FF; Address of START UNIT command START_STOP_COMMON: SETUP_CMD ;S FF; Perform setup for SCSI command MOVL SCDRP$L_CMD_PTR(R5),R0 ;S FF; Get address of command buffer SEND_COMMAND_ORDERED ;S FF; Send the SCSI command CLEANUP_CMD ;S FF; Cleanup from the SCSI command RSB ;S FF; .PAGE .SBTTL READ_WRITE - Send a read or write command ;+ ; READ_WRITE ; ; This routine sends either a SCSI read or write command to the target based on ; setting of the FUNC bit in the SCDRP. The LBN in the command is filled in ; from the MEDIA field and the block count is filled in from the BCNT. The ; pad count field is filled in with the number of additional bytes over BNCT ; that must be transferred to make an integral number of blocks. Note that ; SCSI disks transfer an integral number of blocks, while a user can specify ; request for a fraction of a block. ; ; Context: ; ; SCDRP thread ; IPL = Fork ; Fork lock held ; ; Inputs: ; ; R2 - IRP address ; R3 - UCB address ; R4 - PDT address ; R5 - SCDRP address ; scdrp$is_queue_char - tag type ; ; Outputs: ; ; R0 - Status ; R1,R2 - Destroyed ; All other registers preserved ;- READ_WRITE:: ;S FF; Send a read or write command .JSB_ENTRY INPUT=,OUTPUT=,SCRATCH= MOVL SCDRP$L_MEDIA(R5),- ;S FF; Save the current head position UCB$L_CUR_LBN(R3) ;S FF; CMPL SCDRP$L_MEDIA(R5),- ;S FF; Does the LBN specified require #EXTND_LBN_LIMIT ;S FF; an extended read or write command? BGTRU EXTND_READ_WRITE ;S FF; Branch if so BBC #IRP$V_FUNC,- ;S FF; Branch if this is a write function SCDRP$IS_STS(R5),5$ ;S FF; MOVAL CMD_READ,R2 ;S FF; Get address of SCSI read command BRB 10$ 5$: MOVAL CMD_WRITE,R2 ;S FF; Assume this is write command 10$: SETUP_CMD ;S FF; Set up the command CLRL SCDRP$L_PAD_BCNT(R5) ;S FF; Assume no padding of last page required MOVL SCDRP$L_BCNT(R5),R1 ;S FF; Get byte count BICL3 #^C,R1,R0 ;S FF; Number of bytes in last block BEQL 20$ ;S FF; Branch if block-integral transfer SUBL3 R0,#IOC$C_DISK_BLKSIZ,- ;S FF; Number of bytes of padding required SCDRP$L_PAD_BCNT(R5) ;S FF; for block-integral transfer 20$: ADDL2 #,R1 ;S FF; Round up byte count to block boundary ASHL #-IOC$S_DISK_BLKSIZ,R1,R1 ;S FF; Convert to block count ADDL3 #<4+CMD_READ$B_XFER_COUNT>,-;S FF; Address of transfer length in CDB, SCDRP$L_CMD_PTR(R5),R0 ;S FF; The CDB doesn't start at 0, but 4 MOVB R1,(R0) ;S FF: Set transfer count in SCSI command ;S FF; CMD_READ$B_XFER_COUNT(R0) MOVAL SCDRP$L_MEDIA(R5),R1 ;S FF; Get logical block number field in SCDRP MOVB (R1)+,-(R0) ;S FF; Fill in logical block address MOVB (R1)+,-(R0) ;S FF; CMD_READ$R_LBA, +2, +1 MOVB (R1),R1 ;S FF; Get high order LBN BICL #^C^X1F,R1 ;S FF; Clear LUN bits MOVB R1,-(R0) ;S FF; CMD_READ$R_LBA, +0 BISL #SCDRP$M_FLAG_BUFFER_MAPPED,- ;S FF; Remember the fact that a buffer SCDRP$L_SCSI_FLAGS(R5) ;S FF; has been mapped SPI$BUFFER_MAP ;S FF; Map the user buffer PUSHL R5 ;S FF; SCDRP PUSHL R4 ;S FF; PDT PUSHL R3 ;S FF; UCB CALLS #3,SEND_COMMAND ;S FF; Send the SCSI command BICL #SCDRP$M_FLAG_S0BUF,- ;S FF; Don't deallocate the S0 "user" buffer SCDRP$L_SCSI_FLAGS(R5) ;S FF; at this time CLEANUP_CMD ;S FF; Clean up from the current command RSB ;S FF; Return to caller .PAGE .SBTTL EXTND_READ_WRITE - Send an extended read or write command ;+ ; EXTND_READ_WRITE ; ; This routine sends either a SCSI extended read or write command to the ; target based on setting of the FUNC bit in the SCDRP. An extended command ; must be used when the LBN specified in the QIO won't fit in the LBN field ; of the normal read/write SCSI command. The LBN in the command is filled in ; from the MEDIA field and the block count is filled in from the BCNT. The ; pad count field is filled in with the number of additional bytes over BNCT ; that must be transferred to make an integral number of blocks. Note that ; SCSI disks transfer an integral number of blocks, while a user can specify ; request for a fraction of a block. ; ; Context: ; ; SCDRP thread ; IPL = Fork ; Fork lock held ; ; Inputs: ; ; R2 - IPR address ; R3 - UCB address ; R4 - PDT address ; R5 - SCDRP address ; ; Outputs: ; ; R0 - Status ; R1,R2 - Destroyed ; All other registers preserved ;- EXTND_READ_WRITE: ;S FF; Send an extended read or write command BBC #IRP$V_FUNC,- ;S FF; Branch if this is a write function SCDRP$IS_STS(R5),5$ ;S FF; MOVAL CMD_EXTND_READ,R2 ;S FF; Get address of SCSI extended read command BRB 10$ 5$: MOVAL CMD_EXTND_WRITE,R2 ;S FF; Assume this is an extended write command 10$: SETUP_CMD ;S FF; Set up the command CLRL SCDRP$L_PAD_BCNT(R5) ;S FF; Assume no padding of last page required MOVL SCDRP$L_BCNT(R5),R1 ;S FF; Get byte count BICL3 #^C,R1,R0 ;S FF; Number of bytes in last block BEQL 20$ ;S FF; Branch if block-integral transfer SUBL3 R0,#IOC$C_DISK_BLKSIZ,- ;S FF; Number of bytes of padding required SCDRP$L_PAD_BCNT(R5) ;S FF; for block-integral transfer 20$: ADDL2 #,R1 ;S FF; Round up byte count to block boundary ASHL #-IOC$S_DISK_BLKSIZ,R1,R1 ;S FF; Convert to block count ADDL3 #4,SCDRP$L_CMD_PTR(R5),R0 ;S FF; Get address of SCSI CDB (Command). ;S FF; The CDB doesn't start at 0, but 4. MOVB R1,CMD_EXTND_READ$W_XFER_COUNT+1(R0) ;S FF; Set LSB transfer count ASHL #-8,R1,R1 ;S FF; Get MSB transfer count MOVB R1,CMD_EXTND_READ$W_XFER_COUNT(R0) ;S FF; Set LSB transfer count ADDL3 #SCDRP$L_MEDIA,R5,R1 ;S FF; Get logical block number field in SCDRP ADDL #,R0 ;S FF; Point just beyond LSB of LBN field in ;S FF; SCSI command packet .REPT 4 MOVB (R1)+,-(R0) ;S FF; Fill in logical block address .ENDR BISL #SCDRP$M_FLAG_BUFFER_MAPPED,- ;S FF; Remember the fact that a buffer SCDRP$L_SCSI_FLAGS(R5) ;S FF; has been mapped SPI$BUFFER_MAP ;S FF; Map the user buffer PUSHL R5 ;S FF; PUSHL R4 ;S FF; PUSHL R3 ;S FF; CALLS #3,SEND_COMMAND ;S FF; Send the SCSI command BICL #SCDRP$M_FLAG_S0BUF,- ;S FF; Don't deallocate the S0 "user" buffer SCDRP$L_SCSI_FLAGS(R5) ;S FF; at this time CLEANUP_CMD ;S FF; Clean up from the current command RSB ;S FF; Return to caller .PAGE .SBTTL REASSIGN_BLOCK - Send a reassign block command ;+ ; REASSIGN_BLOCK ; ; This routine sends a reassign blocks command to a target to physically ; relocate the data for a specific logical block on the disk. This is done ; after a read operation fails with a recoverable error or a write operations ; fails with either a recoverable or non-recoverable error. Read operations ; with non-recoverable errors do not result in reassignment operations as this ; could result in undetected user data corruption. ; ; Context: ; ; SCDRP thread ; IPL = Fork ; Fork lock held ; ; Inputs: ; ; R3 - UCB address ; R4 - PDT address ; R5 - SCDRP address of original read/write command ; ; Outputs: ; ; R0 - Status ; R1,R2 - Destroyed ;- REASSIGN_BLOCK:: ;S FF; Send a reassign block command .JSB_ENTRY INPUT=,OUTPUT=,SCRATCH=,PRESERVE= BBS #DEV$V_SWL,UCB$L_DEVCHAR(R3),10$;S FF; Branch if device is read-only, ;S FF; not possible to reassign block BBS #UCB$V_NOREASSIGN,- ;S FF; Branch if device doesn't support UCB$L_DK_FLAGS(R3),10$ ;S FF; reassign_block command MOVAL CMD_REASSIGN_BLOCKS,R2 ;S FF; Address of reassign blocks command SETUP_CMD ;S FF; Perform setup for SCSI command ADDL3 SCDRP$L_SVA_USER(R5),- ;S FF; Get defect list buffer address (and #3,R0 ;S FF; advance to defect list length field) MOVB #4,(R0)+ ;S FF; Just one defective block being ;S FF; reassigned MOVAL SCDRP$L_MEDIA+4(R5),R1 ;S FF; Get address beyond block number to ;S FF; reassign .REPT 4 MOVB -(R1),(R0)+ ;S FF; Fill in a byte of LBN (note bytes are swapped) .ENDR LOG_ERROR - ;S FF; Log a reassign block error (if the TYPE=REASSIGN_BLOCK,- ;S FF; reassign fails, SEND_COMMAND will VMS_STATUS=#SS$_NORMAL,- ;S FF; log another error). UCB=R3,- SCDRP=R5 MOVL #REASSIGN_DISCON_TMO,- ;S FF; Set up extended disconnect timeout SCDRP$L_DISCON_TIMEOUT(R5) ;S FF; for reassign ADDL G^SGN$GL_VMS7,- ;S FF; Alter the time out value by SCDRP$L_DISCON_TIMEOUT(R5) ;S FF; the amount specified SEND_COMMAND_ORDERED ;S FF; Send the SCSI command CLEANUP_CMD ;S FF; Cleanup from the command RSB ;S FF; Return to caller 10$: BUG_CHECK INCONSTATE,FATAL ;S FF; Should never attempt to reassign a ;S FF; block for a write-locked disk, ;S FF; or for a drive which doesn't support ;S FF; reassign_block command .PAGE .SBTTL + .SBTTL + UTILITY ROUTINES .SBTTL + .SBTTL SET_UNIT_ONLINE - Issue SCSI commands to bring unit online ;+ ; SET_UNIT_ONLINE ; ; This routine attempts to bring a unit online. First, an inquiry command is ; sent to the drive to ensure that it can communicate and that it's a valid ; disk-like device. If the INQUIRY fails, a fork thread is set up to ; periodically poll the drive. ; ; If the drive is the system disk, a PACKACK IRP is initiated for this unit ; to make the volume valid. The drive is then set online, and any I/O that had ; been queued to the device while the decice had been busy or offline is ; initiated. ; ; Context ; ; Fork Thread ; IPL = Fork ; Fork lock held ; ; Inputs: ; ; R5 - UCB address ; ; Outputs: ; ; R0 - Status ; R1-R4 - Destroyed ; ; ONLINE bit set in the UCB, BSY bit cleared ;- SET_UNIT_ONLINE:: ;F FF; Issue SCSI commands to bring unit online .JSB_ENTRY INPUT=,OUTPUT=,SCRATCH= 10$: MOVL UCB$L_PDT(R5),R4 ;F FF; Get PDT address MOVL R5,R3 ;F FF; Copy UCB address DK_ALLOC_SCDRP ;F FF; Allocate an SCDRP MOVL UCB$PS_UNITINIT_KPB(R3),- ;S FF; Save KPB address in SCDRP SCDRP$PS_KPB(R5) ;S FF; SUBL #SPI$K_CC_LENGTH,SP ;S FF; Allocate buffer on the KP stack MOVL SP,R2 ;S FF; Copy buffer address MOVL #SPI$K_CC_QNUM_ARGS,- ;S FF; Set argument count in buffer SPI$IL_CC_COUNT(R2) SPI$CONNECTION_CHAR_GET ;S FF; Get current connection characteristics BLBC R0,9$ ;S FF; Forget it if error BISL #SPI$M_CC_ENA_DISCON,- ;S FF; Enable disconnects SPI$IL_CC_CON_FLAGS(R2) SPI$CONNECTION_CHAR_SET ;S FF; Set connection characteristics 9$: ADDL #SPI$K_CC_LENGTH,SP ;S FF; Deallocate buffer BSBW INQUIRY ;S FF; Send an inquiry command BLBC R0,11$ ;S FF; Branch on error BSBW SET_CONN_CHAR ;S FF; Initial connection characteristics 11$: DK_DEALLOC_SCDRP CLRSTK=YES ;S FF; Deallocate the SCDRP MOVL R3,R5 ;F FF; Copy UCB address BLBC R0,20$ ;F FF; Branch if error BISL #UCB$M_ONLINE!UCB$M_BSY,- ;F FF; Set the device online, busy UCB$L_STS(R5) ;F FF; ; If the MSCP server is loaded, call its "new device" routine to allow this ; unit to be served. MOVL G^SCS$GL_MSCP_NEWDEV,R2 ;F FF; Get address of MSCP routine BGEQ 12$ ;F FF; Branch if not available JSB (R2) ;F FF; Make this unit available to be served 12$: CMPL G^SYS$AR_BOOTUCB,R5 ;F FF; Is this the system disk? BNEQ 15$ ;F FF; Branch if not BSBW ALLOC_IRP ;F FF; Get an IRP MOVL #IO$_PACKACK,IRP$L_FUNC(R3) ;F FF; Store function code BISL #IRP$M_PHYSIO,IRP$L_STS(R3) ;F FF; Set physical I/O bit .PRESERVE ATOMICITY INCL UCB$L_QLEN(R5) ;F FF; Increment the queue length .NOPRESERVE ATOMICITY MOVAL 13$,IRP$L_PID(R3) ;F FF; Set I/O completion return address BRB 16$ ;F FF; Start the I/O 13$: .JSB_ENTRY INPUT=,SCRATCH= BLBC IRP$L_IOST1(R5),19$ ;F FF; Branch if PACKACK failed MOVL IRP$L_UCB(R5),R0 ;F FF; Get UCB address BBSS #UCB$V_LCL_VALID,- ;F FF; Set local valid bit and UCB$L_STS(R0),14$ ;F FF; branch if its already set INCB UCB$B_ONLCNT(R0) ;F FF; Increment online count. 14$: BRW DEALLOC_IRP ;F FF; Deallocate the PACKACK IRP 19$: MOVL R5,R3 ;F FF; Get IRP in r3 MOVL IRP$L_UCB(R5),R5 ;F FF; Get UCB address IN R5 FORKLOCK LOCK=UCB$B_FLCK(R5),- ;F FF; Take out device's fork; PRESERVE=NO,- ;F FF; lock SAVIPL= -(SP) CALL_INITIATE ;F FF; initiate I/O, again FORKUNLOCK LOCK=UCB$B_FLCK(R5),- ;F FF; release device's fork PRESERVE=NO,- ;F FF; lock NEWIPL= (SP)+ ,- CONDITION=RESTORE ;F FF; and up as we were RSB 15$: REMQUE @UCB$L_IOQFL(R5),R3 ;F FF; Remove IRP from device pending queue BVS 17$ ;F FF; If found one then 16$: CALL_INITIATE ;F FF; initiate I/O RSB ;F FF; and return 17$: TST_SINGLE 18$ ;F FF; else are there any other reasons not clear bsy? BICL #UCB$M_BSY, UCB$L_STS(R5) ;F FF; Clear UCB busy bit 18$: RSB ;F FF; Return ; We've tried and failed at least once to send an inquiry command. Clear the ; online and busy bits and set up a fork thread to poll the device once ; a minute in an attempt to bring it online. In the meantime, any I/O queued to ; this device will fail with device offline status. Flush the queue of any I/O ; that might have been queued to the device during the period that it was ; online but busy. In the case of the system disk, don't set the unit offline ; and don't flush the queues as this could cause a boot to fail. Instead, just ; poll and hope that eventually the system disk eventually comes back to life. 20$: CMPL G^SYS$AR_BOOTUCB,R5 ;F FF; Is this the system disk? BNEQ 30$ ;F FF; Branch if not MOVZBL UCB$B_FLCK(R5),-(SP) ;F FF; Set the required fork lock PUSHAQ TWO_SECONDS ;F FF; Set the time delay in ticks PUSHL UCB$PS_UNITINIT_KPB(R5) ;F FF; Get address of KPB CALLS # 3, G^ EXE$KP_TQE_WAIT ;F FF; Fork and wait the desired time BRW 10$ ;F FF; And try again 30$: BICL #UCB$M_ONLINE!- ;F FF; Set the unit offline, not busy UCB$M_BSY,- ;F FF; UCB$L_STS(R5) ;F FF; 40$: REMQUE @UCB$L_IOQFL(R5),R3 ;F FF; Remove an IRP from the queue BVS 45$ ;F FF; Branch if queue was empty INSQUE (R3),@UCB$L_FLUSH_IOQBL(R5) ;F FF; Place on end of flush queue BRB 40$ ;F FF; Repeat until I/O queue is empty 45$: REMQUE @UCB$L_FLUSH_IOQFL(R5),R3 ;F FF; Remove an IRP from the flush queue BVS 50$ ;F FF; Branch if queue was empty MOVL R3, UCB$L_IRP(R5) ;F FF; Copy IRP addess to UCB CLRL R1 ;F FF; Clear transfer byte count MOVL #SS$_DEVOFFLINE,R0 ;F FF; Set device offline status CALL_REQCOM ;F FF; Complete the QIO BRB 45$ ;F FF; Continue to flush queue 50$: CLRL UCB$L_IRP(R5) ;F FF; This I/O has been processed. MOVZBL UCB$B_FLCK(R5),-(SP) ;F FF; Set the required fork lock PUSHAQ THIRTY_SECONDS ;F FF; Set the time delay in ticks PUSHL UCB$PS_UNITINIT_KPB(R5) ;F FF; Get address of KPB CALLS # 3, G^ EXE$KP_TQE_WAIT ;F FF; Fork and wait the desired time BRW 10$ ;F FF; And try again .PAGE .SBTTL WAIT_UNIT_READY - Wait for unit to become ready ;+ ; WAIT_UNIT_READY ; ; This routine polls once every second waiting for the ; drive to become ready (spun up). The first time a test unit ready fails with ; DEVOFFLINE status (which comes from a NOT_READY status from the drive), a ; start unit command is sent to the drive in case the failure was ; due to the drive being spun down. Polling continues for READ_POLL_CNT seconds ; or until the device returns success status to a to a test unit ready command. ; ; Context: ; ; SCDRP thread ; IPL = Fork ; Fork lock held ; ; Inputs: ; ; R3 - UCB address ; R4 - PDT address ; R5 - SCDRP address ; ; Outputs: ; ; R0 - Status ; R1,R2 - Destroyed ; All other registers preserved ;- WAIT_UNIT_READY:: ;S FF; Wait for unit to become ready .JSB_ENTRY INPUT=,OUTPUT=,SCRATCH= MOVB #READY_POLL_CNT,- ;S FF; Initialize retry count UCB$B_READY_RETRY(R3) ; 10$: BSBW TEST_UNIT_READY ;S FF; Execute TEST UNIT READY command BLBC R0,30$ ;S FF; Branch on error 20$: RSB ;S FF; Return to caller 30$: CMPL R0,#SS$_CANCEL ;S FF; If cancel status BEQL 20$ ;S FF; Then leave now CMPL R0,#SS$_DEVOFFLINE ;S FF; Is device spun down? BNEQ 40$ ;S FF; Branch if not BBS #UCB$V_FLOPPY,- ;S FF; Branch if this is a floppy, return UCB$L_DK_FLAGS(R3),50$ ;S FF; immediately CMPB #DC$_DISK,UCB$B_DEVCLASS(R3) ;S FF; Is this really a disk device? BNEQ 20$ ;S FF; Branch if not, can't issue start unit BBSS #SCDRP$V_FLAG_DISK_SPUN_UP,- ;S FF; Attempt to spin up the disk just SCDRP$L_SCSI_FLAGS(R5),- ;S FF; once per PACKACK (branch if this 40$ ;S FF; has already been done) BSBW START_UNIT ;S FF; Start up the drive CMPL R0,#SS$_CANCEL ;S FF; If cancel status BEQL 20$ ;S FF; Then leave now 40$: PUSHL #SPL$C_IOLOCK8 ;S FF; Set the required fork lock PUSHAQ ONE_SECOND ;S FF; Set the time delay in ticks PUSHL SCDRP$PS_KPB(R5) ;S FF; Get address of KPB CALLS # 3, G^ EXE$KP_TQE_WAIT ;S FF; Fork and wait the desired time DECB UCB$B_READY_RETRY(R3) ;S FF; Decrement retry count and try again BGTR 10$ ;S FF; if appropriate 50$: MOVZWL #SS$_MEDOFL,R0 ;S FF; Set bad status BRB 20$ ;S FF; Return with error status .PAGE .SBTTL DATACHECK_CMP - Compare user buffer with datacheck buffer ;+ ; DATACHECK_CMP ; ; This routine is called by IO_DATACHECK and performs the actual comparison of ; the data in the user buffer with the data in the datacheck buffer. In doing ; this, the user's buffer is temporarily mapped into system space in order to ; perform the CMPC3. The SPTEs used to double map the user buffer were allocate ; when the driver was loaded and are shared between all units. ; ; Note that SCDRP$L_SVAPTE describes the datacheck buffer in the AXP version ; of this driver. However, in the VAX version, SCDRP$L_SVAPTE describes the ; current segment of the user buffer. In both cases, the original user buffer ; is described by IRP$L_SVAPTE and IRP$L_BOFF. The mapping to the current user ; buffer segment is obtained by taking into account the bytes that have been ; transferred in prior segments (SCDRP$L_ABCNT) to compute a new SVAPTE and a ; new BOFF into the page mapped by that PTE. ; ; Context: ; ; SCDRP thread ; IPL = Fork ; Fork lock held ; ; Inputs: ; ; R5 - SCDRP address ; ; Outputs: ; ; R0 - Status (SS$_NORMAL or SS$_DATACHECK) ; All other registers preserved ;- DATACHECK_CMP:: ;S FF; Compare user buffer with datacheck buffer .JSB_ENTRY INPUT=,OUTPUT=,PRESERVE= ASSUME DATACHECK_SPTE+4 EQ DATACHECK_SVA MOVQ DATACHECK_SPTE,R0 ;S FF; Get SVA of datacheck SPTEs and SVA ;S FF; which these SPTEs map ; Compute the SVA of the PTE that maps the start of the current ; segment of the user buffer. Note that the PTE that maps the start ; of the user buffer is located via IRP$L_SVAPTE but we have already ; completed SCDRP$L_ABCNT bytes in prior segments. Also, compute the ; BOFF of the start of the segment in this page and the number of pages ; that are spanned by the current segment. ; MOVL SCDRP$L_IRP(R5),R3 ;S FF; R3 = corresponding IRP MOVL IRP$L_BOFF(R3),R6 ;S FF; R6 = byte offset into user buffer ADDL SCDRP$L_ABCNT(R5),R6 ;S FF; (initial boff+completed segments) MOVL MMG$GL_VPN_TO_VA,R2 ;S FF; R2 = number of completed pages so far EVAX_SRL R6,R2,R2 ;S FF; EVAX_SLL R2,#PTE$C_SHIFT_SIZE,R2 ;S FF; R2 = va offset to skip completed PTEs ADDL IRP$L_SVAPTE(R3),R2 ;S FF; R2 = SVAPTE user buffer current segment MOVL MMG$GL_BWP_MASK,R4 ;S FF; EVAX_AND R6,R4,R6 ;S FF; R6 = BOFF into user buffer @SVAPTE=R2 ADDL3 SCDRP$L_BCNT(R5),R6,R4 ;S FF; R4 = byte count of pages in cur seg $BYTES_TO_PAGES - ;S FF; Convert to page count SOURCE_BYTCNT=R4,- ;S FF; DEST_PAGCNT=R4,- ;S FF; ROUNDUP=YES ;S FF; Round up ; ; Load the previously allocated SPTEs to map the current segment of ; the user buffer into system space. ; 10$: EVAX_LDQ R3,(R2) ;S FF; Get user's PTE ADDL #PTE$C_BYTES_PER_PTE,R2 ;S FF; Advance to next PTE ASSUME PTE$M_VALID EQ 1 BLBC R3,15$ ;S FF; Branch if PTE not valid EVAX_SRL R3,#PTE$V_PFN,R3 ;S FF; Else, shift PFN to low-order bits BRB 20$ ;S FF; Join common new PTE builder code 15$: CALL_PTETOPFN SAVE_R0R1=YES ;S FF; If PTE not valid, convert to PFN 20$: EVAX_SLL R3,#PTE$V_PFN,R3 ;S FF; Get PFN in the right place EVAX_OR R3,- ;S FF; Set software PTE bits #,R3 EVAX_STQ R3,(R0) ;S FF; Store the new PTE TBI_SINGLE R1 ;S FF; Do TB invalidation ADDL G^MMG$GL_PAGE_SIZE,R1 ;S FF; Point to next page ADDL #PTE$C_BYTES_PER_PTE,R0 ;S FF; Point to next entry in SPT SOBGTR R4,10$ ;S FF; Loop till done ; ; Compare the user buffer (via the system space mapping just completed) ; with the contents of the data check buffer (nonpaged pool). ; ADDL DATACHECK_SVA,R6 ;S FF; Address of double mapped user buffer CMPC3 SCDRP$L_BCNT(R5),(R6),- ;S FF; Compare user's buffer with datacheck @SCDRP$L_DATACHECK(R5) ;S FF; buffer BNEQ 30$ ;S FF; Branch if mismatch occurred MOVL #SS$_NORMAL,R0 ;S FF; Set success status BRB 40$ ;S FF; And return 30$: MOVL #SS$_DATACHECK,R0 ;S FF; Datacheck error 40$: RSB ;S FF; Return to caller .PAGE .SBTTL SEND_COMMAND - Send a SCSI command ;+ ; SEND_COMMAND ; ; This routines sends a command to the SCSI device. It returns any failing ; port status to the caller. If the port status is success, it checks the ; SCSI status byte. If a check condition status is returned, a request ; sense command is sent to the target and the sense key is translated into a ; VMS status code, which is returned as status. ; ; If the target returns a hardware error, one retry is performed, on the ; assumption that the error could have been caused by vibration or some other ; spurious event. ; ; Context: ; ; SCDRP thread ; IPL = Fork ; Fork lock held ; ; Inputs: ; ; 4(AP) - UCB address ; 8(AP) - PDT address ; 12(AP) - SCDRP address ; ; Outputs: ; ; R0 - Status ; R1,R2 - Destroyed ; All other registers preserved ;- SEND_COMMAND:: ;S FF; Send a SCSI command .CALL_ENTRY INPUT=<>,OUTPUT=,- SCRATCH=, PRESERVE= $ARG_DEF ;S FF; SCDRP Address MOVL ucb(AP),R3 ;S FF; MOVL pdt(AP),R4 ;S FF; MOVL scdrp(AP),R5 ;S FF; MOVL #SENDCMD_RETRY_CNT,- ;S FF; Initialize send command retry counter SCDRP$IS_CMD_RETRY_CNT(R5) ;S FF; SEND_COMMAND_RETRY: .IF DEFINED TRACE_SCSI BSBW TRACE_SCSI_CMD ;S FF Save SCSI command in trace buf .ENDC IF_CANCEL 10$ ;S FF; If server requests ;S FF; cancel, then return SPI$SEND_COMMAND - ;S FF; Send the SCSI command TYPE=SCDRP$IS_QUEUE_CHAR(R5) ;S FF; ... based on queue type .IF DEFINED TRACE_SCSI BSBW TRACE_SCSI_STS ;S FF; Save SCSI status in trace buf .ENDC BLBC R0,20$ ;S FF; Branch on error ASSUME SCSI$C_GOOD EQ 0 BICL3 #SCSI$M_STATUS_BYTE_RESERVED,- ;S DS; Mask off reserved status bits @SCDRP$L_STS_PTR(R5), R1 ;S DS; BNEQ 30$ ;S FF; Branch if bad status CLRL SCDRP$L_CL_RETRY(R5) ;S FF; Reset Class Driver retry 10$: RET ;S FF; Return to caller ; The port returned bad status from SPI$SEND_CMD. ; Conditionally log an error and return to the caller. 20$: CMPL R0,#SS$_ABORT ;S FF; Aborted command? BEQL 10$ ;S FF; Branch if so, don't log an error CMPL R0,#SS$_TIMEOUT ;S FF; Command timed out? BEQL 10$ ;S FF; Branch if so, don't log an error CMPL R0,#SS$_MEDOFL ;S FF; Medium offline? BEQL 10$ ;S FF; Branch if so, don't log an error LOG_ERROR - ;S FF; Log a send command error TYPE=SEND_CMD_ERROR,- VMS_STATUS=R0,- UCB=R3,- SCDRP=R5 BRB 10$ ;S FF; Use common exit ; A bad SCSI status code was returned. If the code is a check condition, then ; send a request sense command to the device. Otherwise, the status code is ; something unexpected. Log an error and return SS$_MEDOFL status. 30$: CMPB #SCSI$C_BUSY,R1 ;S FF; Busy status? BEQL 35$ ;S FF; Branch if so CLRL SCDRP$L_CL_RETRY(R5) ;S FF; Otherwise, clear Class Driver retry CMPB #SCSI$C_CHECK_CONDITION,R1 ;S FF; Check condition status? BEQL 40$ ;S FF; Branch if so LOG_ERROR - ;S FF; Log a send command error TYPE=SEND_CMD_ERROR,- VMS_STATUS=#SS$_NOTQUEUED,- UCB=R3,- SCDRP=R5 32$: MOVL #SS$_MEDOFL,R0 ;S FF; Return a generic status code BRW 10$ ; The device has returned busy status. In order to catch the case in which ; the device continually returns busy status, set a limit on the time a device ; can continuously return busy status. If the doesn't successfully execute ; a command or return a status other than busy within this time, assume ; the device is hung and pull on bus reset. 35$: MOVL SCDRP$L_CL_RETRY(R5),R0 ;S FF; Was Class Driver retry set last time? BNEQ 36$ ;S FF; Branch if so ADDL3 #MAX_BUSY_TIME,- ;S FF; Calculate busy timeout time G^EXE$GL_ABSTIM,- ;S FF; SCDRP$L_CL_RETRY(R5) ;S FF; BRB 55$ 36$: CMPL R0,G^EXE$GL_ABSTIM ;S FF; Had device been retrying for an excessive ;S FF; amount of time BGTRU 55$ ;S FF; Branch if not CLRL SCDRP$L_CL_RETRY(R5) ;S FF; Reset Class Driver retry LOG_ERROR - ;S FF; Log a send command error TYPE=SEND_CMD_ERROR,- VMS_STATUS=#SS$_RETRY,- UCB=R3,- SCDRP=R5 SPI$RESET_SCSI_BUS ;S FF; Reset the SCSI bus to attempt to ;S FF; bring the device back to life BRB 32$ ;S FF; Use common exit ; A check condition status code was returned. Determine if the associated port driver ; supports AUTO_SENSE, and if the AUTO_SENSE buffer is valid. ; AUTO Sense buffer is located at SCDRP$PS_SENSE_BUFFER(R5) 40$: CLRB SCDRP$B_SENSE_KEY(R5) ;S FF; Initialize saved sense key field MOVL R5,SCDRP$PS_PREV_SCDRP(R5) ;S FF; We don't need additional SCDRP ;S FF; so we reference our own ; ; Handle the case of invalid autosense data. ; BBS #SCDRP$V_FLAG_ASENSE_VALID, - ;S FF; Is the autosense data valid ? SCDRP$L_SCSI_FLAGS(R5), 45$ ;S FF; Yes, continue CLRL SCDRP$L_SVA_USER(R5) ;S FF; Else set up for reg dump rtn LOG_ERROR - ;S FF; Log an extended sense data error TYPE=EXTND_SENSE_DATA,- VMS_STATUS=#SS$_NODATA,- UCB=R3,- SCDRP=R5 CLRL SCDRP$PS_PREV_SCDRP(R5) ;S FF; No additional SCDRP MOVL #SS$_MEDOFL,R0 ;S FF; Return general error BRW 10$ ;S FF; Return 45$: MOVL SCDRP$L_SVA_USER(R5),R6 ;S FF; Save user buffer address MOVL SCDRP$L_TRANS_CNT(R5),R7 ;S FF; Save transfer count MOVL SCDRP$PS_SENSE_BUFFER(R5),- ;S FF; Copy Auto Sense buffer address SCDRP$L_SVA_USER(R5) ;S FF; to User buffer address ;S FF; for regdump rtn ; The length field is byte 7 of the sense data and represents the number ; of bytes after itself. So add 8 to get total length. Note: Trans_cnt is ; needed by the register dump routine. MOVL SCDRP$L_SVA_USER(R5),R0 ;S FF; Sense data address MOVZBL SCSI$SNS$B_ADD_SENSE_LEN(R0),R0 ;S FF; Addn sense length ADDL3 #1,#SCSI$SNS$B_ADD_SENSE_LEN,R1 ;S FF; Preceding bytes ADDL3 R1,R0,SCDRP$L_TRANS_CNT(R5) ;S FF; Total sense length BSBW LOG_EXTND_SENSE ;S FF; Log an extended sense error BSBW SAVE_ADDNL_INFO ;S FF; Save any valid additional information ;S FF; from the extended sense data BSBW TRANS_SENSE_KEY ;S FF; Translate the extended sense key to ;S FF; a VMS status code in R0 MOVL R6,SCDRP$L_SVA_USER(R5) ;S FF; Restore user buffer address MOVL R7,SCDRP$L_TRANS_CNT(R5) ;S FF; Restore transfer count CLRL SCDRP$PS_PREV_SCDRP(R5) ;S FF; No additional SCDRP CMPB SCDRP$B_SENSE_KEY(R5),- ;S FF; Device (aborted command) error? #SCSI$C_ABORTED_COMMAND ;S FF; BEQL 57$ ;S FF; Branch if so, retry command CMPB SCDRP$B_SENSE_KEY(R5),- ;S FF; Device (hardware) error? #SCSI$C_HARDWARE_ERROR ;S FF; BEQL 57$ ;S FF; Branch if so, retry command BRW 10$ ;S FF; Branch if not, no need to retry 55$: MOVZWL #SS$_MEDOFL,R0 ;S FF; Assume failure, preload status 57$: DECL SCDRP$IS_CMD_RETRY_CNT(R5) ;S FF; Decrement send command retry count BGEQ SEND_COMMAND_RETRY ;S FF; Branch if retry not exhausted, retry ;S FF; the send command BRW 10$ ;S FF; Otherwise, return to caller .PAGE .SBTTL LOG_EXTND_SENSE - Log an extended sense data error ;+ ; LOG_EXTND_SENSE ; ; This routine logs an extended sense data error. ; ; Certain errors are suppressed including: ; ; - UNIT ATTENTIONS caused by media changes. ; ; - The first UNIT ATTENTION seen after a boot but before a successful PACKACK ; has completed. This unit attention is generally due to a bus reset having ; been issued during a boot or crash. IO_PACKACK sets the ATTN_SEEN flag so ; that any unit attentions seen after the first successful PACKACK will ; generate errors. ; ; - A NOT READY sense key if the original command was a test unit ready. This ; is considered normal as it's likely the disk is in the process of being ; spun up. ; ; - A recoverable error if the addition sense code is RECOVERED ERROR WITH READ ; RETRIES. This can occur due to disk vibrations and is thus considered ; acceptable. ; ; Before logging the error, play a trick with the two SCDRPs currently in use. ; Since we're really interested in logging the contents of the original command, ; and not the request sense command, copy the address of the original command ; buffer into the SCDRP for the request sense command. This causes the contents ; of the original command (a read or write, for example) to be logged along ; with the contents of the request sense data returned from the request sense ; command. ; ; Context: ; ; SCDRP thread ; IPL = Fork ; Fork lock held ; ; Inputs: ; ; R3 - UCB address ; R4 - PDT address ; R5 - SCDRP address for REQUEST_SENSE command ; SCDRP$PS_PREV_SCDRP(R5) - Address of SCDRP for original command ; SCDRP$PS_SVA_USER(R5) - Current Sense mode data ; ; Outputs: ; ; R0-R2 - Destroyed ; All other registers preserved ;- LOG_EXTND_SENSE:: ;S FF; Log an extended sense data error .JSB_ENTRY INPUT=,SCRATCH=,PRESERVE= MOVL SCDRP$L_SVA_USER(R5),R0 ;S FF; Get address of extended sense data EXTZV #SCSI$SNS$V_SENSE_KEY,- ;S FF; Get sense key #SCSI$SNS$S_SENSE_KEY,- ;S FF; SCSI$SNS$R_FLAGS(R0),R1 ;S FF; MOVZBL SCSI$SNS$B_ADD_SENSE_CODE(R0),R7;S FF; Get ASC MOVZBL SCSI$SNS$B_ADD_SENSE_QUAL(R0),R8;S FF; Get ASQ CMPB R1,#SCSI$C_UNIT_ATTENTION ;S FF; Unit attention sense key? BNEQ 10$ ;S FF; Branch if not CMPB #SCSI$C_MEDIA_CHANGE,- ;S FF; Unit attention caused by media change? SCSI$SNS$B_ADD_SENSE_CODE(R0) ;S FF; BEQL 40$ ;S FF; Branch if so, don't log an error BBCS #UCB$V_FIRST_ATTN_SEEN,- ;S FF; Branch if this is the first unit UCB$L_DK_FLAGS(R3),40$ ;S FF; attention seen. Don't log an error ; Log all recoverable errors for floppies. Since errors tend to grow on ; floppies, we want to alert the operator as soon as possible. 10$: BBS #UCB$V_FLOPPY,- ;S FF; Branch if this is a floppy, return UCB$L_DK_FLAGS(R3),15$ ;S FF; immediately CMPL R1,#SCSI$C_RECOVERED_ERROR ;S FF; Recovered error sense key? BNEQ 15$ ;S FF; Branch if not CMPB #SCSI$C_RECOVERED_DATA,- ;S FF; Recovered error with read retries? SCSI$SNS$B_ADD_SENSE_CODE(R0) ;S FF; BEQL 40$ ;S FF; Branch if so, dont log an error ; ; For CDROM devices, don't log commands that fail with a sense key of ; Illegal Command, if original QIO is a READ command (readvblk). ; This will prevent the driver from logging errors for reads ; of the home block on cdrom disks mounted foreign. ; 15$: BBC #UCB$V_CDROM,- ;S FF; Is this device a CD-ROM drive? UCB$L_DK_FLAGS(R3),19$ ;S FF; If NOT then proceed. CMPL R1,#SCSI$C_ILLEGAL_REQUEST ;S FF; Illegal command sense key ;S FF; Returned for reads of audio tracks BNEQ 19$ ;S FF; Branch if not ; ; Look up the orginal commands SCDRP and determine whether or not this ; SCDRP is for a READPBLK QIO. If the command is then this is final condition ; in determining whether or not to log an error. ; PUSHL R0 MOVL SCDRP$PS_PREV_SCDRP(R5),R0 ;S FF; Get original command's SCDRP address CMPB #IO$_READPBLK,- ;S FF; Determine whether the original I/O SCDRP$L_FUNC(R0) ;S FF; was for a READ QIO request? BNEQ 17$ ;S FF; If not continue on, POPL R0 ;S FF; Otherwise, don't log this error BRB 40$ ;S FF; Branch if so, dont log an error ; ; Restore register R0 and continue on determining whether other errors ; need to be loggged. ; 17$: POPL R0 19$: MOVL SCDRP$L_CMD_PTR(R5),R2 ;S FF; Save address of request sense cmd MOVL SCDRP$PS_PREV_SCDRP(R5),R0 ;S FF; Get original command's SCDRP address MOVL SCDRP$L_CMD_PTR(R0),- ;S FF; Copy address of original command to SCDRP$L_CMD_PTR(R5) ;S FF; request sense's SCDRP MOVB #SCSI$C_CHECK_CONDITION,- ;S FF; Original command failed with request @SCDRP$L_STS_PTR(R5) ;S FF; sense status ; Don't log errors with NOT_READY sense keys. CMPB R1,#SCSI$C_NOT_READY ;S FF; Device not ready sense key? BEQL 38$ ;S FF; Yes, don't log an error ; Don't log certain Illegal Request errors that can result from sending ; e.g. a 10-byte mode sense to a target that does not support it. CMPL R1,#SCSI$C_ILLEGAL_REQUEST ;S FF; Illegal command sense key BNEQ 36$ CMPL R8,#0 BNEQ 36$ CMPL R7,#^X20 BEQL 38$ ;S FF; Skip ASC/ASQ of 20/00 CMPL R7,#^X24 BEQL 38$ ;S FF; Skip ASC/ASQ of 24/00 36$: LOG_ERROR - ;S FF; Log an extended sense data error TYPE=EXTND_SENSE_DATA,- VMS_STATUS=#SS$_NORMAL,- UCB=R3,- SCDRP=R5 38$: MOVL R2,SCDRP$L_CMD_PTR(R5) ;S FF; Restore address of request sense cmd 40$: RSB ;S FF; Return to caller .PAGE .SBTTL TRANS_SENSE_KEY - Translate extended sense key to VMS status code ;+ ; TRANS_SENSE_KEY ; ; This routine translates an extended sense key to a VMS status code using ; SENSE_KEY_TABLE. If the sense key is not found in the table (indicating an ; invalid sense key), then a generic VMS status code (SS$_MEDOFL) is returned. ; ; Certain situations cause an additional translation including: ; ; - Recoverable errors with read retries are considered acceptable. Thus, if ; this combination of sense key and additional sense code occurs, change the ; VMS status to SS$_NORMAL. ; ; - If the sense key was NOT_READY and the spinup-in-progress flag is set in ; the UCB, then this is an indication that the device is in the process of ; being spun up. Return a DEVOFFLINE status to allow the WAIT_UNIT_READY ; routine to continue to poll for spinup complete. Otherwise, change the ; status to SS$_MEDOFL to cause mount verification to occur for this device. ; In either case clear the CD_VALID bit to indicate any UCB stored media ; specific data is no longer valid. ; ; Context: ; ; SCDRP thread ; IPL = Fork ; Fork lock held ; ; Inputs: ; ; R5 - SCDRP address - of REQUEST SENSE SCDRP ; ; Outputs: ; ; R0 - VMS status code ; R1,R2 - Destroyed ; All other registers perserved ;- TRANS_SENSE_KEY:: ;S FF; Translate extended sense key to VMS status code .JSB_ENTRY INPUT=,OUTPUT=,PRESERVE=,SCRATCH= MOVL SCDRP$L_SVA_USER(R5),R2 ;S FF; Get address of request sense data EXTZV #SCSI$SNS$V_SENSE_KEY,- ;S FF; Get sense key #SCSI$SNS$S_SENSE_KEY,- ;S FF; SCSI$SNS$R_FLAGS(R2),R1 ;S FF; MOVL SCDRP$PS_PREV_SCDRP(R5),R0 ;S FF; Get original SCDRP MOVB R1,SCDRP$B_SENSE_KEY(R0) ;S FF; Save sense key in it ; If this is a UNIT ATTENTION sense key, a media change additional sense code, ; the device is NOT mounted, and the "spinup-in-progress" bit is set, then ; return success. This situation can occur if a piece of removable media, such ; as a floppy, is changed in between mounts. CMPB R1,#SCSI$C_UNIT_ATTENTION ;S FF; Unit attention sense key? BNEQ 5$ ;S FF; Branch if not CMPB #SCSI$C_MODE_CHANGE,- ;S FF; Unit attention caused by mode select SCSI$SNS$B_ADD_SENSE_CODE(R2) ;S FF; parameters change? BEQL 4$ ;S FF; Branch if so CMPB #SCSI$C_MEDIA_CHANGE,- ;S FF; Unit attention caused by media SCSI$SNS$B_ADD_SENSE_CODE(R2) ;S FF; parameters change? BNEQ 5$ ;S FF; Branch if not BBS #DEV$V_MNT,UCB$L_DEVCHAR(R3),5$ ;S FF; Branch if device is mounted BBC #UCB$V_SPINUP_INPROG,- ;S FF; Branch if NOT waiting for unit ready UCB$L_DK_FLAGS(R3),5$ ;S FF; 3$: MOVZWL #SS$_NORMAL,R0 ;S FF; Set success status BRB 40$ ;S FF; Use common exit path 4$: EXTZV #IRP$V_FCODE,#IRP$S_FCODE,- ;S FF; Extract I/O function code SCDRP$L_FUNC(R0),R3 ;S FF; CMPL #IO$_PACKACK, R3 ;S FF; Was mode_change due to an IO$_PACKACK? BEQL 3$ ;S FF; If so, then unit attention was expected 5$: MOVAL SENSE_KEY_TABLE,R0 ;S FF; Get address of sense key to VMS status MOVL (R0)[R1],R0 ;S FF; Pick up VMS error code ; If this is a recoverable error due to read retries, then treat the error ; as success. CMPL R0,#SS$_RECOVERR ;S FF; Recoverable error? BNEQ 35$ ;S FF; Branch if not CMPB #SCSI$C_RECOVERED_DATA,- ;S FF; Recovered error with read retries? SCSI$SNS$B_ADD_SENSE_CODE(R2) ;S FF; BNEQ 35$ ;S FF; Branch if not CMPB UCB$B_DEVTYPE(R3),#DT$_RZ55 ;S FF; If this is an RZ55 then ;S FF; perform the required workaround. BNEQ 12$ ;S FF; If not a RZ55, continue. BSBW RZ55_WORKAROUND ;S FF; 12$: MOVZWL #SS$_NORMAL,R0 ;S FF; Treat this error as success ; The following code allows allows automatic spin-up of disks to work. If ; a NOT_READY SCSI status is returned by the drive, and the disk spinup is ; in progress, then return DEVOFFLINE status to notify the caller that the ; device is still not fully spun up. Otherwise, return MEDOFL status to ; cause mount verification to kick in. 35$: CMPL R0,#SS$_DEVOFFLINE ;S FF; Device offline status? BNEQ 40$ ;S FF; Branch if not BBS #UCB$V_SPINUP_INPROG,- ;S FF; Branch if spinup is in progress UCB$L_DK_FLAGS(R3),40$ ;S FF; 30$: MOVZWL #SS$_MEDOFL,R0 ;S FF; Otherwise, set medium offline status 40$: RSB ;S FF; Return to caller .PAGE .SBTTL SAVE_ADDNL_INFO - Save additional extended sense information ;+ ; SAVE_ADDNL_INFO ; ; This routine is called whenever valid extended sense data is returned by a ; target to obtain any addition data. If a recoverable or non-recoverable error ; occurred, the additional data specifies the the failing LBN, which is saved ; in the UCB. Higher level routines (READPBLK, WRITEPBLK) use this information ; later to reassign a block if appropriate. ; ; The addition data is saved in the ORIGINAL SCDRP pointed to by ; SCDRP$PS_PREV_SCDRP(R5) (R5 has REQUEST_SENSE SCDRP). ; ; Context: ; ; SCDRP thread ; IPL = Fork ; Fork lock held ; ; Inputs: ; ; R3 - UCB address ; R5 - SCDRP address associated with request sense command ; ; SCDRP$PS_PREV_SCDRP(R5) - Address of original SCDRP ; ; Outputs: ; ; R0,R1 - Destroyed ; ; SCDRP$B_ADDNL_INFO - Additional info field from the extended ; sense data or -1 if no valid data. ; UCB$R_SENSE_INFO - Balance of sense info returned by target. ; UCB$B_SENSE_LEN - Additional info length field set to number of ; additional sense data bytes. ;- SAVE_ADDNL_INFO: .JSB_ENTRY INPUT=,SCRATCH= MOVL SCDRP$PS_PREV_SCDRP(R5),R0 ;S FF; Get original SCDRP address MOVAL SCDRP$L_ADDNL_INFO(R0),R1 ;S FF; Get address of additional info field MOVL SCDRP$L_SVA_USER(R5),R0 ;S FF; Get address of request sense data MOVL #-1,(R1) ;S FF; Assume additional info not valid ASSUME SCSI$SNS$V_VALID EQ 7 TSTB (R0) ;S FF; Valid additional info? BGEQ 30$ ;S FF; Branch if not ADDL #,R0 ;S FF; Additional Information field .REPT 4 MOVB -(R0),(R1)+ ;S FF; Copy a byte of additional info from .ENDR ;S FF; the extended sense data to the UCB. ;S FF; (Note that bytes are reversed in the ;S FF; extended sense data). ;+ ; The code above copies the first four bytes of additional sense data, if the ; valid bit in byte 1 is set. This code segment copies the balanace of the ; additional sense data bytes returned by the target to the UCB. These ; additional bytes are used by the audio portion of the driver, but can be ; used for any device type. ;- 30$: PUSHL R7 ;S FF; Save scratch register MOVL SCDRP$L_SVA_USER(R5),R0 ;S FF; Get address of request sense data MOVAB UCB$R_SENSE_INFO(R3),R1 ;S FF; Get address of additional sense field MOVZBL SCSI$SNS$B_ADD_SENSE_LEN(R0),R7 ;S FF; Get count of addl sense bytes CMPB R7,# ;S FF; Is the count <= the size of buffer? BLEQU 40$ ;S FF; Yes, use this count MOVZBL #,R7 40$: MOVAB SCSI$SNS$R_ADD_INFORMATION(R0),R0 ;S FF; Get address of additional information MOVB R7,UCB$B_SENSE_LEN(R3) ;S FF; Save count of actual number of bytes ;S FF; saved in UCB 50$: MOVB (R0)+,(R1)+ ;S FF; Copy a byte of additional info from ;S FF; the extended sense data to the UCB. SOBGTR R7,50$ ;S FF; Copy all Addl sense bytes POPL R7 ;S FF; Restore scratch register. RSB ;S FF; .DISABLE LSB .PAGE .SBTTL ALLOC_IRP - Allocate an I/O request packet ;+ ; ALLOC_IRP ; ; This routine is called during unit init for the system disk to allocate ; and IRP, which is used to force a PACKACK QIO through the driver. ; ; Context ; ; Fork Thread ; IPL = Fork ; Fork lock held ; ; Inputs: ; ; R5 - UCB address ; ; Outputs: ; ; R3 - IRP address ; R0-R2 - Destroyed ; All other registers preserved ;- ALLOC_IRP:: ;F FF; Allocate an I/O request packet .JSB_ENTRY INPUT=,OUTPUT=,SCRATCH= MOVZWL #IRP$C_LENGTH,R1 ;F FF; Load length of IRP MOVL R5,R3 ;F FF; Copy UCB address MOVL UCB$PS_UNITINIT_KPB(R5),R5 ;F FF; Get KPB address BSBW DK_ALLOC_POOL ;F FF; Get nonpaged pool MOVL R3,R5 ;F FF; Restore UCB address MOVL R2,R3 ;F FF; Set up IRP pointer MOVB #DYN$C_IRP,IRP$B_TYPE(R3) ;F FF; Store type MOVL R5,IRP$L_UCB(R3) ;F FF; Set up UCB address MOVAL W^DEALLOC_IRP,IRP$L_PID(R3) ;F FF; Store deallocation routine address RSB ;F FF; Return to caller .PAGE .SBTTL DEALLOC_IRP - Deallocate an I/O request packet ;+ ; DEALLOC_IRP ; ; The routine deallocates the IPR allocated during unit init to queue a PACKACK ; to the system disk. ; ; Context ; ; Fork Thread ; IPL = Fork ; Fork lock held ; ; Inputs: ; ; R5 - IRP address ; ; Outputs: ; ; R0 - Destroyed ; All other registers preserved ;- DEALLOC_IRP:: ;F FF; Deallocate an I/O request packet .JSB_ENTRY INPUT=,SCRATCH= MOVL IRP$L_UCB(R5),R0 ;F FF; Get UCB address .PRESERVE ATOMICITY DECL UCB$L_QLEN(R0) ;F FF; Adjust queue length .NOPRESERVE ATOMICITY MOVL R5,R0 ;F FF; Copy IRP address BSBW DEALLOC_POOL ;F FF; Deallocate the IRP RSB ;F FF; .PAGE .SBTTL INIT_SCDRP - Initialize an SCDRP ;+ ; INIT_SCDRP ; ; This routine initializes an SCDRP which has already been allocated on the ; kernel process stack. The entire SCDRP is zero'ed and various fields ; are initialized. ; ; Context: ; ; SCDRP thread ; IPL = Fork ; Fork lock held ; ; Inputs: ; ; R3 - UCB address ; R5 - SCDRP address ; ; Outputs: ; ; SCDRP$L_UCB - UCB address ; SCDRP$B_FLCK - Fork lock ; SCDRP$L_CDT - CDT address ; SCDRP$L_SCSI_FLAGS - Initialized ;- INIT_SCDRP:: ;S FF; Initialize an SCDRP .JSB_ENTRY INPUT=,PRESERVE= PUSHR #^M ;S FF; Save regs MOVC5 #0,.,#0,#SCDRP$K_DK_LENGTH,(R5) ;S FF; Initialize the SCDRP POPR #^M ;S FF; Restore regs MOVW #SCDRP$K_LENGTH,- ;S FF; Setup SCDRP length field SCDRP$W_SCDRPSIZE(R5) MOVB #DYN$C_SCDRP,- ;S FF; Setup SCDRP structure type SCDRP$B_CD_TYPE(R5) MOVB UCB$B_FLCK(R3),SCDRP$B_FLCK(R5) ;S FF; Copy the fork lock from UCB to the SCDRP MOVL R3,SCDRP$L_UCB(R5) ;S FF; Save UCB address in SCDRP MOVL UCB$PS_SCDT(R3),SCDRP$L_CDT(R5) ;S FF; Save CDT address in SCDRP MOVZWL UCB$W_PHASE_TMO(R3),- ;S FF; Fill in phase change timeout SCDRP$L_DMA_TIMEOUT(R5) ;S FF; field in the SCDRP MOVZWL UCB$W_DISC_TMO(R3),- ;S FF; Fill in disconnect timeout SCDRP$L_DISCON_TIMEOUT(R5) ;S FF; field in the SCDRP RSB ;S FF; .PAGE .SBTTL DK_ALLOC_POOL - Allocate a block of non-paged pool ;+ ; DK_ALLOC_POOL ; ; This routine was formerly called ALLOC_POOL. Its name was changed to reflect ; a new input: R5 is a KPB address, not an SCDRP address. ; ; This routine allocates a block of non-paged pool no smaller than the ; size of a fork block (allowing COM$DRVDEALMEM to fork on this block ; during deallocation). An extra quadword at the top of the block is reserved ; to save the size field, relieving the caller this responsibility. The caller ; is presented with the address just beyond the reserved quadword. Although a ; word would be sufficient for this field, a quadword is used for allignment ; purposes (some blocks are used as IRPs, which are placed on self-relative ; queues and require quadword allignment). ; ; If an allocation failure occurs, the thread is stalled and wakes up once a ; a second to retry the allocation. ; ; Context: ; ; SCDRP/FORK thread ; IPL = Fork ; Fork lock held ; ; Inputs: ; ; R1 - Size of block to allocate ; R3 - UCB address ; R5 - KPB address ; ; Outputs: ; ; R0 - Output status ; R1 - Size of block allocated ; R2 - Address of allocated block ; -8(R2) - Length of allocated block (used by DEALLOC_POOL) ; All other registers perserved ; ; The allocated pool is given the following header: ; ; +-------------------------------------------------------+ ; | requesting UCB | ; +-------------------------------------------------------+ ; | requesting KPB | ; +-------------+-------------+---------------------------+ ; |DYN$C_SCSICLS| DYN$C_MISC | size | ; +-------------+-------------+---------------------------+ ; | longword allocation length | ; +-------------------------------------------------------+ ; ; The returned R2 points to the first byte following this ; header. ;- DK_ALLOC_POOL:: ;SF FF; Allocate a block of non-paged pool .JSB_ENTRY INPUT=,OUTPUT=,PRESERVE= ADDL #DK$DYN$C_LENGTH,R1 ;SF FF; Reserve space for a DK pool header CMPL R1,#FKB$C_LENGTH ;SF FF; Requested size smaller than fork block? BGEQ 10$ ;SF FF; Branch if not MOVL #FKB$C_LENGTH,R1 ;SF FF; Use fork block size as minimum 10$: PUSHL R1 ;SF FF; Save allocation length JSB G^EXE$ALONONPAGED ;SF FF; Allocate a block BLBC R0,20$ ;SF FF; Branch if error ADDL #4,SP ;SF FF; Remove allocation length from stack PUSHR #^M ;SF FF; Save regs MOVC5 #0,.,#0,R1,(R2) ;SF FF; Initialize the packet POPR #^M ;SF FF; Restore regs MOVL R3,DK$DYN$PS_UCB(R2) ;SF FF; Put UCB address in header MOVL R5,DK$DYN$PS_KPB(R2) ;SF FF; Put KPB address in header MOVW R1,DK$DYN$W_SIZE(R2) ;SF FF; Put structure size in header MOVB #DYN$C_MISC,DK$DYN$B_TYPE(R2) ;SF FF; Put structure type in header MOVB #DYN$C_SCSICLS,DK$DYN$B_SUBTYPE(R2) ;SF FF; Put structure sub-type in header MOVL R1,DK$DYN$L_ALLOC_LEN(R2) ;SF FF; Put longword size in header ADDL #DK$DYN$C_LENGTH,R2 ;SF FF; Skip point caller past the header SUBL #DK$DYN$C_LENGTH,R1 ;SF FF; don't give header memory to caller RSB ;SF FF; Return to caller, r0 = status ; ; A pool allocation failure occurred. ; Come back in 2 seconds and retry the operation until successful ; 20$: MOVZBL UCB$B_FLCK(R3),-(SP) ;F FF; Set the required fork lock PUSHAQ TWO_SECONDS ;F FF; Set the time delay in ticks PUSHL R5 ;F FF; Set the KPB address CALLS # 3, G^ EXE$KP_TQE_WAIT ;F FF; Fork and wait the desired time POPL R1 ;SF FF; Restore allocation length BRW 10$ ;SF FF; Try again .PAGE .SBTTL DEALLOC_POOL - Deallocate a block of non-paged pool ;+ ; DEALLOC_POOL ; ; This routine deallocates a block of non-paged pool. The block must ; have been allocated by DK_ALLOC_POOL. ; ; Context: ; ; SCDRP/FORK thread ; IPL = Fork ; Fork lock held ; ; Inputs: ; ; R0 - Address of block to deallocate ; DK$DYN$C_LENGTH bytes preceding this address are assumed to have ; been initialized by DK_ALLOC_POOl ; ; Outputs: ; ; R0 - Destroyed ; All other registers perserved ;- DEALLOC_POOL:: ;SF FF; Deallocate a block of non-paged pool .JSB_ENTRY INPUT=,SCRATCH=,PRESERVE= SUBL #DK$DYN$C_LENGTH,R0 ;SF FF; Point to beginning of allocated block PUSHL DK$DYN$L_ALLOC_LEN(R0) ;SF FF; Set block size parameter PUSHL R0 ;SF FF; Set block address parameter CALLS #2,g^EXE_STD$DEANONPGDSIZ ;SF FF; Call deallocation routine RSB ;SF FF; .PAGE .SBTTL ALLOC_IRPE - Allocate IRP Extension. ;+ ; ALLOC_IRPE: ; ; This routine is called to allocated and link in an IRP extension (IRPE). ; IRP extensions are used in cases where a single IRP doesn't have enough ; space to maintain the entire state of a single I/O request. In this driver ; there are cases where a single request may have 1, 2 or 3 user buffers that ; must be made accessable to the startio routine. For this purpose we use 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. ; ; ; Context: ; ; User thread ; IPL = ASTDEL ; Locks - none ; ; Inputs: ; ; R3 - Original IRP ; Other registers have normal FDT context. ; ; Outputs: ; R0 - Status ; R1 - size allocated ; R2 - Address of allocated IRPE. ; R3 - Address of allocated IRPE. ; R4 - possible use by exe$allocirp ; ; Original IRP field modified: ; IRPE$L_EXTEND - Assigned address of IRPE ; IRP$L_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:: ;U A; Allocate IRP Extension. .JSB_ENTRY INPUT=,OUTPUT= CALL_ALLOCIRP ;U A; Allocate extended IRP BLBC R0,50$ ;U A; Exit if fails MOVL R2,IRP$L_EXTEND(R3) ;U A; Link orig IRP to extended IRP. BISL #IRP$M_EXTEND,- ;U A; Flag this as an extended IRP IRP$L_STS(R3) ;U A; MOVL IRP$L_FUNC(R3),- ;U A; Copy IO function code. IRP$L_FUNC(R2) ;U A; MOVL R3,IRP$L_SEQNUM(R2) ;U A; Save Original IRP Address MOVL R2,R3 ;U A; Get IRPE address MOVL #,- ;U A; Signal to IOPOST that IRP$L_STS(R3) ;U A; this is a Direct, Read, Physical I/O ;+ ; Clear the following fields in the IRPE, since the exit code in the audio ; path determines whether there are sense or destination buffers used for this ; I/O, based on whether or not these fields are zero. ;- CLRL IRPE$L_SVAPTE1(R3) ;U A; Clear SVAPTE1 in IRPE CLRL IRPE$L_SVAPTE2(R3) ;U A; Clear SVAPTE2 in IRPE CLRL IRP$L_PID(R3) ;U A; If not clear SENSE buffer assumed! CLRL IRP$L_OBCNT(R3) ;U A; If not zero destination buffer used. CLRL IRP$L_DIAGBUF(R3) ;U A; Clear SVAPTE for IRPE(SENSE) buffer 50$: RSB ;U A; .PAGE .SBTTL DEALLOC_IRPE - Deallocate an I/O request packet extension ;+ ; ; DEALLOC_IRPE - SUBROUTINE TO DEALLOCATE AN I/O REQUEST PACKET EXTENSION ; ; Context: ; ; User thread ; IPL = ASTDEL ; Locks - none ; ; 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:: ;U A; Deallocate an I/O request packet extension .JSB_ENTRY INPUT=,PRESERVE= MOVL IRP$L_EXTEND(R2),R0 ;U A; GET IRPE ADDRESS BEQL 20$ ;U A; BR IF NONE CLRL IRP$L_EXTEND(R2) ;U A; Delete extended IRPE pointer. BICL #IRP$M_EXTEND,- ;U A; CLEAR EXTEND FLAG IRP$L_STS(R2) ;U A; JSB G^EXE$DEANONPAGED ;U A; DEALLOCATE IRPE 20$: RSB ;U A; DEALLOC_IRPE .PAGE .SBTTL GET_DEVICE_TYPE - Determine device type from inquiry data ;+ ; GET_DEVICE_TYPE ; ; This routine translates the product ID field from the INQUIRY data to ; a VMS device type. In addition, it fills in the media ID, disconnect and ; synchronous flags, and device timeout values in the UCB. ; ; If the revision field in the inquiry data indicates the device is out-of-rev, ; then don't allow disconnects for this device. It's been shown that one of the ; hardest things to get right is the disconnect/reconnect logic. Thus, if the ; device is not up to rev there's a good chance this logic is broken and ; attempting to use the device with disconnects enabled could cause bus problems. ; ; Context: ; ; SCDRP thread ; IPL = Fork ; Fork lock held ; ; Inputs: ; ; R0 - Address of INQUIRY data ; R3 - UCB address ; ; Outputs: ; ; R0,R1 - Destroyed ;- GET_DEVICE_TYPE:: ;S FF; Determine device type from inquiry data .JSB_ENTRY INPUT=,SCRATCH=,PRESERVE= MOVAL SCSI_DEVICE_TABLE,R1 ;S FF; Get product ID translation table 10$: TSTB DTYP_TYPE(R1) ;S FF; End of table BEQL 40$ ;S FF; Branch if so CMPL DTYP_ID_STRING(R1),- ;S FF; Product IDs match? SCSI$INQ$B_PRODUCT_ID(R0) ;S FF; (low-order) BEQL 20$ ;S FF; Branch if so ADDL #DTYP_TABLE_ENTRY_SIZE,R1 ;S FF; Advance to next entry in table BRB 10$ ;S FF; Try next entry 20$: CMPL DTYP_ID_STRING+4(R1),- ;S FF; Product IDs match? SCSI$INQ$B_PRODUCT_ID+4(R0) ;S FF; (high-order) BEQL 21$ ;S FF; Branch if so ADDL #DTYP_TABLE_ENTRY_SIZE,R1 ;S FF; Advance to next entry in table BRB 10$ ;S FF; Try next entry in table 21$: MOVB DTYP_TYPE(R1),- ;S FF; Found a match, extract device type UCB$B_DEVTYPE(R3) BSBW CHECK_REV_LEVEL ;S FF; Check for an acceptable rev level BBC #DEV$V_DTN,- ;S FF; If DTN bit is set, handle DDR UCB$L_DEVCHAR2(R3),22$ ;S FF; mechanism PUSHR #^M ;S FF; Save registers across call PUSHL R3 ;S FF; ioc$remove_device_type(&ucb) CALLS #1,G^IOC$REMOVE_DEVICE_TYPE ;S FF; POPR #^M ;S FF; Restore registers ; ; Detemine if device is a RAID device ; 22$: CMPL #^A/ADP-/,SCSI$INQ$B_PRODUCT_ID(R0) ;S FF; Is this an HSZ10 proto? BEQL 23$ ;S FF; Yes, set RAID device CMPL #^A/HSZ1/,SCSI$INQ$B_PRODUCT_ID(R0) ;S FF; Is this an HSZ10? BEQL 23$ ;S FF; Yes, set RAID device CMPL #^A/HSZ2/,SCSI$INQ$B_PRODUCT_ID(R0) ;S FF; Is this an HSZ20? BEQL 23$ ;S FF; Yes, set RAID device CMPL #^A/HSZ4/,SCSI$INQ$B_PRODUCT_ID(R0) ;S FF; Is this an HSZ40? BEQL 23$ ;S FF; Yes, set RAID device BRB 24$ 23$: BISL #UCB$M_RAID,UCB$L_DK_FLAGS(R3) ;S FF; RAID device BRB 25$ 24$: BICL #UCB$M_RAID,UCB$L_DK_FLAGS(R3) ;S FF; Not RAID device 25$: MOVB DTYP_FLAGS(R1),R2 ;S FF; Get disconnect, synchronous flags ASSUME UCB$V_DISCONNECT+1 EQ UCB$V_SYNCHRONOUS INSV R2,#UCB$V_DISCONNECT,- ;S FF; Initialize these flags in the UCB #2,UCB$L_DK_FLAGS(R3) ;S FF; ; ; Determine if device supports command queueing ; BBC #UCB$V_PORT_CMDQ, - ;S FF; Make sure port supports CMDQ UCB$L_DK_FLAGS(R3), 28$ ;S FF; BBC #UCB$V_DISCONNECT,- ;S FF; Make sure port supports disconnects UCB$L_DK_FLAGS(R3), 28$ ;S FF; CMPB #SCSI$C_ANSI_SCSI_1,- ;S FF; Check SCSI Version CCS(SCSI-1) or SCSI 2? UCB$B_SCSI_VERSION(R3) ;S FF; BGEQ 28$ ;S FF; Br if CCS or SCSI-1 BBC #SCSI$INQ$V_CMDQUE,- ;S FF; Br if CmdQue bit clear SCSI$INQ$R_FLAGS(R0),28$ BISL #UCB$M_CMDQ,UCB$L_DK_FLAGS(R3) ;S FF; CMDQ supported! 28$: MOVL DTYP_MEDIA_ID(R1),- ;S FF; Save media ID UCB$L_MEDIA_ID(R3) MOVW DTYP_PHASE_TMO(R1),- ;S FF; Phase change timeout UCB$W_PHASE_TMO(R3) MOVW DTYP_DISC_TMO(R1),- ;S FF; Disconnect timeout UCB$W_DISC_TMO(R3) CMPW #DEFAULT_DISCONNECT_TIMEOUT,- ;S FF; Is this an RZ type device? UCB$W_DISC_TMO(R3) BNEQ 29$ ;S FF; BR if it is not ADDL G^SGN$GL_VMS7,- ;S FF; Alter the time out by the UCB$W_DISC_TMO(R3) ;S FF; specified amount. 29$: BBC #UCB$V_OUT_OF_REV,- ;S FF; Branch if the device is NOT out-of-rev UCB$L_DK_FLAGS(R3),30$ ;S FF; .IF DEFINED RZ74_CACHE CMPB #DT$_RZ74,UCB$B_DEVTYPE(R3) ;S FF; Is this the RZ74 disk drive? BEQL 30$ ;S FF; If so, don't disable disconnects .ENDC BICB #UCB$M_DISCONNECT,- ;S FF; If the device is out-of-rev, then UCB$L_DK_FLAGS(R3) ;S FF; don't allow disconnects 30$: CMPB #DT$_GENERIC_DK,- ;S FF; Is the device in the table? UCB$B_DEVTYPE(R3) ;S FF; BNEQ 35$ ;S FF; Yes, don't do dynamic naming ; ; At this point: ; R0 = Address of the inquiry data ; R3 = UCB ; ; Register usage: ; R0 = input name pointer ; R1 = output name pointer ; R2 = original SP ; R4 = end of field pointer ; R5 = "last character was a blank" flag ; R6 = scratch ; ; First, process the Vendor ID, compressing multiple blanks into one. ; MOVL SP,R2 ;S FF; Save stack pointer CLRL R5 ;S FF; Initialize "last was blank" flag BICL #7,SP ;S FF; Quad align the stack SUBL #32,SP ;S FF; Allocate space for name and such MOVL SP,R1 ;S FF; Setup pointer into name buffer MOVAB SCSI$INQ$B_VENDOR_ID(R0),R0 ;S FF; Point to vendor ID MOVAB SCSI$INQ$S_VENDOR_ID(R0),R4 ;S FF; Vendor ID is 8 bytes long 301$: CMPL R0,R4 ;S FF; End of field? BEQL 320$ ;S FF; Yes, continue with product ID CMPB #^X20,(R0)+ ;S FF; Is character a blank BNEQ 302$ ;S FF; No, clear flag and copy BLBS R5,301$ ;S FF; If previous char blank, skip this one MOVL #1,R5 ;S FF; Otherwise, set "last was blank" flag MOVB #^X20,(R1)+ ;S FF; and store a blank BRB 301$ ;S FF; Back for next character 302$: CLRL R5 ;S FF; Clear "last was blank" flag MOVB -1(R0),(R1)+ ;S FF; and copy the character BRB 301$ ;S FF; Back for next character ; ; Now, setup to process the product ID. Again, compress multiple blanks ; into one. Also, the copyright symbol "(C)" or "(c)" is treated as a ; name terminator. ; 320$: MOVAB SCSI$INQ$S_PRODUCT_ID(R0),R4 ;S FF; Product ID is 16 bytes long BLBS R5,321$ ;S FF; If trailing blank on Vendor, continue MOVL #1,R5 ;S FF; Otherwise, insert a blank between MOVB #^X20,(R1)+ ;S FF; vendor and product IDs 321$: CMPL R0,R4 ;S FF; See if we have a full type name BEQL 330$ ;S FF; Yes, we're done CMPB #^X20,(R0)+ ;S FF; Is the next character a blank? BNEQ 322$ ;S FF; No, go check for copyright BLBS R5,321$ ;S FF; If previous char blank, skip this one MOVL #1,R5 ;S FF; Otherwise, set "last was blank" flag MOVB #^X20,(R1)+ ;S FF; and store a blank BRB 321$ ;S FF; Back for next character ; ; Check for copyright... ; 322$: CMPB #^A/(/,-1(R0) ;S FF; Was character a "(" BNEQ 323$ ;S FF; Nope, go copy it BICB3 #^X20,(R0),R6 ;S FF; Convert next char to upper case CMPB #^A/C/,R6 ;S FF; See if it's a "C" BNEQ 323$ ;S FF; Nope, just copy data CMPB #^A/)/,1(R0) ;S FF; Look for closing paren BEQL 330$ ;S FF; Found copyright, so we're done 323$: CLRL R5 ;S FF; Clear "last was blank" flag MOVB -1(R0),(R1)+ ;S FF; and copy the character BRB 321$ ;S FF; Back for next character 330$: SUBL3 SP,R1,R0 ;S FF; Calculate name length SUBL R5,R0 ;S FF; Remove any trailing blank PUSHAL 28(SP) ;S FF; Add dynamic name: PUSHL R3 ;S FF; status = ioc$add_device_type( PUSHL R0 ;S FF; &name, namelen, &ucb &(&dtn)) PUSHAB 12(SP) ;S FF; (Buffer is now under 3 longword args) CALLS #4,G^IOC$ADD_DEVICE_TYPE MOVL R2,SP ;S FF; Clean the stack 35$: RSB ;S FF;S FF; Return to caller ; No matching product ID field was found in the table. Assume this is a generic ; SCSI disk. 40$: MOVAL GENERIC_SCSI_DISK,R1 ;S FF; Use generic SCSI disk type BRB 21$ ;S FF; .PAGE .SBTTL CHECK_REV_LEVEL - Check for an out-of-rev device ;+ ; CHECK_REV_LEVEL ; ; This routine checks the revision level returned in the inquiry data with ; the minimum revision level stored in the SCSI_DEVICE_TABLE. If the device's ; revision level is below the minimum allowed, an invalid inquiry data error ; is logged. ; ; Note: since the revision field in the inquiry data is a 4 byte ascii string, ; the low-order byte is the most significant. Thus, the bytes must be compared ; from low byte to high byte, and a CMPL can not be used to perform the ; comparison. ; ; Context: ; ; SCDRP thread ; IPL = Fork ; Fork lock held ; ; Inputs: ; ; R0 - Address of inquiry data retured by target ; R1 - Address of table entry in SCSI_DEVICE_TABLE ; R3 - UCB address ; ; Outputs: ; ; All registers preserved ; An error is logged of the device is out-of-rev ;- CHECK_REV_LEVEL:: ;S FF; Check for an out-of-rev device .JSB_ENTRY INPUT=,PRESERVE= BICL #UCB$M_OUT_OF_REV,- ;S FF; Clear out-of-rev flag in the UCB UCB$L_DK_FLAGS(R3) ;S FF; ADDL #DTYP_MINREV,R1 ;S FF; Min rev level in SCSI_DEVICE_TABLE MOVAL SCSI$INQ$B_PRODUCT_REVISION(R0),R2 ;S FF; Address of rev level in inquiry data .REPT SCSI$INQ$S_PRODUCT_REVISION ;S FF; Check four bytes of revision data CMPB (R2)+,(R1)+ ;S FF; Check a byte of revision BGTRU 10$ ;S FF; Branch if above minimum BLSSU 20$ ;S FF; Branch of below minimum .ENDR ;S FF; 10$: RSB ;S FF; Return to caller 20$: LOG_ERROR - ;S FF; Log an invalid inquiry data error TYPE=INV_INQUIRY_DATA,- ;S FF; VMS_STATUS=#SS$_NORMAL,- ;S FF; UCB=R3 ;S FF; BISL #UCB$M_OUT_OF_REV,- ;S FF; Set out-of-rev flag in the UCB UCB$L_DK_FLAGS(R3) ;S FF; BRB 10$ ;S FF; Use common exit .PAGE .SBTTL SETUP_CMD - Common setup for all SCSI commands ;+ ; SETUP_CMD ; ; This routine common setup prior to the sending of a SCSI command. This ; includes allocating a command buffer, filling in the pointers in the SCDRP ; to the command and status fields, copying the SCSI command to the command ; buffer, allocating an S0 "user" buffer if the command requires transferring ; data to or from the class driver, filling in the SCDRP fields used to map ; this buffer, and mapping the buffer. ; ; Since this routine calls SPI$CMD_BUFFER_ALLOC, which can suspend ; the thread, the return PC must be saved in the SCDRP. ; ; Context: ; ; SCDRP thread ; IPL = Fork ; Fork lock held ; ; Inputs: ; ; 4(AP) - Pointer to entry in SCSI_CMD table ; 8(AP) - UCB address ; 12(AP) - PDT address ; 16(AP) - SCDRP address ; ; Outputs: ; ; R0 - Status ; R1,R2 - Destroyed ; ; SCDRP$L_CMD_BUF - Address of SCSI command buffer ; SCDRP$L_CMD_PTR - Address of SCSI command ; SCDRP$L_STS_PTR - Address to save SCSI status byte ; SCDRP$L_BOFF - Byte offset of S0 "user" buffer ; SCDRP$L_SVAPTE - SVAPTE of of S0 "user" buffer ; ; If a class driver private buffer is allocated elsewhere (e.g. mode sense processing), then the allocating routine fills in the ; following fields. Otherwise, SETUP_CMD has these outputs: ; ; SCDRP$L_SVA_USER- Address of S0 "user" buffer ; SCDRP$L_BCNT - Length of S0 "user" buffer (should be >= alloc len inCDB) ; IRP$V_FUNC - SET/CLEAR to indicate READ/WRITE from S0 "user" buffer ; ; ;- SETUP_CMD:: ;S FF; Common setup for all SCSI commands .CALL_ENTRY INPUT=<>,OUTPUT=,SCRATCH=,- PRESERVE= $ARG_DEF ;S FF; SCDRP Address MOVL cmd(AP),R2 ;S FF; MOVL ucb(AP),R3 ;S FF; MOVL pdt(AP),R4 ;S FF; MOVL scdrp(AP),R5 ;S FF; MOVZBL (R2),R1 ;S FF; Get size SCSI command ADDL #SCSI_CMD_BUF_OVHD,R1 ;S FF; Add in command buffer overhead SPI$CMD_BUFFER_ALLOC RET_PARAM_PTR=R1 ;S FF; Allocate a command buffer MOVB #-1,(R1) ;S FF; Initialize status field MOVAL (R1)+,SCDRP$L_STS_PTR(R5) ;S FF; Address to put SCSI status byte MOVL R1,SCDRP$L_CMD_PTR(R5) ;S FF; Save address of SCSI command MOVZBL (R2)+,R0 ;S FF; Get SCSI command length MOVL R0,(R1)+ ;S FF; Save length in command buffer ; NOTE: SCSI-2 suggests that the SCSI LUN field be left with a zero and ; that message handshaking between the port driver and target convey LUN ; information. UCB$B_LUN(R3) is this value ASHL #-1,R0,R0 ;S FF; Change byte count to word count 10$: MOVW (R2)+,(R1)+ ;S FF; Copy a byte of SCSI command SOBGTR R0,10$ ;S FF; Repeat for entire SCSI command CVTWL (R2),R1 ;S FF; Get length of send data buffer BLSS 20$ ;S FF; Branch if negative, no system buffer ;S FF; involved, leave SCDRP$L_BCNT unchanged BEQL 30$ ;S FF; Branch if zero length, zero SCDRP$L_BCNT .BRANCH_LIKELY BBC - ;S FF; Did we already allocate a buffer? #SCDRP$V_FLAG_CL_PRIVATE_BUFF,- SCDRP$L_SCSI_FLAGS(R5),15$ ; Code for pre-supplied private buffer TSTL SCDRP$L_BCNT(R5) ;S FF; Branch if zero length BEQL 20$ MOVL SCDRP$L_SVA_USER(R5),R1 ;S FF; Get start address of buffer ADDL #2,R2 ;S FF; Skip over length field BRW 18$ ;S FF; Branch to common code ; Code to supply a buffer now 15$: PUSHL R2 ;S FF; Save R2 PUSHL R5 ;S FF; Save SCDRP address MOVL SCDRP$PS_KPB(R5),R5 ;S FF; Get KPB address BSBW DK_ALLOC_POOL ;S FF; Allocate a buffer to receive response POPL R5 ;S FF; Restore SCDRP address MOVL R2,R1 ;S FF; Copy buffer address POPL R2 ;S FF; Restore R2 MOVL R1,SCDRP$L_SVA_USER(R5) ;S FF; Save address of allocated buffer MOVZWL (R2)+,SCDRP$L_BCNT(R5) ;S FF; Save length of transfer ; Common code 18$: CLRL SCDRP$L_PAD_BCNT(R5) ;S FF; No padding required INSV (R2),#IRP$V_FUNC,#1,- ;S FF; Set/clear FUNC bit to indicate READ/ SCDRP$IS_STS(R5) ;S FF; WRITE function MOVL G^MMG$GL_BWP_MASK,R2 ;S FF; Mask of BWP portion of virtual addr EVAX_AND R1,R2,R2 ;S FF; Get byte offset within page MOVL R2,SCDRP$L_BOFF(R5) ;S FF; Save byte offset PUSHL R3 ;S FF; Save R3 MOVL SCDRP$L_SVA_USER(R5),R2 ;S FF; Get user buffer address JSB G^MMG$SVAPTECHK ;S FF; Get SVAPTE of allocated system buffer MOVL R3,SCDRP$L_SVAPTE(R5) ;S FF; Save SVAPTE in SCDRP POPL R3 ;S FF; Restore R3 BISL #SCDRP$M_FLAG_S0BUF!- ;S FF; This buffer is an S0 "user" buffer SCDRP$M_FLAG_BUFFER_MAPPED,- ;S FF; and it has been mapped SCDRP$L_SCSI_FLAGS(R5) ;S FF; SPI$BUFFER_MAP PRIO=HIGH ;S FF; Map the "user" buffer for read access 20$: MOVZWL #SS$_NORMAL,R0 ;S FF; Set success status RET ;S FF; 30$: CLRL SCDRP$L_BCNT(R5) ;S FF; No data being transferred BRB 20$ ;S FF; Use common exit .PAGE .SBTTL CLEANUP_CMD - Common cleanup for all SCSI commands ;+ ; CLEANUP_CMD ; ; This routine performs common cleanup after the sending of a SCSI command ; including unmapping the user buffer and deallocating the command buffer. ; ; Context: ; ; SCDRP thread ; IPL = Fork ; Fork lock held ; ; Inputs: ; ; 4(AP) - PDT address ; 8(AP) - SCDRP address ; ; Outputs: ; ; R2 - Destroyed ; All other registers preserved ;- CLEANUP_CMD:: ;S FF; Common cleanup for all SCSI commands .CALL_ENTRY INPUT=<>,SCRATCH=,PRESERVE= $ARG_DEF ;S FF: SCDRP Address MOVL pdt(AP),R4 ;S FF; MOVL scdrp(AP),R5 ;S FF; BBCC #SCDRP$V_FLAG_BUFFER_MAPPED,- ;S FF; Branch if no buffer has been mapped SCDRP$L_SCSI_FLAGS(R5),10$ ;S FF; SPI$BUFFER_UNMAP ;S FF; Unmap the mapped buffer 10$: BBCC #SCDRP$V_FLAG_S0BUF,- ;S FF; Branch if this is not an S0 "user" SCDRP$L_SCSI_FLAGS(R5),- ;S FF; buffer 20$ ;S FF; MOVL SCDRP$L_SVA_USER(R5),R0 ;S FF; Get address of S0 user buffer CLRL SCDRP$L_SVA_USER(R5) ;S FF; Buffer no longer owned BBS - ;S FF; Private buffer to be handled by other code #SCDRP$V_FLAG_CL_PRIVATE_BUFF,- SCDRP$L_SCSI_FLAGS(R5),20$ BSBW DEALLOC_POOL ;S FF; Deallocate the buffer 20$: MOVL SCDRP$L_CMD_BUF(R5),R0 ;S FF; Get address of command buffer SPI$CMD_BUFFER_DEALLOC ;S FF; Deallocate the command buffer 30$: RET ;S FF; .PAGE .SBTTL SETUP_SENSE_BUFFER - Setup user sense data buffer ;+ ; SETUP_SENSE_BUFFER: ; ; This routine is called to allocate an IRPE and prepare the user supplied ; sense data buffer. The sense data buffer will be locked down and double ; mapped. The mapping information for this buffer will be saved in the IRPE. ; ; Context: ; ; User thread ; IPL = ASTDEL ; Locks - none ; ; Inputs: ; ; R3 - Original IRP ; R5 - UCB Address ; Other registers have normal FDT context. ; ; Outputs: ; ; R0 - Status ;- SETUP_SENSE_BUFFER:: ;U A; Setup user sense data buffer .JSB_ENTRY INPUT=,OUTPUT=,SCRATCH= BSBW ALLOC_IRPE ;U A; Allocate an IRP extension BLBC R0,90$ ;U A; Exit if no IRPE ;+ ; Now, lockdown and double map the users sense buffer, then 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 UCB$PS_AUCB_ADDR(R5),R2 ;U A; Get AUCB pointer MOVL CD_SENSE_ADDR(R2),R0 ;U A; Get address of SENSE buffer. MOVL CD_SENSE_CNT(R2),R1 ;U A; Get size of SENSE buffer. MOVL R1,IRP$L_BCNT(R3) ;U A; Copy Byte count to IRPE MOVL G^MMG$GL_BWP_MASK,R2 ;U A; Byte-within-page mask EVAX_AND R0,R2,R2 ;U A; Get new byte offset within page MOVL R2,IRP$L_BOFF(R3) ;U A; Update BOFF BSBW AUDIO_MAP_PAGE ;U A; Locks and Maps sense buffer BLBC R0,90$ ;U A; Return on error ;+ ; 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),- ;U A; Save SVAPTE of system buffer IRP$L_PID(R3) ;U A; Not really the PID field! MOVL IRP$L_OBCNT(R3),- ;U A; Save number of PTE's allocated IRP$L_AST(R3) ;U A; Not really the AST field MOVL IRP$L_SVAPTE(R3),- ;U A; Copy SVAPTE created by exe$writelock IRP$L_DIAGBUF(R3) ;U A; Not really the DIAGBUF field MOVL IRP$L_SEGVBN(R3),- ;U A; S0 SVA User buffer saved here IRP$L_ASTPRM(R3) ;U A; Not really the AST param field CLRL IRP$L_WIND(R3) ;U A; Clear IRP WIND field CLRL IRP$L_SVAPTE(R3) ;U A; Clear invalid svapte info. CLRL IRP$L_OBCNT(R3) ;U A; Clear what will be dest page count 90$: MOVL IRP$L_SEQNUM(R3),R3 ;U A; Restore Original IRP Address RSB ;U A; .PAGE .SBTTL LOG_ERROR - Write an entry to the errorlog file ;+ ; LOG_ERROR ; ; This routine writes an entry to the errorlog file. If the device is offline, ; no error is logged. This prevents the errorlog file from being filled up while ; the class driver does it its periodic polling of devices that have been set ; offline. The assumption is that the initial error that caused the device to ; be placed offline has been logged and that subsequent errorlog entries would ; be redundent. ; ; If the device class and/or type fields in the UCB have not been initialized, ; then fill in "DISK" and "GENERIC SCSI DISK" respectively so the packet can be ; properly formatted by ERF. This situation could arise if invalid inquiry data ; is received for the device, preventing these fields from being filled in ; properly. ; ; If no I/O is active for this device (for example, if an error is detected ; during unit initialization) log a device attention. Otherwise, log a ; device error and then release the errorlog entry. This prevents errors from ; being lost if multiple errors are logged for a single QIO. ; ; Some type of errors are logged just once per system boot. For example, if ; invalid mode sense data is returned by the target, just one INVALID_SENSE_DATA ; error is logged to prevent filling the errorlog with duplicate packets. The ; DUPLICATE_ERR_MASK table specifies those error types which should be logged ; just once. A bitmask in the UCB records those error types that have been ; logged already. ; ; Inputs: ; ; R4 - SCDRP address (or zero if none) ; R5 - UCB address ; ; Outputs: ; ; All registers preserved ;- LOG_ERROR:: ;? ??; Write an entry to the errorlog file .JSB_ENTRY INPUT=,PRESERVE= BBS #UCB$V_DISABL_ERRLOG,- ;? ??; Branch if errorlogging is disabled UCB$L_DK_FLAGS(R5),40$ ;? ??; for this device BBC #UCB$V_ONLINE,UCB$L_STS(R5),40$ ;? ??; Branch if device is offline MOVL UCB$L_ERROR_TYPE(R5),R7 ;? ??; Save error type BBCS R7,UCB$L_ERR_MASK(R5),5$ ;? ??; Branch if error type not logged yet BBC R7,DUPLICATE_ERR_MASK,40$ ;? ??; Branch if this is a type of error ;? ??; that should be logged just once 5$: MOVB UCB$B_DEVTYPE(R5),R9 ;? ??; Save SCSI device type BNEQ 10$ ;? ??; Branch if device type known MOVB #DT$_GENERIC_DK,- ;? ??; Fill in generic type so ERF can UCB$B_DEVTYPE(R5) ;? ??; translate the errlog packet 10$: MOVB UCB$B_DEVCLASS(R5),R10 ;? ??; Save DEVCLASS field MOVB #DC$_DISK,- ;? ??; Temporarily set this field to a disk UCB$B_DEVCLASS(R5) ;? ??; so ERF can translate packet MOVL UCB$L_DDT(R5),R0 ;? ??; Get DDT address MOVW SCSI_ERROR_LEN_TAB-2[R7],- ;? ??; Save required errorlog packet size DDT$W_ERRORBUF(R0) ;? ??; in the DDT TSTL R4 ;? ??; Was an SCDRP passed? BEQL 20$ ;? ??; No, I/O not in progress MOVL SCDRP$L_IRP(R4),UCB$L_IRP(R5) ;? ??; Copy 'active' IRP address BEQL 20$ ;? ??; Branch if no 'active' IRP CALL_DEVICERR SAVE_R0R1=NO ;? ??; Log a device error CLRL UCB$L_IRP(R5) ;? ??; No longer intersted in 'active' IRP BBCC #UCB$V_ERLOGIP,UCB$L_STS(R5),30$;? ??; Clear error log in progress. MOVL UCB$L_EMB(R5),R2 ;? ??; Get address of error message buffer BEQL 30$ ;? ??; Branch if none available ; The following four fields are normally filled in by REQCOM, but such ; code does not execute when UCB$V_ERLOGIP is clear, so we do it here: MOVL UCB$L_STS(R5),EMB$L_DV_STS(R2) ;? ??; Insert final device status. MOVL UCB$L_ERTCNT(R5),- ;? ??; Insert final error counters. EMB$L_DV_ERTCNT(R2) ;? ??; MOVL UCB$L_ERTMAX(R5),- ;? ??; Insert final error counters. EMB$L_DV_ERTMAX(R2) ;? ??; CLRL EMB$Q_DV_IOSB(R2) ;? ??; Clear I/O status, since we CLRL EMB$Q_DV_IOSB+4(R2) ;? ??; cannot know IOSB at this point. CALL_RELEASEMB ;? ??; Realease the errorlog buffer BRB 30$ ;? ??; Skip no-I/O-in-progress path 20$: CALL_DEVICEATTN SAVE_R0R1=NO ;? ??; Log a device attention 30$: MOVB R10,UCB$B_DEVCLASS(R5) ;? ??; Restore original devclass value MOVB R9,UCB$B_DEVTYPE(R5) ;? ??; Restore device type 40$: RSB ;? ??; Return to caller .PAGE .SBTTL SET_CONN_CHAR - Modify connection characteristics ;+ ; SET_CONN_CHAR ; ; This routine is called at the end of PACKACK to initialize the connection ; characteristics, which specify such things as whether the device supports ; disconnect and synchronous operation, and the bus busy, arbitration, and ; selection retry counters. ; ; This routine first allocates a connection characteristics buffer on the ; kernel process stack, then calls SPI$CONNECTION_CHAR_GET to get the current ; values of the connection characteristics, modifies the values of interest, ; and calls SPI$CONNECTION_CHAR_SET to set up the new values. This allows ; the class driver to change a subset of the characteristics and leave the ; rest unmodified. ; ; Context: ; ; SCDRP Thread ; IPL = Fork ; Fork lock held ; ; Inputs: ; ; R3 - UCB address ; R4 - SPDT address ; R5 - SCDRP address ; ; Outputs: ; ; R0 - Status ; R1,R2 - Destroyed ; All other registers preserved ;- SET_CONN_CHAR:: ;S FF; Modify connection characteristics .JSB_ENTRY INPUT=,OUTPUT=,SCRATCH= SUBL #SPI$K_CC_LENGTH,SP ;S FF; Allocate buffer on the KP stack MOVL SP,R2 ;S FF; Copy buffer address MOVL #SPI$K_CC_QNUM_ARGS,- ;S FF; Set argument count in buffer SPI$IL_CC_COUNT(R2) SPI$CONNECTION_CHAR_GET ;S FF; Get current connection characteristics BLBC R0,10$ ;S FF; Branch on error ASSUME SPI$M_CC_ENA_DISCON EQ 1 EXTZV #UCB$V_DISCONNECT,#1,- ;S FF; Fill in disconnect flag UCB$L_DK_FLAGS(R3),- ;S FF; SPI$IL_CC_CON_FLAGS(R2) ;S FF; EXTZV #UCB$V_SYNCHRONOUS,#1,- ;S FF; Fill in synchronous flag UCB$L_DK_FLAGS(R3),- ;S FF; SPI$IL_CC_SYNCHRONOUS(R2) ;S FF; CLRL SPI$IL_CC_SCSI_FLAGS(R2) ;S FF; We leave FLUSHQ & FREEZEQ zero IF_NOT_CMDQ 1$ ;S FF; Br if no CMDQ support BISL #SPI$M_CC_CMDQ,- ;S FF; Fill in Command queue flag SPI$IL_CC_SCSI_FLAGS(R2) ;S FF; MOVL UCB$L_QDEPTH(R3),- ;S FF; Set max queue depth SPI$IL_CC_MAX_QDEPTH(R2) ;S FF; 1$: CMPB #SCSI$C_ANSI_SCSI_2,- ;S FF; Are we at least SCSI-2? UCB$B_SCSI_VERSION(R3) ;S FF; BGTR 2$ ;S FF; Nope BISL #SPI$M_CC_SCSI_2,- ;S FF; set SCSI_2 char bit SPI$IL_CC_SCSI_FLAGS(R2) ;S FF; 2$: SPI$CONNECTION_CHAR_SET ;S FF; Set the connection characteristics 10$: ADDL #SPI$K_CC_LENGTH,SP ;S FF; Clear buffer off the stack BLBS R0,20$ ;S FF; Branch if success status MOVL #SS$_CTRLERR,R0 ;S FF; Otherwise, return a reasonable status 20$: RSB ;S FF; Return to caller .PAGE .SBTTL SAVE_CONN_CHAR - Save Connection Characteristics ;+ ; SAVE_CONNECTION_CHAR ; ; This routine saves the connection characteristics into a storage area ; allocated by this routine, and pointed to by the field UCB$PS_SAVE_CONN_CHAR. ; The reason behind doing this is to save the device's characteristics prior ; to performing an operation that would change the device characteristics from ; what a set mode operation does. ; ; An example of such an operation is mixed-mode media support of CDROMS. ; ; Context: ; ; SCDRP thread ; IPL = Fork ; Fork lock held ; ; Inputs: ; R3 - UCB address ; R4 - PDT address ; R5 - SCDRP address ; ; Outputs: ; ; R0 - Status ; R1,R2 - Destroyed ; All other registers preserved ; ;- SAVE_CONN_CHAR:: ;S FF; Save Connection Characteristics .JSB_ENTRY INPUT=,OUTPUT=,SCRATCH= ; Add code to save connection characteristics that may be changed from ; the functionality that is being performed. This code is specifically ; being added to support mixed mode audio/data disks, but can be used ; for other processing as well. ; MOVL UCB$PS_SAVE_CONN_CHAR(R3),R2 ;S FF; Pick up the connection char buffer BNEQ 10$ ;S FF; Branch if we do indeed have one ;S FF; If we do not have one for this UCB, ;S FF; then allocate a permanent one MOVL #SPI$K_CC_LENGTH,R1 ;S FF; Size of get/set conn char buffer PUSHL R5 ;S FF; Save SCDRP address MOVL SCDRP$PS_KPB(R5),R5 ;S FF; Get KPB address BSBW DK_ALLOC_POOL ;S FF; Allocate pool POPL R5 ;S FF; REstore SCDRP address MOVL R2,- ;S FF; Save the location into our storage UCB$PS_SAVE_CONN_CHAR(R3) ;S FF; area in the UCB 10$: MOVL #SPI$K_CC_QNUM_ARGS,(R2) ;S FF; Set argument count in buffer SPI$CONNECTION_CHAR_GET ;S FF; Get current connection characteristics RSB ;S FF; .PAGE .SBTTL RESTORE_CONN_CHAR - Restore Connection .SBTTL - Characteristics From UCB Area ;+ ; RESTORE_CONN_CHAR - Restore Connection Characteristics ; ; The objective of this routine is to restore the connection characteristics ; save by a previous call to the SAVE_CONNECT_CHAR routine. ; ; Context: ; ; SCDRP thread ; IPL = Fork ; Fork lock held ; ; Inputs: ; R3 - UCB address ; R4 - PDT address ; R5 - SCDRP address ; ; Outputs: ; ; R0 - Status ; R1,R2 - Destroyed ; All other registers preserved ;- RESTORE_CONN_CHAR:: ;S FF; Restore Connection Charateristics .JSB_ENTRY INPUT=,OUTPUT=,SCRATCH= MOVL UCB$PS_SAVE_CONN_CHAR(R3),R2 ;S FF; Restore Address of Connection Buffer SPI$CONNECTION_CHAR_SET ;S FF; Set the connection characteristics RSB ;S FF; Return status back from this routine .PAGE .SBTTL ATTEMPT_REORDER - Attempt to reorder read/write requests for .SBTTL - seek optimization ;+ ; ATTEMPT_REORDER ; ; This routine is called at the end of STARTIO to scan the pending queue in ; the UCB and determine the most more optimal request to service next. ; An elevator algorithm is used. Thus, the disk head moves in one direction, ; servicing all requests in that direction until no more exist. The CUR_LBN ; field keeps track of the current location of the heads, while the SEEK_DIR ; bit specifies the current direction of motion. An assumption here is that ; the disk is laid out in LBN order. ; ; Note that it's OK to reorder read and write operations in the pending ; queue, but not most other operations, such as PACKACKs. Therefore, scan ; of the pending queue stops if a non-read or -write operation is encountered. ; Also, reordering is not performed if the disk is currently in mount ; verification. ; ; Register usage within this routine: ; ; R0 - Contains the function code of each successive IRP in ; order to check that the function is a read or write ; R1 - Bitmap used to determine whether an IRP in the pending ; queue is "better" than the "best" IRP found so far ; R2 - Address of pending I/O queue listehead in UCB ; R3 - Address of the "best" IRP found at any point ; R4 - Signed displacement from the head position to the LBN of the ; "best" IRP found so far ; R5 - UCB address ; R6 - Address of each successive IRP in pending queue ; being considered as a posible optimal IRP ; R7 - Signed displacement from the heads to the LBN of each ; successive IRP in pending queue as the list is scanned ; R8 - Limit to the number of IRPs in the pending queue that ; get scanned ; ; Context: ; ; Fork thread ; IPL = Fork ; Fork lock held ; ; Inputs: ; ; R2 - Address of I/O pending queue listhead in UCB ; R5 - UCB address ; ; Outputs: ; ; R0-R4 - Destroyed ; All other registers preserved ; Pending queue in UCB is sorted, with most appropriate IRP for current ; head position at front of queue ;- MAX_IRPS_SCANNED = 12 FAIRNESS_CNT = 4 ATTEMPT_REORDER:: ;C FF; Attempt to reorder read/write requests for .JSB_ENTRY INPUT=,SCRATCH=,PRESERVE= ; If mount verification is in progress, don't perform any reordering. BBS #UCB$V_MNTVERIP,- ;C FF; Branch if mount verification is UCB$L_STS(R5),10$ ;C FF; in progress, can't reorder ; Check that the active IRP is either a read or write function. If not, ; we can't reorder. The following code sequence increments the function code, ; and clears the low-order bit so that both IO$_READPBLK and IO$_WRITEPBLK ; get converted to IO$_READPBLK, reducing the number of compare operations ; needed to determine whether the function is a read or write. MOVL (R2),R3 ;C FF; Get first IRP in pending queue ASSUME IRP$S_FCODE LE 7 ;C FF; Allow byte compare ASSUME IO$_WRITEPBLK EQ <^B1011> ASSUME IO$_READPBLK EQ <^B1100> ADDB3 #1,IRP$L_FUNC(R3),R0 ;C FF; Increment function code BICB #<^C>+1,R0 ;C FF; Extract function code and clear LSB CMPB R0,#IO$_READPBLK ;C FF; Read or write function? BNEQ 10$ ;C FF; Branch if not, can't reorder MOVL R2,R6 ;C FF; Copy pending queue listhead address MOVZBL #MAX_IRPS_SCANNED,R8 ;C FF; Set limit on number of IRPs scanned SUBL3 UCB$L_CUR_LBN(R5),- ;C FF; Assume LBN of active IRP is directly IRP$L_MEDIA(R3),R4 ;C FF; under heads (ideal displacement) BNEQ NEXT_IRP ;C FF; Branch if not, continue scan BRW SCAN_DONE ;C FF; Otherwise, no need to continue scan 10$: RSB ;C FF; SCAN_LOOP: ASSUME IRP$S_FCODE LE 7 ;C FF; Allow byte compare ASSUME IO$_WRITEPBLK EQ <^B1011> ASSUME IO$_READPBLK EQ <^B1100> ADDB3 #1,IRP$L_FUNC(R6),R0 ;C FF; Increment function code BICB #<^C>+1,R0 ;C FF; Extract function code and clear LSB CMPB R0,#IO$_READPBLK ;C FF; Read or write function? BNEQ SCAN_DONE ;C FF; Branch if not, stop scan ; Here we build a bitmap in R1 to indicate the following: ; Bit 3 - Set if the heads are moving towards higher LBNs ; Bit 2 - Set if the optimal I/O found so far is requesting a ; higher LBN than the current head position ; Bit 1 - Set if the pending I/O currently being considered ; is requesting a higher LBN than the current head position ; Bit 0 - Set if the pending I/O currently being considered is ; requesting a higher LBN than the optimal I/O found so far ; ; We can then use this bitmap to dispatch to the appropriate code to ; either continue to use the optimal IRP or to replace it with the ; IRP in the pending queue. 5$: MOVZBL UCB$B_SEEK_DIR(R5),R1 ;C FF; Set/clear seek direction bit in R1 10$: TSTL R4 ;C FF; Is optimal I/O LBN > head position? BLSS 20$ ;C FF; Branch if not ADDL #4,R1 ;C FF; Indicate optimal I/O LBN > head position 20$: SUBL3 UCB$L_CUR_LBN(R5),- ;C FF; Calculate displacement to pending I/O IRP$L_MEDIA(R6),R7 ;C FF; BEQL PERFECT_IRP ;C FF; Found the perfect I/O, get out now BLSS 30$ ;C FF; Branch if pending I/O LBN < head position ADDL #2,R1 ;C FF; Indicate pending I/O LBN > head position 30$: CMPL IRP$L_MEDIA(R6),- ;C FF; Compare LBNs of optimal and pending IRP$L_MEDIA(R3) ;C FF; I/Os BLEQ 40$ ;C FF; Branch if pending <= optimal INCL R1 ;C FF; Indicate pending > optimal 40$: DISPATCH R1,TYPE=B,<- ;C FF; Dispatch based on the state in R1 <0, NEXT_IRP>,- ;C FF; to either keep the current optimal <1, SWAP_IRP>,- ;C FF; IRP or replace it with one from the <3, NEXT_IRP>,- ;C FF; pending queue <4, SWAP_IRP>,- <6, SWAP_IRP>,- <7, NEXT_IRP>,- <8, NEXT_IRP>,- <9, SWAP_IRP>,- <11, SWAP_IRP>,- <12, NEXT_IRP>,- <14, SWAP_IRP>,- <15, NEXT_IRP>> BUG_CHECK INCONSTATE,FATAL ;C FF; This should never happen ; The IRP in the pending queue was found to be closer to the heads than ; the optimal one. Therefore replace the optimal IRP with the one from ; the pending queue. SWAP_IRP: MOVQ R6,R3 ;C FF; Replace the optimal IRP with the ;C FF; one from the pending queue REMQUE (R3),R3 ;C FF; Remove IRP from current position in INSQUE (R3),UCB$L_IOQFL(R5) ;C FF; I/O queue and place at front of queue SUBL3 UCB$L_CUR_LBN(R5),- ;C FF; Update distance from current to optimal IRP$L_MEDIA(R3),R4 ;C FF; NEXT_IRP: MOVL (R6),R6 ;C FF; Get address of next IRP in list CMPL R6,R2 ;C FF; End of list? BEQL SCAN_DONE ;C FF; Branch if so SOBGTR R8,SCAN_LOOP ;C FF; Branch if more IRPs are allowed to be scanned SCAN_DONE: ; R4 contains the signed displacement between the current head position and ; the LBN of the IRP that we've chosen to execute. Change the SEEK_DIR ; based on the which direction the heads will now be moving. If the LBN of ; the current IRP is directly under the heads, continue to scan in the same ; direction. INCL UCB$L_FAIRNESS_CNT(R5) ;C FF; Assume perfect IRP TSTL R4 ;C FF; Time to change seek direction? BEQL SCAN_RETURN ;C FF; Branch if not BGTR 10$ ;C FF; CLRB UCB$B_SEEK_DIR(R5) ;C FF; Start motion head towards lower LBNs BRB SCAN_RETURN ;C FF; Use common exit 10$: MOVB #8,UCB$B_SEEK_DIR(R5) ;C FF; Start motion towards higher LBNs 20$: CLRL UCB$L_FAIRNESS_CNT(R5) ;C FF; Indicate this is a new LBN SCAN_RETURN: RSB ;C FF; Return to caller ; While scanning the pending queue, an IRP was found which is requesting ; the LBN at the current head position. No need to scan any longer. Choose ; this IRP as the optimal one and get out. PERFECT_IRP: CMPL #FAIRNESS_CNT,- ;C FF; Is this LBN monopolizing service? UCB$L_FAIRNESS_CNT(R5) BLSS NEXT_IRP ;C FF; If so, don't choose this IRP after all REMQUE (R6),R3 ;C FF; Remove IRP from current position in INSQUE (R3),UCB$L_IOQFL(R5) ;C FF; I/O queue and place at front of queue SUBL3 UCB$L_CUR_LBN(R5),- ;C FF; Update distance from current to optimal IRP$L_MEDIA(R3),R4 ;C FF; BRB SCAN_DONE .PAGE .SBTTL CHECK_HBS - Determine if device supports HBS ;+ ; CHECK_HBS ; ; This routine is called during the first PACKACK function to each unit to ; determine if the device is capable of supporting host-based shadowing. The ; criteria is that the device must allow the forcing of bad blocks using ; the read long / write long SCSI commands. This routine performs a series ; of read longs with varying length fields trying to find one that succeeds. ; The problem here is that these commands are both optional and vendor-unique. ; If a read long command succeeds, the length used for the command is saved ; in the UCB so it can be used for future read long / write long commands. ; If no read long commands succeed, then the device is marked as not supporting ; host-based shadowing. ; ; Context: ; ; SCDRP Thread ; IPL = Fork ; Fork lock held ; ; Inputs: ; ; R3 - UCB address ; R4 - SPDT address ; R5 - SCDRP address ; ; Outputs: ; ; R0 - SS$_NORMAL ; R1-R2 - Destroyed ; All other registers preserved ; ; UCB$L_ - UCB$V_ set if device supports host-based ; shadowing ; UCB$W_READL_LEN - Data length to use with future read long / write long ; commands ;- READL_RETRY_CNT = 8 CHECK_HBS:: ;S FF; Determine if device supports HBS .JSB_ENTRY INPUT=,OUTPUT=,SCRATCH=,PRESERVE= ; ; Assume failure. ; BISL #DEV$M_NOFE,- ;S FF; Drive does not support host based UCB$L_DEVCHAR2(R3) ;S FF; shadowing. (No Forced Error) .BRANCH_LIKELY BBC #DEV$V_SWL,- ;S FF; Branch if device is software UCB$L_DEVCHAR(R3),5$ ;S FF; write locked. Can't support HBS. MOVL #SS$_NORMAL,R0 ;S FF; Set success status BRW 29$ ;S FF; And exit 5$: MOVL #READL_RETRY_CNT,R7 ;S FF; Initialize counter for retries 10$: CLRW UCB$W_READL_LEN(R3) ;S FF; First read long length to try 20$: MOVAL CMD_READ_LONG,R2 ;S FF; Address of read long command SETUP_CMD ;S FF; Perform setup for SCSI command ADDL3 #4,SCDRP$L_CMD_PTR(R5),R6 ;S FF; Get address of SCSI CDB (Command). ;S FF; The CDB doesn't start at 0, but 4. .Disable Flagging ; CMD_READ_LONG$L_LBA is not aligned CLRL CMD_READ_LONG$L_LBA(R6) ;S FF; Attempt a read long on block 0 .Enable Flagging ; Since the read long command is vendor-unique, the transfer length field ; is not not known apriori. Instead, we must send the device a number of ; read long commands with varying length fields until the correct value is ; determined. The current values we try are: 0, 1, 512 -> 1024. In addition ; to checking for good SCSI status returned from the command, we also check ; for a transfer length returned of >= 512 bytes, since read long should return ; at least a block of data (and hopefully some ECC bytes as well). MOVB UCB$W_READL_LEN+1(R3),- ;S FF; Use length determined by CHECK_HBS CMD_READ_LONG$W_XFER_LENGTH(R6) ;S FF; to fill in MSB of xfer length MOVB UCB$W_READL_LEN(R3),- ;S FF; and low-order byte CMD_READ_LONG$W_XFER_LENGTH+1(R6);S FF; ; Use the SPI$SEND_COMMAND macro directly rather than the SEND_COMMAND routine ; as we don't want all the error checking performed by that routine. .IF DEFINED TRACE_SCSI BSBW TRACE_SCSI_CMD ;S FF Save SCSI command in trace buf .ENDC IF_CANCEL 27$ ;S FF; If server requested ;S FF; cancel, then return SPI$SEND_COMMAND TYPE=ORDERED ;S FF; Send the command .IF DEFINED TRACE_SCSI BSBW TRACE_SCSI_STS ;S FF; Save SCSI status in trace buf .ENDC ASSUME SCSI$C_GOOD EQ 0 BICL3 #SCSI$M_STATUS_BYTE_RESERVED,- ;S DS; Mask off reserved status bits @SCDRP$L_STS_PTR(R5), R8 ;S DS; BNEQ 30$ ;S FF; Branch if bad SCSI status BLBC R0,30$ ;S FF; Branch on error CMPL SCDRP$L_TRANS_CNT(R5),- ;S FF; Reasonable transfer length returned? #IOC$C_DISK_BLKSIZ ;S FF; BLSSU 30$ ;S FF; Branch if not ; We've successfully sent a read long command and received a reasonable ; amount of data in return. Clear the bit in the UCB indicating this device ; supports host-based shadowing. BICL #DEV$M_NOFE,- ;S FF; Drive supports host based UCB$L_DEVCHAR2(R3) ;S FF; shadowing. 25$: MOVZWL #SS$_NORMAL,R0 ;S FF; Set unconditional success 27$: CLEANUP_CMD ;S FF; Cleanup from the SCSI command 29$: RSB ;S FF; Return to caller ; We've been unsuccessful in sending a read long command to the drive. ; Unless this is the result of a unit attention, try the next data length ; value in the sequence (or, if we've reached the end of the sequence, ; return without setting the "host-based shadowing supported" bit in the UCB). ; If a unit attention occurred, the failure may have been caused by a bus reset, ; so retry the whole operation beginning with length 0. We start all the ; way back from 0 since, by the time we see the unit attention on the ; current length, we may already be past the correct length. ASSUME READ_LONG_DATA_LEN LT 32768 30$: CMPB R8,#SCSI$C_CHECK_CONDITION ;S FF; Was this a Check Condition? BNEQ 35$ ;S FF; No, just get next data value ; If the Check Condition indicates a sense key of UNIT ATTENTION, then there ; may have been a bus reset, so retry this same data length. The sense ; information must be read before CLEANUP_CMD. BBC #SCDRP$V_FLAG_ASENSE_VALID, - ;S FF; Is the autosense data valid ? SCDRP$L_SCSI_FLAGS(R5), 32$ ;S FF; No, better start over MOVL SCDRP$PS_SENSE_BUFFER(R5),R0 ;S FF; Copy Auto Sense buffer address EXTZV #SCSI$SNS$V_SENSE_KEY,- ;S FF; Get sense key #SCSI$SNS$S_SENSE_KEY,- ;S FF; SCSI$SNS$R_FLAGS(R0),R1 ;S FF; CMPB R1,#SCSI$C_UNIT_ATTENTION ;S FF; Unit attention sense key? BNEQ 35$ ;S FF; No, so get next length 32$: SPI$QUEUE_RELEASE ;S FF; Free the queue from the CHK COND CLEANUP_CMD ;S FF; Cleanup from the read long command SOBGTR R7,10$ ;S FF; Else retry from 0 MOVL #SS$_NORMAL,R0 ;S FF; Set status BRW 29$ ;S FF; Exit when retries exhausted 35$: SPI$QUEUE_RELEASE ;S FF; Free the queue from the CHK COND CLEANUP_CMD ;S FF; Cleanup from the read long command MOVZWL UCB$W_READL_LEN(R3),R0 ;S FF; get the count that we just used CMPL #1,R0 ;S FF; did we just do 1 BNEQ 40$ ;S FF; neq means no- just inc the value MOVL #<512-1>,R0 ;S FF; Skip 2->511 the seq should be ;S FF; 0,1,512,513...READ_LONG_DATA_LEN 40$: INCL R0 ;S FF;S FF; update count MOVW R0,UCB$W_READL_LEN(R3) ;S FF; put it back in ucb CMPL R0,#READ_LONG_DATA_LEN ;S FF; did we get to the max value? .BRANCH_LIKELY BLEQU 20$ ;S FF; Try sending another read long command MOVL #SS$_NORMAL,R0 ;S FF; Set status BRW 29$ ;S FF; And exit .PAGE .SBTTL FORCE_ERROR - Force an ECC error on a block ;+ ; FORCE_ERROR - Force ECC error ; ; This routine is used to generate unrecoverable ECC errors on a set of ; logical blocks. It is used by shadowing code when the master copy of a ; particular block has gone bad and the "forced error" bit for this block ; must be set for all members of the shadow set. Since SCSI doesn't support ; the forced error bit, the alternative is simply to generate an unrecoverable ; ECC error, so all subsequent reads to this block generate an error. ; ; The ECC error is generated by performing a read long, inverting every byte ; in the block, and performing a write long. ; ; This routine performs it action by repeatedly calling FORCE_ONE_ERROR for ; each of the blocks associated with the QIO. ; ; Context: ; ; SCDRP Thread ; IPL = Fork ; Fork lock held ; ; Inputs: ; ; R2 - IRP address ; R3 - UCB address ; R4 - SPDT address ; R5 - SCDRP address ; ; UCB$W_READL_LEN - Length to use for the read long / write long commands ; ; Outputs: ; ; R0 - Status ; R1-R2 - Destroyed ; All other registers preserved ; ;- FORCE_ERROR: ;S FF; Force ECC error CLRL SCDRP$L_ABCNT(R5) ;S FF; Initialize accumulated byte count MOVL IRP$L_MEDIA(R2),- ;S FF; Copy disk logical block number from SCDRP$L_MEDIA(R5) ;S FF; the IRP to the SCDRP MOVL #IOC$C_DISK_BLKSIZ,- ;S FF; Force errors on one block at a time SCDRP$L_BCNT(R5) ;S FF; .print ; What is this MD_ERROR at IRP$L_MEDIA+6? BBC #MSCP$V_MD_ERROR,- ;S FF; Branch if the MD_ERROR bit not set. IRP$L_MEDIA+6(R2),50$ ;S FF; This bit must be set to force an error BBC #UCB$V_HBS_CHECK,- ;S FF; Branch if check for host-based UCB$L_DK_FLAGS(R3),50$ ;S FF; shadowing has not yet been made BBS #DEV$V_SWL,- ;S FF; Branch if device is software UCB$L_DEVCHAR(R3),60$ ;S FF; write locked. Can't support HBS. SET_SINGLE FORCE_ERROR,R3 ;S FF; Single thread force error request 10$: BSBB FORCE_ONE_ERROR ;S FF; Force an error on the next block BLBC R0,20$ ;S FF; Branch on error INCL SCDRP$L_MEDIA(R5) ;S FF; Advance to next block ADDL #IOC$C_DISK_BLKSIZ,- ;S FF; Update accumulated byte count SCDRP$L_ABCNT(R5) ;S FF; MOVL SCDRP$L_IRP(R5),R2 ;S FF; Restore IRP address CMPL SCDRP$L_ABCNT(R5),- ;S FF; Done with entire transfer? IRP$L_BCNT(R2) ;S FF; BLSSU 10$ ;S FF; Branch if not 20$: CLR_SINGLE FORCE_ERROR,R3 ;S FF; Force error request is complete 30$: MOVL SCDRP$L_ABCNT(R5),R1 ;S FF; Get accumulated transfer count INSV R1,#16,#16,R0 ;S FF; Load low-order transfer count BRW COMPLETE_IO ;S FF; Complete the QIO ; The check to determine if the device supports host-based shadowing has ; not yet been made. Therefore, we can attempt to for an error at this time. 50$: MOVZWL #SS$_UNSUPPORTED,R0 ;S FF; Set unsupported status BRB 30$ ;S FF; Use common exit ; The device is write-locked and therefore can not accept the commands ; necessary to cause an uncorrectable ECC error. 60$: MOVL #SS$_WRITLCK,R0 ;S FF; Set write-locked status BRB 30$ ;S FF; Use common exit .PAGE .SBTTL FORCE_ONE_ERROR - Force an unrecoverable error on a block ;+ ; FORCE_ONE_ERROR ; ; This routine is used to generate an unrecoverable ECC error on the specified ; logical block and is called by the routine FORCE_ERROR. The ECC error is ; generated by performing a read long, inverting every byte in the block, and ; performing a write long. Prior to performing the read long / write long ; sequence, the block is read to determine if it already contains a ; non-recoverable error. If so, no action is taken. ; ; Context: ; ; SCDRP Thread ; IPL = Fork ; Fork lock held ; ; Inputs: ; ; R2 - IRP address ; R3 - UCB address ; R4 - SPDT address ; R5 - SCDRP address ; ; UCB$W_READL_LEN - Length to use for the read long / write long commands ; ; Outputs: ; ; R0 - Status ; R1-R2 - Destroyed ; All other registers preserved ; ;- FORCE_ONE_ERROR:: ;S FF; Force an unrecoverable error on a block .JSB_ENTRY INPUT=,OUTPUT=,SCRATCH= ; First, perform a normal read command on the block to determine if the block ; already contains an uncorrectable error. If so, don't bother forcing another. BISL #IRP$M_FUNC,- ;S FF; Set the FUNC but to indicate this SCDRP$IS_STS(R5) ;S FF; is a read function MOVL #IOC$C_DISK_BLKSIZ,R1 ;S FF; Allocate an S0 buffer in which to read PUSHL R5 ;S FF; Save SCDRP address MOVL SCDRP$PS_KPB(R5),R5 ;S FF; Get KPB address BSBW DK_ALLOC_POOL ;S FF; Allocate pool POPL R5 ;S FF; REstore SCDRP address BISB #SCDRP$M_FLAG_S0BUF,- ;S FF; Indicate that this is an S0 "user" SCDRP$L_SCSI_FLAGS(R5) ;S FF; buffer MOVL R2,SCDRP$L_SVA_USER(R5) ;S FF; Save address of S0 "user" buffer PUSHL R3 ;S FF; Save R3 JSB G^MMG$SVAPTECHK ;S FF; Get SVAPTE of allocated system buffer MOVL R3,SCDRP$L_SVAPTE(R5) ;S FF; Save SVAPTE in SCDRP MCOML G^ MMG$GL_BWP_MASK, R3 ;S FF; Get mask for BOFF. BICL3 R3, R2, SCDRP$L_BOFF(R5) ;S FF; Set offset from beginning of page. POPL R3 ;S FF; Restore R3 DISABLE_ERRLOG ;S FF; Temporarily disable errorlogging MOVL #SCDRP$K_QCHAR_UNORDERED,- ;S FF; Send a Simple tagged request SCDRP$IS_QUEUE_CHAR(R5) BSBW READ_WRITE ;S FF; Send out a read command REENABLE_ERRLOG ;S FF; Reenable errorlogging PUSHL R0 ;S FF; Save status MOVL SCDRP$L_SVA_USER(R5),R0 ;S FF; Get address of S0 buffer BSBW DEALLOC_POOL ;S FF; Deallocate the S0 buffer CLEANUP_CMD ;S FF; Clean up SCSI command POPL R0 ;S FF; Restore R0 CMPL R0,#SS$_PARITY ;S FF; Already a hard error on the block? BEQL 40$ ;S FF; Branch if so CMPL R0,#SS$_CANCEL ;S FF; If cancel status BEQL 30$ ; Issue a read long command to get the current contents of the logical ; block. MOVAL CMD_READ_LONG,R2 ;S FF; Address of read long command SETUP_CMD ;S FF; Perform setup for SCSI command ADDL3 #4,SCDRP$L_CMD_PTR(R5),R0 ;S FF; Get address of SCSI CDB (Command). ;S FF; The CDB doesn't start at 0, but 4. MOVB UCB$W_READL_LEN+1(R3),- ;S FF; Use length determined by CHECK_HBS CMD_READ_LONG$W_XFER_LENGTH(R0) ;S FF; to fill in MSB of xfer length MOVB UCB$W_READL_LEN(R3),- ;S FF; and low-order byte CMD_READ_LONG$W_XFER_LENGTH+1(R0);S FF; ADDL #,R0 ;S FF; Address just beyond LBA MOVAL SCDRP$L_MEDIA(R5),R1 ;S FF; Get pointer LBN field .REPT 4 MOVB (R1)+,-(R0) ;S FF; Copy LBN number to SCSI command .ENDR SEND_COMMAND_ORDERED ;S FF; Send the command CLEANUP_CMD ;S FF; Clean up the SCSI command BLBC R0,30$ ;S FF; Branch on error ; Invert every byte in the original block to ensure that subsequent reads to ; the block will result in uncorrectable ECC errors. MOVL SCDRP$L_SVA_USER(R5),R0 ;S FF; Get address of read long data MOVZBL #,R1 ;S FF; Length of disk block in longwords 10$: XORL2 #-1,(R0)+ ;S FF; Invert a longword SOBGTR R1,10$ ;S FF; Repeat for the entire disk block ; Save the original (read long) SCDRP and allocate another one in preparation ; for sending a write long command. MOVL SCDRP$L_MEDIA(R5),R1 ;S FF; Save LBN of block to reassign IF_CANCEL 30$ ;S FF; Check for cancel request before ;S FF; allocating private SCDRP DK_ALLOC_SCDRP ;S FF; Allocate a new SCDRP for the write long MOVAL CMD_WRITE_LONG,R2 ;Są FF; Address of write long command SETUP_CMD ;Są FF; Perform setup for SCSI command ADDL3 #4,SCDRP$L_CMD_PTR(R5),R0 ;Są FF; Get address of SCSI CDB (Command). ;Są FF; The CDB doesn't start at 0, but 4. MOVB UCB$W_READL_LEN+1(R3),- ;Są FF; Use length determined by CHECK_HBS CMD_READ_LONG$W_XFER_LENGTH(R0) ;Są FF; to fill in MSB of xfer length MOVB UCB$W_READL_LEN(R3),- ;Są FF; and low-order byte CMD_READ_LONG$W_XFER_LENGTH+1(R0);Są FF; ADDL #,R0 ;Są FF; Address just beyond LBA MOVAL SCDRP$L_MEDIA(R5),R1 ;S FF; Get pointer LBN field .REPT 4 MOVB (R1)+,-(R0) ;S FF; Copy LBN number to SCSI command .ENDR ; Copy the data, which is now innverted, from the read long buffer to the ; write long buffer. MOVL SCDRP$PS_PREV_SCDRP(R5),R1 ;Są FF; Restore original SCDRP address MOVL SCDRP$L_SVA_USER(R1),R0 ;Są FF; Get address of read long buffer MOVL SCDRP$L_SVA_USER(R5),R1 ;Są FF; Get address of write long buffer ASSUME READ_LONG_DATA_LEN EQ WRITE_LONG_DATA_LEN MOVZWL #READ_LONG_DATA_LEN,R2 ;Są FF; Number of bytes to copy PUSHR #^M ;Są FF; Save regs MOVC3 R2,(R0),(R1) ;Są FF; Copy read long buf to write long buf POPR #^M ;Są FF; Restore regs ; Send the write long command, then perform the cleanup necessary for both ; commands. SEND_COMMAND_ORDERED ;Są FF; Send the command CLEANUP_CMD ;Są FF; Cleanup from the write long command DK_DEALLOC_SCDRP CLRSTK=YES ;Są FF; Deallocate the reassign SCDRP 30$: RSB ;S FF; Return to caller ; The block already has a non-recoverable error associated with it. Don't ; bother forcing a second error, since if the current error is due to a ; previous force error function, issuing a second force error will have the ; effect of making the block good again. Instead, simply return success status. 40$: MOVZWL #SS$_NORMAL,R0 ;S FF; Set success status BRB 30$ ;S FF; Use common exit .PAGE .SBTTL CHECK_QDEPTH - Check histogram data for qdepth change ;+ ; CHECK_QDEPTH ; ; The startio routine in conjuntion with this routine, maintains and analyzies a ; histogram of I/O transfer requests sizes (UCB$L_XLEN_HIST). After every 'n' ; transfers (UCB$L_XLEN_HIST_CYCLE), the histogram is scanned to find the ; maximum value, which is the most popular transfer size used in the past 'n' ; I/O's. The table of queue depths is then indexed and a new queue depth ; appropriate for the current workload is selected. ; ; Context: ; ; SCDRP thread ; IPL = Fork ; Fork lock held ; ; Inputs: ; ; R3 - address of the UCB ; R5 - address of an SCDRP ; ; Outputs: ; ; R0,R1 - destroyed ; all other registers preserved ;- CHECK_QDEPTH: ;S FF; .JSB_ENTRY INPUT=,PRESERVE= MOVL UCB$L_XLEN_HIST(R3),R6 ;S FF; Address of histogram table CLRL R1 ;S FF; Max hist entry count MOVL #XLEN_HIST_BUCKETS-1,R0 ;S FF; Index into histogram table MOVL R0,R2 ;S FF; Save "max" index 10$: CMPL (R6)[R0],R1 ;S FF; Check entry against max seen BLEQ 20$ ;S FF; br if less than max MOVL (R6)[R0],R1 ;S FF; New max MOVL R0,R2 ;S FF; Store new index 20$: CLRL (R6)[R0] ;S FF; Restart histogram SOBGEQ R0,10$ ;S FF; Check all cells MOVAL QDEPTH_TABLE,R6 ;S FF; Address of qdepth vs xlen table MOVL (R6)[R2],R0 ;S FF; select new optimal depth CMPL R0,UCB$L_QDEPTH(R3) ;S FF; If same depth value BEQL 30$ ;S FF; then skip SET_CONN_CHAR MOVL R0,UCB$L_QDEPTH(R3) ;S FF; Set new qdepth INCL UCB$L_QDEPTH_TURNS(R3) ;S FF; Count adjustments BSBW SET_CONN_CHAR ;S FF; Tell port 30$: RSB ;S FF; .PAGE .SBTTL + .SBTTL + TRACE ROUTINES .SBTTL + .SBTTL Trace buffer, symbols ;+ ; TRACE_BUFFER ; ; The driver uses a ring buffer to trace at the QIO, SCSI command, and SCSI ; cmd/status byte level. For each QIO issued, there can be zero or more SCSI ; commands sent, each of which can result in SCSI command, message, and status ; bytes. The trace buffer has the following format: ; ; +-----------------------+ <---- Start of trace buffer header ; | Size of trace buf | ; +-----------------------+ ; | Trace header size | ; +-----------------------+ ; | Total QIOs in buf | ; +-----------------------+ ; | Size of each QIO | ; +-----------------------+ ; | QIO header size | ; +-----------------------+ ; | # SCSI cmds/QIO | ; +-----------------------+ ; | Size of SCSI cmd | ; +-----------------------+ ; | Offset to SCSI sts | ; +-----------------------+ ; | Current QIO # | ; +-----------------------+ ; | | ; | RESERVED | ; | | ; +-----------------------+ <---- End of trace buffer header ; | QIO 0 | ; +-----------------------+ ; | QIO 1 | ; +-----------------------+ ; | . | ; | . | ; +-----------------------+ ; | QIO n-1 | ; +-----------------------+ ; ; For each QIO, the following information is logged: ; ; +-----------------------+ <---- Start of QIO header ; | QIO number | ; +-----------------------+ ; | Valid flag | ; +-----------------------+ ; | SCSI command cnt | ; +-----------------------+ ; | SCSI bus ID | ; +-----------------------+ ; | Function code | ; +-----------------------+ ; | Media (LBN) | ; +-----------------------+ ; | Byte count | ; +-----------------------+ ; | Byte offset | ; +-----------------------+ ; | QIO status | ; +-----------------------+ ; | Sequence number | ; +-----------------------+ ; | | ; | RESERVED | ; | | ; +-----------------------+ <---- End of QIO header ; | SCSI CMD 0 | ; +-----------------------+ ; | SCSI CMD 1 | ; +-----------------------+ ; | . | ; | . | ; +-----------------------+ ; | SCSI CMD n-1 | ; +-----------------------+ ; ; Finally, for each SCSI command, the following information is logged: ; ; +-----------------------+ ; | CMD byte cnt | ; +-----------------------+ ; | SCSI CMD bytes | ; +-----------------------+ ; | SCSI status byte | ; +-----------------------+ ; ; Note that CMD byte count is kept to allow the tracing routines to know where ; to put the next CMD byte and to allow the user to know how much data to ; interpret. Also note that, in order to make it easier to interpret, the data ; is not packed in as well as it could be. ;- $DEFINI TRACE_BUFFER_HEADER $DEF TR$L_TRACE_BUF_SIZE .BLKL 1 ; Size of trace header, plus buffers $DEF TR$L_HEADER_SIZE .BLKL 1 ; Size of trace header $DEF TR$L_TOTAL_QIOS .BLKL 1 ; Number of QIO's to trace $DEF TR$L_CURRENT_QIO .BLKL 1 ; Current QIO number $DEF TR$L_QIO_SEQNUM .BLKL 1 ; Current QIO sequence number $DEF TR$L_QIO_SIZE .BLKL 1 ; Size of QIO header, plus 'n' SCSI commands $DEF TR$L_QIO_HEADER_SIZE .BLKL 1 ; Size of QIO header $DEF TR$L_TOTAL_SCSI_CMDS .BLKL 1 ; 'n' SCSI commands per QIO $DEF TR$L_SCSI_CMD_SIZE .BLKL 1 ; SCSI command bytes + status bytes $DEF TR$C_HEADER_SIZE $DEFEND TRACE_BUFFER_HEADER $DEFINI TRACE_BUFFER_QIO_HEADER $DEF TR$L_QIO_NUMBER .BLKL 1 ; TR$L_CURRENT_QIO number $DEF TR$L_SEQNUM .BLKL 1 ; TR$L_QIO_SEQNUM number $DEF TR$L_DEVICE_NAME .BLKL 1 ; Device name $DEF TR$L_SCSI_UNIT_NUMBER .BLKL 1 ; Unit number $DEF TR$L_VALID_FLAG .BLKL 1 ; Valid flag $DEF TR$L_SCSI_CMD_CNT .BLKL 1 ; Number of SCSI commands $DEF TR$L_QIO_FUNC .BLKL 1 ; $QIO function code $DEF TR$L_QIO_MEDIA .BLKL 1 ; $QIO media (LBN) $DEF TR$L_QIO_BCNT .BLKL 1 ; $QIO byte count $DEF TR$L_QIO_BOFF .BLKL 1 ; $QIO buffer offser $DEF TR$L_QIO_SVAPTE .BLKL 3 ; $QIO SVAPTE and entry $DEF TR$L_QIO_STATUS .BLKL 1 ; $QIO status $DEF TR$C_QIO_HEADER_SIZE $DEFEND TRACE_BUFFER_QIO_HEADER $DEFINI TRACE_BUFFER_SCSI_COMMAND $DEF TR$L_SCSI_CMD_LENGTH .BLKB 4 ; Length of SCSI command $DEF TR$L_SCSI_COMMAND .BLKB 12 ; SCSI command $DEF TR$L_SCSI_MESSAGE .BLKB 12 ; SCSI message $DEF TR$L_SCSI_STATUS .BLKB 4 ; SCSI command status $DEF TR$C_SCSI_CMD_SIZE $DEFEND TRACE_BUFFER_SCSI_COMMAND TR$C_TOTAL_QIOS = 2000 ; Total number of QIO's to trace TR$C_TOTAL_SCSI_CMDS = 10 ; Total number of SCSI commands/QIO to trace TR$C_QIO_SIZE = <> + 31> & ^C31 TR$C_TRACE_BUF_SIZE = TR$C_HEADER_SIZE + $DEFINI TRACE_CLASS_BUSY_BITS $DEF TRB$L_TYPE .BLKB 4 ; Type of Operation $DEF TRB$L_PC .BLKB 4 ; Routine address $DEF TRB$L_CLASS_BUSY .BLKB 4 ; Class Busy Bits $DEF TRB$L_STS .BLKB 4 ; UCB$L_STS $DEF TRB$C_LENGTH ; Size of TRB entry $DEFEND TRACE_CLASS_BUSY_BITS .PAGE .SBTTL SETUP_TRACE - Allocate and set up trace buffer ;+ ; SETUP_TRACE ; ; This routine is called by unit init to allocate a trace buffer, which ; is used to trace QIOs through the driver. The auxstruc field in the CRB is ; filled in with the address of a cell which contains the address of the trace ; buffer. Since the driver can have more than one unit, only the first ; call to this routine actually performs the allocation. Since pool allocation ; can stall this thread, the TRACE_BUFFER cell is given a value of ; 1 (positive integer) to indicate that poll allocation is on progress. This ; prevents a second thread from allocating another buffer. ; ; Context: ; ; Fork thread ; IPL = Fork ; Fork lock held ; ; Inputs: ; ; R2 - KPB ADDRESS ; R5 - UCB ADDRESS ; ; Outputs: ; ; R0-R3 - Destroyed ; All other registers preserved ; ; CRB$L_AUXSTRUC - Address of cell containing trace buffer address ;- SETUP_TRACE:: ;F FF; Allocate and set up trace buffer .JSB_ENTRY INPUT=,SCRATCH=,PRESERVE= .IF DEFINED TRACE TSTL TRACE_BUFFER ;F FF; Trace buffer already set up? BNEQ 10$ ;F FF; Branch if so INCL TRACE_BUFFER ;F FF; Trace buffer setup in progress MOVL #TR$C_TRACE_BUF_SIZE,R1 ;F FF; Get size of trace buffer MOVL R5, R3 ;F FF; Save copy of UCB MOVL R2, R5 ;F FF; Get KPB address BSBW DK_ALLOC_POOL ;F FF; Allocate the trace buffer MOVL R3, R5 ;F FF; Restore UCB address BLBC R0,10$ ;F FF; Branch if failure MOVL R2,TRACE_BUFFER ;F FF; Save trace buffer address MOVL #TR$C_TRACE_BUF_SIZE,- ;F FF; Save trace buffer size TR$L_TRACE_BUF_SIZE(R2) ;F FF; MOVL #TR$C_HEADER_SIZE,- ;F FF; Save header size TR$L_HEADER_SIZE(R2) ;F FF; MOVL #TR$C_TOTAL_QIOS,- ;F FF; Save total number of QIOs in TR$L_TOTAL_QIOS(R2) ;F FF; buffer ;;;;;;;;CLRL TR$L_CURRENT_QIO(R2) ;F FF; Reset current QIO number MOVL #TR$C_QIO_SIZE,- ;F FF; Save size of QIO TR$L_QIO_SIZE(R2) ;F FF; MOVL #TR$C_QIO_HEADER_SIZE,- ;F FF; Save size of QIO header TR$L_QIO_HEADER_SIZE(R2); ;F FF; MOVL #TR$C_TOTAL_SCSI_CMDS,- ;F FF; Save number of SCSI commands TR$L_TOTAL_SCSI_CMDS(R2) ;F FF; per QIO MOVL #TR$C_SCSI_CMD_SIZE,- ;F FF; Save SCSI command size TR$L_SCSI_CMD_SIZE(R2) ;F FF; 10$: .ENDC MOVL UCB$L_CRB(R5),R0 ;F FF; Get CRB address MOVAL TRACE_BUFFER,- ;F FF; Save address of cell pointing to CRB$L_AUXSTRUC(R0) ;F FF; trace buffer in CRB RSB ;F FF; Return to caller .PAGE .IF DEFINED TRACE .SBTTL TRACE_QIO - Trace QIO requests ;+ ; TRACE_QIO ; ; This routine logs information from the current QIO packet including the ; function code, MEDIA, BCNT, and BOFF fields. ; ; Context: ; ; Fork thread ; IPL = Fork ; Fork lock held ; ; Inputs: ; ; R5 - SCDRP address ; ; Outputs: ; ; All registers preserved ;- TRACE_QIO:: ;CF FF; Trace QIO requests .JSB_ENTRY INPUT=,PRESERVE= MOVL TRACE_BUFFER,R0 ;CF FF; Get trace buffer address BGEQ 30$ ;CF FF; Branch if none ADDL3 TR$L_CURRENT_QIO(R0),#1,R1 ;CF FF; Bump QIO number CMPL R1,#TR$C_TOTAL_QIOS ;CF FF; Time to wrap QIO number? BLSS 10$ ;CF FF; Branch if not CLRL R1 ;CF FF; Wrap QIO number 10$: MOVL R1,TR$L_CURRENT_QIO(R0) ;CF FF; Save QIO number MULL3 #TR$C_QIO_SIZE,R1,R2 ;CF FF; Get offset of this QIO in trace buffer ADDL R0,R2 ;CF FF; Address to place info for this QIO ADDL #TR$C_HEADER_SIZE,R2 ;CF FF; Account for trace buffer header PUSHR #^M ;CF FF; Save regs MOVC5 #0,.,#0,#TR$C_QIO_SIZE,(R2) ;CF FF; Clear buffer used to trace this QIO POPR #^M ;CF FF; Restore regs ADDL3 #TR$C_QIO_HEADER_SIZE,- ;CF FF; Get address of place to store R2,R1 ;CF FF; first SCSI command MOVL R1,SCDRP$PS_TR_SCSI_CMD(R5) ;CF FF; Save address to put SCSI command bytes MOVL TR$L_CURRENT_QIO(R0),- ;CF FF; Save QIO number TR$L_QIO_NUMBER(R2) ;CF FF; MOVL TR$L_QIO_SEQNUM(R0),- ;CF FF; Save QIO sequence number TR$L_SEQNUM(R2) ;CF FF; INCL TR$L_QIO_SEQNUM(R0) ;CF FF; Bump QIO sequence number MOVL SCDRP$L_UCB(R5),R1 ;CF FF; Get UCB address MOVZWL UCB$W_UNIT(R1),- ;CF FF; Trace SCSI bus ID TR$L_SCSI_UNIT_NUMBER(R2) MOVL UCB$L_DDB(R1),R1 ;CF FF; Get DDB address .Disable Flagging ; DDB$T_NAME_STR is not aligned MOVL DDB$T_NAME_STR(R1),- ;CF FF; Save SCSI device name TR$L_DEVICE_NAME(R2) ;CF FF; .Enable Flagging MOVL #1,TR$L_VALID_FLAG(R2) ;CF FF; Set valid flag CLRL TR$L_SCSI_CMD_CNT(R2) ;CF FF; Clear SCSI command count MOVL SCDRP$L_IRP(R5),R3 ;CF FF; IRP address of 0? BEQL 15$ ;CF FF; Branch if so MOVL IRP$L_FUNC(R3),- ;CF FF; Trace QIO function code TR$L_QIO_FUNC(R2) ;CF FF; MOVL IRP$L_MEDIA(R3),- ;CF FF; Trace media number (LBN) TR$L_QIO_MEDIA(R2) ;CF FF; MOVL IRP$L_BCNT(R3),- ;CF FF; Trace byte count TR$L_QIO_BCNT(R2) ;CF FF; MOVL IRP$L_BOFF(R3),- ;CF FF; Trace byte offset TR$L_QIO_BOFF(R2) ;CF FF; MOVL IRP$L_SVAPTE(R3),R0 ;CF FF; Get SVAPTE value BEQL 20$ ;CF FF; If Zero, don't trace it MOVL R0,TR$L_QIO_SVAPTE(R2) ;CF FF; Trace SVAPTE value ASSUME PTE$C_BYTES_PER_PTE EQ 8 MOVL (R0),TR$L_QIO_SVAPTE+4(R2) ;CF FF; ... and its quadword MOVL 4(R0),TR$L_QIO_SVAPTE+8(R2) ;CF FF; ... contents BRB 20$ ;CF FF; Skip unit init path 15$: MCOML #0,TR$L_QIO_FUNC(R2) ;CF FF; Special function code for UNIT INIT 20$: MOVAL TR$L_QIO_STATUS(R2),- ;CF FF; SCDRP$PS_TR_QIO_STS(R5) ;CF FF; Save address to put QIO status MCOML #0,TR$L_QIO_STATUS(R2) ;CF FF; Initialize QIO status 30$: RSB ;CF FF; Return to caller .PAGE .SBTTL TRACE_QIO_STAT - Save final status from QIO in trace buffer ;+ ; TRACE_QIO_STAT ; ; This routine saves the final QIO status in the trace buffer. ; ; Context: ; ; Fork thread ; IPL = Fork ; Fork lock held ; ; Inputs: ; R5 - SCDRP address ; ; Outputs: ; R2 - Destroyed ; ;- TRACE_QIO_STAT:: ;C FF; Save final status from QIO in trace buffer .JSB_ENTRY INPUT=,SCRATCH= MOVL SCDRP$PS_TR_QIO_STS(R5),R2 ;C FF; Get address to put status BEQL 10$ ;C FF; Branch if uninitialized MOVL R0,(R2) ;C FF; Save status in trace buffer 10$: RSB ;C FF; .PAGE .SBTTL TRACE_SCSI_CMD - Trace SCSI command bytes ;+ ; TRACE_SCSI_CMD ; ; This routine saves the contents of the SCSI command in the trace buffer. ; ; Context: ; ; SCDRP Thread ; IPL = Fork ; Fork lock held ; ;- TRACE_SCSI_CMD:: ;S FF; Trace SCSI command bytes .JSB_ENTRY INPUT= .IF DEFINED TRACE_SCSI PUSHR #^M ;S FF; Save regs MOVL SCDRP$L_UCB(R5),R3 ;S FF; Get UCB address MOVL TRACE_BUFFER,R0 ;S FF; Get trace buffer address BGEQ 20$ ;S FF; Branch if none MOVL TR$L_CURRENT_QIO(R0),R1 ;S FF; Get current QIO number MULL3 #TR$C_QIO_SIZE,R1,R2 ;S FF; Get offset of this QIO in trace buffer ADDL R0,R2 ;S FF; Address to place info for this QIO ADDL #TR$C_HEADER_SIZE,R2 ;S FF; Account for trace buffer header CMPL TR$L_SCSI_CMD_CNT(R2),- ;S FF; Maximum SCSI commands exceeded? #TR$C_TOTAL_SCSI_CMDS ;S FF; BGEQ 20$ ;S FF; Branch if so INCL TR$L_SCSI_CMD_CNT(R2) ;S FF; One more SCSI command sent MOVL SCDRP$L_CMD_PTR(R5),R0 ;S FF; Get address of SCSI command MOVL (R0)+,R1 ;S FF; Get size of SCSI command MOVL SCDRP$PS_TR_SCSI_CMD(R5),R2 ;S FF; Address of SCSI command BEQL 20$ ;S FF; Not setup yet MOVL R1,TR$L_SCSI_CMD_LENGTH(R2) ;S FF; Save length of command MOVAL TR$L_SCSI_COMMAND(R2),R2 ;S FF; Set pointer to command byte 10$: MOVB (R0)+,(R2)+ ;S FF; Save a command byte SOBGTR R1,10$ ;S FF; Repeat for entire command MOVL SCDRP$PS_TR_SCSI_CMD(R5),R2 ;S FF; Restore SCSI command pointer MOVAL TR$L_SCSI_STATUS(R2),- ;S FF; Save address to put SCSI status byte SCDRP$PS_TR_SCSI_STS(R5) ;S FF; MOVAL TR$C_SCSI_CMD_SIZE(R2),- ;S FF; Save address to put next SCSI command SCDRP$PS_TR_SCSI_CMD(R5) ;S FF; 20$: POPR #^M ;S FF; Restore regs .endc RSB ;S FF; Return to caller .PAGE .SBTTL TRACE_SCSI_STS - Trace SCSI status bytes ;+ ; TRACE_SCSI_STS ; ; This routine saves the SCSI status byte in the trace buffer. ;- ; Context: ; ; SCDRP Thread ; IPL = Fork ; Fork lock held ; TRACE_SCSI_STS:: ;S FF; Trace SCSI status bytes .JSB_ENTRY INPUT= .IF DEFINED TRACE_SCSI PUSHL R1 ;S FF; Save register MOVL SCDRP$PS_TR_SCSI_STS(R5),R1 ;S FF; Get address to put status bytes BEQL 10$ ;S FF; Not initialized yet MOVL R0,(R1) ;S FF; Save status byte 10$: POPL R1 ;S FF; Restore register .ENDC RSB ;S FF; Return to caller .ENDC .SBTTL End of DKDRIVER DK_END: .END