.TITLE PKSDRIVER - SIMport SCSI Port Driver Datalink .IDENT 'X-28' ;**************************************************************************** ;* * ;* COPYRIGHT © 1994, 1995 BY * ;* DIGITAL EQUIPMENT CORPORATION, MAYNARD, MASSACHUSETTS. * ;* ALL RIGHTS RESERVED. * ;* * ;* THIS SOFTWARE IS FURNISHED UNDER A LICENSE AND MAY BE USED AND COPIED * ;* ONLY IN ACCORDANCE WITH THE TERMS OF SUCH LICENSE AND WITH THE * ;* INCLUSION OF THE ABOVE COPYRIGHT NOTICE. THIS SOFTWARE OR ANY OTHER * ;* COPIES THEREOF MAY NOT BE PROVIDED OR OTHERWISE MADE AVAILABLE TO ANY * ;* OTHER PERSON. NO TITLE TO AND OWNERSHIP OF THE SOFTWARE IS HEREBY * ;* TRANSFERRED. * ;* * ;* THE INFORMATION IN THIS SOFTWARE IS SUBJECT TO CHANGE WITHOUT NOTICE * ;* AND SHOULD NOT BE CONSTRUED AS A COMMITMENT BY DIGITAL EQUIPMENT * ;* CORPORATION. * ;* * ;* DIGITAL ASSUMES NO RESPONSIBILITY FOR THE USE OR RELIABILITY OF ITS * ;* SOFTWARE ON EQUIPMENT WHICH IS NOT SUPPLIED BY DIGITAL. * ;* * ;* * ;**************************************************************************** ; ; ; FACILITY: ; ; ALPHA VMS Executive, device drivers ; ; ABSTRACT: ; ; This module contains the SIMport TZA (TURBOCHANNEL to SCSI bus) ; and PZA (PCI to SCSI bus) SCSI port driver datalink code. PKSDRIVER, ; SCSIAUTO, and SCSICOMMON must be built together to create the ; SCSI port driver image SYS$PKSDRIVER.EXE. ; ; ENVIRONMENT: ; ; Kernel mode ; ; AUTHOR: ; ; David Fairbanks ; ; CREATION DATE: Decemder 1993 ; ; REVISION HISTORY: ; ; X-28 DOF Dave Fairbanks 22-Nov-96 ; - Setup SPDT$L_DMA_SIZE correctly in routine SETUP_CSRS. ; The upper 12 bits were still set after the calculation. ; This prevented the use of map registers for IO requests ; that needed them. ; ; X-27 DOF Dave Fairbanks 30-Oct-96 ; - Fix operand ordering in EVAX_SLL instruction in routine ; MAP_CSRS. This caused map registers to be used in cases ; where they were not required. ; - Increase the size of the error log buffer. A previous ; edit increased the number of longwords in the error log ; buffer, however, the error log buffer size was not ; increased accordingly. ; ; X-26 DOF Dave Fairbanks 20-Sept-96 ; - Change 32 bit math to 64 bit math when computing physical ; addresses for BSD's. The upper 32 bits of the physical ; address were getting cleared. This involves changing ADDL ; instructions to EVAX_ADDQ instructions. ; - Comput BSD physical addresses correctly for 1 and 2 byte ; data transfers. Use the physical address already computed ; for the BSM. ; - Return resources to the ADFQ when invalid responses are ; received. ; - Change error log revision to 2. ; - Change ident of previous edit history to X-25. ; ; X-25 RCL Rick Lord 2-Aug-96 ; 1) Modify PK$CONNECTION_CHAR_SET and PK$NEGOTIATE_SYNCH ; to honor STDT$V_DFLG_SUPPRESS_SDTR ; 2) Modify PK$SEND_COMMAND to support user-requested ; asynchronous mode SDTR negotiation. ; ; X-24 DOF Dave Fairbanks 1-Aug-96 ; - Insert the Path Inquiry information and the Adapter ; Status Register into the error log packet in routine ; PKS_REGDUMP. ; - Reduce the time to initialize the adapter by waiting ; for 1/100 of a second when calling EXE$KP_TQE_WAIT ; in macro WAIT_FOR_RESPONSE. ; ; X-23 DOF Dave Fairbanks 24-Jul-96 ; Re-insert the clearing of bit CRCTX$M_ITEM_VALID in ; CRCTX$L_FLAGS. The previous edit removed this instruction. ; Clearing this bit is required to prevent an INCONSTATE ; crash due to a double deallocation of map registers. ; ; X-22 DOF Dave Fairbanks 28-May-96 ; Remove the clearing of bit CRCTX$M_ITEM_VALID in ; CRCTX$L_FLAGS. The previous edit inserted this instruction. ; Clearing this bit causes an ACCVIO during target mode ; operations. ; ; X-21 DOF Dave Fairbanks 2-Apr-96 ; 1.) Merge GOBLIN 15A3 into GRYPHON. This edit was dropped ; in GRYPHON: ; X-15A3 DOF Dave Fairbanks 28-Mar-1996 ; Fix physical address sign extension bug for Rawhide. ; SPDT$L_DMA_BASE is 80000000 for Rawhide. This causes ; bit 31 to be sign extended into the upper longword. ; ; 2.) Merge GOBLIN 15A3A1 into GRYPHON: ; X-15A3A1 DOF Dave Fairbanks 2-Apr-1996 ; In routine PKS$UNMAP_BUFFER_MR, if map registers were used ; to map the user buffer, clear bit CRCTX$M_ITEM_VALID in ; CRCTX$L_FLAGS to invalidate the counted resource for this ; CRCTX. ; ; In routine SETUP_ACCEPT_CCB_LIST, setup the FLINKs of each ; Queue Buffer to point to the next entry in the queue. The ; FLINKs were getting zeroed during queue setup which resulted ; in only one Queue Buffer on the list. ; ; In routine RET_TARGET_MODE_RESOURCES, when deallocating the ; Queue Buffers used for the Accept Target IO CCB list, if ; there are less buffers than we expect to be on the list, ; then BUG_CHECK. ; ; Increase NUM_ADFQ_ENTRIES to 8. We were running out of ; entries during heavy SCSI cluster initialization traffic. ; ; 3.) Allow for memory hole support. After calling IOC$NODE_DATA ; with key IOC$K_DIRECT_DMA_SIZE, compare it to ; MMG$GL_MAXPFN+1 to determine if to use mapping registers. ; ; X-20 SGS0092 Steve Skonetski 19-Mar-96 ; Merge Dave Fairbanks fixes from 15a2 into gryphon. ; ; X-15A2 DOF Dave Fairbanks 26-Feb-1996 ; Map the BSM physical address correctly for systems with memory ; greater than DMA_WINDOW size. On systems with greater than ; DMA_WINDOW size of memory, when PK$MAP_BUFFER_MR was called, if ; all of the user pages and the pad block were under the ; DMA_WINDOW size, the PK$MAP_BUFFER routine was called. ; However, if a BSM was needed, and non-paged pool has grown over ; the DMA_WINDOW size boundary, the BSM was allocated and used ; without the use of mapping registers. This caused the IO to be ; aborted by the PCI bus. ; ; X-19 RCL003 Rick Lord 5-Jan-95 ; 1) Correct several memory deallocations which used ; constants instead of actual allocation sizes. ; 2) Delete obsolete routine RESET_SDTR_FLAG, several ; unreferenced local constants and UCB$PS_SCDT, ; which is no longer necessary with edit X-13. ; 3) Add missing ASSUMEs for S0 buffer bit ; 4) Use configured bus width as maximum SCSI ID to ; scan in PORT_SHUTDOWN instead of a constant. ; 5) Add and load field AB$IW_SIZE to hold AB size ; and allow safe deallocation. ; 6) Change a MOVL of type+subtype+size to a MOVZWL ; for use as a buffer deallocation size. ; ; X-18 SCS Sue Sommer 29-Dec-1995 ; Modify pk$connection_char_set so that it no longer ; sets up unnecessary synchronous negotiation. ; ; X-17 RCL002 Rick Lord 13-Dec-95 ; In INIT_PORT initialize SPDT maximum and configured bus ; width fields. By default they are loaded with 8 and 8, ; but this adapter supports wide busses. ; ; X-16 JCH710 John C. Hallyburton, Jr. 11-Dec-1995 ; STAR:: DOCD$:[EVMS.PROJECT_DOCUMENTS]FS-SCSI-NAMING.PS ; Implement new naming format Inquiry Response message for ; ports with port allocation classes assigned. ; ; X-15 DOF Dave Fairbanks 17-NOV-1995 12:00 ; Fix memory deallocation bug in RET_TARGET_MODE_RESOURCES. ; A register was corrupted across a call to EXE$DEANONPGDSIZ. ; ; X-14 DOF Dave Fairbanks 6-NOV-1995 13:20 ; Add code for SCSI clusters. This includes: ; - Data structure definitions for ENABLE_LUN, ACCEPT_TARGET_IO, ; CONTINUE_TARGET_IO, SET_TARGET_STATE, and ; TARGET_EVENT_ACKNOWLEDGE commands. ; - Adding Target mode status code definitions for Carriers ; and Queue Buffers. ; - Initializing static Inquiry and Request Sense data during ; unit initialization. ; - Building and sending an ENABLE_LUN command to the adapter ; during unit initialization to allow the adapter to respond ; to selections. ; - Adding interrupt dispatch routines to handle CDBs whenever ; the adapter is selected. ; - Re-enable the adapter to respond to selections after ; certain unsolicited Target events occur. ; - Deallocating all Target Mode resources when shutting down ; the port. ; ; X-13 DOF Dave Fairbanks 30-OCT-1995 13:40 ; Fix the way SCSI bus resets are handled. The first entry ; in SPDT$PS_STDT_HASH_TABLE was getting destroyed. This ; caused a device at target ID 0 to be lost after a reset. ; - Delete routines GET_FAKE_CONNECTION, CLEAR_FAKE_CONNECTION, ; GET_SCDT, GET_STDT. ; - Only use routine GET_SCDRP when resetting SCSI bus. ; - Delete SCDRP by calling DRVDEALMEM. ; ; X-11A3 DOF Dave Fairbanks 11-OCT-1995 11:08 ; Force quadword alignment for SPDT cell SPDT$PS_BUSRESET_FKBLK ; and beyond to eliminate alignment faults. ; ; X-12 LDD Dan Duval 18-Aug-1995 10:10 ; DOF Dave Fairbanks ; Ensure that PA's don't get sign-extended. Calculate the ; correct number of pages when checking (in ; PKS$MAP_BUFFER_MR::) whether the entire transfer is within ; the direct-DMA window, and don't use a scratch register for ; the loop counter in this test. Don't use the ; CRCTX$M_ITEM_VALID bit to determine whether to deallocate ; map registers; it doesn't get cleared. ; ; X-11A1 RCL001 Rick Lord 25-Jul-95 ; Replace use of the standard UCB$L_CDT field, which is ; actually past the end of the port driver's UCB, with ; use of a PKSDRIVER-specific field, UCB$PS_SCDT. ; ; X-11 JFD820 James F. Dunham 14-JUN-1995 11:51 ; In the routine PKS$UNMAP_BUFFER, make sure that the ; BSM which was allocated off the QBUF is deallocated. ; Also prevent the (re-)allocation of the PADBLOCK in ; PKS$MAP_BUFFER, if called from PKS$MAP_BUFFER_MR: ; ; X-10 DOF Dave Fairbanks 26-APR-1995 ; - When sending a SCSI bus reset command to the adapter, ; get the Queue Buffer and Carrier from the ADFQ instead ; of the free pool. ; - Setup the Carrier fields correctly in routine SETUP_ADFQ. ; ; X-9 DOF Dave Fairbanks 5-APR-1995 ; - Add two entries (SS$_CTRLERR, SS$_MEDOFL) to the end of ; the CAM_STATUS_TABLE. This fixes the RMS crash where ; R0 was being returned with a zero status code. ; ; X-8 JFD0812 James F. Dunham 21-MAR-1995 ; Make data structure alignment changes to data segement ; ; X-7 DOF Dave Fairbanks 7-Feb-1995 ; - Fix the initialization of SPDT$L_SPTE_BASE. ; ; X-6 DOF Dave Fairbanks 19-Jan-1995 ; - Fix data corruption bug under heavy load. This includes ; modifying the MAP_BUFF routine to call PTETOPFN with the ; argument set up correctly. ; ; X-5 LDD Dan Duval 08-Jan-1995 ; - Add support for DMA map registers, for systems that ; have more physical memory than is directly addressable ; via DMA. ; ; X-4 DOF Dave Fairbanks 29-NOV-1994 ; - Setup the device connection id correctly when sending ; a SET DEVICE STATE command to the adapter. Deallocate ; buffer resource after a DEVICE STATE SET response message ; is received. ; - When a CAM status of data overrun is returned, map this ; status to SS$_DATAOVERUN instead of SS$_NORMAL. ; ; X-3 LDD Dan Duval 04-NOV-1994 ; - Use IOC$NODE_DATA calls IOC$K_DIRECT_DMA_BASE and ; IOC$K_DIRECT_DMA_SIZE to find the direct-DMA window ; on PCI systems (KZPSA), and use the resulting value ; to pass the correct (bus) physical address to the ; adapter for DMA operations. ; ; X-2 DOF Dave Fairbanks 26-OCT-1994 ; - Remove all queue insertions and removals for KPBs ; and Queue Buffers. There is no need for the queuing. ; - In PKS$FORK don't search a queue to find the KPB to resume. ; Just resume the KPB in the SCDRP in the Queue Buffer. ; - When allocating a block of memory, if the allocation failed, ; then KP_STALL_FORK_WAIT instead of crashing. ; - Fix bus reset code when channel is re-enabled. Call ; SPDT$PS_CD_RESET_SCSI_BUS in Kernel Process context. ; Add KP routine RESET_SCSI_BUS. ; ; ************* Change Edit History to Match CMS ******************** ; ; X-1 JFD0660 James F. Dunham 28-SEP-1994 ; SCSI-2 Checkin ; ; X-19 DOF Dave Fairbanks 27-SEP-1994 ; - Move REMOVE_EL from top of PKS$FORK to command specific ; dispatch routines. ; - Return resources to DAFQ when an unsolicited reselection ; occurs. ; - Make DEALLOC_RESOURCES pass the correct block size in R1 ; when calling SPDT$PS_RL_POOL_DEALLOC to deallocate the ; command buffers. ; - Clean up comments in PK$SEND_COMMAND. ; ; X-18 DOF Dave Fairbanks 22-SEP-1994 ; - Fix offsets in routine MAP_CSRS for KZPSA. ; ; X-17 DOF Dave Fairbanks 16-SEP-1994 ; - Set up queuing characteristics using SCDRP$V_FLAG_QUEUED_IO. ; - Use a BBCC to clear the CAR$V_AUTOSENSE_DATA_VALID bit. ; - Do not insert Carriers onto AB$PS_CAR_BLINK. ; - Use R1 as scratch in routine GET_QUEUE_CARRIER. ; - Use IOC$RETURN as the stall routine in GET_QUEUE_CARRIER. ; ; X-16 DOF Dave Fairbanks 12-SEP-1994 ; - The previous edit caused the UCB to be set online too late. ; Set the UCB online (UCB$M_ONLINE bit in UCB$L_STS) in ; routine REINIT_PORT. ; - Remove the test for BUS_RESET_DETECTED from the top of ; routine PK$SEND_COMMAND. ; - Add routines to handle all responses returned from adapter. ; - Move the call to routine SETUP_QUEUES to routine INIT_PORT. ; ; X-15 DOF Dave Fairbanks 9-SEP-1994 ; - After a SCSI bus reset command is sent to the adapter, do ; not set the port online (SPDT$M_STS_ONLINE in SPDT$L_STS) ; until the adapter has returned the channel enabled response ; indicating the reset is complete. ; ; X-14 DOF Dave Fairbanks 8-SEP-1994 ; - Do not set the SPDT online bit SPDT$M_STS_ONLINE at the ; end of routine INIT_ADAPTER. Instead set it in routine ; PK$RESET_SCSI_BUS after the SCSI bus reset has occurred. ; - Correct some of the SCSI bus reset handling code. ; - Call routine RESET_SDTR_FLAG at the correct places. ; - Save R10 in routine RESET_SDTR_FLAG. It gets destroyed. ; - Don't disable autosense when resetting the SCSI bus. ; ; X-13 DOF Dave Fairbanks 6-SEP-1994 ; - Fix SCSI bus resets. Reorganize code to perform a ; SCSI bus reset at adapter initialization time. ; - Add header SIZE/TYPE/SUBTYPE fields for Queue Buffer ; memory. ; ; X-12 DOF Dave Fairbanks 17-AUG-1994 ; - Fix Spinlock release error crash. This involves ; removing all references to KPB$PS_SCSI_PTR2. Use the ; SCDRP$V_DSF_NOWAIT bit in SCDRP$IS_DIPL_SCSI_FLAGS ; instead. ; - In the TAG_QUEUE_ACTION_TABLE make untagged IO requests ; (SCDRP$K_QCHAR_NOT_QUEUED) and ACA IO requests ; (SCDRP$K_QCHAR_ACA) into tagged ordered IO requests. ; ; X-11 DOF Dave Fairbanks 12-AUG-1994 ; - Fix Queue Carrier/Queue Buffer corruption problem. ; - Dispatch on the Carrier function code for response handling ; in routine PKS$FORK. Also add Carrier function code constants. ; - Add STDT version number (STDT$C_VERSION) in ; SPDT$L_VERSION_CHECK in routine REINIT_PORT. ; - Add routine PK$INIT_STDT to complete STDT Initialization. ; - Add synchronous negotiation routine PK$NEGOTIATE_SYNCH. ; Also fix synchronous negotiation setup in PK$SEND_COMMAND. ; - Do not return the autosense data length in cell ; SCDRP$IS_SENSE_BUFFER_LEN during response processing. ; - Fix register usage in routine SETUP_DAFQ. R6 was getting ; overwritten. ; - Fix ACCVIO in PKS_REGDUMP for STDT and KPB addresses. ; - Add code for adapter initiated SCSI bus resets. ; - Miscellaneous comment cleanup ; ; X-10 DOF Dave Fairbanks 13-JUL-1994 ; - Update to base level 4. This includes: ; - In routine PK$SEND_COMMAND return a valid status in R0. ; - Delete all references to SCDRP$IS_REQUEST_STATUS. ; - Move autosense handling from PK$CMD_WAIT_COMPLETION to ; PKS$FORK. Check for valid autosense by testing bit 7 ; of the returned CAM status. Add Carrier status field ; definitions. ; ; X-9 DOF Dave Fairbanks 12-JUL-1994 ; - In routine PK$SEND_COMMAND when setting up the ; queuing characteristics, change the way ; QBUF$IB_TAG_QUEUE_ACTION and QBUF$IB_CAM_FLAGS_1 ; are setup by using a table. ; ; X-8 DOF Dave Fairbanks 20-JUN-1994 ; - Make WAIT_FOR_RESPONSE macro test correct bit. ; (Bit 0 in CAR$PQ_NEXT_PTR(R6)) ; ; X-7 DOF Dave Fairbanks 20-JUN-1994 ; - Move DEVICEUNLOCK from PKS_STALL to PK$SEND_COMMAND. ; - Set up SCDRP$IS_REQUEST_STATUS for error returns. ; - Return SS$_NORMAL from PK$SEND_COMMAND. ; ; X-6 DOF Dave Fairbanks 17-JUN-1994 ; - Add code for Tagged Command Queuing ; ; X-5 DOF Dave Fairbanks 16-JUN-1994 ; - Add code for autosense. ; ; X-4 DOF/LDD Dave Fairbanks/Dan Duval 3-JUN-1994 ; - Modify to handle KZPSA adapter. ; ; X-3 DOF Dave Fairbanks 25-MAY-1994 ; - Correct SCSI bus reset handling. ; - Modify code for SCSI2 non-autosense, non-TCQ. ; ; X-2 DOF Dave Fairbanks 18-MAY-1994 ; - Add code to handle unaligned buffers. This includes ; code necessary to map/unmap user buffers, and copy the ; unaligned data to/from the user buffer at the appropriate ; time. ; - Add REQPORT/RELPORT macros. ; - Use macro BUILD_BSD to create all BSDs. ; - Fix bug when resuming stalled processes. We were ; resuming the wrong process. ; - Delete SPO$L_xxx register offset definitions. These ; were never used. ; - Miscellaneous comment corrections. ; ; X-1 DOF Dave Fairbanks 18-JAN-1994 ; - Replace TIMEDWAITs with PK_TQE_WAITs. ; - Remove unnecessary DEVICELOCK/DEVICEUNLOCK in SETUP_ADFQ. ; - After an I/O has been completed, if a BSM was allocated, ; deallocate the BSM in routine PKS$UNMAP_BUFFER. ; ; X-0 DOF Dave Fairbanks December-1993 ; Initial version. ; .PAGE .SBTTL Include Files ; ; Macro library calls ; $ADPDEF ; ADP offsets $BUSDEF ; Bus-type constants $BUSARRAYDEF ; ADP bus-array definitions $CRAMDEF ; I/O Mailbox definitions $CRBDEF ; CRB offsets $CRCTXDEF ; CRCTX offsets $DCDEF ; Device types $DDBDEF ; DDB offsets $DEVDEF ; Device types $DPTDEF ; DPT offsets $DYNDEF ; Dynamic data structure types $EMBDEF ; EMB offsets $FKBDEF ; Fork block offsets $IDBDEF ; IDB offsets $IODEF ; I/O function codes $IOCDEF ; I/O definitions $IOHANDLEDEF ; I/O handle offsets $IPLDEF ; Interrupt priority level usage $IRPDEF ; IRP offsets $KPBDEF ; KPB offsets $MSGDEF ; Operator message types $PDSCDEF ; PDSC definitions $PFNDEF ; PFN Data base definitions $PKDEF ; SCSI Port Definitions $PTEDEF ; Page table entry bits & fields $SCDRPDEF ; SCSI CDRP definitions $SCDTDEF ; SCSI Connection Descriptor Definitions $SCSIDEF ; SCSI2 definitions $SPDTDEF ; SCSI Port Descriptor Definitions $SPLCODDEF ; Spinlock code definitions $SSDEF ; QIO status return codes $STDTDEF ; SCSI Target Descriptor definitions $TQEDEF ; Timer Queue Entry Definitions $UCBDEF ; UCB definitions $VADEF ; Virtual address fields $VECDEF ; Interrupt dispatch vector offsets $VLEDEF ; Vector List Extension definitions .PAGE .SBTTL Local Definitions for PKSDRIVER ; ; DEBUG ONLY -- take this flag out once testing is completed. ; ;;PKSDEBUG = 1 ; Conditional compile flag for debug ;;PKSHISTORY = 1 ; ; Port specific constants ; NUM_CHANNELS = 1 ; Number of SCSI channels supported PKS_NORM_CMDSIZE = 20 ; For SNDSCSI commands, the command ; buffer requested by a class driver ; should be max. of 20 bytes ; (12 bytes of CDB + 8 bytes class ; driver overhead) PKS_MAXBCNT = 65535 ; Max data byte count supported by ; port driver PKS$M_BYTE_IN_PAGE = <^X1FFF> ; Byte-within-page bit mask for ; 8K-byte page PKS$S_BYTE_IN_PG = 13 ; # of bits to shift for 8K-byte page PKS_ERROR_REV = 2 ; Error log revision number PKS_ERROR_LEN = 256 ; Maximum length of error log packet PKS_INIT_TIMEOUT = 300 ; Port init timeout value = 5 minutes PKS_ENAB_TIMEOUT = 120 ; Port re-enable timeout value = 2 min PKS_XPD1_TIMEOUT = 50 ; Timeout value (1/2 second) in 10 ms ; units for reading XPD1 register PKS_BUSRESET = 1 ; Flag saying bus reset was performed AB_PAGES = 1 ; Number of pages needed for AB NUM_QUEUES = 4 ; Number of queues in AB NUM_QUEUE_BUFFERS = 256 ; Number of Queue Buffers QUEUE_BUFFER_SIZE = 512 ; Size of a Queue Buffer HEADER_SIZE = 16 ; Number of bytes for data structure QUEUE_BUFFER_POOL_SIZE = ; header SIZE/TYPE/SUBTYPE NUM_ADFQ_ENTRIES = 8 ; Number of free entries to insert ; onto the ADFQ ; ; Target mode specific constants ; NUM_ATIO_CCBS = 4 ; Number of Accept Target IO CCBs NUM_TARGET_LUNS = 1 ; Number of target mode LUNs active LUN_NOT_SUPPORTED = ^X7F ; Inquiry data for LUN not supported ; ; CDB offsets ; CDB$IB_OPCODE = 0 ; Operation code CDB$IB_LUN = 1 ; LUN byte CDB$IB_ALLOC_LENGTH = 4 ; Allocation length byte ; ; For permanent KPBs, we use the standard I/O KPB flags except that ; KP$M_DEALLOC_AT_END is not set. We don't want the permanent KPBs ; deallocated after each usage. ; PKS_KP_REGMSK = ^M ; Register save mask for kernel process routine KPB_FLAGS = KP$M_IO & ^C DEVICEIPL = 21 ; Interrupt IPL for this driver READCNTR_INTERVAL = 60 ; Interval between two RDCNT commands ; issued to the port, currently set to ; 60 seconds TQE_TIMER_EXPIRE = 10*100000*100 ; 10 Seconds timeout for TQE IO_TIMEOUT = 180 ; I/O thread wait timeout value in ; seconds. Currently 3 minutes ATIO_COMMAND_TIMEOUT = 5 ; Command timeout for Accept Target IO ; ; Alignment constants used for calling EXE$ALONONPAGED_ALN ; ALN_LONG = 2 ; Longword aligned ALN_QUAD = 3 ; Quadword aligned ALN_DQUAD = 4 ; Double quadword aligned ALN_HEXWORD = 5 ; Hexaword aligned (32-byte) ALN_DHEXWORD = 6 ; Double hexaword aligned ALN_PORTPAGE = 13 ; 8KB aligned ; ; Alignment constants for testing buffer alignment ; BYTE_ALIGNED = 1 ; Byte aligned LW_ALIGNED = 3 ; Longword aligned QUAD_ALIGNED = 7 ; Quadword aligned ; ; SCSI Command codes referred to in this driver ; SCSI$K_REQ_SENSE = ^X3 ; Request Sense command code SCSI$K_INQUIRY = ^X12 ; INQUIRY command code SCSI$K_MSGCMD = 0 ; Command complete message code ; ; Error subtype codes used for error logging. ; SPO$C_ADSE = 1 SPO$C_AMSE = 2 SPO$C_AAC = 3 SPO$C_AME = 4 SPO$C_UNKNOWN_ERR = -1 .PAGE .SBTTL Unit Control Block Definitions .SYMBOL_ALIGNMENT QUAD $DEFINI UCB,GLOBAL . = UCB$K_PK_LENGTH .IF DEFINED PKSDEBUG $DEF UCB$IS_TOP_OF_RING .BLKL 1 $DEF UCB$IS_RING_POINTER .BLKL 1 $DEF UCB$IS_RING_COUNTER .BLKL 1 .ENDC UCB$K_PKS_LENGTH = . $DEFEND UCB .SYMBOL_ALIGNMENT NONE .PAGE .SBTTL SCDRP Definitions ; ; Define SCSI port specific field in SCDRP using the existing field. ; $DEFINI SCDRP,GLOBAL SCDRP$PS_QBUF = SCDRP$L_SAVE_DATA_CNT ; Address of the Q_Buffer structure ; associated with this I/O request $DEFEND SCDRP .PAGE .SBTTL SPDT Definitions ; ; Define PKS specific extension to the SPDT offsets. ; $DEFINI SPDT,GLOBAL,SPDT$C_PKSLENGTH . = .+<4-<.&3>> ; Make sure it is longword aligned here $DEF SPDT$IS_CHANSTATE .BLKL 1 ; Channel specific state transition code $VIELD SPDT,0,<- ; Bits used with port status ,- ; Channel in UNINitialized state ,- ; Channel init, adapter disabled ,- ; Channel init, adapter enabled ,- ; Reset detected on SCSI bus > ; Filler $DEF SPDT$IS_PORTSTS .BLKL 1 ; Port status $VIELD SPDT,0,<- ; Bits used with port status ,- ; Port resource structures allocated ,- ; Reinit the port only ,- ; Reinitialize the adapter ,- ; Need to log an error ,- ; Target mode supported > ; Filler $DEF SPDT$IS_CHAN_DIS_STS .BLKL 1 ; Channel disabled status SPDT$C_BUS_RESET = 2 ; Bus reset detected on SCSI bus SPDT$C_RESET_REQ_DENIED = 3 ; Host denied adapter reset request SPDT$C_CHAN_HW_ERROR = 5 ; Channel hardware error $DEF SPDT$PS_DACQ_T .BLKL 1 ; Driver-Adapter Command queue tail ptr ; ; Queue pointers. ; $DEF SPDT$PS_QCMDFL .BLKL 1 ; Forward link of currently outstanding ; command Q_Buffers $DEF SPDT$PS_QCMDBL .BLKL 1 ; Backward link of currently ; outstanding command Q_Buffers $DEF SPDT$PS_RSPFL .BLKL 1 ; Forward link of response Q_Buffers to ; be processed $DEF SPDT$PS_RSPBL .BLKL 1 ; Backward link of response Q_Buffers ; to be processed $DEF SPDT$PS_DAFQFL .BLKL 1 ; Forward link of free Q_Buffers in the ; Driver-Adapter Free Queue $DEF SPDT$PS_DAFQBL .BLKL 1 ; Backward link of free Q_Buffers in the ; Driver-Adapter Free Queue $DEF SPDT$PS_WCMDFL .BLKL 1 ; SCSI command completion wait queue ; forward link $DEF SPDT$PS_WCMDBL .BLKL 1 ; SCSI command completion wait queue ; backward link $DEF SPDT$PS_PCMDFL .BLKL 1 ; Port command completion wait queue ; forward link $DEF SPDT$PS_PCMDBL .BLKL 1 ; Port command completion wait queue ; backward link $DEF SPDT$PS_CARFL .BLKL 1 ; Carrier free list forward link $DEF SPDT$PS_CARBL .BLKL 1 ; Carrier free list backward link $DEF SPDT$PS_CARWQFL .BLKL 1 ; Carrier wait queue forward link $DEF SPDT$PS_CARWQBL .BLKL 1 ; Carrier wait queue backward link $DEF SPDT$PS_AB .BLKL 1 ; Pointer to Adapter Block $DEF SPDT$PS_RD_CTR_Q_BUFFER .BLKL 1 ; Pointer to static read counter ; Q_Buffer ; ; CRAM pointers for SIMport registers. ; START_OF_SPDT_CRAMS = . $DEF SPDT$PS_CRAM_HW_REV .BLKL 1 ; Hardware Revision Register (Read) $DEF SPDT$PS_CRAM_AMCSR .BLKL 1 ; Adapter Maint Ctl and Sts Reg (Read) $DEF SPDT$PS_CRAM_ABBR .BLKL 1 ; Adapter Block Base Register (Write) $DEF SPDT$PS_CRAM_DACQIR .BLKL 1 ; Driver Adapter Command Queue ; Insertion Register (WRITE) $DEF SPDT$PS_CRAM_DAFQIR .BLKL 1 ; Driver Adapter Free Queue Insertion ; Register (WRITE) $DEF SPDT$PS_CRAM_ASR .BLKL 1 ; Adapter Status Register (READ) $DEF SPDT$PS_CRAM_AFAR .BLKL 1 ; Adapter Failing Addr Register (READ) $DEF SPDT$PS_CRAM_AFPR .BLKL 1 ; Adapter Failing Param Register (READ) $DEF SPDT$PS_CRAM_ERRLOG1 .BLKL 1 ; Error Log Data Register 1 (READ) $DEF SPDT$PS_CRAM_ERRLOG2 .BLKL 1 ; Error Log Data Register 2 (READ) $DEF SPDT$PS_CRAM_ERRLOG3 .BLKL 1 ; Error Log Data Register 3 (READ) $DEF SPDT$PS_CRAM_ERRLOG4 .BLKL 1 ; Error Log Data Register 4 (READ) $DEF SPDT$PS_CRAM_ERRLOG5 .BLKL 1 ; Error Log Data Register 5 (READ) $DEF SPDT$PS_CRAM_ERRLOG6 .BLKL 1 ; Error Log Data Register 6 (READ) $DEF SPDT$PS_CRAM_ERRLOG7 .BLKL 1 ; Error Log Data Register 7 (READ) $DEF SPDT$PS_CRAM_ERRLOG8 .BLKL 1 ; Error Log Data Register 8 (READ) $DEF SPDT$PS_CRAM_ERRLOG9 .BLKL 1 ; Error Log Data Register 9 (READ) $DEF SPDT$PS_CRAM_ERRLOGA .BLKL 1 ; Error Log Data Register A (READ) $DEF SPDT$PS_CRAM_CLEAR_INT .BLKL 1 ; Clear interrupt (WRITE) $DEF SPDT$PS_CRAM_RESET .BLKL 1 ; Module RESET Register (WRITE) CRAM_COUNT = <. - START_OF_SPDT_CRAMS>/4 ; Number of allocated CRAMs ; ; Saved SIMport registers. ; START_OF_SAVE_REGS = . $DEF SPDT$IS_SAVE_HW_REV .BLKL 1 ; Saved HW revision $DEF SPDT$IS_SAVE_AMCSR .BLKL 1 ; Saved AMCSR register $DEF SPDT$IS_SAVE_ASR .BLKL 1 ; Saved ASR register $DEF SPDT$IS_SAVE_AFAR .BLKL 1 ; Saved AFAR register $DEF SPDT$IS_SAVE_AFPR .BLKL 1 ; Saved AFPR register $DEF SPDT$IS_SAVE_ERRLOG1 .BLKL 1 ; Saved error Log Data Register 1 $DEF SPDT$IS_SAVE_ERRLOG2 .BLKL 1 ; Saved error Log Data Register 2 $DEF SPDT$IS_SAVE_ERRLOG3 .BLKL 1 ; Saved error Log Data Register 3 $DEF SPDT$IS_SAVE_ERRLOG4 .BLKL 1 ; Saved error Log Data Register 4 $DEF SPDT$IS_SAVE_ERRLOG5 .BLKL 1 ; Saved error Log Data Register 5 $DEF SPDT$IS_SAVE_ERRLOG6 .BLKL 1 ; Saved error Log Data Register 6 $DEF SPDT$IS_SAVE_ERRLOG7 .BLKL 1 ; Saved error Log Data Register 7 $DEF SPDT$IS_SAVE_ERRLOG8 .BLKL 1 ; Saved error Log Data Register 8 $DEF SPDT$IS_SAVE_ERRLOG9 .BLKL 1 ; Saved error Log Data Register 9 $DEF SPDT$IS_SAVE_ERRLOGA .BLKL 1 ; Saved error Log Data Register A SAVE_REG_COUNT = <. - START_OF_SAVE_REGS>/4 ; Number of saved registers $DEF SPDT$PS_RDCNT_KPB .BLKL 1 ; KPB used to issue a RDCNT command $DEF SPDT$PS_COUNTERS .BLKL 1 ; Pointer to the port counter block ; area in the Adapter Block free ; memory space $DEF SPDT$IS_OVERFLOW .BLKL 1 ; Counter overflow flag $VIELD SPDT,0,<- ; Bits used with counter overflow flags ,- ; Port counters overflowed ,- ; Filler > $DEF SPDT$IS_ENABTMR .BLKL 1 ; Port re-enable timeout value $DEF SPDT$PS_KPB .BLKL 1 ; Adapter-wide KPB address $DEF SPDT$PS_INQUIRY_DATA .BLKL 1 ; Pointer to target mode inquiry data $DEF SPDT$PS_SENSE_DATA .BLKL 1 ; Pointer to target mode sense data $DEF SPDT$PS_ATIO_FL .BLKL 1 ; Accept target IO CCB forward link $DEF SPDT$IS_TARGET_EVENT .BLKL 1 ; Target Event data $DEF SPDT$IB_TARGET_FLAGS .BLKB 4 ; Target Event flags $DEF SPDT$IS_NUM_ACCEPTS .BLKL 1 ; Number Target Accepts $DEF SPDT$IS_NUM_CONTINUES .BLKL 1 ; Number Target Continues $DEF SPDT$IS_NUM_TARGET_EVENTS .BLKL 1 ; Number Target Events $DEF SPDT$IS_ATIO_LIST_HEAD .BLKL 1 ; Accept list head $DEF SPDT$IS_ATIO_LIST_TAIL .BLKL 1 ; and tail pointers ; ; Make sure following fork block is quadword aligned ; . = <<.+7>&^C7> ; Make sure we are quadword aligned $DEF SPDT$PS_BUSRESET_FKBLK .BLKB 32 ; Fork block used for getting KPB ; for BUS RESET SPDT$C_BUSRESET_FKBLK = 32 $DEF SPDT$PS_BUSRESET_KPB .BLKL 1 ; KPB used for SCSI BUS RESET operation ; which is triggered by the port driver $DEF SPDT$IS_CHANNEL .BLKL 1 ; Channel number of this port $VIELD SPDT,0,<- ; Define bit used with SPDT$IS_CHANNEL ,- ; Filler ,- ; Clean buffers after powerfail/crash ,- ; Clean channel resources after RESET > $DEF SPDT$IB_XFER_ALIGN .BLKB 1 ; Transfer alignment capabilities $DEF SPDT$IB_FILL .BLKB 3 ; Filler for alignment ; ; The following locations hold the path inquiry data that was received ; during adapter (re)initialization. ; SPDT$C_START_PI_DATA = . $DEF SPDT$IB_PI_VERSION .BLKB 1 ; Version SPDT$IB_PI_DATA = SPDT$IB_PI_VERSION $DEF SPDT$IB_PI_SCSI_CAPABILITY .BLKB 1 ; SCSI capabilities $VIELD SPDT,0,<- ,- ; Soft reset ,- ; Tagged command queueing ,- ; Reserved ,- ; Linked commands ,- ; Synchronous transfers ,- ; Wide bus 16 ,- ; Wide bus 32 ,- ; Modify data pointers > $DEF SPDT$IB_PI_TARGET_MODE .BLKB 1 ; Target mode support $VIELD SPDT,0,<- ,- ; Reserved ,- ; Phase cognizant mode ,- ; Processor mode > $DEF SPDT$IB_PI_MISCELLANEOUS .BLKB 1 ; Miscellaneous $VIELD SPDT,0,<- ,- ; Reserved ,- ; PI data not kept by XPT ,- ; Removables included in scan ,- ; Scan direction > $DEF SPDT$IW_PI_ENGINE_COUNT .BLKB 2 ; Engine count $DEF SPDT$IB_PI_VENDOR_UNIQUE .BLKB 14 ; Vendor unique $DEF SPDT$IS_PI_PDA_SIZE .BLKL 1 ; Size of private data area $DEF SPDT$IS_PI_ASYNC_EVENT .BLKL 1 ; Asynchronous event ; capabilities $VIELD SPDT,0,<- ,- ; Unsolicited SCSI bus reset ,- ; Unsolicited reselection ,- ; Reserved ,- ; SCSI AEN ,- ; BDR sent to target ,- ; SIM module registered ,- ; SIM module de-registered ,- ; New devices found on rescan ,- ; Reserved ,- ; Vendor unique > $DEF SPDT$IB_PI_PATH_ID .BLKB 1 ; Path id assigned $DEF SPDT$IB_PI_SCSI_DEV_ID .BLKB 1 ; SCSI device id (of initiator) $DEF SPDT$IW_PI_RESERVED .BLKB 2 ; Reserved $DEF SPDT$IB_PI_VENDOR_ID_SIM .BLKB 16 ; Vendor id of SIM supplier $DEF SPDT$IB_PI_VENDOR_ID_HBA .BLKB 16 ; Vendor id of HBA supplier $DEF SPDT$IS_PI_OSD .BLKL 1 ; OSD specific use SPDT$C_PI_DATA_SIZE= <. - SPDT$C_START_PI_DATA> $DEF SPDT$IS_CAR_ADDR .BLKL 1 ; Carrier address $DEF SPDT$IS_STOPPER_ADDR .BLKL 1 ; Stopper address $DEF SPDT$IS_QBUF_ADDR .BLKL 1 ; Queue Buffer address $DEF SPDT$IS_CAM_STS .BLKL 1 ; Returned CAM status $DEF SPDT$IS_DEV_CONNECT_ID .BLKL 1 ; Saved device connection id . = .+<8-<.&7>> ; Make sure it is lw aligned $DEF SPDT$PQ_IOHANDLE_REG .BLKQ 1 ; IOHANDLE for SIMport regs. $DEF SPDT$PQ_IOHANDLE_CLEAR_INT .BLKQ 1 ; IOHANDLE for CLEAR_INT $DEF SPDT$PQ_IOHANDLE_RESET .BLKQ 1 ; IOHANDLE for RESET $DEF SPDT$IQ_PHYS_ADDR .BLKQ 1 ; Bus phys offset for MAP_IO $DEF SPDT$IS_ADAPTER_TYPE .BLKL 1 ; Adapter type SPDT$C_TURBOCHANNEL = 0 ; Turbochannel SPDT$C_PCI = 1 ; PCI $DEF SPDT$IQ_DMA_SIZE .BLKQ 1 ; Direct-DMA window size. .IF DEFINED PKSHISTORY $DEF SPDT$IS_CARRIER_PTR .BLKL 1 $DEF SPDT$IS_BUFFER_PTR .BLKL 1 $DEF SPDT$IS_CARRIER_HIST .BLKL 50 ; History of carrier addresses SPDT$C_CHIST_END = . $DEF SPDT$IS_BUFFER_HIST .BLKL 100 ; History of buffer indexes SPDT$C_BHIST_END = . .ENDC .SYMBOL_ALIGNMENT QUAD SPDT$C_PKSLENGTH = . $DEFEND SPDT .PAGE .SBTTL Adapter Block Definitions ; ; Adapter Block definition ; ; Only the first 80 bytes are visible to the SIMport adapter. The remaining ; bytes are reserved for PKSDRIVERs private use. ; ; NOTE: All pointers are quadword-long addresses in SIMport, and hence ; they are defined as PQ type. ; .SYMBOL_ALIGNMENT QUAD $DEFINI AB,GLOBAL $DEF AB$IW_QB_SIZE .BLKW 1 ; Size (bytes) of a queue buffer AB$C_QB_SIZE = ^XC0 ; Max. size of the adapter ; visible Queue Buffer area $DEF AB$IB_CCB_PTR_SIZE .BLKB 1 ; Bytes allocated for CCB ptr AB$C_CCB_PTR_SIZE = 8 ; Size of CCB pointer $DEF AB$IB_SBZ .BLKB 13 ; Should be zero $DEF AB$PQ_DACQ_HEAD .BLKQ 1 ; DACQ head pointer (physical) $DEF AB$PQ_DACQ_TAIL .BLKQ 1 ; DACQ tail pointer (virtual) $DEF AB$PQ_ADRQ_HEAD .BLKQ 1 ; ADRQ head pointer (virtual) $DEF AB$PQ_ADRQ_TAIL .BLKQ 1 ; ADRQ tail pointer (physical) $DEF AB$PQ_DAFQ_HEAD .BLKQ 1 ; DAFQ head pointer (physical) $DEF AB$PQ_DAFQ_TAIL .BLKQ 1 ; DAFQ tail pointer (virtual) $DEF AB$PQ_ADFQ_HEAD .BLKQ 1 ; ADFQ head pointer (virtual) $DEF AB$PQ_ADFQ_TAIL .BLKQ 1 ; ADFQ tail pointer (physical) ; ; The following additional fields are used by PKSDRIVER for its private use. ; They are not visible to the adapter. ; $DEF AB$IQ_FILL .BLKQ 4 ; Separate port driver specific ; section $DEF AB$PQ_PA .BLKQ 1 ; Physical address of this AB $DEF AB$PS_CAR_FLINK .BLKL 1 ; Forward link of Carriers $DEF AB$PS_CAR_BLINK .BLKL 1 ; Backward link of Carriers $DEF AB$IS_INT_HOLDOFF .BLKL 1 ; Interrupt Holdoff Timer field $DEF AB$IS_RSP_FKLCK .BLKL 1 ; Response interrupt fork ; block lock field $VIELD AB,0,<- ; Interlock bit ,- > $DEF AB$IS_ADP_FKLCK .BLKL 1 ; Adapter error fork block ; lock field $VIELD AB,0,<- ; Interlock bit ,- > ; ; Make sure the following fork blocks are quadword aligned. ; $DEF AB$PS_RSP_FKBLK .BLKB 32 ; Fork block for response ; interrupts $DEF AB$PS_ADP_FKBLK .BLKB 32 ; Fork block used for Adapter ; error interrupts ; ; The following block of memory is used for keeping port counters. ; The port counters read from the CNTRD response are added to the current ; totals kept in this block. When the port is re-initialized, this ; block is cleared to reset the counters. ; AB$C_NUM_PORT_CNTRS = 28 ; Number of port counters AB$C_PORT_CNTRS_SIZE = ; Size (in bytes) of ; port counter block $DEF AB$PS_COUNTERS .BLKL AB$C_NUM_PORT_CNTRS ; ; CRCTX pointer for mapping the AB, if necessary. ; $DEF AB$PS_CRCTX .BLKL 1 $DEF AB$IW_SIZE .BLKW 1 ; RCL003 AB$C_SIZE = . ; Adapter Block size $DEFEND AB .SYMBOL_ALIGNMENT NONE .PAGE .SBTTL Carrier Definitions ; ; Carrier structure definition ; ; PKSDRIVER creates a quadword aligned Queue Carrier, located in host memory, ; for each command to form a one way linked list representing a queue. Each ; Queue Carrier is 72 bytes long. The first 32 bytes are for PKSDRIVER internal ; use only. The adapter-visible part is a 32-byte aligned, 40 byte long ; structure. ; .SYMBOL_ALIGNMENT QUAD $DEFINI CAR,GLOBAL $DEF CAR$PS_FLINK .BLKL 1 ; Forward link of Carrier $DEF CAR$PS_BLINK .BLKL 1 ; Backward link of Carrier $DEF CAR$IW_SIZE .BLKW 1 ; Structure size in bytes $DEF CAR$IB_TYPE .BLKB 1 ; Structure type $DEF CAR$IB_SUBTYPE .BLKB 1 ; Structure subtype $DEF CAR$PS_CRCTX .BLKL 1 ; CRCTX to map this carrier $DEF CAR$PQ_PA .BLKQ 1 ; Physical address of this ; Carrier's adapter-visible ; section $DEF CAR$IQ_FILL2 .BLKQ 1 ; Filler for 32-byte alignment CAR$C_DRVLEN = . ; ; Adapter-visible part of Carrier begins here. ; $DEF CAR$PQ_NEXT_PTR .BLKQ 1 ; Physical address of the next ; Carrier in a port queue $DEF CAR$PQ_QBUF_PTR .BLKQ 1 ; Phys addr of the associated ; Q_Buffer when driver writes ; to this location, VA of the ; associated Q_Buffer when ; the adpater writes to this ; location $DEF CAR$PQ_QBUF_TOKEN .BLKQ 1 ; VA of the associated ; Q_Buffer header $DEF CAR$PQ_CAR_TOKEN .BLKQ 1 ; VA of this Carrier header $DEF CAR$IB_FUNC_CODE .BLKB 1 ; CAM or SIMport Function code ; ; CAM command function code definitions. ; CAR$C_NOP = ^X00 ; No operation CAR$C_EXEC_SCSI_IO = ^X01 ; Execute SCSI I/O CAR$C_PATH_INQUIRY = ^X03 ; Path inquiry CAR$C_REL_SIM_QUEUE = ^X04 ; Release SIM queue CAR$C_SET_ASYNC_CB = ^X05 ; Set asynch callback CAR$C_ABORT_SCSI_IO = ^X10 ; Abort specified SCSI I/O CCB CAR$C_RESET_SCSI_BUS = ^X11 ; Reset indicated SCSI channel CAR$C_RESET_SCSI_DEV = ^X12 ; Reset indicated SCSI device CAR$C_TERM_SCSI_IO = ^X13 ; Terminate specified SCSI CCB CAR$C_ENABLE_LUN = ^X30 ; Enable LUN for target mode CAR$C_ACCEPT_TARGET_IO = ^X32 ; Accept host target mode CCB CAR$C_CONT_TARGET_IO = ^X33 ; Continue host target mode CCB ; ; SIMport vendor unique command function code definitions. ; CAR$C_SET_ADAP_STATE = ^X80 ; Set adapter state CAR$C_SET_PARAM = ^X81 ; Set parameters CAR$C_SET_CHAN_STATE = ^X82 ; Set channel state CAR$C_SET_DEV_STATE = ^X83 ; Set device state CAR$C_VER_ADAP_SANITY = ^X84 ; Verify adapter sanity CAR$C_READ_CNTRS = ^X85 ; Read counters CAR$C_BSD_RESPONSE = ^X86 ; BSD response CAR$C_BUS_RESET_CMD = ^X87 ; Bus reset command CAR$C_UNSOL_RESEL = ^X88 ; Unsolicited reselection CAR$C_CHAN_DISABLED = ^X89 ; Channel disabled CAR$C_DEV_DISABLED = ^X8A ; Device disabled CAR$C_TARGET_EVENT_ACK = ^X8B ; Target event unsolicited msg CAR$C_SET_TARGET_STATE = ^X8C ; Marker for target event ; rundown CAR$C_ADAP_RECOV_COMP = ^X8E ; Adapter recovery completed ; ; SIMport vendor unique response code definitions. ; CAR$C_ADAP_STATE_SET = ^X80 ; Adapter state set response CAR$C_PARAMETER_SET = ^X81 ; Parameter set response CAR$C_CHAN_STATE_SET = ^X82 ; Channel state set response CAR$C_DEV_STATE_SET = ^X83 ; Device state set response CAR$C_ADAP_SANITY_VER = ^X84 ; Adapter sanity verified rsp CAR$C_CNTRS_READ = ^X85 ; Counters read response CAR$C_BSD_REQUEST = ^X86 ; BSD request response CAR$C_BUS_RESET_REQ = ^X87 ; Bus reset request response CAR$C_TARGET_EVENT = ^X8B ; Target event unsolicited msg CAR$C_TARGET_STATE_SET = ^X8C ; Marker for target event ; rundown $DEF CAR$IB_STATUS .BLKB 1 ; Status of CAM or SIMport cmd $VIELD CAR,0,<- ; Carrier status definitions ,- ; CAM status ,- ; SIM queue is frozen ,- ; Autosense data is valid > CAR$C_REQ_IN_PROGRESS = ^X00 ; Request in progress CAR$C_REQ_COMP_NO_ERROR = ^X01 ; Request completed wo error CAR$C_HOST_ABORTED_REQ = ^X02 ; Request aborted by host CAR$C_UNABLE_ABORT_REQ = ^X03 ; Unable to abort request CAR$C_REQ_COMP_WITH_ERROR = ^X04 ; Request completed with error CAR$C_CAM_BUSY = ^X05 ; CAM busy CAR$C_INVALID_REQ = ^X06 ; Invalid request CAR$C_INVALID_PATH_ID = ^X07 ; Invalid path ID CAR$C_DEV_NOT_THERE = ^X08 ; SCSI device not installed CAR$C_UNABLE_TERM_IOP = ^X09 ; Unable to term IO process CAR$C_TARGET_SEL_TO = ^X0A ; Target selection timeout CAR$C_COMMAND_TO = ^X0B ; Command timeout CAR$C_MSG_REJECT_REC = ^X0D ; Message reject received CAR$C_BUS_RESET_TXRX = ^X0E ; SCSI bus reset sent/received CAR$C_PARITY_ERR = ^X0F ; Uncorrectable parity error CAR$C_REQ_SENSE_FAILED = ^X10 ; Autosense request sense failed CAR$C_NO_HBA = ^X11 ; No HBA detected CAR$C_DATA_OVER_UNDER_RUN = ^X12 ; Date overrun/underrun CAR$C_UNEXP_BUS_FREE = ^X13 ; Unexpected bus free CAR$C_BUS_PHASE_SEQ_FAIL = ^X14 ; Bus phase sequence failure CAR$C_INVAL_CCB_SIZE = ^X15 ; CCB length inadquate CAR$C_NO_REQ_CAPABILITY = ^X16 ; Cannot provide req capability CAR$C_DEVICE_RESET_TX = ^X17 ; Bus device reset sent CAR$C_TERM_IOP = ^X18 ; Terminate IO process CAR$C_UNRECOV_HBA_ERROR = ^X19 ; Unrecoverable HBA error CAR$C_SCSI_BUS_RESET_DENIED = ^X1A ; SCSI bus reset denied ; ; Target mode only status codes. ; CAR$C_INIT_DET_ERROR = ^X33 ; Initiator detected error CAR$C_RESOURCE_UNAVAIL = ^X34 ; Resource unavailable CAR$C_UNACKED_EVENT_BY_HOST = ^X35 ; Unacked event by host CAR$C_MESSAGE_REC = ^X36 ; Message received CAR$C_INVALID_CDB = ^X37 ; Invalid CDB CAR$C_INVALID_LUN = ^X38 ; Invalid LUN CAR$C_INVALID_TARGET_ID = ^X39 ; Invalid target ID CAR$C_FUNC_NOT_IMPLEMENTED = ^X3A ; Function not implemented CAR$C_NO_NEXUS = ^X3B ; Nexus not established CAR$C_INVALID_INIT_ID = ^X3C ; Invalid initiator ID CAR$C_SCSI_CDB_REC = ^X3D ; SCSI CDB received CAR$C_LUN_ALREADY_ENABLED = ^X3E ; LUN already enabled CAR$C_SCSI_BUS_BUSY = ^X3F ; SCSI bus busy $DEF CAR$IW_FLAGS .BLKW 1 ; Flags for queue insert ; priority and status return $VIELD CAR,0,<- ; Carrier flag bits definitions ,- ; SIM queue priority ,- ; Return status format ,- ; Unused (SBZ) > $DEF CAR$IS_CONNECT_ID .BLKL 1 ; CAM defined Connect id CAR$IB_RESERVED = CAR$IS_CONNECT_ID CAR$IB_PATH_ID = CAR$IB_RESERVED + 1 CAR$IB_TARGET_ID = CAR$IB_PATH_ID + 1 CAR$IB_LUN = CAR$IB_TARGET_ID + 1 CAR$C_LENGTH = . ; Length of Queue Carrier CAR$C_ADPLEN = . - CAR$C_DRVLEN ; Length of adapter-visible ; section. CAR$C_NUM_CARRIERS = 256 ; Number of Queue Carriers $DEFEND CAR .SYMBOL_ALIGNMENT NONE .PAGE .SBTTL Queue Buffer Definitions ; ; Queue Buffer structure definition ; ; PKSDRIVER returns a quadword aligned Queue Buffer to a class driver whenever ; a class driver invokes the SPI$ALLOC_CMD_BUFFER macro. Each Queue Buffer is ; 512 bytes long. The Queue Buffer is used as follows: ; ; 1.) The first 40 bytes contain a private area for explicit use by PKSDRIVER. ; This section is not visible to the adapter. ; ; 2.) The next 24 bytes contain the CDB, built by the class driver, that is ; passed to PKSDRIVER with the SPI$SEND call. This section is not visible ; to the adapter. ; ; 3.) The next 192 bytes are the adapter visible section. They contain the CCB, ; built by PKSDRIVER, that is passed to the adapter. The first 24 bytes in ; each CCB are the CAM Control Block common header. All CCBs have this same ; common header. Additional fields beyond the CCB header are command ; specific. The length of the CCB depends on the specific command. ; ; 4.) The remaining 256 bytes are the autosense buffer for returning autosense ; data. The autosense buffer is pointed to by the QBUF$PS_SEN_BSD field in ; the private data area of the Execute SCSI I/O command. ; .SYMBOL_ALIGNMENT QUAD $DEFINI QBUF,GLOBAL ; ; First 40 bytes are PKSDRIVER private area. This section is not visible to ; the adapter. ; $DEF QBUF$PS_FLINK .BLKL 1 ; Forward link of Q_Buffers ; in a queue QBUF$PS_INIT_CRCTX_PTR = QBUF$PS_FLINK ; Init CRCTX pointer $DEF QBUF$PS_BLINK .BLKL 1 ; Backward link of Q_Buffers ; in a queue $DEF QBUF$IW_SIZE .BLKW 1 ; Structure size $DEF QBUF$IB_TYPE .BLKB 1 ; Structure type $DEF QBUF$IB_SUBTYPE .BLKB 1 ; Structure subtype $DEF QBUF$PS_SCDT .BLKL 1 ; Corresponding SCDT addr for ; this Queue Buffer QBUF$PS_KPB = QBUF$PS_SCDT ; KPB address of stalled thread $DEF QBUF$PQ_PA_QBUF .BLKQ 1 ; Physical address of ; adapter-visible section $DEF QBUF$PS_CARRIER .BLKL 1 ; Corresponding carrier header ; address. Used when we need ; to do garbage collection on ; port crash $DEF QBUF$PS_SCDRP .BLKL 1 ; Address of SCDRP associated ; with this I/O request $DEF QBUF$PS_PADBLK .BLKL 1 ; Pad block address $DEF QBUF$IW_NUMSEG .BLKW 1 ; Data segments for this cmd $DEF QBUF$IW_PORT_STS .BLKW 1 ; Port status to be returned ; to class driver ; ; The next 24 bytes contain the CDB, built by the class driver, that is ; passed to PKSDRIVER with the SPI$SEND call. This section is not visible ; to the adapter. ; $DEF QBUF$IB_CMDBUF .BLKB 24 ; 24 bytes of command buffer ; area to be passed back to ; class driver QBUF$IS_STATUS = QBUF$IB_CMDBUF ; First longword is SCSI status QBUF$IS_CMDLEN = QBUF$IS_STATUS+4 ; Second longword is SCSI ; command length QBUF$IB_CMD = QBUF$IS_CMDLEN+4 ; The rest is for SCSI command QBUF$C_DRVLEN = . ; Port driver private header ; length ; ; Adapter-visible Queue Buffer section begins here. ; ; The first 24 bytes are the CAM Control Block common header. All CCBs ; have this same common header. ; $DEF QBUF$PQ_CCB_ADDR .BLKQ 1 ; CCB header address (PA) $DEF QBUF$IW_CCB_LENGTH .BLKW 1 ; Length (in bytes) of CCB $DEF QBUF$IB_FUNC_CODE .BLKB 1 ; CAM/SIMport function code ; ; CAM command function code definitions. ; QBUF$C_NOP = ^X00 ; No operation QBUF$C_EXEC_SCSI_IO = ^X01 ; Execute SCSI I/O QBUF$C_PATH_INQUIRY = ^X03 ; Path inquiry QBUF$C_REL_SIM_QUEUE = ^X04 ; Release SIM queue QBUF$C_SET_ASYNC_CB = ^X05 ; Set asynch callback QBUF$C_ABORT_SCSI_IO = ^X10 ; Abort specified SCSI I/O CCB QBUF$C_RESET_SCSI_BUS = ^X11 ; Reset indicated SCSI channel QBUF$C_RESET_SCSI_DEV = ^X12 ; Reset indicated SCSI device QBUF$C_TERM_SCSI_IO = ^X13 ; Terminate specified SCSI CCB QBUF$C_ENABLE_LUN = ^X30 ; Enable LUN for target mode QBUF$C_ACCEPT_TARGET_IO = ^X32 ; Accept host target IO QBUF$C_CONT_TARGET_IO = ^X33 ; Continue host target IO ; ; SIMport vendor unique command function code definitions. ; QBUF$C_SET_ADAP_STATE = ^X80 ; Set adapter state QBUF$C_SET_PARAM = ^X81 ; Set parameters QBUF$C_SET_CHAN_STATE = ^X82 ; Set channel state QBUF$C_SET_DEV_STATE = ^X83 ; Set device state QBUF$C_VER_ADAP_SANITY = ^X84 ; Verify adapter sanity QBUF$C_READ_CNTRS = ^X85 ; Read counters QBUF$C_BSD_RESPONSE = ^X86 ; BSD response QBUF$C_BUS_RESET_CMD = ^X87 ; Bus reset command QBUF$C_UNSOL_RESEL = ^X88 ; Unsolicited reselection QBUF$C_CHAN_DISABLED = ^X89 ; Channel disabled QBUF$C_DEV_DISABLED = ^X8A ; Device disabled QBUF$C_TARGET_EVENT_ACK = ^X8B ; Target event acknowledge msg QBUF$C_SET_TARGET_STATE = ^X8C ; Set Target state ; ; SIMport vendor unique response code definitions. ; QBUF$C_ADAP_STATE_SET = ^X80 ; Adapter state set response QBUF$C_PARAM_SET = ^X81 ; Parameter set response QBUF$C_CHAN_STATE_SET = ^X82 ; Channel state set response QBUF$C_DEV_STATE_SET = ^X83 ; Device state set response QBUF$C_ADAP_SANITY_VER = ^X84 ; Adapter sanity verified rsp QBUF$C_CNTRS_READ = ^X85 ; Counters read response QBUF$C_BSD_REQUEST = ^X86 ; BSD request response QBUF$C_BUS_RESET_REQ = ^X87 ; Bus reset request response $DEF QBUF$IB_CAM_STS .BLKB 1 ; CAM status returned from SIM $DEF QBUF$IS_CONNECT_ID .BLKL 1 ; Connection id QBUF$IB_RSVD = QBUF$IS_CONNECT_ID ; Reserved field QBUF$IB_PATH_ID = QBUF$IB_RSVD+1 ; Path id QBUF$IB_TARGET_ID = QBUF$IB_PATH_ID+1 ; Target id QBUF$IB_LUN = QBUF$IB_TARGET_ID+1 ; LUN $DEF QBUF$IS_CAM_FLAGS .BLKL 1 ; CAM flags QBUF$IB_CAM_FLAGS_1 = QBUF$IS_CAM_FLAGS $VIELD QBUF,0,<- ; Define bit used with CAM flags 1 ,- ; CDB is a pointer ,- ; Tagged queue action enable ,- ; Linked CDB ,- ; Disable callback on completion ,- ; Scatter/gather ,- ; Disable autosense ,- ; Data transfer direction > QBUF$M_NO_DATA_TX = ^XC0 ; No data to transfer QBUF$M_WRITE = ^X80 ; Write data (from initiator to target) QBUF$M_READ = ^X40 ; Read data (from target to initiator) QBUF$IB_CAM_FLAGS_2 = QBUF$IB_CAM_FLAGS_1+1 $VIELD QBUF,0,<- ; Define bit used with CAM flags 2 ,- ; Reserved ,- ; Engine Syncronize ,- ; SIM queue freeze disable ,- ; SIM queue freeze ,- ; SIM queue priority ,- ; Synchronous transfer disable ,- ; Synchronous transfer initiate ,- ; Disable disconnect > QBUF$IB_CAM_FLAGS_3 = QBUF$IB_CAM_FLAGS_2+1 QBUF$IB_CAM_FLAGS_4 = QBUF$IB_CAM_FLAGS_3+1 ; ; CAM flag bits defined for Host Target mode. ; $VIELD QBUF,0,<- ; Define bit used with CAM flags 2 ,- ; Reserved ,- ; Target mode 0 = Host 1 = Ph cog ,- ; Reserved ,- ; Terminate IO process supported ,- ; Disconnect for each CCB for LUN ,- ; Send status byte after data phase > $DEF QBUF$IS_PADDING .BLKL 1 ; Padding for memory alignment QBUF$C_COMMON_HDR_SIZE = . - QBUF$C_DRVLEN ; Common header length QBUF$C_END_OF_COMMON_HDR = . ; ; CAM Control Block common header ends here. The following definitions ; define the specific offsets for each command. ; ; ; Set Adapter State command specific offsets. ; . = QBUF$C_END_OF_COMMON_HDR $DEF QBUF$IB_ADP_STATE .BLKB 1 ; Requested adapter state QBUF$C_SET_ADAP_STATE_ENB = ^X01 ; Request ENABLED state QBUF$C_SET_ADAP_STATE_DIS = ^X02 ; Request DISABLED state $DEF QBUF$IB_SBZ0 .BLKB 3 ; Should be zero QBUF$C_SET_ADAP_STATE_SIZE = . - QBUF$C_DRVLEN ; Set adap state length ; ; Adapter State Set response specific offsets. ; . = QBUF$C_END_OF_COMMON_HDR $DEF QBUF$IB_ADP_STATE_RSP .BLKB 1 ; Requested adapter state $DEF QBUF$IB_N_HOST_SQ .BLKB 1 $DEF QBUF$IW_FLAGS .BLKW 1 $DEF QBUF$IW_N_ADAP_QC .BLKW 1 $DEF QBUF$IB_KA_TIME .BLKB 1 $DEF QBUF$IB_N_FREEQ .BLKB 1 $DEF QBUF$IB_N_CHAN .BLKB 1 $DEF QBUF$IB_XFER_ALIGN .BLKB 1 $DEF QBUF$IB_N_SAC .BLKB 1 $DEF QBUF$IB_SBZ1 .BLKB 5 $DEF QBUF$IQ_NODES_ON_CHAN .BLKQ 1 ; ; Set Parameters command specific offsets. ; . = QBUF$C_END_OF_COMMON_HDR $DEF QBUF$IB_NT_LUNS .BLKB 1 ; Target mode LUNs active $DEF QBUF$IB_N_HOST_SG .BLKB 1 ; Number of host segments $DEF QBUF$IW_SET_PARAM_FLAGS .BLKW 1 ; Flags $VIELD QBUF,0,<- ; Bits used with CAM flags 1 ,- ; Enable port counters ,- ; Filler > $DEF QBUF$IS_SYSTEM_TIME .BLKL 1 ; System time in milliseconds $DEF QBUF$IS_INT_HOLDOFF .BLKL 1 ; Interrupt holdoff time $DEF QBUF$IS_RP_TIMER .BLKL 1 ; Reset reply time $DEF QBUF$IQ_HOST_SG_BSD .BLKQ 1 ; BSD of host segment QBUF$C_SET_PARAM_SIZE = . - QBUF$C_DRVLEN ; Set Parameter length ; ; The Parameters Set response message does not contain any additional fields ; in the CCB. The following are the valid values for the status field in ; the Queue Carrier. ; QBUF$C_INV_COMMAND = 0 ; Cmd not supported or the ; cmd format is incorrect QBUF$C_SUCCESS = 1 ; Param set as requested QBUF$C_NOT_DISABLED = -2 ; Adapter not in disabled state QBUF$C_INV_INT_HOLDOFF = -5 ; Int holdoff timer is not ; valid or not supported QBUF$C_INV_HOST_SG = -6 ; Insufficient host memory ; allocated ; ; Set Channel State command and Channel State Set response specific offsets. ; . = QBUF$C_END_OF_COMMON_HDR $DEF QBUF$IB_CHN_STATE .BLKB 1 ; Requested channel state QBUF$C_SET_CHAN_STATE_ENB = 1 ; Request enabled state QBUF$C_SET_CHAN_STATE_DIS = 2 ; Request disabled state $DEF QBUF$IB_SBZ3 .BLKB 3 ; Should be zero $DEF QBUF$IB_CHAN_ID .BLKB 1 ; Channel ID $DEF QBUF$IB_NODE_ID .BLKB 1 ; Node ID $DEF QBUF$IB_SBZ4 .BLKB 2 ; Should be zero QBUF$C_SET_CHAN_STATE_SIZE = . - QBUF$C_DRVLEN ; Set chan state length ; ; Set Device State command and Device State Set response specific offsets. ; . = QBUF$C_END_OF_COMMON_HDR $DEF QBUF$IB_DEV_STATE .BLKB 1 ; Requested device state QBUF$C_SET_DEV_STATE_ENB = 1 ; Request enabled state $DEF QBUF$SBZ5 .BLKB 7 ; Should be zero QBUF$C_SET_DEV_STATE_SIZE = . - QBUF$C_DRVLEN ; Set device state length ; ; Read Counters command specific offsets. ; . = QBUF$C_END_OF_COMMON_HDR $DEF QBUF$IW_SBZ6 .BLKW 1 ; Should be zero $DEF QBUF$IW_CTR_FLAGS .BLKW 1 ; Counter flags $VIELD QBUF,0,<- ; Bits used with counters flags ,- ; Clear port counters ,- ; Filler > $DEF QBUF$IB_SBZ7 .BLKB 4 ; Should be zero QBUF$C_READ_COUNTERS_SIZE = . - QBUF$C_DRVLEN ; Read counters length ; ; Counters Read response specific offsets. ; . = QBUF$C_END_OF_COMMON_HDR $DEF QBUF$IB_SBZ8 .BLKB 4 ; Should be zero $DEF QBUF$IS_REQ_COUNTERS .BLKL 21 ; Required counters $DEF QBUF$IS_IMP_COUNTERS .BLKL 20 ; Implementation specific QBUF$C_NUM_COUNTERS = <. - QBUF$IS_REQ_COUNTERS>/4 ; Number of counters ; ; Bus Reset Request command specific offsets. ; . = QBUF$C_END_OF_COMMON_HDR $DEF QBUF$IB_REASON .BLKB 1 ; Bus reset reason code QBUF$C_REJECT_BDR = 1 ; Target rejected BDR attempt QBUF$C_PHASE_ERROR = 2 ; Phase error QBUF$C_DATA_OUT = 3 ; Data overrun in dataout phase QBUF$C_RSVD_PHASE = 4 ; Target entered reserved phase QBUF$C_NO_MSG_OUT = 5 ; Target did not enter msg out $DEF QBUF$IB_TRGT_ID .BLKB 1 ; Target id $DEF QBUF$IB_SBZ10 .BLKB 6 ; Should be zero $DEF QBUF$IQ_CCB .BLKQ 1 ; VA of CCB active on the bus QBUF$C_BUS_RESET_REQ_SIZE = . - QBUF$C_DRVLEN ; Bus reset request length ; ; The Bus Reset command does not define any fields in the CCB. The action ; authorized by the host is in the status field of the Queue Carrier. ; The folowing defines the values for the status field in the Queue Carrier. ; CAR$C_INV_COMMAND = 0 ; Command not supported CAR$C_SUCCESS = 1 ; Adapter should reset bus CAR$C_NO_RESET = -13 ; Adapter should not reset bus ; ; Execute SCSI I/O command specific offsets. ; . = QBUF$C_END_OF_COMMON_HDR $DEF QBUF$PS_PERIPH_DRIVER_PTR .BLKQ 1 ; Periph driver pointer QBUF$PS_BSM_PTR = QBUF$PS_PERIPH_DRIVER_PTR ; BSM pointer $DEF QBUF$PS_NEXT_CCB_PTR .BLKQ 1 ; Next CCB pointer ; ; N.B.: the following field is described as "not used" by the SIMport ; spec. It's being used here, on platforms that have map registers, as ; a CRCTX pointer for allocating/deallocating map registers to map the ; Queue Buffer. Implicit in this mechanism is the assumption that the ; adapter DOES NOT ALTER this location, i.e., that it either leaves it ; alone completely or reads and rewrites it with the same value. Current ; controllers (KZTSA and KZPSA) exhibit the expected behavior. There is ; some danger, however, that some future SIMport adapter will break this ; assumption. ; ; Done properly, the CRCTX pointer should be carved out of the autosense ; buffer, by shortening up the latter by a longword. This has not been ; done yet, due to scheduling exigencies/de-risking. ; $DEF QBUF$PS_REQ_MAP_INFO .BLKQ 1 ; Req map info QBUF$PS_CRCTX_PTR = QBUF$PS_REQ_MAP_INFO ; CRCTX pointer $DEF QBUF$PS_CB_COMPLETION .BLKQ 1 ; Callback routine $DEF QBUF$PS_SG_LIST_BUFF_PTR .BLKQ 1 ; SG buff pointer $DEF QBUF$IS_DATA_TRAN_SIZE .BLKL 1 ; Data tranfer size $DEF QBUF$IS_PAD1 .BLKL 1 ; Padding for alignment $DEF QBUF$PS_SENSE_BUFF_PTR .BLKQ 1 ; Autosense buffer ptr $DEF QBUF$IB_SENSE_BUFF_SIZE .BLKB 1 ; Sense buffer size $DEF QBUF$IB_CDB_SIZE .BLKB 1 ; CDB size $DEF QBUF$IW_NUM_SG_ENTRIES .BLKW 1 ; Number SG entries $DEF QBUF$IS_SCSI_IO_VU .BLKL 1 ; Vendor unique $DEF QBUF$IB_SCSI_STS .BLKB 1 ; SCSI status $DEF QBUF$IB_AS_RESID_LENGTH .BLKB 1 ; Autosense resid length $DEF QBUF$IW_RSVD1 .BLKW 1 ; Reserved $DEF QBUF$IS_RESID_LENGTH .BLKL 1 ; Residual length $DEF QBUF$IB_CDB .BLKB 12 ; CDB bytes $DEF QBUF$IS_PAD2 .BLKL 1 ; Padding $DEF QBUF$IS_TIMEOUT_VALUE .BLKL 1 ; Timeout value $DEF QBUF$IS_PAD3 .BLKL 1 ; Padding $DEF QBUF$PS_MSG_BUFF_PTR .BLKQ 1 ; Msg buffer pointer $DEF QBUF$IW_MSG_BUFF_SIZE .BLKW 1 ; Msg buffer size $DEF QBUF$IW_VU_FLAGS .BLKW 1 ; VU flags $DEF QBUF$IB_TAG_QUEUE_ACTION .BLKB 1 ; Tag queue action QBUF$C_SIMPLE_TAG = ^X20 ; Simple tag request QBUF$C_HEAD_OF_QUEUE = ^X21 ; Queue head request QBUF$C_ORDERED_QUEUE = ^X22 ; Ordered queue request $DEF QBUF$IB_TAG_ID .BLKB 1 ; Tag ID $DEF QBUF$IB_INITIATOR_ID .BLKB 1 ; Initiator ID $DEF QBUF$IB_RSVD2 .BLKB 1 ; Reserved QBUF$C_EXEC_SCSI_IO_COMMON_SIZE = . - QBUF$C_DRVLEN - QBUF$C_COMMON_HDR_SIZE ; ; The following section describes the Execute SCSI I/O Private Data Area. ; $DEF QBUF$IB_SCSI_STAT .BLKB 1 ; SCSI status $DEF QBUF$IB_SEN_RES .BLKB 1 ; Sense residual length $DEF QBUF$IW_SBZ9 .BLKW 1 ; Should be zero $DEF QBUF$IS_DATA_RES .BLKL 1 ; Data residual length $DEF QBUF$PS_DATA_BSD1 .BLKQ 1 ; Data 1 BSD $DEF QBUF$PS_DATA_BSD2 .BLKQ 1 ; Data 2 BSD $DEF QBUF$PS_CDB_BSD .BLKQ 1 ; CDB BSD $DEF QBUF$PS_SEN_BSD .BLKQ 1 ; Autosense BSD $DEF QBUF$PS_MSG_BSD .BLKQ 1 ; Message BSD $DEF QBUF$PS_CCB_NEXT_PTR .BLKQ 1 ; Next CCB pointer QBUF$C_EXEC_SCSI_IO_SIZE = . - QBUF$C_DRVLEN ; Exec SCSI I/O length ; ; Path Inquiry command specific offsets. ; . = QBUF$C_END_OF_COMMON_HDR $DEF QBUF$IB_PI_VERSION .BLKB 1 ; Version $DEF QBUF$IB_PI_SCSI_CABAP .BLKB 1 ; SCSI capabilities $DEF QBUF$IB_PI_TARGET_MODE .BLKB 1 ; Target mode $DEF QBUF$IB_PI_MISC .BLKB 1 ; Miscellaneous $DEF QBUF$IW_PI_ENGINE_CNT .BLKW 1 ; Engine count $DEF QBUF$IB_PI_PATH_INQ_VU .BLKB 14 ; Vendor unique $DEF QBUF$IS_PI_PDA_SIZE .BLKL 1 ; Private data area size $DEF QBUF$IS_PI_ASYNCH_EVENTS .BLKL 1 ; Asynchronous events $DEF QBUF$IB_PI_HIGH_PATH_ID .BLKB 1 ; Highest path ID $DEF QBUF$IB_PI_SCSI_DEV_ID .BLKB 1 ; SCSI device ID $DEF QBUF$IW_PI_RSVD7 .BLKW 1 ; Reserved $DEF QBUF$IB_PI_VENDOR_ID_SIM .BLKB 16 ; SIM vendor ID $DEF QBUF$IB_PI_VENDOR_ID_HBA .BLKB 16 ; HBA vendor ID $DEF QBUF$IS_PI_OSD .BLKL 1 ; OS use QBUF$C_PATH_INQ_SIZE = . - QBUF$C_DRVLEN ; Path Inquiry length ; ; Enable LUN command specific offsets. ; . = QBUF$C_END_OF_COMMON_HDR $DEF QBUF$IW_GROUP_6_VU_CDB .BLKW 1 ; Group 6 VU CDB lengths $DEF QBUF$IW_GROUP_7_VU_CDB .BLKW 1 ; Group 7 VU CDB lengths $DEF QBUF$IB_PAD1 .BLKB 4 ; Padding for alignment $DEF QBUF$PS_IMMED_NOTIFY_CCBS .BLKQ 1 ; Immediate notify CCB list $DEF QBUF$IS_NUM_IMMED_NOTIFY_CCBS .BLKL 1 ; Number immediate notify CCBs $DEF QBUF$IB_PAD2 .BLKB 4 ; Padding for alignment $DEF QBUF$PS_TARGET_CCBS .BLKQ 1 ; Target CCB list $DEF QBUF$IS_NUM_TARGET_CCBS .BLKL 1 ; Number target CCBs $DEF QBUF$IB_PAD3 .BLKB 4 ; Padding for alignment ; ; Enable LUN Private Data Area offsets. ; $DEF QBUF$PS_ACCEPT_LIST_HEAD .BLKQ 1 ; Accept target IO list head $DEF QBUF$PS_ACCEPT_LIST_TAIL .BLKQ 1 ; Accept target IO list tail QBUF$C_ENABLE_LUN_SIZE = . - QBUF$C_DRVLEN ; Enable LUN length ; ; Accept Target IO command specific offsets. ; ; The Accept Target IO CCB has the same format as the Execute SCSI IO CCB ; except for the Private Data Area. The Accept Target IO CCB will use the ; same offsets for the common area of the CCB as the Execute SCSI IO command. ; . = QBUF$C_END_OF_COMMON_HDR + QBUF$C_EXEC_SCSI_IO_COMMON_SIZE ; ; The following section describes the Accept Target IO Private Data Area. ; $DEF QBUF$IB_ATIO_SCSI_STAT .BLKB 1 ; SCSI status $DEF QBUF$IB_ATIO_RSVD1 .BLKB 1 ; Reserved $DEF QBUF$IB_ATIO_CDB_LEN .BLKB 1 ; CDB length QBUF$C_ATIO_CDB_BUFF_SIZE = ^X12 ; CDB buffer size $DEF QBUF$IB_ATIO_SEN_LEN .BLKB 1 ; Autosense buffer length $DEF QBUF$IS_ATIO_RSVD2 .BLKB 20 ; Reserved $DEF QBUF$PS_ATIO_CDB_BSD .BLKQ 1 ; CDB BSD $DEF QBUF$PS_ATIO_SEN_BSD .BLKQ 1 ; Autosense BSD $DEF QBUF$IS_ATIO_CMD_TIMEOUT .BLKL 1 ; Command timeout $DEF QBUF$IB_ATIO_TQA .BLKB 1 ; Tagged queue action $DEF QBUF$IB_ATIO_TAG_ID .BLKB 1 ; Tag ID $DEF QBUF$IB_ATIO_INIT_ID .BLKB 1 ; Intiator ID $DEF QBUF$IB_ATIO_RSVD3 .BLKB 9 ; Reserved QBUF$C_ACCEPT_TARGET_IO_SIZE = . - QBUF$C_DRVLEN ; Accept target length ; ; Continue Target IO command specific offsets. ; ; The Continue Target IO CCB has the same format as the Execute SCSI IO CCB ; except for the Private Data Area. The Continue Target IO CCB will use the ; same offsets for the common area of the CCB as the Execute SCSI IO command. ; . = QBUF$C_END_OF_COMMON_HDR + QBUF$C_EXEC_SCSI_IO_COMMON_SIZE ; ; The following section describes the Continue Target IO Private Data Area. ; $DEF QBUF$IB_CTIO_SCSI_STAT .BLKB 1 ; SCSI status $DEF QBUF$IW_CTIO_RSVD1 .BLKW 1 ; Reserved $DEF QBUF$IB_CTIO_SEN_LEN .BLKB 1 ; Autosense buffer length $DEF QBUF$IS_CTIO_DATA_RES .BLKL 1 ; Data buffer residual length $DEF QBUF$PS_CTIO_DATA_BSD1 .BLKQ 1 ; First BSD for data buffer $DEF QBUF$PS_CTIO_DATA_BSD2 .BLKQ 1 ; Second BSD for data buffer $DEF QBUF$IQ_CTIO_RSVD2 .BLKQ 1 ; Reserved $DEF QBUF$PS_CTIO_SEN_BSD .BLKQ 1 ; Autosense BSD $DEF QBUF$IQ_CTIO_RSVD3 .BLKQ 1 ; Reserved $DEF QBUF$PS_CTIO_CCB_NEXT_PTR .BLKQ 1 ; PA of next CCB if linked cmd QBUF$C_CONTINUE_TARGET_IO_SIZE = . - QBUF$C_DRVLEN ; Cont. target length ; ; Set Target State command and Target State Set response specific offsets. ; . = QBUF$C_END_OF_COMMON_HDR $DEF QBUF$IW_TS_SEQUENCE_ID .BLKW 1 ; Sequence ID of target event $DEF QBUF$IB_TS_IID .BLKB 1 ; Initiator ID of event $DEF QBUF$IB_TS_TAG_ID .BLKB 1 ; Tag ID $DEF QBUF$IB_TS_FLAGS .BLKB 1 ; Flags $DEF QBUF$IB_RSVD3 .BLKB 3 ; Reserved (SBZ) QBUF$C_TARGET_STATE_SIZE = . - QBUF$C_DRVLEN ; Set target length ; ; Target Event response specific offsets. ; . = QBUF$C_END_OF_COMMON_HDR $DEF QBUF$IW_TE_SEQUENCE_ID .BLKW 1 ; Sequence ID of event $DEF QBUF$IB_TE_IID .BLKB 1 ; Initiator ID of event $DEF QBUF$IB_TE_TAG_ID .BLKB 1 ; Tag ID $DEF QBUF$IB_TE_FLAGS .BLKB 1 ; Flags $VIELD QBUF,0,<- ; Bits used with TE flags ,- ; Tag ID field is valid ,- ; Reserved (SBZ) > $DEF QBUF$IB_TE_SENSE_LEN .BLKB 1 ; Number of sense bytes returned $DEF QBUF$IB_RSVD4 .BLKB 2 ; Reserved (SBZ) $DEF QBUF$IB_TE_MSG_CODE .BLKB 1 ; SCSI message code $DEF QBUF$IB_TE_MSG_ARGS .BLKB 7 ; SCSI message code data $DEF QBUF$IB_TE_SENSE_BUFF .BLKB 32 ; Sense data returned QBUF$C_TARGET_EVENT_SIZE = . - QBUF$C_DRVLEN ; Target Event CCB length ; ; Target Event Acknowledged command specific offsets. ; . = QBUF$C_END_OF_COMMON_HDR $DEF QBUF$IW_TEA_SEQUENCE_ID .BLKW 1 ; Sequence ID $DEF QBUF$IB_TEA_IID .BLKB 1 ; Initiator ID $DEF QBUF$IB_TEA_TAG_ID .BLKB 1 ; Tag ID $DEF QBUF$IB_TEA_FLAGS .BLKB 1 ; Flags $VIELD QBUF,0,<- ; Bits used with TEA flags ,- ; Tag ID field is valid ,- ; Clear all events ,- ; Reserved (SBZ) > $DEF QBUF$IB_RSVD5 .BLKB 3 ; Reserved (SBZ) QBUF$C_TARGET_EVENT_ACK_SIZE = . - QBUF$C_DRVLEN ; TE Ack CCB length ; ; The remaining 256 bytes are the autosense buffer for returning autosense ; data. The autosense buffer is pointed to by the QBUF$PS_SEN_BSD field in ; the private data area of the Execute SCSI I/O command. ; . = 256 QBUF$C_AUTOSENSE_OFFSET = . - QBUF$C_DRVLEN ; AS offset within adapter- ; visible section. $DEF QBUF$IB_AUTOSENSE_BUF .BLKB 256 ; Autosense buffer QBUF$C_AUTOSENSE_BUF_SIZE = 255 ; Autosense buffer length QBUF$C_LENGTH = . ; Total Q_Buffer length ; (512 bytes) QBUF$C_ADPLEN = . - QBUF$C_DRVLEN ; Length of adapter-visible ; portion. $DEFEND QBUF .SYMBOL_ALIGNMENT NONE .PAGE .SBTTL Inquiry Data Definitions .SYMBOL_ALIGNMENT QUAD $DEFINI INQ,GLOBAL $DEF INQ$PS_FLINK .BLKL 1 ; Forward link $DEF INQ$PS_BLINK .BLKL 1 ; Backward link $DEF INQ$IW_SIZE .BLKW 1 ; Structure size $DEF INQ$IB_TYPE .BLKB 1 ; Structure type $DEF INQ$IB_SUBTYPE .BLKB 1 ; Structure subtype INQ$C_HEADER_LENGTH = . ; Inquiry header length $DEF INQ$IB_PERIPH_DEV_TYPE .BLKB 1 ; Peripheral device type INQ$C_PROC_DEV = ^X3 ; Processor device $DEF INQ$IB_DEV_TYPE_MOD .BLKB 1 ; Device type modifier $DEF INQ$IB_ANSI_VERSION .BLKB 1 ; ANSI approved version INQ$C_COMPLIES_TO_SCSI2 = ^X2 ; Complies to SCSI2 $DEF INQ$IB_RESP_DATA_FORMAT .BLKB 1 ; Response data format INQ$C_SCSI2_FORMAT = ^X2 ; Inquiry data in SCSI2 format $DEF INQ$IB_ADDITIONAL_LEN .BLKB 1 ; Additional inquiry length INQ$C_ADDITIONAL_LEN = ^X1F ; Additional length $DEF INQ$IB_Reserved .BLKB 2 ; Reserved $DEF INQ$IB_FLAGS .BLKB 1 ; Flags $DEF INQ$IB_VENDOR_ID .BLKB 8 ; Vendor ID $DEF INQ$IB_PRODUCT_ID .BLKB 16 ; Product ID $DEF INQ$IS_PRODUCT_REV .BLKB 4 ; Product revision INQ$C_DATA_LENGTH = . - INQ$C_HEADER_LENGTH ; Inquiry data length INQ$C_LENGTH = . ; Total Inquiry data length $DEFEND INQ .SYMBOL_ALIGNMENT NONE .PAGE .SBTTL Request Sense Data Definitions .SYMBOL_ALIGNMENT QUAD $DEFINI SENSE,GLOBAL $DEF SENSE$PS_FLINK .BLKL 1 ; Forward link $DEF SENSE$PS_BLINK .BLKL 1 ; Backward link $DEF SENSE$IW_SIZE .BLKW 1 ; Structure size $DEF SENSE$IB_TYPE .BLKB 1 ; Structure type $DEF SENSE$IB_SUBTYPE .BLKB 1 ; Structure subtype SENSE$C_HEADER_LENGTH = . ; Sense data header length $DEF SENSE$IB_ERROR_CODE .BLKB 1 ; Error code SENSE$C_CURRENT_ERROR = ^X70 ; Current error SENSE$C_DEFERRED_ERROR = ^X71 ; Deferred error $VIELD SENSE,0,<- ; Bits used with info field ,- ; Filler ,- ; Valid bit > $DEF SENSE$IB_SEGMENT_NUMBER .BLKB 1 ; Segment number $DEF SENSE$IB_SENSE_KEY .BLKB 1 ; Sense key SENSE$C_ILLEGAL_REQUEST = ^X5 ; Illegal request $VIELD SENSE,0,<- ; Bits used with info field ,- ; Filler ,- ; Reserved ,- ; Incorrect length indicator ,- ; End of medium ,- ; Filemark > $DEF SENSE$IB_INFORMATION .BLKB 4 ; Information $DEF SENSE$IB_ADDITIONAL_LEN .BLKB 1 ; Additional sense length SENSE$C_ADDITIONAL_LEN = ^XA ; Additional length $DEF SENSE$IB_CMD_SPEC_INFO .BLKB 4 ; Command specific Information $DEF SENSE$IB_ADD_SENSE_CODE .BLKB 1 ; Additional sense code SENSE$C_LUN_NOT_SUPPORTED = ^X25 ; LUN not supported $DEF SENSE$IB_ADD_SENSE_QUAL .BLKB 1 ; Additional sense qualifier $DEF SENSE$IB_FRU_CODE .BLKB 1 ; Field replaceable unit code $DEF SENSE$IB_SENSE_KEY_SPEC .BLKB 3 ; Sense key specific SENSE$C_DATA_LENGTH = . - SENSE$C_HEADER_LENGTH ; Sense data length SENSE$C_LENGTH = . ; Total Sense data length $DEFEND SENSE .SYMBOL_ALIGNMENT NONE .PAGE .SBTTL Buffer Segment Descriptor/Map Definitions .SYMBOL_ALIGNMENT QUAD $DEFINI BSD,GLOBAL $DEF BSD$PQ_PA .BLKB 6 ; PA of BSD BSD$C_PA_SHIFT = 2 ; Number of bits to shift PA $VIELD BSD,0,<- ,- ; Data buffer pointer type ,- ; Reserved field > $DEF BSD$IW_BYTE_COUNT .BLKW 1 ; Size in bytes of host buffer BSD$C_BYTE_COUNT_SHIFT = 48 ; Number of bits to shift byte ; count into upper word BSD$M_BYTE_COUNT = ^X ; Byte count mask bits 63:48 BSD$C_LENGTH = . ; Total BSD length $DEFEND BSD .SYMBOL_ALIGNMENT QUAD $DEFINI BSM,GLOBAL ; ; First 20 bytes are PKSDRIVER private area. This section is not visible to ; the adapter. ; $DEF BSM$PS_FLINK .BLKL 1 ; Forward link of BSMs in queue $DEF BSM$PS_BLINK .BLKL 1 ; Backward link of BSMs in queue $DEF BSM$IW_SIZE .BLKW 1 ; Structure size $DEF BSM$IB_TYPE .BLKB 1 ; Structure type $DEF BSM$IB_SUBTYPE .BLKB 1 ; Structure subtype $DEF BSM$PS_CRCTX .BLKL 1 ; CRCTX pointer for mapping $DEF BSM$PQ_PA .BLKQ 1 ; PA of BSM $DEF BSM$IS_LEADING_COUNT .BLKL 1 ; Leading unaligned bytes $DEF BSM$IS_TRAILING_COUNT .BLKL 1 ; Trailing unaligned bytes $DEF BSM$PS_LEADING_BUFF .BLKL 1 ; Leading buffer $DEF BSM$PS_TRAILING_BUFF .BLKL 1 ; Trailing buffer BSM$C_HEADER_LENGTH = . ; BSM header length ; ; The adapter visible section begins here. ; $DEF BSM$PS_NEXT_BSM_BSD .BLKQ 1 ; BSD for next BSM in list $DEF BSM$IW_NUM_ENTRIES .BLKW 1 ; Number of valid BSDs in BSM $DEF BSM$SBZ .BLKB 6 ; Should be zero $DEF BSM$IS_TOTAL_COUNT .BLKL 1 ; Total bytes mapped by this BSM $DEF BSM$IS_BUFFER_OFFSET .BLKL 1 ; Byte offset from the start of ; the host buffer for this BSM ; ; The following BSDs each define one physicaly contiguous segment of the ; host buffer. ; $DEF BSM$PS_BSD0 .BLKQ 1 $DEF BSM$PS_BSD1 .BLKQ 1 $DEF BSM$PS_BSD2 .BLKQ 1 $DEF BSM$PS_BSD3 .BLKQ 1 $DEF BSM$PS_BSD4 .BLKQ 1 $DEF BSM$PS_BSD5 .BLKQ 1 $DEF BSM$PS_BSD6 .BLKQ 1 $DEF BSM$PS_BSD7 .BLKQ 1 $DEF BSM$PS_BSD8 .BLKQ 1 $DEF BSM$PS_BSD9 .BLKQ 1 $DEF BSM$PS_BSD10 .BLKQ 1 $DEF BSM$PS_BSD11 .BLKQ 1 $DEF BSM$PS_BSD12 .BLKQ 1 $DEF BSM$PS_BSD13 .BLKQ 1 $DEF BSM$PS_BSD14 .BLKQ 1 $DEF BSM$PS_BSD15 .BLKQ 1 $DEF BSM$PS_BSD16 .BLKQ 1 BSM$C_LENGTH = . ; Total BSM length $DEFEND BSM .PAGE .SBTTL Pad block definition ; ; Pad buffer block header definition ; .SYMBOL_ALIGNMENT QUAD $DEFINI PAD,GLOBAL $DEF PAD$PS_FLINK .BLKL 1 ; Forward link $DEF PAD$PS_BLINK .BLKL 1 ; Backward link $DEF PAD$IW_SIZE .BLKW 1 ; Structure size in bytes $DEF PAD$IB_TYPE .BLKB 1 ; Structure type $DEF PAD$IB_SUBTYPE .BLKB 1 ; Structure subtype $DEF PAD$PS_CRCTX .BLKL 1 ; CRCTX pointer for mapping $DEF PAD$PQ_PA .BLKQ 1 ; PA of pad buffer PAD$C_HEADER = . $DEFEND PAD .SYMBOL_ALIGNMENT NONE ; ; Type 0 buffer pointer fields definition. Type 0 buffer pointers point to ; a segment of a host memory buffer (BSD). ; $DEFINI TP0 $VIELD TP0,0,<- ,- ; Data buffer pointer type ,- ; MBZ field ,- ; Bits <47:2> of BSD PA ,- ; Bytes in host buffer segment > TP$C_TYPE0 = 0 ; Type 0 constant $DEFEND TP0 ; ; Type 1 buffer pointer fields definition. Type 1 buffer pointers point to ; a Buffer Segment Map (BSM) for the buffer. ; $DEFINI TP1 $VIELD TP1,0,<- ,- ; Data buffer pointer type ,- ; MBZ field ,- ; Bits <47:2> of BSM PA ,- ; Bytes in host buffer segment > TP$C_TYPE1 = 1 ; Type 1 constant $DEFEND TP1 .PAGE .SBTTL KZTSA Register Definitions ; ; KZTSA register offsets and bit/field definitions. ; $DEFINI TZAREG ; TZA specific registers .=^X0004 $DEF TZA_HW_REV .BLKQ 1 ; Hardware revision register .=^X0040 $DEF TZA_AMCSR .BLKQ 1 ; Adapter maintenance control and ; status register $VIELD TZA_AMCSR,0,<- ; Define bits used with AMCSR register ,- ; Maintenance initialize ,- ; SBZ ,- ; Interrupt enable > .=^X0048 $DEF TZA_ABBR .BLKQ 1 ; Adapter block base register TZA_ABBR$C_PA_SHIFT = 5 ; # of bits to shift to get PA <39:5> .=^X0050 $DEF TZA_DAFQIR .BLKQ 1 ; Driver adapter free queue entry ; insertion register TZA_DAFQIR$C_PA_SHIFT = 3 ; # of bits to shift to get PA <47:3> .=^X0058 $DEF TZA_DACQIR .BLKQ 1 ; Driver adapter command queue entry ; insertion register TZA_DACQIR$C_PA_SHIFT = 3 ; # of bits to shift to get PA <47:3> .=^X0080 $DEF TZA_ASR .BLKQ 1 ; Adapter Status Register $VIELD TZA_ASR,0,<- ; Define bits used with ASR register ,- ; SBZ ,- ; Host data structure has occurred ,- ; Host memory system error has occurred ,- ; Abnormal condition in adapter FW ,- ; SBZ ,- ; Adapter requesting completion int. ,- ; Adapter in uninitialized state ,- ; SBZ ,- ; Adapter maintenance error occurred > .=^X0088 $DEF TZA_AFAR .BLKQ 1 ; Adapter failing address register .=^X0090 $DEF TZA_AFPR .BLKQ 1 ; Adapter failing parameter register ; ; AFPR error codes if ADSE bit is set in ASR. ; AFPR_ILL_AB_FORMAT = 4 ; Illegal adapter block format AFPR_ILL_QB_ALIGN = 8 ; Illegal queue buffer alignment AFPR_QCAR_INVAL_FIELD = 16 ; Queue carrier field contains ; invalid data AFPR_BSM_INVAL_FIELD = 17 ; A BSM or BSD field contains ; invalid data AFPR_BSM_LINK= 18 ; Offset for current BSM does not equal ; offset + length of previous BSM ; ; AFPR error codes if AMSE bit is set in ASR. ; AFPR_ERR_ACCESS_AB = 1 ; Error while accessing adapter block AFPR_ERR_ACCESS_QCAR = 2 ; Error while accessing a queue carrier AFPR_ERR_ACCESS_QBUF = 3 ; Error while accessing q queue buffer AFPR_ERR_ACCESS_BSM = 4 ; Error while accessing a BSM or BSD .=^X00C0 $DEF TZA_ERRLOG1 .BLKL 1 ; Adapter error log data register 1 $DEF TZA_ERRLOG2 .BLKL 1 ; Adapter error log data register 2 $DEF TZA_ERRLOG3 .BLKL 1 ; Adapter error log data register 3 $DEF TZA_ERRLOG4 .BLKL 1 ; Adapter error log data register 4 $DEF TZA_ERRLOG5 .BLKL 1 ; Adapter error log data register 5 $DEF TZA_ERRLOG6 .BLKL 1 ; Adapter error log data register 6 $DEF TZA_ERRLOG7 .BLKL 1 ; Adapter error log data register 7 $DEF TZA_ERRLOG8 .BLKL 1 ; Adapter error log data register 8 $DEF TZA_ERRLOG9 .BLKL 1 ; Adapter error log data register 9 $DEF TZA_ERRLOGA .BLKL 1 ; Adapter error log data register A ; ; Note: the following register offsets are relative to the page on which ; they occur. ; .=^X0 $DEF TZA_CLEAR_INT .BLKL 1 ; Clear Turbochannel interrupt register .=^X0 $DEF TZA_RESET .BLKQ 1 ; Adapter reset register $DEFEND TZAREG .PAGE .SBTTL KZPSA Register Definitions ; ; KZPSA register offsets and bit/field definitions. ; $DEFINI PZAREG ; PZA specific registers .=^X0000 $DEF PZA_AMCSR .BLKQ 1 ; Adapter maintenance control and ; status register $VIELD PZA_AMCSR,0,<- ; Define bits used with AMCSR register ,- ; Maintenance initialize ,- ; SBZ ,- ; Interrupt enable > .=^X0004 $DEF PZA_HW_REV .BLKQ 1 ; Hardware revision register .=^X0008 $DEF PZA_ABBR .BLKQ 1 ; Adapter block base register PZA_ABBR$C_PA_SHIFT = 5 ; # of bits to shift to get PA <39:5> .=^X0010 $DEF PZA_DAFQIR .BLKQ 1 ; Driver adapter free queue entry ; insertion register PZA_DAFQIR$C_PA_SHIFT = 3 ; # of bits to shift to get PA <47:3> .=^X0018 $DEF PZA_DACQIR .BLKQ 1 ; Driver adapter command queue entry ; insertion register PZA_DACQIR$C_PA_SHIFT = 3 ; # of bits to shift to get PA <47:3> .=^X0040 $DEF PZA_ASR .BLKQ 1 ; Adapter Status Register $VIELD PZA_ASR,0,<- ; Define bits used with ASR register ,- ; SBZ ,- ; Host data structure has occurred ,- ; Host memory system error has occurred ,- ; Abnormal condition in adapter FW ,- ; SBZ ,- ; Adapter requesting completion int. ,- ; Adapter in uninitialized state ,- ; SBZ ,- ; Adapter maintenance error occurred > .=^X0048 $DEF PZA_AFAR .BLKQ 1 ; Adapter failing address register .=^X0050 $DEF PZA_AFPR .BLKQ 1 ; Adapter failing parameter register ; ; AFPR error codes if ADSE bit is set in ASR. ; AFPR_ILL_AB_FORMAT = 4 ; Illegal adapter block format AFPR_ILL_QB_ALIGN = 8 ; Illegal queue buffer alignment AFPR_QCAR_INVAL_FIELD = 16 ; Queue carrier field contains ; invalid data AFPR_BSM_INVAL_FIELD = 17 ; A BSM or BSD field contains ; invalid data AFPR_BSM_LINK= 18 ; Offset for current BSM does not equal ; offset + length of previous BSM ; ; AFPR error codes if AMSE bit is set in ASR. ; AFPR_ERR_ACCESS_AB = 1 ; Error while accessing adapter block AFPR_ERR_ACCESS_QCAR = 2 ; Error while accessing a queue carrier AFPR_ERR_ACCESS_QBUF = 3 ; Error while accessing q queue buffer AFPR_ERR_ACCESS_BSM = 4 ; Error while accessing a BSM or BSD .=^X00C0 $DEF PZA_ERRLOG1 .BLKL 1 ; Adapter error log data register 1 $DEF PZA_ERRLOG2 .BLKL 1 ; Adapter error log data register 2 $DEF PZA_ERRLOG3 .BLKL 1 ; Adapter error log data register 3 $DEF PZA_ERRLOG4 .BLKL 1 ; Adapter error log data register 4 $DEF PZA_ERRLOG5 .BLKL 1 ; Adapter error log data register 5 $DEF PZA_ERRLOG6 .BLKL 1 ; Adapter error log data register 6 $DEF PZA_ERRLOG7 .BLKL 1 ; Adapter error log data register 7 $DEF PZA_ERRLOG8 .BLKL 1 ; Adapter error log data register 8 $DEF PZA_ERRLOG9 .BLKL 1 ; Adapter error log data register 9 $DEF PZA_ERRLOGA .BLKL 1 ; Adapter error log data register A ; ; Note: the following register offsets are relative to the page on which ; they occur. ; .=^X0 $DEF PZA_CLEAR_INT .BLKL 1 ; Clear interrupt register .=^X0 $DEF PZA_RESET .BLKQ 1 ; Adapter reset register $DEFEND PZAREG .PAGE .SBTTL CRAM Table and Macro Definitions ; ; Define offsets to reference fields within each table entry as generated ; by the SETUP_CRAMCMD macro. ; $DEFINI TBL $DEF TBL$IS_INDEX .BLKL 1 ; Command index TBL$IS_CSROFF = . ; Start of CSR offset list. $DEF TBL$IS_TZA_CSROFF .BLKL 1 ; Offset from base of SIMport ; adap regs. (KZTSA) $DEF TBL$IS_PZA_CSROFF .BLKL 1 ; Offset from base of SIMport ; adap regs. (KZPSA) $DEF TBL$IS_SPDTOFF .BLKL 1 ; Offset to CRAM ptr in SPDT $DEF TBL$IS_IOHANDLEOFF .BLKL 1 ; Offset to IOHANDLE in SPDT TBL$C_ENTRY_SIZE = . $DEFEND TBL ; ; SETUP_CRAMCMD ; ; This macro generates data which will be used to initialize a single CRAM ; representing a single I/O register. The generated data will be read and ; used to help calculate input/output arguments to an IOC$CRAM_CMD call ; during unit init. ; .MACRO SETUP_CRAMCMD CMD_INDEX,REG,IOHANDLE=REG .LONG CRAMCMD$K_'CMD_INDEX ; Command index .LONG TZA_'REG' ; TZA Offset from base of SIMport regs. .LONG PZA_'REG' ; PZA Offset from base of SIMport regs. .LONG SPDT$PS_CRAM_'REG' ; Offset to CRAM ptr within SPDT .LONG SPDT$PQ_IOHANDLE_'IOHANDLE' ; Offset to IOHANDLE ptr within SPDT .ENDM SETUP_CRAMCMD .PAGE .SBTTL PKSDRIVER Data DRIVER_DATA ; ; The following two tables are used in routine PK$SEND_COMMAND when ; building the command buffer to send to the Simport adapter. ; ; Table for loading QBUF$IB_TAG_QUEUE_ACTION. Indexed by SCDRP$IS_QUEUE_CHAR. ; Each entry in this table is a queuing characteristic indicating how the ; I/O request should be queued to the device. Entries of zero indicate that ; the I/O request is not a queued request. ; TAG_QUEUE_ACTION_TABLE: ; ; QBUF$IB_TAG_QUEUE_ACTION SCDRP$IS_QUEUE_CHAR ; ------------------------ ------------------- .BYTE QBUF$C_SIMPLE_TAG ; SCDRP$K_QCHAR_UNORDERED .BYTE QBUF$C_ORDERED_QUEUE ; SCDRP$K_QCHAR_ORDERED .BYTE QBUF$C_HEAD_OF_QUEUE ; SCDRP$K_QCHAR_HEAD .BYTE QBUF$C_ORDERED_QUEUE ; SCDRP$K_QCHAR_NOT_QUEUED .BYTE QBUF$C_ORDERED_QUEUE ; SCDRP$K_QCHAR_ACA ; ; Table for setting or clearing the TC action enable bit in the CAM flags. ; Indexed by SCDRP$IS_QUEUE_CHAR. ; ; Each entry in this table is a bit mask used for setting or clearing the ; Tagged Command action enable bit in QBUF$IB_CAM_FLAGS_1. ; TC_ACTION_ENABLE_TABLE: ; ; QBUF$V_TQ_ACTION_ENB SCDRP$IS_QUEUE_CHAR ; -------------------- ------------------- .BYTE QBUF$M_TQ_ACTION_ENB ; SCDRP$K_QCHAR_UNORDERED .BYTE QBUF$M_TQ_ACTION_ENB ; SCDRP$K_QCHAR_ORDERED .BYTE QBUF$M_TQ_ACTION_ENB ; SCDRP$K_QCHAR_HEAD .BYTE 0 ; SCDRP$K_QCHAR_NOT_QUEUED .BYTE 0 ; SCDRP$K_QCHAR_ACA ; ; Wait time constants. ; . = <<.+7>&^C7> ; Make sure we're at least quadword aligned SECOND = 10000000 ; Num clock ticks in a second ONE_HUNDR_SEC: .QUAD ; 1/100 second ONE_SECOND: .QUAD <1*SECOND> ; 1 second TWO_SECONDS: .QUAD <2*SECOND> ; 2 seconds THIRTY_SECONDS: .QUAD <30*SECOND> ; 30 seconds .PAGE ; ; This table is read during unit initialization, as a setup to calling ; IOC$CRAM_CMD. Driver loading has already allocated enough CRAMs to allow ; one CRAM per each I/O register (and two CRAMs for any register that is both ; read and written, one for each direction). The calls to IOC$CRAM_CMD in ; the PKS$UNIT_INIT routine will initialize certain fields in each CRAM, ; calculating its arguments from the table below as follows: ; ; CMD_INDEX - Command index of the command to be returned by IOC$CRAM_CMD. ; REGISTER - Name of the register being mapped, used to build offsets ; IOHANDLE - Name of IOHANDLE structure to use for this register. ; Defaults to "REG" (SPDT$PS_IOHANDLE_REG). ; ; ; CMD_INDEX REGISTER IOHANDLE ; . = <<.+3>&^C3> ; Make sure we're at least longword aligned CRAMCMD_TABLE: SETUP_CRAMCMD RDLONG32, HW_REV SETUP_CRAMCMD WTLONG32, AMCSR SETUP_CRAMCMD WTLONG32, ABBR SETUP_CRAMCMD WTLONG32, DAFQIR SETUP_CRAMCMD WTLONG32, DACQIR SETUP_CRAMCMD RDLONG32, ASR SETUP_CRAMCMD RDLONG32, AFAR SETUP_CRAMCMD RDBYTE32, AFPR SETUP_CRAMCMD RDLONG32, ERRLOG1 SETUP_CRAMCMD RDLONG32, ERRLOG2 SETUP_CRAMCMD RDLONG32, ERRLOG3 SETUP_CRAMCMD RDLONG32, ERRLOG4 SETUP_CRAMCMD RDLONG32, ERRLOG5 SETUP_CRAMCMD RDLONG32, ERRLOG6 SETUP_CRAMCMD RDLONG32, ERRLOG7 SETUP_CRAMCMD RDLONG32, ERRLOG8 SETUP_CRAMCMD RDLONG32, ERRLOG9 SETUP_CRAMCMD RDLONG32, ERRLOGA SETUP_CRAMCMD WTLONG32, CLEAR_INT, CLEAR_INT SETUP_CRAMCMD WTLONG32, RESET, RESET .BLKL TBL$C_ENTRY_SIZE ; End of CRAM command table .PAGE .SBTTL Macro Definitions ; ; MAP_PAGE ; ; This macro is a wrapper around IOC$MAP_IO for mapping XZA registers into ; virtual address space. ; .MACRO MAP_PAGE NAME,OFFSET,NODE,ADP,IDB=R3,SPDT=R4,?L1 PUSHAB SPDT$PQ_IOHANDLE_'NAME'(SPDT) ; Where to put IOHANDLE. PUSHL #IOC$K_BUS_MEM_BYTE_GRAN ; Space to map. PUSHL G^MMG$GL_PAGE_SIZE ; Just map the whole page. PUSHL R1 MOVL OFFSET,R1 TSTL SPDT$IS_ADAPTER_TYPE(SPDT) ; Add bus physical base addr BEQL L1 ; if not on Turbochannel EVAX_ADDQ IDB$Q_CSR(IDB),R1,R1 L1: EVAX_STQ R1,SPDT$IQ_PHYS_ADDR(SPDT) POPL R1 PUSHAB SPDT$IQ_PHYS_ADDR(SPDT) ; Physical offset ptr. PUSHL NODE ; Bus node # of adapter. PUSHL ADP ; ADP pointer. CALLS #6,IOC$MAP_IO .ENDM MAP_PAGE ; ; WRITE_CSR ; ; This macro writes sets up the WDATA field in the CRAM of the specified ; register and then calls the CRAM_IO macro to write data into the ; appropriate register. ; ; Note: The argument passed as the CRAM pointer (R0 by default) is destroyed. ; The CRAM_IO macro also destroys R0. ; .MACRO WRITE_CSR REG,DATA,TYPE=L,SPDT=R4,CRAM=R0,?L1 MOVL SPDT$PS_CRAM_'REG'(SPDT),CRAM ; Get CRAM pointer for this reg .IF NB DATA MOV'TYPE' DATA,CRAM$Q_WDATA(CRAM) ; Set up data in the CRAM .ENDC CRAM_IO CRAM ; Write the data to register .IF DEFINED DEBUG .BRANCH_LIKELY BLBS R0,L1 ; Continue if success BSBW FATAL_ERROR ; Else bugcheck L1: .ENDC .ENDM WRITE_CSR ; ; READ_CSR ; ; This macro calls the CRAM_IO macro to read the specified register. ; It then transfers the data to the specified destination byte. ; ; Note: The argument passed as the CRAM pointer (R0 by default) is destroyed. ; The CRAM_IO macro also destroys R0. ; .MACRO READ_CSR REG,DEST,TYPE=L,SPDT=R4,CRAM=R0,?L1 CRAM_IO SPDT$PS_CRAM_'REG'(SPDT) ; Read the SIMport register .IF DEFINED DEBUG .BRANCH_LIKELY BLBS R0,L1 ; Continue if success BSBW FATAL_ERROR ; Else bugcheck L1: .ENDC MOVL SPDT$PS_CRAM_'REG'(SPDT),CRAM ; Get CRAM ptr for this reg .IF NB DEST MOV'TYPE' CRAM$Q_RDATA(CRAM),DEST ; Move data from CRAM to dst .ENDC .ENDM READ_CSR .PAGE ; ; WAIT_FOR_RESPONSE ; ; This macro waits for a response on the ADRQ by polling bit 0 of the ; Stopper Carrier. When bit 0 becomes clear, the adapter has inserted a ; response onto the ADRQ. ; ; INPUT: ; WAIT_TIME - Time to wait in seconds ; SPDT - register containing SPDT address ; ; OUTPUT: ; None. ; .MACRO WAIT_FOR_RESPONSE WAIT_TIME,SPDT,?L1,?L2 PUSHL R6 ; Save R6 MOVL #,R0 ; Get wait time .Disable Flagging ; Turn off info. it's aligned L1: PUSHL #SPL$C_IOLOCK8 ; Fork lock PUSHAQ ONE_HUNDR_SEC ; Time delay in ticks PUSHL R8 ; KPB address CALLS #3,G^EXE$KP_TQE_WAIT ; Fork and wait the desired time .Enable Flagging MOVL SPDT$PS_AB(SPDT),R6 ; Get Adapter Block address MOVL AB$PQ_ADRQ_HEAD(R6),R6 ; Point to ADRQ Stopper Carrier BLBC CAR$PQ_NEXT_PTR(R6),L2 ; Do we have a response yet? SOBGTR R0,L1 ; No, have we timed out? POPL R6 ; Yes, restore R6 BRW PORT_OFFLINE ; Set port offline L2: POPL R6 ; Restore R6 .ENDM WAIT_FOR_RESPONSE .PAGE ; ; BUILD_BSD ; ; This macro builds a Buffer Segment Descriptor (BSD). ; ; INPUT: ; BSD_ADDR - Address of where to build the BSD ; BUFFER_SVAPTE - System Virtual address of PTE of this buffer segment ; BUFFER_SVA - System Virtual address of this buffer segment ; BUFFER_PA - Physical address of this buffer segment ; BYTE_OFFSET - Byte offset in page to start of buffer. This field ; is valid only if BUFFER_SVAPTE is specified. ; BYTE_COUNT - Byte count for this buffer segment ; TYPE - Type of BSD ; Type 0 = Pointer to a data buffer segment. ; Type 1 = Pointer to a Buffer Segment Map (BSM). ; PRESERVE_R0 - Flag for preserving R0 across macro invocation ; YES = Preserve R0 ; NO = Do not preserve R0 ; R4 - SPDT address ; ; N.B.: exactly one of BUFFER_SVAPTE, BUFFER_SVA, and BUFFER_PS should be ; specified in any given invocation of BUILD_BSD. ; ; OUTPUT: ; BSD setup for a buffer segment. ; .MACRO BUILD_BSD BSD_ADDR, BUFFER_SVAPTE, BYTE_OFFSET, BUFFER_SVA,- BYTE_COUNT, TYPE=TP$C_TYPE0, PRESERVE_R0=NO,- BUFFER_PA .IIF IDN PRESERVE_R0, YES, PUSHL R0 ; Save R0 .IF NB BUFFER_SVAPTE .IF NB BUFFER_SVA .ERROR ; Can't use both BUFFER_SVAPTE and BUFFER_SVA in BUILD_BSD .ENDC .IF NB BUFFER_PA .ERROR ; Can't use both BUFFER_SVAPTE and BUFFER_PA in BUILD_BSD .ENDC $SVAPTE_TO_PA - ; Translate buffer SVAPTE to PA SVAPTE=BUFFER_SVAPTE,- ; R0 has PA on return BOFF = BYTE_OFFSET ADDL2 SPDT$L_DMA_BASE(R4),R0 ; add in DMA base address .ENDC .IF NB BUFFER_SVA .IF NB BUFFER_PA .ERROR ; Can't use both BUFFER_SVA and BUFFER_PA in BUILD_BSD .ENDC $SVA_TO_PA - ; Translate buffer SVA to PA SVA=BUFFER_SVA ; R0 has PA on return ADDL2 SPDT$L_DMA_BASE(R4),R0 ; add in DMA base address .ENDC .IF NB BUFFER_PA MOVL BUFFER_PA,R0 .ENDC EVAX_ZAP R0,#^XF0,R0 ; Clear the upper longword EVAX_SRL R0,SPDT$IB_XFER_ALIGN(R4),R0 ; Shift PA to adapter alignment EVAX_SLL R0,#BSD$C_PA_SHIFT,R0 ; Shift PA 2 bits left EVAX_STQ R0,BSD_ADDR ; Store data segment addr MOVL BYTE_COUNT,R0 ; Get byte count EVAX_SLL R0,#BSD$C_BYTE_COUNT_SHIFT,R0 ; Shift byte count to last word EVAX_OR BSD_ADDR,R0,BSD_ADDR ; Insert byte count into BSD .IF GT TYPE EVAX_OR BSD_ADDR,#TP$C_TYPE1,BSD_ADDR ; Make BSD a Type 1 pointer .ENDC .IIF IDN PRESERVE_R0, YES, POPL R0 ; Restore R0 .ENDM BUILD_BSD ; ; MAP_S0 ; ; This macro uses map registers to map a region of S0 space; the driver ; uses it for the Adapter Block, Queue Carriers, Queue Buffers, and pad ; blocks. ; ; If the CRCTX pointer provided has a zero value, a CRCTX is allocated ; from non-paged pool. ; ; The KPB provided is used to stall if an allocation fails. ; ; Note: this macro destroys R0 and R1. ; .MACRO MAP_S0 SVA,NBYTES,CRCTX,KPB$,DMA_ADDR,SPDT=R4,?L1,?L2,?L3 ; ; Have we allocated a CRCTX yet? ; TSTL CRCTX BNEQ L2 ; ; Nope; allocate and initialize one. ; L1: PUSHL SPDT$IS_FLCK(SPDT) ; Fork lock index. PUSHAB CRCTX ; CRCTX ref. PUSHL SPDT$PS_CRAB(SPDT) ; CRAB address CALLS #3,IOC$ALLOC_CRCTX .BRANCH_LIKELY BLBS R0,L2 ; Did we get one? KP_STALL_FORK_WAIT KPB = KPB$ ; Fork and wait BRB L1 ; Try allocation again ; ; How many map registers do we need? ; L2: EVAX_AND SVA,- ; Calculate byte within map SPDT$IS_CRCTX_BWP_MASK(SPDT),R0 ; unit. ADDL2 NBYTES,R0 ; Add byte count, plus ADDL2 SPDT$IS_CRCTX_BWP_MASK(SPDT),R0 ; a map unit to round up. MNEGL SPDT$IS_CRCTX_SHIFT(SPDT),R1 ; Convert bytes to map units. ASHL R1,R0,R1 MOVL CRCTX,R0 ADDL3 R1,#2,CRCTX$L_ITEM_CNT(R0) ; Add 2 for guard entries. ; ; Attempt to allocate the necessary map registers. Stall if immediate ; allocation fails; MAP_CALLBACK will restart us. ; MOVAB MAP_CALLBACK,- ; Set up to restart when CRCTX$L_CALLBACK(R0) ; allocation succeeds after MOVL KPB$,- ; initial failure. CRCTX$L_AUX_CONTEXT(R0) PUSHL R0 ; CRCTX address PUSHL SPDT$PS_CRAB(SPDT) ; CRAB address CALLS #2,IOC$ALLOC_CNT_RES BLBS R0,L3 ; Did we get 'em? MOVL KPB$,R0 MOVAB B^IOC$RETURN,- ; Set stall routine KPB$PS_SCH_STALL_RTN(R0) PUSHL R0 CALLS #1,EXE$KP_STALL_GENERAL ; ; At this point, one way or the other, we have the necessary map registers. ; Calculate the SVAPTE of the region, so we can map it. ; L3: MOVL SVA,R0 EVAX_SLL R0,#<64-VA$V_SYSTEM>,R0 ; Clear space select bit. EVAX_SRL R0,MMG$GQ_64SYS_SHIFT,R0 ; Get virtual page number MOVAQ @MMG$GL_SPTBASE[R0],R0 ; Get SVAPTE that maps VA ; ; Map the region, putting the DMA-able address into DMA_ADDR. ; PUSHAL DMA_ADDR ; Where to put DMA addr. EVAX_AND SVA,- ; Calculate byte-within-page SPDT$IS_CRCTX_BWP_MASK(SPDT),R1 PUSHL R1 ; Byte offset PUSHL R0 ; Buffer SVAPTE PUSHL CRCTX ; CRCTX addr PUSHL SPDT$L_ADP(SPDT) ; ADP addr CALLS #5,IOC$LOAD_MAP .ENDM ; MAP_S0 .PAGE .SBTTL CAM Status Table ; ; This table is used to map the returned CAM status to a VMS status code. ; . = <<.+3>&^C3> ; Make sure we're at least longword aligned CAM_STATUS_TABLE: .LONG SS$_CONTINUE ; Request in progress .LONG SS$_NORMAL ; Request completed w/o error .LONG SS$_ABORT ; CCB request aborted by host .LONG SS$_DEVCMDERR ; Unable to abort request .LONG SS$_NORMAL ; Completed w/error (Check cond) .LONG SS$_MEDOFL ; CAM subsystem busy .LONG SS$_CTRLERR ; CCB request is invalid .LONG SS$_BADPARAM ; Path ID supplied is invalid .LONG SS$_TIMEOUT ; SCSI device not installed .LONG SS$_ABORT ; Unable to term. CCB request .LONG SS$_TIMEOUT ; Target selection timeout .LONG SS$_TIMEOUT ; Command timeout .LONG SS$_CTRLERR ; Reserved (place holder) .LONG SS$_CTRLERR ; Message reject received .LONG SS$_MEDOFL ; SCSI bus reset sent/received .LONG SS$_PARITY ; Uncorrectable parity error .LONG SS$_CTRLERR ; Request sense command failed .LONG SS$_NOSUCHDEV ; No HBA detected .LONG SS$_DATAOVERUN ; Data overrun or underrun .LONG SS$_CTRLERR ; Unexpected SCSI bus free .LONG SS$_CTRLERR ; Target phase sequence error .LONG SS$_BADPARAM ; Invalid CCB length .LONG SS$_MEDOFL ; No requested capability .LONG SS$_MEDOFL ; SCSI BDR sent to target .LONG SS$_ABORT ; CCB Request term. by host .LONG SS$_CTRLERR ; Reserved (place holder) .LONG SS$_MEDOFL ; Bus hung, re-issue IO .PAGE .SBTTL Driver Prologue Table ; ; Driver prologue table ; . = <<.+3>&^C3> ; Make sure we're at least longword aligned DPTAB - ; Define driver prologue table ADAPTER = TURBO_SCSI,- ; Turbo channel adapter. UCBSIZE = UCB$K_PKS_LENGTH,- ; Size of a UCB. UCB_CRAMS = CRAM_COUNT,- ; CRAMs allocated by loader STEP = 2,- ; Step 2 ALPHA SMP = YES,- ; SMP supported MAXUNITS = 1,- ; Only 1 unit allowed NAME = PKSDRIVER,- ; Driver name FLAGS = ; System snapshot 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,DEVICEIPL ; Device IPL DPT_STORE UCB,UCB$L_DEVCHAR,L,- ; Device characteristics 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$_TZA_SCSI ; Device type DPT_STORE UCB,UCB$L_DEVSTS,L,- ; Device Permanent Status ; No conversion of log to phys DPT_STORE REINIT ; Control block re-init values DPT_STORE_ISR CRB$L_INTD,PKS$INTERRUPT ; ISR address DPT_STORE END .PAGE .SBTTL Driver Dispatch Table ; ; Driver dispatch table ; PK$K_MAX_SCSI_ID == 7 ; SCSIAUTO uses this definition .EXTERNAL SA$FUNCTAB ; SCSI auto configure function .EXTERNAL SA$STARTIO ; decision table and routines .EXTERNAL SA$CANCELIO ; .EXTERNAL SA$K_KP_STKSIZ ; KP stack size for auto configure .EXTERNAL SA$K_KP_REGMSK ; KP register save mask .EXTERNAL CLU$GL_ALLOCLS ; Allocation class .EXTERNAL CLU$GL_TAPE_ALLOCLS ; Tape allocation .EXTERNAL IOC$GL_NAMING ; DEVICE_NAMING parameter .EXTERNAL IOC$GL_DEVLIST ; DDB chain head .EXTERNAL SCS$GB_SYSTEMID ; SYSSTSTEMID DDTAB - ; Driver dispatch table DEVNAM = PKS,- ; Name of device START = EXE_STD$KP_STARTIO,- ; Common KP start I/O KP_STARTIO = SA$STARTIO,- ; Start I/O routine CTRLINIT = PKS$CNTRL_INIT,- ; Controller Init UNITINIT = PKS$UNIT_INIT,- ; Unit Init FUNCTB = SA$FUNCTAB,- ; Function decision table CANCEL = SA$CANCELIO,- ; Cancel I/O routine KP_STACK_SIZE = SA$K_KP_STKSIZ,-; Set KP stack size KP_REG_MASK = SA$K_KP_REGMSK,- ; Set KP register save mask REGDMP = PKS_REGDUMP,- ; Register dump routine DIAGBF = 0,- ; Diagnostic buffer size ERLGBF = 4+EMB$L_DV_REGSAV+PKS_ERROR_LEN ; Error log buf size ; ; Function decision table is located in SCSIAUTO.MAR. ; .PAGE ; ; Debug messages and other port messages to be output to OPA0 terminal. ; ; NOTE: If SGN$GL_VMSD1 (VMSD1 SYSGEN parameter) is not set, the debug ; messages will not be displayed. ; DRIVER_DATA ; Driver Data Psect CR = 13 ; ASCII for carriage return LF = 10 ; ASCII for line feed BELL = 7 ; ASCII for bell CTRLR_LETTER = 17 ; Byte offset to device ; controller letter (x) in ; the following messages. PORT_OFF_MSG: .ASCIZ /%PKSDRVR-W- PKx0, Port is going OFFLINE./ UNIT_INIT_MSG: .ASCIZ /%PKSDRVR-I- PKx0, Port (re)initialization completed./ MISC_INTR_MSG: .ASCIZ /%PKSDRVR-I- Adapter error interrupt processing.../ .PAGE .IF DEFINED PKSDEBUG ; Conditional assembly for debug ; ; Keep ring buffer for send and receive carrier/q_buffer pairs to keep track ; of last 20 VA/PA information for debug. Also timestamp each entry in ABSTIME ; unit to keep track of time spent processing a command. Thus each entry ; consists of 5 quadwords in the order of carrier VA, PA, Q_Buffer VA, PA, ; and timestamp. ; . = <<.+3>&^C3> ; Make sure we're at least longword aligned SND_RING: .LONG 0 ; SEND RING buffer for car/qbuf SND_END: .LONG 0 ; End of ring buffer SND_INDEX: .LONG 0 ; Current send location RCV_RING: .LONG 0 ; RECEIVE RING buff for car/qbuf RCV_END: .LONG 0 ; End of ring buffer RCV_INDEX: .LONG 0 ; Current receive location .ENDC .PAGE .SBTTL REV_TABLE - Legal hardware/firmware version table ; ; This table is used to validate the adapter's hardware and firmware versions ; so that the port driver may either (1) shut down the adapter or ; (2) print a warning message. ; ; The revision table has the following format: ; ; First longword: Maximum Firmware version ; Second longword: Maximum Hardware version ; ; If either the maximum Firmware or Hardware version is exceeded, then we ; assume that an experimental version of the adapter is in use and bypass all ; further checks. ; ; Each additional 3 longwords contain the following information: ; ; First longword: Legal Firmware version (-1 for end of the table) ; Second longword: Legal Hardware version ; Third longword: 0 for no warning; 1 for a warning ; ; NOTE: Firmware version entries must appear in ascending order ; . = <<.+3>&^C3> ; Make sure we're at least longword aligned REV_TABLE_TZA: .LONG 0 ; Maximum Firmware revision .LONG 0 ; Maximum Hardware revision .LONG 0,0,0 ; Firmware/Hardware/No warning .LONG -1,-1,-1 ; Patch Space .LONG -1 ; End of table REV_TABLE_PZA: .LONG 0 ; Maximum Firmware revision .LONG 0 ; Maximum Hardware revision .LONG 0,0,0 ; Firmware/Hardware/No warning .LONG -1,-1,-1 ; Patch Space .LONG -1 ; End of table .PAGE .SBTTL PKS$CNTRL_INIT - SIMport Controller Initialization Routine DRIVER_CODE ; Driver Code Psect ; ; This routine is called when PKSDRIVER is loaded or on powerfail recovery. ; For this driver, this routine is a NOP. ; ; CALLING SEQUENCE: ; CALL PKS$CNTRL_INIT ; ; INPUT: ; IPL$_POWER ; R4 = Address of IDB ; R6 = Address of DDB ; R8 = Address of CRB ; ; OUTPUT: ; ; R0 = Status ; .ENABLE LSB PKS$CNTRL_INIT: $DRIVER_CTRLINIT_ENTRY FETCH=NO MOVZWL #SS$_NORMAL,R0 ; Set success status RET ; Return .DISABLE LSB ; PKS$CNTRL_INIT .PAGE .SBTTL PKS$UNIT_INIT - SIMport Unit Initialization Routine ; ; This routine is called when PKSDRIVER is loaded or on powerfail recovery. ; It allocates and initializes the necessary resources for the SIMport SCSI ; port driver to function. ; ; This routine should be called once for each channel the SIMport adapter ; supports as follows: ; ; 1.) KZTSA (TZA) SIMport adapters have one SCSI channel/port. This routine ; should be called once. ; 2.) KZPSA (PZA) SIMport adapters have one SCSI channel/port. This routine ; should be called once. ; ; Adapter-wide resources shared by two or more channels will be allocated ; only once during the first channel's unit initialization and will remain ; allocated throughout the system's life. ; ; CALLING SEQUENCE: ; CALL PKS$UNIT_INIT ; ; INPUT: ; IPL$_POWER ; R4 = Address of IDB ; IDB$Q_CSR - TZA or PZA I/O space base address ; IDB$L_VECTOR - Pointer to IDB extension for vectors, VLE ; VLE$L_VECTOR_LIST - Interrupt vector offsets in SCB ; IDB$L_DEVICE_SPECIFIC - TZA or PZA channel number (0) ; R5 = Address of UCB ; ; OUTPUT: ; R0 = Status ; The unit is set online ; Other registers preserved ; .ENABLE LSB PKS$UNIT_INIT: $DRIVER_UNITINIT_ENTRY FETCH=YES PRESERVE= MOVL R5,IDB$PS_OWNER(R4) ; Copy our UCB address to IDB MOVL R5,IDB$PS_AUXSTRUC(R4) ; Copy our UCB address to IDB ; so that it can be used in ; interrupt routines ; ; The creation of a port must occur at FIPL holding IOLOCK8 (FORK) lock. ; This is achieved by forking before invoking the CREATE_PORT macro. ; ASSUME FKB$K_LENGTH EQ UCB$S_PK_INIFKBLK ; Be sure FKB is big enough BBSSI #UCB$V_PK_IFKB_LOCK,- ; Check to see that this FKB UCB$IL_PK_EXFLAGS(R5),10$ ; is not in use MOVAB UCB$IB_PK_INIFKBLK(R5),R5 ; Fork on alternate FKB MOVB #SPL$C_IOLOCK8,FKB$B_FLCK(R5) ; Set correct spinlock FORK ROUTINE = INIT_PORT,- ; Queue the fork block CONTINUE = 10$ 10$: MOVZWL #SS$_NORMAL,R0 ; Set success status RET ; Return .DISABLE LSB ; PKS$UNIT_INIT .PAGE .SBTTL INIT_PORT - Port Initialization Fork Routine ; ; This fork routine is called when PKSDRIVER is loaded or on powerfail recovery. ; It allocates and initializes the necessary resources for the SIMport SCSI ; port driver to function. ; ; This routine should be called once for each channel the SIMport adapter ; supports as follows: ; ; 1.) KZTSA (TZA) SIMport adapters have one SCSI channel/port. This routine ; should be called once. ; 2.) KZPSA (PZA) SIMport adapters have one SCSI channel/port. This routine ; should be called once. ; ; Adapter-wide resources shared by two or more channels will be allocated ; only once during the first channel's unit initialization and will remain ; allocated throughout the system's life. ; ; CALLING SEQUENCE: ; FORK ROUTINE = INIT_PORT ; ; INPUT: ; IPL$_IOLOCK8 ; R4 = Address of IDB ; IDB$Q_CSR - TZA or PZA I/O space base address ; IDB$L_VECTOR - Pointer to IDB extension for vectors, VLE ; VLE$L_VECTOR_LIST - Interrupt vector offsets in SCB ; IDB$L_DEVICE_SPECIFIC - TZA or PZA channel number (0) ; R5 = Address of UCB ; ; OUTPUT: ; SIMport adapter initialized ; The unit is set online ; .ENABLE LSB INIT_PORT: .JSB_ENTRY INPUT=,- SCRATCH=,- PRESERVE= MOVAB -UCB$IB_PK_INIFKBLK(R5),R5 ; Point to start of UCB BBCCI #UCB$V_PK_IFKB_LOCK,- ; This FKB is no longer busy UCB$IL_PK_EXFLAGS(R5),10$ ; Just clear and proceed 10$: MOVL R4,R3 ; Save IDB address in R3 BBC #UCB$V_POWER,- ; Skip setup of buffers and UCB$L_STS(R5),20$ ; CREATE_PORT if powerfail MOVL UCB$L_PDT(R5),R4 ; Get SPDT address BRW 60$ ; Go re-initialize the port ; ; The following section allocates and initializes all the resources necessary ; to run a SIMport SCSI channel. ; 20$: MOVZWL #SPDT$C_PKSLENGTH,R1 ; Get SPDT size to allocate CREATE_PORT ; Create and initialize an SPDT MOVL UCB$L_PDT(R5),R4 ; Get SPDT address MOVW #32,SPDT$IW_MAX_BUS_WIDTH(R4) ; Load adapter-specific max bus width MOVW #16,SPDT$IW_CONFIG_BUS_WIDTH(R4); Load adapter-specific configured bus width ; ; Set up the timeout routine to read the port counters. Port counters will ; be read each time the timeout routine is called. ; PUSHL R3 ; Save IDB address on stack MOVL UCB$L_CRB(R5),R3 ; Get CRB address of port MOVL R4,CRB$L_SCS_STRUC(R3) ; Save SPDT address in CRB MOVAB READ_COUNTER,CRB$L_TOUTROUT(R3) ; Store the address of timeout ; routine to read port counters MOVB UCB$B_FLCK(R5),CRB$B_FLCK(R3) ; Initialize the fork lock level MNEGL #1,CRB$L_DUETIME(R3) ; Disable wakeups for now POPL R3 ; Restore IDB address in R3 ; ; Initialize queues in SPDT to empty. ; MOVAL SPDT$PS_QCMDFL(R4),R0 ; Get command Q_Buffer queue ; listhead address MOVL R0,(R0) ; Initialize a queue for ; command Q_Buffers MOVL R0,4(R0) MOVAL SPDT$PS_RSPFL(R4),R0 ; Get response Q_Buffer queue ; listhead address MOVL R0,(R0) ; Initialize a queue for ; response Q_Buffers MOVL R0,4(R0) MOVAL SPDT$PS_DAFQFL(R4),R0 ; Get free Q_Buffer queue ; listhead address MOVL R0,(R0) ; Initialize a queue for free ; Q_Buffers MOVL R0,4(R0) MOVAL SPDT$PS_WCMDFL(R4),R0 ; Get SCSI cmd completion wait ; queue listhead address MOVL R0,(R0) ; Initialize the queue MOVL R0,4(R0) MOVAL SPDT$PS_PCMDFL(R4),R0 ; Get port cmd completion wait ; queue listhead address MOVL R0,(R0) ; Initialize the queue MOVL R0,4(R0) MOVAL SPDT$PS_CARFL(R4),R0 ; Get Carrier queue listhead MOVL R0,(R0) ; Initialize the queue MOVL R0,4(R0) MOVAL SPDT$PS_CARWQFL(R4),R0 ; Get Carrier wait queue head MOVL R0,(R0) ; Initialize the queue MOVL R0,4(R0) ; ; Set up miscellaneous port related stuff. ; .IF DEFINED PKSHISTORY MOVAL SPDT$IS_CARRIER_HIST(R4),- SPDT$IS_CARRIER_PTR(R4) MOVAL SPDT$IS_BUFFER_HIST(R4),- SPDT$IS_BUFFER_PTR(R4) .ENDC MOVL #,- ; Intelligent SCSI adapter SPDT$L_PORT_FLAGS(R4) MOVL IDB$L_DEVICE_SPECIFIC(R3),- ; Save channel # of port SPDT$IS_CHANNEL(R4) MOVL UCB$L_DDB(R5),R0 ; Get DDB address SUBB3 #^A'A',- ; Translate controller letter to DDB$T_NAME+3(R0),- ; SCSI bus # and save it in the SPDT$L_SCSI_PORT_ID(R4) ; SPDT ; ; Set up inquiry data for target mode. ; Allocate some pool for the inquiry data and zero the block. ; 30$: SUBL #8,SP ; Allocate two lw on stack MOVL SP,R2 ; Store address of lw to receive ; address of allocated block ADDL3 #4,R2,R6 ; Store address of lw to receive ; size of allocated block PUSHL R6 ; Arg 4 = addr to receive size PUSHL R2 ; Arg 3 = addr to receive addr ; of allocated block PUSHL #ALN_LONG ; Arg 2 = 4 byte alignment PUSHL #INQ$C_LENGTH ; Arg 1 = size of block CALLS #4,EXE$ALONONPAGED_ALN ; Allocate a block POPL R2 ; Save addr of allocated block POPL R6 ; Save allocated size BLBS R0,35$ ; Did we get the block? KP_STALL_FORK_WAIT KPB=R8,- ; No, fork and wait FKB=KPB$PS_UCB(R8) ; using UCB fork block BRB 30$ ; Try allocation again 35$: MOVL R2,SPDT$PS_INQUIRY_DATA(R4) ; Save inquiry data address PUSHR #^M ; Save registers MOVC5 #0,(SP),#0,R6,(R2) ; Zero initialize the block POPR #^M ; Restore registers ; ; Initialize static fields in the inquiry data. R2 points to buffer. ; MOVW R6,INQ$IW_SIZE(R2) ; Structure size MOVB #DYN$C_MISC,INQ$IB_TYPE(R2) ; Set buffer type MOVB #DYN$C_QBUF,INQ$IB_SUBTYPE(R2) ; Set buffer subtype MOVB #INQ$C_PROC_DEV,- ; Peripheral device type INQ$IB_PERIPH_DEV_TYPE(R2) MOVB #INQ$C_COMPLIES_TO_SCSI2,- ; ANSI version INQ$IB_ANSI_VERSION(R2) MOVB #INQ$C_SCSI2_FORMAT,- ; Response data format INQ$IB_RESP_DATA_FORMAT(R2) MOVB #INQ$C_ADDITIONAL_LEN,- ; Additional length INQ$IB_ADDITIONAL_LEN(R2) .Disable Flagging ; Turn off info. it's aligned MOVQ #^A/DEC /,- ; Vendor ID INQ$IB_VENDOR_ID(R2) MOVQ #^A/OpenVMS /,- ; Product ID INQ$IB_PRODUCT_ID(R2) .Enable Flagging MOVL SYS$GQ_VERSION,- ; Product revision INQ$IS_PRODUCT_REV(R2) MOVL UCB$L_DDB(R5),R0 ; Get DDB address MOVB DDB$T_NAME+3(R0),- ; Put ASCII port letter into INQ$IB_PRODUCT_ID+8(R2) ; Product ID MOVB G^CLU$GL_ALLOCLS,- ; Put allocation class into INQ$IB_PRODUCT_ID+9(R2) ; Product ID MOVB G^CLU$GL_TAPE_ALLOCLS,- ; Put tape allocation class INQ$IB_PRODUCT_ID+10(R2) ; into Product ID ; (X-16) If new naming and this port has a port allocation class defined, ; overwrite 8 bytes of the above data with a new naming format message. ; The port allocation class is stored in the port DDB. BLBC IOC$GL_NAMING,40$ ; If old naming, "never mind" MOVL SPDT$L_PORT_UCB(R4),R6 ; Get port UCB MOVL UCB$L_DDB(R6),R0 ; Get port DDB BBC #DDB$V_PAC,- ; If no PAC, nothing to do DDB$B_FLAGS(R0),40$ ; MOVL SCS$GB_SYSTEMID,- INQ$IB_PRODUCT_ID+8(R2) ; Save SCSSYSTEMID MOVL DDB$L_ALLOCLS(R0),- INQ$IB_PRODUCT_ID+12(R2); Save ALLOCLS ; ; Set up Sense data for target mode. ; Allocate some pool for the Sense data and zero the block. ; 40$: SUBL #8,SP ; Allocate two lw on stack MOVL SP,R2 ; Store address of lw to receive ; address of allocated block ADDL3 #4,R2,R6 ; Store address of lw to receive ; size of allocated block PUSHL R6 ; Arg 4 = addr to receive size PUSHL R2 ; Arg 3 = addr to receive addr ; of allocated block PUSHL #ALN_LONG ; Arg 2 = 4 byte alignment PUSHL #SENSE$C_LENGTH ; Arg 1 = size of block CALLS #4,EXE$ALONONPAGED_ALN ; Allocate a block POPL R2 ; Save addr of allocated block POPL R6 ; Save allocated size BLBS R0,45$ ; Did we get the block? KP_STALL_FORK_WAIT KPB=R8,- ; No, fork and wait FKB=KPB$PS_UCB(R8) ; using UCB fork block BRB 40$ ; Try allocation again 45$: MOVL R2,SPDT$PS_SENSE_DATA(R4) ; Save sense data address PUSHR #^M ; Save registers MOVC5 #0,(SP),#0,R6,(R2) ; Zero initialize the block POPR #^M ; Restore registers ; ; Initialize static fields in the Sense data. R2 points to buffer. ; MOVW R6,SENSE$IW_SIZE(R2) ; Structure size MOVB #DYN$C_MISC,SENSE$IB_TYPE(R2) ; Set buffer type MOVB #DYN$C_QBUF,SENSE$IB_SUBTYPE(R2) ; Set buffer subtype MOVB #SENSE$C_CURRENT_ERROR,- ; Setup error code SENSE$IB_ERROR_CODE(R2) MOVB #SENSE$C_ILLEGAL_REQUEST,- ; Setup Sense key SENSE$IB_SENSE_KEY(R2) MOVB #SENSE$C_ADDITIONAL_LEN,- ; Additional sense length SENSE$IB_ADDITIONAL_LEN(R2) MOVB #SENSE$C_LUN_NOT_SUPPORTED,- ; Additional sense code SENSE$IB_ADD_SENSE_CODE(R2) ; ; Discriminate TZA from PZA, based (for now) on the type of bus we're on. ; CLRL SPDT$IS_ADAPTER_TYPE(R4) ; Assume KZTSA MOVL IDB$PS_ADP(R3),R2 ; Get ADP address MOVL ADP$PS_BUS_ARRAY(R2),R2 ; Get BUS_ARRAY address CMPL BUSARRAY$L_BUS_TYPE(R2),- ; Check bus type #BUS$_PCI BNEQ 50$ ; Are we on a PCI bus (KZPSA)? MOVL #SPDT$C_PCI,- ; Yes, indicate so SPDT$IS_ADAPTER_TYPE(R4) ; ; This port driver will impose a maximum transfer size of 64KB for now. ; The class driver will use this value to segment all IO's to be ; less than or equal to MAXBCNT. ; 50$: MOVZWL #PKS_MAXBCNT,- ; Setup maximum segment size SPDT$L_MAXBYTECNT(R4) ; ; If this is the first time the adapter is being initialized, then allocate ; Adapter wide resources. Resources include Queue Buffers, Queue Carriers, ; the Adapter Block, I/O mailboxes (CRAMS), and a KPB for (re)initializing ; the port. BBS #SPDT$V_STRUCT_ALLOCATED,- ; Skip adapter resource init SPDT$IS_PORTSTS(R4),CHNL_SETUP ; if already done ; ; First allocate the command Queue Buffers and mark command slots as free. ; CLRQ -(SP) ; Create temporary buffer for ; return buffer and length PUSHAL 4(SP) ; Buffer address for SPDT addr PUSHAL 4(SP) ; Buffer address for length PUSHL #QUEUE_BUFFER_POOL_SIZE ; Buffer length PUSHL R4 ; SCSI Port Descriptor Table CALLS #4, @SPDT$PS_RL_POOL_ALLOC(R4) ; Allocate command buffers MOVQ (SP)+, R1 ; Allocation length and address BLBC R0,80$ ; Did we get the pool? MOVL R4,QBUF$PS_FLINK(R2) ; Yes, Setup FLINK MOVL R4,QBUF$PS_BLINK(R2) ; Setup BLINK MOVW R1,QBUF$IW_SIZE(R2) ; Save Queue Buffer size MOVB #DYN$C_MISC,QBUF$IB_TYPE(R2) ; Set buffer type MOVB #DYN$C_QBUF,QBUF$IB_SUBTYPE(R2) ; Set buffer subtype ADDL2 #HEADER_SIZE,R2 ; Point past header info MOVL R2,SPDT$PS_CMD_BASE(R4) ; DMA area and CMD area are one ; and the same if using mapping ; registers. CLRL SPDT$IL_CMD_BITS(R4) ; Mark command slots as free CLRL SPDT$IL_CMD_BITS+4(R4) CLRL SPDT$IL_CMD_BITS+8(R4) CLRL SPDT$IL_CMD_BITS+12(R4) ; ; Map the CSRs, and set up the CRAMs. ; BSBW MAP_CSRS ; ; Setup vectors in SPDT to point to PKS specific mapping routines. The ; _MR versions use DMA map registers. ; BITL #SPDT$M_PFLG_MAPPING_REG,- ; Are we using mapping regs? SPDT$L_PORT_FLAGS(R4) BEQL 43$ MOVAL PKS$MAP_BUFFER_MR,- ; Map Buffer routine w/regs SPDT$PS_CD_BUFFER_MAP(R4) MOVAL PKS$UNMAP_BUFFER_MR,- ; Unmap Buffer routine w/regs SPDT$PS_CD_BUFFER_UNMAP(R4) BRB 46$ 43$: MOVAL PKS$MAP_BUFFER,- ; Map Buffer routine SPDT$PS_CD_BUFFER_MAP(R4) MOVAL PKS$UNMAP_BUFFER,- ; Unmap Buffer routine SPDT$PS_CD_BUFFER_UNMAP(R4) ; ; Now allocate the Carriers and Adapter Block, and initialize the queues. ; 46$: BSBW ALLOC_CARRIERS ; Allocate Carriers BLBC R0,80$ ; Do not initialize port if we ; did not get Carriers BSBW ALLOC_AB ; Allocate the Adapter Block BLBC R0,80$ ; Do not initialize port if we ; did not get an Adapter Block BSBW SETUP_QUEUES ; Set up queues in AB BLBC R0,80$ ; Do not initialize port if we ; did not get a SPTEs BSBW SETUP_CRAMS ; Set up the CRAMs ; ; ALlocate a PTE for mapping the user data buffer for unaligned data transfers. ; MOVL #1,R2 ; Need 1 PTE JSB G^LDR$ALLOC_PT ; Get a PTE MOVL R1,SPDT$L_SPTE_SVAPTE(R4) ; Save it in SPDT SUBL2 G^MMG$GL_SPTBASE,R1 ; Get offset into page table ASHL #-PTE$C_SHIFT_SIZE,R1,R1 ASHL G^MMG$GL_VPN_TO_VA,R1,R1 ; ... virtual address BISL3 #VA$M_SYSTEM,R1, - ; Set bit for system address SPDT$L_SPTE_BASE(R4) ; ; For channel initialization, we need to allocate a KPB (Kernel Process Block) ; to be used for (re)initializing the port. The KPB pointer will be kept in ; the SPDT for easy access. ; KP_ALLOCATE_KPB KPB=SPDT$PS_KPB(R4),- ; Allocate KPB for init process STACK=G^MMG$GL_PAGE_SIZE,- ; Size of kernel process stack FLAGS=#KPB_FLAGS ; Make permanent KPB BLBC R0,80$ ; Do not initialize port if we ; did not get a KPB BISL #SPDT$M_STRUCT_ALLOCATED,- ; Set flag that adapter-wide SPDT$IS_PORTSTS(R4) ; resources are allocated now CHNL_SETUP: ; ; Set up pointer in SPDT to the port counters area for this channel. ; MOVL SPDT$PS_AB(R4),R0 ; Get the Adapter Block address MOVAB AB$PS_COUNTERS(R0),- ; Save the address of the SPDT$PS_COUNTERS(R4) ; channel port counters area ; ; (Re-)initialize the port. ; 60$: MOVL SPDT$PS_KPB(R4),R0 ; Get KPB address MOVL R5,KPB$PS_UCB(R0) ; Save port's UCB addr in KPB MOVL R3,KPB$PS_SCSI_PTR1(R0) ; Save IDB addr in KPB KP_START KPB = R0, - ; Call Kernel Process routine REGISTERS = #PKS_KP_REGMSK, - ; to reinit the port ROUTINE = REINIT_PORT ;; BSBW SETUP_SANITY_TQE ; Set up TQE for adapter sanity 70$: CLRL UCB$L_FPC(R5) ; Clear fork semaphore RSB ; Return from INIT_PORT ; ; If we got here, then we failed allocating a resource. Do not set port online ; but do remember that it failed initialization. ; 80$: BISL #SPDT$M_STS_FAILED,- ; Initialization failed SPDT$L_STS(R4) BICL #UCB$M_ONLINE,UCB$L_STS(R5) ; Clear UCB online BICL #SPDT$M_STS_ONLINE,- ; Clear SPDT online SPDT$L_STS(R4) BSBW DEALLOC_RESOURCES ; Deallocate acquired resources BRB 70$ ; Return .DISABLE LSB ; INIT_PORT .PAGE .SBTTL ALLOC_CARRIERS - Allocate the Queue Carriers ; ; This subroutine allocates the Queue Carriers. Carriers are linked onto ; the SPDT SPDT$PS_CARFL and SPDT$PS_CARBL links. ; ; Static fields in the Carriers are also initialized in this routine. ; ; INPUT: ; IPL$_IOLOCK8 ; R4 = SPDT address ; ; OUTPUT: ; R0 = SS$_NORMAL - Successful completion ; SS$_INSFMEM - No memory for Queue Carriers ; R4 = SPDT address ; SPDT$PS_CARFL, SPDT$PS_CARBL Forward and backward link to Carriers ; .ENABLE LSB ALLOC_CARRIERS: .JSB_ENTRY INPUT=,OUTPUT=, - SCRATCH=,PRESERVE= MOVL #CAR$C_NUM_CARRIERS,R8 ; Set up loop counter ; ; Allocate some pool for a Carrier and zero the block. ; 10$: SUBL #8,SP ; Allocate two lw on stack MOVL SP,R2 ; Store address of lw to receive ; address of allocated block ADDL3 #4,R2,R6 ; Store address of lw to receive ; size of allocated block PUSHL R6 ; Arg 4 = addr to receive size PUSHL R2 ; Arg 3 = addr to receive addr ; of allocated block PUSHL #ALN_HEXWORD ; Arg 2 = 32 byte alignment PUSHL #CAR$C_LENGTH ; Arg 1 = size of block CALLS #4,EXE$ALONONPAGED_ALN ; Allocate a block POPL R2 ; Get addr of allocated block POPL R6 ; Get allocated size BLBS R0,20$ ; Did we get the block? MOVZWL #SS$_INSFMEM,R0 ; No, insufficient memory BRB 30$ ; Return error 20$: PUSHR #^M ; Save registers MOVC5 #0,(SP),#0,R6,(R2) ; Zero initialize the block POPR #^M ; Restore registers ; ; Fill in static fields of the Carrier. ; MOVW R6,CAR$IW_SIZE(R2) ; Set the allocated block size MOVB #DYN$C_MISC,CAR$IB_TYPE(R2) ; Set type MOVB #DYN$C_CAR,CAR$IB_SUBTYPE(R2) ; Set subtype MOVL R2,CAR$PQ_CAR_TOKEN(R2) ; Save Carrier VA for port use MOVAB CAR$PQ_NEXT_PTR(R2),R6 ; VA of adapter-visible section $SVA_TO_PA SVA = R6 ; PA of the carrier address ADDL2 SPDT$L_DMA_BASE(R4),R0 ; add in DMA window base EVAX_ZAP R0,#^XF0,R0 ; Clear the upper longword EVAX_STQ R0,CAR$PQ_PA(R2) ; Save PA ; ; Place this Carrier onto a doubly linked list pointed to by SPDT$PS_CARFL ; and SPDT$PS_CARBL. ; INSERT_EL - EL = (R2),- ; Link this Carrier into QUE = @SPDT$PS_CARBL(R4),- ; the SPDT SCRATCH = R0 SOBGTR R8,10$ ; Link next Carrier MOVZWL #SS$_NORMAL,R0 ; Set success status 30$: RSB ; Return .DISABLE LSB ; ALLOC_CARRIERS .PAGE .SBTTL ALLOC_AB - Allocate the Adapter Block ; ; This subroutine allocates the Adapter Block (AB). The adapter requested ; size of the AB is 80 bytes. We allocate 316 bytes of memory for the Adapter ; Block. The port driver uses the remaining free memory area for various ; internal variables and to keep the running total of port counters. ; ; The Adapter Block must be port page aligned (8 KB aligned). ; ; Static fields in the AB are also initialized in this routine. The AB is ; allocated once for a SIMport adapter and will not be deallocated for the ; duration of the system. ; ; INPUT: ; IPL$_IOLOCK8 ; R4 = SPDT address ; ; OUTPUT: ; R0 = SS$_NORMAL - Successful completion ; SS$_INSFMEM - No memory for Adapter Block ; R4 = SPDT address ; SPDT$PS_AB - Address of the allocated Adapter Block ; .ENABLE LSB ALLOC_AB: .JSB_ENTRY INPUT=,OUTPUT=, - SCRATCH=,PRESERVE= ; ; Allocate and zero the memory for the adapter block. ; SUBL #4,SP ; Allocate a longword on stack PUSHL SP ; Arg 4 = Addr to receive size PUSHAB SPDT$PS_AB(R4) ; Arg 3 = Addr to receive addr ; of allocated AB PUSHL #ALN_HEXWORD ; Arg 2 = Hexword alignment PUSHL #AB$C_SIZE ; Arg 1 = Block size CALLS #4,EXE$ALONONPAGED_ALN ; Allocate adapter block POPL R6 ; Get the allocated size BLBS R0,10$ ; Did we get the block? MOVZWL #SS$_INSFMEM,R0 ; No, insufficient memory for AB BRB 40$ ; Return error 10$: MOVL SPDT$PS_AB(R4),R2 ; Get the address of AB PUSHR #^M ; Save registers MOVC5 #0,(SP),#0,R6,(R2) ; Zero initialize the block POPR #^M ; Restore registers ; ; Put physical address of adapter block in the adapter block. Allocate and ; use map registers if needed. ; $SVA_TO_PA SVA = R2 BITL #SPDT$M_PFLG_MAPPING_REG,- ; Are we using map regs? SPDT$L_PORT_FLAGS(R4) BEQL 20$ ; Nope; just use PA. ; ; Is the AB within the direct-DMA window? Note that even if we allocated ; a larger-than-AB$C_SIZE block, only that much has to be visible to the ; adapter; we can safely use that size to calculate the ending PA. ; EVAX_ADDQ #AB$C_SIZE,R0,R3 ; Yes, point to last byte in AB EVAX_CMPULE R3,SPDT$IQ_DMA_SIZE(R4),R7 ; Compare to DMA window size BLBS R7,20$ ; Branch if LE window size ; ; O.K., we hafta map it. Can't use MAP_S0, 'cause we're not executing in ; a KP, so we can't stall if we don't get the memory or registers. ; ; Allocate and init a CRCTX. ; PUSHL SPDT$IS_FLCK(R4) ; Fork lock index PUSHAB AB$PS_CRCTX(R2) ; CRCTX ref. PUSHL SPDT$PS_CRAB(R4) ; CRAB address CALLS #3,IOC$ALLOC_CRCTX BLBC R0,40$ ; Did we get one? ; ; How many map registers do we need? ; EVAX_AND R2,- ; Calculate byte within map SPDT$IS_CRCTX_BWP_MASK(R4),R0 ; unit ADDL2 #AB$C_SIZE,R0 ; Add byte count, plus ADDL2 SPDT$IS_CRCTX_BWP_MASK(R4),R0 ; a map unit to round up. MNEGL SPDT$IS_CRCTX_SHIFT(R4),R1 ; Convert bytes to map units. ASHL R1,R0,R1 MOVL AB$PS_CRCTX(R2),R0 ADDL3 R1,#2,CRCTX$L_ITEM_CNT(R0) ; Add 2 for guard entries. ; ; Attempt to allocate the necessary map registers. Give up (return error) ; if allocation fails. ; PUSHL R0 ; CRCTX address PUSHL SPDT$PS_CRAB(R4) ; CRAB address CALLS #2,IOC$ALLOC_CNT_RES BLBC R0,40$ ; Did we get them? ; ; At this point, we have the necessary map registers. ; Calculate the SVAPTE of the AB, so we can map it. ; EVAX_SLL R2,#<64-VA$V_SYSTEM>,R0 ; Clear space select bit. EVAX_SRL R0,MMG$GQ_64SYS_SHIFT,R0 ; Get virtual page number MOVAQ @MMG$GL_SPTBASE[R0],R0 ; Get SVAPTE that maps VA ; ; Map the region, putting the DMA-able address into AB$PQ_PA. ; PUSHAL AB$PQ_PA(R2) ; Where to put DMA addr. EVAX_AND R2,- ; Calculate byte-within-page MMG$GL_BWP_MASK,R1 PUSHL R1 ; Byte offset PUSHL R0 ; Buffer SVAPTE PUSHL AB$PS_CRCTX(R2) ; CRCTX addr PUSHL SPDT$L_ADP(R4) ; ADP addr CALLS #5,IOC$LOAD_MAP BLBC R0,40$ BRB 30$ ; ; No mapping registers needed, just use PA (+ DMA base). ; 20$: ADDL2 SPDT$L_DMA_BASE(R4),R0 ; Add in DMA window base EVAX_ZAP R0,#^XF0,R0 ; Clear the upper longword EVAX_STQ R0,AB$PQ_PA(R2) ; Save it in the Adapter Block ; ; Set up static fields in the adapter block. ; 30$: MOVW #AB$C_QB_SIZE,AB$IW_QB_SIZE(R2) ; Set up queue buffer size MOVB #AB$C_CCB_PTR_SIZE,- ; Set up CCB pointer size AB$IB_CCB_PTR_SIZE(R2) MOVAL AB$PS_CAR_FLINK(R2),R0 ; Carrier queue listhead addr. MOVL R6,AB$IW_SIZE(R2) ; Save allocation size MOVL R0,(R0) ; Initialize forward link and MOVL R0,4(R0) ; backward link to empty MOVZWL #SS$_NORMAL,R0 ; Set success status 40$: RSB ; Return .DISABLE LSB ; ALLOC_AB .PAGE .SBTTL SETUP_CRAMS - I/O Mailbox Initialization ; This subroutine sets up and initializes all of the I/O Mailboxes (CRAMs) ; necessary for accessing all SIMport adapter registers. ; ; The required number of have been previously allocated (by the DPT ; from driver loading) and are pointed to by the UCB (UCB$PS_CRAM). For each ; CRAM, this routine performs the following: ; ; 1.) Points an SPDT extension location to the CRAM ; 2.) Initializes the CRAM with a system specific command ; ; The CRAMs are not removed the UCB list. Upon completion of this routine, ; the CRAMs are pointed to by the UCB (UCB$PS_CRAM) and the SPDT ; (SPDT$PS_cram_ptr_name). ; ; CALLING SEQUENCE: ; BSBW SETUP_CRAMS ; ; INPUT: ; IPL$_IOLOCK8 ; R3 = IDB address ; R4 = SPDT address ; R5 = UCB address ; ; OUTPUT: ; R0 = Return status ; SS$_NORMAL - Successful completion ; SS$_INSFMEM - Insufficient memory for CRAMs. Device should be ; set offline. ; R4 = SPDT address ; SPDT$PS_AB - Address of the allocated CRAMs ; CRAMS linked from the SPDT and initialized ; .ENABLE LSB SETUP_CRAMS: .JSB_ENTRY INPUT=,OUTPUT=, - SCRATCH=,PRESERVE= MOVAB CRAMCMD_TABLE,R2 ; Table of CRAM pointers MOVL UCB$PS_ADP(R5),R6 ; Get ADP address MOVL UCB$PS_CRAM(R5),R7 ; Point to preallocated CRAMs BNEQ 5$ ; Do we have any CRAMs? MOVZWL #SS$_INSFMEM,R0 ; No, return and set offline BRB 20$ 5$: MOVL SPDT$IS_ADAPTER_TYPE(R4),R9 ; Set up adapter type 10$: ADDL3 TBL$IS_SPDTOFF(R2),R4,R0 ; SPDT extension for CRAM ptr MOVL R7,(R0) ; Point SPDT to CRAM ADDL3 TBL$IS_IOHANDLEOFF(R2),R4,R8 ; SPDT extension for IOHANDLE ; ; Initialize this CRAM with a system specific command. ; CRAM_CMD INDEX = TBL$IS_INDEX(R2),- ; Command index OFFSET = TBL$IS_CSROFF(R2)[R9],-; CSR offset from IDB$Q_CSR ADP = R6,- ; ADP address CRAM = R7,- ; CRAM address COMMAND = R8 ; IOHANDLE address MOVL CRAM$L_FLINK(R7),R7 ; Point to next CRAM in list ADDL2 #TBL$C_ENTRY_SIZE,R2 ; Get next table entry TSTL TBL$IS_SPDTOFF(R2) ; Are we at end of table? BNEQ 10$ ; If not, initialize next CRAM MOVZWL #SS$_NORMAL,R0 ; Return success status 20$: RSB .DISABLE LSB ; SETUP_CRAMS .PAGE .SBTTL MAP_CSRS - Map CSRs ; This subroutine maps the CSRs accessed by this driver to virtual address ; space. There are three pages required. For the TZA, the pages are as follows: ; ; (Base physical address of I/O space for slot + ^XD0000) ; (Base physical address of I/O space for slot + ^XE0000) ; (Base physical address of I/O space for slot + ^XF0000) ; ; For the PZA, the pages are as follows: ; ; (Base physical address of I/O space for slot + ^X100) ; (Base physical address of I/O space for slot + ^X300) ; (Base physical address of I/O space for slot + ^X000) ; ; Since the pages span so much physical address space (121 8k pages in the ; TZA case), we use three separate IOHANDLES, and three calls to IOC$MAP_IO, ; to map them. ; ; IDB$Q_CSR is also setup with the SVA of CSR base address. ; ; CALLING SEQUENCE: ; BSBW MAP_CSRS ; ; INPUT: ; IPL$_IOLOCK8 ; R3 = IDB address ; R4 = SPDT address ; R5 = UCB address ; ; OUTPUT: ; R0 = Return status ; SS$_NORMAL - Successful completion ; SS$_INSFSPTS - Insufficient SPTEs for CSRS. Device should be ; set offline. ; R3 = IDB address ; IDB$Q_CSR - Base VA of the CSRs ; CSRs mapped. ; .ENABLE LSB MAP_CSRS: .JSB_ENTRY INPUT=,OUTPUT=,SCRATCH=, - PRESERVE= ; ; Save our ADP address and bus node number for MAP_IO. ; MOVL IDB$PS_ADP(R3),R7 ; ADP address MOVL UCB$L_CRB(R5),R2 ; Point to CRB MOVL CRB$L_NODE(R2),R2 ; Extract Node number ; ; On the PCI bus, we need to determine the bus physical address where the ; CSR's reside. On Turbochannel, addressing is slot-relative, so we don't need ; to do this. ; CLRL SPDT$L_DMA_BASE(R4) .Disable Flagging ; Turn off info. it's aligned CLRQ SPDT$IQ_PHYS_ADDR(R4) ; For TC offsets relative to .Enable Flagging MOVL #^XD0000,R6 ; zero, and first page @D0000 TSTL SPDT$IS_ADAPTER_TYPE(R4) ; 0=TC, 1=PCI BEQL 10$ MOVL #^X100,R6 ; For PCI first page @100 ; ; See if the Direct DMA window can map all of physical memory. If it can, ; we need not mess with allocating and loading map registers. ; ; If the call fails, we assume that the bus can access all of memory ; directly. ; CLRQ SPDT$IQ_DMA_SIZE(R4) ; Clear all bits PUSHAQ SPDT$IQ_DMA_SIZE(R4) PUSHL #IOC$K_DIRECT_DMA_SIZE PUSHL UCB$L_CRB(R5) ; CRB address CALLS #3,G^IOC$NODE_DATA BLBC R0,7$ EVAX_SLL SPDT$IQ_DMA_SIZE(R4),#20,R1 ; Convert MB to bytes EVAX_STQ R1,SPDT$IQ_DMA_SIZE(R4) MOVL ADP$L_CRAB(R7),- ; Save CRAB pointer for SPDT$PS_CRAB(R4) ; possible mapping operations. BEQL 7$ ; Branch if no regs available ASHL G^MMG$GL_VA_TO_VPN,R1,R1 ; Convert bytes to pages MOVL MMG$GL_MAXPFN,R8 ; Get max PFN number INCL R8 ; plus one CMPL R1,R8 ; Compare with phys. mem. BGEQU 7$ ; Big enough? BISL #SPDT$M_PFLG_MAPPING_REG,- ; Nope, we have to use mapping SPDT$L_PORT_FLAGS(R4) ; regs. ; ; Now find out where in bus address space the window lives. We'll need to ; add this value to all physical addresses passed to the adapter for DMA ; operations. 7$: PUSHAL SPDT$L_DMA_BASE(R4) PUSHL #IOC$K_DIRECT_DMA_BASE PUSHL UCB$L_CRB(R5) ; CRB address CALLS #3,G^IOC$NODE_DATA ; ; Set up to call IOC$READ_PCI_CONFIG to get memory base register. This ; tells us where *in bus space* the CSR's live. ; 9$: CLRQ IDB$Q_CSR(R3) PUSHAB IDB$Q_CSR(R3) ; Where to put PA PUSHL #4 ; # bytes to read PUSHL #16 ; Offset in config space (BAR 0) PUSHL R2 ; Bus node number PUSHL IDB$PS_ADP(R3) ; Point to ADP CALLS #5,IOC$READ_PCI_CONFIG ; Get PCI mem space PA BLBC R0,20$ ; Don't init if can't read ; ; PCI defines the lower nybble for its own use. ; BICL2 #^XF,IDB$Q_CSR(R3) ; ; Map the SIMport registers page. ; 10$: MAP_PAGE - NAME = REG,- ; Use the REG IOHANDLE OFFSET = R6,- ; Offset of reg page NODE = R2,- ; Bus node no ADP = R7 ; ADP address BLBC R0,40$ ; ; Map the CLEAR_INT page. ; MOVL #^XE0000,R6 ; TC: second page @E0000 TSTL SPDT$IS_ADAPTER_TYPE(R4) ; 0=TC, 1=PCI BEQL 20$ MOVL #^X300,R6 ; PCI: second page @300 20$: MAP_PAGE - NAME = CLEAR_INT,- ; Use the CLEAR_INT IOHANDLE OFFSET = R6,- ; Offset of CLEAR_INT page NODE = R2,- ; Bus node number ADP = R7 ; ADP address BLBC R0,40$ ; ; Map the RESET page. ; MOVL #^XF0000,R6 ; TC: third page @F0000 TSTL SPDT$IS_ADAPTER_TYPE(R4) ; 0=TC, 1=PCI BEQL 30$ MOVL #^X0,R6 ; PCI: third page @0 30$: MAP_PAGE - NAME = RESET,- ; Use the RESET IOHANDLE OFFSET = R6,- ; Offset of CLEAR_INT page NODE = R2,- ; Bus node nunmber ADP = R7 ; ADP address BLBC R0,40$ MOVZWL #SS$_NORMAL,R0 ; Success status 40$: RSB ; Return .DISABLE LSB ; MAP_CSRS .PAGE .SBTTL DEALLOC_RESOURCES - Return acquired Initialization Resources ; This subroutine deallocates any resources (if any) that the driver has ; acquired so far. This routine is called if the port is being set offline. ; ; Resources may include: ; 1.) Queue Buffers ; 2.) Carriers ; 3.) Adapter Block ; 4.) Target Mode resources ; ; CALLING SEQUENCE: ; BSBW DEALLOC_RESOURCES ; ; INPUT: ; IPL$_IOLOCK8 ; R4 = SPDT address ; R5 = UCB address ; ; OUTPUT: ; R0 = Return status ; SS$_NORMAL - Successful completion ; .ENABLE LSB DEALLOC_RESOURCES: .JSB_ENTRY INPUT=,OUTPUT=, - SCRATCH=,PRESERVE= ; ; First return the Command Queue Buffers. ; MOVL SPDT$PS_CMD_BASE(R4),R2 ; Get Queue Buffer base address BEQL 10$ ; Do we have any Buffers? SUBL2 #HEADER_SIZE,R2 ; Yes, point to header info MOVL QBUF$IW_SIZE(R2),R1 ; Get block size PUSHL R4 ; SCSI Port Descriptor Table CALLS #1,@SPDT$PS_RL_POOL_DEALLOC(R4) ; Deallocate command buffers ; ; Return the Carriers. The Carriers have been inserted onto a doubly linked ; list in the SPDT pointed to by SPDT$PS_CARFL. ; ; Assumption here is that none of the carriers is in use, so we don't have ; to deallocate any map registers. ; 10$: MOVAL SPDT$PS_CARFL(R4),R6 ; Address of Carrier wait queue 20$: REMOVE_HEAD - ; Get address of the next QUE = R6,- ; Carrier OUT_EL = R0,- EMPTY_ADDR = 30$,- ; Branch if no more entries SCRATCH = R1 MOVL R0,R2 MOVL CAR$PS_CRCTX(R0),R7 ; Have we allocated a CRCTX? BEQL 29$ PUSHL R7 ; Now deallocate the CRCTX. CALLS #1,G^IOC$DEALLOC_CRCTX ; ; Now deallocate the carrier. ; 29$: MOVL R2,R0 MOVZWL CAR$IW_SIZE(R0),R1 ; Get size field JSB EXE$DEANONPGDSIZ ; Deallocate this Carrier BRB 20$ ; Get next Carrier in the queue ; ; Now return the Adapter Block. The Adapter Block is pointed to by the SPDT. ; 30$: MOVL SPDT$PS_AB(R4),R0 ; Get the address of AB BEQL 40$ ; Exit if no AB MOVL AB$PS_CRCTX(R0),R7 ; Have we allocated a CRCTX? BEQL 39$ PUSHL R7 ; Yep, deallocate the regs. PUSHL SPDT$PS_CRAB(R4) CALLS #2,G^IOC$DEALLOC_CNT_RES PUSHL R7 ; Now deallocate the CRCTX. CALLS #1,G^IOC$DEALLOC_CRCTX 39$: MOVL SPDT$PS_AB(R4),R0 MOVL #AB$C_SIZE,R1 ; Get AB size JSB EXE$DEANONPGDSIZ ; Deallocate AB ; ; If Target Mode has been enabled, deallocate all Target Mode resources. ; 40$: BBCC #SPDT$V_TARGET_MODE,- ; Is Target Mode enabled? SPDT$IS_PORTSTS(R4),50$ BSBW RET_TARGET_MODE_RESOURCES ; Yes, return resources 50$: RSB ; Return .DISABLE LSB ; DEALLOC_RESOURCES .PAGE .SBTTL RET_TARGET_MODE_RESOURCES - Return Target Mode Resources ; This subroutine deallocates all resources that the driver has acquired ; for Target Mode. ; ; Resources include: ; - Buffer used for holding the inquiry data ; - Buffer used for holding the sense data ; - Queue Buffers used for the Accept Target IO CCB list ; ; CALLING SEQUENCE: ; BSBW RET_TARGET_MODE_RESOURCES ; ; INPUT: ; IPL$_IOLOCK8 ; R4 = SPDT address ; ; OUTPUT: ; R0 = SS$_NORMAL - Successful completion ; .ENABLE LSB RET_TARGET_MODE_RESOURCES: .JSB_ENTRY INPUT=,OUTPUT=, - SCRATCH=,PRESERVE= ; ; Deallocate the buffer used for holding the inquiry data for target mode. ; TSTL SPDT$PS_INQUIRY_DATA(R4) ; Check inquiry buffer BNEQ 10$ ; Do we have one? BUG_CHECK INCONSTATE,FATAL ; No, we want to capture this 10$: MOVL SPDT$PS_INQUIRY_DATA(R4),R0 ; Yes, get the address MOVZWL INQ$IW_SIZE(R0),R1 ; Get the buffer size JSB EXE$DEANONPGDSIZ ; Deallocate the inquiry buffer CLRL SPDT$PS_INQUIRY_DATA(R4) ; Clear inquiry buffer pointer ; ; Deallocate the buffer used for holding the sense data for target mode. ; TSTL SPDT$PS_SENSE_DATA(R4) ; Check sense buffer BNEQ 20$ ; Do we have one? BUG_CHECK INCONSTATE,FATAL ; No, we want to capture this 20$: MOVL SPDT$PS_SENSE_DATA(R4),R0 ; Yes, get the address MOVZWL SENSE$IW_SIZE(R0),R1 ; Get the buffer size JSB EXE$DEANONPGDSIZ ; Deallocate the sense buffer CLRL SPDT$PS_SENSE_DATA(R4) ; Clear sense buffer pointer ; ; Deallocate the Queue Buffers used for the Accept Target IO CCB list. ; MOVL SPDT$PS_ATIO_FL(R4),R6 ; Point to first Queue Buffer BNEQ 30$ ; Do we have one? BUG_CHECK INCONSTATE,FATAL ; No, we want to capture this 30$: MOVL #NUM_ATIO_CCBS,R7 ; Yes, init loop counter 40$: MOVL R6,R0 ; Point to next Queue Buffer BNEQ 50$ ; Do we have one? BUG_CHECK INCONSTATE,FATAL ; No, we want to capture this 50$: MOVL QBUF$PS_FLINK(R0),R6 ; Save next Queue Buffer ptr MOVZWL QBUF$IW_SIZE(R0),R1 ; Pass Queue Buffer size JSB EXE$DEANONPGDSIZ ; Deallocate the Queue buffer SOBGTR R7,40$ ; Loop for all CCBs CLRL SPDT$PS_ATIO_FL(R4) ; Clear Buffer list pointer MOVZWL #SS$_NORMAL,R0 ; Setup for return RSB ; Return .DISABLE LSB ; RET_TARGET_MODE_RESOURCES .PAGE .SBTTL REINIT_PORT - Reinitialize the SIMport adapter ; ; This subroutine is called to (re)initialize a TZA/PZA SCSI channel. ; (NOTE: The words "channel" and "port" are used interchangeably here.) ; It can be called at the following times: ; 1.) During the first-time initialization of the SIMport adapter ; and the channels during system configuration ; 2.) To reinitialize the channels after the SIMport adapter crashed ; via an error interrupt ; ; This routine will first reset the adapter to the UNINITIALIZED state ; if this is the first-time initialization of the adapter or if this is ; the reinitialization after an adapter crash. This is done by setting ; the MIN bit in the AMCSR. Then, for the channel reinitialization, ; necessary parameters are set up and a command is sent to the adapter to ; bring the channel to the DISABLED state. Once the channel is in the ; DISABLED state, further parameters are set and a command is sent to the ; adapter to bring this channel to the ENABLED state. ; ; If any error is encountered during the (re)initialization sequences, the ; sequence will be restarted from the top. If more than 5 minutes have elapsed ; since the start of the (re)initialization and it had an error, the port ; will be set to offline for the duration of the current system's lifetime. ; Also an OFFLINE warning message will be output to the operator's console. ; ; INPUT: ; 4(AP) = KPB address ; ; OUTPUT: ; If no error, the channel is set to ENABLED state and is ready to ; process commands. ; R2-R10 preserved ; .ENABLE LSB $ARG_DEF <- kpb > ; KPB address. REINIT_PORT: .CALL_ENTRY MOVL kpb(AP),R8 ; Get the KPB address MOVL KPB$PS_UCB(R8),R5 ; Get port's UCB address MOVL UCB$L_PDT(R5),R4 ; Get the port's SPDT address MOVL R4,KPB$PS_SCSI_PTR1(R8) ; Store SPDT into KPB BICL #UCB$M_ONLINE,UCB$L_STS(R5) ; Clear port UCB ONLINE BICL2 #SPDT$M_STS_ONLINE,- ; Clear port SPDT ONLINE SPDT$L_STS(R4) PUSHL KPB$PS_SCSI_SCDRP(R8) ; Save SCDRP from KPB BSBW GET_SCDRP ; Allocate the SCDRP MOVL R2,R5 ; Save the SCDRP MOVL R2,KPB$PS_SCSI_SCDRP(R8) ; Save SCDRP in KPB MOVL R8,SCDRP$PS_KPB(R5) ; Store KPB into SCDRP MOVL R4,SCDRP$PS_SPDT(R5) ; Store SPDT into SCDRP BISL #SPDT$M_INIT_ADAPTER,- ; We need to init the adapter SPDT$IS_PORTSTS(R4) PUSHL R5 ; Push SCDRP on stack PUSHL R4 ; Push SPDT on stack CALLS #2,@SPDT$PS_CD_RESET_SCSI_BUS(R4) ; Reset SCSI bus MOVL R5,R0 ; Deallocate the SCDRP CALL_DRVDEALMEM SAVE_R0R1=NO ; COM_STD$DRVDEALMEM POPL KPB$PS_SCSI_SCDRP(R8) ; Clear the SCDRP pointer MOVL SPDT$L_PORT_UCB(R4),R5 ; Get port UCB BISL #UCB$M_ONLINE,UCB$L_STS(R5) ; Set UCB online BISL #SPDT$M_STS_ONLINE,- ; Set SPDT online SPDT$L_STS(R4) RET ; Return .DISABLE LSB ; REINIT_PORT .PAGE .SBTTL INIT_ADAPTER - (Re)initialize the SIMport adapter ; ; This subroutine is called to (re)initialize a TZA/PZA SCSI channel. ; (NOTE: The words "channel" and "port" are used interchangeably here.) ; It can be called at the following times: ; 1.) During the first-time initialization of the SIMport adapter ; and the channels during system configuration ; 2.) To reinitialize the channels after the SIMport adapter crashed ; via an error interrupt ; ; This routine will first reset the adapter to the UNINITIALIZED state ; if this is the first-time initialization of the adapter or if this is ; the reinitialization after an adapter crash. This is done by setting ; the MIN bit in the AMCSR. Then, for the channel reinitialization, ; necessary parameters are set up and a command is sent to the adapter to ; bring the channel to the DISABLED state. Once the channel is in the ; DISABLED state, further parameters are set and a command is sent to the ; adapter to bring this channel to the ENABLED state. ; ; If any error is encountered during the (re)initialization sequences, the ; sequence will be restarted from the top. If more than 5 minutes have elapsed ; since the start of the (re)initialization and it had an error, the port ; will be set to offline for the duration of the current system's lifetime. ; Also an OFFLINE warning message will be output to the operator's console. ; ; INPUT: ; R8 = KPB address ; ; OUTPUT: ; If no error, the channel is set to ENABLED state and is ready to ; process commands. ; R2-R10 preserved ; .ENABLE LSB INIT_ADAPTER: .JSB_ENTRY MOVL KPB$PS_UCB(R8),R5 ; Get UCB address MOVL UCB$L_PDT(R5),R4 ; Get SPDT address MOVL SPDT$L_ADP(R4),R2 ; Get ADP address MOVL ADP$L_CRB(R2),R3 ; Get CRB address BICL #UCB$M_ONLINE,UCB$L_STS(R5) ; Clear UCB ONLINE to reinit BICL #SPDT$M_STS_ONLINE,- ; Clear SPDT ONLINE to reinit SPDT$L_STS(R4) BSBW CHECK_REV ; Check FW/HW revision levels BLBC R0,PORT_OFFLINE ; Set port offline if revision ; levels are not correct BBS #SPDT$V_PORTONLY,- ; Skip adapter (re)init if SPDT$IS_PORTSTS(R4),90$ ; already done ; ; For adapter reinitialization, clean up all loose ends hanging from previous ; state. This includes cleaning up all Carriers and Q_Buffers and resuming all ; I/O threads that were suspended waiting for responses. ; ;; BSBW CLEANUP_BUF ; Do buffer cleanup ; ; Begin SIMport adapter initialization by setting the Maintenance ; Initialize (MIN) bit in AMCSR to reset the adapter. ; DEVICELOCK - ; Lock device access LOCKADDR = SPDT$L_DLCK(R4),- LOCKIPL = SPDT$B_DIPL(R4),- SAVIPL = -(SP),- PRESERVE = NO WRITE_CSR REG = AMCSR,- ; Set MIN bit in AMCSR DATA = #TZA_AMCSR$M_MIN DEVICEUNLOCK - ; Unlock device access LOCKADDR = SPDT$L_DLCK(R4),- NEWIPL = (SP)+,- PRESERVE = NO ; ; Wait for the UNIN bit to be set in the ASR. When the UNIN bit becomes set, ; the SIMport adapter has completed its reset. ; .Disable Flagging ; Turn off info. it's aligned PUSHL #SPL$C_IOLOCK8 ; Fork lock PUSHAQ ONE_SECOND ; Time delay in ticks PUSHL R8 ; KPB address CALLS #3,G^EXE$KP_TQE_WAIT ; Fork and wait the desired time .Enable Flagging PUSHL R0 ; Save R0 READ_CSR REG=ASR,DEST=R6 ; Read the ASR POPL R0 ; Restore R0 BBS #TZA_ASR$V_UNIN,R6,20$ ; If UNIN set, reset complete 10$: .Disable Flagging ; Turn off info. it's aligned PUSHL #SPL$C_IOLOCK8 ; Fork lock PUSHAQ ONE_SECOND ; Time delay in ticks PUSHL R8 ; KPB address CALLS #3,G^EXE$KP_TQE_WAIT ; Fork and wait the desired time .Enable Flagging PUSHL R0 ; Save R0 READ_CSR REG=ASR,DEST=R6 ; Read the ASR POPL R0 ; Restore R0 BBS #TZA_ASR$V_UNIN,R6,20$ ; If UNIN set, reset complete ; ; The UNIN bit failed to set. We need to do a board level reset. ; DEVICELOCK - ; Lock device access LOCKADDR = SPDT$L_DLCK(R4),- LOCKIPL = SPDT$B_DIPL(R4),- SAVIPL = -(SP),- PRESERVE = NO WRITE_CSR REG = RESET,- ; Do board level reset DATA = #0 DEVICEUNLOCK - ; Unlock device access LOCKADDR = SPDT$L_DLCK(R4),- NEWIPL = (SP)+,- PRESERVE = NO .Disable Flagging ; Turn off info. it's aligned PUSHL #SPL$C_IOLOCK8 ; Fork lock PUSHAQ TWO_SECONDS ; Time delay in ticks PUSHL R8 ; KPB address CALLS #3,G^EXE$KP_TQE_WAIT ; Fork and wait the desired time .Enable Flagging PUSHL R0 ; Save R0 READ_CSR REG=ASR,DEST=R6 ; Read the ASR POPL R0 ; Restore R0 BBS #TZA_ASR$V_UNIN,R6,20$ ; If UNIN set, reset complete BRW PORT_OFFLINE ; UNIN failed, set port offline ; ; The SIMport adapter is now reset. Build a SET ADAPTER STATE command ; requesting DISABLED state and place it on the Driver Adapter Command ; Queue (DACQ). ; ; Allocate some pool for a buffer and zero the block. ; 20$: SUBL #8,SP ; Allocate two lw on stack MOVL SP,R2 ; Store address of lw to receive ; address of allocated block ADDL3 #4,R2,R6 ; Store address of lw to receive ; size of allocated block PUSHL R6 ; Arg 4 = addr to receive size PUSHL R2 ; Arg 3 = addr to receive addr ; of allocated block PUSHL #ALN_LONG ; Arg 2 = 4 byte alignment PUSHL #256 ; Arg 1 = size of block CALLS #4,EXE$ALONONPAGED_ALN ; Allocate a block POPL R2 ; Get addr of allocated block POPL R7 ; Get allocated size BLBS R0,25$ ; Did we get the block? KP_STALL_FORK_WAIT KPB=R8,- ; No, fork and wait FKB=KPB$PS_UCB(R8) ; using UCB fork block BRB 20$ ; Try allocation again 25$: PUSHR #^M ; Save registers MOVC5 #0,(SP),#0,R7,(R2) ; Zero initialize the block POPR #^M ; Restore registers ; ; Initialize static fields in the Queue Buffer. R2 points to Queue Buffer. ; MOVW R7,QBUF$IW_SIZE(R2) ; Save Queue Buffer size MOVB #DYN$C_MISC,QBUF$IB_TYPE(R2) ; Set buffer type MOVB #DYN$C_QBUF,QBUF$IB_SUBTYPE(R2) ; Set buffer subtype MOVAB QBUF$PQ_CCB_ADDR(R2),R6 ; VA of adapter visible section $SVA_TO_PA SVA=R6 ; PA of adapter visible section BITL #SPDT$M_PFLG_MAPPING_REG,- ; Are we using map regs? SPDT$L_PORT_FLAGS(R4) BEQL 27$ ; Nope; just use PA. ; ; Is the Queue Buffer within the direct-DMA window? Only the first ; QBUF$C_ADPLEN bytes starting at QBUF$PQ_CCB_ADDR have to be, that's ; the only portion accessed by the adapter. ; EVAX_ADDQ #QBUF$C_ADPLEN,R0,R3 ; Yes, just use PA EVAX_CMPULE R3,SPDT$IQ_DMA_SIZE(R4),R7 ; Compare to DMA window size BLBS R7,27$ ; Branch if LE window size ; ; O.K., we hafta map it. ; MAP_S0 - SVA = R6,- ; Queue Buffer address (VA). NBYTES = #QBUF$C_ADPLEN,- ; Size to map. CRCTX = QBUF$PS_INIT_CRCTX_PTR(R2),- ; CRCTX pointer reference. KPB$ = SPDT$PS_KPB(R4),- ; KPB to stall on alloc fail DMA_ADDR = QBUF$PQ_PA_QBUF(R2) ; Where to put DMA-able addr BRB 29$ ; ; No mapping registers needed, just use PA (+ DMA base). ; 27$: ADDL2 SPDT$L_DMA_BASE(R4),R0 ; Add in DMA window base EVAX_ZAP R0,#^XF0,R0 ; Clear the upper longword EVAX_STQ R0,QBUF$PQ_PA_QBUF(R2) ; Save physical address 29$: MOVL R2,R7 ; Copy Q_Buffer pointer MOVQ QBUF$PQ_PA_QBUF(R7),- ; Setup PA of CCB QBUF$PQ_CCB_ADDR(R7) MOVW #QBUF$C_SET_ADAP_STATE_SIZE,- ; Setup CCB size QBUF$IW_CCB_LENGTH(R7) MOVB #QBUF$C_SET_ADAP_STATE,- ; Set SIMport function code to QBUF$IB_FUNC_CODE(R7) ; Set Adapter State CLRB QBUF$IB_CAM_STS(R7) ; Clear CAM Status MOVB #QBUF$C_SET_ADAP_STATE_DIS,- ; Request disabled state QBUF$IB_ADP_STATE(R7) BSBW GET_QUEUE_CARRIER ; Get a queue carrier MOVL R7,CAR$PQ_QBUF_TOKEN(R2) ; Copy Q_Buffer virtual addr MOVB QBUF$IB_FUNC_CODE(R7),- ; Put SIMport function code CAR$IB_FUNC_CODE(R2) ; into carrier CLRB CAR$IB_STATUS(R2) ; Clear Status CLRW CAR$IW_FLAGS(R2) ; Flags CLRL CAR$IS_CONNECT_ID(R2) ; Connect id BSBW INSERT_DACQ ; Insert carrier onto DACQ ; ; Write the physical address of the Adapter Block to the Adapter Block Base ; Register and wait for the ADAPTER STATE SET response from the adapter. ; MOVL SPDT$PS_AB(R4),R6 ; Get Adapter Block address MOVL AB$PQ_PA(R6),R7 ; Get physical address of AB EVAX_ZAP R7,#^XF0,R7 ; Clear the upper longword EVAX_SRL R7,#TZA_ABBR$C_PA_SHIFT,R7 ; Shift PA right 5 bits DEVICELOCK - ; Lock device access LOCKADDR = SPDT$L_DLCK(R4),- LOCKIPL = SPDT$B_DIPL(R4),- SAVIPL = -(SP),- PRESERVE = NO WRITE_CSR REG = ABBR,- ; Give AB physical address to DATA = R7,- ; the adapter TYPE = L DEVICEUNLOCK - ; Unlock device access LOCKADDR = SPDT$L_DLCK(R4),- NEWIPL = (SP)+,- PRESERVE = NO WAIT_FOR_RESPONSE- ; Wait for response on ADRQ WAIT_TIME = 2,- ; Wait for 2 seconds SPDT = R4 ; ; We have received an ADAPTER STATE SET response message from the SIMport ; adapter. The adapter should now be in the DISABLED state. Interpret any ; configuration parameters within the response message and place the required ; number of queue elements onto the Driver Adapter Free Queue (DAFQ). ; BSBW REMOVE_ADRQ ; Get response from ADRQ MOVL CAR$PQ_QBUF_PTR(R2),R7 ; Get Q_Buffer virtual address ; written by the adapter MOVB QBUF$IB_XFER_ALIGN(R7),- ; Save adapter alignment SPDT$IB_XFER_ALIGN(R4) ; capabilities MOVZBL QBUF$IB_N_FREEQ(R7),R6 ; Free queue entries requested BSBW SETUP_DAFQ ; Set up free queue entries BSBW SETUP_ADFQ ; Set up ADFQ free queue entries ; ; Send a SET PARAMETERS command to the adapter based on configuration ; information from the ADAPTER STATE SET response message. ; We still have a Queue Buffer in R7 and a Carrier in R2. ; MOVQ QBUF$PQ_PA_QBUF(R7),- ; Setup PA of CCB QBUF$PQ_CCB_ADDR(R7) MOVW #QBUF$C_SET_PARAM_SIZE,- ; Setup CCB size QBUF$IW_CCB_LENGTH(R7) MOVB #QBUF$C_SET_PARAM,- ; Set SIMport function code to QBUF$IB_FUNC_CODE(R7) ; Set Parameters CLRB QBUF$IB_CAM_STS(R7) ; Clear CAM Status MOVB #NUM_TARGET_LUNS,- ; Target mode LUNs active QBUF$IB_NT_LUNS(R7) CLRB QBUF$IB_N_HOST_SG(R7) ; No host 4K segments BISW #QBUF$M_ENB_COUNTERS,- ; Enable port counters QBUF$IW_SET_PARAM_FLAGS(R7) CLRL QBUF$IS_SYSTEM_TIME(R7) ; Setup system time CLRL QBUF$IS_INT_HOLDOFF(R7) ; No interrupt holdoff timer CLRL QBUF$IS_RP_TIMER(R7) ; Reply timer CLRQ QBUF$IQ_HOST_SG_BSD(R7) ; No buffer segment descriptor CLRQ CAR$PQ_QBUF_TOKEN(R2) ; Clear Q_Buffer virtual addr MOVL R7,CAR$PQ_QBUF_TOKEN(R2) ; Copy Q_Buffer virtual addr MOVB QBUF$IB_FUNC_CODE(R7),- ; Put SIMport function code CAR$IB_FUNC_CODE(R2) ; into carrier CLRB CAR$IB_STATUS(R2) ; Clear Status CLRW CAR$IW_FLAGS(R2) ; flags CLRL CAR$IS_CONNECT_ID(R2) ; Connect id BSBW INSERT_DACQ ; Insert command on DACQ EVAX_LDQ R9,CAR$PQ_PA(R2) ; Get PA of new Stopper Carrier EVAX_SRL R9,#TZA_DACQIR$C_PA_SHIFT,R9 ; Shift PA right 3 bits DEVICELOCK - ; Lock device access LOCKADDR = SPDT$L_DLCK(R4),- LOCKIPL = SPDT$B_DIPL(R4),- SAVIPL = -(SP),- PRESERVE = NO WRITE_CSR REG = DACQIR,- ; Notify adapter of an entry DATA = R9,- ; on the DACQ TYPE = L DEVICEUNLOCK - ; Unlock device access LOCKADDR = SPDT$L_DLCK(R4),- NEWIPL = (SP)+,- PRESERVE = NO WAIT_FOR_RESPONSE- ; Wait for response on ADRQ WAIT_TIME = 2,- ; Wait for 2 seconds SPDT = R4 ; ; Issue a second SET ADAPTER STATE command to request the adapter to ; transition to the ENABLED state. ; BSBW REMOVE_ADRQ ; Get response from ADRQ MOVL CAR$PQ_QBUF_PTR(R2),R7 ; Get Q_Buffer virtual address ; written by the adapter MOVQ QBUF$PQ_PA_QBUF(R7),- ; Setup PA of CCB QBUF$PQ_CCB_ADDR(R7) MOVW #QBUF$C_SET_ADAP_STATE_SIZE,- ; Setup CCB size QBUF$IW_CCB_LENGTH(R7) MOVB #QBUF$C_SET_ADAP_STATE,- ; Set SIMport function code to QBUF$IB_FUNC_CODE(R7) ; Set Adapter State CLRB QBUF$IB_CAM_STS(R7) ; Clear CAM Status MOVB #QBUF$C_SET_ADAP_STATE_ENB,- ; Request enabled state QBUF$IB_ADP_STATE(R7) CLRQ CAR$PQ_QBUF_TOKEN(R2) ; Clear Q_Buffer virtual addr MOVL R7,CAR$PQ_QBUF_TOKEN(R2) ; Copy Q_Buffer virtual addr MOVB QBUF$IB_FUNC_CODE(R7),- ; Put SIMport function code CAR$IB_FUNC_CODE(R2) ; into carrier CLRB CAR$IB_STATUS(R2) ; Clear Status CLRW CAR$IW_FLAGS(R2) ; flags CLRL CAR$IS_CONNECT_ID(R2) ; Connect id BSBW INSERT_DACQ ; Insert command on DACQ EVAX_LDQ R9,CAR$PQ_PA(R2) ; Get PA of new Stopper Carrier EVAX_SRL R9,#TZA_DACQIR$C_PA_SHIFT,R9 ; Shift PA right 3 bits DEVICELOCK - ; Lock device access LOCKADDR = SPDT$L_DLCK(R4),- LOCKIPL = SPDT$B_DIPL(R4),- SAVIPL = -(SP),- PRESERVE = NO WRITE_CSR REG = DACQIR,- ; Notify adapter of an entry DATA = R9,- ; on the DACQ TYPE = L DEVICEUNLOCK - ; Unlock device access LOCKADDR = SPDT$L_DLCK(R4),- NEWIPL = (SP)+,- PRESERVE = NO WAIT_FOR_RESPONSE- ; Wait for response on ADRQ WAIT_TIME = 2,- ; Wait for 2 seconds SPDT = R4 ; ; The adapter is now in the ENABLED state. Issue a SET CHANNEL STATE command ; to request the channel to transition to the ENABLED state. ; BSBW REMOVE_ADRQ ; Get response from ADRQ MOVL CAR$PQ_QBUF_PTR(R2),R7 ; Get Q_Buffer virtual address ; written by the adapter MOVQ QBUF$PQ_PA_QBUF(R7),- ; Setup PA of CCB QBUF$PQ_CCB_ADDR(R7) MOVW #QBUF$C_SET_CHAN_STATE_SIZE,- ; Setup CCB size QBUF$IW_CCB_LENGTH(R7) MOVB #QBUF$C_SET_CHAN_STATE,- ; Set SIMport function code to QBUF$IB_FUNC_CODE(R7) ; Set Channel State CLRB QBUF$IB_CAM_STS(R7) ; Clear CAM Status MOVB #QBUF$C_SET_CHAN_STATE_ENB,- ; Request enabled state QBUF$IB_CHN_STATE(R7) CLRB QBUF$IB_CHAN_ID(R7) ; Clear channel id CLRB QBUF$IB_NODE_ID(R7) ; Clear node id CLRQ CAR$PQ_QBUF_TOKEN(R2) ; Clear Q_Buffer virtual addr MOVL R7,CAR$PQ_QBUF_TOKEN(R2) ; Copy Q_Buffer virtual addr MOVB QBUF$IB_FUNC_CODE(R7),- ; Put SIMport function code CAR$IB_FUNC_CODE(R2) ; into carrier CLRB CAR$IB_STATUS(R2) ; Clear Status CLRW CAR$IW_FLAGS(R2) ; flags CLRL CAR$IS_CONNECT_ID(R2) ; Connect id BSBW INSERT_DACQ ; Give the command to adapter EVAX_LDQ R9,CAR$PQ_PA(R2) ; Get PA of new Stopper Carrier EVAX_SRL R9,#TZA_DACQIR$C_PA_SHIFT,R9 ; Shift PA right 3 bits DEVICELOCK - ; Lock device access LOCKADDR = SPDT$L_DLCK(R4),- LOCKIPL = SPDT$B_DIPL(R4),- SAVIPL = -(SP),- PRESERVE = NO WRITE_CSR REG = DACQIR,- ; Notify adapter of an entry DATA = R9,- ; on the DACQ TYPE = L DEVICEUNLOCK - ; Unlock device access LOCKADDR = SPDT$L_DLCK(R4),- NEWIPL = (SP)+,- PRESERVE = NO WAIT_FOR_RESPONSE- ; Wait for response on ADRQ WAIT_TIME = 2,- ; Wait for 2 seconds SPDT = R4 ; ; The Adapter and channel are now in the ENABLED state. Save the host ; adapter SCSI ID in the SPDT. ; BSBW REMOVE_ADRQ ; Get response from ADRQ MOVL CAR$PQ_QBUF_PTR(R2),R7 ; Get Q_Buffer virtual address MOVZBL QBUF$IB_NODE_ID(R7),R9 ; Get host adapter SCSI id MOVL R9,SPDT$IS_SCSI_ID_NUM(R4) ; Save adapter SCSI ID BBSS R9,SPDT$L_SCSI_BUS_ID(R4),30$ ; Save SCSI ID as a mask also ; ; Issue a Path Inquiry command to obtain CAM and SCSI parameter information. ; 30$: MOVQ QBUF$PQ_PA_QBUF(R7),- ; Setup PA of CCB QBUF$PQ_CCB_ADDR(R7) MOVW #QBUF$C_PATH_INQ_SIZE,- ; Setup CCB size QBUF$IW_CCB_LENGTH(R7) MOVB #QBUF$C_PATH_INQUIRY,- ; Set CAM function code to QBUF$IB_FUNC_CODE(R7) ; Path Inquiry CLRB QBUF$IB_CAM_STS(R7) ; Clear CAM Status CLRQ CAR$PQ_QBUF_TOKEN(R2) ; Clear Q_Buffer virtual addr MOVL R7,CAR$PQ_QBUF_TOKEN(R2) ; Copy Q_Buffer virtual addr MOVB QBUF$IB_FUNC_CODE(R7),- ; Put CAM function code into CAR$IB_FUNC_CODE(R2) ; carrier CLRB CAR$IB_STATUS(R2) ; Clear Status CLRW CAR$IW_FLAGS(R2) ; flags CLRL CAR$IS_CONNECT_ID(R2) ; Connect id BSBW INSERT_DACQ ; Give the command to adapter EVAX_LDQ R9,CAR$PQ_PA(R2) ; Get PA of new Stopper Carrier EVAX_SRL R9,#TZA_DACQIR$C_PA_SHIFT,R9 ; Shift PA right 3 bits DEVICELOCK - ; Lock device access LOCKADDR = SPDT$L_DLCK(R4),- LOCKIPL = SPDT$B_DIPL(R4),- SAVIPL = -(SP),- PRESERVE = NO WRITE_CSR REG = DACQIR,- ; Notify adapter of an entry DATA = R9,- ; on the DACQ TYPE = L DEVICEUNLOCK - ; Unlock device access LOCKADDR = SPDT$L_DLCK(R4),- NEWIPL = (SP)+,- PRESERVE = NO WAIT_FOR_RESPONSE- ; Wait for response on ADRQ WAIT_TIME = 2,- ; Wait for 2 seconds SPDT = R4 ; ; Save the path inquiry information that was returned from the adapter ; into the SPDT. ; BSBW REMOVE_ADRQ ; Get response from ADRQ MOVL CAR$PQ_QBUF_PTR(R2),R7 ; Get Q_Buffer virtual address ; written by the adapter MOVAB SPDT$IB_PI_DATA(R4),R10 ; Get start addr of PI data MOVAB QBUF$IB_PI_VERSION(R7),R3 PUSHR #^M ; Save registers MOVC3 #SPDT$C_PI_DATA_SIZE,(R3),(R10) ; Copy returned PI data to SPDT POPR #^M ; Restore registers ; ; Set up for target mode. This includes sending an ENABLE_LUN command to the ; adapter. Start by formatting an Enable LUN command in the Queue Buffer. ; MOVQ QBUF$PQ_PA_QBUF(R7),- ; Setup PA of CCB QBUF$PQ_CCB_ADDR(R7) MOVW #QBUF$C_ENABLE_LUN_SIZE,- ; Setup CCB size QBUF$IW_CCB_LENGTH(R7) MOVB #QBUF$C_ENABLE_LUN,- ; Set CAM function code to QBUF$IB_FUNC_CODE(R7) ; Enable LUN CLRB QBUF$IB_CAM_STS(R7) ; Clear CAM Status CLRL QBUF$IS_CONNECT_ID(R7) ; Clear connect id BISB #QBUF$M_DISCONS_MANDATORY,- ; Discon for each CCB for LUN QBUF$IB_CAM_FLAGS_4(R7) MOVB SPDT$IS_SCSI_ID_NUM(R4),- ; Setup target id QBUF$IB_TARGET_ID(R7) MOVW #6,QBUF$IW_GROUP_6_VU_CDB(R7) ; 6 byte CDB's MOVW #6,QBUF$IW_GROUP_7_VU_CDB(R7) ; 6 byte CDB's .Disable Flagging ; Turn off info. it's aligned CLRL QBUF$IB_PAD2(R7) ; DOF for debug Clear padding CLRQ QBUF$PS_IMMED_NOTIFY_CCBS(R7) ; Immediate notify CCB list MOVL #1,QBUF$IS_NUM_IMMED_NOTIFY_CCBS(R7) ; Number immediate notify CCBs CLRQ QBUF$PS_TARGET_CCBS(R7) ; Target CCB list .Enable Flagging MOVL #NUM_ATIO_CCBS,- ; Number Accept target IO CCBs QBUF$IS_NUM_TARGET_CCBS(R7) BSBW SETUP_ACCEPT_CCB_LIST ; Setup accept CCB list ; ; Fill in the Private Data Area of the Enable LUN command by inserting the ; Accept target IO Carrier/Queue Buffer pair onto the Accept Target IO list ; head and tail. ; MOVL SPDT$IS_ATIO_LIST_HEAD(R4),R0 ; Get Accept list head pointer .Disable Flagging ; Turn off info. it's aligned EVAX_LDQ R0,CAR$PQ_PA(R0) ; Get Accept Carrier PA EVAX_STQ R0,QBUF$PS_ACCEPT_LIST_HEAD(R7) ; Setup Accept list head MOVL SPDT$IS_ATIO_LIST_TAIL(R4),R1 ; Get Accept list tail pointer EVAX_LDQ R1,CAR$PQ_PA(R1) ; Get Accept Carrier PA EVAX_STQ R1,QBUF$PS_ACCEPT_LIST_TAIL(R7) ; Setup Accept list tail .Enable Flagging ; ; Send the Enable LUN command to the SIMport adapter. ; CLRQ CAR$PQ_QBUF_TOKEN(R2) ; Clear Q_Buffer virtual addr MOVL R7,CAR$PQ_QBUF_TOKEN(R2) ; Copy Q_Buffer virtual addr MOVB QBUF$IB_FUNC_CODE(R7),- ; Put CAM function code into CAR$IB_FUNC_CODE(R2) ; Carrier CLRB CAR$IB_STATUS(R2) ; Clear Status CLRW CAR$IW_FLAGS(R2) ; flags CLRL CAR$IS_CONNECT_ID(R2) ; Connect id BSBW INSERT_DACQ ; Give the command to adapter EVAX_LDQ R9,CAR$PQ_PA(R2) ; Get PA of new Stopper Carrier EVAX_SRL R9,#TZA_DACQIR$C_PA_SHIFT,R9 ; Shift PA right 3 bits DEVICELOCK - ; Lock device access LOCKADDR = SPDT$L_DLCK(R4),- LOCKIPL = SPDT$B_DIPL(R4),- SAVIPL = -(SP),- PRESERVE = NO WRITE_CSR REG = DACQIR,- ; Notify adapter of an entry DATA = R9,- ; on the DACQ TYPE = L DEVICEUNLOCK - ; Unlock device access LOCKADDR = SPDT$L_DLCK(R4),- NEWIPL = (SP)+,- PRESERVE = NO WAIT_FOR_RESPONSE- ; Wait for response on ADRQ WAIT_TIME = 2,- ; Wait for 2 seconds SPDT = R4 BSBW REMOVE_ADRQ ; Get response from ADRQ CMPB CAR$IB_STATUS(R2),- ; Check Enable LUN status #CAR$C_REQ_COMP_NO_ERROR BEQL 34$ ; Is the LUN enabled? BSBW RET_TARGET_MODE_RESOURCES ; No, return resources LOG_ERROR - ; Log an error log entry TYPE = CTL_ERR,- ; Controller error CDT = #0 ; No associated SCDT BRB 35$ ; Join common code 34$: CLRL SPDT$IS_NUM_ACCEPTS(R4) ; Clear Accept counter CLRL SPDT$IS_NUM_CONTINUES(R4) ; Clear Coutinue counter CLRL SPDT$IS_NUM_TARGET_EVENTS(R4) ; Clear Target Event counter BISL #SPDT$M_TARGET_MODE,- ; Yes, target mode is enabled SPDT$IS_PORTSTS(R4) ; ; Enable the functions supported on the Turbochannel. ; 35$: PUSHL #IOC$K_DISABLE_PAR ; Disable parity on bus. PUSHL UCB$L_CRB(R5) ; CRB address CALLS #2,IOC$NODE_FUNCTION ; Enable interrupts PUSHL #IOC$K_ENABLE_INTR ; Enable interrupts on TC PUSHL UCB$L_CRB(R5) ; CRB address CALLS #2,IOC$NODE_FUNCTION ; Enable interrupts ; ; Return resources and allow adapter completion and miscellaneous interrupts. ; MOVL CAR$PQ_QBUF_PTR(R2),R7 ; Get Q_Buffer virtual address ; written by the adapter BSBW RET_QUEUE_CARRIER ; Return Carrier to pool MOVL QBUF$PS_INIT_CRCTX_PTR(R7),R0 ; Did we alloc a CRCTX? BEQL 50$ BITL #CRCTX$M_ITEM_VALID,- ; Yep. Any registers? CRCTX$L_FLAGS(R0) BEQL 40$ PUSHL R0 ; CRCTX PUSHL SPDT$PS_CRAB(R4) ; CRAB address CALLS #2,IOC$DEALLOC_CNT_RES 40$: PUSHL QBUF$PS_INIT_CRCTX_PTR(R7) CALLS #1,IOC$DEALLOC_CRCTX 50$: MOVL R7,R0 ; Get buffer address MOVZWL QBUF$IW_SIZE(R0),R1 ; Get buffer size JSB EXE$DEANONPGDSIZ ; Deallocate buffer DEVICELOCK - ; Lock device access LOCKADDR = SPDT$L_DLCK(R4),- LOCKIPL = SPDT$B_DIPL(R4),- SAVIPL = -(SP),- PRESERVE = NO WRITE_CSR REG = AMCSR,- ; Allow adapter interrupts DATA = #TZA_AMCSR$M_INT_ENB DEVICEUNLOCK - ; Unlock device access LOCKADDR = SPDT$L_DLCK(R4),- NEWIPL = (SP)+,- PRESERVE = NO ; ; Initialization has now been completed. Setup miscellaneous SPDT fields. ; MOVW #SPDT$C_TYPE_PKS,- ; Setup Port Type SPDT$W_SPDT_TYPE(R4) MOVL #SCDRP$C_VERSION!- ; Save SCDRP version number, !- ; SPDT version number, !- ; SCDT version number, ,- ; STDT version number, SPDT$L_VERSION_CHECK(R4) ; Data structure version .IF DEFINED PKSDEBUG MOVAB UNIT_INIT_MSG,R1 ; Get addr of msg to output JSB G^EXE$OUTZSTRING ; Send the message .ENDC 90$: RSB ; Return ; ; We have had some type of error. The port cannot be used. Put the port ; offline and return. By setting the FAILED bit and not setting the port ; ONLINE bit we will prevent connections from being established or commands ; being sent using this port. (We may want to reset the port before we finish.) ; PORT_OFFLINE: .IF DEFINED PKSDEBUG MOVAB PORT_OFF_MSG,R1 ; Get addr of msg to output JSB G^EXE$OUTZSTRING ; Send the message .ENDC BICL #UCB$M_ONLINE,UCB$L_STS(R5) ; Clear UCB online BICL #SPDT$M_STS_ONLINE,- ; Clear SPDT online SPDT$L_STS(R4) BSBW PORT_SHUTDOWN ; Go shut down this port BRB 90$ ; Return .DISABLE LSB ; INIT_ADAPTER .PAGE .SBTTL SETUP_ACCEPT_CCB_LIST - Setup Accept Target IO CCB list ; ; This subroutine is called during (re)initialization to setup the ; Accept Target IO CCB list. ; ; It performs the following: ; - Allocates and formats a CCB for the Accept Target IO CCB list. ; - Allocates and links a Carrier for the Accept Target IO CCB. ; ; Note that this routine assumes that it is running in a Kernel Process context. ; ; INPUT: ; R4 = SPDT address ; R8 = KPB address to use for fork stalling ; ; OUTPUT: ; SPDT$IS_ATIO_LIST_HEAD = Carrier VA of Accept list head ; SPDT$IS_ATIO_LIST_TAIL = Carrier VA of Accept list tail ; SPDT$PS_ATIO_FL = Accept Target IO CCB list head pointer ; .ENABLE LSB SETUP_ACCEPT_CCB_LIST: .JSB_ENTRY INPUT=, OUTPUT=, - PRESERVE= ; ; Allocate and format CCBs for the Accept Target IO CCB list. ; CLRL R10 ; Clear CCB list previous ptr CLRL SPDT$IS_ATIO_LIST_HEAD(R4) ; Clear Accept list head CLRL SPDT$IS_ATIO_LIST_TAIL(R4) ; and tail pointers CLRL SPDT$PS_ATIO_FL(R4) ; Clear Queue Buffer next ptr MOVL #NUM_ATIO_CCBS,R9 ; Init loop counter 10$: MOVL #QUEUE_BUFFER_SIZE,R1 ; Load length first SUBL #8,SP ; Allocate two longword in stack MOVL SP,R2 ; Store addr of lw to receive ; address of allocated block ADDL3 #4,R2,R7 ; Store addr of lw to receive ; size of allocated block PUSHL R7 ; Arg 4 = addr to receive size PUSHL R2 ; Arg 3 = addr to receive addr ; of allocated block PUSHL #ALN_QUAD ; Arg 2 = alignment bit PUSHL R1 ; Arg 1 = requested size CALLS #4,EXE$ALONONPAGED_ALN ; Allocate a block POPL R2 ; Get addr of allocated block POPL R7 ; Get allocated size of block BLBS R0,20$ ; Branch if failure to retry KP_STALL_FORK_WAIT KPB=R8,- ; Fork and wait FKB=KPB$PS_UCB(R8) ; using UCB fork block BRB 10$ ; Try allocation again 20$: PUSHR #^M ; Save registers MOVC5 #0,(SP),#0,R7,(R2) ; Zero initialize the block POPR #^M ; Restore registers MOVL SPDT$PS_ATIO_FL(R4),- ; Set FLINK to whatever the QBUF$PS_FLINK(R2) ; queue header points to MOVL R2,SPDT$PS_ATIO_FL(R4) ; Save current Queue Buffer ptr ; ; Fill in static Accept Target IO CCB fields. ; MOVW R7,QBUF$IW_SIZE(R2) ; Save Queue Buffer size MOVB #DYN$C_MISC,QBUF$IB_TYPE(R2) ; Set buffer type MOVB #DYN$C_QBUF,QBUF$IB_SUBTYPE(R2) ; Set buffer subtype MOVAB QBUF$PQ_CCB_ADDR(R2),R6 ; VA of adapter visible section $SVA_TO_PA SVA=R6 ; PA of adapter visible section BITL #SPDT$M_PFLG_MAPPING_REG,- ; Are we using map regs? SPDT$L_PORT_FLAGS(R4) BEQL 30$ ; No, just use PA ; ; Is the Accept Target IO Queue Buffer within the direct-DMA window? ; EVAX_ADDQ #QBUF$C_ADPLEN,R0,R3 ; Yes, just use PA EVAX_CMPULE R3,SPDT$IQ_DMA_SIZE(R4),R7 ; Compare to DMA window size BLBS R7,30$ ; Branch if LE window size ; ; We have to map it. ; MAP_S0 SVA = R6,- ; Queue Buffer address (VA) NBYTES = #QBUF$C_ADPLEN,- ; Size to map CRCTX = QBUF$PS_CRCTX_PTR(R2),- ; CRCTX pointer reference KPB$ = SPDT$PS_KPB(R4),- ; KPB to stall on alloc fail DMA_ADDR = QBUF$PQ_PA_QBUF(R2) ; Where to put DMA-able addr BRB 40$ ; Join common code ; ; No mapping registers needed, just use PA (+ DMA base). ; 30$: ADDL2 SPDT$L_DMA_BASE(R4),R0 ; Add in DMA window base EVAX_ZAP R0,#^XF0,R0 ; Clear the upper longword EVAX_STQ R0,QBUF$PQ_PA_QBUF(R2) ; Save physical address 40$: MOVL R2,R7 ; Working copy MOVQ QBUF$PQ_PA_QBUF(R7),- ; Setup PA of CCB QBUF$PQ_CCB_ADDR(R7) MOVW #QBUF$C_ACCEPT_TARGET_IO_SIZE,- ; Setup CCB size QBUF$IW_CCB_LENGTH(R7) MOVB #QBUF$C_ACCEPT_TARGET_IO,- ; Set function code to QBUF$IB_FUNC_CODE(R7) ; Accept Target IO MOVB SPDT$IS_SCSI_ID_NUM(R4),- ; Setup target id QBUF$IB_TARGET_ID(R7) ; ; Setup autosense buffer pointer and size. ; TSTL QBUF$PS_CRCTX_PTR(R7) ; Do we have map registers? BEQL 50$ ADDL3 #QBUF$C_AUTOSENSE_OFFSET,- ; Yes, just add offset to QBUF$PQ_PA_QBUF(R7),R0 ; DMA_ADDR. BRB 60$ 50$: MOVAB QBUF$IB_AUTOSENSE_BUF(R7),R6 ; Get autosense buffer pointer $SVA_TO_PA SVA=R6 ; Convert to PA ADDL2 SPDT$L_DMA_BASE(R4),R0 ; Add in DMA window base 60$: EVAX_ZAP R0,#^XF0,R0 ; Clear the upper longword EVAX_STQ R0,QBUF$PS_SENSE_BUFF_PTR(R7) ; Save autosense PA in CCB MOVB #QBUF$C_AUTOSENSE_BUF_SIZE,- ; Autosense buffer length QBUF$IB_SENSE_BUFF_SIZE(R7) MOVL #ATIO_COMMAND_TIMEOUT,- ; Setup command timeout QBUF$IS_TIMEOUT_VALUE(R7) ; ; Setup the Private Data Area of Accept Target IO CCB. ; MOVB #QBUF$C_ATIO_CDB_BUFF_SIZE,- ; Setup CDB size QBUF$IB_ATIO_CDB_LEN(R7) MOVB QBUF$IB_SENSE_BUFF_SIZE(R7),- ; Autosense buffer length QBUF$IB_ATIO_SEN_LEN(R7) BUILD_BSD - ; Build a BSD for autosense BSD_ADDR = QBUF$PS_ATIO_SEN_BSD(R7),- BUFFER_PA = R0,- BYTE_COUNT = #QBUF$C_AUTOSENSE_BUF_SIZE MOVL QBUF$IS_TIMEOUT_VALUE(R7),- ; Setup command timeout QBUF$IS_ATIO_CMD_TIMEOUT(R7) ; ; Get a Queue Carrier and link it to the Accept Target IO CCB. ; BSBW GET_QUEUE_CARRIER ; Get a Queue Carrier TSTL SPDT$IS_ATIO_LIST_TAIL(R4) ; Check if first Carrier BNEQ 65$ ; Is this the first Carrier? MOVL R2,SPDT$IS_ATIO_LIST_TAIL(R4) ; Yes, setup tail pointer 65$: CLRQ CAR$PQ_NEXT_PTR(R2) ; Clear old NEXT_PTR MOVL R10,CAR$PQ_NEXT_PTR(R2) ; Link this Carrier in list MOVL CAR$PQ_PA(R2),R10 ; Save Queue Carrier PA MOVQ QBUF$PQ_PA_QBUF(R7),- ; Link Q_Buffer physical addr CAR$PQ_QBUF_PTR(R2) ; into Carrier MOVL R7,CAR$PQ_QBUF_TOKEN(R2) ; Copy Q_Buffer virtual address MOVL R2,QBUF$PS_CARRIER(R7) ; Save Carrier's address ; in Queue Buffer EVAX_MB ; Make sure Q_Buf contents and ; ptr are visible to the ; adapter before NEXT_PTR ; becomes valid EVAX_ADDQ #1,CAR$PQ_NEXT_PTR(R2),- ; Make it a valid Carrier CAR$PQ_NEXT_PTR(R2) .Enable Flagging MOVB QBUF$IB_FUNC_CODE(R7),- ; Fill in function code CAR$IB_FUNC_CODE(R2) CLRB CAR$IB_STATUS(R2) ; Clear Status CLRW CAR$IW_FLAGS(R2) ; Flags MOVL QBUF$IS_CONNECT_ID(R7),- ; Setup connect id CAR$IS_CONNECT_ID(R2) EVAX_MB ; Make sure addr are visible SOBGTR R9,10$ ; Loop for all CCBs MOVL R2,SPDT$IS_ATIO_LIST_HEAD(R4) ; Setup head pointer RSB ; Return .DISABLE LSB ; SETUP_ACCEPT_CCB_LIST .PAGE .SBTTL GET_SCDRP - Get an SCDRP ; ; This subroutine is called during (re)initialization to provide an ; SCDRP. This is necessary because the initialization of the SCSI ; subsystem requires the use of an SCDRP but does not have one provieded ; by the class driver. It is an IO performed by and for the port driver. ; The class driver does not know about it. ; ; All SCDRPs are QUAD aligned and do not cross a port page (8KB) boundary. ; ; Note that this routine assumes that it is running in a Kernel Process context. ; ; INPUT: ; R8 = KPB address to use for fork stalling ; ; OUTPUT: ; R0 = Status ; If SS$_NORMAL status, ; R2 = Address of the allocated SCDRP ; .ENABLE LSB GET_SCDRP: .JSB_ENTRY INPUT=,OUTPUT=, - PRESERVE= 10$: MOVL #SCDRP$K_LENGTH,R1 ; Load length first SUBL #8,SP ; Allocate two longword in stack MOVL SP,R2 ; Store address of longword to receive ; address of allocated block ADDL3 #4,R2,R7 ; Store address of longword to receive ; size of allocated block PUSHL R7 ; Arg 4 = addr to receive size PUSHL R2 ; Arg 3 = addr to receive addr of ; allocated block PUSHL #ALN_QUAD ; Arg 2 = alignment bit = 64 byte alignment PUSHL R1 ; Arg 1 = size of block requested CALLS #4,EXE$ALONONPAGED_ALN ; Allocate a block POPL R2 ; Get the addr of allocated block POPL R7 ; Get the allocated size BLBC R0,30$ ; Branch if failure to retry PUSHR #^M ; Save registers MOVC5 #0,(SP),#0,R7,(R2) ; Zero initialize the block POPR #^M ; Restore registers MOVW R7,SCDRP$W_SCDRPSIZE(R2) ; Set the allocated block size MOVB #DYN$C_SCDRP,SCDRP$B_CD_TYPE(R2); Set type MOVZWL #SS$_NORMAL,R0 ; Set success status RSB ; Return 30$: KP_STALL_FORK_WAIT KPB=R8,- ; Fork and wait FKB=KPB$PS_UCB(R8) ; using UCB fork block BRB 10$ ; Try allocation again .DISABLE LSB ; GET_SCDRP .PAGE .SBTTL PK$INIT_STDT - Complete STDT Initialization ; ; This routine will perform any STDT initialization necessary for the target. ; This includes: ; - Setting up for synchronous negotiation. ; ; Inputs: ; spdt(AP) = SPDT address ; stdt(AP) = STDT address ; FORK IPL ; FORK lock held ; Auto configuration thread ; FORK thread ; ; Outputs: ; R0 = Status ; SS$_NORMAL = Initialization successful. ; FORK IPL ; FORK lock held ; Auto configuration thread ; FORK thread ; .ENABLE LSB $ARG_DEF <- spdt, - ; SPDT address stdt > ; STDT address PK$INIT_STDT:: .CALL_ENTRY INPUT=<>,OUTPUT=,SCRATCH=,PRESERVE= MOVL spdt(AP),R4 ; Get the SPDT address MOVL stdt(AP),R10 ; Get the STDT address ; ; Take out the lock for data structure synchronization. ; DEVICELOCK - ; Get device lock and raise IPL LOCKADDR = SPDT$L_DLCK(R4),- LOCKIPL = SPDT$B_DIPL(R4),- SAVIPL = -(SP),- PRESERVE = NO ; ; Update the requested synchronization type. ; PUSHL R10 ; STDT address. PUSHL R4 ; SPDT address. CALLS #2,PK$NEGOTIATE_SYNCH ; Attempt to negotiate ; synchronous operation ; ; Release the data structure synchronization. ; DEVICEUNLOCK - ; Release device lock LOCKADDR = SPDT$L_DLCK(R4),- NEWIPL = (SP)+,- PRESERVE = NO MOVL #SS$_NORMAL,R0 RET ; Return .DISABLE LSB ; PK$INIT_STDT .PAGE .SBTTL PK$CONNECTION_CHAR_SET - Set connection characteristics ; ; This routine is called from the SC$CONNECTION_CHAR_SET routine in SCSICOMMON ; module to perform any port specific sections of the SET_CONNECTION_CHAR ; function. ; ; SCDRP thread ; FORK IPL ; Fork lock may be held ; ; OUTPUT: ; R0 = SS$_NORMAL ; .ENABLE LSB ; ; Define the offsets to the parameters pointed to by AP. ; $ARG_DEF <- spdt, - ; SPDT address scdrp, - ; SCDRP address scdt, - ; SCDT address stdt, - ; STDT address spi_cc > ; SPI Connection Characteristics PK$CONNECTION_CHAR_SET:: .CALL_ENTRY INPUT=<>, - SCRATCH=<>, - OUTPUT=, - PRESERVE= MOVL stdt(AP),R10 ; Get STDT address BBC #STDT$V_DFLG_SUPPRESS_SDTR,- ; Branch if the caller is not trying STDT$IS_DIPL_FLAGS(R10),10$ ; to suppress SDTR negotiation BICL2 #STDT$M_DFLG_RENEGOTIATE_SYNC,- ; Clear the bit which tells the port driver STDT$IS_DIPL_FLAGS(R10) ; send an SDTR message at the start of a command BRW 20$ ; Take common success path exit 10$: BBCC #STDT$V_DFLG_CLASS_REQ_SDTR,- ; Branch if the caller is not trying to initiate SDTR, STDT$IS_DIPL_FLAGS(R10),20$ ; but go ahead and clear the bit anyway PUSHL stdt(AP) ; Pass STDT address PUSHL spdt(AP) ; Pass SPDT address CALLS #2, PK$NEGOTIATE_SYNCH ; Attempt to negotiate synchronous operation. 20$: MOVZWL #SS$_NORMAL,R0 ; Set success status RET ; Return .DISABLE LSB ; PK$CONNECTION_CHAR_SET .PAGE .SBTTL PK$RESET_SCSI_BUS - Reset the SCSI Bus ; ; This routine is called to RESET the SCSI bus from the SC$RESET routine in ; the SCSICOMMON module at the request of the class driver. ; ; The SCSI bus reset command causes the indicated SCSI channel to return ; all outstanding I/O with bus reset status and then enter the disabled state. ; While in the disabled state, all new I/O is returned with bus reset status. ; The host is required to issue a Set Channel State command to re-enable ; the channel. ; ; Once the channel is back in the ENABLED state, the port driver will log ; the fact that the bus was reset. ; ; NOTE: Due to the fact that the current class driver doesn't issue a ; DEVICE RESET and there is no defined class-port driver interface ; for the DEVICE RESET function, the DEVICE RESET function is not ; implemented at this time. ; ; INPUT: ; spdt(AP) = SPDT address ; scdrp(AP) = SCDRP address ; SCDRP$L_CDT = SCDT address ; SCDRP$PS_KPB - Address of associated Kernel Process Block ; FORK IPL ; FORK lock held ; Fork thread ; ; OUTPUT: ; None ; .ENABLE LSB ; ; Define the offsets to the parameters pointed to by AP. ; $ARG_DEF <- spdt, - ; SPDT address scdrp > ; SCDRP address PK$RESET_SCSI_BUS:: .CALL_ENTRY PRESERVE= MOVL spdt(AP), R4 ; Get SPDT address MOVL scdrp(AP),R5 ; Get SCDRP address MOVL SCDRP$PS_KPB(R5),R8 ; Get the KPB address to pass MOVL R4,KPB$PS_SCSI_PTR1(R8) ; Save SPDT address ; ; If this is unit initialization or an adapter error has occurred, then we ; must first (re)initialize the adapter. ; BBCC #SPDT$V_INIT_ADAPTER,- ; Do we need to init adapter? SPDT$IS_PORTSTS(R4),10$ BSBW INIT_ADAPTER ; Yes, initialize the adapter ; ; Reset the SCSI bus by sending a Reset SCSI Bus message to the adapter. ; Get a Queue Buffer and Carrier from the Adapter Driver Free Queue (ADFQ). ; Set up the Queue Buffer (CCB) for the Reset SCSI Bus command. ; 10$: MOVL SPDT$PS_AB(R4),R6 ; Get Adapter Block address MOVL AB$PQ_ADFQ_HEAD(R6),R2 ; Get Carrier at ADFQ head MOVL CAR$PQ_QBUF_PTR(R2),R7 ; Get Q_Buffer virtual address EVAX_MB ; Make sure Queue Buffer ; contents are visible MOVL CAR$PQ_NEXT_PTR(R2),- ; Update ADFQ head pointer AB$PQ_ADFQ_HEAD(R6) ; with next Carrier address EVAX_MB ; Make sure Queue Buffer ; contents are visible MOVQ QBUF$PQ_PA_QBUF(R7),- ; Setup PA of CCB QBUF$PQ_CCB_ADDR(R7) MOVW #QBUF$C_COMMON_HDR_SIZE,- ; CCB length QBUF$IW_CCB_LENGTH(R7) MOVB #QBUF$C_RESET_SCSI_BUS,- ; Setup function code QBUF$IB_FUNC_CODE(R7) CLRB QBUF$IB_CAM_STS(R7) ; Clear CAM status MOVB SPDT$IB_PI_PATH_ID(R4),- ; Setup path id QBUF$IB_PATH_ID(R7) BISB #QBUF$M_NO_DATA_TX,- ; Direction is no data QBUF$IB_CAM_FLAGS_1(R7) BISB #QBUF$M_SIM_Q_FREEZE_DIS,- ; Set SIM queue freeze disable QBUF$IB_CAM_FLAGS_2(R7) ; ; The Reset SCSI Bus CCB is set up. Allocate a Carrier, setup Carrier fields. ; CLRB CAR$IB_STATUS(R2) ; Clear Status CLRW CAR$IW_FLAGS(R2) ; Flags CLRL CAR$IS_CONNECT_ID(R2) ; Connect id ; ; Send the command to the SIMport adapter. ; BSBW INSERT_DACQ ; Insert command on DACQ DEVICELOCK - ; Get device lock and raise IPL LOCKADDR=SPDT$L_DLCK(R4),- ; to prevent interrupts LOCKIPL=SPDT$B_DIPL(R4),- SAVIPL = -(SP),- PRESERVE=NO EVAX_LDQ R9,CAR$PQ_PA(R2) ; Get PA of new Stopper Carrier EVAX_SRL R9,#TZA_DACQIR$C_PA_SHIFT,R9 ; Shift PA right 3 bits WRITE_CSR REG = DACQIR,- ; Give DACQ head PA to adapter DATA = R9,- TYPE = L DEVICEUNLOCK - ; Release device lock LOCKADDR = SPDT$L_DLCK(R4),- NEWIPL = (SP)+,- PRESERVE = NO ; ; Log an error if necessary that a bus reset occurred. ; BBCC #SPDT$V_LOG_ERROR,- ; Do we need to log this? SPDT$IS_PORTSTS(R4),20$ LOG_ERROR - ; Yes, log the event that the TYPE = BUS_RESET_ISSUED,- ; port issued a bus reset CDT = #0 ; No associated CDT available 20$: MOVZWL #SS$_NORMAL,R0 ; Set sucess status RET ; Return .DISABLE LSB ; PK$RESET_SCSI_BUS .PAGE .SBTTL PK$ABORT_COMMAND - Port specific abort command routine ; ; This routine is called to ABORT the outstanding SCSI command on a given ; connection. The current version of the class-port driver interface ; assumes that there is only one outstanding command per target LUN. ; Therefore this routine will try to abort one command per call at this time. ; ; This routine first checks the outstanding commands in the SPDT to find ; the Q_Buffer with the matching SCDT. When it is found, an ABORT command ; Q_Buffer and an ABORT MARKER Q_Buffer are allocated. The ABORT Q_Buffer ; is queued to the DCCQ1 (higher priority command queue) and the ABORT MARKER ; Q_Buffer is queued to the DCCQ0. ; ; Note that this routine will return to the class driver immediately after ; sending the ABORT command. If the ABORT operation is successful, the aborted ; command is returned via the SEND_CMD interface with the SS$_ABORT return ; status. ; ; This routine assumes that the class driver has called this routine in Kernel ; Process context. If not, no abort attempt is made. ; ; NOTE: Current VMS SCSI class drivers do not abort any commands. This routine ; is provided for any third-party class drivers that might want to abort ; commands. ; ; INPUT: ; spdt(AP) = SPDT address ; scdrp(AP) = SCDRP address ; SCDRP$PS_KPB - Address of associated Kernel Process Block ; scdt(AP) = SCDT address ; ; OUTPUT: ; R0 = SS$_NORMAL ; .ENABLE LSB ; ; Define the offsets to the parameters pointed to by AP ; $ARG_DEF <- spdt, - ; SPDT address scdrp, - ; SCDRP address scdt > ; SCDT address PK$ABORT_COMMAND:: .CALL_ENTRY OUTPUT=,SCRATCH=,- PRESERVE= MOVL spdt(AP),R4 ; Get SPDT address MOVL scdrp(AP),R5 ; Get SCDRP address MOVL scdt(AP),R3 ; Get SCDT address MOVL SCDRP$PS_KPB(R5),R8 ; Is there a KPB? BEQL 200$ ; Exit if not MOVAL SPDT$PS_QCMDFL(R4),R2 ; Get queue header for ; outstanding commands MOVL R2,R6 ; Get working copy 10$: MOVL (R6),R6 ; Get next Q_Buffer in queue CMPL R6,R2 ; End of the queue? BEQL 200$ ; Branch if yes CMPL R3,QBUF$PS_SCDT(R6) ; Is this the command to abort? BNEQ 10$ ; If not, try next command BSBW GET_QUEUE_BUFFER ; Get a Q_Buffer ; R2 = just allocated Q_Buffer MOVL R2,R7 ; Copy ptr to Q_Buffer ;; MOVB #QBUF$C_SNDPM,QBUF$IB_OPC(R7) ; Set Q_Buffer OPC = SNDPM ;; MOVB QBUF$IB_CHNL_IDX(R6),- ; Copy channel index from the ;; QBUF$IB_CHNL_IDX(R7) ; original command buffer ;; MOVB #QBUF$C_ABRTCMD,- ; Set SCSI command opcode to ;; QBUF$IB_SCSI_OPC(R7) ; ABORT COMMAND .Disable Flagging ; Turn off information since ; it's ALIGNED ;; MOVQ QBUF$IQ_XCT_ID(R6),- ; Copy the original command's ;; QBUF$IQ_XCT_ID(R7) ; transaction ID field .Enable Flagging ;; BISW #QBUF$M_R,QBUF$IW_FLAGS(R7) ; Response requested BSBW GET_QUEUE_CARRIER ; Get a Carrier, R2 has addr MOVL R2,QBUF$PS_CARRIER(R7) ; Save the new Carrier's ; address in Q_Buf MOVL R2,R9 ; Save this Carrier's address BSBW GET_QUEUE_BUFFER ; Get a Q_Buffer ; R2 = just allocated Q_Buffer MOVL R2,R6 ; Copy ptr to Q_Buffer ;; MOVB #QBUF$C_SNDPM,QBUF$IB_OPC(R6) ; Set Q_Buffer OPC = SNDPM ;; MOVB QBUF$IB_CHNL_IDX(R7),- ; Copy channel index from the ;; QBUF$IB_CHNL_IDX(R6) ; ABRTCMD Q_Buffer ;; MOVB #QBUF$C_MRKABRT,- ; Set SCSI command opcode to ;; QBUF$IB_SCSI_OPC(R6) ; MARK ABORT .Disable Flagging ; Turn off information since ; it's ALIGNED ;; MOVQ QBUF$IQ_XCT_ID(R7),- ; Copy the transaction ID from ;; QBUF$IQ_XCT_ID(R6) ; the ABRTCMD Q_Buffer .Enable Flagging ;; BISW #QBUF$M_R,QBUF$IW_FLAGS(R6) ; Response requested BSBW GET_QUEUE_CARRIER ; Get a Carrier, R2 has addr ; on return MOVL R2,QBUF$PS_CARRIER(R6) ; Save new Carrier's address ; in Q_Buf ; ; At this point, we have: ; R2 = MRKABRT Carrier address ; R6 = MRKABRT Q_Buffer address ; R7 = ABRTCMD Q_Buffer address ; R9 = ABRTCMD Carrier address ; ;; MOVL SPDT$PS_DCCQ1T(R4),R1 ; Get the DCCQ1 tail ptr (stopper addr) .Disable Flagging ; Turn off information since it's ALIGNED MOVQ QBUF$PQ_PA_QBUF(R7),- ; Link Q_Buffer physical addr CAR$PQ_QBUF_PTR(R1) ; into stopper Carrier MOVL R7,CAR$PQ_QBUF_TOKEN(R1) ; Copy Q_Buffer virtual addr EVAX_MB ; Make sure Q_Buf contents and ; ptr are visible to the ; adapter before NEXT_PTR ; becomes valid EVAX_ADDQ #1,CAR$PQ_PA(R9),- ; Link in new stopper and set bit 0 to CAR$PQ_NEXT_PTR(R1) ; make it a valid carrier .Enable Flagging EVAX_MB ; Make sure new stopper is visible to ; the adapter before the adapter accesses ; the command queue DEVICELOCK - ; Get device lock and raise IPL LOCKADDR = SPDT$L_DLCK(R4),- LOCKIPL = SPDT$B_DIPL(R4),- SAVIPL = -(SP),- PRESERVE = NO ; ; Now we need to write to the CQIB (Command Queue Insertion Block) and QIR ; register to notify the adapter about the DCCQ1 entry insertion of the ; ABRTCMD. ; MOVL SPDT$PS_AB(R4),R3 ; Get the Adapter Block address EVAX_LDQ R0,CAR$PQ_PA(R9) ; Get the new stopper address ;; EVAX_SRL R0,#CQIB$V_SHIFT,R0 ; Shift right to start with PA bit <5> ;; EVAX_BIC R0,#CQIB$M_SBZ,R0 ; Clear out upper bits to get PA <39:5> ;; MOVL AB$PS_AMP_VA(R3),R1 ; Get Adapter Memory Page/CQIB address ;; CRAM_IO SPDT$PS_QIR(R4) ; Signal DCCQ1 entry insertion ;; MOVL R9,SPDT$PS_DCCQ1T(R4) ; Update tail link with new stopper VA ; ; Now set up the pointers for MRKABRT command ; MOVL SPDT$PS_DACQ_T(R4),R1 ; Get DACQ tail ptr (stopper addr) .Disable Flagging ; Turn off information since it's ALIGNED MOVQ QBUF$PQ_PA_QBUF(R6),- ; Link Q_Buffer physical addr CAR$PQ_QBUF_PTR(R1) ; into stopper Carrier MOVL R6,CAR$PQ_QBUF_TOKEN(R1) ; Copy Q_Buffer virtual addr EVAX_MB ; Make sure Q_Buf contents and ptr are ; visible to the adapter before NEXT_PTR ; becomes valid EVAX_ADDQ #1,CAR$PQ_PA(R2),- ; Link in new stopper and set bit 0 to CAR$PQ_NEXT_PTR(R1) ; make it a valid carrier .Enable Flagging EVAX_MB ; Make sure new stopper is visible to ; the adapter before the adapter accesses ; the command queue ;; CRAM_IO SPDT$PS_GCQIR(R4) ; Signal the port about DCCQ0 insertion MOVL R2,SPDT$PS_DACQ_T(R4) ; Update tail link with new stopper VA DEVICEUNLOCK - ; Release device lock LOCKADDR = SPDT$L_DLCK(R4),- NEWIPL = (SP)+,- PRESERVE = NO 200$: MOVZWL #SS$_NORMAL,R0 ; Set sucess status RET ; Return .DISABLE LSB ; PK$ABORT_COMMAND .PAGE .SBTTL PK$CMD_BUFFER_ALLOC - Command buffer allocate routine ; ; This routine is called by the port driver from the routine ; SC$COMMAND_BUFFER_ALLOC (in SCSICOMMON) to allocate a command ; buffer for the current I/O request. First of all, a Q_Buffer must be ; allocated. This is done by calling the subroutine, GET_QUEUE_BUFFER. Once ; a Q_Buffer is allocated and initialized for this I/O request, the pointer ; to the SCSI command buffer area is returned to the class driver. Note that ; the SCSI command buffer area returned to the class driver resides in the ; area of the Q_Buffer which is not visible to the adapter. This is due to ; the fact that the class driver requires 8 bytes of overhead for SCSI status ; and command length fields in front of the SCSI command descriptor block. ; ; NOTE: FUTURE SUPPORT -- The following function can be added when ; we support vendor-unique command size greater ; than 12 bytes. ; If the requested command buffer size is greater than 20 bytes ; (12 bytes of command + 8 bytes class driver overhead), this command ; will be assumed to be a vendor unique command and hence the Q_Buffer ; will be initialized to conform to the vendor unique command format. ; Also a separate command buffer will be allocated to hold the bigger ; vendor unique command and will be pointed to by the Q_Buffer. ; ; For now, if the requested size is greater than 20 bytes, an error status ; of SS$_BADPARAM will be returned. ; ; INPUT: ; spdt(AP) = Port SPDT address ; scdrp(AP) = Class SCDRP address ; buf_size(AP) = Requested size of command buffer ; ret_param_ptr(AP) = Return address to store address of ; class driver-visible command buffer. ; FORK IPL ; FORK lock held ; FORK thread ; R1 = Size of command buffer ; R4 = SPDT address ; R5 = SCDRP address ; SCDRP$PS_KPB - Address of the associated Kernel Process Block ; ; OUTPUT: ; R0 = Status ; If SS$_NORMAL status: ; @ret_param_ptr(AP) = Address of class driver-visible command ; buffer ; scdrp(AP) = Class SCDRP address ; SCDRP$PS_QBUF - Address of allocated Queue Buffer ; .ENABLE LSB ; ; Define the offsets to the parameters pointed to by AP ; $ARG_DEF <- spdt, - ; Port SPDT address scdrp, - ; Class SCDRP address buf_size, - ; Requested command buffer size ret_param_ptr > ; Return address to store ; address of class driver ; visible command buffer PK$CMD_BUFFER_ALLOC:: .CALL_ENTRY OUTPUT=,PRESERVE= MOVL spdt(AP),R4 ; Get SPDT address MOVL scdrp(AP),R5 ; Get SCDRP address MOVL buf_size(AP),R1 ; Get requested buffer size CMPL R1,#PKS_NORM_CMDSIZE ; Is this normal SCSI command? BGTR 20$ ; Branch if not MOVL R1,SCDRP$IS_CMD_BCNT(R5) ; Save byte count in SCDRP in ; case we're stalled MOVL R1,SCDRP$L_CMD_BUF_LEN(R5) ; Set both locations MOVL SCDRP$PS_KPB(R5),R8 ; Get KPB address BSBW GET_QUEUE_BUFFER ; Allocate a Queue Buffer MOVL R8,QBUF$PS_KPB(R2) ; Save KPB address in Q_Buffer MOVL R5,QBUF$PS_SCDRP(R2) ; Use SCDRP address as the ; command transaction ID MOVL R2,SCDRP$PS_QBUF(R5) ; Save address of Queue Buffer MOVAB QBUF$IB_CMDBUF(R2),R2 ; Get command buffer address MOVL R2,@ret_param_ptr(AP) ; Return class driver-visible ; command buffer address MOVZWL #SS$_NORMAL,R0 ; Set success status 10$: RET ; Return 20$: MOVZWL #SS$_BADPARAM,R0 ; Set error status BRB 10$ ; Return .DISABLE LSB ; PK$CMD_BUFFER_ALLOC .PAGE .SBTTL PK$CMD_BUFFER_DEALLOC - Command buffer deallocate routine ; ; This routine deallocates the Queue Buffer associated with the SCSI command ; buffer being returned by the class driver. ; ; NOTE: FUTURE SUPPORT -- The following function can be added when ; we support vendor-unique command size greater ; than 12 bytes. ; If the command buffer being returned was for a vendor-unique command ; larger than 12 bytes, deallocate the vendor unique command descriptor ; block that was previously allocated, before deallocating the Q_Buffer. ; ; INPUT: ; spdt(AP) = SPDT address ; scdrp(AP) = Class SCDRP address ; ; OUTPUT: ; R0 = Status ; .ENABLE LSB ; ; Define the offsets to the parameters pointed to by AP ; $ARG_DEF <- spdt, - ; Port SPDT address scdrp, > ; Class SCDRP address PK$CMD_BUFFER_DEALLOC:: .CALL_ENTRY OUTPUT=,PRESERVE= MOVL spdt(AP),R4 ; Get SPDT address MOVL scdrp(AP),R5 ; Get SCDRP address MOVL SCDRP$PS_QBUF(R5),R0 ; Queue Buffer to deallocate BSBW RET_QUEUE_BUFFER ; Return the Queue Buffer MOVZWL #SS$_NORMAL,R0 ; Set to success status RET ; Return .DISABLE LSB ; PK$CMD_BUFFER_DEALLOC .PAGE .SBTTL PKS$MAP_BUFFER - Map Buffer Routine ; ; This routine serves the same purpose as the SC$MAP_BUFFER routine in ; SCSICOMMON module. The routine will check the user data buffer and ; allocate the necessary resources to represent the data buffer to the ; channel. Resources include: ; ; 1.) Allocating and setting up a pad block if required. ; 2.) Allocating and setting up a Buffer Segment Map (BSM) if required. ; 3.) Setting up one or two Buffer Segment Descriptors (BSDs) in the Queue ; Buffer if required. ; ; A pad block will be allocated if required. ; ; If the data buffer spans more than two physical port pages, A 32-byte ; aligned Buffer Segment Map (BSM) will be allocated to point to each page ; including the beginning page and the end page which might not be a full ; page of the data buffer in physical address format. ; ; If the data buffer only spans two physical port pages, the Buffer Segment ; Descriptors (BSDs) in the Queue Buffer can be used instead of a Buffer ; Segment Map (BSM) to point to these two pages. ; ; If there is no data to be transferred, this routine just returns SS$_NORMAL. ; ; INPUT: ; spdt(AP) = SPDT address ; scdrp(AP) = SCDRP address ; SCDRP$L_BCNT - Transfer byte count ; SCDRP$L_BOFF - Byte offset in page ; SCDRP$L_SVAPTE - System Virtual Address of the first ; P0 Page Table Entry ; SCDRP$L_SCSI_FLAGS - Flags used for mapping and unmapping ; functions ; SCDRP$V_FLAG_S0BUF - If set then data ; buffer is in S0 space ; SCDRP$L_SVA_USER - S0 buffer if SCDRP$V_FLAG_S0BUF is set ; SCDRP$PS_QBUF - Address of the Q_Buffer allocated ; SCDRP$PS_KPB - Address of the associated KPB ; ; OUTPUT: ; R0 = Status ; If SCDRP$L_PAD_BCNT <> 0 or # of 8 KB segments greater than 2, ; QBUF$PS_SGMAP - Address of the scatter-gather map ; .ENABLE LSB ; ; Define the offsets to the parameters pointed to by AP. ; $ARG_DEF <- spdt, - ; SPDT address scdrp > ; SCDRP address PKS$MAP_BUFFER:: .CALL_ENTRY OUTPUT=,SCRATCH=,- PRESERVE= MOVL spdt(AP),R4 ; Get SPDT address MOVL scdrp(AP),R5 ; Get SCDRP address TSTL SCDRP$L_BCNT(R5) ; Is there a data buffer? BEQL 100$ ; No, then just return success ; ; Get the SVAPTE that maps the data buffer. ; ASSUME SCDRP$V_FLAG_S0BUF EQ 0 ; S0 bit must be bit zero of ; SCSI_FLAGS BLBC SCDRP$L_SCSI_FLAGS(R5),10$ ; Is this an S0 data buffer? MOVL SCDRP$L_SVA_USER(R5),R2 ; Yes, get S0 virtual address EVAX_SLL R2,#<64-VA$V_SYSTEM>,R0 ; Clear space select bit EVAX_SRL R0,MMG$GQ_64SYS_SHIFT,R0 ; Get virtual page number MOVAQ @MMG$GL_SPTBASE[R0],R7 ; Get SVAPTE that maps VA BRB 20$ ; Join common code 10$: MOVL SCDRP$L_SVAPTE(R5),R7 ; Get SVAPTE of P0 data buffer ; ; Allocate a pad block for this transfer if required. ; 20$: MOVL SCDRP$PS_QBUF(R5),R6 ; Get Queue Buffer address TSTL SCDRP$L_PAD_BCNT(R5) ; Is the pad count 0? BEQL 40$ ; Yes, don't need pad block TSTL QBUF$PS_PADBLK(R6) ; Do we already had a Pad Block? BNEQ 40$ ; Yes, must have been call by _MR INCW QBUF$IW_NUMSEG(R6) ; Pad block counts as a segment 30$: ADDL3 #PAD$C_HEADER,- ; # of bytes to allocate SCDRP$L_PAD_BCNT(R5),R1 SUBL #4,SP ; Allocate a longword on stack PUSHL SP ; Arg 4 = addr to receive size PUSHAB QBUF$PS_PADBLK(R6) ; Arg 3 = addr to receive addr ; of allocated pad block PUSHL #ALN_LONG ; Arg 2 = longword alignment PUSHL R1 ; Arg 1 = requested block size CALLS #4,EXE$ALONONPAGED_ALN ; Allocate a pad block POPL R1 ; Get the allocated size .BRANCH_LIKELY BLBS R0,35$ ; Did we get a block? MOVL SCDRP$PS_KPB(R5),R0 ; No, get KPB address KP_STALL_FORK_WAIT KPB=R0,- ; Fork and wait FKB=KPB$PS_UCB(R0) ; using class driver UCB BRB 30$ ; Try allocation again 35$: MOVL QBUF$PS_PADBLK(R6),R2 ; Get the address of pad block PUSHR #^M ; Save registers MOVC5 #0,(SP),#0,R1,(R2) ; Zero initialize the block POPR #^M ; Restore registers MOVW R1,PAD$IW_SIZE(R2) ; Set the allocated block size MOVB #DYN$C_MISC,PAD$IB_TYPE(R2) ; Set type MOVB #DYN$C_PADBLK,PAD$IB_SUBTYPE(R2); Set subtype ; ; Setup data transfer size in CCB. ; 40$: ADDL3 SCDRP$L_BCNT(R5),- ; Calculate and store SCDRP$L_PAD_BCNT(R5),- ; data transfer length QBUF$IS_DATA_TRAN_SIZE(R6) ; in Queue Buffer ; ; Calculate the number of segments we need to set up the pointers (BSDs) for ; and the number of current system's PTEs to deal with. ; ;; ADDL3 SCDRP$L_BCNT(R5),- ; Get transfer byte count ;; SCDRP$L_BOFF(R5),R8 ; Add byte offset in page ;; $BYTES_TO_PAGES- ; Compute segment count ;; SOURCE_BYTCNT = R8,- ;; DEST_PAGCNT = R3,- ;; ROUNDUP = YES ;; ADDW R3,QBUF$IW_NUMSEG(R6) ; Add to data segment count MOVL SCDRP$L_BCNT(R5),R3 ; Get transfer byte count MOVL SCDRP$L_BOFF(R5),R1 ; Get byte offset in page ADDL3 R3,R1,R8 ; Add BCNT and BOFF ADDL MMG$GL_BWP_MASK,R8 ; Calculate highest relative ; byte and round ASHL MMG$GL_VA_TO_VPN,R8,R8 ; Calculate the number of PTEs BICL #^C,R1 ; Clear upper bits for offsets ; greater than PAGE_SIZE MOVAB PKS$M_BYTE_IN_PAGE(R3)[R1],R3 ; Calculate highest relative ; byte and round ASHL #-PKS$S_BYTE_IN_PG,R3,R3 ; Number of PAGE_SIZE segments ADDW R3,QBUF$IW_NUMSEG(R6) ; Add to data segment count ; ; Now setup the mapping pointers for the data buffer. If there are more than ; two segments, then we must use a Buffer Segment Map (BSM). If two or less ; segments are required, then we can use the Buffer Segment Descriptors (BSDs) ; in the CCB. ; ; Note: ; The Turbochannel requires all transfers to start on a longword boundary. ; Determine if the data buffer is unaligned. If so, we must move the data ; in the first longword to a temporary aligned buffer so that the data starts ; on a longword boundary. If this is a write function and the data does not ; end on a longword boundary, then we must also re-map the data in the last ; longword as well. ; ; We must check for unaligned buffers here because the mapping for unaligned ; buffers requires one or two additional segments (QBUF$IW_NUMSEG). ; ; If there is any leading or trailing unalignment, then a BSM will be used ; (regardless of the number of BSDs required). ; ; At this point: ; R4 = SPDT address ; R5 = SCDRP address ; R6 = Queue Buffer address ; R7 = SVAPTE of S0 or P0 data buffer ; QBUF$IW_NUMSEG(R6) = Number of PAGE_SIZE data segments ; BITL #LW_ALIGNED,SCDRP$L_BOFF(R5) ; Check leading alignment BNEQ 45$ ; Is it longword aligned? CMPL SCDRP$L_BCNT(R5),#2 ; Yes, check transfer size BLEQ 45$ ; Is it a small transfer? BBC #IRP$V_FUNC,- ; No, are we reading from the SCDRP$IS_STS(R5),42$ ; device? ADDL3 SCDRP$L_BOFF(R5),- ; Yes, point to last byte SCDRP$L_BCNT(R5),R0 BITL #LW_ALIGNED,R0 ; Check trailing alignment BNEQ 45$ ; Is it longword aligned? 42$: CMPW QBUF$IW_NUMSEG(R6),#2 ; Yes, get the segment count BLEQ 50$ ; Is it a small transfer? 45$: BSBW SETUP_BSM ; No, set up a BSM BRB 100$ ; Return ; ; If we come here, then the following conditions exist: ; - There is no leading or trailing unalignment. ; - The user buffer can be mapped by one or two Buffer Segment Descriptors. ; - We will setup Buffer Segment Descriptors located in the CCB. ; 50$: TSTL SCDRP$L_PAD_BCNT(R5) ; If the pad count is not 0 BNEQ 70$ ; then we have a pad block ; ; No pad block is required. The data buffer spans one or two pages. ; CMPW QBUF$IW_NUMSEG(R6),#2 ; Check number of segments BEQL 55$ ; Is there another segment? MOVL SCDRP$L_BCNT(R5),R3 ; No, get transfer byte count BRW 57$ ; Setup the BSD 55$: SUBL3 SCDRP$L_BOFF(R5),- ; Number of bytes transferred G^MMG$GL_PAGE_SIZE,R3 ; by first BSD 57$: BUILD_BSD - ; Build BSD for first segment BSD_ADDR = QBUF$PS_DATA_BSD1(R6),- BUFFER_SVAPTE = R7,- BYTE_OFFSET = SCDRP$L_BOFF(R5),- BYTE_COUNT = R3 CMPW QBUF$IW_NUMSEG(R6),#2 ; Check number of segments BNEQ 100$ ; Is there another segment? ADDL #PTE$C_BYTES_PER_PTE,R7 ; Yes, point to second PTE SUBL3 SCDRP$L_BOFF(R5),- ; Get number of bytes transferred G^MMG$GL_PAGE_SIZE,R3 ; by first BSD SUBL3 R3,SCDRP$L_BCNT(R5),R3 ; Get number of bytes transferred ; by second BSD BUILD_BSD - ; Build BSD for second segment BSD_ADDR = QBUF$PS_DATA_BSD2(R6),- BUFFER_SVAPTE = R7,- BYTE_OFFSET = #0,- BYTE_COUNT = R3 BRW 100$ ; Return ; ; A pad block is required. The data buffer spans one page. The pad block ; is pointed to by the second BSD. (Always need 2 BSDs in this case.) ; First setup BSD for the data buffer. ; 70$: BUILD_BSD - ; Build a BSD for data segment BSD_ADDR = QBUF$PS_DATA_BSD1(R6),- BUFFER_SVAPTE = R7,- BYTE_OFFSET = SCDRP$L_BOFF(R5),- BYTE_COUNT = SCDRP$L_BCNT(R5) ; ; Now setup the BSD for the pad block. ; MOVL QBUF$PS_PADBLK(R6),R1 ; Get PAD block address ADDL2 #PAD$C_HEADER,R1 ; Point to pad data BUILD_BSD - ; Build a BSD for the pad block BSD_ADDR = QBUF$PS_DATA_BSD2(R6),- BUFFER_SVA = R1,- BYTE_COUNT = SCDRP$L_PAD_BCNT(R5) 100$: MOVZWL #SS$_NORMAL,R0 ; Set success status RET ; Return .DISABLE LSB ; PKS$MAP_BUFFER .PAGE .SBTTL SETUP_BSM - Set up Buffer Segment Map ; ; This subroutine is called from the PKS$MAP_BUFFER routine when ; we need to use type 1 data pointers in a Buffer Segment Map to ; pass down the data buffer to the SIMport channel. ; ; INPUT: ; R4 = SPDT address ; R5 = SCDRP address ; SCDRP$L_BCNT - Transfer byte count ; SCDRP$L_PAD_BCNT - Number of bytes required to pad to end of ; block ; SCDRP$L_BOFF - Byte offset in page ; SCDRP$PS_KPB - Address of the associated Kernel Process Block ; R6 = Queue Buffer address ; QBUF$IW_NUMSEG - Number of 8KB data segments ; R7 = SVAPTE of S0 or P0 data buffer ; R8 = Number of PTEs that maps the data buffer ; ; OUTPUT: ; R6 = Queue Buffer address ; QBUF$PS_BSM_PTR - Address of Buffer Segment Map ; QBUF$PS_DATA_BSD1 - Has type 1 data pointer which points to ; the Buffer Segment map ;-- .ENABLE LSB SETUP_BSM: .JSB_ENTRY INPUT=, - SCRATCH=, PRESERVE= ; ; Allocate a hexword aligned block for a BSM. ; 10$: MOVL #BSM$C_LENGTH,R1 ; Size of BSM SUBL #4,SP ; Allocate a longword in stack PUSHL SP ; Arg 4 = addr to receive size PUSHAB QBUF$PS_BSM_PTR(R6) ; Arg 3 = addr to receive addr ; of allocated BSM PUSHL #ALN_HEXWORD ; Arg 2 = hexword alignment PUSHL R1 ; Arg 1 = block size requested CALLS #4,EXE$ALONONPAGED_ALN ; Allocate a BSM POPL R1 ; Get the allocated size BLBS R0,12$ ; Branch if success MOVL SCDRP$PS_KPB(R5),R0 ; Get KPB address KP_STALL_FORK_WAIT KPB=R0,- ; Fork and wait FKB=KPB$PS_UCB(R0) ; using class driver UCB BRB 10$ ; Try allocation again ; ; Zero the BSM block and fill in static fields. ; 12$: MOVL QBUF$PS_BSM_PTR(R6),R2 ; Get addr of BSM PUSHR #^M ; Save registers MOVC5 #0,(SP),#0,R1,(R2) ; Zero initialize the block POPR #^M ; Restore registers MOVW R1,BSM$IW_SIZE(R2) ; Set the allocated block size MOVB #DYN$C_MISC,BSM$IB_TYPE(R2) ; Set type MOVB #DYN$C_SGMAP,BSM$IB_SUBTYPE(R2) ; Set subtype ADDL3 SCDRP$L_BCNT(R5),- ; Total number of bytes plus SCDRP$L_PAD_BCNT(R5),- ; PAD bytes equals total count BSM$IS_TOTAL_COUNT(R2) ; mapped by this BSM ; ; Put physical address of BSM in the BSM. Allocate and use map registers ; if needed. ; $SVA_TO_PA SVA = R2 ; Get physical address BBC #SPDT$V_PFLG_MAPPING_REG,- ; Are we using map registers? SPDT$L_PORT_FLAGS(R4),18$ EVAX_ADDQ #BSM$C_LENGTH,R0,R3 ; Yes, point to last byte in BSM EVAX_CMPULE R3,SPDT$IQ_DMA_SIZE(R4),R11 ; Compare to DMA window size BLBS R11,18$ ; Branch if LE window size ; ; The BSM is above the direct DMA window so we have to map it. We can use ; MAP_S0, because we are executing in a KP, so we can stall if we don't get ; the registers. ; MAP_S0 SVA = R2,- ; BSM address (VA) NBYTES = #BSM$C_LENGTH,- ; Size to map CRCTX = BSM$PS_CRCTX(R2),- ; CRCTX pointer reference KPB$ = SCDRP$PS_KPB(R5),- ; KPB to stall on alloc fail DMA_ADDR = BSM$PQ_PA(R2) ; DMA-able physical address BRB 19$ ; Join common code ; ; Map registers not needed. Just use physical address + DMA base. ; 18$: ADDL2 SPDT$L_DMA_BASE(R4),R0 ; Add in DMA window base EVAX_ZAP R0,#^XF0,R0 ; Clear the upper longword EVAX_STQ R0,BSM$PQ_PA(R2) ; Store BSM PA in BSM ; ; Build a BSD to point to this BSM. Put the BSD into the Private Data Area ; of the CCB (QBUF$PS_DATA_BSD1) and mark this BSD as a type 1 pointer. ; 19$: EVAX_LDQ R9,BSM$PQ_PA(R2) ; PA of start of BSM EVAX_ADDQ #BSM$PS_NEXT_BSM_BSD,R9,R9 ; PA of adapter visible section BUILD_BSD - ; Build a BSD for this BSM BSD_ADDR = QBUF$PS_DATA_BSD1(R6),- BUFFER_PA = R9,- BYTE_COUNT = BSM$IS_TOTAL_COUNT(R2),- TYPE = TP$C_TYPE1 MOVAB BSM$PS_BSD0(R2),R11 ; Point to first BSD entry ; ; At this point: ; R2 = BSM address ; R5 = SCDRP address ; R6 = Queue Buffer address ; R7 = SVAPTE of S0 or P0 data buffer ; R8 = # of PTEs that maps the data buffer ; R11= Pointer to first BSD entry in BSM ; ; Dispatch based on transfer size to build the BSM. ; CMPL SCDRP$L_BCNT(R5),#2 ; Check byte count BGTR LARGE_TRANSFER ; Branch for large transfers BEQL TWO_BYTES ; and two byte transfers ; ; This is a one byte transfer. ; ONE_BYTE: MOVL SCDRP$L_BCNT(R5),- ; Setup lead data count BSM$IS_LEADING_COUNT(R2) EVAX_LDQ R10,BSM$PQ_PA(R2) ; PA of start of BSM EVAX_ADDQ #BSM$PS_LEADING_BUFF,R10,R10 ; PA of aligned data buffer BUILD_BSD - ; Build a BSD for leading BSD_ADDR = (R11),- ; unaligned segment BUFFER_PA = R10,- BYTE_COUNT = SCDRP$L_BCNT(R5) ADDL2 #BSD$C_LENGTH,R11 ; Point to next BSM entry BBS #IRP$V_FUNC,SCDRP$IS_STS(R5),95$ ; Are we writing to the device? ASSUME SCDRP$V_FLAG_S0BUF EQ 0 ; Verify position of S0 bit BLBC SCDRP$L_SCSI_FLAGS(R5),21$ ; Yes, is this an S0 buffer? MOVL SCDRP$L_SVA_USER(R5),R1 ; Yes, get S0 virtual address BRW 215$ ; Join common code 21$: JSB MAP_BUFF ; Yes, map to user data buffer ; R1 = address of page with ; unaligned user data buffer MOVL R1,R9 ; Save page address ADDL SCDRP$L_BOFF(R5),R1 ; Point to unaligned data byte 215$: MOVB (R1),BSM$PS_LEADING_BUFF(R2) ; Align the byte in lead buffer ASSUME SCDRP$V_FLAG_S0BUF EQ 0 ; Verify position of S0 bit BLBS SCDRP$L_SCSI_FLAGS(R5),95$ ; Is this an S0 buffer? MOVL R9,R1 ; Restore page address JSB UNMAP_BUFF ; No, unmap from user buffer BRW 95$ ; Check for padding ; ; This is a two byte transfer. ; TWO_BYTES: MOVL G^MMG$GL_PAGE_SIZE,R9 ; Get page size DECL R9 ; minus 1 CMPL SCDRP$L_BOFF(R5),R9 ; Check if bytes span a page BGEQ 24$ ; Do the bytes span a page? ; ; If here, then the 2 bytes are on the same page. ; BBS #IRP$V_FUNC,- ; No, are we writing to the SCDRP$IS_STS(R5),23$ ; device? ASSUME SCDRP$V_FLAG_S0BUF EQ 0 ; Verify position of S0 bit BLBC SCDRP$L_SCSI_FLAGS(R5),224$ ; Yes, is this an S0 buffer? MOVL SCDRP$L_SVA_USER(R5),R1 ; Yes, get S0 virtual address BRB 225$ ; Join common code 224$: JSB MAP_BUFF ; Map to user P0 data buffer ; R1 = address of page with ; unaligned user data buffer MOVL R1,R9 ; Save page address ADDL SCDRP$L_BOFF(R5),R1 ; Point to unaligned data bytes 225$: MOVB (R1)+,BSM$PS_LEADING_BUFF(R2) ; Align 1st byte in lead buffer MOVB (R1),BSM$PS_LEADING_BUFF+1(R2) ; Align 2nd byte in lead buffer ASSUME SCDRP$V_FLAG_S0BUF EQ 0 ; Verify position of S0 bit BLBS SCDRP$L_SCSI_FLAGS(R5),23$ ; Is this an S0 buffer? MOVL R9,R1 ; No, restore page address JSB UNMAP_BUFF ; Unmap from user buffer 23$: MOVL SCDRP$L_BCNT(R5),- ; Setup lead data count in BSM BSM$IS_LEADING_COUNT(R2) EVAX_LDQ R10,BSM$PQ_PA(R2) ; PA of start of BSM EVAX_ADDQ #BSM$PS_LEADING_BUFF,R10,R10 ; PA of aligned data buffer BUILD_BSD - ; Build a BSD for leading BSD_ADDR = (R11),- ; unaligned segment BUFFER_PA = R10,- BYTE_COUNT = SCDRP$L_BCNT(R5) ADDL2 #BSD$C_LENGTH,R11 ; Point to next BSM entry BRW 95$ ; Check for padding ; ; If here, then the 2 bytes span a page boundary. ; 24$: BBS #IRP$V_FUNC,- ; Are we writing to the device? SCDRP$IS_STS(R5),26$ BLBC SCDRP$L_SCSI_FLAGS(R5),25$ ; Yes, is this an S0 buffer? MOVL SCDRP$L_SVA_USER(R5),R1 ; Yes, get S0 virtual address BRB 255$ ; Join common code 25$: JSB MAP_BUFF ; Map to user P0 data buffer ; R1 = address of page with ; 1st unaligned user data byte MOVL R1,R9 ; Save page address ADDL SCDRP$L_BOFF(R5),R1 ; Point to unaligned data bytes 255$: MOVB (R1),BSM$PS_LEADING_BUFF(R2) ; Align 1st byte in lead buffer ASSUME SCDRP$V_FLAG_S0BUF EQ 0 ; Verify position of S0 bit BLBC SCDRP$L_SCSI_FLAGS(R5),256$ ; Is this an S0 buffer? INCB R1 ; Yes, point to next byte BRW 257$ ; Join common code 256$: MOVL R9,R1 ; Restore page address JSB UNMAP_BUFF ; Unmap from user buffer ADDL2 #PTE$C_BYTES_PER_PTE,R7 ; Point to next Page frame JSB MAP_BUFF ; Map to P0 user data buffer ; R1 = address of page with ; 2nd user data byte MOVL R1,R9 ; Save page address 257$: MOVB (R1),BSM$PS_TRAILING_BUFF(R2) ; Align 2nd byte in trail buffer ASSUME SCDRP$V_FLAG_S0BUF EQ 0 ; Verify position of S0 bit BLBS SCDRP$L_SCSI_FLAGS(R5),26$ ; Is this an S0 buffer? MOVL R9,R1 ; Restore page address JSB UNMAP_BUFF ; No, unmap from P0 user buffer 26$: MOVL #1,BSM$IS_LEADING_COUNT(R2) ; Setup leading count MOVL #1,BSM$IS_TRAILING_COUNT(R2) ; Setup trailing count EVAX_LDQ R10,BSM$PQ_PA(R2) ; PA of start of BSM EVAX_ADDQ #BSM$PS_LEADING_BUFF,R10,R10 ; PA of aligned data buffer BUILD_BSD - ; Build a BSD for leading BSD_ADDR = (R11),- ; unaligned segment BUFFER_PA = R10,- BYTE_COUNT = #1 ADDL2 #BSD$C_LENGTH,R11 ; Point to next BSM entry EVAX_LDQ R10,BSM$PQ_PA(R2) ; PA of start of BSM EVAX_ADDQ #BSM$PS_TRAILING_BUFF,R10,R10 ; PA of aligned data buffer BUILD_BSD - ; Build a BSD for trailing BSD_ADDR = (R11),- ; unaligned segment BUFFER_PA = R10,- BYTE_COUNT = #1 ADDL2 #BSD$C_LENGTH,R11 ; Point to next BSM entry BRW 95$ ; Check for padding ; ; If here, then this I/O request is larger than 2 bytes. ; ; - If there is leading unalignment: ; - Setup the unaligned lead count. ; - Build a BSD to point to the aligned leading buffer. ; - If the leading unaligned bytes are the last bytes on a page AND this is ; a user P0 data buffer, then point R7 (SVAPTE) to the next PTE. ; - If this I/O request is writing to a device, then copy the leading ; unaligned data bytes from the user buffer to the aligned leading buffer ; in the BSM. ; - Build BSDs for any aligned data. ; - If there is trailing unalignment AND this I/O request is reading from a ; device, setup the trailing unaligned byte count and build a BSD to point ; to the aligned trailing bytes buffer in the BSM. ; ; Note: If there is trailing unalignment AND this I/O request is writing to ; a device, nothing has to be done to setup BSDs. The adapter can read ; an odd number of aligned bytes from the user data buffer. ; LARGE_TRANSFER: BITL #LW_ALIGNED,SCDRP$L_BOFF(R5) ; Check leading alignment BEQL 28$ ; Is it longword aligned? MOVL SCDRP$L_BOFF(R5),R3 ; No, get byte offset BICL2 #^C,R3 ; Clear upper bits to get ; offset into longword SUBL3 R3,#4,R3 ; Get number of unaligned bytes MOVL R3,BSM$IS_LEADING_COUNT(R2) ; Setup lead unalignment count MOVL R3,R10 ; Working copy of byte count EVAX_LDQ R9,BSM$PQ_PA(R2) ; PA of start of BSM EVAX_ADDQ #BSM$PS_LEADING_BUFF,R9,R9 ; PA of aligned data buffer BUILD_BSD - ; Build a BSD for leading BSD_ADDR = (R11),- ; unaligned segment BUFFER_PA = R9,- BYTE_COUNT = R10 ADDL2 #BSD$C_LENGTH,R11 ; Point to next BSM entry BBS #IRP$V_FUNC,SCDRP$IS_STS(R5),28$ ; Is this a write function? ASSUME SCDRP$V_FLAG_S0BUF EQ 0 ; Verify position of S0 bit BLBC SCDRP$L_SCSI_FLAGS(R5),274$ ; Yes, is this an S0 buffer? MOVL SCDRP$L_SVA_USER(R5),R1 ; Yes, get S0 virtual address BRW 275$ ; Join common code 274$: JSB MAP_BUFF ; Map to P0 user data buffer MOVL R1,R9 ; Save VA of first page ADDL SCDRP$L_BOFF(R5),R1 ; Point to first data byte 275$: PUSHR #^M ; Save registers MOVC3 R3,(R1),BSM$PS_LEADING_BUFF(R2) ; Copy unaligned data bytes to ; LEADING_BUFF POPR #^M ; Restore registers ASSUME SCDRP$V_FLAG_S0BUF EQ 0 ; Verify position of S0 bit BLBS SCDRP$L_SCSI_FLAGS(R5),276$ ; Is this an S0 buffer? MOVL R9,R1 ; No, restore VA of first page JSB UNMAP_BUFF ; Unmap from P0 user buffer 276$: CMPL BSM$IS_LEADING_COUNT(R2),- ; Unaligned bytes = byte count? SCDRP$L_BCNT(R5) BEQL 90$ ; Branch if yes ; ; If here, then the following conditions exist: ; - Any leading longword unaligned data has been set up in the first BSD. ; If this is a write to disk function, then any leading unaligned data ; has been copied to LEADING_BUFF. ; - The remaining data is longword aligned (although it may end with ; unaligned bytes.) ; - R7 (SVAPTE) is pointing to the correct page table entry. ; ; Before filling in BSDs for aligned data, check for transfers that have ; only leading unaligned and trailing unaligned data. If so, then just ; fill in the trailing unaligned data. Else, fill in the BSDs for any aligned ; data. ; 28$: BBC #IRP$V_FUNC,SCDRP$IS_STS(R5),29$ ; Is this a read function? ADDL3 SCDRP$L_BOFF(R5),- ; Yes, point to last data byte SCDRP$L_BCNT(R5),R3 BICL #^C,R3 ; Clear upper bits to get # of ; unaligned trailing bytes MOVL R3,BSM$IS_TRAILING_COUNT(R2) ; Setup unaligned trailing count ADDL3 BSM$IS_LEADING_COUNT(R2),R3,R3 ; Get total unaligned bytes CMPL R3,SCDRP$L_BCNT(R5) ; Unaligned bytes = byte count? BEQL 90$ ; Branch if yes ; ; We need to setup BSDs for any aligned data. The first page might not ; start on a page boundary so the first BSD requires special handling. ; 29$: MOVZWL QBUF$IW_NUMSEG(R6),R9 ; Get number of BSDs required TSTL SCDRP$L_PAD_BCNT(R5) ; Get PAD byte count BEQL 30$ ; Is the pad count 0? DECL R9 ; No, subtract 1 from # segments 30$: MOVL SCDRP$L_BCNT(R5),R10 ; Number of data bytes SUBL2 BSM$IS_LEADING_COUNT(R2),R10 ; minus leading unaligned bytes MOVL SCDRP$L_BOFF(R5),R1 ; Get byte offset in first page ADDL2 BSM$IS_LEADING_COUNT(R2),R1 ; plus leading unaligned bytes CMPL R1,G^MMG$GL_PAGE_SIZE ; Check byte offset BLSS 31$ ; Are we at the end of a page? CLRL R1 ; Yes, rollover to next page ADDL #PTE$C_BYTES_PER_PTE,R7 ; Point to next PTE 31$: BICL #^C,R1 ; Clear upper bits for offsets ; greater than 8 KB MOVL G^MMG$GL_PAGE_SIZE,R0 ; Get # of bytes in port page SUBL2 R1,R0 ; Get # bytes in first segment CMPL R0,R10 ; Is size bigger than BCNT? BLEQ 35$ ; Branch if not MOVL R10,R0 ; Set correct size if BCNT is ; smaller than port page size ; minus BOFF) 35$: SUBL2 R0,R10 ; Subtract from total byte count MOVL R0,R3 ; Setup byte count BUILD_BSD - ; Build a BSD for this segment BSD_ADDR = (R11),- BUFFER_SVAPTE = R7,- BYTE_OFFSET = R1,- BYTE_COUNT = R3 ADDL #BSD$C_LENGTH,R11 ; Point to next BSM entry DECL R9 ; Account for this segment BEQL 90$ ; Branch if no more data segment ; ; Loop for remaining aligned pages to map. ; 40$: CMPL R10,BSM$IS_TRAILING_COUNT(R2) ; Check remaining byte count BLEQ 90$ ; Are just trailing bytes left? ADDL #PTE$C_BYTES_PER_PTE,R7 ; Point to next PTE MOVL G^MMG$GL_PAGE_SIZE,R3 ; Get # of bytes in port page CMPL R10,R3 ; Check remaining byte count BGEQ 45$ ; Greater or equal to pagesize? MOVL R10,R3 ; No, get remaining byte count 45$: BUILD_BSD - ; Build a BSD for this segment BSD_ADDR = (R11),- BUFFER_SVAPTE = R7,- BYTE_OFFSET = #0,- BYTE_COUNT = R3 ADDL #BSD$C_LENGTH,R11 ; Point to next BSM entry SUBL R3,R10 ; Subtract length from ; remaining data count SOBGTR R9,40$ ; Process next data segment ; ; Check for trailing unalignment. ; 90$: ADDL3 SCDRP$L_BOFF(R5),- ; Point to last data byte SCDRP$L_BCNT(R5),R3 BITL #LW_ALIGNED,R3 ; Check trailing alignment BEQL 95$ ; Is it longword aligned? BBC #IRP$V_FUNC,- ; No, are we reading from the SCDRP$IS_STS(R5),95$ ; device? BICL #^C,R3 ; Yes, get # of unaligned bytes MOVL R3,BSM$IS_TRAILING_COUNT(R2) ; Setup trail unalignment count EVAX_LDQ R10,BSM$PQ_PA(R2) ; PA of start of BSM EVAX_ADDQ #BSM$PS_TRAILING_BUFF,R10,R10 ; PA of aligned data buffer BUILD_BSD - ; Build a BSD for trailing BSD_ADDR = (R11),- ; unaligned segment BUFFER_PA = R10,- BYTE_COUNT = R3 ; ; Since the unaligned trailing bytes require another BSD, we must subtract ; the unaligned trailing byte count from the byte count in the previous BSD. ; However, if this I/O request contains only leading and trailing bytes, ; (that is if LEAD_COUNT + TRAIL_COUNT = BCNT) then the leading count is ; already correct. No need to subtract. ; ; In addition, if the trailing bytes are the first bytes on a page, then ; no need to subtract either. ; ADDL3 BSM$IS_LEADING_COUNT(R2),- ; Get total unaligned bytes BSM$IS_TRAILING_COUNT(R2),R3 CMPL R3,SCDRP$L_BCNT(R5) ; Check against total byte count BEQL 94$ ; Unaligned bytes = byte count? 93$: ADDL3 SCDRP$L_BOFF(R5),- ; No, point to last byte SCDRP$L_BCNT(R5),R3 SUBL2 BSM$IS_TRAILING_COUNT(R2),R3 ; Make it longword aligned BITL #^X1FFF,R3 ; Check page alignment BEQL 94$ ; Are we on a page boundary? SUBL2 #BSD$C_LENGTH,R11 ; No, point to previous BSD EVAX_LDQ R10,(R11) ; Get previous BSD entry EVAX_SRL R10,#BSD$C_BYTE_COUNT_SHIFT,R10 ; Clear out PA SUBW2 BSM$IS_TRAILING_COUNT(R2),R10 ; Decrement byte count EVAX_SLL R10,#BSD$C_BYTE_COUNT_SHIFT,R10 ; Shift byte count to last word EVAX_BIC (R11),#BSD$M_BYTE_COUNT,(R11) ; Clear previous byte count EVAX_OR (R11),R10,(R11) ; Insert new byte count into BSD ADDL2 #BSD$C_LENGTH,R11 ; Point back to our BSD entry 94$: ADDL2 #BSD$C_LENGTH,R11 ; Point to next BSD entry ; ; If we allocated a pad buffer, set up the next BSD in the BSM to point to ; the pad buffer. ; 95$: TSTL SCDRP$L_PAD_BCNT(R5) ; Check the pad count BEQL 100$ ; Is the pad count 0? ADDL3 #PAD$C_HEADER,- ; No, point to pad block QBUF$PS_PADBLK(R6),R1 BUILD_BSD - ; Build a BSD for pad buffer BSD_ADDR = (R11),- BUFFER_SVA = R1,- BYTE_COUNT = SCDRP$L_PAD_BCNT(R5) ADDL2 #BSD$C_LENGTH,R11 ; Point to next BSM entry ; ; Setup the number of segments (BSDs) in this BSM. ; 100$: MOVAB BSM$PS_BSD0(R2),R10 ; Address of first BSD entry SUBL2 R10,R11 ; Subtract from current BSD addr ASHL #-3,R11,R11 ; Divide by the size of a BSD MOVW R11,BSM$IW_NUM_ENTRIES(R2) ; Setup number of BSDs in BSM RSB ; Return to PKS$MAP_BUFFER .DISABLE LSB ; SETUP_BSM .PAGE .SBTTL MAP_BUFF - Map User Date Buffer Routine ; ; This routine maps the user data buffer for performing unaligned data ; transfers. ; ; INPUT: ; R4 = SPDT address ; R5 = SCDRP address ; R7 = SVAPTE of user data buffer ; ; OUTPUT: ; R1 = Address of page with longword that contains unaligned user data ; .ENABLE LSB MAP_BUFF: .JSB_ENTRY INPUT=, OUTPUT=, - PRESERVE= MOVL SPDT$L_SPTE_SVAPTE(R4),R0 EVAX_LDQ R3,(R7) ; Get user PTE BLBC R3,32$ ; If LBS, then it's valid EVAX_SRL R3,#PTE$V_PFN,R3 ;S FF; Else, shift PFN to low-order bits BRB 33$ ;S FF; Join common new PTE builder code 32$: CALL_PTETOPFN SAVE_R0R1=YES ;S FF; If PTE not valid, convert to PFN 33$: EVAX_SLL R3,#PTE$V_PFN,R3 ;S FF; Get PFN in the right place EVAX_OR R3,- ;S FF; Set software PTE bits #,R3 EVAX_STQ R3,(R0) ;S FF; Store the new PTE MOVL SPDT$L_SPTE_BASE(R4),R1 TBI_SINGLE R1 ;S FF; Do TB invalidation RSB .DISABLE LSB ; MAP_BUFF .PAGE .SBTTL UNMAP_BUFF - Unmap User Data Buffer Routine ; ; This routine unmaps the user data buffer for performing unaligned data ; transfers. ; ; INPUT: ; R4 = SPDT address ; R5 = SCDRP address ; ; OUTPUT: ; R0 = Status ; .ENABLE LSB UNMAP_BUFF: .JSB_ENTRY INPUT=, OUTPUT=, - PRESERVE= CLRL @SPDT$L_SPTE_SVAPTE(R4) ; Clear the SPTE TBI_SINGLE R1 ; Invalidate translation buffer RSB .DISABLE LSB ; UNMAP_BUFF .PAGE .SBTTL PKS$UNMAP_BUFFER - Unmap Buffer Routine ; ; This routine replaces the SC$UNMAP_BUFFER routine in the SCSICOMMON module. ; It checks the Queue Buffer private data area to see if a BSM or pad block ; was used for this I/O request. If so, the resources are deallocated. ; ; INPUT: ; spdt(AP) = SPDT address ; scdrp(AP)= SCDRP address ; ; OUTPUT: ; R0 = Status ; .ENABLE LSB ; ; Define the offsets to the parameters pointed to by AP. ; $ARG_DEF <- spdt, - ; SPDT address scdrp > ; SCDRP address PKS$UNMAP_BUFFER:: .CALL_ENTRY OUTPUT=,SCRATCH=,PRESERVE= MOVL spdt(AP),R4 ; Get SPDT address MOVL scdrp(AP),R5 ; Get SCDRP address MOVL SCDRP$PS_QBUF(R5),R6 ; Get Queue Buffer address MOVL QBUF$PS_BSM_PTR(R6),R0 ; Is there a BSM? BEQL 10$ ; Branch if not MOVZWL BSM$IW_SIZE(R0),R1 ; Get BSM size JSB EXE$DEANONPGDSIZ ; Deallocate BSM 10$: MOVL QBUF$PS_PADBLK(R6),R0 ; Is there a pad buffer? BEQL 20$ ; Branch if not MOVZWL PAD$IW_SIZE(R0),R1 ; Get pad block size JSB EXE$DEANONPGDSIZ ; Deallocate pad buffer 20$: MOVZWL #SS$_NORMAL,R0 ; Success RET ; Return .DISABLE LSB ; PKS$UNMAP_BUFFER .PAGE .SBTTL PKS$MAP_BUFFER_MR - Map Buffer Routine for Map Registers ; ; This routine serves the same purpose as the SC$MAP_BUFFER routine in ; SCSICOMMON module. The routine will check the user data buffer and ; allocate the necessary resources to represent the data buffer to the ; channel. Resources include: ; ; 1.) Allocating and setting up a pad block if required. ; 2.) Setting up one or two Buffer Segment Descriptors (BSDs) in the Queue ; Buffer if required. ; ; A pad block will be allocated if required. ; ; Note: since current platforms that provide map registers are PCI-based, ; and since the KZPSA/PCI does byte-aligned I/O properly, the unalignment ; handling used in PKS$MAP_BUFFER isn't needed here. ; ; Note: since map registers support scatter-gather over non-contiguous ; physical pages, at most two BSD's will ever be needed, one for the user ; buffer and one for a possible pad block. The Queue Buffer (QBUF) structure ; contains two BSD's. This routine therefore never needs to allocate a BSM. ; ; If there is no data to be transferred, this routine just returns SS$_NORMAL. ; ; INPUT: ; SPDT(AP) = SPDT address ; SCDRP(AP) = SCDRP address ; SCDRP$L_BCNT - Transfer byte count ; SCDRP$L_BOFF - Byte offset in page ; SCDRP$L_SVAPTE - System Virtual Address of the first ; P0 Page Table Entry ; SCDRP$L_SCSI_FLAGS - Flags used for mapping and unmapping ; functions ; SCDRP$V_FLAG_S0BUF - If set then data ; buffer is in S0 space ; SCDRP$L_SVA_USER - S0 buffer if SCDRP$V_FLAG_S0BUF is set ; SCDRP$PS_QBUF - Address of the Q_Buffer allocated ; SCDRP$PS_KPB - Address of the associated KPB ; ; OUTPUT: ; R0 = Status ; .ENABLE LSB ; ; Define the offsets to the parameters pointed to by AP. ; $ARG_DEF <- SPDT, - ; SPDT address SCDRP > ; SCDRP address PKS$MAP_BUFFER_MR:: .CALL_ENTRY OUTPUT=,SCRATCH=,- PRESERVE= MOVL SPDT(AP),R4 ; Get SPDT address MOVL SCDRP(AP),R5 ; Get SCDRP address MOVL SCDRP$PS_QBUF(R5),R7 ; Get Queue Buffer address TSTL SCDRP$L_BCNT(R5) ; Is there a data buffer? BEQL 110$ ; No, then just return success ; ; Find the first SVAPTE for the user buffer. ; ASSUME SCDRP$V_FLAG_S0BUF EQ 0 ; S0 bit must be bit zero of ; SCSI_FLAGS BLBC SCDRP$L_SCSI_FLAGS(R5),5$ ; Is this an S0 data buffer? MOVL SCDRP$L_SVA_USER(R5),R0 ; Yes, get S0 virtual address EVAX_SLL R0,#<64-VA$V_SYSTEM>,R0 ; Clear space select bit EVAX_SRL R0,MMG$GQ_64SYS_SHIFT,R0 ; Get virtual page number MOVAQ @MMG$GL_SPTBASE[R0],R6 ; Get SVAPTE that maps VA BRB 7$ ; Join common code 5$: MOVL SCDRP$L_SVAPTE(R5),R6 ; Get SVAPTE of P0 data buffer ; ; Allocate a pad block for this transfer if required. ; 7$: TSTL SCDRP$L_PAD_BCNT(R5) ; Is the pad count 0? BEQL 30$ ; Yes, don't need pad block INCW QBUF$IW_NUMSEG(R7) ; Pad block counts as a segment 10$: ADDL3 #PAD$C_HEADER,- ; # of bytes to allocate SCDRP$L_PAD_BCNT(R5),R1 SUBL #4,SP ; Allocate a longword on stack PUSHL SP ; Arg 4 = addr to receive size PUSHAB QBUF$PS_PADBLK(R7) ; Arg 3 = addr to receive addr ; of allocated pad block PUSHL #ALN_LONG ; Arg 2 = longword alignment PUSHL R1 ; Arg 1 = requested block size CALLS #4,EXE$ALONONPAGED_ALN ; Allocate a pad block POPL R1 ; Get the allocated size .BRANCH_LIKELY BLBS R0,20$ ; Did we get a block? MOVL SCDRP$PS_KPB(R5),R0 ; No, get KPB address KP_STALL_FORK_WAIT KPB=R0,- ; Fork and wait FKB=KPB$PS_UCB(R0) ; using class driver UCB BRB 10$ ; Try allocation again 20$: MOVL QBUF$PS_PADBLK(R7),R2 ; Get the address of pad block PUSHR #^M ; Save registers MOVC5 #0,(SP),#0,R1,(R2) ; Zero initialize the block POPR #^M ; Restore registers MOVW R1,PAD$IW_SIZE(R2) ; Set the allocated block size MOVB #DYN$C_MISC,PAD$IB_TYPE(R2) ; Set type MOVB #DYN$C_PADBLK,PAD$IB_SUBTYPE(R2); Set subtype ; ; Optimization: if all of the memory (pad block and user buffer) that ; this transfer touches is within the bus's direct-DMA window, we have no ; need to allocate and fill map registers. ; $SVA_TO_PA SVA=R2 ; Pad block address EVAX_ADDQ R1,R0,R0 ; Add size of pad block EVAX_CMPULT R0,SPDT$IQ_DMA_SIZE(R4),R9 ; Compare to DMA window size BLBC R9,70$ ; Branch if GT window size ; ; Calculate the number of pages involved in the request. ; 30$: ADDL3 SCDRP$L_BOFF(R5),- SCDRP$L_BCNT(R5),R8 ADDL2 G^MMG$GL_BWP_MASK,R8 ; To round up. MNEGL G^MMG$GL_VPN_TO_VA,R1 ASHL R1,R8,R8 ; Convert to pages. ; ; Now cycle through the pages, checking each PA against the DMA window size. ; EVAX_LDQ R2,SPDT$IQ_DMA_SIZE(R4) ; Window size MOVL R6,R3 ; Working copy of SVAPTE. 60$: $SVAPTE_TO_PA - ; Convert SVAPTE to PA. SVAPTE = R3 EVAX_CMPULT R0,R2,R9 ; Compare to DMA window size BLBC R9,70$ ; Branch if GT window size ADDL #PTE$C_BYTES_PER_PTE,R3 ; Point to next SVAPTE. SOBGTR R8,60$ ; ; Here, we've verified that all pages we care about are in the window. Set ; up and call the non-register buffer-mapping routine, and then return. ; PUSHL R5 ; SCDRP addr PUSHL R4 ; SPDT addr CALLS #2,PKS$MAP_BUFFER BRW 120$ ; Return ; ; Map the pad block and build a BSD with the resulting address. ; 70$: MOVL QBUF$PS_PADBLK(R7),R2 ; Point to allocated mem. BEQL 90$ ADDL3 #PAD$C_HEADER,R2,R3 ; Pass pad header MAP_S0 - SVA = R3,- NBYTES = SCDRP$L_PAD_BCNT(R5),- CRCTX = PAD$PS_CRCTX(R2),- KPB$ = SCDRP$PS_KPB(R5),- DMA_ADDR = PAD$PQ_PA(R2) BUILD_BSD - ; Build BSD for data buffer. BSD_ADDR = QBUF$PS_DATA_BSD2(R7),- BUFFER_PA = PAD$PQ_PA(R2),- BYTE_COUNT = SCDRP$L_PAD_BCNT(R5) ; ; Initialize the CRCTX for this I/O's user buffer. ; 90$: MOVAL SCDRP$R_CRCTX_BASE(R5),R2 ; Get CRCTX address PUSHL SPDT$IS_FLCK(R4) PUSHL R2 PUSHL SPDT$PS_CRAB(R4) CALLS #3,G^IOC$INIT_CRCTX ; ; Calculate the number of map registers required to map the I/O buffer. ; ; MOVL SCDRP$L_BOFF(R5),R0 ; Byte-within-page, converted ; BICL SPDT$IS_CRCTX_BWP_MASK(R4),R0 ; to byte within map unit ; ADDL2 SCDRP$L_BCNT(R5),R0 ; Add in byte count ADDL3 SCDRP$L_BOFF(R5),- SCDRP$L_BCNT(R5),R0 ADDL2 SPDT$IS_CRCTX_BWP_MASK(R4),R0 ; Plus map unit size, so MNEGL SPDT$IS_CRCTX_SHIFT(R4),R1 ; the shift will round ASHL R1,R0,R0 ; upward. ADDL #2,R0 ; Add two for guard entries. MOVL R0,CRCTX$L_ITEM_CNT(R2) ; ; Attempt to allocate the necessary map registers. Stall if immediate ; allocation fails; MAP_CALLBACK will restart us. ; MOVAB MAP_CALLBACK,- ; Set up to restart when CRCTX$L_CALLBACK(R2) ; allocation succeeds after MOVL SCDRP$PS_KPB(R5),- ; initial failure. CRCTX$L_AUX_CONTEXT(R2) PUSHL R2 ; CRCTX address PUSHL SPDT$PS_CRAB(R4) ; CRAB address CALLS #2,IOC$ALLOC_CNT_RES BLBS R0,100$ ; Did we get 'em? MOVL SCDRP$PS_KPB(R5),R8 ; Nope; stall. MOVAB B^IOC$RETURN,- ; Set stall routine KPB$PS_SCH_STALL_RTN(R8) PUSHL R8 CALLS #1,EXE$KP_STALL_GENERAL ; ; At this point, one way or the other, we have the necessary map registers. ; Map the data buffer, and build a BSD with the resulting DMA-able address. ; 100$: SUBL #4,SP ; Make room for DMA addr PUSHL SP ; PUSHL SCDRP$L_BOFF(R5) ; Byte offset PUSHL R6 ; Buffer SVAPTE PUSHL R2 ; CRCTX addr PUSHL SPDT$L_ADP(R4) ; ADP addr CALLS #5,IOC$LOAD_MAP POPL R2 ; DMA address. BLBC R0,120$ ; Return error on map failure BUILD_BSD - ; Build BSD for data buffer. BSD_ADDR = QBUF$PS_DATA_BSD1(R7),- BUFFER_PA = R2,- BYTE_COUNT = SCDRP$L_BCNT(R5) INCW QBUF$IW_NUMSEG(R7) ; Maps as one segment. ; ; Setup data transfer size in CCB. ; ADDL3 SCDRP$L_BCNT(R5),- ; Calculate and store SCDRP$L_PAD_BCNT(R5),- ; data transfer length QBUF$IS_DATA_TRAN_SIZE(R7) ; in Queue Buffer 110$: MOVZWL #SS$_NORMAL,R0 ; Set success status 120$: RET ; Return .DISABLE LSB ; PKS$MAP_BUFFER_MR .SBTTL MAP_CALLBACK -- callback for map-register allocation success ; ; When PKS$MAP_BUFFER_MR attempts to allocate a set of map registers, and ; the immediate attempt is unsuccessful, the utility routines for register ; allocation arrange for this routine to be called when the allocation ; eventually succeeds. All we have to do is restart the thread (KP) that is ; waiting on the allocation. ; ; INPUT: ; ; R1 -- CRCTX address for allocation request ; ; OUTPUT: ; ; None ; .ENABLE LSB MAP_CALLBACK: .JSB_ENTRY INPUT= PUSHL #SS$_NORMAL PUSHL CRCTX$L_AUX_CONTEXT(R1) CALLS #2,EXE$KP_RESTART RSB ; Return .DISABLE LSB ; MAP_CALLBACK .PAGE .SBTTL PKS$UNMAP_BUFFER_MR - Unmap Buffer Routine with Map Registers ; ; This routine replaces the SC$UNMAP_BUFFER routine in the SCSICOMMON module, ; for devices (currently, KZPSA) on buses (currently, PCI) that provide DMA ; map registers. ; ; It deallocates the map registers that were used to map the user buffer for ; this request. If a pad block was used for this request, the map registers ; for it, as well as the buffer itself, are deallocated. ; ; INPUT: ; spdt(AP) = SPDT address ; scdrp(AP)= SCDRP address ; ; OUTPUT: ; R0 = Status ; .ENABLE LSB ; ; Define the offsets to the parameters pointed to by AP. ; $ARG_DEF <- SPDT, - ; SPDT address SCDRP > ; SCDRP address PKS$UNMAP_BUFFER_MR:: .CALL_ENTRY OUTPUT=,SCRATCH=,PRESERVE= MOVL SPDT(AP),R4 ; Get SPDT address MOVL SCDRP(AP),R5 ; Get SCDRP address MOVL SCDRP$PS_QBUF(R5),R6 ; Get Queue Buffer address ; ; If there is a BSM, release any map registers that may be allocated to it. ; Then release the BSM itself. ; MOVL QBUF$PS_BSM_PTR(R6),R6 ; Get BSM address BEQL 5$ ; Is there a BSM? MOVL BSM$PS_CRCTX(R6),R7 ; Yes, point to BSM CRCTX BEQL 3$ ; Is there a CRCTX? PUSHL R7 ; Yes, deallocate resources PUSHL SPDT$PS_CRAB(R4) CALLS #2,IOC$DEALLOC_CNT_RES PUSHL R7 ; Deallocate the CRCTX CALLS #1,IOC$DEALLOC_CRCTX 3$: MOVL R6,R0 ; Get BSM address MOVZWL BSM$IW_SIZE(R6),R1 ; Get BSM size JSB EXE$DEANONPGDSIZ ; Deallocate BSM ; ; If we've used map registers to map the user buffer, deallocate said ; registers. ; 5$: MOVAB SCDRP$R_CRCTX_BASE(R5),R6 BITL #CRCTX$M_ITEM_VALID,- ; Do we have a valid CRCTX? CRCTX$L_FLAGS(R6) BEQL 10$ PUSHL R6 ; Yep, deallocate resources PUSHL SPDT$PS_CRAB(R4) CALLS #2,IOC$DEALLOC_CNT_RES BICL2 #CRCTX$M_ITEM_VALID, - ; Invalidate the counted res CRCTX$L_FLAGS(R6) ; extent described by this CRCTX ; ; Now release the registers used to map the pad block, if there was one, and ; deallocate the CRCTX, and then the buffer itself. ; 10$: MOVL SCDRP$PS_QBUF(R5),R6 ; Get Queue Buffer address MOVL QBUF$PS_PADBLK(R6),R6 ; Is there a pad buffer? BEQL 30$ ; Branch if not MOVL PAD$PS_CRCTX(R6),R7 ; Point to Pad Buffer CRCTX BEQL 25$ PUSHL R7 ; Yep, deallocate resources PUSHL SPDT$PS_CRAB(R4) CALLS #2,IOC$DEALLOC_CNT_RES PUSHL R7 ; Now deallocate the CRCTX. CALLS #1,IOC$DEALLOC_CRCTX 25$: MOVZWL PAD$IW_SIZE(R6),R1 ; Get pad block size MOVL R6,R0 JSB EXE$DEANONPGDSIZ ; Deallocate pad buffer 30$: MOVZWL #SS$_NORMAL,R0 ; Success RET ; Return .DISABLE LSB ; PKS$UNMAP_BUFFER_MR .PAGE .SBTTL PK$NEGOTIATE_SYNCH - Start synchronous Negotiation ;+ ; DESCRIPTION: ; ; The KZ*SA adapters will perform synchronous SDTR negotiation at ; the start of each command if the current data transfer mode is ; unknown or in doubt; it will also respond to a CAM (QBUF) bit ; which tells it to negotiate SDTR unconditionally. ; ; It is possible for the KZ*SA to get stuck negotiating SDTR with ; a SCSI device with which negotiation consistently fails; in that ; event a utility must be used to configure the adapter to suppress ; the negotiation to that specific ID. There is no CAM bit available ; which the driver can use to suppress this negotiation. ; ; For that reason, this driver's use of STDT$M_DFLG_SUPPRESS_SDTR is ; a NOP - but we'll put it in anyway because it's harmless, consistent ; with the rest of the drivers and will be useful if the FW should ; ever change it's default behavior. ; ; For this port this routine will set up for synchronous I/O if requested. ; ; INPUT: ; spdt(AP) = SPDT address ; stdt(AP) = STDT address ; Device IPL ; Fork lock may be held ; SPDT lock held ; Auto configuration thread ; FORK thread ; Queue Manager thread ; ; OUTPUT: ; R0 = Status ; SS$_NORMAL = Success ; SS$_ACCVIO = Error returned by the optional debugging code. ; R1 = Destroyed ; R2 - R12 = Preserved ; Device IPL ; Fork lock still held if held upon entry. ; SPDT lock held ; Auto configuration thread ; FORK thread ; Queue Manager thread ; .ENABLE LSB ; ; Define the offsets to the parameters pointed to by AP. ; $ARG_DEF <- spdt, - ; SPDT address stdt > ; STDT address PK$NEGOTIATE_SYNCH:: .CALL_ENTRY OUTPUT=,- SCRATCH=<>,- PRESERVE= MOVL stdt(AP),R10 ; Get STDT address BBS #STDT$V_DFLG_SUPPRESS_SDTR,- ; Branch if SDTR negotiation STDT$IS_DIPL_FLAGS(R10),10$ ; has been suppressed BISL #STDT$M_DFLG_RENEGOTIATE_SYNC,- ; Setup to renegotiate synch STDT$IS_DIPL_FLAGS(R10) ; for this connection 10$: MOVZWL #SS$_NORMAL,R0 ; Success RET ; Return .DISABLE LSB ; PK$NEGOTIATE_SYNCH .PAGE .SBTTL PK$SEND_COMMAND - SCSI port specific I/O Initiate routine ; ; This routine is called by SC$SEND_CMD in SCSICOMMON to send the ; I/O request to the SIMport adapter. ; ; This routine will complete filling in the appropriate fields in the ; Queue Buffer, allocate a Carrier to point to the Queu eBuffer and then ; insert the Carrier on the DACQ tail. Since the SIMport channel handles ; the SCSI bus phase transitions instead of the port driver, once the ; command is queued to the DACQ, this I/O request is suspended/stalled until ; the corresponding response is returned from the adapter or until the ; adapter exits the ENABLED state due to some error. In the case of ; adapter state transition, a cleanup process will be initiated to resume ; any stalled I/O request, returning error status to the class driver. ; ; When the response is processed, the corresponding Carrier is returned ; to the non-paged pool by the FORK routine. ; ; INPUT: ; spdt(AP) = Port SPDT address. ; scdrp(AP) = Class SCDRP address. ; SCDRP$L_CMD_PTR - Command out buffer ; The first longword contains the count of ; the number of cmd 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 to send command ; SCDRP$PS_KPB - Address of associated Kernel Process Block ; scdt(AP) = SCDT address. ; stdt(AP) = STDT address. ; ucb(AP) = Port UCB address. ; SCDRP ownership ; FORK IPL ; FORK lock held ; Queue manager thread ; ; OUTPUT: ; R0 = Port status ; SS$_NORMAL - Success ; SS$_ABORT - Command was aborted ; SS$_CTRLERR - Port failed and there should not be retry ; SS$_DEVOFFLINE - Port is permanently offline ; SS$_MEDOFL - Port is being reinitialized, retry later ; No SCDRP ownership - relinquished to PK$CMD_WAIT_COMPLETION. ; FORK IPL ; FORK lock held ; Queue manager thread ; .ENABLE LSB ; ; Define the offsets to the parameters pointed to by AP. ; $ARG_DEF <- spdt, - ; SPDT address scdrp - ; SCDRP address scdt, - ; SCDT address stdt > ; STDT address PK$SEND_COMMAND:: .CALL_ENTRY OUTPUT=,PRESERVE= MOVL spdt(AP),R4 ; Get the SPDT address. MOVL scdrp(AP),R5 ; Get the SCDRP address. MOVL scdt(AP),R3 ; Get the SCDT address. MOVL stdt(AP),R10 ; Get the STDT address. ; ; Check port online status to be certain that the port is in ENABLED state ; and is capable of handling commands before proceeding. ; BBS #SPDT$V_STS_ONLINE,- ; Is port online? SPDT$L_STS(R4),10$ MOVZWL #SS$_MEDOFL,R0 ; No, medium offline BRW 100$ ; Return ; ; The following instruction is not necessary for the SIMport adapter, but ; is needed to be compatible with existing SCSICOMMON SC$SEND_COMMAND routine. ; 10$: MOVAB SCDRP$B_SCSIMSGI_BUF(R5),- ; Setup pointer to message-in SCDRP$L_SCSIMSGI_PTR(R5) ; buffer ; ; Set up the Queue Buffer (CCB) for the Execute SCSI I/O command. ; Start with the common CCB header. ; MOVL SCDRP$PS_QBUF(R5),R7 ; Get address of Queue Buffer MOVL R7,SPDT$IS_QBUF_ADDR(R4) ; Save Queue Buffer address MOVQ QBUF$PQ_PA_QBUF(R7),- ; Setup PA of CCB QBUF$PQ_CCB_ADDR(R7) MOVW #QBUF$C_EXEC_SCSI_IO_SIZE,- ; CCB length QBUF$IW_CCB_LENGTH(R7) MOVB #QBUF$C_EXEC_SCSI_IO,- ; Setup function code QBUF$IB_FUNC_CODE(R7) CLRB QBUF$IB_CAM_STS(R7) ; Clear CAM status MOVB SPDT$IB_PI_PATH_ID(R4),- ; Setup path id QBUF$IB_PATH_ID(R7) MOVB STDT$IS_SCSI_ID_NUM(R10),- ; Setup target id QBUF$IB_TARGET_ID(R7) MOVB SCDT$L_SCSI_LUN(R3),- ; Setup LUN QBUF$IB_LUN(R7) ; ; Setup the CAM flags longword. (Bytes 3 and 4 are kept cleared.) ; Start with the data transfer direction. ; TSTL SCDRP$L_BCNT(R5) ; Check transfer byte count BNEQ 20$ ; Is there data to transfer? BISB #QBUF$M_NO_DATA_TX,- ; No, direction is no data QBUF$IB_CAM_FLAGS_1(R7) BRW 50$ 20$: BBS #IRP$V_FUNC,SCDRP$IS_STS(R5),40$ ; Is this a write function? BISB #QBUF$M_WRITE,- ; Yes, direction is out QBUF$IB_CAM_FLAGS_1(R7) BRW 50$ 40$: BISB #QBUF$M_READ,- ; Read, direction is in QBUF$IB_CAM_FLAGS_1(R7) ; ; Now setup the queuing characteristics. ; .BRANCH_UNLIKELY 50$: BBC #SCDRP$V_FLAG_QUEUED_IO,- ; Queued request? SCDRP$L_SCSI_FLAGS(R5),520$ MOVL SCDRP$IS_QUEUE_CHAR(R5),R1 ; Yes, Get queue characteristics BISB TC_ACTION_ENABLE_TABLE(R1),- ; Set TCQ action enable QBUF$IB_CAM_FLAGS_1(R7) MOVB TAG_QUEUE_ACTION_TABLE(R1),- ; Setup tag queue action QBUF$IB_TAG_QUEUE_ACTION(R7) BRW 521$ ; Join common code 520$: BICB #QBUF$M_TQ_ACTION_ENB,- ; Not a queued request QBUF$IB_CAM_FLAGS_1(R7) ; Clear TCQ action enable CLRB QBUF$IB_TAG_QUEUE_ACTION(R7) ; Clear tag queue action ; ; Setup sync/async mode. ; 521$: BBCC #STDT$V_DFLG_RENEGOTIATE_SYNC,- ; Branch if user has not requested STDT$IS_DIPL_FLAGS(R10),51$ ; SDTR negotation (of any mode) TSTL STDT$IS_REQUESTED_REQACK_OFFSET(R10) ; Do they want synchronous mode? BNEQ 522$ ; Branch if so (REQ/ACK <> 0) BISB #QBUF$M_SYNC_TRAN_DIS,- ; Negotiate asynchronous mode QBUF$IB_CAM_FLAGS_2(R7) ; data transfers if necessary BRW 51$ ; Join common code 522$: BISB #QBUF$M_SYNC_TRAN_INIT,- ; Negotiate synchronous mode QBUF$IB_CAM_FLAGS_2(R7) ; data transfers unconditionally ; ; Do not allow the SIM queue to be frozen. ; 51$: BISB #QBUF$M_SIM_Q_FREEZE_DIS,- ; Set SIM queue freeze disable QBUF$IB_CAM_FLAGS_2(R7) ; ; The common CCB header is set up. Set up the Execute SCSI I/O extension. ; 515$: MOVL SCDRP$L_CMD_PTR(R5),R9 ; Get pointer to CDB MOVL (R9)+,R0 ; Get command length BICL #^C^XFF,R0 ; Clear upper bytes MOVB R0,QBUF$IB_CDB_SIZE(R7) ; Save command length PUSHR #^M ; Save registers MOVC3 R0,(R9),QBUF$IB_CDB(R7) ; Copy CDB into CCB POPR #^M ; Restore registers ; ; Setup disconnect enable. ; CMPB QBUF$IB_CDB(R7),#SCSI$K_INQUIRY ; Is this an inquiry command? BEQL 516$ ; Enable disconnects if so BBS #SCDT$V_CFLG_ENA_DISCON,- ; Are disconnects allowed? SCDT$IS_CON_FLAGS(R3),516$ BISB #QBUF$M_DIS_DISCONNECT,- ; No, disable disconnect QBUF$IB_CAM_FLAGS_2(R7) 516$: MOVL SCDRP$L_DMA_TIMEOUT(R5),R0 ; Get DMA/phase change timeout ADDL3 SCDRP$L_DISCON_TIMEOUT(R5),R0,- ; Setup command timeout value QBUF$IS_TIMEOUT_VALUE(R7) ; ; If we've set up mapping registers for this Queue Buffer, the mapping covers ; the autosense buffer. Otherwise, we need to supply the latter's PA to the ; adapter. ; TSTL QBUF$PS_CRCTX_PTR(R7) ; Do we have map registers? BEQL 60$ ADDL3 #QBUF$C_AUTOSENSE_OFFSET,- ; Yep, just add offset to QBUF$PQ_PA_QBUF(R7),R0 ; DMA_ADDR. BRB 70$ 60$: MOVAB QBUF$IB_AUTOSENSE_BUF(R7),R6 ; Get autosense buffer pointer $SVA_TO_PA SVA=R6 ; Convert to PA ADDL2 SPDT$L_DMA_BASE(R4),R0 ; Add in DMA window base 70$: EVAX_ZAP R0,#^XF0,R0 ; Clear the upper longword. EVAX_STQ R0,QBUF$PS_SENSE_BUFF_PTR(R7) ; Save autosense PA in CCB MOVB #QBUF$C_AUTOSENSE_BUF_SIZE,- ; Autosense buffer length QBUF$IB_SENSE_BUFF_SIZE(R7) MOVAB QBUF$IB_AUTOSENSE_BUF(R7),R6 ; Get VA of autosense buffer BUILD_BSD - ; Build a BSD for autosense BSD_ADDR = QBUF$PS_SEN_BSD(R7),- BUFFER_PA = R0,- BYTE_COUNT = #QBUF$C_AUTOSENSE_BUF_SIZE ; ; Allocate a Carrier and setup Carrier fields. ; MOVL SCDRP$PS_KPB(R5),R8 ; Get KPB address BSBW GET_QUEUE_CARRIER ; Get a Carrier. R2 = Car ptr CLRB CAR$IB_STATUS(R2) ; Clear Status CLRW CAR$IW_FLAGS(R2) ; Flags CLRL CAR$IS_CONNECT_ID(R2) ; Connect id ; ; Send the command to the SIMport adapter. ; MOVL SPDT$L_DLCK(R4),KPB$PS_DLCK(R8) ; Device lock pointer to KPB BSBW INSERT_DACQ ; Insert command on DACQ DEVICELOCK - ; Get device lock and raise IPL LOCKADDR=SPDT$L_DLCK(R4),- ; to prevent interrupts LOCKIPL=SPDT$B_DIPL(R4),- SAVIPL=KPB$IS_NEWIPL(R8),- PRESERVE=NO EVAX_LDQ R9,CAR$PQ_PA(R2) ; Get PA of new Stopper Carrier EVAX_SRL R9,#TZA_DACQIR$C_PA_SHIFT,R9 ; Shift PA right 3 bits WRITE_CSR REG = DACQIR,- ; Give DACQ head PA to adapter DATA = R9,- TYPE = L DEVICEUNLOCK - ; Release device lock LOCKADDR=SPDT$L_DLCK(R4),- NEWIPL=KPB$IS_NEWIPL(R8),- PRESERVE=NO MOVZWL #SS$_NORMAL,R0 ; Return normal status 100$: RET ; Return to queue manager .DISABLE LSB ; PK$SEND_COMMAND .PAGE .SBTTL PKS_STALL - Kernel Process Stall Routine ; ; This routine performs the corresponding DEVICEUNLOCK for the lock that ; was done in the PK$INITIATE_IO routine. ; ; DEVICEUNLOCK is done here so that the port cannot interrupt us until ; this thread is stalled properly. ; ; CALLED BY: I/O interrupt dispatcher ; ; INPUT: ; 4(AP) = KPB address ; ; OUTPUT: ; None ; ; NOTE: R2 is saved here since SS$RELEASE_PORT destroys it. ; .ENABLE LSB $ARG_DEF <- kpb > ; Kernel Process Block PKS_STALL: .CALL_ENTRY MOVL kpb(AP),R1 ; Get KPB parameter MOVL KPB$PS_SCSI_PTR1(R1),R4 ; Get SPDT address from KPB MOVL KPB$PS_SCSI_SCDRP(R1),R5 ; Get SCDRP address ADDL3 G^EXE$GL_ABSTIM,#IO_TIMEOUT,- ; Set up I/O thread timeout SCDRP$L_DUETIME(R5) ; value in seconds MOVL SCDRP$L_CDT(R5),R3 ; Get SCDT address RET ; Return .PAGE .SBTTL PK$CMD_WAIT_COMPLETION - Stall for I/O completion/reselection ; ; This routine is called by SC$SEND_COMMAND to wait for the I/O ; completion. If a reselection occurs during the processing of this I/O ; request then this routine is responsible for driving the I/O to completion ; in the SCDRP thread context. ; ; This is a NOP right now until we figure out what really goes here. ; ; INPUTS: ; ; spdt(AP) = SPDT address ; scdrp(AP) = SCDRP address ; scdt(AP) = SCDT address ; stdt(AP) = STDT address ; ; OUTPUTS: ; SCDRP ownership - Ownership was transferred by PK$SEND_COMMAND ; SCDRP thread ; R0 = status returned to SC$SEND_COMMAND. ; SS$_NORMAL - SCSI request completed ; SS$_MEDOFL - Port is being reinitialized, retry later ; .ENABLE LSB ; ; Define the offsets to the parameters pointed to by AP. ; $ARG_DEF < - spdt, - ; SPDT address scdrp, - ; SCDRP address scdt, - ; SCDT address stdt > ; STDT address PK$CMD_WAIT_COMPLETION:: .CALL_ENTRY OUTPUT= MOVL spdt(AP),R4 ; Get the SPDT address. MOVL scdrp(AP),R5 ; Get the SCDRP address. MOVL SCDRP$PS_KPB(R5),R8 ; Get KPB address ; ; Wait for the channel to return a response to this command by suspending ; this I/O request to wait for the command completion interrupt. ; BBCC #SCDRP$V_DSF_NOWAIT,- ; Are we stalled? SCDRP$IS_DIPL_SCSI_FLAGS(R5),10$ DEVICEUNLOCK - ; Yes, release device lock LOCKADDR=SPDT$L_DLCK(R4),- NEWIPL=KPB$IS_NEWIPL(R8),- PRESERVE=NO BRW 20$ 10$: MOVL R4,KPB$PS_SCSI_PTR1(R8) ; Save SPDT address MOVL R5,KPB$PS_SCSI_SCDRP(R8) ; Save SCDRP address MOVAB B^PKS_STALL,- ; Set stall routine address KPB$PS_SCH_STALL_RTN(R8) CLRL KPB$PS_SCH_RESTRT_RTN(R8) ; No restart routine required PUSHL R8 ; Pass KPB address CALLS #1,G^EXE$KP_STALL_GENERAL ; Call the general KP staller ; ; This is the point at which the stalled request resumes when the response ; is returned. At this point: ; R4 = SPDT address ; R5 = SCDRP address ; 20$: MOVL KPB$PS_SCSI_SCDRP(R8),R5 ; Get SCDRP address MOVL SCDRP$PS_QBUF(R5),R7 ; Get Queue Buffer address ; ; If this is a read from device data transfer with unaligned data, we have ; to copy the unaligned bytes back to the user buffer. Any unaligned leading ; and/or trailing bytes are currently in the BSM (BSM$PS_LEADING_BUFF, and/or ; BSM$PS_TRAILING_BUFF). The number of unaligned bytes (if any) are in ; BSM$IS_LEADING_COUNT and/or BSM$IS_TRAILING_COUNT. ; ; If this is a write to device data transfer, there is nothing required for ; copying data to/from the user buffer. ; BBC #IRP$V_FUNC,- ; Is this a read function? SCDRP$IS_STS(R5),70$ MOVL QBUF$PS_BSM_PTR(R7),R2 ; Yes, get BSM address BEQL 70$ ; Is there a BSM? TSTL BSM$IS_LEADING_COUNT(R2) ; Yes, check leading alignment BEQL 55$ ; Is there leading unalignment? ASSUME SCDRP$V_FLAG_S0BUF EQ 0 ; Verify position of S0 bit BLBC SCDRP$L_SCSI_FLAGS(R5),52$ ; Yes, is this an S0 buffer? MOVL SCDRP$L_SVA_USER(R5),R1 ; Yes, get S0 virtual address BRB 54$ ; Join common code 52$: MOVL SCDRP$L_SVAPTE(R5),R7 ; Get SVAPTE of P0 data buffer 53$: JSB MAP_BUFF ; Map to user P0 data buffer ; R1 = address of page that ; contains unaligned data MOVL R1,R9 ; Save VA of first page ADDL SCDRP$L_BOFF(R5),R1 ; Point to unaligned data byte 54$: PUSHR #^M ; Save registers MOVC3 BSM$IS_LEADING_COUNT(R2),- ; Copy unaligned data back to BSM$PS_LEADING_BUFF(R2),(R1) ; user buffer POPR #^M ; Restore registers ASSUME SCDRP$V_FLAG_S0BUF EQ 0 ; Verify position of S0 bit BLBS SCDRP$L_SCSI_FLAGS(R5),55$ ; Is this an S0 buffer? MOVL R9,R1 ; No, restore R1 to page addr JSB UNMAP_BUFF ; Unmap from P0 user buffer 55$: TSTL BSM$IS_TRAILING_COUNT(R2) ; Check trailing alignment BNEQ 56$ ; Is there trailing unalignment? MOVL SCDRP$PS_QBUF(R5),R7 ; No, get Q_Buffer address back BRW 70$ ; Join common code 56$: ASSUME SCDRP$V_FLAG_S0BUF EQ 0 ; Verify position of S0 bit BLBC SCDRP$L_SCSI_FLAGS(R5),565$ ; Is this an S0 buffer? MOVL SCDRP$L_SVA_USER(R5),R1 ; Yes, get S0 virtual address ADDL2 SCDRP$L_BCNT(R5),R1 ; Point to last data byte SUBL2 BSM$IS_TRAILING_COUNT(R2),R1 ; Backup to unaligned data bytes BRW 58$ 565$: MOVL SCDRP$L_SVAPTE(R5),R7 ; Get SVAPTE of first page of ; P0 data buffer ADDL3 SCDRP$L_BCNT(R5),- ; Point to last data byte SCDRP$L_BOFF(R5),R9 $BYTES_TO_PAGES- ; Compute page count that SOURCE_BYTCNT = R9,- ; user buffer spans DEST_PAGCNT = R3,- ROUNDUP = NO MULL2 #PTE$C_BYTES_PER_PTE,R3 ; Convert pages to PTE pages ADDL2 R3,R7 ; Add PTE(s) size to starting ; SVAPTE of first page JSB MAP_BUFF ; Map to P0 user data buffer MOVL R1,R10 ; Save VA of first page MCOML G^MMG$GL_BWP_MASK, R3 ; Get complement of BWP mask BICL3 R3,R9,R3 ; get offset to last by in page SUBL2 BSM$IS_TRAILING_COUNT(R2),R3 ; Backup to unaligned data bytes ADDL2 R3,R1 ; Set start of unali 58$: PUSHR #^M ; Save registers MOVC3 BSM$IS_TRAILING_COUNT(R2),- ; Copy unaligned data back to BSM$PS_TRAILING_BUFF(R2),(R1) ; user buffer POPR #^M ; Restore registers ASSUME SCDRP$V_FLAG_S0BUF EQ 0 ; Verify position of S0 bit BLBS SCDRP$L_SCSI_FLAGS(R5),59$ ; Is this an S0 buffer? MOVL R10,R1 ; No, restore R1 to page addr JSB UNMAP_BUFF ; No, unmap from P0 user buffer 59$: MOVL SCDRP$PS_QBUF(R5),R7 ; Get Queue Buffer address back ; ; Return the SCSI status that was returned in the Queue Buffer (CCB). ; 70$: MOVB QBUF$IB_SCSI_STAT(R7),- ; Return SCSI status from @SCDRP$L_STS_PTR(R5) ; Private Data Area MOVB #SCSI$K_MSGCMD,- ; Set up command complete msg SCDRP$B_SCSIMSGI_BUF(R5) ; for SC$SEND_CMD ; ; The SIMport adapter does not provide a transferred byte count. Instead it ; provides a residual byte count of bytes that were not transferred. ; Return the actual transferred byte count. ; SUBL3 QBUF$IS_DATA_RES(R7),- ; Compute actual transfer count QBUF$IS_DATA_TRAN_SIZE(R7),- SCDRP$L_TRANS_CNT(R5) MOVZWL QBUF$IW_PORT_STS(R7),R0 ; Get return port status MOVL SCDRP$L_CDT(R5),R3 ; Get the SCDT address RET ; Return to Queue Manager .PAGE .SBTTL PKS$INTERRUPT - SIMport Adapter Interrupt Service Routine ; ; This routine handles all SIMport adapter interrupts. ; ; The host may be interrupted for the following two reasons: ; ; 1.) To notify the host that new entries are on the adapter driver ; response queue (ADRQ). ; 2.) To notify the host that the adapter status register (ASR) has changed ; and requires servicing. The required service is indicated in the adapter ; status register. The adapter miscellaneous interrupt is typically used ; to inform the host of an adapter error condition that has caused the ; adapter to transition to the uninitialized state. ; ; A fork thread is started (if one is not already running) to handle the ; interrupt. The fork PC in the UCB (UCB$L_FPC) is used as a semaphore to ; determine if a fork thread is currently running. The fork routine must ; clear the fork PC upon routine exit. ; ; The fork routine will handle response interrupts (if any) and then check ; for adapter error interrupts. In the case of an adapter error interrupt, ; all of the responses on the ADRQ will be processed before adapter ; re-initialization is performed. ; ; CALLED BY: I/O interrupt dispatcher ; ; INPUT: ; 4(AP) = IDB address of the channel ; ; OUTPUT: ; None ; .ENABLE LSB PKS$INTERRUPT:: .CALL_ENTRY MOVL 4(AP),R3 ; Get IDB address MOVL IDB$PS_ADP(R3),R0 ; Get ADP address MOVL ADP$L_CRB(R0),R0 ; Get CRB address MOVL IDB$PS_AUXSTRUC(R3),R5 ; Get UCB address MOVL UCB$L_PDT(R5),R4 ; Get SPDT address ; ; Read the ASR register to determine what caused this interrupt. ; DEVICELOCK - ; Lock device access LOCKADDR = SPDT$L_DLCK(R4),- LOCKIPL = SPDT$B_DIPL(R4),- SAVIPL = -(SP),- PRESERVE = NO READ_CSR REG = ASR,- ; Read ASR register DEST = SPDT$IS_SAVE_ASR(R4) TSTL UCB$L_FPC(R5) ; Check fork semaphore BNEQ 10$ ; Is a fork thread running? FORK ROUTINE = PKS$FORK,- ; No, start one running CONTINUE = 10$ 10$: MOVL UCB$L_PDT(R5),R4 ; Get SPDT address back WRITE_CSR REG = CLEAR_INT,- ; Re-enable interrupts DATA = #0 DEVICEUNLOCK - ; Unlock device access LOCKADDR = SPDT$L_DLCK(R4),- NEWIPL = (SP)+,- PRESERVE = NO RET ; Dismiss this interrupt .DISABLE LSB ; PKS$INTERRUPT .PAGE .SBTTL PKS$FORK - Interrupt Handling Fork Routine ; ; This fork routine handles all SIMport adapter interrupts. ; ; The fork routine will handle response interrupts (if any) and then check ; for adapter error interrupts. In the case of an adapter error interrupt, ; all of the responses on the ADRQ will be processed before adapter ; re-initialization is performed. ; ; CALLED BY: Fork dispatcher ; ; INPUT: ; R3 = IDB address ; R4 = SPDT address ; R5 = UCB address ; ; OUTPUT: ; None ; .ENABLE LSB PKS$FORK: .CALL_ENTRY ; ; Start by handling all (if any) responses on the ADRQ. ; RSP_LOOP: MOVL SPDT$PS_AB(R4),R6 ; Get Adapter Block address MOVL AB$PQ_ADRQ_HEAD(R6),R2 ; Get next response Carrier BBS #0,CAR$PQ_NEXT_PTR(R2),- ; Done if Stopper Carrier CHECK_ADAPTER_ERROR .BRANCH_LIKELY BBS #VA$V_SYSTEM,R2,10$ ; Branch if VA BUG_CHECK INCONSTATE,FATAL ; We want to capture this .Disable Flagging 10$: MOVL CAR$PQ_NEXT_PTR(R2),- ; Update ADRQ head pointer AB$PQ_ADRQ_HEAD(R6) ; with next Carrier address .Enable Flagging EVAX_MB ; Make sure Q_Buf contents MOVL CAR$PQ_QBUF_PTR(R2),R7 ; Get Queue Buffer VA .BRANCH_LIKELY BBS #VA$V_SYSTEM,R7,20$ ; Branch if VA BUG_CHECK INCONSTATE,FATAL ; We want to capture this ; ; Dispatch to handle the response. R7 = Queue Buffer, R2 = Carrier. ; 20$: DISPATCH CAR$IB_FUNC_CODE(R2),TYPE=B,PREFIX=CAR$C_,- <- ,- ,- ,- ,- ,- ,- ,- ,- ,- ,- ,- - ,- - - - > ; ; If we fall through to here, we have an invalid response function code. ; Return resources and log the error. ; MOVL SPDT$PS_AB(R4),R6 ; Get Adapter Block address MOVL AB$PQ_ADFQ_HEAD(R6),- ; Insert Carrier at ADFQ head CAR$PQ_NEXT_PTR(R2) MOVL R2,AB$PQ_ADFQ_HEAD(R6) ; Update ADFQ head pointer MOVL SPDT$L_PORT_UCB(R4),R5 ; Get UCB for error logger LOG_ERROR - ; Log illegal response TYPE = ILLEGAL_RSP,- CDT = #0 ; No associated CDT available BRW RSP_LOOP ; Check for more responses ; ; We received a SCSI I/O executed response message. ; EXEC_SCSI_IO_RSP: MOVL QBUF$PS_SCDRP(R7),R5 ; Get SCDRP address ; ; Check if there is any valid autosense data. If so, return the autosense ; buffer address. ; MOVZBL CAR$IB_STATUS(R2),R1 ; Get returned CAM status MOVL R1,SPDT$IS_CAM_STS(R4) ; Save CAM status BBCC #CAR$V_AUTOSENSE_DATA_VALID,- ; Valid autosense data? R1,35$ MOVAB QBUF$IB_AUTOSENSE_BUF(R7),- ; Yes, return autosense buffer SCDRP$PS_SENSE_BUFFER(R5) ; pointer BISL #SCDRP$M_FLAG_ASENSE_VALID,- ; Indicate valid autosense data SCDRP$L_SCSI_FLAGS(R5) ; ; Map the returned CAM status in the Carrier to a VMS status code and return ; resources for this response. ; 35$: ASHL #2,R1,R1 ; Make longword offset MOVAB CAM_STATUS_TABLE,R3 ; Point to beginning of table ADDL2 R1,R3 ; Point to VMS status code MOVL (R3),R1 ; Get VMS status code MOVW R1,QBUF$IW_PORT_STS(R7) ; Setup VMS return status BSBW RET_QUEUE_CARRIER ; Return Carrier to pool ; ; Resume the thread that was waiting for this response. ; 40$: MOVL SCDRP$PS_KPB(R5),R0 ; Get KPB address BBC #KPB$V_ACTIVE,- ; Is KPB stalled? KPB$IS_FLAGS(R0),45$ BISL #SCDRP$M_DSF_NOWAIT,- ; Indicate no stall needed SCDRP$IS_DIPL_SCSI_FLAGS(R5) ; in CMD_WAIT_FOR_COMPLETION BRW RSP_LOOP ; See if more responses 45$: BICL #SCDRP$M_DSF_NOWAIT,- ; Indicate stall needed SCDRP$IS_DIPL_SCSI_FLAGS(R5) ; in CMD_WAIT_FOR_COMPLETION PUSHL R0 ; Pass KPB address CALLS #1,G^EXE$KP_RESTART ; Resume this thread BRW RSP_LOOP ; See if more responses ; ; We received a SCSI bus reset request response message. Send a BUS RESET ; RESPONSE command to adapter allowing the adapter to perform the reset. ; We can use the same Queue Buffer and Carrier that was sent by the adapter. ; BUS_RESET_REQ_RSP: ; The Bus Reset response command does not define any fields in the CCB. ; The action authorized by the host is contained in the status field of ; the Carrier. ; MOVB #QBUF$C_BUS_RESET_CMD,- ; Queue Buffer function code QBUF$IB_FUNC_CODE(R7) MOVL CAR$IS_CONNECT_ID(R2),- ; Set up connect id QBUF$IS_CONNECT_ID(R7) BSBW INSERT_DACQ ; Insert command on DACQ MOVB #CAR$C_SUCCESS,- ; Allow adapter to reset bus CAR$IB_STATUS(R1) ; ; Send the command to the SIMport adapter. This will cause the adapter to ; return all I/O with a reset status. When the Channel State Set response ; is returned, routine CHAN_STATE_SET_RSP will continue with the reset. ; DEVICELOCK - ; Get device lock and raise IPL LOCKADDR = SPDT$L_DLCK(R4),- ; to prevent interrupts LOCKIPL = SPDT$B_DIPL(R4),- SAVIPL = -(SP),- PRESERVE = NO EVAX_LDQ R9,CAR$PQ_PA(R2) ; Get PA of new Stopper Carrier EVAX_SRL R9,#TZA_DACQIR$C_PA_SHIFT,R9 ; Shift PA right 3 bits WRITE_CSR REG = DACQIR,- ; Give DACQ head PA to adapter DATA = R9,- TYPE = L DEVICEUNLOCK - ; Release device lock LOCKADDR = SPDT$L_DLCK(R4),- NEWIPL = (SP)+,- PRESERVE = NO BRW RSP_LOOP ; Check for more responses ; ; We received an unsolicited reselection response message. Log the error. ; UNSOL_RESEL_RSP: BSBW INSERT_DAFQ ; Return resources to free queue LOG_ERROR - ; Log an error log entry TYPE = CTL_ERR,- ; Controller error CDT = #0 ; No associated SCDT BRW RSP_LOOP ; Check for more responses ; ; We received a Channel State Disabled response message. Issue a Set Channel ; State Enabled command to the adapter. R2 = Carrier, R7 = Queue Buffer. ; CHAN_DISABLED_RSP: MOVZBL CAR$IB_STATUS(R2),- ; Save channel disabled status SPDT$IS_CHAN_DIS_STS(R4) BSBW INSERT_DAFQ ; Return resources to free queue ; ; Determine the reason for the channel becoming disabled. If a SCSI bus reset ; has been detected, indicate a bus reset is in progress. This will cause ; IO requests to be returned with the appropriate status. ; CMPL SPDT$IS_CHAN_DIS_STS(R4),- ; Check channel disabled status #SPDT$C_BUS_RESET BNEQ 50$ ; SCSI bus reset been detected? BISL #SPDT$M_RESET_DETECTED,- ; Yes, set reset detected SPDT$IS_CHANSTATE(R4) ; ; Allocate some pool for a Queue Buffer (CCB). ; 50$: SUBL #8,SP ; Allocate two lw on stack MOVL SP,R2 ; Store address of lw to receive ; address of allocated block ADDL3 #4,R2,R6 ; Store address of lw to receive ; size of allocated block PUSHL R6 ; Arg 4 = addr to receive size PUSHL R2 ; Arg 3 = addr to receive addr ; of allocated block PUSHL #ALN_LONG ; Arg 2 = 4 byte alignment PUSHL #256 ; Arg 1 = size of block CALLS #4,EXE$ALONONPAGED_ALN ; Allocate a block POPL R2 ; Get addr of allocated block POPL R6 ; Get allocated size BLBS R0,55$ ; Did we get the block? KP_STALL_FORK_WAIT KPB=R8,- ; No, fork and wait FKB=KPB$PS_UCB(R8) ; using UCB fork block BRB 50$ ; Try allocation again 55$: PUSHR #^M ; Save registers MOVC5 #0,(SP),#0,R6,(R2) ; Zero initialize the block POPR #^M ; Restore registers MOVL R2,R7 ; Copy Queue Buffer pointer ; ; Set up Queue Buffer fields for the Set Channel State Enabled command. ; MOVW R6,QBUF$IW_SIZE(R7) ; Save Queue Buffer size MOVB #DYN$C_MISC,QBUF$IB_TYPE(R7) ; Set buffer type MOVB #DYN$C_QBUF,QBUF$IB_SUBTYPE(R7) ; Set buffer subtype MOVAB QBUF$PQ_CCB_ADDR(R7),R6 ; VA of adapter visible section $SVA_TO_PA SVA = R6 ; PA of adapter visible section BITL #SPDT$M_PFLG_MAPPING_REG,- ; Are we using map regs? SPDT$L_PORT_FLAGS(R4) BEQL 57$ ; Nope; just use PA. ; ; Is the Queue Buffer within the direct-DMA window? ; EVAX_ADDQ #QBUF$C_ADPLEN,R0,R3 ; Yes, just use PA EVAX_CMPULE R3,SPDT$IQ_DMA_SIZE(R4),R10 ; Compare to DMA window size BLBS R10,57$ ; Branch if LE window size ; ; O.K., we hafta map it. ; MAP_S0 - SVA = R6,- ; Queue Buffer address (VA). NBYTES = #QBUF$C_ADPLEN,- ; Size to map. CRCTX = QBUF$PS_CRCTX_PTR(R7),- ; CRCTX pointer reference. KPB$ = SPDT$PS_KPB(R4),- ; KPB to stall on alloc fail DMA_ADDR = QBUF$PQ_PA_QBUF(R7) ; Where to put DMA-able addr BRB 59$ ; ; No mapping registers needed, just use PA (+ DMA base). ; 57$: ADDL2 SPDT$L_DMA_BASE(R4),R0 ; Add in DMA window base EVAX_ZAP R0,#^XF0,R0 ; Clear the upper longword EVAX_STQ R0,QBUF$PQ_PA_QBUF(R7) ; Save physical address 59$: MOVQ QBUF$PQ_PA_QBUF(R7),- ; Setup PA of CCB QBUF$PQ_CCB_ADDR(R7) MOVW #QBUF$C_SET_CHAN_STATE_SIZE,- ; Setup CCB size QBUF$IW_CCB_LENGTH(R7) MOVB #QBUF$C_SET_CHAN_STATE,- ; Setup function code QBUF$IB_FUNC_CODE(R7) MOVB #QBUF$C_SET_CHAN_STATE_ENB,- ; Request enabled state QBUF$IB_CHN_STATE(R7) CLRB QBUF$IB_CAM_STS(R7) ; Clear CAM status MOVB SPDT$IB_PI_PATH_ID(R4),- ; Setup path id QBUF$IB_PATH_ID(R7) BISB #QBUF$M_NO_DATA_TX,- ; Direction is no data QBUF$IB_CAM_FLAGS_1(R7) BISB #QBUF$M_SIM_Q_FREEZE_DIS,- ; Set SIM queue freeze disable QBUF$IB_CAM_FLAGS_2(R7) ; ; Get a Queue Carrier and setup Carrier fields. ; BSBW GET_QUEUE_CARRIER ; Get a Queue Carrier CLRB CAR$IB_STATUS(R2) ; Clear Status CLRW CAR$IW_FLAGS(R2) ; Flags CLRL CAR$IS_CONNECT_ID(R2) ; Connect id ; ; Send the command to the SIMport adapter. This will cause the adapter to ; return all I/O with a reset status. When the Channel State Set response ; is returned, routine CHAN_STATE_SET_RSP will continue with the reset. ; BSBW INSERT_DACQ ; Insert command on DACQ DEVICELOCK - ; Get device lock and raise IPL LOCKADDR = SPDT$L_DLCK(R4),- ; to prevent interrupts LOCKIPL = SPDT$B_DIPL(R4),- SAVIPL = -(SP),- PRESERVE = NO EVAX_LDQ R9,CAR$PQ_PA(R2) ; Get PA of new Stopper Carrier EVAX_SRL R9,#TZA_DACQIR$C_PA_SHIFT,R9 ; Shift PA right 3 bits WRITE_CSR REG = DACQIR,- ; Give DACQ head PA to adapter DATA = R9,- TYPE = L DEVICEUNLOCK - ; Release device lock LOCKADDR = SPDT$L_DLCK(R4),- NEWIPL = (SP)+,- PRESERVE = NO BRW RSP_LOOP ; Check for more responses ; ; We received a Channel State Set Response message. Deallocate command ; resources. If the channel is now disabled, set the port offline. If the ; channel is enabled, reset the SCSI bus if necessary and set the port online. ; CHAN_STATE_SET_RSP: BSBW RET_QUEUE_CARRIER ; Return Carrier CMPB QBUF$IB_CHN_STATE(R7),- ; Check channel state #QBUF$C_SET_CHAN_STATE_ENB BEQL 60$ ; Is the channel enabled? MOVL QBUF$PS_CRCTX_PTR(R7),R3 ; Did we alloc a CRCTX? BEQL 594$ PUSHL R3 ; Yep, deallocate the regs. PUSHL SPDT$PS_CRAB(R4) CALLS #2,G^IOC$DEALLOC_CNT_RES PUSHL R3 CALLS #1,G^IOC$DEALLOC_CRCTX ; And deallocate the CRCTX 594$: MOVL R7,R0 ; Get buffer address MOVZWL QBUF$IW_SIZE(R0),R1 ; Get buffer size JSB EXE$DEANONPGDSIZ ; Deallocate command Buffer MOVL SPDT$PS_KPB(R4),R8 ; Get init process KPB MOVL KPB$PS_UCB(R8),R5 ; Get port's UCB address BICL #UCB$M_ONLINE,UCB$L_STS(R5) ; Clear UCB online BICL #SPDT$M_STS_ONLINE,- ; Clear SPDT online SPDT$L_STS(R4) BSBW PORT_SHUTDOWN ; Shut down this port LOG_ERROR - ; Log an error log entry TYPE = CTL_ERR,- ; Controller error CDT = #0 ; No associated SCDT BRW RSP_LOOP ; Check for more responses ; ; The channel is now in the enabled state. Free up the resources used ; by the Set Channel State request. Check why the channel was ; previously disabled. If it was not on account of a SCSI bus reset, then ; reset the SCSI bus in order to resynchronize device context with the adapter. ; 60$: MOVL QBUF$PS_CRCTX_PTR(R7),R3 ; Did we alloc a CRCTX? BEQL 64$ PUSHL R3 ; Yep, deallocate the regs. PUSHL SPDT$PS_CRAB(R4) CALLS #2,G^IOC$DEALLOC_CNT_RES PUSHL R3 CALLS #1,G^IOC$DEALLOC_CRCTX ; And deallocate the CRCTX. 64$: MOVL R7,R0 ; Get buffer address MOVZWL QBUF$IW_SIZE(R0),R1 ; Get buffer size JSB EXE$DEANONPGDSIZ ; Deallocate command Buffer CMPL SPDT$IS_CHAN_DIS_STS(R4),- ; Check channel disabled status #SPDT$C_BUS_RESET BEQL 70$ ; Do we need to do a bus reset? MOVL SPDT$PS_KPB(R4),R8 ; Yes, get init process KPB MOVL KPB$PS_UCB(R8),R5 ; Get port's UCB address BICL #UCB$M_ONLINE,UCB$L_STS(R5) ; Clear port UCB ONLINE MOVL R4,KPB$PS_SCSI_PTR1(R8) ; Store SPDT into KPB KP_START KPB = R8, - ; Call Kernel Process routine REGISTERS = #PKS_KP_REGMSK, - ; to reset the SCSI bus ROUTINE = RESET_SCSI_BUS 70$: BICL #SPDT$M_RESET_DETECTED,- ; Indicate reset complete SPDT$IS_CHANSTATE(R4) ; ; If target mode has been enabled, then we have to send a TARGET_EVENT_ ; ACKNOWLEDGED command to the adapter to allow the adapter to resume ; responding to selections. ; BBC #SPDT$V_TARGET_MODE,- ; Is target mode enabled? SPDT$IS_PORTSTS(R4),80$ MOVL SPDT$PS_AB(R4),R6 ; Yes, get Adapter Block address MOVL AB$PQ_ADFQ_HEAD(R6),R2 ; Get Carrier at head of queue MOVL CAR$PQ_NEXT_PTR(R2),- ; Update ADFQ head pointer AB$PQ_ADFQ_HEAD(R6) ; with next Carrier address MOVL CAR$PQ_QBUF_PTR(R2),R7 ; Get Queue Buffer VA ; ; Setup Queue Buffer fields for the Target Event Acknowledge command. ; MOVQ QBUF$PQ_PA_QBUF(R7),- ; CCB address (PA) QBUF$PQ_CCB_ADDR(R7) MOVW #QBUF$C_TARGET_EVENT_ACK_SIZE,- ; CCB size QBUF$IW_CCB_LENGTH(R7) MOVB #QBUF$C_TARGET_EVENT_ACK,- ; Function code QBUF$IB_FUNC_CODE(R7) CLRB QBUF$IB_CAM_STS(R7) ; Clear CAM status CLRL QBUF$IS_CONNECT_ID(R7) ; Clear Connect id MOVB SPDT$IS_SCSI_ID_NUM(R4),- ; Setup target id QBUF$IB_TARGET_ID(R7) CLRL QBUF$IS_CAM_FLAGS(R7) ; Clear CAM flags BISB #QBUF$M_NO_DATA_TX,- ; Direction is no data QBUF$IB_CAM_FLAGS_1(R7) CLRL QBUF$IS_DATA_TRAN_SIZE(R7) ; Clear data transfer length CLRW QBUF$IW_TEA_SEQUENCE_ID(R7) ; Clear sequence id MOVB SPDT$IS_SCSI_ID_NUM(R4),- ; Setup initiator id QBUF$IB_TEA_IID(R7) CLRB QBUF$IB_TEA_TAG_ID(R7) ; Clear tag id CLRB QBUF$IB_TEA_FLAGS(R7) ; Clear flags BISB #QBUF$M_CLEAR_ALL_EVENTS,- ; Clear all events QBUF$IB_TEA_FLAGS(R7) ; ; Setup Carrier fields. ; CLRB CAR$IB_STATUS(R2) ; Clear Status CLRW CAR$IW_FLAGS(R2) ; Flags CLRL CAR$IS_CONNECT_ID(R2) ; Connect id ; ; Send the command to the SIMport adapter. This will cause the adapter to ; resume responding to selections. ; CLRQ CAR$PQ_QBUF_TOKEN(R2) ; Clear Q_Buffer virtual addr MOVL R7,CAR$PQ_QBUF_TOKEN(R2) ; Copy Q_Buffer virtual addr BSBW INSERT_DACQ ; Insert command on DACQ DEVICELOCK - ; Get device lock and raise IPL LOCKADDR = SPDT$L_DLCK(R4),- ; to prevent interrupts LOCKIPL = SPDT$B_DIPL(R4),- SAVIPL = -(SP),- PRESERVE = NO EVAX_LDQ R9,CAR$PQ_PA(R2) ; Get PA of new Stopper Carrier EVAX_SRL R9,#TZA_DACQIR$C_PA_SHIFT,R9 ; Shift PA right 3 bits WRITE_CSR REG = DACQIR,- ; Give DACQ head PA to adapter DATA = R9,- TYPE = L DEVICEUNLOCK - ; Release device lock LOCKADDR = SPDT$L_DLCK(R4),- NEWIPL = (SP)+,- PRESERVE = NO 80$: MOVL SPDT$L_PORT_UCB(R4),R5 ; Get port UCB BISL #UCB$M_ONLINE,UCB$L_STS(R5) ; Set UCB online BISL #SPDT$M_STS_ONLINE,- ; Set SPDT online SPDT$L_STS(R4) BRW RSP_LOOP ; Check for more responses ; ; We received a device disabled response message. a Set Device State Enabled ; command to the adapter. R2 = Carrier, R7 = Queue Buffer. ; DEV_DISABLED_RSP: MOVL CAR$IS_CONNECT_ID(R2),- ; Save device connection id SPDT$IS_DEV_CONNECT_ID(R4) BSBW INSERT_DAFQ ; Return resources to free queue ; ; Allocate some pool for a Queue Buffer (CCB). ; 85$: SUBL #8,SP ; Allocate two lw on stack MOVL SP,R2 ; Store address of lw to receive ; address of allocated block ADDL3 #4,R2,R6 ; Store address of lw to receive ; size of allocated block PUSHL R6 ; Arg 4 = addr to receive size PUSHL R2 ; Arg 3 = addr to receive addr ; of allocated block PUSHL #ALN_LONG ; Arg 2 = 4 byte alignment PUSHL #256 ; Arg 1 = size of block CALLS #4,EXE$ALONONPAGED_ALN ; Allocate a block POPL R2 ; Get addr of allocated block POPL R6 ; Get allocated size BLBS R0,90$ ; Did we get the block? MOVL SPDT$PS_KPB(R4),R8 ; No, get KPB address KP_STALL_FORK_WAIT KPB=R8,- ; Fork and wait FKB=KPB$PS_UCB(R8) ; using UCB fork block BRB 85$ ; Try allocation again 90$: PUSHR #^M ; Save registers MOVC5 #0,(SP),#0,R6,(R2) ; Zero initialize the block POPR #^M ; Restore registers MOVL R2,R7 ; Copy Queue Buffer pointer ; ; Set up Queue Buffer fields for the Set Device State Enabled command. ; MOVW R6,QBUF$IW_SIZE(R7) ; Save Queue Buffer size MOVB #DYN$C_MISC,QBUF$IB_TYPE(R7) ; Set buffer type MOVB #DYN$C_QBUF,QBUF$IB_SUBTYPE(R7) ; Set buffer subtype MOVAB QBUF$PQ_CCB_ADDR(R7),R6 ; VA of adapter visible section $SVA_TO_PA SVA = R6 ; PA of adapter visible section BITL #SPDT$M_PFLG_MAPPING_REG,- ; Are we using map regs? SPDT$L_PORT_FLAGS(R4) BEQL 95$ ; Nope; just use PA. ; ; Is the Queue Buffer within the direct-DMA window? ; EVAX_ADDQ #QBUF$C_ADPLEN,R0,R3 ; Yes, point to last byte in AB EVAX_CMPULE R3,SPDT$IQ_DMA_SIZE(R4),R10 ; Compare to DMA window size BLBS R10,95$ ; Branch if LE window size ; ; O.K., we hafta map it. ; MAP_S0 - SVA = R6,- ; Queue Buffer address (VA). NBYTES = #QBUF$C_ADPLEN,- ; Size to map. CRCTX = QBUF$PS_CRCTX_PTR(R7),- ; CRCTX pointer reference. KPB$ = SPDT$PS_KPB(R4),- ; KPB to stall on alloc fail DMA_ADDR = QBUF$PQ_PA_QBUF(R7) ; Where to put DMA-able addr BRB 100$ ; ; No mapping registers needed, just use PA (+ DMA base). ; 95$: ADDL2 SPDT$L_DMA_BASE(R4),R0 ; Add in DMA window base EVAX_ZAP R0,#^XF0,R0 ; Clear the upper longword EVAX_STQ R0,QBUF$PQ_PA_QBUF(R7) ; Save physical address 100$: MOVQ QBUF$PQ_PA_QBUF(R7),- ; Setup PA of CCB QBUF$PQ_CCB_ADDR(R7) MOVW #QBUF$C_SET_DEV_STATE_SIZE,- ; Setup CCB size QBUF$IW_CCB_LENGTH(R7) MOVB #QBUF$C_SET_DEV_STATE,- ; Setup function code QBUF$IB_FUNC_CODE(R7) MOVB #QBUF$C_SET_DEV_STATE_ENB,- ; Request enabled state QBUF$IB_DEV_STATE(R7) CLRB QBUF$IB_CAM_STS(R7) ; Clear CAM status MOVL SPDT$IS_DEV_CONNECT_ID(R4),- ; Set up device connection id QBUF$IS_CONNECT_ID(R7) BISB #QBUF$M_NO_DATA_TX,- ; Direction is no data QBUF$IB_CAM_FLAGS_1(R7) BISB #QBUF$M_SIM_Q_FREEZE_DIS,- ; Set SIM queue freeze disable QBUF$IB_CAM_FLAGS_2(R7) ; ; Get a Queue Carrier and setup Carrier fields. ; BSBW GET_QUEUE_CARRIER ; Get a Queue Carrier CLRB CAR$IB_STATUS(R2) ; Clear Status CLRW CAR$IW_FLAGS(R2) ; Flags MOVL SPDT$IS_DEV_CONNECT_ID(R4),- ; Set up device connection id CAR$IS_CONNECT_ID(R2) ; ; Send the command to the SIMport adapter. ; BSBW INSERT_DACQ ; Insert command on DACQ DEVICELOCK - ; Get device lock and raise IPL LOCKADDR = SPDT$L_DLCK(R4),- ; to prevent interrupts LOCKIPL = SPDT$B_DIPL(R4),- SAVIPL = -(SP),- PRESERVE = NO EVAX_LDQ R9,CAR$PQ_PA(R2) ; Get PA of new Stopper Carrier EVAX_SRL R9,#TZA_DACQIR$C_PA_SHIFT,R9 ; Shift PA right 3 bits WRITE_CSR REG = DACQIR,- ; Give DACQ head PA to adapter DATA = R9,- TYPE = L DEVICEUNLOCK - ; Release device lock LOCKADDR = SPDT$L_DLCK(R4),- NEWIPL = (SP)+,- PRESERVE = NO BRW RSP_LOOP ; Check for more responses ; ; We received a Device State Set Response message. Deallocate command ; resources. ; DEV_STATE_SET_RSP: BSBW RET_QUEUE_CARRIER ; Return Carrier MOVL QBUF$PS_CRCTX_PTR(R7),R3 ; Did we alloc a CRCTX? BEQL 120$ PUSHL R3 ; Yep, deallocate the regs. PUSHL SPDT$PS_CRAB(R4) CALLS #2,G^IOC$DEALLOC_CNT_RES PUSHL R3 CALLS #1,G^IOC$DEALLOC_CRCTX ; And deallocate the CRCTX 120$: MOVL R7,R0 ; Get buffer address MOVZWL QBUF$IW_SIZE(R7),R1 ; and size JSB EXE$DEANONPAGED ; Deallocate command Buffer BRW RSP_LOOP ; Check for more responses ; ; We received the following response messages. Currently we should never ; get here. These messages are handled at unit init time by polling on ; the ADRQ (e.g. Interrupts are not enabled). These messages are included ; here in case these routines are needed in the future. ; ADAP_STATE_SET_RSP: PARAMETER_SET_RSP: BRW RSP_LOOP ; Check for more responses ; ; We received the following response messages. Resume the thread that was ; waiting for this response. ; CNTRS_READ_RSP: ADAP_SANITY_VER_RSP: BSBW RET_QUEUE_CARRIER ; Return Carrier MOVL QBUF$PS_SCDRP(R7),R5 ; Get SCDRP address MOVL SCDRP$PS_KPB(R5),R0 ; Get KPB address PUSHL R0 ; Pass KPB address CALLS #1,G^EXE$KP_RESTART ; Arrange for waiter to resume BRW RSP_LOOP ; Check for more responses ; ; We received a request for another Buffer Segment Descriptor (BSD). ; PKSDRIVER currently does not support this mechanism. Enough BSDs are ; sent to the adapter for each IO request to map the entire buffer. ; Just return the resources (Carrier/Queue Buffer) to the DAFQ. ; BSD_REQ_RSP: BSBW INSERT_DAFQ ; Return resources to free queue BRW RSP_LOOP ; Check for more responses ; ; We received an Accept Target IO response. If the CAM status is CDB_RECEIVED, ; determine the CDB function. We handle Inquiry and Request Sense commands. ; For all other commands, send back check condition status. ; If the CAM status is not CDB_RECEIVED, then just return this CCB to the ; adapter as a free Accept. ; ACCEPT_TARGET_IO_RSP: MOVL R2,SPDT$IS_CAR_ADDR(R4) ; DOF for debug MOVL R7,SPDT$IS_QBUF_ADDR(R4) ; DOF for debug INCL SPDT$IS_NUM_ACCEPTS(R4) ; Keep track of these CMPB CAR$IB_STATUS(R2),- ; Check returned CAM status #CAR$C_SCSI_CDB_REC BEQL 125$ ; Is it CDB received? ; ; The CAM status is not CDB_RECEIVED. Just return this CCB to the adapter. ; MOVW #QBUF$C_ACCEPT_TARGET_IO_SIZE,- ; CCB length QBUF$IW_CCB_LENGTH(R7) MOVB #QBUF$C_ACCEPT_TARGET_IO,- ; Queue Buffer function code QBUF$IB_FUNC_CODE(R7) CLRL QBUF$IS_CAM_FLAGS(R7) ; Clear CAM flags BISB #QBUF$M_DISCONS_MANDATORY,- ; Discon for each CCB for LUN QBUF$IB_CAM_FLAGS_4(R7) CLRB QBUF$IB_SCSI_STS(R7) ; Send back good SCSI status CLRB QBUF$IB_CTIO_SCSI_STAT(R7) ; in Private Data Area also ; ; Return the free Accept CCB to the SIMport adapter. ; CLRQ CAR$PQ_QBUF_TOKEN(R2) ; Clear Q_Buffer virtual addr MOVL R7,CAR$PQ_QBUF_TOKEN(R2) ; Copy Q_Buffer virtual addr BSBW INSERT_DACQ ; Insert command on DACQ DEVICELOCK - ; Get device lock and raise IPL LOCKADDR = SPDT$L_DLCK(R4),- ; to prevent interrupts LOCKIPL = SPDT$B_DIPL(R4),- SAVIPL = -(SP),- PRESERVE = NO EVAX_LDQ R9,CAR$PQ_PA(R2) ; Get PA of new Stopper Carrier EVAX_SRL R9,#TZA_DACQIR$C_PA_SHIFT,R9 ; Shift PA right 3 bits WRITE_CSR REG = DACQIR,- ; Give DACQ head PA to adapter DATA = R9,- TYPE = L DEVICEUNLOCK - ; Release device lock LOCKADDR = SPDT$L_DLCK(R4),- NEWIPL = (SP)+,- PRESERVE = NO BRW RSP_LOOP ; Check for more responses 125$: CMPB QBUF$IB_CDB+CDB$IB_OPCODE(R7),- ; Check CDB op code #SCSI$K_INQUIRY BEQL 130$ ; Is this an Inquiry command? CMPB QBUF$IB_CDB+CDB$IB_OPCODE(R7),- ; No, check for Request Sense #SCSI$K_REQ_SENSE BEQL 140$ ; Is this a Req Sense command? ; ; This is an invalid command. Send back Check Condition status. ; MOVW #QBUF$C_CONTINUE_TARGET_IO_SIZE,- ; CCB length QBUF$IW_CCB_LENGTH(R7) MOVB #QBUF$C_CONT_TARGET_IO,- ; Queue Buffer function code QBUF$IB_FUNC_CODE(R7) CLRB QBUF$IB_CAM_STS(R7) ; Clear CAM status MOVL CAR$IS_CONNECT_ID(R2),- ; Set up connect id QBUF$IS_CONNECT_ID(R7) CLRL QBUF$IS_CAM_FLAGS(R7) ; Clear CAM flags BISB #QBUF$M_NO_DATA_TX,- ; Direction is no data QBUF$IB_CAM_FLAGS_1(R7) BISB #QBUF$M_SEND_STATUS,- ; Send status after data phase QBUF$IB_CAM_FLAGS_4(R7) CLRL QBUF$IS_DATA_TRAN_SIZE(R7) ; Clear data transfer length MOVB #SCSI$C_CHECK_CONDITION,- ; Return check Condition status QBUF$IB_SCSI_STS(R7) MOVB QBUF$IB_ATIO_INIT_ID(R7),- ; Setup initiator id QBUF$IB_INITIATOR_ID(R7) MOVB #SCSI$C_CHECK_CONDITION,- ; Return Check Condition status QBUF$IB_CTIO_SCSI_STAT(R7) ; in Private Data Area also ; ; Send the CONTINUE_TARGET_IO command to the SIMport adapter. ; CLRQ CAR$PQ_QBUF_TOKEN(R2) ; Clear Q_Buffer virtual addr MOVL R7,CAR$PQ_QBUF_TOKEN(R2) ; Copy Q_Buffer virtual addr BSBW INSERT_DACQ ; Insert command on DACQ DEVICELOCK - ; Get device lock and raise IPL LOCKADDR = SPDT$L_DLCK(R4),- ; to prevent interrupts LOCKIPL = SPDT$B_DIPL(R4),- SAVIPL = -(SP),- PRESERVE = NO EVAX_LDQ R9,CAR$PQ_PA(R2) ; Get PA of new Stopper Carrier EVAX_SRL R9,#TZA_DACQIR$C_PA_SHIFT,R9 ; Shift PA right 3 bits WRITE_CSR REG = DACQIR,- ; Give DACQ head PA to adapter DATA = R9,- TYPE = L DEVICEUNLOCK - ; Release device lock LOCKADDR = SPDT$L_DLCK(R4),- NEWIPL = (SP)+,- PRESERVE = NO BRW RSP_LOOP ; Check for more responses ; ; This is an Inquiry command. Send back Inquiry data. ; 130$: MOVW #QBUF$C_CONTINUE_TARGET_IO_SIZE,- ; CCB length QBUF$IW_CCB_LENGTH(R7) MOVB #QBUF$C_CONT_TARGET_IO,- ; Queue Buffer function code QBUF$IB_FUNC_CODE(R7) BISB #QBUF$M_SEND_STATUS,- ; Send status after data phase QBUF$IB_CAM_FLAGS_4(R7) MOVB QBUF$IB_ATIO_INIT_ID(R7),- ; Setup initiator id QBUF$IB_INITIATOR_ID(R7) CLRB QBUF$IB_SCSI_STS(R7) ; Send back good SCSI status CLRB QBUF$IB_CTIO_SCSI_STAT(R7) ; in Private Data Area also ; ; Build a BSD to point to the Inquiry data. Check Inquiry allocation ; length in CDB. Only return up to the allocated or standard length, ; whichever is smaller. ; TSTB QBUF$IB_CDB+CDB$IB_ALLOC_LENGTH(R7) ; Check requested alloc length BNEQ 134$ ; Is it non-zero? BISB #QBUF$M_NO_DATA_TX,- ; No, direction is no data QBUF$IB_CAM_FLAGS_1(R7) CLRQ QBUF$PS_CTIO_DATA_BSD1(R7) ; Clear BSD CLRL QBUF$IS_DATA_TRAN_SIZE(R7) ; Clear data transfer length BRB 138$ ; Join common code 134$: CMPB QBUF$IB_CDB+CDB$IB_ALLOC_LENGTH(R7),- ; Check requested alloc #INQ$C_DATA_LENGTH ; length to our length BLSSU 135$ ; Are they requesting more? MOVL #INQ$C_DATA_LENGTH,R9 ; Yes, send back our length BRB 137$ ; Join common code 135$: MOVZBL QBUF$IB_CDB+CDB$IB_ALLOC_LENGTH(R7),R9; Send back what they ; are requesting 137$: MOVL SPDT$PS_INQUIRY_DATA(R4),R6 ; Point to Inquiry data ADDL #INQ$C_HEADER_LENGTH,R6 ; Point past header BUILD_BSD - ; Build BSD for Inquiry data BSD_ADDR = QBUF$PS_CTIO_DATA_BSD1(R7),- BUFFER_SVA = R6,- BYTE_COUNT = R9 BISB #QBUF$M_WRITE,- ; Data direction is out QBUF$IB_CAM_FLAGS_1(R7) MOVL R9,QBUF$IS_DATA_TRAN_SIZE(R7) ; Setup data transfer length ; ; Send the CONTINUE_TARGET_IO command to the SIMport adapter. ; 138$: CLRQ CAR$PQ_QBUF_TOKEN(R2) ; Clear Q_Buffer virtual addr MOVL R7,CAR$PQ_QBUF_TOKEN(R2) ; Copy Q_Buffer virtual addr BSBW INSERT_DACQ ; Insert command on DACQ MOVB #CAR$C_SUCCESS,- ; Send back good status CAR$IB_STATUS(R1) DEVICELOCK - ; Get device lock and raise IPL LOCKADDR = SPDT$L_DLCK(R4),- ; to prevent interrupts LOCKIPL = SPDT$B_DIPL(R4),- SAVIPL = -(SP),- PRESERVE = NO EVAX_LDQ R9,CAR$PQ_PA(R2) ; Get PA of new Stopper Carrier EVAX_SRL R9,#TZA_DACQIR$C_PA_SHIFT,R9 ; Shift PA right 3 bits WRITE_CSR REG = DACQIR,- ; Give DACQ head PA to adapter DATA = R9,- TYPE = L DEVICEUNLOCK - ; Release device lock LOCKADDR = SPDT$L_DLCK(R4),- NEWIPL = (SP)+,- PRESERVE = NO BRW RSP_LOOP ; Check for more responses ; ; This is a Request Sense command. Send back Sense data. ; 140$: MOVW #QBUF$C_CONTINUE_TARGET_IO_SIZE,- ; CCB length QBUF$IW_CCB_LENGTH(R7) MOVB #QBUF$C_CONT_TARGET_IO,- ; Queue Buffer function code QBUF$IB_FUNC_CODE(R7) BISB #QBUF$M_SEND_STATUS,- ; Send status after data phase QBUF$IB_CAM_FLAGS_4(R7) MOVB QBUF$IB_ATIO_INIT_ID(R7),- ; Setup initiator id QBUF$IB_INITIATOR_ID(R7) CLRB QBUF$IB_SCSI_STS(R7) ; Send back good SCSI status CLRB QBUF$IB_CTIO_SCSI_STAT(R7) ; in Private Data Area also ; ; Build a BSD to point to the Sense data. Check Sense allocation ; length in CDB. Only return up to the allocated or standard length, ; whichever is smaller. ; TSTB QBUF$IB_CDB+CDB$IB_ALLOC_LENGTH(R7) ; Check requested alloc length BNEQ 144$ ; Is it non-zero? BISB #QBUF$M_NO_DATA_TX,- ; No, direction is no data QBUF$IB_CAM_FLAGS_1(R7) CLRQ QBUF$PS_CTIO_DATA_BSD1(R7) ; Clear BSD CLRL QBUF$IS_DATA_TRAN_SIZE(R7) ; Clear data transfer length BRB 148$ ; Join common code 144$: CMPB QBUF$IB_CDB+CDB$IB_ALLOC_LENGTH(R7),- ; Check requested len #SENSE$C_DATA_LENGTH ; to our length BLSSU 145$ ; Are they requesting more? MOVL #SENSE$C_DATA_LENGTH,R9 ; Yes, send back our length BRB 147$ ; Join common code 145$: MOVZBL QBUF$IB_CDB+CDB$IB_ALLOC_LENGTH(R7),R9; Send back what they ; are requesting 147$: MOVL SPDT$PS_SENSE_DATA(R4),R6 ; Point to Sense data ADDL #SENSE$C_HEADER_LENGTH,R6 ; Point past header BUILD_BSD - ; Build BSD for Sense data BSD_ADDR = QBUF$PS_CTIO_DATA_BSD1(R7),- BUFFER_SVA = R6,- BYTE_COUNT = R9 BISB #QBUF$M_WRITE,- ; Data direction is out QBUF$IB_CAM_FLAGS_1(R7) MOVL R9,QBUF$IS_DATA_TRAN_SIZE(R7) ; Setup data transfer length ; ; Send the CONTINUE_TARGET_IO command to the SIMport adapter. ; 148$: CLRQ CAR$PQ_QBUF_TOKEN(R2) ; Clear Q_Buffer virtual addr MOVL R7,CAR$PQ_QBUF_TOKEN(R2) ; Copy Q_Buffer virtual addr BSBW INSERT_DACQ ; Insert command on DACQ MOVB #CAR$C_SUCCESS,- ; Send back good status CAR$IB_STATUS(R1) DEVICELOCK - ; Get device lock and raise IPL LOCKADDR = SPDT$L_DLCK(R4),- ; to prevent interrupts LOCKIPL = SPDT$B_DIPL(R4),- SAVIPL = -(SP),- PRESERVE = NO EVAX_LDQ R9,CAR$PQ_PA(R2) ; Get PA of new Stopper Carrier EVAX_SRL R9,#TZA_DACQIR$C_PA_SHIFT,R9 ; Shift PA right 3 bits WRITE_CSR REG = DACQIR,- ; Give DACQ head PA to adapter DATA = R9,- TYPE = L DEVICEUNLOCK - ; Release device lock LOCKADDR = SPDT$L_DLCK(R4),- NEWIPL = (SP)+,- PRESERVE = NO BRW RSP_LOOP ; Check for more responses ; ; We received a Continue Target IO response. Change the CCB into an Accept ; and return it to the adapter. ; CONT_TARGET_IO_RSP: MOVL R2,SPDT$IS_CAR_ADDR(R4) ; DOF for debug MOVL R7,SPDT$IS_QBUF_ADDR(R4) ; DOF for debug INCL SPDT$IS_NUM_CONTINUES(R4) ; Keep track of these MOVW #QBUF$C_ACCEPT_TARGET_IO_SIZE,- ; CCB length QBUF$IW_CCB_LENGTH(R7) MOVB #QBUF$C_ACCEPT_TARGET_IO,- ; Queue Buffer function code QBUF$IB_FUNC_CODE(R7) CLRB QBUF$IB_CAM_STS(R7) ; Clear CAM status MOVB SPDT$IS_SCSI_ID_NUM(R4),- ; Setup target id QBUF$IB_TARGET_ID(R7) CLRL QBUF$IS_CAM_FLAGS(R7) ; Clear CAM flags BISB #QBUF$M_DISCONS_MANDATORY,- ; Discon for each CCB for LUN QBUF$IB_CAM_FLAGS_4(R7) CLRB QBUF$IB_SCSI_STS(R7) ; Send back good SCSI status CLRB QBUF$IB_ATIO_SCSI_STAT(R7) ; in Private Data Area also ; ; Send the ACCEPT_TARGET_IO command to the SIMport adapter. ; CLRQ CAR$PQ_QBUF_TOKEN(R2) ; Clear Q_Buffer virtual addr MOVL R7,CAR$PQ_QBUF_TOKEN(R2) ; Copy Q_Buffer virtual addr BSBW INSERT_DACQ ; Insert command on DACQ DEVICELOCK - ; Get device lock and raise IPL LOCKADDR = SPDT$L_DLCK(R4),- ; to prevent interrupts LOCKIPL = SPDT$B_DIPL(R4),- SAVIPL = -(SP),- PRESERVE = NO EVAX_LDQ R9,CAR$PQ_PA(R2) ; Get PA of new Stopper Carrier EVAX_SRL R9,#TZA_DACQIR$C_PA_SHIFT,R9 ; Shift PA right 3 bits WRITE_CSR REG = DACQIR,- ; Give DACQ head PA to adapter DATA = R9,- TYPE = L DEVICEUNLOCK - ; Release device lock LOCKADDR = SPDT$L_DLCK(R4),- NEWIPL = (SP)+,- PRESERVE = NO BRW RSP_LOOP ; Check for more responses ; ; We received a TARGET_EVENT response. Send a SET_TARGET_STATE message ; to the adapter. ; TARGET_EVENT_RSP: INCL SPDT$IS_NUM_TARGET_EVENTS(R4) ; Keep track of these MOVL QBUF$IW_TE_SEQUENCE_ID(R7),- ; Save Target Event sequence id SPDT$IS_TARGET_EVENT(R4) MOVB QBUF$IB_TE_FLAGS(R7),- ; Save Target Event flags SPDT$IB_TARGET_FLAGS(R4) BSBW INSERT_DAFQ ; Return resources to free queue MOVL SPDT$PS_AB(R4),R6 ; Get Adapter Block address MOVL AB$PQ_ADFQ_HEAD(R6),R2 ; Get Carrier at head of queue MOVL CAR$PQ_NEXT_PTR(R2),- ; Update ADFQ head pointer AB$PQ_ADFQ_HEAD(R6) ; with next Carrier address MOVL CAR$PQ_QBUF_PTR(R2),R7 ; Get Queue Buffer VA MOVQ QBUF$PQ_PA_QBUF(R7),- ; CCB address (PA) QBUF$PQ_CCB_ADDR(R7) MOVW #QBUF$C_TARGET_STATE_SIZE,- ; CCB size QBUF$IW_CCB_LENGTH(R7) MOVB #QBUF$C_SET_TARGET_STATE,- ; Function code QBUF$IB_FUNC_CODE(R7) CLRB QBUF$IB_CAM_STS(R7) ; Clear CAM status CLRL QBUF$IS_CONNECT_ID(R7) ; Clear Connect id MOVB SPDT$IS_SCSI_ID_NUM(R4),- ; Setup target id QBUF$IB_TARGET_ID(R7) CLRL QBUF$IS_CAM_FLAGS(R7) ; Clear CAM flags BISB #QBUF$M_NO_DATA_TX,- ; Direction is no data QBUF$IB_CAM_FLAGS_1(R7) CLRL QBUF$IS_DATA_TRAN_SIZE(R7) ; Clear data transfer length MOVL SPDT$IS_TARGET_EVENT(R4),- ; Setup sequence id, IID, Tag ID QBUF$IW_TS_SEQUENCE_ID(R7) MOVB SPDT$IB_TARGET_FLAGS(R4),- ; Setup Target Event flags QBUF$IB_TS_FLAGS(R7) ; ; Setup Carrier fields. ; CLRB CAR$IB_STATUS(R2) ; Clear Status CLRW CAR$IW_FLAGS(R2) ; Flags CLRL CAR$IS_CONNECT_ID(R2) ; Connect id ; ; Send the SET_TARGET_STATE command to the SIMport adapter. ; CLRQ CAR$PQ_QBUF_TOKEN(R2) ; Clear Q_Buffer virtual addr MOVL R7,CAR$PQ_QBUF_TOKEN(R2) ; Copy Q_Buffer virtual addr BSBW INSERT_DACQ ; Insert command on DACQ DEVICELOCK - ; Get device lock and raise IPL LOCKADDR = SPDT$L_DLCK(R4),- ; to prevent interrupts LOCKIPL = SPDT$B_DIPL(R4),- SAVIPL = -(SP),- PRESERVE = NO EVAX_LDQ R9,CAR$PQ_PA(R2) ; Get PA of new Stopper Carrier EVAX_SRL R9,#TZA_DACQIR$C_PA_SHIFT,R9 ; Shift PA right 3 bits WRITE_CSR REG = DACQIR,- ; Give DACQ head PA to adapter DATA = R9,- TYPE = L DEVICEUNLOCK - ; Release device lock LOCKADDR = SPDT$L_DLCK(R4),- NEWIPL = (SP)+,- PRESERVE = NO BRW RSP_LOOP ; Check for more responses ; ; We received a TARGET_STATE_SET response message. ; Send a TARGET_EVENT_ACKNOWLEDGE command to the adapter. ; TARGET_STATE_SET_RSP: MOVQ QBUF$PQ_PA_QBUF(R7),- ; CCB address (PA) QBUF$PQ_CCB_ADDR(R7) MOVW #QBUF$C_TARGET_EVENT_ACK_SIZE,- ; CCB size QBUF$IW_CCB_LENGTH(R7) MOVB #QBUF$C_TARGET_EVENT_ACK,- ; Function code QBUF$IB_FUNC_CODE(R7) CLRB QBUF$IB_CAM_STS(R7) ; Clear CAM status CLRL QBUF$IS_CONNECT_ID(R7) ; Clear Connect id MOVB SPDT$IS_SCSI_ID_NUM(R4),- ; Setup target id QBUF$IB_TARGET_ID(R7) CLRL QBUF$IS_CAM_FLAGS(R7) ; Clear CAM flags BISB #QBUF$M_NO_DATA_TX,- ; Direction is no data QBUF$IB_CAM_FLAGS_1(R7) CLRL QBUF$IS_DATA_TRAN_SIZE(R7) ; Clear data transfer length CLRW QBUF$IW_TEA_SEQUENCE_ID(R7) ; Clear sequence id MOVB SPDT$IS_SCSI_ID_NUM(R4),- ; Setup initiator id QBUF$IB_TEA_IID(R7) CLRB QBUF$IB_TEA_TAG_ID(R7) ; Clear tag id CLRB QBUF$IB_TEA_FLAGS(R7) ; Clear flags BISB #QBUF$M_CLEAR_ALL_EVENTS,- ; Clear all events QBUF$IB_TEA_FLAGS(R7) ; ; Setup Carrier fields. ; CLRB CAR$IB_STATUS(R2) ; Clear Status CLRW CAR$IW_FLAGS(R2) ; Flags CLRL CAR$IS_CONNECT_ID(R2) ; Connect id ; ; Send the TARGET_EVENT_ACKNOWLEDGED command to the SIMport adapter. ; This will cause the adapter to resume responding to selections. ; CLRQ CAR$PQ_QBUF_TOKEN(R2) ; Clear Q_Buffer virtual addr MOVL R7,CAR$PQ_QBUF_TOKEN(R2) ; Copy Q_Buffer virtual addr BSBW INSERT_DACQ ; Insert command on DACQ DEVICELOCK - ; Get device lock and raise IPL LOCKADDR = SPDT$L_DLCK(R4),- ; to prevent interrupts LOCKIPL = SPDT$B_DIPL(R4),- SAVIPL = -(SP),- PRESERVE = NO EVAX_LDQ R9,CAR$PQ_PA(R2) ; Get PA of new Stopper Carrier EVAX_SRL R9,#TZA_DACQIR$C_PA_SHIFT,R9 ; Shift PA right 3 bits WRITE_CSR REG = DACQIR,- ; Give DACQ head PA to adapter DATA = R9,- TYPE = L DEVICEUNLOCK - ; Release device lock LOCKADDR = SPDT$L_DLCK(R4),- NEWIPL = (SP)+,- PRESERVE = NO BRW RSP_LOOP ; Check for more responses ; ; Now that all of the responses have been handled, make sure that there are ; no other responses on the ADRQ. If none, see if an adapter error interrupt ; has occurred. If so, log the error, and reinitialize the adapter. ; CHECK_ADAPTER_ERROR: DEVICELOCK - ; Lock device access LOCKADDR = SPDT$L_DLCK(R4),- LOCKIPL = SPDT$B_DIPL(R4),- SAVIPL = -(SP),- PRESERVE = NO MOVL AB$PQ_ADRQ_HEAD(R6),R2 ; Get next response Carrier BBS #0,CAR$PQ_NEXT_PTR(R2),200$ ; Is response queue empty? DEVICEUNLOCK - ; No, unlock device access LOCKADDR = SPDT$L_DLCK(R4),- NEWIPL = (SP)+,- PRESERVE = NO BRW RSP_LOOP ; Handle remaining responses ; ; The response queue is empty. ; 200$: MOVL SPDT$L_PORT_UCB(R4),R5 ; Get UCB address CLRL UCB$L_FPC(R5) ; Clear fork thread semaphore MOVL SPDT$IS_SAVE_ASR(R4),R9 ; Get copy of ASR DEVICEUNLOCK - ; Unlock device access LOCKADDR = SPDT$L_DLCK(R4),- NEWIPL = (SP)+,- PRESERVE = NO .BRANCH_UNLIKELY BBC #TZA_ASR$V_ASIC,R9,- ; If ASIC bit clear then an PKS$ADP_ERROR_INTERRUPT ; error interrupt ocurred RET ; Return to fork dispatcher .DISABLE LSB ; PKS$FORK .PAGE .SBTTL RESET_SCSI_BUS - Reset the SCSI Bus ; ; This routine is called to perform a SCSI bus reset. It creates a fake ; connection to set up required data structures, calls the SCSI bus reset ; routine, and clears the fake connection. ; ; This routine assumes that it is running in a Kernel Process context. ; ; INPUT: ; kpb(AP) = KPB address ; ; OUTPUT: None. ; .ENABLE LSB $ARG_DEF <- kpb > ; KPB address. RESET_SCSI_BUS: .CALL_ENTRY MOVL kpb(AP),R8 ; Get the KPB address MOVL KPB$PS_UCB(R8),R5 ; Get port's UCB address MOVL UCB$L_PDT(R5),R4 ; Get the port's SPDT address MOVL R4,KPB$PS_SCSI_PTR1(R8) ; Store SPDT into KPB BICL2 #UCB$M_ONLINE,UCB$L_STS(R5) ; Clear port UCB ONLINE BICL2 #SPDT$M_STS_ONLINE,- ; Clear port SPDT ONLINE SPDT$L_STS(R4) PUSHL KPB$PS_SCSI_SCDRP(R8) ; Save SCDRP from KPB BSBW GET_SCDRP ; Allocate the SCDRP MOVL R2,R5 ; Save the SCDRP MOVL R2,KPB$PS_SCSI_SCDRP(R8) ; Save SCDRP in KPB MOVL R8,SCDRP$PS_KPB(R5) ; Store KPB into SCDRP MOVL R4,SCDRP$PS_SPDT(R5) ; Store SPDT into SCDRP BICL #SPDT$M_INIT_ADAPTER,- ; Do not reinit the adapter SPDT$IS_PORTSTS(R4) BISL #SPDT$M_LOG_ERROR,- ; Log this in error log SPDT$IS_PORTSTS(R4) PUSHL R5 ; Pass SCDRP PUSHL R4 ; Pass SPDT CALLS #2,@SPDT$PS_CD_RESET_SCSI_BUS(R4) ; Reset SCSI bus MOVL R5,R0 ; Deallocate the SCDRP CALL_DRVDEALMEM SAVE_R0R1=NO ; COM_STD$DRVDEALMEM POPL KPB$PS_SCSI_SCDRP(R8) ; Clear the SCDRP pointer RET ; Return .DISABLE LSB ; RESET_SCSI_BUS .PAGE .SBTTL PKS$ADP_ERROR_INTERRUPT - Adapter error interrupt routine ; ; This routine handles all adapter-wide error interrupts. ; Adapter error interrupts are indicated by one of the following bits set in ; the adapter status register (ASR): ; ; ADSE - Host data structure error ; AMSE - Host memory system error ; AAC - Abnormal condition in adapter firmware ; AME - Adapter maintenance (hardware) error ; ; Adapter errors cause the adapter to go into the "uninitialized" state. ; The port driver will do the following: ; ; 1.) Save necessary registers. ; 2.) Log the adapter error in the system error log. ; 3.) Re-initialize the adapter. ; ; CALLED BY: PKS$FORK ; ; INPUT: ; R3 = IDB address ; R4 = SPDT address ; R5 = UCB address ; IPL$_IOLOCK8 ; ; OUTPUT: ; None ; .ENABLE LSB PKS$ADP_ERROR_INTERRUPT: .IF DEFINED PKSDEBUG MOVAB MISC_INTR_MSG,R1 ; Get addr of msg to output JSB G^EXE$OUTZSTRING ; Send the message .ENDC ; ; Save necessary registers. ; DEVICELOCK - ; Lock device access LOCKADDR = SPDT$L_DLCK(R4),- LOCKIPL = SPDT$B_DIPL(R4),- SAVIPL = -(SP),- PRESERVE = NO READ_CSR REG = ASR, DEST = SPDT$IS_SAVE_ASR(R4) READ_CSR REG = AFAR, DEST = SPDT$IS_SAVE_AFAR(R4) READ_CSR REG = AFPR, DEST = SPDT$IS_SAVE_AFPR(R4) READ_CSR REG = ERRLOG1, DEST = SPDT$IS_SAVE_ERRLOG1(R4) READ_CSR REG = ERRLOG2, DEST = SPDT$IS_SAVE_ERRLOG2(R4) READ_CSR REG = ERRLOG3, DEST = SPDT$IS_SAVE_ERRLOG3(R4) READ_CSR REG = ERRLOG4, DEST = SPDT$IS_SAVE_ERRLOG4(R4) READ_CSR REG = ERRLOG5, DEST = SPDT$IS_SAVE_ERRLOG5(R4) READ_CSR REG = ERRLOG6, DEST = SPDT$IS_SAVE_ERRLOG6(R4) READ_CSR REG = ERRLOG7, DEST = SPDT$IS_SAVE_ERRLOG7(R4) READ_CSR REG = ERRLOG8, DEST = SPDT$IS_SAVE_ERRLOG8(R4) READ_CSR REG = ERRLOG9, DEST = SPDT$IS_SAVE_ERRLOG9(R4) READ_CSR REG = ERRLOGA, DEST = SPDT$IS_SAVE_ERRLOGA(R4) DEVICEUNLOCK - ; Release device lock LOCKADDR = SPDT$L_DLCK(R4),- NEWIPL = (SP)+,- PRESERVE = NO ; ; The SIMport adapter returns the physical address of host memory associated ; with ASR errors. Convert the physical address to a virtual address for ; error logging. ; MOVL SPDT$IS_SAVE_AFAR(R4),R6 ; AFAR register PA ;; $PA_TO_SVA PA=R6 ; Convert to VA (how?) MOVL R0,SPDT$IS_SAVE_AFAR(R4) ; AFAR register VA ; ; Determine the cause of this interrupt. Set up the error type and subtype ; field for error logging. SPDT$IW_ERL_TYPE = SUBTYPE!TYPE. ; Log the adapter error in the system error log. ; BBS #TZA_ASR$V_ADSE,- ; Host data structure error SPDT$IS_SAVE_ASR(R4),10$ BBS #TZA_ASR$V_AMSE,- ; Host memory system error SPDT$IS_SAVE_ASR(R4),20$ BBS #TZA_ASR$V_AAC,- ; Adapter FW error SPDT$IS_SAVE_ASR(R4),30$ BBS #TZA_ASR$V_AME,- ; Adapter maintenance error SPDT$IS_SAVE_ASR(R4),40$ MOVW #SPO$C_UNKNOWN_ERR,- ; We should not get here SPDT$IW_ERL_TYPE(R4) BRB 50$ 10$: INSV #SPO$C_ADSE,- ; Set up error subtype #8,#8,SPDT$IW_ERL_TYPE(R4) BRW 50$ ; Log the error 20$: INSV #SPO$C_AMSE,- ; Set up error subtype #8,#8,SPDT$IW_ERL_TYPE(R4) BRW 50$ ; Log the error 30$: INSV #SPO$C_AAC,- ; Set up error subtype #8,#8,SPDT$IW_ERL_TYPE(R4) BRW 50$ ; Log the error 40$: INSV #SPO$C_AME,- ; Set up error subtype #8,#8,SPDT$IW_ERL_TYPE(R4) 50$: MOVB #SCSI$C_CTL_ERR,- ; Set up error type SPDT$IW_ERL_TYPE(R4) LOG_ERROR - ; Log error in error log TYPE = CTL_ERR,- CDT = #0 ; ; Re-initialize the adapter. ; BICL #UCB$M_ONLINE,UCB$L_STS(R5) ; Clear port UCB online BICL #SPDT$M_STS_ONLINE,- ; Clear channel online bit SPDT$L_STS(R4) CLRL SPDT$IS_CHANSTATE(R4) ; Channel state is UNIN MOVL SPDT$PS_KPB(R4),R8 ; Get KPB address BBS #KPB$V_VALID,KPB$IS_FLAGS(R8),- ; If REINIT_PORT kernel 60$ ; process is running now, the ; adapter reset condition will ; be picked up later MOVL SPDT$L_ADP(R4),R1 ; Get ADP address BICL #SPDT$M_PORTONLY,- ; Clear flag so that we will SPDT$IS_PORTSTS(R4) ; reinit the adapter MOVL SPDT$L_PORT_UCB(R4),- ; Set the port's UCB address KPB$PS_UCB(R8) ; in KPB KP_START KPB = R8, - ; Call Kernel Process routine REGISTERS = #PKS_KP_REGMSK, - ; to reinit the adapter ROUTINE = REINIT_PORT 60$: RET ; Return .DISABLE LSB ; PKS$ADP_ERROR_INTERRUPT .PAGE .SBTTL PKS_REGDUMP - Register dump routine ; ; This routine logs information about the current command and port counters ; when various error conditions are detected. If this is an adapter ; miscellaneous error, SIMport register contents will be logged as well. ; ; CALLED BY: ; error logging routine (ERL$DEVICEATTN) in ERRORLOG ; ; INPUT: ; R0 = Address of the buffer to fill ; R4 = Address of SPDT ; SPDT$IW_ERL_TYPE = error code, SUBTYPE!TYPE ; SPDT$PS_ERL_SCDT = SCDT address if non-zero ; SPDT$PS_COUNTERS = Pointer to port counter block in AB ; R5 = Address of UCB ; ; OUTPUT: ; None ; .ENABLE LSB PKS_REGDUMP: $DRIVER_REGDUMP_ENTRY - PRESERVE=,FETCH=YES MOVL R0,R9 ; Use R9 for address of the ; error log buffer MOVZWL SPDT$IW_ERL_TYPE(R4),R7 ; Get error code MOVL SPDT$PS_ERL_SCDT(R4),R8 ; Get SCDT address PUSHAL (R9)+ ; Save buffer address MOVB #PKS_ERROR_REV,(R9)+ ; Save revision level MOVW R7,(R9)+ ; Save error type and subtype TSTL R8 ; Any CDT address? BEQL 5$ ; Branch if not MOVL SCDT$PS_STDT(R8),R3 ; Get STDT address TSTL R3 ; Any STDT address? BEQL 5$ ; Branch if so MOVB STDT$IS_SCSI_ID_NUM(R3),(R9)+ ; Save target SCSI ID BRB 10$ 5$: MOVB SPDT$IS_SCSI_ID_NUM(R4),(R9)+ ; No STDT. Use adapter id ; ; Save information about the current SCSI command including the command, ; message, and status bytes if SCDT address was passed as a parameter. ; 10$: CLRW (R9)+ ; Assume no SCSI CMD,MSG MOVB #^XFF,(R9)+ ; Assume no SCSI STS byte TSTL R8 ; Any CDT? BEQL 30$ ; Branch if not MOVL SPDT$PS_CHIP_KPB(R4),R3 ; Get the KPB address BEQL 30$ ; Branch if not MOVL KPB$PS_SCSI_SCDRP(R3),R3 ; Get the SCDRP address BEQL 30$ ; Branch if none MOVL SCDRP$L_CMD_PTR(R3),R1 ; Get address of SCSI command BEQL 30$ ; Branch if none SUBL #3,R9 ; Back up ptr into errlog buffer MOVL (R1)+,R2 ; Get command byte count MOVB R2,(R9)+ ; Save command byte count 20$: MOVB (R1)+,(R9)+ ; Save a byte of command SOBGTR R2,20$ ; Repeat for entire command CLRB (R9)+ ; Assume no message byte since ; adap doesn't return MSG byte MOVB @SCDRP$L_STS_PTR(R3),(R9)+ ; Save status byte ; ; Save the path inquiry information that was returned from the adapter ; during initialization. ; 30$: MOVAB SPDT$IB_PI_DATA(R4),R2 ; Get start addr of PI data PUSHR #^M ; Save registers MOVC3 #SPDT$C_PI_DATA_SIZE,(R2),(R9) ; Copy path inquiry data into ; error log buffer MOVL R3,R9 ; Point past inquiry data POPR #^M ; Restore registers ; ; Save the contents of the port error counters. ; MOVB #AB$C_NUM_PORT_CNTRS,(R9)+ ; Save number of port counters MOVZBL #AB$C_NUM_PORT_CNTRS,R6 ; Number of port error counters MOVL SPDT$PS_COUNTERS(R4),R7 ; Point to first error counter 35$: MOVL (R7)+,(R9)+ ; Save next port counter SOBGTR R6,35$ ; until all counters are saved ; ; Save the contents of the SIMport registers. ; DEVICELOCK - ; Get device lock and raise IPL LOCKADDR = SPDT$L_DLCK(R4),- LOCKIPL = SPDT$B_DIPL(R4),- SAVIPL = -(SP),- PRESERVE = NO READ_CSR REG = ASR,DEST = (R9)+ ; Save ASR register READ_CSR REG = AFAR,DEST = (R9)+ ; Save AFAR register READ_CSR REG = AFPR,DEST = (R9)+ ; Save AFPR register READ_CSR REG = ERRLOG1,DEST = (R9)+ ; Save error log1 register READ_CSR REG = ERRLOG2,DEST = (R9)+ ; Save error log2 register READ_CSR REG = ERRLOG3,DEST = (R9)+ ; Save error log3 register READ_CSR REG = ERRLOG4,DEST = (R9)+ ; Save error log4 register READ_CSR REG = ERRLOG5,DEST = (R9)+ ; Save error log5 register READ_CSR REG = ERRLOG6,DEST = (R9)+ ; Save error log6 register READ_CSR REG = ERRLOG7,DEST = (R9)+ ; Save error log7 register READ_CSR REG = ERRLOG8,DEST = (R9)+ ; Save error log8 register READ_CSR REG = ERRLOG9,DEST = (R9)+ ; Save error log9 register READ_CSR REG = ERRLOGA,DEST = (R9)+ ; Save error logA register DEVICEUNLOCK - ; Release device lock LOCKADDR = SPDT$L_DLCK(R4),- NEWIPL = (SP)+,- PRESERVE = NO 50$: SUBL (SP),R9 ; Get number of bytes saved ADDL #3,R9 ; Round up and translate to lw ASHL #-2,R9,@(SP)+ ; Save in top of errlog buffer MOVL R9,R0 ; Copy current pos. in buffer RET ; Return .DISABLE LSB ; PKS_REG_DUMP .PAGE .SBTTL SETUP_QUEUES - Set up the queues in the Adapter Block ; ; This subroutine initializes the adapter queue headers in the Adapter Block ; by placing Stopper Queue Carriers on all queues in the AB. ; ; Note: The virtual address of a Carrier points to the start of the ; Carrier. This allows the Carrier offsets to be correct. ; The physical address of a Carrier points to the start of the ; adapter visible section of the Carrier at offset CAR$PQ_NEXT_PTR ; (which is not the beginning of the Carrier). ; ; This routine should be called only during adapter initialization when the ; SIMport channel is NOT in the ENABLED state. ; ; This routine assumes that it is running in a Kernel Process context. ; ; INPUT: ; R4 = SPDT address ; R8 = KPB address of the initialization sequence ; ; OUTPUT: ; R0 = Status ; All queue headers are initialized with Stopper Queue Carriers. ; .ENABLE LSB SETUP_QUEUES: .JSB_ENTRY INPUT=, SCRATCH=,- OUTPUT=, PRESERVE= MOVL SPDT$PS_AB(R4),R5 ; Get Adapter Block address ; ; Set up and insert a Stopper Carrier on the DACQ queue. ; BSBW GET_QUEUE_CARRIER ; Get a Carrier ; R2 = VA of allocated Carrier MOVL R2,AB$PQ_DACQ_TAIL(R5) ; Store Stopper Carrier VA in ; DACQ tail .Disable Flagging MOVQ CAR$PQ_PA(R2),- ; Store Stopper Carrier PA in AB$PQ_DACQ_HEAD(R5) ; DACQ head .Enable Flagging ; ; Set up and insert a Stopper Carrier on the ADRQ queue. ; BSBW GET_QUEUE_CARRIER ; Get a Carrier ; R2 = VA of allocated Carrier MOVL R2,AB$PQ_ADRQ_HEAD(R5) ; Store Stopper Carrier VA in ; ADRQ head .Disable Flagging MOVQ CAR$PQ_PA(R2),- ; Store Stopper Carrier PA in AB$PQ_ADRQ_TAIL(R5) ; ADRQ tail .Enable Flagging EVAX_OR CAR$PQ_NEXT_PTR(R2),#1,- ; Make this a Stopper Carrier CAR$PQ_NEXT_PTR(R2) ; ; Set up and insert a Stopper Carrier on the DAFQ queue. ; BSBW GET_QUEUE_CARRIER ; Get a Carrier ; R2 = VA of allocated Carrier MOVL R2,AB$PQ_DAFQ_TAIL(R5) ; Store Stopper Carrier VA in ; DAFQ tail .Disable Flagging MOVQ CAR$PQ_PA(R2),- ; Store Stopper Carrier PA in AB$PQ_DAFQ_HEAD(R5) ; DAFQ head .Enable Flagging ; ; Set up and insert a Stopper Carrier on the ADFQ queue. ; BSBW GET_QUEUE_CARRIER ; Get a Carrier ; R2 = VA of allocated Carrier MOVL R2,AB$PQ_ADFQ_HEAD(R5) ; Store Stopper Carrier VA in ; ADFQ head .Disable Flagging MOVQ CAR$PQ_PA(R2),- ; Store Stopper Carrier PA in AB$PQ_ADFQ_TAIL(R5) ; ADFQ tail .Enable Flagging EVAX_OR CAR$PQ_NEXT_PTR(R2),#1,- ; Make this a Stopper Carrier CAR$PQ_NEXT_PTR(R2) MOVZWL #SS$_NORMAL,R0 ; Success return status RSB ; Return .DISABLE LSB ; SETUP_QUEUES .PAGE .SBTTL SETUP_DAFQ - Set up Driver Adapter Free Queue entries ; ; This subroutine is called to set up free queue entries in the DAFQ ; (Driver Adapter Free Queue). It is called from the REINIT_PORT routine after ; the adapter/channel has transitioned to the ENABLED state. ; ; It allocates Q_Buffers and Carriers for the DAFQ (Driver Adapter Free ; Queue) and writes the physical address of the Stopper Carrier on the DAFQ ; to the DAFQIR (Driver Adapter Free Queue Insertion Register) so that the ; adapter can use the free queue entries if needed. ; ; Note: The virtual address of a Carrier points to the start of the ; Carrier. This allows the Carrier offsets to be correct. ; The physical address of a Carrier points to the start of the ; adapter visible section of the Carrier at offset CAR$PQ_NEXT_PTR ; (which is not the beginning of the Carrier). ; ; This routine assumes that it is running in a Kernel Process context. ; ; INPUT: ; R4 = SPDT address ; R6 = Number of free queue entries to insert onto DAFQ ; R8 = KPB address ; ; OUTPUT: ; R0 = SS$_NORMAL Status ; .ENABLE LSB SETUP_DAFQ: .JSB_ENTRY INPUT=, SCRATCH=,- OUTPUT=, PRESERVE= ; ; Allocate some pool for a buffer and zero the block. ; 10$: SUBL #8,SP ; Allocate two lw on stack MOVL SP,R2 ; Store address of lw to receive ; address of allocated block ADDL3 #4,R2,R9 ; Store address of lw to receive ; size of allocated block PUSHL R9 ; Arg 4 = addr to receive size PUSHL R2 ; Arg 3 = addr to receive addr ; of allocated block PUSHL #ALN_LONG ; Arg 2 = 4 byte alignment PUSHL #256 ; Arg 1 = size of block CALLS #4,EXE$ALONONPAGED_ALN ; Allocate a block POPL R2 ; Get addr of allocated block POPL R9 ; Get allocated size BLBS R0,20$ ; Did we get the block? KP_STALL_FORK_WAIT KPB=R8,- ; No, fork and wait FKB=KPB$PS_UCB(R8) ; using UCB fork block BRB 10$ ; Try allocation again 20$: PUSHR #^M ; Save registers MOVC5 #0,(SP),#0,R9,(R2) ; Zero initialize the block POPR #^M ; Restore registers ; ; Initialize static fields in the Queue Buffer. R2 points to Queue Buffer. ; MOVW R9,QBUF$IW_SIZE(R2) ; Save Queue Buffer size MOVB #DYN$C_MISC,QBUF$IB_TYPE(R2) ; Set buffer type MOVB #DYN$C_QBUF,QBUF$IB_SUBTYPE(R2) ; Set buffer subtype MOVAB QBUF$PQ_CCB_ADDR(R2),R9 ; VA of adapter visible section $SVA_TO_PA SVA = R9 ; PA of adapter visible section BITL #SPDT$M_PFLG_MAPPING_REG,- ; Are we using map regs? SPDT$L_PORT_FLAGS(R4) BEQL 30$ ; Nope; just use PA. ; ; Is the Queue Buffer within the direct-DMA window? ; EVAX_ADDQ #QBUF$C_ADPLEN,R0,R3 ; Yes, just use PA EVAX_CMPULE R3,SPDT$IQ_DMA_SIZE(R4),R7 ; Compare to DMA window size BLBS R7,30$ ; Branch if LE window size ; ; O.K., we hafta map it. ; MAP_S0 - SVA = R9,- ; Queue Buffer address (VA). NBYTES = #QBUF$C_ADPLEN,- ; Size to map. CRCTX = QBUF$PS_CRCTX_PTR(R2),- ; CRCTX pointer reference. KPB$ = SPDT$PS_KPB(R4),- ; KPB to stall on alloc fail DMA_ADDR = QBUF$PQ_PA_QBUF(R2) ; Where to put DMA-able addr BRB 40$ ; ; No mapping registers needed, just use PA (+ DMA base). ; 30$: ADDL2 SPDT$L_DMA_BASE(R4),R0 ; Add in DMA window base EVAX_ZAP R0,#^XF0,R0 ; Clear the upper longword EVAX_STQ R0,QBUF$PQ_PA_QBUF(R2) ; Save physical address 40$: MOVL R2,R7 ; Save the Q_Buffer address BSBW GET_QUEUE_CARRIER ; Get a Carrier MOVL SPDT$PS_AB(R4),R5 ; Get the Adapter Block address MOVL AB$PQ_DAFQ_TAIL(R5),R1 ; Get the DAFQ tail ptr .Disable Flagging MOVQ QBUF$PQ_PA_QBUF(R7),- ; Link Q_Buffer phys. address CAR$PQ_QBUF_PTR(R1) ; into stopper Carrier MOVL R7,CAR$PQ_QBUF_TOKEN(R1) ; Copy Q_Buffer virtual address MOVL R1,QBUF$PS_CARRIER(R7) ; Save new carrier's address ; in Q_Buffer EVAX_MB ; Make sure Q_Buffer contents ; and pointer are visible to ; the adapter before NEXT_PTR ; becomes valid EVAX_ADDQ #1,CAR$PQ_PA(R2),- ; Link in new Stopper and CAR$PQ_NEXT_PTR(R1) ; make it a valid Carrier .Enable Flagging CLRB CAR$IB_FUNC_CODE(R1) ; Clear CAM function code CLRB CAR$IB_STATUS(R1) ; Clear CAM status CLRW CAR$IW_FLAGS(R1) ; Clear CAM flags EVAX_MB ; Make sure new Stopper is ; visible to adapter before ; adapter accesses DAFQ MOVL R2,AB$PQ_DAFQ_TAIL(R5) ; Update tail with new Stopper SOBGTR R6,10$ ; Loop for all DAFQ entries ; ; We must notify the adapter of the entries and Stopper Carrier on the DAFQ. ; Write the physical address of the Stopper Carrier (shifted right 3 bits) on ; the DAFQ to the Driver Adapter Free Queue Insertion Register (DAFQIR). ; MOVL AB$PQ_DAFQ_TAIL(R5),R2 ; VA of DAFQ Stopper Carrier EVAX_LDQ R7,CAR$PQ_PA(R2) ; PA of DAFQ Stopper Carrier EVAX_SRL R7,#TZA_DAFQIR$C_PA_SHIFT,R7 ; Shift PA right 3 bits DEVICELOCK - ; Lock device access LOCKADDR = SPDT$L_DLCK(R4),- LOCKIPL = SPDT$B_DIPL(R4),- SAVIPL = -(SP),- PRESERVE = NO WRITE_CSR REG = DAFQIR,- ; Give DAFQ Stopper PA to adap DATA = R7,- TYPE = Q DEVICEUNLOCK - ; Unlock device access LOCKADDR = SPDT$L_DLCK(R4),- NEWIPL = (SP)+,- PRESERVE = NO MOVZWL #SS$_NORMAL,R0 ; Success return status RSB ; Return .DISABLE LSB ; SETUP_DAFQ .PAGE .SBTTL INSERT_DAFQ - Insert a Driver Adapter Free Queue entry ; ; This subroutine is called to insert a free queue entry onto the DAFQ ; (Driver Adapter Free Queue). ; ; After inserting the Queue Buffer and Carrier onto the DAFQ, it writes ; the physical address of the Stopper Carrier on the DAFQ to the ; DAFQIR (Driver Adapter Free Queue Insertion Register) so that the adapter ; can use the free queue entries if needed. ; ; Note: The virtual address of a Carrier points to the start of the ; Carrier. This allows the Carrier offsets to be correct. ; The physical address of a Carrier points to the start of the ; adapter visible section of the Carrier at offset CAR$PQ_NEXT_PTR ; (which is not the beginning of the Carrier). ; ; This routine assumes that it is running in a Kernel Process context. ; ; INPUT: ; R4 = SPDT address ; R2 = Address of Carrier to insert onto DAFQ ; R7 = Address of Queue Buffer to insert onto DAFQ ; ; OUTPUT: ; R0 = SS$_NORMAL Status ; .ENABLE LSB INSERT_DAFQ: .JSB_ENTRY INPUT=, SCRATCH=, OUTPUT=,- PRESERVE= MOVL SPDT$PS_AB(R4),R5 ; Get the Adapter Block address MOVL AB$PQ_DAFQ_TAIL(R5),R1 ; Get the DAFQ tail ptr .Disable Flagging MOVQ QBUF$PQ_PA_QBUF(R7),- ; Link Q_Buffer phys. address CAR$PQ_QBUF_PTR(R1) ; into stopper Carrier MOVL R7,CAR$PQ_QBUF_TOKEN(R1) ; Copy Q_Buffer virtual address MOVL R1,QBUF$PS_CARRIER(R7) ; Save new carrier's address ; in Q_Buffer EVAX_MB ; Make sure Q_Buffer contents ; and pointer are visible to ; the adapter before NEXT_PTR ; becomes valid EVAX_ADDQ #1,CAR$PQ_PA(R2),- ; Link in new Stopper and CAR$PQ_NEXT_PTR(R1) ; make it a valid Carrier .Enable Flagging CLRB CAR$IB_FUNC_CODE(R1) ; Clear CAM function code CLRB CAR$IB_STATUS(R1) ; Clear CAM status CLRW CAR$IW_FLAGS(R1) ; Clear CAM flags EVAX_MB ; Make sure new Stopper is ; visible to adapter before ; adapter accesses DAFQ MOVL R2,AB$PQ_DAFQ_TAIL(R5) ; Update tail with new Stopper ; ; We must notify the adapter of the new entry on the DAFQ. Write the ; physical address of the Stopper Carrier (shifted right 3 bits) on the ; DAFQ to the Driver Adapter Free Queue Insertion Register (DAFQIR). ; MOVL AB$PQ_DAFQ_TAIL(R5),R2 ; VA of DAFQ Stopper Carrier EVAX_LDQ R7,CAR$PQ_PA(R2) ; PA of DAFQ Stopper Carrier EVAX_SRL R7,#TZA_DAFQIR$C_PA_SHIFT,R7 ; Shift PA right 3 bits DEVICELOCK - ; Lock device access LOCKADDR = SPDT$L_DLCK(R4),- LOCKIPL = SPDT$B_DIPL(R4),- SAVIPL = -(SP),- PRESERVE = NO WRITE_CSR REG = DAFQIR,- ; Give DAFQ Stopper PA to adap DATA = R7,- TYPE = Q DEVICEUNLOCK - ; Unlock device access LOCKADDR = SPDT$L_DLCK(R4),- NEWIPL = (SP)+,- PRESERVE = NO MOVZWL #SS$_NORMAL,R0 ; Success return status RSB ; Return .DISABLE LSB ; INSERT_DAFQ .PAGE .SBTTL SETUP_ADFQ - Set up Adapter Driver Free Queue entries ; ; This subroutine is called to set up free queue entries in the ADFQ ; (Adapter Driver Free Queue). It is called from the REINIT_PORT routine after ; the adapter/channel has transitioned to the ENABLED state. ; ; It allocates the required Queue Buffers and Carriers for the ADFQ. ; ; Note: The virtual address of a Carrier points to the start of the ; Carrier. This allows the Carrier offsets to be correct. ; The physical address of a Carrier points to the start of the ; adapter visible section of the Carrier at offset CAR$PQ_NEXT_PTR ; (which is not the beginning of the Carrier). ; ; This routine assumes that it is running in a Kernel Process context. ; ; INPUT: ; R4 = SPDT address ; R8 = KPB address ; ; OUTPUT: ; R0 = SS$_NORMAL Status ; .ENABLE LSB SETUP_ADFQ: .JSB_ENTRY INPUT=, SCRATCH=,- OUTPUT=, PRESERVE= MOVL SPDT$PS_AB(R4),R9 ; Get the Adapter Block address MOVL AB$PQ_ADFQ_HEAD(R9),R2 ; Point to Stopper Carrier MOVL #NUM_ADFQ_ENTRIES,R10 ; Number of entries to insert 10$: MOVL R2,R3 ; Save next Carrier address ; ; Allocate some pool for a buffer and zero the block. ; 15$: SUBL #8,SP ; Allocate two lw on stack MOVL SP,R2 ; Store address of lw to receive ; address of allocated block ADDL3 #4,R2,R6 ; Store address of lw to receive ; size of allocated block PUSHL R6 ; Arg 4 = addr to receive size PUSHL R2 ; Arg 3 = addr to receive addr ; of allocated block PUSHL #ALN_LONG ; Arg 2 = 4 byte alignment PUSHL #256 ; Arg 1 = size of block CALLS #4,EXE$ALONONPAGED_ALN ; Allocate a block POPL R2 ; Get addr of allocated block POPL R6 ; Get allocated size BLBS R0,20$ ; Did we get the block? KP_STALL_FORK_WAIT KPB=R8,- ; No, fork and wait FKB=KPB$PS_UCB(R8) ; using UCB fork block BRB 15$ ; Try allocation again 20$: PUSHR #^M ; Save registers MOVC5 #0,(SP),#0,R6,(R2) ; Zero initialize the block POPR #^M ; Restore registers ; ; Initialize static fields in the Queue Buffer. R2 points to Queue Buffer. ; MOVW R6,QBUF$IW_SIZE(R2) ; Save Queue Buffer size MOVB #DYN$C_MISC,QBUF$IB_TYPE(R2) ; Set buffer type MOVB #DYN$C_QBUF,QBUF$IB_SUBTYPE(R2) ; Set buffer subtype MOVAB QBUF$PQ_CCB_ADDR(R2),R6 ; VA of adapter visible section $SVA_TO_PA SVA = R6 ; PA of adapter visible section BITL #SPDT$M_PFLG_MAPPING_REG,- ; Are we using map regs? SPDT$L_PORT_FLAGS(R4) BEQL 30$ ; Nope; just use PA. ; ; Is the Queue Buffer within the direct-DMA window? ; EVAX_ADDQ #QBUF$C_ADPLEN,R0,R0 ; Yes, just use PA EVAX_CMPULE R0,SPDT$IQ_DMA_SIZE(R4),R7 ; Compare to DMA window size BLBS R7,30$ ; Branch if LE window size ; ; O.K., we hafta map it. ; MAP_S0 - SVA = R6,- ; Queue Buffer address (VA). NBYTES = #QBUF$C_ADPLEN,- ; Size to map. CRCTX = QBUF$PS_CRCTX_PTR(R2),- ; CRCTX pointer reference. KPB$ = SPDT$PS_KPB(R4),- ; KPB to stall on alloc fail DMA_ADDR = QBUF$PQ_PA_QBUF(R2) ; Where to put DMA-able addr BRB 40$ ; ; No mapping registers needed, just use PA (+ DMA base). ; 30$: ADDL2 SPDT$L_DMA_BASE(R4),R0 ; Add in DMA window base EVAX_ZAP R0,#^XF0,R0 ; Clear the upper longword EVAX_STQ R0,QBUF$PQ_PA_QBUF(R2) ; Save physical address 40$: MOVL R2,R7 ; Save the Queue Buffer address BSBW GET_QUEUE_CARRIER ; Get a Carrier .Disable Flagging MOVL R7,CAR$PQ_QBUF_PTR(R2) ; Get Queue Buffer VA MOVL R7,CAR$PQ_QBUF_TOKEN(R2) ; Copy Q_Buffer virtual address MOVL R2,QBUF$PS_CARRIER(R7) ; Save new Carrier's address ; in Queue Buffer EVAX_MB ; Make sure Q_Buffer contents ; and pointer are visible to ; the adapter before NEXT_PTR ; becomes valid MOVL R3,CAR$PQ_NEXT_PTR(R2) ; Link in new Carrier .Enable Flagging CLRB CAR$IB_FUNC_CODE(R2) ; Clear CAM function code CLRB CAR$IB_STATUS(R2) ; Clear CAM status CLRW CAR$IW_FLAGS(R2) ; Clear CAM flags MOVL R2,AB$PQ_ADFQ_HEAD(R9) ; Point head to inserted Carrier EVAX_MB ; Make sure new Stopper is ; visible to adapter before ; adapter accesses ADFQ SOBGTR R10,10$ ; Loop for all ADFQ entries MOVZWL #SS$_NORMAL,R0 ; Success return status RSB ; Return .DISABLE LSB ; SETUP_ADFQ .PAGE .SBTTL INSERT_DACQ - Insert a command on the DACQ ; ; This subroutine is called to insert a command to the port's DACQ. ; It performs the following: ; ; 1.) Sets up pointers in the current Stopper Carrier to point to the ; Queue Buffer. ; 2.) Makes the Carrier that was just allocated previous to this call a new ; Stopper. ; 3.) Stores the virtual address of the new Stopper Carrier in the Q_Buffer ; to facilitate the recovery of Buffers during adapter reinitialization. ; ; Note that this routine can be called only for those I/O threads that need ; not be stalled waiting for the response. ; ; INPUT: ; R2 = Allocated Carrier address ; R4 = SPDT address ; R7 = Allocated Queue Buffer address ; IPL$_IOLOCK8 ; ; OUTPUT: ; R1 = Address of Carrier pointing to Queue Buffer ; Allocated Carrier inserted onto the DACQ. ; AB$PQ_DACQ_TAIL Updated with new Stopper Carrier VA ; INSERT_DACQ: .JSB_ENTRY INPUT=, OUTPUT=,- SCRATCH=,PRESERVE= MOVL SPDT$PS_AB(R4),R6 ; Get Adapter Block address MOVL AB$PQ_DACQ_TAIL(R6),R1 ; Get DACQ tail ptr. (Stopper) MOVL R1,SPDT$IS_CAR_ADDR(R4) ; Debug, Carrier address MOVL R2,SPDT$IS_STOPPER_ADDR(R4) ; Debug, Stopper address .Disable Flagging MOVQ QBUF$PQ_PA_QBUF(R7),- ; Link Q_Buffer physical addr CAR$PQ_QBUF_PTR(R1) ; into Stopper Carrier MOVL R7,CAR$PQ_QBUF_TOKEN(R1) ; Copy Q_Buffer virtual address EVAX_MB ; Make sure Q_Buf contents and MOVL R1,QBUF$PS_CARRIER(R7) ; Save Carrier's address ; in Queue Buffer EVAX_MB ; Make sure Q_Buf contents and ; ptr are visible to the ; adapter before NEXT_PTR ; becomes valid CLRQ CAR$PQ_NEXT_PTR(R2) ; Clear old NEXT_PTR .Enable Flagging MOVB QBUF$IB_FUNC_CODE(R7),- ; Fill in function code CAR$IB_FUNC_CODE(R1) MOVB QBUF$IB_PATH_ID(R7),- ; Setup path id CAR$IB_PATH_ID(R1) MOVB QBUF$IB_TARGET_ID(R7),- ; Setup target id CAR$IB_TARGET_ID(R1) MOVB QBUF$IB_LUN(R7),- ; Setup LUN CAR$IB_LUN(R1) EVAX_MB ; Make sure new Stopper is ; visible to the adapter ; before adapter accesses DACQ MOVL R2,AB$PQ_DACQ_TAIL(R6) ; Update AB DACQ tail pointer ; with new Stopper VA EVAX_ADDQ #1,CAR$PQ_PA(R2),- ; Link new Stopper and make CAR$PQ_NEXT_PTR(R1) ; it a valid Carrier EVAX_MB ; Make sure tail ptr is visible RSB ; Return .DISABLE LSB ; INSERT_DACQ .PAGE .SBTTL REMOVE_ADRQ - Remove a command from the ADRQ ; ; This subroutine is called to remove a command from the port's ADRQ. ; It performs the following: ; ; 1.) Removes a Carrier from the ADRQ head. ; 2.) Dequeues the Queue Buffer from the previous link. ; 3.) Updates the ADRQ head pointer with the next Carrier address. ; ; INPUT: ; R4 = SPDT address ; IPL$_IOLOCK8 ; ; OUTPUT: ; R2 = Address of Carrier removed from ADRQ ; .ENABLE LSB REMOVE_ADRQ: .JSB_ENTRY INPUT=,OUTPUT=,SCRATCH=,- PRESERVE= MOVL SPDT$PS_AB(R4),R6 ; Get Adapter Block address MOVL AB$PQ_ADRQ_HEAD(R6),R2 ; Get Carrier at ADRQ head MOVL CAR$PQ_QBUF_PTR(R2),R7 ; Get Q_Buffer virtual address ; written by the adapter EVAX_MB ; Make sure Q_Buf contents MOVL CAR$PQ_NEXT_PTR(R2),- ; Update ADRQ head pointer AB$PQ_ADRQ_HEAD(R6) ; with next Carrier address EVAX_MB ; Make sure Q_Buf contents RSB ; Return .DISABLE LSB ; REMOVE_ADRQ .PAGE .SBTTL GET_QUEUE_BUFFER - Get a Q_Buffer ; ; This subroutine is called whenever a Q_Buffer is requested. For all ; commands, a 512 byte long Q_Buffer is allocated. The first 40 bytes ; in the allocated block is used internally by PKSDRIVER and is not visible ; to the adapter. The next 24 bytes contain the CDB built by the class driver ; and is not visible to the adapter. The next 192 bytes are the adapter ; visible section and contain the CCB built by PKSDRIVER. The last 256 bytes ; are the autosense buffer. ; ; All Q_Buffers are 64-byte aligned and do not cross a page (8KB) boundary. ; ; Note: This routine assumes that it is running in a Kernel Process context. ; ; INPUT: ; R4 = SPDT address ; R5 = SCDRP address ; R8 = KPB address ; IPL$_IOLOCK8 ; ; OUTPUT: ; R2 = Address of the allocated Q_Buffer ; .ENABLE LSB GET_QUEUE_BUFFER: .JSB_ENTRY INPUT=,OUTPUT=, - PRESERVE= PUSHL R5 ; SCDRP address PUSHL R4 ; SPDT address CALLS #2,@SPDT$PS_RL_CMD_SLOT_ALLOC(R4) ; Allocate command buffer slot BLBC R0,60$ ; Did we get a buffer slot? ASHL #9,SCDRP$IS_CMD_SLOT(R5),R2 ; Yes, multiply slot num by 512 ; to get offset ADDL2 SPDT$PS_CMD_BASE(R4),R2 ; Calculate addr of cmd buffer .IF DEFINED PKSHISTORY MOVL SPDT$IS_BUFFER_PTR(R4),R9 MOVL SCDRP$IS_CMD_SLOT(R5),(R9)+ MOVL R2,(R9)+ MOVL R9,SPDT$IS_BUFFER_PTR(R4) .ENDC ; ; Zero the queue buffer, saving and restoring the CRCTX pointer. ; PUSHL QBUF$PS_CRCTX_PTR(R2) PUSHR #^M ; Save registers MOVC5 #0,.,#0,#QBUF$C_LENGTH,(R2) ; Zero initialize the block POPR #^M ; Restore registers POPL QBUF$PS_CRCTX_PTR(R2) ; ; Initialize static fields in the Queue Buffer. R2 points to Queue Buffer. ; MOVW #QUEUE_BUFFER_SIZE,- ; Save Queue Buffer size QBUF$IW_SIZE(R2) MOVB #DYN$C_MISC,QBUF$IB_TYPE(R2) ; Set buffer type MOVB #DYN$C_QBUF,QBUF$IB_SUBTYPE(R2) ; Set buffer subtype ; ; Do we need map registers to map this Queue Buffer? ; MOVAB QBUF$PQ_CCB_ADDR(R2),R6 ; VA of adapter visible section $SVA_TO_PA SVA = R6 ; PA of adapter visible section BITL #SPDT$M_PFLG_MAPPING_REG,- ; Are we using map regs? SPDT$L_PORT_FLAGS(R4) BEQL 40$ ; Nope, just use PA. ; ; Is this Queue Buffer within the direct-DMA window? ; EVAX_ADDQ #QBUF$C_ADPLEN,R0,R3 ; Yes, just use PA EVAX_CMPULE R3,SPDT$IQ_DMA_SIZE(R4),R7 ; Compare to DMA window size BLBS R7,40$ ; Branch if LE window size ; ; O.K., we have to map it. ; MOVAB QBUF$PQ_CCB_ADDR(R2),R3 MAP_S0 - SVA = R3,- NBYTES = #QBUF$C_ADPLEN,- CRCTX = QBUF$PS_CRCTX_PTR(R2),- KPB$ = R8,- DMA_ADDR = QBUF$PQ_PA_QBUF(R2) BRB 50$ ; ; We come here if we're not using map registers, or if the Queue Buffer is ; entirely within the direct-DMA window. In either case, we can simply ; use the physical address as the DMA-able address. ; 40$: ADDL2 SPDT$L_DMA_BASE(R4),R0 ; Add in DMA window base EVAX_ZAP R0,#^XF0,R0 ; Clear the upper longword EVAX_STQ R0,QBUF$PQ_PA_QBUF(R2) ; Save physical address 50$: RSB ; Return with address in R2 60$: CLRL R2 ; Indicate no buffer allocated BRW 50$ ; Return error .DISABLE LSB ; GET_QUEUE_BUFFER .PAGE .SBTTL RET_QUEUE_BUFFER - Return a Queue Buffer ; ; This routine is called to return a Queue Buffer to non-paged pool ; or the Driver Adapter Free Queue (DAFQ). All queue buffers with ; a function code of execute SCSI I/O are returned to non-paged pool. ; All others are returned to the DAFQ. ; ; INPUT: ; R4 - SPDT address. ; R5 - SCDRP address (If Queue Buffer from Execute SCSI I/O request). ; IPL$_IOLOCK8 ; ; OUTPUT: ; Queue Buffer returned to pool or DAFQ. ; .ENABLE LSB RET_QUEUE_BUFFER: .JSB_ENTRY INPUT=,SCRATCH=, - PRESERVE= ; ; Find the address of the Queue Buffer. ; ASHL #9,SCDRP$IS_CMD_SLOT(R5),R2 ; Yes, multiply slot num by 512 ; to get offset ADDL2 SPDT$PS_CMD_BASE(R4),R2 ; Calculate addr of cmd buffer ; ; Does this buffer have a CRCTX associated? If so, deallocate the map ; registers associated therewith. ; MOVL QBUF$PS_CRCTX_PTR(R2),R0 ; Fetch the CRCTX ptr BEQL 10$ PUSHL R0 ; Yup. Push the CRCTX addr. PUSHL SPDT$PS_CRAB(R4) ; ...and the CRAB addr. CALLS #2,IOC$DEALLOC_CNT_RES ; And deallocate the regs. ; ; This Queue Buffer is from an Execute SCSI I/O request. Return the ; Queue Buffer to buffer free pool. ; 10$: PUSHL R5 ; SCDRP address PUSHL R4 ; SPDT address CALLS #2,@SPDT$PS_RL_CMD_SLOT_DEALLOC(R4) ; Deallocate buffer slot RSB ; Return .DISABLE LSB ; RET_QUEUE_BUFFER .PAGE .SBTTL GET_QUEUE_CARRIER - Get a Queue Carrier ; ; This subroutine is called whenever a Carrier is requested. A Carrier ; consists of 32 bytes of port driver specific fields followed by 40 bytes ; of adapter-visible fields. ; ; Queue Carriers are statically allocated at driver load time and inserted ; onto a doubly linked list pointed to by SPDT$PS_CARFL and SPDT$PS_CARBL. ; ; All Carriers must be 32-byte aligned. ; ; Note: This routine assumes that it is running in a Kernel Process context. ; ; INPUT: ; R4 = SPDT address ; SPDT$PS_AB = pointer to the Adapter Block ; R8 = KPB address to use for fork stalling ; IPL$_IOLOCK8 ; ; OUTPUT: ; R0 = SS$_NORMAL ; R2 = Address of the allocated Carrier ; .ENABLE LSB GET_QUEUE_CARRIER: .JSB_ENTRY INPUT=,OUTPUT=, - SCRATCH=,PRESERVE= ; ; Get the next Carrier from the Carrier free list in the SPDT. ; 10$: MOVAL SPDT$PS_CARFL(R4),R6 ; Get Carrier free list address REMOVE_HEAD - QUE = R6,- ; Carrier free list in SPDT OUT_EL = R2,- ; Carrier address EMPTY_ADDR = 30$,- ; Wait routine if list empty SCRATCH = R1 .IF DEFINED PKSHISTORY MOVL SPDT$IS_CARRIER_PTR(R4),R9 MOVL R2,(R9)+ MOVL R9,SPDT$IS_CARRIER_PTR(R4) .ENDC ; ; Do we need map registers to map this carrier? ; BITL #SPDT$M_PFLG_MAPPING_REG,- ; Are we using map regs? SPDT$L_PORT_FLAGS(R4) BEQL 20$ ; Nope; just use PA. ; ; Is this carrier within the direct-DMA window? ; MOVAB CAR$PQ_NEXT_PTR(R2),R3 $SVA_TO_PA SVA = R3 EVAX_ADDQ #CAR$C_ADPLEN,R0,R0 ; Yes, just use PA EVAX_CMPULE R0,SPDT$IQ_DMA_SIZE(R4),R7 ; Compare to DMA window size BLBS R7,20$ ; Branch if LE window size ; ; O.K., we have to map it. ; MAP_S0 - SVA = R3,- NBYTES = #CAR$C_ADPLEN,- CRCTX = CAR$PS_CRCTX(R2),- KPB$ = R8,- DMA_ADDR = CAR$PQ_PA(R2) 20$: MOVZWL #SS$_NORMAL,R0 ; Set success status RSB ; Return ; ; No Carriers available. Suspend this I/O request to wait for one to be ; returned. Routine RET_QUEUE_CARRIER will restart the kernel process when ; a Carrier is returned. ; 30$: INSERT_EL - EL = KPB$PS_FQFL(R8),- ; Insert KP at the end of QUE = @SPDT$PS_CARWQBL(R4),- ; Carrier wait queue in SPDT SCRATCH = R1 MOVL R4,KPB$PS_SCSI_PTR1(R8) ; Save SPDT address MOVL R5,KPB$PS_SCSI_SCDRP(R8) ; Save SCDRP address MOVAB B^IOC$RETURN,- ; Set stall routine KPB$PS_SCH_STALL_RTN(R8) CLRL KPB$PS_SCH_RESTRT_RTN(R8) ; No restart routine required PUSHL R8 ; Pass KPB address CALLS #1,G^EXE$KP_STALL_GENERAL ; Call the general KP staller ; ; This is the point at which the stalled KP resumes when a Carrier is returned. ; BRB 10$ ; Get the Carrier just returned .DISABLE LSB ; GET_QUEUE_CARRIER .PAGE .SBTTL RET_QUEUE_CARRIER - Return a Queue Carrier ; ; This subroutine is called to return a Carrier to the Carrier free list. ; It performs the following: ; ; 1.) Dequeues the Carrier from the linked list in the Adapter Block. ; 2.) Checks to see if there are any I/O requests waiting for a Carrier. ; If so, restart the Kernel Process passing back the Carrier address. ; 3.) If there are no I/O requests waiting for a Carrier, the Carrier is ; returned to the free list in the SPDT (SPDT$PS_CARBL). ; ; INPUT: ; R2 = Address of the Carrier to deallocate ; R4 = Address of SPDT ; IPL$_IOLOCK8 ; ; OUTPUT: ; Carrier returned to pool ; R1 destroyed ; .ENABLE LSB RET_QUEUE_CARRIER: .JSB_ENTRY INPUT=,SCRATCH=,- PRESERVE= ; ; Does this carrier have a CRCTX associated, and have map registers ; been allocated to it? If so, free them up. ; MOVL CAR$PS_CRCTX(R2),R0 ; Fetch CRCTX ptr. BEQL 5$ PUSHL R0 ; Yep. Push CRCTX addr. PUSHL SPDT$PS_CRAB(R4) ; ...and CRAB addr. CALLS #2,IOC$DEALLOC_CNT_RES ; Deallocate the map regs. ; ; Check to see if there are any I/O requests waiting for a Carrier. ; If so, restart the Kernel Process passing back the Carrier address. ; 5$: MOVAL SPDT$PS_CARWQFL(R4),R6 ; Address of Carrier wait queue REMOVE_HEAD - ; Get next KPB QUE = R6,- ; Carrier wait queue OUT_EL = R3,- EMPTY_ADDR = 10$,- ; Branch if no KPBs waiting SCRATCH = R1 MOVAB -KPB$PS_FQFL(R3),R3 ; Point to beginning of KPB MOVL KPB$PS_SCSI_PTR1(R3),R4 ; Get SPDT address saved in KPB MOVL KPB$PS_SCSI_SCDRP(R3),R5 ; Get SCDRP address saved in KPB PUSHL R2 ; Pass Carrier address PUSHL R3 ; Pass KPB address CALLS #2,G^EXE$KP_RESTART ; Resume the I/O thread that ; was waiting BRB 20$ ; Return ; ; There are no I/O requests waiting for a Carrier. Just return the Carrier ; to the free list. ; 10$: INSERT_EL - EL = CAR$PS_FLINK(R2),- ; Insert Carrier on linked QUE = @SPDT$PS_CARBL(R4),- ; list in SPDT SCRATCH = R1 20$: RSB ; Return .DISABLE LSB ; RET_QUEUE_CARRIER .PAGE .SBTTL CHECK_REV - Check adapter revision numbers ; ; This subroutine reads the SIMport SCSI adapter's hardware and firmware rev ; levels from the HW_REV register and checks them against the valid values in ; the port driver's device revision table. This routine will be called at each ; SIMport adapter configuration time. If the adapter's revision levels are ; greater than the maximum values in the revision table, this adapter will be ; considered as an experimental version and be accepted. Otherwise, if the ; matching revision levels cannot be found in the revision table, an error ; status is returned so that the adapter can be taken off-line. ; ; INPUT: ; None. ; ; OUTPUT: ; R0 - SS$_NORMAL, Revision levels valid ; SS$_CTRLERR, Revision levels not valid ; .ENABLE LSB CHECK_REV: .JSB_ENTRY OUTPUT= MOVZWL #SS$_NORMAL,R0 ; Set success status RSB ; Just return for now .DISABLE LSB ; CHECK_REV .PAGE .SBTTL CLEANUP_BUF - Clean up buffer resources ; ; This subroutine is called from the REINIT_PORT routine when an adapter ; miscellaneous error occurs and at powerfail recovery time as part of the ; adapter reinitialization process. ; ; This routine will recover as many Carriers and Q_Buffers as possible from the ; ADRQ, the DAFQ, the IN_PORT queue, and the IN_DEVICE queue. The recovered ; Carriers are returned to the pool while the Q_Buffers associated with ; currently stalled I/O requests are retained until the stalled I/O is resumed ; with SS$_MEDOFFLIN return status and the class driver calls the command buffer ; deallocation routine explicitly. ; ; Note that this routine assumes that it is running in a Kernel Process context. ; ; INPUT: ; R2 = ADP address ; R4 = SPDT address ; R5 = UCB address ; R8 = KPB address ; ; OUTPUT: ; None ; .ENABLE LSB CLEANUP_BUF: .JSB_ENTRY INPUT=, - SCRATCH=,PRESERVE= BISL #SPDT$M_PWF_CLNUP,- ; Set cleanup in progress flag SPDT$L_STS(R4) ; for channel MOVL SPDT$PS_AB(R4),R5 ; Get Adapter Block address BSBW LOOP_ADRQ ; Clean up ADRQ MOVL AB$PQ_ADRQ_HEAD(R5),R0 ; Get response queue head ptr BEQL 10$ ; Branch if empty and reset BSBW RET_QUEUE_CARRIER ; Return stopper Carrier in ADRQ CLRL AB$PQ_ADRQ_HEAD(R5) ; Clear the head ptr. to reset ; ; Now clean up all buffers in the free queues. ; 10$: MOVL AB$PQ_ADFQ_HEAD(R5),R0 ; Get ADFQ head BEQL 50$ ; Exit loop if ADFQ is reset BBS #0,CAR$PQ_NEXT_PTR(R0),40$ ; Exit loop if this is a stopper .Disable Flagging MOVQ CAR$PQ_NEXT_PTR(R0),- ; Update ADFQ head pointer AB$PQ_ADFQ_HEAD(R5) ; with next Carrier address .Enable Flagging MOVL CAR$PQ_QBUF_PTR(R0),R6 ; Get Q_Buffer virtual address ; written by the adapter BSBW RET_QUEUE_CARRIER ; Return the Carrier MOVL R6,R0 ; Get the Q_Buffer address BSBW RET_QUEUE_BUFFER ; Return this free Q_Buffer BRB 10$ ; Check next free entry in ADFQ 40$: BSBW RET_QUEUE_CARRIER ; Return last Carrier in ADFQ CLRL AB$PQ_ADFQ_HEAD(R5) ; Clear the head ptr. to reset ; ; Clean up DAFQ entries. ; 50$: MOVAL SPDT$PS_DAFQFL(R4),R7 ; Get address of free queue hdr 52$: MOVL QBUF$PS_CARRIER(R6),R0 ; Get Carrier address BSBW RET_QUEUE_CARRIER ; Return associated Carrier MOVL R6,R0 ; Get the Q_Buffer address BSBW RET_QUEUE_BUFFER ; Return this Q_Buffer BRB 52$ ; Go check next free Q_Buffer ; ; Clean up any remaining Carriers allocated for this adapter. Note that the ; Carriers pointed to by the DCCQx and DAFQ head pointers are returned to the ; pool via this method. ; 55$: MOVAL AB$PS_CAR_FLINK(R5),R7 ; Get addr of Carrier que hdr 65$: REMOVE_HEAD - ; Get address of the next QUE = R7,- ; Carrier OUT_EL = R0,- EMPTY_ADDR = 70$,- ; Branch if no more entries SCRATCH = R1 MOVL R0,R2 MOVL CAR$PS_CRCTX(R0),R7 ; Have we allocated a CRCTX? BEQL 69$ PUSHL R7 ; Yep, deallocate the regs. PUSHL SPDT$PS_CRAB(R4) CALLS #2,G^IOC$DEALLOC_CNT_RES PUSHL R7 CALLS #1,G^IOC$DEALLOC_CRCTX ; And deallocate the CRCTX 69$: MOVL R2,R0 MOVZWL CAR$IW_SIZE(R0),R1 ; Get size field JSB EXE$DEANONPGDSIZ ; Deallocate this Carrier BRB 65$ ; Get next Carrier in the queue 70$: BICL #SPDT$M_PWF_CLNUP,- ; Clear cleanup in progress flag SPDT$L_STS(R4) RSB ; Return .DISABLE LSB ; CLEANUP_BUF .PAGE .SBTTL LOOP_ADRQ - Dequeue entries from the ADRQ ; ; This routine dequeues each response entry pair (a Carrier and a Q_Buffer) ; from the ADRQ until the Stopper Carrier is found. For each response entry ; pair dequeued, the Carrier is returned to the pool while the Q_Buffer is ; queued to the channel's SPDT to be processed later. Once all the entries ; have been queued to the SPDT, routine PROCESS_RESPONSE is called to process ; the responses. ; ; This routine can also be called from the CLEANUP_BUF and ENABLE_CHNL routines ; as a part of the adapter/channel reinitialization after the adapter/channel ; crash or power failure. ; ; INPUT: ; R4 = SPDT address of Channel ; R5 = Adapter Block address ; .ENABLE LSB LOOP_ADRQ: .JSB_ENTRY INPUT=,SCRATCH=,- PRESERVE= 10$: MOVL AB$PQ_ADRQ_HEAD(R5),R0 ; Get ADRQ head pointer BEQL 60$ ; Done if empty BBS #VA$V_SYSTEM,R0,20$ ; Branch if VA BUG_CHECK INCONSTATE,FATAL ; We want to capture this 20$: BBC #0,CAR$PQ_NEXT_PTR(R0),50$ ; Exit loop if stopper .Disable Flagging MOVQ CAR$PQ_NEXT_PTR(R0),- ; Update ADRQ head pointer AB$PQ_ADRQ_HEAD(R5) ; with next Carrier address .Enable Flagging MOVL CAR$PQ_QBUF_PTR(R0),R6 ; Get Q_Buffer virtual address ; written by the adapter BBS #VA$V_SYSTEM,R6,30$ ; Branch if VA BUG_CHECK INCONSTATE,FATAL ; We want to capture this ; ; Map the returned CAM status in the Carrier to a VMS status code. ; 30$: MOVZBL CAR$IB_STATUS(R0),R1 ; Get returned CAM status ASHL #2,R1,R1 ; Make longword offset MOVAB CAM_STATUS_TABLE,R2 ; Point to beginning of table ADDL2 R1,R2 ; Point to VMS status code MOVL (R2),R1 ; Get VMS status code MOVL QBUF$PS_SCDRP(R6),R7 ; Get SCDRP address in Q_buffer MOVW R1,QBUF$IW_PORT_STS(R6) ; Setup VMS return status ; ; Return resources for this response. ; BSBW RET_QUEUE_CARRIER ; Return Carrier to pool BRW 10$ ; See if more responses ; ; A stopper Carrier was reached. All Carriers have been taken off of the ADRQ. ; 50$: MOVAL SPDT$PS_RSPFL(R4),R5 ; Get address of response queue MOVL (R5),R2 ; Get first entry in the queue CMPL R2,R5 ; Check for stopper carrier BEQL 60$ ; Is the response queue empty? ;; BSBW PROCESS_RESPONSE ; No, go process responses 60$: RSB ; Return .DISABLE LSB ; LOOP_ADRQ .PAGE .SBTTL ENABLE_CHNL - Clean up and re-enable the channel ; ; This subroutine is called either by the channel error handling routine, ; PROCESS_CHNL_ERR or the bus reset routine, RESET_SCSI_BUS. By the time ; this routine is called, the channel should be in the DISABLED state. ; ; The routine will clean up all buffer resources of the DISABLED channel, ; re-initialize the channel's DCCQ pointers, and re-enable the channel. ; ; INPUT: ; R4 = SPDT address ; R6 = KPB address ; ; OUTPUT: ; None ; .ENABLE LSB ENABLE_CHNL: .JSB_ENTRY INPUT=,- SCRATCH=,PRESERVE= BBS #SPDT$V_PWF_CLNUP,- ; Skip channel cleanup if in SPDT$L_STS(R4),200$ ; middle of adapter reinit BBSSI #SPDT$V_CHNL_CLNUP,- ; Set cleanup in progress after SPDT$L_STS(R4),200$ ; this channel's BUS RESET ; If bit already set, someone ; already started this process MOVL SPDT$PS_AB(R4),R5 ; Get Adapter Block address BSBW LOOP_ADRQ ; Check ADRQ and take care of ; any returned Q_Buffers from ; the disabled channel ; ; Now clean up the Carriers pointed to by the DCCQx Head_Ptrs. The Head_Ptr ; fields of the DACQ queues in the Adapter Block should have been updated by ; the SIMport FW by this time. These will be in the physical address format, ; so we need to go through the linked list of Carriers to find out the ; matching Carriers. Once we find the matching Carrier, instead of returning ; the Carrier to the pool and then reallocating another one as a new stopper ; Carrier, we re-use the one we found. ; MOVAB AB$PQ_DACQ_HEAD(R5),R3 ; Get DACQ head pointer 35$: EVAX_LDQ R1,(R3) ; Get DACQ head pointer contents MOVAL AB$PS_CAR_FLINK(R5),R7 ; Get queue header for ; allocated Carriers MOVL R7,R2 ; Copy for reference 40$: MOVL (R2),R2 ; Get next Carrier in the queue CMPL R2,R7 ; End of the queue? BEQL 50$ ; Yes, the Head_Ptr might not ; have a valid Carrier addr, ; so just lose this one EVAX_CMPEQ R1,CAR$PQ_PA(R2),R0 ; Is this the matching Carrier? BLBC R0,40$ ; Check next Carrier if not .Disable Flagging ; Turn off info, it's ALIGNED CLRQ CAR$PQ_NEXT_PTR(R2) ; Clear fields to reinit Carrier CLRQ CAR$PQ_QBUF_PTR(R2) CLRQ CAR$PQ_QBUF_TOKEN(R2) .Enable Flagging BRB 60$ ; Go join common code ; ; We need to allocate a new Stopper Carrier since firmware didn't return us ; a valid Carrier in the Head pointer. ; 50$: MOVL R6,R8 ; Setup KPB BSBW GET_QUEUE_CARRIER ; Get a Carrier 60$: MOVL R2,8(R3) ; Save tail pointer (VA) .Disable Flagging ; Turn off info, it's aligned MOVQ CAR$PQ_PA(R2),(R3) ; Store stopper Carrier PA in ; DACQ queue head .Enable Flagging CMPL R10,#1 ; Did we write to DACQ head? BEQL 70$ ; Yes, skip next step then EVAX_LDQ R0,CAR$PQ_PA(R2) ; Get the Stopper address ;; EVAX_SRL R0,#CQIB$V_SHIFT,R0 ; Start with PA bit <5> ;; EVAX_BIC R0,#CQIB$M_SBZ,R0 ; Clear bits to get PA <39:5> EVAX_STQ R0,(R9) ; Write Stopper address ADDL #8,R9 ; Point to next CCQxIR field ADDL #16,R3 ; Point to next DACQ Head Ptr SOBGTR R10,35$ ; Go process next DCCQ Head Ptr ; ; Now try re-enabling the channel. ; 70$: BICL #SPDT$M_CHNL_CLNUP,- ; Clear channel cleanup in SPDT$L_STS(R4) ; progress flag KP_START KPB = R8, - ; Call Kernel Process routine REGISTERS = #PKS_KP_REGMSK, - ; to reinit the port ROUTINE = REINIT_PORT ; ; Clear the port counters area to reset the counters and set up the timer to ; call READ_COUNTER routine. ; MOVL SPDT$PS_COUNTERS(R4),R1 ; Get the port counter address PUSHR #^M ; Save registers MOVC5 #0,(SP),#0,- ; Clear the counters #AB$C_PORT_CNTRS_SIZE,(R1) POPR #^M ; Restore registers MOVL UCB$L_CRB(R5),R0 ; Get CRB address of this port ADDL3 #READCNTR_INTERVAL,- ; Set READ_COUNTER wakeup G^EXE$GL_ABSTIM,- ; and save the timeout value CRB$L_DUETIME(R0) 190$: BICL #SPDT$M_CHNL_CLNUP,- ; Clear channel cleanup in SPDT$L_STS(R4) ; progress flag 200$: RSB ; Return .DISABLE LSB ; ENABLE_CHNL .PAGE .SBTTL PORT_SHUTDOWN - Shut down the Port ; ; This subroutine is called to shut down the channel permanently until ; the next system boot. ; ; INPUT: ; R4 = SPDT address ; R5 = UCB address ; ; OUTPUT: ; None ; .ENABLE LSB PORT_SHUTDOWN: .JSB_ENTRY INPUT=,- SCRATCH=, PRESERVE= ; ; By setting the FAILED bit and not setting the port ONLINE bit we will prevent ; connections from being established or commands being sent using this port. ; BISL #SPDT$M_STS_FAILED,- ; Remember that it is dead SPDT$L_STS(R4) ; ; Set all the SCDTs belonging to this port to be in CLOSED state ; (SCDT$C_STATE_CLOSED) so that no further send request can be processed. ; CLRL R3 ; Init. the target ID CLRL R1 ; Init the LUN MOVZWL SPDT$IW_CONFIG_BUS_WIDTH(R4),R7 ; Set maximum ID to check 30$: ; Loop for 64 SCDT vectors FIND_STDT_VECTOR - ; Find STDT address needed TARGET_ID = R3, - ; by FIND_SCDT_VECTOR STDT_ADDR = R6, - SPDT = R4, - STDT = R0 TSTL R0 ; Got STDT if = 0 try next ID BEQL 45$ 35$: FIND_SCDT_VECTOR - ; Translate the LUN to the LUN = R1, - STDT = R0, - ; address of the SCDT entry SCDT_ADDR = R6, - SCDT = R10 TSTL R10 ; Got SCDT if = 0 try next LUN BEQL 40$ ; Branch if no MOVW #SCDT$C_STATE_CLOSED, - ; Set the state to CLOSED SCDT$W_STATE(R10) 40$: AOBLSS #8,R1,35$ ; Try next LUN for the same target ID 45$: CLRL R1 ; Reset the LUN to 0 AOBLSS R7,R3,30$ ; Try next target's LUN 0 MOVL UCB$L_CRB(R5),R0 ; Get CRB address of this port MNEGL #1,CRB$L_DUETIME(R0) ; Disable further wakeups of READ_COUNTER ; ; Print PORT OFFLINE message to operator's terminal. ; .IF DEFINED PKSDEBUG MOVAB PORT_OFF_MSG,R2 ; Get addr of msg to output MOVZBL (R2)+,R1 ; Get message size and address MOVL UCB$L_DDB(R5),R0 ; Get DDB addr in R0 MOVB DDB$T_NAME+3(R0),- ; Copy device controller CTRLR_LETTER(R2) ; letter from DDB to ASCII msg MOVL G^OPA$AR_UCB0,R5 ; Get console terminal UCB address JSB G^IOC$BROADCAST ; Send the message .ENDC ; ; Do we want to error log this? ; ; LOG_ERROR - ; Log error in error log ; TYPE = CTL_ERR,- ; CDT = #0 RSB ; Return .DISABLE LSB ; PORT_SHUTDOWN .PAGE .SBTTL READ_COUNTER - Read port counters ; ; This routine is invoked by the EXE$TIMEOUT routine (in TIMESCHDL.MAR) through ; the CRB timeout mechanism to issue a RDCNT command to the port. The RDCNT ; response contains the cumulative port counters values that the SIMport ; kept since the time the port was enabled or the last time we asked to clear ; all counters. We will ask the port to clear all counters if the last counter ; response contained any counter that overflowed (e.g. set to FFFFFFFF). ; ; This routine allocates a Queue Buffer and a Carrier to queue this request and ; then suspends itself until the response is returned. If the port is not in ; the ENABLED state at the time of the call to this routine, no command is ; issued to the port since port is not processing the commands. ; ; Once the response is returned, the port counter values read will be stored as ; cumulative counts kept by the port driver. ; ; Note that this routine allocates a temporary KPB, and switches itself to be ; a Kernel Process routine in order to wait for the response from the port. ; ; PARANOID WARNING: If the number, the type, or the order of counters returned ; by the port is ever changed, this routine must be modifed ; accordingly. ; ; INPUT: ; R3 = Address of the port's CRB ; ; OUTPUT: ; None ; .ENABLE LSB READ_COUNTER: .JSB_ENTRY INPUT=, SCRATCH=,- PRESERVE= ; ; If the port is not online or there is currently a Read Counter KPB running, ; then just return. ; MOVL CRB$L_SCS_STRUC(R3),R4 ; Get SPDT address of the port BBC #SPDT$V_STS_ONLINE,- ; Exit without resetting due SPDT$L_STS(R4),- ; time if port is not ENABLED 60$ TSTL SPDT$PS_RDCNT_KPB(R4) ; Is there a RDCNT KP running? BNEQ 60$ ; Branch and exit if yes ; ; Allocate a KPB and start the Kernel process. ; KP_ALLOCATE_KPB - ; Allocate Kernel Process block KPB = SPDT$PS_RDCNT_KPB(R4),- STACK = G^MMG$GL_PAGE_SIZE,- ; Set the stack size FLAGS = #KP$M_IO ; VEST, dealloc when done BLBC R0,50$ ; Branch and exit if no KPB MOVL SPDT$PS_RDCNT_KPB(R4),R8 ; Copy KPB pointer MOVL R4,KPB$PS_SCSI_PTR1(R8) ; Save SPDT address in KPB MOVL R3,KPB$PS_SCSI_PTR2(R8) ; Save CRB address in KPB MOVL SPDT$L_PORT_UCB(R4),- ; Save UCB address in KPB KPB$PS_UCB(R8) KP_SWITCH_TO_KP_STACK - ; Start the Kernel Process KPB = R8,- ; KPB pointer REGISTERS = ; Save registers ; ; The remainder of this routine runs as a Kernel Process. It is CALLed as a ; procedure by the Kernel Process Services and will therefore terminate with a ; RET instruction. ; ; Get a Queue Buffer and build a Read Counters command. ; MOVL KPB$PS_SCSI_PTR1(R8),R4 ; Restore SPDT address BSBW GET_QUEUE_BUFFER ; Get a Queue Buffer MOVL R2,R7 ; Copy Queue Buffer pointer MOVL R8,QBUF$PS_KPB(R7) ; Save KPB address in Q_Buffer MOVQ QBUF$PQ_PA_QBUF(R7),- ; Setup PA of CCB QBUF$PQ_CCB_ADDR(R7) MOVW #QBUF$C_READ_COUNTERS_SIZE,- ; Setup CCB size QBUF$IW_CCB_LENGTH(R7) MOVB #QBUF$C_READ_CNTRS,- ; Set SIMport function code to QBUF$IB_FUNC_CODE(R7) ; Read Counters CLRB QBUF$IB_CAM_STS(R7) ; Clear CAM Status MOVB SPDT$IB_PI_PATH_ID(R4),- ; Setup path id QBUF$IB_PATH_ID(R7) BISB #QBUF$M_NO_DATA_TX,- ; Direction is no data QBUF$IB_CAM_FLAGS_1(R7) BISB #QBUF$M_SIM_Q_FREEZE_DIS,- ; Set SIM queue freeze disable QBUF$IB_CAM_FLAGS_2(R7) BBCC #SPDT$V_CTR_OVERFLOW,- ; Previous counter overflow? SPDT$IS_OVERFLOW(R4),10$ BISW #QBUF$M_CLR_COUNTERS,- ; Clear counters after read QBUF$IW_CTR_FLAGS(R7) ; ; Get a Carrrier and link the Carrier to the Queue Buffer and insert the ; Carrier onto the DACQ. ; 10$: BSBW GET_QUEUE_CARRIER ; Get a queue carrier MOVL R7,CAR$PQ_QBUF_TOKEN(R2) ; Copy Queue Buffer virtual addr MOVB QBUF$IB_FUNC_CODE(R7),- ; Put SIMport function code CAR$IB_FUNC_CODE(R2) ; into Carrier CLRB CAR$IB_STATUS(R2) ; Clear Status CLRW CAR$IW_FLAGS(R2) ; Flags CLRL CAR$IS_CONNECT_ID(R2) ; Connect id BSBW INSERT_DACQ ; Insert Carrier onto DACQ MOVL SPDT$L_DLCK(R4),KPB$PS_DLCK(R8) ; Copy device lock ptr to KPB EVAX_LDQ R9,CAR$PQ_PA(R2) ; Get PA of new Stopper Carrier EVAX_SRL R9,#TZA_DACQIR$C_PA_SHIFT,R9 ; Shift PA right 3 bits DEVICELOCK - ; Lock device access LOCKADDR = SPDT$L_DLCK(R4),- LOCKIPL = SPDT$B_DIPL(R4),- SAVIPL = -(SP),- PRESERVE = NO WRITE_CSR REG = DACQIR,- ; Notify adapter of an entry DATA = R9,- ; on the DACQ TYPE = L ; ; Now wait for the channel to return a response to this command by ; suspending this I/O request to wait for the command completion interrupt. ; MOVAB B^RDCNT_STALL, - ; Set stall routine address KPB$PS_SCH_STALL_RTN(R8) CLRL KPB$PS_SCH_RESTRT_RTN(R8) ; No restart routine required PUSHL R8 ; Pass KPB address CALLS #1,G^EXE$KP_STALL_GENERAL ; Call the general KP staller ; ; This is the point at which the stalled request resumes when the CNTRD ; response is returned. Copy the counters to the counters area in the AB. ; At this point: ; R4 = SPDT address ; R7 = Response Queue Buffer address ; R8 = KPB address ; PROCESS_COUNTERS: CMPB #CAR$C_REQ_COMP_NO_ERROR,- ; Check return status QBUF$IB_CAM_STS(R7) BNEQ 40$ ; Branch if the command failed CLRL SPDT$IS_OVERFLOW(R4) ; Clear overflowed counter flag MOVAL QBUF$IS_REQ_COUNTERS(R7),R3 ; Point to beginning of counters MOVL SPDT$PS_COUNTERS(R4),R1 ; Get addr of port counter area MOVL #QBUF$C_NUM_COUNTERS,R0 ; Loop for all counters 20$: CMPL #^XFFFFFFFF,(R3) ; Did this counter overflow? BNEQ 30$ ; Branch if no BISL #SPDT$M_CTR_OVERFLOW,- ; Set counter overflow flag SPDT$IS_OVERFLOW(R4) ; for next RDCNT cmd 30$: MOVL (R3)+,(R1)+ ; Copy the counter to the AB SOBGTR R0,20$ ; Loop for all counters ; ; Return resources and reschedule the next Read Counters command. ; 40$: MOVL R7,R0 ; Get the Queue Buffer address BSBW RET_QUEUE_BUFFER ; Return Queue Buffer to pool CLRL SPDT$PS_RDCNT_KPB(R4) ; Indicate that a new KPB can ; be allocated next time MOVL KPB$PS_SCSI_PTR2(R8),R3 ; Restore CRB address ADDL3 #READCNTR_INTERVAL,- ; Set READ_COUNTER wakeup in G^EXE$GL_ABSTIM,- ; 1 minute and save timeout CRB$L_DUETIME(R3) ; value in CRB RET ; Return and delete the KPB 50$: ADDL3 #READCNTR_INTERVAL,- ; Set READ_COUNTER wakeup in G^EXE$GL_ABSTIM,- ; 1 minute and save timeout CRB$L_DUETIME(R3) ; value in CRB 60$: RSB ; Return .DISABLE LSB ; READ_COUNTER .PAGE .SBTTL RDCNT_STALL - Read Counter stall routine ; ; RDCNT_STALL ; ; Do the DEVICEUNLOCK for the lock that was done in READ_COUNTER routine. ; ; DEVICEUNLOCK is done here so that the port cannot interrupt us until ; this thread is stalled properly. ; RDCNT_STALL: .CALL_ENTRY MOVL 4(AP),R1 ; Get KPB parameter MOVL KPB$PS_SCSI_PTR1(R1),R4 ; Get SPDT address from KPB DEVICEUNLOCK - ; Release device lock LOCKADDR = SPDT$L_DLCK(R4),- NEWIPL = KPB$IS_NEWIPL(R1),- PRESERVE = NO RET ; Return .PAGE .SBTTL SETUP_SANITY_TQE - Set up the TQE for Adapter Sanity ; ; This subroutine is called once per port at the end of the initialization ; of the port to set up a repeating TQE entry for checking adapter sanity. ; This routine makes sure that TQE is set up only once per port. The TQE ; timeout interval is set to 10 seconds. ; ; INPUTS: ; R4 = SPDT address ; IPL$_IOLOCK8 ; ; OUTPUTS: ; None ; ; R2 and R3 are destroyed by EXE$INSTIMQ routine. Hence we ; need to save R3 here. ; .ENABLE LSB SETUP_SANITY_TQE: .JSB_ENTRY INPUT=,SCRATCH=,PRESERVE= MOVAL SPDT$B_TQE(R4),R5 ; Get TQE address TSTB TQE$B_TYPE(R5) ; Is the TQE set up already? BNEQ 10$ ; Branch and exit if yes MOVB #DYN$C_TQE,- ; Set type TQE$B_TYPE(R5) MOVW #TQE$C_LENGTH,- ; Set length TQE$W_SIZE(R5) MOVB #TQE$C_SSREPT,- ; Make this a repeating TQE TQE$B_RQTYPE(R5) CLRL TQE$L_RMOD(R5) ; No AST required MOVAB B^ADP_SANITY_TIMEOUT,- ; Address of wakeup routine TQE$L_FPC(R5) .Disable Flagging ; Turn off info, it's aligned MOVQ #TQE_TIMER_EXPIRE,- TQE$Q_DELTA(R5) ; Compute next timeout (10sec) .Enable Flagging MOVX R4,TQE$Q_FR4(R5) ; Save SPDT address in TQE READ_SYSTIME R0 ; Get current time ADDL TQE$Q_DELTA(R5),R0 ; Compute next timeout (10sec) ADWC TQE$Q_DELTA+4(R5),R1 ; for insertion into queue JSB G^EXE$INSTIMQ ; Insert into timer queue 10$: RSB ; Return .DISABLE LSB ; SETUP_SANITY_TQE .PAGE .SBTTL ADP_SANITY_TIMEOUT - TQE routine to check Adapter Sanity ; ; This routine is called as a recurring system timer event (TQE) every ; TQE_TIMER_EXPIRE seconds. ; ; It traverses the port's I/O completion wait queue searching for any ; stalled I/O thread which timed out waiting for a response. If the port is ; hung for some reason and cannot even generate an error interrupt, the system ; can hang indefinitely unless we reset the port. Hence if this routine finds ; a timed out thread, it will start initiating the resetting of the port. ; This is accomplished by issuing a bus reset and then re-enabling the port. ; The timeout time for the I/O thread is set sufficiently large enough ; (currently 3 minutes) so that we will not encounter a case where the port ; was still functioning but we end up doing the reset. ; ; INPUTS: ; R4 = SPDT address ; R5 = Address of the TQE that causes this routine to execute ; ; OUTPUTS: ; None ; .ENABLE LSB ADP_SANITY_TIMEOUT: .JSB_ENTRY INPUT=,- SCRATCH=,- PRESERVE= ; ; Get the fork lock for the port. ; MOVL SPDT$IS_FLCK(R4),R5 ; Get fork lock index FORKLOCK - ; Lock it LOCK = R5, - ; (do not preserve R0) SAVIPL = -(SP), - PRESERVE = NO MOVAL SPDT$PS_WCMDFL(R4),R1 ; Get wait queue header address MOVL R1,R2 ; Copy for reference 10$: MOVL (R2),R2 ; Get next KPB in the queue CMPL R2,R1 ; End of the queue? BEQL 40$ ; Branch if yes MOVAB -KPB$PS_FQFL(R2),R3 ; Point to beginning of KPB MOVL KPB$PS_SCSI_SCDRP(R3),R5 ; Get SCDRP address in KPB CMPL SCDRP$L_DUETIME(R5),- ; Has the timeout time passed? G^EXE$GL_ABSTIM BGTRU 10$ ; No, check next I/O thread ; ; The port must be hung somehow since an I/O thread waited for more than 3 ; minutes. Try to do bus reset and re-enable the port. As part of the process, ; the stalled I/O threads will be resumed. Since RESET_SCSI_BUS needs to run ; in a Kernel Process context, we need to switch to a Kernel Process here. ; 20$: BBS #SPDT$V_PWF_CLNUP,- ; Skip resetting the bus if we are in the SPDT$L_STS(R4),40$ ; middle of adapter cleanup/reinit. BBS #SPDT$V_CHNL_CLNUP,- ; Skip resetting the bus if we already did SPDT$L_STS(R4),40$ ; and we are cleaning up channel resources TSTL SPDT$PS_BUSRESET_KPB(R4) ; Is there a reset KP running already? BNEQ 40$ ; Branch and exit if yes KP_ALLOCATE_KPB - ; Allocate a kernel process block KPB = SPDT$PS_BUSRESET_KPB(R4),- STACK = G^MMG$GL_PAGE_SIZE,- ; Set the stack size FLAGS = #KP$M_IO ; Use VEST format and deallocate ; the KPB when done BLBS R0,50$ ; Branch if KPB allocated CMPL #SS$_INSFRPGS,R0 ; Check for no free PFNs BEQL 30$ ; Yes, try waiting CMPL #SS$_INSFMEM,R0 ; Check for allocation failure BEQL 30$ ; Yes, try waiting BUG_CHECK INCONSTATE,FATAL ; Die since we couldn't allocate KPB ASSUME FKB$K_LENGTH EQ SPDT$C_BUSRESET_FKBLK ; Be sure FKB is big enough 30$: MOVAB SPDT$PS_BUSRESET_FKBLK(R4),R5 ; Fork on SPDT fork block MOVB #SPL$C_IOLOCK8,FKB$B_FLCK(R5) ; Set correct fork lock MOVL SPDT$IS_FLCK(R4),R6 ; Get fork lock index FORKUNLOCK - ; Unlock it LOCK = R6, - ; (do not preserve R0) NEWIPL = (SP)+, - PRESERVE = NO FORK_WAIT ; Fork wait MOVL SPDT$IS_FLCK(R4),R5 ; Get fork lock index FORKLOCK - ; Lock it LOCK = R5, - ; (do not preserve R0) SAVIPL = -(SP), - PRESERVE = NO .Disable Flagging ; Turn off information BRB 20$ ; Try allocation again .Enable Flagging ; Turn on information 40$: MOVL SPDT$IS_FLCK(R4),R5 ; Get fork lock index FORKUNLOCK - ; Unlock it LOCK = R5, - ; (do not preserve R0) NEWIPL = (SP)+, - PRESERVE = NO MOVAL SPDT$B_TQE(R4),R5 ; Restore TQE address RSB ; Return to TQE dispatcher 50$: MOVL SPDT$PS_BUSRESET_KPB(R4),R6 ; Get the KPB address in R6 MOVL R4,KPB$PS_SCSI_PTR1(R6) ; Save the SPDT address in KPB MOVL SPDT$L_PORT_UCB(R4),- ; Set this port's UCB addr in KPB$PS_UCB(R6) ; KPB ; ; Note that the following macro has to be expanded out here explicitly so ; that we can do FORKUNLOCK before RSBing back to the TQE dispatcher to ; avoid synchronization problem with the rest of the system. ; ; KP_SWITCH_TO_KP_STACK - ; Start the kernel process ; KPB = R6,- ; KPB pointer ; REGISTERS = ; Preserved registers ; ; KP_START R6,SWITCH_LOC,<#^M> ; Start the kernel process MOVL SPDT$IS_FLCK(R4),R5 ; Get fork lock index FORKUNLOCK - ; Unlock it LOCK = R5, - ; (do not preserve R0) NEWIPL = (SP)+, - PRESERVE = NO MOVAL SPDT$B_TQE(R4),R5 ; Restore TQE address RSB ; and return to TQE dispatcher SWITCH_LOC: .CALL_ENTRY PRESERVE= ; Embedded kernel process routine MOVL 4(AP),R6 ; Pick up pointer to KPB ; ; The remainder of this routine runs as a kernel process. It is CALLed as a ; procedure by the Kernel Process Services and will therefore terminate with a ; RET instruction. ; MOVL KPB$PS_SCSI_PTR1(R6),R4 ; Get SPDT address CLRL R3 ; No associated SCDT to pass MOVL SPDT$PS_KPB(R4),R8 ; Get init process KPB MOVL KPB$PS_UCB(R8),R5 ; Get port's UCB address BICL #UCB$M_ONLINE,UCB$L_STS(R5) ; Clear port UCB ONLINE MOVL R4,KPB$PS_SCSI_PTR1(R8) ; Store SPDT into KPB PUSHL KPB$PS_SCSI_SCDRP(R8) ; Save SCDRP from KPB BSBW GET_SCDRP ; Allocate the SCDRP MOVL R2,R5 ; Save the SCDRP MOVL R2,KPB$PS_SCSI_SCDRP(R8) ; Save SCDRP in KPB MOVL R8,SCDRP$PS_KPB(R5) ; Store KPB into SCDRP BISL #SPDT$M_INIT_ADAPTER,- ; Reinit the adapter SPDT$IS_PORTSTS(R4) BISL #SPDT$M_LOG_ERROR,- ; Log this in error log SPDT$IS_PORTSTS(R4) PUSHL R5 ; Pass SCDRP PUSHL R4 ; Pass SPDT CALLS #2,@SPDT$PS_CD_RESET_SCSI_BUS(R4) ; Reset SCSI bus MOVL R5,R0 ; Deallocate the SCDRP CALL_DRVDEALMEM SAVE_R0R1=NO ; COM_STD$DRVDEALMEM POPL KPB$PS_SCSI_SCDRP(R8) ; Clear the SCDRP pointer BISL #SPDT$M_STS_ONLINE,- ; Set SPDT online SPDT$L_STS(R4) CLRL SPDT$PS_BUSRESET_KPB(R4) ; Indicate that a new KPB can ; be allocated next time RET ; Return .DISABLE LSB ; TQE_TIMEOUT .END