summaryrefslogtreecommitdiffstats
path: root/sys/dev
diff options
context:
space:
mode:
authorscottl <scottl@FreeBSD.org>2002-11-30 19:30:09 +0000
committerscottl <scottl@FreeBSD.org>2002-11-30 19:30:09 +0000
commit4098cc8cf175e601d0e78908679f83eec84b7c29 (patch)
tree45afe415b1b53b6c7270f827b6c932b0bf60399d /sys/dev
parentf0816674a3307e103a962d3187e15d127e281088 (diff)
downloadFreeBSD-src-4098cc8cf175e601d0e78908679f83eec84b7c29.zip
FreeBSD-src-4098cc8cf175e601d0e78908679f83eec84b7c29.tar.gz
Bring in many bugfixes and changes obtained from formal testing:
aic7xxx.c: aic7xxx.h: aic7xxx.reg: aic7xxx.seq: Bring in the protocol violation handler from the U320 driver and replace the NO_IDENT sequencer interrupt code with the PROTO_VIOLATION code. Support for this code required the following changes: SEQ_FLAGS: IDENTIFY_SEEN -> NOT_IDENTIFIED Added NO_CDB_SENT SCB_CONTROL: TARGET_SCB == STATUS_RCVD for initiator mode scb->flags: Added SCB_TARGET_SCB since we cannot rely on TARGET_SCB as a target/initiator differentiator due to it being overloaded in initiator mode to indicate that status has been received. aic7xxx.seq: Move data fifo CLRCHN to mesgin_rdptrs which is a safer location for doing this operation. This also saves a sequencer instruction. aic7xxx.c: aic7xxx.h: Change ahc/ahd_upate_neg_request() to take a "negotiation type" enum that allows us to negotiate: o only if the goal and current parameters differ. o only if the goal is non-async o always - even if the negotiation will be for async. aic7xxx.seq: Reset the FIFO whenever a short CDB transfer occurs so that the FIFO contents do not corrupt a future CDB transfer retry. Add support for catching the various protocol violations handled by ahc_handle_protocol_violation. Reformat some comments. aic7xxx.c: aic7xxx.h: Just for safety, have the aic7xxx driver probe the stack depth. aic7xxx.c: aic7xxx.h: Save and restore stack contents during diagnostics. Some chip variants overwrite stale entries on a stack "pop". Don't use 0 to probe the stack depth. 0 is the typical value used to backfill the stack if entries are overwritten on a "pop". aic7xxx.h: Add a missing typedef. Collapse SCB flag entries so they are bit contiguous. Add AHD_ULTRA2_XFER_PERIOD for narrow fallback calculations aic7xxx.c: Don't panic (as a diagnostic to catch bugs) if we decided to force the renegotiation of async even if we believe we are already async. This should allow us to negotiate async instead of the full user goal rate during startup if bus resets are disabled. Add a space to the end of the ahc/ahd_print_devinfo routines so that it behaves as expected by the code that uses it. Only force a renegotiation on a selection timeout if the SCB was valid. Doing otherwise may be dangerous as the connection was not valid for an unknown reason. Add additional diagnostic output to ahc_dump_card_state(), and have it use the register pretty printing functions. Update ahc_reg_print() to handle a NULL cur_col. Add a newline to ahc_dump_card_state() output. Bring back "use_ppr". We need to use_ppr anytime doppr is true or we have non-zero protocol options. The later case was not handled in the recent removal of use_ppr. Move a comment and remove a useless clearing of use_ppr. Don't disable ENBUSFREE when single stepping on a DT capable controller. We cannot re-enable unexpected busfree detection, so we must clear BUSFREE on each step instead. Correct the lookup of the SCB ID in ahc_handle_proto_error. Remove a diagnostic printf. Remove unecessary restoration of the STACK for older chips. Approved by: re (blanket)
Diffstat (limited to 'sys/dev')
-rw-r--r--sys/dev/aic7xxx/aic7xxx.c418
-rw-r--r--sys/dev/aic7xxx/aic7xxx.h51
-rw-r--r--sys/dev/aic7xxx/aic7xxx.reg8
-rw-r--r--sys/dev/aic7xxx/aic7xxx.seq107
4 files changed, 435 insertions, 149 deletions
diff --git a/sys/dev/aic7xxx/aic7xxx.c b/sys/dev/aic7xxx/aic7xxx.c
index a63000b..93c6261 100644
--- a/sys/dev/aic7xxx/aic7xxx.c
+++ b/sys/dev/aic7xxx/aic7xxx.c
@@ -37,7 +37,7 @@
* IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGES.
*
- * $Id: //depot/aic7xxx/aic7xxx/aic7xxx.c#80 $
+ * $Id: //depot/aic7xxx/aic7xxx/aic7xxx.c#100 $
*
* $FreeBSD$
*/
@@ -161,8 +161,6 @@ static struct ahc_syncrate*
static void ahc_update_pending_scbs(struct ahc_softc *ahc);
static void ahc_fetch_devinfo(struct ahc_softc *ahc,
struct ahc_devinfo *devinfo);
-static void ahc_print_devinfo(struct ahc_softc *ahc,
- struct ahc_devinfo *devinfo);
static void ahc_scb_devinfo(struct ahc_softc *ahc,
struct ahc_devinfo *devinfo,
struct scb *scb);
@@ -183,6 +181,7 @@ static void ahc_construct_ppr(struct ahc_softc *ahc,
u_int period, u_int offset,
u_int bus_width, u_int ppr_options);
static void ahc_clear_msg_state(struct ahc_softc *ahc);
+static void ahc_handle_proto_violation(struct ahc_softc *ahc);
static void ahc_handle_message_phase(struct ahc_softc *ahc);
typedef enum {
AHCMSG_1B,
@@ -231,6 +230,7 @@ static int ahc_check_patch(struct ahc_softc *ahc,
u_int start_instr, u_int *skip_addr);
static void ahc_download_instr(struct ahc_softc *ahc,
u_int instrptr, uint8_t *dconsts);
+static int ahc_probe_stack_size(struct ahc_softc *ahc);
#ifdef AHC_TARGET_MODE
static void ahc_queue_lstate_event(struct ahc_softc *ahc,
struct ahc_tmode_lstate *lstate,
@@ -547,7 +547,7 @@ ahc_handle_seqint(struct ahc_softc *ahc, u_int intstat)
== ahc_get_transfer_length(scb)) {
ahc_update_neg_request(ahc, &devinfo,
tstate, targ_info,
- /*force*/TRUE);
+ AHC_NEG_IF_NON_ASYNC);
}
if (tstate->auto_negotiate & devinfo.target_mask) {
hscb->control |= MK_MESSAGE;
@@ -563,16 +563,11 @@ ahc_handle_seqint(struct ahc_softc *ahc, u_int intstat)
scb->flags |= SCB_SENSE;
ahc_qinfifo_requeue_tail(ahc, scb);
ahc_outb(ahc, RETURN_1, SEND_SENSE);
-#ifdef __FreeBSD__
/*
* Ensure we have enough time to actually
* retrieve the sense.
*/
- untimeout(ahc_timeout, (caddr_t)scb,
- scb->io_ctx->ccb_h.timeout_ch);
- scb->io_ctx->ccb_h.timeout_ch =
- timeout(ahc_timeout, (caddr_t)scb, 5 * hz);
-#endif
+ ahc_scb_timer_reset(scb, 5 * 1000000);
break;
}
default:
@@ -626,27 +621,10 @@ ahc_handle_seqint(struct ahc_softc *ahc, u_int intstat)
ahc_name(ahc), devinfo.channel, devinfo.target, rejbyte);
break;
}
- case NO_IDENT:
+ case PROTO_VIOLATION:
{
- /*
- * The reconnecting target either did not send an identify
- * message, or did, but we didn't find an SCB to match and
- * before it could respond to our ATN/abort, it hit a dataphase.
- * The only safe thing to do is to blow it away with a bus
- * reset.
- */
- int found;
-
- printf("%s:%c:%d: Target did not send an IDENTIFY message. "
- "LASTPHASE = 0x%x, SAVED_SCSIID == 0x%x\n",
- ahc_name(ahc), devinfo.channel, devinfo.target,
- ahc_inb(ahc, LASTPHASE), ahc_inb(ahc, SAVED_SCSIID));
- found = ahc_reset_channel(ahc, devinfo.channel,
- /*initiate reset*/TRUE);
- printf("%s: Issued Channel %c Bus Reset. "
- "%d SCBs aborted\n", ahc_name(ahc), devinfo.channel,
- found);
- return;
+ ahc_handle_proto_violation(ahc);
+ break;
}
case IGN_WIDE_RES:
ahc_handle_ign_wide_residue(ahc, &devinfo);
@@ -774,7 +752,44 @@ ahc_handle_seqint(struct ahc_softc *ahc, u_int intstat)
ahc_outb(ahc, LASTPHASE, curphase);
ahc_outb(ahc, SCSISIGO, curphase);
}
- ahc_inb(ahc, SCSIDATL);
+ if ((ahc_inb(ahc, SCSISIGI) & (CDI|MSGI)) == 0) {
+ int wait;
+
+ /*
+ * In a data phase. Faster to bitbucket
+ * the data than to individually ack each
+ * byte. This is also the only strategy
+ * that will work with AUTOACK enabled.
+ */
+ ahc_outb(ahc, SXFRCTL1,
+ ahc_inb(ahc, SXFRCTL1) | BITBUCKET);
+ wait = 5000;
+ while (--wait != 0) {
+ if ((ahc_inb(ahc, SCSISIGI)
+ & (CDI|MSGI)) != 0)
+ break;
+ ahc_delay(100);
+ }
+ ahc_outb(ahc, SXFRCTL1,
+ ahc_inb(ahc, SXFRCTL1) & ~BITBUCKET);
+ if (wait == 0) {
+ struct scb *scb;
+ u_int scb_index;
+
+ ahc_print_devinfo(ahc, &devinfo);
+ printf("Unable to clear parity error. "
+ "Resetting bus.\n");
+ scb_index = ahc_inb(ahc, SCB_TAG);
+ scb = ahc_lookup_scb(ahc, scb_index);
+ if (scb != NULL)
+ ahc_set_transaction_status(scb,
+ CAM_UNCOR_PARITY);
+ ahc_reset_channel(ahc, devinfo.channel,
+ /*init reset*/TRUE);
+ }
+ } else {
+ ahc_inb(ahc, SCSIDATL);
+ }
}
break;
}
@@ -944,9 +959,6 @@ ahc_handle_scsiint(struct ahc_softc *ahc, u_int intstat)
char cur_channel;
char intr_channel;
- /* Make sure the sequencer is in a safe location. */
- ahc_clear_critical_section(ahc);
-
if ((ahc->features & AHC_TWIN) != 0
&& ((ahc_inb(ahc, SBLKCTL) & SELBUSB) != 0))
cur_channel = 'B';
@@ -975,10 +987,13 @@ ahc_handle_scsiint(struct ahc_softc *ahc, u_int intstat)
}
}
+ /* Make sure the sequencer is in a safe location. */
+ ahc_clear_critical_section(ahc);
+
scb_index = ahc_inb(ahc, SCB_TAG);
scb = ahc_lookup_scb(ahc, scb_index);
if (scb != NULL
- && (ahc_inb(ahc, SEQ_FLAGS) & IDENTIFY_SEEN) == 0)
+ && (ahc_inb(ahc, SEQ_FLAGS) & NOT_IDENTIFIED) != 0)
scb = NULL;
if ((ahc->features & AHC_ULTRA2) != 0
@@ -1052,9 +1067,10 @@ ahc_handle_scsiint(struct ahc_softc *ahc, u_int intstat)
break;
}
mesg_out = ahc_phase_table[i].mesg_out;
- if (scb != NULL)
+ if (scb != NULL) {
ahc_print_path(ahc, scb);
- else
+ scb->flags |= SCB_TRANSMISSION_ERROR;
+ } else
printf("%s:%c:%d: ", ahc_name(ahc), intr_channel,
SCSIID_TARGET(ahc, ahc_inb(ahc, SAVED_SCSIID)));
scsirate = ahc_inb(ahc, SCSIRATE);
@@ -1137,7 +1153,18 @@ ahc_handle_scsiint(struct ahc_softc *ahc, u_int intstat)
printf("%s: ahc_intr - referenced scb not "
"valid during SELTO scb(%d, %d)\n",
ahc_name(ahc), scbptr, scb_index);
+ ahc_dump_card_state(ahc);
} else {
+ /*
+ * Force a renegotiation with this target just in
+ * case the cable was pulled and will later be
+ * re-attached. The target may forget its negotiation
+ * settings with us should it attempt to reselect
+ * during the interruption. The target will not issue
+ * a unit attention in this case, so we must always
+ * renegotiate.
+ */
+ ahc_force_renegotiation(ahc);
ahc_set_transaction_status(scb, CAM_SEL_TIMEOUT);
ahc_freeze_devq(ahc, scb);
#ifdef AHC_DEBUG
@@ -1149,16 +1176,6 @@ ahc_handle_scsiint(struct ahc_softc *ahc, u_int intstat)
#endif
}
ahc_outb(ahc, CLRINT, CLRSCSIINT);
- /*
- * Force a renegotiation with this target just in
- * case the cable was pulled and will later be
- * re-attached. The target may forget its negotiation
- * settings with us should it attempt to reselect
- * during the interruption. The target will not issue
- * a unit attention in this case, so we must always
- * renegotiate.
- */
- ahc_force_renegotiation(ahc);
ahc_restart(ahc);
} else if ((status & BUSFREE) != 0
&& (ahc_inb(ahc, SIMODE1) & ENBUSFREE) != 0) {
@@ -1357,7 +1374,7 @@ ahc_force_renegotiation(struct ahc_softc *ahc)
devinfo.target,
&tstate);
ahc_update_neg_request(ahc, &devinfo, tstate,
- targ_info, /*force*/TRUE);
+ targ_info, AHC_NEG_IF_NON_ASYNC);
}
#define AHC_MAX_STEPS 2000
@@ -1420,11 +1437,26 @@ ahc_clear_critical_section(struct ahc_softc *ahc)
simode0 = ahc_inb(ahc, SIMODE0);
ahc_outb(ahc, SIMODE0, 0);
simode1 = ahc_inb(ahc, SIMODE1);
- ahc_outb(ahc, SIMODE1, 0);
+ if ((ahc->features & AHC_DT) != 0)
+ /*
+ * On DT class controllers, we
+ * use the enhanced busfree logic.
+ * Unfortunately we cannot re-enable
+ * busfree detection within the
+ * current connection, so we must
+ * leave it on while single stepping.
+ */
+ ahc_outb(ahc, SIMODE1, ENBUSFREE);
+ else
+ ahc_outb(ahc, SIMODE1, 0);
ahc_outb(ahc, CLRINT, CLRSCSIINT);
ahc_outb(ahc, SEQCTL, ahc_inb(ahc, SEQCTL) | STEP);
stepping = TRUE;
}
+ if ((ahc->features & AHC_DT) != 0) {
+ ahc_outb(ahc, CLRSINT1, CLRBUSFREE);
+ ahc_outb(ahc, CLRINT, CLRSCSIINT);
+ }
ahc_outb(ahc, HCNTRL, ahc->unpause);
while (!ahc_is_paused(ahc))
ahc_delay(200);
@@ -1573,7 +1605,7 @@ ahc_free_tstate(struct ahc_softc *ahc, u_int scsi_id, char channel, int force)
* by the capabilities of the bus connectivity of and sync settings for
* the target.
*/
-static struct ahc_syncrate *
+struct ahc_syncrate *
ahc_devlimited_syncrate(struct ahc_softc *ahc,
struct ahc_initiator_tinfo *tinfo,
u_int *period, u_int *ppr_options, role_t role)
@@ -1786,16 +1818,28 @@ ahc_validate_width(struct ahc_softc *ahc, struct ahc_initiator_tinfo *tinfo,
int
ahc_update_neg_request(struct ahc_softc *ahc, struct ahc_devinfo *devinfo,
struct ahc_tmode_tstate *tstate,
- struct ahc_initiator_tinfo *tinfo, int force)
+ struct ahc_initiator_tinfo *tinfo, ahc_neg_type neg_type)
{
u_int auto_negotiate_orig;
auto_negotiate_orig = tstate->auto_negotiate;
+ if (neg_type == AHC_NEG_ALWAYS) {
+ /*
+ * Force our "current" settings to be
+ * unknown so that unless a bus reset
+ * occurs the need to renegotiate is
+ * recorded persistently.
+ */
+ tinfo->curr.period = AHC_PERIOD_UNKNOWN;
+ tinfo->curr.width = AHC_WIDTH_UNKNOWN;
+ tinfo->curr.offset = AHC_OFFSET_UNKNOWN;
+ tinfo->curr.ppr_options = AHC_OFFSET_UNKNOWN;
+ }
if (tinfo->curr.period != tinfo->goal.period
|| tinfo->curr.width != tinfo->goal.width
|| tinfo->curr.offset != tinfo->goal.offset
|| tinfo->curr.ppr_options != tinfo->goal.ppr_options
- || (force
+ || (neg_type == AHC_NEG_IF_NON_ASYNC
&& (tinfo->goal.offset != 0
|| tinfo->goal.width != MSG_EXT_WDTR_BUS_8_BIT
|| tinfo->goal.ppr_options != 0)))
@@ -1927,7 +1971,7 @@ ahc_set_syncrate(struct ahc_softc *ahc, struct ahc_devinfo *devinfo,
}
update_needed += ahc_update_neg_request(ahc, devinfo, tstate,
- tinfo, /*force*/FALSE);
+ tinfo, AHC_NEG_TO_GOAL);
if (update_needed)
ahc_update_pending_scbs(ahc);
@@ -1989,7 +2033,7 @@ ahc_set_width(struct ahc_softc *ahc, struct ahc_devinfo *devinfo,
}
update_needed += ahc_update_neg_request(ahc, devinfo, tstate,
- tinfo, /*force*/FALSE);
+ tinfo, AHC_NEG_TO_GOAL);
if (update_needed)
ahc_update_pending_scbs(ahc);
}
@@ -2154,10 +2198,10 @@ ahc_compile_devinfo(struct ahc_devinfo *devinfo, u_int our_id, u_int target,
devinfo->target_mask = (0x01 << devinfo->target_offset);
}
-static void
+void
ahc_print_devinfo(struct ahc_softc *ahc, struct ahc_devinfo *devinfo)
{
- printf("%s:%c:%d:%d:", ahc_name(ahc), devinfo->channel,
+ printf("%s:%c:%d:%d: ", ahc_name(ahc), devinfo->channel,
devinfo->target, devinfo->lun);
}
@@ -2170,7 +2214,7 @@ ahc_scb_devinfo(struct ahc_softc *ahc, struct ahc_devinfo *devinfo,
our_id = SCSIID_OUR_ID(scb->hscb->scsiid);
role = ROLE_INITIATOR;
- if ((scb->hscb->control & TARGET_SCB) != 0)
+ if ((scb->flags & SCB_TARGET_SCB) != 0)
role = ROLE_TARGET;
ahc_compile_devinfo(devinfo, our_id, SCB_GET_TARGET(ahc, scb),
SCB_GET_LUN(scb), SCB_GET_CHANNEL(ahc, scb), role);
@@ -2295,7 +2339,6 @@ ahc_build_transfer_msg(struct ahc_softc *ahc, struct ahc_devinfo *devinfo)
int dowide;
int dosync;
int doppr;
- int use_ppr;
u_int period;
u_int ppr_options;
u_int offset;
@@ -2317,23 +2360,37 @@ ahc_build_transfer_msg(struct ahc_softc *ahc, struct ahc_devinfo *devinfo)
&ppr_options, devinfo->role);
dowide = tinfo->curr.width != tinfo->goal.width;
dosync = tinfo->curr.period != period;
- doppr = tinfo->curr.ppr_options != ppr_options;
+ /*
+ * Only use PPR if we have options that need it, even if the device
+ * claims to support it. There might be an expander in the way
+ * that doesn't.
+ */
+ doppr = ppr_options != 0;
if (!dowide && !dosync && !doppr) {
dowide = tinfo->goal.width != MSG_EXT_WDTR_BUS_8_BIT;
dosync = tinfo->goal.offset != 0;
- doppr = tinfo->goal.ppr_options != 0;
}
if (!dowide && !dosync && !doppr) {
- panic("ahc_intr: AWAITING_MSG for negotiation, "
- "but no negotiation needed\n");
+ /*
+ * Force async with a WDTR message if we have a wide bus,
+ * or just issue an SDTR with a 0 offset.
+ */
+ if ((ahc->features & AHC_WIDE) != 0)
+ dowide = 1;
+ else
+ dosync = 1;
+
+ if (bootverbose) {
+ ahc_print_devinfo(ahc, devinfo);
+ printf("Ensuring async\n");
+ }
}
- use_ppr = (tinfo->curr.transport_version >= 3) || doppr;
/* Target initiated PPR is not allowed in the SCSI spec */
if (devinfo->role == ROLE_TARGET)
- use_ppr = 0;
+ doppr = 0;
/*
* Both the PPR message and SDTR message require the
@@ -2343,14 +2400,14 @@ ahc_build_transfer_msg(struct ahc_softc *ahc, struct ahc_devinfo *devinfo)
* Regardless, guarantee that if we are using WDTR and SDTR
* messages that WDTR comes first.
*/
- if (use_ppr || (dosync && !dowide)) {
+ if (doppr || (dosync && !dowide)) {
offset = tinfo->goal.offset;
ahc_validate_offset(ahc, tinfo, rate, &offset,
- use_ppr ? tinfo->goal.width
- : tinfo->curr.width,
+ doppr ? tinfo->goal.width
+ : tinfo->curr.width,
devinfo->role);
- if (use_ppr) {
+ if (doppr) {
ahc_construct_ppr(ahc, devinfo, period, offset,
tinfo->goal.width, ppr_options);
} else {
@@ -2369,6 +2426,8 @@ static void
ahc_construct_sdtr(struct ahc_softc *ahc, struct ahc_devinfo *devinfo,
u_int period, u_int offset)
{
+ if (offset == 0)
+ period = AHC_ASYNC_XFER_PERIOD;
ahc->msgout_buf[ahc->msgout_index++] = MSG_EXTENDED;
ahc->msgout_buf[ahc->msgout_index++] = MSG_EXT_SDTR_LEN;
ahc->msgout_buf[ahc->msgout_index++] = MSG_EXT_SDTR;
@@ -2411,6 +2470,8 @@ ahc_construct_ppr(struct ahc_softc *ahc, struct ahc_devinfo *devinfo,
u_int period, u_int offset, u_int bus_width,
u_int ppr_options)
{
+ if (offset == 0)
+ period = AHC_ASYNC_XFER_PERIOD;
ahc->msgout_buf[ahc->msgout_index++] = MSG_EXTENDED;
ahc->msgout_buf[ahc->msgout_index++] = MSG_EXT_PPR_LEN;
ahc->msgout_buf[ahc->msgout_index++] = MSG_EXT_PPR;
@@ -2449,6 +2510,100 @@ ahc_clear_msg_state(struct ahc_softc *ahc)
ahc_inb(ahc, SEQ_FLAGS2) & ~TARGET_MSG_PENDING);
}
+static void
+ahc_handle_proto_violation(struct ahc_softc *ahc)
+{
+ struct ahc_devinfo devinfo;
+ struct scb *scb;
+ u_int scbid;
+ u_int seq_flags;
+ u_int curphase;
+ u_int lastphase;
+ int found;
+
+ ahc_fetch_devinfo(ahc, &devinfo);
+ scbid = ahc_inb(ahc, SCB_TAG);
+ scb = ahc_lookup_scb(ahc, scbid);
+ seq_flags = ahc_inb(ahc, SEQ_FLAGS);
+ curphase = ahc_inb(ahc, SCSISIGI) & PHASE_MASK;
+ lastphase = ahc_inb(ahc, LASTPHASE);
+ if ((seq_flags & NOT_IDENTIFIED) != 0) {
+
+ /*
+ * The reconnecting target either did not send an
+ * identify message, or did, but we didn't find an SCB
+ * to match.
+ */
+ ahc_print_devinfo(ahc, &devinfo);
+ printf("Target did not send an IDENTIFY message. "
+ "LASTPHASE = 0x%x.\n", lastphase);
+ scb = NULL;
+ } else if (scb == NULL) {
+ /*
+ * We don't seem to have an SCB active for this
+ * transaction. Print an error and reset the bus.
+ */
+ ahc_print_devinfo(ahc, &devinfo);
+ printf("No SCB found during protocol violation\n");
+ goto proto_violation_reset;
+ } else {
+ ahc_set_transaction_status(scb, CAM_SEQUENCE_FAIL);
+ if ((seq_flags & NO_CDB_SENT) != 0) {
+ ahc_print_path(ahc, scb);
+ printf("No or incomplete CDB sent to device.\n");
+ } else if ((ahc_inb(ahc, SCB_CONTROL) & STATUS_RCVD) == 0) {
+ /*
+ * The target never bothered to provide status to
+ * us prior to completing the command. Since we don't
+ * know the disposition of this command, we must attempt
+ * to abort it. Assert ATN and prepare to send an abort
+ * message.
+ */
+ ahc_print_path(ahc, scb);
+ printf("Completed command without status.\n");
+ } else {
+ ahc_print_path(ahc, scb);
+ printf("Unknown protocol violation.\n");
+ ahc_dump_card_state(ahc);
+ }
+ }
+ if ((lastphase & ~P_DATAIN_DT) == 0
+ || lastphase == P_COMMAND) {
+proto_violation_reset:
+ /*
+ * Target either went directly to data/command
+ * phase or didn't respond to our ATN.
+ * The only safe thing to do is to blow
+ * it away with a bus reset.
+ */
+ found = ahc_reset_channel(ahc, 'A', TRUE);
+ printf("%s: Issued Channel %c Bus Reset. "
+ "%d SCBs aborted\n", ahc_name(ahc), 'A', found);
+ } else {
+ /*
+ * Leave the selection hardware off in case
+ * this abort attempt will affect yet to
+ * be sent commands.
+ */
+ ahc_outb(ahc, SCSISEQ,
+ ahc_inb(ahc, SCSISEQ) & ~ENSELO);
+ ahc_assert_atn(ahc);
+ ahc_outb(ahc, MSG_OUT, HOST_MSG);
+ if (scb == NULL) {
+ ahc_print_devinfo(ahc, &devinfo);
+ ahc->msgout_buf[0] = MSG_ABORT_TASK;
+ ahc->msgout_len = 1;
+ ahc->msgout_index = 0;
+ ahc->msg_type = MSG_TYPE_INITIATOR_MSGOUT;
+ } else {
+ ahc_print_path(ahc, scb);
+ scb->flags |= SCB_ABORT;
+ }
+ printf("Protocol violation %s. Attempting to abort.\n",
+ ahc_lookup_phase_entry(curphase)->phasemsg);
+ }
+}
+
/*
* Manual message loop handler.
*/
@@ -3829,6 +3984,8 @@ ahc_free(struct ahc_softc *ahc)
free(ahc->name, M_DEVBUF);
if (ahc->seep_config != NULL)
free(ahc->seep_config, M_DEVBUF);
+ if (ahc->saved_stack != NULL)
+ free(ahc->saved_stack, M_DEVBUF);
#ifndef __FreeBSD__
free(ahc, M_DEVBUF);
#endif
@@ -4364,6 +4521,12 @@ ahc_init(struct ahc_softc *ahc)
size_t driver_data_size;
uint32_t physaddr;
+ ahc->stack_size = ahc_probe_stack_size(ahc);
+ ahc->saved_stack = malloc(ahc->stack_size * sizeof(uint16_t),
+ M_DEVBUF, M_NOWAIT);
+ if (ahc->saved_stack == NULL)
+ return (ENOMEM);
+
#ifdef AHC_DEBUG_SEQUENCER
ahc->flags |= AHC_SEQUENCER_DEBUG;
#endif
@@ -6471,6 +6634,41 @@ ahc_download_instr(struct ahc_softc *ahc, u_int instrptr, uint8_t *dconsts)
}
}
+static int
+ahc_probe_stack_size(struct ahc_softc *ahc)
+{
+ int last_probe;
+
+ last_probe = 0;
+ while (1) {
+ int i;
+
+ /*
+ * We avoid using 0 as a pattern to avoid
+ * confusion if the stack implementation
+ * "back-fills" with zeros when "poping'
+ * entries.
+ */
+ for (i = 1; i <= last_probe+1; i++) {
+ ahc_outb(ahc, STACK, i & 0xFF);
+ ahc_outb(ahc, STACK, (i >> 8) & 0xFF);
+ }
+
+ /* Verify */
+ for (i = last_probe+1; i > 0; i--) {
+ u_int stack_entry;
+
+ stack_entry = ahc_inb(ahc, STACK)
+ |(ahc_inb(ahc, STACK) << 8);
+ if (stack_entry != i)
+ goto sized;
+ }
+ last_probe++;
+ }
+sized:
+ return (last_probe);
+}
+
int
ahc_print_register(ahc_reg_parse_entry_t *table, u_int num_entries,
const char *name, u_int address, u_int value,
@@ -6479,7 +6677,7 @@ ahc_print_register(ahc_reg_parse_entry_t *table, u_int num_entries,
int printed;
u_int printed_mask;
- if (*cur_column >= wrap_point) {
+ if (cur_column != NULL && *cur_column >= wrap_point) {
printf("\n");
*cur_column = 0;
}
@@ -6514,18 +6712,20 @@ ahc_print_register(ahc_reg_parse_entry_t *table, u_int num_entries,
printed += printf(") ");
else
printed += printf(" ");
- *cur_column += printed;
+ if (cur_column != NULL)
+ *cur_column += printed;
return (printed);
}
void
ahc_dump_card_state(struct ahc_softc *ahc)
{
- struct scb *scb;
- struct scb_tailq *untagged_q;
- int target;
- int maxtarget;
- int i;
+ struct scb *scb;
+ struct scb_tailq *untagged_q;
+ u_int cur_col;
+ int target;
+ int maxtarget;
+ int i;
uint8_t last_phase;
uint8_t qinpos;
uint8_t qintail;
@@ -6544,22 +6744,34 @@ ahc_dump_card_state(struct ahc_softc *ahc)
ahc_inb(ahc, ARG_2));
printf("HCNT = 0x%x SCBPTR = 0x%x\n", ahc_inb(ahc, HCNT),
ahc_inb(ahc, SCBPTR));
- printf("SCSISEQ = 0x%x, SBLKCTL = 0x%x\n",
- ahc_inb(ahc, SCSISEQ), ahc_inb(ahc, SBLKCTL));
- printf(" DFCNTRL = 0x%x, DFSTATUS = 0x%x\n",
- ahc_inb(ahc, DFCNTRL), ahc_inb(ahc, DFSTATUS));
- printf("LASTPHASE = 0x%x, SCSISIGI = 0x%x, SXFRCTL0 = 0x%x\n",
- last_phase, ahc_inb(ahc, SCSISIGI), ahc_inb(ahc, SXFRCTL0));
- printf("SSTAT0 = 0x%x, SSTAT1 = 0x%x\n",
- ahc_inb(ahc, SSTAT0), ahc_inb(ahc, SSTAT1));
+ cur_col = 0;
if ((ahc->features & AHC_DT) != 0)
- printf("SCSIPHASE = 0x%x\n", ahc_inb(ahc, SCSIPHASE));
- printf("STACK == 0x%x, 0x%x, 0x%x, 0x%x\n",
- ahc_inb(ahc, STACK) | (ahc_inb(ahc, STACK) << 8),
- ahc_inb(ahc, STACK) | (ahc_inb(ahc, STACK) << 8),
- ahc_inb(ahc, STACK) | (ahc_inb(ahc, STACK) << 8),
- ahc_inb(ahc, STACK) | (ahc_inb(ahc, STACK) << 8));
- printf("SCB count = %d\n", ahc->scb_data->numscbs);
+ ahc_scsisigi_print(ahc_inb(ahc, SCSISIGI), &cur_col, 50);
+ ahc_scsiphase_print(ahc_inb(ahc, SCSIPHASE), &cur_col, 50);
+ ahc_scsibusl_print(ahc_inb(ahc, SCSIBUSL), &cur_col, 50);
+ ahc_lastphase_print(ahc_inb(ahc, LASTPHASE), &cur_col, 50);
+ ahc_scsiseq_print(ahc_inb(ahc, SCSISEQ), &cur_col, 50);
+ ahc_sblkctl_print(ahc_inb(ahc, SBLKCTL), &cur_col, 50);
+ ahc_seqctl_print(ahc_inb(ahc, SEQCTL), &cur_col, 50);
+ ahc_seq_flags_print(ahc_inb(ahc, SEQ_FLAGS), &cur_col, 50);
+ ahc_sstat0_print(ahc_inb(ahc, SSTAT0), &cur_col, 50);
+ ahc_sstat1_print(ahc_inb(ahc, SSTAT1), &cur_col, 50);
+ ahc_sstat2_print(ahc_inb(ahc, SSTAT2), &cur_col, 50);
+ ahc_sstat3_print(ahc_inb(ahc, SSTAT3), &cur_col, 50);
+ ahc_simode0_print(ahc_inb(ahc, SIMODE0), &cur_col, 50);
+ ahc_simode1_print(ahc_inb(ahc, SIMODE1), &cur_col, 50);
+ ahc_sxfrctl0_print(ahc_inb(ahc, SXFRCTL0), &cur_col, 50);
+ ahc_dfcntrl_print(ahc_inb(ahc, DFCNTRL), &cur_col, 50);
+ ahc_dfstatus_print(ahc_inb(ahc, DFSTATUS), &cur_col, 50);
+ if (cur_col != 0)
+ printf("\n");
+ printf("STACK:");
+ for (i = 0; i < ahc->stack_size; i++) {
+ ahc->saved_stack[i] =
+ ahc_inb(ahc, STACK)|(ahc_inb(ahc, STACK) << 8);
+ printf(" 0x%x", ahc->saved_stack[i]);
+ }
+ printf("\nSCB count = %d\n", ahc->scb_data->numscbs);
printf("Kernel NEXTQSCB = %d\n", ahc->next_queued_scb->hscb->tag);
printf("Card NEXTQSCB = %d\n", ahc_inb(ahc, NEXT_QUEUED_SCB));
/* QINFIFO */
@@ -6619,11 +6831,12 @@ ahc_dump_card_state(struct ahc_softc *ahc)
printf("Sequencer SCB Info: ");
for (i = 0; i < ahc->scb_data->maxhscbs; i++) {
ahc_outb(ahc, SCBPTR, i);
- printf("%d(c 0x%x, s 0x%x, l %d, t 0x%x) ",
- i, ahc_inb(ahc, SCB_CONTROL),
- ahc_inb(ahc, SCB_SCSIID),
- ahc_inb(ahc, SCB_LUN),
- ahc_inb(ahc, SCB_TAG));
+ cur_col = printf("\n%3d ", i);
+
+ ahc_scb_control_print(ahc_inb(ahc, SCB_CONTROL), &cur_col, 60);
+ ahc_scb_scsiid_print(ahc_inb(ahc, SCB_SCSIID), &cur_col, 60);
+ ahc_scb_lun_print(ahc_inb(ahc, SCB_LUN), &cur_col, 60);
+ ahc_scb_tag_print(ahc_inb(ahc, SCB_TAG), &cur_col, 60);
}
printf("\n");
@@ -6632,14 +6845,17 @@ ahc_dump_card_state(struct ahc_softc *ahc)
LIST_FOREACH(scb, &ahc->pending_scbs, pending_links) {
if (i++ > 256)
break;
- if (scb != LIST_FIRST(&ahc->pending_scbs))
- printf(", ");
- printf("%d(c 0x%x, s 0x%x, l %d)", scb->hscb->tag,
- scb->hscb->control, scb->hscb->scsiid, scb->hscb->lun);
+ cur_col = printf("\n%3d ", scb->hscb->tag);
+ ahc_scb_control_print(scb->hscb->control, &cur_col, 60);
+ ahc_scb_scsiid_print(scb->hscb->scsiid, &cur_col, 60);
+ ahc_scb_lun_print(scb->hscb->lun, &cur_col, 60);
if ((ahc->flags & AHC_PAGESCBS) == 0) {
ahc_outb(ahc, SCBPTR, scb->hscb->tag);
- printf("(0x%x, 0x%x)", ahc_inb(ahc, SCB_CONTROL),
- ahc_inb(ahc, SCB_TAG));
+ printf("(");
+ ahc_scb_control_print(ahc_inb(ahc, SCB_CONTROL),
+ &cur_col, 60);
+ ahc_scb_tag_print(ahc_inb(ahc, SCB_TAG), &cur_col, 60);
+ printf(")");
}
}
printf("\n");
diff --git a/sys/dev/aic7xxx/aic7xxx.h b/sys/dev/aic7xxx/aic7xxx.h
index 054512a..12acab3 100644
--- a/sys/dev/aic7xxx/aic7xxx.h
+++ b/sys/dev/aic7xxx/aic7xxx.h
@@ -37,7 +37,7 @@
* IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGES.
*
- * $Id: //depot/aic7xxx/aic7xxx/aic7xxx.h#51 $
+ * $Id: //depot/aic7xxx/aic7xxx/aic7xxx.h#62 $
*
* $FreeBSD$
*/
@@ -534,10 +534,21 @@ typedef enum {
SCB_RECOVERY_SCB = 0x0020,
SCB_AUTO_NEGOTIATE = 0x0040,/* Negotiate to achieve goal. */
SCB_NEGOTIATE = 0x0080,/* Negotiation forced for command. */
- SCB_ABORT = 0x1000,
- SCB_UNTAGGEDQ = 0x2000,
- SCB_ACTIVE = 0x4000,
- SCB_TARGET_IMMEDIATE = 0x8000
+ SCB_ABORT = 0x0100,
+ SCB_UNTAGGEDQ = 0x0200,
+ SCB_ACTIVE = 0x0400,
+ SCB_TARGET_IMMEDIATE = 0x0800,
+ SCB_TRANSMISSION_ERROR = 0x1000,/*
+ * We detected a parity or CRC
+ * error that has effected the
+ * payload of the command. This
+ * flag is checked when normal
+ * status is returned to catch
+ * the case of a target not
+ * responding to our attempt
+ * to report the error.
+ */
+ SCB_TARGET_SCB = 0x2000
} scb_flag;
struct scb {
@@ -662,6 +673,11 @@ struct ahc_tmode_lstate;
#define AHC_TRANS_GOAL 0x04 /* Modify negotiation goal */
#define AHC_TRANS_USER 0x08 /* Modify user negotiation settings */
+#define AHC_WIDTH_UNKNOWN 0xFF
+#define AHC_PERIOD_UNKNOWN 0xFF
+#define AHC_OFFSET_UNKNOWN 0x0
+#define AHC_PPR_OPTS_UNKNOWN 0xFF
+
/*
* Transfer Negotiation Information.
*/
@@ -716,6 +732,10 @@ struct ahc_syncrate {
char *rate;
};
+/* Safe and valid period for async negotiations. */
+#define AHC_ASYNC_XFER_PERIOD 0x44
+#define AHC_ULTRA2_XFER_PERIOD 0x0a
+
/*
* Indexes into our table of syncronous transfer rates.
*/
@@ -797,7 +817,7 @@ struct seeprom_config {
#define CFSEAUTOTERM 0x0400 /* Ultra2 Perform secondary Auto Term*/
#define CFSELOWTERM 0x0800 /* Ultra2 secondary low term */
#define CFSEHIGHTERM 0x1000 /* Ultra2 secondary high term */
-#define CFDOMAINVAL 0x4000 /* Perform Domain Validation*/
+#define CFENABLEDV 0x4000 /* Perform Domain Validation*/
/*
* Bus Release Time, Host Adapter ID
@@ -864,6 +884,7 @@ struct ahc_suspend_state {
};
typedef void (*ahc_bus_intr_t)(struct ahc_softc *);
+typedef void ahc_callback_t (void *);
struct ahc_softc {
bus_space_tag_t tag;
@@ -1016,6 +1037,9 @@ struct ahc_softc {
/* PCI cacheline size. */
u_int pci_cachesize;
+ u_int stack_size;
+ uint16_t *saved_stack;
+
/* Per-Unit descriptive information */
const char *description;
char *name;
@@ -1088,6 +1112,7 @@ void ahc_busy_tcl(struct ahc_softc *ahc,
struct ahc_pci_identity *ahc_find_pci_device(ahc_dev_softc_t);
int ahc_pci_config(struct ahc_softc *,
struct ahc_pci_identity *);
+int ahc_pci_test_register_access(struct ahc_softc *);
/*************************** EISA/VL Front End ********************************/
struct aic7770_identity *aic7770_find_device(uint32_t);
@@ -1186,11 +1211,20 @@ void ahc_validate_width(struct ahc_softc *ahc,
struct ahc_initiator_tinfo *tinfo,
u_int *bus_width,
role_t role);
+/*
+ * Negotiation types. These are used to qualify if we should renegotiate
+ * even if our goal and current transport parameters are identical.
+ */
+typedef enum {
+ AHC_NEG_TO_GOAL, /* Renegotiate only if goal and curr differ. */
+ AHC_NEG_IF_NON_ASYNC, /* Renegotiate so long as goal is non-async. */
+ AHC_NEG_ALWAYS /* Renegotiat even if goal is async. */
+} ahc_neg_type;
int ahc_update_neg_request(struct ahc_softc*,
struct ahc_devinfo*,
struct ahc_tmode_tstate*,
struct ahc_initiator_tinfo*,
- int /*force*/);
+ ahc_neg_type);
void ahc_set_width(struct ahc_softc *ahc,
struct ahc_devinfo *devinfo,
u_int width, u_int type, int paused);
@@ -1234,6 +1268,7 @@ extern uint32_t ahc_debug;
#define AHC_SHOW_TERMCTL 0x0008
#define AHC_SHOW_MEMORY 0x0010
#define AHC_SHOW_MESSAGES 0x0020
+#define AHC_SHOW_DV 0x0040
#define AHC_SHOW_SELTO 0x0080
#define AHC_SHOW_QFULL 0x0200
#define AHC_SHOW_QUEUE 0x0400
@@ -1241,6 +1276,8 @@ extern uint32_t ahc_debug;
#define AHC_DEBUG_SEQUENCER 0x1000
#endif
void ahc_print_scb(struct scb *scb);
+void ahc_print_devinfo(struct ahc_softc *ahc,
+ struct ahc_devinfo *dev);
void ahc_dump_card_state(struct ahc_softc *ahc);
int ahc_print_register(ahc_reg_parse_entry_t *table,
u_int num_entries,
diff --git a/sys/dev/aic7xxx/aic7xxx.reg b/sys/dev/aic7xxx/aic7xxx.reg
index eef3b13..b3e4456 100644
--- a/sys/dev/aic7xxx/aic7xxx.reg
+++ b/sys/dev/aic7xxx/aic7xxx.reg
@@ -39,7 +39,7 @@
*
* $FreeBSD$
*/
-VERSION = "$Id: //depot/aic7xxx/aic7xxx/aic7xxx.reg#34 $"
+VERSION = "$Id: //depot/aic7xxx/aic7xxx/aic7xxx.reg#35 $"
/*
* This file is processed by the aic7xxx_asm utility for use in assembling
@@ -804,7 +804,7 @@ register INTSTAT {
field SEQINT 0x01
mask BAD_PHASE SEQINT /* unknown scsi bus phase */
mask SEND_REJECT 0x10|SEQINT /* sending a message reject */
- mask NO_IDENT 0x20|SEQINT /* no IDENTIFY after reconnect*/
+ mask PROTO_VIOLATION 0x20|SEQINT /* SCSI protocol violation */
mask NO_MATCH 0x30|SEQINT /* no cmd match for reconnect */
mask IGN_WIDE_RES 0x40|SEQINT /* Complex IGN Wide Res Msg */
mask PDATA_REINIT 0x50|SEQINT /*
@@ -1062,6 +1062,7 @@ scb {
SCB_CONTROL {
size 1
field TARGET_SCB 0x80
+ field STATUS_RCVD 0x80
field DISCENB 0x40
field TAG_ENB 0x20
field MK_MESSAGE 0x10
@@ -1335,7 +1336,8 @@ scratch_ram {
}
SEQ_FLAGS {
size 1
- field IDENTIFY_SEEN 0x80
+ field NOT_IDENTIFIED 0x80
+ field NO_CDB_SENT 0x40
field TARGET_CMD_IS_TAGGED 0x40
field DPHASE 0x20
/* Target flags */
diff --git a/sys/dev/aic7xxx/aic7xxx.seq b/sys/dev/aic7xxx/aic7xxx.seq
index cf850c3..b784635 100644
--- a/sys/dev/aic7xxx/aic7xxx.seq
+++ b/sys/dev/aic7xxx/aic7xxx.seq
@@ -40,7 +40,7 @@
* $FreeBSD$
*/
-VERSION = "$Id: //depot/aic7xxx/aic7xxx/aic7xxx.seq#50 $"
+VERSION = "$Id: //depot/aic7xxx/aic7xxx/aic7xxx.seq#52 $"
PATCH_ARG_LIST = "struct ahc_softc *ahc"
PREFIX = "ahc_"
@@ -306,7 +306,7 @@ ident_messages_done:
} else {
mvi DFDAT, SCB_LIST_NULL;
}
- or SEQ_FLAGS, TARG_CMD_PENDING|IDENTIFY_SEEN;
+ mvi SEQ_FLAGS, TARG_CMD_PENDING;
test SEQ_FLAGS2, TARGET_MSG_PENDING
jnz target_mesgout_pending;
test SCSISIGI, ATNI jnz target_mesgout_continue;
@@ -648,7 +648,7 @@ initiator_select:
* asserted.
*/
mvi MSG_OUT, MSG_IDENTIFYFLAG;
- or SEQ_FLAGS, IDENTIFY_SEEN;
+ mvi SEQ_FLAGS, NO_CDB_SENT;
mvi CLRSINT0, CLRSELDO;
/*
@@ -707,7 +707,7 @@ clear_target_state:
}
mvi LASTPHASE, P_BUSFREE;
/* clear target specific flags */
- clr SEQ_FLAGS ret;
+ mvi SEQ_FLAGS, NOT_IDENTIFIED|NO_CDB_SENT ret;
sg_advance:
clr A; /* add sizeof(struct scatter) */
@@ -821,9 +821,9 @@ calc_mwi_residual_final:
}
p_data:
- test SEQ_FLAGS,IDENTIFY_SEEN jnz p_data_okay;
- mvi NO_IDENT jmp set_seqint;
-p_data_okay:
+ test SEQ_FLAGS,NOT_IDENTIFIED|NO_CDB_SENT jz p_data_allowed;
+ mvi PROTO_VIOLATION call set_seqint;
+p_data_allowed:
if ((ahc->features & AHC_ULTRA2) != 0) {
mvi DMAPARAMS, PRELOADEN|SCSIEN|HDMAEN;
} else {
@@ -1367,8 +1367,8 @@ if ((ahc->flags & AHC_INITIATORROLE) != 0) {
* Command phase. Set up the DMA registers and let 'er rip.
*/
p_command:
- test SEQ_FLAGS,IDENTIFY_SEEN jnz p_command_okay;
- mvi NO_IDENT jmp set_seqint;
+ test SEQ_FLAGS, NOT_IDENTIFIED jz p_command_okay;
+ mvi PROTO_VIOLATION call set_seqint;
p_command_okay:
if ((ahc->features & AHC_ULTRA2) != 0) {
@@ -1400,7 +1400,7 @@ p_command_from_host:
}
mvi DFCNTRL, (SCSIEN|SDMAEN|HDMAEN|DIRECTION|FIFORESET);
}
- jmp p_command_loop;
+ jmp p_command_xfer;
p_command_embedded:
/*
* The data fifo seems to require 4 byte aligned
@@ -1438,24 +1438,28 @@ p_command_embedded:
call copy_to_fifo_6;
or DFCNTRL, FIFOFLUSH;
}
-p_command_loop:
+p_command_xfer:
+ and SEQ_FLAGS, ~NO_CDB_SENT;
if ((ahc->features & AHC_DT) == 0) {
test SSTAT0, SDONE jnz . + 2;
- test SSTAT1, PHASEMIS jz p_command_loop;
+ test SSTAT1, PHASEMIS jz . - 1;
/*
* Wait for our ACK to go-away on it's own
* instead of being killed by SCSIEN getting cleared.
*/
test SCSISIGI, ACKI jnz .;
} else {
- test DFCNTRL, SCSIEN jnz p_command_loop;
+ test DFCNTRL, SCSIEN jnz .;
}
+ test SSTAT0, SDONE jnz p_command_successful;
+ /*
+ * Don't allow a data phase if the command
+ * was not fully transferred.
+ */
+ or SEQ_FLAGS, NO_CDB_SENT;
+p_command_successful:
and DFCNTRL, ~(SCSIEN|SDMAEN|HDMAEN);
test DFCNTRL, (SCSIEN|SDMAEN|HDMAEN) jnz .;
- if ((ahc->features & AHC_ULTRA2) != 0) {
- /* Drop any residual from the S/G Preload queue */
- or SXFRCTL0, CLRSTCNT;
- }
jmp ITloop;
/*
@@ -1463,10 +1467,10 @@ p_command_loop:
* and store it into the SCB.
*/
p_status:
- test SEQ_FLAGS,IDENTIFY_SEEN jnz p_status_okay;
- mvi NO_IDENT jmp set_seqint;
+ test SEQ_FLAGS, NOT_IDENTIFIED jnz mesgin_proto_violation;
p_status_okay:
mov SCB_SCSI_STATUS, SCSIDATL;
+ or SCB_CONTROL, STATUS_RCVD;
jmp ITloop;
/*
@@ -1587,13 +1591,15 @@ if ((ahc->features & AHC_WIDE) != 0) {
jmp mesgin_done;
}
+mesgin_proto_violation:
+ mvi PROTO_VIOLATION call set_seqint;
+ jmp mesgin_done;
mesgin_reject:
mvi MSG_MESSAGE_REJECT call mk_mesg;
mesgin_done:
mov NONE,SCSIDATL; /*dummy read from latch to ACK*/
jmp ITloop;
-mesgin_complete:
/*
* We received a "command complete" message. Put the SCB_TAG into the QOUTFIFO,
* and trigger a completion interrupt. Before doing so, check to see if there
@@ -1606,27 +1612,49 @@ mesgin_complete:
* it to the QINFIFO and tell us not to post to the QOUTFIFO by setting
* RETURN_1 to SEND_SENSE.
*/
+mesgin_complete:
-/*
- * If ATN is raised, we still want to give the target a message.
- * Perhaps there was a parity error on this last message byte.
- * Either way, the target should take us to message out phase
- * and then attempt to complete the command again. We should use a
- * critical section here to guard against a timeout triggering
- * for this command and setting ATN while we are still processing
- * the completion.
+ /*
+ * If ATN is raised, we still want to give the target a message.
+ * Perhaps there was a parity error on this last message byte.
+ * Either way, the target should take us to message out phase
+ * and then attempt to complete the command again. We should use a
+ * critical section here to guard against a timeout triggering
+ * for this command and setting ATN while we are still processing
+ * the completion.
test SCSISIGI, ATNI jnz mesgin_done;
- */
+ */
-/*
- * See if we attempted to deliver a message but the target ingnored us.
- */
+ /*
+ * If we are identified and have successfully sent the CDB,
+ * any status will do. Optimize this fast path.
+ */
+ test SCB_CONTROL, STATUS_RCVD jz mesgin_proto_violation;
+ test SEQ_FLAGS, NOT_IDENTIFIED|NO_CDB_SENT jz complete_accepted;
+
+ /*
+ * If the target never sent an identify message but instead went
+ * to mesgin to give an invalid message, let the host abort us.
+ */
+ test SEQ_FLAGS, NOT_IDENTIFIED jnz mesgin_proto_violation;
+
+ /*
+ * If we recevied good status but never successfully sent the
+ * cdb, abort the command.
+ */
+ test SCB_SCSI_STATUS,0xff jnz complete_accepted;
+ test SEQ_FLAGS, NO_CDB_SENT jnz mesgin_proto_violation;
+
+complete_accepted:
+ /*
+ * See if we attempted to deliver a message but the target ingnored us.
+ */
test SCB_CONTROL, MK_MESSAGE jz . + 2;
mvi MKMSG_FAILED call set_seqint;
-/*
- * Check for residuals
- */
+ /*
+ * Check for residuals
+ */
test SCB_SGPTR, SG_LIST_NULL jnz check_status;/* No xfer */
test SCB_SGPTR, SG_FULL_RESID jnz upload_scb;/* Never xfered */
test SCB_RESIDUAL_SGPTR, SG_LIST_NULL jz upload_scb;
@@ -1679,7 +1707,8 @@ mesgin_disconnect:
* XXX - Wait for more testing.
test SCSISIGI, ATNI jnz mesgin_done;
*/
-
+ test SEQ_FLAGS, NOT_IDENTIFIED|NO_CDB_SENT
+ jnz mesgin_proto_violation;
or SCB_CONTROL,DISCONNECTED;
if ((ahc->flags & AHC_PAGESCBS) != 0) {
call add_scb_to_disc_list;
@@ -1747,7 +1776,8 @@ mesgin_sdptrs_full:
* Restore pointers message? Data pointers are recopied from the
* 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.
+ * code do the rest. We also reset/reallocate the FIFO to make
+ * sure we have a clean start for the next data or command phase.
*/
mesgin_rdptrs:
and SEQ_FLAGS, ~DPHASE; /*
@@ -1755,6 +1785,7 @@ mesgin_rdptrs:
* the next time through
* the dataphase.
*/
+ or SXFRCTL0, CLRSTCNT|CLRCHN;
jmp mesgin_done;
/*
@@ -1903,7 +1934,7 @@ setup_SCB_id_lun_okay:
mov SCBPTR, A;
}
setup_SCB_tagged:
- mvi SEQ_FLAGS,IDENTIFY_SEEN; /* make note of IDENTIFY */
+ clr SEQ_FLAGS; /* make note of IDENTIFY */
call set_transfer_settings;
/* See if the host wants to send a message upon reconnection */
test SCB_CONTROL, MK_MESSAGE jz mesgin_done;
OpenPOWER on IntegriCloud