aic7xxx_old.c
上传用户:lgb322
上传日期:2013-02-24
资源大小:30529k
文件大小:392k
- p->needppr &= ~mask;
- p->needppr_copy &= ~mask;
- p->needsdtr |= mask;
- p->needsdtr_copy |= mask;
- p->needwdtr |= mask;
- p->needwdtr_copy |= mask;
- }
- }
- }
- queue_depth = p->dev_temp_queue_depth[tindex];
- if (queue_depth >= p->dev_active_cmds[tindex])
- {
- scbp = scbq_remove_head(&p->delayed_scbs[tindex]);
- if (scbp)
- {
- if (queue_depth == 1)
- {
- /*
- * Give extra preference to untagged devices, such as CD-R devices
- * This makes it more likely that a drive *won't* stuff up while
- * waiting on data at a critical time, such as CD-R writing and
- * audio CD ripping operations. Should also benefit tape drives.
- */
- scbq_insert_head(&p->waiting_scbs, scbp);
- }
- else
- {
- scbq_insert_tail(&p->waiting_scbs, scbp);
- }
- #ifdef AIC7XXX_VERBOSE_DEBUGGING
- if (aic7xxx_verbose > 0xffff)
- printk(INFO_LEAD "Moving SCB from delayed to waiting queue.n",
- p->host_no, CTL_OF_SCB(scbp));
- #endif
- if (queue_depth > p->dev_active_cmds[tindex])
- {
- scbp = scbq_remove_head(&p->delayed_scbs[tindex]);
- if (scbp)
- scbq_insert_tail(&p->waiting_scbs, scbp);
- }
- }
- }
- if (!(scb->tag_action))
- {
- aic7xxx_index_busy_target(p, scb->hscb->target_channel_lun,
- /* unbusy */ TRUE);
- if (p->tagenable & (1<<tindex))
- {
- p->dev_temp_queue_depth[tindex] = p->dev_max_queue_depth[tindex];
- }
- }
- if(scb->flags & SCB_DTR_SCB)
- {
- p->dtr_pending &= ~(1 << tindex);
- }
- p->dev_active_cmds[tindex]--;
- p->activescbs--;
- {
- int actual;
- /*
- * XXX: we should actually know how much actually transferred
- * XXX: for each command, but apparently that's too difficult.
- *
- * We set a lower limit of 512 bytes on the transfer length. We
- * ignore anything less than this because we don't have a real
- * reason to count it. Read/Writes to tapes are usually about 20K
- * and disks are a minimum of 512 bytes unless you want to count
- * non-read/write commands (such as TEST_UNIT_READY) which we don't
- */
- actual = scb->sg_length;
- if ((actual >= 512) && (((cmd->result >> 16) & 0xf) == DID_OK))
- {
- struct aic7xxx_xferstats *sp;
- #ifdef AIC7XXX_PROC_STATS
- long *ptr;
- int x;
- #endif /* AIC7XXX_PROC_STATS */
- sp = &p->stats[TARGET_INDEX(cmd)];
- /*
- * For block devices, cmd->request.cmd is always == either READ or
- * WRITE. For character devices, this isn't always set properly, so
- * we check data_cmnd[0]. This catches the conditions for st.c, but
- * I'm still not sure if request.cmd is valid for sg devices.
- */
- if ( (cmd->request.cmd == WRITE) || (cmd->data_cmnd[0] == WRITE_6) ||
- (cmd->data_cmnd[0] == WRITE_FILEMARKS) )
- {
- sp->w_total++;
- #ifdef AIC7XXX_VERBOSE_DEBUGGING
- if ( (sp->w_total > 16) && (aic7xxx_verbose > 0xffff) )
- aic7xxx_verbose &= 0xffff;
- #endif
- #ifdef AIC7XXX_PROC_STATS
- ptr = sp->w_bins;
- #endif /* AIC7XXX_PROC_STATS */
- }
- else
- {
- sp->r_total++;
- #ifdef AIC7XXX_VERBOSE_DEBUGGING
- if ( (sp->r_total > 16) && (aic7xxx_verbose > 0xffff) )
- aic7xxx_verbose &= 0xffff;
- #endif
- #ifdef AIC7XXX_PROC_STATS
- ptr = sp->r_bins;
- #endif /* AIC7XXX_PROC_STATS */
- }
- #ifdef AIC7XXX_PROC_STATS
- x = -11;
- while(actual)
- {
- actual >>= 1;
- x++;
- }
- if (x < 0)
- {
- ptr[0]++;
- }
- else if (x > 7)
- {
- ptr[7]++;
- }
- else
- {
- ptr[x]++;
- }
- #endif /* AIC7XXX_PROC_STATS */
- }
- }
- aic7xxx_free_scb(p, scb);
- aic7xxx_queue_cmd_complete(p, cmd);
- }
- /*+F*************************************************************************
- * Function:
- * aic7xxx_run_done_queue
- *
- * Description:
- * Calls the aic7xxx_done() for the Scsi_Cmnd of each scb in the
- * aborted list, and adds each scb to the free list. If complete
- * is TRUE, we also process the commands complete list.
- *-F*************************************************************************/
- static void
- aic7xxx_run_done_queue(struct aic7xxx_host *p, /*complete*/ int complete)
- {
- struct aic7xxx_scb *scb;
- int i, found = 0;
- for (i = 0; i < p->scb_data->numscbs; i++)
- {
- scb = p->scb_data->scb_array[i];
- if (scb->flags & SCB_QUEUED_FOR_DONE)
- {
- if (aic7xxx_verbose & (VERBOSE_ABORT_PROCESS | VERBOSE_RESET_PROCESS))
- printk(INFO_LEAD "Aborting scb %dn",
- p->host_no, CTL_OF_SCB(scb), scb->hscb->tag);
- found++;
- /*
- * Clear any residual information since the normal aic7xxx_done() path
- * doesn't touch the residuals.
- */
- scb->hscb->residual_SG_segment_count = 0;
- scb->hscb->residual_data_count[0] = 0;
- scb->hscb->residual_data_count[1] = 0;
- scb->hscb->residual_data_count[2] = 0;
- aic7xxx_done(p, scb);
- }
- }
- if (aic7xxx_verbose & (VERBOSE_ABORT_RETURN | VERBOSE_RESET_RETURN))
- {
- printk(INFO_LEAD "%d commands found and queued for "
- "completion.n", p->host_no, -1, -1, -1, found);
- }
- if (complete)
- {
- aic7xxx_done_cmds_complete(p);
- }
- }
- /*+F*************************************************************************
- * Function:
- * aic7xxx_abort_waiting_scb
- *
- * Description:
- * Manipulate the waiting for selection list and return the
- * scb that follows the one that we remove.
- *-F*************************************************************************/
- static unsigned char
- aic7xxx_abort_waiting_scb(struct aic7xxx_host *p, struct aic7xxx_scb *scb,
- unsigned char scbpos, unsigned char prev)
- {
- unsigned char curscb, next;
- /*
- * Select the SCB we want to abort and pull the next pointer out of it.
- */
- curscb = aic_inb(p, SCBPTR);
- aic_outb(p, scbpos, SCBPTR);
- next = aic_inb(p, SCB_NEXT);
- aic7xxx_add_curscb_to_free_list(p);
- /*
- * Update the waiting list
- */
- if (prev == SCB_LIST_NULL)
- {
- /*
- * First in the list
- */
- aic_outb(p, next, WAITING_SCBH);
- }
- else
- {
- /*
- * Select the scb that pointed to us and update its next pointer.
- */
- aic_outb(p, prev, SCBPTR);
- aic_outb(p, next, SCB_NEXT);
- }
- /*
- * Point us back at the original scb position and inform the SCSI
- * system that the command has been aborted.
- */
- aic_outb(p, curscb, SCBPTR);
- return (next);
- }
- /*+F*************************************************************************
- * Function:
- * aic7xxx_search_qinfifo
- *
- * Description:
- * Search the queue-in FIFO for matching SCBs and conditionally
- * requeue. Returns the number of matching SCBs.
- *-F*************************************************************************/
- static int
- aic7xxx_search_qinfifo(struct aic7xxx_host *p, int target, int channel,
- int lun, unsigned char tag, int flags, int requeue,
- volatile scb_queue_type *queue)
- {
- int found;
- unsigned char qinpos, qintail;
- struct aic7xxx_scb *scbp;
- found = 0;
- qinpos = aic_inb(p, QINPOS);
- qintail = p->qinfifonext;
- p->qinfifonext = qinpos;
- while (qinpos != qintail)
- {
- scbp = p->scb_data->scb_array[p->qinfifo[qinpos++]];
- if (aic7xxx_match_scb(p, scbp, target, channel, lun, tag))
- {
- /*
- * We found an scb that needs to be removed.
- */
- if (requeue && (queue != NULL))
- {
- if (scbp->flags & SCB_WAITINGQ)
- {
- scbq_remove(queue, scbp);
- scbq_remove(&p->waiting_scbs, scbp);
- scbq_remove(&p->delayed_scbs[TARGET_INDEX(scbp->cmd)], scbp);
- p->dev_active_cmds[TARGET_INDEX(scbp->cmd)]++;
- p->activescbs++;
- }
- scbq_insert_tail(queue, scbp);
- p->dev_active_cmds[TARGET_INDEX(scbp->cmd)]--;
- p->activescbs--;
- scbp->flags |= SCB_WAITINGQ;
- if ( !(scbp->tag_action & TAG_ENB) )
- {
- aic7xxx_index_busy_target(p, scbp->hscb->target_channel_lun,
- TRUE);
- }
- }
- else if (requeue)
- {
- p->qinfifo[p->qinfifonext++] = scbp->hscb->tag;
- }
- else
- {
- /*
- * Preserve any SCB_RECOVERY_SCB flags on this scb then set the
- * flags we were called with, presumeably so aic7xxx_run_done_queue
- * can find this scb
- */
- scbp->flags = flags | (scbp->flags & SCB_RECOVERY_SCB);
- if (aic7xxx_index_busy_target(p, scbp->hscb->target_channel_lun,
- FALSE) == scbp->hscb->tag)
- {
- aic7xxx_index_busy_target(p, scbp->hscb->target_channel_lun,
- TRUE);
- }
- }
- found++;
- }
- else
- {
- p->qinfifo[p->qinfifonext++] = scbp->hscb->tag;
- }
- }
- /*
- * Now that we've done the work, clear out any left over commands in the
- * qinfifo and update the KERNEL_QINPOS down on the card.
- *
- * NOTE: This routine expect the sequencer to already be paused when
- * it is run....make sure it's that way!
- */
- qinpos = p->qinfifonext;
- while(qinpos != qintail)
- {
- p->qinfifo[qinpos++] = SCB_LIST_NULL;
- }
- if (p->features & AHC_QUEUE_REGS)
- aic_outb(p, p->qinfifonext, HNSCB_QOFF);
- else
- aic_outb(p, p->qinfifonext, KERNEL_QINPOS);
- return (found);
- }
- /*+F*************************************************************************
- * Function:
- * aic7xxx_scb_on_qoutfifo
- *
- * Description:
- * Is the scb that was passed to us currently on the qoutfifo?
- *-F*************************************************************************/
- static int
- aic7xxx_scb_on_qoutfifo(struct aic7xxx_host *p, struct aic7xxx_scb *scb)
- {
- int i=0;
- while(p->qoutfifo[(p->qoutfifonext + i) & 0xff ] != SCB_LIST_NULL)
- {
- if(p->qoutfifo[(p->qoutfifonext + i) & 0xff ] == scb->hscb->tag)
- return TRUE;
- else
- i++;
- }
- return FALSE;
- }
- /*+F*************************************************************************
- * Function:
- * aic7xxx_reset_device
- *
- * Description:
- * The device at the given target/channel has been reset. Abort
- * all active and queued scbs for that target/channel. This function
- * need not worry about linked next pointers because if was a MSG_ABORT_TAG
- * then we had a tagged command (no linked next), if it was MSG_ABORT or
- * MSG_BUS_DEV_RESET then the device won't know about any commands any more
- * and no busy commands will exist, and if it was a bus reset, then nothing
- * knows about any linked next commands any more. In all cases, we don't
- * need to worry about the linked next or busy scb, we just need to clear
- * them.
- *-F*************************************************************************/
- static void
- aic7xxx_reset_device(struct aic7xxx_host *p, int target, int channel,
- int lun, unsigned char tag)
- {
- struct aic7xxx_scb *scbp;
- unsigned char active_scb, tcl;
- int i = 0, j, init_lists = FALSE;
- /*
- * Restore this when we're done
- */
- active_scb = aic_inb(p, SCBPTR);
- if (aic7xxx_verbose & (VERBOSE_RESET_PROCESS | VERBOSE_ABORT_PROCESS))
- {
- printk(INFO_LEAD "Reset device, active_scb %dn",
- p->host_no, channel, target, lun, active_scb);
- printk(INFO_LEAD "Current scb_tag %d, SEQADDR 0x%x, LASTPHASE "
- "0x%xn",
- p->host_no, channel, target, lun, aic_inb(p, SCB_TAG),
- aic_inb(p, SEQADDR0) | (aic_inb(p, SEQADDR1) << 8),
- aic_inb(p, LASTPHASE));
- printk(INFO_LEAD "SG_CACHEPTR 0x%x, SG_COUNT %d, SCSISIGI 0x%xn",
- p->host_no, channel, target, lun,
- (p->features & AHC_ULTRA2) ? aic_inb(p, SG_CACHEPTR) : 0,
- aic_inb(p, SG_COUNT), aic_inb(p, SCSISIGI));
- printk(INFO_LEAD "SSTAT0 0x%x, SSTAT1 0x%x, SSTAT2 0x%xn",
- p->host_no, channel, target, lun, aic_inb(p, SSTAT0),
- aic_inb(p, SSTAT1), aic_inb(p, SSTAT2));
- }
- /*
- * Deal with the busy target and linked next issues.
- */
- {
- int min_target, max_target;
- struct aic7xxx_scb *scbp, *prev_scbp;
- /* Make all targets 'relative' to bus A. */
- if (target == ALL_TARGETS)
- {
- switch (channel)
- {
- case 0:
- min_target = 0;
- max_target = (p->features & AHC_WIDE) ? 15 : 7;
- break;
- case 1:
- min_target = 8;
- max_target = 15;
- break;
- case ALL_CHANNELS:
- default:
- min_target = 0;
- max_target = (p->features & (AHC_TWIN|AHC_WIDE)) ? 15 : 7;
- break;
- }
- }
- else
- {
- min_target = target | (channel << 3);
- max_target = min_target;
- }
- for (i = min_target; i <= max_target; i++)
- {
- if ( i == p->scsi_id )
- {
- continue;
- }
- if (aic7xxx_verbose & (VERBOSE_ABORT_PROCESS | VERBOSE_RESET_PROCESS))
- printk(INFO_LEAD "Cleaning up status information "
- "and delayed_scbs.n", p->host_no, channel, i, lun);
- p->dev_flags[i] &= ~BUS_DEVICE_RESET_PENDING;
- if ( tag == SCB_LIST_NULL )
- {
- p->dev_flags[i] |= DEVICE_PRINT_DTR | DEVICE_RESET_DELAY;
- p->dev_expires[i] = jiffies + (1 * HZ);
- p->dev_timer_active |= (0x01 << i);
- p->dev_last_queue_full_count[i] = 0;
- p->dev_last_queue_full[i] = 0;
- p->dev_temp_queue_depth[i] =
- p->dev_max_queue_depth[i];
- }
- for(j=0; j<MAX_LUNS; j++)
- {
- if (channel == 1)
- tcl = ((i << 4) & 0x70) | (channel << 3) | j;
- else
- tcl = (i << 4) | (channel << 3) | j;
- if ( (aic7xxx_index_busy_target(p, tcl, FALSE) == tag) ||
- (tag == SCB_LIST_NULL) )
- aic7xxx_index_busy_target(p, tcl, /* unbusy */ TRUE);
- }
- j = 0;
- prev_scbp = NULL;
- scbp = p->delayed_scbs[i].head;
- while ( (scbp != NULL) && (j++ <= (p->scb_data->numscbs + 1)) )
- {
- prev_scbp = scbp;
- scbp = scbp->q_next;
- if ( prev_scbp == scbp )
- {
- if (aic7xxx_verbose & (VERBOSE_ABORT | VERBOSE_RESET))
- printk(WARN_LEAD "Yikes!! scb->q_next == scb "
- "in the delayed_scbs queue!n", p->host_no, channel, i, lun);
- scbp = NULL;
- prev_scbp->q_next = NULL;
- p->delayed_scbs[i].tail = prev_scbp;
- }
- if (aic7xxx_match_scb(p, prev_scbp, target, channel, lun, tag))
- {
- scbq_remove(&p->delayed_scbs[i], prev_scbp);
- if (prev_scbp->flags & SCB_WAITINGQ)
- {
- p->dev_active_cmds[i]++;
- p->activescbs++;
- }
- prev_scbp->flags &= ~(SCB_ACTIVE | SCB_WAITINGQ);
- prev_scbp->flags |= SCB_RESET | SCB_QUEUED_FOR_DONE;
- }
- }
- if ( j > (p->scb_data->numscbs + 1) )
- {
- if (aic7xxx_verbose & (VERBOSE_ABORT | VERBOSE_RESET))
- printk(WARN_LEAD "Yikes!! There's a loop in the "
- "delayed_scbs queue!n", p->host_no, channel, i, lun);
- scbq_init(&p->delayed_scbs[i]);
- }
- if ( !(p->dev_timer_active & (0x01 << MAX_TARGETS)) ||
- time_after_eq(p->dev_timer.expires, p->dev_expires[i]) )
- {
- mod_timer(&p->dev_timer, p->dev_expires[i]);
- p->dev_timer_active |= (0x01 << MAX_TARGETS);
- }
- }
- }
- if (aic7xxx_verbose & (VERBOSE_ABORT_PROCESS | VERBOSE_RESET_PROCESS))
- printk(INFO_LEAD "Cleaning QINFIFO.n", p->host_no, channel, target, lun );
- aic7xxx_search_qinfifo(p, target, channel, lun, tag,
- SCB_RESET | SCB_QUEUED_FOR_DONE, /* requeue */ FALSE, NULL);
- /*
- * Search the waiting_scbs queue for matches, this catches any SCB_QUEUED
- * ABORT/RESET commands.
- */
- if (aic7xxx_verbose & (VERBOSE_ABORT_PROCESS | VERBOSE_RESET_PROCESS))
- printk(INFO_LEAD "Cleaning waiting_scbs.n", p->host_no, channel,
- target, lun );
- {
- struct aic7xxx_scb *scbp, *prev_scbp;
- j = 0;
- prev_scbp = NULL;
- scbp = p->waiting_scbs.head;
- while ( (scbp != NULL) && (j++ <= (p->scb_data->numscbs + 1)) )
- {
- prev_scbp = scbp;
- scbp = scbp->q_next;
- if ( prev_scbp == scbp )
- {
- if (aic7xxx_verbose & (VERBOSE_ABORT | VERBOSE_RESET))
- printk(WARN_LEAD "Yikes!! scb->q_next == scb "
- "in the waiting_scbs queue!n", p->host_no, CTL_OF_SCB(scbp));
- scbp = NULL;
- prev_scbp->q_next = NULL;
- p->waiting_scbs.tail = prev_scbp;
- }
- if (aic7xxx_match_scb(p, prev_scbp, target, channel, lun, tag))
- {
- scbq_remove(&p->waiting_scbs, prev_scbp);
- if (prev_scbp->flags & SCB_WAITINGQ)
- {
- p->dev_active_cmds[TARGET_INDEX(prev_scbp->cmd)]++;
- p->activescbs++;
- }
- prev_scbp->flags &= ~(SCB_ACTIVE | SCB_WAITINGQ);
- prev_scbp->flags |= SCB_RESET | SCB_QUEUED_FOR_DONE;
- }
- }
- if ( j > (p->scb_data->numscbs + 1) )
- {
- if (aic7xxx_verbose & (VERBOSE_ABORT | VERBOSE_RESET))
- printk(WARN_LEAD "Yikes!! There's a loop in the "
- "waiting_scbs queue!n", p->host_no, channel, target, lun);
- scbq_init(&p->waiting_scbs);
- }
- }
- /*
- * Search waiting for selection list.
- */
- if (aic7xxx_verbose & (VERBOSE_ABORT_PROCESS | VERBOSE_RESET_PROCESS))
- printk(INFO_LEAD "Cleaning waiting for selection "
- "list.n", p->host_no, channel, target, lun);
- {
- unsigned char next, prev, scb_index;
- next = aic_inb(p, WAITING_SCBH); /* Start at head of list. */
- prev = SCB_LIST_NULL;
- j = 0;
- while ( (next != SCB_LIST_NULL) && (j++ <= (p->scb_data->maxscbs + 1)) )
- {
- aic_outb(p, next, SCBPTR);
- scb_index = aic_inb(p, SCB_TAG);
- if (scb_index >= p->scb_data->numscbs)
- {
- /*
- * No aic7xxx_verbose check here.....we want to see this since it
- * means either the kernel driver or the sequencer screwed things up
- */
- printk(WARN_LEAD "Waiting List inconsistency; SCB index=%d, "
- "numscbs=%dn", p->host_no, channel, target, lun, scb_index,
- p->scb_data->numscbs);
- next = aic_inb(p, SCB_NEXT);
- aic7xxx_add_curscb_to_free_list(p);
- }
- else
- {
- scbp = p->scb_data->scb_array[scb_index];
- if (aic7xxx_match_scb(p, scbp, target, channel, lun, tag))
- {
- next = aic7xxx_abort_waiting_scb(p, scbp, next, prev);
- if (scbp->flags & SCB_WAITINGQ)
- {
- p->dev_active_cmds[TARGET_INDEX(scbp->cmd)]++;
- p->activescbs++;
- }
- scbp->flags &= ~(SCB_ACTIVE | SCB_WAITINGQ);
- scbp->flags |= SCB_RESET | SCB_QUEUED_FOR_DONE;
- if (prev == SCB_LIST_NULL)
- {
- /*
- * This is either the first scb on the waiting list, or we
- * have already yanked the first and haven't left any behind.
- * Either way, we need to turn off the selection hardware if
- * it isn't already off.
- */
- aic_outb(p, aic_inb(p, SCSISEQ) & ~ENSELO, SCSISEQ);
- aic_outb(p, CLRSELTIMEO, CLRSINT1);
- }
- }
- else
- {
- prev = next;
- next = aic_inb(p, SCB_NEXT);
- }
- }
- }
- if ( j > (p->scb_data->maxscbs + 1) )
- {
- printk(WARN_LEAD "Yikes!! There is a loop in the waiting for "
- "selection list!n", p->host_no, channel, target, lun);
- init_lists = TRUE;
- }
- }
- /*
- * Go through disconnected list and remove any entries we have queued
- * for completion, zeroing their control byte too.
- */
- if (aic7xxx_verbose & (VERBOSE_ABORT_PROCESS | VERBOSE_RESET_PROCESS))
- printk(INFO_LEAD "Cleaning disconnected scbs "
- "list.n", p->host_no, channel, target, lun);
- if (p->flags & AHC_PAGESCBS)
- {
- unsigned char next, prev, scb_index;
- next = aic_inb(p, DISCONNECTED_SCBH);
- prev = SCB_LIST_NULL;
- j = 0;
- while ( (next != SCB_LIST_NULL) && (j++ <= (p->scb_data->maxscbs + 1)) )
- {
- aic_outb(p, next, SCBPTR);
- scb_index = aic_inb(p, SCB_TAG);
- if (scb_index > p->scb_data->numscbs)
- {
- printk(WARN_LEAD "Disconnected List inconsistency; SCB index=%d, "
- "numscbs=%dn", p->host_no, channel, target, lun, scb_index,
- p->scb_data->numscbs);
- next = aic7xxx_rem_scb_from_disc_list(p, next, prev);
- }
- else
- {
- scbp = p->scb_data->scb_array[scb_index];
- if (aic7xxx_match_scb(p, scbp, target, channel, lun, tag))
- {
- next = aic7xxx_rem_scb_from_disc_list(p, next, prev);
- if (scbp->flags & SCB_WAITINGQ)
- {
- p->dev_active_cmds[TARGET_INDEX(scbp->cmd)]++;
- p->activescbs++;
- }
- scbp->flags &= ~(SCB_ACTIVE | SCB_WAITINGQ);
- scbp->flags |= SCB_RESET | SCB_QUEUED_FOR_DONE;
- scbp->hscb->control = 0;
- }
- else
- {
- prev = next;
- next = aic_inb(p, SCB_NEXT);
- }
- }
- }
- if ( j > (p->scb_data->maxscbs + 1) )
- {
- printk(WARN_LEAD "Yikes!! There is a loop in the disconnected list!n",
- p->host_no, channel, target, lun);
- init_lists = TRUE;
- }
- }
- /*
- * Walk the free list making sure no entries on the free list have
- * a valid SCB_TAG value or SCB_CONTROL byte.
- */
- if (p->flags & AHC_PAGESCBS)
- {
- unsigned char next;
- j = 0;
- next = aic_inb(p, FREE_SCBH);
- if ( (next >= p->scb_data->maxhscbs) && (next != SCB_LIST_NULL) )
- {
- printk(WARN_LEAD "Bogus FREE_SCBH!.n", p->host_no, channel,
- target, lun);
- init_lists = TRUE;
- next = SCB_LIST_NULL;
- }
- while ( (next != SCB_LIST_NULL) && (j++ <= (p->scb_data->maxscbs + 1)) )
- {
- aic_outb(p, next, SCBPTR);
- if (aic_inb(p, SCB_TAG) < p->scb_data->numscbs)
- {
- printk(WARN_LEAD "Free list inconsistency!.n", p->host_no, channel,
- target, lun);
- init_lists = TRUE;
- next = SCB_LIST_NULL;
- }
- else
- {
- aic_outb(p, SCB_LIST_NULL, SCB_TAG);
- aic_outb(p, 0, SCB_CONTROL);
- next = aic_inb(p, SCB_NEXT);
- }
- }
- if ( j > (p->scb_data->maxscbs + 1) )
- {
- printk(WARN_LEAD "Yikes!! There is a loop in the free list!n",
- p->host_no, channel, target, lun);
- init_lists = TRUE;
- }
- }
- /*
- * Go through the hardware SCB array looking for commands that
- * were active but not on any list.
- */
- if (init_lists)
- {
- aic_outb(p, SCB_LIST_NULL, FREE_SCBH);
- aic_outb(p, SCB_LIST_NULL, WAITING_SCBH);
- aic_outb(p, SCB_LIST_NULL, DISCONNECTED_SCBH);
- }
- for (i = p->scb_data->maxhscbs - 1; i >= 0; i--)
- {
- unsigned char scbid;
- aic_outb(p, i, SCBPTR);
- if (init_lists)
- {
- aic_outb(p, SCB_LIST_NULL, SCB_TAG);
- aic_outb(p, SCB_LIST_NULL, SCB_NEXT);
- aic_outb(p, 0, SCB_CONTROL);
- aic7xxx_add_curscb_to_free_list(p);
- }
- else
- {
- scbid = aic_inb(p, SCB_TAG);
- if (scbid < p->scb_data->numscbs)
- {
- scbp = p->scb_data->scb_array[scbid];
- if (aic7xxx_match_scb(p, scbp, target, channel, lun, tag))
- {
- aic_outb(p, 0, SCB_CONTROL);
- aic_outb(p, SCB_LIST_NULL, SCB_TAG);
- aic7xxx_add_curscb_to_free_list(p);
- }
- }
- }
- }
- /*
- * Go through the entire SCB array now and look for commands for
- * for this target that are stillactive. These are other (most likely
- * tagged) commands that were disconnected when the reset occurred.
- * Any commands we find here we know this about, it wasn't on any queue,
- * it wasn't in the qinfifo, it wasn't in the disconnected or waiting
- * lists, so it really must have been a paged out SCB. In that case,
- * we shouldn't need to bother with updating any counters, just mark
- * the correct flags and go on.
- */
- for (i = 0; i < p->scb_data->numscbs; i++)
- {
- scbp = p->scb_data->scb_array[i];
- if ((scbp->flags & SCB_ACTIVE) &&
- aic7xxx_match_scb(p, scbp, target, channel, lun, tag) &&
- !aic7xxx_scb_on_qoutfifo(p, scbp))
- {
- if (scbp->flags & SCB_WAITINGQ)
- {
- scbq_remove(&p->waiting_scbs, scbp);
- scbq_remove(&p->delayed_scbs[TARGET_INDEX(scbp->cmd)], scbp);
- p->dev_active_cmds[TARGET_INDEX(scbp->cmd)]++;
- p->activescbs++;
- }
- scbp->flags |= SCB_RESET | SCB_QUEUED_FOR_DONE;
- scbp->flags &= ~(SCB_ACTIVE | SCB_WAITINGQ);
- }
- }
- aic_outb(p, active_scb, SCBPTR);
- }
- /*+F*************************************************************************
- * Function:
- * aic7xxx_clear_intstat
- *
- * Description:
- * Clears the interrupt status.
- *-F*************************************************************************/
- static void
- aic7xxx_clear_intstat(struct aic7xxx_host *p)
- {
- /* Clear any interrupt conditions this may have caused. */
- aic_outb(p, CLRSELDO | CLRSELDI | CLRSELINGO, CLRSINT0);
- aic_outb(p, CLRSELTIMEO | CLRATNO | CLRSCSIRSTI | CLRBUSFREE | CLRSCSIPERR |
- CLRPHASECHG | CLRREQINIT, CLRSINT1);
- aic_outb(p, CLRSCSIINT | CLRSEQINT | CLRBRKADRINT | CLRPARERR, CLRINT);
- }
- /*+F*************************************************************************
- * Function:
- * aic7xxx_reset_current_bus
- *
- * Description:
- * Reset the current SCSI bus.
- *-F*************************************************************************/
- static void
- aic7xxx_reset_current_bus(struct aic7xxx_host *p)
- {
- /* Disable reset interrupts. */
- aic_outb(p, aic_inb(p, SIMODE1) & ~ENSCSIRST, SIMODE1);
- /* Turn off the bus' current operations, after all, we shouldn't have any
- * valid commands left to cause a RSELI and SELO once we've tossed the
- * bus away with this reset, so we might as well shut down the sequencer
- * until the bus is restarted as oppossed to saving the current settings
- * and restoring them (which makes no sense to me). */
- /* Turn on the bus reset. */
- aic_outb(p, aic_inb(p, SCSISEQ) | SCSIRSTO, SCSISEQ);
- while ( (aic_inb(p, SCSISEQ) & SCSIRSTO) == 0)
- mdelay(5);
- /*
- * Some of the new Ultra2 chipsets need a longer delay after a chip
- * reset than just the init setup creates, so we have to delay here
- * before we go into a reset in order to make the chips happy.
- */
- if (p->features & AHC_ULTRA2)
- mdelay(250);
- else
- mdelay(50);
- /* Turn off the bus reset. */
- aic_outb(p, 0, SCSISEQ);
- mdelay(10);
- aic7xxx_clear_intstat(p);
- /* Re-enable reset interrupts. */
- aic_outb(p, aic_inb(p, SIMODE1) | ENSCSIRST, SIMODE1);
- }
- /*+F*************************************************************************
- * Function:
- * aic7xxx_reset_channel
- *
- * Description:
- * Reset the channel.
- *-F*************************************************************************/
- static void
- aic7xxx_reset_channel(struct aic7xxx_host *p, int channel, int initiate_reset)
- {
- unsigned long offset_min, offset_max;
- unsigned char sblkctl;
- int cur_channel;
- if (aic7xxx_verbose & VERBOSE_RESET_PROCESS)
- printk(INFO_LEAD "Reset channel called, %s initiate reset.n",
- p->host_no, channel, -1, -1, (initiate_reset==TRUE) ? "will" : "won't" );
- if (channel == 1)
- {
- p->needsdtr |= (p->needsdtr_copy & 0xFF00);
- p->dtr_pending &= 0x00FF;
- offset_min = 8;
- offset_max = 16;
- }
- else
- {
- if (p->features & AHC_TWIN)
- {
- /* Channel A */
- p->needsdtr |= (p->needsdtr_copy & 0x00FF);
- p->dtr_pending &= 0xFF00;
- offset_min = 0;
- offset_max = 8;
- }
- else
- {
- p->needppr = p->needppr_copy;
- p->needsdtr = p->needsdtr_copy;
- p->needwdtr = p->needwdtr_copy;
- p->dtr_pending = 0x0;
- offset_min = 0;
- if (p->features & AHC_WIDE)
- {
- offset_max = 16;
- }
- else
- {
- offset_max = 8;
- }
- }
- }
- while (offset_min < offset_max)
- {
- /*
- * Revert to async/narrow transfers until we renegotiate.
- */
- aic_outb(p, 0, TARG_SCSIRATE + offset_min);
- if (p->features & AHC_ULTRA2)
- {
- aic_outb(p, 0, TARG_OFFSET + offset_min);
- }
- offset_min++;
- }
- /*
- * Reset the bus and unpause/restart the controller
- */
- sblkctl = aic_inb(p, SBLKCTL);
- if ( (p->chip & AHC_CHIPID_MASK) == AHC_AIC7770 )
- cur_channel = (sblkctl & SELBUSB) >> 3;
- else
- cur_channel = 0;
- if ( (cur_channel != channel) && (p->features & AHC_TWIN) )
- {
- /*
- * Case 1: Command for another bus is active
- */
- if (aic7xxx_verbose & VERBOSE_RESET_PROCESS)
- printk(INFO_LEAD "Stealthily resetting idle channel.n", p->host_no,
- channel, -1, -1);
- /*
- * Stealthily reset the other bus without upsetting the current bus.
- */
- aic_outb(p, sblkctl ^ SELBUSB, SBLKCTL);
- aic_outb(p, aic_inb(p, SIMODE1) & ~ENBUSFREE, SIMODE1);
- if (initiate_reset)
- {
- aic7xxx_reset_current_bus(p);
- }
- aic_outb(p, aic_inb(p, SCSISEQ) & (ENSELI|ENRSELI|ENAUTOATNP), SCSISEQ);
- aic7xxx_clear_intstat(p);
- aic_outb(p, sblkctl, SBLKCTL);
- }
- else
- {
- /*
- * Case 2: A command from this bus is active or we're idle.
- */
- if (aic7xxx_verbose & VERBOSE_RESET_PROCESS)
- printk(INFO_LEAD "Resetting currently active channel.n", p->host_no,
- channel, -1, -1);
- aic_outb(p, aic_inb(p, SIMODE1) & ~(ENBUSFREE|ENREQINIT),
- SIMODE1);
- p->flags &= ~AHC_HANDLING_REQINITS;
- p->msg_type = MSG_TYPE_NONE;
- p->msg_len = 0;
- if (initiate_reset)
- {
- aic7xxx_reset_current_bus(p);
- }
- aic_outb(p, aic_inb(p, SCSISEQ) & (ENSELI|ENRSELI|ENAUTOATNP), SCSISEQ);
- aic7xxx_clear_intstat(p);
- }
- if (aic7xxx_verbose & VERBOSE_RESET_RETURN)
- printk(INFO_LEAD "Channel resetn", p->host_no, channel, -1, -1);
- /*
- * Clean up all the state information for the pending transactions
- * on this bus.
- */
- aic7xxx_reset_device(p, ALL_TARGETS, channel, ALL_LUNS, SCB_LIST_NULL);
- if ( !(p->features & AHC_TWIN) )
- {
- restart_sequencer(p);
- }
- return;
- }
- /*+F*************************************************************************
- * Function:
- * aic7xxx_run_waiting_queues
- *
- * Description:
- * Scan the awaiting_scbs queue downloading and starting as many
- * scbs as we can.
- *-F*************************************************************************/
- static void
- aic7xxx_run_waiting_queues(struct aic7xxx_host *p)
- {
- struct aic7xxx_scb *scb;
- int tindex;
- int sent;
- #if LINUX_VERSION_CODE < KERNEL_VERSION(2,1,95)
- unsigned long cpu_flags = 0;
- #endif
- if (p->waiting_scbs.head == NULL)
- return;
- sent = 0;
- /*
- * First handle SCBs that are waiting but have been assigned a slot.
- */
- DRIVER_LOCK
- while ((scb = scbq_remove_head(&p->waiting_scbs)) != NULL)
- {
- tindex = TARGET_INDEX(scb->cmd);
- if ( !scb->tag_action && (p->tagenable & (1<<tindex)) )
- {
- p->dev_temp_queue_depth[tindex] = 1;
- }
- if ( (p->dev_active_cmds[tindex] >=
- p->dev_temp_queue_depth[tindex]) ||
- (p->dev_flags[tindex] & (DEVICE_RESET_DELAY|DEVICE_WAS_BUSY)) ||
- (p->flags & AHC_RESET_DELAY) )
- {
- scbq_insert_tail(&p->delayed_scbs[tindex], scb);
- }
- else
- {
- scb->flags &= ~SCB_WAITINGQ;
- p->dev_active_cmds[tindex]++;
- p->activescbs++;
- if ( !(scb->tag_action) )
- {
- aic7xxx_busy_target(p, scb);
- }
- p->qinfifo[p->qinfifonext++] = scb->hscb->tag;
- sent++;
- }
- }
- if (sent)
- {
- if (p->features & AHC_QUEUE_REGS)
- aic_outb(p, p->qinfifonext, HNSCB_QOFF);
- else
- {
- pause_sequencer(p);
- aic_outb(p, p->qinfifonext, KERNEL_QINPOS);
- unpause_sequencer(p, FALSE);
- }
- if (p->activescbs > p->max_activescbs)
- p->max_activescbs = p->activescbs;
- }
- DRIVER_UNLOCK
- }
- #ifdef CONFIG_PCI
- #define DPE 0x80
- #define SSE 0x40
- #define RMA 0x20
- #define RTA 0x10
- #define STA 0x08
- #define DPR 0x01
- /*+F*************************************************************************
- * Function:
- * aic7xxx_pci_intr
- *
- * Description:
- * Check the scsi card for PCI errors and clear the interrupt
- *
- * NOTE: If you don't have this function and a 2940 card encounters
- * a PCI error condition, the machine will end up locked as the
- * interrupt handler gets slammed with non-stop PCI error interrupts
- *-F*************************************************************************/
- static void
- aic7xxx_pci_intr(struct aic7xxx_host *p)
- {
- unsigned char status1;
- pci_read_config_byte(p->pdev, PCI_STATUS + 1, &status1);
- if ( (status1 & DPE) && (aic7xxx_verbose & VERBOSE_MINOR_ERROR) )
- printk(WARN_LEAD "Data Parity Error during PCI address or PCI write"
- "phase.n", p->host_no, -1, -1, -1);
- if ( (status1 & SSE) && (aic7xxx_verbose & VERBOSE_MINOR_ERROR) )
- printk(WARN_LEAD "Signal System Error Detectedn", p->host_no,
- -1, -1, -1);
- if ( (status1 & RMA) && (aic7xxx_verbose & VERBOSE_MINOR_ERROR) )
- printk(WARN_LEAD "Received a PCI Master Abortn", p->host_no,
- -1, -1, -1);
- if ( (status1 & RTA) && (aic7xxx_verbose & VERBOSE_MINOR_ERROR) )
- printk(WARN_LEAD "Received a PCI Target Abortn", p->host_no,
- -1, -1, -1);
- if ( (status1 & STA) && (aic7xxx_verbose & VERBOSE_MINOR_ERROR) )
- printk(WARN_LEAD "Signaled a PCI Target Abortn", p->host_no,
- -1, -1, -1);
- if ( (status1 & DPR) && (aic7xxx_verbose & VERBOSE_MINOR_ERROR) )
- printk(WARN_LEAD "Data Parity Error has been reported via PCI pin "
- "PERR#n", p->host_no, -1, -1, -1);
-
- pci_write_config_byte(p->pdev, PCI_STATUS + 1, status1);
- if (status1 & (DPR|RMA|RTA))
- aic_outb(p, CLRPARERR, CLRINT);
- if ( (aic7xxx_panic_on_abort) && (p->spurious_int > 500) )
- aic7xxx_panic_abort(p, NULL);
- }
- #endif /* CONFIG_PCI */
- /*+F*************************************************************************
- * Function:
- * aic7xxx_timer
- *
- * Description:
- * Take expired extries off of delayed queues and place on waiting queue
- * then run waiting queue to start commands.
- ***************************************************************************/
- static void
- aic7xxx_timer(struct aic7xxx_host *p)
- {
- int i, j;
- unsigned long cpu_flags = 0;
- struct aic7xxx_scb *scb;
- spin_lock_irqsave(&io_request_lock, cpu_flags);
- p->dev_timer_active &= ~(0x01 << MAX_TARGETS);
- if ( (p->dev_timer_active & (0x01 << p->scsi_id)) &&
- time_after_eq(jiffies, p->dev_expires[p->scsi_id]) )
- {
- p->flags &= ~AHC_RESET_DELAY;
- p->dev_timer_active &= ~(0x01 << p->scsi_id);
- }
- for(i=0; i<MAX_TARGETS; i++)
- {
- if ( (i != p->scsi_id) &&
- (p->dev_timer_active & (0x01 << i)) &&
- time_after_eq(jiffies, p->dev_expires[i]) )
- {
- p->dev_timer_active &= ~(0x01 << i);
- p->dev_flags[i] &= ~(DEVICE_RESET_DELAY|DEVICE_WAS_BUSY);
- p->dev_temp_queue_depth[i] = p->dev_max_queue_depth[i];
- j = 0;
- while ( ((scb = scbq_remove_head(&p->delayed_scbs[i])) != NULL) &&
- (j++ < p->scb_data->numscbs) )
- {
- scbq_insert_tail(&p->waiting_scbs, scb);
- }
- if (j == p->scb_data->numscbs)
- {
- printk(INFO_LEAD "timer: Yikes, loop in delayed_scbs list.n",
- p->host_no, 0, i, -1);
- scbq_init(&p->delayed_scbs[i]);
- scbq_init(&p->waiting_scbs);
- /*
- * Well, things are screwed now, wait for a reset to clean the junk
- * out.
- */
- }
- }
- else if ( p->dev_timer_active & (0x01 << i) )
- {
- if ( p->dev_timer_active & (0x01 << MAX_TARGETS) )
- {
- if ( time_after_eq(p->dev_timer.expires, p->dev_expires[i]) )
- {
- p->dev_timer.expires = p->dev_expires[i];
- }
- }
- else
- {
- p->dev_timer.expires = p->dev_expires[i];
- p->dev_timer_active |= (0x01 << MAX_TARGETS);
- }
- }
- }
- if ( p->dev_timer_active & (0x01 << MAX_TARGETS) )
- {
- add_timer(&p->dev_timer);
- }
- aic7xxx_run_waiting_queues(p);
- spin_unlock_irqrestore(&io_request_lock, cpu_flags);
- }
- /*+F*************************************************************************
- * Function:
- * aic7xxx_construct_ppr
- *
- * Description:
- * Build up a Parallel Protocol Request message for use with SCSI-3
- * devices.
- *-F*************************************************************************/
- static void
- aic7xxx_construct_ppr(struct aic7xxx_host *p, struct aic7xxx_scb *scb)
- {
- int tindex = TARGET_INDEX(scb->cmd);
- p->msg_buf[p->msg_index++] = MSG_EXTENDED;
- p->msg_buf[p->msg_index++] = MSG_EXT_PPR_LEN;
- p->msg_buf[p->msg_index++] = MSG_EXT_PPR;
- p->msg_buf[p->msg_index++] = p->transinfo[tindex].goal_period;
- p->msg_buf[p->msg_index++] = 0;
- p->msg_buf[p->msg_index++] = p->transinfo[tindex].goal_offset;
- p->msg_buf[p->msg_index++] = p->transinfo[tindex].goal_width;
- p->msg_buf[p->msg_index++] = p->transinfo[tindex].goal_options;
- p->msg_len += 8;
- }
- /*+F*************************************************************************
- * Function:
- * aic7xxx_construct_sdtr
- *
- * Description:
- * Constucts a synchronous data transfer message in the message
- * buffer on the sequencer.
- *-F*************************************************************************/
- static void
- aic7xxx_construct_sdtr(struct aic7xxx_host *p, unsigned char period,
- unsigned char offset)
- {
- p->msg_buf[p->msg_index++] = MSG_EXTENDED;
- p->msg_buf[p->msg_index++] = MSG_EXT_SDTR_LEN;
- p->msg_buf[p->msg_index++] = MSG_EXT_SDTR;
- p->msg_buf[p->msg_index++] = period;
- p->msg_buf[p->msg_index++] = offset;
- p->msg_len += 5;
- }
- /*+F*************************************************************************
- * Function:
- * aic7xxx_construct_wdtr
- *
- * Description:
- * Constucts a wide data transfer message in the message buffer
- * on the sequencer.
- *-F*************************************************************************/
- static void
- aic7xxx_construct_wdtr(struct aic7xxx_host *p, unsigned char bus_width)
- {
- p->msg_buf[p->msg_index++] = MSG_EXTENDED;
- p->msg_buf[p->msg_index++] = MSG_EXT_WDTR_LEN;
- p->msg_buf[p->msg_index++] = MSG_EXT_WDTR;
- p->msg_buf[p->msg_index++] = bus_width;
- p->msg_len += 4;
- }
- /*+F*************************************************************************
- * Function:
- * aic7xxx_calc_residual
- *
- * Description:
- * Calculate the residual data not yet transferred.
- *-F*************************************************************************/
- static void
- aic7xxx_calculate_residual (struct aic7xxx_host *p, struct aic7xxx_scb *scb)
- {
- struct aic7xxx_hwscb *hscb;
- Scsi_Cmnd *cmd;
- int actual, i;
- cmd = scb->cmd;
- hscb = scb->hscb;
- /*
- * Don't destroy valid residual information with
- * residual coming from a check sense operation.
- */
- if (((scb->hscb->control & DISCONNECTED) == 0) &&
- (scb->flags & SCB_SENSE) == 0)
- {
- /*
- * 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 = scb->sg_length;
- for (i=1; i < hscb->residual_SG_segment_count; i++)
- {
- actual -= scb->sg_list[scb->sg_count - i].length;
- }
- actual -= (hscb->residual_data_count[2] << 16) |
- (hscb->residual_data_count[1] << 8) |
- hscb->residual_data_count[0];
- if (actual < cmd->underflow)
- {
- if (aic7xxx_verbose & VERBOSE_MINOR_ERROR)
- {
- printk(INFO_LEAD "Underflow - Wanted %u, %s %u, residual SG "
- "count %d.n", p->host_no, CTL_OF_SCB(scb), cmd->underflow,
- (cmd->request.cmd == WRITE) ? "wrote" : "read", actual,
- hscb->residual_SG_segment_count);
- printk(INFO_LEAD "status 0x%x.n", p->host_no, CTL_OF_SCB(scb),
- hscb->target_status);
- }
- /*
- * In 2.4, only send back the residual information, don't flag this
- * as an error. Before 2.4 we had to flag this as an error because
- * the mid layer didn't check residual data counts to see if the
- * command needs retried.
- */
- #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,0)
- cmd->resid = scb->sg_length - actual;
- #else
- aic7xxx_error(cmd) = DID_RETRY_COMMAND;
- #endif
- aic7xxx_status(cmd) = hscb->target_status;
- }
- }
- /*
- * Clean out the residual information in the SCB for the
- * next consumer.
- */
- hscb->residual_data_count[2] = 0;
- hscb->residual_data_count[1] = 0;
- hscb->residual_data_count[0] = 0;
- hscb->residual_SG_segment_count = 0;
- }
- /*+F*************************************************************************
- * Function:
- * aic7xxx_handle_device_reset
- *
- * Description:
- * Interrupt handler for sequencer interrupts (SEQINT).
- *-F*************************************************************************/
- static void
- aic7xxx_handle_device_reset(struct aic7xxx_host *p, int target, int channel)
- {
- unsigned short targ_mask;
- unsigned char tindex = target;
- tindex |= ((channel & 0x01) << 3);
- targ_mask = (0x01 << tindex);
- /*
- * Go back to async/narrow transfers and renegotiate.
- */
- p->needppr |= (p->needppr_copy & targ_mask);
- p->needsdtr |= (p->needsdtr_copy & targ_mask);
- p->needwdtr |= (p->needwdtr_copy & targ_mask);
- p->dtr_pending &= ~targ_mask;
- aic_outb(p, 0, TARG_SCSIRATE + tindex);
- if (p->features & AHC_ULTRA2)
- aic_outb(p, 0, TARG_OFFSET + tindex);
- aic7xxx_reset_device(p, target, channel, ALL_LUNS, SCB_LIST_NULL);
- if (aic7xxx_verbose & VERBOSE_RESET_PROCESS)
- printk(INFO_LEAD "Bus Device Reset delivered.n", p->host_no, channel,
- target, -1);
- aic7xxx_run_done_queue(p, /*complete*/ TRUE);
- }
- /*+F*************************************************************************
- * Function:
- * aic7xxx_handle_seqint
- *
- * Description:
- * Interrupt handler for sequencer interrupts (SEQINT).
- *-F*************************************************************************/
- 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%xn",
- 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%xn", 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%xn", 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, continuingn", 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_INn", 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 %sn", p->host_no,
- CTL_OF_SCB(scb), (scb->flags & SCB_MSGOUT_SDTR) ?
- "SDTR" : "WDTR");
- else
- printk(INFO_LEAD "Requesting SENSE, no MSGn", 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 %dn", p->host_no, CTL_OF_SCB(scb),
- p->dev_max_queue_depth[tindex],
- p->dev_active_cmds[tindex]);
- else
- printk(INFO_LEAD "Target busyn", 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 %dn", 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 disabledn",
- 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 %dn",
- 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%lxn", 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%xn", 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%xn",
- 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%pn", 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%xn", 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%xn",
- 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);
- }
- /*+F*************************************************************************
- * Function:
- * aic7xxx_parse_msg
- *
- * Description:
- * Parses incoming messages into actions on behalf of
- * aic7xxx_handle_reqinit
- *_F*************************************************************************/
- static int
- aic7xxx_parse_msg(struct aic7xxx_host *p, struct aic7xxx_scb *scb)
- {
- int reject, reply, done;
- unsigned char target_scsirate, tindex;
- unsigned short target_mask;
- unsigned char target, channel, lun;
- unsigned char bus_width, new_bus_width;
- unsigned char trans_options, new_trans_options;
- unsigned int period, new_period, offset, new_offset, maxsync;
- struct aic7xxx_syncrate *syncrate;
- target = scb->cmd->target;
- channel = scb->cmd->channel;
- lun = scb->cmd->lun;
- reply = reject = done = FALSE;
- tindex = TARGET_INDEX(scb->cmd);
- target_scsirate = aic_inb(p, TARG_SCSIRATE + tindex);
- target_mask = (0x01 << tindex);
- /*
- * Parse as much of the message as is availible,
- * rejecting it if we don't support it. When
- * the entire message is availible and has been
- * handled, return TRUE indicating that we have
- * parsed an entire message.
- */
- if (p->msg_buf[0] != MSG_EXTENDED)
- {
- reject = TRUE;
- }
- /*
- * Even if we are an Ultra3 card, don't allow Ultra3 sync rates when
- * using the SDTR messages. We need the PPR messages to enable the
- * higher speeds that include things like Dual Edge clocking.
- */
- if (p->features & AHC_ULTRA2)
- {
- if ( (aic_inb(p, SBLKCTL) & ENAB40) &&
- !(aic_inb(p, SSTAT2) & EXP_ACTIVE) )
- {
- if (p->features & AHC_ULTRA3)
- maxsync = AHC_SYNCRATE_ULTRA3;
- else
- maxsync = AHC_SYNCRATE_ULTRA2;
- }
- else
- {
- maxsync = AHC_SYNCRATE_ULTRA;
- }
- }
- else if (p->features & AHC_ULTRA)
- {
- maxsync = AHC_SYNCRATE_ULTRA;
- }
- else
- {
- maxsync = AHC_SYNCRATE_FAST;
- }
- /*
- * Just accept the length byte outright and perform
- * more checking once we know the message type.
- */
- if ( !reject && (p->msg_len > 2) )
- {
- switch(p->msg_buf[2])
- {
- case MSG_EXT_SDTR:
- {
-
- if (p->msg_buf[1] != MSG_EXT_SDTR_LEN)
- {
- reject = TRUE;
- break;
- }
- if (p->msg_len < (MSG_EXT_SDTR_LEN + 2))
- {
- break;
- }
- period = new_period = p->msg_buf[3];
- offset = new_offset = p->msg_buf[4];
- trans_options = new_trans_options = 0;
- bus_width = new_bus_width = target_scsirate & WIDEXFER;
- /*
- * If our current max syncrate is in the Ultra3 range, bump it back
- * down to Ultra2 since we can't negotiate DT transfers using SDTR
- */
- if(maxsync == AHC_SYNCRATE_ULTRA3)
- maxsync = AHC_SYNCRATE_ULTRA2;
- /*
- * We might have a device that is starting negotiation with us
- * before we can start up negotiation with it....be prepared to
- * have a device ask for a higher speed then we want to give it
- * in that case
- */
- if ( (scb->flags & (SCB_MSGOUT_SENT|SCB_MSGOUT_SDTR)) !=
- (SCB_MSGOUT_SENT|SCB_MSGOUT_SDTR) )
- {
- if (!(p->dev_flags[tindex] & DEVICE_DTR_SCANNED))
- {
- /*
- * We shouldn't get here unless this is a narrow drive, wide
- * devices should trigger this same section of code in the WDTR
- * handler first instead.
- */
- p->transinfo[tindex].goal_width = MSG_EXT_WDTR_BUS_8_BIT;
- p->transinfo[tindex].goal_options = 0;
- if(p->transinfo[tindex].user_offset)
- {
- p->needsdtr_copy |= target_mask;
- p->transinfo[tindex].goal_period =
- MAX(10,p->transinfo[tindex].user_period);
- if(p->features & AHC_ULTRA2)
- {
- p->transinfo[tindex].goal_offset = MAX_OFFSET_ULTRA2;
- }
- else
- {
- p->transinfo[tindex].goal_offset = MAX_OFFSET_8BIT;
- }
- }
- else
- {
- p->needsdtr_copy &= ~target_mask;
- p->transinfo[tindex].goal_period = 255;
- p->transinfo[tindex].goal_offset = 0;
- }
- p->dev_flags[tindex] |= DEVICE_DTR_SCANNED | DEVICE_PRINT_DTR;
- }
- else if ((p->needsdtr_copy & target_mask) == 0)
- {
- /*
- * This is a preemptive message from the target, we've already
- * scanned this target and set our options for it, and we
- * don't need a WDTR with this target (for whatever reason),
- * so reject this incoming WDTR
- */
- reject = TRUE;
- break;
- }
- /* The device is sending this message first and we have to reply */
- reply = TRUE;
-
- if (aic7xxx_verbose & VERBOSE_NEGOTIATION2)
- {
- printk(INFO_LEAD "Received pre-emptive SDTR message from "
- "target.n", p->host_no, CTL_OF_SCB(scb));
- }
- /*
- * Validate the values the device passed to us against our SEEPROM
- * settings. We don't have to do this if we aren't replying since
- * the device isn't allowed to send values greater than the ones
- * we first sent to it.
- */
- new_period = MAX(period, p->transinfo[tindex].goal_period);
- new_offset = MIN(offset, p->transinfo[tindex].goal_offset);
- }
-
- /*
- * Use our new_period, new_offset, bus_width, and card options
- * to determine the actual syncrate settings
- */
- syncrate = aic7xxx_find_syncrate(p, &new_period, maxsync,
- &trans_options);
- aic7xxx_validate_offset(p, syncrate, &new_offset, bus_width);
- /*
- * Did we drop to async? If so, send a reply regardless of whether
- * or not we initiated this negotiation.
- */
- if ((new_offset == 0) && (new_offset != offset))
- {
- p->needsdtr_copy &= ~target_mask;
- reply = TRUE;
- }
-
- /*
- * Did we start this, if not, or if we went too low and had to
- * go async, then send an SDTR back to the target
- */
- if(reply)
- {
- /* when sending a reply, make sure that the goal settings are
- * updated along with current and active since the code that
- * will actually build the message for the sequencer uses the
- * goal settings as its guidelines.
- */
- aic7xxx_set_syncrate(p, syncrate, target, channel, new_period,
- new_offset, trans_options,
- AHC_TRANS_GOAL|AHC_TRANS_ACTIVE|AHC_TRANS_CUR);
- scb->flags &= ~SCB_MSGOUT_BITS;
- scb->flags |= SCB_MSGOUT_SDTR;
- aic_outb(p, HOST_MSG, MSG_OUT);
- aic_outb(p, aic_inb(p, SCSISIGO) | ATNO, SCSISIGO);
- }
- else
- {
- aic7xxx_set_syncrate(p, syncrate, target, channel, new_period,
- new_offset, trans_options,
- AHC_TRANS_ACTIVE|AHC_TRANS_CUR);
- p->needsdtr &= ~target_mask;
- }
- done = TRUE;
- break;
- }
- case MSG_EXT_WDTR:
- {
-
- if (p->msg_buf[1] != MSG_EXT_WDTR_LEN)
- {
- reject = TRUE;
- break;
- }
- if (p->msg_len < (MSG_EXT_WDTR_LEN + 2))
- {
- break;
- }
- bus_width = new_bus_width = p->msg_buf[3];
- if ( (scb->flags & (SCB_MSGOUT_SENT|SCB_MSGOUT_WDTR)) ==
- (SCB_MSGOUT_SENT|SCB_MSGOUT_WDTR) )
- {
- switch(bus_width)
- {
- default:
- {
- reject = TRUE;
- if ( (aic7xxx_verbose & VERBOSE_NEGOTIATION2) &&
- ((p->dev_flags[tindex] & DEVICE_PRINT_DTR) ||
- (aic7xxx_verbose > 0xffff)) )
- {
- printk(INFO_LEAD "Requesting %d bit transfers, rejecting.n",
- p->host_no, CTL_OF_SCB(scb), 8 * (0x01 << bus_width));
- }
- } /* We fall through on purpose */
- case MSG_EXT_WDTR_BUS_8_BIT:
- {
- p->transinfo[tindex].goal_width = MSG_EXT_WDTR_BUS_8_BIT;
- p->needwdtr_copy &= ~target_mask;
- break;
- }
- case MSG_EXT_WDTR_BUS_16_BIT:
- {
- break;
- }
- }
- p->needwdtr &= ~target_mask;
- aic7xxx_set_width(p, target, channel, lun, new_bus_width,
- AHC_TRANS_ACTIVE|AHC_TRANS_CUR);
- }
- else
- {
- if ( !(p->dev_flags[tindex] & DEVICE_DTR_SCANNED) )
- {
- /*
- * Well, we now know the WDTR and SYNC caps of this device since
- * it contacted us first, mark it as such and copy the user stuff
- * over to the goal stuff.
- */
- if( (p->features & AHC_WIDE) && p->transinfo[tindex].user_width )
- {
- p->transinfo[tindex].goal_width = MSG_EXT_WDTR_BUS_16_BIT;
- p->needwdtr_copy |= target_mask;
- }
-
- /*
- * Devices that support DT transfers don't start WDTR requests
- */
- p->transinfo[tindex].goal_options = 0;
- if(p->transinfo[tindex].user_offset)
- {
- p->needsdtr_copy |= target_mask;
- p->transinfo[tindex].goal_period =
- MAX(10,p->transinfo[tindex].user_period);
- if(p->features & AHC_ULTRA2)
- {
- p->transinfo[tindex].goal_offset = MAX_OFFSET_ULTRA2;
- }
- else if( p->transinfo[tindex].goal_width )
- {
- p->transinfo[tindex].goal_offset = MAX_OFFSET_16BIT;
- }
- else
- {
- p->transinfo[tindex].goal_offset = MAX_OFFSET_8BIT;
- }
- } else {
- p->needsdtr_copy &= ~target_mask;
- p->transinfo[tindex].goal_period = 255;
- p->transinfo[tindex].goal_offset = 0;
- }
-
- p->dev_flags[tindex] |= DEVICE_DTR_SCANNED | DEVICE_PRINT_DTR;
- }
- else if ((p->needwdtr_copy & target_mask) == 0)
- {
- /*
- * This is a preemptive message from the target, we've already
- * scanned this target and set our options for it, and we
- * don't need a WDTR with this target (for whatever reason),
- * so reject this incoming WDTR
- */
- reject = TRUE;
- break;
- }
- /* The device is sending this message first and we have to reply */
- reply = TRUE;
- if (aic7xxx_verbose & VERBOSE_NEGOTIATION2)
- {
- printk(INFO_LEAD "Received pre-emptive WDTR message from "
- "target.n", p->host_no, CTL_OF_SCB(scb));
- }
- switch(bus_width)
- {
- case MSG_EXT_WDTR_BUS_16_BIT:
- {
- if ( (p->features & AHC_WIDE) &&
- (p->transinfo[tindex].goal_width ==
- MSG_EXT_WDTR_BUS_16_BIT) )
- {
- new_bus_width = MSG_EXT_WDTR_BUS_16_BIT;
- break;
- }
- } /* Fall through if we aren't a wide card */
- default:
- case MSG_EXT_WDTR_BUS_8_BIT:
- {
- p->needwdtr_copy &= ~target_mask;
- new_bus_width = MSG_EXT_WDTR_BUS_8_BIT;
- break;
- }
- }
- scb->flags &= ~SCB_MSGOUT_BITS;
- scb->flags |= SCB_MSGOUT_WDTR;
- p->needwdtr &= ~target_mask;
- if((p->dtr_pending & target_mask) == 0)
- {
- /* there is no other command with SCB_DTR_SCB already set that will
- * trigger the release of the dtr_pending bit. Both set the bit
- * and set scb->flags |= SCB_DTR_SCB
- */
- p->dtr_pending |= target_mask;
- scb->flags |= SCB_DTR_SCB;
- }
- aic_outb(p, HOST_MSG, MSG_OUT);
- aic_outb(p, aic_inb(p, SCSISIGO) | ATNO, SCSISIGO);
- /* when sending a reply, make sure that the goal settings are
- * updated along with current and active since the code that
- * will actually build the message for the sequencer uses the
- * goal settings as its guidelines.
- */
- aic7xxx_set_width(p, target, channel, lun, new_bus_width,
- AHC_TRANS_GOAL|AHC_TRANS_ACTIVE|AHC_TRANS_CUR);
- }
-
- /*
- * By virtue of the SCSI spec, a WDTR message negates any existing
- * SDTR negotiations. So, even if needsdtr isn't marked for this
- * device, we still have to do a new SDTR message if the device
- * supports SDTR at all. Therefore, we check needsdtr_copy instead
- * of needstr.
- */
- aic7xxx_set_syncrate(p, NULL, target, channel, 0, 0, 0,
- AHC_TRANS_ACTIVE|AHC_TRANS_CUR|AHC_TRANS_QUITE);
- p->needsdtr |= (p->needsdtr_copy & target_mask);
- done = TRUE;
- break;
- }
- case MSG_EXT_PPR:
- {
-
- if (p->msg_buf[1] != MSG_EXT_PPR_LEN)
- {
- reject = TRUE;
- break;
- }
- if (p->msg_len < (MSG_EXT_PPR_LEN + 2))
- {
- break;
- }
- period = new_period = p->msg_buf[3];
- offset = new_offset = p->msg_buf[5];
- bus_width = new_bus_width = p->msg_buf[6];
- trans_options = new_trans_options = p->msg_buf[7] & 0xf;
- if(aic7xxx_verbose & VERBOSE_NEGOTIATION2)
- {
- printk(INFO_LEAD "Parsing PPR message (%d/%d/%d/%d)n",
- p->host_no, CTL_OF_SCB(scb), period, offset, bus_width,
- trans_options);
- }
- /*
- * We might have a device that is starting negotiation with us
- * before we can start up negotiation with it....be prepared to
- * have a device ask for a higher speed then we want to give it
- * in that case
- */
- if ( (scb->flags & (SCB_MSGOUT_SENT|SCB_MSGOUT_PPR)) !=
- (SCB_MSGOUT_SENT|SCB_MSGOUT_PPR) )
- {
- /* Have we scanned the device yet? */
- if (!(p->dev_flags[tindex] & DEVICE_DTR_SCANNED))
- {
- /* The device is electing to use PPR messages, so we will too until
- * we know better */
- p->needppr |= target_mask;
- p->needppr_copy |= target_mask;
- p->needsdtr &= ~target_mask;
- p->needsdtr_copy &= ~target_mask;
- p->needwdtr &= ~target_mask;
- p->needwdtr_copy &= ~target_mask;
-
- /* We know the device is SCSI-3 compliant due to PPR */
- p->dev_flags[tindex] |= DEVICE_SCSI_3;
-
- /*
- * Not only is the device starting this up, but it also hasn't
- * been scanned yet, so this would likely be our TUR or our
- * INQUIRY command at scan time, so we need to use the
- * settings from the SEEPROM if they existed. Of course, even
- * if we didn't find a SEEPROM, we stuffed default values into
- * the user settings anyway, so use those in all cases.
- */
- p->transinfo[tindex].goal_width =
- p->transinfo[tindex].user_width;
- if(p->transinfo[tindex].user_offset)
- {
- p->transinfo[tindex].goal_period =
- p->transinfo[tindex].user_period;
- p->transinfo[tindex].goal_options =
- p->transinfo[tindex].user_options;
- if(p->features & AHC_ULTRA2)
- {
- p->transinfo[tindex].goal_offset = MAX_OFFSET_ULTRA2;
- }
- else if( p->transinfo[tindex].goal_width &&
- (bus_width == MSG_EXT_WDTR_BUS_16_BIT) &&
- p->features & AHC_WIDE )
- {
- p->transinfo[tindex].goal_offset = MAX_OFFSET_16BIT;
- }
- else
- {
- p->transinfo[tindex].goal_offset = MAX_OFFSET_8BIT;
- }
- }
- else
- {
- p->transinfo[tindex].goal_period = 255;
- p->transinfo[tindex].goal_offset = 0;
- p->transinfo[tindex].goal_options = 0;
- }
- p->dev_flags[tindex] |= DEVICE_DTR_SCANNED | DEVICE_PRINT_DTR;
- }
- else if ((p->needppr_copy & target_mask) == 0)
- {
- /*
- * This is a preemptive message from the target, we've already
- * scanned this target and set our options for it, and we
- * don't need a PPR with this target (for whatever reason),
- * so reject this incoming PPR
- */
- reject = TRUE;
- break;
- }
- /* The device is sending this message first and we have to reply */
- reply = TRUE;
-
- if (aic7xxx_verbose & VERBOSE_NEGOTIATION2)
- {
- printk(INFO_LEAD "Received pre-emptive PPR message from "
- "target.n", p->host_no, CTL_OF_SCB(scb));
- }
- }
- switch(bus_width)
- {
- case MSG_EXT_WDTR_BUS_16_BIT:
- {
- if ( (p->transinfo[tindex].goal_width ==
- MSG_EXT_WDTR_BUS_16_BIT) && p->features & AHC_WIDE)
- {
- break;
- }
- }
- default:
- {
- if ( (aic7xxx_verbose & VERBOSE_NEGOTIATION2) &&
- ((p->dev_flags[tindex] & DEVICE_PRINT_DTR) ||
- (aic7xxx_verbose > 0xffff)) )
- {
- reply = TRUE;
- printk(INFO_LEAD "Requesting %d bit transfers, rejecting.n",
- p->host_no, CTL_OF_SCB(scb), 8 * (0x01 << bus_width));
- }
- } /* We fall through on purpose */
- case MSG_EXT_WDTR_BUS_8_BIT:
- {
- /*
- * According to the spec, if we aren't wide, we also can't be
- * Dual Edge so clear the options byte
- */
- new_trans_options = 0;
- new_bus_width = MSG_EXT_WDTR_BUS_8_BIT;
- break;
- }
- }
- if(reply)
- {
- /* when sending a reply, make sure that the goal settings are
- * updated along with current and active since the code that
- * will actually build the message for the sequencer uses the
- * goal settings as its guidelines.
- */
- aic7xxx_set_width(p, target, channel, lun, new_bus_width,
- AHC_TRANS_GOAL|AHC_TRANS_ACTIVE|AHC_TRANS_CUR);
- syncrate = aic7xxx_find_syncrate(p, &new_period, maxsync,
- &new_trans_options);
- aic7xxx_validate_offset(p, syncrate, &new_offset, new_bus_width);
- aic7xxx_set_syncrate(p, syncrate, target, channel, new_period,
- new_offset, new_trans_options,
- AHC_TRANS_GOAL|AHC_TRANS_ACTIVE|AHC_TRANS_CUR);
- }
- else
- {
- aic7xxx_set_width(p, target, channel, lun, new_bus_width,
- AHC_TRANS_ACTIVE|AHC_TRANS_CUR);
- syncrate = aic7xxx_find_syncrate(p, &new_period, maxsync,
- &new_trans_options);
- aic7xxx_validate_offset(p, syncrate, &new_offset, new_bus_width);
- aic7xxx_set_syncrate(p, syncrate, target, channel, new_period,
- new_offset, new_trans_options,
- AHC_TRANS_ACTIVE|AHC_TRANS_CUR);
- }
- /*
- * As it turns out, if we don't *have* to have PPR messages, then
- * configure ourselves not to use them since that makes some
- * external drive chassis work (those chassis can't parse PPR
- * messages and they mangle the SCSI bus until you send a WDTR
- * and SDTR that they can understand).
- */
- if(new_trans_options == 0)
- {
- p->needppr &= ~target_mask;
- p->needppr_copy &= ~target_mask;
- if(new_offset)
- {
- p->needsdtr |= target_mask;
- p->needsdtr_copy |= target_mask;
- }
- if (new_bus_width)
- {
- p->needwdtr |= target_mask;
- p->needwdtr_copy |= target_mask;
- }
- }