summaryrefslogtreecommitdiffstats
path: root/sys
diff options
context:
space:
mode:
authorgibbs <gibbs@FreeBSD.org>1999-01-14 06:14:15 +0000
committergibbs <gibbs@FreeBSD.org>1999-01-14 06:14:15 +0000
commitb0bf7fe0ffc8c67f71b47dbcce97e589f7a925e3 (patch)
treec1d8ff4c7c13731bc2b4ff206d30fbe777b0a02f /sys
parenteec3d5cfc887b77bd68687cd2cd39b51e2068162 (diff)
downloadFreeBSD-src-b0bf7fe0ffc8c67f71b47dbcce97e589f7a925e3.zip
FreeBSD-src-b0bf7fe0ffc8c67f71b47dbcce97e589f7a925e3.tar.gz
Add support for routing initiator transactions to disabled luns to the
black hole device. The controller will now only accept selections if the black hole device is present and some other target/lun is enabled for target mode. Handle the IGNORE WIDE RESIDUE message. This support has not been tested. Checkpoint work on handling ABORT, BUS DEVICE RESET, TERMINATE I/O PROCESS, and CLEAR QUEUE messages as a target. Fix a few problems with tagged command handling in target mode. Wait until the sync offset counter falls to 0 before changing phase after a data-in transfer completes as the DMA logic seems to indicate transfer complete as soon as our last REQ is issued. Simplify some of the target mode message handling code in the sequencer.
Diffstat (limited to 'sys')
-rw-r--r--sys/dev/aic7xxx/aic7xxx.c618
-rw-r--r--sys/dev/aic7xxx/aic7xxx.h10
-rw-r--r--sys/dev/aic7xxx/aic7xxx.reg22
-rw-r--r--sys/dev/aic7xxx/aic7xxx.seq65
4 files changed, 452 insertions, 263 deletions
diff --git a/sys/dev/aic7xxx/aic7xxx.c b/sys/dev/aic7xxx/aic7xxx.c
index 7b22345..ee2cc0e 100644
--- a/sys/dev/aic7xxx/aic7xxx.c
+++ b/sys/dev/aic7xxx/aic7xxx.c
@@ -5,7 +5,7 @@
* pci/ahc_pci.c 3985, 3980, 3940, 2940, aic7895, aic7890,
* aic7880, aic7870, aic7860, and aic7850 controllers
*
- * Copyright (c) 1994, 1995, 1996, 1997, 1998 Justin T. Gibbs.
+ * Copyright (c) 1994, 1995, 1996, 1997, 1998, 1999 Justin T. Gibbs.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
@@ -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.13 1998/12/15 08:22:40 gibbs Exp $
+ * $Id: aic7xxx.c,v 1.14 1998/12/17 00:06:52 gibbs Exp $
*/
/*
* A few notes on features of the driver.
@@ -219,7 +219,9 @@ static void ahc_compile_devinfo(struct ahc_devinfo *devinfo,
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,
+static void ahc_handle_en_lun(struct ahc_softc *ahc, struct cam_sim *sim,
+ union ccb *ccb);
+static int 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);
@@ -238,6 +240,8 @@ static void ahc_handle_message_phase(struct ahc_softc *ahc,
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_ign_wide_residue(struct ahc_softc *ahc,
+ struct ahc_devinfo *devinfo);
static void ahc_handle_devreset(struct ahc_softc *ahc, int target,
char channel, cam_status status,
ac_code acode, char *message,
@@ -1179,8 +1183,15 @@ ahc_intr(void *arg)
while (ahc->targetcmds[ahc->tqinfifonext].cmd_valid) {
struct target_cmd *cmd;
- cmd = &ahc->targetcmds[ahc->tqinfifonext++];
- ahc_handle_target_cmd(ahc, cmd);
+ cmd = &ahc->targetcmds[ahc->tqinfifonext];
+
+ /*
+ * Only advance through the queue if we
+ * had the resources to process the command.
+ */
+ if (ahc_handle_target_cmd(ahc, cmd) != 0)
+ break;
+ ahc->tqinfifonext++;
cmd->cmd_valid = 0;
}
}
@@ -1213,6 +1224,193 @@ ahc_intr(void *arg)
}
static void
+ahc_handle_en_lun(struct ahc_softc *ahc, struct cam_sim *sim, union ccb *ccb)
+{
+ struct tmode_tstate *tstate;
+ struct tmode_lstate *lstate;
+ struct ccb_en_lun *cel;
+ cam_status status;
+ int target;
+ int lun;
+
+ status = ahc_find_tmode_devs(ahc, sim, ccb, &tstate, &lstate,
+ /* notfound_failure*/FALSE);
+
+ if (status != CAM_REQ_CMP) {
+ ccb->ccb_h.status = status;
+ return;
+ }
+
+ cel = &ccb->cel;
+ target = ccb->ccb_h.target_id;
+ lun = ccb->ccb_h.target_lun;
+ if (cel->enable != 0) {
+ u_int scsiseq;
+
+ /* Are we already enabled?? */
+ if (lstate != NULL) {
+ ccb->ccb_h.status = CAM_LUN_ALRDY_ENA;
+ return;
+ }
+
+ if (cel->grp6_len != 0
+ || cel->grp7_len != 0) {
+ /*
+ * Don't (yet?) support vendor
+ * specific commands.
+ */
+ ccb->ccb_h.status = CAM_REQ_INVALID;
+ return;
+ }
+
+ /*
+ * Seems to be okay.
+ * Setup our data structures.
+ */
+ if (target != CAM_TARGET_WILDCARD && tstate == NULL) {
+ tstate = malloc(sizeof(*tstate), M_DEVBUF, M_NOWAIT);
+ if (tstate == NULL) {
+ ccb->ccb_h.status = CAM_RESRC_UNAVAIL;
+ return;
+ }
+ bzero(tstate, sizeof(*tstate));
+ ahc->enabled_targets[target] = tstate;
+ }
+ lstate = malloc(sizeof(*lstate), M_DEVBUF, M_NOWAIT);
+ if (lstate == NULL) {
+ ccb->ccb_h.status = CAM_RESRC_UNAVAIL;
+ return;
+ }
+ bzero(lstate, sizeof(*lstate));
+ SLIST_INIT(&lstate->accept_tios);
+ SLIST_INIT(&lstate->immed_notifies);
+ if (target != CAM_TARGET_WILDCARD) {
+ tstate->enabled_luns[lun] = lstate;
+ ahc->enabled_luns++;
+ } else
+ ahc->black_hole = lstate;
+ pause_sequencer(ahc);
+ if ((ahc->features & AHC_MULTI_TID) != 0) {
+ u_int16_t targid_mask;
+
+ targid_mask = ahc_inb(ahc, TARGID)
+ | (ahc_inb(ahc, TARGID + 1) << 8);
+
+ targid_mask |= (0x01 << target);
+ ahc_outb(ahc, TARGID, targid_mask);
+ ahc_outb(ahc, TARGID+1, (targid_mask >> 8));
+ }
+ /* Allow select-in operations */
+ if (ahc->black_hole != NULL && ahc->enabled_luns > 0) {
+ scsiseq = ahc_inb(ahc, SCSISEQ_TEMPLATE);
+ scsiseq |= ENSELI;
+ ahc_outb(ahc, SCSISEQ_TEMPLATE, scsiseq);
+ scsiseq = ahc_inb(ahc, SCSISEQ);
+ scsiseq |= ENSELI;
+ ahc_outb(ahc, SCSISEQ, scsiseq);
+ }
+ unpause_sequencer(ahc, /*always?*/FALSE);
+ ccb->ccb_h.status = CAM_REQ_CMP;
+ xpt_print_path(ccb->ccb_h.path);
+ printf("Lun now enabled for target mode\n");
+ xpt_done(ccb);
+ } else {
+ struct ccb_hdr *elm;
+
+ if (lstate == NULL) {
+ ccb->ccb_h.status = CAM_LUN_INVALID;
+ return;
+ }
+
+ ccb->ccb_h.status = CAM_REQ_CMP;
+ LIST_FOREACH(elm, &ahc->pending_ccbs, sim_links.le) {
+ if (elm->func_code == XPT_CONT_TARGET_IO
+ && !xpt_path_comp(elm->path, ccb->ccb_h.path)){
+ printf("CTIO pending\n");
+ ccb->ccb_h.status = CAM_REQ_INVALID;
+ return;
+ }
+ }
+
+ if (SLIST_FIRST(&lstate->accept_tios) != NULL) {
+ printf("ATIOs pending\n");
+ ccb->ccb_h.status = CAM_REQ_INVALID;
+ }
+
+ if (SLIST_FIRST(&lstate->immed_notifies) != NULL) {
+ printf("INOTs pending\n");
+ ccb->ccb_h.status = CAM_REQ_INVALID;
+ }
+
+ if (ccb->ccb_h.status == CAM_REQ_CMP) {
+ int i, empty;
+
+ xpt_print_path(ccb->ccb_h.path);
+ printf("Target mode disabled\n");
+ free(lstate, M_DEVBUF);
+
+ pause_sequencer(ahc);
+ /* Can we clean up the target too? */
+ if (target != CAM_TARGET_WILDCARD) {
+ tstate->enabled_luns[lun] = NULL;
+ ahc->enabled_luns--;
+ for (empty = 1, i = 0; i < 8; i++)
+ if (tstate->enabled_luns[i] != NULL) {
+ empty = 0;
+ break;
+ }
+ if (empty) {
+ free(tstate, M_DEVBUF);
+ ahc->enabled_targets[target] = NULL;
+ if (ahc->features & AHC_MULTI_TID) {
+ u_int16_t targid_mask;
+
+ targid_mask =
+ ahc_inb(ahc, TARGID)
+ | (ahc_inb(ahc, TARGID + 1)
+ << 8);
+
+ targid_mask &= (0x01 << target);
+ ahc_outb(ahc, TARGID,
+ targid_mask);
+ ahc_outb(ahc, TARGID+1,
+ (targid_mask >> 8));
+ }
+
+ for (empty = 1, i = 0; i < 16; i++)
+ if (ahc->enabled_targets[i]
+ != NULL) {
+ empty = 0;
+ break;
+ }
+ }
+ } else {
+
+ ahc->black_hole = NULL;
+
+ /*
+ * We can't allow selections without
+ * our black hole device.
+ */
+ empty = TRUE;
+ }
+ if (empty) {
+ /* Disallow select-in */
+ u_int scsiseq;
+
+ scsiseq = ahc_inb(ahc, SCSISEQ_TEMPLATE);
+ scsiseq &= ~ENSELI;
+ ahc_outb(ahc, SCSISEQ_TEMPLATE, scsiseq);
+ scsiseq = ahc_inb(ahc, SCSISEQ);
+ scsiseq &= ~ENSELI;
+ ahc_outb(ahc, SCSISEQ, scsiseq);
+ }
+ unpause_sequencer(ahc, /*always?*/FALSE);
+ }
+ }
+}
+
+static int
ahc_handle_target_cmd(struct ahc_softc *ahc, struct target_cmd *cmd)
{
struct tmode_tstate *tstate;
@@ -1234,22 +1432,26 @@ ahc_handle_target_cmd(struct ahc_softc *ahc, struct target_cmd *cmd)
lstate = tstate->enabled_luns[lun];
/*
- * XXX Need to have a default TMODE devce that attaches to luns
- * that wouldn't otherwise be enabled and returns the proper
- * inquiry information. After all, we don't want to duplicate
- * this code in each driver. For now, simply drop it on the
- * floor.
+ * Commands for disabled luns go to the black hole driver.
*/
if (lstate == NULL) {
printf("Incoming Command on disabled lun\n");
- return;
+ lstate = ahc->black_hole;
+ atio =
+ (struct ccb_accept_tio*)SLIST_FIRST(&lstate->accept_tios);
+ /* Fill in the wildcards */
+ atio->ccb_h.target_id = target;
+ atio->ccb_h.target_lun = lun;
+ } else {
+ atio =
+ (struct ccb_accept_tio*)SLIST_FIRST(&lstate->accept_tios);
}
-
- atio = (struct ccb_accept_tio*)SLIST_FIRST(&lstate->accept_tios);
- /* XXX Should reconnect and return BUSY status */
if (atio == NULL) {
printf("No ATIOs for incoming command\n");
- return;
+ /*
+ * Wait for more ATIOs from the peripheral driver for this lun.
+ */
+ return (1);
}
SLIST_REMOVE_HEAD(&lstate->accept_tios, sim_links.sle);
@@ -1304,6 +1506,7 @@ ahc_handle_target_cmd(struct ahc_softc *ahc, struct target_cmd *cmd)
ahc->pending_device = lstate;
}
xpt_done((union ccb*)atio);
+ return (0);
}
static void
@@ -1533,6 +1736,17 @@ ahc_handle_seqint(struct ahc_softc *ahc, u_int intstat)
}
break;
}
+ case TRACE_POINT:
+ {
+ printf("SSTAT2 = 0x%x DFCNTRL = 0x%x\n", ahc_inb(ahc, SSTAT2),
+ ahc_inb(ahc, DFCNTRL));
+ printf("SSTAT3 = 0x%x DSTATUS = 0x%x\n", ahc_inb(ahc, SSTAT3),
+ ahc_inb(ahc, DFSTATUS));
+ printf("SSTAT0 = 0x%x, SCB_DATACNT = 0x%x\n",
+ ahc_inb(ahc, SSTAT0),
+ ahc_inb(ahc, SCB_DATACNT));
+ break;
+ }
case TARGET_MSG_HELP:
{
/*
@@ -2271,7 +2485,7 @@ reswitch:
int msgout_request;
if (ahc->msgout_len == 0)
- panic("Target REQINIT with no active message");
+ panic("Target MSGIN with no active message");
/*
* If we interrupted a mesgout session, the initiator
@@ -2449,14 +2663,33 @@ ahc_parse_msg(struct ahc_softc *ahc, struct cam_path *path,
* byte outright and perform more checking once we know the
* extended message type.
*/
- if (ahc->msgin_buf[0] == MSG_MESSAGE_REJECT) {
+ switch (ahc->msgin_buf[0]) {
+ case MSG_MESSAGE_REJECT:
response = ahc_handle_msg_reject(ahc, devinfo);
+ /* FALLTHROUGH */
+ case MSG_NOOP:
done = TRUE;
- } else if (ahc->msgin_buf[0] == MSG_NOOP) {
- done = TRUE;
- } else if (ahc->msgin_buf[0] != MSG_EXTENDED) {
- reject = TRUE;
- } else if (ahc->msgin_index >= 2) {
+ break;
+ case MSG_IGN_WIDE_RESIDUE:
+ {
+ struct ahc_target_tinfo *tinfo;
+
+ tinfo = &ahc->transinfo[devinfo->target_offset];
+ /* Wait for the whole message */
+ if (ahc->msgin_index >= 1) {
+ if (ahc->msgin_buf[1] != 1
+ || tinfo->current.width == MSG_EXT_WDTR_BUS_8_BIT) {
+ reject = TRUE;
+ done = TRUE;
+ } else
+ ahc_handle_ign_wide_residue(ahc, devinfo);
+ }
+ }
+ case MSG_EXTENDED:
+ {
+ /* Wait for enough of the message to begin validation */
+ if (ahc->msgin_index < 2)
+ break;
switch (ahc->msgin_buf[2]) {
case MSG_EXT_SDTR:
{
@@ -2536,7 +2769,13 @@ ahc_parse_msg(struct ahc_softc *ahc, struct cam_path *path,
if (ahc->msgin_index < (MSG_EXT_WDTR_LEN + 1))
break;
- if (devinfo->role == ROLE_TARGET) {
+ /*
+ * Due to a problem with sync/wide transfers
+ * on the aic7880 only allow this on Ultra2
+ * controllers for the moment.
+ */
+ if (devinfo->role == ROLE_TARGET
+ && (ahc->features & AHC_ULTRA2) == 0) {
reject = TRUE;
break;
}
@@ -2631,6 +2870,19 @@ ahc_parse_msg(struct ahc_softc *ahc, struct cam_path *path,
break;
}
}
+ case MSG_ABORT:
+ case MSG_ABORT_TAG:
+ case MSG_BUS_DEV_RESET:
+ case MSG_CLEAR_QUEUE:
+ case MSG_TERM_IO_PROC:
+ /* Target mode messages */
+ if (devinfo->role != ROLE_TARGET)
+ reject = TRUE;
+ break;
+ default:
+ reject = TRUE;
+ break;
+ }
if (reject) {
/*
@@ -2652,6 +2904,89 @@ ahc_parse_msg(struct ahc_softc *ahc, struct cam_path *path,
}
static void
+ahc_handle_ign_wide_residue(struct ahc_softc *ahc, struct ahc_devinfo *devinfo)
+{
+ u_int scb_index;
+ struct scb *scb;
+
+ scb_index = ahc_inb(ahc, SCB_TAG);
+ scb = ahc->scb_data->scbarray[scb_index];
+ if ((ahc_inb(ahc, SEQ_FLAGS) & DPHASE) == 0
+ || (scb->ccb->ccb_h.flags & CAM_DIR_MASK) != CAM_DIR_IN) {
+ /*
+ * Ignore the message if we haven't
+ * seen an appropriate data phase yet.
+ */
+ } else {
+ /*
+ * If the residual occurred on the last
+ * transfer and the transfer request was
+ * expected to end on an odd count, do
+ * nothing. Otherwise, subtract a byte
+ * and update the residual count accordingly.
+ */
+ u_int resid_sgcnt;
+
+ resid_sgcnt = ahc_inb(ahc, SCB_RESID_SGCNT);
+ if (resid_sgcnt == 0
+ && ahc_inb(ahc, DATA_COUNT_ODD) == 1) {
+ /*
+ * If the residual occurred on the last
+ * transfer and the transfer request was
+ * expected to end on an odd count, do
+ * nothing.
+ */
+ } else {
+ u_int data_cnt;
+ u_int data_addr;
+ u_int sg_index;
+
+ data_cnt = (ahc_inb(ahc, SCB_RESID_DCNT + 2) << 16)
+ | (ahc_inb(ahc, SCB_RESID_DCNT + 1) << 8)
+ | (ahc_inb(ahc, SCB_RESID_DCNT));
+
+ data_addr = (ahc_inb(ahc, SHADDR + 3) << 24)
+ | (ahc_inb(ahc, SHADDR + 2) << 16)
+ | (ahc_inb(ahc, SHADDR + 1) << 8)
+ | (ahc_inb(ahc, SHADDR));
+
+ data_cnt += 1;
+ data_addr -= 1;
+
+ sg_index = scb->sg_count - resid_sgcnt;
+
+ /*
+ * scb->ahc_dma starts with the second S/G entry.
+ */
+ if (sg_index-- != 0
+ && (scb->ahc_dma[sg_index].len < data_cnt)) {
+ u_int sg_addr;
+
+ data_cnt = 1;
+ data_addr = scb->ahc_dma[sg_index - 1].addr
+ + scb->ahc_dma[sg_index - 1].len - 1;
+
+ sg_addr = scb->ahc_dmaphys
+ + (sg_index * sizeof(*scb->ahc_dma));
+ ahc_outb(ahc, SG_NEXT + 3, sg_addr >> 24);
+ ahc_outb(ahc, SG_NEXT + 2, sg_addr >> 16);
+ ahc_outb(ahc, SG_NEXT + 1, sg_addr >> 8);
+ ahc_outb(ahc, SG_NEXT, sg_addr);
+ }
+
+ ahc_outb(ahc, SCB_RESID_DCNT + 2, data_cnt >> 16);
+ ahc_outb(ahc, SCB_RESID_DCNT + 1, data_cnt >> 8);
+ ahc_outb(ahc, SCB_RESID_DCNT, data_cnt);
+
+ ahc_outb(ahc, SHADDR + 3, data_addr >> 24);
+ ahc_outb(ahc, SHADDR + 2, data_addr >> 16);
+ ahc_outb(ahc, SHADDR + 1, data_addr >> 8);
+ ahc_outb(ahc, SHADDR, data_addr);
+ }
+ }
+}
+
+static void
ahc_handle_devreset(struct ahc_softc *ahc, int target, char channel,
cam_status status, ac_code acode, char *message,
int verbose_only)
@@ -3264,22 +3599,34 @@ ahc_find_tmode_devs(struct ahc_softc *ahc, struct cam_sim *sim, union ccb *ccb,
return (CAM_REQ_INVALID);
/* Range check target and lun */
- if (cam_sim_bus(sim) == 0)
- our_id = ahc->our_id;
- else
- our_id = ahc->our_id_b;
- if (ccb->ccb_h.target_id > ((ahc->features & AHC_WIDE) ? 15 : 7)
- || ((ahc->features & AHC_MULTI_TID) == 0
- && (ccb->ccb_h.target_id != our_id)))
- return (CAM_TID_INVALID);
- if (ccb->ccb_h.target_lun > 8)
- return (CAM_LUN_INVALID);
-
- *tstate = ahc->enabled_targets[ccb->ccb_h.target_id];
- *lstate = NULL;
- if (*tstate != NULL)
- *lstate = (*tstate)->enabled_luns[ccb->ccb_h.target_lun];
+ /*
+ * Handle the 'black hole' device that sucks up
+ * requests to unattached luns on enabled targets.
+ */
+ if (ccb->ccb_h.target_id == CAM_TARGET_WILDCARD
+ && ccb->ccb_h.target_lun == CAM_LUN_WILDCARD) {
+ *tstate = NULL;
+ *lstate = ahc->black_hole;
+ } else {
+ if (cam_sim_bus(sim) == 0)
+ our_id = ahc->our_id;
+ else
+ our_id = ahc->our_id_b;
+ if (ccb->ccb_h.target_id > ((ahc->features & AHC_WIDE) ? 15 : 7)
+ || ((ahc->features & AHC_MULTI_TID) == 0
+ && (ccb->ccb_h.target_id != our_id)))
+ return (CAM_TID_INVALID);
+
+ if (ccb->ccb_h.target_lun > 8)
+ return (CAM_LUN_INVALID);
+
+ *tstate = ahc->enabled_targets[ccb->ccb_h.target_id];
+ *lstate = NULL;
+ if (*tstate != NULL)
+ *lstate =
+ (*tstate)->enabled_luns[ccb->ccb_h.target_lun];
+ }
if (notfound_failure != 0 && *lstate == NULL)
return (CAM_PATH_INVALID);
@@ -3313,9 +3660,15 @@ ahc_action(struct cam_sim *sim, union ccb *ccb)
&lstate, TRUE);
if (status != CAM_REQ_CMP) {
- ccb->ccb_h.status = status;
- xpt_done(ccb);
- break;
+ if (ccb->ccb_h.func_code == XPT_CONT_TARGET_IO) {
+ /* Response from the black hole device */
+ tstate = NULL;
+ lstate = ahc->black_hole;
+ } else {
+ ccb->ccb_h.status = status;
+ xpt_done(ccb);
+ break;
+ }
}
if (ccb->ccb_h.func_code == XPT_ACCEPT_TARGET_IO) {
SLIST_INSERT_HEAD(&lstate->accept_tios, &ccb->ccb_h,
@@ -3442,188 +3795,9 @@ ahc_action(struct cam_sim *sim, union ccb *ccb)
break;
}
case XPT_EN_LUN: /* Enable LUN as a target */
- {
- struct tmode_tstate *tstate;
- struct tmode_lstate *lstate;
- struct ccb_en_lun *cel;
- cam_status status;
- int target;
- int lun;
-
- status = ahc_find_tmode_devs(ahc, sim, ccb, &tstate, &lstate,
- /* notfound_failure*/FALSE);
-
- if (status != CAM_REQ_CMP) {
- ccb->ccb_h.status = status;
- xpt_done(ccb);
- break;
- }
-
- cel = &ccb->cel;
- target = ccb->ccb_h.target_id;
- lun = ccb->ccb_h.target_lun;
- if (cel->enable != 0) {
- u_int scsiseq;
-
- /* Are we already enabled?? */
- if (lstate != NULL) {
- ccb->ccb_h.status = CAM_LUN_ALRDY_ENA;
- xpt_done(ccb);
- break;
- }
-
- if (cel->grp6_len != 0
- || cel->grp7_len != 0) {
- /*
- * Don't (yet?) support vendor
- * specific commands.
- */
- ccb->ccb_h.status = CAM_REQ_INVALID;
- xpt_done(ccb);
- break;
- }
-
- /*
- * Seems to be okay.
- * Setup our data structures.
- */
- if (tstate == NULL) {
- tstate = malloc(sizeof(*tstate),
- M_DEVBUF, M_NOWAIT);
- if (tstate == NULL) {
- ccb->ccb_h.status = CAM_RESRC_UNAVAIL;
- xpt_done(ccb);
- break;
- }
- bzero(tstate, sizeof(*tstate));
- ahc->enabled_targets[target] = tstate;
- }
- lstate = malloc(sizeof(*lstate), M_DEVBUF, M_NOWAIT);
- if (lstate == NULL) {
- ccb->ccb_h.status = CAM_RESRC_UNAVAIL;
- xpt_done(ccb);
- break;
- }
- bzero(lstate, sizeof(*lstate));
- SLIST_INIT(&lstate->accept_tios);
- SLIST_INIT(&lstate->immed_notifies);
- tstate->enabled_luns[lun] = lstate;
- pause_sequencer(ahc);
- if ((ahc->features & AHC_MULTI_TID) != 0) {
- u_int16_t targid_mask;
-
- targid_mask = ahc_inb(ahc, TARGID)
- | (ahc_inb(ahc, TARGID + 1) << 8);
-
- targid_mask |= (0x01 << target);
- ahc_outb(ahc, TARGID, targid_mask);
- ahc_outb(ahc, TARGID+1, (targid_mask >> 8));
- }
- /* Allow select-in operations */
- scsiseq = ahc_inb(ahc, SCSISEQ_TEMPLATE);
- scsiseq |= ENSELI;
- ahc_outb(ahc, SCSISEQ_TEMPLATE, scsiseq);
- scsiseq = ahc_inb(ahc, SCSISEQ);
- scsiseq |= ENSELI;
- ahc_outb(ahc, SCSISEQ, scsiseq);
- unpause_sequencer(ahc, /*always?*/FALSE);
- ccb->ccb_h.status = CAM_REQ_CMP;
- xpt_print_path(ccb->ccb_h.path);
- printf("Lun now enabled for target mode\n");
- xpt_done(ccb);
- break;
- } else {
- struct ccb_hdr *elm;
-
- /* XXX Fully Implement Disable */
- if (lstate == NULL) {
- ccb->ccb_h.status = CAM_LUN_INVALID;
- xpt_done(ccb);
- break;
- }
-
- ccb->ccb_h.status = CAM_REQ_CMP;
- LIST_FOREACH(elm, &ahc->pending_ccbs, sim_links.le) {
- if (elm->func_code == XPT_CONT_TARGET_IO
- && !xpt_path_comp(elm->path, ccb->ccb_h.path)){
- printf("CTIO pending\n");
- ccb->ccb_h.status = CAM_REQ_INVALID;
- break;
- }
- }
-
- if (SLIST_FIRST(&lstate->accept_tios) != NULL) {
- printf("ATIOs pending\n");
- ccb->ccb_h.status = CAM_REQ_INVALID;
- }
-
- if (SLIST_FIRST(&lstate->immed_notifies) != NULL) {
- printf("INOTs pending\n");
- ccb->ccb_h.status = CAM_REQ_INVALID;
- }
-
- if (ccb->ccb_h.status == CAM_REQ_CMP) {
- int i, empty;
-
- free(lstate, M_DEVBUF);
- tstate->enabled_luns[lun] = NULL;
-
- /* Can we clean up the target too? */
- for (empty = 1, i = 0; i < 8; i++)
- if (tstate->enabled_luns[i] != NULL) {
- empty = 0;
- break;
- }
- if (empty) {
- printf("Target Empty\n");
- free(tstate, M_DEVBUF);
- ahc->enabled_targets[target] = NULL;
- pause_sequencer(ahc);
- if (ahc->features & AHC_MULTI_TID) {
- u_int16_t targid_mask;
-
- targid_mask =
- ahc_inb(ahc, TARGID)
- | (ahc_inb(ahc, TARGID + 1)
- << 8);
-
- targid_mask &= (0x01 << target);
- ahc_outb(ahc, TARGID,
- targid_mask);
- ahc_outb(ahc, TARGID+1,
- (targid_mask >> 8));
- }
-
- for (empty = 1, i = 0; i < 16; i++)
- if (ahc->enabled_targets[i]
- != NULL) {
- empty = 0;
- break;
- }
- if (empty) {
- /* Disallow select-in */
- u_int scsiseq;
-
- printf("No targets\n");
- scsiseq =
- ahc_inb(ahc,
- SCSISEQ_TEMPLATE);
- scsiseq &= ~ENSELI;
- ahc_outb(ahc, SCSISEQ_TEMPLATE,
- scsiseq);
- scsiseq = ahc_inb(ahc, SCSISEQ);
- scsiseq &= ~ENSELI;
- ahc_outb(ahc, SCSISEQ, scsiseq);
- }
- unpause_sequencer(ahc,
- /*always?*/FALSE);
- }
- }
- xpt_done(ccb);
- break;
- }
+ ahc_handle_en_lun(ahc, sim, ccb);
+ xpt_done(ccb);
break;
- }
case XPT_ABORT: /* Abort the specified CCB */
{
ahc_abort_ccb(ahc, sim, ccb);
diff --git a/sys/dev/aic7xxx/aic7xxx.h b/sys/dev/aic7xxx/aic7xxx.h
index bfe74a0..e46d277 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.3 1998/12/10 04:14:50 gibbs Exp $
+ * $Id: aic7xxx.h,v 1.4 1998/12/15 08:22:41 gibbs Exp $
*/
#ifndef _AIC7XXX_H_
@@ -409,6 +409,12 @@ struct ahc_softc {
struct tmode_tstate* enabled_targets[16];
/*
+ * The black hole device responsible for handling requests for
+ * disabled luns on enabled targets.
+ */
+ struct tmode_lstate* black_hole;
+
+ /*
* Device instance currently on the bus awaiting a continue TIO
* for a command that was not given the disconnect priveledge.
*/
@@ -492,6 +498,8 @@ struct ahc_softc {
u_int msgout_index; /* Current index in msgout */
u_int msgin_index; /* Current index in msgin */
+ u_int enabled_luns;
+
/*
* "Bus" addresses of our data structures.
*/
diff --git a/sys/dev/aic7xxx/aic7xxx.reg b/sys/dev/aic7xxx/aic7xxx.reg
index 058bc8f..775c0e3 100644
--- a/sys/dev/aic7xxx/aic7xxx.reg
+++ b/sys/dev/aic7xxx/aic7xxx.reg
@@ -1,7 +1,7 @@
/*
* Aic7xxx register and scratch ram definitions.
*
- * Copyright (c) 1994-1998 Justin Gibbs.
+ * Copyright (c) 1994-1999 Justin Gibbs.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
@@ -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.10 1998/12/15 08:22:42 gibbs Exp $
+ * $Id: aic7xxx.reg,v 1.11 1998/12/17 00:06:52 gibbs Exp $
*/
/*
@@ -471,7 +471,7 @@ register SEQCTL {
* Sequencer RAM Data (p. 3-34)
* Single byte window into the Scratch Ram area starting at the address
* specified by SEQADDR0 and SEQADDR1. To write a full word, simply write
- * four bytes in sucessesion. The SEQADDRs will increment after the most
+ * four bytes in succession. The SEQADDRs will increment after the most
* significant byte is written
*/
register SEQRAM {
@@ -678,6 +678,7 @@ register INTSTAT {
mask ABORT_REQUESTED 0x50|SEQINT /* Reconect of aborted SCB */
mask BAD_STATUS 0x70|SEQINT /* Bad status from target */
mask RESIDUAL 0x80|SEQINT /* Residual byte count != 0 */
+ mask TRACE_POINT 0x90|SEQINT
mask HOST_MSG_LOOP 0xa0|SEQINT /*
* The bus is ready for the
* host to perform another
@@ -1330,6 +1331,21 @@ scratch_ram {
}
/*
+ * Track whether the transfer byte count for
+ * the current data phase is odd.
+ */
+ DATA_COUNT_ODD {
+ size 1
+ }
+
+ /*
+ * The initiator specified tag for this target mode transaction.
+ */
+ INITIATOR_TAG {
+ size 1
+ }
+
+ /*
* These are reserved registers in the card's scratch ram. Some of
* the values are specified in the AHA2742 technical reference manual
* and are initialized by the BIOS at boot time.
diff --git a/sys/dev/aic7xxx/aic7xxx.seq b/sys/dev/aic7xxx/aic7xxx.seq
index 504a876..3377449 100644
--- a/sys/dev/aic7xxx/aic7xxx.seq
+++ b/sys/dev/aic7xxx/aic7xxx.seq
@@ -1,7 +1,7 @@
/*
* Adaptec 274x/284x/294x device driver firmware for Linux and FreeBSD.
*
- * Copyright (c) 1994-1998 Justin Gibbs.
+ * Copyright (c) 1994-1999 Justin Gibbs.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
@@ -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.82 1998/12/15 08:22:42 gibbs Exp $
+ * $Id: aic7xxx.seq,v 1.83 1998/12/17 00:06:52 gibbs Exp $
*/
#include <dev/aic7xxx/aic7xxx.reg>
@@ -215,24 +215,16 @@ select_in:
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
- * went wrong should we need to terminate this selection
- * before doing real work. Initialize it to SCB_LIST_NULL to
- * indicate an improper initiator selection.
- */
- mvi LAST_MSG, SCB_LIST_NULL;
-
- /*
* Setup the DMA for sending the identify and
* command information.
*/
@@ -273,6 +265,9 @@ select_in:
}
}
+ /* No tag yet */
+ mvi INITIATOR_TAG, SCB_LIST_NULL;
+
/*
* If ATN isn't asserted, the target isn't interested
* in talking to us. Go directly to bus free.
@@ -291,7 +286,7 @@ select_in:
* BUS_DEVICE_RESET.
*/
/* XXX May need to be more lax here for older initiators... */
- test DINDEX, MSG_IDENTIFYFLAG jz more_first_messages;
+ test DINDEX, MSG_IDENTIFYFLAG jz host_target_message_loop;
/* Store for host */
if ((ahc->features & AHC_CMD_CHAN) != 0) {
mov CCSCBRAM, DINDEX;
@@ -329,6 +324,7 @@ select_in:
* free.
*/
test SCSISIGI, ATNI jz target_busfree;
+
/*
* Store the tag for the host.
*/
@@ -338,16 +334,12 @@ select_in:
} else {
mov DFDAT, DINDEX;
}
+ mov INITIATOR_TAG, DINDEX;
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...
+ * run it's own target mode message state engine.
*/
host_target_message_loop:
mvi INTSTAT, HOST_MSG_LOOP;
@@ -356,27 +348,14 @@ host_target_message_loop:
test SSTAT0, SPIORDY jz .;
jmp host_target_message_loop;
-more_first_messages:
- /*
- * Hmm. Now we're down to only accepting
- * either an ABORT or BDR.
- */
- cmp DINDEX, MSG_ABORT je . + 2;
- cmp DINDEX, MSG_BUS_DEV_RESET jne target_busfree;
-
- /* Record the event and notify the host */
- mov LAST_MSG, DINDEX;
- jmp target_busfree;
-
ident_messages_done:
- mvi LAST_MSG, MSG_NOOP; /* We are so far successful */
/* Terminate the ident list */
if ((ahc->features & AHC_CMD_CHAN) != 0) {
mvi CCSCBRAM, SCB_LIST_NULL;
} else {
mvi DFDAT, SCB_LIST_NULL;
}
- or SEQ_FLAGS, TARG_CMD_PENDING;
+ or SEQ_FLAGS, TARG_CMD_PENDING|IDENTIFY_SEEN;
test SCSISIGI, ATNI jnz target_mesgout_pending_msg;
jmp target_ITloop;
@@ -458,13 +437,15 @@ select_out:
*/
test SCB_CONTROL, TAG_ENB jz . + 3;
mvi MSG_SIMPLE_Q_TAG call target_outb;
- mov SCB_TAG call target_outb;
+ mov SCB_INITIATOR_TAG call target_outb;
+ mov INITIATOR_TAG, SCB_INITIATOR_TAG;
target_synccmd:
/*
* Now determine what phases the host wants us
* to go through.
*/
mov SEQ_FLAGS, SCB_TARGET_PHASES;
+
target_ITloop:
/*
@@ -507,8 +488,6 @@ target_disconnect:
target_busfree:
clr SCSISIGO;
call complete_target_cmd;
- cmp LAST_MSG, MSG_NOOP je . + 2;
- mvi INTSTAT, TARGET_MSG_HELP;
call clear_target_state;
jmp poll_for_work;
@@ -558,13 +537,13 @@ target_dphase:
* Data direction flags are from the
* perspective of the initiator.
*/
- mov ALLZEROS call initialize_channel;
test SCB_TARGET_PHASES[1], TARGET_DATA_IN jz . + 4;
mvi LASTPHASE, P_DATAOUT;
mvi P_DATAIN|BSYO call change_phase;
- jmp p_data;
+ jmp . + 3;
mvi LASTPHASE, P_DATAIN;
mvi P_DATAOUT|BSYO call change_phase;
+ mov ALLZEROS call initialize_channel;
jmp p_data;
target_sphase:
@@ -685,6 +664,7 @@ data_phase_reinit:
mvi DINDEX, STCNT;
mvi SCB_RESID_DCNT call bcopy_3;
}
+ and DATA_COUNT_ODD, 0x1, SCB_RESID_DCNT[0];
jmp data_phase_loop;
p_data:
@@ -719,6 +699,7 @@ p_data:
mvi DINDEX, HADDR;
mvi SCB_DATAPTR call bcopy_7;
}
+ and DATA_COUNT_ODD, 0x1, SCB_DATACNT[0];
if ((ahc->features & AHC_ULTRA2) == 0) {
if ((ahc->features & AHC_CMD_CHAN) != 0) {
@@ -845,6 +826,10 @@ prefetched_segs_avail:
mvi HADDR call dfdat_in_7;
}
+ /* Track odd'ness */
+ test HCNT[0], 0x1 jz . + 2;
+ xor DATA_COUNT_ODD, 0x1;
+
if ((ahc->features & AHC_ULTRA2) == 0) {
/* Load STCNT as well. It is a mirror of HCNT */
if ((ahc->features & AHC_CMD_CHAN) != 0) {
@@ -895,6 +880,12 @@ data_phase_finish:
if ((ahc->flags & AHC_TARGETMODE) != 0) {
test SEQ_FLAGS, DPHASE_PENDING jz ITloop;
and SEQ_FLAGS, ~DPHASE_PENDING;
+ /*
+ * For data-in phases, wait for any pending acks from the
+ * initiator before changing phase.
+ */
+ test DFCNTRL, DIRECTION jz target_ITloop;
+ test SSTAT1, REQINIT jnz .;
jmp target_ITloop;
}
jmp ITloop;
OpenPOWER on IntegriCloud