Enscript Output

extractedLnx/linux-2.5.9/drivers/scsi/aic7xxx_old.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 or else they cause
           * a parity error during msg_out phase to signal that they don't
           * like our settings.
           */
          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;
          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;
          scb->flags &= ~SCB_MSGOUT_BITS;
          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) )
              {
                /*
                 * 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);

                scb->sg_list[0].length = 
                  cpu_to_le32(sizeof(cmd->sense_buffer));
		scb->sg_list[0].address =
                        cpu_to_le32(pci_map_single(p->pdev, cmd->sense_buffer,
                                                   sizeof(cmd->sense_buffer),
                                                   PCI_DMA_FROMDEVICE));

                /*
                 * 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->SCSI_cmd_pointer = 
                  cpu_to_le32(SCB_DMA_ADDR(scb, scb->sense_cmd));
                hscb->data_count = scb->sg_list[0].length;
                hscb->data_pointer = scb->sg_list[0].address;
                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;
                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 */
              printk(INFO_LEAD "CHECK_CONDITION on REQUEST_SENSE, returning "
                     "an error.\n", p->host_no, CTL_OF_SCB(scb));
              aic7xxx_error(cmd) = DID_ERROR;
              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)
        {
          if (aic7xxx_verbose & VERBOSE_NEGOTIATION2)
          {
            printk(INFO_LEAD "Sending PPR (%d/%d/%d/%d) message.\n",
                   p->host_no, CTL_OF_SCB(scb),
                   p->transinfo[tindex].goal_period,
                   p->transinfo[tindex].goal_offset,
                   p->transinfo[tindex].goal_width,
                   p->transinfo[tindex].goal_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), 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);
          printk(KERN_WARNING "  Raw SCSI Command: 0x");
          for (i = 0; i < scb->hscb->SCSI_cmd_length; i++)
          {
            printk("%02x ", scb->cmd->cmnd[i]);
          }
          printk("\n");
          if(aic7xxx_verbose > 0xffff)
          {
            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, sg_addr;
        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;
        }
        if(aic7xxx_verbose & VERBOSE_MINOR_ERROR)
          printk(INFO_LEAD "Got WIDE_RESIDUE message, patching up data "
                 "pointer.\n", p->host_no, CTL_OF_SCB(scb));

        /*
         * 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);
        sg_addr = aic_inb(p, SG_COUNT + 1) | (aic_inb(p, SG_COUNT + 2) << 8) |
          (aic_inb(p, SG_COUNT + 3) << 16) | (aic_inb(p, SG_COUNT + 4) << 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) << 16);
        index = scb->sg_count - ((resid_sgcnt) ? resid_sgcnt : 1);
        native_addr = le32_to_cpu(scb->sg_list[index].address);
        native_length = le32_to_cpu(scb->sg_list[index].length);
        /*
         * If resid_dcnt == native_length, then we just loaded this SG
         * segment and we need to back it up one...
         */
        if(resid_dcnt == native_length)
        {
          if(index == 0)
          {
            /*
             * Oops, this isn't right, we can't back up to before the
             * beginning.  This must be a bogus message, ignore it.
             */
            break;
          }
          resid_dcnt = 1;
          resid_sgcnt += 1;
          native_addr = le32_to_cpu(scb->sg_list[index - 1].address);
          native_length = le32_to_cpu(scb->sg_list[index - 1].length);
          cur_addr = native_addr + (native_length - 1);
          sg_addr -= sizeof(struct hw_scatterlist);
        }
        else
        {
          /*
           * resid_dcnt != native_length, so we are in the middle of a SG
           * element.  Back it up one byte and leave the rest alone.
           */
          resid_dcnt += 1;
          cur_addr -= 1;
        }
        
        /*
         * Output the new addresses and counts to the right places on the
         * card.
         */
        aic_outb(p, resid_sgcnt, SG_COUNT);
        aic_outb(p, resid_sgcnt, SCB_RESID_SGCNT);
        aic_outb(p, sg_addr & 0xff, SG_COUNT + 1);
        aic_outb(p, (sg_addr >> 8) & 0xff, SG_COUNT + 2);
        aic_outb(p, (sg_addr >> 16) & 0xff, SG_COUNT + 3);
        aic_outb(p, (sg_addr >> 24) & 0xff, SG_COUNT + 4);
        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);

        /*
         * The sequencer actually wants to find the new address
         * in the SHADDR register set.  On the Ultra2 and later controllers
         * this register set is readonly.  In order to get the right number
         * into the register, you actually have to enter it in HADDR and then
         * use the PRELOADEN bit of DFCNTRL to drop it through from the
         * HADDR register to the SHADDR register.  On non-Ultra2 controllers,
         * we simply write it direct.
         */
        if(p->features & AHC_ULTRA2)
        {
          /*
           * We might as well be accurate and drop both the resid_dcnt and
           * cur_addr into HCNT and HADDR and have both of them drop
           * through to the shadow layer together.
           */
          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);
          aic_outb(p, aic_inb(p, DMAPARAMS) | PRELOADEN, DFCNTRL);
          udelay(1);
          aic_outb(p, aic_inb(p, DMAPARAMS) & ~(SCSIEN|HDMAEN), DFCNTRL);
          i=0;
          while(((aic_inb(p, DFCNTRL) & (SCSIEN|HDMAEN)) != 0) && (i++ < 1000))
          {
            udelay(1);
          }
        }
        else
        {
          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;

    case SEQ_SG_FIXUP:
    {
      unsigned char scb_index, tmp;
      int sg_addr, sg_length;

      scb_index = aic_inb(p, SCB_TAG);

      if(scb_index > p->scb_data->numscbs)
      {
        printk(WARN_LEAD "invalid scb_index during SEQ_SG_FIXUP.\n",
          p->host_no, -1, -1, -1);
        printk(INFO_LEAD "SCSISIGI 0x%x, SEQADDR 0x%x, SSTAT0 0x%x, SSTAT1 "
           "0x%x\n", p->host_no, -1, -1, -1,
           aic_inb(p, SCSISIGI),
           aic_inb(p, SEQADDR0) | (aic_inb(p, SEQADDR1) << 8),
           aic_inb(p, SSTAT0), aic_inb(p, SSTAT1));
        printk(INFO_LEAD "SG_CACHEPTR 0x%x, SSTAT2 0x%x, STCNT 0x%x\n",
           p->host_no, -1, -1, -1, aic_inb(p, SG_CACHEPTR),
           aic_inb(p, SSTAT2), aic_inb(p, STCNT + 2) << 16 |
           aic_inb(p, STCNT + 1) << 8 | aic_inb(p, STCNT));
        /*
         * 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 SEQ_SG_FIXUP flags:0x%x "
               "scb->cmd:0x%p\n", p->host_no, CTL_OF_SCB(scb),
               scb->flags, scb->cmd);
        printk(INFO_LEAD "SCSISIGI 0x%x, SEQADDR 0x%x, SSTAT0 0x%x, SSTAT1 "
           "0x%x\n", p->host_no, CTL_OF_SCB(scb),
           aic_inb(p, SCSISIGI),
           aic_inb(p, SEQADDR0) | (aic_inb(p, SEQADDR1) << 8),
           aic_inb(p, SSTAT0), aic_inb(p, SSTAT1));
        printk(INFO_LEAD "SG_CACHEPTR 0x%x, SSTAT2 0x%x, STCNT 0x%x\n",
           p->host_no, CTL_OF_SCB(scb), aic_inb(p, SG_CACHEPTR),
           aic_inb(p, SSTAT2), aic_inb(p, STCNT + 2) << 16 |
           aic_inb(p, STCNT + 1) << 8 | aic_inb(p, STCNT));
        break;
      }
      if(aic7xxx_verbose & VERBOSE_MINOR_ERROR)
        printk(INFO_LEAD "Fixing up SG address for sequencer.\n", p->host_no,
               CTL_OF_SCB(scb));
      /*
       * Advance the SG pointer to the next element in the list
       */
      tmp = aic_inb(p, SG_NEXT);
      tmp += SG_SIZEOF;
      aic_outb(p, tmp, SG_NEXT);
      if( tmp < SG_SIZEOF )
        aic_outb(p, aic_inb(p, SG_NEXT + 1) + 1, SG_NEXT + 1);
      tmp = aic_inb(p, SG_COUNT) - 1;
      aic_outb(p, tmp, SG_COUNT);
      sg_addr = le32_to_cpu(scb->sg_list[scb->sg_count - tmp].address);
      sg_length = le32_to_cpu(scb->sg_list[scb->sg_count - tmp].length);
      /*
       * Now stuff the element we just advanced past down onto the
       * card so it can be stored in the residual area.
       */
      aic_outb(p, sg_addr & 0xff, HADDR);
      aic_outb(p, (sg_addr >> 8) & 0xff, HADDR + 1);
      aic_outb(p, (sg_addr >> 16) & 0xff, HADDR + 2);
      aic_outb(p, (sg_addr >> 24) & 0xff, HADDR + 3);
      aic_outb(p, sg_length & 0xff, HCNT);
      aic_outb(p, (sg_length >> 8) & 0xff, HCNT + 1);
      aic_outb(p, (sg_length >> 16) & 0xff, HCNT + 2);
      aic_outb(p, (tmp << 2) | ((tmp == 1) ? LAST_SEG : 0), SG_CACHEPTR);
      aic_outb(p, aic_inb(p, DMAPARAMS), DFCNTRL);
      while(aic_inb(p, SSTAT0) & SDONE) udelay(1);
      while(aic_inb(p, DFCNTRL) & (HDMAEN|SCSIEN)) aic_outb(p, 0, DFCNTRL);
    }
    break;

#if AIC7XXX_NOT_YET 
    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.