summaryrefslogtreecommitdiffstats
path: root/sys/dev/aic
diff options
context:
space:
mode:
authorluoqi <luoqi@FreeBSD.org>1999-10-21 15:49:25 +0000
committerluoqi <luoqi@FreeBSD.org>1999-10-21 15:49:25 +0000
commite2a07af8e9edccde7317330a3790e61a69e2784f (patch)
tree09dc1863b4d13f823ca4db53559878afb9793560 /sys/dev/aic
parent8c2a435f28152413be887b091d56aeaac9f2d827 (diff)
downloadFreeBSD-src-e2a07af8e9edccde7317330a3790e61a69e2784f.zip
FreeBSD-src-e2a07af8e9edccde7317330a3790e61a69e2784f.tar.gz
Non-functional changes. Add some comments before I start to forget how
everything works myself.
Diffstat (limited to 'sys/dev/aic')
-rw-r--r--sys/dev/aic/aic.c114
1 files changed, 111 insertions, 3 deletions
diff --git a/sys/dev/aic/aic.c b/sys/dev/aic/aic.c
index f99e216..db8c2b7 100644
--- a/sys/dev/aic/aic.c
+++ b/sys/dev/aic/aic.c
@@ -330,6 +330,9 @@ aic_execute_scb(void *arg, bus_dma_segment_t *dm_segs, int nseg, int error)
splx(s);
}
+/*
+ * Start another command if the controller is not busy.
+ */
static void
aic_start(struct aic_softc *aic)
{
@@ -354,6 +357,9 @@ aic_start(struct aic_softc *aic)
aic_outb(aic, SCSISEQ, ENRESELI);
}
+/*
+ * Start a selection.
+ */
static void
aic_select(struct aic_softc *aic)
{
@@ -374,6 +380,10 @@ aic_select(struct aic_softc *aic)
aic_outb(aic, SCSISEQ, ENRESELI|ENSELO|ENAUTOATNO);
}
+/*
+ * We have successfully selected a target, prepare for the information
+ * transfer phases.
+ */
static void
aic_selected(struct aic_softc *aic)
{
@@ -398,6 +408,8 @@ aic_selected(struct aic_softc *aic)
if ((ti->flags & TINFO_SDTR_NEGO) != 0)
aic->msg_outq |= AIC_MSG_SDTR;
}
+
+ /* mark target/lun busy only for untagged operations */
if ((aic->msg_outq & AIC_MSG_TAG_Q) == 0)
ti->lubusy |= 1 << scb->lun;
@@ -409,6 +421,10 @@ aic_selected(struct aic_softc *aic)
aic_outb(aic, SCSIRATE, ti->scsirate);
}
+/*
+ * We are re-selected by a target, save the target id and wait for the
+ * target to further identify itself.
+ */
static void
aic_reselected(struct aic_softc *aic)
{
@@ -417,6 +433,10 @@ aic_reselected(struct aic_softc *aic)
CAM_DEBUG_PRINT(CAM_DEBUG_TRACE, ("aic_reselected\n"));
+ /*
+ * If we have started a selection, it must have lost out in
+ * the arbitration, put the command back to the pending queue.
+ */
if (aic->nexus) {
TAILQ_INSERT_HEAD(&aic->pending_ccbs,
&aic->nexus->ccb->ccb_h, sim_links.tqe);
@@ -425,7 +445,9 @@ aic_reselected(struct aic_softc *aic)
selid = aic_inb(aic, SELID) & SCSI_ID_MASK;
if (selid & (selid - 1)) {
- ;
+ /* this should never have happened */
+ aic_reset(aic, /*initiate_reset*/TRUE);
+ return;
}
aic->state = AIC_RESELECTED;
@@ -441,6 +463,9 @@ aic_reselected(struct aic_softc *aic)
aic_outb(aic, SCSIRATE, ti->scsirate);
}
+/*
+ * Wait for SPIORDY (SCSI PIO ready) flag, or a phase change.
+ */
static __inline int
aic_spiordy(struct aic_softc *aic)
{
@@ -453,6 +478,9 @@ aic_spiordy(struct aic_softc *aic)
return (spiordy);
}
+/*
+ * Read messages.
+ */
static void
aic_msgin(struct aic_softc *aic)
{
@@ -466,6 +494,11 @@ aic_msgin(struct aic_softc *aic)
aic->flags &= ~AIC_DROP_MSGIN;
aic->msg_len = 0;
do {
+ /*
+ * If a parity error is detected, drop the remaining
+ * bytes and inform the target so it could resend
+ * the messages.
+ */
if (aic_inb(aic, SSTAT1) & SCSIPERR) {
aic_outb(aic, CLRSINT1, CLRSCSIPERR);
aic->flags |= AIC_DROP_MSGIN;
@@ -475,6 +508,7 @@ aic_msgin(struct aic_softc *aic)
aic_inb(aic, SCSIDAT);
continue;
}
+ /* read the message byte without ACKing on it */
aic->msg_buf[aic->msg_len++] = aic_inb(aic, SCSIBUS);
if (aic->msg_buf[0] == MSG_EXTENDED) {
if (aic->msg_len < 2) {
@@ -501,10 +535,15 @@ aic_msgin(struct aic_softc *aic)
msglen = 2;
else
msglen = 1;
+ /*
+ * If we have a complete message, handle it before the final
+ * ACK (in case we decide to reject the message).
+ */
if (aic->msg_len == msglen) {
aic_handle_msgin(aic);
aic->msg_len = 0;
}
+ /* ACK on the message byte */
(void) aic_inb(aic, SCSIDAT);
} while (aic_spiordy(aic));
@@ -512,6 +551,9 @@ aic_msgin(struct aic_softc *aic)
aic_outb(aic, SIMODE1, ENSCSIRST|ENBUSFREE|ENREQINIT);
}
+/*
+ * Handle a message.
+ */
static void
aic_handle_msgin(struct aic_softc *aic)
{
@@ -524,7 +566,10 @@ aic_handle_msgin(struct aic_softc *aic)
int tag;
ti = &aic->tinfo[aic->target];
-
+ /*
+ * We expect to see an IDENTIFY message, possibly followed
+ * by a tagged queue message.
+ */
if (MSG_ISIDENTIFY(aic->msg_buf[0])) {
aic->lun = aic->msg_buf[0] & MSG_IDENTIFY_LUNMASK;
if (ti->flags & TINFO_TAG_ENB)
@@ -539,6 +584,7 @@ aic_handle_msgin(struct aic_softc *aic)
return;
}
+ /* Find the nexus */
TAILQ_FOREACH(ccb_h, &aic->nexus_ccbs, sim_links.tqe) {
scb = (struct aic_scb *)ccb_h->ccb_scb_ptr;
if (ccb_h->target_id == aic->target &&
@@ -547,11 +593,13 @@ aic_handle_msgin(struct aic_softc *aic)
break;
}
+ /* ABORT if nothing is found */
if (!ccb_h) {
aic_sched_msgout(aic, MSG_ABORT); /* MSG_ABORT_TAG?*/
return;
}
+ /* Reestablish the nexus */
TAILQ_REMOVE(&aic->nexus_ccbs, ccb_h, sim_links.tqe);
aic->nexus = scb;
scb->flags &= ~SCB_DISCONNECTED;
@@ -567,12 +615,13 @@ aic_handle_msgin(struct aic_softc *aic)
csio = &scb->ccb->csio;
ccb_h->status &= ~CAM_STATUS_MASK;
if ((scb->flags & SCB_SENSE) != 0) {
+ /* auto REQUEST SENSE command */
scb->flags &= ~SCB_SENSE;
csio->sense_resid = scb->data_len;
if (scb->status == SCSI_STATUS_OK) {
ccb_h->status |=
CAM_SCSI_STATUS_ERROR|CAM_AUTOSNS_VALID;
- scsi_sense_print(csio);
+ /*scsi_sense_print(csio);*/
} else {
ccb_h->status |= CAM_AUTOSENSE_FAIL;
printf("ccb %p sense failed %x\n",
@@ -582,10 +631,12 @@ aic_handle_msgin(struct aic_softc *aic)
csio->scsi_status = scb->status;
csio->resid = scb->data_len;
if (scb->status == SCSI_STATUS_OK) {
+ /* everything goes well */
ccb_h->status |= CAM_REQ_CMP;
} else if ((ccb_h->flags & CAM_DIS_AUTOSENSE) == 0 &&
(csio->scsi_status == SCSI_STATUS_CHECK_COND ||
csio->scsi_status == SCSI_STATUS_CMD_TERMINATED)) {
+ /* try to retrieve sense information */
scb->flags |= SCB_SENSE;
aic->flags |= AIC_BUSFREE_OK;
return;
@@ -609,6 +660,10 @@ aic_handle_msgin(struct aic_softc *aic)
max(ti->goal.period, aic->msg_buf[3]);
ti->current.offset = aic->msg_buf[4] =
min(ti->goal.offset, aic->msg_buf[4]);
+ /*
+ * The target initiated the negotiation,
+ * send back a response.
+ */
aic_sched_msgout(aic, 0);
}
ti->flags &= ~(TINFO_SDTR_SENT|TINFO_SDTR_NEGO);
@@ -643,6 +698,7 @@ aic_handle_msgin(struct aic_softc *aic)
scb = aic->nexus;
ti = &aic->tinfo[scb->target];
ti->flags &= ~TINFO_TAG_ENB;
+ ti->lubusy |= 1 << scb->lun;
break;
case AIC_MSG_SDTR:
scb = aic->nexus;
@@ -676,6 +732,9 @@ aic_handle_msgin(struct aic_softc *aic)
}
}
+/*
+ * Raise ATNO to signal the target that we have a message for it.
+ */
static void
aic_sched_msgout(struct aic_softc *aic, u_int8_t msg)
{
@@ -687,6 +746,9 @@ aic_sched_msgout(struct aic_softc *aic, u_int8_t msg)
aic_outb(aic, SCSISIGO, aic_inb(aic, SCSISIGI) | ATNO);
}
+/*
+ * Send messages.
+ */
static void
aic_msgout(struct aic_softc *aic)
{
@@ -700,12 +762,19 @@ aic_msgout(struct aic_softc *aic)
aic_outb(aic, SIMODE1, ENSCSIRST|ENPHASEMIS|ENBUSFREE);
aic_outb(aic, SXFRCTL0, CHEN|SPIOEN);
+ /*
+ * If the previous phase is also the message out phase,
+ * we need to retransmit all the messages, probably
+ * because the target has detected a parity error during
+ * the past transmission.
+ */
if (aic->prev_phase == PH_MSGOUT)
aic->msg_outq = aic->msg_sent;
do {
int q = aic->msg_outq;
if (msgidx > 0 && msgidx == aic->msg_len) {
+ /* complete message sent, start the next one */
q &= -q;
aic->msg_sent |= q;
aic->msg_outq ^= q;
@@ -713,6 +782,7 @@ aic_msgout(struct aic_softc *aic)
msgidx = 0;
}
if (msgidx == 0) {
+ /* setup the message */
switch (q & -q) {
case AIC_MSG_IDENTIFY:
scb = aic->nexus;
@@ -742,6 +812,7 @@ aic_msgout(struct aic_softc *aic)
ti->flags |= TINFO_SDTR_SENT;
break;
case AIC_MSG_MSGBUF:
+ /* a single message already in the buffer */
if (aic->msg_buf[0] == MSG_BUS_DEV_RESET ||
aic->msg_buf[0] == MSG_ABORT ||
aic->msg_buf[0] == MSG_ABORT_TAG)
@@ -749,8 +820,13 @@ aic_msgout(struct aic_softc *aic)
break;
}
}
+ /*
+ * If this is the last message byte of all messages,
+ * clear ATNO to signal transmission complete.
+ */
if ((q & (q - 1)) == 0 && msgidx == aic->msg_len - 1)
aic_outb(aic, CLRSINT1, CLRATNO);
+ /* transmit the message byte */
aic_outb(aic, SCSIDAT, aic->msg_buf[msgidx++]);
} while (aic_spiordy(aic));
@@ -758,6 +834,9 @@ aic_msgout(struct aic_softc *aic)
aic_outb(aic, SIMODE1, ENSCSIRST|ENBUSFREE|ENREQINIT);
}
+/*
+ * Read data bytes.
+ */
static void
aic_datain(struct aic_softc *aic)
{
@@ -772,6 +851,7 @@ aic_datain(struct aic_softc *aic)
while (scb->data_len > 0) {
for (;;) {
+ /* wait for the fifo the fill up or a phase change */
dmastat = aic_inb(aic, DMASTAT);
if (dmastat & (INTSTAT|DFIFOFULL))
break;
@@ -779,6 +859,10 @@ aic_datain(struct aic_softc *aic)
if (dmastat & DFIFOFULL) {
n = FIFOSIZE;
} else {
+ /*
+ * No more data, wait for the remaining bytes in
+ * the scsi fifo to be transfer to the host fifo.
+ */
while (!(aic_inb(aic, SSTAT2) & SEMPTY))
;
n = aic_inb(aic, FIFOSTAT);
@@ -813,6 +897,9 @@ aic_datain(struct aic_softc *aic)
aic_outb(aic, SIMODE1, ENSCSIRST|ENBUSFREE|ENREQINIT);
}
+/*
+ * Send data bytes.
+ */
static void
aic_dataout(struct aic_softc *aic)
{
@@ -827,6 +914,7 @@ aic_dataout(struct aic_softc *aic)
while (scb->data_len > 0) {
for (;;) {
+ /* wait for the fifo to clear up or a phase change */
dmastat = aic_inb(aic, DMASTAT);
if (dmastat & (INTSTAT|DFIFOEMP))
break;
@@ -857,6 +945,7 @@ aic_dataout(struct aic_softc *aic)
}
for (;;) {
+ /* wait until all bytes in the fifos are transmitted */
dmastat = aic_inb(aic, DMASTAT);
if (dmastat & INTSTAT)
break;
@@ -868,6 +957,9 @@ aic_dataout(struct aic_softc *aic)
aic_outb(aic, SIMODE1, ENSCSIRST|ENBUSFREE|ENREQINIT);
}
+/*
+ * Send the scsi command.
+ */
static void
aic_cmd(struct aic_softc *aic)
{
@@ -875,6 +967,7 @@ aic_cmd(struct aic_softc *aic)
struct scsi_request_sense sense_cmd;
if (scb->flags & SCB_SENSE) {
+ /* autosense request */
sense_cmd.opcode = REQUEST_SENSE;
sense_cmd.byte2 = scb->lun << 2;
sense_cmd.length = scb->ccb->csio.sense_len;
@@ -897,6 +990,10 @@ aic_cmd(struct aic_softc *aic)
aic_outb(aic, SXFRCTL0, CHEN);
}
+/*
+ * Finish off a command. The caller is responsible to remove the ccb
+ * from any queue.
+ */
static void
aic_done(struct aic_softc *aic, struct aic_scb *scb)
{
@@ -1052,6 +1149,7 @@ aic_intr(void *arg)
sstat1 = aic_inb(aic, SSTAT1);
if ((sstat1 & SCSIRSTI) != 0) {
+ /* a device-initiated bus reset */
aic_outb(aic, CLRSINT1, CLRSCSIRSTI);
aic_reset(aic, /*initiate_reset*/FALSE);
return;
@@ -1142,6 +1240,7 @@ aic_intr(void *arg)
ccb->ccb_h.status = CAM_UNEXP_BUSFREE;
aic_done(aic, scb);
} else if (scb->flags & SCB_SENSE) {
+ /* autosense request */
aic->flags &= ~AIC_BUSFREE_OK;
aic_select(aic);
aic_outb(aic, DMACNTRL0, INTEN);
@@ -1160,6 +1259,9 @@ aic_intr(void *arg)
aic_outb(aic, DMACNTRL0, INTEN);
}
+/*
+ * Reset ourselves.
+ */
static void
aic_chip_reset(struct aic_softc *aic)
{
@@ -1200,6 +1302,9 @@ aic_chip_reset(struct aic_softc *aic)
aic_outb(aic, BRSTCNTRL, EISA_BRST_TIM);
}
+/*
+ * Reset the SCSI bus
+ */
static void
aic_scsi_reset(struct aic_softc *aic)
{
@@ -1209,6 +1314,9 @@ aic_scsi_reset(struct aic_softc *aic)
DELAY(50);
}
+/*
+ * Reset. Abort all pending commands.
+ */
static void
aic_reset(struct aic_softc *aic, int initiate_reset)
{
OpenPOWER on IntegriCloud