diff options
author | gibbs <gibbs@FreeBSD.org> | 1998-12-10 04:14:50 +0000 |
---|---|---|
committer | gibbs <gibbs@FreeBSD.org> | 1998-12-10 04:14:50 +0000 |
commit | 3e9f8a5d64d6121ee52e602908a9e59084338daf (patch) | |
tree | abec1e1a9cc5f48573ff23427cdfdcbd064d62c1 /sys/dev/aic7xxx | |
parent | a9b7180f827f980eb3ef7952855b0f5a5915beb1 (diff) | |
download | FreeBSD-src-3e9f8a5d64d6121ee52e602908a9e59084338daf.zip FreeBSD-src-3e9f8a5d64d6121ee52e602908a9e59084338daf.tar.gz |
Revamp the way that exceptional message handling is performed so that it
is more robust and common code can be used for both the target and iniator
roles. The mechanism for tracking negotiation state has also been simplified.
Add support for sync/wide negotiation in target mode and fix many of
the target mode bugs running at higher speeds uncovered. Make a first
stab at getting all of the bus skew delays correct. Sync+Wide dataout
transfers still cause problems, but this may be an initiator problem.
Ensure that we exit BITBUCKET mode if the controller is restarted.
Add support for target mode only firmware downloads. This has been
tested on the aic7880, but should mean that we can perform target mode
on any aic7xxx controller. Mixed mode (initiator and target roles in
the same firmware load) is currently only supported on the aic7890, but
with optimization, may fit on chips with less instruction space.
Diffstat (limited to 'sys/dev/aic7xxx')
-rw-r--r-- | sys/dev/aic7xxx/aic7xxx.c | 1432 | ||||
-rw-r--r-- | sys/dev/aic7xxx/aic7xxx.h | 28 | ||||
-rw-r--r-- | sys/dev/aic7xxx/aic7xxx.reg | 37 | ||||
-rw-r--r-- | sys/dev/aic7xxx/aic7xxx.seq | 273 | ||||
-rw-r--r-- | sys/dev/aic7xxx/aicasm/aicasm_gram.y | 6 | ||||
-rw-r--r-- | sys/dev/aic7xxx/aicasm_gram.y | 6 |
6 files changed, 1118 insertions, 664 deletions
diff --git a/sys/dev/aic7xxx/aic7xxx.c b/sys/dev/aic7xxx/aic7xxx.c index aec2bfb..c5ffbf2 100644 --- a/sys/dev/aic7xxx/aic7xxx.c +++ b/sys/dev/aic7xxx/aic7xxx.c @@ -36,7 +36,7 @@ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * - * $Id: aic7xxx.c,v 1.10 1998/11/23 01:33:46 gibbs Exp $ + * $Id: aic7xxx.c,v 1.11 1998/12/04 22:54:44 archie Exp $ */ /* * A few notes on features of the driver. @@ -133,9 +133,8 @@ #define MAX(a,b) (((a) > (b)) ? (a) : (b)) #define MIN(a,b) (((a) < (b)) ? (a) : (b)) -#define ALL_TARGETS (~0) -#define ALL_LUNS (~0) #define ALL_CHANNELS '\0' +#define ALL_TARGETS_MASK 0xFFFF #define SIM_IS_SCSIBUS_B(ahc, sim) \ (sim == ahc->sim_b) @@ -155,11 +154,22 @@ #define ccb_scb_ptr spriv_ptr0 #define ccb_ahc_ptr spriv_ptr1 +typedef enum { + ROLE_UNKNOWN, + ROLE_INITIATOR, + ROLE_TARGET, +} role_t; + struct ahc_devinfo { int target_offset; u_int16_t target_mask; u_int8_t target; + u_int8_t lun; char channel; + role_t role; /* + * Only guaranteed to be correct if not + * in the busfree state. + */ }; typedef enum { @@ -204,16 +214,28 @@ static struct scb * static void ahc_fetch_devinfo(struct ahc_softc *ahc, struct ahc_devinfo *devinfo); static void ahc_compile_devinfo(struct ahc_devinfo *devinfo, - u_int target, char channel); + u_int target, u_int lun, char channel, + role_t role); static u_int ahc_abort_wscb(struct ahc_softc *ahc, u_int scbpos, u_int prev); static void ahc_done(struct ahc_softc *ahc, struct scb *scbp); static void ahc_handle_target_cmd(struct ahc_softc *ahc, struct target_cmd *cmd); static void ahc_handle_seqint(struct ahc_softc *ahc, u_int intstat); static void ahc_handle_scsiint(struct ahc_softc *ahc, u_int intstat); -static void ahc_handle_reqinit(struct ahc_softc *ahc, - struct scb *scb); -static int ahc_parse_msg(struct ahc_softc *ahc, struct scb *scb, +static void ahc_build_transfer_msg(struct ahc_softc *ahc, + struct ahc_devinfo *devinfo); +static void ahc_setup_initiator_msgout(struct ahc_softc *ahc, + struct ahc_devinfo *devinfo, + struct scb *scb); +static void ahc_setup_target_msgin(struct ahc_softc *ahc, + struct ahc_devinfo *devinfo); +static void ahc_handle_msg_reject(struct ahc_softc *ahc, + struct ahc_devinfo *devinfo); +static void ahc_clear_msg_state(struct ahc_softc *ahc); +static void ahc_handle_message_phase(struct ahc_softc *ahc, + struct cam_path *path); +static int ahc_sent_msg(struct ahc_softc *ahc, u_int msgtype, int full); +static int ahc_parse_msg(struct ahc_softc *ahc, struct cam_path *path, struct ahc_devinfo *devinfo); static void ahc_handle_devreset(struct ahc_softc *ahc, int target, char channel, cam_status status, @@ -246,6 +268,8 @@ static void ahc_add_curscb_to_free_list(struct ahc_softc *ahc); static void ahc_clear_intstat(struct ahc_softc *ahc); static void ahc_reset_current_bus(struct ahc_softc *ahc); static struct ahc_syncrate * + ahc_devlimited_syncrate(struct ahc_softc *ahc, u_int *period); +static struct ahc_syncrate * ahc_find_syncrate(struct ahc_softc *ahc, u_int *period, u_int maxsync); static u_int ahc_find_period(struct ahc_softc *ahc, u_int scsirate, @@ -253,6 +277,13 @@ static u_int ahc_find_period(struct ahc_softc *ahc, u_int scsirate, static void ahc_validate_offset(struct ahc_softc *ahc, struct ahc_syncrate *syncrate, u_int *offset, int wide); +static void ahc_update_target_msg_request(struct ahc_softc *ahc, + struct ahc_devinfo *devinfo, + struct ahc_target_tinfo *tinfo, + int force); +static int ahc_create_path(struct ahc_softc *ahc, + struct ahc_devinfo *devinfo, + struct cam_path **path); static void ahc_set_syncrate(struct ahc_softc *ahc, struct ahc_devinfo *devinfo, struct cam_path *path, @@ -274,6 +305,7 @@ static void ahc_set_recoveryscb(struct ahc_softc *ahc, struct scb *scb); static timeout_t ahc_timeout; +static __inline int sequencer_paused(struct ahc_softc *ahc); static __inline void pause_sequencer(struct ahc_softc *ahc); static __inline void unpause_sequencer(struct ahc_softc *ahc, int unpause_always); @@ -296,6 +328,12 @@ ahc_hscb_busaddr(struct ahc_softc *ahc, u_int index) #define AHC_BUSRESET_DELAY 25 /* Reset delay in us */ +static __inline int +sequencer_paused(struct ahc_softc *ahc) +{ + return ((ahc_inb(ahc, HCNTRL) & PAUSE) != 0); +} + static __inline void pause_sequencer(struct ahc_softc *ahc) { @@ -305,16 +343,15 @@ pause_sequencer(struct ahc_softc *ahc) * Since the sequencer can disable pausing in a critical section, we * must loop until it actually stops. */ - while ((ahc_inb(ahc, HCNTRL) & PAUSE) == 0) + while (sequencer_paused(ahc) == 0) ; } static __inline void unpause_sequencer(struct ahc_softc *ahc, int unpause_always) { - if ((ahc->flags & AHC_HANDLING_REQINITS) == 0 - && (unpause_always - || (ahc_inb(ahc, INTSTAT) & (SCSIINT | SEQINT | BRKADRINT)) == 0)) + if (unpause_always + || (ahc_inb(ahc, INTSTAT) & (SCSIINT | SEQINT | BRKADRINT)) == 0) ahc_outb(ahc, HCNTRL, ahc->unpause); } @@ -560,6 +597,30 @@ ahc_reset(struct ahc_softc *ahc) } /* + * Called when we have an active connection to a target on the bus, + * this function finds the nearest syncrate to the input period limited + * by the capabilities of the bus connectivity of the target. + */ +static struct ahc_syncrate * +ahc_devlimited_syncrate(struct ahc_softc *ahc, u_int *period) { + u_int maxsync; + + if ((ahc->features & AHC_ULTRA2) != 0) { + if ((ahc_inb(ahc, SBLKCTL) & ENAB40) != 0 + && (ahc_inb(ahc, SSTAT2) & EXP_ACTIVE) == 0) { + maxsync = AHC_SYNCRATE_ULTRA2; + } else { + maxsync = AHC_SYNCRATE_ULTRA; + } + } else if ((ahc->features & AHC_ULTRA) != 0) { + maxsync = AHC_SYNCRATE_ULTRA; + } else { + maxsync = AHC_SYNCRATE_FAST; + } + return (ahc_find_syncrate(ahc, period, maxsync)); +} + +/* * Look up the valid period to SCSIRATE conversion in our table. * Return the period and offset that should be sent to the target * if this was the beginning of an SDTR. @@ -653,10 +714,63 @@ ahc_validate_offset(struct ahc_softc *ahc, struct ahc_syncrate *syncrate, } static void +ahc_update_target_msg_request(struct ahc_softc *ahc, + struct ahc_devinfo *devinfo, + struct ahc_target_tinfo *tinfo, + int force) +{ + int paused; + u_int targ_msg_req_orig; + + targ_msg_req_orig = ahc->targ_msg_req; + if (tinfo->current.period != tinfo->goal.period + || tinfo->current.width != tinfo->goal.width + || (force + && (tinfo->goal.period != 0 + || tinfo->goal.width != MSG_EXT_WDTR_BUS_8_BIT))) + ahc->targ_msg_req |= devinfo->target_mask; + else + ahc->targ_msg_req &= ~devinfo->target_mask; + + if (ahc->targ_msg_req != targ_msg_req_orig) { + /* Update the message request bit for this target */ + paused = sequencer_paused(ahc); + + if (!paused) + pause_sequencer(ahc); + + ahc_outb(ahc, TARGET_MSG_REQUEST, ahc->targ_msg_req & 0xFF); + ahc_outb(ahc, TARGET_MSG_REQUEST + 1, + (ahc->targ_msg_req >> 8) & 0xFF); + + if (!paused) + unpause_sequencer(ahc, /*unpause always*/FALSE); + } +} + +static int +ahc_create_path(struct ahc_softc *ahc, struct ahc_devinfo *devinfo, + struct cam_path **path) +{ + path_id_t path_id; + int error; + + if (devinfo->channel == 'B') + path_id = cam_sim_path(ahc->sim_b); + else + path_id = cam_sim_path(ahc->sim); + + return (xpt_create_path(path, /*periph*/NULL, + path_id, devinfo->target, + devinfo->lun)); +} + +static void ahc_set_syncrate(struct ahc_softc *ahc, struct ahc_devinfo *devinfo, struct cam_path *path, struct ahc_syncrate *syncrate, u_int period, u_int offset, u_int type) { + struct ahc_target_tinfo *tinfo; u_int old_period; u_int old_offset; @@ -665,15 +779,16 @@ ahc_set_syncrate(struct ahc_softc *ahc, struct ahc_devinfo *devinfo, offset = 0; } - old_period = ahc->transinfo[devinfo->target_offset].current.period; - old_offset = ahc->transinfo[devinfo->target_offset].current.offset; + tinfo = &ahc->transinfo[devinfo->target_offset]; + old_period = tinfo->current.period; + old_offset = tinfo->current.offset; if ((type & AHC_TRANS_CUR) != 0 && (old_period != period || old_offset != offset)) { - struct ccb_trans_settings neg; + struct cam_path *path2; u_int scsirate; - scsirate = ahc->transinfo[devinfo->target_offset].scsirate; + scsirate = tinfo->scsirate; if ((ahc->features & AHC_ULTRA2) != 0) { scsirate &= ~SXFR_ULTRA2; @@ -682,11 +797,8 @@ ahc_set_syncrate(struct ahc_softc *ahc, struct ahc_devinfo *devinfo, scsirate |= syncrate->sxfr_ultra2; } - if ((type & AHC_TRANS_ACTIVE) == AHC_TRANS_ACTIVE) { + if ((type & AHC_TRANS_ACTIVE) == AHC_TRANS_ACTIVE) ahc_outb(ahc, SCSIOFFSET, offset); - } - ahc_outb(ahc, TARG_OFFSET + devinfo->target_offset, - offset); } else { scsirate &= ~(SXFR|SOFS); @@ -715,25 +827,45 @@ ahc_set_syncrate(struct ahc_softc *ahc, struct ahc_devinfo *devinfo, if ((type & AHC_TRANS_ACTIVE) == AHC_TRANS_ACTIVE) ahc_outb(ahc, SCSIRATE, scsirate); - ahc->transinfo[devinfo->target_offset].scsirate = scsirate; - ahc->transinfo[devinfo->target_offset].current.period = period; - ahc->transinfo[devinfo->target_offset].current.offset = offset; + tinfo->scsirate = scsirate; + tinfo->current.period = period; + tinfo->current.offset = offset; /* Update the syncrates in any pending scbs */ ahc_update_pending_syncrates(ahc); /* - * Tell the SCSI layer about the + * If possible, tell the SCSI layer about the * new transfer parameters. */ - neg.sync_period = period; - neg.sync_offset = offset; - neg.valid = CCB_TRANS_SYNC_RATE_VALID - | CCB_TRANS_SYNC_OFFSET_VALID; - xpt_setup_ccb(&neg.ccb_h, path, /*priority*/1); - xpt_async(AC_TRANSFER_NEG, path, &neg); + /* If possible, update the XPT's notion of our transfer rate */ + path2 = NULL; + if (path == NULL) { + int error; + + error = ahc_create_path(ahc, devinfo, &path2); + if (error == CAM_REQ_CMP) + path = path2; + else + path2 = NULL; + } + + if (path != NULL) { + struct ccb_trans_settings neg; + + neg.sync_period = period; + neg.sync_offset = offset; + neg.valid = CCB_TRANS_SYNC_RATE_VALID + | CCB_TRANS_SYNC_OFFSET_VALID; + xpt_setup_ccb(&neg.ccb_h, path, /*priority*/1); + xpt_async(AC_TRANSFER_NEG, path, &neg); + } + + if (path2 != NULL) + xpt_free_path(path2); + if (bootverbose) { - if (neg.sync_offset != 0) { + if (offset != 0) { printf("%s: target %d synchronous at %sMHz, " "offset = 0x%x\n", ahc_name(ahc), devinfo->target, syncrate->rate, offset); @@ -746,57 +878,80 @@ ahc_set_syncrate(struct ahc_softc *ahc, struct ahc_devinfo *devinfo, } if ((type & AHC_TRANS_GOAL) != 0) { - ahc->transinfo[devinfo->target_offset].goal.period = period; - ahc->transinfo[devinfo->target_offset].goal.offset = offset; + tinfo->goal.period = period; + tinfo->goal.offset = offset; } if ((type & AHC_TRANS_USER) != 0) { - ahc->transinfo[devinfo->target_offset].user.period = period; - ahc->transinfo[devinfo->target_offset].user.offset = offset; + tinfo->user.period = period; + tinfo->user.offset = offset; } + + ahc_update_target_msg_request(ahc, devinfo, tinfo, /*force*/FALSE); } static void ahc_set_width(struct ahc_softc *ahc, struct ahc_devinfo *devinfo, struct cam_path *path, u_int width, u_int type) { - u_int oldwidth; + struct ahc_target_tinfo *tinfo; + u_int oldwidth; - oldwidth = ahc->transinfo[devinfo->target_offset].current.width; + tinfo = &ahc->transinfo[devinfo->target_offset]; + oldwidth = tinfo->current.width; if ((type & AHC_TRANS_CUR) != 0 && oldwidth != width) { - struct ccb_trans_settings neg; + struct cam_path *path2; u_int scsirate; - scsirate = ahc->transinfo[devinfo->target_offset].scsirate; + scsirate = tinfo->scsirate; scsirate &= ~WIDEXFER; if (width == MSG_EXT_WDTR_BUS_16_BIT) scsirate |= WIDEXFER; - ahc->transinfo[devinfo->target_offset].scsirate = scsirate; + tinfo->scsirate = scsirate; if ((type & AHC_TRANS_ACTIVE) == AHC_TRANS_ACTIVE) ahc_outb(ahc, SCSIRATE, scsirate); - ahc->transinfo[devinfo->target_offset].current.width = width; + tinfo->current.width = width; + + /* If possible, update the XPT's notion of our transfer rate */ + path2 = NULL; + if (path == NULL) { + int error; + + error = ahc_create_path(ahc, devinfo, &path2); + if (error == CAM_REQ_CMP) + path = path2; + else + path2 = NULL; + } + + if (path != NULL) { + struct ccb_trans_settings neg; + + neg.bus_width = width; + neg.valid = CCB_TRANS_BUS_WIDTH_VALID; + xpt_setup_ccb(&neg.ccb_h, path, /*priority*/1); + xpt_async(AC_TRANSFER_NEG, path, &neg); + } + + if (path2 != NULL) + xpt_free_path(path2); - /* Tell the SCSI layer about the new transfer params */ - neg.bus_width = width; - neg.valid = CCB_TRANS_BUS_WIDTH_VALID; - xpt_setup_ccb(&neg.ccb_h, path, /*priority*/1); - xpt_async(AC_TRANSFER_NEG, path, &neg); if (bootverbose) { printf("%s: target %d using %dbit transfers\n", ahc_name(ahc), devinfo->target, - 8 * (0x01 << neg.bus_width)); + 8 * (0x01 << width)); } } - if ((type & AHC_TRANS_GOAL) != 0) { - ahc->transinfo[devinfo->target_offset].goal.width = width; - } - if ((type & AHC_TRANS_USER) != 0) { - ahc->transinfo[devinfo->target_offset].user.width = width; - } + if ((type & AHC_TRANS_GOAL) != 0) + tinfo->goal.width = width; + if ((type & AHC_TRANS_USER) != 0) + tinfo->user.width = width; + + ahc_update_target_msg_request(ahc, devinfo, tinfo, /*force*/FALSE); } /* @@ -928,19 +1083,29 @@ fail: static void ahc_fetch_devinfo(struct ahc_softc *ahc, struct ahc_devinfo *devinfo) { - u_int saved_tcl; + u_int saved_tcl; + role_t role; + + if (ahc_inb(ahc, SSTAT0) & TARGET) + role = ROLE_TARGET; + else + role = ROLE_INITIATOR; saved_tcl = ahc_inb(ahc, SAVED_TCL); ahc_compile_devinfo(devinfo, (saved_tcl >> 4) & 0x0f, - (saved_tcl & SELBUSB) ? 'B': 'A'); + saved_tcl & 0x3, (saved_tcl & SELBUSB) ? 'B': 'A', + role); } static void -ahc_compile_devinfo(struct ahc_devinfo *devinfo, u_int target, char channel) +ahc_compile_devinfo(struct ahc_devinfo *devinfo, u_int target, u_int lun, + char channel, role_t role) { devinfo->target = target; + devinfo->lun = lun; devinfo->target_offset = target; devinfo->channel = channel; + devinfo->role = role; if (channel == 'B') devinfo->target_offset += 8; devinfo->target_mask = (0x01 << devinfo->target_offset); @@ -1035,8 +1200,8 @@ ahc_intr(void *arg) (ahc_inb(ahc, SEQADDR1) << 8)); /* Tell everyone that this HBA is no longer availible */ - ahc_abort_scbs(ahc, ALL_TARGETS, ALL_CHANNELS, - ALL_LUNS, SCB_LIST_NULL, CAM_NO_HBA); + ahc_abort_scbs(ahc, CAM_TARGET_WILDCARD, ALL_CHANNELS, + CAM_LUN_WILDCARD, SCB_LIST_NULL, CAM_NO_HBA); } if (intstat & SEQINT) ahc_handle_seqint(ahc, intstat); @@ -1060,11 +1225,6 @@ ahc_handle_target_cmd(struct ahc_softc *ahc, struct target_cmd *cmd) target = cmd->targ_id; lun = (cmd->identify & MSG_IDENTIFY_LUNMASK); - xpt_print_path(ahc->path); - printf("Received Target Command (%d:%d:%d)\n", - initiator, target, lun); - ahc_dump_targcmd(cmd); - byte = cmd->bytes; tstate = ahc->enabled_targets[target]; lstate = NULL; @@ -1139,8 +1299,6 @@ ahc_handle_target_cmd(struct ahc_softc *ahc, struct target_cmd *cmd) * continue target I/O comes in response * to this accept tio. */ - xpt_print_path(atio->ccb_h.path); - printf("Incoming Command did not disconnect %p\n", lstate); ahc->pending_device = lstate; } xpt_done((union ccb*)atio); @@ -1218,128 +1376,9 @@ ahc_handle_seqint(struct ahc_softc *ahc, u_int intstat) devinfo.target); } break; - case EXTENDED_MSG: - { - ahc->msg_type = MSG_TYPE_INITIATOR_MSGIN; - ahc->msg_len = 0; - ahc->msg_index = 0; - - /* - * To actually receive the message, simply turn on - * REQINIT interrupts and let our interrupt handler - * do the rest (REQINIT should already be true). - */ - ahc_outb(ahc, SIMODE1, ahc_inb(ahc, SIMODE1) | ENREQINIT); - ahc->flags |= AHC_HANDLING_REQINITS; - return; - } case REJECT_MSG: { - /* - * What we care about here is if we had an - * outstanding SDTR or WDTR message for this - * target. If we did, this is a signal that - * the target is refusing negotiation. - */ - u_int scb_index; - u_int last_msg; - - scb_index = ahc_inb(ahc, SCB_TAG); - scb = ahc->scb_data->scbarray[scb_index]; - - last_msg = ahc_inb(ahc, LAST_MSG); - - if ((last_msg == MSG_IDENTIFYFLAG - || last_msg == HOST_MSG) - && (scb->flags & SCB_MSGOUT_WDTR) != 0 - && (scb->flags & SCB_MSGOUT_SENT) != 0) { - struct ahc_target_tinfo *tinfo; - - /* note 8bit xfers and clear flag */ - printf("%s:%c:%d: refuses WIDE negotiation. Using " - "8bit transfers\n", ahc_name(ahc), - devinfo.channel, devinfo.target); - scb->flags &= ~SCB_MSGOUT_BITS; - ahc->wdtrpending &= ~devinfo.target_mask; - ahc_set_width(ahc, &devinfo, scb->ccb->ccb_h.path, - MSG_EXT_WDTR_BUS_8_BIT, - AHC_TRANS_ACTIVE|AHC_TRANS_GOAL); - ahc_set_syncrate(ahc, &devinfo, scb->ccb->ccb_h.path, - /*syncrate*/NULL, /*period*/0, - /*offset*/0, AHC_TRANS_ACTIVE); - tinfo = &ahc->transinfo[devinfo.target_offset]; - if (tinfo->goal.period) { - /* Start the sync negotiation */ - ahc->sdtrpending |= devinfo.target_mask; - scb->flags |= SCB_MSGOUT_SDTR; - ahc_outb(ahc, MSG_OUT, HOST_MSG); - ahc_outb(ahc, SCSISIGO, - ahc_inb(ahc, SCSISIGO) | ATNO); - } - } else if ((last_msg == MSG_IDENTIFYFLAG - || last_msg == HOST_MSG) - && (scb->flags & SCB_MSGOUT_SDTR) != 0 - && (scb->flags & SCB_MSGOUT_SENT) != 0) { - - /* note asynch xfers and clear flag */ - ahc_set_syncrate(ahc, &devinfo, scb->ccb->ccb_h.path, - /*syncrate*/NULL, /*period*/0, - /*offset*/0, - AHC_TRANS_ACTIVE|AHC_TRANS_GOAL); - scb->flags &= ~SCB_MSGOUT_BITS; - ahc->sdtrpending &= ~devinfo.target_mask; - printf("%s:%c:%d: refuses synchronous negotiation. " - "Using asynchronous transfers\n", - ahc_name(ahc), - devinfo.channel, devinfo.target); - } else if ((last_msg == MSG_IDENTIFYFLAG) - && (scb->hscb->control & MSG_SIMPLE_Q_TAG) != 0) { - struct ccb_trans_settings neg; - - printf("%s:%c:%d: refuses tagged commands. Performing " - "non-tagged I/O\n", ahc_name(ahc), - devinfo.channel, devinfo.target); - - ahc->tagenable &= ~devinfo.target_mask; - neg.flags = 0; - neg.valid = CCB_TRANS_TQ_VALID; - xpt_setup_ccb(&neg.ccb_h, scb->ccb->ccb_h.path, - /*priority*/1); - xpt_async(AC_TRANSFER_NEG, scb->ccb->ccb_h.path, &neg); - /* - * Resend the identify for this CCB as the target - * may believe that the selection is invalid otherwise. - */ - ahc_outb(ahc, SCB_CONTROL, ahc_inb(ahc, SCB_CONTROL) - & ~MSG_SIMPLE_Q_TAG); - scb->hscb->control &= ~MSG_SIMPLE_Q_TAG; - scb->ccb->ccb_h.flags &= ~CAM_TAG_ACTION_VALID; - ahc_outb(ahc, MSG_OUT, MSG_IDENTIFYFLAG); - ahc_outb(ahc, SCSISIGO, ahc_inb(ahc, SCSISIGO) | ATNO); - - /* - * Requeue all tagged commands for this target - * currently in our posession so they can be - * converted to untagged commands. - */ - ahc_search_qinfifo(ahc, SCB_TARGET(scb), - SCB_CHANNEL(scb), - SCB_LUN(scb), - /*tag*/SCB_LIST_NULL, - CAM_REQUEUE_REQ, - SEARCH_COMPLETE); - } else { - /* - * Otherwise, we ignore it. - */ -#ifdef AHC_DEBUG - if (ahc_debug & AHC_SHOWMISC) - printf("%s:%c:%d: Message reject -- ignored\n", - ahc_name(ahc), devinfo.channel, - devinfo.target); -#endif - break; - } + ahc_handle_msg_reject(ahc, &devinfo); break; } case BAD_STATUS: @@ -1444,31 +1483,17 @@ ahc_handle_seqint(struct ahc_softc *ahc, u_int intstat) * but due to the way we page SCBs, we can't. */ hscb->control = 0; + /* * This request sense could be because the * the device lost power or in some other * way has lost our transfer negotiations. * Renegotiate if appropriate. */ - ahc_set_width(ahc, &devinfo, - scb->ccb->ccb_h.path, - MSG_EXT_WDTR_BUS_8_BIT, - AHC_TRANS_CUR); - ahc_set_syncrate(ahc, &devinfo, - scb->ccb->ccb_h.path, - /*syncrate*/NULL, /*period*/0, - /*offset*/0, AHC_TRANS_CUR); - scb->flags &= ~SCB_MSGOUT_BITS; tinfo = &ahc->transinfo[devinfo.target_offset]; - if (tinfo->goal.width) { - ahc->wdtrpending |= devinfo.target_mask; - hscb->control |= MK_MESSAGE; - scb->flags |= SCB_MSGOUT_WDTR; - } else if (tinfo->goal.period) { - ahc->sdtrpending |= devinfo.target_mask; - hscb->control |= MK_MESSAGE; - scb->flags |= SCB_MSGOUT_SDTR; - } + ahc_update_target_msg_request(ahc, &devinfo, + tinfo, + /*force*/TRUE); hscb->status = 0; hscb->SG_count = 1; hscb->SG_pointer = scb->ahc_dmaphys; @@ -1511,21 +1536,6 @@ ahc_handle_seqint(struct ahc_softc *ahc, u_int intstat) } break; } - case TARGET_SYNC_CMD: - { - /* - * We've already processed the command. If the command - * is still pending, don't unpause the sequencer until - * it returns. - */ - xpt_print_path(ahc->path); - printf("Saw a target sync cmd\n"); - if (ahc->pending_device != NULL) { - printf(" Pending device too.\n"); - return; - } - break; - } case TARGET_MSG_HELP: { /* @@ -1534,105 +1544,56 @@ ahc_handle_seqint(struct ahc_softc *ahc, u_int intstat) restart_sequencer(ahc); return; } - case AWAITING_MSG: + case HOST_MSG_LOOP: { - u_int scb_index; - - scb_index = ahc_inb(ahc, SCB_TAG); - scb = ahc->scb_data->scbarray[scb_index]; - - /* - * To facilitate adding multiple messages together, - * each routine should increment the index and len - * variables instead of setting them explicitly. - */ - ahc->msg_index = 0; - ahc->msg_len = 0; - /* - * This SCB had MK_MESSAGE set in its control byte or - * we have explicitly set HOST_MSG in MSG_OUT, - * informing the sequencer that we want to send a - * special message to this target. + * The sequencer has encountered a message phase + * that requires host assistance for completion. + * While handling the message phase(s), we will be + * notified by the sequencer after each byte is + * transfered so we can track bus phases. + * + * If this is the first time we've seen a HOST_MSG_LOOP, + * initialize the state of the host message loop. */ - if ((scb->flags & SCB_DEVICE_RESET) == 0 - && ahc_inb(ahc, MSG_OUT) == MSG_IDENTIFYFLAG - && (scb->hscb->control & TAG_ENB) != 0) { - ahc->msg_buf[ahc->msg_index++] = - scb->ccb->csio.tag_action; - ahc->msg_buf[ahc->msg_index++] = - scb->hscb->tag; - ahc->msg_len += 2; - } - - if (scb->flags & SCB_DEVICE_RESET) { - ahc->msg_buf[ahc->msg_index++] = MSG_BUS_DEV_RESET; - ahc->msg_len++; - xpt_print_path(scb->ccb->ccb_h.path); - printf("Bus Device Reset Message Sent\n"); - } else if (scb->flags & SCB_ABORT) { - if ((scb->hscb->control & TAG_ENB) != 0) - ahc->msg_buf[ahc->msg_index++] = MSG_ABORT_TAG; - else - ahc->msg_buf[ahc->msg_index++] = MSG_ABORT; - ahc->msg_len++; - xpt_print_path(scb->ccb->ccb_h.path); - printf("Abort Message Sent\n"); - } else if (scb->flags & SCB_MSGOUT_WDTR) { - struct ahc_target_tinfo *tinfo; - - tinfo = &ahc->transinfo[devinfo.target_offset]; - ahc_construct_wdtr(ahc, tinfo->goal.width); - } else if (scb->flags & SCB_MSGOUT_SDTR) { - struct ahc_target_tinfo *tinfo; - u_int period; - u_int maxsync; - - /* - * Now that the target is actually selected, we - * can further refine our sync rate based on the - * output transceiver mode. - */ - if ((ahc->features & AHC_ULTRA2) != 0) { - if ((ahc_inb(ahc, SBLKCTL) & ENAB40) != 0 - && (ahc_inb(ahc, SSTAT2) & EXP_ACTIVE) == 0) { - maxsync = AHC_SYNCRATE_ULTRA2; - } else { - maxsync = AHC_SYNCRATE_ULTRA; + if (ahc->msg_type == MSG_TYPE_NONE) { + u_int bus_phase; + + bus_phase = ahc_inb(ahc, SCSISIGI) & PHASE_MASK; + if (bus_phase != P_MESGIN && bus_phase != P_MESGOUT) + panic("ahc_intr: HOST_MSG_LOOP bad phase 0x%x", + bus_phase); + + if (devinfo.role == ROLE_INITIATOR) { + struct scb *scb; + u_int scb_index; + + scb_index = ahc_inb(ahc, SCB_TAG); + scb = ahc->scb_data->scbarray[scb_index]; + + if (bus_phase == P_MESGOUT) + ahc_setup_initiator_msgout(ahc, + &devinfo, + scb); + else { + ahc->msg_type = + MSG_TYPE_INITIATOR_MSGIN; + ahc->msgin_index = 0; } - } else if ((ahc->features & AHC_ULTRA) != 0) { - maxsync = AHC_SYNCRATE_ULTRA; } else { - maxsync = AHC_SYNCRATE_FAST; + if (bus_phase == P_MESGOUT) { + ahc->msg_type = + MSG_TYPE_TARGET_MSGOUT; + ahc->msgin_index = 0; + } else + /* XXX Ever executed??? */ + ahc_setup_target_msgin(ahc, &devinfo); } - tinfo = &ahc->transinfo[devinfo.target_offset]; - period = tinfo->goal.period; - ahc_find_syncrate(ahc, &period, maxsync); - ahc_construct_sdtr(ahc, period, tinfo->goal.offset); - } else { - printf("ahc_intr: AWAITING_MSG for an SCB that " - "does not have a waiting message"); - panic("SCB = %d, SCB Control = %x, MSG_OUT = %x " - "SCB flags = %x", scb_index, scb->hscb->control, - ahc_inb(ahc, MSG_OUT), scb->flags); } - /* - * Record the fact that we attempted to send a message. - */ - scb->flags |= SCB_MSGOUT_SENT; - - /* - * To actually send the message, simply turn on - * REQINIT interrupts and let our interrupt handler - * do the rest (REQINIT should already be true). - */ - ahc->msg_index = 0; - ahc->msg_type = MSG_TYPE_INITIATOR_MSGOUT; - ahc->flags |= AHC_HANDLING_REQINITS; - ahc_outb(ahc, SIMODE1, ahc_inb(ahc, SIMODE1) | ENREQINIT); - - return; + /* Pass a NULL path so that handlers generate their own */ + ahc_handle_message_phase(ahc, /*path*/NULL); + break; } case DATA_OVERRUN: { @@ -1825,7 +1786,7 @@ ahc_handle_scsiint(struct ahc_softc *ahc, u_int intstat) CAM_UNEXP_BUSFREE); } else { ahc_abort_scbs(ahc, target, channel, - ALL_LUNS, SCB_LIST_NULL, + CAM_LUN_WILDCARD, SCB_LIST_NULL, CAM_UNEXP_BUSFREE); printf("%s: ", ahc_name(ahc)); } @@ -1835,9 +1796,8 @@ ahc_handle_scsiint(struct ahc_softc *ahc, u_int intstat) | (ahc_inb(ahc, SEQADDR1) << 8)); } ahc_outb(ahc, MSG_OUT, MSG_NOOP); - ahc_outb(ahc, SIMODE1, - ahc_inb(ahc, SIMODE1) & ~(ENBUSFREE|ENREQINIT)); - ahc->flags &= ~AHC_HANDLING_REQINITS; + ahc_clear_msg_state(ahc); + ahc_outb(ahc, SIMODE1, ahc_inb(ahc, SIMODE1) & ~ENBUSFREE); ahc_outb(ahc, CLRSINT1, CLRBUSFREE); ahc_outb(ahc, CLRINT, CLRSCSIINT); restart_sequencer(ahc); @@ -1873,9 +1833,7 @@ ahc_handle_scsiint(struct ahc_softc *ahc, u_int intstat) /* Stop the selection */ ahc_outb(ahc, SCSISEQ, 0); - ahc_outb(ahc, SIMODE1, - ahc_inb(ahc, SIMODE1) & ~ENREQINIT); - ahc->flags &= ~AHC_HANDLING_REQINITS; + ahc_clear_msg_state(ahc); ahc_outb(ahc, CLRSINT1, CLRSELTIMEO|CLRBUSFREE); @@ -1946,14 +1904,14 @@ ahc_handle_scsiint(struct ahc_softc *ahc, u_int intstat) * mesg_out to something other than MSG_NOP. */ if (mesg_out != MSG_NOOP) { - ahc_outb(ahc, MSG_OUT, mesg_out); + if (ahc->msg_type != MSG_TYPE_NONE) + ahc->send_msg_perror = TRUE; + else + ahc_outb(ahc, MSG_OUT, mesg_out); } ahc_outb(ahc, CLRSINT1, CLRSCSIPERR); ahc_outb(ahc, CLRINT, CLRSCSIINT); unpause_sequencer(ahc, /*unpause_always*/TRUE); - } else if ((status & REQINIT) != 0 - && (ahc->flags & AHC_HANDLING_REQINITS) != 0) { - ahc_handle_reqinit(ahc, scb); } else { xpt_print_path(scb->ccb->ccb_h.path); printf("Unknown SCSIINT. Status = 0x%x\n", status); @@ -1964,101 +1922,523 @@ ahc_handle_scsiint(struct ahc_softc *ahc, u_int intstat) } static void -ahc_handle_reqinit(struct ahc_softc *ahc, struct scb *scb) +ahc_build_transfer_msg(struct ahc_softc *ahc, struct ahc_devinfo *devinfo) +{ + /* + * We need to initiate transfer negotiations. + * If our current and goal settings are identical, + * we want to renegotiate due to a check condition. + */ + struct ahc_target_tinfo *tinfo; + int dowide; + int dosync; + + tinfo = &ahc->transinfo[devinfo->target_offset]; + dowide = tinfo->current.width != tinfo->goal.width; + dosync = tinfo->current.period != tinfo->goal.period; + + if (!dowide && !dosync) { + dowide = tinfo->goal.width != MSG_EXT_WDTR_BUS_8_BIT; + dosync = tinfo->goal.period != 0; + } + + if (dowide) + ahc_construct_wdtr(ahc, tinfo->goal.width); + else if (dosync) { + u_int period; + + period = tinfo->goal.period; + ahc_devlimited_syncrate(ahc, &period); + ahc_construct_sdtr(ahc, period, tinfo->goal.offset); + } else { + panic("ahc_intr: AWAITING_MSG for negotiation, " + "but no negotiation needed\n"); + } +} + +static void +ahc_setup_initiator_msgout(struct ahc_softc *ahc, struct ahc_devinfo *devinfo, + struct scb *scb) +{ + /* + * To facilitate adding multiple messages together, + * each routine should increment the index and len + * variables instead of setting them explicitly. + */ + ahc->msgout_index = 0; + ahc->msgout_len = 0; + + if ((scb->flags & SCB_DEVICE_RESET) == 0 + && ahc_inb(ahc, MSG_OUT) == MSG_IDENTIFYFLAG) { + u_int identify_msg; + + identify_msg = MSG_IDENTIFYFLAG | SCB_LUN(scb); + if ((scb->hscb->control & DISCENB) != 0) + identify_msg |= MSG_IDENTIFY_DISCFLAG; + ahc->msgout_buf[ahc->msgout_index++] = identify_msg; + ahc->msgout_len++; + + if ((scb->hscb->control & TAG_ENB) != 0) { + ahc->msgout_buf[ahc->msgout_index++] = + scb->ccb->csio.tag_action; + ahc->msgout_buf[ahc->msgout_index++] = scb->hscb->tag; + ahc->msgout_len += 2; + } + } + + if (scb->flags & SCB_DEVICE_RESET) { + ahc->msgout_buf[ahc->msgout_index++] = MSG_BUS_DEV_RESET; + ahc->msgout_len++; + xpt_print_path(scb->ccb->ccb_h.path); + printf("Bus Device Reset Message Sent\n"); + } else if (scb->flags & SCB_ABORT) { + if ((scb->hscb->control & TAG_ENB) != 0) + ahc->msgout_buf[ahc->msgout_index++] = MSG_ABORT_TAG; + else + ahc->msgout_buf[ahc->msgout_index++] = MSG_ABORT; + ahc->msgout_len++; + xpt_print_path(scb->ccb->ccb_h.path); + printf("Abort Message Sent\n"); + } else if ((ahc->targ_msg_req & devinfo->target_mask) != 0) { + ahc_build_transfer_msg(ahc, devinfo); + } else { + printf("ahc_intr: AWAITING_MSG for an SCB that " + "does not have a waiting message"); + panic("SCB = %d, SCB Control = %x, MSG_OUT = %x " + "SCB flags = %x", scb->hscb->tag, scb->hscb->control, + ahc_inb(ahc, MSG_OUT), scb->flags); + } + + /* + * Clear the MK_MESSAGE flag from the SCB so we aren't + * asked to send this message again. + */ + ahc_outb(ahc, SCB_CONTROL, ahc_inb(ahc, SCB_CONTROL) & ~MK_MESSAGE); + ahc->msgout_index = 0; + ahc->msg_type = MSG_TYPE_INITIATOR_MSGOUT; +} + +static void +ahc_setup_target_msgin(struct ahc_softc *ahc, struct ahc_devinfo *devinfo) +{ + /* + * To facilitate adding multiple messages together, + * each routine should increment the index and len + * variables instead of setting them explicitly. + */ + ahc->msgout_index = 0; + ahc->msgout_len = 0; + + if ((ahc->targ_msg_req & devinfo->target_mask) != 0) + ahc_build_transfer_msg(ahc, devinfo); + else + panic("ahc_intr: AWAITING target message with no message"); + + ahc->msgout_index = 0; + ahc->msg_type = MSG_TYPE_TARGET_MSGIN; +} + +static void +ahc_handle_msg_reject(struct ahc_softc *ahc, struct ahc_devinfo *devinfo) +{ + /* + * What we care about here is if we had an + * outstanding SDTR or WDTR message for this + * target. If we did, this is a signal that + * the target is refusing negotiation. + */ + struct scb *scb; + u_int scb_index; + u_int last_msg; + + scb_index = ahc_inb(ahc, SCB_TAG); + scb = ahc->scb_data->scbarray[scb_index]; + + /* Might be necessary */ + last_msg = ahc_inb(ahc, LAST_MSG); + + if (ahc_sent_msg(ahc, MSG_EXT_WDTR, /*full*/FALSE)) { + struct ahc_target_tinfo *tinfo; + + /* note 8bit xfers and clear flag */ + printf("%s:%c:%d: refuses WIDE negotiation. Using " + "8bit transfers\n", ahc_name(ahc), + devinfo->channel, devinfo->target); + ahc_set_width(ahc, devinfo, scb->ccb->ccb_h.path, + MSG_EXT_WDTR_BUS_8_BIT, + AHC_TRANS_ACTIVE|AHC_TRANS_GOAL); + ahc_set_syncrate(ahc, devinfo, scb->ccb->ccb_h.path, + /*syncrate*/NULL, /*period*/0, + /*offset*/0, AHC_TRANS_ACTIVE); + tinfo = &ahc->transinfo[devinfo->target_offset]; + if (tinfo->goal.period) { + u_int period; + + /* Start the sync negotiation */ + period = tinfo->goal.period; + ahc_devlimited_syncrate(ahc, &period); + ahc->msgout_index = 0; + ahc->msgout_len = 0; + ahc_construct_sdtr(ahc, period, tinfo->goal.offset); + ahc_outb(ahc, SCSISIGO, ahc_inb(ahc, SCSISIGO) | ATNO); + } + } else if (ahc_sent_msg(ahc, MSG_EXT_SDTR, /*full*/FALSE)) { + /* note asynch xfers and clear flag */ + ahc_set_syncrate(ahc, devinfo, scb->ccb->ccb_h.path, + /*syncrate*/NULL, /*period*/0, + /*offset*/0, + AHC_TRANS_ACTIVE|AHC_TRANS_GOAL); + printf("%s:%c:%d: refuses synchronous negotiation. " + "Using asynchronous transfers\n", + ahc_name(ahc), + devinfo->channel, devinfo->target); + } else if ((scb->hscb->control & MSG_SIMPLE_Q_TAG) != 0) { + struct ccb_trans_settings neg; + + printf("%s:%c:%d: refuses tagged commands. Performing " + "non-tagged I/O\n", ahc_name(ahc), + devinfo->channel, devinfo->target); + + ahc->tagenable &= ~devinfo->target_mask; + neg.flags = 0; + neg.valid = CCB_TRANS_TQ_VALID; + xpt_setup_ccb(&neg.ccb_h, scb->ccb->ccb_h.path, /*priority*/1); + xpt_async(AC_TRANSFER_NEG, scb->ccb->ccb_h.path, &neg); + + /* + * Resend the identify for this CCB as the target + * may believe that the selection is invalid otherwise. + */ + ahc_outb(ahc, SCB_CONTROL, ahc_inb(ahc, SCB_CONTROL) + & ~MSG_SIMPLE_Q_TAG); + scb->hscb->control &= ~MSG_SIMPLE_Q_TAG; + scb->ccb->ccb_h.flags &= ~CAM_TAG_ACTION_VALID; + ahc_outb(ahc, MSG_OUT, MSG_IDENTIFYFLAG); + ahc_outb(ahc, SCSISIGO, ahc_inb(ahc, SCSISIGO) | ATNO); + + /* + * Requeue all tagged commands for this target + * currently in our posession so they can be + * converted to untagged commands. + */ + ahc_search_qinfifo(ahc, SCB_TARGET(scb), SCB_CHANNEL(scb), + SCB_LUN(scb), /*tag*/SCB_LIST_NULL, + CAM_REQUEUE_REQ, SEARCH_COMPLETE); + } else { + /* + * Otherwise, we ignore it. + */ + printf("%s:%c:%d: Message reject for %x -- ignored\n", + ahc_name(ahc), devinfo->channel, devinfo->target, + last_msg); + } +} + +static void +ahc_clear_msg_state(struct ahc_softc *ahc) +{ + ahc->msgout_len = 0; + ahc->msgin_index = 0; + ahc->msg_type = MSG_TYPE_NONE; +} + +static void +ahc_handle_message_phase(struct ahc_softc *ahc, struct cam_path *path) { struct ahc_devinfo devinfo; - u_int simode1; + u_int bus_phase; + int end_session; ahc_fetch_devinfo(ahc, &devinfo); + + end_session = FALSE; + bus_phase = ahc_inb(ahc, SCSISIGI) & PHASE_MASK; + +reswitch: switch (ahc->msg_type) { case MSG_TYPE_INITIATOR_MSGOUT: { - int lastbyte; - int phasemis; - u_int bus_phase; + int lastbyte; + int phasemis; + int msgdone; - if (ahc->msg_len == 0) + if (ahc->msgout_len == 0) panic("REQINIT interrupt with no active message"); - lastbyte = (ahc->msg_index == ahc->msg_len - 1); - bus_phase = ahc_inb(ahc, SCSISIGI) & PHASE_MASK; phasemis = bus_phase != P_MESGOUT; + if (phasemis) { + if (bus_phase == P_MESGIN) { + u_int scsisigo; - if (lastbyte || phasemis) { - /* Time to end our message session */ - ahc->msg_len = 0; - ahc->msg_type = MSG_TYPE_NONE; - simode1 = ahc_inb(ahc, SIMODE1) & ~ENREQINIT; - ahc_outb(ahc, SIMODE1, simode1); - ahc_outb(ahc, CLRINT, CLRSCSIINT); - ahc->flags &= ~AHC_HANDLING_REQINITS; - - if (phasemis == 0) { - ahc_outb(ahc, SINDEX, - ahc->msg_buf[ahc->msg_index]); - ahc_outb(ahc, RETURN_1, 0); - } else { - ahc_outb(ahc, RETURN_1, MSGOUT_PHASEMIS); + /* + * Change gears and see if + * this messages is of interest to + * us or should be passed back to + * the sequencer. + */ + ahc->send_msg_perror = FALSE; + ahc->msg_type = MSG_TYPE_INITIATOR_MSGIN; + ahc->msgin_index = 0; + goto reswitch; } + end_session = TRUE; + break; + } - unpause_sequencer(ahc, /* unpause_always */TRUE); - } else { + if (ahc->send_msg_perror) { + ahc_outb(ahc, CLRSINT1, CLRATNO); + ahc_outb(ahc, CLRSINT1, CLRREQINIT); + ahc_outb(ahc, SCSIDATL, MSG_PARITY_ERROR); + break; + } + + msgdone = ahc->msgout_index == ahc->msgout_len; + if (msgdone) { /* - * Clear our interrupt status and present the byte - * on the bus, but don't unpause the sequencer. + * The target has requested a retry. + * Re-assert ATN, reset our message index to + * 0, and try again. */ - ahc_outb(ahc, CLRSINT1, CLRREQINIT); - ahc_outb(ahc, CLRINT, CLRSCSIINT); - ahc_outb(ahc, SCSIDATL, ahc->msg_buf[ahc->msg_index++]); + ahc->msgout_index = 0; + ahc_outb(ahc, SCSISIGO, ahc_inb(ahc, SCSISIGO) | ATNO); + } + + lastbyte = ahc->msgout_index == (ahc->msgout_len - 1); + if (lastbyte) { + /* Last byte is signified by dropping ATN */ + ahc_outb(ahc, CLRSINT1, CLRATNO); } + + /* + * Clear our interrupt status and present + * the next byte on the bus. + */ + ahc_outb(ahc, CLRSINT1, CLRREQINIT); + ahc_outb(ahc, SCSIDATL, ahc->msgout_buf[ahc->msgout_index++]); break; } case MSG_TYPE_INITIATOR_MSGIN: { - int phasemis; - int done; + int phasemis; + int message_done; - phasemis = (ahc_inb(ahc, SCSISIGI) & PHASE_MASK) != P_MESGIN; + phasemis = bus_phase != P_MESGIN; - if (phasemis == 0) { + if (phasemis) { + ahc->msgin_index = 0; + if (bus_phase == P_MESGOUT + && (ahc->send_msg_perror == TRUE + || (ahc->msgout_len != 0 + && ahc->msgout_index == 0))) { + u_int scsisigo; - ahc->msg_len++; - /* Pull the byte in without acking it */ - ahc->msg_buf[ahc->msg_index] = ahc_inb(ahc, SCSIBUSL); - done = ahc_parse_msg(ahc, scb, &devinfo); - /* Ack the byte */ - ahc_outb(ahc, CLRSINT1, CLRREQINIT); - ahc_outb(ahc, CLRINT, CLRSCSIINT); + ahc->msg_type = MSG_TYPE_INITIATOR_MSGOUT; + goto reswitch; + } + end_session = TRUE; + break; + } + + /* Pull the byte in without acking it */ + ahc->msgin_buf[ahc->msgin_index] = ahc_inb(ahc, SCSIBUSL); + + message_done = ahc_parse_msg(ahc, path, &devinfo); + + if (message_done) { + /* + * Clear our incoming message buffer in case there + * is another message following this one. + */ + ahc->msgin_index = 0; + + /* + * If this message illicited a response, + * assert ATN so the target takes us to the + * message out phase. + */ + if (ahc->msgout_len != 0) + ahc_outb(ahc, SCSISIGO, + ahc_inb(ahc, SCSISIGO) | ATNO); + } + + /* Ack the byte */ + ahc_outb(ahc, CLRSINT1, CLRREQINIT); + ahc_inb(ahc, SCSIDATL); + ahc->msgin_index++; + break; + } + case MSG_TYPE_TARGET_MSGIN: + { + int msgdone; + int msgout_request; + + if (ahc->msgout_len == 0) + panic("Target REQINIT with no active message"); + + /* + * If we interrupted a mesgout session, the initiator + * will not know this until our first REQ. So, we + * only honor mesgout requests after we've sent our + * first byte. + */ + if ((ahc_inb(ahc, SCSISIGI) & ATNI) != 0 + && ahc->msgout_index > 0) + msgout_request = TRUE; + else + msgout_request = FALSE; + + if (msgout_request) { + + /* + * Change gears and see if + * this messages is of interest to + * us or should be passed back to + * the sequencer. + */ + ahc->msg_type = MSG_TYPE_TARGET_MSGOUT; + ahc_outb(ahc, SCSISIGO, P_MESGOUT | BSYO); + ahc->msgin_index = 0; + /* Dummy read to REQ for first byte */ ahc_inb(ahc, SCSIDATL); - ahc->msg_index++; + ahc_outb(ahc, SXFRCTL0, + ahc_inb(ahc, SXFRCTL0) | SPIOEN); + break; } - if (phasemis || done) { - /* Time to end our message session */ - ahc->msg_len = 0; - ahc->msg_type = MSG_TYPE_NONE; - simode1 = ahc_inb(ahc, SIMODE1) & ~ENREQINIT; - ahc->flags &= ~AHC_HANDLING_REQINITS; - ahc_outb(ahc, SIMODE1, simode1); - ahc_outb(ahc, CLRINT, CLRSCSIINT); - unpause_sequencer(ahc, /* unpause_always */TRUE); + + msgdone = ahc->msgout_index == ahc->msgout_len; + if (msgdone) { + ahc_outb(ahc, SXFRCTL0, + ahc_inb(ahc, SXFRCTL0) & ~SPIOEN); + end_session = TRUE; + break; } + + /* + * Present the next byte on the bus. + */ + ahc_outb(ahc, SXFRCTL0, ahc_inb(ahc, SXFRCTL0) | SPIOEN); + ahc_outb(ahc, SCSIDATL, ahc->msgout_buf[ahc->msgout_index++]); + break; + } + case MSG_TYPE_TARGET_MSGOUT: + { + int lastbyte; + int msgdone; + + /* + * The initiator signals that this is + * the last byte by dropping ATN. + */ + lastbyte = (ahc_inb(ahc, SCSISIGI) & ATNI) == 0; + + /* + * Read the latched byte, but turn off SPIOEN first + * so that we don't inadvertantly cause a REQ for the + * next byte. + */ + ahc_outb(ahc, SXFRCTL0, ahc_inb(ahc, SXFRCTL0) & ~SPIOEN); + ahc->msgin_buf[ahc->msgin_index] = ahc_inb(ahc, SCSIDATL); + msgdone = ahc_parse_msg(ahc, path, &devinfo); + ahc->msgin_index++; + + /* + * XXX Read spec about initiator dropping ATN too soon + * and use msgdone to detect it. + */ + if (msgdone) { + ahc->msgin_index = 0; + + /* + * If this message illicited a response, transition + * to the Message in phase and send it. + */ + if (ahc->msgout_len != 0) { + ahc_outb(ahc, SCSISIGO, P_MESGIN | BSYO); + ahc_outb(ahc, SXFRCTL0, + ahc_inb(ahc, SXFRCTL0) | SPIOEN); + ahc->msg_type = MSG_TYPE_TARGET_MSGIN; + ahc->msgin_index = 0; + break; + } + } + + if (lastbyte) + end_session = TRUE; + else { + /* Ask for the next byte. */ + ahc_outb(ahc, SXFRCTL0, + ahc_inb(ahc, SXFRCTL0) | SPIOEN); + } + break; } default: panic("Unknown REQINIT message type"); } + + if (end_session) { + ahc_clear_msg_state(ahc); + ahc_outb(ahc, RETURN_1, EXIT_MSG_LOOP); + } else + ahc_outb(ahc, RETURN_1, CONT_MSG_LOOP); } +/* + * See if we sent a particular extended message to the target. + * If "full" is true, the target saw the full message. + * If "full" is false, the target saw at least the first + * byte of the message. + */ static int -ahc_parse_msg(struct ahc_softc *ahc, struct scb *scb, +ahc_sent_msg(struct ahc_softc *ahc, u_int msgtype, int full) +{ + int found; + int index; + + found = FALSE; + index = 0; + + while (index < ahc->msgout_len) { + if ((ahc->msgout_buf[index] & MSG_IDENTIFYFLAG) != 0 + || ahc->msgout_buf[index] == MSG_MESSAGE_REJECT) + index++; + else if (ahc->msgout_buf[index] >= MSG_SIMPLE_Q_TAG + && ahc->msgout_buf[index] < MSG_IGN_WIDE_RESIDUE) { + /* Skip tag type and tag id */ + index += 2; + } else if (ahc->msgout_buf[index] == MSG_EXTENDED) { + /* Found a candidate */ + if (ahc->msgout_buf[index+2] == msgtype) { + u_int end_index; + + end_index = index + 1 + + ahc->msgout_buf[index + 1]; + if (full) { + if (ahc->msgout_index > end_index) + found = TRUE; + } else if (ahc->msgout_index > index) + found = TRUE; + } + break; + } else { + panic("ahc_sent_msg: Inconsistent msg buffer"); + } + } + return (found); +} + +static int +ahc_parse_msg(struct ahc_softc *ahc, struct cam_path *path, struct ahc_devinfo *devinfo) { int reject; int done; + int response; u_int targ_scsirate; done = FALSE; + response = FALSE; reject = FALSE; targ_scsirate = ahc->transinfo[devinfo->target_offset].scsirate; /* @@ -2067,17 +2447,20 @@ ahc_parse_msg(struct ahc_softc *ahc, struct scb *scb, * the entire message is availible and has been * handled, return TRUE indicating that we have * parsed an entire message. + * + * In the case of extended messages, we accept the length + * byte outright and perform more checking once we know the + * extended message type. */ - if (ahc->msg_buf[0] != MSG_EXTENDED) { + if (ahc->msgin_buf[0] == MSG_MESSAGE_REJECT) { + ahc_handle_msg_reject(ahc, devinfo); + done = TRUE; + } else if (ahc->msgin_buf[0] == MSG_NOOP) { + done = TRUE; + } else if (ahc->msgin_buf[0] != MSG_EXTENDED) { reject = TRUE; - } - - /* - * Just accept the length byte outright and perform - * more checking once we know the message type. - */ - if (!reject && (ahc->msg_len > 2)) { - switch (ahc->msg_buf[2]) { + } else if (ahc->msgin_index >= 2) { + switch (ahc->msgin_buf[2]) { case MSG_EXT_SDTR: { struct ahc_syncrate *syncrate; @@ -2086,7 +2469,7 @@ ahc_parse_msg(struct ahc_softc *ahc, struct scb *scb, u_int saved_offset; u_int maxsync; - if (ahc->msg_buf[1] != MSG_EXT_SDTR_LEN) { + if (ahc->msgin_buf[1] != MSG_EXT_SDTR_LEN) { reject = TRUE; break; } @@ -2094,28 +2477,19 @@ ahc_parse_msg(struct ahc_softc *ahc, struct scb *scb, /* * Wait until we have both args before validating * and acting on this message. + * + * Add one to MSG_EXT_SDTR_LEN to account for + * the extended message preamble. */ - if (ahc->msg_len < (MSG_EXT_SDTR_LEN + /*preamble*/2)) + if (ahc->msgin_index < (MSG_EXT_SDTR_LEN + 1)) break; - period = ahc->msg_buf[3]; - saved_offset = offset = ahc->msg_buf[4]; - if ((ahc->features & AHC_ULTRA2) != 0) { - if ((ahc_inb(ahc, SBLKCTL) & ENAB40) != 0 - && (ahc_inb(ahc, SSTAT2) & EXP_ACTIVE) == 0) { - maxsync = AHC_SYNCRATE_ULTRA2; - } else { - maxsync = AHC_SYNCRATE_ULTRA; - } - } else if ((ahc->features & AHC_ULTRA) != 0) { - maxsync = AHC_SYNCRATE_ULTRA; - } else { - maxsync = AHC_SYNCRATE_FAST; - } - syncrate = ahc_find_syncrate(ahc, &period, maxsync); + period = ahc->msgin_buf[3]; + saved_offset = offset = ahc->msgin_buf[4]; + syncrate = ahc_devlimited_syncrate(ahc, &period); ahc_validate_offset(ahc, syncrate, &offset, targ_scsirate & WIDEXFER); - ahc_set_syncrate(ahc, devinfo, scb->ccb->ccb_h.path, + ahc_set_syncrate(ahc, devinfo, path, syncrate, period, offset, AHC_TRANS_ACTIVE|AHC_TRANS_GOAL); @@ -2124,27 +2498,23 @@ ahc_parse_msg(struct ahc_softc *ahc, struct scb *scb, * and didn't have to fall down to async * transfers. */ - if ((scb->flags & (SCB_MSGOUT_SDTR|SCB_MSGOUT_SENT)) - == (SCB_MSGOUT_SDTR|SCB_MSGOUT_SENT)) { + if (ahc_sent_msg(ahc, MSG_EXT_SDTR, /*full*/TRUE)) { /* We started it */ if (saved_offset != offset) { /* Went too low - force async */ reject = TRUE; } - scb->flags &= ~SCB_MSGOUT_BITS; - ahc->sdtrpending &= ~devinfo->target_mask; } else { /* * Send our own SDTR in reply */ - scb->flags &= ~SCB_MSGOUT_BITS; - scb->flags |= SCB_MSGOUT_SDTR; - ahc->sdtrpending |= devinfo->target_mask; - xpt_print_path(scb->ccb->ccb_h.path); - printf("Sending SDTR!!\n"); - ahc_outb(ahc, MSG_OUT, HOST_MSG); - ahc_outb(ahc, SCSISIGO, - ahc_inb(ahc, SCSISIGO) | ATNO); + if (bootverbose) + printf("Sending SDTR!\n"); + ahc->msgout_index = 0; + ahc->msgout_len = 0; + ahc_construct_sdtr(ahc, period, offset); + ahc->msgout_index = 0; + response = TRUE; } done = TRUE; break; @@ -2152,8 +2522,10 @@ ahc_parse_msg(struct ahc_softc *ahc, struct scb *scb, case MSG_EXT_WDTR: { u_int bus_width; + u_int sending_reply; - if (ahc->msg_buf[1] != MSG_EXT_WDTR_LEN) { + sending_reply = FALSE; + if (ahc->msgin_buf[1] != MSG_EXT_WDTR_LEN) { reject = TRUE; break; } @@ -2161,13 +2533,15 @@ ahc_parse_msg(struct ahc_softc *ahc, struct scb *scb, /* * Wait until we have our arg before validating * and acting on this message. + * + * Add one to MSG_EXT_WDTR_LEN to account for + * the extended message preamble. */ - if (ahc->msg_len < (MSG_EXT_WDTR_LEN + /*preamble*/2)) + if (ahc->msgin_index < (MSG_EXT_WDTR_LEN + 1)) break; - bus_width = ahc->msg_buf[3]; - if ((scb->flags & (SCB_MSGOUT_WDTR|SCB_MSGOUT_SENT)) - == (SCB_MSGOUT_WDTR|SCB_MSGOUT_SENT)) { + bus_width = ahc->msgin_buf[3]; + if (ahc_sent_msg(ahc, MSG_EXT_WDTR, /*full*/TRUE)) { /* * Don't send a WDTR back to the * target, since we asked first. @@ -2191,15 +2565,12 @@ ahc_parse_msg(struct ahc_softc *ahc, struct scb *scb, case MSG_EXT_WDTR_BUS_16_BIT: break; } - scb->flags &= ~SCB_MSGOUT_WDTR; - ahc->wdtrpending &= ~devinfo->target_mask; } else { /* * Send our own WDTR in reply */ - printf("Sending WDTR!\n"); - scb->flags &= ~SCB_MSGOUT_BITS; - scb->flags |= SCB_MSGOUT_WDTR; + if (bootverbose) + printf("Sending WDTR!\n"); switch (bus_width) { default: if (ahc->features & AHC_WIDE) { @@ -2213,33 +2584,36 @@ ahc_parse_msg(struct ahc_softc *ahc, struct scb *scb, bus_width = MSG_EXT_WDTR_BUS_8_BIT; break; } - ahc_outb(ahc, MSG_OUT, HOST_MSG); - ahc_outb(ahc, SCSISIGO, - ahc_inb(ahc, SCSISIGO) | ATNO); - ahc->wdtrpending |= devinfo->target_mask; + ahc->msgout_index = 0; + ahc->msgout_len = 0; + ahc_construct_wdtr(ahc, bus_width); + ahc->msgout_index = 0; + response = TRUE; + sending_reply = TRUE; } - ahc_set_width(ahc, devinfo, scb->ccb->ccb_h.path, - bus_width, + ahc_set_width(ahc, devinfo, path, bus_width, AHC_TRANS_ACTIVE|AHC_TRANS_GOAL); /* After a wide message, we are async */ - ahc_set_syncrate(ahc, devinfo, scb->ccb->ccb_h.path, + ahc_set_syncrate(ahc, devinfo, path, /*syncrate*/NULL, /*period*/0, /*offset*/0, AHC_TRANS_ACTIVE); - if ((ahc->wdtrpending & devinfo->target_mask) == 0 - && (reject == 0)) { + if (sending_reply == FALSE && reject == FALSE) { struct ahc_target_tinfo *tinfo; - scb->flags &= ~SCB_MSGOUT_WDTR; tinfo = &ahc->transinfo[devinfo->target_offset]; if (tinfo->goal.period) { + u_int period; + /* Start the sync negotiation */ - ahc->sdtrpending |= - devinfo->target_mask; - scb->flags |= SCB_MSGOUT_SDTR; - ahc_outb(ahc, MSG_OUT, HOST_MSG); - ahc_outb(ahc, SCSISIGO, - ahc_inb(ahc, SCSISIGO) | ATNO); + period = tinfo->goal.period; + ahc_devlimited_syncrate(ahc, &period); + ahc->msgout_index = 0; + ahc->msgout_len = 0; + ahc_construct_sdtr(ahc, period, + tinfo->goal.offset); + ahc->msgout_index = 0; + response = TRUE; } } done = TRUE; @@ -2257,10 +2631,17 @@ ahc_parse_msg(struct ahc_softc *ahc, struct scb *scb, * Assert attention and setup to * reject the message. */ - ahc_outb(ahc, MSG_OUT, MSG_MESSAGE_REJECT); - ahc_outb(ahc, SCSISIGO, ahc_inb(ahc, SCSISIGO) | ATNO); + ahc->msgout_index = 0; + ahc->msgout_len = 1; + ahc->msgout_buf[0] = MSG_MESSAGE_REJECT; done = TRUE; + response = TRUE; } + + if (done && !response) + /* Clear the outgoing message buffer */ + ahc->msgout_len = 0; + return (done); } @@ -2271,29 +2652,24 @@ ahc_handle_devreset(struct ahc_softc *ahc, int target, char channel, { struct ahc_devinfo devinfo; struct cam_path *path; - path_id_t path_id; int found; int error; - ahc_compile_devinfo(&devinfo, target, channel); + ahc_compile_devinfo(&devinfo, target, CAM_LUN_WILDCARD, channel, + ROLE_UNKNOWN); - if (channel == 'B') - path_id = cam_sim_path(ahc->sim_b); - else - path_id = cam_sim_path(ahc->sim); + error = ahc_create_path(ahc, &devinfo, &path); - error = xpt_create_path(&path, /*periph*/NULL, path_id, target, - CAM_LUN_WILDCARD); /* * Go back to async/narrow transfers and renegotiate. + * ahc_set_width and ahc_set_syncrate can cope with NULL + * paths. */ - if (error == CAM_REQ_CMP) { - ahc_set_width(ahc, &devinfo, path, MSG_EXT_WDTR_BUS_8_BIT, - AHC_TRANS_CUR); - ahc_set_syncrate(ahc, &devinfo, path, /*syncrate*/NULL, - /*period*/0, /*offset*/0, AHC_TRANS_CUR); - } - found = ahc_abort_scbs(ahc, target, channel, ALL_LUNS, + ahc_set_width(ahc, &devinfo, path, MSG_EXT_WDTR_BUS_8_BIT, + AHC_TRANS_CUR); + ahc_set_syncrate(ahc, &devinfo, path, /*syncrate*/NULL, + /*period*/0, /*offset*/0, AHC_TRANS_CUR); + found = ahc_abort_scbs(ahc, target, channel, CAM_LUN_WILDCARD, SCB_LIST_NULL, status); if (error == CAM_REQ_CMP && acode != 0) @@ -2345,8 +2721,6 @@ ahc_done(struct ahc_softc *ahc, struct scb *scb) ahc_index_busy_tcl(ahc, scb->hscb->tcl, /*unbusy*/TRUE); if (ccb->ccb_h.func_code == XPT_CONT_TARGET_IO) { - xpt_print_path(ccb->ccb_h.path); - printf("CONT_TARGET_IO complete\n"); ccb->ccb_h.status = CAM_REQ_CMP; ahc_free_scb(ahc, scb); xpt_done(ccb); @@ -2391,22 +2765,6 @@ ahc_done(struct ahc_softc *ahc, struct scb *scb) ccb->ccb_h.status); } - if ((scb->flags & (SCB_MSGOUT_WDTR|SCB_MSGOUT_SDTR)) != 0) { - /* - * Turn off the pending flags for any DTR messages - * regardless of whether they completed successfully - * or not. This ensures that we don't have lingering - * state after we abort an SCB. - */ - u_int16_t mask; - - mask = (0x01 << (SCB_TARGET(scb) - | (SCB_IS_SCSIBUS_B(scb) ? SELBUSB : 0))); - if (scb->flags & SCB_MSGOUT_WDTR) - ahc->wdtrpending &= ~mask; - if (scb->flags & SCB_MSGOUT_SDTR) - ahc->sdtrpending &= ~mask; - } /* Don't clobber any existing error state */ if (ahc_ccb_status(ccb) == CAM_REQ_INPROG) { ccb->ccb_h.status |= CAM_REQ_CMP; @@ -2477,11 +2835,21 @@ ahc_init(struct ahc_softc *ahc) } /* + * Default to allowing initiator operations. + */ + ahc->flags |= AHC_INITIATORMODE; + + /* * XXX Would be better to use a per device flag, but PCI and EISA * devices don't have them yet. */ - if ((AHC_TMODE_ENABLE & (0x01 << ahc->unit)) != 0) + if ((AHC_TMODE_ENABLE & (0x01 << ahc->unit)) != 0) { ahc->flags |= AHC_TARGETMODE; + if ((ahc->features & AHC_ULTRA2) == 0) + /* Only have space for both on the Ultra2 chips */ + ahc->flags &= ~AHC_INITIATORMODE; + } + if ((ahc->features & AHC_TWIN) != 0) { printf("Twin Channel, A SCSI Id=%d, B SCSI Id=%d, primary %c, ", @@ -2624,7 +2992,7 @@ ahc_init(struct ahc_softc *ahc) * flag. */ ahc->ultraenb = 0; - ahc->tagenable = ALL_TARGETS; + ahc->tagenable = ALL_TARGETS_MASK; /* Grab the disconnection disable table and invert it for our needs */ if (ahc->flags & AHC_USEDEFAULTS) { @@ -2632,7 +3000,7 @@ ahc_init(struct ahc_softc *ahc) "device parameters\n", ahc_name(ahc)); ahc->flags |= AHC_EXTENDED_TRANS_A|AHC_EXTENDED_TRANS_B| AHC_TERM_ENB_A|AHC_TERM_ENB_B; - ahc->discenable = ALL_TARGETS; + ahc->discenable = ALL_TARGETS_MASK; if ((ahc->features & AHC_ULTRA) != 0) ahc->ultraenb = 0xffff; } else { @@ -2708,8 +3076,6 @@ ahc_init(struct ahc_softc *ahc) } } - ahc->sdtrpending = 0; - ahc->wdtrpending = 0; #ifdef AHC_DEBUG if (ahc_debug & AHC_SHOWMISC) @@ -2824,6 +3190,10 @@ ahc_init(struct ahc_softc *ahc) ahc_outb(ahc, QINPOS, 0); ahc_outb(ahc, QOUTPOS, 0); + /* Don't have any special messages to send to targets */ + ahc_outb(ahc, TARGET_MSG_REQUEST, 0); + ahc_outb(ahc, TARGET_MSG_REQUEST + 1, 0); + /* * Use the built in queue management registers * if they are available. @@ -2957,8 +3327,6 @@ ahc_action(struct cam_sim *sim, union ccb *ccb) * the original command. */ target_id = ccb->csio.init_id; - xpt_print_path(ccb->ccb_h.path); - printf("Sending a continue TIO\n"); /* FALLTHROUGH */ } case XPT_SCSI_IO: /* Execute the requested I/O operation */ @@ -3020,21 +3388,7 @@ ahc_action(struct cam_sim *sim, union ccb *ccb) hscb->control |= MK_MESSAGE; ahc_execute_scb(scb, NULL, 0, 0); } else { - if (ccb->ccb_h.func_code == XPT_SCSI_IO) { - if (tinfo->current.width != tinfo->goal.width) { - if ((ahc->wdtrpending & mask) == 0) { - ahc->wdtrpending |= mask; - hscb->control |= MK_MESSAGE; - scb->flags |= SCB_MSGOUT_WDTR; - } - } else if ((tinfo->current.period - != tinfo->goal.period) - && (ahc->sdtrpending & mask) == 0) { - ahc->sdtrpending |= mask; - hscb->control |= MK_MESSAGE; - scb->flags |= SCB_MSGOUT_SDTR; - } - } else { + if (ccb->ccb_h.func_code == XPT_CONT_TARGET_IO) { if (ahc->pending_device == lstate) { scb->flags |= SCB_TARGET_IMMEDIATE; ahc->pending_device = NULL; @@ -3194,7 +3548,9 @@ ahc_action(struct cam_sim *sim, union ccb *ccb) cts = &ccb->cts; ahc_compile_devinfo(&devinfo, cts->ccb_h.target_id, - SIM_IS_SCSIBUS_B(ahc, sim) ? 'B' : 'A'); + cts->ccb_h.target_lun, + SIM_IS_SCSIBUS_B(ahc, sim) ? 'B' : 'A', + ROLE_UNKNOWN); tinfo = &ahc->transinfo[devinfo.target_offset]; update_type = 0; if ((cts->flags & CCB_TRANS_CURRENT_SETTINGS) != 0) @@ -3230,10 +3586,8 @@ ahc_action(struct cam_sim *sim, union ccb *ccb) cts->bus_width = MSG_EXT_WDTR_BUS_8_BIT; break; } - if ((update_type & AHC_TRANS_GOAL) != 0) - tinfo->goal.width = cts->bus_width; - if ((update_type & AHC_TRANS_USER) != 0) - tinfo->user.width = cts->bus_width; + ahc_set_width(ahc, &devinfo, cts->ccb_h.path, + cts->bus_width, update_type); } if ((cts->valid & CCB_TRANS_SYNC_RATE_VALID) != 0) { @@ -3263,14 +3617,9 @@ ahc_action(struct cam_sim *sim, union ccb *ccb) if (cts->sync_offset == 0) cts->sync_period = 0; - if ((update_type & AHC_TRANS_GOAL) != 0) { - tinfo->goal.period = cts->sync_period; - tinfo->goal.offset = cts->sync_offset; - } - if ((update_type & AHC_TRANS_USER) != 0) { - tinfo->user.period = cts->sync_period; - tinfo->user.offset = cts->sync_offset; - } + ahc_set_syncrate(ahc, &devinfo, cts->ccb_h.path, + syncrate, cts->sync_period, + cts->sync_offset, update_type); } splx(s); ccb->ccb_h.status = CAM_REQ_CMP; @@ -3288,7 +3637,9 @@ ahc_action(struct cam_sim *sim, union ccb *ccb) cts = &ccb->cts; ahc_compile_devinfo(&devinfo, cts->ccb_h.target_id, - SIM_IS_SCSIBUS_B(ahc, sim) ? 'B' : 'A'); + cts->ccb_h.target_lun, + SIM_IS_SCSIBUS_B(ahc, sim) ? 'B' : 'A', + ROLE_UNKNOWN); targ_info = &ahc->transinfo[devinfo.target_offset]; if ((cts->flags & CCB_TRANS_CURRENT_SETTINGS) != 0) @@ -3393,7 +3744,8 @@ ahc_action(struct cam_sim *sim, union ccb *ccb) } else { cpi->target_sprt = 0; } - cpi->hba_misc = 0; + cpi->hba_misc = (ahc->flags & AHC_INITIATORMODE) + ? 0 : PIM_NOINITIATOR|PIM_NOBUSRESET; cpi->hba_eng_cnt = 0; cpi->max_target = (ahc->features & AHC_WIDE) ? 15 : 7; cpi->max_lun = 7; @@ -3431,7 +3783,9 @@ ahc_async(void *callback_arg, u_int32_t code, struct cam_path *path, void *arg) struct ahc_devinfo devinfo; ahc_compile_devinfo(&devinfo, xpt_path_target_id(path), - SIM_IS_SCSIBUS_B(ahc, sim) ? 'B' : 'A'); + xpt_path_lun_id(path), + SIM_IS_SCSIBUS_B(ahc, sim) ? 'B' : 'A', + ROLE_UNKNOWN); /* * Revert to async/narrow transfers @@ -3530,12 +3884,12 @@ ahc_execute_scb(void *arg, bus_dma_segment_t *dm_segs, int nsegments, (ccb->ccb_h.timeout * hz) / 1000); if ((scb->flags & SCB_TARGET_IMMEDIATE) != 0) { - xpt_print_path(ccb->ccb_h.path); - printf("Returning an immediate CTIO\n"); if ((ahc->flags & AHC_PAGESCBS) == 0) ahc_outb(ahc, SCBPTR, scb->hscb->tag); + pause_sequencer(ahc); ahc_outb(ahc, SCB_TAG, scb->hscb->tag); - unpause_sequencer(ahc, /*unpause_always*/TRUE); + ahc_outb(ahc, RETURN_1, CONT_MSG_LOOP); + unpause_sequencer(ahc, /*unpause_always*/FALSE); } else { ahc->qinfifo[ahc->qinfifonext++] = scb->hscb->tag; @@ -4098,6 +4452,9 @@ ahc_timeout(void *arg) printf("SEQADDR == 0x%x\n", ahc_inb(ahc, SEQADDR0) | (ahc_inb(ahc, SEQADDR1) << 8)); + + printf("SIMODE1 = 0x%x\n", ahc_inb(ahc, SIMODE1)); + printf("INTSTAT = 0x%x\n", ahc_inb(ahc, INTSTAT)); printf("SSTAT1 == 0x%x\n", ahc_inb(ahc, SSTAT1)); #if 0 printf("SCSIRATE == 0x%x\n", ahc_inb(ahc, SCSIRATE)); @@ -4565,8 +4922,9 @@ ahc_reset_channel(struct ahc_softc *ahc, char channel, int initiate_reset) * Clean up all the state information for the * pending transactions on this bus. */ - found = ahc_abort_scbs(ahc, ALL_TARGETS, channel, ALL_LUNS, - SCB_LIST_NULL, CAM_SCSI_BUS_RESET); + found = ahc_abort_scbs(ahc, CAM_TARGET_WILDCARD, channel, + CAM_LUN_WILDCARD, SCB_LIST_NULL, + CAM_SCSI_BUS_RESET); path = channel == 'B' ? ahc->path_b : ahc->path; /* Notify the XPT that a bus reset occurred */ @@ -4579,7 +4937,8 @@ ahc_reset_channel(struct ahc_softc *ahc, char channel, int initiate_reset) for (target = 0; target <= max_target; target++) { struct ahc_devinfo devinfo; - ahc_compile_devinfo(&devinfo, target, channel); + ahc_compile_devinfo(&devinfo, target, CAM_LUN_WILDCARD, + channel, ROLE_UNKNOWN); ahc_set_width(ahc, &devinfo, path, MSG_EXT_WDTR_BUS_8_BIT, AHC_TRANS_CUR); ahc_set_syncrate(ahc, &devinfo, path, /*syncrate*/NULL, @@ -4601,8 +4960,7 @@ ahc_reset_channel(struct ahc_softc *ahc, char channel, int initiate_reset) * upsetting the current bus. */ ahc_outb(ahc, SBLKCTL, sblkctl ^ SELBUSB); - ahc_outb(ahc, SIMODE1, - ahc_inb(ahc, SIMODE1) & ~(ENBUSFREE|ENREQINIT)); + ahc_outb(ahc, SIMODE1, ahc_inb(ahc, SIMODE1) & ~ENBUSFREE); ahc_outb(ahc, SCSISEQ, ahc_inb(ahc, SCSISEQ) & (ENSELI|ENRSELI|ENAUTOATNP)); if (initiate_reset) @@ -4612,10 +4970,8 @@ ahc_reset_channel(struct ahc_softc *ahc, char channel, int initiate_reset) unpause_sequencer(ahc, /*unpause_always*/FALSE); } else { /* Case 2: A command from this bus is active or we're idle */ - ahc_outb(ahc, SIMODE1, - ahc_inb(ahc, SIMODE1) & ~(ENBUSFREE|ENREQINIT)); - ahc->flags &= ~AHC_HANDLING_REQINITS; - ahc->msg_type = MSG_TYPE_NONE; + ahc_clear_msg_state(ahc); + ahc_outb(ahc, SIMODE1, ahc_inb(ahc, SIMODE1) & ~ENBUSFREE); ahc_outb(ahc, SCSISEQ, ahc_inb(ahc, SCSISEQ) & (ENSELI|ENRSELI|ENAUTOATNP)); if (initiate_reset) @@ -4636,9 +4992,9 @@ ahc_match_scb (struct scb *scb, int target, char channel, int lun, u_int tag) match = ((chan == channel) || (channel == ALL_CHANNELS)); if (match != 0) - match = ((targ == target) || (target == ALL_TARGETS)); + match = ((targ == target) || (target == CAM_TARGET_WILDCARD)); if (match != 0) - match = ((lun == slun) || (lun == ALL_LUNS)); + match = ((lun == slun) || (lun == CAM_LUN_WILDCARD)); if (match != 0) match = ((tag == scb->hscb->tag) || (tag == SCB_LIST_NULL)); @@ -4648,22 +5004,22 @@ ahc_match_scb (struct scb *scb, int target, char channel, int lun, u_int tag) static void ahc_construct_sdtr(struct ahc_softc *ahc, u_int period, u_int offset) { - ahc->msg_buf[ahc->msg_index++] = MSG_EXTENDED; - ahc->msg_buf[ahc->msg_index++] = MSG_EXT_SDTR_LEN; - ahc->msg_buf[ahc->msg_index++] = MSG_EXT_SDTR; - ahc->msg_buf[ahc->msg_index++] = period; - ahc->msg_buf[ahc->msg_index++] = offset; - ahc->msg_len += 5; + 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; + ahc->msgout_buf[ahc->msgout_index++] = period; + ahc->msgout_buf[ahc->msgout_index++] = offset; + ahc->msgout_len += 5; } static void ahc_construct_wdtr(struct ahc_softc *ahc, u_int bus_width) { - ahc->msg_buf[ahc->msg_index++] = MSG_EXTENDED; - ahc->msg_buf[ahc->msg_index++] = MSG_EXT_WDTR_LEN; - ahc->msg_buf[ahc->msg_index++] = MSG_EXT_WDTR; - ahc->msg_buf[ahc->msg_index++] = bus_width; - ahc->msg_len += 4; + ahc->msgout_buf[ahc->msgout_index++] = MSG_EXTENDED; + ahc->msgout_buf[ahc->msgout_index++] = MSG_EXT_WDTR_LEN; + ahc->msgout_buf[ahc->msgout_index++] = MSG_EXT_WDTR; + ahc->msgout_buf[ahc->msgout_index++] = bus_width; + ahc->msgout_len += 4; } static void @@ -4758,7 +5114,9 @@ ahc_update_pending_syncrates(struct ahc_softc *ahc) pending_scb = (struct scb *)ccbh->ccb_scb_ptr; pending_hscb = pending_scb->hscb; ahc_compile_devinfo(&devinfo, SCB_TARGET(pending_scb), - SCB_CHANNEL(pending_scb)); + SCB_LUN(pending_scb), + SCB_CHANNEL(pending_scb), + ROLE_UNKNOWN); tinfo = &ahc->transinfo[devinfo.target_offset]; pending_hscb->control &= ~ULTRAENB; if ((ahc->ultraenb & devinfo.target_mask) != 0) @@ -4789,7 +5147,9 @@ ahc_update_pending_syncrates(struct ahc_softc *ahc) pending_scb = ahc->scb_data->scbarray[scb_tag]; pending_hscb = pending_scb->hscb; ahc_compile_devinfo(&devinfo, SCB_TARGET(pending_scb), - SCB_CHANNEL(pending_scb)); + SCB_LUN(pending_scb), + SCB_CHANNEL(pending_scb), + ROLE_UNKNOWN); tinfo = &ahc->transinfo[devinfo.target_offset]; control = ahc_inb(ahc, SCB_CONTROL); control &= ~ULTRAENB; diff --git a/sys/dev/aic7xxx/aic7xxx.h b/sys/dev/aic7xxx/aic7xxx.h index 560cb3f..b2c240f 100644 --- a/sys/dev/aic7xxx/aic7xxx.h +++ b/sys/dev/aic7xxx/aic7xxx.h @@ -34,7 +34,7 @@ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * - * $Id: aic7xxx.h,v 1.1 1998/09/15 07:24:16 gibbs Exp $ + * $Id: aic7xxx.h,v 1.2 1998/11/23 01:33:47 gibbs Exp $ */ #ifndef _AIC7XXX_H_ @@ -143,7 +143,10 @@ typedef enum { AHC_EXTENDED_TRANS_B = 0x200, AHC_TERM_ENB_A = 0x400, AHC_TERM_ENB_B = 0x800, - AHC_HANDLING_REQINITS = 0x1000, + AHC_INITIATORMODE = 0x1000,/* + * Allow initiator operations on + * this controller. + */ AHC_TARGETMODE = 0x2000,/* * Allow target operations on this * controller. @@ -165,11 +168,6 @@ typedef enum { SCB_DEVICE_RESET = 0x0004, SCB_SENSE = 0x0008, SCB_RECOVERY_SCB = 0x0040, - SCB_MSGOUT_SENT = 0x0200, - SCB_MSGOUT_SDTR = 0x0400, - SCB_MSGOUT_WDTR = 0x0800, - SCB_MSGOUT_BITS = (SCB_MSGOUT_SDTR|SCB_MSGOUT_WDTR - |SCB_MSGOUT_SENT), SCB_ABORT = 0x1000, SCB_QUEUED_MSG = 0x2000, SCB_ACTIVE = 0x4000, @@ -384,7 +382,9 @@ struct ahc_syncrate { typedef enum { MSG_TYPE_NONE = 0x00, MSG_TYPE_INITIATOR_MSGOUT = 0x01, - MSG_TYPE_INITIATOR_MSGIN = 0x02 + MSG_TYPE_INITIATOR_MSGIN = 0x02, + MSG_TYPE_TARGET_MSGOUT = 0x03, + MSG_TYPE_TARGET_MSGIN = 0x04 } ahc_msg_type; struct ahc_softc { @@ -442,10 +442,9 @@ struct ahc_softc { * Per target state bitmasks. */ u_int16_t ultraenb; /* Using ultra sync rate */ - u_int16_t sdtrpending; /* Pending SDTR request */ - u_int16_t wdtrpending; /* Pending WDTR request */ u_int16_t discenable; /* Disconnection allowed */ u_int16_t tagenable; /* Tagged Queuing allowed */ + u_int16_t targ_msg_req; /* Need negotiation messages */ /* * Hooks into the XPT. @@ -481,10 +480,13 @@ struct ahc_softc { /* * Incoming and outgoing message handling. */ + u_int8_t send_msg_perror; ahc_msg_type msg_type; - u_int8_t msg_buf[8]; /* Message we are sending */ - u_int msg_len; /* Length of message to send */ - u_int msg_index; /* Current index in message */ + u_int8_t msgout_buf[8]; /* Message we are sending */ + u_int8_t msgin_buf[8]; /* Message we are receiving */ + u_int msgout_len; /* Length of message to send */ + u_int msgout_index; /* Current index in msgout */ + u_int msgin_index; /* Current index in msgin */ /* * "Bus" addresses of our data structures. diff --git a/sys/dev/aic7xxx/aic7xxx.reg b/sys/dev/aic7xxx/aic7xxx.reg index a6ab963..bcb96c2 100644 --- a/sys/dev/aic7xxx/aic7xxx.reg +++ b/sys/dev/aic7xxx/aic7xxx.reg @@ -32,7 +32,7 @@ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * - * $Id: aic7xxx.reg,v 1.7 1998/09/15 07:24:16 gibbs Exp $ + * $Id: aic7xxx.reg,v 1.8 1998/11/23 01:33:47 gibbs Exp $ */ /* @@ -675,20 +675,20 @@ register INTSTAT { mask SEND_REJECT 0x10|SEQINT /* sending a message reject */ mask NO_IDENT 0x20|SEQINT /* no IDENTIFY after reconnect*/ mask NO_MATCH 0x30|SEQINT /* no cmd match for reconnect */ - mask EXTENDED_MSG 0x40|SEQINT /* Extended message received */ mask ABORT_REQUESTED 0x50|SEQINT /* Reconect of aborted SCB */ mask REJECT_MSG 0x60|SEQINT /* Reject message received */ mask BAD_STATUS 0x70|SEQINT /* Bad status from target */ mask RESIDUAL 0x80|SEQINT /* Residual byte count != 0 */ - mask AWAITING_MSG 0xa0|SEQINT /* - * Kernel requested to specify - * a message to this target - * (command was null), so tell - * it that it can fill the - * message buffer. + mask HOST_MSG_LOOP 0xa0|SEQINT /* + * The bus is ready for the + * host to perform another + * message transaction. This + * mechanism is used for things + * like sync/wide negotiation + * that require a kernel based + * message state engine. */ mask TARGET_MSG_HELP 0xb0|SEQINT - mask TARGET_SYNC_CMD 0xc0|SEQINT mask TRACEPOINT 0xd0|SEQINT mask MSGIN_PHASEMIS 0xe0|SEQINT /* * Target changed phase on us @@ -1277,10 +1277,13 @@ scratch_ram { } ARG_1 { size 1 - mask SEND_MSG 0x80 - mask SEND_SENSE 0x40 - mask SEND_REJ 0x20 - mask MSGOUT_PHASEMIS 0x10 + mask SEND_MSG 0x80 + mask SEND_SENSE 0x40 + mask SEND_REJ 0x20 + mask MSGOUT_PHASEMIS 0x10 + mask EXIT_MSG_LOOP 0x08 + mask CONT_MSG_LOOP 0x04 + mask CONT_TARG_SESSION 0x02 alias RETURN_1 } ARG_2 { @@ -1303,6 +1306,14 @@ scratch_ram { size 1 } + /* + * Interrupt kernel for a message to this target on + * the next transaction. This is usually used for + * negotiation requests. + */ + TARGET_MSG_REQUEST { + size 2 + } /* * These are reserved registers in the card's scratch ram. Some of diff --git a/sys/dev/aic7xxx/aic7xxx.seq b/sys/dev/aic7xxx/aic7xxx.seq index 2a4e92e..007a3af 100644 --- a/sys/dev/aic7xxx/aic7xxx.seq +++ b/sys/dev/aic7xxx/aic7xxx.seq @@ -32,7 +32,7 @@ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * - * $Id: aic7xxx.seq,v 1.79 1998/09/21 16:46:13 gibbs Exp $ + * $Id: aic7xxx.seq,v 1.80 1998/11/23 01:33:47 gibbs Exp $ */ #include <dev/aic7xxx/aic7xxx.reg> @@ -58,6 +58,7 @@ reset: clr SCSISIGO; /* De-assert BSY */ + and SXFRCTL1, ~BITBUCKET; /* Always allow reselection */ if ((ahc->flags & AHC_TARGETMODE) != 0) { mvi SCSISEQ, ENSELI|ENRSELI|ENAUTOATNP; @@ -185,7 +186,7 @@ initialize_scsiid: and SINDEX, TARGET_SCB, SCB_CONTROL; or SCSISEQ, ENSELO|ENAUTOATNO|ENSELI |ENRSELI|ENAUTOATNP, SINDEX ret ; - } else { + } else if ((ahc->flags & AHC_INITIATORMODE) != 0) { mvi SCSISEQ, ENSELO|ENAUTOATNO|ENRSELI|ENAUTOATNP ret; } @@ -216,13 +217,17 @@ selection: mvi CLRSINT0, CLRSELDI; select_in: if ((ahc->flags & AHC_TARGETMODE) != 0) { - test SSTAT0, TARGET jz initiator_reselect; + if ((ahc->flags & AHC_INITIATORMODE) != 0) { + test SSTAT0, TARGET jz initiator_reselect; + } /* * We've just been selected. Assert BSY and * setup the phase for receiving messages * from the target. */ + mvi SCSISIGO, P_MESGOUT|BSYO; + mvi CLRSINT1, CLRBUSFREE; /* * LAST_MSG gives an indication to the host of what @@ -284,7 +289,7 @@ select_in: * initiator. We follow the guidlines from section 6.5 * of the SCSI-2 spec for what messages are allowed when. */ - call targ_inb; + call target_inb; /* * Our first message must be one of IDENTIFY, ABORT, or @@ -305,7 +310,7 @@ select_in: or SEQ_FLAGS, NO_DISCONNECT; test SCSISIGI, ATNI jz ident_messages_done; - call targ_inb; + call target_inb; /* * If this is a tagged request, the tagged message must * immediately follow the identify. We test for a valid @@ -332,7 +337,7 @@ select_in: /* * Store the tag for the host. */ - call targ_inb; + call target_inb; if ((ahc->features & AHC_CMD_CHAN) != 0) { mov CCSCBRAM, DINDEX; } else { @@ -340,6 +345,22 @@ select_in: } jmp ident_messages_done; + /* + * Pushed message loop to allow the kernel to + * run it's own message state engine. To avoid an + * extra nop instruction after signaling the kernel, + * we perform the phase_lock before checking to see + * if we should exit the loop and skip the phase_lock + * in the ITloop. Performing back to back phase_locks + * shouldn't hurt, but why do it twice... + */ +host_target_message_loop: + mvi INTSTAT, HOST_MSG_LOOP; + nop; + cmp RETURN_1, EXIT_MSG_LOOP je target_ITloop; + test SSTAT0, SPIORDY jz .; + jmp host_target_message_loop; + more_first_messages: /* * Hmm. Now we're down to only accepting @@ -361,6 +382,7 @@ ident_messages_done: mvi DFDAT, SCB_LIST_NULL; } or SEQ_FLAGS, TARG_CMD_PENDING; + test SCSISIGI, ATNI jnz target_mesgout_pending_msg; jmp target_ITloop; /* @@ -368,16 +390,20 @@ ident_messages_done: * message byte we receive so it can be checked prior to * driving REQ on the bus for the next byte. */ -targ_inb: - /* Drive REQ on the bus by enabling SCSI PIO */ +target_inb: + /* + * Drive REQ on the bus by enabling SCSI PIO. + */ or SXFRCTL0, SPIOEN; /* Wait for the byte */ test SSTAT0, SPIORDY jz .; /* Prevent our read from triggering another REQ */ and SXFRCTL0, ~SPIOEN; + /* Save latched contents */ mov DINDEX, SCSIDATL ret; } +if ((ahc->flags & AHC_INITIATORMODE) != 0) { /* * 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 yet. @@ -389,7 +415,7 @@ initiator_reselect: test SBLKCTL, SELBUSB jz . + 2; or SAVED_TCL, SELBUSB; } - or SXFRCTL0, SPIOEN|CLRCHN; + or SXFRCTL0, SPIOEN|CLRSTCNT|CLRCHN; mvi CLRSINT1,CLRBUSFREE; or SIMODE1, ENBUSFREE; /* * We aren't expecting a @@ -399,6 +425,7 @@ initiator_reselect: */ mvi MSG_OUT, MSG_NOOP; /* No message to send */ jmp ITloop; +} /* * After the selection, remove this SCB from the "waiting SCB" @@ -410,7 +437,7 @@ select_out: /* Turn off the selection hardware */ if ((ahc->flags & AHC_TARGETMODE) != 0) { mvi SCSISEQ, ENSELI|ENRSELI|ENAUTOATNP; - } else { + } else if ((ahc->flags & AHC_INITIATORMODE) != 0) { mvi SCSISEQ, ENRSELI|ENAUTOATNP; } mvi CLRSINT0, CLRSELDO; @@ -425,7 +452,8 @@ select_out: * Assert BSY and setup the phase for * sending our identify messages. */ - mvi SCSISIGO, P_MESGIN|BSYO; + mvi P_MESGIN|BSYO call change_phase; + mvi CLRSINT1,CLRBUSFREE; /* * Start out with a simple identify message. @@ -449,9 +477,10 @@ target_synccmd: target_ITloop: /* - * XXX Start honoring ATN signals now that - * we properly identified ourself. + * Start honoring ATN signals now that + * we properly identified ourself. */ + test SCSISIGI, ATNI jnz target_mesgout; test SEQ_FLAGS, CMDPHASE_PENDING jnz target_cmdphase; test SEQ_FLAGS, DPHASE_PENDING jnz target_dphase; test SEQ_FLAGS, SPHASE_PENDING jnz target_sphase; @@ -464,17 +493,25 @@ target_ITloop: if ((ahc->flags & AHC_PAGESCBS) != 0) { mov ALLZEROS call get_free_or_disc_scb; } + mov RETURN_1, ALLZEROS; call complete_target_cmd; + cmp RETURN_1, CONT_MSG_LOOP jne .; mvi DMAPARAMS, HDMAEN|DIRECTION|FIFORESET; mov SCB_TAG call dma_scb; jmp target_synccmd; +target_mesgout: + mvi SCSISIGO, P_MESGOUT|BSYO; + call target_inb; + /* Local Processing goes here... */ +target_mesgout_pending_msg: + jmp host_target_message_loop; + target_disconnect: - mvi SCSISIGO, P_MESGIN|BSYO; + mvi P_MESGIN|BSYO call change_phase; mvi MSG_DISCONNECT call target_outb; target_busfree: - and SXFRCTL0, ~SPIOEN; clr SCSISIGO; call complete_target_cmd; cmp LAST_MSG, MSG_NOOP je . + 2; @@ -483,8 +520,8 @@ target_busfree: jmp poll_for_work; target_cmdphase: - mvi SCSISIGO, P_COMMAND|BSYO; - call targ_inb; + mvi P_COMMAND|BSYO call change_phase; + call target_inb; mov A, DINDEX; /* Store for host */ if ((ahc->features & AHC_CMD_CHAN) != 0) { @@ -528,20 +565,20 @@ target_dphase: * Data direction flags are from the * perspective of the initiator. */ - and SXFRCTL0, ~SPIOEN; - or SXFRCTL0, CLRCHN; + mov ALLZEROS call initialize_channel; test SCB_TARGET_PHASES[1], TARGET_DATA_IN jz . + 4; - mvi SCSISIGO, P_DATAIN|BSYO; mvi LASTPHASE, P_DATAOUT; + mvi P_DATAIN|BSYO call change_phase; jmp p_data; - mvi SCSISIGO, P_DATAOUT|BSYO; mvi LASTPHASE, P_DATAIN; + mvi P_DATAOUT|BSYO call change_phase; jmp p_data; target_sphase: - mvi SCSISIGO, P_STATUS|BSYO; + mvi P_STATUS|BSYO call change_phase; + mvi LASTPHASE, P_STATUS; mov SCB_TARGET_STATUS call target_outb; - /* XXX Watch for ATN for parity errors??? */ + /* XXX Watch for ATN or parity errors??? */ mvi SCSISIGO, P_MESGIN|BSYO; /* MSG_CMDCMPLT is 0, but we can't do an immediate of 0 */ mov ALLZEROS call target_outb; @@ -570,29 +607,33 @@ complete_target_cmd: call dma_finish; } inc TQINPOS; - test SEQ_FLAGS, NO_DISCONNECT jz . + 2; - mvi INTSTAT,TARGET_SYNC_CMD|CMDCMPLT ret; mvi INTSTAT,CMDCMPLT ret; } + +if ((ahc->flags & AHC_INITIATORMODE) != 0) { initiator_select: mvi SPIOEN call initialize_channel; -/* - * We aren't expecting a bus free, so interrupt - * the kernel driver if it happens. - */ + + /* + * We aren't expecting a bus free, so interrupt + * the kernel driver if it happens. + */ mvi CLRSINT1,CLRBUSFREE; or SIMODE1, ENBUSFREE; -/* - * As soon as we get a successful selection, the target should go - * into the message out phase since we have ATN asserted. - */ + + /* + * As soon as we get a successful selection, the target + * should go into the message out phase since we have ATN + * asserted. + */ mvi MSG_OUT, MSG_IDENTIFYFLAG; or SEQ_FLAGS, IDENTIFY_SEEN; -/* - * Main loop for information transfer phases. Wait for the target - * to assert REQ before checking MSG, C/D and I/O for the bus phase. - */ + /* + * Main loop for information transfer phases. Wait for the + * target to assert REQ before checking MSG, C/D and I/O for + * the bus phase. + */ ITloop: call phase_lock; @@ -604,7 +645,7 @@ ITloop: cmp A,P_STATUS je p_status; cmp A,P_MESGIN je p_mesgin; - mvi INTSTAT,BAD_PHASE; /* unknown phase - signal driver */ + mvi INTSTAT,BAD_PHASE; jmp ITloop; /* Try reading the bus again. */ await_busfree: @@ -615,20 +656,27 @@ await_busfree: test SSTAT1,REQINIT|BUSFREE jz .; test SSTAT1, BUSFREE jnz poll_for_work; mvi INTSTAT, BAD_PHASE; +} clear_target_state: - clr DFCNTRL; /* - * We assume that the kernel driver - * may reset us at any time, even - * in the middle of a DMA, so clear - * DFCNTRL too. - */ - clr SCSIRATE; /* - * We don't know the target we will - * connect to, so default to narrow - * transfers to avoid parity problems. - */ - and SXFRCTL0, ~(FAST20); + /* + * We assume that the kernel driver may reset us + * at any time, even in the middle of a DMA, so + * clear DFCNTRL too. + */ + clr DFCNTRL; + + /* + * We don't know the target we will connect to, + * so default to narrow transfers to avoid + * parity problems. + */ + if ((ahc->features & AHC_ULTRA2) != 0) { + bmov SCSIRATE, ALLZEROS, 2; + } else { + clr SCSIRATE; + and SXFRCTL0, ~(FAST20); + } mvi LASTPHASE, P_BUSFREE; /* clear target specific flags */ clr SEQ_FLAGS ret; @@ -818,7 +866,12 @@ prefetched_segs_avail: add SG_NEXT[0],SG_SIZEOF; adc SG_NEXT[1],A; + if ((ahc->flags & AHC_TARGETMODE) != 0) { + test SSTAT0, TARGET jnz data_phase_loop; + } + test SSTAT1, REQINIT jz .; test SSTAT1,PHASEMIS jz data_phase_loop; + /* Ensure the last seg is visable at the shaddow layer */ if ((ahc->features & AHC_ULTRA2) != 0) { or DFCNTRL, PRELOADEN; @@ -847,7 +900,7 @@ data_phase_finish: } if ((ahc->flags & AHC_TARGETMODE) != 0) { - test SEQ_FLAGS, DPHASE_PENDING jz . + 3; + test SEQ_FLAGS, DPHASE_PENDING jz ITloop; and SEQ_FLAGS, ~DPHASE_PENDING; jmp target_ITloop; } @@ -878,6 +931,7 @@ ultra2_dmahalt: ret; } +if ((ahc->flags & AHC_INITIATORMODE) != 0) { /* * Command phase. Set up the DMA registers and let 'er rip. */ @@ -934,17 +988,22 @@ p_status: jmp ITloop; /* - * Message out phase. If MSG_OUT is 0x80, build I full indentify message - * sequence and send it to the target. In addition, if the MK_MESSAGE bit - * is set in the SCB_CONTROL byte, interrupt the host and allow it to send - * it's own message. + * Message out phase. If MSG_OUT is MSG_IDENTIFYFLAG, build a full + * indentify message sequence and send it to the target. The host may + * override this behavior by setting the MK_MESSAGE bit in the SCB + * control byte. This will cause us to interrupt the host and allow + * it to handle the message phase completely on its own. If the bit + * associated with this target is set, we will also interrupt the host, + * thereby allowing it to send a message on the next selection regardless + * of the transaction being sent. * * If MSG_OUT is == HOST_MSG, also interrupt the host and take a message. - * This is done to allow the hsot to send messages outside of an identify + * This is done to allow the host to send messages outside of an identify * sequence while protecting the seqencer from testing the MK_MESSAGE bit * on an SCB that might not be for the current nexus. (For example, a * BDR message in responce to a bad reselection would leave us pointed to * an SCB that doesn't have anything to do with the current target). + * * Otherwise, treat MSG_OUT as a 1 byte message to send (abort, abort tag, * bus device reset). * @@ -952,18 +1011,29 @@ p_status: * in case the target decides to put us in this phase for some strange * reason. */ +p_mesgout_retry: + or SCSISIGO,ATNO,LASTPHASE;/* turn on ATN for the retry */ p_mesgout: mov SINDEX, MSG_OUT; cmp SINDEX, MSG_IDENTIFYFLAG jne p_mesgout_from_host; + test SCB_CONTROL,MK_MESSAGE jnz host_message_loop; + mov FUNCTION1, SCB_TCL; + mov A, FUNCTION1; + mov SINDEX, TARGET_MSG_REQUEST[0]; + if ((ahc->features & AHC_TWIN) != 0) { + /* Second Channel uses high byte bits */ + test SCB_TCL, SELBUSB jz . + 2; + mov SINDEX, TARGET_MSG_REQUEST[1]; + } else if ((ahc->features & AHC_WIDE) != 0) { + test SCB_TCL, 0x80 jz . + 2; /* target > 7 */ + mov SINDEX, TARGET_MSG_REQUEST[1]; + } + test SINDEX, A jnz host_message_loop; p_mesgout_identify: and SINDEX,LID,SCB_TCL; /* lun */ and A,DISCENB,SCB_CONTROL; /* mask off disconnect privledge */ or SINDEX,A; /* or in disconnect privledge */ or SINDEX,MSG_IDENTIFYFLAG; -p_mesgout_mk_message: - test SCB_CONTROL,MK_MESSAGE jz p_mesgout_tag; - mov SCSIDATL, SINDEX; /* Send the last byte */ - jmp p_mesgout_from_host + 1;/* Skip HOST_MSG test */ /* * Send a tag message if TAG_ENB is set in the SCB control block. * Use SCB_TAG (the position in the kernel's SCB array) as the tag value. @@ -978,40 +1048,46 @@ p_mesgout_tag: cmp LASTPHASE, P_MESGOUT jne p_mesgout_done; mov SCB_TAG jmp p_mesgout_onebyte; /* - * Interrupt the driver, and allow it to send a message - * if it asks. + * Interrupt the driver, and allow it to handle this message + * phase and any required retries. */ p_mesgout_from_host: cmp SINDEX, HOST_MSG jne p_mesgout_onebyte; - mvi INTSTAT,AWAITING_MSG; - nop; - /* - * Did the host detect a phase change? - */ - cmp RETURN_1, MSGOUT_PHASEMIS je p_mesgout_done; + jmp host_message_loop; p_mesgout_onebyte: mvi CLRSINT1, CLRATNO; mov SCSIDATL, SINDEX; /* - * If the next bus phase after ATN drops is a message out, it means + * If the next bus phase after ATN drops is message out, it means * that the target is requesting that the last message(s) be resent. */ call phase_lock; - cmp LASTPHASE, P_MESGOUT jne p_mesgout_done; - or SCSISIGO,ATNO,LASTPHASE;/* turn on ATN for the retry */ - jmp p_mesgout; + cmp LASTPHASE, P_MESGOUT je p_mesgout_retry; p_mesgout_done: mvi CLRSINT1,CLRATNO; /* Be sure to turn ATNO off */ mov LAST_MSG, MSG_OUT; - cmp MSG_OUT, MSG_IDENTIFYFLAG jne . + 2; - and SCB_CONTROL, ~MK_MESSAGE; mvi MSG_OUT, MSG_NOOP; /* No message left */ jmp ITloop; /* + * Pushed message loop to allow the kernel to + * RUN IT's own message state engine. To avoid an + * extra nop instruction after signaling the kernel, + * we perform the phase_lock before checking to see + * if we should exit the loop and skip the phase_lock + * in the ITloop. Performing back to back phase_locks + * shouldn't hurt, but why do it twice... + */ +host_message_loop: + mvi INTSTAT, HOST_MSG_LOOP; + call phase_lock; + cmp RETURN_1, EXIT_MSG_LOOP je ITloop + 1; + jmp host_message_loop; + +/* * Message in phase. Bytes are read using Automatic PIO mode. */ p_mesgin: @@ -1022,7 +1098,7 @@ p_mesgin: cmp A,MSG_SAVEDATAPOINTER je mesgin_sdptrs; cmp ALLZEROS,A je mesgin_complete; cmp A,MSG_RESTOREPOINTERS je mesgin_rdptrs; - cmp A,MSG_EXTENDED je mesgin_extended; + cmp A,MSG_EXTENDED je host_message_loop; cmp A,MSG_MESSAGE_REJECT je mesgin_reject; cmp A,MSG_NOOP je mesgin_done; @@ -1099,6 +1175,7 @@ complete_queue: add_to_free_list: call add_scb_to_free_list; jmp await_busfree; +} complete_post: /* Post the SCBID in SINDEX and issue an interrupt */ @@ -1113,22 +1190,9 @@ complete_post: if ((ahc->features & AHC_QUEUE_REGS) == 0) { inc QOUTPOS; } - if ((ahc->flags & AHC_TARGETMODE) != 0) { - test SEQ_FLAGS, NO_DISCONNECT jz . + 2; - mvi INTSTAT,TARGET_SYNC_CMD|CMDCMPLT ret; - } mvi INTSTAT,CMDCMPLT ret; -/* - * Is it an extended message? Copy the message to our message buffer and - * notify the host. The host will tell us whether to reject this message, - * respond to it with the message that the host placed in our message buffer, - * or simply to do nothing. - */ -mesgin_extended: - mvi INTSTAT,EXTENDED_MSG; /* let driver know */ - jmp ITloop; - +if ((ahc->flags & AHC_INITIATORMODE) != 0) { /* * Is it a disconnect message? Set a flag in the SCB to remind us * and await the bus going free. @@ -1311,18 +1375,35 @@ inb_first: mov DINDIR,SCSIBUSL ret; /*read byte directly from bus*/ inb_last: mov NONE,SCSIDATL ret; /*dummy read from latch to ACK*/ +} if ((ahc->flags & AHC_TARGETMODE) != 0) { - /* - * Send a byte to an initiator in Automatic PIO mode. - * SPIOEN must be on prior to calling this routine. - */ +/* + * Change to a new phase. If we are changing the state of the I/O signal, + * from out to in, wait an additional data release delay before continuing. + */ +change_phase: + and DINDEX, IOI, SCSISIGI; + mov SCSISIGO, SINDEX; + and A, IOI, SINDEX; + cmp DINDEX, A je change_phase_wait; + test SINDEX, IOI jz change_phase_wait; + call change_phase_wait; +change_phase_wait: + nop; + nop; + nop; + nop ret; + +/* + * Send a byte to an initiator in Automatic PIO mode. + */ target_outb: or SXFRCTL0, SPIOEN; test SSTAT0, SPIORDY jz .; mov SCSIDATL, SINDEX; test SSTAT0, SPIORDY jz .; - ret; + and SXFRCTL0, ~SPIOEN ret; } mesgin_phasemis: @@ -1541,8 +1622,8 @@ get_SCBID_from_host: phase_lock: test SSTAT1, REQINIT jz phase_lock; test SSTAT1, SCSIPERR jnz phase_lock; - and LASTPHASE, PHASE_MASK, SCSISIGI; - mov SCSISIGO, LASTPHASE ret; + and SCSISIGO, PHASE_MASK, SCSISIGI; + and LASTPHASE, PHASE_MASK, SCSISIGI ret; if ((ahc->features & AHC_CMD_CHAN) == 0) { set_stcnt_from_hcnt: diff --git a/sys/dev/aic7xxx/aicasm/aicasm_gram.y b/sys/dev/aic7xxx/aicasm/aicasm_gram.y index 09a62b9..fdf3522 100644 --- a/sys/dev/aic7xxx/aicasm/aicasm_gram.y +++ b/sys/dev/aic7xxx/aicasm/aicasm_gram.y @@ -26,7 +26,7 @@ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * - * $Id: aicasm_gram.y,v 1.4 1997/09/27 19:37:28 gibbs Exp $ + * $Id: aicasm_gram.y,v 1.5 1998/09/15 07:24:17 gibbs Exp $ */ #include <stdio.h> @@ -861,12 +861,12 @@ code: ; code: - T_NOP ';' + T_NOP ret ';' { expression_t immed; make_expression(&immed, 0xff); - format_1_instr(AIC_OP_AND, &none, &immed, &allzeros, FALSE); + format_1_instr(AIC_OP_AND, &none, &immed, &allzeros, $2); } ; diff --git a/sys/dev/aic7xxx/aicasm_gram.y b/sys/dev/aic7xxx/aicasm_gram.y index 09a62b9..fdf3522 100644 --- a/sys/dev/aic7xxx/aicasm_gram.y +++ b/sys/dev/aic7xxx/aicasm_gram.y @@ -26,7 +26,7 @@ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * - * $Id: aicasm_gram.y,v 1.4 1997/09/27 19:37:28 gibbs Exp $ + * $Id: aicasm_gram.y,v 1.5 1998/09/15 07:24:17 gibbs Exp $ */ #include <stdio.h> @@ -861,12 +861,12 @@ code: ; code: - T_NOP ';' + T_NOP ret ';' { expression_t immed; make_expression(&immed, 0xff); - format_1_instr(AIC_OP_AND, &none, &immed, &allzeros, FALSE); + format_1_instr(AIC_OP_AND, &none, &immed, &allzeros, $2); } ; |