Enscript Output

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.