diff options
Diffstat (limited to 'drivers/scsi/aic7xxx/aic7xxx_osm.c')
-rw-r--r-- | drivers/scsi/aic7xxx/aic7xxx_osm.c | 1681 |
1 files changed, 10 insertions, 1671 deletions
diff --git a/drivers/scsi/aic7xxx/aic7xxx_osm.c b/drivers/scsi/aic7xxx/aic7xxx_osm.c index 6b6ee0a52a4..d74b99dab7e 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(¤t->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_dev; - cmd->scsi_done = ahc_linux_dv_complete; -} - -/* - * Synthesize an inquiry command. On the return trip, it'll be - * sniffed and the device transfer settings set for us. - */ -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) -{ - -#ifdef AHC_DEBUG - if (ahc_debug & AHC_SHOW_DV) { - ahc_print_devinfo(ahc, devinfo); - printf("Sending INQ\n"); - } -#endif - if (targ->inq_data == NULL) - targ->inq_data = malloc(AHC_LINUX_DV_INQ_LEN, - M_DEVBUF, M_WAITOK); - if (targ->dv_state > AHC_DV_STATE_INQ_ASYNC) { - if (targ->dv_buffer != NULL) - free(targ->dv_buffer, M_DEVBUF); - targ->dv_buffer = malloc(AHC_LINUX_DV_INQ_LEN, - M_DEVBUF, M_WAITOK); - } - - ahc_linux_dv_fill_cmd(ahc, cmd, devinfo); - cmd->sc_data_direction = DMA_FROM_DEVICE; - cmd->cmd_len = 6; - cmd->cmnd[0] = INQUIRY; - cmd->cmnd[4] = request_length; - cmd->request_bufflen = request_length; - if (targ->dv_state > AHC_DV_STATE_INQ_ASYNC) - cmd->request_buffer = targ->dv_buffer; - else - cmd->request_buffer = targ->inq_data; - memset(cmd->request_buffer, 0, AHC_LINUX_DV_INQ_LEN); -} - -static void -ahc_linux_dv_tur(struct ahc_softc *ahc, struct scsi_cmnd *cmd, - struct ahc_devinfo *devinfo) -{ - -#ifdef AHC_DEBUG - if (ahc_debug & AHC_SHOW_DV) { - ahc_print_devinfo(ahc, devinfo); - printf("Sending TUR\n"); - } -#endif - /* Do a TUR to clear out any non-fatal transitional state */ - ahc_linux_dv_fill_cmd(ahc, cmd, devinfo); - cmd->sc_data_direction = DMA_NONE; - cmd->cmd_len = 6; - cmd->cmnd[0] = TEST_UNIT_READY; -} - -#define AHC_REBD_LEN 4 - -static void -ahc_linux_dv_rebd(struct ahc_softc *ahc, struct scsi_cmnd *cmd, - struct ahc_devinfo *devinfo, struct ahc_linux_target *targ) -{ - -#ifdef AHC_DEBUG - if (ahc_debug & AHC_SHOW_DV) { - ahc_print_devinfo(ahc, devinfo); - printf("Sending REBD\n"); - } -#endif - if (targ->dv_buffer != NULL) - free(targ->dv_buffer, M_DEVBUF); - targ->dv_buffer = malloc(AHC_REBD_LEN, M_DEVBUF, M_WAITOK); - ahc_linux_dv_fill_cmd(ahc, cmd, devinfo); - cmd->sc_data_direction = DMA_FROM_DEVICE; - cmd->cmd_len = 10; - cmd->cmnd[0] = READ_BUFFER; - cmd->cmnd[1] = 0x0b; - scsi_ulto3b(AHC_REBD_LEN, &cmd->cmnd[6]); - cmd->request_bufflen = AHC_REBD_LEN; - cmd->underflow = cmd->request_bufflen; - cmd->request_buffer = targ->dv_buffer; -} - -static void -ahc_linux_dv_web(struct ahc_softc *ahc, struct scsi_cmnd *cmd, - struct ahc_devinfo *devinfo, struct ahc_linux_target *targ) -{ - -#ifdef AHC_DEBUG - if (ahc_debug & AHC_SHOW_DV) { - ahc_print_devinfo(ahc, devinfo); - printf("Sending WEB\n"); - } -#endif - ahc_linux_dv_fill_cmd(ahc, cmd, devinfo); - cmd->sc_data_direction = DMA_TO_DEVICE; - cmd->cmd_len = 10; - cmd->cmnd[0] = WRITE_BUFFER; - cmd->cmnd[1] = 0x0a; - scsi_ulto3b(targ->dv_echo_size, &cmd->cmnd[6]); - cmd->request_bufflen = targ->dv_echo_size; - cmd->underflow = cmd->request_bufflen; - cmd->request_buffer = targ->dv_buffer; -} - -static void -ahc_linux_dv_reb(struct ahc_softc *ahc, struct scsi_cmnd *cmd, - struct ahc_devinfo *devinfo, struct ahc_linux_target *targ) -{ - -#ifdef AHC_DEBUG - if (ahc_debug & AHC_SHOW_DV) { - ahc_print_devinfo(ahc, devinfo); - printf("Sending REB\n"); - } -#endif - ahc_linux_dv_fill_cmd(ahc, cmd, devinfo); - cmd->sc_data_direction = DMA_FROM_DEVICE; - cmd->cmd_len = 10; - cmd->cmnd[0] = READ_BUFFER; - cmd->cmnd[1] = 0x0a; - scsi_ulto3b(targ->dv_echo_size, &cmd->cmnd[6]); - cmd->request_bufflen = targ->dv_echo_size; - cmd->underflow = cmd->request_bufflen; - cmd->request_buffer = targ->dv_buffer1; -} - -static void -ahc_linux_dv_su(struct ahc_softc *ahc, struct scsi_cmnd *cmd, - struct ahc_devinfo *devinfo, - struct ahc_linux_target *targ) -{ - u_int le; - - le = SID_IS_REMOVABLE(targ->inq_data) ? SSS_LOEJ : 0; - -#ifdef AHC_DEBUG - if (ahc_debug & AHC_SHOW_DV) { - ahc_print_devinfo(ahc, devinfo); - printf("Sending SU\n"); - } -#endif - ahc_linux_dv_fill_cmd(ahc, cmd, devinfo); - cmd->sc_data_direction = DMA_NONE; - cmd->cmd_len = 6; - cmd->cmnd[0] = START_STOP_UNIT; - cmd->cmnd[4] = le | SSS_START; -} - -static int -ahc_linux_fallback(struct ahc_softc *ahc, struct ahc_devinfo *devinfo) -{ - struct ahc_linux_target *targ; - struct ahc_initiator_tinfo *tinfo; - struct ahc_transinfo *goal; - struct ahc_tmode_tstate *tstate; - struct ahc_syncrate *syncrate; - u_long s; - u_int width; - u_int period; - u_int offset; - u_int ppr_options; - u_int cur_speed; - u_int wide_speed; - u_int narrow_speed; - u_int fallback_speed; - -#ifdef AHC_DEBUG - if (ahc_debug & AHC_SHOW_DV) { - ahc_print_devinfo(ahc, devinfo); - printf("Trying to fallback\n"); - } -#endif - ahc_lock(ahc, &s); - targ = ahc->platform_data->targets[devinfo->target_offset]; - tinfo = ahc_fetch_transinfo(ahc, devinfo->channel, - devinfo->our_scsiid, - devinfo->target, &tstate); - goal = &tinfo->goal; - width = goal->width; - period = goal->period; - offset = goal->offset; - ppr_options = goal->ppr_options; - if (offset == 0) - period = AHC_ASYNC_XFER_PERIOD; - if (targ->dv_next_narrow_period == 0) - targ->dv_next_narrow_period = MAX(period, AHC_SYNCRATE_ULTRA2); - if (targ->dv_next_wide_period == 0) - targ->dv_next_wide_period = period; - if (targ->dv_max_width == 0) - targ->dv_max_width = width; - if (targ->dv_max_ppr_options == 0) - targ->dv_max_ppr_options = ppr_options; - if (targ->dv_last_ppr_options == 0) - targ->dv_last_ppr_options = ppr_options; - - cur_speed = aic_calc_speed(width, period, offset, AHC_SYNCRATE_MIN); - wide_speed = aic_calc_speed(MSG_EXT_WDTR_BUS_16_BIT, - targ->dv_next_wide_period, - MAX_OFFSET, - AHC_SYNCRATE_MIN); - narrow_speed = aic_calc_speed(MSG_EXT_WDTR_BUS_8_BIT, - targ->dv_next_narrow_period, - MAX_OFFSET, - AHC_SYNCRATE_MIN); - fallback_speed = aic_calc_speed(width, period+1, offset, - AHC_SYNCRATE_MIN); -#ifdef AHC_DEBUG - if (ahc_debug & AHC_SHOW_DV) { - printf("cur_speed= %d, wide_speed= %d, narrow_speed= %d, " - "fallback_speed= %d\n", cur_speed, wide_speed, - narrow_speed, fallback_speed); - } -#endif - - if (cur_speed > 160000) { - /* - * Paced/DT/IU_REQ only transfer speeds. All we - * can do is fallback in terms of syncrate. - */ - period++; - } else if (cur_speed > 80000) { - if ((ppr_options & MSG_EXT_PPR_IU_REQ) != 0) { - /* - * Try without IU_REQ as it may be confusing - * an expander. - */ - ppr_options &= ~MSG_EXT_PPR_IU_REQ; - } else { - /* - * Paced/DT only transfer speeds. All we - * can do is fallback in terms of syncrate. - */ - period++; - ppr_options = targ->dv_max_ppr_options; - } - } else if (cur_speed > 3300) { - - /* - * In this range we the following - * options ordered from highest to - * lowest desireability: - * - * o Wide/DT - * o Wide/non-DT - * o Narrow at a potentally higher sync rate. - * - * All modes are tested with and without IU_REQ - * set since using IUs may confuse an expander. - */ - if ((ppr_options & MSG_EXT_PPR_IU_REQ) != 0) { - - ppr_options &= ~MSG_EXT_PPR_IU_REQ; - } else if ((ppr_options & MSG_EXT_PPR_DT_REQ) != 0) { - /* - * Try going non-DT. - */ - ppr_options = targ->dv_max_ppr_options; - ppr_options &= ~MSG_EXT_PPR_DT_REQ; - } else if (targ->dv_last_ppr_options != 0) { - /* - * Try without QAS or any other PPR options. - * We may need a non-PPR message to work with - * an expander. We look at the "last PPR options" - * so we will perform this fallback even if the - * target responded to our PPR negotiation with - * no option bits set. - */ - ppr_options = 0; - } else if (width == MSG_EXT_WDTR_BUS_16_BIT) { - /* - * If the next narrow speed is greater than - * the next wide speed, fallback to narrow. - * Otherwise fallback to the next DT/Wide setting. - * The narrow async speed will always be smaller - * than the wide async speed, so handle this case - * specifically. - */ - ppr_options = targ->dv_max_ppr_options; - if (narrow_speed > fallback_speed - || period >= AHC_ASYNC_XFER_PERIOD) { - targ->dv_next_wide_period = period+1; - width = MSG_EXT_WDTR_BUS_8_BIT; - period = targ->dv_next_narrow_period; - } else { - period++; - } - } else if ((ahc->features & AHC_WIDE) != 0 - && targ->dv_max_width != 0 - && wide_speed >= fallback_speed - && (targ->dv_next_wide_period <= AHC_ASYNC_XFER_PERIOD - || period >= AHC_ASYNC_XFER_PERIOD)) { - - /* - * We are narrow. Try falling back - * to the next wide speed with - * all supported ppr options set. - */ - targ->dv_next_narrow_period = period+1; - width = MSG_EXT_WDTR_BUS_16_BIT; - period = targ->dv_next_wide_period; - ppr_options = targ->dv_max_ppr_options; - } else { - /* Only narrow fallback is allowed. */ - period++; - ppr_options = targ->dv_max_ppr_options; - } - } else { - ahc_unlock(ahc, &s); - return (-1); - } - offset = MAX_OFFSET; - syncrate = ahc_find_syncrate(ahc, &period, &ppr_options, - AHC_SYNCRATE_DT); - ahc_set_width(ahc, devinfo, width, AHC_TRANS_GOAL, FALSE); - if (period == 0) { - period = 0; - offset = 0; - ppr_options = 0; - if (width == MSG_EXT_WDTR_BUS_8_BIT) - targ->dv_next_narrow_period = AHC_ASYNC_XFER_PERIOD; - else - targ->dv_next_wide_period = AHC_ASYNC_XFER_PERIOD; - } - ahc_set_syncrate(ahc, devinfo, syncrate, period, offset, - ppr_options, AHC_TRANS_GOAL, FALSE); - targ->dv_last_ppr_options = ppr_options; - ahc_unlock(ahc, &s); - return (0); -} - -static void -ahc_linux_dv_timeout(struct scsi_cmnd *cmd) -{ - struct ahc_softc *ahc; - struct scb *scb; - u_long flags; - - ahc = *((struct ahc_softc **)cmd->device->host->hostdata); - ahc_lock(ahc, &flags); - -#ifdef AHC_DEBUG - if (ahc_debug & AHC_SHOW_DV) { - printf("%s: Timeout while doing DV command %x.\n", - ahc_name(ahc), cmd->cmnd[0]); - ahc_dump_card_state(ahc); - } -#endif - - /* - * Guard against "done race". No action is - * required if we just completed. - */ - if ((scb = (struct scb *)cmd->host_scribble) == NULL) { - ahc_unlock(ahc, &flags); - return; - } - - /* - * Command has not completed. Mark this - * SCB as having failing status prior to - * resetting the bus, so we get the correct - * error code. - */ - if ((scb->flags & SCB_SENSE) != 0) - ahc_set_transaction_status(scb, CAM_AUTOSENSE_FAIL); - else - ahc_set_transaction_status(scb, CAM_CMD_TIMEOUT); - ahc_reset_channel(ahc, cmd->device->channel + 'A', /*initiate*/TRUE); - - /* - * Add a minimal bus settle delay for devices that are slow to - * respond after bus resets. - */ - ahc_linux_freeze_simq(ahc); - init_timer(&ahc->platform_data->reset_timer); - ahc->platform_data->reset_timer.data = (u_long)ahc; - ahc->platform_data->reset_timer.expires = jiffies + HZ / 2; - ahc->platform_data->reset_timer.function = - (ahc_linux_callback_t *)ahc_linux_release_simq; - add_timer(&ahc->platform_data->reset_timer); - if (ahc_linux_next_device_to_run(ahc) != NULL) - ahc_schedule_runq(ahc); - ahc_linux_run_complete_queue(ahc); - ahc_unlock(ahc, &flags); -} - -static void -ahc_linux_dv_complete(struct scsi_cmnd *cmd) -{ - struct ahc_softc *ahc; - - ahc = *((struct ahc_softc **)cmd->device->host->hostdata); - - /* Delete the DV timer before it goes off! */ - scsi_delete_timer(cmd); - -#ifdef AHC_DEBUG - if (ahc_debug & AHC_SHOW_DV) - printf("%s:%d:%d: Command completed, status= 0x%x\n", - ahc_name(ahc), cmd->device->channel, - cmd->device->id, cmd->result); -#endif - - /* Wake up the state machine */ - up(&ahc->platform_data->dv_cmd_sem); -} - -static void -ahc_linux_generate_dv_pattern(struct ahc_linux_target *targ) -{ - uint16_t b; - u_int i; - u_int j; - - if (targ->dv_buffer != NULL) - free(targ->dv_buffer, M_DEVBUF); - targ->dv_buffer = malloc(targ->dv_echo_size, M_DEVBUF, M_WAITOK); - if (targ->dv_buffer1 != NULL) - free(targ->dv_buffer1, M_DEVBUF); - targ->dv_buffer1 = malloc(targ->dv_echo_size, M_DEVBUF, M_WAITOK); - - i = 0; - b = 0x0001; - for (j = 0 ; i < targ->dv_echo_size; j++) { - if (j < 32) { - /* - * 32bytes of sequential numbers. - */ - targ->dv_buffer[i++] = j & 0xff; - } else if (j < 48) { - /* - * 32bytes of repeating 0x0000, 0xffff. - */ - targ->dv_buffer[i++] = (j & 0x02) ? 0xff : 0x00; - } else if (j < 64) { - /* - * 32bytes of repeating 0x5555, 0xaaaa. - */ - targ->dv_buffer[i++] = (j & 0x02) ? 0xaa : 0x55; - } else { - /* - * Remaining buffer is filled with a repeating - * patter of: - * - * 0xffff - * ~0x0001 << shifted once in each loop. - */ - if (j & 0x02) { - if (j & 0x01) { - targ->dv_buffer[i++] = ~(b >> 8) & 0xff; - b <<= 1; - if (b == 0x0000) - b = 0x0001; - } else { - targ->dv_buffer[i++] = (~b & 0xff); - } - } else { - targ->dv_buffer[i++] = 0xff; - } - } - } -} - static u_int ahc_linux_user_tagdepth(struct ahc_softc *ahc, struct ahc_devinfo *devinfo) { @@ -3482,48 +2016,6 @@ ahc_linux_user_tagdepth(struct ahc_softc *ahc, struct ahc_devinfo *devinfo) return (tags); } -static u_int -ahc_linux_user_dv_setting(struct ahc_softc *ahc) -{ - static int warned_user; - int dv; - - if (ahc->unit >= NUM_ELEMENTS(aic7xxx_dv_settings)) { - if (warned_user == 0) { - - printf(KERN_WARNING -"aic7xxx: WARNING: Insufficient dv settings instances\n" -"aic7xxx: for installed controllers. Using defaults\n" -"aic7xxx: Please update the aic7xxx_dv_settings array\n" -"aic7xxx: in the aic7xxx_osm.c source file.\n"); - warned_user++; - } - dv = -1; - } else { - - dv = aic7xxx_dv_settings[ahc->unit]; - } - - if (dv < 0) { - u_long s; - - /* - * Apply the default. - */ - /* - * XXX - Enable DV on non-U160 controllers once it - * has been tested there. - */ - ahc_lock(ahc, &s); - dv = (ahc->features & AHC_DT); - if (ahc->seep_config != 0 - && ahc->seep_config->signature >= CFSIGNATURE2) - dv = (ahc->seep_config->adapter_control & CFENABLEDV); - ahc_unlock(ahc, &s); - } - return (dv); -} - /* * Determines the queue depth for a given device. */ @@ -3574,8 +2066,7 @@ ahc_linux_run_device_queue(struct ahc_softc *ahc, struct ahc_linux_device *dev) * Schedule us to run later. The only reason we are not * running is because the whole controller Q is frozen. */ - if (ahc->platform_data->qfrozen != 0 - && AHC_DV_SIMQ_FROZEN(ahc) == 0) { + if (ahc->platform_data->qfrozen != 0) { TAILQ_INSERT_TAIL(&ahc->platform_data->device_runq, dev, links); dev->flags |= AHC_DEV_ON_RUN_LIST; @@ -3616,9 +2107,6 @@ ahc_linux_run_device_queue(struct ahc_softc *ahc, struct ahc_linux_device *dev) if ((ahc->user_discenable & mask) != 0) hscb->control |= DISCENB; - if (AHC_DV_CMD(cmd) != 0) - scb->flags |= SCB_SILENT; - if ((tstate->auto_negotiate & mask) != 0) { scb->flags |= SCB_AUTO_NEGOTIATE; scb->hscb->control |= MK_MESSAGE; @@ -3811,7 +2299,6 @@ ahc_linux_alloc_target(struct ahc_softc *ahc, u_int channel, u_int target) targ->channel = channel; targ->target = target; targ->ahc = ahc; - targ->flags = AHC_DV_REQUIRED; ahc->platform_data->targets[target_offset] = targ; return (targ); } @@ -3850,10 +2337,6 @@ ahc_linux_free_target(struct ahc_softc *ahc, struct ahc_linux_target *targ) ahc->platform_data->targets[target_offset] = NULL; if (targ->inq_data != NULL) free(targ->inq_data, M_DEVBUF); - if (targ->dv_buffer != NULL) - free(targ->dv_buffer, M_DEVBUF); - if (targ->dv_buffer1 != NULL) - free(targ->dv_buffer1, M_DEVBUF); free(targ, M_DEVBUF); } @@ -3900,8 +2383,7 @@ __ahc_linux_free_device(struct ahc_softc *ahc, struct ahc_linux_device *dev) targ->devices[dev->lun] = NULL; free(dev, M_DEVBUF); targ->refcount--; - if (targ->refcount == 0 - && (targ->flags & AHC_DV_REQUIRED) == 0) + if (targ->refcount == 0) ahc_linux_free_target(ahc, targ); } @@ -4105,16 +2587,7 @@ ahc_done(struct ahc_softc *ahc, struct scb *scb) ahc_linux_handle_scsi_status(ahc, dev, scb); } else if (ahc_get_transaction_status(scb) == CAM_SEL_TIMEOUT) { dev->flags |= AHC_DEV_UNCONFIGURED; - if (AHC_DV_CMD(cmd) == FALSE) - dev->target->flags &= ~AHC_DV_REQUIRED; } - /* - * Start DV for devices that require it assuming the first command - * sent does not result in a selection timeout. - */ - if (ahc_get_transaction_status(scb) != CAM_SEL_TIMEOUT - && (dev->target->flags & AHC_DV_REQUIRED) != 0) - ahc_linux_start_dv(ahc); if (dev->openings == 1 && ahc_get_transaction_status(scb) == CAM_REQ_CMP @@ -4158,13 +2631,6 @@ ahc_done(struct ahc_softc *ahc, struct scb *scb) ahc_free_scb(ahc, scb); ahc_linux_queue_cmd_complete(ahc, cmd); - - if ((ahc->platform_data->flags & AHC_DV_WAIT_SIMQ_EMPTY) != 0 - && LIST_FIRST(&ahc->pending_scbs) == NULL) { - ahc->platform_data->flags &= ~AHC_DV_WAIT_SIMQ_EMPTY; - up(&ahc->platform_data->dv_sem); - } - } static void @@ -4341,7 +2807,7 @@ ahc_linux_queue_cmd_complete(struct ahc_softc *ahc, Scsi_Cmnd *cmd) * full error information available when making * state change decisions. */ - if (AHC_DV_CMD(cmd) == FALSE) { + { u_int new_status; switch (ahc_cmd_get_transaction_status(cmd)) { @@ -4432,115 +2898,6 @@ ahc_linux_queue_cmd_complete(struct ahc_softc *ahc, Scsi_Cmnd *cmd) } static void -ahc_linux_filter_inquiry(struct ahc_softc *ahc, struct ahc_devinfo *devinfo) -{ - struct scsi_inquiry_data *sid; - struct ahc_initiator_tinfo *tinfo; - struct ahc_transinfo *user; - struct ahc_transinfo *goal; - struct ahc_transinfo *curr; - struct ahc_tmode_tstate *tstate; - struct ahc_syncrate *syncrate; - struct ahc_linux_device *dev; - u_int maxsync; - u_int width; - u_int period; - u_int offset; - u_int ppr_options; - u_int trans_version; - u_int prot_version; - - /* - * Determine if this lun actually exists. If so, - * hold on to its corresponding device structure. - * If not, make sure we release the device and - * don't bother processing the rest of this inquiry - * command. - */ - dev = ahc_linux_get_device(ahc, devinfo->channel - 'A', - devinfo->target, devinfo->lun, - /*alloc*/TRUE); - - sid = (struct scsi_inquiry_data *)dev->target->inq_data; - if (SID_QUAL(sid) == SID_QUAL_LU_CONNECTED) { - - dev->flags &= ~AHC_DEV_UNCONFIGURED; - } else { - dev->flags |= AHC_DEV_UNCONFIGURED; - return; - } - - /* - * Update our notion of this device's transfer - * negotiation capabilities. - */ - tinfo = ahc_fetch_transinfo(ahc, devinfo->channel, - devinfo->our_scsiid, - devinfo->target, &tstate); - user = &tinfo->user; - goal = &tinfo->goal; - curr = &tinfo->curr; - width = user->width; - period = user->period; - offset = user->offset; - ppr_options = user->ppr_options; - trans_version = user->transport_version; - prot_version = MIN(user->protocol_version, SID_ANSI_REV(sid)); - - /* - * Only attempt SPI3/4 once we've verified that - * the device claims to support SPI3/4 features. - */ - if (prot_version < SCSI_REV_2) - trans_version = SID_ANSI_REV(sid); - else - trans_version = SCSI_REV_2; - - if ((sid->flags & SID_WBus16) == 0) - width = MSG_EXT_WDTR_BUS_8_BIT; - if ((sid->flags & SID_Sync) == 0) { - period = 0; - offset = 0; - ppr_options = 0; - } - if ((sid->spi3data & SID_SPI_QAS) == 0) - ppr_options &= ~MSG_EXT_PPR_QAS_REQ; - if ((sid->spi3data & SID_SPI_CLOCK_DT) == 0) - ppr_options &= MSG_EXT_PPR_QAS_REQ; - if ((sid->spi3data & SID_SPI_IUS) == 0) - ppr_options &= (MSG_EXT_PPR_DT_REQ - | MSG_EXT_PPR_QAS_REQ); - - if (prot_version > SCSI_REV_2 - && ppr_options != 0) - trans_version = user->transport_version; - - ahc_validate_width(ahc, /*tinfo limit*/NULL, &width, ROLE_UNKNOWN); - if ((ahc->features & AHC_ULTRA2) != 0) - maxsync = AHC_SYNCRATE_DT; - else if ((ahc->features & AHC_ULTRA) != 0) - maxsync = AHC_SYNCRATE_ULTRA; - else - maxsync = AHC_SYNCRATE_FAST; - - syncrate = ahc_find_syncrate(ahc, &period, &ppr_options, maxsync); - ahc_validate_offset(ahc, /*tinfo limit*/NULL, syncrate, - &offset, width, ROLE_UNKNOWN); - if (offset == 0 || period == 0) { - period = 0; - offset = 0; - ppr_options = 0; - } - /* Apply our filtered user settings. */ - curr->transport_version = trans_version; - curr->protocol_version = prot_version; - ahc_set_width(ahc, devinfo, width, AHC_TRANS_GOAL, /*paused*/FALSE); - ahc_set_syncrate(ahc, devinfo, syncrate, period, - offset, ppr_options, AHC_TRANS_GOAL, - /*paused*/FALSE); -} - -static void ahc_linux_sem_timeout(u_long arg) { struct ahc_softc *ahc; @@ -4585,11 +2942,6 @@ ahc_linux_release_simq(u_long arg) ahc->platform_data->qfrozen--; if (ahc->platform_data->qfrozen == 0) unblock_reqs = 1; - if (AHC_DV_SIMQ_FROZEN(ahc) - && ((ahc->platform_data->flags & AHC_DV_WAIT_SIMQ_RELEASE) != 0)) { - ahc->platform_data->flags &= ~AHC_DV_WAIT_SIMQ_RELEASE; - up(&ahc->platform_data->dv_sem); - } ahc_schedule_runq(ahc); ahc_unlock(ahc, &s); /* @@ -5274,19 +3626,6 @@ ahc_linux_init(void) static void ahc_linux_exit(void) { - struct ahc_softc *ahc; - - /* - * Shutdown DV threads before going into the SCSI mid-layer. - * This avoids situations where the mid-layer locks the entire - * kernel so that waiting for our DV threads to exit leads - * to deadlock. - */ - TAILQ_FOREACH(ahc, &ahc_tailq, links) { - - ahc_linux_kill_dv_thread(ahc); - } - #if LINUX_VERSION_CODE < KERNEL_VERSION(2,5,0) /* * In 2.4 we have to unregister from the PCI core _after_ |