extractedLnx/linux/drivers/scsi/aic7xxx.c_aic7xxx_isr.c
static void
aic7xxx_isr(int irq, void *dev_id, struct pt_regs *regs)
{
int base, intstat, actual, scb_index, run_aborted_queue = FALSE;
struct aic7xxx_host *p;
struct aic7xxx_scb *scb = NULL;
short transfer;
unsigned char ha_flags, scsi_id, bus_width;
unsigned char offset, rate, scratch, scratch_offset;
unsigned char max_offset, rej_byte;
unsigned short target_mask;
char channel;
unsigned int addr; /* must be 32 bits */
Scsi_Cmnd *cmd;
p = (struct aic7xxx_host *) aic7xxx_boards[irq]->hostdata;
/*
* Search for the host with a pending interrupt. If we can't find
* one, then we've encountered a spurious interrupt.
*/
while ((p != NULL) && !(inb(INTSTAT + p->base) & INT_PEND))
{
if (p->next == NULL)
{
p = NULL;
}
else
{
p = (struct aic7xxx_host *) p->next->hostdata;
}
}
if (p == NULL)
return;
/*
* Keep track of interrupts for /proc/scsi
*/
p->isr_count++;
if (!(p->flags & A_SCANNED) && (p->isr_count == 1))
{
/*
* We must only have one card at this IRQ and it must have been
* added to the board data before the spurious interrupt occurred.
* It is sufficient that we check isr_count and not the spurious
* interrupt count.
*/
printk("aic7xxx: (aic7xxx_isr) Encountered spurious interrupt.\n");
return;
}
base = p->base;
/*
* Handle all the interrupt sources - especially for SCSI
* interrupts, we won't get a second chance at them.
*/
intstat = inb(INTSTAT + base);
/*
* Indicate that we're in the interrupt handler.
*/
p->flags |= IN_ISR;
if (intstat & BRKADRINT)
{
int i;
unsigned char errno = inb(ERROR + base);
printk(KERN_ERR "scsi%d: BRKADRINT error(0x%x):\n", p->host_no, errno);
for (i = 0; i < NUMBER(hard_error); i++)
{
if (errno & hard_error[i].errno)
{
printk(KERN_ERR " %s\n", hard_error[i].errmesg);
}
}
panic("scsi%d: BRKADRINT, error 0x%x, seqaddr 0x%x.\n", p->host_no,
inb(ERROR + base), (inb(SEQADDR1 + base) << 8) | inb(SEQADDR0 + base));
}
if (intstat & SEQINT)
{
/*
* Although the sequencer is paused immediately on
* a SEQINT, an interrupt for a SCSIINT condition will
* unpaused the sequencer before this point.
*/
PAUSE_SEQUENCER(p);
scsi_id = (inb(SCSIID + base) >> 4) & 0x0F;
scratch_offset = scsi_id;
channel = 'A';
if (inb(SBLKCTL + base) & SELBUSB)
{
channel = 'B';
scratch_offset += 8;
}
target_mask = (0x01 << scratch_offset);
switch (intstat & SEQINT_MASK)
{
case NO_MATCH:
if (p->flags & PAGE_ENABLED)
{
/* SCB Page-in request */
struct aic7xxx_scb *outscb;
u_char arg_1 = inb(ARG_1 + base);
int use_disconnected = FALSE;
/*
* The sequencer expects this value upon return. Assume
* we will find the paged out SCB and set the value now.
* If we don't, and one of the methods used to acquire an
* SCB calls aic7xxx_done(), we will end up in our queue
* routine and unpause the sequencer without giving it the
* correct return value, which causes a hang.
*/
outb(SCB_PAGEDIN, RETURN_1 + base);
if (arg_1 == SCB_LIST_NULL)
{
/* Non-tagged command */
int index = scsi_id;
if (channel == 'B')
{
index |= SELBUSB;
}
scb = p->pagedout_ntscbs[index];
}
else
scb = (p->scb_array[arg_1]);
if (!(scb->state & SCB_PAGED_OUT))
{
printk(KERN_WARNING "scsi%d: No active paged-out SCB for reconnecting "
"target %d, channel %c - Issuing ABORT. SAVED_TCL(0x%x).\n",
p->host_no, scsi_id, channel, inb(SAVED_TCL + base));
aic7xxx_unbusy_target(scsi_id, channel, base);
outb(CLRSELTIMEO, CLRSINT1 + base);
outb(0, RETURN_1 + base);
break;
}
/*
* Now to pick the SCB to page out. Either take a free SCB, an
* assigned SCB, an SCB that just completed, or the first one
* on the disconnected SCB list.
*/
if (p->scb_link->free_scbs.head != NULL)
{
outscb = p->scb_link->free_scbs.head;
scbq_remove_head(&p->scb_link->free_scbs);
scb->position = outscb->position;
outscb->position = SCB_LIST_NULL;
scbq_insert_head(&p->page_scbs, outscb);
outb(scb->position, SCBPTR + base);
aic7xxx_putscb(p, scb);
scb->state &= ~SCB_PAGED_OUT;
}
else if (p->assigned_scbs.head != NULL)
{
outscb = p->assigned_scbs.head;
scbq_remove_head(&p->assigned_scbs);
scb->position = outscb->position;
outscb->position = SCB_LIST_NULL;
scbq_insert_head(&p->waiting_scbs, outscb);
outscb->state = (outscb->state & ~SCB_ASSIGNEDQ) | SCB_WAITINGQ;
outb(scb->position, SCBPTR + base);
aic7xxx_putscb(p, scb);
scb->state &= ~SCB_PAGED_OUT;
}
else if (intstat & CMDCMPLT)
{
int scb_index;
outb(CLRCMDINT, CLRINT + base);
scb_index = inb(QOUTFIFO + base);
if (!(inb(QOUTCNT + base) & p->qcntmask))
{
intstat &= ~CMDCMPLT;
}
outscb = (p->scb_array[scb_index]);
if (!(outscb->state & SCB_ACTIVE))
{
printk(KERN_WARNING "scsi%d: No command for completed SCB %d "
"during NO_MATCH interrupt\n", scb_index, p->host_no);
use_disconnected = TRUE;
}
else
{
scb->position = outscb->position;
outscb->position = SCB_LIST_NULL;
outb(scb->position, SCBPTR + base);
aic7xxx_putscb(p, scb);
scb->state &= ~SCB_PAGED_OUT;
outscb->cmd->result |= (aic7xxx_error(outscb->cmd) << 16);
if ((outscb->cmd->flags & WAS_SENSE) &&
!(outscb->cmd->flags & ASKED_FOR_SENSE))
{
/*
* Got sense information.
*/
outscb->cmd->flags &= ASKED_FOR_SENSE;
}
p->device_status[TARGET_INDEX(outscb->cmd)].flags
|= DEVICE_SUCCESS;
aic7xxx_done(p, outscb);
}
}
else
{
use_disconnected = TRUE;
}
if (use_disconnected)
{
u_char tag;
u_char next;
u_char disc_scb = inb(DISCONNECTED_SCBH + base);
if (disc_scb != SCB_LIST_NULL)
{
outb(disc_scb, SCBPTR + base);
tag = inb(SCB_TAG + base);
outscb = (p->scb_array[tag]);
next = inb(SCB_NEXT + base);
if (next != SCB_LIST_NULL)
{
outb(next, SCBPTR + base);
outb(SCB_LIST_NULL, SCB_PREV + base);
outb(disc_scb, SCBPTR + base);
}
outb(next, DISCONNECTED_SCBH + base);
aic7xxx_page_scb(p, outscb, scb);
}
else if (inb(QINCNT + base) & p->qcntmask)
{
/* Pull one of our queued commands as a last resort. */
disc_scb = inb(QINFIFO + base);
outb(disc_scb, SCBPTR + base);
tag = inb(SCB_TAG + base);
outscb = (p->scb_array[tag]);
if ((outscb->control & 0x23) != TAG_ENB)
{
/*
* This is not a simple tagged command so its position
* in the queue matters. Take the command at the end of
* the queue instead.
*/
int i;
int saved_queue[AIC7XXX_MAXSCB];
int queued = inb(QINCNT + base) & p->qcntmask;
/* Count the command we removed already */
saved_queue[0] = disc_scb;
queued++;
/* Empty the input queue. */
for (i = 1; i < queued; i++)
{
saved_queue[i] = inb(QINFIFO + base);
}
/* Put everyone back but the last entry. */
queued--;
for (i = 0; i < queued; i++)
{
outb(saved_queue[i], QINFIFO + base);
}
outb(saved_queue[queued], SCBPTR + base);
tag = inb(SCB_TAG + base);
outscb = (p->scb_array[tag]);
}
scb->position = outscb->position;
outscb->position = SCB_LIST_NULL;
scbq_insert_head(&p->waiting_scbs, outscb);
outscb->state |= SCB_WAITINGQ;
aic7xxx_putscb(p, scb);
scb->state &= ~SCB_PAGED_OUT;
}
else
{
printk(KERN_WARNING "scsi%d: Page-in request with no candidates "
"target %d, channel %c - Issuing ABORT. SAVED_TCL(0x%x).\n",
p->host_no, scsi_id, channel, inb(SAVED_TCL + base));
aic7xxx_unbusy_target(scsi_id, channel, base);
outb(CLRSELTIMEO, CLRSINT1 + base);
outb(0, RETURN_1 + base);
}
}
}
else
{
printk(KERN_WARNING "scsi%d: No active SCB for reconnecting "
"target %d, channel %c - Issuing ABORT. SAVED_TCL(0x%x).\n",
p->host_no, scsi_id, channel, inb(SAVED_TCL + base));
aic7xxx_unbusy_target(scsi_id, channel, base);
outb(0, SCB_CONTROL + base);
outb(CLRSELTIMEO, CLRSINT1 + base);
outb(0, RETURN_1 + base);
}
break;
case BAD_PHASE:
panic("scsi%d: Unknown scsi bus phase.\n", p->host_no);
break;
case SEND_REJECT:
rej_byte = inb(REJBYTE + base);
if ((rej_byte & 0xF0) == 0x20)
{
scb_index = inb(SCB_TAG + base);
scb = (p->scb_array[scb_index]);
printk(KERN_WARNING "scsi%d: Tagged message received without identify."
"Disabling tagged commands for target %d channel %c.\n",
p->host_no, scsi_id, channel);
scb->cmd->device->tagged_supported = 0;
scb->cmd->device->tagged_queue = 0;
}
else
{
printk(KERN_WARNING "scsi%d: Rejecting unknown message (0x%x) received "
"from target %d channel %c.\n",
p->host_no, rej_byte, scsi_id, channel);
}
break;
case NO_IDENT:
panic("scsi%d: Target %d, channel %c, did not send an IDENTIFY "
"message. SAVED_TCL 0x%x.\n",
p->host_no, scsi_id, channel, inb(SAVED_TCL + base));
break;
case SDTR_MSG:
/*
* Help the sequencer to translate the negotiated
* transfer rate. Transfer is 1/4 the period
* in ns as is returned by the sync negotiation
* message. So, we must multiply by four.
*/
transfer = (inb(ARG_1 + base) << 2);
offset = inb(ACCUM + base);
scratch = inb(TARG_SCRATCH + base + scratch_offset);
/*
* The maximum offset for a wide device is 0x08; for a
* 8-bit bus device the maximum offset is 0x0F.
*/
if (scratch & WIDEXFER)
{
max_offset = 0x08;
}
else
{
max_offset = 0x0F;
}
aic7xxx_scsirate(p, &rate, transfer, MIN(offset, max_offset),
scsi_id, channel);
/*
* Preserve the wide transfer flag.
*/
scratch = rate | (scratch & WIDEXFER);
outb(scratch, TARG_SCRATCH + base + scratch_offset);
outb(scratch, SCSIRATE + base);
if ((scratch & 0x0F) == 0)
{
/*
* One of two things happened. Either the device requested
* asynchronous data transfers, or it requested a synchronous
* data transfer rate that was so low that asynchronous
* transfers are faster (not to mention the controller won't
* support them). In both cases the synchronous data transfer
* rate and the offset are set to 0 indicating asynchronous
* transfers.
*
* If the device requested an asynchronous transfer, then
* accept the request. If the device is being forced to
* asynchronous data transfers and this is the first time
* we've seen the request, accept the request. If we've
* already seen the request, then attempt to force
* asynchronous data transfers by rejecting the message.
*/
if ((offset == 0) || (p->sdtr_pending & target_mask))
{
/*
* Device requested asynchronous transfers or we're
* forcing asynchronous transfers for the first time.
*/
outb(0, RETURN_1 + base);
}
else
{
/*
* The first time in forcing asynchronous transfers
* failed, so we try sending a reject message.
*/
outb(SEND_REJ, RETURN_1 + base);
}
}
else
{
/*
* See if we initiated Sync Negotiation
*/
if (p->sdtr_pending & target_mask)
{
/*
* Don't send an SDTR back to the target.
*/
outb(0, RETURN_1 + base);
}
else
{
/*
* Send our own SDTR in reply.
*/
printk("aic7xxx: Sending SDTR!!\n");
outb(SEND_SDTR, RETURN_1 + base);
}
}
/*
* Clear the flags.
*/
p->needsdtr &= ~target_mask;
p->sdtr_pending &= ~target_mask;
break;
case WDTR_MSG:
{
bus_width = inb(ARG_1 + base);
printk(KERN_INFO "scsi%d: Received MSG_WDTR, Target %d, channel %c "
"needwdtr(0x%x).\n", p->host_no, scsi_id, channel, p->needwdtr);
scratch = inb(TARG_SCRATCH + base + scratch_offset);
if (p->wdtr_pending & target_mask)
{
/*
* Don't send an WDTR back to the target, since we asked first.
*/
outb(0, RETURN_1 + base);
switch (bus_width)
{
case BUS_8_BIT:
scratch &= 0x7F;
break;
case BUS_16_BIT:
printk(KERN_INFO "scsi%d: Target %d, channel %c, using 16 bit "
"transfers.\n", p->host_no, scsi_id, channel);
scratch |= 0x80;
break;
case BUS_32_BIT:
outb(SEND_REJ, RETURN_1 + base);
printk(KERN_INFO "scsi%d: Target %d, channel %c, requesting 32 bit "
"transfers, rejecting...\n", p->host_no, scsi_id, channel);
break;
}
}
else
{
/*
* Send our own WDTR in reply.
*/
printk(KERN_INFO "scsi%d: Will send WDTR!!\n", p->host_no);
switch (bus_width)
{
case BUS_8_BIT:
scratch &= 0x7F;
break;
case BUS_32_BIT:
/*
* Negotiate 16 bits.
*/
bus_width = BUS_16_BIT;
/* Yes, we mean to fall thru here. */
case BUS_16_BIT:
printk(KERN_INFO "scsi%d: Target %d, channel %c, using 16 bit "
"transfers.\n", p->host_no, scsi_id, channel);
scratch |= 0x80;
break;
}
outb(bus_width | SEND_WDTR, RETURN_1 + base);
}
p->needwdtr &= ~target_mask;
p->wdtr_pending &= ~target_mask;
outb(scratch, TARG_SCRATCH + base + scratch_offset);
outb(scratch, SCSIRATE + base);
break;
}
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.
*/
scratch = inb(TARG_SCRATCH + base + scratch_offset);
if (p->wdtr_pending & target_mask)
{
/*
* note 8bit xfers and clear flag
*/
scratch &= 0x7F;
p->needwdtr &= ~target_mask;
p->wdtr_pending &= ~target_mask;
printk(KERN_WARNING "scsi%d: Target %d, channel %c, refusing WIDE "
"negotiation; using 8 bit transfers.\n",
p->host_no, scsi_id, channel);
}
else
{
if (p->sdtr_pending & target_mask)
{
/*
* note asynch xfers and clear flag
*/
scratch &= 0xF0;
p->needsdtr &= ~target_mask;
p->sdtr_pending &= ~target_mask;
printk(KERN_WARNING "scsi%d: Target %d, channel %c, refusing "
"synchronous negotiation; using asynchronous transfers.\n",
p->host_no, scsi_id, channel);
}
/*
* Otherwise, we ignore it.
*/
}
outb(scratch, TARG_SCRATCH + base + scratch_offset);
outb(scratch, SCSIRATE + base);
break;
}
case BAD_STATUS:
/* 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 sequencerrunning in the common case of command completes
* without error.
*/
scb_index = inb(SCB_TAG + base);
scb = (p->scb_array[scb_index]);
outb(0, RETURN_1 + base); /* CHECK_CONDITION may change this */
if (!(scb->state & SCB_ACTIVE) || (scb->cmd == NULL))
{
printk(KERN_WARNING "scsi%d: Referenced SCB not valid during "
"SEQINT 0x%x, scb %d, state 0x%x, cmd 0x%lx.\n", p->host_no,
intstat, scb_index, scb->state, (unsigned long) scb->cmd);
}
else
{
cmd = scb->cmd;
scb->target_status = inb(SCB_TARGET_STATUS + base);
aic7xxx_status(cmd) = scb->target_status;
cmd->result |= scb->target_status;
switch (status_byte(scb->target_status))
{
case GOOD:
printk(KERN_WARNING "aic7xxx: Interrupted for status of GOOD???\n");
break;
case CHECK_CONDITION:
if ((aic7xxx_error(cmd) == 0) && !(cmd->flags & WAS_SENSE))
{
unsigned char tcl;
unsigned int req_buf; /* must be 32 bits */
tcl = scb->target_channel_lun;
/*
* Send a sense command to the requesting target.
*/
cmd->flags |= WAS_SENSE;
memcpy((void *) scb->sense_cmd, (void *) generic_sense,
sizeof(generic_sense));
scb->sense_cmd[1] = (cmd->lun << 5);
scb->sense_cmd[4] = sizeof(cmd->sense_buffer);
scb->sg_list[0].address = VIRT_TO_BUS(&cmd->sense_buffer);
scb->sg_list[0].length = sizeof(cmd->sense_buffer);
req_buf = VIRT_TO_BUS(&scb->sg_list[0]);
cmd->cmd_len = COMMAND_SIZE(cmd->cmnd[0]);
scb->control = scb->control & DISCENB;
scb->target_channel_lun = tcl;
addr = VIRT_TO_BUS(scb->sense_cmd);
scb->SCSI_cmd_length = COMMAND_SIZE(scb->sense_cmd[0]);
memcpy(scb->SCSI_cmd_pointer, &addr,
sizeof(scb->SCSI_cmd_pointer));
scb->SG_segment_count = 1;
memcpy(scb->SG_list_pointer, &req_buf,
sizeof(scb->SG_list_pointer));
scb->data_count = scb->sg_list[0].length;
memcpy(scb->data_pointer, &(scb->sg_list[0].address),
sizeof(scb->data_pointer));
aic7xxx_putscb(p, scb);
/*
* Ensure that the target is "BUSY" so we don't get overlapping
* commands if we happen to be doing tagged I/O.
*/
aic7xxx_busy_target(scsi_id, channel, base);
aic7xxx_add_waiting_scb(base, scb);
outb(SEND_SENSE, RETURN_1 + base);
} /* first time sense, no errors */
else
{
cmd->flags &= ~ASKED_FOR_SENSE;
if (aic7xxx_error(cmd) == 0)
{
aic7xxx_error(cmd) = DID_RETRY_COMMAND;
}
}
break;
case BUSY:
printk(KERN_WARNING "scsi%d: Target busy, TCL=0x%x.\n",
p->host_no, scb->target_channel_lun);
if (!aic7xxx_error(cmd))
{
/* The error code here used to be DID_BUS_BUSY,
* but after extensive testing, it has been determined
* that a DID_BUS_BUSY return is a waste of time. If
* the problem is something that will go away, then it
* will, if it isn't, then you don't want the endless
* looping that you get with a DID_BUS_BUSY. Better
* to be on the safe side and specify an error condition
* that will eventually lead to a reset or abort of some
* sort instead of an endless loop.
*/
aic7xxx_error(cmd) = DID_RETRY_COMMAND;
}
break;
case QUEUE_FULL:
printk(KERN_WARNING "scsi%d: Queue full.\n", p->host_no);
scb->state |= SCB_ASSIGNEDQ;
scbq_insert_tail(&p->assigned_scbs, scb);
break;
default:
printk(KERN_WARNING "scsi%d: Unexpected target status 0x%x.\n",
p->host_no, scb->target_status);
if (!aic7xxx_error(cmd))
{
aic7xxx_error(cmd) = DID_RETRY_COMMAND;
}
break;
} /* end switch */
} /* end else of */
break;
case RESIDUAL:
scb_index = inb(SCB_TAG + base);
scb = (p->scb_array[scb_index]);
if (!(scb->state & SCB_ACTIVE) || (scb->cmd == NULL))
{
printk(KERN_WARNING "scsi%d: Referenced SCB not valid during "
"SEQINT 0x%x, scb %d, state 0x%x, cmd 0x%lx.\n", p->host_no,
intstat, scb_index, scb->state, (unsigned long) scb->cmd);
}
else
{
cmd = scb->cmd;
/*
* Don't destroy valid residual information with
* residual coming from a check sense operation.
*/
if (!(cmd->flags & WAS_SENSE))
{
/*
* We had an underflow. At this time, there's only
* one other driver that bothers to check for this,
* and cmd->underflow seems to be set rather half-
* heartedly in the higher-level SCSI code.
*/
actual = aic7xxx_length(cmd, scb->residual_SG_segment_count);
actual -= (inb(SCB_RESID_DCNT2 + base) << 16) |
(inb(SCB_RESID_DCNT1 + base) << 8) |
inb(SCB_RESID_DCNT0 + base);
if (actual < cmd->underflow)
{
printk(KERN_WARNING "scsi%d: Target %d underflow - "
"Wanted at least %u, got %u, residual SG count %d.\n",
p->host_no, cmd->target, cmd->underflow, actual,
inb(SCB_RESID_SGCNT + base));
aic7xxx_error(cmd) = DID_RETRY_COMMAND;
aic7xxx_status(cmd) = scb->target_status;
}
}
}
break;
case ABORT_TAG:
scb_index = inb(SCB_TAG + base);
scb = (p->scb_array[scb_index]);
if (!(scb->state & SCB_ACTIVE) || (scb->cmd == NULL))
{
printk(KERN_WARNING "scsi%d: Referenced SCB not valid during "
"SEQINT 0x%x, scb %d, state 0x%x, cmd 0x%lx\n", p->host_no,
intstat, scb_index, scb->state, (unsigned long) scb->cmd);
}
else
{
cmd = scb->cmd;
/*
* We didn't receive a valid tag back from the target
* on a reconnect.
*/
printk("scsi%d: Invalid tag received on target %d, channel %c, "
"lun %d - Sending ABORT_TAG.\n", p->host_no,
scsi_id, channel, cmd->lun & 0x07);
cmd->result = (DID_RETRY_COMMAND << 16);
aic7xxx_done(p, scb);
}
break;
case AWAITING_MSG:
scb_index = inb(SCB_TAG + base);
scb = (p->scb_array[scb_index]);
if (!(scb->state & SCB_ACTIVE) || (scb->cmd == NULL))
{
printk(KERN_WARNING "scsi%d: Referenced SCB not valid during "
"SEQINT 0x%x, scb %d, state 0x%x, cmd 0x%lx.\n", p->host_no,
intstat, scb_index, scb->state, (unsigned long) scb->cmd);
}
else
{
/*
* This SCB had a zero length command, informing the sequencer
* that we wanted to send a special message to this target.
* We only do this for BUS_DEVICE_RESET messages currently.
*/
if (scb->state & SCB_DEVICE_RESET)
{
#ifdef AIC7XXX_DEBUG_ABORT
printk ("aic7xxx: (isr) sending bus device reset to target %d\n",
scsi_id);
#endif
outb(MSG_BUS_DEVICE_RESET, MSG0 + base);
outb(1, MSG_LEN + base);
}
else
{
panic("scsi%d: AWAITING_SCB for an SCB that does "
"not have a waiting message.\n", p->host_no);
}
}
break;
case IMMEDDONE:
scb_index = inb(SCB_TAG + base);
scb = (p->scb_array[scb_index]);
#ifdef AIC7XXX_DEBUG_ABORT
printk("aic7xxx: received IMMEDDONE for target %d, scb %d, state %d\n",
scsi_id, scb_index, scb->state);
#endif
if (scb->state & SCB_DEVICE_RESET)
{
int found;
/*
* Go back to async/narrow transfers and renegotiate.
*/
aic7xxx_unbusy_target(scsi_id, channel, base);
p->needsdtr |= (p->needsdtr_copy & target_mask);
p->needwdtr |= (p->needwdtr_copy & target_mask);
p->sdtr_pending &= ~target_mask;
p->wdtr_pending &= ~target_mask;
scratch = inb(TARG_SCRATCH + base + scratch_offset);
scratch &= SXFR;
outb(scratch, TARG_SCRATCH + base + scratch_offset);
found = aic7xxx_reset_device(p, (int) scsi_id, channel);
printk(KERN_INFO "scsi%d: Bus Device Reset delivered, %d SCBs "
"aborted.\n", p->host_no, found);
/* Indicate that we want to call aic7xxx_done_aborted_scbs() */
run_aborted_queue = TRUE;
}
else
{
panic("scsi%d: Immediate complete for unknown operation.\n",
p->host_no);
}
break;
case DATA_OVERRUN:
{
unsigned int overrun;
scb = (p->scb_array[inb(base + SCB_TAG)]);
overrun = inb(base + STCNT0) | (inb(base + STCNT1) << 8) |
(inb(base + STCNT2) << 16);
overrun =0x00FFFFFF - overrun;
printk(KERN_WARNING "scsi%d: data overrun of %d bytes detected; forcing "
"a retry.\n", p->host_no, overrun);
aic7xxx_error(scb->cmd) = DID_RETRY_COMMAND;
break;
}
#if AIC7XXX_NOT_YET
/* XXX Fill these in later */
case MESG_BUFFER_BUSY:
break;
case MSGIN_PHASEMIS:
break;
#endif
default: /* unknown */
printk(KERN_WARNING "scsi%d: SEQINT, INTSTAT 0x%x, SCSISIGI 0x%x.\n",
p->host_no, intstat, inb(SCSISIGI + base));
break;
}
/*
* Clear the sequencer interrupt and unpause the sequencer.
*/
outb(CLRSEQINT, CLRINT + base);
UNPAUSE_SEQUENCER(p);
}
if (intstat & SCSIINT)
{
int status = inb(SSTAT1 + base);
scsi_id = (inb(SCSIID + base) >> 4) & 0x0F;
channel = 'A';
if (inb(SBLKCTL + base) & SELBUSB)
{
channel = 'B';
}
scb_index = inb(SCB_TAG + base);
scb = (p->scb_array[scb_index]);
if (status & SCSIRSTI)
{
PAUSE_SEQUENCER(p);
printk(KERN_WARNING "scsi%d: SCSIINT - Someone reset channel %c.\n",
p->host_no, channel);
/*
* Go through and abort all commands for the channel, but do not
* reset the channel again.
*/
aic7xxx_reset_channel(p, channel, FALSE);
run_aborted_queue = TRUE;
}
else if (!(scb->state & SCB_ACTIVE) || (scb->cmd == NULL))
{
printk(KERN_WARNING "scsi%d: SCSIINT - No command for SCB.\n", p->host_no);
/*
* Turn off the interrupt and set status to zero, so that it
* falls through the rest of the SCSIINT code.
*/
outb(status, CLRSINT1 + base);
UNPAUSE_SEQUENCER(p);
outb(CLRSCSIINT, CLRINT + base);
scb = NULL;
}
else if (status & SCSIPERR)
{
char *phase;
unsigned char mesg_out = MSG_NOP;
unsigned char lastphase = inb(LASTPHASE + base);
cmd = scb->cmd;
switch (lastphase)
{
case P_DATAOUT:
phase = "Data-Out";
break;
case P_DATAIN:
phase = "Data-In";
mesg_out = MSG_INITIATOR_DET_ERROR;
break;
case P_COMMAND:
phase = "Command";
break;
case P_MESGOUT:
phase = "Message-Out";
break;
case P_STATUS:
phase = "Status";
mesg_out = MSG_INITIATOR_DET_ERROR;
break;
case P_MESGIN:
phase = "Message-In";
mesg_out = MSG_MSG_PARITY_ERROR;
break;
default:
phase = "unknown";
break;
}
/*
* A parity error has occurred during a data
* transfer phase. Flag it and continue.
*/
printk(KERN_WARNING "scsi%d: Parity error during phase %s on target %d, "
"channel %d, lun %d.\n", p->host_no, phase,
cmd->target, cmd->channel & 0x01, cmd->lun & 0x07);
/*
* We've set the hardware to assert ATN if we get a parity
* error on "in" phases, so all we need to do is stuff the
* message buffer with the appropriate message. In phases
* have set mesg_out to something other than MSG_NOP.
*/
if (mesg_out != MSG_NOP)
{
outb(mesg_out, MSG0 + base);
outb(1, MSG_LEN + base);
cmd->result = DID_PARITY << 16;
}
else
{
/*
* Should we allow the target to make this decision for us?
*/
cmd->result = DID_RETRY_COMMAND << 16;
}
aic7xxx_done(p, scb);
}
else if (status & SELTO)
{
unsigned char waiting;
cmd = scb->cmd;
cmd->result = (DID_TIME_OUT << 16);
/*
* Clear an pending messages for the timed out
* target and mark the target as free.
*/
ha_flags = inb(FLAGS + base);
outb(0, MSG_LEN + base);
aic7xxx_unbusy_target(scsi_id, channel, base);
/*
* Stop the selection.
*/
outb(0, SCSISEQ + base);
outb(0, SCB_CONTROL + base);
outb(CLRSELTIMEO, CLRSINT1 + base);
outb(CLRSCSIINT, CLRINT + base);
/*
* Shift the waiting for selection queue forward
*/
waiting = inb(WAITING_SCBH + base);
outb(waiting, SCBPTR + base);
waiting = inb(SCB_NEXT + base);
outb(waiting, WAITING_SCBH + base);
RESTART_SEQUENCER(p);
aic7xxx_done(p, scb);
}
else if (!(status & BUSFREE))
{
/*
* We don't know what's going on. Turn off the
* interrupt source and try to continue.
*/
printk(KERN_WARNING "aic7xxx: SSTAT1(0x%x).\n", status);
outb(status, CLRSINT1 + base);
UNPAUSE_SEQUENCER(p);
outb(CLRSCSIINT, CLRINT + base);
}
}
if (run_aborted_queue)
aic7xxx_done_aborted_scbs(p);
if (intstat & CMDCMPLT)
{
int complete;
/*
* The sequencer will continue running when it
* issues this interrupt. There may be >1 commands
* finished, so loop until we've processed them all.
*/
do {
complete = inb(QOUTFIFO + base);
scb = (p->scb_array[complete]);
if (!(scb->state & SCB_ACTIVE) || (scb->cmd == NULL))
{
printk(KERN_WARNING "scsi%d: CMDCMPLT without command for SCB %d.\n"
" QOUTCNT %d, QINCNT %d, SCB state 0x%x, cmd 0x%lx, "
"pos(%d).\n", p->host_no, complete, inb(QOUTCNT + base),
inb(QINCNT + base), scb->state, (unsigned long) scb->cmd,
scb->position);
outb(CLRCMDINT, CLRINT + base);
continue;
}
cmd = scb->cmd;
cmd->result |= (aic7xxx_error(cmd) << 16);
if ((cmd->flags & WAS_SENSE) && !(cmd->flags & ASKED_FOR_SENSE))
{
/*
* Got sense information.
*/
cmd->flags &= ASKED_FOR_SENSE;
}
p->device_status[TARGET_INDEX(cmd)].flags |= DEVICE_SUCCESS;
/*
* Clear interrupt status before checking the output queue again.
* This eliminates a race condition whereby a command could
* complete between the queue poll and the interrupt clearing,
* so notification of the command being complete never made it
* back up to the kernel.
*/
outb(CLRCMDINT, CLRINT + base);
aic7xxx_done(p, scb);
#ifdef AIC7XXX_PROC_STATS
/*
* XXX: we should actually know how much actually transferred
* XXX: for each command, but apparently that's too difficult.
*/
actual = aic7xxx_length(cmd, 0);
if (!(cmd->flags & WAS_SENSE) && (actual > 0))
{
struct aic7xxx_xferstats *sp;
long *ptr;
int x;
sp = &p->stats[cmd->channel & 0x01][cmd->target & 0x0F][cmd->lun & 0x07];
sp->xfers++;
if (cmd->request.cmd == WRITE)
{
sp->w_total++;
sp->w_total512 += (actual >> 9);
ptr = sp->w_bins;
}
else
{
sp->r_total++;
sp->r_total512 += (actual >> 9);
ptr = sp->r_bins;
}
for (x = 9; x <= 17; x++)
{
if (actual < (1 << x))
{
ptr[x - 9]++;
break;
}
}
if (x > 17)
{
ptr[x - 9]++;
}
}
#endif /* AIC7XXX_PROC_STATS */
} while (inb(QOUTCNT + base) & p->qcntmask);
}
aic7xxx_done_cmds_complete(p);
p->flags &= ~IN_ISR;
aic7xxx_run_waiting_queues(p);
}
Generated by GNU enscript 1.6.4.