summaryrefslogtreecommitdiffstats
path: root/drivers/scsi/aic7xxx
diff options
context:
space:
mode:
author <jejb@titanic.il.steeleye.com>2005-04-17 18:03:20 -0500
committerJames Bottomley <jejb@titanic>2005-04-18 13:52:27 -0500
commitcb624029cab62e5415287d15b2ec907b8f322ff5 (patch)
tree9ab51220f3ad2a63632b69c5d3662f2c9a771a09 /drivers/scsi/aic7xxx
parent92d161c373b45be158b73a162bedebb5293a73d3 (diff)
aic7xxx: convert to SPI transport class Domain Validation
Now that we export all the parameters, this is easy to do. It also means that we can dump about 2000 lines of code that were dedicated to doing this internally. Additionally, this removes all the aic7xxx driver abuse of SCSI timers which were embedded in the DV routines. Signed-off-by: James Bottomley <James.Bottomley@SteelEye.com>
Diffstat (limited to 'drivers/scsi/aic7xxx')
-rw-r--r--drivers/scsi/aic7xxx/aic7xxx_osm.c1681
-rw-r--r--drivers/scsi/aic7xxx/aic7xxx_osm.h40
2 files changed, 10 insertions, 1711 deletions
diff --git a/drivers/scsi/aic7xxx/aic7xxx_osm.c b/drivers/scsi/aic7xxx/aic7xxx_osm.c
index 6b6ee0a52a46..d74b99dab7ec 100644
--- a/drivers/scsi/aic7xxx/aic7xxx_osm.c
+++ b/drivers/scsi/aic7xxx/aic7xxx_osm.c
@@ -275,39 +275,6 @@ static adapter_tag_info_t aic7xxx_tag_info[] =
};
/*
- * DV option:
- *
- * positive value = DV Enabled
- * zero = DV Disabled
- * negative value = DV Default for adapter type/seeprom
- */
-#ifdef CONFIG_AIC7XXX_DV_SETTING
-#define AIC7XXX_CONFIGED_DV CONFIG_AIC7XXX_DV_SETTING
-#else
-#define AIC7XXX_CONFIGED_DV -1
-#endif
-
-static int8_t aic7xxx_dv_settings[] =
-{
- AIC7XXX_CONFIGED_DV,
- AIC7XXX_CONFIGED_DV,
- AIC7XXX_CONFIGED_DV,
- AIC7XXX_CONFIGED_DV,
- AIC7XXX_CONFIGED_DV,
- AIC7XXX_CONFIGED_DV,
- AIC7XXX_CONFIGED_DV,
- AIC7XXX_CONFIGED_DV,
- AIC7XXX_CONFIGED_DV,
- AIC7XXX_CONFIGED_DV,
- AIC7XXX_CONFIGED_DV,
- AIC7XXX_CONFIGED_DV,
- AIC7XXX_CONFIGED_DV,
- AIC7XXX_CONFIGED_DV,
- AIC7XXX_CONFIGED_DV,
- AIC7XXX_CONFIGED_DV
-};
-
-/*
* There should be a specific return value for this in scsi.h, but
* it seems that most drivers ignore it.
*/
@@ -454,7 +421,6 @@ MODULE_PARM_DESC(aic7xxx,
" tag_info:<tag_str> Set per-target tag depth\n"
" global_tag_depth:<int> Global tag depth for every target\n"
" on every bus\n"
-" dv:<dv_settings> Set per-controller Domain Validation Setting.\n"
" seltime:<int> Selection Timeout\n"
" (0/256ms,1/128ms,2/64ms,3/32ms)\n"
"\n"
@@ -471,7 +437,6 @@ static void ahc_linux_handle_scsi_status(struct ahc_softc *,
struct scb *);
static void ahc_linux_queue_cmd_complete(struct ahc_softc *ahc,
Scsi_Cmnd *cmd);
-static void ahc_linux_filter_inquiry(struct ahc_softc*, struct ahc_devinfo*);
static void ahc_linux_sem_timeout(u_long arg);
static void ahc_linux_freeze_simq(struct ahc_softc *ahc);
static void ahc_linux_release_simq(u_long arg);
@@ -480,49 +445,8 @@ static int ahc_linux_queue_recovery_cmd(Scsi_Cmnd *cmd, scb_flag flag);
static void ahc_linux_initialize_scsi_bus(struct ahc_softc *ahc);
static void ahc_linux_size_nseg(void);
static void ahc_linux_thread_run_complete_queue(struct ahc_softc *ahc);
-static void ahc_linux_start_dv(struct ahc_softc *ahc);
-static void ahc_linux_dv_timeout(struct scsi_cmnd *cmd);
-static int ahc_linux_dv_thread(void *data);
-static void ahc_linux_kill_dv_thread(struct ahc_softc *ahc);
-static void ahc_linux_dv_target(struct ahc_softc *ahc, u_int target);
-static void ahc_linux_dv_transition(struct ahc_softc *ahc,
- struct scsi_cmnd *cmd,
- struct ahc_devinfo *devinfo,
- struct ahc_linux_target *targ);
-static void ahc_linux_dv_fill_cmd(struct ahc_softc *ahc,
- struct scsi_cmnd *cmd,
- struct ahc_devinfo *devinfo);
-static void ahc_linux_dv_inq(struct ahc_softc *ahc,
- struct scsi_cmnd *cmd,
- struct ahc_devinfo *devinfo,
- struct ahc_linux_target *targ,
- u_int request_length);
-static void ahc_linux_dv_tur(struct ahc_softc *ahc,
- struct scsi_cmnd *cmd,
- struct ahc_devinfo *devinfo);
-static void ahc_linux_dv_rebd(struct ahc_softc *ahc,
- struct scsi_cmnd *cmd,
- struct ahc_devinfo *devinfo,
- struct ahc_linux_target *targ);
-static void ahc_linux_dv_web(struct ahc_softc *ahc,
- struct scsi_cmnd *cmd,
- struct ahc_devinfo *devinfo,
- struct ahc_linux_target *targ);
-static void ahc_linux_dv_reb(struct ahc_softc *ahc,
- struct scsi_cmnd *cmd,
- struct ahc_devinfo *devinfo,
- struct ahc_linux_target *targ);
-static void ahc_linux_dv_su(struct ahc_softc *ahc,
- struct scsi_cmnd *cmd,
- struct ahc_devinfo *devinfo,
- struct ahc_linux_target *targ);
-static int ahc_linux_fallback(struct ahc_softc *ahc,
- struct ahc_devinfo *devinfo);
-static void ahc_linux_dv_complete(Scsi_Cmnd *cmd);
-static void ahc_linux_generate_dv_pattern(struct ahc_linux_target *targ);
static u_int ahc_linux_user_tagdepth(struct ahc_softc *ahc,
struct ahc_devinfo *devinfo);
-static u_int ahc_linux_user_dv_setting(struct ahc_softc *ahc);
static void ahc_linux_device_queue_depth(struct ahc_softc *ahc,
struct ahc_linux_device *dev);
static struct ahc_linux_target* ahc_linux_alloc_target(struct ahc_softc*,
@@ -538,7 +462,6 @@ static void ahc_linux_run_device_queue(struct ahc_softc*,
struct ahc_linux_device*);
static void ahc_linux_setup_tag_info_global(char *p);
static aic_option_callback_t ahc_linux_setup_tag_info;
-static aic_option_callback_t ahc_linux_setup_dv;
static int aic7xxx_setup(char *s);
static int ahc_linux_next_unit(void);
static void ahc_runq_tasklet(unsigned long data);
@@ -667,8 +590,7 @@ ahc_linux_next_device_to_run(struct ahc_softc *ahc)
{
if ((ahc->flags & AHC_RESOURCE_SHORTAGE) != 0
- || (ahc->platform_data->qfrozen != 0
- && AHC_DV_SIMQ_FROZEN(ahc) == 0))
+ || (ahc->platform_data->qfrozen != 0))
return (NULL);
return (TAILQ_FIRST(&ahc->platform_data->device_runq));
}
@@ -966,8 +888,7 @@ ahc_linux_queue(Scsi_Cmnd * cmd, void (*scsi_done) (Scsi_Cmnd *))
* DV commands through so long as we are only frozen to
* perform DV.
*/
- if (ahc->platform_data->qfrozen != 0
- && AHC_DV_CMD(cmd) == 0) {
+ if (ahc->platform_data->qfrozen != 0) {
ahc_cmd_set_transaction_status(cmd, CAM_REQUEUE_REQ);
ahc_linux_queue_cmd_complete(ahc, cmd);
@@ -1034,6 +955,11 @@ ahc_linux_slave_configure(Scsi_Device *device)
ahc_linux_device_queue_depth(ahc, dev);
}
ahc_midlayer_entrypoint_unlock(ahc, &flags);
+
+ /* Initial Domain Validation */
+ if (!spi_initial_dv(device->sdev_target))
+ spi_dv_device(device);
+
return (0);
}
@@ -1549,18 +1475,6 @@ ahc_linux_setup_tag_info(u_long arg, int instance, int targ, int32_t value)
}
}
-static void
-ahc_linux_setup_dv(u_long arg, int instance, int targ, int32_t value)
-{
-
- if ((instance >= 0)
- && (instance < NUM_ELEMENTS(aic7xxx_dv_settings))) {
- aic7xxx_dv_settings[instance] = value;
- if (bootverbose)
- printf("dv[%d] = %d\n", instance, value);
- }
-}
-
/*
* Handle Linux boot parameters. This routine allows for assigning a value
* to a parameter with a ':' between the parameter and the value.
@@ -1620,9 +1534,6 @@ aic7xxx_setup(char *s)
} else if (strncmp(p, "tag_info", n) == 0) {
s = aic_parse_brace_option("tag_info", p + n, end,
2, ahc_linux_setup_tag_info, 0);
- } else if (strncmp(p, "dv", n) == 0) {
- s = aic_parse_brace_option("dv", p + n, end, 1,
- ahc_linux_setup_dv, 0);
} else if (p[n] == ':') {
*(options[i].flag) = simple_strtoul(p + n + 1, NULL, 0);
} else if (strncmp(p, "verbose", n) == 0) {
@@ -1645,7 +1556,6 @@ ahc_linux_register_host(struct ahc_softc *ahc, Scsi_Host_Template *template)
struct Scsi_Host *host;
char *new_name;
u_long s;
- u_int targ_offset;
template->name = ahc->description;
host = scsi_host_alloc(template, sizeof(struct ahc_softc *));
@@ -1681,55 +1591,7 @@ ahc_linux_register_host(struct ahc_softc *ahc, Scsi_Host_Template *template)
scsi_set_pci_device(host, ahc->dev_softc);
#endif
ahc_linux_initialize_scsi_bus(ahc);
- ahc_unlock(ahc, &s);
- ahc->platform_data->dv_pid = kernel_thread(ahc_linux_dv_thread, ahc, 0);
- ahc_lock(ahc, &s);
- if (ahc->platform_data->dv_pid < 0) {
- printf("%s: Failed to create DV thread, error= %d\n",
- ahc_name(ahc), ahc->platform_data->dv_pid);
- return (-ahc->platform_data->dv_pid);
- }
- /*
- * Initially allocate *all* of our linux target objects
- * so that the DV thread will scan them all in parallel
- * just after driver initialization. Any device that
- * does not exist will have its target object destroyed
- * by the selection timeout handler. In the case of a
- * device that appears after the initial DV scan, async
- * negotiation will occur for the first command, and DV
- * will comence should that first command be successful.
- */
- for (targ_offset = 0;
- targ_offset < host->max_id * (host->max_channel + 1);
- targ_offset++) {
- u_int channel;
- u_int target;
-
- channel = 0;
- target = targ_offset;
- if (target > 7
- && (ahc->features & AHC_TWIN) != 0) {
- channel = 1;
- target &= 0x7;
- }
- /*
- * Skip our own ID. Some Compaq/HP storage devices
- * have enclosure management devices that respond to
- * single bit selection (i.e. selecting ourselves).
- * It is expected that either an external application
- * or a modified kernel will be used to probe this
- * ID if it is appropriate. To accommodate these
- * installations, ahc_linux_alloc_target() will allocate
- * for our ID if asked to do so.
- */
- if ((channel == 0 && target == ahc->our_id)
- || (channel == 1 && target == ahc->our_id_b))
- continue;
-
- ahc_linux_alloc_target(ahc, channel, target);
- }
ahc_intr_enable(ahc, TRUE);
- ahc_linux_start_dv(ahc);
ahc_unlock(ahc, &s);
host->transportt = ahc_linux_transport_template;
@@ -1866,8 +1728,6 @@ ahc_platform_alloc(struct ahc_softc *ahc, void *platform_arg)
ahc->platform_data->completeq_timer.function =
(ahc_linux_callback_t *)ahc_linux_thread_run_complete_queue;
init_MUTEX_LOCKED(&ahc->platform_data->eh_sem);
- init_MUTEX_LOCKED(&ahc->platform_data->dv_sem);
- init_MUTEX_LOCKED(&ahc->platform_data->dv_cmd_sem);
tasklet_init(&ahc->platform_data->runq_tasklet, ahc_runq_tasklet,
(unsigned long)ahc);
ahc->seltime = (aic7xxx_seltime & 0x3) << 4;
@@ -1887,7 +1747,6 @@ ahc_platform_free(struct ahc_softc *ahc)
if (ahc->platform_data != NULL) {
del_timer_sync(&ahc->platform_data->completeq_timer);
- ahc_linux_kill_dv_thread(ahc);
tasklet_kill(&ahc->platform_data->runq_tasklet);
if (ahc->platform_data->host != NULL) {
#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,5,0)
@@ -2126,1331 +1985,6 @@ ahc_linux_thread_run_complete_queue(struct ahc_softc *ahc)
ahc_unlock(ahc, &flags);
}
-static void
-ahc_linux_start_dv(struct ahc_softc *ahc)
-{
-
- /*
- * Freeze the simq and signal ahc_linux_queue to not let any
- * more commands through.
- */
- if ((ahc->platform_data->flags & AHC_DV_ACTIVE) == 0) {
-#ifdef AHC_DEBUG
- if (ahc_debug & AHC_SHOW_DV)
- printf("%s: Waking DV thread\n", ahc_name(ahc));
-#endif
-
- ahc->platform_data->flags |= AHC_DV_ACTIVE;
- ahc_linux_freeze_simq(ahc);
-
- /* Wake up the DV kthread */
- up(&ahc->platform_data->dv_sem);
- }
-}
-
-static void
-ahc_linux_kill_dv_thread(struct ahc_softc *ahc)
-{
- u_long s;
-
- ahc_lock(ahc, &s);
- if (ahc->platform_data->dv_pid != 0) {
- ahc->platform_data->flags |= AHC_DV_SHUTDOWN;
- ahc_unlock(ahc, &s);
- up(&ahc->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(&ahc->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 ahc_free() in the
- * module shutdown case to avoid bogus locking
- * in the SCSI mid-layer, but we ahc_free() is
- * called without killing the DV thread in the
- * instance detach case, so ahc_platform_free()
- * calls us again to verify that the DV thread
- * is dead.
- */
- ahc->platform_data->dv_pid = 0;
- } else {
- ahc_unlock(ahc, &s);
- }
-}
-
-static int
-ahc_linux_dv_thread(void *data)
-{
- struct ahc_softc *ahc;
- int target;
- u_long s;
-
- ahc = (struct ahc_softc *)data;
-
-#ifdef AHC_DEBUG
- if (ahc_debug & AHC_SHOW_DV)
- printf("Launching DV Thread\n");
-#endif
-
- /*
- * Complete thread creation.
- */
- lock_kernel();
-#if LINUX_VERSION_CODE < KERNEL_VERSION(2,5,0)
- /*
- * Don't care about any signals.
- */
- siginitsetinv(&current->blocked, 0);
-
- daemonize();
- sprintf(current->comm, "ahc_dv_%d", ahc->unit);
-#else
- daemonize("ahc_dv_%d", ahc->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(&ahc->platform_data->dv_sem);
-
- /* Check to see if we've been signaled to exit */
- ahc_lock(ahc, &s);
- if ((ahc->platform_data->flags & AHC_DV_SHUTDOWN) != 0) {
- ahc_unlock(ahc, &s);
- break;
- }
- ahc_unlock(ahc, &s);
-
-#ifdef AHC_DEBUG
- if (ahc_debug & AHC_SHOW_DV)
- printf("%s: Beginning Domain Validation\n",
- ahc_name(ahc));
-#endif
-
- /*
- * Wait for any pending commands to drain before proceeding.
- */
- ahc_lock(ahc, &s);
- while (LIST_FIRST(&ahc->pending_scbs) != NULL) {
- ahc->platform_data->flags |= AHC_DV_WAIT_SIMQ_EMPTY;
- ahc_unlock(ahc, &s);
- down_interruptible(&ahc->platform_data->dv_sem);
- ahc_lock(ahc, &s);
- }
-
- /*
- * Wait for the SIMQ to be released so that DV is the
- * only reason the queue is frozen.
- */
- while (AHC_DV_SIMQ_FROZEN(ahc) == 0) {
- ahc->platform_data->flags |= AHC_DV_WAIT_SIMQ_RELEASE;
- ahc_unlock(ahc, &s);
- down_interruptible(&ahc->platform_data->dv_sem);
- ahc_lock(ahc, &s);
- }
- ahc_unlock(ahc, &s);
-
- for (target = 0; target < AHC_NUM_TARGETS; target++)
- ahc_linux_dv_target(ahc, target);
-
- ahc_lock(ahc, &s);
- ahc->platform_data->flags &= ~AHC_DV_ACTIVE;
- ahc_unlock(ahc, &s);
-
- /*
- * Release the SIMQ so that normal commands are
- * allowed to continue on the bus.
- */
- ahc_linux_release_simq((u_long)ahc);
- }
- up(&ahc->platform_data->eh_sem);
- return (0);
-}
-
-#define AHC_LINUX_DV_INQ_SHORT_LEN 36
-#define AHC_LINUX_DV_INQ_LEN 256
-#define AHC_LINUX_DV_TIMEOUT (HZ / 4)
-
-#define AHC_SET_DV_STATE(ahc, targ, newstate) \
- ahc_set_dv_state(ahc, targ, newstate, __LINE__)
-
-static __inline void
-ahc_set_dv_state(struct ahc_softc *ahc, struct ahc_linux_target *targ,
- ahc_dv_state newstate, u_int line)
-{
- ahc_dv_state oldstate;
-
- oldstate = targ->dv_state;
-#ifdef AHC_DEBUG
- if (ahc_debug & AHC_SHOW_DV)
- printf("%s:%d: Going from state %d to state %d\n",
- ahc_name(ahc), line, oldstate, newstate);
-#endif
-
- if (oldstate == newstate)
- targ->dv_state_retry++;
- else
- targ->dv_state_retry = 0;
- targ->dv_state = newstate;
-}
-
-static void
-ahc_linux_dv_target(struct ahc_softc *ahc, u_int target_offset)
-{
- struct ahc_devinfo devinfo;
- struct ahc_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;
- ahc_lock(ahc, &s);
- targ = ahc->platform_data->targets[target_offset];
- if (targ == NULL || (targ->flags & AHC_DV_REQUIRED) == 0) {
- ahc_unlock(ahc, &s);
- return;
- }
- ahc_compile_devinfo(&devinfo,
- targ->channel == 0 ? ahc->our_id : ahc->our_id_b,
- targ->target, /*lun*/0, targ->channel + 'A',
- ROLE_INITIATOR);
-#ifdef AHC_DEBUG
- if (ahc_debug & AHC_SHOW_DV) {
- ahc_print_devinfo(ahc, &devinfo);
- printf("Performing DV\n");
- }
-#endif
-
- ahc_unlock(ahc, &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 = ahc->platform_data->host;
- scsi_dev->id = devinfo.target;
- scsi_dev->lun = devinfo.lun;
- scsi_dev->channel = devinfo.channel - 'A';
- ahc->platform_data->dv_scsi_dev = scsi_dev;
-
- AHC_SET_DV_STATE(ahc, targ, AHC_DV_STATE_INQ_SHORT_ASYNC);
-
- while (targ->dv_state != AHC_DV_STATE_EXIT) {
- timeout = AHC_LINUX_DV_TIMEOUT;
- switch (targ->dv_state) {
- case AHC_DV_STATE_INQ_SHORT_ASYNC:
- case AHC_DV_STATE_INQ_ASYNC:
- case AHC_DV_STATE_INQ_ASYNC_VERIFY:
- /*
- * Set things to async narrow to reduce the
- * chance that the INQ will fail.
- */
- ahc_lock(ahc, &s);
- ahc_set_syncrate(ahc, &devinfo, NULL, 0, 0, 0,
- AHC_TRANS_GOAL, /*paused*/FALSE);
- ahc_set_width(ahc, &devinfo, MSG_EXT_WDTR_BUS_8_BIT,
- AHC_TRANS_GOAL, /*paused*/FALSE);
- ahc_unlock(ahc, &s);
- timeout = 10 * HZ;
- targ->flags &= ~AHC_INQ_VALID;
- /* FALLTHROUGH */
- case AHC_DV_STATE_INQ_VERIFY:
- {
- u_int inq_len;
-
- if (targ->dv_state == AHC_DV_STATE_INQ_SHORT_ASYNC)
- inq_len = AHC_LINUX_DV_INQ_SHORT_LEN;
- else
- inq_len = targ->inq_data->additional_length + 5;
- ahc_linux_dv_inq(ahc, cmd, &devinfo, targ, inq_len);
- break;
- }
- case AHC_DV_STATE_TUR:
- case AHC_DV_STATE_BUSY:
- timeout = 5 * HZ;
- ahc_linux_dv_tur(ahc, cmd, &devinfo);
- break;
- case AHC_DV_STATE_REBD:
- ahc_linux_dv_rebd(ahc, cmd, &devinfo, targ);
- break;
- case AHC_DV_STATE_WEB:
- ahc_linux_dv_web(ahc, cmd, &devinfo, targ);
- break;
-
- case AHC_DV_STATE_REB:
- ahc_linux_dv_reb(ahc, cmd, &devinfo, targ);
- break;
-
- case AHC_DV_STATE_SU:
- ahc_linux_dv_su(ahc, cmd, &devinfo, targ);
- timeout = 50 * HZ;
- break;
-
- default:
- ahc_print_devinfo(ahc, &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 AHC_DEBUG
- if ((ahc_debug & AHC_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, ahc_linux_dv_timeout);
- /*
- * In 2.5.X, it is assumed that all calls from the
- * "midlayer" (which we are emulating) will have the
- * ahc host lock held. For other kernels, the
- * io_request_lock must be held.
- */
-#if AHC_SCSI_HAS_HOST_LOCK != 0
- ahc_lock(ahc, &s);
-#else
- spin_lock_irqsave(&io_request_lock, s);
-#endif
- ahc_linux_queue(cmd, ahc_linux_dv_complete);
-#if AHC_SCSI_HAS_HOST_LOCK != 0
- ahc_unlock(ahc, &s);
-#else
- spin_unlock_irqrestore(&io_request_lock, s);
-#endif
- down_interruptible(&ahc->platform_data->dv_cmd_sem);
- /*
- * Wait for the SIMQ to be released so that DV is the
- * only reason the queue is frozen.
- */
- ahc_lock(ahc, &s);
- while (AHC_DV_SIMQ_FROZEN(ahc) == 0) {
- ahc->platform_data->flags |= AHC_DV_WAIT_SIMQ_RELEASE;
- ahc_unlock(ahc, &s);
- down_interruptible(&ahc->platform_data->dv_sem);
- ahc_lock(ahc, &s);
- }
- ahc_unlock(ahc, &s);
-
- ahc_linux_dv_transition(ahc, cmd, &devinfo, targ);
- }
-
-out:
- if ((targ->flags & AHC_INQ_VALID) != 0
- && ahc_linux_get_device(ahc, 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.
- */
- ahc_linux_filter_inquiry(ahc, &devinfo);
- if ((targ->flags & (AHC_BASIC_DV|AHC_ENHANCED_DV)) != 0) {
- ahc_print_devinfo(ahc, &devinfo);
- printf("DV failed to configure device. "
- "Please file a bug report against "
- "this driver.\n");
- }
- }
-
- if (cmd != NULL)
- free(cmd, M_DEVBUF);
-
- if (ahc->platform_data->dv_scsi_dev != NULL) {
- free(ahc->platform_data->dv_scsi_dev, M_DEVBUF);
- ahc->platform_data->dv_scsi_dev = NULL;
- }
-
- ahc_lock(ahc, &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 &= ~AHC_DV_REQUIRED;
- if (targ->refcount == 0)
- ahc_linux_free_target(ahc, targ);
- ahc_unlock(ahc, &s);
-}
-
-static void
-ahc_linux_dv_transition(struct ahc_softc *ahc, struct scsi_cmnd *cmd,
- struct ahc_devinfo *devinfo,
- struct ahc_linux_target *targ)
-{
- u_int32_t status;
-
- status = aic_error_action(cmd, targ->inq_data,
- ahc_cmd_get_transaction_status(cmd),
- ahc_cmd_get_scsi_status(cmd));
-
-#ifdef AHC_DEBUG
- if (ahc_debug & AHC_SHOW_DV) {
- ahc_print_devinfo(ahc, devinfo);
- printf("Entering ahc_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 AHC_DV_STATE_INQ_SHORT_ASYNC:
- case AHC_DV_STATE_INQ_ASYNC:
- switch (status & SS_MASK) {
- case SS_NOP:
- {
- AHC_SET_DV_STATE(ahc, targ, targ->dv_state+1);
- break;
- }
- case SS_INQ_REFRESH:
- AHC_SET_DV_STATE(ahc, targ,
- AHC_DV_STATE_INQ_SHORT_ASYNC);
- break;
- case SS_TUR:
- case SS_RETRY:
- AHC_SET_DV_STATE(ahc, targ, targ->dv_state);
- if (ahc_cmd_get_transaction_status(cmd)
- == CAM_REQUEUE_REQ)
- targ->dv_state_retry--;
- if ((status & SS_ERRMASK) == EBUSY)
- AHC_SET_DV_STATE(ahc, targ, AHC_DV_STATE_BUSY);
- if (targ->dv_state_retry < 10)
- break;
- /* FALLTHROUGH */
- default:
- AHC_SET_DV_STATE(ahc, targ, AHC_DV_STATE_EXIT);
-#ifdef AHC_DEBUG
- if (ahc_debug & AHC_SHOW_DV) {
- ahc_print_devinfo(ahc, devinfo);
- printf("Failed DV inquiry, skipping\n");
- }
-#endif
- break;
- }
- break;
- case AHC_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,
- AHC_LINUX_DV_INQ_LEN) != 0) {
- /*
- * Inquiry data must have changed.
- * Try from the top again.
- */
- AHC_SET_DV_STATE(ahc, targ,
- AHC_DV_STATE_INQ_SHORT_ASYNC);
- break;
- }
-
- AHC_SET_DV_STATE(ahc, targ, targ->dv_state+1);
- targ->flags |= AHC_INQ_VALID;
- if (ahc_linux_user_dv_setting(ahc) == 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 |= AHC_BASIC_DV;
- break;
- case SID_SPI_CLOCK_DT:
- case SID_SPI_CLOCK_DT_ST:
- targ->flags |= AHC_ENHANCED_DV;
- break;
- }
- break;
- }
- case SS_INQ_REFRESH:
- AHC_SET_DV_STATE(ahc, targ,
- AHC_DV_STATE_INQ_SHORT_ASYNC);
- break;
- case SS_TUR:
- case SS_RETRY:
- AHC_SET_DV_STATE(ahc, targ, targ->dv_state);
- if (ahc_cmd_get_transaction_status(cmd)
- == CAM_REQUEUE_REQ)
- targ->dv_state_retry--;
-
- if ((status & SS_ERRMASK) == EBUSY)
- AHC_SET_DV_STATE(ahc, targ, AHC_DV_STATE_BUSY);
- if (targ->dv_state_retry < 10)
- break;
- /* FALLTHROUGH */
- default:
- AHC_SET_DV_STATE(ahc, targ, AHC_DV_STATE_EXIT);
-#ifdef AHC_DEBUG
- if (ahc_debug & AHC_SHOW_DV) {
- ahc_print_devinfo(ahc, devinfo);
- printf("Failed DV inquiry, skipping\n");
- }
-#endif
- break;
- }
- break;
- case AHC_DV_STATE_INQ_VERIFY:
- switch (status & SS_MASK) {
- case SS_NOP:
- {
-
- if (memcmp(targ->inq_data, targ->dv_buffer,
- AHC_LINUX_DV_INQ_LEN) == 0) {
- AHC_SET_DV_STATE(ahc, targ, AHC_DV_STATE_EXIT);
- break;
- }
-#ifdef AHC_DEBUG
- if (ahc_debug & AHC_SHOW_DV) {
- int i;
-
- ahc_print_devinfo(ahc, devinfo);
- printf("Inquiry buffer mismatch:");
- for (i = 0; i < AHC_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 (ahc_linux_fallback(ahc, devinfo) != 0) {
- AHC_SET_DV_STATE(ahc, targ, AHC_DV_STATE_EXIT);
- break;
- }
- /*
- * Do not count "falling back"
- * against our retries.
- */
- targ->dv_state_retry = 0;
- AHC_SET_DV_STATE(ahc, targ, targ->dv_state);
- break;
- }
- case SS_INQ_REFRESH:
- AHC_SET_DV_STATE(ahc, targ,
- AHC_DV_STATE_INQ_SHORT_ASYNC);
- break;
- case SS_TUR:
- case SS_RETRY:
- AHC_SET_DV_STATE(ahc, targ, targ->dv_state);
- if (ahc_cmd_get_transaction_status(cmd)
- == CAM_REQUEUE_REQ) {
- targ->dv_state_retry--;
- } else if ((status & SSQ_FALLBACK) != 0) {
- if (ahc_linux_fallback(ahc, devinfo) != 0) {
- AHC_SET_DV_STATE(ahc, targ,
- AHC_DV_STATE_EXIT);
- break;
- }
- /*
- * Do not count "falling back"
- * against our retries.
- */
- targ->dv_state_retry = 0;
- } else if ((status & SS_ERRMASK) == EBUSY)
- AHC_SET_DV_STATE(ahc, targ, AHC_DV_STATE_BUSY);
- if (targ->dv_state_retry < 10)
- break;
- /* FALLTHROUGH */
- default:
- AHC_SET_DV_STATE(ahc, targ, AHC_DV_STATE_EXIT);
-#ifdef AHC_DEBUG
- if (ahc_debug & AHC_SHOW_DV) {
- ahc_print_devinfo(ahc, devinfo);
- printf("Failed DV inquiry, skipping\n");
- }
-#endif
- break;
- }
- break;
-
- case AHC_DV_STATE_TUR:
- switch (status & SS_MASK) {
- case SS_NOP:
- if ((targ->flags & AHC_BASIC_DV) != 0) {
- ahc_linux_filter_inquiry(ahc, devinfo);
- AHC_SET_DV_STATE(ahc, targ,
- AHC_DV_STATE_INQ_VERIFY);
- } else if ((targ->flags & AHC_ENHANCED_DV) != 0) {
- AHC_SET_DV_STATE(ahc, targ, AHC_DV_STATE_REBD);
- } else {
- AHC_SET_DV_STATE(ahc, targ, AHC_DV_STATE_EXIT);
- }
- break;
- case SS_RETRY:
- case SS_TUR:
- if ((status & SS_ERRMASK) == EBUSY) {
- AHC_SET_DV_STATE(ahc, targ, AHC_DV_STATE_BUSY);
- break;
- }
- AHC_SET_DV_STATE(ahc, targ, targ->dv_state);
- if (ahc_cmd_get_transaction_status(cmd)
- == CAM_REQUEUE_REQ) {
- targ->dv_state_retry--;
- } else if ((status & SSQ_FALLBACK) != 0) {
- if (ahc_linux_fallback(ahc, devinfo) != 0) {
- AHC_SET_DV_STATE(ahc, targ,
- AHC_DV_STATE_EXIT);
- break;
- }
- /*
- * Do not count "falling back"
- * against our retries.
- */
- targ->dv_state_retry = 0;
- }
- if (targ->dv_state_retry >= 10) {
-#ifdef AHC_DEBUG
- if (ahc_debug & AHC_SHOW_DV) {
- ahc_print_devinfo(ahc, devinfo);
- printf("DV TUR reties exhausted\n");
- }
-#endif
- AHC_SET_DV_STATE(ahc, targ, AHC_DV_STATE_EXIT);
- break;
- }
- if (status & SSQ_DELAY)
- ssleep(1);
-
- break;
- case SS_START:
- AHC_SET_DV_STATE(ahc, targ, AHC_DV_STATE_SU);
- break;
- case SS_INQ_REFRESH:
- AHC_SET_DV_STATE(ahc, targ,
- AHC_DV_STATE_INQ_SHORT_ASYNC);
- break;
- default:
- AHC_SET_DV_STATE(ahc, targ, AHC_DV_STATE_EXIT);
- break;
- }
- break;
-
- case AHC_DV_STATE_REBD:
- switch (status & SS_MASK) {
- case SS_NOP:
- {
- uint32_t echo_size;
-
- AHC_SET_DV_STATE(ahc, targ, AHC_DV_STATE_WEB);
- echo_size = scsi_3btoul(&targ->dv_buffer[1]);
- echo_size &= 0x1FFF;
-#ifdef AHC_DEBUG
- if (ahc_debug & AHC_SHOW_DV) {
- ahc_print_devinfo(ahc, devinfo);
- printf("Echo buffer size= %d\n", echo_size);
- }
-#endif
- if (echo_size == 0) {
- AHC_SET_DV_STATE(ahc, targ, AHC_DV_STATE_EXIT);
- break;
- }
-
- /* Generate the buffer pattern */
- targ->dv_echo_size = echo_size;
- ahc_linux_generate_dv_pattern(targ);
- /*
- * Setup initial negotiation values.
- */
- ahc_linux_filter_inquiry(ahc, devinfo);
- break;
- }
- case SS_INQ_REFRESH:
- AHC_SET_DV_STATE(ahc, targ,
- AHC_DV_STATE_INQ_SHORT_ASYNC);
- break;
- case SS_RETRY:
- AHC_SET_DV_STATE(ahc, targ, targ->dv_state);
- if (ahc_cmd_get_transaction_status(cmd)
- == CAM_REQUEUE_REQ)
- targ->dv_state_retry--;
- if (targ->dv_state_retry <= 10)
- break;
-#ifdef AHC_DEBUG
- if (ahc_debug & AHC_SHOW_DV) {
- ahc_print_devinfo(ahc, devinfo);
- printf("DV REBD reties exhausted\n");
- }
-#endif
- /* FALLTHROUGH */
- case SS_FATAL:
- default:
- /*
- * Setup initial negotiation values
- * and try level 1 DV.
- */
- ahc_linux_filter_inquiry(ahc, devinfo);
- AHC_SET_DV_STATE(ahc, targ, AHC_DV_STATE_INQ_VERIFY);
- targ->dv_echo_size = 0;
- break;
- }
- break;
-
- case AHC_DV_STATE_WEB:
- switch (status & SS_MASK) {
- case SS_NOP:
- AHC_SET_DV_STATE(ahc, targ, AHC_DV_STATE_REB);
- break;
- case SS_INQ_REFRESH:
- AHC_SET_DV_STATE(ahc, targ,
- AHC_DV_STATE_INQ_SHORT_ASYNC);
- break;
- case SS_RETRY:
- AHC_SET_DV_STATE(ahc, targ, targ->dv_state);
- if (ahc_cmd_get_transaction_status(cmd)
- == CAM_REQUEUE_REQ) {
- targ->dv_state_retry--;
- } else if ((status & SSQ_FALLBACK) != 0) {
- if (ahc_linux_fallback(ahc, devinfo) != 0) {
- AHC_SET_DV_STATE(ahc, targ,
- AHC_DV_STATE_EXIT);
- break;
- }
- /*
- * Do not count "falling back"
- * against our retries.
- */
- targ->dv_state_retry = 0;
- }
- if (targ->dv_state_retry <= 10)
- break;
- /* FALLTHROUGH */
-#ifdef AHC_DEBUG
- if (ahc_debug & AHC_SHOW_DV) {
- ahc_print_devinfo(ahc, devinfo);
- printf("DV WEB reties exhausted\n");
- }
-#endif
- default:
- AHC_SET_DV_STATE(ahc, targ, AHC_DV_STATE_EXIT);
- break;
- }
- break;
-
- case AHC_DV_STATE_REB:
- switch (status & SS_MASK) {
- case SS_NOP:
- if (memcmp(targ->dv_buffer, targ->dv_buffer1,
- targ->dv_echo_size) != 0) {
- if (ahc_linux_fallback(ahc, devinfo) != 0)
- AHC_SET_DV_STATE(ahc, targ,
- AHC_DV_STATE_EXIT);
- else
- AHC_SET_DV_STATE(ahc, targ,
- AHC_DV_STATE_WEB);
- break;
- }
-
- 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;
- }
- AHC_SET_DV_STATE(ahc, targ, AHC_DV_STATE_EXIT);
- break;
- case SS_INQ_REFRESH:
- AHC_SET_DV_STATE(ahc, targ,
- AHC_DV_STATE_INQ_SHORT_ASYNC);
- break;
- case SS_RETRY:
- AHC_SET_DV_STATE(ahc, targ, targ->dv_state);
- if (ahc_cmd_get_transaction_status(cmd)
- == CAM_REQUEUE_REQ) {
- targ->dv_state_retry--;
- } else if ((status & SSQ_FALLBACK) != 0) {
- if (ahc_linux_fallback(ahc, devinfo) != 0) {
- AHC_SET_DV_STATE(ahc, targ,
- AHC_DV_STATE_EXIT);
- break;
- }
- AHC_SET_DV_STATE(ahc, targ, AHC_DV_STATE_WEB);
- }
- if (targ->dv_state_retry <= 10) {
- if ((status & (SSQ_DELAY_RANDOM|SSQ_DELAY))!= 0)
- msleep(ahc->our_id*1000/10);
- break;
- }
-#ifdef AHC_DEBUG
- if (ahc_debug & AHC_SHOW_DV) {
- ahc_print_devinfo(ahc, devinfo);
- printf("DV REB reties exhausted\n");
- }
-#endif
- /* FALLTHROUGH */
- default:
- AHC_SET_DV_STATE(ahc, targ, AHC_DV_STATE_EXIT);
- break;
- }
- break;
-
- case AHC_DV_STATE_SU:
- switch (status & SS_MASK) {
- case SS_NOP:
- case SS_INQ_REFRESH:
- AHC_SET_DV_STATE(ahc, targ,
- AHC_DV_STATE_INQ_SHORT_ASYNC);
- break;
- default:
- AHC_SET_DV_STATE(ahc, targ, AHC_DV_STATE_EXIT);
- break;
- }
- break;
-
- case AHC_DV_STATE_BUSY:
- switch (status & SS_MASK) {
- case SS_NOP:
- case SS_INQ_REFRESH:
- AHC_SET_DV_STATE(ahc, targ,
- AHC_DV_STATE_INQ_SHORT_ASYNC);
- break;
- case SS_TUR:
- case SS_RETRY:
- AHC_SET_DV_STATE(ahc, targ, targ->dv_state);
- if (ahc_cmd_get_transaction_status(cmd)
- == CAM_REQUEUE_REQ) {
- targ->dv_state_retry--;
- } else if (targ->dv_state_retry < 60) {
- if ((status & SSQ_DELAY) != 0)
- ssleep(1);
- } else {
-#ifdef AHC_DEBUG
- if (ahc_debug & AHC_SHOW_DV) {
- ahc_print_devinfo(ahc, devinfo);
- printf("DV BUSY reties exhausted\n");
- }
-#endif
- AHC_SET_DV_STATE(ahc, targ, AHC_DV_STATE_EXIT);
- }
- break;
- default:
- AHC_SET_DV_STATE(ahc, targ, AHC_DV_STATE_EXIT);
- break;
- }
- break;
-
- default:
- printf("%s: Invalid DV completion state %d\n", ahc_name(ahc),
- targ->dv_state);
- AHC_SET_DV_STATE(ahc, targ, AHC_DV_STATE_EXIT);
- break;
- }
-}
-
-static void
-ahc_linux_dv_fill_cmd(struct ahc_softc *ahc, struct scsi_cmnd *cmd,
- struct ahc_devinfo *devinfo)
-{
- memset(cmd, 0, sizeof(struct scsi_cmnd));
- cmd->device = ahc->platform_data->dv_scsi_d