patch-1.3.36 linux/drivers/scsi/aic7xxx.seq

Next file: linux/drivers/scsi/aic7xxx_asm.c
Previous file: linux/drivers/scsi/aic7xxx.h
Back to the patch index
Back to the overall index

diff -u --recursive --new-file v1.3.35/linux/drivers/scsi/aic7xxx.seq linux/drivers/scsi/aic7xxx.seq
@@ -25,9 +25,9 @@
 # optimizations provided by Justin T. Gibbs (gibbs@FreeBSD.org)
 ##-M#########################################################################
 
-VERSION AIC7XXX_SEQ_VER "$Id: aic7xxx.seq,v 2.0 1995/08/02 05:28:42 deang Exp $"
+VERSION AIC7XXX_SEQ_VER "$Id: aic7xxx.seq,v 2.1 1995/08/30 07:47:07 deang Exp $"
 
-SCBMASK		= 0x1f
+SCBMASK		= 0xff
 
 SCSISEQ		= 0x00
 ENRSELI		= 0x10
@@ -48,6 +48,7 @@
 SELDI		= 0x20
 CLRSINT1	= 0x0c
 SSTAT1		= 0x0c
+PHASEMIS	= 0x10
 SIMODE1		= 0x11
 SCSIBUSL	= 0x12
 SHADDR		= 0x14
@@ -83,18 +84,19 @@
 SCSICONF_B	= 0x5b
 
 #  The two reserved bytes at SCBARRAY+1[23] are expected to be set to
-#  zero, and the reserved bit in SCBARRAY+0 is used as an internal flag
-#  to indicate whether or not to reload scatter-gather parameters after
-#  a disconnect.  We also use bits 6 & 7 to indicate whether or not to
-#  initiate SDTR or WDTR repectively when starting this command.
+#  zero. Bit 3 in SCBARRAY+0 is used as an internal flag to indicate 
+#  whether or not to DMA an SCB from host ram. This flag prevents the
+#  "re-fetching" of transactions that are requed because the target is
+#  busy with another command. We also use bits 6 & 7 to indicate whether 
+#  or not to initiate SDTR or WDTR repectively when starting this command.
 #
 SCBARRAY+0	= 0xa0
 
 DISCONNECTED	= 0x04
 NEEDDMA		= 0x08
-SG_LOAD		= 0x10
+NEEDSDTR	= 0x10
 TAG_ENB		= 0x20
-NEEDSDTR	= 0x40
+DISCENB		= 0x40
 NEEDWDTR	= 0x80
 
 SCBARRAY+1	= 0xa1
@@ -144,6 +146,7 @@
 						# (command was null), so tell
 						# it that it can fill the
 						# message buffer.
+IMMEDDONE	= 0xb1
 
 
 #  The host adapter card (at least the BIOS) uses 20-2f for SCSI
@@ -153,11 +156,11 @@
 #  scratchspace (actually a value that can be copied directly into
 #  SCSIRATE).  The kernel driver will enable synchronous negotiation
 #  for all targets that have a value other than 0 in the lower four
-#  bits of the target scratch space.  This should work irregardless of
-#  whether the bios has been installed. NEEDWDTR and NEEDSDTR are the top
-#  two bits of the SCB control byte.  The kernel driver will set these
-#  when a WDTR or SDTR message should be sent to the target the SCB's 
-#  command references.
+#  bits of the target scratch space.  This should work regardless of
+#  whether the bios has been installed. NEEDSDTR and NEEDWDTR are the
+#  fouth and sevent bits of the SCB control byte.  The kernel driver 
+#  will set these when a WDTR or SDTR message should be sent to the 
+#  target the SCB's command references.
 #
 #  REJBYTE contains the first byte of a MESSAGE IN message, so the driver 
 #  can report an intelligible error if a message is rejected.
@@ -168,9 +171,9 @@
 #  no idea what the lun is, and we can't select the right SCB register
 #  bank, so force a kernel panic if the target attempts a data in/out or
 #  command phase instead of corrupting something.  FLAGS also contains
-#  configuration bits so that we can optimize for TWIN and WIDE controllers
-#  as well as the MAX_OFFSET bit which we set when we want to negotiate for
-#  maximum sync offset irregardless of what the per target scratch space says.
+#  configuration bits so that we can optimize for TWIN and WIDE controllers,
+#  the MAX_OFFSET bit which we set when we want to negotiate for maximum sync 
+#  offset irregardless of what the per target scratch space says.
 #
 #  Note that SG_NEXT occupies four bytes.
 #
@@ -198,13 +201,9 @@
 # Linux users should use 0xc (12) for SG_SIZEOF
 #SG_SIZEOF	= 0x8 				# sizeof(struct ahc_dma)
 SG_SIZEOF	= 0xc 				# sizeof(struct scatterlist)
-# if AIC7XXX_USE_SG
-SCB_SIZEOF	= 0x13				# sizeof SCB to DMA (19 bytes)
-# else
-#SCB_SIZEOF	= 0x1a				# sizeof SCB without SG
-# endif
+SCB_SIZEOF	= 0x1a				# sizeof SCB to DMA (26 bytes)
 
-SG_NOLOAD	= 0x4c				# load SG pointer/length?
+DMAPARAMS	= 0x4c				# Parameters for DMA
 SG_COUNT	= 0x4d				# working value of SG count
 SG_NEXT		= 0x4e				# working value of SG pointer
 SG_NEXT+0	= 0x4e
@@ -216,6 +215,7 @@
 FLAGS		= 0x53				# Device configuration flags
 TWIN_BUS	= 0x01
 WIDE_BUS	= 0x02
+DPHASE		= 0x04
 MAX_OFFSET	= 0x08
 ACTIVE_MSG	= 0x20
 IDENTIFY_SEEN	= 0x40
@@ -240,8 +240,8 @@
 # ram since a reconnecting target can request sense and this will create
 # yet another SCB waiting for selection.  The solution used here is to 
 # use byte 31 of the SCB as a psuedo-next pointer and to thread a list
-# of SCBs that are awaiting selection.  Since 0 is a valid SCB offset, 
-# SCB_LIST_NULL is 0x10 which is out of range.  The kernel driver must
+# of SCBs that are awaiting selection.  Since 0-0xfe are valid SCB offsets, 
+# SCB_LIST_NULL is 0xff which is out of range.  The kernel driver must
 # add an entry to this list everytime a request sense occurs.  The sequencer
 # will automatically consume the entries.
 
@@ -249,26 +249,22 @@
 						# selection
 WAITING_SCBT	= 0x58				# tail of list of SCBs awaiting
 						# selection
-SCB_LIST_NULL	= 0x10
+SCB_LIST_NULL	= 0xff
 
 
 #  Poll QINCNT for work - the lower bits contain
 #  the number of entries in the Queue In FIFO.
 #
-start:
-	test	WAITING_SCBH,SCB_LIST_NULL jz start_waiting
 poll_for_work:
 	test	FLAGS,TWIN_BUS	jz start2	# Are we a twin channel device?
 # For fairness, we check the other bus first, since we just finished a 
 # transaction on the current channel.
 	xor	SBLKCTL,0x08			# Toggle to the other bus
 	test	SSTAT0,SELDI	jnz reselect
-	test	SSTAT0,SELDO	jnz select
 	xor	SBLKCTL,0x08			# Toggle to the original bus
 start2:
 	test	SSTAT0,SELDI	jnz reselect
-	test	SSTAT0,SELDO	jnz select
-	test	WAITING_SCBH,SCB_LIST_NULL jz start_waiting
+	cmp	WAITING_SCBH,SCB_LIST_NULL jne start_waiting
 	test	QINCNT,SCBMASK	jz poll_for_work
 
 # We have at least one queued SCB now and we don't have any 
@@ -300,15 +296,10 @@
 # Copy the SCB from the FIFO to  the SCBARRAY
 
 	mvi	DINDEX, SCBARRAY+0
-	call	bcopy_3_dfdat 
-	call	bcopy_4_dfdat
-	call	bcopy_4_dfdat
-	call	bcopy_4_dfdat   
-	call	bcopy_4_dfdat
-# ifndef AIC7XXX_USE_SG
-#	call	bcopy_3_dfdat
-#	call	bcopy_4_dfdat
-# endif
+	call	bcopy_5_dfdat 
+	call	bcopy_7_dfdat
+	call	bcopy_7_dfdat
+	call	bcopy_7_dfdat   
 
 # See if there is not already an active SCB for this target.  This code
 # locks out on a per target basis instead of target/lun.  Although this
@@ -320,12 +311,12 @@
 # initialization, board reset, and a target's SELTO.
 
 test_busy:
-	test	SCBARRAY+0,0x20	jnz start_scb
 	and	FUNCTION1,0x70,SCBARRAY+1
 	mov	A,FUNCTION1
 	test	SCBARRAY+1,0x88	jz test_a	# Id < 8 && A channel
 
 	test	ACTIVE_B,A	jnz requeue
+	test	SCBARRAY+0,TAG_ENB	jnz start_scb
 	or	ACTIVE_B,A	# Mark the current target as busy
 	jmp	start_scb
 
@@ -341,6 +332,7 @@
 
 test_a:
 	test	ACTIVE_A,A	jnz requeue
+	test	SCBARRAY+0,TAG_ENB	jnz start_scb
 	or	ACTIVE_A,A	# Mark the current target as busy
 
 start_scb:
@@ -358,8 +350,7 @@
 start_selection:
 	or	SCSISEQ,0x48			# ENSELO|ENAUTOATNO
 	mov	WAITING_SCBH, SCBPTR
-	clr	SG_NOLOAD
-	and	FLAGS,0x3f	# !RESELECTING
+	and	FLAGS,0x3f	# !RESELECTING 
 
 #  As soon as we get a successful selection, the target should go
 #  into the message out phase since we have ATN asserted.  Prepare
@@ -378,17 +369,17 @@
 #  so we interrupt the driver, allow it to fill the message buffer, and
 #  then go back into the arbitration loop
 	mvi     INTSTAT,AWAITING_MSG
-	jmp     poll_for_work
+	jmp     wait_for_selection
 
 identify:
-	mov	SCBARRAY+1	call disconnect	# disconnect ok?
+	and	A,DISCENB,SCBARRAY+0		# mask off disconnect privledge
 
 	and	SINDEX,0x7,SCBARRAY+1		# lun
-	or	SINDEX,A			# return value from disconnect
+	or	SINDEX,A			# or in disconnect privledge
 	or	SINDEX,0x80	call mk_mesg	# IDENTIFY message
 
 	mov	A,SINDEX
-	test	SCBARRAY+0,0xe0	jz  !message	# WDTR, SDTR or TAG??
+	test	SCBARRAY+0,0xb0	jz  !message	# WDTR, SDTR or TAG??
 	cmp	MSG_START+0,A	jne !message	# did driver beat us?
 
 # Tag Message if Tag enabled in SCB control block.  Use SCBPTR as the tag
@@ -408,7 +399,10 @@
 	mov	DINDEX	call mk_dtr	# build DTR message if needed
 
 !message:
-	jmp	poll_for_work
+wait_for_selection:
+	test	SSTAT0,SELDI	jnz reselect
+	test	SSTAT0,SELDO	jnz select
+	jmp	wait_for_selection
 
 #  Reselection has been initiated by a target. Make a note that we've been
 #  reselected, but haven't seen an IDENTIFY message from the target
@@ -461,78 +455,118 @@
 
 p_dataout:
 	mvi	0		call scsisig	# !CDO|!IOO|!MSGO
-	call	assert
-	call	sg_load
+	mvi	DMAPARAMS,0x7d			# WIDEODD|SCSIEN|SDMAEN|HDMAEN|
+						#   DIRECTION|FIFORESET
+	jmp	data_phase_init
 
-	mvi	DINDEX,HADDR
-	mvi	SCBARRAY+19	call bcopy_4
+# If we re-enter the data phase after going through another phase, the
+# STCNT may have been cleared, so restore it from the residual field.
+data_phase_reinit:
+	mvi	DINDEX, STCNT
+	mvi	SCBARRAY+15	call bcopy_3
+	jmp	data_phase_loop
 
-#	mvi	DINDEX,HCNT	# implicit since HCNT is next to HADDR
-	mvi	SCBARRAY+23	call bcopy_3
+# Reads should not use WIDEODD since it may make the last byte for a SG segment
+# go to the next segment.
+p_datain:
+	mvi	0x40		call scsisig	# !CDO|IOO|!MSGO
+	mvi	DMAPARAMS,0x39			# SCSIEN|SDMAEN|HDMAEN|
+						#   !DIRECTION|FIFORESET
+data_phase_init:
+	call	assert
 
-	mvi	DINDEX,STCNT
-	mvi	SCBARRAY+23	call bcopy_3
+	test	FLAGS, DPHASE	jnz data_phase_reinit
+	call	sg_scb2ram
+	or	FLAGS, DPHASE			# We have seen a data phase
 
+data_phase_loop:
 # If we are the last SG block, don't set wideodd.
-	test    SCBARRAY+18,0xff jnz p_dataout_wideodd
-	mvi	0x3d		call dma	# SCSIEN|SDMAEN|HDMAEN|
-						#   DIRECTION|FIFORESET
-	jmp	p_dataout_rest
+	cmp	SG_COUNT,0x01 jne data_phase_wideodd
+	and	DMAPARAMS, 0xbf		        # Turn off WIDEODD 
+data_phase_wideodd:
+	mov	DMAPARAMS  call dma
 
-p_dataout_wideodd:
-	mvi	0xbd		call dma	# WIDEODD|SCSIEN|SDMAEN|HDMAEN|
-						#   DIRECTION|FIFORESET
+# Exit if we had an underrun
+	test	SSTAT0,0x04	jz data_phase_finish	# underrun STCNT != 0
 
-p_dataout_rest:
-#  After a DMA finishes, save the final transfer pointer and count
-#  back into the SCB, in case a device disconnects in the middle of
-#  a transfer.  Use SHADDR and STCNT instead of HADDR and HCNT, since
-#  it's a reflection of how many bytes were transferred on the SCSI
-#  (as opposed to the host) bus.
+#  Advance the scatter-gather pointers if needed 
 #
-	mvi	DINDEX,SCBARRAY+23
-	mvi	STCNT		call bcopy_3
-
-	mvi	DINDEX,SCBARRAY+19
-	mvi	SHADDR		call bcopy_4
+sg_advance:
+	dec	SG_COUNT			# one less segment to go
 
-	call	sg_advance
-	mov	SCBARRAY+18,SG_COUNT		# residual S/G count
+	test	SG_COUNT, 0xff	jz data_phase_finish #Are we done?
 
-	jmp	ITloop
+	clr	A				# add sizeof(struct scatter)
+	add	SG_NEXT+0,SG_SIZEOF,SG_NEXT+0
+	adc	SG_NEXT+1,A,SG_NEXT+1
+	adc	SG_NEXT+2,A,SG_NEXT+2
+	adc	SG_NEXT+3,A,SG_NEXT+3
 
-p_datain:
-	mvi	0x40		call scsisig	# !CDO|IOO|!MSGO
-	call	assert
-	call	sg_load
+#  Load a struct scatter and set up the data address and length.
+#  If the working value of the SG count is nonzero, then
+#  we need to load a new set of values.
+#
+#  This, like all DMA's, assumes a little-endian host data storage.
+#
+sg_load:
+	clr	HCNT+2
+	clr	HCNT+1
+	mvi	HCNT+0,SG_SIZEOF
 
 	mvi	DINDEX,HADDR
-	mvi	SCBARRAY+19	call bcopy_4
+	mvi	SG_NEXT		call bcopy_4
 
-#	mvi	DINDEX,HCNT	# implicit since HCNT is next to HADDR
-	mvi	SCBARRAY+23	call bcopy_3
+	mvi	DFCNTRL,0xd			# HDMAEN|DIRECTION|FIFORESET
 
-	mvi	DINDEX,STCNT
-	mvi	SCBARRAY+23	call bcopy_3
+#  Wait for DMA from host memory to data FIFO to complete, then disable
+#  DMA and wait for it to acknowledge that it's off.
+#
+	call	dma_finish
 
-# If we are the last SG block, don't set wideodd.
-	test	SCBARRAY+18,0xff jnz p_datain_wideodd
-	mvi	0x39		call dma	# SCSIEN|SDMAEN|HDMAEN|
-						#   !DIRECTION|FIFORESET
-	jmp	p_datain_rest
-p_datain_wideodd:
-	mvi	0xb9		call dma	# WIDEODD|SCSIEN|SDMAEN|HDMAEN|
-						#   !DIRECTION|FIFORESET
-p_datain_rest:
-	mvi	DINDEX,SCBARRAY+23
-	mvi	STCNT		call bcopy_3
+#  Copy data from FIFO into SCB data pointer and data count.  This assumes
+#  that the struct scatterlist has this structure (this and sizeof(struct
+#  scatterlist) == 12 are asserted in aic7xxx.c):
+#
+#	struct scatterlist {
+#		char *address;		/* four bytes, little-endian order */
+#		...			/* four bytes, ignored */
+#		unsigned short length;	/* two bytes, little-endian order */
+#	}
+#
 
-	mvi	DINDEX,SCBARRAY+19
-	mvi	SHADDR		call bcopy_4
+# Not in FreeBSD.  the scatter list entry is only 8 bytes.
+# 
+# struct ahc_dma_seg {
+#       physaddr addr;                  /* four bytes, little-endian order */
+#       long    len;                    /* four bytes, little endian order */   
+# };
+#
+
+	mvi	DINDEX,HADDR
+#	call	bcopy_7_dfdat
+
+# For Linux, we must throw away four bytes since there is a 32bit gap
+# in the middle of a struct scatterlist
+	call	bcopy_4_dfdat
+	mov	NONE,DFDAT
+	mov	NONE,DFDAT
+	mov	NONE,DFDAT
+	mov	NONE,DFDAT
+	call	bcopy_3_dfdat		#Only support 24 bit length.
 
-	call	sg_advance
-	mov	SCBARRAY+18,SG_COUNT		# residual S/G count
+# Load STCNT as well.  It is a mirror of HCNT
+	mvi	DINDEX,STCNT
+	mvi	HCNT	call bcopy_3
+        test    SSTAT1,PHASEMIS  jz data_phase_loop
 
+data_phase_finish:
+#  After a DMA finishes, save the SG and STCNT residuals back into the SCB
+#  We use STCNT instead of HCNT, since it's a reflection of how many bytes 
+#  were transferred on the SCSI (as opposed to the host) bus.
+#
+	mvi	DINDEX,SCBARRAY+15
+	mvi	STCNT		call bcopy_3
+	mov	SCBARRAY+18, SG_COUNT
 	jmp	ITloop
 
 #  Command phase.  Set up the DMA registers and let 'er rip - the
@@ -543,11 +577,9 @@
 	mvi	0x80		call scsisig	# CDO|!IOO|!MSGO
 	call	assert
 
+# Load HADDR and HCNT.  We can do this in one bcopy since they are neighbors
 	mvi	DINDEX,HADDR
-	mvi	SCBARRAY+7	call bcopy_4
-
-#	mvi	DINDEX,HCNT	# implicit since HCNT is next to HADDR
-	mvi	SCBARRAY+11	call bcopy_3
+	mvi	SCBARRAY+7	call bcopy_7
 
 	mvi	DINDEX,STCNT
 	mvi	SCBARRAY+11	call bcopy_3
@@ -563,7 +595,7 @@
 	mvi	0xc0		call scsisig	# CDO|IOO|!MSGO
 
 	mvi	SCBARRAY+14	call inb_first
-	jmp	p_mesgin_done
+	jmp	mesgin_done
 
 #  Message out phase.  If there is no active message, but the target
 #  took us into this phase anyway, build a no-op message and send it.
@@ -651,8 +683,34 @@
 	mvi	A		call inb_first	# read the 1st message byte
 	mvi	REJBYTE,A			# save it for the driver
 
-	cmp	ALLZEROS,A	jne p_mesgin1
+	test	A,0x80		jnz mesgin_identify	# identify message?
+	cmp	A,4		je mesgin_disconnect	# disconnect?
+	cmp	A,2		je mesgin_sdptrs	# save data pointers?
+	cmp	ALLZEROS,A	je mesgin_complete	# command complete?
+	cmp	A,3		je mesgin_rdptrs	# restore pointers code?
+	cmp	A,1		je mesgin_extended	# extended message?
+	cmp	A,7		je mesgin_reject	# message reject code?
+
+rej_mesgin:
+#  We have no idea what this message in is, and there's no way
+#  to pass it up to the kernel, so we issue a message reject and
+#  hope for the best.  Since we're now using manual PIO mode to
+#  read in the message, there should no longer be a race condition
+#  present when we assert ATN.  In any case, rejection should be a
+#  rare occurrence - signal the driver when it happens.
+#
+	or	SINDEX,0x10,SIGSTATE		# turn on ATNO
+	call	scsisig
+	mvi	INTSTAT,SEND_REJECT		# let driver know
+
+	mvi	0x7		call mk_mesg	# MESSAGE REJECT message
+
+mesgin_done:
+	call	inb_last			# ack & turn auto PIO back on
+	jmp	ITloop
+
 
+mesgin_complete:
 #  We got a "command complete" message, so put the SCB pointer
 #  into the Queue Out, and trigger a completion interrupt.
 #  Check status for non zero return and interrupt driver if needed
@@ -669,19 +727,17 @@
 #  before the command complete code tried processing it.
 
 # First check for residuals
-	test	SCBARRAY+15,0xff	jnz resid
-	test	SCBARRAY+16,0xff	jnz resid
-	test	SCBARRAY+17,0xff	jnz resid
+	test	SCBARRAY+18,0xff	jnz resid
 
 check_status:
 	test	SCBARRAY+14,0xff	jz status_ok	# 0 Status?
 	mvi	INTSTAT,BAD_STATUS			# let driver know
 	test	RETURN_1, 0x80	jz status_ok
-	jmp	p_mesgin_done
+	jmp	mesgin_done
 
 status_ok:
 #  First, mark this target as free.
-	test	SCBARRAY+0,0x20	jnz complete		# Tagged command
+	test	SCBARRAY+0,TAG_ENB	jnz complete		# Tagged command
 	and	FUNCTION1,0x70,SCBARRAY+1
 	mov	A,FUNCTION1
 	test	SCBARRAY+1,0x88 jz clear_a
@@ -691,11 +747,16 @@
 clear_a:
 	xor	ACTIVE_A,A
 
+	test    SCBARRAY+11,0xff jnz complete  # Immediate message complete
+# Pause the sequencer until the driver gets around to handling the command
+# complete.  This is so that any action that might require carefull timing
+# with the completion of this command can occur.
+	mvi	INTSTAT,IMMEDDONE
+	jmp	poll_for_work
 complete:
 	mov	QOUTFIFO,SCBPTR
 	mvi	INTSTAT,CMDCMPLT
-	test    SCBARRAY+11,0xff jz start	# Immediate message complete
-	jmp	p_mesgin_done
+	jmp	mesgin_done
 
 # If we have a residual count, interrupt and tell the host.  Other
 # alternatives are to pause the sequencer on all command completes (yuck),
@@ -714,21 +775,19 @@
 #  apparently this can be done after any message in byte, according
 #  to the SCSI-2 spec.
 #
-p_mesgin1:
-	cmp	A,1		jne p_mesgin2	# extended message code?
-	
+mesgin_extended:
 	mvi	ARG_1		call inb_next	# extended message length
 	mvi	A		call inb_next	# extended message code
 
 	cmp	A,1		je p_mesginSDTR	# Syncronous negotiation message
 	cmp	A,3		je p_mesginWDTR # Wide negotiation message
-	jmp	p_mesginN
+	jmp	rej_mesgin
 
 p_mesginWDTR:
-	cmp	ARG_1,2		jne p_mesginN	# extended mesg length = 2
+	cmp	ARG_1,2		jne rej_mesgin	# extended mesg length=2
 	mvi	A		call inb_next	# Width of bus
 	mvi	INTSTAT,MSG_WDTR		# let driver know
-	test	RETURN_1,0x80	jz p_mesgin_done# Do we need to send WDTR?
+	test	RETURN_1,0x80	jz mesgin_done# Do we need to send WDTR?
 
 # We didn't initiate the wide negotiation, so we must respond to the request
 	and	RETURN_1,0x7f			# Clear the SEND_WDTR Flag
@@ -737,59 +796,53 @@
 	mvi	MSG_START+0	call mk_wdtr	# build WDTR message	
 	or	SINDEX,0x10,SIGSTATE		# turn on ATNO
 	call	scsisig
-	jmp	p_mesgin_done
+	jmp	mesgin_done
 
 p_mesginSDTR:
-	cmp	ARG_1,3		jne p_mesginN	# extended mesg length = 3
+	cmp	ARG_1,3		jne rej_mesgin	# extended mesg length=3
 	mvi	ARG_1		call inb_next	# xfer period
 	mvi	A		call inb_next	# REQ/ACK offset
 	mvi	INTSTAT,MSG_SDTR		# call driver to convert
 
-	test	RETURN_1,0xc0	jz p_mesgin_done# Do we need to mk_sdtr or rej?
-	test	RETURN_1,0x40	jnz p_mesginN	# Requested SDTR too small - rej
+	test	RETURN_1,0xc0	jz mesgin_done# Do we need to mk_sdtr or rej?
+	test	RETURN_1,0x40	jnz rej_mesgin	# Requested SDTR too small - rej
 	or	FLAGS,ACTIVE_MSG
 	mvi	DINDEX, MSG_START+0
 	mvi     MSG_START+0     call mk_sdtr
 	or	SINDEX,0x10,SIGSTATE		# turn on ATNO
 	call	scsisig
-	jmp	p_mesgin_done
+	jmp	mesgin_done
 
 #  Is it a disconnect message?  Set a flag in the SCB to remind us
 #  and await the bus going free.
 #
-p_mesgin2:
-	cmp	A,4		jne p_mesgin3	# disconnect code?
-
-	or	SCBARRAY+0,0x4			# set "disconnected" bit
-	jmp	p_mesgin_done
+mesgin_disconnect:
+	or	SCBARRAY+0,DISCONNECTED
+	jmp	mesgin_done
 
 #  Save data pointers message?  Copy working values into the SCB,
 #  usually in preparation for a disconnect.
 #
-p_mesgin3:
-	cmp	A,2		jne p_mesgin4	# save data pointers code?
-
+mesgin_sdptrs:
 	call	sg_ram2scb
-	jmp	p_mesgin_done
+	jmp	mesgin_done
 
 #  Restore pointers message?  Data pointers are recopied from the
-#  SCB anyway at the start of any DMA operation, so the only thing
-#  to copy is the scatter-gather values.
-#
-p_mesgin4:
-	cmp	A,3		jne p_mesgin5	# restore pointers code?
-
-	call	sg_scb2ram
-	jmp	p_mesgin_done
+#  SCB anytime we enter a data phase for the first time, so all
+#  we need to do is clear the DPHASE flag and let the data phase
+#  code do the rest.
+#
+mesgin_rdptrs:
+	and	FLAGS,0xfb			# !DPHASE we'll reload them
+						#   the next time through
+	jmp	mesgin_done
 
 #  Identify message?  For a reconnecting target, this tells us the lun
 #  that the reconnection is for - find the correct SCB and switch to it,
 #  clearing the "disconnected" bit so we don't "find" it by accident later.
 #
-p_mesgin5:
-	test	A,0x80		jz p_mesgin6	# identify message?
-
-	test	A,0x78		jnz p_mesginN	# !DiscPriv|!LUNTAR|!Reserved
+mesgin_identify:
+	test	A,0x78		jnz rej_mesgin	# !DiscPriv|!LUNTAR|!Reserved
 
 	and	A,0x07				# lun in lower three bits
 	or      SAVED_TCL,A,SELID          
@@ -802,8 +855,6 @@
 	and	SCBARRAY+0,0xfb			# clear disconnect bit in SCB
 	or	FLAGS,IDENTIFY_SEEN		# make note of IDENTIFY
 
-	call	sg_scb2ram			# implied restore pointers
-						#   required on reselect
 	jmp	ITloop
 get_tag:
 	mvi	A		call inb_first
@@ -828,34 +879,12 @@
 #  the target selecting 8bit or asynchronous transfer, otherwise just ignore 
 #  it since we have no clue what it pertains to.
 #
-p_mesgin6:
-	cmp	A,7		jne p_mesgin7	# message reject code?
-
+mesgin_reject:
 	mvi	INTSTAT, MSG_REJECT
-	jmp	p_mesgin_done
+	jmp	mesgin_done
 
 #  [ ADD MORE MESSAGE HANDLING HERE ]
 #
-p_mesgin7:
-
-#  We have no idea what this message in is, and there's no way
-#  to pass it up to the kernel, so we issue a message reject and
-#  hope for the best.  Since we're now using manual PIO mode to
-#  read in the message, there should no longer be a race condition
-#  present when we assert ATN.  In any case, rejection should be a
-#  rare occurrence - signal the driver when it happens.
-#
-p_mesginN:
-	or	SINDEX,0x10,SIGSTATE		# turn on ATNO
-	call	scsisig
-	mvi	INTSTAT,SEND_REJECT		# let driver know
-
-	mvi	0x7		call mk_mesg	# MESSAGE REJECT message
-
-p_mesgin_done:
-	call	inb_last			# ack & turn auto PIO back on
-	jmp	ITloop
-
 
 #  Bus free phase.  It might be useful to interrupt the device
 #  driver if we aren't expecting this.  For now, make sure that
@@ -868,33 +897,34 @@
 #  if this is an immediate command, perform a psuedo command complete to
 #  notify the driver.
 	test	SCBARRAY+11,0xff	jz status_ok
-	jmp	start
+	jmp	poll_for_work
 
 #  Instead of a generic bcopy routine that requires an argument, we unroll
-#  the two cases that are actually used, and call them explicitly.  This
-#  not only reduces the overhead of doing a bcopy by 2/3rds, but ends up
-#  saving space in the program since you don't have to put the argument 
-#  into the accumulator before the call.  Both functions expect DINDEX to
-#  contain the destination address and SINDEX to contain the source 
-#  address.
-bcopy_3:
+#  the cases that are actually used, and call them explicitly.  This
+#  not only reduces the overhead of doing a bcopy, but ends up saving space 
+#  in the program since you don't have to put the argument into the accumulator
+#  before the call.  Both functions expect DINDEX to contain the destination 
+#  address and SINDEX to contain the source address.
+bcopy_7:
 	mov	DINDIR,SINDIR
 	mov	DINDIR,SINDIR
-	mov	DINDIR,SINDIR	ret
-
+bcopy_5:
+	mov	DINDIR,SINDIR
 bcopy_4:
 	mov	DINDIR,SINDIR
+bcopy_3:
 	mov	DINDIR,SINDIR
 	mov	DINDIR,SINDIR
 	mov	DINDIR,SINDIR	ret
 	
-bcopy_3_dfdat:
+bcopy_7_dfdat:
 	mov	DINDIR,DFDAT
 	mov	DINDIR,DFDAT
-	mov	DINDIR,DFDAT	ret
-
+bcopy_5_dfdat:
+	mov	DINDIR,DFDAT
 bcopy_4_dfdat:
 	mov	DINDIR,DFDAT
+bcopy_3_dfdat:
 	mov	DINDIR,DFDAT
 	mov	DINDIR,DFDAT
 	mov	DINDIR,DFDAT	ret
@@ -962,7 +992,6 @@
 dma:
 	mov	DFCNTRL,SINDEX
 dma1:
-dma2:
 	test	SSTAT0,0x1	jnz dma3	# DMADONE
 	test	SSTAT1,0x10	jz dma1		# PHASEMIS, ie. underrun
 
@@ -978,19 +1007,14 @@
 dma4:
 	test	DFSTATUS,0x1	jz dma4		# !FIFOEMP
 
-#  Now shut the DMA enables off, and copy STCNT (ie. the underrun
-#  amount, if any) to the SCB registers; SG_COUNT will get copied to
-#  the SCB's residual S/G count field after sg_advance is called.  Make
-#  sure that the DMA enables are actually off first lest we get an ILLSADDR.
+#  Now shut the DMA enables off and make sure that the DMA enables are 
+#  actually off first lest we get an ILLSADDR.
 #
 dma5:
 	clr	DFCNTRL				# disable DMA
 dma6:
 	test	DFCNTRL,0x38	jnz dma6	# SCSIENACK|SDMAENACK|HDMAENACK
 
-	mvi	DINDEX,SCBARRAY+15
-	mvi	STCNT		call bcopy_3
-
 	ret
 
 dma_finish:
@@ -1022,10 +1046,9 @@
 
 	mvi	SXFRCTL0,0x8a			# DFON|SPIOEN|CLRCHN
 
-#  Initialize scatter-gather pointers by setting up the working copy
-#  in scratch RAM.
-#
-	call	sg_scb2ram
+#  Make sure that the system knows we have not been through a DATA
+#  phase.
+	and	FLAGS, 0xfb			# !DPHASE
 
 #  Initialize SCSIRATE with the appropriate value for this target.
 #
@@ -1041,29 +1064,6 @@
 
 	mvi	INTSTAT,NO_IDENT 	ret	# no - cause a kernel panic
 
-#  Find out if disconnection is ok from the information the BIOS has left
-#  us.  The tcl from SCBARRAY+1 should be in SINDEX; A will
-#  contain either 0x40 (disconnection ok) or 0x00 (disconnection not ok)
-#  on exit.
-#
-#  To allow for wide or twin busses, we check the upper bit of the target ID
-#  and the channel ID and look at the appropriate disconnect register. 
-#
-disconnect:
-	and	FUNCTION1,0x70,SINDEX		# strip off extra just in case
-	mov	A,FUNCTION1
-	test	SINDEX, 0x88	jz disconnect_a
-
-	test	DISC_DSB_B,A	jz disconnect1	# bit nonzero if DISabled
-	clr	A		ret
-
-disconnect_a:
-	test	DISC_DSB_A,A	jz disconnect1	# bit nonzero if DISabled
-	clr	A		ret
-
-disconnect1:
-	mvi	A,0x40		ret
-
 #  Locate the SCB matching the target ID/channel/lun in SAVED_TCL and switch 
 #  the SCB to it.  Have the kernel print a warning message if it can't be 
 #  found, and generate an ABORT message to the target.  SINDEX should be
@@ -1073,7 +1073,7 @@
 	mov	A,SAVED_TCL
 	mov	SCBPTR,SINDEX			# switch to new SCB
 	cmp	SCBARRAY+1,A	jne findSCB1	# target ID/channel/lun match?
-	test	SCBARRAY+0,0x4	jz findSCB1	# should be disconnected
+	test	SCBARRAY+0,DISCONNECTED	jz findSCB1 # should be disconnected
 	test	SCBARRAY+0,TAG_ENB jnz get_tag
 	ret
 
@@ -1089,113 +1089,40 @@
 	call	scsisig
 	ret
 
-#  Make a working copy of the scatter-gather parameters in the SCB.
+#  Make a working copy of the scatter-gather parameters from the SCB.
 #
 sg_scb2ram:
+	mvi	DINDEX,HADDR
+	mvi	SCBARRAY+19	call bcopy_7
+
+	mvi	DINDEX,STCNT
+	mvi	SCBARRAY+23	call bcopy_3
+
 	mov	SG_COUNT,SCBARRAY+2
 
 	mvi	DINDEX,SG_NEXT
 	mvi	SCBARRAY+3	call bcopy_4
+	ret
 
-	mvi	SG_NOLOAD,0x80
-	test	SCBARRAY+0,0x10	jnz return	# don't reload s/g?
-	clr	SG_NOLOAD	 ret
-
-#  Copying RAM values back to SCB, for Save Data Pointers message.
+#  Copying RAM values back to SCB, for Save Data Pointers message, but
+#  only if we've actually been into a data phase to change them.  This
+#  protects against bogus data in scratch ram and the residual counts
+#  since they are only initialized when we go into data_in or data_out.
 #
 sg_ram2scb:
+	test	FLAGS, DPHASE	jz return
 	mov	SCBARRAY+2,SG_COUNT
 
 	mvi	DINDEX,SCBARRAY+3
 	mvi	SG_NEXT		call bcopy_4
+	
+	mvi	DINDEX,SCBARRAY+19
+	mvi	SHADDR	call bcopy_4
 
-	and	SCBARRAY+0,0xef,SCBARRAY+0
-	test	SG_NOLOAD,0x80	jz return	# reload s/g?
-	or	SCBARRAY+0,SG_LOAD	 ret
-
-#  Load a struct scatter if needed and set up the data address and
-#  length.  If the working value of the SG count is nonzero, then
-#  we need to load a new set of values.
-#
-#  This, like the above DMA, assumes a little-endian host data storage.
-#
-sg_load:
-	test	SG_COUNT,0xff	jz return	# SG being used?
-	test	SG_NOLOAD,0x80	jnz return	# don't reload s/g?
-
-	clr	HCNT+2
-	clr	HCNT+1
-	mvi	HCNT+0,SG_SIZEOF
-
-	mvi	DINDEX,HADDR
-	mvi	SG_NEXT		call bcopy_4
-
-	mvi	DFCNTRL,0xd			# HDMAEN|DIRECTION|FIFORESET
-
-#  Wait for DMA from host memory to data FIFO to complete, then disable
-#  DMA and wait for it to acknowledge that it's off.
-#
-
-	call	dma_finish
-
-#  Copy data from FIFO into SCB data pointer and data count.  This assumes
-#  that the struct scatterlist has this structure (this and sizeof(struct
-#  scatterlist) == 12 are asserted in aic7xxx.c):
-#
-#	struct scatterlist {
-#		char *address;		/* four bytes, little-endian order */
-#		...			/* four bytes, ignored */
-#		unsigned short length;	/* two bytes, little-endian order */
-#	}
-#
-
-# Not in FreeBSD.  the scatter list entry is only 8 bytes.
-# 
-# struct ahc_dma_seg {
-#       physaddr addr;                  /* four bytes, little-endian order */
-#       long    len;                    /* four bytes, little endian order */   
-# };
-#
-
-	mvi	DINDEX, SCBARRAY+19
-	call	bcopy_4_dfdat
-
-# For Linux, we must throw away four bytes since there is a 32bit gap
-# in the middle of a struct scatterlist
-	mov	NONE,DFDAT
-	mov	NONE,DFDAT
-	mov	NONE,DFDAT
-	mov	NONE,DFDAT
-
-	call	bcopy_3_dfdat		#Only support 24 bit length.
+# Use the residual number since STCNT is corrupted by any message transfer
+	mvi	SCBARRAY+15	call bcopy_3
 	ret
 
-#  Advance the scatter-gather pointers only IF NEEDED.  If SG is enabled,
-#  and the SCSI transfer count is zero (note that this should be called
-#  right after a DMA finishes), then move the working copies of the SG
-#  pointer/length along.  If the SCSI transfer count is not zero, then
-#  presumably the target is disconnecting - do not reload the SG values
-#  next time.
-#
-sg_advance:
-	test	SG_COUNT,0xff	jz return	# s/g enabled?
-
-	test	STCNT+0,0xff	jnz sg_advance1	# SCSI transfer count nonzero?
-	test	STCNT+1,0xff	jnz sg_advance1
-	test	STCNT+2,0xff	jnz sg_advance1
-
-	clr	SG_NOLOAD			# reload s/g next time
-	dec	SG_COUNT			# one less segment to go
-
-	clr	A				# add sizeof(struct scatter)
-	add	SG_NEXT+0,SG_SIZEOF,SG_NEXT+0
-	adc	SG_NEXT+1,A,SG_NEXT+1
-	adc	SG_NEXT+2,A,SG_NEXT+2
-	adc	SG_NEXT+3,A,SG_NEXT+3	ret
-
-sg_advance1:
-	mvi	SG_NOLOAD,0x80	ret		# don't reload s/g next time
-
 #  Add the array base SYNCNEG to the target offset (the target address
 #  is in SCSIID), and return the result in SINDEX.  The accumulator
 #  contains the 3->8 decoding of the target ID on return.
@@ -1217,7 +1144,7 @@
 #  reject, you wouldn't be able to tell which message was the culpret.
 #
 mk_dtr:
-	test	SCBARRAY+0,0xc0 jz return	# NEEDWDTR|NEEDSDTR
+	test	SCBARRAY+0,0x90 jz return	# NEEDWDTR|NEEDSDTR
 	test	SCBARRAY+0,NEEDWDTR jnz  mk_wdtr_16bit
 	or	FLAGS, MAX_OFFSET	# Force an offset of 15 or 8 if WIDE
 

FUNET's LINUX-ADM group, linux-adm@nic.funet.fi
TCL-scripts by Sam Shen, slshen@lbl.gov with Sam's (original) version
of this