.TITLE SKDRIVER - VAX/VMS Sample SCSI Class Driver .IDENT 'X-2' ; .LIST MEB ;**************************************************************************** ;* * ;* COPYRIGHT (c) 1989, 1991 BY * ;* DIGITAL EQUIPMENT CORPORATION, MAYNARD, MASSACHUSETTS. * ;* ALL RIGHTS RESERVED. * ;* * ;* THIS SOFTWARE IS FURNISHED UNDER A LICENSE AND MAY BE USED AND COPIED * ;* ONLY IN ACCORDANCE WITH THE TERMS OF SUCH LICENSE AND WITH THE * ;* INCLUSION OF THE ABOVE COPYRIGHT NOTICE. THIS SOFTWARE OR ANY OTHER * ;* COPIES THEREOF MAY NOT BE PROVIDED OR OTHERWISE MADE AVAILABLE TO ANY * ;* OTHER PERSON. NO TITLE TO AND OWNERSHIP OF THE SOFTWARE IS HEREBY * ;* TRANSFERRED. * ;* * ;* THE INFORMATION IN THIS SOFTWARE IS SUBJECT TO CHANGE WITHOUT NOTICE * ;* AND SHOULD NOT BE CONSTRUED AS A COMMITMENT BY DIGITAL EQUIPMENT * ;* CORPORATION. * ;* * ;* DIGITAL ASSUMES NO RESPONSIBILITY FOR THE USE OR RELIABILITY OF ITS * ;* SOFTWARE ON EQUIPMENT WHICH IS NOT SUPPLIED BY DIGITAL. * ;* * ;* * ;**************************************************************************** ; ;++ ; ; FACILITY: ; ; VAX/VMS Sample SCSI Class Driver ; ; ABSTRACT: ; ; This module contains a sample SCSI class driver. This template ; supports two modes of operation: either the SCSI command ; packets are formatted in the application program (passthru mode) or ; the SCSI command packets are formatted within the driver. In the ; latter case, command processing and error recovery are implemented ; within a third-party SCSI class driver derived from this driver. ; ; Passthru mode is the method of access used by the generic SCSI ; class driver (GKDRIVER). GKDRIVER provides access to a SCSI device ; from an application program. The QIO interface of the GKDRIVER ; is fixed. However, third-party SCSI class drivers can define a ; unique QIO interface. Third-party class drivers can have device ; specific error recovery, log device errors and implement asynchronous ; event notification (AEN). Third-party class drivers have direct access ; to the SCSI Port Interface (SPI) routines, while using the passthru ; function provides access to SCSI without writing a driver. ; ; The code to perform the IO$_DIAGNOSE function is included in this ; driver for informational purposes only. Typical third-party SCSI ; class drivers do not require this function. If the IO$_DIAGNOSE ; function is required, you should use the VMS-supported SCSI ; generic class driver (GKDRIVER). ; ; SKDRIVER supports three I/O functions: ; ; IO$_AVAILABLE - Inquiry and Test Unit Ready sequence, ; IO$_DIAGNOSE - Passthru function ; IO$_READLBLK - Return Inquiry data to user ;++ ; MODIFIED BY: ; ; X-2 CRB0267 Colin R. Blake 18-DEC-1991 11:47:55.38 ; This is the C2 version being inserted into mainline. ; ; T-1 RLA0286 Robert L. Adams 22-Apr-1991 15:26 ; add support for privilege auditing. ; ;-- .SBTTL + .SBTTL + SYMBOL DEFINITIONS .SBTTL + .SBTTL External symbol definitions ;+ ; External symbols ;- $CRBDEF ; Channel request block $DDBDEF ; Device data block $DEVDEF ; Device characteristics $EMBDEF ; Error log message buffer $DYNDEF ; Data structure types $FKBDEF ; Define fork block symbols $IODEF ; I/O function codes $IPLDEF ; Hardware IPL definitions $IRPDEF ; I/O request packet $NSADEF ; security symbols $ORBDEF ; Object rights block $PCBDEF ; Process control block $PRVDEF ; Privilege mask $SCDRPDEF ; SCSI SCDRP symbols $SSDEF ; System status codes $UCBDEF ; Unit control block $VECDEF ; Interrupt vector block .SBTTL Miscellaneous local symbols ;+ ; Local symbols ;- ;+ ; Argument list (AP) offsets for device-dependent QIO parameters ;- P1 = 0 ; First QIO parameter P2 = 4 ; Second QIO parameter P3 = 8 ; Third QIO parameter P4 = 12 ; Fourth QIO parameter P5 = 16 ; Fifth QIO parameter P6 = 20 ; Sixth QIO parameter SCDRPS_PER_UNIT = 2 ; Number of SCDRPs to allocate per unit UCB_STACK_SIZE = 10 ; Size of internal stack in UCB MAX_BCNT = ^XFFFF ; Maximum byte count .IIF NDF DT$_GENERIC_SCSI, DT$_GENERIC_SCSI = 5 ; GENERIC SCSI DEVICE ASSEMBLE_PASSTHRU = 1 ; If 0 don't assemble DIAG code, if 1 do. SCSI$M_STS = ^XC1 ; Used to extract vendor unique STS bits. DIAG_BUF_LEN = 60 ; Length in bytes of DIAGNOSE input buffer. MAX_CMD_LEN = 248 ; Maximum size in bytes of a SCSI CMD. INQ_DATA_LEN = 36 ; Exact number of INQUIRY bytes required. NUM_ARGS = 10 ; Number of SET/GET CONNECTION CHAR arguments. .SBTTL SCSI Peripheral Device Types ;+ ; Define SCSI Peripheral Device Types ;- SCSI_C_DA = 0 ; Direct Access SCSI_C_SA = 1 ; Sequential Access SCSI_C_PT = 2 ; Printer SCSI_C_PR = 3 ; Processor SCSI_C_WR = 4 ; Write-once Read-multiple SCSI_C_RO = 5 ; Read-only direct access .SBTTL Sense key codes ;+ ; Define SCSI sense key codes. ;- SCSI_C_NO_SENSE = 0 ; No sense data SCSI_C_RECOVERED_ERROR = 1 ; Recovered error (treated as success) SCSI_C_NOT_READY = 2 ; Device not ready SCSI_C_MEDIUM_ERROR = 3 ; Medium (parity) error SCSI_C_HARDWARE_ERROR = 4 ; Hardware error SCSI_C_ILLEGAL_REQUEST = 5 ; Illegal request SCSI_C_UNIT_ATTENTION = 6 ; Unit attention (media change, reset) SCSI_C_DATA_PROTECT = 7 ; Data protection (write lock error) SCSI_C_BLANK_CHECK = 8 ; Blank check (advance past end of data) SCSI_C_VENDOR_UNIQUE = 9 ; Vendor unique key SCSI_C_COPY_ABORTED = 10 ; Copy operation aborted SCSI_C_ABORTED_COMMAND = 11 ; Command aborted SCSI_C_EQUAL = 12 ; Compare operation, data match SCSI_C_VOLUME_OVERFLOW = 13 ; Write beyond physical end of tape SCSI_C_MISCOMPARE = 14 ; Compare operation, data mismatch ;++ ; Define offsets in various SCSI command packets. ;-- ;+ ; REQUEST SENSE data offsets. ;- SCSI_XS_B_ERR_CODE = 0 ; Extended sense error code SCSI_XS_B_KEY = 2 ; Extended sense KEY field SCSI_XS_V_KEY = 0 ; Extended sense KEY bit number SCSI_XS_S_KEY = 4 ; Extended sense KEY length SCSI_XS_B_ADDNL_INFO = 3 ; Extended sense additional code SCSI_XS_B_ADDNL_CODE = 12 ; Extended sense additional code SCSI_XS_B_ADDNL_CODE30 = 8 ; " " (TZ30) SCSI_XS_B_ADDNL_CODE50 = 8 ; " " (TZK50) SCSI_XS_M_EOF = ^X80 ; Extended sense end of file SCSI_XS_M_EOM = ^X40 ; Extended sense end of medium SCSI_XS_M_ILI = ^X20 ; Extended sense illegal length indicator SCSI_XS_V_ADDNL_VALID = 7 ; Extended sense additional data valid ;+ ; INQUIRY data offsets. ;- SCSI_INQ_B_DEVTYPE = 0 ; Inquiry device type SCSI_INQ_B_DEVQUAL = 1 ; Inquiry device qualifier field SCSI_INQ_V_DEVQUAL = 0 ; Inquiry device qualifier starting bit SCSI_INQ_S_DEVQUAL = 7 ; Inquiry device qualifier length SCSI_INQ_V_REMOVABLE = 7 ; Inquiry removable bit SCSI_SKIP_B_CNT = 2 ; Skip record count ;+ ; MODE SELECT/SENSE data offsets. ;- SCSI_MSNS_B_WP = 2 ; Mode sense write protect field SCSI_MSNS_V_WP = 7 ; Mode sense write protect bit SCSI_MSEL_W_RSVD0 = 0 ; Mode select reserved SCSI_MSEL_B_SPEED = 2 ; Mode select speed field SCSI_MSEL_B_MODE = 2 ; Mode select buffered mode SCSI_MSEL_B_DSCLEN = 3 ; Mode select record descriptor length SCSI_MSEL_C_DSCLEN = 8 ; Mode select record descriptor length SCSI_MSEL_B_DENS = 4 ; Mode select density SCSI_MSEL_B_BLOCKS = 5 ; Mode select number of blocks SCSI_MSEL_B_RSVD1 = 8 ; Mode select reserved SCSI_MSEL_B_BLKLEN = 9 ; Mode select block length SCSI_MSEL_B_VULEN = 12 ; Mode select vendor unique length SCSI_MSEL_B_VU = 13 ; Mode select vendor unique field SCSI_MSEL_M_BUF = ^X10 ; Mode select buffered mode SCSI_MSEL_M_NOF = 7 ; Number of fillers for generic device SCSI_MSEL_M_NOF50 = 7 ; Number of fillers for TZK50 SCSI_MSEL_M_NOF30 = ^X0F ; Number of fillers for TZ30 SCSI_MSEL_M_RESEL = ^X40 ; Reselection timeout flag ;+ ; SPI interface, Get/set connect characteristics symbols. ;- SET_CON_L_LEN = 0 ; Length field SET_CON_L_CON_FLAGS = 4 ; Flags field SET_CON_M_DISC = 1 ; Enable disconnect flag SET_CON_M_NORETRY = 2 ; Disable command retry flag SET_CON_L_SYN_FLAG = 8 ; Synchronous flag field SET_CON_M_SYN = 1 ; Synchronous flag .SBTTL Template class driver extensions to the UCB ;+ ; Template class driver extensions to the UCB. ;- $DEFINI UCB ; Start of UCB definitions .=UCB$K_LCL_DISK_LENGTH ; Position at end of UCB $DEF UCB_L_STACK_PTR .BLKL 1 ; Internal stack pointer $DEF UCB_L_STACK .BLKL UCB_STACK_SIZE ; Internal stack $DEF UCB_L_SCDRP .BLKL 1 ; Address of active SCDRP $DEF UCB_L_SCDT .BLKL 1 ; SCDT address $DEF UCB_L_SK_FLAGS .BLKL 1 ; Class driver flags _VIELD UCB,0,<- ; ,- ; Device supports disconnect ,- ; Disable error logging > ; Device supports synchronous operation $DEF UCB_W_PHASE_TMO .BLKW 1 ; Phase change timeout $DEF UCB_W_DISC_TMO .BLKW 1 ; Disconnect timeout $DEF UCB_L_SCDRPQ_FL .BLKL 1 ; Queue of free SCDRPs used to $DEF UCB_L_SCDRPQ_BL .BLKL 1 ; send SCSI commands $DEF UCB_L_SAVER6 .BLKL 1 ; Safe place for R6. $DEF UCB_L_SAVER7 .BLKL 1 ; Safe place for R7. $DEF UCB_L_SCDRP_SAV1 .BLKL 1 ; Safe place for SCDRP address. $DEF UCB_B_LUN .BLKB 1 ; Save device LUN $DEF UCB_K_SK_UCBLEN ; Length of extended UCB $DEFEND UCB ; End of UCB definitions .SBTTL Error log packet formats ;+ ; The following are the definitions for class driver error log packets. ; The VMS error log formatter formats third-party SCSI class driver ; error log packets. The ERF utility formats a standard error ; log packet for third-party class drivers. The standard packet is defined ; below. If a user would like to dump additional data to the error log, simply ; increase the size of the error log packet defined. The additional data ; will be dumped as untranslated longwords in the error log. ;- $DEFINI ERROR_PACKETS . = EMB$L_DV_REGSAV ; Start of area to dump error info $DEF ERR_LW_CNT .BLKL 1 ; Count of number of longwords that follow $DEF ERR_REVISION .BLKB 1 ; Revision level $DEF ERR_HW_REV .BLKL 1 ; Hardware revision $DEF ERR_TYPE .BLKB 1 ; Error type $DEF ERR_SCSI_ID .BLKB 1 ; SCSI ID $DEF ERR_SCSI_LUN .BLKB 1 ; SCSI logical unit $DEF ERR_SCSI_SUBLUN .BLKB 1 ; SCSI sublogical unit $DEF ERR_PORT_STATUS .BLKL 1 ; Port status code $DEF ERR_CMD_LEN .BLKB 1 ; SCSI command length field $DEF ERR_CMD_BYTES .BLKB 12 ; Maximum possible command bytes $DEF ERR_SCSI_STS .BLKB 1 ; SCSI status byte $DEF ERR_TXT_LEN .BLKB 1 ; Error message text size $DEF ERR_TXT_BYTES .BLKB 60 ; Maximum possible text bytes .=.+4 ; Reserve one longword after end of defined packet. $DEF ERR_K_COMMAND_LENGTH ; Length of packet containing SCSI command $DEFEND ERROR_PACKETS .SBTTL SCSI Class driver error log types. ;+ ; SCSI class driver error log types. Each error that is logged by the ; class driver should have a unique error type. ;- CLS_DRV_ERROR_01 = 1 ; Class driver specific error type. CLS_DRV_ERROR_02 = 2 ; Class driver specific error type. CLS_DRV_ERROR_03 = 3 ; Class driver specific error type. CLS_DRV_ERROR_04 = 4 ; Class driver specific error type. CLS_DRV_ERROR_05 = 5 ; Class driver specific error type. CLS_DRV_ERROR_06 = 6 ; Class driver specific error type. .SBTTL + .SBTTL + MACRO DEFINITIONS .SBTTL + .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.) ; Class drivers can specify on a command by command basis the DMA Timeout and ; Disconnect Timeout values. The disconnect timeout is the maximum number ; of seconds that an I/O can be disconnected from the bus. A timeout of -1 ; allows an infinite timeout. The DMA timeout is the maximum timeout for ; a DMA transfer to complete or a phase change on the SCSI bus to occur; ; this timeout is also in units of seconds. ; The SETUP_CMD routine uses this information in preparing to send a SCSI ; command. The macro generates a label and the SCSI command information as ; follows: ; ; +-----------------------+ ; | SCSI cmd length | 1 byte ; +-----------------------+ ; | SCSI cmd bytes | n bytes ; +-----------------------+ ; | DMA buffer length | 2 bytes ; +-----------------------+ ; | DMA direction | 1 byte ; +-----------------------+ ; | DMA Timeout | 1 longword ; +-----------------------+ ; | Disconnect Timeout | 1 longword ; +-----------------------+ ; ; ; DMA direction is defined as: 0=write, 1=read. ;- .MACRO SCSI_CMD, NAME, CMD_BYTES, DMA_LEN=0, DMA_DIR=READ,- DMA_TMO=0, DISCON_TMO=0 'NAME'_CMD: $$$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 .LONG DMA_TMO .LONG DISCON_TMO .ENDM SCSI_CMD .SBTTL LOG_ERROR - Log a SCSI class driver error ;+ ; LOG_ERROR ; ; This macro logs a SCSI class driver error. The error type and VMS status ; code are placed in R7 and R8 respectively, and the LOG_ERROR routine is ; called. ;- .MACRO LOG_ERROR,TYPE,VMS_STATUS,UCB=R3,MESSAGE='',?LABEL_1 .SHOW EXPANSIONS PUSHR #^M ; Save registers .IF DIF UCB,R5 MOVL UCB,R5 ; Get UCB address .ENDC MOVL #'TYPE',R7 ; Get error code MOVL VMS_STATUS,R8 ; And VMS status code .IF LESS_THAN 60-%LENGTH(MESSAGE) ; Maximum size message is 60 .ERROR ; Message text is greater than 60 characters .ENDC .SAVE_PSECT LOCAL_BLOCK .PSECT $$$111_TEXT LABEL_1: .ASCIC /'MESSAGE'/ .RESTORE_PSECT MOVAL LABEL_1,R11 BSBW LOG_ERROR ; Write an error log entry POPR #^M ; Restore registers .NOSHOW EXPANSIONS .ENDM LOG_ERROR .SBTTL WORD_BRANCHES - Define word displacement branches ;+ ; WORD_BRANCHES ; ; This macro defines for each Bxxx (conditional branch) instruction an equivalent ; macro named BxxxW with a word displacement. The macro takes as an argument ; a list of tuples, each tuple containing 3 items: 1) a conditional branch ; opcode; 2) the opcode with the opposite polarity; and 3) the number of ; arguments required by the opcode. ;- .MACRO WORD_BRANCHES LIST .MACRO WORD_BRANCHES2, OPCODE1, OPCODE2, ARGCNT .IF EQ ARGCNT-0 .MACRO OPCODE1, DST, ?L OPCODE2 L BRW DST L: .ENDM OPCODE1 .ENDC .IF EQ ARGCNT-1 .MACRO OPCODE1, FIELD, DST, ?L OPCODE2 FIELD,L BRW DST L: .ENDM OPCODE1 .ENDC .IF EQ ARGCNT-2 .MACRO OPCODE1, BIT, FIELD, DST, ?L OPCODE2 BIT,FIELD,L BRW DST L: .ENDM OPCODE1 .ENDC .ENDM WORD_BRANCHES2 .MACRO WORD_BRANCHES1, OPCODE1, OPCODE2, ARGCNT WORD_BRANCHES2 'OPCODE1'W, OPCODE2, ARGCNT WORD_BRANCHES2 'OPCODE2'W, OPCODE1, ARGCNT .ENDM WORD_BRANCHES1 .IRP ENTRY, WORD_BRANCHES1 ENTRY .ENDR .ENDM WORD_BRANCHES WORD_BRANCHES <- ,- ,- ,- ,- ,- ,- ,- ,- ,- ,- ,- > .SBTTL INIT_UCB_STACK - Initialize the internal UCB stack .SBTTL SUBPUSH - Push an item on the UCB stack .SBTTL SUBPOP - Pop an item from the UCB stack .SBTTL SUBSAVE - Save a return address on the UCB stack .SBTTL SUBRETURN - Return to the address saved on the UCB stack ;+ ; INIT_UCB_STACK ; SUBPUSH ; SUBPOP ; SUBSAVE ; SUBRETURN ; ; These macros manipulate the UCB internal stack, which is used to save ; routine return address and temporary variables. ;- .MACRO INIT_UCB_STACK,UCB=R5,?L1 MOVAL UCB_L_STACK-4(UCB),- UCB_L_STACK_PTR(UCB) .ENDM INIT_UCB_STACK .MACRO SUBPUSH,ARG,UCB=R3,?L1,?L2 ADDL #4,UCB_L_STACK_PTR(UCB) MOVL ARG,@UCB_L_STACK_PTR(UCB) .ENDM SUBPUSH .MACRO SUBPOP,ARG,UCB=R3,?L1,?L2 MOVL @UCB_L_STACK_PTR(UCB),ARG SUBL #4,UCB_L_STACK_PTR(UCB) .ENDM SUBPOP .MACRO SUBSAVE,UCB=R3,?L1,?L2 SUBPUSH (SP)+,UCB .ENDM SUBSAVE .MACRO SUBRETURN,UCB=R3,?L1,?L2 SUBPOP -(SP),UCB RSB .ENDM SUBRETURN .SBTTL SK_WAIT - Stall a thread for a specific number of seconds ;+ ; SK_WAIT ; ; This macro uses the device timeout mechanism to stall a thread for a specified ; number of seconds. The UCB address and stall time are required as inputs. ;- .MACRO SK_WAIT,SECONDS,UCB=R5,SCRATCH=R0,?L .IF DIF UCB,R5 MOVL R5,SCRATCH MOVL UCB,R5 MOVL SCRATCH,UCB .ENDC DSBINT ENVIRON=UNIPROCESSOR PUSHL SECONDS BSBW SK_WAIT .WORD L-. L: IOFORK BICW #UCB$M_TIMOUT,- UCB$W_STS(R5) .IF DIF UCB,R5 MOVL UCB,SCRATCH MOVL R5,UCB MOVL SCRATCH,R5 .ENDC .ENDM SK_WAIT .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. ;- .IIF NDF DPT$M_NO_IDB_DISPATCH, DPT$M_NO_IDB_DISPATCH = ^X1000 DPTAB - ; DPT-creation macro END=SK_END,- ; End of driver label ADAPTER=NULL,- ; Adapter type UCBSIZE=,- ; Length of UCB NAME=SKDRIVER,- ; Driver name FLAGS= ; Don't fill in IDB$L_UCBLST DPT_STORE INIT ; Start of load ; initialization table DPT_STORE UCB,UCB$L_MAXBCNT,L,MAX_BCNT ; Maximum 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_AVL!- ; Available DEV$M_IDV!- ; Input device DEV$M_ODV!- ; Output device DEV$M_SHR!- ; Shareable Device DEV$M_ELG!- ; Error logging enabled DEV$M_RND> ; Random Access Device DPT_STORE UCB,UCB$L_DEVCHAR2,L,<- ; Device characteristics DEV$M_NNM> ; Prefix name with "node$" DPT_STORE UCB,UCB$B_DEVTYPE,B,DT$_GENERIC_SCSI ; Generic SCSI device DPT_STORE UCB,UCB$B_DEVCLASS,B,DC$_MISC ; Sample device class DPT_STORE UCB,UCB$W_DEVSTS,W,- ; Set no logical to physical UCB$M_NOCNVRT ; block number conversion DPT_STORE REINIT ; Start of reload ; initialization table DPT_STORE DDB,DDB$L_DDT,D,SK$DDT ; Address of DDT DPT_STORE CRB,- ; Address of controller CRB$L_INTD+VEC$L_INITIAL,- ; initialization routine D,SK_CTRL_INIT DPT_STORE CRB,- ; Address of device CRB$L_INTD+VEC$L_UNITINIT,- ; unit initialization D,SK_UNIT_INIT ; routine DPT_STORE CRB,CRB$B_FLCK,B,IPL$_IOLOCK8 ; Initialize fork lock field DPT_STORE END ; End of initialization ; tables .SBTTL Driver dispatch table ;+ ; Driver dispatch table ; ; This table defines the entry points into the driver. ;- DDTAB - ; DDT-creation macro DEVNAM=SK,- ; Name of device START=SK_STARTIO,- ; Start I/O routine FUNCTB=SK_FUNCTABLE,- ; FDT address REGDMP=SK_REG_DUMP ; Register dump routine .SBTTL Function decision table ;+ ; Function decision table ; ; This table lists the $QIO function codes implemented by the driver and the ; preprocessing routines used by each function. ;- SK_FUNCTABLE: ; FDT for driver FUNCTAB,- ; Valid I/O functions ; Special pass-through function FUNCTAB,<> ; Buffered I/O functions FUNCTAB SK_READ, ; Issue SCSI INQUIRY command. FUNCTAB +EXE$ZEROPARM, ; Issue SCSI INQUIRY command. FUNCTAB SK_DIAGNOSE, ; Special pass-through function .SBTTL SCSI Command Packet Definition Table SK_CMD_DEFS:: SCSI_CMD - NAME = TEST_UNIT_READY,- CMD_BYTES = <0, 0, 0, 0, 0, 0> SCSI_CMD NAME = INQUIRY,- CMD_BYTES = <18 , 0, 0, 0, 36, 0>,- DMA_LEN = 36,- DMA_DIR = READ,- DMA_TMO = 0,- ; Use default DISCON_TMO = 0 ; Use default SCSI_CMD NAME = REQUEST_SENSE,- CMD_BYTES = <3, 0, 0, 0, 18, 0>,- DMA_LEN = 18,- DMA_DIR = READ,- DMA_TMO = 0,- ; Use default DISCON_TMO = 0 ; Use default SCSI_CMD NAME = MODE_SELECT,- CMD_BYTES = <21, 0, 0, 0, 4, 0>,- DMA_LEN = 4,- DMA_DIR = WRITE,- DMA_TMO = 0,- ; Use default DISCON_TMO = 0 ; Use default SCSI_CMD NAME = QIO_INQUIRY,- ; Normally this would be CMD_BYTES = <18 , 0, 0, 0, 0, 0>,-; a read/write command. DMA_LEN = -1,- ; If data goes to user buffer, DMA_DIR = READ,- ; then use -1 here. DMA_TMO = 0,- ; Use default DISCON_TMO = 0 ; Use default SK_CMD_DEFS_END =. .SBTTL + .SBTTL + DRIVER ENTRY POINTS .SBTTL + .SBTTL SK_CTRL_INIT - Controller initialization routine ;++ ; SK_CTRL_INIT ; ; This routine is called to perform controller-specific initialization and ; is called by the operating system in three places: ; ; - at system startup ; - during driver loading and reloading ; - during recovery from a power failure ; ; Currently this routine is a NOP. ; ; INPUTS: ; ; R4 - address of the CSR (controller status register) ; R5 - address of the IDB (interrupt data block) ; R6 - address of the DDB (device data block) ; R8 - address of the CRB (channel request block) ; ; OUTPUTS: ; ; All registers preserved ;-- SK_CTRL_INIT: MOVZWL #SS$_NORMAL,R0 ; Set success status RSB ; Return to caller .SBTTL SK_UNIT_INIT - Unit initialization routine ;++ ; SK_UNIT_INIT ; ; This routine allocates a set of SCDRPs and places them on a queue in the ; UCB, forms a connection to the port driver by calling SPI$CONNECT, and ; sets the unit online. ; ; INPUTS: ; ; R5 - UCB address ; ; OUTPUTS: ; ; R0-R3 - Destroyed ; All other registers preserved ;-- SK_UNIT_INIT: ; Initialize unit BBC #UCB$V_POWER,- ; Branch if we're not here due to a UCB$W_STS(R5),2$ ; powerfail RSB ; Otherwise, exit immediately ;+ ; Fork twice for now to allow the port driver's unit init routine to execute ; before ours. ;- 2$: FORK ; Fork to drop IPL to SYNCH FORK ; 2nd Fork synchronizes with port driver. INIT_UCB_STACK ; Initialize the internal stack in the UCB MOVAL UCB_L_SCDRPQ_FL(R5),R0 ; Initialize the SCDRP queue header MOVL R0,(R0) ; in the UCB MOVL R0,4(R0) ; MOVL #SCDRPS_PER_UNIT,R4 ; Number of SCDRPs allocated per unit 10$: MOVL #,R1 ; Length of SCDRP MOVL R5,R3 ; Copy UCB address BSBW ALLOC_POOL ; Go allocate an SCDRP MOVW R1,SCDRP$W_SCDRPSIZE(R2); Save length of SCDRP INSQUE SCDRP$L_FQFL(R2),- ; Place SCDRP in UCB queue UCB_L_SCDRPQ_FL(R5) ; SOBGTR R4,10$ ; Repeat for all SCDRPs ;+ ; All SCSI device 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 then used as the ID ; while the remainder is the LUN. Note that the unit number contains three ; digits because early versions of SCSI provided for sublogical unit numbers. ; This feature has since been removed and the second digit in the unit number ; is not used. ;- MOVZWL #SS$_BADPARAM,R0 ; Assume bad LUN or SUBLUN specified MOVZWL UCB$W_UNIT(R5),R1 ; Get device unit number CLRL R2 ; Prepare for extended divide EDIV #100,R1,R1,R2 ; Extract SCSI bus ID from LUN CMPL R1,#7 ; Valid SCSI ID (0 <= n <= 7)? BGTRUW 20$ ; Branch if not CMPL R2,#7 ; Valid LUN (0 <= n <= 7)? BGTRUW 20$ ; Branch if not MULB3 #<1@5>,R2,UCB_B_LUN(R5) ; Save LUN (shifted left 5 bits for use ; later in SETUP_CMD) ASHL #16,R1,R1 ; Place SCSI ID in high-order word of R1 ASHL #16,R2,R2 ; Place LUN in high-order word of R2 MOVL UCB$L_DDB(R5),R0 ; Get DDB address SUBB3 #^A'A',- ; Translate controller letter to DDB$T_NAME+3(R0),R1 ; SCSI bus ID SPI$CONNECT ; Connect to the port driver BLBC R0,20$ ; Branch if connect attempt failed CMPL R1,UCB$L_MAXBCNT(R5) ; For MAXBCNT, use minimum supported BGEQ 15$ ; value of port and class drivers MOVL R1,UCB$L_MAXBCNT(R5) ; Save maximum byte count in UCB 15$: MOVL R2,UCB_L_SCDT(R5) ; Save SCDT address MOVL R4,UCB$L_PDT(R5) ; Save PDT address BISW #UCB$M_ONLINE,- ; Set unit online UCB$W_STS(R5) ; 20$: RSB ; Return to caller .SBTTL + .SBTTL + QIO FDT INTERFACE ROUTINES .SBTTL + .SBTTL SK_READ - FDT preprocessing for sending SCSI Inquiry command. ;++ ; SK_READ ; ; This routine performs FDT preprocessing including: ; ; - Validating access to, and locking, the read/write buffer ; ; INPUTS: ; ; R0 - Address of FDT routine ; R3 - IRP address ; R4 - PCB address ; R5 - UCB address ; R6 - CCB address ; R7 - Bit number of user-specified I/O function code ; R8 - Address of current entry in FDT ; AP - Address of first function-dependent argument (P1) ; ; OUTPUTS: ; ;-- SK_READ: ;+ ; Use system routines to execute I/O preprocessing. ;- TSTL P2(AP) ; There must be bytes to receive. BEQL BADPARAM ; Bad input parameters. JMP G^EXE$MODIFY ; Lock down pages, set up IRP, ; JUMP to EXE$QIODRVPKT, etc... BADPARAM: MOVZWL #SS$_BADPARAM,R0 ; Set bad parameter status JMP G^EXE$ABORTIO ; Abort the I/O with status in R0 .SBTTL SK_DIAGNOSE - FDT preprocessing for special pass-through function ;++ ; SK_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 nonpaged pool ; ; INPUTS: ; ; R0 - Address of FDT routine ; R3 - IRP address ; R4 - PCB address ; R5 - UCB address ; R6 - CCB address ; R7 - Bit number of user-specified I/O function code ; R8 - Address of current entry in FDT ; AP - Address of first function-dependent argument (P1) ; ; OUTPUTS: ; ;-- DSC_OPCODE = 0 DSC_FLAGS = 4 DSC_CMDADR = 8 DSC_CMDLEN = 12 DSC_DATADR = 16 DSC_DATLEN = 20 DSC_PADCNT = 24 DSC_PHSTMO = 28 DSC_DSCTMO = 32 SK_DIAGNOSE: .IF NOT_EQUAL ASSEMBLE_PASSTHRU ; Flag to control assembly of IO$_DIAGNOSE ;+ ; Check privileges and do the appropriate audits if necessary. ;- MOVL R2,-(SP) ; Save the contents of R2 MOVL UCB$L_ORB(R5),R2 ; Get orb address - we will find the name in of the device in there CLRQ -(SP) ; Clear the terminator and retlen at the end of our itemlist MOVL ORB$L_NAME_POINTER(R2),-(SP) ; bufadr = pointer to the character string object name. MOVZWL ORB$W_NAME_LENGTH(R2),R2 ; get the length from the orb ADDL3 #,R2,-(SP) ; put the itemcode and bufsiz together in itemlist AUDIT_S_ITMLST = 16 ; The itemlist is 16 bytes long (term = 4, retlen = 4, bufadr = 4, bufsiz = 2, itmcod = 2) MOVL SP,R2 ; Get the address of the itemlist $IFPRIV DIAGNOSE, - ; Branch if process has DIAGNOSE priv 10$, - MSG=DIAGNOSE_7, - ITMLST=R2, - PRESERVE=NO ADDL #AUDIT_S_ITMLST,SP ; Clear the itemlist off the stack MOVL (SP)+,R2 ; Restore the contents of R2 MOVZWL #SS$_NOPRIV,R0 ; Set no privilege status BRW 50$ ; Branch to abort the I/O ;+ ; First, check that we have read access to the user's descriptor. ;- 10$: ADDL #AUDIT_S_ITMLST,SP ; Clear the privilege audit itemlist off stack MOVL (SP)+,R2 ; Restore the contents of R2 - scratched in priv auditing MOVQ (AP),R0 ; Get user descriptor address, length MOVL R0,R9 ; Save a copy of descriptor address CMPL R1,#DIAG_BUF_LEN ; Valid descriptor length BLSSW 40$ ; Branch if not JSB G^EXE$WRITECHK ; Check for read access to the descriptor ; buffer (don't return if no access) CMPL DSC_OPCODE(R9),#1 ; Valid opcode? BNEQW 40$ ; Branch if not CMPL DSC_DATLEN(R9),- ; Reasonable read/write data buffer UCB$L_MAXBCNT(R5) ; length? BGTRUW 40$ ; Branch if not CMPL DSC_PADCNT(R9),#511 ; Reasonable pad count? BGTRU 40$ ; Branch if not MOVQ DSC_CMDADR(R9),R0 ; Get SCSI command buffer address, length CMPL R1,#MAX_CMD_LEN ; Valid command length? BGTRU 40$ ; Branch if not JSB G^EXE$WRITECHK ; Check for read access to the command ; buffer (don't return if no access) ADDL #8,R1 ; Reserve space for command buffer overhead JSB G^EXE$ALONONPAGED ; Allocate a buffer in which to copy ; the SCSI command BLBC R0,50$ ; Branch on error MOVL R1,(R2)+ ; Save length of buffer MOVL R2,IRP$L_MEDIA(R3) ; Save the command buffer address MOVL DSC_CMDLEN(R9),R0 ; Get length of the SCSI command MOVL R0,(R2)+ ; Save it in the command buffer PUSHR #^M ; Save registers MOVC3 R0,@DSC_CMDADR(R9),(R2) ; Copy the SCSI command from the user's ; buffer to the buffer in pool POPR #^M ; Restore registers CLRL IRP$L_BCNT(R3) ; Assume no user read/write data MOVL DSC_DATADR(R9),R0 ; Get address of user data buffer BEQL 30$ ; Branch if no user read/write data MOVL DSC_DATLEN(R9),R1 ; Get length of user data buffer BEQL 30$ ; Branch if no user read/write data MOVAL G^EXE$READLOCKR,R2 ; Assume user is performing a read BLBS DSC_FLAGS(R9),20$ ; Branch if this is a read operation MOVAL G^EXE$WRITELOCKR,R2 ; Other check for read access 20$: JSB (R2) ; Check access to and lock down buffer BLBC R0,60$ ; Branch on error 30$: MOVAL IRP$C_CDRP(R3),R0 ; Get address of SCDRP within IRP MOVL DSC_FLAGS(R9),(R0)+ ; Save flags field in IRP/SCDRP MOVAL DSC_PADCNT(R9),R1 ; Get address of pad count field .REPT 3 MOVL (R1)+,(R0)+ ; Save pad count, timeout values .ENDR JMP G^EXE$QIODRVPKT ; Queue the packet to the driver 40$: MOVZWL #SS$_BADPARAM,R0 ; Set bad parameter status 50$: JMP G^EXE$ABORTIO ; Abort the I/O with status in R0 ;+ ; We arrive here if the last FDT operation - checking access to and locking ; down the user's read/write buffer - fails. EXE$READLOCKR or EXE$WRITE LOCKR ; returns to us through a coroutine call to allow us to give up any resources ; which we have allocated during FDT processing. Deallocate the buffer ; containing a copy of the SCSI command, then return from the coroutine call. ; R0 and R1 must be preserved. ;- 60$: PUSHQ R0 ; Save registers MOVL IRP$L_MEDIA(R3),R0 ; Get address of nonpaged pool buffer ; containing SCSI command MOVL -(R0),R1 ; Get length of buffer JSB G^EXE$DEANONPGDSIZ ; Deallocate the packet POPQ R0 ; Restore registers RSB ; Return from coroutine call .ENDC ; IF ASSEMBLE_PASTHRU .IF EQUAL ASSEMBLE_PASSTHRU ; IF IO$_DIAGNOSE not assembled, do this.. MOVZBL #SS$_ILLIOFUNC,R0 ; Specify the error type JMP G^EXE$ABORTIO ; Abort the I/O with status in R0 .ENDC .SBTTL + .SBTTL + STARTIO SCSI COMMAND EXECUTION ROUTINES .SBTTL + .SBTTL SK_STARTIO - Driver STARTIO entry point ;++ ; SK_STARTIO ; ; This routine is the STARTIO entry point into the driver. Its main function ; is to dispatch to the function-code-specific routine that starts a specific ; I/O function. ; ; INPUTS: ; ; R3 - IRP address ; R5 - UCB address ; ; OUTPUTS: ; ; R0 - 1st longword of I/O status: contains status code and ; number of bytes transferred ; R1 - 2nd longword of I/O status: low-order word contains high-order ; word of number of bytes transferred ; R4 - Destroyed ; All other registers preserved ;-- SK_STARTIO: .ENABLE LSB ; SK_STARTIO INIT_UCB_STACK ; Initialize the internal stack in the UCB MOVL UCB$L_PDT(R5),R4 ; Get PDT address MOVL R3,R2 ; Copy IRP address MOVL R5,R3 ; Copy UCB address BSBW ALLOC_SCDRP ; Allocate an SCDRP MOVL R2,SCDRP$L_IRP(R5) ; Save IRP address in SCDRP EXTZV #IRP$V_FCODE,- ; Extract I/O function code #IRP$S_FCODE,- ; IRP$W_FUNC(R2),R1 ; ASSUME IRP$S_FCODE LE 7 ; Allow byte mode dispatch DISPATCH R1,TYPE=B,<- ; Dispatch according to function ,- ,- > ;+ ; Bogus I/O function code will fall through. Set illegal function code ; status and complete the I/O. ;- IO_BOGUS: MOVZBL #SS$_ILLIOFUNC,R0 ; Specify the error type ; BRB COMPLETE_IO ; Fall through to exit path for ; if other error then uncomment. COMPLETE_IO: BSBW DEALLOC_SCDRP ; Deallocate the SCDRP MOVL R3,R5 ; Copy UCB address REQCOM ; Complete the I/O .DISABLE LSB ; SK_STARTIO .SBTTL IO_INQUIRY - Send SCSI INQUIRY command. ;++ ; IO_INQUIRY ; ; This routine is intended as an example of how to write a STARTIO ; routine for a SCSI class driver. ; ; This routine sends an inquiry command to the target. If ; errors occur during the execution of this operation no retries ; occur. However, this class driver issues a REQUEST SENSE to ; determine the nature of the event. If the event is fatal, the ; error is logged and the I/O fails. If the event is ; benign, then the I/O completes with a REQCOM. ; ; IO_INQUIRY calls the port driver to allocate command buffer areas, ; maps the system or user buffer such that the port driver has access ; to these areas, and then calls the port driver's SEND_CMD entry point ; to send the SCSI command to a target. ; When the port driver returns from this call, the INQUIRY data has been ; moved, the command status is in the status-in buffer and the SCSI ; bus is free. The class driver checks the transfer count, releases ; its resources and completes the I/O with a call to REQCOM. ; ; INPUTS: ; ; R3 - UCB address ; R4 - PDT address ; R5 - SCDRP address ; ; OUPUTS: ; ; R0 - Status ; ; SS$_NORMAL - I/O completed successfully. ; SS$_ILLSEQOP - I/O failed, bad sense key. ; SS$_IVSTSFLG - Invalid SCSI status returned. ; SS$_OPINCMPL - I/O failed, insufficient data returned. ; ;-- IO_INQUIRY: .ENABLE LSB ; IO_INQUIRY MOVAL INQUIRY_CMD,R2 ; Address of INQUIRY command BSBW SETUP_CMD ; Perform setup for SCSI command BLBC R0,35$ ; BSBW SEND_COMMAND ; Send the SCSI command ;+ ; Determine by sending the INQUIRY command, what target is at this ID. ; ; After a call to the port driver, when the port status (R0) and SCSI ; command status have been checked, the class driver must verify that ; the number of bytes that were to be received or sent have been delivered ; by the port driver. SCDRP$L_TRANS_CNT contains the actual number of bytes ; of data transferred by the port driver. ;- BLBC R0,35$ ; Branch on error CMPL SCDRP$L_TRANS_CNT(R5),- ; Sufficient inquiry data returned? #INQ_DATA_LEN ; BLSSUW 34$ ; Branch if not MOVL SCDRP$L_SVA_USER(R5),R1 ; Get address of inquiry data CMPB #SCSI_C_DA,- ; Is this a SCSI disk device? SCSI_INQ_B_DEVTYPE(R1) ; Check INQUIRY data ;*** BNEQ SOMEWHERE ; If it's not the target you want. 30$: BSBW CLEANUP_CMD ; Clean up from the SCSI command ;+ ; Now that the class driver knows what target is out there, determine if ; the target is ready by sending a TEST UNIT READY command. ;- MOVAL TEST_UNIT_READY_CMD,R2 ; Test Unit Ready command BSBW SETUP_CMD ; Perform setup for SCSI command BLBC R0,35$ ; Branch on error BSBW SEND_COMMAND ; Send the SCSI command BLBC R0,35$ ; Branch on error BSBW CLEANUP_CMD ; Clean up from the SCSI command CLRL R1 ; Clean up R1 BRW COMPLETE_IO ; Complete the user's I/O. ;+ ; Any error the class driver encounters is logged. ; R0 contains the VMS status. ;- 34$: MOVZWL #SS$_OPINCOMPL,R0 35$: LOG_ERROR - ; Log an invalid inquiry data error TYPE=CLS_DRV_ERROR_01,- ; VMS_STATUS=R0,- ; I/O operation failed UCB=R3,- ; MESSAGE= BSBW CLEANUP_CMD ; Clean up from the SCSI command CLRL R1 ; Clean up R1 BRW COMPLETE_IO ; Complete the user's I/O. .DISABLE LSB ; IO_INQUIRY .SBTTL IO_READ - Send SCSI INQUIRY command and return data. ;++ ; IO_READ ; ; This routine is intended as an example of how to write a STARTIO ; routine that reads data from a target device and returns the data ; to a user buffer. Normally, some form of read command would be used ; to retrieve data from a target; however the format of read commands ; varies depending on the SCSI device class. Therefore, this ; example uses the INQUIRY command to get data from the target; the ; INQUIRY command is one of the few commands that is common among ; all device types. ; ; Third-party class drivers traditionally do NOT return the INQUIRY ; data to the application. Rather, the class driver uses this ; information to establish the characteristics of the SCSI target ; and the class driver's connection to this target. ; ; IO_READ calls the port driver to allocate command buffer areas, ; maps user read buffer such that the port driver has access to these ; areas and then calls the port driver's SEND_CMD entry point ; to send the SCSI command to a target. When the port driver returns from ; this call, the INQUIRY data has been moved to the user's buffer, ; the command status is in the status-in buffer and the SCSI bus is free. ; The class driver checks the transfer count, releases its resources and ; complete the I/O with a call to REQCOM. ; ; INPUTS: ; ; R3 - UCB address ; R4 - PDT address ; R5 - SCDRP address ; ; OUPUTS: ; ; R0 - Status ; ; SS$_NORMAL - I/O completed successfully. ; SS$_ILLSEQOP - I/O failed, bad sense key. ; SS$_IVSTSFLG - Invalid SCSI status returned. ; SS$_OPINCMPL - I/O failed, insufficient data returned. ; ;-- IO_READ: .ENABLE LSB ; IO_READ ;+ ; WARNING: If the user provides the wrong byte count the SCSI bus may hang. ; SCSI port drivers can recover from this error; however, the recovery mechanism ; may be severe and this I/O request will fail. ;- MOVL #SCDRP$M_BUFFER_MAPPED,-; Set buffer mapped flag to prevent SCDRP$L_SCSI_FLAGS(R5) ; allocation of S0 buffer for data MOVAL QIO_INQUIRY_CMD,R2 ; Address of INQUIRY command for user data BSBW SETUP_CMD ; Perform setup for SCSI command BLBC R0,300$ ; Setup failed SPI$MAP_BUFFER ; Map the user buffer BSBW SEND_COMMAND ; Send the SCSI command ;+ ; The port driver has been called to send the command and now returns ; with the data moved to the user's buffer, the port status in R0, and SCSI ; status in the STATUSIN buffer. The class driver checks the port driver ; and SCSI command status and then verifies that the number of bytes that were ; received equals the BCNT. SCDRP$L_TRANS_CNT contains the actual number ; of bytes of data transferred by the port driver. ;- BLBC R0,35$ ; Branch on error CMPL SCDRP$L_TRANS_CNT(R5),- ; Sufficient inquiry data returned? SCDRP$L_BCNT(R5) BNEQUW 34$ ; Branch if not 30$: MOVL SCDRP$L_TRANS_CNT(R5),R1; Return transaction count in IOSB BSBW CLEANUP_CMD ; Clean up from the SCSI command BRW COMPLETE_IO ; Complete the user's I/O ;+ ; Errors the class driver encounters are logged. ; R0 contains the VMS status. ;- 34$: MOVZWL #SS$_OPINCOMPL,R0 35$: LOG_ERROR - ; Log an invalid inquiry data error TYPE=CLS_DRV_ERROR_04,- ; VMS_STATUS=R0,- ; I/O operation failed UCB=R3,- ; MESSAGE= BSBW CLEANUP_CMD ; Clean up from the SCSI command CLRL R1 ; Clean up R1 BRW COMPLETE_IO ; Complete the user's I/O ;+ ; The template driver does not support segmented I/O. This exercise ; is left to the user. ;- 300$: BICL #SCDRP$M_BUFFER_MAPPED,-; No buffer mapped, so don't unmap. SCDRP$L_SCSI_FLAGS(R5) ; LOG_ERROR - ; Log an invalid inquiry data error TYPE=CLS_DRV_ERROR_05,- ; VMS_STATUS=R0,- ; I/O operation failed. UCB=R3,- ; MESSAGE= CLRL R1 ; Clean up R1 BRW COMPLETE_IO ; Complete the user's I/O .DISABLE LSB ; IO_READ .SBTTL IO_DIAGNOSE - Special pass-through function ;++ ; IO_DIAGNOSE ; ; STARTIO routine for the passthru function of the template SCSI ; class driver. This routine assumes that the user has provided ; a buffer that contains the SCSI command packet and that the ; FDT routines in the driver have made the appropriate checks ; during I/O preprocessing to allow access to the user data areas ; during STARTIO. ; ; IO_DIAGNOSE makes calls into the port driver to allocate command ; buffer areas, maps the user buffer such that the port driver ; can access user areas, and then calls the port driver's SEND_CMD ; entry point to send the SCSI command to a target. When the port driver ; returns from this call, the user's data has been moved, the ; command status is in the status-in buffer and the SCSI bus ; is free. The class driver releases its resources and ; completes the I/O with a call to REQCOM. ; ; INPUTS: ; ; R2 - IRP address ; R3 - UCB address ; R4 - PDT address ; R5 - SCDRP address ; ; OUTPUTS: ; ; R0 - Status ; R1,R2 - Destroyed ; All other registers preserved ;-- IO_DIAGNOSE: .ENABLE LSB ; IO_DIAGNOSE .IF NOT_EQUAL ASSEMBLE_PASSTHRU ; IF assemble IO$_DIAGNOSE if ASSM_PASS. MOVL IRP$L_MEDIA(R2),- ; Copy command buffer from IRP to SCDRP$L_MEDIA(R5) ; SCDRP MOVL IRP$L_SVAPTE(R2),- ; and SVAPTE, SCDRP$L_SVAPTE(R5) ; MOVL IRP$L_BCNT(R2),- ; BCNT, SCDRP$L_BCNT(R5) ; MOVW IRP$W_BOFF(R2),- ; and BOFF SCDRP$W_BOFF(R5) ; MOVW IRP$W_STS(R2),- ; and STS SCDRP$W_STS(R5) ; MOVAL IRP$C_CDRP(R2),R0 ; Get address of SCDRP portion of IRP EXTZV #1,#1,(R0),R1 ; Get disconnect flag INSV R1,#UCB_V_DISCONNECT,- ; Fill in disconnect flag in UCB #1,UCB_L_SK_FLAGS(R3) ; EXTZV #2,#1,(R0),R1 ; Get synchronous flag INSV R1,#UCB_V_SYNCHRONOUS,- ; Fill in synchronous flag in UCB #1,UCB_L_SK_FLAGS(R3) ; ADDL #4,R0 ; Advance to pad count field MOVL (R0)+,- ; Fill in the pad count in the SCDRP SCDRP$L_PAD_BCNT(R5) ; MOVL (R0)+,- ; Fill in the phase change (DMA) timeout SCDRP$L_DMA_TIMEOUT(R5) ; in the SCDRP MOVL (R0)+,- ; Fill in the disconnect timeout in the SCDRP$L_DISCON_TIMEOUT(R5) ; SCDRP BSBW SET_CONN_CHAR ; Set up the connect characteristics MOVL SCDRP$L_MEDIA(R5),R1 ; Get address of SCSI command in pool MOVL (R1)+,R1 ; Get length of SCSI command ADDL #8,R1 ; Account for overhead SPI$ALLOCATE_COMMAND_BUFFER ; Allocate a command buffer MOVL R2,SCDRP$L_CMD_BUF(R5) ; Save address of command buffer CLRL (R2)+ ; Reserve a longword for status MOVB #^XFF,-1(R2) ; Initialize status field MOVAL -1(R2),- ; Address to save status byte SCDRP$L_STS_PTR(R5) ; MOVL R2,SCDRP$L_CMD_PTR(R5) ; Address of SCSI command in cmd buffer MOVL SCDRP$L_MEDIA(R5),R0 ; Get SCSI command in pool again MOVL (R0),(R2)+ ; Copy SCSI command length PUSHR #^M ; Save registers MOVC3 (R0),4(R0),(R2) ; Copy SCSI command to command buffer POPR #^M ; Restore registers MOVL -(R0),R1 ; Get length of command buffer in pool JSB G^EXE$DEANONPGDSIZ ; Deallocate the buffer TSTL SCDRP$L_BCNT(R5) ; Any user data buffer? BEQL 10$ ; Branch if not SPI$MAP_BUFFER ; Map the user's data buffer 10$: SPI$SEND_COMMAND ; Send the SCSI command PUSHL R0 ; Save returned port status TSTL SCDRP$L_BCNT(R5) ; User buffer mapped? BEQL 20$ ; Branch if not SPI$UNMAP_BUFFER ; Unmap the user's data buffer 20$: MOVL SCDRP$L_CMD_BUF(R5),R0 ; Get the command buffer address PUSHL (R0) ; Save the SCSI status byte SPI$DEALLOCATE_COMMAND_BUFFER ; Deallocate the command buffer POPL R1 ; Restore the SCSI status byte POPL R0 ; Restore the port status INSV SCDRP$L_TRANS_CNT(R5),- ; Copy the transfer count to the #16,#16,R0 ; high-order word of R0 .ENDC ; If ASS_DIAG FALSE don't assemble BRW COMPLETE_IO ; Complete the QIO .DISABLE LSB ; IO_DIAGNOSE .SBTTL + .SBTTL + UTILITY ROUTINES .SBTTL + .SBTTL SEND_COMMAND - Send a SCSI command ;++ ; SEND_COMMAND ; ; This routines sends a command to the SCSI device. It returns any failing ; port status to the caller. If the port status is success, it checks the ; SCSI status byte. If a check condition status is returned, a request ; sense command is sent to the target and the sense key is translated into a ; VMS status code, which is returned as status. ; ; INPUTS: ; ; R3 - UCB address ; R4 - PDT address ; R5 - SCDRP address ; ; OUTPUTS: ; ; R0 - Status ; SS$_IVSTSFLG - Invalid SCSI status returned. ; SS$_ILLSEQOP - I/O operation failed. ; R1,R2 - Destroyed ; All other registers preserved ;-- SEND_COMMAND: .ENABLE LSB ; SEND_COMMAND SUBSAVE ; Save return address SPI$SEND_COMMAND ; Send the SCSI command BLBC R0,10$ ; If port failed, return MOVZBL @SCDRP$L_STS_PTR(R5),R1 ; Get SCSI status byte BICB #SCSI$M_STS,R1 ; Clear reserved, vendor-unique bits BNEQ 20$ ; Branch if bad status 10$: SUBRETURN ; Return to caller ;+ ; 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. ;- 20$: CMPB R1,#2 ; Check condition status? BNEQ 90$ ; Branch if not ;+ ; A check condition status code was returned. Save the original SCDRP address, ; allocate a second one and send a request sense command. If the request ; sense succeeds, translate the sense key to a VMS status code and return that ; as the status code for the original command. ;- 45$: MOVL R5,UCB_L_SCDRP_SAV1(R3) ; Save original SCDRP address BSBW ALLOC_SCDRP ; Allocate an additional SCDRP BSBW REQUEST_SENSE ; Send a request sense command BLBC R0,50$ ; Branch on error ; a VMS status code in R0 ;+ ; Look at the results of the request sense to determine the exact nature ; of the event. ;- MOVL SCDRP$L_SVA_USER(R5),R1 ; Get address of REQUEST SENSE DATA. BICB3 #^XF0,SCSI_XS_B_ERR_CODE(R1),- ; First check ERROR CODE. R0 ; In this case zero is good, but this BNEQ 50$ ; is really device specific. BICB3 #^XF0,SCSI_XS_B_KEY(R1),- ; Mask off SENSE KEY. R0 ;+ ; Depending on the value of the sense key, dispatch to the appropriate ; error recovery. ;- DISPATCH R0,TYPE=B,<- ; Dispatch according to SENSE KEY. ,- ; No sense data ,-; Recovered error ,- ; Device not ready ,- ; Medium (parity) error ,- ; Hardware error ,- ; Illegal request ,-; Unit attention ( reset... ) ,- ; Data protection (write lock) ,- ; Blank check ,- ; Vendor unique key ,- ; Copy operation aborted ,- ; Command aborted ,- ; Data match ,- ; Write past physical end > ; Data mismatch ;+ ; Either the sense key was bad or the key was invalid. In either case ; indicate that the command failed. Some class drivers will want ; to translate each bad sense key to a unique class driver SS$_XXXXX ; status code. Here we will always return SS$_ILLSEQOP. ;- SK_BAD: MOVL #SS$_ILLSEQOP,R0 ; I/O operation failed BRB 50$ ; cleanup and return error ;+ ; If the sense key indicated that the operation completed successfully, ; then return success. ;- SK_OK: MOVL #SS$_NORMAL,R0 ; I/O operation succeeded ; BRB 50$ ; Clean up and return error 50$: BSBW CLEANUP_CMD ; Clean up the request sense command BSBW DEALLOC_SCDRP ; Deallocate the request sense SCDRP MOVL UCB_L_SCDRP_SAV1(R3),R5 ; Restore original SCDRP address MOVL R5,UCB_L_SCDRP(R3) ; Copy it to the UCB BRW 10$ ; Return to caller ;+ ; If the status returned for the last command was anything other than ; check condition, log an error and return a status of SS$_IVSTSFLG to ; indicate that command failed and that there is no request sense data. ;- 90$: MOVL #SS$_IVSTSFLG,R0 ; Return a generic status code LOG_ERROR - ; Log a send command error TYPE=CLS_DRV_ERROR_02,- ; Generic user class driver error VMS_STATUS=R0,- ; UCB=R3,- ; MESSAGE= BRW 10$ .DISABLE LSB ; SEND_COMMAND .SBTTL REQUEST_SENSE - Send a request sense command ;++ ; REQUEST_SENSE ; ; This routine is called by SEND_COMMAND when a command fails with check ; condition status. A request sense command is sent to the target. ; ; INPUTS: ; ; R3 - UCB address ; R4 - PDT address ; R5 - SCDRP address ; ; OUTPUTS: ; ; R0 - Status ; SS$_IVSTSFLG - Bad SCSI status returned during ; REQUEST SENSE. ; R1,R2 - Destroyed ; All other registers preserved ;-- REQUEST_SENSE: .ENABLE LSB ; REQUEST_SENSE SUBSAVE ; Save return address MOVAL REQUEST_SENSE_CMD,R2 ; Address of REQUEST_SENSE command BSBW SETUP_CMD ; Perform setup for SCSI command BLBC R0,10$ ; Branch on error SPI$SEND_COMMAND ; Send the SCSI command BLBC R0,10$ ; Return on error MOVZBL @SCDRP$L_STS_PTR(R5),R1 ; Get SCSI status byte BICB #SCSI$M_STS,R1 ; Clear reserved, vendor unique bits BNEQ 20$ ; Branch if bad status 10$: SUBRETURN ; Return to caller 20$: MOVZWL #SS$_IVSTSFLG,R0 ; Return bad SCSI status to caller. BRB 10$ .DISABLE LSB ; REQUEST_SENSE .SBTTL SET_CONN_CHAR - Modify connection characteristics ;++ ; SET_CONN_CHAR ; ; This routine is called to initialize the connection characteristics, which ; specify such things as whether the device supports disconnect and ; synchronous operation, and the bus busy, arbitration, selection, and ; command retry counters. ; ; This routine first does a SPI$GET_CONNECTION_CHAR to get the current ; values of the connection characteristics, modifies the values of interest, ; then does a SPI$SET_CONNECTION_CHAR to set up the new values. This allows ; the class driver to change a subset of the characteristics and leave the ; rest unmodified. ; ; INPUTS: ; ; R3 - UCB address ; R4 - SPDT address ; R5 - SCDRP address ; ; OUTPUTS: ; ; R0-R2 - Destroyed ; All other registers preserved ;-- SET_CONN_CHAR: .ENABLE LSB ; SET_CONN_CHAR SUBSAVE ; Save return address MOVL #<*4>,R1 ; Size of get/set connection char buffer BSBW ALLOC_POOL ; Allocate the buffer SUBPUSH R2 ; Save address of buffer MOVL #NUM_ARGS,(R2) ; Set argument count in buffer SPI$GET_CONNECTION_CHAR ; Get current connection characteristics BLBC R0,10$ ; Branch on error ;+ ; Some devices won't select if selected with attention. ; ; NOTE: It is strongly suggested that targets and devices ; support the disconnect/reselection sequence. All ; Digital-supplied devices support this feature to ; ensure consistent bus performance. ; ; EXTZV #UCB_V_DISCONNECT,#1,- ; Fill in disconnect flag ; UCB_L_SK_FLAGS(R3),4(R2); ;- EXTZV #UCB_V_SYNCHRONOUS,#1,- ; Fill in synchronous flag UCB_L_SK_FLAGS(R3),8(R2); SPI$SET_CONNECTION_CHAR ; Set the connection characteristics 10$: PUSHL R0 ; Save return status SUBPOP R0 ; Get address of characteristics buffer BSBW DEALLOC_POOL ; Deallocate the buffer POPL R0 ; Restore return status BLBS R0,20$ ; Branch if success status MOVL #SS$_CTRLERR,R0 ; Otherwise, return a reasonable status 20$: SUBRETURN ; Return to caller .DISABLE LSB ; SET_CONN_CHAR .SBTTL SK_WAIT - Stall for the specified number of seconds ;++ ; SK_WAIT ; ; This routine is used by the SK_WAIT macro to stall a thread for a specified ; number of seconds. It sets the timeout bit in the UCB and relies on the ; device timeout mechanism to resume the stalled thread. ; ; INPUTS: ; ; IPL - 31 ; R5 - UCB address ; (SP) - Return address ; 4(SP) - Wait time in seconds ; 8(SP) - Saved IPL ; 12(SP) - Address of caller's caller ; ; OUPUTS: ; ; Stack - Return address, wait time, IPL removed ; Control returns to caller's caller ; All registers preserved ; ; NOTE: The use of the SK_WAIT macro destroys R0-R3 ;-- SK_WAIT: MOVQ R3,UCB$L_FR3(R5) ; Save R3 and R4 in fork block ADDL3 #2,(SP)+,UCB$L_FPC(R5) ; Save return address in fork block BISW #UCB$M_TIM,UCB$W_STS(R5); Set timer expected bit ADDL3 (SP)+,G^EXE$GL_ABSTIM,- ; Set up timeout time in UCB UCB$L_DUETIM(R5) ; BICW #UCB$M_TIMOUT,- ; Clear timer expired bit UCB$W_STS(R5) ; ENBINT ; Reenable interrupts RSB ; Return to caller's caller .SBTTL ALLOC_SCDRP - Allocate an SCDRP ;++ ; ALLOC_SCDRP ; ; This routine allocates an SCDRP by attempting to remove one from the queue ; in the UCB. If the queue is empty (which should never happen), then bugcheck. ; The entire SCDRP is zeroed and various fields are initialized. ; ; INPUTS: ; ; R3 - UCB address ; ; UCB_L_SCDRPQ_FL - Queue of SCDRPs ; ; OUTPUTS: ; ; R5 - SCDRP address ; All other registers preserved ; ; SCDRP$L_UCB - UCB address ; SCDRP$L_IRP - IRP address ; SCDRP$L_CDT - SCDT address ; SCDRP$L_SCSI_FLAGS - Initialized ; SCDRP$L_CL_SSK_PTR - Initialized ;-- ALLOC_SCDRP: .ENABLE LSB ; ALLOC_SCDRP REMQUE @UCB_L_SCDRPQ_FL(R3),R5 ; Remove an SCDRP from the queue PUSHR #^M ; Save registers MOVC5 #0,.,#0,- ; Initialize the SCDRP #SCDRP$C_LENGTH-12,- ; 12(R5) ; POPR #^M ; Restore registers MOVL R5,UCB_L_SCDRP(R3) ; Save SCDRP address in UCB MOVL R3,SCDRP$L_UCB(R5) ; Save UCB address in SCDRP MOVB UCB$B_FLCK(R3),- ; Copy the fork lock field from the SCDRP$B_FLCK(R5) ; UCB to the SCDRP MOVL UCB_L_SCDT(R3),- ; Save SCDT address in SCDRP SCDRP$L_CDT(R5) ; MOVAL SCDRP$L_SCSI_STK-4(R5),-; Initialize the SCDRP stack pointer SCDRP$L_SCSI_STK_PTR(R5); RSB .DISABLE LSB ; ALLOC_SCDRP .SBTTL DEALLOC_SCDRP - Deallocate an SCDRP ;++ ; DEALLOC_SCDRP ; ; This routine deallocates an SCDRP by returning it to the queue in the ; UCB. A sanity check is made to ensure that any map registers for this ; command have been deallocated. ; ; INPUTS: ; ; R3 - UCB address ; R5 - SCDRP address ; ; OUTPUTS: ; ; R3 - UCB address ; R5 - UCB address (for _R5 entry point) ; All other registers preserved ; ; UCB_L_SCDRP - Cleared to indicate no active SCDRP ;-- DEALLOC_SCDRP: .ENABLE LSB ; DEALLOC_SCDRP INSQUE SCDRP$L_FQFL(R5),- ; Insert SCDRP in UCB queue UCB_L_SCDRPQ_FL(R3) ; CLRL UCB_L_SCDRP(R3) ; No active SCDRP for this UCB RSB .DISABLE LSB ; DEALLOC_SCDRP .SBTTL ALLOC_POOL - Allocate a block of nonpaged pool ;++ ; ALLOC_POOL ; ; This routine allocates a block of nonpaged 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 of 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 ; alignment purposes (some blocks are used as IRPs, which are placed on ; self-relative queues and require quadword alignment.) ; ; If an allocation failure occurs, the thread is stalled and wakes up once a ; second to retry the allocation. ; ; INPUTS: ; ; R1 - Size of block to allocate ; R3 - UCB address ; ; OUTPUTS: ; ; R0 - Destroyed ; R1 - Size of block allocated ; R2 - Address of allocated block ; -8(R2) - Length of allocated block (used by DEALLOC_POOL) ; All other registers preserved ;-- ALLOC_POOL: .ENABLE LSB ; ALLOC_POOL ADDL #8,R1 ; Reserve a quadword to save size CMPL R1,#FKB$C_LENGTH ; Requested size smaller than fork block? BGEQ 10$ ; Branch if not MOVL #FKB$C_LENGTH,R1 ; Use fork block size as minimum 10$: PUSHL R1 ; Save allocation length PUSHL R3 ; Save UCB address JSB G^EXE$ALONONPAGED ; Allocate a block POPL R3 ; Restore register BLBC R0,20$ ; Branch if error ADDL #4,SP ; Remove allocation length from stack PUSHR #^M ; Save registers MOVC5 #0,.,#0,R1,(R2) ; Initialize the packet POPR #^M ; Restore registers MOVL R1,(R2)+ ; Save size of block ADDL #4,R2 ; Skip a longword RSB ; Return to caller ;+ ; A pool allocation failure occurred. Come back once a second and retry the ; operation until successful. ;- 20$: SUBPUSH (SP)+ ; Save allocation length (PUSHL R1 above) SUBSAVE ; Save return address SK_WAIT #1,UCB=R3 ; Wait a second SUBPOP -(SP) ; Restore return address SUBPOP R1 ; Restore allocation length BRW 10$ ; Try again .DISABLE LSB ; ALLOC_POOL .SBTTL DEALLOC_POOL - Deallocate a block of nonpaged pool ;++ ; DEALLOC_POOL ; ; This routine deallocates a block of nonpaged pool. The size of the block ; is stored in the reserved quadword at a negative offset from the beginning ; of the block. ; ; INPUTS: ; ; R0 - Address of block to deallocate ; -8(R0) - Length of block to deallocate ; ; OUTPUTS: ; ; R0 - Destroyed ; All other registers preserved ;-- DEALLOC_POOL: .ENABLE LSB ; DEALLOC_POOL PUSHQ R1 ; Save R1,R2 SUBL #4,R0 ; Skip a longword MOVL -(R0),IRP$W_SIZE(R0) ; Copy size field CLRB IRP$B_TYPE(R0) ; Clear type field (prevents block from ; being interpreted as shared memory ; during deallocation) JSB G^EXE$DEANONPAGED ; Deallocate the block POPQ R1 ; Restore R1,R2 RSB .DISABLE LSB ; DEALLOC_POOL .SBTTL SETUP_CMD - Common setup for all SCSI commands ;++ ; SETUP_CMD ; ; This routine performs common setup prior to the sending of a SCSI command. ; Setup includes allocating a command buffer, filling in the pointers in the ; SCDRP to the command and status fields, copying the SCSI command to the ; command buffer, allocating an S0 "user" buffer if the command requires ; transferring data to or from the class driver, filling in the SCDRP fields ; used to map this buffer, and mapping the buffer. ; ; Since this routine calls SPI$ALLOCATE_COMMAND_BUFFER, which can suspend ; the thread, the return PC must be saved in the SCDRP. ; ; INPUTS: ; ; R2 - Pointer to entry in SCSI_CMD table ; R4 - PDT address ; R5 - SCDRP address ; ; OUTPUTS: ; ; R0 - Status ; R1,R2 - Destroyed ; ; SCDRP$L_CMD_BUF - Address of SCSI command buffer ; SCDRP$L_CMD_PTR - Address of SCSI command ; SCDRP$L_STS_PTR - Address to save SCSI status byte ; SCDRP$L_SVA_USER- Address of S0 "user" buffer ; SCDRP$L_BCNT - Length of S0 "user" buffer ; SCDRP$W_BOFF - Byte offset of S0 "user" buffer ; SCDRP$L_SVAPTE - SVAPTE of S0 "user" buffer ; IRP$V_FUNC - SET/CLEAR to indicate READ/WRITE from S0 "user" buffer ; SCDRP$L_DMA_TIMEOUT - Time in seconds for a DMA timeout. ; SCDRP$L_DISCON_TIMEOUT - Time in seconds for a disconnect to time out. ; ;-- .ENABLE LSB ; SETUP_CMD SETUP_CMD: SCSI_CMD_BUF_OVHD = 4 + 4 ; 4 bytes to save status byte + ; 4 bytes for SCSI command length SUBSAVE ; Save return address MOVZBL (R2),R1 ; Get size SCSI command ADDL #SCSI_CMD_BUF_OVHD,R1 ; Add in command buffer overhead SUBPUSH R2 ; Save R2 SPI$ALLOCATE_COMMAND_BUFFER ; Allocate a command buffer MOVL R2,R1 ; Copy command buffer address SUBPOP R2 ; Restore R2 MOVB #^XFF,(R1) ; Initialize status field MOVAL (R1)+,- ; Address to put SCSI status byte SCDRP$L_STS_PTR(R5) ; MOVL R1,SCDRP$L_CMD_PTR(R5) ; Save address of SCSI command MOVZBL (R2)+,R0 ; Get SCSI command length MOVL R0,(R1)+ ; Save length in command buffer ASHL #-1,R0,R0 ; Change byte count to word count 10$: MOVW (R2)+,(R1)+ ; Copy a byte of SCSI command SOBGTR R0,10$ ; Repeat for entire SCSI command ;+ ; There is a dependency here that the format of the SCSI_COMMAND record ; does not change. ; Copy the per command timeout values from the SCSI_CMD block to the ; SCSI Class Driver Request Packet. ; ; R2 points at the direction field in the SCSI_CMD block. ;- MOVL 3(R2),- ; Time in seconds for a DMA timeout. SCDRP$L_DMA_TIMEOUT(R5) MOVL 7(R2),- ; Disconnect timeout in seconds. SCDRP$L_DISCON_TIMEOUT(R5) ;+ ; Determine if a buffer has already been mapped. If no buffer has been mapped, ; then allocate a system buffer and map it to receive the data from the ; target device. ;- BBC #SCDRP$V_BUFFER_MAPPED,-; If buffer is mapped then do special SCDRP$L_SCSI_FLAGS(R5),-; setup for this command. 20$ ;+ ; During the STARTIO operation in the class driver, the user's QIO parameters ; must be copied from the IRP to SCDRP (SCSI Class Driver Request Packet). ; The user data is then mapped, the SCSI CMD packet is allocated, and the ; command is sent to a target, over the connection established during UNIT INIT. ;- MOVL UCB$L_IRP(R3),R2 ; Get current I/O's IRP address CLRL SCDRP$L_ABCNT(R5) ; Initialize accumulated byte count MOVW IRP$W_FUNC(R2),- ; Copy function code and modifiers, SCDRP$W_FUNC(R5) ; MEDIA, SVAPTE, and BOFF fields, MOVW IRP$W_STS(R2),- ; and STS SCDRP$W_STS(R5) ; MOVL IRP$L_MEDIA(R2),- ; from the IRP to the SCDRP SCDRP$L_MEDIA(R5) ; MOVL IRP$L_SVAPTE(R2),- ; SCDRP$L_SVAPTE(R5) ; MOVW IRP$W_BOFF(R2),- ; SCDRP$W_BOFF(R5) ; MOVL IRP$L_BCNT(R2),- ; Copy user's BCNT from IRP to SCDRP. SCDRP$L_BCNT(R5) ; CMPL SCDRP$L_BCNT(R5),- ; Transfer length greater than maximum UCB$L_MAXBCNT(R3) ; supported? BGTR 300$ ; GTR, therefore I/O must be segmented CLRL SCDRP$L_PAD_BCNT(R5) ; No padding of last page required ADDL3 #<4+4>,- ; Address of transfer length field in SCDRP$L_CMD_PTR(R5),R1 ; SCSI command MOVB SCDRP$L_BCNT(R5),(R1) ; Copy user-supplied byte count to command. BRW 50$ ; Setup finished. 20$: CVTWL (R2),R1 ; Get length of send data buffer BLSS 50$ ; Branch if negative, no system buffer ; involved, leave SCDRP$L_BCNT unchanged BEQL 30$ ; Branch if zero length, zero SCDRP$L_BCNT SUBPUSH R2 ; Save R2 BSBW ALLOC_POOL ; Allocate a buffer to receive response MOVL R2,R1 ; Copy buffer address SUBPOP R2 ; Restore R2 MOVL R1,SCDRP$L_SVA_USER(R5) ; Save address of allocated buffer MOVZWL (R2)+,SCDRP$L_BCNT(R5) ; Save length of transfer CLRL SCDRP$L_PAD_BCNT(R5) ; No padding required BICW3 #^C<^X1FF>,R1,- ; And byte offset within page SCDRP$W_BOFF(R5) ; INSV (R2),#IRP$V_FUNC,#1,- ; Set/clear FUNC bit to indicate READ/ SCDRP$W_STS(R5) ; WRITE function PUSHL R3 ; Save R3 MOVL SCDRP$L_SVA_USER(R5),R2 ; Get user buffer address JSB G^MMG$SVAPTECHK ; Get SVAPTE of allocated system buffer MOVL R3,SCDRP$L_SVAPTE(R5) ; Save SVAPTE in SCDRP POPL R3 ; Restore R3 BISB #SCDRP$M_S0BUF!- ; This buffer is an S0 "user" buffer SCDRP$M_BUFFER_MAPPED,-; and it has been mapped SCDRP$L_SCSI_FLAGS(R5) ; SPI$MAP_BUFFER ; Map the "user" buffer for read access 50$: MOVZWL #SS$_NORMAL,R0 ; Set success status 52$: SUBRETURN 30$: CLRL SCDRP$L_BCNT(R5) ; No data being transferred BRB 50$ ; Use common exit 300$: MOVZWL #SS$_IVBUFLEN,R0 ; Bad byte count BRB 52$ .DISABLE LSB ; SETUP_CMD .SBTTL CLEANUP_CMD - Common cleanup for all SCSI commands ;++ ; CLEANUP_CMD ; ; This routine performs common cleanup after the sending of a SCSI command, ; including unmapping the user buffer and deallocating the command buffer. ; ; INPUTS: ; ; R4 - PDT address ; R5 - SCDRP address ; ; OUTPUTS: ; ; R2 - Destroyed ; All other registers preserved ;-- CLEANUP_CMD: .ENABLE LSB ; CLEANUP_CMD PUSHR #^M ; Save registers BBCC #SCDRP$V_BUFFER_MAPPED,-; Branch if no buffer has been mapped SCDRP$L_SCSI_FLAGS(R5),-; 10$ SPI$UNMAP_BUFFER ; Unmap the mapped buffer 10$: BBCC #SCDRP$V_S0BUF,- ; Branch if this is not an S0 "user" SCDRP$L_SCSI_FLAGS(R5),-; buffer 20$ ; MOVL SCDRP$L_SVA_USER(R5),R0 ; Get address of S0 user buffer CLRL SCDRP$L_SVA_USER(R5) ; Buffer no longer owned BSBW DEALLOC_POOL ; Deallocate the buffer 20$: MOVL SCDRP$L_CMD_BUF(R5),R0 ; Get address of command buffer SPI$DEALLOCATE_COMMAND_BUFFER ; Deallocate the command buffer 30$: POPR #^M ; Restore registers RSB .DISABLE LSB ; CLEANUP_CMD .SBTTL LOG_ERROR - Write an entry to the error log file ;++ ; LOG_ERROR ; ; This routine writes an entry to the error log file. If the device is offline, ; no error is logged. This prevents the error log file from being filled up while ; the class driver does 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 error log entries would ; be redundant. ; ; INPUTS: ; ; R5 - UCB address ; R7 - Error type ; R8 - VMS status code ; ; OUTPUTS: ; ; All registers preserved ;-- LOG_ERROR: .ENABLE LSB ; LOG_ERROR PUSHR #^M ; Save registers MOVB UCB$B_DEVTYPE(R5),R9 ; Save SCSI device type 10$: MOVB UCB$B_DEVCLASS(R5),R10 ; Save DEVCLASS field MOVL UCB$L_DDT(R5),R0 ; Get DDT address MOVW #ERR_K_COMMAND_LENGTH,- ; Length of packet containing SCSI command DDT$W_ERRORBUF(R0) ; in the DDT JSB G^ERL$DEVICERR ; Log a device error BBCC #UCB$V_ERLOGIP,- ; Clear error log in progress UCB$W_STS(R5),30$ ; MOVL UCB$L_EMB(R5),R2 ; Get address of error message buffer BEQL 30$ ; Branch if none available JSB G^ERL$RELEASEMB ; Release the error log buffer 30$: POPR #^M ; Restore registers 40$: RSB ; Return to caller .DISABLE LSB ; LOG_ERROR .SBTTL SK_REG_DUMP - Device register dump routine ;++ ; SK_REG_DUMP ; ; This routine dumps device-specific information into an error log packet. ; The format of this information is as follows: ; ; +-----------------------+ ; | Longword count | 4 bytes ; +-----------------------+ ; | Revision | 1 byte ; +-----------------------+ ; | HW revision | 4 bytes ; +-----------------------+ ; | Error Type | 1 byte ; +-----------------------+ ; | SCSI ID | 1 byte ; +-----------------------+ ; | SCSI LUN | 1 byte ; +-----------------------+ ; | SCSI SUBLUN | 1 byte ; +-----------------------+ ; | Port status | 4 bytes ; +-----------------------+ ; | SCSI CMD LENGTH | 1 bytes ; +-----------------------+ ; | SCSI CMD BYTES | Up to 12 bytes ; +-----------------------+ ; | SCSI STS | 1 byte ; +-----------------------+ ; | Error Text Count | 1 bytes ; +-----------------------+ ; | Error Text | Up to 60 bytes ; +-----------------------+ ; ; Inputs: ; ; R0 - Output buffer address ; R5 - UCB address ; ; Outputs: ; ; R1-R3 - Destroyed ; All other registers preserved ;-- SK_REG_DUMP: .ENABLE LSB ; SK_REG_DUMP MOVL #<+1>,- (R0)+ ; Length of error log packet in words MOVB #0,(R0)+ ; Save revision level CLRL (R0)+ ; Save hardware revision level MOVB R7,(R0)+ ; Save error type MOVZWL UCB$W_UNIT(R5),R1 ; Get unit number CLRL R2 ; Prepare for extended divide EDIV #100,R1,R1,R2 ; Extract SCSI bus ID from unit number MOVB R1,(R0)+ ; Save SCSI bus ID MOVL R2,R1 ; Copy LUN, SUBLUN CLRL R2 ; Prepare for extended divide EDIV #10,R1,R1,R2 ; Extract LUN and SUBLUN MOVB R1,(R0)+ ; Save LUN field MOVB R2,(R0)+ ; Save SUBLUN field MOVL R8,(R0)+ ; Save port status code MOVL UCB_L_SCDRP(R5),R1 ; Get active SCDRP address BEQL 50$ ; Branch if none active MOVL SCDRP$L_CMD_PTR(R1),R2 ; Get address of SCSI command BEQL 50$ ; Branch if none active MOVL (R2)+,R3 ; Get number of SCSI command bytes MOVB R3,(R0)+ ; Save command length 10$: MOVB (R2)+,(R0)+ ; Save a command byte SOBGTR R3,10$ ; Continue for entire SCSI command MOVL SCDRP$L_STS_PTR(R1),R2 ; Get address of status byte MOVB (R2),(R0)+ ; Save SCSI status byte MOVB (R11)+,R3 ; Get count of number of text bytes. BEQL 50$ ; If no text finished MOVB R3,(R0)+ ; Save text length 20$: MOVB (R11)+,(R0)+ ; Save a text byte in error packet SOBGTR R3,20$ ; Continue for entire text string command 50$: RSB ; Return .DISABLE LSB ; SK_REG_DUMP SK_PATCH: .BLKB 200 ; Patch space SK_END: ; Last location in driver .END