extractedLnx/linux/drivers/scsi/aic7xxx.c_aic7xxx_handle_seqint.c
static void
aic7xxx_handle_seqint(struct aic7xxx_host *p, unsigned char intstat)
{
struct aic7xxx_scb *scb;
unsigned short target_mask;
unsigned char target, lun, tindex;
unsigned char queue_flag = FALSE;
char channel;
target = ((aic_inb(p, SAVED_TCL) >> 4) & 0x0f);
if ( (p->chip & AHC_CHIPID_MASK) == AHC_AIC7770 )
channel = (aic_inb(p, SBLKCTL) & SELBUSB) >> 3;
else
channel = 0;
tindex = target + (channel << 3);
lun = aic_inb(p, SAVED_TCL) & 0x07;
target_mask = (0x01 << tindex);
/*
* Go ahead and clear the SEQINT now, that avoids any interrupt race
* conditions later on in case we enable some other interrupt.
*/
aic_outb(p, CLRSEQINT, CLRINT);
switch (intstat & SEQINT_MASK)
{
case NO_MATCH:
{
aic_outb(p, aic_inb(p, SCSISEQ) & (ENSELI|ENRSELI|ENAUTOATNP),
SCSISEQ);
printk(WARN_LEAD "No active SCB for reconnecting target - Issuing "
"BUS DEVICE RESET.\n", p->host_no, channel, target, lun);
printk(WARN_LEAD " SAVED_TCL=0x%x, ARG_1=0x%x, SEQADDR=0x%x\n",
p->host_no, channel, target, lun,
aic_inb(p, SAVED_TCL), aic_inb(p, ARG_1),
(aic_inb(p, SEQADDR1) << 8) | aic_inb(p, SEQADDR0));
if (aic7xxx_panic_on_abort)
aic7xxx_panic_abort(p, NULL);
}
break;
case SEND_REJECT:
{
if (aic7xxx_verbose & VERBOSE_MINOR_ERROR)
printk(INFO_LEAD "Rejecting unknown message (0x%x) received from "
"target, SEQ_FLAGS=0x%x\n", p->host_no, channel, target, lun,
aic_inb(p, ACCUM), aic_inb(p, SEQ_FLAGS));
}
break;
case NO_IDENT:
{
/*
* The reconnecting target either did not send an identify
* message, or did, but we didn't find an SCB to match and
* before it could respond to our ATN/abort, it hit a dataphase.
* The only safe thing to do is to blow it away with a bus
* reset.
*/
if (aic7xxx_verbose & (VERBOSE_SEQINT | VERBOSE_RESET_MID))
printk(INFO_LEAD "Target did not send an IDENTIFY message; "
"LASTPHASE 0x%x, SAVED_TCL 0x%x\n", p->host_no, channel, target,
lun, aic_inb(p, LASTPHASE), aic_inb(p, SAVED_TCL));
aic7xxx_reset_channel(p, channel, /*initiate reset*/ TRUE);
aic7xxx_run_done_queue(p, TRUE);
}
break;
case BAD_PHASE:
if (aic_inb(p, LASTPHASE) == P_BUSFREE)
{
if (aic7xxx_verbose & VERBOSE_SEQINT)
printk(INFO_LEAD "Missed busfree.\n", p->host_no, channel,
target, lun);
restart_sequencer(p);
}
else
{
if (aic7xxx_verbose & VERBOSE_SEQINT)
printk(INFO_LEAD "Unknown scsi bus phase, continuing\n", p->host_no,
channel, target, lun);
}
break;
case EXTENDED_MSG:
{
p->msg_type = MSG_TYPE_INITIATOR_MSGIN;
p->msg_len = 0;
p->msg_index = 0;
#ifdef AIC7XXX_VERBOSE_DEBUGGING
if (aic7xxx_verbose > 0xffff)
printk(INFO_LEAD "Enabling REQINITs for MSG_IN\n", p->host_no,
channel, target, lun);
#endif
/*
* To actually receive the message, simply turn on
* REQINIT interrupts and let our interrupt handler
* do the rest (REQINIT should already be true).
*/
p->flags |= AHC_HANDLING_REQINITS;
aic_outb(p, aic_inb(p, SIMODE1) | ENREQINIT, SIMODE1);
/*
* We don't want the sequencer unpaused yet so we return early
*/
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.
*/
unsigned char scb_index;
unsigned char last_msg;
scb_index = aic_inb(p, SCB_TAG);
scb = p->scb_data->scb_array[scb_index];
last_msg = aic_inb(p, LAST_MSG);
if ( (last_msg == MSG_IDENTIFYFLAG) &&
(scb->tag_action) &&
!(scb->flags & SCB_MSGOUT_BITS) )
{
if (scb->tag_action == MSG_ORDERED_Q_TAG)
{
/*
* OK...the device seems able to accept tagged commands, but
* not ordered tag commands, only simple tag commands. So, we
* disable ordered tag commands and go on with life just like
* normal.
*/
p->orderedtag &= ~target_mask;
scb->tag_action = MSG_SIMPLE_Q_TAG;
scb->hscb->control &= ~SCB_TAG_TYPE;
scb->hscb->control |= MSG_SIMPLE_Q_TAG;
aic_outb(p, scb->hscb->control, SCB_CONTROL);
/*
* OK..we set the tag type to simple tag command, now we re-assert
* ATNO and hope this will take us into the identify phase again
* so we can resend the tag type and info to the device.
*/
aic_outb(p, MSG_IDENTIFYFLAG, MSG_OUT);
aic_outb(p, aic_inb(p, SCSISIGI) | ATNO, SCSISIGO);
}
else if (scb->tag_action == MSG_SIMPLE_Q_TAG)
{
unsigned char i, reset = 0;
struct aic7xxx_scb *scbp;
int old_verbose;
/*
* Hmmmm....the device is flaking out on tagged commands. The
* bad thing is that we already have tagged commands enabled in
* the device struct in the mid level code. We also have a queue
* set according to the tagged queue depth. Gonna have to live
* with it by controlling our queue depth internally and making
* sure we don't set the tagged command flag any more.
*/
p->tagenable &= ~target_mask;
p->orderedtag &= ~target_mask;
p->dev_max_queue_depth[tindex] =
p->dev_temp_queue_depth[tindex] = 1;
/*
* We set this command up as a bus device reset. However, we have
* to clear the tag type as it's causing us problems. We shouldnt
* have to worry about any other commands being active, since if
* the device is refusing tagged commands, this should be the
* first tagged command sent to the device, however, we do have
* to worry about any other tagged commands that may already be
* in the qinfifo. The easiest way to do this, is to issue a BDR,
* send all the commands back to the mid level code, then let them
* come back and get rebuilt as untagged commands.
*/
scb->tag_action = 0;
scb->hscb->control &= ~(TAG_ENB | SCB_TAG_TYPE);
aic_outb(p, scb->hscb->control, SCB_CONTROL);
old_verbose = aic7xxx_verbose;
aic7xxx_verbose &= ~(VERBOSE_RESET|VERBOSE_ABORT);
for (i=0; i!=p->scb_data->numscbs; i++)
{
scbp = p->scb_data->scb_array[i];
if ((scbp->flags & SCB_ACTIVE) && (scbp != scb))
{
if (aic7xxx_match_scb(p, scbp, target, channel, lun, i))
{
aic7xxx_reset_device(p, target, channel, lun, i);
reset++;
}
aic7xxx_run_done_queue(p, TRUE);
}
}
aic7xxx_verbose = old_verbose;
/*
* Wait until after the for loop to set the busy index since
* aic7xxx_reset_device will clear the busy index during its
* operation.
*/
aic7xxx_busy_target(p, scb);
printk(INFO_LEAD "Device is refusing tagged commands, using "
"untagged I/O.\n", p->host_no, channel, target, lun);
aic_outb(p, MSG_IDENTIFYFLAG, MSG_OUT);
aic_outb(p, aic_inb(p, SCSISIGI) | ATNO, SCSISIGO);
}
}
else if (scb->flags & SCB_MSGOUT_PPR)
{
/*
* As per the draft specs, any device capable of supporting any of
* the option values other than 0 are not allowed to reject the
* PPR message. Instead, they must negotiate out what they do
* support instead of rejecting our offering.
*/
p->needppr &= ~target_mask;
p->needppr_copy &= ~target_mask;
aic7xxx_set_width(p, target, channel, lun, MSG_EXT_WDTR_BUS_8_BIT,
(AHC_TRANS_ACTIVE|AHC_TRANS_CUR|AHC_TRANS_QUITE));
aic7xxx_set_syncrate(p, NULL, target, channel, 0, 0, 0,
AHC_TRANS_ACTIVE|AHC_TRANS_CUR|AHC_TRANS_QUITE);
p->transinfo[tindex].goal_options = 0;
p->dtr_pending &= ~target_mask;
scb->flags &= ~SCB_MSGOUT_BITS;
if(aic7xxx_verbose & VERBOSE_NEGOTIATION2)
{
printk(INFO_LEAD "Device is rejecting PPR messages, falling "
"back.\n", p->host_no, channel, target, lun);
}
if ( p->transinfo[tindex].goal_width )
{
p->needwdtr |= target_mask;
p->needwdtr_copy |= target_mask;
p->dtr_pending |= target_mask;
scb->flags |= SCB_MSGOUT_WDTR;
}
if ( p->transinfo[tindex].goal_offset )
{
p->needsdtr |= target_mask;
p->needsdtr_copy |= target_mask;
if( !(p->dtr_pending & target_mask) )
{
p->dtr_pending |= target_mask;
scb->flags |= SCB_MSGOUT_SDTR;
}
}
if ( p->dtr_pending & target_mask )
{
aic_outb(p, HOST_MSG, MSG_OUT);
aic_outb(p, aic_inb(p, SCSISIGI) | ATNO, SCSISIGO);
}
}
else if (scb->flags & SCB_MSGOUT_WDTR)
{
/*
* note 8bit xfers and clear flag
*/
p->needwdtr &= ~target_mask;
p->needwdtr_copy &= ~target_mask;
p->dtr_pending &= ~target_mask;
scb->flags &= ~SCB_MSGOUT_BITS;
aic7xxx_set_width(p, target, channel, lun, MSG_EXT_WDTR_BUS_8_BIT,
(AHC_TRANS_ACTIVE|AHC_TRANS_GOAL|AHC_TRANS_CUR));
aic7xxx_set_syncrate(p, NULL, target, channel, 0, 0, 0,
AHC_TRANS_ACTIVE|AHC_TRANS_CUR|AHC_TRANS_QUITE);
if(aic7xxx_verbose & VERBOSE_NEGOTIATION2)
{
printk(INFO_LEAD "Device is rejecting WDTR messages, using "
"narrow transfers.\n", p->host_no, channel, target, lun);
}
p->needsdtr |= (p->needsdtr_copy & target_mask);
}
else if (scb->flags & SCB_MSGOUT_SDTR)
{
/*
* note asynch xfers and clear flag
*/
p->needsdtr &= ~target_mask;
p->needsdtr_copy &= ~target_mask;
p->dtr_pending &= ~target_mask;
scb->flags &= ~SCB_MSGOUT_SDTR;
aic7xxx_set_syncrate(p, NULL, target, channel, 0, 0, 0,
(AHC_TRANS_CUR|AHC_TRANS_ACTIVE|AHC_TRANS_GOAL));
if(aic7xxx_verbose & VERBOSE_NEGOTIATION2)
{
printk(INFO_LEAD "Device is rejecting SDTR messages, using "
"async transfers.\n", p->host_no, channel, target, lun);
}
}
else if (aic7xxx_verbose & VERBOSE_SEQINT)
{
/*
* Otherwise, we ignore it.
*/
printk(INFO_LEAD "Received MESSAGE_REJECT for unknown cause. "
"Ignoring.\n", p->host_no, channel, target, lun);
}
}
break;
case BAD_STATUS:
{
unsigned char scb_index;
struct aic7xxx_hwscb *hscb;
Scsi_Cmnd *cmd;
/* The sequencer will notify us when a command has an error that
* would be of interest to the kernel. This allows us to leave
* the sequencer running in the common case of command completes
* without error. The sequencer will have DMA'd the SCB back
* up to us, so we can reference the drivers SCB array.
*
* Set the default return value to 0 indicating not to send
* sense. The sense code will change this if needed and this
* reduces code duplication.
*/
aic_outb(p, 0, RETURN_1);
scb_index = aic_inb(p, SCB_TAG);
if (scb_index > p->scb_data->numscbs)
{
printk(WARN_LEAD "Invalid SCB during SEQINT 0x%02x, SCB_TAG %d.\n",
p->host_no, channel, target, lun, intstat, scb_index);
break;
}
scb = p->scb_data->scb_array[scb_index];
hscb = scb->hscb;
if (!(scb->flags & SCB_ACTIVE) || (scb->cmd == NULL))
{
printk(WARN_LEAD "Invalid SCB during SEQINT 0x%x, scb %d, flags 0x%x,"
" cmd 0x%lx.\n", p->host_no, channel, target, lun, intstat,
scb_index, scb->flags, (unsigned long) scb->cmd);
}
else
{
cmd = scb->cmd;
hscb->target_status = aic_inb(p, SCB_TARGET_STATUS);
aic7xxx_status(cmd) = hscb->target_status;
cmd->result = hscb->target_status;
switch (status_byte(hscb->target_status))
{
case GOOD:
if (aic7xxx_verbose & VERBOSE_SEQINT)
printk(INFO_LEAD "Interrupted for status of GOOD???\n",
p->host_no, CTL_OF_SCB(scb));
break;
case COMMAND_TERMINATED:
case CHECK_CONDITION:
if ( !(scb->flags & SCB_SENSE) )
{
unsigned char *sense_buffer;
/*
* XXX - How do we save the residual (if there is one).
*/
if ( hscb->residual_SG_segment_count != 0 )
aic7xxx_calculate_residual(p, scb);
/*
* Send a sense command to the requesting target.
* XXX - revisit this and get rid of the memcopys.
*/
memcpy(scb->sense_cmd, &generic_sense[0],
sizeof(generic_sense));
scb->sense_cmd[1] = (cmd->lun << 5);
scb->sense_cmd[4] = sizeof(cmd->sense_buffer);
sense_buffer = cmd->sense_buffer;
scb->sg_list[0].length =
cpu_to_le32(sizeof(cmd->sense_buffer));
/*
* XXX - We should allow disconnection, but can't as it
* might allow overlapped tagged commands.
*/
/* hscb->control &= DISCENB; */
hscb->control = 0;
hscb->target_status = 0;
hscb->SG_list_pointer =
cpu_to_le32(SCB_DMA_ADDR(scb, scb->sg_list));
hscb->data_count = scb->sg_list[0].length;
hscb->SCSI_cmd_pointer =
cpu_to_le32(SCB_DMA_ADDR(scb, scb->sense_cmd));
hscb->SCSI_cmd_length = COMMAND_SIZE(scb->sense_cmd[0]);
hscb->residual_SG_segment_count = 0;
hscb->residual_data_count[0] = 0;
hscb->residual_data_count[1] = 0;
hscb->residual_data_count[2] = 0;
scb->sg_count = hscb->SG_segment_count = 1;
scb->sg_length = sizeof(cmd->sense_buffer);
scb->tag_action = 0;
/*
* This problem could be caused if the target has lost power
* or found some other way to loose the negotiation settings,
* so if needed, we'll re-negotiate while doing the sense cmd.
* However, if this SCB already was attempting to negotiate,
* then we assume this isn't the problem and skip this part.
*/
if ( (scb->cmd->cmnd[0] != TEST_UNIT_READY) &&
(p->dev_flags[tindex] & DEVICE_SCANNED) &&
!(p->dtr_pending & target_mask) )
{
p->needppr |= (p->needppr_copy & target_mask);
p->needwdtr |= (p->needwdtr_copy & target_mask);
p->needsdtr |= (p->needsdtr_copy & target_mask);
}
else if ( scb->cmd == p->dev_dtr_cmnd[tindex] )
{
/*
* This is already a negotiation command, so we must have
* already done PPR, WDTR or SDTR. Since our negotiation
* could have gotten rejected, we don't really know the
* full state of things. Don't do anything here, and allow
* the negotiation_complete() handler to do the right
* thing.
*/
/*
* This is the important part though. We are getting sense
* info back from this device. It's going into a fake
* command. We need to put that into the real command
* instead so that the mid level SCSI code can act upon it.
* So, when we set up these fake commands, the next pointer
* is used to point to the real command. Use that to change
* the address of our sense_buffer[] to the real command.
* However, don't do this if the real command is also a
* TEST_UNIT_READY as it will most likely pull down its own
* SENSE information anyway.
*/
if (cmd->next->cmnd[0] != TEST_UNIT_READY)
sense_buffer = cmd->next->sense_buffer;
}
scb->sg_list[0].address =
cpu_to_le32(pci_map_single(p->pdev, sense_buffer,
sizeof(cmd->sense_buffer),
PCI_DMA_FROMDEVICE));
hscb->data_pointer = scb->sg_list[0].address;
scb->flags |= SCB_SENSE;
/*
* Ensure the target is busy since this will be an
* an untagged request.
*/
#ifdef AIC7XXX_VERBOSE_DEBUGGING
if (aic7xxx_verbose & VERBOSE_NEGOTIATION2)
{
if (scb->flags & SCB_MSGOUT_BITS)
printk(INFO_LEAD "Requesting SENSE with %s\n", p->host_no,
CTL_OF_SCB(scb), (scb->flags & SCB_MSGOUT_SDTR) ?
"SDTR" : "WDTR");
else
printk(INFO_LEAD "Requesting SENSE, no MSG\n", p->host_no,
CTL_OF_SCB(scb));
}
#endif
aic7xxx_busy_target(p, scb);
aic_outb(p, SEND_SENSE, RETURN_1);
aic7xxx_error(cmd) = DID_OK;
break;
} /* first time sense, no errors */
aic7xxx_error(cmd) = DID_OK;
scb->flags &= ~SCB_SENSE;
break;
case QUEUE_FULL:
queue_flag = TRUE; /* Mark that this is a QUEUE_FULL and */
case BUSY: /* drop through to here */
{
struct aic7xxx_scb *next_scbp, *prev_scbp;
unsigned char active_hscb, next_hscb, prev_hscb, scb_index;
/*
* We have to look three places for queued commands:
* 1: QINFIFO
* 2: p->waiting_scbs queue
* 3: WAITING_SCBS list on card (for commands that are started
* but haven't yet made it to the device)
*/
aic7xxx_search_qinfifo(p, target, channel, lun,
SCB_LIST_NULL, 0, TRUE,
&p->delayed_scbs[tindex]);
next_scbp = p->waiting_scbs.head;
while ( next_scbp != NULL )
{
prev_scbp = next_scbp;
next_scbp = next_scbp->q_next;
if ( aic7xxx_match_scb(p, prev_scbp, target, channel, lun,
SCB_LIST_NULL) )
{
scbq_remove(&p->waiting_scbs, prev_scbp);
scbq_insert_tail(&p->delayed_scbs[tindex],
prev_scbp);
}
}
next_scbp = NULL;
active_hscb = aic_inb(p, SCBPTR);
prev_hscb = next_hscb = scb_index = SCB_LIST_NULL;
next_hscb = aic_inb(p, WAITING_SCBH);
while (next_hscb != SCB_LIST_NULL)
{
aic_outb(p, next_hscb, SCBPTR);
scb_index = aic_inb(p, SCB_TAG);
if (scb_index < p->scb_data->numscbs)
{
next_scbp = p->scb_data->scb_array[scb_index];
if (aic7xxx_match_scb(p, next_scbp, target, channel, lun,
SCB_LIST_NULL) )
{
if (next_scbp->flags & SCB_WAITINGQ)
{
p->dev_active_cmds[tindex]++;
p->activescbs--;
scbq_remove(&p->delayed_scbs[tindex], next_scbp);
scbq_remove(&p->waiting_scbs, next_scbp);
}
scbq_insert_head(&p->delayed_scbs[tindex],
next_scbp);
next_scbp->flags |= SCB_WAITINGQ;
p->dev_active_cmds[tindex]--;
p->activescbs--;
next_hscb = aic_inb(p, SCB_NEXT);
aic_outb(p, 0, SCB_CONTROL);
aic_outb(p, SCB_LIST_NULL, SCB_TAG);
aic7xxx_add_curscb_to_free_list(p);
if (prev_hscb == SCB_LIST_NULL)
{
/* We were first on the list,
* so we kill the selection
* hardware. Let the sequencer
* re-init the hardware itself
*/
aic_outb(p, aic_inb(p, SCSISEQ) & ~ENSELO, SCSISEQ);
aic_outb(p, CLRSELTIMEO, CLRSINT1);
aic_outb(p, next_hscb, WAITING_SCBH);
}
else
{
aic_outb(p, prev_hscb, SCBPTR);
aic_outb(p, next_hscb, SCB_NEXT);
}
}
else
{
prev_hscb = next_hscb;
next_hscb = aic_inb(p, SCB_NEXT);
}
} /* scb_index >= p->scb_data->numscbs */
}
aic_outb(p, active_hscb, SCBPTR);
if (scb->flags & SCB_WAITINGQ)
{
scbq_remove(&p->delayed_scbs[tindex], scb);
scbq_remove(&p->waiting_scbs, scb);
p->dev_active_cmds[tindex]++;
p->activescbs++;
}
scbq_insert_head(&p->delayed_scbs[tindex], scb);
p->dev_active_cmds[tindex]--;
p->activescbs--;
scb->flags |= SCB_WAITINGQ | SCB_WAS_BUSY;
if ( !(p->dev_timer_active & (0x01 << tindex)) )
{
p->dev_timer_active |= (0x01 << tindex);
if ( p->dev_active_cmds[tindex] )
{
p->dev_expires[tindex] = jiffies + HZ;
}
else
{
p->dev_expires[tindex] = jiffies + (HZ / 10);
}
if ( !(p->dev_timer_active & (0x01 << MAX_TARGETS)) )
{
p->dev_timer.expires = p->dev_expires[tindex];
p->dev_timer_active |= (0x01 << MAX_TARGETS);
add_timer(&p->dev_timer);
}
else if ( time_after_eq(p->dev_timer.expires,
p->dev_expires[tindex]) )
mod_timer(&p->dev_timer, p->dev_expires[tindex]);
}
#ifdef AIC7XXX_VERBOSE_DEBUGGING
if( (aic7xxx_verbose & VERBOSE_MINOR_ERROR) ||
(aic7xxx_verbose > 0xffff) )
{
if (queue_flag)
printk(INFO_LEAD "Queue full received; queue depth %d, "
"active %d\n", p->host_no, CTL_OF_SCB(scb),
p->dev_max_queue_depth[tindex],
p->dev_active_cmds[tindex]);
else
printk(INFO_LEAD "Target busy\n", p->host_no, CTL_OF_SCB(scb));
}
#endif
if (queue_flag)
{
if ( p->dev_last_queue_full[tindex] !=
p->dev_active_cmds[tindex] )
{
p->dev_last_queue_full[tindex] =
p->dev_active_cmds[tindex];
p->dev_last_queue_full_count[tindex] = 0;
}
else
{
p->dev_last_queue_full_count[tindex]++;
}
if ( (p->dev_last_queue_full_count[tindex] > 14) &&
(p->dev_active_cmds[tindex] > 4) )
{
if (aic7xxx_verbose & VERBOSE_NEGOTIATION2)
printk(INFO_LEAD "Queue depth reduced to %d\n", p->host_no,
CTL_OF_SCB(scb), p->dev_active_cmds[tindex]);
p->dev_max_queue_depth[tindex] =
p->dev_active_cmds[tindex];
p->dev_last_queue_full[tindex] = 0;
p->dev_last_queue_full_count[tindex] = 0;
p->dev_temp_queue_depth[tindex] =
p->dev_active_cmds[tindex];
}
else if (p->dev_active_cmds[tindex] == 0)
{
if (aic7xxx_verbose & VERBOSE_NEGOTIATION)
{
printk(INFO_LEAD "QUEUE_FULL status received with 0 "
"commands active.\n", p->host_no, CTL_OF_SCB(scb));
printk(INFO_LEAD "Tagged Command Queueing disabled\n",
p->host_no, CTL_OF_SCB(scb));
}
p->dev_max_queue_depth[tindex] = 1;
p->dev_temp_queue_depth[tindex] = 1;
scb->tag_action = 0;
scb->hscb->control &= ~(MSG_ORDERED_Q_TAG|MSG_SIMPLE_Q_TAG);
}
else
{
p->dev_flags[tindex] |= DEVICE_WAS_BUSY;
p->dev_temp_queue_depth[tindex] =
p->dev_active_cmds[tindex];
}
}
break;
}
default:
if (aic7xxx_verbose & VERBOSE_SEQINT)
printk(INFO_LEAD "Unexpected target status 0x%x.\n", p->host_no,
CTL_OF_SCB(scb), scb->hscb->target_status);
if (!aic7xxx_error(cmd))
{
aic7xxx_error(cmd) = DID_RETRY_COMMAND;
}
break;
} /* end switch */
} /* end else of */
}
break;
case AWAITING_MSG:
{
unsigned char scb_index, msg_out;
scb_index = aic_inb(p, SCB_TAG);
msg_out = aic_inb(p, MSG_OUT);
scb = p->scb_data->scb_array[scb_index];
p->msg_index = p->msg_len = 0;
/*
* This SCB had a MK_MESSAGE set in its control byte informing
* the sequencer that we wanted to send a special message to
* this target.
*/
if ( !(scb->flags & SCB_DEVICE_RESET) &&
(msg_out == MSG_IDENTIFYFLAG) &&
(scb->hscb->control & TAG_ENB) )
{
p->msg_buf[p->msg_index++] = scb->tag_action;
p->msg_buf[p->msg_index++] = scb->hscb->tag;
p->msg_len += 2;
}
if (scb->flags & SCB_DEVICE_RESET)
{
p->msg_buf[p->msg_index++] = MSG_BUS_DEV_RESET;
p->msg_len++;
if (aic7xxx_verbose & VERBOSE_RESET_PROCESS)
printk(INFO_LEAD "Bus device reset mailed.\n",
p->host_no, CTL_OF_SCB(scb));
}
else if (scb->flags & SCB_ABORT)
{
if (scb->tag_action)
{
p->msg_buf[p->msg_index++] = MSG_ABORT_TAG;
}
else
{
p->msg_buf[p->msg_index++] = MSG_ABORT;
}
p->msg_len++;
if (aic7xxx_verbose & VERBOSE_ABORT_PROCESS)
printk(INFO_LEAD "Abort message mailed.\n", p->host_no,
CTL_OF_SCB(scb));
}
else if (scb->flags & SCB_MSGOUT_PPR)
{
unsigned int max_sync, period;
unsigned char options = 0;
if (p->features & AHC_ULTRA2)
{
if ( (aic_inb(p, SBLKCTL) & ENAB40) &&
!(aic_inb(p, SSTAT2) & EXP_ACTIVE) )
{
if( (p->features & AHC_ULTRA3) &&
(p->dev_flags[tindex] & DEVICE_SCSI_3) &&
(p->transinfo[tindex].goal_width ==
MSG_EXT_WDTR_BUS_16_BIT) &&
(p->transinfo[tindex].goal_options != 0) )
{
max_sync = AHC_SYNCRATE_ULTRA3;
options = p->transinfo[tindex].goal_options;
}
else
{
max_sync = AHC_SYNCRATE_ULTRA2;
}
}
else
{
max_sync = AHC_SYNCRATE_ULTRA;
}
}
else if (p->features & AHC_ULTRA)
{
max_sync = AHC_SYNCRATE_ULTRA;
}
else
{
max_sync = AHC_SYNCRATE_FAST;
}
period = p->transinfo[tindex].goal_period;
aic7xxx_find_syncrate(p, &period, max_sync, &options);
p->transinfo[tindex].goal_period = period;
p->transinfo[tindex].goal_options = options;
if (aic7xxx_verbose & VERBOSE_NEGOTIATION2)
{
printk(INFO_LEAD "Sending PPR (%d/%d/%d/%d) message.\n",
p->host_no, CTL_OF_SCB(scb), period,
p->transinfo[tindex].goal_offset,
p->transinfo[tindex].goal_width, options);
}
aic7xxx_construct_ppr(p, scb);
}
else if (scb->flags & SCB_MSGOUT_WDTR)
{
if (aic7xxx_verbose & VERBOSE_NEGOTIATION2)
{
printk(INFO_LEAD "Sending WDTR message.\n", p->host_no,
CTL_OF_SCB(scb));
}
aic7xxx_construct_wdtr(p, p->transinfo[tindex].goal_width);
}
else if (scb->flags & SCB_MSGOUT_SDTR)
{
unsigned int max_sync, period;
unsigned char options = 0;
/*
* Now that the device is selected, use the bits in SBLKCTL and
* SSTAT2 to determine the max sync rate for this device.
*/
if (p->features & AHC_ULTRA2)
{
if ( (aic_inb(p, SBLKCTL) & ENAB40) &&
!(aic_inb(p, SSTAT2) & EXP_ACTIVE) )
{
max_sync = AHC_SYNCRATE_ULTRA2;
}
else
{
max_sync = AHC_SYNCRATE_ULTRA;
}
}
else if (p->features & AHC_ULTRA)
{
max_sync = AHC_SYNCRATE_ULTRA;
}
else
{
max_sync = AHC_SYNCRATE_FAST;
}
period = p->transinfo[tindex].goal_period;
aic7xxx_find_syncrate(p, &period, max_sync, &options);
if (aic7xxx_verbose & VERBOSE_NEGOTIATION2)
{
printk(INFO_LEAD "Sending SDTR %d/%d message.\n", p->host_no,
CTL_OF_SCB(scb),
p->transinfo[tindex].goal_period,
p->transinfo[tindex].goal_offset);
}
aic7xxx_construct_sdtr(p, period,
p->transinfo[tindex].goal_offset);
}
else
{
sti();
panic("aic7xxx: AWAITING_MSG for an SCB that does "
"not have a waiting message.\n");
}
/*
* We've set everything up to send our message, now to actually do
* so we need to enable reqinit interrupts and let the interrupt
* handler do the rest. We don't want to unpause the sequencer yet
* though so we'll return early. We also have to make sure that
* we clear the SEQINT *BEFORE* we set the REQINIT handler active
* or else it's possible on VLB cards to loose the first REQINIT
* interrupt. Edge triggered EISA cards could also loose this
* interrupt, although PCI and level triggered cards should not
* have this problem since they continually interrupt the kernel
* until we take care of the situation.
*/
scb->flags |= SCB_MSGOUT_SENT;
p->msg_index = 0;
p->msg_type = MSG_TYPE_INITIATOR_MSGOUT;
p->flags |= AHC_HANDLING_REQINITS;
aic_outb(p, aic_inb(p, SIMODE1) | ENREQINIT, SIMODE1);
return;
}
break;
case DATA_OVERRUN:
{
unsigned char scb_index = aic_inb(p, SCB_TAG);
unsigned char lastphase = aic_inb(p, LASTPHASE);
unsigned int i;
scb = (p->scb_data->scb_array[scb_index]);
/*
* XXX - What do we really want to do on an overrun? The
* mid-level SCSI code should handle this, but for now,
* we'll just indicate that the command should retried.
* If we retrieved sense info on this target, then the
* base SENSE info should have been saved prior to the
* overrun error. In that case, we return DID_OK and let
* the mid level code pick up on the sense info. Otherwise
* we return DID_ERROR so the command will get retried.
*/
if ( !(scb->flags & SCB_SENSE) )
{
printk(WARN_LEAD "Data overrun detected in %s phase, tag %d;\n",
p->host_no, CTL_OF_SCB(scb),
(lastphase == P_DATAIN) ? "Data-In" : "Data-Out", scb->hscb->tag);
printk(KERN_WARNING " %s seen Data Phase. Length=%d, NumSGs=%d.\n",
(aic_inb(p, SEQ_FLAGS) & DPHASE) ? "Have" : "Haven't",
scb->sg_length, scb->sg_count);
for (i = 0; i < scb->sg_count; i++)
{
printk(KERN_WARNING " sg[%d] - Addr 0x%x : Length %d\n",
i,
le32_to_cpu(scb->sg_list[i].address),
le32_to_cpu(scb->sg_list[i].length) );
}
aic7xxx_error(scb->cmd) = DID_ERROR;
}
else
printk(INFO_LEAD "Data Overrun during SEND_SENSE operation.\n",
p->host_no, CTL_OF_SCB(scb));
}
break;
case WIDE_RESIDUE:
{
unsigned char resid_sgcnt, index;
unsigned char scb_index = aic_inb(p, SCB_TAG);
unsigned int cur_addr, resid_dcnt;
unsigned int native_addr, native_length;
int i;
if(scb_index > p->scb_data->numscbs)
{
printk(WARN_LEAD "invalid scb_index during WIDE_RESIDUE.\n",
p->host_no, -1, -1, -1);
/*
* XXX: Add error handling here
*/
break;
}
scb = p->scb_data->scb_array[scb_index];
if(!(scb->flags & SCB_ACTIVE) || (scb->cmd == NULL))
{
printk(WARN_LEAD "invalid scb during WIDE_RESIDUE flags:0x%x "
"scb->cmd:0x%lx\n", p->host_no, CTL_OF_SCB(scb),
scb->flags, (unsigned long)scb->cmd);
break;
}
/*
* We have a valid scb to use on this WIDE_RESIDUE message, so
* we need to walk the sg list looking for this particular sg
* segment, then see if we happen to be at the very beginning of
* the segment. If we are, then we have to back things up to
* the previous segment. If not, then we simply need to remove
* one byte from this segments address and add one to the byte
* count.
*/
cur_addr = aic_inb(p, SHADDR) | (aic_inb(p, SHADDR + 1) << 8) |
(aic_inb(p, SHADDR + 2) << 16) | (aic_inb(p, SHADDR + 3) << 24);
resid_sgcnt = aic_inb(p, SCB_RESID_SGCNT);
resid_dcnt = aic_inb(p, SCB_RESID_DCNT) |
(aic_inb(p, SCB_RESID_DCNT + 1) << 8) |
(aic_inb(p, SCB_RESID_DCNT + 2) << 24);
index = scb->sg_count - resid_sgcnt;
native_addr = le32_to_cpu(scb->sg_list[index].address);
native_length = le32_to_cpu(scb->sg_list[index].length);
/*
* Make sure this is a valid sg_seg for the given pointer
*/
if(cur_addr < native_addr ||
cur_addr > (native_addr + native_length))
{
printk(WARN_LEAD "invalid cur_addr:0x%x during WIDE_RESIDUE\n",
p->host_no, CTL_OF_SCB(scb), cur_addr);
if(index > 0)
printk(WARN_LEAD " sg_address[-1]:0x%x sg_length[-1]:%d\n",
p->host_no, CTL_OF_SCB(scb),
le32_to_cpu(scb->sg_list[index - 1].address),
le32_to_cpu(scb->sg_list[index - 1].length));
printk(WARN_LEAD " sg_address:0x%x sg_length:%d\n",
p->host_no, CTL_OF_SCB(scb),
native_addr, native_length);
if(resid_sgcnt > 1)
printk(WARN_LEAD " sg_address[1]:0x%x sg_length[1]:%d\n",
p->host_no, CTL_OF_SCB(scb),
le32_to_cpu(scb->sg_list[index + 1].address),
le32_to_cpu(scb->sg_list[index + 1].length));
break;
}
/*
* If our current address matches the sg_seg->address then we
* have to back up the sg array to the previous segment and set
* it up to have only one byte of transfer left to go.
*/
if(cur_addr == native_addr)
{
if(index == 0)
{
printk(WARN_LEAD "bogus WIDE_RESIDUE message, no data has been "
"transferred.\n", p->host_no, CTL_OF_SCB(scb));
break;
}
resid_sgcnt++;
index--;
cur_addr = le32_to_cpu(scb->sg_list[index].address) +
le32_to_cpu(scb->sg_list[index].length) - 1;
native_addr = aic_inb(p, SG_NEXT) | (aic_inb(p, SG_NEXT + 1) << 8)
| (aic_inb(p, SG_NEXT + 2) << 16) | (aic_inb(p, SG_NEXT + 3) << 24);
native_addr -= SG_SIZEOF;
aic_outb(p, resid_sgcnt, SG_COUNT);
aic_outb(p, resid_sgcnt, SCB_RESID_SGCNT);
aic_outb(p, native_addr & 0xff, SG_NEXT);
aic_outb(p, (native_addr >> 8) & 0xff, SG_NEXT + 1);
aic_outb(p, (native_addr >> 16) & 0xff, SG_NEXT + 2);
aic_outb(p, (native_addr >> 24) & 0xff, SG_NEXT + 3);
aic_outb(p, 1, SCB_RESID_DCNT);
aic_outb(p, 0, SCB_RESID_DCNT + 1);
aic_outb(p, 0, SCB_RESID_DCNT + 2);
aic_outb(p, 1, HCNT);
aic_outb(p, 0, HCNT + 1);
aic_outb(p, 0, HCNT + 2);
aic_outb(p, cur_addr & 0xff, HADDR);
aic_outb(p, (cur_addr >> 8) & 0xff, HADDR + 1);
aic_outb(p, (cur_addr >> 16) & 0xff, HADDR + 2);
aic_outb(p, (cur_addr >> 24) & 0xff, HADDR + 3);
/*
* The sequencer actually wants to find the new address and byte
* count in the SHCNT and SHADDR register sets. These registers
* are a shadow of the regular HCNT and HADDR registers. On the
* Ultra2 controllers, these registers are read only and the way
* we have to set their values is to put the values we want into
* the HCNT and HADDR registers and then output PRELOADEN into
* the DFCNTRL register which causes the card to latch the current
* values in the HADDR and HCNT registers and drop it through to
* the shadow registers. On older cards we copy them directly
* across by hand.
*/
if(p->features & AHC_ULTRA2)
{
aic_outb(p, aic_inb(p, DMAPARAMS), DFCNTRL);
i=0;
udelay(1);
while(((aic_inb(p, SSTAT0) & SDONE) != 0) && (i++ < 1000))
{
aic_outb(p, aic_inb(p, DMAPARAMS), DFCNTRL);
udelay(1);
}
}
else
{
aic_outb(p, 1, STCNT);
aic_outb(p, 0, STCNT + 1);
aic_outb(p, 0, STCNT + 2);
aic_outb(p, cur_addr & 0xff, SHADDR);
aic_outb(p, (cur_addr >> 8) & 0xff, SHADDR + 1);
aic_outb(p, (cur_addr >> 16) & 0xff, SHADDR + 2);
aic_outb(p, (cur_addr >> 24) & 0xff, SHADDR + 3);
}
}
else
{
/*
* Back the data pointer up by one and add one to the remaining
* byte count. Then store that in the HCNT and HADDR registers.
*/
cur_addr--;
resid_dcnt++;
aic_outb(p, resid_dcnt & 0xff, SCB_RESID_DCNT);
aic_outb(p, (resid_dcnt >> 8) & 0xff, SCB_RESID_DCNT + 1);
aic_outb(p, (resid_dcnt >> 16) & 0xff, SCB_RESID_DCNT + 2);
aic_outb(p, resid_dcnt & 0xff, HCNT);
aic_outb(p, (resid_dcnt >> 8) & 0xff, HCNT + 1);
aic_outb(p, (resid_dcnt >> 16) & 0xff, HCNT + 2);
aic_outb(p, cur_addr & 0xff, HADDR);
aic_outb(p, (cur_addr >> 8) & 0xff, HADDR + 1);
aic_outb(p, (cur_addr >> 16) & 0xff, HADDR + 2);
aic_outb(p, (cur_addr >> 24) & 0xff, HADDR + 3);
if(p->features & AHC_ULTRA2)
{
aic_outb(p, aic_inb(p, DMAPARAMS), DFCNTRL);
i=0;
udelay(1);
while(((aic_inb(p, SSTAT0) & SDONE) != 0) && (i++ < 1000))
{
aic_outb(p, aic_inb(p, DMAPARAMS), DFCNTRL);
udelay(1);
}
}
else
{
aic_outb(p, resid_dcnt & 0xff, STCNT);
aic_outb(p, (resid_dcnt >> 8) & 0xff, STCNT + 1);
aic_outb(p, (resid_dcnt >> 16) & 0xff, STCNT + 2);
aic_outb(p, cur_addr & 0xff, SHADDR);
aic_outb(p, (cur_addr >> 8) & 0xff, SHADDR + 1);
aic_outb(p, (cur_addr >> 16) & 0xff, SHADDR + 2);
aic_outb(p, (cur_addr >> 24) & 0xff, SHADDR + 3);
}
}
}
break;
#if AIC7XXX_NOT_YET
case TRACEPOINT:
{
printk(INFO_LEAD "Tracepoint #1 reached.\n", p->host_no,
channel, target, lun);
}
break;
case TRACEPOINT2:
{
printk(INFO_LEAD "Tracepoint #2 reached.\n", p->host_no,
channel, target, lun);
}
break;
/* XXX Fill these in later */
case MSG_BUFFER_BUSY:
printk("aic7xxx: Message buffer busy.\n");
break;
case MSGIN_PHASEMIS:
printk("aic7xxx: Message-in phasemis.\n");
break;
#endif
default: /* unknown */
printk(WARN_LEAD "Unknown SEQINT, INTSTAT 0x%x, SCSISIGI 0x%x.\n",
p->host_no, channel, target, lun, intstat,
aic_inb(p, SCSISIGI));
break;
}
/*
* Clear the sequencer interrupt and unpause the sequencer.
*/
unpause_sequencer(p, /* unpause always */ TRUE);
}
Generated by GNU enscript 1.6.4.