- for (; target_id < numtarg; target_id++) {
- struct ahd_devinfo devinfo;
- struct ahd_initiator_tinfo *tinfo;
- struct ahd_tmode_tstate *tstate;
-
- tinfo = ahd_fetch_transinfo(ahd, 'A', ahd->our_id,
- target_id, &tstate);
- ahd_compile_devinfo(&devinfo, ahd->our_id, target_id,
- CAM_LUN_WILDCARD, 'A', ROLE_INITIATOR);
- ahd_update_neg_request(ahd, &devinfo, tstate,
- tinfo, AHD_NEG_ALWAYS);
- }
- /* Give the bus some time to recover */
- if ((ahd->flags & AHD_RESET_BUS_A) != 0) {
- ahd_freeze_simq(ahd);
- init_timer(&ahd->platform_data->reset_timer);
- ahd->platform_data->reset_timer.data = (u_long)ahd;
- ahd->platform_data->reset_timer.expires =
- jiffies + (AIC79XX_RESET_DELAY * HZ)/1000;
- ahd->platform_data->reset_timer.function =
- (ahd_linux_callback_t *)ahd_release_simq;
- add_timer(&ahd->platform_data->reset_timer);
- }
-}
-
-int
-ahd_platform_alloc(struct ahd_softc *ahd, void *platform_arg)
-{
- ahd->platform_data =
- malloc(sizeof(struct ahd_platform_data), M_DEVBUF, M_NOWAIT);
- if (ahd->platform_data == NULL)
- return (ENOMEM);
- memset(ahd->platform_data, 0, sizeof(struct ahd_platform_data));
- TAILQ_INIT(&ahd->platform_data->completeq);
- TAILQ_INIT(&ahd->platform_data->device_runq);
- ahd->platform_data->irq = AHD_LINUX_NOIRQ;
- ahd->platform_data->hw_dma_mask = 0xFFFFFFFF;
- ahd_lockinit(ahd);
- ahd_done_lockinit(ahd);
- init_timer(&ahd->platform_data->completeq_timer);
- ahd->platform_data->completeq_timer.data = (u_long)ahd;
- ahd->platform_data->completeq_timer.function =
- (ahd_linux_callback_t *)ahd_linux_thread_run_complete_queue;
-#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,3,0)
- init_MUTEX_LOCKED(&ahd->platform_data->eh_sem);
- init_MUTEX_LOCKED(&ahd->platform_data->dv_sem);
- init_MUTEX_LOCKED(&ahd->platform_data->dv_cmd_sem);
-#else
- ahd->platform_data->eh_sem = MUTEX_LOCKED;
- ahd->platform_data->dv_sem = MUTEX_LOCKED;
- ahd->platform_data->dv_cmd_sem = MUTEX_LOCKED;
-#endif
- ahd_setup_runq_tasklet(ahd);
- ahd->seltime = (aic79xx_seltime & 0x3) << 4;
- return (0);
-}
-
-void
-ahd_platform_free(struct ahd_softc *ahd)
-{
- struct ahd_linux_target *targ;
- struct ahd_linux_device *dev;
- int i, j;
-
- if (ahd->platform_data != NULL) {
- del_timer_sync(&ahd->platform_data->completeq_timer);
- ahd_linux_kill_dv_thread(ahd);
- ahd_teardown_runq_tasklet(ahd);
- if (ahd->platform_data->host != NULL) {
-#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,5,0)
- scsi_remove_host(ahd->platform_data->host);
-#endif
- scsi_host_put(ahd->platform_data->host);
- }
-
- /* destroy all of the device and target objects */
- for (i = 0; i < AHD_NUM_TARGETS; i++) {
- targ = ahd->platform_data->targets[i];
- if (targ != NULL) {
- /* Keep target around through the loop. */
- targ->refcount++;
- for (j = 0; j < AHD_NUM_LUNS; j++) {
-
- if (targ->devices[j] == NULL)
- continue;
- dev = targ->devices[j];
- ahd_linux_free_device(ahd, dev);
- }
- /*
- * Forcibly free the target now that
- * all devices are gone.
- */
- ahd_linux_free_target(ahd, targ);
- }
- }
-
- if (ahd->platform_data->irq != AHD_LINUX_NOIRQ)
- free_irq(ahd->platform_data->irq, ahd);
- if (ahd->tags[0] == BUS_SPACE_PIO
- && ahd->bshs[0].ioport != 0)
- release_region(ahd->bshs[0].ioport, 256);
- if (ahd->tags[1] == BUS_SPACE_PIO
- && ahd->bshs[1].ioport != 0)
- release_region(ahd->bshs[1].ioport, 256);
- if (ahd->tags[0] == BUS_SPACE_MEMIO
- && ahd->bshs[0].maddr != NULL) {
- u_long base_addr;
-
- base_addr = (u_long)ahd->bshs[0].maddr;
- base_addr &= PAGE_MASK;
- iounmap((void *)base_addr);
-#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,0)
- release_mem_region(ahd->platform_data->mem_busaddr,
- 0x1000);
-#endif
- }
-#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,0) && \
- LINUX_VERSION_CODE < KERNEL_VERSION(2,5,0)
- /*
- * In 2.4 we detach from the scsi midlayer before the PCI
- * layer invokes our remove callback. No per-instance
- * detach is provided, so we must reach inside the PCI
- * subsystem's internals and detach our driver manually.
- */
- if (ahd->dev_softc != NULL)
- ahd->dev_softc->driver = NULL;
-#endif
- free(ahd->platform_data, M_DEVBUF);
- }
-}
-
-void
-ahd_platform_init(struct ahd_softc *ahd)
-{
- /*
- * Lookup and commit any modified IO Cell options.
- */
- if (ahd->unit < NUM_ELEMENTS(aic79xx_iocell_info)) {
- struct ahd_linux_iocell_opts *iocell_opts;
-
- iocell_opts = &aic79xx_iocell_info[ahd->unit];
- if (iocell_opts->precomp != AIC79XX_DEFAULT_PRECOMP)
- AHD_SET_PRECOMP(ahd, iocell_opts->precomp);
- if (iocell_opts->slewrate != AIC79XX_DEFAULT_SLEWRATE)
- AHD_SET_SLEWRATE(ahd, iocell_opts->slewrate);
- if (iocell_opts->amplitude != AIC79XX_DEFAULT_AMPLITUDE)
- AHD_SET_AMPLITUDE(ahd, iocell_opts->amplitude);
- }
-
-}
-
-void
-ahd_platform_freeze_devq(struct ahd_softc *ahd, struct scb *scb)
-{
- ahd_platform_abort_scbs(ahd, SCB_GET_TARGET(ahd, scb),
- SCB_GET_CHANNEL(ahd, scb),
- SCB_GET_LUN(scb), SCB_LIST_NULL,
- ROLE_UNKNOWN, CAM_REQUEUE_REQ);
-}
-
-void
-ahd_platform_set_tags(struct ahd_softc *ahd, struct ahd_devinfo *devinfo,
- ahd_queue_alg alg)
-{
- struct ahd_linux_device *dev;
- int was_queuing;
- int now_queuing;
-
- dev = ahd_linux_get_device(ahd, devinfo->channel - 'A',
- devinfo->target,
- devinfo->lun, /*alloc*/FALSE);
- if (dev == NULL)
- return;
- was_queuing = dev->flags & (AHD_DEV_Q_BASIC|AHD_DEV_Q_TAGGED);
- switch (alg) {
- default:
- case AHD_QUEUE_NONE:
- now_queuing = 0;
- break;
- case AHD_QUEUE_BASIC:
- now_queuing = AHD_DEV_Q_BASIC;
- break;
- case AHD_QUEUE_TAGGED:
- now_queuing = AHD_DEV_Q_TAGGED;
- break;
- }
- if ((dev->flags & AHD_DEV_FREEZE_TIL_EMPTY) == 0
- && (was_queuing != now_queuing)
- && (dev->active != 0)) {
- dev->flags |= AHD_DEV_FREEZE_TIL_EMPTY;
- dev->qfrozen++;
- }
-
- dev->flags &= ~(AHD_DEV_Q_BASIC|AHD_DEV_Q_TAGGED|AHD_DEV_PERIODIC_OTAG);
- if (now_queuing) {
- u_int usertags;
-
- usertags = ahd_linux_user_tagdepth(ahd, devinfo);
- if (!was_queuing) {
- /*
- * Start out agressively and allow our
- * dynamic queue depth algorithm to take
- * care of the rest.
- */
- dev->maxtags = usertags;
- dev->openings = dev->maxtags - dev->active;
- }
- if (dev->maxtags == 0) {
- /*
- * Queueing is disabled by the user.
- */
- dev->openings = 1;
- } else if (alg == AHD_QUEUE_TAGGED) {
- dev->flags |= AHD_DEV_Q_TAGGED;
- if (aic79xx_periodic_otag != 0)
- dev->flags |= AHD_DEV_PERIODIC_OTAG;
- } else
- dev->flags |= AHD_DEV_Q_BASIC;
- } else {
- /* We can only have one opening. */
- dev->maxtags = 0;
- dev->openings = 1 - dev->active;
- }
-#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,5,0)
- if (dev->scsi_device != NULL) {
- switch ((dev->flags & (AHD_DEV_Q_BASIC|AHD_DEV_Q_TAGGED))) {
- case AHD_DEV_Q_BASIC:
- scsi_adjust_queue_depth(dev->scsi_device,
- MSG_SIMPLE_TASK,
- dev->openings + dev->active);
- break;
- case AHD_DEV_Q_TAGGED:
- scsi_adjust_queue_depth(dev->scsi_device,
- MSG_ORDERED_TASK,
- dev->openings + dev->active);
- break;
- default:
- /*
- * We allow the OS to queue 2 untagged transactions to
- * us at any time even though we can only execute them
- * serially on the controller/device. This should
- * remove some latency.
- */
- scsi_adjust_queue_depth(dev->scsi_device,
- /*NON-TAGGED*/0,
- /*queue depth*/2);
- break;
- }
- }
-#endif
-}
-
-int
-ahd_platform_abort_scbs(struct ahd_softc *ahd, int target, char channel,
- int lun, u_int tag, role_t role, uint32_t status)
-{
- int targ;
- int maxtarg;
- int maxlun;
- int clun;
- int count;
-
- if (tag != SCB_LIST_NULL)
- return (0);
-
- targ = 0;
- if (target != CAM_TARGET_WILDCARD) {
- targ = target;
- maxtarg = targ + 1;
- } else {
- maxtarg = (ahd->features & AHD_WIDE) ? 16 : 8;
- }
- clun = 0;
- if (lun != CAM_LUN_WILDCARD) {
- clun = lun;
- maxlun = clun + 1;
- } else {
- maxlun = AHD_NUM_LUNS;
- }
-
- count = 0;
- for (; targ < maxtarg; targ++) {
-
- for (; clun < maxlun; clun++) {
- struct ahd_linux_device *dev;
- struct ahd_busyq *busyq;
- struct ahd_cmd *acmd;
-
- dev = ahd_linux_get_device(ahd, /*chan*/0, targ,
- clun, /*alloc*/FALSE);
- if (dev == NULL)
- continue;
-
- busyq = &dev->busyq;
- while ((acmd = TAILQ_FIRST(busyq)) != NULL) {
- Scsi_Cmnd *cmd;
-
- cmd = &acmd_scsi_cmd(acmd);
- TAILQ_REMOVE(busyq, acmd,
- acmd_links.tqe);
- count++;
- cmd->result = status << 16;
- ahd_linux_queue_cmd_complete(ahd, cmd);
- }
- }
- }
-
- return (count);
-}
-
-static void
-ahd_linux_thread_run_complete_queue(struct ahd_softc *ahd)
-{
- u_long flags;
-
- ahd_lock(ahd, &flags);
- del_timer(&ahd->platform_data->completeq_timer);
- ahd->platform_data->flags &= ~AHD_RUN_CMPLT_Q_TIMER;
- ahd_linux_run_complete_queue(ahd);
- ahd_unlock(ahd, &flags);
-}
-
-static void
-ahd_linux_start_dv(struct ahd_softc *ahd)
-{
-
- /*
- * Freeze the simq and signal ahd_linux_queue to not let any
- * more commands through
- */
- if ((ahd->platform_data->flags & AHD_DV_ACTIVE) == 0) {
-#ifdef AHD_DEBUG
- if (ahd_debug & AHD_SHOW_DV)
- printf("%s: Starting DV\n", ahd_name(ahd));
-#endif
-
- ahd->platform_data->flags |= AHD_DV_ACTIVE;
- ahd_freeze_simq(ahd);
-
- /* Wake up the DV kthread */
- up(&ahd->platform_data->dv_sem);
- }
-}
-
-static int
-ahd_linux_dv_thread(void *data)
-{
- struct ahd_softc *ahd;
- int target;
- u_long s;
-
- ahd = (struct ahd_softc *)data;
-
-#ifdef AHD_DEBUG
- if (ahd_debug & AHD_SHOW_DV)
- printf("In DV Thread\n");
-#endif
-
- /*
- * Complete thread creation.
- */
- lock_kernel();
-#if LINUX_VERSION_CODE < KERNEL_VERSION(2,5,60)
- /*
- * Don't care about any signals.
- */
- siginitsetinv(¤t->blocked, 0);
-
- daemonize();
- sprintf(current->comm, "ahd_dv_%d", ahd->unit);
-#else
- daemonize("ahd_dv_%d", ahd->unit);
- current->flags |= PF_FREEZE;
-#endif
- unlock_kernel();
-
- while (1) {
- /*
- * Use down_interruptible() rather than down() to
- * avoid inclusion in the load average.
- */
- down_interruptible(&ahd->platform_data->dv_sem);
-
- /* Check to see if we've been signaled to exit */
- ahd_lock(ahd, &s);
- if ((ahd->platform_data->flags & AHD_DV_SHUTDOWN) != 0) {
- ahd_unlock(ahd, &s);
- break;
- }
- ahd_unlock(ahd, &s);
-
-#ifdef AHD_DEBUG
- if (ahd_debug & AHD_SHOW_DV)
- printf("%s: Beginning Domain Validation\n",
- ahd_name(ahd));
-#endif
-
- /*
- * Wait for any pending commands to drain before proceeding.
- */
- ahd_lock(ahd, &s);
- while (LIST_FIRST(&ahd->pending_scbs) != NULL) {
- ahd->platform_data->flags |= AHD_DV_WAIT_SIMQ_EMPTY;
- ahd_unlock(ahd, &s);
- down_interruptible(&ahd->platform_data->dv_sem);
- ahd_lock(ahd, &s);
- }
-
- /*
- * Wait for the SIMQ to be released so that DV is the
- * only reason the queue is frozen.
- */
- while (AHD_DV_SIMQ_FROZEN(ahd) == 0) {
- ahd->platform_data->flags |= AHD_DV_WAIT_SIMQ_RELEASE;
- ahd_unlock(ahd, &s);
- down_interruptible(&ahd->platform_data->dv_sem);
- ahd_lock(ahd, &s);
- }
- ahd_unlock(ahd, &s);
-
- for (target = 0; target < AHD_NUM_TARGETS; target++)
- ahd_linux_dv_target(ahd, target);
-
- ahd_lock(ahd, &s);
- ahd->platform_data->flags &= ~AHD_DV_ACTIVE;
- ahd_unlock(ahd, &s);
-
- /*
- * Release the SIMQ so that normal commands are
- * allowed to continue on the bus.
- */
- ahd_release_simq(ahd);
- }
- up(&ahd->platform_data->eh_sem);
- return (0);
-}
-
-static void
-ahd_linux_kill_dv_thread(struct ahd_softc *ahd)
-{
- u_long s;
-
- ahd_lock(ahd, &s);
- if (ahd->platform_data->dv_pid != 0) {
- ahd->platform_data->flags |= AHD_DV_SHUTDOWN;
- ahd_unlock(ahd, &s);
- up(&ahd->platform_data->dv_sem);
-
- /*
- * Use the eh_sem as an indicator that the
- * dv thread is exiting. Note that the dv
- * thread must still return after performing
- * the up on our semaphore before it has
- * completely exited this module. Unfortunately,
- * there seems to be no easy way to wait for the
- * exit of a thread for which you are not the
- * parent (dv threads are parented by init).
- * Cross your fingers...
- */
- down(&ahd->platform_data->eh_sem);
-
- /*
- * Mark the dv thread as already dead. This
- * avoids attempting to kill it a second time.
- * This is necessary because we must kill the
- * DV thread before calling ahd_free() in the
- * module shutdown case to avoid bogus locking
- * in the SCSI mid-layer, but we ahd_free() is
- * called without killing the DV thread in the
- * instance detach case, so ahd_platform_free()
- * calls us again to verify that the DV thread
- * is dead.
- */
- ahd->platform_data->dv_pid = 0;
- } else {
- ahd_unlock(ahd, &s);
- }
-}
-
-#define AHD_LINUX_DV_INQ_SHORT_LEN 36
-#define AHD_LINUX_DV_INQ_LEN 256
-#define AHD_LINUX_DV_TIMEOUT (HZ / 4)
-
-#define AHD_SET_DV_STATE(ahd, targ, newstate) \
- ahd_set_dv_state(ahd, targ, newstate, __LINE__)
-
-static __inline void
-ahd_set_dv_state(struct ahd_softc *ahd, struct ahd_linux_target *targ,
- ahd_dv_state newstate, u_int line)
-{
- ahd_dv_state oldstate;
-
- oldstate = targ->dv_state;
-#ifdef AHD_DEBUG
- if (ahd_debug & AHD_SHOW_DV)
- printf("%s:%d: Going from state %d to state %d\n",
- ahd_name(ahd), line, oldstate, newstate);
-#endif
-
- if (oldstate == newstate)
- targ->dv_state_retry++;
- else
- targ->dv_state_retry = 0;
- targ->dv_state = newstate;
-}
-
-static void
-ahd_linux_dv_target(struct ahd_softc *ahd, u_int target_offset)
-{
- struct ahd_devinfo devinfo;
- struct ahd_linux_target *targ;
- struct scsi_cmnd *cmd;
- struct scsi_device *scsi_dev;
- struct scsi_sense_data *sense;
- uint8_t *buffer;
- u_long s;
- u_int timeout;
- int echo_size;
-
- sense = NULL;
- buffer = NULL;
- echo_size = 0;
- ahd_lock(ahd, &s);
- targ = ahd->platform_data->targets[target_offset];
- if (targ == NULL || (targ->flags & AHD_DV_REQUIRED) == 0) {
- ahd_unlock(ahd, &s);
- return;
- }
- ahd_compile_devinfo(&devinfo, ahd->our_id, targ->target, /*lun*/0,
- targ->channel + 'A', ROLE_INITIATOR);
-#ifdef AHD_DEBUG
- if (ahd_debug & AHD_SHOW_DV) {
- ahd_print_devinfo(ahd, &devinfo);
- printf("Performing DV\n");
- }
-#endif
-
- ahd_unlock(ahd, &s);
-
- cmd = malloc(sizeof(struct scsi_cmnd), M_DEVBUF, M_WAITOK);
- scsi_dev = malloc(sizeof(struct scsi_device), M_DEVBUF, M_WAITOK);
- scsi_dev->host = ahd->platform_data->host;
- scsi_dev->id = devinfo.target;
- scsi_dev->lun = devinfo.lun;
- scsi_dev->channel = devinfo.channel - 'A';
- ahd->platform_data->dv_scsi_dev = scsi_dev;
-
- AHD_SET_DV_STATE(ahd, targ, AHD_DV_STATE_INQ_SHORT_ASYNC);
-
- while (targ->dv_state != AHD_DV_STATE_EXIT) {
- timeout = AHD_LINUX_DV_TIMEOUT;
- switch (targ->dv_state) {
- case AHD_DV_STATE_INQ_SHORT_ASYNC:
- case AHD_DV_STATE_INQ_ASYNC:
- case AHD_DV_STATE_INQ_ASYNC_VERIFY:
- /*
- * Set things to async narrow to reduce the
- * chance that the INQ will fail.
- */
- ahd_lock(ahd, &s);
- ahd_set_syncrate(ahd, &devinfo, 0, 0, 0,
- AHD_TRANS_GOAL, /*paused*/FALSE);
- ahd_set_width(ahd, &devinfo, MSG_EXT_WDTR_BUS_8_BIT,
- AHD_TRANS_GOAL, /*paused*/FALSE);
- ahd_unlock(ahd, &s);
- timeout = 10 * HZ;
- targ->flags &= ~AHD_INQ_VALID;
- /* FALLTHROUGH */
- case AHD_DV_STATE_INQ_VERIFY:
- {
- u_int inq_len;
-
- if (targ->dv_state == AHD_DV_STATE_INQ_SHORT_ASYNC)
- inq_len = AHD_LINUX_DV_INQ_SHORT_LEN;
- else
- inq_len = targ->inq_data->additional_length + 5;
- ahd_linux_dv_inq(ahd, cmd, &devinfo, targ, inq_len);
- break;
- }
- case AHD_DV_STATE_TUR:
- case AHD_DV_STATE_BUSY:
- timeout = 5 * HZ;
- ahd_linux_dv_tur(ahd, cmd, &devinfo);
- break;
- case AHD_DV_STATE_REBD:
- ahd_linux_dv_rebd(ahd, cmd, &devinfo, targ);
- break;
- case AHD_DV_STATE_WEB:
- ahd_linux_dv_web(ahd, cmd, &devinfo, targ);
- break;
-
- case AHD_DV_STATE_REB:
- ahd_linux_dv_reb(ahd, cmd, &devinfo, targ);
- break;
-
- case AHD_DV_STATE_SU:
- ahd_linux_dv_su(ahd, cmd, &devinfo, targ);
- timeout = 50 * HZ;
- break;
-
- default:
- ahd_print_devinfo(ahd, &devinfo);
- printf("Unknown DV state %d\n", targ->dv_state);
- goto out;
- }
-
- /* Queue the command and wait for it to complete */
- /* Abuse eh_timeout in the scsi_cmnd struct for our purposes */
- init_timer(&cmd->eh_timeout);
-#ifdef AHD_DEBUG
- if ((ahd_debug & AHD_SHOW_MESSAGES) != 0)
- /*
- * All of the printfs during negotiation
- * really slow down the negotiation.
- * Add a bit of time just to be safe.
- */
- timeout += HZ;
-#endif
- scsi_add_timer(cmd, timeout, ahd_linux_dv_timeout);
- /*
- * In 2.5.X, it is assumed that all calls from the
- * "midlayer" (which we are emulating) will have the
- * ahd host lock held. For other kernels, the
- * io_request_lock must be held.
- */
-#if AHD_SCSI_HAS_HOST_LOCK != 0
- ahd_lock(ahd, &s);
-#else
- spin_lock_irqsave(&io_request_lock, s);
-#endif
- ahd_linux_queue(cmd, ahd_linux_dv_complete);
-#if AHD_SCSI_HAS_HOST_LOCK != 0
- ahd_unlock(ahd, &s);
-#else
- spin_unlock_irqrestore(&io_request_lock, s);
-#endif
- down_interruptible(&ahd->platform_data->dv_cmd_sem);
- /*
- * Wait for the SIMQ to be released so that DV is the
- * only reason the queue is frozen.
- */
- ahd_lock(ahd, &s);
- while (AHD_DV_SIMQ_FROZEN(ahd) == 0) {
- ahd->platform_data->flags |= AHD_DV_WAIT_SIMQ_RELEASE;
- ahd_unlock(ahd, &s);
- down_interruptible(&ahd->platform_data->dv_sem);
- ahd_lock(ahd, &s);
- }
- ahd_unlock(ahd, &s);
-
- ahd_linux_dv_transition(ahd, cmd, &devinfo, targ);
- }
-
-out:
- if ((targ->flags & AHD_INQ_VALID) != 0
- && ahd_linux_get_device(ahd, devinfo.channel - 'A',
- devinfo.target, devinfo.lun,
- /*alloc*/FALSE) == NULL) {
- /*
- * The DV state machine failed to configure this device.
- * This is normal if DV is disabled. Since we have inquiry
- * data, filter it and use the "optimistic" negotiation
- * parameters found in the inquiry string.
- */
- ahd_linux_filter_inquiry(ahd, &devinfo);
- if ((targ->flags & (AHD_BASIC_DV|AHD_ENHANCED_DV)) != 0) {
- ahd_print_devinfo(ahd, &devinfo);
- printf("DV failed to configure device. "
- "Please file a bug report against "
- "this driver.\n");
- }
- }
-
- if (cmd != NULL)
- free(cmd, M_DEVBUF);
-
- if (ahd->platform_data->dv_scsi_dev != NULL) {
- free(ahd->platform_data->dv_scsi_dev, M_DEVBUF);
- ahd->platform_data->dv_scsi_dev = NULL;
- }
-
- ahd_lock(ahd, &s);
- if (targ->dv_buffer != NULL) {
- free(targ->dv_buffer, M_DEVBUF);
- targ->dv_buffer = NULL;
- }
- if (targ->dv_buffer1 != NULL) {
- free(targ->dv_buffer1, M_DEVBUF);
- targ->dv_buffer1 = NULL;
- }
- targ->flags &= ~AHD_DV_REQUIRED;
- if (targ->refcount == 0)
- ahd_linux_free_target(ahd, targ);
- ahd_unlock(ahd, &s);
-}
-
-static __inline int
-ahd_linux_dv_fallback(struct ahd_softc *ahd, struct ahd_devinfo *devinfo)
-{
- u_long s;
- int retval;
-
- ahd_lock(ahd, &s);
- retval = ahd_linux_fallback(ahd, devinfo);
- ahd_unlock(ahd, &s);
-
- return (retval);
-}
-
-static void
-ahd_linux_dv_transition(struct ahd_softc *ahd, struct scsi_cmnd *cmd,
- struct ahd_devinfo *devinfo,
- struct ahd_linux_target *targ)
-{
- u_int32_t status;
-
- status = aic_error_action(cmd, targ->inq_data,
- ahd_cmd_get_transaction_status(cmd),
- ahd_cmd_get_scsi_status(cmd));
-
-
-#ifdef AHD_DEBUG
- if (ahd_debug & AHD_SHOW_DV) {
- ahd_print_devinfo(ahd, devinfo);
- printf("Entering ahd_linux_dv_transition, state= %d, "
- "status= 0x%x, cmd->result= 0x%x\n", targ->dv_state,
- status, cmd->result);
- }
-#endif
-
- switch (targ->dv_state) {
- case AHD_DV_STATE_INQ_SHORT_ASYNC:
- case AHD_DV_STATE_INQ_ASYNC:
- switch (status & SS_MASK) {
- case SS_NOP:
- {
- AHD_SET_DV_STATE(ahd, targ, targ->dv_state+1);
- break;
- }
- case SS_INQ_REFRESH:
- AHD_SET_DV_STATE(ahd, targ,
- AHD_DV_STATE_INQ_SHORT_ASYNC);
- break;
- case SS_TUR:
- case SS_RETRY:
- AHD_SET_DV_STATE(ahd, targ, targ->dv_state);
- if (ahd_cmd_get_transaction_status(cmd)
- == CAM_REQUEUE_REQ)
- targ->dv_state_retry--;
- if ((status & SS_ERRMASK) == EBUSY)
- AHD_SET_DV_STATE(ahd, targ, AHD_DV_STATE_BUSY);
- if (targ->dv_state_retry < 10)
- break;
- /* FALLTHROUGH */
- default:
- AHD_SET_DV_STATE(ahd, targ, AHD_DV_STATE_EXIT);
-#ifdef AHD_DEBUG
- if (ahd_debug & AHD_SHOW_DV) {
- ahd_print_devinfo(ahd, devinfo);
- printf("Failed DV inquiry, skipping\n");
- }
-#endif
- break;
- }
- break;
- case AHD_DV_STATE_INQ_ASYNC_VERIFY:
- switch (status & SS_MASK) {
- case SS_NOP:
- {
- u_int xportflags;
- u_int spi3data;
-
- if (memcmp(targ->inq_data, targ->dv_buffer,
- AHD_LINUX_DV_INQ_LEN) != 0) {
- /*
- * Inquiry data must have changed.
- * Try from the top again.
- */
- AHD_SET_DV_STATE(ahd, targ,
- AHD_DV_STATE_INQ_SHORT_ASYNC);
- break;
- }
-
- AHD_SET_DV_STATE(ahd, targ, targ->dv_state+1);
- targ->flags |= AHD_INQ_VALID;
- if (ahd_linux_user_dv_setting(ahd) == 0)
- break;
-
- xportflags = targ->inq_data->flags;
- if ((xportflags & (SID_Sync|SID_WBus16)) == 0)
- break;
-
- spi3data = targ->inq_data->spi3data;
- switch (spi3data & SID_SPI_CLOCK_DT_ST) {
- default:
- case SID_SPI_CLOCK_ST:
- /* Assume only basic DV is supported. */
- targ->flags |= AHD_BASIC_DV;
- break;
- case SID_SPI_CLOCK_DT:
- case SID_SPI_CLOCK_DT_ST:
- targ->flags |= AHD_ENHANCED_DV;
- break;
- }
- break;
- }
- case SS_INQ_REFRESH:
- AHD_SET_DV_STATE(ahd, targ,
- AHD_DV_STATE_INQ_SHORT_ASYNC);
- break;
- case SS_TUR:
- case SS_RETRY:
- AHD_SET_DV_STATE(ahd, targ, targ->dv_state);
- if (ahd_cmd_get_transaction_status(cmd)
- == CAM_REQUEUE_REQ)
- targ->dv_state_retry--;
-
- if ((status & SS_ERRMASK) == EBUSY)
- AHD_SET_DV_STATE(ahd, targ, AHD_DV_STATE_BUSY);
- if (targ->dv_state_retry < 10)
- break;
- /* FALLTHROUGH */
- default:
- AHD_SET_DV_STATE(ahd, targ, AHD_DV_STATE_EXIT);
-#ifdef AHD_DEBUG
- if (ahd_debug & AHD_SHOW_DV) {
- ahd_print_devinfo(ahd, devinfo);
- printf("Failed DV inquiry, skipping\n");
- }
-#endif
- break;
- }
- break;
- case AHD_DV_STATE_INQ_VERIFY:
- switch (status & SS_MASK) {
- case SS_NOP:
- {
-
- if (memcmp(targ->inq_data, targ->dv_buffer,
- AHD_LINUX_DV_INQ_LEN) == 0) {
- AHD_SET_DV_STATE(ahd, targ, AHD_DV_STATE_EXIT);
- break;
- }
-
-#ifdef AHD_DEBUG
- if (ahd_debug & AHD_SHOW_DV) {
- int i;
-
- ahd_print_devinfo(ahd, devinfo);
- printf("Inquiry buffer mismatch:");
- for (i = 0; i < AHD_LINUX_DV_INQ_LEN; i++) {
- if ((i & 0xF) == 0)
- printf("\n ");
- printf("0x%x:0x0%x ",
- ((uint8_t *)targ->inq_data)[i],
- targ->dv_buffer[i]);
- }
- printf("\n");
- }
-#endif
-
- if (ahd_linux_dv_fallback(ahd, devinfo) != 0) {
- AHD_SET_DV_STATE(ahd, targ, AHD_DV_STATE_EXIT);
- break;
- }
- /*
- * Do not count "falling back"
- * against our retries.
- */
- targ->dv_state_retry = 0;
- AHD_SET_DV_STATE(ahd, targ, targ->dv_state);
- break;
- }
- case SS_INQ_REFRESH:
- AHD_SET_DV_STATE(ahd, targ,
- AHD_DV_STATE_INQ_SHORT_ASYNC);
- break;
- case SS_TUR:
- case SS_RETRY:
- AHD_SET_DV_STATE(ahd, targ, targ->dv_state);
- if (ahd_cmd_get_transaction_status(cmd)
- == CAM_REQUEUE_REQ) {
- targ->dv_state_retry--;
- } else if ((status & SSQ_FALLBACK) != 0) {
- if (ahd_linux_dv_fallback(ahd, devinfo) != 0) {
- AHD_SET_DV_STATE(ahd, targ,
- AHD_DV_STATE_EXIT);
- break;
- }
- /*
- * Do not count "falling back"
- * against our retries.
- */
- targ->dv_state_retry = 0;
- } else if ((status & SS_ERRMASK) == EBUSY)
- AHD_SET_DV_STATE(ahd, targ, AHD_DV_STATE_BUSY);
- if (targ->dv_state_retry < 10)
- break;
- /* FALLTHROUGH */
- default:
- AHD_SET_DV_STATE(ahd, targ, AHD_DV_STATE_EXIT);
-#ifdef AHD_DEBUG
- if (ahd_debug & AHD_SHOW_DV) {
- ahd_print_devinfo(ahd, devinfo);
- printf("Failed DV inquiry, skipping\n");
- }
-#endif
- break;
- }
- break;
-
- case AHD_DV_STATE_TUR:
- switch (status & SS_MASK) {
- case SS_NOP:
- if ((targ->flags & AHD_BASIC_DV) != 0) {
- ahd_linux_filter_inquiry(ahd, devinfo);
- AHD_SET_DV_STATE(ahd, targ,
- AHD_DV_STATE_INQ_VERIFY);
- } else if ((targ->flags & AHD_ENHANCED_DV) != 0) {
- AHD_SET_DV_STATE(ahd, targ, AHD_DV_STATE_REBD);