.TITLE PKVDRIVER - VAX/VMS VME TO SCSI CIPRICO Port Driver Datalink .IDENT 'X-4' .ENABLE SUPPRESSION .LIST MEB ;**************************************************************************** ;* * ;* COPYRIGHT (c) 1990 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 Executive, device drivers ; ; ABSTRACT: ; ; ; ENVIRONMENT: ; ; Kernel mode, nonpaged ; ; ; .SBTTL Include Files ;+ ; Macro library calls ;- $ADPDEF ; Adapter Control Block offsets $CRBDEF ; Channel Request Block offsets $DCDEF ; Device types $DDBDEF ; Device Data Block offsets $DEVDEF ; Device characteristics $DPTDEF ; Driver Prologue Table offsets $DYNDEF ; Dynamic data structure types $EMBDEF ; Error Message Buffer offsets $IDBDEF ; Interrupt Dispatch Block offsets $IODEF ; I/O function codes $IPLDEF ; Interrupt Priority Level usage $IRPDEF ; I/O Request Packet offsets $MSGDEF ; Operator message types $PKDEF ; SCSI Port Definitions $PKVDEF ; PKVDRIVER-specific Definitions $PRDEF ; Processor Registers $PTEDEF ; Page Table Entry bits & fields $RPBDEF ; RPBdefs $SPLCODDEF ; Spinlock Code Definitions $SSDEF ; QIO Status return codes $SCDRPDEF ; SCSI Class Driver Request Packet Definitions $SCDTDEF ; SCSI Connection Descriptor Definitions $SPDTDEF ; SCSI Port Descriptor Definitions $TQEDEF ; Timer Queue Entry block definitions $VADEF ; Virtual Address fields $VECDEF ; CRB interrupt dispatch Vector offsets $XVIBDEF ; XMI to VME adapter definitions ;++ ; ; Local Constants ; ;-- .SBTTL UCB EXTENSIONS $DEFINI UCB,GLOBAL ;+ ; Device-dependent Unit Control Block offsets for PKCDRIVER ;- . = UCB$K_COMMON_AUTO_DEF $DEF UCB$L_PARAM_BLOCK,.LONG $DEF UCB$L_VMEMD,.LONG $DEF UCB$L_VME_COMM,.LONG $DEF UCB$L_ADP,.LONG $DEF UCB$L_QUE_HEADER,.LONG $DEF UCB$L_QUE_MAPREG,.LONG $DEF UCB$L_QUE_NUMREG,.LONG $DEF UCB$L_STAT_BLOCK,.LONG $DEF UCB$L_TYPE0_NUMREG,.LONG $DEF UCB$L_TYPE0_MAPREG,.LONG $DEF UCB$L_TYPE0_VMEADDRS,.LONG $DEF UCB$L_TYPE0_VMSADDRS,.LONG $DEF UCB$L_TYPE0_PC,.LONG $DEF UCB$L_SCATTER_MAPS,.LONG $DEF UCB$L_SCATTER_VMEADDRS,.LONG $DEF UCB$L_SCATTER_MAPNUM,.LONG $DEF UCB$L_SCATTER_MAPREG,.LONG ;+ ; The following scratchin and scratchout pointer are for supporting the ; pad_bcnt function for disk drives since they can only read and write ; full blocks, there must be some way to transfer partial blocks. ;- $DEF UCB$L_SCRATCH_IN,.LONG $DEF UCB$L_SCRATCH_OUT,.LONG $DEF UCB$L_SCRATCH_INVME,.LONG $DEF UCB$L_SCRATCH_OUTVME,.LONG $DEF UCB$L_SCR_MAPREG,.LONG $DEF UCB$L_SCR_NUMREG,.LONG $DEF UCB$L_FLAGS,.LONG $VIELD PKV,0,<- ; UCB device specific bit definitions ,- ; A TYPE-0 param blk has been passed. ,- ; Use the UCB fork block on in ISR > UCB$K_PKV_LENGTH = . $DEFEND UCB .SBTTL Driver Tables ;+ ; Driver Prologue Table ;- DPTAB - ; Define Driver Prologue Table. END = PKV_END,- ; End of driver. ADAPTER = VME,- ; XMI to VME adapter. UCBSIZE = UCB$K_PKV_LENGTH,- ; Size of a UCB. MAXUNITS = 1,- ; One port supported today. NAME = PKVDRIVER,- ; Driver name. FLAGS = ; Snapshots allowed. DPT_STORE INIT ; Control block init value. DPT_STORE UCB,UCB$B_FLCK,B,SPL$C_IOLOCK8 ; Fork lock. DPT_STORE UCB,UCB$B_DIPL,B,- ; Device IPL. PKV$K_DEVICEIPL DPT_STORE UCB,UCB$L_DEVCHAR,L,- ; Device characteristics: ; Output device DPT_STORE UCB,UCB$L_DEVCHAR2,L,- ; Device characteristics: ; Prefix name with "node$" DPT_STORE UCB,UCB$B_DEVCLASS,B,DC$_BUS ; Device class DPT_STORE UCB,UCB$B_DEVTYPE,B,DT$_XVIB ; Device type DPT_STORE UCB,UCB$W_DEVSTS,W,- ; Device Permanent Status: ; No conversion of logical to phys DPT_STORE REINIT ; Control block re-init values. DPT_STORE CRB,CRB$L_INTD+VEC$L_ISR,D,- ; ISR address. PKV$INTERRUPT ; DPT_STORE CRB,CRB$L_INTD+VEC$L_UNITINIT,-; Unit init. D,PKV$UNIT_INIT ; DPT_STORE CRB,CRB$L_INTD+VEC$L_INITIAL,- ; Controller init. D,PKV$CNTRL_INIT ; DPT_STORE DDB,DDB$L_DDT,D,PKV$DDT ; DDT address. DPT_STORE END ; ;+ ; Driver Dispatch Table ;- DDTAB - ; Driver Dispatch Table. DEVNAM = PKV,- ; Generic name. START = PKV_STARTIO,- ; Start I/O operation. FUNCTB = PKV_FUNCTABLE,- ; Function Decision Table. CANCEL = PKV_CANCELIO,- ; Cancel I/O entry point. REGDMP = PKV_REGDUMP,- ; Register Dump routine. DIAGBF = 0,- ; Diagnostic Buffer size. ERLGBF = <4+EMB$L_DV_REGSAV+- ; Error log buffer size PKV$K_ERROR_LEN> ;+ ; Function Decision Table ;- PKV_FUNCTABLE: ; Function Decision Table. FUNCTAB ,- ; Valid I/O functions. ; FUNCTAB ,- ; Buffered I/O functions. ; FUNCTAB PKV$AUTO_FDT,- ; FDT for AUTOCONFIGURE. ; FUNCTAB +EXE$READ,- ; FDT for AUTOCONFIGURE. ; FUNCTAB +EXE$ZEROPARM,- ; Zero parameter functions. ; BPT_FLAG: .LONG 0 .MACRO TEST_IPL IPL,?L1 MFPR #PR$_IPL,-(SP) ; Get current IPL CMPL (SP)+,IPL ; Are we at right IPL? BEQL L1 ; Br if yes JSB G^INI$BRK ; Hit BPT L1: .ENDM .SBTTL Initialization Routines .SBTTL + PKV$CNTRL_INIT - Controller Initialization Routine ;++ ; This routine is called when the driver is loaded or on powerfail recovery. ; For this port this routine will simply RSB for now. ; ; INPUTS: ; ; IPL$_POWER ; R3 = Pointer to our "CSR" ; R4 = Same as R3 ; R5 = Address of our IDB ; R8 = Address of our UCB ; ; OUTPUT: ; ; For PKVDRIVER, this routine simply returns. ; ;-- .ENABLE LSB PKV$CNTRL_INIT: RSB ; Return to SYSGEN. .DISABLE LSB .SBTTL + PKV$UNIT_INIT - Unit Initialization Routine ;++ ; This routine is called when the driver is loaded or on powerfail recovery. ; ; INPUTS: ; ; IPL$_POWER ; R3 = Pointer to our "CSR" ; R4 = Same as R3 ; R5 = Address of our UCB ; ; OUTPUTS: ; ; R4 - R11 are preserved. ; Other items mentioned above. ; ;-- .ENABLE LSB ; PKV$UNIT_INIT PKV$UNIT_INIT: ;+ ; To allow the port driver to be reloaded, copy the UCB address of ANY ; unit into the UCBLST in the IDB. ;- BBS #UCB$V_POWER,- ; Skip IDB change after UCB$W_STS(R5),100$ ; a powerfailure. MOVL UCB$L_CRB(R5),R0 ; Get CRB address. MOVL CRB$L_INTD+VEC$L_IDB(R0),R0 ; Get IDB address into R0. MOVL R5,IDB$L_UCBLST(R0) ; Allow reloading of port. ;+ ; The creation of a port must occur at FIPL holding IOLOCK8 (FORK) lock. ; This is achieved by forking before invoking the CREATE_PORT macro. ;- 100$: BBSSI #UCB$V_INILOCK,- ; Check to see that this FKB UCB$L_FKBLKFLAG(R5),111$ ; is not in use. MOVB #SPL$C_IOLOCK8,- ; Set correct spinlock (R5) MOVQ R4,-(SP) ; Save R4 and R5 PUSHAB 110$ ; Return from EXE$FORK PUSHAB 120$ ; Fork PC MOVAB UCB$L_INIFKBLK(R5),R5 ; Fork on alternate FKB JMP G^EXE$FORK ; Queue the fork block. 110$: MOVZBL #SS$_NORMAL,R0 ; Success to sysgen MOVQ (SP)+,R4 ; Restore R4 and R5 111$: RSB ; and return 120$: MOVAB -UCB$L_INIFKBLK(R5),R5 ; R5 to start of UCB. BBCCI #UCB$V_INILOCK,- ; This FKB is no longer busy. UCB$L_FKBLKFLAG(R5),130$ ; Just clear and proceed. 130$: BBC #UCB$V_POWER,- ; Skip setup of DMA buffers and UCB$W_STS(R5),135$ ; CREATE_PORT if powerfail. MOVL UCB$L_PDT(R5),R4 ; GET SPDT ADDRESS IN R4. BRW 135$ ; Powerfail! ;+ ; Create and initialize the SCSI Port Descriptor Table (SPDT). ;- 135$: MOVZWL #SPDT$C_PKNLENGTH,R1 ; Set length (use PKNLENGTH). CREATE_PORT ; Link up a PDT. PUSHL R4 ; Save CSR address. MOVL UCB$L_PDT(R5),R4 ; GET SPDT ADDRESS IN R4. POPL R0 ; Restore CSR address to R0 MOVL R0,SPDT$L_PORT_CSR(R4) ; Save Port HW's CSR in SPDT. CLRL SPDT$L_BUS_HUNG_CNT(R4) ; Save for later use MOVW #SPDT$C_PKC,- ; SPDT$W_SPDT_TYPE(R4) ; Setup Port Type MOVW #SPDT$C_PKCLENGTH,- ; SPDT$W_SIZE(R4) ; Setup Size. MOVL #!- ; SPDT version number. >,- ; SCDT version number. SPDT$L_VERSION_CHECK(R4) ; Save data structure version in SPDT. MOVL #,- ; Port supports LUNS SPDT$L_PORT_FLAGS(R4) ; 138$: MOVL UCB$L_DDB(R5),R0 ; Get DDB address SUBB3 #^A'A',DDB$T_NAME+3(R0),- ; Translate controller letter to SPDT$L_SCSI_PORT_ID(R4) ; SCSI port ID and save it in SPDT. ;+ ; Limit transfers to a reasonable limit, as of now the limit is y 65,535 bytes in ; a single transfer. This limit is pulled out of the air the XMI to VME adapter ; can support transfers upto 32 MB with its 64K of mapping registers. ;- MOVL #PKV$K_MAXBCNT,- ; Setup maximum segment size. SPDT$L_MAXBYTECNT(R4) MOVB #1,SPDT$L_PORT_FLAGS+3(R4) ; fill in divisor for ;+ ; Allocate 2 scratch pages to take care of pad counts page transfers. We allocate a scratch ; in page and a scratch out page. The scratch out page has to be inited to zero, while the ; in page will be over written with various transfers ;- MOVZBL #2,R1 ; Number of pages to allocate JSB G^EXE$ALOPHYCNTG ; Go allocate our pages BLBS R0,5$ BRW 200$ ; Branch on failure 5$: MOVL R2,UCB$L_SCRATCH_IN(R5) ; Save for later MOVAB 512(R2),UCB$L_SCRATCH_OUT(R5) ; Save for later PUSHR #^M MOVC5 #0,@UCB$L_SCRATCH_OUT(R5),#0,- ; Init the the scatter gather maps #512,@UCB$L_SCRATCH_OUT(R5) POPR #^M ;+ ; Now we must map the scratch pages into VME address space. These will stayed ; mapped at all times and more than one device can be coping to these areas ; at the same time. ;- MOVL #1024,R3 ; Always 2 pages CLRL R1 ; Always page aligned(no BOFF) MOVL #3,R3 ; Always 3 map registers MOVL UCB$L_CRB(R5),R2 ; ON our way to get the ADP MOVL CRB$L_INTD+VEC$L_ADP(R2),R2 ; Get the ADP out of the CRB interrupt vector MOVL R2,UCB$L_ADP(R5) ; Save the ADP for later MOVAB UCB$L_VMEMD(R5),R1 ; allocated map reg descriptor BSBW IOC$ALOVMEMAP_DMAN ; Allocate the map registers BLBS R0,6$ ; Branch on error, can't init SCSI adapter BRW 200$ 6$: MOVZWL VME_MD$W_NUMREG(R1),- ; Save number allocated UCB$L_SCR_NUMREG(R5) MOVZWL VME_MD$W_MAPREG(R1),- ; Starting map registers allocated UCB$L_SCR_MAPREG(R5) ; This will determine VME address mapped to PUSHR #^M MOVL UCB$L_SCRATCH_IN(R5),R3 ; SVA to start the map reg loading EXTZV S^#VA$V_SVPN,- ; Get buffer virtual page number S^#VA$S_SVPN,R3,R3 MOVL G^MMG$GL_SPTBASE,R0 ; Get system page table address MOVAL (R0)[R3],R3 ; get the SVAPTE MOVL #1024,R4 ; Byte count 2 pages CLRL R5 ; Page aligned no BOFF MOVL #XVIB$M_RMWMODE,R0 BSBW IOC$LOADVMEMAP_DMAN ; Map the VME POPR #^M ;+ ; Now we can compute the VME address from the Map register number and the BOFF, ; we save this in the UCB for later use. ;- MULL3 #512,UCB$L_SCR_MAPREG(R5),- ; Get the VME base address for the param UCB$L_SCRATCH_INVME(R5) ADDL3 #512,UCB$L_SCRATCH_INVME(R5),- ; The input scratch page right after output UCB$L_SCRATCH_OUTVME(R5) ;+ ; Allocate command packets for SCSI adapter. The max size allowed for command ; blocks is 64K. This means there are 2340(64K/28) parameter blocks. The one ; additional page is the queue header for the parameter blocks as described ; in the adapter spec. ;- MOVL #<+ - + - CMD_QUE$S_CMD_QUEUE>,R1 JSB G^EXE$ALONONPAGED ; Get command packets BLBS R0,10$ ; FAILURE, don't set online BRW 200$ 10$: MOVL R2,UCB$L_QUE_HEADER(R5) ; Adpater queue header ADDL3 #CMD_QUE$S_CMD_QUEUE,R2,- ; Point passed header to param blocks UCB$L_PARAM_BLOCK(R5) ADDL3 #+ - CMD_QUE$S_CMD_QUEUE,R2,- ; UCB$L_STAT_BLOCK(R5) ; Save start of status blocks PUSHR #^M PUSHL R5 MOVC5 #0,@UCB$L_PARAM_BLOCK(R5),#0,- ; Init the param blocks #,- @UCB$L_PARAM_BLOCK(R5) MOVL (SP),R5 MOVC5 #0,@UCB$L_QUE_HEADER(R5),#0,- ; Init the queue header #CMD_QUE$S_CMD_QUEUE,- @UCB$L_QUE_HEADER(R5) ;+ ; Allocate the max size allowed for status blocks 64K. The same queue header that ; describes the parameter blocks is also used for the status blocks. ;- POPL R5 MOVC5 #0,@UCB$L_STAT_BLOCK(R5),#0,- ; Init the status blocks #,- @UCB$L_STAT_BLOCK(R5) POPR #^M ;+ ; Set up the queue header for both the parameter blocks and status blocks. ;- MOVL UCB$L_QUE_HEADER(R5),R2 ; Get the header address MOVL #PKV$K_PARAMBLK_COUNT,- ; Load total number of param blocks CMD_QUE$L_NUM_PARMBLKS(R2) MOVL #PKV$K_STATBLK_COUNT,- ; Load total number of status blocks CMD_QUE$L_NUM_STATBLKS(R2) CLRL CMD_QUE$L_PARAM_IN(R2) ; First free parma block is number zero CLRL CMD_QUE$L_PARAM_OUT(R2) ; Mark queue as empty (no blocks to pass) CLRL CMD_QUE$L_STATUS_IN(R2) ; Mark queue as empty (no blocks to pass) CLRL CMD_QUE$L_STATUS_OUT(R2) ; First free status block is number zero ;+ ; Now map the queue header, param blocks, and status blocks into VME address space ;- MOVL #<+ - + - CMD_QUE$S_CMD_QUEUE>,R3 BICL3 #^C<^X1FF>,- ; Get byte offset within the first page UCB$L_QUE_HEADER(R5),R1 MOVAB ^X3FF(R3)[R1],R3 ; Calculate highest relative byte and round ASHL #-9,R3,R3 ; Calculate number of map registers required MOVL UCB$L_CRB(R5),R2 ; ON our way to get the ADP MOVL CRB$L_INTD+VEC$L_ADP(R2),R2 ; Get the ADP out of the CRB interrupt vector MOVL R2,UCB$L_ADP(R5) ; Save the ADP for later MOVAB UCB$L_VMEMD(R5),R1 ; allocated map reg descriptor BSBW IOC$ALOVMEMAP_DMAN ; Allocate the map registers BLBS R0,30$ ; Branch on error, can't init SCSI adapter BRW 200$ 30$: MOVZWL VME_MD$W_NUMREG(R1),- ; Save number allocated UCB$L_QUE_NUMREG(R5) MOVZWL VME_MD$W_MAPREG(R1),- ; Starting map registers allocated UCB$L_QUE_MAPREG(R5) ; This will determine VME address mapped to PUSHR #^M MOVL #<+ - + - CMD_QUE$S_CMD_QUEUE>,R4 MOVL UCB$L_QUE_HEADER(R5),R3 ; SVA to start the map reg loading BICL3 #^C<^X1FF>,R3,R5 ; Get the BOFF EXTZV S^#VA$V_SVPN,- ; Get buffer virtual page number S^#VA$S_SVPN,R3,R3 MOVL G^MMG$GL_SPTBASE,R0 ; Get system page table address MOVAL (R0)[R3],R3 ; get the SVAPTE MOVL #XVIB$M_RMWMODE,R0 BSBW IOC$LOADVMEMAP_DMAN ; Map the VME POPR #^M ;+ ; Now we can compute the VME address from the Map register number and the BOFF, ; we save this in the UCB for later use. ;- MULL3 #512,UCB$L_QUE_MAPREG(R5),R0 ; Get the VME base address for the param BICL3 #^C<^X1FF>,- ; Get offset with the first page UCB$L_QUE_HEADER(R5),R1 ADDL3 R0,R1,UCB$L_VME_COMM(R5) ; Add start offset + BOFF = VME comm base ;+ ; Allocate the same number of scatter gather maps as we have param blocks, ; because at most we will need a one one matchup of maps to param blocks ;- MOVL #,R1 JSB G^EXE$ALONONPAGED ; Get command packets BLBS R0,35$ ; FAILURE, don't set online BRW 200$ 35$: MOVL R2,UCB$L_SCATTER_MAPS(R5) ; Save scatter gather SVA PUSHR #^M MOVC5 #0,@UCB$L_SCATTER_MAPS(R5),#0,- ; Init the the scatter gather maps #,- @UCB$L_SCATTER_MAPS(R5) POPR #^M ;+ ; Now map our scatter gather maps on to the VME ;- MOVL #,R3 BICL3 #^C<^X1FF>,- ; Get byte offset within the first page UCB$L_SCATTER_MAPS(R5),R1 MOVAB ^X3FF(R3)[R1],R3 ; Calculate highest relative byte and round ASHL #-9,R3,R3 ; Calculate number of map registers required MOVL UCB$L_ADP(R5),R2 ; Get the ADP MOVAB UCB$L_VMEMD(R5),R1 ; Allocated map reg descriptor BSBW IOC$ALOVMEMAP_DMAN ; Allocate the map registers BLBS R0,37$ ; Branch on error, can't init SCSI adapter BRW 200$ 37$: MOVZWL VME_MD$W_NUMREG(R1),- ; Save number allocated UCB$L_SCATTER_MAPNUM(R5) MOVZWL VME_MD$W_MAPREG(R1),- ; Starting map registers allocated UCB$L_SCATTER_MAPREG(R5) ; This will determine VME address mapped to PUSHR #^M MOVL UCB$L_SCATTER_MAPS(R5),R3 ; SVA to start the map reg loading BICL3 #^C<^X1FF>,R3,R5 ; Get the BOFF EXTZV S^#VA$V_SVPN,- ; Get buffer virtual page number S^#VA$S_SVPN,R3,R3 MOVL G^MMG$GL_SPTBASE,R4 ; Get system page table address MOVAL (R4)[R3],R3 ; get the SVAPTE MOVL #XVIB$M_RMWMODE,R0 ; Don't do RMW access and no byte swapping MOVL #,R4 BSBW IOC$LOADVMEMAP_DMAN ; Map the VME POPR #^M ;+ ; Now compute the VME address we just mapped the scatter gather maps to. ;- MULL3 #512,- ; Get the VME base address for the param UCB$L_SCATTER_MAPREG(R5),R0 BICL3 #^C<^X1FF>,- ; Get offset with the first page UCB$L_SCATTER_MAPS(R5),R1 ADDL3 R0,R1,UCB$L_SCATTER_VMEADDRS(R5); Add start offset + BOFF = VME comm base ;+ ; Now we must send a TYPE-0 parameter block to the SCSI adapter to set it into the proper ; mode of operation. ;- 40$: MOVL #,R1 ; Size in bytes of a type-0 param block JSB G^EXE$ALONONPAGED ; Get command packets BLBS R0,50$ ; Error can't init SCSI adapter BRW 200$ 50$: PUSHR #^M MOVC5 #0,(R2),#0,- ; zero the block #,(R2) POPR #^M ;+ ; We have to map the TYPE-0 block into VME memory ;- MOVL R2,UCB$L_TYPE0_VMSADDRS(R5) ; Save system virt for later MOVL #,R3 BICL3 #^C<^X1FF>,R2,R1 ; Get byte offset within the first page MOVAB ^X3FF(R3)[R1],R3 ; Calculate highest relative byte and round ASHL #-9,R3,R3 ; Calculate number of map registers required MOVL UCB$L_ADP(R5),R2 MOVAB UCB$L_VMEMD(R5),R1 ; allocated map reg descriptor BSBW IOC$ALOVMEMAP_DMAN ; Allocate the map registers BLBS R0,60$ ; Branch on error, can't init SCSI adapter BRW 200$ 60$: MOVZWL VME_MD$W_NUMREG(R1),- ; Save number allocated UCB$L_TYPE0_NUMREG(R5) MOVZWL VME_MD$W_MAPREG(R1),- ; Starting map registers allocated UCB$L_TYPE0_MAPREG(R5) ; This will determine VME address mapped to PUSHR #^M MOVL UCB$L_TYPE0_VMSADDRS(R5),R3 ; SVA to start the map reg loading BICL3 #^C<^X1FF>,R3,R5 ; Get the BOFF EXTZV S^#VA$V_SVPN,- ; Get buffer virtual page number S^#VA$S_SVPN,R3,R3 MOVL G^MMG$GL_SPTBASE,R4 ; Get system page table address MOVAL (R4)[R3],R3 ; get the SVAPTE MOVL #XVIB$M_RMWMODE,R0 ; No RMW and NO byte swapping MOVL #,R4 BSBW IOC$LOADVMEMAP_DMAN ; Map to the VME POPR #^M ;+ ; Now compute the VME address we just mapped the type0 param block to and save it ;- MULL3 #512,UCB$L_TYPE0_MAPREG(R5),R0 ; Get the VME base address for the param BICL3 #^C<^X1FF>,- ; Get offset with the first page UCB$L_TYPE0_VMSADDRS(R5),R1 ADDL3 R0,R1,- ; Add start offset + BOFF = VME comm base UCB$L_TYPE0_VMEADDRS(R5) ;+ ; Now setup the command queues ;- MOVL UCB$L_TYPE0_VMSADDRS(R5),R2 ; Restore the TYPE0 block addrs MOVL #^XDEAD1234,- ; Id this block for later PARAMBLK$L_COMD_ID(R2) MOVB #^X0B,PARAMBLK$B_ADDR_MOD(R2) ; Extended non-priv data access MOVL UCB$L_VME_COMM(R5),- ; Address of the command lists on the VME PARAMBLK$L_VME_ADDRS(R2) MOVL UCB$L_CRB(R5),R1 ; Getting IDB MOVL CRB$L_INTD+VEC$L_IDB(R1),R1 ; Got the IDB MOVZBL IDB$B_VECTOR(R1),R1 ; Get the vector address. (from sysgen) ASHL #2,R1,R1 ; Turn into a byte offset from a longword BISL #^X400,R1 ; Request level 4 with vector from sysgen MOVW R1,PARAMBLK$L_TRANS_COUNT(R2) ; Interrupt level and vector for cmd ints MOVL #^X01000000,- ; Command of START COMMAND LIST PARAMBLK$L_TRANS_COUNT+4(R2) MOVAB 95$,UCB$L_TYPE0_PC(R5) BRW PK$SEND_TYPE0 95$: BBS #PKV$V_ERROR,R0,200$ ; Branch if error BBC #PKV$V_READY,R0,200$ ; Branch if not ready ;+ ; Now we need to send a START COMMAND LIST and SETUP type0 param blocks to ; the adatper. The first command will be a setup command. ;- MOVL UCB$L_TYPE0_VMSADDRS(R5),R2 ; Restore the TYPE0 block addrs PUSHR #^M MOVC5 #0,(R2),#0,- ; zero the block #,(R2) POPR #^M MOVL #^XDEAD4321,- ; Id this block for later PARAMBLK$L_COMD_ID(R2) MOVB #^X07,PARAMBLK$B_ADDR_MOD(R2) ; This will be our SCSI ID MOVB #^X90,PARAMBLK$B_FLAGS(R2) ; Synch count of 16 specified in bytes(80) MOVB #^X03,- PARAMBLK$B_FLAGS_RESVED(R2) ; block mode by addrs mode, allow disconnects MOVL #^X07000000,- ; Command of SET CONTROLLER OPTIONS PARAMBLK$L_TRANS_COUNT+4(R2) MOVAB 80$,UCB$L_TYPE0_PC(R5) BRW PK$SEND_TYPE0 80$: BBS #PKV$V_ERROR,R0,200$ ; Branch if error BBC #PKV$V_READY,R0,200$ ; Branch if not ready ;+ ; Set this SCSI port online ;- BISW #UCB$M_ONLINE,- ; Set port UCB ONLINE. UCB$W_STS(R5) ; MOVL UCB$L_PDT(R5),R4 ; GET SPDT ADDRESS IN R4. BISL #SPDT$M_ONLINE,- ; Set port SPDT ONLINE. SPDT$L_STS(R4) ; BBS #UCB$V_POWER,- ; Is this a powerfail? UCB$W_STS(R5),190$ ; If yes, skip further init. ;+ ; Initialize the PORT_RING which saves the PCs of the last n requestors and ; releasers of the PORT channel. ;- MOVAL SPDT$L_PORT_RING(R4),- SPDT$L_PORT_RING_PTR(R4) PUT <<13><10>"%%PKV, Port driver loaded, SPDT created and port inited."> BSBW SS$SETUP_CONNECTION_TIMEOUT_TQE 190$: RSB ;+ ; The port cannot be used since it failed part of its initialization sequence. ; By setting the FAILED bit and not setting the port ONLINE bit we will prevent ; connections from being established using this port. ;- 200$: BISL #SPDT$M_FAILED,- ; Don't set port SPDT ONLINE, but do SPDT$L_STS(R4) ; remember that it failed initialization. RSB ; Return from unit init. .DISABLE LSB ; PKV$UNIT_INIT .SBTTL + PK$SEND_TYPE0 ;++ ; PK$SEND_TYPE0: ; ; This routine is called to send a type0 command to the adapter. ; ; ; INPUTS: ; R4 - SPDT ; R5 - UCB ; ; OUTPUT: ; .ENABLE LSB ;+ ; Now setup the TYPE0 block for our first command to the adapter ;- PK$SEND_TYPE0: MOVL UCB$L_TYPE0_VMSADDRS(R5),R2 ; Restore the TYPE0 block addrs MOVB #^XFF,PARAMBLK$B_TARGET_ID(R2) ; Target is the adapter its self MOVL UCB$L_CRB(R5),R1 ; Getting IDB MOVL CRB$L_INTD+VEC$L_IDB(R1),R1 ; Got the IDB MOVZBL IDB$B_VECTOR(R1),R1 ; Get the vector address. (from sysgen) ASHL #2,R1,R1 ; Turn into a byte offset from a longword BISL #^X400,R1 ; Request level 4 with vector from sysgen MOVW R1,TYPE0BLK$W_INTERRUPT(R2) ; Interrupt for TYPE0 MOVZBL #^X0B,R0 ; Addrs modifier extended; non-priv; data ASHL #8,R0,R0 ; Shift to high byte MOVB #^X87,R0 ; do longword accesses with word&byte swap MOVL SPDT$L_PORT_CSR(R4),R4 ; Get port CSR DEVICELOCK - LOCKADDR=UCB$L_DLCK(R5),- ; Lock device access SAVIPL=-(SP),- ; Save current IPL PRESERVE=YES ; DON'T Preserve R0 WRITE_CSR R0,PKV$B_APORT_AMPB(R4),VME=#0,- LENGTH=WORD,ENVIRON=SPECIFIC ;+ ; Before we write the address of the command list we must change the little endian vax ; to a big endian VME address. ;- MOVW UCB$L_TYPE0_VMEADDRS+2(R5),R0 ; Get the MSW SWAPWORD R0 ; Swap the MSW WRITE_CSR R0,PKV$W_APORT_MSW(R4),VME=#0,- LENGTH=WORD,ENVIRON=SPECIFIC MOVW UCB$L_TYPE0_VMEADDRS(R5),R0 ; Get the LSW SWAPWORD R0 ; Swap the LSW WRITE_CSR R0,PKV$W_APORT_LSW(R4),VME=#0,- LENGTH=WORD,ENVIRON=SPECIFIC BBSSI #PKV$V_TYPE0,UCB$L_FLAGS(R5),0$ ; ISR will be ready for a type-0 0$: CLRL R0 WRITE_CSR R0,PKV$W_CHAN_ATTN(R4),VME=#0,- LENGTH=WORD,ENVIRON=SPECIFIC ;+ ; Now we wait for an interrupt to get back status on out init command ;- WFIKPCH 20$,#5 ; Should init in 5 seconds IOFORK ; Lets get back to a fork thread READ_CSR PKV$W_STATUS_PORT(R4),R1,VME=#0,- LENGTH=WORD,ENVIRON=SPECIFIC MOVL R1,R0 ; MOve status to R0 SWAPWORD R0 ; Back to little endian 10$: MOVL UCB$L_PDT(R5),R4 ; Get SPDT address. JMP @UCB$L_TYPE0_PC(R5) ; Return to caller 20$: CLRL R0 BRB 10$ .DISABLE LSB .SBTTL + PK$SET_CONNECTION_CHAR ;++ ; PK$SET_CONNECTION_CHAR: ; ; This routine is called to perform any port-specific sections of the ; SET_CONNECTION_CHAR routine. We will send a command to the CIRPICO ; controller to let it know how to handle this device. ; ; ; INPUTS: ; ; R3 = Address of SCDT. ; R4 = address of the SPDT ; ; OUTPUT: ; ; R0,R1 destroyed ; ;-- PK$SET_CONNECTION_CHAR:: ; ; Set flag to let send command path know this device has had its characteristics changed ; The command to change them will be sent before any more SCSI commands are sent to the ; device. ; BISL #^X80000000,- ; Request connecion char change SPDT$L_BUS_HUNG_CNT(R4) RSB PKV$SETUP_DEVICE: PUSHL R5 ; Save SCDRP MOVL SPDT$L_PORT_UCB(R4),R5 ; Now we have the port UCB MOVL UCB$L_QUE_HEADER(R5),R1 ; Parmeter block que header MOVL CMD_QUE$L_PARAM_IN(R1),R0 ; Next free parameter block MULL2 #PARAMBLK$S_PARAM_BLOCK,R0 ; Byte offset to our param block MOVL UCB$L_PARAM_BLOCK(R5),R1 ; Start of param block in the que ADDL R0,R1 ; Address of our param block PUSHR #^M ; Zero out param blk MOVC5 #0,(R1),#0,- #PARAMBLK$S_PARAM_BLOCK,(R1) POPR #^M FFS #0,#8,SCDT$L_SCSI_BUS_ID(R3),- ; Load Target ID into param blk PARAMBLK$B_ADDR_MOD(R1) MOVB #^XFF,PARAMBLK$B_TARGET_ID(R1) ; Send command to adapter MOVW SCDT$L_DISCON_TIMEOUT(R3),- ; Set disconnect timeout PARAMBLK$B_FLAGS(R1) MOVB SCDT$W_CMD_RETRY_CNT(R3),- ; Retry count limit PARAMBLK$L_VME_ADDRS(R1) MOVB #^X1C,PARAMBLK$L_VME_ADDRS+1(R1) ; Retry, bus err, cmd err, parity err MOVW SCDT$L_DISCON_TIMEOUT(R3),- ; SELECT timeout PARAMBLK$L_VME_ADDRS+2(R1) MOVB #^X02,PARAMBLK$L_TRANS_COUNT(R1) ; Sync and disconnects MOVL #^X08,PARAMBLK$B_SCSI_CMD+3(R1) ; Setup unit cmd POPL R0 ; Get SCDRP back MOVL R0,PARAMBLK$L_COMD_ID(R1) ; Save SCDRP CLRL SCDRP$L_SAVE_DATA_PTR(R0) ; Init to no status packet yet MOVAB 10$,SCDRP$L_FPC(R0) MOVQ R3,SCDRP$L_FR3(R0) ; ; Now that we have the command block ready we can bump queue header to indicate ; there is another parameter block ready to process. ; MOVL SPDT$L_PORT_UCB(R4),R1 ; Get the port UCB address MOVL UCB$L_QUE_HEADER(R1),R1 ; Get pointer to queue header MOVL CMD_QUE$L_PARAM_IN(R1),R0 ; Next free parameter block INCL R0 ; Point to next param block in the queue BICL #^C,R0 ; Modulo the queue size MOVL R0,CMD_QUE$L_PARAM_IN(R1) ; Point to next entry MOVZBL #1,R1 ; 1 = param block ready in cmd queue SWAPWORD R1 ; Turn cmd into big endian MOVL SPDT$L_PORT_CSR(R4),R2 ; Get port CSR DEVICELOCK - LOCKADDR=UCB$L_DLCK(R5),- ; Lock device access SAVIPL=-(SP),- ; Save current IPL PRESERVE=NO ; Preserve R0 WRITE_CSR R1,PKV$W_CHAN_ATTN(R2),VME=#0,- LENGTH=WORD,ENVIRON=SPECIFIC WFIKPCH 20$,#5 ; Should happen in 5 seconds 10$: MOVL SPDT$L_PORT_UCB(R4),R0 ; Now we have the port UCB BICL #UCB$M_TIM,UCB$L_STS(R0) ; Tell timoue search we got int FORK ; Lets get back to a fork thread ;+ ; Get the status out of the status block. The status block addrs is returned in the SCDRP ;- MOVL SCDRP$L_SAVE_DATA_PTR(R5),R0 ; Get the address of the status block BITB #^C,- ; Successfull command complete STATUSBLK$B_FLAGS(R0) BNEQ 20$ ; Branch if not BRB PKV$NO_CHARREQ ; Join main send command flow ; ; Return error to the class driver, we can no longer talk to scsi controller ; SS$_CTRLERR - Port failed and there should be not retry. ; 20$: LOG_ERROR,TYPE=CTL_ERR jsb g^ini$brk MOVZWL #SS$_CTRLERR,R0 SUBRETURN .SBTTL + PK$SEND_CMD - SCSI Port-specific Send routine ;++ ; PK$SEND_CMD: ; ; This routine is called by port drivers to send a command to a SCSI device. ; This is a port-specific routine. PK$SEND_CMD interacts with the NCR 53C94 ; hardware to send SCSI CMD packets and responds to subsequent bus phase ; changes. The NCR 53C94 also arbitrates for the SCSI bus, and selects the ; target device prior to sending the Command Descriptor Block (CDB). ; This routine returns with command status in the STATUSIN buffer and port ; status in R0. ; ; INPUTS: ; ; R2 = Address of VME SCSI CSRs. ; R3 = SCDT address. ; R4 = Port SPDT address. ; R5 = Class SCDRP address. ; ; SCDRP$L_CMD_PTR - Command out buffer. The first longword ; contains the number of command bytes. ; SCDRP$L_BCNT - Number of bytes of user data. ; SCDRP$L_STS_PTR - Address of the buffer to be used for status ; bytes from the target. ; SCDRP$L_CDT - Connection ID of connection over which to ; send message ; ; OUTPUTS: ; ; R0 = Port status: SS$_NORMAL - Success. ; SS$_MEDOFL - Port failed and retry should be atempted. ; SS$_CTRLERR - Port failed and there should be not retry. ; SS$_NOSUCHID - Invalid SCSI ID in SCDT. ; ; R3,R4,R5 are Preserved. ; ;-- .ENABLE LSB PK$SEND_CMD:: SUBSAVE ;+ ; If device chars have been modified send a SET UNIT CHAR command to controller ; before we send this command. ;- BBCC #31,SPDT$L_BUS_HUNG_CNT(R4),- ; See if setup request has been made PKV$NO_CHARREQ BRW PKV$SETUP_DEVICE ;@@@ ;+ ; Set up the starting values for the retry counter and message pointers ; for this SCDRP. These values are pulled from the SCDT, where they are ; set up during the 'get connection characteristics' sequence. ;- PKV$NO_CHARREQ: CLRL SCDRP$L_SAVE_DATA_PTR(R5) ; Init to no status packet yet MOVAB SCDRP$B_SCSIMSGI_BUF(R5),- ; Setup pointer to message SCDRP$L_SCSIMSGI_PTR(R5) ; buffers. MOVAB SCDRP$B_SCSIMSGO_BUF(R5),- ; Setup pointer to message SCDRP$L_SCSIMSGO_PTR(R5) ; buffers. ;+ ; Get a command packet off the free queue, and load the SCSI command transfer ; size. The SCDRP is put in the serial number portion of the packet so we ; can figure out how it belongs to after the command completes. ;- BSBW PKV$_SETUP_COMMAND ; Go setup command in param blk MOVL R5,PARAMBLK$L_COMD_ID(R1) ; For packet serial # use SCDRP FFS #0,#8,SCDT$L_SCSI_BUS_ID(R3),- ; Load Target ID into param blk PARAMBLK$B_TARGET_ID(R1) MOVB #^X0B,PARAMBLK$B_ADDR_MOD(R1) ; No priv extended BLOCK mode MOVB #^X48,PARAMBLK$B_FLAGS(R1) ; flags MOVL SCDRP$L_BCNT(R5),- ; Byte count to transfer PARAMBLK$L_TRANS_COUNT(R1) BNEQ 0$ ; No data to xfer, just send param blk BRW 20$ 0$: BSBW PKV$_LOADMAPS ; Load map regs and param blk MOVL SCDRP$L_DATA_PTR(R5),- ; VME address for user's buffer PARAMBLK$L_VME_ADDRS(R1) ;+ ; If the SCDRP$W_PAD_COUNT is non zero we must set up Scatter/gather maps on ; the adapter to read/write to a scratch area. ;- TSTW SCDRP$W_PAD_BCNT(R5) ; See if we have any padding to do BEQL 20$ ; Branch if no padding PUSHL R3 ; Save the SCDT address MOVL SPDT$L_PORT_UCB(R4),R3 ; Get the port UCB address BISL #PARAMBLK$M_SGO,- ; We have a pad count so use scatter/gather PARAMBLK$B_FLAGS(R1) MOVZWL SCDRP$W_CMD_MAPREG(R5),R0 ; Index for this param block MULL #SCATGATH$S_SCAT_GATHER,R0 ; Offset to our scatter gather map PUSHL R0 ; Save the offest for VME addrs calc later MOVL UCB$L_SCATTER_MAPS(R3),R2 ; BAse address of the scatter maps ADDL2 R2,R0 ; Address of our scatter gather maps MNEGL #1,SCATGATH$L_NEXTSG(R0) ; No other maps linked to this one MOVZWL SCDRP$W_BCNT(R5),- ; Load count for this VME address SCATGATH$L_DATADESC(R0) MOVB #^X0B,SCATGATH$B_ADDRMOD(R0) ; No priv BLOCK mode MOVL SCDRP$L_DATA_PTR(R5),- ; VME address for user's buffer SCATGATH$L_DATAADDR(R0) ; ; Now lets setup the scratch page after we check to see if we are doing a read ; or a write. ; MOVZWL SCDRP$W_PAD_BCNT(R5),- ; Load count for this VME address SCATGATH$L_DATADESC1(R0) MOVB #^X0B,- ; No priv data BLOCK mode SCATGATH$L_DATADESC1+3(R0) CLRL SCATGATH$L_DATADESC2(R0) ; No more maps to use MOVL UCB$L_SCRATCH_INVME(R3),- ; VME address for user's buffer SCATGATH$L_DATAADDR1(R0) BBS #IRP$V_FUNC,SCDRP$W_STS(R5),10$ ; Branch if it's read request MOVL UCB$L_SCRATCH_OUTVME(R3),- ; VME address for user's buffer SCATGATH$L_DATAADDR1(R0) ; ; We have to load the param block with the address of the scatter/gather maps instead of the ; VME address of the users's buffer. The user's buffer address is loaded into the maps instead ; 10$: MOVL UCB$L_SCATTER_VMEADDRS(R3),R2 ; Get the base VME address for the maps POPL R0 ; Offset to scatter/gather maps ADDL3 R2,R0,PARAMBLK$L_VME_ADDRS(R1) ; VME address of our scatter/gather maps CLRL PARAMBLK$L_TRANS_COUNT(R1) ; Bcnt in the scat/gath maps POPL R3 ; Restore the SCDT address ; ; Now that we have the command block ready we can bump queue header to indicate ; there is another parameter block ready to process. ; 20$: MOVL SPDT$L_PORT_UCB(R4),R1 ; Get the port UCB address MOVL UCB$L_QUE_HEADER(R1),R1 ; Get pointer to queue header MOVL CMD_QUE$L_PARAM_IN(R1),R0 ; Next free parameter block INCL R0 ; Point to next param block in the queue BICL #^C,R0 ; Modulo the queue size MOVL R0,CMD_QUE$L_PARAM_IN(R1) ; Point to next entry ;+ ; After the command packet has been passed to the adapter we wait for an ; interrupt and release the port. We release the port because the adapter will ; take care of the entire SCSI transaction and notify us when it's done or an ; error occurs. ;- MOVL SPDT$L_PORT_CSR(R4),R2 ; Get port CSR MOVZBL #1,R1 ; 1 = param block ready in cmd queue SWAPWORD R1 ; Turn cmd into big endian DEVICELOCK - ; Acquire device lock to ensure synchronization. LOCKADDR=SPDT$L_DLCK(R4),- LOCKIPL=SPDT$B_DIPL(R4),- PRESERVE=NO WRITE_CSR R1,PKV$W_CHAN_ATTN(R2),VME=#0,- LENGTH=WORD,ENVIRON=SPECIFIC WFIRELPORT 1000$,SCDRP$L_DISCON_TIMEOUT(R5); Wait for command completion FORK REQPORT ;+ ; Get the status out of the status block. The status block addrs is returned in the SCDRP ;- MOVL SCDRP$L_SAVE_DATA_PTR(R5),R0 ; Get the address of the status block MOVB STATUSBLK$B_SCSI_PHASE(R0),- ; Return SCSI status back to class driver @SCDRP$L_STS_PTR(R5) MOVL SCDRP$L_SCSIMSGI_PTR(R5),R1 ; Get address of MSG IN area MOVL STATUSBLK$L_SCSI_STAT(R0),(R1)+ MOVW STATUSBLK$L_SCSI_INFO(R0),(R1)+ MOVZWL SCDRP$L_BCNT(R5),- ; Sufficient inquiry data returned? SCDRP$L_TRANS_CNT(R5) ADDL SCDRP$W_PAD_BCNT(R5),- ; Include the pad in xfered count SCDRP$L_TRANS_CNT(R5) BITB #^C,- ; Successfull command complete STATUSBLK$B_FLAGS(R0) BNEQ 100$ ; Branch if not 30$: MOVZBL #SS$_NORMAL,R0 SUBRETURN 100$: BSBW PK$HANDLE_ERROR SUBRETURN 1000$: LOG_ERROR,TYPE=CTL_ERR jsb g^ini$brk MOVL SPDT$L_PORT_UCB(R4),R0 BICW #UCB$M_ONLINE,- ; Set the port offline UCB$W_STS(R0) MOVZWL #SS$_CTRLERR,R0 ; Return error to the class driver SUBRETURN .DISABLE LSB PKV$INTERRUPT:: MOVL @(SP)+,R3 ; Get address of IDB DEVICELOCK - ; Give us access to those CSRs. LOCKADDR=IDB$L_SPL(R3),- ; Use the Dev lock address in IDB. PRESERVE=NO,- CONDITION=NOSETIPL MOVL IDB$L_UCBLST(R3),R5 ; For now assume port is PKx0 BBCC #UCB$V_INT,UCB$W_STS(R5),0$ 0$: MOVL UCB$L_PDT(R5),R4 ; Get SPDT address. BBCCI #PKV$V_TYPE0,- ; branch if no TYPE0 pending UCB$L_FLAGS(R5),10$ MOVQ UCB$L_FR3(R5),R3 ; Restore fork registers JSB @UCB$L_FPC(R5) ; Where we were waiting for interrupt BRB 1000$ ; Finish up TYPE0 ;+ ; We loop pulling off status packets till there are no more before we dismiss this ; interrupt. We the list is empty we are done. That means we could get an interrupt ; and not have any status to pull off. ;- 10$: MOVL UCB$L_QUE_HEADER(R5),R0 ; Get queue header address MOVL CMD_QUE$L_STATUS_OUT(R0),R1 CMPL R1,CMD_QUE$L_STATUS_IN(R0) ; Check to see if its empty BEQL 1000$ ; Branch if it is, time to leave ;+ ; We have a entry in the queue and we have its index, calc its address and resume ; the fork process. ;- ;### this incl must be moved to a point when we are done with the status blk ; MULL3 #STATUSBLK$S_STATUS_BLOCK,- ; Get offset of our status block R1,R2 ADDL3 R2,UCB$L_STAT_BLOCK(R5),R3 ; SVA of our status block INCL R1 ; Point to next status block BICL #^C,R1 ; mod to the que size MOVL R1,CMD_QUE$L_STATUS_OUT(R0) ; Indicate we got the block PUSHL R5 MOVL STATUSBLK$L_COMD_ID(R3),R5 ; Address of SCDRP for this transfer MOVL R3,SCDRP$L_SAVE_DATA_PTR(R5) ; Save for fork process MOVQ SCDRP$L_FR3(R5),R3 ; Restore registers JSB @SCDRP$L_FPC(R5) ; Resume fork process POPL R5 BRB 10$ ; Check for more status blocks 1000$: DEVICEUNLOCK - LOCKADDR=UCB$L_DLCK(R5),- PRESERVE=NO POPR #^M REI .SBTTL + PK$RESET_SCSI_BUS - Reset the SCSI Bus ;++ ; PK$RESET_SCSI_BUS: ; ; This routine is called to RESET the SCSI Bus. This code asserts the RST line ; on the SCSI bus for the "Reset Hold Time" of 25usec. ; ; ; INPUTS: ; ; R2 = Address of NCR 53C94 CSRs ; R3 = Address of SCDT ; R4 = Address of SPDT ; R5 = Address of SCDRP ; ; OUTPUTS: ; ; SCSI Bus is Reset, SCDT$L_RST_CNT is incremented. ; ;-- .ENABLE LSB ; PK$RESET_SCSI PK$RESET_SCSI_BUS:: ; Reset the SCSI Bus. RSB .DISABLE LSB ; PK$RESET_SCSI_BUS .SBTTL + PK$SENSE_PHASE - Sense SCSI bus phase, ATN signal ;++ ; PK$SENSE_PHASE: ; ; This routine is used during target mode operation to return the current ; SCSI bus phase and the state of ATN signal. The data is returned in R1 ; and has the following format: ; ; 31 30 3 2 1 0 ; +-----+-----------------+-----+-----+-----+ ; | ATN | | MSG | C/D | I/O | ; +-----+-----------------+-----+-----+-----+ ; ; ; NOTE THIS ROUTINE IS NOT CURRENTLY IMPLEMENTED IN THIS PORT. ?? ** ; ; INPUTS: ; ; R4 = Address of SPDT ; ; OUTPUTS: ; ; R0 = SS$_NORMAL ; R1 = SCSI bus phase, ATN signal ; R2 is destroyed; all other registers are preserved. ;-- PK$SENSE_PHASE:: RSB ; Return to caller .SBTTL + PK$RELEASE_BUS - Release SCSI bus ;++ ; PK$RELEASE_BUS: ; ; This routine is used during target mode operation to release the SCSI bus ; and the port. This should be the last routine called by the class driver ; callback routine. ; ; INPUTS: ; ; R4 = Address of SPDT ; R5 = Address of SCDRP ; ; OUTPUTS: ; R0 = SS$_NORMAL ; R2 is destroyed; all other registers are preserved. ; ;-- PK$RELEASE_BUS:: RSB ; Return to caller. .SBTTL + PK$ENABLE_TARGET_MODE - Enable target mode ;+ ; PK$ENABLE_TARGET_MODE: ; ; This routine gets called periodically by the software timer to enable target ; mode which can potentially get disabled if a bus reset occurs. Bus resets ; cause the select enable register to get cleared, preventing the port driver ; from being selected. ; ; INPUTS: ; ; R4 = Address of SPDT ; ; OUTPUTS: ; ; R2 is destroyed; all other registers are preserved. ; ;- PK$ENABLE_TARGET_MODE:: MOVL SPDT$L_PORT_CSR(R4),R2 ; Get address of port CSRs RSB .SBTTL PKV$AUTO_FDT - FDT jacket routine for SCSI Autoconfiguration ;++ ; PKV$AUTO_FDT: ; ; This is the FDT routine used for SCSI Autoconfigure. This routine serves ; to transfer control to PKN$AUTO_FDT, which is in SCSIAUTO.MAR. ; ; INPUTS: ; ; R3 = Address of IRP ; R4 = Address of PCB ; R5 = Address of UCB ; R6 = Address of CCB ; R8 = Address of FDT routine ; AP = Address of P1 ; P1 = Buffer Address ; P2 = Buffer size in bytes ; P3 = SCSI ID ; ; OUTPUTS: ; ; Control is never returned to this routine. ; ;-- PKV$AUTO_FDT: BRW PKN$AUTO_FDT ; Branch to the real FDT routine. .SBTTL PKV_STARTIO - Start I/O Operation ;+ ; PKV_STARTIO: ; ; The STARTIO routine is not ever called by the end user. Rather, there are ; several function codes which have special meanings, as described below. ; ; INPUTS: ; ; R3 = Address of our IRP ; R5 = Address of our UCB ; ; OUTPUTS: ; ; R0 = 1st longword of I/O status: contains status code and ; number of bytes transferred. ; R1 = 2nd longword of I/O status: device-dependent. ; ; This routine preserves all registers except R0-R2 and R4. ; ;- PKV_RESTARTIO: MOVL UCB$L_IRP(R5),R3 ; Pick up original IRP pointer PKV_STARTIO: MOVW IRP$W_FUNC(R3),- ; Save function code & modifier UCB$W_FUNC(R5) ; ;+ ; Do final set up and dispatch to the routine which performs the function. ;- EXTZV #IRP$V_FCODE,#IRP$S_FCODE,- ; Extract I/O function code... IRP$W_FUNC(R3),R1 ; ...without function modifiers ASSUME IO$_NOP EQ 0 BEQL IO_NOP ; Branch if function code is IO$_NOP CMPL #IO$_READPBLK,R1 ; Note all Read functs -> READPBLK BNEQ 40$ BRW PK$INQUIRE ; Branch if yes. 40$: BRW IO_BOGUS ; Bogus request if we fall through ;+ ; Subroutines return to next page. ;- ;+ ; Subroutines do their thing(s), then return here with status in R0. ; Note that R5 must contain the address of the UCB upon returning here. ;- FINISH_IO:: MOVL UCB$L_IRP(R5),R3 ; Restore original IRP pointer REQCOM ; Complete request ;+ ; The following STARTIO locations are useful for testing out new features ; in the port driver prior to merging the port driver with the class driver. ; Under normal operartions, all QIOs issued to the port driver are failed ; with an illegal I/O function status (R0 = SS$_ILLIOFUNC). ;- .ENABLE LSB IO_BOGUS: ; Illegal function code MOVZBL #SS$_ILLIOFUNC,R0 ; Specify the error type BRW FINISH_IO IO_NOP: MOVZBL #SS$_ILLIOFUNC,R0 ; Specify the error type BRW FINISH_IO .DISABLE LSB .SBTTL PKV_CANCELIO - Cancel I/O Routine ;+ ; PKV_CANCELIO: ; ; Cancels an I/O operation in progress. ; This routine calls IOC$CANCELIO to set the cancel bit in the UCB status ; word if: ; ; the device is busy, ; the IRP's process ID matches the cancel process ID, ; the IRP channel matches the cancel channel. ; ; If IOC$CANCELIO sets the cancel bit, then this driver routine does ; device-dependent cancel I/O fixups. ; ; 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 the 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 ; These reason codes are defined by the $CANDEF macro. ; ; OUTPUTS: ; ; The routine must preserve all registers except R0-R3. ; ; The routine may set the UCB$M_CANCEL bit in UCB$W_STS. ; ;- PKV_CANCELIO: ; Cancel an I/O operation JSB G^IOC$CANCELIO ; Set cancel bit if appropriate. BBC #UCB$V_CANCEL,- ; If the cancel bit is not set, UCB$W_STS(R5),10$ ; just return. ;+ ; Device-dependent cancel operations go next. ;- ;+ ; Finally, the return. ;- 10$: RSB ; Return .SBTTL PKV_REGDUMP - Save port state in Errorlog buffer ;+ ; PKV_REGDUMP: ; ; Save all information about the current state of the port in an errorlog ; buffer. ; ; INPUTS: ; ; R0 = Address of errorlog buffer were register values ; are to be dumped. ; R4 = Address of SPDT ; R5 = Address of UCB ; R7 = Error type and subtype ; R8 = Address of SCDT ; ;- PKV_REGDUMP: PUSHAL (R0)+ ; Save address of start of buffer PUSHR #^M ; Save regs MOVB #PKV$K_ERROR_REV,(R0)+ ; Save revision level MOVW R7,(R0)+ ; Save error type and subtype. MOVB #-1,(R0)+ ; Assume no CDT (set SCSI ID to -1) TSTL R8 ; Any CDT address? BEQL 10$ ; Branch if not FFS #0,#8,- ; Translate SCSI ID bitmask to SCDT$L_SCSI_BUS_ID(R8),- ; SCSI ID and save in errlog buffer -1(R0) ; ;+ ; Save information about the current SCSI command including the command, ; message, and status bytes. ;- 10$: CLRW (R0)+ ; Assume no SCSI CMD,MSG MOVB #^xFF,(R0)+ ; Assume no SCSI STS byte TSTL R8 ; Any CDT? BEQL 45$ ; Branch if not MOVL SCDT$L_SCDRP_ADDR(R8),R3 ; Get the SCDRP address BEQL 45$ ; Branch if none MOVL SCDRP$L_CMD_PTR(R3),R1 ; Get address of SCSI command BEQL 45$ ; Branch if none SUBL #3,R0 ; Back up pointer into errlog buffer MOVL (R1)+,R2 ; Get command byte count MOVB R2,(R0)+ ; Save command byte count 20$: MOVB (R1)+,(R0)+ ; Save a byte of command SOBGTR R2,20$ ; Repeat for entire command MOVB #1,(R0)+ ; One message byte MOVB @SCDRP$L_SCSIMSGI_PTR(R3),(R0)+ ; 40$: MOVB @SCDRP$L_STS_PTR(R3),(R0)+ ; Save status byte ;+ ; We must figure out what the current parameter block and status block if ; any that there were and copy those to the error log. ;- 45$: MOVL #^XDEADDEAD,(R0)+ ; Mark status packet MOVL UCB$L_PDT(R5),R1 ; Get the PDT MOVL SPDT$L_PORT_CSR(R1),R1 ; CSR of the SCSI controller PUSHL R0 READ_CSR PKV$W_STATUS_PORT(R1),(R0)+,VME=#0,- LENGTH=WORD,ENVIRON=SPECIFIC POPL R0 MOVL SCDRP$L_SAVE_DATA_PTR(R3),R1 ; address of status packet BEQL 100$ ; No status packet PUSHR #^M MOVC3 #STATUSBLK$S_STATUS_BLOCK,- ; Status packet into err log (R1),(R0) POPR #^M ADDL2 #STATUSBLK$S_STATUS_BLOCK,R0 ; Update status packet size ;+ ; Figure size of error log packet ;- 100$: POPR #^M ; Restore regs SUBL (SP),R0 ; Get number of bytes saved ADDL #3,R0 ; Round up and translate to longword ASHL #-2,R0,@(SP)+ ; count and save in top of errlog buffer RSB .SBTTL + SS$ALLOC_MAP_RES - Allocate map resources. ;++ ; SS$ALLOC_MAP_RES ; ; This routine is called by the class driver from the routine SC$MAP_BUFFER to ; map the user data for DMA by the port driver. The port driver will allocate ; and load the mapping registers required for the transfer. If DMA maps cannot ; be allocated at this time, the SCDRP is queued onto the DMA map wait queue ; until resources become available. ; ; INPUTS: ; ; R4 - Port SPDT address. ; SPDT$L_UCB - Address of class UCB ; ; R5 - Class SCDRP address. ; ; SCDRP$L_BCNT - Transfer byte count. ; SCDRP$W_BOFF - Byte offset in page. ; SCDRP$L_SVAPTE - SVA of first PTE of user buffer. ; ; OUTPUTS: ; ; R0 - Status. ; ; R5 - Class SCDRP address. ; Port-specific mapping information (1 longword): ; ; SCDRP$W_NUMREG - Number of mapping registers allocated. ; SCDRP$W_MAPREG - First mapping register allocated. ; ; R3,R4,R5 - Preserved. ; ;-- .ENABL LSB SS$ALLOC_MAP_RES:: PUSHR #^M MOVL SPDT$L_ADP(R4),R2 ; Get the ADP of the VME 2 XMI adapter MOVZWL SCDRP$L_BCNT(R5),R3 ; Get transfer byte count MOVZWL #511,R4 ; Get worst byte offset in page MOVAB ^X3FF(R3)[R4],R3 ; Calculate highest relative byte and round ASHL #-9,R3,R3 ; Calculate number of map registers required MOVAL -(SP),R1 ; Where the allocation desriptor will be returned BSBW IOC$ALOVMEMAP_DMAN ; Allocate the map registers BLBC R0,100$ ; Branch if no registers MOVW VME_MD$W_MAPREG(R1),- ; Starting mapping register SCDRP$W_MAPREG(R5) MOVW VME_MD$W_NUMREG(R1),- ; Number of registers SCDRP$W_NUMREG(R5) POPL R0 ; Free up map descriptor MOVZBL #SS$_NORMAL,R0 ; Success 50$: POPR #^M RSB 100$: CLRL R0 ; FAILURE BRB 50$ .SBTTL SS$DEALLOC_MAP_RES - RELEASE MAPPING RESOURCES ;++ ; SS$DEALLOC_MAP_RES ; ; This routine is called to release a segment of the DMA buffer. ; ; INPUTS: ; ; R4 - Port SPDT address. ; ; SPDT$L_DMA_BASE - SVA of the DMA buffer. ; SPDT$L_SPTE_SVAPTE - SVAPTE for the first SPTE allocated ; by the port. ; ; R5 - Class SCDRP address. ; ; SCDRP$L_BCNT - Transfer byte count ; SCDRP$W_BOFF - Byte offset in page ; ; ; OUTPUTS: ; ; R5 - Address of SCDRP ; ; SCDRP$W_MAPREG - Page number of the first of DMA buffer. ; SCDRP$W_NUMREG - Number of pages allocated. ; This number represents both the ; number of DMA pages allocated and ; number of SPTE's allocated. ; SCDRP$L_SVA_DMA- Address of the DMA buffer. ; ; R0 - Status ; ; If there are drivers waiting for segments of the DMA buffer will ; restore the next driver thread stalled waiting for this resource. ; Driver processes waiting here have their context stored in either ; a UCB fork block or a SCDRP fork block and the processing required to ; resume each of these types of driver process is slightly different. ; What is done for each is to allocate the required map registers ;-- .ENABL LSB SS$DEALLOC_MAP_RES:: MOVAL -(SP),R1 ; Map register descriptor MOVW SCDRP$W_NUMREG(R5),- ; Load descriptor with # of regsters VME_MD$W_NUMREG(R1) MOVW SCDRP$W_MAPREG(R5),- ; Load with start reg # VME_MD$W_MAPREG(R1) CLRW SCDRP$W_NUMREG(R5) ; reset scdrp CLRW SCDRP$W_MAPREG(R5) ; reset scdrp MOVL SPDT$L_ADP(R4),R2 ; Get the VME adapter's ADP BSBW IOC$RELVMEMAP_DMAN ; Release those registers POPL R0 ; Clean map descriptor off the stack MOVZBL #SS$_NORMAL,R0 RSB .DISABLE LSB .SBTTL + PK$ALLOC_CMD_BUFFER - Allocate Command Buffer Routine ;++ ; PK$ALLOCATE_CMD_BUFFER: ; ; This routine is called by the port driver routine SC$ALLOC_COMMAND_BUFFER ; to allocate a command DMA buffer. The actual allocation of buffer slots is ; handled by the SCSISUBS routine SS$ALLOC_CMD_RES. If there is another thread ; waiting for command buffer resources, the current request will be placed at ; the end of the command buffer wait queue. ; ; INPUTS: ; ; R1 = Size of command buffer required. ; ; R4 = Port SPDT address. ; SPDT$L_DMA_BASE - SVA of the DMA buffer. ; ; R5 = Class SCDRP address. ; ; OUTPUTS: ; ; R0 = Port Status: SS$_NORMAL - Success. ; SS$_BADPARAM - Failure. ; ; R1 = Size of buffer allocated. ; ; R2 = Address of command buffer ; ; R5 = Class SCDRP address. ; Port-specific mapping information (2 longwords) filled in: ; ; SCDRP$W_CMD_NUMREG - Number of pages in DMA command buffer allocated. ; SCDRP$W_CMD_MAPREG - Starting page number of DMA command buffer. ; SCDRP$L_SVA_CMD - System virtual address of DMA buffer allocated ; for the command. ; SCDRP$W_CMD_BCNT - Command buffer byte count. ; SCDRP$L_CMD_BUF_LEN - Command buffer length (just be thorough). ; ; R3,R4,R5 are preserved. ; ;-- .ENABL LSB PK$ALLOC_CMD_BUFFER:: JSB G^EXE$ALONONPAGED ; Get command packets BLBC R0,30$ ; FAILURE, don't set online MOVW R1,SCDRP$W_CMD_BCNT(R5) ; Pass back size allocated MOVL R1,SCDRP$L_CMD_BUF_LEN(R5) ; Another copy of SCSI cmd buffer MOVL R2,SCDRP$L_SVA_CMD(R5) MOVZWL #SS$_NORMAL,R0 ; Success 20$: RSB ; Return to caller 30$: MOVL #SS$_BADPARAM,R0 ; Return error status BRB 20$ ; Use common exit .DISABLE LSB .SBTTL + PK$DEALLOC_CMD_BUFFER - Deallocate Command Buffer Routine ;++ ; PK$DEALLOCATE_CMD_BUFFER: ; ; This routine is called by the port driver routine SC$DEALLOC_COMMAND_BUFFER ; to deallocate command buffer resources. The actual deallocation of buffer ; slots is handled by the SCSISUBS routine SS$DEALLOC_CMD_RES. ; ; If there are threads waiting for command buffer slots it will restore ; the next driver thread stalled waiting for this resource. Driver processes ; waiting here have their context stored in either a UCB fork block or an ; SCDRP fork block and the processing required to resume each of these types ; of driver process is slightly different. ; ; INPUTS: ; ; R4 - Port SPDT address. ; R5 - Class SCDRP address. ; ; SCDRP$W_CMD_NUMREG - Cleared. ; SCDRP$W_CMD_MAPREG - Cleared. ; SCDRP$L_SVA_CMD - Cleared. ; ; OUTPUTS: ; ; R0 - Status: SS$_NORMAL for success, ; SS$DEALLOC_CMD_RES will Bugcheck on failure. ; ; R3-R5 are preserved. ; ;-- PK$DEALLOC_CMD_BUFFER:: MOVL SCDRP$L_SVA_CMD(R5),R0 CLRB IRP$B_TYPE(R0) MOVW SCDRP$W_CMD_BCNT(R5),- IRP$W_SIZE(R0) JSB G^EXE$DEANONPAGED CLRW SCDRP$W_CMD_NUMREG(R5) ; Clear reqired fields CLRW SCDRP$W_CMD_MAPREG(R5) ; ditto CLRL SCDRP$L_SVA_CMD(R5) ; Clear CMD buffer address. RSB .SBTTL + PK$LOAD_MAPS - Load map registers that map users DMA buffer ;++ ; PK$LOAD_MAPS ; ; ; INPUTS: ; r5 - scdrp ; r4 - SPDT ; ; OUTPUTS: ; ; ;-- .ENABLE LSB PKV$_LOADMAPS: PUSHR #^M MOVL R5,R6 ; SCDRP into working register MOVAL -(SP),R1 MOVW SCDRP$W_NUMREG(R6),- ; Save number allocated VME_MD$W_NUMREG(R1) MOVW SCDRP$W_MAPREG(R6),- ; Starting map registers allocated VME_MD$W_MAPREG(R1) ; This will determine VME address mapped to MOVL SPDT$L_ADP(R4),R2 ; Get the ADP of the VME 2 XMI adapter MOVL SCDRP$W_BOFF(R6),R5 ; Get the BOFF for the buffer MOVL SCDRP$L_BCNT(R6),R4 ; Get the byte count MOVL SCDRP$L_SVAPTE(R6),R3 ; R3 => first PTE of buffer BBC #SCDRP$V_S0BUF,- ; Are we a s0 address? SCDRP$L_SCSI_FLAGS(R6),10$ ; Branch if NOT BICL3 #^C<^X1FF>,- ; Get offset with the first page SCDRP$L_SVA_USER(R6),R5 MOVL SCDRP$L_SVA_USER(R6),R3 ; SVA to start the map reg loading EXTZV S^#VA$V_SVPN,- ; Get buffer virtual page number S^#VA$S_SVPN,R3,R3 MOVL G^MMG$GL_SPTBASE,R0 ; Get system page table address MOVAL (R0)[R3],R3 ; get the SVAPTE 10$: MOVL #XVIB$M_RMWMODE,R0 BSBW IOC$LOADVMEMAP_DMAN ; Map the VME POPL R0 ; Clean off map reg descriptor ;+ ; Now we can compute the VME address from the Map register number and the BOFF, ; we save this in the SCDRP for later use. ;- MOVZWL SCDRP$W_MAPREG(R6),R0 MULL2 #512,R0 ; Get the VME base address for the param ADDL3 R0,R5,SCDRP$L_DATA_PTR(R6) ; Add start offset + BOFF = VME comm base MOVZBL #SS$_NORMAL,R0 20$: POPR #^M RSB 100$: CLRL R0 BRB 20$ .DISABLE LSB ; INPUT ; R5 - SCDRP ; SCDRP$L_CMD_BUF ; ; OUTPUT ; R1 - parameter block for this command ; R5 - SCDRP ; SCDRP$W_CMD_MAPREG ; .ENABLE LSB PKV$_SETUP_COMMAND: MOVL SPDT$L_PORT_UCB(R4),R0 ; Now we have the port UCB MOVL UCB$L_QUE_HEADER(R0),R1 ; Parmeter block que header MOVL CMD_QUE$L_PARAM_IN(R1),R2 ; Next free parameter block MOVW R2,SCDRP$W_CMD_MAPREG(R5) ; Save index to our param block MULL2 #PARAMBLK$S_PARAM_BLOCK,R2 ; Byte offset to our param block MOVL UCB$L_PARAM_BLOCK(R0),R1 ; Start of param block in the que ADDL R2,R1 ; Address of our param block ; ; Now we move the SCSI command into the the command block. It must be ordered ; in big endian, so we swap the bytes from little to big endian ; MOVL SCDRP$L_CMD_PTR(R5),R2 MOVL (R2)+,R0 ; Length of SCSI command PUSHL R1 MOVAB PARAMBLK$B_SCSI_CMD(R1),R1 ; Move it into the command packet 10$: MOVB (R2)+,(R1)+ SOBGTR R0,10$ MOVL (SP),R1 ; Restore Param blk addrs MOVAB PARAMBLK$B_SCSI_CMD(R1),R1 ; Pass this to the swap routine MOVZBL #12,R0 ; Size of SCSI command BSBW BYTE_SWAP_LONG POPL R1 ; Restore param blk addrs RSB .DISABLE LSB .ENABLE LSB PK$HANDLE_ERROR: ;+ ; Handle all transfer error here. If it's a SCSI error that will be passed back up to ; the class driver to deal with. An adapter error will be handled here. ;- BBS #STATUSBLK$V_ERR,- ; Branch if not an adapter error STATUSBLK$B_FLAGS(R0),20$ BBS #STATUSBLK$V_DTT,- ; Branch if a partial transfer STATUSBLK$B_FLAGS(R0),30$ BBS #STATUSBLK$V_SE,- STATUSBLK$B_FLAGS(R0),40$ ; Branch if a soft error BBS #STATUSBLK$V_RTY,- STATUSBLK$B_FLAGS(R0),50$ ; Branch if did retry commands ;+ ; No error conditions were observed by the adapter so just return ;- 50$: MOVZBL #SS$_NORMAL,R0 RSB ;+ ; The adapter has reported an error. So we look to see what the error number is and take ; the correct action for that error. ;- 20$: BRW PKV$PARSE_ERROR ;+ ; A partial transfer has taken place. This means we requested more info than was given ; us. This is a normal situation under some conditions. If there is a problem log the ; error and return an error back to the class driver. ;- 30$: MOVZBL #SS$_NORMAL,R0 RSB ;+ ; One of the SCSI command had to be retried. This is not an error to the class driver, ; but we bump our counter and log the error. ;- 40$: INCL SPDT$L_RETRY_CNT(R4) ; Keep track of retries LOG_ERROR TYPE=ARB_FAIL MOVZBL #SS$_NORMAL,R0 RSB .DISABLE LSB .ENABLE LSB PKV$PARSE_ERROR: MOVZBL STATUSBLK$B_ERROR(R0),R1 ; Get the adapter error code CMPL #^X14,R1 ; Check for driver coding error. BGEQ 100$ ; Branch on coding error CMPL #^X61,R1 ; Check for hard adapter problem BLEQ 200$ ;+ ; If we make it to here we had some sort of SCSI error. We log the error and return ; a retry status to the class driver. ;- CASE SRC=R1,- DISPLIST=- <10$,- ; 14 - VME bus timeout 10$,- ; 15 - SCSI bus error 20$,- ; 16 - scatter/gather fetch error 1000$,- ; 17 - NDF 1000$,- ; 18 - NDF 1000$,- ; 19 - NDF 1000$,- ; 1A - NDF 1000$,- ; 1B - NDF 1000$,- ; 1C - NDF 1000$,- ; 1D - NDF 30$,- ; 1E - Selection timeout 40$,- ; 1F - timeout 50$,- ; 20 - SCSI parity error 10$,- ; 21 - unexpected disconnect 10$,- ; 22 - General SCSI bus error 80$,- ; 23 - Bad status/not logged 60$,- ; 24 - Phase error 10$,- ; 25 - bad byte seen 20$,- ; 26 - error synchronous negotiation 20$,- ; 27 - scatter/gather error 70$,- ; 28 - SCSI bus has been reset 1000$,- ; 29 - NDF 1000$,- ; 2A - NDF 90$>,- ; 2B - Vendor unique command error TYPE=L,LIMIT=#^X14 ;+ ; There has been some sort of protical error between the port driver and the SCSI ; adapter. This was caused most likely by a coding error in the port driver. We will ; log the error, set a controller error for the class driver and set the port offline. ;- 100$: LOG_ERROR,TYPE=CTL_ERR ; Log the controller error jsb g^ini$brk MOVL SPDT$L_PORT_UCB(R4),R0 BICW #UCB$M_ONLINE,- ; Set the port offline UCB$W_STS(R0) MOVZWL #SS$_CTRLERR,R0 ; Return error to the class driver RSB ;+ ; Some sort of HW error or internal adapter error has occured. We log the error, ; return an error to the class driver, and then set the port offline. ;- 200$: LOG_ERROR,TYPE=CTL_ERR jsb g^ini$brk MOVL SPDT$L_PORT_UCB(R4),R0 BICW #UCB$M_ONLINE,- ; Set the port offline UCB$W_STS(R0) MOVZWL #SS$_CTRLERR,R0 ; Return error to the class driver RSB 10$: LOG_ERROR,TYPE=BUS_ERR BRW 95$ 20$: LOG_ERROR,TYPE=CTL_ERR BRW 95$ 30$: LOG_ERROR,TYPE=SEL_FAIL BRW 95$ 40$: LOG_ERROR,TYPE=TIMEOUT BRW 95$ 50$: LOG_ERROR,TYPE=PARITY_ERROR BRB 95$ 60$: LOG_ERROR,TYPE=PHASE_ERROR BRB 95$ 70$: LOG_ERROR,TYPE=BUS_RESET BRB 95$ 80$: MOVZBL #SS$_NORMAL,R0 RSB 90$: LOG_ERROR,TYPE=ILLEGAL_MSG BRB 95$ 95$: MOVZWL #SS$_MEDOFL,R0 ; Ask class driver to retry the operation RSB 1000$: LOG_ERROR,TYPE=RESERVED5 RSB .DISABLE LSB PK$SELECT_TO_ABORT:: JSB G^INI$BRK HALT PK$ABORT:: JSB G^INI$BRK HALT PK$CONNECT_TO_TARGET:: MOVZBL #1,R0 RSB PK$FINISH_CMD:: JSB G^INI$BRK HALT PK$RECIEVE_BYTES:: JSB G^INI$BRK HALT PK$SEND_BYTES:: JSB G^INI$BRK HALT PK$SET_PHASE:: JSB G^INI$BRK HALT PK$RECEIVE_BYTES:: JSB G^INI$BRK HALT PKV_END: .END ; Address of last location in driver